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