Loading drivers/gpu/drm/vc4/vc4_drv.c +2 −0 Original line number Diff line number Diff line Loading @@ -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 = { Loading drivers/gpu/drm/vc4/vc4_drv.h +4 −0 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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); Loading drivers/gpu/drm/vc4/vc4_gem.c +185 −0 Original line number Diff line number Diff line Loading @@ -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) { Loading @@ -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); } Loading Loading @@ -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); } include/uapi/drm/vc4_drm.h +45 −0 Original line number Diff line number Diff line Loading @@ -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) Loading @@ -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. */ Loading Loading @@ -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_ */ Loading
drivers/gpu/drm/vc4/vc4_drv.c +2 −0 Original line number Diff line number Diff line Loading @@ -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 = { Loading
drivers/gpu/drm/vc4/vc4_drv.h +4 −0 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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); Loading
drivers/gpu/drm/vc4/vc4_gem.c +185 −0 Original line number Diff line number Diff line Loading @@ -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) { Loading @@ -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); } Loading Loading @@ -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); }
include/uapi/drm/vc4_drm.h +45 −0 Original line number Diff line number Diff line Loading @@ -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) Loading @@ -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. */ Loading Loading @@ -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_ */