From 1a4adb05632e902c9819af7c5eeded5243f1dc6c Mon Sep 17 00:00:00 2001 From: Ravikant B Sharma Date: Tue, 8 Nov 2016 17:30:31 +0530 Subject: drm/vmwgfx: Fix NULL pointer comparison Replace direct comparisons to NULL i.e. 'x == NULL' with '!x'. As per coding standard. Signed-off-by: Ravikant B Sharma Reviewed-by: Sinclair Yeh Signed-off-by: Sinclair Yeh --- drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index c7b53d987f06..2154257c21fa 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -264,7 +264,7 @@ static int vmw_resource_val_add(struct vmw_sw_context *sw_context, } node = kzalloc(sizeof(*node), GFP_KERNEL); - if (unlikely(node == NULL)) { + if (unlikely(!node)) { DRM_ERROR("Failed to allocate a resource validation " "entry.\n"); return -ENOMEM; @@ -452,7 +452,7 @@ static int vmw_resource_relocation_add(struct list_head *list, struct vmw_resource_relocation *rel; rel = kmalloc(sizeof(*rel), GFP_KERNEL); - if (unlikely(rel == NULL)) { + if (unlikely(!rel)) { DRM_ERROR("Failed to allocate a resource relocation.\n"); return -ENOMEM; } -- cgit v1.2.3 From 8bd6287ffdb95a623bdbad37e49d1c12c60970ed Mon Sep 17 00:00:00 2001 From: Brian Paul Date: Mon, 17 Jul 2017 07:36:10 -0700 Subject: drm/vmwgfx: fix comment mistake for vmw_cmd_dx_set_index_buffer() Comment fix. Signed-off-by: Brian Paul Reviewed-by: Neha Bhende --- drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index 2154257c21fa..c8faf1ac5333 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -2584,7 +2584,7 @@ static int vmw_cmd_dx_set_vertex_buffers(struct vmw_private *dev_priv, /** * vmw_cmd_dx_ia_set_vertex_buffers - Validate an - * SVGA_3D_CMD_DX_IA_SET_VERTEX_BUFFERS command. + * SVGA_3D_CMD_DX_IA_SET_INDEX_BUFFER command. * * @dev_priv: Pointer to a device private struct. * @sw_context: The software context being used for this batch. -- cgit v1.2.3 From fcfffdd8f98ac305285dca568b5065ef86be6458 Mon Sep 17 00:00:00 2001 From: Sinclair Yeh Date: Mon, 17 Jul 2017 23:28:36 -0700 Subject: drm/vmwgfx: Fix gcc-7.1.1 warning The current code does not look correct, and the reason for it is probably lost. Since this now generates a compiler warning, fix it to what makes sense. Cc: Reported-by: Arnd Bergmann Reported-by: Linus Torvalds Signed-off-by: Sinclair Yeh Reviewed-by: Brian Paul --- drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index c8faf1ac5333..2cfb3c93f42a 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -519,7 +519,7 @@ static int vmw_cmd_invalid(struct vmw_private *dev_priv, struct vmw_sw_context *sw_context, SVGA3dCmdHeader *header) { - return capable(CAP_SYS_ADMIN) ? : -EINVAL; + return -EINVAL; } static int vmw_cmd_ok(struct vmw_private *dev_priv, -- cgit v1.2.3 From 65b97a2bec2fc75e9137e8a17954cb66809215bf Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Thu, 24 Aug 2017 08:06:29 +0200 Subject: drm/vmwgfx: Restart command buffers after errors Previously we skipped the command buffer and added an extra fence to avoid hangs due to skipped fence commands. Now we instead restart the command buffer after the failing command, if there are any commands left. In addition we print out some information about the failing command and its location in the command buffer. Testing Done: ran glxgears using mesa modified to send the NOP_ERROR command before each 10th clear and verified that we detected the device error properly and that there were no other device errors caused by incorrectly ordered command buffers. Also ran the piglit "quick" test suite which generates a couple of device errors and verified that they were handled as intended. Signed-off-by: Thomas Hellstrom Reviewed-by: Brian Paul Reviewed-by: Sinclair Yeh --- drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c | 183 +++++++++++++++++++++++++++----- drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 2 +- drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 48 ++++++++- 3 files changed, 206 insertions(+), 27 deletions(-) (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c index 2e5718509c23..847490bbb2e2 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c @@ -51,6 +51,7 @@ struct vmw_cmdbuf_context { struct list_head hw_submitted; struct list_head preempted; unsigned num_hw_submitted; + bool block_submission; }; /** @@ -60,6 +61,9 @@ struct vmw_cmdbuf_context { * kernel command submissions, @cur. * @space_mutex: Mutex to protect against starvation when we allocate * main pool buffer space. + * @error_mutex: Mutex to serialize the work queue error handling. + * Note this is not needed if the same workqueue handler + * can't race with itself... * @work: A struct work_struct implementeing command buffer error handling. * Immutable. * @dev_priv: Pointer to the device private struct. Immutable. @@ -101,6 +105,7 @@ struct vmw_cmdbuf_context { struct vmw_cmdbuf_man { struct mutex cur_mutex; struct mutex space_mutex; + struct mutex error_mutex; struct work_struct work; struct vmw_private *dev_priv; struct vmw_cmdbuf_context ctx[SVGA_CB_CONTEXT_MAX]; @@ -179,12 +184,13 @@ struct vmw_cmdbuf_alloc_info { }; /* Loop over each context in the command buffer manager. */ -#define for_each_cmdbuf_ctx(_man, _i, _ctx) \ +#define for_each_cmdbuf_ctx(_man, _i, _ctx) \ for (_i = 0, _ctx = &(_man)->ctx[0]; (_i) < SVGA_CB_CONTEXT_MAX; \ ++(_i), ++(_ctx)) -static int vmw_cmdbuf_startstop(struct vmw_cmdbuf_man *man, bool enable); - +static int vmw_cmdbuf_startstop(struct vmw_cmdbuf_man *man, u32 context, + bool enable); +static int vmw_cmdbuf_preempt(struct vmw_cmdbuf_man *man, u32 context); /** * vmw_cmdbuf_cur_lock - Helper to lock the cur_mutex. @@ -329,7 +335,8 @@ static void vmw_cmdbuf_ctx_submit(struct vmw_cmdbuf_man *man, struct vmw_cmdbuf_context *ctx) { while (ctx->num_hw_submitted < man->max_hw_submitted && - !list_empty(&ctx->submitted)) { + !list_empty(&ctx->submitted) && + !ctx->block_submission) { struct vmw_cmdbuf_header *entry; SVGACBStatus status; @@ -384,12 +391,17 @@ static void vmw_cmdbuf_ctx_process(struct vmw_cmdbuf_man *man, __vmw_cmdbuf_header_free(entry); break; case SVGA_CB_STATUS_COMMAND_ERROR: - case SVGA_CB_STATUS_CB_HEADER_ERROR: + entry->cb_header->status = SVGA_CB_STATUS_NONE; list_add_tail(&entry->list, &man->error); schedule_work(&man->work); break; case SVGA_CB_STATUS_PREEMPTED: - list_add(&entry->list, &ctx->preempted); + entry->cb_header->status = SVGA_CB_STATUS_NONE; + list_add_tail(&entry->list, &ctx->preempted); + break; + case SVGA_CB_STATUS_CB_HEADER_ERROR: + WARN_ONCE(true, "Command buffer header error.\n"); + __vmw_cmdbuf_header_free(entry); break; default: WARN_ONCE(true, "Undefined command buffer status.\n"); @@ -497,24 +509,111 @@ static void vmw_cmdbuf_work_func(struct work_struct *work) container_of(work, struct vmw_cmdbuf_man, work); struct vmw_cmdbuf_header *entry, *next; uint32_t dummy; - bool restart = false; + bool restart[SVGA_CB_CONTEXT_MAX]; + bool send_fence = false; + struct list_head restart_head[SVGA_CB_CONTEXT_MAX]; + int i; + struct vmw_cmdbuf_context *ctx; + for_each_cmdbuf_ctx(man, i, ctx) { + INIT_LIST_HEAD(&restart_head[i]); + restart[i] = false; + } + + mutex_lock(&man->error_mutex); spin_lock(&man->lock); list_for_each_entry_safe(entry, next, &man->error, list) { - restart = true; - DRM_ERROR("Command buffer error.\n"); + SVGACBHeader *cb_hdr = entry->cb_header; + SVGA3dCmdHeader *header = (SVGA3dCmdHeader *) + (entry->cmd + cb_hdr->errorOffset); + u32 error_cmd_size, new_start_offset; + const char *cmd_name; + + list_del_init(&entry->list); + restart[entry->cb_context] = true; + + if (!vmw_cmd_describe(header, &error_cmd_size, &cmd_name)) { + DRM_ERROR("Unknown command causing device error.\n"); + DRM_ERROR("Command buffer offset is %lu\n", + (unsigned long) cb_hdr->errorOffset); + __vmw_cmdbuf_header_free(entry); + send_fence = true; + continue; + } - list_del(&entry->list); - __vmw_cmdbuf_header_free(entry); - wake_up_all(&man->idle_queue); + DRM_ERROR("Command \"%s\" causing device error.\n", cmd_name); + DRM_ERROR("Command buffer offset is %lu\n", + (unsigned long) cb_hdr->errorOffset); + DRM_ERROR("Command size is %lu\n", + (unsigned long) error_cmd_size); + + new_start_offset = cb_hdr->errorOffset + error_cmd_size; + + if (new_start_offset >= cb_hdr->length) { + __vmw_cmdbuf_header_free(entry); + send_fence = true; + continue; + } + + if (man->using_mob) + cb_hdr->ptr.mob.mobOffset += new_start_offset; + else + cb_hdr->ptr.pa += (u64) new_start_offset; + + entry->cmd += new_start_offset; + cb_hdr->length -= new_start_offset; + cb_hdr->errorOffset = 0; + list_add_tail(&entry->list, &restart_head[entry->cb_context]); + man->ctx[entry->cb_context].block_submission = true; + } + spin_unlock(&man->lock); + + /* Preempt all contexts with errors */ + for_each_cmdbuf_ctx(man, i, ctx) { + if (ctx->block_submission && vmw_cmdbuf_preempt(man, i)) + DRM_ERROR("Failed preempting command buffer " + "context %u.\n", i); + } + + spin_lock(&man->lock); + for_each_cmdbuf_ctx(man, i, ctx) { + if (!ctx->block_submission) + continue; + + /* Move preempted command buffers to the preempted queue. */ + vmw_cmdbuf_ctx_process(man, ctx, &dummy); + + /* + * Add the preempted queue after the command buffer + * that caused an error. + */ + list_splice_init(&ctx->preempted, restart_head[i].prev); + + /* + * Finally add all command buffers first in the submitted + * queue, to rerun them. + */ + list_splice_init(&restart_head[i], &ctx->submitted); + + ctx->block_submission = false; } + + vmw_cmdbuf_man_process(man); spin_unlock(&man->lock); - if (restart && vmw_cmdbuf_startstop(man, true)) - DRM_ERROR("Failed restarting command buffer context 0.\n"); + for_each_cmdbuf_ctx(man, i, ctx) { + if (restart[i] && vmw_cmdbuf_startstop(man, i, true)) + DRM_ERROR("Failed restarting command buffer " + "context %u.\n", i); + } /* Send a new fence in case one was removed */ - vmw_fifo_send_fence(man->dev_priv, &dummy); + if (send_fence) { + vmw_fifo_send_fence(man->dev_priv, &dummy); + wake_up_all(&man->idle_queue); + } + + mutex_unlock(&man->error_mutex); } /** @@ -1056,6 +1155,29 @@ static int vmw_cmdbuf_send_device_command(struct vmw_cmdbuf_man *man, return 0; } +/** + * vmw_cmdbuf_preempt - Send a preempt command through the device + * context. + * + * @man: The command buffer manager. + * + * Synchronously sends a preempt command. + */ +static int vmw_cmdbuf_preempt(struct vmw_cmdbuf_man *man, u32 context) +{ + struct { + uint32 id; + SVGADCCmdPreempt body; + } __packed cmd; + + cmd.id = SVGA_DC_CMD_PREEMPT; + cmd.body.context = SVGA_CB_CONTEXT_0 + context; + cmd.body.ignoreIDZero = 0; + + return vmw_cmdbuf_send_device_command(man, &cmd, sizeof(cmd)); +} + + /** * vmw_cmdbuf_startstop - Send a start / stop command through the device * context. @@ -1065,7 +1187,7 @@ static int vmw_cmdbuf_send_device_command(struct vmw_cmdbuf_man *man, * * Synchronously sends a device start / stop context command. */ -static int vmw_cmdbuf_startstop(struct vmw_cmdbuf_man *man, +static int vmw_cmdbuf_startstop(struct vmw_cmdbuf_man *man, u32 context, bool enable) { struct { @@ -1075,7 +1197,7 @@ static int vmw_cmdbuf_startstop(struct vmw_cmdbuf_man *man, cmd.id = SVGA_DC_CMD_START_STOP_CONTEXT; cmd.body.enable = (enable) ? 1 : 0; - cmd.body.context = SVGA_CB_CONTEXT_0; + cmd.body.context = SVGA_CB_CONTEXT_0 + context; return vmw_cmdbuf_send_device_command(man, &cmd, sizeof(cmd)); } @@ -1174,7 +1296,7 @@ struct vmw_cmdbuf_man *vmw_cmdbuf_man_create(struct vmw_private *dev_priv) { struct vmw_cmdbuf_man *man; struct vmw_cmdbuf_context *ctx; - int i; + unsigned int i; int ret; if (!(dev_priv->capabilities & SVGA_CAP_COMMAND_BUFFERS)) @@ -1209,6 +1331,7 @@ struct vmw_cmdbuf_man *vmw_cmdbuf_man_create(struct vmw_private *dev_priv) spin_lock_init(&man->lock); mutex_init(&man->cur_mutex); mutex_init(&man->space_mutex); + mutex_init(&man->error_mutex); man->default_size = VMW_CMDBUF_INLINE_SIZE; init_waitqueue_head(&man->alloc_queue); init_waitqueue_head(&man->idle_queue); @@ -1217,11 +1340,14 @@ struct vmw_cmdbuf_man *vmw_cmdbuf_man_create(struct vmw_private *dev_priv) INIT_WORK(&man->work, &vmw_cmdbuf_work_func); vmw_generic_waiter_add(dev_priv, SVGA_IRQFLAG_ERROR, &dev_priv->error_waiters); - ret = vmw_cmdbuf_startstop(man, true); - if (ret) { - DRM_ERROR("Failed starting command buffer context 0.\n"); - vmw_cmdbuf_man_destroy(man); - return ERR_PTR(ret); + for_each_cmdbuf_ctx(man, i, ctx) { + ret = vmw_cmdbuf_startstop(man, i, true); + if (ret) { + DRM_ERROR("Failed starting command buffer " + "context %u.\n", i); + vmw_cmdbuf_man_destroy(man); + return ERR_PTR(ret); + } } return man; @@ -1271,10 +1397,16 @@ void vmw_cmdbuf_remove_pool(struct vmw_cmdbuf_man *man) */ void vmw_cmdbuf_man_destroy(struct vmw_cmdbuf_man *man) { + struct vmw_cmdbuf_context *ctx; + unsigned int i; + WARN_ON_ONCE(man->has_pool); (void) vmw_cmdbuf_idle(man, false, 10*HZ); - if (vmw_cmdbuf_startstop(man, false)) - DRM_ERROR("Failed stopping command buffer context 0.\n"); + + for_each_cmdbuf_ctx(man, i, ctx) + if (vmw_cmdbuf_startstop(man, i, false)) + DRM_ERROR("Failed stopping command buffer " + "context %u.\n", i); vmw_generic_waiter_remove(man->dev_priv, SVGA_IRQFLAG_ERROR, &man->dev_priv->error_waiters); @@ -1283,5 +1415,6 @@ void vmw_cmdbuf_man_destroy(struct vmw_cmdbuf_man *man) dma_pool_destroy(man->headers); mutex_destroy(&man->cur_mutex); mutex_destroy(&man->space_mutex); + mutex_destroy(&man->error_mutex); kfree(man); } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index b74cee295ede..3f802e835216 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -846,7 +846,7 @@ extern int vmw_validate_single_buffer(struct vmw_private *dev_priv, struct ttm_buffer_object *bo, bool interruptible, bool validate_as_mob); - +bool vmw_cmd_describe(const void *buf, u32 *size, char const **cmd); /** * IRQs and wating - vmwgfx_irq.c diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index 2cfb3c93f42a..ba2032e04f06 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -112,11 +112,12 @@ struct vmw_cmd_entry { bool user_allow; bool gb_disable; bool gb_enable; + const char *cmd_name; }; #define VMW_CMD_DEF(_cmd, _func, _user_allow, _gb_disable, _gb_enable) \ [(_cmd) - SVGA_3D_CMD_BASE] = {(_func), (_user_allow),\ - (_gb_disable), (_gb_enable)} + (_gb_disable), (_gb_enable), #_cmd} static int vmw_resource_context_res_add(struct vmw_private *dev_priv, struct vmw_sw_context *sw_context, @@ -3469,6 +3470,51 @@ static const struct vmw_cmd_entry vmw_cmd_entries[SVGA_3D_CMD_MAX] = { true, false, true), }; +bool vmw_cmd_describe(const void *buf, u32 *size, char const **cmd) +{ + u32 cmd_id = ((u32 *) buf)[0]; + + if (cmd_id >= SVGA_CMD_MAX) { + SVGA3dCmdHeader *header = (SVGA3dCmdHeader *) buf; + const struct vmw_cmd_entry *entry; + + *size = header->size + sizeof(SVGA3dCmdHeader); + cmd_id = header->id; + if (cmd_id >= SVGA_3D_CMD_MAX) + return false; + + cmd_id -= SVGA_3D_CMD_BASE; + entry = &vmw_cmd_entries[cmd_id]; + *cmd = entry->cmd_name; + return true; + } + + switch (cmd_id) { + case SVGA_CMD_UPDATE: + *cmd = "SVGA_CMD_UPDATE"; + *size = sizeof(u32) + sizeof(SVGAFifoCmdUpdate); + break; + case SVGA_CMD_DEFINE_GMRFB: + *cmd = "SVGA_CMD_DEFINE_GMRFB"; + *size = sizeof(u32) + sizeof(SVGAFifoCmdDefineGMRFB); + break; + case SVGA_CMD_BLIT_GMRFB_TO_SCREEN: + *cmd = "SVGA_CMD_BLIT_GMRFB_TO_SCREEN"; + *size = sizeof(u32) + sizeof(SVGAFifoCmdBlitGMRFBToScreen); + break; + case SVGA_CMD_BLIT_SCREEN_TO_GMRFB: + *cmd = "SVGA_CMD_BLIT_SCREEN_TO_GMRFB"; + *size = sizeof(u32) + sizeof(SVGAFifoCmdBlitGMRFBToScreen); + break; + default: + *cmd = "UNKNOWN"; + *size = 0; + return false; + } + + return true; +} + static int vmw_cmd_check(struct vmw_private *dev_priv, struct vmw_sw_context *sw_context, void *buf, uint32_t *size) -- cgit v1.2.3 From 5f55be5f306a619e0cdc58104f06a564f5bb30d5 Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Thu, 24 Aug 2017 08:06:30 +0200 Subject: drm/vmwgfx: Support the NOP_ERROR command Can be used by user-space applications to test and verify the kernel command buffer error recovery functionality. Malicious user-space apps could potentially use this command to slow down graphics processing somewhat, but they could also accomplish the same thing using a random malformed command so this should be considered safe. At least as safe as it gets. Signed-off-by: Thomas Hellstrom Reviewed-by: Brian Paul Reviewed-by: Sinclair Yeh --- drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index ba2032e04f06..8c583fd16c79 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -3303,6 +3303,8 @@ static const struct vmw_cmd_entry vmw_cmd_entries[SVGA_3D_CMD_MAX] = { true, false, true), VMW_CMD_DEF(SVGA_3D_CMD_NOP, &vmw_cmd_ok, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_NOP_ERROR, &vmw_cmd_ok, + true, false, true), VMW_CMD_DEF(SVGA_3D_CMD_ENABLE_GART, &vmw_cmd_invalid, false, false, true), VMW_CMD_DEF(SVGA_3D_CMD_DISABLE_GART, &vmw_cmd_invalid, -- cgit v1.2.3 From 2cfa0bb25d25aa183ea29f1f9c2bc65f3f2c2264 Mon Sep 17 00:00:00 2001 From: Sinclair Yeh Date: Wed, 5 Jul 2017 01:37:55 -0700 Subject: drm/vmwgfx: Prepare to support fence fd Make the fields and flags available. Signed-off-by: Sinclair Yeh Reviewed-by: Deepak Singh Rawat Reviewed-by: Thomas Hellstrom --- drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 5 ----- include/uapi/drm/vmwgfx_drm.h | 11 ++++++++--- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index 8c583fd16c79..178dabcdb198 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -4449,11 +4449,6 @@ int vmw_execbuf_ioctl(struct drm_device *dev, unsigned long data, arg.context_handle = (uint32_t) -1; break; case 2: - if (arg.pad64 != 0) { - DRM_ERROR("Unused IOCTL data not set to zero.\n"); - return -EINVAL; - } - break; default: break; } diff --git a/include/uapi/drm/vmwgfx_drm.h b/include/uapi/drm/vmwgfx_drm.h index d9dfde9aa757..0bc784f5e0db 100644 --- a/include/uapi/drm/vmwgfx_drm.h +++ b/include/uapi/drm/vmwgfx_drm.h @@ -297,13 +297,17 @@ union drm_vmw_surface_reference_arg { * @version: Allows expanding the execbuf ioctl parameters without breaking * backwards compatibility, since user-space will always tell the kernel * which version it uses. - * @flags: Execbuf flags. None currently. + * @flags: Execbuf flags. + * @imported_fence_fd: FD for a fence imported from another device * * Argument to the DRM_VMW_EXECBUF Ioctl. */ #define DRM_VMW_EXECBUF_VERSION 2 +#define DRM_VMW_EXECBUF_FLAG_IMPORT_FENCE_FD (1 << 0) +#define DRM_VMW_EXECBUF_FLAG_EXPORT_FENCE_FD (1 << 1) + struct drm_vmw_execbuf_arg { __u64 commands; __u32 command_size; @@ -312,7 +316,7 @@ struct drm_vmw_execbuf_arg { __u32 version; __u32 flags; __u32 context_handle; - __u32 pad64; + __s32 imported_fence_fd; }; /** @@ -328,6 +332,7 @@ struct drm_vmw_execbuf_arg { * @passed_seqno: The highest seqno number processed by the hardware * so far. This can be used to mark user-space fence objects as signaled, and * to determine whether a fence seqno might be stale. + * @fd: FD associated with the fence, -1 if not exported * @error: This member should've been set to -EFAULT on submission. * The following actions should be take on completion: * error == -EFAULT: Fence communication failed. The host is synchronized. @@ -345,7 +350,7 @@ struct drm_vmw_fence_rep { __u32 mask; __u32 seqno; __u32 passed_seqno; - __u32 pad64; + __s32 fd; __s32 error; }; -- cgit v1.2.3 From 585851164660e8dff961178a9533857b21d63975 Mon Sep 17 00:00:00 2001 From: Sinclair Yeh Date: Wed, 5 Jul 2017 01:45:40 -0700 Subject: drm/vmwgfx: Add support for imported Fence File Descriptor This allows vmwgfx to wait on a fence created by another device. v2: * Remove special handling for vmwgfx fence and just use dma_fence_wait() * Use interruptible waits * Added function documentation Signed-off-by: Sinclair Yeh Reviewed-by: Deepak Singh Rawat Reviewed-by: Thomas Hellstrom --- drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 24 ++++++++++++++++-- drivers/gpu/drm/vmwgfx/vmwgfx_fence.c | 45 +++++++++++++++++++++++++++++++++ drivers/gpu/drm/vmwgfx/vmwgfx_fence.h | 4 +++ 3 files changed, 71 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index 178dabcdb198..f94f43cca58e 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -24,6 +24,7 @@ * USE OR OTHER DEALINGS IN THE SOFTWARE. * **************************************************************************/ +#include #include "vmwgfx_drv.h" #include "vmwgfx_reg.h" @@ -4414,6 +4415,7 @@ int vmw_execbuf_ioctl(struct drm_device *dev, unsigned long data, static const size_t copy_offset[] = { offsetof(struct drm_vmw_execbuf_arg, context_handle), sizeof(struct drm_vmw_execbuf_arg)}; + struct dma_fence *in_fence = NULL; if (unlikely(size < copy_offset[0])) { DRM_ERROR("Invalid command size, ioctl %d\n", @@ -4453,6 +4455,21 @@ int vmw_execbuf_ioctl(struct drm_device *dev, unsigned long data, break; } + + /* If imported a fence FD from elsewhere, then wait on it */ + if (arg.flags & DRM_VMW_EXECBUF_FLAG_IMPORT_FENCE_FD) { + in_fence = sync_file_get_fence(arg.imported_fence_fd); + + if (!in_fence) { + DRM_ERROR("Cannot get imported fence\n"); + return -EINVAL; + } + + ret = vmw_wait_dma_fence(dev_priv->fman, in_fence); + if (ret) + goto out; + } + ret = ttm_read_lock(&dev_priv->reservation_sem, true); if (unlikely(ret != 0)) return ret; @@ -4465,9 +4482,12 @@ int vmw_execbuf_ioctl(struct drm_device *dev, unsigned long data, NULL); ttm_read_unlock(&dev_priv->reservation_sem); if (unlikely(ret != 0)) - return ret; + goto out; vmw_kms_cursor_post_execbuf(dev_priv); - return 0; +out: + if (in_fence) + dma_fence_put(in_fence); + return ret; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c index c812570ff159..21563ca8ac1e 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c @@ -644,6 +644,51 @@ out_no_object: } +/** + * vmw_wait_dma_fence - Wait for a dma fence + * + * @fman: pointer to a fence manager + * @fence: DMA fence to wait on + * + * This function handles the case when the fence is actually a fence + * array. If that's the case, it'll wait on each of the child fence + */ +int vmw_wait_dma_fence(struct vmw_fence_manager *fman, + struct dma_fence *fence) +{ + struct dma_fence_array *fence_array; + int ret = 0; + int i; + + + if (dma_fence_is_signaled(fence)) + return 0; + + if (!dma_fence_is_array(fence)) + return dma_fence_wait(fence, true); + + /* From i915: Note that if the fence-array was created in + * signal-on-any mode, we should *not* decompose it into its individual + * fences. However, we don't currently store which mode the fence-array + * is operating in. Fortunately, the only user of signal-on-any is + * private to amdgpu and we should not see any incoming fence-array + * from sync-file being in signal-on-any mode. + */ + + fence_array = to_dma_fence_array(fence); + for (i = 0; i < fence_array->num_fences; i++) { + struct dma_fence *child = fence_array->fences[i]; + + ret = dma_fence_wait(child, true); + + if (ret < 0) + return ret; + } + + return 0; +} + + /** * vmw_fence_fifo_down - signal all unsignaled fence objects. */ diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h index d9d85aa6ed20..20224dba9d8e 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h @@ -28,6 +28,7 @@ #ifndef _VMWGFX_FENCE_H_ #include +#include #define VMW_FENCE_WAIT_TIMEOUT (5*HZ) @@ -102,6 +103,9 @@ extern int vmw_user_fence_create(struct drm_file *file_priv, struct vmw_fence_obj **p_fence, uint32_t *p_handle); +extern int vmw_wait_dma_fence(struct vmw_fence_manager *fman, + struct dma_fence *fence); + extern void vmw_fence_fifo_up(struct vmw_fence_manager *fman); extern void vmw_fence_fifo_down(struct vmw_fence_manager *fman); -- cgit v1.2.3 From c906965dee22d5e95d0651759ba107b420212a9f Mon Sep 17 00:00:00 2001 From: Sinclair Yeh Date: Wed, 5 Jul 2017 01:49:32 -0700 Subject: drm/vmwgfx: Add export fence to file descriptor support Added code to link a fence to a out_fence_fd file descriptor and thread out_fence_fd down to vmw_execbuf_copy_fence_user() so it can be copied into the IOCTL reply and be passed back up the the user. v2: Make sure to sync and clean up in case of failure Signed-off-by: Sinclair Yeh Reviewed-by: Deepak Singh Rawat Reviewed-by: Thomas Hellstrom --- drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 8 +++- drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 69 +++++++++++++++++++++++++++++---- drivers/gpu/drm/vmwgfx/vmwgfx_fence.c | 2 +- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 2 +- 4 files changed, 70 insertions(+), 11 deletions(-) (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 3f802e835216..d6ab22c7d263 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -40,6 +40,7 @@ #include #include #include "vmwgfx_fence.h" +#include #define VMWGFX_DRIVER_NAME "vmwgfx" #define VMWGFX_DRIVER_DATE "20170607" @@ -826,7 +827,8 @@ extern int vmw_execbuf_process(struct drm_file *file_priv, uint32_t dx_context_handle, struct drm_vmw_fence_rep __user *user_fence_rep, - struct vmw_fence_obj **out_fence); + struct vmw_fence_obj **out_fence, + uint32_t flags); extern void __vmw_execbuf_release_pinned_bo(struct vmw_private *dev_priv, struct vmw_fence_obj *fence); extern void vmw_execbuf_release_pinned_bo(struct vmw_private *dev_priv); @@ -841,7 +843,9 @@ extern void vmw_execbuf_copy_fence_user(struct vmw_private *dev_priv, struct drm_vmw_fence_rep __user *user_fence_rep, struct vmw_fence_obj *fence, - uint32_t fence_handle); + uint32_t fence_handle, + int32_t out_fence_fd, + struct sync_file *sync_file); extern int vmw_validate_single_buffer(struct vmw_private *dev_priv, struct ttm_buffer_object *bo, bool interruptible, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index f94f43cca58e..21c62a34e558 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -3830,6 +3830,8 @@ int vmw_execbuf_fence_commands(struct drm_file *file_priv, * which the information should be copied. * @fence: Pointer to the fenc object. * @fence_handle: User-space fence handle. + * @out_fence_fd: exported file descriptor for the fence. -1 if not used + * @sync_file: Only used to clean up in case of an error in this function. * * This function copies fence information to user-space. If copying fails, * The user-space struct drm_vmw_fence_rep::error member is hopefully @@ -3845,7 +3847,9 @@ vmw_execbuf_copy_fence_user(struct vmw_private *dev_priv, int ret, struct drm_vmw_fence_rep __user *user_fence_rep, struct vmw_fence_obj *fence, - uint32_t fence_handle) + uint32_t fence_handle, + int32_t out_fence_fd, + struct sync_file *sync_file) { struct drm_vmw_fence_rep fence_rep; @@ -3855,6 +3859,7 @@ vmw_execbuf_copy_fence_user(struct vmw_private *dev_priv, memset(&fence_rep, 0, sizeof(fence_rep)); fence_rep.error = ret; + fence_rep.fd = out_fence_fd; if (ret == 0) { BUG_ON(fence == NULL); @@ -3877,6 +3882,14 @@ vmw_execbuf_copy_fence_user(struct vmw_private *dev_priv, * and unreference the handle. */ if (unlikely(ret != 0) && (fence_rep.error == 0)) { + if (sync_file) + fput(sync_file->file); + + if (fence_rep.fd != -1) { + put_unused_fd(fence_rep.fd); + fence_rep.fd = -1; + } + ttm_ref_object_base_unref(vmw_fp->tfile, fence_handle, TTM_REF_USAGE); DRM_ERROR("Fence copy error. Syncing.\n"); @@ -4052,7 +4065,8 @@ int vmw_execbuf_process(struct drm_file *file_priv, uint64_t throttle_us, uint32_t dx_context_handle, struct drm_vmw_fence_rep __user *user_fence_rep, - struct vmw_fence_obj **out_fence) + struct vmw_fence_obj **out_fence, + uint32_t flags) { struct vmw_sw_context *sw_context = &dev_priv->ctx; struct vmw_fence_obj *fence = NULL; @@ -4062,20 +4076,33 @@ int vmw_execbuf_process(struct drm_file *file_priv, struct ww_acquire_ctx ticket; uint32_t handle; int ret; + int32_t out_fence_fd = -1; + struct sync_file *sync_file = NULL; + + + if (flags & DRM_VMW_EXECBUF_FLAG_EXPORT_FENCE_FD) { + out_fence_fd = get_unused_fd_flags(O_CLOEXEC); + if (out_fence_fd < 0) { + DRM_ERROR("Failed to get a fence file descriptor.\n"); + return out_fence_fd; + } + } if (throttle_us) { ret = vmw_wait_lag(dev_priv, &dev_priv->fifo.marker_queue, throttle_us); if (ret) - return ret; + goto out_free_fence_fd; } kernel_commands = vmw_execbuf_cmdbuf(dev_priv, user_commands, kernel_commands, command_size, &header); - if (IS_ERR(kernel_commands)) - return PTR_ERR(kernel_commands); + if (IS_ERR(kernel_commands)) { + ret = PTR_ERR(kernel_commands); + goto out_free_fence_fd; + } ret = mutex_lock_interruptible(&dev_priv->cmdbuf_mutex); if (ret) { @@ -4211,8 +4238,32 @@ int vmw_execbuf_process(struct drm_file *file_priv, __vmw_execbuf_release_pinned_bo(dev_priv, fence); vmw_clear_validations(sw_context); + + /* + * If anything fails here, give up trying to export the fence + * and do a sync since the user mode will not be able to sync + * the fence itself. This ensures we are still functionally + * correct. + */ + if (flags & DRM_VMW_EXECBUF_FLAG_EXPORT_FENCE_FD) { + + sync_file = sync_file_create(&fence->base); + if (!sync_file) { + DRM_ERROR("Unable to create sync file for fence\n"); + put_unused_fd(out_fence_fd); + out_fence_fd = -1; + + (void) vmw_fence_obj_wait(fence, false, false, + VMW_FENCE_WAIT_TIMEOUT); + } else { + /* Link the fence with the FD created earlier */ + fd_install(out_fence_fd, sync_file->file); + } + } + vmw_execbuf_copy_fence_user(dev_priv, vmw_fpriv(file_priv), ret, - user_fence_rep, fence, handle); + user_fence_rep, fence, handle, + out_fence_fd, sync_file); /* Don't unreference when handing fence out */ if (unlikely(out_fence != NULL)) { @@ -4263,6 +4314,9 @@ out_unlock: out_free_header: if (header) vmw_cmdbuf_header_free(header); +out_free_fence_fd: + if (out_fence_fd >= 0) + put_unused_fd(out_fence_fd); return ret; } @@ -4479,7 +4533,8 @@ int vmw_execbuf_ioctl(struct drm_device *dev, unsigned long data, NULL, arg.command_size, arg.throttle_us, arg.context_handle, (void __user *)(unsigned long)arg.fence_rep, - NULL); + NULL, + arg.flags); ttm_read_unlock(&dev_priv->reservation_sem); if (unlikely(ret != 0)) goto out; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c index 21563ca8ac1e..3bbad22b3748 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c @@ -1150,7 +1150,7 @@ int vmw_fence_event_ioctl(struct drm_device *dev, void *data, } vmw_execbuf_copy_fence_user(dev_priv, vmw_fp, 0, user_fence_rep, fence, - handle); + handle, -1, NULL); vmw_fence_obj_unreference(&fence); return 0; out_no_create: diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 36dd7930bf5f..5d50e45ae274 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -2494,7 +2494,7 @@ void vmw_kms_helper_buffer_finish(struct vmw_private *dev_priv, if (file_priv) vmw_execbuf_copy_fence_user(dev_priv, vmw_fpriv(file_priv), ret, user_fence_rep, fence, - handle); + handle, -1, NULL); if (out_fence) *out_fence = fence; else -- cgit v1.2.3