xref: /linux/drivers/gpu/drm/xe/xe_hw_engine_group.c (revision 570f7e331f5febb30f1384817463c7e42b65ca7d)
1 // SPDX-License-Identifier: MIT
2 /*
3  * Copyright © 2024 Intel Corporation
4  */
5 
6 #include <drm/drm_managed.h>
7 
8 #include "xe_assert.h"
9 #include "xe_device_types.h"
10 #include "xe_exec_queue.h"
11 #include "xe_gt.h"
12 #include "xe_gt_stats.h"
13 #include "xe_hw_engine_group.h"
14 #include "xe_sync.h"
15 #include "xe_vm.h"
16 
17 static void
18 hw_engine_group_resume_lr_jobs_func(struct work_struct *w)
19 {
20 	struct xe_exec_queue *q;
21 	struct xe_hw_engine_group *group = container_of(w, struct xe_hw_engine_group, resume_work);
22 	int err;
23 	enum xe_hw_engine_group_execution_mode previous_mode;
24 
25 	err = xe_hw_engine_group_get_mode(group, EXEC_MODE_LR, &previous_mode,
26 					  NULL, 0);
27 	if (err)
28 		return;
29 
30 	if (previous_mode == EXEC_MODE_LR)
31 		goto put;
32 
33 	list_for_each_entry(q, &group->exec_queue_list, hw_engine_group_link) {
34 		if (!xe_vm_in_fault_mode(q->vm))
35 			continue;
36 
37 		/*
38 		 * Only resume queues that were actually suspended. A queue whose
39 		 * suspend() failed (e.g. killed/banned/wedged) was never
40 		 * suspended, so it must not be resumed.
41 		 */
42 		if (!READ_ONCE(q->lr.suspended))
43 			continue;
44 
45 		WRITE_ONCE(q->lr.suspended, false);
46 		q->ops->resume(q);
47 	}
48 
49 put:
50 	xe_hw_engine_group_put(group);
51 }
52 
53 static struct xe_hw_engine_group *
54 hw_engine_group_alloc(struct xe_device *xe)
55 {
56 	struct xe_hw_engine_group *group;
57 	int err;
58 
59 	group = drmm_kzalloc(&xe->drm, sizeof(*group), GFP_KERNEL);
60 	if (!group)
61 		return ERR_PTR(-ENOMEM);
62 
63 	group->resume_wq = alloc_workqueue("xe-resume-lr-jobs-wq", WQ_PERCPU,
64 					   0);
65 	if (!group->resume_wq)
66 		return ERR_PTR(-ENOMEM);
67 
68 	err = drmm_add_action_or_reset(&xe->drm, __drmm_workqueue_release, group->resume_wq);
69 	if (err)
70 		return ERR_PTR(err);
71 
72 	init_rwsem(&group->mode_sem);
73 	INIT_WORK(&group->resume_work, hw_engine_group_resume_lr_jobs_func);
74 	INIT_LIST_HEAD(&group->exec_queue_list);
75 
76 	return group;
77 }
78 
79 /**
80  * xe_hw_engine_setup_groups() - Setup the hw engine groups for the gt
81  * @gt: The gt for which groups are setup
82  *
83  * Return: 0 on success, negative error code on error.
84  */
85 int xe_hw_engine_setup_groups(struct xe_gt *gt)
86 {
87 	struct xe_hw_engine *hwe;
88 	enum xe_hw_engine_id id;
89 	struct xe_hw_engine_group *group_rcs_ccs, *group_bcs, *group_vcs_vecs;
90 	struct xe_device *xe = gt_to_xe(gt);
91 
92 	group_rcs_ccs = hw_engine_group_alloc(xe);
93 	if (IS_ERR(group_rcs_ccs))
94 		return PTR_ERR(group_rcs_ccs);
95 
96 	group_bcs = hw_engine_group_alloc(xe);
97 	if (IS_ERR(group_bcs))
98 		return PTR_ERR(group_bcs);
99 
100 	group_vcs_vecs = hw_engine_group_alloc(xe);
101 	if (IS_ERR(group_vcs_vecs))
102 		return PTR_ERR(group_vcs_vecs);
103 
104 	for_each_hw_engine(hwe, gt, id) {
105 		switch (hwe->class) {
106 		case XE_ENGINE_CLASS_COPY:
107 			hwe->hw_engine_group = group_bcs;
108 			break;
109 		case XE_ENGINE_CLASS_RENDER:
110 		case XE_ENGINE_CLASS_COMPUTE:
111 			hwe->hw_engine_group = group_rcs_ccs;
112 			break;
113 		case XE_ENGINE_CLASS_VIDEO_DECODE:
114 		case XE_ENGINE_CLASS_VIDEO_ENHANCE:
115 			hwe->hw_engine_group = group_vcs_vecs;
116 			break;
117 		case XE_ENGINE_CLASS_OTHER:
118 			break;
119 		case XE_ENGINE_CLASS_MAX:
120 			xe_gt_assert(gt, false);
121 		}
122 	}
123 
124 	return 0;
125 }
126 
127 /**
128  * xe_hw_engine_group_add_exec_queue() - Add an exec queue to a hw engine group
129  * @group: The hw engine group
130  * @q: The exec_queue
131  *
132  * Return: 0 on success,
133  *	    -EINTR if the lock could not be acquired
134  */
135 int xe_hw_engine_group_add_exec_queue(struct xe_hw_engine_group *group, struct xe_exec_queue *q)
136 {
137 	int err;
138 	struct xe_device *xe = gt_to_xe(q->gt);
139 
140 	xe_assert(xe, group);
141 	xe_assert(xe, !(q->flags & EXEC_QUEUE_FLAG_VM));
142 	xe_assert(xe, q->vm);
143 
144 	if (xe_vm_in_preempt_fence_mode(q->vm))
145 		return 0;
146 
147 	err = down_write_killable(&group->mode_sem);
148 	if (err)
149 		return err;
150 
151 	if (xe_vm_in_fault_mode(q->vm) && group->cur_mode == EXEC_MODE_DMA_FENCE) {
152 		/*
153 		 * suspend() can fail (e.g. killed/banned/wedged), leaving the
154 		 * queue un-suspended. Propagate the failure so the queue is not
155 		 * added; on failure nothing was suspended, so there is nothing to
156 		 * undo. Only record the queue as suspended (and later resume it)
157 		 * once suspend() has succeeded.
158 		 */
159 		err = q->ops->suspend(q);
160 		if (err)
161 			goto err_suspend;
162 
163 		WRITE_ONCE(q->lr.suspended, true);
164 		err = q->ops->suspend_wait(q);
165 		if (err)
166 			goto err_suspend;
167 
168 		xe_hw_engine_group_resume_faulting_lr_jobs(group);
169 	}
170 
171 	list_add(&q->hw_engine_group_link, &group->exec_queue_list);
172 	up_write(&group->mode_sem);
173 
174 	return 0;
175 
176 err_suspend:
177 	up_write(&group->mode_sem);
178 	return err;
179 }
180 ALLOW_ERROR_INJECTION(xe_hw_engine_group_add_exec_queue, ERRNO);
181 
182 /**
183  * xe_hw_engine_group_del_exec_queue() - Delete an exec queue from a hw engine group
184  * @group: The hw engine group
185  * @q: The exec_queue
186  */
187 void xe_hw_engine_group_del_exec_queue(struct xe_hw_engine_group *group, struct xe_exec_queue *q)
188 {
189 	struct xe_device *xe = gt_to_xe(q->gt);
190 
191 	xe_assert(xe, group);
192 	xe_assert(xe, q->vm);
193 
194 	down_write(&group->mode_sem);
195 
196 	if (!list_empty(&q->hw_engine_group_link))
197 		list_del(&q->hw_engine_group_link);
198 
199 	up_write(&group->mode_sem);
200 }
201 
202 /**
203  * xe_hw_engine_group_resume_faulting_lr_jobs() - Asynchronously resume the hw engine group's
204  * faulting LR jobs
205  * @group: The hw engine group
206  */
207 void xe_hw_engine_group_resume_faulting_lr_jobs(struct xe_hw_engine_group *group)
208 {
209 	queue_work(group->resume_wq, &group->resume_work);
210 }
211 
212 /**
213  * xe_hw_engine_group_suspend_faulting_lr_jobs() - Suspend the faulting LR jobs of this group
214  * @group: The hw engine group
215  * @has_deps: dma-fence job triggering suspend has dependencies
216  *
217  * Return: 0 on success, negative error code on error.
218  */
219 static int xe_hw_engine_group_suspend_faulting_lr_jobs(struct xe_hw_engine_group *group,
220 						       bool has_deps)
221 {
222 	int err;
223 	struct xe_exec_queue *q;
224 	struct xe_gt *gt = NULL;
225 	bool need_resume = false;
226 	ktime_t start = xe_gt_stats_ktime_get();
227 
228 	lockdep_assert_held_write(&group->mode_sem);
229 
230 	list_for_each_entry(q, &group->exec_queue_list, hw_engine_group_link) {
231 
232 		if (!xe_vm_in_fault_mode(q->vm))
233 			continue;
234 
235 		if (has_deps)
236 			return -EAGAIN;
237 
238 		xe_gt_stats_incr(q->gt, XE_GT_STATS_ID_HW_ENGINE_GROUP_SUSPEND_LR_QUEUE_COUNT, 1);
239 		/*
240 		 * suspend() only fails when the queue is killed/banned/wedged.
241 		 * Such a queue is being torn down (its removal from HW is handled
242 		 * by the kill/ban teardown), so it is not a live fault-mode
243 		 * context the mode switch must preempt. Skip it rather than
244 		 * failing the switch, otherwise one dying sibling would block a
245 		 * dma-fence submission on the healthy queues in the group. Only
246 		 * queues recorded as suspended below are later waited on and
247 		 * resumed.
248 		 */
249 		err = q->ops->suspend(q);
250 		if (err)
251 			continue;
252 
253 		WRITE_ONCE(q->lr.suspended, true);
254 		need_resume = true;
255 		gt = q->gt;
256 	}
257 
258 	list_for_each_entry(q, &group->exec_queue_list, hw_engine_group_link) {
259 		if (!xe_vm_in_fault_mode(q->vm))
260 			continue;
261 
262 		/* Only wait on queues that were actually suspended above. */
263 		if (!READ_ONCE(q->lr.suspended))
264 			continue;
265 
266 		err = q->ops->suspend_wait(q);
267 		if (err)
268 			goto err_resume;
269 	}
270 
271 	if (gt) {
272 		xe_gt_stats_incr(gt,
273 				 XE_GT_STATS_ID_HW_ENGINE_GROUP_SUSPEND_LR_QUEUE_US,
274 				 xe_gt_stats_ktime_us_delta(start));
275 	}
276 
277 	if (need_resume)
278 		xe_hw_engine_group_resume_faulting_lr_jobs(group);
279 
280 	return 0;
281 
282 err_resume:
283 	/*
284 	 * A suspend_wait() failed partway through the mode switch. Resume the
285 	 * sibling queues that were already suspended in this call so they are
286 	 * not left suspended forever.
287 	 *
288 	 * resume() requires the suspend to have completed (suspend_pending
289 	 * cleared) or it trips the !suspend_pending assert. So skip the resume
290 	 * when either:
291 	 *  - suspend_wait_blocking() fails: on a GuC timeout it bans the queue
292 	 *    and triggers cleanup, so the queue is being torn down; or
293 	 *  - reset_status() is true: the queue was reset/killed/banned/wedged.
294 	 *    suspend_wait() can return success in this case via its killed/
295 	 *    stopped wait condition while suspend_pending is still set, and the
296 	 *    queue is being torn down anyway, so its state is resolved by
297 	 *    teardown rather than by a resume here.
298 	 * In either case leave the queue marked suspended.
299 	 *
300 	 * Use the *blocking* (uninterruptible) wait here: the queues resumed on
301 	 * this path may belong to a different process than the one that
302 	 * triggered the mode switch. An interruptible suspend_wait() would
303 	 * return -ERESTARTSYS if the triggering task is signalled, skip the
304 	 * resume, and leave the other process's queue suspended forever
305 	 * (cross-process DoS).
306 	 */
307 	list_for_each_entry(q, &group->exec_queue_list, hw_engine_group_link) {
308 		if (!xe_vm_in_fault_mode(q->vm))
309 			continue;
310 
311 		if (!READ_ONCE(q->lr.suspended))
312 			continue;
313 
314 		if (q->ops->suspend_wait_blocking(q) || q->ops->reset_status(q))
315 			continue;
316 
317 		WRITE_ONCE(q->lr.suspended, false);
318 		q->ops->resume(q);
319 	}
320 
321 	return err;
322 }
323 
324 /**
325  * xe_hw_engine_group_wait_for_dma_fence_jobs() - Wait for dma fence jobs to complete
326  * @group: The hw engine group
327  *
328  * This function is not meant to be called directly from a user IOCTL as dma_fence_wait()
329  * is not interruptible.
330  *
331  * Return: 0 on success,
332  *	   -ETIME if waiting for one job failed
333  */
334 static int xe_hw_engine_group_wait_for_dma_fence_jobs(struct xe_hw_engine_group *group)
335 {
336 	long timeout;
337 	struct xe_exec_queue *q;
338 	struct xe_gt *gt = NULL;
339 	struct dma_fence *fence;
340 	ktime_t start = xe_gt_stats_ktime_get();
341 
342 	lockdep_assert_held_write(&group->mode_sem);
343 
344 	list_for_each_entry(q, &group->exec_queue_list, hw_engine_group_link) {
345 		if (xe_vm_in_lr_mode(q->vm))
346 			continue;
347 
348 		xe_gt_stats_incr(q->gt, XE_GT_STATS_ID_HW_ENGINE_GROUP_WAIT_DMA_QUEUE_COUNT, 1);
349 		fence = xe_exec_queue_last_fence_get_for_resume(q, q->vm);
350 		timeout = dma_fence_wait(fence, false);
351 		dma_fence_put(fence);
352 		gt = q->gt;
353 
354 		if (timeout < 0)
355 			return -ETIME;
356 	}
357 
358 	if (gt) {
359 		xe_gt_stats_incr(gt,
360 				 XE_GT_STATS_ID_HW_ENGINE_GROUP_WAIT_DMA_QUEUE_US,
361 				 xe_gt_stats_ktime_us_delta(start));
362 	}
363 
364 	return 0;
365 }
366 
367 static int switch_mode(struct xe_hw_engine_group *group, bool has_deps)
368 {
369 	int err = 0;
370 	enum xe_hw_engine_group_execution_mode new_mode;
371 
372 	lockdep_assert_held_write(&group->mode_sem);
373 
374 	switch (group->cur_mode) {
375 	case EXEC_MODE_LR:
376 		new_mode = EXEC_MODE_DMA_FENCE;
377 		err = xe_hw_engine_group_suspend_faulting_lr_jobs(group,
378 								  has_deps);
379 		break;
380 	case EXEC_MODE_DMA_FENCE:
381 		new_mode = EXEC_MODE_LR;
382 		err = xe_hw_engine_group_wait_for_dma_fence_jobs(group);
383 		break;
384 	}
385 
386 	if (err)
387 		return err;
388 
389 	group->cur_mode = new_mode;
390 
391 	return 0;
392 }
393 
394 static int wait_syncs(struct xe_sync_entry *syncs, int num_syncs)
395 {
396 	int err, i;
397 
398 	for (i = 0; i < num_syncs; ++i) {
399 		err = xe_sync_entry_wait(syncs + i);
400 		if (err)
401 			return err;
402 	}
403 
404 	return 0;
405 }
406 
407 /**
408  * xe_hw_engine_group_get_mode() - Get the group to execute in the new mode
409  * @group: The hw engine group
410  * @new_mode: The new execution mode
411  * @previous_mode: Pointer to the previous mode provided for use by caller
412  * @syncs: Syncs from exec IOCTL
413  * @num_syncs: Number of syncs from exec IOCTL
414  *
415  * Return: 0 if successful, -EINTR if locking failed.
416  */
417 int xe_hw_engine_group_get_mode(struct xe_hw_engine_group *group,
418 				enum xe_hw_engine_group_execution_mode new_mode,
419 				enum xe_hw_engine_group_execution_mode *previous_mode,
420 				struct xe_sync_entry *syncs, int num_syncs)
421 __acquires(&group->mode_sem)
422 {
423 	bool has_deps = !!num_syncs;
424 	int err = down_read_interruptible(&group->mode_sem);
425 
426 	if (err)
427 		return err;
428 
429 	*previous_mode = group->cur_mode;
430 
431 	if (new_mode != group->cur_mode) {
432 		up_read(&group->mode_sem);
433 retry:
434 		err = down_write_killable(&group->mode_sem);
435 		if (err)
436 			return err;
437 
438 		if (new_mode != group->cur_mode) {
439 			err = switch_mode(group, has_deps);
440 			if (err) {
441 				up_write(&group->mode_sem);
442 
443 				if (err != -EAGAIN)
444 					return err;
445 
446 				err = wait_syncs(syncs, num_syncs);
447 				if (err)
448 					return err;
449 
450 				has_deps = false;
451 				goto retry;
452 			}
453 		}
454 		downgrade_write(&group->mode_sem);
455 	}
456 
457 	return err;
458 }
459 
460 /**
461  * xe_hw_engine_group_put() - Put the group
462  * @group: The hw engine group
463  */
464 void xe_hw_engine_group_put(struct xe_hw_engine_group *group)
465 __releases(&group->mode_sem)
466 {
467 	up_read(&group->mode_sem);
468 }
469 
470 /**
471  * xe_hw_engine_group_find_exec_mode() - Find the execution mode for this exec queue
472  * @q: The exec_queue
473  */
474 enum xe_hw_engine_group_execution_mode
475 xe_hw_engine_group_find_exec_mode(struct xe_exec_queue *q)
476 {
477 	if (xe_vm_in_fault_mode(q->vm))
478 		return EXEC_MODE_LR;
479 	else
480 		return EXEC_MODE_DMA_FENCE;
481 }
482