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