xref: /haiku/src/system/kernel/sem.cpp (revision 1f0635d2277dcd0818dc7f539c1cb1b296f6444b)
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 	while (queued_thread* entry = sem.queue.RemoveHead()) {
334 		entry->queued = false;
335 		thread_unblock(entry->thread, B_BAD_SEM_ID);
336 	}
337 
338 	int32 id = sem.id;
339 	sem.id = -1;
340 	*_name = sem.u.used.name;
341 	sem.u.used.name = NULL;
342 
343 	RELEASE_SEM_LOCK(sem);
344 
345 	// append slot to the free list
346 	GRAB_SEM_LIST_LOCK();
347 	free_sem_slot(id % sMaxSems, id + sMaxSems);
348 	atomic_add(&sUsedSems, -1);
349 	RELEASE_SEM_LIST_LOCK();
350 }
351 
352 
353 static status_t
354 delete_sem_internal(sem_id id, bool checkPermission)
355 {
356 	if (sSemsActive == false)
357 		return B_NO_MORE_SEMS;
358 	if (id < 0)
359 		return B_BAD_SEM_ID;
360 
361 	int32 slot = id % sMaxSems;
362 
363 	cpu_status state = disable_interrupts();
364 	GRAB_SEM_LIST_LOCK();
365 	GRAB_SEM_LOCK(sSems[slot]);
366 
367 	if (sSems[slot].id != id) {
368 		RELEASE_SEM_LOCK(sSems[slot]);
369 		RELEASE_SEM_LIST_LOCK();
370 		restore_interrupts(state);
371 		TRACE(("delete_sem: invalid sem_id %ld\n", id));
372 		return B_BAD_SEM_ID;
373 	}
374 
375 	if (checkPermission
376 		&& sSems[slot].u.used.owner == team_get_kernel_team_id()) {
377 		RELEASE_SEM_LOCK(sSems[slot]);
378 		RELEASE_SEM_LIST_LOCK();
379 		restore_interrupts(state);
380 		dprintf("thread %" B_PRId32 " tried to delete kernel semaphore "
381 			"%" B_PRId32 ".\n", thread_get_current_thread_id(), id);
382 		return B_NOT_ALLOWED;
383 	}
384 
385 	if (sSems[slot].u.used.owner >= 0) {
386 		list_remove_link(&sSems[slot].u.used.team_link);
387 		sSems[slot].u.used.owner = -1;
388 	} else
389 		panic("sem %" B_PRId32 " has no owner", id);
390 
391 	RELEASE_SEM_LIST_LOCK();
392 
393 	char* name;
394 	uninit_sem_locked(sSems[slot], &name);
395 
396 	SpinLocker schedulerLocker(thread_get_current_thread()->scheduler_lock);
397 	scheduler_reschedule_if_necessary_locked();
398 	schedulerLocker.Unlock();
399 
400 	restore_interrupts(state);
401 
402 	free(name);
403 	return B_OK;
404 }
405 
406 
407 //	#pragma mark - Private Kernel API
408 
409 
410 // TODO: Name clash with POSIX sem_init()... (we could just use C++)
411 status_t
412 haiku_sem_init(kernel_args *args)
413 {
414 	area_id area;
415 	int32 i;
416 
417 	TRACE(("sem_init: entry\n"));
418 
419 	// compute maximal number of semaphores depending on the available memory
420 	// 128 MB -> 16384 semaphores, 448 kB fixed array size
421 	// 256 MB -> 32768, 896 kB
422 	// 512 MB and more-> 65536, 1.75 MB
423 	i = vm_page_num_pages() / 2;
424 	while (sMaxSems < i && sMaxSems < kMaxSemaphores)
425 		sMaxSems <<= 1;
426 
427 	// create and initialize semaphore table
428 	virtual_address_restrictions virtualRestrictions = {};
429 	virtualRestrictions.address_specification = B_ANY_KERNEL_ADDRESS;
430 	physical_address_restrictions physicalRestrictions = {};
431 	area = create_area_etc(B_SYSTEM_TEAM, "sem_table",
432 		sizeof(struct sem_entry) * sMaxSems, B_FULL_LOCK,
433 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, CREATE_AREA_DONT_WAIT, 0,
434 		&virtualRestrictions, &physicalRestrictions, (void**)&sSems);
435 	if (area < 0)
436 		panic("unable to allocate semaphore table!\n");
437 
438 	memset(sSems, 0, sizeof(struct sem_entry) * sMaxSems);
439 	for (i = 0; i < sMaxSems; i++) {
440 		sSems[i].id = -1;
441 		free_sem_slot(i, i);
442 	}
443 
444 	// add debugger commands
445 	add_debugger_command_etc("sems", &dump_sem_list,
446 		"Dump a list of all active semaphores (for team, with name, etc.)",
447 		"[ ([ \"team\" | \"owner\" ] <team>) | (\"name\" <name>) ]"
448 			" | (\"last\" <last acquirer>)\n"
449 		"Prints a list of all active semaphores meeting the given\n"
450 		"requirement. If no argument is given, all sems are listed.\n"
451 		"  <team>             - The team owning the semaphores.\n"
452 		"  <name>             - Part of the name of the semaphores.\n"
453 		"  <last acquirer>    - The thread that last acquired the semaphore.\n"
454 		, 0);
455 	add_debugger_command_etc("sem", &dump_sem_info,
456 		"Dump info about a particular semaphore",
457 		"<sem>\n"
458 		"Prints info about the specified semaphore.\n"
459 		"  <sem>  - pointer to the semaphore structure, semaphore ID, or name\n"
460 		"           of the semaphore to print info for.\n", 0);
461 
462 	TRACE(("sem_init: exit\n"));
463 
464 	sSemsActive = true;
465 
466 	return 0;
467 }
468 
469 
470 /*!	Creates a semaphore with the given parameters.
471 
472 	This function is only available from within the kernel, and
473 	should not be made public - if possible, we should remove it
474 	completely (and have only create_sem() exported).
475 */
476 sem_id
477 create_sem_etc(int32 count, const char* name, team_id owner)
478 {
479 	struct sem_entry* sem = NULL;
480 	cpu_status state;
481 	sem_id id = B_NO_MORE_SEMS;
482 	char* tempName;
483 	size_t nameLength;
484 
485 	if (sSemsActive == false || sUsedSems == sMaxSems)
486 		return B_NO_MORE_SEMS;
487 
488 	if (name == NULL)
489 		name = "unnamed semaphore";
490 
491 	// get the owning team
492 	Team* team = Team::Get(owner);
493 	if (team == NULL)
494 		return B_BAD_TEAM_ID;
495 	BReference<Team> teamReference(team, true);
496 
497 	// clone the name
498 	nameLength = strlen(name) + 1;
499 	nameLength = min_c(nameLength, B_OS_NAME_LENGTH);
500 	tempName = (char*)malloc(nameLength);
501 	if (tempName == NULL)
502 		return B_NO_MEMORY;
503 
504 	strlcpy(tempName, name, nameLength);
505 
506 	state = disable_interrupts();
507 	GRAB_SEM_LIST_LOCK();
508 
509 	// get the first slot from the free list
510 	sem = sFreeSemsHead;
511 	if (sem) {
512 		// remove it from the free list
513 		sFreeSemsHead = sem->u.unused.next;
514 		if (!sFreeSemsHead)
515 			sFreeSemsTail = NULL;
516 
517 		// init the slot
518 		GRAB_SEM_LOCK(*sem);
519 		sem->id = sem->u.unused.next_id;
520 		sem->u.used.count = count;
521 		sem->u.used.net_count = count;
522 		new(&sem->queue) ThreadQueue;
523 		sem->u.used.name = tempName;
524 		sem->u.used.owner = team->id;
525 		sem->u.used.select_infos = NULL;
526 		id = sem->id;
527 
528 		list_add_item(&team->sem_list, &sem->u.used.team_link);
529 
530 		RELEASE_SEM_LOCK(*sem);
531 
532 		atomic_add(&sUsedSems, 1);
533 
534 		KTRACE("create_sem_etc(count: %ld, name: %s, owner: %ld) -> %ld",
535 			count, name, owner, id);
536 
537 		T_SCHEDULING_ANALYSIS(CreateSemaphore(id, name));
538 		NotifyWaitObjectListeners(&WaitObjectListener::SemaphoreCreated, id,
539 			name);
540 	}
541 
542 	RELEASE_SEM_LIST_LOCK();
543 	restore_interrupts(state);
544 
545 	if (sem == NULL)
546 		free(tempName);
547 
548 	return id;
549 }
550 
551 
552 status_t
553 select_sem(int32 id, struct select_info* info, bool kernel)
554 {
555 	cpu_status state;
556 	int32 slot;
557 	status_t error = B_OK;
558 
559 	if (id < 0)
560 		return B_BAD_SEM_ID;
561 
562 	slot = id % sMaxSems;
563 
564 	state = disable_interrupts();
565 	GRAB_SEM_LOCK(sSems[slot]);
566 
567 	if (sSems[slot].id != id) {
568 		// bad sem ID
569 		error = B_BAD_SEM_ID;
570 	} else if (!kernel
571 		&& sSems[slot].u.used.owner == team_get_kernel_team_id()) {
572 		// kernel semaphore, but call from userland
573 		error = B_NOT_ALLOWED;
574 	} else {
575 		info->selected_events &= B_EVENT_ACQUIRE_SEMAPHORE | B_EVENT_INVALID;
576 
577 		if (info->selected_events != 0) {
578 			info->next = sSems[slot].u.used.select_infos;
579 			sSems[slot].u.used.select_infos = info;
580 
581 			if (sSems[slot].u.used.count > 0)
582 				notify_select_events(info, B_EVENT_ACQUIRE_SEMAPHORE);
583 		}
584 	}
585 
586 	RELEASE_SEM_LOCK(sSems[slot]);
587 	restore_interrupts(state);
588 
589 	return error;
590 }
591 
592 
593 status_t
594 deselect_sem(int32 id, struct select_info* info, bool kernel)
595 {
596 	cpu_status state;
597 	int32 slot;
598 
599 	if (id < 0)
600 		return B_BAD_SEM_ID;
601 
602 	if (info->selected_events == 0)
603 		return B_OK;
604 
605 	slot = id % sMaxSems;
606 
607 	state = disable_interrupts();
608 	GRAB_SEM_LOCK(sSems[slot]);
609 
610 	if (sSems[slot].id == id) {
611 		select_info** infoLocation = &sSems[slot].u.used.select_infos;
612 		while (*infoLocation != NULL && *infoLocation != info)
613 			infoLocation = &(*infoLocation)->next;
614 
615 		if (*infoLocation == info)
616 			*infoLocation = info->next;
617 	}
618 
619 	RELEASE_SEM_LOCK(sSems[slot]);
620 	restore_interrupts(state);
621 
622 	return B_OK;
623 }
624 
625 
626 /*!	Forcibly removes a thread from a semaphores wait queue. May have to wake up
627 	other threads in the process.
628 	Must be called with semaphore lock held. The thread lock must not be held.
629 */
630 static void
631 remove_thread_from_sem(queued_thread *entry, struct sem_entry *sem)
632 {
633 	if (!entry->queued)
634 		return;
635 
636 	sem->queue.Remove(entry);
637 	entry->queued = false;
638 	sem->u.used.count += entry->count;
639 
640 	// We're done with this entry. We only have to check, if other threads
641 	// need unblocking, too.
642 
643 	// Now see if more threads need to be woken up. We get the scheduler lock
644 	// for that time, so the blocking state of threads won't change (due to
645 	// interruption or timeout). We need that lock anyway when unblocking a
646 	// thread.
647 	while ((entry = sem->queue.Head()) != NULL) {
648 		SpinLocker schedulerLocker(entry->thread->scheduler_lock);
649 		if (thread_is_blocked(entry->thread)) {
650 			// The thread is still waiting. If its count is satisfied, unblock
651 			// it. Otherwise we can't unblock any other thread.
652 			if (entry->count > sem->u.used.net_count)
653 				break;
654 
655 			thread_unblock_locked(entry->thread, B_OK);
656 			sem->u.used.net_count -= entry->count;
657 		} else {
658 			// The thread is no longer waiting, but still queued, which means
659 			// acquiration failed and we can just remove it.
660 			sem->u.used.count += entry->count;
661 		}
662 
663 		sem->queue.Remove(entry);
664 		entry->queued = false;
665 	}
666 
667 	// select notification, if the semaphore is now acquirable
668 	if (sem->u.used.count > 0)
669 		notify_sem_select_events(sem, B_EVENT_ACQUIRE_SEMAPHORE);
670 }
671 
672 
673 /*!	This function deletes all semaphores belonging to a particular team.
674 */
675 void
676 sem_delete_owned_sems(Team* team)
677 {
678 	while (true) {
679 		char* name;
680 
681 		{
682 			// get the next semaphore from the team's sem list
683 			InterruptsLocker locker;
684 			SpinLocker semListLocker(sSemsSpinlock);
685 			sem_entry* sem = (sem_entry*)list_remove_head_item(&team->sem_list);
686 			if (sem == NULL)
687 				break;
688 
689 			// delete the semaphore
690 			GRAB_SEM_LOCK(*sem);
691 			semListLocker.Unlock();
692 			uninit_sem_locked(*sem, &name);
693 		}
694 
695 		free(name);
696 	}
697 
698 	scheduler_reschedule_if_necessary();
699 }
700 
701 
702 int32
703 sem_max_sems(void)
704 {
705 	return sMaxSems;
706 }
707 
708 
709 int32
710 sem_used_sems(void)
711 {
712 	return sUsedSems;
713 }
714 
715 
716 //	#pragma mark - Public Kernel API
717 
718 
719 sem_id
720 create_sem(int32 count, const char* name)
721 {
722 	return create_sem_etc(count, name, team_get_kernel_team_id());
723 }
724 
725 
726 status_t
727 delete_sem(sem_id id)
728 {
729 	return delete_sem_internal(id, false);
730 }
731 
732 
733 status_t
734 acquire_sem(sem_id id)
735 {
736 	return switch_sem_etc(-1, id, 1, 0, 0);
737 }
738 
739 
740 status_t
741 acquire_sem_etc(sem_id id, int32 count, uint32 flags, bigtime_t timeout)
742 {
743 	return switch_sem_etc(-1, id, count, flags, timeout);
744 }
745 
746 
747 status_t
748 switch_sem(sem_id toBeReleased, sem_id toBeAcquired)
749 {
750 	return switch_sem_etc(toBeReleased, toBeAcquired, 1, 0, 0);
751 }
752 
753 
754 status_t
755 switch_sem_etc(sem_id semToBeReleased, sem_id id, int32 count,
756 	uint32 flags, bigtime_t timeout)
757 {
758 	int slot = id % sMaxSems;
759 	int state;
760 	status_t status = B_OK;
761 
762 	if (gKernelStartup)
763 		return B_OK;
764 	if (sSemsActive == false)
765 		return B_NO_MORE_SEMS;
766 #if KDEBUG
767 	if (!are_interrupts_enabled()) {
768 		panic("switch_sem_etc: called with interrupts disabled for sem "
769 			"%" B_PRId32 "\n", id);
770 	}
771 #endif
772 
773 	if (id < 0)
774 		return B_BAD_SEM_ID;
775 	if (count <= 0
776 		|| (flags & (B_RELATIVE_TIMEOUT | B_ABSOLUTE_TIMEOUT)) == (B_RELATIVE_TIMEOUT | B_ABSOLUTE_TIMEOUT)) {
777 		return B_BAD_VALUE;
778 	}
779 
780 	state = disable_interrupts();
781 	GRAB_SEM_LOCK(sSems[slot]);
782 
783 	if (sSems[slot].id != id) {
784 		TRACE(("switch_sem_etc: bad sem %ld\n", id));
785 		status = B_BAD_SEM_ID;
786 		goto err;
787 	}
788 
789 	// TODO: the B_CHECK_PERMISSION flag should be made private, as it
790 	//	doesn't have any use outside the kernel
791 	if ((flags & B_CHECK_PERMISSION) != 0
792 		&& sSems[slot].u.used.owner == team_get_kernel_team_id()) {
793 		dprintf("thread %" B_PRId32 " tried to acquire kernel semaphore "
794 			"%" B_PRId32 ".\n", thread_get_current_thread_id(), id);
795 		status = B_NOT_ALLOWED;
796 		goto err;
797 	}
798 
799 	if (sSems[slot].u.used.count - count < 0) {
800 		if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0) {
801 			// immediate timeout
802 			status = B_WOULD_BLOCK;
803 			goto err;
804 		} else if ((flags & B_ABSOLUTE_TIMEOUT) != 0 && timeout < 0) {
805 			// absolute negative timeout
806 			status = B_TIMED_OUT;
807 			goto err;
808 		}
809 	}
810 
811 	KTRACE("switch_sem_etc(semToBeReleased: %ld, sem: %ld, count: %ld, "
812 		"flags: 0x%lx, timeout: %lld)", semToBeReleased, id, count, flags,
813 		timeout);
814 
815 	if ((sSems[slot].u.used.count -= count) < 0) {
816 		// we need to block
817 		Thread *thread = thread_get_current_thread();
818 
819 		TRACE(("switch_sem_etc(id = %ld): block name = %s, thread = %p,"
820 			" name = %s\n", id, sSems[slot].u.used.name, thread, thread->name));
821 
822 		// do a quick check to see if the thread has any pending signals
823 		// this should catch most of the cases where the thread had a signal
824 		SpinLocker schedulerLocker(thread->scheduler_lock);
825 		if (thread_is_interrupted(thread, flags)) {
826 			schedulerLocker.Unlock();
827 			sSems[slot].u.used.count += count;
828 			status = B_INTERRUPTED;
829 				// the other semaphore will be released later
830 			goto err;
831 		}
832 
833 		schedulerLocker.Unlock();
834 
835 		if ((flags & (B_RELATIVE_TIMEOUT | B_ABSOLUTE_TIMEOUT)) == 0)
836 			timeout = B_INFINITE_TIMEOUT;
837 
838 		// enqueue in the semaphore queue and get ready to wait
839 		queued_thread queueEntry(thread, count);
840 		sSems[slot].queue.Add(&queueEntry);
841 		queueEntry.queued = true;
842 
843 		thread_prepare_to_block(thread, flags, THREAD_BLOCK_TYPE_SEMAPHORE,
844 			(void*)(addr_t)id);
845 
846 		RELEASE_SEM_LOCK(sSems[slot]);
847 
848 		// release the other semaphore, if any
849 		if (semToBeReleased >= 0) {
850 			release_sem_etc(semToBeReleased, 1, B_DO_NOT_RESCHEDULE);
851 			semToBeReleased = -1;
852 		}
853 
854 		status_t acquireStatus = timeout == B_INFINITE_TIMEOUT
855 			? thread_block() : thread_block_with_timeout(flags, timeout);
856 
857 		GRAB_SEM_LOCK(sSems[slot]);
858 
859 		// If we're still queued, this means the acquiration failed, and we
860 		// need to remove our entry and (potentially) wake up other threads.
861 		if (queueEntry.queued)
862 			remove_thread_from_sem(&queueEntry, &sSems[slot]);
863 
864 		if (acquireStatus >= B_OK) {
865 			sSems[slot].u.used.last_acquirer = thread_get_current_thread_id();
866 #if DEBUG_SEM_LAST_ACQUIRER
867 			sSems[slot].u.used.last_acquire_count = count;
868 #endif
869 		}
870 
871 		RELEASE_SEM_LOCK(sSems[slot]);
872 		restore_interrupts(state);
873 
874 		TRACE(("switch_sem_etc(sem %ld): exit block name %s, "
875 			"thread %ld (%s)\n", id, sSems[slot].u.used.name, thread->id,
876 			thread->name));
877 		KTRACE("switch_sem_etc() done: 0x%lx", acquireStatus);
878 		return acquireStatus;
879 	} else {
880 		sSems[slot].u.used.net_count -= count;
881 		sSems[slot].u.used.last_acquirer = thread_get_current_thread_id();
882 #if DEBUG_SEM_LAST_ACQUIRER
883 		sSems[slot].u.used.last_acquire_count = count;
884 #endif
885 	}
886 
887 err:
888 	RELEASE_SEM_LOCK(sSems[slot]);
889 	restore_interrupts(state);
890 
891 	if (status == B_INTERRUPTED && semToBeReleased >= B_OK) {
892 		// depending on when we were interrupted, we need to still
893 		// release the semaphore to always leave in a consistent
894 		// state
895 		release_sem_etc(semToBeReleased, 1, B_DO_NOT_RESCHEDULE);
896 	}
897 
898 #if 0
899 	if (status == B_NOT_ALLOWED)
900 	_user_debugger("Thread tried to acquire kernel semaphore.");
901 #endif
902 
903 	KTRACE("switch_sem_etc() done: 0x%lx", status);
904 
905 	return status;
906 }
907 
908 
909 status_t
910 release_sem(sem_id id)
911 {
912 	return release_sem_etc(id, 1, 0);
913 }
914 
915 
916 status_t
917 release_sem_etc(sem_id id, int32 count, uint32 flags)
918 {
919 	int32 slot = id % sMaxSems;
920 
921 	if (gKernelStartup)
922 		return B_OK;
923 	if (sSemsActive == false)
924 		return B_NO_MORE_SEMS;
925 	if (id < 0)
926 		return B_BAD_SEM_ID;
927 	if (count <= 0 && (flags & B_RELEASE_ALL) == 0)
928 		return B_BAD_VALUE;
929 #if KDEBUG
930 	if ((flags & B_DO_NOT_RESCHEDULE) == 0 && !are_interrupts_enabled()) {
931 		panic("release_sem_etc(): called with interrupts disabled and "
932 			"rescheduling allowed for sem_id %" B_PRId32, id);
933 	}
934 #endif
935 
936 	InterruptsLocker _;
937 	SpinLocker semLocker(sSems[slot].lock);
938 
939 	if (sSems[slot].id != id) {
940 		TRACE(("sem_release_etc: invalid sem_id %ld\n", id));
941 		return B_BAD_SEM_ID;
942 	}
943 
944 	// ToDo: the B_CHECK_PERMISSION flag should be made private, as it
945 	//	doesn't have any use outside the kernel
946 	if ((flags & B_CHECK_PERMISSION) != 0
947 		&& sSems[slot].u.used.owner == team_get_kernel_team_id()) {
948 		dprintf("thread %" B_PRId32 " tried to release kernel semaphore.\n",
949 			thread_get_current_thread_id());
950 		return B_NOT_ALLOWED;
951 	}
952 
953 	KTRACE("release_sem_etc(sem: %ld, count: %ld, flags: 0x%lx)", id, count,
954 		flags);
955 
956 	sSems[slot].u.used.last_acquirer = -sSems[slot].u.used.last_acquirer;
957 #if DEBUG_SEM_LAST_ACQUIRER
958 	sSems[slot].u.used.last_releaser = thread_get_current_thread_id();
959 	sSems[slot].u.used.last_release_count = count;
960 #endif
961 
962 	if (flags & B_RELEASE_ALL) {
963 		count = sSems[slot].u.used.net_count - sSems[slot].u.used.count;
964 
965 		// is there anything to do for us at all?
966 		if (count == 0)
967 			return B_OK;
968 
969 		// Don't release more than necessary -- there might be interrupted/
970 		// timed out threads in the queue.
971 		flags |= B_RELEASE_IF_WAITING_ONLY;
972 	}
973 
974 	// Grab the scheduler lock, so thread_is_blocked() is reliable (due to
975 	// possible interruptions or timeouts, it wouldn't be otherwise).
976 	while (count > 0) {
977 		queued_thread* entry = sSems[slot].queue.Head();
978 		if (entry == NULL) {
979 			if ((flags & B_RELEASE_IF_WAITING_ONLY) == 0) {
980 				sSems[slot].u.used.count += count;
981 				sSems[slot].u.used.net_count += count;
982 			}
983 			break;
984 		}
985 
986 		SpinLocker schedulerLock(entry->thread->scheduler_lock);
987 		if (thread_is_blocked(entry->thread)) {
988 			// The thread is still waiting. If its count is satisfied,
989 			// unblock it. Otherwise we can't unblock any other thread.
990 			if (entry->count > sSems[slot].u.used.net_count + count) {
991 				sSems[slot].u.used.count += count;
992 				sSems[slot].u.used.net_count += count;
993 				break;
994 			}
995 
996 			thread_unblock_locked(entry->thread, B_OK);
997 
998 			int delta = min_c(count, entry->count);
999 			sSems[slot].u.used.count += delta;
1000 			sSems[slot].u.used.net_count += delta - entry->count;
1001 			count -= delta;
1002 		} else {
1003 			// The thread is no longer waiting, but still queued, which
1004 			// means acquiration failed and we can just remove it.
1005 			sSems[slot].u.used.count += entry->count;
1006 		}
1007 
1008 		sSems[slot].queue.Remove(entry);
1009 		entry->queued = false;
1010 	}
1011 
1012 	if (sSems[slot].u.used.count > 0)
1013 		notify_sem_select_events(&sSems[slot], B_EVENT_ACQUIRE_SEMAPHORE);
1014 
1015 	// If we've unblocked another thread reschedule, if we've not explicitly
1016 	// been told not to.
1017 	if ((flags & B_DO_NOT_RESCHEDULE) == 0) {
1018 		semLocker.Unlock();
1019 
1020 		SpinLocker _(thread_get_current_thread()->scheduler_lock);
1021 		scheduler_reschedule_if_necessary_locked();
1022 	}
1023 
1024 	return B_OK;
1025 }
1026 
1027 
1028 status_t
1029 get_sem_count(sem_id id, int32 *_count)
1030 {
1031 	int slot;
1032 	int state;
1033 
1034 	if (sSemsActive == false)
1035 		return B_NO_MORE_SEMS;
1036 	if (id < 0)
1037 		return B_BAD_SEM_ID;
1038 	if (_count == NULL)
1039 		return B_BAD_VALUE;
1040 
1041 	slot = id % sMaxSems;
1042 
1043 	state = disable_interrupts();
1044 	GRAB_SEM_LOCK(sSems[slot]);
1045 
1046 	if (sSems[slot].id != id) {
1047 		RELEASE_SEM_LOCK(sSems[slot]);
1048 		restore_interrupts(state);
1049 		TRACE(("sem_get_count: invalid sem_id %ld\n", id));
1050 		return B_BAD_SEM_ID;
1051 	}
1052 
1053 	*_count = sSems[slot].u.used.count;
1054 
1055 	RELEASE_SEM_LOCK(sSems[slot]);
1056 	restore_interrupts(state);
1057 
1058 	return B_OK;
1059 }
1060 
1061 
1062 /*!	Called by the get_sem_info() macro. */
1063 status_t
1064 _get_sem_info(sem_id id, struct sem_info *info, size_t size)
1065 {
1066 	status_t status = B_OK;
1067 	int state;
1068 	int slot;
1069 
1070 	if (!sSemsActive)
1071 		return B_NO_MORE_SEMS;
1072 	if (id < 0)
1073 		return B_BAD_SEM_ID;
1074 	if (info == NULL || size != sizeof(sem_info))
1075 		return B_BAD_VALUE;
1076 
1077 	slot = id % sMaxSems;
1078 
1079 	state = disable_interrupts();
1080 	GRAB_SEM_LOCK(sSems[slot]);
1081 
1082 	if (sSems[slot].id != id) {
1083 		status = B_BAD_SEM_ID;
1084 		TRACE(("get_sem_info: invalid sem_id %ld\n", id));
1085 	} else
1086 		fill_sem_info(&sSems[slot], info, size);
1087 
1088 	RELEASE_SEM_LOCK(sSems[slot]);
1089 	restore_interrupts(state);
1090 
1091 	return status;
1092 }
1093 
1094 
1095 /*!	Called by the get_next_sem_info() macro. */
1096 status_t
1097 _get_next_sem_info(team_id teamID, int32 *_cookie, struct sem_info *info,
1098 	size_t size)
1099 {
1100 	if (!sSemsActive)
1101 		return B_NO_MORE_SEMS;
1102 	if (_cookie == NULL || info == NULL || size != sizeof(sem_info))
1103 		return B_BAD_VALUE;
1104 	if (teamID < 0)
1105 		return B_BAD_TEAM_ID;
1106 
1107 	Team* team = Team::Get(teamID);
1108 	if (team == NULL)
1109 		return B_BAD_TEAM_ID;
1110 	BReference<Team> teamReference(team, true);
1111 
1112 	InterruptsSpinLocker semListLocker(sSemsSpinlock);
1113 
1114 	// TODO: find a way to iterate the list that is more reliable
1115 	sem_entry* sem = (sem_entry*)list_get_first_item(&team->sem_list);
1116 	int32 newIndex = *_cookie;
1117 	int32 index = 0;
1118 	bool found = false;
1119 
1120 	while (!found) {
1121 		// find the next entry to be returned
1122 		while (sem != NULL && index < newIndex) {
1123 			sem = (sem_entry*)list_get_next_item(&team->sem_list, sem);
1124 			index++;
1125 		}
1126 
1127 		if (sem == NULL)
1128 			return B_BAD_VALUE;
1129 
1130 		GRAB_SEM_LOCK(*sem);
1131 
1132 		if (sem->id != -1 && sem->u.used.owner == team->id) {
1133 			// found one!
1134 			fill_sem_info(sem, info, size);
1135 			newIndex = index + 1;
1136 			found = true;
1137 		} else
1138 			newIndex++;
1139 
1140 		RELEASE_SEM_LOCK(*sem);
1141 	}
1142 
1143 	if (!found)
1144 		return B_BAD_VALUE;
1145 
1146 	*_cookie = newIndex;
1147 	return B_OK;
1148 }
1149 
1150 
1151 status_t
1152 set_sem_owner(sem_id id, team_id newTeamID)
1153 {
1154 	if (sSemsActive == false)
1155 		return B_NO_MORE_SEMS;
1156 	if (id < 0)
1157 		return B_BAD_SEM_ID;
1158 	if (newTeamID < 0)
1159 		return B_BAD_TEAM_ID;
1160 
1161 	int32 slot = id % sMaxSems;
1162 
1163 	// get the new team
1164 	Team* newTeam = Team::Get(newTeamID);
1165 	if (newTeam == NULL)
1166 		return B_BAD_TEAM_ID;
1167 	BReference<Team> newTeamReference(newTeam, true);
1168 
1169 	InterruptsSpinLocker semListLocker(sSemsSpinlock);
1170 	SpinLocker semLocker(sSems[slot].lock);
1171 
1172 	if (sSems[slot].id != id) {
1173 		TRACE(("set_sem_owner: invalid sem_id %ld\n", id));
1174 		return B_BAD_SEM_ID;
1175 	}
1176 
1177 	list_remove_link(&sSems[slot].u.used.team_link);
1178 	list_add_item(&newTeam->sem_list, &sSems[slot].u.used.team_link);
1179 
1180 	sSems[slot].u.used.owner = newTeam->id;
1181 	return B_OK;
1182 }
1183 
1184 
1185 /*!	Returns the name of the semaphore. The name is not copied, so the caller
1186 	must make sure that the semaphore remains alive as long as the name is used.
1187 */
1188 const char*
1189 sem_get_name_unsafe(sem_id id)
1190 {
1191 	int slot = id % sMaxSems;
1192 
1193 	if (sSemsActive == false || id < 0 || sSems[slot].id != id)
1194 		return NULL;
1195 
1196 	return sSems[slot].u.used.name;
1197 }
1198 
1199 
1200 //	#pragma mark - Syscalls
1201 
1202 
1203 sem_id
1204 _user_create_sem(int32 count, const char *userName)
1205 {
1206 	char name[B_OS_NAME_LENGTH];
1207 
1208 	if (userName == NULL)
1209 		return create_sem_etc(count, NULL, team_get_current_team_id());
1210 
1211 	if (!IS_USER_ADDRESS(userName)
1212 		|| user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK)
1213 		return B_BAD_ADDRESS;
1214 
1215 	return create_sem_etc(count, name, team_get_current_team_id());
1216 }
1217 
1218 
1219 status_t
1220 _user_delete_sem(sem_id id)
1221 {
1222 	return delete_sem_internal(id, true);
1223 }
1224 
1225 
1226 status_t
1227 _user_acquire_sem(sem_id id)
1228 {
1229 	status_t error = switch_sem_etc(-1, id, 1,
1230 		B_CAN_INTERRUPT | B_CHECK_PERMISSION, 0);
1231 
1232 	return syscall_restart_handle_post(error);
1233 }
1234 
1235 
1236 status_t
1237 _user_acquire_sem_etc(sem_id id, int32 count, uint32 flags, bigtime_t timeout)
1238 {
1239 	syscall_restart_handle_timeout_pre(flags, timeout);
1240 
1241 	status_t error = switch_sem_etc(-1, id, count,
1242 		flags | B_CAN_INTERRUPT | B_CHECK_PERMISSION, timeout);
1243 
1244 	return syscall_restart_handle_timeout_post(error, timeout);
1245 }
1246 
1247 
1248 status_t
1249 _user_switch_sem(sem_id releaseSem, sem_id id)
1250 {
1251 	status_t error = switch_sem_etc(releaseSem, id, 1,
1252 		B_CAN_INTERRUPT | B_CHECK_PERMISSION, 0);
1253 
1254 	if (releaseSem < 0)
1255 		return syscall_restart_handle_post(error);
1256 
1257 	return error;
1258 }
1259 
1260 
1261 status_t
1262 _user_switch_sem_etc(sem_id releaseSem, sem_id id, int32 count, uint32 flags,
1263 	bigtime_t timeout)
1264 {
1265 	if (releaseSem < 0)
1266 		syscall_restart_handle_timeout_pre(flags, timeout);
1267 
1268 	status_t error = switch_sem_etc(releaseSem, id, count,
1269 		flags | B_CAN_INTERRUPT | B_CHECK_PERMISSION, timeout);
1270 
1271 	if (releaseSem < 0)
1272 		return syscall_restart_handle_timeout_post(error, timeout);
1273 
1274 	return error;
1275 }
1276 
1277 
1278 status_t
1279 _user_release_sem(sem_id id)
1280 {
1281 	return release_sem_etc(id, 1, B_CHECK_PERMISSION);
1282 }
1283 
1284 
1285 status_t
1286 _user_release_sem_etc(sem_id id, int32 count, uint32 flags)
1287 {
1288 	return release_sem_etc(id, count, flags | B_CHECK_PERMISSION);
1289 }
1290 
1291 
1292 status_t
1293 _user_get_sem_count(sem_id id, int32 *userCount)
1294 {
1295 	status_t status;
1296 	int32 count;
1297 
1298 	if (userCount == NULL || !IS_USER_ADDRESS(userCount))
1299 		return B_BAD_ADDRESS;
1300 
1301 	status = get_sem_count(id, &count);
1302 	if (status == B_OK && user_memcpy(userCount, &count, sizeof(int32)) < B_OK)
1303 		return B_BAD_ADDRESS;
1304 
1305 	return status;
1306 }
1307 
1308 
1309 status_t
1310 _user_get_sem_info(sem_id id, struct sem_info *userInfo, size_t size)
1311 {
1312 	struct sem_info info;
1313 	status_t status;
1314 
1315 	if (userInfo == NULL || !IS_USER_ADDRESS(userInfo))
1316 		return B_BAD_ADDRESS;
1317 
1318 	status = _get_sem_info(id, &info, size);
1319 	if (status == B_OK && user_memcpy(userInfo, &info, size) < B_OK)
1320 		return B_BAD_ADDRESS;
1321 
1322 	return status;
1323 }
1324 
1325 
1326 status_t
1327 _user_get_next_sem_info(team_id team, int32 *userCookie, struct sem_info *userInfo,
1328 	size_t size)
1329 {
1330 	struct sem_info info;
1331 	int32 cookie;
1332 	status_t status;
1333 
1334 	if (userCookie == NULL || userInfo == NULL
1335 		|| !IS_USER_ADDRESS(userCookie) || !IS_USER_ADDRESS(userInfo)
1336 		|| user_memcpy(&cookie, userCookie, sizeof(int32)) < B_OK)
1337 		return B_BAD_ADDRESS;
1338 
1339 	status = _get_next_sem_info(team, &cookie, &info, size);
1340 
1341 	if (status == B_OK) {
1342 		if (user_memcpy(userInfo, &info, size) < B_OK
1343 			|| user_memcpy(userCookie, &cookie, sizeof(int32)) < B_OK)
1344 			return B_BAD_ADDRESS;
1345 	}
1346 
1347 	return status;
1348 }
1349 
1350 
1351 status_t
1352 _user_set_sem_owner(sem_id id, team_id team)
1353 {
1354 	return set_sem_owner(id, team);
1355 }
1356