File Index
Files and directories
Sign in
FILE

xe_sriov_pf_sysfs.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/xe/xe_sriov_pf_sysfs.c C
  1// SPDX-License-Identifier: MIT
  2/*
  3 * Copyright © 2025 Intel Corporation
  4 */
  5
  6#include <linux/kobject.h>
  7#include <linux/sysfs.h>
  8
  9#include <drm/drm_managed.h>
 10
 11#include "xe_assert.h"
 12#include "xe_pci_sriov.h"
 13#include "xe_pm.h"
 14#include "xe_sriov.h"
 15#include "xe_sriov_pf.h"
 16#include "xe_sriov_pf_control.h"
 17#include "xe_sriov_pf_helpers.h"
 18#include "xe_sriov_pf_provision.h"
 19#include "xe_sriov_pf_sysfs.h"
 20#include "xe_sriov_printk.h"
 21
 22static int emit_choice(char *buf, int choice, const char * const *array, size_t size)
 23{
 24	int pos = 0;
 25	int n;
 26
 27	for (n = 0; n < size; n++) {
 28		pos += sysfs_emit_at(buf, pos, "%s%s%s%s",
 29				    n ? " " : "",
 30				    n == choice ? "[" : "",
 31				    array[n],
 32				    n == choice ? "]" : "");
 33	}
 34	pos += sysfs_emit_at(buf, pos, "\n");
 35
 36	return pos;
 37}
 38
 39/*
 40 * /sys/bus/pci/drivers/xe/BDF/
 41 * :
 42 * ├── sriov_admin/
 43 *     ├── ...
 44 *     ├── .bulk_profile
 45 *     │   ├── exec_quantum_ms
 46 *     │   ├── preempt_timeout_us
 47 *     │   └── sched_priority
 48 *     ├── pf/
 49 *     │   ├── ...
 50 *     │   ├── device -> ../../../BDF
 51 *     │   └── profile
 52 *     │       ├── exec_quantum_ms
 53 *     │       ├── preempt_timeout_us
 54 *     │       └── sched_priority
 55 *     ├── vf1/
 56 *     │   ├── ...
 57 *     │   ├── device -> ../../../BDF.1
 58 *     │   ├── stop
 59 *     │   └── profile
 60 *     │       ├── exec_quantum_ms
 61 *     │       ├── preempt_timeout_us
 62 *     │       └── sched_priority
 63 *     ├── vf2/
 64 *     :
 65 *     └── vfN/
 66 */
 67
 68struct xe_sriov_kobj {
 69	struct kobject base;
 70	struct xe_device *xe;
 71	unsigned int vfid;
 72};
 73#define to_xe_sriov_kobj(p) container_of_const((p), struct xe_sriov_kobj, base)
 74
 75struct xe_sriov_dev_attr {
 76	struct attribute attr;
 77	ssize_t (*show)(struct xe_device *xe, char *buf);
 78	ssize_t (*store)(struct xe_device *xe, const char *buf, size_t count);
 79};
 80#define to_xe_sriov_dev_attr(p) container_of_const((p), struct xe_sriov_dev_attr, attr)
 81
 82#define XE_SRIOV_DEV_ATTR(NAME) \
 83struct xe_sriov_dev_attr xe_sriov_dev_attr_##NAME = \
 84	__ATTR(NAME, 0644, xe_sriov_dev_attr_##NAME##_show, xe_sriov_dev_attr_##NAME##_store)
 85
 86#define XE_SRIOV_DEV_ATTR_RO(NAME) \
 87struct xe_sriov_dev_attr xe_sriov_dev_attr_##NAME = \
 88	__ATTR(NAME, 0444, xe_sriov_dev_attr_##NAME##_show, NULL)
 89
 90#define XE_SRIOV_DEV_ATTR_WO(NAME) \
 91struct xe_sriov_dev_attr xe_sriov_dev_attr_##NAME = \
 92	__ATTR(NAME, 0200, NULL, xe_sriov_dev_attr_##NAME##_store)
 93
 94struct xe_sriov_vf_attr {
 95	struct attribute attr;
 96	ssize_t (*show)(struct xe_device *xe, unsigned int vfid, char *buf);
 97	ssize_t (*store)(struct xe_device *xe, unsigned int vfid, const char *buf, size_t count);
 98};
 99#define to_xe_sriov_vf_attr(p) container_of_const((p), struct xe_sriov_vf_attr, attr)
100
101#define XE_SRIOV_VF_ATTR(NAME) \
102struct xe_sriov_vf_attr xe_sriov_vf_attr_##NAME = \
103	__ATTR(NAME, 0644, xe_sriov_vf_attr_##NAME##_show, xe_sriov_vf_attr_##NAME##_store)
104
105#define XE_SRIOV_VF_ATTR_RO(NAME) \
106struct xe_sriov_vf_attr xe_sriov_vf_attr_##NAME = \
107	__ATTR(NAME, 0444, xe_sriov_vf_attr_##NAME##_show, NULL)
108
109#define XE_SRIOV_VF_ATTR_WO(NAME) \
110struct xe_sriov_vf_attr xe_sriov_vf_attr_##NAME = \
111	__ATTR(NAME, 0200, NULL, xe_sriov_vf_attr_##NAME##_store)
112
113/* device level attributes go here */
114
115#define DEFINE_SIMPLE_BULK_PROVISIONING_SRIOV_DEV_ATTR_WO(NAME, ITEM, TYPE)		\
116											\
117static ssize_t xe_sriov_dev_attr_##NAME##_store(struct xe_device *xe,			\
118						const char *buf, size_t count)		\
119{											\
120	TYPE value;									\
121	int err;									\
122											\
123	err = kstrto##TYPE(buf, 0, &value);						\
124	if (err)									\
125		return err;								\
126											\
127	err = xe_sriov_pf_provision_bulk_apply_##ITEM(xe, value);			\
128	return err ?: count;								\
129}											\
130											\
131static XE_SRIOV_DEV_ATTR_WO(NAME)
132
133DEFINE_SIMPLE_BULK_PROVISIONING_SRIOV_DEV_ATTR_WO(exec_quantum_ms, eq, u32);
134DEFINE_SIMPLE_BULK_PROVISIONING_SRIOV_DEV_ATTR_WO(preempt_timeout_us, pt, u32);
135
136static const char * const sched_priority_names[] = {
137	[GUC_SCHED_PRIORITY_LOW] = "low",
138	[GUC_SCHED_PRIORITY_NORMAL] = "normal",
139	[GUC_SCHED_PRIORITY_HIGH] = "high",
140};
141
142static bool sched_priority_change_allowed(unsigned int vfid)
143{
144	/* As of today GuC FW allows to selectively change only the PF priority. */
145	return vfid == PFID;
146}
147
148static bool sched_priority_high_allowed(unsigned int vfid)
149{
150	/* As of today GuC FW allows to select 'high' priority only for the PF. */
151	return vfid == PFID;
152}
153
154static bool sched_priority_bulk_high_allowed(struct xe_device *xe)
155{
156	/* all VFs are equal - it's sufficient to check VF1 only */
157	return sched_priority_high_allowed(VFID(1));
158}
159
160static ssize_t xe_sriov_dev_attr_sched_priority_store(struct xe_device *xe,
161						      const char *buf, size_t count)
162{
163	size_t num_priorities = ARRAY_SIZE(sched_priority_names);
164	int match;
165	int err;
166
167	if (!sched_priority_bulk_high_allowed(xe))
168		num_priorities--;
169
170	match = __sysfs_match_string(sched_priority_names, num_priorities, buf);
171	if (match < 0)
172		return -EINVAL;
173
174	err = xe_sriov_pf_provision_bulk_apply_priority(xe, match);
175	return err ?: count;
176}
177
178static XE_SRIOV_DEV_ATTR_WO(sched_priority);
179
180static struct attribute *bulk_profile_dev_attrs[] = {
181	&xe_sriov_dev_attr_exec_quantum_ms.attr,
182	&xe_sriov_dev_attr_preempt_timeout_us.attr,
183	&xe_sriov_dev_attr_sched_priority.attr,
184	NULL
185};
186
187static const struct attribute_group bulk_profile_dev_attr_group = {
188	.name = ".bulk_profile",
189	.attrs = bulk_profile_dev_attrs,
190};
191
192static const struct attribute_group *xe_sriov_dev_attr_groups[] = {
193	&bulk_profile_dev_attr_group,
194	NULL
195};
196
197/* and VF-level attributes go here */
198
199#define DEFINE_SIMPLE_PROVISIONING_SRIOV_VF_ATTR(NAME, ITEM, TYPE, FORMAT)		\
200static ssize_t xe_sriov_vf_attr_##NAME##_show(struct xe_device *xe, unsigned int vfid,	\
201					      char *buf)				\
202{											\
203	TYPE value = 0;									\
204	int err;									\
205											\
206	err = xe_sriov_pf_provision_query_vf_##ITEM(xe, vfid, &value);			\
207	if (err)									\
208		return err;								\
209											\
210	return sysfs_emit(buf, FORMAT, value);						\
211}											\
212											\
213static ssize_t xe_sriov_vf_attr_##NAME##_store(struct xe_device *xe, unsigned int vfid,	\
214					       const char *buf, size_t count)		\
215{											\
216	TYPE value;									\
217	int err;									\
218											\
219	err = kstrto##TYPE(buf, 0, &value);						\
220	if (err)									\
221		return err;								\
222											\
223	err = xe_sriov_pf_provision_apply_vf_##ITEM(xe, vfid, value);			\
224	return err ?: count;								\
225}											\
226											\
227static XE_SRIOV_VF_ATTR(NAME)
228
229DEFINE_SIMPLE_PROVISIONING_SRIOV_VF_ATTR(exec_quantum_ms, eq, u32, "%u\n");
230DEFINE_SIMPLE_PROVISIONING_SRIOV_VF_ATTR(preempt_timeout_us, pt, u32, "%u\n");
231
232static ssize_t xe_sriov_vf_attr_sched_priority_show(struct xe_device *xe, unsigned int vfid,
233						    char *buf)
234{
235	size_t num_priorities = ARRAY_SIZE(sched_priority_names);
236	u32 priority;
237	int err;
238
239	err = xe_sriov_pf_provision_query_vf_priority(xe, vfid, &priority);
240	if (err)
241		return err;
242
243	if (!sched_priority_high_allowed(vfid))
244		num_priorities--;
245
246	xe_assert(xe, priority < num_priorities);
247	return emit_choice(buf, priority, sched_priority_names, num_priorities);
248}
249
250static ssize_t xe_sriov_vf_attr_sched_priority_store(struct xe_device *xe, unsigned int vfid,
251						     const char *buf, size_t count)
252{
253	size_t num_priorities = ARRAY_SIZE(sched_priority_names);
254	int match;
255	int err;
256
257	if (!sched_priority_change_allowed(vfid))
258		return -EOPNOTSUPP;
259
260	if (!sched_priority_high_allowed(vfid))
261		num_priorities--;
262
263	match = __sysfs_match_string(sched_priority_names, num_priorities, buf);
264	if (match < 0)
265		return -EINVAL;
266
267	err = xe_sriov_pf_provision_apply_vf_priority(xe, vfid, match);
268	return err ?: count;
269}
270
271static XE_SRIOV_VF_ATTR(sched_priority);
272
273static struct attribute *profile_vf_attrs[] = {
274	&xe_sriov_vf_attr_exec_quantum_ms.attr,
275	&xe_sriov_vf_attr_preempt_timeout_us.attr,
276	&xe_sriov_vf_attr_sched_priority.attr,
277	NULL
278};
279
280static umode_t profile_vf_attr_is_visible(struct kobject *kobj,
281					  struct attribute *attr, int index)
282{
283	struct xe_sriov_kobj *vkobj = to_xe_sriov_kobj(kobj);
284
285	if (attr == &xe_sriov_vf_attr_sched_priority.attr &&
286	    !sched_priority_change_allowed(vkobj->vfid))
287		return attr->mode & 0444;
288
289	return attr->mode;
290}
291
292static const struct attribute_group profile_vf_attr_group = {
293	.name = "profile",
294	.attrs = profile_vf_attrs,
295	.is_visible = profile_vf_attr_is_visible,
296};
297
298#define DEFINE_SIMPLE_CONTROL_SRIOV_VF_ATTR(NAME)					\
299											\
300static ssize_t xe_sriov_vf_attr_##NAME##_store(struct xe_device *xe, unsigned int vfid,	\
301					       const char *buf, size_t count)		\
302{											\
303	bool yes;									\
304	int err;									\
305											\
306	if (!vfid)									\
307		return -EPERM;								\
308											\
309	err = kstrtobool(buf, &yes);							\
310	if (err)									\
311		return err;								\
312	if (!yes)									\
313		return count;								\
314											\
315	err = xe_sriov_pf_control_##NAME##_vf(xe, vfid);				\
316	return err ?: count;								\
317}											\
318											\
319static XE_SRIOV_VF_ATTR_WO(NAME)
320
321DEFINE_SIMPLE_CONTROL_SRIOV_VF_ATTR(stop);
322
323static struct attribute *control_vf_attrs[] = {
324	&xe_sriov_vf_attr_stop.attr,
325	NULL
326};
327
328static umode_t control_vf_attr_is_visible(struct kobject *kobj,
329					  struct attribute *attr, int index)
330{
331	struct xe_sriov_kobj *vkobj = to_xe_sriov_kobj(kobj);
332
333	if (vkobj->vfid == PFID)
334		return 0;
335
336	return attr->mode;
337}
338
339static const struct attribute_group control_vf_attr_group = {
340	.attrs = control_vf_attrs,
341	.is_visible = control_vf_attr_is_visible,
342};
343
344static const struct attribute_group *xe_sriov_vf_attr_groups[] = {
345	&profile_vf_attr_group,
346	&control_vf_attr_group,
347	NULL
348};
349
350/* no user serviceable parts below */
351
352static void action_put_kobject(void *arg)
353{
354	struct kobject *kobj = arg;
355
356	kobject_put(kobj);
357}
358
359static struct kobject *create_xe_sriov_kobj(struct xe_device *xe, unsigned int vfid,
360					    const struct kobj_type *ktype)
361{
362	struct xe_sriov_kobj *vkobj;
363	int err;
364
365	xe_sriov_pf_assert_vfid(xe, vfid);
366
367	vkobj = kzalloc(sizeof(*vkobj), GFP_KERNEL);
368	if (!vkobj)
369		return ERR_PTR(-ENOMEM);
370
371	vkobj->xe = xe;
372	vkobj->vfid = vfid;
373	kobject_init(&vkobj->base, ktype);
374
375	err = devm_add_action_or_reset(xe->drm.dev, action_put_kobject, &vkobj->base);
376	if (err)
377		return ERR_PTR(err);
378
379	return &vkobj->base;
380}
381
382static void release_xe_sriov_kobj(struct kobject *kobj)
383{
384	struct xe_sriov_kobj *vkobj = to_xe_sriov_kobj(kobj);
385
386	kfree(vkobj);
387}
388
389static ssize_t xe_sriov_dev_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
390{
391	struct xe_sriov_dev_attr *vattr  = to_xe_sriov_dev_attr(attr);
392	struct xe_sriov_kobj *vkobj = to_xe_sriov_kobj(kobj);
393	struct xe_device *xe = vkobj->xe;
394
395	if (!vattr->show)
396		return -EPERM;
397
398	return vattr->show(xe, buf);
399}
400
401static ssize_t xe_sriov_dev_attr_store(struct kobject *kobj, struct attribute *attr,
402				       const char *buf, size_t count)
403{
404	struct xe_sriov_dev_attr *vattr = to_xe_sriov_dev_attr(attr);
405	struct xe_sriov_kobj *vkobj = to_xe_sriov_kobj(kobj);
406	struct xe_device *xe = vkobj->xe;
407	ssize_t ret;
408
409	if (!vattr->store)
410		return -EPERM;
411
412	xe_pm_runtime_get(xe);
413	ret = xe_sriov_pf_wait_ready(xe) ?: vattr->store(xe, buf, count);
414	xe_pm_runtime_put(xe);
415
416	return ret;
417}
418
419static ssize_t xe_sriov_vf_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
420{
421	struct xe_sriov_vf_attr *vattr = to_xe_sriov_vf_attr(attr);
422	struct xe_sriov_kobj *vkobj = to_xe_sriov_kobj(kobj);
423	struct xe_device *xe = vkobj->xe;
424	unsigned int vfid = vkobj->vfid;
425
426	xe_sriov_pf_assert_vfid(xe, vfid);
427
428	if (!vattr->show)
429		return -EPERM;
430
431	return vattr->show(xe, vfid, buf);
432}
433
434static ssize_t xe_sriov_vf_attr_store(struct kobject *kobj, struct attribute *attr,
435				      const char *buf, size_t count)
436{
437	struct xe_sriov_vf_attr *vattr = to_xe_sriov_vf_attr(attr);
438	struct xe_sriov_kobj *vkobj = to_xe_sriov_kobj(kobj);
439	struct xe_device *xe = vkobj->xe;
440	unsigned int vfid = vkobj->vfid;
441	ssize_t ret;
442
443	xe_sriov_pf_assert_vfid(xe, vfid);
444
445	if (!vattr->store)
446		return -EPERM;
447
448	xe_pm_runtime_get(xe);
449	ret = xe_sriov_pf_wait_ready(xe) ?: vattr->store(xe, vfid, buf, count);
450	xe_pm_runtime_get(xe);
451
452	return ret;
453}
454
455static const struct sysfs_ops xe_sriov_dev_sysfs_ops = {
456	.show = xe_sriov_dev_attr_show,
457	.store = xe_sriov_dev_attr_store,
458};
459
460static const struct sysfs_ops xe_sriov_vf_sysfs_ops = {
461	.show = xe_sriov_vf_attr_show,
462	.store = xe_sriov_vf_attr_store,
463};
464
465static const struct kobj_type xe_sriov_dev_ktype = {
466	.release = release_xe_sriov_kobj,
467	.sysfs_ops = &xe_sriov_dev_sysfs_ops,
468	.default_groups = xe_sriov_dev_attr_groups,
469};
470
471static const struct kobj_type xe_sriov_vf_ktype = {
472	.release = release_xe_sriov_kobj,
473	.sysfs_ops = &xe_sriov_vf_sysfs_ops,
474	.default_groups = xe_sriov_vf_attr_groups,
475};
476
477static int pf_sysfs_error(struct xe_device *xe, int err, const char *what)
478{
479	if (IS_ENABLED(CONFIG_DRM_XE_DEBUG))
480		xe_sriov_dbg(xe, "Failed to setup sysfs %s (%pe)\n", what, ERR_PTR(err));
481	return err;
482}
483
484static void pf_sysfs_note(struct xe_device *xe, int err, const char *what)
485{
486	xe_sriov_dbg(xe, "Failed to setup sysfs %s (%pe)\n", what, ERR_PTR(err));
487}
488
489static int pf_setup_root(struct xe_device *xe)
490{
491	struct kobject *parent = &xe->drm.dev->kobj;
492	struct kobject *root;
493	int err;
494
495	root = create_xe_sriov_kobj(xe, PFID, &xe_sriov_dev_ktype);
496	if (IS_ERR(root))
497		return pf_sysfs_error(xe, PTR_ERR(root), "root obj");
498
499	err = kobject_add(root, parent, "sriov_admin");
500	if (err)
501		return pf_sysfs_error(xe, err, "root init");
502
503	xe_assert(xe, IS_SRIOV_PF(xe));
504	xe_assert(xe, !xe->sriov.pf.sysfs.root);
505	xe->sriov.pf.sysfs.root = root;
506	return 0;
507}
508
509static int pf_setup_tree(struct xe_device *xe)
510{
511	unsigned int totalvfs = xe_sriov_pf_get_totalvfs(xe);
512	struct kobject *root, *kobj;
513	unsigned int n;
514	int err;
515
516	xe_assert(xe, IS_SRIOV_PF(xe));
517	root = xe->sriov.pf.sysfs.root;
518
519	for (n = 0; n <= totalvfs; n++) {
520		kobj = create_xe_sriov_kobj(xe, VFID(n), &xe_sriov_vf_ktype);
521		if (IS_ERR(kobj))
522			return pf_sysfs_error(xe, PTR_ERR(kobj), "tree obj");
523
524		if (n)
525			err = kobject_add(kobj, root, "vf%u", n);
526		else
527			err = kobject_add(kobj, root, "pf");
528		if (err)
529			return pf_sysfs_error(xe, err, "tree init");
530
531		xe_assert(xe, !xe->sriov.pf.vfs[n].kobj);
532		xe->sriov.pf.vfs[n].kobj = kobj;
533	}
534
535	return 0;
536}
537
538static void action_rm_device_link(void *arg)
539{
540	struct kobject *kobj = arg;
541
542	sysfs_remove_link(kobj, "device");
543}
544
545static int pf_link_pf_device(struct xe_device *xe)
546{
547	struct kobject *kobj = xe->sriov.pf.vfs[PFID].kobj;
548	int err;
549
550	err = sysfs_create_link(kobj, &xe->drm.dev->kobj, "device");
551	if (err)
552		return pf_sysfs_error(xe, err, "PF device link");
553
554	err = devm_add_action_or_reset(xe->drm.dev, action_rm_device_link, kobj);
555	if (err)
556		return pf_sysfs_error(xe, err, "PF unlink action");
557
558	return 0;
559}
560
561/**
562 * xe_sriov_pf_sysfs_init() - Setup PF's SR-IOV sysfs tree.
563 * @xe: the PF &xe_device to setup sysfs
564 *
565 * This function will create additional nodes that will represent PF and VFs
566 * devices, each populated with SR-IOV Xe specific attributes.
567 *
568 * Return: 0 on success or a negative error code on failure.
569 */
570int xe_sriov_pf_sysfs_init(struct xe_device *xe)
571{
572	int err;
573
574	err = pf_setup_root(xe);
575	if (err)
576		return err;
577
578	err = pf_setup_tree(xe);
579	if (err)
580		return err;
581
582	err = pf_link_pf_device(xe);
583	if (err)
584		return err;
585
586	return 0;
587}
588
589/**
590 * xe_sriov_pf_sysfs_link_vfs() - Add VF's links in SR-IOV sysfs tree.
591 * @xe: the &xe_device where to update sysfs
592 * @num_vfs: number of enabled VFs to link
593 *
594 * This function is specific for the PF driver.
595 *
596 * This function will add symbolic links between VFs represented in the SR-IOV
597 * sysfs tree maintained by the PF and enabled VF PCI devices.
598 *
599 * The @xe_sriov_pf_sysfs_unlink_vfs() shall be used to remove those links.
600 */
601void xe_sriov_pf_sysfs_link_vfs(struct xe_device *xe, unsigned int num_vfs)
602{
603	unsigned int totalvfs = xe_sriov_pf_get_totalvfs(xe);
604	struct pci_dev *pf_pdev = to_pci_dev(xe->drm.dev);
605	struct pci_dev *vf_pdev = NULL;
606	unsigned int n;
607	int err;
608
609	xe_assert(xe, IS_SRIOV_PF(xe));
610	xe_assert(xe, num_vfs <= totalvfs);
611
612	for (n = 1; n <= num_vfs; n++) {
613		vf_pdev = xe_pci_sriov_get_vf_pdev(pf_pdev, VFID(n));
614		if (!vf_pdev)
615			return pf_sysfs_note(xe, -ENOENT, "VF link");
616
617		err = sysfs_create_link(xe->sriov.pf.vfs[VFID(n)].kobj,
618					&vf_pdev->dev.kobj, "device");
619
620		/* must balance xe_pci_sriov_get_vf_pdev() */
621		pci_dev_put(vf_pdev);
622
623		if (err)
624			return pf_sysfs_note(xe, err, "VF link");
625	}
626}
627
628/**
629 * xe_sriov_pf_sysfs_unlink_vfs() - Remove VF's links from SR-IOV sysfs tree.
630 * @xe: the &xe_device where to update sysfs
631 * @num_vfs: number of VFs to unlink
632 *
633 * This function shall be called only on the PF.
634 * This function will remove "device" links added by @xe_sriov_sysfs_link_vfs().
635 */
636void xe_sriov_pf_sysfs_unlink_vfs(struct xe_device *xe, unsigned int num_vfs)
637{
638	unsigned int n;
639
640	xe_assert(xe, IS_SRIOV_PF(xe));
641	xe_assert(xe, num_vfs <= xe_sriov_pf_get_totalvfs(xe));
642
643	for (n = 1; n <= num_vfs; n++)
644		sysfs_remove_link(xe->sriov.pf.vfs[VFID(n)].kobj, "device");
645}
~/ Read-only file preview.