xref: /linux/drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.c (revision d53b8e36925256097a08d7cb749198d85cbf9b2b)
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_monitor.h"
21 #include "xe_gt_sriov_pf_policy.h"
22 #include "xe_gt_sriov_pf_service.h"
23 #include "xe_pm.h"
24 
25 /*
26  *      /sys/kernel/debug/dri/0/
27  *      ├── gt0		# d_inode->i_private = gt
28  *      │   ├── pf	# d_inode->i_private = gt
29  *      │   ├── vf1	# d_inode->i_private = VFID(1)
30  *      :   :
31  *      │   ├── vfN	# d_inode->i_private = VFID(N)
32  */
33 
34 static void *extract_priv(struct dentry *d)
35 {
36 	return d->d_inode->i_private;
37 }
38 
39 static struct xe_gt *extract_gt(struct dentry *d)
40 {
41 	return extract_priv(d->d_parent);
42 }
43 
44 static unsigned int extract_vfid(struct dentry *d)
45 {
46 	return extract_priv(d) == extract_gt(d) ? PFID : (uintptr_t)extract_priv(d);
47 }
48 
49 /*
50  *      /sys/kernel/debug/dri/0/
51  *      ├── gt0
52  *      │   ├── pf
53  *      │   │   ├── ggtt_available
54  *      │   │   ├── ggtt_provisioned
55  *      │   │   ├── contexts_provisioned
56  *      │   │   ├── doorbells_provisioned
57  *      │   │   ├── runtime_registers
58  *      │   │   ├── negotiated_versions
59  *      │   │   ├── adverse_events
60  */
61 
62 static const struct drm_info_list pf_info[] = {
63 	{
64 		"ggtt_available",
65 		.show = xe_gt_debugfs_simple_show,
66 		.data = xe_gt_sriov_pf_config_print_available_ggtt,
67 	},
68 	{
69 		"ggtt_provisioned",
70 		.show = xe_gt_debugfs_simple_show,
71 		.data = xe_gt_sriov_pf_config_print_ggtt,
72 	},
73 	{
74 		"contexts_provisioned",
75 		.show = xe_gt_debugfs_simple_show,
76 		.data = xe_gt_sriov_pf_config_print_ctxs,
77 	},
78 	{
79 		"doorbells_provisioned",
80 		.show = xe_gt_debugfs_simple_show,
81 		.data = xe_gt_sriov_pf_config_print_dbs,
82 	},
83 	{
84 		"runtime_registers",
85 		.show = xe_gt_debugfs_simple_show,
86 		.data = xe_gt_sriov_pf_service_print_runtime,
87 	},
88 	{
89 		"negotiated_versions",
90 		.show = xe_gt_debugfs_simple_show,
91 		.data = xe_gt_sriov_pf_service_print_version,
92 	},
93 	{
94 		"adverse_events",
95 		.show = xe_gt_debugfs_simple_show,
96 		.data = xe_gt_sriov_pf_monitor_print_events,
97 	},
98 };
99 
100 /*
101  *      /sys/kernel/debug/dri/0/
102  *      ├── gt0
103  *      │   ├── pf
104  *      │   │   ├── reset_engine
105  *      │   │   ├── sample_period
106  *      │   │   ├── sched_if_idle
107  */
108 
109 #define DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(POLICY, TYPE, FORMAT)		\
110 										\
111 static int POLICY##_set(void *data, u64 val)					\
112 {										\
113 	struct xe_gt *gt = extract_gt(data);					\
114 	struct xe_device *xe = gt_to_xe(gt);					\
115 	int err;								\
116 										\
117 	if (val > (TYPE)~0ull)							\
118 		return -EOVERFLOW;						\
119 										\
120 	xe_pm_runtime_get(xe);							\
121 	err = xe_gt_sriov_pf_policy_set_##POLICY(gt, val);			\
122 	xe_pm_runtime_put(xe);							\
123 										\
124 	return err;								\
125 }										\
126 										\
127 static int POLICY##_get(void *data, u64 *val)					\
128 {										\
129 	struct xe_gt *gt = extract_gt(data);					\
130 										\
131 	*val = xe_gt_sriov_pf_policy_get_##POLICY(gt);				\
132 	return 0;								\
133 }										\
134 										\
135 DEFINE_DEBUGFS_ATTRIBUTE(POLICY##_fops, POLICY##_get, POLICY##_set, FORMAT)
136 
137 DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(reset_engine, bool, "%llu\n");
138 DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(sched_if_idle, bool, "%llu\n");
139 DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(sample_period, u32, "%llu\n");
140 
141 static void pf_add_policy_attrs(struct xe_gt *gt, struct dentry *parent)
142 {
143 	xe_gt_assert(gt, gt == extract_gt(parent));
144 	xe_gt_assert(gt, PFID == extract_vfid(parent));
145 
146 	debugfs_create_file_unsafe("reset_engine", 0644, parent, parent, &reset_engine_fops);
147 	debugfs_create_file_unsafe("sched_if_idle", 0644, parent, parent, &sched_if_idle_fops);
148 	debugfs_create_file_unsafe("sample_period_ms", 0644, parent, parent, &sample_period_fops);
149 }
150 
151 /*
152  *      /sys/kernel/debug/dri/0/
153  *      ├── gt0
154  *      │   ├── pf
155  *      │   │   ├── ggtt_spare
156  *      │   │   ├── lmem_spare
157  *      │   │   ├── doorbells_spare
158  *      │   │   ├── contexts_spare
159  *      │   │   ├── exec_quantum_ms
160  *      │   │   ├── preempt_timeout_us
161  *      │   ├── vf1
162  *      │   │   ├── ggtt_quota
163  *      │   │   ├── lmem_quota
164  *      │   │   ├── doorbells_quota
165  *      │   │   ├── contexts_quota
166  *      │   │   ├── exec_quantum_ms
167  *      │   │   ├── preempt_timeout_us
168  */
169 
170 #define DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(CONFIG, TYPE, FORMAT)		\
171 										\
172 static int CONFIG##_set(void *data, u64 val)					\
173 {										\
174 	struct xe_gt *gt = extract_gt(data);					\
175 	unsigned int vfid = extract_vfid(data);					\
176 	struct xe_device *xe = gt_to_xe(gt);					\
177 	int err;								\
178 										\
179 	if (val > (TYPE)~0ull)							\
180 		return -EOVERFLOW;						\
181 										\
182 	xe_pm_runtime_get(xe);							\
183 	err = xe_gt_sriov_pf_config_set_##CONFIG(gt, vfid, val);		\
184 	xe_pm_runtime_put(xe);							\
185 										\
186 	return err;								\
187 }										\
188 										\
189 static int CONFIG##_get(void *data, u64 *val)					\
190 {										\
191 	struct xe_gt *gt = extract_gt(data);					\
192 	unsigned int vfid = extract_vfid(data);					\
193 										\
194 	*val = xe_gt_sriov_pf_config_get_##CONFIG(gt, vfid);			\
195 	return 0;								\
196 }										\
197 										\
198 DEFINE_DEBUGFS_ATTRIBUTE(CONFIG##_fops, CONFIG##_get, CONFIG##_set, FORMAT)
199 
200 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(ggtt, u64, "%llu\n");
201 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(lmem, u64, "%llu\n");
202 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(ctxs, u32, "%llu\n");
203 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(dbs, u32, "%llu\n");
204 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(exec_quantum, u32, "%llu\n");
205 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(preempt_timeout, u32, "%llu\n");
206 
207 /*
208  *      /sys/kernel/debug/dri/0/
209  *      ├── gt0
210  *      │   ├── pf
211  *      │   │   ├── threshold_cat_error_count
212  *      │   │   ├── threshold_doorbell_time_us
213  *      │   │   ├── threshold_engine_reset_count
214  *      │   │   ├── threshold_guc_time_us
215  *      │   │   ├── threshold_irq_time_us
216  *      │   │   ├── threshold_page_fault_count
217  *      │   ├── vf1
218  *      │   │   ├── threshold_cat_error_count
219  *      │   │   ├── threshold_doorbell_time_us
220  *      │   │   ├── threshold_engine_reset_count
221  *      │   │   ├── threshold_guc_time_us
222  *      │   │   ├── threshold_irq_time_us
223  *      │   │   ├── threshold_page_fault_count
224  */
225 
226 static int set_threshold(void *data, u64 val, enum xe_guc_klv_threshold_index index)
227 {
228 	struct xe_gt *gt = extract_gt(data);
229 	unsigned int vfid = extract_vfid(data);
230 	struct xe_device *xe = gt_to_xe(gt);
231 	int err;
232 
233 	if (val > (u32)~0ull)
234 		return -EOVERFLOW;
235 
236 	xe_pm_runtime_get(xe);
237 	err = xe_gt_sriov_pf_config_set_threshold(gt, vfid, index, val);
238 	xe_pm_runtime_put(xe);
239 
240 	return err;
241 }
242 
243 static int get_threshold(void *data, u64 *val, enum xe_guc_klv_threshold_index index)
244 {
245 	struct xe_gt *gt = extract_gt(data);
246 	unsigned int vfid = extract_vfid(data);
247 
248 	*val = xe_gt_sriov_pf_config_get_threshold(gt, vfid, index);
249 	return 0;
250 }
251 
252 #define DEFINE_SRIOV_GT_THRESHOLD_DEBUGFS_ATTRIBUTE(THRESHOLD, INDEX)		\
253 										\
254 static int THRESHOLD##_set(void *data, u64 val)					\
255 {										\
256 	return set_threshold(data, val, INDEX);					\
257 }										\
258 										\
259 static int THRESHOLD##_get(void *data, u64 *val)				\
260 {										\
261 	return get_threshold(data, val, INDEX);					\
262 }										\
263 										\
264 DEFINE_DEBUGFS_ATTRIBUTE(THRESHOLD##_fops, THRESHOLD##_get, THRESHOLD##_set, "%llu\n")
265 
266 /* generate all threshold attributes */
267 #define define_threshold_attribute(TAG, NAME, ...) \
268 	DEFINE_SRIOV_GT_THRESHOLD_DEBUGFS_ATTRIBUTE(NAME, MAKE_XE_GUC_KLV_THRESHOLD_INDEX(TAG));
269 MAKE_XE_GUC_KLV_THRESHOLDS_SET(define_threshold_attribute)
270 #undef define_threshold_attribute
271 
272 static void pf_add_config_attrs(struct xe_gt *gt, struct dentry *parent, unsigned int vfid)
273 {
274 	xe_gt_assert(gt, gt == extract_gt(parent));
275 	xe_gt_assert(gt, vfid == extract_vfid(parent));
276 
277 	if (!xe_gt_is_media_type(gt)) {
278 		debugfs_create_file_unsafe(vfid ? "ggtt_quota" : "ggtt_spare",
279 					   0644, parent, parent, &ggtt_fops);
280 		if (IS_DGFX(gt_to_xe(gt)))
281 			debugfs_create_file_unsafe(vfid ? "lmem_quota" : "lmem_spare",
282 						   0644, parent, parent, &lmem_fops);
283 	}
284 	debugfs_create_file_unsafe(vfid ? "doorbells_quota" : "doorbells_spare",
285 				   0644, parent, parent, &dbs_fops);
286 	debugfs_create_file_unsafe(vfid ? "contexts_quota" : "contexts_spare",
287 				   0644, parent, parent, &ctxs_fops);
288 	debugfs_create_file_unsafe("exec_quantum_ms", 0644, parent, parent,
289 				   &exec_quantum_fops);
290 	debugfs_create_file_unsafe("preempt_timeout_us", 0644, parent, parent,
291 				   &preempt_timeout_fops);
292 
293 	/* register all threshold attributes */
294 #define register_threshold_attribute(TAG, NAME, ...) \
295 	debugfs_create_file_unsafe("threshold_" #NAME, 0644, parent, parent, \
296 				   &NAME##_fops);
297 	MAKE_XE_GUC_KLV_THRESHOLDS_SET(register_threshold_attribute)
298 #undef register_threshold_attribute
299 }
300 
301 /*
302  *      /sys/kernel/debug/dri/0/
303  *      ├── gt0
304  *      │   ├── vf1
305  *      │   │   ├── control { stop, pause, resume }
306  */
307 
308 static const struct {
309 	const char *cmd;
310 	int (*fn)(struct xe_gt *gt, unsigned int vfid);
311 } control_cmds[] = {
312 	{ "stop", xe_gt_sriov_pf_control_stop_vf },
313 	{ "pause", xe_gt_sriov_pf_control_pause_vf },
314 	{ "resume", xe_gt_sriov_pf_control_resume_vf },
315 };
316 
317 static ssize_t control_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
318 {
319 	struct dentry *dent = file_dentry(file);
320 	struct dentry *parent = dent->d_parent;
321 	struct xe_gt *gt = extract_gt(parent);
322 	struct xe_device *xe = gt_to_xe(gt);
323 	unsigned int vfid = extract_vfid(parent);
324 	int ret = -EINVAL;
325 	char cmd[32];
326 	size_t n;
327 
328 	xe_gt_assert(gt, vfid);
329 	xe_gt_sriov_pf_assert_vfid(gt, vfid);
330 
331 	if (*pos)
332 		return -ESPIPE;
333 
334 	if (count > sizeof(cmd) - 1)
335 		return -EINVAL;
336 
337 	ret = simple_write_to_buffer(cmd, sizeof(cmd) - 1, pos, buf, count);
338 	if (ret < 0)
339 		return ret;
340 	cmd[ret] = '\0';
341 
342 	for (n = 0; n < ARRAY_SIZE(control_cmds); n++) {
343 		xe_gt_assert(gt, sizeof(cmd) > strlen(control_cmds[n].cmd));
344 
345 		if (sysfs_streq(cmd, control_cmds[n].cmd)) {
346 			xe_pm_runtime_get(xe);
347 			ret = control_cmds[n].fn ? (*control_cmds[n].fn)(gt, vfid) : 0;
348 			xe_pm_runtime_put(xe);
349 			break;
350 		}
351 	}
352 
353 	return (ret < 0) ? ret : count;
354 }
355 
356 static ssize_t control_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
357 {
358 	char help[128];
359 	size_t n;
360 
361 	help[0] = '\0';
362 	for (n = 0; n < ARRAY_SIZE(control_cmds); n++) {
363 		strlcat(help, control_cmds[n].cmd, sizeof(help));
364 		strlcat(help, "\n", sizeof(help));
365 	}
366 
367 	return simple_read_from_buffer(buf, count, ppos, help, strlen(help));
368 }
369 
370 static const struct file_operations control_ops = {
371 	.owner		= THIS_MODULE,
372 	.open		= simple_open,
373 	.write		= control_write,
374 	.read		= control_read,
375 	.llseek		= default_llseek,
376 };
377 
378 /**
379  * xe_gt_sriov_pf_debugfs_register - Register SR-IOV PF specific entries in GT debugfs.
380  * @gt: the &xe_gt to register
381  * @root: the &dentry that represents the GT directory
382  *
383  * Register SR-IOV PF entries that are GT related and must be shown under GT debugfs.
384  */
385 void xe_gt_sriov_pf_debugfs_register(struct xe_gt *gt, struct dentry *root)
386 {
387 	struct xe_device *xe = gt_to_xe(gt);
388 	struct drm_minor *minor = xe->drm.primary;
389 	int n, totalvfs = xe_sriov_pf_get_totalvfs(xe);
390 	struct dentry *pfdentry;
391 	struct dentry *vfdentry;
392 	char buf[14]; /* should be enough up to "vf%u\0" for 2^32 - 1 */
393 
394 	xe_gt_assert(gt, IS_SRIOV_PF(xe));
395 	xe_gt_assert(gt, root->d_inode->i_private == gt);
396 
397 	/*
398 	 *      /sys/kernel/debug/dri/0/
399 	 *      ├── gt0
400 	 *      │   ├── pf
401 	 */
402 	pfdentry = debugfs_create_dir("pf", root);
403 	if (IS_ERR(pfdentry))
404 		return;
405 	pfdentry->d_inode->i_private = gt;
406 
407 	drm_debugfs_create_files(pf_info, ARRAY_SIZE(pf_info), pfdentry, minor);
408 	pf_add_policy_attrs(gt, pfdentry);
409 	pf_add_config_attrs(gt, pfdentry, PFID);
410 
411 	for (n = 1; n <= totalvfs; n++) {
412 		/*
413 		 *      /sys/kernel/debug/dri/0/
414 		 *      ├── gt0
415 		 *      │   ├── vf1
416 		 *      │   ├── vf2
417 		 */
418 		snprintf(buf, sizeof(buf), "vf%u", n);
419 		vfdentry = debugfs_create_dir(buf, root);
420 		if (IS_ERR(vfdentry))
421 			break;
422 		vfdentry->d_inode->i_private = (void *)(uintptr_t)n;
423 
424 		pf_add_config_attrs(gt, vfdentry, VFID(n));
425 		debugfs_create_file("control", 0600, vfdentry, NULL, &control_ops);
426 	}
427 }
428