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