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