xref: /haiku/src/system/kernel/port.cpp (revision f7c507c3a6fbf3a44c59500543926a9088724968)
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 <vm/vm.h>
37 #include <wait_for_objects.h>
38 
39 
40 //#define TRACE_PORTS
41 #ifdef TRACE_PORTS
42 #	define TRACE(x) dprintf x
43 #else
44 #	define TRACE(x)
45 #endif
46 
47 
48 #if __GNUC__ >= 3
49 #	define GCC_2_NRV(x)
50 	// GCC >= 3.1 doesn't need it anymore
51 #else
52 #	define GCC_2_NRV(x) return x;
53 	// GCC 2 named return value syntax
54 	// see http://gcc.gnu.org/onlinedocs/gcc-2.95.2/gcc_5.html#SEC106
55 #endif
56 
57 
58 // Locking:
59 // * sPortsLock: Protects the sPorts and sPortsByName hash tables.
60 // * sTeamListLock[]: Protects Team::port_list. Lock index for given team is
61 //   (Team::id % kTeamListLockCount).
62 // * Port::lock: Protects all Port members save team_link, hash_link, lock and
63 //   state. id is immutable.
64 //
65 // Port::state ensures atomicity by providing a linearization point for adding
66 // and removing ports to the hash tables and the team port list.
67 // * sPortsLock and sTeamListLock[] are locked separately and not in a nested
68 //   fashion, so a port can be in the hash table but not in the team port list
69 //   or vice versa. => Without further provisions, insertion and removal are
70 //   not linearizable and thus not concurrency-safe.
71 // * To make insertion and removal linearizable, Port::state was added. It is
72 //   always only accessed atomically and updates are done using
73 //   atomic_test_and_set(). A port is only seen as existent when its state is
74 //   Port::kActive.
75 // * Deletion of ports is done in two steps: logical and physical deletion.
76 //   First, logical deletion happens and sets Port::state to Port::kDeleted.
77 //   This is an atomic operation and from then on, functions like
78 //   get_locked_port() consider this port as deleted and ignore it. Secondly,
79 //   physical deletion removes the port from hash tables and team port list.
80 //   In a similar way, port creation first inserts into hashes and team list
81 //   and only then sets port to Port::kActive.
82 //   This creates a linearization point at the atomic update of Port::state,
83 //   operations become linearizable and thus concurrency-safe. To help
84 //   understanding, the linearization points are annotated with comments.
85 // * Ports are reference-counted so it's not a problem when someone still
86 //   has a reference to a deleted port.
87 
88 
89 namespace {
90 
91 struct port_message : DoublyLinkedListLinkImpl<port_message> {
92 	int32				code;
93 	size_t				size;
94 	uid_t				sender;
95 	gid_t				sender_group;
96 	team_id				sender_team;
97 	char				buffer[0];
98 };
99 
100 typedef DoublyLinkedList<port_message> MessageList;
101 
102 } // namespace
103 
104 
105 static void put_port_message(port_message* message);
106 
107 
108 namespace {
109 
110 struct Port : public KernelReferenceable {
111 	enum State {
112 		kUnused = 0,
113 		kActive,
114 		kDeleted
115 	};
116 
117 	struct list_link	team_link;
118 	Port*				hash_link;
119 	port_id				id;
120 	team_id				owner;
121 	Port*				name_hash_link;
122 	size_t				name_hash;
123 	int32				capacity;
124 	mutex				lock;
125 	int32				state;
126 	uint32				read_count;
127 	int32				write_count;
128 	ConditionVariable	read_condition;
129 	ConditionVariable	write_condition;
130 	int32				total_count;
131 		// messages read from port since creation
132 	select_info*		select_infos;
133 	MessageList			messages;
134 
135 	Port(team_id owner, int32 queueLength, char* name)
136 		:
137 		owner(owner),
138 		name_hash(0),
139 		capacity(queueLength),
140 		state(kUnused),
141 		read_count(0),
142 		write_count(queueLength),
143 		total_count(0),
144 		select_infos(NULL)
145 	{
146 		// id is initialized when the caller adds the port to the hash table
147 
148 		mutex_init(&lock, name);
149 		read_condition.Init(this, "port read");
150 		write_condition.Init(this, "port write");
151 	}
152 
153 	virtual ~Port()
154 	{
155 		while (port_message* message = messages.RemoveHead())
156 			put_port_message(message);
157 
158 		free((char*)lock.name);
159 		lock.name = NULL;
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 		mutex_lock(&portRef->lock);
639 	else
640 		portRef.Unset();
641 
642 	return portRef;
643 }
644 
645 
646 static BReference<Port>
647 get_port(port_id id) GCC_2_NRV(portRef)
648 {
649 #if __GNUC__ >= 3
650 	BReference<Port> portRef;
651 #endif
652 	ReadLocker portsLocker(sPortsLock);
653 	portRef.SetTo(sPorts.Lookup(id));
654 
655 	return portRef;
656 }
657 
658 
659 /*!	You need to own the port's lock when calling this function */
660 static inline bool
661 is_port_closed(Port* port)
662 {
663 	return port->capacity == 0;
664 }
665 
666 
667 static void
668 put_port_message(port_message* message)
669 {
670 	const size_t size = sizeof(port_message) + message->size;
671 	free(message);
672 
673 	atomic_add(&sTotalSpaceCommited, -size);
674 	if (sWaitingForSpace > 0)
675 		sNoSpaceCondition.NotifyAll();
676 }
677 
678 
679 /*! Port must be locked. */
680 static status_t
681 get_port_message(int32 code, size_t bufferSize, uint32 flags, bigtime_t timeout,
682 	port_message** _message, Port& port)
683 {
684 	const size_t size = sizeof(port_message) + bufferSize;
685 
686 	while (true) {
687 		int32 previouslyCommited = atomic_add(&sTotalSpaceCommited, size);
688 
689 		while (previouslyCommited + size > kTotalSpaceLimit) {
690 			// TODO: add per team limit
691 
692 			// We are not allowed to allocate more memory, as our
693 			// space limit has been reached - just wait until we get
694 			// some free space again.
695 
696 			atomic_add(&sTotalSpaceCommited, -size);
697 
698 			// TODO: we don't want to wait - but does that also mean we
699 			// shouldn't wait for free memory?
700 			if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0)
701 				return B_WOULD_BLOCK;
702 
703 			ConditionVariableEntry entry;
704 			sNoSpaceCondition.Add(&entry);
705 
706 			port_id portID = port.id;
707 			mutex_unlock(&port.lock);
708 
709 			atomic_add(&sWaitingForSpace, 1);
710 
711 			// TODO: right here the condition could be notified and we'd
712 			//       miss it.
713 
714 			status_t status = entry.Wait(flags, timeout);
715 
716 			atomic_add(&sWaitingForSpace, -1);
717 
718 			// re-lock the port
719 			BReference<Port> newPortRef = get_locked_port(portID);
720 
721 			if (newPortRef.Get() != &port || is_port_closed(&port)) {
722 				// the port is no longer usable
723 				return B_BAD_PORT_ID;
724 			}
725 
726 			if (status == B_TIMED_OUT)
727 				return B_TIMED_OUT;
728 
729 			previouslyCommited = atomic_add(&sTotalSpaceCommited, size);
730 			continue;
731 		}
732 
733 		// Quota is fulfilled, try to allocate the buffer
734 		port_message* message = (port_message*)malloc(size);
735 		if (message != NULL) {
736 			message->code = code;
737 			message->size = bufferSize;
738 
739 			*_message = message;
740 			return B_OK;
741 		}
742 
743 		// We weren't able to allocate and we'll start over,so we remove our
744 		// size from the commited-counter again.
745 		atomic_add(&sTotalSpaceCommited, -size);
746 		continue;
747 	}
748 }
749 
750 
751 /*!	Fills the port_info structure with information from the specified
752 	port.
753 	The port's lock must be held when called.
754 */
755 static void
756 fill_port_info(Port* port, port_info* info, size_t size)
757 {
758 	info->port = port->id;
759 	info->team = port->owner;
760 	info->capacity = port->capacity;
761 
762 	info->queue_count = port->read_count;
763 	info->total_count = port->total_count;
764 
765 	strlcpy(info->name, port->lock.name, B_OS_NAME_LENGTH);
766 }
767 
768 
769 static ssize_t
770 copy_port_message(port_message* message, int32* _code, void* buffer,
771 	size_t bufferSize, bool userCopy)
772 {
773 	// check output buffer size
774 	size_t size = std::min(bufferSize, message->size);
775 
776 	// copy message
777 	if (_code != NULL)
778 		*_code = message->code;
779 
780 	if (size > 0) {
781 		if (userCopy) {
782 			status_t status = user_memcpy(buffer, message->buffer, size);
783 			if (status != B_OK)
784 				return status;
785 		} else
786 			memcpy(buffer, message->buffer, size);
787 	}
788 
789 	return size;
790 }
791 
792 
793 static void
794 uninit_port(Port* port)
795 {
796 	MutexLocker locker(port->lock);
797 
798 	notify_port_select_events(port, B_EVENT_INVALID);
799 	port->select_infos = NULL;
800 
801 	// Release the threads that were blocking on this port.
802 	// read_port() will see the B_BAD_PORT_ID return value, and act accordingly
803 	port->read_condition.NotifyAll(B_BAD_PORT_ID);
804 	port->write_condition.NotifyAll(B_BAD_PORT_ID);
805 	sNotificationService.Notify(PORT_REMOVED, port->id);
806 }
807 
808 
809 /*! Caller must ensure there is still a reference to the port. (Either by
810  *  holding a reference itself or by holding a lock on one of the data
811  *  structures in which it is referenced.)
812  */
813 static status_t
814 delete_port_logical(Port* port)
815 {
816 	for (;;) {
817 		// Try to logically delete
818 		const int32 oldState = atomic_test_and_set(&port->state,
819 			Port::kDeleted, Port::kActive);
820 			// Linearization point for port deletion
821 
822 		switch (oldState) {
823 			case Port::kActive:
824 				// Logical deletion succesful
825 				return B_OK;
826 
827 			case Port::kDeleted:
828 				// Someone else already deleted it in the meantime
829 				TRACE(("delete_port_logical: already deleted port_id %ld\n",
830 						port->id));
831 				return B_BAD_PORT_ID;
832 
833 			case Port::kUnused:
834 				// Port is still being created, retry
835 				continue;
836 
837 			default:
838 				// Port state got corrupted somehow
839 				panic("Invalid port state!\n");
840 		}
841 	}
842 }
843 
844 
845 //	#pragma mark - private kernel API
846 
847 
848 /*! This function deletes all the ports that are owned by the passed team.
849 */
850 void
851 delete_owned_ports(Team* team)
852 {
853 	TRACE(("delete_owned_ports(owner = %ld)\n", team->id));
854 
855 	list deletionList;
856 	list_init_etc(&deletionList, port_team_link_offset());
857 
858 	const uint8 lockIndex = team->id % kTeamListLockCount;
859 	MutexLocker teamPortsListLocker(sTeamListLock[lockIndex]);
860 
861 	// Try to logically delete all ports from the team's port list.
862 	// On success, move the port to deletionList.
863 	Port* port = (Port*)list_get_first_item(&team->port_list);
864 	while (port != NULL) {
865 		status_t status = delete_port_logical(port);
866 			// Contains linearization point
867 
868 		Port* nextPort = (Port*)list_get_next_item(&team->port_list, port);
869 
870 		if (status == B_OK) {
871 			list_remove_link(&port->team_link);
872 			list_add_item(&deletionList, port);
873 		}
874 
875 		port = nextPort;
876 	}
877 
878 	teamPortsListLocker.Unlock();
879 
880 	// Remove all ports in deletionList from hashes
881 	{
882 		WriteLocker portsLocker(sPortsLock);
883 
884 		for (Port* port = (Port*)list_get_first_item(&deletionList);
885 			 port != NULL;
886 			 port = (Port*)list_get_next_item(&deletionList, port)) {
887 
888 			sPorts.Remove(port);
889 			sPortsByName.Remove(port);
890 			port->ReleaseReference();
891 				// joint reference for sPorts and sPortsByName
892 		}
893 	}
894 
895 	// Uninitialize ports and release team port list references
896 	while (Port* port = (Port*)list_remove_head_item(&deletionList)) {
897 		atomic_add(&sUsedPorts, -1);
898 		uninit_port(port);
899 		port->ReleaseReference();
900 			// Reference for team port list
901 	}
902 }
903 
904 
905 int32
906 port_max_ports(void)
907 {
908 	return sMaxPorts;
909 }
910 
911 
912 int32
913 port_used_ports(void)
914 {
915 	return sUsedPorts;
916 }
917 
918 
919 size_t
920 port_team_link_offset()
921 {
922 	// Somewhat ugly workaround since we cannot use offsetof() for a class
923 	// with vtable (GCC4 throws a warning then).
924 	Port* port = (Port*)0;
925 	return (size_t)&port->team_link;
926 }
927 
928 
929 status_t
930 port_init(kernel_args *args)
931 {
932 	// initialize ports table and by-name hash
933 	new(&sPorts) PortHashTable;
934 	if (sPorts.Init() != B_OK) {
935 		panic("Failed to init port hash table!");
936 		return B_NO_MEMORY;
937 	}
938 
939 	new(&sPortsByName) PortNameHashTable;
940 	if (sPortsByName.Init() != B_OK) {
941 		panic("Failed to init port by name hash table!");
942 		return B_NO_MEMORY;
943 	}
944 
945 	sNoSpaceCondition.Init(&sPorts, "port space");
946 
947 	// add debugger commands
948 	add_debugger_command_etc("ports", &dump_port_list,
949 		"Dump a list of all active ports (for team, with name, etc.)",
950 		"[ ([ \"team\" | \"owner\" ] <team>) | (\"name\" <name>) ]\n"
951 		"Prints a list of all active ports meeting the given\n"
952 		"requirement. If no argument is given, all ports are listed.\n"
953 		"  <team>             - The team owning the ports.\n"
954 		"  <name>             - Part of the name of the ports.\n", 0);
955 	add_debugger_command_etc("port", &dump_port_info,
956 		"Dump info about a particular port",
957 		"(<id> | [ \"address\" ] <address>) | ([ \"name\" ] <name>) "
958 			"| (\"condition\" <address>)\n"
959 		"Prints info about the specified port.\n"
960 		"  <address>   - Pointer to the port structure.\n"
961 		"  <name>      - Name of the port.\n"
962 		"  <condition> - address of the port's read or write condition.\n", 0);
963 
964 	new(&sNotificationService) PortNotificationService();
965 	sNotificationService.Register();
966 	sPortsActive = true;
967 	return B_OK;
968 }
969 
970 
971 //	#pragma mark - public kernel API
972 
973 
974 port_id
975 create_port(int32 queueLength, const char* name)
976 {
977 	TRACE(("create_port(queueLength = %ld, name = \"%s\")\n", queueLength,
978 		name));
979 
980 	if (!sPortsActive) {
981 		panic("ports used too early!\n");
982 		return B_BAD_PORT_ID;
983 	}
984 	if (queueLength < 1 || queueLength > MAX_QUEUE_LENGTH)
985 		return B_BAD_VALUE;
986 
987 	Team* team = thread_get_current_thread()->team;
988 	if (team == NULL)
989 		return B_BAD_TEAM_ID;
990 
991 	// check & dup name
992 	char* nameBuffer = strdup(name != NULL ? name : "unnamed port");
993 	if (nameBuffer == NULL)
994 		return B_NO_MEMORY;
995 
996 	// create a port
997 	Port* port = new(std::nothrow) Port(team_get_current_team_id(), queueLength,
998 		nameBuffer);
999 	if (port == NULL) {
1000 		free(nameBuffer);
1001 		return B_NO_MEMORY;
1002 	}
1003 
1004 	// check the ports limit
1005 	const int32 previouslyUsed = atomic_add(&sUsedPorts, 1);
1006 	if (previouslyUsed + 1 >= sMaxPorts) {
1007 		atomic_add(&sUsedPorts, -1);
1008 		delete port;
1009 		return B_NO_MORE_PORTS;
1010 	}
1011 
1012 	{
1013 		WriteLocker locker(sPortsLock);
1014 
1015 		// allocate a port ID
1016 		do {
1017 			port->id = sNextPortID++;
1018 
1019 			// handle integer overflow
1020 			if (sNextPortID < 0)
1021 				sNextPortID = 1;
1022 		} while (sPorts.Lookup(port->id) != NULL);
1023 
1024 		// Insert port physically:
1025 		// (1/2) Insert into hash tables
1026 		port->AcquireReference();
1027 			// joint reference for sPorts and sPortsByName
1028 
1029 		sPorts.Insert(port);
1030 		sPortsByName.Insert(port);
1031 	}
1032 
1033 	// (2/2) Insert into team list
1034 	{
1035 		const uint8 lockIndex = port->owner % kTeamListLockCount;
1036 		MutexLocker teamPortsListLocker(sTeamListLock[lockIndex]);
1037 		port->AcquireReference();
1038 		list_add_item(&team->port_list, port);
1039 	}
1040 
1041 	// tracing, notifications, etc.
1042 	T(Create(port));
1043 
1044 	const port_id id = port->id;
1045 
1046 	// Insert port logically by marking it active
1047 	const int32 oldState = atomic_test_and_set(&port->state,
1048 		Port::kActive, Port::kUnused);
1049 		// Linearization point for port creation
1050 
1051 	if (oldState != Port::kUnused) {
1052 		// Nobody is allowed to tamper with the port before it's active.
1053 		panic("Port state was modified during creation!\n");
1054 	}
1055 
1056 	TRACE(("create_port() done: port created %ld\n", id));
1057 
1058 	sNotificationService.Notify(PORT_ADDED, id);
1059 	return id;
1060 }
1061 
1062 
1063 status_t
1064 close_port(port_id id)
1065 {
1066 	TRACE(("close_port(id = %ld)\n", id));
1067 
1068 	if (!sPortsActive || id < 0)
1069 		return B_BAD_PORT_ID;
1070 
1071 	// get the port
1072 	BReference<Port> portRef = get_locked_port(id);
1073 	if (portRef == NULL) {
1074 		TRACE(("close_port: invalid port_id %ld\n", id));
1075 		return B_BAD_PORT_ID;
1076 	}
1077 	MutexLocker lock(&portRef->lock, true);
1078 
1079 	// mark port to disable writing - deleting the semaphores will
1080 	// wake up waiting read/writes
1081 	portRef->capacity = 0;
1082 
1083 	notify_port_select_events(portRef, B_EVENT_INVALID);
1084 	portRef->select_infos = NULL;
1085 
1086 	portRef->read_condition.NotifyAll(B_BAD_PORT_ID);
1087 	portRef->write_condition.NotifyAll(B_BAD_PORT_ID);
1088 
1089 	return B_OK;
1090 }
1091 
1092 
1093 status_t
1094 delete_port(port_id id)
1095 {
1096 	TRACE(("delete_port(id = %ld)\n", id));
1097 
1098 	if (!sPortsActive || id < 0)
1099 		return B_BAD_PORT_ID;
1100 
1101 	BReference<Port> portRef = get_port(id);
1102 
1103 	if (portRef == NULL) {
1104 		TRACE(("delete_port: invalid port_id %ld\n", id));
1105 		return B_BAD_PORT_ID;
1106 	}
1107 
1108 	status_t status = delete_port_logical(portRef);
1109 		// Contains linearization point
1110 	if (status != B_OK)
1111 		return status;
1112 
1113 	// Now remove port physically:
1114 	// (1/2) Remove from hash tables
1115 	{
1116 		WriteLocker portsLocker(sPortsLock);
1117 
1118 		sPorts.Remove(portRef);
1119 		sPortsByName.Remove(portRef);
1120 
1121 		portRef->ReleaseReference();
1122 			// joint reference for sPorts and sPortsByName
1123 	}
1124 
1125 	// (2/2) Remove from team port list
1126 	{
1127 		const uint8 lockIndex = portRef->owner % kTeamListLockCount;
1128 		MutexLocker teamPortsListLocker(sTeamListLock[lockIndex]);
1129 
1130 		list_remove_link(&portRef->team_link);
1131 		portRef->ReleaseReference();
1132 	}
1133 
1134 	uninit_port(portRef);
1135 
1136 	T(Delete(portRef));
1137 
1138 	atomic_add(&sUsedPorts, -1);
1139 
1140 	return B_OK;
1141 }
1142 
1143 
1144 status_t
1145 select_port(int32 id, struct select_info* info, bool kernel)
1146 {
1147 	if (id < 0)
1148 		return B_BAD_PORT_ID;
1149 
1150 	// get the port
1151 	BReference<Port> portRef = get_locked_port(id);
1152 	if (portRef == NULL)
1153 		return B_BAD_PORT_ID;
1154 	MutexLocker locker(portRef->lock, true);
1155 
1156 	// port must not yet be closed
1157 	if (is_port_closed(portRef))
1158 		return B_BAD_PORT_ID;
1159 
1160 	if (!kernel && portRef->owner == team_get_kernel_team_id()) {
1161 		// kernel port, but call from userland
1162 		return B_NOT_ALLOWED;
1163 	}
1164 
1165 	info->selected_events &= B_EVENT_READ | B_EVENT_WRITE | B_EVENT_INVALID;
1166 
1167 	if (info->selected_events != 0) {
1168 		uint16 events = 0;
1169 
1170 		info->next = portRef->select_infos;
1171 		portRef->select_infos = info;
1172 
1173 		// check for events
1174 		if ((info->selected_events & B_EVENT_READ) != 0
1175 			&& !portRef->messages.IsEmpty()) {
1176 			events |= B_EVENT_READ;
1177 		}
1178 
1179 		if (portRef->write_count > 0)
1180 			events |= B_EVENT_WRITE;
1181 
1182 		if (events != 0)
1183 			notify_select_events(info, events);
1184 	}
1185 
1186 	return B_OK;
1187 }
1188 
1189 
1190 status_t
1191 deselect_port(int32 id, struct select_info* info, bool kernel)
1192 {
1193 	if (id < 0)
1194 		return B_BAD_PORT_ID;
1195 	if (info->selected_events == 0)
1196 		return B_OK;
1197 
1198 	// get the port
1199 	BReference<Port> portRef = get_locked_port(id);
1200 	if (portRef == NULL)
1201 		return B_BAD_PORT_ID;
1202 	MutexLocker locker(portRef->lock, true);
1203 
1204 	// find and remove the infos
1205 	select_info** infoLocation = &portRef->select_infos;
1206 	while (*infoLocation != NULL && *infoLocation != info)
1207 		infoLocation = &(*infoLocation)->next;
1208 
1209 	if (*infoLocation == info)
1210 		*infoLocation = info->next;
1211 
1212 	return B_OK;
1213 }
1214 
1215 
1216 port_id
1217 find_port(const char* name)
1218 {
1219 	TRACE(("find_port(name = \"%s\")\n", name));
1220 
1221 	if (!sPortsActive) {
1222 		panic("ports used too early!\n");
1223 		return B_NAME_NOT_FOUND;
1224 	}
1225 	if (name == NULL)
1226 		return B_BAD_VALUE;
1227 
1228 	ReadLocker locker(sPortsLock);
1229 	Port* port = sPortsByName.Lookup(name);
1230 		// Since we have sPortsLock and don't return the port itself,
1231 		// no BReference necessary
1232 
1233 	if (port != NULL && port->state == Port::kActive)
1234 		return port->id;
1235 
1236 	return B_NAME_NOT_FOUND;
1237 }
1238 
1239 
1240 status_t
1241 _get_port_info(port_id id, port_info* info, size_t size)
1242 {
1243 	TRACE(("get_port_info(id = %ld)\n", id));
1244 
1245 	if (info == NULL || size != sizeof(port_info))
1246 		return B_BAD_VALUE;
1247 	if (!sPortsActive || id < 0)
1248 		return B_BAD_PORT_ID;
1249 
1250 	// get the port
1251 	BReference<Port> portRef = get_locked_port(id);
1252 	if (portRef == NULL) {
1253 		TRACE(("get_port_info: invalid port_id %ld\n", id));
1254 		return B_BAD_PORT_ID;
1255 	}
1256 	MutexLocker locker(portRef->lock, true);
1257 
1258 	// fill a port_info struct with info
1259 	fill_port_info(portRef, info, size);
1260 	return B_OK;
1261 }
1262 
1263 
1264 status_t
1265 _get_next_port_info(team_id teamID, int32* _cookie, struct port_info* info,
1266 	size_t size)
1267 {
1268 	TRACE(("get_next_port_info(team = %ld)\n", teamID));
1269 
1270 	if (info == NULL || size != sizeof(port_info) || _cookie == NULL
1271 		|| teamID < 0) {
1272 		return B_BAD_VALUE;
1273 	}
1274 	if (!sPortsActive)
1275 		return B_BAD_PORT_ID;
1276 
1277 	Team* team = Team::Get(teamID);
1278 	if (team == NULL)
1279 		return B_BAD_TEAM_ID;
1280 	BReference<Team> teamReference(team, true);
1281 
1282 	// iterate through the team's port list
1283 	const uint8 lockIndex = teamID % kTeamListLockCount;
1284 	MutexLocker teamPortsListLocker(sTeamListLock[lockIndex]);
1285 
1286 	int32 stopIndex = *_cookie;
1287 	int32 index = 0;
1288 
1289 	Port* port = (Port*)list_get_first_item(&team->port_list);
1290 	while (port != NULL) {
1291 		if (!is_port_closed(port)) {
1292 			if (index == stopIndex)
1293 				break;
1294 			index++;
1295 		}
1296 
1297 		port = (Port*)list_get_next_item(&team->port_list, port);
1298 	}
1299 
1300 	if (port == NULL)
1301 		return B_BAD_PORT_ID;
1302 
1303 	// fill in the port info
1304 	BReference<Port> portRef = port;
1305 	teamPortsListLocker.Unlock();
1306 		// Only use portRef below this line...
1307 
1308 	MutexLocker locker(portRef->lock);
1309 	fill_port_info(portRef, info, size);
1310 
1311 	*_cookie = stopIndex + 1;
1312 	return B_OK;
1313 }
1314 
1315 
1316 ssize_t
1317 port_buffer_size(port_id id)
1318 {
1319 	return port_buffer_size_etc(id, 0, 0);
1320 }
1321 
1322 
1323 ssize_t
1324 port_buffer_size_etc(port_id id, uint32 flags, bigtime_t timeout)
1325 {
1326 	port_message_info info;
1327 	status_t error = get_port_message_info_etc(id, &info, flags, timeout);
1328 	return error != B_OK ? error : info.size;
1329 }
1330 
1331 
1332 status_t
1333 _get_port_message_info_etc(port_id id, port_message_info* info,
1334 	size_t infoSize, uint32 flags, bigtime_t timeout)
1335 {
1336 	if (info == NULL || infoSize != sizeof(port_message_info))
1337 		return B_BAD_VALUE;
1338 	if (!sPortsActive || id < 0)
1339 		return B_BAD_PORT_ID;
1340 
1341 	flags &= B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT | B_RELATIVE_TIMEOUT
1342 		| B_ABSOLUTE_TIMEOUT;
1343 
1344 	// get the port
1345 	BReference<Port> portRef = get_locked_port(id);
1346 	if (portRef == NULL)
1347 		return B_BAD_PORT_ID;
1348 	MutexLocker locker(portRef->lock, true);
1349 
1350 	if (is_port_closed(portRef) && portRef->messages.IsEmpty()) {
1351 		T(Info(portRef, 0, B_BAD_PORT_ID));
1352 		TRACE(("_get_port_message_info_etc(): closed port %ld\n", id));
1353 		return B_BAD_PORT_ID;
1354 	}
1355 
1356 	while (portRef->read_count == 0) {
1357 		// We need to wait for a message to appear
1358 		if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0)
1359 			return B_WOULD_BLOCK;
1360 
1361 		ConditionVariableEntry entry;
1362 		portRef->read_condition.Add(&entry);
1363 
1364 		locker.Unlock();
1365 
1366 		// block if no message, or, if B_TIMEOUT flag set, block with timeout
1367 		status_t status = entry.Wait(flags, timeout);
1368 
1369 		if (status != B_OK) {
1370 			T(Info(portRef, 0, status));
1371 			return status;
1372 		}
1373 
1374 		// re-lock
1375 		BReference<Port> newPortRef = get_locked_port(id);
1376 		if (newPortRef == NULL) {
1377 			T(Info(id, 0, 0, 0, B_BAD_PORT_ID));
1378 			return B_BAD_PORT_ID;
1379 		}
1380 		locker.SetTo(newPortRef->lock, true);
1381 
1382 		if (newPortRef != portRef
1383 			|| (is_port_closed(portRef) && portRef->messages.IsEmpty())) {
1384 			// the port is no longer there
1385 			T(Info(id, 0, 0, 0, B_BAD_PORT_ID));
1386 			return B_BAD_PORT_ID;
1387 		}
1388 	}
1389 
1390 	// determine tail & get the length of the message
1391 	port_message* message = portRef->messages.Head();
1392 	if (message == NULL) {
1393 		panic("port %" B_PRId32 ": no messages found\n", portRef->id);
1394 		return B_ERROR;
1395 	}
1396 
1397 	info->size = message->size;
1398 	info->sender = message->sender;
1399 	info->sender_group = message->sender_group;
1400 	info->sender_team = message->sender_team;
1401 
1402 	T(Info(portRef, message->code, B_OK));
1403 
1404 	// notify next one, as we haven't read from the port
1405 	portRef->read_condition.NotifyOne();
1406 
1407 	return B_OK;
1408 }
1409 
1410 
1411 ssize_t
1412 port_count(port_id id)
1413 {
1414 	if (!sPortsActive || id < 0)
1415 		return B_BAD_PORT_ID;
1416 
1417 	// get the port
1418 	BReference<Port> portRef = get_locked_port(id);
1419 	if (portRef == NULL) {
1420 		TRACE(("port_count: invalid port_id %ld\n", id));
1421 		return B_BAD_PORT_ID;
1422 	}
1423 	MutexLocker locker(portRef->lock, true);
1424 
1425 	// return count of messages
1426 	return portRef->read_count;
1427 }
1428 
1429 
1430 ssize_t
1431 read_port(port_id port, int32* msgCode, void* buffer, size_t bufferSize)
1432 {
1433 	return read_port_etc(port, msgCode, buffer, bufferSize, 0, 0);
1434 }
1435 
1436 
1437 ssize_t
1438 read_port_etc(port_id id, int32* _code, void* buffer, size_t bufferSize,
1439 	uint32 flags, bigtime_t timeout)
1440 {
1441 	if (!sPortsActive || id < 0)
1442 		return B_BAD_PORT_ID;
1443 	if ((buffer == NULL && bufferSize > 0) || timeout < 0)
1444 		return B_BAD_VALUE;
1445 
1446 	bool userCopy = (flags & PORT_FLAG_USE_USER_MEMCPY) != 0;
1447 	bool peekOnly = !userCopy && (flags & B_PEEK_PORT_MESSAGE) != 0;
1448 		// TODO: we could allow peeking for user apps now
1449 
1450 	flags &= B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT | B_RELATIVE_TIMEOUT
1451 		| B_ABSOLUTE_TIMEOUT;
1452 
1453 	// get the port
1454 	BReference<Port> portRef = get_locked_port(id);
1455 	if (portRef == NULL)
1456 		return B_BAD_PORT_ID;
1457 	MutexLocker locker(portRef->lock, true);
1458 
1459 	if (is_port_closed(portRef) && portRef->messages.IsEmpty()) {
1460 		T(Read(portRef, 0, B_BAD_PORT_ID));
1461 		TRACE(("read_port_etc(): closed port %ld\n", id));
1462 		return B_BAD_PORT_ID;
1463 	}
1464 
1465 	while (portRef->read_count == 0) {
1466 		if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0)
1467 			return B_WOULD_BLOCK;
1468 
1469 		// We need to wait for a message to appear
1470 		ConditionVariableEntry entry;
1471 		portRef->read_condition.Add(&entry);
1472 
1473 		locker.Unlock();
1474 
1475 		// block if no message, or, if B_TIMEOUT flag set, block with timeout
1476 		status_t status = entry.Wait(flags, timeout);
1477 
1478 		// re-lock
1479 		BReference<Port> newPortRef = get_locked_port(id);
1480 		if (newPortRef == NULL) {
1481 			T(Read(id, 0, 0, 0, B_BAD_PORT_ID));
1482 			return B_BAD_PORT_ID;
1483 		}
1484 		locker.SetTo(newPortRef->lock, true);
1485 
1486 		if (newPortRef != portRef
1487 			|| (is_port_closed(portRef) && portRef->messages.IsEmpty())) {
1488 			// the port is no longer there
1489 			T(Read(id, 0, 0, 0, B_BAD_PORT_ID));
1490 			return B_BAD_PORT_ID;
1491 		}
1492 
1493 		if (status != B_OK) {
1494 			T(Read(portRef, 0, status));
1495 			return status;
1496 		}
1497 	}
1498 
1499 	// determine tail & get the length of the message
1500 	port_message* message = portRef->messages.Head();
1501 	if (message == NULL) {
1502 		panic("port %" B_PRId32 ": no messages found\n", portRef->id);
1503 		return B_ERROR;
1504 	}
1505 
1506 	if (peekOnly) {
1507 		size_t size = copy_port_message(message, _code, buffer, bufferSize,
1508 			userCopy);
1509 
1510 		T(Read(portRef, message->code, size));
1511 
1512 		portRef->read_condition.NotifyOne();
1513 			// we only peeked, but didn't grab the message
1514 		return size;
1515 	}
1516 
1517 	portRef->messages.RemoveHead();
1518 	portRef->total_count++;
1519 	portRef->write_count++;
1520 	portRef->read_count--;
1521 
1522 	notify_port_select_events(portRef, B_EVENT_WRITE);
1523 	portRef->write_condition.NotifyOne();
1524 		// make one spot in queue available again for write
1525 
1526 	T(Read(portRef, message->code, std::min(bufferSize, message->size)));
1527 
1528 	locker.Unlock();
1529 
1530 	size_t size = copy_port_message(message, _code, buffer, bufferSize,
1531 		userCopy);
1532 
1533 	put_port_message(message);
1534 	return size;
1535 }
1536 
1537 
1538 status_t
1539 write_port(port_id id, int32 msgCode, const void* buffer, size_t bufferSize)
1540 {
1541 	iovec vec = { (void*)buffer, bufferSize };
1542 
1543 	return writev_port_etc(id, msgCode, &vec, 1, bufferSize, 0, 0);
1544 }
1545 
1546 
1547 status_t
1548 write_port_etc(port_id id, int32 msgCode, const void* buffer,
1549 	size_t bufferSize, uint32 flags, bigtime_t timeout)
1550 {
1551 	iovec vec = { (void*)buffer, bufferSize };
1552 
1553 	return writev_port_etc(id, msgCode, &vec, 1, bufferSize, flags, timeout);
1554 }
1555 
1556 
1557 status_t
1558 writev_port_etc(port_id id, int32 msgCode, const iovec* msgVecs,
1559 	size_t vecCount, size_t bufferSize, uint32 flags, bigtime_t timeout)
1560 {
1561 	if (!sPortsActive || id < 0)
1562 		return B_BAD_PORT_ID;
1563 	if (bufferSize > PORT_MAX_MESSAGE_SIZE)
1564 		return B_BAD_VALUE;
1565 
1566 	bool userCopy = (flags & PORT_FLAG_USE_USER_MEMCPY) != 0;
1567 
1568 	// mask irrelevant flags (for acquire_sem() usage)
1569 	flags &= B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT | B_RELATIVE_TIMEOUT
1570 		| B_ABSOLUTE_TIMEOUT;
1571 	if ((flags & B_RELATIVE_TIMEOUT) != 0
1572 		&& timeout != B_INFINITE_TIMEOUT && timeout > 0) {
1573 		// Make the timeout absolute, since we have more than one step where
1574 		// we might have to wait
1575 		flags = (flags & ~B_RELATIVE_TIMEOUT) | B_ABSOLUTE_TIMEOUT;
1576 		timeout += system_time();
1577 	}
1578 
1579 	status_t status;
1580 	port_message* message = NULL;
1581 
1582 	// get the port
1583 	BReference<Port> portRef = get_locked_port(id);
1584 	if (portRef == NULL) {
1585 		TRACE(("write_port_etc: invalid port_id %ld\n", id));
1586 		return B_BAD_PORT_ID;
1587 	}
1588 	MutexLocker locker(portRef->lock, true);
1589 
1590 	if (is_port_closed(portRef)) {
1591 		TRACE(("write_port_etc: port %ld closed\n", id));
1592 		return B_BAD_PORT_ID;
1593 	}
1594 
1595 	if (portRef->write_count <= 0) {
1596 		if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0)
1597 			return B_WOULD_BLOCK;
1598 
1599 		portRef->write_count--;
1600 
1601 		// We need to block in order to wait for a free message slot
1602 		ConditionVariableEntry entry;
1603 		portRef->write_condition.Add(&entry);
1604 
1605 		locker.Unlock();
1606 
1607 		status = entry.Wait(flags, timeout);
1608 
1609 		// re-lock
1610 		BReference<Port> newPortRef = get_locked_port(id);
1611 		if (newPortRef == NULL) {
1612 			T(Write(id, 0, 0, 0, 0, B_BAD_PORT_ID));
1613 			return B_BAD_PORT_ID;
1614 		}
1615 		locker.SetTo(newPortRef->lock, true);
1616 
1617 		if (newPortRef != portRef || is_port_closed(portRef)) {
1618 			// the port is no longer there
1619 			T(Write(id, 0, 0, 0, 0, B_BAD_PORT_ID));
1620 			return B_BAD_PORT_ID;
1621 		}
1622 
1623 		if (status != B_OK)
1624 			goto error;
1625 	} else
1626 		portRef->write_count--;
1627 
1628 	status = get_port_message(msgCode, bufferSize, flags, timeout,
1629 		&message, *portRef);
1630 	if (status != B_OK) {
1631 		if (status == B_BAD_PORT_ID) {
1632 			// the port had to be unlocked and is now no longer there
1633 			T(Write(id, 0, 0, 0, 0, B_BAD_PORT_ID));
1634 			return B_BAD_PORT_ID;
1635 		}
1636 
1637 		goto error;
1638 	}
1639 
1640 	// sender credentials
1641 	message->sender = geteuid();
1642 	message->sender_group = getegid();
1643 	message->sender_team = team_get_current_team_id();
1644 
1645 	if (bufferSize > 0) {
1646 		size_t offset = 0;
1647 		for (uint32 i = 0; i < vecCount; i++) {
1648 			size_t bytes = msgVecs[i].iov_len;
1649 			if (bytes > bufferSize)
1650 				bytes = bufferSize;
1651 
1652 			if (userCopy) {
1653 				status_t status = user_memcpy(message->buffer + offset,
1654 					msgVecs[i].iov_base, bytes);
1655 				if (status != B_OK) {
1656 					put_port_message(message);
1657 					goto error;
1658 				}
1659 			} else
1660 				memcpy(message->buffer + offset, msgVecs[i].iov_base, bytes);
1661 
1662 			bufferSize -= bytes;
1663 			if (bufferSize == 0)
1664 				break;
1665 
1666 			offset += bytes;
1667 		}
1668 	}
1669 
1670 	portRef->messages.Add(message);
1671 	portRef->read_count++;
1672 
1673 	T(Write(id, portRef->read_count, portRef->write_count, message->code,
1674 		message->size, B_OK));
1675 
1676 	notify_port_select_events(portRef, B_EVENT_READ);
1677 	portRef->read_condition.NotifyOne();
1678 	return B_OK;
1679 
1680 error:
1681 	// Give up our slot in the queue again, and let someone else
1682 	// try and fail
1683 	T(Write(id, portRef->read_count, portRef->write_count, 0, 0, status));
1684 	portRef->write_count++;
1685 	notify_port_select_events(portRef, B_EVENT_WRITE);
1686 	portRef->write_condition.NotifyOne();
1687 
1688 	return status;
1689 }
1690 
1691 
1692 status_t
1693 set_port_owner(port_id id, team_id newTeamID)
1694 {
1695 	TRACE(("set_port_owner(id = %ld, team = %ld)\n", id, newTeamID));
1696 
1697 	if (id < 0)
1698 		return B_BAD_PORT_ID;
1699 
1700 	// get the new team
1701 	Team* team = Team::Get(newTeamID);
1702 	if (team == NULL)
1703 		return B_BAD_TEAM_ID;
1704 	BReference<Team> teamReference(team, true);
1705 
1706 	// get the port
1707 	BReference<Port> portRef = get_locked_port(id);
1708 	if (portRef == NULL) {
1709 		TRACE(("set_port_owner: invalid port_id %ld\n", id));
1710 		return B_BAD_PORT_ID;
1711 	}
1712 	MutexLocker locker(portRef->lock, true);
1713 
1714 	// transfer ownership to other team
1715 	if (team->id != portRef->owner) {
1716 		uint8 firstLockIndex  = portRef->owner % kTeamListLockCount;
1717 		uint8 secondLockIndex = team->id % kTeamListLockCount;
1718 
1719 		// Avoid deadlocks: always lock lower index first
1720 		if (secondLockIndex < firstLockIndex) {
1721 			uint8 temp = secondLockIndex;
1722 			secondLockIndex = firstLockIndex;
1723 			firstLockIndex = temp;
1724 		}
1725 
1726 		MutexLocker oldTeamPortsListLocker(sTeamListLock[firstLockIndex]);
1727 		MutexLocker newTeamPortsListLocker;
1728 		if (firstLockIndex != secondLockIndex) {
1729 			newTeamPortsListLocker.SetTo(sTeamListLock[secondLockIndex],
1730 					false);
1731 		}
1732 
1733 		// Now that we have locked the team port lists, check the state again
1734 		if (portRef->state == Port::kActive) {
1735 			list_remove_link(&portRef->team_link);
1736 			list_add_item(&team->port_list, portRef.Get());
1737 			portRef->owner = team->id;
1738 		} else {
1739 			// Port was already deleted. We haven't changed anything yet so
1740 			// we can cancel the operation.
1741 			return B_BAD_PORT_ID;
1742 		}
1743 	}
1744 
1745 	T(OwnerChange(portRef, team->id, B_OK));
1746 	return B_OK;
1747 }
1748 
1749 
1750 //	#pragma mark - syscalls
1751 
1752 
1753 port_id
1754 _user_create_port(int32 queueLength, const char *userName)
1755 {
1756 	char name[B_OS_NAME_LENGTH];
1757 
1758 	if (userName == NULL)
1759 		return create_port(queueLength, NULL);
1760 
1761 	if (!IS_USER_ADDRESS(userName)
1762 		|| user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK)
1763 		return B_BAD_ADDRESS;
1764 
1765 	return create_port(queueLength, name);
1766 }
1767 
1768 
1769 status_t
1770 _user_close_port(port_id id)
1771 {
1772 	return close_port(id);
1773 }
1774 
1775 
1776 status_t
1777 _user_delete_port(port_id id)
1778 {
1779 	return delete_port(id);
1780 }
1781 
1782 
1783 port_id
1784 _user_find_port(const char *userName)
1785 {
1786 	char name[B_OS_NAME_LENGTH];
1787 
1788 	if (userName == NULL)
1789 		return B_BAD_VALUE;
1790 	if (!IS_USER_ADDRESS(userName)
1791 		|| user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK)
1792 		return B_BAD_ADDRESS;
1793 
1794 	return find_port(name);
1795 }
1796 
1797 
1798 status_t
1799 _user_get_port_info(port_id id, struct port_info *userInfo)
1800 {
1801 	struct port_info info;
1802 	status_t status;
1803 
1804 	if (userInfo == NULL)
1805 		return B_BAD_VALUE;
1806 	if (!IS_USER_ADDRESS(userInfo))
1807 		return B_BAD_ADDRESS;
1808 
1809 	status = get_port_info(id, &info);
1810 
1811 	// copy back to user space
1812 	if (status == B_OK
1813 		&& user_memcpy(userInfo, &info, sizeof(struct port_info)) < B_OK)
1814 		return B_BAD_ADDRESS;
1815 
1816 	return status;
1817 }
1818 
1819 
1820 status_t
1821 _user_get_next_port_info(team_id team, int32 *userCookie,
1822 	struct port_info *userInfo)
1823 {
1824 	struct port_info info;
1825 	status_t status;
1826 	int32 cookie;
1827 
1828 	if (userCookie == NULL || userInfo == NULL)
1829 		return B_BAD_VALUE;
1830 	if (!IS_USER_ADDRESS(userCookie) || !IS_USER_ADDRESS(userInfo)
1831 		|| user_memcpy(&cookie, userCookie, sizeof(int32)) < B_OK)
1832 		return B_BAD_ADDRESS;
1833 
1834 	status = get_next_port_info(team, &cookie, &info);
1835 
1836 	// copy back to user space
1837 	if (user_memcpy(userCookie, &cookie, sizeof(int32)) < B_OK
1838 		|| (status == B_OK && user_memcpy(userInfo, &info,
1839 				sizeof(struct port_info)) < B_OK))
1840 		return B_BAD_ADDRESS;
1841 
1842 	return status;
1843 }
1844 
1845 
1846 ssize_t
1847 _user_port_buffer_size_etc(port_id port, uint32 flags, bigtime_t timeout)
1848 {
1849 	syscall_restart_handle_timeout_pre(flags, timeout);
1850 
1851 	status_t status = port_buffer_size_etc(port, flags | B_CAN_INTERRUPT,
1852 		timeout);
1853 
1854 	return syscall_restart_handle_timeout_post(status, timeout);
1855 }
1856 
1857 
1858 ssize_t
1859 _user_port_count(port_id port)
1860 {
1861 	return port_count(port);
1862 }
1863 
1864 
1865 status_t
1866 _user_set_port_owner(port_id port, team_id team)
1867 {
1868 	return set_port_owner(port, team);
1869 }
1870 
1871 
1872 ssize_t
1873 _user_read_port_etc(port_id port, int32 *userCode, void *userBuffer,
1874 	size_t bufferSize, uint32 flags, bigtime_t timeout)
1875 {
1876 	int32 messageCode;
1877 	ssize_t	bytesRead;
1878 
1879 	syscall_restart_handle_timeout_pre(flags, timeout);
1880 
1881 	if (userBuffer == NULL && bufferSize != 0)
1882 		return B_BAD_VALUE;
1883 	if ((userCode != NULL && !IS_USER_ADDRESS(userCode))
1884 		|| (userBuffer != NULL && !IS_USER_ADDRESS(userBuffer)))
1885 		return B_BAD_ADDRESS;
1886 
1887 	bytesRead = read_port_etc(port, &messageCode, userBuffer, bufferSize,
1888 		flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, timeout);
1889 
1890 	if (bytesRead >= 0 && userCode != NULL
1891 		&& user_memcpy(userCode, &messageCode, sizeof(int32)) < B_OK)
1892 		return B_BAD_ADDRESS;
1893 
1894 	return syscall_restart_handle_timeout_post(bytesRead, timeout);
1895 }
1896 
1897 
1898 status_t
1899 _user_write_port_etc(port_id port, int32 messageCode, const void *userBuffer,
1900 	size_t bufferSize, uint32 flags, bigtime_t timeout)
1901 {
1902 	iovec vec = { (void *)userBuffer, bufferSize };
1903 
1904 	syscall_restart_handle_timeout_pre(flags, timeout);
1905 
1906 	if (userBuffer == NULL && bufferSize != 0)
1907 		return B_BAD_VALUE;
1908 	if (userBuffer != NULL && !IS_USER_ADDRESS(userBuffer))
1909 		return B_BAD_ADDRESS;
1910 
1911 	status_t status = writev_port_etc(port, messageCode, &vec, 1, bufferSize,
1912 		flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, timeout);
1913 
1914 	return syscall_restart_handle_timeout_post(status, timeout);
1915 }
1916 
1917 
1918 status_t
1919 _user_writev_port_etc(port_id port, int32 messageCode, const iovec *userVecs,
1920 	size_t vecCount, size_t bufferSize, uint32 flags, bigtime_t timeout)
1921 {
1922 	syscall_restart_handle_timeout_pre(flags, timeout);
1923 
1924 	if (userVecs == NULL && bufferSize != 0)
1925 		return B_BAD_VALUE;
1926 	if (userVecs != NULL && !IS_USER_ADDRESS(userVecs))
1927 		return B_BAD_ADDRESS;
1928 
1929 	iovec *vecs = NULL;
1930 	if (userVecs && vecCount != 0) {
1931 		vecs = (iovec*)malloc(sizeof(iovec) * vecCount);
1932 		if (vecs == NULL)
1933 			return B_NO_MEMORY;
1934 
1935 		if (user_memcpy(vecs, userVecs, sizeof(iovec) * vecCount) < B_OK) {
1936 			free(vecs);
1937 			return B_BAD_ADDRESS;
1938 		}
1939 	}
1940 
1941 	status_t status = writev_port_etc(port, messageCode, vecs, vecCount,
1942 		bufferSize, flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT,
1943 		timeout);
1944 
1945 	free(vecs);
1946 	return syscall_restart_handle_timeout_post(status, timeout);
1947 }
1948 
1949 
1950 status_t
1951 _user_get_port_message_info_etc(port_id port, port_message_info *userInfo,
1952 	size_t infoSize, uint32 flags, bigtime_t timeout)
1953 {
1954 	if (userInfo == NULL || infoSize != sizeof(port_message_info))
1955 		return B_BAD_VALUE;
1956 
1957 	syscall_restart_handle_timeout_pre(flags, timeout);
1958 
1959 	port_message_info info;
1960 	status_t error = _get_port_message_info_etc(port, &info, sizeof(info),
1961 		flags | B_CAN_INTERRUPT, timeout);
1962 
1963 	// copy info to userland
1964 	if (error == B_OK && (!IS_USER_ADDRESS(userInfo)
1965 			|| user_memcpy(userInfo, &info, sizeof(info)) != B_OK)) {
1966 		error = B_BAD_ADDRESS;
1967 	}
1968 
1969 	return syscall_restart_handle_timeout_post(error, timeout);
1970 }
1971