Branch data Line data Source code
1 : : /*
2 : : * SPDX-License-Identifier: MIT
3 : : *
4 : : * Copyright © 2019 Intel Corporation
5 : : */
6 : :
7 : : #include <linux/workqueue.h>
8 : :
9 : : #include "i915_drv.h" /* for_each_engine() */
10 : : #include "i915_request.h"
11 : : #include "intel_engine_heartbeat.h"
12 : : #include "intel_gt.h"
13 : : #include "intel_gt_pm.h"
14 : : #include "intel_gt_requests.h"
15 : : #include "intel_timeline.h"
16 : :
17 : 0 : static bool retire_requests(struct intel_timeline *tl)
18 : : {
19 : 0 : struct i915_request *rq, *rn;
20 : :
21 [ # # ]: 0 : list_for_each_entry_safe(rq, rn, &tl->requests, link)
22 [ # # ]: 0 : if (!i915_request_retire(rq))
23 : : return false;
24 : :
25 : : /* And check nothing new was submitted */
26 : 0 : return !i915_active_fence_isset(&tl->last_request);
27 : : }
28 : :
29 : 0 : static bool flush_submission(struct intel_gt *gt)
30 : : {
31 : 0 : struct intel_engine_cs *engine;
32 : 0 : enum intel_engine_id id;
33 : 0 : bool active = false;
34 : :
35 [ # # ]: 0 : if (!intel_gt_pm_is_awake(gt))
36 : : return false;
37 : :
38 [ # # # # ]: 0 : for_each_engine(engine, gt, id) {
39 : 0 : intel_engine_flush_submission(engine);
40 : 0 : active |= flush_work(&engine->retire_work);
41 : 0 : active |= flush_work(&engine->wakeref.work);
42 : : }
43 : :
44 : : return active;
45 : : }
46 : :
47 : 0 : static void engine_retire(struct work_struct *work)
48 : : {
49 : 0 : struct intel_engine_cs *engine =
50 : 0 : container_of(work, typeof(*engine), retire_work);
51 : 0 : struct intel_timeline *tl = xchg(&engine->retire, NULL);
52 : :
53 : 0 : do {
54 : 0 : struct intel_timeline *next = xchg(&tl->retire, NULL);
55 : :
56 : : /*
57 : : * Our goal here is to retire _idle_ timelines as soon as
58 : : * possible (as they are idle, we do not expect userspace
59 : : * to be cleaning up anytime soon).
60 : : *
61 : : * If the timeline is currently locked, either it is being
62 : : * retired elsewhere or about to be!
63 : : */
64 [ # # ]: 0 : if (mutex_trylock(&tl->mutex)) {
65 : 0 : retire_requests(tl);
66 : 0 : mutex_unlock(&tl->mutex);
67 : : }
68 : 0 : intel_timeline_put(tl);
69 : :
70 : 0 : GEM_BUG_ON(!next);
71 : 0 : tl = ptr_mask_bits(next, 1);
72 [ # # ]: 0 : } while (tl);
73 : 0 : }
74 : :
75 : 0 : static bool add_retire(struct intel_engine_cs *engine,
76 : : struct intel_timeline *tl)
77 : : {
78 : : #define STUB ((struct intel_timeline *)1)
79 : 0 : struct intel_timeline *first;
80 : :
81 : : /*
82 : : * We open-code a llist here to include the additional tag [BIT(0)]
83 : : * so that we know when the timeline is already on a
84 : : * retirement queue: either this engine or another.
85 : : */
86 : :
87 [ # # ]: 0 : if (cmpxchg(&tl->retire, NULL, STUB)) /* already queued */
88 : : return false;
89 : :
90 : 0 : intel_timeline_get(tl);
91 : 0 : first = READ_ONCE(engine->retire);
92 : 0 : do
93 : 0 : tl->retire = ptr_pack_bits(first, 1, 1);
94 [ # # # # ]: 0 : while (!try_cmpxchg(&engine->retire, &first, tl));
95 : :
96 : 0 : return !first;
97 : : }
98 : :
99 : 0 : void intel_engine_add_retire(struct intel_engine_cs *engine,
100 : : struct intel_timeline *tl)
101 : : {
102 : : /* We don't deal well with the engine disappearing beneath us */
103 : 0 : GEM_BUG_ON(intel_engine_is_virtual(engine));
104 : :
105 [ # # ]: 0 : if (add_retire(engine, tl))
106 : 0 : schedule_work(&engine->retire_work);
107 : 0 : }
108 : :
109 : 0 : void intel_engine_init_retire(struct intel_engine_cs *engine)
110 : : {
111 : 0 : INIT_WORK(&engine->retire_work, engine_retire);
112 : 0 : }
113 : :
114 : 0 : void intel_engine_fini_retire(struct intel_engine_cs *engine)
115 : : {
116 : 0 : flush_work(&engine->retire_work);
117 : 0 : GEM_BUG_ON(engine->retire);
118 : 0 : }
119 : :
120 : 0 : long intel_gt_retire_requests_timeout(struct intel_gt *gt, long timeout)
121 : : {
122 : 0 : struct intel_gt_timelines *timelines = >->timelines;
123 : 0 : struct intel_timeline *tl, *tn;
124 : 0 : unsigned long active_count = 0;
125 : 0 : bool interruptible;
126 : 0 : LIST_HEAD(free);
127 : :
128 : 0 : interruptible = true;
129 [ # # ]: 0 : if (unlikely(timeout < 0))
130 : 0 : timeout = -timeout, interruptible = false;
131 : :
132 : 0 : flush_submission(gt); /* kick the ksoftirqd tasklets */
133 : 0 : spin_lock(&timelines->lock);
134 [ # # ]: 0 : list_for_each_entry_safe(tl, tn, &timelines->active_list, link) {
135 [ # # ]: 0 : if (!mutex_trylock(&tl->mutex)) {
136 : 0 : active_count++; /* report busy to caller, try again? */
137 : 0 : continue;
138 : : }
139 : :
140 : 0 : intel_timeline_get(tl);
141 : 0 : GEM_BUG_ON(!atomic_read(&tl->active_count));
142 : 0 : atomic_inc(&tl->active_count); /* pin the list element */
143 : 0 : spin_unlock(&timelines->lock);
144 : :
145 [ # # ]: 0 : if (timeout > 0) {
146 : 0 : struct dma_fence *fence;
147 : :
148 : 0 : fence = i915_active_fence_get(&tl->last_request);
149 [ # # ]: 0 : if (fence) {
150 : 0 : mutex_unlock(&tl->mutex);
151 : :
152 : 0 : timeout = dma_fence_wait_timeout(fence,
153 : : interruptible,
154 : : timeout);
155 : 0 : dma_fence_put(fence);
156 : :
157 : : /* Retirement is best effort */
158 [ # # ]: 0 : if (!mutex_trylock(&tl->mutex)) {
159 : 0 : active_count++;
160 : 0 : goto out_active;
161 : : }
162 : : }
163 : : }
164 : :
165 [ # # # # ]: 0 : if (!retire_requests(tl) || flush_submission(gt))
166 : 0 : active_count++;
167 : 0 : mutex_unlock(&tl->mutex);
168 : :
169 : 0 : out_active: spin_lock(&timelines->lock);
170 : :
171 : : /* Resume list iteration after reacquiring spinlock */
172 : 0 : list_safe_reset_next(tl, tn, link);
173 [ # # ]: 0 : if (atomic_dec_and_test(&tl->active_count))
174 : 0 : list_del(&tl->link);
175 : :
176 : :
177 : : /* Defer the final release to after the spinlock */
178 [ # # ]: 0 : if (refcount_dec_and_test(&tl->kref.refcount)) {
179 : 0 : GEM_BUG_ON(atomic_read(&tl->active_count));
180 : 0 : list_add(&tl->link, &free);
181 : : }
182 : : }
183 : 0 : spin_unlock(&timelines->lock);
184 : :
185 [ # # ]: 0 : list_for_each_entry_safe(tl, tn, &free, link)
186 : 0 : __intel_timeline_free(&tl->kref);
187 : :
188 [ # # ]: 0 : return active_count ? timeout : 0;
189 : : }
190 : :
191 : 0 : int intel_gt_wait_for_idle(struct intel_gt *gt, long timeout)
192 : : {
193 : : /* If the device is asleep, we have no requests outstanding */
194 [ # # ]: 0 : if (!intel_gt_pm_is_awake(gt))
195 : : return 0;
196 : :
197 [ # # ]: 0 : while ((timeout = intel_gt_retire_requests_timeout(gt, timeout)) > 0) {
198 : 0 : cond_resched();
199 [ # # ]: 0 : if (signal_pending(current))
200 : : return -EINTR;
201 : : }
202 : :
203 : 0 : return timeout;
204 : : }
205 : :
206 : 0 : static void retire_work_handler(struct work_struct *work)
207 : : {
208 : 0 : struct intel_gt *gt =
209 : 0 : container_of(work, typeof(*gt), requests.retire_work.work);
210 : :
211 : 0 : schedule_delayed_work(>->requests.retire_work,
212 : : round_jiffies_up_relative(HZ));
213 : 0 : intel_gt_retire_requests(gt);
214 : 0 : }
215 : :
216 : 0 : void intel_gt_init_requests(struct intel_gt *gt)
217 : : {
218 : 0 : INIT_DELAYED_WORK(>->requests.retire_work, retire_work_handler);
219 : 0 : }
220 : :
221 : 0 : void intel_gt_park_requests(struct intel_gt *gt)
222 : : {
223 : 0 : cancel_delayed_work(>->requests.retire_work);
224 : 0 : }
225 : :
226 : 0 : void intel_gt_unpark_requests(struct intel_gt *gt)
227 : : {
228 : 0 : schedule_delayed_work(>->requests.retire_work,
229 : : round_jiffies_up_relative(HZ));
230 : 0 : }
231 : :
232 : 0 : void intel_gt_fini_requests(struct intel_gt *gt)
233 : : {
234 : : /* Wait until the work is marked as finished before unloading! */
235 : 0 : cancel_delayed_work_sync(>->requests.retire_work);
236 : 0 : }
|