1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25#include "i915_selftest.h"
26#include "gem/i915_gem_pm.h"
27
28
29#define ATTEMPTS (GUC_NUM_DOORBELLS + GUC_CLIENT_PRIORITY_NUM)
30
31static struct intel_guc_client *clients[ATTEMPTS];
32
33static bool available_dbs(struct intel_guc *guc, u32 priority)
34{
35 unsigned long offset;
36 unsigned long end;
37 u16 id;
38
39
40 offset = 0;
41 end = GUC_NUM_DOORBELLS / 2;
42 if (priority <= GUC_CLIENT_PRIORITY_HIGH) {
43 offset = end;
44 end += offset;
45 }
46
47 id = find_next_zero_bit(guc->doorbell_bitmap, end, offset);
48 if (id < end)
49 return true;
50
51 return false;
52}
53
54static int check_all_doorbells(struct intel_guc *guc)
55{
56 u16 db_id;
57
58 pr_info_once("Max number of doorbells: %d", GUC_NUM_DOORBELLS);
59 for (db_id = 0; db_id < GUC_NUM_DOORBELLS; ++db_id) {
60 if (!doorbell_ok(guc, db_id)) {
61 pr_err("doorbell %d, not ok\n", db_id);
62 return -EIO;
63 }
64 }
65
66 return 0;
67}
68
69static int ring_doorbell_nop(struct intel_guc_client *client)
70{
71 struct guc_process_desc *desc = __get_process_desc(client);
72 int err;
73
74 client->use_nop_wqi = true;
75
76 spin_lock_irq(&client->wq_lock);
77
78 guc_wq_item_append(client, 0, 0, 0, 0);
79 guc_ring_doorbell(client);
80
81 spin_unlock_irq(&client->wq_lock);
82
83 client->use_nop_wqi = false;
84
85
86
87
88 err = wait_for(READ_ONCE(desc->head) == READ_ONCE(desc->tail), 10);
89 if (err) {
90 pr_err("doorbell %u ring failed!\n", client->doorbell_id);
91 return -EIO;
92 }
93
94 if (desc->wq_status != WQ_STATUS_ACTIVE) {
95 pr_err("doorbell %u ring put WQ in bad state (%u)!\n",
96 client->doorbell_id, desc->wq_status);
97 return -EIO;
98 }
99
100 return 0;
101}
102
103
104
105
106static int validate_client(struct intel_guc_client *client,
107 int client_priority,
108 bool is_preempt_client)
109{
110 struct drm_i915_private *dev_priv = guc_to_i915(client->guc);
111 struct i915_gem_context *ctx_owner = is_preempt_client ?
112 dev_priv->preempt_context : dev_priv->kernel_context;
113
114 if (client->owner != ctx_owner ||
115 client->engines != INTEL_INFO(dev_priv)->engine_mask ||
116 client->priority != client_priority ||
117 client->doorbell_id == GUC_DOORBELL_INVALID)
118 return -EINVAL;
119 else
120 return 0;
121}
122
123static bool client_doorbell_in_sync(struct intel_guc_client *client)
124{
125 return !client || doorbell_ok(client->guc, client->doorbell_id);
126}
127
128
129
130
131
132
133
134
135
136
137
138static int igt_guc_clients(void *args)
139{
140 struct drm_i915_private *dev_priv = args;
141 intel_wakeref_t wakeref;
142 struct intel_guc *guc;
143 int err = 0;
144
145 GEM_BUG_ON(!HAS_GUC(dev_priv));
146 mutex_lock(&dev_priv->drm.struct_mutex);
147 wakeref = intel_runtime_pm_get(&dev_priv->runtime_pm);
148
149 guc = &dev_priv->guc;
150 if (!guc) {
151 pr_err("No guc object!\n");
152 err = -EINVAL;
153 goto unlock;
154 }
155
156 err = check_all_doorbells(guc);
157 if (err)
158 goto unlock;
159
160
161
162
163
164 guc_clients_disable(guc);
165 guc_clients_destroy(guc);
166 if (guc->execbuf_client || guc->preempt_client) {
167 pr_err("guc_clients_destroy lied!\n");
168 err = -EINVAL;
169 goto unlock;
170 }
171
172 err = guc_clients_create(guc);
173 if (err) {
174 pr_err("Failed to create clients\n");
175 goto unlock;
176 }
177 GEM_BUG_ON(!guc->execbuf_client);
178
179 err = validate_client(guc->execbuf_client,
180 GUC_CLIENT_PRIORITY_KMD_NORMAL, false);
181 if (err) {
182 pr_err("execbug client validation failed\n");
183 goto out;
184 }
185
186 if (guc->preempt_client) {
187 err = validate_client(guc->preempt_client,
188 GUC_CLIENT_PRIORITY_KMD_HIGH, true);
189 if (err) {
190 pr_err("preempt client validation failed\n");
191 goto out;
192 }
193 }
194
195
196 if (!has_doorbell(guc->execbuf_client) ||
197 (guc->preempt_client && !has_doorbell(guc->preempt_client))) {
198 pr_err("guc_clients_create didn't reserve doorbells\n");
199 err = -EINVAL;
200 goto out;
201 }
202
203
204 guc_clients_enable(guc);
205
206
207 if (!client_doorbell_in_sync(guc->execbuf_client) ||
208 !client_doorbell_in_sync(guc->preempt_client)) {
209 pr_err("failed to initialize the doorbells\n");
210 err = -EINVAL;
211 goto out;
212 }
213
214
215
216
217
218 err = create_doorbell(guc->execbuf_client);
219
220out:
221
222
223
224
225 guc_clients_disable(guc);
226 guc_clients_destroy(guc);
227 guc_clients_create(guc);
228 guc_clients_enable(guc);
229unlock:
230 intel_runtime_pm_put(&dev_priv->runtime_pm, wakeref);
231 mutex_unlock(&dev_priv->drm.struct_mutex);
232 return err;
233}
234
235
236
237
238
239
240static int igt_guc_doorbells(void *arg)
241{
242 struct drm_i915_private *dev_priv = arg;
243 intel_wakeref_t wakeref;
244 struct intel_guc *guc;
245 int i, err = 0;
246 u16 db_id;
247
248 GEM_BUG_ON(!HAS_GUC(dev_priv));
249 mutex_lock(&dev_priv->drm.struct_mutex);
250 wakeref = intel_runtime_pm_get(&dev_priv->runtime_pm);
251
252 guc = &dev_priv->guc;
253 if (!guc) {
254 pr_err("No guc object!\n");
255 err = -EINVAL;
256 goto unlock;
257 }
258
259 err = check_all_doorbells(guc);
260 if (err)
261 goto unlock;
262
263 for (i = 0; i < ATTEMPTS; i++) {
264 clients[i] = guc_client_alloc(dev_priv,
265 INTEL_INFO(dev_priv)->engine_mask,
266 i % GUC_CLIENT_PRIORITY_NUM,
267 dev_priv->kernel_context);
268
269 if (!clients[i]) {
270 pr_err("[%d] No guc client\n", i);
271 err = -EINVAL;
272 goto out;
273 }
274
275 if (IS_ERR(clients[i])) {
276 if (PTR_ERR(clients[i]) != -ENOSPC) {
277 pr_err("[%d] unexpected error\n", i);
278 err = PTR_ERR(clients[i]);
279 goto out;
280 }
281
282 if (available_dbs(guc, i % GUC_CLIENT_PRIORITY_NUM)) {
283 pr_err("[%d] non-db related alloc fail\n", i);
284 err = -EINVAL;
285 goto out;
286 }
287
288
289 continue;
290 }
291
292
293
294
295
296 if (clients[i]->stage_id >= GUC_NUM_DOORBELLS) {
297 pr_err("[%d] more clients than doorbells (%d >= %d)\n",
298 i, clients[i]->stage_id, GUC_NUM_DOORBELLS);
299 err = -EINVAL;
300 goto out;
301 }
302
303 err = validate_client(clients[i],
304 i % GUC_CLIENT_PRIORITY_NUM, false);
305 if (err) {
306 pr_err("[%d] client_alloc sanity check failed!\n", i);
307 err = -EINVAL;
308 goto out;
309 }
310
311 db_id = clients[i]->doorbell_id;
312
313 err = __guc_client_enable(clients[i]);
314 if (err) {
315 pr_err("[%d] Failed to create a doorbell\n", i);
316 goto out;
317 }
318
319
320 if (db_id != clients[i]->doorbell_id) {
321 pr_err("[%d] doorbell id changed (%d != %d)\n",
322 i, db_id, clients[i]->doorbell_id);
323 err = -EINVAL;
324 goto out;
325 }
326
327 err = check_all_doorbells(guc);
328 if (err)
329 goto out;
330
331 err = ring_doorbell_nop(clients[i]);
332 if (err)
333 goto out;
334 }
335
336out:
337 for (i = 0; i < ATTEMPTS; i++)
338 if (!IS_ERR_OR_NULL(clients[i])) {
339 __guc_client_disable(clients[i]);
340 guc_client_free(clients[i]);
341 }
342unlock:
343 intel_runtime_pm_put(&dev_priv->runtime_pm, wakeref);
344 mutex_unlock(&dev_priv->drm.struct_mutex);
345 return err;
346}
347
348int intel_guc_live_selftest(struct drm_i915_private *dev_priv)
349{
350 static const struct i915_subtest tests[] = {
351 SUBTEST(igt_guc_clients),
352 SUBTEST(igt_guc_doorbells),
353 };
354
355 if (!USES_GUC_SUBMISSION(dev_priv))
356 return 0;
357
358 return i915_subtests(tests, dev_priv);
359}
360