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