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