1 /* 2 * Copyright 2002-2009, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 * 5 * Copyright 2001, Mark-Jan Bastian. All rights reserved. 6 * Distributed under the terms of the NewOS License. 7 */ 8 9 /*! Ports for IPC */ 10 11 #include <port.h> 12 13 #include <ctype.h> 14 #include <iovec.h> 15 #include <stdlib.h> 16 #include <string.h> 17 18 #include <OS.h> 19 20 #include <arch/int.h> 21 #include <cbuf.h> 22 #include <kernel.h> 23 #include <Notifications.h> 24 #include <sem.h> 25 #include <syscall_restart.h> 26 #include <team.h> 27 #include <util/AutoLock.h> 28 #include <util/list.h> 29 #include <wait_for_objects.h> 30 31 32 //#define TRACE_PORTS 33 #ifdef TRACE_PORTS 34 # define TRACE(x) dprintf x 35 #else 36 # define TRACE(x) 37 #endif 38 39 40 typedef struct port_msg { 41 list_link link; 42 int32 code; 43 cbuf *buffer_chain; 44 size_t size; 45 uid_t sender; 46 gid_t sender_group; 47 team_id sender_team; 48 } port_msg; 49 50 struct port_entry { 51 port_id id; 52 team_id owner; 53 int32 capacity; 54 spinlock lock; 55 const char *name; 56 sem_id read_sem; 57 sem_id write_sem; 58 int32 total_count; // messages read from port since creation 59 select_info *select_infos; 60 struct list msg_queue; 61 }; 62 63 class PortNotificationService : public DefaultNotificationService { 64 public: 65 PortNotificationService(); 66 67 void Notify(uint32 opcode, port_id team); 68 }; 69 70 #define MAX_QUEUE_LENGTH 4096 71 #define PORT_MAX_MESSAGE_SIZE (256 * 1024) 72 73 // sMaxPorts must be power of 2 74 static int32 sMaxPorts = 4096; 75 static int32 sUsedPorts = 0; 76 77 static struct port_entry *sPorts = NULL; 78 static area_id sPortArea = 0; 79 static bool sPortsActive = false; 80 static port_id sNextPort = 1; 81 static int32 sFirstFreeSlot = 1; 82 83 static PortNotificationService sNotificationService; 84 85 static spinlock sPortSpinlock = B_SPINLOCK_INITIALIZER; 86 87 #define GRAB_PORT_LIST_LOCK() acquire_spinlock(&sPortSpinlock) 88 #define RELEASE_PORT_LIST_LOCK() release_spinlock(&sPortSpinlock) 89 #define GRAB_PORT_LOCK(s) acquire_spinlock(&(s).lock) 90 #define RELEASE_PORT_LOCK(s) release_spinlock(&(s).lock) 91 92 93 // #pragma mark - TeamNotificationService 94 95 96 PortNotificationService::PortNotificationService() 97 : DefaultNotificationService("ports") 98 { 99 } 100 101 102 void 103 PortNotificationService::Notify(uint32 opcode, port_id port) 104 { 105 char eventBuffer[64]; 106 KMessage event; 107 event.SetTo(eventBuffer, sizeof(eventBuffer), PORT_MONITOR); 108 event.AddInt32("event", opcode); 109 event.AddInt32("port", port); 110 111 DefaultNotificationService::Notify(event, opcode); 112 } 113 114 115 // #pragma mark - 116 117 118 static int 119 dump_port_list(int argc, char **argv) 120 { 121 const char *name = NULL; 122 team_id owner = -1; 123 int32 i; 124 125 if (argc > 2) { 126 if (!strcmp(argv[1], "team") || !strcmp(argv[1], "owner")) 127 owner = strtoul(argv[2], NULL, 0); 128 else if (!strcmp(argv[1], "name")) 129 name = argv[2]; 130 } else if (argc > 1) 131 owner = strtoul(argv[1], NULL, 0); 132 133 kprintf("port id cap r-sem r-cnt w-sem w-cnt total team name\n"); 134 135 for (i = 0; i < sMaxPorts; i++) { 136 struct port_entry *port = &sPorts[i]; 137 if (port->id < 0 138 || (owner != -1 && port->owner != owner) 139 || (name != NULL && strstr(port->name, name) == NULL)) 140 continue; 141 142 int32 readCount, writeCount; 143 get_sem_count(port->read_sem, &readCount); 144 get_sem_count(port->write_sem, &writeCount); 145 kprintf("%p %8ld %4ld %6ld %6ld %6ld %6ld %8ld %6ld %s\n", port, 146 port->id, port->capacity, port->read_sem, readCount, 147 port->write_sem, writeCount, port->total_count, port->owner, 148 port->name); 149 } 150 return 0; 151 } 152 153 154 static void 155 _dump_port_info(struct port_entry *port) 156 { 157 int32 count; 158 159 kprintf("PORT: %p\n", port); 160 kprintf(" id: %ld\n", port->id); 161 kprintf(" name: \"%s\"\n", port->name); 162 kprintf(" owner: %ld\n", port->owner); 163 kprintf(" capacity: %ld\n", port->capacity); 164 kprintf(" read_sem: %ld\n", port->read_sem); 165 kprintf(" write_sem: %ld\n", port->write_sem); 166 get_sem_count(port->read_sem, &count); 167 kprintf(" read_sem count: %ld\n", count); 168 get_sem_count(port->write_sem, &count); 169 kprintf(" write_sem count: %ld\n", count); 170 kprintf(" total count: %ld\n", port->total_count); 171 172 set_debug_variable("_port", (addr_t)port); 173 set_debug_variable("_portID", port->id); 174 set_debug_variable("_owner", port->owner); 175 set_debug_variable("_readSem", port->read_sem); 176 set_debug_variable("_writeSem", port->write_sem); 177 } 178 179 180 static int 181 dump_port_info(int argc, char **argv) 182 { 183 const char *name = NULL; 184 sem_id sem = -1; 185 int i; 186 187 if (argc < 2) { 188 print_debugger_command_usage(argv[0]); 189 return 0; 190 } 191 192 if (argc > 2) { 193 if (!strcmp(argv[1], "address")) { 194 _dump_port_info((struct port_entry *)strtoul(argv[2], NULL, 0)); 195 return 0; 196 } else if (!strcmp(argv[1], "sem")) 197 sem = strtoul(argv[2], NULL, 0); 198 else if (!strcmp(argv[1], "name")) 199 name = argv[2]; 200 } else if (isdigit(argv[1][0])) { 201 // if the argument looks like a number, treat it as such 202 uint32 num = strtoul(argv[1], NULL, 0); 203 uint32 slot = num % sMaxPorts; 204 if (sPorts[slot].id != (int)num) { 205 kprintf("port %ld (%#lx) doesn't exist!\n", num, num); 206 return 0; 207 } 208 _dump_port_info(&sPorts[slot]); 209 return 0; 210 } else 211 name = argv[1]; 212 213 // walk through the ports list, trying to match name 214 for (i = 0; i < sMaxPorts; i++) { 215 if ((name != NULL && sPorts[i].name != NULL 216 && !strcmp(name, sPorts[i].name)) 217 || (sem != -1 && (sPorts[i].read_sem == sem 218 || sPorts[i].write_sem == sem))) { 219 _dump_port_info(&sPorts[i]); 220 return 0; 221 } 222 } 223 224 return 0; 225 } 226 227 228 static void 229 notify_port_select_events(int slot, uint16 events) 230 { 231 if (sPorts[slot].select_infos) 232 notify_select_events_list(sPorts[slot].select_infos, events); 233 } 234 235 236 static void 237 put_port_msg(port_msg *msg) 238 { 239 cbuf_free_chain(msg->buffer_chain); 240 free(msg); 241 } 242 243 244 static port_msg * 245 get_port_msg(int32 code, size_t bufferSize) 246 { 247 // ToDo: investigate preallocation of port_msgs (or use a slab allocator) 248 cbuf *bufferChain = NULL; 249 250 port_msg *msg = (port_msg *)malloc(sizeof(port_msg)); 251 if (msg == NULL) 252 return NULL; 253 254 if (bufferSize > 0) { 255 bufferChain = cbuf_get_chain(bufferSize); 256 if (bufferChain == NULL) { 257 free(msg); 258 return NULL; 259 } 260 } 261 262 msg->code = code; 263 msg->buffer_chain = bufferChain; 264 msg->size = bufferSize; 265 return msg; 266 } 267 268 269 /*! You need to own the port's lock when calling this function */ 270 static bool 271 is_port_closed(int32 slot) 272 { 273 return sPorts[slot].capacity == 0; 274 } 275 276 277 /*! Fills the port_info structure with information from the specified 278 port. 279 The port lock must be held when called. 280 */ 281 static void 282 fill_port_info(struct port_entry *port, port_info *info, size_t size) 283 { 284 int32 count; 285 286 info->port = port->id; 287 info->team = port->owner; 288 info->capacity = port->capacity; 289 290 get_sem_count(port->read_sem, &count); 291 if (count < 0) 292 count = 0; 293 294 info->queue_count = count; 295 info->total_count = port->total_count; 296 297 strlcpy(info->name, port->name, B_OS_NAME_LENGTH); 298 } 299 300 301 // #pragma mark - private kernel API 302 303 304 /*! This function cycles through the ports table, deleting all 305 the ports that are owned by the passed team_id 306 */ 307 int 308 delete_owned_ports(team_id owner) 309 { 310 // ToDo: investigate maintaining a list of ports in the team 311 // to make this simpler and more efficient. 312 cpu_status state; 313 int i; 314 int count = 0; 315 316 TRACE(("delete_owned_ports(owner = %ld)\n", owner)); 317 318 if (!sPortsActive) 319 return B_BAD_PORT_ID; 320 321 state = disable_interrupts(); 322 GRAB_PORT_LIST_LOCK(); 323 324 for (i = 0; i < sMaxPorts; i++) { 325 if (sPorts[i].id != -1 && sPorts[i].owner == owner) { 326 port_id id = sPorts[i].id; 327 328 RELEASE_PORT_LIST_LOCK(); 329 restore_interrupts(state); 330 331 delete_port(id); 332 count++; 333 334 state = disable_interrupts(); 335 GRAB_PORT_LIST_LOCK(); 336 } 337 } 338 339 RELEASE_PORT_LIST_LOCK(); 340 restore_interrupts(state); 341 342 return count; 343 } 344 345 346 int32 347 port_max_ports(void) 348 { 349 return sMaxPorts; 350 } 351 352 353 int32 354 port_used_ports(void) 355 { 356 return sUsedPorts; 357 } 358 359 360 status_t 361 port_init(kernel_args *args) 362 { 363 size_t size = sizeof(struct port_entry) * sMaxPorts; 364 int32 i; 365 366 // create and initialize ports table 367 sPortArea = create_area("port_table", (void **)&sPorts, B_ANY_KERNEL_ADDRESS, 368 size, B_FULL_LOCK, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); 369 if (sPortArea < 0) { 370 panic("unable to allocate kernel port table!\n"); 371 return sPortArea; 372 } 373 374 // ToDo: investigate preallocating a list of port_msgs to 375 // speed up actual message sending/receiving, a slab allocator 376 // might do it as well, though :-) 377 378 memset(sPorts, 0, size); 379 for (i = 0; i < sMaxPorts; i++) 380 sPorts[i].id = -1; 381 382 // add debugger commands 383 add_debugger_command_etc("ports", &dump_port_list, 384 "Dump a list of all active ports (for team, with name, etc.)", 385 "[ ([ \"team\" | \"owner\" ] <team>) | (\"name\" <name>) ]\n" 386 "Prints a list of all active ports meeting the given\n" 387 "requirement. If no argument is given, all ports are listed.\n" 388 " <team> - The team owning the ports.\n" 389 " <name> - Part of the name of the ports.\n", 0); 390 add_debugger_command_etc("port", &dump_port_info, 391 "Dump info about a particular port", 392 "([ \"address\" ] <address>) | ([ \"name\" ] <name>) " 393 "| (\"sem\" <sem>)\n" 394 "Prints info about the specified port.\n" 395 " <address> - Pointer to the port structure.\n" 396 " <name> - Name of the port.\n" 397 " <sem> - ID of the port's read or write semaphore.\n", 0); 398 399 new(&sNotificationService) PortNotificationService(); 400 sPortsActive = true; 401 return B_OK; 402 } 403 404 405 // #pragma mark - public kernel API 406 407 408 port_id 409 create_port(int32 queueLength, const char *name) 410 { 411 cpu_status state; 412 char nameBuffer[B_OS_NAME_LENGTH]; 413 sem_id readSem, writeSem; 414 status_t status; 415 team_id owner; 416 int32 slot; 417 418 TRACE(("create_port(queueLength = %ld, name = \"%s\")\n", queueLength, 419 name)); 420 421 if (!sPortsActive) 422 return B_BAD_PORT_ID; 423 424 // check queue length 425 if (queueLength < 1 426 || queueLength > MAX_QUEUE_LENGTH) 427 return B_BAD_VALUE; 428 429 // check early on if there are any free port slots to use 430 if (atomic_add(&sUsedPorts, 1) >= sMaxPorts) { 431 status = B_NO_MORE_PORTS; 432 goto err1; 433 } 434 435 // check & dup name 436 if (name == NULL) 437 name = "unnamed port"; 438 439 // ToDo: we could save the memory and use the semaphore name only instead 440 strlcpy(nameBuffer, name, B_OS_NAME_LENGTH); 441 name = strdup(nameBuffer); 442 if (name == NULL) { 443 status = B_NO_MEMORY; 444 goto err1; 445 } 446 447 // create read sem with owner set to -1 448 // ToDo: should be B_SYSTEM_TEAM 449 readSem = create_sem_etc(0, name, -1); 450 if (readSem < B_OK) { 451 status = readSem; 452 goto err2; 453 } 454 455 // create write sem 456 writeSem = create_sem_etc(queueLength, name, -1); 457 if (writeSem < B_OK) { 458 status = writeSem; 459 goto err3; 460 } 461 462 owner = team_get_current_team_id(); 463 464 state = disable_interrupts(); 465 GRAB_PORT_LIST_LOCK(); 466 467 // find the first empty spot 468 for (slot = 0; slot < sMaxPorts; slot++) { 469 int32 i = (slot + sFirstFreeSlot) % sMaxPorts; 470 471 if (sPorts[i].id == -1) { 472 port_id id; 473 474 // make the port_id be a multiple of the slot it's in 475 if (i >= sNextPort % sMaxPorts) 476 sNextPort += i - sNextPort % sMaxPorts; 477 else 478 sNextPort += sMaxPorts - (sNextPort % sMaxPorts - i); 479 sFirstFreeSlot = slot + 1; 480 481 GRAB_PORT_LOCK(sPorts[i]); 482 sPorts[i].id = sNextPort++; 483 RELEASE_PORT_LIST_LOCK(); 484 485 sPorts[i].capacity = queueLength; 486 sPorts[i].owner = owner; 487 sPorts[i].name = name; 488 489 sPorts[i].read_sem = readSem; 490 sPorts[i].write_sem = writeSem; 491 492 list_init(&sPorts[i].msg_queue); 493 sPorts[i].total_count = 0; 494 sPorts[i].select_infos = NULL; 495 id = sPorts[i].id; 496 497 RELEASE_PORT_LOCK(sPorts[i]); 498 restore_interrupts(state); 499 500 TRACE(("create_port() done: port created %ld\n", id)); 501 502 sNotificationService.Notify(PORT_ADDED, id); 503 return id; 504 } 505 } 506 507 // not enough ports... 508 509 // TODO: due to sUsedPorts, this cannot happen anymore - as 510 // long as sMaxPorts stays constant over the kernel run 511 // time (which it should be). IOW we could simply panic() 512 // here. 513 514 RELEASE_PORT_LIST_LOCK(); 515 restore_interrupts(state); 516 517 status = B_NO_MORE_PORTS; 518 519 delete_sem(writeSem); 520 err3: 521 delete_sem(readSem); 522 err2: 523 free((char *)name); 524 err1: 525 atomic_add(&sUsedPorts, -1); 526 527 return status; 528 } 529 530 531 status_t 532 close_port(port_id id) 533 { 534 sem_id readSem, writeSem; 535 cpu_status state; 536 int32 slot; 537 538 TRACE(("close_port(id = %ld)\n", id)); 539 540 if (!sPortsActive || id < 0) 541 return B_BAD_PORT_ID; 542 543 slot = id % sMaxPorts; 544 545 // walk through the sem list, trying to match name 546 state = disable_interrupts(); 547 GRAB_PORT_LOCK(sPorts[slot]); 548 549 if (sPorts[slot].id != id) { 550 RELEASE_PORT_LOCK(sPorts[slot]); 551 restore_interrupts(state); 552 TRACE(("close_port: invalid port_id %ld\n", id)); 553 return B_BAD_PORT_ID; 554 } 555 556 // mark port to disable writing - deleting the semaphores will 557 // wake up waiting read/writes 558 sPorts[slot].capacity = 0; 559 readSem = sPorts[slot].read_sem; 560 writeSem = sPorts[slot].write_sem; 561 562 notify_port_select_events(slot, B_EVENT_INVALID); 563 sPorts[slot].select_infos = NULL; 564 565 RELEASE_PORT_LOCK(sPorts[slot]); 566 restore_interrupts(state); 567 568 delete_sem(readSem); 569 delete_sem(writeSem); 570 571 return B_NO_ERROR; 572 } 573 574 575 status_t 576 delete_port(port_id id) 577 { 578 cpu_status state; 579 sem_id readSem, writeSem; 580 const char *name; 581 struct list list; 582 port_msg *msg; 583 int32 slot; 584 585 TRACE(("delete_port(id = %ld)\n", id)); 586 587 if (!sPortsActive || id < 0) 588 return B_BAD_PORT_ID; 589 590 slot = id % sMaxPorts; 591 592 state = disable_interrupts(); 593 GRAB_PORT_LOCK(sPorts[slot]); 594 595 if (sPorts[slot].id != id) { 596 RELEASE_PORT_LOCK(sPorts[slot]); 597 restore_interrupts(state); 598 599 TRACE(("delete_port: invalid port_id %ld\n", id)); 600 return B_BAD_PORT_ID; 601 } 602 603 /* mark port as invalid */ 604 sPorts[slot].id = -1; 605 name = sPorts[slot].name; 606 readSem = sPorts[slot].read_sem; 607 writeSem = sPorts[slot].write_sem; 608 sPorts[slot].name = NULL; 609 list_move_to_list(&sPorts[slot].msg_queue, &list); 610 611 notify_port_select_events(slot, B_EVENT_INVALID); 612 sPorts[slot].select_infos = NULL; 613 614 RELEASE_PORT_LOCK(sPorts[slot]); 615 616 // update the first free slot hint in the array 617 GRAB_PORT_LIST_LOCK(); 618 if (slot < sFirstFreeSlot) 619 sFirstFreeSlot = slot; 620 RELEASE_PORT_LIST_LOCK(); 621 622 restore_interrupts(state); 623 624 atomic_add(&sUsedPorts, -1); 625 626 // free the queue 627 while ((msg = (port_msg *)list_remove_head_item(&list)) != NULL) { 628 put_port_msg(msg); 629 } 630 631 free((char *)name); 632 633 // release the threads that were blocking on this port by deleting the sem 634 // read_port() will see the B_BAD_SEM_ID acq_sem() return value, and act accordingly 635 delete_sem(readSem); 636 delete_sem(writeSem); 637 sNotificationService.Notify(PORT_REMOVED, id); 638 639 return B_OK; 640 } 641 642 643 status_t 644 select_port(int32 id, struct select_info *info, bool kernel) 645 { 646 cpu_status state; 647 int32 slot; 648 status_t error = B_OK; 649 650 if (id < 0) 651 return B_BAD_PORT_ID; 652 653 slot = id % sMaxPorts; 654 655 state = disable_interrupts(); 656 GRAB_PORT_LOCK(sPorts[slot]); 657 658 if (sPorts[slot].id != id || is_port_closed(slot)) { 659 // bad port ID 660 error = B_BAD_SEM_ID; 661 } else if (!kernel && sPorts[slot].owner == team_get_kernel_team_id()) { 662 // kernel port, but call from userland 663 error = B_NOT_ALLOWED; 664 } else { 665 info->selected_events &= B_EVENT_READ | B_EVENT_WRITE | B_EVENT_INVALID; 666 667 if (info->selected_events != 0) { 668 uint16 events = 0; 669 int32 writeCount = 0; 670 671 info->next = sPorts[slot].select_infos; 672 sPorts[slot].select_infos = info; 673 674 // check for events 675 if ((info->selected_events & B_EVENT_READ) != 0 676 && !list_is_empty(&sPorts[slot].msg_queue)) { 677 events |= B_EVENT_READ; 678 } 679 680 if (get_sem_count(sPorts[slot].write_sem, &writeCount) == B_OK 681 && writeCount > 0) { 682 events |= B_EVENT_WRITE; 683 } 684 685 if (events != 0) 686 notify_select_events(info, events); 687 } 688 } 689 690 RELEASE_PORT_LOCK(sPorts[slot]); 691 restore_interrupts(state); 692 693 return error; 694 } 695 696 697 status_t 698 deselect_port(int32 id, struct select_info *info, bool kernel) 699 { 700 cpu_status state; 701 int32 slot; 702 703 if (id < 0) 704 return B_BAD_PORT_ID; 705 706 if (info->selected_events == 0) 707 return B_OK; 708 709 slot = id % sMaxPorts; 710 711 state = disable_interrupts(); 712 GRAB_PORT_LOCK(sPorts[slot]); 713 714 if (sPorts[slot].id == id) { 715 select_info** infoLocation = &sPorts[slot].select_infos; 716 while (*infoLocation != NULL && *infoLocation != info) 717 infoLocation = &(*infoLocation)->next; 718 719 if (*infoLocation == info) 720 *infoLocation = info->next; 721 } 722 723 RELEASE_PORT_LOCK(sPorts[slot]); 724 restore_interrupts(state); 725 726 return B_OK; 727 } 728 729 730 port_id 731 find_port(const char *name) 732 { 733 port_id portFound = B_NAME_NOT_FOUND; 734 cpu_status state; 735 int32 i; 736 737 TRACE(("find_port(name = \"%s\")\n", name)); 738 739 if (!sPortsActive) 740 return B_NAME_NOT_FOUND; 741 if (name == NULL) 742 return B_BAD_VALUE; 743 744 // Since we have to check every single port, and we don't 745 // care if it goes away at any point, we're only grabbing 746 // the port lock in question, not the port list lock 747 748 // loop over list 749 for (i = 0; i < sMaxPorts && portFound < B_OK; i++) { 750 // lock every individual port before comparing 751 state = disable_interrupts(); 752 GRAB_PORT_LOCK(sPorts[i]); 753 754 if (sPorts[i].id >= 0 && !strcmp(name, sPorts[i].name)) 755 portFound = sPorts[i].id; 756 757 RELEASE_PORT_LOCK(sPorts[i]); 758 restore_interrupts(state); 759 } 760 761 return portFound; 762 } 763 764 765 status_t 766 _get_port_info(port_id id, port_info *info, size_t size) 767 { 768 cpu_status state; 769 int slot; 770 771 TRACE(("get_port_info(id = %ld)\n", id)); 772 773 if (info == NULL || size != sizeof(port_info)) 774 return B_BAD_VALUE; 775 if (!sPortsActive || id < 0) 776 return B_BAD_PORT_ID; 777 778 slot = id % sMaxPorts; 779 780 state = disable_interrupts(); 781 GRAB_PORT_LOCK(sPorts[slot]); 782 783 if (sPorts[slot].id != id || sPorts[slot].capacity == 0) { 784 RELEASE_PORT_LOCK(sPorts[slot]); 785 restore_interrupts(state); 786 TRACE(("get_port_info: invalid port_id %ld\n", id)); 787 return B_BAD_PORT_ID; 788 } 789 790 // fill a port_info struct with info 791 fill_port_info(&sPorts[slot], info, size); 792 793 RELEASE_PORT_LOCK(sPorts[slot]); 794 restore_interrupts(state); 795 796 return B_OK; 797 } 798 799 800 status_t 801 _get_next_port_info(team_id team, int32 *_cookie, struct port_info *info, size_t size) 802 { 803 cpu_status state; 804 int slot; 805 806 TRACE(("get_next_port_info(team = %ld)\n", team)); 807 808 if (info == NULL || size != sizeof(port_info) || _cookie == NULL || team < B_OK) 809 return B_BAD_VALUE; 810 if (!sPortsActive) 811 return B_BAD_PORT_ID; 812 813 slot = *_cookie; 814 if (slot >= sMaxPorts) 815 return B_BAD_PORT_ID; 816 817 if (team == B_CURRENT_TEAM) 818 team = team_get_current_team_id(); 819 820 info->port = -1; // used as found flag 821 822 // spinlock 823 state = disable_interrupts(); 824 GRAB_PORT_LIST_LOCK(); 825 826 while (slot < sMaxPorts) { 827 GRAB_PORT_LOCK(sPorts[slot]); 828 if (sPorts[slot].id != -1 && sPorts[slot].capacity != 0 && sPorts[slot].owner == team) { 829 // found one! 830 fill_port_info(&sPorts[slot], info, size); 831 832 RELEASE_PORT_LOCK(sPorts[slot]); 833 slot++; 834 break; 835 } 836 RELEASE_PORT_LOCK(sPorts[slot]); 837 slot++; 838 } 839 RELEASE_PORT_LIST_LOCK(); 840 restore_interrupts(state); 841 842 if (info->port == -1) 843 return B_BAD_PORT_ID; 844 845 *_cookie = slot; 846 return B_NO_ERROR; 847 } 848 849 850 ssize_t 851 port_buffer_size(port_id id) 852 { 853 return port_buffer_size_etc(id, 0, 0); 854 } 855 856 857 ssize_t 858 port_buffer_size_etc(port_id id, uint32 flags, bigtime_t timeout) 859 { 860 port_message_info info; 861 status_t error = get_port_message_info_etc(id, &info, flags, timeout); 862 return error != B_OK ? error : info.size; 863 } 864 865 status_t 866 _get_port_message_info_etc(port_id id, port_message_info *info, 867 size_t infoSize, uint32 flags, bigtime_t timeout) 868 { 869 if (info == NULL || infoSize != sizeof(port_message_info)) 870 return B_BAD_VALUE; 871 872 cpu_status state; 873 sem_id cachedSem; 874 status_t status; 875 port_msg *msg; 876 int32 slot; 877 878 if (!sPortsActive || id < 0) 879 return B_BAD_PORT_ID; 880 881 slot = id % sMaxPorts; 882 883 state = disable_interrupts(); 884 GRAB_PORT_LOCK(sPorts[slot]); 885 886 if (sPorts[slot].id != id 887 || (is_port_closed(slot) && list_is_empty(&sPorts[slot].msg_queue))) { 888 RELEASE_PORT_LOCK(sPorts[slot]); 889 restore_interrupts(state); 890 TRACE(("port_buffer_size_etc(): %s port %ld\n", 891 sPorts[slot].id == id ? "closed" : "invalid", id)); 892 return B_BAD_PORT_ID; 893 } 894 895 cachedSem = sPorts[slot].read_sem; 896 897 RELEASE_PORT_LOCK(sPorts[slot]); 898 restore_interrupts(state); 899 900 // block if no message, or, if B_TIMEOUT flag set, block with timeout 901 902 status = acquire_sem_etc(cachedSem, 1, flags, timeout); 903 if (status != B_OK && status != B_BAD_SEM_ID) 904 return status; 905 906 // in case of B_BAD_SEM_ID, the port might have been closed but not yet 907 // deleted, ie. there could still be messages waiting for us 908 909 state = disable_interrupts(); 910 GRAB_PORT_LOCK(sPorts[slot]); 911 912 if (sPorts[slot].id != id) { 913 // the port is no longer there 914 RELEASE_PORT_LOCK(sPorts[slot]); 915 restore_interrupts(state); 916 return B_BAD_PORT_ID; 917 } 918 919 // determine tail & get the length of the message 920 status_t error = B_OK; 921 msg = (port_msg*)list_get_first_item(&sPorts[slot].msg_queue); 922 if (msg == NULL) { 923 if (status == B_OK) 924 panic("port %ld: no messages found\n", sPorts[slot].id); 925 926 error = B_BAD_PORT_ID; 927 } else { 928 info->size = msg->size; 929 info->sender = msg->sender; 930 info->sender_group = msg->sender_group; 931 info->sender_team = msg->sender_team; 932 } 933 934 RELEASE_PORT_LOCK(sPorts[slot]); 935 restore_interrupts(state); 936 937 // restore read_sem, as we haven't read from the port 938 release_sem(cachedSem); 939 940 // return length of item at end of queue 941 return error; 942 } 943 944 945 ssize_t 946 port_count(port_id id) 947 { 948 cpu_status state; 949 int32 count = 0; 950 int32 slot; 951 952 if (!sPortsActive || id < 0) 953 return B_BAD_PORT_ID; 954 955 slot = id % sMaxPorts; 956 957 state = disable_interrupts(); 958 GRAB_PORT_LOCK(sPorts[slot]); 959 960 if (sPorts[slot].id != id) { 961 RELEASE_PORT_LOCK(sPorts[slot]); 962 restore_interrupts(state); 963 TRACE(("port_count: invalid port_id %ld\n", id)); 964 return B_BAD_PORT_ID; 965 } 966 967 if (get_sem_count(sPorts[slot].read_sem, &count) == B_OK) { 968 // do not return negative numbers 969 if (count < 0) 970 count = 0; 971 } else { 972 // the port might have been closed - we need to actually count the messages 973 void *message = NULL; 974 while ((message = list_get_next_item(&sPorts[slot].msg_queue, message)) != NULL) { 975 count++; 976 } 977 } 978 979 RELEASE_PORT_LOCK(sPorts[slot]); 980 restore_interrupts(state); 981 982 // return count of messages 983 return count; 984 } 985 986 987 ssize_t 988 read_port(port_id port, int32 *msgCode, void *msgBuffer, size_t bufferSize) 989 { 990 return read_port_etc(port, msgCode, msgBuffer, bufferSize, 0, 0); 991 } 992 993 994 ssize_t 995 read_port_etc(port_id id, int32 *_msgCode, void *msgBuffer, size_t bufferSize, 996 uint32 flags, bigtime_t timeout) 997 { 998 cpu_status state; 999 sem_id cachedSem; 1000 status_t status; 1001 bool userCopy = (flags & PORT_FLAG_USE_USER_MEMCPY) != 0; 1002 bool peekOnly = !userCopy && (flags & B_PEEK_PORT_MESSAGE) != 0; 1003 port_msg *msg; 1004 size_t size; 1005 int slot; 1006 1007 if (!sPortsActive || id < 0) 1008 return B_BAD_PORT_ID; 1009 1010 if ((msgBuffer == NULL && bufferSize > 0) 1011 || timeout < 0) 1012 return B_BAD_VALUE; 1013 1014 flags = flags & (B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT 1015 | B_RELATIVE_TIMEOUT | B_ABSOLUTE_TIMEOUT); 1016 slot = id % sMaxPorts; 1017 1018 state = disable_interrupts(); 1019 GRAB_PORT_LOCK(sPorts[slot]); 1020 1021 if (sPorts[slot].id != id 1022 || (is_port_closed(slot) && list_is_empty(&sPorts[slot].msg_queue))) { 1023 RELEASE_PORT_LOCK(sPorts[slot]); 1024 restore_interrupts(state); 1025 TRACE(("read_port_etc(): %s port %ld\n", 1026 sPorts[slot].id == id ? "closed" : "invalid", id)); 1027 return B_BAD_PORT_ID; 1028 } 1029 // store sem_id in local variable 1030 cachedSem = sPorts[slot].read_sem; 1031 1032 // unlock port && enable ints/ 1033 RELEASE_PORT_LOCK(sPorts[slot]); 1034 restore_interrupts(state); 1035 1036 status = acquire_sem_etc(cachedSem, 1, flags, timeout); 1037 // get 1 entry from the queue, block if needed 1038 1039 if (status != B_OK && status != B_BAD_SEM_ID) 1040 return status; 1041 1042 // in case of B_BAD_SEM_ID, the port might have been closed but not yet 1043 // deleted, ie. there could still be messages waiting for us 1044 1045 state = disable_interrupts(); 1046 GRAB_PORT_LOCK(sPorts[slot]); 1047 1048 // first, let's check if the port is still alive 1049 if (sPorts[slot].id == -1) { 1050 // the port has been deleted in the meantime 1051 RELEASE_PORT_LOCK(sPorts[slot]); 1052 restore_interrupts(state); 1053 return B_BAD_PORT_ID; 1054 } 1055 1056 msg = (port_msg*)list_get_first_item(&sPorts[slot].msg_queue); 1057 if (msg == NULL) { 1058 if (status == B_OK) 1059 panic("port %ld: no messages found", sPorts[slot].id); 1060 1061 // the port has obviously been closed, but no messages are left anymore 1062 RELEASE_PORT_LOCK(sPorts[slot]); 1063 restore_interrupts(state); 1064 return B_BAD_PORT_ID; 1065 } 1066 1067 if (peekOnly) { 1068 size = min_c(bufferSize, msg->size); 1069 if (_msgCode != NULL) 1070 *_msgCode = msg->code; 1071 if (size > 0) 1072 cbuf_memcpy_from_chain(msgBuffer, msg->buffer_chain, 0, size); 1073 RELEASE_PORT_LOCK(sPorts[slot]); 1074 restore_interrupts(state); 1075 release_sem_etc(cachedSem, 1, B_DO_NOT_RESCHEDULE); 1076 // we only peeked, but didn't grab the message 1077 return size; 1078 } 1079 1080 list_remove_link(msg); 1081 1082 sPorts[slot].total_count++; 1083 1084 notify_port_select_events(slot, B_EVENT_WRITE); 1085 1086 cachedSem = sPorts[slot].write_sem; 1087 1088 RELEASE_PORT_LOCK(sPorts[slot]); 1089 restore_interrupts(state); 1090 1091 // check output buffer size 1092 size = min_c(bufferSize, msg->size); 1093 1094 // copy message 1095 if (_msgCode != NULL) 1096 *_msgCode = msg->code; 1097 if (size > 0) { 1098 if (userCopy) { 1099 if ((status = cbuf_user_memcpy_from_chain(msgBuffer, msg->buffer_chain, 0, size) < B_OK)) { 1100 // leave the port intact, for other threads that might not crash 1101 put_port_msg(msg); 1102 release_sem(cachedSem); 1103 return status; 1104 } 1105 } else 1106 cbuf_memcpy_from_chain(msgBuffer, msg->buffer_chain, 0, size); 1107 } 1108 put_port_msg(msg); 1109 1110 // make one spot in queue available again for write 1111 release_sem(cachedSem); 1112 // ToDo: we might think about setting B_NO_RESCHEDULE here 1113 // from time to time (always?) 1114 1115 return size; 1116 } 1117 1118 1119 status_t 1120 write_port(port_id id, int32 msgCode, const void *msgBuffer, size_t bufferSize) 1121 { 1122 iovec vec = { (void *)msgBuffer, bufferSize }; 1123 1124 return writev_port_etc(id, msgCode, &vec, 1, bufferSize, 0, 0); 1125 } 1126 1127 1128 status_t 1129 write_port_etc(port_id id, int32 msgCode, const void *msgBuffer, 1130 size_t bufferSize, uint32 flags, bigtime_t timeout) 1131 { 1132 iovec vec = { (void *)msgBuffer, bufferSize }; 1133 1134 return writev_port_etc(id, msgCode, &vec, 1, bufferSize, flags, timeout); 1135 } 1136 1137 1138 status_t 1139 writev_port_etc(port_id id, int32 msgCode, const iovec *msgVecs, 1140 size_t vecCount, size_t bufferSize, uint32 flags, 1141 bigtime_t timeout) 1142 { 1143 cpu_status state; 1144 sem_id cachedSem; 1145 status_t status; 1146 port_msg *msg; 1147 bool userCopy = (flags & PORT_FLAG_USE_USER_MEMCPY) > 0; 1148 int slot; 1149 1150 if (!sPortsActive || id < 0) 1151 return B_BAD_PORT_ID; 1152 1153 // mask irrelevant flags (for acquire_sem() usage) 1154 flags = flags & (B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT 1155 | B_RELATIVE_TIMEOUT | B_ABSOLUTE_TIMEOUT); 1156 slot = id % sMaxPorts; 1157 1158 if (bufferSize > PORT_MAX_MESSAGE_SIZE) 1159 return B_BAD_VALUE; 1160 1161 state = disable_interrupts(); 1162 GRAB_PORT_LOCK(sPorts[slot]); 1163 1164 if (sPorts[slot].id != id) { 1165 RELEASE_PORT_LOCK(sPorts[slot]); 1166 restore_interrupts(state); 1167 TRACE(("write_port_etc: invalid port_id %ld\n", id)); 1168 return B_BAD_PORT_ID; 1169 } 1170 1171 if (is_port_closed(slot)) { 1172 RELEASE_PORT_LOCK(sPorts[slot]); 1173 restore_interrupts(state); 1174 TRACE(("write_port_etc: port %ld closed\n", id)); 1175 return B_BAD_PORT_ID; 1176 } 1177 1178 // store sem_id in local variable 1179 cachedSem = sPorts[slot].write_sem; 1180 1181 RELEASE_PORT_LOCK(sPorts[slot]); 1182 restore_interrupts(state); 1183 1184 status = acquire_sem_etc(cachedSem, 1, flags, timeout); 1185 // get 1 entry from the queue, block if needed 1186 1187 if (status == B_BAD_SEM_ID) { 1188 // somebody deleted or closed the port 1189 return B_BAD_PORT_ID; 1190 } 1191 if (status != B_OK) 1192 return status; 1193 1194 msg = get_port_msg(msgCode, bufferSize); 1195 if (msg == NULL) { 1196 // Give up our slot in the queue again, and let someone else try and fail 1197 // TODO: try to free some resources and try again? 1198 release_sem(cachedSem); 1199 return B_NO_MEMORY; 1200 } 1201 1202 // sender credentials 1203 msg->sender = geteuid(); 1204 msg->sender_group = getegid(); 1205 msg->sender_team = team_get_current_team_id(); 1206 1207 if (bufferSize > 0) { 1208 uint32 i; 1209 if (userCopy) { 1210 // copy from user memory 1211 for (i = 0; i < vecCount; i++) { 1212 size_t bytes = msgVecs[i].iov_len; 1213 if (bytes > bufferSize) 1214 bytes = bufferSize; 1215 1216 if ((status = cbuf_user_memcpy_to_chain(msg->buffer_chain, 1217 0, msgVecs[i].iov_base, bytes)) < B_OK) { 1218 put_port_msg(msg); 1219 return status; 1220 } 1221 1222 bufferSize -= bytes; 1223 if (bufferSize == 0) 1224 break; 1225 } 1226 } else { 1227 // copy from kernel memory 1228 for (i = 0; i < vecCount; i++) { 1229 size_t bytes = msgVecs[i].iov_len; 1230 if (bytes > bufferSize) 1231 bytes = bufferSize; 1232 1233 if ((status = cbuf_memcpy_to_chain(msg->buffer_chain, 1234 0, msgVecs[i].iov_base, bytes)) < 0) { 1235 put_port_msg(msg); 1236 return status; 1237 } 1238 1239 bufferSize -= bytes; 1240 if (bufferSize == 0) 1241 break; 1242 } 1243 } 1244 } 1245 1246 // attach message to queue 1247 state = disable_interrupts(); 1248 GRAB_PORT_LOCK(sPorts[slot]); 1249 1250 // first, let's check if the port is still alive 1251 if (sPorts[slot].id == -1) { 1252 // the port has been deleted in the meantime 1253 RELEASE_PORT_LOCK(sPorts[slot]); 1254 restore_interrupts(state); 1255 1256 put_port_msg(msg); 1257 return B_BAD_PORT_ID; 1258 } 1259 1260 list_add_item(&sPorts[slot].msg_queue, msg); 1261 1262 notify_port_select_events(slot, B_EVENT_READ); 1263 1264 // store sem_id in local variable 1265 cachedSem = sPorts[slot].read_sem; 1266 1267 RELEASE_PORT_LOCK(sPorts[slot]); 1268 restore_interrupts(state); 1269 1270 // release sem, allowing read (might reschedule) 1271 release_sem(cachedSem); 1272 1273 return B_NO_ERROR; 1274 } 1275 1276 1277 status_t 1278 set_port_owner(port_id id, team_id team) 1279 { 1280 cpu_status state; 1281 int slot; 1282 // ToDo: Shouldn't we at least check, whether the team exists? 1283 1284 TRACE(("set_port_owner(id = %ld, team = %ld)\n", id, team)); 1285 1286 if (!sPortsActive || id < 0) 1287 return B_BAD_PORT_ID; 1288 1289 slot = id % sMaxPorts; 1290 1291 state = disable_interrupts(); 1292 GRAB_PORT_LOCK(sPorts[slot]); 1293 1294 if (sPorts[slot].id != id) { 1295 RELEASE_PORT_LOCK(sPorts[slot]); 1296 restore_interrupts(state); 1297 TRACE(("set_port_owner: invalid port_id %ld\n", id)); 1298 return B_BAD_PORT_ID; 1299 } 1300 1301 // transfer ownership to other team 1302 sPorts[slot].owner = team; 1303 1304 // unlock port 1305 RELEASE_PORT_LOCK(sPorts[slot]); 1306 restore_interrupts(state); 1307 1308 return B_NO_ERROR; 1309 } 1310 1311 1312 // #pragma mark - syscalls 1313 1314 1315 port_id 1316 _user_create_port(int32 queueLength, const char *userName) 1317 { 1318 char name[B_OS_NAME_LENGTH]; 1319 1320 if (userName == NULL) 1321 return create_port(queueLength, NULL); 1322 1323 if (!IS_USER_ADDRESS(userName) 1324 || user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK) 1325 return B_BAD_ADDRESS; 1326 1327 return create_port(queueLength, name); 1328 } 1329 1330 1331 status_t 1332 _user_close_port(port_id id) 1333 { 1334 return close_port(id); 1335 } 1336 1337 1338 status_t 1339 _user_delete_port(port_id id) 1340 { 1341 return delete_port(id); 1342 } 1343 1344 1345 port_id 1346 _user_find_port(const char *userName) 1347 { 1348 char name[B_OS_NAME_LENGTH]; 1349 1350 if (userName == NULL) 1351 return B_BAD_VALUE; 1352 if (!IS_USER_ADDRESS(userName) 1353 || user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK) 1354 return B_BAD_ADDRESS; 1355 1356 return find_port(name); 1357 } 1358 1359 1360 status_t 1361 _user_get_port_info(port_id id, struct port_info *userInfo) 1362 { 1363 struct port_info info; 1364 status_t status; 1365 1366 if (userInfo == NULL) 1367 return B_BAD_VALUE; 1368 if (!IS_USER_ADDRESS(userInfo)) 1369 return B_BAD_ADDRESS; 1370 1371 status = get_port_info(id, &info); 1372 1373 // copy back to user space 1374 if (status == B_OK 1375 && user_memcpy(userInfo, &info, sizeof(struct port_info)) < B_OK) 1376 return B_BAD_ADDRESS; 1377 1378 return status; 1379 } 1380 1381 1382 status_t 1383 _user_get_next_port_info(team_id team, int32 *userCookie, 1384 struct port_info *userInfo) 1385 { 1386 struct port_info info; 1387 status_t status; 1388 int32 cookie; 1389 1390 if (userCookie == NULL || userInfo == NULL) 1391 return B_BAD_VALUE; 1392 if (!IS_USER_ADDRESS(userCookie) || !IS_USER_ADDRESS(userInfo) 1393 || user_memcpy(&cookie, userCookie, sizeof(int32)) < B_OK) 1394 return B_BAD_ADDRESS; 1395 1396 status = get_next_port_info(team, &cookie, &info); 1397 1398 // copy back to user space 1399 if (user_memcpy(userCookie, &cookie, sizeof(int32)) < B_OK 1400 || (status == B_OK && user_memcpy(userInfo, &info, 1401 sizeof(struct port_info)) < B_OK)) 1402 return B_BAD_ADDRESS; 1403 1404 return status; 1405 } 1406 1407 1408 ssize_t 1409 _user_port_buffer_size_etc(port_id port, uint32 flags, bigtime_t timeout) 1410 { 1411 syscall_restart_handle_timeout_pre(flags, timeout); 1412 1413 status_t status = port_buffer_size_etc(port, flags | B_CAN_INTERRUPT, 1414 timeout); 1415 1416 return syscall_restart_handle_timeout_post(status, timeout); 1417 } 1418 1419 1420 ssize_t 1421 _user_port_count(port_id port) 1422 { 1423 return port_count(port); 1424 } 1425 1426 1427 status_t 1428 _user_set_port_owner(port_id port, team_id team) 1429 { 1430 return set_port_owner(port, team); 1431 } 1432 1433 1434 ssize_t 1435 _user_read_port_etc(port_id port, int32 *userCode, void *userBuffer, 1436 size_t bufferSize, uint32 flags, bigtime_t timeout) 1437 { 1438 int32 messageCode; 1439 ssize_t bytesRead; 1440 1441 syscall_restart_handle_timeout_pre(flags, timeout); 1442 1443 if (userBuffer == NULL && bufferSize != 0) 1444 return B_BAD_VALUE; 1445 if ((userCode != NULL && !IS_USER_ADDRESS(userCode)) 1446 || (userBuffer != NULL && !IS_USER_ADDRESS(userBuffer))) 1447 return B_BAD_ADDRESS; 1448 1449 bytesRead = read_port_etc(port, &messageCode, userBuffer, bufferSize, 1450 flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, timeout); 1451 1452 if (bytesRead >= 0 && userCode != NULL 1453 && user_memcpy(userCode, &messageCode, sizeof(int32)) < B_OK) 1454 return B_BAD_ADDRESS; 1455 1456 return syscall_restart_handle_timeout_post(bytesRead, timeout); 1457 } 1458 1459 1460 status_t 1461 _user_write_port_etc(port_id port, int32 messageCode, const void *userBuffer, 1462 size_t bufferSize, uint32 flags, bigtime_t timeout) 1463 { 1464 iovec vec = { (void *)userBuffer, bufferSize }; 1465 1466 syscall_restart_handle_timeout_pre(flags, timeout); 1467 1468 if (userBuffer == NULL && bufferSize != 0) 1469 return B_BAD_VALUE; 1470 if (userBuffer != NULL && !IS_USER_ADDRESS(userBuffer)) 1471 return B_BAD_ADDRESS; 1472 1473 status_t status = writev_port_etc(port, messageCode, &vec, 1, bufferSize, 1474 flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, timeout); 1475 1476 return syscall_restart_handle_timeout_post(status, timeout); 1477 } 1478 1479 1480 status_t 1481 _user_writev_port_etc(port_id port, int32 messageCode, const iovec *userVecs, 1482 size_t vecCount, size_t bufferSize, uint32 flags, bigtime_t timeout) 1483 { 1484 syscall_restart_handle_timeout_pre(flags, timeout); 1485 1486 if (userVecs == NULL && bufferSize != 0) 1487 return B_BAD_VALUE; 1488 if (userVecs != NULL && !IS_USER_ADDRESS(userVecs)) 1489 return B_BAD_ADDRESS; 1490 1491 iovec *vecs = NULL; 1492 if (userVecs && vecCount != 0) { 1493 vecs = (iovec*)malloc(sizeof(iovec) * vecCount); 1494 if (vecs == NULL) 1495 return B_NO_MEMORY; 1496 1497 if (user_memcpy(vecs, userVecs, sizeof(iovec) * vecCount) < B_OK) { 1498 free(vecs); 1499 return B_BAD_ADDRESS; 1500 } 1501 } 1502 1503 status_t status = writev_port_etc(port, messageCode, vecs, vecCount, 1504 bufferSize, flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, 1505 timeout); 1506 1507 free(vecs); 1508 return syscall_restart_handle_timeout_post(status, timeout); 1509 } 1510 1511 1512 status_t 1513 _user_get_port_message_info_etc(port_id port, port_message_info *userInfo, 1514 size_t infoSize, uint32 flags, bigtime_t timeout) 1515 { 1516 if (userInfo == NULL || infoSize != sizeof(port_message_info)) 1517 return B_BAD_VALUE; 1518 1519 syscall_restart_handle_timeout_pre(flags, timeout); 1520 1521 port_message_info info; 1522 status_t error = _get_port_message_info_etc(port, &info, sizeof(info), 1523 flags | B_CAN_INTERRUPT, timeout); 1524 1525 // copy info to userland 1526 if (error == B_OK && (!IS_USER_ADDRESS(userInfo) 1527 || user_memcpy(userInfo, &info, sizeof(info)) != B_OK)) { 1528 error = B_BAD_ADDRESS; 1529 } 1530 1531 return syscall_restart_handle_timeout_post(error, timeout); 1532 } 1533