diff options
Diffstat (limited to 'arch/frv/kernel/semaphore.c')
-rw-r--r-- | arch/frv/kernel/semaphore.c | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/arch/frv/kernel/semaphore.c b/arch/frv/kernel/semaphore.c new file mode 100644 index 00000000000..5cba9c1f2b3 --- /dev/null +++ b/arch/frv/kernel/semaphore.c @@ -0,0 +1,156 @@ +/* semaphore.c: FR-V semaphores + * + * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * - Derived from lib/rwsem-spinlock.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/config.h> +#include <linux/sched.h> +#include <linux/module.h> +#include <asm/semaphore.h> + +struct sem_waiter { + struct list_head list; + struct task_struct *task; +}; + +#if SEM_DEBUG +void semtrace(struct semaphore *sem, const char *str) +{ + if (sem->debug) + printk("[%d] %s({%d,%d})\n", + current->pid, + str, + sem->counter, + list_empty(&sem->wait_list) ? 0 : 1); +} +#else +#define semtrace(SEM,STR) do { } while(0) +#endif + +/* + * wait for a token to be granted from a semaphore + * - entered with lock held and interrupts disabled + */ +void __down(struct semaphore *sem, unsigned long flags) +{ + struct task_struct *tsk = current; + struct sem_waiter waiter; + + semtrace(sem, "Entering __down"); + + /* set up my own style of waitqueue */ + waiter.task = tsk; + get_task_struct(tsk); + + list_add_tail(&waiter.list, &sem->wait_list); + + /* we don't need to touch the semaphore struct anymore */ + spin_unlock_irqrestore(&sem->wait_lock, flags); + + /* wait to be given the semaphore */ + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + + for (;;) { + if (list_empty(&waiter.list)) + break; + schedule(); + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + } + + tsk->state = TASK_RUNNING; + semtrace(sem, "Leaving __down"); +} + +EXPORT_SYMBOL(__down); + +/* + * interruptibly wait for a token to be granted from a semaphore + * - entered with lock held and interrupts disabled + */ +int __down_interruptible(struct semaphore *sem, unsigned long flags) +{ + struct task_struct *tsk = current; + struct sem_waiter waiter; + int ret; + + semtrace(sem,"Entering __down_interruptible"); + + /* set up my own style of waitqueue */ + waiter.task = tsk; + get_task_struct(tsk); + + list_add_tail(&waiter.list, &sem->wait_list); + + /* we don't need to touch the semaphore struct anymore */ + set_task_state(tsk, TASK_INTERRUPTIBLE); + + spin_unlock_irqrestore(&sem->wait_lock, flags); + + /* wait to be given the semaphore */ + ret = 0; + for (;;) { + if (list_empty(&waiter.list)) + break; + if (unlikely(signal_pending(current))) + goto interrupted; + schedule(); + set_task_state(tsk, TASK_INTERRUPTIBLE); + } + + out: + tsk->state = TASK_RUNNING; + semtrace(sem, "Leaving __down_interruptible"); + return ret; + + interrupted: + spin_lock_irqsave(&sem->wait_lock, flags); + + if (!list_empty(&waiter.list)) { + list_del(&waiter.list); + ret = -EINTR; + } + + spin_unlock_irqrestore(&sem->wait_lock, flags); + if (ret == -EINTR) + put_task_struct(current); + goto out; +} + +EXPORT_SYMBOL(__down_interruptible); + +/* + * release a single token back to a semaphore + * - entered with lock held and interrupts disabled + */ +void __up(struct semaphore *sem) +{ + struct task_struct *tsk; + struct sem_waiter *waiter; + + semtrace(sem,"Entering __up"); + + /* grant the token to the process at the front of the queue */ + waiter = list_entry(sem->wait_list.next, struct sem_waiter, list); + + /* We must be careful not to touch 'waiter' after we set ->task = NULL. + * It is an allocated on the waiter's stack and may become invalid at + * any time after that point (due to a wakeup from another source). + */ + list_del_init(&waiter->list); + tsk = waiter->task; + mb(); + waiter->task = NULL; + wake_up_process(tsk); + put_task_struct(tsk); + + semtrace(sem,"Leaving __up"); +} + +EXPORT_SYMBOL(__up); |