1 /* 2 * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2002-2010, Axel Dörfler, axeld@pinc-software.de. 4 * Distributed under the terms of the MIT License. 5 * 6 * Copyright 2001, Travis Geiselbrecht. All rights reserved. 7 * Distributed under the terms of the NewOS License. 8 */ 9 10 11 /*! Semaphore code */ 12 13 14 #include <sem.h> 15 16 #include <stdlib.h> 17 #include <string.h> 18 19 #include <OS.h> 20 21 #include <arch/int.h> 22 #include <boot/kernel_args.h> 23 #include <cpu.h> 24 #include <debug.h> 25 #include <int.h> 26 #include <kernel.h> 27 #include <ksignal.h> 28 #include <kscheduler.h> 29 #include <listeners.h> 30 #include <scheduling_analysis.h> 31 #include <smp.h> 32 #include <syscall_restart.h> 33 #include <team.h> 34 #include <thread.h> 35 #include <util/AutoLock.h> 36 #include <util/DoublyLinkedList.h> 37 #include <vfs.h> 38 #include <vm/vm_page.h> 39 #include <wait_for_objects.h> 40 41 #include "kernel_debug_config.h" 42 43 44 //#define TRACE_SEM 45 #ifdef TRACE_SEM 46 # define TRACE(x) dprintf_no_syslog x 47 #else 48 # define TRACE(x) ; 49 #endif 50 51 //#define KTRACE_SEM 52 #ifdef KTRACE_SEM 53 # define KTRACE(x...) ktrace_printf(x) 54 #else 55 # define KTRACE(x...) do {} while (false) 56 #endif 57 58 59 // Locking: 60 // * sSemsSpinlock: Protects the semaphore free list (sFreeSemsHead, 61 // sFreeSemsTail), Team::sem_list, and together with sem_entry::lock 62 // write access to sem_entry::owner/team_link. 63 // * sem_entry::lock: Protects all sem_entry members. owner, team_link 64 // additional need sSemsSpinlock for write access. 65 // lock itself doesn't need protection -- sem_entry objects are never deleted. 66 // 67 // The locking order is sSemsSpinlock -> sem_entry::lock -> scheduler lock. All 68 // semaphores are in the sSems array (sem_entry[]). Access by sem_id requires 69 // computing the object index (id % sMaxSems), locking the respective 70 // sem_entry::lock and verifying that sem_entry::id matches afterwards. 71 72 73 struct queued_thread : DoublyLinkedListLinkImpl<queued_thread> { 74 queued_thread(Thread *thread, int32 count) 75 : 76 thread(thread), 77 count(count), 78 queued(false) 79 { 80 } 81 82 Thread *thread; 83 int32 count; 84 bool queued; 85 }; 86 87 typedef DoublyLinkedList<queued_thread> ThreadQueue; 88 89 struct sem_entry { 90 union { 91 // when slot in use 92 struct { 93 struct list_link team_link; 94 int32 count; 95 int32 net_count; 96 // count + acquisition count of all blocked 97 // threads 98 char* name; 99 team_id owner; 100 select_info* select_infos; 101 thread_id last_acquirer; 102 #if DEBUG_SEM_LAST_ACQUIRER 103 int32 last_acquire_count; 104 thread_id last_releaser; 105 int32 last_release_count; 106 #endif 107 } used; 108 109 // when slot unused 110 struct { 111 sem_id next_id; 112 struct sem_entry* next; 113 } unused; 114 } u; 115 116 sem_id id; 117 spinlock lock; // protects only the id field when unused 118 ThreadQueue queue; // should be in u.used, but has a constructor 119 }; 120 121 static const int32 kMaxSemaphores = 65536; 122 static int32 sMaxSems = 4096; 123 // Final value is computed based on the amount of available memory 124 static int32 sUsedSems = 0; 125 126 static struct sem_entry *sSems = NULL; 127 static bool sSemsActive = false; 128 static struct sem_entry *sFreeSemsHead = NULL; 129 static struct sem_entry *sFreeSemsTail = NULL; 130 131 static spinlock sSemsSpinlock = B_SPINLOCK_INITIALIZER; 132 133 134 static int 135 dump_sem_list(int argc, char** argv) 136 { 137 const char* name = NULL; 138 team_id owner = -1; 139 thread_id last = -1; 140 int32 i; 141 142 if (argc > 2) { 143 if (!strcmp(argv[1], "team") || !strcmp(argv[1], "owner")) 144 owner = strtoul(argv[2], NULL, 0); 145 else if (!strcmp(argv[1], "name")) 146 name = argv[2]; 147 else if (!strcmp(argv[1], "last")) 148 last = strtoul(argv[2], NULL, 0); 149 } else if (argc > 1) 150 owner = strtoul(argv[1], NULL, 0); 151 152 kprintf("%-*s id count team last name\n", B_PRINTF_POINTER_WIDTH, 153 "sem"); 154 155 for (i = 0; i < sMaxSems; i++) { 156 struct sem_entry* sem = &sSems[i]; 157 if (sem->id < 0 158 || (last != -1 && sem->u.used.last_acquirer != last) 159 || (name != NULL && strstr(sem->u.used.name, name) == NULL) 160 || (owner != -1 && sem->u.used.owner != owner)) 161 continue; 162 163 kprintf("%p %6" B_PRId32 " %5" B_PRId32 " %6" B_PRId32 " " 164 "%6" B_PRId32 " " 165 " %s\n", sem, sem->id, sem->u.used.count, 166 sem->u.used.owner, 167 sem->u.used.last_acquirer > 0 ? sem->u.used.last_acquirer : 0, 168 sem->u.used.name); 169 } 170 171 return 0; 172 } 173 174 175 static void 176 dump_sem(struct sem_entry* sem) 177 { 178 kprintf("SEM: %p\n", sem); 179 kprintf("id: %" B_PRId32 " (%#" B_PRIx32 ")\n", sem->id, sem->id); 180 if (sem->id >= 0) { 181 kprintf("name: '%s'\n", sem->u.used.name); 182 kprintf("owner: %" B_PRId32 "\n", sem->u.used.owner); 183 kprintf("count: %" B_PRId32 "\n", sem->u.used.count); 184 kprintf("queue: "); 185 if (!sem->queue.IsEmpty()) { 186 ThreadQueue::Iterator it = sem->queue.GetIterator(); 187 while (queued_thread* entry = it.Next()) 188 kprintf(" %" B_PRId32, entry->thread->id); 189 kprintf("\n"); 190 } else 191 kprintf(" -\n"); 192 193 set_debug_variable("_sem", (addr_t)sem); 194 set_debug_variable("_semID", sem->id); 195 set_debug_variable("_owner", sem->u.used.owner); 196 197 #if DEBUG_SEM_LAST_ACQUIRER 198 kprintf("last acquired by: %" B_PRId32 ", count: %" B_PRId32 "\n", 199 sem->u.used.last_acquirer, sem->u.used.last_acquire_count); 200 kprintf("last released by: %" B_PRId32 ", count: %" B_PRId32 "\n", 201 sem->u.used.last_releaser, sem->u.used.last_release_count); 202 203 if (sem->u.used.last_releaser != 0) 204 set_debug_variable("_releaser", sem->u.used.last_releaser); 205 else 206 unset_debug_variable("_releaser"); 207 #else 208 kprintf("last acquired by: %" B_PRId32 "\n", sem->u.used.last_acquirer); 209 #endif 210 211 if (sem->u.used.last_acquirer != 0) 212 set_debug_variable("_acquirer", sem->u.used.last_acquirer); 213 else 214 unset_debug_variable("_acquirer"); 215 } else { 216 kprintf("next: %p\n", sem->u.unused.next); 217 kprintf("next_id: %" B_PRId32 "\n", sem->u.unused.next_id); 218 } 219 } 220 221 222 static int 223 dump_sem_info(int argc, char **argv) 224 { 225 bool found = false; 226 addr_t num; 227 int32 i; 228 229 if (argc < 2) { 230 print_debugger_command_usage(argv[0]); 231 return 0; 232 } 233 234 num = strtoul(argv[1], NULL, 0); 235 236 if (IS_KERNEL_ADDRESS(num)) { 237 dump_sem((struct sem_entry *)num); 238 return 0; 239 } else if (num >= 0) { 240 uint32 slot = num % sMaxSems; 241 if (sSems[slot].id != (int)num) { 242 kprintf("sem %ld (%#lx) doesn't exist!\n", num, num); 243 return 0; 244 } 245 246 dump_sem(&sSems[slot]); 247 return 0; 248 } 249 250 // walk through the sem list, trying to match name 251 for (i = 0; i < sMaxSems; i++) { 252 if (sSems[i].u.used.name != NULL 253 && strcmp(argv[1], sSems[i].u.used.name) == 0) { 254 dump_sem(&sSems[i]); 255 found = true; 256 } 257 } 258 259 if (!found) 260 kprintf("sem \"%s\" doesn't exist!\n", argv[1]); 261 return 0; 262 } 263 264 265 /*! \brief Appends a semaphore slot to the free list. 266 267 The semaphore list must be locked. 268 The slot's id field is not changed. It should already be set to -1. 269 270 \param slot The index of the semaphore slot. 271 \param nextID The ID the slot will get when reused. If < 0 the \a slot 272 is used. 273 */ 274 static void 275 free_sem_slot(int slot, sem_id nextID) 276 { 277 struct sem_entry *sem = sSems + slot; 278 // set next_id to the next possible value; for sanity check the current ID 279 if (nextID < 0) 280 sem->u.unused.next_id = slot; 281 else 282 sem->u.unused.next_id = nextID; 283 // append the entry to the list 284 if (sFreeSemsTail) 285 sFreeSemsTail->u.unused.next = sem; 286 else 287 sFreeSemsHead = sem; 288 sFreeSemsTail = sem; 289 sem->u.unused.next = NULL; 290 } 291 292 293 static inline void 294 notify_sem_select_events(struct sem_entry* sem, uint16 events) 295 { 296 if (sem->u.used.select_infos) 297 notify_select_events_list(sem->u.used.select_infos, events); 298 } 299 300 301 /*! Fills the sem_info structure with information from the given semaphore. 302 The semaphore's lock must be held when called. 303 */ 304 static void 305 fill_sem_info(struct sem_entry* sem, sem_info* info, size_t size) 306 { 307 info->sem = sem->id; 308 info->team = sem->u.used.owner; 309 strlcpy(info->name, sem->u.used.name, sizeof(info->name)); 310 info->count = sem->u.used.count; 311 info->latest_holder = sem->u.used.last_acquirer; 312 } 313 314 315 /*! You must call this function with interrupts disabled, and the semaphore's 316 spinlock held. Note that it will unlock the spinlock itself. 317 Since it cannot free() the semaphore's name with interrupts turned off, it 318 will return that one in \a name. 319 */ 320 static void 321 uninit_sem_locked(struct sem_entry& sem, char** _name, SpinLocker& locker) 322 { 323 KTRACE("delete_sem(sem: %ld)", sem.u.used.id); 324 325 notify_sem_select_events(&sem, B_EVENT_INVALID); 326 sem.u.used.select_infos = NULL; 327 328 // free any threads waiting for this semaphore 329 while (queued_thread* entry = sem.queue.RemoveHead()) { 330 entry->queued = false; 331 thread_unblock(entry->thread, B_BAD_SEM_ID); 332 } 333 334 int32 id = sem.id; 335 sem.id = -1; 336 *_name = sem.u.used.name; 337 sem.u.used.name = NULL; 338 339 locker.Unlock(); 340 341 // append slot to the free list 342 SpinLocker _(&sSemsSpinlock); 343 free_sem_slot(id % sMaxSems, id + sMaxSems); 344 atomic_add(&sUsedSems, -1); 345 } 346 347 348 static status_t 349 delete_sem_internal(sem_id id, bool checkPermission) 350 { 351 if (sSemsActive == false) 352 return B_NO_MORE_SEMS; 353 if (id < 0) 354 return B_BAD_SEM_ID; 355 356 int32 slot = id % sMaxSems; 357 358 InterruptsLocker interruptsLocker; 359 SpinLocker listLocker(sSemsSpinlock); 360 SpinLocker semLocker(sSems[slot].lock); 361 362 if (sSems[slot].id != id) { 363 TRACE(("delete_sem: invalid sem_id %ld\n", id)); 364 return B_BAD_SEM_ID; 365 } 366 367 if (checkPermission 368 && sSems[slot].u.used.owner == team_get_kernel_team_id()) { 369 dprintf("thread %" B_PRId32 " tried to delete kernel semaphore " 370 "%" B_PRId32 ".\n", thread_get_current_thread_id(), id); 371 return B_NOT_ALLOWED; 372 } 373 374 if (sSems[slot].u.used.owner >= 0) { 375 list_remove_link(&sSems[slot].u.used.team_link); 376 sSems[slot].u.used.owner = -1; 377 } else 378 panic("sem %" B_PRId32 " has no owner", id); 379 380 listLocker.Unlock(); 381 382 char* name; 383 uninit_sem_locked(sSems[slot], &name, semLocker); 384 385 SpinLocker schedulerLocker(thread_get_current_thread()->scheduler_lock); 386 scheduler_reschedule_if_necessary_locked(); 387 schedulerLocker.Unlock(); 388 389 interruptsLocker.Unlock(); 390 391 free(name); 392 return B_OK; 393 } 394 395 396 // #pragma mark - Private Kernel API 397 398 399 // TODO: Name clash with POSIX sem_init()... (we could just use C++) 400 status_t 401 haiku_sem_init(kernel_args *args) 402 { 403 area_id area; 404 int32 i; 405 406 TRACE(("sem_init: entry\n")); 407 408 // compute maximal number of semaphores depending on the available memory 409 // 128 MB -> 16384 semaphores, 448 kB fixed array size 410 // 256 MB -> 32768, 896 kB 411 // 512 MB and more-> 65536, 1.75 MB 412 i = vm_page_num_pages() / 2; 413 while (sMaxSems < i && sMaxSems < kMaxSemaphores) 414 sMaxSems <<= 1; 415 416 // create and initialize semaphore table 417 virtual_address_restrictions virtualRestrictions = {}; 418 virtualRestrictions.address_specification = B_ANY_KERNEL_ADDRESS; 419 physical_address_restrictions physicalRestrictions = {}; 420 area = create_area_etc(B_SYSTEM_TEAM, "sem_table", 421 sizeof(struct sem_entry) * sMaxSems, B_FULL_LOCK, 422 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, CREATE_AREA_DONT_WAIT, 0, 423 &virtualRestrictions, &physicalRestrictions, (void**)&sSems); 424 if (area < 0) 425 panic("unable to allocate semaphore table!\n"); 426 427 memset(sSems, 0, sizeof(struct sem_entry) * sMaxSems); 428 for (i = 0; i < sMaxSems; i++) { 429 sSems[i].id = -1; 430 free_sem_slot(i, i); 431 } 432 433 // add debugger commands 434 add_debugger_command_etc("sems", &dump_sem_list, 435 "Dump a list of all active semaphores (for team, with name, etc.)", 436 "[ ([ \"team\" | \"owner\" ] <team>) | (\"name\" <name>) ]" 437 " | (\"last\" <last acquirer>)\n" 438 "Prints a list of all active semaphores meeting the given\n" 439 "requirement. If no argument is given, all sems are listed.\n" 440 " <team> - The team owning the semaphores.\n" 441 " <name> - Part of the name of the semaphores.\n" 442 " <last acquirer> - The thread that last acquired the semaphore.\n" 443 , 0); 444 add_debugger_command_etc("sem", &dump_sem_info, 445 "Dump info about a particular semaphore", 446 "<sem>\n" 447 "Prints info about the specified semaphore.\n" 448 " <sem> - pointer to the semaphore structure, semaphore ID, or name\n" 449 " of the semaphore to print info for.\n", 0); 450 451 TRACE(("sem_init: exit\n")); 452 453 sSemsActive = true; 454 455 return 0; 456 } 457 458 459 /*! Creates a semaphore with the given parameters. 460 461 This function is only available from within the kernel, and 462 should not be made public - if possible, we should remove it 463 completely (and have only create_sem() exported). 464 */ 465 sem_id 466 create_sem_etc(int32 count, const char* name, team_id owner) 467 { 468 struct sem_entry* sem = NULL; 469 sem_id id = B_NO_MORE_SEMS; 470 char* tempName; 471 size_t nameLength; 472 473 if (sSemsActive == false || sUsedSems == sMaxSems) 474 return B_NO_MORE_SEMS; 475 476 if (name == NULL) 477 name = "unnamed semaphore"; 478 479 // get the owning team 480 Team* team = Team::Get(owner); 481 if (team == NULL) 482 return B_BAD_TEAM_ID; 483 BReference<Team> teamReference(team, true); 484 485 // clone the name 486 nameLength = strlen(name) + 1; 487 nameLength = min_c(nameLength, B_OS_NAME_LENGTH); 488 tempName = (char*)malloc(nameLength); 489 if (tempName == NULL) 490 return B_NO_MEMORY; 491 492 strlcpy(tempName, name, nameLength); 493 494 InterruptsSpinLocker _(&sSemsSpinlock); 495 496 // get the first slot from the free list 497 sem = sFreeSemsHead; 498 if (sem) { 499 // remove it from the free list 500 sFreeSemsHead = sem->u.unused.next; 501 if (!sFreeSemsHead) 502 sFreeSemsTail = NULL; 503 504 // init the slot 505 SpinLocker semLocker(sem->lock); 506 sem->id = sem->u.unused.next_id; 507 sem->u.used.count = count; 508 sem->u.used.net_count = count; 509 new(&sem->queue) ThreadQueue; 510 sem->u.used.name = tempName; 511 sem->u.used.owner = team->id; 512 sem->u.used.select_infos = NULL; 513 id = sem->id; 514 515 list_add_item(&team->sem_list, &sem->u.used.team_link); 516 517 semLocker.Unlock(); 518 519 atomic_add(&sUsedSems, 1); 520 521 KTRACE("create_sem_etc(count: %ld, name: %s, owner: %ld) -> %ld", 522 count, name, owner, id); 523 524 T_SCHEDULING_ANALYSIS(CreateSemaphore(id, name)); 525 NotifyWaitObjectListeners(&WaitObjectListener::SemaphoreCreated, id, 526 name); 527 } 528 529 if (sem == NULL) 530 free(tempName); 531 532 return id; 533 } 534 535 536 status_t 537 select_sem(int32 id, struct select_info* info, bool kernel) 538 { 539 if (id < 0) 540 return B_BAD_SEM_ID; 541 542 int32 slot = id % sMaxSems; 543 544 InterruptsSpinLocker _(&sSems[slot].lock); 545 546 if (sSems[slot].id != id) { 547 // bad sem ID 548 return B_BAD_SEM_ID; 549 } else if (!kernel 550 && sSems[slot].u.used.owner == team_get_kernel_team_id()) { 551 // kernel semaphore, but call from userland 552 return B_NOT_ALLOWED; 553 } else { 554 info->selected_events &= B_EVENT_ACQUIRE_SEMAPHORE | B_EVENT_INVALID; 555 556 if (info->selected_events != 0) { 557 info->next = sSems[slot].u.used.select_infos; 558 sSems[slot].u.used.select_infos = info; 559 560 if (sSems[slot].u.used.count > 0) 561 notify_select_events(info, B_EVENT_ACQUIRE_SEMAPHORE); 562 } 563 } 564 565 return B_OK; 566 } 567 568 569 status_t 570 deselect_sem(int32 id, struct select_info* info, bool kernel) 571 { 572 if (id < 0) 573 return B_BAD_SEM_ID; 574 575 if (info->selected_events == 0) 576 return B_OK; 577 578 int32 slot = id % sMaxSems; 579 580 InterruptsSpinLocker _(&sSems[slot].lock); 581 582 if (sSems[slot].id == id) { 583 select_info** infoLocation = &sSems[slot].u.used.select_infos; 584 while (*infoLocation != NULL && *infoLocation != info) 585 infoLocation = &(*infoLocation)->next; 586 587 if (*infoLocation == info) 588 *infoLocation = info->next; 589 } 590 591 return B_OK; 592 } 593 594 595 /*! Forcibly removes a thread from a semaphores wait queue. May have to wake up 596 other threads in the process. 597 Must be called with semaphore lock held. The thread lock must not be held. 598 */ 599 static void 600 remove_thread_from_sem(queued_thread *entry, struct sem_entry *sem) 601 { 602 if (!entry->queued) 603 return; 604 605 sem->queue.Remove(entry); 606 entry->queued = false; 607 sem->u.used.count += entry->count; 608 609 // We're done with this entry. We only have to check, if other threads 610 // need unblocking, too. 611 612 // Now see if more threads need to be woken up. We get the scheduler lock 613 // for that time, so the blocking state of threads won't change (due to 614 // interruption or timeout). We need that lock anyway when unblocking a 615 // thread. 616 while ((entry = sem->queue.Head()) != NULL) { 617 SpinLocker schedulerLocker(entry->thread->scheduler_lock); 618 if (thread_is_blocked(entry->thread)) { 619 // The thread is still waiting. If its count is satisfied, unblock 620 // it. Otherwise we can't unblock any other thread. 621 if (entry->count > sem->u.used.net_count) 622 break; 623 624 thread_unblock_locked(entry->thread, B_OK); 625 sem->u.used.net_count -= entry->count; 626 } else { 627 // The thread is no longer waiting, but still queued, which means 628 // acquiration failed and we can just remove it. 629 sem->u.used.count += entry->count; 630 } 631 632 sem->queue.Remove(entry); 633 entry->queued = false; 634 } 635 636 // select notification, if the semaphore is now acquirable 637 if (sem->u.used.count > 0) 638 notify_sem_select_events(sem, B_EVENT_ACQUIRE_SEMAPHORE); 639 } 640 641 642 /*! This function deletes all semaphores belonging to a particular team. 643 */ 644 void 645 sem_delete_owned_sems(Team* team) 646 { 647 while (true) { 648 char* name; 649 650 { 651 // get the next semaphore from the team's sem list 652 InterruptsLocker _; 653 SpinLocker semListLocker(sSemsSpinlock); 654 sem_entry* sem = (sem_entry*)list_remove_head_item(&team->sem_list); 655 if (sem == NULL) 656 break; 657 658 // delete the semaphore 659 SpinLocker semLocker(sem->lock); 660 semListLocker.Unlock(); 661 uninit_sem_locked(*sem, &name, semLocker); 662 } 663 664 free(name); 665 } 666 667 scheduler_reschedule_if_necessary(); 668 } 669 670 671 int32 672 sem_max_sems(void) 673 { 674 return sMaxSems; 675 } 676 677 678 int32 679 sem_used_sems(void) 680 { 681 return sUsedSems; 682 } 683 684 685 // #pragma mark - Public Kernel API 686 687 688 sem_id 689 create_sem(int32 count, const char* name) 690 { 691 return create_sem_etc(count, name, team_get_kernel_team_id()); 692 } 693 694 695 status_t 696 delete_sem(sem_id id) 697 { 698 return delete_sem_internal(id, false); 699 } 700 701 702 status_t 703 acquire_sem(sem_id id) 704 { 705 return switch_sem_etc(-1, id, 1, 0, 0); 706 } 707 708 709 status_t 710 acquire_sem_etc(sem_id id, int32 count, uint32 flags, bigtime_t timeout) 711 { 712 return switch_sem_etc(-1, id, count, flags, timeout); 713 } 714 715 716 status_t 717 switch_sem(sem_id toBeReleased, sem_id toBeAcquired) 718 { 719 return switch_sem_etc(toBeReleased, toBeAcquired, 1, 0, 0); 720 } 721 722 723 status_t 724 switch_sem_etc(sem_id semToBeReleased, sem_id id, int32 count, 725 uint32 flags, bigtime_t timeout) 726 { 727 int slot = id % sMaxSems; 728 729 if (gKernelStartup) 730 return B_OK; 731 if (sSemsActive == false) 732 return B_NO_MORE_SEMS; 733 #if KDEBUG 734 if (!are_interrupts_enabled()) { 735 panic("switch_sem_etc: called with interrupts disabled for sem " 736 "%" B_PRId32 "\n", id); 737 } 738 #endif 739 740 if (id < 0) 741 return B_BAD_SEM_ID; 742 if (count <= 0 743 || (flags & (B_RELATIVE_TIMEOUT | B_ABSOLUTE_TIMEOUT)) == (B_RELATIVE_TIMEOUT | B_ABSOLUTE_TIMEOUT)) { 744 return B_BAD_VALUE; 745 } 746 747 InterruptsLocker _; 748 SpinLocker semLocker(sSems[slot].lock); 749 750 if (sSems[slot].id != id) { 751 TRACE(("switch_sem_etc: bad sem %ld\n", id)); 752 return B_BAD_SEM_ID; 753 } 754 755 // TODO: the B_CHECK_PERMISSION flag should be made private, as it 756 // doesn't have any use outside the kernel 757 if ((flags & B_CHECK_PERMISSION) != 0 758 && sSems[slot].u.used.owner == team_get_kernel_team_id()) { 759 dprintf("thread %" B_PRId32 " tried to acquire kernel semaphore " 760 "%" B_PRId32 ".\n", thread_get_current_thread_id(), id); 761 #if 0 762 _user_debugger("Thread tried to acquire kernel semaphore."); 763 #endif 764 return B_NOT_ALLOWED; 765 } 766 767 if (sSems[slot].u.used.count - count < 0) { 768 if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0) { 769 // immediate timeout 770 return B_WOULD_BLOCK; 771 } else if ((flags & B_ABSOLUTE_TIMEOUT) != 0 && timeout < 0) { 772 // absolute negative timeout 773 return B_TIMED_OUT; 774 } 775 } 776 777 KTRACE("switch_sem_etc(semToBeReleased: %ld, sem: %ld, count: %ld, " 778 "flags: 0x%lx, timeout: %lld)", semToBeReleased, id, count, flags, 779 timeout); 780 781 if ((sSems[slot].u.used.count -= count) < 0) { 782 // we need to block 783 Thread *thread = thread_get_current_thread(); 784 785 TRACE(("switch_sem_etc(id = %ld): block name = %s, thread = %p," 786 " name = %s\n", id, sSems[slot].u.used.name, thread, thread->name)); 787 788 // do a quick check to see if the thread has any pending signals 789 // this should catch most of the cases where the thread had a signal 790 SpinLocker schedulerLocker(thread->scheduler_lock); 791 if (thread_is_interrupted(thread, flags)) { 792 schedulerLocker.Unlock(); 793 sSems[slot].u.used.count += count; 794 if (semToBeReleased >= B_OK) { 795 // we need to still release the semaphore to always 796 // leave in a consistent state 797 release_sem_etc(semToBeReleased, 1, B_DO_NOT_RESCHEDULE); 798 } 799 return B_INTERRUPTED; 800 } 801 802 schedulerLocker.Unlock(); 803 804 if ((flags & (B_RELATIVE_TIMEOUT | B_ABSOLUTE_TIMEOUT)) == 0) 805 timeout = B_INFINITE_TIMEOUT; 806 807 // enqueue in the semaphore queue and get ready to wait 808 queued_thread queueEntry(thread, count); 809 sSems[slot].queue.Add(&queueEntry); 810 queueEntry.queued = true; 811 812 thread_prepare_to_block(thread, flags, THREAD_BLOCK_TYPE_SEMAPHORE, 813 (void*)(addr_t)id); 814 815 semLocker.Unlock(); 816 817 // release the other semaphore, if any 818 if (semToBeReleased >= 0) { 819 release_sem_etc(semToBeReleased, 1, B_DO_NOT_RESCHEDULE); 820 semToBeReleased = -1; 821 } 822 823 status_t acquireStatus = timeout == B_INFINITE_TIMEOUT 824 ? thread_block() : thread_block_with_timeout(flags, timeout); 825 826 semLocker.Lock(); 827 828 // If we're still queued, this means the acquiration failed, and we 829 // need to remove our entry and (potentially) wake up other threads. 830 if (queueEntry.queued) 831 remove_thread_from_sem(&queueEntry, &sSems[slot]); 832 833 if (acquireStatus >= B_OK) { 834 sSems[slot].u.used.last_acquirer = thread_get_current_thread_id(); 835 #if DEBUG_SEM_LAST_ACQUIRER 836 sSems[slot].u.used.last_acquire_count = count; 837 #endif 838 } 839 840 semLocker.Unlock(); 841 842 TRACE(("switch_sem_etc(sem %ld): exit block name %s, " 843 "thread %ld (%s)\n", id, sSems[slot].u.used.name, thread->id, 844 thread->name)); 845 KTRACE("switch_sem_etc() done: 0x%lx", acquireStatus); 846 return acquireStatus; 847 } else { 848 sSems[slot].u.used.net_count -= count; 849 sSems[slot].u.used.last_acquirer = thread_get_current_thread_id(); 850 #if DEBUG_SEM_LAST_ACQUIRER 851 sSems[slot].u.used.last_acquire_count = count; 852 #endif 853 } 854 855 KTRACE("switch_sem_etc() done: 0x%lx", status); 856 857 return B_OK; 858 } 859 860 861 status_t 862 release_sem(sem_id id) 863 { 864 return release_sem_etc(id, 1, 0); 865 } 866 867 868 status_t 869 release_sem_etc(sem_id id, int32 count, uint32 flags) 870 { 871 int32 slot = id % sMaxSems; 872 873 if (gKernelStartup) 874 return B_OK; 875 if (sSemsActive == false) 876 return B_NO_MORE_SEMS; 877 if (id < 0) 878 return B_BAD_SEM_ID; 879 if (count <= 0 && (flags & B_RELEASE_ALL) == 0) 880 return B_BAD_VALUE; 881 #if KDEBUG 882 if ((flags & B_DO_NOT_RESCHEDULE) == 0 && !are_interrupts_enabled()) { 883 panic("release_sem_etc(): called with interrupts disabled and " 884 "rescheduling allowed for sem_id %" B_PRId32, id); 885 } 886 #endif 887 888 InterruptsLocker _; 889 SpinLocker semLocker(sSems[slot].lock); 890 891 if (sSems[slot].id != id) { 892 TRACE(("sem_release_etc: invalid sem_id %ld\n", id)); 893 return B_BAD_SEM_ID; 894 } 895 896 // ToDo: the B_CHECK_PERMISSION flag should be made private, as it 897 // doesn't have any use outside the kernel 898 if ((flags & B_CHECK_PERMISSION) != 0 899 && sSems[slot].u.used.owner == team_get_kernel_team_id()) { 900 dprintf("thread %" B_PRId32 " tried to release kernel semaphore.\n", 901 thread_get_current_thread_id()); 902 return B_NOT_ALLOWED; 903 } 904 905 KTRACE("release_sem_etc(sem: %ld, count: %ld, flags: 0x%lx)", id, count, 906 flags); 907 908 sSems[slot].u.used.last_acquirer = -sSems[slot].u.used.last_acquirer; 909 #if DEBUG_SEM_LAST_ACQUIRER 910 sSems[slot].u.used.last_releaser = thread_get_current_thread_id(); 911 sSems[slot].u.used.last_release_count = count; 912 #endif 913 914 if (flags & B_RELEASE_ALL) { 915 count = sSems[slot].u.used.net_count - sSems[slot].u.used.count; 916 917 // is there anything to do for us at all? 918 if (count == 0) 919 return B_OK; 920 921 // Don't release more than necessary -- there might be interrupted/ 922 // timed out threads in the queue. 923 flags |= B_RELEASE_IF_WAITING_ONLY; 924 } 925 926 // Grab the scheduler lock, so thread_is_blocked() is reliable (due to 927 // possible interruptions or timeouts, it wouldn't be otherwise). 928 while (count > 0) { 929 queued_thread* entry = sSems[slot].queue.Head(); 930 if (entry == NULL) { 931 if ((flags & B_RELEASE_IF_WAITING_ONLY) == 0) { 932 sSems[slot].u.used.count += count; 933 sSems[slot].u.used.net_count += count; 934 } 935 break; 936 } 937 938 SpinLocker schedulerLock(entry->thread->scheduler_lock); 939 if (thread_is_blocked(entry->thread)) { 940 // The thread is still waiting. If its count is satisfied, 941 // unblock it. Otherwise we can't unblock any other thread. 942 if (entry->count > sSems[slot].u.used.net_count + count) { 943 sSems[slot].u.used.count += count; 944 sSems[slot].u.used.net_count += count; 945 break; 946 } 947 948 thread_unblock_locked(entry->thread, B_OK); 949 950 int delta = min_c(count, entry->count); 951 sSems[slot].u.used.count += delta; 952 sSems[slot].u.used.net_count += delta - entry->count; 953 count -= delta; 954 } else { 955 // The thread is no longer waiting, but still queued, which 956 // means acquiration failed and we can just remove it. 957 sSems[slot].u.used.count += entry->count; 958 } 959 960 sSems[slot].queue.Remove(entry); 961 entry->queued = false; 962 } 963 964 if (sSems[slot].u.used.count > 0) 965 notify_sem_select_events(&sSems[slot], B_EVENT_ACQUIRE_SEMAPHORE); 966 967 // If we've unblocked another thread reschedule, if we've not explicitly 968 // been told not to. 969 if ((flags & B_DO_NOT_RESCHEDULE) == 0) { 970 semLocker.Unlock(); 971 972 SpinLocker _(thread_get_current_thread()->scheduler_lock); 973 scheduler_reschedule_if_necessary_locked(); 974 } 975 976 return B_OK; 977 } 978 979 980 status_t 981 get_sem_count(sem_id id, int32 *_count) 982 { 983 if (sSemsActive == false) 984 return B_NO_MORE_SEMS; 985 if (id < 0) 986 return B_BAD_SEM_ID; 987 if (_count == NULL) 988 return B_BAD_VALUE; 989 990 int slot = id % sMaxSems; 991 992 InterruptsSpinLocker _(sSems[slot].lock); 993 994 if (sSems[slot].id != id) { 995 TRACE(("sem_get_count: invalid sem_id %ld\n", id)); 996 return B_BAD_SEM_ID; 997 } 998 999 *_count = sSems[slot].u.used.count; 1000 1001 return B_OK; 1002 } 1003 1004 1005 /*! Called by the get_sem_info() macro. */ 1006 status_t 1007 _get_sem_info(sem_id id, struct sem_info *info, size_t size) 1008 { 1009 if (!sSemsActive) 1010 return B_NO_MORE_SEMS; 1011 if (id < 0) 1012 return B_BAD_SEM_ID; 1013 if (info == NULL || size != sizeof(sem_info)) 1014 return B_BAD_VALUE; 1015 1016 int slot = id % sMaxSems; 1017 1018 InterruptsSpinLocker _(sSems[slot].lock); 1019 1020 if (sSems[slot].id != id) { 1021 TRACE(("get_sem_info: invalid sem_id %ld\n", id)); 1022 return B_BAD_SEM_ID; 1023 } else 1024 fill_sem_info(&sSems[slot], info, size); 1025 1026 return B_OK; 1027 } 1028 1029 1030 /*! Called by the get_next_sem_info() macro. */ 1031 status_t 1032 _get_next_sem_info(team_id teamID, int32 *_cookie, struct sem_info *info, 1033 size_t size) 1034 { 1035 if (!sSemsActive) 1036 return B_NO_MORE_SEMS; 1037 if (_cookie == NULL || info == NULL || size != sizeof(sem_info)) 1038 return B_BAD_VALUE; 1039 if (teamID < 0) 1040 return B_BAD_TEAM_ID; 1041 1042 Team* team = Team::Get(teamID); 1043 if (team == NULL) 1044 return B_BAD_TEAM_ID; 1045 BReference<Team> teamReference(team, true); 1046 1047 InterruptsSpinLocker semListLocker(sSemsSpinlock); 1048 1049 // TODO: find a way to iterate the list that is more reliable 1050 sem_entry* sem = (sem_entry*)list_get_first_item(&team->sem_list); 1051 int32 newIndex = *_cookie; 1052 int32 index = 0; 1053 bool found = false; 1054 1055 while (!found) { 1056 // find the next entry to be returned 1057 while (sem != NULL && index < newIndex) { 1058 sem = (sem_entry*)list_get_next_item(&team->sem_list, sem); 1059 index++; 1060 } 1061 1062 if (sem == NULL) 1063 return B_BAD_VALUE; 1064 1065 SpinLocker _(sem->lock); 1066 1067 if (sem->id != -1 && sem->u.used.owner == team->id) { 1068 // found one! 1069 fill_sem_info(sem, info, size); 1070 newIndex = index + 1; 1071 found = true; 1072 } else 1073 newIndex++; 1074 } 1075 1076 if (!found) 1077 return B_BAD_VALUE; 1078 1079 *_cookie = newIndex; 1080 return B_OK; 1081 } 1082 1083 1084 status_t 1085 set_sem_owner(sem_id id, team_id newTeamID) 1086 { 1087 if (sSemsActive == false) 1088 return B_NO_MORE_SEMS; 1089 if (id < 0) 1090 return B_BAD_SEM_ID; 1091 if (newTeamID < 0) 1092 return B_BAD_TEAM_ID; 1093 1094 int32 slot = id % sMaxSems; 1095 1096 // get the new team 1097 Team* newTeam = Team::Get(newTeamID); 1098 if (newTeam == NULL) 1099 return B_BAD_TEAM_ID; 1100 BReference<Team> newTeamReference(newTeam, true); 1101 1102 InterruptsSpinLocker semListLocker(sSemsSpinlock); 1103 SpinLocker semLocker(sSems[slot].lock); 1104 1105 if (sSems[slot].id != id) { 1106 TRACE(("set_sem_owner: invalid sem_id %ld\n", id)); 1107 return B_BAD_SEM_ID; 1108 } 1109 1110 list_remove_link(&sSems[slot].u.used.team_link); 1111 list_add_item(&newTeam->sem_list, &sSems[slot].u.used.team_link); 1112 1113 sSems[slot].u.used.owner = newTeam->id; 1114 return B_OK; 1115 } 1116 1117 1118 /*! Returns the name of the semaphore. The name is not copied, so the caller 1119 must make sure that the semaphore remains alive as long as the name is used. 1120 */ 1121 const char* 1122 sem_get_name_unsafe(sem_id id) 1123 { 1124 int slot = id % sMaxSems; 1125 1126 if (sSemsActive == false || id < 0 || sSems[slot].id != id) 1127 return NULL; 1128 1129 return sSems[slot].u.used.name; 1130 } 1131 1132 1133 // #pragma mark - Syscalls 1134 1135 1136 sem_id 1137 _user_create_sem(int32 count, const char *userName) 1138 { 1139 char name[B_OS_NAME_LENGTH]; 1140 1141 if (userName == NULL) 1142 return create_sem_etc(count, NULL, team_get_current_team_id()); 1143 1144 if (!IS_USER_ADDRESS(userName) 1145 || user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK) 1146 return B_BAD_ADDRESS; 1147 1148 return create_sem_etc(count, name, team_get_current_team_id()); 1149 } 1150 1151 1152 status_t 1153 _user_delete_sem(sem_id id) 1154 { 1155 return delete_sem_internal(id, true); 1156 } 1157 1158 1159 status_t 1160 _user_acquire_sem(sem_id id) 1161 { 1162 status_t error = switch_sem_etc(-1, id, 1, 1163 B_CAN_INTERRUPT | B_CHECK_PERMISSION, 0); 1164 1165 return syscall_restart_handle_post(error); 1166 } 1167 1168 1169 status_t 1170 _user_acquire_sem_etc(sem_id id, int32 count, uint32 flags, bigtime_t timeout) 1171 { 1172 syscall_restart_handle_timeout_pre(flags, timeout); 1173 1174 status_t error = switch_sem_etc(-1, id, count, 1175 flags | B_CAN_INTERRUPT | B_CHECK_PERMISSION, timeout); 1176 1177 return syscall_restart_handle_timeout_post(error, timeout); 1178 } 1179 1180 1181 status_t 1182 _user_switch_sem(sem_id releaseSem, sem_id id) 1183 { 1184 status_t error = switch_sem_etc(releaseSem, id, 1, 1185 B_CAN_INTERRUPT | B_CHECK_PERMISSION, 0); 1186 1187 if (releaseSem < 0) 1188 return syscall_restart_handle_post(error); 1189 1190 return error; 1191 } 1192 1193 1194 status_t 1195 _user_switch_sem_etc(sem_id releaseSem, sem_id id, int32 count, uint32 flags, 1196 bigtime_t timeout) 1197 { 1198 if (releaseSem < 0) 1199 syscall_restart_handle_timeout_pre(flags, timeout); 1200 1201 status_t error = switch_sem_etc(releaseSem, id, count, 1202 flags | B_CAN_INTERRUPT | B_CHECK_PERMISSION, timeout); 1203 1204 if (releaseSem < 0) 1205 return syscall_restart_handle_timeout_post(error, timeout); 1206 1207 return error; 1208 } 1209 1210 1211 status_t 1212 _user_release_sem(sem_id id) 1213 { 1214 return release_sem_etc(id, 1, B_CHECK_PERMISSION); 1215 } 1216 1217 1218 status_t 1219 _user_release_sem_etc(sem_id id, int32 count, uint32 flags) 1220 { 1221 return release_sem_etc(id, count, flags | B_CHECK_PERMISSION); 1222 } 1223 1224 1225 status_t 1226 _user_get_sem_count(sem_id id, int32 *userCount) 1227 { 1228 status_t status; 1229 int32 count; 1230 1231 if (userCount == NULL || !IS_USER_ADDRESS(userCount)) 1232 return B_BAD_ADDRESS; 1233 1234 status = get_sem_count(id, &count); 1235 if (status == B_OK && user_memcpy(userCount, &count, sizeof(int32)) < B_OK) 1236 return B_BAD_ADDRESS; 1237 1238 return status; 1239 } 1240 1241 1242 status_t 1243 _user_get_sem_info(sem_id id, struct sem_info *userInfo, size_t size) 1244 { 1245 struct sem_info info; 1246 status_t status; 1247 1248 if (userInfo == NULL || !IS_USER_ADDRESS(userInfo)) 1249 return B_BAD_ADDRESS; 1250 1251 status = _get_sem_info(id, &info, size); 1252 if (status == B_OK && user_memcpy(userInfo, &info, size) < B_OK) 1253 return B_BAD_ADDRESS; 1254 1255 return status; 1256 } 1257 1258 1259 status_t 1260 _user_get_next_sem_info(team_id team, int32 *userCookie, struct sem_info *userInfo, 1261 size_t size) 1262 { 1263 struct sem_info info; 1264 int32 cookie; 1265 status_t status; 1266 1267 if (userCookie == NULL || userInfo == NULL 1268 || !IS_USER_ADDRESS(userCookie) || !IS_USER_ADDRESS(userInfo) 1269 || user_memcpy(&cookie, userCookie, sizeof(int32)) < B_OK) 1270 return B_BAD_ADDRESS; 1271 1272 status = _get_next_sem_info(team, &cookie, &info, size); 1273 1274 if (status == B_OK) { 1275 if (user_memcpy(userInfo, &info, size) < B_OK 1276 || user_memcpy(userCookie, &cookie, sizeof(int32)) < B_OK) 1277 return B_BAD_ADDRESS; 1278 } 1279 1280 return status; 1281 } 1282 1283 1284 status_t 1285 _user_set_sem_owner(sem_id id, team_id team) 1286 { 1287 return set_sem_owner(id, team); 1288 } 1289