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.
24 #include <AutoDeleter.h>
29 #include <Notifications.h>
31 #include <syscall_restart.h>
34 #include <util/AutoLock.h>
35 #include <util/list.h>
37 #include <wait_for_objects.h>
42 # define TRACE(x) dprintf x
50 // GCC >= 3.1 doesn't need it anymore
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
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
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.
91 struct port_message
: DoublyLinkedListLinkImpl
<port_message
> {
100 typedef DoublyLinkedList
<port_message
> MessageList
;
105 static void put_port_message(port_message
* message
);
110 struct Port
: public KernelReferenceable
{
117 struct list_link team_link
;
121 Port
* name_hash_link
;
128 ConditionVariable read_condition
;
129 ConditionVariable write_condition
;
131 // messages read from port since creation
132 select_info
* select_infos
;
133 MessageList messages
;
135 Port(team_id owner
, int32 queueLength
, char* name
)
139 capacity(queueLength
),
142 write_count(queueLength
),
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");
155 while (port_message
* message
= messages
.RemoveHead())
156 put_port_message(message
);
158 free((char*)lock
.name
);
164 struct PortHashDefinition
{
165 typedef port_id KeyType
;
166 typedef Port ValueType
;
168 size_t HashKey(port_id key
) const
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
);
204 for (size_t index
= 0; index
< length
; index
++)
205 hash
= 31 * hash
+ key
[index
];
210 size_t Hash(Port
* value
) const
212 size_t& hash
= value
->name_hash
;
214 hash
= HashKey(value
->lock
.name
);
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
{
234 PortNotificationService();
236 void Notify(uint32 opcode
, port_id team
);
242 // #pragma mark - tracing
246 namespace PortTracing
{
248 class Create
: public AbstractTraceEntry
{
254 fCapacity(port
->capacity
)
256 fName
= alloc_tracing_buffer_strcpy(port
->lock
.name
, B_OS_NAME_LENGTH
,
262 virtual void AddDump(TraceOutput
& out
)
264 out
.Print("port %ld created, name \"%s\", owner %ld, capacity %ld",
265 fID
, fName
, fOwner
, fCapacity
);
276 class Delete
: public AbstractTraceEntry
{
285 virtual void AddDump(TraceOutput
& out
)
287 out
.Print("port %ld deleted", fID
);
295 class Read
: public AbstractTraceEntry
{
297 Read(const BReference
<Port
>& portRef
, int32 code
, ssize_t result
)
300 fReadCount(portRef
->read_count
),
301 fWriteCount(portRef
->write_count
),
308 Read(port_id id
, int32 readCount
, int32 writeCount
, int32 code
,
312 fReadCount(readCount
),
313 fWriteCount(writeCount
),
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
);
335 class Write
: public AbstractTraceEntry
{
337 Write(port_id id
, int32 readCount
, int32 writeCount
, int32 code
,
338 size_t bufferSize
, ssize_t result
)
341 fReadCount(readCount
),
342 fWriteCount(writeCount
),
344 fBufferSize(bufferSize
),
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
);
366 class Info
: public AbstractTraceEntry
{
368 Info(const BReference
<Port
>& portRef
, int32 code
, ssize_t result
)
371 fReadCount(portRef
->read_count
),
372 fWriteCount(portRef
->write_count
),
379 Info(port_id id
, int32 readCount
, int32 writeCount
, int32 code
,
383 fReadCount(readCount
),
384 fWriteCount(writeCount
),
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
);
406 class OwnerChange
: public AbstractTraceEntry
{
408 OwnerChange(Port
* port
, team_id newOwner
, status_t status
)
411 fOldOwner(port
->owner
),
418 virtual void AddDump(TraceOutput
& out
)
420 out
.Print("port %ld owner change from %ld to %ld: %s", fID
, fOldOwner
,
421 fNewOwner
, strerror(fStatus
));
431 } // namespace PortTracing
433 # define T(x) new(std::nothrow) PortTracing::x;
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");
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")
488 PortNotificationService::Notify(uint32 opcode
, port_id port
)
490 char eventBuffer
[128];
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
504 dump_port_list(int argc
, char** argv
)
506 const char* name
= NULL
;
510 if (!strcmp(argv
[1], "team") || !strcmp(argv
[1], "owner"))
511 owner
= strtoul(argv
[2], NULL
, 0);
512 else if (!strcmp(argv
[1], "name"))
515 owner
= strtoul(argv
[1], NULL
, 0);
517 kprintf("port id cap read-cnt write-cnt total team "
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
))
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
);
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
);
564 dump_port_info(int argc
, char** argv
)
566 ConditionVariable
* condition
= NULL
;
567 const char* name
= NULL
;
570 print_debugger_command_usage(argv
[0]);
575 if (!strcmp(argv
[1], "address")) {
576 _dump_port_info((Port
*)parse_expression(argv
[2]));
578 } else if (!strcmp(argv
[1], "condition"))
579 condition
= (ConditionVariable
*)parse_expression(argv
[2]);
580 else if (!strcmp(argv
[1], "name"))
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",
591 _dump_port_info(port
);
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
);
612 // #pragma mark - internal helper functions
615 /*! Notifies the port's select events.
616 The port must be locked.
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
)
630 BReference
<Port
> portRef
;
633 ReadLocker
portsLocker(sPortsLock
);
634 portRef
.SetTo(sPorts
.Lookup(id
));
637 if (portRef
!= NULL
&& portRef
->state
== Port::kActive
)
638 mutex_lock(&portRef
->lock
);
646 static BReference
<Port
>
647 get_port(port_id id
) GCC_2_NRV(portRef
)
650 BReference
<Port
> portRef
;
652 ReadLocker
portsLocker(sPortsLock
);
653 portRef
.SetTo(sPorts
.Lookup(id
));
659 /*! You need to own the port's lock when calling this function */
661 is_port_closed(Port
* port
)
663 return port
->capacity
== 0;
668 put_port_message(port_message
* message
)
670 const size_t size
= sizeof(port_message
) + message
->size
;
673 atomic_add(&sTotalSpaceCommited
, -size
);
674 if (sWaitingForSpace
> 0)
675 sNoSpaceCondition
.NotifyAll();
679 /*! Port must be locked. */
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
;
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
714 status_t status
= entry
.Wait(flags
, timeout
);
716 atomic_add(&sWaitingForSpace
, -1);
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
)
729 previouslyCommited
= atomic_add(&sTotalSpaceCommited
, size
);
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
;
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
);
751 /*! Fills the port_info structure with information from the specified
753 The port's lock must be held when called.
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
);
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
);
778 *_code
= message
->code
;
782 status_t status
= user_memcpy(buffer
, message
->buffer
, size
);
786 memcpy(buffer
, message
->buffer
, size
);
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.)
814 delete_port_logical(Port
* port
)
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
824 // Logical deletion succesful
828 // Someone else already deleted it in the meantime
829 TRACE(("delete_port_logical: already deleted port_id %ld\n",
831 return B_BAD_PORT_ID
;
834 // Port is still being created, retry
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.
851 delete_owned_ports(Team
* team
)
853 TRACE(("delete_owned_ports(owner = %ld)\n", team
->id
));
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
);
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
);
886 port
= (Port
*)list_get_next_item(&deletionList
, 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);
899 port
->ReleaseReference();
900 // Reference for team port list
913 port_used_ports(void)
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
;
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!");
939 new(&sPortsByName
) PortNameHashTable
;
940 if (sPortsByName
.Init() != B_OK
) {
941 panic("Failed to init port by name hash table!");
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();
971 // #pragma mark - public kernel API
975 create_port(int32 queueLength
, const char* name
)
977 TRACE(("create_port(queueLength = %ld, name = \"%s\")\n", queueLength
,
981 panic("ports used too early!\n");
982 return B_BAD_PORT_ID
;
984 if (queueLength
< 1 || queueLength
> MAX_QUEUE_LENGTH
)
987 Team
* team
= thread_get_current_thread()->team
;
989 return B_BAD_TEAM_ID
;
992 char* nameBuffer
= strdup(name
!= NULL
? name
: "unnamed port");
993 if (nameBuffer
== NULL
)
997 Port
* port
= new(std::nothrow
) Port(team_get_current_team_id(), queueLength
,
1004 // check the ports limit
1005 const int32 previouslyUsed
= atomic_add(&sUsedPorts
, 1);
1006 if (previouslyUsed
+ 1 >= sMaxPorts
) {
1007 atomic_add(&sUsedPorts
, -1);
1009 return B_NO_MORE_PORTS
;
1013 WriteLocker
locker(sPortsLock
);
1015 // allocate a port ID
1017 port
->id
= sNextPortID
++;
1019 // handle integer overflow
1020 if (sNextPortID
< 0)
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.
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
);
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
;
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
);
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
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
);
1138 atomic_add(&sUsedPorts
, -1);
1145 select_port(int32 id
, struct select_info
* info
, bool kernel
)
1148 return B_BAD_PORT_ID
;
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) {
1170 info
->next
= portRef
->select_infos
;
1171 portRef
->select_infos
= info
;
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
;
1183 notify_select_events(info
, events
);
1191 deselect_port(int32 id
, struct select_info
* info
, bool kernel
)
1194 return B_BAD_PORT_ID
;
1195 if (info
->selected_events
== 0)
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
;
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
;
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
)
1236 return B_NAME_NOT_FOUND
;
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
))
1247 if (!sPortsActive
|| id
< 0)
1248 return B_BAD_PORT_ID
;
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
);
1265 _get_next_port_info(team_id teamID
, int32
* _cookie
, struct port_info
* info
,
1268 TRACE(("get_next_port_info(team = %ld)\n", teamID
));
1270 if (info
== NULL
|| size
!= sizeof(port_info
) || _cookie
== NULL
1275 return B_BAD_PORT_ID
;
1277 Team
* team
= Team::Get(teamID
);
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
;
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
)
1297 port
= (Port
*)list_get_next_item(&team
->port_list
, port
);
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;
1317 port_buffer_size(port_id id
)
1319 return port_buffer_size_etc(id
, 0, 0);
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
;
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
))
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
;
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
);
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
));
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
);
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();
1412 port_count(port_id id
)
1414 if (!sPortsActive
|| id
< 0)
1415 return B_BAD_PORT_ID
;
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
;
1431 read_port(port_id port
, int32
* msgCode
, void* buffer
, size_t bufferSize
)
1433 return read_port_etc(port
, msgCode
, buffer
, bufferSize
, 0, 0);
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)
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
;
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
);
1475 // block if no message, or, if B_TIMEOUT flag set, block with timeout
1476 status_t status
= entry
.Wait(flags
, timeout
);
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
));
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
);
1507 size_t size
= copy_port_message(message
, _code
, buffer
, bufferSize
,
1510 T(Read(portRef
, message
->code
, size
));
1512 portRef
->read_condition
.NotifyOne();
1513 // we only peeked, but didn't grab the message
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
)));
1530 size_t size
= copy_port_message(message
, _code
, buffer
, bufferSize
,
1533 put_port_message(message
);
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);
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
);
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
)
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;
1580 port_message
* message
= NULL
;
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
);
1607 status
= entry
.Wait(flags
, timeout
);
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
;
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
;
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) {
1647 for (uint32 i
= 0; i
< vecCount
; i
++) {
1648 size_t bytes
= msgVecs
[i
].iov_len
;
1649 if (bytes
> bufferSize
)
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
);
1660 memcpy(message
->buffer
+ offset
, msgVecs
[i
].iov_base
, bytes
);
1662 bufferSize
-= bytes
;
1663 if (bufferSize
== 0)
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();
1681 // Give up our slot in the queue again, and let someone else
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();
1693 set_port_owner(port_id id
, team_id newTeamID
)
1695 TRACE(("set_port_owner(id = %ld, team = %ld)\n", id
, newTeamID
));
1698 return B_BAD_PORT_ID
;
1701 Team
* team
= Team::Get(newTeamID
);
1703 return B_BAD_TEAM_ID
;
1704 BReference
<Team
> teamReference(team
, true);
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
],
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
;
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
));
1750 // #pragma mark - syscalls
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
);
1770 _user_close_port(port_id id
)
1772 return close_port(id
);
1777 _user_delete_port(port_id id
)
1779 return delete_port(id
);
1784 _user_find_port(const char *userName
)
1786 char name
[B_OS_NAME_LENGTH
];
1788 if (userName
== NULL
)
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
);
1799 _user_get_port_info(port_id id
, struct port_info
*userInfo
)
1801 struct port_info info
;
1804 if (userInfo
== NULL
)
1806 if (!IS_USER_ADDRESS(userInfo
))
1807 return B_BAD_ADDRESS
;
1809 status
= get_port_info(id
, &info
);
1811 // copy back to user space
1813 && user_memcpy(userInfo
, &info
, sizeof(struct port_info
)) < B_OK
)
1814 return B_BAD_ADDRESS
;
1821 _user_get_next_port_info(team_id team
, int32
*userCookie
,
1822 struct port_info
*userInfo
)
1824 struct port_info info
;
1828 if (userCookie
== NULL
|| userInfo
== NULL
)
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
;
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
,
1854 return syscall_restart_handle_timeout_post(status
, timeout
);
1859 _user_port_count(port_id port
)
1861 return port_count(port
);
1866 _user_set_port_owner(port_id port
, team_id team
)
1868 return set_port_owner(port
, team
);
1873 _user_read_port_etc(port_id port
, int32
*userCode
, void *userBuffer
,
1874 size_t bufferSize
, uint32 flags
, bigtime_t timeout
)
1879 syscall_restart_handle_timeout_pre(flags
, timeout
);
1881 if (userBuffer
== NULL
&& bufferSize
!= 0)
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
);
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)
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
);
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)
1926 if (userVecs
!= NULL
&& !IS_USER_ADDRESS(userVecs
))
1927 return B_BAD_ADDRESS
;
1930 if (userVecs
&& vecCount
!= 0) {
1931 vecs
= (iovec
*)malloc(sizeof(iovec
) * vecCount
);
1935 if (user_memcpy(vecs
, userVecs
, sizeof(iovec
) * vecCount
) < B_OK
) {
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
,
1946 return syscall_restart_handle_timeout_post(status
, timeout
);
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
))
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
);