gpu: pvr: add dvfs lock
authorImre Deak <imre.deak@gmail.com>
Mon, 26 Jul 2010 19:22:07 +0000 (22:22 +0300)
committerGrazvydas Ignotas <notasas@gmail.com>
Sun, 20 May 2012 18:09:41 +0000 (21:09 +0300)
We need to protect the SGX HW access with a separate lock as the
pvr_lock. This is to overcome a lockdep issue between the clock
change notification lock and pvr_lock.

Fixes: NB#181087 - pvrsrvkm: possible circular locking dependency detected

Signed-off-by: Imre Deak <imre.deak@gmail.com>
pvr/pvr_bridge_k.c
pvr/pvr_bridge_km.h
pvr/sgxinit.c
pvr/sgxutils.c

index f252b0a..698e363 100644 (file)
@@ -42,8 +42,8 @@
 
 /* Global driver lock protecting all HW and SW state tracking objects. */
 DEFINE_MUTEX(gPVRSRVLock);
-int pvr_dvfs_active;
-DECLARE_WAIT_QUEUE_HEAD(pvr_dvfs_wq);
+static int pvr_dvfs_active;
+static DECLARE_WAIT_QUEUE_HEAD(pvr_dvfs_wq);
 int pvr_disabled;
 
 /*
@@ -63,22 +63,26 @@ int pvr_disabled;
  * lead to a dead lock though since at 3. we always release A, before it's
  * again acquired at 4. To avoid the warning use a wait queue based approach
  * so that we can unlock B before 3.
- *
- * Must be called with gPVRSRVLock held.
  */
-void pvr_dvfs_wait_active(void)
+void pvr_dvfs_lock(void)
 {
-       while (pvr_dvfs_active) {
+       while (cmpxchg(&pvr_dvfs_active, 0, 1)) {
                DEFINE_WAIT(pvr_dvfs_wait);
                prepare_to_wait(&pvr_dvfs_wq, &pvr_dvfs_wait,
                                TASK_UNINTERRUPTIBLE);
-               mutex_unlock(&gPVRSRVLock);
-               schedule();
-               mutex_lock(&gPVRSRVLock);
+               if (pvr_dvfs_active)
+                       schedule();
                finish_wait(&pvr_dvfs_wq, &pvr_dvfs_wait);
        }
 }
 
+void pvr_dvfs_unlock(void)
+{
+       BUG_ON(!pvr_dvfs_active);
+       pvr_dvfs_active = 0;
+       wake_up(&pvr_dvfs_wq);
+}
+
 #if defined(DEBUG_BRIDGE_KM)
 static off_t printLinuxBridgeStats(char *buffer, size_t size, off_t off);
 #endif
index ea981ad..a338ff6 100644 (file)
 #include "pvr_bridge.h"
 #include "perproc.h"
 
-extern int pvr_dvfs_active;
+extern void pvr_dvfs_lock(void);
+extern void pvr_dvfs_unlock(void);
 extern struct mutex gPVRSRVLock;
-extern wait_queue_head_t pvr_dvfs_wq;
 extern int pvr_disabled;
 
-void pvr_dvfs_wait_active(void);
-
-static inline void pvr_dvfs_lock(void)
-{
-       mutex_lock(&gPVRSRVLock);
-       pvr_dvfs_active = 1;
-       mutex_unlock(&gPVRSRVLock);
-}
-
-static inline void pvr_dvfs_unlock(void)
-{
-       mutex_lock(&gPVRSRVLock);
-       pvr_dvfs_active = 0;
-       wake_up(&pvr_dvfs_wq);
-       mutex_unlock(&gPVRSRVLock);
-}
-
 static inline void pvr_lock(void)
 {
        mutex_lock(&gPVRSRVLock);
-       if (pvr_dvfs_active)
-               pvr_dvfs_wait_active();
 }
 
 static inline void pvr_unlock(void)
index 32f5c52..01b8697 100644 (file)
@@ -452,9 +452,11 @@ enum PVRSRV_ERROR DevInitSGXPart2KM(struct PVRSRV_PER_PROCESS_DATA *psPerProc,
 
        PVR_ASSERT(psDeviceNode->pfnDeviceISR == SGX_ISRHandler);
 
+       pvr_dvfs_lock();
        l = readl(&psDevInfo->psSGXHostCtl->ui32PowerStatus);
        l |= PVRSRV_USSE_EDM_POWMAN_NO_WORK;
        writel(l, &psDevInfo->psSGXHostCtl->ui32PowerStatus);
+       pvr_dvfs_unlock();
        eDefaultPowerState = PVRSRV_POWER_STATE_D3;
 
        eError = PVRSRVRegisterPowerDevice(psDeviceNode->sDevId.ui32DeviceIndex,
@@ -676,6 +678,7 @@ void HWRecoveryResetSGX(struct PVRSRV_DEVICE_NODE *psDeviceNode)
 
        BUG_ON(!pvr_is_locked());
 
+       pvr_dvfs_lock();
        l = readl(&psSGXHostCtl->ui32InterruptClearFlags);
        l |= PVRSRV_USSE_EDM_INTERRUPT_HWR;
        writel(l, &psSGXHostCtl->ui32InterruptClearFlags);
@@ -700,6 +703,7 @@ void HWRecoveryResetSGX(struct PVRSRV_DEVICE_NODE *psDeviceNode)
                pvr_disable();
 
                PDUMPRESUME();
+               pvr_dvfs_unlock();
 
                return;
        }
@@ -708,6 +712,8 @@ void HWRecoveryResetSGX(struct PVRSRV_DEVICE_NODE *psDeviceNode)
 
        SGXScheduleProcessQueues(psDeviceNode);
 
+       pvr_dvfs_unlock();
+
        PVRSRVProcessQueues(IMG_TRUE);
 }
 
@@ -745,6 +751,7 @@ static void SGXOSTimer(struct work_struct *work)
        if (bPoweredDown) {
                ui32LockupCounter = 0;
        } else {
+               pvr_dvfs_lock();
                ui32CurrentEDMTasks = OSReadHWReg(psDevInfo->pvRegsBaseKM,
                                                psDevInfo->ui32EDMTaskReg0);
                if (psDevInfo->ui32EDMTaskReg1 != 0)
@@ -767,6 +774,7 @@ static void SGXOSTimer(struct work_struct *work)
                        ui32EDMTasks = ui32CurrentEDMTasks;
                        ui32NumResets = psDevInfo->ui32NumResets;
                }
+               pvr_dvfs_unlock();
        }
 
        bLockup |= cmpxchg(&sgx_reset_forced, 1, 0);
@@ -776,9 +784,11 @@ static void SGXOSTimer(struct work_struct *work)
                                                psDevInfo->psSGXHostCtl;
                u32 l;
 
+               pvr_dvfs_lock();
                l = readl(&psSGXHostCtl->ui32HostDetectedLockups);
                l++;
                writel(l, &psSGXHostCtl->ui32HostDetectedLockups);
+               pvr_dvfs_unlock();
 
                HWRecoveryResetSGX(psDeviceNode);
        }
@@ -906,6 +916,9 @@ static void SGX_MISRHandler(void *pvData)
        enum PVRSRV_ERROR err;
 
        dev_idx = psDeviceNode->sDevId.ui32DeviceIndex;
+
+       pvr_dvfs_lock();
+
        err = PVRSRVSetDevicePowerStateKM(dev_idx, PVRSRV_POWER_STATE_D0);
        BUG_ON(err != PVRSRV_OK);
 
@@ -919,6 +932,8 @@ static void SGX_MISRHandler(void *pvData)
                SGXScheduleProcessQueues(psDeviceNode);
 
        SGXTestActivePowerEvent(psDeviceNode);
+
+       pvr_dvfs_unlock();
 }
 
 enum PVRSRV_ERROR SGXRegisterDevice(struct PVRSRV_DEVICE_NODE *psDeviceNode)
@@ -1394,6 +1409,7 @@ enum PVRSRV_ERROR SGXGetMiscInfoKM(struct PVRSRV_SGXDEV_INFO *psDevInfo,
                                return PVRSRV_ERROR_INVALID_PARAMS;
                        }
 
+                       pvr_dvfs_lock();
                        ui32MatchingFlags = readl(&psDevInfo->
                                                 psSGXHostCtl->ui32HWPerfFlags);
                        ui32MatchingFlags &=
@@ -1411,6 +1427,7 @@ enum PVRSRV_ERROR SGXGetMiscInfoKM(struct PVRSRV_SGXDEV_INFO *psDevInfo,
 
                        writel(psMiscInfo->uData.ui32NewHWPerfStatus,
                                &psDevInfo->psSGXHostCtl->ui32HWPerfFlags);
+                       pvr_dvfs_unlock();
 #if defined(PDUMP)
                        PDUMPCOMMENTWITHFLAGS(PDUMP_FLAGS_CONTINUOUS,
                                              "SGX ukernel HWPerf status %u\n",
@@ -1437,15 +1454,19 @@ enum PVRSRV_ERROR SGXGetMiscInfoKM(struct PVRSRV_SGXDEV_INFO *psDevInfo,
 
                        psHWPerfCB->ui32OrdinalGRAPHICS = 0xffffffffUL;
 
+                       pvr_dvfs_lock();;
                        l = readl(&psDevInfo->psSGXHostCtl->ui32HWPerfFlags);
                        l |= PVRSRV_SGX_HWPERF_GRAPHICS_ON;
                        writel(l, &psDevInfo->psSGXHostCtl->ui32HWPerfFlags);
+                       pvr_dvfs_unlock();
 
                        return PVRSRV_OK;
                }
        case SGX_MISC_INFO_REQUEST_HWPERF_CB_OFF:
                {
+                       pvr_dvfs_lock();
                        writel(0, &psDevInfo->psSGXHostCtl->ui32HWPerfFlags);
+                       pvr_dvfs_unlock();
 
                        return PVRSRV_OK;
                }
@@ -1529,6 +1550,8 @@ enum PVRSRV_ERROR SGXReadDiffCountersKM(void *hDevHandle, u32 ui32Reg,
 
        *pbActive = bPowered;
 
+       pvr_dvfs_lock();
+
        {
                struct PVRSRV_SGXDEV_DIFF_INFO sNew,
                                               *psPrev = &psDevInfo->sDiffInfo;
@@ -1597,6 +1620,8 @@ enum PVRSRV_ERROR SGXReadDiffCountersKM(void *hDevHandle, u32 ui32Reg,
 
        SGXTestActivePowerEvent(psDeviceNode);
 
+       pvr_dvfs_unlock();
+
        return PVRSRV_OK;
 }
 
index 6f49578..616aad4 100644 (file)
@@ -253,6 +253,8 @@ enum PVRSRV_ERROR SGXScheduleCCBCommandKM(
 
        PDUMPSUSPEND();
 
+       pvr_dvfs_lock();
+
        eError =
            PVRSRVSetDevicePowerStateKM(psDeviceNode->sDevId.ui32DeviceIndex,
                                        PVRSRV_POWER_STATE_D0);
@@ -264,6 +266,7 @@ enum PVRSRV_ERROR SGXScheduleCCBCommandKM(
        } else {
                PVR_DPF(PVR_DBG_ERROR, "%s: can't power on device (%d)",
                                        __func__, eError);
+               pvr_dvfs_unlock();
                return eError;
        }
 
@@ -273,6 +276,8 @@ enum PVRSRV_ERROR SGXScheduleCCBCommandKM(
        if (ui32CallerID != ISR_ID)
                SGXTestActivePowerEvent(psDeviceNode);
 
+       pvr_dvfs_unlock();
+
        return eError;
 }
 
@@ -314,7 +319,9 @@ enum PVRSRV_ERROR SGXScheduleProcessQueuesKM(struct PVRSRV_DEVICE_NODE
 {
        enum PVRSRV_ERROR eError;
 
+       pvr_dvfs_lock();
        eError = SGXScheduleProcessQueues(psDeviceNode);
+       pvr_dvfs_unlock();
 
        return eError;
 }
@@ -360,6 +367,7 @@ void SGXCleanupRequest(struct PVRSRV_DEVICE_NODE *psDeviceNode,
 #endif
        u32 l;
 
+       pvr_dvfs_lock();
        if (readl(&psSGXHostCtl->ui32PowerStatus) &
             PVRSRV_USSE_EDM_POWMAN_NO_WORK) {
                ;
@@ -448,6 +456,7 @@ void SGXCleanupRequest(struct PVRSRV_DEVICE_NODE *psDeviceNode,
                         sizeof(u32), 0, hUniqueTag);
 #endif
        }
+       pvr_dvfs_unlock();
 }
 
 struct SGX_HW_RENDER_CONTEXT_CLEANUP {