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, 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(&lock, 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 free((char*)lock.name); 159 lock.name = NULL; 160 } 161 }; 162 163 164 struct PortHashDefinition { 165 typedef port_id KeyType; 166 typedef Port ValueType; 167 168 size_t HashKey(port_id key) const 169 { 170 return key; 171 } 172 173 size_t Hash(Port* value) const 174 { 175 return HashKey(value->id); 176 } 177 178 bool Compare(port_id key, Port* value) const 179 { 180 return value->id == key; 181 } 182 183 Port*& GetLink(Port* value) const 184 { 185 return value->hash_link; 186 } 187 }; 188 189 typedef BOpenHashTable<PortHashDefinition> PortHashTable; 190 191 192 struct PortNameHashDefinition { 193 typedef const char* KeyType; 194 typedef Port ValueType; 195 196 size_t HashKey(const char* key) const 197 { 198 // Hash function: hash(key) = key[0] * 31^(length - 1) 199 // + key[1] * 31^(length - 2) + ... + key[length - 1] 200 201 const size_t length = strlen(key); 202 203 size_t hash = 0; 204 for (size_t index = 0; index < length; index++) 205 hash = 31 * hash + key[index]; 206 207 return hash; 208 } 209 210 size_t Hash(Port* value) const 211 { 212 size_t& hash = value->name_hash; 213 if (hash == 0) 214 hash = HashKey(value->lock.name); 215 return hash; 216 } 217 218 bool Compare(const char* key, Port* value) const 219 { 220 return (strcmp(key, value->lock.name) == 0); 221 } 222 223 Port*& GetLink(Port* value) const 224 { 225 return value->name_hash_link; 226 } 227 }; 228 229 typedef BOpenHashTable<PortNameHashDefinition> PortNameHashTable; 230 231 232 class PortNotificationService : public DefaultNotificationService { 233 public: 234 PortNotificationService(); 235 236 void Notify(uint32 opcode, port_id team); 237 }; 238 239 } // namespace 240 241 242 // #pragma mark - tracing 243 244 245 #if PORT_TRACING 246 namespace PortTracing { 247 248 class Create : public AbstractTraceEntry { 249 public: 250 Create(Port* port) 251 : 252 fID(port->id), 253 fOwner(port->owner), 254 fCapacity(port->capacity) 255 { 256 fName = alloc_tracing_buffer_strcpy(port->lock.name, B_OS_NAME_LENGTH, 257 false); 258 259 Initialized(); 260 } 261 262 virtual void AddDump(TraceOutput& out) 263 { 264 out.Print("port %ld created, name \"%s\", owner %ld, capacity %ld", 265 fID, fName, fOwner, fCapacity); 266 } 267 268 private: 269 port_id fID; 270 char* fName; 271 team_id fOwner; 272 int32 fCapacity; 273 }; 274 275 276 class Delete : public AbstractTraceEntry { 277 public: 278 Delete(Port* port) 279 : 280 fID(port->id) 281 { 282 Initialized(); 283 } 284 285 virtual void AddDump(TraceOutput& out) 286 { 287 out.Print("port %ld deleted", fID); 288 } 289 290 private: 291 port_id fID; 292 }; 293 294 295 class Read : public AbstractTraceEntry { 296 public: 297 Read(const BReference<Port>& portRef, int32 code, ssize_t result) 298 : 299 fID(portRef->id), 300 fReadCount(portRef->read_count), 301 fWriteCount(portRef->write_count), 302 fCode(code), 303 fResult(result) 304 { 305 Initialized(); 306 } 307 308 Read(port_id id, int32 readCount, int32 writeCount, int32 code, 309 ssize_t result) 310 : 311 fID(id), 312 fReadCount(readCount), 313 fWriteCount(writeCount), 314 fCode(code), 315 fResult(result) 316 { 317 Initialized(); 318 } 319 320 virtual void AddDump(TraceOutput& out) 321 { 322 out.Print("port %ld read, read %ld, write %ld, code %lx: %ld", 323 fID, fReadCount, fWriteCount, fCode, fResult); 324 } 325 326 private: 327 port_id fID; 328 int32 fReadCount; 329 int32 fWriteCount; 330 int32 fCode; 331 ssize_t fResult; 332 }; 333 334 335 class Write : public AbstractTraceEntry { 336 public: 337 Write(port_id id, int32 readCount, int32 writeCount, int32 code, 338 size_t bufferSize, ssize_t result) 339 : 340 fID(id), 341 fReadCount(readCount), 342 fWriteCount(writeCount), 343 fCode(code), 344 fBufferSize(bufferSize), 345 fResult(result) 346 { 347 Initialized(); 348 } 349 350 virtual void AddDump(TraceOutput& out) 351 { 352 out.Print("port %ld write, read %ld, write %ld, code %lx, size %ld: %ld", 353 fID, fReadCount, fWriteCount, fCode, fBufferSize, fResult); 354 } 355 356 private: 357 port_id fID; 358 int32 fReadCount; 359 int32 fWriteCount; 360 int32 fCode; 361 size_t fBufferSize; 362 ssize_t fResult; 363 }; 364 365 366 class Info : public AbstractTraceEntry { 367 public: 368 Info(const BReference<Port>& portRef, int32 code, ssize_t result) 369 : 370 fID(portRef->id), 371 fReadCount(portRef->read_count), 372 fWriteCount(portRef->write_count), 373 fCode(code), 374 fResult(result) 375 { 376 Initialized(); 377 } 378 379 Info(port_id id, int32 readCount, int32 writeCount, int32 code, 380 ssize_t result) 381 : 382 fID(id), 383 fReadCount(readCount), 384 fWriteCount(writeCount), 385 fCode(code), 386 fResult(result) 387 { 388 Initialized(); 389 } 390 391 virtual void AddDump(TraceOutput& out) 392 { 393 out.Print("port %ld info, read %ld, write %ld, code %lx: %ld", 394 fID, fReadCount, fWriteCount, fCode, fResult); 395 } 396 397 private: 398 port_id fID; 399 int32 fReadCount; 400 int32 fWriteCount; 401 int32 fCode; 402 ssize_t fResult; 403 }; 404 405 406 class OwnerChange : public AbstractTraceEntry { 407 public: 408 OwnerChange(Port* port, team_id newOwner, status_t status) 409 : 410 fID(port->id), 411 fOldOwner(port->owner), 412 fNewOwner(newOwner), 413 fStatus(status) 414 { 415 Initialized(); 416 } 417 418 virtual void AddDump(TraceOutput& out) 419 { 420 out.Print("port %ld owner change from %ld to %ld: %s", fID, fOldOwner, 421 fNewOwner, strerror(fStatus)); 422 } 423 424 private: 425 port_id fID; 426 team_id fOldOwner; 427 team_id fNewOwner; 428 status_t fStatus; 429 }; 430 431 } // namespace PortTracing 432 433 # define T(x) new(std::nothrow) PortTracing::x; 434 #else 435 # define T(x) ; 436 #endif 437 438 439 static const size_t kInitialPortBufferSize = 4 * 1024 * 1024; 440 static const size_t kTotalSpaceLimit = 64 * 1024 * 1024; 441 static const size_t kTeamSpaceLimit = 8 * 1024 * 1024; 442 static const size_t kBufferGrowRate = kInitialPortBufferSize; 443 444 #define MAX_QUEUE_LENGTH 4096 445 #define PORT_MAX_MESSAGE_SIZE (256 * 1024) 446 447 static int32 sMaxPorts = 4096; 448 static int32 sUsedPorts; 449 450 static PortHashTable sPorts; 451 static PortNameHashTable sPortsByName; 452 static ConditionVariable sNoSpaceCondition; 453 static int32 sTotalSpaceCommited; 454 static int32 sWaitingForSpace; 455 static port_id sNextPortID = 1; 456 static bool sPortsActive = false; 457 static rw_lock sPortsLock = RW_LOCK_INITIALIZER("ports list"); 458 459 enum { 460 kTeamListLockCount = 8 461 }; 462 463 static mutex sTeamListLock[kTeamListLockCount] = { 464 MUTEX_INITIALIZER("team ports list 1"), 465 MUTEX_INITIALIZER("team ports list 2"), 466 MUTEX_INITIALIZER("team ports list 3"), 467 MUTEX_INITIALIZER("team ports list 4"), 468 MUTEX_INITIALIZER("team ports list 5"), 469 MUTEX_INITIALIZER("team ports list 6"), 470 MUTEX_INITIALIZER("team ports list 7"), 471 MUTEX_INITIALIZER("team ports list 8") 472 }; 473 474 static PortNotificationService sNotificationService; 475 476 477 // #pragma mark - TeamNotificationService 478 479 480 PortNotificationService::PortNotificationService() 481 : 482 DefaultNotificationService("ports") 483 { 484 } 485 486 487 void 488 PortNotificationService::Notify(uint32 opcode, port_id port) 489 { 490 char eventBuffer[128]; 491 KMessage event; 492 event.SetTo(eventBuffer, sizeof(eventBuffer), PORT_MONITOR); 493 event.AddInt32("event", opcode); 494 event.AddInt32("port", port); 495 496 DefaultNotificationService::Notify(event, opcode); 497 } 498 499 500 // #pragma mark - debugger commands 501 502 503 static int 504 dump_port_list(int argc, char** argv) 505 { 506 const char* name = NULL; 507 team_id owner = -1; 508 509 if (argc > 2) { 510 if (!strcmp(argv[1], "team") || !strcmp(argv[1], "owner")) 511 owner = strtoul(argv[2], NULL, 0); 512 else if (!strcmp(argv[1], "name")) 513 name = argv[2]; 514 } else if (argc > 1) 515 owner = strtoul(argv[1], NULL, 0); 516 517 kprintf("port id cap read-cnt write-cnt total team " 518 "name\n"); 519 520 for (PortHashTable::Iterator it = sPorts.GetIterator(); 521 Port* port = it.Next();) { 522 if ((owner != -1 && port->owner != owner) 523 || (name != NULL && strstr(port->lock.name, name) == NULL)) 524 continue; 525 526 kprintf("%p %8" B_PRId32 " %4" B_PRId32 " %9" B_PRIu32 " %9" B_PRId32 527 " %8" B_PRId32 " %6" B_PRId32 " %s\n", port, port->id, 528 port->capacity, port->read_count, port->write_count, 529 port->total_count, port->owner, port->lock.name); 530 } 531 532 return 0; 533 } 534 535 536 static void 537 _dump_port_info(Port* port) 538 { 539 kprintf("PORT: %p\n", port); 540 kprintf(" id: %" B_PRId32 "\n", port->id); 541 kprintf(" name: \"%s\"\n", port->lock.name); 542 kprintf(" owner: %" B_PRId32 "\n", port->owner); 543 kprintf(" capacity: %" B_PRId32 "\n", port->capacity); 544 kprintf(" read_count: %" B_PRIu32 "\n", port->read_count); 545 kprintf(" write_count: %" B_PRId32 "\n", port->write_count); 546 kprintf(" total count: %" B_PRId32 "\n", port->total_count); 547 548 if (!port->messages.IsEmpty()) { 549 kprintf("messages:\n"); 550 551 MessageList::Iterator iterator = port->messages.GetIterator(); 552 while (port_message* message = iterator.Next()) { 553 kprintf(" %p %08" B_PRIx32 " %ld\n", message, message->code, message->size); 554 } 555 } 556 557 set_debug_variable("_port", (addr_t)port); 558 set_debug_variable("_portID", port->id); 559 set_debug_variable("_owner", port->owner); 560 } 561 562 563 static int 564 dump_port_info(int argc, char** argv) 565 { 566 ConditionVariable* condition = NULL; 567 const char* name = NULL; 568 569 if (argc < 2) { 570 print_debugger_command_usage(argv[0]); 571 return 0; 572 } 573 574 if (argc > 2) { 575 if (!strcmp(argv[1], "address")) { 576 _dump_port_info((Port*)parse_expression(argv[2])); 577 return 0; 578 } else if (!strcmp(argv[1], "condition")) 579 condition = (ConditionVariable*)parse_expression(argv[2]); 580 else if (!strcmp(argv[1], "name")) 581 name = argv[2]; 582 } else if (parse_expression(argv[1]) > 0) { 583 // if the argument looks like a number, treat it as such 584 int32 num = parse_expression(argv[1]); 585 Port* port = sPorts.Lookup(num); 586 if (port == NULL || port->state != Port::kActive) { 587 kprintf("port %" B_PRId32 " (%#" B_PRIx32 ") doesn't exist!\n", 588 num, num); 589 return 0; 590 } 591 _dump_port_info(port); 592 return 0; 593 } else 594 name = argv[1]; 595 596 // walk through the ports list, trying to match name 597 for (PortHashTable::Iterator it = sPorts.GetIterator(); 598 Port* port = it.Next();) { 599 if ((name != NULL && port->lock.name != NULL 600 && !strcmp(name, port->lock.name)) 601 || (condition != NULL && (&port->read_condition == condition 602 || &port->write_condition == condition))) { 603 _dump_port_info(port); 604 return 0; 605 } 606 } 607 608 return 0; 609 } 610 611 612 // #pragma mark - internal helper functions 613 614 615 /*! Notifies the port's select events. 616 The port must be locked. 617 */ 618 static void 619 notify_port_select_events(Port* port, uint16 events) 620 { 621 if (port->select_infos) 622 notify_select_events_list(port->select_infos, events); 623 } 624 625 626 static BReference<Port> 627 get_locked_port(port_id id) GCC_2_NRV(portRef) 628 { 629 #if __GNUC__ >= 3 630 BReference<Port> portRef; 631 #endif 632 { 633 ReadLocker portsLocker(sPortsLock); 634 portRef.SetTo(sPorts.Lookup(id)); 635 } 636 637 if (portRef != NULL && portRef->state == Port::kActive) 638 mutex_lock(&portRef->lock); 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 // check & dup name 992 char* nameBuffer = strdup(name != NULL ? name : "unnamed port"); 993 if (nameBuffer == NULL) 994 return B_NO_MEMORY; 995 996 // create a port 997 Port* port = new(std::nothrow) Port(team_get_current_team_id(), queueLength, 998 nameBuffer); 999 if (port == NULL) { 1000 free(nameBuffer); 1001 return B_NO_MEMORY; 1002 } 1003 1004 // check the ports limit 1005 const int32 previouslyUsed = atomic_add(&sUsedPorts, 1); 1006 if (previouslyUsed + 1 >= sMaxPorts) { 1007 atomic_add(&sUsedPorts, -1); 1008 delete port; 1009 return B_NO_MORE_PORTS; 1010 } 1011 1012 { 1013 WriteLocker locker(sPortsLock); 1014 1015 // allocate a port ID 1016 do { 1017 port->id = sNextPortID++; 1018 1019 // handle integer overflow 1020 if (sNextPortID < 0) 1021 sNextPortID = 1; 1022 } while (sPorts.Lookup(port->id) != NULL); 1023 1024 // Insert port physically: 1025 // (1/2) Insert into hash tables 1026 port->AcquireReference(); 1027 // joint reference for sPorts and sPortsByName 1028 1029 sPorts.Insert(port); 1030 sPortsByName.Insert(port); 1031 } 1032 1033 // (2/2) Insert into team list 1034 { 1035 const uint8 lockIndex = port->owner % kTeamListLockCount; 1036 MutexLocker teamPortsListLocker(sTeamListLock[lockIndex]); 1037 port->AcquireReference(); 1038 list_add_item(&team->port_list, port); 1039 } 1040 1041 // tracing, notifications, etc. 1042 T(Create(port)); 1043 1044 const port_id id = port->id; 1045 1046 // Insert port logically by marking it active 1047 const int32 oldState = atomic_test_and_set(&port->state, 1048 Port::kActive, Port::kUnused); 1049 // Linearization point for port creation 1050 1051 if (oldState != Port::kUnused) { 1052 // Nobody is allowed to tamper with the port before it's active. 1053 panic("Port state was modified during creation!\n"); 1054 } 1055 1056 TRACE(("create_port() done: port created %ld\n", id)); 1057 1058 sNotificationService.Notify(PORT_ADDED, id); 1059 return id; 1060 } 1061 1062 1063 status_t 1064 close_port(port_id id) 1065 { 1066 TRACE(("close_port(id = %ld)\n", id)); 1067 1068 if (!sPortsActive || id < 0) 1069 return B_BAD_PORT_ID; 1070 1071 // get the port 1072 BReference<Port> portRef = get_locked_port(id); 1073 if (portRef == NULL) { 1074 TRACE(("close_port: invalid port_id %ld\n", id)); 1075 return B_BAD_PORT_ID; 1076 } 1077 MutexLocker lock(&portRef->lock, true); 1078 1079 // mark port to disable writing - deleting the semaphores will 1080 // wake up waiting read/writes 1081 portRef->capacity = 0; 1082 1083 notify_port_select_events(portRef, B_EVENT_INVALID); 1084 portRef->select_infos = NULL; 1085 1086 portRef->read_condition.NotifyAll(B_BAD_PORT_ID); 1087 portRef->write_condition.NotifyAll(B_BAD_PORT_ID); 1088 1089 return B_OK; 1090 } 1091 1092 1093 status_t 1094 delete_port(port_id id) 1095 { 1096 TRACE(("delete_port(id = %ld)\n", id)); 1097 1098 if (!sPortsActive || id < 0) 1099 return B_BAD_PORT_ID; 1100 1101 BReference<Port> portRef = get_port(id); 1102 1103 if (portRef == NULL) { 1104 TRACE(("delete_port: invalid port_id %ld\n", id)); 1105 return B_BAD_PORT_ID; 1106 } 1107 1108 status_t status = delete_port_logical(portRef); 1109 // Contains linearization point 1110 if (status != B_OK) 1111 return status; 1112 1113 // Now remove port physically: 1114 // (1/2) Remove from hash tables 1115 { 1116 WriteLocker portsLocker(sPortsLock); 1117 1118 sPorts.Remove(portRef); 1119 sPortsByName.Remove(portRef); 1120 1121 portRef->ReleaseReference(); 1122 // joint reference for sPorts and sPortsByName 1123 } 1124 1125 // (2/2) Remove from team port list 1126 { 1127 const uint8 lockIndex = portRef->owner % kTeamListLockCount; 1128 MutexLocker teamPortsListLocker(sTeamListLock[lockIndex]); 1129 1130 list_remove_link(&portRef->team_link); 1131 portRef->ReleaseReference(); 1132 } 1133 1134 uninit_port(portRef); 1135 1136 T(Delete(portRef)); 1137 1138 atomic_add(&sUsedPorts, -1); 1139 1140 return B_OK; 1141 } 1142 1143 1144 status_t 1145 select_port(int32 id, struct select_info* info, bool kernel) 1146 { 1147 if (id < 0) 1148 return B_BAD_PORT_ID; 1149 1150 // get the port 1151 BReference<Port> portRef = get_locked_port(id); 1152 if (portRef == NULL) 1153 return B_BAD_PORT_ID; 1154 MutexLocker locker(portRef->lock, true); 1155 1156 // port must not yet be closed 1157 if (is_port_closed(portRef)) 1158 return B_BAD_PORT_ID; 1159 1160 if (!kernel && portRef->owner == team_get_kernel_team_id()) { 1161 // kernel port, but call from userland 1162 return B_NOT_ALLOWED; 1163 } 1164 1165 info->selected_events &= B_EVENT_READ | B_EVENT_WRITE | B_EVENT_INVALID; 1166 1167 if (info->selected_events != 0) { 1168 uint16 events = 0; 1169 1170 info->next = portRef->select_infos; 1171 portRef->select_infos = info; 1172 1173 // check for events 1174 if ((info->selected_events & B_EVENT_READ) != 0 1175 && !portRef->messages.IsEmpty()) { 1176 events |= B_EVENT_READ; 1177 } 1178 1179 if (portRef->write_count > 0) 1180 events |= B_EVENT_WRITE; 1181 1182 if (events != 0) 1183 notify_select_events(info, events); 1184 } 1185 1186 return B_OK; 1187 } 1188 1189 1190 status_t 1191 deselect_port(int32 id, struct select_info* info, bool kernel) 1192 { 1193 if (id < 0) 1194 return B_BAD_PORT_ID; 1195 if (info->selected_events == 0) 1196 return B_OK; 1197 1198 // get the port 1199 BReference<Port> portRef = get_locked_port(id); 1200 if (portRef == NULL) 1201 return B_BAD_PORT_ID; 1202 MutexLocker locker(portRef->lock, true); 1203 1204 // find and remove the infos 1205 select_info** infoLocation = &portRef->select_infos; 1206 while (*infoLocation != NULL && *infoLocation != info) 1207 infoLocation = &(*infoLocation)->next; 1208 1209 if (*infoLocation == info) 1210 *infoLocation = info->next; 1211 1212 return B_OK; 1213 } 1214 1215 1216 port_id 1217 find_port(const char* name) 1218 { 1219 TRACE(("find_port(name = \"%s\")\n", name)); 1220 1221 if (!sPortsActive) { 1222 panic("ports used too early!\n"); 1223 return B_NAME_NOT_FOUND; 1224 } 1225 if (name == NULL) 1226 return B_BAD_VALUE; 1227 1228 ReadLocker locker(sPortsLock); 1229 Port* port = sPortsByName.Lookup(name); 1230 // Since we have sPortsLock and don't return the port itself, 1231 // no BReference necessary 1232 1233 if (port != NULL && port->state == Port::kActive) 1234 return port->id; 1235 1236 return B_NAME_NOT_FOUND; 1237 } 1238 1239 1240 status_t 1241 _get_port_info(port_id id, port_info* info, size_t size) 1242 { 1243 TRACE(("get_port_info(id = %ld)\n", id)); 1244 1245 if (info == NULL || size != sizeof(port_info)) 1246 return B_BAD_VALUE; 1247 if (!sPortsActive || id < 0) 1248 return B_BAD_PORT_ID; 1249 1250 // get the port 1251 BReference<Port> portRef = get_locked_port(id); 1252 if (portRef == NULL) { 1253 TRACE(("get_port_info: invalid port_id %ld\n", id)); 1254 return B_BAD_PORT_ID; 1255 } 1256 MutexLocker locker(portRef->lock, true); 1257 1258 // fill a port_info struct with info 1259 fill_port_info(portRef, info, size); 1260 return B_OK; 1261 } 1262 1263 1264 status_t 1265 _get_next_port_info(team_id teamID, int32* _cookie, struct port_info* info, 1266 size_t size) 1267 { 1268 TRACE(("get_next_port_info(team = %ld)\n", teamID)); 1269 1270 if (info == NULL || size != sizeof(port_info) || _cookie == NULL 1271 || teamID < 0) { 1272 return B_BAD_VALUE; 1273 } 1274 if (!sPortsActive) 1275 return B_BAD_PORT_ID; 1276 1277 Team* team = Team::Get(teamID); 1278 if (team == NULL) 1279 return B_BAD_TEAM_ID; 1280 BReference<Team> teamReference(team, true); 1281 1282 // iterate through the team's port list 1283 const uint8 lockIndex = teamID % kTeamListLockCount; 1284 MutexLocker teamPortsListLocker(sTeamListLock[lockIndex]); 1285 1286 int32 stopIndex = *_cookie; 1287 int32 index = 0; 1288 1289 Port* port = (Port*)list_get_first_item(&team->port_list); 1290 while (port != NULL) { 1291 if (!is_port_closed(port)) { 1292 if (index == stopIndex) 1293 break; 1294 index++; 1295 } 1296 1297 port = (Port*)list_get_next_item(&team->port_list, port); 1298 } 1299 1300 if (port == NULL) 1301 return B_BAD_PORT_ID; 1302 1303 // fill in the port info 1304 BReference<Port> portRef = port; 1305 teamPortsListLocker.Unlock(); 1306 // Only use portRef below this line... 1307 1308 MutexLocker locker(portRef->lock); 1309 fill_port_info(portRef, info, size); 1310 1311 *_cookie = stopIndex + 1; 1312 return B_OK; 1313 } 1314 1315 1316 ssize_t 1317 port_buffer_size(port_id id) 1318 { 1319 return port_buffer_size_etc(id, 0, 0); 1320 } 1321 1322 1323 ssize_t 1324 port_buffer_size_etc(port_id id, uint32 flags, bigtime_t timeout) 1325 { 1326 port_message_info info; 1327 status_t error = get_port_message_info_etc(id, &info, flags, timeout); 1328 return error != B_OK ? error : info.size; 1329 } 1330 1331 1332 status_t 1333 _get_port_message_info_etc(port_id id, port_message_info* info, 1334 size_t infoSize, uint32 flags, bigtime_t timeout) 1335 { 1336 if (info == NULL || infoSize != sizeof(port_message_info)) 1337 return B_BAD_VALUE; 1338 if (!sPortsActive || id < 0) 1339 return B_BAD_PORT_ID; 1340 1341 flags &= B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT | B_RELATIVE_TIMEOUT 1342 | B_ABSOLUTE_TIMEOUT; 1343 1344 // get the port 1345 BReference<Port> portRef = get_locked_port(id); 1346 if (portRef == NULL) 1347 return B_BAD_PORT_ID; 1348 MutexLocker locker(portRef->lock, true); 1349 1350 if (is_port_closed(portRef) && portRef->messages.IsEmpty()) { 1351 T(Info(portRef, 0, B_BAD_PORT_ID)); 1352 TRACE(("_get_port_message_info_etc(): closed port %ld\n", id)); 1353 return B_BAD_PORT_ID; 1354 } 1355 1356 while (portRef->read_count == 0) { 1357 // We need to wait for a message to appear 1358 if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0) 1359 return B_WOULD_BLOCK; 1360 1361 ConditionVariableEntry entry; 1362 portRef->read_condition.Add(&entry); 1363 1364 locker.Unlock(); 1365 1366 // block if no message, or, if B_TIMEOUT flag set, block with timeout 1367 status_t status = entry.Wait(flags, timeout); 1368 1369 if (status != B_OK) { 1370 T(Info(portRef, 0, status)); 1371 return status; 1372 } 1373 1374 // re-lock 1375 BReference<Port> newPortRef = get_locked_port(id); 1376 if (newPortRef == NULL) { 1377 T(Info(id, 0, 0, 0, B_BAD_PORT_ID)); 1378 return B_BAD_PORT_ID; 1379 } 1380 locker.SetTo(newPortRef->lock, true); 1381 1382 if (newPortRef != portRef 1383 || (is_port_closed(portRef) && portRef->messages.IsEmpty())) { 1384 // the port is no longer there 1385 T(Info(id, 0, 0, 0, B_BAD_PORT_ID)); 1386 return B_BAD_PORT_ID; 1387 } 1388 } 1389 1390 // determine tail & get the length of the message 1391 port_message* message = portRef->messages.Head(); 1392 if (message == NULL) { 1393 panic("port %" B_PRId32 ": no messages found\n", portRef->id); 1394 return B_ERROR; 1395 } 1396 1397 info->size = message->size; 1398 info->sender = message->sender; 1399 info->sender_group = message->sender_group; 1400 info->sender_team = message->sender_team; 1401 1402 T(Info(portRef, message->code, B_OK)); 1403 1404 // notify next one, as we haven't read from the port 1405 portRef->read_condition.NotifyOne(); 1406 1407 return B_OK; 1408 } 1409 1410 1411 ssize_t 1412 port_count(port_id id) 1413 { 1414 if (!sPortsActive || id < 0) 1415 return B_BAD_PORT_ID; 1416 1417 // get the port 1418 BReference<Port> portRef = get_locked_port(id); 1419 if (portRef == NULL) { 1420 TRACE(("port_count: invalid port_id %ld\n", id)); 1421 return B_BAD_PORT_ID; 1422 } 1423 MutexLocker locker(portRef->lock, true); 1424 1425 // return count of messages 1426 return portRef->read_count; 1427 } 1428 1429 1430 ssize_t 1431 read_port(port_id port, int32* msgCode, void* buffer, size_t bufferSize) 1432 { 1433 return read_port_etc(port, msgCode, buffer, bufferSize, 0, 0); 1434 } 1435 1436 1437 ssize_t 1438 read_port_etc(port_id id, int32* _code, void* buffer, size_t bufferSize, 1439 uint32 flags, bigtime_t timeout) 1440 { 1441 if (!sPortsActive || id < 0) 1442 return B_BAD_PORT_ID; 1443 if ((buffer == NULL && bufferSize > 0) || timeout < 0) 1444 return B_BAD_VALUE; 1445 1446 bool userCopy = (flags & PORT_FLAG_USE_USER_MEMCPY) != 0; 1447 bool peekOnly = !userCopy && (flags & B_PEEK_PORT_MESSAGE) != 0; 1448 // TODO: we could allow peeking for user apps now 1449 1450 flags &= B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT | B_RELATIVE_TIMEOUT 1451 | B_ABSOLUTE_TIMEOUT; 1452 1453 // get the port 1454 BReference<Port> portRef = get_locked_port(id); 1455 if (portRef == NULL) 1456 return B_BAD_PORT_ID; 1457 MutexLocker locker(portRef->lock, true); 1458 1459 if (is_port_closed(portRef) && portRef->messages.IsEmpty()) { 1460 T(Read(portRef, 0, B_BAD_PORT_ID)); 1461 TRACE(("read_port_etc(): closed port %ld\n", id)); 1462 return B_BAD_PORT_ID; 1463 } 1464 1465 while (portRef->read_count == 0) { 1466 if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0) 1467 return B_WOULD_BLOCK; 1468 1469 // We need to wait for a message to appear 1470 ConditionVariableEntry entry; 1471 portRef->read_condition.Add(&entry); 1472 1473 locker.Unlock(); 1474 1475 // block if no message, or, if B_TIMEOUT flag set, block with timeout 1476 status_t status = entry.Wait(flags, timeout); 1477 1478 // re-lock 1479 BReference<Port> newPortRef = get_locked_port(id); 1480 if (newPortRef == NULL) { 1481 T(Read(id, 0, 0, 0, B_BAD_PORT_ID)); 1482 return B_BAD_PORT_ID; 1483 } 1484 locker.SetTo(newPortRef->lock, true); 1485 1486 if (newPortRef != portRef 1487 || (is_port_closed(portRef) && portRef->messages.IsEmpty())) { 1488 // the port is no longer there 1489 T(Read(id, 0, 0, 0, B_BAD_PORT_ID)); 1490 return B_BAD_PORT_ID; 1491 } 1492 1493 if (status != B_OK) { 1494 T(Read(portRef, 0, status)); 1495 return status; 1496 } 1497 } 1498 1499 // determine tail & get the length of the message 1500 port_message* message = portRef->messages.Head(); 1501 if (message == NULL) { 1502 panic("port %" B_PRId32 ": no messages found\n", portRef->id); 1503 return B_ERROR; 1504 } 1505 1506 if (peekOnly) { 1507 size_t size = copy_port_message(message, _code, buffer, bufferSize, 1508 userCopy); 1509 1510 T(Read(portRef, message->code, size)); 1511 1512 portRef->read_condition.NotifyOne(); 1513 // we only peeked, but didn't grab the message 1514 return size; 1515 } 1516 1517 portRef->messages.RemoveHead(); 1518 portRef->total_count++; 1519 portRef->write_count++; 1520 portRef->read_count--; 1521 1522 notify_port_select_events(portRef, B_EVENT_WRITE); 1523 portRef->write_condition.NotifyOne(); 1524 // make one spot in queue available again for write 1525 1526 T(Read(portRef, message->code, std::min(bufferSize, message->size))); 1527 1528 locker.Unlock(); 1529 1530 size_t size = copy_port_message(message, _code, buffer, bufferSize, 1531 userCopy); 1532 1533 put_port_message(message); 1534 return size; 1535 } 1536 1537 1538 status_t 1539 write_port(port_id id, int32 msgCode, const void* buffer, size_t bufferSize) 1540 { 1541 iovec vec = { (void*)buffer, bufferSize }; 1542 1543 return writev_port_etc(id, msgCode, &vec, 1, bufferSize, 0, 0); 1544 } 1545 1546 1547 status_t 1548 write_port_etc(port_id id, int32 msgCode, const void* buffer, 1549 size_t bufferSize, uint32 flags, bigtime_t timeout) 1550 { 1551 iovec vec = { (void*)buffer, bufferSize }; 1552 1553 return writev_port_etc(id, msgCode, &vec, 1, bufferSize, flags, timeout); 1554 } 1555 1556 1557 status_t 1558 writev_port_etc(port_id id, int32 msgCode, const iovec* msgVecs, 1559 size_t vecCount, size_t bufferSize, uint32 flags, bigtime_t timeout) 1560 { 1561 if (!sPortsActive || id < 0) 1562 return B_BAD_PORT_ID; 1563 if (bufferSize > PORT_MAX_MESSAGE_SIZE) 1564 return B_BAD_VALUE; 1565 1566 bool userCopy = (flags & PORT_FLAG_USE_USER_MEMCPY) != 0; 1567 1568 // mask irrelevant flags (for acquire_sem() usage) 1569 flags &= B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT | B_RELATIVE_TIMEOUT 1570 | B_ABSOLUTE_TIMEOUT; 1571 if ((flags & B_RELATIVE_TIMEOUT) != 0 1572 && timeout != B_INFINITE_TIMEOUT && timeout > 0) { 1573 // Make the timeout absolute, since we have more than one step where 1574 // we might have to wait 1575 flags = (flags & ~B_RELATIVE_TIMEOUT) | B_ABSOLUTE_TIMEOUT; 1576 timeout += system_time(); 1577 } 1578 1579 status_t status; 1580 port_message* message = NULL; 1581 1582 // get the port 1583 BReference<Port> portRef = get_locked_port(id); 1584 if (portRef == NULL) { 1585 TRACE(("write_port_etc: invalid port_id %ld\n", id)); 1586 return B_BAD_PORT_ID; 1587 } 1588 MutexLocker locker(portRef->lock, true); 1589 1590 if (is_port_closed(portRef)) { 1591 TRACE(("write_port_etc: port %ld closed\n", id)); 1592 return B_BAD_PORT_ID; 1593 } 1594 1595 if (portRef->write_count <= 0) { 1596 if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0) 1597 return B_WOULD_BLOCK; 1598 1599 portRef->write_count--; 1600 1601 // We need to block in order to wait for a free message slot 1602 ConditionVariableEntry entry; 1603 portRef->write_condition.Add(&entry); 1604 1605 locker.Unlock(); 1606 1607 status = entry.Wait(flags, timeout); 1608 1609 // re-lock 1610 BReference<Port> newPortRef = get_locked_port(id); 1611 if (newPortRef == NULL) { 1612 T(Write(id, 0, 0, 0, 0, B_BAD_PORT_ID)); 1613 return B_BAD_PORT_ID; 1614 } 1615 locker.SetTo(newPortRef->lock, true); 1616 1617 if (newPortRef != portRef || is_port_closed(portRef)) { 1618 // the port is no longer there 1619 T(Write(id, 0, 0, 0, 0, B_BAD_PORT_ID)); 1620 return B_BAD_PORT_ID; 1621 } 1622 1623 if (status != B_OK) 1624 goto error; 1625 } else 1626 portRef->write_count--; 1627 1628 status = get_port_message(msgCode, bufferSize, flags, timeout, 1629 &message, *portRef); 1630 if (status != B_OK) { 1631 if (status == B_BAD_PORT_ID) { 1632 // the port had to be unlocked and is now no longer there 1633 T(Write(id, 0, 0, 0, 0, B_BAD_PORT_ID)); 1634 return B_BAD_PORT_ID; 1635 } 1636 1637 goto error; 1638 } 1639 1640 // sender credentials 1641 message->sender = geteuid(); 1642 message->sender_group = getegid(); 1643 message->sender_team = team_get_current_team_id(); 1644 1645 if (bufferSize > 0) { 1646 size_t offset = 0; 1647 for (uint32 i = 0; i < vecCount; i++) { 1648 size_t bytes = msgVecs[i].iov_len; 1649 if (bytes > bufferSize) 1650 bytes = bufferSize; 1651 1652 if (userCopy) { 1653 status_t status = user_memcpy(message->buffer + offset, 1654 msgVecs[i].iov_base, bytes); 1655 if (status != B_OK) { 1656 put_port_message(message); 1657 goto error; 1658 } 1659 } else 1660 memcpy(message->buffer + offset, msgVecs[i].iov_base, bytes); 1661 1662 bufferSize -= bytes; 1663 if (bufferSize == 0) 1664 break; 1665 1666 offset += bytes; 1667 } 1668 } 1669 1670 portRef->messages.Add(message); 1671 portRef->read_count++; 1672 1673 T(Write(id, portRef->read_count, portRef->write_count, message->code, 1674 message->size, B_OK)); 1675 1676 notify_port_select_events(portRef, B_EVENT_READ); 1677 portRef->read_condition.NotifyOne(); 1678 return B_OK; 1679 1680 error: 1681 // Give up our slot in the queue again, and let someone else 1682 // try and fail 1683 T(Write(id, portRef->read_count, portRef->write_count, 0, 0, status)); 1684 portRef->write_count++; 1685 notify_port_select_events(portRef, B_EVENT_WRITE); 1686 portRef->write_condition.NotifyOne(); 1687 1688 return status; 1689 } 1690 1691 1692 status_t 1693 set_port_owner(port_id id, team_id newTeamID) 1694 { 1695 TRACE(("set_port_owner(id = %ld, team = %ld)\n", id, newTeamID)); 1696 1697 if (id < 0) 1698 return B_BAD_PORT_ID; 1699 1700 // get the new team 1701 Team* team = Team::Get(newTeamID); 1702 if (team == NULL) 1703 return B_BAD_TEAM_ID; 1704 BReference<Team> teamReference(team, true); 1705 1706 // get the port 1707 BReference<Port> portRef = get_locked_port(id); 1708 if (portRef == NULL) { 1709 TRACE(("set_port_owner: invalid port_id %ld\n", id)); 1710 return B_BAD_PORT_ID; 1711 } 1712 MutexLocker locker(portRef->lock, true); 1713 1714 // transfer ownership to other team 1715 if (team->id != portRef->owner) { 1716 uint8 firstLockIndex = portRef->owner % kTeamListLockCount; 1717 uint8 secondLockIndex = team->id % kTeamListLockCount; 1718 1719 // Avoid deadlocks: always lock lower index first 1720 if (secondLockIndex < firstLockIndex) { 1721 uint8 temp = secondLockIndex; 1722 secondLockIndex = firstLockIndex; 1723 firstLockIndex = temp; 1724 } 1725 1726 MutexLocker oldTeamPortsListLocker(sTeamListLock[firstLockIndex]); 1727 MutexLocker newTeamPortsListLocker; 1728 if (firstLockIndex != secondLockIndex) { 1729 newTeamPortsListLocker.SetTo(sTeamListLock[secondLockIndex], 1730 false); 1731 } 1732 1733 // Now that we have locked the team port lists, check the state again 1734 if (portRef->state == Port::kActive) { 1735 list_remove_link(&portRef->team_link); 1736 list_add_item(&team->port_list, portRef.Get()); 1737 portRef->owner = team->id; 1738 } else { 1739 // Port was already deleted. We haven't changed anything yet so 1740 // we can cancel the operation. 1741 return B_BAD_PORT_ID; 1742 } 1743 } 1744 1745 T(OwnerChange(portRef, team->id, B_OK)); 1746 return B_OK; 1747 } 1748 1749 1750 // #pragma mark - syscalls 1751 1752 1753 port_id 1754 _user_create_port(int32 queueLength, const char *userName) 1755 { 1756 char name[B_OS_NAME_LENGTH]; 1757 1758 if (userName == NULL) 1759 return create_port(queueLength, NULL); 1760 1761 if (!IS_USER_ADDRESS(userName) 1762 || user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK) 1763 return B_BAD_ADDRESS; 1764 1765 return create_port(queueLength, name); 1766 } 1767 1768 1769 status_t 1770 _user_close_port(port_id id) 1771 { 1772 return close_port(id); 1773 } 1774 1775 1776 status_t 1777 _user_delete_port(port_id id) 1778 { 1779 return delete_port(id); 1780 } 1781 1782 1783 port_id 1784 _user_find_port(const char *userName) 1785 { 1786 char name[B_OS_NAME_LENGTH]; 1787 1788 if (userName == NULL) 1789 return B_BAD_VALUE; 1790 if (!IS_USER_ADDRESS(userName) 1791 || user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK) 1792 return B_BAD_ADDRESS; 1793 1794 return find_port(name); 1795 } 1796 1797 1798 status_t 1799 _user_get_port_info(port_id id, struct port_info *userInfo) 1800 { 1801 struct port_info info; 1802 status_t status; 1803 1804 if (userInfo == NULL) 1805 return B_BAD_VALUE; 1806 if (!IS_USER_ADDRESS(userInfo)) 1807 return B_BAD_ADDRESS; 1808 1809 status = get_port_info(id, &info); 1810 1811 // copy back to user space 1812 if (status == B_OK 1813 && user_memcpy(userInfo, &info, sizeof(struct port_info)) < B_OK) 1814 return B_BAD_ADDRESS; 1815 1816 return status; 1817 } 1818 1819 1820 status_t 1821 _user_get_next_port_info(team_id team, int32 *userCookie, 1822 struct port_info *userInfo) 1823 { 1824 struct port_info info; 1825 status_t status; 1826 int32 cookie; 1827 1828 if (userCookie == NULL || userInfo == NULL) 1829 return B_BAD_VALUE; 1830 if (!IS_USER_ADDRESS(userCookie) || !IS_USER_ADDRESS(userInfo) 1831 || user_memcpy(&cookie, userCookie, sizeof(int32)) < B_OK) 1832 return B_BAD_ADDRESS; 1833 1834 status = get_next_port_info(team, &cookie, &info); 1835 1836 // copy back to user space 1837 if (user_memcpy(userCookie, &cookie, sizeof(int32)) < B_OK 1838 || (status == B_OK && user_memcpy(userInfo, &info, 1839 sizeof(struct port_info)) < B_OK)) 1840 return B_BAD_ADDRESS; 1841 1842 return status; 1843 } 1844 1845 1846 ssize_t 1847 _user_port_buffer_size_etc(port_id port, uint32 flags, bigtime_t timeout) 1848 { 1849 syscall_restart_handle_timeout_pre(flags, timeout); 1850 1851 status_t status = port_buffer_size_etc(port, flags | B_CAN_INTERRUPT, 1852 timeout); 1853 1854 return syscall_restart_handle_timeout_post(status, timeout); 1855 } 1856 1857 1858 ssize_t 1859 _user_port_count(port_id port) 1860 { 1861 return port_count(port); 1862 } 1863 1864 1865 status_t 1866 _user_set_port_owner(port_id port, team_id team) 1867 { 1868 return set_port_owner(port, team); 1869 } 1870 1871 1872 ssize_t 1873 _user_read_port_etc(port_id port, int32 *userCode, void *userBuffer, 1874 size_t bufferSize, uint32 flags, bigtime_t timeout) 1875 { 1876 int32 messageCode; 1877 ssize_t bytesRead; 1878 1879 syscall_restart_handle_timeout_pre(flags, timeout); 1880 1881 if (userBuffer == NULL && bufferSize != 0) 1882 return B_BAD_VALUE; 1883 if ((userCode != NULL && !IS_USER_ADDRESS(userCode)) 1884 || (userBuffer != NULL && !IS_USER_ADDRESS(userBuffer))) 1885 return B_BAD_ADDRESS; 1886 1887 bytesRead = read_port_etc(port, &messageCode, userBuffer, bufferSize, 1888 flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, timeout); 1889 1890 if (bytesRead >= 0 && userCode != NULL 1891 && user_memcpy(userCode, &messageCode, sizeof(int32)) < B_OK) 1892 return B_BAD_ADDRESS; 1893 1894 return syscall_restart_handle_timeout_post(bytesRead, timeout); 1895 } 1896 1897 1898 status_t 1899 _user_write_port_etc(port_id port, int32 messageCode, const void *userBuffer, 1900 size_t bufferSize, uint32 flags, bigtime_t timeout) 1901 { 1902 iovec vec = { (void *)userBuffer, bufferSize }; 1903 1904 syscall_restart_handle_timeout_pre(flags, timeout); 1905 1906 if (userBuffer == NULL && bufferSize != 0) 1907 return B_BAD_VALUE; 1908 if (userBuffer != NULL && !IS_USER_ADDRESS(userBuffer)) 1909 return B_BAD_ADDRESS; 1910 1911 status_t status = writev_port_etc(port, messageCode, &vec, 1, bufferSize, 1912 flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, timeout); 1913 1914 return syscall_restart_handle_timeout_post(status, timeout); 1915 } 1916 1917 1918 status_t 1919 _user_writev_port_etc(port_id port, int32 messageCode, const iovec *userVecs, 1920 size_t vecCount, size_t bufferSize, uint32 flags, bigtime_t timeout) 1921 { 1922 syscall_restart_handle_timeout_pre(flags, timeout); 1923 1924 if (userVecs == NULL && bufferSize != 0) 1925 return B_BAD_VALUE; 1926 if (userVecs != NULL && !IS_USER_ADDRESS(userVecs)) 1927 return B_BAD_ADDRESS; 1928 1929 iovec *vecs = NULL; 1930 if (userVecs && vecCount != 0) { 1931 vecs = (iovec*)malloc(sizeof(iovec) * vecCount); 1932 if (vecs == NULL) 1933 return B_NO_MEMORY; 1934 1935 if (user_memcpy(vecs, userVecs, sizeof(iovec) * vecCount) < B_OK) { 1936 free(vecs); 1937 return B_BAD_ADDRESS; 1938 } 1939 } 1940 1941 status_t status = writev_port_etc(port, messageCode, vecs, vecCount, 1942 bufferSize, flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, 1943 timeout); 1944 1945 free(vecs); 1946 return syscall_restart_handle_timeout_post(status, timeout); 1947 } 1948 1949 1950 status_t 1951 _user_get_port_message_info_etc(port_id port, port_message_info *userInfo, 1952 size_t infoSize, uint32 flags, bigtime_t timeout) 1953 { 1954 if (userInfo == NULL || infoSize != sizeof(port_message_info)) 1955 return B_BAD_VALUE; 1956 1957 syscall_restart_handle_timeout_pre(flags, timeout); 1958 1959 port_message_info info; 1960 status_t error = _get_port_message_info_etc(port, &info, sizeof(info), 1961 flags | B_CAN_INTERRUPT, timeout); 1962 1963 // copy info to userland 1964 if (error == B_OK && (!IS_USER_ADDRESS(userInfo) 1965 || user_memcpy(userInfo, &info, sizeof(info)) != B_OK)) { 1966 error = B_BAD_ADDRESS; 1967 } 1968 1969 return syscall_restart_handle_timeout_post(error, timeout); 1970 } 1971