* [PATCH 0/3] Extend drm_ras for qaic telemetry
@ 2026-10-09 17:31 Zack McKevitt
2026-10-09 17:31 ` [PATCH 1/3] drm/drm_ras: Remove admin-perm flag from list-nodes operation Zack McKevitt
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Zack McKevitt @ 2026-10-09 17:31 UTC (permalink / raw)
To: dri-devel, netdev, riana.tauro, aravind.iddamsetty, rodrigo.vivi,
raag.jadav, simona.vetter, airlied, kuba, ogabbay, lizhi.hou,
karol.wachowski
Cc: jeff.hugo, carl.vanderlip, troy.hanson, youssef.abdulrahman,
Zack McKevitt
This patch series extends the drm_ras netlink interface to include a new
node type for telemetry nodes. Telemetry nodes support two operations,
reading and writing, and drivers may specify an optional predoit function
to validate each operation.
Telemetry read operations take the drm_ras node id and telemetry field to
be read and return the field's value and any status reported by the driver.
Telemetry write operations take the node id, telemetry field, and value,
and return only the status reported by the driver.
This extension removes the admin requirement on the list-nodes operation.
This allows drm_ras nodes to be discovered by userspace programs for drivers
that do not necessarily require admin privileges, like qaic.
The new drm_ras telemetry interface is implemented by the qaic driver, which
exposes the telemetry fields to userspace and enforces filesystem-esque
permission checks on telemetry operations in its telemetry_pre callback.
Zack McKevitt (3):
drm/drm_ras: Remove admin-perm flag from list-nodes operation
drm/drm_ras: Extend DRM RAS interface to include telemetry nodes
accel/qaic: Add callbacks for DRM RAS netlink telemetry
Documentation/netlink/specs/drm_ras.yaml | 56 ++-
drivers/accel/qaic/Makefile | 1 +
drivers/accel/qaic/qaic.h | 19 +
drivers/accel/qaic/qaic_drv.c | 19 +
drivers/accel/qaic/qaic_telemetry.c | 463 +++++++++++++++++++++++
drivers/accel/qaic/qaic_telemetry.h | 18 +
drivers/gpu/drm/drm_ras.c | 193 +++++++++-
drivers/gpu/drm/drm_ras_nl.c | 31 +-
drivers/gpu/drm/drm_ras_nl.h | 6 +
include/drm/drm_ras.h | 39 ++
include/uapi/drm/drm_ras.h | 14 +
include/uapi/drm/qaic_accel.h | 28 ++
12 files changed, 876 insertions(+), 11 deletions(-)
create mode 100644 drivers/accel/qaic/qaic_telemetry.c
create mode 100644 drivers/accel/qaic/qaic_telemetry.h
--
2.43.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/3] drm/drm_ras: Remove admin-perm flag from list-nodes operation
2026-10-09 17:31 [PATCH 0/3] Extend drm_ras for qaic telemetry Zack McKevitt
@ 2026-10-09 17:31 ` Zack McKevitt
2026-10-09 17:31 ` [PATCH 2/3] drm/drm_ras: Extend DRM RAS interface to include telemetry nodes Zack McKevitt
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Zack McKevitt @ 2026-10-09 17:31 UTC (permalink / raw)
To: dri-devel, netdev, riana.tauro, aravind.iddamsetty, rodrigo.vivi,
raag.jadav, simona.vetter, airlied, kuba, ogabbay, lizhi.hou,
karol.wachowski
Cc: jeff.hugo, carl.vanderlip, troy.hanson, youssef.abdulrahman,
Zack McKevitt
Drivers may want to avoid requiring CAP_NET_ADMIN for subsequent
drm-ras operations. However, userspace must still be able to access
the list of RAS nodes to determine the node IDs. So, drop the
CAP_NET_ADMIN requirement for dumping RAS nodes so that drivers
with different permission requirements are still able to use the
interface.
This prepares the interface to be used for telemetry by the qaic
driver.
Signed-off-by: Zack McKevitt <zachary.mckevitt@oss.qualcomm.com>
---
Documentation/netlink/specs/drm_ras.yaml | 1 -
drivers/gpu/drm/drm_ras_nl.c | 2 +-
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/Documentation/netlink/specs/drm_ras.yaml b/Documentation/netlink/specs/drm_ras.yaml
index 4c2ac9a1ba3f..a88fdab7a1c7 100644
--- a/Documentation/netlink/specs/drm_ras.yaml
+++ b/Documentation/netlink/specs/drm_ras.yaml
@@ -112,7 +112,6 @@ operations:
the node IDs. These IDs are required for all subsequent
operations on nodes, such as querying error counters.
attribute-set: node-attrs
- flags: [admin-perm]
dump:
reply:
attributes:
diff --git a/drivers/gpu/drm/drm_ras_nl.c b/drivers/gpu/drm/drm_ras_nl.c
index c9f5e0ceb3b5..ff0dc406c27c 100644
--- a/drivers/gpu/drm/drm_ras_nl.c
+++ b/drivers/gpu/drm/drm_ras_nl.c
@@ -46,7 +46,7 @@ static const struct genl_split_ops drm_ras_nl_ops[] = {
{
.cmd = DRM_RAS_CMD_LIST_NODES,
.dumpit = drm_ras_nl_list_nodes_dumpit,
- .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DUMP,
+ .flags = GENL_CMD_CAP_DUMP,
},
{
.cmd = DRM_RAS_CMD_GET_ERROR_COUNTER,
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 2/3] drm/drm_ras: Extend DRM RAS interface to include telemetry nodes
2026-10-09 17:31 [PATCH 0/3] Extend drm_ras for qaic telemetry Zack McKevitt
2026-10-09 17:31 ` [PATCH 1/3] drm/drm_ras: Remove admin-perm flag from list-nodes operation Zack McKevitt
@ 2026-10-09 17:31 ` Zack McKevitt
2026-10-09 17:31 ` [PATCH 3/3] accel/qaic: Add callbacks for DRM RAS netlink telemetry Zack McKevitt
2026-10-10 9:42 ` [syzbot ci] Re: Extend drm_ras for qaic telemetry syzbot ci
3 siblings, 0 replies; 5+ messages in thread
From: Zack McKevitt @ 2026-10-09 17:31 UTC (permalink / raw)
To: dri-devel, netdev, riana.tauro, aravind.iddamsetty, rodrigo.vivi,
raag.jadav, simona.vetter, airlied, kuba, ogabbay, lizhi.hou,
karol.wachowski
Cc: jeff.hugo, carl.vanderlip, troy.hanson, youssef.abdulrahman,
Zack McKevitt
Add a new node type, attribute set, and doit function for performing
telemetry read and write operations using the DRM RAS interface.
Signed-off-by: Zack McKevitt <zachary.mckevitt@oss.qualcomm.com>
---
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 ++
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
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.
+ -
+ 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
+ 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
+ reply:
+ attributes:
+ - value
+ - status
+ -
+ name: telemetry-write
+ doc: Perform a write of the specified telemetry field.
+ 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
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 3/3] accel/qaic: Add callbacks for DRM RAS netlink telemetry
2026-10-09 17:31 [PATCH 0/3] Extend drm_ras for qaic telemetry Zack McKevitt
2026-10-09 17:31 ` [PATCH 1/3] drm/drm_ras: Remove admin-perm flag from list-nodes operation Zack McKevitt
2026-10-09 17:31 ` [PATCH 2/3] drm/drm_ras: Extend DRM RAS interface to include telemetry nodes Zack McKevitt
@ 2026-10-09 17:31 ` Zack McKevitt
2026-10-10 9:42 ` [syzbot ci] Re: Extend drm_ras for qaic telemetry syzbot ci
3 siblings, 0 replies; 5+ messages in thread
From: Zack McKevitt @ 2026-10-09 17:31 UTC (permalink / raw)
To: dri-devel, netdev, riana.tauro, aravind.iddamsetty, rodrigo.vivi,
raag.jadav, simona.vetter, airlied, kuba, ogabbay, lizhi.hou,
karol.wachowski
Cc: jeff.hugo, carl.vanderlip, troy.hanson, youssef.abdulrahman,
Zack McKevitt
Use the DRM RAS netlink infrastructure to perform device telemetry.
During PCI probe, create a DRM RAS node with DRM_RAS_NODE_TYPE_TELEMETRY
type and add callbacks to telemetry_request for read and write operations.
Co-developed-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
Signed-off-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
Signed-off-by: Zack McKevitt <zachary.mckevitt@oss.qualcomm.com>
---
drivers/accel/qaic/Makefile | 1 +
drivers/accel/qaic/qaic.h | 19 ++
drivers/accel/qaic/qaic_drv.c | 19 ++
drivers/accel/qaic/qaic_telemetry.c | 463 ++++++++++++++++++++++++++++
drivers/accel/qaic/qaic_telemetry.h | 18 ++
include/uapi/drm/qaic_accel.h | 28 ++
6 files changed, 548 insertions(+)
create mode 100644 drivers/accel/qaic/qaic_telemetry.c
create mode 100644 drivers/accel/qaic/qaic_telemetry.h
diff --git a/drivers/accel/qaic/Makefile b/drivers/accel/qaic/Makefile
index 71f727b74da3..b2bbe9b3e077 100644
--- a/drivers/accel/qaic/Makefile
+++ b/drivers/accel/qaic/Makefile
@@ -13,6 +13,7 @@ qaic-y := \
qaic_ras.o \
qaic_ssr.o \
qaic_sysfs.o \
+ qaic_telemetry.o \
qaic_timesync.o \
sahara.o
diff --git a/drivers/accel/qaic/qaic.h b/drivers/accel/qaic/qaic.h
index 02ca99000e2a..f5dbeadba658 100644
--- a/drivers/accel/qaic/qaic.h
+++ b/drivers/accel/qaic/qaic.h
@@ -17,6 +17,7 @@
#include <linux/workqueue.h>
#include <drm/drm_device.h>
#include <drm/drm_gem.h>
+#include <drm/drm_ras.h>
#define QAIC_DBC_BASE SZ_128K
#define QAIC_DBC_SIZE SZ_4K
@@ -203,6 +204,24 @@ struct qaic_device {
void *ssr_mhi_buf;
/* DBC which is under SSR. Sentinel U32_MAX would mean that no SSR in progress */
u32 ssr_dbc;
+ /* MHI telemetry channel device */
+ struct mhi_device *tele_ch;
+ /* Head in list of requests queued in MHI telemetry device */
+ struct list_head tele_xfer_list;
+ /* Req. ID of request that will be queued next in MHI telemetry device */
+ u32 tele_next_seq_num;
+ /* Synchronizes MHI telemetry device transactions and its xfer list */
+ struct mutex tele_mutex;
+ /*
+ * TRUE: A tx MHI transaction has failed and a rx buffer is still queued
+ * in telemetry device. Such a buffer is considered lost rx buffer
+ * FALSE: No rx buffer is lost in telemetry device
+ */
+ bool tele_lost_buf;
+ /* Work queue for tasks related to MHI telemetry device */
+ struct workqueue_struct *tele_wq;
+ /* Netlink telemetry node */
+ struct drm_ras_node *ras_node;
/* Array of DBC struct of this device */
struct dma_bridge_chan dbc[] __counted_by(num_dbc);
};
diff --git a/drivers/accel/qaic/qaic_drv.c b/drivers/accel/qaic/qaic_drv.c
index c1bb6db78d12..d2aed51052a6 100644
--- a/drivers/accel/qaic/qaic_drv.c
+++ b/drivers/accel/qaic/qaic_drv.c
@@ -32,6 +32,7 @@
#include "qaic_debugfs.h"
#include "qaic_ras.h"
#include "qaic_ssr.h"
+#include "qaic_telemetry.h"
#include "qaic_timesync.h"
#include "sahara.h"
@@ -381,6 +382,7 @@ static void qaic_notify_reset(struct qaic_device *qdev)
qdev->dev_state = QAIC_OFFLINE;
/* wake up any waiters to avoid waiting for timeouts at sync */
wake_all_cntl(qdev);
+ wake_all_telemetry(qdev);
for (i = 0; i < qdev->num_dbc; ++i)
wakeup_dbc(qdev, i);
synchronize_srcu(&qdev->dev_lock);
@@ -431,6 +433,9 @@ static struct qaic_device *create_qdev(struct pci_dev *pdev,
if (ret)
return NULL;
ret = drmm_mutex_init(drm, &qdev->bootlog_mutex);
+ if (ret)
+ return NULL;
+ ret = drmm_mutex_init(drm, &qdev->tele_mutex);
if (ret)
return NULL;
@@ -443,6 +448,9 @@ static struct qaic_device *create_qdev(struct pci_dev *pdev,
qdev->ssr_wq = qaicm_wq_init(drm, "qaic_ssr");
if (IS_ERR(qdev->ssr_wq))
return NULL;
+ qdev->tele_wq = qaicm_wq_init(drm, "qaic_tele");
+ if (IS_ERR(qdev->tele_wq))
+ return NULL;
ret = qaicm_srcu_init(drm, &qdev->dev_lock);
if (ret)
@@ -458,6 +466,7 @@ static struct qaic_device *create_qdev(struct pci_dev *pdev,
INIT_LIST_HEAD(&qdev->cntl_xfer_list);
INIT_LIST_HEAD(&qdev->bootlog);
+ INIT_LIST_HEAD(&qdev->tele_xfer_list);
INIT_LIST_HEAD(&qddev->users);
for (i = 0; i < qdev->num_dbc; ++i) {
@@ -595,6 +604,10 @@ static int qaic_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (ret)
return ret;
+ ret = qaic_drm_ras_register_node(qdev);
+ if (ret)
+ return ret;
+
qdev->mhi_cntrl = qaic_mhi_register_controller(pdev, qdev->bar_mhi, mhi_irq,
qdev->single_msi, config->family);
if (IS_ERR(qdev->mhi_cntrl)) {
@@ -615,6 +628,7 @@ static void qaic_pci_remove(struct pci_dev *pdev)
qaic_dev_reset_clean_local_state(qdev);
qaic_mhi_free_controller(qdev->mhi_cntrl, link_up);
+ qaic_drm_ras_unregister_node(qdev);
qaic_destroy_drm_device(qdev, QAIC_NO_PARTITION);
}
@@ -813,6 +827,10 @@ static int __init qaic_init(void)
goto free_bootlog;
}
+ ret = qaic_telemetry_register();
+ if (ret)
+ pr_debug("qaic: qaic_telemetry_register failed %d\n", ret);
+
return 0;
free_bootlog:
@@ -842,6 +860,7 @@ static void __exit qaic_exit(void)
* reinitializing the link_up state after the cleanup is done.
*/
link_up = true;
+ qaic_telemetry_unregister();
qaic_ssr_unregister();
qaic_ras_unregister();
qaic_bootlog_unregister();
diff --git a/drivers/accel/qaic/qaic_telemetry.c b/drivers/accel/qaic/qaic_telemetry.c
new file mode 100644
index 000000000000..dbfb36cf319c
--- /dev/null
+++ b/drivers/accel/qaic/qaic_telemetry.c
@@ -0,0 +1,463 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. */
+/* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */
+
+#include <drm/drm_file.h>
+#include <drm/drm_ras.h>
+#include <asm/byteorder.h>
+#include <linux/completion.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/kref.h>
+#include <linux/list.h>
+#include <linux/mhi.h>
+#include <linux/mutex.h>
+#include <linux/namei.h>
+#include <linux/path.h>
+#include <linux/sprintf.h>
+#include <linux/srcu.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+#include <uapi/drm/qaic_accel.h>
+
+#include "qaic.h"
+#include "qaic_telemetry.h"
+
+#define MAGIC 0x55AA
+#define VERSION 0x1
+
+enum fw_errno {
+ ERR_READ_ONLY = -1,
+ ERR_NOT_SUPPORTED = -2,
+ ERR_WHILE_PROCESSING = -3,
+ ERR_INVALID_VALUE = -4,
+ ERR_INVALID_HEADER = -5,
+};
+
+enum cmd_type {
+ TYPE_READ, /* read value from device */
+ TYPE_WRITE, /* write value to device */
+};
+
+enum msg_type {
+ MSG_PUSH, /* async push from device */
+ MSG_REQ, /* sync request to device */
+ MSG_RESP, /* sync response from device */
+};
+
+struct telemetry_data {
+ u8 cmd;
+ u8 cmd_type;
+ u8 status;
+ __le64 val; /* signed */
+} __packed;
+
+struct telemetry_header {
+ __le16 magic;
+ __le16 ver;
+ __le32 seq_num;
+ u8 type;
+ u8 id;
+ __le16 len;
+} __packed;
+
+struct telemetry_msg { /* little endian encoded */
+ struct telemetry_header hdr;
+ struct telemetry_data data;
+} __packed;
+
+struct wrapper_msg {
+ struct kref ref_count;
+ struct telemetry_msg msg;
+};
+
+struct xfer_queue_elem {
+ /*
+ * Node in list of ongoing transfer request on telemetry channel.
+ * Maintained by root device struct
+ */
+ struct list_head list;
+ /* Sequence number of this transfer request */
+ u32 seq_num;
+ /* This is used to wait on until completion of transfer request */
+ struct completion xfer_done;
+ /* Received data from device */
+ void *buf;
+};
+
+struct resp_work {
+ /* Work struct to schedule work coming on QAIC_TELEMETRY channel */
+ struct work_struct work;
+ /* Root struct of device, used to access device resources */
+ struct qaic_device *qdev;
+ /* Buffer used by MHI for transfer requests */
+ void *buf;
+};
+
+static void free_wrapper(struct kref *ref)
+{
+ struct wrapper_msg *wrapper = container_of(ref, struct wrapper_msg, ref_count);
+
+ kfree(wrapper);
+}
+
+int telemetry_request(struct qaic_device *qdev, u8 cmd, u8 cmd_type, s64 *val)
+{
+ struct wrapper_msg *wrapper;
+ struct xfer_queue_elem elem;
+ struct telemetry_msg *resp;
+ struct telemetry_msg *req;
+ long ret = 0;
+
+ wrapper = kzalloc_obj(*wrapper);
+ if (!wrapper)
+ return -ENOMEM;
+
+ kref_init(&wrapper->ref_count);
+ req = &wrapper->msg;
+
+ ret = mutex_lock_interruptible(&qdev->tele_mutex);
+ if (ret)
+ goto free_req;
+
+ req->hdr.magic = cpu_to_le16(MAGIC);
+ req->hdr.ver = cpu_to_le16(VERSION);
+ req->hdr.seq_num = cpu_to_le32(qdev->tele_next_seq_num++);
+ req->hdr.type = MSG_REQ;
+ req->hdr.id = 0;
+ req->hdr.len = cpu_to_le16(sizeof(req->data));
+
+ req->data.cmd = cmd;
+ req->data.cmd_type = cmd_type;
+ req->data.status = 0;
+ if (cmd_type == TYPE_READ)
+ req->data.val = cpu_to_le64(0);
+ else
+ req->data.val = cpu_to_le64(*val);
+
+ elem.seq_num = qdev->tele_next_seq_num - 1;
+ elem.buf = NULL;
+ init_completion(&elem.xfer_done);
+ if (likely(!qdev->tele_lost_buf)) {
+ resp = kzalloc_obj(*resp);
+ if (!resp) {
+ mutex_unlock(&qdev->tele_mutex);
+ ret = -ENOMEM;
+ goto free_req;
+ }
+
+ ret = mhi_queue_buf(qdev->tele_ch, DMA_FROM_DEVICE, resp, sizeof(*resp), MHI_EOT);
+ if (ret) {
+ mutex_unlock(&qdev->tele_mutex);
+ goto free_resp;
+ }
+ } else {
+ /*
+ * we lost a buffer because we queued a recv buf, but then
+ * queuing the corresponding tx buf failed. To try to avoid
+ * a memory leak, lets reclaim it and use it for this
+ * transaction.
+ */
+ qdev->tele_lost_buf = false;
+ }
+
+ kref_get(&wrapper->ref_count);
+ ret = mhi_queue_buf(qdev->tele_ch, DMA_TO_DEVICE, req, sizeof(*req), MHI_EOT);
+ if (ret) {
+ qdev->tele_lost_buf = true;
+ kref_put(&wrapper->ref_count, free_wrapper);
+ mutex_unlock(&qdev->tele_mutex);
+ goto free_req;
+ }
+
+ list_add_tail(&elem.list, &qdev->tele_xfer_list);
+ mutex_unlock(&qdev->tele_mutex);
+
+ ret = wait_for_completion_interruptible_timeout(&elem.xfer_done, HZ);
+ /*
+ * not using _interruptable because we have to cleanup or we'll
+ * likely cause memory corruption
+ */
+ mutex_lock(&qdev->tele_mutex);
+ if (!list_empty(&elem.list))
+ list_del(&elem.list);
+ if (!ret && !elem.buf)
+ ret = -ETIMEDOUT;
+ else if (ret > 0 && !elem.buf)
+ ret = -EIO;
+ mutex_unlock(&qdev->tele_mutex);
+
+ resp = elem.buf;
+
+ if (ret < 0)
+ goto free_resp;
+
+ if (le16_to_cpu(resp->hdr.magic) != MAGIC || le16_to_cpu(resp->hdr.ver) != VERSION ||
+ resp->hdr.type != MSG_RESP || resp->hdr.id != 0 ||
+ le16_to_cpu(resp->hdr.len) != sizeof(resp->data) || resp->data.cmd != cmd ||
+ resp->data.cmd_type != cmd_type) {
+ ret = -EINVAL;
+ goto free_resp;
+ }
+
+ if (resp->data.status) {
+ switch ((int8_t) resp->data.status) {
+ case ERR_READ_ONLY:
+ ret = -EROFS;
+ break;
+ case ERR_NOT_SUPPORTED:
+ ret = -EOPNOTSUPP;
+ break;
+ case ERR_WHILE_PROCESSING:
+ ret = -EREMOTEIO;
+ break;
+ case ERR_INVALID_VALUE:
+ ret = -EINVAL;
+ break;
+ case ERR_INVALID_HEADER:
+ ret = -EBADR;
+ break;
+ default:
+ ret = -EPROTO;
+ }
+ goto free_resp;
+ }
+
+ if (cmd_type == TYPE_READ)
+ *val = le64_to_cpu(resp->data.val);
+
+ ret = 0;
+
+free_resp:
+ kfree(resp);
+free_req:
+ kref_put(&wrapper->ref_count, free_wrapper);
+
+ return ret;
+}
+
+static int qaic_telemetry_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id)
+{
+ struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(mhi_dev->mhi_cntrl->cntrl_dev));
+ int ret;
+
+ ret = mhi_prepare_for_transfer(mhi_dev);
+ if (ret)
+ return ret;
+
+ dev_set_drvdata(&mhi_dev->dev, qdev);
+ qdev->tele_ch = mhi_dev;
+ qdev->tele_lost_buf = false;
+
+ return 0;
+}
+
+static void qaic_telemetry_mhi_remove(struct mhi_device *mhi_dev)
+{
+ struct qaic_device *qdev = dev_get_drvdata(&mhi_dev->dev);
+
+ mhi_unprepare_from_transfer(qdev->tele_ch);
+ qdev->tele_ch = NULL;
+}
+
+static void resp_worker(struct work_struct *work)
+{
+ struct resp_work *resp = container_of(work, struct resp_work, work);
+ struct qaic_device *qdev = resp->qdev;
+ struct telemetry_msg *msg = resp->buf;
+ struct xfer_queue_elem *elem;
+ struct xfer_queue_elem *i;
+ bool found = false;
+
+ if (le16_to_cpu(msg->hdr.magic) != MAGIC) {
+ kfree(msg);
+ kfree(resp);
+ return;
+ }
+
+ mutex_lock(&qdev->tele_mutex);
+ list_for_each_entry_safe(elem, i, &qdev->tele_xfer_list, list) {
+ if (elem->seq_num == le32_to_cpu(msg->hdr.seq_num)) {
+ found = true;
+ list_del_init(&elem->list);
+ elem->buf = msg;
+ complete_all(&elem->xfer_done);
+ break;
+ }
+ }
+ mutex_unlock(&qdev->tele_mutex);
+
+ if (!found)
+ /* request must have timed out, drop packet */
+ kfree(msg);
+
+ kfree(resp);
+}
+
+static void qaic_telemetry_mhi_ul_xfer_cb(struct mhi_device *mhi_dev,
+ struct mhi_result *mhi_result)
+{
+ struct telemetry_msg *msg = mhi_result->buf_addr;
+ struct wrapper_msg *wrapper;
+
+ wrapper = container_of(msg, struct wrapper_msg, msg);
+ kref_put(&wrapper->ref_count, free_wrapper);
+}
+
+static void qaic_telemetry_mhi_dl_xfer_cb(struct mhi_device *mhi_dev,
+ struct mhi_result *mhi_result)
+{
+ struct qaic_device *qdev = dev_get_drvdata(&mhi_dev->dev);
+ struct telemetry_msg *msg = mhi_result->buf_addr;
+ struct resp_work *resp;
+
+ if (mhi_result->transaction_status) {
+ kfree(msg);
+ return;
+ }
+
+ resp = kmalloc(sizeof(*resp), GFP_ATOMIC);
+ if (!resp) {
+ pci_err(qdev->pdev, "dl_xfer_cb alloc fail, dropping message\n");
+ kfree(msg);
+ return;
+ }
+
+ INIT_WORK(&resp->work, resp_worker);
+ resp->qdev = qdev;
+ resp->buf = msg;
+ queue_work(qdev->tele_wq, &resp->work);
+}
+
+static const struct mhi_device_id qaic_telemetry_mhi_match_table[] = {
+ { .chan = "QAIC_TELEMETRY", },
+ {},
+};
+
+static struct mhi_driver qaic_telemetry_mhi_driver = {
+ .id_table = qaic_telemetry_mhi_match_table,
+ .remove = qaic_telemetry_mhi_remove,
+ .probe = qaic_telemetry_mhi_probe,
+ .ul_xfer_cb = qaic_telemetry_mhi_ul_xfer_cb,
+ .dl_xfer_cb = qaic_telemetry_mhi_dl_xfer_cb,
+ .driver = {
+ .name = "qaic_telemetry",
+ },
+};
+
+int qaic_telemetry_register(void)
+{
+ return mhi_driver_register(&qaic_telemetry_mhi_driver);
+}
+
+void qaic_telemetry_unregister(void)
+{
+ mhi_driver_unregister(&qaic_telemetry_mhi_driver);
+}
+
+void wake_all_telemetry(struct qaic_device *qdev)
+{
+ struct xfer_queue_elem *elem;
+ struct xfer_queue_elem *i;
+
+ mutex_lock(&qdev->tele_mutex);
+ list_for_each_entry_safe(elem, i, &qdev->tele_xfer_list, list) {
+ list_del_init(&elem->list);
+ complete_all(&elem->xfer_done);
+ }
+ qdev->tele_lost_buf = false;
+ mutex_unlock(&qdev->tele_mutex);
+}
+
+static int qaic_telemetry_pre(struct drm_ras_node *ep)
+{
+ struct qaic_device *qdev = ep->priv;
+ struct inode *dev_inode;
+ struct path path;
+ char devpath[24];
+ int ret;
+
+ snprintf(devpath, sizeof(devpath), "/dev/accel/%s", to_accel_kdev(qdev->qddev)->kobj.name);
+ ret = kern_path(devpath, LOOKUP_FOLLOW, &path);
+ if (ret)
+ return -EINVAL;
+
+ dev_inode = d_inode(path.dentry);
+ ret = inode_permission(&nop_mnt_idmap, dev_inode, MAY_READ | MAY_WRITE);
+ path_put(&path);
+
+ return ret;
+}
+
+static int qaic_telemetry_read(struct drm_ras_node *ep, u32 *field, u64 *value, s32 *status)
+{
+ struct qaic_device *qdev = ep->priv;
+
+ *status = (s32)telemetry_request(qdev, *field, TYPE_READ, value);
+
+ return *status;
+}
+
+static int qaic_telemetry_write(struct drm_ras_node *ep, u32 *field, u64 *value, s32 *status)
+{
+ struct qaic_device *qdev = ep->priv;
+
+ *status = (s32)telemetry_request(qdev, *field, TYPE_WRITE, value);
+
+ return *status;
+}
+
+int qaic_drm_ras_register_node(struct qaic_device *qdev)
+{
+ struct drm_ras_node *node;
+ struct pci_dev *pdev = qdev->pdev;
+ const char *device_name, *node_name;
+ int ret;
+
+ node = kzalloc_obj(struct drm_ras_node);
+ if (!node)
+ return -ENOMEM;
+
+ device_name = kasprintf(GFP_KERNEL, "%04x:%02x:%02x.%d",
+ pci_domain_nr(pdev->bus), pdev->bus->number,
+ PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
+ if (!device_name) {
+ kfree(node);
+ return -ENOMEM;
+ }
+
+ node_name = kasprintf(GFP_KERNEL, "qaic_telemetry");
+ if (!node_name) {
+ kfree(device_name);
+ kfree(node);
+ return -ENOMEM;
+ }
+
+ node->device_name = device_name;
+ node->node_name = node_name;
+ node->type = DRM_RAS_NODE_TYPE_TELEMETRY;
+ node->priv = qdev;
+ node->telemetry_read = qaic_telemetry_read;
+ node->telemetry_write = qaic_telemetry_write;
+ node->telemetry_pre = qaic_telemetry_pre;
+
+ ret = drm_ras_node_register(node);
+ if (ret) {
+ kfree(device_name);
+ kfree(node_name);
+ kfree(qdev->ras_node);
+ return ret;
+ }
+
+ qdev->ras_node = node;
+ return 0;
+}
+
+void qaic_drm_ras_unregister_node(struct qaic_device *qdev)
+{
+ drm_ras_node_unregister(qdev->ras_node);
+ kfree(qdev->ras_node->node_name);
+ kfree(qdev->ras_node->device_name);
+ kfree(qdev->ras_node);
+}
diff --git a/drivers/accel/qaic/qaic_telemetry.h b/drivers/accel/qaic/qaic_telemetry.h
new file mode 100644
index 000000000000..a80922591e37
--- /dev/null
+++ b/drivers/accel/qaic/qaic_telemetry.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+/* Copyright (c) 2020, The Linux Foundation. All rights reserved. */
+
+#ifndef __QAIC_TELEMETRY_H__
+#define __QAIC_TELEMETRY_H__
+
+#include "qaic.h"
+
+int telemetry_request(struct qaic_device *qdev, u8 cmd, u8 cmd_type, s64 *val);
+int qaic_drm_ras_register_node(struct qaic_device *qdev);
+void qaic_drm_ras_unregister_node(struct qaic_device *qdev);
+
+int qaic_telemetry_register(void);
+void qaic_telemetry_unregister(void);
+void wake_all_telemetry(struct qaic_device *qdev);
+
+#endif /* __QAIC_TELEMETRY_H__ */
diff --git a/include/uapi/drm/qaic_accel.h b/include/uapi/drm/qaic_accel.h
index c92d0309d583..5aac05101426 100644
--- a/include/uapi/drm/qaic_accel.h
+++ b/include/uapi/drm/qaic_accel.h
@@ -51,6 +51,34 @@ extern "C" {
#define QAIC_TRANS_VALIDATE_PARTITION_FROM_DEV 19
#define QAIC_TRANS_VALIDATE_PARTITION_TO_DEV 20
+/* Telemetry fields */
+#define QAIC_DEVINFO_THERMAL_SOC_MAX_TEMP 1
+#define QAIC_DEVINFO_THERMAL_BOARD_TEMP 2
+#define QAIC_DEVINFO_THERMAL_BOARD_MAX_TEMP 3
+#define QAIC_DEVINFO_THERMAL_DDR_TEMP 4
+#define QAIC_DEVINFO_THERMAL_WARNING_TEMP 5
+#define QAIC_DEVINFO_THERMAL_SHUTDOWN_TEMP 6
+#define QAIC_DEVINFO_BOARD_TDP 7
+#define QAIC_DEVINFO_BOARD_POWER 8
+#define QAIC_DEVINFO_POWER_STATE 9
+#define QAIC_DEVINFO_POWER_MAX 10
+#define QAIC_DEVINFO_THROTTLE_PERCENT 11
+#define QAIC_DEVINFO_THROTTLE_TIME 12
+#define QAIC_DEVINFO_UPTIME 13
+#define QAIC_DEVINFO_THERMAL_SOC_FLOOR_TEMP 14
+#define QAIC_DEVINFO_THERMAL_SOC_CEILING_TEMP 15
+#define QAIC_DEVINFO_VOLTAGE_CX 16
+#define QAIC_DEVINFO_VOLTAGE_MX 17
+#define QAIC_DEVINFO_VOLTAGE_NSP_CX 18
+#define QAIC_DEVINFO_VOLTAGE_NSP_MX 19
+#define QAIC_DEVINFO_CFG_TOPS 20
+#define QAIC_DEVINFO_THERMAL_PMIC_TEMP_A 21
+#define QAIC_DEVINFO_THERMAL_PMIC_TEMP_C 22
+#define QAIC_DEVINFO_THERMAL_PMIC_TEMP_E 23
+#define QAIC_DEVINFO_POWER_ACTION 24
+#define QAIC_DEVINFO_SOC_TDP 25
+#define QAIC_DEVINFO_SOC_POWER 26
+
/**
* struct qaic_manage_trans_hdr - Header for a transaction in a manage message.
* @type: In. Identifies this transaction. See QAIC_TRANS_* defines.
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [syzbot ci] Re: Extend drm_ras for qaic telemetry
2026-10-09 17:31 [PATCH 0/3] Extend drm_ras for qaic telemetry Zack McKevitt
` (2 preceding siblings ...)
2026-10-09 17:31 ` [PATCH 3/3] accel/qaic: Add callbacks for DRM RAS netlink telemetry Zack McKevitt
@ 2026-10-10 9:42 ` syzbot ci
3 siblings, 0 replies; 5+ messages in thread
From: syzbot ci @ 2026-10-10 9:42 UTC (permalink / raw)
To: airlied, aravind.iddamsetty, carl.vanderlip, dri-devel, jeff.hugo,
karol.wachowski, kuba, lizhi.hou, netdev, ogabbay, raag.jadav,
riana.tauro, rodrigo.vivi, simona.vetter, troy.hanson,
youssef.abdulrahman, zachary.mckevitt
Cc: syzbot, syzkaller-bugs
syzbot ci has tested the following series
[v1] Extend drm_ras for qaic telemetry
https://lore.kernel.org/all/20261009173152.2436195-1-zachary.mckevitt@oss.qualcomm.com
* [PATCH 1/3] drm/drm_ras: Remove admin-perm flag from list-nodes operation
* [PATCH 2/3] drm/drm_ras: Extend DRM RAS interface to include telemetry nodes
* [PATCH 3/3] accel/qaic: Add callbacks for DRM RAS netlink telemetry
and found the following issue:
general protection fault in drm_ras_nl_pre_telemetry
Full report is available here:
https://ci.syzbot.org/series/6915fb7c-08cd-48b4-b8a4-3834f3e60de5
***
general protection fault in drm_ras_nl_pre_telemetry
tree: drm-next
URL: https://gitlab.freedesktop.org/drm/kernel.git
base: f2bf20011b95401f63e573d54134544c452c4117
arch: amd64
compiler: Debian clang version 22.1.8 (++20260613092233+e80beda6e255-1~exp1~20260613092250.77), Debian LLD 22.1.8
config: https://ci.syzbot.org/builds/d48f7944-a0fb-4983-a3c6-c037c81dd832/config
syz repro: https://ci.syzbot.org/findings/c2ca90ba-3f79-4e56-94d4-b1ddc64056e5/syz_repro
Oops: general protection fault, probably for non-canonical address 0xdffffc0000000000: 0000 [#1] SMP KASAN PTI
KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007]
CPU: 1 UID: 0 PID: 5808 Comm: syz.0.17 Not tainted syzkaller #0 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014
RIP: 0010:nla_get_u32 include/net/netlink.h:1734 [inline]
RIP: 0010:drm_ras_nl_pre_telemetry+0x60/0xe0 drivers/gpu/drm/drm_ras.c:656
Code: 8b 1b 48 83 c3 08 48 89 d8 48 c1 e8 03 42 80 3c 38 00 74 08 48 89 df e8 3e 3c 9a fc 48 8b 1b 48 83 c3 04 48 89 d8 48 c1 e8 03 <42> 0f b6 04 38 84 c0 75 5b 8b 33 48 c7 c7 80 d3 6f 90 e8 c9 8c 49
RSP: 0018:ffffc9000379f420 EFLAGS: 00010247
RAX: 0000000000000000 RBX: 0000000000000004 RCX: ffff8881bf17bc00
RDX: 0000000000000000 RSI: ffff8881bf38d380 RDI: ffffc9000379f600
RBP: ffffc9000379f588 R08: ffffc9000379f4ff R09: 0000000000000000
R10: ffffc9000379f4d0 R11: ffffffff859eb380 R12: ffffc9000379f600
R13: dffffc0000000000 R14: ffff8881bf38d380 R15: dffffc0000000000
FS: 00007f2c256e36c0(0000) GS:ffff8882a7cb6000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000200000000100 CR3: 000000016e7ee000 CR4: 00000000000006f0
Call Trace:
<TASK>
genl_family_rcv_msg_doit+0x1db/0x340 net/netlink/genetlink.c:1109
genl_family_rcv_msg net/netlink/genetlink.c:1194 [inline]
genl_rcv_msg+0x614/0x7a0 net/netlink/genetlink.c:1209
netlink_rcv_skb+0x226/0x4a0 net/netlink/af_netlink.c:2575
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1218
netlink_unicast_kernel net/netlink/af_netlink.c:1338 [inline]
netlink_unicast+0x7bd/0x940 net/netlink/af_netlink.c:1364
netlink_sendmsg+0x813/0xb40 net/netlink/af_netlink.c:1919
sock_sendmsg_nosec+0x13a/0x180 net/socket.c:800
__sock_sendmsg net/socket.c:815 [inline]
____sys_sendmsg+0x54e/0x850 net/socket.c:2713
___sys_sendmsg+0x2a5/0x360 net/socket.c:2767
__sys_sendmsg net/socket.c:2799 [inline]
__do_sys_sendmsg net/socket.c:2804 [inline]
__se_sys_sendmsg net/socket.c:2802 [inline]
__x64_sys_sendmsg+0x1b1/0x290 net/socket.c:2802
do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline]
do_syscall_64+0x166/0x520 arch/x86/entry/syscall_64.c:84
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f2c2479e1d9
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f2c256e3028 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007f2c24a25fa0 RCX: 00007f2c2479e1d9
RDX: 0000000000000000 RSI: 0000200000000100 RDI: 0000000000000004
RBP: 00007f2c24835888 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f2c24a26038 R14: 00007f2c24a25fa0 R15: 00007ffc88247b58
</TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:nla_get_u32 include/net/netlink.h:1734 [inline]
RIP: 0010:drm_ras_nl_pre_telemetry+0x60/0xe0 drivers/gpu/drm/drm_ras.c:656
Code: 8b 1b 48 83 c3 08 48 89 d8 48 c1 e8 03 42 80 3c 38 00 74 08 48 89 df e8 3e 3c 9a fc 48 8b 1b 48 83 c3 04 48 89 d8 48 c1 e8 03 <42> 0f b6 04 38 84 c0 75 5b 8b 33 48 c7 c7 80 d3 6f 90 e8 c9 8c 49
RSP: 0018:ffffc9000379f420 EFLAGS: 00010247
RAX: 0000000000000000 RBX: 0000000000000004 RCX: ffff8881bf17bc00
RDX: 0000000000000000 RSI: ffff8881bf38d380 RDI: ffffc9000379f600
RBP: ffffc9000379f588 R08: ffffc9000379f4ff R09: 0000000000000000
R10: ffffc9000379f4d0 R11: ffffffff859eb380 R12: ffffc9000379f600
R13: dffffc0000000000 R14: ffff8881bf38d380 R15: dffffc0000000000
FS: 00007f2c256e36c0(0000) GS:ffff8882a7cb6000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f2c247eb8c0 CR3: 000000016e7ee000 CR4: 00000000000006f0
----------------
Code disassembly (best guess):
0: 8b 1b mov (%rbx),%ebx
2: 48 83 c3 08 add $0x8,%rbx
6: 48 89 d8 mov %rbx,%rax
9: 48 c1 e8 03 shr $0x3,%rax
d: 42 80 3c 38 00 cmpb $0x0,(%rax,%r15,1)
12: 74 08 je 0x1c
14: 48 89 df mov %rbx,%rdi
17: e8 3e 3c 9a fc call 0xfc9a3c5a
1c: 48 8b 1b mov (%rbx),%rbx
1f: 48 83 c3 04 add $0x4,%rbx
23: 48 89 d8 mov %rbx,%rax
26: 48 c1 e8 03 shr $0x3,%rax
* 2a: 42 0f b6 04 38 movzbl (%rax,%r15,1),%eax <-- trapping instruction
2f: 84 c0 test %al,%al
31: 75 5b jne 0x8e
33: 8b 33 mov (%rbx),%esi
35: 48 c7 c7 80 d3 6f 90 mov $0xffffffff906fd380,%rdi
3c: e8 .byte 0xe8
3d: c9 leave
3e: 8c .byte 0x8c
3f: 49 rex.WB
***
If these findings have caused you to resend the series or submit a
separate fix, please add the following tag to your commit message:
Tested-by: syzbot@syzkaller.appspotmail.com
---
This report is generated by a bot. It may contain errors.
syzbot ci engineers can be reached at syzkaller@googlegroups.com.
To test a fix for this bug, please reply with `#syz test`
(on a separate line) and attach the patch to the email.
Notes:
- The patch will be applied on top of the tested series (as an
incremental fix).
- To test a new version of the whole series, please send it directly
to syzbot@lists.linux.dev.
- Arguments like custom git repos and branches are not supported.
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-10-10 9:42 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-09 17:31 [PATCH 0/3] Extend drm_ras for qaic telemetry Zack McKevitt
2026-10-09 17:31 ` [PATCH 1/3] drm/drm_ras: Remove admin-perm flag from list-nodes operation Zack McKevitt
2026-10-09 17:31 ` [PATCH 2/3] drm/drm_ras: Extend DRM RAS interface to include telemetry nodes Zack McKevitt
2026-10-09 17:31 ` [PATCH 3/3] accel/qaic: Add callbacks for DRM RAS netlink telemetry Zack McKevitt
2026-10-10 9:42 ` [syzbot ci] Re: Extend drm_ras for qaic telemetry syzbot ci
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox