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_tsc_64(&s->s_base_tsc); 59 break; 60 61 case STATE_TS: 62 read_tsc_64(&cur_tsc); 63 64 tsc_delta = cur_tsc - s->s_base_tsc; 65 66 micro_delta = tsc_64_to_micros(tsc_delta); 67 68 if (micro_delta >= s->s_usecs) { 69 s->s_timeout = TRUE; 70 return FALSE; 71 } 72 73 if (micro_delta >= TSC_SPIN) { 74 s->s_usecs -= micro_delta; 75 getticks(&s->s_base_uptime); 76 s->s_state = STATE_UPTIME; 77 } 78 79 break; 80 81 case STATE_UPTIME: 82 getticks(&now); 83 84 /* We assume that sys_hz() caches its return value. */ 85 micro_delta = ((now - s->s_base_uptime) * 1000 / sys_hz()) * 86 1000; 87 88 if (micro_delta >= s->s_usecs) { 89 s->s_timeout = TRUE; 90 return FALSE; 91 } 92 93 break; 94 95 default: 96 panic("spin_check: invalid state %d", s->s_state); 97 } 98 99 return TRUE; 100 } 101