RDMA/efa: Keep EQ resources alive while IRQ is registered

The completion IRQ handler accesses the EQ state and DMA buffer. Its IRQ was
registered before that state was initialized, while teardown released the
buffer before free_irq() synchronized the handler.

Initialize the EQ without arming it, register the IRQ, and then arm it.
Reverse the resource order during teardown by freeing the IRQ before
destroying the EQ.

Fixes: 2a152512a1 ("RDMA/efa: CQ notifications")
Link: https://patch.msgid.link/20260907-use-after-free-of-admin-queue-struct-v1-2-dd9d9267fbf4@nvidia.com
Reviewed-by: Michael Margolin <mrgolin@amazon.com>
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
This commit is contained in:
Leon Romanovsky 2026-09-10 09:35:13 -04:00
parent e08aca85c0
commit e22a3627b7
3 changed files with 13 additions and 11 deletions

View File

@ -1252,7 +1252,7 @@ static void efa_com_destroy_eq(struct efa_com_dev *edev,
err);
}
static void efa_com_arm_eq(struct efa_com_dev *edev, struct efa_com_eq *eeq)
void efa_com_arm_eq(struct efa_com_dev *edev, struct efa_com_eq *eeq)
{
u32 val = 0;
@ -1341,7 +1341,6 @@ int efa_com_eq_init(struct efa_com_dev *edev, struct efa_com_eq *eeq,
eeq->phase = 1;
eeq->depth = params.depth;
eeq->cb = cb;
efa_com_arm_eq(edev, eeq);
return 0;

View File

@ -169,6 +169,7 @@ int efa_com_admin_init(struct efa_com_dev *edev,
void efa_com_admin_destroy(struct efa_com_dev *edev);
int efa_com_eq_init(struct efa_com_dev *edev, struct efa_com_eq *eeq,
efa_eqe_handler cb, u16 depth, u8 msix_vec);
void efa_com_arm_eq(struct efa_com_dev *edev, struct efa_com_eq *eeq);
void efa_com_eq_destroy(struct efa_com_dev *edev, struct efa_com_eq *eeq);
int efa_com_dev_reset(struct efa_com_dev *edev,
enum efa_regs_reset_reason_types reset_reason);

View File

@ -302,28 +302,30 @@ static void efa_set_host_info(struct efa_dev *dev)
static void efa_destroy_eq(struct efa_dev *dev, struct efa_eq *eq)
{
efa_com_eq_destroy(&dev->edev, &eq->eeq);
efa_free_irq(dev, &eq->irq);
efa_com_eq_destroy(&dev->edev, &eq->eeq);
}
static int efa_create_eq(struct efa_dev *dev, struct efa_eq *eq, u32 msix_vec)
{
int err;
efa_setup_comp_irq(dev, eq, msix_vec);
err = efa_request_irq(dev, &eq->irq);
if (err)
return err;
err = efa_com_eq_init(&dev->edev, &eq->eeq, efa_process_eqe,
dev->dev_attr.max_eq_depth, msix_vec);
if (err)
goto err_free_comp_irq;
return err;
efa_setup_comp_irq(dev, eq, msix_vec);
err = efa_request_irq(dev, &eq->irq);
if (err)
goto err_destroy_eq;
efa_com_arm_eq(&dev->edev, &eq->eeq);
return 0;
err_free_comp_irq:
efa_free_irq(dev, &eq->irq);
err_destroy_eq:
efa_com_eq_destroy(&dev->edev, &eq->eeq);
return err;
}