From: Christian Gmeiner <[email protected]> The OpenGL and Vulkan robustness extensions let an application detect a GPU reset and check if its own context caused it, so the application can drop the broken context and build a new one. The kernel knows both facts, but etnaviv has no way to report them to userspace.
Add two counters and a RESET_QUERY ioctl that returns both in one call: a per-GPU counter that counts every reset of that GPU, and a per-context counter that only counts the resets this context was guilty of. Userspace compares the counters with saved values: if the context counter moved the context was guilty, if only the GPU counter moved the context was an innocent victim. The GPU counter is per GPU core and not per device, so a hang on one pipe does not look like an innocent reset to contexts that only use another pipe. The timeout handler updates both counters and the ioctl reads both under the GPU lock. Without the lock the query could run between the two updates, see only the GPU counter move and report an innocent reset to the guilty context. Bump the driver minor version so userspace can detect the feature. Signed-off-by: Christian Gmeiner <[email protected]> --- drivers/gpu/drm/etnaviv/etnaviv_drv.c | 29 ++++++++++++++++++++++++++++- drivers/gpu/drm/etnaviv/etnaviv_drv.h | 1 + drivers/gpu/drm/etnaviv/etnaviv_gpu.h | 2 ++ drivers/gpu/drm/etnaviv/etnaviv_sched.c | 5 +++++ include/uapi/drm/etnaviv_drm.h | 19 ++++++++++++++++++- 5 files changed, 54 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c index a27ed014fb4e..5c50e5a24408 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c @@ -296,6 +296,32 @@ static int etnaviv_ioctl_get_param(struct drm_device *dev, void *data, return etnaviv_gpu_get_param(gpu, args->param, &args->value); } +static int etnaviv_ioctl_reset_query(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct etnaviv_drm_private *priv = dev->dev_private; + struct etnaviv_file_private *ctx = file->driver_priv; + struct drm_etnaviv_reset_query *args = data; + struct etnaviv_gpu *gpu; + + if (args->flags) + return -EINVAL; + + if (args->pipe >= ETNA_MAX_PIPES) + return -EINVAL; + + gpu = priv->gpu[args->pipe]; + if (!gpu) + return -ENXIO; + + mutex_lock(&gpu->lock); + args->global_reset_counter = gpu->reset_counter; + args->context_reset_counter = ctx->reset_counter; + mutex_unlock(&gpu->lock); + + return 0; +} + static int etnaviv_ioctl_gem_new(struct drm_device *dev, void *data, struct drm_file *file) { @@ -502,6 +528,7 @@ static const struct drm_ioctl_desc etnaviv_ioctls[] = { ETNA_IOCTL(GEM_WAIT, gem_wait, DRM_RENDER_ALLOW), ETNA_IOCTL(PM_QUERY_DOM, pm_query_dom, DRM_RENDER_ALLOW), ETNA_IOCTL(PM_QUERY_SIG, pm_query_sig, DRM_RENDER_ALLOW), + ETNA_IOCTL(RESET_QUERY, reset_query, DRM_RENDER_ALLOW), }; static void etnaviv_show_fdinfo(struct drm_printer *p, struct drm_file *file) @@ -530,7 +557,7 @@ static const struct drm_driver etnaviv_drm_driver = { .name = "etnaviv", .desc = "etnaviv DRM", .major = 1, - .minor = 4, + .minor = 5, }; /* diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.h b/drivers/gpu/drm/etnaviv/etnaviv_drv.h index cba4323ae589..9c348aa7f8d3 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.h @@ -34,6 +34,7 @@ struct etnaviv_file_private { int id; struct etnaviv_iommu_context *mmu; struct drm_sched_entity sched_entity[ETNA_MAX_PIPES]; + u32 reset_counter; }; struct etnaviv_drm_private { diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.h b/drivers/gpu/drm/etnaviv/etnaviv_gpu.h index 5cb46c84e03a..a97780131426 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.h @@ -148,6 +148,8 @@ struct etnaviv_gpu { u32 hangcheck_primid; u32 hangcheck_fence; + u32 reset_counter; + void __iomem *mmio; int irq; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_sched.c b/drivers/gpu/drm/etnaviv/etnaviv_sched.c index 139e6e38784b..6e9b677122e3 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_sched.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_sched.c @@ -79,6 +79,11 @@ static enum drm_gpu_sched_stat etnaviv_sched_timedout_job(struct drm_sched_job if(sched_job) drm_sched_increase_karma(sched_job); + mutex_lock(&gpu->lock); + gpu->reset_counter++; + submit->ctx->reset_counter++; + mutex_unlock(&gpu->lock); + /* get the GPU back into the init state */ etnaviv_core_dump(submit); etnaviv_gpu_recover_hang(submit); diff --git a/include/uapi/drm/etnaviv_drm.h b/include/uapi/drm/etnaviv_drm.h index af024d90453d..b50a3a0799e6 100644 --- a/include/uapi/drm/etnaviv_drm.h +++ b/include/uapi/drm/etnaviv_drm.h @@ -265,6 +265,21 @@ struct drm_etnaviv_pm_signal { char name[64]; /* out, name of domain */ }; +/* + * Reset status query: + * + * Both counters start at zero and only ever increase. Userspace saves + * both values and compares them on a later query: if the context + * counter moved this context caused a reset, if only the global + * counter moved the GPU was reset on behalf of another context. + */ +struct drm_etnaviv_reset_query { + __u32 pipe; /* in */ + __u32 flags; /* in, must be 0 */ + __u32 global_reset_counter; /* out, all resets of this GPU core */ + __u32 context_reset_counter; /* out, resets caused by this context */ +}; + #define DRM_ETNAVIV_GET_PARAM 0x00 /* placeholder: #define DRM_ETNAVIV_SET_PARAM 0x01 @@ -279,7 +294,8 @@ struct drm_etnaviv_pm_signal { #define DRM_ETNAVIV_GEM_WAIT 0x09 #define DRM_ETNAVIV_PM_QUERY_DOM 0x0a #define DRM_ETNAVIV_PM_QUERY_SIG 0x0b -#define DRM_ETNAVIV_NUM_IOCTLS 0x0c +#define DRM_ETNAVIV_RESET_QUERY 0x0c +#define DRM_ETNAVIV_NUM_IOCTLS 0x0d #define DRM_IOCTL_ETNAVIV_GET_PARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GET_PARAM, struct drm_etnaviv_param) #define DRM_IOCTL_ETNAVIV_GEM_NEW DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_NEW, struct drm_etnaviv_gem_new) @@ -292,6 +308,7 @@ struct drm_etnaviv_pm_signal { #define DRM_IOCTL_ETNAVIV_GEM_WAIT DRM_IOW(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_WAIT, struct drm_etnaviv_gem_wait) #define DRM_IOCTL_ETNAVIV_PM_QUERY_DOM DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_PM_QUERY_DOM, struct drm_etnaviv_pm_domain) #define DRM_IOCTL_ETNAVIV_PM_QUERY_SIG DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_PM_QUERY_SIG, struct drm_etnaviv_pm_signal) +#define DRM_IOCTL_ETNAVIV_RESET_QUERY DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_RESET_QUERY, struct drm_etnaviv_reset_query) #if defined(__cplusplus) } -- 2.54.0
