gpu: pvr: dump render status buffers during SGX HW recovery
authorImre Deak <imre.deak@nokia.com>
Wed, 9 Feb 2011 15:17:55 +0000 (17:17 +0200)
committerGrazvydas Ignotas <notasas@gmail.com>
Sun, 20 May 2012 18:43:03 +0000 (21:43 +0300)
To aid debugging, dump any status buffers that are provided by the
ogles user space library for the rendering context that was active
at the time of recovery. At the moment the USSE final patched
versions of vertex and fragment shaders that were bound within the
frame causing the HWRec are provided, but later on more types can be
added.

Signed-off-by: Imre Deak <imre.deak@nokia.com>
pvr/sgxapi_km.h
pvr/sgxinfo.h
pvr/sgxinfokm.h
pvr/sgxinit.c

index 88d66fa..39ec960 100644 (file)
@@ -29,6 +29,7 @@
 
 
 #include "sgxdefs.h"
+#include "img_types.h"
 
 #if defined(__KERNEL__)
 #include <linux/unistd.h>
@@ -178,6 +179,23 @@ struct SGX_MISC_INFO {
        } uData;
 };
 
+enum render_state_buf_type {
+       RSB_USSE_VERTEX_PROG,
+       RSB_USSE_FRAGMENT_PROG,
+};
+
+struct render_state_buf_info {
+       u32                             buf_id;
+       u32                             offset;
+       u32                             size;
+       enum render_state_buf_type      type;
+};
+
+struct render_state_buf_list {
+       u32                             cnt;
+       struct render_state_buf_info    info[20];
+};
+
 #define SGX_KICKTA_DUMPBITMAP_MAX_NAME_LENGTH          256
 
 struct SGX_KICKTA_DUMPBITMAP {
index eda7f34..8310244 100644 (file)
@@ -71,6 +71,7 @@ struct SGX_BRIDGE_INIT_INFO {
 
        struct SGX_INIT_SCRIPTS sScripts;
 
+       u32 state_buf_ofs;
 };
 
 struct SGXMKIF_COMMAND {
@@ -201,6 +202,9 @@ struct SGXMKIF_HOST_CTL {
 
        /*< to count time wraps in the Timer task */
        u32 ui32TimeWraps;
+
+       u32 render_state_buf_ta_handle;
+       u32 render_state_buf_3d_handle;
 };
 
 struct SGX_CLIENT_INFO {
index 83228d4..4191842 100644 (file)
@@ -152,6 +152,7 @@ struct PVRSRV_SGXDEV_INFO {
 
        u32 asSGXDevData[SGX_MAX_DEV_DATA];
 
+       u32 state_buf_ofs;
 };
 
 struct SGX_TIMING_INFORMATION {
index c1a15a4..f75989b 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 "ra.h"
 #include "mmu.h"
+#include "mm.h"
 #include "handle.h"
 #include "perproc.h"
 
@@ -161,6 +165,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:
@@ -602,7 +608,7 @@ static size_t __print_edm_trace(struct PVRSRV_SGXDEV_INFO *sdev, char *dst,
        if (dst)                                                           \
                p += snprintf(dst + p, dst_len - p, fmt, ## __VA_ARGS__);  \
        else                                                               \
-               pr_err(fmt, ## __VA_ARGS__);                               \
+               printk(KERN_DEBUG fmt, ## __VA_ARGS__);                    \
 } while (0)
 
        if (!sdev->psKernelEDMStatusBufferMemInfo)
@@ -704,6 +710,236 @@ static void dump_sgx_registers(struct PVRSRV_SGXDEV_INFO *psDevInfo)
                readl(psDevInfo->pvRegsBaseKM + EUR_CR_CLKGATECTL));
 }
 
+#define BUF_DESC_CORRUPT       (1 << 31)
+
+static void add_uniq_items(struct render_state_buf_list *dst,
+                          const struct render_state_buf_list *src)
+{
+       int i;
+
+       for (i = 0; i < src->cnt; i++) {
+               const struct render_state_buf_info *sbinf = &src->info[i];
+               int j;
+
+               for (j = 0; j < dst->cnt; j++) {
+                       if (sbinf->buf_id == dst->info[j].buf_id) {
+                               if (memcmp(sbinf, &dst->info[j], sizeof(sbinf)))
+                                       dst->info[j].type |= BUF_DESC_CORRUPT;
+                               break;
+                       }
+               }
+               if (j == dst->cnt) {
+                       /* Bound for cnt is guaranteed by the caller */
+                       dst->info[dst->cnt] = *sbinf;
+                       dst->cnt++;
+               }
+       }
+}
+
+static struct render_state_buf_list *create_merged_uniq_list(
+               struct render_state_buf_list **bl_set, int set_size)
+{
+       int i;
+       struct render_state_buf_list *dbl;
+       size_t size;
+
+       /*
+        * Create a buf list big enough to contain all elements from each
+        * list in bl_set.
+        */
+       size = offsetof(struct render_state_buf_list, info[0]);
+       for (i = 0; i < set_size; i++) {
+               if (!bl_set[i])
+                       continue;
+               size += bl_set[i]->cnt * sizeof(bl_set[i]->info[0]);
+       }
+       if (!size)
+               return NULL;
+       dbl = kmalloc(size, GFP_KERNEL);
+       if (!dbl)
+               return NULL;
+
+       dbl->cnt = 0;
+       for (i = 0; i < set_size; i++) {
+               if (bl_set[i])
+                       add_uniq_items(dbl, bl_set[i]);
+       }
+
+       return dbl;
+}
+
+static void *vmap_buf(struct PVRSRV_PER_PROCESS_DATA *proc,
+                       u32 handle, off_t offset, size_t size)
+{
+       struct PVRSRV_KERNEL_MEM_INFO *minfo;
+       struct LinuxMemArea *mem_area;
+       enum PVRSRV_ERROR err;
+       unsigned start_ofs;
+       unsigned end_ofs;
+       int pg_cnt;
+       struct page **pages;
+       void *map;
+       int i;
+
+       if (offset & PAGE_MASK)
+               return NULL;
+
+       err = PVRSRVLookupHandle(proc->psHandleBase, (void **)&minfo,
+                               (void *)handle, PVRSRV_HANDLE_TYPE_MEM_INFO);
+       if (err != PVRSRV_OK)
+               return NULL;
+       if (minfo->pvLinAddrKM)
+               return minfo->pvLinAddrKM;
+
+       err = PVRSRVLookupOSMemHandle(proc->psHandleBase, (void *)&mem_area,
+                                       (void *)handle);
+       if (err != PVRSRV_OK)
+               return NULL;
+
+       start_ofs = offset & PAGE_MASK;
+       end_ofs = PAGE_ALIGN(offset + size);
+       pg_cnt = (end_ofs - start_ofs) >> PAGE_SHIFT;
+       pages = kmalloc(pg_cnt * sizeof(pages[0]), GFP_KERNEL);
+       if (!pages)
+               return NULL;
+       for (i = 0; i < pg_cnt; i++) {
+               unsigned pfn;
+
+               pfn = LinuxMemAreaToCpuPFN(mem_area, start_ofs);
+               if (!pfn_valid(pfn))
+                       goto err;
+               pages[i] = pfn_to_page(pfn);
+               start_ofs += PAGE_SIZE;
+       }
+       map = vmap(pages, pg_cnt, VM_MAP, PAGE_KERNEL);
+       map += offset;
+err:
+       kfree(pages);
+
+       return map;
+}
+
+static void vunmap_buf(struct PVRSRV_PER_PROCESS_DATA *proc,
+                       u32 handle, void *map)
+{
+       struct PVRSRV_KERNEL_MEM_INFO *minfo;
+       enum PVRSRV_ERROR err;
+
+       err = PVRSRVLookupHandle(proc->psHandleBase, (void **)&minfo,
+                               (void *)handle, PVRSRV_HANDLE_TYPE_MEM_INFO);
+       if (err != PVRSRV_OK)
+               return;
+       if (minfo->pvLinAddrKM)
+               return;
+       vunmap((void *)(((unsigned long)map) & PAGE_MASK));
+}
+
+static void dump_buf(void *start, size_t size, u32 type)
+{
+       unsigned addr = 0;
+       char *corr = "";
+
+       if (type & BUF_DESC_CORRUPT) {
+               type &= ~BUF_DESC_CORRUPT;
+               corr = "(corrupt)";
+       }
+       printk(KERN_DEBUG "type %d%s size %d\n", type, corr, size);
+       while (addr < size) {
+               if (!(addr % 16)) {
+                       if (addr)
+                               printk("\n");
+                       printk(KERN_DEBUG "%08x ", addr);
+               }
+               printk("%08x ", *(u32 *)(start + addr));
+               addr += 4;
+       }
+       if (addr)
+               printk("\n");
+}
+
+static struct render_state_buf_list *get_state_buf_list(
+                       struct PVRSRV_PER_PROCESS_DATA *proc,
+                       u32 handle, off_t offset)
+{
+       struct PVRSRV_KERNEL_MEM_INFO *container;
+       struct render_state_buf_list *buf;
+       enum PVRSRV_ERROR err;
+
+       err = PVRSRVLookupHandle(proc->psHandleBase, (void **)&container,
+                               (void *)handle, PVRSRV_HANDLE_TYPE_MEM_INFO);
+       if (err != PVRSRV_OK)
+               return NULL;
+       if (!container->pvLinAddrKM)
+               return NULL;
+       if (offset + sizeof(*buf) > container->ui32AllocSize)
+               return NULL;
+
+       buf = container->pvLinAddrKM + offset;
+
+       if (buf->cnt > ARRAY_SIZE(buf->info))
+               return NULL;
+
+       return buf;
+}
+
+static void dump_state_buf_list(struct PVRSRV_PER_PROCESS_DATA *proc,
+                               struct render_state_buf_list *bl)
+{
+       int i;
+
+       if (!bl->cnt)
+               return;
+
+       printk(KERN_DEBUG "Dumping %d render state buffers\n", bl->cnt);
+       for (i = 0; i < bl->cnt; i++) {
+               struct render_state_buf_info *binfo;
+               void *map;
+
+               binfo = &bl->info[i];
+
+               map = vmap_buf(proc, binfo->buf_id, binfo->offset, binfo->size);
+               if (!map)
+                       continue;
+               dump_buf(map, binfo->size, binfo->type);
+
+               vunmap_buf(proc, binfo->buf_id, map);
+       }
+}
+
+static void dump_sgx_state_bufs(struct PVRSRV_DEVICE_NODE *dev_node)
+{
+       struct PVRSRV_SGXDEV_INFO *dev_info = dev_node->pvDevice;
+       struct SGXMKIF_HOST_CTL __iomem *hctl = dev_info->psSGXHostCtl;
+       struct PVRSRV_PER_PROCESS_DATA *proc;
+       struct render_state_buf_list *bl_set[2] = { NULL };
+       struct render_state_buf_list *mbl;
+       u32 handle_ta;
+       u32 handle_3d;
+
+       proc = find_cur_proc_data(dev_node);
+       if (!proc)
+               return;
+
+       handle_ta = readl(&hctl->render_state_buf_ta_handle);
+       handle_3d = readl(&hctl->render_state_buf_3d_handle);
+       bl_set[0] = get_state_buf_list(proc, handle_ta,
+                                       dev_info->state_buf_ofs);
+       /*
+        * The two buf list can be the same if the TA and 3D phases used the
+        * same context at the time of the HWrec. In this case just ignore
+        * one of them.
+        */
+       if (handle_ta != handle_3d)
+               bl_set[1] = get_state_buf_list(proc, handle_3d,
+                                       dev_info->state_buf_ofs);
+       mbl = create_merged_uniq_list(bl_set, ARRAY_SIZE(bl_set));
+       if (!mbl)
+               return;
+
+       dump_state_buf_list(proc, mbl);
+       kfree(mbl);
+}
+
 /* Should be called with pvr_lock held */
 void HWRecoveryResetSGX(struct PVRSRV_DEVICE_NODE *psDeviceNode)
 {
@@ -726,6 +962,7 @@ void HWRecoveryResetSGX(struct PVRSRV_DEVICE_NODE *psDeviceNode)
        dump_process_info(psDeviceNode);
        dump_sgx_registers(psDevInfo);
        dump_edm(psDevInfo);
+       dump_sgx_state_bufs(psDeviceNode);
 
        PDUMPSUSPEND();