/* test-mark.c * */ #include #include #include #include #include #include #include static void pmc_flush_cache(void) { register int i; /* write back and invalidate cache (a serializing instruction) */ __asm__ __volatile__ ( "wbinvd" : : : "memory" ); /* The wbinvd instruction does not wait for the external caches * to be flushed, but only requests that it be done. The loop * is to be sure that enough time has elapsed, but the compiler * might simplify or even remove it. The loop bound is for a * 512 KB L2 cache. On a Pentium Pro/II/III, the loop uses * 2 cycles per iteration. * * Does wbinvd also cause the TLB to be flushed? * A comment in mtrr.c suggests that it does. */ for (i = 0; i < 512*1024; i++) { cpu_relax(); } } static void noinline test2(const struct marker *mdata, void *call_private, ...) { /* not called */ printk("blah\n"); } /* * Generic marker flavor always available. * Note : the empty asm volatile with read constraint is used here instead of a * "used" attribute to fix a gcc 4.1.x bug. * Make sure the alignment of the structure in the __markers section will * not add unwanted padding between the beginning of the section and the * structure. Force alignment to the same alignment as the section start. */ #define __my_trace_mark(generic, name, call_private, format, args...) \ do { \ static const char __mstrtab_##name[] \ __attribute__((section("__markers_strings"))) \ = #name "\0" format; \ static struct marker __mark_##name \ __attribute__((section("__markers"), aligned(8))) = \ { __mstrtab_##name, &__mstrtab_##name[sizeof(#name)], \ 0, 0, marker_probe_cb, \ { __mark_empty_function, NULL}, NULL }; \ __mark_check_format(format, ## args); \ if (!generic) { \ if (unlikely(imv_read(__mark_##name.state))) \ test2 \ (&__mark_##name, call_private, \ ## args); \ } else { \ if (unlikely(_imv_read(__mark_##name.state))) \ test2 \ (&__mark_##name, call_private, \ ## args); \ } \ } while (0) struct proc_dir_entry *pentry = NULL; static inline void test(unsigned long arg, unsigned long arg2) { volatile int temp[5]; #ifdef CACHEFLUSH clflush(¤t->pid); //pmc_flush_cache(); #endif temp[2] = (temp[0] + 60) << 10; temp[3] = (temp[2] + 60) << 10; temp[4] = (temp[3] + 60) << 10; temp[0] = (temp[4] + 60) << 10; //asm volatile (""); //__my_trace_mark(1, kernel_debug_test, NULL, "%d %d %ld %ld", 2, current->pid, arg, arg2); barrier(); __my_trace_mark(0, kernel_debug_test, NULL, "%d %d %ld %ld", 2, current->pid, arg, arg2); barrier(); } static int my_open(struct inode *inode, struct file *file) { unsigned int i; cycles_t cycles1, cycles2; unsigned long flags; local_irq_save(flags); #ifdef CACHEFLUSH //pmc_flush_cache(); /* initial write back, without cycle count */ //msleep(20); /* wait for L2 flush */ #endif rdtsc_barrier(); cycles1 = get_cycles(); rdtsc_barrier(); for(i=0; i<2000; i++) { test(i, i); test(i, i); test(i, i); test(i, i); test(i, i); test(i, i); test(i, i); test(i, i); test(i, i); test(i, i); } rdtsc_barrier(); cycles2 = get_cycles(); rdtsc_barrier(); local_irq_restore(flags); printk("cycles : %llu\n", cycles2-cycles1); return -EPERM; } static struct file_operations my_operations = { .open = my_open, }; int init_module(void) { pentry = create_proc_entry("testmark", 0444, NULL); if (pentry) pentry->proc_fops = &my_operations; return 0; } void cleanup_module(void) { remove_proc_entry("testmark", NULL); } MODULE_LICENSE("GPL"); MODULE_AUTHOR("Mathieu Desnoyers"); MODULE_DESCRIPTION("Marker Test"); MODULE_VERSION("1.0");