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