xref: /haiku/src/system/kernel/locks/user_mutex.cpp (revision ca458a2b55da6bfae12346076c9ec67c5244bcf7)
1813d4cbeSIngo Weinhold /*
29dd4d2ddSJé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> {
27402b4156SAugustin Cavalier 	phys_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 {
35402b4156SAugustin Cavalier 	typedef phys_addr_t		KeyType;
36813d4cbeSIngo Weinhold 	typedef UserMutexEntry	ValueType;
37813d4cbeSIngo Weinhold 
389686d931SAugustin Cavalier 	size_t HashKey(phys_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 
48402b4156SAugustin Cavalier 	bool Compare(phys_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 
66cf1b26a9SAugustin Cavalier // #pragma mark - user atomics
67cf1b26a9SAugustin Cavalier 
68cf1b26a9SAugustin Cavalier 
69cf1b26a9SAugustin Cavalier static int32
70cf1b26a9SAugustin Cavalier user_atomic_or(int32* value, int32 orValue)
71cf1b26a9SAugustin Cavalier {
72cf1b26a9SAugustin Cavalier 	set_ac();
73cf1b26a9SAugustin Cavalier 	int32 result = atomic_or(value, orValue);
74cf1b26a9SAugustin Cavalier 	clear_ac();
75cf1b26a9SAugustin Cavalier 	return result;
76cf1b26a9SAugustin Cavalier }
77cf1b26a9SAugustin Cavalier 
78cf1b26a9SAugustin Cavalier 
79cf1b26a9SAugustin Cavalier static int32
80cf1b26a9SAugustin Cavalier user_atomic_and(int32* value, int32 orValue)
81cf1b26a9SAugustin Cavalier {
82cf1b26a9SAugustin Cavalier 	set_ac();
83cf1b26a9SAugustin Cavalier 	int32 result = atomic_and(value, orValue);
84cf1b26a9SAugustin Cavalier 	clear_ac();
85cf1b26a9SAugustin Cavalier 	return result;
86cf1b26a9SAugustin Cavalier }
87cf1b26a9SAugustin Cavalier 
88cf1b26a9SAugustin Cavalier 
89cf1b26a9SAugustin Cavalier static int32
90cf1b26a9SAugustin Cavalier user_atomic_get(int32* value)
91cf1b26a9SAugustin Cavalier {
92cf1b26a9SAugustin Cavalier 	set_ac();
93cf1b26a9SAugustin Cavalier 	int32 result = atomic_get(value);
94cf1b26a9SAugustin Cavalier 	clear_ac();
95cf1b26a9SAugustin Cavalier 	return result;
96cf1b26a9SAugustin Cavalier }
97cf1b26a9SAugustin Cavalier 
98cf1b26a9SAugustin Cavalier 
99cf1b26a9SAugustin Cavalier static int32
100cf1b26a9SAugustin Cavalier user_atomic_test_and_set(int32* value, int32 newValue, int32 testAgainst)
101cf1b26a9SAugustin Cavalier {
102cf1b26a9SAugustin Cavalier 	set_ac();
103cf1b26a9SAugustin Cavalier 	int32 result = atomic_test_and_set(value, newValue, testAgainst);
104cf1b26a9SAugustin Cavalier 	clear_ac();
105cf1b26a9SAugustin Cavalier 	return result;
106cf1b26a9SAugustin Cavalier }
107cf1b26a9SAugustin Cavalier 
108cf1b26a9SAugustin Cavalier 
109cf1b26a9SAugustin Cavalier // #pragma mark - user mutex entries
110cf1b26a9SAugustin Cavalier 
111cf1b26a9SAugustin Cavalier 
112813d4cbeSIngo Weinhold static void
113813d4cbeSIngo Weinhold add_user_mutex_entry(UserMutexEntry* entry)
114813d4cbeSIngo Weinhold {
115813d4cbeSIngo Weinhold 	UserMutexEntry* firstEntry = sUserMutexTable.Lookup(entry->address);
116813d4cbeSIngo Weinhold 	if (firstEntry != NULL)
117813d4cbeSIngo Weinhold 		firstEntry->otherEntries.Add(entry);
118813d4cbeSIngo Weinhold 	else
119813d4cbeSIngo Weinhold 		sUserMutexTable.Insert(entry);
120813d4cbeSIngo Weinhold }
121813d4cbeSIngo Weinhold 
122813d4cbeSIngo Weinhold 
123813d4cbeSIngo Weinhold static bool
124813d4cbeSIngo Weinhold remove_user_mutex_entry(UserMutexEntry* entry)
125813d4cbeSIngo Weinhold {
126813d4cbeSIngo Weinhold 	UserMutexEntry* firstEntry = sUserMutexTable.Lookup(entry->address);
127813d4cbeSIngo Weinhold 	if (firstEntry != entry) {
128813d4cbeSIngo Weinhold 		// The entry is not the first entry in the table. Just remove it from
129813d4cbeSIngo Weinhold 		// the first entry's list.
130813d4cbeSIngo Weinhold 		firstEntry->otherEntries.Remove(entry);
131813d4cbeSIngo Weinhold 		return true;
132813d4cbeSIngo Weinhold 	}
133813d4cbeSIngo Weinhold 
134813d4cbeSIngo Weinhold 	// The entry is the first entry in the table. Remove it from the table and,
135813d4cbeSIngo Weinhold 	// if any, add the next entry to the table.
136813d4cbeSIngo Weinhold 	sUserMutexTable.Remove(entry);
137813d4cbeSIngo Weinhold 
1380dffa8d4SIngo Weinhold 	firstEntry = entry->otherEntries.RemoveHead();
139813d4cbeSIngo Weinhold 	if (firstEntry != NULL) {
140813d4cbeSIngo Weinhold 		firstEntry->otherEntries.MoveFrom(&entry->otherEntries);
141813d4cbeSIngo Weinhold 		sUserMutexTable.Insert(firstEntry);
142813d4cbeSIngo Weinhold 		return true;
143813d4cbeSIngo Weinhold 	}
144813d4cbeSIngo Weinhold 
145813d4cbeSIngo Weinhold 	return false;
146813d4cbeSIngo Weinhold }
147813d4cbeSIngo Weinhold 
148813d4cbeSIngo Weinhold 
149813d4cbeSIngo Weinhold static status_t
150402b4156SAugustin Cavalier user_mutex_wait_locked(int32* mutex, phys_addr_t physicalAddress, const char* name,
151d6d439f3SHamish Morrison 	uint32 flags, bigtime_t timeout, MutexLocker& locker, bool& lastWaiter)
152813d4cbeSIngo Weinhold {
153813d4cbeSIngo Weinhold 	// add the entry to the table
154813d4cbeSIngo Weinhold 	UserMutexEntry entry;
155813d4cbeSIngo Weinhold 	entry.address = physicalAddress;
156813d4cbeSIngo Weinhold 	entry.locked = false;
157813d4cbeSIngo Weinhold 	add_user_mutex_entry(&entry);
158813d4cbeSIngo Weinhold 
159813d4cbeSIngo Weinhold 	// wait
160813d4cbeSIngo Weinhold 	entry.condition.Init((void*)physicalAddress, "user mutex");
1618f68daedSAugustin Cavalier 	status_t error = entry.condition.Wait(locker.Get(), flags, timeout);
162813d4cbeSIngo Weinhold 
163d6d439f3SHamish Morrison 	if (error != B_OK && entry.locked)
164d6d439f3SHamish Morrison 		error = B_OK;
165d6d439f3SHamish Morrison 
166d6d439f3SHamish Morrison 	if (!entry.locked) {
167d6d439f3SHamish Morrison 		// if nobody woke us up, we have to dequeue ourselves
168d6d439f3SHamish Morrison 		lastWaiter = !remove_user_mutex_entry(&entry);
169d6d439f3SHamish Morrison 	} else {
170d6d439f3SHamish Morrison 		// otherwise the waker has done the work of marking the
171d6d439f3SHamish Morrison 		// mutex or semaphore uncontended
172d6d439f3SHamish Morrison 		lastWaiter = false;
173813d4cbeSIngo Weinhold 	}
174813d4cbeSIngo Weinhold 
175d6d439f3SHamish Morrison 	return error;
176813d4cbeSIngo Weinhold }
177813d4cbeSIngo Weinhold 
178d6d439f3SHamish Morrison 
179901b48c2SAugustin Cavalier static bool
180901b48c2SAugustin Cavalier user_mutex_prepare_to_lock(int32* mutex)
181d6d439f3SHamish Morrison {
182cf1b26a9SAugustin Cavalier 	int32 oldValue = user_atomic_or(mutex,
183d6d439f3SHamish Morrison 		B_USER_MUTEX_LOCKED | B_USER_MUTEX_WAITING);
1846f3f29c7SAugustin Cavalier 	if ((oldValue & B_USER_MUTEX_LOCKED) == 0
185d6d439f3SHamish Morrison 			|| (oldValue & B_USER_MUTEX_DISABLED) != 0) {
186d6d439f3SHamish Morrison 		// clear the waiting flag and be done
1876f3f29c7SAugustin Cavalier 		if ((oldValue & B_USER_MUTEX_WAITING) == 0)
188cf1b26a9SAugustin Cavalier 			user_atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING);
189901b48c2SAugustin Cavalier 		return true;
190d6d439f3SHamish Morrison 	}
191d6d439f3SHamish Morrison 
192901b48c2SAugustin Cavalier 	return false;
193901b48c2SAugustin Cavalier }
194901b48c2SAugustin Cavalier 
195901b48c2SAugustin Cavalier 
196901b48c2SAugustin Cavalier static status_t
197901b48c2SAugustin Cavalier user_mutex_lock_locked(int32* mutex, phys_addr_t physicalAddress,
198901b48c2SAugustin Cavalier 	const char* name, uint32 flags, bigtime_t timeout, MutexLocker& locker)
199901b48c2SAugustin Cavalier {
200901b48c2SAugustin Cavalier 	if (user_mutex_prepare_to_lock(mutex))
201901b48c2SAugustin Cavalier 		return B_OK;
202901b48c2SAugustin Cavalier 
203d6d439f3SHamish Morrison 	bool lastWaiter;
204d6d439f3SHamish Morrison 	status_t error = user_mutex_wait_locked(mutex, physicalAddress, name,
205d6d439f3SHamish Morrison 		flags, timeout, locker, lastWaiter);
206d6d439f3SHamish Morrison 
2076f3f29c7SAugustin Cavalier 	if (lastWaiter)
208cf1b26a9SAugustin Cavalier 		user_atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING);
209d6d439f3SHamish Morrison 
210813d4cbeSIngo Weinhold 	return error;
211813d4cbeSIngo Weinhold }
212813d4cbeSIngo Weinhold 
213813d4cbeSIngo Weinhold 
214813d4cbeSIngo Weinhold static void
2156f3f29c7SAugustin Cavalier user_mutex_unblock_locked(int32* mutex, phys_addr_t physicalAddress, uint32 flags)
216813d4cbeSIngo Weinhold {
217fb67dbf0SHamish Morrison 	UserMutexEntry* entry = sUserMutexTable.Lookup(physicalAddress);
218fb67dbf0SHamish Morrison 	if (entry == NULL) {
2196f3f29c7SAugustin Cavalier 		// no one is waiting
2206f3f29c7SAugustin Cavalier 		user_atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING);
221fb67dbf0SHamish Morrison 		return;
222fb67dbf0SHamish Morrison 	}
223fb67dbf0SHamish Morrison 
2246f3f29c7SAugustin Cavalier 	// Someone is waiting: try to hand off the lock to them, if possible.
225*ca458a2bSAugustin Cavalier 	int32 oldValue = 0;
226*ca458a2bSAugustin Cavalier 	if ((flags & B_USER_MUTEX_UNBLOCK_ALL) == 0) {
227*ca458a2bSAugustin Cavalier 		oldValue = user_atomic_or(mutex, B_USER_MUTEX_LOCKED);
2286f3f29c7SAugustin Cavalier 		if ((oldValue & B_USER_MUTEX_LOCKED) != 0)
2296f3f29c7SAugustin Cavalier 			return;
230*ca458a2bSAugustin Cavalier 	} else {
231*ca458a2bSAugustin Cavalier 		oldValue = user_atomic_get(mutex);
232*ca458a2bSAugustin Cavalier 	}
2330dffa8d4SIngo Weinhold 
234813d4cbeSIngo Weinhold 	// unblock the first thread
235813d4cbeSIngo Weinhold 	entry->locked = true;
236813d4cbeSIngo Weinhold 	entry->condition.NotifyOne();
237813d4cbeSIngo Weinhold 
238813d4cbeSIngo Weinhold 	if ((flags & B_USER_MUTEX_UNBLOCK_ALL) != 0
2390dffa8d4SIngo Weinhold 			|| (oldValue & B_USER_MUTEX_DISABLED) != 0) {
240fb67dbf0SHamish Morrison 		// unblock and dequeue all the other waiting threads as well
241fb67dbf0SHamish Morrison 		while (UserMutexEntry* otherEntry = entry->otherEntries.RemoveHead()) {
242813d4cbeSIngo Weinhold 			otherEntry->locked = true;
243813d4cbeSIngo Weinhold 			otherEntry->condition.NotifyOne();
244813d4cbeSIngo Weinhold 		}
245fb67dbf0SHamish Morrison 
246fb67dbf0SHamish Morrison 		// dequeue the first thread and mark the mutex uncontended
247fb67dbf0SHamish Morrison 		sUserMutexTable.Remove(entry);
248cf1b26a9SAugustin Cavalier 		user_atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING);
2490dffa8d4SIngo Weinhold 	} else {
250fb67dbf0SHamish Morrison 		bool otherWaiters = remove_user_mutex_entry(entry);
2519dd4d2ddSJérôme Duval 		if (!otherWaiters) {
252cf1b26a9SAugustin Cavalier 			user_atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING);
2539dd4d2ddSJérôme Duval 		}
254813d4cbeSIngo Weinhold 	}
255813d4cbeSIngo Weinhold }
256813d4cbeSIngo Weinhold 
257813d4cbeSIngo Weinhold 
258813d4cbeSIngo Weinhold static status_t
259402b4156SAugustin Cavalier user_mutex_sem_acquire_locked(int32* sem, phys_addr_t physicalAddress,
260d6d439f3SHamish Morrison 	const char* name, uint32 flags, bigtime_t timeout, MutexLocker& locker)
261d6d439f3SHamish Morrison {
262d6d439f3SHamish Morrison 	// The semaphore may have been released in the meantime, and we also
263d6d439f3SHamish Morrison 	// need to mark it as contended if it isn't already.
264cf1b26a9SAugustin Cavalier 	int32 oldValue = user_atomic_get(sem);
265d6d439f3SHamish Morrison 	while (oldValue > -1) {
266cf1b26a9SAugustin Cavalier 		int32 value = user_atomic_test_and_set(sem, oldValue - 1, oldValue);
267d6d439f3SHamish Morrison 		if (value == oldValue && value > 0)
268d6d439f3SHamish Morrison 			return B_OK;
269d6d439f3SHamish Morrison 		oldValue = value;
270d6d439f3SHamish Morrison 	}
271d6d439f3SHamish Morrison 
272d6d439f3SHamish Morrison 	bool lastWaiter;
273d6d439f3SHamish Morrison 	status_t error = user_mutex_wait_locked(sem, physicalAddress, name, flags,
274d6d439f3SHamish Morrison 		timeout, locker, lastWaiter);
275d6d439f3SHamish Morrison 
276cf1b26a9SAugustin Cavalier 	if (lastWaiter)
277cf1b26a9SAugustin Cavalier 		user_atomic_test_and_set(sem, 0, -1);
278d6d439f3SHamish Morrison 
279d6d439f3SHamish Morrison 	return error;
280d6d439f3SHamish Morrison }
281d6d439f3SHamish Morrison 
282d6d439f3SHamish Morrison 
283d6d439f3SHamish Morrison static void
284402b4156SAugustin Cavalier user_mutex_sem_release_locked(int32* sem, phys_addr_t physicalAddress)
285d6d439f3SHamish Morrison {
286d6d439f3SHamish Morrison 	UserMutexEntry* entry = sUserMutexTable.Lookup(physicalAddress);
287d6d439f3SHamish Morrison 	if (!entry) {
288d6d439f3SHamish Morrison 		// no waiters - mark as uncontended and release
289cf1b26a9SAugustin Cavalier 		int32 oldValue = user_atomic_get(sem);
290d6d439f3SHamish Morrison 		while (true) {
291d6d439f3SHamish Morrison 			int32 inc = oldValue < 0 ? 2 : 1;
292cf1b26a9SAugustin Cavalier 			int32 value = user_atomic_test_and_set(sem, oldValue + inc, oldValue);
293d6d439f3SHamish Morrison 			if (value == oldValue)
294d6d439f3SHamish Morrison 				return;
295d6d439f3SHamish Morrison 			oldValue = value;
296d6d439f3SHamish Morrison 		}
297d6d439f3SHamish Morrison 	}
298d6d439f3SHamish Morrison 
299d6d439f3SHamish Morrison 	bool otherWaiters = remove_user_mutex_entry(entry);
300d6d439f3SHamish Morrison 
301d6d439f3SHamish Morrison 	entry->locked = true;
302d6d439f3SHamish Morrison 	entry->condition.NotifyOne();
303d6d439f3SHamish Morrison 
304d6d439f3SHamish Morrison 	if (!otherWaiters) {
305d6d439f3SHamish Morrison 		// mark the semaphore uncontended
306cf1b26a9SAugustin Cavalier 		user_atomic_test_and_set(sem, 0, -1);
307d6d439f3SHamish Morrison 	}
308d6d439f3SHamish Morrison }
309d6d439f3SHamish Morrison 
310d6d439f3SHamish Morrison 
311d6d439f3SHamish Morrison static status_t
312813d4cbeSIngo Weinhold user_mutex_lock(int32* mutex, const char* name, uint32 flags, bigtime_t timeout)
313813d4cbeSIngo Weinhold {
314813d4cbeSIngo Weinhold 	// wire the page and get the physical address
315813d4cbeSIngo Weinhold 	VMPageWiringInfo wiringInfo;
316813d4cbeSIngo Weinhold 	status_t error = vm_wire_page(B_CURRENT_TEAM, (addr_t)mutex, true,
317813d4cbeSIngo Weinhold 		&wiringInfo);
318813d4cbeSIngo Weinhold 	if (error != B_OK)
319813d4cbeSIngo Weinhold 		return error;
320813d4cbeSIngo Weinhold 
321813d4cbeSIngo Weinhold 	// get the lock
322813d4cbeSIngo Weinhold 	{
323813d4cbeSIngo Weinhold 		MutexLocker locker(sUserMutexTableLock);
324813d4cbeSIngo Weinhold 		error = user_mutex_lock_locked(mutex, wiringInfo.physicalAddress, name,
325813d4cbeSIngo Weinhold 			flags, timeout, locker);
326813d4cbeSIngo Weinhold 	}
327813d4cbeSIngo Weinhold 
328813d4cbeSIngo Weinhold 	// unwire the page
329813d4cbeSIngo Weinhold 	vm_unwire_page(&wiringInfo);
330813d4cbeSIngo Weinhold 
331813d4cbeSIngo Weinhold 	return error;
332813d4cbeSIngo Weinhold }
333813d4cbeSIngo Weinhold 
334813d4cbeSIngo Weinhold 
335813d4cbeSIngo Weinhold static status_t
336813d4cbeSIngo Weinhold user_mutex_switch_lock(int32* fromMutex, int32* toMutex, const char* name,
337813d4cbeSIngo Weinhold 	uint32 flags, bigtime_t timeout)
338813d4cbeSIngo Weinhold {
339813d4cbeSIngo Weinhold 	// wire the pages and get the physical addresses
340813d4cbeSIngo Weinhold 	VMPageWiringInfo fromWiringInfo;
341813d4cbeSIngo Weinhold 	status_t error = vm_wire_page(B_CURRENT_TEAM, (addr_t)fromMutex, true,
342813d4cbeSIngo Weinhold 		&fromWiringInfo);
343813d4cbeSIngo Weinhold 	if (error != B_OK)
344813d4cbeSIngo Weinhold 		return error;
345813d4cbeSIngo Weinhold 
346813d4cbeSIngo Weinhold 	VMPageWiringInfo toWiringInfo;
347813d4cbeSIngo Weinhold 	error = vm_wire_page(B_CURRENT_TEAM, (addr_t)toMutex, true, &toWiringInfo);
348813d4cbeSIngo Weinhold 	if (error != B_OK) {
349813d4cbeSIngo Weinhold 		vm_unwire_page(&fromWiringInfo);
350813d4cbeSIngo Weinhold 		return error;
351813d4cbeSIngo Weinhold 	}
352813d4cbeSIngo Weinhold 
353813d4cbeSIngo Weinhold 	// unlock the first mutex and lock the second one
354813d4cbeSIngo Weinhold 	{
355813d4cbeSIngo Weinhold 		MutexLocker locker(sUserMutexTableLock);
356901b48c2SAugustin Cavalier 
357901b48c2SAugustin Cavalier 		const bool alreadyLocked = user_mutex_prepare_to_lock(toMutex);
3586f3f29c7SAugustin Cavalier 		user_atomic_and(fromMutex, ~(int32)B_USER_MUTEX_LOCKED);
3596f3f29c7SAugustin Cavalier 		user_mutex_unblock_locked(fromMutex, fromWiringInfo.physicalAddress,
360813d4cbeSIngo Weinhold 			flags);
361813d4cbeSIngo Weinhold 
362901b48c2SAugustin Cavalier 		if (!alreadyLocked) {
363813d4cbeSIngo Weinhold 			error = user_mutex_lock_locked(toMutex, toWiringInfo.physicalAddress,
364813d4cbeSIngo Weinhold 				name, flags, timeout, locker);
365813d4cbeSIngo Weinhold 		}
366901b48c2SAugustin Cavalier 	}
367813d4cbeSIngo Weinhold 
368813d4cbeSIngo Weinhold 	// unwire the pages
369813d4cbeSIngo Weinhold 	vm_unwire_page(&toWiringInfo);
370813d4cbeSIngo Weinhold 	vm_unwire_page(&fromWiringInfo);
371813d4cbeSIngo Weinhold 
372813d4cbeSIngo Weinhold 	return error;
373813d4cbeSIngo Weinhold }
374813d4cbeSIngo Weinhold 
375813d4cbeSIngo Weinhold 
376813d4cbeSIngo Weinhold // #pragma mark - kernel private
377813d4cbeSIngo Weinhold 
378813d4cbeSIngo Weinhold 
379813d4cbeSIngo Weinhold void
380813d4cbeSIngo Weinhold user_mutex_init()
381813d4cbeSIngo Weinhold {
382813d4cbeSIngo Weinhold 	if (sUserMutexTable.Init() != B_OK)
383813d4cbeSIngo Weinhold 		panic("user_mutex_init(): Failed to init table!");
384813d4cbeSIngo Weinhold }
385813d4cbeSIngo Weinhold 
386813d4cbeSIngo Weinhold 
387813d4cbeSIngo Weinhold // #pragma mark - syscalls
388813d4cbeSIngo Weinhold 
389813d4cbeSIngo Weinhold 
390813d4cbeSIngo Weinhold status_t
391813d4cbeSIngo Weinhold _user_mutex_lock(int32* mutex, const char* name, uint32 flags,
392813d4cbeSIngo Weinhold 	bigtime_t timeout)
393813d4cbeSIngo Weinhold {
394813d4cbeSIngo Weinhold 	if (mutex == NULL || !IS_USER_ADDRESS(mutex) || (addr_t)mutex % 4 != 0)
395813d4cbeSIngo Weinhold 		return B_BAD_ADDRESS;
396813d4cbeSIngo Weinhold 
397813d4cbeSIngo Weinhold 	syscall_restart_handle_timeout_pre(flags, timeout);
398813d4cbeSIngo Weinhold 
399813d4cbeSIngo Weinhold 	status_t error = user_mutex_lock(mutex, name, flags | B_CAN_INTERRUPT,
400813d4cbeSIngo Weinhold 		timeout);
401813d4cbeSIngo Weinhold 
402813d4cbeSIngo Weinhold 	return syscall_restart_handle_timeout_post(error, timeout);
403813d4cbeSIngo Weinhold }
404813d4cbeSIngo Weinhold 
405813d4cbeSIngo Weinhold 
406813d4cbeSIngo Weinhold status_t
4076f3f29c7SAugustin Cavalier _user_mutex_unblock(int32* mutex, uint32 flags)
408813d4cbeSIngo Weinhold {
409813d4cbeSIngo Weinhold 	if (mutex == NULL || !IS_USER_ADDRESS(mutex) || (addr_t)mutex % 4 != 0)
410813d4cbeSIngo Weinhold 		return B_BAD_ADDRESS;
411813d4cbeSIngo Weinhold 
412813d4cbeSIngo Weinhold 	// wire the page and get the physical address
413813d4cbeSIngo Weinhold 	VMPageWiringInfo wiringInfo;
414813d4cbeSIngo Weinhold 	status_t error = vm_wire_page(B_CURRENT_TEAM, (addr_t)mutex, true,
415813d4cbeSIngo Weinhold 		&wiringInfo);
416813d4cbeSIngo Weinhold 	if (error != B_OK)
417813d4cbeSIngo Weinhold 		return error;
418813d4cbeSIngo Weinhold 
419813d4cbeSIngo Weinhold 	{
420813d4cbeSIngo Weinhold 		MutexLocker locker(sUserMutexTableLock);
4216f3f29c7SAugustin Cavalier 		user_mutex_unblock_locked(mutex, wiringInfo.physicalAddress, flags);
422813d4cbeSIngo Weinhold 	}
423813d4cbeSIngo Weinhold 
424813d4cbeSIngo Weinhold 	vm_unwire_page(&wiringInfo);
425813d4cbeSIngo Weinhold 
426813d4cbeSIngo Weinhold 	return B_OK;
427813d4cbeSIngo Weinhold }
428813d4cbeSIngo Weinhold 
429813d4cbeSIngo Weinhold 
430813d4cbeSIngo Weinhold status_t
431813d4cbeSIngo Weinhold _user_mutex_switch_lock(int32* fromMutex, int32* toMutex, const char* name,
432813d4cbeSIngo Weinhold 	uint32 flags, bigtime_t timeout)
433813d4cbeSIngo Weinhold {
434813d4cbeSIngo Weinhold 	if (fromMutex == NULL || !IS_USER_ADDRESS(fromMutex)
435813d4cbeSIngo Weinhold 			|| (addr_t)fromMutex % 4 != 0 || toMutex == NULL
436813d4cbeSIngo Weinhold 			|| !IS_USER_ADDRESS(toMutex) || (addr_t)toMutex % 4 != 0) {
437813d4cbeSIngo Weinhold 		return B_BAD_ADDRESS;
438813d4cbeSIngo Weinhold 	}
439813d4cbeSIngo Weinhold 
4400dffa8d4SIngo Weinhold 	return user_mutex_switch_lock(fromMutex, toMutex, name,
441813d4cbeSIngo Weinhold 		flags | B_CAN_INTERRUPT, timeout);
442813d4cbeSIngo Weinhold }
443d6d439f3SHamish Morrison 
444d6d439f3SHamish Morrison 
445d6d439f3SHamish Morrison status_t
446d6d439f3SHamish Morrison _user_mutex_sem_acquire(int32* sem, const char* name, uint32 flags,
447d6d439f3SHamish Morrison 	bigtime_t timeout)
448d6d439f3SHamish Morrison {
449d6d439f3SHamish Morrison 	if (sem == NULL || !IS_USER_ADDRESS(sem) || (addr_t)sem % 4 != 0)
450d6d439f3SHamish Morrison 		return B_BAD_ADDRESS;
451d6d439f3SHamish Morrison 
452d6d439f3SHamish Morrison 	syscall_restart_handle_timeout_pre(flags, timeout);
453d6d439f3SHamish Morrison 
454d6d439f3SHamish Morrison 	// wire the page and get the physical address
455d6d439f3SHamish Morrison 	VMPageWiringInfo wiringInfo;
456d6d439f3SHamish Morrison 	status_t error = vm_wire_page(B_CURRENT_TEAM, (addr_t)sem, true,
457d6d439f3SHamish Morrison 		&wiringInfo);
458d6d439f3SHamish Morrison 	if (error != B_OK)
459d6d439f3SHamish Morrison 		return error;
460d6d439f3SHamish Morrison 
461d6d439f3SHamish Morrison 	{
462d6d439f3SHamish Morrison 		MutexLocker locker(sUserMutexTableLock);
463d6d439f3SHamish Morrison 		error = user_mutex_sem_acquire_locked(sem, wiringInfo.physicalAddress,
464d6d439f3SHamish Morrison 			name, flags | B_CAN_INTERRUPT, timeout, locker);
465d6d439f3SHamish Morrison 	}
466d6d439f3SHamish Morrison 
467d6d439f3SHamish Morrison 	vm_unwire_page(&wiringInfo);
468d6d439f3SHamish Morrison 	return syscall_restart_handle_timeout_post(error, timeout);
469d6d439f3SHamish Morrison }
470d6d439f3SHamish Morrison 
471d6d439f3SHamish Morrison 
472d6d439f3SHamish Morrison status_t
473d6d439f3SHamish Morrison _user_mutex_sem_release(int32* sem)
474d6d439f3SHamish Morrison {
475d6d439f3SHamish Morrison 	if (sem == NULL || !IS_USER_ADDRESS(sem) || (addr_t)sem % 4 != 0)
476d6d439f3SHamish Morrison 		return B_BAD_ADDRESS;
477d6d439f3SHamish Morrison 
478d6d439f3SHamish Morrison 	// wire the page and get the physical address
479d6d439f3SHamish Morrison 	VMPageWiringInfo wiringInfo;
480d6d439f3SHamish Morrison 	status_t error = vm_wire_page(B_CURRENT_TEAM, (addr_t)sem, true,
481d6d439f3SHamish Morrison 		&wiringInfo);
482d6d439f3SHamish Morrison 	if (error != B_OK)
483d6d439f3SHamish Morrison 		return error;
484d6d439f3SHamish Morrison 
485d6d439f3SHamish Morrison 	{
486d6d439f3SHamish Morrison 		MutexLocker locker(sUserMutexTableLock);
487d6d439f3SHamish Morrison 		user_mutex_sem_release_locked(sem, wiringInfo.physicalAddress);
488d6d439f3SHamish Morrison 	}
489d6d439f3SHamish Morrison 
490d6d439f3SHamish Morrison 	vm_unwire_page(&wiringInfo);
491d6d439f3SHamish Morrison 	return B_OK;
492d6d439f3SHamish Morrison }
493