/**
 * @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);
	}
}
Ejemplo n.º 2
0
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;
}