libroot_debug: Merge guarded heap into libroot_debug.
[haiku.git] / src / system / kernel / port.cpp
blob7d9bdddf0a3e3eafab19e6afadc401fba7c0d912
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.
6 * Copyright 2001, Mark-Jan Bastian. All rights reserved.
7 * Distributed under the terms of the NewOS License.
8 */
11 /*! Ports for IPC */
14 #include <port.h>
16 #include <algorithm>
17 #include <ctype.h>
18 #include <iovec.h>
19 #include <stdlib.h>
20 #include <string.h>
22 #include <OS.h>
24 #include <AutoDeleter.h>
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>
40 //#define TRACE_PORTS
41 #ifdef TRACE_PORTS
42 # define TRACE(x) dprintf x
43 #else
44 # define TRACE(x)
45 #endif
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
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.
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.
89 struct port_message;
92 static void put_port_message(port_message* message);
95 struct port_message : DoublyLinkedListLinkImpl<port_message> {
96 int32 code;
97 size_t size;
98 uid_t sender;
99 gid_t sender_group;
100 team_id sender_team;
101 char buffer[0];
104 typedef DoublyLinkedList<port_message> MessageList;
107 struct Port : public KernelReferenceable {
108 enum State {
109 kUnused = 0,
110 kActive,
111 kDeleted
114 struct list_link team_link;
115 Port* hash_link;
116 port_id id;
117 team_id owner;
118 Port* name_hash_link;
119 size_t name_hash;
120 int32 capacity;
121 mutex lock;
122 int32 state;
123 uint32 read_count;
124 int32 write_count;
125 ConditionVariable read_condition;
126 ConditionVariable write_condition;
127 int32 total_count;
128 // messages read from port since creation
129 select_info* select_infos;
130 MessageList messages;
132 Port(team_id owner, int32 queueLength, char* name)
134 owner(owner),
135 name_hash(0),
136 capacity(queueLength),
137 state(kUnused),
138 read_count(0),
139 write_count(queueLength),
140 total_count(0),
141 select_infos(NULL)
143 // id is initialized when the caller adds the port to the hash table
145 mutex_init(&lock, name);
146 read_condition.Init(this, "port read");
147 write_condition.Init(this, "port write");
150 virtual ~Port()
152 while (port_message* message = messages.RemoveHead())
153 put_port_message(message);
155 free((char*)lock.name);
156 lock.name = NULL;
161 struct PortHashDefinition {
162 typedef port_id KeyType;
163 typedef Port ValueType;
165 size_t HashKey(port_id key) const
167 return key;
170 size_t Hash(Port* value) const
172 return HashKey(value->id);
175 bool Compare(port_id key, Port* value) const
177 return value->id == key;
180 Port*& GetLink(Port* value) const
182 return value->hash_link;
186 typedef BOpenHashTable<PortHashDefinition> PortHashTable;
189 struct PortNameHashDefinition {
190 typedef const char* KeyType;
191 typedef Port ValueType;
193 size_t HashKey(const char* key) const
195 // Hash function: hash(key) = key[0] * 31^(length - 1)
196 // + key[1] * 31^(length - 2) + ... + key[length - 1]
198 const size_t length = strlen(key);
200 size_t hash = 0;
201 for (size_t index = 0; index < length; index++)
202 hash = 31 * hash + key[index];
204 return hash;
207 size_t Hash(Port* value) const
209 size_t& hash = value->name_hash;
210 if (hash == 0)
211 hash = HashKey(value->lock.name);
212 return hash;
215 bool Compare(const char* key, Port* value) const
217 return (strcmp(key, value->lock.name) == 0);
220 Port*& GetLink(Port* value) const
222 return value->name_hash_link;
226 typedef BOpenHashTable<PortNameHashDefinition> PortNameHashTable;
229 class PortNotificationService : public DefaultNotificationService {
230 public:
231 PortNotificationService();
233 void Notify(uint32 opcode, port_id team);
237 // #pragma mark - tracing
240 #if PORT_TRACING
241 namespace PortTracing {
243 class Create : public AbstractTraceEntry {
244 public:
245 Create(Port* port)
247 fID(port->id),
248 fOwner(port->owner),
249 fCapacity(port->capacity)
251 fName = alloc_tracing_buffer_strcpy(port->lock.name, B_OS_NAME_LENGTH,
252 false);
254 Initialized();
257 virtual void AddDump(TraceOutput& out)
259 out.Print("port %ld created, name \"%s\", owner %ld, capacity %ld",
260 fID, fName, fOwner, fCapacity);
263 private:
264 port_id fID;
265 char* fName;
266 team_id fOwner;
267 int32 fCapacity;
271 class Delete : public AbstractTraceEntry {
272 public:
273 Delete(Port* port)
275 fID(port->id)
277 Initialized();
280 virtual void AddDump(TraceOutput& out)
282 out.Print("port %ld deleted", fID);
285 private:
286 port_id fID;
290 class Read : public AbstractTraceEntry {
291 public:
292 Read(const BReference<Port>& portRef, int32 code, ssize_t result)
294 fID(portRef->id),
295 fReadCount(portRef->read_count),
296 fWriteCount(portRef->write_count),
297 fCode(code),
298 fResult(result)
300 Initialized();
303 Read(port_id id, int32 readCount, int32 writeCount, int32 code,
304 ssize_t result)
306 fID(id),
307 fReadCount(readCount),
308 fWriteCount(writeCount),
309 fCode(code),
310 fResult(result)
312 Initialized();
315 virtual void AddDump(TraceOutput& out)
317 out.Print("port %ld read, read %ld, write %ld, code %lx: %ld",
318 fID, fReadCount, fWriteCount, fCode, fResult);
321 private:
322 port_id fID;
323 int32 fReadCount;
324 int32 fWriteCount;
325 int32 fCode;
326 ssize_t fResult;
330 class Write : public AbstractTraceEntry {
331 public:
332 Write(port_id id, int32 readCount, int32 writeCount, int32 code,
333 size_t bufferSize, ssize_t result)
335 fID(id),
336 fReadCount(readCount),
337 fWriteCount(writeCount),
338 fCode(code),
339 fBufferSize(bufferSize),
340 fResult(result)
342 Initialized();
345 virtual void AddDump(TraceOutput& out)
347 out.Print("port %ld write, read %ld, write %ld, code %lx, size %ld: %ld",
348 fID, fReadCount, fWriteCount, fCode, fBufferSize, fResult);
351 private:
352 port_id fID;
353 int32 fReadCount;
354 int32 fWriteCount;
355 int32 fCode;
356 size_t fBufferSize;
357 ssize_t fResult;
361 class Info : public AbstractTraceEntry {
362 public:
363 Info(const BReference<Port>& portRef, int32 code, ssize_t result)
365 fID(portRef->id),
366 fReadCount(portRef->read_count),
367 fWriteCount(portRef->write_count),
368 fCode(code),
369 fResult(result)
371 Initialized();
374 Info(port_id id, int32 readCount, int32 writeCount, int32 code,
375 ssize_t result)
377 fID(id),
378 fReadCount(readCount),
379 fWriteCount(writeCount),
380 fCode(code),
381 fResult(result)
383 Initialized();
386 virtual void AddDump(TraceOutput& out)
388 out.Print("port %ld info, read %ld, write %ld, code %lx: %ld",
389 fID, fReadCount, fWriteCount, fCode, fResult);
392 private:
393 port_id fID;
394 int32 fReadCount;
395 int32 fWriteCount;
396 int32 fCode;
397 ssize_t fResult;
401 class OwnerChange : public AbstractTraceEntry {
402 public:
403 OwnerChange(Port* port, team_id newOwner, status_t status)
405 fID(port->id),
406 fOldOwner(port->owner),
407 fNewOwner(newOwner),
408 fStatus(status)
410 Initialized();
413 virtual void AddDump(TraceOutput& out)
415 out.Print("port %ld owner change from %ld to %ld: %s", fID, fOldOwner,
416 fNewOwner, strerror(fStatus));
419 private:
420 port_id fID;
421 team_id fOldOwner;
422 team_id fNewOwner;
423 status_t fStatus;
426 } // namespace PortTracing
428 # define T(x) new(std::nothrow) PortTracing::x;
429 #else
430 # define T(x) ;
431 #endif
434 static const size_t kInitialPortBufferSize = 4 * 1024 * 1024;
435 static const size_t kTotalSpaceLimit = 64 * 1024 * 1024;
436 static const size_t kTeamSpaceLimit = 8 * 1024 * 1024;
437 static const size_t kBufferGrowRate = kInitialPortBufferSize;
439 #define MAX_QUEUE_LENGTH 4096
440 #define PORT_MAX_MESSAGE_SIZE (256 * 1024)
442 static int32 sMaxPorts = 4096;
443 static int32 sUsedPorts;
445 static PortHashTable sPorts;
446 static PortNameHashTable sPortsByName;
447 static ConditionVariable sNoSpaceCondition;
448 static int32 sTotalSpaceCommited;
449 static int32 sWaitingForSpace;
450 static port_id sNextPortID = 1;
451 static bool sPortsActive = false;
452 static rw_lock sPortsLock = RW_LOCK_INITIALIZER("ports list");
454 enum {
455 kTeamListLockCount = 8
458 static mutex sTeamListLock[kTeamListLockCount] = {
459 MUTEX_INITIALIZER("team ports list 1"),
460 MUTEX_INITIALIZER("team ports list 2"),
461 MUTEX_INITIALIZER("team ports list 3"),
462 MUTEX_INITIALIZER("team ports list 4"),
463 MUTEX_INITIALIZER("team ports list 5"),
464 MUTEX_INITIALIZER("team ports list 6"),
465 MUTEX_INITIALIZER("team ports list 7"),
466 MUTEX_INITIALIZER("team ports list 8")
469 static PortNotificationService sNotificationService;
472 // #pragma mark - TeamNotificationService
475 PortNotificationService::PortNotificationService()
477 DefaultNotificationService("ports")
482 void
483 PortNotificationService::Notify(uint32 opcode, port_id port)
485 char eventBuffer[128];
486 KMessage event;
487 event.SetTo(eventBuffer, sizeof(eventBuffer), PORT_MONITOR);
488 event.AddInt32("event", opcode);
489 event.AddInt32("port", port);
491 DefaultNotificationService::Notify(event, opcode);
495 // #pragma mark - debugger commands
498 static int
499 dump_port_list(int argc, char** argv)
501 const char* name = NULL;
502 team_id owner = -1;
504 if (argc > 2) {
505 if (!strcmp(argv[1], "team") || !strcmp(argv[1], "owner"))
506 owner = strtoul(argv[2], NULL, 0);
507 else if (!strcmp(argv[1], "name"))
508 name = argv[2];
509 } else if (argc > 1)
510 owner = strtoul(argv[1], NULL, 0);
512 kprintf("port id cap read-cnt write-cnt total team "
513 "name\n");
515 for (PortHashTable::Iterator it = sPorts.GetIterator();
516 Port* port = it.Next();) {
517 if ((owner != -1 && port->owner != owner)
518 || (name != NULL && strstr(port->lock.name, name) == NULL))
519 continue;
521 kprintf("%p %8" B_PRId32 " %4" B_PRId32 " %9" B_PRIu32 " %9" B_PRId32
522 " %8" B_PRId32 " %6" B_PRId32 " %s\n", port, port->id,
523 port->capacity, port->read_count, port->write_count,
524 port->total_count, port->owner, port->lock.name);
527 return 0;
531 static void
532 _dump_port_info(Port* port)
534 kprintf("PORT: %p\n", port);
535 kprintf(" id: %" B_PRId32 "\n", port->id);
536 kprintf(" name: \"%s\"\n", port->lock.name);
537 kprintf(" owner: %" B_PRId32 "\n", port->owner);
538 kprintf(" capacity: %" B_PRId32 "\n", port->capacity);
539 kprintf(" read_count: %" B_PRIu32 "\n", port->read_count);
540 kprintf(" write_count: %" B_PRId32 "\n", port->write_count);
541 kprintf(" total count: %" B_PRId32 "\n", port->total_count);
543 if (!port->messages.IsEmpty()) {
544 kprintf("messages:\n");
546 MessageList::Iterator iterator = port->messages.GetIterator();
547 while (port_message* message = iterator.Next()) {
548 kprintf(" %p %08" B_PRIx32 " %ld\n", message, message->code, message->size);
552 set_debug_variable("_port", (addr_t)port);
553 set_debug_variable("_portID", port->id);
554 set_debug_variable("_owner", port->owner);
558 static int
559 dump_port_info(int argc, char** argv)
561 ConditionVariable* condition = NULL;
562 const char* name = NULL;
564 if (argc < 2) {
565 print_debugger_command_usage(argv[0]);
566 return 0;
569 if (argc > 2) {
570 if (!strcmp(argv[1], "address")) {
571 _dump_port_info((Port*)parse_expression(argv[2]));
572 return 0;
573 } else if (!strcmp(argv[1], "condition"))
574 condition = (ConditionVariable*)parse_expression(argv[2]);
575 else if (!strcmp(argv[1], "name"))
576 name = argv[2];
577 } else if (parse_expression(argv[1]) > 0) {
578 // if the argument looks like a number, treat it as such
579 int32 num = parse_expression(argv[1]);
580 Port* port = sPorts.Lookup(num);
581 if (port == NULL || port->state != Port::kActive) {
582 kprintf("port %" B_PRId32 " (%#" B_PRIx32 ") doesn't exist!\n",
583 num, num);
584 return 0;
586 _dump_port_info(port);
587 return 0;
588 } else
589 name = argv[1];
591 // walk through the ports list, trying to match name
592 for (PortHashTable::Iterator it = sPorts.GetIterator();
593 Port* port = it.Next();) {
594 if ((name != NULL && port->lock.name != NULL
595 && !strcmp(name, port->lock.name))
596 || (condition != NULL && (&port->read_condition == condition
597 || &port->write_condition == condition))) {
598 _dump_port_info(port);
599 return 0;
603 return 0;
607 // #pragma mark - internal helper functions
610 /*! Notifies the port's select events.
611 The port must be locked.
613 static void
614 notify_port_select_events(Port* port, uint16 events)
616 if (port->select_infos)
617 notify_select_events_list(port->select_infos, events);
621 static BReference<Port>
622 get_locked_port(port_id id) GCC_2_NRV(portRef)
624 #if __GNUC__ >= 3
625 BReference<Port> portRef;
626 #endif
628 ReadLocker portsLocker(sPortsLock);
629 portRef.SetTo(sPorts.Lookup(id));
632 if (portRef != NULL && portRef->state == Port::kActive)
633 mutex_lock(&portRef->lock);
634 else
635 portRef.Unset();
637 return portRef;
641 static BReference<Port>
642 get_port(port_id id) GCC_2_NRV(portRef)
644 #if __GNUC__ >= 3
645 BReference<Port> portRef;
646 #endif
647 ReadLocker portsLocker(sPortsLock);
648 portRef.SetTo(sPorts.Lookup(id));
650 return portRef;
654 /*! You need to own the port's lock when calling this function */
655 static inline bool
656 is_port_closed(Port* port)
658 return port->capacity == 0;
662 static void
663 put_port_message(port_message* message)
665 const size_t size = sizeof(port_message) + message->size;
666 free(message);
668 atomic_add(&sTotalSpaceCommited, -size);
669 if (sWaitingForSpace > 0)
670 sNoSpaceCondition.NotifyAll();
674 /*! Port must be locked. */
675 static status_t
676 get_port_message(int32 code, size_t bufferSize, uint32 flags, bigtime_t timeout,
677 port_message** _message, Port& port)
679 const size_t size = sizeof(port_message) + bufferSize;
681 while (true) {
682 int32 previouslyCommited = atomic_add(&sTotalSpaceCommited, size);
684 while (previouslyCommited + size > kTotalSpaceLimit) {
685 // TODO: add per team limit
687 // We are not allowed to allocate more memory, as our
688 // space limit has been reached - just wait until we get
689 // some free space again.
691 atomic_add(&sTotalSpaceCommited, -size);
693 // TODO: we don't want to wait - but does that also mean we
694 // shouldn't wait for free memory?
695 if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0)
696 return B_WOULD_BLOCK;
698 ConditionVariableEntry entry;
699 sNoSpaceCondition.Add(&entry);
701 port_id portID = port.id;
702 mutex_unlock(&port.lock);
704 atomic_add(&sWaitingForSpace, 1);
706 // TODO: right here the condition could be notified and we'd
707 // miss it.
709 status_t status = entry.Wait(flags, timeout);
711 atomic_add(&sWaitingForSpace, -1);
713 // re-lock the port
714 BReference<Port> newPortRef = get_locked_port(portID);
716 if (newPortRef.Get() != &port || is_port_closed(&port)) {
717 // the port is no longer usable
718 return B_BAD_PORT_ID;
721 if (status == B_TIMED_OUT)
722 return B_TIMED_OUT;
724 previouslyCommited = atomic_add(&sTotalSpaceCommited, size);
725 continue;
728 // Quota is fulfilled, try to allocate the buffer
729 port_message* message = (port_message*)malloc(size);
730 if (message != NULL) {
731 message->code = code;
732 message->size = bufferSize;
734 *_message = message;
735 return B_OK;
738 // We weren't able to allocate and we'll start over,so we remove our
739 // size from the commited-counter again.
740 atomic_add(&sTotalSpaceCommited, -size);
741 continue;
746 /*! Fills the port_info structure with information from the specified
747 port.
748 The port's lock must be held when called.
750 static void
751 fill_port_info(Port* port, port_info* info, size_t size)
753 info->port = port->id;
754 info->team = port->owner;
755 info->capacity = port->capacity;
757 info->queue_count = port->read_count;
758 info->total_count = port->total_count;
760 strlcpy(info->name, port->lock.name, B_OS_NAME_LENGTH);
764 static ssize_t
765 copy_port_message(port_message* message, int32* _code, void* buffer,
766 size_t bufferSize, bool userCopy)
768 // check output buffer size
769 size_t size = std::min(bufferSize, message->size);
771 // copy message
772 if (_code != NULL)
773 *_code = message->code;
775 if (size > 0) {
776 if (userCopy) {
777 status_t status = user_memcpy(buffer, message->buffer, size);
778 if (status != B_OK)
779 return status;
780 } else
781 memcpy(buffer, message->buffer, size);
784 return size;
788 static void
789 uninit_port(Port* port)
791 MutexLocker locker(port->lock);
793 notify_port_select_events(port, B_EVENT_INVALID);
794 port->select_infos = NULL;
796 // Release the threads that were blocking on this port.
797 // read_port() will see the B_BAD_PORT_ID return value, and act accordingly
798 port->read_condition.NotifyAll(B_BAD_PORT_ID);
799 port->write_condition.NotifyAll(B_BAD_PORT_ID);
800 sNotificationService.Notify(PORT_REMOVED, port->id);
804 /*! Caller must ensure there is still a reference to the port. (Either by
805 * holding a reference itself or by holding a lock on one of the data
806 * structures in which it is referenced.)
808 static status_t
809 delete_port_logical(Port* port)
811 for (;;) {
812 // Try to logically delete
813 const int32 oldState = atomic_test_and_set(&port->state,
814 Port::kDeleted, Port::kActive);
815 // Linearization point for port deletion
817 switch (oldState) {
818 case Port::kActive:
819 // Logical deletion succesful
820 return B_OK;
822 case Port::kDeleted:
823 // Someone else already deleted it in the meantime
824 TRACE(("delete_port_logical: already deleted port_id %ld\n",
825 port->id));
826 return B_BAD_PORT_ID;
828 case Port::kUnused:
829 // Port is still being created, retry
830 continue;
832 default:
833 // Port state got corrupted somehow
834 panic("Invalid port state!\n");
840 // #pragma mark - private kernel API
843 /*! This function deletes all the ports that are owned by the passed team.
845 void
846 delete_owned_ports(Team* team)
848 TRACE(("delete_owned_ports(owner = %ld)\n", team->id));
850 list deletionList;
851 list_init_etc(&deletionList, port_team_link_offset());
853 const uint8 lockIndex = team->id % kTeamListLockCount;
854 MutexLocker teamPortsListLocker(sTeamListLock[lockIndex]);
856 // Try to logically delete all ports from the team's port list.
857 // On success, move the port to deletionList.
858 Port* port = (Port*)list_get_first_item(&team->port_list);
859 while (port != NULL) {
860 status_t status = delete_port_logical(port);
861 // Contains linearization point
863 Port* nextPort = (Port*)list_get_next_item(&team->port_list, port);
865 if (status == B_OK) {
866 list_remove_link(&port->team_link);
867 list_add_item(&deletionList, port);
870 port = nextPort;
873 teamPortsListLocker.Unlock();
875 // Remove all ports in deletionList from hashes
877 WriteLocker portsLocker(sPortsLock);
879 for (Port* port = (Port*)list_get_first_item(&deletionList);
880 port != NULL;
881 port = (Port*)list_get_next_item(&deletionList, port)) {
883 sPorts.Remove(port);
884 sPortsByName.Remove(port);
885 port->ReleaseReference();
886 // joint reference for sPorts and sPortsByName
890 // Uninitialize ports and release team port list references
891 while (Port* port = (Port*)list_remove_head_item(&deletionList)) {
892 atomic_add(&sUsedPorts, -1);
893 uninit_port(port);
894 port->ReleaseReference();
895 // Reference for team port list
900 int32
901 port_max_ports(void)
903 return sMaxPorts;
907 int32
908 port_used_ports(void)
910 return sUsedPorts;
914 size_t
915 port_team_link_offset()
917 // Somewhat ugly workaround since we cannot use offsetof() for a class
918 // with vtable (GCC4 throws a warning then).
919 Port* port = (Port*)0;
920 return (size_t)&port->team_link;
924 status_t
925 port_init(kernel_args *args)
927 // initialize ports table and by-name hash
928 new(&sPorts) PortHashTable;
929 if (sPorts.Init() != B_OK) {
930 panic("Failed to init port hash table!");
931 return B_NO_MEMORY;
934 new(&sPortsByName) PortNameHashTable;
935 if (sPortsByName.Init() != B_OK) {
936 panic("Failed to init port by name hash table!");
937 return B_NO_MEMORY;
940 sNoSpaceCondition.Init(&sPorts, "port space");
942 // add debugger commands
943 add_debugger_command_etc("ports", &dump_port_list,
944 "Dump a list of all active ports (for team, with name, etc.)",
945 "[ ([ \"team\" | \"owner\" ] <team>) | (\"name\" <name>) ]\n"
946 "Prints a list of all active ports meeting the given\n"
947 "requirement. If no argument is given, all ports are listed.\n"
948 " <team> - The team owning the ports.\n"
949 " <name> - Part of the name of the ports.\n", 0);
950 add_debugger_command_etc("port", &dump_port_info,
951 "Dump info about a particular port",
952 "(<id> | [ \"address\" ] <address>) | ([ \"name\" ] <name>) "
953 "| (\"condition\" <address>)\n"
954 "Prints info about the specified port.\n"
955 " <address> - Pointer to the port structure.\n"
956 " <name> - Name of the port.\n"
957 " <condition> - address of the port's read or write condition.\n", 0);
959 new(&sNotificationService) PortNotificationService();
960 sNotificationService.Register();
961 sPortsActive = true;
962 return B_OK;
966 // #pragma mark - public kernel API
969 port_id
970 create_port(int32 queueLength, const char* name)
972 TRACE(("create_port(queueLength = %ld, name = \"%s\")\n", queueLength,
973 name));
975 if (!sPortsActive) {
976 panic("ports used too early!\n");
977 return B_BAD_PORT_ID;
979 if (queueLength < 1 || queueLength > MAX_QUEUE_LENGTH)
980 return B_BAD_VALUE;
982 Team* team = thread_get_current_thread()->team;
983 if (team == NULL)
984 return B_BAD_TEAM_ID;
986 // check & dup name
987 char* nameBuffer = strdup(name != NULL ? name : "unnamed port");
988 if (nameBuffer == NULL)
989 return B_NO_MEMORY;
991 // create a port
992 Port* port = new(std::nothrow) Port(team_get_current_team_id(), queueLength,
993 nameBuffer);
994 if (port == NULL) {
995 free(nameBuffer);
996 return B_NO_MEMORY;
999 // check the ports limit
1000 const int32 previouslyUsed = atomic_add(&sUsedPorts, 1);
1001 if (previouslyUsed + 1 >= sMaxPorts) {
1002 atomic_add(&sUsedPorts, -1);
1003 delete port;
1004 return B_NO_MORE_PORTS;
1008 WriteLocker locker(sPortsLock);
1010 // allocate a port ID
1011 do {
1012 port->id = sNextPortID++;
1014 // handle integer overflow
1015 if (sNextPortID < 0)
1016 sNextPortID = 1;
1017 } while (sPorts.Lookup(port->id) != NULL);
1019 // Insert port physically:
1020 // (1/2) Insert into hash tables
1021 port->AcquireReference();
1022 // joint reference for sPorts and sPortsByName
1024 sPorts.Insert(port);
1025 sPortsByName.Insert(port);
1028 // (2/2) Insert into team list
1030 const uint8 lockIndex = port->owner % kTeamListLockCount;
1031 MutexLocker teamPortsListLocker(sTeamListLock[lockIndex]);
1032 port->AcquireReference();
1033 list_add_item(&team->port_list, port);
1036 // tracing, notifications, etc.
1037 T(Create(port));
1039 const port_id id = port->id;
1041 // Insert port logically by marking it active
1042 const int32 oldState = atomic_test_and_set(&port->state,
1043 Port::kActive, Port::kUnused);
1044 // Linearization point for port creation
1046 if (oldState != Port::kUnused) {
1047 // Nobody is allowed to tamper with the port before it's active.
1048 panic("Port state was modified during creation!\n");
1051 TRACE(("create_port() done: port created %ld\n", id));
1053 sNotificationService.Notify(PORT_ADDED, id);
1054 return id;
1058 status_t
1059 close_port(port_id id)
1061 TRACE(("close_port(id = %ld)\n", id));
1063 if (!sPortsActive || id < 0)
1064 return B_BAD_PORT_ID;
1066 // get the port
1067 BReference<Port> portRef = get_locked_port(id);
1068 if (portRef == NULL) {
1069 TRACE(("close_port: invalid port_id %ld\n", id));
1070 return B_BAD_PORT_ID;
1072 MutexLocker lock(&portRef->lock, true);
1074 // mark port to disable writing - deleting the semaphores will
1075 // wake up waiting read/writes
1076 portRef->capacity = 0;
1078 notify_port_select_events(portRef, B_EVENT_INVALID);
1079 portRef->select_infos = NULL;
1081 portRef->read_condition.NotifyAll(B_BAD_PORT_ID);
1082 portRef->write_condition.NotifyAll(B_BAD_PORT_ID);
1084 return B_OK;
1088 status_t
1089 delete_port(port_id id)
1091 TRACE(("delete_port(id = %ld)\n", id));
1093 if (!sPortsActive || id < 0)
1094 return B_BAD_PORT_ID;
1096 BReference<Port> portRef = get_port(id);
1098 if (portRef == NULL) {
1099 TRACE(("delete_port: invalid port_id %ld\n", id));
1100 return B_BAD_PORT_ID;
1103 status_t status = delete_port_logical(portRef);
1104 // Contains linearization point
1105 if (status != B_OK)
1106 return status;
1108 // Now remove port physically:
1109 // (1/2) Remove from hash tables
1111 WriteLocker portsLocker(sPortsLock);
1113 sPorts.Remove(portRef);
1114 sPortsByName.Remove(portRef);
1116 portRef->ReleaseReference();
1117 // joint reference for sPorts and sPortsByName
1120 // (2/2) Remove from team port list
1122 const uint8 lockIndex = portRef->owner % kTeamListLockCount;
1123 MutexLocker teamPortsListLocker(sTeamListLock[lockIndex]);
1125 list_remove_link(&portRef->team_link);
1126 portRef->ReleaseReference();
1129 uninit_port(portRef);
1131 T(Delete(portRef));
1133 atomic_add(&sUsedPorts, -1);
1135 return B_OK;
1139 status_t
1140 select_port(int32 id, struct select_info* info, bool kernel)
1142 if (id < 0)
1143 return B_BAD_PORT_ID;
1145 // get the port
1146 BReference<Port> portRef = get_locked_port(id);
1147 if (portRef == NULL)
1148 return B_BAD_PORT_ID;
1149 MutexLocker locker(portRef->lock, true);
1151 // port must not yet be closed
1152 if (is_port_closed(portRef))
1153 return B_BAD_PORT_ID;
1155 if (!kernel && portRef->owner == team_get_kernel_team_id()) {
1156 // kernel port, but call from userland
1157 return B_NOT_ALLOWED;
1160 info->selected_events &= B_EVENT_READ | B_EVENT_WRITE | B_EVENT_INVALID;
1162 if (info->selected_events != 0) {
1163 uint16 events = 0;
1165 info->next = portRef->select_infos;
1166 portRef->select_infos = info;
1168 // check for events
1169 if ((info->selected_events & B_EVENT_READ) != 0
1170 && !portRef->messages.IsEmpty()) {
1171 events |= B_EVENT_READ;
1174 if (portRef->write_count > 0)
1175 events |= B_EVENT_WRITE;
1177 if (events != 0)
1178 notify_select_events(info, events);
1181 return B_OK;
1185 status_t
1186 deselect_port(int32 id, struct select_info* info, bool kernel)
1188 if (id < 0)
1189 return B_BAD_PORT_ID;
1190 if (info->selected_events == 0)
1191 return B_OK;
1193 // get the port
1194 BReference<Port> portRef = get_locked_port(id);
1195 if (portRef == NULL)
1196 return B_BAD_PORT_ID;
1197 MutexLocker locker(portRef->lock, true);
1199 // find and remove the infos
1200 select_info** infoLocation = &portRef->select_infos;
1201 while (*infoLocation != NULL && *infoLocation != info)
1202 infoLocation = &(*infoLocation)->next;
1204 if (*infoLocation == info)
1205 *infoLocation = info->next;
1207 return B_OK;
1211 port_id
1212 find_port(const char* name)
1214 TRACE(("find_port(name = \"%s\")\n", name));
1216 if (!sPortsActive) {
1217 panic("ports used too early!\n");
1218 return B_NAME_NOT_FOUND;
1220 if (name == NULL)
1221 return B_BAD_VALUE;
1223 ReadLocker locker(sPortsLock);
1224 Port* port = sPortsByName.Lookup(name);
1225 // Since we have sPortsLock and don't return the port itself,
1226 // no BReference necessary
1228 if (port != NULL && port->state == Port::kActive)
1229 return port->id;
1231 return B_NAME_NOT_FOUND;
1235 status_t
1236 _get_port_info(port_id id, port_info* info, size_t size)
1238 TRACE(("get_port_info(id = %ld)\n", id));
1240 if (info == NULL || size != sizeof(port_info))
1241 return B_BAD_VALUE;
1242 if (!sPortsActive || id < 0)
1243 return B_BAD_PORT_ID;
1245 // get the port
1246 BReference<Port> portRef = get_locked_port(id);
1247 if (portRef == NULL) {
1248 TRACE(("get_port_info: invalid port_id %ld\n", id));
1249 return B_BAD_PORT_ID;
1251 MutexLocker locker(portRef->lock, true);
1253 // fill a port_info struct with info
1254 fill_port_info(portRef, info, size);
1255 return B_OK;
1259 status_t
1260 _get_next_port_info(team_id teamID, int32* _cookie, struct port_info* info,
1261 size_t size)
1263 TRACE(("get_next_port_info(team = %ld)\n", teamID));
1265 if (info == NULL || size != sizeof(port_info) || _cookie == NULL
1266 || teamID < 0) {
1267 return B_BAD_VALUE;
1269 if (!sPortsActive)
1270 return B_BAD_PORT_ID;
1272 Team* team = Team::Get(teamID);
1273 if (team == NULL)
1274 return B_BAD_TEAM_ID;
1275 BReference<Team> teamReference(team, true);
1277 // iterate through the team's port list
1278 const uint8 lockIndex = teamID % kTeamListLockCount;
1279 MutexLocker teamPortsListLocker(sTeamListLock[lockIndex]);
1281 int32 stopIndex = *_cookie;
1282 int32 index = 0;
1284 Port* port = (Port*)list_get_first_item(&team->port_list);
1285 while (port != NULL) {
1286 if (!is_port_closed(port)) {
1287 if (index == stopIndex)
1288 break;
1289 index++;
1292 port = (Port*)list_get_next_item(&team->port_list, port);
1295 if (port == NULL)
1296 return B_BAD_PORT_ID;
1298 // fill in the port info
1299 BReference<Port> portRef = port;
1300 teamPortsListLocker.Unlock();
1301 // Only use portRef below this line...
1303 MutexLocker locker(portRef->lock);
1304 fill_port_info(portRef, info, size);
1306 *_cookie = stopIndex + 1;
1307 return B_OK;
1311 ssize_t
1312 port_buffer_size(port_id id)
1314 return port_buffer_size_etc(id, 0, 0);
1318 ssize_t
1319 port_buffer_size_etc(port_id id, uint32 flags, bigtime_t timeout)
1321 port_message_info info;
1322 status_t error = get_port_message_info_etc(id, &info, flags, timeout);
1323 return error != B_OK ? error : info.size;
1327 status_t
1328 _get_port_message_info_etc(port_id id, port_message_info* info,
1329 size_t infoSize, uint32 flags, bigtime_t timeout)
1331 if (info == NULL || infoSize != sizeof(port_message_info))
1332 return B_BAD_VALUE;
1333 if (!sPortsActive || id < 0)
1334 return B_BAD_PORT_ID;
1336 flags &= B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT | B_RELATIVE_TIMEOUT
1337 | B_ABSOLUTE_TIMEOUT;
1339 // get the port
1340 BReference<Port> portRef = get_locked_port(id);
1341 if (portRef == NULL)
1342 return B_BAD_PORT_ID;
1343 MutexLocker locker(portRef->lock, true);
1345 if (is_port_closed(portRef) && portRef->messages.IsEmpty()) {
1346 T(Info(portRef, 0, B_BAD_PORT_ID));
1347 TRACE(("_get_port_message_info_etc(): closed port %ld\n", id));
1348 return B_BAD_PORT_ID;
1351 while (portRef->read_count == 0) {
1352 // We need to wait for a message to appear
1353 if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0)
1354 return B_WOULD_BLOCK;
1356 ConditionVariableEntry entry;
1357 portRef->read_condition.Add(&entry);
1359 locker.Unlock();
1361 // block if no message, or, if B_TIMEOUT flag set, block with timeout
1362 status_t status = entry.Wait(flags, timeout);
1364 if (status != B_OK) {
1365 T(Info(portRef, 0, status));
1366 return status;
1369 // re-lock
1370 BReference<Port> newPortRef = get_locked_port(id);
1371 if (newPortRef == NULL) {
1372 T(Info(id, 0, 0, 0, B_BAD_PORT_ID));
1373 return B_BAD_PORT_ID;
1375 locker.SetTo(newPortRef->lock, true);
1377 if (newPortRef != portRef
1378 || (is_port_closed(portRef) && portRef->messages.IsEmpty())) {
1379 // the port is no longer there
1380 T(Info(id, 0, 0, 0, B_BAD_PORT_ID));
1381 return B_BAD_PORT_ID;
1385 // determine tail & get the length of the message
1386 port_message* message = portRef->messages.Head();
1387 if (message == NULL) {
1388 panic("port %" B_PRId32 ": no messages found\n", portRef->id);
1389 return B_ERROR;
1392 info->size = message->size;
1393 info->sender = message->sender;
1394 info->sender_group = message->sender_group;
1395 info->sender_team = message->sender_team;
1397 T(Info(portRef, message->code, B_OK));
1399 // notify next one, as we haven't read from the port
1400 portRef->read_condition.NotifyOne();
1402 return B_OK;
1406 ssize_t
1407 port_count(port_id id)
1409 if (!sPortsActive || id < 0)
1410 return B_BAD_PORT_ID;
1412 // get the port
1413 BReference<Port> portRef = get_locked_port(id);
1414 if (portRef == NULL) {
1415 TRACE(("port_count: invalid port_id %ld\n", id));
1416 return B_BAD_PORT_ID;
1418 MutexLocker locker(portRef->lock, true);
1420 // return count of messages
1421 return portRef->read_count;
1425 ssize_t
1426 read_port(port_id port, int32* msgCode, void* buffer, size_t bufferSize)
1428 return read_port_etc(port, msgCode, buffer, bufferSize, 0, 0);
1432 ssize_t
1433 read_port_etc(port_id id, int32* _code, void* buffer, size_t bufferSize,
1434 uint32 flags, bigtime_t timeout)
1436 if (!sPortsActive || id < 0)
1437 return B_BAD_PORT_ID;
1438 if ((buffer == NULL && bufferSize > 0) || timeout < 0)
1439 return B_BAD_VALUE;
1441 bool userCopy = (flags & PORT_FLAG_USE_USER_MEMCPY) != 0;
1442 bool peekOnly = !userCopy && (flags & B_PEEK_PORT_MESSAGE) != 0;
1443 // TODO: we could allow peeking for user apps now
1445 flags &= B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT | B_RELATIVE_TIMEOUT
1446 | B_ABSOLUTE_TIMEOUT;
1448 // get the port
1449 BReference<Port> portRef = get_locked_port(id);
1450 if (portRef == NULL)
1451 return B_BAD_PORT_ID;
1452 MutexLocker locker(portRef->lock, true);
1454 if (is_port_closed(portRef) && portRef->messages.IsEmpty()) {
1455 T(Read(portRef, 0, B_BAD_PORT_ID));
1456 TRACE(("read_port_etc(): closed port %ld\n", id));
1457 return B_BAD_PORT_ID;
1460 while (portRef->read_count == 0) {
1461 if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0)
1462 return B_WOULD_BLOCK;
1464 // We need to wait for a message to appear
1465 ConditionVariableEntry entry;
1466 portRef->read_condition.Add(&entry);
1468 locker.Unlock();
1470 // block if no message, or, if B_TIMEOUT flag set, block with timeout
1471 status_t status = entry.Wait(flags, timeout);
1473 // re-lock
1474 BReference<Port> newPortRef = get_locked_port(id);
1475 if (newPortRef == NULL) {
1476 T(Read(id, 0, 0, 0, B_BAD_PORT_ID));
1477 return B_BAD_PORT_ID;
1479 locker.SetTo(newPortRef->lock, true);
1481 if (newPortRef != portRef
1482 || (is_port_closed(portRef) && portRef->messages.IsEmpty())) {
1483 // the port is no longer there
1484 T(Read(id, 0, 0, 0, B_BAD_PORT_ID));
1485 return B_BAD_PORT_ID;
1488 if (status != B_OK) {
1489 T(Read(portRef, 0, status));
1490 return status;
1494 // determine tail & get the length of the message
1495 port_message* message = portRef->messages.Head();
1496 if (message == NULL) {
1497 panic("port %" B_PRId32 ": no messages found\n", portRef->id);
1498 return B_ERROR;
1501 if (peekOnly) {
1502 size_t size = copy_port_message(message, _code, buffer, bufferSize,
1503 userCopy);
1505 T(Read(portRef, message->code, size));
1507 portRef->read_condition.NotifyOne();
1508 // we only peeked, but didn't grab the message
1509 return size;
1512 portRef->messages.RemoveHead();
1513 portRef->total_count++;
1514 portRef->write_count++;
1515 portRef->read_count--;
1517 notify_port_select_events(portRef, B_EVENT_WRITE);
1518 portRef->write_condition.NotifyOne();
1519 // make one spot in queue available again for write
1521 T(Read(portRef, message->code, std::min(bufferSize, message->size)));
1523 locker.Unlock();
1525 size_t size = copy_port_message(message, _code, buffer, bufferSize,
1526 userCopy);
1528 put_port_message(message);
1529 return size;
1533 status_t
1534 write_port(port_id id, int32 msgCode, const void* buffer, size_t bufferSize)
1536 iovec vec = { (void*)buffer, bufferSize };
1538 return writev_port_etc(id, msgCode, &vec, 1, bufferSize, 0, 0);
1542 status_t
1543 write_port_etc(port_id id, int32 msgCode, const void* buffer,
1544 size_t bufferSize, uint32 flags, bigtime_t timeout)
1546 iovec vec = { (void*)buffer, bufferSize };
1548 return writev_port_etc(id, msgCode, &vec, 1, bufferSize, flags, timeout);
1552 status_t
1553 writev_port_etc(port_id id, int32 msgCode, const iovec* msgVecs,
1554 size_t vecCount, size_t bufferSize, uint32 flags, bigtime_t timeout)
1556 if (!sPortsActive || id < 0)
1557 return B_BAD_PORT_ID;
1558 if (bufferSize > PORT_MAX_MESSAGE_SIZE)
1559 return B_BAD_VALUE;
1561 // mask irrelevant flags (for acquire_sem() usage)
1562 flags &= B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT | B_RELATIVE_TIMEOUT
1563 | B_ABSOLUTE_TIMEOUT;
1564 if ((flags & B_RELATIVE_TIMEOUT) != 0
1565 && timeout != B_INFINITE_TIMEOUT && timeout > 0) {
1566 // Make the timeout absolute, since we have more than one step where
1567 // we might have to wait
1568 flags = (flags & ~B_RELATIVE_TIMEOUT) | B_ABSOLUTE_TIMEOUT;
1569 timeout += system_time();
1572 bool userCopy = (flags & PORT_FLAG_USE_USER_MEMCPY) > 0;
1574 status_t status;
1575 port_message* message = NULL;
1577 // get the port
1578 BReference<Port> portRef = get_locked_port(id);
1579 if (portRef == NULL) {
1580 TRACE(("write_port_etc: invalid port_id %ld\n", id));
1581 return B_BAD_PORT_ID;
1583 MutexLocker locker(portRef->lock, true);
1585 if (is_port_closed(portRef)) {
1586 TRACE(("write_port_etc: port %ld closed\n", id));
1587 return B_BAD_PORT_ID;
1590 if (portRef->write_count <= 0) {
1591 if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0)
1592 return B_WOULD_BLOCK;
1594 portRef->write_count--;
1596 // We need to block in order to wait for a free message slot
1597 ConditionVariableEntry entry;
1598 portRef->write_condition.Add(&entry);
1600 locker.Unlock();
1602 status = entry.Wait(flags, timeout);
1604 // re-lock
1605 BReference<Port> newPortRef = get_locked_port(id);
1606 if (newPortRef == NULL) {
1607 T(Write(id, 0, 0, 0, 0, B_BAD_PORT_ID));
1608 return B_BAD_PORT_ID;
1610 locker.SetTo(newPortRef->lock, true);
1612 if (newPortRef != portRef || is_port_closed(portRef)) {
1613 // the port is no longer there
1614 T(Write(id, 0, 0, 0, 0, B_BAD_PORT_ID));
1615 return B_BAD_PORT_ID;
1618 if (status != B_OK)
1619 goto error;
1620 } else
1621 portRef->write_count--;
1623 status = get_port_message(msgCode, bufferSize, flags, timeout,
1624 &message, *portRef);
1625 if (status != B_OK) {
1626 if (status == B_BAD_PORT_ID) {
1627 // the port had to be unlocked and is now no longer there
1628 T(Write(id, 0, 0, 0, 0, B_BAD_PORT_ID));
1629 return B_BAD_PORT_ID;
1632 goto error;
1635 // sender credentials
1636 message->sender = geteuid();
1637 message->sender_group = getegid();
1638 message->sender_team = team_get_current_team_id();
1640 if (bufferSize > 0) {
1641 size_t offset = 0;
1642 for (uint32 i = 0; i < vecCount; i++) {
1643 size_t bytes = msgVecs[i].iov_len;
1644 if (bytes > bufferSize)
1645 bytes = bufferSize;
1647 if (userCopy) {
1648 status_t status = user_memcpy(message->buffer + offset,
1649 msgVecs[i].iov_base, bytes);
1650 if (status != B_OK) {
1651 put_port_message(message);
1652 goto error;
1654 } else
1655 memcpy(message->buffer + offset, msgVecs[i].iov_base, bytes);
1657 bufferSize -= bytes;
1658 if (bufferSize == 0)
1659 break;
1661 offset += bytes;
1665 portRef->messages.Add(message);
1666 portRef->read_count++;
1668 T(Write(id, portRef->read_count, portRef->write_count, message->code,
1669 message->size, B_OK));
1671 notify_port_select_events(portRef, B_EVENT_READ);
1672 portRef->read_condition.NotifyOne();
1673 return B_OK;
1675 error:
1676 // Give up our slot in the queue again, and let someone else
1677 // try and fail
1678 T(Write(id, portRef->read_count, portRef->write_count, 0, 0, status));
1679 portRef->write_count++;
1680 notify_port_select_events(portRef, B_EVENT_WRITE);
1681 portRef->write_condition.NotifyOne();
1683 return status;
1687 status_t
1688 set_port_owner(port_id id, team_id newTeamID)
1690 TRACE(("set_port_owner(id = %ld, team = %ld)\n", id, newTeamID));
1692 if (id < 0)
1693 return B_BAD_PORT_ID;
1695 // get the new team
1696 Team* team = Team::Get(newTeamID);
1697 if (team == NULL)
1698 return B_BAD_TEAM_ID;
1699 BReference<Team> teamReference(team, true);
1701 // get the port
1702 BReference<Port> portRef = get_locked_port(id);
1703 if (portRef == NULL) {
1704 TRACE(("set_port_owner: invalid port_id %ld\n", id));
1705 return B_BAD_PORT_ID;
1707 MutexLocker locker(portRef->lock, true);
1709 // transfer ownership to other team
1710 if (team->id != portRef->owner) {
1711 uint8 firstLockIndex = portRef->owner % kTeamListLockCount;
1712 uint8 secondLockIndex = team->id % kTeamListLockCount;
1714 // Avoid deadlocks: always lock lower index first
1715 if (secondLockIndex < firstLockIndex) {
1716 uint8 temp = secondLockIndex;
1717 secondLockIndex = firstLockIndex;
1718 firstLockIndex = temp;
1721 MutexLocker oldTeamPortsListLocker(sTeamListLock[firstLockIndex]);
1722 MutexLocker newTeamPortsListLocker;
1723 if (firstLockIndex != secondLockIndex) {
1724 newTeamPortsListLocker.SetTo(sTeamListLock[secondLockIndex],
1725 false);
1728 // Now that we have locked the team port lists, check the state again
1729 if (portRef->state == Port::kActive) {
1730 list_remove_link(&portRef->team_link);
1731 list_add_item(&team->port_list, portRef.Get());
1732 portRef->owner = team->id;
1733 } else {
1734 // Port was already deleted. We haven't changed anything yet so
1735 // we can cancel the operation.
1736 return B_BAD_PORT_ID;
1740 T(OwnerChange(portRef, team->id, B_OK));
1741 return B_OK;
1745 // #pragma mark - syscalls
1748 port_id
1749 _user_create_port(int32 queueLength, const char *userName)
1751 char name[B_OS_NAME_LENGTH];
1753 if (userName == NULL)
1754 return create_port(queueLength, NULL);
1756 if (!IS_USER_ADDRESS(userName)
1757 || user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK)
1758 return B_BAD_ADDRESS;
1760 return create_port(queueLength, name);
1764 status_t
1765 _user_close_port(port_id id)
1767 return close_port(id);
1771 status_t
1772 _user_delete_port(port_id id)
1774 return delete_port(id);
1778 port_id
1779 _user_find_port(const char *userName)
1781 char name[B_OS_NAME_LENGTH];
1783 if (userName == NULL)
1784 return B_BAD_VALUE;
1785 if (!IS_USER_ADDRESS(userName)
1786 || user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK)
1787 return B_BAD_ADDRESS;
1789 return find_port(name);
1793 status_t
1794 _user_get_port_info(port_id id, struct port_info *userInfo)
1796 struct port_info info;
1797 status_t status;
1799 if (userInfo == NULL)
1800 return B_BAD_VALUE;
1801 if (!IS_USER_ADDRESS(userInfo))
1802 return B_BAD_ADDRESS;
1804 status = get_port_info(id, &info);
1806 // copy back to user space
1807 if (status == B_OK
1808 && user_memcpy(userInfo, &info, sizeof(struct port_info)) < B_OK)
1809 return B_BAD_ADDRESS;
1811 return status;
1815 status_t
1816 _user_get_next_port_info(team_id team, int32 *userCookie,
1817 struct port_info *userInfo)
1819 struct port_info info;
1820 status_t status;
1821 int32 cookie;
1823 if (userCookie == NULL || userInfo == NULL)
1824 return B_BAD_VALUE;
1825 if (!IS_USER_ADDRESS(userCookie) || !IS_USER_ADDRESS(userInfo)
1826 || user_memcpy(&cookie, userCookie, sizeof(int32)) < B_OK)
1827 return B_BAD_ADDRESS;
1829 status = get_next_port_info(team, &cookie, &info);
1831 // copy back to user space
1832 if (user_memcpy(userCookie, &cookie, sizeof(int32)) < B_OK
1833 || (status == B_OK && user_memcpy(userInfo, &info,
1834 sizeof(struct port_info)) < B_OK))
1835 return B_BAD_ADDRESS;
1837 return status;
1841 ssize_t
1842 _user_port_buffer_size_etc(port_id port, uint32 flags, bigtime_t timeout)
1844 syscall_restart_handle_timeout_pre(flags, timeout);
1846 status_t status = port_buffer_size_etc(port, flags | B_CAN_INTERRUPT,
1847 timeout);
1849 return syscall_restart_handle_timeout_post(status, timeout);
1853 ssize_t
1854 _user_port_count(port_id port)
1856 return port_count(port);
1860 status_t
1861 _user_set_port_owner(port_id port, team_id team)
1863 return set_port_owner(port, team);
1867 ssize_t
1868 _user_read_port_etc(port_id port, int32 *userCode, void *userBuffer,
1869 size_t bufferSize, uint32 flags, bigtime_t timeout)
1871 int32 messageCode;
1872 ssize_t bytesRead;
1874 syscall_restart_handle_timeout_pre(flags, timeout);
1876 if (userBuffer == NULL && bufferSize != 0)
1877 return B_BAD_VALUE;
1878 if ((userCode != NULL && !IS_USER_ADDRESS(userCode))
1879 || (userBuffer != NULL && !IS_USER_ADDRESS(userBuffer)))
1880 return B_BAD_ADDRESS;
1882 bytesRead = read_port_etc(port, &messageCode, userBuffer, bufferSize,
1883 flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, timeout);
1885 if (bytesRead >= 0 && userCode != NULL
1886 && user_memcpy(userCode, &messageCode, sizeof(int32)) < B_OK)
1887 return B_BAD_ADDRESS;
1889 return syscall_restart_handle_timeout_post(bytesRead, timeout);
1893 status_t
1894 _user_write_port_etc(port_id port, int32 messageCode, const void *userBuffer,
1895 size_t bufferSize, uint32 flags, bigtime_t timeout)
1897 iovec vec = { (void *)userBuffer, bufferSize };
1899 syscall_restart_handle_timeout_pre(flags, timeout);
1901 if (userBuffer == NULL && bufferSize != 0)
1902 return B_BAD_VALUE;
1903 if (userBuffer != NULL && !IS_USER_ADDRESS(userBuffer))
1904 return B_BAD_ADDRESS;
1906 status_t status = writev_port_etc(port, messageCode, &vec, 1, bufferSize,
1907 flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, timeout);
1909 return syscall_restart_handle_timeout_post(status, timeout);
1913 status_t
1914 _user_writev_port_etc(port_id port, int32 messageCode, const iovec *userVecs,
1915 size_t vecCount, size_t bufferSize, uint32 flags, bigtime_t timeout)
1917 syscall_restart_handle_timeout_pre(flags, timeout);
1919 if (userVecs == NULL && bufferSize != 0)
1920 return B_BAD_VALUE;
1921 if (userVecs != NULL && !IS_USER_ADDRESS(userVecs))
1922 return B_BAD_ADDRESS;
1924 iovec *vecs = NULL;
1925 if (userVecs && vecCount != 0) {
1926 vecs = (iovec*)malloc(sizeof(iovec) * vecCount);
1927 if (vecs == NULL)
1928 return B_NO_MEMORY;
1930 if (user_memcpy(vecs, userVecs, sizeof(iovec) * vecCount) < B_OK) {
1931 free(vecs);
1932 return B_BAD_ADDRESS;
1936 status_t status = writev_port_etc(port, messageCode, vecs, vecCount,
1937 bufferSize, flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT,
1938 timeout);
1940 free(vecs);
1941 return syscall_restart_handle_timeout_post(status, timeout);
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)
1949 if (userInfo == NULL || infoSize != sizeof(port_message_info))
1950 return B_BAD_VALUE;
1952 syscall_restart_handle_timeout_pre(flags, timeout);
1954 port_message_info info;
1955 status_t error = _get_port_message_info_etc(port, &info, sizeof(info),
1956 flags | B_CAN_INTERRUPT, timeout);
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;
1964 return syscall_restart_handle_timeout_post(error, timeout);