⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 semaphore.c

📁 linux-2.4.29操作系统的源码
💻 C
字号:
/* * Copyright (C) 1999, 2001, 02, 03 Ralf Baechle * * Heavily inspired by the Alpha implementation */#include <linux/config.h>#include <linux/errno.h>#include <linux/module.h>#include <linux/sched.h>#ifndef CONFIG_CPU_HAS_LLDSCD/* * On machines without lld/scd we need a spinlock to make the manipulation of * sem->count and sem->waking atomic.  Scalability isn't an issue because * this lock is used on UP only so it's just an empty variable. */spinlock_t semaphore_lock = SPIN_LOCK_UNLOCKED;EXPORT_SYMBOL(semaphore_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() 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_wakeup(struct semaphore *sem){	wake_up(&sem->wait);}EXPORT_SYMBOL(__up_wakeup);#ifdef CONFIG_CPU_HAS_LLSCstatic inline int waking_non_zero(struct semaphore *sem){	int ret, tmp;	__asm__ __volatile__(	"1:	ll	%1, %2			# waking_non_zero	\n"	"	blez	%1, 2f						\n"	"	subu	%0, %1, 1					\n"	"	sc	%0, %2						\n"	"	beqz	%0, 1b						\n"	"2:								\n"	: "=r" (ret), "=r" (tmp), "+m" (sem->waking)	: "0" (0));	return ret;}#else /* !CONFIG_CPU_HAS_LLSC */static inline int waking_non_zero(struct semaphore *sem){	unsigned long flags;	int waking, ret = 0;	spin_lock_irqsave(&semaphore_lock, flags);	waking = atomic_read(&sem->waking);	if (waking > 0) {		atomic_set(&sem->waking, waking - 1);		ret = 1;	}	spin_unlock_irqrestore(&semaphore_lock, flags);	return ret;}#endif /* !CONFIG_CPU_HAS_LLSC *//* * 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()". */void __down_failed(struct semaphore * sem){	struct task_struct *tsk = current;	wait_queue_t wait;	init_waitqueue_entry(&wait, tsk);	__set_current_state(TASK_UNINTERRUPTIBLE);	add_wait_queue_exclusive(&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 (;;) {		if (waking_non_zero(sem))			break;		schedule();		__set_current_state(TASK_UNINTERRUPTIBLE);	}	__set_current_state(TASK_RUNNING);	remove_wait_queue(&sem->wait, &wait);}EXPORT_SYMBOL(__down_failed);#ifdef CONFIG_CPU_HAS_LLDSCD/* * waking_non_zero_interruptible: *	1	got the lock *	0	go to sleep *	-EINTR	interrupted * * We must undo the sem->count down_interruptible decrement * simultaneously and atomically with the sem->waking adjustment, * otherwise we can race with wake_one_more. * * This is accomplished by doing a 64-bit lld/scd on the 2 32-bit words. * * This is crazy.  Normally it's strictly forbidden to use 64-bit operations * in the 32-bit MIPS kernel.  In this case it's however ok because if an * interrupt has destroyed the upper half of registers sc will fail. * Note also that this will not work for MIPS32 CPUs! * * Pseudocode: * * If(sem->waking > 0) { *	Decrement(sem->waking) *	Return(SUCCESS) * } else If(signal_pending(tsk)) { *	Increment(sem->count) *	Return(-EINTR) * } else { *	Return(SLEEP) * } */static inline intwaking_non_zero_interruptible(struct semaphore *sem, struct task_struct *tsk){	long ret, tmp;	__asm__ __volatile__(	"	.set	push		# waking_non_zero_interruptible	\n"	"	.set	mips3						\n"	"	.set	noat						\n"	"0:	lld	%1, %2						\n"	"	li	%0, 0						\n"	"	sll	$1, %1, 0					\n"	"	blez	$1, 1f						\n"	"	daddiu	%1, %1, -1					\n"	"	li	%0, 1						\n"	"	b	2f						\n"	"1:	beqz	%3, 2f						\n"	"	li	%0, %4						\n"	"	dli	$1, 0x0000000100000000				\n"	"	daddu	%1, %1, $1					\n"	"2:	scd	%1, %2						\n"	"	beqz	%1, 0b						\n"	"	.set	pop						\n"	: "=&r" (ret), "=&r" (tmp), "=m" (*sem)	: "r" (signal_pending(tsk)), "i" (-EINTR));	return ret;}#else /* !CONFIG_CPU_HAS_LLDSCD */static inline int waking_non_zero_interruptible(struct semaphore *sem,						struct task_struct *tsk){	int waking, pending, ret = 0;	unsigned long flags;	pending = signal_pending(tsk);	spin_lock_irqsave(&semaphore_lock, flags);	waking = atomic_read(&sem->waking);	if (waking > 0) {		atomic_set(&sem->waking, waking - 1);		ret = 1;	} else if (pending) {		atomic_set(&sem->count, atomic_read(&sem->count) + 1);		ret = -EINTR;	}	spin_unlock_irqrestore(&semaphore_lock, flags);	return ret;}#endif /* !CONFIG_CPU_HAS_LLDSCD */int __down_failed_interruptible(struct semaphore * sem){	struct task_struct *tsk = current;	wait_queue_t wait;	int ret = 0;	init_waitqueue_entry(&wait, tsk);	__set_current_state(TASK_INTERRUPTIBLE);	add_wait_queue_exclusive(&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 (;;) {		ret = waking_non_zero_interruptible(sem, tsk);		if (ret) {			if (ret == 1)				/* ret != 0 only if we get interrupted -arca */				ret = 0;			break;		}		schedule();		__set_current_state(TASK_INTERRUPTIBLE);	}	__set_current_state(TASK_RUNNING);	remove_wait_queue(&sem->wait, &wait);	return ret;}EXPORT_SYMBOL(__down_failed_interruptible);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -