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