1 // SPDX-License-Identifier: MIT
2 /*
3 * Copyright �� 2021 Intel Corporation
4 */
5
6 #include "selftests/igt_spinner.h"
7 #include "selftests/intel_scheduler_helpers.h"
8
request_add_spin(struct i915_request * rq,struct igt_spinner * spin)9 static int request_add_spin(struct i915_request *rq, struct igt_spinner *spin)
10 {
11 int err = 0;
12
13 i915_request_get(rq);
14 i915_request_add(rq);
15 if (spin && !igt_wait_for_spinner(spin, rq))
16 err = -ETIMEDOUT;
17
18 return err;
19 }
20
nop_user_request(struct intel_context * ce,struct i915_request * from)21 static struct i915_request *nop_user_request(struct intel_context *ce,
22 struct i915_request *from)
23 {
24 struct i915_request *rq;
25 int ret;
26
27 rq = intel_context_create_request(ce);
28 if (IS_ERR(rq))
29 return rq;
30
31 if (from) {
32 ret = i915_sw_fence_await_dma_fence(&rq->submit,
33 &from->fence, 0,
34 I915_FENCE_GFP);
35 if (ret < 0) {
36 i915_request_put(rq);
37 return ERR_PTR(ret);
38 }
39 }
40
41 i915_request_get(rq);
42 i915_request_add(rq);
43
44 return rq;
45 }
46
intel_guc_scrub_ctbs(void * arg)47 static int intel_guc_scrub_ctbs(void *arg)
48 {
49 struct intel_gt *gt = arg;
50 int ret = 0;
51 int i;
52 struct i915_request *last[3] = {NULL, NULL, NULL}, *rq;
53 intel_wakeref_t wakeref;
54 struct intel_engine_cs *engine;
55 struct intel_context *ce;
56
57 if (!intel_has_gpu_reset(gt))
58 return 0;
59
60 wakeref = intel_runtime_pm_get(gt->uncore->rpm);
61 engine = intel_selftest_find_any_engine(gt);
62
63 /* Submit requests and inject errors forcing G2H to be dropped */
64 for (i = 0; i < 3; ++i) {
65 ce = intel_context_create(engine);
66 if (IS_ERR(ce)) {
67 ret = PTR_ERR(ce);
68 drm_err(>->i915->drm, "Failed to create context, %d: %d\n", i, ret);
69 goto err;
70 }
71
72 switch (i) {
73 case 0:
74 ce->drop_schedule_enable = true;
75 break;
76 case 1:
77 ce->drop_schedule_disable = true;
78 break;
79 case 2:
80 ce->drop_deregister = true;
81 break;
82 }
83
84 rq = nop_user_request(ce, NULL);
85 intel_context_put(ce);
86
87 if (IS_ERR(rq)) {
88 ret = PTR_ERR(rq);
89 drm_err(>->i915->drm, "Failed to create request, %d: %d\n", i, ret);
90 goto err;
91 }
92
93 last[i] = rq;
94 }
95
96 for (i = 0; i < 3; ++i) {
97 ret = i915_request_wait(last[i], 0, HZ);
98 if (ret < 0) {
99 drm_err(>->i915->drm, "Last request failed to complete: %d\n", ret);
100 goto err;
101 }
102 i915_request_put(last[i]);
103 last[i] = NULL;
104 }
105
106 /* Force all H2G / G2H to be submitted / processed */
107 intel_gt_retire_requests(gt);
108 msleep(500);
109
110 /* Scrub missing G2H */
111 intel_gt_handle_error(engine->gt, -1, 0, "selftest reset");
112
113 /* GT will not idle if G2H are lost */
114 ret = intel_gt_wait_for_idle(gt, HZ);
115 if (ret < 0) {
116 drm_err(>->i915->drm, "GT failed to idle: %d\n", ret);
117 goto err;
118 }
119
120 err:
121 for (i = 0; i < 3; ++i)
122 if (last[i])
123 i915_request_put(last[i]);
124 intel_runtime_pm_put(gt->uncore->rpm, wakeref);
125
126 return ret;
127 }
128
129 /*
130 * intel_guc_steal_guc_ids - Test to exhaust all guc_ids and then steal one
131 *
132 * This test creates a spinner which is used to block all subsequent submissions
133 * until it completes. Next, a loop creates a context and a NOP request each
134 * iteration until the guc_ids are exhausted (request creation returns -EAGAIN).
135 * The spinner is ended, unblocking all requests created in the loop. At this
136 * point all guc_ids are exhausted but are available to steal. Try to create
137 * another request which should successfully steal a guc_id. Wait on last
138 * request to complete, idle GPU, verify a guc_id was stolen via a counter, and
139 * exit the test. Test also artificially reduces the number of guc_ids so the
140 * test runs in a timely manner.
141 */
intel_guc_steal_guc_ids(void * arg)142 static int intel_guc_steal_guc_ids(void *arg)
143 {
144 struct intel_gt *gt = arg;
145 struct intel_guc *guc = >->uc.guc;
146 int ret, sv, context_index = 0;
147 intel_wakeref_t wakeref;
148 struct intel_engine_cs *engine;
149 struct intel_context **ce;
150 struct igt_spinner spin;
151 struct i915_request *spin_rq = NULL, *rq, *last = NULL;
152 int number_guc_id_stolen = guc->number_guc_id_stolen;
153
154 ce = kcalloc(GUC_MAX_CONTEXT_ID, sizeof(*ce), GFP_KERNEL);
155 if (!ce) {
156 drm_err(>->i915->drm, "Context array allocation failed\n");
157 return -ENOMEM;
158 }
159
160 wakeref = intel_runtime_pm_get(gt->uncore->rpm);
161 engine = intel_selftest_find_any_engine(gt);
162 sv = guc->submission_state.num_guc_ids;
163 guc->submission_state.num_guc_ids = 512;
164
165 /* Create spinner to block requests in below loop */
166 ce[context_index] = intel_context_create(engine);
167 if (IS_ERR(ce[context_index])) {
168 ret = PTR_ERR(ce[context_index]);
169 ce[context_index] = NULL;
170 drm_err(>->i915->drm, "Failed to create context: %d\n", ret);
171 goto err_wakeref;
172 }
173 ret = igt_spinner_init(&spin, engine->gt);
174 if (ret) {
175 drm_err(>->i915->drm, "Failed to create spinner: %d\n", ret);
176 goto err_contexts;
177 }
178 spin_rq = igt_spinner_create_request(&spin, ce[context_index],
179 MI_ARB_CHECK);
180 if (IS_ERR(spin_rq)) {
181 ret = PTR_ERR(spin_rq);
182 drm_err(>->i915->drm, "Failed to create spinner request: %d\n", ret);
183 goto err_contexts;
184 }
185 ret = request_add_spin(spin_rq, &spin);
186 if (ret) {
187 drm_err(>->i915->drm, "Failed to add Spinner request: %d\n", ret);
188 goto err_spin_rq;
189 }
190
191 /* Use all guc_ids */
192 while (ret != -EAGAIN) {
193 ce[++context_index] = intel_context_create(engine);
194 if (IS_ERR(ce[context_index])) {
195 ret = PTR_ERR(ce[context_index--]);
196 ce[context_index] = NULL;
197 drm_err(>->i915->drm, "Failed to create context: %d\n", ret);
198 goto err_spin_rq;
199 }
200
201 rq = nop_user_request(ce[context_index], spin_rq);
202 if (IS_ERR(rq)) {
203 ret = PTR_ERR(rq);
204 rq = NULL;
205 if (ret != -EAGAIN) {
206 drm_err(>->i915->drm, "Failed to create request, %d: %d\n",
207 context_index, ret);
208 goto err_spin_rq;
209 }
210 } else {
211 if (last)
212 i915_request_put(last);
213 last = rq;
214 }
215 }
216
217 /* Release blocked requests */
218 igt_spinner_end(&spin);
219 ret = intel_selftest_wait_for_rq(spin_rq);
220 if (ret) {
221 drm_err(>->i915->drm, "Spin request failed to complete: %d\n", ret);
222 i915_request_put(last);
223 goto err_spin_rq;
224 }
225 i915_request_put(spin_rq);
226 igt_spinner_fini(&spin);
227 spin_rq = NULL;
228
229 /* Wait for last request */
230 ret = i915_request_wait(last, 0, HZ * 30);
231 i915_request_put(last);
232 if (ret < 0) {
233 drm_err(>->i915->drm, "Last request failed to complete: %d\n", ret);
234 goto err_spin_rq;
235 }
236
237 /* Try to steal guc_id */
238 rq = nop_user_request(ce[context_index], NULL);
239 if (IS_ERR(rq)) {
240 ret = PTR_ERR(rq);
241 drm_err(>->i915->drm, "Failed to steal guc_id, %d: %d\n", context_index, ret);
242 goto err_spin_rq;
243 }
244
245 /* Wait for request with stolen guc_id */
246 ret = i915_request_wait(rq, 0, HZ);
247 i915_request_put(rq);
248 if (ret < 0) {
249 drm_err(>->i915->drm, "Request with stolen guc_id failed to complete: %d\n", ret);
250 goto err_spin_rq;
251 }
252
253 /* Wait for idle */
254 ret = intel_gt_wait_for_idle(gt, HZ * 30);
255 if (ret < 0) {
256 drm_err(>->i915->drm, "GT failed to idle: %d\n", ret);
257 goto err_spin_rq;
258 }
259
260 /* Verify a guc_id was stolen */
261 if (guc->number_guc_id_stolen == number_guc_id_stolen) {
262 drm_err(>->i915->drm, "No guc_id was stolen");
263 ret = -EINVAL;
264 } else {
265 ret = 0;
266 }
267
268 err_spin_rq:
269 if (spin_rq) {
270 igt_spinner_end(&spin);
271 intel_selftest_wait_for_rq(spin_rq);
272 i915_request_put(spin_rq);
273 igt_spinner_fini(&spin);
274 intel_gt_wait_for_idle(gt, HZ * 30);
275 }
276 err_contexts:
277 for (; context_index >= 0 && ce[context_index]; --context_index)
278 intel_context_put(ce[context_index]);
279 err_wakeref:
280 intel_runtime_pm_put(gt->uncore->rpm, wakeref);
281 kfree(ce);
282 guc->submission_state.num_guc_ids = sv;
283
284 return ret;
285 }
286
intel_guc_live_selftests(struct drm_i915_private * i915)287 int intel_guc_live_selftests(struct drm_i915_private *i915)
288 {
289 static const struct i915_subtest tests[] = {
290 SUBTEST(intel_guc_scrub_ctbs),
291 SUBTEST(intel_guc_steal_guc_ids),
292 };
293 struct intel_gt *gt = to_gt(i915);
294
295 if (intel_gt_is_wedged(gt))
296 return 0;
297
298 if (!intel_uc_uses_guc_submission(>->uc))
299 return 0;
300
301 return intel_gt_live_subtests(tests, gt);
302 }
303