hc
2024-10-22 8ac6c7a54ed1b98d142dce24b11c6de6a1e239a5
kernel/drivers/gpu/drm/drm_syncobj.c
....@@ -29,32 +29,187 @@
2929 /**
3030 * DOC: Overview
3131 *
32
- * DRM synchronisation objects (syncobj, see struct &drm_syncobj) are
33
- * persistent objects that contain an optional fence. The fence can be updated
34
- * with a new fence, or be NULL.
35
- *
36
- * syncobj's can be waited upon, where it will wait for the underlying
37
- * fence.
38
- *
39
- * syncobj's can be export to fd's and back, these fd's are opaque and
40
- * have no other use case, except passing the syncobj between processes.
41
- *
32
+ * DRM synchronisation objects (syncobj, see struct &drm_syncobj) provide a
33
+ * container for a synchronization primitive which can be used by userspace
34
+ * to explicitly synchronize GPU commands, can be shared between userspace
35
+ * processes, and can be shared between different DRM drivers.
4236 * Their primary use-case is to implement Vulkan fences and semaphores.
37
+ * The syncobj userspace API provides ioctls for several operations:
4338 *
44
- * syncobj have a kref reference count, but also have an optional file.
45
- * The file is only created once the syncobj is exported.
46
- * The file takes a reference on the kref.
39
+ * - Creation and destruction of syncobjs
40
+ * - Import and export of syncobjs to/from a syncobj file descriptor
41
+ * - Import and export a syncobj's underlying fence to/from a sync file
42
+ * - Reset a syncobj (set its fence to NULL)
43
+ * - Signal a syncobj (set a trivially signaled fence)
44
+ * - Wait for a syncobj's fence to appear and be signaled
45
+ *
46
+ * The syncobj userspace API also provides operations to manipulate a syncobj
47
+ * in terms of a timeline of struct &dma_fence_chain rather than a single
48
+ * struct &dma_fence, through the following operations:
49
+ *
50
+ * - Signal a given point on the timeline
51
+ * - Wait for a given point to appear and/or be signaled
52
+ * - Import and export from/to a given point of a timeline
53
+ *
54
+ * At it's core, a syncobj is simply a wrapper around a pointer to a struct
55
+ * &dma_fence which may be NULL.
56
+ * When a syncobj is first created, its pointer is either NULL or a pointer
57
+ * to an already signaled fence depending on whether the
58
+ * &DRM_SYNCOBJ_CREATE_SIGNALED flag is passed to
59
+ * &DRM_IOCTL_SYNCOBJ_CREATE.
60
+ *
61
+ * If the syncobj is considered as a binary (its state is either signaled or
62
+ * unsignaled) primitive, when GPU work is enqueued in a DRM driver to signal
63
+ * the syncobj, the syncobj's fence is replaced with a fence which will be
64
+ * signaled by the completion of that work.
65
+ * If the syncobj is considered as a timeline primitive, when GPU work is
66
+ * enqueued in a DRM driver to signal the a given point of the syncobj, a new
67
+ * struct &dma_fence_chain pointing to the DRM driver's fence and also
68
+ * pointing to the previous fence that was in the syncobj. The new struct
69
+ * &dma_fence_chain fence replace the syncobj's fence and will be signaled by
70
+ * completion of the DRM driver's work and also any work associated with the
71
+ * fence previously in the syncobj.
72
+ *
73
+ * When GPU work which waits on a syncobj is enqueued in a DRM driver, at the
74
+ * time the work is enqueued, it waits on the syncobj's fence before
75
+ * submitting the work to hardware. That fence is either :
76
+ *
77
+ * - The syncobj's current fence if the syncobj is considered as a binary
78
+ * primitive.
79
+ * - The struct &dma_fence associated with a given point if the syncobj is
80
+ * considered as a timeline primitive.
81
+ *
82
+ * If the syncobj's fence is NULL or not present in the syncobj's timeline,
83
+ * the enqueue operation is expected to fail.
84
+ *
85
+ * With binary syncobj, all manipulation of the syncobjs's fence happens in
86
+ * terms of the current fence at the time the ioctl is called by userspace
87
+ * regardless of whether that operation is an immediate host-side operation
88
+ * (signal or reset) or or an operation which is enqueued in some driver
89
+ * queue. &DRM_IOCTL_SYNCOBJ_RESET and &DRM_IOCTL_SYNCOBJ_SIGNAL can be used
90
+ * to manipulate a syncobj from the host by resetting its pointer to NULL or
91
+ * setting its pointer to a fence which is already signaled.
92
+ *
93
+ * With a timeline syncobj, all manipulation of the synobj's fence happens in
94
+ * terms of a u64 value referring to point in the timeline. See
95
+ * dma_fence_chain_find_seqno() to see how a given point is found in the
96
+ * timeline.
97
+ *
98
+ * Note that applications should be careful to always use timeline set of
99
+ * ioctl() when dealing with syncobj considered as timeline. Using a binary
100
+ * set of ioctl() with a syncobj considered as timeline could result incorrect
101
+ * synchronization. The use of binary syncobj is supported through the
102
+ * timeline set of ioctl() by using a point value of 0, this will reproduce
103
+ * the behavior of the binary set of ioctl() (for example replace the
104
+ * syncobj's fence when signaling).
105
+ *
106
+ *
107
+ * Host-side wait on syncobjs
108
+ * --------------------------
109
+ *
110
+ * &DRM_IOCTL_SYNCOBJ_WAIT takes an array of syncobj handles and does a
111
+ * host-side wait on all of the syncobj fences simultaneously.
112
+ * If &DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL is set, the wait ioctl will wait on
113
+ * all of the syncobj fences to be signaled before it returns.
114
+ * Otherwise, it returns once at least one syncobj fence has been signaled
115
+ * and the index of a signaled fence is written back to the client.
116
+ *
117
+ * Unlike the enqueued GPU work dependencies which fail if they see a NULL
118
+ * fence in a syncobj, if &DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT is set,
119
+ * the host-side wait will first wait for the syncobj to receive a non-NULL
120
+ * fence and then wait on that fence.
121
+ * If &DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT is not set and any one of the
122
+ * syncobjs in the array has a NULL fence, -EINVAL will be returned.
123
+ * Assuming the syncobj starts off with a NULL fence, this allows a client
124
+ * to do a host wait in one thread (or process) which waits on GPU work
125
+ * submitted in another thread (or process) without having to manually
126
+ * synchronize between the two.
127
+ * This requirement is inherited from the Vulkan fence API.
128
+ *
129
+ * Similarly, &DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT takes an array of syncobj
130
+ * handles as well as an array of u64 points and does a host-side wait on all
131
+ * of syncobj fences at the given points simultaneously.
132
+ *
133
+ * &DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT also adds the ability to wait for a given
134
+ * fence to materialize on the timeline without waiting for the fence to be
135
+ * signaled by using the &DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE flag. This
136
+ * requirement is inherited from the wait-before-signal behavior required by
137
+ * the Vulkan timeline semaphore API.
138
+ *
139
+ *
140
+ * Import/export of syncobjs
141
+ * -------------------------
142
+ *
143
+ * &DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE and &DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD
144
+ * provide two mechanisms for import/export of syncobjs.
145
+ *
146
+ * The first lets the client import or export an entire syncobj to a file
147
+ * descriptor.
148
+ * These fd's are opaque and have no other use case, except passing the
149
+ * syncobj between processes.
150
+ * All exported file descriptors and any syncobj handles created as a
151
+ * result of importing those file descriptors own a reference to the
152
+ * same underlying struct &drm_syncobj and the syncobj can be used
153
+ * persistently across all the processes with which it is shared.
154
+ * The syncobj is freed only once the last reference is dropped.
155
+ * Unlike dma-buf, importing a syncobj creates a new handle (with its own
156
+ * reference) for every import instead of de-duplicating.
157
+ * The primary use-case of this persistent import/export is for shared
158
+ * Vulkan fences and semaphores.
159
+ *
160
+ * The second import/export mechanism, which is indicated by
161
+ * &DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE or
162
+ * &DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE lets the client
163
+ * import/export the syncobj's current fence from/to a &sync_file.
164
+ * When a syncobj is exported to a sync file, that sync file wraps the
165
+ * sycnobj's fence at the time of export and any later signal or reset
166
+ * operations on the syncobj will not affect the exported sync file.
167
+ * When a sync file is imported into a syncobj, the syncobj's fence is set
168
+ * to the fence wrapped by that sync file.
169
+ * Because sync files are immutable, resetting or signaling the syncobj
170
+ * will not affect any sync files whose fences have been imported into the
171
+ * syncobj.
172
+ *
173
+ *
174
+ * Import/export of timeline points in timeline syncobjs
175
+ * -----------------------------------------------------
176
+ *
177
+ * &DRM_IOCTL_SYNCOBJ_TRANSFER provides a mechanism to transfer a struct
178
+ * &dma_fence_chain of a syncobj at a given u64 point to another u64 point
179
+ * into another syncobj.
180
+ *
181
+ * Note that if you want to transfer a struct &dma_fence_chain from a given
182
+ * point on a timeline syncobj from/into a binary syncobj, you can use the
183
+ * point 0 to mean take/replace the fence in the syncobj.
47184 */
48185
49
-#include <drm/drmP.h>
186
+#include <linux/anon_inodes.h>
50187 #include <linux/file.h>
51188 #include <linux/fs.h>
52
-#include <linux/anon_inodes.h>
53
-#include <linux/sync_file.h>
54189 #include <linux/sched/signal.h>
190
+#include <linux/sync_file.h>
191
+#include <linux/uaccess.h>
192
+
193
+#include <drm/drm.h>
194
+#include <drm/drm_drv.h>
195
+#include <drm/drm_file.h>
196
+#include <drm/drm_gem.h>
197
+#include <drm/drm_print.h>
198
+#include <drm/drm_syncobj.h>
199
+#include <drm/drm_utils.h>
55200
56201 #include "drm_internal.h"
57
-#include <drm/drm_syncobj.h>
202
+
203
+struct syncobj_wait_entry {
204
+ struct list_head node;
205
+ struct task_struct *task;
206
+ struct dma_fence *fence;
207
+ struct dma_fence_cb fence_cb;
208
+ u64 point;
209
+};
210
+
211
+static void syncobj_wait_syncobj_func(struct drm_syncobj *syncobj,
212
+ struct syncobj_wait_entry *wait);
58213
59214 /**
60215 * drm_syncobj_find - lookup and reference a sync object.
....@@ -82,77 +237,79 @@
82237 }
83238 EXPORT_SYMBOL(drm_syncobj_find);
84239
85
-static void drm_syncobj_add_callback_locked(struct drm_syncobj *syncobj,
86
- struct drm_syncobj_cb *cb,
87
- drm_syncobj_func_t func)
240
+static void drm_syncobj_fence_add_wait(struct drm_syncobj *syncobj,
241
+ struct syncobj_wait_entry *wait)
88242 {
89
- cb->func = func;
90
- list_add_tail(&cb->node, &syncobj->cb_list);
91
-}
243
+ struct dma_fence *fence;
92244
93
-static int drm_syncobj_fence_get_or_add_callback(struct drm_syncobj *syncobj,
94
- struct dma_fence **fence,
95
- struct drm_syncobj_cb *cb,
96
- drm_syncobj_func_t func)
97
-{
98
- int ret;
99
-
100
- WARN_ON(*fence);
101
-
102
- *fence = drm_syncobj_fence_get(syncobj);
103
- if (*fence)
104
- return 1;
245
+ if (wait->fence)
246
+ return;
105247
106248 spin_lock(&syncobj->lock);
107249 /* We've already tried once to get a fence and failed. Now that we
108250 * have the lock, try one more time just to be sure we don't add a
109251 * callback when a fence has already been set.
110252 */
111
- if (syncobj->fence) {
112
- *fence = dma_fence_get(rcu_dereference_protected(syncobj->fence,
113
- lockdep_is_held(&syncobj->lock)));
114
- ret = 1;
253
+ fence = dma_fence_get(rcu_dereference_protected(syncobj->fence, 1));
254
+ if (!fence || dma_fence_chain_find_seqno(&fence, wait->point)) {
255
+ dma_fence_put(fence);
256
+ list_add_tail(&wait->node, &syncobj->cb_list);
257
+ } else if (!fence) {
258
+ wait->fence = dma_fence_get_stub();
115259 } else {
116
- *fence = NULL;
117
- drm_syncobj_add_callback_locked(syncobj, cb, func);
118
- ret = 0;
260
+ wait->fence = fence;
119261 }
120262 spin_unlock(&syncobj->lock);
263
+}
121264
122
- return ret;
265
+static void drm_syncobj_remove_wait(struct drm_syncobj *syncobj,
266
+ struct syncobj_wait_entry *wait)
267
+{
268
+ if (!wait->node.next)
269
+ return;
270
+
271
+ spin_lock(&syncobj->lock);
272
+ list_del_init(&wait->node);
273
+ spin_unlock(&syncobj->lock);
123274 }
124275
125276 /**
126
- * drm_syncobj_add_callback - adds a callback to syncobj::cb_list
127
- * @syncobj: Sync object to which to add the callback
128
- * @cb: Callback to add
129
- * @func: Func to use when initializing the drm_syncobj_cb struct
277
+ * drm_syncobj_add_point - add new timeline point to the syncobj
278
+ * @syncobj: sync object to add timeline point do
279
+ * @chain: chain node to use to add the point
280
+ * @fence: fence to encapsulate in the chain node
281
+ * @point: sequence number to use for the point
130282 *
131
- * This adds a callback to be called next time the fence is replaced
283
+ * Add the chain node as new timeline point to the syncobj.
132284 */
133
-void drm_syncobj_add_callback(struct drm_syncobj *syncobj,
134
- struct drm_syncobj_cb *cb,
135
- drm_syncobj_func_t func)
285
+void drm_syncobj_add_point(struct drm_syncobj *syncobj,
286
+ struct dma_fence_chain *chain,
287
+ struct dma_fence *fence,
288
+ uint64_t point)
136289 {
137
- spin_lock(&syncobj->lock);
138
- drm_syncobj_add_callback_locked(syncobj, cb, func);
139
- spin_unlock(&syncobj->lock);
140
-}
141
-EXPORT_SYMBOL(drm_syncobj_add_callback);
290
+ struct syncobj_wait_entry *cur, *tmp;
291
+ struct dma_fence *prev;
142292
143
-/**
144
- * drm_syncobj_add_callback - removes a callback to syncobj::cb_list
145
- * @syncobj: Sync object from which to remove the callback
146
- * @cb: Callback to remove
147
- */
148
-void drm_syncobj_remove_callback(struct drm_syncobj *syncobj,
149
- struct drm_syncobj_cb *cb)
150
-{
293
+ dma_fence_get(fence);
294
+
151295 spin_lock(&syncobj->lock);
152
- list_del_init(&cb->node);
296
+
297
+ prev = drm_syncobj_fence_get(syncobj);
298
+ /* You are adding an unorder point to timeline, which could cause payload returned from query_ioctl is 0! */
299
+ if (prev && prev->seqno >= point)
300
+ DRM_DEBUG("You are adding an unorder point to timeline!\n");
301
+ dma_fence_chain_init(chain, prev, fence, point);
302
+ rcu_assign_pointer(syncobj->fence, &chain->base);
303
+
304
+ list_for_each_entry_safe(cur, tmp, &syncobj->cb_list, node)
305
+ syncobj_wait_syncobj_func(syncobj, cur);
153306 spin_unlock(&syncobj->lock);
307
+
308
+ /* Walk the chain once to trigger garbage collection */
309
+ dma_fence_chain_for_each(fence, prev);
310
+ dma_fence_put(prev);
154311 }
155
-EXPORT_SYMBOL(drm_syncobj_remove_callback);
312
+EXPORT_SYMBOL(drm_syncobj_add_point);
156313
157314 /**
158315 * drm_syncobj_replace_fence - replace fence in a sync object.
....@@ -165,7 +322,7 @@
165322 struct dma_fence *fence)
166323 {
167324 struct dma_fence *old_fence;
168
- struct drm_syncobj_cb *cur, *tmp;
325
+ struct syncobj_wait_entry *cur, *tmp;
169326
170327 if (fence)
171328 dma_fence_get(fence);
....@@ -177,10 +334,8 @@
177334 rcu_assign_pointer(syncobj->fence, fence);
178335
179336 if (fence != old_fence) {
180
- list_for_each_entry_safe(cur, tmp, &syncobj->cb_list, node) {
181
- list_del_init(&cur->node);
182
- cur->func(syncobj, cur);
183
- }
337
+ list_for_each_entry_safe(cur, tmp, &syncobj->cb_list, node)
338
+ syncobj_wait_syncobj_func(syncobj, cur);
184339 }
185340
186341 spin_unlock(&syncobj->lock);
....@@ -189,52 +344,28 @@
189344 }
190345 EXPORT_SYMBOL(drm_syncobj_replace_fence);
191346
192
-struct drm_syncobj_null_fence {
193
- struct dma_fence base;
194
- spinlock_t lock;
195
-};
196
-
197
-static const char *drm_syncobj_null_fence_get_name(struct dma_fence *fence)
347
+/**
348
+ * drm_syncobj_assign_null_handle - assign a stub fence to the sync object
349
+ * @syncobj: sync object to assign the fence on
350
+ *
351
+ * Assign a already signaled stub fence to the sync object.
352
+ */
353
+static void drm_syncobj_assign_null_handle(struct drm_syncobj *syncobj)
198354 {
199
- return "syncobjnull";
355
+ struct dma_fence *fence = dma_fence_get_stub();
356
+
357
+ drm_syncobj_replace_fence(syncobj, fence);
358
+ dma_fence_put(fence);
200359 }
201360
202
-static bool drm_syncobj_null_fence_enable_signaling(struct dma_fence *fence)
203
-{
204
- dma_fence_enable_sw_signaling(fence);
205
- return !dma_fence_is_signaled(fence);
206
-}
207
-
208
-static const struct dma_fence_ops drm_syncobj_null_fence_ops = {
209
- .get_driver_name = drm_syncobj_null_fence_get_name,
210
- .get_timeline_name = drm_syncobj_null_fence_get_name,
211
- .enable_signaling = drm_syncobj_null_fence_enable_signaling,
212
- .release = NULL,
213
-};
214
-
215
-static int drm_syncobj_assign_null_handle(struct drm_syncobj *syncobj)
216
-{
217
- struct drm_syncobj_null_fence *fence;
218
- fence = kzalloc(sizeof(*fence), GFP_KERNEL);
219
- if (fence == NULL)
220
- return -ENOMEM;
221
-
222
- spin_lock_init(&fence->lock);
223
- dma_fence_init(&fence->base, &drm_syncobj_null_fence_ops,
224
- &fence->lock, 0, 0);
225
- dma_fence_signal(&fence->base);
226
-
227
- drm_syncobj_replace_fence(syncobj, &fence->base);
228
-
229
- dma_fence_put(&fence->base);
230
-
231
- return 0;
232
-}
233
-
361
+/* 5s default for wait submission */
362
+#define DRM_SYNCOBJ_WAIT_FOR_SUBMIT_TIMEOUT 5000000000ULL
234363 /**
235364 * drm_syncobj_find_fence - lookup and reference the fence in a sync object
236365 * @file_private: drm file private pointer
237366 * @handle: sync object handle to lookup.
367
+ * @point: timeline point
368
+ * @flags: DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT or not
238369 * @fence: out parameter for the fence
239370 *
240371 * This is just a convenience function that combines drm_syncobj_find() and
....@@ -245,20 +376,73 @@
245376 * dma_fence_put().
246377 */
247378 int drm_syncobj_find_fence(struct drm_file *file_private,
248
- u32 handle,
379
+ u32 handle, u64 point, u64 flags,
249380 struct dma_fence **fence)
250381 {
251382 struct drm_syncobj *syncobj = drm_syncobj_find(file_private, handle);
252
- int ret = 0;
383
+ struct syncobj_wait_entry wait;
384
+ u64 timeout = nsecs_to_jiffies64(DRM_SYNCOBJ_WAIT_FOR_SUBMIT_TIMEOUT);
385
+ int ret;
253386
254387 if (!syncobj)
255388 return -ENOENT;
256389
257390 *fence = drm_syncobj_fence_get(syncobj);
258
- if (!*fence) {
391
+
392
+ if (*fence) {
393
+ ret = dma_fence_chain_find_seqno(fence, point);
394
+ if (!ret) {
395
+ /* If the requested seqno is already signaled
396
+ * drm_syncobj_find_fence may return a NULL
397
+ * fence. To make sure the recipient gets
398
+ * signalled, use a new fence instead.
399
+ */
400
+ if (!*fence)
401
+ *fence = dma_fence_get_stub();
402
+
403
+ goto out;
404
+ }
405
+ dma_fence_put(*fence);
406
+ } else {
259407 ret = -EINVAL;
260408 }
409
+
410
+ if (!(flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT))
411
+ goto out;
412
+
413
+ memset(&wait, 0, sizeof(wait));
414
+ wait.task = current;
415
+ wait.point = point;
416
+ drm_syncobj_fence_add_wait(syncobj, &wait);
417
+
418
+ do {
419
+ set_current_state(TASK_INTERRUPTIBLE);
420
+ if (wait.fence) {
421
+ ret = 0;
422
+ break;
423
+ }
424
+ if (timeout == 0) {
425
+ ret = -ETIME;
426
+ break;
427
+ }
428
+
429
+ if (signal_pending(current)) {
430
+ ret = -ERESTARTSYS;
431
+ break;
432
+ }
433
+
434
+ timeout = schedule_timeout(timeout);
435
+ } while (1);
436
+
437
+ __set_current_state(TASK_RUNNING);
438
+ *fence = wait.fence;
439
+
440
+ if (wait.node.next)
441
+ drm_syncobj_remove_wait(syncobj, &wait);
442
+
443
+out:
261444 drm_syncobj_put(syncobj);
445
+
262446 return ret;
263447 }
264448 EXPORT_SYMBOL(drm_syncobj_find_fence);
....@@ -294,7 +478,6 @@
294478 int drm_syncobj_create(struct drm_syncobj **out_syncobj, uint32_t flags,
295479 struct dma_fence *fence)
296480 {
297
- int ret;
298481 struct drm_syncobj *syncobj;
299482
300483 syncobj = kzalloc(sizeof(struct drm_syncobj), GFP_KERNEL);
....@@ -305,13 +488,8 @@
305488 INIT_LIST_HEAD(&syncobj->cb_list);
306489 spin_lock_init(&syncobj->lock);
307490
308
- if (flags & DRM_SYNCOBJ_CREATE_SIGNALED) {
309
- ret = drm_syncobj_assign_null_handle(syncobj);
310
- if (ret < 0) {
311
- drm_syncobj_put(syncobj);
312
- return ret;
313
- }
314
- }
491
+ if (flags & DRM_SYNCOBJ_CREATE_SIGNALED)
492
+ drm_syncobj_assign_null_handle(syncobj);
315493
316494 if (fence)
317495 drm_syncobj_replace_fence(syncobj, fence);
....@@ -452,20 +630,19 @@
452630 int fd, u32 *handle)
453631 {
454632 struct drm_syncobj *syncobj;
455
- struct file *file;
633
+ struct fd f = fdget(fd);
456634 int ret;
457635
458
- file = fget(fd);
459
- if (!file)
636
+ if (!f.file)
460637 return -EINVAL;
461638
462
- if (file->f_op != &drm_syncobj_file_fops) {
463
- fput(file);
639
+ if (f.file->f_op != &drm_syncobj_file_fops) {
640
+ fdput(f);
464641 return -EINVAL;
465642 }
466643
467644 /* take a reference to put in the idr */
468
- syncobj = file->private_data;
645
+ syncobj = f.file->private_data;
469646 drm_syncobj_get(syncobj);
470647
471648 idr_preload(GFP_KERNEL);
....@@ -480,7 +657,7 @@
480657 } else
481658 drm_syncobj_put(syncobj);
482659
483
- fput(file);
660
+ fdput(f);
484661 return ret;
485662 }
486663
....@@ -516,7 +693,7 @@
516693 if (fd < 0)
517694 return fd;
518695
519
- ret = drm_syncobj_find_fence(file_private, handle, &fence);
696
+ ret = drm_syncobj_find_fence(file_private, handle, 0, 0, &fence);
520697 if (ret)
521698 goto err_put_fd;
522699
....@@ -583,7 +760,7 @@
583760 struct drm_syncobj_create *args = data;
584761
585762 if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
586
- return -ENODEV;
763
+ return -EOPNOTSUPP;
587764
588765 /* no valid flags yet */
589766 if (args->flags & ~DRM_SYNCOBJ_CREATE_SIGNALED)
....@@ -600,7 +777,7 @@
600777 struct drm_syncobj_destroy *args = data;
601778
602779 if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
603
- return -ENODEV;
780
+ return -EOPNOTSUPP;
604781
605782 /* make sure padding is empty */
606783 if (args->pad)
....@@ -615,7 +792,7 @@
615792 struct drm_syncobj_handle *args = data;
616793
617794 if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
618
- return -ENODEV;
795
+ return -EOPNOTSUPP;
619796
620797 if (args->pad)
621798 return -EINVAL;
....@@ -639,7 +816,7 @@
639816 struct drm_syncobj_handle *args = data;
640817
641818 if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
642
- return -ENODEV;
819
+ return -EOPNOTSUPP;
643820
644821 if (args->pad)
645822 return -EINVAL;
....@@ -657,12 +834,79 @@
657834 &args->handle);
658835 }
659836
660
-struct syncobj_wait_entry {
661
- struct task_struct *task;
837
+static int drm_syncobj_transfer_to_timeline(struct drm_file *file_private,
838
+ struct drm_syncobj_transfer *args)
839
+{
840
+ struct drm_syncobj *timeline_syncobj = NULL;
662841 struct dma_fence *fence;
663
- struct dma_fence_cb fence_cb;
664
- struct drm_syncobj_cb syncobj_cb;
665
-};
842
+ struct dma_fence_chain *chain;
843
+ int ret;
844
+
845
+ timeline_syncobj = drm_syncobj_find(file_private, args->dst_handle);
846
+ if (!timeline_syncobj) {
847
+ return -ENOENT;
848
+ }
849
+ ret = drm_syncobj_find_fence(file_private, args->src_handle,
850
+ args->src_point, args->flags,
851
+ &fence);
852
+ if (ret)
853
+ goto err;
854
+ chain = kzalloc(sizeof(struct dma_fence_chain), GFP_KERNEL);
855
+ if (!chain) {
856
+ ret = -ENOMEM;
857
+ goto err1;
858
+ }
859
+ drm_syncobj_add_point(timeline_syncobj, chain, fence, args->dst_point);
860
+err1:
861
+ dma_fence_put(fence);
862
+err:
863
+ drm_syncobj_put(timeline_syncobj);
864
+
865
+ return ret;
866
+}
867
+
868
+static int
869
+drm_syncobj_transfer_to_binary(struct drm_file *file_private,
870
+ struct drm_syncobj_transfer *args)
871
+{
872
+ struct drm_syncobj *binary_syncobj = NULL;
873
+ struct dma_fence *fence;
874
+ int ret;
875
+
876
+ binary_syncobj = drm_syncobj_find(file_private, args->dst_handle);
877
+ if (!binary_syncobj)
878
+ return -ENOENT;
879
+ ret = drm_syncobj_find_fence(file_private, args->src_handle,
880
+ args->src_point, args->flags, &fence);
881
+ if (ret)
882
+ goto err;
883
+ drm_syncobj_replace_fence(binary_syncobj, fence);
884
+ dma_fence_put(fence);
885
+err:
886
+ drm_syncobj_put(binary_syncobj);
887
+
888
+ return ret;
889
+}
890
+int
891
+drm_syncobj_transfer_ioctl(struct drm_device *dev, void *data,
892
+ struct drm_file *file_private)
893
+{
894
+ struct drm_syncobj_transfer *args = data;
895
+ int ret;
896
+
897
+ if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
898
+ return -EOPNOTSUPP;
899
+
900
+ if (args->pad)
901
+ return -EINVAL;
902
+
903
+ if (args->dst_point)
904
+ ret = drm_syncobj_transfer_to_timeline(file_private, args);
905
+ else
906
+ ret = drm_syncobj_transfer_to_binary(file_private, args);
907
+
908
+ return ret;
909
+}
666910
667911 static void syncobj_wait_fence_func(struct dma_fence *fence,
668912 struct dma_fence_cb *cb)
....@@ -674,18 +918,29 @@
674918 }
675919
676920 static void syncobj_wait_syncobj_func(struct drm_syncobj *syncobj,
677
- struct drm_syncobj_cb *cb)
921
+ struct syncobj_wait_entry *wait)
678922 {
679
- struct syncobj_wait_entry *wait =
680
- container_of(cb, struct syncobj_wait_entry, syncobj_cb);
923
+ struct dma_fence *fence;
681924
682925 /* This happens inside the syncobj lock */
683
- wait->fence = dma_fence_get(rcu_dereference_protected(syncobj->fence,
684
- lockdep_is_held(&syncobj->lock)));
926
+ fence = rcu_dereference_protected(syncobj->fence,
927
+ lockdep_is_held(&syncobj->lock));
928
+ dma_fence_get(fence);
929
+ if (!fence || dma_fence_chain_find_seqno(&fence, wait->point)) {
930
+ dma_fence_put(fence);
931
+ return;
932
+ } else if (!fence) {
933
+ wait->fence = dma_fence_get_stub();
934
+ } else {
935
+ wait->fence = fence;
936
+ }
937
+
685938 wake_up_process(wait->task);
939
+ list_del_init(&wait->node);
686940 }
687941
688942 static signed long drm_syncobj_array_wait_timeout(struct drm_syncobj **syncobjs,
943
+ void __user *user_points,
689944 uint32_t count,
690945 uint32_t flags,
691946 signed long timeout,
....@@ -693,13 +948,27 @@
693948 {
694949 struct syncobj_wait_entry *entries;
695950 struct dma_fence *fence;
696
- signed long ret;
951
+ uint64_t *points;
697952 uint32_t signaled_count, i;
698953
699
- entries = kcalloc(count, sizeof(*entries), GFP_KERNEL);
700
- if (!entries)
954
+ points = kmalloc_array(count, sizeof(*points), GFP_KERNEL);
955
+ if (points == NULL)
701956 return -ENOMEM;
702957
958
+ if (!user_points) {
959
+ memset(points, 0, count * sizeof(uint64_t));
960
+
961
+ } else if (copy_from_user(points, user_points,
962
+ sizeof(uint64_t) * count)) {
963
+ timeout = -EFAULT;
964
+ goto err_free_points;
965
+ }
966
+
967
+ entries = kcalloc(count, sizeof(*entries), GFP_KERNEL);
968
+ if (!entries) {
969
+ timeout = -ENOMEM;
970
+ goto err_free_points;
971
+ }
703972 /* Walk the list of sync objects and initialize entries. We do
704973 * this up-front so that we can properly return -EINVAL if there is
705974 * a syncobj with a missing fence and then never have the chance of
....@@ -707,29 +976,33 @@
707976 */
708977 signaled_count = 0;
709978 for (i = 0; i < count; ++i) {
979
+ struct dma_fence *fence;
980
+
710981 entries[i].task = current;
711
- entries[i].fence = drm_syncobj_fence_get(syncobjs[i]);
712
- if (!entries[i].fence) {
982
+ entries[i].point = points[i];
983
+ fence = drm_syncobj_fence_get(syncobjs[i]);
984
+ if (!fence || dma_fence_chain_find_seqno(&fence, points[i])) {
985
+ dma_fence_put(fence);
713986 if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT) {
714987 continue;
715988 } else {
716
- ret = -EINVAL;
989
+ timeout = -EINVAL;
717990 goto cleanup_entries;
718991 }
719992 }
720993
721
- if (dma_fence_is_signaled(entries[i].fence)) {
994
+ if (fence)
995
+ entries[i].fence = fence;
996
+ else
997
+ entries[i].fence = dma_fence_get_stub();
998
+
999
+ if ((flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE) ||
1000
+ dma_fence_is_signaled(entries[i].fence)) {
7221001 if (signaled_count == 0 && idx)
7231002 *idx = i;
7241003 signaled_count++;
7251004 }
7261005 }
727
-
728
- /* Initialize ret to the max of timeout and 1. That way, the
729
- * default return value indicates a successful wait and not a
730
- * timeout.
731
- */
732
- ret = max_t(signed long, timeout, 1);
7331006
7341007 if (signaled_count == count ||
7351008 (signaled_count > 0 &&
....@@ -744,15 +1017,8 @@
7441017 */
7451018
7461019 if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT) {
747
- for (i = 0; i < count; ++i) {
748
- if (entries[i].fence)
749
- continue;
750
-
751
- drm_syncobj_fence_get_or_add_callback(syncobjs[i],
752
- &entries[i].fence,
753
- &entries[i].syncobj_cb,
754
- syncobj_wait_syncobj_func);
755
- }
1020
+ for (i = 0; i < count; ++i)
1021
+ drm_syncobj_fence_add_wait(syncobjs[i], &entries[i]);
7561022 }
7571023
7581024 do {
....@@ -764,7 +1030,8 @@
7641030 if (!fence)
7651031 continue;
7661032
767
- if (dma_fence_is_signaled(fence) ||
1033
+ if ((flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE) ||
1034
+ dma_fence_is_signaled(fence) ||
7681035 (!entries[i].fence_cb.func &&
7691036 dma_fence_add_callback(fence,
7701037 &entries[i].fence_cb,
....@@ -784,27 +1051,24 @@
7841051 goto done_waiting;
7851052
7861053 if (timeout == 0) {
787
- /* If we are doing a 0 timeout wait and we got
788
- * here, then we just timed out.
789
- */
790
- ret = 0;
1054
+ timeout = -ETIME;
7911055 goto done_waiting;
7921056 }
7931057
794
- ret = schedule_timeout(ret);
1058
+ if (signal_pending(current)) {
1059
+ timeout = -ERESTARTSYS;
1060
+ goto done_waiting;
1061
+ }
7951062
796
- if (ret > 0 && signal_pending(current))
797
- ret = -ERESTARTSYS;
798
- } while (ret > 0);
1063
+ timeout = schedule_timeout(timeout);
1064
+ } while (1);
7991065
8001066 done_waiting:
8011067 __set_current_state(TASK_RUNNING);
8021068
8031069 cleanup_entries:
8041070 for (i = 0; i < count; ++i) {
805
- if (entries[i].syncobj_cb.func)
806
- drm_syncobj_remove_callback(syncobjs[i],
807
- &entries[i].syncobj_cb);
1071
+ drm_syncobj_remove_wait(syncobjs[i], &entries[i]);
8081072 if (entries[i].fence_cb.func)
8091073 dma_fence_remove_callback(entries[i].fence,
8101074 &entries[i].fence_cb);
....@@ -812,7 +1076,10 @@
8121076 }
8131077 kfree(entries);
8141078
815
- return ret;
1079
+err_free_points:
1080
+ kfree(points);
1081
+
1082
+ return timeout;
8161083 }
8171084
8181085 /**
....@@ -822,7 +1089,7 @@
8221089 *
8231090 * Calculate the timeout in jiffies from an absolute time in sec/nsec.
8241091 */
825
-static signed long drm_timeout_abs_to_jiffies(int64_t timeout_nsec)
1092
+signed long drm_timeout_abs_to_jiffies(int64_t timeout_nsec)
8261093 {
8271094 ktime_t abs_timeout, now;
8281095 u64 timeout_ns, timeout_jiffies64;
....@@ -846,26 +1113,38 @@
8461113
8471114 return timeout_jiffies64 + 1;
8481115 }
1116
+EXPORT_SYMBOL(drm_timeout_abs_to_jiffies);
8491117
8501118 static int drm_syncobj_array_wait(struct drm_device *dev,
8511119 struct drm_file *file_private,
8521120 struct drm_syncobj_wait *wait,
853
- struct drm_syncobj **syncobjs)
1121
+ struct drm_syncobj_timeline_wait *timeline_wait,
1122
+ struct drm_syncobj **syncobjs, bool timeline)
8541123 {
855
- signed long timeout = drm_timeout_abs_to_jiffies(wait->timeout_nsec);
856
- signed long ret = 0;
1124
+ signed long timeout = 0;
8571125 uint32_t first = ~0;
8581126
859
- ret = drm_syncobj_array_wait_timeout(syncobjs,
860
- wait->count_handles,
861
- wait->flags,
862
- timeout, &first);
863
- if (ret < 0)
864
- return ret;
865
-
866
- wait->first_signaled = first;
867
- if (ret == 0)
868
- return -ETIME;
1127
+ if (!timeline) {
1128
+ timeout = drm_timeout_abs_to_jiffies(wait->timeout_nsec);
1129
+ timeout = drm_syncobj_array_wait_timeout(syncobjs,
1130
+ NULL,
1131
+ wait->count_handles,
1132
+ wait->flags,
1133
+ timeout, &first);
1134
+ if (timeout < 0)
1135
+ return timeout;
1136
+ wait->first_signaled = first;
1137
+ } else {
1138
+ timeout = drm_timeout_abs_to_jiffies(timeline_wait->timeout_nsec);
1139
+ timeout = drm_syncobj_array_wait_timeout(syncobjs,
1140
+ u64_to_user_ptr(timeline_wait->points),
1141
+ timeline_wait->count_handles,
1142
+ timeline_wait->flags,
1143
+ timeout, &first);
1144
+ if (timeout < 0)
1145
+ return timeout;
1146
+ timeline_wait->first_signaled = first;
1147
+ }
8691148 return 0;
8701149 }
8711150
....@@ -920,6 +1199,7 @@
9201199 uint32_t count)
9211200 {
9221201 uint32_t i;
1202
+
9231203 for (i = 0; i < count; i++)
9241204 drm_syncobj_put(syncobjs[i]);
9251205 kfree(syncobjs);
....@@ -934,7 +1214,7 @@
9341214 int ret = 0;
9351215
9361216 if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
937
- return -ENODEV;
1217
+ return -EOPNOTSUPP;
9381218
9391219 if (args->flags & ~(DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL |
9401220 DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT))
....@@ -951,12 +1231,47 @@
9511231 return ret;
9521232
9531233 ret = drm_syncobj_array_wait(dev, file_private,
954
- args, syncobjs);
1234
+ args, NULL, syncobjs, false);
9551235
9561236 drm_syncobj_array_free(syncobjs, args->count_handles);
9571237
9581238 return ret;
9591239 }
1240
+
1241
+int
1242
+drm_syncobj_timeline_wait_ioctl(struct drm_device *dev, void *data,
1243
+ struct drm_file *file_private)
1244
+{
1245
+ struct drm_syncobj_timeline_wait *args = data;
1246
+ struct drm_syncobj **syncobjs;
1247
+ int ret = 0;
1248
+
1249
+ if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
1250
+ return -EOPNOTSUPP;
1251
+
1252
+ if (args->flags & ~(DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL |
1253
+ DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT |
1254
+ DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE))
1255
+ return -EINVAL;
1256
+
1257
+ if (args->count_handles == 0)
1258
+ return -EINVAL;
1259
+
1260
+ ret = drm_syncobj_array_find(file_private,
1261
+ u64_to_user_ptr(args->handles),
1262
+ args->count_handles,
1263
+ &syncobjs);
1264
+ if (ret < 0)
1265
+ return ret;
1266
+
1267
+ ret = drm_syncobj_array_wait(dev, file_private,
1268
+ NULL, args, syncobjs, true);
1269
+
1270
+ drm_syncobj_array_free(syncobjs, args->count_handles);
1271
+
1272
+ return ret;
1273
+}
1274
+
9601275
9611276 int
9621277 drm_syncobj_reset_ioctl(struct drm_device *dev, void *data,
....@@ -968,7 +1283,7 @@
9681283 int ret;
9691284
9701285 if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
971
- return -ENODEV;
1286
+ return -EOPNOTSUPP;
9721287
9731288 if (args->pad != 0)
9741289 return -EINVAL;
....@@ -1001,7 +1316,7 @@
10011316 int ret;
10021317
10031318 if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
1004
- return -ENODEV;
1319
+ return -EOPNOTSUPP;
10051320
10061321 if (args->pad != 0)
10071322 return -EINVAL;
....@@ -1016,12 +1331,151 @@
10161331 if (ret < 0)
10171332 return ret;
10181333
1019
- for (i = 0; i < args->count_handles; i++) {
1020
- ret = drm_syncobj_assign_null_handle(syncobjs[i]);
1021
- if (ret < 0)
1022
- break;
1334
+ for (i = 0; i < args->count_handles; i++)
1335
+ drm_syncobj_assign_null_handle(syncobjs[i]);
1336
+
1337
+ drm_syncobj_array_free(syncobjs, args->count_handles);
1338
+
1339
+ return ret;
1340
+}
1341
+
1342
+int
1343
+drm_syncobj_timeline_signal_ioctl(struct drm_device *dev, void *data,
1344
+ struct drm_file *file_private)
1345
+{
1346
+ struct drm_syncobj_timeline_array *args = data;
1347
+ struct drm_syncobj **syncobjs;
1348
+ struct dma_fence_chain **chains;
1349
+ uint64_t *points;
1350
+ uint32_t i, j;
1351
+ int ret;
1352
+
1353
+ if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
1354
+ return -EOPNOTSUPP;
1355
+
1356
+ if (args->flags != 0)
1357
+ return -EINVAL;
1358
+
1359
+ if (args->count_handles == 0)
1360
+ return -EINVAL;
1361
+
1362
+ ret = drm_syncobj_array_find(file_private,
1363
+ u64_to_user_ptr(args->handles),
1364
+ args->count_handles,
1365
+ &syncobjs);
1366
+ if (ret < 0)
1367
+ return ret;
1368
+
1369
+ points = kmalloc_array(args->count_handles, sizeof(*points),
1370
+ GFP_KERNEL);
1371
+ if (!points) {
1372
+ ret = -ENOMEM;
1373
+ goto out;
1374
+ }
1375
+ if (!u64_to_user_ptr(args->points)) {
1376
+ memset(points, 0, args->count_handles * sizeof(uint64_t));
1377
+ } else if (copy_from_user(points, u64_to_user_ptr(args->points),
1378
+ sizeof(uint64_t) * args->count_handles)) {
1379
+ ret = -EFAULT;
1380
+ goto err_points;
10231381 }
10241382
1383
+ chains = kmalloc_array(args->count_handles, sizeof(void *), GFP_KERNEL);
1384
+ if (!chains) {
1385
+ ret = -ENOMEM;
1386
+ goto err_points;
1387
+ }
1388
+ for (i = 0; i < args->count_handles; i++) {
1389
+ chains[i] = kzalloc(sizeof(struct dma_fence_chain), GFP_KERNEL);
1390
+ if (!chains[i]) {
1391
+ for (j = 0; j < i; j++)
1392
+ kfree(chains[j]);
1393
+ ret = -ENOMEM;
1394
+ goto err_chains;
1395
+ }
1396
+ }
1397
+
1398
+ for (i = 0; i < args->count_handles; i++) {
1399
+ struct dma_fence *fence = dma_fence_get_stub();
1400
+
1401
+ drm_syncobj_add_point(syncobjs[i], chains[i],
1402
+ fence, points[i]);
1403
+ dma_fence_put(fence);
1404
+ }
1405
+err_chains:
1406
+ kfree(chains);
1407
+err_points:
1408
+ kfree(points);
1409
+out:
1410
+ drm_syncobj_array_free(syncobjs, args->count_handles);
1411
+
1412
+ return ret;
1413
+}
1414
+
1415
+int drm_syncobj_query_ioctl(struct drm_device *dev, void *data,
1416
+ struct drm_file *file_private)
1417
+{
1418
+ struct drm_syncobj_timeline_array *args = data;
1419
+ struct drm_syncobj **syncobjs;
1420
+ uint64_t __user *points = u64_to_user_ptr(args->points);
1421
+ uint32_t i;
1422
+ int ret;
1423
+
1424
+ if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
1425
+ return -EOPNOTSUPP;
1426
+
1427
+ if (args->flags & ~DRM_SYNCOBJ_QUERY_FLAGS_LAST_SUBMITTED)
1428
+ return -EINVAL;
1429
+
1430
+ if (args->count_handles == 0)
1431
+ return -EINVAL;
1432
+
1433
+ ret = drm_syncobj_array_find(file_private,
1434
+ u64_to_user_ptr(args->handles),
1435
+ args->count_handles,
1436
+ &syncobjs);
1437
+ if (ret < 0)
1438
+ return ret;
1439
+
1440
+ for (i = 0; i < args->count_handles; i++) {
1441
+ struct dma_fence_chain *chain;
1442
+ struct dma_fence *fence;
1443
+ uint64_t point;
1444
+
1445
+ fence = drm_syncobj_fence_get(syncobjs[i]);
1446
+ chain = to_dma_fence_chain(fence);
1447
+ if (chain) {
1448
+ struct dma_fence *iter, *last_signaled =
1449
+ dma_fence_get(fence);
1450
+
1451
+ if (args->flags &
1452
+ DRM_SYNCOBJ_QUERY_FLAGS_LAST_SUBMITTED) {
1453
+ point = fence->seqno;
1454
+ } else {
1455
+ dma_fence_chain_for_each(iter, fence) {
1456
+ if (iter->context != fence->context) {
1457
+ dma_fence_put(iter);
1458
+ /* It is most likely that timeline has
1459
+ * unorder points. */
1460
+ break;
1461
+ }
1462
+ dma_fence_put(last_signaled);
1463
+ last_signaled = dma_fence_get(iter);
1464
+ }
1465
+ point = dma_fence_is_signaled(last_signaled) ?
1466
+ last_signaled->seqno :
1467
+ to_dma_fence_chain(last_signaled)->prev_seqno;
1468
+ }
1469
+ dma_fence_put(last_signaled);
1470
+ } else {
1471
+ point = 0;
1472
+ }
1473
+ dma_fence_put(fence);
1474
+ ret = copy_to_user(&points[i], &point, sizeof(uint64_t));
1475
+ ret = ret ? -EFAULT : 0;
1476
+ if (ret)
1477
+ break;
1478
+ }
10251479 drm_syncobj_array_free(syncobjs, args->count_handles);
10261480
10271481 return ret;