xref: /haiku/src/system/kernel/port.cpp (revision 13581b3d2a71545960b98fefebc5225b5bf29072)
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 
137 	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 
155 	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 
169 	size_t HashKey(port_id key) const
170 	{
171 		return key;
172 	}
173 
174 	size_t Hash(Port* value) const
175 	{
176 		return HashKey(value->id);
177 	}
178 
179 	bool Compare(port_id key, Port* value) const
180 	{
181 		return value->id == key;
182 	}
183 
184 	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 
197 	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 
211 	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 
219 	bool Compare(const char* key, Port* value) const
220 	{
221 		return (strcmp(key, value->lock.name) == 0);
222 	}
223 
224 	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:
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 
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:
279 	Delete(Port* port)
280 		:
281 		fID(port->id)
282 	{
283 		Initialized();
284 	}
285 
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:
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 
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 
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:
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 
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:
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 
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 
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:
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 
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 
481 PortNotificationService::PortNotificationService()
482 	:
483 	DefaultNotificationService("ports")
484 {
485 }
486 
487 
488 void
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
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
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
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
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>
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>
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
663 is_port_closed(Port* port)
664 {
665 	return port->capacity == 0;
666 }
667 
668 
669 static void
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
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
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
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
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
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
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
908 port_max_ports(void)
909 {
910 	return sMaxPorts;
911 }
912 
913 
914 int32
915 port_used_ports(void)
916 {
917 	return sUsedPorts;
918 }
919 
920 
921 size_t
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
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
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
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
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
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
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
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
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
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
1315 port_buffer_size(port_id id)
1316 {
1317 	return port_buffer_size_etc(id, 0, 0);
1318 }
1319 
1320 
1321 ssize_t
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
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
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
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
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
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
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
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
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
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
1768 _user_close_port(port_id id)
1769 {
1770 	return close_port(id);
1771 }
1772 
1773 
1774 status_t
1775 _user_delete_port(port_id id)
1776 {
1777 	return delete_port(id);
1778 }
1779 
1780 
1781 port_id
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
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
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
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
1857 _user_port_count(port_id port)
1858 {
1859 	return port_count(port);
1860 }
1861 
1862 
1863 status_t
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
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
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
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
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