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
73struct 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
87typedef DoublyLinkedList<queued_thread> ThreadQueue;
88
89struct 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
121static const int32 kMaxSemaphores = 65536;
122static int32 sMaxSems = 4096;
123	// Final value is computed based on the amount of available memory
124static int32 sUsedSems = 0;
125
126static struct sem_entry *sSems = NULL;
127static bool sSemsActive = false;
128static struct sem_entry	*sFreeSemsHead = NULL;
129static struct sem_entry	*sFreeSemsTail = NULL;
130
131static 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
138static int
139dump_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
179static void
180dump_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
226static int
227dump_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*/
278static void
279free_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
297static inline void
298notify_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*/
308static void
309fill_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*/
324static void
325uninit_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
355static status_t
356delete_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++)
413status_t
414haiku_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*/
478sem_id
479create_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
554status_t
555select_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
595status_t
596deselect_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*/
632static void
633remove_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*/
680void
681sem_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
707int32
708sem_max_sems(void)
709{
710	return sMaxSems;
711}
712
713
714int32
715sem_used_sems(void)
716{
717	return sUsedSems;
718}
719
720
721//	#pragma mark - Public Kernel API
722
723
724sem_id
725create_sem(int32 count, const char* name)
726{
727	return create_sem_etc(count, name, team_get_kernel_team_id());
728}
729
730
731status_t
732delete_sem(sem_id id)
733{
734	return delete_sem_internal(id, false);
735}
736
737
738status_t
739acquire_sem(sem_id id)
740{
741	return switch_sem_etc(-1, id, 1, 0, 0);
742}
743
744
745status_t
746acquire_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
752status_t
753switch_sem(sem_id toBeReleased, sem_id toBeAcquired)
754{
755	return switch_sem_etc(toBeReleased, toBeAcquired, 1, 0, 0);
756}
757
758
759status_t
760switch_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
894err:
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
916status_t
917release_sem(sem_id id)
918{
919	return release_sem_etc(id, 1, 0);
920}
921
922
923status_t
924release_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
1031status_t
1032get_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. */
1066status_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. */
1099status_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
1154status_t
1155set_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*/
1191const char*
1192sem_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
1206sem_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
1222status_t
1223_user_delete_sem(sem_id id)
1224{
1225	return delete_sem_internal(id, true);
1226}
1227
1228
1229status_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
1239status_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
1251status_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
1264status_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
1281status_t
1282_user_release_sem(sem_id id)
1283{
1284	return release_sem_etc(id, 1, B_CHECK_PERMISSION);
1285}
1286
1287
1288status_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
1295status_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
1312status_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
1329status_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
1354status_t
1355_user_set_sem_owner(sem_id id, team_id team)
1356{
1357	return set_sem_owner(id, team);
1358}
1359