Contributors: 6
Author Tokens Token Proportion Commits Commit Proportion
Michal Wajdeczko 1361 83.81% 12 57.14%
Michał Winiarski 225 13.85% 4 19.05%
Matthew Brost 26 1.60% 1 4.76%
Lucas De Marchi 7 0.43% 2 9.52%
Eric Anholt 3 0.18% 1 4.76%
Rodrigo Vivi 2 0.12% 1 4.76%
Total 1624 21


// SPDX-License-Identifier: MIT
/*
 * Copyright © 2025 Intel Corporation
 */

#include <linux/debugfs.h>
#include <drm/drm_debugfs.h>

#include "xe_device.h"
#include "xe_device_types.h"
#include "xe_pm.h"
#include "xe_sriov_pf.h"
#include "xe_sriov_pf_control.h"
#include "xe_sriov_pf_debugfs.h"
#include "xe_sriov_pf_helpers.h"
#include "xe_sriov_pf_migration.h"
#include "xe_sriov_pf_provision.h"
#include "xe_sriov_pf_service.h"
#include "xe_sriov_printk.h"
#include "xe_tile_sriov_pf_debugfs.h"

/*
 *      /sys/kernel/debug/dri/BDF/
 *      ├── sriov		# d_inode->i_private = (xe_device*)
 *      │   ├── pf		# d_inode->i_private = (xe_device*)
 *      │   ├── vf1		# d_inode->i_private = VFID(1)
 *      :   :
 *      │   ├── vfN		# d_inode->i_private = VFID(N)
 */

static void *extract_priv(struct dentry *d)
{
	return d->d_inode->i_private;
}

static struct xe_device *extract_xe(struct dentry *d)
{
	return extract_priv(d->d_parent);
}

static unsigned int extract_vfid(struct dentry *d)
{
	void *p = extract_priv(d);

	return p == extract_xe(d) ? PFID : (uintptr_t)p;
}

/*
 *      /sys/kernel/debug/dri/BDF/
 *      ├── sriov
 *      │   ├── restore_auto_provisioning
 *      │   :
 *      │   ├── pf/
 *      │   ├── vf1
 *      │   │   ├── ...
 */

static ssize_t from_file_write_to_xe_call(struct file *file, const char __user *userbuf,
					  size_t count, loff_t *ppos,
					  int (*call)(struct xe_device *))
{
	struct dentry *dent = file_dentry(file);
	struct xe_device *xe = extract_xe(dent);
	bool yes;
	int ret;

	if (*ppos)
		return -EINVAL;
	ret = kstrtobool_from_user(userbuf, count, &yes);
	if (ret < 0)
		return ret;
	if (yes) {
		xe_pm_runtime_get(xe);
		ret = call(xe);
		xe_pm_runtime_put(xe);
	}
	if (ret < 0)
		return ret;
	return count;
}

#define DEFINE_SRIOV_ATTRIBUTE(OP)						\
static int OP##_show(struct seq_file *s, void *unused)				\
{										\
	return 0;								\
}										\
static ssize_t OP##_write(struct file *file, const char __user *userbuf,	\
			  size_t count, loff_t *ppos)				\
{										\
	return from_file_write_to_xe_call(file, userbuf, count, ppos,		\
					  xe_sriov_pf_##OP);			\
}										\
DEFINE_SHOW_STORE_ATTRIBUTE(OP)

static inline int xe_sriov_pf_restore_auto_provisioning(struct xe_device *xe)
{
	return xe_sriov_pf_provision_set_mode(xe, XE_SRIOV_PROVISIONING_MODE_AUTO);
}

DEFINE_SRIOV_ATTRIBUTE(restore_auto_provisioning);

static int lockdown_vfs_enabling_open(struct inode *inode, struct file *file)
{
	struct dentry *dent = file_dentry(file);
	struct xe_device *xe = extract_xe(dent);
	ssize_t ret;

	ret = xe_sriov_pf_lockdown(xe);
	if (ret < 0)
		return ret;

	file->private_data = xe;
	return nonseekable_open(inode, file);
}

static int lockdown_vfs_enabling_release(struct inode *inode, struct file *file)
{
	struct xe_device *xe = file->private_data;

	xe_sriov_pf_end_lockdown(xe);
	return 0;
}

static const struct file_operations lockdown_vfs_enabling_fops = {
	.owner		= THIS_MODULE,
	.open		= lockdown_vfs_enabling_open,
	.release	= lockdown_vfs_enabling_release,
};

static void pf_populate_root(struct xe_device *xe, struct dentry *dent)
{
	debugfs_create_file("restore_auto_provisioning", 0200, dent, xe,
			    &restore_auto_provisioning_fops);
	debugfs_create_file("lockdown_vfs_enabling", 0400, dent, xe,
			    &lockdown_vfs_enabling_fops);
}

static int simple_show(struct seq_file *m, void *data)
{
	struct drm_printer p = drm_seq_file_printer(m);
	struct drm_info_node *node = m->private;
	struct dentry *parent = node->dent->d_parent;
	struct xe_device *xe = parent->d_inode->i_private;
	void (*print)(struct xe_device *, struct drm_printer *) = node->info_ent->data;

	print(xe, &p);
	return 0;
}

static const struct drm_info_list debugfs_list[] = {
	{ .name = "vfs", .show = simple_show, .data = xe_sriov_pf_print_vfs_summary },
	{ .name = "versions", .show = simple_show, .data = xe_sriov_pf_service_print_versions },
};

static void pf_populate_pf(struct xe_device *xe, struct dentry *pfdent)
{
	struct drm_minor *minor = xe->drm.primary;

	drm_debugfs_create_files(debugfs_list, ARRAY_SIZE(debugfs_list), pfdent, minor);
}

/*
 *      /sys/kernel/debug/dri/BDF/
 *      ├── sriov
 *      │   ├── vf1
 *      │   │   ├── migration_data
 *      │   │   ├── pause
 *      │   │   ├── reset
 *      │   │   ├── resume
 *      │   │   ├── stop
 *      │   │   ├── save
 *      │   │   ├── restore
 *      │   │   :
 *      │   ├── vf2
 *      │   │   ├── ...
 */

static int from_file_read_to_vf_call(struct seq_file *s,
				     int (*call)(struct xe_device *, unsigned int))
{
	struct dentry *dent = file_dentry(s->file)->d_parent;
	struct xe_device *xe = extract_xe(dent);
	unsigned int vfid = extract_vfid(dent);
	int ret;

	xe_pm_runtime_get(xe);
	ret = call(xe, vfid);
	xe_pm_runtime_put(xe);

	if (ret < 0)
		return ret;

	return 0;
}

static ssize_t from_file_write_to_vf_call(struct file *file, const char __user *userbuf,
					  size_t count, loff_t *ppos,
					  int (*call)(struct xe_device *, unsigned int))
{
	struct dentry *dent = file_dentry(file)->d_parent;
	struct xe_device *xe = extract_xe(dent);
	unsigned int vfid = extract_vfid(dent);
	bool yes;
	int ret;

	if (*ppos)
		return -EINVAL;
	ret = kstrtobool_from_user(userbuf, count, &yes);
	if (ret < 0)
		return ret;
	if (yes) {
		xe_pm_runtime_get(xe);
		ret = call(xe, vfid);
		xe_pm_runtime_put(xe);
	}
	if (ret < 0)
		return ret;
	return count;
}

#define DEFINE_VF_CONTROL_ATTRIBUTE(OP)						\
static int OP##_show(struct seq_file *s, void *unused)				\
{										\
	return 0;								\
}										\
static ssize_t OP##_write(struct file *file, const char __user *userbuf,	\
			  size_t count, loff_t *ppos)				\
{										\
	return from_file_write_to_vf_call(file, userbuf, count, ppos,		\
					  xe_sriov_pf_control_##OP);		\
}										\
DEFINE_SHOW_STORE_ATTRIBUTE(OP)

#define DEFINE_VF_CONTROL_ATTRIBUTE_RW(OP)					\
static int OP##_show(struct seq_file *s, void *unused)				\
{										\
	return from_file_read_to_vf_call(s,					\
					 xe_sriov_pf_control_finish_##OP);	\
}										\
static ssize_t OP##_write(struct file *file, const char __user *userbuf,	\
			  size_t count, loff_t *ppos)				\
{										\
	return from_file_write_to_vf_call(file, userbuf, count, ppos,		\
					  xe_sriov_pf_control_trigger_##OP);	\
}										\
DEFINE_SHOW_STORE_ATTRIBUTE(OP)

DEFINE_VF_CONTROL_ATTRIBUTE(pause_vf);
DEFINE_VF_CONTROL_ATTRIBUTE(resume_vf);
DEFINE_VF_CONTROL_ATTRIBUTE(stop_vf);
DEFINE_VF_CONTROL_ATTRIBUTE(reset_vf);
DEFINE_VF_CONTROL_ATTRIBUTE_RW(save_vf);
DEFINE_VF_CONTROL_ATTRIBUTE_RW(restore_vf);

static ssize_t data_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
{
	struct dentry *dent = file_dentry(file)->d_parent;
	struct xe_device *xe = extract_xe(dent);
	unsigned int vfid = extract_vfid(dent);

	if (*pos)
		return -ESPIPE;

	return xe_sriov_pf_migration_write(xe, vfid, buf, count);
}

static ssize_t data_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
	struct dentry *dent = file_dentry(file)->d_parent;
	struct xe_device *xe = extract_xe(dent);
	unsigned int vfid = extract_vfid(dent);

	if (*ppos)
		return -ESPIPE;

	return xe_sriov_pf_migration_read(xe, vfid, buf, count);
}

static const struct file_operations data_vf_fops = {
	.owner		= THIS_MODULE,
	.open		= simple_open,
	.write		= data_write,
	.read		= data_read,
	.llseek		= default_llseek,
};

static ssize_t size_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos)
{
	struct dentry *dent = file_dentry(file)->d_parent;
	struct xe_device *xe = extract_xe(dent);
	unsigned int vfid = extract_vfid(dent);
	char buf[21];
	ssize_t ret;
	int len;

	xe_pm_runtime_get(xe);
	ret = xe_sriov_pf_migration_size(xe, vfid);
	xe_pm_runtime_put(xe);
	if (ret < 0)
		return ret;

	len = scnprintf(buf, sizeof(buf), "%zd\n", ret);

	return simple_read_from_buffer(ubuf, count, ppos, buf, len);
}

static const struct file_operations size_vf_fops = {
	.owner		= THIS_MODULE,
	.open		= simple_open,
	.read		= size_read,
	.llseek		= default_llseek,
};

static void pf_populate_vf(struct xe_device *xe, struct dentry *vfdent)
{
	debugfs_create_file("pause", 0200, vfdent, xe, &pause_vf_fops);
	debugfs_create_file("resume", 0200, vfdent, xe, &resume_vf_fops);
	debugfs_create_file("stop", 0200, vfdent, xe, &stop_vf_fops);
	debugfs_create_file("reset", 0200, vfdent, xe, &reset_vf_fops);
	debugfs_create_file("save", 0600, vfdent, xe, &save_vf_fops);
	debugfs_create_file("restore", 0600, vfdent, xe, &restore_vf_fops);
	debugfs_create_file("migration_data", 0600, vfdent, xe, &data_vf_fops);
	debugfs_create_file("migration_size", 0400, vfdent, xe, &size_vf_fops);
}

static void pf_populate_with_tiles(struct xe_device *xe, struct dentry *dent, unsigned int vfid)
{
	struct xe_tile *tile;
	unsigned int id;

	for_each_tile(tile, xe, id)
		xe_tile_sriov_pf_debugfs_populate(tile, dent, vfid);
}

/**
 * xe_sriov_pf_debugfs_register - Register PF debugfs attributes.
 * @xe: the &xe_device
 * @root: the root &dentry
 *
 * Create separate directory that will contain all SR-IOV related files,
 * organized per each SR-IOV function (PF, VF1, VF2, ..., VFn).
 */
void xe_sriov_pf_debugfs_register(struct xe_device *xe, struct dentry *root)
{
	int totalvfs = xe_sriov_pf_get_totalvfs(xe);
	struct dentry *pfdent;
	struct dentry *vfdent;
	struct dentry *dent;
	char vfname[16]; /* should be more than enough for "vf%u\0" and VFID(UINT_MAX) */
	unsigned int n;

	/*
	 *      /sys/kernel/debug/dri/BDF/
	 *      ├── sriov		# d_inode->i_private = (xe_device*)
	 *      │   ├── ...
	 */
	dent = debugfs_create_dir("sriov", root);
	if (IS_ERR(dent))
		return;
	dent->d_inode->i_private = xe;

	pf_populate_root(xe, dent);

	/*
	 *      /sys/kernel/debug/dri/BDF/
	 *      ├── sriov		# d_inode->i_private = (xe_device*)
	 *      │   ├── pf		# d_inode->i_private = (xe_device*)
	 *      │   │   ├── ...
	 */
	pfdent = debugfs_create_dir("pf", dent);
	if (IS_ERR(pfdent))
		return;
	pfdent->d_inode->i_private = xe;

	pf_populate_pf(xe, pfdent);
	pf_populate_with_tiles(xe, pfdent, PFID);

	/*
	 *      /sys/kernel/debug/dri/BDF/
	 *      ├── sriov		# d_inode->i_private = (xe_device*)
	 *      │   ├── vf1		# d_inode->i_private = VFID(1)
	 *      │   ├── vf2		# d_inode->i_private = VFID(2)
	 *      │   ├── ...
	 */
	for (n = 1; n <= totalvfs; n++) {
		snprintf(vfname, sizeof(vfname), "vf%u", VFID(n));
		vfdent = debugfs_create_dir(vfname, dent);
		if (IS_ERR(vfdent))
			return;
		vfdent->d_inode->i_private = (void *)(uintptr_t)VFID(n);

		pf_populate_vf(xe, vfdent);
		pf_populate_with_tiles(xe, vfdent, VFID(n));
	}
}