drm/vmwgfx: Add export fence to file descriptor support
authorSinclair Yeh <syeh@vmware.com>
Wed, 5 Jul 2017 08:49:32 +0000 (01:49 -0700)
committerSinclair Yeh <syeh@vmware.com>
Mon, 28 Aug 2017 15:51:46 +0000 (17:51 +0200)
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 <syeh@vmware.com>
Reviewed-by: Deepak Singh Rawat <drawat@vmware.com>
Reviewed-by: Thomas Hellstrom <thellstrom@vmware.com>
drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
drivers/gpu/drm/vmwgfx/vmwgfx_kms.c

index 3f802e83521649d7bcd869c868e6911acf8d51be..d6ab22c7d263616abf03294be29a8b77cdef4713 100644 (file)
@@ -40,6 +40,7 @@
 #include <drm/ttm/ttm_execbuf_util.h>
 #include <drm/ttm/ttm_module.h>
 #include "vmwgfx_fence.h"
+#include <linux/sync_file.h>
 
 #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,
index f94f43cca58ec3e3e171d624166bd8b3989ee963..21c62a34e5580af7e56505a64bbae48707ae2599 100644 (file)
@@ -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;
index 21563ca8ac1e724f58eb1f41f2919689f6d34387..3bbad22b37488cec5417b8e605b77ef8a4f2e399 100644 (file)
@@ -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:
index 36dd7930bf5f9b6587846f19f69eef3759a12ae8..5d50e45ae2742608ca639805d815cbff3ef29faa 100644 (file)
@@ -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