Annotation of prex-old/sys/sync/mutex.c, Revision 1.1.1.1
1.1 nbrk 1: /*-
2: * Copyright (c) 2005-2007, Kohsuke Ohtani
3: * All rights reserved.
4: *
5: * Redistribution and use in source and binary forms, with or without
6: * modification, are permitted provided that the following conditions
7: * are met:
8: * 1. Redistributions of source code must retain the above copyright
9: * notice, this list of conditions and the following disclaimer.
10: * 2. Redistributions in binary form must reproduce the above copyright
11: * notice, this list of conditions and the following disclaimer in the
12: * documentation and/or other materials provided with the distribution.
13: * 3. Neither the name of the author nor the names of any co-contributors
14: * may be used to endorse or promote products derived from this software
15: * without specific prior written permission.
16: *
17: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20: * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27: * SUCH DAMAGE.
28: */
29:
30: /*
31: * mutex.c - mutual exclusion service.
32: */
33:
34: /*
35: * A mutex is used to protect un-sharable resources.
36: * A thread can use mutex_lock() to ensure that global resource is not
37: * accessed by other thread. The mutex is effective only the threads
38: * belonging to the same task.
39: *
40: * Prex will change the thread priority to prevent priority inversion.
41: *
42: * <Priority inheritance>
43: * The priority is changed at the following conditions.
44: *
45: * 1. When the current thread can not lock the mutex and its mutex
46: * owner has lower priority than current thread, the priority
47: * of mutex owner is boosted to same priority with current thread.
48: * If this mutex owner is waiting for another mutex, such related
49: * mutexes are also processed.
50: *
51: * 2. When the current thread unlocks the mutex and its priority
52: * has already been inherited, the current priority is reset.
53: * In this time, the current priority is changed to the highest
54: * priority among the threads waiting for the mutexes locked by
55: * current thread.
56: *
57: * 3. When the thread priority is changed by user request, the
58: * inherited thread's priority is changed.
59: *
60: * <Limitation>
61: *
62: * 1. If the priority is changed by user request, the priority
63: * recomputation is done only when the new priority is higher
64: * than old priority. The inherited priority is reset to base
65: * priority when the mutex is unlocked.
66: *
67: * 2. Even if thread is killed with mutex waiting, the related
68: * priority is not adjusted.
69: *
70: * <Important>
71: * Since this implementation does not support recursive lock, a thread
72: * can not lock the same mutex twice.
73: */
74:
75: #include <kernel.h>
76: #include <event.h>
77: #include <sched.h>
78: #include <kmem.h>
79: #include <thread.h>
80: #include <task.h>
81: #include <sync.h>
82:
83: /* max mutex count to inherit priority */
84: #define MAXINHERIT 10
85:
86: /* forward declarations */
87: static int prio_inherit(thread_t th);
88: static void prio_uninherit(thread_t th);
89:
90: /*
91: * Initialize a mutex.
92: *
93: * If an initialized mutex is reinitialized, undefined behavior
94: * results. Technically, we can not detect such error condition
95: * here because we can not touch the passed object in kernel.
96: */
97: int
98: mutex_init(mutex_t *mtx)
99: {
100: mutex_t m;
101:
102: if ((m = kmem_alloc(sizeof(struct mutex))) == NULL)
103: return ENOMEM;
104:
105: event_init(&m->event, "mutex");
106: m->task = cur_task();
107: m->owner = NULL;
108: m->prio = MIN_PRIO;
109: m->magic = MUTEX_MAGIC;
110:
111: if (umem_copyout(&m, mtx, sizeof(mutex_t))) {
112: kmem_free(m);
113: return EFAULT;
114: }
115: return 0;
116: }
117:
118: /*
119: * Destroy the specified mutex.
120: * The mutex must be unlock state, otherwise it fails with EBUSY.
121: */
122: int
123: mutex_destroy(mutex_t *mtx)
124: {
125: mutex_t m;
126: int err = 0;
127:
128: sched_lock();
129: if (umem_copyin(mtx, &m, sizeof(mutex_t))) {
130: err = EFAULT;
131: } else if (!mutex_valid(m)) {
132: err = EINVAL;
133: } else if (m->owner || event_waiting(&m->event)) {
134: err = EBUSY;
135: } else {
136: m->magic = 0;
137: kmem_free(m);
138: }
139: sched_unlock();
140: ASSERT(err == 0);
141: return err;
142: }
143:
144: /*
145: * Copy mutex from user space.
146: * If it is not initialized, create new mutex.
147: * @umtx: pointer to mutex in user space.
148: * @kmtx: pointer to mutex in kernel space.
149: */
150: static int
151: mutex_copyin(mutex_t *umtx, mutex_t *kmtx)
152: {
153: mutex_t m;
154: int err;
155:
156: if (umem_copyin(umtx, &m, sizeof(mutex_t)))
157: return EFAULT;
158:
159: if (m == MUTEX_INITIALIZER) {
160: /*
161: * Allocate mutex.
162: */
163: if ((err = mutex_init(umtx)))
164: return err;
165: umem_copyin(umtx, &m, sizeof(mutex_t));
166: } else {
167: if (!mutex_valid(m))
168: return EINVAL;
169: }
170: *kmtx = m;
171: return 0;
172: }
173:
174: /*
175: * Lock a mutex.
176: *
177: * A current thread is blocked if the mutex has already been locked.
178: * If current thread receives any exception while waiting mutex, this
179: * routine returns with EINTR in order to invoke exception handler.
180: * But, POSIX thread assumes this function does NOT return with EINTR.
181: * So, system call stub routine in library must call this again if
182: * it gets EINTR.
183: */
184: int
185: mutex_lock(mutex_t *mtx)
186: {
187: mutex_t m;
188: int rc, err;
189:
190: sched_lock();
191: if ((err = mutex_copyin(mtx, &m)))
192: goto out;
193:
194: if (m->owner == cur_thread) {
195: /*
196: * Recursive lock
197: */
198: m->lock_count++;
199: } else {
200: /*
201: * Check whether a target mutex is locked.
202: * If the mutex is not locked, this routine
203: * returns immediately.
204: */
205: if (m->owner == NULL)
206: m->prio = cur_thread->prio;
207: else {
208: /*
209: * Wait for a mutex.
210: */
211: cur_thread->wait_mutex = m;
212: if ((err = prio_inherit(cur_thread))) {
213: cur_thread->wait_mutex = NULL;
214: goto out;
215: }
216: rc = sched_sleep(&m->event);
217: cur_thread->wait_mutex = NULL;
218: if (rc == SLP_INTR) {
219: err = EINTR;
220: goto out;
221: }
222: }
223: m->lock_count = 1;
224: }
225: m->owner = cur_thread;
226: list_insert(&cur_thread->mutexes, &m->link);
227: out:
228: sched_unlock();
229: return err;
230: }
231:
232: /*
233: * Try to lock a mutex without blocking.
234: */
235: int
236: mutex_trylock(mutex_t *mtx)
237: {
238: mutex_t m;
239: int err;
240:
241: sched_lock();
242: if ((err = mutex_copyin(mtx, &m)))
243: goto out;
244: if (m->owner == cur_thread)
245: m->lock_count++;
246: else {
247: if (m->owner != NULL)
248: err = EBUSY;
249: else {
250: m->lock_count = 1;
251: m->owner = cur_thread;
252: list_insert(&cur_thread->mutexes, &m->link);
253: }
254: }
255: out:
256: sched_unlock();
257: return err;
258: }
259:
260: /*
261: * Unlock a mutex.
262: * Caller must be a current mutex owner.
263: */
264: int
265: mutex_unlock(mutex_t *mtx)
266: {
267: mutex_t m;
268: int err;
269:
270: sched_lock();
271: if ((err = mutex_copyin(mtx, &m)))
272: goto out;
273: if (m->owner != cur_thread || m->lock_count <= 0) {
274: err = EPERM;
275: goto out;
276: }
277: if (--m->lock_count == 0) {
278: list_remove(&m->link);
279: prio_uninherit(cur_thread);
280: /*
281: * Change the mutex owner, and make the next
282: * owner runnable if it exists.
283: */
284: m->owner = sched_wakeone(&m->event);
285: if (m->owner)
286: m->owner->wait_mutex = NULL;
287:
288: m->prio = m->owner ? m->owner->prio : MIN_PRIO;
289: }
290: out:
291: sched_unlock();
292: ASSERT(err == 0);
293: return err;
294: }
295:
296: /*
297: * Clean up mutex.
298: *
299: * This is called with scheduling locked when thread is terminated.
300: * If a thread is terminated with mutex hold, all waiting threads
301: * keeps waiting forever. So, all mutex locked by terminated thread
302: * must be unlocked. Even if the terminated thread is waiting some
303: * mutex, the inherited priority of other mutex owner is not adjusted.
304: */
305: void
306: mutex_cleanup(thread_t th)
307: {
308: list_t head;
309: mutex_t m;
310: thread_t owner;
311:
312: /*
313: * Purge all mutexes held by the thread.
314: */
315: head = &th->mutexes;
316: while (!list_empty(head)) {
317: /*
318: * Release locked mutex.
319: */
320: m = list_entry(list_first(head), struct mutex, link);
321: m->lock_count = 0;
322: list_remove(&m->link);
323: /*
324: * Change the mutex owner if other thread
325: * is waiting for it.
326: */
327: owner = sched_wakeone(&m->event);
328: if (owner) {
329: owner->wait_mutex = NULL;
330: m->lock_count = 1;
331: list_insert(&owner->mutexes, &m->link);
332: }
333: m->owner = owner;
334: }
335: }
336:
337: /*
338: * This is called with scheduling locked before thread priority
339: * is changed.
340: */
341: void
342: mutex_setprio(thread_t th, int prio)
343: {
344: if (th->wait_mutex && prio < th->prio)
345: prio_inherit(th);
346: }
347:
348: /*
349: * Inherit priority.
350: * @waiter: thread that is about to wait a mutex.
351: *
352: * To prevent priority inversion, we must ensure the higher priority
353: * thread does not wait other lower priority thread. So, raise the
354: * priority of mutex owner which blocks the "waiter" thread. If such
355: * mutex owner is also waiting for other mutex, that mutex is also
356: * processed.
357: * Returns EDEALK if it finds deadlock condition.
358: */
359: static int
360: prio_inherit(thread_t waiter)
361: {
362: mutex_t m = waiter->wait_mutex;
363: thread_t owner;
364: int count = 0;
365:
366: do {
367: owner = m->owner;
368: /*
369: * If the owner of relative mutex has already
370: * been waiting for the "waiter" thread, it
371: * causes a deadlock.
372: */
373: if (owner == waiter) {
374: printk("Deadlock! mutex=%x owner=%x waiter=%x\n",
375: m, owner, waiter);
376: return EDEADLK;
377: }
378: /*
379: * If the priority of the mutex owner is lower
380: * than "waiter" thread's, we rise the mutex
381: * owner's priority.
382: */
383: if (owner->prio > waiter->prio) {
384: sched_setprio(owner, owner->base_prio, waiter->prio);
385: m->prio = waiter->prio;
386: }
387: /*
388: * If the mutex owner is waiting for another
389: * mutex, that mutex is also processed.
390: */
391: m = (mutex_t)owner->wait_mutex;
392:
393: /* Fail safe... */
394: ASSERT(count < MAXINHERIT);
395: if (count >= MAXINHERIT)
396: break;
397:
398: } while (m != NULL);
399: return 0;
400: }
401:
402: /*
403: * Un-inherit priority
404: *
405: * The priority of specified thread is reset to the base priority.
406: * If specified thread locks other mutex and higher priority thread
407: * is waiting for it, the priority is kept to that level.
408: */
409: static void
410: prio_uninherit(thread_t th)
411: {
412: int top_prio;
413: list_t head, n;
414: mutex_t m;
415:
416: /* Check if the priority is inherited. */
417: if (th->prio == th->base_prio)
418: return;
419:
420: top_prio = th->base_prio;
421: /*
422: * Find the highest priority thread that is waiting
423: * for the thread. This is done by checking all mutexes
424: * that the thread locks.
425: */
426: head = &th->mutexes;
427: for (n = list_first(head); n != head; n = list_next(n)) {
428: m = list_entry(n, struct mutex, link);
429: if (m->prio < top_prio)
430: top_prio = m->prio;
431: }
432: sched_setprio(th, th->base_prio, top_prio);
433: }
CVSweb