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