xref: /openbsd-src/sys/dev/pci/drm/i915/gt/intel_gt_requests.c (revision f005ef32267c16bdb134f0e9fa4477dbe07c263a)
15ca02815Sjsg // SPDX-License-Identifier: MIT
2c349dbc7Sjsg /*
3c349dbc7Sjsg  * Copyright © 2019 Intel Corporation
4c349dbc7Sjsg  */
5c349dbc7Sjsg 
6c349dbc7Sjsg #include <linux/workqueue.h>
7c349dbc7Sjsg 
8c349dbc7Sjsg #include "i915_drv.h" /* for_each_engine() */
9c349dbc7Sjsg #include "i915_request.h"
10c349dbc7Sjsg #include "intel_engine_heartbeat.h"
115ca02815Sjsg #include "intel_execlists_submission.h"
12c349dbc7Sjsg #include "intel_gt.h"
13c349dbc7Sjsg #include "intel_gt_pm.h"
14c349dbc7Sjsg #include "intel_gt_requests.h"
15c349dbc7Sjsg #include "intel_timeline.h"
16c349dbc7Sjsg 
retire_requests(struct intel_timeline * tl)17c349dbc7Sjsg static bool retire_requests(struct intel_timeline *tl)
18c349dbc7Sjsg {
19c349dbc7Sjsg 	struct i915_request *rq, *rn;
20c349dbc7Sjsg 
21c349dbc7Sjsg 	list_for_each_entry_safe(rq, rn, &tl->requests, link)
22c349dbc7Sjsg 		if (!i915_request_retire(rq))
23c349dbc7Sjsg 			return false;
24c349dbc7Sjsg 
25c349dbc7Sjsg 	/* And check nothing new was submitted */
26c349dbc7Sjsg 	return !i915_active_fence_isset(&tl->last_request);
27c349dbc7Sjsg }
28c349dbc7Sjsg 
engine_active(const struct intel_engine_cs * engine)29ad8b1aafSjsg static bool engine_active(const struct intel_engine_cs *engine)
30ad8b1aafSjsg {
31ad8b1aafSjsg 	return !list_empty(&engine->kernel_context->timeline->requests);
32ad8b1aafSjsg }
33ad8b1aafSjsg 
flush_submission(struct intel_gt * gt,long timeout)34ad8b1aafSjsg static bool flush_submission(struct intel_gt *gt, long timeout)
35c349dbc7Sjsg {
36c349dbc7Sjsg 	struct intel_engine_cs *engine;
37c349dbc7Sjsg 	enum intel_engine_id id;
38c349dbc7Sjsg 	bool active = false;
39c349dbc7Sjsg 
40ad8b1aafSjsg 	if (!timeout)
41ad8b1aafSjsg 		return false;
42ad8b1aafSjsg 
43c349dbc7Sjsg 	if (!intel_gt_pm_is_awake(gt))
44c349dbc7Sjsg 		return false;
45c349dbc7Sjsg 
46c349dbc7Sjsg 	for_each_engine(engine, gt, id) {
47c349dbc7Sjsg 		intel_engine_flush_submission(engine);
48ad8b1aafSjsg 
49ad8b1aafSjsg 		/* Flush the background retirement and idle barriers */
50ad8b1aafSjsg 		flush_work(&engine->retire_work);
51ad8b1aafSjsg 		flush_delayed_work(&engine->wakeref.work);
52ad8b1aafSjsg 
53ad8b1aafSjsg 		/* Is the idle barrier still outstanding? */
54ad8b1aafSjsg 		active |= engine_active(engine);
55c349dbc7Sjsg 	}
56c349dbc7Sjsg 
57c349dbc7Sjsg 	return active;
58c349dbc7Sjsg }
59c349dbc7Sjsg 
engine_retire(struct work_struct * work)60c349dbc7Sjsg static void engine_retire(struct work_struct *work)
61c349dbc7Sjsg {
62c349dbc7Sjsg 	struct intel_engine_cs *engine =
63c349dbc7Sjsg 		container_of(work, typeof(*engine), retire_work);
64c349dbc7Sjsg 	struct intel_timeline *tl = xchg(&engine->retire, NULL);
65c349dbc7Sjsg 
66c349dbc7Sjsg 	do {
67c349dbc7Sjsg 		struct intel_timeline *next = xchg(&tl->retire, NULL);
68c349dbc7Sjsg 
69c349dbc7Sjsg 		/*
70c349dbc7Sjsg 		 * Our goal here is to retire _idle_ timelines as soon as
71c349dbc7Sjsg 		 * possible (as they are idle, we do not expect userspace
72c349dbc7Sjsg 		 * to be cleaning up anytime soon).
73c349dbc7Sjsg 		 *
74c349dbc7Sjsg 		 * If the timeline is currently locked, either it is being
75c349dbc7Sjsg 		 * retired elsewhere or about to be!
76c349dbc7Sjsg 		 */
77c349dbc7Sjsg 		if (mutex_trylock(&tl->mutex)) {
78c349dbc7Sjsg 			retire_requests(tl);
79c349dbc7Sjsg 			mutex_unlock(&tl->mutex);
80c349dbc7Sjsg 		}
81c349dbc7Sjsg 		intel_timeline_put(tl);
82c349dbc7Sjsg 
83c349dbc7Sjsg 		GEM_BUG_ON(!next);
84c349dbc7Sjsg 		tl = ptr_mask_bits(next, 1);
85c349dbc7Sjsg 	} while (tl);
86c349dbc7Sjsg }
87c349dbc7Sjsg 
add_retire(struct intel_engine_cs * engine,struct intel_timeline * tl)88c349dbc7Sjsg static bool add_retire(struct intel_engine_cs *engine,
89c349dbc7Sjsg 		       struct intel_timeline *tl)
90c349dbc7Sjsg {
91c349dbc7Sjsg #define RSTUB ((struct intel_timeline *)1)
92c349dbc7Sjsg 	struct intel_timeline *first;
93c349dbc7Sjsg 
94c349dbc7Sjsg 	/*
95c349dbc7Sjsg 	 * We open-code a llist here to include the additional tag [BIT(0)]
96c349dbc7Sjsg 	 * so that we know when the timeline is already on a
97c349dbc7Sjsg 	 * retirement queue: either this engine or another.
98c349dbc7Sjsg 	 */
99c349dbc7Sjsg 
100c349dbc7Sjsg 	if (cmpxchg(&tl->retire, NULL, RSTUB)) /* already queued */
101c349dbc7Sjsg 		return false;
102c349dbc7Sjsg 
103c349dbc7Sjsg 	intel_timeline_get(tl);
104c349dbc7Sjsg 	first = READ_ONCE(engine->retire);
105c349dbc7Sjsg 	do
106c349dbc7Sjsg 		tl->retire = ptr_pack_bits(first, 1, 1);
107c349dbc7Sjsg 	while (!try_cmpxchg(&engine->retire, &first, tl));
108c349dbc7Sjsg 
109c349dbc7Sjsg 	return !first;
110c349dbc7Sjsg }
111c349dbc7Sjsg 
intel_engine_add_retire(struct intel_engine_cs * engine,struct intel_timeline * tl)112c349dbc7Sjsg void intel_engine_add_retire(struct intel_engine_cs *engine,
113c349dbc7Sjsg 			     struct intel_timeline *tl)
114c349dbc7Sjsg {
115c349dbc7Sjsg 	/* We don't deal well with the engine disappearing beneath us */
116c349dbc7Sjsg 	GEM_BUG_ON(intel_engine_is_virtual(engine));
117c349dbc7Sjsg 
118c349dbc7Sjsg 	if (add_retire(engine, tl))
119*f005ef32Sjsg 		queue_work(engine->i915->unordered_wq, &engine->retire_work);
120c349dbc7Sjsg }
121c349dbc7Sjsg 
intel_engine_init_retire(struct intel_engine_cs * engine)122c349dbc7Sjsg void intel_engine_init_retire(struct intel_engine_cs *engine)
123c349dbc7Sjsg {
124c349dbc7Sjsg 	INIT_WORK(&engine->retire_work, engine_retire);
125c349dbc7Sjsg }
126c349dbc7Sjsg 
intel_engine_fini_retire(struct intel_engine_cs * engine)127c349dbc7Sjsg void intel_engine_fini_retire(struct intel_engine_cs *engine)
128c349dbc7Sjsg {
129c349dbc7Sjsg 	flush_work(&engine->retire_work);
130c349dbc7Sjsg 	GEM_BUG_ON(engine->retire);
131c349dbc7Sjsg }
132c349dbc7Sjsg 
intel_gt_retire_requests_timeout(struct intel_gt * gt,long timeout,long * remaining_timeout)1335ca02815Sjsg long intel_gt_retire_requests_timeout(struct intel_gt *gt, long timeout,
1345ca02815Sjsg 				      long *remaining_timeout)
135c349dbc7Sjsg {
136c349dbc7Sjsg 	struct intel_gt_timelines *timelines = &gt->timelines;
137c349dbc7Sjsg 	struct intel_timeline *tl, *tn;
138c349dbc7Sjsg 	unsigned long active_count = 0;
139c349dbc7Sjsg 	DRM_LIST_HEAD(free);
140c349dbc7Sjsg 
141ad8b1aafSjsg 	flush_submission(gt, timeout); /* kick the ksoftirqd tasklets */
142c349dbc7Sjsg 	spin_lock(&timelines->lock);
143c349dbc7Sjsg 	list_for_each_entry_safe(tl, tn, &timelines->active_list, link) {
144c349dbc7Sjsg 		if (!mutex_trylock(&tl->mutex)) {
145c349dbc7Sjsg 			active_count++; /* report busy to caller, try again? */
146c349dbc7Sjsg 			continue;
147c349dbc7Sjsg 		}
148c349dbc7Sjsg 
149c349dbc7Sjsg 		intel_timeline_get(tl);
150c349dbc7Sjsg 		GEM_BUG_ON(!atomic_read(&tl->active_count));
151c349dbc7Sjsg 		atomic_inc(&tl->active_count); /* pin the list element */
152c349dbc7Sjsg 		spin_unlock(&timelines->lock);
153c349dbc7Sjsg 
154c349dbc7Sjsg 		if (timeout > 0) {
155c349dbc7Sjsg 			struct dma_fence *fence;
156c349dbc7Sjsg 
157c349dbc7Sjsg 			fence = i915_active_fence_get(&tl->last_request);
158c349dbc7Sjsg 			if (fence) {
159c349dbc7Sjsg 				mutex_unlock(&tl->mutex);
160c349dbc7Sjsg 
161c349dbc7Sjsg 				timeout = dma_fence_wait_timeout(fence,
1625ca02815Sjsg 								 true,
163c349dbc7Sjsg 								 timeout);
164c349dbc7Sjsg 				dma_fence_put(fence);
165c349dbc7Sjsg 
166c349dbc7Sjsg 				/* Retirement is best effort */
167c349dbc7Sjsg 				if (!mutex_trylock(&tl->mutex)) {
168c349dbc7Sjsg 					active_count++;
169c349dbc7Sjsg 					goto out_active;
170c349dbc7Sjsg 				}
171c349dbc7Sjsg 			}
172c349dbc7Sjsg 		}
173c349dbc7Sjsg 
174ad8b1aafSjsg 		if (!retire_requests(tl))
175c349dbc7Sjsg 			active_count++;
176c349dbc7Sjsg 		mutex_unlock(&tl->mutex);
177c349dbc7Sjsg 
178c349dbc7Sjsg out_active:	spin_lock(&timelines->lock);
179c349dbc7Sjsg 
180c349dbc7Sjsg 		/* Resume list iteration after reacquiring spinlock */
181c349dbc7Sjsg 		list_safe_reset_next(tl, tn, link);
182c349dbc7Sjsg 		if (atomic_dec_and_test(&tl->active_count))
183c349dbc7Sjsg 			list_del(&tl->link);
184c349dbc7Sjsg 
185c349dbc7Sjsg 		/* Defer the final release to after the spinlock */
186c349dbc7Sjsg 		if (refcount_dec_and_test(&tl->kref.refcount)) {
187c349dbc7Sjsg 			GEM_BUG_ON(atomic_read(&tl->active_count));
188c349dbc7Sjsg 			list_add(&tl->link, &free);
189c349dbc7Sjsg 		}
190c349dbc7Sjsg 	}
191c349dbc7Sjsg 	spin_unlock(&timelines->lock);
192c349dbc7Sjsg 
193c349dbc7Sjsg 	list_for_each_entry_safe(tl, tn, &free, link)
194c349dbc7Sjsg 		__intel_timeline_free(&tl->kref);
195c349dbc7Sjsg 
196ad8b1aafSjsg 	if (flush_submission(gt, timeout)) /* Wait, there's more! */
197ad8b1aafSjsg 		active_count++;
198ad8b1aafSjsg 
1995ca02815Sjsg 	if (remaining_timeout)
2005ca02815Sjsg 		*remaining_timeout = timeout;
2015ca02815Sjsg 
2021bb76ff1Sjsg 	return active_count ? timeout ?: -ETIME : 0;
203c349dbc7Sjsg }
204c349dbc7Sjsg 
retire_work_handler(struct work_struct * work)205c349dbc7Sjsg static void retire_work_handler(struct work_struct *work)
206c349dbc7Sjsg {
207c349dbc7Sjsg 	struct intel_gt *gt =
208c349dbc7Sjsg 		container_of(work, typeof(*gt), requests.retire_work.work);
209c349dbc7Sjsg 
210*f005ef32Sjsg 	queue_delayed_work(gt->i915->unordered_wq, &gt->requests.retire_work,
211c349dbc7Sjsg 			   round_jiffies_up_relative(HZ));
212c349dbc7Sjsg 	intel_gt_retire_requests(gt);
213c349dbc7Sjsg }
214c349dbc7Sjsg 
intel_gt_init_requests(struct intel_gt * gt)215c349dbc7Sjsg void intel_gt_init_requests(struct intel_gt *gt)
216c349dbc7Sjsg {
217c349dbc7Sjsg 	INIT_DELAYED_WORK(&gt->requests.retire_work, retire_work_handler);
218c349dbc7Sjsg }
219c349dbc7Sjsg 
intel_gt_park_requests(struct intel_gt * gt)220c349dbc7Sjsg void intel_gt_park_requests(struct intel_gt *gt)
221c349dbc7Sjsg {
222c349dbc7Sjsg 	cancel_delayed_work(&gt->requests.retire_work);
223c349dbc7Sjsg }
224c349dbc7Sjsg 
intel_gt_unpark_requests(struct intel_gt * gt)225c349dbc7Sjsg void intel_gt_unpark_requests(struct intel_gt *gt)
226c349dbc7Sjsg {
227*f005ef32Sjsg 	queue_delayed_work(gt->i915->unordered_wq, &gt->requests.retire_work,
228c349dbc7Sjsg 			   round_jiffies_up_relative(HZ));
229c349dbc7Sjsg }
230c349dbc7Sjsg 
intel_gt_fini_requests(struct intel_gt * gt)231c349dbc7Sjsg void intel_gt_fini_requests(struct intel_gt *gt)
232c349dbc7Sjsg {
233c349dbc7Sjsg 	/* Wait until the work is marked as finished before unloading! */
234c349dbc7Sjsg 	cancel_delayed_work_sync(&gt->requests.retire_work);
2355ca02815Sjsg 
2365ca02815Sjsg 	flush_work(&gt->watchdog.work);
2375ca02815Sjsg }
2385ca02815Sjsg 
intel_gt_watchdog_work(struct work_struct * work)2395ca02815Sjsg void intel_gt_watchdog_work(struct work_struct *work)
2405ca02815Sjsg {
2415ca02815Sjsg 	struct intel_gt *gt =
2425ca02815Sjsg 		container_of(work, typeof(*gt), watchdog.work);
2435ca02815Sjsg 	struct i915_request *rq, *rn;
2445ca02815Sjsg 	struct llist_node *first;
2455ca02815Sjsg 
2465ca02815Sjsg 	first = llist_del_all(&gt->watchdog.list);
2475ca02815Sjsg 	if (!first)
2485ca02815Sjsg 		return;
2495ca02815Sjsg 
2505ca02815Sjsg 	llist_for_each_entry_safe(rq, rn, first, watchdog.link) {
2515ca02815Sjsg 		if (!i915_request_completed(rq)) {
2525ca02815Sjsg 			struct dma_fence *f = &rq->fence;
2535ca02815Sjsg 
2545ca02815Sjsg 			pr_notice("Fence expiration time out i915-%s:%s:%llx!\n",
2555ca02815Sjsg 				  f->ops->get_driver_name(f),
2565ca02815Sjsg 				  f->ops->get_timeline_name(f),
2575ca02815Sjsg 				  f->seqno);
2585ca02815Sjsg 			i915_request_cancel(rq, -EINTR);
2595ca02815Sjsg 		}
2605ca02815Sjsg 		i915_request_put(rq);
2615ca02815Sjsg 	}
262c349dbc7Sjsg }
263