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