[PATCH v4 10/13] scsi: fnic: Abort timed-out NVMe LS requests
From: Karan Tilak Kumar
Date: Fri Jun 12 2026 - 14:23:16 EST
Add an FDLS helper that sends ABTS frames for outstanding NVMe LS
requests.
Use the active LS request OXID when building the ABTS frame, send it
through the FCoE transmit path, and call it from LS timeout and abort
handling.
Reviewed-by: Sesidhar Baddela <sebaddel@xxxxxxxxx>
Reviewed-by: Arulprabhu Ponnusamy <arulponn@xxxxxxxxx>
Reviewed-by: Gian Carlo Boffa <gcboffa@xxxxxxxxx>
Reviewed-by: Arun Easi <aeasi@xxxxxxxxx>
Reviewed-by: Hannes Reinecke <hare@xxxxxxxxxx>
Reviewed-by: Lee Duncan <lduncan@xxxxxxxx>
Signed-off-by: Karan Tilak Kumar <kartilak@xxxxxxxxx>
Co-developed-by: Hannes Reinecke <hare@xxxxxxxxxx>
---
Changes between v3 and v4:
Incorporate review comments from Sashiko:
Free LS ABTS frame when send fails
Reuse NVMe LS request cleanup for abort failure
---
drivers/scsi/fnic/fdls_disc.c | 44 ++++++++++++++++++++++++
drivers/scsi/fnic/fnic_fdls.h | 2 ++
drivers/scsi/fnic/fnic_nvme.c | 65 +++++++++++++++++++++++++++++------
3 files changed, 100 insertions(+), 11 deletions(-)
diff --git a/drivers/scsi/fnic/fdls_disc.c b/drivers/scsi/fnic/fdls_disc.c
index 5b4087f1247a..873dbd22ada8 100644
--- a/drivers/scsi/fnic/fdls_disc.c
+++ b/drivers/scsi/fnic/fdls_disc.c
@@ -654,6 +654,50 @@ fdls_send_logo_resp(struct fnic_iport_s *iport,
fnic_send_fcoe_frame(iport, frame, frame_size);
}
+int fdls_send_ls_req_abts(struct fnic_iport_s *iport,
+ struct fnic_tport_s *tport, unsigned int oxid)
+{
+ uint8_t *frame;
+ uint8_t s_id[3];
+ uint8_t d_id[3];
+ struct fnic *fnic = iport->fnic;
+ struct fc_frame_header *pls_req_abts;
+ uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
+ sizeof(struct fc_frame_header);
+ int ret;
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic,
+ "Failed to allocate frame to send ls req ABTS");
+ return -ENOMEM;
+ }
+
+ pls_req_abts = (struct fc_frame_header *) (frame +
+ FNIC_ETH_FCOE_HDRS_OFFSET);
+ fdls_init_fabric_abts_frame(frame, iport);
+
+ hton24(s_id, iport->fcid);
+ hton24(d_id, tport->fcid);
+ FNIC_STD_SET_S_ID(*pls_req_abts, s_id);
+ FNIC_STD_SET_D_ID(*pls_req_abts, d_id);
+
+ FNIC_STD_SET_OX_ID(*pls_req_abts, oxid);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic,
+ "iport 0x%x: tport: 0x%x FDLS sending ls req abts with oxid: 0x%x",
+ iport->fcid, tport->fcid, oxid);
+
+ ret = fnic_send_fcoe_frame(iport, frame, frame_size);
+ if (ret) {
+ mempool_free(frame, fnic->frame_pool);
+ return ret;
+ }
+
+ return 0;
+}
+
+
void
fdls_send_tport_abts(struct fnic_iport_s *iport,
struct fnic_tport_s *tport)
diff --git a/drivers/scsi/fnic/fnic_fdls.h b/drivers/scsi/fnic/fnic_fdls.h
index 0a68d0fb11b1..ce4b3aae77e3 100644
--- a/drivers/scsi/fnic/fnic_fdls.h
+++ b/drivers/scsi/fnic/fnic_fdls.h
@@ -407,6 +407,8 @@ uint16_t fdls_alloc_oxid(struct fnic_iport_s *iport, int oxid_frame_type,
uint16_t *active_oxid);
void fdls_free_oxid(struct fnic_iport_s *iport,
uint16_t oxid, uint16_t *active_oxid);
+int fdls_send_ls_req_abts(struct fnic_iport_s *iport,
+ struct fnic_tport_s *tport, unsigned int oxid);
void fdls_tgt_logout(struct fnic_iport_s *iport,
struct fnic_tport_s *tport);
void fnic_del_fabric_timer_sync(struct fnic *fnic);
diff --git a/drivers/scsi/fnic/fnic_nvme.c b/drivers/scsi/fnic/fnic_nvme.c
index 014660725373..9cc8f0baf09c 100644
--- a/drivers/scsi/fnic/fnic_nvme.c
+++ b/drivers/scsi/fnic/fnic_nvme.c
@@ -24,6 +24,10 @@
#if IS_ENABLED(CONFIG_NVME_FC)
+static bool nvfnic_ls_req_cleanup(struct fnic_iport_s *iport,
+ struct nvmefc_ls_req *lsreq,
+ uint16_t oxid);
+
int nvfnic_get_sg_count(struct fnic_io_req *io_req)
{
return io_req->fcp_req->sg_cnt;
@@ -1232,6 +1236,23 @@ void nvfnic_ls_rsp_recv(struct fnic_iport_s *iport,
spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
}
+static bool nvfnic_ls_req_cleanup(struct fnic_iport_s *iport,
+ struct nvmefc_ls_req *lsreq,
+ uint16_t oxid)
+{
+ struct nvfnic_ls_req *nvfnic_ls_req = lsreq->private;
+
+ if (!nvfnic_ls_req)
+ return false;
+
+ lsreq->private = NULL;
+ list_del(&nvfnic_ls_req->list);
+ fdls_free_oxid(iport, oxid, &nvfnic_ls_req->oxid);
+ nvfnic_ls_req->state = FNIC_LS_REQ_CMD_COMPLETE;
+
+ return true;
+}
+
void nvfnic_ls_req_timeout(struct timer_list *t)
{
struct nvfnic_ls_req *nvfnic_ls_req = timer_container_of(nvfnic_ls_req,
@@ -1241,6 +1262,7 @@ void nvfnic_ls_req_timeout(struct timer_list *t)
struct fnic_iport_s *iport = &fnic->iport;
struct fnic_tport_s *tport = (struct fnic_tport_s *) nvfnic_ls_req->tport;
uint16_t oxid = nvfnic_ls_req->oxid;
+ int timeout;
FNIC_NVME_DBG(KERN_INFO, fnic,
"tport: 0x%x lsreq: 0x%x state: %d timeout\n",
@@ -1262,10 +1284,8 @@ void nvfnic_ls_req_timeout(struct timer_list *t)
"tport: 0x%x lsreq: 0x%x abort timeout\n",
tport->fcid, nvfnic_ls_req->oxid);
- list_del(&nvfnic_ls_req->list);
ls_req = nvfnic_ls_req->ls_req;
- fdls_free_oxid(iport, oxid, &nvfnic_ls_req->oxid);
- ls_req->private = NULL;
+ nvfnic_ls_req_cleanup(iport, ls_req, oxid);
spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
ls_req->done(ls_req, -ETIMEDOUT);
return;
@@ -1274,6 +1294,19 @@ void nvfnic_ls_req_timeout(struct timer_list *t)
FNIC_NVME_DBG(KERN_ERR, fnic,
"tport: 0x%x lsreq: 0x%x sending abort\n",
tport->fcid, nvfnic_ls_req->oxid);
+ nvfnic_ls_req->state = FNIC_LS_REQ_CMD_ABTS_PENDING;
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+
+ if (fdls_send_ls_req_abts(iport, tport, nvfnic_ls_req->oxid) == 0) {
+ timeout = FNIC_LS_REQ_TMO_MSECS(ls_req->timeout);
+ mod_timer(&nvfnic_ls_req->ls_req_timer,
+ round_jiffies(jiffies + msecs_to_jiffies(timeout)));
+ return;
+ }
+ FNIC_NVME_DBG(KERN_ERR, fnic,
+ "tport: 0x%x lsreq: 0x%x cannot send abort\n",
+ tport->fcid, oxid);
+ spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
}
if (ls_req->private == NULL) {
@@ -1281,10 +1314,8 @@ void nvfnic_ls_req_timeout(struct timer_list *t)
return;
}
- list_del(&nvfnic_ls_req->list);
ls_req = nvfnic_ls_req->ls_req;
- fdls_free_oxid(iport, oxid, &nvfnic_ls_req->oxid);
- ls_req->private = NULL;
+ nvfnic_ls_req_cleanup(iport, ls_req, oxid);
spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
ls_req->done(ls_req, -ETIMEDOUT);
@@ -1403,11 +1434,7 @@ int nvfnic_ls_req_send(struct nvme_fc_local_port *lport,
if (ret) {
timer_delete_sync(&nvfnic_ls_req->ls_req_timer);
spin_lock_irqsave(&fnic->fnic_lock, flags);
- list_del(&nvfnic_ls_req->list);
- fdls_free_oxid(iport, nvfnic_ls_req->oxid,
- &nvfnic_ls_req->oxid);
- nvfnic_ls_req->state = FNIC_LS_REQ_CMD_COMPLETE;
- ls_req->private = NULL;
+ nvfnic_ls_req_cleanup(iport, ls_req, nvfnic_ls_req->oxid);
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
mempool_free(frame, fnic->frame_pool);
return ret;
@@ -1521,6 +1548,7 @@ void nvfnic_ls_req_abort(struct nvme_fc_local_port *lport,
struct nvfnic_ls_req *nvfnic_ls_req;
uint16_t oxid;
int timeout;
+ int ret;
spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
@@ -1579,10 +1607,25 @@ void nvfnic_ls_req_abort(struct nvme_fc_local_port *lport,
/* Mark the state and flags */
nvfnic_ls_req->state = FNIC_LS_REQ_CMD_ABTS_PENDING;
+ oxid = nvfnic_ls_req->oxid;
timeout = FNIC_LS_REQ_TMO_MSECS(lsreq->timeout);
mod_timer(&nvfnic_ls_req->ls_req_timer,
round_jiffies(jiffies + msecs_to_jiffies(timeout)));
spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+
+ ret = fdls_send_ls_req_abts(iport, tport, oxid);
+ if (!ret)
+ return;
+
+ timer_delete_sync(&nvfnic_ls_req->ls_req_timer);
+ spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+ if (!nvfnic_ls_req_cleanup(iport, lsreq, oxid)) {
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+ return;
+ }
+
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+ lsreq->done(lsreq, -EAGAIN);
}
bool nvfnic_queue_abort_io_req(struct fnic *fnic, int tag,
--
2.47.1