|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. ! 3: * ! 4: * @APPLE_LICENSE_HEADER_START@ ! 5: * ! 6: * The contents of this file constitute Original Code as defined in and ! 7: * are subject to the Apple Public Source License Version 1.1 (the ! 8: * "License"). You may not use this file except in compliance with the ! 9: * License. Please obtain a copy of the License at ! 10: * http://www.apple.com/publicsource and read it before using this file. ! 11: * ! 12: * This Original Code and all software distributed under the License are ! 13: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER ! 14: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, ! 15: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, ! 16: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the ! 17: * License for the specific language governing rights and limitations ! 18: * under the License. ! 19: * ! 20: * @APPLE_LICENSE_HEADER_END@ ! 21: */ ! 22: /* ! 23: * @OSF_COPYRIGHT@ ! 24: */ ! 25: /* ! 26: * Mach Operating System ! 27: * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University ! 28: * All Rights Reserved. ! 29: * ! 30: * Permission to use, copy, modify and distribute this software and its ! 31: * documentation is hereby granted, provided that both the copyright ! 32: * notice and this permission notice appear in all copies of the ! 33: * software, derivative works or modified versions, and any portions ! 34: * thereof, and that both notices appear in supporting documentation. ! 35: * ! 36: * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" ! 37: * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR ! 38: * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. ! 39: * ! 40: * Carnegie Mellon requests users of this software to return to ! 41: * ! 42: * Software Distribution Coordinator or [email protected] ! 43: * School of Computer Science ! 44: * Carnegie Mellon University ! 45: * Pittsburgh PA 15213-3890 ! 46: * ! 47: * any improvements or extensions that they make and grant Carnegie Mellon ! 48: * the rights to redistribute these changes. ! 49: */ ! 50: /* ! 51: * File: kern/lock.c ! 52: * Author: Avadis Tevanian, Jr., Michael Wayne Young ! 53: * Date: 1985 ! 54: * ! 55: * Locking primitives implementation ! 56: */ ! 57: ! 58: #include <cpus.h> ! 59: #include <mach_kdb.h> ! 60: #include <mach_ldebug.h> ! 61: ! 62: #include <kern/lock.h> ! 63: #include <kern/etap_macros.h> ! 64: #include <kern/misc_protos.h> ! 65: #include <kern/thread.h> ! 66: #include <kern/sched_prim.h> ! 67: #include <kern/xpr.h> ! 68: #include <kern/debug.h> ! 69: #include <string.h> ! 70: ! 71: #if MACH_KDB ! 72: #include <ddb/db_command.h> ! 73: #include <ddb/db_output.h> ! 74: #include <ddb/db_sym.h> ! 75: #include <ddb/db_print.h> ! 76: #endif /* MACH_KDB */ ! 77: ! 78: #ifdef __ppc__ ! 79: #include <ppc/Firmware.h> ! 80: #include <ppc/POWERMAC/mp/MPPlugIn.h> ! 81: #endif ! 82: ! 83: #define ANY_LOCK_DEBUG (USLOCK_DEBUG || LOCK_DEBUG || MUTEX_DEBUG) ! 84: ! 85: /* ! 86: * Some portions of the lock debugging code must run with ! 87: * interrupts disabled. This can be machine-dependent, ! 88: * but we don't have any good hooks for that at the moment. ! 89: * If your architecture is different, add a machine-dependent ! 90: * ifdef here for these macros. XXX ! 91: */ ! 92: ! 93: #define DISABLE_INTERRUPTS(s) s = ml_set_interrupts_enabled(FALSE) ! 94: #define ENABLE_INTERRUPTS(s) (void)ml_set_interrupts_enabled(s) ! 95: ! 96: #if NCPUS > 1 ! 97: /* Time we loop without holding the interlock. ! 98: * The former is for when we cannot sleep, the latter ! 99: * for when our thread can go to sleep (loop less) ! 100: * we shouldn't retake the interlock at all frequently ! 101: * if we cannot go to sleep, since it interferes with ! 102: * any other processors. In particular, 100 is too small ! 103: * a number for powerpc MP systems because of cache ! 104: * coherency issues and differing lock fetch times between ! 105: * the processors ! 106: */ ! 107: unsigned int lock_wait_time[2] = { (unsigned int)-1, 100 } ; ! 108: #else /* NCPUS > 1 */ ! 109: ! 110: /* ! 111: * It is silly to spin on a uni-processor as if we ! 112: * thought something magical would happen to the ! 113: * want_write bit while we are executing. ! 114: */ ! 115: ! 116: unsigned int lock_wait_time[2] = { 0, 0 }; ! 117: #endif /* NCPUS > 1 */ ! 118: ! 119: /* Forwards */ ! 120: ! 121: #if MACH_KDB ! 122: void db_print_simple_lock( ! 123: simple_lock_t addr); ! 124: ! 125: void db_print_mutex( ! 126: mutex_t * addr); ! 127: #endif /* MACH_KDB */ ! 128: ! 129: ! 130: #if USLOCK_DEBUG ! 131: /* ! 132: * Perform simple lock checks. ! 133: */ ! 134: int uslock_check = 1; ! 135: int max_lock_loops = 100000000; ! 136: decl_simple_lock_data(extern , printf_lock) ! 137: decl_simple_lock_data(extern , panic_lock) ! 138: #if MACH_KDB && NCPUS > 1 ! 139: decl_simple_lock_data(extern , kdb_lock) ! 140: #endif /* MACH_KDB && NCPUS >1 */ ! 141: #endif /* USLOCK_DEBUG */ ! 142: ! 143: ! 144: /* ! 145: * We often want to know the addresses of the callers ! 146: * of the various lock routines. However, this information ! 147: * is only used for debugging and statistics. ! 148: */ ! 149: typedef void *pc_t; ! 150: #define INVALID_PC ((void *) VM_MAX_KERNEL_ADDRESS) ! 151: #define INVALID_THREAD ((void *) VM_MAX_KERNEL_ADDRESS) ! 152: #if ANY_LOCK_DEBUG || ETAP_LOCK_TRACE ! 153: #define OBTAIN_PC(pc,l) ((pc) = (void *) GET_RETURN_PC(&(l))) ! 154: #else /* ANY_LOCK_DEBUG || ETAP_LOCK_TRACE */ ! 155: #ifdef lint ! 156: /* ! 157: * Eliminate lint complaints about unused local pc variables. ! 158: */ ! 159: #define OBTAIN_PC(pc,l) ++pc ! 160: #else /* lint */ ! 161: #define OBTAIN_PC(pc,l) ! 162: #endif /* lint */ ! 163: #endif /* USLOCK_DEBUG || ETAP_LOCK_TRACE */ ! 164: ! 165: ! 166: /* #ifndef USIMPLE_LOCK_CALLS ! 167: * The i386 production version of usimple_locks isn't ready yet. ! 168: */ ! 169: /* ! 170: * Portable lock package implementation of usimple_locks. ! 171: */ ! 172: ! 173: #if ETAP_LOCK_TRACE ! 174: #define ETAPCALL(stmt) stmt ! 175: void etap_simplelock_init(simple_lock_t, etap_event_t); ! 176: void etap_simplelock_unlock(simple_lock_t); ! 177: void etap_simplelock_hold(simple_lock_t, pc_t, etap_time_t); ! 178: etap_time_t etap_simplelock_miss(simple_lock_t); ! 179: ! 180: void etap_mutex_init(mutex_t*, etap_event_t); ! 181: void etap_mutex_unlock(mutex_t*); ! 182: void etap_mutex_hold(mutex_t*, pc_t, etap_time_t); ! 183: etap_time_t etap_mutex_miss(mutex_t*); ! 184: #else /* ETAP_LOCK_TRACE */ ! 185: #define ETAPCALL(stmt) ! 186: #endif /* ETAP_LOCK_TRACE */ ! 187: ! 188: #if USLOCK_DEBUG ! 189: #define USLDBG(stmt) stmt ! 190: void usld_lock_init(usimple_lock_t, etap_event_t); ! 191: void usld_lock_pre(usimple_lock_t, pc_t); ! 192: void usld_lock_post(usimple_lock_t, pc_t); ! 193: void usld_unlock(usimple_lock_t, pc_t); ! 194: void usld_lock_try_pre(usimple_lock_t, pc_t); ! 195: void usld_lock_try_post(usimple_lock_t, pc_t); ! 196: void usld_lock_held(usimple_lock_t); ! 197: void usld_lock_none_held(void); ! 198: int usld_lock_common_checks(usimple_lock_t, char *); ! 199: #else /* USLOCK_DEBUG */ ! 200: #define USLDBG(stmt) ! 201: #endif /* USLOCK_DEBUG */ ! 202: ! 203: /* ! 204: * Initialize a usimple_lock. ! 205: * ! 206: * No change in preemption state. ! 207: */ ! 208: void ! 209: usimple_lock_init( ! 210: usimple_lock_t l, ! 211: etap_event_t event) ! 212: { ! 213: USLDBG(usld_lock_init(l, event)); ! 214: ETAPCALL(etap_simplelock_init((l),(event))); ! 215: hw_lock_init(&l->interlock); ! 216: } ! 217: ! 218: ! 219: /* ! 220: * Acquire a usimple_lock. ! 221: * ! 222: * Returns with preemption disabled. Note ! 223: * that the hw_lock routines are responsible for ! 224: * maintaining preemption state. ! 225: */ ! 226: void ! 227: usimple_lock( ! 228: usimple_lock_t l) ! 229: { ! 230: int i; ! 231: pc_t pc; ! 232: #if ETAP_LOCK_TRACE ! 233: etap_time_t start_wait_time; ! 234: int no_miss_info = 0; ! 235: #endif /* ETAP_LOCK_TRACE */ ! 236: #if USLOCK_DEBUG ! 237: int count = 0; ! 238: #endif /* USLOCK_DEBUG */ ! 239: ! 240: OBTAIN_PC(pc, l); ! 241: USLDBG(usld_lock_pre(l, pc)); ! 242: #if ETAP_LOCK_TRACE ! 243: ETAP_TIME_CLEAR(start_wait_time); ! 244: #endif /* ETAP_LOCK_TRACE */ ! 245: ! 246: #ifdef __ppc__ ! 247: if(!hw_lock_to(&l->interlock, LockTimeOut)) { /* Try to get the lock with a timeout */ ! 248: ! 249: panic("simple lock deadlock detection - l=%08X, cpu=%d, ret=%08X", l, cpu_number(), pc); ! 250: ! 251: #else /* __ppc__ */ ! 252: while (!hw_lock_try(&l->interlock)) { ! 253: ETAPCALL(if (no_miss_info++ == 0) ! 254: start_wait_time = etap_simplelock_miss(l)); ! 255: while (hw_lock_held(&l->interlock)) { ! 256: /* ! 257: * Spin watching the lock value in cache, ! 258: * without consuming external bus cycles. ! 259: * On most SMP architectures, the atomic ! 260: * instruction(s) used by hw_lock_try ! 261: * cost much, much more than an ordinary ! 262: * memory read. ! 263: */ ! 264: #if USLOCK_DEBUG ! 265: if (count++ > max_lock_loops ! 266: #if MACH_KDB && NCPUS > 1 ! 267: && l != &kdb_lock ! 268: #endif /* MACH_KDB && NCPUS > 1 */ ! 269: ) { ! 270: if (l == &printf_lock) { ! 271: return; ! 272: } ! 273: mp_disable_preemption(); ! 274: panic("simple lock deadlock detection - l=%08X (=%08X), cpu=%d, ret=%08X", ! 275: l, *hw_lock_addr(l->interlock), cpu_number(), pc); ! 276: count = 0; ! 277: mp_enable_preemption(); ! 278: } ! 279: #endif /* USLOCK_DEBUG */ ! 280: } ! 281: #endif /* 0 */ ! 282: } ! 283: ETAPCALL(etap_simplelock_hold(l, pc, start_wait_time)); ! 284: USLDBG(usld_lock_post(l, pc)); ! 285: } ! 286: ! 287: ! 288: /* ! 289: * Release a usimple_lock. ! 290: * ! 291: * Returns with preemption enabled. Note ! 292: * that the hw_lock routines are responsible for ! 293: * maintaining preemption state. ! 294: */ ! 295: void ! 296: usimple_unlock( ! 297: usimple_lock_t l) ! 298: { ! 299: pc_t pc; ! 300: ! 301: // checkNMI(); /* (TEST/DEBUG) */ ! 302: ! 303: OBTAIN_PC(pc, l); ! 304: USLDBG(usld_unlock(l, pc)); ! 305: ETAPCALL(etap_simplelock_unlock(l)); ! 306: hw_lock_unlock(&l->interlock); ! 307: } ! 308: ! 309: ! 310: /* ! 311: * Conditionally acquire a usimple_lock. ! 312: * ! 313: * On success, returns with preemption disabled. ! 314: * On failure, returns with preemption in the same state ! 315: * as when first invoked. Note that the hw_lock routines ! 316: * are responsible for maintaining preemption state. ! 317: * ! 318: * XXX No stats are gathered on a miss; I preserved this ! 319: * behavior from the original assembly-language code, but ! 320: * doesn't it make sense to log misses? XXX ! 321: */ ! 322: unsigned int ! 323: usimple_lock_try( ! 324: usimple_lock_t l) ! 325: { ! 326: pc_t pc; ! 327: unsigned int success; ! 328: etap_time_t zero_time; ! 329: ! 330: OBTAIN_PC(pc, l); ! 331: USLDBG(usld_lock_try_pre(l, pc)); ! 332: if (success = hw_lock_try(&l->interlock)) { ! 333: USLDBG(usld_lock_try_post(l, pc)); ! 334: ETAP_TIME_CLEAR(zero_time); ! 335: ETAPCALL(etap_simplelock_hold(l, pc, zero_time)); ! 336: } ! 337: return success; ! 338: } ! 339: ! 340: #if ETAP_LOCK_TRACE ! 341: void ! 342: simple_lock_no_trace( ! 343: simple_lock_t l) ! 344: { ! 345: pc_t pc; ! 346: ! 347: OBTAIN_PC(pc, l); ! 348: USLDBG(usld_lock_pre(l, pc)); ! 349: while (!hw_lock_try(&l->interlock)) { ! 350: while (hw_lock_held(&l->interlock)) { ! 351: /* ! 352: * Spin watching the lock value in cache, ! 353: * without consuming external bus cycles. ! 354: * On most SMP architectures, the atomic ! 355: * instruction(s) used by hw_lock_try ! 356: * cost much, much more than an ordinary ! 357: * memory read. ! 358: */ ! 359: } ! 360: } ! 361: USLDBG(usld_lock_post(l, pc)); ! 362: } ! 363: ! 364: void ! 365: simple_unlock_no_trace( ! 366: simple_lock_t l) ! 367: { ! 368: pc_t pc; ! 369: ! 370: OBTAIN_PC(pc, l); ! 371: USLDBG(usld_unlock(l, pc)); ! 372: hw_lock_unlock(&l->interlock); ! 373: } ! 374: ! 375: int ! 376: simple_lock_try_no_trace( ! 377: simple_lock_t l) ! 378: { ! 379: pc_t pc; ! 380: unsigned int success; ! 381: ! 382: OBTAIN_PC(pc, l); ! 383: USLDBG(usld_lock_try_pre(l, pc)); ! 384: if (success = hw_lock_try(&l->interlock)) { ! 385: USLDBG(usld_lock_try_post(l, pc)); ! 386: } ! 387: return success; ! 388: } ! 389: #endif /* ETAP_LOCK_TRACE */ ! 390: ! 391: ! 392: #if USLOCK_DEBUG ! 393: /* ! 394: * Verify that the lock is locked and owned by ! 395: * the current thread. ! 396: */ ! 397: void ! 398: usimple_lock_held( ! 399: usimple_lock_t l) ! 400: { ! 401: usld_lock_held(l); ! 402: } ! 403: ! 404: ! 405: /* ! 406: * Verify that no usimple_locks are held by ! 407: * this processor. Typically used in a ! 408: * trap handler when returning to user mode ! 409: * or in a path known to relinquish the processor. ! 410: */ ! 411: void ! 412: usimple_lock_none_held(void) ! 413: { ! 414: usld_lock_none_held(); ! 415: } ! 416: #endif /* USLOCK_DEBUG */ ! 417: ! 418: ! 419: #if USLOCK_DEBUG ! 420: /* ! 421: * States of a usimple_lock. The default when initializing ! 422: * a usimple_lock is setting it up for debug checking. ! 423: */ ! 424: #define USLOCK_CHECKED 0x0001 /* lock is being checked */ ! 425: #define USLOCK_TAKEN 0x0002 /* lock has been taken */ ! 426: #define USLOCK_INIT 0xBAA0 /* lock has been initialized */ ! 427: #define USLOCK_INITIALIZED (USLOCK_INIT|USLOCK_CHECKED) ! 428: #define USLOCK_CHECKING(l) (uslock_check && \ ! 429: ((l)->debug.state & USLOCK_CHECKED)) ! 430: ! 431: /* ! 432: * Maintain a per-cpu stack of acquired usimple_locks. ! 433: */ ! 434: void usl_stack_push(usimple_lock_t, int); ! 435: void usl_stack_pop(usimple_lock_t, int); ! 436: ! 437: /* ! 438: * Trace activities of a particularly interesting lock. ! 439: */ ! 440: void usl_trace(usimple_lock_t, int, pc_t, const char *); ! 441: ! 442: ! 443: /* ! 444: * Initialize the debugging information contained ! 445: * in a usimple_lock. ! 446: */ ! 447: void ! 448: usld_lock_init( ! 449: usimple_lock_t l, ! 450: etap_event_t type) ! 451: { ! 452: if (l == USIMPLE_LOCK_NULL) ! 453: panic("lock initialization: null lock pointer"); ! 454: l->lock_type = USLOCK_TAG; ! 455: l->debug.state = uslock_check ? USLOCK_INITIALIZED : 0; ! 456: l->debug.lock_cpu = l->debug.unlock_cpu = 0; ! 457: l->debug.lock_pc = l->debug.unlock_pc = INVALID_PC; ! 458: l->debug.lock_thread = l->debug.unlock_thread = INVALID_THREAD; ! 459: l->debug.duration[0] = l->debug.duration[1] = 0; ! 460: l->debug.unlock_cpu = l->debug.unlock_cpu = 0; ! 461: l->debug.unlock_pc = l->debug.unlock_pc = INVALID_PC; ! 462: l->debug.unlock_thread = l->debug.unlock_thread = INVALID_THREAD; ! 463: } ! 464: ! 465: ! 466: /* ! 467: * These checks apply to all usimple_locks, not just ! 468: * those with USLOCK_CHECKED turned on. ! 469: */ ! 470: int ! 471: usld_lock_common_checks( ! 472: usimple_lock_t l, ! 473: char *caller) ! 474: { ! 475: if (l == USIMPLE_LOCK_NULL) ! 476: panic("%s: null lock pointer", caller); ! 477: if (l->lock_type != USLOCK_TAG) ! 478: panic("%s: 0x%x is not a usimple lock", caller, (integer_t) l); ! 479: if (!(l->debug.state & USLOCK_INIT)) ! 480: panic("%s: 0x%x is not an initialized lock", ! 481: caller, (integer_t) l); ! 482: return USLOCK_CHECKING(l); ! 483: } ! 484: ! 485: ! 486: /* ! 487: * Debug checks on a usimple_lock just before attempting ! 488: * to acquire it. ! 489: */ ! 490: /* ARGSUSED */ ! 491: void ! 492: usld_lock_pre( ! 493: usimple_lock_t l, ! 494: pc_t pc) ! 495: { ! 496: char *caller = "usimple_lock"; ! 497: ! 498: ! 499: #if 0 ! 500: printf("*** %08X %08X %04X %02X %08X %02X %08X - %s\n", /* (TEST/DEBUG) */ ! 501: l->debug.lock_pc, ! 502: l->debug.lock_thread, ! 503: l->debug.state, ! 504: l->debug.lock_cpu, ! 505: l->debug.unlock_thread, ! 506: l->debug.unlock_cpu, ! 507: l->debug.unlock_pc, ! 508: caller); ! 509: #endif ! 510: ! 511: if (!usld_lock_common_checks(l, caller)) ! 512: return; ! 513: ! 514: if ((l->debug.state & USLOCK_TAKEN) && ! 515: l->debug.lock_thread == (void *) current_thread()) { ! 516: printf("%s: lock 0x%x already locked (at 0x%x) by", ! 517: caller, (integer_t) l, l->debug.lock_pc); ! 518: printf(" current thread 0x%x (new attempt at pc 0x%x)\n", ! 519: l->debug.lock_thread, pc); ! 520: panic(caller); ! 521: } ! 522: mp_disable_preemption(); ! 523: usl_trace(l, cpu_number(), pc, caller); ! 524: mp_enable_preemption(); ! 525: } ! 526: ! 527: ! 528: /* ! 529: * Debug checks on a usimple_lock just after acquiring it. ! 530: * ! 531: * Pre-emption has been disabled at this point, ! 532: * so we are safe in using cpu_number. ! 533: */ ! 534: void ! 535: usld_lock_post( ! 536: usimple_lock_t l, ! 537: pc_t pc) ! 538: { ! 539: register int mycpu; ! 540: char *caller = "successful usimple_lock"; ! 541: ! 542: ! 543: #if 0 ! 544: printf("*** %08X %08X %04X %02X %08X %02X %08X - %s\n", /* (TEST/DEBUG) */ ! 545: l->debug.lock_pc, ! 546: l->debug.lock_thread, ! 547: l->debug.state, ! 548: l->debug.lock_cpu, ! 549: l->debug.unlock_thread, ! 550: l->debug.unlock_cpu, ! 551: l->debug.unlock_pc, ! 552: caller); ! 553: #endif ! 554: ! 555: if (!usld_lock_common_checks(l, caller)) ! 556: return; ! 557: ! 558: if (!((l->debug.state & ~USLOCK_TAKEN) == USLOCK_INITIALIZED)) ! 559: panic("%s: lock 0x%x became uninitialized", ! 560: caller, (integer_t) l); ! 561: if ((l->debug.state & USLOCK_TAKEN)) ! 562: panic("%s: lock 0x%x became TAKEN by someone else", ! 563: caller, (integer_t) l); ! 564: ! 565: mycpu = cpu_number(); ! 566: l->debug.lock_thread = (void *)current_thread(); ! 567: l->debug.state |= USLOCK_TAKEN; ! 568: l->debug.lock_pc = pc; ! 569: l->debug.lock_cpu = mycpu; ! 570: ! 571: usl_stack_push(l, mycpu); ! 572: usl_trace(l, mycpu, pc, caller); ! 573: } ! 574: ! 575: ! 576: /* ! 577: * Debug checks on a usimple_lock just before ! 578: * releasing it. Note that the caller has not ! 579: * yet released the hardware lock. ! 580: * ! 581: * Preemption is still disabled, so there's ! 582: * no problem using cpu_number. ! 583: */ ! 584: void ! 585: usld_unlock( ! 586: usimple_lock_t l, ! 587: pc_t pc) ! 588: { ! 589: register int mycpu; ! 590: char *caller = "usimple_unlock"; ! 591: ! 592: ! 593: #if 0 ! 594: printf("*** %08X %08X %04X %02X %08X %02X %08X - %s\n", /* (TEST/DEBUG) */ ! 595: l->debug.lock_pc, ! 596: l->debug.lock_thread, ! 597: l->debug.state, ! 598: l->debug.lock_cpu, ! 599: l->debug.unlock_thread, ! 600: l->debug.unlock_cpu, ! 601: l->debug.unlock_pc, ! 602: caller); ! 603: #endif ! 604: ! 605: if (!usld_lock_common_checks(l, caller)) ! 606: return; ! 607: ! 608: mycpu = cpu_number(); ! 609: ! 610: if (!(l->debug.state & USLOCK_TAKEN)) ! 611: panic("%s: lock 0x%x hasn't been taken", ! 612: caller, (integer_t) l); ! 613: if (l->debug.lock_thread != (void *) current_thread()) ! 614: panic("%s: unlocking lock 0x%x, owned by thread 0x%x", ! 615: caller, (integer_t) l, l->debug.lock_thread); ! 616: if (l->debug.lock_cpu != mycpu) { ! 617: printf("%s: unlocking lock 0x%x on cpu 0x%x", ! 618: caller, (integer_t) l, mycpu); ! 619: printf(" (acquired on cpu 0x%x)\n", l->debug.lock_cpu); ! 620: panic(caller); ! 621: } ! 622: usl_trace(l, mycpu, pc, caller); ! 623: usl_stack_pop(l, mycpu); ! 624: ! 625: l->debug.unlock_thread = l->debug.lock_thread; ! 626: l->debug.lock_thread = INVALID_PC; ! 627: l->debug.state &= ~USLOCK_TAKEN; ! 628: l->debug.unlock_pc = pc; ! 629: l->debug.unlock_cpu = mycpu; ! 630: } ! 631: ! 632: ! 633: /* ! 634: * Debug checks on a usimple_lock just before ! 635: * attempting to acquire it. ! 636: * ! 637: * Preemption isn't guaranteed to be disabled. ! 638: */ ! 639: void ! 640: usld_lock_try_pre( ! 641: usimple_lock_t l, ! 642: pc_t pc) ! 643: { ! 644: char *caller = "usimple_lock_try"; ! 645: ! 646: if (!usld_lock_common_checks(l, caller)) ! 647: return; ! 648: mp_disable_preemption(); ! 649: usl_trace(l, cpu_number(), pc, caller); ! 650: mp_enable_preemption(); ! 651: } ! 652: ! 653: ! 654: /* ! 655: * Debug checks on a usimple_lock just after ! 656: * successfully attempting to acquire it. ! 657: * ! 658: * Preemption has been disabled by the ! 659: * lock acquisition attempt, so it's safe ! 660: * to use cpu_number. ! 661: */ ! 662: void ! 663: usld_lock_try_post( ! 664: usimple_lock_t l, ! 665: pc_t pc) ! 666: { ! 667: register int mycpu; ! 668: char *caller = "successful usimple_lock_try"; ! 669: ! 670: if (!usld_lock_common_checks(l, caller)) ! 671: return; ! 672: ! 673: if (!((l->debug.state & ~USLOCK_TAKEN) == USLOCK_INITIALIZED)) ! 674: panic("%s: lock 0x%x became uninitialized", ! 675: caller, (integer_t) l); ! 676: if ((l->debug.state & USLOCK_TAKEN)) ! 677: panic("%s: lock 0x%x became TAKEN by someone else", ! 678: caller, (integer_t) l); ! 679: ! 680: mycpu = cpu_number(); ! 681: l->debug.lock_thread = (void *) current_thread(); ! 682: l->debug.state |= USLOCK_TAKEN; ! 683: l->debug.lock_pc = pc; ! 684: l->debug.lock_cpu = mycpu; ! 685: ! 686: #if 0 ! 687: printf("*** %08X %08X %04X %02X %08X %02X %08X - %s\n", /* (TEST/DEBUG) */ ! 688: l->debug.lock_pc, ! 689: l->debug.lock_thread, ! 690: l->debug.state, ! 691: l->debug.lock_cpu, ! 692: l->debug.unlock_thread, ! 693: l->debug.unlock_cpu, ! 694: l->debug.unlock_pc, ! 695: caller); ! 696: #endif ! 697: ! 698: usl_stack_push(l, mycpu); ! 699: usl_trace(l, mycpu, pc, caller); ! 700: } ! 701: ! 702: ! 703: /* ! 704: * Determine whether the lock in question is owned ! 705: * by the current thread. ! 706: */ ! 707: void ! 708: usld_lock_held( ! 709: usimple_lock_t l) ! 710: { ! 711: char *caller = "usimple_lock_held"; ! 712: ! 713: ! 714: #if 0 ! 715: printf("*** %08X %08X %04X %02X %08X %02X %08X - %s\n", /* (TEST/DEBUG) */ ! 716: l->debug.lock_pc, ! 717: l->debug.lock_thread, ! 718: l->debug.state, ! 719: l->debug.lock_cpu, ! 720: l->debug.unlock_thread, ! 721: l->debug.unlock_cpu, ! 722: l->debug.unlock_pc, ! 723: caller); ! 724: #endif ! 725: ! 726: if (!usld_lock_common_checks(l, caller)) ! 727: return; ! 728: ! 729: if (!(l->debug.state & USLOCK_TAKEN)) ! 730: panic("%s: lock 0x%x hasn't been taken", ! 731: caller, (integer_t) l); ! 732: if (l->debug.lock_thread != (void *) current_thread()) ! 733: panic("%s: lock 0x%x is owned by thread 0x%x", caller, ! 734: (integer_t) l, (integer_t) l->debug.lock_thread); ! 735: ! 736: /* ! 737: * The usimple_lock is active, so preemption ! 738: * is disabled and the current cpu should ! 739: * match the one recorded at lock acquisition time. ! 740: */ ! 741: if (l->debug.lock_cpu != cpu_number()) ! 742: panic("%s: current cpu 0x%x isn't acquiring cpu 0x%x", ! 743: caller, cpu_number(), (integer_t) l->debug.lock_cpu); ! 744: } ! 745: ! 746: ! 747: /* ! 748: * Per-cpu stack of currently active usimple_locks. ! 749: * Requires spl protection so that interrupt-level ! 750: * locks plug-n-play with their thread-context friends. ! 751: */ ! 752: #define USLOCK_STACK_DEPTH 20 ! 753: usimple_lock_t uslock_stack[NCPUS][USLOCK_STACK_DEPTH]; ! 754: unsigned int uslock_stack_index[NCPUS]; ! 755: boolean_t uslock_stack_enabled = TRUE; ! 756: ! 757: ! 758: /* ! 759: * Record a usimple_lock just acquired on ! 760: * the current processor. ! 761: * ! 762: * Preemption has been disabled by lock ! 763: * acquisition, so it's safe to use the cpu number ! 764: * specified by the caller. ! 765: */ ! 766: void ! 767: usl_stack_push( ! 768: usimple_lock_t l, ! 769: int mycpu) ! 770: { ! 771: boolean_t s; ! 772: ! 773: if (uslock_stack_enabled == FALSE) ! 774: return; ! 775: ! 776: DISABLE_INTERRUPTS(s); ! 777: assert(uslock_stack_index[mycpu] >= 0); ! 778: assert(uslock_stack_index[mycpu] < USLOCK_STACK_DEPTH); ! 779: if (uslock_stack_index[mycpu] >= USLOCK_STACK_DEPTH) { ! 780: printf("usl_stack_push (cpu 0x%x): too many locks (%d)", ! 781: mycpu, uslock_stack_index[mycpu]); ! 782: printf(" disabling stacks\n"); ! 783: uslock_stack_enabled = FALSE; ! 784: ENABLE_INTERRUPTS(s); ! 785: return; ! 786: } ! 787: uslock_stack[mycpu][uslock_stack_index[mycpu]] = l; ! 788: uslock_stack_index[mycpu]++; ! 789: ENABLE_INTERRUPTS(s); ! 790: } ! 791: ! 792: ! 793: /* ! 794: * Eliminate the entry for a usimple_lock ! 795: * that had been active on the current processor. ! 796: * ! 797: * Preemption has been disabled by lock ! 798: * acquisition, and we haven't yet actually ! 799: * released the hardware lock associated with ! 800: * this usimple_lock, so it's safe to use the ! 801: * cpu number supplied by the caller. ! 802: */ ! 803: void ! 804: usl_stack_pop( ! 805: usimple_lock_t l, ! 806: int mycpu) ! 807: { ! 808: unsigned int i, index; ! 809: boolean_t s; ! 810: ! 811: if (uslock_stack_enabled == FALSE) ! 812: return; ! 813: ! 814: DISABLE_INTERRUPTS(s); ! 815: assert(uslock_stack_index[mycpu] > 0); ! 816: assert(uslock_stack_index[mycpu] <= USLOCK_STACK_DEPTH); ! 817: if (uslock_stack_index[mycpu] == 0) { ! 818: printf("usl_stack_pop (cpu 0x%x): not enough locks (%d)", ! 819: mycpu, uslock_stack_index[mycpu]); ! 820: printf(" disabling stacks\n"); ! 821: uslock_stack_enabled = FALSE; ! 822: ENABLE_INTERRUPTS(s); ! 823: return; ! 824: } ! 825: index = --uslock_stack_index[mycpu]; ! 826: for (i = 0; i <= index; ++i) { ! 827: if (uslock_stack[mycpu][i] == l) { ! 828: if (i != index) ! 829: uslock_stack[mycpu][i] = ! 830: uslock_stack[mycpu][index]; ! 831: ENABLE_INTERRUPTS(s); ! 832: return; ! 833: } ! 834: } ! 835: ENABLE_INTERRUPTS(s); ! 836: panic("usl_stack_pop: can't find usimple_lock 0x%x", l); ! 837: } ! 838: ! 839: ! 840: /* ! 841: * Determine whether any usimple_locks are currently held. ! 842: * ! 843: * Caller's preemption state is uncertain. If ! 844: * preemption has been disabled, this check is accurate. ! 845: * Otherwise, this check is just a guess. We do the best ! 846: * we can by disabling scheduler interrupts, so at least ! 847: * the check is accurate w.r.t. whatever cpu we're running ! 848: * on while in this routine. ! 849: */ ! 850: void ! 851: usld_lock_none_held() ! 852: { ! 853: register int mycpu; ! 854: boolean_t s; ! 855: unsigned int locks_held; ! 856: char *caller = "usimple_lock_none_held"; ! 857: ! 858: DISABLE_INTERRUPTS(s); ! 859: mp_disable_preemption(); ! 860: mycpu = cpu_number(); ! 861: locks_held = uslock_stack_index[mycpu]; ! 862: mp_enable_preemption(); ! 863: ENABLE_INTERRUPTS(s); ! 864: if (locks_held > 0) ! 865: panic("%s: no locks should be held (0x%x locks held)", ! 866: caller, (integer_t) locks_held); ! 867: } ! 868: ! 869: ! 870: /* ! 871: * For very special cases, set traced_lock to point to a ! 872: * specific lock of interest. The result is a series of ! 873: * XPRs showing lock operations on that lock. The lock_seq ! 874: * value is used to show the order of those operations. ! 875: */ ! 876: usimple_lock_t traced_lock; ! 877: unsigned int lock_seq; ! 878: ! 879: void ! 880: usl_trace( ! 881: usimple_lock_t l, ! 882: int mycpu, ! 883: pc_t pc, ! 884: const char * op_name) ! 885: { ! 886: if (traced_lock == l) { ! 887: XPR(XPR_SLOCK, ! 888: "seq %d, cpu %d, %s @ %x\n", ! 889: (integer_t) lock_seq, (integer_t) mycpu, ! 890: (integer_t) op_name, (integer_t) pc, 0); ! 891: lock_seq++; ! 892: } ! 893: } ! 894: ! 895: ! 896: ! 897: #if MACH_KDB ! 898: #define printf kdbprintf ! 899: void db_show_all_slocks(void); ! 900: void ! 901: db_show_all_slocks(void) ! 902: { ! 903: unsigned int i, index; ! 904: int mycpu = cpu_number(); ! 905: usimple_lock_t l; ! 906: ! 907: if (uslock_stack_enabled == FALSE) ! 908: return; ! 909: ! 910: #if 0 ! 911: if (!mach_slocks_init) ! 912: iprintf("WARNING: simple locks stack may not be accurate\n"); ! 913: #endif ! 914: assert(uslock_stack_index[mycpu] >= 0); ! 915: assert(uslock_stack_index[mycpu] <= USLOCK_STACK_DEPTH); ! 916: index = uslock_stack_index[mycpu]; ! 917: for (i = 0; i < index; ++i) { ! 918: l = uslock_stack[mycpu][i]; ! 919: iprintf("%d: ", i); ! 920: db_printsym((vm_offset_t)l, DB_STGY_ANY); ! 921: if (l->debug.lock_pc != INVALID_PC) { ! 922: printf(" locked by "); ! 923: db_printsym((int)l->debug.lock_pc, DB_STGY_PROC); ! 924: } ! 925: printf("\n"); ! 926: } ! 927: } ! 928: #endif /* MACH_KDB */ ! 929: ! 930: #endif /* USLOCK_DEBUG */ ! 931: ! 932: /* #endif USIMPLE_LOCK_CALLS */ ! 933: ! 934: /* ! 935: * Routine: lock_alloc ! 936: * Function: ! 937: * Allocate a lock for external users who cannot ! 938: * hard-code the structure definition into their ! 939: * objects. ! 940: * For now just use kalloc, but a zone is probably ! 941: * warranted. ! 942: */ ! 943: lock_t * ! 944: lock_alloc( ! 945: boolean_t can_sleep, ! 946: etap_event_t event, ! 947: etap_event_t i_event) ! 948: { ! 949: lock_t *l; ! 950: ! 951: if ((l = (lock_t *)kalloc(sizeof(lock_t))) != 0) ! 952: lock_init(l, can_sleep, event, i_event); ! 953: return(l); ! 954: } ! 955: ! 956: /* ! 957: * Routine: lock_free ! 958: * Function: ! 959: * Free a lock allocated for external users. ! 960: * For now just use kfree, but a zone is probably ! 961: * warranted. ! 962: */ ! 963: void ! 964: lock_free( ! 965: lock_t *l) ! 966: { ! 967: kfree((vm_offset_t)l, sizeof(lock_t)); ! 968: } ! 969: ! 970: ! 971: /* ! 972: * Routine: lock_init ! 973: * Function: ! 974: * Initialize a lock; required before use. ! 975: * Note that clients declare the "struct lock" ! 976: * variables and then initialize them, rather ! 977: * than getting a new one from this module. ! 978: */ ! 979: void ! 980: lock_init( ! 981: lock_t *l, ! 982: boolean_t can_sleep, ! 983: etap_event_t event, ! 984: etap_event_t i_event) ! 985: { ! 986: (void) memset((void *) l, 0, sizeof(lock_t)); ! 987: ! 988: #if ETAP_LOCK_TRACE ! 989: etap_event_table_assign(&l->u.event_table_chain, event); ! 990: l->u.s.start_list = SD_ENTRY_NULL; ! 991: #endif /* ETAP_LOCK_TRACE */ ! 992: ! 993: simple_lock_init(&l->interlock, i_event); ! 994: l->want_write = FALSE; ! 995: l->want_upgrade = FALSE; ! 996: l->read_count = 0; ! 997: l->can_sleep = can_sleep; ! 998: ! 999: #if ETAP_LOCK_ACCUMULATE ! 1000: l->cbuff_write = etap_cbuff_reserve(lock_event_table(l)); ! 1001: if (l->cbuff_write != CBUFF_ENTRY_NULL) { ! 1002: l->cbuff_write->event = event; ! 1003: l->cbuff_write->instance = (unsigned long) l; ! 1004: l->cbuff_write->kind = WRITE_LOCK; ! 1005: } ! 1006: l->cbuff_read = CBUFF_ENTRY_NULL; ! 1007: #endif /* ETAP_LOCK_ACCUMULATE */ ! 1008: } ! 1009: ! 1010: ! 1011: /* ! 1012: * Sleep locks. These use the same data structure and algorithm ! 1013: * as the spin locks, but the process sleeps while it is waiting ! 1014: * for the lock. These work on uniprocessor systems. ! 1015: */ ! 1016: ! 1017: #define DECREMENTER_TIMEOUT 1000000 ! 1018: ! 1019: void ! 1020: lock_write( ! 1021: register lock_t * l) ! 1022: { ! 1023: register int i; ! 1024: start_data_node_t entry = {0}; ! 1025: boolean_t lock_miss = FALSE; ! 1026: unsigned short dynamic = 0; ! 1027: unsigned short trace = 0; ! 1028: etap_time_t total_time; ! 1029: etap_time_t stop_wait_time; ! 1030: pc_t pc; ! 1031: #if MACH_LDEBUG ! 1032: int decrementer; ! 1033: #endif /* MACH_LDEBUG */ ! 1034: ! 1035: ! 1036: ETAP_STAMP(lock_event_table(l), trace, dynamic); ! 1037: ETAP_CREATE_ENTRY(entry, trace); ! 1038: MON_ASSIGN_PC(entry->start_pc, pc, trace); ! 1039: ! 1040: simple_lock(&l->interlock); ! 1041: ! 1042: /* ! 1043: * Link the new start_list entry ! 1044: */ ! 1045: ETAP_LINK_ENTRY(l, entry, trace); ! 1046: ! 1047: #if MACH_LDEBUG ! 1048: decrementer = DECREMENTER_TIMEOUT; ! 1049: #endif /* MACH_LDEBUG */ ! 1050: ! 1051: /* ! 1052: * Try to acquire the want_write bit. ! 1053: */ ! 1054: while (l->want_write) { ! 1055: if (!lock_miss) { ! 1056: ETAP_CONTENTION_TIMESTAMP(entry, trace); ! 1057: lock_miss = TRUE; ! 1058: } ! 1059: ! 1060: i = lock_wait_time[l->can_sleep ? 1 : 0]; ! 1061: if (i != 0) { ! 1062: simple_unlock(&l->interlock); ! 1063: #if MACH_LDEBUG ! 1064: if (!--decrementer) ! 1065: Debugger("timeout - want_write"); ! 1066: #endif /* MACH_LDEBUG */ ! 1067: while (--i != 0 && l->want_write) ! 1068: continue; ! 1069: simple_lock(&l->interlock); ! 1070: } ! 1071: ! 1072: if (l->can_sleep && l->want_write) { ! 1073: l->waiting = TRUE; ! 1074: ETAP_SET_REASON(current_thread(), ! 1075: BLOCKED_ON_COMPLEX_LOCK); ! 1076: thread_sleep_simple_lock((event_t) l, ! 1077: simple_lock_addr(l->interlock), FALSE); ! 1078: simple_lock(&l->interlock); ! 1079: } ! 1080: } ! 1081: l->want_write = TRUE; ! 1082: ! 1083: /* Wait for readers (and upgrades) to finish */ ! 1084: ! 1085: #if MACH_LDEBUG ! 1086: decrementer = DECREMENTER_TIMEOUT; ! 1087: #endif /* MACH_LDEBUG */ ! 1088: while ((l->read_count != 0) || l->want_upgrade) { ! 1089: if (!lock_miss) { ! 1090: ETAP_CONTENTION_TIMESTAMP(entry,trace); ! 1091: lock_miss = TRUE; ! 1092: } ! 1093: ! 1094: i = lock_wait_time[l->can_sleep ? 1 : 0]; ! 1095: if (i != 0) { ! 1096: simple_unlock(&l->interlock); ! 1097: #if MACH_LDEBUG ! 1098: if (!--decrementer) ! 1099: Debugger("timeout - wait for readers"); ! 1100: #endif /* MACH_LDEBUG */ ! 1101: while (--i != 0 && (l->read_count != 0 || ! 1102: l->want_upgrade)) ! 1103: continue; ! 1104: simple_lock(&l->interlock); ! 1105: } ! 1106: ! 1107: if (l->can_sleep && (l->read_count != 0 || l->want_upgrade)) { ! 1108: l->waiting = TRUE; ! 1109: ETAP_SET_REASON(current_thread(), ! 1110: BLOCKED_ON_COMPLEX_LOCK); ! 1111: thread_sleep_simple_lock((event_t) l, ! 1112: simple_lock_addr(l->interlock), FALSE); ! 1113: simple_lock(&l->interlock); ! 1114: } ! 1115: } ! 1116: ! 1117: /* ! 1118: * do not collect wait data if either the lock ! 1119: * was free or no wait traces are enabled. ! 1120: */ ! 1121: ! 1122: if (lock_miss && ETAP_CONTENTION_ENABLED(trace)) { ! 1123: ETAP_TIMESTAMP(stop_wait_time); ! 1124: ETAP_TOTAL_TIME(total_time, ! 1125: stop_wait_time, ! 1126: entry->start_wait_time); ! 1127: CUM_WAIT_ACCUMULATE(l->cbuff_write, total_time, dynamic, trace); ! 1128: MON_DATA_COLLECT(l, ! 1129: entry, ! 1130: total_time, ! 1131: WRITE_LOCK, ! 1132: MON_CONTENTION, ! 1133: trace); ! 1134: } ! 1135: ! 1136: simple_unlock(&l->interlock); ! 1137: ! 1138: /* ! 1139: * Set start hold time if some type of hold tracing is enabled. ! 1140: * ! 1141: * Note: if the stop_wait_time was already stamped, use ! 1142: * it as the start_hold_time instead of doing an ! 1143: * expensive bus access. ! 1144: * ! 1145: */ ! 1146: ! 1147: if (lock_miss && ETAP_CONTENTION_ENABLED(trace)) ! 1148: ETAP_COPY_START_HOLD_TIME(entry, stop_wait_time, trace); ! 1149: else ! 1150: ETAP_DURATION_TIMESTAMP(entry, trace); ! 1151: ! 1152: } ! 1153: ! 1154: void ! 1155: lock_done( ! 1156: register lock_t * l) ! 1157: { ! 1158: boolean_t do_wakeup = FALSE; ! 1159: start_data_node_t entry; ! 1160: unsigned short dynamic = 0; ! 1161: unsigned short trace = 0; ! 1162: etap_time_t stop_hold_time; ! 1163: etap_time_t total_time; ! 1164: unsigned long lock_kind; ! 1165: pc_t pc; ! 1166: ! 1167: ! 1168: ETAP_STAMP(lock_event_table(l), trace, dynamic); ! 1169: ! 1170: simple_lock(&l->interlock); ! 1171: ! 1172: if (l->read_count != 0) { ! 1173: l->read_count--; ! 1174: lock_kind = READ_LOCK; ! 1175: } ! 1176: else ! 1177: if (l->want_upgrade) { ! 1178: l->want_upgrade = FALSE; ! 1179: lock_kind = WRITE_LOCK; ! 1180: } ! 1181: else { ! 1182: l->want_write = FALSE; ! 1183: lock_kind = WRITE_LOCK; ! 1184: } ! 1185: ! 1186: /* ! 1187: * There is no reason to wakeup a waiting thread ! 1188: * if the read-count is non-zero. Consider: ! 1189: * we must be dropping a read lock ! 1190: * threads are waiting only if one wants a write lock ! 1191: * if there are still readers, they can't proceed ! 1192: */ ! 1193: ! 1194: if (l->waiting && (l->read_count == 0)) { ! 1195: l->waiting = FALSE; ! 1196: do_wakeup = TRUE; ! 1197: } ! 1198: /* ! 1199: * Collect hold data if hold tracing is ! 1200: * enabled. ! 1201: */ ! 1202: ! 1203: /* ! 1204: * NOTE: All complex locks whose tracing was on when the ! 1205: * lock was acquired will have an entry in the start_data ! 1206: * list. ! 1207: */ ! 1208: ! 1209: ETAP_UNLINK_ENTRY(l,entry); ! 1210: if (ETAP_DURATION_ENABLED(trace) && entry != SD_ENTRY_NULL) { ! 1211: ETAP_TIMESTAMP (stop_hold_time); ! 1212: ETAP_TOTAL_TIME (total_time, ! 1213: stop_hold_time, ! 1214: entry->start_hold_time); ! 1215: ! 1216: if (lock_kind & WRITE_LOCK) ! 1217: CUM_HOLD_ACCUMULATE (l->cbuff_write, ! 1218: total_time, ! 1219: dynamic, ! 1220: trace); ! 1221: else { ! 1222: CUM_READ_ENTRY_RESERVE(l,l->cbuff_read,trace); ! 1223: CUM_HOLD_ACCUMULATE (l->cbuff_read, ! 1224: total_time, ! 1225: dynamic, ! 1226: trace); ! 1227: } ! 1228: MON_ASSIGN_PC(entry->end_pc,pc,trace); ! 1229: MON_DATA_COLLECT(l,entry, ! 1230: total_time, ! 1231: lock_kind, ! 1232: MON_DURATION, ! 1233: trace); ! 1234: } ! 1235: ! 1236: simple_unlock(&l->interlock); ! 1237: ! 1238: ETAP_DESTROY_ENTRY(entry); ! 1239: ! 1240: if (do_wakeup) ! 1241: thread_wakeup((event_t) l); ! 1242: } ! 1243: ! 1244: void ! 1245: lock_read( ! 1246: register lock_t * l) ! 1247: { ! 1248: register int i; ! 1249: start_data_node_t entry = {0}; ! 1250: boolean_t lock_miss = FALSE; ! 1251: unsigned short dynamic = 0; ! 1252: unsigned short trace = 0; ! 1253: etap_time_t total_time; ! 1254: etap_time_t stop_wait_time; ! 1255: pc_t pc; ! 1256: #if MACH_LDEBUG ! 1257: int decrementer; ! 1258: #endif /* MACH_LDEBUG */ ! 1259: ! 1260: ETAP_STAMP(lock_event_table(l), trace, dynamic); ! 1261: ETAP_CREATE_ENTRY(entry, trace); ! 1262: MON_ASSIGN_PC(entry->start_pc, pc, trace); ! 1263: ! 1264: simple_lock(&l->interlock); ! 1265: ! 1266: /* ! 1267: * Link the new start_list entry ! 1268: */ ! 1269: ETAP_LINK_ENTRY(l,entry,trace); ! 1270: ! 1271: #if MACH_LDEBUG ! 1272: decrementer = DECREMENTER_TIMEOUT; ! 1273: #endif /* MACH_LDEBUG */ ! 1274: while ((0 == l->read_count) && (l->want_write || l->want_upgrade)) { ! 1275: if (!lock_miss) { ! 1276: ETAP_CONTENTION_TIMESTAMP(entry, trace); ! 1277: lock_miss = TRUE; ! 1278: } ! 1279: ! 1280: i = lock_wait_time[l->can_sleep ? 1 : 0]; ! 1281: ! 1282: if (i != 0) { ! 1283: simple_unlock(&l->interlock); ! 1284: #if MACH_LDEBUG ! 1285: if (!--decrementer) ! 1286: Debugger("timeout - wait no writers"); ! 1287: #endif /* MACH_LDEBUG */ ! 1288: while (--i != 0 && ! 1289: ((0 == l->read_count) && (l->want_write || l->want_upgrade))) ! 1290: continue; ! 1291: simple_lock(&l->interlock); ! 1292: } ! 1293: ! 1294: if (l->can_sleep && ! 1295: ((0 == l->read_count) && (l->want_write || l->want_upgrade))) { ! 1296: l->waiting = TRUE; ! 1297: thread_sleep_simple_lock((event_t) l, ! 1298: simple_lock_addr(l->interlock), FALSE); ! 1299: simple_lock(&l->interlock); ! 1300: } ! 1301: } ! 1302: ! 1303: l->read_count++; ! 1304: ! 1305: /* ! 1306: * Do not collect wait data if the lock was free ! 1307: * or if no wait traces are enabled. ! 1308: */ ! 1309: ! 1310: if (lock_miss && ETAP_CONTENTION_ENABLED(trace)) { ! 1311: ETAP_TIMESTAMP(stop_wait_time); ! 1312: ETAP_TOTAL_TIME(total_time, ! 1313: stop_wait_time, ! 1314: entry->start_wait_time); ! 1315: CUM_READ_ENTRY_RESERVE(l, l->cbuff_read, trace); ! 1316: CUM_WAIT_ACCUMULATE(l->cbuff_read, total_time, dynamic, trace); ! 1317: MON_DATA_COLLECT(l, ! 1318: entry, ! 1319: total_time, ! 1320: READ_LOCK, ! 1321: MON_CONTENTION, ! 1322: trace); ! 1323: } ! 1324: simple_unlock(&l->interlock); ! 1325: ! 1326: /* ! 1327: * Set start hold time if some type of hold tracing is enabled. ! 1328: * ! 1329: * Note: if the stop_wait_time was already stamped, use ! 1330: * it instead of doing an expensive bus access. ! 1331: * ! 1332: */ ! 1333: ! 1334: if (lock_miss && ETAP_CONTENTION_ENABLED(trace)) ! 1335: ETAP_COPY_START_HOLD_TIME(entry, stop_wait_time, trace); ! 1336: else ! 1337: ETAP_DURATION_TIMESTAMP(entry,trace); ! 1338: } ! 1339: ! 1340: ! 1341: /* ! 1342: * Routine: lock_read_to_write ! 1343: * Function: ! 1344: * Improves a read-only lock to one with ! 1345: * write permission. If another reader has ! 1346: * already requested an upgrade to a write lock, ! 1347: * no lock is held upon return. ! 1348: * ! 1349: * Returns TRUE if the upgrade *failed*. ! 1350: */ ! 1351: ! 1352: boolean_t ! 1353: lock_read_to_write( ! 1354: register lock_t * l) ! 1355: { ! 1356: register int i; ! 1357: boolean_t do_wakeup = FALSE; ! 1358: start_data_node_t entry = {0}; ! 1359: boolean_t lock_miss = FALSE; ! 1360: unsigned short dynamic = 0; ! 1361: unsigned short trace = 0; ! 1362: etap_time_t total_time; ! 1363: etap_time_t stop_time; ! 1364: pc_t pc; ! 1365: #if MACH_LDEBUG ! 1366: int decrementer; ! 1367: #endif /* MACH_LDEBUG */ ! 1368: ! 1369: ! 1370: ETAP_STAMP(lock_event_table(l), trace, dynamic); ! 1371: ! 1372: simple_lock(&l->interlock); ! 1373: ! 1374: l->read_count--; ! 1375: ! 1376: /* ! 1377: * Since the read lock is lost whether the write lock ! 1378: * is acquired or not, read hold data is collected here. ! 1379: * This, of course, is assuming some type of hold ! 1380: * tracing is enabled. ! 1381: * ! 1382: * Note: trace is set to zero if the entry does not exist. ! 1383: */ ! 1384: ! 1385: ETAP_FIND_ENTRY(l, entry, trace); ! 1386: ! 1387: if (ETAP_DURATION_ENABLED(trace)) { ! 1388: ETAP_TIMESTAMP(stop_time); ! 1389: ETAP_TOTAL_TIME(total_time, stop_time, entry->start_hold_time); ! 1390: CUM_HOLD_ACCUMULATE(l->cbuff_read, total_time, dynamic, trace); ! 1391: MON_ASSIGN_PC(entry->end_pc, pc, trace); ! 1392: MON_DATA_COLLECT(l, ! 1393: entry, ! 1394: total_time, ! 1395: READ_LOCK, ! 1396: MON_DURATION, ! 1397: trace); ! 1398: } ! 1399: ! 1400: if (l->want_upgrade) { ! 1401: /* ! 1402: * Someone else has requested upgrade. ! 1403: * Since we've released a read lock, wake ! 1404: * him up. ! 1405: */ ! 1406: if (l->waiting && (l->read_count == 0)) { ! 1407: l->waiting = FALSE; ! 1408: do_wakeup = TRUE; ! 1409: } ! 1410: ! 1411: ETAP_UNLINK_ENTRY(l, entry); ! 1412: simple_unlock(&l->interlock); ! 1413: ETAP_DESTROY_ENTRY(entry); ! 1414: ! 1415: if (do_wakeup) ! 1416: thread_wakeup((event_t) l); ! 1417: return (TRUE); ! 1418: } ! 1419: ! 1420: l->want_upgrade = TRUE; ! 1421: ! 1422: MON_ASSIGN_PC(entry->start_pc, pc, trace); ! 1423: ! 1424: #if MACH_LDEBUG ! 1425: decrementer = DECREMENTER_TIMEOUT; ! 1426: #endif /* MACH_LDEBUG */ ! 1427: while (l->read_count != 0) { ! 1428: if (!lock_miss) { ! 1429: ETAP_CONTENTION_TIMESTAMP(entry, trace); ! 1430: lock_miss = TRUE; ! 1431: } ! 1432: ! 1433: i = lock_wait_time[l->can_sleep ? 1 : 0]; ! 1434: ! 1435: if (i != 0) { ! 1436: simple_unlock(&l->interlock); ! 1437: #if MACH_LDEBUG ! 1438: if (!--decrementer) ! 1439: Debugger("timeout - read_count"); ! 1440: #endif /* MACH_LDEBUG */ ! 1441: while (--i != 0 && l->read_count != 0) ! 1442: continue; ! 1443: simple_lock(&l->interlock); ! 1444: } ! 1445: ! 1446: if (l->can_sleep && l->read_count != 0) { ! 1447: l->waiting = TRUE; ! 1448: thread_sleep_simple_lock((event_t) l, ! 1449: simple_lock_addr(l->interlock), FALSE); ! 1450: simple_lock(&l->interlock); ! 1451: } ! 1452: } ! 1453: ! 1454: /* ! 1455: * do not collect wait data if the lock was free ! 1456: * or if no wait traces are enabled. ! 1457: */ ! 1458: ! 1459: if (lock_miss && ETAP_CONTENTION_ENABLED(trace)) { ! 1460: ETAP_TIMESTAMP (stop_time); ! 1461: ETAP_TOTAL_TIME(total_time, stop_time, entry->start_wait_time); ! 1462: CUM_WAIT_ACCUMULATE(l->cbuff_write, total_time, dynamic, trace); ! 1463: MON_DATA_COLLECT(l, ! 1464: entry, ! 1465: total_time, ! 1466: WRITE_LOCK, ! 1467: MON_CONTENTION, ! 1468: trace); ! 1469: } ! 1470: ! 1471: simple_unlock(&l->interlock); ! 1472: ! 1473: /* ! 1474: * Set start hold time if some type of hold tracing is enabled ! 1475: * ! 1476: * Note: if the stop_time was already stamped, use ! 1477: * it as the new start_hold_time instead of doing ! 1478: * an expensive VME access. ! 1479: * ! 1480: */ ! 1481: ! 1482: if (lock_miss && ETAP_CONTENTION_ENABLED(trace)) ! 1483: ETAP_COPY_START_HOLD_TIME(entry, stop_time, trace); ! 1484: else ! 1485: ETAP_DURATION_TIMESTAMP(entry, trace); ! 1486: ! 1487: return (FALSE); ! 1488: } ! 1489: ! 1490: void ! 1491: lock_write_to_read( ! 1492: register lock_t * l) ! 1493: { ! 1494: boolean_t do_wakeup = FALSE; ! 1495: start_data_node_t entry = {0}; ! 1496: unsigned short dynamic = 0; ! 1497: unsigned short trace = 0; ! 1498: etap_time_t stop_hold_time; ! 1499: etap_time_t total_time; ! 1500: pc_t pc; ! 1501: ! 1502: ETAP_STAMP(lock_event_table(l), trace,dynamic); ! 1503: ! 1504: simple_lock(&l->interlock); ! 1505: ! 1506: l->read_count++; ! 1507: if (l->want_upgrade) ! 1508: l->want_upgrade = FALSE; ! 1509: else ! 1510: l->want_write = FALSE; ! 1511: ! 1512: if (l->waiting) { ! 1513: l->waiting = FALSE; ! 1514: do_wakeup = TRUE; ! 1515: } ! 1516: ! 1517: /* ! 1518: * Since we are switching from a write lock to a read lock, ! 1519: * the write lock data is stored and the read lock data ! 1520: * collection begins. ! 1521: * ! 1522: * Note: trace is set to zero if the entry does not exist. ! 1523: */ ! 1524: ! 1525: ETAP_FIND_ENTRY(l, entry, trace); ! 1526: ! 1527: if (ETAP_DURATION_ENABLED(trace)) { ! 1528: ETAP_TIMESTAMP (stop_hold_time); ! 1529: ETAP_TOTAL_TIME(total_time, stop_hold_time, entry->start_hold_time); ! 1530: CUM_HOLD_ACCUMULATE(l->cbuff_write, total_time, dynamic, trace); ! 1531: MON_ASSIGN_PC(entry->end_pc, pc, trace); ! 1532: MON_DATA_COLLECT(l, ! 1533: entry, ! 1534: total_time, ! 1535: WRITE_LOCK, ! 1536: MON_DURATION, ! 1537: trace); ! 1538: } ! 1539: ! 1540: simple_unlock(&l->interlock); ! 1541: ! 1542: /* ! 1543: * Set start hold time if some type of hold tracing is enabled ! 1544: * ! 1545: * Note: if the stop_hold_time was already stamped, use ! 1546: * it as the new start_hold_time instead of doing ! 1547: * an expensive bus access. ! 1548: * ! 1549: */ ! 1550: ! 1551: if (ETAP_DURATION_ENABLED(trace)) ! 1552: ETAP_COPY_START_HOLD_TIME(entry, stop_hold_time, trace); ! 1553: else ! 1554: ETAP_DURATION_TIMESTAMP(entry, trace); ! 1555: ! 1556: MON_ASSIGN_PC(entry->start_pc, pc, trace); ! 1557: ! 1558: if (do_wakeup) ! 1559: thread_wakeup((event_t) l); ! 1560: } ! 1561: ! 1562: ! 1563: #if 0 /* Unused */ ! 1564: /* ! 1565: * Routine: lock_try_write ! 1566: * Function: ! 1567: * Tries to get a write lock. ! 1568: * ! 1569: * Returns FALSE if the lock is not held on return. ! 1570: */ ! 1571: ! 1572: boolean_t ! 1573: lock_try_write( ! 1574: register lock_t * l) ! 1575: { ! 1576: start_data_node_t entry = {0}; ! 1577: unsigned short trace = 0; ! 1578: pc_t pc; ! 1579: ! 1580: ETAP_STAMP(lock_event_table(l), trace, trace); ! 1581: ETAP_CREATE_ENTRY(entry, trace); ! 1582: ! 1583: simple_lock(&l->interlock); ! 1584: ! 1585: if (l->want_write || l->want_upgrade || l->read_count) { ! 1586: /* ! 1587: * Can't get lock. ! 1588: */ ! 1589: simple_unlock(&l->interlock); ! 1590: ETAP_DESTROY_ENTRY(entry); ! 1591: return(FALSE); ! 1592: } ! 1593: ! 1594: /* ! 1595: * Have lock. ! 1596: */ ! 1597: ! 1598: l->want_write = TRUE; ! 1599: ! 1600: ETAP_LINK_ENTRY(l, entry, trace); ! 1601: ! 1602: simple_unlock(&l->interlock); ! 1603: ! 1604: MON_ASSIGN_PC(entry->start_pc, pc, trace); ! 1605: ETAP_DURATION_TIMESTAMP(entry, trace); ! 1606: ! 1607: return(TRUE); ! 1608: } ! 1609: ! 1610: /* ! 1611: * Routine: lock_try_read ! 1612: * Function: ! 1613: * Tries to get a read lock. ! 1614: * ! 1615: * Returns FALSE if the lock is not held on return. ! 1616: */ ! 1617: ! 1618: boolean_t ! 1619: lock_try_read( ! 1620: register lock_t * l) ! 1621: { ! 1622: start_data_node_t entry = {0}; ! 1623: unsigned short trace = 0; ! 1624: pc_t pc; ! 1625: ! 1626: ETAP_STAMP(lock_event_table(l), trace, trace); ! 1627: ETAP_CREATE_ENTRY(entry, trace); ! 1628: ! 1629: simple_lock(&l->interlock); ! 1630: ! 1631: if (l->want_write || l->want_upgrade) { ! 1632: simple_unlock(&l->interlock); ! 1633: ETAP_DESTROY_ENTRY(entry); ! 1634: return(FALSE); ! 1635: } ! 1636: ! 1637: l->read_count++; ! 1638: ! 1639: ETAP_LINK_ENTRY(l, entry, trace); ! 1640: ! 1641: simple_unlock(&l->interlock); ! 1642: ! 1643: MON_ASSIGN_PC(entry->start_pc, pc, trace); ! 1644: ETAP_DURATION_TIMESTAMP(entry, trace); ! 1645: ! 1646: return(TRUE); ! 1647: } ! 1648: #endif /* Unused */ ! 1649: ! 1650: #if MACH_KDB ! 1651: ! 1652: void db_show_one_lock(lock_t *); ! 1653: ! 1654: ! 1655: void ! 1656: db_show_one_lock( ! 1657: lock_t *lock) ! 1658: { ! 1659: db_printf("Read_count = 0x%x, %swant_upgrade, %swant_write, ", ! 1660: lock->read_count, ! 1661: lock->want_upgrade ? "" : "!", ! 1662: lock->want_write ? "" : "!"); ! 1663: db_printf("%swaiting, %scan_sleep\n", ! 1664: lock->waiting ? "" : "!", lock->can_sleep ? "" : "!"); ! 1665: db_printf("Interlock:\n"); ! 1666: db_show_one_simple_lock((db_expr_t)simple_lock_addr(lock->interlock), ! 1667: TRUE, (db_expr_t)0, (char *)0); ! 1668: } ! 1669: #endif /* MACH_KDB */ ! 1670: ! 1671: /* ! 1672: * The C portion of the mutex package. These routines are only invoked ! 1673: * if the optimized assembler routines can't do the work. ! 1674: */ ! 1675: ! 1676: /* ! 1677: * Routine: lock_alloc ! 1678: * Function: ! 1679: * Allocate a mutex for external users who cannot ! 1680: * hard-code the structure definition into their ! 1681: * objects. ! 1682: * For now just use kalloc, but a zone is probably ! 1683: * warranted. ! 1684: */ ! 1685: mutex_t * ! 1686: mutex_alloc( ! 1687: etap_event_t event) ! 1688: { ! 1689: mutex_t *m; ! 1690: ! 1691: if ((m = (mutex_t *)kalloc(sizeof(mutex_t))) != 0) ! 1692: mutex_init(m, event); ! 1693: return(m); ! 1694: } ! 1695: ! 1696: /* ! 1697: * Routine: mutex_free ! 1698: * Function: ! 1699: * Free a mutex allocated for external users. ! 1700: * For now just use kfree, but a zone is probably ! 1701: * warranted. ! 1702: */ ! 1703: void ! 1704: mutex_free( ! 1705: mutex_t *m) ! 1706: { ! 1707: kfree((vm_offset_t)m, sizeof(mutex_t)); ! 1708: } ! 1709: ! 1710: ! 1711: /* ! 1712: * mutex_lock_wait: Invoked if the assembler routine mutex_lock () fails ! 1713: * because the mutex is already held by another thread. Called with the ! 1714: * interlock locked and returns with the interlock unlocked. ! 1715: */ ! 1716: ! 1717: void ! 1718: mutex_lock_wait ( ! 1719: mutex_t * m) ! 1720: { ! 1721: m->waiters++; ! 1722: ETAP_SET_REASON(current_thread(), BLOCKED_ON_MUTEX_LOCK); ! 1723: thread_sleep_interlock ((event_t) m, &m->interlock, THREAD_UNINT); ! 1724: } ! 1725: ! 1726: /* ! 1727: * mutex_unlock_wakeup: Invoked if the assembler routine mutex_unlock () ! 1728: * fails because there are thread(s) waiting for this mutex. Called and ! 1729: * returns with the interlock locked. ! 1730: */ ! 1731: ! 1732: void ! 1733: mutex_unlock_wakeup ( ! 1734: mutex_t * m) ! 1735: { ! 1736: assert(m->waiters); ! 1737: m->waiters--; ! 1738: thread_wakeup_one ((event_t) m); ! 1739: } ! 1740: ! 1741: /* ! 1742: * mutex_pause: Called by former callers of simple_lock_pause(). ! 1743: */ ! 1744: ! 1745: void ! 1746: mutex_pause(void) ! 1747: { ! 1748: assert_wait_timeout( 1, THREAD_INTERRUPTIBLE); ! 1749: ETAP_SET_REASON(current_thread(), BLOCKED_ON_MUTEX_LOCK); ! 1750: thread_block(0); ! 1751: thread_cancel_timer(); ! 1752: } ! 1753: ! 1754: #if MACH_KDB ! 1755: /* ! 1756: * Routines to print out simple_locks and mutexes in a nicely-formatted ! 1757: * fashion. ! 1758: */ ! 1759: ! 1760: char *simple_lock_labels = "ENTRY ILK THREAD DURATION CALLER"; ! 1761: char *mutex_labels = "ENTRY LOCKED WAITERS THREAD CALLER"; ! 1762: ! 1763: void ! 1764: db_show_one_simple_lock ( ! 1765: db_expr_t addr, ! 1766: boolean_t have_addr, ! 1767: db_expr_t count, ! 1768: char * modif) ! 1769: { ! 1770: simple_lock_t saddr = (simple_lock_t)addr; ! 1771: ! 1772: if (saddr == (simple_lock_t)0 || !have_addr) { ! 1773: db_error ("No simple_lock\n"); ! 1774: } ! 1775: #if USLOCK_DEBUG ! 1776: else if (saddr->lock_type != USLOCK_TAG) ! 1777: db_error ("Not a simple_lock\n"); ! 1778: #endif /* USLOCK_DEBUG */ ! 1779: ! 1780: db_printf ("%s\n", simple_lock_labels); ! 1781: db_print_simple_lock (saddr); ! 1782: } ! 1783: ! 1784: void ! 1785: db_print_simple_lock ( ! 1786: simple_lock_t addr) ! 1787: { ! 1788: ! 1789: db_printf ("%08x %3d", addr, *hw_lock_addr(addr->interlock)); ! 1790: #if USLOCK_DEBUG ! 1791: db_printf (" %08x", addr->debug.lock_thread); ! 1792: db_printf (" %08x ", addr->debug.duration[1]); ! 1793: db_printsym ((int)addr->debug.lock_pc, DB_STGY_ANY); ! 1794: #endif /* USLOCK_DEBUG */ ! 1795: db_printf ("\n"); ! 1796: } ! 1797: ! 1798: void ! 1799: db_show_one_mutex ( ! 1800: db_expr_t addr, ! 1801: boolean_t have_addr, ! 1802: db_expr_t count, ! 1803: char * modif) ! 1804: { ! 1805: mutex_t * maddr = (mutex_t *)addr; ! 1806: ! 1807: if (maddr == (mutex_t *)0 || !have_addr) ! 1808: db_error ("No mutex\n"); ! 1809: #if MACH_LDEBUG ! 1810: else if (maddr->type != MUTEX_TAG) ! 1811: db_error ("Not a mutex\n"); ! 1812: #endif /* MACH_LDEBUG */ ! 1813: ! 1814: db_printf ("%s\n", mutex_labels); ! 1815: db_print_mutex (maddr); ! 1816: } ! 1817: ! 1818: void ! 1819: db_print_mutex ( ! 1820: mutex_t * addr) ! 1821: { ! 1822: db_printf ("%08x %6d %7d", ! 1823: addr, *hw_lock_addr(addr->locked), addr->waiters); ! 1824: #if MACH_LDEBUG ! 1825: db_printf (" %08x ", addr->thread); ! 1826: db_printsym (addr->pc, DB_STGY_ANY); ! 1827: #endif /* MACH_LDEBUG */ ! 1828: db_printf ("\n"); ! 1829: } ! 1830: #endif /* MACH_KDB */ ! 1831: ! 1832: #if MACH_LDEBUG ! 1833: extern void meter_simple_lock ( ! 1834: simple_lock_t l); ! 1835: extern void meter_simple_unlock ( ! 1836: simple_lock_t l); ! 1837: extern void cyctm05_stamp ( ! 1838: unsigned long * start); ! 1839: extern void cyctm05_diff ( ! 1840: unsigned long * start, ! 1841: unsigned long * end, ! 1842: unsigned long * diff); ! 1843: ! 1844: #if 0 ! 1845: simple_lock_data_t loser; ! 1846: #endif ! 1847: ! 1848: void ! 1849: meter_simple_lock( ! 1850: simple_lock_t lp) ! 1851: { ! 1852: #if 0 ! 1853: cyctm05_stamp (lp->duration); ! 1854: #endif ! 1855: } ! 1856: ! 1857: int long_simple_lock_crash; ! 1858: int long_simple_lock_time = 0x600; ! 1859: /* ! 1860: * This is pretty gawd-awful. XXX ! 1861: */ ! 1862: decl_simple_lock_data(extern,kd_tty) ! 1863: ! 1864: void ! 1865: meter_simple_unlock( ! 1866: simple_lock_t lp) ! 1867: { ! 1868: #if 0 ! 1869: unsigned long stime[2], etime[2], delta[2]; ! 1870: ! 1871: if (lp == &kd_tty) /* XXX */ ! 1872: return; /* XXX */ ! 1873: ! 1874: stime[0] = lp->duration[0]; ! 1875: stime[1] = lp->duration[1]; ! 1876: ! 1877: cyctm05_stamp (etime); ! 1878: ! 1879: if (etime[1] < stime[1]) /* XXX */ ! 1880: return; /* XXX */ ! 1881: ! 1882: cyctm05_diff (stime, etime, delta); ! 1883: ! 1884: if (delta[1] >= 0x10000) /* XXX */ ! 1885: return; /* XXX */ ! 1886: ! 1887: lp->duration[0] = delta[0]; ! 1888: lp->duration[1] = delta[1]; ! 1889: ! 1890: if (loser.duration[1] < lp->duration[1]) ! 1891: loser = *lp; ! 1892: ! 1893: assert (!long_simple_lock_crash || delta[1] < long_simple_lock_time); ! 1894: #endif ! 1895: } ! 1896: #endif /* MACH_LDEBUG */ ! 1897: ! 1898: ! 1899: #if ETAP_LOCK_TRACE ! 1900: ! 1901: /* ! 1902: * ============================================================== ! 1903: * ETAP hook when initializing a usimple_lock. May be invoked ! 1904: * from the portable lock package or from an optimized machine- ! 1905: * dependent implementation. ! 1906: * ============================================================== ! 1907: */ ! 1908: ! 1909: void ! 1910: etap_simplelock_init ( ! 1911: simple_lock_t l, ! 1912: etap_event_t event) ! 1913: { ! 1914: ETAP_CLEAR_TRACE_DATA(l); ! 1915: etap_event_table_assign(&l->u.event_table_chain, event); ! 1916: ! 1917: #if ETAP_LOCK_ACCUMULATE ! 1918: /* reserve an entry in the cumulative buffer */ ! 1919: l->cbuff_entry = etap_cbuff_reserve(lock_event_table(l)); ! 1920: /* initialize the entry if one was returned */ ! 1921: if (l->cbuff_entry != CBUFF_ENTRY_NULL) { ! 1922: l->cbuff_entry->event = event; ! 1923: l->cbuff_entry->instance = (unsigned long) l; ! 1924: l->cbuff_entry->kind = SPIN_LOCK; ! 1925: } ! 1926: #endif /* ETAP_LOCK_ACCUMULATE */ ! 1927: } ! 1928: ! 1929: ! 1930: void ! 1931: etap_simplelock_unlock( ! 1932: simple_lock_t l) ! 1933: { ! 1934: unsigned short dynamic = 0; ! 1935: unsigned short trace = 0; ! 1936: etap_time_t total_time; ! 1937: etap_time_t stop_hold_time; ! 1938: pc_t pc; ! 1939: ! 1940: OBTAIN_PC(pc, l); ! 1941: ETAP_STAMP(lock_event_table(l), trace, dynamic); ! 1942: ! 1943: /* ! 1944: * Calculate & collect hold time data only if ! 1945: * the hold tracing was enabled throughout the ! 1946: * whole operation. This prevents collection of ! 1947: * bogus data caused by mid-operation trace changes. ! 1948: * ! 1949: */ ! 1950: ! 1951: if (ETAP_DURATION_ENABLED(trace) && ETAP_WHOLE_OP(l)) { ! 1952: ETAP_TIMESTAMP (stop_hold_time); ! 1953: ETAP_TOTAL_TIME(total_time, stop_hold_time, ! 1954: l->u.s.start_hold_time); ! 1955: CUM_HOLD_ACCUMULATE(l->cbuff_entry, total_time, dynamic, trace); ! 1956: MON_ASSIGN_PC(l->end_pc, pc, trace); ! 1957: MON_DATA_COLLECT(l, ! 1958: l, ! 1959: total_time, ! 1960: SPIN_LOCK, ! 1961: MON_DURATION, ! 1962: trace); ! 1963: } ! 1964: ETAP_CLEAR_TRACE_DATA(l); ! 1965: } ! 1966: ! 1967: /* ======================================================================== ! 1968: * Since the the simple_lock() routine is machine dependant, it must always ! 1969: * be coded in assembly. The two hook routines below are used to collect ! 1970: * lock_stat data. ! 1971: * ======================================================================== ! 1972: */ ! 1973: ! 1974: /* ! 1975: * ROUTINE: etap_simplelock_miss() ! 1976: * ! 1977: * FUNCTION: This spin lock routine is called upon the first ! 1978: * spin (miss) of the lock. ! 1979: * ! 1980: * A timestamp is taken at the beginning of the wait period, ! 1981: * if wait tracing is enabled. ! 1982: * ! 1983: * ! 1984: * PARAMETERS: ! 1985: * - lock address. ! 1986: * - timestamp address. ! 1987: * ! 1988: * RETURNS: Wait timestamp value. The timestamp value is later used ! 1989: * by etap_simplelock_hold(). ! 1990: * ! 1991: * NOTES: This routine is NOT ALWAYS called. The lock may be free ! 1992: * (never spinning). For this reason the pc is collected in ! 1993: * etap_simplelock_hold(). ! 1994: * ! 1995: */ ! 1996: etap_time_t ! 1997: etap_simplelock_miss ( ! 1998: simple_lock_t l) ! 1999: ! 2000: { ! 2001: unsigned short trace = 0; ! 2002: unsigned short dynamic = 0; ! 2003: etap_time_t start_miss_time; ! 2004: ! 2005: ETAP_STAMP(lock_event_table(l), trace, dynamic); ! 2006: ! 2007: if (trace & ETAP_CONTENTION) ! 2008: ETAP_TIMESTAMP(start_miss_time); ! 2009: ! 2010: return(start_miss_time); ! 2011: } ! 2012: ! 2013: /* ! 2014: * ROUTINE: etap_simplelock_hold() ! 2015: * ! 2016: * FUNCTION: This spin lock routine is ALWAYS called once the lock ! 2017: * is acquired. Here, the contention time is calculated and ! 2018: * the start hold time is stamped. ! 2019: * ! 2020: * PARAMETERS: ! 2021: * - lock address. ! 2022: * - PC of the calling function. ! 2023: * - start wait timestamp. ! 2024: * ! 2025: */ ! 2026: ! 2027: void ! 2028: etap_simplelock_hold ( ! 2029: simple_lock_t l, ! 2030: pc_t pc, ! 2031: etap_time_t start_hold_time) ! 2032: { ! 2033: unsigned short dynamic = 0; ! 2034: unsigned short trace = 0; ! 2035: etap_time_t total_time; ! 2036: etap_time_t stop_hold_time; ! 2037: ! 2038: ETAP_STAMP(lock_event_table(l), trace, dynamic); ! 2039: ! 2040: MON_ASSIGN_PC(l->start_pc, pc, trace); ! 2041: ! 2042: /* do not collect wait data if lock was free */ ! 2043: if (ETAP_TIME_IS_ZERO(start_hold_time) && (trace & ETAP_CONTENTION)) { ! 2044: ETAP_TIMESTAMP(stop_hold_time); ! 2045: ETAP_TOTAL_TIME(total_time, ! 2046: stop_hold_time, ! 2047: start_hold_time); ! 2048: CUM_WAIT_ACCUMULATE(l->cbuff_entry, total_time, dynamic, trace); ! 2049: MON_DATA_COLLECT(l, ! 2050: l, ! 2051: total_time, ! 2052: SPIN_LOCK, ! 2053: MON_CONTENTION, ! 2054: trace); ! 2055: ETAP_COPY_START_HOLD_TIME(&l->u.s, stop_hold_time, trace); ! 2056: } ! 2057: else ! 2058: ETAP_DURATION_TIMESTAMP(&l->u.s, trace); ! 2059: } ! 2060: ! 2061: void ! 2062: etap_mutex_init ( ! 2063: mutex_t *l, ! 2064: etap_event_t event) ! 2065: { ! 2066: ETAP_CLEAR_TRACE_DATA(l); ! 2067: etap_event_table_assign(&l->u.event_table_chain, event); ! 2068: ! 2069: #if ETAP_LOCK_ACCUMULATE ! 2070: /* reserve an entry in the cumulative buffer */ ! 2071: l->cbuff_entry = etap_cbuff_reserve(lock_event_table(l)); ! 2072: /* initialize the entry if one was returned */ ! 2073: if (l->cbuff_entry != CBUFF_ENTRY_NULL) { ! 2074: l->cbuff_entry->event = event; ! 2075: l->cbuff_entry->instance = (unsigned long) l; ! 2076: l->cbuff_entry->kind = MUTEX_LOCK; ! 2077: } ! 2078: #endif /* ETAP_LOCK_ACCUMULATE */ ! 2079: } ! 2080: ! 2081: etap_time_t ! 2082: etap_mutex_miss ( ! 2083: mutex_t *l) ! 2084: { ! 2085: unsigned short trace = 0; ! 2086: unsigned short dynamic = 0; ! 2087: etap_time_t start_miss_time; ! 2088: ! 2089: ETAP_STAMP(lock_event_table(l), trace, dynamic); ! 2090: ! 2091: if (trace & ETAP_CONTENTION) ! 2092: ETAP_TIMESTAMP(start_miss_time); ! 2093: else ! 2094: ETAP_TIME_CLEAR(start_miss_time); ! 2095: ! 2096: return(start_miss_time); ! 2097: } ! 2098: ! 2099: void ! 2100: etap_mutex_hold ( ! 2101: mutex_t *l, ! 2102: pc_t pc, ! 2103: etap_time_t start_hold_time) ! 2104: { ! 2105: unsigned short dynamic = 0; ! 2106: unsigned short trace = 0; ! 2107: etap_time_t total_time; ! 2108: etap_time_t stop_hold_time; ! 2109: ! 2110: ETAP_STAMP(lock_event_table(l), trace, dynamic); ! 2111: ! 2112: MON_ASSIGN_PC(l->start_pc, pc, trace); ! 2113: ! 2114: /* do not collect wait data if lock was free */ ! 2115: if (!ETAP_TIME_IS_ZERO(start_hold_time) && (trace & ETAP_CONTENTION)) { ! 2116: ETAP_TIMESTAMP(stop_hold_time); ! 2117: ETAP_TOTAL_TIME(total_time, ! 2118: stop_hold_time, ! 2119: start_hold_time); ! 2120: CUM_WAIT_ACCUMULATE(l->cbuff_entry, total_time, dynamic, trace); ! 2121: MON_DATA_COLLECT(l, ! 2122: l, ! 2123: total_time, ! 2124: MUTEX_LOCK, ! 2125: MON_CONTENTION, ! 2126: trace); ! 2127: ETAP_COPY_START_HOLD_TIME(&l->u.s, stop_hold_time, trace); ! 2128: } ! 2129: else ! 2130: ETAP_DURATION_TIMESTAMP(&l->u.s, trace); ! 2131: } ! 2132: ! 2133: void ! 2134: etap_mutex_unlock( ! 2135: mutex_t *l) ! 2136: { ! 2137: unsigned short dynamic = 0; ! 2138: unsigned short trace = 0; ! 2139: etap_time_t total_time; ! 2140: etap_time_t stop_hold_time; ! 2141: pc_t pc; ! 2142: ! 2143: OBTAIN_PC(pc, l); ! 2144: ETAP_STAMP(lock_event_table(l), trace, dynamic); ! 2145: ! 2146: /* ! 2147: * Calculate & collect hold time data only if ! 2148: * the hold tracing was enabled throughout the ! 2149: * whole operation. This prevents collection of ! 2150: * bogus data caused by mid-operation trace changes. ! 2151: * ! 2152: */ ! 2153: ! 2154: if (ETAP_DURATION_ENABLED(trace) && ETAP_WHOLE_OP(l)) { ! 2155: ETAP_TIMESTAMP(stop_hold_time); ! 2156: ETAP_TOTAL_TIME(total_time, stop_hold_time, ! 2157: l->u.s.start_hold_time); ! 2158: CUM_HOLD_ACCUMULATE(l->cbuff_entry, total_time, dynamic, trace); ! 2159: MON_ASSIGN_PC(l->end_pc, pc, trace); ! 2160: MON_DATA_COLLECT(l, ! 2161: l, ! 2162: total_time, ! 2163: MUTEX_LOCK, ! 2164: MON_DURATION, ! 2165: trace); ! 2166: } ! 2167: ETAP_CLEAR_TRACE_DATA(l); ! 2168: } ! 2169: ! 2170: #endif /* ETAP_LOCK_TRACE */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.