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