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