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