Annotation of prex/sys/sync/mutex.c, Revision 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. A thread
! 36: * can use mutex_lock() to ensure that global resource is not
! 37: * accessed by other thread. The mutex is effective only the
! 38: * threads 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
! 46: * mutex owner has lower priority than current thread, the
! 47: * priority of mutex owner is boosted to same priority with
! 48: * current thread. If this mutex owner is waiting for another
! 49: * mutex, such related 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:
! 71: #include <kernel.h>
! 72: #include <event.h>
! 73: #include <sched.h>
! 74: #include <kmem.h>
! 75: #include <thread.h>
! 76: #include <task.h>
! 77: #include <sync.h>
! 78:
! 79: /* max mutex count to inherit priority */
! 80: #define MAXINHERIT 10
! 81:
! 82: /* forward declarations */
! 83: static int prio_inherit(thread_t th);
! 84: static void prio_uninherit(thread_t th);
! 85:
! 86: /*
! 87: * Initialize a mutex.
! 88: *
! 89: * If an initialized mutex is reinitialized, undefined
! 90: * behavior results. Technically, we can not detect such
! 91: * error condition here because we can not touch the passed
! 92: * object in kernel.
! 93: */
! 94: int
! 95: mutex_init(mutex_t *mtx)
! 96: {
! 97: mutex_t m;
! 98:
! 99: if ((m = kmem_alloc(sizeof(struct mutex))) == NULL)
! 100: return ENOMEM;
! 101:
! 102: event_init(&m->event, "mutex");
! 103: m->task = cur_task();
! 104: m->owner = NULL;
! 105: m->prio = MIN_PRIO;
! 106: m->magic = MUTEX_MAGIC;
! 107:
! 108: if (umem_copyout(&m, mtx, sizeof(mutex_t))) {
! 109: kmem_free(m);
! 110: return EFAULT;
! 111: }
! 112: return 0;
! 113: }
! 114:
! 115: /*
! 116: * Destroy the specified mutex.
! 117: * The mutex must be unlock state, otherwise it fails with EBUSY.
! 118: */
! 119: int
! 120: mutex_destroy(mutex_t *mtx)
! 121: {
! 122: mutex_t m;
! 123: int err = 0;
! 124:
! 125: sched_lock();
! 126: if (umem_copyin(mtx, &m, sizeof(mutex_t))) {
! 127: err = EFAULT;
! 128: goto out;
! 129: }
! 130: if (!mutex_valid(m)) {
! 131: err = EINVAL;
! 132: goto out;
! 133: }
! 134: if (m->owner || event_waiting(&m->event)) {
! 135: err = EBUSY;
! 136: goto out;
! 137: }
! 138:
! 139: m->magic = 0;
! 140: kmem_free(m);
! 141: out:
! 142: sched_unlock();
! 143: ASSERT(err == 0);
! 144: return err;
! 145: }
! 146:
! 147: /*
! 148: * Copy mutex from user space.
! 149: * If it is not initialized, create new mutex.
! 150: */
! 151: static int
! 152: mutex_copyin(mutex_t *umtx, mutex_t *kmtx)
! 153: {
! 154: mutex_t m;
! 155: int err;
! 156:
! 157: if (umem_copyin(umtx, &m, sizeof(mutex_t)))
! 158: return EFAULT;
! 159:
! 160: if (m == MUTEX_INITIALIZER) {
! 161: /*
! 162: * Allocate new mutex, and retreive its id
! 163: * from the user space.
! 164: */
! 165: if ((err = mutex_init(umtx)))
! 166: return err;
! 167: umem_copyin(umtx, &m, sizeof(mutex_t));
! 168: } else {
! 169: if (!mutex_valid(m))
! 170: return EINVAL;
! 171: }
! 172: *kmtx = m;
! 173: return 0;
! 174: }
! 175:
! 176: /*
! 177: * Lock a mutex.
! 178: *
! 179: * A current thread is blocked if the mutex has already been
! 180: * locked. If current thread receives any exception while
! 181: * waiting mutex, this routine returns with EINTR in order to
! 182: * invoke exception handler. But, POSIX thread assumes this
! 183: * function does NOT return with EINTR. So, system call stub
! 184: * routine in library must call this again if it gets EINTR.
! 185: */
! 186: int
! 187: mutex_lock(mutex_t *mtx)
! 188: {
! 189: mutex_t m;
! 190: int rc, err;
! 191:
! 192: sched_lock();
! 193: if ((err = mutex_copyin(mtx, &m)))
! 194: goto out;
! 195:
! 196: if (m->owner == cur_thread) {
! 197: /*
! 198: * Recursive lock
! 199: */
! 200: m->locks++;
! 201: ASSERT(m->locks != 0);
! 202: } else {
! 203: /*
! 204: * Check whether a target mutex is locked.
! 205: * If the mutex is not locked, this routine
! 206: * returns immediately.
! 207: */
! 208: if (m->owner == NULL)
! 209: m->prio = cur_thread->prio;
! 210: else {
! 211: /*
! 212: * Wait for a mutex.
! 213: */
! 214: cur_thread->wait_mutex = m;
! 215: if ((err = prio_inherit(cur_thread))) {
! 216: cur_thread->wait_mutex = NULL;
! 217: goto out;
! 218: }
! 219: rc = sched_sleep(&m->event);
! 220: cur_thread->wait_mutex = NULL;
! 221: if (rc == SLP_INTR) {
! 222: err = EINTR;
! 223: goto out;
! 224: }
! 225: }
! 226: m->locks = 1;
! 227: }
! 228: m->owner = cur_thread;
! 229: list_insert(&cur_thread->mutexes, &m->link);
! 230: out:
! 231: sched_unlock();
! 232: return err;
! 233: }
! 234:
! 235: /*
! 236: * Try to lock a mutex without blocking.
! 237: */
! 238: int
! 239: mutex_trylock(mutex_t *mtx)
! 240: {
! 241: mutex_t m;
! 242: int err;
! 243:
! 244: sched_lock();
! 245: if ((err = mutex_copyin(mtx, &m)))
! 246: goto out;
! 247: if (m->owner == cur_thread)
! 248: m->locks++;
! 249: else {
! 250: if (m->owner != NULL)
! 251: err = EBUSY;
! 252: else {
! 253: m->locks = 1;
! 254: m->owner = cur_thread;
! 255: list_insert(&cur_thread->mutexes, &m->link);
! 256: }
! 257: }
! 258: out:
! 259: sched_unlock();
! 260: return err;
! 261: }
! 262:
! 263: /*
! 264: * Unlock a mutex.
! 265: * Caller must be a current mutex owner.
! 266: */
! 267: int
! 268: mutex_unlock(mutex_t *mtx)
! 269: {
! 270: mutex_t m;
! 271: int err;
! 272:
! 273: sched_lock();
! 274: if ((err = mutex_copyin(mtx, &m)))
! 275: goto out;
! 276: if (m->owner != cur_thread || m->locks <= 0) {
! 277: err = EPERM;
! 278: goto out;
! 279: }
! 280: if (--m->locks == 0) {
! 281: list_remove(&m->link);
! 282: prio_uninherit(cur_thread);
! 283: /*
! 284: * Change the mutex owner, and make the next
! 285: * owner runnable if it exists.
! 286: */
! 287: m->owner = sched_wakeone(&m->event);
! 288: if (m->owner)
! 289: m->owner->wait_mutex = NULL;
! 290:
! 291: m->prio = m->owner ? m->owner->prio : MIN_PRIO;
! 292: }
! 293: out:
! 294: sched_unlock();
! 295: ASSERT(err == 0);
! 296: return err;
! 297: }
! 298:
! 299: /*
! 300: * Clean up mutex.
! 301: *
! 302: * This is called with scheduling locked when thread is
! 303: * terminated. If a thread is terminated with mutex hold, all
! 304: * waiting threads keeps waiting forever. So, all mutex locked by
! 305: * terminated thread must be unlocked. Even if the terminated
! 306: * thread is waiting some mutex, the inherited priority of other
! 307: * mutex owner is not adjusted.
! 308: */
! 309: void
! 310: mutex_cleanup(thread_t th)
! 311: {
! 312: list_t head;
! 313: mutex_t m;
! 314: thread_t owner;
! 315:
! 316: /*
! 317: * Purge all mutexes held by the thread.
! 318: */
! 319: head = &th->mutexes;
! 320: while (!list_empty(head)) {
! 321: /*
! 322: * Release locked mutex.
! 323: */
! 324: m = list_entry(list_first(head), struct mutex, link);
! 325: m->locks = 0;
! 326: list_remove(&m->link);
! 327: /*
! 328: * Change the mutex owner if other thread
! 329: * is waiting for it.
! 330: */
! 331: owner = sched_wakeone(&m->event);
! 332: if (owner) {
! 333: owner->wait_mutex = NULL;
! 334: m->locks = 1;
! 335: list_insert(&owner->mutexes, &m->link);
! 336: }
! 337: m->owner = owner;
! 338: }
! 339: }
! 340:
! 341: /*
! 342: * This is called with scheduling locked before thread priority
! 343: * is changed.
! 344: */
! 345: void
! 346: mutex_setprio(thread_t th, int prio)
! 347: {
! 348: if (th->wait_mutex && prio < th->prio)
! 349: prio_inherit(th);
! 350: }
! 351:
! 352: /*
! 353: * Inherit priority.
! 354: *
! 355: * To prevent priority inversion, we must ensure the higher
! 356: * priority thread does not wait other lower priority thread. So,
! 357: * raise the priority of mutex owner which blocks the "waiter"
! 358: * thread. If such mutex owner is also waiting for other mutex,
! 359: * that mutex is also processed. Returns EDEALK if it finds
! 360: * deadlock condition.
! 361: */
! 362: static int
! 363: prio_inherit(thread_t waiter)
! 364: {
! 365: mutex_t m = waiter->wait_mutex;
! 366: thread_t owner;
! 367: int count = 0;
! 368:
! 369: do {
! 370: owner = m->owner;
! 371: /*
! 372: * If the owner of relative mutex has already
! 373: * been waiting for the "waiter" thread, it
! 374: * causes a deadlock.
! 375: */
! 376: if (owner == waiter) {
! 377: DPRINTF(("Deadlock! mutex=%x owner=%x waiter=%x\n",
! 378: m, owner, waiter));
! 379: return EDEADLK;
! 380: }
! 381: /*
! 382: * If the priority of the mutex owner is lower
! 383: * than "waiter" thread's, we rise the mutex
! 384: * owner's priority.
! 385: */
! 386: if (owner->prio > waiter->prio) {
! 387: sched_setprio(owner, owner->baseprio, waiter->prio);
! 388: m->prio = waiter->prio;
! 389: }
! 390: /*
! 391: * If the mutex owner is waiting for another
! 392: * mutex, that mutex is also processed.
! 393: */
! 394: m = (mutex_t)owner->wait_mutex;
! 395:
! 396: /* Fail safe... */
! 397: ASSERT(count < MAXINHERIT);
! 398: if (count >= MAXINHERIT)
! 399: break;
! 400:
! 401: } while (m != NULL);
! 402: return 0;
! 403: }
! 404:
! 405: /*
! 406: * Un-inherit priority
! 407: *
! 408: * The priority of specified thread is reset to the base
! 409: * priority. If specified thread locks other mutex and higher
! 410: * priority thread is waiting for it, the priority is kept to
! 411: * that level.
! 412: */
! 413: static void
! 414: prio_uninherit(thread_t th)
! 415: {
! 416: int top_prio;
! 417: list_t head, n;
! 418: mutex_t m;
! 419:
! 420: /* Check if the priority is inherited. */
! 421: if (th->prio == th->baseprio)
! 422: return;
! 423:
! 424: top_prio = th->baseprio;
! 425: /*
! 426: * Find the highest priority thread that is waiting
! 427: * for the thread. This is done by checking all mutexes
! 428: * that the thread locks.
! 429: */
! 430: head = &th->mutexes;
! 431: for (n = list_first(head); n != head; n = list_next(n)) {
! 432: m = list_entry(n, struct mutex, link);
! 433: if (m->prio < top_prio)
! 434: top_prio = m->prio;
! 435: }
! 436: sched_setprio(th, th->baseprio, top_prio);
! 437: }
CVSweb