gpu: pvr: get rid of unnecessary hash lookups for the proc object
[sgx.git] / pvr / sgxinit.c
index 301b94f..6170038 100644 (file)
@@ -30,6 +30,8 @@
 #include <linux/io.h>
 #include <linux/slab.h>
 #include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
 
 #include "sgxdefs.h"
 #include "sgxmmu.h"
 #include "pvr_bridge_km.h"
 #include "sgx_bridge_km.h"
 #include "resman.h"
+#include "bridged_support.h"
 
-#include "pdump_km.h"
+#include "pvr_pdump.h"
 #include "ra.h"
 #include "mmu.h"
+#include "mm.h"
 #include "handle.h"
 #include "perproc.h"
 
 #include "pvrversion.h"
 #include "sgx_options.h"
 
+#ifdef CONFIG_DEBUG_FS
+#include "pvr_debugfs.h"
+#endif
+
 static IMG_BOOL SGX_ISRHandler(void *pvData);
 
 static u32 gui32EventStatusServicesByISR;
@@ -124,11 +132,8 @@ static enum PVRSRV_ERROR InitDevInfo(struct PVRSRV_PER_PROCESS_DATA *psPerProc,
 
        psDevInfo->psKernelHWPerfCBMemInfo =
            (struct PVRSRV_KERNEL_MEM_INFO *)psInitInfo->hKernelHWPerfCBMemInfo;
-#ifdef PVRSRV_USSE_EDM_STATUS_DEBUG
        psDevInfo->psKernelEDMStatusBufferMemInfo =
-           (struct PVRSRV_KERNEL_MEM_INFO *)psInitInfo->
-                                                 hKernelEDMStatusBufferMemInfo;
-#endif
+                                   psInitInfo->hKernelEDMStatusBufferMemInfo;
 
        eError = OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP,
                            sizeof(struct PVRSRV_SGX_CCB_INFO),
@@ -164,6 +169,8 @@ static enum PVRSRV_ERROR InitDevInfo(struct PVRSRV_PER_PROCESS_DATA *psPerProc,
        OSMemCopy(&psDevInfo->asSGXDevData, &psInitInfo->asInitDevData,
                  sizeof(psDevInfo->asSGXDevData));
 
+       psDevInfo->state_buf_ofs = psInitInfo->state_buf_ofs;
+
        return PVRSRV_OK;
 
 failed_allockernelccb:
@@ -342,6 +349,8 @@ static enum PVRSRV_ERROR DevInitSGXPart1(void *pvDeviceNode)
 
        hKernelDevMemContext = BM_CreateContext(psDeviceNode, &sPDDevPAddr,
                                                NULL, NULL);
+       if (!hKernelDevMemContext)
+               goto err1;
 
        psDevInfo->sKernelPDDevPAddr = sPDDevPAddr;
 
@@ -357,6 +366,9 @@ static enum PVRSRV_ERROR DevInitSGXPart1(void *pvDeviceNode)
                                    BM_CreateHeap(hKernelDevMemContext,
                                                  &psDeviceMemoryHeap[i]);
 
+                               if (!hDevMemHeap)
+                                       goto err2;
+
                                psDeviceMemoryHeap[i].hDevMemHeap = hDevMemHeap;
                                break;
                        }
@@ -367,10 +379,28 @@ static enum PVRSRV_ERROR DevInitSGXPart1(void *pvDeviceNode)
        if (eError != PVRSRV_OK) {
                PVR_DPF(PVR_DBG_ERROR,
                         "DevInitSGX : Failed to alloc memory for BIF reset");
-               return PVRSRV_ERROR_GENERIC;
+               goto err2;
        }
 
        return PVRSRV_OK;
+err2:
+       while (i) {
+               int type;
+
+               i--;
+               type = psDeviceMemoryHeap[i].DevMemHeapType;
+               if (type != DEVICE_MEMORY_HEAP_KERNEL &&
+                   type != DEVICE_MEMORY_HEAP_SHARED &&
+                   type != DEVICE_MEMORY_HEAP_SHARED_EXPORTED)
+                       continue;
+               BM_DestroyHeap(psDeviceMemoryHeap[i].hDevMemHeap);
+       }
+       BM_DestroyContext(hKernelDevMemContext);
+err1:
+       OSFreeMem(PVRSRV_OS_NON_PAGEABLE_HEAP,
+                 sizeof(struct PVRSRV_SGXDEV_INFO), psDevInfo, NULL);
+
+       return PVRSRV_ERROR_GENERIC;
 }
 
 enum PVRSRV_ERROR SGXGetInfoForSrvinitKM(void *hDevHandle,
@@ -452,9 +482,11 @@ enum PVRSRV_ERROR DevInitSGXPart2KM(struct PVRSRV_PER_PROCESS_DATA *psPerProc,
 
        PVR_ASSERT(psDeviceNode->pfnDeviceISR == SGX_ISRHandler);
 
+       pvr_dev_lock();
        l = readl(&psDevInfo->psSGXHostCtl->ui32PowerStatus);
        l |= PVRSRV_USSE_EDM_POWMAN_NO_WORK;
        writel(l, &psDevInfo->psSGXHostCtl->ui32PowerStatus);
+       pvr_dev_unlock();
        eDefaultPowerState = PVRSRV_POWER_STATE_D3;
 
        eError = PVRSRVRegisterPowerDevice(psDeviceNode->sDevId.ui32DeviceIndex,
@@ -587,65 +619,42 @@ static enum PVRSRV_ERROR DevDeInitSGX(void *pvDeviceNode)
        return PVRSRV_OK;
 }
 
-#ifdef PVRSRV_USSE_EDM_STATUS_DEBUG
-
-#define SGXMK_TRACE_BUFFER_SIZE                512
-
-static void dump_edm(struct PVRSRV_SGXDEV_INFO *psDevInfo)
-{
-       u32 *trace_buffer =
-               psDevInfo->psKernelEDMStatusBufferMemInfo->pvLinAddrKM;
-       u32 last_code, write_offset;
-       int i;
-
-       last_code = *trace_buffer;
-       trace_buffer++;
-       write_offset = *trace_buffer;
-
-       pr_err("Last SGX microkernel status code: 0x%x\n", last_code);
-
-       trace_buffer++;
-       /* Dump the status values */
-
-       for (i = 0; i < SGXMK_TRACE_BUFFER_SIZE; i++) {
-               u32     *buf;
-               buf = trace_buffer + (((write_offset + i) %
-                                       SGXMK_TRACE_BUFFER_SIZE) * 4);
-               pr_err("(MKT%u) %8.8X %8.8X %8.8X %8.8X\n", i,
-                               buf[2], buf[3], buf[1], buf[0]);
-       }
-}
-#else
-static void dump_edm(struct PVRSRV_SGXDEV_INFO *psDevInfo) {}
-#endif
-
-static void dump_process_info(struct PVRSRV_DEVICE_NODE *dev)
+static struct PVRSRV_PER_PROCESS_DATA *find_cur_proc_data(
+                                       struct PVRSRV_DEVICE_NODE *dev)
 {
        struct PVRSRV_SGXDEV_INFO *dev_info = dev->pvDevice;
        u32 page_dir = readl(dev_info->pvRegsBaseKM +
                                EUR_CR_BIF_DIR_LIST_BASE0);
        struct BM_CONTEXT *bm_ctx;
        struct RESMAN_CONTEXT *res_ctx = NULL;
+       struct PVRSRV_PER_PROCESS_DATA *proc_data = NULL;
 
        bm_ctx = bm_find_context(dev->sDevMemoryInfo.pBMContext, page_dir);
        if (bm_ctx)
                res_ctx = pvr_get_resman_ctx(bm_ctx);
 
-       if (res_ctx) {
-               struct task_struct *tsk;
-               struct PVRSRV_PER_PROCESS_DATA *proc;
-               int pid;
-
-               proc = pvr_get_proc_by_ctx(res_ctx);
-               pid = proc->ui32PID;
-               rcu_read_lock();
-               tsk = pid_task(find_vpid(pid), PIDTYPE_PID);
-               pr_err("PID = %d, process name = %s\n", pid, tsk->comm);
-               rcu_read_unlock();
-       }
+       if (res_ctx)
+               proc_data = pvr_get_proc_by_ctx(res_ctx);
+
+       return proc_data;
+}
+
+static void pr_err_process_info(struct PVRSRV_PER_PROCESS_DATA *proc)
+{
+       struct task_struct *tsk;
+       int pid;
+
+       if (!proc)
+               return;
+
+       pid = proc->ui32PID;
+       rcu_read_lock();
+       tsk = pid_task(find_vpid(pid), PIDTYPE_PID);
+       pr_err("PID = %d, process name = %s\n", pid, tsk->comm);
+       rcu_read_unlock();
 }
 
-static void dump_sgx_registers(struct PVRSRV_SGXDEV_INFO *psDevInfo)
+static void pr_err_sgx_registers(struct PVRSRV_SGXDEV_INFO *psDevInfo)
 {
        pr_err("EVENT_STATUS =     0x%08X\n"
                "EVENT_STATUS2 =    0x%08X\n"
@@ -664,14 +673,17 @@ static void dump_sgx_registers(struct PVRSRV_SGXDEV_INFO *psDevInfo)
 }
 
 /* Should be called with pvr_lock held */
-void HWRecoveryResetSGX(struct PVRSRV_DEVICE_NODE *psDeviceNode)
+void
+HWRecoveryResetSGX(struct PVRSRV_DEVICE_NODE *psDeviceNode, const char *caller)
 {
        enum PVRSRV_ERROR eError;
        struct PVRSRV_SGXDEV_INFO *psDevInfo =
            (struct PVRSRV_SGXDEV_INFO *)psDeviceNode->pvDevice;
        struct SGXMKIF_HOST_CTL __iomem *psSGXHostCtl =
                                        psDevInfo->psSGXHostCtl;
+       struct PVRSRV_PER_PROCESS_DATA *proc;
        u32 l;
+       int max_retries = 10;
 
        BUG_ON(!pvr_is_locked());
 
@@ -679,25 +691,41 @@ void HWRecoveryResetSGX(struct PVRSRV_DEVICE_NODE *psDeviceNode)
        l |= PVRSRV_USSE_EDM_INTERRUPT_HWR;
        writel(l, &psSGXHostCtl->ui32InterruptClearFlags);
 
-       pr_err("HWRecoveryResetSGX: SGX Hardware Recovery triggered\n");
+       pr_err("SGX Hardware Recovery triggered (from %s)\n", caller);
 
-       dump_process_info(psDeviceNode);
-       dump_sgx_registers(psDevInfo);
-       dump_edm(psDevInfo);
+       proc = find_cur_proc_data(psDeviceNode);
+
+       pr_err_process_info(proc);
+       pr_err_sgx_registers(psDevInfo);
+#ifdef PVRSRV_USSE_EDM_STATUS_DEBUG
+       edm_trace_print(psDevInfo, NULL, 0);
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+       pvr_hwrec_dump(proc, psDevInfo);
+#endif
 
        PDUMPSUSPEND();
 
        do {
                eError = SGXInitialise(psDevInfo, IMG_TRUE);
-       } while (eError == PVRSRV_ERROR_RETRY);
-       if (eError != PVRSRV_OK)
-               PVR_DPF(PVR_DBG_ERROR,
-                        "HWRecoveryResetSGX: SGXInitialise failed (%d)",
-                        eError);
+               if (eError != PVRSRV_ERROR_RETRY)
+                       break;
+       } while (max_retries--);
+
+       if (eError != PVRSRV_OK) {
+               pr_err("%s: recovery failed (%d). Disabling the driver",
+                       __func__, eError);
+               pvr_disable();
+
+               PDUMPRESUME();
+
+               return;
+       }
 
        PDUMPRESUME();
 
-       SGXScheduleProcessQueuesKM(psDeviceNode);
+       SGXScheduleProcessQueues(psDeviceNode);
 
        PVRSRVProcessQueues(IMG_TRUE);
 }
@@ -719,7 +747,8 @@ static void SGXOSTimer(struct work_struct *work)
        IMG_BOOL bPoweredDown;
 
        pvr_lock();
-       if (!data->armed) {
+
+       if (!data->armed || pvr_is_disabled()) {
                pvr_unlock();
                return;
        }
@@ -735,6 +764,7 @@ static void SGXOSTimer(struct work_struct *work)
        if (bPoweredDown) {
                ui32LockupCounter = 0;
        } else {
+               pvr_dev_lock();
                ui32CurrentEDMTasks = OSReadHWReg(psDevInfo->pvRegsBaseKM,
                                                psDevInfo->ui32EDMTaskReg0);
                if (psDevInfo->ui32EDMTaskReg1 != 0)
@@ -757,6 +787,7 @@ static void SGXOSTimer(struct work_struct *work)
                        ui32EDMTasks = ui32CurrentEDMTasks;
                        ui32NumResets = psDevInfo->ui32NumResets;
                }
+               pvr_dev_unlock();
        }
 
        bLockup |= cmpxchg(&sgx_reset_forced, 1, 0);
@@ -766,11 +797,13 @@ static void SGXOSTimer(struct work_struct *work)
                                                psDevInfo->psSGXHostCtl;
                u32 l;
 
+               pvr_dev_lock();
                l = readl(&psSGXHostCtl->ui32HostDetectedLockups);
                l++;
                writel(l, &psSGXHostCtl->ui32HostDetectedLockups);
 
-               HWRecoveryResetSGX(psDeviceNode);
+               HWRecoveryResetSGX(psDeviceNode, __func__);
+               pvr_dev_unlock();
        }
 
        queue_delayed_work(data->work_queue, &data->work,
@@ -895,30 +928,26 @@ static void SGX_MISRHandler(void *pvData)
        int dev_idx;
        enum PVRSRV_ERROR err;
 
-       /*
-        * Yet again we have to cope with the caller ID argument, which
-        * determines whether the power lock is properly locked or just
-        * trylock'd. If trylock fails we just get back here with an error
-        * so we need a proper lock. Actually trylock should never fail
-        * either because of the next assumption.
-        * The code afterwards depends on noone turning off the power and
-        * also the power lock to be released, so ask for the lock to be
-        * released when returning from the function.
-        */
        dev_idx = psDeviceNode->sDevId.ui32DeviceIndex;
+
+       pvr_dev_lock();
+
        err = PVRSRVSetDevicePowerStateKM(dev_idx, PVRSRV_POWER_STATE_D0);
        BUG_ON(err != PVRSRV_OK);
 
        l1 = readl(&psSGXHostCtl->ui32InterruptFlags);
        l2 = readl(&psSGXHostCtl->ui32InterruptClearFlags);
+
        if ((l1 & PVRSRV_USSE_EDM_INTERRUPT_HWR) &&
            !(l2 & PVRSRV_USSE_EDM_INTERRUPT_HWR))
-               HWRecoveryResetSGX(psDeviceNode);
+               HWRecoveryResetSGX(psDeviceNode, __func__);
 
        if (psDeviceNode->bReProcessDeviceCommandComplete)
-               SGXScheduleProcessQueuesKM(psDeviceNode);
+               SGXScheduleProcessQueues(psDeviceNode);
 
        SGXTestActivePowerEvent(psDeviceNode);
+
+       pvr_dev_unlock();
 }
 
 enum PVRSRV_ERROR SGXRegisterDevice(struct PVRSRV_DEVICE_NODE *psDeviceNode)
@@ -1190,7 +1219,8 @@ enum PVRSRV_ERROR SGXDevInitCompatCheck(struct PVRSRV_DEVICE_NODE *psDeviceNode)
        struct PVRSRV_KERNEL_MEM_INFO *psMemInfo;
        enum PVRSRV_ERROR eError;
 #if !defined(NO_HARDWARE)
-       u32 ui32BuildOptions, ui32BuildOptionsMismatch;
+       u32 opts;
+       u32 opt_mismatch;
        struct PVRSRV_SGX_MISCINFO_FEATURES *psSGXFeatures;
 #endif
 
@@ -1207,8 +1237,7 @@ enum PVRSRV_ERROR SGXDevInitCompatCheck(struct PVRSRV_DEVICE_NODE *psDeviceNode)
 
        eError = SGXGetBuildInfoKM(psDevInfo, psDeviceNode);
        if (eError != PVRSRV_OK) {
-               PVR_DPF(PVR_DBG_ERROR, "SGXDevInitCompatCheck: "
-                               "Unable to validate device DDK version");
+               pr_err("pvr: unable to validate device DDK version\n");
                goto exit;
        }
        psSGXFeatures =
@@ -1218,9 +1247,8 @@ enum PVRSRV_ERROR SGXDevInitCompatCheck(struct PVRSRV_DEVICE_NODE *psDeviceNode)
             ((PVRVERSION_MAJ << 16) | (PVRVERSION_MIN << 8) |
              PVRVERSION_BRANCH)) ||
             (psSGXFeatures->ui32DDKBuild != PVRVERSION_BUILD)) {
-               PVR_DPF(PVR_DBG_ERROR, "SGXDevInitCompatCheck: "
-                       "Incompatible driver DDK revision (%ld)"
-                       "/device DDK revision (%ld).",
+               pr_err("pvr: incompatible driver DDK revision (%d)"
+                       "/device DDK revision (%d).\n",
                         PVRVERSION_BUILD, psSGXFeatures->ui32DDKBuild);
                eError = PVRSRV_ERROR_DDK_VERSION_MISMATCH;
                goto exit;
@@ -1230,24 +1258,20 @@ enum PVRSRV_ERROR SGXDevInitCompatCheck(struct PVRSRV_DEVICE_NODE *psDeviceNode)
                         PVRVERSION_BUILD, psSGXFeatures->ui32DDKBuild);
        }
 
-       ui32BuildOptions = psSGXFeatures->ui32BuildOptions;
-       if (ui32BuildOptions != (SGX_BUILD_OPTIONS)) {
-               ui32BuildOptionsMismatch =
-                   ui32BuildOptions ^ (SGX_BUILD_OPTIONS);
-               if (((SGX_BUILD_OPTIONS) & ui32BuildOptionsMismatch) != 0)
-                       PVR_DPF(PVR_DBG_ERROR, "SGXInit: "
-                               "Mismatch in driver and microkernel build "
+       opts = psSGXFeatures->ui32BuildOptions;
+       opt_mismatch = opts ^ SGX_BUILD_OPTIONS;
+       /* we support the ABIs both with and without EDM tracing option */
+       opt_mismatch &= ~PVRSRV_USSE_EDM_STATUS_DEBUG_SET_OFFSET;
+       if (opt_mismatch) {
+               if (SGX_BUILD_OPTIONS & opt_mismatch)
+                       pr_err("pvr: mismatch in driver and microkernel build "
                                "options; extra options present in driver: "
-                               "(0x%lx)",
-                                (SGX_BUILD_OPTIONS) &
-                                ui32BuildOptionsMismatch);
+                               "(0x%x)", SGX_BUILD_OPTIONS & opt_mismatch);
 
-               if ((ui32BuildOptions & ui32BuildOptionsMismatch) != 0)
-                       PVR_DPF(PVR_DBG_ERROR, "SGXInit: "
-                               "Mismatch in driver and microkernel build "
+               if (opts & opt_mismatch)
+                       pr_err("pvr: Mismatch in driver and microkernel build "
                                "options; extra options present in "
-                               "microkernel: (0x%lx)",
-                                ui32BuildOptions & ui32BuildOptionsMismatch);
+                               "microkernel: (0x%x)", opts & opt_mismatch);
                eError = PVRSRV_ERROR_BUILD_MISMATCH;
                goto exit;
        } else {
@@ -1394,6 +1418,7 @@ enum PVRSRV_ERROR SGXGetMiscInfoKM(struct PVRSRV_SGXDEV_INFO *psDevInfo,
                                return PVRSRV_ERROR_INVALID_PARAMS;
                        }
 
+                       pvr_dev_lock();
                        ui32MatchingFlags = readl(&psDevInfo->
                                                 psSGXHostCtl->ui32HWPerfFlags);
                        ui32MatchingFlags &=
@@ -1411,6 +1436,7 @@ enum PVRSRV_ERROR SGXGetMiscInfoKM(struct PVRSRV_SGXDEV_INFO *psDevInfo,
 
                        writel(psMiscInfo->uData.ui32NewHWPerfStatus,
                                &psDevInfo->psSGXHostCtl->ui32HWPerfFlags);
+                       pvr_dev_unlock();
 #if defined(PDUMP)
                        PDUMPCOMMENTWITHFLAGS(PDUMP_FLAGS_CONTINUOUS,
                                              "SGX ukernel HWPerf status %u\n",
@@ -1437,15 +1463,19 @@ enum PVRSRV_ERROR SGXGetMiscInfoKM(struct PVRSRV_SGXDEV_INFO *psDevInfo,
 
                        psHWPerfCB->ui32OrdinalGRAPHICS = 0xffffffffUL;
 
+                       pvr_dev_lock();;
                        l = readl(&psDevInfo->psSGXHostCtl->ui32HWPerfFlags);
                        l |= PVRSRV_SGX_HWPERF_GRAPHICS_ON;
                        writel(l, &psDevInfo->psSGXHostCtl->ui32HWPerfFlags);
+                       pvr_dev_unlock();
 
                        return PVRSRV_OK;
                }
        case SGX_MISC_INFO_REQUEST_HWPERF_CB_OFF:
                {
+                       pvr_dev_lock();
                        writel(0, &psDevInfo->psSGXHostCtl->ui32HWPerfFlags);
+                       pvr_dev_unlock();
 
                        return PVRSRV_OK;
                }
@@ -1496,6 +1526,38 @@ enum PVRSRV_ERROR SGXGetMiscInfoKM(struct PVRSRV_SGXDEV_INFO *psDevInfo,
        }
 }
 
+
+
+static bool sgxps_active;
+static unsigned long sgxps_timeout;
+
+IMG_BOOL isSGXPerfServerActive(void)
+{
+       if (!sgxps_active)
+               return 0;
+
+       if (time_before_eq((unsigned long)OSClockus(), sgxps_timeout))
+               return 1;
+
+       sgxps_active = false;
+       PVR_DPF(DBGPRIV_WARNING, "pvr: perf server inactive\n");
+
+       return 0;
+}
+
+
+void SGXPerfServerMonitor(u32 u32TimeStamp)
+{
+       if (!sgxps_active) {
+               PVR_DPF(DBGPRIV_WARNING, "pvr: perf server active\n");
+               sgxps_active = true;
+       }
+
+       /* turn off after 1 second of inactivity */
+       sgxps_timeout = u32TimeStamp + 1000000;
+}
+
+
 enum PVRSRV_ERROR SGXReadDiffCountersKM(void *hDevHandle, u32 ui32Reg,
                                   u32 *pui32Old, IMG_BOOL bNew, u32 ui32New,
                                   u32 ui32NewReset, u32 ui32CountersReg,
@@ -1512,7 +1574,8 @@ enum PVRSRV_ERROR SGXReadDiffCountersKM(void *hDevHandle, u32 ui32Reg,
                psDevInfo->ui32HWGroupRequested = ui32New;
        psDevInfo->ui32HWReset |= ui32NewReset;
 
-       SysAcquireData(&psSysData);
+       if (SysAcquireData(&psSysData) != PVRSRV_OK)
+               return PVRSRV_ERROR_GENERIC;
 
        psPowerDevice = psSysData->psPowerDeviceList;
        while (psPowerDevice) {
@@ -1529,6 +1592,8 @@ enum PVRSRV_ERROR SGXReadDiffCountersKM(void *hDevHandle, u32 ui32Reg,
 
        *pbActive = bPowered;
 
+       pvr_dev_lock();
+
        {
                struct PVRSRV_SGXDEV_DIFF_INFO sNew,
                                               *psPrev = &psDevInfo->sDiffInfo;
@@ -1538,6 +1603,8 @@ enum PVRSRV_ERROR SGXReadDiffCountersKM(void *hDevHandle, u32 ui32Reg,
                *pui32Time = sNew.ui32Time[0];
                if (sNew.ui32Time[0] != psPrev->ui32Time[0] && bPowered) {
 
+                       SGXPerfServerMonitor(*pui32Time);
+
                        *pui32Old =
                            OSReadHWReg(psDevInfo->pvRegsBaseKM, ui32Reg);
 
@@ -1597,6 +1664,8 @@ enum PVRSRV_ERROR SGXReadDiffCountersKM(void *hDevHandle, u32 ui32Reg,
 
        SGXTestActivePowerEvent(psDeviceNode);
 
+       pvr_dev_unlock();
+
        return PVRSRV_OK;
 }