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