aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2016-07-15 04:31:11 -0400
committerDaniel Vetter <daniel.vetter@ffwll.ch>2016-07-18 02:54:55 -0400
commit4077798484459a2eced2050045099a466ecb618a (patch)
tree4e39e4fbc73ac8e5c51b48d676fa46d6f013e662
parentfc497ed8361f34e465f31e9babcb88efe38fe433 (diff)
drm/vgem: Attach sw fences to exported vGEM dma-buf (ioctl)
vGEM buffers are useful for passing data between software clients and hardware renders. By allowing the user to create and attach fences to the exported vGEM buffers (on the dma-buf), the user can implement a deferred renderer and queue hardware operations like flipping and then signal the buffer readiness (i.e. this allows the user to schedule operations out-of-order, but have them complete in-order). This also makes it much easier to write tightly controlled testcases for dma-buf fencing and signaling between hardware drivers. v2: Don't pretend the fences exist in an ordered timeline, but allocate a separate fence-context for each fence so that the fences are unordered. v3: Make the debug output more interesting, and show the signaled status. v4: Automatically signal the fence to prevent userspace from indefinitely hanging drivers. Testcase: igt/vgem_basic/dmabuf-fence Testcase: igt/vgem_slow/nohang Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Cc: Sean Paul <seanpaul@chromium.org> Cc: Zach Reizner <zachr@google.com> Cc: Gustavo Padovan <gustavo.padovan@collabora.co.uk> Cc: Daniel Vetter <daniel.vetter@ffwll.ch> Acked-by: Zach Reizner <zachr@google.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> Link: http://patchwork.freedesktop.org/patch/msgid/1468571471-12610-1-git-send-email-chris@chris-wilson.co.uk
-rw-r--r--drivers/gpu/drm/vgem/Makefile2
-rw-r--r--drivers/gpu/drm/vgem/vgem_drv.c34
-rw-r--r--drivers/gpu/drm/vgem/vgem_drv.h16
-rw-r--r--drivers/gpu/drm/vgem/vgem_fence.c283
-rw-r--r--include/uapi/drm/vgem_drm.h62
5 files changed, 396 insertions, 1 deletions
diff --git a/drivers/gpu/drm/vgem/Makefile b/drivers/gpu/drm/vgem/Makefile
index 3f4c7b842028..bfcdea1330e6 100644
--- a/drivers/gpu/drm/vgem/Makefile
+++ b/drivers/gpu/drm/vgem/Makefile
@@ -1,4 +1,4 @@
1ccflags-y := -Iinclude/drm 1ccflags-y := -Iinclude/drm
2vgem-y := vgem_drv.o 2vgem-y := vgem_drv.o vgem_fence.o
3 3
4obj-$(CONFIG_DRM_VGEM) += vgem.o 4obj-$(CONFIG_DRM_VGEM) += vgem.o
diff --git a/drivers/gpu/drm/vgem/vgem_drv.c b/drivers/gpu/drm/vgem/vgem_drv.c
index 29c2aab3c1a7..c15bafb06665 100644
--- a/drivers/gpu/drm/vgem/vgem_drv.c
+++ b/drivers/gpu/drm/vgem/vgem_drv.c
@@ -83,6 +83,34 @@ static const struct vm_operations_struct vgem_gem_vm_ops = {
83 .close = drm_gem_vm_close, 83 .close = drm_gem_vm_close,
84}; 84};
85 85
86static int vgem_open(struct drm_device *dev, struct drm_file *file)
87{
88 struct vgem_file *vfile;
89 int ret;
90
91 vfile = kzalloc(sizeof(*vfile), GFP_KERNEL);
92 if (!vfile)
93 return -ENOMEM;
94
95 file->driver_priv = vfile;
96
97 ret = vgem_fence_open(vfile);
98 if (ret) {
99 kfree(vfile);
100 return ret;
101 }
102
103 return 0;
104}
105
106static void vgem_preclose(struct drm_device *dev, struct drm_file *file)
107{
108 struct vgem_file *vfile = file->driver_priv;
109
110 vgem_fence_close(vfile);
111 kfree(vfile);
112}
113
86/* ioctls */ 114/* ioctls */
87 115
88static struct drm_gem_object *vgem_gem_create(struct drm_device *dev, 116static struct drm_gem_object *vgem_gem_create(struct drm_device *dev,
@@ -164,6 +192,8 @@ unref:
164} 192}
165 193
166static struct drm_ioctl_desc vgem_ioctls[] = { 194static struct drm_ioctl_desc vgem_ioctls[] = {
195 DRM_IOCTL_DEF_DRV(VGEM_FENCE_ATTACH, vgem_fence_attach_ioctl, DRM_AUTH|DRM_RENDER_ALLOW),
196 DRM_IOCTL_DEF_DRV(VGEM_FENCE_SIGNAL, vgem_fence_signal_ioctl, DRM_AUTH|DRM_RENDER_ALLOW),
167}; 197};
168 198
169static int vgem_mmap(struct file *filp, struct vm_area_struct *vma) 199static int vgem_mmap(struct file *filp, struct vm_area_struct *vma)
@@ -271,9 +301,12 @@ static int vgem_prime_mmap(struct drm_gem_object *obj,
271 301
272static struct drm_driver vgem_driver = { 302static struct drm_driver vgem_driver = {
273 .driver_features = DRIVER_GEM | DRIVER_PRIME, 303 .driver_features = DRIVER_GEM | DRIVER_PRIME,
304 .open = vgem_open,
305 .preclose = vgem_preclose,
274 .gem_free_object_unlocked = vgem_gem_free_object, 306 .gem_free_object_unlocked = vgem_gem_free_object,
275 .gem_vm_ops = &vgem_gem_vm_ops, 307 .gem_vm_ops = &vgem_gem_vm_ops,
276 .ioctls = vgem_ioctls, 308 .ioctls = vgem_ioctls,
309 .num_ioctls = ARRAY_SIZE(vgem_ioctls),
277 .fops = &vgem_driver_fops, 310 .fops = &vgem_driver_fops,
278 311
279 .dumb_create = vgem_gem_dumb_create, 312 .dumb_create = vgem_gem_dumb_create,
@@ -328,5 +361,6 @@ module_init(vgem_init);
328module_exit(vgem_exit); 361module_exit(vgem_exit);
329 362
330MODULE_AUTHOR("Red Hat, Inc."); 363MODULE_AUTHOR("Red Hat, Inc.");
364MODULE_AUTHOR("Intel Corporation");
331MODULE_DESCRIPTION(DRIVER_DESC); 365MODULE_DESCRIPTION(DRIVER_DESC);
332MODULE_LICENSE("GPL and additional rights"); 366MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/gpu/drm/vgem/vgem_drv.h b/drivers/gpu/drm/vgem/vgem_drv.h
index 988cbaae7588..1f8798ad329c 100644
--- a/drivers/gpu/drm/vgem/vgem_drv.h
+++ b/drivers/gpu/drm/vgem/vgem_drv.h
@@ -32,9 +32,25 @@
32#include <drm/drmP.h> 32#include <drm/drmP.h>
33#include <drm/drm_gem.h> 33#include <drm/drm_gem.h>
34 34
35#include <uapi/drm/vgem_drm.h>
36
37struct vgem_file {
38 struct idr fence_idr;
39 struct mutex fence_mutex;
40};
41
35#define to_vgem_bo(x) container_of(x, struct drm_vgem_gem_object, base) 42#define to_vgem_bo(x) container_of(x, struct drm_vgem_gem_object, base)
36struct drm_vgem_gem_object { 43struct drm_vgem_gem_object {
37 struct drm_gem_object base; 44 struct drm_gem_object base;
38}; 45};
39 46
47int vgem_fence_open(struct vgem_file *file);
48int vgem_fence_attach_ioctl(struct drm_device *dev,
49 void *data,
50 struct drm_file *file);
51int vgem_fence_signal_ioctl(struct drm_device *dev,
52 void *data,
53 struct drm_file *file);
54void vgem_fence_close(struct vgem_file *file);
55
40#endif 56#endif
diff --git a/drivers/gpu/drm/vgem/vgem_fence.c b/drivers/gpu/drm/vgem/vgem_fence.c
new file mode 100644
index 000000000000..e77b52208699
--- /dev/null
+++ b/drivers/gpu/drm/vgem/vgem_fence.c
@@ -0,0 +1,283 @@
1/*
2 * Copyright 2016 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software")
6 * to deal in the software without restriction, including without limitation
7 * on the rights to use, copy, modify, merge, publish, distribute, sub
8 * license, and/or sell copies of the Software, and to permit persons to whom
9 * them Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTIBILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER
19 * IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 */
22
23#include <linux/dma-buf.h>
24#include <linux/reservation.h>
25
26#include "vgem_drv.h"
27
28#define VGEM_FENCE_TIMEOUT (10*HZ)
29
30struct vgem_fence {
31 struct fence base;
32 struct spinlock lock;
33 struct timer_list timer;
34};
35
36static const char *vgem_fence_get_driver_name(struct fence *fence)
37{
38 return "vgem";
39}
40
41static const char *vgem_fence_get_timeline_name(struct fence *fence)
42{
43 return "unbound";
44}
45
46static bool vgem_fence_signaled(struct fence *fence)
47{
48 return false;
49}
50
51static bool vgem_fence_enable_signaling(struct fence *fence)
52{
53 return true;
54}
55
56static void vgem_fence_release(struct fence *base)
57{
58 struct vgem_fence *fence = container_of(base, typeof(*fence), base);
59
60 del_timer_sync(&fence->timer);
61 fence_free(&fence->base);
62}
63
64static void vgem_fence_value_str(struct fence *fence, char *str, int size)
65{
66 snprintf(str, size, "%u", fence->seqno);
67}
68
69static void vgem_fence_timeline_value_str(struct fence *fence, char *str,
70 int size)
71{
72 snprintf(str, size, "%u", fence_is_signaled(fence) ? fence->seqno : 0);
73}
74
75const struct fence_ops vgem_fence_ops = {
76 .get_driver_name = vgem_fence_get_driver_name,
77 .get_timeline_name = vgem_fence_get_timeline_name,
78 .enable_signaling = vgem_fence_enable_signaling,
79 .signaled = vgem_fence_signaled,
80 .wait = fence_default_wait,
81 .release = vgem_fence_release,
82
83 .fence_value_str = vgem_fence_value_str,
84 .timeline_value_str = vgem_fence_timeline_value_str,
85};
86
87static void vgem_fence_timeout(unsigned long data)
88{
89 struct vgem_fence *fence = (struct vgem_fence *)data;
90
91 fence_signal(&fence->base);
92}
93
94static struct fence *vgem_fence_create(struct vgem_file *vfile,
95 unsigned int flags)
96{
97 struct vgem_fence *fence;
98
99 fence = kzalloc(sizeof(*fence), GFP_KERNEL);
100 if (!fence)
101 return NULL;
102
103 spin_lock_init(&fence->lock);
104 fence_init(&fence->base, &vgem_fence_ops, &fence->lock,
105 fence_context_alloc(1), 1);
106
107 setup_timer(&fence->timer, vgem_fence_timeout, (unsigned long)fence);
108
109 /* We force the fence to expire within 10s to prevent driver hangs */
110 mod_timer(&fence->timer, VGEM_FENCE_TIMEOUT);
111
112 return &fence->base;
113}
114
115static int attach_dmabuf(struct drm_device *dev,
116 struct drm_gem_object *obj)
117{
118 struct dma_buf *dmabuf;
119
120 if (obj->dma_buf)
121 return 0;
122
123 dmabuf = dev->driver->gem_prime_export(dev, obj, 0);
124 if (IS_ERR(dmabuf))
125 return PTR_ERR(dmabuf);
126
127 obj->dma_buf = dmabuf;
128 drm_gem_object_reference(obj);
129 return 0;
130}
131
132/*
133 * vgem_fence_attach_ioctl (DRM_IOCTL_VGEM_FENCE_ATTACH):
134 *
135 * Create and attach a fence to the vGEM handle. This fence is then exposed
136 * via the dma-buf reservation object and visible to consumers of the exported
137 * dma-buf. If the flags contain VGEM_FENCE_WRITE, the fence indicates the
138 * vGEM buffer is being written to by the client and is exposed as an exclusive
139 * fence, otherwise the fence indicates the client is current reading from the
140 * buffer and all future writes should wait for the client to signal its
141 * completion. Note that if a conflicting fence is already on the dma-buf (i.e.
142 * an exclusive fence when adding a read, or any fence when adding a write),
143 * -EBUSY is reported. Serialisation between operations should be handled
144 * by waiting upon the dma-buf.
145 *
146 * This returns the handle for the new fence that must be signaled within 10
147 * seconds (or otherwise it will automatically expire). See
148 * vgem_fence_signal_ioctl (DRM_IOCTL_VGEM_FENCE_SIGNAL).
149 *
150 * If the vGEM handle does not exist, vgem_fence_attach_ioctl returns -ENOENT.
151 */
152int vgem_fence_attach_ioctl(struct drm_device *dev,
153 void *data,
154 struct drm_file *file)
155{
156 struct drm_vgem_fence_attach *arg = data;
157 struct vgem_file *vfile = file->driver_priv;
158 struct reservation_object *resv;
159 struct drm_gem_object *obj;
160 struct fence *fence;
161 int ret;
162
163 if (arg->flags & ~VGEM_FENCE_WRITE)
164 return -EINVAL;
165
166 if (arg->pad)
167 return -EINVAL;
168
169 obj = drm_gem_object_lookup(file, arg->handle);
170 if (!obj)
171 return -ENOENT;
172
173 ret = attach_dmabuf(dev, obj);
174 if (ret)
175 goto err;
176
177 fence = vgem_fence_create(vfile, arg->flags);
178 if (!fence) {
179 ret = -ENOMEM;
180 goto err;
181 }
182
183 /* Check for a conflicting fence */
184 resv = obj->dma_buf->resv;
185 if (!reservation_object_test_signaled_rcu(resv,
186 arg->flags & VGEM_FENCE_WRITE)) {
187 ret = -EBUSY;
188 goto err_fence;
189 }
190
191 /* Expose the fence via the dma-buf */
192 ret = 0;
193 mutex_lock(&resv->lock.base);
194 if (arg->flags & VGEM_FENCE_WRITE)
195 reservation_object_add_excl_fence(resv, fence);
196 else if ((ret = reservation_object_reserve_shared(resv)) == 0)
197 reservation_object_add_shared_fence(resv, fence);
198 mutex_unlock(&resv->lock.base);
199
200 /* Record the fence in our idr for later signaling */
201 if (ret == 0) {
202 mutex_lock(&vfile->fence_mutex);
203 ret = idr_alloc(&vfile->fence_idr, fence, 1, 0, GFP_KERNEL);
204 mutex_unlock(&vfile->fence_mutex);
205 if (ret > 0) {
206 arg->out_fence = ret;
207 ret = 0;
208 }
209 }
210err_fence:
211 if (ret) {
212 fence_signal(fence);
213 fence_put(fence);
214 }
215err:
216 drm_gem_object_unreference_unlocked(obj);
217 return ret;
218}
219
220/*
221 * vgem_fence_signal_ioctl (DRM_IOCTL_VGEM_FENCE_SIGNAL):
222 *
223 * Signal and consume a fence ealier attached to a vGEM handle using
224 * vgem_fence_attach_ioctl (DRM_IOCTL_VGEM_FENCE_ATTACH).
225 *
226 * All fences must be signaled within 10s of attachment or otherwise they
227 * will automatically expire (and a vgem_fence_signal_ioctl returns -ETIMEDOUT).
228 *
229 * Signaling a fence indicates to all consumers of the dma-buf that the
230 * client has completed the operation associated with the fence, and that the
231 * buffer is then ready for consumption.
232 *
233 * If the fence does not exist (or has already been signaled by the client),
234 * vgem_fence_signal_ioctl returns -ENOENT.
235 */
236int vgem_fence_signal_ioctl(struct drm_device *dev,
237 void *data,
238 struct drm_file *file)
239{
240 struct vgem_file *vfile = file->driver_priv;
241 struct drm_vgem_fence_signal *arg = data;
242 struct fence *fence;
243 int ret;
244
245 if (arg->flags)
246 return -EINVAL;
247
248 mutex_lock(&vfile->fence_mutex);
249 fence = idr_replace(&vfile->fence_idr, NULL, arg->fence);
250 mutex_unlock(&vfile->fence_mutex);
251 if (!fence)
252 return -ENOENT;
253 if (IS_ERR(fence))
254 return PTR_ERR(fence);
255
256 if (fence_is_signaled(fence))
257 ret = -ETIMEDOUT;
258
259 fence_signal(fence);
260 fence_put(fence);
261 return ret;
262}
263
264int vgem_fence_open(struct vgem_file *vfile)
265{
266 mutex_init(&vfile->fence_mutex);
267 idr_init(&vfile->fence_idr);
268
269 return 0;
270}
271
272static int __vgem_fence_idr_fini(int id, void *p, void *data)
273{
274 fence_signal(p);
275 fence_put(p);
276 return 0;
277}
278
279void vgem_fence_close(struct vgem_file *vfile)
280{
281 idr_for_each(&vfile->fence_idr, __vgem_fence_idr_fini, vfile);
282 idr_destroy(&vfile->fence_idr);
283}
diff --git a/include/uapi/drm/vgem_drm.h b/include/uapi/drm/vgem_drm.h
new file mode 100644
index 000000000000..bf66f5db6da8
--- /dev/null
+++ b/include/uapi/drm/vgem_drm.h
@@ -0,0 +1,62 @@
1/*
2 * Copyright 2016 Intel Corporation
3 * All Rights Reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sub license, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial portions
15 * of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
20 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
21 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 *
25 */
26
27#ifndef _UAPI_VGEM_DRM_H_
28#define _UAPI_VGEM_DRM_H_
29
30#include "drm.h"
31
32#if defined(__cplusplus)
33extern "C" {
34#endif
35
36/* Please note that modifications to all structs defined here are
37 * subject to backwards-compatibility constraints.
38 */
39#define DRM_VGEM_FENCE_ATTACH 0x1
40#define DRM_VGEM_FENCE_SIGNAL 0x2
41
42#define DRM_IOCTL_VGEM_FENCE_ATTACH DRM_IOWR( DRM_COMMAND_BASE + DRM_VGEM_FENCE_ATTACH, struct drm_vgem_fence_attach)
43#define DRM_IOCTL_VGEM_FENCE_SIGNAL DRM_IOW( DRM_COMMAND_BASE + DRM_VGEM_FENCE_SIGNAL, struct drm_vgem_fence_signal)
44
45struct drm_vgem_fence_attach {
46 __u32 handle;
47 __u32 flags;
48#define VGEM_FENCE_WRITE 0x1
49 __u32 out_fence;
50 __u32 pad;
51};
52
53struct drm_vgem_fence_signal {
54 __u32 fence;
55 __u32 flags;
56};
57
58#if defined(__cplusplus)
59}
60#endif
61
62#endif /* _UAPI_VGEM_DRM_H_ */