1 // SPDX-License-Identifier: MIT
2 /*
3 * Copyright © 2023-2024 Intel Corporation
4 */
5
6 #include <linux/debugfs.h>
7
8 #include <drm/drm_print.h>
9 #include <drm/drm_debugfs.h>
10
11 #include "xe_bo.h"
12 #include "xe_debugfs.h"
13 #include "xe_device.h"
14 #include "xe_gt.h"
15 #include "xe_gt_debugfs.h"
16 #include "xe_gt_sriov_pf_config.h"
17 #include "xe_gt_sriov_pf_control.h"
18 #include "xe_gt_sriov_pf_debugfs.h"
19 #include "xe_gt_sriov_pf_helpers.h"
20 #include "xe_gt_sriov_pf_migration.h"
21 #include "xe_gt_sriov_pf_monitor.h"
22 #include "xe_gt_sriov_pf_policy.h"
23 #include "xe_gt_sriov_pf_service.h"
24 #include "xe_pm.h"
25 #include "xe_sriov_pf.h"
26
27 /*
28 * /sys/kernel/debug/dri/0/
29 * ├── gt0 # d_inode->i_private = gt
30 * │ ├── pf # d_inode->i_private = gt
31 * │ ├── vf1 # d_inode->i_private = VFID(1)
32 * : :
33 * │ ├── vfN # d_inode->i_private = VFID(N)
34 */
35
extract_priv(struct dentry * d)36 static void *extract_priv(struct dentry *d)
37 {
38 return d->d_inode->i_private;
39 }
40
extract_gt(struct dentry * d)41 static struct xe_gt *extract_gt(struct dentry *d)
42 {
43 return extract_priv(d->d_parent);
44 }
45
extract_vfid(struct dentry * d)46 static unsigned int extract_vfid(struct dentry *d)
47 {
48 return extract_priv(d) == extract_gt(d) ? PFID : (uintptr_t)extract_priv(d);
49 }
50
51 /*
52 * /sys/kernel/debug/dri/0/
53 * ├── gt0
54 * │ ├── pf
55 * │ │ ├── contexts_provisioned
56 * │ │ ├── doorbells_provisioned
57 * │ │ ├── runtime_registers
58 * │ │ ├── negotiated_versions
59 * │ │ ├── adverse_events
60 * ├── gt1
61 * │ ├── pf
62 * │ │ ├── ...
63 */
64
65 static const struct drm_info_list pf_info[] = {
66 {
67 "contexts_provisioned",
68 .show = xe_gt_debugfs_simple_show,
69 .data = xe_gt_sriov_pf_config_print_ctxs,
70 },
71 {
72 "doorbells_provisioned",
73 .show = xe_gt_debugfs_simple_show,
74 .data = xe_gt_sriov_pf_config_print_dbs,
75 },
76 {
77 "runtime_registers",
78 .show = xe_gt_debugfs_simple_show,
79 .data = xe_gt_sriov_pf_service_print_runtime,
80 },
81 {
82 "adverse_events",
83 .show = xe_gt_debugfs_simple_show,
84 .data = xe_gt_sriov_pf_monitor_print_events,
85 },
86 };
87
88 /*
89 * /sys/kernel/debug/dri/0/
90 * ├── gt0
91 * │ ├── pf
92 * │ │ ├── ggtt_available
93 * │ │ ├── ggtt_provisioned
94 */
95
96 static const struct drm_info_list pf_ggtt_info[] = {
97 {
98 "ggtt_available",
99 .show = xe_gt_debugfs_simple_show,
100 .data = xe_gt_sriov_pf_config_print_available_ggtt,
101 },
102 {
103 "ggtt_provisioned",
104 .show = xe_gt_debugfs_simple_show,
105 .data = xe_gt_sriov_pf_config_print_ggtt,
106 },
107 };
108
109 /*
110 * /sys/kernel/debug/dri/0/
111 * ├── gt0
112 * │ ├── pf
113 * │ │ ├── lmem_provisioned
114 */
115
116 static const struct drm_info_list pf_lmem_info[] = {
117 {
118 "lmem_provisioned",
119 .show = xe_gt_debugfs_simple_show,
120 .data = xe_gt_sriov_pf_config_print_lmem,
121 },
122 };
123
124 /*
125 * /sys/kernel/debug/dri/0/
126 * ├── gt0
127 * │ ├── pf
128 * │ │ ├── reset_engine
129 * │ │ ├── sample_period
130 * │ │ ├── sched_if_idle
131 */
132
133 #define DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(POLICY, TYPE, FORMAT) \
134 \
135 static int POLICY##_set(void *data, u64 val) \
136 { \
137 struct xe_gt *gt = extract_gt(data); \
138 struct xe_device *xe = gt_to_xe(gt); \
139 int err; \
140 \
141 if (val > (TYPE)~0ull) \
142 return -EOVERFLOW; \
143 \
144 xe_pm_runtime_get(xe); \
145 err = xe_gt_sriov_pf_policy_set_##POLICY(gt, val); \
146 xe_pm_runtime_put(xe); \
147 \
148 return err; \
149 } \
150 \
151 static int POLICY##_get(void *data, u64 *val) \
152 { \
153 struct xe_gt *gt = extract_gt(data); \
154 \
155 *val = xe_gt_sriov_pf_policy_get_##POLICY(gt); \
156 return 0; \
157 } \
158 \
159 DEFINE_DEBUGFS_ATTRIBUTE(POLICY##_fops, POLICY##_get, POLICY##_set, FORMAT)
160
161 DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(reset_engine, bool, "%llu\n");
162 DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(sched_if_idle, bool, "%llu\n");
163 DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(sample_period, u32, "%llu\n");
164
pf_add_policy_attrs(struct xe_gt * gt,struct dentry * parent)165 static void pf_add_policy_attrs(struct xe_gt *gt, struct dentry *parent)
166 {
167 xe_gt_assert(gt, gt == extract_gt(parent));
168 xe_gt_assert(gt, PFID == extract_vfid(parent));
169
170 debugfs_create_file_unsafe("reset_engine", 0644, parent, parent, &reset_engine_fops);
171 debugfs_create_file_unsafe("sched_if_idle", 0644, parent, parent, &sched_if_idle_fops);
172 debugfs_create_file_unsafe("sample_period_ms", 0644, parent, parent, &sample_period_fops);
173 }
174
175 /*
176 * /sys/kernel/debug/dri/0/
177 * ├── gt0
178 * │ ├── pf
179 * │ │ ├── ggtt_spare
180 * │ │ ├── lmem_spare
181 * │ │ ├── doorbells_spare
182 * │ │ ├── contexts_spare
183 * │ │ ├── exec_quantum_ms
184 * │ │ ├── preempt_timeout_us
185 * │ │ ├── sched_priority
186 * │ ├── vf1
187 * │ │ ├── ggtt_quota
188 * │ │ ├── lmem_quota
189 * │ │ ├── doorbells_quota
190 * │ │ ├── contexts_quota
191 * │ │ ├── exec_quantum_ms
192 * │ │ ├── preempt_timeout_us
193 * │ │ ├── sched_priority
194 */
195
196 #define DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(CONFIG, TYPE, FORMAT) \
197 \
198 static int CONFIG##_set(void *data, u64 val) \
199 { \
200 struct xe_gt *gt = extract_gt(data); \
201 unsigned int vfid = extract_vfid(data); \
202 struct xe_device *xe = gt_to_xe(gt); \
203 int err; \
204 \
205 if (val > (TYPE)~0ull) \
206 return -EOVERFLOW; \
207 \
208 xe_pm_runtime_get(xe); \
209 err = xe_sriov_pf_wait_ready(xe) ?: \
210 xe_gt_sriov_pf_config_set_##CONFIG(gt, vfid, val); \
211 xe_pm_runtime_put(xe); \
212 \
213 return err; \
214 } \
215 \
216 static int CONFIG##_get(void *data, u64 *val) \
217 { \
218 struct xe_gt *gt = extract_gt(data); \
219 unsigned int vfid = extract_vfid(data); \
220 \
221 *val = xe_gt_sriov_pf_config_get_##CONFIG(gt, vfid); \
222 return 0; \
223 } \
224 \
225 DEFINE_DEBUGFS_ATTRIBUTE(CONFIG##_fops, CONFIG##_get, CONFIG##_set, FORMAT)
226
227 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(ggtt, u64, "%llu\n");
228 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(lmem, u64, "%llu\n");
229 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(ctxs, u32, "%llu\n");
230 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(dbs, u32, "%llu\n");
231 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(exec_quantum, u32, "%llu\n");
232 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(preempt_timeout, u32, "%llu\n");
233 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(sched_priority, u32, "%llu\n");
234
235 /*
236 * /sys/kernel/debug/dri/0/
237 * ├── gt0
238 * │ ├── pf
239 * │ │ ├── threshold_cat_error_count
240 * │ │ ├── threshold_doorbell_time_us
241 * │ │ ├── threshold_engine_reset_count
242 * │ │ ├── threshold_guc_time_us
243 * │ │ ├── threshold_irq_time_us
244 * │ │ ├── threshold_page_fault_count
245 * │ ├── vf1
246 * │ │ ├── threshold_cat_error_count
247 * │ │ ├── threshold_doorbell_time_us
248 * │ │ ├── threshold_engine_reset_count
249 * │ │ ├── threshold_guc_time_us
250 * │ │ ├── threshold_irq_time_us
251 * │ │ ├── threshold_page_fault_count
252 */
253
set_threshold(void * data,u64 val,enum xe_guc_klv_threshold_index index)254 static int set_threshold(void *data, u64 val, enum xe_guc_klv_threshold_index index)
255 {
256 struct xe_gt *gt = extract_gt(data);
257 unsigned int vfid = extract_vfid(data);
258 struct xe_device *xe = gt_to_xe(gt);
259 int err;
260
261 if (val > (u32)~0ull)
262 return -EOVERFLOW;
263
264 xe_pm_runtime_get(xe);
265 err = xe_gt_sriov_pf_config_set_threshold(gt, vfid, index, val);
266 xe_pm_runtime_put(xe);
267
268 return err;
269 }
270
get_threshold(void * data,u64 * val,enum xe_guc_klv_threshold_index index)271 static int get_threshold(void *data, u64 *val, enum xe_guc_klv_threshold_index index)
272 {
273 struct xe_gt *gt = extract_gt(data);
274 unsigned int vfid = extract_vfid(data);
275
276 *val = xe_gt_sriov_pf_config_get_threshold(gt, vfid, index);
277 return 0;
278 }
279
280 #define DEFINE_SRIOV_GT_THRESHOLD_DEBUGFS_ATTRIBUTE(THRESHOLD, INDEX) \
281 \
282 static int THRESHOLD##_set(void *data, u64 val) \
283 { \
284 return set_threshold(data, val, INDEX); \
285 } \
286 \
287 static int THRESHOLD##_get(void *data, u64 *val) \
288 { \
289 return get_threshold(data, val, INDEX); \
290 } \
291 \
292 DEFINE_DEBUGFS_ATTRIBUTE(THRESHOLD##_fops, THRESHOLD##_get, THRESHOLD##_set, "%llu\n")
293
294 /* generate all threshold attributes */
295 #define define_threshold_attribute(TAG, NAME, ...) \
296 DEFINE_SRIOV_GT_THRESHOLD_DEBUGFS_ATTRIBUTE(NAME, MAKE_XE_GUC_KLV_THRESHOLD_INDEX(TAG));
MAKE_XE_GUC_KLV_THRESHOLDS_SET(define_threshold_attribute)297 MAKE_XE_GUC_KLV_THRESHOLDS_SET(define_threshold_attribute)
298 #undef define_threshold_attribute
299
300 static void pf_add_config_attrs(struct xe_gt *gt, struct dentry *parent, unsigned int vfid)
301 {
302 xe_gt_assert(gt, gt == extract_gt(parent));
303 xe_gt_assert(gt, vfid == extract_vfid(parent));
304
305 if (xe_gt_is_main_type(gt)) {
306 debugfs_create_file_unsafe(vfid ? "ggtt_quota" : "ggtt_spare",
307 0644, parent, parent, &ggtt_fops);
308 if (xe_device_has_lmtt(gt_to_xe(gt)))
309 debugfs_create_file_unsafe(vfid ? "lmem_quota" : "lmem_spare",
310 0644, parent, parent, &lmem_fops);
311 }
312 debugfs_create_file_unsafe(vfid ? "doorbells_quota" : "doorbells_spare",
313 0644, parent, parent, &dbs_fops);
314 debugfs_create_file_unsafe(vfid ? "contexts_quota" : "contexts_spare",
315 0644, parent, parent, &ctxs_fops);
316 debugfs_create_file_unsafe("exec_quantum_ms", 0644, parent, parent,
317 &exec_quantum_fops);
318 debugfs_create_file_unsafe("preempt_timeout_us", 0644, parent, parent,
319 &preempt_timeout_fops);
320 debugfs_create_file_unsafe("sched_priority", 0644, parent, parent,
321 &sched_priority_fops);
322
323 /* register all threshold attributes */
324 #define register_threshold_attribute(TAG, NAME, ...) \
325 debugfs_create_file_unsafe("threshold_" #NAME, 0644, parent, parent, \
326 &NAME##_fops);
327 MAKE_XE_GUC_KLV_THRESHOLDS_SET(register_threshold_attribute)
328 #undef register_threshold_attribute
329 }
330
331 /*
332 * /sys/kernel/debug/dri/0/
333 * ├── gt0
334 * │ ├── vf1
335 * │ │ ├── control { stop, pause, resume }
336 */
337
338 static const struct {
339 const char *cmd;
340 int (*fn)(struct xe_gt *gt, unsigned int vfid);
341 } control_cmds[] = {
342 { "stop", xe_gt_sriov_pf_control_stop_vf },
343 { "pause", xe_gt_sriov_pf_control_pause_vf },
344 { "resume", xe_gt_sriov_pf_control_resume_vf },
345 #ifdef CONFIG_DRM_XE_DEBUG_SRIOV
346 { "restore!", xe_gt_sriov_pf_migration_restore_guc_state },
347 #endif
348 };
349
control_write(struct file * file,const char __user * buf,size_t count,loff_t * pos)350 static ssize_t control_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
351 {
352 struct dentry *dent = file_dentry(file);
353 struct dentry *parent = dent->d_parent;
354 struct xe_gt *gt = extract_gt(parent);
355 struct xe_device *xe = gt_to_xe(gt);
356 unsigned int vfid = extract_vfid(parent);
357 int ret = -EINVAL;
358 char cmd[32];
359 size_t n;
360
361 xe_gt_assert(gt, vfid);
362 xe_gt_sriov_pf_assert_vfid(gt, vfid);
363
364 if (*pos)
365 return -ESPIPE;
366
367 if (count > sizeof(cmd) - 1)
368 return -EINVAL;
369
370 ret = simple_write_to_buffer(cmd, sizeof(cmd) - 1, pos, buf, count);
371 if (ret < 0)
372 return ret;
373 cmd[ret] = '\0';
374
375 for (n = 0; n < ARRAY_SIZE(control_cmds); n++) {
376 xe_gt_assert(gt, sizeof(cmd) > strlen(control_cmds[n].cmd));
377
378 if (sysfs_streq(cmd, control_cmds[n].cmd)) {
379 xe_pm_runtime_get(xe);
380 ret = control_cmds[n].fn ? (*control_cmds[n].fn)(gt, vfid) : 0;
381 xe_pm_runtime_put(xe);
382 break;
383 }
384 }
385
386 return (ret < 0) ? ret : count;
387 }
388
control_read(struct file * file,char __user * buf,size_t count,loff_t * ppos)389 static ssize_t control_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
390 {
391 char help[128];
392 size_t n;
393
394 help[0] = '\0';
395 for (n = 0; n < ARRAY_SIZE(control_cmds); n++) {
396 strlcat(help, control_cmds[n].cmd, sizeof(help));
397 strlcat(help, "\n", sizeof(help));
398 }
399
400 return simple_read_from_buffer(buf, count, ppos, help, strlen(help));
401 }
402
403 static const struct file_operations control_ops = {
404 .owner = THIS_MODULE,
405 .open = simple_open,
406 .write = control_write,
407 .read = control_read,
408 .llseek = default_llseek,
409 };
410
411 /*
412 * /sys/kernel/debug/dri/0/
413 * ├── gt0
414 * │ ├── vf1
415 * │ │ ├── guc_state
416 */
guc_state_read(struct file * file,char __user * buf,size_t count,loff_t * pos)417 static ssize_t guc_state_read(struct file *file, char __user *buf,
418 size_t count, loff_t *pos)
419 {
420 struct dentry *dent = file_dentry(file);
421 struct dentry *parent = dent->d_parent;
422 struct xe_gt *gt = extract_gt(parent);
423 unsigned int vfid = extract_vfid(parent);
424
425 return xe_gt_sriov_pf_migration_read_guc_state(gt, vfid, buf, count, pos);
426 }
427
guc_state_write(struct file * file,const char __user * buf,size_t count,loff_t * pos)428 static ssize_t guc_state_write(struct file *file, const char __user *buf,
429 size_t count, loff_t *pos)
430 {
431 struct dentry *dent = file_dentry(file);
432 struct dentry *parent = dent->d_parent;
433 struct xe_gt *gt = extract_gt(parent);
434 unsigned int vfid = extract_vfid(parent);
435
436 if (*pos)
437 return -EINVAL;
438
439 return xe_gt_sriov_pf_migration_write_guc_state(gt, vfid, buf, count);
440 }
441
442 static const struct file_operations guc_state_ops = {
443 .owner = THIS_MODULE,
444 .read = guc_state_read,
445 .write = guc_state_write,
446 .llseek = default_llseek,
447 };
448
449 /*
450 * /sys/kernel/debug/dri/0/
451 * ├── gt0
452 * │ ├── vf1
453 * │ │ ├── config_blob
454 */
config_blob_read(struct file * file,char __user * buf,size_t count,loff_t * pos)455 static ssize_t config_blob_read(struct file *file, char __user *buf,
456 size_t count, loff_t *pos)
457 {
458 struct dentry *dent = file_dentry(file);
459 struct dentry *parent = dent->d_parent;
460 struct xe_gt *gt = extract_gt(parent);
461 unsigned int vfid = extract_vfid(parent);
462 ssize_t ret;
463 void *tmp;
464
465 ret = xe_gt_sriov_pf_config_save(gt, vfid, NULL, 0);
466 if (!ret)
467 return -ENODATA;
468 if (ret < 0)
469 return ret;
470
471 tmp = kzalloc(ret, GFP_KERNEL);
472 if (!tmp)
473 return -ENOMEM;
474
475 ret = xe_gt_sriov_pf_config_save(gt, vfid, tmp, ret);
476 if (ret > 0)
477 ret = simple_read_from_buffer(buf, count, pos, tmp, ret);
478
479 kfree(tmp);
480 return ret;
481 }
482
config_blob_write(struct file * file,const char __user * buf,size_t count,loff_t * pos)483 static ssize_t config_blob_write(struct file *file, const char __user *buf,
484 size_t count, loff_t *pos)
485 {
486 struct dentry *dent = file_dentry(file);
487 struct dentry *parent = dent->d_parent;
488 struct xe_gt *gt = extract_gt(parent);
489 unsigned int vfid = extract_vfid(parent);
490 ssize_t ret;
491 void *tmp;
492
493 if (*pos)
494 return -EINVAL;
495
496 if (!count)
497 return -ENODATA;
498
499 if (count > SZ_4K)
500 return -EINVAL;
501
502 tmp = kzalloc(count, GFP_KERNEL);
503 if (!tmp)
504 return -ENOMEM;
505
506 if (copy_from_user(tmp, buf, count)) {
507 ret = -EFAULT;
508 } else {
509 ret = xe_gt_sriov_pf_config_restore(gt, vfid, tmp, count);
510 if (!ret)
511 ret = count;
512 }
513 kfree(tmp);
514 return ret;
515 }
516
517 static const struct file_operations config_blob_ops = {
518 .owner = THIS_MODULE,
519 .read = config_blob_read,
520 .write = config_blob_write,
521 .llseek = default_llseek,
522 };
523
524 /**
525 * xe_gt_sriov_pf_debugfs_register - Register SR-IOV PF specific entries in GT debugfs.
526 * @gt: the &xe_gt to register
527 * @root: the &dentry that represents the GT directory
528 *
529 * Register SR-IOV PF entries that are GT related and must be shown under GT debugfs.
530 */
xe_gt_sriov_pf_debugfs_register(struct xe_gt * gt,struct dentry * root)531 void xe_gt_sriov_pf_debugfs_register(struct xe_gt *gt, struct dentry *root)
532 {
533 struct xe_device *xe = gt_to_xe(gt);
534 struct drm_minor *minor = xe->drm.primary;
535 int n, totalvfs = xe_sriov_pf_get_totalvfs(xe);
536 struct dentry *pfdentry;
537 struct dentry *vfdentry;
538 char buf[14]; /* should be enough up to "vf%u\0" for 2^32 - 1 */
539
540 xe_gt_assert(gt, IS_SRIOV_PF(xe));
541 xe_gt_assert(gt, root->d_inode->i_private == gt);
542
543 /*
544 * /sys/kernel/debug/dri/0/
545 * ├── gt0
546 * │ ├── pf
547 */
548 pfdentry = debugfs_create_dir("pf", root);
549 if (IS_ERR(pfdentry))
550 return;
551 pfdentry->d_inode->i_private = gt;
552
553 drm_debugfs_create_files(pf_info, ARRAY_SIZE(pf_info), pfdentry, minor);
554 if (xe_gt_is_main_type(gt)) {
555 drm_debugfs_create_files(pf_ggtt_info,
556 ARRAY_SIZE(pf_ggtt_info),
557 pfdentry, minor);
558 if (xe_device_has_lmtt(gt_to_xe(gt)))
559 drm_debugfs_create_files(pf_lmem_info,
560 ARRAY_SIZE(pf_lmem_info),
561 pfdentry, minor);
562 }
563
564 pf_add_policy_attrs(gt, pfdentry);
565 pf_add_config_attrs(gt, pfdentry, PFID);
566
567 for (n = 1; n <= totalvfs; n++) {
568 /*
569 * /sys/kernel/debug/dri/0/
570 * ├── gt0
571 * │ ├── vf1
572 * │ ├── vf2
573 */
574 snprintf(buf, sizeof(buf), "vf%u", n);
575 vfdentry = debugfs_create_dir(buf, root);
576 if (IS_ERR(vfdentry))
577 break;
578 vfdentry->d_inode->i_private = (void *)(uintptr_t)n;
579
580 pf_add_config_attrs(gt, vfdentry, VFID(n));
581 debugfs_create_file("control", 0600, vfdentry, NULL, &control_ops);
582
583 /* for testing/debugging purposes only! */
584 if (IS_ENABLED(CONFIG_DRM_XE_DEBUG)) {
585 debugfs_create_file("guc_state",
586 IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV) ? 0600 : 0400,
587 vfdentry, NULL, &guc_state_ops);
588 debugfs_create_file("config_blob",
589 IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV) ? 0600 : 0400,
590 vfdentry, NULL, &config_blob_ops);
591 }
592 }
593 }
594