[PATCH v2 09/13] scsi: fnic: Send NVMe LS requests through FDLS
From: Karan Tilak Kumar
Date: Wed May 27 2026 - 15:56:19 EST
Add the FC frame wrapper for NVMe LS requests and build LS request frames
from the NVMe-FC transport callback.
Allocate OXIDs, track outstanding LS requests on the target port, arm
request timers, and register the LS request callback in the NVMe FC port
template.
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>
---
drivers/scsi/fnic/fdls_fc.h | 8 +++
drivers/scsi/fnic/fnic_nvme.c | 110 +++++++++++++++++++++++++++++++++-
drivers/scsi/fnic/fnic_nvme.h | 3 +
3 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/drivers/scsi/fnic/fdls_fc.h b/drivers/scsi/fnic/fdls_fc.h
index a7b8b969f019..381b87d549dd 100644
--- a/drivers/scsi/fnic/fdls_fc.h
+++ b/drivers/scsi/fnic/fdls_fc.h
@@ -30,6 +30,9 @@
#include <linux/if_ether.h>
#include <scsi/fc/fc_encaps.h>
#include <scsi/fc/fc_fcoe.h>
+#include <linux/nvme.h>
+#include <linux/nvme-fc.h>
+#include <linux/nvme-fc-driver.h>
#define FDLS_MIN_FRAMES (32)
#define FDLS_MIN_FRAME_ELEM (4)
@@ -251,6 +254,11 @@ struct fc_std_logo {
struct fc_els_logo els;
} __packed;
+struct fc_std_ls_req {
+ struct fc_frame_header fchdr;
+ struct nvmefc_ls_req ls_req;
+} __packed;
+
#define FNIC_ETH_FCOE_HDRS_OFFSET \
(sizeof(struct ethhdr) + sizeof(struct fcoe_hdr))
diff --git a/drivers/scsi/fnic/fnic_nvme.c b/drivers/scsi/fnic/fnic_nvme.c
index 26d4cbb06f50..83a43df28956 100644
--- a/drivers/scsi/fnic/fnic_nvme.c
+++ b/drivers/scsi/fnic/fnic_nvme.c
@@ -1244,6 +1244,114 @@ void nvfnic_ls_req_timeout(struct timer_list *t)
ls_req->done(ls_req, -ETIMEDOUT);
}
+/**
+ * nvfnic_ls_req_send - Send NVMe FC link service (LS) request
+ * @lport: Pointer to local NVMe FC port structure
+ * @rport: Pointer to remote NVMe FC port structure
+ * @ls_req: Pointer to the link service request structure
+ *
+ * This function is used to send link service (LS) commands to an NVMe
+ * Discovery Controller for discovery operations, as well as to regular
+ * NVMe subsystems during association. It encapsulates the logic for
+ * transmitting LS requests over the NVMe over Fabrics (NVMe-oF) FC
+ * transport.
+ *
+ * Returns: 0 on success, or a negative error code on failure.
+ */
+int nvfnic_ls_req_send(struct nvme_fc_local_port *lport,
+ struct nvme_fc_remote_port *rport,
+ struct nvmefc_ls_req *ls_req)
+{
+ int timeout;
+ uint8_t *frame;
+ uint8_t fcid[3];
+ unsigned long flags = 0;
+ struct fnic_iport_s *iport = lport->private;
+ struct nvmefc_ls_req *pls_req;
+ struct fnic *fnic = iport->fnic;
+ struct fc_std_ls_req *pfc_std_ls_req;
+ struct nvfnic_ls_req *nvfnic_ls_req = ls_req->private;
+ uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
+ sizeof(struct fc_frame_header) + ls_req->rqstlen;
+ struct fnic_tport_s *tport;
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+
+ tport = (struct fnic_tport_s *)rport->private;
+ INIT_LIST_HEAD(&nvfnic_ls_req->list);
+
+ if (!nvfnic_transport_ready(iport, tport)) {
+ FNIC_NVME_DBG(KERN_INFO, fnic,
+ "iport: 0x%x tport: 0x%x transport not ready\n",
+ iport->fcid, tport->fcid);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return -ENOLINK;
+ }
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_NVME_DBG(KERN_ERR, fnic,
+ "Failed to allocate frame to send NVME LS REQ");
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return -ENOMEM;
+ }
+
+ if (fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_NVME_LS,
+ &nvfnic_ls_req->oxid) == FNIC_UNASSIGNED_OXID) {
+ FNIC_FCS_DBG(KERN_INFO, fnic,
+ "0x%x: Failed to allocate OXID to send NVME LS REQ",
+ iport->fcid);
+ mempool_free(frame, fnic->frame_pool);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return -EAGAIN;
+ }
+
+ timer_setup(&nvfnic_ls_req->ls_req_timer, nvfnic_ls_req_timeout,
+ 0UL);
+
+ nvfnic_ls_req->fnic = fnic;
+ nvfnic_ls_req->tport = tport;
+ nvfnic_ls_req->state = FNIC_LS_REQ_CMD_INIT;
+ nvfnic_ls_req->ls_req = ls_req;
+
+ pfc_std_ls_req = (struct fc_std_ls_req *) (frame +
+ FNIC_ETH_FCOE_HDRS_OFFSET);
+ *pfc_std_ls_req = (struct fc_std_ls_req) {
+ .fchdr = {.fh_r_ctl = FC_RCTL_ELS4_REQ,
+ .fh_type = FC_TYPE_NVME,
+ .fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0},
+ .fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID)}
+ };
+
+ hton24(fcid, iport->fcid);
+ FNIC_STD_SET_S_ID(pfc_std_ls_req->fchdr, fcid);
+
+ hton24(fcid, tport->fcid);
+ FNIC_STD_SET_D_ID(pfc_std_ls_req->fchdr, fcid);
+
+ FNIC_STD_SET_OX_ID(pfc_std_ls_req->fchdr, nvfnic_ls_req->oxid);
+
+ pls_req = (struct nvmefc_ls_req *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET +
+ sizeof(struct fc_frame_header));
+ memcpy(pls_req, ls_req->rqstaddr, ls_req->rqstlen);
+
+ FNIC_NVME_DBG(KERN_INFO, fnic,
+ "0x%x: NVME send ls req with oxid: 0x%x type: 0x%02x len: %d",
+ iport->fcid, nvfnic_ls_req->oxid, *((uint8_t *) ls_req->rqstaddr),
+ ls_req->rqstlen);
+
+ list_add_tail(&nvfnic_ls_req->list, &tport->ls_req_list);
+ nvfnic_ls_req->state = FNIC_LS_REQ_CMD_PENDING;
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+ fnic_send_fcoe_frame(iport, frame, frame_size);
+ 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 0;
+}
+
void nvfnic_local_port_delete(struct nvme_fc_local_port *lport)
{
struct fnic_iport_s *iport = (struct fnic_iport_s *) lport->private;
@@ -1544,7 +1652,7 @@ nvme_fc_port_template nvfnic_port = {
.remoteport_delete = nvfnic_remote_port_delete,
.create_queue = nvfnic_create_queue,
.delete_queue = NULL,
- .ls_req = NULL,
+ .ls_req = nvfnic_ls_req_send,
.ls_abort = nvfnic_ls_req_abort,
.fcp_io = nvfnic_fcpio_send,
.fcp_abort = nvfnic_fcpio_abort,
diff --git a/drivers/scsi/fnic/fnic_nvme.h b/drivers/scsi/fnic/fnic_nvme.h
index 22d69f40cd65..8cf9beb8c997 100644
--- a/drivers/scsi/fnic/fnic_nvme.h
+++ b/drivers/scsi/fnic/fnic_nvme.h
@@ -109,6 +109,9 @@ void nvfnic_ls_req_abort(struct nvme_fc_local_port *lport,
struct nvmefc_ls_req *lsreq);
int nvfnic_create_queue(struct nvme_fc_local_port *lport, unsigned int idx,
u16 size, void **handle);
+int nvfnic_ls_req_send(struct nvme_fc_local_port *lport,
+ struct nvme_fc_remote_port *rport,
+ struct nvmefc_ls_req *ls_req);
void nvfnic_ls_req_timeout(struct timer_list *t);
uint16_t nvfnic_alloc_ls_req_oxid(struct fnic_iport_s *iport);
struct nvfnic_ls_req *nvfnic_find_ls_req(struct fnic_tport_s *tport,
--
2.47.1