xref: /haiku/src/system/kernel/port.cpp (revision cfc3fa87da824bdf593eb8b817a83b6376e77935)
1 /*
2  * Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  *
5  * Copyright 2001, Mark-Jan Bastian. All rights reserved.
6  * Distributed under the terms of the NewOS License.
7  */
8 
9 /*!	Ports for IPC */
10 
11 #include <port.h>
12 
13 #include <ctype.h>
14 #include <iovec.h>
15 #include <stdlib.h>
16 #include <string.h>
17 
18 #include <OS.h>
19 
20 #include <arch/int.h>
21 #include <cbuf.h>
22 #include <kernel.h>
23 #include <sem.h>
24 #include <syscall_restart.h>
25 #include <team.h>
26 #include <util/list.h>
27 #include <wait_for_objects.h>
28 
29 
30 //#define TRACE_PORTS
31 #ifdef TRACE_PORTS
32 #	define TRACE(x) dprintf x
33 #else
34 #	define TRACE(x)
35 #endif
36 
37 
38 typedef struct port_msg {
39 	list_link	link;
40 	int32		code;
41 	cbuf		*buffer_chain;
42 	size_t		size;
43 } port_msg;
44 
45 struct port_entry {
46 	port_id 	id;
47 	team_id 	owner;
48 	int32 		capacity;
49 	spinlock	lock;
50 	const char	*name;
51 	sem_id		read_sem;
52 	sem_id		write_sem;
53 	int32		total_count;	// messages read from port since creation
54 	select_info	*select_infos;
55 	struct list	msg_queue;
56 };
57 
58 
59 #define MAX_QUEUE_LENGTH 4096
60 #define PORT_MAX_MESSAGE_SIZE 65536
61 
62 // sMaxPorts must be power of 2
63 static int32 sMaxPorts = 4096;
64 static int32 sUsedPorts = 0;
65 
66 static struct port_entry *sPorts = NULL;
67 static area_id sPortArea = 0;
68 static bool sPortsActive = false;
69 static port_id sNextPort = 1;
70 static int32 sFirstFreeSlot = 1;
71 
72 static spinlock sPortSpinlock = 0;
73 
74 #define GRAB_PORT_LIST_LOCK() acquire_spinlock(&sPortSpinlock)
75 #define RELEASE_PORT_LIST_LOCK() release_spinlock(&sPortSpinlock)
76 #define GRAB_PORT_LOCK(s) acquire_spinlock(&(s).lock)
77 #define RELEASE_PORT_LOCK(s) release_spinlock(&(s).lock)
78 
79 
80 static int
81 dump_port_list(int argc, char **argv)
82 {
83 	const char *name = NULL;
84 	team_id owner = -1;
85 	int32 i;
86 
87 	if (argc > 2) {
88 		if (!strcmp(argv[1], "team") || !strcmp(argv[1], "owner"))
89 			owner = strtoul(argv[2], NULL, 0);
90 		else if (!strcmp(argv[1], "name"))
91 			name = argv[2];
92 	} else if (argc > 1)
93 		owner = strtoul(argv[1], NULL, 0);
94 
95 	kprintf("port           id  cap  r-sem  w-sem   team  name\n");
96 
97 	for (i = 0; i < sMaxPorts; i++) {
98 		struct port_entry *port = &sPorts[i];
99 		if (port->id < 0
100 			|| (owner != -1 && port->owner != owner)
101 			|| (name != NULL && strstr(port->name, name) == NULL))
102 			continue;
103 
104 		kprintf("%p %6ld %4ld %6ld %6ld %6ld  %s\n", port, port->id,
105 			port->capacity, port->read_sem, port->write_sem, port->owner,
106 			port->name);
107 	}
108 	return 0;
109 }
110 
111 
112 static void
113 _dump_port_info(struct port_entry *port)
114 {
115 	int32 count;
116 
117 	kprintf("PORT: %p\n", port);
118 	kprintf(" id:              %ld\n", port->id);
119 	kprintf(" name:            \"%s\"\n", port->name);
120 	kprintf(" owner:           %ld\n", port->owner);
121 	kprintf(" capacity:        %ld\n", port->capacity);
122 	kprintf(" read_sem:        %ld\n", port->read_sem);
123 	kprintf(" write_sem:       %ld\n", port->write_sem);
124  	get_sem_count(port->read_sem, &count);
125  	kprintf(" read_sem count:  %ld\n", count);
126  	get_sem_count(port->write_sem, &count);
127 	kprintf(" write_sem count: %ld\n", count);
128 	kprintf(" total count:     %ld\n", port->total_count);
129 
130 	set_debug_variable("_port", (addr_t)port);
131 	set_debug_variable("_portID", port->id);
132 	set_debug_variable("_owner", port->owner);
133 	set_debug_variable("_readSem", port->read_sem);
134 	set_debug_variable("_writeSem", port->write_sem);
135 }
136 
137 
138 static int
139 dump_port_info(int argc, char **argv)
140 {
141 	const char *name = NULL;
142 	sem_id sem = -1;
143 	int i;
144 
145 	if (argc < 2) {
146 		print_debugger_command_usage(argv[0]);
147 		return 0;
148 	}
149 
150 	if (argc > 2) {
151 		if (!strcmp(argv[1], "address")) {
152 			_dump_port_info((struct port_entry *)strtoul(argv[2], NULL, 0));
153 			return 0;
154 		} else if (!strcmp(argv[1], "sem"))
155 			sem = strtoul(argv[2], NULL, 0);
156 		else if (!strcmp(argv[1], "name"))
157 			name = argv[2];
158 	} else if (isdigit(argv[1][0])) {
159 		// if the argument looks like a number, treat it as such
160 		uint32 num = strtoul(argv[1], NULL, 0);
161 		uint32 slot = num % sMaxPorts;
162 		if (sPorts[slot].id != (int)num) {
163 			kprintf("port %ld (%#lx) doesn't exist!\n", num, num);
164 			return 0;
165 		}
166 		_dump_port_info(&sPorts[slot]);
167 		return 0;
168 	} else
169 		name = argv[1];
170 
171 	// walk through the ports list, trying to match name
172 	for (i = 0; i < sMaxPorts; i++) {
173 		if ((name != NULL && sPorts[i].name != NULL
174 				&& !strcmp(name, sPorts[i].name))
175 			|| (sem != -1 && (sPorts[i].read_sem == sem
176 				|| sPorts[i].write_sem == sem))) {
177 			_dump_port_info(&sPorts[i]);
178 			return 0;
179 		}
180 	}
181 
182 	return 0;
183 }
184 
185 
186 static void
187 notify_port_select_events(int slot, uint16 events)
188 {
189 	if (sPorts[slot].select_infos)
190 		notify_select_events_list(sPorts[slot].select_infos, events);
191 }
192 
193 
194 static void
195 put_port_msg(port_msg *msg)
196 {
197 	cbuf_free_chain(msg->buffer_chain);
198 	free(msg);
199 }
200 
201 
202 static port_msg *
203 get_port_msg(int32 code, size_t bufferSize)
204 {
205 	// ToDo: investigate preallocation of port_msgs (or use a slab allocator)
206 	cbuf *bufferChain = NULL;
207 
208 	port_msg *msg = (port_msg *)malloc(sizeof(port_msg));
209 	if (msg == NULL)
210 		return NULL;
211 
212 	if (bufferSize > 0) {
213 		bufferChain = cbuf_get_chain(bufferSize);
214 		if (bufferChain == NULL) {
215 			free(msg);
216 			return NULL;
217 		}
218 	}
219 
220 	msg->code = code;
221 	msg->buffer_chain = bufferChain;
222 	msg->size = bufferSize;
223 	return msg;
224 }
225 
226 
227 /*!	You need to own the port's lock when calling this function */
228 static bool
229 is_port_closed(int32 slot)
230 {
231 	return sPorts[slot].capacity == 0;
232 }
233 
234 
235 /*!	Fills the port_info structure with information from the specified
236 	port.
237 	The port lock must be held when called.
238 */
239 static void
240 fill_port_info(struct port_entry *port, port_info *info, size_t size)
241 {
242 	int32 count;
243 
244 	info->port = port->id;
245 	info->team = port->owner;
246 	info->capacity = port->capacity;
247 
248 	get_sem_count(port->read_sem, &count);
249 	if (count < 0)
250 		count = 0;
251 
252 	info->queue_count = count;
253 	info->total_count = port->total_count;
254 
255 	strlcpy(info->name, port->name, B_OS_NAME_LENGTH);
256 }
257 
258 
259 //	#pragma mark - private kernel API
260 
261 
262 /*! This function cycles through the ports table, deleting all
263 	the ports that are owned by the passed team_id
264 */
265 int
266 delete_owned_ports(team_id owner)
267 {
268 	// ToDo: investigate maintaining a list of ports in the team
269 	//	to make this simpler and more efficient.
270 	cpu_status state;
271 	int i;
272 	int count = 0;
273 
274 	TRACE(("delete_owned_ports(owner = %ld)\n", owner));
275 
276 	if (!sPortsActive)
277 		return B_BAD_PORT_ID;
278 
279 	state = disable_interrupts();
280 	GRAB_PORT_LIST_LOCK();
281 
282 	for (i = 0; i < sMaxPorts; i++) {
283 		if (sPorts[i].id != -1 && sPorts[i].owner == owner) {
284 			port_id id = sPorts[i].id;
285 
286 			RELEASE_PORT_LIST_LOCK();
287 			restore_interrupts(state);
288 
289 			delete_port(id);
290 			count++;
291 
292 			state = disable_interrupts();
293 			GRAB_PORT_LIST_LOCK();
294 		}
295 	}
296 
297 	RELEASE_PORT_LIST_LOCK();
298 	restore_interrupts(state);
299 
300 	return count;
301 }
302 
303 
304 int32
305 port_max_ports(void)
306 {
307 	return sMaxPorts;
308 }
309 
310 
311 int32
312 port_used_ports(void)
313 {
314 	return sUsedPorts;
315 }
316 
317 
318 status_t
319 port_init(kernel_args *args)
320 {
321 	size_t size = sizeof(struct port_entry) * sMaxPorts;
322 	int32 i;
323 
324 	// create and initialize ports table
325 	sPortArea = create_area("port_table", (void **)&sPorts, B_ANY_KERNEL_ADDRESS,
326 		size, B_FULL_LOCK, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
327 	if (sPortArea < 0) {
328 		panic("unable to allocate kernel port table!\n");
329 		return sPortArea;
330 	}
331 
332 	// ToDo: investigate preallocating a list of port_msgs to
333 	//	speed up actual message sending/receiving, a slab allocator
334 	//	might do it as well, though :-)
335 
336 	memset(sPorts, 0, size);
337 	for (i = 0; i < sMaxPorts; i++)
338 		sPorts[i].id = -1;
339 
340 	// add debugger commands
341 	add_debugger_command_etc("ports", &dump_port_list,
342 		"Dump a list of all active ports (for team, with name, etc.)",
343 		"[ ([ \"team\" | \"owner\" ] <team>) | (\"name\" <name>) ]\n"
344 		"Prints a list of all active ports meeting the given\n"
345 		"requirement. If no argument is given, all ports are listed.\n"
346 		"  <team>             - The team owning the ports.\n"
347 		"  <name>             - Part of the name of the ports.\n", 0);
348 	add_debugger_command_etc("port", &dump_port_info,
349 		"Dump info about a particular port",
350 		"([ \"address\" ] <address>) | ([ \"name\" ] <name>) "
351 			"| (\"sem\" <sem>)\n"
352 		"Prints info about the specified port.\n"
353 		"  <address>  - Pointer to the port structure.\n"
354 		"  <name>     - Name of the port.\n"
355 		"  <sem>      - ID of the port's read or write semaphore.\n", 0);
356 
357 	sPortsActive = true;
358 	return B_OK;
359 }
360 
361 
362 //	#pragma mark - public kernel API
363 
364 
365 port_id
366 create_port(int32 queueLength, const char *name)
367 {
368 	cpu_status state;
369 	char nameBuffer[B_OS_NAME_LENGTH];
370 	sem_id readSem, writeSem;
371 	status_t status;
372 	team_id	owner;
373 	int32 slot;
374 
375 	TRACE(("create_port(queueLength = %ld, name = \"%s\")\n", queueLength, name));
376 
377 	if (!sPortsActive)
378 		return B_BAD_PORT_ID;
379 
380 	// check queue length
381 	if (queueLength < 1
382 		|| queueLength > MAX_QUEUE_LENGTH)
383 		return B_BAD_VALUE;
384 
385 	// check early on if there are any free port slots to use
386 	if (atomic_add(&sUsedPorts, 1) >= sMaxPorts) {
387 		status = B_NO_MORE_PORTS;
388 		goto err1;
389 	}
390 
391 	// check & dup name
392 	if (name == NULL)
393 		name = "unnamed port";
394 
395 	// ToDo: we could save the memory and use the semaphore name only instead
396 	strlcpy(nameBuffer, name, B_OS_NAME_LENGTH);
397 	name = strdup(nameBuffer);
398 	if (name == NULL) {
399 		status = B_NO_MEMORY;
400 		goto err1;
401 	}
402 
403 	// create read sem with owner set to -1
404 	// ToDo: should be B_SYSTEM_TEAM
405 	readSem = create_sem_etc(0, name, -1);
406 	if (readSem < B_OK) {
407 		status = readSem;
408 		goto err2;
409 	}
410 
411 	// create write sem
412 	writeSem = create_sem_etc(queueLength, name, -1);
413 	if (writeSem < B_OK) {
414 		status = writeSem;
415 		goto err3;
416 	}
417 
418 	owner = team_get_current_team_id();
419 
420 	state = disable_interrupts();
421 	GRAB_PORT_LIST_LOCK();
422 
423 	// find the first empty spot
424 	for (slot = 0; slot < sMaxPorts; slot++) {
425 		int32 i = (slot + sFirstFreeSlot) % sMaxPorts;
426 
427 		if (sPorts[i].id == -1) {
428 			port_id id;
429 
430 			// make the port_id be a multiple of the slot it's in
431 			if (i >= sNextPort % sMaxPorts)
432 				sNextPort += i - sNextPort % sMaxPorts;
433 			else
434 				sNextPort += sMaxPorts - (sNextPort % sMaxPorts - i);
435 			sFirstFreeSlot = slot + 1;
436 
437 			GRAB_PORT_LOCK(sPorts[i]);
438 			sPorts[i].id = sNextPort++;
439 			RELEASE_PORT_LIST_LOCK();
440 
441 			sPorts[i].capacity = queueLength;
442 			sPorts[i].owner = owner;
443 			sPorts[i].name = name;
444 
445 			sPorts[i].read_sem	= readSem;
446 			sPorts[i].write_sem	= writeSem;
447 
448 			list_init(&sPorts[i].msg_queue);
449 			sPorts[i].total_count = 0;
450 			sPorts[i].select_infos = NULL;
451 			id = sPorts[i].id;
452 
453 			RELEASE_PORT_LOCK(sPorts[i]);
454 			restore_interrupts(state);
455 
456 			TRACE(("create_port() done: port created %ld\n", id));
457 
458 			return id;
459 		}
460 	}
461 
462 	// not enough ports...
463 
464 	// ToDo: due to sUsedPorts, this cannot happen anymore - as
465 	//		long as sMaxPorts stays constant over the kernel run
466 	//		time (which it should be). IOW we could simply panic()
467 	//		here.
468 
469 	RELEASE_PORT_LIST_LOCK();
470 	restore_interrupts(state);
471 
472 	status = B_NO_MORE_PORTS;
473 
474 	delete_sem(writeSem);
475 err3:
476 	delete_sem(readSem);
477 err2:
478 	free((char *)name);
479 err1:
480 	atomic_add(&sUsedPorts, -1);
481 
482 	return status;
483 }
484 
485 
486 status_t
487 close_port(port_id id)
488 {
489 	sem_id readSem, writeSem;
490 	cpu_status state;
491 	int32 slot;
492 
493 	TRACE(("close_port(id = %ld)\n", id));
494 
495 	if (!sPortsActive || id < 0)
496 		return B_BAD_PORT_ID;
497 
498 	slot = id % sMaxPorts;
499 
500 	// walk through the sem list, trying to match name
501 	state = disable_interrupts();
502 	GRAB_PORT_LOCK(sPorts[slot]);
503 
504 	if (sPorts[slot].id != id) {
505 		RELEASE_PORT_LOCK(sPorts[slot]);
506 		restore_interrupts(state);
507 		TRACE(("close_port: invalid port_id %ld\n", id));
508 		return B_BAD_PORT_ID;
509 	}
510 
511 	// mark port to disable writing - deleting the semaphores will
512 	// wake up waiting read/writes
513 	sPorts[slot].capacity = 0;
514 	readSem = sPorts[slot].read_sem;
515 	writeSem = sPorts[slot].write_sem;
516 
517 	notify_port_select_events(slot, B_EVENT_INVALID);
518 	sPorts[slot].select_infos = NULL;
519 
520 	RELEASE_PORT_LOCK(sPorts[slot]);
521 	restore_interrupts(state);
522 
523 	delete_sem(readSem);
524 	delete_sem(writeSem);
525 
526 	return B_NO_ERROR;
527 }
528 
529 
530 status_t
531 delete_port(port_id id)
532 {
533 	cpu_status state;
534 	sem_id readSem, writeSem;
535 	const char *name;
536 	struct list list;
537 	port_msg *msg;
538 	int32 slot;
539 
540 	TRACE(("delete_port(id = %ld)\n", id));
541 
542 	if (!sPortsActive || id < 0)
543 		return B_BAD_PORT_ID;
544 
545 	slot = id % sMaxPorts;
546 
547 	state = disable_interrupts();
548 	GRAB_PORT_LOCK(sPorts[slot]);
549 
550 	if (sPorts[slot].id != id) {
551 		RELEASE_PORT_LOCK(sPorts[slot]);
552 		restore_interrupts(state);
553 
554 		TRACE(("delete_port: invalid port_id %ld\n", id));
555 		return B_BAD_PORT_ID;
556 	}
557 
558 	/* mark port as invalid */
559 	sPorts[slot].id	= -1;
560 	name = sPorts[slot].name;
561 	readSem = sPorts[slot].read_sem;
562 	writeSem = sPorts[slot].write_sem;
563 	sPorts[slot].name = NULL;
564 	list_move_to_list(&sPorts[slot].msg_queue, &list);
565 
566 	notify_port_select_events(slot, B_EVENT_INVALID);
567 	sPorts[slot].select_infos = NULL;
568 
569 	RELEASE_PORT_LOCK(sPorts[slot]);
570 
571 	// update the first free slot hint in the array
572 	GRAB_PORT_LIST_LOCK();
573 	if (slot < sFirstFreeSlot)
574 		sFirstFreeSlot = slot;
575 	RELEASE_PORT_LIST_LOCK();
576 
577 	restore_interrupts(state);
578 
579 	atomic_add(&sUsedPorts, -1);
580 
581 	// free the queue
582 	while ((msg = (port_msg *)list_remove_head_item(&list)) != NULL) {
583 		put_port_msg(msg);
584 	}
585 
586 	free((char *)name);
587 
588 	// release the threads that were blocking on this port by deleting the sem
589 	// read_port() will see the B_BAD_SEM_ID acq_sem() return value, and act accordingly
590 	delete_sem(readSem);
591 	delete_sem(writeSem);
592 
593 	return B_OK;
594 }
595 
596 
597 status_t
598 select_port(int32 id, struct select_info *info, bool kernel)
599 {
600 	cpu_status state;
601 	int32 slot;
602 	status_t error = B_OK;
603 
604 	if (id < 0)
605 		return B_BAD_PORT_ID;
606 
607 	slot = id % sMaxPorts;
608 
609 	state = disable_interrupts();
610 	GRAB_PORT_LOCK(sPorts[slot]);
611 
612 	if (sPorts[slot].id != id || is_port_closed(slot)) {
613 		// bad port ID
614 		error = B_BAD_SEM_ID;
615 	} else if (!kernel && sPorts[slot].owner == team_get_kernel_team_id()) {
616 		// kernel port, but call from userland
617 		error = B_NOT_ALLOWED;
618 	} else {
619 		info->selected_events &= B_EVENT_READ | B_EVENT_WRITE | B_EVENT_INVALID;
620 
621 		if (info->selected_events != 0) {
622 			uint16 events = 0;
623 			int32 writeCount = 0;
624 
625 			info->next = sPorts[slot].select_infos;
626 			sPorts[slot].select_infos = info;
627 
628 			// check for events
629 			if ((info->selected_events & B_EVENT_READ) != 0
630 				&& !list_is_empty(&sPorts[slot].msg_queue)) {
631 				events |= B_EVENT_READ;
632 			}
633 
634 			if (get_sem_count(sPorts[slot].write_sem, &writeCount) == B_OK
635 				&& writeCount > 0) {
636 				events |= B_EVENT_WRITE;
637 			}
638 
639 			if (events != 0)
640 				notify_select_events(info, events);
641 		}
642 	}
643 
644 	RELEASE_PORT_LOCK(sPorts[slot]);
645 	restore_interrupts(state);
646 
647 	return error;
648 }
649 
650 
651 status_t
652 deselect_port(int32 id, struct select_info *info, bool kernel)
653 {
654 	cpu_status state;
655 	int32 slot;
656 
657 	if (id < 0)
658 		return B_BAD_PORT_ID;
659 
660 	if (info->selected_events == 0)
661 		return B_OK;
662 
663 	slot = id % sMaxPorts;
664 
665 	state = disable_interrupts();
666 	GRAB_PORT_LOCK(sPorts[slot]);
667 
668 	if (sPorts[slot].id == id) {
669 		select_info** infoLocation = &sPorts[slot].select_infos;
670 		while (*infoLocation != NULL && *infoLocation != info)
671 			infoLocation = &(*infoLocation)->next;
672 
673 		if (*infoLocation == info)
674 			*infoLocation = info->next;
675 	}
676 
677 	RELEASE_PORT_LOCK(sPorts[slot]);
678 	restore_interrupts(state);
679 
680 	return B_OK;
681 }
682 
683 
684 port_id
685 find_port(const char *name)
686 {
687 	port_id portFound = B_NAME_NOT_FOUND;
688 	cpu_status state;
689 	int32 i;
690 
691 	TRACE(("find_port(name = \"%s\")\n", name));
692 
693 	if (!sPortsActive)
694 		return B_NAME_NOT_FOUND;
695 	if (name == NULL)
696 		return B_BAD_VALUE;
697 
698 	// Since we have to check every single port, and we don't
699 	// care if it goes away at any point, we're only grabbing
700 	// the port lock in question, not the port list lock
701 
702 	// loop over list
703 	for (i = 0; i < sMaxPorts && portFound < B_OK; i++) {
704 		// lock every individual port before comparing
705 		state = disable_interrupts();
706 		GRAB_PORT_LOCK(sPorts[i]);
707 
708 		if (sPorts[i].id >= 0 && !strcmp(name, sPorts[i].name))
709 			portFound = sPorts[i].id;
710 
711 		RELEASE_PORT_LOCK(sPorts[i]);
712 		restore_interrupts(state);
713 	}
714 
715 	return portFound;
716 }
717 
718 
719 status_t
720 _get_port_info(port_id id, port_info *info, size_t size)
721 {
722 	cpu_status state;
723 	int slot;
724 
725 	TRACE(("get_port_info(id = %ld)\n", id));
726 
727 	if (info == NULL || size != sizeof(port_info))
728 		return B_BAD_VALUE;
729 	if (!sPortsActive || id < 0)
730 		return B_BAD_PORT_ID;
731 
732 	slot = id % sMaxPorts;
733 
734 	state = disable_interrupts();
735 	GRAB_PORT_LOCK(sPorts[slot]);
736 
737 	if (sPorts[slot].id != id || sPorts[slot].capacity == 0) {
738 		RELEASE_PORT_LOCK(sPorts[slot]);
739 		restore_interrupts(state);
740 		TRACE(("get_port_info: invalid port_id %ld\n", id));
741 		return B_BAD_PORT_ID;
742 	}
743 
744 	// fill a port_info struct with info
745 	fill_port_info(&sPorts[slot], info, size);
746 
747 	RELEASE_PORT_LOCK(sPorts[slot]);
748 	restore_interrupts(state);
749 
750 	return B_OK;
751 }
752 
753 
754 status_t
755 _get_next_port_info(team_id team, int32 *_cookie, struct port_info *info, size_t size)
756 {
757 	cpu_status state;
758 	int slot;
759 
760 	TRACE(("get_next_port_info(team = %ld)\n", team));
761 
762 	if (info == NULL || size != sizeof(port_info) || _cookie == NULL || team < B_OK)
763 		return B_BAD_VALUE;
764 	if (!sPortsActive)
765 		return B_BAD_PORT_ID;
766 
767 	slot = *_cookie;
768 	if (slot >= sMaxPorts)
769 		return B_BAD_PORT_ID;
770 
771 	if (team == B_CURRENT_TEAM)
772 		team = team_get_current_team_id();
773 
774 	info->port = -1; // used as found flag
775 
776 	// spinlock
777 	state = disable_interrupts();
778 	GRAB_PORT_LIST_LOCK();
779 
780 	while (slot < sMaxPorts) {
781 		GRAB_PORT_LOCK(sPorts[slot]);
782 		if (sPorts[slot].id != -1 && sPorts[slot].capacity != 0 && sPorts[slot].owner == team) {
783 			// found one!
784 			fill_port_info(&sPorts[slot], info, size);
785 
786 			RELEASE_PORT_LOCK(sPorts[slot]);
787 			slot++;
788 			break;
789 		}
790 		RELEASE_PORT_LOCK(sPorts[slot]);
791 		slot++;
792 	}
793 	RELEASE_PORT_LIST_LOCK();
794 	restore_interrupts(state);
795 
796 	if (info->port == -1)
797 		return B_BAD_PORT_ID;
798 
799 	*_cookie = slot;
800 	return B_NO_ERROR;
801 }
802 
803 
804 ssize_t
805 port_buffer_size(port_id id)
806 {
807 	return port_buffer_size_etc(id, 0, 0);
808 }
809 
810 
811 ssize_t
812 port_buffer_size_etc(port_id id, uint32 flags, bigtime_t timeout)
813 {
814 	cpu_status state;
815 	sem_id cachedSem;
816 	status_t status;
817 	port_msg *msg;
818 	ssize_t size;
819 	int32 slot;
820 
821 	if (!sPortsActive || id < 0)
822 		return B_BAD_PORT_ID;
823 
824 	slot = id % sMaxPorts;
825 
826 	state = disable_interrupts();
827 	GRAB_PORT_LOCK(sPorts[slot]);
828 
829 	if (sPorts[slot].id != id
830 		|| (is_port_closed(slot) && list_is_empty(&sPorts[slot].msg_queue))) {
831 		RELEASE_PORT_LOCK(sPorts[slot]);
832 		restore_interrupts(state);
833 		TRACE(("port_buffer_size_etc(): %s port %ld\n",
834 			sPorts[slot].id == id ? "closed" : "invalid", id));
835 		return B_BAD_PORT_ID;
836 	}
837 
838 	cachedSem = sPorts[slot].read_sem;
839 
840 	RELEASE_PORT_LOCK(sPorts[slot]);
841 	restore_interrupts(state);
842 
843 	// block if no message, or, if B_TIMEOUT flag set, block with timeout
844 
845 	status = acquire_sem_etc(cachedSem, 1, flags, timeout);
846 	if (status != B_OK && status != B_BAD_SEM_ID)
847 		return status;
848 
849 	// in case of B_BAD_SEM_ID, the port might have been closed but not yet
850 	// deleted, ie. there could still be messages waiting for us
851 
852 	state = disable_interrupts();
853 	GRAB_PORT_LOCK(sPorts[slot]);
854 
855 	if (sPorts[slot].id != id) {
856 		// the port is no longer there
857 		RELEASE_PORT_LOCK(sPorts[slot]);
858 		restore_interrupts(state);
859 		return B_BAD_PORT_ID;
860 	}
861 
862 	// determine tail & get the length of the message
863 	msg = (port_msg*)list_get_first_item(&sPorts[slot].msg_queue);
864 	if (msg == NULL) {
865 		if (status == B_OK)
866 			panic("port %ld: no messages found\n", sPorts[slot].id);
867 
868 		size = B_BAD_PORT_ID;
869 	} else
870 		size = msg->size;
871 
872 	RELEASE_PORT_LOCK(sPorts[slot]);
873 	restore_interrupts(state);
874 
875 	// restore read_sem, as we haven't read from the port
876 	release_sem(cachedSem);
877 
878 	// return length of item at end of queue
879 	return size;
880 }
881 
882 
883 ssize_t
884 port_count(port_id id)
885 {
886 	cpu_status state;
887 	int32 count = 0;
888 	int32 slot;
889 
890 	if (!sPortsActive || id < 0)
891 		return B_BAD_PORT_ID;
892 
893 	slot = id % sMaxPorts;
894 
895 	state = disable_interrupts();
896 	GRAB_PORT_LOCK(sPorts[slot]);
897 
898 	if (sPorts[slot].id != id) {
899 		RELEASE_PORT_LOCK(sPorts[slot]);
900 		restore_interrupts(state);
901 		TRACE(("port_count: invalid port_id %ld\n", id));
902 		return B_BAD_PORT_ID;
903 	}
904 
905 	if (get_sem_count(sPorts[slot].read_sem, &count) == B_OK) {
906 		// do not return negative numbers
907 		if (count < 0)
908 			count = 0;
909 	} else {
910 		// the port might have been closed - we need to actually count the messages
911 		void *message = NULL;
912 		while ((message = list_get_next_item(&sPorts[slot].msg_queue, message)) != NULL) {
913 			count++;
914 		}
915 	}
916 
917 	RELEASE_PORT_LOCK(sPorts[slot]);
918 	restore_interrupts(state);
919 
920 	// return count of messages
921 	return count;
922 }
923 
924 
925 ssize_t
926 read_port(port_id port, int32 *msgCode, void *msgBuffer, size_t bufferSize)
927 {
928 	return read_port_etc(port, msgCode, msgBuffer, bufferSize, 0, 0);
929 }
930 
931 
932 ssize_t
933 read_port_etc(port_id id, int32 *_msgCode, void *msgBuffer, size_t bufferSize,
934 	uint32 flags, bigtime_t timeout)
935 {
936 	cpu_status state;
937 	sem_id cachedSem;
938 	status_t status;
939 	bool userCopy = (flags & PORT_FLAG_USE_USER_MEMCPY) > 0;
940 	port_msg *msg;
941 	size_t size;
942 	int slot;
943 
944 	if (!sPortsActive || id < 0)
945 		return B_BAD_PORT_ID;
946 
947 	if ((msgBuffer == NULL && bufferSize > 0)
948 		|| timeout < 0)
949 		return B_BAD_VALUE;
950 
951 	flags = flags & (B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT
952 				| B_RELATIVE_TIMEOUT | B_ABSOLUTE_TIMEOUT);
953 	slot = id % sMaxPorts;
954 
955 	state = disable_interrupts();
956 	GRAB_PORT_LOCK(sPorts[slot]);
957 
958 	if (sPorts[slot].id != id
959 		|| (is_port_closed(slot) && list_is_empty(&sPorts[slot].msg_queue))) {
960 		RELEASE_PORT_LOCK(sPorts[slot]);
961 		restore_interrupts(state);
962 		TRACE(("read_port_etc(): %s port %ld\n",
963 			sPorts[slot].id == id ? "closed" : "invalid", id));
964 		return B_BAD_PORT_ID;
965 	}
966 	// store sem_id in local variable
967 	cachedSem = sPorts[slot].read_sem;
968 
969 	// unlock port && enable ints/
970 	RELEASE_PORT_LOCK(sPorts[slot]);
971 	restore_interrupts(state);
972 
973 	status = acquire_sem_etc(cachedSem, 1, flags, timeout);
974 		// get 1 entry from the queue, block if needed
975 
976 	if (status != B_OK && status != B_BAD_SEM_ID)
977 		return status;
978 
979 	// in case of B_BAD_SEM_ID, the port might have been closed but not yet
980 	// deleted, ie. there could still be messages waiting for us
981 
982 	state = disable_interrupts();
983 	GRAB_PORT_LOCK(sPorts[slot]);
984 
985 	// first, let's check if the port is still alive
986 	if (sPorts[slot].id == -1) {
987 		// the port has been deleted in the meantime
988 		RELEASE_PORT_LOCK(sPorts[slot]);
989 		restore_interrupts(state);
990 		return B_BAD_PORT_ID;
991 	}
992 
993 	msg = (port_msg*)list_get_first_item(&sPorts[slot].msg_queue);
994 	if (msg == NULL) {
995 		if (status == B_OK)
996 			panic("port %ld: no messages found", sPorts[slot].id);
997 
998 		// the port has obviously been closed, but no messages are left anymore
999 		RELEASE_PORT_LOCK(sPorts[slot]);
1000 		restore_interrupts(state);
1001 		return B_BAD_PORT_ID;
1002 	}
1003 
1004 	list_remove_link(msg);
1005 
1006 	sPorts[slot].total_count++;
1007 
1008 	notify_port_select_events(slot, B_EVENT_WRITE);
1009 
1010 	cachedSem = sPorts[slot].write_sem;
1011 
1012 	RELEASE_PORT_LOCK(sPorts[slot]);
1013 	restore_interrupts(state);
1014 
1015 	// check output buffer size
1016 	size = min_c(bufferSize, msg->size);
1017 
1018 	// copy message
1019 	if (_msgCode != NULL)
1020 		*_msgCode = msg->code;
1021 	if (size > 0) {
1022 		if (userCopy) {
1023 			if ((status = cbuf_user_memcpy_from_chain(msgBuffer, msg->buffer_chain, 0, size) < B_OK)) {
1024 				// leave the port intact, for other threads that might not crash
1025 				put_port_msg(msg);
1026 				release_sem(cachedSem);
1027 				return status;
1028 			}
1029 		} else
1030 			cbuf_memcpy_from_chain(msgBuffer, msg->buffer_chain, 0, size);
1031 	}
1032 	put_port_msg(msg);
1033 
1034 	// make one spot in queue available again for write
1035 	release_sem(cachedSem);
1036 		// ToDo: we might think about setting B_NO_RESCHEDULE here
1037 		//	from time to time (always?)
1038 
1039 	return size;
1040 }
1041 
1042 
1043 status_t
1044 write_port(port_id id, int32 msgCode, const void *msgBuffer, size_t bufferSize)
1045 {
1046 	iovec vec = { (void *)msgBuffer, bufferSize };
1047 
1048 	return writev_port_etc(id, msgCode, &vec, 1, bufferSize, 0, 0);
1049 }
1050 
1051 
1052 status_t
1053 write_port_etc(port_id id, int32 msgCode, const void *msgBuffer,
1054 	size_t bufferSize, uint32 flags, bigtime_t timeout)
1055 {
1056 	iovec vec = { (void *)msgBuffer, bufferSize };
1057 
1058 	return writev_port_etc(id, msgCode, &vec, 1, bufferSize, flags, timeout);
1059 }
1060 
1061 
1062 status_t
1063 writev_port_etc(port_id id, int32 msgCode, const iovec *msgVecs,
1064 	size_t vecCount, size_t bufferSize, uint32 flags,
1065 	bigtime_t timeout)
1066 {
1067 	cpu_status state;
1068 	sem_id cachedSem;
1069 	status_t status;
1070 	port_msg *msg;
1071 	bool userCopy = (flags & PORT_FLAG_USE_USER_MEMCPY) > 0;
1072 	int slot;
1073 
1074 	if (!sPortsActive || id < 0)
1075 		return B_BAD_PORT_ID;
1076 
1077 	// mask irrelevant flags (for acquire_sem() usage)
1078 	flags = flags & (B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT
1079 				| B_RELATIVE_TIMEOUT | B_ABSOLUTE_TIMEOUT);
1080 	slot = id % sMaxPorts;
1081 
1082 	if (bufferSize > PORT_MAX_MESSAGE_SIZE)
1083 		return B_BAD_VALUE;
1084 
1085 	state = disable_interrupts();
1086 	GRAB_PORT_LOCK(sPorts[slot]);
1087 
1088 	if (sPorts[slot].id != id) {
1089 		RELEASE_PORT_LOCK(sPorts[slot]);
1090 		restore_interrupts(state);
1091 		TRACE(("write_port_etc: invalid port_id %ld\n", id));
1092 		return B_BAD_PORT_ID;
1093 	}
1094 
1095 	if (is_port_closed(slot)) {
1096 		RELEASE_PORT_LOCK(sPorts[slot]);
1097 		restore_interrupts(state);
1098 		TRACE(("write_port_etc: port %ld closed\n", id));
1099 		return B_BAD_PORT_ID;
1100 	}
1101 
1102 	// store sem_id in local variable
1103 	cachedSem = sPorts[slot].write_sem;
1104 
1105 	RELEASE_PORT_LOCK(sPorts[slot]);
1106 	restore_interrupts(state);
1107 
1108 	status = acquire_sem_etc(cachedSem, 1, flags, timeout);
1109 		// get 1 entry from the queue, block if needed
1110 
1111 	if (status == B_BAD_SEM_ID) {
1112 		// somebody deleted or closed the port
1113 		return B_BAD_PORT_ID;
1114 	}
1115 	if (status != B_OK)
1116 		return status;
1117 
1118 	msg = get_port_msg(msgCode, bufferSize);
1119 	if (msg == NULL)
1120 		return B_NO_MEMORY;
1121 
1122 	if (bufferSize > 0) {
1123 		uint32 i;
1124 		if (userCopy) {
1125 			// copy from user memory
1126 			for (i = 0; i < vecCount; i++) {
1127 				size_t bytes = msgVecs[i].iov_len;
1128 				if (bytes > bufferSize)
1129 					bytes = bufferSize;
1130 
1131 				if ((status = cbuf_user_memcpy_to_chain(msg->buffer_chain,
1132 						0, msgVecs[i].iov_base, bytes)) < B_OK)
1133 					return status;
1134 
1135 				bufferSize -= bytes;
1136 				if (bufferSize == 0)
1137 					break;
1138 			}
1139 		} else {
1140 			// copy from kernel memory
1141 			for (i = 0; i < vecCount; i++) {
1142 				size_t bytes = msgVecs[i].iov_len;
1143 				if (bytes > bufferSize)
1144 					bytes = bufferSize;
1145 
1146 				if ((status = cbuf_memcpy_to_chain(msg->buffer_chain,
1147 						0, msgVecs[i].iov_base, bytes)) < 0)
1148 					return status;
1149 
1150 				bufferSize -= bytes;
1151 				if (bufferSize == 0)
1152 					break;
1153 			}
1154 		}
1155 	}
1156 
1157 	// attach message to queue
1158 	state = disable_interrupts();
1159 	GRAB_PORT_LOCK(sPorts[slot]);
1160 
1161 	// first, let's check if the port is still alive
1162 	if (sPorts[slot].id == -1) {
1163 		// the port has been deleted in the meantime
1164 		RELEASE_PORT_LOCK(sPorts[slot]);
1165 		restore_interrupts(state);
1166 
1167 		put_port_msg(msg);
1168 		return B_BAD_PORT_ID;
1169 	}
1170 
1171 	list_add_item(&sPorts[slot].msg_queue, msg);
1172 
1173 	notify_port_select_events(slot, B_EVENT_READ);
1174 
1175 	// store sem_id in local variable
1176 	cachedSem = sPorts[slot].read_sem;
1177 
1178 	RELEASE_PORT_LOCK(sPorts[slot]);
1179 	restore_interrupts(state);
1180 
1181 	// release sem, allowing read (might reschedule)
1182 	release_sem(cachedSem);
1183 
1184 	return B_NO_ERROR;
1185 }
1186 
1187 
1188 status_t
1189 set_port_owner(port_id id, team_id team)
1190 {
1191 	cpu_status state;
1192 	int slot;
1193 // ToDo: Shouldn't we at least check, whether the team exists?
1194 
1195 	TRACE(("set_port_owner(id = %ld, team = %ld)\n", id, team));
1196 
1197 	if (!sPortsActive || id < 0)
1198 		return B_BAD_PORT_ID;
1199 
1200 	slot = id % sMaxPorts;
1201 
1202 	state = disable_interrupts();
1203 	GRAB_PORT_LOCK(sPorts[slot]);
1204 
1205 	if (sPorts[slot].id != id) {
1206 		RELEASE_PORT_LOCK(sPorts[slot]);
1207 		restore_interrupts(state);
1208 		TRACE(("set_port_owner: invalid port_id %ld\n", id));
1209 		return B_BAD_PORT_ID;
1210 	}
1211 
1212 	// transfer ownership to other team
1213 	sPorts[slot].owner = team;
1214 
1215 	// unlock port
1216 	RELEASE_PORT_LOCK(sPorts[slot]);
1217 	restore_interrupts(state);
1218 
1219 	return B_NO_ERROR;
1220 }
1221 
1222 
1223 //	#pragma mark - syscalls
1224 
1225 
1226 port_id
1227 _user_create_port(int32 queueLength, const char *userName)
1228 {
1229 	char name[B_OS_NAME_LENGTH];
1230 
1231 	if (userName == NULL)
1232 		return create_port(queueLength, NULL);
1233 
1234 	if (!IS_USER_ADDRESS(userName)
1235 		|| user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK)
1236 		return B_BAD_ADDRESS;
1237 
1238 	return create_port(queueLength, name);
1239 }
1240 
1241 
1242 status_t
1243 _user_close_port(port_id id)
1244 {
1245 	return close_port(id);
1246 }
1247 
1248 
1249 status_t
1250 _user_delete_port(port_id id)
1251 {
1252 	return delete_port(id);
1253 }
1254 
1255 
1256 port_id
1257 _user_find_port(const char *userName)
1258 {
1259 	char name[B_OS_NAME_LENGTH];
1260 
1261 	if (userName == NULL)
1262 		return B_BAD_VALUE;
1263 	if (!IS_USER_ADDRESS(userName)
1264 		|| user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK)
1265 		return B_BAD_ADDRESS;
1266 
1267 	return find_port(name);
1268 }
1269 
1270 
1271 status_t
1272 _user_get_port_info(port_id id, struct port_info *userInfo)
1273 {
1274 	struct port_info info;
1275 	status_t status;
1276 
1277 	if (userInfo == NULL)
1278 		return B_BAD_VALUE;
1279 	if (!IS_USER_ADDRESS(userInfo))
1280 		return B_BAD_ADDRESS;
1281 
1282 	status = get_port_info(id, &info);
1283 
1284 	// copy back to user space
1285 	if (status == B_OK
1286 		&& user_memcpy(userInfo, &info, sizeof(struct port_info)) < B_OK)
1287 		return B_BAD_ADDRESS;
1288 
1289 	return status;
1290 }
1291 
1292 
1293 status_t
1294 _user_get_next_port_info(team_id team, int32 *userCookie,
1295 	struct port_info *userInfo)
1296 {
1297 	struct port_info info;
1298 	status_t status;
1299 	int32 cookie;
1300 
1301 	if (userCookie == NULL || userInfo == NULL)
1302 		return B_BAD_VALUE;
1303 	if (!IS_USER_ADDRESS(userCookie) || !IS_USER_ADDRESS(userInfo)
1304 		|| user_memcpy(&cookie, userCookie, sizeof(int32)) < B_OK)
1305 		return B_BAD_ADDRESS;
1306 
1307 	status = get_next_port_info(team, &cookie, &info);
1308 
1309 	// copy back to user space
1310 	if (user_memcpy(userCookie, &cookie, sizeof(int32)) < B_OK
1311 		|| (status == B_OK && user_memcpy(userInfo, &info,
1312 				sizeof(struct port_info)) < B_OK))
1313 		return B_BAD_ADDRESS;
1314 
1315 	return status;
1316 }
1317 
1318 
1319 ssize_t
1320 _user_port_buffer_size_etc(port_id port, uint32 flags, bigtime_t timeout)
1321 {
1322 	syscall_restart_handle_timeout_pre(flags, timeout);
1323 
1324 	status_t status = port_buffer_size_etc(port, flags | B_CAN_INTERRUPT,
1325 		timeout);
1326 
1327 	return syscall_restart_handle_timeout_post(status, timeout);
1328 }
1329 
1330 
1331 ssize_t
1332 _user_port_count(port_id port)
1333 {
1334 	return port_count(port);
1335 }
1336 
1337 
1338 status_t
1339 _user_set_port_owner(port_id port, team_id team)
1340 {
1341 	return set_port_owner(port, team);
1342 }
1343 
1344 
1345 ssize_t
1346 _user_read_port_etc(port_id port, int32 *userCode, void *userBuffer,
1347 	size_t bufferSize, uint32 flags, bigtime_t timeout)
1348 {
1349 	int32 messageCode;
1350 	ssize_t	bytesRead;
1351 
1352 	syscall_restart_handle_timeout_pre(flags, timeout);
1353 
1354 	if (userBuffer == NULL && bufferSize != 0)
1355 		return B_BAD_VALUE;
1356 	if ((userCode != NULL && !IS_USER_ADDRESS(userCode))
1357 		|| (userBuffer != NULL && !IS_USER_ADDRESS(userBuffer)))
1358 		return B_BAD_ADDRESS;
1359 
1360 	bytesRead = read_port_etc(port, &messageCode, userBuffer, bufferSize,
1361 		flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, timeout);
1362 
1363 	if (bytesRead >= 0 && userCode != NULL
1364 		&& user_memcpy(userCode, &messageCode, sizeof(int32)) < B_OK)
1365 		return B_BAD_ADDRESS;
1366 
1367 	return syscall_restart_handle_timeout_post(bytesRead, timeout);
1368 }
1369 
1370 
1371 status_t
1372 _user_write_port_etc(port_id port, int32 messageCode, const void *userBuffer,
1373 	size_t bufferSize, uint32 flags, bigtime_t timeout)
1374 {
1375 	iovec vec = { (void *)userBuffer, bufferSize };
1376 
1377 	syscall_restart_handle_timeout_pre(flags, timeout);
1378 
1379 	if (userBuffer == NULL && bufferSize != 0)
1380 		return B_BAD_VALUE;
1381 	if (userBuffer != NULL && !IS_USER_ADDRESS(userBuffer))
1382 		return B_BAD_ADDRESS;
1383 
1384 	status_t status = writev_port_etc(port, messageCode, &vec, 1, bufferSize,
1385 		flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, timeout);
1386 
1387 	return syscall_restart_handle_timeout_post(status, timeout);
1388 }
1389 
1390 
1391 status_t
1392 _user_writev_port_etc(port_id port, int32 messageCode, const iovec *userVecs,
1393 	size_t vecCount, size_t bufferSize, uint32 flags, bigtime_t timeout)
1394 {
1395 	syscall_restart_handle_timeout_pre(flags, timeout);
1396 
1397 	if (userVecs == NULL && bufferSize != 0)
1398 		return B_BAD_VALUE;
1399 	if (userVecs != NULL && !IS_USER_ADDRESS(userVecs))
1400 		return B_BAD_ADDRESS;
1401 
1402 	iovec *vecs = NULL;
1403 	if (userVecs && vecCount != 0) {
1404 		vecs = (iovec*)malloc(sizeof(iovec) * vecCount);
1405 		if (vecs == NULL)
1406 			return B_NO_MEMORY;
1407 
1408 		if (user_memcpy(vecs, userVecs, sizeof(iovec) * vecCount) < B_OK) {
1409 			free(vecs);
1410 			return B_BAD_ADDRESS;
1411 		}
1412 	}
1413 
1414 	status_t status = writev_port_etc(port, messageCode, vecs, vecCount,
1415 		bufferSize, flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT,
1416 		timeout);
1417 
1418 	free(vecs);
1419 	return syscall_restart_handle_timeout_post(status, timeout);
1420 }
1421