mirror of
https://github.com/torvalds/linux.git
synced 2026-03-07 23:04:33 +01:00
ctx_restore_mid_bb memory is allocated in wa_bb_store(), but
xe_config_device_release() only frees ctx_restore_post_bb.
Free ctx_restore_mid_bb[0].cs as well to avoid leaking the allocation
when the configfs device is removed.
Fixes: b30d5de3d4 ("drm/xe/configfs: Add mid context restore bb")
Signed-off-by: Shuicheng Lin <shuicheng.lin@intel.com>
Reviewed-by: Nitin Gote <nitin.r.gote@intel.com>
Link: https://patch.msgid.link/20260225013448.3547687-2-shuicheng.lin@intel.com
Signed-off-by: Matt Roper <matthew.d.roper@intel.com>
(cherry picked from commit a235e7d0098337c3f2d1e8f3610c719a589e115f)
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
1291 lines
33 KiB
C
1291 lines
33 KiB
C
// SPDX-License-Identifier: MIT
|
|
/*
|
|
* Copyright © 2025 Intel Corporation
|
|
*/
|
|
|
|
#include <linux/bitops.h>
|
|
#include <linux/ctype.h>
|
|
#include <linux/configfs.h>
|
|
#include <linux/cleanup.h>
|
|
#include <linux/find.h>
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/string.h>
|
|
|
|
#include "instructions/xe_mi_commands.h"
|
|
#include "xe_configfs.h"
|
|
#include "xe_gt_types.h"
|
|
#include "xe_hw_engine_types.h"
|
|
#include "xe_module.h"
|
|
#include "xe_pci_types.h"
|
|
#include "xe_sriov_types.h"
|
|
|
|
/**
|
|
* DOC: Xe Configfs
|
|
*
|
|
* Overview
|
|
* ========
|
|
*
|
|
* Configfs is a filesystem-based manager of kernel objects. Xe KMD registers a
|
|
* configfs subsystem called ``xe`` that creates a directory in the mounted
|
|
* configfs directory. The user can create devices under this directory and
|
|
* configure them as necessary. See Documentation/filesystems/configfs.rst for
|
|
* more information about how configfs works.
|
|
*
|
|
* Create devices
|
|
* ==============
|
|
*
|
|
* To create a device, the ``xe`` module should already be loaded, but some
|
|
* attributes can only be set before binding the device. It can be accomplished
|
|
* by blocking the driver autoprobe::
|
|
*
|
|
* # echo 0 > /sys/bus/pci/drivers_autoprobe
|
|
* # modprobe xe
|
|
*
|
|
* In order to create a device, the user has to create a directory inside ``xe``::
|
|
*
|
|
* # mkdir /sys/kernel/config/xe/0000:03:00.0/
|
|
*
|
|
* Every device created is populated by the driver with entries that can be
|
|
* used to configure it::
|
|
*
|
|
* /sys/kernel/config/xe/
|
|
* ├── 0000:00:02.0
|
|
* │ └── ...
|
|
* ├── 0000:00:02.1
|
|
* │ └── ...
|
|
* :
|
|
* └── 0000:03:00.0
|
|
* ├── survivability_mode
|
|
* ├── gt_types_allowed
|
|
* ├── engines_allowed
|
|
* └── enable_psmi
|
|
*
|
|
* After configuring the attributes as per next section, the device can be
|
|
* probed with::
|
|
*
|
|
* # echo 0000:03:00.0 > /sys/bus/pci/drivers/xe/bind
|
|
* # # or
|
|
* # echo 0000:03:00.0 > /sys/bus/pci/drivers_probe
|
|
*
|
|
* Configure Attributes
|
|
* ====================
|
|
*
|
|
* Survivability mode:
|
|
* -------------------
|
|
*
|
|
* Enable survivability mode on supported cards. This setting only takes
|
|
* effect when probing the device. Example to enable it::
|
|
*
|
|
* # echo 1 > /sys/kernel/config/xe/0000:03:00.0/survivability_mode
|
|
*
|
|
* This attribute can only be set before binding to the device.
|
|
*
|
|
* Allowed GT types:
|
|
* -----------------
|
|
*
|
|
* Allow only specific types of GTs to be detected and initialized by the
|
|
* driver. Any combination of GT types can be enabled/disabled, although
|
|
* some settings will cause the device to fail to probe.
|
|
*
|
|
* Writes support both comma- and newline-separated input format. Reads
|
|
* will always return one GT type per line. "primary" and "media" are the
|
|
* GT type names supported by this interface.
|
|
*
|
|
* This attribute can only be set before binding to the device.
|
|
*
|
|
* Examples:
|
|
*
|
|
* Allow both primary and media GTs to be initialized and used. This matches
|
|
* the driver's default behavior::
|
|
*
|
|
* # echo 'primary,media' > /sys/kernel/config/xe/0000:03:00.0/gt_types_allowed
|
|
*
|
|
* Allow only the primary GT of each tile to be initialized and used,
|
|
* effectively disabling the media GT if it exists on the platform::
|
|
*
|
|
* # echo 'primary' > /sys/kernel/config/xe/0000:03:00.0/gt_types_allowed
|
|
*
|
|
* Allow only the media GT of each tile to be initialized and used,
|
|
* effectively disabling the primary GT. **This configuration will cause
|
|
* device probe failure on all current platforms, but may be allowed on
|
|
* igpu platforms in the future**::
|
|
*
|
|
* # echo 'media' > /sys/kernel/config/xe/0000:03:00.0/gt_types_allowed
|
|
*
|
|
* Disable all GTs. Only other GPU IP (such as display) is potentially usable.
|
|
* **This configuration will cause device probe failure on all current
|
|
* platforms, but may be allowed on igpu platforms in the future**::
|
|
*
|
|
* # echo '' > /sys/kernel/config/xe/0000:03:00.0/gt_types_allowed
|
|
*
|
|
* Allowed engines:
|
|
* ----------------
|
|
*
|
|
* Allow only a set of engine(s) to be available, disabling the other engines
|
|
* even if they are available in hardware. This is applied after HW fuses are
|
|
* considered on each tile. Examples:
|
|
*
|
|
* Allow only one render and one copy engines, nothing else::
|
|
*
|
|
* # echo 'rcs0,bcs0' > /sys/kernel/config/xe/0000:03:00.0/engines_allowed
|
|
*
|
|
* Allow only compute engines and first copy engine::
|
|
*
|
|
* # echo 'ccs*,bcs0' > /sys/kernel/config/xe/0000:03:00.0/engines_allowed
|
|
*
|
|
* Note that the engine names are the per-GT hardware names. On multi-tile
|
|
* platforms, writing ``rcs0,bcs0`` to this file would allow the first render
|
|
* and copy engines on each tile.
|
|
*
|
|
* The requested configuration may not be supported by the platform and driver
|
|
* may fail to probe. For example: if at least one copy engine is expected to be
|
|
* available for migrations, but it's disabled. This is intended for debugging
|
|
* purposes only.
|
|
*
|
|
* This attribute can only be set before binding to the device.
|
|
*
|
|
* PSMI
|
|
* ----
|
|
*
|
|
* Enable extra debugging capabilities to trace engine execution. Only useful
|
|
* during early platform enabling and requires additional hardware connected.
|
|
* Once it's enabled, additionals WAs are added and runtime configuration is
|
|
* done via debugfs. Example to enable it::
|
|
*
|
|
* # echo 1 > /sys/kernel/config/xe/0000:03:00.0/enable_psmi
|
|
*
|
|
* This attribute can only be set before binding to the device.
|
|
*
|
|
* Context restore BB
|
|
* ------------------
|
|
*
|
|
* Allow to execute a batch buffer during any context switches. When the
|
|
* GPU is restoring the context, it executes additional commands. It's useful
|
|
* for testing additional workarounds and validating certain HW behaviors: it's
|
|
* not intended for normal execution and will taint the kernel with TAINT_TEST
|
|
* when used.
|
|
*
|
|
* The syntax allows to pass straight instructions to be executed by the engine
|
|
* in a batch buffer or set specific registers.
|
|
*
|
|
* #. Generic instruction::
|
|
*
|
|
* <engine-class> cmd <instr> [[dword0] [dword1] [...]]
|
|
*
|
|
* #. Simple register setting::
|
|
*
|
|
* <engine-class> reg <address> <value>
|
|
*
|
|
* Commands are saved per engine class: all instances of that class will execute
|
|
* those commands during context switch. The instruction, dword arguments,
|
|
* addresses and values are in hex format like in the examples below.
|
|
*
|
|
* #. Execute a LRI command to write 0xDEADBEEF to register 0x4f10 after the
|
|
* normal context restore::
|
|
*
|
|
* # echo 'rcs cmd 11000001 4F100 DEADBEEF' \
|
|
* > /sys/kernel/config/xe/0000:03:00.0/ctx_restore_post_bb
|
|
*
|
|
* #. Execute a LRI command to write 0xDEADBEEF to register 0x4f10 at the
|
|
* beginning of the context restore::
|
|
*
|
|
* # echo 'rcs cmd 11000001 4F100 DEADBEEF' \
|
|
* > /sys/kernel/config/xe/0000:03:00.0/ctx_restore_mid_bb
|
|
|
|
* #. Load certain values in a couple of registers (it can be used as a simpler
|
|
* alternative to the `cmd`) action::
|
|
*
|
|
* # cat > /sys/kernel/config/xe/0000:03:00.0/ctx_restore_post_bb <<EOF
|
|
* rcs reg 4F100 DEADBEEF
|
|
* rcs reg 4F104 FFFFFFFF
|
|
* EOF
|
|
*
|
|
* .. note::
|
|
*
|
|
* When using multiple lines, make sure to use a command that is
|
|
* implemented with a single write syscall, like HEREDOC.
|
|
*
|
|
* Currently this is implemented only for post and mid context restore and
|
|
* these attributes can only be set before binding to the device.
|
|
*
|
|
* Max SR-IOV Virtual Functions
|
|
* ----------------------------
|
|
*
|
|
* This config allows to limit number of the Virtual Functions (VFs) that can
|
|
* be managed by the Physical Function (PF) driver, where value 0 disables the
|
|
* PF mode (no VFs).
|
|
*
|
|
* The default max_vfs config value is taken from the max_vfs modparam.
|
|
*
|
|
* How to enable PF with support with unlimited (up to HW limit) number of VFs::
|
|
*
|
|
* # echo unlimited > /sys/kernel/config/xe/0000:00:02.0/sriov/max_vfs
|
|
* # echo 0000:00:02.0 > /sys/bus/pci/drivers/xe/bind
|
|
*
|
|
* How to enable PF with support up to 3 VFs::
|
|
*
|
|
* # echo 3 > /sys/kernel/config/xe/0000:00:02.0/sriov/max_vfs
|
|
* # echo 0000:00:02.0 > /sys/bus/pci/drivers/xe/bind
|
|
*
|
|
* How to disable PF mode and always run as native::
|
|
*
|
|
* # echo 0 > /sys/kernel/config/xe/0000:00:02.0/sriov/max_vfs
|
|
* # echo 0000:00:02.0 > /sys/bus/pci/drivers/xe/bind
|
|
*
|
|
* This setting only takes effect when probing the device.
|
|
*
|
|
* Remove devices
|
|
* ==============
|
|
*
|
|
* The created device directories can be removed using ``rmdir``::
|
|
*
|
|
* # rmdir /sys/kernel/config/xe/0000:03:00.0/
|
|
*/
|
|
|
|
/* Similar to struct xe_bb, but not tied to HW (yet) */
|
|
struct wa_bb {
|
|
u32 *cs;
|
|
u32 len; /* in dwords */
|
|
};
|
|
|
|
struct xe_config_group_device {
|
|
struct config_group group;
|
|
struct config_group sriov;
|
|
|
|
struct xe_config_device {
|
|
u64 gt_types_allowed;
|
|
u64 engines_allowed;
|
|
struct wa_bb ctx_restore_post_bb[XE_ENGINE_CLASS_MAX];
|
|
struct wa_bb ctx_restore_mid_bb[XE_ENGINE_CLASS_MAX];
|
|
bool survivability_mode;
|
|
bool enable_psmi;
|
|
struct {
|
|
unsigned int max_vfs;
|
|
} sriov;
|
|
} config;
|
|
|
|
/* protects attributes */
|
|
struct mutex lock;
|
|
/* matching descriptor */
|
|
const struct xe_device_desc *desc;
|
|
/* tentative SR-IOV mode */
|
|
enum xe_sriov_mode mode;
|
|
};
|
|
|
|
static const struct xe_config_device device_defaults = {
|
|
.gt_types_allowed = U64_MAX,
|
|
.engines_allowed = U64_MAX,
|
|
.survivability_mode = false,
|
|
.enable_psmi = false,
|
|
.sriov = {
|
|
.max_vfs = UINT_MAX,
|
|
},
|
|
};
|
|
|
|
static void set_device_defaults(struct xe_config_device *config)
|
|
{
|
|
*config = device_defaults;
|
|
#ifdef CONFIG_PCI_IOV
|
|
config->sriov.max_vfs = xe_modparam.max_vfs;
|
|
#endif
|
|
}
|
|
|
|
struct engine_info {
|
|
const char *cls;
|
|
u64 mask;
|
|
enum xe_engine_class engine_class;
|
|
};
|
|
|
|
/* Some helpful macros to aid on the sizing of buffer allocation when parsing */
|
|
#define MAX_ENGINE_CLASS_CHARS 5
|
|
#define MAX_ENGINE_INSTANCE_CHARS 2
|
|
|
|
static const struct engine_info engine_info[] = {
|
|
{ .cls = "rcs", .mask = XE_HW_ENGINE_RCS_MASK, .engine_class = XE_ENGINE_CLASS_RENDER },
|
|
{ .cls = "bcs", .mask = XE_HW_ENGINE_BCS_MASK, .engine_class = XE_ENGINE_CLASS_COPY },
|
|
{ .cls = "vcs", .mask = XE_HW_ENGINE_VCS_MASK, .engine_class = XE_ENGINE_CLASS_VIDEO_DECODE },
|
|
{ .cls = "vecs", .mask = XE_HW_ENGINE_VECS_MASK, .engine_class = XE_ENGINE_CLASS_VIDEO_ENHANCE },
|
|
{ .cls = "ccs", .mask = XE_HW_ENGINE_CCS_MASK, .engine_class = XE_ENGINE_CLASS_COMPUTE },
|
|
{ .cls = "gsccs", .mask = XE_HW_ENGINE_GSCCS_MASK, .engine_class = XE_ENGINE_CLASS_OTHER },
|
|
};
|
|
|
|
static const struct {
|
|
const char *name;
|
|
enum xe_gt_type type;
|
|
} gt_types[] = {
|
|
{ .name = "primary", .type = XE_GT_TYPE_MAIN },
|
|
{ .name = "media", .type = XE_GT_TYPE_MEDIA },
|
|
};
|
|
|
|
static struct xe_config_group_device *to_xe_config_group_device(struct config_item *item)
|
|
{
|
|
return container_of(to_config_group(item), struct xe_config_group_device, group);
|
|
}
|
|
|
|
static struct xe_config_device *to_xe_config_device(struct config_item *item)
|
|
{
|
|
return &to_xe_config_group_device(item)->config;
|
|
}
|
|
|
|
static bool is_bound(struct xe_config_group_device *dev)
|
|
{
|
|
unsigned int domain, bus, slot, function;
|
|
struct pci_dev *pdev;
|
|
const char *name;
|
|
bool ret;
|
|
|
|
lockdep_assert_held(&dev->lock);
|
|
|
|
name = dev->group.cg_item.ci_name;
|
|
if (sscanf(name, "%x:%x:%x.%x", &domain, &bus, &slot, &function) != 4)
|
|
return false;
|
|
|
|
pdev = pci_get_domain_bus_and_slot(domain, bus, PCI_DEVFN(slot, function));
|
|
if (!pdev)
|
|
return false;
|
|
|
|
ret = pci_get_drvdata(pdev);
|
|
if (ret)
|
|
pci_dbg(pdev, "Already bound to driver\n");
|
|
|
|
pci_dev_put(pdev);
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t survivability_mode_show(struct config_item *item, char *page)
|
|
{
|
|
struct xe_config_device *dev = to_xe_config_device(item);
|
|
|
|
return sprintf(page, "%d\n", dev->survivability_mode);
|
|
}
|
|
|
|
static ssize_t survivability_mode_store(struct config_item *item, const char *page, size_t len)
|
|
{
|
|
struct xe_config_group_device *dev = to_xe_config_group_device(item);
|
|
bool survivability_mode;
|
|
int ret;
|
|
|
|
ret = kstrtobool(page, &survivability_mode);
|
|
if (ret)
|
|
return ret;
|
|
|
|
guard(mutex)(&dev->lock);
|
|
if (is_bound(dev))
|
|
return -EBUSY;
|
|
|
|
dev->config.survivability_mode = survivability_mode;
|
|
|
|
return len;
|
|
}
|
|
|
|
static ssize_t gt_types_allowed_show(struct config_item *item, char *page)
|
|
{
|
|
struct xe_config_device *dev = to_xe_config_device(item);
|
|
char *p = page;
|
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(gt_types); i++)
|
|
if (dev->gt_types_allowed & BIT_ULL(gt_types[i].type))
|
|
p += sprintf(p, "%s\n", gt_types[i].name);
|
|
|
|
return p - page;
|
|
}
|
|
|
|
static ssize_t gt_types_allowed_store(struct config_item *item, const char *page,
|
|
size_t len)
|
|
{
|
|
struct xe_config_group_device *dev = to_xe_config_group_device(item);
|
|
char *buf __free(kfree) = kstrdup(page, GFP_KERNEL);
|
|
char *p = buf;
|
|
u64 typemask = 0;
|
|
|
|
if (!buf)
|
|
return -ENOMEM;
|
|
|
|
while (p) {
|
|
char *typename = strsep(&p, ",\n");
|
|
bool matched = false;
|
|
|
|
if (typename[0] == '\0')
|
|
continue;
|
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(gt_types); i++) {
|
|
if (strcmp(typename, gt_types[i].name) == 0) {
|
|
typemask |= BIT(gt_types[i].type);
|
|
matched = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!matched)
|
|
return -EINVAL;
|
|
}
|
|
|
|
guard(mutex)(&dev->lock);
|
|
if (is_bound(dev))
|
|
return -EBUSY;
|
|
|
|
dev->config.gt_types_allowed = typemask;
|
|
|
|
return len;
|
|
}
|
|
|
|
static ssize_t engines_allowed_show(struct config_item *item, char *page)
|
|
{
|
|
struct xe_config_device *dev = to_xe_config_device(item);
|
|
char *p = page;
|
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(engine_info); i++) {
|
|
u64 mask = engine_info[i].mask;
|
|
|
|
if ((dev->engines_allowed & mask) == mask) {
|
|
p += sprintf(p, "%s*\n", engine_info[i].cls);
|
|
} else if (mask & dev->engines_allowed) {
|
|
u16 bit0 = __ffs64(mask), bit;
|
|
|
|
mask &= dev->engines_allowed;
|
|
|
|
for_each_set_bit(bit, (const unsigned long *)&mask, 64)
|
|
p += sprintf(p, "%s%u\n", engine_info[i].cls,
|
|
bit - bit0);
|
|
}
|
|
}
|
|
|
|
return p - page;
|
|
}
|
|
|
|
/*
|
|
* Lookup engine_info. If @mask is not NULL, reduce the mask according to the
|
|
* instance in @pattern.
|
|
*
|
|
* Examples of inputs:
|
|
* - lookup_engine_info("rcs0", &mask): return "rcs" entry from @engine_info and
|
|
* mask == BIT_ULL(XE_HW_ENGINE_RCS0)
|
|
* - lookup_engine_info("rcs*", &mask): return "rcs" entry from @engine_info and
|
|
* mask == XE_HW_ENGINE_RCS_MASK
|
|
* - lookup_engine_info("rcs", NULL): return "rcs" entry from @engine_info
|
|
*/
|
|
static const struct engine_info *lookup_engine_info(const char *pattern, u64 *mask)
|
|
{
|
|
for (size_t i = 0; i < ARRAY_SIZE(engine_info); i++) {
|
|
u8 instance;
|
|
u16 bit;
|
|
|
|
if (!str_has_prefix(pattern, engine_info[i].cls))
|
|
continue;
|
|
|
|
pattern += strlen(engine_info[i].cls);
|
|
if (!mask)
|
|
return *pattern ? NULL : &engine_info[i];
|
|
|
|
if (!strcmp(pattern, "*")) {
|
|
*mask = engine_info[i].mask;
|
|
return &engine_info[i];
|
|
}
|
|
|
|
if (kstrtou8(pattern, 10, &instance))
|
|
return NULL;
|
|
|
|
bit = __ffs64(engine_info[i].mask) + instance;
|
|
if (bit >= fls64(engine_info[i].mask))
|
|
return NULL;
|
|
|
|
*mask = BIT_ULL(bit);
|
|
return &engine_info[i];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int parse_engine(const char *s, const char *end_chars, u64 *mask,
|
|
const struct engine_info **pinfo)
|
|
{
|
|
char buf[MAX_ENGINE_CLASS_CHARS + MAX_ENGINE_INSTANCE_CHARS + 1];
|
|
const struct engine_info *info;
|
|
size_t len;
|
|
|
|
len = strcspn(s, end_chars);
|
|
if (len >= sizeof(buf))
|
|
return -EINVAL;
|
|
|
|
memcpy(buf, s, len);
|
|
buf[len] = '\0';
|
|
|
|
info = lookup_engine_info(buf, mask);
|
|
if (!info)
|
|
return -ENOENT;
|
|
|
|
if (pinfo)
|
|
*pinfo = info;
|
|
|
|
return len;
|
|
}
|
|
|
|
static ssize_t engines_allowed_store(struct config_item *item, const char *page,
|
|
size_t len)
|
|
{
|
|
struct xe_config_group_device *dev = to_xe_config_group_device(item);
|
|
ssize_t patternlen, p;
|
|
u64 mask, val = 0;
|
|
|
|
for (p = 0; p < len; p += patternlen + 1) {
|
|
patternlen = parse_engine(page + p, ",\n", &mask, NULL);
|
|
if (patternlen < 0)
|
|
return -EINVAL;
|
|
|
|
val |= mask;
|
|
}
|
|
|
|
guard(mutex)(&dev->lock);
|
|
if (is_bound(dev))
|
|
return -EBUSY;
|
|
|
|
dev->config.engines_allowed = val;
|
|
|
|
return len;
|
|
}
|
|
|
|
static ssize_t enable_psmi_show(struct config_item *item, char *page)
|
|
{
|
|
struct xe_config_device *dev = to_xe_config_device(item);
|
|
|
|
return sprintf(page, "%d\n", dev->enable_psmi);
|
|
}
|
|
|
|
static ssize_t enable_psmi_store(struct config_item *item, const char *page, size_t len)
|
|
{
|
|
struct xe_config_group_device *dev = to_xe_config_group_device(item);
|
|
bool val;
|
|
int ret;
|
|
|
|
ret = kstrtobool(page, &val);
|
|
if (ret)
|
|
return ret;
|
|
|
|
guard(mutex)(&dev->lock);
|
|
if (is_bound(dev))
|
|
return -EBUSY;
|
|
|
|
dev->config.enable_psmi = val;
|
|
|
|
return len;
|
|
}
|
|
|
|
static bool wa_bb_read_advance(bool dereference, char **p,
|
|
const char *append, size_t len,
|
|
size_t *max_size)
|
|
{
|
|
if (dereference) {
|
|
if (len >= *max_size)
|
|
return false;
|
|
*max_size -= len;
|
|
if (append)
|
|
memcpy(*p, append, len);
|
|
}
|
|
|
|
*p += len;
|
|
|
|
return true;
|
|
}
|
|
|
|
static ssize_t wa_bb_show(struct xe_config_group_device *dev,
|
|
struct wa_bb wa_bb[static XE_ENGINE_CLASS_MAX],
|
|
char *data, size_t sz)
|
|
{
|
|
char *p = data;
|
|
|
|
guard(mutex)(&dev->lock);
|
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(engine_info); i++) {
|
|
enum xe_engine_class ec = engine_info[i].engine_class;
|
|
size_t len;
|
|
|
|
if (!wa_bb[ec].len)
|
|
continue;
|
|
|
|
len = snprintf(p, sz, "%s:", engine_info[i].cls);
|
|
if (!wa_bb_read_advance(data, &p, NULL, len, &sz))
|
|
return -ENOBUFS;
|
|
|
|
for (size_t j = 0; j < wa_bb[ec].len; j++) {
|
|
len = snprintf(p, sz, " %08x", wa_bb[ec].cs[j]);
|
|
if (!wa_bb_read_advance(data, &p, NULL, len, &sz))
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
if (!wa_bb_read_advance(data, &p, "\n", 1, &sz))
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
if (!wa_bb_read_advance(data, &p, "", 1, &sz))
|
|
return -ENOBUFS;
|
|
|
|
/* Reserve one more to match check for '\0' */
|
|
if (!data)
|
|
p++;
|
|
|
|
return p - data;
|
|
}
|
|
|
|
static ssize_t ctx_restore_mid_bb_show(struct config_item *item, char *page)
|
|
{
|
|
struct xe_config_group_device *dev = to_xe_config_group_device(item);
|
|
|
|
return wa_bb_show(dev, dev->config.ctx_restore_mid_bb, page, SZ_4K);
|
|
}
|
|
|
|
static ssize_t ctx_restore_post_bb_show(struct config_item *item, char *page)
|
|
{
|
|
struct xe_config_group_device *dev = to_xe_config_group_device(item);
|
|
|
|
return wa_bb_show(dev, dev->config.ctx_restore_post_bb, page, SZ_4K);
|
|
}
|
|
|
|
static void wa_bb_append(struct wa_bb *wa_bb, u32 val)
|
|
{
|
|
if (wa_bb->cs)
|
|
wa_bb->cs[wa_bb->len] = val;
|
|
|
|
wa_bb->len++;
|
|
}
|
|
|
|
static ssize_t parse_hex(const char *line, u32 *pval)
|
|
{
|
|
char numstr[12];
|
|
const char *p;
|
|
ssize_t numlen;
|
|
|
|
p = line + strspn(line, " \t");
|
|
if (!*p || *p == '\n')
|
|
return 0;
|
|
|
|
numlen = strcspn(p, " \t\n");
|
|
if (!numlen || numlen >= sizeof(numstr) - 1)
|
|
return -EINVAL;
|
|
|
|
memcpy(numstr, p, numlen);
|
|
numstr[numlen] = '\0';
|
|
p += numlen;
|
|
|
|
if (kstrtou32(numstr, 16, pval))
|
|
return -EINVAL;
|
|
|
|
return p - line;
|
|
}
|
|
|
|
/*
|
|
* Parse lines with the format
|
|
*
|
|
* <engine-class> cmd <u32> <u32...>
|
|
* <engine-class> reg <u32_addr> <u32_val>
|
|
*
|
|
* and optionally save them in @wa_bb[i].cs is non-NULL.
|
|
*
|
|
* Return the number of dwords parsed.
|
|
*/
|
|
static ssize_t parse_wa_bb_lines(const char *lines,
|
|
struct wa_bb wa_bb[static XE_ENGINE_CLASS_MAX])
|
|
{
|
|
ssize_t dwords = 0, ret;
|
|
const char *p;
|
|
|
|
for (p = lines; *p; p++) {
|
|
const struct engine_info *info = NULL;
|
|
u32 val, val2;
|
|
|
|
/* Also allow empty lines */
|
|
p += strspn(p, " \t\n");
|
|
if (!*p)
|
|
break;
|
|
|
|
ret = parse_engine(p, " \t\n", NULL, &info);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
p += ret;
|
|
p += strspn(p, " \t");
|
|
|
|
if (str_has_prefix(p, "cmd")) {
|
|
for (p += strlen("cmd"); *p;) {
|
|
ret = parse_hex(p, &val);
|
|
if (ret < 0)
|
|
return -EINVAL;
|
|
if (!ret)
|
|
break;
|
|
|
|
p += ret;
|
|
dwords++;
|
|
wa_bb_append(&wa_bb[info->engine_class], val);
|
|
}
|
|
} else if (str_has_prefix(p, "reg")) {
|
|
p += strlen("reg");
|
|
ret = parse_hex(p, &val);
|
|
if (ret <= 0)
|
|
return -EINVAL;
|
|
|
|
p += ret;
|
|
ret = parse_hex(p, &val2);
|
|
if (ret <= 0)
|
|
return -EINVAL;
|
|
|
|
p += ret;
|
|
dwords += 3;
|
|
wa_bb_append(&wa_bb[info->engine_class],
|
|
MI_LOAD_REGISTER_IMM | MI_LRI_NUM_REGS(1));
|
|
wa_bb_append(&wa_bb[info->engine_class], val);
|
|
wa_bb_append(&wa_bb[info->engine_class], val2);
|
|
} else {
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
return dwords;
|
|
}
|
|
|
|
static ssize_t wa_bb_store(struct wa_bb wa_bb[static XE_ENGINE_CLASS_MAX],
|
|
struct xe_config_group_device *dev,
|
|
const char *page, size_t len)
|
|
{
|
|
/* tmp_wa_bb must match wa_bb's size */
|
|
struct wa_bb tmp_wa_bb[XE_ENGINE_CLASS_MAX] = { };
|
|
ssize_t count, class;
|
|
u32 *tmp;
|
|
|
|
/* 1. Count dwords - wa_bb[i].cs is NULL for all classes */
|
|
count = parse_wa_bb_lines(page, tmp_wa_bb);
|
|
if (count < 0)
|
|
return count;
|
|
|
|
guard(mutex)(&dev->lock);
|
|
|
|
if (is_bound(dev))
|
|
return -EBUSY;
|
|
|
|
/*
|
|
* 2. Allocate a u32 array and set the pointers to the right positions
|
|
* according to the length of each class' wa_bb
|
|
*/
|
|
tmp = krealloc(wa_bb[0].cs, count * sizeof(u32), GFP_KERNEL);
|
|
if (!tmp)
|
|
return -ENOMEM;
|
|
|
|
if (!count) {
|
|
memset(wa_bb, 0, sizeof(tmp_wa_bb));
|
|
return len;
|
|
}
|
|
|
|
for (class = 0, count = 0; class < XE_ENGINE_CLASS_MAX; ++class) {
|
|
tmp_wa_bb[class].cs = tmp + count;
|
|
count += tmp_wa_bb[class].len;
|
|
tmp_wa_bb[class].len = 0;
|
|
}
|
|
|
|
/* 3. Parse wa_bb lines again, this time saving the values */
|
|
count = parse_wa_bb_lines(page, tmp_wa_bb);
|
|
if (count < 0)
|
|
return count;
|
|
|
|
memcpy(wa_bb, tmp_wa_bb, sizeof(tmp_wa_bb));
|
|
|
|
return len;
|
|
}
|
|
|
|
static ssize_t ctx_restore_mid_bb_store(struct config_item *item,
|
|
const char *data, size_t sz)
|
|
{
|
|
struct xe_config_group_device *dev = to_xe_config_group_device(item);
|
|
|
|
return wa_bb_store(dev->config.ctx_restore_mid_bb, dev, data, sz);
|
|
}
|
|
|
|
static ssize_t ctx_restore_post_bb_store(struct config_item *item,
|
|
const char *data, size_t sz)
|
|
{
|
|
struct xe_config_group_device *dev = to_xe_config_group_device(item);
|
|
|
|
return wa_bb_store(dev->config.ctx_restore_post_bb, dev, data, sz);
|
|
}
|
|
|
|
CONFIGFS_ATTR(, ctx_restore_mid_bb);
|
|
CONFIGFS_ATTR(, ctx_restore_post_bb);
|
|
CONFIGFS_ATTR(, enable_psmi);
|
|
CONFIGFS_ATTR(, engines_allowed);
|
|
CONFIGFS_ATTR(, gt_types_allowed);
|
|
CONFIGFS_ATTR(, survivability_mode);
|
|
|
|
static struct configfs_attribute *xe_config_device_attrs[] = {
|
|
&attr_ctx_restore_mid_bb,
|
|
&attr_ctx_restore_post_bb,
|
|
&attr_enable_psmi,
|
|
&attr_engines_allowed,
|
|
&attr_gt_types_allowed,
|
|
&attr_survivability_mode,
|
|
NULL,
|
|
};
|
|
|
|
static void xe_config_device_release(struct config_item *item)
|
|
{
|
|
struct xe_config_group_device *dev = to_xe_config_group_device(item);
|
|
|
|
mutex_destroy(&dev->lock);
|
|
|
|
kfree(dev->config.ctx_restore_mid_bb[0].cs);
|
|
kfree(dev->config.ctx_restore_post_bb[0].cs);
|
|
kfree(dev);
|
|
}
|
|
|
|
static struct configfs_item_operations xe_config_device_ops = {
|
|
.release = xe_config_device_release,
|
|
};
|
|
|
|
static bool xe_config_device_is_visible(struct config_item *item,
|
|
struct configfs_attribute *attr, int n)
|
|
{
|
|
struct xe_config_group_device *dev = to_xe_config_group_device(item);
|
|
|
|
if (attr == &attr_survivability_mode) {
|
|
if (!dev->desc->is_dgfx || dev->desc->platform < XE_BATTLEMAGE)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static struct configfs_group_operations xe_config_device_group_ops = {
|
|
.is_visible = xe_config_device_is_visible,
|
|
};
|
|
|
|
static const struct config_item_type xe_config_device_type = {
|
|
.ct_item_ops = &xe_config_device_ops,
|
|
.ct_group_ops = &xe_config_device_group_ops,
|
|
.ct_attrs = xe_config_device_attrs,
|
|
.ct_owner = THIS_MODULE,
|
|
};
|
|
|
|
static ssize_t sriov_max_vfs_show(struct config_item *item, char *page)
|
|
{
|
|
struct xe_config_group_device *dev = to_xe_config_group_device(item->ci_parent);
|
|
|
|
guard(mutex)(&dev->lock);
|
|
|
|
if (dev->config.sriov.max_vfs == UINT_MAX)
|
|
return sprintf(page, "%s\n", "unlimited");
|
|
else
|
|
return sprintf(page, "%u\n", dev->config.sriov.max_vfs);
|
|
}
|
|
|
|
static ssize_t sriov_max_vfs_store(struct config_item *item, const char *page, size_t len)
|
|
{
|
|
struct xe_config_group_device *dev = to_xe_config_group_device(item->ci_parent);
|
|
unsigned int max_vfs;
|
|
int ret;
|
|
|
|
guard(mutex)(&dev->lock);
|
|
|
|
if (is_bound(dev))
|
|
return -EBUSY;
|
|
|
|
ret = kstrtouint(page, 0, &max_vfs);
|
|
if (ret) {
|
|
if (!sysfs_streq(page, "unlimited"))
|
|
return ret;
|
|
max_vfs = UINT_MAX;
|
|
}
|
|
|
|
dev->config.sriov.max_vfs = max_vfs;
|
|
return len;
|
|
}
|
|
|
|
CONFIGFS_ATTR(sriov_, max_vfs);
|
|
|
|
static struct configfs_attribute *xe_config_sriov_attrs[] = {
|
|
&sriov_attr_max_vfs,
|
|
NULL,
|
|
};
|
|
|
|
static bool xe_config_sriov_is_visible(struct config_item *item,
|
|
struct configfs_attribute *attr, int n)
|
|
{
|
|
struct xe_config_group_device *dev = to_xe_config_group_device(item->ci_parent);
|
|
|
|
if (attr == &sriov_attr_max_vfs && dev->mode != XE_SRIOV_MODE_PF)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static struct configfs_group_operations xe_config_sriov_group_ops = {
|
|
.is_visible = xe_config_sriov_is_visible,
|
|
};
|
|
|
|
static const struct config_item_type xe_config_sriov_type = {
|
|
.ct_owner = THIS_MODULE,
|
|
.ct_group_ops = &xe_config_sriov_group_ops,
|
|
.ct_attrs = xe_config_sriov_attrs,
|
|
};
|
|
|
|
static const struct xe_device_desc *xe_match_desc(struct pci_dev *pdev)
|
|
{
|
|
struct device_driver *driver = driver_find("xe", &pci_bus_type);
|
|
struct pci_driver *drv = to_pci_driver(driver);
|
|
const struct pci_device_id *ids = drv ? drv->id_table : NULL;
|
|
const struct pci_device_id *found = pci_match_id(ids, pdev);
|
|
|
|
return found ? (const void *)found->driver_data : NULL;
|
|
}
|
|
|
|
static struct pci_dev *get_physfn_instead(struct pci_dev *virtfn)
|
|
{
|
|
struct pci_dev *physfn = pci_physfn(virtfn);
|
|
|
|
pci_dev_get(physfn);
|
|
pci_dev_put(virtfn);
|
|
return physfn;
|
|
}
|
|
|
|
static struct config_group *xe_config_make_device_group(struct config_group *group,
|
|
const char *name)
|
|
{
|
|
unsigned int domain, bus, slot, function;
|
|
struct xe_config_group_device *dev;
|
|
const struct xe_device_desc *match;
|
|
enum xe_sriov_mode mode;
|
|
struct pci_dev *pdev;
|
|
char canonical[16];
|
|
int vfnumber = 0;
|
|
int ret;
|
|
|
|
ret = sscanf(name, "%x:%x:%x.%x", &domain, &bus, &slot, &function);
|
|
if (ret != 4)
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
ret = scnprintf(canonical, sizeof(canonical), "%04x:%02x:%02x.%d", domain, bus,
|
|
PCI_SLOT(PCI_DEVFN(slot, function)),
|
|
PCI_FUNC(PCI_DEVFN(slot, function)));
|
|
if (ret != 12 || strcmp(name, canonical))
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
pdev = pci_get_domain_bus_and_slot(domain, bus, PCI_DEVFN(slot, function));
|
|
mode = pdev ? dev_is_pf(&pdev->dev) ?
|
|
XE_SRIOV_MODE_PF : XE_SRIOV_MODE_NONE : XE_SRIOV_MODE_VF;
|
|
|
|
if (!pdev && function)
|
|
pdev = pci_get_domain_bus_and_slot(domain, bus, PCI_DEVFN(slot, 0));
|
|
if (!pdev && slot)
|
|
pdev = pci_get_domain_bus_and_slot(domain, bus, PCI_DEVFN(0, 0));
|
|
if (!pdev)
|
|
return ERR_PTR(-ENODEV);
|
|
|
|
if (PCI_DEVFN(slot, function) != pdev->devfn) {
|
|
pdev = get_physfn_instead(pdev);
|
|
vfnumber = PCI_DEVFN(slot, function) - pdev->devfn;
|
|
if (!dev_is_pf(&pdev->dev) || vfnumber > pci_sriov_get_totalvfs(pdev)) {
|
|
pci_dev_put(pdev);
|
|
return ERR_PTR(-ENODEV);
|
|
}
|
|
}
|
|
|
|
match = xe_match_desc(pdev);
|
|
if (match && vfnumber && !match->has_sriov) {
|
|
pci_info(pdev, "xe driver does not support VFs on this device\n");
|
|
match = NULL;
|
|
} else if (!match) {
|
|
pci_info(pdev, "xe driver does not support configuration of this device\n");
|
|
}
|
|
|
|
pci_dev_put(pdev);
|
|
|
|
if (!match)
|
|
return ERR_PTR(-ENOENT);
|
|
|
|
dev = kzalloc_obj(*dev);
|
|
if (!dev)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
dev->desc = match;
|
|
dev->mode = match->has_sriov ? mode : XE_SRIOV_MODE_NONE;
|
|
|
|
set_device_defaults(&dev->config);
|
|
|
|
config_group_init_type_name(&dev->group, name, &xe_config_device_type);
|
|
if (dev->mode != XE_SRIOV_MODE_NONE) {
|
|
config_group_init_type_name(&dev->sriov, "sriov", &xe_config_sriov_type);
|
|
configfs_add_default_group(&dev->sriov, &dev->group);
|
|
}
|
|
|
|
mutex_init(&dev->lock);
|
|
|
|
return &dev->group;
|
|
}
|
|
|
|
static struct configfs_group_operations xe_config_group_ops = {
|
|
.make_group = xe_config_make_device_group,
|
|
};
|
|
|
|
static const struct config_item_type xe_configfs_type = {
|
|
.ct_group_ops = &xe_config_group_ops,
|
|
.ct_owner = THIS_MODULE,
|
|
};
|
|
|
|
static struct configfs_subsystem xe_configfs = {
|
|
.su_group = {
|
|
.cg_item = {
|
|
.ci_namebuf = "xe",
|
|
.ci_type = &xe_configfs_type,
|
|
},
|
|
},
|
|
};
|
|
|
|
static struct xe_config_group_device *find_xe_config_group_device(struct pci_dev *pdev)
|
|
{
|
|
struct config_item *item;
|
|
|
|
mutex_lock(&xe_configfs.su_mutex);
|
|
item = config_group_find_item(&xe_configfs.su_group, pci_name(pdev));
|
|
mutex_unlock(&xe_configfs.su_mutex);
|
|
|
|
if (!item)
|
|
return NULL;
|
|
|
|
return to_xe_config_group_device(item);
|
|
}
|
|
|
|
static void dump_custom_dev_config(struct pci_dev *pdev,
|
|
struct xe_config_group_device *dev)
|
|
{
|
|
#define PRI_CUSTOM_ATTR(fmt_, attr_) do { \
|
|
if (dev->config.attr_ != device_defaults.attr_) \
|
|
pci_info(pdev, "configfs: " __stringify(attr_) " = " fmt_ "\n", \
|
|
dev->config.attr_); \
|
|
} while (0)
|
|
|
|
PRI_CUSTOM_ATTR("%llx", gt_types_allowed);
|
|
PRI_CUSTOM_ATTR("%llx", engines_allowed);
|
|
PRI_CUSTOM_ATTR("%d", enable_psmi);
|
|
PRI_CUSTOM_ATTR("%d", survivability_mode);
|
|
|
|
#undef PRI_CUSTOM_ATTR
|
|
}
|
|
|
|
/**
|
|
* xe_configfs_check_device() - Test if device was configured by configfs
|
|
* @pdev: the &pci_dev device to test
|
|
*
|
|
* Try to find the configfs group that belongs to the specified pci device
|
|
* and print a diagnostic message if different than the default value.
|
|
*/
|
|
void xe_configfs_check_device(struct pci_dev *pdev)
|
|
{
|
|
struct xe_config_group_device *dev = find_xe_config_group_device(pdev);
|
|
|
|
if (!dev)
|
|
return;
|
|
|
|
/* memcmp here is safe as both are zero-initialized */
|
|
if (memcmp(&dev->config, &device_defaults, sizeof(dev->config))) {
|
|
pci_info(pdev, "Found custom settings in configfs\n");
|
|
dump_custom_dev_config(pdev, dev);
|
|
}
|
|
|
|
config_group_put(&dev->group);
|
|
}
|
|
|
|
/**
|
|
* xe_configfs_get_survivability_mode - get configfs survivability mode attribute
|
|
* @pdev: pci device
|
|
*
|
|
* Return: survivability_mode attribute in configfs
|
|
*/
|
|
bool xe_configfs_get_survivability_mode(struct pci_dev *pdev)
|
|
{
|
|
struct xe_config_group_device *dev = find_xe_config_group_device(pdev);
|
|
bool mode;
|
|
|
|
if (!dev)
|
|
return device_defaults.survivability_mode;
|
|
|
|
mode = dev->config.survivability_mode;
|
|
config_group_put(&dev->group);
|
|
|
|
return mode;
|
|
}
|
|
|
|
static u64 get_gt_types_allowed(struct pci_dev *pdev)
|
|
{
|
|
struct xe_config_group_device *dev = find_xe_config_group_device(pdev);
|
|
u64 mask;
|
|
|
|
if (!dev)
|
|
return device_defaults.gt_types_allowed;
|
|
|
|
mask = dev->config.gt_types_allowed;
|
|
config_group_put(&dev->group);
|
|
|
|
return mask;
|
|
}
|
|
|
|
/**
|
|
* xe_configfs_primary_gt_allowed - determine whether primary GTs are supported
|
|
* @pdev: pci device
|
|
*
|
|
* Return: True if primary GTs are enabled, false if they have been disabled via
|
|
* configfs.
|
|
*/
|
|
bool xe_configfs_primary_gt_allowed(struct pci_dev *pdev)
|
|
{
|
|
return get_gt_types_allowed(pdev) & BIT_ULL(XE_GT_TYPE_MAIN);
|
|
}
|
|
|
|
/**
|
|
* xe_configfs_media_gt_allowed - determine whether media GTs are supported
|
|
* @pdev: pci device
|
|
*
|
|
* Return: True if the media GTs are enabled, false if they have been disabled
|
|
* via configfs.
|
|
*/
|
|
bool xe_configfs_media_gt_allowed(struct pci_dev *pdev)
|
|
{
|
|
return get_gt_types_allowed(pdev) & BIT_ULL(XE_GT_TYPE_MEDIA);
|
|
}
|
|
|
|
/**
|
|
* xe_configfs_get_engines_allowed - get engine allowed mask from configfs
|
|
* @pdev: pci device
|
|
*
|
|
* Return: engine mask with allowed engines set in configfs
|
|
*/
|
|
u64 xe_configfs_get_engines_allowed(struct pci_dev *pdev)
|
|
{
|
|
struct xe_config_group_device *dev = find_xe_config_group_device(pdev);
|
|
u64 engines_allowed;
|
|
|
|
if (!dev)
|
|
return device_defaults.engines_allowed;
|
|
|
|
engines_allowed = dev->config.engines_allowed;
|
|
config_group_put(&dev->group);
|
|
|
|
return engines_allowed;
|
|
}
|
|
|
|
/**
|
|
* xe_configfs_get_psmi_enabled - get configfs enable_psmi setting
|
|
* @pdev: pci device
|
|
*
|
|
* Return: enable_psmi setting in configfs
|
|
*/
|
|
bool xe_configfs_get_psmi_enabled(struct pci_dev *pdev)
|
|
{
|
|
struct xe_config_group_device *dev = find_xe_config_group_device(pdev);
|
|
bool ret;
|
|
|
|
if (!dev)
|
|
return false;
|
|
|
|
ret = dev->config.enable_psmi;
|
|
config_group_put(&dev->group);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* xe_configfs_get_ctx_restore_mid_bb - get configfs ctx_restore_mid_bb setting
|
|
* @pdev: pci device
|
|
* @class: hw engine class
|
|
* @cs: pointer to the bb to use - only valid during probe
|
|
*
|
|
* Return: Number of dwords used in the mid_ctx_restore setting in configfs
|
|
*/
|
|
u32 xe_configfs_get_ctx_restore_mid_bb(struct pci_dev *pdev,
|
|
enum xe_engine_class class,
|
|
const u32 **cs)
|
|
{
|
|
struct xe_config_group_device *dev = find_xe_config_group_device(pdev);
|
|
u32 len;
|
|
|
|
if (!dev)
|
|
return 0;
|
|
|
|
if (cs)
|
|
*cs = dev->config.ctx_restore_mid_bb[class].cs;
|
|
|
|
len = dev->config.ctx_restore_mid_bb[class].len;
|
|
config_group_put(&dev->group);
|
|
|
|
return len;
|
|
}
|
|
|
|
/**
|
|
* xe_configfs_get_ctx_restore_post_bb - get configfs ctx_restore_post_bb setting
|
|
* @pdev: pci device
|
|
* @class: hw engine class
|
|
* @cs: pointer to the bb to use - only valid during probe
|
|
*
|
|
* Return: Number of dwords used in the post_ctx_restore setting in configfs
|
|
*/
|
|
u32 xe_configfs_get_ctx_restore_post_bb(struct pci_dev *pdev,
|
|
enum xe_engine_class class,
|
|
const u32 **cs)
|
|
{
|
|
struct xe_config_group_device *dev = find_xe_config_group_device(pdev);
|
|
u32 len;
|
|
|
|
if (!dev)
|
|
return 0;
|
|
|
|
*cs = dev->config.ctx_restore_post_bb[class].cs;
|
|
len = dev->config.ctx_restore_post_bb[class].len;
|
|
config_group_put(&dev->group);
|
|
|
|
return len;
|
|
}
|
|
|
|
#ifdef CONFIG_PCI_IOV
|
|
/**
|
|
* xe_configfs_get_max_vfs() - Get number of VFs that could be managed
|
|
* @pdev: the &pci_dev device
|
|
*
|
|
* Find the configfs group that belongs to the PCI device and return maximum
|
|
* number of Virtual Functions (VFs) that could be managed by this device.
|
|
* If configfs group is not present, use value of max_vfs module parameter.
|
|
*
|
|
* Return: maximum number of VFs that could be managed.
|
|
*/
|
|
unsigned int xe_configfs_get_max_vfs(struct pci_dev *pdev)
|
|
{
|
|
struct xe_config_group_device *dev = find_xe_config_group_device(pdev);
|
|
unsigned int max_vfs;
|
|
|
|
if (!dev)
|
|
return xe_modparam.max_vfs;
|
|
|
|
scoped_guard(mutex, &dev->lock)
|
|
max_vfs = dev->config.sriov.max_vfs;
|
|
|
|
config_group_put(&dev->group);
|
|
|
|
return max_vfs;
|
|
}
|
|
#endif
|
|
|
|
int __init xe_configfs_init(void)
|
|
{
|
|
int ret;
|
|
|
|
config_group_init(&xe_configfs.su_group);
|
|
mutex_init(&xe_configfs.su_mutex);
|
|
ret = configfs_register_subsystem(&xe_configfs);
|
|
if (ret) {
|
|
mutex_destroy(&xe_configfs.su_mutex);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void xe_configfs_exit(void)
|
|
{
|
|
configfs_unregister_subsystem(&xe_configfs);
|
|
mutex_destroy(&xe_configfs.su_mutex);
|
|
}
|