Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Smp timebase synchronization for ppc.
0004  *
0005  * Copyright (C) 2003 Samuel Rydh (samuel@ibrium.se)
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     /* if this fails then this kernel won't work anyway... */
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     /* binary search */
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     /* guard against inaccurate mttb */
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     /* exiting */
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 }