xref: /haiku/src/system/kernel/sem.cpp (revision fc1ca2da5cfcb00ffdf791606d5ae97fdd58a638)
1 /*
2  * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Copyright 2002-2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
4  * Distributed under the terms of the MIT License.
5  *
6  * Copyright 2001, Travis Geiselbrecht. All rights reserved.
7  * Distributed under the terms of the NewOS License.
8  */
9 
10 /*! Semaphore code */
11 
12 
13 #include <OS.h>
14 
15 #include <sem.h>
16 #include <kernel.h>
17 #include <kscheduler.h>
18 #include <ksignal.h>
19 #include <smp.h>
20 #include <int.h>
21 #include <arch/int.h>
22 #include <debug.h>
23 #include <thread.h>
24 #include <team.h>
25 #include <util/AutoLock.h>
26 #include <util/DoublyLinkedList.h>
27 #include <vfs.h>
28 #include <vm_low_memory.h>
29 #include <vm_page.h>
30 #include <boot/kernel_args.h>
31 #include <syscall_restart.h>
32 #include <wait_for_objects.h>
33 
34 #include <string.h>
35 #include <stdlib.h>
36 
37 
38 //#define TRACE_SEM
39 #ifdef TRACE_SEM
40 #	define TRACE(x) dprintf_no_syslog x
41 #else
42 #	define TRACE(x) ;
43 #endif
44 
45 //#define KTRACE_SEM
46 #ifdef KTRACE_SEM
47 #	define KTRACE(x...) ktrace_printf(x)
48 #else
49 #	define KTRACE(x...) do {} while (false)
50 #endif
51 
52 #define DEBUG_LAST_ACQUIRER
53 
54 struct queued_thread : DoublyLinkedListLinkImpl<queued_thread> {
55 	queued_thread(struct thread *thread, int32 count)
56 		:
57 		thread(thread),
58 		count(count),
59 		queued(false)
60 	{
61 	}
62 
63 	struct thread	*thread;
64 	int32			count;
65 	bool			queued;
66 };
67 
68 typedef DoublyLinkedList<queued_thread> ThreadQueue;
69 
70 struct sem_entry {
71 	sem_id		id;
72 	spinlock	lock;	// protects only the id field when unused
73 	union {
74 		// when slot in use
75 		struct {
76 			int32				count;
77 			int32				net_count;
78 									// count + acquisition count of all blocked
79 									// threads
80 			char				*name;
81 			team_id				owner;	// if set to -1, means owned by a port
82 			select_info			*select_infos;
83 			thread_id			last_acquirer;
84 #ifdef DEBUG_LAST_ACQUIRER
85 			int32				last_acquire_count;
86 			thread_id			last_releaser;
87 			int32				last_release_count;
88 #endif
89 		} used;
90 
91 		// when slot unused
92 		struct {
93 			sem_id				next_id;
94 			struct sem_entry	*next;
95 		} unused;
96 	} u;
97 
98 	ThreadQueue			queue;	// should be in u.used, but has a constructor
99 };
100 
101 static const int32 kMaxSemaphores = 131072;
102 static int32 sMaxSems = 4096;
103 	// Final value is computed based on the amount of available memory
104 static int32 sUsedSems = 0;
105 
106 static struct sem_entry *sSems = NULL;
107 static bool sSemsActive = false;
108 static struct sem_entry	*sFreeSemsHead = NULL;
109 static struct sem_entry	*sFreeSemsTail = NULL;
110 
111 static spinlock sem_spinlock = 0;
112 #define GRAB_SEM_LIST_LOCK()     acquire_spinlock(&sem_spinlock)
113 #define RELEASE_SEM_LIST_LOCK()  release_spinlock(&sem_spinlock)
114 #define GRAB_SEM_LOCK(s)         acquire_spinlock(&(s).lock)
115 #define RELEASE_SEM_LOCK(s)      release_spinlock(&(s).lock)
116 
117 
118 static int
119 dump_sem_list(int argc, char **argv)
120 {
121 	const char *name = NULL;
122 	team_id owner = -1;
123 	thread_id last = -1;
124 	int32 i;
125 
126 	if (argc > 2) {
127 		if (!strcmp(argv[1], "team") || !strcmp(argv[1], "owner"))
128 			owner = strtoul(argv[2], NULL, 0);
129 		else if (!strcmp(argv[1], "name"))
130 			name = argv[2];
131 		else if (!strcmp(argv[1], "last"))
132 			last = strtoul(argv[2], NULL, 0);
133 	} else if (argc > 1)
134 		owner = strtoul(argv[1], NULL, 0);
135 
136 	kprintf("sem            id count   team   last  name\n");
137 
138 	for (i = 0; i < sMaxSems; i++) {
139 		struct sem_entry *sem = &sSems[i];
140 		if (sem->id < 0
141 			|| (last != -1 && sem->u.used.last_acquirer != last)
142 			|| (name != NULL && strstr(sem->u.used.name, name) == NULL)
143 			|| (owner != -1 && sem->u.used.owner != owner))
144 			continue;
145 
146 		kprintf("%p %6ld %5ld %6ld "
147 			"%6ld "
148 			" %s\n", sem, sem->id, sem->u.used.count,
149 			sem->u.used.owner,
150 			sem->u.used.last_acquirer > 0 ? sem->u.used.last_acquirer : 0,
151 			sem->u.used.name);
152 	}
153 
154 	return 0;
155 }
156 
157 
158 static void
159 dump_sem(struct sem_entry *sem)
160 {
161 	kprintf("SEM: %p\n", sem);
162 	kprintf("id:      %ld (%#lx)\n", sem->id, sem->id);
163 	if (sem->id >= 0) {
164 		kprintf("name:    '%s'\n", sem->u.used.name);
165 		kprintf("owner:   %ld\n", sem->u.used.owner);
166 		kprintf("count:   %ld\n", sem->u.used.count);
167 		kprintf("queue:  ");
168 		if (!sem->queue.IsEmpty()) {
169 			ThreadQueue::Iterator it = sem->queue.GetIterator();
170 			while (queued_thread* entry = it.Next())
171 				kprintf(" %ld", entry->thread->id);
172 			kprintf("\n");
173 		} else
174 			kprintf(" -\n");
175 
176 		set_debug_variable("_sem", (addr_t)sem);
177 		set_debug_variable("_semID", sem->id);
178 		set_debug_variable("_owner", sem->u.used.owner);
179 
180 #ifdef DEBUG_LAST_ACQUIRER
181 		kprintf("last acquired by: %ld, count: %ld\n", sem->u.used.last_acquirer,
182 			sem->u.used.last_acquire_count);
183 		kprintf("last released by: %ld, count: %ld\n", sem->u.used.last_releaser,
184 			sem->u.used.last_release_count);
185 
186 		if (sem->u.used.last_releaser != 0)
187 			set_debug_variable("_releaser", sem->u.used.last_releaser);
188 		else
189 			unset_debug_variable("_releaser");
190 #else
191 		kprintf("last acquired by: %ld\n", sem->u.used.last_acquirer);
192 #endif
193 
194 		if (sem->u.used.last_acquirer != 0)
195 			set_debug_variable("_acquirer", sem->u.used.last_acquirer);
196 		else
197 			unset_debug_variable("_acquirer");
198 
199 	} else {
200 		kprintf("next:    %p\n", sem->u.unused.next);
201 		kprintf("next_id: %ld\n", sem->u.unused.next_id);
202 	}
203 }
204 
205 
206 static int
207 dump_sem_info(int argc, char **argv)
208 {
209 	bool found = false;
210 	addr_t num;
211 	int32 i;
212 
213 	if (argc < 2) {
214 		print_debugger_command_usage(argv[0]);
215 		return 0;
216 	}
217 
218 	num = strtoul(argv[1], NULL, 0);
219 
220 	if (IS_KERNEL_ADDRESS(num)) {
221 		dump_sem((struct sem_entry *)num);
222 		return 0;
223 	} else if (num > 0) {
224 		uint32 slot = num % sMaxSems;
225 		if (sSems[slot].id != (int)num) {
226 			kprintf("sem %ld (%#lx) doesn't exist!\n", num, num);
227 			return 0;
228 		}
229 
230 		dump_sem(&sSems[slot]);
231 		return 0;
232 	}
233 
234 	// walk through the sem list, trying to match name
235 	for (i = 0; i < sMaxSems; i++) {
236 		if (sSems[i].u.used.name != NULL
237 			&& strcmp(argv[1], sSems[i].u.used.name) == 0) {
238 			dump_sem(&sSems[i]);
239 			found = true;
240 		}
241 	}
242 
243 	if (!found)
244 		kprintf("sem \"%s\" doesn't exist!\n", argv[1]);
245 	return 0;
246 }
247 
248 
249 /*!	\brief Appends a semaphore slot to the free list.
250 
251 	The semaphore list must be locked.
252 	The slot's id field is not changed. It should already be set to -1.
253 
254 	\param slot The index of the semaphore slot.
255 	\param nextID The ID the slot will get when reused. If < 0 the \a slot
256 		   is used.
257 */
258 static void
259 free_sem_slot(int slot, sem_id nextID)
260 {
261 	struct sem_entry *sem = sSems + slot;
262 	// set next_id to the next possible value; for sanity check the current ID
263 	if (nextID < 0)
264 		sem->u.unused.next_id = slot;
265 	else
266 		sem->u.unused.next_id = nextID;
267 	// append the entry to the list
268 	if (sFreeSemsTail)
269 		sFreeSemsTail->u.unused.next = sem;
270 	else
271 		sFreeSemsHead = sem;
272 	sFreeSemsTail = sem;
273 	sem->u.unused.next = NULL;
274 }
275 
276 
277 static inline void
278 notify_sem_select_events(struct sem_entry* sem, uint16 events)
279 {
280 	if (sem->u.used.select_infos)
281 		notify_select_events_list(sem->u.used.select_infos, events);
282 }
283 
284 
285 /*!	Fills the thread_info structure with information from the specified
286 	thread.
287 	The thread lock must be held when called.
288 */
289 static void
290 fill_sem_info(struct sem_entry *sem, sem_info *info, size_t size)
291 {
292 	info->sem = sem->id;
293 	info->team = sem->u.used.owner;
294 	strlcpy(info->name, sem->u.used.name, sizeof(info->name));
295 	info->count = sem->u.used.count;
296 	info->latest_holder = sem->u.used.last_acquirer;
297 }
298 
299 
300 //	#pragma mark - Private Kernel API
301 
302 
303 status_t
304 sem_init(kernel_args *args)
305 {
306 	area_id area;
307 	int32 i;
308 
309 	TRACE(("sem_init: entry\n"));
310 
311 	// compute maximal number of semaphores depending on the available memory
312 	// 128 MB -> 16384 semaphores, 448 kB fixed array size
313 	// 256 MB -> 32768, 896 kB
314 	// 512 MB -> 65536, 1.75 MB
315 	// 1024 MB and more -> 131072, 3.5 MB
316 	i = vm_page_num_pages() / 2;
317 	while (sMaxSems < i && sMaxSems < kMaxSemaphores)
318 		sMaxSems <<= 1;
319 
320 	// create and initialize semaphore table
321 	area = create_area("sem_table", (void **)&sSems, B_ANY_KERNEL_ADDRESS,
322 		sizeof(struct sem_entry) * sMaxSems, B_FULL_LOCK,
323 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
324 	if (area < 0)
325 		panic("unable to allocate semaphore table!\n");
326 
327 	memset(sSems, 0, sizeof(struct sem_entry) * sMaxSems);
328 	for (i = 0; i < sMaxSems; i++) {
329 		sSems[i].id = -1;
330 		free_sem_slot(i, i);
331 	}
332 
333 	// add debugger commands
334 	add_debugger_command_etc("sems", &dump_sem_list,
335 		"Dump a list of all active semaphores (for team, with name, etc.)",
336 		"[ ([ \"team\" | \"owner\" ] <team>) | (\"name\" <name>) ]"
337 			" | (\"last\" <last acquirer>)\n"
338 		"Prints a list of all active semaphores meeting the given\n"
339 		"requirement. If no argument is given, all sems are listed.\n"
340 		"  <team>             - The team owning the semaphores.\n"
341 		"  <name>             - Part of the name of the semaphores.\n"
342 		"  <last acquirer>    - The thread that last acquired the semaphore.\n"
343 		, 0);
344 	add_debugger_command_etc("sem", &dump_sem_info,
345 		"Dump info about a particular semaphore",
346 		"<sem>\n"
347 		"Prints info about the specified semaphore.\n"
348 		"  <sem>  - pointer to the semaphore structure, semaphore ID, or name\n"
349 		"           of the semaphore to print info for.\n", 0);
350 
351 	TRACE(("sem_init: exit\n"));
352 
353 	sSemsActive = true;
354 
355 	return 0;
356 }
357 
358 
359 /*!	Creates a semaphore with the given parameters.
360 	Note, the team_id is not checked, it must be correct, or else
361 	that semaphore might not be deleted.
362 	This function is only available from within the kernel, and
363 	should not be made public - if possible, we should remove it
364 	completely (and have only create_sem() exported).
365 */
366 sem_id
367 create_sem_etc(int32 count, const char *name, team_id owner)
368 {
369 	struct sem_entry *sem = NULL;
370 	cpu_status state;
371 	sem_id id = B_NO_MORE_SEMS;
372 	char *tempName;
373 	size_t nameLength;
374 
375 	if (sSemsActive == false)
376 		return B_NO_MORE_SEMS;
377 
378 #if 0
379 	// TODO: the code below might cause unwanted deadlocks,
380 	// we need an asynchronously running low resource handler.
381 	if (sUsedSems == sMaxSems) {
382 		// The vnode cache may have collected lots of semaphores.
383 		// Freeing some unused vnodes should improve our situation.
384 		// TODO: maybe create a generic "low resources" handler, instead
385 		//	of only the specialised low memory thing?
386 		vfs_free_unused_vnodes(B_LOW_MEMORY_WARNING);
387 	}
388 	if (sUsedSems == sMaxSems) {
389 		// try again with more enthusiasm
390 		vfs_free_unused_vnodes(B_LOW_MEMORY_CRITICAL);
391 	}
392 #endif
393 	if (sUsedSems == sMaxSems)
394 		return B_NO_MORE_SEMS;
395 
396 	if (name == NULL)
397 		name = "unnamed semaphore";
398 
399 	nameLength = strlen(name) + 1;
400 	nameLength = min_c(nameLength, B_OS_NAME_LENGTH);
401 	tempName = (char *)malloc(nameLength);
402 	if (tempName == NULL)
403 		return B_NO_MEMORY;
404 	strlcpy(tempName, name, nameLength);
405 
406 	state = disable_interrupts();
407 	GRAB_SEM_LIST_LOCK();
408 
409 	// get the first slot from the free list
410 	sem = sFreeSemsHead;
411 	if (sem) {
412 		// remove it from the free list
413 		sFreeSemsHead = sem->u.unused.next;
414 		if (!sFreeSemsHead)
415 			sFreeSemsTail = NULL;
416 
417 		// init the slot
418 		GRAB_SEM_LOCK(*sem);
419 		sem->id = sem->u.unused.next_id;
420 		sem->u.used.count = count;
421 		sem->u.used.net_count = count;
422 		new(&sem->queue) ThreadQueue;
423 		sem->u.used.name = tempName;
424 		sem->u.used.owner = owner;
425 		sem->u.used.select_infos = NULL;
426 		id = sem->id;
427 		RELEASE_SEM_LOCK(*sem);
428 
429 		atomic_add(&sUsedSems, 1);
430 
431 		KTRACE("create_sem_etc(count: %ld, name: %s, owner: %ld) -> %ld",
432 			count, name, owner, id);
433 	}
434 
435 	RELEASE_SEM_LIST_LOCK();
436 	restore_interrupts(state);
437 
438 	if (!sem)
439 		free(tempName);
440 
441 	return id;
442 }
443 
444 
445 status_t
446 select_sem(int32 id, struct select_info* info, bool kernel)
447 {
448 	cpu_status state;
449 	int32 slot;
450 	status_t error = B_OK;
451 
452 	if (id < 0)
453 		return B_BAD_SEM_ID;
454 
455 	slot = id % sMaxSems;
456 
457 	state = disable_interrupts();
458 	GRAB_SEM_LOCK(sSems[slot]);
459 
460 	if (sSems[slot].id != id) {
461 		// bad sem ID
462 		error = B_BAD_SEM_ID;
463 	} else if (!kernel
464 		&& sSems[slot].u.used.owner == team_get_kernel_team_id()) {
465 		// kernel semaphore, but call from userland
466 		error = B_NOT_ALLOWED;
467 	} else {
468 		info->selected_events &= B_EVENT_ACQUIRE_SEMAPHORE | B_EVENT_INVALID;
469 
470 		if (info->selected_events != 0) {
471 			info->next = sSems[slot].u.used.select_infos;
472 			sSems[slot].u.used.select_infos = info;
473 
474 			if (sSems[slot].u.used.count > 0)
475 				notify_select_events(info, B_EVENT_ACQUIRE_SEMAPHORE);
476 		}
477 	}
478 
479 	RELEASE_SEM_LOCK(sSems[slot]);
480 	restore_interrupts(state);
481 
482 	return error;
483 }
484 
485 
486 status_t
487 deselect_sem(int32 id, struct select_info* info, bool kernel)
488 {
489 	cpu_status state;
490 	int32 slot;
491 
492 	if (id < 0)
493 		return B_BAD_SEM_ID;
494 
495 	if (info->selected_events == 0)
496 		return B_OK;
497 
498 	slot = id % sMaxSems;
499 
500 	state = disable_interrupts();
501 	GRAB_SEM_LOCK(sSems[slot]);
502 
503 	if (sSems[slot].id == id) {
504 		select_info** infoLocation = &sSems[slot].u.used.select_infos;
505 		while (*infoLocation != NULL && *infoLocation != info)
506 			infoLocation = &(*infoLocation)->next;
507 
508 		if (*infoLocation == info)
509 			*infoLocation = info->next;
510 	}
511 
512 	RELEASE_SEM_LOCK(sSems[slot]);
513 	restore_interrupts(state);
514 
515 	return B_OK;
516 }
517 
518 
519 /*!	Forcibly removes a thread from a semaphores wait queue. May have to wake up
520 	other threads in the process.
521 	Must be called with semaphore lock held. The thread lock must not be held.
522 */
523 static void
524 remove_thread_from_sem(queued_thread *entry, struct sem_entry *sem)
525 {
526 	if (!entry->queued)
527 		return;
528 
529 	sem->queue.Remove(entry);
530 	entry->queued = false;
531 	sem->u.used.count += entry->count;
532 
533 	// We're done with this entry. We only have to check, if other threads
534 	// need unblocking, too.
535 
536 	// Now see if more threads need to be woken up. We get the thread lock for
537 	// that time, so the blocking state of threads won't change. We need that
538 	// lock anyway when unblocking a thread.
539 	GRAB_THREAD_LOCK();
540 
541 	while ((entry = sem->queue.Head()) != NULL) {
542 		if (thread_is_blocked(entry->thread)) {
543 			// The thread is still waiting. If its count is satisfied, unblock
544 			// it. Otherwise we can't unblock any other thread.
545 			if (entry->count > sem->u.used.net_count)
546 				break;
547 
548 			thread_unblock_locked(entry->thread, B_OK);
549 			sem->u.used.net_count -= entry->count;
550 		} else {
551 			// The thread is no longer waiting, but still queued, which means
552 			// acquiration failed and we can just remove it.
553 			sem->u.used.count += entry->count;
554 		}
555 
556 		sem->queue.Remove(entry);
557 		entry->queued = false;
558 	}
559 
560 	RELEASE_THREAD_LOCK();
561 
562 	// select notification, if the semaphore is now acquirable
563 	if (sem->u.used.count > 0)
564 		notify_sem_select_events(sem, B_EVENT_ACQUIRE_SEMAPHORE);
565 }
566 
567 
568 /*!	This function cycles through the sem table, deleting all the sems
569 	that are owned by the specified team.
570 */
571 int
572 sem_delete_owned_sems(team_id owner)
573 {
574 	int state;
575 	int i;
576 	int count = 0;
577 
578 	// ToDo: that looks horribly inefficient - maybe it would be better
579 	//	to have them in a list in the team
580 
581 	if (owner < 0)
582 		return B_BAD_TEAM_ID;
583 
584 	state = disable_interrupts();
585 	GRAB_SEM_LIST_LOCK();
586 
587 	for (i = 0; i < sMaxSems; i++) {
588 		if (sSems[i].id != -1 && sSems[i].u.used.owner == owner) {
589 			sem_id id = sSems[i].id;
590 
591 			RELEASE_SEM_LIST_LOCK();
592 			restore_interrupts(state);
593 
594 			delete_sem(id);
595 			count++;
596 
597 			state = disable_interrupts();
598 			GRAB_SEM_LIST_LOCK();
599 		}
600 	}
601 
602 	RELEASE_SEM_LIST_LOCK();
603 	restore_interrupts(state);
604 
605 	return count;
606 }
607 
608 
609 int32
610 sem_max_sems(void)
611 {
612 	return sMaxSems;
613 }
614 
615 
616 int32
617 sem_used_sems(void)
618 {
619 	return sUsedSems;
620 }
621 
622 
623 //	#pragma mark - Public Kernel API
624 
625 
626 sem_id
627 create_sem(int32 count, const char *name)
628 {
629 	return create_sem_etc(count, name, team_get_kernel_team_id());
630 }
631 
632 
633 status_t
634 delete_sem(sem_id id)
635 {
636 	if (sSemsActive == false)
637 		return B_NO_MORE_SEMS;
638 	if (id < 0)
639 		return B_BAD_SEM_ID;
640 
641 	int32 slot = id % sMaxSems;
642 
643 	cpu_status state = disable_interrupts();
644 	GRAB_SEM_LOCK(sSems[slot]);
645 
646 	if (sSems[slot].id != id) {
647 		RELEASE_SEM_LOCK(sSems[slot]);
648 		restore_interrupts(state);
649 		TRACE(("delete_sem: invalid sem_id %ld\n", id));
650 		return B_BAD_SEM_ID;
651 	}
652 
653 	KTRACE("delete_sem(sem: %ld)", id);
654 
655 	notify_sem_select_events(&sSems[slot], B_EVENT_INVALID);
656 	sSems[slot].u.used.select_infos = NULL;
657 
658 	// free any threads waiting for this semaphore
659 	GRAB_THREAD_LOCK();
660 	while (queued_thread* entry = sSems[slot].queue.RemoveHead()) {
661 		entry->queued = false;
662 		thread_unblock_locked(entry->thread, B_BAD_SEM_ID);
663 	}
664 	RELEASE_THREAD_LOCK();
665 
666 	sSems[slot].id = -1;
667 	char *name = sSems[slot].u.used.name;
668 	sSems[slot].u.used.name = NULL;
669 
670 	RELEASE_SEM_LOCK(sSems[slot]);
671 
672 	// append slot to the free list
673 	GRAB_SEM_LIST_LOCK();
674 	free_sem_slot(slot, id + sMaxSems);
675 	atomic_add(&sUsedSems, -1);
676 	RELEASE_SEM_LIST_LOCK();
677 
678 	restore_interrupts(state);
679 
680 	free(name);
681 
682 	return B_OK;
683 }
684 
685 
686 status_t
687 acquire_sem(sem_id id)
688 {
689 	return switch_sem_etc(-1, id, 1, 0, 0);
690 }
691 
692 
693 status_t
694 acquire_sem_etc(sem_id id, int32 count, uint32 flags, bigtime_t timeout)
695 {
696 	return switch_sem_etc(-1, id, count, flags, timeout);
697 }
698 
699 
700 status_t
701 switch_sem(sem_id toBeReleased, sem_id toBeAcquired)
702 {
703 	return switch_sem_etc(toBeReleased, toBeAcquired, 1, 0, 0);
704 }
705 
706 
707 status_t
708 switch_sem_etc(sem_id semToBeReleased, sem_id id, int32 count,
709 	uint32 flags, bigtime_t timeout)
710 {
711 	int slot = id % sMaxSems;
712 	int state;
713 	status_t status = B_OK;
714 
715 	if (kernel_startup)
716 		return B_OK;
717 	if (sSemsActive == false)
718 		return B_NO_MORE_SEMS;
719 
720 	if (!are_interrupts_enabled()) {
721 		panic("switch_sem_etc: called with interrupts disabled for sem %ld\n",
722 			id);
723 	}
724 
725 	if (id < 0)
726 		return B_BAD_SEM_ID;
727 	if (count <= 0
728 		|| (flags & (B_RELATIVE_TIMEOUT | B_ABSOLUTE_TIMEOUT)) == (B_RELATIVE_TIMEOUT | B_ABSOLUTE_TIMEOUT)) {
729 		return B_BAD_VALUE;
730 	}
731 
732 	state = disable_interrupts();
733 	GRAB_SEM_LOCK(sSems[slot]);
734 
735 	if (sSems[slot].id != id) {
736 		TRACE(("switch_sem_etc: bad sem %ld\n", id));
737 		status = B_BAD_SEM_ID;
738 		goto err;
739 	}
740 
741 	// ToDo: the B_CHECK_PERMISSION flag should be made private, as it
742 	//	doesn't have any use outside the kernel
743 	if ((flags & B_CHECK_PERMISSION) != 0
744 		&& sSems[slot].u.used.owner == team_get_kernel_team_id()) {
745 		dprintf("thread %ld tried to acquire kernel semaphore %ld.\n",
746 			thread_get_current_thread_id(), id);
747 		status = B_NOT_ALLOWED;
748 		goto err;
749 	}
750 
751 	if (sSems[slot].u.used.count - count < 0) {
752 		if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0) {
753 			// immediate timeout
754 			status = B_WOULD_BLOCK;
755 			goto err;
756 		} else if ((flags & B_ABSOLUTE_TIMEOUT) != 0 && timeout < 0) {
757 			// absolute negative timeout
758 			status = B_TIMED_OUT;
759 			goto err;
760 		}
761 	}
762 
763 	KTRACE("switch_sem_etc(semToBeReleased: %ld, sem: %ld, count: %ld, "
764 		"flags: 0x%lx, timeout: %lld)", semToBeReleased, id, count, flags,
765 		timeout);
766 
767 	if ((sSems[slot].u.used.count -= count) < 0) {
768 		// we need to block
769 		struct thread *thread = thread_get_current_thread();
770 
771 		TRACE(("switch_sem_etc(id = %ld): block name = %s, thread = %p,"
772 			" name = %s\n", id, sSems[slot].u.used.name, thread, thread->name));
773 
774 		// do a quick check to see if the thread has any pending signals
775 		// this should catch most of the cases where the thread had a signal
776 		if (thread_is_interrupted(thread, flags)) {
777 			sSems[slot].u.used.count += count;
778 			status = B_INTERRUPTED;
779 				// the other semaphore will be released later
780 			goto err;
781 		}
782 
783 		if ((flags & (B_RELATIVE_TIMEOUT | B_ABSOLUTE_TIMEOUT)) == 0)
784 			timeout = B_INFINITE_TIMEOUT;
785 
786 		// enqueue in the semaphore queue and get ready to wait
787 		queued_thread queueEntry(thread, count);
788 		sSems[slot].queue.Add(&queueEntry);
789 		queueEntry.queued = true;
790 
791 		thread_prepare_to_block(thread, flags, THREAD_BLOCK_TYPE_SEMAPHORE,
792 			(void*)(addr_t)id);
793 
794 		RELEASE_SEM_LOCK(sSems[slot]);
795 
796 		// release the other semaphore, if any
797 		if (semToBeReleased >= 0) {
798 			release_sem_etc(semToBeReleased, 1, B_DO_NOT_RESCHEDULE);
799 			semToBeReleased = -1;
800 		}
801 
802 		GRAB_THREAD_LOCK();
803 
804 		status_t acquireStatus = timeout == B_INFINITE_TIMEOUT
805 			? thread_block_locked(thread)
806 			: thread_block_with_timeout_locked(flags, timeout);
807 
808 		RELEASE_THREAD_LOCK();
809 		GRAB_SEM_LOCK(sSems[slot]);
810 
811 		// If we're still queued, this means the acquiration failed, and we
812 		// need to remove our entry and (potentially) wake up other threads.
813 		if (queueEntry.queued)
814 			remove_thread_from_sem(&queueEntry, &sSems[slot]);
815 
816 		if (acquireStatus >= B_OK) {
817 			sSems[slot].u.used.last_acquirer = thread_get_current_thread_id();
818 #ifdef DEBUG_LAST_ACQUIRER
819 			sSems[slot].u.used.last_acquire_count = count;
820 #endif
821 		}
822 
823 		RELEASE_SEM_LOCK(sSems[slot]);
824 		restore_interrupts(state);
825 
826 		TRACE(("switch_sem_etc(sem %ld): exit block name %s, "
827 			"thread %ld (%s)\n", id, sSems[slot].u.used.name, thread->id,
828 			thread->name));
829 		KTRACE("switch_sem_etc() done: 0x%lx", acquireStatus);
830 		return acquireStatus;
831 	} else {
832 		sSems[slot].u.used.net_count -= count;
833 		sSems[slot].u.used.last_acquirer = thread_get_current_thread_id();
834 #ifdef DEBUG_LAST_ACQUIRER
835 		sSems[slot].u.used.last_acquire_count = count;
836 #endif
837 	}
838 
839 err:
840 	RELEASE_SEM_LOCK(sSems[slot]);
841 	restore_interrupts(state);
842 
843 	if (status == B_INTERRUPTED && semToBeReleased >= B_OK) {
844 		// depending on when we were interrupted, we need to still
845 		// release the semaphore to always leave in a consistent
846 		// state
847 		release_sem_etc(semToBeReleased, 1, B_DO_NOT_RESCHEDULE);
848 	}
849 
850 #if 0
851 	if (status == B_NOT_ALLOWED)
852 	_user_debugger("Thread tried to acquire kernel semaphore.");
853 #endif
854 
855 	KTRACE("switch_sem_etc() done: 0x%lx", status);
856 
857 	return status;
858 }
859 
860 
861 status_t
862 release_sem(sem_id id)
863 {
864 	return release_sem_etc(id, 1, 0);
865 }
866 
867 
868 status_t
869 release_sem_etc(sem_id id, int32 count, uint32 flags)
870 {
871 	int32 slot = id % sMaxSems;
872 
873 	if (kernel_startup)
874 		return B_OK;
875 	if (sSemsActive == false)
876 		return B_NO_MORE_SEMS;
877 	if (id < 0)
878 		return B_BAD_SEM_ID;
879 	if (count <= 0 && (flags & B_RELEASE_ALL) == 0)
880 		return B_BAD_VALUE;
881 
882 	InterruptsLocker _;
883 	SpinLocker semLocker(sSems[slot].lock);
884 
885 	if (sSems[slot].id != id) {
886 		TRACE(("sem_release_etc: invalid sem_id %ld\n", id));
887 		return B_BAD_SEM_ID;
888 	}
889 
890 	// ToDo: the B_CHECK_PERMISSION flag should be made private, as it
891 	//	doesn't have any use outside the kernel
892 	if ((flags & B_CHECK_PERMISSION) != 0
893 		&& sSems[slot].u.used.owner == team_get_kernel_team_id()) {
894 		dprintf("thread %ld tried to release kernel semaphore.\n",
895 			thread_get_current_thread_id());
896 		return B_NOT_ALLOWED;
897 	}
898 
899 	KTRACE("release_sem_etc(sem: %ld, count: %ld, flags: 0x%lx)", id, count,
900 		flags);
901 
902 	sSems[slot].u.used.last_acquirer = -sSems[slot].u.used.last_acquirer;
903 #ifdef DEBUG_LAST_ACQUIRER
904 	sSems[slot].u.used.last_releaser = thread_get_current_thread_id();
905 	sSems[slot].u.used.last_release_count = count;
906 #endif
907 
908 	if (flags & B_RELEASE_ALL) {
909 		count = sSems[slot].u.used.net_count - sSems[slot].u.used.count;
910 
911 		// is there anything to do for us at all?
912 		if (count == 0)
913 			return B_OK;
914 
915 		// Don't release more than necessary -- there might be interrupted/
916 		// timed out threads in the queue.
917 		flags |= B_RELEASE_IF_WAITING_ONLY;
918 	}
919 
920 	bool unblockedAny = false;
921 
922 	SpinLocker threadLocker(thread_spinlock);
923 
924 	while (count > 0) {
925 		queued_thread* entry = sSems[slot].queue.Head();
926 		if (entry == NULL) {
927 			if ((flags & B_RELEASE_IF_WAITING_ONLY) == 0) {
928 				sSems[slot].u.used.count += count;
929 				sSems[slot].u.used.net_count += count;
930 			}
931 			break;
932 		}
933 
934 		if (thread_is_blocked(entry->thread)) {
935 			// The thread is still waiting. If its count is satisfied,
936 			// unblock it. Otherwise we can't unblock any other thread.
937 			if (entry->count > sSems[slot].u.used.net_count + count) {
938 				sSems[slot].u.used.count += count;
939 				sSems[slot].u.used.net_count += count;
940 				break;
941 			}
942 
943 			thread_unblock_locked(entry->thread, B_OK);
944 
945 			int delta = min_c(count, entry->count);
946 			sSems[slot].u.used.count += delta;
947 			sSems[slot].u.used.net_count += delta - entry->count;
948 			count -= delta;
949 			unblockedAny = true;
950 		} else {
951 			// The thread is no longer waiting, but still queued, which
952 			// means acquiration failed and we can just remove it.
953 			sSems[slot].u.used.count += entry->count;
954 		}
955 
956 		sSems[slot].queue.Remove(entry);
957 		entry->queued = false;
958 	}
959 
960 	threadLocker.Unlock();
961 
962 	if (sSems[slot].u.used.count > 0)
963 		notify_sem_select_events(&sSems[slot], B_EVENT_ACQUIRE_SEMAPHORE);
964 
965 	// If we've unblocked another thread reschedule, if we've not explicitly
966 	// been told not to.
967 	if (unblockedAny && (flags & B_DO_NOT_RESCHEDULE) == 0) {
968 		semLocker.Unlock();
969 		threadLocker.Lock();
970 		scheduler_reschedule();
971 	}
972 
973 	return B_OK;
974 }
975 
976 
977 status_t
978 get_sem_count(sem_id id, int32 *_count)
979 {
980 	int slot;
981 	int state;
982 
983 	if (sSemsActive == false)
984 		return B_NO_MORE_SEMS;
985 	if (id < 0)
986 		return B_BAD_SEM_ID;
987 	if (_count == NULL)
988 		return B_BAD_VALUE;
989 
990 	slot = id % sMaxSems;
991 
992 	state = disable_interrupts();
993 	GRAB_SEM_LOCK(sSems[slot]);
994 
995 	if (sSems[slot].id != id) {
996 		RELEASE_SEM_LOCK(sSems[slot]);
997 		restore_interrupts(state);
998 		TRACE(("sem_get_count: invalid sem_id %ld\n", id));
999 		return B_BAD_SEM_ID;
1000 	}
1001 
1002 	*_count = sSems[slot].u.used.count;
1003 
1004 	RELEASE_SEM_LOCK(sSems[slot]);
1005 	restore_interrupts(state);
1006 
1007 	return B_OK;
1008 }
1009 
1010 
1011 /*!	Called by the get_sem_info() macro. */
1012 status_t
1013 _get_sem_info(sem_id id, struct sem_info *info, size_t size)
1014 {
1015 	status_t status = B_OK;
1016 	int state;
1017 	int slot;
1018 
1019 	if (!sSemsActive)
1020 		return B_NO_MORE_SEMS;
1021 	if (id < 0)
1022 		return B_BAD_SEM_ID;
1023 	if (info == NULL || size != sizeof(sem_info))
1024 		return B_BAD_VALUE;
1025 
1026 	slot = id % sMaxSems;
1027 
1028 	state = disable_interrupts();
1029 	GRAB_SEM_LOCK(sSems[slot]);
1030 
1031 	if (sSems[slot].id != id) {
1032 		status = B_BAD_SEM_ID;
1033 		TRACE(("get_sem_info: invalid sem_id %ld\n", id));
1034 	} else
1035 		fill_sem_info(&sSems[slot], info, size);
1036 
1037 	RELEASE_SEM_LOCK(sSems[slot]);
1038 	restore_interrupts(state);
1039 
1040 	return status;
1041 }
1042 
1043 
1044 /*!	Called by the get_next_sem_info() macro. */
1045 status_t
1046 _get_next_sem_info(team_id team, int32 *_cookie, struct sem_info *info,
1047 	size_t size)
1048 {
1049 	int state;
1050 	int slot;
1051 	bool found = false;
1052 
1053 	if (!sSemsActive)
1054 		return B_NO_MORE_SEMS;
1055 	if (_cookie == NULL || info == NULL || size != sizeof(sem_info))
1056 		return B_BAD_VALUE;
1057 
1058 	if (team == B_CURRENT_TEAM)
1059 		team = team_get_current_team_id();
1060 	/* prevents sSems[].owner == -1 >= means owned by a port */
1061 	if (team < 0 || !team_is_valid(team))
1062 		return B_BAD_TEAM_ID;
1063 
1064 	slot = *_cookie;
1065 	if (slot >= sMaxSems)
1066 		return B_BAD_VALUE;
1067 
1068 	state = disable_interrupts();
1069 	GRAB_SEM_LIST_LOCK();
1070 
1071 	while (slot < sMaxSems) {
1072 		if (sSems[slot].id != -1 && sSems[slot].u.used.owner == team) {
1073 			GRAB_SEM_LOCK(sSems[slot]);
1074 			if (sSems[slot].id != -1 && sSems[slot].u.used.owner == team) {
1075 				// found one!
1076 				fill_sem_info(&sSems[slot], info, size);
1077 
1078 				RELEASE_SEM_LOCK(sSems[slot]);
1079 				slot++;
1080 				found = true;
1081 				break;
1082 			}
1083 			RELEASE_SEM_LOCK(sSems[slot]);
1084 		}
1085 		slot++;
1086 	}
1087 	RELEASE_SEM_LIST_LOCK();
1088 	restore_interrupts(state);
1089 
1090 	if (!found)
1091 		return B_BAD_VALUE;
1092 
1093 	*_cookie = slot;
1094 	return B_OK;
1095 }
1096 
1097 
1098 status_t
1099 set_sem_owner(sem_id id, team_id team)
1100 {
1101 	int state;
1102 	int slot;
1103 
1104 	if (sSemsActive == false)
1105 		return B_NO_MORE_SEMS;
1106 	if (id < 0)
1107 		return B_BAD_SEM_ID;
1108 	if (team < 0 || !team_is_valid(team))
1109 		return B_BAD_TEAM_ID;
1110 
1111 	slot = id % sMaxSems;
1112 
1113 	state = disable_interrupts();
1114 	GRAB_SEM_LOCK(sSems[slot]);
1115 
1116 	if (sSems[slot].id != id) {
1117 		RELEASE_SEM_LOCK(sSems[slot]);
1118 		restore_interrupts(state);
1119 		TRACE(("set_sem_owner: invalid sem_id %ld\n", id));
1120 		return B_BAD_SEM_ID;
1121 	}
1122 
1123 	// ToDo: this is a small race condition: the team ID could already
1124 	// be invalid at this point - we would lose one semaphore slot in
1125 	// this case!
1126 	// The only safe way to do this is to prevent either team (the new
1127 	// or the old owner) from dying until we leave the spinlock.
1128 	sSems[slot].u.used.owner = team;
1129 
1130 	RELEASE_SEM_LOCK(sSems[slot]);
1131 	restore_interrupts(state);
1132 
1133 	return B_NO_ERROR;
1134 }
1135 
1136 
1137 //	#pragma mark - Syscalls
1138 
1139 
1140 sem_id
1141 _user_create_sem(int32 count, const char *userName)
1142 {
1143 	char name[B_OS_NAME_LENGTH];
1144 
1145 	if (userName == NULL)
1146 		return create_sem_etc(count, NULL, team_get_current_team_id());
1147 
1148 	if (!IS_USER_ADDRESS(userName)
1149 		|| user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK)
1150 		return B_BAD_ADDRESS;
1151 
1152 	return create_sem_etc(count, name, team_get_current_team_id());
1153 }
1154 
1155 
1156 status_t
1157 _user_delete_sem(sem_id id)
1158 {
1159 	return delete_sem(id);
1160 }
1161 
1162 
1163 status_t
1164 _user_acquire_sem(sem_id id)
1165 {
1166 	status_t error = switch_sem_etc(-1, id, 1,
1167 		B_CAN_INTERRUPT | B_CHECK_PERMISSION, 0);
1168 
1169 	return syscall_restart_handle_post(error);
1170 }
1171 
1172 
1173 status_t
1174 _user_acquire_sem_etc(sem_id id, int32 count, uint32 flags, bigtime_t timeout)
1175 {
1176 	syscall_restart_handle_timeout_pre(flags, timeout);
1177 
1178 	status_t error = switch_sem_etc(-1, id, count,
1179 		flags | B_CAN_INTERRUPT | B_CHECK_PERMISSION, timeout);
1180 
1181 	return syscall_restart_handle_timeout_post(error, timeout);
1182 }
1183 
1184 
1185 status_t
1186 _user_switch_sem(sem_id releaseSem, sem_id id)
1187 {
1188 	status_t error = switch_sem_etc(releaseSem, id, 1,
1189 		B_CAN_INTERRUPT | B_CHECK_PERMISSION, 0);
1190 
1191 	if (releaseSem < 0)
1192 		return syscall_restart_handle_post(error);
1193 
1194 	return error;
1195 }
1196 
1197 
1198 status_t
1199 _user_switch_sem_etc(sem_id releaseSem, sem_id id, int32 count, uint32 flags,
1200 	bigtime_t timeout)
1201 {
1202 	if (releaseSem < 0)
1203 		syscall_restart_handle_timeout_pre(flags, timeout);
1204 
1205 	status_t error = switch_sem_etc(releaseSem, id, count,
1206 		flags | B_CAN_INTERRUPT | B_CHECK_PERMISSION, timeout);
1207 
1208 	if (releaseSem < 0)
1209 		return syscall_restart_handle_timeout_post(error, timeout);
1210 
1211 	return error;
1212 }
1213 
1214 
1215 status_t
1216 _user_release_sem(sem_id id)
1217 {
1218 	return release_sem_etc(id, 1, B_CHECK_PERMISSION);
1219 }
1220 
1221 
1222 status_t
1223 _user_release_sem_etc(sem_id id, int32 count, uint32 flags)
1224 {
1225 	return release_sem_etc(id, count, flags | B_CHECK_PERMISSION);
1226 }
1227 
1228 
1229 status_t
1230 _user_get_sem_count(sem_id id, int32 *userCount)
1231 {
1232 	status_t status;
1233 	int32 count;
1234 
1235 	if (userCount == NULL || !IS_USER_ADDRESS(userCount))
1236 		return B_BAD_ADDRESS;
1237 
1238 	status = get_sem_count(id, &count);
1239 	if (status == B_OK && user_memcpy(userCount, &count, sizeof(int32)) < B_OK)
1240 		return B_BAD_ADDRESS;
1241 
1242 	return status;
1243 }
1244 
1245 
1246 status_t
1247 _user_get_sem_info(sem_id id, struct sem_info *userInfo, size_t size)
1248 {
1249 	struct sem_info info;
1250 	status_t status;
1251 
1252 	if (userInfo == NULL || !IS_USER_ADDRESS(userInfo))
1253 		return B_BAD_ADDRESS;
1254 
1255 	status = _get_sem_info(id, &info, size);
1256 	if (status == B_OK && user_memcpy(userInfo, &info, size) < B_OK)
1257 		return B_BAD_ADDRESS;
1258 
1259 	return status;
1260 }
1261 
1262 
1263 status_t
1264 _user_get_next_sem_info(team_id team, int32 *userCookie, struct sem_info *userInfo,
1265 	size_t size)
1266 {
1267 	struct sem_info info;
1268 	int32 cookie;
1269 	status_t status;
1270 
1271 	if (userCookie == NULL || userInfo == NULL
1272 		|| !IS_USER_ADDRESS(userCookie) || !IS_USER_ADDRESS(userInfo)
1273 		|| user_memcpy(&cookie, userCookie, sizeof(int32)) < B_OK)
1274 		return B_BAD_ADDRESS;
1275 
1276 	status = _get_next_sem_info(team, &cookie, &info, size);
1277 
1278 	if (status == B_OK) {
1279 		if (user_memcpy(userInfo, &info, size) < B_OK
1280 			|| user_memcpy(userCookie, &cookie, sizeof(int32)) < B_OK)
1281 			return B_BAD_ADDRESS;
1282 	}
1283 
1284 	return status;
1285 }
1286 
1287 
1288 status_t
1289 _user_set_sem_owner(sem_id id, team_id team)
1290 {
1291 	return set_sem_owner(id, team);
1292 }
1293