/** * @brief Unlock the mutex. Also does a data barrier before unlocking so any * modifications made before the lock gets released will be completed * before the lock is released. * @param mutex as passed to Mutex_Lock() * @param mode as passed to Mutex_Lock() * @param line the line number of the code that called this function */ void Mutex_UnlockLine(Mutex *mutex, MutexMode mode, int line) { Mutex_State newState, oldState; DMB(); do { oldState.state = ATOMIC_GETO(mutex->state); newState.mode = oldState.mode - mode; newState.blck = oldState.blck; mutex->lineUnl = line; ASSERT(oldState.mode >= mode); } while (!ATOMIC_SETIF(mutex->state, newState.state, oldState.state)); /* * If another thread was blocked, then wake it up. */ if (oldState.blck) { if (mode == MutexModeSH) WAKEUPONE(mutex->lockWaitQ); else WAKEUPALL(mutex->lockWaitQ); } }
void Mksck_DecRefc(Mksck *mksck) { uint32 oldRefc; DMB(); do { while ((oldRefc = ATOMIC_GETO(mksck->refCount)) == 1) { MksckPage *mksckPage = Mksck_ToSharedPage(mksck); while (Mutex_Lock(&mksckPage->mutex, MutexModeEX) < 0) ; if (ATOMIC_SETIF(mksck->refCount, 0, 1)) { #if 0 KNOWN_BUG(MVP-1349); PRINTK("Mksck_DecRefc: %08X " \ "shutDown %u, foundEmpty %u, " \ "foundFull %u, blocked %u\n", mksck->addr.addr, mksck->shutDown, mksck->foundEmpty, mksck->foundFull, ATOMIC_GETO(mksck->mutex.blocked)); #endif ASSERT(mksck->peer == 0); Mutex_Unlock(&mksckPage->mutex, MutexModeEX); MksckPage_DecRefc(mksckPage); return; } Mutex_Unlock(&mksckPage->mutex, MutexModeEX); } ASSERT(oldRefc != 0); } while (!ATOMIC_SETIF(mksck->refCount, oldRefc - 1, oldRefc)); }
/** * @brief Lock the mutex. Also does a data barrier after locking so the * locking is complete before any shared data is accessed. * @param[in,out] mutex which mutex to lock * @param mode mutex lock mode * @param file the file of the caller code * @param line the line number of the code that called this function * @return rc = 0: mutex now locked by caller<br> * < 0: interrupted */ int Mutex_LockLine(Mutex *mutex, MutexMode mode, const char *file, int line) { Mutex_State newState, oldState; MutexCheckSleep(file, line); /* * If uncontended, just set new lock state and return success status. * If contended, mark state saying there is a waiting thread to wake. */ do { lock_start: /* * Get current state and calculate what new state would be. * New state adds 1 for shared and 0xFFFF for exclusive. * If the 16 bit field overflows, there is contention. */ oldState.state = ATOMIC_GETO(mutex->state); newState.mode = oldState.mode + mode; newState.blck = oldState.blck; /* * So we are saying there is no contention if new state * indicates no overflow. * * On fairness: The test here allows a new-comer thread to grab * the lock even if there is a blocked thread. For example 2 * threads repeatedly obtaining shared access can starve a third * wishing to obtain an exclusive lock. Currently this is only a * hypothetical situation as mksck use exclusive lock only and * the code never has more than 2 threads using the same mutex. */ if ((uint32)newState.mode >= (uint32)mode) { if (!ATOMIC_SETIF(mutex->state, newState.state, oldState.state)) goto lock_start; DMB(); mutex->line = line; mutex->lineUnl = -1; return 0; } /* * There is contention, so increment the number of blocking * threads. */ newState.mode = oldState.mode; newState.blck = oldState.blck + 1; } while (!ATOMIC_SETIF(mutex->state, newState.state, oldState.state)); /* * Statistics... */ ATOMIC_ADDV(mutex->blocked, 1); /* * Mutex is contended, state has been updated to say there is a blocking * thread. * * So now we block till someone wakes us up. */ do { DEFINE_WAIT(waiter); /* * This will make sure we catch any wakes done after we check * the lock state again. */ prepare_to_wait((wait_queue_head_t *)mutex->lockWaitQ, &waiter, TASK_INTERRUPTIBLE); /* * Now that we will catch wakes, check the lock state again. * If now uncontended, mark it locked, abandon the wait and * return success. */ set_new_state: /* * Same as the original check for contention above, except * that we must decrement the number of waiting threads by one * if we are successful in locking the mutex. */ oldState.state = ATOMIC_GETO(mutex->state); newState.mode = oldState.mode + mode; newState.blck = oldState.blck - 1; ASSERT(oldState.blck); if ((uint32)newState.mode >= (uint32)mode) { if (!ATOMIC_SETIF(mutex->state, newState.state, oldState.state)) goto set_new_state; /* * No longer contended and we were able to lock it. */ finish_wait((wait_queue_head_t *)mutex->lockWaitQ, &waiter); DMB(); mutex->line = line; mutex->lineUnl = -1; return 0; } /* * Wait for a wake that happens any time after prepare_to_wait() * returned. */ WARN(!schedule_timeout(10*HZ), "Mutex_Lock: soft lockup - stuck for 10s!\n"); finish_wait((wait_queue_head_t *)mutex->lockWaitQ, &waiter); } while (!signal_pending(current)); /* * We aren't waiting anymore, decrement the number of waiting threads. */ do { oldState.state = ATOMIC_GETO(mutex->state); newState.mode = oldState.mode; newState.blck = oldState.blck - 1; ASSERT(oldState.blck); } while (!ATOMIC_SETIF(mutex->state, newState.state, oldState.state)); return -ERESTARTSYS; }