[PATCH v5 08/13] scsi: fnic: Handle NVMe LS frames in FDLS
From: Karan Tilak Kumar
Date: Wed Jun 24 2026 - 00:48:26 EST
Classify NVMe LS request OXIDs, route NVMe LS responses and ABTS frames
through the FCS receive path, and reset NVMe exchanges when FDLS tears
down target ports.
Extend FDLS link-down and frame-processing paths so NVMe LS traffic
follows the same discovery and cleanup state machine as FCP traffic.
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>
---
Incorporate review comments from Lee Duncan:
Replace the NVMe LS OXID switch with a direct frame-type check.
Rename the NVMe frame helper to follow fnic function naming style.
Changes between v4 and v5:
Incorporate review comments from Sashiko:
Check cleaned buffers before dereferencing them
Drain OXID reclaim state on reset
---
drivers/scsi/fnic/fdls_disc.c | 39 +++++++++++++++++++++++++++++++++++
drivers/scsi/fnic/fnic_fcs.c | 34 +++++++++++++++++++++++-------
2 files changed, 66 insertions(+), 7 deletions(-)
diff --git a/drivers/scsi/fnic/fdls_disc.c b/drivers/scsi/fnic/fdls_disc.c
index e03256183ac6..f66c121cb712 100644
--- a/drivers/scsi/fnic/fdls_disc.c
+++ b/drivers/scsi/fnic/fdls_disc.c
@@ -387,10 +387,25 @@ static bool fdls_is_oxid_tgt_req(uint16_t oxid)
return true;
}
+static inline bool fdls_is_oxid_nvme_req(uint16_t oxid)
+{
+ return FNIC_FRAME_TYPE(oxid) == FNIC_FRAME_TYPE_NVME_LS;
+}
+
static void fdls_reset_oxid_pool(struct fnic_iport_s *iport)
{
struct fnic_oxid_pool_s *oxid_pool = &iport->oxid_pool;
+ struct reclaim_entry_s *reclaim_entry, *next;
+ cancel_delayed_work(&oxid_pool->oxid_reclaim_work);
+ cancel_delayed_work(&oxid_pool->schedule_oxid_free_retry);
+ list_for_each_entry_safe(reclaim_entry, next,
+ &oxid_pool->oxid_reclaim_list, links) {
+ list_del(&reclaim_entry->links);
+ kfree(reclaim_entry);
+ }
+ bitmap_clear(oxid_pool->pending_schedule_free, 0, FNIC_OXID_POOL_SZ);
+ bitmap_clear(oxid_pool->bitmap, 0, FNIC_OXID_POOL_SZ);
oxid_pool->next_idx = 0;
}
@@ -1288,6 +1303,10 @@ bool fdls_delete_tport(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
fnic_rport_exch_reset(iport->fnic, tport->fcid);
spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+ } else if (IS_FNIC_NVME_INITIATOR(fnic)) {
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+ nvfnic_exch_reset(iport, tport);
+ spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
}
if ((tport->flags & FNIC_FDLS_SCSI_REGISTERED) ||
@@ -1829,6 +1848,7 @@ static struct fnic_tport_s *fdls_create_tport(struct fnic_iport_s *iport,
tport->fcid = fcid;
tport->wwpn = wwpn;
tport->iport = iport;
+ INIT_LIST_HEAD(&tport->ls_req_list);
FNIC_FCS_DBG(KERN_DEBUG, fnic,
"Need to setup tport timer callback");
@@ -2440,6 +2460,8 @@ static void fdls_tport_timer_callback(struct timer_list *t)
struct fnic *fnic = iport->fnic;
uint16_t oxid;
unsigned long flags;
+ struct fc_frame_header fchdr = {0};
+ uint8_t fcid[3];
spin_lock_irqsave(&fnic->fnic_lock, flags);
if (!tport->timer_pending) {
@@ -2532,6 +2554,12 @@ static void fdls_tport_timer_callback(struct timer_list *t)
FNIC_FCS_DBG(KERN_INFO, fnic,
"0x%x timeout tport 0x%x oxid 0x%x state %d\n",
iport->fcid, tport->fcid, oxid, tport->state);
+ if (IS_FNIC_NVME_INITIATOR(fnic)) {
+ hton24(fcid, tport->fcid);
+ FNIC_STD_SET_S_ID(fchdr, fcid);
+ FNIC_STD_SET_OX_ID(fchdr, oxid);
+ nvfnic_process_ls_abts_rsp(iport, &fchdr);
+ }
break;
}
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
@@ -2842,6 +2870,12 @@ fdls_process_tgt_prli_rsp(struct fnic_iport_s *iport,
"mismatched target zoned with FC SCSI initiator: 0x%x",
tgt_fcid);
mismatched_tgt = true;
+ } else if (IS_FNIC_NVME_INITIATOR(fnic) &&
+ prli_rsp->sp.spp_type != FC_TYPE_NVME) {
+ FNIC_FCS_DBG(KERN_ERR, fnic,
+ "mismatched target zoned with NVME initiator: 0x%x",
+ tgt_fcid);
+ mismatched_tgt = true;
}
if (mismatched_tgt) {
fdls_tgt_logout(iport, tport);
@@ -4853,6 +4887,8 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport,
return FNIC_FDMI_BLS_ABTS_RSP;
} else if (fdls_is_oxid_tgt_req(oxid)) {
return FNIC_TPORT_BLS_ABTS_RSP;
+ } else if (fdls_is_oxid_nvme_req(oxid)) {
+ return FNIC_LS_REQ_ABTS_RSP;
}
FNIC_FCS_DBG(KERN_INFO, fnic,
"Received ABTS rsp with unknown oxid(0x%x) from 0x%x. Dropping frame",
@@ -5085,6 +5121,9 @@ void fnic_fdls_recv_frame(struct fnic_iport_s *iport, void *rx_frame,
case FNIC_FABRIC_BLS_ABTS_RSP:
fdls_process_fabric_abts_rsp(iport, fchdr);
break;
+ case FNIC_LS_REQ_ABTS_RSP:
+ nvfnic_process_ls_abts_rsp(iport, fchdr);
+ break;
case FNIC_FDMI_BLS_ABTS_RSP:
fdls_process_fdmi_abts_rsp(iport, fchdr);
break;
diff --git a/drivers/scsi/fnic/fnic_fcs.c b/drivers/scsi/fnic/fnic_fcs.c
index 94b7c150c08c..b00672ef8b00 100644
--- a/drivers/scsi/fnic/fnic_fcs.c
+++ b/drivers/scsi/fnic/fnic_fcs.c
@@ -31,6 +31,11 @@ struct workqueue_struct *fnic_event_queue;
static uint8_t FCOE_ALL_FCF_MAC[6] = FC_FCOE_FLOGI_MAC;
+static inline bool fnic_is_nvme_frame(struct fc_frame_header *fchdr)
+{
+ return (fchdr->fh_type == FC_TYPE_NVME);
+}
+
/*
* Internal Functions
* This function will initialize the src_mac address to be
@@ -284,6 +289,7 @@ void fnic_handle_frame(struct work_struct *work)
struct fnic *fnic = container_of(work, struct fnic, frame_work);
struct fnic_frame_list *cur_frame, *next;
int fchdr_offset = 0;
+ struct fc_frame_header *fchdr;
spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
list_for_each_entry_safe(cur_frame, next, &fnic->frame_queue, links) {
@@ -313,8 +319,14 @@ void fnic_handle_frame(struct work_struct *work)
fchdr_offset = (cur_frame->rx_ethhdr_stripped) ?
0 : FNIC_ETH_FCOE_HDRS_OFFSET;
- fnic_fdls_recv_frame(&fnic->iport, cur_frame->fp,
- cur_frame->frame_len, fchdr_offset);
+ fchdr = (struct fc_frame_header *)((u8 *)cur_frame->fp + fchdr_offset);
+ if (IS_FNIC_NVME_INITIATOR(fnic) && fnic_is_nvme_frame(fchdr)) {
+ nvfnic_ls_rsp_recv(&fnic->iport, fchdr,
+ cur_frame->frame_len - fchdr_offset);
+ } else {
+ fnic_fdls_recv_frame(&fnic->iport, cur_frame->fp,
+ cur_frame->frame_len, fchdr_offset);
+ }
mempool_free(cur_frame->fp, fnic->frame_recv_pool);
mempool_free(cur_frame, fnic->frame_elem_pool);
@@ -614,9 +626,13 @@ int fnic_alloc_rq_frame(struct vnic_rq *rq)
void fnic_free_rq_buf(struct vnic_rq *rq, struct vnic_rq_buf *buf)
{
- void *rq_buf = buf->os_buf;
+ void *rq_buf;
struct fnic *fnic = vnic_dev_priv(rq->vdev);
+ if (WARN_ON(!buf))
+ return;
+
+ rq_buf = buf->os_buf;
dma_unmap_single(&fnic->pdev->dev, buf->dma_addr, buf->len,
DMA_FROM_DEVICE);
@@ -651,7 +667,7 @@ static int fnic_send_frame(struct fnic *fnic, void *frame, int frame_len)
dma_unmap_single(&fnic->pdev->dev, pa, frame_len, DMA_TO_DEVICE);
FNIC_FCS_DBG(KERN_INFO, fnic,
"vnic work queue descriptor is not available");
- ret = -1;
+ ret = -ENXIO;
goto fnic_send_frame_end;
}
@@ -685,7 +701,6 @@ fdls_send_fcoe_frame(struct fnic *fnic, void *frame, int frame_size,
struct fcoe_hdr *pfcoe_hdr;
struct fnic_frame_list *frame_elem;
int len = frame_size;
- int ret;
struct fc_frame_header *fchdr = (struct fc_frame_header *) (frame +
FNIC_ETH_FCOE_HDRS_OFFSET);
@@ -723,8 +738,7 @@ fdls_send_fcoe_frame(struct fnic *fnic, void *frame, int frame_size,
fnic_debug_dump_fc_frame(fnic, fchdr, frame_size, "Outgoing");
- ret = fnic_send_frame(fnic, frame, len);
- return ret;
+ return fnic_send_frame(fnic, frame, len);
}
int fnic_send_fcoe_frame(struct fnic_iport_s *iport, void *frame,
@@ -872,6 +886,9 @@ static void fnic_wq_complete_frame_send(struct vnic_wq *wq,
{
struct fnic *fnic = vnic_dev_priv(wq->vdev);
+ if (WARN_ON(!buf))
+ return;
+
dma_unmap_single(&fnic->pdev->dev, buf->dma_addr, buf->len,
DMA_TO_DEVICE);
mempool_free(buf->os_buf, fnic->frame_pool);
@@ -914,6 +931,9 @@ void fnic_free_wq_buf(struct vnic_wq *wq, struct vnic_wq_buf *buf)
{
struct fnic *fnic = vnic_dev_priv(wq->vdev);
+ if (WARN_ON(!buf))
+ return;
+
dma_unmap_single(&fnic->pdev->dev, buf->dma_addr, buf->len,
DMA_TO_DEVICE);
--
2.47.1