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
89namespace {
90
91struct 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
100typedef DoublyLinkedList<port_message> MessageList;
101
102} // namespace
103
104
105static void put_port_message(port_message* message);
106
107
108namespace {
109
110struct 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
164struct 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
189typedef BOpenHashTable<PortHashDefinition> PortHashTable;
190
191
192struct 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
229typedef BOpenHashTable<PortNameHashDefinition> PortNameHashTable;
230
231
232class PortNotificationService : public DefaultNotificationService {
233public:
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
246namespace PortTracing {
247
248class Create : public AbstractTraceEntry {
249public:
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
268private:
269	port_id				fID;
270	char*				fName;
271	team_id				fOwner;
272	int32		 		fCapacity;
273};
274
275
276class Delete : public AbstractTraceEntry {
277public:
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
290private:
291	port_id				fID;
292};
293
294
295class Read : public AbstractTraceEntry {
296public:
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
326private:
327	port_id				fID;
328	int32				fReadCount;
329	int32				fWriteCount;
330	int32				fCode;
331	ssize_t				fResult;
332};
333
334
335class Write : public AbstractTraceEntry {
336public:
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
356private:
357	port_id				fID;
358	int32				fReadCount;
359	int32				fWriteCount;
360	int32				fCode;
361	size_t				fBufferSize;
362	ssize_t				fResult;
363};
364
365
366class Info : public AbstractTraceEntry {
367public:
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
397private:
398	port_id				fID;
399	int32				fReadCount;
400	int32				fWriteCount;
401	int32				fCode;
402	ssize_t				fResult;
403};
404
405
406class OwnerChange : public AbstractTraceEntry {
407public:
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
424private:
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
439static const size_t kInitialPortBufferSize = 4 * 1024 * 1024;
440static const size_t kTotalSpaceLimit = 64 * 1024 * 1024;
441static const size_t kTeamSpaceLimit = 8 * 1024 * 1024;
442static const size_t kBufferGrowRate = kInitialPortBufferSize;
443
444#define MAX_QUEUE_LENGTH 4096
445#define PORT_MAX_MESSAGE_SIZE (256 * 1024)
446
447static int32 sMaxPorts = 4096;
448static int32 sUsedPorts;
449
450static PortHashTable sPorts;
451static PortNameHashTable sPortsByName;
452static ConditionVariable sNoSpaceCondition;
453static int32 sTotalSpaceCommited;
454static int32 sWaitingForSpace;
455static port_id sNextPortID = 1;
456static bool sPortsActive = false;
457static rw_lock sPortsLock = RW_LOCK_INITIALIZER("ports list");
458
459enum {
460	kTeamListLockCount = 8
461};
462
463static 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
474static PortNotificationService sNotificationService;
475
476
477//	#pragma mark - TeamNotificationService
478
479
480PortNotificationService::PortNotificationService()
481	:
482	DefaultNotificationService("ports")
483{
484}
485
486
487void
488PortNotificationService::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
503static int
504dump_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
536static 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
563static int
564dump_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*/
618static void
619notify_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
626static BReference<Port>
627get_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
646static BReference<Port>
647get_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 */
660static inline bool
661is_port_closed(Port* port)
662{
663	return port->capacity == 0;
664}
665
666
667static void
668put_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. */
680static status_t
681get_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*/
755static void
756fill_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
769static ssize_t
770copy_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
793static void
794uninit_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 */
813static status_t
814delete_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*/
850void
851delete_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
905int32
906port_max_ports(void)
907{
908	return sMaxPorts;
909}
910
911
912int32
913port_used_ports(void)
914{
915	return sUsedPorts;
916}
917
918
919size_t
920port_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
929status_t
930port_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
974port_id
975create_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
1063status_t
1064close_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
1093status_t
1094delete_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
1144status_t
1145select_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
1190status_t
1191deselect_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
1216port_id
1217find_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
1240status_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
1264status_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
1316ssize_t
1317port_buffer_size(port_id id)
1318{
1319	return port_buffer_size_etc(id, 0, 0);
1320}
1321
1322
1323ssize_t
1324port_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
1332status_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
1411ssize_t
1412port_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
1430ssize_t
1431read_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
1437ssize_t
1438read_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
1538status_t
1539write_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
1547status_t
1548write_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
1557status_t
1558writev_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
1680error:
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
1692status_t
1693set_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
1753port_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
1769status_t
1770_user_close_port(port_id id)
1771{
1772	return close_port(id);
1773}
1774
1775
1776status_t
1777_user_delete_port(port_id id)
1778{
1779	return delete_port(id);
1780}
1781
1782
1783port_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
1798status_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
1820status_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
1846ssize_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
1858ssize_t
1859_user_port_count(port_id port)
1860{
1861	return port_count(port);
1862}
1863
1864
1865status_t
1866_user_set_port_owner(port_id port, team_id team)
1867{
1868	return set_port_owner(port, team);
1869}
1870
1871
1872ssize_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
1898status_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
1918status_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
1950status_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