1/* semaphore.c: FR-V semaphores 2 * 3 * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved. 4 * Written by David Howells (dhowells@redhat.com) 5 * - Derived from lib/rwsem-spinlock.c 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; either version 10 * 2 of the License, or (at your option) any later version. 11 */ 12 13#include <linux/sched.h> 14#include <linux/module.h> 15#include <asm/semaphore.h> 16 17struct sem_waiter { 18 struct list_head list; 19 struct task_struct *task; 20}; 21 22#ifdef CONFIG_DEBUG_SEMAPHORE 23void semtrace(struct semaphore *sem, const char *str) 24{ 25 if (sem->debug) 26 printk("[%d] %s({%d,%d})\n", 27 current->pid, 28 str, 29 sem->counter, 30 list_empty(&sem->wait_list) ? 0 : 1); 31} 32#else 33#define semtrace(SEM,STR) do { } while(0) 34#endif 35 36/* 37 * wait for a token to be granted from a semaphore 38 * - entered with lock held and interrupts disabled 39 */ 40void __down(struct semaphore *sem, unsigned long flags) 41{ 42 struct task_struct *tsk = current; 43 struct sem_waiter waiter; 44 45 semtrace(sem, "Entering __down"); 46 47 /* set up my own style of waitqueue */ 48 waiter.task = tsk; 49 get_task_struct(tsk); 50 51 list_add_tail(&waiter.list, &sem->wait_list); 52 53 /* we don't need to touch the semaphore struct anymore */ 54 spin_unlock_irqrestore(&sem->wait_lock, flags); 55 56 /* wait to be given the semaphore */ 57 set_task_state(tsk, TASK_UNINTERRUPTIBLE); 58 59 for (;;) { 60 if (list_empty(&waiter.list)) 61 break; 62 schedule(); 63 set_task_state(tsk, TASK_UNINTERRUPTIBLE); 64 } 65 66 tsk->state = TASK_RUNNING; 67 semtrace(sem, "Leaving __down"); 68} 69 70EXPORT_SYMBOL(__down); 71 72/* 73 * interruptibly wait for a token to be granted from a semaphore 74 * - entered with lock held and interrupts disabled 75 */ 76int __down_interruptible(struct semaphore *sem, unsigned long flags) 77{ 78 struct task_struct *tsk = current; 79 struct sem_waiter waiter; 80 int ret; 81 82 semtrace(sem,"Entering __down_interruptible"); 83 84 /* set up my own style of waitqueue */ 85 waiter.task = tsk; 86 get_task_struct(tsk); 87 88 list_add_tail(&waiter.list, &sem->wait_list); 89 90 /* we don't need to touch the semaphore struct anymore */ 91 set_task_state(tsk, TASK_INTERRUPTIBLE); 92 93 spin_unlock_irqrestore(&sem->wait_lock, flags); 94 95 /* wait to be given the semaphore */ 96 ret = 0; 97 for (;;) { 98 if (list_empty(&waiter.list)) 99 break; 100 if (unlikely(signal_pending(current))) 101 goto interrupted; 102 schedule(); 103 set_task_state(tsk, TASK_INTERRUPTIBLE); 104 } 105 106 out: 107 tsk->state = TASK_RUNNING; 108 semtrace(sem, "Leaving __down_interruptible"); 109 return ret; 110 111 interrupted: 112 spin_lock_irqsave(&sem->wait_lock, flags); 113 114 if (!list_empty(&waiter.list)) { 115 list_del(&waiter.list); 116 ret = -EINTR; 117 } 118 119 spin_unlock_irqrestore(&sem->wait_lock, flags); 120 if (ret == -EINTR) 121 put_task_struct(current); 122 goto out; 123} 124 125EXPORT_SYMBOL(__down_interruptible); 126 127/* 128 * release a single token back to a semaphore 129 * - entered with lock held and interrupts disabled 130 */ 131void __up(struct semaphore *sem) 132{ 133 struct task_struct *tsk; 134 struct sem_waiter *waiter; 135 136 semtrace(sem,"Entering __up"); 137 138 /* grant the token to the process at the front of the queue */ 139 waiter = list_entry(sem->wait_list.next, struct sem_waiter, list); 140 141 /* We must be careful not to touch 'waiter' after we set ->task = NULL. 142 * It is an allocated on the waiter's stack and may become invalid at 143 * any time after that point (due to a wakeup from another source). 144 */ 145 list_del_init(&waiter->list); 146 tsk = waiter->task; 147 mb(); 148 waiter->task = NULL; 149 wake_up_process(tsk); 150 put_task_struct(tsk); 151 152 semtrace(sem,"Leaving __up"); 153} 154 155EXPORT_SYMBOL(__up); 156