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