enic: add admin RQ buffer management

The admin receive queue needs pre-posted DMA buffers for incoming
mailbox messages from VFs. Each buffer is a kzalloc'd region mapped
for DMA (2048 bytes, sufficient for any MBOX message).  Zeroing on
allocation ensures that if a completion reports more bytes than
hardware actually DMA-wrote, the parser reads zero padding rather
than uninitialised heap contents.

Add enic_admin_rq_fill(gfp) to post buffers at open time, and
enic_admin_rq_drain() to unmap and free them at close time.
Wire both into the admin channel open/close paths. The gfp_t
parameter lets the caller pass the allocation context; both current
callers -- channel open and the CQ-poll work handler that refills
after draining (added in the next patch) -- run in process context
and use GFP_KERNEL.

Signed-off-by: Satish Kharat <satishkh@cisco.com>
Link: https://patch.msgid.link/20260812-enic-sriov-v2-admin-channel-v2-v13-3-b3809e448aba@cisco.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Satish Kharat 2026-08-12 05:48:03 -07:00 committed by Jakub Kicinski
parent 3258931d40
commit a0da4ea750

View File

@ -3,6 +3,7 @@
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/dma-mapping.h>
#include "vnic_dev.h"
#include "vnic_wq.h"
@ -34,10 +35,63 @@ static void enic_admin_wq_buf_clean(struct vnic_wq *wq,
}
}
/* No-op: admin RQ buffer teardown is handled in enic_admin_channel_close */
static void enic_admin_rq_buf_clean(struct vnic_rq *rq,
struct vnic_rq_buf *buf)
{
struct enic *enic = vnic_dev_priv(rq->vdev);
if (!buf->os_buf)
return;
dma_unmap_single(&enic->pdev->dev, buf->dma_addr, buf->len,
DMA_FROM_DEVICE);
kfree(buf->os_buf);
buf->os_buf = NULL;
}
static int enic_admin_rq_post_one(struct enic *enic, gfp_t gfp)
{
struct vnic_rq *rq = &enic->admin_rq;
struct rq_enet_desc *desc;
dma_addr_t dma_addr;
void *buf;
buf = kzalloc(ENIC_ADMIN_BUF_SIZE, gfp);
if (!buf)
return -ENOMEM;
dma_addr = dma_map_single(&enic->pdev->dev, buf, ENIC_ADMIN_BUF_SIZE,
DMA_FROM_DEVICE);
if (dma_mapping_error(&enic->pdev->dev, dma_addr)) {
kfree(buf);
return -ENOMEM;
}
desc = vnic_rq_next_desc(rq);
rq_enet_desc_enc(desc, (u64)dma_addr | VNIC_PADDR_TARGET,
RQ_ENET_TYPE_ONLY_SOP, ENIC_ADMIN_BUF_SIZE);
vnic_rq_post(rq, buf, 0, dma_addr, ENIC_ADMIN_BUF_SIZE, 0);
return 0;
}
static int enic_admin_rq_fill(struct enic *enic, gfp_t gfp)
{
struct vnic_rq *rq = &enic->admin_rq;
int err;
while (vnic_rq_desc_avail(rq) > 0) {
err = enic_admin_rq_post_one(enic, gfp);
if (err)
return err;
}
return 0;
}
static void enic_admin_rq_drain(struct enic *enic)
{
vnic_rq_clean(&enic->admin_rq, enic_admin_rq_buf_clean);
}
static int enic_admin_qp_type_set(struct enic *enic, u32 enable)
@ -171,6 +225,13 @@ int enic_admin_channel_open(struct enic *enic)
vnic_wq_enable(&enic->admin_wq);
vnic_rq_enable(&enic->admin_rq);
err = enic_admin_rq_fill(enic, GFP_KERNEL);
if (err) {
netdev_err(enic->netdev,
"Failed to fill admin RQ buffers: %d\n", err);
goto disable_queues;
}
err = enic_admin_qp_type_set(enic, QP_ENABLE);
if (err) {
netdev_err(enic->netdev,
@ -188,6 +249,7 @@ int enic_admin_channel_open(struct enic *enic)
netdev_warn(enic->netdev, "Failed to disable admin WQ\n");
if (vnic_rq_disable(&enic->admin_rq))
netdev_warn(enic->netdev, "Failed to disable admin RQ\n");
enic_admin_rq_drain(enic);
enic_admin_free_resources(enic);
return err;
}
@ -218,7 +280,7 @@ void enic_admin_channel_close(struct enic *enic)
"Failed to disable admin RQ: %d\n", err);
vnic_wq_clean(&enic->admin_wq, enic_admin_wq_buf_clean);
vnic_rq_clean(&enic->admin_rq, enic_admin_rq_buf_clean);
enic_admin_rq_drain(enic);
vnic_cq_clean(&enic->admin_cq[0]);
vnic_cq_clean(&enic->admin_cq[1]);
enic_admin_free_resources(enic);