xref: /minix/minix/lib/libsys/arch/i386/spin.c (revision 9f988b79)
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 			s->s_base_uptime = getticks();
76 			s->s_state = STATE_UPTIME;
77 		}
78 
79 		break;
80 
81 	case STATE_UPTIME:
82 		now = getticks();
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