mirror of
https://github.com/torvalds/linux.git
synced 2026-05-23 06:31:58 +02:00
drm/xe/configfs: Add post context restore bb
Allow the user to specify commands to execute during a context restore. Currently it's possible to parse 2 types of actions: - cmd: the instructions are added as is to the bb - reg: just use the address and value, without worrying about encoding the right LRI instruction. This is possibly the most useful use case, so added a dedicated action for that. This also prepares for future BBs: mid context restore and rc6 context restore that can re-use the same parsing functions. Reviewed-by: Raag Jadav <raag.jadav@intel.com> Link: https://lore.kernel.org/r/20250916-wa-bb-cmds-v5-4-306bddbc15da@intel.com Signed-off-by: Lucas De Marchi <lucas.demarchi@intel.com>
This commit is contained in:
parent
6c6988c5e0
commit
39ac06f700
|
|
@ -4,6 +4,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/configfs.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/find.h>
|
||||
|
|
@ -12,6 +13,7 @@
|
|||
#include <linux/pci.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include "instructions/xe_mi_commands.h"
|
||||
#include "xe_configfs.h"
|
||||
#include "xe_hw_engine_types.h"
|
||||
#include "xe_module.h"
|
||||
|
|
@ -115,6 +117,37 @@
|
|||
*
|
||||
* 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.
|
||||
*
|
||||
* Currently this is implemented only for post context restore. Examples:
|
||||
*
|
||||
* #. Execute a LRI command to write 0xDEADBEEF to register 0x4f10::
|
||||
*
|
||||
* # echo 'rcs cmd 11000001 4F100 DEADBEEF' \
|
||||
* > /sys/kernel/config/xe/0000:03:00.0/ctx_restore_post_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.
|
||||
*
|
||||
* This attribute can only be set before binding to the device.
|
||||
*
|
||||
* Remove devices
|
||||
* ==============
|
||||
*
|
||||
|
|
@ -123,11 +156,18 @@
|
|||
* # 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 xe_config_device {
|
||||
u64 engines_allowed;
|
||||
struct wa_bb ctx_restore_post_bb[XE_ENGINE_CLASS_MAX];
|
||||
bool survivability_mode;
|
||||
bool enable_psmi;
|
||||
} config;
|
||||
|
|
@ -371,11 +411,233 @@ static ssize_t enable_psmi_store(struct config_item *item, const char *page, siz
|
|||
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_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_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_post_bb);
|
||||
CONFIGFS_ATTR(, enable_psmi);
|
||||
CONFIGFS_ATTR(, engines_allowed);
|
||||
CONFIGFS_ATTR(, survivability_mode);
|
||||
|
||||
static struct configfs_attribute *xe_config_device_attrs[] = {
|
||||
&attr_ctx_restore_post_bb,
|
||||
&attr_enable_psmi,
|
||||
&attr_engines_allowed,
|
||||
&attr_survivability_mode,
|
||||
|
|
@ -387,6 +649,8 @@ 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_post_bb[0].cs);
|
||||
kfree(dev);
|
||||
}
|
||||
|
||||
|
|
@ -636,14 +900,26 @@ bool xe_configfs_get_psmi_enabled(struct pci_dev *pdev)
|
|||
/**
|
||||
* 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: post_ctx_restore setting in configfs
|
||||
* 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)
|
||||
{
|
||||
return 0;
|
||||
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;
|
||||
}
|
||||
|
||||
int __init xe_configfs_init(void)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user