]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
drm/xe/configfs: Add attribute to disable engines
authorLucas De Marchi <lucas.demarchi@intel.com>
Wed, 28 May 2025 21:54:07 +0000 (14:54 -0700)
committerLucas De Marchi <lucas.demarchi@intel.com>
Mon, 2 Jun 2025 18:00:46 +0000 (11:00 -0700)
Add the userspace interface to load the driver with fewer engines.
The syntax is to just echo the engine names to a file in configfs, like
below:

echo 'rcs0,bcs0' > /sys/kernel/config/xe/<bdf>/engine_allowed

With that engines other than rcs0 and bcs0 will not be enabled. To
enable all instances from a class, a '*' can be used.

Reviewed-by: Matt Roper <matthew.d.roper@intel.com>
Link: https://lore.kernel.org/r/20250528-engine-mask-v4-4-f4636d2a890a@intel.com
Signed-off-by: Lucas De Marchi <lucas.demarchi@intel.com>
drivers/gpu/drm/xe/xe_configfs.c

index 8320d57ef5a809ec3b7997044f40f0092f1833d5..8ec1ff1e4e808b37999032295d3c4c43a3b1fc96 100644 (file)
@@ -3,14 +3,19 @@
  * Copyright © 2025 Intel Corporation
  */
 
+#include <linux/bitops.h>
 #include <linux/configfs.h>
+#include <linux/find.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/pci.h>
+#include <linux/string.h>
 
 #include "xe_configfs.h"
 #include "xe_module.h"
 
+#include "xe_hw_engine_types.h"
+
 /**
  * DOC: Xe Configfs
  *
  *     # echo 1 > /sys/kernel/config/xe/0000:03:00.0/survivability_mode
  *     # echo 0000:03:00.0 > /sys/bus/pci/drivers/xe/bind  (Enters survivability mode if supported)
  *
+ * 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.
+ *
  * Remove devices
  * ==============
  *
@@ -60,11 +89,30 @@ struct xe_config_device {
        struct config_group group;
 
        bool survivability_mode;
+       u64 engines_allowed;
 
        /* protects attributes */
        struct mutex lock;
 };
 
+struct engine_info {
+       const char *cls;
+       u64 mask;
+};
+
+/* 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 },
+       { .cls = "bcs", .mask = XE_HW_ENGINE_BCS_MASK },
+       { .cls = "vcs", .mask = XE_HW_ENGINE_VCS_MASK },
+       { .cls = "vecs", .mask = XE_HW_ENGINE_VECS_MASK },
+       { .cls = "ccs", .mask = XE_HW_ENGINE_CCS_MASK },
+       { .cls = "gsccs", .mask = XE_HW_ENGINE_GSCCS_MASK },
+};
+
 static struct xe_config_device *to_xe_config_device(struct config_item *item)
 {
        return container_of(to_config_group(item), struct xe_config_device, group);
@@ -94,10 +142,96 @@ static ssize_t survivability_mode_store(struct config_item *item, const char *pa
        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;
+}
+
+static bool lookup_engine_mask(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 (!strcmp(pattern, "*")) {
+                       *mask = engine_info[i].mask;
+                       return true;
+               }
+
+               if (kstrtou8(pattern, 10, &instance))
+                       return false;
+
+               bit = __ffs64(engine_info[i].mask) + instance;
+               if (bit >= fls64(engine_info[i].mask))
+                       return false;
+
+               *mask = BIT_ULL(bit);
+               return true;
+       }
+
+       return false;
+}
+
+static ssize_t engines_allowed_store(struct config_item *item, const char *page,
+                                    size_t len)
+{
+       struct xe_config_device *dev = to_xe_config_device(item);
+       size_t patternlen, p;
+       u64 mask, val = 0;
+
+       for (p = 0; p < len; p += patternlen + 1) {
+               char buf[MAX_ENGINE_CLASS_CHARS + MAX_ENGINE_INSTANCE_CHARS + 1];
+
+               patternlen = strcspn(page + p, ",\n");
+               if (patternlen >= sizeof(buf))
+                       return -EINVAL;
+
+               memcpy(buf, page + p, patternlen);
+               buf[patternlen] = '\0';
+
+               if (!lookup_engine_mask(buf, &mask))
+                       return -EINVAL;
+
+               val |= mask;
+       }
+
+       mutex_lock(&dev->lock);
+       dev->engines_allowed = val;
+       mutex_unlock(&dev->lock);
+
+       return len;
+}
+
 CONFIGFS_ATTR(, survivability_mode);
+CONFIGFS_ATTR(, engines_allowed);
 
 static struct configfs_attribute *xe_config_device_attrs[] = {
        &attr_survivability_mode,
+       &attr_engines_allowed,
        NULL,
 };
 
@@ -139,6 +273,9 @@ static struct config_group *xe_config_make_device_group(struct config_group *gro
        if (!dev)
                return ERR_PTR(-ENOMEM);
 
+       /* Default values */
+       dev->engines_allowed = U64_MAX;
+
        config_group_init_type_name(&dev->group, name, &xe_config_device_type);
 
        mutex_init(&dev->lock);
@@ -237,8 +374,16 @@ void xe_configfs_clear_survivability_mode(struct pci_dev *pdev)
  */
 u64 xe_configfs_get_engines_allowed(struct pci_dev *pdev)
 {
-       /* dummy implementation */
-       return U64_MAX;
+       struct xe_config_device *dev = configfs_find_group(pdev);
+       u64 engines_allowed;
+
+       if (!dev)
+               return U64_MAX;
+
+       engines_allowed = dev->engines_allowed;
+       config_item_put(&dev->group.cg_item);
+
+       return engines_allowed;
 }
 
 int __init xe_configfs_init(void)