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