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