1 // SPDX-License-Identifier: MIT
2 /*
3 * Copyright © 2021 Intel Corporation
4 */
5
6 #include "xe_sync.h"
7
8 #include <linux/dma-fence-array.h>
9 #include <linux/kthread.h>
10 #include <linux/sched/mm.h>
11 #include <linux/uaccess.h>
12
13 #include <drm/drm_print.h>
14 #include <drm/drm_syncobj.h>
15 #include <uapi/drm/xe_drm.h>
16
17 #include "xe_device_types.h"
18 #include "xe_exec_queue.h"
19 #include "xe_macros.h"
20 #include "xe_sched_job_types.h"
21
22 struct xe_user_fence {
23 struct xe_device *xe;
24 struct kref refcount;
25 struct dma_fence_cb cb;
26 struct work_struct worker;
27 struct mm_struct *mm;
28 u64 __user *addr;
29 u64 value;
30 int signalled;
31 };
32
user_fence_destroy(struct kref * kref)33 static void user_fence_destroy(struct kref *kref)
34 {
35 struct xe_user_fence *ufence = container_of(kref, struct xe_user_fence,
36 refcount);
37
38 mmdrop(ufence->mm);
39 kfree(ufence);
40 }
41
user_fence_get(struct xe_user_fence * ufence)42 static void user_fence_get(struct xe_user_fence *ufence)
43 {
44 kref_get(&ufence->refcount);
45 }
46
user_fence_put(struct xe_user_fence * ufence)47 static void user_fence_put(struct xe_user_fence *ufence)
48 {
49 kref_put(&ufence->refcount, user_fence_destroy);
50 }
51
user_fence_create(struct xe_device * xe,u64 addr,u64 value)52 static struct xe_user_fence *user_fence_create(struct xe_device *xe, u64 addr,
53 u64 value)
54 {
55 struct xe_user_fence *ufence;
56 u64 __user *ptr = u64_to_user_ptr(addr);
57 u64 __maybe_unused prefetch_val;
58
59 if (get_user(prefetch_val, ptr))
60 return ERR_PTR(-EFAULT);
61
62 ufence = kzalloc(sizeof(*ufence), GFP_KERNEL);
63 if (!ufence)
64 return ERR_PTR(-ENOMEM);
65
66 ufence->xe = xe;
67 kref_init(&ufence->refcount);
68 ufence->addr = ptr;
69 ufence->value = value;
70 ufence->mm = current->mm;
71 mmgrab(ufence->mm);
72
73 return ufence;
74 }
75
user_fence_worker(struct work_struct * w)76 static void user_fence_worker(struct work_struct *w)
77 {
78 struct xe_user_fence *ufence = container_of(w, struct xe_user_fence, worker);
79
80 WRITE_ONCE(ufence->signalled, 1);
81 if (mmget_not_zero(ufence->mm)) {
82 kthread_use_mm(ufence->mm);
83 if (copy_to_user(ufence->addr, &ufence->value, sizeof(ufence->value)))
84 XE_WARN_ON("Copy to user failed");
85 kthread_unuse_mm(ufence->mm);
86 mmput(ufence->mm);
87 }
88
89 /*
90 * Wake up waiters only after updating the ufence state, allowing the UMD
91 * to safely reuse the same ufence without encountering -EBUSY errors.
92 */
93 wake_up_all(&ufence->xe->ufence_wq);
94 user_fence_put(ufence);
95 }
96
kick_ufence(struct xe_user_fence * ufence,struct dma_fence * fence)97 static void kick_ufence(struct xe_user_fence *ufence, struct dma_fence *fence)
98 {
99 INIT_WORK(&ufence->worker, user_fence_worker);
100 queue_work(ufence->xe->ordered_wq, &ufence->worker);
101 dma_fence_put(fence);
102 }
103
user_fence_cb(struct dma_fence * fence,struct dma_fence_cb * cb)104 static void user_fence_cb(struct dma_fence *fence, struct dma_fence_cb *cb)
105 {
106 struct xe_user_fence *ufence = container_of(cb, struct xe_user_fence, cb);
107
108 kick_ufence(ufence, fence);
109 }
110
xe_sync_entry_parse(struct xe_device * xe,struct xe_file * xef,struct xe_sync_entry * sync,struct drm_xe_sync __user * sync_user,unsigned int flags)111 int xe_sync_entry_parse(struct xe_device *xe, struct xe_file *xef,
112 struct xe_sync_entry *sync,
113 struct drm_xe_sync __user *sync_user,
114 unsigned int flags)
115 {
116 struct drm_xe_sync sync_in;
117 int err;
118 bool exec = flags & SYNC_PARSE_FLAG_EXEC;
119 bool in_lr_mode = flags & SYNC_PARSE_FLAG_LR_MODE;
120 bool disallow_user_fence = flags & SYNC_PARSE_FLAG_DISALLOW_USER_FENCE;
121 bool signal;
122
123 if (copy_from_user(&sync_in, sync_user, sizeof(*sync_user)))
124 return -EFAULT;
125
126 if (XE_IOCTL_DBG(xe, sync_in.flags & ~DRM_XE_SYNC_FLAG_SIGNAL) ||
127 XE_IOCTL_DBG(xe, sync_in.reserved[0] || sync_in.reserved[1]))
128 return -EINVAL;
129
130 signal = sync_in.flags & DRM_XE_SYNC_FLAG_SIGNAL;
131 switch (sync_in.type) {
132 case DRM_XE_SYNC_TYPE_SYNCOBJ:
133 if (XE_IOCTL_DBG(xe, in_lr_mode && signal))
134 return -EOPNOTSUPP;
135
136 if (XE_IOCTL_DBG(xe, upper_32_bits(sync_in.addr)))
137 return -EINVAL;
138
139 sync->syncobj = drm_syncobj_find(xef->drm, sync_in.handle);
140 if (XE_IOCTL_DBG(xe, !sync->syncobj))
141 return -ENOENT;
142
143 if (!signal) {
144 sync->fence = drm_syncobj_fence_get(sync->syncobj);
145 if (XE_IOCTL_DBG(xe, !sync->fence))
146 return -EINVAL;
147 }
148 break;
149
150 case DRM_XE_SYNC_TYPE_TIMELINE_SYNCOBJ:
151 if (XE_IOCTL_DBG(xe, in_lr_mode && signal))
152 return -EOPNOTSUPP;
153
154 if (XE_IOCTL_DBG(xe, upper_32_bits(sync_in.addr)))
155 return -EINVAL;
156
157 if (XE_IOCTL_DBG(xe, sync_in.timeline_value == 0))
158 return -EINVAL;
159
160 sync->syncobj = drm_syncobj_find(xef->drm, sync_in.handle);
161 if (XE_IOCTL_DBG(xe, !sync->syncobj))
162 return -ENOENT;
163
164 if (signal) {
165 sync->chain_fence = dma_fence_chain_alloc();
166 if (!sync->chain_fence)
167 return -ENOMEM;
168 } else {
169 sync->fence = drm_syncobj_fence_get(sync->syncobj);
170 if (XE_IOCTL_DBG(xe, !sync->fence))
171 return -EINVAL;
172
173 err = dma_fence_chain_find_seqno(&sync->fence,
174 sync_in.timeline_value);
175 if (err)
176 return err;
177 }
178 break;
179
180 case DRM_XE_SYNC_TYPE_USER_FENCE:
181 if (XE_IOCTL_DBG(xe, disallow_user_fence))
182 return -EOPNOTSUPP;
183
184 if (XE_IOCTL_DBG(xe, !signal))
185 return -EOPNOTSUPP;
186
187 if (XE_IOCTL_DBG(xe, sync_in.addr & 0x7))
188 return -EINVAL;
189
190 if (exec) {
191 sync->addr = sync_in.addr;
192 } else {
193 sync->ufence = user_fence_create(xe, sync_in.addr,
194 sync_in.timeline_value);
195 if (XE_IOCTL_DBG(xe, IS_ERR(sync->ufence)))
196 return PTR_ERR(sync->ufence);
197 }
198
199 break;
200
201 default:
202 return -EINVAL;
203 }
204
205 sync->type = sync_in.type;
206 sync->flags = sync_in.flags;
207 sync->timeline_value = sync_in.timeline_value;
208
209 return 0;
210 }
211
xe_sync_entry_add_deps(struct xe_sync_entry * sync,struct xe_sched_job * job)212 int xe_sync_entry_add_deps(struct xe_sync_entry *sync, struct xe_sched_job *job)
213 {
214 if (sync->fence)
215 return drm_sched_job_add_dependency(&job->drm,
216 dma_fence_get(sync->fence));
217
218 return 0;
219 }
220
xe_sync_entry_signal(struct xe_sync_entry * sync,struct dma_fence * fence)221 void xe_sync_entry_signal(struct xe_sync_entry *sync, struct dma_fence *fence)
222 {
223 if (!(sync->flags & DRM_XE_SYNC_FLAG_SIGNAL))
224 return;
225
226 if (sync->chain_fence) {
227 drm_syncobj_add_point(sync->syncobj, sync->chain_fence,
228 fence, sync->timeline_value);
229 /*
230 * The chain's ownership is transferred to the
231 * timeline.
232 */
233 sync->chain_fence = NULL;
234 } else if (sync->syncobj) {
235 drm_syncobj_replace_fence(sync->syncobj, fence);
236 } else if (sync->ufence) {
237 int err;
238
239 dma_fence_get(fence);
240 user_fence_get(sync->ufence);
241 err = dma_fence_add_callback(fence, &sync->ufence->cb,
242 user_fence_cb);
243 if (err == -ENOENT) {
244 kick_ufence(sync->ufence, fence);
245 } else if (err) {
246 XE_WARN_ON("failed to add user fence");
247 user_fence_put(sync->ufence);
248 dma_fence_put(fence);
249 }
250 }
251 }
252
xe_sync_entry_cleanup(struct xe_sync_entry * sync)253 void xe_sync_entry_cleanup(struct xe_sync_entry *sync)
254 {
255 if (sync->syncobj)
256 drm_syncobj_put(sync->syncobj);
257 dma_fence_put(sync->fence);
258 dma_fence_chain_free(sync->chain_fence);
259 if (sync->ufence)
260 user_fence_put(sync->ufence);
261 }
262
263 /**
264 * xe_sync_in_fence_get() - Get a fence from syncs, exec queue, and VM
265 * @sync: input syncs
266 * @num_sync: number of syncs
267 * @q: exec queue
268 * @vm: VM
269 *
270 * Get a fence from syncs, exec queue, and VM. If syncs contain in-fences create
271 * and return a composite fence of all in-fences + last fence. If no in-fences
272 * return last fence on input exec queue. Caller must drop reference to
273 * returned fence.
274 *
275 * Return: fence on success, ERR_PTR(-ENOMEM) on failure
276 */
277 struct dma_fence *
xe_sync_in_fence_get(struct xe_sync_entry * sync,int num_sync,struct xe_exec_queue * q,struct xe_vm * vm)278 xe_sync_in_fence_get(struct xe_sync_entry *sync, int num_sync,
279 struct xe_exec_queue *q, struct xe_vm *vm)
280 {
281 struct dma_fence **fences = NULL;
282 struct dma_fence_array *cf = NULL;
283 struct dma_fence *fence;
284 int i, num_in_fence = 0, current_fence = 0;
285
286 lockdep_assert_held(&vm->lock);
287
288 /* Count in-fences */
289 for (i = 0; i < num_sync; ++i) {
290 if (sync[i].fence) {
291 ++num_in_fence;
292 fence = sync[i].fence;
293 }
294 }
295
296 /* Easy case... */
297 if (!num_in_fence) {
298 fence = xe_exec_queue_last_fence_get(q, vm);
299 return fence;
300 }
301
302 /* Create composite fence */
303 fences = kmalloc_array(num_in_fence + 1, sizeof(*fences), GFP_KERNEL);
304 if (!fences)
305 return ERR_PTR(-ENOMEM);
306 for (i = 0; i < num_sync; ++i) {
307 if (sync[i].fence) {
308 dma_fence_get(sync[i].fence);
309 fences[current_fence++] = sync[i].fence;
310 }
311 }
312 fences[current_fence++] = xe_exec_queue_last_fence_get(q, vm);
313 cf = dma_fence_array_create(num_in_fence, fences,
314 vm->composite_fence_ctx,
315 vm->composite_fence_seqno++,
316 false);
317 if (!cf) {
318 --vm->composite_fence_seqno;
319 goto err_out;
320 }
321
322 return &cf->base;
323
324 err_out:
325 while (current_fence)
326 dma_fence_put(fences[--current_fence]);
327 kfree(fences);
328 kfree(cf);
329
330 return ERR_PTR(-ENOMEM);
331 }
332
333 /**
334 * __xe_sync_ufence_get() - Get user fence from user fence
335 * @ufence: input user fence
336 *
337 * Get a user fence reference from user fence
338 *
339 * Return: xe_user_fence pointer with reference
340 */
__xe_sync_ufence_get(struct xe_user_fence * ufence)341 struct xe_user_fence *__xe_sync_ufence_get(struct xe_user_fence *ufence)
342 {
343 user_fence_get(ufence);
344
345 return ufence;
346 }
347
348 /**
349 * xe_sync_ufence_get() - Get user fence from sync
350 * @sync: input sync
351 *
352 * Get a user fence reference from sync.
353 *
354 * Return: xe_user_fence pointer with reference
355 */
xe_sync_ufence_get(struct xe_sync_entry * sync)356 struct xe_user_fence *xe_sync_ufence_get(struct xe_sync_entry *sync)
357 {
358 user_fence_get(sync->ufence);
359
360 return sync->ufence;
361 }
362
363 /**
364 * xe_sync_ufence_put() - Put user fence reference
365 * @ufence: user fence reference
366 *
367 */
xe_sync_ufence_put(struct xe_user_fence * ufence)368 void xe_sync_ufence_put(struct xe_user_fence *ufence)
369 {
370 user_fence_put(ufence);
371 }
372
373 /**
374 * xe_sync_ufence_get_status() - Get user fence status
375 * @ufence: user fence
376 *
377 * Return: 1 if signalled, 0 not signalled, <0 on error
378 */
xe_sync_ufence_get_status(struct xe_user_fence * ufence)379 int xe_sync_ufence_get_status(struct xe_user_fence *ufence)
380 {
381 return READ_ONCE(ufence->signalled);
382 }
383