diff options
Diffstat (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c')
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 148 |
1 files changed, 133 insertions, 15 deletions
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index 2cfb3c93f42a..21c62a34e558 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 <linux/sync_file.h> #include "vmwgfx_drv.h" #include "vmwgfx_reg.h" @@ -112,11 +113,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, @@ -3302,6 +3304,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, @@ -3469,6 +3473,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) @@ -3781,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 @@ -3796,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; @@ -3806,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); @@ -3828,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"); @@ -4003,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; @@ -4013,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) { @@ -4162,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)) { @@ -4214,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; } @@ -4366,6 +4469,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", @@ -4401,15 +4505,25 @@ 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; } + + /* 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; @@ -4419,12 +4533,16 @@ 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)) - return ret; + goto out; vmw_kms_cursor_post_execbuf(dev_priv); - return 0; +out: + if (in_fence) + dma_fence_put(in_fence); + return ret; } |