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