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.
92 static void put_port_message(port_message
* message
);
95 struct port_message
: DoublyLinkedListLinkImpl
<port_message
> {
104 typedef DoublyLinkedList
<port_message
> MessageList
;
107 struct Port
: public KernelReferenceable
{
114 struct list_link team_link
;
118 Port
* name_hash_link
;
125 ConditionVariable read_condition
;
126 ConditionVariable write_condition
;
128 // messages read from port since creation
129 select_info
* select_infos
;
130 MessageList messages
;
132 Port(team_id owner
, int32 queueLength
, char* name
)
136 capacity(queueLength
),
139 write_count(queueLength
),
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");
152 while (port_message
* message
= messages
.RemoveHead())
153 put_port_message(message
);
155 free((char*)lock
.name
);
161 struct PortHashDefinition
{
162 typedef port_id KeyType
;
163 typedef Port ValueType
;
165 size_t HashKey(port_id key
) const
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
);
201 for (size_t index
= 0; index
< length
; index
++)
202 hash
= 31 * hash
+ key
[index
];
207 size_t Hash(Port
* value
) const
209 size_t& hash
= value
->name_hash
;
211 hash
= HashKey(value
->lock
.name
);
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
{
231 PortNotificationService();
233 void Notify(uint32 opcode
, port_id team
);
237 // #pragma mark - tracing
241 namespace PortTracing
{
243 class Create
: public AbstractTraceEntry
{
249 fCapacity(port
->capacity
)
251 fName
= alloc_tracing_buffer_strcpy(port
->lock
.name
, B_OS_NAME_LENGTH
,
257 virtual void AddDump(TraceOutput
& out
)
259 out
.Print("port %ld created, name \"%s\", owner %ld, capacity %ld",
260 fID
, fName
, fOwner
, fCapacity
);
271 class Delete
: public AbstractTraceEntry
{
280 virtual void AddDump(TraceOutput
& out
)
282 out
.Print("port %ld deleted", fID
);
290 class Read
: public AbstractTraceEntry
{
292 Read(const BReference
<Port
>& portRef
, int32 code
, ssize_t result
)
295 fReadCount(portRef
->read_count
),
296 fWriteCount(portRef
->write_count
),
303 Read(port_id id
, int32 readCount
, int32 writeCount
, int32 code
,
307 fReadCount(readCount
),
308 fWriteCount(writeCount
),
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
);
330 class Write
: public AbstractTraceEntry
{
332 Write(port_id id
, int32 readCount
, int32 writeCount
, int32 code
,
333 size_t bufferSize
, ssize_t result
)
336 fReadCount(readCount
),
337 fWriteCount(writeCount
),
339 fBufferSize(bufferSize
),
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
);
361 class Info
: public AbstractTraceEntry
{
363 Info(const BReference
<Port
>& portRef
, int32 code
, ssize_t result
)
366 fReadCount(portRef
->read_count
),
367 fWriteCount(portRef
->write_count
),
374 Info(port_id id
, int32 readCount
, int32 writeCount
, int32 code
,
378 fReadCount(readCount
),
379 fWriteCount(writeCount
),
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
);
401 class OwnerChange
: public AbstractTraceEntry
{
403 OwnerChange(Port
* port
, team_id newOwner
, status_t status
)
406 fOldOwner(port
->owner
),
413 virtual void AddDump(TraceOutput
& out
)
415 out
.Print("port %ld owner change from %ld to %ld: %s", fID
, fOldOwner
,
416 fNewOwner
, strerror(fStatus
));
426 } // namespace PortTracing
428 # define T(x) new(std::nothrow) PortTracing::x;
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");
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")
483 PortNotificationService::Notify(uint32 opcode
, port_id port
)
485 char eventBuffer
[128];
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
499 dump_port_list(int argc
, char** argv
)
501 const char* name
= NULL
;
505 if (!strcmp(argv
[1], "team") || !strcmp(argv
[1], "owner"))
506 owner
= strtoul(argv
[2], NULL
, 0);
507 else if (!strcmp(argv
[1], "name"))
510 owner
= strtoul(argv
[1], NULL
, 0);
512 kprintf("port id cap read-cnt write-cnt total team "
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
))
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
);
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
);
559 dump_port_info(int argc
, char** argv
)
561 ConditionVariable
* condition
= NULL
;
562 const char* name
= NULL
;
565 print_debugger_command_usage(argv
[0]);
570 if (!strcmp(argv
[1], "address")) {
571 _dump_port_info((Port
*)parse_expression(argv
[2]));
573 } else if (!strcmp(argv
[1], "condition"))
574 condition
= (ConditionVariable
*)parse_expression(argv
[2]);
575 else if (!strcmp(argv
[1], "name"))
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",
586 _dump_port_info(port
);
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
);
607 // #pragma mark - internal helper functions
610 /*! Notifies the port's select events.
611 The port must be locked.
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
)
625 BReference
<Port
> portRef
;
628 ReadLocker
portsLocker(sPortsLock
);
629 portRef
.SetTo(sPorts
.Lookup(id
));
632 if (portRef
!= NULL
&& portRef
->state
== Port::kActive
)
633 mutex_lock(&portRef
->lock
);
641 static BReference
<Port
>
642 get_port(port_id id
) GCC_2_NRV(portRef
)
645 BReference
<Port
> portRef
;
647 ReadLocker
portsLocker(sPortsLock
);
648 portRef
.SetTo(sPorts
.Lookup(id
));
654 /*! You need to own the port's lock when calling this function */
656 is_port_closed(Port
* port
)
658 return port
->capacity
== 0;
663 put_port_message(port_message
* message
)
665 const size_t size
= sizeof(port_message
) + message
->size
;
668 atomic_add(&sTotalSpaceCommited
, -size
);
669 if (sWaitingForSpace
> 0)
670 sNoSpaceCondition
.NotifyAll();
674 /*! Port must be locked. */
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
;
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
709 status_t status
= entry
.Wait(flags
, timeout
);
711 atomic_add(&sWaitingForSpace
, -1);
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
)
724 previouslyCommited
= atomic_add(&sTotalSpaceCommited
, size
);
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
;
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
);
746 /*! Fills the port_info structure with information from the specified
748 The port's lock must be held when called.
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
);
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
);
773 *_code
= message
->code
;
777 status_t status
= user_memcpy(buffer
, message
->buffer
, size
);
781 memcpy(buffer
, message
->buffer
, size
);
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.)
809 delete_port_logical(Port
* port
)
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
819 // Logical deletion succesful
823 // Someone else already deleted it in the meantime
824 TRACE(("delete_port_logical: already deleted port_id %ld\n",
826 return B_BAD_PORT_ID
;
829 // Port is still being created, retry
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.
846 delete_owned_ports(Team
* team
)
848 TRACE(("delete_owned_ports(owner = %ld)\n", team
->id
));
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
);
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
);
881 port
= (Port
*)list_get_next_item(&deletionList
, 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);
894 port
->ReleaseReference();
895 // Reference for team port list
908 port_used_ports(void)
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
;
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!");
934 new(&sPortsByName
) PortNameHashTable
;
935 if (sPortsByName
.Init() != B_OK
) {
936 panic("Failed to init port by name hash table!");
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();
966 // #pragma mark - public kernel API
970 create_port(int32 queueLength
, const char* name
)
972 TRACE(("create_port(queueLength = %ld, name = \"%s\")\n", queueLength
,
976 panic("ports used too early!\n");
977 return B_BAD_PORT_ID
;
979 if (queueLength
< 1 || queueLength
> MAX_QUEUE_LENGTH
)
982 Team
* team
= thread_get_current_thread()->team
;
984 return B_BAD_TEAM_ID
;
987 char* nameBuffer
= strdup(name
!= NULL
? name
: "unnamed port");
988 if (nameBuffer
== NULL
)
992 Port
* port
= new(std::nothrow
) Port(team_get_current_team_id(), queueLength
,
999 // check the ports limit
1000 const int32 previouslyUsed
= atomic_add(&sUsedPorts
, 1);
1001 if (previouslyUsed
+ 1 >= sMaxPorts
) {
1002 atomic_add(&sUsedPorts
, -1);
1004 return B_NO_MORE_PORTS
;
1008 WriteLocker
locker(sPortsLock
);
1010 // allocate a port ID
1012 port
->id
= sNextPortID
++;
1014 // handle integer overflow
1015 if (sNextPortID
< 0)
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.
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
);
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
;
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
);
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
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
);
1133 atomic_add(&sUsedPorts
, -1);
1140 select_port(int32 id
, struct select_info
* info
, bool kernel
)
1143 return B_BAD_PORT_ID
;
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) {
1165 info
->next
= portRef
->select_infos
;
1166 portRef
->select_infos
= info
;
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
;
1178 notify_select_events(info
, events
);
1186 deselect_port(int32 id
, struct select_info
* info
, bool kernel
)
1189 return B_BAD_PORT_ID
;
1190 if (info
->selected_events
== 0)
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
;
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
;
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
)
1231 return B_NAME_NOT_FOUND
;
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
))
1242 if (!sPortsActive
|| id
< 0)
1243 return B_BAD_PORT_ID
;
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
);
1260 _get_next_port_info(team_id teamID
, int32
* _cookie
, struct port_info
* info
,
1263 TRACE(("get_next_port_info(team = %ld)\n", teamID
));
1265 if (info
== NULL
|| size
!= sizeof(port_info
) || _cookie
== NULL
1270 return B_BAD_PORT_ID
;
1272 Team
* team
= Team::Get(teamID
);
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
;
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
)
1292 port
= (Port
*)list_get_next_item(&team
->port_list
, port
);
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;
1312 port_buffer_size(port_id id
)
1314 return port_buffer_size_etc(id
, 0, 0);
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
;
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
))
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
;
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
);
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
));
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
);
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();
1407 port_count(port_id id
)
1409 if (!sPortsActive
|| id
< 0)
1410 return B_BAD_PORT_ID
;
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
;
1426 read_port(port_id port
, int32
* msgCode
, void* buffer
, size_t bufferSize
)
1428 return read_port_etc(port
, msgCode
, buffer
, bufferSize
, 0, 0);
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)
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
;
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
);
1470 // block if no message, or, if B_TIMEOUT flag set, block with timeout
1471 status_t status
= entry
.Wait(flags
, timeout
);
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
));
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
);
1502 size_t size
= copy_port_message(message
, _code
, buffer
, bufferSize
,
1505 T(Read(portRef
, message
->code
, size
));
1507 portRef
->read_condition
.NotifyOne();
1508 // we only peeked, but didn't grab the message
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
)));
1525 size_t size
= copy_port_message(message
, _code
, buffer
, bufferSize
,
1528 put_port_message(message
);
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);
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
);
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
)
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;
1575 port_message
* message
= NULL
;
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
);
1602 status
= entry
.Wait(flags
, timeout
);
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
;
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
;
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) {
1642 for (uint32 i
= 0; i
< vecCount
; i
++) {
1643 size_t bytes
= msgVecs
[i
].iov_len
;
1644 if (bytes
> bufferSize
)
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
);
1655 memcpy(message
->buffer
+ offset
, msgVecs
[i
].iov_base
, bytes
);
1657 bufferSize
-= bytes
;
1658 if (bufferSize
== 0)
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();
1676 // Give up our slot in the queue again, and let someone else
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();
1688 set_port_owner(port_id id
, team_id newTeamID
)
1690 TRACE(("set_port_owner(id = %ld, team = %ld)\n", id
, newTeamID
));
1693 return B_BAD_PORT_ID
;
1696 Team
* team
= Team::Get(newTeamID
);
1698 return B_BAD_TEAM_ID
;
1699 BReference
<Team
> teamReference(team
, true);
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
],
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
;
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
));
1745 // #pragma mark - syscalls
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
);
1765 _user_close_port(port_id id
)
1767 return close_port(id
);
1772 _user_delete_port(port_id id
)
1774 return delete_port(id
);
1779 _user_find_port(const char *userName
)
1781 char name
[B_OS_NAME_LENGTH
];
1783 if (userName
== NULL
)
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
);
1794 _user_get_port_info(port_id id
, struct port_info
*userInfo
)
1796 struct port_info info
;
1799 if (userInfo
== NULL
)
1801 if (!IS_USER_ADDRESS(userInfo
))
1802 return B_BAD_ADDRESS
;
1804 status
= get_port_info(id
, &info
);
1806 // copy back to user space
1808 && user_memcpy(userInfo
, &info
, sizeof(struct port_info
)) < B_OK
)
1809 return B_BAD_ADDRESS
;
1816 _user_get_next_port_info(team_id team
, int32
*userCookie
,
1817 struct port_info
*userInfo
)
1819 struct port_info info
;
1823 if (userCookie
== NULL
|| userInfo
== NULL
)
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
;
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
,
1849 return syscall_restart_handle_timeout_post(status
, timeout
);
1854 _user_port_count(port_id port
)
1856 return port_count(port
);
1861 _user_set_port_owner(port_id port
, team_id team
)
1863 return set_port_owner(port
, team
);
1868 _user_read_port_etc(port_id port
, int32
*userCode
, void *userBuffer
,
1869 size_t bufferSize
, uint32 flags
, bigtime_t timeout
)
1874 syscall_restart_handle_timeout_pre(flags
, timeout
);
1876 if (userBuffer
== NULL
&& bufferSize
!= 0)
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
);
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)
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
);
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)
1921 if (userVecs
!= NULL
&& !IS_USER_ADDRESS(userVecs
))
1922 return B_BAD_ADDRESS
;
1925 if (userVecs
&& vecCount
!= 0) {
1926 vecs
= (iovec
*)malloc(sizeof(iovec
) * vecCount
);
1930 if (user_memcpy(vecs
, userVecs
, sizeof(iovec
) * vecCount
) < B_OK
) {
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
,
1941 return syscall_restart_handle_timeout_post(status
, timeout
);
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
))
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
);