<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On Wed, May 24, 2017 at 10:25 AM, Jason Ekstrand <span dir="ltr"><<a href="mailto:jason@jlekstrand.net" target="_blank">jason@jlekstrand.net</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div><div class="h5">On Wed, May 24, 2017 at 12:06 AM, Dave Airlie <span dir="ltr"><<a href="mailto:airlied@gmail.com" target="_blank">airlied@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">From: Dave Airlie <<a href="mailto:airlied@redhat.com" target="_blank">airlied@redhat.com</a>><br>
<br>
This interface will allow sync object to be used to back<br>
Vulkan fences. This API is pretty much the vulkan fence waiting<br>
API, and I've ported the code from amdgpu.<br>
<br>
v2: accept relative timeout, pass remaining time back<br>
to userspace.<br>
v3: return to absolute timeouts.<br>
<br>
Signed-off-by: Dave Airlie <<a href="mailto:airlied@redhat.com" target="_blank">airlied@redhat.com</a>><br>
---<br>
drivers/gpu/drm/drm_internal.<wbr>h | 2 +<br>
drivers/gpu/drm/drm_ioctl.c | 2 +<br>
drivers/gpu/drm/drm_syncobj.<wbr>c | 164 ++++++++++++++++++++++++++++++<wbr>+++++++++++<br>
include/uapi/drm/drm.h | 14 ++++<br>
4 files changed, 182 insertions(+)<br>
<br>
diff --git a/drivers/gpu/drm/drm_internal<wbr>.h b/drivers/gpu/drm/drm_internal<wbr>.h<br>
index 3fdef2c..53e3f6b 100644<br>
--- a/drivers/gpu/drm/drm_internal<wbr>.h<br>
+++ b/drivers/gpu/drm/drm_internal<wbr>.h<br>
@@ -156,3 +156,5 @@ int drm_syncobj_handle_to_fd_ioctl<wbr>(struct drm_device *dev, void *data,<br>
struct drm_file *file_private);<br>
int drm_syncobj_fd_to_handle_ioctl<wbr>(struct drm_device *dev, void *data,<br>
struct drm_file *file_private);<br>
+int drm_syncobj_wait_ioctl(struct drm_device *dev, void *data,<br>
+ struct drm_file *file_private);<br>
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c<br>
index f1e5681..385ce74 100644<br>
--- a/drivers/gpu/drm/drm_ioctl.c<br>
+++ b/drivers/gpu/drm/drm_ioctl.c<br>
@@ -657,6 +657,8 @@ static const struct drm_ioctl_desc drm_ioctls[] = {<br>
DRM_UNLOCKED|DRM_RENDER_ALLOW)<wbr>,<br>
DRM_IOCTL_DEF(DRM_IOCTL_SYNCOB<wbr>J_FD_TO_HANDLE, drm_syncobj_fd_to_handle_ioctl<wbr>,<br>
DRM_UNLOCKED|DRM_RENDER_ALLOW)<wbr>,<br>
+ DRM_IOCTL_DEF(DRM_IOCTL_SYNCO<wbr>BJ_WAIT, drm_syncobj_wait_ioctl,<br>
+ DRM_UNLOCKED|DRM_RENDER_<wbr>ALLOW),<br>
};<br>
<br>
#define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls )<br>
diff --git a/drivers/gpu/drm/drm_syncobj.<wbr>c b/drivers/gpu/drm/drm_syncobj.<wbr>c<br>
index b611480..8b87594 100644<br>
--- a/drivers/gpu/drm/drm_syncobj.<wbr>c<br>
+++ b/drivers/gpu/drm/drm_syncobj.<wbr>c<br>
@@ -1,5 +1,7 @@<br>
/*<br>
* Copyright 2017 Red Hat<br>
+ * Parts ported from amdgpu (fence wait code).<br>
+ * Copyright 2016 Advanced Micro Devices, Inc.<br>
*<br>
* Permission is hereby granted, free of charge, to any person obtaining a<br>
* copy of this software and associated documentation files (the "Software"),<br>
@@ -31,6 +33,9 @@<br>
* that contain an optional fence. The fence can be updated with a new<br>
* fence, or be NULL.<br>
*<br>
+ * syncobj's can be waited upon, where it will wait for the underlying<br>
+ * fence.<br>
+ *<br>
* syncobj's can be export to fd's and back, these fd's are opaque and<br>
* have no other use case, except passing the syncobj between processes.<br>
*<br>
@@ -375,3 +380,162 @@ drm_syncobj_fd_to_handle_ioctl<wbr>(struct drm_device *dev, void *data,<br>
return drm_syncobj_fd_to_handle(file_<wbr>private, args->fd,<br>
&args->handle);<br>
}<br>
+<br>
+<br>
+/**<br>
+ * drm_timeout_abs_to_jiffies - calculate jiffies timeout from absolute value<br>
+ *<br>
+ * @timeout_ns: timeout in ns<br>
+ *<br>
+ * Calculate the timeout in jiffies from an absolute timeout in ns.<br>
+ */<br>
+unsigned long drm_timeout_abs_to_jiffies(uin<wbr>t64_t timeout_ns)<br>
+{<br>
+ unsigned long timeout_jiffies;<br>
+ ktime_t timeout;<br>
+<br>
+ /* clamp timeout if it's to large */<br>
+ if (((int64_t)timeout_ns) < 0)<br>
+ return MAX_SCHEDULE_TIMEOUT;<br>
+<br>
+ timeout = ktime_sub(ns_to_ktime(timeout_<wbr>ns), ktime_get());<br>
+ if (ktime_to_ns(timeout) < 0)<br>
+ return 0;<br>
+<br>
+ timeout_jiffies = nsecs_to_jiffies(ktime_to_ns(t<wbr>imeout));<br>
+ /* clamp timeout to avoid unsigned-> signed overflow */<br>
+ if (timeout_jiffies > MAX_SCHEDULE_TIMEOUT )<br>
+ return MAX_SCHEDULE_TIMEOUT - 1;<br>
+<br>
+ return timeout_jiffies;<br>
+}<br>
+<br>
+static int drm_syncobj_wait_all_fences(st<wbr>ruct drm_device *dev,<br>
+ struct drm_file *file_private,<br>
+ struct drm_syncobj_wait *wait,<br>
+ uint32_t *handles)<br>
+{<br>
+ uint32_t i;<br>
+ int ret;<br>
+ unsigned long timeout = drm_timeout_abs_to_jiffies(wai<wbr>t->timeout_ns);<br>
+<br>
+ for (i = 0; i < wait->count_handles; i++) {<br>
+ struct dma_fence *fence;<br>
+<br>
+ ret = drm_syncobj_fence_get(file_pri<wbr>vate, handles[i],<br>
+ &fence);<br>
+ if (ret)<br>
+ return ret;<br>
+<br>
+ if (!fence)<br>
+ continue;<br>
+<br>
+ ret = dma_fence_wait_timeout(fence, true, timeout);<br>
+<br>
+ dma_fence_put(fence);<br>
+ if (ret < 0)<br>
+ return ret;<br>
+ if (ret == 0)<br>
+ break;<br>
+ timeout = ret;<br>
+ }<br>
+<br>
+ wait->out_timeout_ns = jiffies_to_nsecs(ret);<br>
+ wait->out_status = (ret > 0);<br>
+ wait->first_signaled = 0;<br>
+ return 0;<br>
+}<br>
+<br>
+static int drm_syncobj_wait_any_fence(str<wbr>uct drm_device *dev,<br>
+ struct drm_file *file_private,<br>
+ struct drm_syncobj_wait *wait,<br>
+ uint32_t *handles)<br>
+{<br>
+ unsigned long timeout = drm_timeout_abs_to_jiffies(wai<wbr>t->timeout_ns);<br>
+ struct dma_fence **array;<br>
+ uint32_t i;<br>
+ int ret;<br>
+ uint32_t first = ~0;<br>
+<br>
+ /* Prepare the fence array */<br>
+ array = kcalloc(wait->count_handles,<br>
+ sizeof(struct dma_fence *), GFP_KERNEL);<br>
+<br>
+ if (array == NULL)<br>
+ return -ENOMEM;<br>
+<br>
+ for (i = 0; i < wait->count_handles; i++) {<br>
+ struct dma_fence *fence;<br>
+<br>
+ ret = drm_syncobj_fence_get(file_pri<wbr>vate, handles[i],<br>
+ &fence);<br>
+ if (ret)<br>
+ goto err_free_fence_array;<br>
+ else if (fence)<br>
+ array[i] = fence;<br>
+ else { /* NULL, the fence has been already signaled */<br>
+ ret = 1;<br>
+ goto out;<br>
+ }<br>
+ }<br></blockquote></div></div></div></div></div></blockquote><div><br></div><div>These two functions have unexpected and subtly different behaviors when drm_syncobj_fence_get fails. wait_all may fail in the middle of waiting whereas wait_any fails up-front and holds a reference to ensure it doesn't fail later. Given that freeing things is inherently racy, it seems less than ideal to wait some arbitrary amount of time governed by fence[0] before getting fence[1]. Would it make sense to move the array setup into _ioctl and make both hold a reference to every fence?<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div><div class="h5"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+<br>
+ ret = dma_fence_wait_any_timeout(arr<wbr>ay, wait->count_handles, true, timeout,<br>
+ &first);<br>
+ if (ret < 0)<br>
+ goto err_free_fence_array;<br>
+out:<br>
+ wait->out_timeout_ns = jiffies_to_nsecs(ret);<br>
+ wait->out_status = (ret > 0);<br>
+ wait->first_signaled = first;<br>
+ /* set return value 0 to indicate success */<br>
+ ret = 0;<br>
+<br>
+err_free_fence_array:<br>
+ for (i = 0; i < wait->count_handles; i++)<br>
+ dma_fence_put(array[i]);<br>
+ kfree(array);<br>
+<br>
+ return ret;<br>
+}<br>
+<br>
+int<br>
+drm_syncobj_wait_ioctl(struct drm_device *dev, void *data,<br>
+ struct drm_file *file_private)<br>
+{<br>
+ struct drm_syncobj_wait *args = data;<br>
+ uint32_t *handles;<br>
+ int ret = 0;<br>
+<br>
+ if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))<br>
+ return -ENODEV;<br>
+<br>
+ if (args->flags != 0 && args->flags != DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AL<wbr>L)<br>
+ return -EINVAL;<br>
+<br>
+ if (args->count_handles == 0)<br>
+ return 0;<br>
+<br>
+ /* Get the handles from userspace */<br>
+ handles = kmalloc_array(args->count_hand<wbr>les, sizeof(uint32_t),<br>
+ GFP_KERNEL);<br>
+ if (handles == NULL)<br>
+ return -ENOMEM;<br>
+<br>
+ if (copy_from_user(handles,<br>
+ (void __user *)(unsigned long)(args->handles),<br>
+ sizeof(uint32_t) * args->count_handles)) {<br>
+ ret = -EFAULT;<br>
+ goto err_free_handles;<br>
+ }<br>
+<br>
+ if (args->flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AL<wbr>L)<br>
+ ret = drm_syncobj_wait_all_fences(de<wbr>v, file_private,<br>
+ args, handles);<br>
+ else<br>
+ ret = drm_syncobj_wait_any_fence(dev<wbr>, file_private,<br>
+ args, handles);<br>
+err_free_handles:<br>
+ kfree(handles);<br>
+<br>
+ return ret;<br>
+}<br>
diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h<br>
index 96c5c78..d6e2f62 100644<br>
--- a/include/uapi/drm/drm.h<br>
+++ b/include/uapi/drm/drm.h<br>
@@ -716,6 +716,19 @@ struct drm_syncobj_handle {<br>
__u32 pad;<br>
};<br>
<br>
+#define DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AL<wbr>L (1 << 0)<br>
+struct drm_syncobj_wait {<br>
+ __u64 handles;<br>
+ /* absolute timeout */<br>
+ __u64 timeout_ns;<br>
+ /* remaining timeout */<br>
+ __u64 out_timeout_ns;<br></blockquote><div><br></div></div></div><div>Any particular reason why we don't just make timeout_ns an in/out? I don't really care and haven't given it much thought; mostly just curious.<br></div><span class=""><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+ __u32 count_handles;<br>
+ __u32 flags;<br>
+ __u32 out_status;<br>
+ __u32 first_signaled; /* on valid when not waiting all */<br>
+};<br>
+<br>
#if defined(__cplusplus)<br>
}<br>
#endif<br>
@@ -838,6 +851,7 @@ extern "C" {<br>
#define DRM_IOCTL_SYNCOBJ_DESTROY DRM_IOWR(0xC0, struct drm_syncobj_destroy)<br>
#define DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD DRM_IOWR(0xC1, struct drm_syncobj_handle)<br>
#define DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE DRM_IOWR(0xC2, struct drm_syncobj_handle)<br>
+#define DRM_IOCTL_SYNCOBJ_WAIT DRM_IOWR(0xC3, struct drm_syncobj_wait)<br>
<br>
/**<br>
* Device specific ioctls should only be in their respective headers<br>
<span class="m_-1872291078869286988HOEnZb"><font color="#888888">--<br>
2.9.4<br>
<br>
______________________________<wbr>_________________<br>
dri-devel mailing list<br>
<a href="mailto:dri-devel@lists.freedesktop.org" target="_blank">dri-devel@lists.freedesktop.or<wbr>g</a><br>
<a href="https://lists.freedesktop.org/mailman/listinfo/dri-devel" rel="noreferrer" target="_blank">https://lists.freedesktop.org/<wbr>mailman/listinfo/dri-devel</a><br>
</font></span></blockquote></span></div><br></div></div>
</blockquote></div><br></div></div>