summaryrefslogtreecommitdiff
path: root/arch/frv/kernel/semaphore.c
blob: 5cba9c1f2b3dcf2c01579528f878f44ce88fbc90 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
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);