Thread (16 messages) 16 messages, 2 authors, 2d ago

Re: [PATCH net-next v12 05/12] enic: add admin CQ service with MSI-X interrupt and workqueue polling

From: Jakub Kicinski <kuba@kernel.org>
Date: 2026-07-28 00:41:24
Also in: lkml

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
Now I have the confirmation. Let me generate the report.

commit 91d5187517ef604e2669cd4fde3a2c606489afe1
Author: Satish Kharat [off-list ref]

enic: add admin CQ service with MSI-X interrupt and workqueue polling

This adds an MSI-X driven CQ service for the admin channel with a
workqueue-based poller, a bounded admin_msg backlog, and a deferred
dispatch work_struct for received admin messages.
quoted hunk ↗ jump to hunk
diff --git a/drivers/net/ethernet/cisco/enic/enic_admin.c b/drivers/net/ethernet/cisco/enic/enic_admin.c
index b2be42092106..9358165263d9 100644
--- a/drivers/net/ethernet/cisco/enic/enic_admin.c
+++ b/drivers/net/ethernet/cisco/enic/enic_admin.c
[ ... ]
quoted hunk ↗ jump to hunk
@@ -94,6 +96,266 @@ static void enic_admin_rq_drain(struct enic *enic)
 	vnic_rq_clean(&enic->admin_rq, enic_admin_rq_buf_clean);
 }
 
+static unsigned int enic_admin_cq_color(void *cq_desc, unsigned int desc_size)
+{
[ ... ]
+unsigned int enic_admin_rq_cq_service(struct enic *enic)
+{
+	struct vnic_cq *cq = &enic->admin_cq[1];
+	struct vnic_rq *rq = &enic->admin_rq;
+	struct cq_enet_rq_desc *rq_desc;
+	struct vnic_rq_buf *buf;
+	u16 bwf, bytes_written;
+	unsigned int work = 0;
+	void *desc;
+
[ ... ]
+		rq_desc = desc;
+		bwf = le16_to_cpu(rq_desc->bytes_written_flags);
+		bytes_written = bwf & CQ_ENET_RQ_DESC_BYTES_WRITTEN_MASK;
+		if (bytes_written > buf->len)
+			goto next_desc;
+
+		dma_sync_single_for_cpu(&enic->pdev->dev,
+					buf->dma_addr, buf->len,
+					DMA_FROM_DEVICE);
[ ... ]
+		enic_admin_msg_enqueue(enic, buf->os_buf, bytes_written);
[Medium]
Can this path leak uninitialised heap contents to downstream admin
message consumers?

The admin RQ buffers are allocated with plain kmalloc() in
enic_admin_rq_post_one():

    buf = kmalloc(ENIC_ADMIN_BUF_SIZE, gfp);

so the buffer contents are uninitialised heap.  The only bound applied
here is bytes_written > buf->len, but there is no check that hardware
actually DMA'd bytes_written bytes.  enic_admin_msg_enqueue() then does:

    memcpy(msg->data, buf, len);

A firmware or peer that DMAs a short payload but reports a larger
bytes_written value would cause the tail region to be copied verbatim
from stale heap into the queued admin_msg.

The commit comment above this block already flags a "buggy or hostile
VF" as part of the threat model.  Would switching enic_admin_rq_post_one()
to kzalloc(), or zeroing [bytes_written, buf->len) after
dma_sync_single_for_cpu() and before enqueue, be preferable?

The admin_rq_handler is NULL at this point in the series, but later
patches ("enic: add MBOX PF handlers for VF register and capability"
and "enic: add MBOX VF handlers for capability, register and link
state") process payload bytes, so any stale content would then reach
handler logic.

[ ... ]
+static void enic_admin_poll_work_handler(struct work_struct *work)
+{
+	struct enic *enic = container_of(work, struct enic, admin_poll_work);
+	unsigned int credits;
+	unsigned int rq_work;
+
+	/* Snapshot the pending credit count before draining so we acknowledge
+	 * exactly what the hardware reported for this interrupt.  Credits that
+	 * accrue while enic_admin_rq_cq_service() runs are left for the next
+	 * interrupt, which is harmless on this low-rate control path.
+	 */
+	credits = vnic_intr_credits(&enic->admin_intr);
+
+	rq_work = enic_admin_rq_cq_service(enic);
+
+	if (rq_work > 0)
+		schedule_work(&enic->admin_msg_work);
+
+	/* Acknowledge the snapshotted credits and unmask the vector.  Unlike
+	 * the NAPI data path, the admin channel is not re-polled, so the vector
+	 * must be re-armed here to receive the next completion.  The unmask is
+	 * applied through the interrupt mask register independently of the
+	 * credit count, so returning zero credits on a spurious wakeup still
+	 * re-arms the vector.
+	 */
+	vnic_intr_return_credits(&enic->admin_intr,
+				 credits,
+				 1 /* unmask */, 0);
+}
[Low]
The commit message states:

    At least one credit is returned so the write always
    carries a non-zero acknowledgment and re-arms the vector, since the
    admin channel is not re-polled like the NAPI data path.

Does the code actually enforce a floor of one credit?

credits comes directly from vnic_intr_credits() and is passed verbatim
to vnic_intr_return_credits() with no max(credits, 1U) clamp.  On a
spurious wakeup credits can be zero.  The in-function comment right
below correctly notes:

    returning zero credits on a spurious wakeup still
    re-arms the vector.

Should the commit message be reworded to match the actual behaviour
(the UNMASK bit re-arms the vector independently of the credit count),
or should a max(credits, 1U) be added to match the message?

[ ... ]
-- 
pw-bot: cr
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help