On 10/14/2026 3:11 PM, Rodrigo Vivi wrote:
On Fri, Oct 09, 2026 at 10:31:51AM -0700, Zack McKevitt wrote:
Add a new node type, attribute set, and doit function for performing
telemetry read and write operations using the DRM RAS interface.

Hi Zack,

I'm glad to see more users of drm-ras and glad that the flexibility is paying of


Signed-off-by: Zack McKevitt <[email protected]>
---
  Documentation/netlink/specs/drm_ras.yaml |  55 ++++++-
  drivers/gpu/drm/drm_ras.c                | 193 ++++++++++++++++++++++-
  drivers/gpu/drm/drm_ras_nl.c             |  29 ++++
  drivers/gpu/drm/drm_ras_nl.h             |   6 +
  include/drm/drm_ras.h                    |  39 +++++
  include/uapi/drm/drm_ras.h               |  14 ++

just to confirm, you used ynl to generate these most of these files right?
Please make sure to mention in the commit message so we are sure.


Correct, ill add a note in the commit in the v2.

  6 files changed, 327 insertions(+), 9 deletions(-)

diff --git a/Documentation/netlink/specs/drm_ras.yaml 
b/Documentation/netlink/specs/drm_ras.yaml
index a88fdab7a1c7..d558eb1cba3f 100644
--- a/Documentation/netlink/specs/drm_ras.yaml
+++ b/Documentation/netlink/specs/drm_ras.yaml
@@ -16,7 +16,7 @@ definitions:
      type: enum
      name: node-type
      value-start: 1
-    entries: [error-counter]
+    entries: [error-counter, telemetry]
      doc: >-
           Type of the node. Currently, only error-counter nodes are

Please ensure that the doc is also updated.


Will do.

           supported, which expose reliability counters for a hardware/software
@@ -100,6 +100,31 @@ attribute-sets:
          name: error-value
          type: u32
          doc: Current value of the error counter.
+  -
+    name: telemetry-attrs
+    attributes:
+      -
+        name: node-id
+        type: u32
+        doc: >-
+             Unique identifier for the node.
+             Assigned dynamically by the DRM RAS core upon registration.
+      -
+        name: field
+        type: u32
+        doc:  Telemetry field to be read/written, e.g., SoC temp.
+      -
+        name: value
+        type: u64
+        doc: Value of telemetry field that was read/written.
+      -
+        name: status
+        type: s32
+        doc: Status of the telemetry operation.

is this status the hardware itself that is giving?
Please add some doc around it to help us understand the need and see if
is generic enough and mandatory vs optional.


Yes, it is a hardware status. I will add a more in depth doc for this in the next iteration.

+      -
+        name: pad
+        type: pad
+        doc: Alignment padding used for 64-bit
operations:
    list:
@@ -198,6 +223,34 @@ operations:
              - node-id
              - error-id
              - error-threshold
+    -
+      name: telemetry-read

should we do get_/set_ instead of read/write to align with the existing
entries?!


Sure, will do.

+      doc: Perform a read of the specified telemetry field.
+      attribute-set: telemetry-attrs
+      do:
+        pre: drm-ras-nl-pre-telemetry
+        request:
+          attributes:
+            - node-id
+            - field

how do users know what field to read?
Why not a dump or at least a dump field for field discovery?


We define the set of fields for our devices in our uapi file qaic_accel.h, but I think a dump is probably a good idea here.

+        reply:
+          attributes:
+            - value
+            - status
+    -
+      name: telemetry-write
+      doc: Perform a write of the specified telemetry field.

what is a write telemetry operation?

Our device has certain fields that are writable, e.g., QAIC_DEVINFO_THERMAL_WARNING_TEMP where the host is able to modify the device's thermal warning temperature threshold. These make up a small subset of telemetry fields.

can we make this optional?


Sure, I will make it optional in the v2.

even write is allowed without admin cap?


It seems like all of our existing writable telemetry fields should be restricted to admin cap only, so I will add the admin cap back in for this.

+      attribute-set: telemetry-attrs
+      do:
+        pre: drm-ras-nl-pre-telemetry
+        request:
+          attributes:
+            - node-id
+            - field
+            - value
+        reply:
+          attributes:
+            - status
mcast-groups:
    list:
diff --git a/drivers/gpu/drm/drm_ras.c b/drivers/gpu/drm/drm_ras.c
index b099eb67836e..b88293295cda 100644
--- a/drivers/gpu/drm/drm_ras.c
+++ b/drivers/gpu/drm/drm_ras.c
@@ -53,6 +53,15 @@
   * 7. SET_ERROR_THRESHOLD: Set error threshold of a given counter.
   *    Userspace must provide Node ID, Error ID and threshold to be set.
   *
+ * 8. TELEMETRY_READ: Read a telemetry value.
+ *    Userspace must provide Node ID and telemetry field to be read.
+ *    Returns the status of the read and the value read (if valid).
+ *
+ * 9. TELEMETRY_WRITE: Write a telemetry value.
+ *    Userspace must provide Node ID, the telemetry field to write, and the 
value
+ *    to be written.
+ *    Returns the status of the write.
+ *
   * Node registration:
   *
   * - drm_ras_node_register(): Registers a new node and assigns
@@ -63,7 +72,6 @@
   * Node type:
   *
   * - ERROR_COUNTER:
- *     + Currently, only error counters are supported.
   *     + The driver must implement the query_error_counter() callback to 
provide
   *       the name and the value of the error counter.
   *     + The driver must provide a error_counter_range.last value informing 
the
@@ -81,6 +89,12 @@
   *       that are raised by the hardware.
   *     + The driver is responsible for error threshold bounds checking.
   *
+ * - TELEMETRY:
+ *     + The driver must implement the telemetry_read() and telemetry_write()
+ *       callbacks.
+ *     + The driver can optionally provide a pre_doit callback, 
telemetry_pre(),
+ *       to enforce permissions checks on telemetry operations.
+ *
   * Netlink handlers:
   *
   * - drm_ras_nl_list_nodes_dumpit(): Implements the LIST_NODES
@@ -95,6 +109,12 @@
   *   operation, fetching the error threshold of a specific counter.
   * - drm_ras_nl_set_error_threshold_doit(): Implements the 
SET_ERROR_THRESHOLD doit
   *   operation, setting the error threshold of a specific counter.
+ * - drm_ras_nl_pre_telemetry(): Implements the TELEMETRY_READ and 
TELEMETRY_WRITE
+ *   pre_doit operation, returning 0 if valid.
+ * - drm_ras_nl_telemetry_read_doit(): Implements the TELEMETRY_READ doit 
operation,
+ *   invoking the driver's telemetry_read callback and returning the results.
+ * - drm_ras_nl_telemetry_write_doit(): Implements the TELEMETRY_WRITE doit 
operation,
+ *   invoking the driver's telemetry_write callback and returning the results.
   */
static DEFINE_XARRAY_ALLOC(drm_ras_xa);
@@ -617,6 +637,156 @@ int drm_ras_nl_set_error_threshold_doit(struct sk_buff 
*skb, struct genl_info *i
        return set_node_error_threshold(node_id, error_id, threshold);
  }
+/**
+ * drm_ras_nl_pre_telemetry - Enforce permission checks
+ * @ops: Generic netlink operations
+ * @skb: Netlink message buffer
+ * @info: Generic Netlink info containing attributes of the request
+ *
+ * Enforces a telemetry node's pre_doit conditions (if they exist).
+ *
+ * Returns 0 if no pre_doit is specified, otherwise returns the pre_doit.
+ */
+int drm_ras_nl_pre_telemetry(const struct genl_split_ops *ops, struct sk_buff 
*skb,
+                            struct genl_info *info)
+{
+       struct drm_ras_node *node;
+       u32 node_id;
+
+       node_id = nla_get_u32(info->attrs[DRM_RAS_A_TELEMETRY_ATTRS_NODE_ID]);
+       node = xa_load(&drm_ras_xa, node_id);
+
+       if (node->telemetry_pre)
+               return node->telemetry_pre(node);
+
+       return 0;
+}
+
+/**
+ * drm_ras_nl_telemetry_read_doit() - Query a telemetry value
+ * @skb: Netlink message buffer
+ * @info: Generic Netlink info containing attributes of the request
+ *
+ * Extracts the telemetry field from the netlink attributes and
+ * reads that value from the device.
+ *
+ * Return: 0 on success, or negative errno on failure.
+ */
+int drm_ras_nl_telemetry_read_doit(struct sk_buff *skb, struct genl_info *info)
+{
+       struct drm_ras_node *node;
+       struct sk_buff *msg;
+       struct nlattr *hdr;
+       u32 node_id, field;
+       s32 status;
+       u64 value;
+       int ret;
+
+       if (!info->attrs || !info->attrs[DRM_RAS_A_TELEMETRY_ATTRS_FIELD])
+               return -EINVAL;
+
+       field = nla_get_u32(info->attrs[DRM_RAS_A_TELEMETRY_ATTRS_FIELD]);
+       node_id = nla_get_u32(info->attrs[DRM_RAS_A_TELEMETRY_ATTRS_NODE_ID]);
+
+       node = xa_load(&drm_ras_xa, node_id);
+       if (!node || !node->telemetry_read)
+               return -ENOENT;
+
+       msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       hdr = genlmsg_iput(msg, info);
+       if (!hdr) {
+               nlmsg_free(msg);
+               return -EMSGSIZE;
+       }
+
+       ret = node->telemetry_read(node, &field, &value, &status);
+       if (ret) {
+               nlmsg_free(msg);
+               return ret;
+       }
+
+       ret = nla_put_s32(msg, DRM_RAS_A_TELEMETRY_ATTRS_STATUS, status);
+       if (ret) {
+               genlmsg_cancel(msg, hdr);
+               nlmsg_free(msg);
+               return ret;
+       }
+
+       ret = nla_put_64bit(msg, DRM_RAS_A_TELEMETRY_ATTRS_VALUE, sizeof(value),
+                           &value, DRM_RAS_A_TELEMETRY_ATTRS_PAD);
+       if (ret) {
+               genlmsg_cancel(msg, hdr);
+               nlmsg_free(msg);
+               return ret;
+       }
+
+       genlmsg_end(msg, hdr);
+
+       return genlmsg_reply(msg, info);
+}
+
+/**
+ * drm_ras_nl_telemetry_write_doit() - Write a telemetry value
+ * @skb: Netlink message buffer
+ * @info: Generic Netlink info containing attributes of the request
+ *
+ * Extracts the telemetry field and value to be written from the netlink
+ * attributes and writes that value to the specified field on the device.
+ *
+ * Return: 0 on success, or negative errno on failure.
+ */
+int drm_ras_nl_telemetry_write_doit(struct sk_buff *skb, struct genl_info 
*info)
+{
+       struct drm_ras_node *node;
+       struct sk_buff *msg;
+       struct nlattr *hdr;
+       u32 node_id, field;
+       s32 status;
+       u64 value;
+       int ret;
+
+       if (!info->attrs || !info->attrs[DRM_RAS_A_TELEMETRY_ATTRS_FIELD])
+               return -EINVAL;
+
+       value = nla_get_u64(info->attrs[DRM_RAS_A_TELEMETRY_ATTRS_VALUE]);
+       field = nla_get_u32(info->attrs[DRM_RAS_A_TELEMETRY_ATTRS_FIELD]);
+       node_id = nla_get_u32(info->attrs[DRM_RAS_A_TELEMETRY_ATTRS_NODE_ID]);
+
+       node = xa_load(&drm_ras_xa, node_id);
+       if (!node || !node->telemetry_read)
+               return -ENOENT;
+
+       msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       hdr = genlmsg_iput(msg, info);
+       if (!hdr) {
+               nlmsg_free(msg);
+               return -EMSGSIZE;
+       }
+
+       ret = node->telemetry_write(node, &field, &value, &status);
+       if (ret) {
+               nlmsg_free(msg);
+               return ret;
+       }
+
+       ret = nla_put_s32(msg, DRM_RAS_A_TELEMETRY_ATTRS_STATUS, status);
+       if (ret) {
+               genlmsg_cancel(msg, hdr);
+               nlmsg_free(msg);
+               return ret;
+       }
+
+       genlmsg_end(msg, hdr);
+
+       return genlmsg_reply(msg, info);
+}
+
  /**
   * drm_ras_node_register() - Register a new RAS node
   * @node: Node structure to register
@@ -631,14 +801,21 @@ int drm_ras_node_register(struct drm_ras_node *node)
        if (!node->device_name || !node->node_name)
                return -EINVAL;
- /* Currently, only Error Counter Endpoints are supported */
-       if (node->type != DRM_RAS_NODE_TYPE_ERROR_COUNTER)
-               return -EINVAL;
-
-       /* Mandatory entries for Error Counter Node */
-       if (node->type == DRM_RAS_NODE_TYPE_ERROR_COUNTER &&
-           (!node->error_counter_range.last || !node->query_error_counter))
+       /* Currently, only Error Counter and Telemetry endpoints are supported 
*/
+       switch (node->type) {
+       case DRM_RAS_NODE_TYPE_ERROR_COUNTER:
+               /* Mandatory entries for Error Counter Node */
+               if (!node->error_counter_range.last || 
!node->query_error_counter)
+                       return -EINVAL;
+               break;
+       case DRM_RAS_NODE_TYPE_TELEMETRY:
+               /* Mandatory entries for Telemetry Node */
+               if (!node->telemetry_read || !node->telemetry_write)
+                       return -EINVAL;
+               break;
+       default:
                return -EINVAL;
+       }
return xa_alloc(&drm_ras_xa, &node->id, node, xa_limit_32b, GFP_KERNEL);
  }
diff --git a/drivers/gpu/drm/drm_ras_nl.c b/drivers/gpu/drm/drm_ras_nl.c
index ff0dc406c27c..4577bc1a333e 100644
--- a/drivers/gpu/drm/drm_ras_nl.c
+++ b/drivers/gpu/drm/drm_ras_nl.c
@@ -41,6 +41,19 @@ static const struct nla_policy 
drm_ras_set_error_threshold_nl_policy[DRM_RAS_A_E
        [DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_THRESHOLD] = { .type = NLA_U32, },
  };
+/* DRM_RAS_CMD_TELEMETRY_READ - do */
+static const struct nla_policy 
drm_ras_telemetry_read_nl_policy[DRM_RAS_A_TELEMETRY_ATTRS_FIELD + 1] = {
+       [DRM_RAS_A_TELEMETRY_ATTRS_NODE_ID] = { .type = NLA_U32, },
+       [DRM_RAS_A_TELEMETRY_ATTRS_FIELD] = { .type = NLA_U32, },
+};
+
+/* DRM_RAS_CMD_TELEMETRY_WRITE - do */
+static const struct nla_policy 
drm_ras_telemetry_write_nl_policy[DRM_RAS_A_TELEMETRY_ATTRS_VALUE + 1] = {
+       [DRM_RAS_A_TELEMETRY_ATTRS_NODE_ID] = { .type = NLA_U32, },
+       [DRM_RAS_A_TELEMETRY_ATTRS_FIELD] = { .type = NLA_U32, },
+       [DRM_RAS_A_TELEMETRY_ATTRS_VALUE] = { .type = NLA_U64, },
+};
+
  /* Ops table for drm_ras */
  static const struct genl_split_ops drm_ras_nl_ops[] = {
        {
@@ -83,6 +96,22 @@ static const struct genl_split_ops drm_ras_nl_ops[] = {
                .maxattr        = DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_THRESHOLD,
                .flags          = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
        },
+       {
+               .cmd            = DRM_RAS_CMD_TELEMETRY_READ,
+               .pre_doit       = drm_ras_nl_pre_telemetry,
+               .doit           = drm_ras_nl_telemetry_read_doit,
+               .policy         = drm_ras_telemetry_read_nl_policy,
+               .maxattr        = DRM_RAS_A_TELEMETRY_ATTRS_FIELD,
+               .flags          = GENL_CMD_CAP_DO,
+       },
+       {
+               .cmd            = DRM_RAS_CMD_TELEMETRY_WRITE,
+               .pre_doit       = drm_ras_nl_pre_telemetry,
+               .doit           = drm_ras_nl_telemetry_write_doit,
+               .policy         = drm_ras_telemetry_write_nl_policy,
+               .maxattr        = DRM_RAS_A_TELEMETRY_ATTRS_VALUE,
+               .flags          = GENL_CMD_CAP_DO,
+       },
  };
static const struct genl_multicast_group drm_ras_nl_mcgrps[] = {
diff --git a/drivers/gpu/drm/drm_ras_nl.h b/drivers/gpu/drm/drm_ras_nl.h
index 9aef097ea10f..4bdd7ed268a1 100644
--- a/drivers/gpu/drm/drm_ras_nl.h
+++ b/drivers/gpu/drm/drm_ras_nl.h
@@ -12,6 +12,9 @@
#include <uapi/drm/drm_ras.h> +int drm_ras_nl_pre_telemetry(const struct genl_split_ops *ops,
+                            struct sk_buff *skb, struct genl_info *info);
+
  int drm_ras_nl_list_nodes_dumpit(struct sk_buff *skb,
                                 struct netlink_callback *cb);
  int drm_ras_nl_get_error_counter_doit(struct sk_buff *skb,
@@ -24,6 +27,9 @@ int drm_ras_nl_get_error_threshold_doit(struct sk_buff *skb,
                                        struct genl_info *info);
  int drm_ras_nl_set_error_threshold_doit(struct sk_buff *skb,
                                        struct genl_info *info);
+int drm_ras_nl_telemetry_read_doit(struct sk_buff *skb, struct genl_info 
*info);
+int drm_ras_nl_telemetry_write_doit(struct sk_buff *skb,
+                                   struct genl_info *info);
enum {
        DRM_RAS_NLGRP_ERROR_REPORT,
diff --git a/include/drm/drm_ras.h b/include/drm/drm_ras.h
index 1765a3130b09..2a2cdd44839d 100644
--- a/include/drm/drm_ras.h
+++ b/include/drm/drm_ras.h
@@ -100,6 +100,45 @@ struct drm_ras_node {
         */
        int (*set_error_threshold)(struct drm_ras_node *node, u32 error_id, u32 
threshold);
+ /**
+        * @telemetry_pre:
+        *
+        * This callback is used to enforce any pre_doit conditions necessary
+        * for telemetry. This is an optional callback for telemetry_node.
+        *
+        * Returns 0 on success, negative error code on failure.
+        */
+       int (*telemetry_pre)(struct drm_ras_node *ep);
+
+       /**
+        * @telemetry_read:
+        *
+        * This callback is used for a drm-ras telemetry node to read a
+        * telemetry value. This is a mandatory callback for telemetry_node.
+        *
+        * The callback will read the desired device-specific field into value
+        * and update status corresponding to a device-specific error code.
+        *
+        * Returns 0 on success, negative error code on failure.
+        */
+       int (*telemetry_read)(struct drm_ras_node *ep, u32 *field, u64 *value,
+                             s32 *status);
+
+       /**
+        * @telemetry_write:
+        *
+        * This callback is used for a drm-ras telemetry node to write a
+        * telemetry value. This is a mandatory callback for telemetry_node.
+        *
+        * The callback will write the value to the desired device-specific
+        * field and update status corresponding to a device-specific error
+        * code.
+        *
+        * Returns 0 on success, negative error code on failure.
+        */
+       int (*telemetry_write)(struct drm_ras_node *ep, u32 *field, u64 *value,
+                              s32 *status);
+
        /** @priv: Driver private data */
        void *priv;
  };
diff --git a/include/uapi/drm/drm_ras.h b/include/uapi/drm/drm_ras.h
index 2f832275ee6e..d39c78a9ed5a 100644
--- a/include/uapi/drm/drm_ras.h
+++ b/include/uapi/drm/drm_ras.h
@@ -16,6 +16,7 @@
   */
  enum drm_ras_node_type {
        DRM_RAS_NODE_TYPE_ERROR_COUNTER = 1,
+       DRM_RAS_NODE_TYPE_TELEMETRY,
  };
enum {
@@ -51,6 +52,17 @@ enum {
        DRM_RAS_A_ERROR_EVENT_ATTRS_MAX = (__DRM_RAS_A_ERROR_EVENT_ATTRS_MAX - 
1)
  };
+enum {
+       DRM_RAS_A_TELEMETRY_ATTRS_NODE_ID = 1,
+       DRM_RAS_A_TELEMETRY_ATTRS_FIELD,
+       DRM_RAS_A_TELEMETRY_ATTRS_VALUE,
+       DRM_RAS_A_TELEMETRY_ATTRS_STATUS,
+       DRM_RAS_A_TELEMETRY_ATTRS_PAD,
+
+       __DRM_RAS_A_TELEMETRY_ATTRS_MAX,
+       DRM_RAS_A_TELEMETRY_ATTRS_MAX = (__DRM_RAS_A_TELEMETRY_ATTRS_MAX - 1)
+};
+
  enum {
        DRM_RAS_CMD_LIST_NODES = 1,
        DRM_RAS_CMD_GET_ERROR_COUNTER,
@@ -58,6 +70,8 @@ enum {
        DRM_RAS_CMD_ERROR_EVENT,
        DRM_RAS_CMD_GET_ERROR_THRESHOLD,
        DRM_RAS_CMD_SET_ERROR_THRESHOLD,
+       DRM_RAS_CMD_TELEMETRY_READ,
+       DRM_RAS_CMD_TELEMETRY_WRITE,
__DRM_RAS_CMD_MAX,
        DRM_RAS_CMD_MAX = (__DRM_RAS_CMD_MAX - 1)
--
2.43.0


Reply via email to