From: Luc Verhaegen Date: Tue, 22 Mar 2011 11:38:38 +0000 (+0100) Subject: gpu: pvr: debugfs: add hwrec status buffer dumping X-Git-Url: http://git.openpandora.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=23804d0f0890d082efc52f15460da7e29b6d8dcc;p=sgx.git gpu: pvr: debugfs: add hwrec status buffer dumping Move code over from sgxinit.c, and dump status buffers in binary format to a debugfs file instead of sending it as %08X's to syslog. Shares process info from the top level now, instead of acquiring this information several times. This now also prints the caller of the HWRecovery routine. Signed-off-by: Luc Verhaegen Signed-off-by: Imre Deak --- diff --git a/pvr/pvr_debugfs.c b/pvr/pvr_debugfs.c index b9d32a3..3c3b30a 100644 --- a/pvr/pvr_debugfs.c +++ b/pvr/pvr_debugfs.c @@ -38,6 +38,8 @@ #include "sgxutils.h" #include "pvr_debugfs.h" #include "mmu.h" +#include "bridged_support.h" +#include "mm.h" static struct dentry *debugfs_dir; static u32 pvr_reset; @@ -89,7 +91,7 @@ static int pvr_debugfs_reset(void *data, u64 val) goto exit; } - HWRecoveryResetSGX(node); + HWRecoveryResetSGX(node, __func__); SGXTestActivePowerEvent(node); exit: @@ -355,8 +357,260 @@ hwrec_mem_print(char *format, ...) } #endif /* CONFIG_PVR_DEBUG */ +/* + * Render status buffer dumping. + */ +static size_t hwrec_status_size; +static u32 hwrec_status_pages[1024]; + +static int +hwrec_status_write(char *buffer, size_t size) +{ + return hwrec_pages_write(buffer, size, &hwrec_status_size, + hwrec_status_pages, + ARRAY_SIZE(hwrec_status_pages)); +} + +static void +hwrec_status_free(void) +{ + hwrec_pages_free(&hwrec_status_size, hwrec_status_pages); +} + +static int +hwrec_status_print(char *format, ...) +{ + char tmp[25]; + va_list ap; + size_t size; + + va_start(ap, format); + size = vscnprintf(tmp, sizeof(tmp), format, ap); + va_end(ap); + + return hwrec_status_write(tmp, size); +} + +#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 = NULL; + 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) +{ + char *corr = ""; + + if (type & BUF_DESC_CORRUPT) { + type &= ~BUF_DESC_CORRUPT; + corr = "(corrupt)"; + } + hwrec_status_print("\n", type, corr, size); + hwrec_status_write(start, size); +} + +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; + + pr_info("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_PER_PROCESS_DATA *proc, + struct PVRSRV_SGXDEV_INFO *dev_info) +{ + struct SGXMKIF_HOST_CTL __iomem *hctl = dev_info->psSGXHostCtl; + struct render_state_buf_list *bl_set[2] = { NULL }; + struct render_state_buf_list *mbl; + u32 handle_ta; + u32 handle_3d; + + 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); +} + void -pvr_hwrec_dump(struct PVRSRV_SGXDEV_INFO *psDevInfo) +pvr_hwrec_dump(struct PVRSRV_PER_PROCESS_DATA *proc_data, + struct PVRSRV_SGXDEV_INFO *psDevInfo) { mutex_lock(hwrec_mutex); @@ -383,6 +637,9 @@ pvr_hwrec_dump(struct PVRSRV_SGXDEV_INFO *psDevInfo) hwrec_edm_buf = pvr_edm_buffer_create(psDevInfo); #endif + hwrec_status_free(); + dump_sgx_state_bufs(proc_data, psDevInfo); + hwrec_event = 1; mutex_unlock(hwrec_mutex); @@ -725,6 +982,66 @@ static const struct file_operations hwrec_edm_fops = { }; #endif /* PVRSRV_USSE_EDM_STATUS_DEBUG */ +/* + * Provides a dump of the TA and 3D status buffers. + */ +static loff_t +hwrec_status_llseek(struct file *filp, loff_t offset, int whence) +{ + loff_t f_pos; + + mutex_lock(hwrec_mutex); + + if (hwrec_status_size) + f_pos = hwrec_llseek_helper(filp, offset, whence, + hwrec_status_size); + else + f_pos = 0; + + mutex_unlock(hwrec_mutex); + + return f_pos; +} + +static ssize_t +hwrec_status_read(struct file *filp, char __user *buf, size_t size, + loff_t *f_pos) +{ + mutex_lock(hwrec_mutex); + + if ((*f_pos >= 0) && (*f_pos < hwrec_status_size)) { + int page, offset; + + size = min(size, (size_t) hwrec_status_size - (size_t) *f_pos); + + page = (*f_pos) / PAGE_SIZE; + offset = (*f_pos) & ~PAGE_MASK; + + size = min(size, (size_t) PAGE_SIZE - offset); + + if (copy_to_user(buf, + ((u8 *) hwrec_status_pages[page]) + offset, + size)) { + mutex_unlock(hwrec_mutex); + return -EFAULT; + } + } else + size = 0; + + mutex_unlock(hwrec_mutex); + + *f_pos += size; + return size; +} + +static const struct file_operations hwrec_status_fops = { + .owner = THIS_MODULE, + .llseek = hwrec_status_llseek, + .read = hwrec_status_read, + .open = hwrec_file_open, + .release = hwrec_file_release, +}; + /* * */ @@ -784,6 +1101,12 @@ int pvr_debugfs_init(void) } #endif + if (!debugfs_create_file("hwrec_status", S_IRUSR, debugfs_dir, NULL, + &hwrec_status_fops)) { + debugfs_remove_recursive(debugfs_dir); + return -ENODEV; + } + return 0; } @@ -802,4 +1125,6 @@ void pvr_debugfs_cleanup(void) if (hwrec_edm_buf) pvr_edm_buffer_destroy(hwrec_edm_buf); #endif + + hwrec_status_free(); } diff --git a/pvr/pvr_debugfs.h b/pvr/pvr_debugfs.h index b9d7ebb..72bf34b 100644 --- a/pvr/pvr_debugfs.h +++ b/pvr/pvr_debugfs.h @@ -27,7 +27,8 @@ int pvr_debugfs_init(void); void pvr_debugfs_cleanup(void); -void pvr_hwrec_dump(struct PVRSRV_SGXDEV_INFO *psDevInfo); +void pvr_hwrec_dump(struct PVRSRV_PER_PROCESS_DATA *proc_data, + struct PVRSRV_SGXDEV_INFO *psDevInfo); #ifdef CONFIG_PVR_DEBUG /* to be used for memory dumping from mmu.c */ diff --git a/pvr/sgxinfokm.h b/pvr/sgxinfokm.h index 6722e0b..2517e04 100644 --- a/pvr/sgxinfokm.h +++ b/pvr/sgxinfokm.h @@ -214,7 +214,8 @@ struct timer_work_data * SGXOSTimerInit(struct PVRSRV_DEVICE_NODE *psDeviceNode); void SGXOSTimerDeInit(struct timer_work_data *data); -void HWRecoveryResetSGX(struct PVRSRV_DEVICE_NODE *psDeviceNode); +void HWRecoveryResetSGX(struct PVRSRV_DEVICE_NODE *psDeviceNode, + const char *caller); void SGXReset(struct PVRSRV_SGXDEV_INFO *psDevInfo, u32 ui32PDUMPFlags); enum PVRSRV_ERROR SGXInitialise(struct PVRSRV_SGXDEV_INFO *psDevInfo, diff --git a/pvr/sgxinit.c b/pvr/sgxinit.c index 2b5612d..1942ba6 100644 --- a/pvr/sgxinit.c +++ b/pvr/sgxinit.c @@ -639,25 +639,22 @@ static struct PVRSRV_PER_PROCESS_DATA *find_cur_proc_data( return proc_data; } -static void dump_process_info(struct PVRSRV_DEVICE_NODE *dev) +static void pr_err_process_info(struct PVRSRV_PER_PROCESS_DATA *proc) { - struct PVRSRV_PER_PROCESS_DATA *proc; - - proc = find_cur_proc_data(dev); + struct task_struct *tsk; + int pid; - if (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(); - } + 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" @@ -675,244 +672,16 @@ 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) +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; @@ -922,14 +691,15 @@ void HWRecoveryResetSGX(struct PVRSRV_DEVICE_NODE *psDeviceNode) l |= PVRSRV_USSE_EDM_INTERRUPT_HWR; writel(l, &psSGXHostCtl->ui32InterruptClearFlags); - pr_err("%s: SGX Hardware Recovery triggered\n", __func__); + pr_err("SGX Hardware Recovery triggered (from %s)\n", caller); + + proc = find_cur_proc_data(psDeviceNode); - dump_process_info(psDeviceNode); - dump_sgx_registers(psDevInfo); - dump_sgx_state_bufs(psDeviceNode); + pr_err_process_info(proc); + pr_err_sgx_registers(psDevInfo); #ifdef CONFIG_DEBUG_FS - pvr_hwrec_dump(psDevInfo); + pvr_hwrec_dump(proc, psDevInfo); #endif PDUMPSUSPEND(); @@ -1029,7 +799,7 @@ static void SGXOSTimer(struct work_struct *work) l++; writel(l, &psSGXHostCtl->ui32HostDetectedLockups); - HWRecoveryResetSGX(psDeviceNode); + HWRecoveryResetSGX(psDeviceNode, __func__); pvr_dev_unlock(); } @@ -1167,7 +937,7 @@ static void SGX_MISRHandler(void *pvData) if ((l1 & PVRSRV_USSE_EDM_INTERRUPT_HWR) && !(l2 & PVRSRV_USSE_EDM_INTERRUPT_HWR)) - HWRecoveryResetSGX(psDeviceNode); + HWRecoveryResetSGX(psDeviceNode, __func__); if (psDeviceNode->bReProcessDeviceCommandComplete) SGXScheduleProcessQueues(psDeviceNode);