From: David van Moolenbroek Date: Mon, 12 Jul 2010 23:14:40 +0000 (+0000) Subject: libsys: add standard condition spinning primitives X-Git-Tag: v3.1.8~253 X-Git-Url: http://zhaoyanbai.com/repos/rndc-confgen.html?a=commitdiff_plain;h=1ecdac623a21665ac78f6f8d351a8b68d7e6b1c0;p=minix.git libsys: add standard condition spinning primitives --- diff --git a/drivers/at_wini/at_wini.c b/drivers/at_wini/at_wini.c index 4cbd3f22b..3a2b96516 100644 --- a/drivers/at_wini/at_wini.c +++ b/drivers/at_wini/at_wini.c @@ -44,7 +44,7 @@ struct command { }; /* Timeouts and max retries. */ -PRIVATE int timeout_ticks = DEF_TIMEOUT_TICKS; +PRIVATE int timeout_usecs = DEF_TIMEOUT_USECS; PRIVATE int max_errors = MAX_ERRORS; PRIVATE long w_standard_timeouts = 0; PRIVATE long w_pci_debug = 0; @@ -941,12 +941,12 @@ PRIVATE int w_io_test(void) save_dev = w_device; /* Reduce timeout values for this test transaction. */ - save_timeout = timeout_ticks; + save_timeout = timeout_usecs; save_errors = max_errors; save_wakeup = wakeup_ticks; if (!w_standard_timeouts) { - timeout_ticks = system_hz * 4; + timeout_usecs = 4000000; wakeup_ticks = system_hz * 6; max_errors = 3; } @@ -964,7 +964,7 @@ PRIVATE int w_io_test(void) panic("Couldn't switch back devices"); /* Restore parameters. */ - timeout_ticks = save_timeout; + timeout_usecs = save_timeout; max_errors = save_errors; wakeup_ticks = save_wakeup; w_testing = 0; @@ -1920,24 +1920,19 @@ int mask; /* status mask */ int value; /* required status */ { /* Wait until controller is in the required state. Return zero on timeout. - * An alarm that set a timeout flag is used. TIMEOUT is in micros, we need - * ticks. Disabling the alarm is not needed, because a static flag is used - * and a leftover timeout cannot do any harm. */ unsigned long w_status; - clock_t t0, t1; + spin_t spin; int s; - getuptime(&t0); - do { + SPIN_FOR(&spin, timeout_usecs) { if ((s=sys_inb(w_wn->base_cmd + REG_STATUS, &w_status)) != OK) panic("Couldn't read register: %d", s); w_wn->w_status= w_status; if ((w_wn->w_status & mask) == value) { return 1; } - } while ((s=getuptime(&t1)) == OK && (t1-t0) < timeout_ticks ); - if (OK != s) printf("AT_WINI: warning, get_uptime failed: %d\n",s); + } w_need_reset(); /* controller gone deaf */ return(0); @@ -1951,23 +1946,18 @@ int mask; /* status mask */ int value; /* required status */ { /* Wait until controller is in the required state. Return zero on timeout. - * An alarm that set a timeout flag is used. TIMEOUT is in micros, we need - * ticks. Disabling the alarm is not needed, because a static flag is used - * and a leftover timeout cannot do any harm. */ unsigned long w_status; - clock_t t0, t1; + spin_t spin; int s; - getuptime(&t0); - do { + SPIN_FOR(&spin, timeout_usecs) { if ((s=sys_inb(w_wn->base_dma + DMA_STATUS, &w_status)) != OK) panic("Couldn't read register: %d", s); if ((w_status & mask) == value) { return 1; } - } while ((s=getuptime(&t1)) == OK && (t1-t0) < timeout_ticks ); - if (OK != s) printf("AT_WINI: warning, get_uptime failed: %d\n",s); + } return(0); } @@ -2320,7 +2310,7 @@ message *m; if (timeout == 0) { /* Restore defaults. */ - timeout_ticks = DEF_TIMEOUT_TICKS; + timeout_usecs = DEF_TIMEOUT_USECS; max_errors = MAX_ERRORS; wakeup_ticks = WAKEUP_TICKS; w_silent = 0; @@ -2336,9 +2326,11 @@ message *m; wakeup_ticks = timeout; max_errors = 3; w_silent = 1; + + timeout = timeout * 1000000 / sys_hz(); - if (timeout_ticks > timeout) - timeout_ticks = timeout; + if (timeout_usecs > timeout) + timeout_usecs = timeout; } r= sys_safecopyto(m->IO_ENDPT, diff --git a/drivers/at_wini/at_wini.h b/drivers/at_wini/at_wini.h index aefa1826b..95a26bbeb 100644 --- a/drivers/at_wini/at_wini.h +++ b/drivers/at_wini/at_wini.h @@ -200,7 +200,7 @@ #define NR_SUBDEVS (MAX_DRIVES * SUB_PER_DRIVE) #define DELAY_USECS 1000 /* controller timeout in microseconds */ #define DELAY_TICKS 1 /* controller timeout in ticks */ -#define DEF_TIMEOUT_TICKS 300 /* controller timeout in ticks */ +#define DEF_TIMEOUT_USECS 5000000L /* controller timeout in microseconds */ #define RECOVERY_USECS 500000 /* controller recovery time in microseconds */ #define RECOVERY_TICKS 30 /* controller recovery time in ticks */ #define INITIALIZED 0x01 /* drive is initialized */ diff --git a/drivers/floppy/floppy.c b/drivers/floppy/floppy.c index 2a706e5a1..d79c0fe9c 100644 --- a/drivers/floppy/floppy.c +++ b/drivers/floppy/floppy.c @@ -130,8 +130,7 @@ #define NR_DRIVES 2 /* maximum number of drives */ #define DIVISOR 128 /* used for sector size encoding */ #define SECTOR_SIZE_CODE 2 /* code to say "512" to the controller */ -#define TIMEOUT_MICROS 500000L /* microseconds waiting for FDC */ -#define TIMEOUT_TICKS 30 /* ticks waiting for FDC */ +#define TIMEOUT_MICROS 5000000L /* microseconds waiting for FDC */ #define NT 7 /* number of diskette/drive combinations */ #define UNCALIBRATED 0 /* drive needs to be calibrated at next use */ #define CALIBRATED 1 /* no calibration needed */ @@ -950,15 +949,14 @@ PRIVATE int fdc_results(void) int s, result_nr; unsigned long status; - clock_t t0,t1; + spin_t spin; /* Extract bytes from FDC until it says it has no more. The loop is * really an outer loop on result_nr and an inner loop on status. * A timeout flag alarm is set. */ result_nr = 0; - getuptime(&t0); - do { + SPIN_FOR(&spin, TIMEOUT_MICROS) { /* Reading one byte is almost a mirror of fdc_out() - the DIRECTION * bit must be set instead of clear, but the CTL_BUSY bit destroys * the perfection of the mirror. @@ -981,8 +979,7 @@ PRIVATE int fdc_results(void) return(OK); /* only good exit */ } - } while ( (s=getuptime(&t1))==OK && (t1-t0) < TIMEOUT_TICKS ); - if (OK!=s) printf("FLOPPY: warning, getuptime failed: %d\n", s); + } need_reset = TRUE; /* controller chip must be reset */ if ((s=sys_irqenable(&irq_hook_id)) != OK) @@ -1026,27 +1023,26 @@ PRIVATE void fdc_out( * can only write to it when it is listening, and it decides when to listen. * If the controller refuses to listen, the FDC chip is given a hard reset. */ - clock_t t0, t1; + spin_t spin; int s; unsigned long status; if (need_reset) return; /* if controller is not listening, return */ /* It may take several tries to get the FDC to accept a command. */ - getuptime(&t0); - do { - if ( (s=getuptime(&t1))==OK && (t1-t0) > TIMEOUT_TICKS ) { - if (OK!=s) printf("FLOPPY: warning, getuptime failed: %d\n", s); - need_reset = TRUE; /* hit it over the head */ - return; - } + SPIN_FOR(&spin, TIMEOUT_MICROS) { if ((s=sys_inb(FDC_STATUS, &status)) != OK) panic("Sys_inb in fdc_out() failed: %d", s); + + if ((status & (MASTER | DIRECTION)) == (MASTER | 0)) { + if ((s=sys_outb(FDC_DATA, val)) != OK) + panic("Sys_outb in fdc_out() failed: %d", s); + + return; + } } - while ((status & (MASTER | DIRECTION)) != (MASTER | 0)); - - if ((s=sys_outb(FDC_DATA, val)) != OK) - panic("Sys_outb in fdc_out() failed: %d", s); + + need_reset = TRUE; /* hit it over the head */ } /*===========================================================================* diff --git a/drivers/fxp/fxp.c b/drivers/fxp/fxp.c index 3271c6620..544ba53bc 100644 --- a/drivers/fxp/fxp.c +++ b/drivers/fxp/fxp.c @@ -946,7 +946,6 @@ static void fxp_confaddr(fxp_t *fp) { static char eakey[]= FXP_ENVVAR "#_EA"; static char eafmt[]= "x:x:x:x:x:x"; - clock_t t0,t1; int i, r; u32_t bus_addr; long v; @@ -987,12 +986,8 @@ static void fxp_confaddr(fxp_t *fp) fxp_cu_ptr_cmd(fp, SC_CU_START, bus_addr, TRUE /* check idle */); - getuptime(&t0); - do { - /* Wait for CU command to complete */ - if (tmpbufp->ias.ias_status & CBL_F_C) - break; - } while (getuptime(&t1)==OK && (t1-t0) < micros_to_ticks(1000)); + /* Wait for CU command to complete */ + SPIN_UNTIL(tmpbufp->ias.ias_status & CBL_F_C, 1000); if (!(tmpbufp->ias.ias_status & CBL_F_C)) panic("fxp_confaddr: CU command failed to complete"); @@ -1356,7 +1351,6 @@ fxp_t *fp; { int r; u32_t bus_addr; - clock_t t0,t1; /* Configure device */ tmpbufp->cc.cc_status= 0; @@ -1372,12 +1366,8 @@ fxp_t *fp; fxp_cu_ptr_cmd(fp, SC_CU_START, bus_addr, TRUE /* check idle */); - getuptime(&t0); - do { - /* Wait for CU command to complete */ - if (tmpbufp->cc.cc_status & CBL_F_C) - break; - } while (getuptime(&t1)==OK && (t1-t0) < micros_to_ticks(100000)); + /* Wait for CU command to complete */ + SPIN_UNTIL(tmpbufp->cc.cc_status & CBL_F_C, 100000); if (!(tmpbufp->cc.cc_status & CBL_F_C)) panic("fxp_do_conf: CU command failed to complete"); @@ -1395,7 +1385,7 @@ int cmd; phys_bytes bus_addr; int check_idle; { - clock_t t0,t1; + spin_t spin; port_t port; u8_t scb_cmd; @@ -1412,15 +1402,15 @@ int check_idle; fxp_outb(port, SCB_CMD, cmd); /* What is a reasonable time-out? There is nothing in the - * documentation. 1 ms should be enough. + * documentation. 1 ms should be enough. We use 100 ms. */ - getuptime(&t0); + spin_init(&spin, 100000); do { /* Wait for CU command to be accepted */ scb_cmd= fxp_inb(port, SCB_CMD); if ((scb_cmd & SC_CUC_MASK) == SC_CU_NOP) break; - } while (getuptime(&t1)==OK && (t1-t0) < micros_to_ticks(100000)); + } while (spin_check(&spin)); if ((scb_cmd & SC_CUC_MASK) != SC_CU_NOP) panic("fxp_cu_ptr_cmd: CU does not accept command"); @@ -1435,7 +1425,7 @@ int cmd; phys_bytes bus_addr; int check_idle; { - clock_t t0,t1; + spin_t spin; port_t port; u8_t scb_cmd; @@ -1451,13 +1441,13 @@ int check_idle; fxp_outl(port, SCB_POINTER, bus_addr); fxp_outb(port, SCB_CMD, cmd); - getuptime(&t0); + spin_init(&spin, 1000); do { /* Wait for RU command to be accepted */ scb_cmd= fxp_inb(port, SCB_CMD); if ((scb_cmd & SC_RUC_MASK) == SC_RU_NOP) break; - } while (getuptime(&t1)==OK && (t1-t0) < micros_to_ticks(1000)); + } while (spin_check(&spin)); if ((scb_cmd & SC_RUC_MASK) != SC_RU_NOP) panic("fxp_ru_ptr_cmd: RU does not accept command"); @@ -1501,7 +1491,6 @@ fxp_t *fp; *===========================================================================*/ static void fxp_getstat_s(message *mp) { - clock_t t0,t1; int r; fxp_t *fp; u32_t *p; @@ -1520,12 +1509,8 @@ static void fxp_getstat_s(message *mp) */ fxp_cu_ptr_cmd(fp, SC_CU_DUMP_SC, 0, FALSE /* do not check idle */); - getuptime(&t0); - do { - /* Wait for CU command to complete */ - if (*p != 0) - break; - } while (getuptime(&t1)==OK && (t1-t0) < micros_to_ticks(1000)); + /* Wait for CU command to complete */ + SPIN_UNTIL(*p != 0, 1000); if (*p == 0) panic("fxp_getstat: CU command failed to complete"); @@ -2220,7 +2205,7 @@ PRIVATE u16_t mii_read(fp, reg) fxp_t *fp; int reg; { - clock_t t0,t1; + spin_t spin; port_t port; u32_t v; @@ -2234,12 +2219,12 @@ int reg; fxp_outl(port, CSR_MDI_CTL, CM_READ | (1 << CM_PHYADDR_SHIFT) | (reg << CM_REG_SHIFT)); - getuptime(&t0); + spin_init(&spin, 100000); do { v= fxp_inl(port, CSR_MDI_CTL); if (v & CM_READY) break; - } while (getuptime(&t1)==OK && (t1-t0) < micros_to_ticks(100000)); + } while (spin_check(&spin)); if (!(v & CM_READY)) panic("mii_read: MDI not ready after command"); diff --git a/drivers/rtl8139/rtl8139.c b/drivers/rtl8139/rtl8139.c index c7cef8c84..c7c182e74 100644 --- a/drivers/rtl8139/rtl8139.c +++ b/drivers/rtl8139/rtl8139.c @@ -689,18 +689,13 @@ re_t *rep; u32_t t; phys_bytes bus_buf; int i; - clock_t t0,t1; port= rep->re_base_port; #if 0 /* Reset the PHY */ rl_outb(port, RL_BMCR, MII_CTRL_RST); - getuptime(&t0); - do { - if (!(rl_inb(port, RL_BMCR) & MII_CTRL_RST)) - break; - } while (getuptime(&t1)==OK && (t1-t0) < system_hz); + SPIN_UNTIL(!(rl_inb(port, RL_BMCR) & MII_CTRL_RST), 1000000); if (rl_inb(port, RL_BMCR) & MII_CTRL_RST) panic("reset PHY failed to complete"); #endif @@ -711,11 +706,7 @@ re_t *rep; port, rl_inb(port, RL_CR)); #endif rl_outb(port, RL_CR, RL_CR_RST); - getuptime(&t0); - do { - if (!(rl_inb(port, RL_CR) & RL_CR_RST)) - break; - } while (getuptime(&t1)==OK && (t1-t0) < system_hz); + SPIN_UNTIL(!(rl_inb(port, RL_CR) & RL_CR_RST), 1000000); #if VERBOSE printf("rl_reset_hw: (after reset) port = 0x%x, RL_CR = 0x%x\n", port, rl_inb(port, RL_CR)); @@ -1572,7 +1563,6 @@ static void rl_clear_rx(re_t *rep) { port_t port; u8_t cr; - clock_t t0,t1; rep->re_clear_rx= FALSE; port= rep->re_base_port; @@ -1581,11 +1571,7 @@ static void rl_clear_rx(re_t *rep) cr= rl_inb(port, RL_CR); cr &= ~RL_CR_RE; rl_outb(port, RL_CR, cr); - getuptime(&t0); - do { - if (!(rl_inb(port, RL_CR) & RL_CR_RE)) - break; - } while (getuptime(&t1)==OK && (t1-t0) < system_hz); + SPIN_UNTIL(!(rl_inb(port, RL_CR) & RL_CR_RE), 1000000); if (rl_inb(port, RL_CR) & RL_CR_RE) panic("cannot disable receiver"); @@ -1819,7 +1805,6 @@ static int rl_handler(re_t *rep) #if 0 u8_t cr; #endif - clock_t t0,t1; int_event_check = FALSE; /* disable check by default */ port= rep->re_base_port; @@ -1912,11 +1897,7 @@ static int rl_handler(re_t *rep) cr= rl_inb(port, RL_CR); cr &= ~RL_CR_TE; rl_outb(port, RL_CR, cr); - getuptime(&t0); - do { - if (!(rl_inb(port, RL_CR) & RL_CR_TE)) - break; - } while (getuptime(&t1)==OK && (t1-t0) < system_hz); + SPIN_UNTIL(!(rl_inb(port, RL_CR) & RL_CR_TE), 1000000); if (rl_inb(port, RL_CR) & RL_CR_TE) { panic("cannot disable transmitter"); } @@ -2289,7 +2270,6 @@ int a; u16_t w; { int b, i, cmd; - clock_t t0, t1; outb_reg3(dep, 1, 0x80 | 0x8); /* Set CS */ @@ -2316,11 +2296,7 @@ u16_t w; outb_reg3(dep, 1, 0x80); /* Drop CS */ /* micro_delay(1); */ /* Is this required? */ outb_reg3(dep, 1, 0x80 | 0x8); /* Set CS */ - getuptime(&t0); - do { - if (inb_reg3(dep, 1) & 1) - break; - } while (getuptime(&t1) == OK && (t1 == t0)); + SPIN_UNTIL(inb_reg3(dep, 1) & 1, 10000); if (!(inb_reg3(dep, 1) & 1)) panic("device remains busy"); } diff --git a/drivers/rtl8169/rtl8169.c b/drivers/rtl8169/rtl8169.c index 7141d9a48..f802992db 100644 --- a/drivers/rtl8169/rtl8169.c +++ b/drivers/rtl8169/rtl8169.c @@ -1015,11 +1015,7 @@ re_t *rep; printf("rl_reset_hw: (before reset) port = 0x%x, RL_CR = 0x%x\n", port, rl_inb(port, RL_CR)); rl_outb(port, RL_CR, RL_CR_RST); - getuptime(&t0); - do { - if (!(rl_inb(port, RL_CR) & RL_CR_RST)) - break; - } while (getuptime(&t1) == OK && (t1 - t0) < system_hz); + SPIN_UNTIL(!(rl_inb(port, RL_CR) & RL_CR_RST), 1000000); printf("rl_reset_hw: (after reset) port = 0x%x, RL_CR = 0x%x\n", port, rl_inb(port, RL_CR)); if (rl_inb(port, RL_CR) & RL_CR_RST) diff --git a/drivers/ti1225/ti1225.c b/drivers/ti1225/ti1225.c index 1d590c88b..81ef98ea3 100644 --- a/drivers/ti1225/ti1225.c +++ b/drivers/ti1225/ti1225.c @@ -316,7 +316,7 @@ PRIVATE void do_int(struct port *pp) { int r, devind, vcc_5v, vcc_3v, vcc_Xv, vcc_Yv, socket_5v, socket_3v, socket_Xv, socket_Yv; - clock_t t0, t1; + spin_t spin; u32_t csr_event, csr_present, csr_control; u8_t v8; u16_t v16; @@ -453,12 +453,12 @@ PRIVATE void do_int(struct port *pp) printf("TI_CARD_CTRL: 0x%02x\n", v8); } - getuptime(&t0); + spin_init(&spin, 100000); do { csr_present= pp->csr_ptr->csr_present; if (csr_present & CP_PWRCYCLE) break; - } while (getuptime(&t1)==OK && (t1-t0) < micros_to_ticks(100000)); + } while (spin_check(&spin)); if (!(csr_present & CP_PWRCYCLE)) { diff --git a/include/Makefile b/include/Makefile index 13f1855e2..f17616515 100644 --- a/include/Makefile +++ b/include/Makefile @@ -22,7 +22,7 @@ INCS+= minix/a.out.h minix/bitmap.h minix/callnr.h minix/cdrom.h \ minix/netdriver.h minix/partition.h minix/paths.h \ minix/portio.h minix/profile.h minix/queryparam.h \ minix/rs.h minix/safecopies.h minix/sched.h minix/sef.h minix/sound.h \ - minix/sys_config.h minix/sysinfo.h minix/syslib.h \ + minix/spin.h minix/sys_config.h minix/sysinfo.h minix/syslib.h \ minix/sysutil.h minix/timers.h minix/tty.h minix/type.h minix/types.h \ minix/u64.h minix/vfsif.h minix/vm.h \ minix/compiler.h minix/compiler-ack.h minix/sha2.h diff --git a/include/minix/drivers.h b/include/minix/drivers.h index fd2ae6cb6..39a5b8320 100644 --- a/include/minix/drivers.h +++ b/include/minix/drivers.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include /* IRQ vectors and miscellaneous ports */ diff --git a/include/minix/spin.h b/include/minix/spin.h new file mode 100644 index 000000000..c445e0e55 --- /dev/null +++ b/include/minix/spin.h @@ -0,0 +1,38 @@ +/* Prototypes for condition spinning helper functions (part of libsys). */ +#ifndef _MINIX_SPIN_H +#define _MINIX_SPIN_H + +/* Opaque spin state structure. */ +typedef struct { + int s_state; + u32_t s_usecs; + u64_t s_base_tsc; + clock_t s_base_uptime; + int s_timeout; +} spin_t; + +/* Functions. */ +_PROTOTYPE( void spin_init, (spin_t *s, u32_t usecs) ); +_PROTOTYPE( int spin_check, (spin_t *s) ); + +/* Macros. */ + +/* Execute a loop for at least 'u' microseconds, using spin object 's'. + * The body of the loop is guaranteed to be executed at least once. + */ +#define SPIN_FOR(s,u) \ + for (spin_init((s), (u)); spin_check((s)); ) + +/* Return whether spin object 's' timed out after a loop. */ +#define SPIN_TIMEOUT(s) ((s)->s_timeout) + +/* Spin until the given condition becomes true, or 'u' microseconds expired. + * The condition is guaranteed to be checked at least once. + */ +#define SPIN_UNTIL(c,u) do { \ + spin_t s; \ + SPIN_FOR(&s,(u)) \ + if (c) break; \ +} while (0) + +#endif /* _MINIX_SPIN_H */ diff --git a/lib/libsys/Makefile b/lib/libsys/Makefile index cc8e7773c..a4c1dfab2 100644 --- a/lib/libsys/Makefile +++ b/lib/libsys/Makefile @@ -121,7 +121,8 @@ SRCS= \ profile_extern.c \ profile.c \ vprintf.c \ - timers.c + timers.c \ + spin.c CPPFLAGS.sched_start.c+= -I${MINIXSRCDIR} diff --git a/lib/libsys/spin.c b/lib/libsys/spin.c new file mode 100644 index 000000000..43eaca7cb --- /dev/null +++ b/lib/libsys/spin.c @@ -0,0 +1,99 @@ +/* Helper functions that allow driver writers to easily busy-wait (spin) for a + * condition to become satisfied within a certain maximum time span. + */ +/* This implementation first spins without making any system calls for a + * while, and then starts using system calls (specifically, the system call to + * obtain the current time) while spinning. The reason for this is that in + * many cases, the condition to be checked will become satisfied rather + * quickly, and we want to avoid getting descheduled in that case. However, + * after a while, running out of scheduling quantum will cause our priority to + * be lowered, and we can avoid this by voluntarily giving up the CPU, by + * making a system call. + */ +#include "sysutil.h" +#include + +/* Number of microseconds to keep spinning initially, without performing a + * system call. We pick a value somewhat smaller than a typical clock tick. + * Note that for the above reasons, we want to avoid using sys_hz() here. + */ +#define TSC_SPIN 1000 /* in microseconds */ + +/* Internal spin states. */ +enum { + STATE_INIT, /* simply check the condition (once) */ + STATE_BASE_TS, /* get the initial TSC value (once) */ + STATE_TS, /* use the TSC to spin (up to TSC_SPIN us) */ + STATE_UPTIME /* use the clock to spin */ +}; + +PUBLIC void spin_init(spin_t *s, u32_t usecs) +{ + /* Initialize the given spin state structure, set to spin at most the + * given number of microseconds. + */ + s->s_state = STATE_INIT; + s->s_usecs = usecs; + s->s_timeout = FALSE; +} + +PUBLIC int spin_check(spin_t *s) +{ + /* Check whether a timeout has taken place. Return TRUE if the caller + * should continue spinning, and FALSE if a timeout has occurred. The + * implementation assumes that it is okay to spin a little bit too long + * (up to a full clock tick extra). + */ + u64_t cur_tsc, tsc_delta; + clock_t now, micro_delta; + + switch (s->s_state) { + case STATE_INIT: + s->s_state = STATE_BASE_TS; + break; + + case STATE_BASE_TS: + s->s_state = STATE_TS; + read_tsc_64(&s->s_base_tsc); + break; + + case STATE_TS: + read_tsc_64(&cur_tsc); + + tsc_delta = sub64(cur_tsc, s->s_base_tsc); + + micro_delta = tsc_64_to_micros(tsc_delta); + + if (micro_delta >= s->s_usecs) { + s->s_timeout = TRUE; + return FALSE; + } + + if (micro_delta >= TSC_SPIN) { + s->s_usecs -= micro_delta; + getuptime(&s->s_base_uptime); + s->s_state = STATE_UPTIME; + } + + break; + + case STATE_UPTIME: + getuptime(&now); + + /* We assume that sys_hz() caches its return value. */ + micro_delta = ((now - s->s_base_uptime) * 1000 / sys_hz()) * + 1000; + + if (micro_delta >= s->s_usecs) { + s->s_timeout = TRUE; + return FALSE; + } + + break; + + default: + panic("spin_check: invalid state %d", s->s_state); + } + + return TRUE; +}