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