| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/10/09 21:03 | flow | patch-triage |
12mArgs:null Results: {
"EnableConfigs": [
"DRM_RAS",
"DRM",
"NET"
],
"FocusSymbols": [
"drm_ras_nl_pre_telemetry",
"drm_ras_nl_telemetry_read_doit",
"drm_ras_nl_telemetry_write_doit",
"drm_ras_node_register"
],
"KMSANReasoning": "The patch series introduces telemetry operations to the DRM RAS subsystem and implements telemetry support in the Qualcomm Cloud AI (qaic) accelerator driver.\n\nAnalysis of memory initialization and user-space boundaries:\n1. User-space data exposure: The new netlink commands (DRM_RAS_CMD_TELEMETRY_READ and DRM_RAS_CMD_TELEMETRY_WRITE) return scalar primitive values ('status' as s32 via nla_put_s32 and 'value' as u64 via nla_put_64bit with DRM_RAS_A_TELEMETRY_ATTRS_PAD). In successful telemetry execution paths, both 'status' and 'value' are explicitly assigned before being passed to nla_put_*; on error, the netlink message is aborted and freed, so no uninitialized stack data is emitted. Furthermore, 64-bit netlink padding is zeroed by the netlink subsystem (skb_put_zero in __nla_reserve_nohdr).\n2. Heap and stack structures: In qaic_telemetry.c, messages (wrapper_msg, telemetry_msg) and drm_ras_node are allocated with zeroing allocators (kzalloc_obj). The stack structure xfer_queue_elem has all its used fields (seq_num, buf, xfer_done, list) initialized before submission. The resp_work struct allocated in the DL transfer callback has all its fields explicitly initialized.\n3. Potential bug surfaces: The patch introduces risks of NULL pointer dereferences (e.g. missing checks for Netlink attributes in drm_ras_nl_pre_telemetry and drm_ras_nl_telemetry_write_doit, unchecked xa_load results prior to dereferencing node-\u003etelemetry_pre, checking node-\u003etelemetry_read instead of node-\u003etelemetry_write in telemetry_write_doit) and a memory leak in qaic_drm_ras_register_node error handling. These bugs manifest as general kernel crashes (oops/page fault) or memory leaks, which are handled by standard KASAN/LOCKDEP/kmemleak testing and do not involve uninitialized memory access.\n\nTherefore, dedicated KMSAN fuzzing is not required.",
"NeedsKMSAN": false,
"Reasoning": "The patch expands the DRM RAS Generic Netlink subsystem with new telemetry operations (TELEMETRY_READ and TELEMETRY_WRITE) and removes GENL_ADMIN_PERM from node listing. The Generic Netlink family 'drm_ras' is registered during DRM core initialization and can be queried from unprivileged userspace over standard AF_NETLINK sockets without physical hardware. The newly added Netlink pre-handler drm_ras_nl_pre_telemetry() directly dereferences the looked-up node pointer without validating that it is non-NULL, introducing an easily triggered NULL pointer dereference when querying an invalid node ID.",
"WorthFuzzing": true
} |
| 1/1 | 2026/10/09 21:03 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit fcad46b922e1e426dbe5607ebd256b48f44fe6e0\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Fri Oct 9 21:03:30 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/Documentation/netlink/specs/drm_ras.yaml b/Documentation/netlink/specs/drm_ras.yaml\nindex 4c2ac9a1ba3f5..d558eb1cba3f1 100644\n--- a/Documentation/netlink/specs/drm_ras.yaml\n+++ b/Documentation/netlink/specs/drm_ras.yaml\n@@ -16,7 +16,7 @@ definitions:\n type: enum\n name: node-type\n value-start: 1\n- entries: [error-counter]\n+ entries: [error-counter, telemetry]\n doc: \u003e-\n Type of the node. Currently, only error-counter nodes are\n supported, which expose reliability counters for a hardware/software\n@@ -100,6 +100,31 @@ attribute-sets:\n name: error-value\n type: u32\n doc: Current value of the error counter.\n+ -\n+ name: telemetry-attrs\n+ attributes:\n+ -\n+ name: node-id\n+ type: u32\n+ doc: \u003e-\n+ Unique identifier for the node.\n+ Assigned dynamically by the DRM RAS core upon registration.\n+ -\n+ name: field\n+ type: u32\n+ doc: Telemetry field to be read/written, e.g., SoC temp.\n+ -\n+ name: value\n+ type: u64\n+ doc: Value of telemetry field that was read/written.\n+ -\n+ name: status\n+ type: s32\n+ doc: Status of the telemetry operation.\n+ -\n+ name: pad\n+ type: pad\n+ doc: Alignment padding used for 64-bit\n \n operations:\n list:\n@@ -112,7 +137,6 @@ operations:\n the node IDs. These IDs are required for all subsequent\n operations on nodes, such as querying error counters.\n attribute-set: node-attrs\n- flags: [admin-perm]\n dump:\n reply:\n attributes:\n@@ -199,6 +223,34 @@ operations:\n - node-id\n - error-id\n - error-threshold\n+ -\n+ name: telemetry-read\n+ doc: Perform a read of the specified telemetry field.\n+ attribute-set: telemetry-attrs\n+ do:\n+ pre: drm-ras-nl-pre-telemetry\n+ request:\n+ attributes:\n+ - node-id\n+ - field\n+ reply:\n+ attributes:\n+ - value\n+ - status\n+ -\n+ name: telemetry-write\n+ doc: Perform a write of the specified telemetry field.\n+ attribute-set: telemetry-attrs\n+ do:\n+ pre: drm-ras-nl-pre-telemetry\n+ request:\n+ attributes:\n+ - node-id\n+ - field\n+ - value\n+ reply:\n+ attributes:\n+ - status\n \n mcast-groups:\n list:\ndiff --git a/drivers/accel/qaic/Makefile b/drivers/accel/qaic/Makefile\nindex 71f727b74da3b..b2bbe9b3e0774 100644\n--- a/drivers/accel/qaic/Makefile\n+++ b/drivers/accel/qaic/Makefile\n@@ -13,6 +13,7 @@ qaic-y := \\\n \tqaic_ras.o \\\n \tqaic_ssr.o \\\n \tqaic_sysfs.o \\\n+\tqaic_telemetry.o \\\n \tqaic_timesync.o \\\n \tsahara.o\n \ndiff --git a/drivers/accel/qaic/qaic.h b/drivers/accel/qaic/qaic.h\nindex 02ca99000e2ae..f5dbeadba658e 100644\n--- a/drivers/accel/qaic/qaic.h\n+++ b/drivers/accel/qaic/qaic.h\n@@ -17,6 +17,7 @@\n #include \u003clinux/workqueue.h\u003e\n #include \u003cdrm/drm_device.h\u003e\n #include \u003cdrm/drm_gem.h\u003e\n+#include \u003cdrm/drm_ras.h\u003e\n \n #define QAIC_DBC_BASE\t\tSZ_128K\n #define QAIC_DBC_SIZE\t\tSZ_4K\n@@ -203,6 +204,24 @@ struct qaic_device {\n \tvoid\t\t\t*ssr_mhi_buf;\n \t/* DBC which is under SSR. Sentinel U32_MAX would mean that no SSR in progress */\n \tu32\t\t\tssr_dbc;\n+\t/* MHI telemetry channel device */\n+\tstruct mhi_device\t*tele_ch;\n+\t/* Head in list of requests queued in MHI telemetry device */\n+\tstruct list_head\ttele_xfer_list;\n+\t/* Req. ID of request that will be queued next in MHI telemetry device */\n+\tu32\t\t\ttele_next_seq_num;\n+\t/* Synchronizes MHI telemetry device transactions and its xfer list */\n+\tstruct mutex\t\ttele_mutex;\n+\t/*\n+\t * TRUE: A tx MHI transaction has failed and a rx buffer is still queued\n+\t * in telemetry device. Such a buffer is considered lost rx buffer\n+\t * FALSE: No rx buffer is lost in telemetry device\n+\t */\n+\tbool\t\t\ttele_lost_buf;\n+\t/* Work queue for tasks related to MHI telemetry device */\n+\tstruct workqueue_struct\t*tele_wq;\n+\t/* Netlink telemetry node */\n+\tstruct drm_ras_node *ras_node;\n \t/* Array of DBC struct of this device */\n \tstruct dma_bridge_chan\tdbc[] __counted_by(num_dbc);\n };\ndiff --git a/drivers/accel/qaic/qaic_drv.c b/drivers/accel/qaic/qaic_drv.c\nindex c1bb6db78d129..d2aed51052a66 100644\n--- a/drivers/accel/qaic/qaic_drv.c\n+++ b/drivers/accel/qaic/qaic_drv.c\n@@ -32,6 +32,7 @@\n #include \"qaic_debugfs.h\"\n #include \"qaic_ras.h\"\n #include \"qaic_ssr.h\"\n+#include \"qaic_telemetry.h\"\n #include \"qaic_timesync.h\"\n #include \"sahara.h\"\n \n@@ -381,6 +382,7 @@ static void qaic_notify_reset(struct qaic_device *qdev)\n \tqdev-\u003edev_state = QAIC_OFFLINE;\n \t/* wake up any waiters to avoid waiting for timeouts at sync */\n \twake_all_cntl(qdev);\n+\twake_all_telemetry(qdev);\n \tfor (i = 0; i \u003c qdev-\u003enum_dbc; ++i)\n \t\twakeup_dbc(qdev, i);\n \tsynchronize_srcu(\u0026qdev-\u003edev_lock);\n@@ -431,6 +433,9 @@ static struct qaic_device *create_qdev(struct pci_dev *pdev,\n \tif (ret)\n \t\treturn NULL;\n \tret = drmm_mutex_init(drm, \u0026qdev-\u003ebootlog_mutex);\n+\tif (ret)\n+\t\treturn NULL;\n+\tret = drmm_mutex_init(drm, \u0026qdev-\u003etele_mutex);\n \tif (ret)\n \t\treturn NULL;\n \n@@ -443,6 +448,9 @@ static struct qaic_device *create_qdev(struct pci_dev *pdev,\n \tqdev-\u003essr_wq = qaicm_wq_init(drm, \"qaic_ssr\");\n \tif (IS_ERR(qdev-\u003essr_wq))\n \t\treturn NULL;\n+\tqdev-\u003etele_wq = qaicm_wq_init(drm, \"qaic_tele\");\n+\tif (IS_ERR(qdev-\u003etele_wq))\n+\t\treturn NULL;\n \n \tret = qaicm_srcu_init(drm, \u0026qdev-\u003edev_lock);\n \tif (ret)\n@@ -458,6 +466,7 @@ static struct qaic_device *create_qdev(struct pci_dev *pdev,\n \n \tINIT_LIST_HEAD(\u0026qdev-\u003ecntl_xfer_list);\n \tINIT_LIST_HEAD(\u0026qdev-\u003ebootlog);\n+\tINIT_LIST_HEAD(\u0026qdev-\u003etele_xfer_list);\n \tINIT_LIST_HEAD(\u0026qddev-\u003eusers);\n \n \tfor (i = 0; i \u003c qdev-\u003enum_dbc; ++i) {\n@@ -595,6 +604,10 @@ static int qaic_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)\n \tif (ret)\n \t\treturn ret;\n \n+\tret = qaic_drm_ras_register_node(qdev);\n+\tif (ret)\n+\t\treturn ret;\n+\n \tqdev-\u003emhi_cntrl = qaic_mhi_register_controller(pdev, qdev-\u003ebar_mhi, mhi_irq,\n \t\t\t\t\t\t qdev-\u003esingle_msi, config-\u003efamily);\n \tif (IS_ERR(qdev-\u003emhi_cntrl)) {\n@@ -615,6 +628,7 @@ static void qaic_pci_remove(struct pci_dev *pdev)\n \n \tqaic_dev_reset_clean_local_state(qdev);\n \tqaic_mhi_free_controller(qdev-\u003emhi_cntrl, link_up);\n+\tqaic_drm_ras_unregister_node(qdev);\n \tqaic_destroy_drm_device(qdev, QAIC_NO_PARTITION);\n }\n \n@@ -813,6 +827,10 @@ static int __init qaic_init(void)\n \t\tgoto free_bootlog;\n \t}\n \n+\tret = qaic_telemetry_register();\n+\tif (ret)\n+\t\tpr_debug(\"qaic: qaic_telemetry_register failed %d\\n\", ret);\n+\n \treturn 0;\n \n free_bootlog:\n@@ -842,6 +860,7 @@ static void __exit qaic_exit(void)\n \t * reinitializing the link_up state after the cleanup is done.\n \t */\n \tlink_up = true;\n+\tqaic_telemetry_unregister();\n \tqaic_ssr_unregister();\n \tqaic_ras_unregister();\n \tqaic_bootlog_unregister();\ndiff --git a/drivers/accel/qaic/qaic_telemetry.c b/drivers/accel/qaic/qaic_telemetry.c\nnew file mode 100644\nindex 0000000000000..dbfb36cf319ce\n--- /dev/null\n+++ b/drivers/accel/qaic/qaic_telemetry.c\n@@ -0,0 +1,463 @@\n+// SPDX-License-Identifier: GPL-2.0-only\n+\n+/* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. */\n+/* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */\n+\n+#include \u003cdrm/drm_file.h\u003e\n+#include \u003cdrm/drm_ras.h\u003e\n+#include \u003casm/byteorder.h\u003e\n+#include \u003clinux/completion.h\u003e\n+#include \u003clinux/fs.h\u003e\n+#include \u003clinux/kernel.h\u003e\n+#include \u003clinux/kref.h\u003e\n+#include \u003clinux/list.h\u003e\n+#include \u003clinux/mhi.h\u003e\n+#include \u003clinux/mutex.h\u003e\n+#include \u003clinux/namei.h\u003e\n+#include \u003clinux/path.h\u003e\n+#include \u003clinux/sprintf.h\u003e\n+#include \u003clinux/srcu.h\u003e\n+#include \u003clinux/types.h\u003e\n+#include \u003clinux/workqueue.h\u003e\n+#include \u003cuapi/drm/qaic_accel.h\u003e\n+\n+#include \"qaic.h\"\n+#include \"qaic_telemetry.h\"\n+\n+#define MAGIC\t\t\t\t0x55AA\n+#define VERSION\t\t\t\t0x1\n+\n+enum fw_errno {\n+\tERR_READ_ONLY\t\t= -1,\n+\tERR_NOT_SUPPORTED\t= -2,\n+\tERR_WHILE_PROCESSING\t= -3,\n+\tERR_INVALID_VALUE\t= -4,\n+\tERR_INVALID_HEADER\t= -5,\n+};\n+\n+enum cmd_type {\n+\tTYPE_READ, /* read value from device */\n+\tTYPE_WRITE, /* write value to device */\n+};\n+\n+enum msg_type {\n+\tMSG_PUSH, /* async push from device */\n+\tMSG_REQ, /* sync request to device */\n+\tMSG_RESP, /* sync response from device */\n+};\n+\n+struct telemetry_data {\n+\tu8\tcmd;\n+\tu8\tcmd_type;\n+\tu8\tstatus;\n+\t__le64\tval; /* signed */\n+} __packed;\n+\n+struct telemetry_header {\n+\t__le16\tmagic;\n+\t__le16\tver;\n+\t__le32\tseq_num;\n+\tu8\ttype;\n+\tu8\tid;\n+\t__le16\tlen;\n+} __packed;\n+\n+struct telemetry_msg { /* little endian encoded */\n+\tstruct telemetry_header hdr;\n+\tstruct telemetry_data data;\n+} __packed;\n+\n+struct wrapper_msg {\n+\tstruct kref ref_count;\n+\tstruct telemetry_msg msg;\n+};\n+\n+struct xfer_queue_elem {\n+\t/*\n+\t * Node in list of ongoing transfer request on telemetry channel.\n+\t * Maintained by root device struct\n+\t */\n+\tstruct list_head list;\n+\t/* Sequence number of this transfer request */\n+\tu32 seq_num;\n+\t/* This is used to wait on until completion of transfer request */\n+\tstruct completion xfer_done;\n+\t/* Received data from device */\n+\tvoid *buf;\n+};\n+\n+struct resp_work {\n+\t/* Work struct to schedule work coming on QAIC_TELEMETRY channel */\n+\tstruct work_struct work;\n+\t/* Root struct of device, used to access device resources */\n+\tstruct qaic_device *qdev;\n+\t/* Buffer used by MHI for transfer requests */\n+\tvoid *buf;\n+};\n+\n+static void free_wrapper(struct kref *ref)\n+{\n+\tstruct wrapper_msg *wrapper = container_of(ref, struct wrapper_msg, ref_count);\n+\n+\tkfree(wrapper);\n+}\n+\n+int telemetry_request(struct qaic_device *qdev, u8 cmd, u8 cmd_type, s64 *val)\n+{\n+\tstruct wrapper_msg *wrapper;\n+\tstruct xfer_queue_elem elem;\n+\tstruct telemetry_msg *resp;\n+\tstruct telemetry_msg *req;\n+\tlong ret = 0;\n+\n+\twrapper = kzalloc_obj(*wrapper);\n+\tif (!wrapper)\n+\t\treturn -ENOMEM;\n+\n+\tkref_init(\u0026wrapper-\u003eref_count);\n+\treq = \u0026wrapper-\u003emsg;\n+\n+\tret = mutex_lock_interruptible(\u0026qdev-\u003etele_mutex);\n+\tif (ret)\n+\t\tgoto free_req;\n+\n+\treq-\u003ehdr.magic = cpu_to_le16(MAGIC);\n+\treq-\u003ehdr.ver = cpu_to_le16(VERSION);\n+\treq-\u003ehdr.seq_num = cpu_to_le32(qdev-\u003etele_next_seq_num++);\n+\treq-\u003ehdr.type = MSG_REQ;\n+\treq-\u003ehdr.id = 0;\n+\treq-\u003ehdr.len = cpu_to_le16(sizeof(req-\u003edata));\n+\n+\treq-\u003edata.cmd = cmd;\n+\treq-\u003edata.cmd_type = cmd_type;\n+\treq-\u003edata.status = 0;\n+\tif (cmd_type == TYPE_READ)\n+\t\treq-\u003edata.val = cpu_to_le64(0);\n+\telse\n+\t\treq-\u003edata.val = cpu_to_le64(*val);\n+\n+\telem.seq_num = qdev-\u003etele_next_seq_num - 1;\n+\telem.buf = NULL;\n+\tinit_completion(\u0026elem.xfer_done);\n+\tif (likely(!qdev-\u003etele_lost_buf)) {\n+\t\tresp = kzalloc_obj(*resp);\n+\t\tif (!resp) {\n+\t\t\tmutex_unlock(\u0026qdev-\u003etele_mutex);\n+\t\t\tret = -ENOMEM;\n+\t\t\tgoto free_req;\n+\t\t}\n+\n+\t\tret = mhi_queue_buf(qdev-\u003etele_ch, DMA_FROM_DEVICE, resp, sizeof(*resp), MHI_EOT);\n+\t\tif (ret) {\n+\t\t\tmutex_unlock(\u0026qdev-\u003etele_mutex);\n+\t\t\tgoto free_resp;\n+\t\t}\n+\t} else {\n+\t\t/*\n+\t\t * we lost a buffer because we queued a recv buf, but then\n+\t\t * queuing the corresponding tx buf failed. To try to avoid\n+\t\t * a memory leak, lets reclaim it and use it for this\n+\t\t * transaction.\n+\t\t */\n+\t\tqdev-\u003etele_lost_buf = false;\n+\t}\n+\n+\tkref_get(\u0026wrapper-\u003eref_count);\n+\tret = mhi_queue_buf(qdev-\u003etele_ch, DMA_TO_DEVICE, req, sizeof(*req), MHI_EOT);\n+\tif (ret) {\n+\t\tqdev-\u003etele_lost_buf = true;\n+\t\tkref_put(\u0026wrapper-\u003eref_count, free_wrapper);\n+\t\tmutex_unlock(\u0026qdev-\u003etele_mutex);\n+\t\tgoto free_req;\n+\t}\n+\n+\tlist_add_tail(\u0026elem.list, \u0026qdev-\u003etele_xfer_list);\n+\tmutex_unlock(\u0026qdev-\u003etele_mutex);\n+\n+\tret = wait_for_completion_interruptible_timeout(\u0026elem.xfer_done, HZ);\n+\t/*\n+\t * not using _interruptable because we have to cleanup or we'll\n+\t * likely cause memory corruption\n+\t */\n+\tmutex_lock(\u0026qdev-\u003etele_mutex);\n+\tif (!list_empty(\u0026elem.list))\n+\t\tlist_del(\u0026elem.list);\n+\tif (!ret \u0026\u0026 !elem.buf)\n+\t\tret = -ETIMEDOUT;\n+\telse if (ret \u003e 0 \u0026\u0026 !elem.buf)\n+\t\tret = -EIO;\n+\tmutex_unlock(\u0026qdev-\u003etele_mutex);\n+\n+\tresp = elem.buf;\n+\n+\tif (ret \u003c 0)\n+\t\tgoto free_resp;\n+\n+\tif (le16_to_cpu(resp-\u003ehdr.magic) != MAGIC || le16_to_cpu(resp-\u003ehdr.ver) != VERSION ||\n+\t resp-\u003ehdr.type != MSG_RESP || resp-\u003ehdr.id != 0 ||\n+\t le16_to_cpu(resp-\u003ehdr.len) != sizeof(resp-\u003edata) || resp-\u003edata.cmd != cmd ||\n+\t resp-\u003edata.cmd_type != cmd_type) {\n+\t\tret = -EINVAL;\n+\t\tgoto free_resp;\n+\t}\n+\n+\tif (resp-\u003edata.status) {\n+\t\tswitch ((int8_t) resp-\u003edata.status) {\n+\t\tcase ERR_READ_ONLY:\n+\t\t\tret = -EROFS;\n+\t\t\tbreak;\n+\t\tcase ERR_NOT_SUPPORTED:\n+\t\t\tret = -EOPNOTSUPP;\n+\t\t\tbreak;\n+\t\tcase ERR_WHILE_PROCESSING:\n+\t\t\tret = -EREMOTEIO;\n+\t\t\tbreak;\n+\t\tcase ERR_INVALID_VALUE:\n+\t\t\tret = -EINVAL;\n+\t\t\tbreak;\n+\t\tcase ERR_INVALID_HEADER:\n+\t\t\tret = -EBADR;\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tret = -EPROTO;\n+\t\t}\n+\t\tgoto free_resp;\n+\t}\n+\n+\tif (cmd_type == TYPE_READ)\n+\t\t*val = le64_to_cpu(resp-\u003edata.val);\n+\n+\tret = 0;\n+\n+free_resp:\n+\tkfree(resp);\n+free_req:\n+\tkref_put(\u0026wrapper-\u003eref_count, free_wrapper);\n+\n+\treturn ret;\n+}\n+\n+static int qaic_telemetry_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id)\n+{\n+\tstruct qaic_device *qdev = pci_get_drvdata(to_pci_dev(mhi_dev-\u003emhi_cntrl-\u003ecntrl_dev));\n+\tint ret;\n+\n+\tret = mhi_prepare_for_transfer(mhi_dev);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tdev_set_drvdata(\u0026mhi_dev-\u003edev, qdev);\n+\tqdev-\u003etele_ch = mhi_dev;\n+\tqdev-\u003etele_lost_buf = false;\n+\n+\treturn 0;\n+}\n+\n+static void qaic_telemetry_mhi_remove(struct mhi_device *mhi_dev)\n+{\n+\tstruct qaic_device *qdev = dev_get_drvdata(\u0026mhi_dev-\u003edev);\n+\n+\tmhi_unprepare_from_transfer(qdev-\u003etele_ch);\n+\tqdev-\u003etele_ch = NULL;\n+}\n+\n+static void resp_worker(struct work_struct *work)\n+{\n+\tstruct resp_work *resp = container_of(work, struct resp_work, work);\n+\tstruct qaic_device *qdev = resp-\u003eqdev;\n+\tstruct telemetry_msg *msg = resp-\u003ebuf;\n+\tstruct xfer_queue_elem *elem;\n+\tstruct xfer_queue_elem *i;\n+\tbool found = false;\n+\n+\tif (le16_to_cpu(msg-\u003ehdr.magic) != MAGIC) {\n+\t\tkfree(msg);\n+\t\tkfree(resp);\n+\t\treturn;\n+\t}\n+\n+\tmutex_lock(\u0026qdev-\u003etele_mutex);\n+\tlist_for_each_entry_safe(elem, i, \u0026qdev-\u003etele_xfer_list, list) {\n+\t\tif (elem-\u003eseq_num == le32_to_cpu(msg-\u003ehdr.seq_num)) {\n+\t\t\tfound = true;\n+\t\t\tlist_del_init(\u0026elem-\u003elist);\n+\t\t\telem-\u003ebuf = msg;\n+\t\t\tcomplete_all(\u0026elem-\u003exfer_done);\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\tmutex_unlock(\u0026qdev-\u003etele_mutex);\n+\n+\tif (!found)\n+\t\t/* request must have timed out, drop packet */\n+\t\tkfree(msg);\n+\n+\tkfree(resp);\n+}\n+\n+static void qaic_telemetry_mhi_ul_xfer_cb(struct mhi_device *mhi_dev,\n+\t\t\t\t\t struct mhi_result *mhi_result)\n+{\n+\tstruct telemetry_msg *msg = mhi_result-\u003ebuf_addr;\n+\tstruct wrapper_msg *wrapper;\n+\n+\twrapper = container_of(msg, struct wrapper_msg, msg);\n+\tkref_put(\u0026wrapper-\u003eref_count, free_wrapper);\n+}\n+\n+static void qaic_telemetry_mhi_dl_xfer_cb(struct mhi_device *mhi_dev,\n+\t\t\t\t\t struct mhi_result *mhi_result)\n+{\n+\tstruct qaic_device *qdev = dev_get_drvdata(\u0026mhi_dev-\u003edev);\n+\tstruct telemetry_msg *msg = mhi_result-\u003ebuf_addr;\n+\tstruct resp_work *resp;\n+\n+\tif (mhi_result-\u003etransaction_status) {\n+\t\tkfree(msg);\n+\t\treturn;\n+\t}\n+\n+\tresp = kmalloc(sizeof(*resp), GFP_ATOMIC);\n+\tif (!resp) {\n+\t\tpci_err(qdev-\u003epdev, \"dl_xfer_cb alloc fail, dropping message\\n\");\n+\t\tkfree(msg);\n+\t\treturn;\n+\t}\n+\n+\tINIT_WORK(\u0026resp-\u003ework, resp_worker);\n+\tresp-\u003eqdev = qdev;\n+\tresp-\u003ebuf = msg;\n+\tqueue_work(qdev-\u003etele_wq, \u0026resp-\u003ework);\n+}\n+\n+static const struct mhi_device_id qaic_telemetry_mhi_match_table[] = {\n+\t{ .chan = \"QAIC_TELEMETRY\", },\n+\t{},\n+};\n+\n+static struct mhi_driver qaic_telemetry_mhi_driver = {\n+\t.id_table = qaic_telemetry_mhi_match_table,\n+\t.remove = qaic_telemetry_mhi_remove,\n+\t.probe = qaic_telemetry_mhi_probe,\n+\t.ul_xfer_cb = qaic_telemetry_mhi_ul_xfer_cb,\n+\t.dl_xfer_cb = qaic_telemetry_mhi_dl_xfer_cb,\n+\t.driver = {\n+\t\t.name = \"qaic_telemetry\",\n+\t},\n+};\n+\n+int qaic_telemetry_register(void)\n+{\n+\treturn mhi_driver_register(\u0026qaic_telemetry_mhi_driver);\n+}\n+\n+void qaic_telemetry_unregister(void)\n+{\n+\tmhi_driver_unregister(\u0026qaic_telemetry_mhi_driver);\n+}\n+\n+void wake_all_telemetry(struct qaic_device *qdev)\n+{\n+\tstruct xfer_queue_elem *elem;\n+\tstruct xfer_queue_elem *i;\n+\n+\tmutex_lock(\u0026qdev-\u003etele_mutex);\n+\tlist_for_each_entry_safe(elem, i, \u0026qdev-\u003etele_xfer_list, list) {\n+\t\tlist_del_init(\u0026elem-\u003elist);\n+\t\tcomplete_all(\u0026elem-\u003exfer_done);\n+\t}\n+\tqdev-\u003etele_lost_buf = false;\n+\tmutex_unlock(\u0026qdev-\u003etele_mutex);\n+}\n+\n+static int qaic_telemetry_pre(struct drm_ras_node *ep)\n+{\n+\tstruct qaic_device *qdev = ep-\u003epriv;\n+\tstruct inode *dev_inode;\n+\tstruct path path;\n+\tchar devpath[24];\n+\tint ret;\n+\n+\tsnprintf(devpath, sizeof(devpath), \"/dev/accel/%s\", to_accel_kdev(qdev-\u003eqddev)-\u003ekobj.name);\n+\tret = kern_path(devpath, LOOKUP_FOLLOW, \u0026path);\n+\tif (ret)\n+\t\treturn -EINVAL;\n+\n+\tdev_inode = d_inode(path.dentry);\n+\tret = inode_permission(\u0026nop_mnt_idmap, dev_inode, MAY_READ | MAY_WRITE);\n+\tpath_put(\u0026path);\n+\n+\treturn ret;\n+}\n+\n+static int qaic_telemetry_read(struct drm_ras_node *ep, u32 *field, u64 *value, s32 *status)\n+{\n+\tstruct qaic_device *qdev = ep-\u003epriv;\n+\n+\t*status = (s32)telemetry_request(qdev, *field, TYPE_READ, value);\n+\n+\treturn *status;\n+}\n+\n+static int qaic_telemetry_write(struct drm_ras_node *ep, u32 *field, u64 *value, s32 *status)\n+{\n+\tstruct qaic_device *qdev = ep-\u003epriv;\n+\n+\t*status = (s32)telemetry_request(qdev, *field, TYPE_WRITE, value);\n+\n+\treturn *status;\n+}\n+\n+int qaic_drm_ras_register_node(struct qaic_device *qdev)\n+{\n+\tstruct drm_ras_node *node;\n+\tstruct pci_dev *pdev = qdev-\u003epdev;\n+\tconst char *device_name, *node_name;\n+\tint ret;\n+\n+\tnode = kzalloc_obj(struct drm_ras_node);\n+\tif (!node)\n+\t\treturn -ENOMEM;\n+\n+\tdevice_name = kasprintf(GFP_KERNEL, \"%04x:%02x:%02x.%d\",\n+\t\t\t\tpci_domain_nr(pdev-\u003ebus), pdev-\u003ebus-\u003enumber,\n+\t\t\t\tPCI_SLOT(pdev-\u003edevfn), PCI_FUNC(pdev-\u003edevfn));\n+\tif (!device_name) {\n+\t\tkfree(node);\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\tnode_name = kasprintf(GFP_KERNEL, \"qaic_telemetry\");\n+\tif (!node_name) {\n+\t\tkfree(device_name);\n+\t\tkfree(node);\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\tnode-\u003edevice_name = device_name;\n+\tnode-\u003enode_name = node_name;\n+\tnode-\u003etype = DRM_RAS_NODE_TYPE_TELEMETRY;\n+\tnode-\u003epriv = qdev;\n+\tnode-\u003etelemetry_read = qaic_telemetry_read;\n+\tnode-\u003etelemetry_write = qaic_telemetry_write;\n+\tnode-\u003etelemetry_pre = qaic_telemetry_pre;\n+\n+\tret = drm_ras_node_register(node);\n+\tif (ret) {\n+\t\tkfree(device_name);\n+\t\tkfree(node_name);\n+\t\tkfree(qdev-\u003eras_node);\n+\t\treturn ret;\n+\t}\n+\n+\tqdev-\u003eras_node = node;\n+\treturn 0;\n+}\n+\n+void qaic_drm_ras_unregister_node(struct qaic_device *qdev)\n+{\n+\tdrm_ras_node_unregister(qdev-\u003eras_node);\n+\tkfree(qdev-\u003eras_node-\u003enode_name);\n+\tkfree(qdev-\u003eras_node-\u003edevice_name);\n+\tkfree(qdev-\u003eras_node);\n+}\ndiff --git a/drivers/accel/qaic/qaic_telemetry.h b/drivers/accel/qaic/qaic_telemetry.h\nnew file mode 100644\nindex 0000000000000..a80922591e37c\n--- /dev/null\n+++ b/drivers/accel/qaic/qaic_telemetry.h\n@@ -0,0 +1,18 @@\n+/* SPDX-License-Identifier: GPL-2.0-only */\n+\n+/* Copyright (c) 2020, The Linux Foundation. All rights reserved. */\n+\n+#ifndef __QAIC_TELEMETRY_H__\n+#define __QAIC_TELEMETRY_H__\n+\n+#include \"qaic.h\"\n+\n+int telemetry_request(struct qaic_device *qdev, u8 cmd, u8 cmd_type, s64 *val);\n+int qaic_drm_ras_register_node(struct qaic_device *qdev);\n+void qaic_drm_ras_unregister_node(struct qaic_device *qdev);\n+\n+int qaic_telemetry_register(void);\n+void qaic_telemetry_unregister(void);\n+void wake_all_telemetry(struct qaic_device *qdev);\n+\n+#endif /* __QAIC_TELEMETRY_H__ */\ndiff --git a/drivers/gpu/drm/drm_ras.c b/drivers/gpu/drm/drm_ras.c\nindex b099eb67836e7..b88293295cda9 100644\n--- a/drivers/gpu/drm/drm_ras.c\n+++ b/drivers/gpu/drm/drm_ras.c\n@@ -53,6 +53,15 @@\n * 7. SET_ERROR_THRESHOLD: Set error threshold of a given counter.\n * Userspace must provide Node ID, Error ID and threshold to be set.\n *\n+ * 8. TELEMETRY_READ: Read a telemetry value.\n+ * Userspace must provide Node ID and telemetry field to be read.\n+ * Returns the status of the read and the value read (if valid).\n+ *\n+ * 9. TELEMETRY_WRITE: Write a telemetry value.\n+ * Userspace must provide Node ID, the telemetry field to write, and the value\n+ * to be written.\n+ * Returns the status of the write.\n+ *\n * Node registration:\n *\n * - drm_ras_node_register(): Registers a new node and assigns\n@@ -63,7 +72,6 @@\n * Node type:\n *\n * - ERROR_COUNTER:\n- * + Currently, only error counters are supported.\n * + The driver must implement the query_error_counter() callback to provide\n * the name and the value of the error counter.\n * + The driver must provide a error_counter_range.last value informing the\n@@ -81,6 +89,12 @@\n * that are raised by the hardware.\n * + The driver is responsible for error threshold bounds checking.\n *\n+ * - TELEMETRY:\n+ * + The driver must implement the telemetry_read() and telemetry_write()\n+ * callbacks.\n+ * + The driver can optionally provide a pre_doit callback, telemetry_pre(),\n+ * to enforce permissions checks on telemetry operations.\n+ *\n * Netlink handlers:\n *\n * - drm_ras_nl_list_nodes_dumpit(): Implements the LIST_NODES\n@@ -95,6 +109,12 @@\n * operation, fetching the error threshold of a specific counter.\n * - drm_ras_nl_set_error_threshold_doit(): Implements the SET_ERROR_THRESHOLD doit\n * operation, setting the error threshold of a specific counter.\n+ * - drm_ras_nl_pre_telemetry(): Implements the TELEMETRY_READ and TELEMETRY_WRITE\n+ * pre_doit operation, returning 0 if valid.\n+ * - drm_ras_nl_telemetry_read_doit(): Implements the TELEMETRY_READ doit operation,\n+ * invoking the driver's telemetry_read callback and returning the results.\n+ * - drm_ras_nl_telemetry_write_doit(): Implements the TELEMETRY_WRITE doit operation,\n+ * invoking the driver's telemetry_write callback and returning the results.\n */\n \n static DEFINE_XARRAY_ALLOC(drm_ras_xa);\n@@ -617,6 +637,156 @@ int drm_ras_nl_set_error_threshold_doit(struct sk_buff *skb, struct genl_info *i\n \treturn set_node_error_threshold(node_id, error_id, threshold);\n }\n \n+/**\n+ * drm_ras_nl_pre_telemetry - Enforce permission checks\n+ * @ops: Generic netlink operations\n+ * @skb: Netlink message buffer\n+ * @info: Generic Netlink info containing attributes of the request\n+ *\n+ * Enforces a telemetry node's pre_doit conditions (if they exist).\n+ *\n+ * Returns 0 if no pre_doit is specified, otherwise returns the pre_doit.\n+ */\n+int drm_ras_nl_pre_telemetry(const struct genl_split_ops *ops, struct sk_buff *skb,\n+\t\t\t struct genl_info *info)\n+{\n+\tstruct drm_ras_node *node;\n+\tu32 node_id;\n+\n+\tnode_id = nla_get_u32(info-\u003eattrs[DRM_RAS_A_TELEMETRY_ATTRS_NODE_ID]);\n+\tnode = xa_load(\u0026drm_ras_xa, node_id);\n+\n+\tif (node-\u003etelemetry_pre)\n+\t\treturn node-\u003etelemetry_pre(node);\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * drm_ras_nl_telemetry_read_doit() - Query a telemetry value\n+ * @skb: Netlink message buffer\n+ * @info: Generic Netlink info containing attributes of the request\n+ *\n+ * Extracts the telemetry field from the netlink attributes and\n+ * reads that value from the device.\n+ *\n+ * Return: 0 on success, or negative errno on failure.\n+ */\n+int drm_ras_nl_telemetry_read_doit(struct sk_buff *skb, struct genl_info *info)\n+{\n+\tstruct drm_ras_node *node;\n+\tstruct sk_buff *msg;\n+\tstruct nlattr *hdr;\n+\tu32 node_id, field;\n+\ts32 status;\n+\tu64 value;\n+\tint ret;\n+\n+\tif (!info-\u003eattrs || !info-\u003eattrs[DRM_RAS_A_TELEMETRY_ATTRS_FIELD])\n+\t\treturn -EINVAL;\n+\n+\tfield = nla_get_u32(info-\u003eattrs[DRM_RAS_A_TELEMETRY_ATTRS_FIELD]);\n+\tnode_id = nla_get_u32(info-\u003eattrs[DRM_RAS_A_TELEMETRY_ATTRS_NODE_ID]);\n+\n+\tnode = xa_load(\u0026drm_ras_xa, node_id);\n+\tif (!node || !node-\u003etelemetry_read)\n+\t\treturn -ENOENT;\n+\n+\tmsg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);\n+\tif (!msg)\n+\t\treturn -ENOMEM;\n+\n+\thdr = genlmsg_iput(msg, info);\n+\tif (!hdr) {\n+\t\tnlmsg_free(msg);\n+\t\treturn -EMSGSIZE;\n+\t}\n+\n+\tret = node-\u003etelemetry_read(node, \u0026field, \u0026value, \u0026status);\n+\tif (ret) {\n+\t\tnlmsg_free(msg);\n+\t\treturn ret;\n+\t}\n+\n+\tret = nla_put_s32(msg, DRM_RAS_A_TELEMETRY_ATTRS_STATUS, status);\n+\tif (ret) {\n+\t\tgenlmsg_cancel(msg, hdr);\n+\t\tnlmsg_free(msg);\n+\t\treturn ret;\n+\t}\n+\n+\tret = nla_put_64bit(msg, DRM_RAS_A_TELEMETRY_ATTRS_VALUE, sizeof(value),\n+\t\t\t \u0026value, DRM_RAS_A_TELEMETRY_ATTRS_PAD);\n+\tif (ret) {\n+\t\tgenlmsg_cancel(msg, hdr);\n+\t\tnlmsg_free(msg);\n+\t\treturn ret;\n+\t}\n+\n+\tgenlmsg_end(msg, hdr);\n+\n+\treturn genlmsg_reply(msg, info);\n+}\n+\n+/**\n+ * drm_ras_nl_telemetry_write_doit() - Write a telemetry value\n+ * @skb: Netlink message buffer\n+ * @info: Generic Netlink info containing attributes of the request\n+ *\n+ * Extracts the telemetry field and value to be written from the netlink\n+ * attributes and writes that value to the specified field on the device.\n+ *\n+ * Return: 0 on success, or negative errno on failure.\n+ */\n+int drm_ras_nl_telemetry_write_doit(struct sk_buff *skb, struct genl_info *info)\n+{\n+\tstruct drm_ras_node *node;\n+\tstruct sk_buff *msg;\n+\tstruct nlattr *hdr;\n+\tu32 node_id, field;\n+\ts32 status;\n+\tu64 value;\n+\tint ret;\n+\n+\tif (!info-\u003eattrs || !info-\u003eattrs[DRM_RAS_A_TELEMETRY_ATTRS_FIELD])\n+\t\treturn -EINVAL;\n+\n+\tvalue = nla_get_u64(info-\u003eattrs[DRM_RAS_A_TELEMETRY_ATTRS_VALUE]);\n+\tfield = nla_get_u32(info-\u003eattrs[DRM_RAS_A_TELEMETRY_ATTRS_FIELD]);\n+\tnode_id = nla_get_u32(info-\u003eattrs[DRM_RAS_A_TELEMETRY_ATTRS_NODE_ID]);\n+\n+\tnode = xa_load(\u0026drm_ras_xa, node_id);\n+\tif (!node || !node-\u003etelemetry_read)\n+\t\treturn -ENOENT;\n+\n+\tmsg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);\n+\tif (!msg)\n+\t\treturn -ENOMEM;\n+\n+\thdr = genlmsg_iput(msg, info);\n+\tif (!hdr) {\n+\t\tnlmsg_free(msg);\n+\t\treturn -EMSGSIZE;\n+\t}\n+\n+\tret = node-\u003etelemetry_write(node, \u0026field, \u0026value, \u0026status);\n+\tif (ret) {\n+\t\tnlmsg_free(msg);\n+\t\treturn ret;\n+\t}\n+\n+\tret = nla_put_s32(msg, DRM_RAS_A_TELEMETRY_ATTRS_STATUS, status);\n+\tif (ret) {\n+\t\tgenlmsg_cancel(msg, hdr);\n+\t\tnlmsg_free(msg);\n+\t\treturn ret;\n+\t}\n+\n+\tgenlmsg_end(msg, hdr);\n+\n+\treturn genlmsg_reply(msg, info);\n+}\n+\n /**\n * drm_ras_node_register() - Register a new RAS node\n * @node: Node structure to register\n@@ -631,14 +801,21 @@ int drm_ras_node_register(struct drm_ras_node *node)\n \tif (!node-\u003edevice_name || !node-\u003enode_name)\n \t\treturn -EINVAL;\n \n-\t/* Currently, only Error Counter Endpoints are supported */\n-\tif (node-\u003etype != DRM_RAS_NODE_TYPE_ERROR_COUNTER)\n-\t\treturn -EINVAL;\n-\n-\t/* Mandatory entries for Error Counter Node */\n-\tif (node-\u003etype == DRM_RAS_NODE_TYPE_ERROR_COUNTER \u0026\u0026\n-\t (!node-\u003eerror_counter_range.last || !node-\u003equery_error_counter))\n+\t/* Currently, only Error Counter and Telemetry endpoints are supported */\n+\tswitch (node-\u003etype) {\n+\tcase DRM_RAS_NODE_TYPE_ERROR_COUNTER:\n+\t\t/* Mandatory entries for Error Counter Node */\n+\t\tif (!node-\u003eerror_counter_range.last || !node-\u003equery_error_counter)\n+\t\t\treturn -EINVAL;\n+\t\tbreak;\n+\tcase DRM_RAS_NODE_TYPE_TELEMETRY:\n+\t\t/* Mandatory entries for Telemetry Node */\n+\t\tif (!node-\u003etelemetry_read || !node-\u003etelemetry_write)\n+\t\t\treturn -EINVAL;\n+\t\tbreak;\n+\tdefault:\n \t\treturn -EINVAL;\n+\t}\n \n \treturn xa_alloc(\u0026drm_ras_xa, \u0026node-\u003eid, node, xa_limit_32b, GFP_KERNEL);\n }\ndiff --git a/drivers/gpu/drm/drm_ras_nl.c b/drivers/gpu/drm/drm_ras_nl.c\nindex c9f5e0ceb3b5f..4577bc1a333e5 100644\n--- a/drivers/gpu/drm/drm_ras_nl.c\n+++ b/drivers/gpu/drm/drm_ras_nl.c\n@@ -41,12 +41,25 @@ static const struct nla_policy drm_ras_set_error_threshold_nl_policy[DRM_RAS_A_E\n \t[DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_THRESHOLD] = { .type = NLA_U32, },\n };\n \n+/* DRM_RAS_CMD_TELEMETRY_READ - do */\n+static const struct nla_policy drm_ras_telemetry_read_nl_policy[DRM_RAS_A_TELEMETRY_ATTRS_FIELD + 1] = {\n+\t[DRM_RAS_A_TELEMETRY_ATTRS_NODE_ID] = { .type = NLA_U32, },\n+\t[DRM_RAS_A_TELEMETRY_ATTRS_FIELD] = { .type = NLA_U32, },\n+};\n+\n+/* DRM_RAS_CMD_TELEMETRY_WRITE - do */\n+static const struct nla_policy drm_ras_telemetry_write_nl_policy[DRM_RAS_A_TELEMETRY_ATTRS_VALUE + 1] = {\n+\t[DRM_RAS_A_TELEMETRY_ATTRS_NODE_ID] = { .type = NLA_U32, },\n+\t[DRM_RAS_A_TELEMETRY_ATTRS_FIELD] = { .type = NLA_U32, },\n+\t[DRM_RAS_A_TELEMETRY_ATTRS_VALUE] = { .type = NLA_U64, },\n+};\n+\n /* Ops table for drm_ras */\n static const struct genl_split_ops drm_ras_nl_ops[] = {\n \t{\n \t\t.cmd\t= DRM_RAS_CMD_LIST_NODES,\n \t\t.dumpit\t= drm_ras_nl_list_nodes_dumpit,\n-\t\t.flags\t= GENL_ADMIN_PERM | GENL_CMD_CAP_DUMP,\n+\t\t.flags\t= GENL_CMD_CAP_DUMP,\n \t},\n \t{\n \t\t.cmd\t\t= DRM_RAS_CMD_GET_ERROR_COUNTER,\n@@ -83,6 +96,22 @@ static const struct genl_split_ops drm_ras_nl_ops[] = {\n \t\t.maxattr\t= DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_THRESHOLD,\n \t\t.flags\t\t= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,\n \t},\n+\t{\n+\t\t.cmd\t\t= DRM_RAS_CMD_TELEMETRY_READ,\n+\t\t.pre_doit\t= drm_ras_nl_pre_telemetry,\n+\t\t.doit\t\t= drm_ras_nl_telemetry_read_doit,\n+\t\t.policy\t\t= drm_ras_telemetry_read_nl_policy,\n+\t\t.maxattr\t= DRM_RAS_A_TELEMETRY_ATTRS_FIELD,\n+\t\t.flags\t\t= GENL_CMD_CAP_DO,\n+\t},\n+\t{\n+\t\t.cmd\t\t= DRM_RAS_CMD_TELEMETRY_WRITE,\n+\t\t.pre_doit\t= drm_ras_nl_pre_telemetry,\n+\t\t.doit\t\t= drm_ras_nl_telemetry_write_doit,\n+\t\t.policy\t\t= drm_ras_telemetry_write_nl_policy,\n+\t\t.maxattr\t= DRM_RAS_A_TELEMETRY_ATTRS_VALUE,\n+\t\t.flags\t\t= GENL_CMD_CAP_DO,\n+\t},\n };\n \n static const struct genl_multicast_group drm_ras_nl_mcgrps[] = {\ndiff --git a/drivers/gpu/drm/drm_ras_nl.h b/drivers/gpu/drm/drm_ras_nl.h\nindex 9aef097ea10f3..4bdd7ed268a1b 100644\n--- a/drivers/gpu/drm/drm_ras_nl.h\n+++ b/drivers/gpu/drm/drm_ras_nl.h\n@@ -12,6 +12,9 @@\n \n #include \u003cuapi/drm/drm_ras.h\u003e\n \n+int drm_ras_nl_pre_telemetry(const struct genl_split_ops *ops,\n+\t\t\t struct sk_buff *skb, struct genl_info *info);\n+\n int drm_ras_nl_list_nodes_dumpit(struct sk_buff *skb,\n \t\t\t\t struct netlink_callback *cb);\n int drm_ras_nl_get_error_counter_doit(struct sk_buff *skb,\n@@ -24,6 +27,9 @@ int drm_ras_nl_get_error_threshold_doit(struct sk_buff *skb,\n \t\t\t\t\tstruct genl_info *info);\n int drm_ras_nl_set_error_threshold_doit(struct sk_buff *skb,\n \t\t\t\t\tstruct genl_info *info);\n+int drm_ras_nl_telemetry_read_doit(struct sk_buff *skb, struct genl_info *info);\n+int drm_ras_nl_telemetry_write_doit(struct sk_buff *skb,\n+\t\t\t\t struct genl_info *info);\n \n enum {\n \tDRM_RAS_NLGRP_ERROR_REPORT,\ndiff --git a/include/drm/drm_ras.h b/include/drm/drm_ras.h\nindex 1765a3130b09b..2a2cdd44839de 100644\n--- a/include/drm/drm_ras.h\n+++ b/include/drm/drm_ras.h\n@@ -100,6 +100,45 @@ struct drm_ras_node {\n \t */\n \tint (*set_error_threshold)(struct drm_ras_node *node, u32 error_id, u32 threshold);\n \n+\t/**\n+\t * @telemetry_pre:\n+\t *\n+\t * This callback is used to enforce any pre_doit conditions necessary\n+\t * for telemetry. This is an optional callback for telemetry_node.\n+\t *\n+\t * Returns 0 on success, negative error code on failure.\n+\t */\n+\tint (*telemetry_pre)(struct drm_ras_node *ep);\n+\n+\t/**\n+\t * @telemetry_read:\n+\t *\n+\t * This callback is used for a drm-ras telemetry node to read a\n+\t * telemetry value. This is a mandatory callback for telemetry_node.\n+\t *\n+\t * The callback will read the desired device-specific field into value\n+\t * and update status corresponding to a device-specific error code.\n+\t *\n+\t * Returns 0 on success, negative error code on failure.\n+\t */\n+\tint (*telemetry_read)(struct drm_ras_node *ep, u32 *field, u64 *value,\n+\t\t\t s32 *status);\n+\n+\t/**\n+\t * @telemetry_write:\n+\t *\n+\t * This callback is used for a drm-ras telemetry node to write a\n+\t * telemetry value. This is a mandatory callback for telemetry_node.\n+\t *\n+\t * The callback will write the value to the desired device-specific\n+\t * field and update status corresponding to a device-specific error\n+\t * code.\n+\t *\n+\t * Returns 0 on success, negative error code on failure.\n+\t */\n+\tint (*telemetry_write)(struct drm_ras_node *ep, u32 *field, u64 *value,\n+\t\t\t s32 *status);\n+\n \t/** @priv: Driver private data */\n \tvoid *priv;\n };\ndiff --git a/include/uapi/drm/drm_ras.h b/include/uapi/drm/drm_ras.h\nindex 2f832275ee6ef..d39c78a9ed5a7 100644\n--- a/include/uapi/drm/drm_ras.h\n+++ b/include/uapi/drm/drm_ras.h\n@@ -16,6 +16,7 @@\n */\n enum drm_ras_node_type {\n \tDRM_RAS_NODE_TYPE_ERROR_COUNTER = 1,\n+\tDRM_RAS_NODE_TYPE_TELEMETRY,\n };\n \n enum {\n@@ -51,6 +52,17 @@ enum {\n \tDRM_RAS_A_ERROR_EVENT_ATTRS_MAX = (__DRM_RAS_A_ERROR_EVENT_ATTRS_MAX - 1)\n };\n \n+enum {\n+\tDRM_RAS_A_TELEMETRY_ATTRS_NODE_ID = 1,\n+\tDRM_RAS_A_TELEMETRY_ATTRS_FIELD,\n+\tDRM_RAS_A_TELEMETRY_ATTRS_VALUE,\n+\tDRM_RAS_A_TELEMETRY_ATTRS_STATUS,\n+\tDRM_RAS_A_TELEMETRY_ATTRS_PAD,\n+\n+\t__DRM_RAS_A_TELEMETRY_ATTRS_MAX,\n+\tDRM_RAS_A_TELEMETRY_ATTRS_MAX = (__DRM_RAS_A_TELEMETRY_ATTRS_MAX - 1)\n+};\n+\n enum {\n \tDRM_RAS_CMD_LIST_NODES = 1,\n \tDRM_RAS_CMD_GET_ERROR_COUNTER,\n@@ -58,6 +70,8 @@ enum {\n \tDRM_RAS_CMD_ERROR_EVENT,\n \tDRM_RAS_CMD_GET_ERROR_THRESHOLD,\n \tDRM_RAS_CMD_SET_ERROR_THRESHOLD,\n+\tDRM_RAS_CMD_TELEMETRY_READ,\n+\tDRM_RAS_CMD_TELEMETRY_WRITE,\n \n \t__DRM_RAS_CMD_MAX,\n \tDRM_RAS_CMD_MAX = (__DRM_RAS_CMD_MAX - 1)\ndiff --git a/include/uapi/drm/qaic_accel.h b/include/uapi/drm/qaic_accel.h\nindex c92d0309d583b..5aac051014267 100644\n--- a/include/uapi/drm/qaic_accel.h\n+++ b/include/uapi/drm/qaic_accel.h\n@@ -51,6 +51,34 @@ extern \"C\" {\n #define QAIC_TRANS_VALIDATE_PARTITION_FROM_DEV\t19\n #define QAIC_TRANS_VALIDATE_PARTITION_TO_DEV\t20\n \n+/* Telemetry fields */\n+#define QAIC_DEVINFO_THERMAL_SOC_MAX_TEMP\t1\n+#define QAIC_DEVINFO_THERMAL_BOARD_TEMP\t\t2\n+#define QAIC_DEVINFO_THERMAL_BOARD_MAX_TEMP\t3\n+#define QAIC_DEVINFO_THERMAL_DDR_TEMP\t\t4\n+#define QAIC_DEVINFO_THERMAL_WARNING_TEMP\t5\n+#define QAIC_DEVINFO_THERMAL_SHUTDOWN_TEMP\t6\n+#define QAIC_DEVINFO_BOARD_TDP\t\t\t7\n+#define QAIC_DEVINFO_BOARD_POWER\t\t8\n+#define QAIC_DEVINFO_POWER_STATE\t\t9\n+#define QAIC_DEVINFO_POWER_MAX\t\t\t10\n+#define QAIC_DEVINFO_THROTTLE_PERCENT\t\t11\n+#define QAIC_DEVINFO_THROTTLE_TIME\t\t12\n+#define QAIC_DEVINFO_UPTIME\t\t\t13\n+#define QAIC_DEVINFO_THERMAL_SOC_FLOOR_TEMP\t14\n+#define QAIC_DEVINFO_THERMAL_SOC_CEILING_TEMP\t15\n+#define QAIC_DEVINFO_VOLTAGE_CX\t\t\t16\n+#define QAIC_DEVINFO_VOLTAGE_MX\t\t\t17\n+#define QAIC_DEVINFO_VOLTAGE_NSP_CX\t\t18\n+#define QAIC_DEVINFO_VOLTAGE_NSP_MX\t\t19\n+#define QAIC_DEVINFO_CFG_TOPS\t\t\t20\n+#define QAIC_DEVINFO_THERMAL_PMIC_TEMP_A\t21\n+#define QAIC_DEVINFO_THERMAL_PMIC_TEMP_C\t22\n+#define QAIC_DEVINFO_THERMAL_PMIC_TEMP_E\t23\n+#define QAIC_DEVINFO_POWER_ACTION\t\t24\n+#define QAIC_DEVINFO_SOC_TDP\t\t\t25\n+#define QAIC_DEVINFO_SOC_POWER\t\t\t26\n+\n /**\n * struct qaic_manage_trans_hdr - Header for a transaction in a manage message.\n * @type: In. Identifies this transaction. See QAIC_TRANS_* defines.\n"
} |
| 2/1 | 2026/10/09 21:03 | agent | patch-evaluator |
7mModel:core Args: null Results: {
"EnableConfigs": [
"DRM_RAS",
"DRM",
"NET"
],
"FocusSymbols": [
"drm_ras_nl_pre_telemetry",
"drm_ras_nl_telemetry_read_doit",
"drm_ras_nl_telemetry_write_doit",
"drm_ras_node_register"
],
"Reasoning": "The patch expands the DRM RAS Generic Netlink subsystem with new telemetry operations (TELEMETRY_READ and TELEMETRY_WRITE) and removes GENL_ADMIN_PERM from node listing. The Generic Netlink family 'drm_ras' is registered during DRM core initialization and can be queried from unprivileged userspace over standard AF_NETLINK sockets without physical hardware. The newly added Netlink pre-handler drm_ras_nl_pre_telemetry() directly dereferences the looked-up node pointer without validating that it is non-NULL, introducing an easily triggered NULL pointer dereference when querying an invalid node ID.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.
IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.
================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.
- REACHABILITY IS THE PRIMARY GATE:
Fuzzing can only discover bugs in code that can actually execute in standard virtualized
environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
regardless of whether it adds assertions or complex logic.
- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
Patch authors routinely label changes as "cleanups", "refactorings", or state
"No functional change intended". Do NOT take these claims at face value.
Code refactorings that rearrange logic, introduce helper functions, or alter state management
in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
If reachable executable code is modified or refactored, it MUST be fuzzed.
- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
Even if the author believes the invariant always holds, fuzzing is essential to verify whether
an unusual sequence of operations can violate it.
================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:
- Non-kernel and non-executable changes:
* Modifications to Documentation/, comments, or spelling fixes.
* User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
that do not affect the compiled kernel image (vmlinux) or kernel modules.
* Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
that do not alter control flow or data structures.
* Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
* Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
* Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
physical PCI hot-unplug or manual sysfs driver unbinding.
* Code paths exclusive to architectures other than the target architecture.
================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
* Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
* Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
* Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
* Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).
================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:
- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
Do NOT list generic, ubiquitous functions called by almost every program in the corpus
(including, but not limited to: general memory allocators and deallocators, page fault
and trap handlers, or core synchronization primitives; this is not an exhaustive list).
Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
which severely dilutes fuzzing effort away from the actual changes.
- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
List functions that specifically implement the logic being added or altered, or direct API entrypoints
for the subsystem feature under review.
- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).
================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit fcad46b922e1e426dbe5607ebd256b48f44fe6e0
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Oct 9 21:03:30 2026 +0000
syz-cluster: applied patch under review
diff --git a/Documentation/netlink/specs/drm_ras.yaml b/Documentation/netlink/specs/drm_ras.yaml
index 4c2ac9a1ba3f5..d558eb1cba3f1 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:
@@ -112,7 +137,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:
@@ -199,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/accel/qaic/Makefile b/drivers/accel/qaic/Makefile
index 71f727b74da3b..b2bbe9b3e0774 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 02ca99000e2ae..f5dbeadba658e 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 c1bb6db78d129..d2aed51052a66 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 0000000000000..dbfb36cf319ce
--- /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 0000000000000..a80922591e37c
--- /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/drivers/gpu/drm/drm_ras.c b/drivers/gpu/drm/drm_ras.c
index b099eb67836e7..b88293295cda9 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 c9f5e0ceb3b5f..4577bc1a333e5 100644
--- a/drivers/gpu/drm/drm_ras_nl.c
+++ b/drivers/gpu/drm/drm_ras_nl.c
@@ -41,12 +41,25 @@ 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[] = {
{
.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,
@@ -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 9aef097ea10f3..4bdd7ed268a1b 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 1765a3130b09b..2a2cdd44839de 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 2f832275ee6ef..d39c78a9ed5a7 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)
diff --git a/include/uapi/drm/qaic_accel.h b/include/uapi/drm/qaic_accel.h
index c92d0309d583b..5aac051014267 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.
|
| 3/2 | 2026/10/09 21:03 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 16874 output: 29 thoughts: 463 Thoughts: **Analyzing the Patch: A Deep Dive** Okay, let's break down this patch and the associated review request. The user has helpfully provided the target architecture (amd64) and the diff itself, which is a great start. Now, let's dig into the details. First, I need to understand what's being modified. The file list is a good indicator of the scope. We've got changes spanning across two key subsystems: `drivers/accel/qaic` and `drivers/gpu/drm`. Immediately, I need to understand these are related. The `qaic` directory – okay, this is Qualcomm Cloud AI 100 driver, so a PCIe accelerator. The first question that pops up: is this hardware something easily accessible in a standard QEMU/syzkaller environment? That's going to influence my ability to test this thoroughly. I'll need to confirm that. I’ll need to do a quick check of `drivers/accel/qaic/Kconfig` and `qaic_drv.c` to confirm the functionality. But, back to `drivers/gpu/drm/drm_ras`... This is where it gets interesting. I need to refresh my memory on what DRM RAS is exactly. Is this a generic netlink interface? Let's check `drm_ras.c`. To understand the implications of this, I think I'll use a `grepper` or `read-file` to check drm_ras.c and drivers/gpu/drm/Makefile or Kconfig. The documentation file `Documentation/netlink/specs/drm_ras.yaml` will also be very useful here. |
| 4/2 | 2026/10/09 21:04 | tool | grepper |
0mArgs:{
"Expression": "drm_ras",
"PathPrefixes": [
"drivers/gpu/drm"
]
}Results: {
"Output": "\nFull output is too long, showing 200 out of 680 lines.\nUse more precise expression or PathPrefixes if possible.\n\nNumber of matching lines per file (16 files in total):\ndrivers/gpu/drm/Makefile:1\ndrivers/gpu/drm/drm_drv.c:3\ndrivers/gpu/drm/drm_ras.c:70\ndrivers/gpu/drm/drm_ras_genl_family.c:8\ndrivers/gpu/drm/drm_ras_nl.c:35\ndrivers/gpu/drm/drm_ras_nl.h:12\ndrivers/gpu/drm/xe/Makefile:1\ndrivers/gpu/drm/xe/xe_device.c:1\ndrivers/gpu/drm/xe/xe_device_types.h:4\ndrivers/gpu/drm/xe/xe_drm_ras.c:37\ndrivers/gpu/drm/xe/xe_drm_ras.h:2\ndrivers/gpu/drm/xe/xe_drm_ras_types.h:7\ndrivers/gpu/drm/xe/xe_hw_error.c:7\ndrivers/gpu/drm/xe/xe_pci.c:3\ndrivers/gpu/drm/xe/xe_pci_types.h:1\ndrivers/gpu/drm/xe/xe_ras.c:7\n\ndrivers/gpu/drm/Makefile=94=drm-$(CONFIG_DRM_DRAW) += drm_draw.o\ndrivers/gpu/drm/Makefile:95:drm-$(CONFIG_DRM_RAS) += drm_ras.o drm_ras_nl.o drm_ras_genl_family.o\ndrivers/gpu/drm/Makefile-96-obj-$(CONFIG_DRM)\t+= drm.o\n--\ndrivers/gpu/drm/drm_drv.c-55-#include \u003cdrm/drm_privacy_screen_machine.h\u003e\ndrivers/gpu/drm/drm_drv.c:56:#include \u003cdrm/drm_ras_genl_family.h\u003e\ndrivers/gpu/drm/drm_drv.c-57-\n--\ndrivers/gpu/drm/drm_drv.c=1254=static void drm_core_exit(void)\ndrivers/gpu/drm/drm_drv.c-1255-{\ndrivers/gpu/drm/drm_drv.c:1256:\tdrm_ras_genl_family_unregister();\ndrivers/gpu/drm/drm_drv.c-1257-\tdrm_privacy_screen_lookup_exit();\n--\ndrivers/gpu/drm/drm_drv.c=1266=static int __init drm_core_init(void)\n--\ndrivers/gpu/drm/drm_drv.c-1293-\ndrivers/gpu/drm/drm_drv.c:1294:\tret = drm_ras_genl_family_register();\ndrivers/gpu/drm/drm_drv.c-1295-\tif (ret \u003c 0)\n--\ndrivers/gpu/drm/drm_ras.c-11-\ndrivers/gpu/drm/drm_ras.c:12:#include \u003cdrm/drm_ras.h\u003e\ndrivers/gpu/drm/drm_ras.c-13-\ndrivers/gpu/drm/drm_ras.c:14:#include \"drm_ras_nl.h\"\ndrivers/gpu/drm/drm_ras.c-15-\n--\ndrivers/gpu/drm/drm_ras.c-24- *\ndrivers/gpu/drm/drm_ras.c:25: * The nodes are stored in a global xarray `drm_ras_xa` to allow\ndrivers/gpu/drm/drm_ras.c-26- * efficient lookup by ID. Nodes can be registered or unregistered\n--\ndrivers/gpu/drm/drm_ras.c-28- *\ndrivers/gpu/drm/drm_ras.c:29: * A Generic Netlink family `drm_ras` exposes the below operations to\ndrivers/gpu/drm/drm_ras.c-30- * userspace:\n--\ndrivers/gpu/drm/drm_ras.c-66- *\ndrivers/gpu/drm/drm_ras.c:67: * - drm_ras_node_register(): Registers a new node and assigns\ndrivers/gpu/drm/drm_ras.c-68- * it a unique ID in the xarray.\ndrivers/gpu/drm/drm_ras.c:69: * - drm_ras_node_unregister(): Removes a previously registered\ndrivers/gpu/drm/drm_ras.c-70- * node from the xarray.\n--\ndrivers/gpu/drm/drm_ras.c-99- *\ndrivers/gpu/drm/drm_ras.c:100: * - drm_ras_nl_list_nodes_dumpit(): Implements the LIST_NODES\ndrivers/gpu/drm/drm_ras.c-101- * operation, iterating over the xarray.\ndrivers/gpu/drm/drm_ras.c:102: * - drm_ras_nl_get_error_counter_dumpit(): Implements the GET_ERROR_COUNTER dumpit\ndrivers/gpu/drm/drm_ras.c-103- * operation, fetching all counters from a specific node.\ndrivers/gpu/drm/drm_ras.c:104: * - drm_ras_nl_get_error_counter_doit(): Implements the GET_ERROR_COUNTER doit\ndrivers/gpu/drm/drm_ras.c-105- * operation, fetching a counter value from a specific node.\ndrivers/gpu/drm/drm_ras.c:106: * - drm_ras_nl_clear_error_counter_doit(): Implements the CLEAR_ERROR_COUNTER doit\ndrivers/gpu/drm/drm_ras.c-107- * operation, clearing a counter value from a specific node.\ndrivers/gpu/drm/drm_ras.c:108: * - drm_ras_nl_get_error_threshold_doit(): Implements the GET_ERROR_THRESHOLD doit\ndrivers/gpu/drm/drm_ras.c-109- * operation, fetching the error threshold of a specific counter.\ndrivers/gpu/drm/drm_ras.c:110: * - drm_ras_nl_set_error_threshold_doit(): Implements the SET_ERROR_THRESHOLD doit\ndrivers/gpu/drm/drm_ras.c-111- * operation, setting the error threshold of a specific counter.\ndrivers/gpu/drm/drm_ras.c:112: * - drm_ras_nl_pre_telemetry(): Implements the TELEMETRY_READ and TELEMETRY_WRITE\ndrivers/gpu/drm/drm_ras.c-113- * pre_doit operation, returning 0 if valid.\ndrivers/gpu/drm/drm_ras.c:114: * - drm_ras_nl_telemetry_read_doit(): Implements the TELEMETRY_READ doit operation,\ndrivers/gpu/drm/drm_ras.c-115- * invoking the driver's telemetry_read callback and returning the results.\ndrivers/gpu/drm/drm_ras.c:116: * - drm_ras_nl_telemetry_write_doit(): Implements the TELEMETRY_WRITE doit operation,\ndrivers/gpu/drm/drm_ras.c-117- * invoking the driver's telemetry_write callback and returning the results.\n--\ndrivers/gpu/drm/drm_ras.c-119-\ndrivers/gpu/drm/drm_ras.c:120:static DEFINE_XARRAY_ALLOC(drm_ras_xa);\ndrivers/gpu/drm/drm_ras.c-121-\n--\ndrivers/gpu/drm/drm_ras.c-124- */\ndrivers/gpu/drm/drm_ras.c:125:struct drm_ras_ctx {\ndrivers/gpu/drm/drm_ras.c-126-\t/* Which xarray id to restart the dump from */\n--\ndrivers/gpu/drm/drm_ras.c-130-/**\ndrivers/gpu/drm/drm_ras.c:131: * drm_ras_nl_list_nodes_dumpit() - Dump all registered RAS nodes\ndrivers/gpu/drm/drm_ras.c-132- * @skb: Netlink message buffer\n--\ndrivers/gpu/drm/drm_ras.c-144- */\ndrivers/gpu/drm/drm_ras.c:145:int drm_ras_nl_list_nodes_dumpit(struct sk_buff *skb,\ndrivers/gpu/drm/drm_ras.c-146-\t\t\t\t struct netlink_callback *cb)\n--\ndrivers/gpu/drm/drm_ras.c-148-\tconst struct genl_info *info = genl_info_dump(cb);\ndrivers/gpu/drm/drm_ras.c:149:\tstruct drm_ras_ctx *ctx = (void *)cb-\u003ectx;\ndrivers/gpu/drm/drm_ras.c:150:\tstruct drm_ras_node *node;\ndrivers/gpu/drm/drm_ras.c-151-\tstruct nlattr *hdr;\n--\ndrivers/gpu/drm/drm_ras.c-154-\ndrivers/gpu/drm/drm_ras.c:155:\txa_for_each_start(\u0026drm_ras_xa, id, node, ctx-\u003erestart) {\ndrivers/gpu/drm/drm_ras.c-156-\t\thdr = genlmsg_iput(skb, info);\n--\ndrivers/gpu/drm/drm_ras.c=198=static int get_node_error_counter(u32 node_id, u32 error_id,\n--\ndrivers/gpu/drm/drm_ras.c-200-{\ndrivers/gpu/drm/drm_ras.c:201:\tstruct drm_ras_node *node;\ndrivers/gpu/drm/drm_ras.c-202-\ndrivers/gpu/drm/drm_ras.c:203:\tnode = xa_load(\u0026drm_ras_xa, node_id);\ndrivers/gpu/drm/drm_ras.c-204-\tif (!node || !node-\u003equery_error_counter)\n--\ndrivers/gpu/drm/drm_ras.c=214=static int get_node_error_threshold(u32 node_id, u32 error_id, const char **name, u32 *threshold)\ndrivers/gpu/drm/drm_ras.c-215-{\ndrivers/gpu/drm/drm_ras.c:216:\tstruct drm_ras_node *node;\ndrivers/gpu/drm/drm_ras.c-217-\ndrivers/gpu/drm/drm_ras.c:218:\tnode = xa_load(\u0026drm_ras_xa, node_id);\ndrivers/gpu/drm/drm_ras.c-219-\tif (!node)\n--\ndrivers/gpu/drm/drm_ras.c=231=static int set_node_error_threshold(u32 node_id, u32 error_id, u32 threshold)\ndrivers/gpu/drm/drm_ras.c-232-{\ndrivers/gpu/drm/drm_ras.c:233:\tstruct drm_ras_node *node;\ndrivers/gpu/drm/drm_ras.c-234-\ndrivers/gpu/drm/drm_ras.c:235:\tnode = xa_load(\u0026drm_ras_xa, node_id);\ndrivers/gpu/drm/drm_ras.c-236-\tif (!node)\n--\ndrivers/gpu/drm/drm_ras.c=248=static int msg_reply_value(struct sk_buff *msg, u32 error_id,\n--\ndrivers/gpu/drm/drm_ras.c-265-\ndrivers/gpu/drm/drm_ras.c:266:static int msg_put_error_event_attrs(struct sk_buff *msg, struct drm_ras_node *node,\ndrivers/gpu/drm/drm_ras.c-267-\t\t\t\t u32 error_id, const char *error_name, u32 value)\n--\ndrivers/gpu/drm/drm_ras.c=349=static int doit_reply_threshold(struct genl_info *info, u32 node_id, u32 error_id)\n--\ndrivers/gpu/drm/drm_ras.c-386-/**\ndrivers/gpu/drm/drm_ras.c:387: * drm_ras_nl_error_event() - Report an error event\ndrivers/gpu/drm/drm_ras.c-388- * @node: Node structure\n--\ndrivers/gpu/drm/drm_ras.c-400- */\ndrivers/gpu/drm/drm_ras.c:401:int drm_ras_nl_error_event(struct drm_ras_node *node, u32 error_id, const char *error_name,\ndrivers/gpu/drm/drm_ras.c-402-\t\t\t u32 value)\n--\ndrivers/gpu/drm/drm_ras.c-412-\t/* Check the node is currently registered */\ndrivers/gpu/drm/drm_ras.c:413:\tif (xa_load(\u0026drm_ras_xa, node-\u003eid) != node)\ndrivers/gpu/drm/drm_ras.c-414-\t\treturn -ENOENT;\n--\ndrivers/gpu/drm/drm_ras.c-424-\ndrivers/gpu/drm/drm_ras.c:425:\tgenl_info_init_ntf(\u0026info, \u0026drm_ras_nl_family, DRM_RAS_CMD_ERROR_EVENT);\ndrivers/gpu/drm/drm_ras.c-426-\n--\ndrivers/gpu/drm/drm_ras.c-441-\tgenlmsg_end(msg, hdr);\ndrivers/gpu/drm/drm_ras.c:442:\tgenlmsg_multicast_allns(\u0026drm_ras_nl_family, msg, 0, DRM_RAS_NLGRP_ERROR_REPORT);\ndrivers/gpu/drm/drm_ras.c-443-\n--\ndrivers/gpu/drm/drm_ras.c-451-}\ndrivers/gpu/drm/drm_ras.c:452:EXPORT_SYMBOL(drm_ras_nl_error_event);\ndrivers/gpu/drm/drm_ras.c-453-\ndrivers/gpu/drm/drm_ras.c-454-/**\ndrivers/gpu/drm/drm_ras.c:455: * drm_ras_nl_get_error_counter_dumpit() - Dump all Error Counters\ndrivers/gpu/drm/drm_ras.c-456- * @skb: Netlink message buffer\n--\ndrivers/gpu/drm/drm_ras.c-468- */\ndrivers/gpu/drm/drm_ras.c:469:int drm_ras_nl_get_error_counter_dumpit(struct sk_buff *skb,\ndrivers/gpu/drm/drm_ras.c-470-\t\t\t\t\tstruct netlink_callback *cb)\n--\ndrivers/gpu/drm/drm_ras.c-472-\tconst struct genl_info *info = genl_info_dump(cb);\ndrivers/gpu/drm/drm_ras.c:473:\tstruct drm_ras_ctx *ctx = (void *)cb-\u003ectx;\ndrivers/gpu/drm/drm_ras.c:474:\tstruct drm_ras_node *node;\ndrivers/gpu/drm/drm_ras.c-475-\tstruct nlattr *hdr;\n--\ndrivers/gpu/drm/drm_ras.c-484-\ndrivers/gpu/drm/drm_ras.c:485:\tnode = xa_load(\u0026drm_ras_xa, node_id);\ndrivers/gpu/drm/drm_ras.c-486-\tif (!node)\n--\ndrivers/gpu/drm/drm_ras.c-525-/**\ndrivers/gpu/drm/drm_ras.c:526: * drm_ras_nl_get_error_counter_doit() - Query an error counter of an node\ndrivers/gpu/drm/drm_ras.c-527- * @skb: Netlink message buffer\n--\ndrivers/gpu/drm/drm_ras.c-535- */\ndrivers/gpu/drm/drm_ras.c:536:int drm_ras_nl_get_error_counter_doit(struct sk_buff *skb,\ndrivers/gpu/drm/drm_ras.c-537-\t\t\t\t struct genl_info *info)\n--\ndrivers/gpu/drm/drm_ras.c-552-/**\ndrivers/gpu/drm/drm_ras.c:553: * drm_ras_nl_clear_error_counter_doit() - Clear an error counter of a node\ndrivers/gpu/drm/drm_ras.c-554- * @skb: Netlink message buffer\n--\ndrivers/gpu/drm/drm_ras.c-561- */\ndrivers/gpu/drm/drm_ras.c:562:int drm_ras_nl_clear_error_counter_doit(struct sk_buff *skb,\ndrivers/gpu/drm/drm_ras.c-563-\t\t\t\t\tstruct genl_info *info)\ndrivers/gpu/drm/drm_ras.c-564-{\ndrivers/gpu/drm/drm_ras.c:565:\tstruct drm_ras_node *node;\ndrivers/gpu/drm/drm_ras.c-566-\tu32 node_id, error_id;\n--\ndrivers/gpu/drm/drm_ras.c-575-\ndrivers/gpu/drm/drm_ras.c:576:\tnode = xa_load(\u0026drm_ras_xa, node_id);\ndrivers/gpu/drm/drm_ras.c-577-\tif (!node || !node-\u003eclear_error_counter)\n--\ndrivers/gpu/drm/drm_ras.c-587-/**\ndrivers/gpu/drm/drm_ras.c:588: * drm_ras_nl_get_error_threshold_doit() - Query error threshold of a counter\ndrivers/gpu/drm/drm_ras.c-589- * @skb: Netlink message buffer\n--\ndrivers/gpu/drm/drm_ras.c-597- */\ndrivers/gpu/drm/drm_ras.c:598:int drm_ras_nl_get_error_threshold_doit(struct sk_buff *skb, struct genl_info *info)\ndrivers/gpu/drm/drm_ras.c-599-{\n--\ndrivers/gpu/drm/drm_ras.c-613-/**\ndrivers/gpu/drm/drm_ras.c:614: * drm_ras_nl_set_error_threshold_doit() - Set error threshold of a counter\ndrivers/gpu/drm/drm_ras.c-615- * @skb: Netlink message buffer\n--\ndrivers/gpu/drm/drm_ras.c-622- */\ndrivers/gpu/drm/drm_ras.c:623:int drm_ras_nl_set_error_threshold_doit(struct sk_buff *skb, struct genl_info *info)\ndrivers/gpu/drm/drm_ras.c-624-{\n--\ndrivers/gpu/drm/drm_ras.c-640-/**\ndrivers/gpu/drm/drm_ras.c:641: * drm_ras_nl_pre_telemetry - Enforce permission checks\ndrivers/gpu/drm/drm_ras.c-642- * @ops: Generic netlink operations\n--\ndrivers/gpu/drm/drm_ras.c-649- */\ndrivers/gpu/drm/drm_ras.c:650:int drm_ras_nl_pre_telemetry(const struct genl_split_ops *ops, struct sk_buff *skb,\ndrivers/gpu/drm/drm_ras.c-651-\t\t\t struct genl_info *info)\n\n"
} |
| 5/2 | 2026/10/09 21:04 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 22629 output: 18 thoughts: 38 Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person: **Investigating the Configuration Option CONFIG_DRM_RAS** Alright, time to dive in. My immediate thought is, "Okay, `CONFIG_DRM_RAS`... I need to figure out precisely what that config option controls within the DRM (Direct Rendering Manager) subsystem, and where its definition lives in the kernel's build system." My next step is clearly to leverage the tools at my disposal. I'll need to use `grep` – that's the obvious choice – to search the `Kconfig` files. I'll be targeting the `DRM_RAS` symbol itself. That should quickly pinpoint where the option is declared and, crucially, what dependencies or context surrounds it. Hopefully, this will lead me to the relevant source code responsible for defining the functionality of this config. |
| 6/2 | 2026/10/09 21:04 | tool | grepper |
0mArgs:{
"Expression": "config DRM_RAS"
}Results: {
"Output": "drivers/gpu/drm/Kconfig=131=config DRM_PANIC_HELPER\n--\ndrivers/gpu/drm/Kconfig-136-\ndrivers/gpu/drm/Kconfig:137:config DRM_RAS\ndrivers/gpu/drm/Kconfig-138-\tbool \"DRM RAS support\"\n"
} |
| 7/2 | 2026/10/09 21:04 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 22968 output: 36 thoughts: 21 Thoughts: Here's my take on the thought process, tailored for an expert audience: **Diving into the DRM Configuration** Okay, time to dig into this. I need to get a handle on the current configuration landscape for the Direct Rendering Manager (DRM) within the kernel. I'm going to zero in on the `drivers/gpu/drm/Kconfig` file. Specifically, I'm thinking the area around line 137 is a good starting point. I suspect that's where a particular feature I'm interested in, or a related section of the configuration options, might reside. I'll see what options are defined there and how they're structured. This will help me understand the dependencies, default states, and potentially some subtleties that aren't immediately obvious from a cursory glance. I'm hoping to quickly identify the relevant configuration symbols and their relationships, allowing me to build a more complete mental model of the module. |
| 8/2 | 2026/10/09 21:05 | tool | read-file |
0mArgs:{
"File": "drivers/gpu/drm/Kconfig",
"FirstLine": 130,
"LineCount": 30
}Results: {
"Contents": " 130:\t\n 131:\tconfig DRM_PANIC_HELPER\n 132:\t\tbool\n 133:\t\tdepends on DRM\n 134:\t\tselect DRM_DRAW\n 135:\t\tselect FONT_SUPPORT\n 136:\t\n 137:\tconfig DRM_RAS\n 138:\t\tbool \"DRM RAS support\"\n 139:\t\tdepends on DRM\n 140:\t\tdepends on NET\n 141:\t\thelp\n 142:\t\t Enables the DRM RAS (Reliability, Availability and Serviceability)\n 143:\t\t support for DRM drivers. This provides a Generic Netlink interface\n 144:\t\t for error reporting and queries.\n 145:\t\t If in doubt, say \"N\".\n 146:\t\n 147:\tconfig DRM_DEBUG_DP_MST_TOPOLOGY_REFS\n 148:\t bool \"Enable refcount backtrace history in the DP MST helpers\"\n 149:\t\tdepends on STACKTRACE_SUPPORT\n 150:\t select STACKDEPOT\n 151:\t select DRM_KMS_HELPER\n 152:\t depends on DEBUG_KERNEL\n 153:\t depends on EXPERT\n 154:\t help\n 155:\t Enables debug tracing for topology refs in DRM's DP MST helpers. A\n 156:\t history of each topology reference/dereference will be printed to the\n 157:\t kernel log once a port or branch device's topology refcount reaches 0.\n 158:\t\n 159:\t This has the potential to use a lot of memory and print some very\n"
} |
| 9/2 | 2026/10/09 21:05 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 23656 output: 21 thoughts: 109 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **DRM RAS Interface Deep Dive** Alright, that's interesting. I see that `config DRM_RAS` defines a Generic Netlink interface for DRM drivers, specifically for RAS (Reliability, Availability, and Serviceability) functionality. I immediately jumped to `drivers/gpu/drm/drm_drv.c`, and, as expected, `drm_ras_genl_family_register()` is called within `drm_core_init()`. Standard initialization procedure there. Now, the crucial question pops up: what exactly *registers* this `drm_ras_node`? That's the key to understanding the full flow. Let me dig into the function `drm_ras_node_register`. I need to figure out how these nodes are set up. Time to fire up `grep` and hunt for instances of `drm_ras_node_register`. This should give me the connection points and driver interactions I need to see. |
| 10/2 | 2026/10/09 21:05 | tool | grepper |
0mArgs:{
"Expression": "drm_ras_node_register"
}Results: {
"Output": "Documentation/gpu/drm-ras.rst=35=Drivers are responsible for registering and unregistering nodes via the\nDocumentation/gpu/drm-ras.rst:36:`drm_ras_node_register()` and `drm_ras_node_unregister()` APIs.\nDocumentation/gpu/drm-ras.rst-37-\n--\ndrivers/accel/qaic/qaic_telemetry.c=411=int qaic_drm_ras_register_node(struct qaic_device *qdev)\n--\ndrivers/accel/qaic/qaic_telemetry.c-444-\ndrivers/accel/qaic/qaic_telemetry.c:445:\tret = drm_ras_node_register(node);\ndrivers/accel/qaic/qaic_telemetry.c-446-\tif (ret) {\n--\ndrivers/gpu/drm/drm_ras.c-66- *\ndrivers/gpu/drm/drm_ras.c:67: * - drm_ras_node_register(): Registers a new node and assigns\ndrivers/gpu/drm/drm_ras.c-68- * it a unique ID in the xarray.\n--\ndrivers/gpu/drm/drm_ras.c=741=int drm_ras_nl_telemetry_write_doit(struct sk_buff *skb, struct genl_info *info)\n--\ndrivers/gpu/drm/drm_ras.c-790-/**\ndrivers/gpu/drm/drm_ras.c:791: * drm_ras_node_register() - Register a new RAS node\ndrivers/gpu/drm/drm_ras.c-792- * @node: Node structure to register\n--\ndrivers/gpu/drm/drm_ras.c-798- */\ndrivers/gpu/drm/drm_ras.c:799:int drm_ras_node_register(struct drm_ras_node *node)\ndrivers/gpu/drm/drm_ras.c-800-{\n--\ndrivers/gpu/drm/drm_ras.c-821-}\ndrivers/gpu/drm/drm_ras.c:822:EXPORT_SYMBOL(drm_ras_node_register);\ndrivers/gpu/drm/drm_ras.c-823-\n--\ndrivers/gpu/drm/xe/xe_drm_ras.c=191=static int register_nodes(struct xe_device *xe)\n--\ndrivers/gpu/drm/xe/xe_drm_ras.c-203-\ndrivers/gpu/drm/xe/xe_drm_ras.c:204:\t\tret = drm_ras_node_register(node);\ndrivers/gpu/drm/xe/xe_drm_ras.c-205-\t\tif (ret)\n--\ninclude/drm/drm_ras.h=146=struct drm_device;\n--\ninclude/drm/drm_ras.h-148-#if IS_ENABLED(CONFIG_DRM_RAS)\ninclude/drm/drm_ras.h:149:int drm_ras_node_register(struct drm_ras_node *node);\ninclude/drm/drm_ras.h-150-void drm_ras_node_unregister(struct drm_ras_node *node);\ninclude/drm/drm_ras.h=151=int drm_ras_nl_error_event(struct drm_ras_node *node, u32 error_id, const char *error_name,\n--\ninclude/drm/drm_ras.h-153-#else\ninclude/drm/drm_ras.h:154:static inline int drm_ras_node_register(struct drm_ras_node *node) { return 0; }\ninclude/drm/drm_ras.h-155-static inline void drm_ras_node_unregister(struct drm_ras_node *node) { }\n"
} |
| 11/2 | 2026/10/09 21:05 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 24939 output: 26 thoughts: 119 Thoughts: Okay, here's my thought process as I digest this: **Analyzing RAS Node Registrations** Alright, hold on a second. I need to take a closer look at these files, specifically `drivers/gpu/drm/xe/xe_drm_ras.c` and `drivers/accel/qaic`. My initial hunch is that they're both working with the RAS (Reliability, Availability, Serviceability) framework, and I'm right. Now, what exactly *are* they registering? Aha! `xe` is setting up `ERROR_COUNTER` nodes, that makes sense for a graphics driver. But wait, look at `qaic`... it's doing something different: it's registering `TELEMETRY` nodes. The code clearly shows `node->type = DRM_RAS_NODE_TYPE_TELEMETRY;`. So, Qaic is the primary owner of Telemetry node type, which is interesting. Now, a crucial question arises: Is `qaic` the *only* one using these `TELEMETRY` nodes? Is this the only user of telemetry nodes? That's a critical point to verify. Let's fire up a `grep` and search the entire codebase for `DRM_RAS_NODE_TYPE_TELEMETRY`. I need to be sure before I move forward. |
| 12/2 | 2026/10/09 21:05 | tool | grepper |
0mArgs:{
"Expression": "DRM_RAS_NODE_TYPE_TELEMETRY"
}Results: {
"Output": "drivers/accel/qaic/qaic_telemetry.c=411=int qaic_drm_ras_register_node(struct qaic_device *qdev)\n--\ndrivers/accel/qaic/qaic_telemetry.c-438-\tnode-\u003enode_name = node_name;\ndrivers/accel/qaic/qaic_telemetry.c:439:\tnode-\u003etype = DRM_RAS_NODE_TYPE_TELEMETRY;\ndrivers/accel/qaic/qaic_telemetry.c-440-\tnode-\u003epriv = qdev;\n--\ndrivers/gpu/drm/drm_ras.c=799=int drm_ras_node_register(struct drm_ras_node *node)\n--\ndrivers/gpu/drm/drm_ras.c-810-\t\tbreak;\ndrivers/gpu/drm/drm_ras.c:811:\tcase DRM_RAS_NODE_TYPE_TELEMETRY:\ndrivers/gpu/drm/drm_ras.c-812-\t\t/* Mandatory entries for Telemetry Node */\n--\ninclude/uapi/drm/drm_ras.h=17=enum drm_ras_node_type {\ninclude/uapi/drm/drm_ras.h-18-\tDRM_RAS_NODE_TYPE_ERROR_COUNTER = 1,\ninclude/uapi/drm/drm_ras.h:19:\tDRM_RAS_NODE_TYPE_TELEMETRY,\ninclude/uapi/drm/drm_ras.h-20-};\n"
} |
| 13/2 | 2026/10/09 21:05 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 25751 output: 40 thoughts: 206 Thoughts: Okay, let's see. My initial thought is to investigate whether the `DRM_RAS_CMD_TELEMETRY_READ` and `DRM_RAS_CMD_TELEMETRY_WRITE` generic netlink commands are accessible from userspace even without any underlying hardware. **Investigating Userspace Access to Generic Netlink Telemetry Commands** Wait a minute, can userspace actually send generic netlink messages to the `drm_ras_nl_family`? Yes, looks like it can! It appears `DRM_RAS_CMD_TELEMETRY_READ` and `DRM_RAS_CMD_TELEMETRY_WRITE` are indeed generic netlink commands exposed through genl. Alright, now the next logical step is to dive deeper into the code. I need to examine the relevant functions to see how this works. I should focus on `drm_ras_nl_pre_telemetry`, `drm_ras_nl_telemetry_read_doit`, and `drm_ras_nl_telemetry_write_doit` in the `drivers/gpu/drm/drm_ras.c` file. Time to read lines 635 to 770 in that file to understand the flow and any potential access restrictions. I need to see how the kernel handles these requests and what, if any, checks are in place. |
| 14/2 | 2026/10/09 21:05 | tool | read-file |
0mArgs:{
"File": "drivers/gpu/drm/drm_ras.c",
"FirstLine": 635,
"LineCount": 100
}Results: {
"Contents": " 635:\t\tthreshold = nla_get_u32(info-\u003eattrs[DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_THRESHOLD]);\n 636:\t\n 637:\t\treturn set_node_error_threshold(node_id, error_id, threshold);\n 638:\t}\n 639:\t\n 640:\t/**\n 641:\t * drm_ras_nl_pre_telemetry - Enforce permission checks\n 642:\t * @ops: Generic netlink operations\n 643:\t * @skb: Netlink message buffer\n 644:\t * @info: Generic Netlink info containing attributes of the request\n 645:\t *\n 646:\t * Enforces a telemetry node's pre_doit conditions (if they exist).\n 647:\t *\n 648:\t * Returns 0 if no pre_doit is specified, otherwise returns the pre_doit.\n 649:\t */\n 650:\tint drm_ras_nl_pre_telemetry(const struct genl_split_ops *ops, struct sk_buff *skb,\n 651:\t\t\t\t struct genl_info *info)\n 652:\t{\n 653:\t\tstruct drm_ras_node *node;\n 654:\t\tu32 node_id;\n 655:\t\n 656:\t\tnode_id = nla_get_u32(info-\u003eattrs[DRM_RAS_A_TELEMETRY_ATTRS_NODE_ID]);\n 657:\t\tnode = xa_load(\u0026drm_ras_xa, node_id);\n 658:\t\n 659:\t\tif (node-\u003etelemetry_pre)\n 660:\t\t\treturn node-\u003etelemetry_pre(node);\n 661:\t\n 662:\t\treturn 0;\n 663:\t}\n 664:\t\n 665:\t/**\n 666:\t * drm_ras_nl_telemetry_read_doit() - Query a telemetry value\n 667:\t * @skb: Netlink message buffer\n 668:\t * @info: Generic Netlink info containing attributes of the request\n 669:\t *\n 670:\t * Extracts the telemetry field from the netlink attributes and\n 671:\t * reads that value from the device.\n 672:\t *\n 673:\t * Return: 0 on success, or negative errno on failure.\n 674:\t */\n 675:\tint drm_ras_nl_telemetry_read_doit(struct sk_buff *skb, struct genl_info *info)\n 676:\t{\n 677:\t\tstruct drm_ras_node *node;\n 678:\t\tstruct sk_buff *msg;\n 679:\t\tstruct nlattr *hdr;\n 680:\t\tu32 node_id, field;\n 681:\t\ts32 status;\n 682:\t\tu64 value;\n 683:\t\tint ret;\n 684:\t\n 685:\t\tif (!info-\u003eattrs || !info-\u003eattrs[DRM_RAS_A_TELEMETRY_ATTRS_FIELD])\n 686:\t\t\treturn -EINVAL;\n 687:\t\n 688:\t\tfield = nla_get_u32(info-\u003eattrs[DRM_RAS_A_TELEMETRY_ATTRS_FIELD]);\n 689:\t\tnode_id = nla_get_u32(info-\u003eattrs[DRM_RAS_A_TELEMETRY_ATTRS_NODE_ID]);\n 690:\t\n 691:\t\tnode = xa_load(\u0026drm_ras_xa, node_id);\n 692:\t\tif (!node || !node-\u003etelemetry_read)\n 693:\t\t\treturn -ENOENT;\n 694:\t\n 695:\t\tmsg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);\n 696:\t\tif (!msg)\n 697:\t\t\treturn -ENOMEM;\n 698:\t\n 699:\t\thdr = genlmsg_iput(msg, info);\n 700:\t\tif (!hdr) {\n 701:\t\t\tnlmsg_free(msg);\n 702:\t\t\treturn -EMSGSIZE;\n 703:\t\t}\n 704:\t\n 705:\t\tret = node-\u003etelemetry_read(node, \u0026field, \u0026value, \u0026status);\n 706:\t\tif (ret) {\n 707:\t\t\tnlmsg_free(msg);\n 708:\t\t\treturn ret;\n 709:\t\t}\n 710:\t\n 711:\t\tret = nla_put_s32(msg, DRM_RAS_A_TELEMETRY_ATTRS_STATUS, status);\n 712:\t\tif (ret) {\n 713:\t\t\tgenlmsg_cancel(msg, hdr);\n 714:\t\t\tnlmsg_free(msg);\n 715:\t\t\treturn ret;\n 716:\t\t}\n 717:\t\n 718:\t\tret = nla_put_64bit(msg, DRM_RAS_A_TELEMETRY_ATTRS_VALUE, sizeof(value),\n 719:\t\t\t\t \u0026value, DRM_RAS_A_TELEMETRY_ATTRS_PAD);\n 720:\t\tif (ret) {\n 721:\t\t\tgenlmsg_cancel(msg, hdr);\n 722:\t\t\tnlmsg_free(msg);\n 723:\t\t\treturn ret;\n 724:\t\t}\n 725:\t\n 726:\t\tgenlmsg_end(msg, hdr);\n 727:\t\n 728:\t\treturn genlmsg_reply(msg, info);\n 729:\t}\n 730:\t\n 731:\t/**\n 732:\t * drm_ras_nl_telemetry_write_doit() - Write a telemetry value\n 733:\t * @skb: Netlink message buffer\n 734:\t * @info: Generic Netlink info containing attributes of the request\n"
} |
| 15/2 | 2026/10/09 21:05 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 27854 output: 38 thoughts: 401 Thoughts: **Kernel Vulnerability: Potential NULL Pointer Dereference in drm_ras_nl_pre_telemetry** Okay, let's break this down. Line 650... I'm looking at `drm_ras_nl_pre_telemetry`. I see `node = xa_load(&drm_ras_xa, node_id);`. My first thought is: What if `node` is `NULL`? If userspace sends a `DRM_RAS_CMD_TELEMETRY_READ` command with an invalid `node_id`, or if that node isn't registered in `drm_ras_xa` (like node_id 0 or 42), `xa_load` could return `NULL`. Then, immediately following that, we have `if (node->telemetry_pre)`. BOOM! Null pointer dereference! That's a kernel crash waiting to happen. Holy cow. But wait, even deeper... are we *sure* `info->attrs[DRM_RAS_A_TELEMETRY_ATTRS_NODE_ID]` actually *exists*? We've got to check the policy defined in `drm_ras_nl.c` for this command. We need to make absolutely sure the attribute is there and validated before we even get to this point. This looks like a potentially serious vulnerability. |
| 16/2 | 2026/10/09 21:06 | tool | grepper |
0mArgs:{
"Expression": "drm_ras_telemetry_read_nl_policy",
"PathPrefixes": [
"drivers/gpu/drm"
]
}Results: {
"Output": "drivers/gpu/drm/drm_ras_nl.c=38=static const struct nla_policy drm_ras_set_error_threshold_nl_policy[DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_THRESHOLD + 1] = {\n--\ndrivers/gpu/drm/drm_ras_nl.c-44-/* DRM_RAS_CMD_TELEMETRY_READ - do */\ndrivers/gpu/drm/drm_ras_nl.c:45:static const struct nla_policy drm_ras_telemetry_read_nl_policy[DRM_RAS_A_TELEMETRY_ATTRS_FIELD + 1] = {\ndrivers/gpu/drm/drm_ras_nl.c-46-\t[DRM_RAS_A_TELEMETRY_ATTRS_NODE_ID] = { .type = NLA_U32, },\n--\ndrivers/gpu/drm/drm_ras_nl.c=58=static const struct genl_split_ops drm_ras_nl_ops[] = {\n--\ndrivers/gpu/drm/drm_ras_nl.c-102-\t\t.doit\t\t= drm_ras_nl_telemetry_read_doit,\ndrivers/gpu/drm/drm_ras_nl.c:103:\t\t.policy\t\t= drm_ras_telemetry_read_nl_policy,\ndrivers/gpu/drm/drm_ras_nl.c-104-\t\t.maxattr\t= DRM_RAS_A_TELEMETRY_ATTRS_FIELD,\n"
} |
| 17/2 | 2026/10/09 21:06 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 28960 output: 24 thoughts: 341 Thoughts: **My Concerns with the `drm_ras_telemetry_read_nl_policy` Function**
Okay, here's what's going through my head as I dissect this code: First, I see that `NLA_U32` is used, but I'm immediately wondering if the genl core mandates that attribute. Does the Netlink specification or the genl core enforce the validation, or does it even require it to be present? Without strict enforcement via `NLA_POLICY_EXACT_LEN` or similar checks in the Netlink spec, the user might simply omit an attribute, and the code might not handle this gracefully!
And it gets worse! Even if `info->attrs[DRM_RAS_A_TELEMETRY_ATTRS_NODE_ID]` *is* provided, what happens if the `node_id` is invalid, say `12345`? The subsequent `xa_load` returns `NULL`, right? Then, the very next line, `if (node->telemetry_pre)` attempts to dereference `NULL->telemetry_pre`, leading to a certain NULL pointer dereference, and a crash in generic Netlink! This is a textbook vulnerability.
Now, a more fundamental question – what happens if `info->attrs[DRM_RAS_A_TELEMETRY_ATTRS_NODE_ID]` is actually `NULL`, meaning the user didn't even *provide* a node ID? Does the Netlink spec mandate the `node-id` attribute in the first place? I need to clarify that.
Let me consult the documentation: specifically, `Documentation/netlink/specs/drm_ras.yaml`. The spec says:
```yaml
do:
pre: drm-ras-nl-pre-telemetry
request:
attributes:
- node-id
- field
```
Okay, so `node-id` is listed as a mandatory attribute within the `request` block. Excellent - the spec *does* mandate it. So at least there is a requirement, I just need to verify that it is properly enforced in code. I need to understand the translation from YAML to the actual C code. I should check `drivers/gpu/drm/drm_ras_genl_family.c` to see how the YAML is processed. That's my next step.
|
| 18/2 | 2026/10/09 21:07 | tool | grepper |
0mArgs:{
"Expression": "drm_ras_nl_pre_telemetry"
}Results: {
"Output": "drivers/gpu/drm/drm_ras.c-111- * operation, setting the error threshold of a specific counter.\ndrivers/gpu/drm/drm_ras.c:112: * - drm_ras_nl_pre_telemetry(): Implements the TELEMETRY_READ and TELEMETRY_WRITE\ndrivers/gpu/drm/drm_ras.c-113- * pre_doit operation, returning 0 if valid.\n--\ndrivers/gpu/drm/drm_ras.c=623=int drm_ras_nl_set_error_threshold_doit(struct sk_buff *skb, struct genl_info *info)\n--\ndrivers/gpu/drm/drm_ras.c-640-/**\ndrivers/gpu/drm/drm_ras.c:641: * drm_ras_nl_pre_telemetry - Enforce permission checks\ndrivers/gpu/drm/drm_ras.c-642- * @ops: Generic netlink operations\n--\ndrivers/gpu/drm/drm_ras.c-649- */\ndrivers/gpu/drm/drm_ras.c:650:int drm_ras_nl_pre_telemetry(const struct genl_split_ops *ops, struct sk_buff *skb,\ndrivers/gpu/drm/drm_ras.c-651-\t\t\t struct genl_info *info)\n--\ndrivers/gpu/drm/drm_ras_nl.c=58=static const struct genl_split_ops drm_ras_nl_ops[] = {\n--\ndrivers/gpu/drm/drm_ras_nl.c-100-\t\t.cmd\t\t= DRM_RAS_CMD_TELEMETRY_READ,\ndrivers/gpu/drm/drm_ras_nl.c:101:\t\t.pre_doit\t= drm_ras_nl_pre_telemetry,\ndrivers/gpu/drm/drm_ras_nl.c-102-\t\t.doit\t\t= drm_ras_nl_telemetry_read_doit,\n--\ndrivers/gpu/drm/drm_ras_nl.c-108-\t\t.cmd\t\t= DRM_RAS_CMD_TELEMETRY_WRITE,\ndrivers/gpu/drm/drm_ras_nl.c:109:\t\t.pre_doit\t= drm_ras_nl_pre_telemetry,\ndrivers/gpu/drm/drm_ras_nl.c-110-\t\t.doit\t\t= drm_ras_nl_telemetry_write_doit,\n--\ndrivers/gpu/drm/drm_ras_nl.h-14-\ndrivers/gpu/drm/drm_ras_nl.h:15:int drm_ras_nl_pre_telemetry(const struct genl_split_ops *ops,\ndrivers/gpu/drm/drm_ras_nl.h-16-\t\t\t struct sk_buff *skb, struct genl_info *info);\n"
} |
| 19/2 | 2026/10/09 21:07 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 30539 output: 42 thoughts: 37 Thoughts: Here's my take on the thought process, as requested: **Diving into the DRM RAS Netlink Registration** Okay, hold on a second. Before I proceed any further with this line of investigation, I need to get a clear understanding of the existing framework for the DRM (Direct Rendering Manager) RAS (Reliability, Availability, and Serviceability) netlink interface. I'm going to quickly check the code that sets this up. Specifically, I'm pulling up `drivers/gpu/drm/drm_ras_genl_family.c`. I need to refresh my memory on how this netlink family is registered, how the messages are structured, and the overall architecture. This is critical for getting a proper foundation, and potentially helping the team find a solution. |
| 20/2 | 2026/10/09 21:07 | tool | read-file |
0mArgs:{
"File": "drivers/gpu/drm/drm_ras_genl_family.c",
"FirstLine": 1,
"LineCount": 80
}Results: {
"Contents": " 1:\t// SPDX-License-Identifier: MIT\n 2:\t/*\n 3:\t * Copyright © 2026 Intel Corporation\n 4:\t */\n 5:\t\n 6:\t#include \u003cdrm/drm_ras_genl_family.h\u003e\n 7:\t#include \"drm_ras_nl.h\"\n 8:\t\n 9:\t/* Track family registration so the drm_exit can be called at any time */\n 10:\tstatic bool registered;\n 11:\t\n 12:\t/**\n 13:\t * drm_ras_genl_family_register() - Register drm-ras genl family\n 14:\t *\n 15:\t * Only to be called one at drm_drv_init()\n 16:\t */\n 17:\tint drm_ras_genl_family_register(void)\n 18:\t{\n 19:\t\tint ret;\n 20:\t\n 21:\t\tregistered = false;\n 22:\t\n 23:\t\tret = genl_register_family(\u0026drm_ras_nl_family);\n 24:\t\tif (ret)\n 25:\t\t\treturn ret;\n 26:\t\n 27:\t\tregistered = true;\n 28:\t\treturn 0;\n 29:\t}\n 30:\t\n 31:\t/**\n 32:\t * drm_ras_genl_family_unregister() - Unregister drm-ras genl family\n 33:\t *\n 34:\t * To be called one at drm_drv_exit() at any moment, but only once.\n 35:\t */\n 36:\tvoid drm_ras_genl_family_unregister(void)\n 37:\t{\n 38:\t\tif (registered) {\n 39:\t\t\tgenl_unregister_family(\u0026drm_ras_nl_family);\n 40:\t\t\tregistered = false;\n 41:\t\t}\n 42:\t}\n"
} |
| 21/2 | 2026/10/09 21:07 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 31279 output: 33 thoughts: 29 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience and presented in the first person: **Diving into the RAS Implementation** Alright, let's get into it. My initial focus here is on `drivers/gpu/drm/drm_ras_nl.c`, specifically around the `drm_ras_nl_family`. I immediately zero in on that area because the Network Link (NL) interface is key. My understanding is that this file likely houses the Netlink interface implementation for Reliability, Availability, and Serviceability (RAS) functionality within the Direct Rendering Manager (DRM) subsystem. I'm thinking, this is where the interesting logic resides—the plumbing that allows user-space tools to interact with the kernel's RAS features, likely to monitor errors, request information, and maybe even trigger actions related to hardware faults. I will be looking into the message types defined and any associated data structures. I want to see how this implementation meshes with the broader DRM error reporting architecture. Is it built on top of existing DRM error handling, or does it represent a new layer entirely? I want to see the details. |
| 22/2 | 2026/10/09 21:07 | tool | grepper |
0mArgs:{
"Expression": "drm_ras_nl_family",
"PathPrefixes": [
"drivers/gpu/drm"
]
}Results: {
"Output": "drivers/gpu/drm/drm_ras.c=401=int drm_ras_nl_error_event(struct drm_ras_node *node, u32 error_id, const char *error_name,\n--\ndrivers/gpu/drm/drm_ras.c-424-\ndrivers/gpu/drm/drm_ras.c:425:\tgenl_info_init_ntf(\u0026info, \u0026drm_ras_nl_family, DRM_RAS_CMD_ERROR_EVENT);\ndrivers/gpu/drm/drm_ras.c-426-\n--\ndrivers/gpu/drm/drm_ras.c-441-\tgenlmsg_end(msg, hdr);\ndrivers/gpu/drm/drm_ras.c:442:\tgenlmsg_multicast_allns(\u0026drm_ras_nl_family, msg, 0, DRM_RAS_NLGRP_ERROR_REPORT);\ndrivers/gpu/drm/drm_ras.c-443-\n--\ndrivers/gpu/drm/drm_ras_genl_family.c=17=int drm_ras_genl_family_register(void)\n--\ndrivers/gpu/drm/drm_ras_genl_family.c-22-\ndrivers/gpu/drm/drm_ras_genl_family.c:23:\tret = genl_register_family(\u0026drm_ras_nl_family);\ndrivers/gpu/drm/drm_ras_genl_family.c-24-\tif (ret)\n--\ndrivers/gpu/drm/drm_ras_genl_family.c=36=void drm_ras_genl_family_unregister(void)\n--\ndrivers/gpu/drm/drm_ras_genl_family.c-38-\tif (registered) {\ndrivers/gpu/drm/drm_ras_genl_family.c:39:\t\tgenl_unregister_family(\u0026drm_ras_nl_family);\ndrivers/gpu/drm/drm_ras_genl_family.c-40-\t\tregistered = false;\n--\ndrivers/gpu/drm/drm_ras_nl.c=117=static const struct genl_multicast_group drm_ras_nl_mcgrps[] = {\n--\ndrivers/gpu/drm/drm_ras_nl.c-120-\ndrivers/gpu/drm/drm_ras_nl.c:121:struct genl_family drm_ras_nl_family __ro_after_init = {\ndrivers/gpu/drm/drm_ras_nl.c-122-\t.name\t\t= DRM_RAS_FAMILY_NAME,\n--\ndrivers/gpu/drm/drm_ras_nl.h=34=enum {\n--\ndrivers/gpu/drm/drm_ras_nl.h-37-\ndrivers/gpu/drm/drm_ras_nl.h:38:extern struct genl_family drm_ras_nl_family;\ndrivers/gpu/drm/drm_ras_nl.h-39-\n"
} |
| 23/2 | 2026/10/09 21:07 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 32306 output: 40 thoughts: 23 Thoughts: Okay, here's my attempt at summarizing the thought process, tailored for an expert audience, and written in the first person: **Diving into `drm_ras_nl.c` - Line Investigation** Alright, time to get my hands dirty. Let's start with a focused look at `drm_ras_nl.c`, specifically lines 50 through 140. I know this section likely deals with the Network Link side of things related to RAS (Reliability, Availability, and Serviceability) for Direct Rendering Manager. I’m expecting to see the core logic for either setting up the Netlink sockets, handling incoming messages related to RAS events or possibly error reporting through the Netlink interface. It's a key area to understand how the driver communicates with userspace tools and other kernel subsystems for fault monitoring and management. I anticipate some conditional logic in there to distinguish different RAS events. I will look for any clues on how memory errors are reported over the Netlink interface. Any specific error event types that are handled will be of high interest. |
| 24/2 | 2026/10/09 21:08 | tool | read-file |
0mArgs:{
"File": "drivers/gpu/drm/drm_ras_nl.c",
"FirstLine": 50,
"LineCount": 80
}Results: {
"Contents": " 50:\t/* DRM_RAS_CMD_TELEMETRY_WRITE - do */\n 51:\tstatic const struct nla_policy drm_ras_telemetry_write_nl_policy[DRM_RAS_A_TELEMETRY_ATTRS_VALUE + 1] = {\n 52:\t\t[DRM_RAS_A_TELEMETRY_ATTRS_NODE_ID] = { .type = NLA_U32, },\n 53:\t\t[DRM_RAS_A_TELEMETRY_ATTRS_FIELD] = { .type = NLA_U32, },\n 54:\t\t[DRM_RAS_A_TELEMETRY_ATTRS_VALUE] = { .type = NLA_U64, },\n 55:\t};\n 56:\t\n 57:\t/* Ops table for drm_ras */\n 58:\tstatic const struct genl_split_ops drm_ras_nl_ops[] = {\n 59:\t\t{\n 60:\t\t\t.cmd\t= DRM_RAS_CMD_LIST_NODES,\n 61:\t\t\t.dumpit\t= drm_ras_nl_list_nodes_dumpit,\n 62:\t\t\t.flags\t= GENL_CMD_CAP_DUMP,\n 63:\t\t},\n 64:\t\t{\n 65:\t\t\t.cmd\t\t= DRM_RAS_CMD_GET_ERROR_COUNTER,\n 66:\t\t\t.doit\t\t= drm_ras_nl_get_error_counter_doit,\n 67:\t\t\t.policy\t\t= drm_ras_get_error_counter_do_nl_policy,\n 68:\t\t\t.maxattr\t= DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_ID,\n 69:\t\t\t.flags\t\t= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,\n 70:\t\t},\n 71:\t\t{\n 72:\t\t\t.cmd\t\t= DRM_RAS_CMD_GET_ERROR_COUNTER,\n 73:\t\t\t.dumpit\t\t= drm_ras_nl_get_error_counter_dumpit,\n 74:\t\t\t.policy\t\t= drm_ras_get_error_counter_dump_nl_policy,\n 75:\t\t\t.maxattr\t= DRM_RAS_A_ERROR_COUNTER_ATTRS_NODE_ID,\n 76:\t\t\t.flags\t\t= GENL_ADMIN_PERM | GENL_CMD_CAP_DUMP,\n 77:\t\t},\n 78:\t\t{\n 79:\t\t\t.cmd\t\t= DRM_RAS_CMD_CLEAR_ERROR_COUNTER,\n 80:\t\t\t.doit\t\t= drm_ras_nl_clear_error_counter_doit,\n 81:\t\t\t.policy\t\t= drm_ras_clear_error_counter_nl_policy,\n 82:\t\t\t.maxattr\t= DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_ID,\n 83:\t\t\t.flags\t\t= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,\n 84:\t\t},\n 85:\t\t{\n 86:\t\t\t.cmd\t\t= DRM_RAS_CMD_GET_ERROR_THRESHOLD,\n 87:\t\t\t.doit\t\t= drm_ras_nl_get_error_threshold_doit,\n 88:\t\t\t.policy\t\t= drm_ras_get_error_threshold_nl_policy,\n 89:\t\t\t.maxattr\t= DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_ID,\n 90:\t\t\t.flags\t\t= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,\n 91:\t\t},\n 92:\t\t{\n 93:\t\t\t.cmd\t\t= DRM_RAS_CMD_SET_ERROR_THRESHOLD,\n 94:\t\t\t.doit\t\t= drm_ras_nl_set_error_threshold_doit,\n 95:\t\t\t.policy\t\t= drm_ras_set_error_threshold_nl_policy,\n 96:\t\t\t.maxattr\t= DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_THRESHOLD,\n 97:\t\t\t.flags\t\t= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,\n 98:\t\t},\n 99:\t\t{\n 100:\t\t\t.cmd\t\t= DRM_RAS_CMD_TELEMETRY_READ,\n 101:\t\t\t.pre_doit\t= drm_ras_nl_pre_telemetry,\n 102:\t\t\t.doit\t\t= drm_ras_nl_telemetry_read_doit,\n 103:\t\t\t.policy\t\t= drm_ras_telemetry_read_nl_policy,\n 104:\t\t\t.maxattr\t= DRM_RAS_A_TELEMETRY_ATTRS_FIELD,\n 105:\t\t\t.flags\t\t= GENL_CMD_CAP_DO,\n 106:\t\t},\n 107:\t\t{\n 108:\t\t\t.cmd\t\t= DRM_RAS_CMD_TELEMETRY_WRITE,\n 109:\t\t\t.pre_doit\t= drm_ras_nl_pre_telemetry,\n 110:\t\t\t.doit\t\t= drm_ras_nl_telemetry_write_doit,\n 111:\t\t\t.policy\t\t= drm_ras_telemetry_write_nl_policy,\n 112:\t\t\t.maxattr\t= DRM_RAS_A_TELEMETRY_ATTRS_VALUE,\n 113:\t\t\t.flags\t\t= GENL_CMD_CAP_DO,\n 114:\t\t},\n 115:\t};\n 116:\t\n 117:\tstatic const struct genl_multicast_group drm_ras_nl_mcgrps[] = {\n 118:\t\t[DRM_RAS_NLGRP_ERROR_REPORT] = { \"error-report\", },\n 119:\t};\n 120:\t\n 121:\tstruct genl_family drm_ras_nl_family __ro_after_init = {\n 122:\t\t.name\t\t= DRM_RAS_FAMILY_NAME,\n 123:\t\t.version\t= DRM_RAS_FAMILY_VERSION,\n 124:\t\t.netnsok\t= true,\n 125:\t\t.parallel_ops\t= true,\n 126:\t\t.module\t\t= THIS_MODULE,\n 127:\t\t.split_ops\t= drm_ras_nl_ops,\n 128:\t\t.n_split_ops\t= ARRAY_SIZE(drm_ras_nl_ops),\n 129:\t\t.mcgrps\t\t= drm_ras_nl_mcgrps,\n"
} |
| 25/2 | 2026/10/09 21:08 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 34211 output: 40 thoughts: 1310 Thoughts: **A Deep Dive into a Potential Kernel Vulnerability**
Okay, so I'm looking at this `drm_ras_nl_ops` structure, and something immediately jumps out at me. The `flags` for `DRM_RAS_CMD_TELEMETRY_READ` and `DRM_RAS_CMD_TELEMETRY_WRITE` are set to `GENL_CMD_CAP_DO`, but critically, they lack `GENL_ADMIN_PERM`. And it's not just these; `DRM_RAS_CMD_LIST_NODES` also had `GENL_ADMIN_PERM` removed. This is a red flag. Anyone with generic netlink access, which is basically any unprivileged user socket that can use `AF_NETLINK` and `NETLINK_GENERIC`, can potentially send messages to the `"drm_ras"` family with commands like `DRM_RAS_CMD_TELEMETRY_READ` and `DRM_RAS_CMD_TELEMETRY_WRITE`.
Let's trace what happens when someone sends `DRM_RAS_CMD_TELEMETRY_READ`. The generic netlink core matches the command, and it calls `ops->pre_doit`, which is `drm_ras_nl_pre_telemetry`. I'm seeing this:
```c
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;
}
```
Okay, now hold on a second. I'm focusing on that `info->attrs[DRM_RAS_A_TELEMETRY_ATTRS_NODE_ID]` line. What happens if the user crafts a message where `node_id = 1`, but there's no actual hardware registered? `xa_load` returns `NULL`. So, `node` is `NULL`. Then the code executes `if (node->telemetry_pre)`, which dereferences a null pointer, right? It's a kernel crash in `drm_ras_nl_pre_telemetry`! What's even worse? It doesn't even check if that `DRM_RAS_A_TELEMETRY_ATTRS_NODE_ID` attribute is even present in the first place! If it is missing, `info->attrs[DRM_RAS_A_TELEMETRY_ATTRS_NODE_ID]` is `NULL`, and the call to `nla_get_u32(NULL)` will cause another crash.
But it gets worse, I'm now looking at `drm_ras_nl_telemetry_write_doit`:
```c
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) // BUG: in write_doit it checks !node->telemetry_read instead of telemetry_write?!
```
See it?! On line 749 of `drivers/gpu/drm/drm_ras.c`, the check `!node->telemetry_read` instead of `!node->telemetry_write`! I see it! Also, it's the same problem as before. It doesn't even check for the existence of the node!
So, can I reproduce this in a QEMU environment? Let's check. Can I enable `CONFIG_DRM_RAS` without specific hardware? Let's look at `drivers/gpu/drm/Kconfig`:
```kconfig
config DRM_RAS
bool "DRM
|
| 26/2 | 2026/10/09 21:08 | tool | read-file |
0mArgs:{
"File": "drivers/gpu/drm/drm_drv.c",
"FirstLine": 1280,
"LineCount": 35
}Results: {
"Contents": "1280:\t\tdrm_debugfs_bridge_params();\n1281:\t\n1282:\t\tret = register_chrdev(DRM_MAJOR, \"drm\", \u0026drm_stub_fops);\n1283:\t\tif (ret \u003c 0)\n1284:\t\t\tgoto error;\n1285:\t\n1286:\t\tret = accel_core_init();\n1287:\t\tif (ret \u003c 0)\n1288:\t\t\tgoto error;\n1289:\t\n1290:\t\tdrm_panic_init();\n1291:\t\n1292:\t\tdrm_privacy_screen_lookup_init();\n1293:\t\n1294:\t\tret = drm_ras_genl_family_register();\n1295:\t\tif (ret \u003c 0)\n1296:\t\t\tgoto error;\n1297:\t\n1298:\t\tdrm_core_init_complete = true;\n1299:\t\n1300:\t\tDRM_DEBUG(\"Initialized\\n\");\n1301:\t\treturn 0;\n1302:\t\n1303:\terror:\n1304:\t\tdrm_core_exit();\n1305:\t\treturn ret;\n1306:\t}\n1307:\t\n1308:\tmodule_init(drm_core_init);\n1309:\tmodule_exit(drm_core_exit);\n"
} |
| 27/2 | 2026/10/09 21:08 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 36962 output: 36 thoughts: 257 Thoughts: **My Initial Assessment of the DRM RAS Module's Netlink Interface** Okay, so the `drm_core_init` function calls `drm_ras_genl_family_register()` right at the start. That's good, efficient initialization. When `CONFIG_DRM_RAS` is enabled, the `DRM_RAS_FAMILY_NAME` which is "drm_ras," is registered in the kernel as a Generic Netlink family. This opens a potential attack surface, of course. Userspace interacts with this via the standard netlink socket - `socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC)`. The fact that Syzkaller, which I'm very familiar with, has built-in support for generic netlink, or can fuzz it easily, immediately catches my attention. That is a clear vector for fuzzing. If userspace sends a `DRM_RAS_CMD_TELEMETRY_READ` or `DRM_RAS_CMD_TELEMETRY_WRITE` message, it looks like the calls are going to `drm_ras_nl_pre_telemetry`, then `drm_ras_nl_telemetry_read_doit` or `drm_ras_nl_telemetry_write_doit` specifically. That's the code I need to really dig into now. Wait, before I get too far down this road, I should also look at `drivers/accel/qaic`, just to see if there's any related functionality that might be vulnerable. What exactly *is* qaic? Time to check the `Kconfig` file in that directory and get a handle on what it does. |
| 28/2 | 2026/10/09 21:08 | tool | read-file |
0mArgs:{
"File": "drivers/accel/qaic/Kconfig",
"FirstLine": 1,
"LineCount": 40
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: GPL-2.0-only\n 2:\t#\n 3:\t# Qualcomm Cloud AI accelerators driver\n 4:\t#\n 5:\t\n 6:\tconfig DRM_ACCEL_QAIC\n 7:\t\ttristate \"Qualcomm Cloud AI accelerators\"\n 8:\t\tdepends on DRM_ACCEL\n 9:\t\tdepends on PCI \u0026\u0026 HAS_IOMEM\n 10:\t\tdepends on MHI_BUS\n 11:\t\tselect CRC32\n 12:\t\tselect WANT_DEV_COREDUMP\n 13:\t\thelp\n 14:\t\t Enables driver for Qualcomm's Cloud AI accelerator PCIe cards that are\n 15:\t\t designed to accelerate Deep Learning inference workloads.\n 16:\t\n 17:\t\t The driver manages the PCIe devices and provides an IOCTL interface\n 18:\t\t for users to submit workloads to the devices.\n 19:\t\n 20:\t\t If unsure, say N.\n 21:\t\n 22:\t\t To compile this driver as a module, choose M here: the\n 23:\t\t module will be called qaic.\n"
} |
| 29/2 | 2026/10/09 21:08 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 37905 output: 30 thoughts: 1385 Thoughts: **Fuzzing Analysis of DRM_RAS Netlink Interface** Okay, so I've got this patch series related to `DRM_RAS`, which leverages Netlink, and I need to determine if it's worth fuzzing. The immediate concern is the dependency on the Qualcomm Cloud AI accelerator PCIe cards (QAIC), which, if required, would make it *not* fuzzable under standard QEMU environments. However, the `DRM_RAS` code resides within the DRM subsystem, specifically `drivers/gpu/drm/drm_ras.c` and `drivers/gpu/drm/drm_ras_nl.c`, and importantly, it uses generic Netlink! This is crucial. Let's break down the rules. My understanding is that fuzzing is primarily for code that executes in standard virtualized environments. If the code is unreachable due to hardware requirements unavailable in a standard QEMU setup, it is *not* fuzzable. We need to be wary of claims of "no functional change" or "refactoring". Any new or modified assertions in reachable code *must* be fuzzed. Conversely, we *don't* fuzz code that is structurally unreachable, especially vendor-specific PCIe hardware like QAIC. Now, Netlink is specifically listed as a reachable protocol: "Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.)." And, here, `DRM_RAS` registers a Generic Netlink family during DRM core initialization! Even without any QAIC or Xe hardware in the system, the `drm_ras_genl_family` is active. This means that any userspace process can open a Netlink socket, resolve the "drm_ras" family, and invoke new Netlink commands: `DRM_RAS_CMD_TELEMETRY_READ`, `DRM_RAS_CMD_TELEMETRY_WRITE`, and the dump command `DRM_RAS_CMD_LIST_NODES`. The removal of `GENL_ADMIN_PERM` on the dump command means *any* unprivileged user can now call it! Crucially, when `DRM_RAS_CMD_TELEMETRY_READ` or `DRM_RAS_CMD_TELEMETRY_WRITE` is invoked, it immediately executes `drm_ras_nl_pre_telemetry()`. And *here* is where it gets interesting: Inside `drm_ras_nl_pre_telemetry()`, a `node_id` is read from the Netlink message. If the node is not found (which is likely if no hardware is present, or if an invalid ID is used), we get a NULL pointer dereference. Furthermore, if the `DRM_RAS_A_TELEMETRY_ATTRS_NODE_ID` attribute is omitted from the Netlink message, this *also* results in a NULL pointer dereference. The `drm_ras_nl_telemetry_read_doit()` and `drm_ras_nl_telemetry_write_doit()` functions, as Netlink entrypoints, are also potentially vulnerable. Therefore, this patch *definitively* modifies code that is directly reachable via Netlink! So, the `WorthFuzzing` should be `TRUE`! Now, let's look into the required configuration settings. What kernel config flags are needed, without the `CONFIG_` prefix? I'll trace dependencies for `DRM_RAS`. The Kconfig shows that `DRM_RAS` depends on `DRM` and `NET`. But... What about `DRM_ACCEL_QAIC`? Does it need to be included as well? `DRM_ACCEL_QAIC` depends on `DRM_ACCEL`, `PCI`, `HAS_IOMEM`, `MHI_BUS`. But QAIC can’t be fuzzed without QAIC hardware (unless MHI bus has a mock device, which standard QEMU doesn't have). Could QAIC be enabled anyway? `DRM_RAS` needs `DRM` and `NET`. Does `DRM` depend on anything else? I need to grep for `config DRM` in `drivers/gpu/drm/Kconfig` to be sure. |
| 30/2 | 2026/10/09 21:08 | tool | grepper |
0mArgs:{
"Expression": "config DRM\\b",
"PathPrefixes": [
"drivers/gpu/drm"
]
}Results: {
"Output": "drivers/gpu/drm/Kconfig-7-#\ndrivers/gpu/drm/Kconfig:8:menuconfig DRM\ndrivers/gpu/drm/Kconfig-9-\ttristate \"Direct Rendering Manager (XFree86 4.1.0 and higher DRI support)\"\n"
} |
| 31/2 | 2026/10/09 21:08 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 40314 output: 34 thoughts: 11 Thoughts: Okay, let's dive into this. Here's how I'd approach this task, as if these were my internal thoughts: **Analyzing `menuconfig DRM` Dependencies** Alright, I need to get a clear picture of what's going on with this `menuconfig DRM` situation. My gut feeling is that something upstream in the configuration chain is affecting this module, so I'm thinking of meticulously looking into any dependencies it might have. This is standard operating procedure, really. I need to understand what other components, drivers, or settings are tied to the DRM configuration, either directly or indirectly. The goal is to identify all the pieces that contribute to the current state of DRM, and spot any potential bottlenecks or conflicts that may be present. I'll need to trace back through the configurations, looking for any conditional dependencies that are either active or inactive. This will give me a better understanding of the wider context. |
| 32/2 | 2026/10/09 21:08 | tool | read-file |
0mArgs:{
"File": "drivers/gpu/drm/Kconfig",
"FirstLine": 5,
"LineCount": 25
}Results: {
"Contents": " 5:\t# This driver provides support for the\n 6:\t# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.\n 7:\t#\n 8:\tmenuconfig DRM\n 9:\t\ttristate \"Direct Rendering Manager (XFree86 4.1.0 and higher DRI support)\"\n 10:\t\tdepends on (AGP || AGP=n) \u0026\u0026 !EMULATED_CMPXCHG \u0026\u0026 HAS_DMA\n 11:\t\tselect DRM_PANEL_ORIENTATION_QUIRKS\n 12:\t\tselect HDMI\n 13:\t\tselect I2C\n 14:\t\tselect DMA_SHARED_BUFFER\n 15:\t\tselect SYNC_FILE\n 16:\t# gallium uses SYS_kcmp for os_same_file_description() to de-duplicate\n 17:\t# device and dmabuf fd. Let's make sure that is available for our userspace.\n 18:\t\tselect KCMP\n 19:\t\tselect VIDEO\n 20:\t\thelp\n 21:\t\t Kernel-level support for the Direct Rendering Infrastructure (DRI)\n 22:\t\t introduced in XFree86 4.0. If you say Y here, you need to select\n 23:\t\t the module that's right for your graphics card from the list below.\n 24:\t\t These modules provide support for synchronization, security, and\n 25:\t\t DMA transfers. Please see \u003chttp://dri.sourceforge.net/\u003e for more\n 26:\t\t details. You should also select and configure AGP\n 27:\t\t (/dev/agpgart) support if it is available for your platform.\n 28:\t\n 29:\tmenu \"DRM debugging options\"\n"
} |
| 33/2 | 2026/10/09 21:08 | llm | patch-evaluator |
2mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 40978 output: 228 thoughts: 1533 Thoughts: **Analysis of DRM RAS Telemetry for Fuzzing** Okay, let's break this down for fuzzing purposes. First, I need to confirm the configurations. It looks like we need `CONFIG_NET`, which is standard, and then `CONFIG_DRM` and `CONFIG_DRM_RAS`. The dependency is `DRM_RAS` depends on `DRM` and `NET`, so that makes sense. We *might* need `CONFIG_DRM_ACCEL_QAIC` if we want to include that path, but for now, I'll keep that optional. After checking the code, it looks like `DRM_RAS`, `DRM`, and `NET` will definitely give us reachability. Now, let's get into the heart of the matter: identifying those *FocusSymbols* as the instructions require for effective fuzzing. We're looking for code that specifically implements logic that's been added or changed, or the direct entry points for a feature that's under review. The primary code we are targetting is the generic netlink handler in `drivers/gpu/drm/drm_ras.c` (reachable with `CONFIG_DRM_RAS`, `CONFIG_DRM`, `CONFIG_NET`), which is exposed through the userspace Netlink interface. Looking at the code, these functions seem like the most relevant: * `drm_ras_nl_pre_telemetry` * `drm_ras_nl_telemetry_read_doit` * `drm_ras_nl_telemetry_write_doit` * `drm_ras_node_register` (This one's been modified to support the new `DRM_RAS_NODE_TYPE_TELEMETRY`) Also, `drm_ras_nl_list_nodes_dumpit` (has modified flags in `drm_ras_nl_ops`) I'm also considering the QAIC telemetry functions, in case that path is somehow accessible, but it seems like we *need* a physical PCIe card for this to work, which is not what we're after, so that is not needed for this exercise. Now, I'm checking if those chosen symbols are actually accessible. The relevant makefile shows that the core of the code is built into `drm.ko` when `CONFIG_DRM=y` and `drm_ras.o` and `drm_ras_nl.o` are part of `drm.ko` when `CONFIG_DRM_RAS` is enabled. Since we're enabling both, they will be part of the build. They are non-static functions declared in `drivers/gpu/drm/drm_ras_nl.h` and implemented in `drivers/gpu/drm/drm_ras.c`. That's good, they're distinct symbols. Let's do a final sanity check, pulling everything together: 1. **WorthFuzzing:** *True*. The patch exposes telemetry support through Generic Netlink. It seems reachable in virtualized environments, which is exactly what we need. It looks like it is immediately vulnerable to a NULL pointer dereference, too. We should take it with a focus on that vulnerability. 2. **EnableConfigs:** We need `DRM_RAS`, `DRM`, and `NET`. 3. **FocusSymbols:** I've identified `drm_ras_nl_pre_telemetry`, `drm_ras_nl_telemetry_read_doit`, `drm_ras_nl_telemetry_write_doit`, and `drm_ras_node_register`. This seems spot-on. I'm ready to move forward and call `set-results`. |
| 34/2 | 2026/10/09 21:11 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"DRM_RAS",
"DRM",
"NET"
],
"FocusSymbols": [
"drm_ras_nl_pre_telemetry",
"drm_ras_nl_telemetry_read_doit",
"drm_ras_nl_telemetry_write_doit",
"drm_ras_node_register"
],
"Reasoning": "The patch expands the DRM RAS Generic Netlink subsystem with new telemetry operations (TELEMETRY_READ and TELEMETRY_WRITE) and removes GENL_ADMIN_PERM from node listing. The Generic Netlink family 'drm_ras' is registered during DRM core initialization and can be queried from unprivileged userspace over standard AF_NETLINK sockets without physical hardware. The newly added Netlink pre-handler drm_ras_nl_pre_telemetry() directly dereferences the looked-up node pointer without validating that it is non-NULL, introducing an easily triggered NULL pointer dereference when querying an invalid node ID.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"DRM_RAS",
"DRM",
"NET"
],
"FocusSymbols": [
"drm_ras_nl_pre_telemetry",
"drm_ras_nl_telemetry_read_doit",
"drm_ras_nl_telemetry_write_doit",
"drm_ras_node_register"
],
"Reasoning": "The patch expands the DRM RAS Generic Netlink subsystem with new telemetry operations (TELEMETRY_READ and TELEMETRY_WRITE) and removes GENL_ADMIN_PERM from node listing. The Generic Netlink family 'drm_ras' is registered during DRM core initialization and can be queried from unprivileged userspace over standard AF_NETLINK sockets without physical hardware. The newly added Netlink pre-handler drm_ras_nl_pre_telemetry() directly dereferences the looked-up node pointer without validating that it is non-NULL, introducing an easily triggered NULL pointer dereference when querying an invalid node ID.",
"WorthFuzzing": true
} |
| 35/1 | 2026/10/09 21:11 | action | If |
4mArgs:{
"WorthFuzzing": true
}Results: null |
| 36/2 | 2026/10/09 21:11 | agent | kmsan-evaluator |
4mModel:core Args: null Results: {
"KMSANReasoning": "The patch series introduces telemetry operations to the DRM RAS subsystem and implements telemetry support in the Qualcomm Cloud AI (qaic) accelerator driver.\n\nAnalysis of memory initialization and user-space boundaries:\n1. User-space data exposure: The new netlink commands (DRM_RAS_CMD_TELEMETRY_READ and DRM_RAS_CMD_TELEMETRY_WRITE) return scalar primitive values ('status' as s32 via nla_put_s32 and 'value' as u64 via nla_put_64bit with DRM_RAS_A_TELEMETRY_ATTRS_PAD). In successful telemetry execution paths, both 'status' and 'value' are explicitly assigned before being passed to nla_put_*; on error, the netlink message is aborted and freed, so no uninitialized stack data is emitted. Furthermore, 64-bit netlink padding is zeroed by the netlink subsystem (skb_put_zero in __nla_reserve_nohdr).\n2. Heap and stack structures: In qaic_telemetry.c, messages (wrapper_msg, telemetry_msg) and drm_ras_node are allocated with zeroing allocators (kzalloc_obj). The stack structure xfer_queue_elem has all its used fields (seq_num, buf, xfer_done, list) initialized before submission. The resp_work struct allocated in the DL transfer callback has all its fields explicitly initialized.\n3. Potential bug surfaces: The patch introduces risks of NULL pointer dereferences (e.g. missing checks for Netlink attributes in drm_ras_nl_pre_telemetry and drm_ras_nl_telemetry_write_doit, unchecked xa_load results prior to dereferencing node-\u003etelemetry_pre, checking node-\u003etelemetry_read instead of node-\u003etelemetry_write in telemetry_write_doit) and a memory leak in qaic_drm_ras_register_node error handling. These bugs manifest as general kernel crashes (oops/page fault) or memory leaks, which are handled by standard KASAN/LOCKDEP/kmemleak testing and do not involve uninitialized memory access.\n\nTherefore, dedicated KMSAN fuzzing is not required.",
"NeedsKMSAN": false
}Instruction: You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.
CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
a comprehensive suite of debugging tools and sanitizers, including KASAN
(out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
(locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
or page allocations) and kernel-to-user memory info-leaks.
Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.
Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.
Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
attributes, ioctl output arguments, socket options, or BPF buffers) where fields
or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
uninitialized bytes of existing buffers.
Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
dereferences, locking deadlocks, or use-after-free bugs (these are already caught
by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.
Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit fcad46b922e1e426dbe5607ebd256b48f44fe6e0
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Oct 9 21:03:30 2026 +0000
syz-cluster: applied patch under review
diff --git a/Documentation/netlink/specs/drm_ras.yaml b/Documentation/netlink/specs/drm_ras.yaml
index 4c2ac9a1ba3f5..d558eb1cba3f1 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:
@@ -112,7 +137,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:
@@ -199,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/accel/qaic/Makefile b/drivers/accel/qaic/Makefile
index 71f727b74da3b..b2bbe9b3e0774 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 02ca99000e2ae..f5dbeadba658e 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 c1bb6db78d129..d2aed51052a66 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 0000000000000..dbfb36cf319ce
--- /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 0000000000000..a80922591e37c
--- /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/drivers/gpu/drm/drm_ras.c b/drivers/gpu/drm/drm_ras.c
index b099eb67836e7..b88293295cda9 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 c9f5e0ceb3b5f..4577bc1a333e5 100644
--- a/drivers/gpu/drm/drm_ras_nl.c
+++ b/drivers/gpu/drm/drm_ras_nl.c
@@ -41,12 +41,25 @@ 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[] = {
{
.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,
@@ -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 9aef097ea10f3..4bdd7ed268a1b 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 1765a3130b09b..2a2cdd44839de 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 2f832275ee6ef..d39c78a9ed5a7 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)
diff --git a/include/uapi/drm/qaic_accel.h b/include/uapi/drm/qaic_accel.h
index c92d0309d583b..5aac051014267 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.
|
| 37/3 | 2026/10/09 21:11 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 16086 output: 48 thoughts: 2293 Thoughts: **KMSAN Evaluation: Telemetry Node Implementation** Okay, let's break this down. My task is to determine if this patch series, focusing on adding telemetry node support to `drm_ras` and driver-specific implementations in `qaic`, warrants a KMSAN fuzzing session in addition to the standard KASAN setup. I need to see if there are any risks of uninitialized memory usage or potential info leaks. I've reviewed the rules: KMSAN is only needed if KASAN, LOCKDEP, or other standard detectors *won't* catch a bug. I need to look for cases where kernel data structures are sent to userspace and might contain uninitialized fields, or if logic depends on uninitialized variables. Now, let's dig into the code. First, the core of the patch is the introduction of the new netlink interface, `drm_ras_nl_pre_telemetry`, and the addition of `read` and `write` functions associated with netlink messages: `drm_ras_nl_telemetry_read_doit` and `drm_ras_nl_telemetry_write_doit`. These new functions in the `drm_ras` netlink implementation are the targets of inspection. I've examined the `drm_ras_nl_telemetry_read_doit` function. It retrieves `value` and `status` from a `node->telemetry_read` call. If `telemetry_read` returns a non-zero value, meaning there was an error, the function just frees the netlink message and returns, and *never* sends potentially uninitialized data to the user. But if `telemetry_read` returns 0, the code puts `value` and `status` in the netlink message. Looking at `qaic_telemetry_read`, it calls `telemetry_request`, assigning the result to status. The `telemetry_request` in turn reads data via `le64_to_cpu(resp->data.val)`. It looks like if `telemetry_request` returns non-zero, the *result* of `telemetry_read` is non-zero, which is consistent. If it returns zero, `value` is assigned and put in the message. The same logic is used in `drm_ras_nl_telemetry_write_doit`. Crucially, in `drm_ras_nl_telemetry_read_doit`, the `status` and `value` variables are *always* initialized within the `qaic_telemetry_read` calls, so there shouldn't be any uninitialized reads. And the netlink attribute `nla_put_64bit` is used to send this value. Now, what about the write path? In the `drm_ras_nl_telemetry_write_doit` function, there's a serious bug: the code checks `node->telemetry_read`, even though it should check `node->telemetry_write`. It's a bug that potentially results in bad side effects. But what is sent back via the message? It's just status. The main concern then is the use of `nla_put_64bit` in the netlink write path. Does that function zero the padding? I'll need to check the kernel source of `nla_put_64bit`. However, the main issue is the logic bug with calling `telemetry_read` in a `write` context. This will result in an unhandled error, and KASAN should handle this logic bug. I think I need to check the `nla_put_64bit` code to make sure there are no issues. However, the existing logic checks mean that KASAN should handle it. **Conclusion:** Given that, the patch doesn't *directly* introduce a significant risk of uninitialized memory usage *that KMSAN would catch*. While there's a logic bug that *should* be caught by KASAN. So, I will set `NeedsKMSAN=false`. |
| 38/3 | 2026/10/09 21:11 | tool | grepper |
0mError:no matches Args: {
"Expression": "def.*nla_put_64bit",
"PathPrefixes": [
"include/net/netlink.h",
"lib/nlattr.c"
]
}Results: {
"error": "no matches"
} |
| 39/3 | 2026/10/09 21:11 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 19314 output: 46 thoughts: 16 Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind the expert user: **Hunting Down `nla_put_64bit`** Alright, so the first thing I need to do is track down the definition of `nla_put_64bit`. I'm likely needing to understand how a 64-bit attribute is inserted into the Netlink message. My instinct tells me this is probably in the Netlink header files, or maybe kernel documentation. Let's start with a quick search of my working directory's headers; I'll probably want to know its location and purpose to fully comprehend a piece of code I'm currently looking at. I'll need to know its parameters, how it handles potential errors, and what the overall Netlink message construction process looks like in relation to this function. Maybe I'll also want to know which other similar functions exist for different data types. |
| 40/3 | 2026/10/09 21:11 | tool | grepper |
0mArgs:{
"Expression": "nla_put_64bit",
"PathPrefixes": [
"include/net/netlink.h",
"lib/nlattr.c"
]
}Results: {
"Output": "include/net/netlink.h=562=void __nla_put(struct sk_buff *skb, int attrtype, int attrlen,\ninclude/net/netlink.h-563-\t const void *data);\ninclude/net/netlink.h:564:void __nla_put_64bit(struct sk_buff *skb, int attrtype, int attrlen,\ninclude/net/netlink.h-565-\t\t const void *data, int padattr);\n--\ninclude/net/netlink.h=567=int nla_put(struct sk_buff *skb, int attrtype, int attrlen, const void *data);\ninclude/net/netlink.h:568:int nla_put_64bit(struct sk_buff *skb, int attrtype, int attrlen,\ninclude/net/netlink.h-569-\t\t const void *data, int padattr);\n--\ninclude/net/netlink.h=1524=static inline int nla_put_u64_64bit(struct sk_buff *skb, int attrtype,\n--\ninclude/net/netlink.h-1528-\ninclude/net/netlink.h:1529:\treturn nla_put_64bit(skb, attrtype, sizeof(u64), \u0026tmp, padattr);\ninclude/net/netlink.h-1530-}\n--\ninclude/net/netlink.h=1539=static inline int nla_put_be64(struct sk_buff *skb, int attrtype, __be64 value,\n--\ninclude/net/netlink.h-1543-\ninclude/net/netlink.h:1544:\treturn nla_put_64bit(skb, attrtype, sizeof(__be64), \u0026tmp, padattr);\ninclude/net/netlink.h-1545-}\n--\ninclude/net/netlink.h=1570=static inline int nla_put_le64(struct sk_buff *skb, int attrtype, __le64 value,\n--\ninclude/net/netlink.h-1574-\ninclude/net/netlink.h:1575:\treturn nla_put_64bit(skb, attrtype, sizeof(__le64), \u0026tmp, padattr);\ninclude/net/netlink.h-1576-}\n--\ninclude/net/netlink.h=1624=static inline int nla_put_s64(struct sk_buff *skb, int attrtype, s64 value,\n--\ninclude/net/netlink.h-1628-\ninclude/net/netlink.h:1629:\treturn nla_put_64bit(skb, attrtype, sizeof(s64), \u0026tmp, padattr);\ninclude/net/netlink.h-1630-}\n--\ninclude/net/netlink.h=1677=static inline int nla_put_msecs(struct sk_buff *skb, int attrtype,\n--\ninclude/net/netlink.h-1681-\ninclude/net/netlink.h:1682:\treturn nla_put_64bit(skb, attrtype, sizeof(u64), \u0026tmp, padattr);\ninclude/net/netlink.h-1683-}\n--\nlib/nlattr.c=1043=EXPORT_SYMBOL(__nla_put);\n--\nlib/nlattr.c-1045-/**\nlib/nlattr.c:1046: * __nla_put_64bit - Add a netlink attribute to a socket buffer and align it\nlib/nlattr.c-1047- * @skb: socket buffer to add attribute to\n--\nlib/nlattr.c-1055- */\nlib/nlattr.c:1056:void __nla_put_64bit(struct sk_buff *skb, int attrtype, int attrlen,\nlib/nlattr.c-1057-\t\t const void *data, int padattr)\n--\nlib/nlattr.c-1063-}\nlib/nlattr.c:1064:EXPORT_SYMBOL(__nla_put_64bit);\nlib/nlattr.c-1065-\n--\nlib/nlattr.c=1102=EXPORT_SYMBOL(nla_put);\n--\nlib/nlattr.c-1104-/**\nlib/nlattr.c:1105: * nla_put_64bit - Add a netlink attribute to a socket buffer and align it\nlib/nlattr.c-1106- * @skb: socket buffer to add attribute to\n--\nlib/nlattr.c-1114- */\nlib/nlattr.c:1115:int nla_put_64bit(struct sk_buff *skb, int attrtype, int attrlen,\nlib/nlattr.c-1116-\t\t const void *data, int padattr)\n--\nlib/nlattr.c-1126-\nlib/nlattr.c:1127:\t__nla_put_64bit(skb, attrtype, attrlen, data, padattr);\nlib/nlattr.c-1128-\treturn 0;\nlib/nlattr.c-1129-}\nlib/nlattr.c:1130:EXPORT_SYMBOL(nla_put_64bit);\nlib/nlattr.c-1131-\n"
} |
| 41/3 | 2026/10/09 21:11 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 20956 output: 35 thoughts: 35 Thoughts: Here's my take on the thought process: **Diving into the Kernel's Netlink Attribute Handling** Okay, so I need to understand how 64-bit values are handled within the Netlink attribute system. Specifically, I need to see what's happening within the `__nla_put_64bit` function. Let me check the source code, specifically `lib/nlattr.c`, around line 1056. I figure that's the best place to start. Let's see how they actually put a 64-bit attribute into the attribute structure. I'm hoping to understand the low-level details of memory alignment, data packing, and any potential endianness considerations. That will give me a better understanding of how the attribute library works. |
| 42/3 | 2026/10/09 21:12 | tool | read-file |
0mArgs:{
"File": "lib/nlattr.c",
"FirstLine": 1050,
"LineCount": 25
}Results: {
"Contents": "1050:\t * @data: head of attribute payload\n1051:\t * @padattr: attribute type for the padding\n1052:\t *\n1053:\t * The caller is responsible to ensure that the skb provides enough\n1054:\t * tailroom for the attribute header and payload.\n1055:\t */\n1056:\tvoid __nla_put_64bit(struct sk_buff *skb, int attrtype, int attrlen,\n1057:\t\t\t const void *data, int padattr)\n1058:\t{\n1059:\t\tstruct nlattr *nla;\n1060:\t\n1061:\t\tnla = __nla_reserve_64bit(skb, attrtype, attrlen, padattr);\n1062:\t\tmemcpy(nla_data(nla), data, attrlen);\n1063:\t}\n1064:\tEXPORT_SYMBOL(__nla_put_64bit);\n1065:\t\n1066:\t/**\n1067:\t * __nla_put_nohdr - Add a netlink attribute without header\n1068:\t * @skb: socket buffer to add attribute to\n1069:\t * @attrlen: length of attribute payload\n1070:\t * @data: head of attribute payload\n1071:\t *\n1072:\t * The caller is responsible to ensure that the skb provides enough\n1073:\t * tailroom for the attribute payload.\n1074:\t */\n"
} |
| 43/3 | 2026/10/09 21:12 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 21593 output: 36 thoughts: 18 Thoughts: Okay, here's my summary, presented as a stream of thought: **Diving into `__nla_reserve_64bit`** Alright, let's get down to brass tacks. I'm taking a look at this `__nla_reserve_64bit` function. My expert eye immediately registers that "nla" strongly suggests it's related to Netlink attributes, likely in the Linux kernel space. Given the name, "reserve_64bit," I'm betting this function is designed to allocate or pre-allocate a chunk of space within a Netlink attribute structure. And the "64bit" part? That’s almost certainly hinting at the size of the reservation. Likely, we're talking about reserving space for a 64-bit value, perhaps an integer or a pointer, within the Netlink attribute's data payload. I need to understand the implications of this reservation. What happens if the reservation fails? Does it return an error code? Or maybe a smaller block? Is it safe? Does it handle alignment issues? Time to dig in! |
| 44/3 | 2026/10/09 21:12 | tool | grepper |
0mArgs:{
"Expression": "__nla_reserve_64bit",
"PathPrefixes": [
"lib/nlattr.c"
]
}Results: {
"Output": "lib/nlattr.c=914=EXPORT_SYMBOL(__nla_reserve);\n--\nlib/nlattr.c-916-/**\nlib/nlattr.c:917: * __nla_reserve_64bit - reserve room for attribute on the skb and align it\nlib/nlattr.c-918- * @skb: socket buffer to reserve room on\n--\nlib/nlattr.c-929- */\nlib/nlattr.c:930:struct nlattr *__nla_reserve_64bit(struct sk_buff *skb, int attrtype,\nlib/nlattr.c-931-\t\t\t\t int attrlen, int padattr)\n--\nlib/nlattr.c-936-}\nlib/nlattr.c:937:EXPORT_SYMBOL(__nla_reserve_64bit);\nlib/nlattr.c-938-\n--\nlib/nlattr.c=990=struct nlattr *nla_reserve_64bit(struct sk_buff *skb, int attrtype, int attrlen,\n--\nlib/nlattr.c-1001-\nlib/nlattr.c:1002:\treturn __nla_reserve_64bit(skb, attrtype, attrlen, padattr);\nlib/nlattr.c-1003-}\n--\nlib/nlattr.c=1056=void __nla_put_64bit(struct sk_buff *skb, int attrtype, int attrlen,\n--\nlib/nlattr.c-1060-\nlib/nlattr.c:1061:\tnla = __nla_reserve_64bit(skb, attrtype, attrlen, padattr);\nlib/nlattr.c-1062-\tmemcpy(nla_data(nla), data, attrlen);\n"
} |
| 45/3 | 2026/10/09 21:12 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 22344 output: 34 thoughts: 19 Thoughts: Here's a summary of my thought process, expert to expert: **Diving into `lib/nlattr.c`** Alright, let's get down to brass tacks. I'm thinking we need to delve into `lib/nlattr.c`, specifically around line 930. The context here is likely where the attribute handling logic is happening, and if we're dealing with something tricky, that's a prime location to start digging. This probably means we're dealing with either attribute parsing, validation, or manipulation. I expect to find some critical data structures or helper functions in that area. Let's see what interesting tidbits lie in wait. |
| 46/3 | 2026/10/09 21:12 | tool | read-file |
0mArgs:{
"File": "lib/nlattr.c",
"FirstLine": 925,
"LineCount": 40
}Results: {
"Contents": " 925:\t * attribute will have a 64-bit aligned nla_data() area.\n 926:\t *\n 927:\t * The caller is responsible to ensure that the skb provides enough\n 928:\t * tailroom for the attribute header and payload.\n 929:\t */\n 930:\tstruct nlattr *__nla_reserve_64bit(struct sk_buff *skb, int attrtype,\n 931:\t\t\t\t\t int attrlen, int padattr)\n 932:\t{\n 933:\t\tnla_align_64bit(skb, padattr);\n 934:\t\n 935:\t\treturn __nla_reserve(skb, attrtype, attrlen);\n 936:\t}\n 937:\tEXPORT_SYMBOL(__nla_reserve_64bit);\n 938:\t\n 939:\t/**\n 940:\t * __nla_reserve_nohdr - reserve room for attribute without header\n 941:\t * @skb: socket buffer to reserve room on\n 942:\t * @attrlen: length of attribute payload\n 943:\t *\n 944:\t * Reserves room for attribute payload without a header.\n 945:\t *\n 946:\t * The caller is responsible to ensure that the skb provides enough\n 947:\t * tailroom for the payload.\n 948:\t */\n 949:\tvoid *__nla_reserve_nohdr(struct sk_buff *skb, int attrlen)\n 950:\t{\n 951:\t\treturn skb_put_zero(skb, NLA_ALIGN(attrlen));\n 952:\t}\n 953:\tEXPORT_SYMBOL(__nla_reserve_nohdr);\n 954:\t\n 955:\t/**\n 956:\t * nla_reserve - reserve room for attribute on the skb\n 957:\t * @skb: socket buffer to reserve room on\n 958:\t * @attrtype: attribute type\n 959:\t * @attrlen: length of attribute payload\n 960:\t *\n 961:\t * Adds a netlink attribute header to a socket buffer and reserves\n 962:\t * room for the payload but does not copy it.\n 963:\t *\n 964:\t * Returns NULL if the tailroom of the skb is insufficient to store\n"
} |
| 47/3 | 2026/10/09 21:12 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 23167 output: 46 thoughts: 16 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience and presented in the first person: **Analyzing `nla_align_64bit`** Alright, let's dive into `nla_align_64bit`. My initial thought is that we're dealing with something related to aligning Netlink attributes, and the "64bit" bit strongly suggests this is specifically addressing issues around 64-bit architectures, and possibly alignment concerns that arise with those architectures. I'm immediately thinking about potential padding, and how this macro or function likely manages the necessary space between attribute fields to ensure proper data access. Perhaps it's dealing with cache line alignment for performance considerations? Or maybe ensuring that certain attribute types, like pointers or larger numeric fields, are properly aligned for atomic operations? The name's fairly self-explanatory, but I need to look deeper to see the actual implementation and usage context to understand *how* it's achieving alignment and what potential trade-offs are involved. I'm wondering if this is a platform-specific adjustment, or if it has to be defined with conditional compilation. Let's see how it's used within the Netlink framework. |
| 48/3 | 2026/10/09 21:12 | tool | grepper |
0mArgs:{
"Expression": "nla_align_64bit",
"PathPrefixes": [
"lib/nlattr.c",
"include/net/netlink.h"
]
}Results: {
"Output": "include/net/netlink.h=2364=static inline bool nla_need_padding_for_64bit(struct sk_buff *skb)\n--\ninclude/net/netlink.h-2378-/**\ninclude/net/netlink.h:2379: * nla_align_64bit - 64-bit align the nla_data() of next attribute\ninclude/net/netlink.h-2380- * @skb: socket buffer the message is stored in\n--\ninclude/net/netlink.h-2389- */\ninclude/net/netlink.h:2390:static inline int nla_align_64bit(struct sk_buff *skb, int padattr)\ninclude/net/netlink.h-2391-{\n--\nlib/nlattr.c=930=struct nlattr *__nla_reserve_64bit(struct sk_buff *skb, int attrtype,\n--\nlib/nlattr.c-932-{\nlib/nlattr.c:933:\tnla_align_64bit(skb, padattr);\nlib/nlattr.c-934-\n"
} |
| 49/3 | 2026/10/09 21:12 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 23783 output: 37 thoughts: 25 Thoughts: Here's my summary, as though I'm reading and thinking about the code: **Diving into netlink.h** Alright, let's get down to brass tacks and take a look at `nla_align_64bit` within `include/net/netlink.h`. I'm curious to see how this macro handles alignment within the Netlink protocol. Given the "64bit" in its name, I'm already anticipating some memory layout considerations related to architectures with 64-bit word sizes. It's a key function for ensuring proper data structures are aligned within Netlink messages to guarantee efficient access and prevent performance penalties related to unaligned memory accesses. I'm going to look for any architecture-specific adjustments. |
| 50/3 | 2026/10/09 21:12 | tool | read-file |
0mArgs:{
"File": "include/net/netlink.h",
"FirstLine": 2385,
"LineCount": 25
}Results: {
"Contents": "2385:\t * This will only be done in architectures which do not have\n2386:\t * CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS defined.\n2387:\t *\n2388:\t * Returns: zero on success or a negative error code.\n2389:\t */\n2390:\tstatic inline int nla_align_64bit(struct sk_buff *skb, int padattr)\n2391:\t{\n2392:\t\tif (nla_need_padding_for_64bit(skb) \u0026\u0026\n2393:\t\t !nla_reserve(skb, padattr, 0))\n2394:\t\t\treturn -EMSGSIZE;\n2395:\t\n2396:\t\treturn 0;\n2397:\t}\n2398:\t\n2399:\t/**\n2400:\t * nla_total_size_64bit - total length of attribute including padding\n2401:\t * @payload: length of payload\n2402:\t */\n2403:\tstatic inline int nla_total_size_64bit(int payload)\n2404:\t{\n2405:\t\treturn NLA_ALIGN(nla_attr_size(payload))\n2406:\t#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS\n2407:\t\t\t+ NLA_ALIGN(nla_attr_size(0))\n2408:\t#endif\n2409:\t\t\t;\n"
} |
| 51/3 | 2026/10/09 21:12 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 24402 output: 41 thoughts: 9394 Thoughts: **Summary of my Deep Dive into the Code** Okay, here's the breakdown of my thought process after reviewing the provided code snippets. My initial focus was on understanding how `nla_reserve` interacts with the Netlink infrastructure. I then dove into `drm_ras_nl_telemetry_read_doit` and `drm_ras_nl_telemetry_write_doit`, which handle Netlink requests. I noticed a potential issue: in `drm_ras_nl_telemetry_read_doit`, the `value` and `status` variables are declared as local stack variables, *uninitialized*. I wanted to know where and how those variables are written to, and when. I examined `qaic_telemetry_read` and the underlying `telemetry_request` function. I realized that *both* `value` and `status` are assigned a value *before* being used in the Netlink response if the read command is working. If there is a read error, it jumps out, and that's fine. I then started thinking about the write command. While reviewing `drm_ras_nl_telemetry_write_doit`, I realized that the value attribute in the request is not being checked if it's there. So, a missing attribute in Netlink could lead to a NULL pointer dereference. The `node_id` can also lead to a NULL pointer dereference. So can `node->telemetry_read` being null, but that one is checked. I have to check all the NULL pointer cases. I checked documentation to confirm the expected behavior of the `telemetry_read` callback, ensuring it's supposed to initialize both `value` and `status`. I meticulously examined the `telemetry_request` function, particularly the read and write branches and their error handling. I realized that, in the write case, since value is passed by reference, the value written is not checked to be correct, and could lead to uninitialized memory being written. I meticulously examined all the other structures and functions that I could find, in particular, the `qaic_telemetry_pre` which has a security flaw! So, there are NULL pointer dereferences which are likely bugs caught with KASAN, and the missing check for the presence of the value. |
| 52/3 | 2026/10/09 21:13 | tool | grepper |
0mArgs:{
"Expression": "drm_ras_nl_.*doit",
"PathPrefixes": [
"drivers/gpu/drm/drm_ras.c"
]
}Results: {
"Output": "drivers/gpu/drm/drm_ras.c-103- * operation, fetching all counters from a specific node.\ndrivers/gpu/drm/drm_ras.c:104: * - drm_ras_nl_get_error_counter_doit(): Implements the GET_ERROR_COUNTER doit\ndrivers/gpu/drm/drm_ras.c-105- * operation, fetching a counter value from a specific node.\ndrivers/gpu/drm/drm_ras.c:106: * - drm_ras_nl_clear_error_counter_doit(): Implements the CLEAR_ERROR_COUNTER doit\ndrivers/gpu/drm/drm_ras.c-107- * operation, clearing a counter value from a specific node.\ndrivers/gpu/drm/drm_ras.c:108: * - drm_ras_nl_get_error_threshold_doit(): Implements the GET_ERROR_THRESHOLD doit\ndrivers/gpu/drm/drm_ras.c-109- * operation, fetching the error threshold of a specific counter.\ndrivers/gpu/drm/drm_ras.c:110: * - drm_ras_nl_set_error_threshold_doit(): Implements the SET_ERROR_THRESHOLD doit\ndrivers/gpu/drm/drm_ras.c-111- * operation, setting the error threshold of a specific counter.\n--\ndrivers/gpu/drm/drm_ras.c-113- * pre_doit operation, returning 0 if valid.\ndrivers/gpu/drm/drm_ras.c:114: * - drm_ras_nl_telemetry_read_doit(): Implements the TELEMETRY_READ doit operation,\ndrivers/gpu/drm/drm_ras.c-115- * invoking the driver's telemetry_read callback and returning the results.\ndrivers/gpu/drm/drm_ras.c:116: * - drm_ras_nl_telemetry_write_doit(): Implements the TELEMETRY_WRITE doit operation,\ndrivers/gpu/drm/drm_ras.c-117- * invoking the driver's telemetry_write callback and returning the results.\n--\ndrivers/gpu/drm/drm_ras.c=469=int drm_ras_nl_get_error_counter_dumpit(struct sk_buff *skb,\n--\ndrivers/gpu/drm/drm_ras.c-525-/**\ndrivers/gpu/drm/drm_ras.c:526: * drm_ras_nl_get_error_counter_doit() - Query an error counter of an node\ndrivers/gpu/drm/drm_ras.c-527- * @skb: Netlink message buffer\n--\ndrivers/gpu/drm/drm_ras.c-535- */\ndrivers/gpu/drm/drm_ras.c:536:int drm_ras_nl_get_error_counter_doit(struct sk_buff *skb,\ndrivers/gpu/drm/drm_ras.c-537-\t\t\t\t struct genl_info *info)\n--\ndrivers/gpu/drm/drm_ras.c-552-/**\ndrivers/gpu/drm/drm_ras.c:553: * drm_ras_nl_clear_error_counter_doit() - Clear an error counter of a node\ndrivers/gpu/drm/drm_ras.c-554- * @skb: Netlink message buffer\n--\ndrivers/gpu/drm/drm_ras.c-561- */\ndrivers/gpu/drm/drm_ras.c:562:int drm_ras_nl_clear_error_counter_doit(struct sk_buff *skb,\ndrivers/gpu/drm/drm_ras.c-563-\t\t\t\t\tstruct genl_info *info)\n--\ndrivers/gpu/drm/drm_ras.c-587-/**\ndrivers/gpu/drm/drm_ras.c:588: * drm_ras_nl_get_error_threshold_doit() - Query error threshold of a counter\ndrivers/gpu/drm/drm_ras.c-589- * @skb: Netlink message buffer\n--\ndrivers/gpu/drm/drm_ras.c-597- */\ndrivers/gpu/drm/drm_ras.c:598:int drm_ras_nl_get_error_threshold_doit(struct sk_buff *skb, struct genl_info *info)\ndrivers/gpu/drm/drm_ras.c-599-{\n--\ndrivers/gpu/drm/drm_ras.c-613-/**\ndrivers/gpu/drm/drm_ras.c:614: * drm_ras_nl_set_error_threshold_doit() - Set error threshold of a counter\ndrivers/gpu/drm/drm_ras.c-615- * @skb: Netlink message buffer\n--\ndrivers/gpu/drm/drm_ras.c-622- */\ndrivers/gpu/drm/drm_ras.c:623:int drm_ras_nl_set_error_threshold_doit(struct sk_buff *skb, struct genl_info *info)\ndrivers/gpu/drm/drm_ras.c-624-{\n--\ndrivers/gpu/drm/drm_ras.c=650=int drm_ras_nl_pre_telemetry(const struct genl_split_ops *ops, struct sk_buff *skb,\n--\ndrivers/gpu/drm/drm_ras.c-665-/**\ndrivers/gpu/drm/drm_ras.c:666: * drm_ras_nl_telemetry_read_doit() - Query a telemetry value\ndrivers/gpu/drm/drm_ras.c-667- * @skb: Netlink message buffer\n--\ndrivers/gpu/drm/drm_ras.c-674- */\ndrivers/gpu/drm/drm_ras.c:675:int drm_ras_nl_telemetry_read_doit(struct sk_buff *skb, struct genl_info *info)\ndrivers/gpu/drm/drm_ras.c-676-{\n--\ndrivers/gpu/drm/drm_ras.c-731-/**\ndrivers/gpu/drm/drm_ras.c:732: * drm_ras_nl_telemetry_write_doit() - Write a telemetry value\ndrivers/gpu/drm/drm_ras.c-733- * @skb: Netlink message buffer\n--\ndrivers/gpu/drm/drm_ras.c-740- */\ndrivers/gpu/drm/drm_ras.c:741:int drm_ras_nl_telemetry_write_doit(struct sk_buff *skb, struct genl_info *info)\ndrivers/gpu/drm/drm_ras.c-742-{\n"
} |
| 53/3 | 2026/10/09 21:13 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 35974 output: 39 thoughts: 46 Thoughts: Okay, let's dive into this. Here's how I'd approach understanding `drm_ras_nl_get_error_counter_doit`: **Analyzing `drm_ras_nl_get_error_counter_doit`** Alright, I need to understand this function. Let's start by looking directly at the code. I'm going to start by going to `drivers/gpu/drm/drm_ras.c` and zeroing in on roughly line 536, where this function definition should be. The goal is to see exactly what this `drm_ras_nl_get_error_counter_doit` function does, especially in regards to error counters. I'll be looking for things like: * **Input arguments:** What does it take as input? Is it passing in specific error counter information or handles? How are those handled? * **Data structures:** What data structures does it interact with? Are there specific error counter structures defined? How are these structures populated and accessed? * **Error handling:** Is there any error handling? Does it return error codes? What are those error codes and what do they represent? * **Netlink usage:** Since it's dealing with Netlink (`nl`), how does it handle Netlink messages? Is it a Netlink handler? How does it parse incoming data and format outgoing responses? Is this using the standard Netlink interface for the kernel's DRM subsystem? * **Hardware interaction:** I'll also be looking for any indications of direct hardware interaction, or interactions with the RAS (Reliability, Availability, and Serviceability) subsystem and its counters. I'll need to know whether it reads from MMIO directly, or has some abstract function layer on top of hardware access. Essentially, I want to understand the function's responsibility, how it interacts with the DRM subsystem, and how it handles retrieving and potentially returning error counter data via Netlink. Based on that information, I can then assess the implications on the larger context of the device and its RAS subsystem. |
| 54/3 | 2026/10/09 21:13 | tool | read-file |
0mArgs:{
"File": "drivers/gpu/drm/drm_ras.c",
"FirstLine": 536,
"LineCount": 40
}Results: {
"Contents": " 536:\tint drm_ras_nl_get_error_counter_doit(struct sk_buff *skb,\n 537:\t\t\t\t\t struct genl_info *info)\n 538:\t{\n 539:\t\tu32 node_id, error_id;\n 540:\t\n 541:\t\tif (!info-\u003eattrs ||\n 542:\t\t GENL_REQ_ATTR_CHECK(info, DRM_RAS_A_ERROR_COUNTER_ATTRS_NODE_ID) ||\n 543:\t\t GENL_REQ_ATTR_CHECK(info, DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_ID))\n 544:\t\t\treturn -EINVAL;\n 545:\t\n 546:\t\tnode_id = nla_get_u32(info-\u003eattrs[DRM_RAS_A_ERROR_COUNTER_ATTRS_NODE_ID]);\n 547:\t\terror_id = nla_get_u32(info-\u003eattrs[DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_ID]);\n 548:\t\n 549:\t\treturn doit_reply_value(info, node_id, error_id);\n 550:\t}\n 551:\t\n 552:\t/**\n 553:\t * drm_ras_nl_clear_error_counter_doit() - Clear an error counter of a node\n 554:\t * @skb: Netlink message buffer\n 555:\t * @info: Generic Netlink info containing attributes of the request\n 556:\t *\n 557:\t * Extracts the node ID and error ID from the netlink attributes and\n 558:\t * clears the current value.\n 559:\t *\n 560:\t * Return: 0 on success, or negative errno on failure.\n 561:\t */\n 562:\tint drm_ras_nl_clear_error_counter_doit(struct sk_buff *skb,\n 563:\t\t\t\t\t\tstruct genl_info *info)\n 564:\t{\n 565:\t\tstruct drm_ras_node *node;\n 566:\t\tu32 node_id, error_id;\n 567:\t\n 568:\t\tif (!info-\u003eattrs ||\n 569:\t\t GENL_REQ_ATTR_CHECK(info, DRM_RAS_A_ERROR_COUNTER_ATTRS_NODE_ID) ||\n 570:\t\t GENL_REQ_ATTR_CHECK(info, DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_ID))\n 571:\t\t\treturn -EINVAL;\n 572:\t\n 573:\t\tnode_id = nla_get_u32(info-\u003eattrs[DRM_RAS_A_ERROR_COUNTER_ATTRS_NODE_ID]);\n 574:\t\terror_id = nla_get_u32(info-\u003eattrs[DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_ID]);\n 575:\t\n"
} |
| 55/3 | 2026/10/09 21:13 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 37270 output: 19 thoughts: 809 Thoughts: **Analyzing Potential Null Pointer Dereference and Uninitialized Memory in Telemetry Code**
Okay, so I was looking at this telemetry code, and something jumped out at me. I noticed that `GENL_REQ_ATTR_CHECK` was being used for error counters, but it wasn't being used in the telemetry side of things. This means that if specific attributes are missing, `drm_ras_nl_pre_telemetry` and the `drm_ras_nl_telemetry_*_doit` functions could try to access `nla_get_u32(NULL)`, resulting in a crash due to a null pointer dereference. The good news is, this is being caught by the general kernel crash mechanisms or KASAN builds, so we're covered.
But then, I started thinking about `drm_ras_nl_telemetry_read_doit` and immediately thought about *uninitialized memory*! Let's take another look at the code:
```c
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);
}
```
My concern here is: what if `ret` from `node->telemetry_read(node, &field, &value, &status);` is 0 (success) but *either* `value` or `status` aren't initialized? The code doesn't explicitly initialize them before the call. This is definitely something to look into.
The next thing I'll do is check what `node->telemetry_read` actually *does*. I bet I can find the answer quickly since I know there's likely a limited set of implementations for this. *Only* `qaic_telemetry_read`! So that's the one to look at.
Now, let me just double-check that there aren't any other implementations lurking around somewhere...
|
| 56/3 | 2026/10/09 21:13 | tool | grepper |
0mError:no matches Args: {
"Expression": "\\.telemetry_read"
}Results: {
"error": "no matches"
} |
| 57/3 | 2026/10/09 21:13 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 39032 output: 18 thoughts: 23 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Expanding the Scope: Global Search for `telemetry_read`** Alright, given the context we're working with, my first impulse is to perform a global search. We need to understand the complete footprint of this `telemetry_read` function or identifier. I want to cast a wide net across the entire project's codebase – that's the whole tree. No point in being myopic here. The goal is to uncover every instance, every usage, and most critically, to identify all *other* files where this function or element plays a part. This broader view is critical. We must understand it in the context of the entire codebase and associated components. This is not just about finding the exact definition or implementation. It's about revealing how it's used, where it is used, and which other components are in play. I am looking for a complete picture. |
| 58/3 | 2026/10/09 21:13 | tool | grepper |
0mArgs:{
"Expression": "telemetry_read"
}Results: {
"Output": "arch/x86/include/asm/intel_telemetry.h=84=int telemetry_get_evtname(enum telemetry_unit telem_unit,\n--\narch/x86/include/asm/intel_telemetry.h-86-\narch/x86/include/asm/intel_telemetry.h:87:int telemetry_read_events(enum telemetry_unit telem_unit,\narch/x86/include/asm/intel_telemetry.h-88-\t\t\t struct telemetry_evtlog *evtlog, int len);\narch/x86/include/asm/intel_telemetry.h-89-\narch/x86/include/asm/intel_telemetry.h:90:int telemetry_read_eventlog(enum telemetry_unit telem_unit,\narch/x86/include/asm/intel_telemetry.h-91-\t\t\t struct telemetry_evtlog *evtlog, int len);\n--\ndrivers/accel/qaic/qaic_telemetry.c=373=static int qaic_telemetry_pre(struct drm_ras_node *ep)\n--\ndrivers/accel/qaic/qaic_telemetry.c-392-\ndrivers/accel/qaic/qaic_telemetry.c:393:static int qaic_telemetry_read(struct drm_ras_node *ep, u32 *field, u64 *value, s32 *status)\ndrivers/accel/qaic/qaic_telemetry.c-394-{\n--\ndrivers/accel/qaic/qaic_telemetry.c=411=int qaic_drm_ras_register_node(struct qaic_device *qdev)\n--\ndrivers/accel/qaic/qaic_telemetry.c-440-\tnode-\u003epriv = qdev;\ndrivers/accel/qaic/qaic_telemetry.c:441:\tnode-\u003etelemetry_read = qaic_telemetry_read;\ndrivers/accel/qaic/qaic_telemetry.c-442-\tnode-\u003etelemetry_write = qaic_telemetry_write;\n--\ndrivers/gpu/drm/drm_ras.c-92- * - TELEMETRY:\ndrivers/gpu/drm/drm_ras.c:93: * + The driver must implement the telemetry_read() and telemetry_write()\ndrivers/gpu/drm/drm_ras.c-94- * callbacks.\n--\ndrivers/gpu/drm/drm_ras.c-113- * pre_doit operation, returning 0 if valid.\ndrivers/gpu/drm/drm_ras.c:114: * - drm_ras_nl_telemetry_read_doit(): Implements the TELEMETRY_READ doit operation,\ndrivers/gpu/drm/drm_ras.c:115: * invoking the driver's telemetry_read callback and returning the results.\ndrivers/gpu/drm/drm_ras.c-116- * - drm_ras_nl_telemetry_write_doit(): Implements the TELEMETRY_WRITE doit operation,\n--\ndrivers/gpu/drm/drm_ras.c=650=int drm_ras_nl_pre_telemetry(const struct genl_split_ops *ops, struct sk_buff *skb,\n--\ndrivers/gpu/drm/drm_ras.c-665-/**\ndrivers/gpu/drm/drm_ras.c:666: * drm_ras_nl_telemetry_read_doit() - Query a telemetry value\ndrivers/gpu/drm/drm_ras.c-667- * @skb: Netlink message buffer\n--\ndrivers/gpu/drm/drm_ras.c-674- */\ndrivers/gpu/drm/drm_ras.c:675:int drm_ras_nl_telemetry_read_doit(struct sk_buff *skb, struct genl_info *info)\ndrivers/gpu/drm/drm_ras.c-676-{\n--\ndrivers/gpu/drm/drm_ras.c-691-\tnode = xa_load(\u0026drm_ras_xa, node_id);\ndrivers/gpu/drm/drm_ras.c:692:\tif (!node || !node-\u003etelemetry_read)\ndrivers/gpu/drm/drm_ras.c-693-\t\treturn -ENOENT;\n--\ndrivers/gpu/drm/drm_ras.c-704-\ndrivers/gpu/drm/drm_ras.c:705:\tret = node-\u003etelemetry_read(node, \u0026field, \u0026value, \u0026status);\ndrivers/gpu/drm/drm_ras.c-706-\tif (ret) {\n--\ndrivers/gpu/drm/drm_ras.c=741=int drm_ras_nl_telemetry_write_doit(struct sk_buff *skb, struct genl_info *info)\n--\ndrivers/gpu/drm/drm_ras.c-758-\tnode = xa_load(\u0026drm_ras_xa, node_id);\ndrivers/gpu/drm/drm_ras.c:759:\tif (!node || !node-\u003etelemetry_read)\ndrivers/gpu/drm/drm_ras.c-760-\t\treturn -ENOENT;\n--\ndrivers/gpu/drm/drm_ras.c=799=int drm_ras_node_register(struct drm_ras_node *node)\n--\ndrivers/gpu/drm/drm_ras.c-812-\t\t/* Mandatory entries for Telemetry Node */\ndrivers/gpu/drm/drm_ras.c:813:\t\tif (!node-\u003etelemetry_read || !node-\u003etelemetry_write)\ndrivers/gpu/drm/drm_ras.c-814-\t\t\treturn -EINVAL;\n--\ndrivers/gpu/drm/drm_ras_nl.c=38=static const struct nla_policy drm_ras_set_error_threshold_nl_policy[DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_THRESHOLD + 1] = {\n--\ndrivers/gpu/drm/drm_ras_nl.c-44-/* DRM_RAS_CMD_TELEMETRY_READ - do */\ndrivers/gpu/drm/drm_ras_nl.c:45:static const struct nla_policy drm_ras_telemetry_read_nl_policy[DRM_RAS_A_TELEMETRY_ATTRS_FIELD + 1] = {\ndrivers/gpu/drm/drm_ras_nl.c-46-\t[DRM_RAS_A_TELEMETRY_ATTRS_NODE_ID] = { .type = NLA_U32, },\n--\ndrivers/gpu/drm/drm_ras_nl.c=58=static const struct genl_split_ops drm_ras_nl_ops[] = {\n--\ndrivers/gpu/drm/drm_ras_nl.c-101-\t\t.pre_doit\t= drm_ras_nl_pre_telemetry,\ndrivers/gpu/drm/drm_ras_nl.c:102:\t\t.doit\t\t= drm_ras_nl_telemetry_read_doit,\ndrivers/gpu/drm/drm_ras_nl.c:103:\t\t.policy\t\t= drm_ras_telemetry_read_nl_policy,\ndrivers/gpu/drm/drm_ras_nl.c-104-\t\t.maxattr\t= DRM_RAS_A_TELEMETRY_ATTRS_FIELD,\n--\ndrivers/gpu/drm/drm_ras_nl.h=28=int drm_ras_nl_set_error_threshold_doit(struct sk_buff *skb,\ndrivers/gpu/drm/drm_ras_nl.h-29-\t\t\t\t\tstruct genl_info *info);\ndrivers/gpu/drm/drm_ras_nl.h:30:int drm_ras_nl_telemetry_read_doit(struct sk_buff *skb, struct genl_info *info);\ndrivers/gpu/drm/drm_ras_nl.h-31-int drm_ras_nl_telemetry_write_doit(struct sk_buff *skb,\n--\ndrivers/platform/x86/intel/telemetry/core.c=51=static const struct telemetry_core_ops telm_defpltops = {\n--\ndrivers/platform/x86/intel/telemetry/core.c-58-/**\ndrivers/platform/x86/intel/telemetry/core.c:59: * telemetry_read_events() - Fetches samples as specified by evtlog.telem_evt_id\ndrivers/platform/x86/intel/telemetry/core.c-60- * @telem_unit: Specify whether IOSS or PSS Read\n--\ndrivers/platform/x86/intel/telemetry/core.c-66- */\ndrivers/platform/x86/intel/telemetry/core.c:67:int telemetry_read_events(enum telemetry_unit telem_unit,\ndrivers/platform/x86/intel/telemetry/core.c-68-\t\t\t struct telemetry_evtlog *evtlog, int len)\n--\ndrivers/platform/x86/intel/telemetry/core.c-72-}\ndrivers/platform/x86/intel/telemetry/core.c:73:EXPORT_SYMBOL_GPL(telemetry_read_events);\ndrivers/platform/x86/intel/telemetry/core.c-74-\ndrivers/platform/x86/intel/telemetry/core.c-75-/**\ndrivers/platform/x86/intel/telemetry/core.c:76: * telemetry_read_eventlog() - Fetch the Telemetry log from PSS or IOSS\ndrivers/platform/x86/intel/telemetry/core.c-77- * @telem_unit: Specify whether IOSS or PSS Read\n--\ndrivers/platform/x86/intel/telemetry/core.c-82- */\ndrivers/platform/x86/intel/telemetry/core.c:83:int telemetry_read_eventlog(enum telemetry_unit telem_unit,\ndrivers/platform/x86/intel/telemetry/core.c-84-\t\t\t struct telemetry_evtlog *evtlog, int len)\n--\ndrivers/platform/x86/intel/telemetry/core.c-88-}\ndrivers/platform/x86/intel/telemetry/core.c:89:EXPORT_SYMBOL_GPL(telemetry_read_eventlog);\ndrivers/platform/x86/intel/telemetry/core.c-90-\n--\ndrivers/platform/x86/intel/telemetry/debugfs.c=331=static int telem_pss_states_show(struct seq_file *s, void *unused)\n--\ndrivers/platform/x86/intel/telemetry/debugfs.c-343-\ndrivers/platform/x86/intel/telemetry/debugfs.c:344:\tret = telemetry_read_eventlog(TELEM_PSS, evtlog,\ndrivers/platform/x86/intel/telemetry/debugfs.c-345-\t\t\t\t TELEM_MAX_OS_ALLOCATED_EVENTS);\n--\ndrivers/platform/x86/intel/telemetry/debugfs.c=470=static int telem_ioss_states_show(struct seq_file *s, void *unused)\n--\ndrivers/platform/x86/intel/telemetry/debugfs.c-475-\ndrivers/platform/x86/intel/telemetry/debugfs.c:476:\tret = telemetry_read_eventlog(TELEM_IOSS, evtlog,\ndrivers/platform/x86/intel/telemetry/debugfs.c-477-\t\t\t\t TELEM_MAX_OS_ALLOCATED_EVENTS);\n--\ndrivers/platform/x86/intel/telemetry/debugfs.c=499=static int telem_soc_states_show(struct seq_file *s, void *unused)\n--\ndrivers/platform/x86/intel/telemetry/debugfs.c-511-\ndrivers/platform/x86/intel/telemetry/debugfs.c:512:\tret = telemetry_read_eventlog(TELEM_IOSS, evtlog,\ndrivers/platform/x86/intel/telemetry/debugfs.c-513-\t\t\t\t TELEM_MAX_OS_ALLOCATED_EVENTS);\n--\ndrivers/platform/x86/intel/telemetry/debugfs.c-614-\tevtlog-\u003etelem_evtid = conf-\u003epss_idle_id;\ndrivers/platform/x86/intel/telemetry/debugfs.c:615:\tret = telemetry_read_events(TELEM_PSS, evtlog, 1);\ndrivers/platform/x86/intel/telemetry/debugfs.c-616-\tif (ret \u003c 0)\n--\ninclude/drm/drm_ras.h=16=struct drm_ras_node {\n--\ninclude/drm/drm_ras.h-113-\t/**\ninclude/drm/drm_ras.h:114:\t * @telemetry_read:\ninclude/drm/drm_ras.h-115-\t *\n--\ninclude/drm/drm_ras.h-123-\t */\ninclude/drm/drm_ras.h:124:\tint (*telemetry_read)(struct drm_ras_node *ep, u32 *field, u64 *value,\ninclude/drm/drm_ras.h-125-\t\t\t s32 *status);\n"
} |
| 59/3 | 2026/10/09 21:13 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 42584 output: 39 thoughts: 2800 Thoughts: **Summary of the Telemetry and Netlink Interaction** Okay, so I've been diving deep into how the telemetry read and write functions work, focusing particularly on potential vulnerabilities and information leaks. My first pass looks at `qaic_telemetry_read` and `qaic_telemetry_write`, which call `telemetry_request`, which handles the actual MHI transfer. The `telemetry_request` function seems safe, especially with the use of `__packed` structs (`telemetry_msg`, `telemetry_data`, `telemetry_header`) and zero-initialized memory (`kzalloc_obj`). It looks like failure paths in `telemetry_request` correctly set a status and return it. Crucially, the read and write functions correctly propagate the status to userspace via Netlink. I've traced the path from `qaic_telemetry_read` through `drm_ras_nl_telemetry_read_doit`, confirming that if the `telemetry_request` succeeds, a status of 0 is passed along with the value read from the MHI device. The key here is where `value` comes from. It's read from the MHI device after it is transferred. This data is written into `resp`, which is DMA memory, and which, therefore, will need to be correctly initialized for the data to be valid. However, KMSAN isn't likely applicable for this hardware. I have been able to verify that the syzkaller fuzzer *can* send Netlink messages to these functions, and therefore needs to be considered. I've also confirmed that when a Netlink message is constructed, `nla_put_s32` and `nla_put_64bit` are used, but that these functions do not leak uninitialized memory. Padding in the Netlink messages is explicitly zeroed, specifically from `__nla_reserve_64bit`. Importantly, this patch also modifies the `LIST_NODES` command. The `GENL_ADMIN_PERM` flag has been removed, making it accessible to unprivileged users, but this does not appear to expose any new vulnerabilities. |
| 60/3 | 2026/10/09 21:14 | tool | grepper |
0mArgs:{
"Expression": "drm_ras_nl_list_nodes_dumpit",
"PathPrefixes": [
"drivers/gpu/drm/"
]
}Results: {
"Output": "drivers/gpu/drm/drm_ras.c-99- *\ndrivers/gpu/drm/drm_ras.c:100: * - drm_ras_nl_list_nodes_dumpit(): Implements the LIST_NODES\ndrivers/gpu/drm/drm_ras.c-101- * operation, iterating over the xarray.\n--\ndrivers/gpu/drm/drm_ras.c=125=struct drm_ras_ctx {\n--\ndrivers/gpu/drm/drm_ras.c-130-/**\ndrivers/gpu/drm/drm_ras.c:131: * drm_ras_nl_list_nodes_dumpit() - Dump all registered RAS nodes\ndrivers/gpu/drm/drm_ras.c-132- * @skb: Netlink message buffer\n--\ndrivers/gpu/drm/drm_ras.c-144- */\ndrivers/gpu/drm/drm_ras.c:145:int drm_ras_nl_list_nodes_dumpit(struct sk_buff *skb,\ndrivers/gpu/drm/drm_ras.c-146-\t\t\t\t struct netlink_callback *cb)\n--\ndrivers/gpu/drm/drm_ras_nl.c=58=static const struct genl_split_ops drm_ras_nl_ops[] = {\n--\ndrivers/gpu/drm/drm_ras_nl.c-60-\t\t.cmd\t= DRM_RAS_CMD_LIST_NODES,\ndrivers/gpu/drm/drm_ras_nl.c:61:\t\t.dumpit\t= drm_ras_nl_list_nodes_dumpit,\ndrivers/gpu/drm/drm_ras_nl.c-62-\t\t.flags\t= GENL_CMD_CAP_DUMP,\n--\ndrivers/gpu/drm/drm_ras_nl.h=15=int drm_ras_nl_pre_telemetry(const struct genl_split_ops *ops,\n--\ndrivers/gpu/drm/drm_ras_nl.h-17-\ndrivers/gpu/drm/drm_ras_nl.h:18:int drm_ras_nl_list_nodes_dumpit(struct sk_buff *skb,\ndrivers/gpu/drm/drm_ras_nl.h-19-\t\t\t\t struct netlink_callback *cb);\n"
} |
| 61/3 | 2026/10/09 21:14 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 46446 output: 39 thoughts: 39 Thoughts: Okay, let's dive into this. **Analyzing drm_ras_nl_list_nodes_dumpit in drivers/gpu/drm/drm_ras.c** Alright, I'm going to pull up `drm_ras_nl_list_nodes_dumpit`, specifically looking at the area around line 145 in `drivers/gpu/drm/drm_ras.c`. This suggests we're dealing with a Netlink function, likely involved in listing or dumping information related to Reliability, Availability, and Serviceability (RAS) nodes within the Direct Rendering Manager (DRM) subsystem. Given the naming convention and location, I'd guess this function is probably handling a Netlink message to retrieve and output data about the RAS infrastructure tied to the DRM drivers. I'm keen to see how the data is structured and how it's handled within the Netlink message framework, particularly the interaction between user space requests and the kernel's response. I expect we'll see memory allocation, data population, and potentially some error handling related to these RAS nodes. |
| 62/3 | 2026/10/09 21:14 | tool | read-file |
0mArgs:{
"File": "drivers/gpu/drm/drm_ras.c",
"FirstLine": 145,
"LineCount": 55
}Results: {
"Contents": " 145:\tint drm_ras_nl_list_nodes_dumpit(struct sk_buff *skb,\n 146:\t\t\t\t\t struct netlink_callback *cb)\n 147:\t{\n 148:\t\tconst struct genl_info *info = genl_info_dump(cb);\n 149:\t\tstruct drm_ras_ctx *ctx = (void *)cb-\u003ectx;\n 150:\t\tstruct drm_ras_node *node;\n 151:\t\tstruct nlattr *hdr;\n 152:\t\tunsigned long id;\n 153:\t\tint ret;\n 154:\t\n 155:\t\txa_for_each_start(\u0026drm_ras_xa, id, node, ctx-\u003erestart) {\n 156:\t\t\thdr = genlmsg_iput(skb, info);\n 157:\t\t\tif (!hdr) {\n 158:\t\t\t\tret = -EMSGSIZE;\n 159:\t\t\t\tbreak;\n 160:\t\t\t}\n 161:\t\n 162:\t\t\tret = nla_put_u32(skb, DRM_RAS_A_NODE_ATTRS_NODE_ID, node-\u003eid);\n 163:\t\t\tif (ret) {\n 164:\t\t\t\tgenlmsg_cancel(skb, hdr);\n 165:\t\t\t\tbreak;\n 166:\t\t\t}\n 167:\t\n 168:\t\t\tret = nla_put_string(skb, DRM_RAS_A_NODE_ATTRS_DEVICE_NAME,\n 169:\t\t\t\t\t node-\u003edevice_name);\n 170:\t\t\tif (ret) {\n 171:\t\t\t\tgenlmsg_cancel(skb, hdr);\n 172:\t\t\t\tbreak;\n 173:\t\t\t}\n 174:\t\n 175:\t\t\tret = nla_put_string(skb, DRM_RAS_A_NODE_ATTRS_NODE_NAME,\n 176:\t\t\t\t\t node-\u003enode_name);\n 177:\t\t\tif (ret) {\n 178:\t\t\t\tgenlmsg_cancel(skb, hdr);\n 179:\t\t\t\tbreak;\n 180:\t\t\t}\n 181:\t\n 182:\t\t\tret = nla_put_u32(skb, DRM_RAS_A_NODE_ATTRS_NODE_TYPE,\n 183:\t\t\t\t\t node-\u003etype);\n 184:\t\t\tif (ret) {\n 185:\t\t\t\tgenlmsg_cancel(skb, hdr);\n 186:\t\t\t\tbreak;\n 187:\t\t\t}\n 188:\t\n 189:\t\t\tgenlmsg_end(skb, hdr);\n 190:\t\t}\n 191:\t\n 192:\t\tif (ret == -EMSGSIZE)\n 193:\t\t\tctx-\u003erestart = id;\n 194:\t\n 195:\t\treturn ret;\n 196:\t}\n 197:\t\n 198:\tstatic int get_node_error_counter(u32 node_id, u32 error_id,\n 199:\t\t\t\t\t const char **name, u32 *value)\n"
} |
| 63/3 | 2026/10/09 21:14 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 47565 output: 35 thoughts: 594 Thoughts: **Uninitialized Variable in `drm_ras_nl_list_nodes_dumpit`?** Okay, let's break this down. I see that the code in `drm_ras_nl_list_nodes_dumpit` is populating some node information: `node->id`, `node->device_name`, `node->node_name`, and `node->type`. Specifically, in the related function `qaic_drm_ras_register_node`, I note that `device_name` and `node_name` are allocated using `kasprintf`, and `node->type` is set to `DRM_RAS_NODE_TYPE_TELEMETRY`. Everything seems initialized. But wait, hold on... what's going on with `ret` in `drm_ras_nl_list_nodes_dumpit`? I see it declared on line 153: `int ret;`. Now, here's the kicker: what happens if `drm_ras_xa` is empty? If the `drm_ras_xa` is empty, the loop `xa_for_each_start` wouldn't even execute once, right? And then, on line 192, we're using `ret` in `if (ret == -EMSGSIZE)`. That's bad. *It's being used uninitialized!* Hold on. Wait a minute! Now, did THIS patch introduce it? I should check the git diff. Okay, the diff is now visible, and I see the changes. It looks like the patch changed the `.flags` from `GENL_ADMIN_PERM | GENL_CMD_CAP_DUMP` to just `GENL_CMD_CAP_DUMP`. Okay, so did that patch *introduce* it? Wait, was `drm_ras_nl_list_nodes_dumpit` even changed in this patch? To check, I'll review the git log for that file. Okay, I see that the function `drm_ras_nl_list_nodes_dumpit` was already written. Thus, the uninitialized variable must have been there before this patch. |
| 64/3 | 2026/10/09 21:14 | tool | grepper |
0mArgs:{
"Expression": "int ret;",
"PathPrefixes": [
"drivers/gpu/drm/drm_ras.c"
]
}Results: {
"Output": "drivers/gpu/drm/drm_ras.c=145=int drm_ras_nl_list_nodes_dumpit(struct sk_buff *skb,\n--\ndrivers/gpu/drm/drm_ras.c-152-\tunsigned long id;\ndrivers/gpu/drm/drm_ras.c:153:\tint ret;\ndrivers/gpu/drm/drm_ras.c-154-\n--\ndrivers/gpu/drm/drm_ras.c=248=static int msg_reply_value(struct sk_buff *msg, u32 error_id,\n--\ndrivers/gpu/drm/drm_ras.c-250-{\ndrivers/gpu/drm/drm_ras.c:251:\tint ret;\ndrivers/gpu/drm/drm_ras.c-252-\n--\ndrivers/gpu/drm/drm_ras.c=266=static int msg_put_error_event_attrs(struct sk_buff *msg, struct drm_ras_node *node,\n--\ndrivers/gpu/drm/drm_ras.c-268-{\ndrivers/gpu/drm/drm_ras.c:269:\tint ret;\ndrivers/gpu/drm/drm_ras.c-270-\n--\ndrivers/gpu/drm/drm_ras.c=294=static int msg_reply_threshold(struct sk_buff *msg, u32 error_id, const char *error_name,\n--\ndrivers/gpu/drm/drm_ras.c-296-{\ndrivers/gpu/drm/drm_ras.c:297:\tint ret;\ndrivers/gpu/drm/drm_ras.c-298-\n--\ndrivers/gpu/drm/drm_ras.c=310=static int doit_reply_value(struct genl_info *info, u32 node_id,\n--\ndrivers/gpu/drm/drm_ras.c-316-\tu32 value;\ndrivers/gpu/drm/drm_ras.c:317:\tint ret;\ndrivers/gpu/drm/drm_ras.c-318-\n--\ndrivers/gpu/drm/drm_ras.c=349=static int doit_reply_threshold(struct genl_info *info, u32 node_id, u32 error_id)\n--\ndrivers/gpu/drm/drm_ras.c-354-\tu32 threshold;\ndrivers/gpu/drm/drm_ras.c:355:\tint ret;\ndrivers/gpu/drm/drm_ras.c-356-\n--\ndrivers/gpu/drm/drm_ras.c=401=int drm_ras_nl_error_event(struct drm_ras_node *node, u32 error_id, const char *error_name,\n--\ndrivers/gpu/drm/drm_ras.c-406-\tstruct nlattr *hdr;\ndrivers/gpu/drm/drm_ras.c:407:\tint ret;\ndrivers/gpu/drm/drm_ras.c-408-\n--\ndrivers/gpu/drm/drm_ras.c=469=int drm_ras_nl_get_error_counter_dumpit(struct sk_buff *skb,\n--\ndrivers/gpu/drm/drm_ras.c-477-\tu32 node_id, error_id, value;\ndrivers/gpu/drm/drm_ras.c:478:\tint ret;\ndrivers/gpu/drm/drm_ras.c-479-\n--\ndrivers/gpu/drm/drm_ras.c=675=int drm_ras_nl_telemetry_read_doit(struct sk_buff *skb, struct genl_info *info)\n--\ndrivers/gpu/drm/drm_ras.c-682-\tu64 value;\ndrivers/gpu/drm/drm_ras.c:683:\tint ret;\ndrivers/gpu/drm/drm_ras.c-684-\n--\ndrivers/gpu/drm/drm_ras.c=741=int drm_ras_nl_telemetry_write_doit(struct sk_buff *skb, struct genl_info *info)\n--\ndrivers/gpu/drm/drm_ras.c-748-\tu64 value;\ndrivers/gpu/drm/drm_ras.c:749:\tint ret;\ndrivers/gpu/drm/drm_ras.c-750-\n"
} |
| 65/3 | 2026/10/09 21:14 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 49750 output: 39 thoughts: 459 Thoughts: **Uninitialized Variable in Netlink Dump Function**
Okay, hold on a second! Let me focus here. I'm looking at `drm_ras_nl_list_nodes_dumpit` and something's immediately jumping out at me. Specifically, in this code block:
```c
int drm_ras_nl_list_nodes_dumpit(struct sk_buff *skb,
struct netlink_callback *cb)
{
const struct genl_info *info = genl_info_dump(cb);
struct drm_ras_ctx *ctx = (void *)cb->ctx;
struct drm_ras_node *node;
struct nlattr *hdr;
unsigned long id;
int ret;
xa_for_each_start(&drm_ras_xa, id, node, ctx->restart) {
...
}
if (ret == -EMSGSIZE)
ctx->restart = id;
return ret;
}
```
...Wait! See that `int ret;`? The code then proceeds to use `ret` without initializing it! If `drm_ras_xa` is empty, or if we're at the end of a dump, `xa_for_each_start` will effectively *never* execute, so `ret` will remain uninitialized. Then, it's used in `if (ret == -EMSGSIZE)` and eventually *returned* uninitialized. This is a classic bug that could lead to unpredictable behavior and potentially information leaks.
Now, before I dig further, I need to check the context. What was the command flag before this patch, specifically for `DRM_RAS_CMD_LIST_NODES`? Ah, I remember, it was `flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DUMP`! But now, it's changed to `flags = GENL_CMD_CAP_DUMP;`, which drops `GENL_ADMIN_PERM`. Does that matter? I need to think this through. Even *with* `GENL_ADMIN_PERM`, Syzkaller, given its typical setup as a root-privileged process inside its user namespace or a root-privileged VM, would still have effective admin permissions. So, that flag drop is likely not directly related to the bug.
I'm starting to think about whether this is a reachable function, and whether DRM-RAS is even compiled in the kernel I'm looking at. Let me check the call-sites, and make sure that it's actually used. Let me go over to line 469 and look at `drm_ras_nl_get_error_counter_dumpit`. I need to ensure it's not a dead code path!
|
| 66/3 | 2026/10/09 21:14 | tool | read-file |
0mArgs:{
"File": "drivers/gpu/drm/drm_ras.c",
"FirstLine": 469,
"LineCount": 45
}Results: {
"Contents": " 469:\tint drm_ras_nl_get_error_counter_dumpit(struct sk_buff *skb,\n 470:\t\t\t\t\t\tstruct netlink_callback *cb)\n 471:\t{\n 472:\t\tconst struct genl_info *info = genl_info_dump(cb);\n 473:\t\tstruct drm_ras_ctx *ctx = (void *)cb-\u003ectx;\n 474:\t\tstruct drm_ras_node *node;\n 475:\t\tstruct nlattr *hdr;\n 476:\t\tconst char *error_name;\n 477:\t\tu32 node_id, error_id, value;\n 478:\t\tint ret;\n 479:\t\n 480:\t\tif (!info-\u003eattrs || GENL_REQ_ATTR_CHECK(info, DRM_RAS_A_ERROR_COUNTER_ATTRS_NODE_ID))\n 481:\t\t\treturn -EINVAL;\n 482:\t\n 483:\t\tnode_id = nla_get_u32(info-\u003eattrs[DRM_RAS_A_ERROR_COUNTER_ATTRS_NODE_ID]);\n 484:\t\n 485:\t\tnode = xa_load(\u0026drm_ras_xa, node_id);\n 486:\t\tif (!node)\n 487:\t\t\treturn -ENOENT;\n 488:\t\n 489:\t\tfor (error_id = max(node-\u003eerror_counter_range.first, ctx-\u003erestart);\n 490:\t\t error_id \u003c= node-\u003eerror_counter_range.last;\n 491:\t\t error_id++) {\n 492:\t\t\tret = get_node_error_counter(node_id, error_id,\n 493:\t\t\t\t\t\t \u0026error_name, \u0026value);\n 494:\t\t\t/*\n 495:\t\t\t * For non-contiguous range, driver return -ENOENT as indication\n 496:\t\t\t * to skip this ID when listing all errors.\n 497:\t\t\t */\n 498:\t\t\tif (ret == -ENOENT)\n 499:\t\t\t\tcontinue;\n 500:\t\t\tif (ret)\n 501:\t\t\t\treturn ret;\n 502:\t\n 503:\t\t\thdr = genlmsg_iput(skb, info);\n 504:\t\n 505:\t\t\tif (!hdr) {\n 506:\t\t\t\tret = -EMSGSIZE;\n 507:\t\t\t\tbreak;\n 508:\t\t\t}\n 509:\t\n 510:\t\t\tret = msg_reply_value(skb, error_id, error_name, value);\n 511:\t\t\tif (ret) {\n 512:\t\t\t\tgenlmsg_cancel(skb, hdr);\n 513:\t\t\t\tbreak;\n"
} |
| 67/3 | 2026/10/09 21:14 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 51563 output: 39 thoughts: 40 Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience: **Analyzing `drm_ras.c` for RAS Implementation Details** Alright, let's dive into the kernel code. My immediate focus is on the Reliability, Availability, and Serviceability (RAS) implementation within the Direct Rendering Manager (DRM) subsystem. Specifically, I'm zeroing in on `drivers/gpu/drm/drm_ras.c`. I need to understand how the driver handles error detection, logging, and potentially, error correction or mitigation at the hardware level. Lines 514 through 535 are probably where the core logic or an important piece of the structure I am looking for will be located, so that's where I need to start. From there I'll follow any function calls or data structures to piece together the larger context of what's happening. I know the DRM subsystem has a unique approach to managing GPUs and hardware resources, so I expect a modular or possibly hardware-specific implementation, depending on what I am looking for. Let's see what we can find. |
| 68/3 | 2026/10/09 21:14 | tool | read-file |
0mArgs:{
"File": "drivers/gpu/drm/drm_ras.c",
"FirstLine": 514,
"LineCount": 25
}Results: {
"Contents": " 514:\t\t\t}\n 515:\t\n 516:\t\t\tgenlmsg_end(skb, hdr);\n 517:\t\t}\n 518:\t\n 519:\t\tif (ret == -EMSGSIZE)\n 520:\t\t\tctx-\u003erestart = error_id;\n 521:\t\n 522:\t\treturn ret;\n 523:\t}\n 524:\t\n 525:\t/**\n 526:\t * drm_ras_nl_get_error_counter_doit() - Query an error counter of an node\n 527:\t * @skb: Netlink message buffer\n 528:\t * @info: Generic Netlink info containing attributes of the request\n 529:\t *\n 530:\t * Extracts the node ID and error ID from the netlink attributes and\n 531:\t * retrieves the current value of the corresponding error counter. Sends the\n 532:\t * result back to the requesting user via the standard Genl reply.\n 533:\t *\n 534:\t * Return: 0 on success, or negative errno on failure.\n 535:\t */\n 536:\tint drm_ras_nl_get_error_counter_doit(struct sk_buff *skb,\n 537:\t\t\t\t\t struct genl_info *info)\n 538:\t{\n"
} |
| 69/3 | 2026/10/09 21:14 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 52235 output: 502 thoughts: 7225 Thoughts: **Analysis of KMSAN Applicability** Okay, let's break this down. We're looking at a patch introducing new telemetry functionality within the DRM subsystem, specifically focusing on potential uninitialized memory reads. First, I went through the existing error counter code (`drm_ras_nl_get_error_counter_dumpit`) and quickly confirmed that any issues there are likely pre-existing and unrelated to the current patch's additions. The focus is on the new netlink handlers: `drm_ras_nl_telemetry_read_doit` and `drm_ras_nl_telemetry_write_doit`. I meticulously examined these functions, tracing data flow to ensure variables like `status` and `value` are *always* initialized before use, and that any failure paths gracefully handle potentially uninitialized variables. The new functions call `node->telemetry_read` and `node->telemetry_write`, which in turn call into `qaic_telemetry_read` and `qaic_telemetry_write`, respectively, that I then scrutinized. I found `status` is ALWAYS assigned by a function call that receives a pointer to `status`. The `value` is either obtained by an attribute, or it's not used. Even with MHI transfers, which introduce the potential for uninitialized data from the device, the buffer (`resp`) is zero-initialized via `kzalloc_obj` before transfer, handling the case when using MHI. The "lost buffer" scenario, where an existing buffer is reused, is also carefully examined and the `resp` variable is correctly assigned when the transfer is completed. I checked for potential buffer overruns and uninitialized memory copying to userspace via netlink. However, the scalar values sent to userspace (`status`, `value`, node info) are carefully set before transmission, and padding is taken care of with `nla_put_*`'s arguments, ensuring the data is valid and initialized. Then, I meticulously examined the four conditions where `NeedsKMSAN=true`. I confirmed that no kernel structures containing uninitialized fields are sent to userspace, no conditional logic relies on uninitialized variables, no complex data structures are initialized in a way that could lead to later uninitialized reads, and no length/bounds checks allow uninitialized byte accesses. I did, however, find *other* potential issues, such as NULL pointer dereferences in `drm_ras_nl_pre_telemetry` due to missing attribute checks and potential errors calling `telemetry_read` instead of `telemetry_write`, and a memory leak during node registration that can be caught by KASAN and memory leak detectors, respectively. I also confirmed zero-initialization on `struct drm_ras_node` at registration. Because I could not find a single potential uninitialized memory read that wouldn't be caught by KASAN, I concluded that KMSAN is *not* necessary here. The patch has several bugs, but the bugs are not due to uninitialized memory. Standard debugging is sufficient. |
| 70/3 | 2026/10/09 21:15 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch series introduces telemetry operations to the DRM RAS subsystem and implements telemetry support in the Qualcomm Cloud AI (qaic) accelerator driver.\n\nAnalysis of memory initialization and user-space boundaries:\n1. User-space data exposure: The new netlink commands (DRM_RAS_CMD_TELEMETRY_READ and DRM_RAS_CMD_TELEMETRY_WRITE) return scalar primitive values ('status' as s32 via nla_put_s32 and 'value' as u64 via nla_put_64bit with DRM_RAS_A_TELEMETRY_ATTRS_PAD). In successful telemetry execution paths, both 'status' and 'value' are explicitly assigned before being passed to nla_put_*; on error, the netlink message is aborted and freed, so no uninitialized stack data is emitted. Furthermore, 64-bit netlink padding is zeroed by the netlink subsystem (skb_put_zero in __nla_reserve_nohdr).\n2. Heap and stack structures: In qaic_telemetry.c, messages (wrapper_msg, telemetry_msg) and drm_ras_node are allocated with zeroing allocators (kzalloc_obj). The stack structure xfer_queue_elem has all its used fields (seq_num, buf, xfer_done, list) initialized before submission. The resp_work struct allocated in the DL transfer callback has all its fields explicitly initialized.\n3. Potential bug surfaces: The patch introduces risks of NULL pointer dereferences (e.g. missing checks for Netlink attributes in drm_ras_nl_pre_telemetry and drm_ras_nl_telemetry_write_doit, unchecked xa_load results prior to dereferencing node-\u003etelemetry_pre, checking node-\u003etelemetry_read instead of node-\u003etelemetry_write in telemetry_write_doit) and a memory leak in qaic_drm_ras_register_node error handling. These bugs manifest as general kernel crashes (oops/page fault) or memory leaks, which are handled by standard KASAN/LOCKDEP/kmemleak testing and do not involve uninitialized memory access.\n\nTherefore, dedicated KMSAN fuzzing is not required.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch series introduces telemetry operations to the DRM RAS subsystem and implements telemetry support in the Qualcomm Cloud AI (qaic) accelerator driver.\n\nAnalysis of memory initialization and user-space boundaries:\n1. User-space data exposure: The new netlink commands (DRM_RAS_CMD_TELEMETRY_READ and DRM_RAS_CMD_TELEMETRY_WRITE) return scalar primitive values ('status' as s32 via nla_put_s32 and 'value' as u64 via nla_put_64bit with DRM_RAS_A_TELEMETRY_ATTRS_PAD). In successful telemetry execution paths, both 'status' and 'value' are explicitly assigned before being passed to nla_put_*; on error, the netlink message is aborted and freed, so no uninitialized stack data is emitted. Furthermore, 64-bit netlink padding is zeroed by the netlink subsystem (skb_put_zero in __nla_reserve_nohdr).\n2. Heap and stack structures: In qaic_telemetry.c, messages (wrapper_msg, telemetry_msg) and drm_ras_node are allocated with zeroing allocators (kzalloc_obj). The stack structure xfer_queue_elem has all its used fields (seq_num, buf, xfer_done, list) initialized before submission. The resp_work struct allocated in the DL transfer callback has all its fields explicitly initialized.\n3. Potential bug surfaces: The patch introduces risks of NULL pointer dereferences (e.g. missing checks for Netlink attributes in drm_ras_nl_pre_telemetry and drm_ras_nl_telemetry_write_doit, unchecked xa_load results prior to dereferencing node-\u003etelemetry_pre, checking node-\u003etelemetry_read instead of node-\u003etelemetry_write in telemetry_write_doit) and a memory leak in qaic_drm_ras_register_node error handling. These bugs manifest as general kernel crashes (oops/page fault) or memory leaks, which are handled by standard KASAN/LOCKDEP/kmemleak testing and do not involve uninitialized memory access.\n\nTherefore, dedicated KMSAN fuzzing is not required.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|