root/arch/powerpc/kernel/smp-tbsync.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. enter_contest
  2. smp_generic_take_timebase
  3. start_contest
  4. smp_generic_give_timebase

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Smp timebase synchronization for ppc.
   4  *
   5  * Copyright (C) 2003 Samuel Rydh (samuel@ibrium.se)
   6  *
   7  */
   8 
   9 #include <linux/kernel.h>
  10 #include <linux/sched.h>
  11 #include <linux/smp.h>
  12 #include <linux/unistd.h>
  13 #include <linux/slab.h>
  14 #include <linux/atomic.h>
  15 #include <asm/smp.h>
  16 #include <asm/time.h>
  17 
  18 #define NUM_ITER                300
  19 
  20 enum {
  21         kExit=0, kSetAndTest, kTest
  22 };
  23 
  24 static struct {
  25         volatile u64            tb;
  26         volatile u64            mark;
  27         volatile int            cmd;
  28         volatile int            handshake;
  29         int                     filler[2];
  30 
  31         volatile int            ack;
  32         int                     filler2[7];
  33 
  34         volatile int            race_result;
  35 } *tbsync;
  36 
  37 static volatile int             running;
  38 
  39 static void enter_contest(u64 mark, long add)
  40 {
  41         while (get_tb() < mark)
  42                 tbsync->race_result = add;
  43 }
  44 
  45 void smp_generic_take_timebase(void)
  46 {
  47         int cmd;
  48         u64 tb;
  49         unsigned long flags;
  50 
  51         local_irq_save(flags);
  52         while (!running)
  53                 barrier();
  54         rmb();
  55 
  56         for (;;) {
  57                 tbsync->ack = 1;
  58                 while (!tbsync->handshake)
  59                         barrier();
  60                 rmb();
  61 
  62                 cmd = tbsync->cmd;
  63                 tb = tbsync->tb;
  64                 mb();
  65                 tbsync->ack = 0;
  66                 if (cmd == kExit)
  67                         break;
  68 
  69                 while (tbsync->handshake)
  70                         barrier();
  71                 if (cmd == kSetAndTest)
  72                         set_tb(tb >> 32, tb & 0xfffffffful);
  73                 enter_contest(tbsync->mark, -1);
  74         }
  75         local_irq_restore(flags);
  76 }
  77 
  78 static int start_contest(int cmd, long offset, int num)
  79 {
  80         int i, score=0;
  81         u64 tb;
  82         u64 mark;
  83 
  84         tbsync->cmd = cmd;
  85 
  86         local_irq_disable();
  87         for (i = -3; i < num; ) {
  88                 tb = get_tb() + 400;
  89                 tbsync->tb = tb + offset;
  90                 tbsync->mark = mark = tb + 400;
  91 
  92                 wmb();
  93 
  94                 tbsync->handshake = 1;
  95                 while (tbsync->ack)
  96                         barrier();
  97 
  98                 while (get_tb() <= tb)
  99                         barrier();
 100                 tbsync->handshake = 0;
 101                 enter_contest(mark, 1);
 102 
 103                 while (!tbsync->ack)
 104                         barrier();
 105 
 106                 if (i++ > 0)
 107                         score += tbsync->race_result;
 108         }
 109         local_irq_enable();
 110         return score;
 111 }
 112 
 113 void smp_generic_give_timebase(void)
 114 {
 115         int i, score, score2, old, min=0, max=5000, offset=1000;
 116 
 117         pr_debug("Software timebase sync\n");
 118 
 119         /* if this fails then this kernel won't work anyway... */
 120         tbsync = kzalloc( sizeof(*tbsync), GFP_KERNEL );
 121         mb();
 122         running = 1;
 123 
 124         while (!tbsync->ack)
 125                 barrier();
 126 
 127         pr_debug("Got ack\n");
 128 
 129         /* binary search */
 130         for (old = -1; old != offset ; offset = (min+max) / 2) {
 131                 score = start_contest(kSetAndTest, offset, NUM_ITER);
 132 
 133                 pr_debug("score %d, offset %d\n", score, offset );
 134 
 135                 if( score > 0 )
 136                         max = offset;
 137                 else
 138                         min = offset;
 139                 old = offset;
 140         }
 141         score = start_contest(kSetAndTest, min, NUM_ITER);
 142         score2 = start_contest(kSetAndTest, max, NUM_ITER);
 143 
 144         pr_debug("Min %d (score %d), Max %d (score %d)\n",
 145                  min, score, max, score2);
 146         score = abs(score);
 147         score2 = abs(score2);
 148         offset = (score < score2) ? min : max;
 149 
 150         /* guard against inaccurate mttb */
 151         for (i = 0; i < 10; i++) {
 152                 start_contest(kSetAndTest, offset, NUM_ITER/10);
 153 
 154                 if ((score2 = start_contest(kTest, offset, NUM_ITER)) < 0)
 155                         score2 = -score2;
 156                 if (score2 <= score || score2 < 20)
 157                         break;
 158         }
 159         pr_debug("Final offset: %d (%d/%d)\n", offset, score2, NUM_ITER );
 160 
 161         /* exiting */
 162         tbsync->cmd = kExit;
 163         wmb();
 164         tbsync->handshake = 1;
 165         while (tbsync->ack)
 166                 barrier();
 167         tbsync->handshake = 0;
 168         kfree(tbsync);
 169         tbsync = NULL;
 170         running = 0;
 171 }

/* [<][>][^][v][top][bottom][index][help] */