Skip to content

Commit 39ac06f

Browse files
author
Lucas De Marchi
committed
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 <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Lucas De Marchi <[email protected]>
1 parent 6c6988c commit 39ac06f

1 file changed

Lines changed: 278 additions & 2 deletions

File tree

drivers/gpu/drm/xe/xe_configfs.c

Lines changed: 278 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55

66
#include <linux/bitops.h>
7+
#include <linux/ctype.h>
78
#include <linux/configfs.h>
89
#include <linux/cleanup.h>
910
#include <linux/find.h>
@@ -12,6 +13,7 @@
1213
#include <linux/pci.h>
1314
#include <linux/string.h>
1415

16+
#include "instructions/xe_mi_commands.h"
1517
#include "xe_configfs.h"
1618
#include "xe_hw_engine_types.h"
1719
#include "xe_module.h"
@@ -115,6 +117,37 @@
115117
*
116118
* This attribute can only be set before binding to the device.
117119
*
120+
* Context restore BB
121+
* ------------------
122+
*
123+
* Allow to execute a batch buffer during any context switches. When the
124+
* GPU is restoring the context, it executes additional commands. It's useful
125+
* for testing additional workarounds and validating certain HW behaviors: it's
126+
* not intended for normal execution and will taint the kernel with TAINT_TEST
127+
* when used.
128+
*
129+
* Currently this is implemented only for post context restore. Examples:
130+
*
131+
* #. Execute a LRI command to write 0xDEADBEEF to register 0x4f10::
132+
*
133+
* # echo 'rcs cmd 11000001 4F100 DEADBEEF' \
134+
* > /sys/kernel/config/xe/0000:03:00.0/ctx_restore_post_bb
135+
*
136+
* #. Load certain values in a couple of registers (it can be used as a simpler
137+
* alternative to the `cmd`) action::
138+
*
139+
* # cat > /sys/kernel/config/xe/0000:03:00.0/ctx_restore_post_bb <<EOF
140+
* rcs reg 4F100 DEADBEEF
141+
* rcs reg 4F104 FFFFFFFF
142+
* EOF
143+
*
144+
* .. note::
145+
*
146+
* When using multiple lines, make sure to use a command that is
147+
* implemented with a single write syscall, like HEREDOC.
148+
*
149+
* This attribute can only be set before binding to the device.
150+
*
118151
* Remove devices
119152
* ==============
120153
*
@@ -123,11 +156,18 @@
123156
* # rmdir /sys/kernel/config/xe/0000:03:00.0/
124157
*/
125158

159+
/* Similar to struct xe_bb, but not tied to HW (yet) */
160+
struct wa_bb {
161+
u32 *cs;
162+
u32 len; /* in dwords */
163+
};
164+
126165
struct xe_config_group_device {
127166
struct config_group group;
128167

129168
struct xe_config_device {
130169
u64 engines_allowed;
170+
struct wa_bb ctx_restore_post_bb[XE_ENGINE_CLASS_MAX];
131171
bool survivability_mode;
132172
bool enable_psmi;
133173
} config;
@@ -371,11 +411,233 @@ static ssize_t enable_psmi_store(struct config_item *item, const char *page, siz
371411
return len;
372412
}
373413

414+
static bool wa_bb_read_advance(bool dereference, char **p,
415+
const char *append, size_t len,
416+
size_t *max_size)
417+
{
418+
if (dereference) {
419+
if (len >= *max_size)
420+
return false;
421+
*max_size -= len;
422+
if (append)
423+
memcpy(*p, append, len);
424+
}
425+
426+
*p += len;
427+
428+
return true;
429+
}
430+
431+
static ssize_t wa_bb_show(struct xe_config_group_device *dev,
432+
struct wa_bb wa_bb[static XE_ENGINE_CLASS_MAX],
433+
char *data, size_t sz)
434+
{
435+
char *p = data;
436+
437+
guard(mutex)(&dev->lock);
438+
439+
for (size_t i = 0; i < ARRAY_SIZE(engine_info); i++) {
440+
enum xe_engine_class ec = engine_info[i].engine_class;
441+
size_t len;
442+
443+
if (!wa_bb[ec].len)
444+
continue;
445+
446+
len = snprintf(p, sz, "%s:", engine_info[i].cls);
447+
if (!wa_bb_read_advance(data, &p, NULL, len, &sz))
448+
return -ENOBUFS;
449+
450+
for (size_t j = 0; j < wa_bb[ec].len; j++) {
451+
len = snprintf(p, sz, " %08x", wa_bb[ec].cs[j]);
452+
if (!wa_bb_read_advance(data, &p, NULL, len, &sz))
453+
return -ENOBUFS;
454+
}
455+
456+
if (!wa_bb_read_advance(data, &p, "\n", 1, &sz))
457+
return -ENOBUFS;
458+
}
459+
460+
if (!wa_bb_read_advance(data, &p, "", 1, &sz))
461+
return -ENOBUFS;
462+
463+
/* Reserve one more to match check for '\0' */
464+
if (!data)
465+
p++;
466+
467+
return p - data;
468+
}
469+
470+
static ssize_t ctx_restore_post_bb_show(struct config_item *item, char *page)
471+
{
472+
struct xe_config_group_device *dev = to_xe_config_group_device(item);
473+
474+
return wa_bb_show(dev, dev->config.ctx_restore_post_bb, page, SZ_4K);
475+
}
476+
477+
static void wa_bb_append(struct wa_bb *wa_bb, u32 val)
478+
{
479+
if (wa_bb->cs)
480+
wa_bb->cs[wa_bb->len] = val;
481+
482+
wa_bb->len++;
483+
}
484+
485+
static ssize_t parse_hex(const char *line, u32 *pval)
486+
{
487+
char numstr[12];
488+
const char *p;
489+
ssize_t numlen;
490+
491+
p = line + strspn(line, " \t");
492+
if (!*p || *p == '\n')
493+
return 0;
494+
495+
numlen = strcspn(p, " \t\n");
496+
if (!numlen || numlen >= sizeof(numstr) - 1)
497+
return -EINVAL;
498+
499+
memcpy(numstr, p, numlen);
500+
numstr[numlen] = '\0';
501+
p += numlen;
502+
503+
if (kstrtou32(numstr, 16, pval))
504+
return -EINVAL;
505+
506+
return p - line;
507+
}
508+
509+
/*
510+
* Parse lines with the format
511+
*
512+
* <engine-class> cmd <u32> <u32...>
513+
* <engine-class> reg <u32_addr> <u32_val>
514+
*
515+
* and optionally save them in @wa_bb[i].cs is non-NULL.
516+
*
517+
* Return the number of dwords parsed.
518+
*/
519+
static ssize_t parse_wa_bb_lines(const char *lines,
520+
struct wa_bb wa_bb[static XE_ENGINE_CLASS_MAX])
521+
{
522+
ssize_t dwords = 0, ret;
523+
const char *p;
524+
525+
for (p = lines; *p; p++) {
526+
const struct engine_info *info = NULL;
527+
u32 val, val2;
528+
529+
/* Also allow empty lines */
530+
p += strspn(p, " \t\n");
531+
if (!*p)
532+
break;
533+
534+
ret = parse_engine(p, " \t\n", NULL, &info);
535+
if (ret < 0)
536+
return ret;
537+
538+
p += ret;
539+
p += strspn(p, " \t");
540+
541+
if (str_has_prefix(p, "cmd")) {
542+
for (p += strlen("cmd"); *p;) {
543+
ret = parse_hex(p, &val);
544+
if (ret < 0)
545+
return -EINVAL;
546+
if (!ret)
547+
break;
548+
549+
p += ret;
550+
dwords++;
551+
wa_bb_append(&wa_bb[info->engine_class], val);
552+
}
553+
} else if (str_has_prefix(p, "reg")) {
554+
p += strlen("reg");
555+
ret = parse_hex(p, &val);
556+
if (ret <= 0)
557+
return -EINVAL;
558+
559+
p += ret;
560+
ret = parse_hex(p, &val2);
561+
if (ret <= 0)
562+
return -EINVAL;
563+
564+
p += ret;
565+
dwords += 3;
566+
wa_bb_append(&wa_bb[info->engine_class],
567+
MI_LOAD_REGISTER_IMM | MI_LRI_NUM_REGS(1));
568+
wa_bb_append(&wa_bb[info->engine_class], val);
569+
wa_bb_append(&wa_bb[info->engine_class], val2);
570+
} else {
571+
return -EINVAL;
572+
}
573+
}
574+
575+
return dwords;
576+
}
577+
578+
static ssize_t wa_bb_store(struct wa_bb wa_bb[static XE_ENGINE_CLASS_MAX],
579+
struct xe_config_group_device *dev,
580+
const char *page, size_t len)
581+
{
582+
/* tmp_wa_bb must match wa_bb's size */
583+
struct wa_bb tmp_wa_bb[XE_ENGINE_CLASS_MAX] = { };
584+
ssize_t count, class;
585+
u32 *tmp;
586+
587+
/* 1. Count dwords - wa_bb[i].cs is NULL for all classes */
588+
count = parse_wa_bb_lines(page, tmp_wa_bb);
589+
if (count < 0)
590+
return count;
591+
592+
guard(mutex)(&dev->lock);
593+
594+
if (is_bound(dev))
595+
return -EBUSY;
596+
597+
/*
598+
* 2. Allocate a u32 array and set the pointers to the right positions
599+
* according to the length of each class' wa_bb
600+
*/
601+
tmp = krealloc(wa_bb[0].cs, count * sizeof(u32), GFP_KERNEL);
602+
if (!tmp)
603+
return -ENOMEM;
604+
605+
if (!count) {
606+
memset(wa_bb, 0, sizeof(tmp_wa_bb));
607+
return len;
608+
}
609+
610+
for (class = 0, count = 0; class < XE_ENGINE_CLASS_MAX; ++class) {
611+
tmp_wa_bb[class].cs = tmp + count;
612+
count += tmp_wa_bb[class].len;
613+
tmp_wa_bb[class].len = 0;
614+
}
615+
616+
/* 3. Parse wa_bb lines again, this time saving the values */
617+
count = parse_wa_bb_lines(page, tmp_wa_bb);
618+
if (count < 0)
619+
return count;
620+
621+
memcpy(wa_bb, tmp_wa_bb, sizeof(tmp_wa_bb));
622+
623+
return len;
624+
}
625+
626+
static ssize_t ctx_restore_post_bb_store(struct config_item *item,
627+
const char *data, size_t sz)
628+
{
629+
struct xe_config_group_device *dev = to_xe_config_group_device(item);
630+
631+
return wa_bb_store(dev->config.ctx_restore_post_bb, dev, data, sz);
632+
}
633+
634+
CONFIGFS_ATTR(, ctx_restore_post_bb);
374635
CONFIGFS_ATTR(, enable_psmi);
375636
CONFIGFS_ATTR(, engines_allowed);
376637
CONFIGFS_ATTR(, survivability_mode);
377638

378639
static struct configfs_attribute *xe_config_device_attrs[] = {
640+
&attr_ctx_restore_post_bb,
379641
&attr_enable_psmi,
380642
&attr_engines_allowed,
381643
&attr_survivability_mode,
@@ -387,6 +649,8 @@ static void xe_config_device_release(struct config_item *item)
387649
struct xe_config_group_device *dev = to_xe_config_group_device(item);
388650

389651
mutex_destroy(&dev->lock);
652+
653+
kfree(dev->config.ctx_restore_post_bb[0].cs);
390654
kfree(dev);
391655
}
392656

@@ -636,14 +900,26 @@ bool xe_configfs_get_psmi_enabled(struct pci_dev *pdev)
636900
/**
637901
* xe_configfs_get_ctx_restore_post_bb - get configfs ctx_restore_post_bb setting
638902
* @pdev: pci device
903+
* @class: hw engine class
904+
* @cs: pointer to the bb to use - only valid during probe
639905
*
640-
* Return: post_ctx_restore setting in configfs
906+
* Return: Number of dwords used in the post_ctx_restore setting in configfs
641907
*/
642908
u32 xe_configfs_get_ctx_restore_post_bb(struct pci_dev *pdev,
643909
enum xe_engine_class class,
644910
const u32 **cs)
645911
{
646-
return 0;
912+
struct xe_config_group_device *dev = find_xe_config_group_device(pdev);
913+
u32 len;
914+
915+
if (!dev)
916+
return 0;
917+
918+
*cs = dev->config.ctx_restore_post_bb[class].cs;
919+
len = dev->config.ctx_restore_post_bb[class].len;
920+
config_group_put(&dev->group);
921+
922+
return len;
647923
}
648924

649925
int __init xe_configfs_init(void)

0 commit comments

Comments
 (0)