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