@@ -2731,6 +2731,12 @@ struct drm_i915_private {
} oa;
} perf;
+ struct {
+ struct kobject gt_kobj;
+ struct kobject engines_kobj;
+ struct kobject engines_classes_kobjs[MAX_ENGINE_CLASS + 1];
+ } gt_topology;
+
/* Abstract the submission mechanism (legacy ringbuffer or execlists) away */
struct {
void (*resume)(struct drm_i915_private *);
@@ -570,6 +570,178 @@ static void i915_setup_error_capture(struct device *kdev) {}
static void i915_teardown_error_capture(struct device *kdev) {}
#endif
+static struct attribute engine_class_attr = {
+ .name = "class",
+ .mode = 0444,
+};
+
+static struct attribute engine_id_attr = {
+ .name = "id",
+ .mode = 0444,
+};
+
+static struct attribute engine_instance_attr = {
+ .name = "instance",
+ .mode = 0444,
+};
+
+static ssize_t
+show_engine_attr(struct kobject *kobj, struct attribute *attr, char *buf)
+{
+ struct intel_engine_cs *engine =
+ container_of(kobj, struct intel_engine_cs, instance_kobj);
+
+ if (attr == &engine_class_attr)
+ return sprintf(buf, "%hhu\n", engine->uabi_class);
+ if (attr == &engine_id_attr)
+ return sprintf(buf, "%hhu\n", engine->uabi_id);
+ if (attr == &engine_instance_attr)
+ return sprintf(buf, "%hhu\n", engine->instance);
+ return sprintf(buf, "\n");
+}
+
+static const struct sysfs_ops engine_ops = {
+ .show = show_engine_attr,
+};
+
+static struct kobj_type engine_type = {
+ .sysfs_ops = &engine_ops,
+};
+
+static struct attribute engine_capability_hevc_attr = {
+ .name = "hevc",
+ .mode = 0444,
+};
+
+static ssize_t
+show_engine_capabilities_attr(struct kobject *kobj, struct attribute *attr, char *buf)
+{
+ struct intel_engine_cs *engine =
+ container_of(kobj, struct intel_engine_cs, capabilities_kobj);
+
+ if (attr == &engine_capability_hevc_attr)
+ return sprintf(buf, "%i\n", INTEL_GEN(engine->i915) >= 8);
+ return sprintf(buf, "\n");
+}
+
+static const struct sysfs_ops engine_capabilities_ops = {
+ .show = show_engine_capabilities_attr,
+};
+
+static struct kobj_type engine_capabilities_type = {
+ .sysfs_ops = &engine_capabilities_ops,
+};
+
+static int i915_setup_engines_sysfs(struct drm_i915_private *dev_priv,
+ struct kobject *gt_kobj)
+{
+ struct intel_engine_cs *engine_for_class, *engine;
+ enum intel_engine_id id_for_class, id;
+ struct kobject *engines_kobj = &dev_priv->gt_topology.engines_kobj;
+ bool registred[MAX_ENGINE_CLASS + 1] = { false, };
+ int ret;
+
+ ret = kobject_init_and_add(engines_kobj, gt_kobj->ktype, gt_kobj,
+ "engines");
+ if (ret)
+ return ret;
+
+ for_each_engine(engine_for_class, dev_priv, id_for_class) {
+ struct kobject *engine_class_kobj =
+ &dev_priv->gt_topology.engines_classes_kobjs[engine_for_class->class];
+
+ if (registred[engine_for_class->class])
+ continue;
+
+ registred[engine_for_class->class] = true;
+
+ ret = kobject_init_and_add(engine_class_kobj,
+ engines_kobj->ktype,
+ engines_kobj,
+ intel_engine_get_class_name(engine_for_class));
+ if (ret)
+ return ret;
+
+ for_each_engine(engine, dev_priv, id) {
+ if (engine->class != engine_for_class->class)
+ continue;
+
+ ret = kobject_init_and_add(&engine->instance_kobj,
+ &engine_type,
+ engine_class_kobj,
+ "%d", engine->instance);
+ if (ret)
+ return ret;
+
+ ret = sysfs_create_file(&engine->instance_kobj,
+ &engine_class_attr);
+ if (ret)
+ return ret;
+ ret = sysfs_create_file(&engine->instance_kobj,
+ &engine_id_attr);
+ if (ret)
+ return ret;
+ ret = sysfs_create_file(&engine->instance_kobj,
+ &engine_instance_attr);
+ if (ret)
+ return ret;
+
+ ret = kobject_init_and_add(&engine->capabilities_kobj,
+ &engine_capabilities_type,
+ &engine->instance_kobj,
+ "capabilities");
+ if (ret)
+ return ret;
+
+ if (engine->id == VCS) {
+ ret = sysfs_create_file(&engine->capabilities_kobj,
+ &engine_capability_hevc_attr);
+ if (ret)
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void i915_teardown_engines_sysfs(struct drm_i915_private *dev_priv)
+{
+ struct intel_engine_cs *engine;
+ enum intel_engine_id id;
+
+ for_each_engine(engine, dev_priv, id) {
+ sysfs_remove_file(&engine->instance_kobj, &engine_class_attr);
+ sysfs_remove_file(&engine->instance_kobj, &engine_id_attr);
+ sysfs_remove_file(&engine->instance_kobj, &engine_instance_attr);
+ sysfs_remove_file(&engine->capabilities_kobj,
+ &engine_capability_hevc_attr);
+ }
+}
+
+static int i915_setup_gt_sysfs(struct drm_i915_private *dev_priv,
+ struct device *kdev)
+{
+ int ret;
+
+ ret = kobject_init_and_add(&dev_priv->gt_topology.gt_kobj,
+ kdev->kobj.ktype,
+ &kdev->kobj,
+ "gt");
+ if (ret)
+ return ret;
+
+ return i915_setup_engines_sysfs(dev_priv, &dev_priv->gt_topology.gt_kobj);
+}
+
+static void i915_teardown_gt_sysfs(struct drm_i915_private *dev_priv)
+{
+ i915_teardown_engines_sysfs(dev_priv);
+
+ kobject_get(&dev_priv->gt_topology.gt_kobj);
+ kobject_del(&dev_priv->gt_topology.gt_kobj);
+}
+
void i915_setup_sysfs(struct drm_i915_private *dev_priv)
{
struct device *kdev = dev_priv->drm.primary->kdev;
@@ -616,6 +788,10 @@ void i915_setup_sysfs(struct drm_i915_private *dev_priv)
if (ret)
DRM_ERROR("RPS sysfs setup failed\n");
+ ret = i915_setup_gt_sysfs(dev_priv, kdev);
+ if (ret)
+ DRM_ERROR("GT sysfs setup failed\n");
+
i915_setup_error_capture(kdev);
}
@@ -625,6 +801,8 @@ void i915_teardown_sysfs(struct drm_i915_private *dev_priv)
i915_teardown_error_capture(kdev);
+ i915_teardown_gt_sysfs(dev_priv);
+
if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
sysfs_remove_files(&kdev->kobj, vlv_attrs);
else
@@ -131,6 +131,18 @@ static const struct engine_info intel_engines[] = {
},
};
+/**
+ * intel_engine_get_class_name() - return the name of the class for an engine
+ * @engine: engine
+ *
+ * Return: a string naming the class of the engine
+ */
+const char *
+intel_engine_get_class_name(struct intel_engine_cs *engine)
+{
+ return intel_engine_classes[engine->class].name;
+}
+
/**
* ___intel_engine_context_size() - return the size of the context for an engine
* @dev_priv: i915 device private
@@ -282,6 +282,9 @@ struct intel_engine_cs {
struct drm_i915_private *i915;
char name[INTEL_ENGINE_CS_MAX_NAME];
+ struct kobject instance_kobj;
+ struct kobject capabilities_kobj;
+
enum intel_engine_id id;
unsigned int hw_id;
unsigned int guc_id;
@@ -995,6 +998,7 @@ gen8_emit_ggtt_write(u32 *cs, u32 value, u32 gtt_offset)
return cs;
}
+const char *intel_engine_get_class_name(struct intel_engine_cs *engine);
bool intel_engine_is_idle(struct intel_engine_cs *engine);
bool intel_engines_are_idle(struct drm_i915_private *dev_priv);
This enables userspace to discover the engines available on the GPU. Here is the layout on a Skylake GT4: /sys/devices/pci0000:00/0000:00:02.0/drm/card0/gt/ └── engines ├── bcs │ └── 0 │ ├── capabilities │ ├── class │ ├── id │ └── instance ├── rcs │ └── 0 │ ├── capabilities │ ├── class │ ├── id │ └── instance ├── vcs │ ├── 0 │ │ ├── capabilities │ │ │ └── hevc │ │ ├── class │ │ ├── id │ │ └── instance │ └── 1 │ ├── capabilities │ ├── class │ ├── id │ └── instance └── vecs └── 0 ├── capabilities ├── class ├── id └── instance Instance is the instance number of the engine for a particular class : $ cat /sys/devices/pci0000\:00/0000\:00\:02.0/drm/card0/gt/engines/bcs/0/instance 0 Id is a global id used from submitting commands from userspace through execbuf : $ cat /sys/devices/pci0000\:00/0000\:00\:02.0/drm/card0/gt/engines/bcs/0/id 3 Class maps to enum drm_i915_gem_engine_class in i915_drm.h : $ cat /sys/devices/pci0000\:00/0000\:00\:02.0/drm/card0/gt/engines/bcs/0/class 1 Further capabilities can be added later as attributes of each engine. v2: Add capabilities sub directory (Tvrtko) Move engines directory to drm/card/gt (Chris) v3: Move engines to drm/card/gt/engines/ (Tvrtko) Add instance attribute to engines (Tvrtko) v4: Fix dev_priv->gt_topology.engines_classes_kobj size (Lionel) Signed-off-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com> --- drivers/gpu/drm/i915/i915_drv.h | 6 ++ drivers/gpu/drm/i915/i915_sysfs.c | 178 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_engine_cs.c | 12 +++ drivers/gpu/drm/i915/intel_ringbuffer.h | 4 + 4 files changed, 200 insertions(+)