0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/kernel.h>
0010 #include <linux/sched.h>
0011 #include <linux/smp.h>
0012 #include <linux/unistd.h>
0013 #include <linux/slab.h>
0014 #include <linux/atomic.h>
0015 #include <asm/smp.h>
0016 #include <asm/time.h>
0017
0018 #define NUM_ITER 300
0019
0020 enum {
0021 kExit=0, kSetAndTest, kTest
0022 };
0023
0024 static struct {
0025 volatile u64 tb;
0026 volatile u64 mark;
0027 volatile int cmd;
0028 volatile int handshake;
0029 int filler[2];
0030
0031 volatile int ack;
0032 int filler2[7];
0033
0034 volatile int race_result;
0035 } *tbsync;
0036
0037 static volatile int running;
0038
0039 static void enter_contest(u64 mark, long add)
0040 {
0041 while (get_tb() < mark)
0042 tbsync->race_result = add;
0043 }
0044
0045 void smp_generic_take_timebase(void)
0046 {
0047 int cmd;
0048 u64 tb;
0049 unsigned long flags;
0050
0051 local_irq_save(flags);
0052 while (!running)
0053 barrier();
0054 rmb();
0055
0056 for (;;) {
0057 tbsync->ack = 1;
0058 while (!tbsync->handshake)
0059 barrier();
0060 rmb();
0061
0062 cmd = tbsync->cmd;
0063 tb = tbsync->tb;
0064 mb();
0065 tbsync->ack = 0;
0066 if (cmd == kExit)
0067 break;
0068
0069 while (tbsync->handshake)
0070 barrier();
0071 if (cmd == kSetAndTest)
0072 set_tb(tb >> 32, tb & 0xfffffffful);
0073 enter_contest(tbsync->mark, -1);
0074 }
0075 local_irq_restore(flags);
0076 }
0077
0078 static int start_contest(int cmd, long offset, int num)
0079 {
0080 int i, score=0;
0081 u64 tb;
0082 u64 mark;
0083
0084 tbsync->cmd = cmd;
0085
0086 local_irq_disable();
0087 for (i = -3; i < num; ) {
0088 tb = get_tb() + 400;
0089 tbsync->tb = tb + offset;
0090 tbsync->mark = mark = tb + 400;
0091
0092 wmb();
0093
0094 tbsync->handshake = 1;
0095 while (tbsync->ack)
0096 barrier();
0097
0098 while (get_tb() <= tb)
0099 barrier();
0100 tbsync->handshake = 0;
0101 enter_contest(mark, 1);
0102
0103 while (!tbsync->ack)
0104 barrier();
0105
0106 if (i++ > 0)
0107 score += tbsync->race_result;
0108 }
0109 local_irq_enable();
0110 return score;
0111 }
0112
0113 void smp_generic_give_timebase(void)
0114 {
0115 int i, score, score2, old, min=0, max=5000, offset=1000;
0116
0117 pr_debug("Software timebase sync\n");
0118
0119
0120 tbsync = kzalloc( sizeof(*tbsync), GFP_KERNEL );
0121 mb();
0122 running = 1;
0123
0124 while (!tbsync->ack)
0125 barrier();
0126
0127 pr_debug("Got ack\n");
0128
0129
0130 for (old = -1; old != offset ; offset = (min+max) / 2) {
0131 score = start_contest(kSetAndTest, offset, NUM_ITER);
0132
0133 pr_debug("score %d, offset %d\n", score, offset );
0134
0135 if( score > 0 )
0136 max = offset;
0137 else
0138 min = offset;
0139 old = offset;
0140 }
0141 score = start_contest(kSetAndTest, min, NUM_ITER);
0142 score2 = start_contest(kSetAndTest, max, NUM_ITER);
0143
0144 pr_debug("Min %d (score %d), Max %d (score %d)\n",
0145 min, score, max, score2);
0146 score = abs(score);
0147 score2 = abs(score2);
0148 offset = (score < score2) ? min : max;
0149
0150
0151 for (i = 0; i < 10; i++) {
0152 start_contest(kSetAndTest, offset, NUM_ITER/10);
0153
0154 if ((score2 = start_contest(kTest, offset, NUM_ITER)) < 0)
0155 score2 = -score2;
0156 if (score2 <= score || score2 < 20)
0157 break;
0158 }
0159 pr_debug("Final offset: %d (%d/%d)\n", offset, score2, NUM_ITER );
0160
0161
0162 tbsync->cmd = kExit;
0163 wmb();
0164 tbsync->handshake = 1;
0165 while (tbsync->ack)
0166 barrier();
0167 tbsync->handshake = 0;
0168 kfree(tbsync);
0169 tbsync = NULL;
0170 running = 0;
0171 }