1 /*
2 * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2002-2015, Axel Dörfler, axeld@pinc-software.de.
4 * Distributed under the terms of the MIT License.
5 *
6 * Copyright 2001, Mark-Jan Bastian. All rights reserved.
7 * Distributed under the terms of the NewOS License.
8 */
9
10
11 /*! Ports for IPC */
12
13
14 #include <port.h>
15
16 #include <algorithm>
17 #include <ctype.h>
18 #include <iovec.h>
19 #include <stdlib.h>
20 #include <string.h>
21
22 #include <OS.h>
23
24 #include <AutoDeleter.h>
25 #include <StackOrHeapArray.h>
26
27 #include <arch/int.h>
28 #include <heap.h>
29 #include <kernel.h>
30 #include <Notifications.h>
31 #include <sem.h>
32 #include <syscall_restart.h>
33 #include <team.h>
34 #include <tracing.h>
35 #include <util/AutoLock.h>
36 #include <util/list.h>
37 #include <util/iovec_support.h>
38 #include <vm/vm.h>
39 #include <wait_for_objects.h>
40
41
42 //#define TRACE_PORTS
43 #ifdef TRACE_PORTS
44 # define TRACE(x) dprintf x
45 #else
46 # define TRACE(x)
47 #endif
48
49
50 #if __GNUC__ >= 3
51 # define GCC_2_NRV(x)
52 // GCC >= 3.1 doesn't need it anymore
53 #else
54 # define GCC_2_NRV(x) return x;
55 // GCC 2 named return value syntax
56 // see http://gcc.gnu.org/onlinedocs/gcc-2.95.2/gcc_5.html#SEC106
57 #endif
58
59
60 // Locking:
61 // * sPortsLock: Protects the sPorts and sPortsByName hash tables.
62 // * sTeamListLock[]: Protects Team::port_list. Lock index for given team is
63 // (Team::id % kTeamListLockCount).
64 // * Port::lock: Protects all Port members save team_link, hash_link, lock and
65 // state. id is immutable.
66 //
67 // Port::state ensures atomicity by providing a linearization point for adding
68 // and removing ports to the hash tables and the team port list.
69 // * sPortsLock and sTeamListLock[] are locked separately and not in a nested
70 // fashion, so a port can be in the hash table but not in the team port list
71 // or vice versa. => Without further provisions, insertion and removal are
72 // not linearizable and thus not concurrency-safe.
73 // * To make insertion and removal linearizable, Port::state was added. It is
74 // always only accessed atomically and updates are done using
75 // atomic_test_and_set(). A port is only seen as existent when its state is
76 // Port::kActive.
77 // * Deletion of ports is done in two steps: logical and physical deletion.
78 // First, logical deletion happens and sets Port::state to Port::kDeleted.
79 // This is an atomic operation and from then on, functions like
80 // get_locked_port() consider this port as deleted and ignore it. Secondly,
81 // physical deletion removes the port from hash tables and team port list.
82 // In a similar way, port creation first inserts into hashes and team list
83 // and only then sets port to Port::kActive.
84 // This creates a linearization point at the atomic update of Port::state,
85 // operations become linearizable and thus concurrency-safe. To help
86 // understanding, the linearization points are annotated with comments.
87 // * Ports are reference-counted so it's not a problem when someone still
88 // has a reference to a deleted port.
89
90
91 namespace {
92
93 struct port_message : DoublyLinkedListLinkImpl<port_message> {
94 int32 code;
95 size_t size;
96 uid_t sender;
97 gid_t sender_group;
98 team_id sender_team;
99 char buffer[0];
100 };
101
102 typedef DoublyLinkedList<port_message> MessageList;
103
104 } // namespace
105
106
107 static void put_port_message(port_message* message);
108
109
110 namespace {
111
112 struct Port : public KernelReferenceable {
113 enum State {
114 kUnused = 0,
115 kActive,
116 kDeleted
117 };
118
119 struct list_link team_link;
120 Port* hash_link;
121 port_id id;
122 team_id owner;
123 Port* name_hash_link;
124 size_t name_hash;
125 int32 capacity;
126 mutex lock;
127 int32 state;
128 uint32 read_count;
129 int32 write_count;
130 ConditionVariable read_condition;
131 ConditionVariable write_condition;
132 int32 total_count;
133 // messages read from port since creation
134 select_info* select_infos;
135 MessageList messages;
136
Port__anone3cefd910211::Port137 Port(team_id owner, int32 queueLength, const char* name)
138 :
139 owner(owner),
140 name_hash(0),
141 capacity(queueLength),
142 state(kUnused),
143 read_count(0),
144 write_count(queueLength),
145 total_count(0),
146 select_infos(NULL)
147 {
148 // id is initialized when the caller adds the port to the hash table
149
150 mutex_init_etc(&lock, name, MUTEX_FLAG_CLONE_NAME);
151 read_condition.Init(this, "port read");
152 write_condition.Init(this, "port write");
153 }
154
~Port__anone3cefd910211::Port155 virtual ~Port()
156 {
157 while (port_message* message = messages.RemoveHead())
158 put_port_message(message);
159
160 mutex_destroy(&lock);
161 }
162 };
163
164
165 struct PortHashDefinition {
166 typedef port_id KeyType;
167 typedef Port ValueType;
168
HashKey__anone3cefd910211::PortHashDefinition169 size_t HashKey(port_id key) const
170 {
171 return key;
172 }
173
Hash__anone3cefd910211::PortHashDefinition174 size_t Hash(Port* value) const
175 {
176 return HashKey(value->id);
177 }
178
Compare__anone3cefd910211::PortHashDefinition179 bool Compare(port_id key, Port* value) const
180 {
181 return value->id == key;
182 }
183
GetLink__anone3cefd910211::PortHashDefinition184 Port*& GetLink(Port* value) const
185 {
186 return value->hash_link;
187 }
188 };
189
190 typedef BOpenHashTable<PortHashDefinition> PortHashTable;
191
192
193 struct PortNameHashDefinition {
194 typedef const char* KeyType;
195 typedef Port ValueType;
196
HashKey__anone3cefd910211::PortNameHashDefinition197 size_t HashKey(const char* key) const
198 {
199 // Hash function: hash(key) = key[0] * 31^(length - 1)
200 // + key[1] * 31^(length - 2) + ... + key[length - 1]
201
202 const size_t length = strlen(key);
203
204 size_t hash = 0;
205 for (size_t index = 0; index < length; index++)
206 hash = 31 * hash + key[index];
207
208 return hash;
209 }
210
Hash__anone3cefd910211::PortNameHashDefinition211 size_t Hash(Port* value) const
212 {
213 size_t& hash = value->name_hash;
214 if (hash == 0)
215 hash = HashKey(value->lock.name);
216 return hash;
217 }
218
Compare__anone3cefd910211::PortNameHashDefinition219 bool Compare(const char* key, Port* value) const
220 {
221 return (strcmp(key, value->lock.name) == 0);
222 }
223
GetLink__anone3cefd910211::PortNameHashDefinition224 Port*& GetLink(Port* value) const
225 {
226 return value->name_hash_link;
227 }
228 };
229
230 typedef BOpenHashTable<PortNameHashDefinition> PortNameHashTable;
231
232
233 class PortNotificationService : public DefaultNotificationService {
234 public:
235 PortNotificationService();
236
237 void Notify(uint32 opcode, port_id team);
238 };
239
240 } // namespace
241
242
243 // #pragma mark - tracing
244
245
246 #if PORT_TRACING
247 namespace PortTracing {
248
249 class Create : public AbstractTraceEntry {
250 public:
Create(Port * port)251 Create(Port* port)
252 :
253 fID(port->id),
254 fOwner(port->owner),
255 fCapacity(port->capacity)
256 {
257 fName = alloc_tracing_buffer_strcpy(port->lock.name, B_OS_NAME_LENGTH,
258 false);
259
260 Initialized();
261 }
262
AddDump(TraceOutput & out)263 virtual void AddDump(TraceOutput& out)
264 {
265 out.Print("port %ld created, name \"%s\", owner %ld, capacity %ld",
266 fID, fName, fOwner, fCapacity);
267 }
268
269 private:
270 port_id fID;
271 char* fName;
272 team_id fOwner;
273 int32 fCapacity;
274 };
275
276
277 class Delete : public AbstractTraceEntry {
278 public:
Delete(Port * port)279 Delete(Port* port)
280 :
281 fID(port->id)
282 {
283 Initialized();
284 }
285
AddDump(TraceOutput & out)286 virtual void AddDump(TraceOutput& out)
287 {
288 out.Print("port %ld deleted", fID);
289 }
290
291 private:
292 port_id fID;
293 };
294
295
296 class Read : public AbstractTraceEntry {
297 public:
Read(const BReference<Port> & portRef,int32 code,ssize_t result)298 Read(const BReference<Port>& portRef, int32 code, ssize_t result)
299 :
300 fID(portRef->id),
301 fReadCount(portRef->read_count),
302 fWriteCount(portRef->write_count),
303 fCode(code),
304 fResult(result)
305 {
306 Initialized();
307 }
308
Read(port_id id,int32 readCount,int32 writeCount,int32 code,ssize_t result)309 Read(port_id id, int32 readCount, int32 writeCount, int32 code,
310 ssize_t result)
311 :
312 fID(id),
313 fReadCount(readCount),
314 fWriteCount(writeCount),
315 fCode(code),
316 fResult(result)
317 {
318 Initialized();
319 }
320
AddDump(TraceOutput & out)321 virtual void AddDump(TraceOutput& out)
322 {
323 out.Print("port %ld read, read %ld, write %ld, code %lx: %ld",
324 fID, fReadCount, fWriteCount, fCode, fResult);
325 }
326
327 private:
328 port_id fID;
329 int32 fReadCount;
330 int32 fWriteCount;
331 int32 fCode;
332 ssize_t fResult;
333 };
334
335
336 class Write : public AbstractTraceEntry {
337 public:
Write(port_id id,int32 readCount,int32 writeCount,int32 code,size_t bufferSize,ssize_t result)338 Write(port_id id, int32 readCount, int32 writeCount, int32 code,
339 size_t bufferSize, ssize_t result)
340 :
341 fID(id),
342 fReadCount(readCount),
343 fWriteCount(writeCount),
344 fCode(code),
345 fBufferSize(bufferSize),
346 fResult(result)
347 {
348 Initialized();
349 }
350
AddDump(TraceOutput & out)351 virtual void AddDump(TraceOutput& out)
352 {
353 out.Print("port %ld write, read %ld, write %ld, code %lx, size %ld: %ld",
354 fID, fReadCount, fWriteCount, fCode, fBufferSize, fResult);
355 }
356
357 private:
358 port_id fID;
359 int32 fReadCount;
360 int32 fWriteCount;
361 int32 fCode;
362 size_t fBufferSize;
363 ssize_t fResult;
364 };
365
366
367 class Info : public AbstractTraceEntry {
368 public:
Info(const BReference<Port> & portRef,int32 code,ssize_t result)369 Info(const BReference<Port>& portRef, int32 code, ssize_t result)
370 :
371 fID(portRef->id),
372 fReadCount(portRef->read_count),
373 fWriteCount(portRef->write_count),
374 fCode(code),
375 fResult(result)
376 {
377 Initialized();
378 }
379
Info(port_id id,int32 readCount,int32 writeCount,int32 code,ssize_t result)380 Info(port_id id, int32 readCount, int32 writeCount, int32 code,
381 ssize_t result)
382 :
383 fID(id),
384 fReadCount(readCount),
385 fWriteCount(writeCount),
386 fCode(code),
387 fResult(result)
388 {
389 Initialized();
390 }
391
AddDump(TraceOutput & out)392 virtual void AddDump(TraceOutput& out)
393 {
394 out.Print("port %ld info, read %ld, write %ld, code %lx: %ld",
395 fID, fReadCount, fWriteCount, fCode, fResult);
396 }
397
398 private:
399 port_id fID;
400 int32 fReadCount;
401 int32 fWriteCount;
402 int32 fCode;
403 ssize_t fResult;
404 };
405
406
407 class OwnerChange : public AbstractTraceEntry {
408 public:
OwnerChange(Port * port,team_id newOwner,status_t status)409 OwnerChange(Port* port, team_id newOwner, status_t status)
410 :
411 fID(port->id),
412 fOldOwner(port->owner),
413 fNewOwner(newOwner),
414 fStatus(status)
415 {
416 Initialized();
417 }
418
AddDump(TraceOutput & out)419 virtual void AddDump(TraceOutput& out)
420 {
421 out.Print("port %ld owner change from %ld to %ld: %s", fID, fOldOwner,
422 fNewOwner, strerror(fStatus));
423 }
424
425 private:
426 port_id fID;
427 team_id fOldOwner;
428 team_id fNewOwner;
429 status_t fStatus;
430 };
431
432 } // namespace PortTracing
433
434 # define T(x) new(std::nothrow) PortTracing::x;
435 #else
436 # define T(x) ;
437 #endif
438
439
440 static const size_t kInitialPortBufferSize = 4 * 1024 * 1024;
441 static const size_t kTotalSpaceLimit = 64 * 1024 * 1024;
442 static const size_t kTeamSpaceLimit = 8 * 1024 * 1024;
443 static const size_t kBufferGrowRate = kInitialPortBufferSize;
444
445 #define MAX_QUEUE_LENGTH 4096
446 #define PORT_MAX_MESSAGE_SIZE (256 * 1024)
447
448 static int32 sMaxPorts = 4096;
449 static int32 sUsedPorts;
450
451 static PortHashTable sPorts;
452 static PortNameHashTable sPortsByName;
453 static ConditionVariable sNoSpaceCondition;
454 static int32 sTotalSpaceCommited;
455 static int32 sWaitingForSpace;
456 static port_id sNextPortID = 1;
457 static bool sPortsActive = false;
458 static rw_lock sPortsLock = RW_LOCK_INITIALIZER("ports list");
459
460 enum {
461 kTeamListLockCount = 8
462 };
463
464 static mutex sTeamListLock[kTeamListLockCount] = {
465 MUTEX_INITIALIZER("team ports list 1"),
466 MUTEX_INITIALIZER("team ports list 2"),
467 MUTEX_INITIALIZER("team ports list 3"),
468 MUTEX_INITIALIZER("team ports list 4"),
469 MUTEX_INITIALIZER("team ports list 5"),
470 MUTEX_INITIALIZER("team ports list 6"),
471 MUTEX_INITIALIZER("team ports list 7"),
472 MUTEX_INITIALIZER("team ports list 8")
473 };
474
475 static PortNotificationService sNotificationService;
476
477
478 // #pragma mark - TeamNotificationService
479
480
PortNotificationService()481 PortNotificationService::PortNotificationService()
482 :
483 DefaultNotificationService("ports")
484 {
485 }
486
487
488 void
Notify(uint32 opcode,port_id port)489 PortNotificationService::Notify(uint32 opcode, port_id port)
490 {
491 char eventBuffer[128];
492 KMessage event;
493 event.SetTo(eventBuffer, sizeof(eventBuffer), PORT_MONITOR);
494 event.AddInt32("event", opcode);
495 event.AddInt32("port", port);
496
497 DefaultNotificationService::Notify(event, opcode);
498 }
499
500
501 // #pragma mark - debugger commands
502
503
504 static int
dump_port_list(int argc,char ** argv)505 dump_port_list(int argc, char** argv)
506 {
507 const char* name = NULL;
508 team_id owner = -1;
509
510 if (argc > 2) {
511 if (!strcmp(argv[1], "team") || !strcmp(argv[1], "owner"))
512 owner = strtoul(argv[2], NULL, 0);
513 else if (!strcmp(argv[1], "name"))
514 name = argv[2];
515 } else if (argc > 1)
516 owner = strtoul(argv[1], NULL, 0);
517
518 kprintf("port id cap read-cnt write-cnt total team "
519 "name\n");
520
521 for (PortHashTable::Iterator it = sPorts.GetIterator();
522 Port* port = it.Next();) {
523 if ((owner != -1 && port->owner != owner)
524 || (name != NULL && strstr(port->lock.name, name) == NULL))
525 continue;
526
527 kprintf("%p %8" B_PRId32 " %4" B_PRId32 " %9" B_PRIu32 " %9" B_PRId32
528 " %8" B_PRId32 " %6" B_PRId32 " %s\n", port, port->id,
529 port->capacity, port->read_count, port->write_count,
530 port->total_count, port->owner, port->lock.name);
531 }
532
533 return 0;
534 }
535
536
537 static void
_dump_port_info(Port * port)538 _dump_port_info(Port* port)
539 {
540 kprintf("PORT: %p\n", port);
541 kprintf(" id: %" B_PRId32 "\n", port->id);
542 kprintf(" name: \"%s\"\n", port->lock.name);
543 kprintf(" owner: %" B_PRId32 "\n", port->owner);
544 kprintf(" capacity: %" B_PRId32 "\n", port->capacity);
545 kprintf(" read_count: %" B_PRIu32 "\n", port->read_count);
546 kprintf(" write_count: %" B_PRId32 "\n", port->write_count);
547 kprintf(" total count: %" B_PRId32 "\n", port->total_count);
548
549 if (!port->messages.IsEmpty()) {
550 kprintf("messages:\n");
551
552 MessageList::Iterator iterator = port->messages.GetIterator();
553 while (port_message* message = iterator.Next()) {
554 kprintf(" %p %08" B_PRIx32 " %ld\n", message, message->code, message->size);
555 }
556 }
557
558 set_debug_variable("_port", (addr_t)port);
559 set_debug_variable("_portID", port->id);
560 set_debug_variable("_owner", port->owner);
561 }
562
563
564 static int
dump_port_info(int argc,char ** argv)565 dump_port_info(int argc, char** argv)
566 {
567 ConditionVariable* condition = NULL;
568 const char* name = NULL;
569
570 if (argc < 2) {
571 print_debugger_command_usage(argv[0]);
572 return 0;
573 }
574
575 if (argc > 2) {
576 if (!strcmp(argv[1], "address")) {
577 _dump_port_info((Port*)parse_expression(argv[2]));
578 return 0;
579 } else if (!strcmp(argv[1], "condition"))
580 condition = (ConditionVariable*)parse_expression(argv[2]);
581 else if (!strcmp(argv[1], "name"))
582 name = argv[2];
583 } else if (parse_expression(argv[1]) > 0) {
584 // if the argument looks like a number, treat it as such
585 int32 num = parse_expression(argv[1]);
586 Port* port = sPorts.Lookup(num);
587 if (port == NULL || port->state != Port::kActive) {
588 kprintf("port %" B_PRId32 " (%#" B_PRIx32 ") doesn't exist!\n",
589 num, num);
590 return 0;
591 }
592 _dump_port_info(port);
593 return 0;
594 } else
595 name = argv[1];
596
597 // walk through the ports list, trying to match name
598 for (PortHashTable::Iterator it = sPorts.GetIterator();
599 Port* port = it.Next();) {
600 if ((name != NULL && port->lock.name != NULL
601 && !strcmp(name, port->lock.name))
602 || (condition != NULL && (&port->read_condition == condition
603 || &port->write_condition == condition))) {
604 _dump_port_info(port);
605 return 0;
606 }
607 }
608
609 return 0;
610 }
611
612
613 // #pragma mark - internal helper functions
614
615
616 /*! Notifies the port's select events.
617 The port must be locked.
618 */
619 static void
notify_port_select_events(Port * port,uint16 events)620 notify_port_select_events(Port* port, uint16 events)
621 {
622 if (port->select_infos)
623 notify_select_events_list(port->select_infos, events);
624 }
625
626
627 static BReference<Port>
get_locked_port(port_id id)628 get_locked_port(port_id id) GCC_2_NRV(portRef)
629 {
630 #if __GNUC__ >= 3
631 BReference<Port> portRef;
632 #endif
633 {
634 ReadLocker portsLocker(sPortsLock);
635 portRef.SetTo(sPorts.Lookup(id));
636 }
637
638 if (portRef != NULL && portRef->state == Port::kActive) {
639 if (mutex_lock(&portRef->lock) != B_OK)
640 portRef.Unset();
641 } else
642 portRef.Unset();
643
644 return portRef;
645 }
646
647
648 static BReference<Port>
get_port(port_id id)649 get_port(port_id id) GCC_2_NRV(portRef)
650 {
651 #if __GNUC__ >= 3
652 BReference<Port> portRef;
653 #endif
654 ReadLocker portsLocker(sPortsLock);
655 portRef.SetTo(sPorts.Lookup(id));
656
657 return portRef;
658 }
659
660
661 /*! You need to own the port's lock when calling this function */
662 static inline bool
is_port_closed(Port * port)663 is_port_closed(Port* port)
664 {
665 return port->capacity == 0;
666 }
667
668
669 static void
put_port_message(port_message * message)670 put_port_message(port_message* message)
671 {
672 const size_t size = sizeof(port_message) + message->size;
673 free(message);
674
675 atomic_add(&sTotalSpaceCommited, -size);
676 if (sWaitingForSpace > 0)
677 sNoSpaceCondition.NotifyAll();
678 }
679
680
681 /*! Port must be locked. */
682 static status_t
get_port_message(int32 code,size_t bufferSize,uint32 flags,bigtime_t timeout,port_message ** _message,Port & port)683 get_port_message(int32 code, size_t bufferSize, uint32 flags, bigtime_t timeout,
684 port_message** _message, Port& port)
685 {
686 const size_t size = sizeof(port_message) + bufferSize;
687
688 while (true) {
689 int32 previouslyCommited = atomic_add(&sTotalSpaceCommited, size);
690
691 while (previouslyCommited + size > kTotalSpaceLimit) {
692 // TODO: add per team limit
693
694 // We are not allowed to allocate more memory, as our
695 // space limit has been reached - just wait until we get
696 // some free space again.
697
698 atomic_add(&sTotalSpaceCommited, -size);
699
700 // TODO: we don't want to wait - but does that also mean we
701 // shouldn't wait for free memory?
702 if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0)
703 return B_WOULD_BLOCK;
704
705 ConditionVariableEntry entry;
706 sNoSpaceCondition.Add(&entry);
707
708 port_id portID = port.id;
709 mutex_unlock(&port.lock);
710
711 atomic_add(&sWaitingForSpace, 1);
712
713 // TODO: right here the condition could be notified and we'd
714 // miss it.
715
716 status_t status = entry.Wait(flags, timeout);
717
718 atomic_add(&sWaitingForSpace, -1);
719
720 // re-lock the port
721 BReference<Port> newPortRef = get_locked_port(portID);
722
723 if (newPortRef.Get() != &port || is_port_closed(&port)) {
724 // the port is no longer usable
725 return B_BAD_PORT_ID;
726 }
727
728 if (status == B_TIMED_OUT)
729 return B_TIMED_OUT;
730
731 previouslyCommited = atomic_add(&sTotalSpaceCommited, size);
732 continue;
733 }
734
735 // Quota is fulfilled, try to allocate the buffer
736 port_message* message = (port_message*)malloc(size);
737 if (message != NULL) {
738 message->code = code;
739 message->size = bufferSize;
740
741 *_message = message;
742 return B_OK;
743 }
744
745 // We weren't able to allocate and we'll start over,so we remove our
746 // size from the commited-counter again.
747 atomic_add(&sTotalSpaceCommited, -size);
748 continue;
749 }
750 }
751
752
753 /*! Fills the port_info structure with information from the specified
754 port.
755 The port's lock must be held when called.
756 */
757 static void
fill_port_info(Port * port,port_info * info,size_t size)758 fill_port_info(Port* port, port_info* info, size_t size)
759 {
760 info->port = port->id;
761 info->team = port->owner;
762 info->capacity = port->capacity;
763
764 info->queue_count = port->read_count;
765 info->total_count = port->total_count;
766
767 strlcpy(info->name, port->lock.name, B_OS_NAME_LENGTH);
768 }
769
770
771 static ssize_t
copy_port_message(port_message * message,int32 * _code,void * buffer,size_t bufferSize,bool userCopy)772 copy_port_message(port_message* message, int32* _code, void* buffer,
773 size_t bufferSize, bool userCopy)
774 {
775 // check output buffer size
776 size_t size = std::min(bufferSize, message->size);
777
778 // copy message
779 if (_code != NULL)
780 *_code = message->code;
781
782 if (size > 0) {
783 if (userCopy) {
784 status_t status = user_memcpy(buffer, message->buffer, size);
785 if (status != B_OK)
786 return status;
787 } else
788 memcpy(buffer, message->buffer, size);
789 }
790
791 return size;
792 }
793
794
795 static void
uninit_port(Port * port)796 uninit_port(Port* port)
797 {
798 MutexLocker locker(port->lock);
799
800 notify_port_select_events(port, B_EVENT_INVALID);
801 port->select_infos = NULL;
802
803 // Release the threads that were blocking on this port.
804 // read_port() will see the B_BAD_PORT_ID return value, and act accordingly
805 port->read_condition.NotifyAll(B_BAD_PORT_ID);
806 port->write_condition.NotifyAll(B_BAD_PORT_ID);
807 sNotificationService.Notify(PORT_REMOVED, port->id);
808 }
809
810
811 /*! Caller must ensure there is still a reference to the port. (Either by
812 * holding a reference itself or by holding a lock on one of the data
813 * structures in which it is referenced.)
814 */
815 static status_t
delete_port_logical(Port * port)816 delete_port_logical(Port* port)
817 {
818 for (;;) {
819 // Try to logically delete
820 const int32 oldState = atomic_test_and_set(&port->state,
821 Port::kDeleted, Port::kActive);
822 // Linearization point for port deletion
823
824 switch (oldState) {
825 case Port::kActive:
826 // Logical deletion succesful
827 return B_OK;
828
829 case Port::kDeleted:
830 // Someone else already deleted it in the meantime
831 TRACE(("delete_port_logical: already deleted port_id %ld\n",
832 port->id));
833 return B_BAD_PORT_ID;
834
835 case Port::kUnused:
836 // Port is still being created, retry
837 continue;
838
839 default:
840 // Port state got corrupted somehow
841 panic("Invalid port state!\n");
842 }
843 }
844 }
845
846
847 // #pragma mark - private kernel API
848
849
850 /*! This function deletes all the ports that are owned by the passed team.
851 */
852 void
delete_owned_ports(Team * team)853 delete_owned_ports(Team* team)
854 {
855 TRACE(("delete_owned_ports(owner = %ld)\n", team->id));
856
857 list deletionList;
858 list_init_etc(&deletionList, port_team_link_offset());
859
860 const uint8 lockIndex = team->id % kTeamListLockCount;
861 MutexLocker teamPortsListLocker(sTeamListLock[lockIndex]);
862
863 // Try to logically delete all ports from the team's port list.
864 // On success, move the port to deletionList.
865 Port* port = (Port*)list_get_first_item(&team->port_list);
866 while (port != NULL) {
867 status_t status = delete_port_logical(port);
868 // Contains linearization point
869
870 Port* nextPort = (Port*)list_get_next_item(&team->port_list, port);
871
872 if (status == B_OK) {
873 list_remove_link(&port->team_link);
874 list_add_item(&deletionList, port);
875 }
876
877 port = nextPort;
878 }
879
880 teamPortsListLocker.Unlock();
881
882 // Remove all ports in deletionList from hashes
883 {
884 WriteLocker portsLocker(sPortsLock);
885
886 for (Port* port = (Port*)list_get_first_item(&deletionList);
887 port != NULL;
888 port = (Port*)list_get_next_item(&deletionList, port)) {
889
890 sPorts.Remove(port);
891 sPortsByName.Remove(port);
892 port->ReleaseReference();
893 // joint reference for sPorts and sPortsByName
894 }
895 }
896
897 // Uninitialize ports and release team port list references
898 while (Port* port = (Port*)list_remove_head_item(&deletionList)) {
899 atomic_add(&sUsedPorts, -1);
900 uninit_port(port);
901 port->ReleaseReference();
902 // Reference for team port list
903 }
904 }
905
906
907 int32
port_max_ports(void)908 port_max_ports(void)
909 {
910 return sMaxPorts;
911 }
912
913
914 int32
port_used_ports(void)915 port_used_ports(void)
916 {
917 return sUsedPorts;
918 }
919
920
921 size_t
port_team_link_offset()922 port_team_link_offset()
923 {
924 // Somewhat ugly workaround since we cannot use offsetof() for a class
925 // with vtable (GCC4 throws a warning then).
926 Port* port = (Port*)0;
927 return (size_t)&port->team_link;
928 }
929
930
931 status_t
port_init(kernel_args * args)932 port_init(kernel_args *args)
933 {
934 // initialize ports table and by-name hash
935 new(&sPorts) PortHashTable;
936 if (sPorts.Init() != B_OK) {
937 panic("Failed to init port hash table!");
938 return B_NO_MEMORY;
939 }
940
941 new(&sPortsByName) PortNameHashTable;
942 if (sPortsByName.Init() != B_OK) {
943 panic("Failed to init port by name hash table!");
944 return B_NO_MEMORY;
945 }
946
947 sNoSpaceCondition.Init(&sPorts, "port space");
948
949 // add debugger commands
950 add_debugger_command_etc("ports", &dump_port_list,
951 "Dump a list of all active ports (for team, with name, etc.)",
952 "[ ([ \"team\" | \"owner\" ] <team>) | (\"name\" <name>) ]\n"
953 "Prints a list of all active ports meeting the given\n"
954 "requirement. If no argument is given, all ports are listed.\n"
955 " <team> - The team owning the ports.\n"
956 " <name> - Part of the name of the ports.\n", 0);
957 add_debugger_command_etc("port", &dump_port_info,
958 "Dump info about a particular port",
959 "(<id> | [ \"address\" ] <address>) | ([ \"name\" ] <name>) "
960 "| (\"condition\" <address>)\n"
961 "Prints info about the specified port.\n"
962 " <address> - Pointer to the port structure.\n"
963 " <name> - Name of the port.\n"
964 " <condition> - address of the port's read or write condition.\n", 0);
965
966 new(&sNotificationService) PortNotificationService();
967 sNotificationService.Register();
968 sPortsActive = true;
969 return B_OK;
970 }
971
972
973 // #pragma mark - public kernel API
974
975
976 port_id
create_port(int32 queueLength,const char * name)977 create_port(int32 queueLength, const char* name)
978 {
979 TRACE(("create_port(queueLength = %ld, name = \"%s\")\n", queueLength,
980 name));
981
982 if (!sPortsActive) {
983 panic("ports used too early!\n");
984 return B_BAD_PORT_ID;
985 }
986 if (queueLength < 1 || queueLength > MAX_QUEUE_LENGTH)
987 return B_BAD_VALUE;
988
989 Team* team = thread_get_current_thread()->team;
990 if (team == NULL)
991 return B_BAD_TEAM_ID;
992
993 // create a port
994 BReference<Port> port;
995 {
996 Port* newPort = new(std::nothrow) Port(team_get_current_team_id(),
997 queueLength, name != NULL ? name : "unnamed port");
998 if (newPort == NULL)
999 return B_NO_MEMORY;
1000 port.SetTo(newPort, true);
1001 }
1002
1003 // check the ports limit
1004 const int32 previouslyUsed = atomic_add(&sUsedPorts, 1);
1005 if (previouslyUsed + 1 >= sMaxPorts) {
1006 atomic_add(&sUsedPorts, -1);
1007 return B_NO_MORE_PORTS;
1008 }
1009
1010 {
1011 WriteLocker locker(sPortsLock);
1012
1013 // allocate a port ID
1014 do {
1015 port->id = sNextPortID++;
1016
1017 // handle integer overflow
1018 if (sNextPortID < 0)
1019 sNextPortID = 1;
1020 } while (sPorts.Lookup(port->id) != NULL);
1021
1022 // Insert port physically:
1023 // (1/2) Insert into hash tables
1024 port->AcquireReference();
1025 // joint reference for sPorts and sPortsByName
1026
1027 sPorts.Insert(port);
1028 sPortsByName.Insert(port);
1029 }
1030
1031 // (2/2) Insert into team list
1032 {
1033 const uint8 lockIndex = port->owner % kTeamListLockCount;
1034 MutexLocker teamPortsListLocker(sTeamListLock[lockIndex]);
1035 port->AcquireReference();
1036 list_add_item(&team->port_list, port);
1037 }
1038
1039 // tracing, notifications, etc.
1040 T(Create(port));
1041
1042 const port_id id = port->id;
1043
1044 // Insert port logically by marking it active
1045 const int32 oldState = atomic_test_and_set(&port->state,
1046 Port::kActive, Port::kUnused);
1047 // Linearization point for port creation
1048
1049 if (oldState != Port::kUnused) {
1050 // Nobody is allowed to tamper with the port before it's active.
1051 panic("Port state was modified during creation!\n");
1052 }
1053
1054 TRACE(("create_port() done: port created %ld\n", id));
1055
1056 sNotificationService.Notify(PORT_ADDED, id);
1057 return id;
1058 }
1059
1060
1061 status_t
close_port(port_id id)1062 close_port(port_id id)
1063 {
1064 TRACE(("close_port(id = %ld)\n", id));
1065
1066 if (!sPortsActive || id < 0)
1067 return B_BAD_PORT_ID;
1068
1069 // get the port
1070 BReference<Port> portRef = get_locked_port(id);
1071 if (portRef == NULL) {
1072 TRACE(("close_port: invalid port_id %ld\n", id));
1073 return B_BAD_PORT_ID;
1074 }
1075 MutexLocker lock(&portRef->lock, true);
1076
1077 // mark port to disable writing - deleting the semaphores will
1078 // wake up waiting read/writes
1079 portRef->capacity = 0;
1080
1081 notify_port_select_events(portRef, B_EVENT_INVALID);
1082 portRef->select_infos = NULL;
1083
1084 portRef->read_condition.NotifyAll(B_BAD_PORT_ID);
1085 portRef->write_condition.NotifyAll(B_BAD_PORT_ID);
1086
1087 return B_OK;
1088 }
1089
1090
1091 status_t
delete_port(port_id id)1092 delete_port(port_id id)
1093 {
1094 TRACE(("delete_port(id = %ld)\n", id));
1095
1096 if (!sPortsActive || id < 0)
1097 return B_BAD_PORT_ID;
1098
1099 BReference<Port> portRef = get_port(id);
1100
1101 if (portRef == NULL) {
1102 TRACE(("delete_port: invalid port_id %ld\n", id));
1103 return B_BAD_PORT_ID;
1104 }
1105
1106 status_t status = delete_port_logical(portRef);
1107 // Contains linearization point
1108 if (status != B_OK)
1109 return status;
1110
1111 // Now remove port physically:
1112 // (1/2) Remove from hash tables
1113 {
1114 WriteLocker portsLocker(sPortsLock);
1115
1116 sPorts.Remove(portRef);
1117 sPortsByName.Remove(portRef);
1118
1119 portRef->ReleaseReference();
1120 // joint reference for sPorts and sPortsByName
1121 }
1122
1123 // (2/2) Remove from team port list
1124 {
1125 const uint8 lockIndex = portRef->owner % kTeamListLockCount;
1126 MutexLocker teamPortsListLocker(sTeamListLock[lockIndex]);
1127
1128 list_remove_link(&portRef->team_link);
1129 portRef->ReleaseReference();
1130 }
1131
1132 uninit_port(portRef);
1133
1134 T(Delete(portRef));
1135
1136 atomic_add(&sUsedPorts, -1);
1137
1138 return B_OK;
1139 }
1140
1141
1142 status_t
select_port(int32 id,struct select_info * info,bool kernel)1143 select_port(int32 id, struct select_info* info, bool kernel)
1144 {
1145 if (id < 0)
1146 return B_BAD_PORT_ID;
1147
1148 // get the port
1149 BReference<Port> portRef = get_locked_port(id);
1150 if (portRef == NULL)
1151 return B_BAD_PORT_ID;
1152 MutexLocker locker(portRef->lock, true);
1153
1154 // port must not yet be closed
1155 if (is_port_closed(portRef))
1156 return B_BAD_PORT_ID;
1157
1158 if (!kernel && portRef->owner == team_get_kernel_team_id()) {
1159 // kernel port, but call from userland
1160 return B_NOT_ALLOWED;
1161 }
1162
1163 info->selected_events &= B_EVENT_READ | B_EVENT_WRITE | B_EVENT_INVALID;
1164
1165 if (info->selected_events != 0) {
1166 uint16 events = 0;
1167
1168 info->next = portRef->select_infos;
1169 portRef->select_infos = info;
1170
1171 // check for events
1172 if ((info->selected_events & B_EVENT_READ) != 0
1173 && !portRef->messages.IsEmpty()) {
1174 events |= B_EVENT_READ;
1175 }
1176
1177 if (portRef->write_count > 0)
1178 events |= B_EVENT_WRITE;
1179
1180 if (events != 0)
1181 notify_select_events(info, events);
1182 }
1183
1184 return B_OK;
1185 }
1186
1187
1188 status_t
deselect_port(int32 id,struct select_info * info,bool kernel)1189 deselect_port(int32 id, struct select_info* info, bool kernel)
1190 {
1191 if (id < 0)
1192 return B_BAD_PORT_ID;
1193 if (info->selected_events == 0)
1194 return B_OK;
1195
1196 // get the port
1197 BReference<Port> portRef = get_locked_port(id);
1198 if (portRef == NULL)
1199 return B_BAD_PORT_ID;
1200 MutexLocker locker(portRef->lock, true);
1201
1202 // find and remove the infos
1203 select_info** infoLocation = &portRef->select_infos;
1204 while (*infoLocation != NULL && *infoLocation != info)
1205 infoLocation = &(*infoLocation)->next;
1206
1207 if (*infoLocation == info)
1208 *infoLocation = info->next;
1209
1210 return B_OK;
1211 }
1212
1213
1214 port_id
find_port(const char * name)1215 find_port(const char* name)
1216 {
1217 TRACE(("find_port(name = \"%s\")\n", name));
1218
1219 if (!sPortsActive) {
1220 panic("ports used too early!\n");
1221 return B_NAME_NOT_FOUND;
1222 }
1223 if (name == NULL)
1224 return B_BAD_VALUE;
1225
1226 ReadLocker locker(sPortsLock);
1227 Port* port = sPortsByName.Lookup(name);
1228 // Since we have sPortsLock and don't return the port itself,
1229 // no BReference necessary
1230
1231 if (port != NULL && port->state == Port::kActive)
1232 return port->id;
1233
1234 return B_NAME_NOT_FOUND;
1235 }
1236
1237
1238 status_t
_get_port_info(port_id id,port_info * info,size_t size)1239 _get_port_info(port_id id, port_info* info, size_t size)
1240 {
1241 TRACE(("get_port_info(id = %ld)\n", id));
1242
1243 if (info == NULL || size != sizeof(port_info))
1244 return B_BAD_VALUE;
1245 if (!sPortsActive || id < 0)
1246 return B_BAD_PORT_ID;
1247
1248 // get the port
1249 BReference<Port> portRef = get_locked_port(id);
1250 if (portRef == NULL) {
1251 TRACE(("get_port_info: invalid port_id %ld\n", id));
1252 return B_BAD_PORT_ID;
1253 }
1254 MutexLocker locker(portRef->lock, true);
1255
1256 // fill a port_info struct with info
1257 fill_port_info(portRef, info, size);
1258 return B_OK;
1259 }
1260
1261
1262 status_t
_get_next_port_info(team_id teamID,int32 * _cookie,struct port_info * info,size_t size)1263 _get_next_port_info(team_id teamID, int32* _cookie, struct port_info* info,
1264 size_t size)
1265 {
1266 TRACE(("get_next_port_info(team = %ld)\n", teamID));
1267
1268 if (info == NULL || size != sizeof(port_info) || _cookie == NULL
1269 || teamID < 0) {
1270 return B_BAD_VALUE;
1271 }
1272 if (!sPortsActive)
1273 return B_BAD_PORT_ID;
1274
1275 Team* team = Team::Get(teamID);
1276 if (team == NULL)
1277 return B_BAD_TEAM_ID;
1278 BReference<Team> teamReference(team, true);
1279
1280 // iterate through the team's port list
1281 const uint8 lockIndex = teamID % kTeamListLockCount;
1282 MutexLocker teamPortsListLocker(sTeamListLock[lockIndex]);
1283
1284 int32 stopIndex = *_cookie;
1285 int32 index = 0;
1286
1287 Port* port = (Port*)list_get_first_item(&team->port_list);
1288 while (port != NULL) {
1289 if (!is_port_closed(port)) {
1290 if (index == stopIndex)
1291 break;
1292 index++;
1293 }
1294
1295 port = (Port*)list_get_next_item(&team->port_list, port);
1296 }
1297
1298 if (port == NULL)
1299 return B_BAD_PORT_ID;
1300
1301 // fill in the port info
1302 BReference<Port> portRef = port;
1303 teamPortsListLocker.Unlock();
1304 // Only use portRef below this line...
1305
1306 MutexLocker locker(portRef->lock);
1307 fill_port_info(portRef, info, size);
1308
1309 *_cookie = stopIndex + 1;
1310 return B_OK;
1311 }
1312
1313
1314 ssize_t
port_buffer_size(port_id id)1315 port_buffer_size(port_id id)
1316 {
1317 return port_buffer_size_etc(id, 0, 0);
1318 }
1319
1320
1321 ssize_t
port_buffer_size_etc(port_id id,uint32 flags,bigtime_t timeout)1322 port_buffer_size_etc(port_id id, uint32 flags, bigtime_t timeout)
1323 {
1324 port_message_info info;
1325 status_t error = get_port_message_info_etc(id, &info, flags, timeout);
1326 return error != B_OK ? error : info.size;
1327 }
1328
1329
1330 status_t
_get_port_message_info_etc(port_id id,port_message_info * info,size_t infoSize,uint32 flags,bigtime_t timeout)1331 _get_port_message_info_etc(port_id id, port_message_info* info,
1332 size_t infoSize, uint32 flags, bigtime_t timeout)
1333 {
1334 if (info == NULL || infoSize != sizeof(port_message_info))
1335 return B_BAD_VALUE;
1336 if (!sPortsActive || id < 0)
1337 return B_BAD_PORT_ID;
1338
1339 flags &= B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT | B_RELATIVE_TIMEOUT
1340 | B_ABSOLUTE_TIMEOUT;
1341
1342 // get the port
1343 BReference<Port> portRef = get_locked_port(id);
1344 if (portRef == NULL)
1345 return B_BAD_PORT_ID;
1346 MutexLocker locker(portRef->lock, true);
1347
1348 if (is_port_closed(portRef) && portRef->messages.IsEmpty()) {
1349 T(Info(portRef, 0, B_BAD_PORT_ID));
1350 TRACE(("_get_port_message_info_etc(): closed port %ld\n", id));
1351 return B_BAD_PORT_ID;
1352 }
1353
1354 while (portRef->read_count == 0) {
1355 // We need to wait for a message to appear
1356 if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0)
1357 return B_WOULD_BLOCK;
1358
1359 ConditionVariableEntry entry;
1360 portRef->read_condition.Add(&entry);
1361
1362 locker.Unlock();
1363
1364 // block if no message, or, if B_TIMEOUT flag set, block with timeout
1365 status_t status = entry.Wait(flags, timeout);
1366
1367 if (status != B_OK) {
1368 T(Info(portRef, 0, status));
1369 return status;
1370 }
1371
1372 // re-lock
1373 BReference<Port> newPortRef = get_locked_port(id);
1374 if (newPortRef == NULL) {
1375 T(Info(id, 0, 0, 0, B_BAD_PORT_ID));
1376 return B_BAD_PORT_ID;
1377 }
1378 locker.SetTo(newPortRef->lock, true);
1379
1380 if (newPortRef != portRef
1381 || (is_port_closed(portRef) && portRef->messages.IsEmpty())) {
1382 // the port is no longer there
1383 T(Info(id, 0, 0, 0, B_BAD_PORT_ID));
1384 return B_BAD_PORT_ID;
1385 }
1386 }
1387
1388 // determine tail & get the length of the message
1389 port_message* message = portRef->messages.Head();
1390 if (message == NULL) {
1391 panic("port %" B_PRId32 ": no messages found\n", portRef->id);
1392 return B_ERROR;
1393 }
1394
1395 info->size = message->size;
1396 info->sender = message->sender;
1397 info->sender_group = message->sender_group;
1398 info->sender_team = message->sender_team;
1399
1400 T(Info(portRef, message->code, B_OK));
1401
1402 // notify next one, as we haven't read from the port
1403 portRef->read_condition.NotifyOne();
1404
1405 return B_OK;
1406 }
1407
1408
1409 ssize_t
port_count(port_id id)1410 port_count(port_id id)
1411 {
1412 if (!sPortsActive || id < 0)
1413 return B_BAD_PORT_ID;
1414
1415 // get the port
1416 BReference<Port> portRef = get_locked_port(id);
1417 if (portRef == NULL) {
1418 TRACE(("port_count: invalid port_id %ld\n", id));
1419 return B_BAD_PORT_ID;
1420 }
1421 MutexLocker locker(portRef->lock, true);
1422
1423 // return count of messages
1424 return portRef->read_count;
1425 }
1426
1427
1428 ssize_t
read_port(port_id port,int32 * msgCode,void * buffer,size_t bufferSize)1429 read_port(port_id port, int32* msgCode, void* buffer, size_t bufferSize)
1430 {
1431 return read_port_etc(port, msgCode, buffer, bufferSize, 0, 0);
1432 }
1433
1434
1435 ssize_t
read_port_etc(port_id id,int32 * _code,void * buffer,size_t bufferSize,uint32 flags,bigtime_t timeout)1436 read_port_etc(port_id id, int32* _code, void* buffer, size_t bufferSize,
1437 uint32 flags, bigtime_t timeout)
1438 {
1439 if (!sPortsActive || id < 0)
1440 return B_BAD_PORT_ID;
1441 if ((buffer == NULL && bufferSize > 0) || timeout < 0)
1442 return B_BAD_VALUE;
1443
1444 bool userCopy = (flags & PORT_FLAG_USE_USER_MEMCPY) != 0;
1445 bool peekOnly = !userCopy && (flags & B_PEEK_PORT_MESSAGE) != 0;
1446 // TODO: we could allow peeking for user apps now
1447
1448 flags &= B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT | B_RELATIVE_TIMEOUT
1449 | B_ABSOLUTE_TIMEOUT;
1450
1451 // get the port
1452 BReference<Port> portRef = get_locked_port(id);
1453 if (portRef == NULL)
1454 return B_BAD_PORT_ID;
1455 MutexLocker locker(portRef->lock, true);
1456
1457 if (is_port_closed(portRef) && portRef->messages.IsEmpty()) {
1458 T(Read(portRef, 0, B_BAD_PORT_ID));
1459 TRACE(("read_port_etc(): closed port %ld\n", id));
1460 return B_BAD_PORT_ID;
1461 }
1462
1463 while (portRef->read_count == 0) {
1464 if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0)
1465 return B_WOULD_BLOCK;
1466
1467 // We need to wait for a message to appear
1468 ConditionVariableEntry entry;
1469 portRef->read_condition.Add(&entry);
1470
1471 locker.Unlock();
1472
1473 // block if no message, or, if B_TIMEOUT flag set, block with timeout
1474 status_t status = entry.Wait(flags, timeout);
1475
1476 // re-lock
1477 BReference<Port> newPortRef = get_locked_port(id);
1478 if (newPortRef == NULL) {
1479 T(Read(id, 0, 0, 0, B_BAD_PORT_ID));
1480 return B_BAD_PORT_ID;
1481 }
1482 locker.SetTo(newPortRef->lock, true);
1483
1484 if (newPortRef != portRef
1485 || (is_port_closed(portRef) && portRef->messages.IsEmpty())) {
1486 // the port is no longer there
1487 T(Read(id, 0, 0, 0, B_BAD_PORT_ID));
1488 return B_BAD_PORT_ID;
1489 }
1490
1491 if (status != B_OK) {
1492 T(Read(portRef, 0, status));
1493 return status;
1494 }
1495 }
1496
1497 // determine tail & get the length of the message
1498 port_message* message = portRef->messages.Head();
1499 if (message == NULL) {
1500 panic("port %" B_PRId32 ": no messages found\n", portRef->id);
1501 return B_ERROR;
1502 }
1503
1504 if (peekOnly) {
1505 size_t size = copy_port_message(message, _code, buffer, bufferSize,
1506 userCopy);
1507
1508 T(Read(portRef, message->code, size));
1509
1510 portRef->read_condition.NotifyOne();
1511 // we only peeked, but didn't grab the message
1512 return size;
1513 }
1514
1515 portRef->messages.RemoveHead();
1516 portRef->total_count++;
1517 portRef->write_count++;
1518 portRef->read_count--;
1519
1520 notify_port_select_events(portRef, B_EVENT_WRITE);
1521 portRef->write_condition.NotifyOne();
1522 // make one spot in queue available again for write
1523
1524 T(Read(portRef, message->code, std::min(bufferSize, message->size)));
1525
1526 locker.Unlock();
1527
1528 size_t size = copy_port_message(message, _code, buffer, bufferSize,
1529 userCopy);
1530
1531 put_port_message(message);
1532 return size;
1533 }
1534
1535
1536 status_t
write_port(port_id id,int32 msgCode,const void * buffer,size_t bufferSize)1537 write_port(port_id id, int32 msgCode, const void* buffer, size_t bufferSize)
1538 {
1539 iovec vec = { (void*)buffer, bufferSize };
1540
1541 return writev_port_etc(id, msgCode, &vec, 1, bufferSize, 0, 0);
1542 }
1543
1544
1545 status_t
write_port_etc(port_id id,int32 msgCode,const void * buffer,size_t bufferSize,uint32 flags,bigtime_t timeout)1546 write_port_etc(port_id id, int32 msgCode, const void* buffer,
1547 size_t bufferSize, uint32 flags, bigtime_t timeout)
1548 {
1549 iovec vec = { (void*)buffer, bufferSize };
1550
1551 return writev_port_etc(id, msgCode, &vec, 1, bufferSize, flags, timeout);
1552 }
1553
1554
1555 status_t
writev_port_etc(port_id id,int32 msgCode,const iovec * msgVecs,size_t vecCount,size_t bufferSize,uint32 flags,bigtime_t timeout)1556 writev_port_etc(port_id id, int32 msgCode, const iovec* msgVecs,
1557 size_t vecCount, size_t bufferSize, uint32 flags, bigtime_t timeout)
1558 {
1559 if (!sPortsActive || id < 0)
1560 return B_BAD_PORT_ID;
1561 if (bufferSize > PORT_MAX_MESSAGE_SIZE)
1562 return B_BAD_VALUE;
1563
1564 bool userCopy = (flags & PORT_FLAG_USE_USER_MEMCPY) != 0;
1565
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();
1575 }
1576
1577 status_t status;
1578 port_message* message = NULL;
1579
1580 // get the port
1581 BReference<Port> portRef = get_locked_port(id);
1582 if (portRef == NULL) {
1583 TRACE(("write_port_etc: invalid port_id %ld\n", id));
1584 return B_BAD_PORT_ID;
1585 }
1586 MutexLocker locker(portRef->lock, true);
1587
1588 if (is_port_closed(portRef)) {
1589 TRACE(("write_port_etc: port %ld closed\n", id));
1590 return B_BAD_PORT_ID;
1591 }
1592
1593 if (portRef->write_count <= 0) {
1594 if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0)
1595 return B_WOULD_BLOCK;
1596
1597 portRef->write_count--;
1598
1599 // We need to block in order to wait for a free message slot
1600 ConditionVariableEntry entry;
1601 portRef->write_condition.Add(&entry);
1602
1603 locker.Unlock();
1604
1605 status = entry.Wait(flags, timeout);
1606
1607 // re-lock
1608 BReference<Port> newPortRef = get_locked_port(id);
1609 if (newPortRef == NULL) {
1610 T(Write(id, 0, 0, 0, 0, B_BAD_PORT_ID));
1611 return B_BAD_PORT_ID;
1612 }
1613 locker.SetTo(newPortRef->lock, true);
1614
1615 if (newPortRef != portRef || is_port_closed(portRef)) {
1616 // the port is no longer there
1617 T(Write(id, 0, 0, 0, 0, B_BAD_PORT_ID));
1618 return B_BAD_PORT_ID;
1619 }
1620
1621 if (status != B_OK)
1622 goto error;
1623 } else
1624 portRef->write_count--;
1625
1626 status = get_port_message(msgCode, bufferSize, flags, timeout,
1627 &message, *portRef);
1628 if (status != B_OK) {
1629 if (status == B_BAD_PORT_ID) {
1630 // the port had to be unlocked and is now no longer there
1631 T(Write(id, 0, 0, 0, 0, B_BAD_PORT_ID));
1632 return B_BAD_PORT_ID;
1633 }
1634
1635 goto error;
1636 }
1637
1638 // sender credentials
1639 message->sender = geteuid();
1640 message->sender_group = getegid();
1641 message->sender_team = team_get_current_team_id();
1642
1643 if (bufferSize > 0) {
1644 size_t offset = 0;
1645 for (uint32 i = 0; i < vecCount; i++) {
1646 size_t bytes = msgVecs[i].iov_len;
1647 if (bytes > bufferSize)
1648 bytes = bufferSize;
1649
1650 if (userCopy) {
1651 status_t status = user_memcpy(message->buffer + offset,
1652 msgVecs[i].iov_base, bytes);
1653 if (status != B_OK) {
1654 put_port_message(message);
1655 goto error;
1656 }
1657 } else
1658 memcpy(message->buffer + offset, msgVecs[i].iov_base, bytes);
1659
1660 bufferSize -= bytes;
1661 if (bufferSize == 0)
1662 break;
1663
1664 offset += bytes;
1665 }
1666 }
1667
1668 portRef->messages.Add(message);
1669 portRef->read_count++;
1670
1671 T(Write(id, portRef->read_count, portRef->write_count, message->code,
1672 message->size, B_OK));
1673
1674 notify_port_select_events(portRef, B_EVENT_READ);
1675 portRef->read_condition.NotifyOne();
1676 return B_OK;
1677
1678 error:
1679 // Give up our slot in the queue again, and let someone else
1680 // try and fail
1681 T(Write(id, portRef->read_count, portRef->write_count, 0, 0, status));
1682 portRef->write_count++;
1683 notify_port_select_events(portRef, B_EVENT_WRITE);
1684 portRef->write_condition.NotifyOne();
1685
1686 return status;
1687 }
1688
1689
1690 status_t
set_port_owner(port_id id,team_id newTeamID)1691 set_port_owner(port_id id, team_id newTeamID)
1692 {
1693 TRACE(("set_port_owner(id = %ld, team = %ld)\n", id, newTeamID));
1694
1695 if (id < 0)
1696 return B_BAD_PORT_ID;
1697
1698 // get the new team
1699 Team* team = Team::Get(newTeamID);
1700 if (team == NULL)
1701 return B_BAD_TEAM_ID;
1702 BReference<Team> teamReference(team, true);
1703
1704 // get the port
1705 BReference<Port> portRef = get_locked_port(id);
1706 if (portRef == NULL) {
1707 TRACE(("set_port_owner: invalid port_id %ld\n", id));
1708 return B_BAD_PORT_ID;
1709 }
1710 MutexLocker locker(portRef->lock, true);
1711
1712 // transfer ownership to other team
1713 if (team->id != portRef->owner) {
1714 uint8 firstLockIndex = portRef->owner % kTeamListLockCount;
1715 uint8 secondLockIndex = team->id % kTeamListLockCount;
1716
1717 // Avoid deadlocks: always lock lower index first
1718 if (secondLockIndex < firstLockIndex) {
1719 uint8 temp = secondLockIndex;
1720 secondLockIndex = firstLockIndex;
1721 firstLockIndex = temp;
1722 }
1723
1724 MutexLocker oldTeamPortsListLocker(sTeamListLock[firstLockIndex]);
1725 MutexLocker newTeamPortsListLocker;
1726 if (firstLockIndex != secondLockIndex) {
1727 newTeamPortsListLocker.SetTo(sTeamListLock[secondLockIndex],
1728 false);
1729 }
1730
1731 // Now that we have locked the team port lists, check the state again
1732 if (portRef->state == Port::kActive) {
1733 list_remove_link(&portRef->team_link);
1734 list_add_item(&team->port_list, portRef.Get());
1735 portRef->owner = team->id;
1736 } else {
1737 // Port was already deleted. We haven't changed anything yet so
1738 // we can cancel the operation.
1739 return B_BAD_PORT_ID;
1740 }
1741 }
1742
1743 T(OwnerChange(portRef, team->id, B_OK));
1744 return B_OK;
1745 }
1746
1747
1748 // #pragma mark - syscalls
1749
1750
1751 port_id
_user_create_port(int32 queueLength,const char * userName)1752 _user_create_port(int32 queueLength, const char *userName)
1753 {
1754 char name[B_OS_NAME_LENGTH];
1755
1756 if (userName == NULL)
1757 return create_port(queueLength, NULL);
1758
1759 if (!IS_USER_ADDRESS(userName)
1760 || user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK)
1761 return B_BAD_ADDRESS;
1762
1763 return create_port(queueLength, name);
1764 }
1765
1766
1767 status_t
_user_close_port(port_id id)1768 _user_close_port(port_id id)
1769 {
1770 return close_port(id);
1771 }
1772
1773
1774 status_t
_user_delete_port(port_id id)1775 _user_delete_port(port_id id)
1776 {
1777 return delete_port(id);
1778 }
1779
1780
1781 port_id
_user_find_port(const char * userName)1782 _user_find_port(const char *userName)
1783 {
1784 char name[B_OS_NAME_LENGTH];
1785
1786 if (userName == NULL)
1787 return B_BAD_VALUE;
1788 if (!IS_USER_ADDRESS(userName)
1789 || user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK)
1790 return B_BAD_ADDRESS;
1791
1792 return find_port(name);
1793 }
1794
1795
1796 status_t
_user_get_port_info(port_id id,struct port_info * userInfo)1797 _user_get_port_info(port_id id, struct port_info *userInfo)
1798 {
1799 struct port_info info;
1800 status_t status;
1801
1802 if (userInfo == NULL)
1803 return B_BAD_VALUE;
1804 if (!IS_USER_ADDRESS(userInfo))
1805 return B_BAD_ADDRESS;
1806
1807 status = get_port_info(id, &info);
1808
1809 // copy back to user space
1810 if (status == B_OK
1811 && user_memcpy(userInfo, &info, sizeof(struct port_info)) < B_OK)
1812 return B_BAD_ADDRESS;
1813
1814 return status;
1815 }
1816
1817
1818 status_t
_user_get_next_port_info(team_id team,int32 * userCookie,struct port_info * userInfo)1819 _user_get_next_port_info(team_id team, int32 *userCookie,
1820 struct port_info *userInfo)
1821 {
1822 struct port_info info;
1823 status_t status;
1824 int32 cookie;
1825
1826 if (userCookie == NULL || userInfo == NULL)
1827 return B_BAD_VALUE;
1828 if (!IS_USER_ADDRESS(userCookie) || !IS_USER_ADDRESS(userInfo)
1829 || user_memcpy(&cookie, userCookie, sizeof(int32)) < B_OK)
1830 return B_BAD_ADDRESS;
1831
1832 status = get_next_port_info(team, &cookie, &info);
1833
1834 // copy back to user space
1835 if (user_memcpy(userCookie, &cookie, sizeof(int32)) < B_OK
1836 || (status == B_OK && user_memcpy(userInfo, &info,
1837 sizeof(struct port_info)) < B_OK))
1838 return B_BAD_ADDRESS;
1839
1840 return status;
1841 }
1842
1843
1844 ssize_t
_user_port_buffer_size_etc(port_id port,uint32 flags,bigtime_t timeout)1845 _user_port_buffer_size_etc(port_id port, uint32 flags, bigtime_t timeout)
1846 {
1847 syscall_restart_handle_timeout_pre(flags, timeout);
1848
1849 status_t status = port_buffer_size_etc(port, flags | B_CAN_INTERRUPT,
1850 timeout);
1851
1852 return syscall_restart_handle_timeout_post(status, timeout);
1853 }
1854
1855
1856 ssize_t
_user_port_count(port_id port)1857 _user_port_count(port_id port)
1858 {
1859 return port_count(port);
1860 }
1861
1862
1863 status_t
_user_set_port_owner(port_id port,team_id team)1864 _user_set_port_owner(port_id port, team_id team)
1865 {
1866 return set_port_owner(port, team);
1867 }
1868
1869
1870 ssize_t
_user_read_port_etc(port_id port,int32 * userCode,void * userBuffer,size_t bufferSize,uint32 flags,bigtime_t timeout)1871 _user_read_port_etc(port_id port, int32 *userCode, void *userBuffer,
1872 size_t bufferSize, uint32 flags, bigtime_t timeout)
1873 {
1874 int32 messageCode;
1875 ssize_t bytesRead;
1876
1877 syscall_restart_handle_timeout_pre(flags, timeout);
1878
1879 if (userBuffer == NULL && bufferSize != 0)
1880 return B_BAD_VALUE;
1881 if ((userCode != NULL && !IS_USER_ADDRESS(userCode))
1882 || (userBuffer != NULL && !IS_USER_ADDRESS(userBuffer)))
1883 return B_BAD_ADDRESS;
1884
1885 bytesRead = read_port_etc(port, &messageCode, userBuffer, bufferSize,
1886 flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, timeout);
1887
1888 if (bytesRead >= 0 && userCode != NULL
1889 && user_memcpy(userCode, &messageCode, sizeof(int32)) < B_OK)
1890 return B_BAD_ADDRESS;
1891
1892 return syscall_restart_handle_timeout_post(bytesRead, timeout);
1893 }
1894
1895
1896 status_t
_user_write_port_etc(port_id port,int32 messageCode,const void * userBuffer,size_t bufferSize,uint32 flags,bigtime_t timeout)1897 _user_write_port_etc(port_id port, int32 messageCode, const void *userBuffer,
1898 size_t bufferSize, uint32 flags, bigtime_t timeout)
1899 {
1900 iovec vec = { (void *)userBuffer, bufferSize };
1901
1902 syscall_restart_handle_timeout_pre(flags, timeout);
1903
1904 if (userBuffer == NULL && bufferSize != 0)
1905 return B_BAD_VALUE;
1906 if (userBuffer != NULL && !IS_USER_ADDRESS(userBuffer))
1907 return B_BAD_ADDRESS;
1908
1909 status_t status = writev_port_etc(port, messageCode, &vec, 1, bufferSize,
1910 flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, timeout);
1911
1912 return syscall_restart_handle_timeout_post(status, timeout);
1913 }
1914
1915
1916 status_t
_user_writev_port_etc(port_id port,int32 messageCode,const iovec * userVecs,size_t vecCount,size_t bufferSize,uint32 flags,bigtime_t timeout)1917 _user_writev_port_etc(port_id port, int32 messageCode, const iovec *userVecs,
1918 size_t vecCount, size_t bufferSize, uint32 flags, bigtime_t timeout)
1919 {
1920 syscall_restart_handle_timeout_pre(flags, timeout);
1921
1922 if (userVecs == NULL && bufferSize != 0)
1923 return B_BAD_VALUE;
1924 if (vecCount > IOV_MAX)
1925 return B_BAD_VALUE;
1926
1927 BStackOrHeapArray<iovec, 16> vecs(vecCount);
1928 if (!vecs.IsValid())
1929 return B_NO_MEMORY;
1930
1931 if (userVecs != NULL && vecCount != 0) {
1932 status_t status = get_iovecs_from_user(userVecs, vecCount, vecs);
1933 if (status != B_OK)
1934 return status;
1935 }
1936
1937 status_t status = writev_port_etc(port, messageCode, vecs, vecCount,
1938 bufferSize, flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT,
1939 timeout);
1940
1941 return syscall_restart_handle_timeout_post(status, timeout);
1942 }
1943
1944
1945 status_t
_user_get_port_message_info_etc(port_id port,port_message_info * userInfo,size_t infoSize,uint32 flags,bigtime_t timeout)1946 _user_get_port_message_info_etc(port_id port, port_message_info *userInfo,
1947 size_t infoSize, uint32 flags, bigtime_t timeout)
1948 {
1949 if (userInfo == NULL || infoSize != sizeof(port_message_info))
1950 return B_BAD_VALUE;
1951
1952 syscall_restart_handle_timeout_pre(flags, timeout);
1953
1954 port_message_info info;
1955 status_t error = _get_port_message_info_etc(port, &info, sizeof(info),
1956 flags | B_CAN_INTERRUPT, timeout);
1957
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;
1962 }
1963
1964 return syscall_restart_handle_timeout_post(error, timeout);
1965 }
1966