101 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			101 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* 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>
 | |
| #include <minix/minlib.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 */
 | |
| };
 | |
| 
 | |
| 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;
 | |
| }
 | |
| 
 | |
| 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 = 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;
 | |
| 			getticks(&s->s_base_uptime);
 | |
| 			s->s_state = STATE_UPTIME;
 | |
| 		}
 | |
| 
 | |
| 		break;
 | |
| 
 | |
| 	case STATE_UPTIME:
 | |
| 		getticks(&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;
 | |
| }
 | 
