| 1 | /* test-async-tsc.c |
| 2 | * |
| 3 | * test async tsc on AMD. |
| 4 | */ |
| 5 | |
| 6 | |
| 7 | #include <asm/atomic.h> |
| 8 | #include <linux/module.h> |
| 9 | #include <asm/timex.h> |
| 10 | |
| 11 | //LTT #define LTT_MIN_PROBE_DURATION 400 |
| 12 | //get_cycles |
| 13 | #define LTT_MIN_PROBE_DURATION 200 |
| 14 | |
| 15 | static atomic_long_t ltt_last_tsc = ATOMIC_LONG_INIT(0); |
| 16 | |
| 17 | /* When the local TSC is discovered to lag behind the highest TSC counter, we |
| 18 | * increment the TSC count of an amount that should be, ideally, lower than the |
| 19 | * execution time of this routine, in cycles : this is the granularity we look |
| 20 | * for : we must be able to order the events. */ |
| 21 | |
| 22 | cycles_t ltt_tsc_read(void) |
| 23 | { |
| 24 | cycles_t new_tsc; |
| 25 | cycles_t last_tsc; |
| 26 | |
| 27 | new_tsc = get_cycles_sync(); |
| 28 | if (cpu_has(&cpu_data[smp_processor_id()], X86_FEATURE_CONSTANT_TSC)) |
| 29 | return new_tsc; |
| 30 | |
| 31 | do { |
| 32 | last_tsc = atomic_long_read(<t_last_tsc); |
| 33 | if (new_tsc < last_tsc) { |
| 34 | /* last_tsc may only have incremented since last read, |
| 35 | * therefore the condition new_tsc < last_tsc still |
| 36 | * applies even if it has been updated. Therefore, we |
| 37 | * can use add_return, cheaper than cmpxchg here. */ |
| 38 | new_tsc = atomic_long_add_return(LTT_MIN_PROBE_DURATION, |
| 39 | <t_last_tsc); |
| 40 | break; |
| 41 | } |
| 42 | /* cmpxchg will fail if ltt_last_tsc has been concurrently |
| 43 | * updated by add_return or set to a lower tsc value by a |
| 44 | * concurrent CPU at the same time. cmpxchg will succeed if |
| 45 | * the other CPUs update the ltt_last_tsc with a cmpxchg or |
| 46 | * add_return to a value higher than the new_tsc : it's ok |
| 47 | * since the current get_cycles happened before the one that |
| 48 | * causes the ltt_last_tsc to become higher than new_tsc. |
| 49 | * It also succeeds if we write to the memory location |
| 50 | * successfully without concurrent modification. */ |
| 51 | } while (atomic_long_cmpxchg(<t_last_tsc, last_tsc, new_tsc) |
| 52 | < new_tsc); |
| 53 | return new_tsc; |
| 54 | } |
| 55 | |
| 56 | static int __init test_init(void) |
| 57 | { |
| 58 | int i; |
| 59 | cycles_t time1, time2; |
| 60 | volatile cycles_t myval; |
| 61 | |
| 62 | time1 = get_cycles(); |
| 63 | for (i=0; i<200; i++) { |
| 64 | //printk("time %llu\n", ltt_tsc_read()); |
| 65 | //myval = ltt_tsc_read(); |
| 66 | myval = get_cycles_sync(); |
| 67 | } |
| 68 | time2 = get_cycles(); |
| 69 | printk("timediff %llu\n", time2-time1); |
| 70 | return -EPERM; |
| 71 | } |
| 72 | |
| 73 | static void __exit test_exit(void) |
| 74 | { |
| 75 | } |
| 76 | |
| 77 | module_init(test_init); |
| 78 | module_exit(test_exit); |
| 79 | |
| 80 | MODULE_LICENSE("GPL"); |
| 81 | MODULE_AUTHOR("Mathieu Desnoyers"); |
| 82 | MODULE_DESCRIPTION("sync async tsc"); |
| 83 | |