From: Rodrigo Vivi <rodrigo.vivi@intel.com>
To: Stuart Summers <stuart.summers@intel.com>
Cc: <intel-xe@lists.freedesktop.org>, <fei.yang@intel.com>
Subject: Re: [PATCH 1/1] drm/xe/configfs: Allow overriding the GuC hwconfig table
Date: Wed, 11 Nov 2026 14:37:14 -0500 [thread overview]
Message-ID: <avTD6oXFiZcjPmAg@intel.com> (raw)
In-Reply-To: <20261111000818.238585-4-stuart.summers@intel.com>
On Wed, Nov 11, 2026 at 12:08:18AM +0000, Stuart Summers wrote:
> Add a binary configfs attribute holding a raw hwconfig blob. When set
> before bind, it is loaded into the hwconfig BO instead of querying GuC,
> so all consumers - including the DRM_XE_DEVICE_QUERY_HWCONFIG IOCTL -
> see the override. This also permits supplying a table on platforms where
> GuC exposes none, which is useful for testing userspace against
> hwconfig data the hardware does not report.
we should only put this in after we get that series that make most of
configfs as debug environments only...
>
> Signed-off-by: Stuart Summers <stuart.summers@intel.com>
> ---
> drivers/gpu/drm/xe/xe_configfs.c | 119 ++++++++++++++++++++++++++-
> drivers/gpu/drm/xe/xe_configfs.h | 6 ++
> drivers/gpu/drm/xe/xe_guc_hwconfig.c | 36 +++++---
> 3 files changed, 150 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/gpu/drm/xe/xe_configfs.c b/drivers/gpu/drm/xe/xe_configfs.c
> index 2ddd707a6dbd..6f810b33e12d 100644
> --- a/drivers/gpu/drm/xe/xe_configfs.c
> +++ b/drivers/gpu/drm/xe/xe_configfs.c
> @@ -63,7 +63,8 @@
> * ├── engines_allowed
> * ├── migrate_ulls_period_ms
> * ├── enable_psmi
> - * └── disable_vram_page_offline
> + * ├── disable_vram_page_offline
> + * └── hwconfig
> *
> * After configuring the attributes as per next section, the device can be
> * probed with::
> @@ -277,6 +278,22 @@
> *
> * This attribute can only be set before binding to the device.
> *
> + * Hwconfig:
> + * ---------
> + *
> + * Override the hardware configuration table normally obtained from the GuC
> + * with a user-provided binary blob. When set, the driver uses this table in
> + * place of the GuC one everywhere it is consumed, including the table
> + * reported to userspace by the DRM_XE_DEVICE_QUERY_HWCONFIG query. This also
> + * allows a table to be provided on platforms where the GuC exposes none.
> + *
> + * The blob is the raw hwconfig table, so its size must be a multiple of 4
> + * bytes. Writing a new blob replaces the previous one::
> + *
> + * # cat hwconfig.bin > /sys/kernel/config/xe/0000:03:00.0/hwconfig
> + *
> + * This attribute can only be set before binding to the device.
> + *
> * Remove devices
> * ==============
> *
> @@ -291,6 +308,12 @@ struct wa_bb {
> u32 len; /* in dwords */
> };
>
> +/* User-provided replacement for the GuC-provided hwconfig table */
> +struct hwconfig_blob {
> + void *data;
> + size_t len; /* in bytes */
> +};
> +
> struct xe_config_group_device {
> struct config_group group;
> struct config_group sriov;
> @@ -299,6 +322,7 @@ struct xe_config_group_device {
> u64 gt_types_allowed;
> u64 engines_allowed;
> u32 migrate_ulls_period_ms;
> + struct hwconfig_blob hwconfig;
> 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;
> @@ -937,6 +961,50 @@ static ssize_t ctx_restore_post_bb_store(struct config_item *item,
> return wa_bb_store(dev->config.ctx_restore_post_bb, dev, data, sz);
> }
>
> +static ssize_t hwconfig_read(struct config_item *item, void *buf, size_t count)
> +{
> + struct xe_config_group_device *dev = to_xe_config_group_device(item);
> +
> + guard(mutex)(&dev->lock);
> +
> + if (!buf)
> + return dev->config.hwconfig.len;
> +
> + count = min(count, dev->config.hwconfig.len);
> + memcpy(buf, dev->config.hwconfig.data, count);
> +
> + return count;
> +}
> +
> +static ssize_t hwconfig_write(struct config_item *item, const void *buf, size_t count)
> +{
> + struct xe_config_group_device *dev = to_xe_config_group_device(item);
> + void *data = NULL;
> +
> + /* The table is a sequence of dwords */
> + if (count % sizeof(u32))
> + return -EINVAL;
> +
> + if (count) {
> + data = kmemdup(buf, count, GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> + }
> +
> + guard(mutex)(&dev->lock);
> +
> + if (is_bound(dev)) {
> + kfree(data);
> + return -EBUSY;
> + }
> +
> + kfree(dev->config.hwconfig.data);
> + dev->config.hwconfig.data = data;
> + dev->config.hwconfig.len = count;
> +
> + return count;
> +}
> +
> CONFIGFS_ATTR(, ctx_restore_mid_bb);
> CONFIGFS_ATTR(, ctx_restore_post_bb);
> CONFIGFS_ATTR(, enable_multi_queue);
> @@ -946,6 +1014,7 @@ CONFIGFS_ATTR(, disable_vram_page_offline);
> CONFIGFS_ATTR(, engines_allowed);
> CONFIGFS_ATTR(, gt_types_allowed);
> CONFIGFS_ATTR(, survivability_mode);
> +CONFIGFS_BIN_ATTR(, hwconfig, NULL, SZ_64K);
>
> static struct configfs_attribute *xe_config_device_attrs[] = {
> &attr_ctx_restore_mid_bb,
> @@ -960,12 +1029,18 @@ static struct configfs_attribute *xe_config_device_attrs[] = {
> NULL,
> };
>
> +static const struct configfs_bin_attribute *const xe_config_device_bin_attrs[] = {
> + &attr_hwconfig,
> + 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.hwconfig.data);
> kfree(dev->config.ctx_restore_mid_bb[0].cs);
> kfree(dev->config.ctx_restore_post_bb[0].cs);
> kfree(dev);
> @@ -1001,6 +1076,7 @@ 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_bin_attrs = xe_config_device_bin_attrs,
> .ct_owner = THIS_MODULE,
> };
>
> @@ -1243,6 +1319,10 @@ static void dump_custom_dev_config(struct pci_dev *pdev,
> PRI_CUSTOM_ATTR("%u", sriov.admin_only_pf);
>
> #undef PRI_CUSTOM_ATTR
> +
> + if (dev->config.hwconfig.len)
> + pci_info(pdev, "configfs: hwconfig = %zu bytes\n",
> + dev->config.hwconfig.len);
> }
>
> /**
> @@ -1479,6 +1559,43 @@ u32 xe_configfs_get_ctx_restore_post_bb(struct pci_dev *pdev,
> return len;
> }
>
> +/**
> + * xe_configfs_get_hwconfig - get a copy of the configfs hwconfig blob
> + * @pdev: pci device
> + * @blob: pointer to store the newly allocated copy of the blob
> + *
> + * If a hwconfig table override was written to configfs for @pdev, allocate a
> + * copy of it and store the pointer in @blob. The caller takes ownership of it
> + * and is responsible for freeing it with kfree().
> + *
> + * Return: size in bytes of the blob stored in @blob, or 0 if no override is
> + * set or it could not be copied.
> + */
> +size_t xe_configfs_get_hwconfig(struct pci_dev *pdev, void **blob)
> +{
> + struct xe_config_group_device *dev = find_xe_config_group_device(pdev);
> + size_t len = 0;
> +
> + *blob = NULL;
> +
> + if (!dev)
> + return 0;
> +
> + scoped_guard(mutex, &dev->lock) {
> + if (!dev->config.hwconfig.len)
> + break;
> +
> + *blob = kmemdup(dev->config.hwconfig.data,
> + dev->config.hwconfig.len, GFP_KERNEL);
> + if (*blob)
> + len = dev->config.hwconfig.len;
> + }
> +
> + config_group_put(&dev->group);
> +
> + return len;
> +}
> +
> #ifdef CONFIG_PCI_IOV
> /**
> * xe_configfs_admin_only_pf() - Get PF's operational mode.
> diff --git a/drivers/gpu/drm/xe/xe_configfs.h b/drivers/gpu/drm/xe/xe_configfs.h
> index 327b648944ff..526250e9fdfe 100644
> --- a/drivers/gpu/drm/xe/xe_configfs.h
> +++ b/drivers/gpu/drm/xe/xe_configfs.h
> @@ -32,6 +32,7 @@ u32 xe_configfs_get_ctx_restore_mid_bb(struct pci_dev *pdev,
> u32 xe_configfs_get_ctx_restore_post_bb(struct pci_dev *pdev,
> enum xe_engine_class class,
> const u32 **cs);
> +size_t xe_configfs_get_hwconfig(struct pci_dev *pdev, void **blob);
> #ifdef CONFIG_PCI_IOV
> unsigned int xe_configfs_get_max_vfs(struct pci_dev *pdev);
> bool xe_configfs_admin_only_pf(struct pci_dev *pdev);
> @@ -54,6 +55,11 @@ static inline u32 xe_configfs_get_ctx_restore_mid_bb(struct pci_dev *pdev,
> static inline u32 xe_configfs_get_ctx_restore_post_bb(struct pci_dev *pdev,
> enum xe_engine_class class,
> const u32 **cs) { return 0; }
> +static inline size_t xe_configfs_get_hwconfig(struct pci_dev *pdev, void **blob)
> +{
> + *blob = NULL;
> + return 0;
> +}
> #ifdef CONFIG_PCI_IOV
> static inline unsigned int xe_configfs_get_max_vfs(struct pci_dev *pdev)
> {
> diff --git a/drivers/gpu/drm/xe/xe_guc_hwconfig.c b/drivers/gpu/drm/xe/xe_guc_hwconfig.c
> index dc667a71b438..33abfdfcff41 100644
> --- a/drivers/gpu/drm/xe/xe_guc_hwconfig.c
> +++ b/drivers/gpu/drm/xe/xe_guc_hwconfig.c
> @@ -10,7 +10,9 @@
>
> #include "abi/guc_actions_abi.h"
> #include "xe_bo.h"
> +#include "xe_configfs.h"
> #include "xe_device_types.h"
> +#include "xe_gt_printk.h"
> #include "xe_gt_types.h"
> #include "xe_guc.h"
> #include "xe_map.h"
> @@ -55,6 +57,7 @@ int xe_guc_hwconfig_init(struct xe_guc *guc)
> struct xe_gt *gt = guc_to_gt(guc);
> struct xe_tile *tile = gt_to_tile(gt);
> struct xe_bo *bo;
> + void *blob;
> u32 size;
> int err;
>
> @@ -68,25 +71,38 @@ int xe_guc_hwconfig_init(struct xe_guc *guc)
> if (gt != xe_root_mmio_gt(xe))
> return 0;
>
> - /* ADL_P, DG2+ supports hwconfig table */
> - if (GRAPHICS_VERx100(xe) < 1255 && xe->info.platform != XE_ALDERLAKE_P)
> - return 0;
> -
> - err = guc_hwconfig_size(guc, &size);
> - if (err)
> - return err;
> - if (!size)
> - return -EINVAL;
> + size = xe_configfs_get_hwconfig(to_pci_dev(xe->drm.dev), &blob);
> + if (!size) {
> + /* ADL_P, DG2+ supports hwconfig table */
> + if (GRAPHICS_VERx100(xe) < 1255 && xe->info.platform != XE_ALDERLAKE_P)
> + return 0;
> +
> + err = guc_hwconfig_size(guc, &size);
> + if (err)
> + return err;
> + if (!size)
> + return -EINVAL;
> + }
>
> bo = xe_managed_bo_create_pin_map(xe, tile, PAGE_ALIGN(size),
> XE_BO_FLAG_SYSTEM |
> XE_BO_FLAG_GGTT |
> XE_BO_FLAG_GGTT_INVALIDATE);
> - if (IS_ERR(bo))
> + if (IS_ERR(bo)) {
> + kfree(blob);
> return PTR_ERR(bo);
> + }
> guc->hwconfig.bo = bo;
> guc->hwconfig.size = size;
>
> + if (blob) {
> + xe_map_memcpy_to(xe, &bo->vmap, 0, blob, size);
> + kfree(blob);
> + xe_gt_info(gt, "Using hwconfig table override from configfs (%u bytes)\n",
> + size);
> + return 0;
> + }
> +
> return guc_hwconfig_copy(guc);
> }
>
> --
> 2.43.0
>
next prev parent reply other threads:[~2026-11-11 19:37 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-11 0:08 [PATCH 0/1] Add a configfs to program the hwconfig Stuart Summers
2026-11-11 0:08 ` [PATCH 1/1] drm/xe/configfs: Allow overriding the GuC hwconfig table Stuart Summers
2026-11-11 0:25 ` sashiko-bot
2026-11-11 19:37 ` Rodrigo Vivi [this message]
2026-11-11 1:35 ` ✓ CI.KUnit: success for Add a configfs to program the hwconfig Patchwork
2026-11-11 2:09 ` ✓ Xe.CI.BAT: " Patchwork
2026-11-11 7:06 ` ✓ Xe.CI.FULL: " Patchwork
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=avTD6oXFiZcjPmAg@intel.com \
--to=rodrigo.vivi@intel.com \
--cc=fei.yang@intel.com \
--cc=intel-xe@lists.freedesktop.org \
--cc=stuart.summers@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox