1 /* Helper functions that allow driver writers to easily busy-wait (spin) for a 2 * condition to become satisfied within a certain maximum time span. 3 */ 4 /* This implementation first spins without making any system calls for a 5 * while, and then starts using system calls (specifically, the system call to 6 * obtain the current time) while spinning. The reason for this is that in 7 * many cases, the condition to be checked will become satisfied rather 8 * quickly, and we want to avoid getting descheduled in that case. However, 9 * after a while, running out of scheduling quantum will cause our priority to 10 * be lowered, and we can avoid this by voluntarily giving up the CPU, by 11 * making a system call. 12 */ 13 #include "sysutil.h" 14 #include <minix/spin.h> 15 #include <minix/minlib.h> 16 17 /* Number of microseconds to keep spinning initially, without performing a 18 * system call. We pick a value somewhat smaller than a typical clock tick. 19 * Note that for the above reasons, we want to avoid using sys_hz() here. 20 */ 21 #define TSC_SPIN 1000 /* in microseconds */ 22 23 /* Internal spin states. */ 24 enum { 25 STATE_INIT, /* simply check the condition (once) */ 26 STATE_BASE_TS, /* get the initial TSC value (once) */ 27 STATE_TS, /* use the TSC to spin (up to TSC_SPIN us) */ 28 STATE_UPTIME /* use the clock to spin */ 29 }; 30 31 void spin_init(spin_t *s, u32_t usecs) 32 { 33 /* Initialize the given spin state structure, set to spin at most the 34 * given number of microseconds. 35 */ 36 s->s_state = STATE_INIT; 37 s->s_usecs = usecs; 38 s->s_timeout = FALSE; 39 } 40 41 int spin_check(spin_t *s) 42 { 43 /* Check whether a timeout has taken place. Return TRUE if the caller 44 * should continue spinning, and FALSE if a timeout has occurred. The 45 * implementation assumes that it is okay to spin a little bit too long 46 * (up to a full clock tick extra). 47 */ 48 u64_t cur_tsc, tsc_delta; 49 clock_t now, micro_delta; 50 51 switch (s->s_state) { 52 case STATE_INIT: 53 s->s_state = STATE_BASE_TS; 54 break; 55 56 case STATE_BASE_TS: 57 s->s_state = STATE_TS; 58 read_frclock_64(&s->s_base_tsc); 59 break; 60 61 case STATE_TS: 62 read_frclock_64(&cur_tsc); 63 tsc_delta = delta_frclock_64(s->s_base_tsc, cur_tsc); 64 micro_delta = frclock_64_to_micros(tsc_delta); 65 66 if (micro_delta >= s->s_usecs) { 67 s->s_timeout = TRUE; 68 return FALSE; 69 } 70 71 if (micro_delta >= TSC_SPIN) { 72 s->s_usecs -= micro_delta; 73 s->s_base_uptime = getticks(); 74 s->s_state = STATE_UPTIME; 75 } 76 77 break; 78 79 case STATE_UPTIME: 80 now = getticks(); 81 82 /* We assume that sys_hz() caches its return value. */ 83 micro_delta = ((now - s->s_base_uptime) * 1000 / sys_hz()) * 84 1000; 85 86 if (micro_delta >= s->s_usecs) { 87 s->s_timeout = TRUE; 88 return FALSE; 89 } 90 91 break; 92 93 default: 94 panic("spin_check: invalid state %d", s->s_state); 95 } 96 97 return TRUE; 98 } 99