]> Zhao Yanbai Git Server - minix.git/commitdiff
libsys: add standard condition spinning primitives
authorDavid van Moolenbroek <david@minix3.org>
Mon, 12 Jul 2010 23:14:40 +0000 (23:14 +0000)
committerDavid van Moolenbroek <david@minix3.org>
Mon, 12 Jul 2010 23:14:40 +0000 (23:14 +0000)
12 files changed:
drivers/at_wini/at_wini.c
drivers/at_wini/at_wini.h
drivers/floppy/floppy.c
drivers/fxp/fxp.c
drivers/rtl8139/rtl8139.c
drivers/rtl8169/rtl8169.c
drivers/ti1225/ti1225.c
include/Makefile
include/minix/drivers.h
include/minix/spin.h [new file with mode: 0644]
lib/libsys/Makefile
lib/libsys/spin.c [new file with mode: 0644]

index 4cbd3f22b3ca530489bc78d4fb5333502f33c700..3a2b965165e6f027b5327289d44e01556a1a352c 100644 (file)
@@ -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,
index aefa1826b4cd28defb6841d543dc6325e98a614c..95a26bbeb64733f382bfe9896179c878b9e051d9 100644 (file)
 #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 */
index 2a706e5a1c4ec3b4256efd247e2ccb6783a3b7e8..d79c0fe9c83e6ee0ab92bd9e580c2c6d4d82e96a 100644 (file)
 #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 */
 }
 
 /*===========================================================================*
index 3271c6620e350bae6446985c6d9b75f30fcdf573..544ba53bce264b4ccca902e3432312076684da13 100644 (file)
@@ -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");
index c7cef8c84f87dbac0e0073d482bb52ec54fe49e3..c7c182e7494dac327ba27a246b74a53212a3fbe3 100644 (file)
@@ -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");
 }
index 7141d9a48d1bd87241f9d789bd4802b74644c828..f802992db51c272438ff84a19da848e7bd27c0d6 100644 (file)
@@ -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)
index 1d590c88b22cf64bdbae219a4b9c00d7a1fdf1f2..81ef98ea345aef051d2567c9b28e7a90440351bf 100644 (file)
@@ -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))
        {
index 13f1855e255365c2e575186fedc826c642d404d9..f17616515242cd13e0b9635936527f297fd58fee 100644 (file)
@@ -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
index fd2ae6cb6a8b6a2f803a09cd7bccce9892c1be06..39a5b8320cfd771ab78888c918f2191370ec9ad8 100644 (file)
@@ -22,6 +22,7 @@
 #include <minix/syslib.h>
 #include <minix/sysutil.h>
 #include <minix/timers.h>
+#include <minix/spin.h>
 #include <minix/bitmap.h>
 
 #include <machine/interrupt.h> /* IRQ vectors and miscellaneous ports */
diff --git a/include/minix/spin.h b/include/minix/spin.h
new file mode 100644 (file)
index 0000000..c445e0e
--- /dev/null
@@ -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 */
index cc8e7773cbb7db6178fcba83a5766b16e4fc0975..a4c1dfab280a6963fad293f3c8e5e2799823ee67 100644 (file)
@@ -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 (file)
index 0000000..43eaca7
--- /dev/null
@@ -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 <minix/spin.h>
+
+/* 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;
+}