331 lines
9.5 KiB
Diff
331 lines
9.5 KiB
Diff
From: Eric Anholt <eric@anholt.net>
|
|
Date: Fri, 30 Oct 2015 10:09:02 -0700
|
|
Subject: [08/16] drm/vc4: Add an interface for capturing the GPU state after a
|
|
hang.
|
|
Origin: http://cgit.freedesktop.org/~airlied/linux/commit?id=214613656b5179f0daab6e0a080814b5100d45f0
|
|
|
|
This can be parsed with vc4-gpu-tools tools for trying to figure out
|
|
what was going on.
|
|
|
|
v2: Use __u32-style types.
|
|
|
|
Signed-off-by: Eric Anholt <eric@anholt.net>
|
|
---
|
|
drivers/gpu/drm/vc4/vc4_drv.c | 2 +
|
|
drivers/gpu/drm/vc4/vc4_drv.h | 4 +
|
|
drivers/gpu/drm/vc4/vc4_gem.c | 185 ++++++++++++++++++++++++++++++++++++++++++
|
|
include/uapi/drm/vc4_drm.h | 45 ++++++++++
|
|
4 files changed, 236 insertions(+)
|
|
|
|
diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c
|
|
index 2cfee59..97226b6 100644
|
|
--- a/drivers/gpu/drm/vc4/vc4_drv.c
|
|
+++ b/drivers/gpu/drm/vc4/vc4_drv.c
|
|
@@ -80,6 +80,8 @@ static const struct drm_ioctl_desc vc4_drm_ioctls[] = {
|
|
DRM_IOCTL_DEF_DRV(VC4_CREATE_BO, vc4_create_bo_ioctl, 0),
|
|
DRM_IOCTL_DEF_DRV(VC4_MMAP_BO, vc4_mmap_bo_ioctl, 0),
|
|
DRM_IOCTL_DEF_DRV(VC4_CREATE_SHADER_BO, vc4_create_shader_bo_ioctl, 0),
|
|
+ DRM_IOCTL_DEF_DRV(VC4_GET_HANG_STATE, vc4_get_hang_state_ioctl,
|
|
+ DRM_ROOT_ONLY),
|
|
};
|
|
|
|
static struct drm_driver vc4_drm_driver = {
|
|
diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
|
|
index f9927d8..080865e 100644
|
|
--- a/drivers/gpu/drm/vc4/vc4_drv.h
|
|
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
|
|
@@ -19,6 +19,8 @@ struct vc4_dev {
|
|
|
|
struct drm_fbdev_cma *fbdev;
|
|
|
|
+ struct vc4_hang_state *hang_state;
|
|
+
|
|
/* The kernel-space BO cache. Tracks buffers that have been
|
|
* unreferenced by all other users (refcounts of 0!) but not
|
|
* yet freed, so we can do cheap allocations.
|
|
@@ -361,6 +363,8 @@ int vc4_create_shader_bo_ioctl(struct drm_device *dev, void *data,
|
|
struct drm_file *file_priv);
|
|
int vc4_mmap_bo_ioctl(struct drm_device *dev, void *data,
|
|
struct drm_file *file_priv);
|
|
+int vc4_get_hang_state_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv);
|
|
int vc4_mmap(struct file *filp, struct vm_area_struct *vma);
|
|
int vc4_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma);
|
|
void *vc4_prime_vmap(struct drm_gem_object *obj);
|
|
diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c
|
|
index 5fb0556..39f29e7 100644
|
|
--- a/drivers/gpu/drm/vc4/vc4_gem.c
|
|
+++ b/drivers/gpu/drm/vc4/vc4_gem.c
|
|
@@ -40,6 +40,186 @@ vc4_queue_hangcheck(struct drm_device *dev)
|
|
round_jiffies_up(jiffies + msecs_to_jiffies(100)));
|
|
}
|
|
|
|
+struct vc4_hang_state {
|
|
+ struct drm_vc4_get_hang_state user_state;
|
|
+
|
|
+ u32 bo_count;
|
|
+ struct drm_gem_object **bo;
|
|
+};
|
|
+
|
|
+static void
|
|
+vc4_free_hang_state(struct drm_device *dev, struct vc4_hang_state *state)
|
|
+{
|
|
+ unsigned int i;
|
|
+
|
|
+ mutex_lock(&dev->struct_mutex);
|
|
+ for (i = 0; i < state->user_state.bo_count; i++)
|
|
+ drm_gem_object_unreference(state->bo[i]);
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
+
|
|
+ kfree(state);
|
|
+}
|
|
+
|
|
+int
|
|
+vc4_get_hang_state_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct drm_vc4_get_hang_state *get_state = data;
|
|
+ struct drm_vc4_get_hang_state_bo *bo_state;
|
|
+ struct vc4_hang_state *kernel_state;
|
|
+ struct drm_vc4_get_hang_state *state;
|
|
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
|
|
+ unsigned long irqflags;
|
|
+ u32 i;
|
|
+ int ret;
|
|
+
|
|
+ spin_lock_irqsave(&vc4->job_lock, irqflags);
|
|
+ kernel_state = vc4->hang_state;
|
|
+ if (!kernel_state) {
|
|
+ spin_unlock_irqrestore(&vc4->job_lock, irqflags);
|
|
+ return -ENOENT;
|
|
+ }
|
|
+ state = &kernel_state->user_state;
|
|
+
|
|
+ /* If the user's array isn't big enough, just return the
|
|
+ * required array size.
|
|
+ */
|
|
+ if (get_state->bo_count < state->bo_count) {
|
|
+ get_state->bo_count = state->bo_count;
|
|
+ spin_unlock_irqrestore(&vc4->job_lock, irqflags);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ vc4->hang_state = NULL;
|
|
+ spin_unlock_irqrestore(&vc4->job_lock, irqflags);
|
|
+
|
|
+ /* Save the user's BO pointer, so we don't stomp it with the memcpy. */
|
|
+ state->bo = get_state->bo;
|
|
+ memcpy(get_state, state, sizeof(*state));
|
|
+
|
|
+ bo_state = kcalloc(state->bo_count, sizeof(*bo_state), GFP_KERNEL);
|
|
+ if (!bo_state) {
|
|
+ ret = -ENOMEM;
|
|
+ goto err_free;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < state->bo_count; i++) {
|
|
+ struct vc4_bo *vc4_bo = to_vc4_bo(kernel_state->bo[i]);
|
|
+ u32 handle;
|
|
+
|
|
+ ret = drm_gem_handle_create(file_priv, kernel_state->bo[i],
|
|
+ &handle);
|
|
+
|
|
+ if (ret) {
|
|
+ state->bo_count = i - 1;
|
|
+ goto err;
|
|
+ }
|
|
+ bo_state[i].handle = handle;
|
|
+ bo_state[i].paddr = vc4_bo->base.paddr;
|
|
+ bo_state[i].size = vc4_bo->base.base.size;
|
|
+ }
|
|
+
|
|
+ ret = copy_to_user((void __user *)(uintptr_t)get_state->bo,
|
|
+ bo_state,
|
|
+ state->bo_count * sizeof(*bo_state));
|
|
+ kfree(bo_state);
|
|
+
|
|
+err_free:
|
|
+
|
|
+ vc4_free_hang_state(dev, kernel_state);
|
|
+
|
|
+err:
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void
|
|
+vc4_save_hang_state(struct drm_device *dev)
|
|
+{
|
|
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
|
|
+ struct drm_vc4_get_hang_state *state;
|
|
+ struct vc4_hang_state *kernel_state;
|
|
+ struct vc4_exec_info *exec;
|
|
+ struct vc4_bo *bo;
|
|
+ unsigned long irqflags;
|
|
+ unsigned int i, unref_list_count;
|
|
+
|
|
+ kernel_state = kcalloc(1, sizeof(*state), GFP_KERNEL);
|
|
+ if (!kernel_state)
|
|
+ return;
|
|
+
|
|
+ state = &kernel_state->user_state;
|
|
+
|
|
+ spin_lock_irqsave(&vc4->job_lock, irqflags);
|
|
+ exec = vc4_first_job(vc4);
|
|
+ if (!exec) {
|
|
+ spin_unlock_irqrestore(&vc4->job_lock, irqflags);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ unref_list_count = 0;
|
|
+ list_for_each_entry(bo, &exec->unref_list, unref_head)
|
|
+ unref_list_count++;
|
|
+
|
|
+ state->bo_count = exec->bo_count + unref_list_count;
|
|
+ kernel_state->bo = kcalloc(state->bo_count, sizeof(*kernel_state->bo),
|
|
+ GFP_ATOMIC);
|
|
+ if (!kernel_state->bo) {
|
|
+ spin_unlock_irqrestore(&vc4->job_lock, irqflags);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < exec->bo_count; i++) {
|
|
+ drm_gem_object_reference(&exec->bo[i]->base);
|
|
+ kernel_state->bo[i] = &exec->bo[i]->base;
|
|
+ }
|
|
+
|
|
+ list_for_each_entry(bo, &exec->unref_list, unref_head) {
|
|
+ drm_gem_object_reference(&bo->base.base);
|
|
+ kernel_state->bo[i] = &bo->base.base;
|
|
+ i++;
|
|
+ }
|
|
+
|
|
+ state->start_bin = exec->ct0ca;
|
|
+ state->start_render = exec->ct1ca;
|
|
+
|
|
+ spin_unlock_irqrestore(&vc4->job_lock, irqflags);
|
|
+
|
|
+ state->ct0ca = V3D_READ(V3D_CTNCA(0));
|
|
+ state->ct0ea = V3D_READ(V3D_CTNEA(0));
|
|
+
|
|
+ state->ct1ca = V3D_READ(V3D_CTNCA(1));
|
|
+ state->ct1ea = V3D_READ(V3D_CTNEA(1));
|
|
+
|
|
+ state->ct0cs = V3D_READ(V3D_CTNCS(0));
|
|
+ state->ct1cs = V3D_READ(V3D_CTNCS(1));
|
|
+
|
|
+ state->ct0ra0 = V3D_READ(V3D_CT00RA0);
|
|
+ state->ct1ra0 = V3D_READ(V3D_CT01RA0);
|
|
+
|
|
+ state->bpca = V3D_READ(V3D_BPCA);
|
|
+ state->bpcs = V3D_READ(V3D_BPCS);
|
|
+ state->bpoa = V3D_READ(V3D_BPOA);
|
|
+ state->bpos = V3D_READ(V3D_BPOS);
|
|
+
|
|
+ state->vpmbase = V3D_READ(V3D_VPMBASE);
|
|
+
|
|
+ state->dbge = V3D_READ(V3D_DBGE);
|
|
+ state->fdbgo = V3D_READ(V3D_FDBGO);
|
|
+ state->fdbgb = V3D_READ(V3D_FDBGB);
|
|
+ state->fdbgr = V3D_READ(V3D_FDBGR);
|
|
+ state->fdbgs = V3D_READ(V3D_FDBGS);
|
|
+ state->errstat = V3D_READ(V3D_ERRSTAT);
|
|
+
|
|
+ spin_lock_irqsave(&vc4->job_lock, irqflags);
|
|
+ if (vc4->hang_state) {
|
|
+ spin_unlock_irqrestore(&vc4->job_lock, irqflags);
|
|
+ vc4_free_hang_state(dev, kernel_state);
|
|
+ } else {
|
|
+ vc4->hang_state = kernel_state;
|
|
+ spin_unlock_irqrestore(&vc4->job_lock, irqflags);
|
|
+ }
|
|
+}
|
|
+
|
|
static void
|
|
vc4_reset(struct drm_device *dev)
|
|
{
|
|
@@ -64,6 +244,8 @@ vc4_reset_work(struct work_struct *work)
|
|
struct vc4_dev *vc4 =
|
|
container_of(work, struct vc4_dev, hangcheck.reset_work);
|
|
|
|
+ vc4_save_hang_state(vc4->dev);
|
|
+
|
|
vc4_reset(vc4->dev);
|
|
}
|
|
|
|
@@ -679,4 +861,7 @@ vc4_gem_destroy(struct drm_device *dev)
|
|
}
|
|
|
|
vc4_bo_cache_destroy(dev);
|
|
+
|
|
+ if (vc4->hang_state)
|
|
+ vc4_free_hang_state(dev, vc4->hang_state);
|
|
}
|
|
diff --git a/include/uapi/drm/vc4_drm.h b/include/uapi/drm/vc4_drm.h
|
|
index fe4161b..eeb37e3 100644
|
|
--- a/include/uapi/drm/vc4_drm.h
|
|
+++ b/include/uapi/drm/vc4_drm.h
|
|
@@ -32,6 +32,7 @@
|
|
#define DRM_VC4_CREATE_BO 0x03
|
|
#define DRM_VC4_MMAP_BO 0x04
|
|
#define DRM_VC4_CREATE_SHADER_BO 0x05
|
|
+#define DRM_VC4_GET_HANG_STATE 0x06
|
|
|
|
#define DRM_IOCTL_VC4_SUBMIT_CL DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_SUBMIT_CL, struct drm_vc4_submit_cl)
|
|
#define DRM_IOCTL_VC4_WAIT_SEQNO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_WAIT_SEQNO, struct drm_vc4_wait_seqno)
|
|
@@ -39,6 +40,7 @@
|
|
#define DRM_IOCTL_VC4_CREATE_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_CREATE_BO, struct drm_vc4_create_bo)
|
|
#define DRM_IOCTL_VC4_MMAP_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_MMAP_BO, struct drm_vc4_mmap_bo)
|
|
#define DRM_IOCTL_VC4_CREATE_SHADER_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_CREATE_SHADER_BO, struct drm_vc4_create_shader_bo)
|
|
+#define DRM_IOCTL_VC4_GET_HANG_STATE DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_GET_HANG_STATE, struct drm_vc4_get_hang_state)
|
|
|
|
struct drm_vc4_submit_rcl_surface {
|
|
__u32 hindex; /* Handle index, or ~0 if not present. */
|
|
@@ -231,4 +233,47 @@ struct drm_vc4_create_shader_bo {
|
|
__u32 pad;
|
|
};
|
|
|
|
+struct drm_vc4_get_hang_state_bo {
|
|
+ __u32 handle;
|
|
+ __u32 paddr;
|
|
+ __u32 size;
|
|
+ __u32 pad;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct drm_vc4_hang_state - ioctl argument for collecting state
|
|
+ * from a GPU hang for analysis.
|
|
+*/
|
|
+struct drm_vc4_get_hang_state {
|
|
+ /** Pointer to array of struct drm_vc4_get_hang_state_bo. */
|
|
+ __u64 bo;
|
|
+ /**
|
|
+ * On input, the size of the bo array. Output is the number
|
|
+ * of bos to be returned.
|
|
+ */
|
|
+ __u32 bo_count;
|
|
+
|
|
+ __u32 start_bin, start_render;
|
|
+
|
|
+ __u32 ct0ca, ct0ea;
|
|
+ __u32 ct1ca, ct1ea;
|
|
+ __u32 ct0cs, ct1cs;
|
|
+ __u32 ct0ra0, ct1ra0;
|
|
+
|
|
+ __u32 bpca, bpcs;
|
|
+ __u32 bpoa, bpos;
|
|
+
|
|
+ __u32 vpmbase;
|
|
+
|
|
+ __u32 dbge;
|
|
+ __u32 fdbgo;
|
|
+ __u32 fdbgb;
|
|
+ __u32 fdbgr;
|
|
+ __u32 fdbgs;
|
|
+ __u32 errstat;
|
|
+
|
|
+ /* Pad that we may save more registers into in the future. */
|
|
+ __u32 pad[16];
|
|
+};
|
|
+
|
|
#endif /* _UAPI_VC4_DRM_H_ */
|