1 /* 2 * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2002-2010, Axel Dörfler, axeld@pinc-software.de. 4 * Distributed under the terms of the MIT License. 5 * 6 * Copyright 2001, Mark-Jan Bastian. All rights reserved. 7 * Distributed under the terms of the NewOS License. 8 */ 9 10 11 /*! Ports for IPC */ 12 13 14 #include <port.h> 15 16 #include <ctype.h> 17 #include <iovec.h> 18 #include <stdlib.h> 19 #include <string.h> 20 21 #include <OS.h> 22 23 #include <AutoDeleter.h> 24 25 #include <arch/int.h> 26 #include <heap.h> 27 #include <kernel.h> 28 #include <Notifications.h> 29 #include <sem.h> 30 #include <syscall_restart.h> 31 #include <team.h> 32 #include <tracing.h> 33 #include <util/AutoLock.h> 34 #include <util/list.h> 35 #include <vm/vm.h> 36 #include <wait_for_objects.h> 37 38 39 //#define TRACE_PORTS 40 #ifdef TRACE_PORTS 41 # define TRACE(x) dprintf x 42 #else 43 # define TRACE(x) 44 #endif 45 46 47 #if __GNUC__ >= 3 48 # define GCC_2_NRV(x) 49 // GCC >= 3.1 doesn't need it anymore 50 #else 51 # define GCC_2_NRV(x) return x; 52 // GCC 2 named return value syntax 53 // see http://gcc.gnu.org/onlinedocs/gcc-2.95.2/gcc_5.html#SEC106 54 #endif 55 56 57 // Locking: 58 // * sPortsLock: Protects the sPorts and sPortsByName hash tables. 59 // * sTeamListLock[]: Protects Team::port_list. Lock index for given team is 60 // (Team::id % kTeamListLockCount). 61 // * Port::lock: Protects all Port members save team_link, hash_link, lock and 62 // state. id is immutable. 63 // 64 // Port::state ensures atomicity by providing a linearization point for adding 65 // and removing ports to the hash tables and the team port list. 66 // * sPortsLock and sTeamListLock[] are locked separately and not in a nested 67 // fashion, so a port can be in the hash table but not in the team port list 68 // or vice versa. => Without further provisions, insertion and removal are 69 // not linearizable and thus not concurrency-safe. 70 // * To make insertion and removal linearizable, Port::state was added. It is 71 // always only accessed atomically and updates are done using 72 // atomic_test_and_set(). A port is only seen as existent when its state is 73 // Port::kActive. 74 // * Deletion of ports is done in two steps: logical and physical deletion. 75 // First, logical deletion happens and sets Port::state to Port::kDeleted. 76 // This is an atomic operation and from then on, functions like 77 // get_locked_port() consider this port as deleted and ignore it. Secondly, 78 // physical deletion removes the port from hash tables and team port list. 79 // In a similar way, port creation first inserts into hashes and team list 80 // and only then sets port to Port::kActive. 81 // This creates a linearization point at the atomic update of Port::state, 82 // operations become linearizable and thus concurrency-safe. To help 83 // understanding, the linearization points are annotated with comments. 84 // * Ports are reference-counted so it's not a problem when someone still 85 // has a reference to a deleted port. 86 87 88 struct port_message; 89 90 91 static void put_port_message(port_message* message); 92 93 94 struct port_message : DoublyLinkedListLinkImpl<port_message> { 95 int32 code; 96 size_t size; 97 uid_t sender; 98 gid_t sender_group; 99 team_id sender_team; 100 char buffer[0]; 101 }; 102 103 typedef DoublyLinkedList<port_message> MessageList; 104 105 106 struct Port : public KernelReferenceable { 107 enum State { 108 kUnused = 0, 109 kActive, 110 kDeleted 111 }; 112 113 struct list_link team_link; 114 Port* hash_link; 115 port_id id; 116 team_id owner; 117 Port* name_hash_link; 118 size_t name_hash; 119 int32 capacity; 120 mutex lock; 121 int32 state; 122 uint32 read_count; 123 int32 write_count; 124 ConditionVariable read_condition; 125 ConditionVariable write_condition; 126 int32 total_count; 127 // messages read from port since creation 128 select_info* select_infos; 129 MessageList messages; 130 131 Port(team_id owner, int32 queueLength, char* name) 132 : 133 owner(owner), 134 name_hash(0), 135 capacity(queueLength), 136 state(kUnused), 137 read_count(0), 138 write_count(queueLength), 139 total_count(0), 140 select_infos(NULL) 141 { 142 // id is initialized when the caller adds the port to the hash table 143 144 mutex_init(&lock, name); 145 read_condition.Init(this, "port read"); 146 write_condition.Init(this, "port write"); 147 } 148 149 virtual ~Port() 150 { 151 while (port_message* message = messages.RemoveHead()) 152 put_port_message(message); 153 154 free((char*)lock.name); 155 lock.name = NULL; 156 } 157 }; 158 159 160 struct PortHashDefinition { 161 typedef port_id KeyType; 162 typedef Port ValueType; 163 164 size_t HashKey(port_id key) const 165 { 166 return key; 167 } 168 169 size_t Hash(Port* value) const 170 { 171 return HashKey(value->id); 172 } 173 174 bool Compare(port_id key, Port* value) const 175 { 176 return value->id == key; 177 } 178 179 Port*& GetLink(Port* value) const 180 { 181 return value->hash_link; 182 } 183 }; 184 185 typedef BOpenHashTable<PortHashDefinition> PortHashTable; 186 187 188 struct PortNameHashDefinition { 189 typedef const char* KeyType; 190 typedef Port ValueType; 191 192 size_t HashKey(const char* key) const 193 { 194 // Hash function: hash(key) = key[0] * 31^(length - 1) 195 // + key[1] * 31^(length - 2) + ... + key[length - 1] 196 197 const size_t length = strlen(key); 198 199 size_t hash = 0; 200 for (size_t index = 0; index < length; index++) 201 hash = 31 * hash + key[index]; 202 203 return hash; 204 } 205 206 size_t Hash(Port* value) const 207 { 208 size_t& hash = value->name_hash; 209 if (hash == 0) 210 hash = HashKey(value->lock.name); 211 return hash; 212 } 213 214 bool Compare(const char* key, Port* value) const 215 { 216 return (strcmp(key, value->lock.name) == 0); 217 } 218 219 Port*& GetLink(Port* value) const 220 { 221 return value->name_hash_link; 222 } 223 }; 224 225 typedef BOpenHashTable<PortNameHashDefinition> PortNameHashTable; 226 227 228 class PortNotificationService : public DefaultNotificationService { 229 public: 230 PortNotificationService(); 231 232 void Notify(uint32 opcode, port_id team); 233 }; 234 235 236 // #pragma mark - tracing 237 238 239 #if PORT_TRACING 240 namespace PortTracing { 241 242 class Create : public AbstractTraceEntry { 243 public: 244 Create(Port* port) 245 : 246 fID(port->id), 247 fOwner(port->owner), 248 fCapacity(port->capacity) 249 { 250 fName = alloc_tracing_buffer_strcpy(port->lock.name, B_OS_NAME_LENGTH, 251 false); 252 253 Initialized(); 254 } 255 256 virtual void AddDump(TraceOutput& out) 257 { 258 out.Print("port %ld created, name \"%s\", owner %ld, capacity %ld", 259 fID, fName, fOwner, fCapacity); 260 } 261 262 private: 263 port_id fID; 264 char* fName; 265 team_id fOwner; 266 int32 fCapacity; 267 }; 268 269 270 class Delete : public AbstractTraceEntry { 271 public: 272 Delete(Port* port) 273 : 274 fID(port->id) 275 { 276 Initialized(); 277 } 278 279 virtual void AddDump(TraceOutput& out) 280 { 281 out.Print("port %ld deleted", fID); 282 } 283 284 private: 285 port_id fID; 286 }; 287 288 289 class Read : public AbstractTraceEntry { 290 public: 291 Read(port_id id, int32 readCount, int32 writeCount, int32 code, 292 ssize_t result) 293 : 294 fID(id), 295 fReadCount(readCount), 296 fWriteCount(writeCount), 297 fCode(code), 298 fResult(result) 299 { 300 Initialized(); 301 } 302 303 virtual void AddDump(TraceOutput& out) 304 { 305 out.Print("port %ld read, read %ld, write %ld, code %lx: %ld", 306 fID, fReadCount, fWriteCount, fCode, fResult); 307 } 308 309 private: 310 port_id fID; 311 int32 fReadCount; 312 int32 fWriteCount; 313 int32 fCode; 314 ssize_t fResult; 315 }; 316 317 318 class Write : public AbstractTraceEntry { 319 public: 320 Write(port_id id, int32 readCount, int32 writeCount, int32 code, 321 size_t bufferSize, ssize_t result) 322 : 323 fID(id), 324 fReadCount(readCount), 325 fWriteCount(writeCount), 326 fCode(code), 327 fBufferSize(bufferSize), 328 fResult(result) 329 { 330 Initialized(); 331 } 332 333 virtual void AddDump(TraceOutput& out) 334 { 335 out.Print("port %ld write, read %ld, write %ld, code %lx, size %ld: %ld", 336 fID, fReadCount, fWriteCount, fCode, fBufferSize, fResult); 337 } 338 339 private: 340 port_id fID; 341 int32 fReadCount; 342 int32 fWriteCount; 343 int32 fCode; 344 size_t fBufferSize; 345 ssize_t fResult; 346 }; 347 348 349 class Info : public AbstractTraceEntry { 350 public: 351 Info(port_id id, int32 readCount, int32 writeCount, int32 code, 352 ssize_t result) 353 : 354 fID(id), 355 fReadCount(readCount), 356 fWriteCount(writeCount), 357 fCode(code), 358 fResult(result) 359 { 360 Initialized(); 361 } 362 363 virtual void AddDump(TraceOutput& out) 364 { 365 out.Print("port %ld info, read %ld, write %ld, code %lx: %ld", 366 fID, fReadCount, fWriteCount, fCode, fResult); 367 } 368 369 private: 370 port_id fID; 371 int32 fReadCount; 372 int32 fWriteCount; 373 int32 fCode; 374 ssize_t fResult; 375 }; 376 377 378 class OwnerChange : public AbstractTraceEntry { 379 public: 380 OwnerChange(Port* port, team_id newOwner, status_t status) 381 : 382 fID(port->id), 383 fOldOwner(port->owner), 384 fNewOwner(newOwner), 385 fStatus(status) 386 { 387 Initialized(); 388 } 389 390 virtual void AddDump(TraceOutput& out) 391 { 392 out.Print("port %ld owner change from %ld to %ld: %s", fID, fOldOwner, 393 fNewOwner, strerror(fStatus)); 394 } 395 396 private: 397 port_id fID; 398 team_id fOldOwner; 399 team_id fNewOwner; 400 status_t fStatus; 401 }; 402 403 } // namespace PortTracing 404 405 # define T(x) new(std::nothrow) PortTracing::x; 406 #else 407 # define T(x) ; 408 #endif 409 410 411 static const size_t kInitialPortBufferSize = 4 * 1024 * 1024; 412 static const size_t kTotalSpaceLimit = 64 * 1024 * 1024; 413 static const size_t kTeamSpaceLimit = 8 * 1024 * 1024; 414 static const size_t kBufferGrowRate = kInitialPortBufferSize; 415 416 #define MAX_QUEUE_LENGTH 4096 417 #define PORT_MAX_MESSAGE_SIZE (256 * 1024) 418 419 static int32 sMaxPorts = 4096; 420 static int32 sUsedPorts; 421 422 static PortHashTable sPorts; 423 static PortNameHashTable sPortsByName; 424 static ConditionVariable sNoSpaceCondition; 425 static int32 sTotalSpaceCommited; 426 static int32 sWaitingForSpace; 427 static port_id sNextPortID = 1; 428 static bool sPortsActive = false; 429 static rw_lock sPortsLock = RW_LOCK_INITIALIZER("ports list"); 430 431 enum { 432 kTeamListLockCount = 8 433 }; 434 435 static mutex sTeamListLock[kTeamListLockCount] = { 436 MUTEX_INITIALIZER("team ports list 1"), 437 MUTEX_INITIALIZER("team ports list 2"), 438 MUTEX_INITIALIZER("team ports list 3"), 439 MUTEX_INITIALIZER("team ports list 4"), 440 MUTEX_INITIALIZER("team ports list 5"), 441 MUTEX_INITIALIZER("team ports list 6"), 442 MUTEX_INITIALIZER("team ports list 7"), 443 MUTEX_INITIALIZER("team ports list 8") 444 }; 445 446 static PortNotificationService sNotificationService; 447 448 449 // #pragma mark - TeamNotificationService 450 451 452 PortNotificationService::PortNotificationService() 453 : 454 DefaultNotificationService("ports") 455 { 456 } 457 458 459 void 460 PortNotificationService::Notify(uint32 opcode, port_id port) 461 { 462 char eventBuffer[128]; 463 KMessage event; 464 event.SetTo(eventBuffer, sizeof(eventBuffer), PORT_MONITOR); 465 event.AddInt32("event", opcode); 466 event.AddInt32("port", port); 467 468 DefaultNotificationService::Notify(event, opcode); 469 } 470 471 472 // #pragma mark - debugger commands 473 474 475 static int 476 dump_port_list(int argc, char** argv) 477 { 478 const char* name = NULL; 479 team_id owner = -1; 480 481 if (argc > 2) { 482 if (!strcmp(argv[1], "team") || !strcmp(argv[1], "owner")) 483 owner = strtoul(argv[2], NULL, 0); 484 else if (!strcmp(argv[1], "name")) 485 name = argv[2]; 486 } else if (argc > 1) 487 owner = strtoul(argv[1], NULL, 0); 488 489 kprintf("port id cap read-cnt write-cnt total team " 490 "name\n"); 491 492 for (PortHashTable::Iterator it = sPorts.GetIterator(); 493 Port* port = it.Next();) { 494 if ((owner != -1 && port->owner != owner) 495 || (name != NULL && strstr(port->lock.name, name) == NULL)) 496 continue; 497 498 kprintf("%p %8" B_PRId32 " %4" B_PRId32 " %9" B_PRIu32 " %9" B_PRId32 499 " %8" B_PRId32 " %6" B_PRId32 " %s\n", port, port->id, 500 port->capacity, port->read_count, port->write_count, 501 port->total_count, port->owner, port->lock.name); 502 } 503 504 return 0; 505 } 506 507 508 static void 509 _dump_port_info(Port* port) 510 { 511 kprintf("PORT: %p\n", port); 512 kprintf(" id: %" B_PRId32 "\n", port->id); 513 kprintf(" name: \"%s\"\n", port->lock.name); 514 kprintf(" owner: %" B_PRId32 "\n", port->owner); 515 kprintf(" capacity: %" B_PRId32 "\n", port->capacity); 516 kprintf(" read_count: %" B_PRIu32 "\n", port->read_count); 517 kprintf(" write_count: %" B_PRId32 "\n", port->write_count); 518 kprintf(" total count: %" B_PRId32 "\n", port->total_count); 519 520 if (!port->messages.IsEmpty()) { 521 kprintf("messages:\n"); 522 523 MessageList::Iterator iterator = port->messages.GetIterator(); 524 while (port_message* message = iterator.Next()) { 525 kprintf(" %p %08" B_PRIx32 " %ld\n", message, message->code, message->size); 526 } 527 } 528 529 set_debug_variable("_port", (addr_t)port); 530 set_debug_variable("_portID", port->id); 531 set_debug_variable("_owner", port->owner); 532 } 533 534 535 static int 536 dump_port_info(int argc, char** argv) 537 { 538 ConditionVariable* condition = NULL; 539 const char* name = NULL; 540 541 if (argc < 2) { 542 print_debugger_command_usage(argv[0]); 543 return 0; 544 } 545 546 if (argc > 2) { 547 if (!strcmp(argv[1], "address")) { 548 _dump_port_info((Port*)parse_expression(argv[2])); 549 return 0; 550 } else if (!strcmp(argv[1], "condition")) 551 condition = (ConditionVariable*)parse_expression(argv[2]); 552 else if (!strcmp(argv[1], "name")) 553 name = argv[2]; 554 } else if (parse_expression(argv[1]) > 0) { 555 // if the argument looks like a number, treat it as such 556 int32 num = parse_expression(argv[1]); 557 Port* port = sPorts.Lookup(num); 558 if (port == NULL || port->state != Port::kActive) { 559 kprintf("port %" B_PRId32 " (%#" B_PRIx32 ") doesn't exist!\n", 560 num, num); 561 return 0; 562 } 563 _dump_port_info(port); 564 return 0; 565 } else 566 name = argv[1]; 567 568 // walk through the ports list, trying to match name 569 for (PortHashTable::Iterator it = sPorts.GetIterator(); 570 Port* port = it.Next();) { 571 if ((name != NULL && port->lock.name != NULL 572 && !strcmp(name, port->lock.name)) 573 || (condition != NULL && (&port->read_condition == condition 574 || &port->write_condition == condition))) { 575 _dump_port_info(port); 576 return 0; 577 } 578 } 579 580 return 0; 581 } 582 583 584 // #pragma mark - internal helper functions 585 586 587 /*! Notifies the port's select events. 588 The port must be locked. 589 */ 590 static void 591 notify_port_select_events(Port* port, uint16 events) 592 { 593 if (port->select_infos) 594 notify_select_events_list(port->select_infos, events); 595 } 596 597 598 static BReference<Port> 599 get_locked_port(port_id id) GCC_2_NRV(portRef) 600 { 601 #if __GNUC__ >= 3 602 BReference<Port> portRef; 603 #endif 604 { 605 ReadLocker portsLocker(sPortsLock); 606 portRef.SetTo(sPorts.Lookup(id)); 607 } 608 609 if (portRef != NULL && portRef->state == Port::kActive) 610 mutex_lock(&portRef->lock); 611 else 612 portRef.Unset(); 613 614 return portRef; 615 } 616 617 618 static BReference<Port> 619 get_port(port_id id) GCC_2_NRV(portRef) 620 { 621 #if __GNUC__ >= 3 622 BReference<Port> portRef; 623 #endif 624 ReadLocker portsLocker(sPortsLock); 625 portRef.SetTo(sPorts.Lookup(id)); 626 627 return portRef; 628 } 629 630 631 /*! You need to own the port's lock when calling this function */ 632 static inline bool 633 is_port_closed(Port* port) 634 { 635 return port->capacity == 0; 636 } 637 638 639 static void 640 put_port_message(port_message* message) 641 { 642 const size_t size = sizeof(port_message) + message->size; 643 free(message); 644 645 atomic_add(&sTotalSpaceCommited, -size); 646 if (sWaitingForSpace > 0) 647 sNoSpaceCondition.NotifyAll(); 648 } 649 650 651 /*! Port must be locked. */ 652 static status_t 653 get_port_message(int32 code, size_t bufferSize, uint32 flags, bigtime_t timeout, 654 port_message** _message, Port& port) 655 { 656 const size_t size = sizeof(port_message) + bufferSize; 657 658 while (true) { 659 int32 previouslyCommited = atomic_add(&sTotalSpaceCommited, size); 660 661 while (previouslyCommited + size > kTotalSpaceLimit) { 662 // TODO: add per team limit 663 664 // We are not allowed to allocate more memory, as our 665 // space limit has been reached - just wait until we get 666 // some free space again. 667 668 atomic_add(&sTotalSpaceCommited, -size); 669 670 // TODO: we don't want to wait - but does that also mean we 671 // shouldn't wait for free memory? 672 if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0) 673 return B_WOULD_BLOCK; 674 675 ConditionVariableEntry entry; 676 sNoSpaceCondition.Add(&entry); 677 678 port_id portID = port.id; 679 mutex_unlock(&port.lock); 680 681 atomic_add(&sWaitingForSpace, 1); 682 683 // TODO: right here the condition could be notified and we'd 684 // miss it. 685 686 status_t status = entry.Wait(flags, timeout); 687 688 atomic_add(&sWaitingForSpace, -1); 689 690 // re-lock the port 691 BReference<Port> newPortRef = get_locked_port(portID); 692 693 if (newPortRef.Get() != &port || is_port_closed(&port)) { 694 // the port is no longer usable 695 return B_BAD_PORT_ID; 696 } 697 698 if (status == B_TIMED_OUT) 699 return B_TIMED_OUT; 700 701 previouslyCommited = atomic_add(&sTotalSpaceCommited, size); 702 continue; 703 } 704 705 // Quota is fulfilled, try to allocate the buffer 706 port_message* message = (port_message*)malloc(size); 707 if (message != NULL) { 708 message->code = code; 709 message->size = bufferSize; 710 711 *_message = message; 712 return B_OK; 713 } 714 715 // We weren't able to allocate and we'll start over,so we remove our 716 // size from the commited-counter again. 717 atomic_add(&sTotalSpaceCommited, -size); 718 continue; 719 } 720 } 721 722 723 /*! Fills the port_info structure with information from the specified 724 port. 725 The port's lock must be held when called. 726 */ 727 static void 728 fill_port_info(Port* port, port_info* info, size_t size) 729 { 730 info->port = port->id; 731 info->team = port->owner; 732 info->capacity = port->capacity; 733 734 info->queue_count = port->read_count; 735 info->total_count = port->total_count; 736 737 strlcpy(info->name, port->lock.name, B_OS_NAME_LENGTH); 738 } 739 740 741 static ssize_t 742 copy_port_message(port_message* message, int32* _code, void* buffer, 743 size_t bufferSize, bool userCopy) 744 { 745 // check output buffer size 746 size_t size = min_c(bufferSize, message->size); 747 748 // copy message 749 if (_code != NULL) 750 *_code = message->code; 751 752 if (size > 0) { 753 if (userCopy) { 754 status_t status = user_memcpy(buffer, message->buffer, size); 755 if (status != B_OK) 756 return status; 757 } else 758 memcpy(buffer, message->buffer, size); 759 } 760 761 return size; 762 } 763 764 765 static void 766 uninit_port(Port* port) 767 { 768 MutexLocker locker(port->lock); 769 770 notify_port_select_events(port, B_EVENT_INVALID); 771 port->select_infos = NULL; 772 773 // Release the threads that were blocking on this port. 774 // read_port() will see the B_BAD_PORT_ID return value, and act accordingly 775 port->read_condition.NotifyAll(B_BAD_PORT_ID); 776 port->write_condition.NotifyAll(B_BAD_PORT_ID); 777 sNotificationService.Notify(PORT_REMOVED, port->id); 778 } 779 780 781 /*! Caller must ensure there is still a reference to the port. (Either by 782 * holding a reference itself or by holding a lock on one of the data 783 * structures in which it is referenced.) 784 */ 785 static status_t 786 delete_port_logical(Port* port) 787 { 788 for (;;) { 789 // Try to logically delete 790 const int32 oldState = atomic_test_and_set(&port->state, 791 Port::kDeleted, Port::kActive); 792 // Linearization point for port deletion 793 794 switch (oldState) { 795 case Port::kActive: 796 // Logical deletion succesful 797 return B_OK; 798 799 case Port::kDeleted: 800 // Someone else already deleted it in the meantime 801 TRACE(("delete_port_logical: already deleted port_id %ld\n", 802 port->id)); 803 return B_BAD_PORT_ID; 804 805 case Port::kUnused: 806 // Port is still being created, retry 807 continue; 808 809 default: 810 // Port state got corrupted somehow 811 panic("Invalid port state!\n"); 812 } 813 } 814 } 815 816 817 // #pragma mark - private kernel API 818 819 820 /*! This function deletes all the ports that are owned by the passed team. 821 */ 822 void 823 delete_owned_ports(Team* team) 824 { 825 TRACE(("delete_owned_ports(owner = %ld)\n", team->id)); 826 827 list deletionList; 828 list_init_etc(&deletionList, port_team_link_offset()); 829 830 const uint8 lockIndex = team->id % kTeamListLockCount; 831 MutexLocker teamPortsListLocker(sTeamListLock[lockIndex]); 832 833 // Try to logically delete all ports from the team's port list. 834 // On success, move the port to deletionList. 835 Port* port = (Port*)list_get_first_item(&team->port_list); 836 while (port != NULL) { 837 status_t status = delete_port_logical(port); 838 // Contains linearization point 839 840 Port* nextPort = (Port*)list_get_next_item(&team->port_list, port); 841 842 if (status == B_OK) { 843 list_remove_link(&port->team_link); 844 list_add_item(&deletionList, port); 845 } 846 847 port = nextPort; 848 } 849 850 teamPortsListLocker.Unlock(); 851 852 // Remove all ports in deletionList from hashes 853 { 854 WriteLocker portsLocker(sPortsLock); 855 856 for (Port* port = (Port*)list_get_first_item(&deletionList); 857 port != NULL; 858 port = (Port*)list_get_next_item(&deletionList, port)) { 859 860 sPorts.Remove(port); 861 sPortsByName.Remove(port); 862 port->ReleaseReference(); 863 // joint reference for sPorts and sPortsByName 864 } 865 } 866 867 // Uninitialize ports and release team port list references 868 while (Port* port = (Port*)list_remove_head_item(&deletionList)) { 869 atomic_add(&sUsedPorts, -1); 870 uninit_port(port); 871 port->ReleaseReference(); 872 // Reference for team port list 873 } 874 } 875 876 877 int32 878 port_max_ports(void) 879 { 880 return sMaxPorts; 881 } 882 883 884 int32 885 port_used_ports(void) 886 { 887 return sUsedPorts; 888 } 889 890 891 size_t 892 port_team_link_offset() 893 { 894 // Somewhat ugly workaround since we cannot use offsetof() for a class 895 // with vtable (GCC4 throws a warning then). 896 Port* port = (Port*)0; 897 return (size_t)&port->team_link; 898 } 899 900 901 status_t 902 port_init(kernel_args *args) 903 { 904 // initialize ports table and by-name hash 905 new(&sPorts) PortHashTable; 906 if (sPorts.Init() != B_OK) { 907 panic("Failed to init port hash table!"); 908 return B_NO_MEMORY; 909 } 910 911 new(&sPortsByName) PortNameHashTable; 912 if (sPortsByName.Init() != B_OK) { 913 panic("Failed to init port by name hash table!"); 914 return B_NO_MEMORY; 915 } 916 917 sNoSpaceCondition.Init(&sPorts, "port space"); 918 919 // add debugger commands 920 add_debugger_command_etc("ports", &dump_port_list, 921 "Dump a list of all active ports (for team, with name, etc.)", 922 "[ ([ \"team\" | \"owner\" ] <team>) | (\"name\" <name>) ]\n" 923 "Prints a list of all active ports meeting the given\n" 924 "requirement. If no argument is given, all ports are listed.\n" 925 " <team> - The team owning the ports.\n" 926 " <name> - Part of the name of the ports.\n", 0); 927 add_debugger_command_etc("port", &dump_port_info, 928 "Dump info about a particular port", 929 "(<id> | [ \"address\" ] <address>) | ([ \"name\" ] <name>) " 930 "| (\"condition\" <address>)\n" 931 "Prints info about the specified port.\n" 932 " <address> - Pointer to the port structure.\n" 933 " <name> - Name of the port.\n" 934 " <condition> - address of the port's read or write condition.\n", 0); 935 936 new(&sNotificationService) PortNotificationService(); 937 sNotificationService.Register(); 938 sPortsActive = true; 939 return B_OK; 940 } 941 942 943 // #pragma mark - public kernel API 944 945 946 port_id 947 create_port(int32 queueLength, const char* name) 948 { 949 TRACE(("create_port(queueLength = %ld, name = \"%s\")\n", queueLength, 950 name)); 951 952 if (!sPortsActive) { 953 panic("ports used too early!\n"); 954 return B_BAD_PORT_ID; 955 } 956 if (queueLength < 1 || queueLength > MAX_QUEUE_LENGTH) 957 return B_BAD_VALUE; 958 959 Team* team = thread_get_current_thread()->team; 960 if (team == NULL) 961 return B_BAD_TEAM_ID; 962 963 // check & dup name 964 char* nameBuffer = strdup(name != NULL ? name : "unnamed port"); 965 if (nameBuffer == NULL) 966 return B_NO_MEMORY; 967 968 // create a port 969 Port* port = new(std::nothrow) Port(team_get_current_team_id(), queueLength, 970 nameBuffer); 971 if (port == NULL) { 972 free(nameBuffer); 973 return B_NO_MEMORY; 974 } 975 976 // check the ports limit 977 const int32 previouslyUsed = atomic_add(&sUsedPorts, 1); 978 if (previouslyUsed + 1 >= sMaxPorts) { 979 atomic_add(&sUsedPorts, -1); 980 delete port; 981 return B_NO_MORE_PORTS; 982 } 983 984 { 985 WriteLocker locker(sPortsLock); 986 987 // allocate a port ID 988 do { 989 port->id = sNextPortID++; 990 991 // handle integer overflow 992 if (sNextPortID < 0) 993 sNextPortID = 1; 994 } while (sPorts.Lookup(port->id) != NULL); 995 996 // Insert port physically: 997 // (1/2) Insert into hash tables 998 port->AcquireReference(); 999 // joint reference for sPorts and sPortsByName 1000 1001 sPorts.Insert(port); 1002 sPortsByName.Insert(port); 1003 } 1004 1005 // (2/2) Insert into team list 1006 { 1007 const uint8 lockIndex = port->owner % kTeamListLockCount; 1008 MutexLocker teamPortsListLocker(sTeamListLock[lockIndex]); 1009 port->AcquireReference(); 1010 list_add_item(&team->port_list, port); 1011 } 1012 1013 // tracing, notifications, etc. 1014 T(Create(port)); 1015 1016 const port_id id = port->id; 1017 1018 // Insert port logically by marking it active 1019 const int32 oldState = atomic_test_and_set(&port->state, 1020 Port::kActive, Port::kUnused); 1021 // Linearization point for port creation 1022 1023 if (oldState != Port::kUnused) { 1024 // Nobody is allowed to tamper with the port before it's active. 1025 panic("Port state was modified during creation!\n"); 1026 } 1027 1028 TRACE(("create_port() done: port created %ld\n", id)); 1029 1030 sNotificationService.Notify(PORT_ADDED, id); 1031 return id; 1032 } 1033 1034 1035 status_t 1036 close_port(port_id id) 1037 { 1038 TRACE(("close_port(id = %ld)\n", id)); 1039 1040 if (!sPortsActive || id < 0) 1041 return B_BAD_PORT_ID; 1042 1043 // get the port 1044 BReference<Port> portRef = get_locked_port(id); 1045 if (portRef == NULL) { 1046 TRACE(("close_port: invalid port_id %ld\n", id)); 1047 return B_BAD_PORT_ID; 1048 } 1049 MutexLocker lock(&portRef->lock, true); 1050 1051 // mark port to disable writing - deleting the semaphores will 1052 // wake up waiting read/writes 1053 portRef->capacity = 0; 1054 1055 notify_port_select_events(portRef, B_EVENT_INVALID); 1056 portRef->select_infos = NULL; 1057 1058 portRef->read_condition.NotifyAll(B_BAD_PORT_ID); 1059 portRef->write_condition.NotifyAll(B_BAD_PORT_ID); 1060 1061 return B_OK; 1062 } 1063 1064 1065 status_t 1066 delete_port(port_id id) 1067 { 1068 TRACE(("delete_port(id = %ld)\n", id)); 1069 1070 if (!sPortsActive || id < 0) 1071 return B_BAD_PORT_ID; 1072 1073 BReference<Port> portRef = get_port(id); 1074 1075 if (portRef == NULL) { 1076 TRACE(("delete_port: invalid port_id %ld\n", id)); 1077 return B_BAD_PORT_ID; 1078 } 1079 1080 status_t status = delete_port_logical(portRef); 1081 // Contains linearization point 1082 if (status != B_OK) 1083 return status; 1084 1085 // Now remove port physically: 1086 // (1/2) Remove from hash tables 1087 { 1088 WriteLocker portsLocker(sPortsLock); 1089 1090 sPorts.Remove(portRef); 1091 sPortsByName.Remove(portRef); 1092 1093 portRef->ReleaseReference(); 1094 // joint reference for sPorts and sPortsByName 1095 } 1096 1097 // (2/2) Remove from team port list 1098 { 1099 const uint8 lockIndex = portRef->owner % kTeamListLockCount; 1100 MutexLocker teamPortsListLocker(sTeamListLock[lockIndex]); 1101 1102 list_remove_link(&portRef->team_link); 1103 portRef->ReleaseReference(); 1104 } 1105 1106 uninit_port(portRef); 1107 1108 T(Delete(portRef)); 1109 1110 atomic_add(&sUsedPorts, -1); 1111 1112 return B_OK; 1113 } 1114 1115 1116 status_t 1117 select_port(int32 id, struct select_info* info, bool kernel) 1118 { 1119 if (id < 0) 1120 return B_BAD_PORT_ID; 1121 1122 // get the port 1123 BReference<Port> portRef = get_locked_port(id); 1124 if (portRef == NULL) 1125 return B_BAD_PORT_ID; 1126 MutexLocker locker(portRef->lock, true); 1127 1128 // port must not yet be closed 1129 if (is_port_closed(portRef)) 1130 return B_BAD_PORT_ID; 1131 1132 if (!kernel && portRef->owner == team_get_kernel_team_id()) { 1133 // kernel port, but call from userland 1134 return B_NOT_ALLOWED; 1135 } 1136 1137 info->selected_events &= B_EVENT_READ | B_EVENT_WRITE | B_EVENT_INVALID; 1138 1139 if (info->selected_events != 0) { 1140 uint16 events = 0; 1141 1142 info->next = portRef->select_infos; 1143 portRef->select_infos = info; 1144 1145 // check for events 1146 if ((info->selected_events & B_EVENT_READ) != 0 1147 && !portRef->messages.IsEmpty()) { 1148 events |= B_EVENT_READ; 1149 } 1150 1151 if (portRef->write_count > 0) 1152 events |= B_EVENT_WRITE; 1153 1154 if (events != 0) 1155 notify_select_events(info, events); 1156 } 1157 1158 return B_OK; 1159 } 1160 1161 1162 status_t 1163 deselect_port(int32 id, struct select_info* info, bool kernel) 1164 { 1165 if (id < 0) 1166 return B_BAD_PORT_ID; 1167 if (info->selected_events == 0) 1168 return B_OK; 1169 1170 // get the port 1171 BReference<Port> portRef = get_locked_port(id); 1172 if (portRef == NULL) 1173 return B_BAD_PORT_ID; 1174 MutexLocker locker(portRef->lock, true); 1175 1176 // find and remove the infos 1177 select_info** infoLocation = &portRef->select_infos; 1178 while (*infoLocation != NULL && *infoLocation != info) 1179 infoLocation = &(*infoLocation)->next; 1180 1181 if (*infoLocation == info) 1182 *infoLocation = info->next; 1183 1184 return B_OK; 1185 } 1186 1187 1188 port_id 1189 find_port(const char* name) 1190 { 1191 TRACE(("find_port(name = \"%s\")\n", name)); 1192 1193 if (!sPortsActive) { 1194 panic("ports used too early!\n"); 1195 return B_NAME_NOT_FOUND; 1196 } 1197 if (name == NULL) 1198 return B_BAD_VALUE; 1199 1200 ReadLocker locker(sPortsLock); 1201 Port* port = sPortsByName.Lookup(name); 1202 // Since we have sPortsLock and don't return the port itself, 1203 // no BReference necessary 1204 1205 if (port != NULL && port->state == Port::kActive) 1206 return port->id; 1207 1208 return B_NAME_NOT_FOUND; 1209 } 1210 1211 1212 status_t 1213 _get_port_info(port_id id, port_info* info, size_t size) 1214 { 1215 TRACE(("get_port_info(id = %ld)\n", id)); 1216 1217 if (info == NULL || size != sizeof(port_info)) 1218 return B_BAD_VALUE; 1219 if (!sPortsActive || id < 0) 1220 return B_BAD_PORT_ID; 1221 1222 // get the port 1223 BReference<Port> portRef = get_locked_port(id); 1224 if (portRef == NULL) { 1225 TRACE(("get_port_info: invalid port_id %ld\n", id)); 1226 return B_BAD_PORT_ID; 1227 } 1228 MutexLocker locker(portRef->lock, true); 1229 1230 // fill a port_info struct with info 1231 fill_port_info(portRef, info, size); 1232 return B_OK; 1233 } 1234 1235 1236 status_t 1237 _get_next_port_info(team_id teamID, int32* _cookie, struct port_info* info, 1238 size_t size) 1239 { 1240 TRACE(("get_next_port_info(team = %ld)\n", teamID)); 1241 1242 if (info == NULL || size != sizeof(port_info) || _cookie == NULL 1243 || teamID < 0) { 1244 return B_BAD_VALUE; 1245 } 1246 if (!sPortsActive) 1247 return B_BAD_PORT_ID; 1248 1249 Team* team = Team::Get(teamID); 1250 if (team == NULL) 1251 return B_BAD_TEAM_ID; 1252 BReference<Team> teamReference(team, true); 1253 1254 // iterate through the team's port list 1255 const uint8 lockIndex = teamID % kTeamListLockCount; 1256 MutexLocker teamPortsListLocker(sTeamListLock[lockIndex]); 1257 1258 int32 stopIndex = *_cookie; 1259 int32 index = 0; 1260 1261 Port* port = (Port*)list_get_first_item(&team->port_list); 1262 while (port != NULL) { 1263 if (!is_port_closed(port)) { 1264 if (index == stopIndex) 1265 break; 1266 index++; 1267 } 1268 1269 port = (Port*)list_get_next_item(&team->port_list, port); 1270 } 1271 1272 if (port == NULL) 1273 return B_BAD_PORT_ID; 1274 1275 // fill in the port info 1276 BReference<Port> portRef = port; 1277 teamPortsListLocker.Unlock(); 1278 // Only use portRef below this line... 1279 1280 MutexLocker locker(portRef->lock); 1281 fill_port_info(portRef, info, size); 1282 1283 *_cookie = stopIndex + 1; 1284 return B_OK; 1285 } 1286 1287 1288 ssize_t 1289 port_buffer_size(port_id id) 1290 { 1291 return port_buffer_size_etc(id, 0, 0); 1292 } 1293 1294 1295 ssize_t 1296 port_buffer_size_etc(port_id id, uint32 flags, bigtime_t timeout) 1297 { 1298 port_message_info info; 1299 status_t error = get_port_message_info_etc(id, &info, flags, timeout); 1300 return error != B_OK ? error : info.size; 1301 } 1302 1303 1304 status_t 1305 _get_port_message_info_etc(port_id id, port_message_info* info, 1306 size_t infoSize, uint32 flags, bigtime_t timeout) 1307 { 1308 if (info == NULL || infoSize != sizeof(port_message_info)) 1309 return B_BAD_VALUE; 1310 if (!sPortsActive || id < 0) 1311 return B_BAD_PORT_ID; 1312 1313 flags &= B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT | B_RELATIVE_TIMEOUT 1314 | B_ABSOLUTE_TIMEOUT; 1315 1316 // get the port 1317 BReference<Port> portRef = get_locked_port(id); 1318 if (portRef == NULL) 1319 return B_BAD_PORT_ID; 1320 MutexLocker locker(portRef->lock, true); 1321 1322 if (is_port_closed(portRef) && portRef->messages.IsEmpty()) { 1323 T(Info(portRef, 0, B_BAD_PORT_ID)); 1324 TRACE(("_get_port_message_info_etc(): closed port %ld\n", id)); 1325 return B_BAD_PORT_ID; 1326 } 1327 1328 while (portRef->read_count == 0) { 1329 // We need to wait for a message to appear 1330 if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0) 1331 return B_WOULD_BLOCK; 1332 1333 ConditionVariableEntry entry; 1334 portRef->read_condition.Add(&entry); 1335 1336 locker.Unlock(); 1337 1338 // block if no message, or, if B_TIMEOUT flag set, block with timeout 1339 status_t status = entry.Wait(flags, timeout); 1340 1341 if (status != B_OK) { 1342 T(Info(port, 0, status)); 1343 return status; 1344 } 1345 1346 // re-lock 1347 BReference<Port> newPortRef = get_locked_port(id); 1348 if (newPortRef == NULL) { 1349 T(Info(id, 0, 0, 0, B_BAD_PORT_ID)); 1350 return B_BAD_PORT_ID; 1351 } 1352 locker.SetTo(newPortRef->lock, true); 1353 1354 if (newPortRef != portRef 1355 || (is_port_closed(portRef) && portRef->messages.IsEmpty())) { 1356 // the port is no longer there 1357 T(Info(id, 0, 0, 0, B_BAD_PORT_ID)); 1358 return B_BAD_PORT_ID; 1359 } 1360 } 1361 1362 // determine tail & get the length of the message 1363 port_message* message = portRef->messages.Head(); 1364 if (message == NULL) { 1365 panic("port %" B_PRId32 ": no messages found\n", portRef->id); 1366 return B_ERROR; 1367 } 1368 1369 info->size = message->size; 1370 info->sender = message->sender; 1371 info->sender_group = message->sender_group; 1372 info->sender_team = message->sender_team; 1373 1374 T(Info(id, id->read_count, id->write_count, message->code, B_OK)); 1375 1376 // notify next one, as we haven't read from the port 1377 portRef->read_condition.NotifyOne(); 1378 1379 return B_OK; 1380 } 1381 1382 1383 ssize_t 1384 port_count(port_id id) 1385 { 1386 if (!sPortsActive || id < 0) 1387 return B_BAD_PORT_ID; 1388 1389 // get the port 1390 BReference<Port> portRef = get_locked_port(id); 1391 if (portRef == NULL) { 1392 TRACE(("port_count: invalid port_id %ld\n", id)); 1393 return B_BAD_PORT_ID; 1394 } 1395 MutexLocker locker(portRef->lock, true); 1396 1397 // return count of messages 1398 return portRef->read_count; 1399 } 1400 1401 1402 ssize_t 1403 read_port(port_id port, int32* msgCode, void* buffer, size_t bufferSize) 1404 { 1405 return read_port_etc(port, msgCode, buffer, bufferSize, 0, 0); 1406 } 1407 1408 1409 ssize_t 1410 read_port_etc(port_id id, int32* _code, void* buffer, size_t bufferSize, 1411 uint32 flags, bigtime_t timeout) 1412 { 1413 if (!sPortsActive || id < 0) 1414 return B_BAD_PORT_ID; 1415 if ((buffer == NULL && bufferSize > 0) || timeout < 0) 1416 return B_BAD_VALUE; 1417 1418 bool userCopy = (flags & PORT_FLAG_USE_USER_MEMCPY) != 0; 1419 bool peekOnly = !userCopy && (flags & B_PEEK_PORT_MESSAGE) != 0; 1420 // TODO: we could allow peeking for user apps now 1421 1422 flags &= B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT | B_RELATIVE_TIMEOUT 1423 | B_ABSOLUTE_TIMEOUT; 1424 1425 // get the port 1426 BReference<Port> portRef = get_locked_port(id); 1427 if (portRef == NULL) 1428 return B_BAD_PORT_ID; 1429 MutexLocker locker(portRef->lock, true); 1430 1431 if (is_port_closed(portRef) && portRef->messages.IsEmpty()) { 1432 T(Read(portRef, 0, B_BAD_PORT_ID)); 1433 TRACE(("read_port_etc(): closed port %ld\n", id)); 1434 return B_BAD_PORT_ID; 1435 } 1436 1437 while (portRef->read_count == 0) { 1438 if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0) 1439 return B_WOULD_BLOCK; 1440 1441 // We need to wait for a message to appear 1442 ConditionVariableEntry entry; 1443 portRef->read_condition.Add(&entry); 1444 1445 locker.Unlock(); 1446 1447 // block if no message, or, if B_TIMEOUT flag set, block with timeout 1448 status_t status = entry.Wait(flags, timeout); 1449 1450 // re-lock 1451 BReference<Port> newPortRef = get_locked_port(id); 1452 if (newPortRef == NULL) { 1453 T(Read(id, 0, 0, 0, B_BAD_PORT_ID)); 1454 return B_BAD_PORT_ID; 1455 } 1456 locker.SetTo(newPortRef->lock, true); 1457 1458 if (newPortRef != portRef 1459 || (is_port_closed(portRef) && portRef->messages.IsEmpty())) { 1460 // the port is no longer there 1461 T(Read(id, 0, 0, 0, B_BAD_PORT_ID)); 1462 return B_BAD_PORT_ID; 1463 } 1464 1465 if (status != B_OK) { 1466 T(Read(portRef, 0, status)); 1467 return status; 1468 } 1469 } 1470 1471 // determine tail & get the length of the message 1472 port_message* message = portRef->messages.Head(); 1473 if (message == NULL) { 1474 panic("port %" B_PRId32 ": no messages found\n", portRef->id); 1475 return B_ERROR; 1476 } 1477 1478 if (peekOnly) { 1479 size_t size = copy_port_message(message, _code, buffer, bufferSize, 1480 userCopy); 1481 1482 T(Read(portRef, message->code, size)); 1483 1484 portRef->read_condition.NotifyOne(); 1485 // we only peeked, but didn't grab the message 1486 return size; 1487 } 1488 1489 portRef->messages.RemoveHead(); 1490 portRef->total_count++; 1491 portRef->write_count++; 1492 portRef->read_count--; 1493 1494 notify_port_select_events(portRef, B_EVENT_WRITE); 1495 portRef->write_condition.NotifyOne(); 1496 // make one spot in queue available again for write 1497 1498 T(Read(id, portRef->read_count, portRef->write_count, message->code, 1499 min_c(bufferSize, message->size))); 1500 1501 locker.Unlock(); 1502 1503 size_t size = copy_port_message(message, _code, buffer, bufferSize, 1504 userCopy); 1505 1506 put_port_message(message); 1507 return size; 1508 } 1509 1510 1511 status_t 1512 write_port(port_id id, int32 msgCode, const void* buffer, size_t bufferSize) 1513 { 1514 iovec vec = { (void*)buffer, bufferSize }; 1515 1516 return writev_port_etc(id, msgCode, &vec, 1, bufferSize, 0, 0); 1517 } 1518 1519 1520 status_t 1521 write_port_etc(port_id id, int32 msgCode, const void* buffer, 1522 size_t bufferSize, uint32 flags, bigtime_t timeout) 1523 { 1524 iovec vec = { (void*)buffer, bufferSize }; 1525 1526 return writev_port_etc(id, msgCode, &vec, 1, bufferSize, flags, timeout); 1527 } 1528 1529 1530 status_t 1531 writev_port_etc(port_id id, int32 msgCode, const iovec* msgVecs, 1532 size_t vecCount, size_t bufferSize, uint32 flags, bigtime_t timeout) 1533 { 1534 if (!sPortsActive || id < 0) 1535 return B_BAD_PORT_ID; 1536 if (bufferSize > PORT_MAX_MESSAGE_SIZE) 1537 return B_BAD_VALUE; 1538 1539 // mask irrelevant flags (for acquire_sem() usage) 1540 flags &= B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT | B_RELATIVE_TIMEOUT 1541 | B_ABSOLUTE_TIMEOUT; 1542 if ((flags & B_RELATIVE_TIMEOUT) != 0 1543 && timeout != B_INFINITE_TIMEOUT && timeout > 0) { 1544 // Make the timeout absolute, since we have more than one step where 1545 // we might have to wait 1546 flags = (flags & ~B_RELATIVE_TIMEOUT) | B_ABSOLUTE_TIMEOUT; 1547 timeout += system_time(); 1548 } 1549 1550 bool userCopy = (flags & PORT_FLAG_USE_USER_MEMCPY) > 0; 1551 1552 status_t status; 1553 port_message* message = NULL; 1554 1555 // get the port 1556 BReference<Port> portRef = get_locked_port(id); 1557 if (portRef == NULL) { 1558 TRACE(("write_port_etc: invalid port_id %ld\n", id)); 1559 return B_BAD_PORT_ID; 1560 } 1561 MutexLocker locker(portRef->lock, true); 1562 1563 if (is_port_closed(portRef)) { 1564 TRACE(("write_port_etc: port %ld closed\n", id)); 1565 return B_BAD_PORT_ID; 1566 } 1567 1568 if (portRef->write_count <= 0) { 1569 if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0) 1570 return B_WOULD_BLOCK; 1571 1572 portRef->write_count--; 1573 1574 // We need to block in order to wait for a free message slot 1575 ConditionVariableEntry entry; 1576 portRef->write_condition.Add(&entry); 1577 1578 locker.Unlock(); 1579 1580 status = entry.Wait(flags, timeout); 1581 1582 // re-lock 1583 BReference<Port> newPortRef = get_locked_port(id); 1584 if (newPortRef == NULL) { 1585 T(Write(id, 0, 0, 0, 0, B_BAD_PORT_ID)); 1586 return B_BAD_PORT_ID; 1587 } 1588 locker.SetTo(newPortRef->lock, true); 1589 1590 if (newPortRef != portRef || is_port_closed(portRef)) { 1591 // the port is no longer there 1592 T(Write(id, 0, 0, 0, 0, B_BAD_PORT_ID)); 1593 return B_BAD_PORT_ID; 1594 } 1595 1596 if (status != B_OK) 1597 goto error; 1598 } else 1599 portRef->write_count--; 1600 1601 status = get_port_message(msgCode, bufferSize, flags, timeout, 1602 &message, *portRef); 1603 if (status != B_OK) { 1604 if (status == B_BAD_PORT_ID) { 1605 // the port had to be unlocked and is now no longer there 1606 T(Write(id, 0, 0, 0, 0, B_BAD_PORT_ID)); 1607 return B_BAD_PORT_ID; 1608 } 1609 1610 goto error; 1611 } 1612 1613 // sender credentials 1614 message->sender = geteuid(); 1615 message->sender_group = getegid(); 1616 message->sender_team = team_get_current_team_id(); 1617 1618 if (bufferSize > 0) { 1619 size_t offset = 0; 1620 for (uint32 i = 0; i < vecCount; i++) { 1621 size_t bytes = msgVecs[i].iov_len; 1622 if (bytes > bufferSize) 1623 bytes = bufferSize; 1624 1625 if (userCopy) { 1626 status_t status = user_memcpy(message->buffer + offset, 1627 msgVecs[i].iov_base, bytes); 1628 if (status != B_OK) { 1629 put_port_message(message); 1630 goto error; 1631 } 1632 } else 1633 memcpy(message->buffer + offset, msgVecs[i].iov_base, bytes); 1634 1635 bufferSize -= bytes; 1636 if (bufferSize == 0) 1637 break; 1638 1639 offset += bytes; 1640 } 1641 } 1642 1643 portRef->messages.Add(message); 1644 portRef->read_count++; 1645 1646 T(Write(id, portRef->read_count, portRef->write_count, message->code, 1647 message->size, B_OK)); 1648 1649 notify_port_select_events(portRef, B_EVENT_READ); 1650 portRef->read_condition.NotifyOne(); 1651 return B_OK; 1652 1653 error: 1654 // Give up our slot in the queue again, and let someone else 1655 // try and fail 1656 T(Write(id, portRef->read_count, portRef->write_count, 0, 0, status)); 1657 portRef->write_count++; 1658 notify_port_select_events(portRef, B_EVENT_WRITE); 1659 portRef->write_condition.NotifyOne(); 1660 1661 return status; 1662 } 1663 1664 1665 status_t 1666 set_port_owner(port_id id, team_id newTeamID) 1667 { 1668 TRACE(("set_port_owner(id = %ld, team = %ld)\n", id, newTeamID)); 1669 1670 if (id < 0) 1671 return B_BAD_PORT_ID; 1672 1673 // get the new team 1674 Team* team = Team::Get(newTeamID); 1675 if (team == NULL) 1676 return B_BAD_TEAM_ID; 1677 BReference<Team> teamReference(team, true); 1678 1679 // get the port 1680 BReference<Port> portRef = get_locked_port(id); 1681 if (portRef == NULL) { 1682 TRACE(("set_port_owner: invalid port_id %ld\n", id)); 1683 return B_BAD_PORT_ID; 1684 } 1685 MutexLocker locker(portRef->lock, true); 1686 1687 // transfer ownership to other team 1688 if (team->id != portRef->owner) { 1689 uint8 firstLockIndex = portRef->owner % kTeamListLockCount; 1690 uint8 secondLockIndex = team->id % kTeamListLockCount; 1691 1692 // Avoid deadlocks: always lock lower index first 1693 if (secondLockIndex < firstLockIndex) { 1694 uint8 temp = secondLockIndex; 1695 secondLockIndex = firstLockIndex; 1696 firstLockIndex = temp; 1697 } 1698 1699 MutexLocker oldTeamPortsListLocker(sTeamListLock[firstLockIndex]); 1700 MutexLocker newTeamPortsListLocker; 1701 if (firstLockIndex != secondLockIndex) { 1702 newTeamPortsListLocker.SetTo(sTeamListLock[secondLockIndex], 1703 false); 1704 } 1705 1706 // Now that we have locked the team port lists, check the state again 1707 if (portRef->state == Port::kActive) { 1708 list_remove_link(&portRef->team_link); 1709 list_add_item(&team->port_list, portRef.Get()); 1710 portRef->owner = team->id; 1711 } else { 1712 // Port was already deleted. We haven't changed anything yet so 1713 // we can cancel the operation. 1714 return B_BAD_PORT_ID; 1715 } 1716 } 1717 1718 T(OwnerChange(portRef, team->id, B_OK)); 1719 return B_OK; 1720 } 1721 1722 1723 // #pragma mark - syscalls 1724 1725 1726 port_id 1727 _user_create_port(int32 queueLength, const char *userName) 1728 { 1729 char name[B_OS_NAME_LENGTH]; 1730 1731 if (userName == NULL) 1732 return create_port(queueLength, NULL); 1733 1734 if (!IS_USER_ADDRESS(userName) 1735 || user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK) 1736 return B_BAD_ADDRESS; 1737 1738 return create_port(queueLength, name); 1739 } 1740 1741 1742 status_t 1743 _user_close_port(port_id id) 1744 { 1745 return close_port(id); 1746 } 1747 1748 1749 status_t 1750 _user_delete_port(port_id id) 1751 { 1752 return delete_port(id); 1753 } 1754 1755 1756 port_id 1757 _user_find_port(const char *userName) 1758 { 1759 char name[B_OS_NAME_LENGTH]; 1760 1761 if (userName == NULL) 1762 return B_BAD_VALUE; 1763 if (!IS_USER_ADDRESS(userName) 1764 || user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK) 1765 return B_BAD_ADDRESS; 1766 1767 return find_port(name); 1768 } 1769 1770 1771 status_t 1772 _user_get_port_info(port_id id, struct port_info *userInfo) 1773 { 1774 struct port_info info; 1775 status_t status; 1776 1777 if (userInfo == NULL) 1778 return B_BAD_VALUE; 1779 if (!IS_USER_ADDRESS(userInfo)) 1780 return B_BAD_ADDRESS; 1781 1782 status = get_port_info(id, &info); 1783 1784 // copy back to user space 1785 if (status == B_OK 1786 && user_memcpy(userInfo, &info, sizeof(struct port_info)) < B_OK) 1787 return B_BAD_ADDRESS; 1788 1789 return status; 1790 } 1791 1792 1793 status_t 1794 _user_get_next_port_info(team_id team, int32 *userCookie, 1795 struct port_info *userInfo) 1796 { 1797 struct port_info info; 1798 status_t status; 1799 int32 cookie; 1800 1801 if (userCookie == NULL || userInfo == NULL) 1802 return B_BAD_VALUE; 1803 if (!IS_USER_ADDRESS(userCookie) || !IS_USER_ADDRESS(userInfo) 1804 || user_memcpy(&cookie, userCookie, sizeof(int32)) < B_OK) 1805 return B_BAD_ADDRESS; 1806 1807 status = get_next_port_info(team, &cookie, &info); 1808 1809 // copy back to user space 1810 if (user_memcpy(userCookie, &cookie, sizeof(int32)) < B_OK 1811 || (status == B_OK && user_memcpy(userInfo, &info, 1812 sizeof(struct port_info)) < B_OK)) 1813 return B_BAD_ADDRESS; 1814 1815 return status; 1816 } 1817 1818 1819 ssize_t 1820 _user_port_buffer_size_etc(port_id port, uint32 flags, bigtime_t timeout) 1821 { 1822 syscall_restart_handle_timeout_pre(flags, timeout); 1823 1824 status_t status = port_buffer_size_etc(port, flags | B_CAN_INTERRUPT, 1825 timeout); 1826 1827 return syscall_restart_handle_timeout_post(status, timeout); 1828 } 1829 1830 1831 ssize_t 1832 _user_port_count(port_id port) 1833 { 1834 return port_count(port); 1835 } 1836 1837 1838 status_t 1839 _user_set_port_owner(port_id port, team_id team) 1840 { 1841 return set_port_owner(port, team); 1842 } 1843 1844 1845 ssize_t 1846 _user_read_port_etc(port_id port, int32 *userCode, void *userBuffer, 1847 size_t bufferSize, uint32 flags, bigtime_t timeout) 1848 { 1849 int32 messageCode; 1850 ssize_t bytesRead; 1851 1852 syscall_restart_handle_timeout_pre(flags, timeout); 1853 1854 if (userBuffer == NULL && bufferSize != 0) 1855 return B_BAD_VALUE; 1856 if ((userCode != NULL && !IS_USER_ADDRESS(userCode)) 1857 || (userBuffer != NULL && !IS_USER_ADDRESS(userBuffer))) 1858 return B_BAD_ADDRESS; 1859 1860 bytesRead = read_port_etc(port, &messageCode, userBuffer, bufferSize, 1861 flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, timeout); 1862 1863 if (bytesRead >= 0 && userCode != NULL 1864 && user_memcpy(userCode, &messageCode, sizeof(int32)) < B_OK) 1865 return B_BAD_ADDRESS; 1866 1867 return syscall_restart_handle_timeout_post(bytesRead, timeout); 1868 } 1869 1870 1871 status_t 1872 _user_write_port_etc(port_id port, int32 messageCode, const void *userBuffer, 1873 size_t bufferSize, uint32 flags, bigtime_t timeout) 1874 { 1875 iovec vec = { (void *)userBuffer, bufferSize }; 1876 1877 syscall_restart_handle_timeout_pre(flags, timeout); 1878 1879 if (userBuffer == NULL && bufferSize != 0) 1880 return B_BAD_VALUE; 1881 if (userBuffer != NULL && !IS_USER_ADDRESS(userBuffer)) 1882 return B_BAD_ADDRESS; 1883 1884 status_t status = writev_port_etc(port, messageCode, &vec, 1, bufferSize, 1885 flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, timeout); 1886 1887 return syscall_restart_handle_timeout_post(status, timeout); 1888 } 1889 1890 1891 status_t 1892 _user_writev_port_etc(port_id port, int32 messageCode, const iovec *userVecs, 1893 size_t vecCount, size_t bufferSize, uint32 flags, bigtime_t timeout) 1894 { 1895 syscall_restart_handle_timeout_pre(flags, timeout); 1896 1897 if (userVecs == NULL && bufferSize != 0) 1898 return B_BAD_VALUE; 1899 if (userVecs != NULL && !IS_USER_ADDRESS(userVecs)) 1900 return B_BAD_ADDRESS; 1901 1902 iovec *vecs = NULL; 1903 if (userVecs && vecCount != 0) { 1904 vecs = (iovec*)malloc(sizeof(iovec) * vecCount); 1905 if (vecs == NULL) 1906 return B_NO_MEMORY; 1907 1908 if (user_memcpy(vecs, userVecs, sizeof(iovec) * vecCount) < B_OK) { 1909 free(vecs); 1910 return B_BAD_ADDRESS; 1911 } 1912 } 1913 1914 status_t status = writev_port_etc(port, messageCode, vecs, vecCount, 1915 bufferSize, flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, 1916 timeout); 1917 1918 free(vecs); 1919 return syscall_restart_handle_timeout_post(status, timeout); 1920 } 1921 1922 1923 status_t 1924 _user_get_port_message_info_etc(port_id port, port_message_info *userInfo, 1925 size_t infoSize, uint32 flags, bigtime_t timeout) 1926 { 1927 if (userInfo == NULL || infoSize != sizeof(port_message_info)) 1928 return B_BAD_VALUE; 1929 1930 syscall_restart_handle_timeout_pre(flags, timeout); 1931 1932 port_message_info info; 1933 status_t error = _get_port_message_info_etc(port, &info, sizeof(info), 1934 flags | B_CAN_INTERRUPT, timeout); 1935 1936 // copy info to userland 1937 if (error == B_OK && (!IS_USER_ADDRESS(userInfo) 1938 || user_memcpy(userInfo, &info, sizeof(info)) != B_OK)) { 1939 error = B_BAD_ADDRESS; 1940 } 1941 1942 return syscall_restart_handle_timeout_post(error, timeout); 1943 } 1944