xref: /linux/drivers/gpu/drm/xe/xe_sync.c (revision e7d759f31ca295d589f7420719c311870bb3166f)
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 <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 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 };
31 
32 static void user_fence_destroy(struct kref *kref)
33 {
34 	struct user_fence *ufence = container_of(kref, struct user_fence,
35 						 refcount);
36 
37 	mmdrop(ufence->mm);
38 	kfree(ufence);
39 }
40 
41 static void user_fence_get(struct user_fence *ufence)
42 {
43 	kref_get(&ufence->refcount);
44 }
45 
46 static void user_fence_put(struct user_fence *ufence)
47 {
48 	kref_put(&ufence->refcount, user_fence_destroy);
49 }
50 
51 static struct user_fence *user_fence_create(struct xe_device *xe, u64 addr,
52 					    u64 value)
53 {
54 	struct user_fence *ufence;
55 
56 	ufence = kmalloc(sizeof(*ufence), GFP_KERNEL);
57 	if (!ufence)
58 		return NULL;
59 
60 	ufence->xe = xe;
61 	kref_init(&ufence->refcount);
62 	ufence->addr = u64_to_user_ptr(addr);
63 	ufence->value = value;
64 	ufence->mm = current->mm;
65 	mmgrab(ufence->mm);
66 
67 	return ufence;
68 }
69 
70 static void user_fence_worker(struct work_struct *w)
71 {
72 	struct user_fence *ufence = container_of(w, struct user_fence, worker);
73 
74 	if (mmget_not_zero(ufence->mm)) {
75 		kthread_use_mm(ufence->mm);
76 		if (copy_to_user(ufence->addr, &ufence->value, sizeof(ufence->value)))
77 			XE_WARN_ON("Copy to user failed");
78 		kthread_unuse_mm(ufence->mm);
79 		mmput(ufence->mm);
80 	}
81 
82 	wake_up_all(&ufence->xe->ufence_wq);
83 	user_fence_put(ufence);
84 }
85 
86 static void kick_ufence(struct user_fence *ufence, struct dma_fence *fence)
87 {
88 	INIT_WORK(&ufence->worker, user_fence_worker);
89 	queue_work(ufence->xe->ordered_wq, &ufence->worker);
90 	dma_fence_put(fence);
91 }
92 
93 static void user_fence_cb(struct dma_fence *fence, struct dma_fence_cb *cb)
94 {
95 	struct user_fence *ufence = container_of(cb, struct user_fence, cb);
96 
97 	kick_ufence(ufence, fence);
98 }
99 
100 int xe_sync_entry_parse(struct xe_device *xe, struct xe_file *xef,
101 			struct xe_sync_entry *sync,
102 			struct drm_xe_sync __user *sync_user,
103 			unsigned int flags)
104 {
105 	struct drm_xe_sync sync_in;
106 	int err;
107 	bool exec = flags & SYNC_PARSE_FLAG_EXEC;
108 	bool in_lr_mode = flags & SYNC_PARSE_FLAG_LR_MODE;
109 	bool disallow_user_fence = flags & SYNC_PARSE_FLAG_DISALLOW_USER_FENCE;
110 	bool signal;
111 
112 	if (copy_from_user(&sync_in, sync_user, sizeof(*sync_user)))
113 		return -EFAULT;
114 
115 	if (XE_IOCTL_DBG(xe, sync_in.flags & ~DRM_XE_SYNC_FLAG_SIGNAL) ||
116 	    XE_IOCTL_DBG(xe, sync_in.reserved[0] || sync_in.reserved[1]))
117 		return -EINVAL;
118 
119 	signal = sync_in.flags & DRM_XE_SYNC_FLAG_SIGNAL;
120 	switch (sync_in.type) {
121 	case DRM_XE_SYNC_TYPE_SYNCOBJ:
122 		if (XE_IOCTL_DBG(xe, in_lr_mode && signal))
123 			return -EOPNOTSUPP;
124 
125 		if (XE_IOCTL_DBG(xe, upper_32_bits(sync_in.addr)))
126 			return -EINVAL;
127 
128 		sync->syncobj = drm_syncobj_find(xef->drm, sync_in.handle);
129 		if (XE_IOCTL_DBG(xe, !sync->syncobj))
130 			return -ENOENT;
131 
132 		if (!signal) {
133 			sync->fence = drm_syncobj_fence_get(sync->syncobj);
134 			if (XE_IOCTL_DBG(xe, !sync->fence))
135 				return -EINVAL;
136 		}
137 		break;
138 
139 	case DRM_XE_SYNC_TYPE_TIMELINE_SYNCOBJ:
140 		if (XE_IOCTL_DBG(xe, in_lr_mode && signal))
141 			return -EOPNOTSUPP;
142 
143 		if (XE_IOCTL_DBG(xe, upper_32_bits(sync_in.addr)))
144 			return -EINVAL;
145 
146 		if (XE_IOCTL_DBG(xe, sync_in.timeline_value == 0))
147 			return -EINVAL;
148 
149 		sync->syncobj = drm_syncobj_find(xef->drm, sync_in.handle);
150 		if (XE_IOCTL_DBG(xe, !sync->syncobj))
151 			return -ENOENT;
152 
153 		if (signal) {
154 			sync->chain_fence = dma_fence_chain_alloc();
155 			if (!sync->chain_fence)
156 				return -ENOMEM;
157 		} else {
158 			sync->fence = drm_syncobj_fence_get(sync->syncobj);
159 			if (XE_IOCTL_DBG(xe, !sync->fence))
160 				return -EINVAL;
161 
162 			err = dma_fence_chain_find_seqno(&sync->fence,
163 							 sync_in.timeline_value);
164 			if (err)
165 				return err;
166 		}
167 		break;
168 
169 	case DRM_XE_SYNC_TYPE_USER_FENCE:
170 		if (XE_IOCTL_DBG(xe, disallow_user_fence))
171 			return -EOPNOTSUPP;
172 
173 		if (XE_IOCTL_DBG(xe, !signal))
174 			return -EOPNOTSUPP;
175 
176 		if (XE_IOCTL_DBG(xe, sync_in.addr & 0x7))
177 			return -EINVAL;
178 
179 		if (exec) {
180 			sync->addr = sync_in.addr;
181 		} else {
182 			sync->ufence = user_fence_create(xe, sync_in.addr,
183 							 sync_in.timeline_value);
184 			if (XE_IOCTL_DBG(xe, !sync->ufence))
185 				return -ENOMEM;
186 		}
187 
188 		break;
189 
190 	default:
191 		return -EINVAL;
192 	}
193 
194 	sync->type = sync_in.type;
195 	sync->flags = sync_in.flags;
196 	sync->timeline_value = sync_in.timeline_value;
197 
198 	return 0;
199 }
200 
201 int xe_sync_entry_wait(struct xe_sync_entry *sync)
202 {
203 	if (sync->fence)
204 		dma_fence_wait(sync->fence, true);
205 
206 	return 0;
207 }
208 
209 int xe_sync_entry_add_deps(struct xe_sync_entry *sync, struct xe_sched_job *job)
210 {
211 	int err;
212 
213 	if (sync->fence) {
214 		err = drm_sched_job_add_dependency(&job->drm,
215 						   dma_fence_get(sync->fence));
216 		if (err) {
217 			dma_fence_put(sync->fence);
218 			return err;
219 		}
220 	}
221 
222 	return 0;
223 }
224 
225 void xe_sync_entry_signal(struct xe_sync_entry *sync, struct xe_sched_job *job,
226 			  struct dma_fence *fence)
227 {
228 	if (!(sync->flags & DRM_XE_SYNC_FLAG_SIGNAL))
229 		return;
230 
231 	if (sync->chain_fence) {
232 		drm_syncobj_add_point(sync->syncobj, sync->chain_fence,
233 				      fence, sync->timeline_value);
234 		/*
235 		 * The chain's ownership is transferred to the
236 		 * timeline.
237 		 */
238 		sync->chain_fence = NULL;
239 	} else if (sync->syncobj) {
240 		drm_syncobj_replace_fence(sync->syncobj, fence);
241 	} else if (sync->ufence) {
242 		int err;
243 
244 		dma_fence_get(fence);
245 		user_fence_get(sync->ufence);
246 		err = dma_fence_add_callback(fence, &sync->ufence->cb,
247 					     user_fence_cb);
248 		if (err == -ENOENT) {
249 			kick_ufence(sync->ufence, fence);
250 		} else if (err) {
251 			XE_WARN_ON("failed to add user fence");
252 			user_fence_put(sync->ufence);
253 			dma_fence_put(fence);
254 		}
255 	} else if (sync->type == DRM_XE_SYNC_TYPE_USER_FENCE) {
256 		job->user_fence.used = true;
257 		job->user_fence.addr = sync->addr;
258 		job->user_fence.value = sync->timeline_value;
259 	}
260 }
261 
262 void xe_sync_entry_cleanup(struct xe_sync_entry *sync)
263 {
264 	if (sync->syncobj)
265 		drm_syncobj_put(sync->syncobj);
266 	if (sync->fence)
267 		dma_fence_put(sync->fence);
268 	if (sync->chain_fence)
269 		dma_fence_put(&sync->chain_fence->base);
270 	if (sync->ufence)
271 		user_fence_put(sync->ufence);
272 }
273 
274 /**
275  * xe_sync_in_fence_get() - Get a fence from syncs, exec queue, and VM
276  * @sync: input syncs
277  * @num_sync: number of syncs
278  * @q: exec queue
279  * @vm: VM
280  *
281  * Get a fence from syncs, exec queue, and VM. If syncs contain in-fences create
282  * and return a composite fence of all in-fences + last fence. If no in-fences
283  * return last fence on  input exec queue. Caller must drop reference to
284  * returned fence.
285  *
286  * Return: fence on success, ERR_PTR(-ENOMEM) on failure
287  */
288 struct dma_fence *
289 xe_sync_in_fence_get(struct xe_sync_entry *sync, int num_sync,
290 		     struct xe_exec_queue *q, struct xe_vm *vm)
291 {
292 	struct dma_fence **fences = NULL;
293 	struct dma_fence_array *cf = NULL;
294 	struct dma_fence *fence;
295 	int i, num_in_fence = 0, current_fence = 0;
296 
297 	lockdep_assert_held(&vm->lock);
298 
299 	/* Count in-fences */
300 	for (i = 0; i < num_sync; ++i) {
301 		if (sync[i].fence) {
302 			++num_in_fence;
303 			fence = sync[i].fence;
304 		}
305 	}
306 
307 	/* Easy case... */
308 	if (!num_in_fence) {
309 		fence = xe_exec_queue_last_fence_get(q, vm);
310 		dma_fence_get(fence);
311 		return fence;
312 	}
313 
314 	/* Create composite fence */
315 	fences = kmalloc_array(num_in_fence + 1, sizeof(*fences), GFP_KERNEL);
316 	if (!fences)
317 		return ERR_PTR(-ENOMEM);
318 	for (i = 0; i < num_sync; ++i) {
319 		if (sync[i].fence) {
320 			dma_fence_get(sync[i].fence);
321 			fences[current_fence++] = sync[i].fence;
322 		}
323 	}
324 	fences[current_fence++] = xe_exec_queue_last_fence_get(q, vm);
325 	dma_fence_get(fences[current_fence - 1]);
326 	cf = dma_fence_array_create(num_in_fence, fences,
327 				    vm->composite_fence_ctx,
328 				    vm->composite_fence_seqno++,
329 				    false);
330 	if (!cf) {
331 		--vm->composite_fence_seqno;
332 		goto err_out;
333 	}
334 
335 	return &cf->base;
336 
337 err_out:
338 	while (current_fence)
339 		dma_fence_put(fences[--current_fence]);
340 	kfree(fences);
341 	kfree(cf);
342 
343 	return ERR_PTR(-ENOMEM);
344 }
345