headers/bsd: Add sys/queue.h.
[haiku.git] / src / system / kernel / port.cpp
blobe9dff1780b24b5170a85a0c9aa1fe22ae20452d3
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 namespace {
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];
100 typedef DoublyLinkedList<port_message> MessageList;
102 } // namespace
105 static void put_port_message(port_message* message);
108 namespace {
110 struct Port : public KernelReferenceable {
111 enum State {
112 kUnused = 0,
113 kActive,
114 kDeleted
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;
135 Port(team_id owner, int32 queueLength, char* name)
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)
146 // id is initialized when the caller adds the port to the hash table
148 mutex_init(&lock, name);
149 read_condition.Init(this, "port read");
150 write_condition.Init(this, "port write");
153 virtual ~Port()
155 while (port_message* message = messages.RemoveHead())
156 put_port_message(message);
158 free((char*)lock.name);
159 lock.name = NULL;
164 struct PortHashDefinition {
165 typedef port_id KeyType;
166 typedef Port ValueType;
168 size_t HashKey(port_id key) const
170 return key;
173 size_t Hash(Port* value) const
175 return HashKey(value->id);
178 bool Compare(port_id key, Port* value) const
180 return value->id == key;
183 Port*& GetLink(Port* value) const
185 return value->hash_link;
189 typedef BOpenHashTable<PortHashDefinition> PortHashTable;
192 struct PortNameHashDefinition {
193 typedef const char* KeyType;
194 typedef Port ValueType;
196 size_t HashKey(const char* key) const
198 // Hash function: hash(key) = key[0] * 31^(length - 1)
199 // + key[1] * 31^(length - 2) + ... + key[length - 1]
201 const size_t length = strlen(key);
203 size_t hash = 0;
204 for (size_t index = 0; index < length; index++)
205 hash = 31 * hash + key[index];
207 return hash;
210 size_t Hash(Port* value) const
212 size_t& hash = value->name_hash;
213 if (hash == 0)
214 hash = HashKey(value->lock.name);
215 return hash;
218 bool Compare(const char* key, Port* value) const
220 return (strcmp(key, value->lock.name) == 0);
223 Port*& GetLink(Port* value) const
225 return value->name_hash_link;
229 typedef BOpenHashTable<PortNameHashDefinition> PortNameHashTable;
232 class PortNotificationService : public DefaultNotificationService {
233 public:
234 PortNotificationService();
236 void Notify(uint32 opcode, port_id team);
239 } // namespace
242 // #pragma mark - tracing
245 #if PORT_TRACING
246 namespace PortTracing {
248 class Create : public AbstractTraceEntry {
249 public:
250 Create(Port* port)
252 fID(port->id),
253 fOwner(port->owner),
254 fCapacity(port->capacity)
256 fName = alloc_tracing_buffer_strcpy(port->lock.name, B_OS_NAME_LENGTH,
257 false);
259 Initialized();
262 virtual void AddDump(TraceOutput& out)
264 out.Print("port %ld created, name \"%s\", owner %ld, capacity %ld",
265 fID, fName, fOwner, fCapacity);
268 private:
269 port_id fID;
270 char* fName;
271 team_id fOwner;
272 int32 fCapacity;
276 class Delete : public AbstractTraceEntry {
277 public:
278 Delete(Port* port)
280 fID(port->id)
282 Initialized();
285 virtual void AddDump(TraceOutput& out)
287 out.Print("port %ld deleted", fID);
290 private:
291 port_id fID;
295 class Read : public AbstractTraceEntry {
296 public:
297 Read(const BReference<Port>& portRef, int32 code, ssize_t result)
299 fID(portRef->id),
300 fReadCount(portRef->read_count),
301 fWriteCount(portRef->write_count),
302 fCode(code),
303 fResult(result)
305 Initialized();
308 Read(port_id id, int32 readCount, int32 writeCount, int32 code,
309 ssize_t result)
311 fID(id),
312 fReadCount(readCount),
313 fWriteCount(writeCount),
314 fCode(code),
315 fResult(result)
317 Initialized();
320 virtual void AddDump(TraceOutput& out)
322 out.Print("port %ld read, read %ld, write %ld, code %lx: %ld",
323 fID, fReadCount, fWriteCount, fCode, fResult);
326 private:
327 port_id fID;
328 int32 fReadCount;
329 int32 fWriteCount;
330 int32 fCode;
331 ssize_t fResult;
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)
340 fID(id),
341 fReadCount(readCount),
342 fWriteCount(writeCount),
343 fCode(code),
344 fBufferSize(bufferSize),
345 fResult(result)
347 Initialized();
350 virtual void AddDump(TraceOutput& out)
352 out.Print("port %ld write, read %ld, write %ld, code %lx, size %ld: %ld",
353 fID, fReadCount, fWriteCount, fCode, fBufferSize, fResult);
356 private:
357 port_id fID;
358 int32 fReadCount;
359 int32 fWriteCount;
360 int32 fCode;
361 size_t fBufferSize;
362 ssize_t fResult;
366 class Info : public AbstractTraceEntry {
367 public:
368 Info(const BReference<Port>& portRef, int32 code, ssize_t result)
370 fID(portRef->id),
371 fReadCount(portRef->read_count),
372 fWriteCount(portRef->write_count),
373 fCode(code),
374 fResult(result)
376 Initialized();
379 Info(port_id id, int32 readCount, int32 writeCount, int32 code,
380 ssize_t result)
382 fID(id),
383 fReadCount(readCount),
384 fWriteCount(writeCount),
385 fCode(code),
386 fResult(result)
388 Initialized();
391 virtual void AddDump(TraceOutput& out)
393 out.Print("port %ld info, read %ld, write %ld, code %lx: %ld",
394 fID, fReadCount, fWriteCount, fCode, fResult);
397 private:
398 port_id fID;
399 int32 fReadCount;
400 int32 fWriteCount;
401 int32 fCode;
402 ssize_t fResult;
406 class OwnerChange : public AbstractTraceEntry {
407 public:
408 OwnerChange(Port* port, team_id newOwner, status_t status)
410 fID(port->id),
411 fOldOwner(port->owner),
412 fNewOwner(newOwner),
413 fStatus(status)
415 Initialized();
418 virtual void AddDump(TraceOutput& out)
420 out.Print("port %ld owner change from %ld to %ld: %s", fID, fOldOwner,
421 fNewOwner, strerror(fStatus));
424 private:
425 port_id fID;
426 team_id fOldOwner;
427 team_id fNewOwner;
428 status_t fStatus;
431 } // namespace PortTracing
433 # define T(x) new(std::nothrow) PortTracing::x;
434 #else
435 # define T(x) ;
436 #endif
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;
444 #define MAX_QUEUE_LENGTH 4096
445 #define PORT_MAX_MESSAGE_SIZE (256 * 1024)
447 static int32 sMaxPorts = 4096;
448 static int32 sUsedPorts;
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");
459 enum {
460 kTeamListLockCount = 8
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")
474 static PortNotificationService sNotificationService;
477 // #pragma mark - TeamNotificationService
480 PortNotificationService::PortNotificationService()
482 DefaultNotificationService("ports")
487 void
488 PortNotificationService::Notify(uint32 opcode, port_id port)
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);
496 DefaultNotificationService::Notify(event, opcode);
500 // #pragma mark - debugger commands
503 static int
504 dump_port_list(int argc, char** argv)
506 const char* name = NULL;
507 team_id owner = -1;
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);
517 kprintf("port id cap read-cnt write-cnt total team "
518 "name\n");
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;
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);
532 return 0;
536 static void
537 _dump_port_info(Port* port)
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);
548 if (!port->messages.IsEmpty()) {
549 kprintf("messages:\n");
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);
557 set_debug_variable("_port", (addr_t)port);
558 set_debug_variable("_portID", port->id);
559 set_debug_variable("_owner", port->owner);
563 static int
564 dump_port_info(int argc, char** argv)
566 ConditionVariable* condition = NULL;
567 const char* name = NULL;
569 if (argc < 2) {
570 print_debugger_command_usage(argv[0]);
571 return 0;
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;
591 _dump_port_info(port);
592 return 0;
593 } else
594 name = argv[1];
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;
608 return 0;
612 // #pragma mark - internal helper functions
615 /*! Notifies the port's select events.
616 The port must be locked.
618 static void
619 notify_port_select_events(Port* port, uint16 events)
621 if (port->select_infos)
622 notify_select_events_list(port->select_infos, events);
626 static BReference<Port>
627 get_locked_port(port_id id) GCC_2_NRV(portRef)
629 #if __GNUC__ >= 3
630 BReference<Port> portRef;
631 #endif
633 ReadLocker portsLocker(sPortsLock);
634 portRef.SetTo(sPorts.Lookup(id));
637 if (portRef != NULL && portRef->state == Port::kActive)
638 mutex_lock(&portRef->lock);
639 else
640 portRef.Unset();
642 return portRef;
646 static BReference<Port>
647 get_port(port_id id) GCC_2_NRV(portRef)
649 #if __GNUC__ >= 3
650 BReference<Port> portRef;
651 #endif
652 ReadLocker portsLocker(sPortsLock);
653 portRef.SetTo(sPorts.Lookup(id));
655 return portRef;
659 /*! You need to own the port's lock when calling this function */
660 static inline bool
661 is_port_closed(Port* port)
663 return port->capacity == 0;
667 static void
668 put_port_message(port_message* message)
670 const size_t size = sizeof(port_message) + message->size;
671 free(message);
673 atomic_add(&sTotalSpaceCommited, -size);
674 if (sWaitingForSpace > 0)
675 sNoSpaceCondition.NotifyAll();
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)
684 const size_t size = sizeof(port_message) + bufferSize;
686 while (true) {
687 int32 previouslyCommited = atomic_add(&sTotalSpaceCommited, size);
689 while (previouslyCommited + size > kTotalSpaceLimit) {
690 // TODO: add per team limit
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.
696 atomic_add(&sTotalSpaceCommited, -size);
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;
703 ConditionVariableEntry entry;
704 sNoSpaceCondition.Add(&entry);
706 port_id portID = port.id;
707 mutex_unlock(&port.lock);
709 atomic_add(&sWaitingForSpace, 1);
711 // TODO: right here the condition could be notified and we'd
712 // miss it.
714 status_t status = entry.Wait(flags, timeout);
716 atomic_add(&sWaitingForSpace, -1);
718 // re-lock the port
719 BReference<Port> newPortRef = get_locked_port(portID);
721 if (newPortRef.Get() != &port || is_port_closed(&port)) {
722 // the port is no longer usable
723 return B_BAD_PORT_ID;
726 if (status == B_TIMED_OUT)
727 return B_TIMED_OUT;
729 previouslyCommited = atomic_add(&sTotalSpaceCommited, size);
730 continue;
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;
739 *_message = message;
740 return B_OK;
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;
751 /*! Fills the port_info structure with information from the specified
752 port.
753 The port's lock must be held when called.
755 static void
756 fill_port_info(Port* port, port_info* info, size_t size)
758 info->port = port->id;
759 info->team = port->owner;
760 info->capacity = port->capacity;
762 info->queue_count = port->read_count;
763 info->total_count = port->total_count;
765 strlcpy(info->name, port->lock.name, B_OS_NAME_LENGTH);
769 static ssize_t
770 copy_port_message(port_message* message, int32* _code, void* buffer,
771 size_t bufferSize, bool userCopy)
773 // check output buffer size
774 size_t size = std::min(bufferSize, message->size);
776 // copy message
777 if (_code != NULL)
778 *_code = message->code;
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);
789 return size;
793 static void
794 uninit_port(Port* port)
796 MutexLocker locker(port->lock);
798 notify_port_select_events(port, B_EVENT_INVALID);
799 port->select_infos = NULL;
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);
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.)
813 static status_t
814 delete_port_logical(Port* port)
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
822 switch (oldState) {
823 case Port::kActive:
824 // Logical deletion succesful
825 return B_OK;
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;
833 case Port::kUnused:
834 // Port is still being created, retry
835 continue;
837 default:
838 // Port state got corrupted somehow
839 panic("Invalid port state!\n");
845 // #pragma mark - private kernel API
848 /*! This function deletes all the ports that are owned by the passed team.
850 void
851 delete_owned_ports(Team* team)
853 TRACE(("delete_owned_ports(owner = %ld)\n", team->id));
855 list deletionList;
856 list_init_etc(&deletionList, port_team_link_offset());
858 const uint8 lockIndex = team->id % kTeamListLockCount;
859 MutexLocker teamPortsListLocker(sTeamListLock[lockIndex]);
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
868 Port* nextPort = (Port*)list_get_next_item(&team->port_list, port);
870 if (status == B_OK) {
871 list_remove_link(&port->team_link);
872 list_add_item(&deletionList, port);
875 port = nextPort;
878 teamPortsListLocker.Unlock();
880 // Remove all ports in deletionList from hashes
882 WriteLocker portsLocker(sPortsLock);
884 for (Port* port = (Port*)list_get_first_item(&deletionList);
885 port != NULL;
886 port = (Port*)list_get_next_item(&deletionList, port)) {
888 sPorts.Remove(port);
889 sPortsByName.Remove(port);
890 port->ReleaseReference();
891 // joint reference for sPorts and sPortsByName
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
905 int32
906 port_max_ports(void)
908 return sMaxPorts;
912 int32
913 port_used_ports(void)
915 return sUsedPorts;
919 size_t
920 port_team_link_offset()
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;
929 status_t
930 port_init(kernel_args *args)
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;
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;
945 sNoSpaceCondition.Init(&sPorts, "port space");
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);
964 new(&sNotificationService) PortNotificationService();
965 sNotificationService.Register();
966 sPortsActive = true;
967 return B_OK;
971 // #pragma mark - public kernel API
974 port_id
975 create_port(int32 queueLength, const char* name)
977 TRACE(("create_port(queueLength = %ld, name = \"%s\")\n", queueLength,
978 name));
980 if (!sPortsActive) {
981 panic("ports used too early!\n");
982 return B_BAD_PORT_ID;
984 if (queueLength < 1 || queueLength > MAX_QUEUE_LENGTH)
985 return B_BAD_VALUE;
987 Team* team = thread_get_current_thread()->team;
988 if (team == NULL)
989 return B_BAD_TEAM_ID;
991 // check & dup name
992 char* nameBuffer = strdup(name != NULL ? name : "unnamed port");
993 if (nameBuffer == NULL)
994 return B_NO_MEMORY;
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;
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;
1013 WriteLocker locker(sPortsLock);
1015 // allocate a port ID
1016 do {
1017 port->id = sNextPortID++;
1019 // handle integer overflow
1020 if (sNextPortID < 0)
1021 sNextPortID = 1;
1022 } while (sPorts.Lookup(port->id) != NULL);
1024 // Insert port physically:
1025 // (1/2) Insert into hash tables
1026 port->AcquireReference();
1027 // joint reference for sPorts and sPortsByName
1029 sPorts.Insert(port);
1030 sPortsByName.Insert(port);
1033 // (2/2) Insert into team list
1035 const uint8 lockIndex = port->owner % kTeamListLockCount;
1036 MutexLocker teamPortsListLocker(sTeamListLock[lockIndex]);
1037 port->AcquireReference();
1038 list_add_item(&team->port_list, port);
1041 // tracing, notifications, etc.
1042 T(Create(port));
1044 const port_id id = port->id;
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
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");
1056 TRACE(("create_port() done: port created %ld\n", id));
1058 sNotificationService.Notify(PORT_ADDED, id);
1059 return id;
1063 status_t
1064 close_port(port_id id)
1066 TRACE(("close_port(id = %ld)\n", id));
1068 if (!sPortsActive || id < 0)
1069 return B_BAD_PORT_ID;
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;
1077 MutexLocker lock(&portRef->lock, true);
1079 // mark port to disable writing - deleting the semaphores will
1080 // wake up waiting read/writes
1081 portRef->capacity = 0;
1083 notify_port_select_events(portRef, B_EVENT_INVALID);
1084 portRef->select_infos = NULL;
1086 portRef->read_condition.NotifyAll(B_BAD_PORT_ID);
1087 portRef->write_condition.NotifyAll(B_BAD_PORT_ID);
1089 return B_OK;
1093 status_t
1094 delete_port(port_id id)
1096 TRACE(("delete_port(id = %ld)\n", id));
1098 if (!sPortsActive || id < 0)
1099 return B_BAD_PORT_ID;
1101 BReference<Port> portRef = get_port(id);
1103 if (portRef == NULL) {
1104 TRACE(("delete_port: invalid port_id %ld\n", id));
1105 return B_BAD_PORT_ID;
1108 status_t status = delete_port_logical(portRef);
1109 // Contains linearization point
1110 if (status != B_OK)
1111 return status;
1113 // Now remove port physically:
1114 // (1/2) Remove from hash tables
1116 WriteLocker portsLocker(sPortsLock);
1118 sPorts.Remove(portRef);
1119 sPortsByName.Remove(portRef);
1121 portRef->ReleaseReference();
1122 // joint reference for sPorts and sPortsByName
1125 // (2/2) Remove from team port list
1127 const uint8 lockIndex = portRef->owner % kTeamListLockCount;
1128 MutexLocker teamPortsListLocker(sTeamListLock[lockIndex]);
1130 list_remove_link(&portRef->team_link);
1131 portRef->ReleaseReference();
1134 uninit_port(portRef);
1136 T(Delete(portRef));
1138 atomic_add(&sUsedPorts, -1);
1140 return B_OK;
1144 status_t
1145 select_port(int32 id, struct select_info* info, bool kernel)
1147 if (id < 0)
1148 return B_BAD_PORT_ID;
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);
1156 // port must not yet be closed
1157 if (is_port_closed(portRef))
1158 return B_BAD_PORT_ID;
1160 if (!kernel && portRef->owner == team_get_kernel_team_id()) {
1161 // kernel port, but call from userland
1162 return B_NOT_ALLOWED;
1165 info->selected_events &= B_EVENT_READ | B_EVENT_WRITE | B_EVENT_INVALID;
1167 if (info->selected_events != 0) {
1168 uint16 events = 0;
1170 info->next = portRef->select_infos;
1171 portRef->select_infos = info;
1173 // check for events
1174 if ((info->selected_events & B_EVENT_READ) != 0
1175 && !portRef->messages.IsEmpty()) {
1176 events |= B_EVENT_READ;
1179 if (portRef->write_count > 0)
1180 events |= B_EVENT_WRITE;
1182 if (events != 0)
1183 notify_select_events(info, events);
1186 return B_OK;
1190 status_t
1191 deselect_port(int32 id, struct select_info* info, bool kernel)
1193 if (id < 0)
1194 return B_BAD_PORT_ID;
1195 if (info->selected_events == 0)
1196 return B_OK;
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);
1204 // find and remove the infos
1205 select_info** infoLocation = &portRef->select_infos;
1206 while (*infoLocation != NULL && *infoLocation != info)
1207 infoLocation = &(*infoLocation)->next;
1209 if (*infoLocation == info)
1210 *infoLocation = info->next;
1212 return B_OK;
1216 port_id
1217 find_port(const char* name)
1219 TRACE(("find_port(name = \"%s\")\n", name));
1221 if (!sPortsActive) {
1222 panic("ports used too early!\n");
1223 return B_NAME_NOT_FOUND;
1225 if (name == NULL)
1226 return B_BAD_VALUE;
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
1233 if (port != NULL && port->state == Port::kActive)
1234 return port->id;
1236 return B_NAME_NOT_FOUND;
1240 status_t
1241 _get_port_info(port_id id, port_info* info, size_t size)
1243 TRACE(("get_port_info(id = %ld)\n", id));
1245 if (info == NULL || size != sizeof(port_info))
1246 return B_BAD_VALUE;
1247 if (!sPortsActive || id < 0)
1248 return B_BAD_PORT_ID;
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;
1256 MutexLocker locker(portRef->lock, true);
1258 // fill a port_info struct with info
1259 fill_port_info(portRef, info, size);
1260 return B_OK;
1264 status_t
1265 _get_next_port_info(team_id teamID, int32* _cookie, struct port_info* info,
1266 size_t size)
1268 TRACE(("get_next_port_info(team = %ld)\n", teamID));
1270 if (info == NULL || size != sizeof(port_info) || _cookie == NULL
1271 || teamID < 0) {
1272 return B_BAD_VALUE;
1274 if (!sPortsActive)
1275 return B_BAD_PORT_ID;
1277 Team* team = Team::Get(teamID);
1278 if (team == NULL)
1279 return B_BAD_TEAM_ID;
1280 BReference<Team> teamReference(team, true);
1282 // iterate through the team's port list
1283 const uint8 lockIndex = teamID % kTeamListLockCount;
1284 MutexLocker teamPortsListLocker(sTeamListLock[lockIndex]);
1286 int32 stopIndex = *_cookie;
1287 int32 index = 0;
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++;
1297 port = (Port*)list_get_next_item(&team->port_list, port);
1300 if (port == NULL)
1301 return B_BAD_PORT_ID;
1303 // fill in the port info
1304 BReference<Port> portRef = port;
1305 teamPortsListLocker.Unlock();
1306 // Only use portRef below this line...
1308 MutexLocker locker(portRef->lock);
1309 fill_port_info(portRef, info, size);
1311 *_cookie = stopIndex + 1;
1312 return B_OK;
1316 ssize_t
1317 port_buffer_size(port_id id)
1319 return port_buffer_size_etc(id, 0, 0);
1323 ssize_t
1324 port_buffer_size_etc(port_id id, uint32 flags, bigtime_t timeout)
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;
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)
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;
1341 flags &= B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT | B_RELATIVE_TIMEOUT
1342 | B_ABSOLUTE_TIMEOUT;
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);
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;
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;
1361 ConditionVariableEntry entry;
1362 portRef->read_condition.Add(&entry);
1364 locker.Unlock();
1366 // block if no message, or, if B_TIMEOUT flag set, block with timeout
1367 status_t status = entry.Wait(flags, timeout);
1369 if (status != B_OK) {
1370 T(Info(portRef, 0, status));
1371 return status;
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;
1380 locker.SetTo(newPortRef->lock, true);
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;
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;
1397 info->size = message->size;
1398 info->sender = message->sender;
1399 info->sender_group = message->sender_group;
1400 info->sender_team = message->sender_team;
1402 T(Info(portRef, message->code, B_OK));
1404 // notify next one, as we haven't read from the port
1405 portRef->read_condition.NotifyOne();
1407 return B_OK;
1411 ssize_t
1412 port_count(port_id id)
1414 if (!sPortsActive || id < 0)
1415 return B_BAD_PORT_ID;
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;
1423 MutexLocker locker(portRef->lock, true);
1425 // return count of messages
1426 return portRef->read_count;
1430 ssize_t
1431 read_port(port_id port, int32* msgCode, void* buffer, size_t bufferSize)
1433 return read_port_etc(port, msgCode, buffer, bufferSize, 0, 0);
1437 ssize_t
1438 read_port_etc(port_id id, int32* _code, void* buffer, size_t bufferSize,
1439 uint32 flags, bigtime_t timeout)
1441 if (!sPortsActive || id < 0)
1442 return B_BAD_PORT_ID;
1443 if ((buffer == NULL && bufferSize > 0) || timeout < 0)
1444 return B_BAD_VALUE;
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
1450 flags &= B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT | B_RELATIVE_TIMEOUT
1451 | B_ABSOLUTE_TIMEOUT;
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);
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;
1465 while (portRef->read_count == 0) {
1466 if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0)
1467 return B_WOULD_BLOCK;
1469 // We need to wait for a message to appear
1470 ConditionVariableEntry entry;
1471 portRef->read_condition.Add(&entry);
1473 locker.Unlock();
1475 // block if no message, or, if B_TIMEOUT flag set, block with timeout
1476 status_t status = entry.Wait(flags, timeout);
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;
1484 locker.SetTo(newPortRef->lock, true);
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;
1493 if (status != B_OK) {
1494 T(Read(portRef, 0, status));
1495 return status;
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;
1506 if (peekOnly) {
1507 size_t size = copy_port_message(message, _code, buffer, bufferSize,
1508 userCopy);
1510 T(Read(portRef, message->code, size));
1512 portRef->read_condition.NotifyOne();
1513 // we only peeked, but didn't grab the message
1514 return size;
1517 portRef->messages.RemoveHead();
1518 portRef->total_count++;
1519 portRef->write_count++;
1520 portRef->read_count--;
1522 notify_port_select_events(portRef, B_EVENT_WRITE);
1523 portRef->write_condition.NotifyOne();
1524 // make one spot in queue available again for write
1526 T(Read(portRef, message->code, std::min(bufferSize, message->size)));
1528 locker.Unlock();
1530 size_t size = copy_port_message(message, _code, buffer, bufferSize,
1531 userCopy);
1533 put_port_message(message);
1534 return size;
1538 status_t
1539 write_port(port_id id, int32 msgCode, const void* buffer, size_t bufferSize)
1541 iovec vec = { (void*)buffer, bufferSize };
1543 return writev_port_etc(id, msgCode, &vec, 1, bufferSize, 0, 0);
1547 status_t
1548 write_port_etc(port_id id, int32 msgCode, const void* buffer,
1549 size_t bufferSize, uint32 flags, bigtime_t timeout)
1551 iovec vec = { (void*)buffer, bufferSize };
1553 return writev_port_etc(id, msgCode, &vec, 1, bufferSize, flags, timeout);
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)
1561 if (!sPortsActive || id < 0)
1562 return B_BAD_PORT_ID;
1563 if (bufferSize > PORT_MAX_MESSAGE_SIZE)
1564 return B_BAD_VALUE;
1566 // mask irrelevant flags (for acquire_sem() usage)
1567 flags &= B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT | B_RELATIVE_TIMEOUT
1568 | B_ABSOLUTE_TIMEOUT;
1569 if ((flags & B_RELATIVE_TIMEOUT) != 0
1570 && timeout != B_INFINITE_TIMEOUT && timeout > 0) {
1571 // Make the timeout absolute, since we have more than one step where
1572 // we might have to wait
1573 flags = (flags & ~B_RELATIVE_TIMEOUT) | B_ABSOLUTE_TIMEOUT;
1574 timeout += system_time();
1577 bool userCopy = (flags & PORT_FLAG_USE_USER_MEMCPY) > 0;
1579 status_t status;
1580 port_message* message = NULL;
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;
1588 MutexLocker locker(portRef->lock, true);
1590 if (is_port_closed(portRef)) {
1591 TRACE(("write_port_etc: port %ld closed\n", id));
1592 return B_BAD_PORT_ID;
1595 if (portRef->write_count <= 0) {
1596 if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0)
1597 return B_WOULD_BLOCK;
1599 portRef->write_count--;
1601 // We need to block in order to wait for a free message slot
1602 ConditionVariableEntry entry;
1603 portRef->write_condition.Add(&entry);
1605 locker.Unlock();
1607 status = entry.Wait(flags, timeout);
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;
1615 locker.SetTo(newPortRef->lock, true);
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;
1623 if (status != B_OK)
1624 goto error;
1625 } else
1626 portRef->write_count--;
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;
1637 goto error;
1640 // sender credentials
1641 message->sender = geteuid();
1642 message->sender_group = getegid();
1643 message->sender_team = team_get_current_team_id();
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;
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;
1659 } else
1660 memcpy(message->buffer + offset, msgVecs[i].iov_base, bytes);
1662 bufferSize -= bytes;
1663 if (bufferSize == 0)
1664 break;
1666 offset += bytes;
1670 portRef->messages.Add(message);
1671 portRef->read_count++;
1673 T(Write(id, portRef->read_count, portRef->write_count, message->code,
1674 message->size, B_OK));
1676 notify_port_select_events(portRef, B_EVENT_READ);
1677 portRef->read_condition.NotifyOne();
1678 return B_OK;
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();
1688 return status;
1692 status_t
1693 set_port_owner(port_id id, team_id newTeamID)
1695 TRACE(("set_port_owner(id = %ld, team = %ld)\n", id, newTeamID));
1697 if (id < 0)
1698 return B_BAD_PORT_ID;
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);
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;
1712 MutexLocker locker(portRef->lock, true);
1714 // transfer ownership to other team
1715 if (team->id != portRef->owner) {
1716 uint8 firstLockIndex = portRef->owner % kTeamListLockCount;
1717 uint8 secondLockIndex = team->id % kTeamListLockCount;
1719 // Avoid deadlocks: always lock lower index first
1720 if (secondLockIndex < firstLockIndex) {
1721 uint8 temp = secondLockIndex;
1722 secondLockIndex = firstLockIndex;
1723 firstLockIndex = temp;
1726 MutexLocker oldTeamPortsListLocker(sTeamListLock[firstLockIndex]);
1727 MutexLocker newTeamPortsListLocker;
1728 if (firstLockIndex != secondLockIndex) {
1729 newTeamPortsListLocker.SetTo(sTeamListLock[secondLockIndex],
1730 false);
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;
1745 T(OwnerChange(portRef, team->id, B_OK));
1746 return B_OK;
1750 // #pragma mark - syscalls
1753 port_id
1754 _user_create_port(int32 queueLength, const char *userName)
1756 char name[B_OS_NAME_LENGTH];
1758 if (userName == NULL)
1759 return create_port(queueLength, NULL);
1761 if (!IS_USER_ADDRESS(userName)
1762 || user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK)
1763 return B_BAD_ADDRESS;
1765 return create_port(queueLength, name);
1769 status_t
1770 _user_close_port(port_id id)
1772 return close_port(id);
1776 status_t
1777 _user_delete_port(port_id id)
1779 return delete_port(id);
1783 port_id
1784 _user_find_port(const char *userName)
1786 char name[B_OS_NAME_LENGTH];
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;
1794 return find_port(name);
1798 status_t
1799 _user_get_port_info(port_id id, struct port_info *userInfo)
1801 struct port_info info;
1802 status_t status;
1804 if (userInfo == NULL)
1805 return B_BAD_VALUE;
1806 if (!IS_USER_ADDRESS(userInfo))
1807 return B_BAD_ADDRESS;
1809 status = get_port_info(id, &info);
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;
1816 return status;
1820 status_t
1821 _user_get_next_port_info(team_id team, int32 *userCookie,
1822 struct port_info *userInfo)
1824 struct port_info info;
1825 status_t status;
1826 int32 cookie;
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;
1834 status = get_next_port_info(team, &cookie, &info);
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;
1842 return status;
1846 ssize_t
1847 _user_port_buffer_size_etc(port_id port, uint32 flags, bigtime_t timeout)
1849 syscall_restart_handle_timeout_pre(flags, timeout);
1851 status_t status = port_buffer_size_etc(port, flags | B_CAN_INTERRUPT,
1852 timeout);
1854 return syscall_restart_handle_timeout_post(status, timeout);
1858 ssize_t
1859 _user_port_count(port_id port)
1861 return port_count(port);
1865 status_t
1866 _user_set_port_owner(port_id port, team_id team)
1868 return set_port_owner(port, team);
1872 ssize_t
1873 _user_read_port_etc(port_id port, int32 *userCode, void *userBuffer,
1874 size_t bufferSize, uint32 flags, bigtime_t timeout)
1876 int32 messageCode;
1877 ssize_t bytesRead;
1879 syscall_restart_handle_timeout_pre(flags, timeout);
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;
1887 bytesRead = read_port_etc(port, &messageCode, userBuffer, bufferSize,
1888 flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, timeout);
1890 if (bytesRead >= 0 && userCode != NULL
1891 && user_memcpy(userCode, &messageCode, sizeof(int32)) < B_OK)
1892 return B_BAD_ADDRESS;
1894 return syscall_restart_handle_timeout_post(bytesRead, timeout);
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)
1902 iovec vec = { (void *)userBuffer, bufferSize };
1904 syscall_restart_handle_timeout_pre(flags, timeout);
1906 if (userBuffer == NULL && bufferSize != 0)
1907 return B_BAD_VALUE;
1908 if (userBuffer != NULL && !IS_USER_ADDRESS(userBuffer))
1909 return B_BAD_ADDRESS;
1911 status_t status = writev_port_etc(port, messageCode, &vec, 1, bufferSize,
1912 flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, timeout);
1914 return syscall_restart_handle_timeout_post(status, timeout);
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)
1922 syscall_restart_handle_timeout_pre(flags, timeout);
1924 if (userVecs == NULL && bufferSize != 0)
1925 return B_BAD_VALUE;
1926 if (userVecs != NULL && !IS_USER_ADDRESS(userVecs))
1927 return B_BAD_ADDRESS;
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;
1935 if (user_memcpy(vecs, userVecs, sizeof(iovec) * vecCount) < B_OK) {
1936 free(vecs);
1937 return B_BAD_ADDRESS;
1941 status_t status = writev_port_etc(port, messageCode, vecs, vecCount,
1942 bufferSize, flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT,
1943 timeout);
1945 free(vecs);
1946 return syscall_restart_handle_timeout_post(status, timeout);
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)
1954 if (userInfo == NULL || infoSize != sizeof(port_message_info))
1955 return B_BAD_VALUE;
1957 syscall_restart_handle_timeout_pre(flags, timeout);
1959 port_message_info info;
1960 status_t error = _get_port_message_info_etc(port, &info, sizeof(info),
1961 flags | B_CAN_INTERRUPT, timeout);
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;
1969 return syscall_restart_handle_timeout_post(error, timeout);