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