/* * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved. * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "los_base.h" #include "los_spinlock.h" #include "los_task_pri.h" #include "los_printf_pri.h" #include "los_atomic.h" #include "los_exc.h" #if (LOSCFG_KERNEL_SMP_LOCKDEP == YES) #define LOCKDEP_GET_NAME(lockDep, index) (((SPIN_LOCK_S *)((lockDep)->heldLocks[(index)].lockPtr))->name) #define LOCKDEP_GET_ADDR(lockDep, index) ((lockDep)->heldLocks[(index)].lockAddr) STATIC Atomic g_lockdepAvailable = 1; /* atomic insurance for lockdep check */ STATIC INLINE VOID OsLockDepRequire(UINT32 *intSave) { *intSave = LOS_IntLock(); while (LOS_AtomicCmpXchg32bits(&g_lockdepAvailable, 0, 1)) { /* busy waiting */ } } STATIC INLINE VOID OsLockDepRelease(UINT32 intSave) { LOS_AtomicSet(&g_lockdepAvailable, 1); LOS_IntRestore(intSave); } STATIC INLINE UINT64 OsLockDepGetCycles(VOID) { UINT32 high, low; LOS_GetCpuCycle(&high, &low); /* combine cycleHi and cycleLo into 8 bytes cycles */ return (((UINT64)high << 32) + low); // 32 bits for lower half of UINT64 } STATIC INLINE CHAR *OsLockDepErrorStringGet(enum LockDepErrType type) { CHAR *errorString = NULL; switch (type) { case LOCKDEP_ERR_DOUBLE_LOCK: errorString = "double lock"; break; case LOCKDEP_ERR_DEAD_LOCK: errorString = "dead lock"; break; case LOCKDEP_ERR_UNLOCK_WITOUT_LOCK: errorString = "unlock without lock"; break; case LOCKDEP_ERR_OVERFLOW: errorString = "lockdep overflow"; break; default: errorString = "unknow error code"; break; } return errorString; } WEAK VOID OsLockDepPanic(enum LockDepErrType errType) { /* halt here */ (VOID)errType; (VOID)LOS_IntLock(); OsBackTrace(); while (1) {} } STATIC VOID OsLockDepDumpLock(const LosTaskCB *task, const SPIN_LOCK_S *lock, const VOID *requestAddr, enum LockDepErrType errType) { INT32 i; const LockDep *lockDep = &task->lockDep; const LosTaskCB *temp = task; PrintExcInfo("lockdep check failed\n"); PrintExcInfo("error type : %s\n", OsLockDepErrorStringGet(errType)); PrintExcInfo("request addr : 0x%x\n", requestAddr); while (1) { PrintExcInfo("task name : %s\n", temp->taskName); PrintExcInfo("task id : %u\n", temp->taskID); PrintExcInfo("cpu num : %u\n", temp->currCpu); PrintExcInfo("start dumping lockdep infomation\n"); for (i = 0; i < lockDep->lockDepth; i++) { if (lockDep->heldLocks[i].lockPtr == lock) { PrintExcInfo("[%d] %s <-- addr:0x%x\n", i, LOCKDEP_GET_NAME(lockDep, i), LOCKDEP_GET_ADDR(lockDep, i)); } else { PrintExcInfo("[%d] %s \n", i, LOCKDEP_GET_NAME(lockDep, i)); } } PrintExcInfo("[%d] %s <-- now\n", i, lock->name); if (errType == LOCKDEP_ERR_DEAD_LOCK) { temp = lock->owner; lock = temp->lockDep.waitLock; lockDep = &temp->lockDep; } if (temp == task) { break; } } OsLockDepPanic(errType); } STATIC BOOL OsLockDepCheckDependancy(const LosTaskCB *current, LosTaskCB *lockOwner) { BOOL checkResult = TRUE; SPIN_LOCK_S *lockTemp = NULL; do { if (current == lockOwner) { checkResult = FALSE; return checkResult; } if (lockOwner->lockDep.waitLock != NULL) { lockTemp = lockOwner->lockDep.waitLock; lockOwner = lockTemp->owner; } else { break; } } while (lockOwner != SPINLOCK_OWNER_INIT); return checkResult; } VOID OsLockDepCheckIn(SPIN_LOCK_S *lock) { UINT32 intSave; enum LockDepErrType checkResult = LOCKDEP_SUCEESS; VOID *requestAddr = (VOID *)__builtin_return_address(0); LosTaskCB *current = OsCurrTaskGet(); LockDep *lockDep = ¤t->lockDep; LosTaskCB *lockOwner = NULL; OsLockDepRequire(&intSave); if (lockDep->lockDepth >= (INT32)MAX_LOCK_DEPTH) { checkResult = LOCKDEP_ERR_OVERFLOW; goto OUT; } lockOwner = lock->owner; /* not owned by any tasks yet, not doing following checks */ if (lockOwner == SPINLOCK_OWNER_INIT) { goto OUT; } if (current == lockOwner) { checkResult = LOCKDEP_ERR_DOUBLE_LOCK; goto OUT; } if (OsLockDepCheckDependancy(current, lockOwner) != TRUE) { checkResult = LOCKDEP_ERR_DEAD_LOCK; goto OUT; } OUT: if (checkResult == LOCKDEP_SUCEESS) { /* * though the check may succeed, the waitLock still need to be set. * because the OsLockDepCheckIn and OsLockDepRecord is not strictly muti-core * sequential, there would be more than two tasks can pass the checking, but * only one task can successfully obtain the lock. */ lockDep->waitLock = lock; lockDep->heldLocks[lockDep->lockDepth].lockAddr = requestAddr; lockDep->heldLocks[lockDep->lockDepth].waitTime = OsLockDepGetCycles(); /* start time */ } else { OsLockDepDumpLock(current, lock, requestAddr, checkResult); } OsLockDepRelease(intSave); } VOID OsLockDepRecord(SPIN_LOCK_S *lock) { UINT32 intSave; UINT64 cycles; LosTaskCB *current = OsCurrTaskGet(); LockDep *lockDep = ¤t->lockDep; HeldLocks *heldlock = &lockDep->heldLocks[lockDep->lockDepth]; OsLockDepRequire(&intSave); /* * OsLockDepCheckIn records start time t1, after the lock is obtained, we * get the time t2, (t2 - t1) is the time of waiting for the lock */ cycles = OsLockDepGetCycles(); heldlock->waitTime = cycles - heldlock->waitTime; heldlock->holdTime = cycles; /* record lock info */ lock->owner = current; lock->cpuid = ArchCurrCpuid(); /* record lockdep info */ heldlock->lockPtr = lock; lockDep->lockDepth++; lockDep->waitLock = NULL; OsLockDepRelease(intSave); } VOID OsLockDepCheckOut(SPIN_LOCK_S *lock) { UINT32 intSave; INT32 depth; enum LockDepErrType checkResult = LOCKDEP_SUCEESS; VOID *requestAddr = (VOID *)__builtin_return_address(0); LosTaskCB *current = OsCurrTaskGet(); LosTaskCB *owner = NULL; LockDep *lockDep = NULL; HeldLocks *heldlocks = NULL; OsLockDepRequire(&intSave); owner = lock->owner; if (owner == SPINLOCK_OWNER_INIT) { checkResult = LOCKDEP_ERR_UNLOCK_WITOUT_LOCK; OsLockDepDumpLock(current, lock, requestAddr, checkResult); goto OUT; } lockDep = &owner->lockDep; heldlocks = &lockDep->heldLocks[0]; depth = lockDep->lockDepth; /* find the lock position */ while (--depth >= 0) { if (heldlocks[depth].lockPtr == lock) { break; } } LOS_ASSERT(depth >= 0); /* record lock holding time */ heldlocks[depth].holdTime = OsLockDepGetCycles() - heldlocks[depth].holdTime; /* if unlock a older lock, needs move heldLock records */ while (depth < lockDep->lockDepth - 1) { lockDep->heldLocks[depth] = lockDep->heldLocks[depth + 1]; depth++; } lockDep->lockDepth--; lock->cpuid = (UINT32)(-1); lock->owner = SPINLOCK_OWNER_INIT; OUT: OsLockDepRelease(intSave); } VOID OsLockdepClearSpinlocks(VOID) { LosTaskCB *task = OsCurrTaskGet(); LockDep *lockDep = &task->lockDep; SPIN_LOCK_S *lock = NULL; /* * Unlock spinlocks that running task has held. * lockDepth will decrease after each spinlock is unlockded. */ while (lockDep->lockDepth) { lock = lockDep->heldLocks[lockDep->lockDepth - 1].lockPtr; LOS_SpinUnlock(lock); } } #else /* LOSCFG_KERNEL_SMP_LOCKDEP */ VOID OsLockDepCheckIn(SPIN_LOCK_S *lock) { (VOID)lock; return; } VOID OsLockDepRecord(SPIN_LOCK_S *lock) { (VOID)lock; return; } VOID OsLockDepCheckOut(SPIN_LOCK_S *lock) { (VOID)lock; return; } VOID OsLockdepClearSpinlocks(VOID) { return; } #endif