133 lines
		
	
	
	
		
			3.7 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			133 lines
		
	
	
	
		
			3.7 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
|   | /*
 | ||
|  |  *  Generic semaphore code. Buyer beware. Do your own | ||
|  |  * specific changes in <asm/semaphore-helper.h> | ||
|  |  */ | ||
|  | 
 | ||
|  | #include <linux/sched.h>
 | ||
|  | #include <linux/init.h>
 | ||
|  | #include <asm/semaphore-helper.h>
 | ||
|  | 
 | ||
|  | #ifndef CONFIG_RMW_INSNS
 | ||
|  | spinlock_t semaphore_wake_lock; | ||
|  | #endif
 | ||
|  | 
 | ||
|  | /*
 | ||
|  |  * Semaphores are implemented using a two-way counter: | ||
|  |  * The "count" variable is decremented for each process | ||
|  |  * that tries to sleep, while the "waking" variable is | ||
|  |  * incremented when the "up()" code goes to wake up waiting | ||
|  |  * processes. | ||
|  |  * | ||
|  |  * Notably, the inline "up()" and "down()" functions can | ||
|  |  * efficiently test if they need to do any extra work (up | ||
|  |  * needs to do something only if count was negative before | ||
|  |  * the increment operation. | ||
|  |  * | ||
|  |  * waking_non_zero() (from asm/semaphore.h) must execute | ||
|  |  * atomically. | ||
|  |  * | ||
|  |  * When __up() is called, the count was negative before | ||
|  |  * incrementing it, and we need to wake up somebody. | ||
|  |  * | ||
|  |  * This routine adds one to the count of processes that need to | ||
|  |  * wake up and exit.  ALL waiting processes actually wake up but | ||
|  |  * only the one that gets to the "waking" field first will gate | ||
|  |  * through and acquire the semaphore.  The others will go back | ||
|  |  * to sleep. | ||
|  |  * | ||
|  |  * Note that these functions are only called when there is | ||
|  |  * contention on the lock, and as such all this is the | ||
|  |  * "non-critical" part of the whole semaphore business. The | ||
|  |  * critical part is the inline stuff in <asm/semaphore.h> | ||
|  |  * where we want to avoid any extra jumps and calls. | ||
|  |  */ | ||
|  | void __up(struct semaphore *sem) | ||
|  | { | ||
|  | 	wake_one_more(sem); | ||
|  | 	wake_up(&sem->wait); | ||
|  | } | ||
|  | 
 | ||
|  | /*
 | ||
|  |  * Perform the "down" function.  Return zero for semaphore acquired, | ||
|  |  * return negative for signalled out of the function. | ||
|  |  * | ||
|  |  * If called from __down, the return is ignored and the wait loop is | ||
|  |  * not interruptible.  This means that a task waiting on a semaphore | ||
|  |  * using "down()" cannot be killed until someone does an "up()" on | ||
|  |  * the semaphore. | ||
|  |  * | ||
|  |  * If called from __down_interruptible, the return value gets checked | ||
|  |  * upon return.  If the return value is negative then the task continues | ||
|  |  * with the negative value in the return register (it can be tested by | ||
|  |  * the caller). | ||
|  |  * | ||
|  |  * Either form may be used in conjunction with "up()". | ||
|  |  * | ||
|  |  */ | ||
|  | 
 | ||
|  | 
 | ||
|  | #define DOWN_HEAD(task_state)						\
 | ||
|  | 									\ | ||
|  | 									\ | ||
|  | 	current->state = (task_state);					\ | ||
|  | 	add_wait_queue(&sem->wait, &wait);				\ | ||
|  | 									\ | ||
|  | 	/*								\
 | ||
|  | 	 * Ok, we're set up.  sem->count is known to be less than zero	\ | ||
|  | 	 * so we must wait.						\ | ||
|  | 	 *								\ | ||
|  | 	 * We can let go the lock for purposes of waiting.		\ | ||
|  | 	 * We re-acquire it after awaking so as to protect		\ | ||
|  | 	 * all semaphore operations.					\ | ||
|  | 	 *								\ | ||
|  | 	 * If "up()" is called before we call waking_non_zero() then	\ | ||
|  | 	 * we will catch it right away.  If it is called later then	\ | ||
|  | 	 * we will have to go through a wakeup cycle to catch it.	\ | ||
|  | 	 *								\ | ||
|  | 	 * Multiple waiters contend for the semaphore lock to see	\ | ||
|  | 	 * who gets to gate through and who has to wait some more.	\ | ||
|  | 	 */								\ | ||
|  | 	for (;;) { | ||
|  | 
 | ||
|  | #define DOWN_TAIL(task_state)			\
 | ||
|  | 		current->state = (task_state);	\ | ||
|  | 	}					\ | ||
|  | 	current->state = TASK_RUNNING;		\ | ||
|  | 	remove_wait_queue(&sem->wait, &wait); | ||
|  | 
 | ||
|  | void __sched __down(struct semaphore * sem) | ||
|  | { | ||
|  | 	DECLARE_WAITQUEUE(wait, current); | ||
|  | 
 | ||
|  | 	DOWN_HEAD(TASK_UNINTERRUPTIBLE) | ||
|  | 	if (waking_non_zero(sem)) | ||
|  | 		break; | ||
|  | 	schedule(); | ||
|  | 	DOWN_TAIL(TASK_UNINTERRUPTIBLE) | ||
|  | } | ||
|  | 
 | ||
|  | int __sched __down_interruptible(struct semaphore * sem) | ||
|  | { | ||
|  | 	DECLARE_WAITQUEUE(wait, current); | ||
|  | 	int ret = 0; | ||
|  | 
 | ||
|  | 	DOWN_HEAD(TASK_INTERRUPTIBLE) | ||
|  | 
 | ||
|  | 	ret = waking_non_zero_interruptible(sem, current); | ||
|  | 	if (ret) | ||
|  | 	{ | ||
|  | 		if (ret == 1) | ||
|  | 			/* ret != 0 only if we get interrupted -arca */ | ||
|  | 			ret = 0; | ||
|  | 		break; | ||
|  | 	} | ||
|  | 	schedule(); | ||
|  | 	DOWN_TAIL(TASK_INTERRUPTIBLE) | ||
|  | 	return ret; | ||
|  | } | ||
|  | 
 | ||
|  | int __down_trylock(struct semaphore * sem) | ||
|  | { | ||
|  | 	return waking_non_zero_trylock(sem); | ||
|  | } |