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