1813d4cbeSIngo Weinhold /* 2813d4cbeSIngo Weinhold * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. 3813d4cbeSIngo Weinhold * Distributed under the terms of the MIT License. 4813d4cbeSIngo Weinhold */ 5813d4cbeSIngo Weinhold 6813d4cbeSIngo Weinhold 7813d4cbeSIngo Weinhold #include <user_mutex.h> 8813d4cbeSIngo Weinhold #include <user_mutex_defs.h> 9813d4cbeSIngo Weinhold 10813d4cbeSIngo Weinhold #include <condition_variable.h> 11813d4cbeSIngo Weinhold #include <kernel.h> 12813d4cbeSIngo Weinhold #include <lock.h> 13813d4cbeSIngo Weinhold #include <smp.h> 14813d4cbeSIngo Weinhold #include <syscall_restart.h> 15813d4cbeSIngo Weinhold #include <util/AutoLock.h> 16813d4cbeSIngo Weinhold #include <util/OpenHashTable.h> 17813d4cbeSIngo Weinhold #include <vm/vm.h> 18813d4cbeSIngo Weinhold #include <vm/VMArea.h> 19813d4cbeSIngo Weinhold 20813d4cbeSIngo Weinhold 21813d4cbeSIngo Weinhold struct UserMutexEntry; 22813d4cbeSIngo Weinhold typedef DoublyLinkedList<UserMutexEntry> UserMutexEntryList; 23813d4cbeSIngo Weinhold 24813d4cbeSIngo Weinhold struct UserMutexEntry : public DoublyLinkedListLinkImpl<UserMutexEntry> { 25813d4cbeSIngo Weinhold addr_t address; 26813d4cbeSIngo Weinhold ConditionVariable condition; 27813d4cbeSIngo Weinhold bool locked; 28813d4cbeSIngo Weinhold UserMutexEntryList otherEntries; 29813d4cbeSIngo Weinhold UserMutexEntry* hashNext; 30813d4cbeSIngo Weinhold }; 31813d4cbeSIngo Weinhold 32813d4cbeSIngo Weinhold struct UserMutexHashDefinition { 33813d4cbeSIngo Weinhold typedef addr_t KeyType; 34813d4cbeSIngo Weinhold typedef UserMutexEntry ValueType; 35813d4cbeSIngo Weinhold 36813d4cbeSIngo Weinhold size_t HashKey(addr_t key) const 37813d4cbeSIngo Weinhold { 38813d4cbeSIngo Weinhold return key >> 2; 39813d4cbeSIngo Weinhold } 40813d4cbeSIngo Weinhold 41813d4cbeSIngo Weinhold size_t Hash(const UserMutexEntry* value) const 42813d4cbeSIngo Weinhold { 43813d4cbeSIngo Weinhold return HashKey(value->address); 44813d4cbeSIngo Weinhold } 45813d4cbeSIngo Weinhold 46813d4cbeSIngo Weinhold bool Compare(addr_t key, const UserMutexEntry* value) const 47813d4cbeSIngo Weinhold { 48813d4cbeSIngo Weinhold return value->address == key; 49813d4cbeSIngo Weinhold } 50813d4cbeSIngo Weinhold 51813d4cbeSIngo Weinhold UserMutexEntry*& GetLink(UserMutexEntry* value) const 52813d4cbeSIngo Weinhold { 53813d4cbeSIngo Weinhold return value->hashNext; 54813d4cbeSIngo Weinhold } 55813d4cbeSIngo Weinhold }; 56813d4cbeSIngo Weinhold 57813d4cbeSIngo Weinhold typedef BOpenHashTable<UserMutexHashDefinition> UserMutexTable; 58813d4cbeSIngo Weinhold 59813d4cbeSIngo Weinhold 60813d4cbeSIngo Weinhold static UserMutexTable sUserMutexTable; 61813d4cbeSIngo Weinhold static mutex sUserMutexTableLock = MUTEX_INITIALIZER("user mutex table"); 62813d4cbeSIngo Weinhold 63813d4cbeSIngo Weinhold 64813d4cbeSIngo Weinhold static void 65813d4cbeSIngo Weinhold add_user_mutex_entry(UserMutexEntry* entry) 66813d4cbeSIngo Weinhold { 67813d4cbeSIngo Weinhold UserMutexEntry* firstEntry = sUserMutexTable.Lookup(entry->address); 68813d4cbeSIngo Weinhold if (firstEntry != NULL) 69813d4cbeSIngo Weinhold firstEntry->otherEntries.Add(entry); 70813d4cbeSIngo Weinhold else 71813d4cbeSIngo Weinhold sUserMutexTable.Insert(entry); 72813d4cbeSIngo Weinhold } 73813d4cbeSIngo Weinhold 74813d4cbeSIngo Weinhold 75813d4cbeSIngo Weinhold static bool 76813d4cbeSIngo Weinhold remove_user_mutex_entry(UserMutexEntry* entry) 77813d4cbeSIngo Weinhold { 78813d4cbeSIngo Weinhold UserMutexEntry* firstEntry = sUserMutexTable.Lookup(entry->address); 79813d4cbeSIngo Weinhold if (firstEntry != entry) { 80813d4cbeSIngo Weinhold // The entry is not the first entry in the table. Just remove it from 81813d4cbeSIngo Weinhold // the first entry's list. 82813d4cbeSIngo Weinhold firstEntry->otherEntries.Remove(entry); 83813d4cbeSIngo Weinhold return true; 84813d4cbeSIngo Weinhold } 85813d4cbeSIngo Weinhold 86813d4cbeSIngo Weinhold // The entry is the first entry in the table. Remove it from the table and, 87813d4cbeSIngo Weinhold // if any, add the next entry to the table. 88813d4cbeSIngo Weinhold sUserMutexTable.Remove(entry); 89813d4cbeSIngo Weinhold 900dffa8d4SIngo Weinhold firstEntry = entry->otherEntries.RemoveHead(); 91813d4cbeSIngo Weinhold if (firstEntry != NULL) { 92813d4cbeSIngo Weinhold firstEntry->otherEntries.MoveFrom(&entry->otherEntries); 93813d4cbeSIngo Weinhold sUserMutexTable.Insert(firstEntry); 94813d4cbeSIngo Weinhold return true; 95813d4cbeSIngo Weinhold } 96813d4cbeSIngo Weinhold 97813d4cbeSIngo Weinhold return false; 98813d4cbeSIngo Weinhold } 99813d4cbeSIngo Weinhold 100813d4cbeSIngo Weinhold 101813d4cbeSIngo Weinhold static status_t 102*73ad2473SPawel Dziepak user_mutex_lock_locked(int32* mutex, addr_t physicalAddress, const char* name, 103813d4cbeSIngo Weinhold uint32 flags, bigtime_t timeout, MutexLocker& locker) 104813d4cbeSIngo Weinhold { 105813d4cbeSIngo Weinhold // mark the mutex locked + waiting 106813d4cbeSIngo Weinhold int32 oldValue = atomic_or(mutex, 107813d4cbeSIngo Weinhold B_USER_MUTEX_LOCKED | B_USER_MUTEX_WAITING); 108813d4cbeSIngo Weinhold 109813d4cbeSIngo Weinhold // The mutex might have been unlocked (or disabled) in the meantime. 110813d4cbeSIngo Weinhold if ((oldValue & (B_USER_MUTEX_LOCKED | B_USER_MUTEX_WAITING)) == 0 111813d4cbeSIngo Weinhold || (oldValue & B_USER_MUTEX_DISABLED) != 0) { 112813d4cbeSIngo Weinhold // clear the waiting flag and be done 113813d4cbeSIngo Weinhold atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING); 114813d4cbeSIngo Weinhold return B_OK; 115813d4cbeSIngo Weinhold } 116813d4cbeSIngo Weinhold 117813d4cbeSIngo Weinhold // we have to wait 118813d4cbeSIngo Weinhold 119813d4cbeSIngo Weinhold // add the entry to the table 120813d4cbeSIngo Weinhold UserMutexEntry entry; 121813d4cbeSIngo Weinhold entry.address = physicalAddress; 122813d4cbeSIngo Weinhold entry.locked = false; 123813d4cbeSIngo Weinhold add_user_mutex_entry(&entry); 124813d4cbeSIngo Weinhold 125813d4cbeSIngo Weinhold // wait 126813d4cbeSIngo Weinhold ConditionVariableEntry waitEntry; 127813d4cbeSIngo Weinhold entry.condition.Init((void*)physicalAddress, "user mutex"); 128813d4cbeSIngo Weinhold entry.condition.Add(&waitEntry); 129813d4cbeSIngo Weinhold 130813d4cbeSIngo Weinhold locker.Unlock(); 131813d4cbeSIngo Weinhold status_t error = waitEntry.Wait(flags, timeout); 132813d4cbeSIngo Weinhold locker.Lock(); 133813d4cbeSIngo Weinhold 134813d4cbeSIngo Weinhold // dequeue 135813d4cbeSIngo Weinhold if (!remove_user_mutex_entry(&entry)) { 136813d4cbeSIngo Weinhold // no one is waiting anymore -- clear the waiting flag 137813d4cbeSIngo Weinhold atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING); 138813d4cbeSIngo Weinhold } 139813d4cbeSIngo Weinhold 140813d4cbeSIngo Weinhold if (error != B_OK 141813d4cbeSIngo Weinhold && (entry.locked || (*mutex & B_USER_MUTEX_DISABLED) != 0)) { 142813d4cbeSIngo Weinhold // timeout or interrupt, but the mutex was unlocked or disabled in time 143813d4cbeSIngo Weinhold error = B_OK; 144813d4cbeSIngo Weinhold } 145813d4cbeSIngo Weinhold 146813d4cbeSIngo Weinhold return error; 147813d4cbeSIngo Weinhold } 148813d4cbeSIngo Weinhold 149813d4cbeSIngo Weinhold 150813d4cbeSIngo Weinhold static void 151*73ad2473SPawel Dziepak user_mutex_unlock_locked(int32* mutex, addr_t physicalAddress, uint32 flags) 152813d4cbeSIngo Weinhold { 153813d4cbeSIngo Weinhold if (UserMutexEntry* entry = sUserMutexTable.Lookup(physicalAddress)) { 1540dffa8d4SIngo Weinhold // Someone is waiting -- set the locked flag. It might still be set, 1550dffa8d4SIngo Weinhold // but when using userland atomic operations, the caller will usually 1560dffa8d4SIngo Weinhold // have cleared it already. 1570dffa8d4SIngo Weinhold int32 oldValue = atomic_or(mutex, B_USER_MUTEX_LOCKED); 1580dffa8d4SIngo Weinhold 159813d4cbeSIngo Weinhold // unblock the first thread 160813d4cbeSIngo Weinhold entry->locked = true; 161813d4cbeSIngo Weinhold entry->condition.NotifyOne(); 162813d4cbeSIngo Weinhold 163813d4cbeSIngo Weinhold if ((flags & B_USER_MUTEX_UNBLOCK_ALL) != 0 1640dffa8d4SIngo Weinhold || (oldValue & B_USER_MUTEX_DISABLED) != 0) { 165813d4cbeSIngo Weinhold // unblock all the other waiting threads as well 166813d4cbeSIngo Weinhold for (UserMutexEntryList::Iterator it 167813d4cbeSIngo Weinhold = entry->otherEntries.GetIterator(); 168813d4cbeSIngo Weinhold UserMutexEntry* otherEntry = it.Next();) { 169813d4cbeSIngo Weinhold otherEntry->locked = true; 170813d4cbeSIngo Weinhold otherEntry->condition.NotifyOne(); 171813d4cbeSIngo Weinhold } 172813d4cbeSIngo Weinhold } 1730dffa8d4SIngo Weinhold } else { 1740dffa8d4SIngo Weinhold // no one is waiting -- clear locked flag 1750dffa8d4SIngo Weinhold atomic_and(mutex, ~(int32)B_USER_MUTEX_LOCKED); 176813d4cbeSIngo Weinhold } 177813d4cbeSIngo Weinhold } 178813d4cbeSIngo Weinhold 179813d4cbeSIngo Weinhold 180813d4cbeSIngo Weinhold static status_t 181813d4cbeSIngo Weinhold user_mutex_lock(int32* mutex, const char* name, uint32 flags, bigtime_t timeout) 182813d4cbeSIngo Weinhold { 183813d4cbeSIngo Weinhold // wire the page and get the physical address 184813d4cbeSIngo Weinhold VMPageWiringInfo wiringInfo; 185813d4cbeSIngo Weinhold status_t error = vm_wire_page(B_CURRENT_TEAM, (addr_t)mutex, true, 186813d4cbeSIngo Weinhold &wiringInfo); 187813d4cbeSIngo Weinhold if (error != B_OK) 188813d4cbeSIngo Weinhold return error; 189813d4cbeSIngo Weinhold 190813d4cbeSIngo Weinhold // get the lock 191813d4cbeSIngo Weinhold { 192813d4cbeSIngo Weinhold MutexLocker locker(sUserMutexTableLock); 193813d4cbeSIngo Weinhold error = user_mutex_lock_locked(mutex, wiringInfo.physicalAddress, name, 194813d4cbeSIngo Weinhold flags, timeout, locker); 195813d4cbeSIngo Weinhold } 196813d4cbeSIngo Weinhold 197813d4cbeSIngo Weinhold // unwire the page 198813d4cbeSIngo Weinhold vm_unwire_page(&wiringInfo); 199813d4cbeSIngo Weinhold 200813d4cbeSIngo Weinhold return error; 201813d4cbeSIngo Weinhold } 202813d4cbeSIngo Weinhold 203813d4cbeSIngo Weinhold 204813d4cbeSIngo Weinhold static status_t 205813d4cbeSIngo Weinhold user_mutex_switch_lock(int32* fromMutex, int32* toMutex, const char* name, 206813d4cbeSIngo Weinhold uint32 flags, bigtime_t timeout) 207813d4cbeSIngo Weinhold { 208813d4cbeSIngo Weinhold // wire the pages and get the physical addresses 209813d4cbeSIngo Weinhold VMPageWiringInfo fromWiringInfo; 210813d4cbeSIngo Weinhold status_t error = vm_wire_page(B_CURRENT_TEAM, (addr_t)fromMutex, true, 211813d4cbeSIngo Weinhold &fromWiringInfo); 212813d4cbeSIngo Weinhold if (error != B_OK) 213813d4cbeSIngo Weinhold return error; 214813d4cbeSIngo Weinhold 215813d4cbeSIngo Weinhold VMPageWiringInfo toWiringInfo; 216813d4cbeSIngo Weinhold error = vm_wire_page(B_CURRENT_TEAM, (addr_t)toMutex, true, &toWiringInfo); 217813d4cbeSIngo Weinhold if (error != B_OK) { 218813d4cbeSIngo Weinhold vm_unwire_page(&fromWiringInfo); 219813d4cbeSIngo Weinhold return error; 220813d4cbeSIngo Weinhold } 221813d4cbeSIngo Weinhold 222813d4cbeSIngo Weinhold // unlock the first mutex and lock the second one 223813d4cbeSIngo Weinhold { 224813d4cbeSIngo Weinhold MutexLocker locker(sUserMutexTableLock); 225813d4cbeSIngo Weinhold user_mutex_unlock_locked(fromMutex, fromWiringInfo.physicalAddress, 226813d4cbeSIngo Weinhold flags); 227813d4cbeSIngo Weinhold 228813d4cbeSIngo Weinhold error = user_mutex_lock_locked(toMutex, toWiringInfo.physicalAddress, 229813d4cbeSIngo Weinhold name, flags, timeout, locker); 230813d4cbeSIngo Weinhold } 231813d4cbeSIngo Weinhold 232813d4cbeSIngo Weinhold // unwire the pages 233813d4cbeSIngo Weinhold vm_unwire_page(&toWiringInfo); 234813d4cbeSIngo Weinhold vm_unwire_page(&fromWiringInfo); 235813d4cbeSIngo Weinhold 236813d4cbeSIngo Weinhold return error; 237813d4cbeSIngo Weinhold } 238813d4cbeSIngo Weinhold 239813d4cbeSIngo Weinhold 240813d4cbeSIngo Weinhold // #pragma mark - kernel private 241813d4cbeSIngo Weinhold 242813d4cbeSIngo Weinhold 243813d4cbeSIngo Weinhold void 244813d4cbeSIngo Weinhold user_mutex_init() 245813d4cbeSIngo Weinhold { 246813d4cbeSIngo Weinhold if (sUserMutexTable.Init() != B_OK) 247813d4cbeSIngo Weinhold panic("user_mutex_init(): Failed to init table!"); 248813d4cbeSIngo Weinhold } 249813d4cbeSIngo Weinhold 250813d4cbeSIngo Weinhold 251813d4cbeSIngo Weinhold // #pragma mark - syscalls 252813d4cbeSIngo Weinhold 253813d4cbeSIngo Weinhold 254813d4cbeSIngo Weinhold status_t 255813d4cbeSIngo Weinhold _user_mutex_lock(int32* mutex, const char* name, uint32 flags, 256813d4cbeSIngo Weinhold bigtime_t timeout) 257813d4cbeSIngo Weinhold { 258813d4cbeSIngo Weinhold if (mutex == NULL || !IS_USER_ADDRESS(mutex) || (addr_t)mutex % 4 != 0) 259813d4cbeSIngo Weinhold return B_BAD_ADDRESS; 260813d4cbeSIngo Weinhold 261813d4cbeSIngo Weinhold syscall_restart_handle_timeout_pre(flags, timeout); 262813d4cbeSIngo Weinhold 263813d4cbeSIngo Weinhold status_t error = user_mutex_lock(mutex, name, flags | B_CAN_INTERRUPT, 264813d4cbeSIngo Weinhold timeout); 265813d4cbeSIngo Weinhold 266813d4cbeSIngo Weinhold return syscall_restart_handle_timeout_post(error, timeout); 267813d4cbeSIngo Weinhold } 268813d4cbeSIngo Weinhold 269813d4cbeSIngo Weinhold 270813d4cbeSIngo Weinhold status_t 271813d4cbeSIngo Weinhold _user_mutex_unlock(int32* mutex, uint32 flags) 272813d4cbeSIngo Weinhold { 273813d4cbeSIngo Weinhold if (mutex == NULL || !IS_USER_ADDRESS(mutex) || (addr_t)mutex % 4 != 0) 274813d4cbeSIngo Weinhold return B_BAD_ADDRESS; 275813d4cbeSIngo Weinhold 276813d4cbeSIngo Weinhold // wire the page and get the physical address 277813d4cbeSIngo Weinhold VMPageWiringInfo wiringInfo; 278813d4cbeSIngo Weinhold status_t error = vm_wire_page(B_CURRENT_TEAM, (addr_t)mutex, true, 279813d4cbeSIngo Weinhold &wiringInfo); 280813d4cbeSIngo Weinhold if (error != B_OK) 281813d4cbeSIngo Weinhold return error; 282813d4cbeSIngo Weinhold 283813d4cbeSIngo Weinhold { 284813d4cbeSIngo Weinhold MutexLocker locker(sUserMutexTableLock); 285813d4cbeSIngo Weinhold user_mutex_unlock_locked(mutex, wiringInfo.physicalAddress, flags); 286813d4cbeSIngo Weinhold } 287813d4cbeSIngo Weinhold 288813d4cbeSIngo Weinhold vm_unwire_page(&wiringInfo); 289813d4cbeSIngo Weinhold 290813d4cbeSIngo Weinhold return B_OK; 291813d4cbeSIngo Weinhold } 292813d4cbeSIngo Weinhold 293813d4cbeSIngo Weinhold 294813d4cbeSIngo Weinhold status_t 295813d4cbeSIngo Weinhold _user_mutex_switch_lock(int32* fromMutex, int32* toMutex, const char* name, 296813d4cbeSIngo Weinhold uint32 flags, bigtime_t timeout) 297813d4cbeSIngo Weinhold { 298813d4cbeSIngo Weinhold if (fromMutex == NULL || !IS_USER_ADDRESS(fromMutex) 299813d4cbeSIngo Weinhold || (addr_t)fromMutex % 4 != 0 || toMutex == NULL 300813d4cbeSIngo Weinhold || !IS_USER_ADDRESS(toMutex) || (addr_t)toMutex % 4 != 0) { 301813d4cbeSIngo Weinhold return B_BAD_ADDRESS; 302813d4cbeSIngo Weinhold } 303813d4cbeSIngo Weinhold 3040dffa8d4SIngo Weinhold return user_mutex_switch_lock(fromMutex, toMutex, name, 305813d4cbeSIngo Weinhold flags | B_CAN_INTERRUPT, timeout); 306813d4cbeSIngo Weinhold } 307