Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+278 -2
+278 -2
drivers/gpu/drm/xe/xe_configfs.c
··· 4 4 */ 5 5 6 6 #include <linux/bitops.h> 7 + #include <linux/ctype.h> 7 8 #include <linux/configfs.h> 8 9 #include <linux/cleanup.h> 9 10 #include <linux/find.h> ··· 13 12 #include <linux/pci.h> 14 13 #include <linux/string.h> 15 14 15 + #include "instructions/xe_mi_commands.h" 16 16 #include "xe_configfs.h" 17 17 #include "xe_hw_engine_types.h" 18 18 #include "xe_module.h" ··· 117 115 * 118 116 * This attribute can only be set before binding to the device. 119 117 * 118 + * Context restore BB 119 + * ------------------ 120 + * 121 + * Allow to execute a batch buffer during any context switches. When the 122 + * GPU is restoring the context, it executes additional commands. It's useful 123 + * for testing additional workarounds and validating certain HW behaviors: it's 124 + * not intended for normal execution and will taint the kernel with TAINT_TEST 125 + * when used. 126 + * 127 + * Currently this is implemented only for post context restore. Examples: 128 + * 129 + * #. Execute a LRI command to write 0xDEADBEEF to register 0x4f10:: 130 + * 131 + * # echo 'rcs cmd 11000001 4F100 DEADBEEF' \ 132 + * > /sys/kernel/config/xe/0000:03:00.0/ctx_restore_post_bb 133 + * 134 + * #. Load certain values in a couple of registers (it can be used as a simpler 135 + * alternative to the `cmd`) action:: 136 + * 137 + * # cat > /sys/kernel/config/xe/0000:03:00.0/ctx_restore_post_bb <<EOF 138 + * rcs reg 4F100 DEADBEEF 139 + * rcs reg 4F104 FFFFFFFF 140 + * EOF 141 + * 142 + * .. note:: 143 + * 144 + * When using multiple lines, make sure to use a command that is 145 + * implemented with a single write syscall, like HEREDOC. 146 + * 147 + * This attribute can only be set before binding to the device. 148 + * 120 149 * Remove devices 121 150 * ============== 122 151 * ··· 156 123 * # rmdir /sys/kernel/config/xe/0000:03:00.0/ 157 124 */ 158 125 126 + /* Similar to struct xe_bb, but not tied to HW (yet) */ 127 + struct wa_bb { 128 + u32 *cs; 129 + u32 len; /* in dwords */ 130 + }; 131 + 159 132 struct xe_config_group_device { 160 133 struct config_group group; 161 134 162 135 struct xe_config_device { 163 136 u64 engines_allowed; 137 + struct wa_bb ctx_restore_post_bb[XE_ENGINE_CLASS_MAX]; 164 138 bool survivability_mode; 165 139 bool enable_psmi; 166 140 } config; ··· 411 371 return len; 412 372 } 413 373 374 + static bool wa_bb_read_advance(bool dereference, char **p, 375 + const char *append, size_t len, 376 + size_t *max_size) 377 + { 378 + if (dereference) { 379 + if (len >= *max_size) 380 + return false; 381 + *max_size -= len; 382 + if (append) 383 + memcpy(*p, append, len); 384 + } 385 + 386 + *p += len; 387 + 388 + return true; 389 + } 390 + 391 + static ssize_t wa_bb_show(struct xe_config_group_device *dev, 392 + struct wa_bb wa_bb[static XE_ENGINE_CLASS_MAX], 393 + char *data, size_t sz) 394 + { 395 + char *p = data; 396 + 397 + guard(mutex)(&dev->lock); 398 + 399 + for (size_t i = 0; i < ARRAY_SIZE(engine_info); i++) { 400 + enum xe_engine_class ec = engine_info[i].engine_class; 401 + size_t len; 402 + 403 + if (!wa_bb[ec].len) 404 + continue; 405 + 406 + len = snprintf(p, sz, "%s:", engine_info[i].cls); 407 + if (!wa_bb_read_advance(data, &p, NULL, len, &sz)) 408 + return -ENOBUFS; 409 + 410 + for (size_t j = 0; j < wa_bb[ec].len; j++) { 411 + len = snprintf(p, sz, " %08x", wa_bb[ec].cs[j]); 412 + if (!wa_bb_read_advance(data, &p, NULL, len, &sz)) 413 + return -ENOBUFS; 414 + } 415 + 416 + if (!wa_bb_read_advance(data, &p, "\n", 1, &sz)) 417 + return -ENOBUFS; 418 + } 419 + 420 + if (!wa_bb_read_advance(data, &p, "", 1, &sz)) 421 + return -ENOBUFS; 422 + 423 + /* Reserve one more to match check for '\0' */ 424 + if (!data) 425 + p++; 426 + 427 + return p - data; 428 + } 429 + 430 + static ssize_t ctx_restore_post_bb_show(struct config_item *item, char *page) 431 + { 432 + struct xe_config_group_device *dev = to_xe_config_group_device(item); 433 + 434 + return wa_bb_show(dev, dev->config.ctx_restore_post_bb, page, SZ_4K); 435 + } 436 + 437 + static void wa_bb_append(struct wa_bb *wa_bb, u32 val) 438 + { 439 + if (wa_bb->cs) 440 + wa_bb->cs[wa_bb->len] = val; 441 + 442 + wa_bb->len++; 443 + } 444 + 445 + static ssize_t parse_hex(const char *line, u32 *pval) 446 + { 447 + char numstr[12]; 448 + const char *p; 449 + ssize_t numlen; 450 + 451 + p = line + strspn(line, " \t"); 452 + if (!*p || *p == '\n') 453 + return 0; 454 + 455 + numlen = strcspn(p, " \t\n"); 456 + if (!numlen || numlen >= sizeof(numstr) - 1) 457 + return -EINVAL; 458 + 459 + memcpy(numstr, p, numlen); 460 + numstr[numlen] = '\0'; 461 + p += numlen; 462 + 463 + if (kstrtou32(numstr, 16, pval)) 464 + return -EINVAL; 465 + 466 + return p - line; 467 + } 468 + 469 + /* 470 + * Parse lines with the format 471 + * 472 + * <engine-class> cmd <u32> <u32...> 473 + * <engine-class> reg <u32_addr> <u32_val> 474 + * 475 + * and optionally save them in @wa_bb[i].cs is non-NULL. 476 + * 477 + * Return the number of dwords parsed. 478 + */ 479 + static ssize_t parse_wa_bb_lines(const char *lines, 480 + struct wa_bb wa_bb[static XE_ENGINE_CLASS_MAX]) 481 + { 482 + ssize_t dwords = 0, ret; 483 + const char *p; 484 + 485 + for (p = lines; *p; p++) { 486 + const struct engine_info *info = NULL; 487 + u32 val, val2; 488 + 489 + /* Also allow empty lines */ 490 + p += strspn(p, " \t\n"); 491 + if (!*p) 492 + break; 493 + 494 + ret = parse_engine(p, " \t\n", NULL, &info); 495 + if (ret < 0) 496 + return ret; 497 + 498 + p += ret; 499 + p += strspn(p, " \t"); 500 + 501 + if (str_has_prefix(p, "cmd")) { 502 + for (p += strlen("cmd"); *p;) { 503 + ret = parse_hex(p, &val); 504 + if (ret < 0) 505 + return -EINVAL; 506 + if (!ret) 507 + break; 508 + 509 + p += ret; 510 + dwords++; 511 + wa_bb_append(&wa_bb[info->engine_class], val); 512 + } 513 + } else if (str_has_prefix(p, "reg")) { 514 + p += strlen("reg"); 515 + ret = parse_hex(p, &val); 516 + if (ret <= 0) 517 + return -EINVAL; 518 + 519 + p += ret; 520 + ret = parse_hex(p, &val2); 521 + if (ret <= 0) 522 + return -EINVAL; 523 + 524 + p += ret; 525 + dwords += 3; 526 + wa_bb_append(&wa_bb[info->engine_class], 527 + MI_LOAD_REGISTER_IMM | MI_LRI_NUM_REGS(1)); 528 + wa_bb_append(&wa_bb[info->engine_class], val); 529 + wa_bb_append(&wa_bb[info->engine_class], val2); 530 + } else { 531 + return -EINVAL; 532 + } 533 + } 534 + 535 + return dwords; 536 + } 537 + 538 + static ssize_t wa_bb_store(struct wa_bb wa_bb[static XE_ENGINE_CLASS_MAX], 539 + struct xe_config_group_device *dev, 540 + const char *page, size_t len) 541 + { 542 + /* tmp_wa_bb must match wa_bb's size */ 543 + struct wa_bb tmp_wa_bb[XE_ENGINE_CLASS_MAX] = { }; 544 + ssize_t count, class; 545 + u32 *tmp; 546 + 547 + /* 1. Count dwords - wa_bb[i].cs is NULL for all classes */ 548 + count = parse_wa_bb_lines(page, tmp_wa_bb); 549 + if (count < 0) 550 + return count; 551 + 552 + guard(mutex)(&dev->lock); 553 + 554 + if (is_bound(dev)) 555 + return -EBUSY; 556 + 557 + /* 558 + * 2. Allocate a u32 array and set the pointers to the right positions 559 + * according to the length of each class' wa_bb 560 + */ 561 + tmp = krealloc(wa_bb[0].cs, count * sizeof(u32), GFP_KERNEL); 562 + if (!tmp) 563 + return -ENOMEM; 564 + 565 + if (!count) { 566 + memset(wa_bb, 0, sizeof(tmp_wa_bb)); 567 + return len; 568 + } 569 + 570 + for (class = 0, count = 0; class < XE_ENGINE_CLASS_MAX; ++class) { 571 + tmp_wa_bb[class].cs = tmp + count; 572 + count += tmp_wa_bb[class].len; 573 + tmp_wa_bb[class].len = 0; 574 + } 575 + 576 + /* 3. Parse wa_bb lines again, this time saving the values */ 577 + count = parse_wa_bb_lines(page, tmp_wa_bb); 578 + if (count < 0) 579 + return count; 580 + 581 + memcpy(wa_bb, tmp_wa_bb, sizeof(tmp_wa_bb)); 582 + 583 + return len; 584 + } 585 + 586 + static ssize_t ctx_restore_post_bb_store(struct config_item *item, 587 + const char *data, size_t sz) 588 + { 589 + struct xe_config_group_device *dev = to_xe_config_group_device(item); 590 + 591 + return wa_bb_store(dev->config.ctx_restore_post_bb, dev, data, sz); 592 + } 593 + 594 + CONFIGFS_ATTR(, ctx_restore_post_bb); 414 595 CONFIGFS_ATTR(, enable_psmi); 415 596 CONFIGFS_ATTR(, engines_allowed); 416 597 CONFIGFS_ATTR(, survivability_mode); 417 598 418 599 static struct configfs_attribute *xe_config_device_attrs[] = { 600 + &attr_ctx_restore_post_bb, 419 601 &attr_enable_psmi, 420 602 &attr_engines_allowed, 421 603 &attr_survivability_mode, ··· 649 387 struct xe_config_group_device *dev = to_xe_config_group_device(item); 650 388 651 389 mutex_destroy(&dev->lock); 390 + 391 + kfree(dev->config.ctx_restore_post_bb[0].cs); 652 392 kfree(dev); 653 393 } 654 394 ··· 900 636 /** 901 637 * xe_configfs_get_ctx_restore_post_bb - get configfs ctx_restore_post_bb setting 902 638 * @pdev: pci device 639 + * @class: hw engine class 640 + * @cs: pointer to the bb to use - only valid during probe 903 641 * 904 - * Return: post_ctx_restore setting in configfs 642 + * Return: Number of dwords used in the post_ctx_restore setting in configfs 905 643 */ 906 644 u32 xe_configfs_get_ctx_restore_post_bb(struct pci_dev *pdev, 907 645 enum xe_engine_class class, 908 646 const u32 **cs) 909 647 { 910 - return 0; 648 + struct xe_config_group_device *dev = find_xe_config_group_device(pdev); 649 + u32 len; 650 + 651 + if (!dev) 652 + return 0; 653 + 654 + *cs = dev->config.ctx_restore_post_bb[class].cs; 655 + len = dev->config.ctx_restore_post_bb[class].len; 656 + config_group_put(&dev->group); 657 + 658 + return len; 911 659 } 912 660 913 661 int __init xe_configfs_init(void)