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