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