1813d4cbeSIngo Weinhold /* 20ab9f280SAugustin Cavalier * Copyright 2023, Haiku, Inc. All rights reserved. 39dd4d2ddSJérôme Duval * Copyright 2018, Jérôme Duval, jerome.duval@gmail.com. 4d6d439f3SHamish Morrison * Copyright 2015, Hamish Morrison, hamishm53@gmail.com. 5813d4cbeSIngo Weinhold * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. 6813d4cbeSIngo Weinhold * Distributed under the terms of the MIT License. 7813d4cbeSIngo Weinhold */ 8813d4cbeSIngo Weinhold 9813d4cbeSIngo Weinhold 10813d4cbeSIngo Weinhold #include <user_mutex.h> 11813d4cbeSIngo Weinhold #include <user_mutex_defs.h> 12813d4cbeSIngo Weinhold 13813d4cbeSIngo Weinhold #include <condition_variable.h> 14813d4cbeSIngo Weinhold #include <kernel.h> 15813d4cbeSIngo Weinhold #include <lock.h> 16813d4cbeSIngo Weinhold #include <smp.h> 17813d4cbeSIngo Weinhold #include <syscall_restart.h> 18813d4cbeSIngo Weinhold #include <util/AutoLock.h> 1993d7d1c5SAugustin Cavalier #include <util/ThreadAutoLock.h> 20813d4cbeSIngo Weinhold #include <util/OpenHashTable.h> 21813d4cbeSIngo Weinhold #include <vm/vm.h> 22813d4cbeSIngo Weinhold #include <vm/VMArea.h> 23fb688aa1SAugustin Cavalier #include <arch/generic/user_memory.h> 24813d4cbeSIngo Weinhold 25813d4cbeSIngo Weinhold 260ab9f280SAugustin Cavalier /*! One UserMutexEntry corresponds to one mutex address. 270ab9f280SAugustin Cavalier * 280ab9f280SAugustin Cavalier * The mutex's "waiting" state is controlled by the rw_lock: a waiter acquires 290ab9f280SAugustin Cavalier * a "read" lock before initiating a wait, and an unblocker acquires a "write" 300ab9f280SAugustin Cavalier * lock. That way, unblockers can be sure that no waiters will start waiting 310ab9f280SAugustin Cavalier * during unblock, and they can thus safely (without races) unset WAITING. 320ab9f280SAugustin Cavalier */ 3313491fd2SAugustin Cavalier struct UserMutexEntry { 3493d7d1c5SAugustin Cavalier generic_addr_t address; 3513491fd2SAugustin Cavalier UserMutexEntry* hash_next; 3613491fd2SAugustin Cavalier int32 ref_count; 3713491fd2SAugustin Cavalier 380ab9f280SAugustin Cavalier rw_lock lock; 39813d4cbeSIngo Weinhold ConditionVariable condition; 40813d4cbeSIngo Weinhold }; 41813d4cbeSIngo Weinhold 42813d4cbeSIngo Weinhold struct UserMutexHashDefinition { 4393d7d1c5SAugustin Cavalier typedef generic_addr_t KeyType; 44813d4cbeSIngo Weinhold typedef UserMutexEntry ValueType; 45813d4cbeSIngo Weinhold 4693d7d1c5SAugustin Cavalier size_t HashKey(generic_addr_t key) const 47813d4cbeSIngo Weinhold { 48813d4cbeSIngo Weinhold return key >> 2; 49813d4cbeSIngo Weinhold } 50813d4cbeSIngo Weinhold 51813d4cbeSIngo Weinhold size_t Hash(const UserMutexEntry* value) const 52813d4cbeSIngo Weinhold { 53813d4cbeSIngo Weinhold return HashKey(value->address); 54813d4cbeSIngo Weinhold } 55813d4cbeSIngo Weinhold 5693d7d1c5SAugustin Cavalier bool Compare(generic_addr_t key, const UserMutexEntry* value) const 57813d4cbeSIngo Weinhold { 58813d4cbeSIngo Weinhold return value->address == key; 59813d4cbeSIngo Weinhold } 60813d4cbeSIngo Weinhold 61813d4cbeSIngo Weinhold UserMutexEntry*& GetLink(UserMutexEntry* value) const 62813d4cbeSIngo Weinhold { 6313491fd2SAugustin Cavalier return value->hash_next; 64813d4cbeSIngo Weinhold } 65813d4cbeSIngo Weinhold }; 66813d4cbeSIngo Weinhold 67813d4cbeSIngo Weinhold typedef BOpenHashTable<UserMutexHashDefinition> UserMutexTable; 68813d4cbeSIngo Weinhold 69813d4cbeSIngo Weinhold 7093d7d1c5SAugustin Cavalier struct user_mutex_context { 7193d7d1c5SAugustin Cavalier UserMutexTable table; 7293d7d1c5SAugustin Cavalier rw_lock lock; 7393d7d1c5SAugustin Cavalier }; 7493d7d1c5SAugustin Cavalier static user_mutex_context sSharedUserMutexContext; 75c5a0df24SAugustin Cavalier static const char* kUserMutexEntryType = "umtx entry"; 76813d4cbeSIngo Weinhold 77813d4cbeSIngo Weinhold 78cf1b26a9SAugustin Cavalier // #pragma mark - user atomics 79cf1b26a9SAugustin Cavalier 80cf1b26a9SAugustin Cavalier 81cf1b26a9SAugustin Cavalier static int32 82fb688aa1SAugustin Cavalier user_atomic_or(int32* value, int32 orValue, bool isWired) 83cf1b26a9SAugustin Cavalier { 84fb688aa1SAugustin Cavalier int32 result; 85fb688aa1SAugustin Cavalier if (isWired) { 86cf1b26a9SAugustin Cavalier set_ac(); 87fb688aa1SAugustin Cavalier result = atomic_or(value, orValue); 88cf1b26a9SAugustin Cavalier clear_ac(); 89cf1b26a9SAugustin Cavalier return result; 90cf1b26a9SAugustin Cavalier } 91cf1b26a9SAugustin Cavalier 92fb688aa1SAugustin Cavalier return user_access([=, &result] { 93fb688aa1SAugustin Cavalier result = atomic_or(value, orValue); 94fb688aa1SAugustin Cavalier }) ? result : INT32_MIN; 95cf1b26a9SAugustin Cavalier } 96cf1b26a9SAugustin Cavalier 97cf1b26a9SAugustin Cavalier 98cf1b26a9SAugustin Cavalier static int32 99fb688aa1SAugustin Cavalier user_atomic_and(int32* value, int32 andValue, bool isWired) 100cf1b26a9SAugustin Cavalier { 101fb688aa1SAugustin Cavalier int32 result; 102fb688aa1SAugustin Cavalier if (isWired) { 103cf1b26a9SAugustin Cavalier set_ac(); 104fb688aa1SAugustin Cavalier result = atomic_and(value, andValue); 105cf1b26a9SAugustin Cavalier clear_ac(); 106cf1b26a9SAugustin Cavalier return result; 107cf1b26a9SAugustin Cavalier } 108cf1b26a9SAugustin Cavalier 109fb688aa1SAugustin Cavalier return user_access([=, &result] { 110fb688aa1SAugustin Cavalier result = atomic_and(value, andValue); 111fb688aa1SAugustin Cavalier }) ? result : INT32_MIN; 112fb688aa1SAugustin Cavalier } 113fb688aa1SAugustin Cavalier 114cf1b26a9SAugustin Cavalier 115cf1b26a9SAugustin Cavalier static int32 116fb688aa1SAugustin Cavalier user_atomic_get(int32* value, bool isWired) 117cf1b26a9SAugustin Cavalier { 118fb688aa1SAugustin Cavalier int32 result; 119fb688aa1SAugustin Cavalier if (isWired) { 120cf1b26a9SAugustin Cavalier set_ac(); 121fb688aa1SAugustin Cavalier result = atomic_get(value); 122cf1b26a9SAugustin Cavalier clear_ac(); 123cf1b26a9SAugustin Cavalier return result; 124cf1b26a9SAugustin Cavalier } 125cf1b26a9SAugustin Cavalier 126fb688aa1SAugustin Cavalier return user_access([=, &result] { 127fb688aa1SAugustin Cavalier result = atomic_get(value); 128fb688aa1SAugustin Cavalier }) ? result : INT32_MIN; 129fb688aa1SAugustin Cavalier } 130fb688aa1SAugustin Cavalier 131fb688aa1SAugustin Cavalier 132fb688aa1SAugustin Cavalier static int32 133fb688aa1SAugustin Cavalier user_atomic_test_and_set(int32* value, int32 newValue, int32 testAgainst, 134fb688aa1SAugustin Cavalier bool isWired) 135fb688aa1SAugustin Cavalier { 136fb688aa1SAugustin Cavalier int32 result; 137fb688aa1SAugustin Cavalier if (isWired) { 138fb688aa1SAugustin Cavalier set_ac(); 139fb688aa1SAugustin Cavalier result = atomic_test_and_set(value, newValue, testAgainst); 140fb688aa1SAugustin Cavalier clear_ac(); 141fb688aa1SAugustin Cavalier return result; 142fb688aa1SAugustin Cavalier } 143fb688aa1SAugustin Cavalier 144fb688aa1SAugustin Cavalier return user_access([=, &result] { 145fb688aa1SAugustin Cavalier result = atomic_test_and_set(value, newValue, testAgainst); 146fb688aa1SAugustin Cavalier }) ? result : INT32_MIN; 147fb688aa1SAugustin Cavalier } 148fb688aa1SAugustin Cavalier 149cf1b26a9SAugustin Cavalier 15093d7d1c5SAugustin Cavalier // #pragma mark - user mutex context 15193d7d1c5SAugustin Cavalier 15293d7d1c5SAugustin Cavalier 153c5a0df24SAugustin Cavalier static int 154c5a0df24SAugustin Cavalier dump_user_mutex(int argc, char** argv) 155c5a0df24SAugustin Cavalier { 156c5a0df24SAugustin Cavalier if (argc != 2) { 157c5a0df24SAugustin Cavalier print_debugger_command_usage(argv[0]); 158c5a0df24SAugustin Cavalier return 0; 159c5a0df24SAugustin Cavalier } 160c5a0df24SAugustin Cavalier 161c5a0df24SAugustin Cavalier addr_t threadID = parse_expression(argv[1]); 162c5a0df24SAugustin Cavalier if (threadID == 0) 163c5a0df24SAugustin Cavalier return 0; 164c5a0df24SAugustin Cavalier 165c5a0df24SAugustin Cavalier Thread* thread = Thread::GetDebug(threadID); 166c5a0df24SAugustin Cavalier if (thread == NULL) { 167c5a0df24SAugustin Cavalier kprintf("no such thread\n"); 168c5a0df24SAugustin Cavalier return 0; 169c5a0df24SAugustin Cavalier } 170c5a0df24SAugustin Cavalier 171c5a0df24SAugustin Cavalier if (thread->wait.type != THREAD_BLOCK_TYPE_CONDITION_VARIABLE) { 172c5a0df24SAugustin Cavalier kprintf("thread is not blocked on cvar (thus not user_mutex)\n"); 173c5a0df24SAugustin Cavalier return 0; 174c5a0df24SAugustin Cavalier } 175c5a0df24SAugustin Cavalier 176c5a0df24SAugustin Cavalier ConditionVariable* variable = (ConditionVariable*)thread->wait.object; 177c5a0df24SAugustin Cavalier if (variable->ObjectType() != kUserMutexEntryType) { 178c5a0df24SAugustin Cavalier kprintf("thread is not blocked on user_mutex\n"); 179c5a0df24SAugustin Cavalier return 0; 180c5a0df24SAugustin Cavalier } 181c5a0df24SAugustin Cavalier 182c5a0df24SAugustin Cavalier UserMutexEntry* entry = (UserMutexEntry*)variable->Object(); 183c5a0df24SAugustin Cavalier 184c5a0df24SAugustin Cavalier const bool physical = (sSharedUserMutexContext.table.Lookup(entry->address) == entry); 185c5a0df24SAugustin Cavalier kprintf("user mutex entry %p\n", entry); 186c5a0df24SAugustin Cavalier kprintf(" address: 0x%" B_PRIxPHYSADDR " (%s)\n", entry->address, 187c5a0df24SAugustin Cavalier physical ? "physical" : "virtual"); 188c5a0df24SAugustin Cavalier kprintf(" refcount: %" B_PRId32 "\n", entry->ref_count); 189c5a0df24SAugustin Cavalier kprintf(" lock: %p\n", &entry->lock); 190c5a0df24SAugustin Cavalier 191c5a0df24SAugustin Cavalier int32 mutex = 0; 192c5a0df24SAugustin Cavalier status_t status = B_ERROR; 193c5a0df24SAugustin Cavalier if (!physical) { 194c5a0df24SAugustin Cavalier status = debug_memcpy(thread->team->id, &mutex, 195c5a0df24SAugustin Cavalier (void*)entry->address, sizeof(mutex)); 196c5a0df24SAugustin Cavalier } 197c5a0df24SAugustin Cavalier 198c5a0df24SAugustin Cavalier if (status == B_OK) 199c5a0df24SAugustin Cavalier kprintf(" mutex: 0x%" B_PRIx32 "\n", mutex); 200c5a0df24SAugustin Cavalier 201c5a0df24SAugustin Cavalier entry->condition.Dump(); 202c5a0df24SAugustin Cavalier 203c5a0df24SAugustin Cavalier return 0; 204c5a0df24SAugustin Cavalier } 205c5a0df24SAugustin Cavalier 206c5a0df24SAugustin Cavalier 20793d7d1c5SAugustin Cavalier void 20893d7d1c5SAugustin Cavalier user_mutex_init() 20993d7d1c5SAugustin Cavalier { 21093d7d1c5SAugustin Cavalier sSharedUserMutexContext.lock = RW_LOCK_INITIALIZER("shared user mutex table"); 21193d7d1c5SAugustin Cavalier if (sSharedUserMutexContext.table.Init() != B_OK) 21293d7d1c5SAugustin Cavalier panic("user_mutex_init(): Failed to init table!"); 213c5a0df24SAugustin Cavalier 214c5a0df24SAugustin Cavalier add_debugger_command_etc("user_mutex", &dump_user_mutex, 215c5a0df24SAugustin Cavalier "Dump user-mutex info", 216c5a0df24SAugustin Cavalier "<thread>\n" 217c5a0df24SAugustin Cavalier "Prints info about the user-mutex a thread is blocked on.\n" 218c5a0df24SAugustin Cavalier " <thread> - Thread ID that is blocked on a user mutex\n", 0); 21993d7d1c5SAugustin Cavalier } 22093d7d1c5SAugustin Cavalier 22193d7d1c5SAugustin Cavalier 22293d7d1c5SAugustin Cavalier struct user_mutex_context* 22393d7d1c5SAugustin Cavalier get_team_user_mutex_context() 22493d7d1c5SAugustin Cavalier { 22593d7d1c5SAugustin Cavalier struct user_mutex_context* context = 22693d7d1c5SAugustin Cavalier thread_get_current_thread()->team->user_mutex_context; 22793d7d1c5SAugustin Cavalier if (context != NULL) 22893d7d1c5SAugustin Cavalier return context; 22993d7d1c5SAugustin Cavalier 23093d7d1c5SAugustin Cavalier Team* team = thread_get_current_thread()->team; 23193d7d1c5SAugustin Cavalier TeamLocker teamLocker(team); 23293d7d1c5SAugustin Cavalier if (team->user_mutex_context != NULL) 23393d7d1c5SAugustin Cavalier return team->user_mutex_context; 23493d7d1c5SAugustin Cavalier 23593d7d1c5SAugustin Cavalier context = new(std::nothrow) user_mutex_context; 23693d7d1c5SAugustin Cavalier if (context == NULL) 23793d7d1c5SAugustin Cavalier return NULL; 23893d7d1c5SAugustin Cavalier 23993d7d1c5SAugustin Cavalier context->lock = RW_LOCK_INITIALIZER("user mutex table"); 24093d7d1c5SAugustin Cavalier if (context->table.Init() != B_OK) { 24193d7d1c5SAugustin Cavalier delete context; 24293d7d1c5SAugustin Cavalier return NULL; 24393d7d1c5SAugustin Cavalier } 24493d7d1c5SAugustin Cavalier 24593d7d1c5SAugustin Cavalier team->user_mutex_context = context; 24693d7d1c5SAugustin Cavalier return context; 24793d7d1c5SAugustin Cavalier } 24893d7d1c5SAugustin Cavalier 24993d7d1c5SAugustin Cavalier 25093d7d1c5SAugustin Cavalier void 25193d7d1c5SAugustin Cavalier delete_user_mutex_context(struct user_mutex_context* context) 25293d7d1c5SAugustin Cavalier { 25393d7d1c5SAugustin Cavalier if (context == NULL) 25493d7d1c5SAugustin Cavalier return; 25593d7d1c5SAugustin Cavalier 25693d7d1c5SAugustin Cavalier // This should be empty at this point in team destruction. 25793d7d1c5SAugustin Cavalier ASSERT(context->table.IsEmpty()); 25893d7d1c5SAugustin Cavalier delete context; 25993d7d1c5SAugustin Cavalier } 260cf1b26a9SAugustin Cavalier 261cf1b26a9SAugustin Cavalier 26213491fd2SAugustin Cavalier static UserMutexEntry* 26393d7d1c5SAugustin Cavalier get_user_mutex_entry(struct user_mutex_context* context, 26493d7d1c5SAugustin Cavalier generic_addr_t address, bool noInsert = false, bool alreadyLocked = false) 265813d4cbeSIngo Weinhold { 2660ab9f280SAugustin Cavalier ReadLocker tableReadLocker; 2670ab9f280SAugustin Cavalier if (!alreadyLocked) 26893d7d1c5SAugustin Cavalier tableReadLocker.SetTo(context->lock, false); 2690ab9f280SAugustin Cavalier 27093d7d1c5SAugustin Cavalier UserMutexEntry* entry = context->table.Lookup(address); 27113491fd2SAugustin Cavalier if (entry != NULL) { 27213491fd2SAugustin Cavalier atomic_add(&entry->ref_count, 1); 27313491fd2SAugustin Cavalier return entry; 2740ab9f280SAugustin Cavalier } else if (noInsert) 2750ab9f280SAugustin Cavalier return entry; 2760ab9f280SAugustin Cavalier 2770ab9f280SAugustin Cavalier tableReadLocker.Unlock(); 27893d7d1c5SAugustin Cavalier WriteLocker tableWriteLocker(context->lock); 2790ab9f280SAugustin Cavalier 28093d7d1c5SAugustin Cavalier entry = context->table.Lookup(address); 2810ab9f280SAugustin Cavalier if (entry != NULL) { 2820ab9f280SAugustin Cavalier atomic_add(&entry->ref_count, 1); 2830ab9f280SAugustin Cavalier return entry; 28413491fd2SAugustin Cavalier } 28513491fd2SAugustin Cavalier 28613491fd2SAugustin Cavalier entry = new(std::nothrow) UserMutexEntry; 28713491fd2SAugustin Cavalier if (entry == NULL) 28813491fd2SAugustin Cavalier return entry; 28913491fd2SAugustin Cavalier 29013491fd2SAugustin Cavalier entry->address = address; 29113491fd2SAugustin Cavalier entry->ref_count = 1; 2920ab9f280SAugustin Cavalier rw_lock_init(&entry->lock, "UserMutexEntry lock"); 293c5a0df24SAugustin Cavalier entry->condition.Init(entry, kUserMutexEntryType); 29413491fd2SAugustin Cavalier 29593d7d1c5SAugustin Cavalier context->table.Insert(entry); 29613491fd2SAugustin Cavalier return entry; 297813d4cbeSIngo Weinhold } 298813d4cbeSIngo Weinhold 299813d4cbeSIngo Weinhold 30013491fd2SAugustin Cavalier static void 30193d7d1c5SAugustin Cavalier put_user_mutex_entry(struct user_mutex_context* context, UserMutexEntry* entry) 302813d4cbeSIngo Weinhold { 3030ab9f280SAugustin Cavalier if (entry == NULL) 3040ab9f280SAugustin Cavalier return; 3050ab9f280SAugustin Cavalier 30693d7d1c5SAugustin Cavalier const generic_addr_t address = entry->address; 30713491fd2SAugustin Cavalier if (atomic_add(&entry->ref_count, -1) != 1) 30813491fd2SAugustin Cavalier return; 309813d4cbeSIngo Weinhold 31093d7d1c5SAugustin Cavalier WriteLocker tableWriteLocker(context->lock); 31113491fd2SAugustin Cavalier 31213491fd2SAugustin Cavalier // Was it removed & deleted while we were waiting for the lock? 31393d7d1c5SAugustin Cavalier if (context->table.Lookup(address) != entry) 31413491fd2SAugustin Cavalier return; 31513491fd2SAugustin Cavalier 31613491fd2SAugustin Cavalier // Or did someone else acquire a reference to it? 31713491fd2SAugustin Cavalier if (atomic_get(&entry->ref_count) > 0) 31813491fd2SAugustin Cavalier return; 31913491fd2SAugustin Cavalier 32093d7d1c5SAugustin Cavalier context->table.Remove(entry); 3210ab9f280SAugustin Cavalier tableWriteLocker.Unlock(); 3220ab9f280SAugustin Cavalier 3230ab9f280SAugustin Cavalier rw_lock_destroy(&entry->lock); 32413491fd2SAugustin Cavalier delete entry; 325813d4cbeSIngo Weinhold } 326813d4cbeSIngo Weinhold 327813d4cbeSIngo Weinhold 328813d4cbeSIngo Weinhold static status_t 3290ab9f280SAugustin Cavalier user_mutex_wait_locked(UserMutexEntry* entry, 3300ab9f280SAugustin Cavalier uint32 flags, bigtime_t timeout, ReadLocker& locker) 331813d4cbeSIngo Weinhold { 33213491fd2SAugustin Cavalier ConditionVariableEntry waiter; 33313491fd2SAugustin Cavalier entry->condition.Add(&waiter); 33413491fd2SAugustin Cavalier locker.Unlock(); 335813d4cbeSIngo Weinhold 3360ab9f280SAugustin Cavalier return waiter.Wait(flags, timeout); 337813d4cbeSIngo Weinhold } 338813d4cbeSIngo Weinhold 339d6d439f3SHamish Morrison 340901b48c2SAugustin Cavalier static bool 341fb688aa1SAugustin Cavalier user_mutex_prepare_to_lock(UserMutexEntry* entry, int32* mutex, bool isWired) 342d6d439f3SHamish Morrison { 3430ab9f280SAugustin Cavalier ASSERT_READ_LOCKED_RW_LOCK(&entry->lock); 3440ab9f280SAugustin Cavalier 345cf1b26a9SAugustin Cavalier int32 oldValue = user_atomic_or(mutex, 346fb688aa1SAugustin Cavalier B_USER_MUTEX_LOCKED | B_USER_MUTEX_WAITING, isWired); 3476f3f29c7SAugustin Cavalier if ((oldValue & B_USER_MUTEX_LOCKED) == 0 348d6d439f3SHamish Morrison || (oldValue & B_USER_MUTEX_DISABLED) != 0) { 3490ab9f280SAugustin Cavalier // possibly unset waiting flag 3500ab9f280SAugustin Cavalier if ((oldValue & B_USER_MUTEX_WAITING) == 0) { 3510ab9f280SAugustin Cavalier rw_lock_read_unlock(&entry->lock); 3520ab9f280SAugustin Cavalier rw_lock_write_lock(&entry->lock); 3530ab9f280SAugustin Cavalier if (entry->condition.EntriesCount() == 0) 354fb688aa1SAugustin Cavalier user_atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING, isWired); 3550ab9f280SAugustin Cavalier rw_lock_write_unlock(&entry->lock); 3560ab9f280SAugustin Cavalier rw_lock_read_lock(&entry->lock); 3570ab9f280SAugustin Cavalier } 358901b48c2SAugustin Cavalier return true; 359d6d439f3SHamish Morrison } 360d6d439f3SHamish Morrison 361901b48c2SAugustin Cavalier return false; 362901b48c2SAugustin Cavalier } 363901b48c2SAugustin Cavalier 364901b48c2SAugustin Cavalier 365901b48c2SAugustin Cavalier static status_t 3660ab9f280SAugustin Cavalier user_mutex_lock_locked(UserMutexEntry* entry, int32* mutex, 367fb688aa1SAugustin Cavalier uint32 flags, bigtime_t timeout, ReadLocker& locker, bool isWired) 368901b48c2SAugustin Cavalier { 369fb688aa1SAugustin Cavalier if (user_mutex_prepare_to_lock(entry, mutex, isWired)) 370901b48c2SAugustin Cavalier return B_OK; 371901b48c2SAugustin Cavalier 3720ab9f280SAugustin Cavalier status_t error = user_mutex_wait_locked(entry, flags, timeout, locker); 3730ab9f280SAugustin Cavalier 3740ab9f280SAugustin Cavalier // possibly unset waiting flag 3750ab9f280SAugustin Cavalier if (error != B_OK && entry->condition.EntriesCount() == 0) { 3760ab9f280SAugustin Cavalier WriteLocker writeLocker(entry->lock); 3770ab9f280SAugustin Cavalier if (entry->condition.EntriesCount() == 0) 378fb688aa1SAugustin Cavalier user_atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING, isWired); 3790ab9f280SAugustin Cavalier } 3800ab9f280SAugustin Cavalier 3810ab9f280SAugustin Cavalier return error; 382813d4cbeSIngo Weinhold } 383813d4cbeSIngo Weinhold 384813d4cbeSIngo Weinhold 385813d4cbeSIngo Weinhold static void 386fb688aa1SAugustin Cavalier user_mutex_unblock(UserMutexEntry* entry, int32* mutex, uint32 flags, bool isWired) 387813d4cbeSIngo Weinhold { 3880ab9f280SAugustin Cavalier WriteLocker entryLocker(entry->lock); 3890ab9f280SAugustin Cavalier if (entry->condition.EntriesCount() == 0) { 3900ab9f280SAugustin Cavalier // Nobody is actually waiting at present. 391fb688aa1SAugustin Cavalier user_atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING, isWired); 392fb67dbf0SHamish Morrison return; 393fb67dbf0SHamish Morrison } 394fb67dbf0SHamish Morrison 395ca458a2bSAugustin Cavalier int32 oldValue = 0; 396ca458a2bSAugustin Cavalier if ((flags & B_USER_MUTEX_UNBLOCK_ALL) == 0) { 39713491fd2SAugustin Cavalier // This is not merely an unblock, but a hand-off. 398fb688aa1SAugustin Cavalier oldValue = user_atomic_or(mutex, B_USER_MUTEX_LOCKED, isWired); 3996f3f29c7SAugustin Cavalier if ((oldValue & B_USER_MUTEX_LOCKED) != 0) 4006f3f29c7SAugustin Cavalier return; 401ca458a2bSAugustin Cavalier } 4020dffa8d4SIngo Weinhold 403813d4cbeSIngo Weinhold if ((flags & B_USER_MUTEX_UNBLOCK_ALL) != 0 4040dffa8d4SIngo Weinhold || (oldValue & B_USER_MUTEX_DISABLED) != 0) { 40513491fd2SAugustin Cavalier // unblock and dequeue all the waiting threads 40613491fd2SAugustin Cavalier entry->condition.NotifyAll(B_OK); 40713491fd2SAugustin Cavalier } else { 40813491fd2SAugustin Cavalier entry->condition.NotifyOne(B_OK); 409813d4cbeSIngo Weinhold } 410fb67dbf0SHamish Morrison 41113491fd2SAugustin Cavalier if (entry->condition.EntriesCount() == 0) 412fb688aa1SAugustin Cavalier user_atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING, isWired); 413813d4cbeSIngo Weinhold } 414813d4cbeSIngo Weinhold 415813d4cbeSIngo Weinhold 416813d4cbeSIngo Weinhold static status_t 4170ab9f280SAugustin Cavalier user_mutex_sem_acquire_locked(UserMutexEntry* entry, int32* sem, 418fb688aa1SAugustin Cavalier uint32 flags, bigtime_t timeout, ReadLocker& locker, bool isWired) 419d6d439f3SHamish Morrison { 420d6d439f3SHamish Morrison // The semaphore may have been released in the meantime, and we also 421d6d439f3SHamish Morrison // need to mark it as contended if it isn't already. 422fb688aa1SAugustin Cavalier int32 oldValue = user_atomic_get(sem, isWired); 423d6d439f3SHamish Morrison while (oldValue > -1) { 424fb688aa1SAugustin Cavalier int32 value = user_atomic_test_and_set(sem, oldValue - 1, oldValue, isWired); 425d6d439f3SHamish Morrison if (value == oldValue && value > 0) 426d6d439f3SHamish Morrison return B_OK; 427d6d439f3SHamish Morrison oldValue = value; 428d6d439f3SHamish Morrison } 429d6d439f3SHamish Morrison 4300ab9f280SAugustin Cavalier return user_mutex_wait_locked(entry, flags, 43113491fd2SAugustin Cavalier timeout, locker); 432d6d439f3SHamish Morrison } 433d6d439f3SHamish Morrison 434d6d439f3SHamish Morrison 435d6d439f3SHamish Morrison static void 436fb688aa1SAugustin Cavalier user_mutex_sem_release(UserMutexEntry* entry, int32* sem, bool isWired) 437d6d439f3SHamish Morrison { 438*2cc89328SAugustin Cavalier WriteLocker entryLocker(entry->lock); 439*2cc89328SAugustin Cavalier if (entry->condition.EntriesCount() == 0) { 440d6d439f3SHamish Morrison // no waiters - mark as uncontended and release 441fb688aa1SAugustin Cavalier int32 oldValue = user_atomic_get(sem, isWired); 442d6d439f3SHamish Morrison while (true) { 443d6d439f3SHamish Morrison int32 inc = oldValue < 0 ? 2 : 1; 444fb688aa1SAugustin Cavalier int32 value = user_atomic_test_and_set(sem, oldValue + inc, oldValue, isWired); 445d6d439f3SHamish Morrison if (value == oldValue) 446d6d439f3SHamish Morrison return; 447d6d439f3SHamish Morrison oldValue = value; 448d6d439f3SHamish Morrison } 449d6d439f3SHamish Morrison } 450d6d439f3SHamish Morrison 45113491fd2SAugustin Cavalier entry->condition.NotifyOne(B_OK); 45213491fd2SAugustin Cavalier if (entry->condition.EntriesCount() == 0) { 453d6d439f3SHamish Morrison // mark the semaphore uncontended 454fb688aa1SAugustin Cavalier user_atomic_test_and_set(sem, 0, -1, isWired); 455d6d439f3SHamish Morrison } 456d6d439f3SHamish Morrison } 457d6d439f3SHamish Morrison 458d6d439f3SHamish Morrison 459fb688aa1SAugustin Cavalier // #pragma mark - syscalls 460fb688aa1SAugustin Cavalier 461fb688aa1SAugustin Cavalier 462fb688aa1SAugustin Cavalier struct UserMutexContextFetcher { 463fb688aa1SAugustin Cavalier UserMutexContextFetcher(int32* mutex, uint32 flags) 464fb688aa1SAugustin Cavalier : 465fb688aa1SAugustin Cavalier fInitStatus(B_OK), 466fb688aa1SAugustin Cavalier fShared((flags & B_USER_MUTEX_SHARED) != 0), 467fb688aa1SAugustin Cavalier fAddress(0) 468fb688aa1SAugustin Cavalier { 469fb688aa1SAugustin Cavalier if (!fShared) { 470fb688aa1SAugustin Cavalier fContext = get_team_user_mutex_context(); 471fb688aa1SAugustin Cavalier if (fContext == NULL) { 472fb688aa1SAugustin Cavalier fInitStatus = B_NO_MEMORY; 473fb688aa1SAugustin Cavalier return; 474fb688aa1SAugustin Cavalier } 475fb688aa1SAugustin Cavalier 476fb688aa1SAugustin Cavalier fAddress = (addr_t)mutex; 477fb688aa1SAugustin Cavalier } else { 478fb688aa1SAugustin Cavalier fContext = &sSharedUserMutexContext; 479fb688aa1SAugustin Cavalier 480fb688aa1SAugustin Cavalier // wire the page and get the physical address 481fb688aa1SAugustin Cavalier fInitStatus = vm_wire_page(B_CURRENT_TEAM, (addr_t)mutex, true, 482fb688aa1SAugustin Cavalier &fWiringInfo); 483fb688aa1SAugustin Cavalier if (fInitStatus != B_OK) 484fb688aa1SAugustin Cavalier return; 485fb688aa1SAugustin Cavalier fAddress = fWiringInfo.physicalAddress; 486fb688aa1SAugustin Cavalier } 487fb688aa1SAugustin Cavalier } 488fb688aa1SAugustin Cavalier 489fb688aa1SAugustin Cavalier ~UserMutexContextFetcher() 490fb688aa1SAugustin Cavalier { 491fb688aa1SAugustin Cavalier if (fInitStatus != B_OK) 492fb688aa1SAugustin Cavalier return; 493fb688aa1SAugustin Cavalier 494fb688aa1SAugustin Cavalier if (fShared) 495fb688aa1SAugustin Cavalier vm_unwire_page(&fWiringInfo); 496fb688aa1SAugustin Cavalier } 497fb688aa1SAugustin Cavalier 498fb688aa1SAugustin Cavalier status_t InitCheck() const 499fb688aa1SAugustin Cavalier { return fInitStatus; } 500fb688aa1SAugustin Cavalier 501fb688aa1SAugustin Cavalier struct user_mutex_context* Context() const 502fb688aa1SAugustin Cavalier { return fContext; } 503fb688aa1SAugustin Cavalier 504fb688aa1SAugustin Cavalier generic_addr_t Address() const 505fb688aa1SAugustin Cavalier { return fAddress; } 506fb688aa1SAugustin Cavalier 507fb688aa1SAugustin Cavalier bool IsWired() const 508fb688aa1SAugustin Cavalier { return fShared; } 509fb688aa1SAugustin Cavalier 510fb688aa1SAugustin Cavalier private: 511fb688aa1SAugustin Cavalier status_t fInitStatus; 512fb688aa1SAugustin Cavalier bool fShared; 513fb688aa1SAugustin Cavalier struct user_mutex_context* fContext; 514fb688aa1SAugustin Cavalier VMPageWiringInfo fWiringInfo; 515fb688aa1SAugustin Cavalier generic_addr_t fAddress; 516fb688aa1SAugustin Cavalier }; 517fb688aa1SAugustin Cavalier 518fb688aa1SAugustin Cavalier 519d6d439f3SHamish Morrison static status_t 520813d4cbeSIngo Weinhold user_mutex_lock(int32* mutex, const char* name, uint32 flags, bigtime_t timeout) 521813d4cbeSIngo Weinhold { 522fb688aa1SAugustin Cavalier UserMutexContextFetcher contextFetcher(mutex, flags); 523fb688aa1SAugustin Cavalier if (contextFetcher.InitCheck() != B_OK) 524fb688aa1SAugustin Cavalier return contextFetcher.InitCheck(); 525813d4cbeSIngo Weinhold 526813d4cbeSIngo Weinhold // get the lock 527fb688aa1SAugustin Cavalier UserMutexEntry* entry = get_user_mutex_entry(contextFetcher.Context(), 528fb688aa1SAugustin Cavalier contextFetcher.Address()); 5290ab9f280SAugustin Cavalier if (entry == NULL) 5300ab9f280SAugustin Cavalier return B_NO_MEMORY; 531fb688aa1SAugustin Cavalier status_t error = B_OK; 532813d4cbeSIngo Weinhold { 5330ab9f280SAugustin Cavalier ReadLocker entryLocker(entry->lock); 5340ab9f280SAugustin Cavalier error = user_mutex_lock_locked(entry, mutex, 535fb688aa1SAugustin Cavalier flags, timeout, entryLocker, contextFetcher.IsWired()); 536813d4cbeSIngo Weinhold } 537fb688aa1SAugustin Cavalier put_user_mutex_entry(contextFetcher.Context(), entry); 538813d4cbeSIngo Weinhold 539813d4cbeSIngo Weinhold return error; 540813d4cbeSIngo Weinhold } 541813d4cbeSIngo Weinhold 542813d4cbeSIngo Weinhold 543813d4cbeSIngo Weinhold static status_t 54493d7d1c5SAugustin Cavalier user_mutex_switch_lock(int32* fromMutex, uint32 fromFlags, 54593d7d1c5SAugustin Cavalier int32* toMutex, const char* name, uint32 toFlags, bigtime_t timeout) 546813d4cbeSIngo Weinhold { 547fb688aa1SAugustin Cavalier UserMutexContextFetcher fromFetcher(fromMutex, fromFlags); 548fb688aa1SAugustin Cavalier if (fromFetcher.InitCheck() != B_OK) 549fb688aa1SAugustin Cavalier return fromFetcher.InitCheck(); 55093d7d1c5SAugustin Cavalier 551fb688aa1SAugustin Cavalier UserMutexContextFetcher toFetcher(toMutex, toFlags); 552fb688aa1SAugustin Cavalier if (toFetcher.InitCheck() != B_OK) 553fb688aa1SAugustin Cavalier return toFetcher.InitCheck(); 554813d4cbeSIngo Weinhold 555813d4cbeSIngo Weinhold // unlock the first mutex and lock the second one 5560ab9f280SAugustin Cavalier UserMutexEntry* fromEntry = NULL, 557fb688aa1SAugustin Cavalier *toEntry = get_user_mutex_entry(toFetcher.Context(), toFetcher.Address()); 5580ab9f280SAugustin Cavalier if (toEntry == NULL) 5590ab9f280SAugustin Cavalier return B_NO_MEMORY; 560fb688aa1SAugustin Cavalier status_t error = B_OK; 561813d4cbeSIngo Weinhold { 5620ab9f280SAugustin Cavalier ConditionVariableEntry waiter; 563901b48c2SAugustin Cavalier 5640ab9f280SAugustin Cavalier bool alreadyLocked = false; 5650ab9f280SAugustin Cavalier { 5660ab9f280SAugustin Cavalier ReadLocker entryLocker(toEntry->lock); 567fb688aa1SAugustin Cavalier alreadyLocked = user_mutex_prepare_to_lock(toEntry, toMutex, 568fb688aa1SAugustin Cavalier toFetcher.IsWired()); 5690ab9f280SAugustin Cavalier if (!alreadyLocked) 5700ab9f280SAugustin Cavalier toEntry->condition.Add(&waiter); 5710ab9f280SAugustin Cavalier } 572813d4cbeSIngo Weinhold 573fb688aa1SAugustin Cavalier const int32 oldValue = user_atomic_and(fromMutex, ~(int32)B_USER_MUTEX_LOCKED, 574fb688aa1SAugustin Cavalier fromFetcher.IsWired()); 575fb688aa1SAugustin Cavalier if ((oldValue & B_USER_MUTEX_WAITING) != 0) { 576fb688aa1SAugustin Cavalier fromEntry = get_user_mutex_entry(fromFetcher.Context(), 577fb688aa1SAugustin Cavalier fromFetcher.Address(), true); 578fb688aa1SAugustin Cavalier if (fromEntry != NULL) { 579fb688aa1SAugustin Cavalier user_mutex_unblock(fromEntry, fromMutex, fromFlags, 580fb688aa1SAugustin Cavalier fromFetcher.IsWired()); 581fb688aa1SAugustin Cavalier } 582fb688aa1SAugustin Cavalier } 5830ab9f280SAugustin Cavalier 5840ab9f280SAugustin Cavalier if (!alreadyLocked) 58593d7d1c5SAugustin Cavalier error = waiter.Wait(toFlags, timeout); 586813d4cbeSIngo Weinhold } 587fb688aa1SAugustin Cavalier put_user_mutex_entry(fromFetcher.Context(), fromEntry); 588fb688aa1SAugustin Cavalier put_user_mutex_entry(toFetcher.Context(), toEntry); 589813d4cbeSIngo Weinhold 590813d4cbeSIngo Weinhold return error; 591813d4cbeSIngo Weinhold } 592813d4cbeSIngo Weinhold 593813d4cbeSIngo Weinhold 594813d4cbeSIngo Weinhold status_t 595813d4cbeSIngo Weinhold _user_mutex_lock(int32* mutex, const char* name, uint32 flags, 596813d4cbeSIngo Weinhold bigtime_t timeout) 597813d4cbeSIngo Weinhold { 598813d4cbeSIngo Weinhold if (mutex == NULL || !IS_USER_ADDRESS(mutex) || (addr_t)mutex % 4 != 0) 599813d4cbeSIngo Weinhold return B_BAD_ADDRESS; 600813d4cbeSIngo Weinhold 601813d4cbeSIngo Weinhold syscall_restart_handle_timeout_pre(flags, timeout); 602813d4cbeSIngo Weinhold 603813d4cbeSIngo Weinhold status_t error = user_mutex_lock(mutex, name, flags | B_CAN_INTERRUPT, 604813d4cbeSIngo Weinhold timeout); 605813d4cbeSIngo Weinhold 606813d4cbeSIngo Weinhold return syscall_restart_handle_timeout_post(error, timeout); 607813d4cbeSIngo Weinhold } 608813d4cbeSIngo Weinhold 609813d4cbeSIngo Weinhold 610813d4cbeSIngo Weinhold status_t 6116f3f29c7SAugustin Cavalier _user_mutex_unblock(int32* mutex, uint32 flags) 612813d4cbeSIngo Weinhold { 613813d4cbeSIngo Weinhold if (mutex == NULL || !IS_USER_ADDRESS(mutex) || (addr_t)mutex % 4 != 0) 614813d4cbeSIngo Weinhold return B_BAD_ADDRESS; 615813d4cbeSIngo Weinhold 616fb688aa1SAugustin Cavalier UserMutexContextFetcher contextFetcher(mutex, flags); 617fb688aa1SAugustin Cavalier if (contextFetcher.InitCheck() != B_OK) 618fb688aa1SAugustin Cavalier return contextFetcher.InitCheck(); 619fb688aa1SAugustin Cavalier struct user_mutex_context* context = contextFetcher.Context(); 620813d4cbeSIngo Weinhold 6210ab9f280SAugustin Cavalier // In the case where there is no entry, we must hold the read lock until we 6220ab9f280SAugustin Cavalier // unset WAITING, because otherwise some other thread could initiate a wait. 62393d7d1c5SAugustin Cavalier ReadLocker tableReadLocker(context->lock); 624fb688aa1SAugustin Cavalier UserMutexEntry* entry = get_user_mutex_entry(context, 625fb688aa1SAugustin Cavalier contextFetcher.Address(), true, true); 6260ab9f280SAugustin Cavalier if (entry == NULL) { 627fb688aa1SAugustin Cavalier user_atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING, contextFetcher.IsWired()); 6280ab9f280SAugustin Cavalier tableReadLocker.Unlock(); 6290ab9f280SAugustin Cavalier } else { 6300ab9f280SAugustin Cavalier tableReadLocker.Unlock(); 631fb688aa1SAugustin Cavalier user_mutex_unblock(entry, mutex, flags, contextFetcher.IsWired()); 632813d4cbeSIngo Weinhold } 63393d7d1c5SAugustin Cavalier put_user_mutex_entry(context, entry); 634813d4cbeSIngo Weinhold 635813d4cbeSIngo Weinhold return B_OK; 636813d4cbeSIngo Weinhold } 637813d4cbeSIngo Weinhold 638813d4cbeSIngo Weinhold 639813d4cbeSIngo Weinhold status_t 64093d7d1c5SAugustin Cavalier _user_mutex_switch_lock(int32* fromMutex, uint32 fromFlags, 64193d7d1c5SAugustin Cavalier int32* toMutex, const char* name, uint32 toFlags, bigtime_t timeout) 642813d4cbeSIngo Weinhold { 643813d4cbeSIngo Weinhold if (fromMutex == NULL || !IS_USER_ADDRESS(fromMutex) 644813d4cbeSIngo Weinhold || (addr_t)fromMutex % 4 != 0 || toMutex == NULL 645813d4cbeSIngo Weinhold || !IS_USER_ADDRESS(toMutex) || (addr_t)toMutex % 4 != 0) { 646813d4cbeSIngo Weinhold return B_BAD_ADDRESS; 647813d4cbeSIngo Weinhold } 648813d4cbeSIngo Weinhold 64993d7d1c5SAugustin Cavalier return user_mutex_switch_lock(fromMutex, fromFlags, toMutex, name, 65093d7d1c5SAugustin Cavalier toFlags | B_CAN_INTERRUPT, timeout); 651813d4cbeSIngo Weinhold } 652d6d439f3SHamish Morrison 653d6d439f3SHamish Morrison 654d6d439f3SHamish Morrison status_t 655d6d439f3SHamish Morrison _user_mutex_sem_acquire(int32* sem, const char* name, uint32 flags, 656d6d439f3SHamish Morrison bigtime_t timeout) 657d6d439f3SHamish Morrison { 658d6d439f3SHamish Morrison if (sem == NULL || !IS_USER_ADDRESS(sem) || (addr_t)sem % 4 != 0) 659d6d439f3SHamish Morrison return B_BAD_ADDRESS; 660d6d439f3SHamish Morrison 661d6d439f3SHamish Morrison syscall_restart_handle_timeout_pre(flags, timeout); 662d6d439f3SHamish Morrison 66393d7d1c5SAugustin Cavalier struct user_mutex_context* context; 66493d7d1c5SAugustin Cavalier 66593d7d1c5SAugustin Cavalier // TODO: use the per-team context when possible 66693d7d1c5SAugustin Cavalier context = &sSharedUserMutexContext; 66793d7d1c5SAugustin Cavalier 668d6d439f3SHamish Morrison // wire the page and get the physical address 669d6d439f3SHamish Morrison VMPageWiringInfo wiringInfo; 670d6d439f3SHamish Morrison status_t error = vm_wire_page(B_CURRENT_TEAM, (addr_t)sem, true, 671d6d439f3SHamish Morrison &wiringInfo); 672d6d439f3SHamish Morrison if (error != B_OK) 673d6d439f3SHamish Morrison return error; 674d6d439f3SHamish Morrison 67593d7d1c5SAugustin Cavalier UserMutexEntry* entry = get_user_mutex_entry(context, wiringInfo.physicalAddress); 6760ab9f280SAugustin Cavalier if (entry == NULL) 6770ab9f280SAugustin Cavalier return B_NO_MEMORY; 678d6d439f3SHamish Morrison { 6790ab9f280SAugustin Cavalier ReadLocker entryLocker(entry->lock); 6800ab9f280SAugustin Cavalier error = user_mutex_sem_acquire_locked(entry, sem, 681fb688aa1SAugustin Cavalier flags | B_CAN_INTERRUPT, timeout, entryLocker, true); 682d6d439f3SHamish Morrison } 68393d7d1c5SAugustin Cavalier put_user_mutex_entry(context, entry); 684d6d439f3SHamish Morrison 685d6d439f3SHamish Morrison vm_unwire_page(&wiringInfo); 686d6d439f3SHamish Morrison return syscall_restart_handle_timeout_post(error, timeout); 687d6d439f3SHamish Morrison } 688d6d439f3SHamish Morrison 689d6d439f3SHamish Morrison 690d6d439f3SHamish Morrison status_t 691d6d439f3SHamish Morrison _user_mutex_sem_release(int32* sem) 692d6d439f3SHamish Morrison { 693d6d439f3SHamish Morrison if (sem == NULL || !IS_USER_ADDRESS(sem) || (addr_t)sem % 4 != 0) 694d6d439f3SHamish Morrison return B_BAD_ADDRESS; 695d6d439f3SHamish Morrison 69693d7d1c5SAugustin Cavalier struct user_mutex_context* context; 69793d7d1c5SAugustin Cavalier 69893d7d1c5SAugustin Cavalier // TODO: use the per-team context when possible 69993d7d1c5SAugustin Cavalier context = &sSharedUserMutexContext; 70093d7d1c5SAugustin Cavalier 701d6d439f3SHamish Morrison // wire the page and get the physical address 702d6d439f3SHamish Morrison VMPageWiringInfo wiringInfo; 703d6d439f3SHamish Morrison status_t error = vm_wire_page(B_CURRENT_TEAM, (addr_t)sem, true, 704d6d439f3SHamish Morrison &wiringInfo); 705d6d439f3SHamish Morrison if (error != B_OK) 706d6d439f3SHamish Morrison return error; 707d6d439f3SHamish Morrison 70893d7d1c5SAugustin Cavalier UserMutexEntry* entry = get_user_mutex_entry(context, 709*2cc89328SAugustin Cavalier wiringInfo.physicalAddress); 710d6d439f3SHamish Morrison { 711fb688aa1SAugustin Cavalier user_mutex_sem_release(entry, sem, true); 712d6d439f3SHamish Morrison } 71393d7d1c5SAugustin Cavalier put_user_mutex_entry(context, entry); 714d6d439f3SHamish Morrison 715d6d439f3SHamish Morrison vm_unwire_page(&wiringInfo); 716d6d439f3SHamish Morrison return B_OK; 717d6d439f3SHamish Morrison } 718