xref: /haiku/src/system/kernel/locks/user_mutex.cpp (revision 9dd4d2dd055e70a44961e68a19bb8b3b188e23ac)
1813d4cbeSIngo Weinhold /*
2*9dd4d2ddSJérôme Duval  * Copyright 2018, Jérôme Duval, jerome.duval@gmail.com.
3d6d439f3SHamish Morrison  * Copyright 2015, Hamish Morrison, hamishm53@gmail.com.
4813d4cbeSIngo Weinhold  * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
5813d4cbeSIngo Weinhold  * Distributed under the terms of the MIT License.
6813d4cbeSIngo Weinhold  */
7813d4cbeSIngo Weinhold 
8813d4cbeSIngo Weinhold 
9813d4cbeSIngo Weinhold #include <user_mutex.h>
10813d4cbeSIngo Weinhold #include <user_mutex_defs.h>
11813d4cbeSIngo Weinhold 
12813d4cbeSIngo Weinhold #include <condition_variable.h>
13813d4cbeSIngo Weinhold #include <kernel.h>
14813d4cbeSIngo Weinhold #include <lock.h>
15813d4cbeSIngo Weinhold #include <smp.h>
16813d4cbeSIngo Weinhold #include <syscall_restart.h>
17813d4cbeSIngo Weinhold #include <util/AutoLock.h>
18813d4cbeSIngo Weinhold #include <util/OpenHashTable.h>
19813d4cbeSIngo Weinhold #include <vm/vm.h>
20813d4cbeSIngo Weinhold #include <vm/VMArea.h>
21813d4cbeSIngo Weinhold 
22813d4cbeSIngo Weinhold 
23813d4cbeSIngo Weinhold struct UserMutexEntry;
24813d4cbeSIngo Weinhold typedef DoublyLinkedList<UserMutexEntry> UserMutexEntryList;
25813d4cbeSIngo Weinhold 
26813d4cbeSIngo Weinhold struct UserMutexEntry : public DoublyLinkedListLinkImpl<UserMutexEntry> {
27813d4cbeSIngo Weinhold 	addr_t				address;
28813d4cbeSIngo Weinhold 	ConditionVariable	condition;
29813d4cbeSIngo Weinhold 	bool				locked;
30813d4cbeSIngo Weinhold 	UserMutexEntryList	otherEntries;
31813d4cbeSIngo Weinhold 	UserMutexEntry*		hashNext;
32813d4cbeSIngo Weinhold };
33813d4cbeSIngo Weinhold 
34813d4cbeSIngo Weinhold struct UserMutexHashDefinition {
35813d4cbeSIngo Weinhold 	typedef addr_t			KeyType;
36813d4cbeSIngo Weinhold 	typedef UserMutexEntry	ValueType;
37813d4cbeSIngo Weinhold 
38813d4cbeSIngo Weinhold 	size_t HashKey(addr_t key) const
39813d4cbeSIngo Weinhold 	{
40813d4cbeSIngo Weinhold 		return key >> 2;
41813d4cbeSIngo Weinhold 	}
42813d4cbeSIngo Weinhold 
43813d4cbeSIngo Weinhold 	size_t Hash(const UserMutexEntry* value) const
44813d4cbeSIngo Weinhold 	{
45813d4cbeSIngo Weinhold 		return HashKey(value->address);
46813d4cbeSIngo Weinhold 	}
47813d4cbeSIngo Weinhold 
48813d4cbeSIngo Weinhold 	bool Compare(addr_t key, const UserMutexEntry* value) const
49813d4cbeSIngo Weinhold 	{
50813d4cbeSIngo Weinhold 		return value->address == key;
51813d4cbeSIngo Weinhold 	}
52813d4cbeSIngo Weinhold 
53813d4cbeSIngo Weinhold 	UserMutexEntry*& GetLink(UserMutexEntry* value) const
54813d4cbeSIngo Weinhold 	{
55813d4cbeSIngo Weinhold 		return value->hashNext;
56813d4cbeSIngo Weinhold 	}
57813d4cbeSIngo Weinhold };
58813d4cbeSIngo Weinhold 
59813d4cbeSIngo Weinhold typedef BOpenHashTable<UserMutexHashDefinition> UserMutexTable;
60813d4cbeSIngo Weinhold 
61813d4cbeSIngo Weinhold 
62813d4cbeSIngo Weinhold static UserMutexTable sUserMutexTable;
63813d4cbeSIngo Weinhold static mutex sUserMutexTableLock = MUTEX_INITIALIZER("user mutex table");
64813d4cbeSIngo Weinhold 
65813d4cbeSIngo Weinhold 
66813d4cbeSIngo Weinhold static void
67813d4cbeSIngo Weinhold add_user_mutex_entry(UserMutexEntry* entry)
68813d4cbeSIngo Weinhold {
69813d4cbeSIngo Weinhold 	UserMutexEntry* firstEntry = sUserMutexTable.Lookup(entry->address);
70813d4cbeSIngo Weinhold 	if (firstEntry != NULL)
71813d4cbeSIngo Weinhold 		firstEntry->otherEntries.Add(entry);
72813d4cbeSIngo Weinhold 	else
73813d4cbeSIngo Weinhold 		sUserMutexTable.Insert(entry);
74813d4cbeSIngo Weinhold }
75813d4cbeSIngo Weinhold 
76813d4cbeSIngo Weinhold 
77813d4cbeSIngo Weinhold static bool
78813d4cbeSIngo Weinhold remove_user_mutex_entry(UserMutexEntry* entry)
79813d4cbeSIngo Weinhold {
80813d4cbeSIngo Weinhold 	UserMutexEntry* firstEntry = sUserMutexTable.Lookup(entry->address);
81813d4cbeSIngo Weinhold 	if (firstEntry != entry) {
82813d4cbeSIngo Weinhold 		// The entry is not the first entry in the table. Just remove it from
83813d4cbeSIngo Weinhold 		// the first entry's list.
84813d4cbeSIngo Weinhold 		firstEntry->otherEntries.Remove(entry);
85813d4cbeSIngo Weinhold 		return true;
86813d4cbeSIngo Weinhold 	}
87813d4cbeSIngo Weinhold 
88813d4cbeSIngo Weinhold 	// The entry is the first entry in the table. Remove it from the table and,
89813d4cbeSIngo Weinhold 	// if any, add the next entry to the table.
90813d4cbeSIngo Weinhold 	sUserMutexTable.Remove(entry);
91813d4cbeSIngo Weinhold 
920dffa8d4SIngo Weinhold 	firstEntry = entry->otherEntries.RemoveHead();
93813d4cbeSIngo Weinhold 	if (firstEntry != NULL) {
94813d4cbeSIngo Weinhold 		firstEntry->otherEntries.MoveFrom(&entry->otherEntries);
95813d4cbeSIngo Weinhold 		sUserMutexTable.Insert(firstEntry);
96813d4cbeSIngo Weinhold 		return true;
97813d4cbeSIngo Weinhold 	}
98813d4cbeSIngo Weinhold 
99813d4cbeSIngo Weinhold 	return false;
100813d4cbeSIngo Weinhold }
101813d4cbeSIngo Weinhold 
102813d4cbeSIngo Weinhold 
103813d4cbeSIngo Weinhold static status_t
104d6d439f3SHamish Morrison user_mutex_wait_locked(int32* mutex, addr_t physicalAddress, const char* name,
105d6d439f3SHamish Morrison 	uint32 flags, bigtime_t timeout, MutexLocker& locker, bool& lastWaiter)
106813d4cbeSIngo Weinhold {
107813d4cbeSIngo Weinhold 	// add the entry to the table
108813d4cbeSIngo Weinhold 	UserMutexEntry entry;
109813d4cbeSIngo Weinhold 	entry.address = physicalAddress;
110813d4cbeSIngo Weinhold 	entry.locked = false;
111813d4cbeSIngo Weinhold 	add_user_mutex_entry(&entry);
112813d4cbeSIngo Weinhold 
113813d4cbeSIngo Weinhold 	// wait
114813d4cbeSIngo Weinhold 	ConditionVariableEntry waitEntry;
115813d4cbeSIngo Weinhold 	entry.condition.Init((void*)physicalAddress, "user mutex");
116813d4cbeSIngo Weinhold 	entry.condition.Add(&waitEntry);
117813d4cbeSIngo Weinhold 
118813d4cbeSIngo Weinhold 	locker.Unlock();
119813d4cbeSIngo Weinhold 	status_t error = waitEntry.Wait(flags, timeout);
120813d4cbeSIngo Weinhold 	locker.Lock();
121813d4cbeSIngo Weinhold 
122d6d439f3SHamish Morrison 	if (error != B_OK && entry.locked)
123d6d439f3SHamish Morrison 		error = B_OK;
124d6d439f3SHamish Morrison 
125d6d439f3SHamish Morrison 	if (!entry.locked) {
126d6d439f3SHamish Morrison 		// if nobody woke us up, we have to dequeue ourselves
127d6d439f3SHamish Morrison 		lastWaiter = !remove_user_mutex_entry(&entry);
128d6d439f3SHamish Morrison 	} else {
129d6d439f3SHamish Morrison 		// otherwise the waker has done the work of marking the
130d6d439f3SHamish Morrison 		// mutex or semaphore uncontended
131d6d439f3SHamish Morrison 		lastWaiter = false;
132813d4cbeSIngo Weinhold 	}
133813d4cbeSIngo Weinhold 
134d6d439f3SHamish Morrison 	return error;
135813d4cbeSIngo Weinhold }
136813d4cbeSIngo Weinhold 
137d6d439f3SHamish Morrison 
138d6d439f3SHamish Morrison static status_t
139d6d439f3SHamish Morrison user_mutex_lock_locked(int32* mutex, addr_t physicalAddress,
140d6d439f3SHamish Morrison 	const char* name, uint32 flags, bigtime_t timeout, MutexLocker& locker)
141d6d439f3SHamish Morrison {
142d6d439f3SHamish Morrison 	// mark the mutex locked + waiting
143*9dd4d2ddSJérôme Duval 	set_ac();
144d6d439f3SHamish Morrison 	int32 oldValue = atomic_or(mutex,
145d6d439f3SHamish Morrison 		B_USER_MUTEX_LOCKED | B_USER_MUTEX_WAITING);
146*9dd4d2ddSJérôme Duval 	clear_ac();
147d6d439f3SHamish Morrison 
148d6d439f3SHamish Morrison 	if ((oldValue & (B_USER_MUTEX_LOCKED | B_USER_MUTEX_WAITING)) == 0
149d6d439f3SHamish Morrison 			|| (oldValue & B_USER_MUTEX_DISABLED) != 0) {
150d6d439f3SHamish Morrison 		// clear the waiting flag and be done
151*9dd4d2ddSJérôme Duval 		set_ac();
152d6d439f3SHamish Morrison 		atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING);
153*9dd4d2ddSJérôme Duval 		clear_ac();
154d6d439f3SHamish Morrison 		return B_OK;
155d6d439f3SHamish Morrison 	}
156d6d439f3SHamish Morrison 
157d6d439f3SHamish Morrison 	bool lastWaiter;
158d6d439f3SHamish Morrison 	status_t error = user_mutex_wait_locked(mutex, physicalAddress, name,
159d6d439f3SHamish Morrison 		flags, timeout, locker, lastWaiter);
160d6d439f3SHamish Morrison 
161*9dd4d2ddSJérôme Duval 	if (lastWaiter) {
162*9dd4d2ddSJérôme Duval 		set_ac();
163d6d439f3SHamish Morrison 		atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING);
164*9dd4d2ddSJérôme Duval 		clear_ac();
165*9dd4d2ddSJérôme Duval 	}
166d6d439f3SHamish Morrison 
167813d4cbeSIngo Weinhold 	return error;
168813d4cbeSIngo Weinhold }
169813d4cbeSIngo Weinhold 
170813d4cbeSIngo Weinhold 
171813d4cbeSIngo Weinhold static void
17273ad2473SPawel Dziepak user_mutex_unlock_locked(int32* mutex, addr_t physicalAddress, uint32 flags)
173813d4cbeSIngo Weinhold {
174fb67dbf0SHamish Morrison 	UserMutexEntry* entry = sUserMutexTable.Lookup(physicalAddress);
175fb67dbf0SHamish Morrison 	if (entry == NULL) {
176fb67dbf0SHamish Morrison 		// no one is waiting -- clear locked flag
177*9dd4d2ddSJérôme Duval 		set_ac();
178fb67dbf0SHamish Morrison 		atomic_and(mutex, ~(int32)B_USER_MUTEX_LOCKED);
179*9dd4d2ddSJérôme Duval 		clear_ac();
180fb67dbf0SHamish Morrison 		return;
181fb67dbf0SHamish Morrison 	}
182fb67dbf0SHamish Morrison 
1830dffa8d4SIngo Weinhold 	// Someone is waiting -- set the locked flag. It might still be set,
1840dffa8d4SIngo Weinhold 	// but when using userland atomic operations, the caller will usually
1850dffa8d4SIngo Weinhold 	// have cleared it already.
186*9dd4d2ddSJérôme Duval 	set_ac();
1870dffa8d4SIngo Weinhold 	int32 oldValue = atomic_or(mutex, B_USER_MUTEX_LOCKED);
188*9dd4d2ddSJérôme Duval 	clear_ac();
1890dffa8d4SIngo Weinhold 
190813d4cbeSIngo Weinhold 	// unblock the first thread
191813d4cbeSIngo Weinhold 	entry->locked = true;
192813d4cbeSIngo Weinhold 	entry->condition.NotifyOne();
193813d4cbeSIngo Weinhold 
194813d4cbeSIngo Weinhold 	if ((flags & B_USER_MUTEX_UNBLOCK_ALL) != 0
1950dffa8d4SIngo Weinhold 			|| (oldValue & B_USER_MUTEX_DISABLED) != 0) {
196fb67dbf0SHamish Morrison 		// unblock and dequeue all the other waiting threads as well
197fb67dbf0SHamish Morrison 		while (UserMutexEntry* otherEntry = entry->otherEntries.RemoveHead()) {
198813d4cbeSIngo Weinhold 			otherEntry->locked = true;
199813d4cbeSIngo Weinhold 			otherEntry->condition.NotifyOne();
200813d4cbeSIngo Weinhold 		}
201fb67dbf0SHamish Morrison 
202fb67dbf0SHamish Morrison 		// dequeue the first thread and mark the mutex uncontended
203fb67dbf0SHamish Morrison 		sUserMutexTable.Remove(entry);
204*9dd4d2ddSJérôme Duval 		set_ac();
205fb67dbf0SHamish Morrison 		atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING);
206*9dd4d2ddSJérôme Duval 		clear_ac();
2070dffa8d4SIngo Weinhold 	} else {
208fb67dbf0SHamish Morrison 		bool otherWaiters = remove_user_mutex_entry(entry);
209*9dd4d2ddSJérôme Duval 		if (!otherWaiters) {
210*9dd4d2ddSJérôme Duval 			set_ac();
211fb67dbf0SHamish Morrison 			atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING);
212*9dd4d2ddSJérôme Duval 			clear_ac();
213*9dd4d2ddSJérôme Duval 		}
214813d4cbeSIngo Weinhold 	}
215813d4cbeSIngo Weinhold }
216813d4cbeSIngo Weinhold 
217813d4cbeSIngo Weinhold 
218813d4cbeSIngo Weinhold static status_t
219d6d439f3SHamish Morrison user_mutex_sem_acquire_locked(int32* sem, addr_t physicalAddress,
220d6d439f3SHamish Morrison 	const char* name, uint32 flags, bigtime_t timeout, MutexLocker& locker)
221d6d439f3SHamish Morrison {
222d6d439f3SHamish Morrison 	// The semaphore may have been released in the meantime, and we also
223d6d439f3SHamish Morrison 	// need to mark it as contended if it isn't already.
224*9dd4d2ddSJérôme Duval 	set_ac();
225d6d439f3SHamish Morrison 	int32 oldValue = atomic_get(sem);
226*9dd4d2ddSJérôme Duval 	clear_ac();
227d6d439f3SHamish Morrison 	while (oldValue > -1) {
228*9dd4d2ddSJérôme Duval 		set_ac();
229d6d439f3SHamish Morrison 		int32 value = atomic_test_and_set(sem, oldValue - 1, oldValue);
230*9dd4d2ddSJérôme Duval 		clear_ac();
231d6d439f3SHamish Morrison 		if (value == oldValue && value > 0)
232d6d439f3SHamish Morrison 			return B_OK;
233d6d439f3SHamish Morrison 		oldValue = value;
234d6d439f3SHamish Morrison 	}
235d6d439f3SHamish Morrison 
236d6d439f3SHamish Morrison 	bool lastWaiter;
237d6d439f3SHamish Morrison 	status_t error = user_mutex_wait_locked(sem, physicalAddress, name, flags,
238d6d439f3SHamish Morrison 		timeout, locker, lastWaiter);
239d6d439f3SHamish Morrison 
240*9dd4d2ddSJérôme Duval 	if (lastWaiter) {
241*9dd4d2ddSJérôme Duval 		set_ac();
242d6d439f3SHamish Morrison 		atomic_test_and_set(sem, 0, -1);
243*9dd4d2ddSJérôme Duval 		clear_ac();
244*9dd4d2ddSJérôme Duval 	}
245d6d439f3SHamish Morrison 
246d6d439f3SHamish Morrison 	return error;
247d6d439f3SHamish Morrison }
248d6d439f3SHamish Morrison 
249d6d439f3SHamish Morrison 
250d6d439f3SHamish Morrison static void
251d6d439f3SHamish Morrison user_mutex_sem_release_locked(int32* sem, addr_t physicalAddress)
252d6d439f3SHamish Morrison {
253d6d439f3SHamish Morrison 	UserMutexEntry* entry = sUserMutexTable.Lookup(physicalAddress);
254d6d439f3SHamish Morrison 	if (!entry) {
255d6d439f3SHamish Morrison 		// no waiters - mark as uncontended and release
256*9dd4d2ddSJérôme Duval 		set_ac();
257d6d439f3SHamish Morrison 		int32 oldValue = atomic_get(sem);
258*9dd4d2ddSJérôme Duval 		clear_ac();
259d6d439f3SHamish Morrison 		while (true) {
260d6d439f3SHamish Morrison 			int32 inc = oldValue < 0 ? 2 : 1;
261*9dd4d2ddSJérôme Duval 			set_ac();
262d6d439f3SHamish Morrison 			int32 value = atomic_test_and_set(sem, oldValue + inc, oldValue);
263*9dd4d2ddSJérôme Duval 			clear_ac();
264d6d439f3SHamish Morrison 			if (value == oldValue)
265d6d439f3SHamish Morrison 				return;
266d6d439f3SHamish Morrison 			oldValue = value;
267d6d439f3SHamish Morrison 		}
268d6d439f3SHamish Morrison 	}
269d6d439f3SHamish Morrison 
270d6d439f3SHamish Morrison 	bool otherWaiters = remove_user_mutex_entry(entry);
271d6d439f3SHamish Morrison 
272d6d439f3SHamish Morrison 	entry->locked = true;
273d6d439f3SHamish Morrison 	entry->condition.NotifyOne();
274d6d439f3SHamish Morrison 
275d6d439f3SHamish Morrison 	if (!otherWaiters) {
276d6d439f3SHamish Morrison 		// mark the semaphore uncontended
277*9dd4d2ddSJérôme Duval 		set_ac();
278d6d439f3SHamish Morrison 		atomic_test_and_set(sem, 0, -1);
279*9dd4d2ddSJérôme Duval 		clear_ac();
280d6d439f3SHamish Morrison 	}
281d6d439f3SHamish Morrison }
282d6d439f3SHamish Morrison 
283d6d439f3SHamish Morrison 
284d6d439f3SHamish Morrison static status_t
285813d4cbeSIngo Weinhold user_mutex_lock(int32* mutex, const char* name, uint32 flags, bigtime_t timeout)
286813d4cbeSIngo Weinhold {
287813d4cbeSIngo Weinhold 	// wire the page and get the physical address
288813d4cbeSIngo Weinhold 	VMPageWiringInfo wiringInfo;
289813d4cbeSIngo Weinhold 	status_t error = vm_wire_page(B_CURRENT_TEAM, (addr_t)mutex, true,
290813d4cbeSIngo Weinhold 		&wiringInfo);
291813d4cbeSIngo Weinhold 	if (error != B_OK)
292813d4cbeSIngo Weinhold 		return error;
293813d4cbeSIngo Weinhold 
294813d4cbeSIngo Weinhold 	// get the lock
295813d4cbeSIngo Weinhold 	{
296813d4cbeSIngo Weinhold 		MutexLocker locker(sUserMutexTableLock);
297813d4cbeSIngo Weinhold 		error = user_mutex_lock_locked(mutex, wiringInfo.physicalAddress, name,
298813d4cbeSIngo Weinhold 			flags, timeout, locker);
299813d4cbeSIngo Weinhold 	}
300813d4cbeSIngo Weinhold 
301813d4cbeSIngo Weinhold 	// unwire the page
302813d4cbeSIngo Weinhold 	vm_unwire_page(&wiringInfo);
303813d4cbeSIngo Weinhold 
304813d4cbeSIngo Weinhold 	return error;
305813d4cbeSIngo Weinhold }
306813d4cbeSIngo Weinhold 
307813d4cbeSIngo Weinhold 
308813d4cbeSIngo Weinhold static status_t
309813d4cbeSIngo Weinhold user_mutex_switch_lock(int32* fromMutex, int32* toMutex, const char* name,
310813d4cbeSIngo Weinhold 	uint32 flags, bigtime_t timeout)
311813d4cbeSIngo Weinhold {
312813d4cbeSIngo Weinhold 	// wire the pages and get the physical addresses
313813d4cbeSIngo Weinhold 	VMPageWiringInfo fromWiringInfo;
314813d4cbeSIngo Weinhold 	status_t error = vm_wire_page(B_CURRENT_TEAM, (addr_t)fromMutex, true,
315813d4cbeSIngo Weinhold 		&fromWiringInfo);
316813d4cbeSIngo Weinhold 	if (error != B_OK)
317813d4cbeSIngo Weinhold 		return error;
318813d4cbeSIngo Weinhold 
319813d4cbeSIngo Weinhold 	VMPageWiringInfo toWiringInfo;
320813d4cbeSIngo Weinhold 	error = vm_wire_page(B_CURRENT_TEAM, (addr_t)toMutex, true, &toWiringInfo);
321813d4cbeSIngo Weinhold 	if (error != B_OK) {
322813d4cbeSIngo Weinhold 		vm_unwire_page(&fromWiringInfo);
323813d4cbeSIngo Weinhold 		return error;
324813d4cbeSIngo Weinhold 	}
325813d4cbeSIngo Weinhold 
326813d4cbeSIngo Weinhold 	// unlock the first mutex and lock the second one
327813d4cbeSIngo Weinhold 	{
328813d4cbeSIngo Weinhold 		MutexLocker locker(sUserMutexTableLock);
329813d4cbeSIngo Weinhold 		user_mutex_unlock_locked(fromMutex, fromWiringInfo.physicalAddress,
330813d4cbeSIngo Weinhold 			flags);
331813d4cbeSIngo Weinhold 
332813d4cbeSIngo Weinhold 		error = user_mutex_lock_locked(toMutex, toWiringInfo.physicalAddress,
333813d4cbeSIngo Weinhold 			name, flags, timeout, locker);
334813d4cbeSIngo Weinhold 	}
335813d4cbeSIngo Weinhold 
336813d4cbeSIngo Weinhold 	// unwire the pages
337813d4cbeSIngo Weinhold 	vm_unwire_page(&toWiringInfo);
338813d4cbeSIngo Weinhold 	vm_unwire_page(&fromWiringInfo);
339813d4cbeSIngo Weinhold 
340813d4cbeSIngo Weinhold 	return error;
341813d4cbeSIngo Weinhold }
342813d4cbeSIngo Weinhold 
343813d4cbeSIngo Weinhold 
344813d4cbeSIngo Weinhold // #pragma mark - kernel private
345813d4cbeSIngo Weinhold 
346813d4cbeSIngo Weinhold 
347813d4cbeSIngo Weinhold void
348813d4cbeSIngo Weinhold user_mutex_init()
349813d4cbeSIngo Weinhold {
350813d4cbeSIngo Weinhold 	if (sUserMutexTable.Init() != B_OK)
351813d4cbeSIngo Weinhold 		panic("user_mutex_init(): Failed to init table!");
352813d4cbeSIngo Weinhold }
353813d4cbeSIngo Weinhold 
354813d4cbeSIngo Weinhold 
355813d4cbeSIngo Weinhold // #pragma mark - syscalls
356813d4cbeSIngo Weinhold 
357813d4cbeSIngo Weinhold 
358813d4cbeSIngo Weinhold status_t
359813d4cbeSIngo Weinhold _user_mutex_lock(int32* mutex, const char* name, uint32 flags,
360813d4cbeSIngo Weinhold 	bigtime_t timeout)
361813d4cbeSIngo Weinhold {
362813d4cbeSIngo Weinhold 	if (mutex == NULL || !IS_USER_ADDRESS(mutex) || (addr_t)mutex % 4 != 0)
363813d4cbeSIngo Weinhold 		return B_BAD_ADDRESS;
364813d4cbeSIngo Weinhold 
365813d4cbeSIngo Weinhold 	syscall_restart_handle_timeout_pre(flags, timeout);
366813d4cbeSIngo Weinhold 
367813d4cbeSIngo Weinhold 	status_t error = user_mutex_lock(mutex, name, flags | B_CAN_INTERRUPT,
368813d4cbeSIngo Weinhold 		timeout);
369813d4cbeSIngo Weinhold 
370813d4cbeSIngo Weinhold 	return syscall_restart_handle_timeout_post(error, timeout);
371813d4cbeSIngo Weinhold }
372813d4cbeSIngo Weinhold 
373813d4cbeSIngo Weinhold 
374813d4cbeSIngo Weinhold status_t
375813d4cbeSIngo Weinhold _user_mutex_unlock(int32* mutex, uint32 flags)
376813d4cbeSIngo Weinhold {
377813d4cbeSIngo Weinhold 	if (mutex == NULL || !IS_USER_ADDRESS(mutex) || (addr_t)mutex % 4 != 0)
378813d4cbeSIngo Weinhold 		return B_BAD_ADDRESS;
379813d4cbeSIngo Weinhold 
380813d4cbeSIngo Weinhold 	// wire the page and get the physical address
381813d4cbeSIngo Weinhold 	VMPageWiringInfo wiringInfo;
382813d4cbeSIngo Weinhold 	status_t error = vm_wire_page(B_CURRENT_TEAM, (addr_t)mutex, true,
383813d4cbeSIngo Weinhold 		&wiringInfo);
384813d4cbeSIngo Weinhold 	if (error != B_OK)
385813d4cbeSIngo Weinhold 		return error;
386813d4cbeSIngo Weinhold 
387813d4cbeSIngo Weinhold 	{
388813d4cbeSIngo Weinhold 		MutexLocker locker(sUserMutexTableLock);
389813d4cbeSIngo Weinhold 		user_mutex_unlock_locked(mutex, wiringInfo.physicalAddress, flags);
390813d4cbeSIngo Weinhold 	}
391813d4cbeSIngo Weinhold 
392813d4cbeSIngo Weinhold 	vm_unwire_page(&wiringInfo);
393813d4cbeSIngo Weinhold 
394813d4cbeSIngo Weinhold 	return B_OK;
395813d4cbeSIngo Weinhold }
396813d4cbeSIngo Weinhold 
397813d4cbeSIngo Weinhold 
398813d4cbeSIngo Weinhold status_t
399813d4cbeSIngo Weinhold _user_mutex_switch_lock(int32* fromMutex, int32* toMutex, const char* name,
400813d4cbeSIngo Weinhold 	uint32 flags, bigtime_t timeout)
401813d4cbeSIngo Weinhold {
402813d4cbeSIngo Weinhold 	if (fromMutex == NULL || !IS_USER_ADDRESS(fromMutex)
403813d4cbeSIngo Weinhold 			|| (addr_t)fromMutex % 4 != 0 || toMutex == NULL
404813d4cbeSIngo Weinhold 			|| !IS_USER_ADDRESS(toMutex) || (addr_t)toMutex % 4 != 0) {
405813d4cbeSIngo Weinhold 		return B_BAD_ADDRESS;
406813d4cbeSIngo Weinhold 	}
407813d4cbeSIngo Weinhold 
4080dffa8d4SIngo Weinhold 	return user_mutex_switch_lock(fromMutex, toMutex, name,
409813d4cbeSIngo Weinhold 		flags | B_CAN_INTERRUPT, timeout);
410813d4cbeSIngo Weinhold }
411d6d439f3SHamish Morrison 
412d6d439f3SHamish Morrison 
413d6d439f3SHamish Morrison status_t
414d6d439f3SHamish Morrison _user_mutex_sem_acquire(int32* sem, const char* name, uint32 flags,
415d6d439f3SHamish Morrison 	bigtime_t timeout)
416d6d439f3SHamish Morrison {
417d6d439f3SHamish Morrison 	if (sem == NULL || !IS_USER_ADDRESS(sem) || (addr_t)sem % 4 != 0)
418d6d439f3SHamish Morrison 		return B_BAD_ADDRESS;
419d6d439f3SHamish Morrison 
420d6d439f3SHamish Morrison 	syscall_restart_handle_timeout_pre(flags, timeout);
421d6d439f3SHamish Morrison 
422d6d439f3SHamish Morrison 	// wire the page and get the physical address
423d6d439f3SHamish Morrison 	VMPageWiringInfo wiringInfo;
424d6d439f3SHamish Morrison 	status_t error = vm_wire_page(B_CURRENT_TEAM, (addr_t)sem, true,
425d6d439f3SHamish Morrison 		&wiringInfo);
426d6d439f3SHamish Morrison 	if (error != B_OK)
427d6d439f3SHamish Morrison 		return error;
428d6d439f3SHamish Morrison 
429d6d439f3SHamish Morrison 	{
430d6d439f3SHamish Morrison 		MutexLocker locker(sUserMutexTableLock);
431d6d439f3SHamish Morrison 		error = user_mutex_sem_acquire_locked(sem, wiringInfo.physicalAddress,
432d6d439f3SHamish Morrison 			name, flags | B_CAN_INTERRUPT, timeout, locker);
433d6d439f3SHamish Morrison 	}
434d6d439f3SHamish Morrison 
435d6d439f3SHamish Morrison 	vm_unwire_page(&wiringInfo);
436d6d439f3SHamish Morrison 	return syscall_restart_handle_timeout_post(error, timeout);
437d6d439f3SHamish Morrison }
438d6d439f3SHamish Morrison 
439d6d439f3SHamish Morrison 
440d6d439f3SHamish Morrison status_t
441d6d439f3SHamish Morrison _user_mutex_sem_release(int32* sem)
442d6d439f3SHamish Morrison {
443d6d439f3SHamish Morrison 	if (sem == NULL || !IS_USER_ADDRESS(sem) || (addr_t)sem % 4 != 0)
444d6d439f3SHamish Morrison 		return B_BAD_ADDRESS;
445d6d439f3SHamish Morrison 
446d6d439f3SHamish Morrison 	// wire the page and get the physical address
447d6d439f3SHamish Morrison 	VMPageWiringInfo wiringInfo;
448d6d439f3SHamish Morrison 	status_t error = vm_wire_page(B_CURRENT_TEAM, (addr_t)sem, true,
449d6d439f3SHamish Morrison 		&wiringInfo);
450d6d439f3SHamish Morrison 	if (error != B_OK)
451d6d439f3SHamish Morrison 		return error;
452d6d439f3SHamish Morrison 
453d6d439f3SHamish Morrison 	{
454d6d439f3SHamish Morrison 		MutexLocker locker(sUserMutexTableLock);
455d6d439f3SHamish Morrison 		user_mutex_sem_release_locked(sem, wiringInfo.physicalAddress);
456d6d439f3SHamish Morrison 	}
457d6d439f3SHamish Morrison 
458d6d439f3SHamish Morrison 	vm_unwire_page(&wiringInfo);
459d6d439f3SHamish Morrison 	return B_OK;
460d6d439f3SHamish Morrison }
461