scsi: qla2xxx: Add Unsolicited LS Request and Response Support for NVMe
authorManish Rangankar <mrangankar@marvell.com>
Mon, 21 Aug 2023 13:00:37 +0000 (18:30 +0530)
committerMartin K. Petersen <martin.petersen@oracle.com>
Mon, 21 Aug 2023 21:45:14 +0000 (17:45 -0400)
Introduce infrastructure in the driver to support the processing of
unsolicited LS (Link Service) requests. This will involve the utilization
of a new pass-up of unsolicited FC-NVMe request IOCB interface. Unsolicited
requests will be submitted to the NVMe transport layer through
nvme_fc_rcv_ls_req(). Any received LS responses, which are sent using
xmt_ls_rsp(), will be forwarded to the firmware through the existing
Pass-Through IOCB interface, responsible for sending FC-NVMe Link Service
requests and responses.

Signed-off-by: Manish Rangankar <mrangankar@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Link: https://lore.kernel.org/r/20230821130045.34850-2-njavali@marvell.com
Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/qla2xxx/qla_dbg.c
drivers/scsi/qla2xxx/qla_dbg.h
drivers/scsi/qla2xxx/qla_def.h
drivers/scsi/qla2xxx/qla_gbl.h
drivers/scsi/qla2xxx/qla_init.c
drivers/scsi/qla2xxx/qla_iocb.c
drivers/scsi/qla2xxx/qla_isr.c
drivers/scsi/qla2xxx/qla_nvme.c
drivers/scsi/qla2xxx/qla_nvme.h
drivers/scsi/qla2xxx/qla_os.c
include/linux/nvme-fc-driver.h

index d7e8454304ceea98feddf77802104a32ba1e0c1c..4d104425146baf07e8f1b61fcae4328cedc17cf5 100644 (file)
@@ -12,9 +12,8 @@
  * ----------------------------------------------------------------------
  * | Module Init and Probe        |       0x0199       |                |
  * | Mailbox commands             |       0x1206       | 0x11a5-0x11ff |
- * | Device Discovery             |       0x2134       | 0x210e-0x2115  |
- * |                              |                    | 0x211c-0x2128  |
- * |                              |                    | 0x212c-0x2134  |
+ * | Device Discovery             |       0x2134       | 0x2112-0x2115  |
+ * |                              |                    | 0x2127-0x2128  |
  * | Queue Command and IO tracing |       0x3074       | 0x300b         |
  * |                              |                    | 0x3027-0x3028  |
  * |                              |                    | 0x303d-0x3041  |
index 70482b55d240ecf720ada456713c32ab22c2c856..54f0a412226f6676327334024d0f5aa34c4d5c2f 100644 (file)
@@ -368,6 +368,7 @@ ql_log_qp(uint32_t, struct qla_qpair *, int32_t, const char *fmt, ...);
 #define ql_dbg_tgt_tmr 0x00001000 /* Target mode task management */
 #define ql_dbg_tgt_dif  0x00000800 /* Target mode dif */
 #define ql_dbg_edif    0x00000400 /* edif and purex debug */
+#define ql_dbg_unsol   0x00000100 /* Unsolicited path debug */
 
 extern int qla27xx_dump_mpi_ram(struct qla_hw_data *, uint32_t, uint32_t *,
        uint32_t, void **);
index 97ecb2158c8ae63329ed60ecd8ec88d04edf9edf..2007d5bb5f9f831e4465a0b735b11737114ec06e 100644 (file)
@@ -346,6 +346,12 @@ struct name_list_extended {
        u8                      sent;
 };
 
+struct qla_nvme_fc_rjt {
+       struct fcnvme_ls_rjt *c;
+       dma_addr_t  cdma;
+       u16 size;
+};
+
 struct els_reject {
        struct fc_els_ls_rjt *c;
        dma_addr_t  cdma;
@@ -503,6 +509,20 @@ struct ct_arg {
        port_id_t       id;
 };
 
+struct qla_nvme_lsrjt_pt_arg {
+       struct fc_port *fcport;
+       u8 opcode;
+       u8 vp_idx;
+       u8 reason;
+       u8 explanation;
+       __le16 nport_handle;
+       u16 control_flags;
+       __le16 ox_id;
+       __le32 xchg_address;
+       u32 tx_byte_count, rx_byte_count;
+       dma_addr_t tx_addr, rx_addr;
+};
+
 /*
  * SRB extensions.
  */
@@ -611,13 +631,16 @@ struct srb_iocb {
                        void *desc;
 
                        /* These are only used with ls4 requests */
-                       int cmd_len;
-                       int rsp_len;
+                       __le32 cmd_len;
+                       __le32 rsp_len;
                        dma_addr_t cmd_dma;
                        dma_addr_t rsp_dma;
                        enum nvmefc_fcp_datadir dir;
                        uint32_t dl;
                        uint32_t timeout_sec;
+                       __le32 exchange_address;
+                       __le16 nport_handle;
+                       __le16 ox_id;
                        struct  list_head   entry;
                } nvme;
                struct {
@@ -707,6 +730,10 @@ typedef struct srb {
        struct fc_port *fcport;
        struct scsi_qla_host *vha;
        unsigned int start_timer:1;
+       unsigned int abort:1;
+       unsigned int aborted:1;
+       unsigned int completed:1;
+       unsigned int unsol_rsp:1;
 
        uint32_t handle;
        uint16_t flags;
@@ -2542,6 +2569,7 @@ enum rscn_addr_format {
 typedef struct fc_port {
        struct list_head list;
        struct scsi_qla_host *vha;
+       struct list_head unsol_ctx_head;
 
        unsigned int conf_compl_supported:1;
        unsigned int deleted:2;
@@ -4802,6 +4830,7 @@ struct qla_hw_data {
        struct els_reject elsrej;
        u8 edif_post_stop_cnt_down;
        struct qla_vp_map *vp_map;
+       struct qla_nvme_fc_rjt lsrjt;
 };
 
 #define RX_ELS_SIZE (roundup(sizeof(struct enode) + ELS_MAX_PAYLOAD, SMP_CACHE_BYTES))
@@ -4834,6 +4863,7 @@ struct active_regions {
  * is variable) starting at "iocb".
  */
 struct purex_item {
+       void *purls_context;
        struct list_head list;
        struct scsi_qla_host *vha;
        void (*process_item)(struct scsi_qla_host *vha,
index 33fba9d6296933608dee464601c6360fa619469c..911e9adf41d36a6892c7456b7f5ad05212dd54e5 100644 (file)
@@ -611,7 +611,11 @@ qla2xxx_msix_rsp_q_hs(int irq, void *dev_id);
 fc_port_t *qla2x00_find_fcport_by_loopid(scsi_qla_host_t *, uint16_t);
 fc_port_t *qla2x00_find_fcport_by_wwpn(scsi_qla_host_t *, u8 *, u8);
 fc_port_t *qla2x00_find_fcport_by_nportid(scsi_qla_host_t *, port_id_t *, u8);
-void __qla_consume_iocb(struct scsi_qla_host *vha, void **pkt, struct rsp_que **rsp);
+void qla24xx_queue_purex_item(scsi_qla_host_t *, struct purex_item *,
+                             void (*process_item)(struct scsi_qla_host *,
+                             struct purex_item *));
+void __qla_consume_iocb(struct scsi_qla_host *, void **, struct rsp_que **);
+void qla2xxx_process_purls_iocb(void **pkt, struct rsp_que **rsp);
 
 /*
  * Global Function Prototypes in qla_sup.c source file.
@@ -674,9 +678,11 @@ extern int qla2xxx_get_vpd_field(scsi_qla_host_t *, char *, char *, size_t);
 extern void qla2xxx_flash_npiv_conf(scsi_qla_host_t *);
 extern int qla24xx_read_fcp_prio_cfg(scsi_qla_host_t *);
 extern int qla2x00_mailbox_passthru(struct bsg_job *bsg_job);
-int __qla_copy_purex_to_buffer(struct scsi_qla_host *vha, void **pkt,
-       struct rsp_que **rsp, u8 *buf, u32 buf_len);
-
+int qla2x00_sys_ld_info(struct bsg_job *bsg_job);
+int __qla_copy_purex_to_buffer(struct scsi_qla_host *, void **,
+       struct rsp_que **, u8 *, u32);
+struct purex_item *qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha,
+       void **pkt, struct rsp_que **rsp, bool is_purls, bool byte_order);
 int qla_mailbox_passthru(scsi_qla_host_t *vha, uint16_t *mbx_in,
                         uint16_t *mbx_out);
 
index 53e76bbee915bb5077488e440abebceb603a5150..27644957ae4eed0fb05d8755cc05f8c1637bdd5b 100644 (file)
@@ -5554,6 +5554,7 @@ qla2x00_alloc_fcport(scsi_qla_host_t *vha, gfp_t flags)
        INIT_WORK(&fcport->reg_work, qla_register_fcport_fn);
        INIT_LIST_HEAD(&fcport->gnl_entry);
        INIT_LIST_HEAD(&fcport->list);
+       INIT_LIST_HEAD(&fcport->unsol_ctx_head);
 
        INIT_LIST_HEAD(&fcport->sess_cmd_list);
        spin_lock_init(&fcport->sess_cmd_lock);
index d0b12c746a8a1113c970dde3f409e057443cca7e..7fbd917f6e1f29a63a4f7613b9fd28df992201cc 100644 (file)
@@ -3771,21 +3771,28 @@ qla_nvme_ls(srb_t *sp, struct pt_ls4_request *cmd_pkt)
        nvme = &sp->u.iocb_cmd;
        cmd_pkt->entry_type = PT_LS4_REQUEST;
        cmd_pkt->entry_count = 1;
-       cmd_pkt->control_flags = cpu_to_le16(CF_LS4_ORIGINATOR << CF_LS4_SHIFT);
-
        cmd_pkt->timeout = cpu_to_le16(nvme->u.nvme.timeout_sec);
-       cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
        cmd_pkt->vp_index = sp->fcport->vha->vp_idx;
 
+       if (sp->unsol_rsp) {
+               cmd_pkt->control_flags =
+                               cpu_to_le16(CF_LS4_RESPONDER << CF_LS4_SHIFT);
+               cmd_pkt->nport_handle = nvme->u.nvme.nport_handle;
+               cmd_pkt->exchange_address = nvme->u.nvme.exchange_address;
+       } else {
+               cmd_pkt->control_flags =
+                               cpu_to_le16(CF_LS4_ORIGINATOR << CF_LS4_SHIFT);
+               cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
+               cmd_pkt->rx_dseg_count = cpu_to_le16(1);
+               cmd_pkt->rx_byte_count = nvme->u.nvme.rsp_len;
+               cmd_pkt->dsd[1].length  = nvme->u.nvme.rsp_len;
+               put_unaligned_le64(nvme->u.nvme.rsp_dma, &cmd_pkt->dsd[1].address);
+       }
+
        cmd_pkt->tx_dseg_count = cpu_to_le16(1);
-       cmd_pkt->tx_byte_count = cpu_to_le32(nvme->u.nvme.cmd_len);
-       cmd_pkt->dsd[0].length = cpu_to_le32(nvme->u.nvme.cmd_len);
+       cmd_pkt->tx_byte_count = nvme->u.nvme.cmd_len;
+       cmd_pkt->dsd[0].length = nvme->u.nvme.cmd_len;
        put_unaligned_le64(nvme->u.nvme.cmd_dma, &cmd_pkt->dsd[0].address);
-
-       cmd_pkt->rx_dseg_count = cpu_to_le16(1);
-       cmd_pkt->rx_byte_count = cpu_to_le32(nvme->u.nvme.rsp_len);
-       cmd_pkt->dsd[1].length = cpu_to_le32(nvme->u.nvme.rsp_len);
-       put_unaligned_le64(nvme->u.nvme.rsp_dma, &cmd_pkt->dsd[1].address);
 }
 
 static void
index 1f42a413b5988375637a6284c03f2665bc5932fb..867025c89909e1ae9f27dd2c8acdd9b6937a0be5 100644 (file)
@@ -823,6 +823,135 @@ qla83xx_handle_8200_aen(scsi_qla_host_t *vha, uint16_t *mb)
        }
 }
 
+/**
+ * qla27xx_copy_multiple_pkt() - Copy over purex/purls packets that can
+ * span over multiple IOCBs.
+ * @vha: SCSI driver HA context
+ * @pkt: ELS packet
+ * @rsp: Response queue
+ * @is_purls: True, for Unsolicited Received FC-NVMe LS rsp IOCB
+ *            false, for Unsolicited Received ELS IOCB
+ * @byte_order: True, to change the byte ordering of iocb payload
+ */
+struct purex_item *
+qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt,
+                         struct rsp_que **rsp, bool is_purls,
+                         bool byte_order)
+{
+       struct purex_entry_24xx *purex = NULL;
+       struct pt_ls4_rx_unsol *purls = NULL;
+       struct rsp_que *rsp_q = *rsp;
+       sts_cont_entry_t *new_pkt;
+       uint16_t no_bytes = 0, total_bytes = 0, pending_bytes = 0;
+       uint16_t buffer_copy_offset = 0, payload_size = 0;
+       uint16_t entry_count, entry_count_remaining;
+       struct purex_item *item;
+       void *iocb_pkt = NULL;
+
+       if (is_purls) {
+               purls = *pkt;
+               total_bytes = (le16_to_cpu(purls->frame_size) & 0x0FFF) -
+                             PURX_ELS_HEADER_SIZE;
+               entry_count = entry_count_remaining = purls->entry_count;
+               payload_size = sizeof(purls->payload);
+       } else {
+               purex = *pkt;
+               total_bytes = (le16_to_cpu(purex->frame_size) & 0x0FFF) -
+                             PURX_ELS_HEADER_SIZE;
+               entry_count = entry_count_remaining = purex->entry_count;
+               payload_size = sizeof(purex->els_frame_payload);
+       }
+
+       pending_bytes = total_bytes;
+       no_bytes = (pending_bytes > payload_size) ? payload_size :
+                  pending_bytes;
+       ql_dbg(ql_dbg_async, vha, 0x509a,
+              "%s LS, frame_size 0x%x, entry count %d\n",
+              (is_purls ? "PURLS" : "FPIN"), total_bytes, entry_count);
+
+       item = qla24xx_alloc_purex_item(vha, total_bytes);
+       if (!item)
+               return item;
+
+       iocb_pkt = &item->iocb;
+
+       if (is_purls)
+               memcpy(iocb_pkt, &purls->payload[0], no_bytes);
+       else
+               memcpy(iocb_pkt, &purex->els_frame_payload[0], no_bytes);
+       buffer_copy_offset += no_bytes;
+       pending_bytes -= no_bytes;
+       --entry_count_remaining;
+
+       if (is_purls)
+               ((response_t *)purls)->signature = RESPONSE_PROCESSED;
+       else
+               ((response_t *)purex)->signature = RESPONSE_PROCESSED;
+       wmb();
+
+       do {
+               while ((total_bytes > 0) && (entry_count_remaining > 0)) {
+                       if (rsp_q->ring_ptr->signature == RESPONSE_PROCESSED) {
+                               ql_dbg(ql_dbg_async, vha, 0x5084,
+                                      "Ran out of IOCBs, partial data 0x%x\n",
+                                      buffer_copy_offset);
+                               cpu_relax();
+                               continue;
+                       }
+
+                       new_pkt = (sts_cont_entry_t *)rsp_q->ring_ptr;
+                       *pkt = new_pkt;
+
+                       if (new_pkt->entry_type != STATUS_CONT_TYPE) {
+                               ql_log(ql_log_warn, vha, 0x507a,
+                                      "Unexpected IOCB type, partial data 0x%x\n",
+                                      buffer_copy_offset);
+                               break;
+                       }
+
+                       rsp_q->ring_index++;
+                       if (rsp_q->ring_index == rsp_q->length) {
+                               rsp_q->ring_index = 0;
+                               rsp_q->ring_ptr = rsp_q->ring;
+                       } else {
+                               rsp_q->ring_ptr++;
+                       }
+                       no_bytes = (pending_bytes > sizeof(new_pkt->data)) ?
+                               sizeof(new_pkt->data) : pending_bytes;
+                       if ((buffer_copy_offset + no_bytes) <= total_bytes) {
+                               memcpy(((uint8_t *)iocb_pkt + buffer_copy_offset),
+                                      new_pkt->data, no_bytes);
+                               buffer_copy_offset += no_bytes;
+                               pending_bytes -= no_bytes;
+                               --entry_count_remaining;
+                       } else {
+                               ql_log(ql_log_warn, vha, 0x5044,
+                                      "Attempt to copy more that we got, optimizing..%x\n",
+                                      buffer_copy_offset);
+                               memcpy(((uint8_t *)iocb_pkt + buffer_copy_offset),
+                                      new_pkt->data,
+                                      total_bytes - buffer_copy_offset);
+                       }
+
+                       ((response_t *)new_pkt)->signature = RESPONSE_PROCESSED;
+                       wmb();
+               }
+
+               if (pending_bytes != 0 || entry_count_remaining != 0) {
+                       ql_log(ql_log_fatal, vha, 0x508b,
+                              "Dropping partial FPIN, underrun bytes = 0x%x, entry cnts 0x%x\n",
+                              total_bytes, entry_count_remaining);
+                       qla24xx_free_purex_item(item);
+                       return NULL;
+               }
+       } while (entry_count_remaining > 0);
+
+       if (byte_order)
+               host_to_fcp_swap((uint8_t *)&item->iocb, total_bytes);
+
+       return item;
+}
+
 int
 qla2x00_is_a_vp_did(scsi_qla_host_t *vha, uint32_t rscn_entry)
 {
@@ -958,7 +1087,7 @@ initialize_purex_header:
        return item;
 }
 
-static void
+void
 qla24xx_queue_purex_item(scsi_qla_host_t *vha, struct purex_item *pkt,
                         void (*process_item)(struct scsi_qla_host *vha,
                                              struct purex_item *pkt))
@@ -3811,6 +3940,7 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
        struct qla_hw_data *ha = vha->hw;
        struct purex_entry_24xx *purex_entry;
        struct purex_item *pure_item;
+       struct pt_ls4_rx_unsol *p;
        u16 rsp_in = 0, cur_ring_index;
        int is_shadow_hba;
 
@@ -3983,7 +4113,19 @@ process_err:
                        qla28xx_sa_update_iocb_entry(vha, rsp->req,
                                (struct sa_update_28xx *)pkt);
                        break;
-
+               case PT_LS4_UNSOL:
+                       p = (void *)pkt;
+                       if (qla_chk_cont_iocb_avail(vha, rsp, (response_t *)pkt, rsp_in)) {
+                               rsp->ring_ptr = (response_t *)pkt;
+                               rsp->ring_index = cur_ring_index;
+
+                               ql_dbg(ql_dbg_init, vha, 0x2124,
+                                      "Defer processing UNSOL LS req opcode %#x...\n",
+                                      p->payload[0]);
+                               return;
+                       }
+                       qla2xxx_process_purls_iocb((void **)&pkt, &rsp);
+                       break;
                default:
                        /* Type Not Supported. */
                        ql_dbg(ql_dbg_async, vha, 0x5042,
index 9941b38eac93c5a59ef8084f24b5c46a21deea91..1a31e877e6cb62f1ca669d17b4d9c653fb05240f 100644 (file)
 #include <linux/blk-mq.h>
 
 static struct nvme_fc_port_template qla_nvme_fc_transport;
+static int qla_nvme_ls_reject_iocb(struct scsi_qla_host *vha,
+                                  struct qla_qpair *qp,
+                                  struct qla_nvme_lsrjt_pt_arg *a,
+                                  bool is_xchg_terminate);
+
+struct qla_nvme_unsol_ctx {
+       struct list_head elem;
+       struct scsi_qla_host *vha;
+       struct fc_port *fcport;
+       struct srb *sp;
+       struct nvmefc_ls_rsp lsrsp;
+       struct nvmefc_ls_rsp *fd_rsp;
+       struct work_struct lsrsp_work;
+       struct work_struct abort_work;
+       __le32 exchange_address;
+       __le16 nport_handle;
+       __le16 ox_id;
+       int comp_status;
+       spinlock_t cmd_lock;
+};
 
 int qla_nvme_register_remote(struct scsi_qla_host *vha, struct fc_port *fcport)
 {
@@ -216,6 +236,55 @@ static void qla_nvme_sp_ls_done(srb_t *sp, int res)
        schedule_work(&priv->ls_work);
 }
 
+static void qla_nvme_release_lsrsp_cmd_kref(struct kref *kref)
+{
+       struct srb *sp = container_of(kref, struct srb, cmd_kref);
+       struct qla_nvme_unsol_ctx *uctx = sp->priv;
+       struct nvmefc_ls_rsp *fd_rsp;
+       unsigned long flags;
+
+       if (!uctx) {
+               qla2x00_rel_sp(sp);
+               return;
+       }
+
+       spin_lock_irqsave(&uctx->cmd_lock, flags);
+       uctx->sp = NULL;
+       sp->priv = NULL;
+       spin_unlock_irqrestore(&uctx->cmd_lock, flags);
+
+       fd_rsp = uctx->fd_rsp;
+
+       list_del(&uctx->elem);
+
+       fd_rsp->done(fd_rsp);
+       kfree(uctx);
+       qla2x00_rel_sp(sp);
+}
+
+static void qla_nvme_lsrsp_complete(struct work_struct *work)
+{
+       struct qla_nvme_unsol_ctx *uctx =
+               container_of(work, struct qla_nvme_unsol_ctx, lsrsp_work);
+
+       kref_put(&uctx->sp->cmd_kref, qla_nvme_release_lsrsp_cmd_kref);
+}
+
+static void qla_nvme_sp_lsrsp_done(srb_t *sp, int res)
+{
+       struct qla_nvme_unsol_ctx *uctx = sp->priv;
+
+       if (WARN_ON_ONCE(kref_read(&sp->cmd_kref) == 0))
+               return;
+
+       if (res)
+               res = -EINVAL;
+
+       uctx->comp_status = res;
+       INIT_WORK(&uctx->lsrsp_work, qla_nvme_lsrsp_complete);
+       schedule_work(&uctx->lsrsp_work);
+}
+
 /* it assumed that QPair lock is held. */
 static void qla_nvme_sp_done(srb_t *sp, int res)
 {
@@ -288,6 +357,92 @@ out:
        kref_put(&sp->cmd_kref, sp->put_fn);
 }
 
+static int qla_nvme_xmt_ls_rsp(struct nvme_fc_local_port *lport,
+                              struct nvme_fc_remote_port *rport,
+                              struct nvmefc_ls_rsp *fd_resp)
+{
+       struct qla_nvme_unsol_ctx *uctx = container_of(fd_resp,
+                               struct qla_nvme_unsol_ctx, lsrsp);
+       struct qla_nvme_rport *qla_rport = rport->private;
+       fc_port_t *fcport = qla_rport->fcport;
+       struct scsi_qla_host *vha = uctx->vha;
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_nvme_lsrjt_pt_arg a;
+       struct srb_iocb *nvme;
+       srb_t *sp;
+       int rval = QLA_FUNCTION_FAILED;
+       uint8_t cnt = 0;
+
+       if (!fcport || fcport->deleted)
+               goto out;
+
+       if (!ha->flags.fw_started)
+               goto out;
+
+       /* Alloc SRB structure */
+       sp = qla2x00_get_sp(vha, fcport, GFP_ATOMIC);
+       if (!sp)
+               goto out;
+
+       sp->type = SRB_NVME_LS;
+       sp->name = "nvme_ls";
+       sp->done = qla_nvme_sp_lsrsp_done;
+       sp->put_fn = qla_nvme_release_lsrsp_cmd_kref;
+       sp->priv = (void *)uctx;
+       sp->unsol_rsp = 1;
+       uctx->sp = sp;
+       spin_lock_init(&uctx->cmd_lock);
+       nvme = &sp->u.iocb_cmd;
+       uctx->fd_rsp = fd_resp;
+       nvme->u.nvme.desc = fd_resp;
+       nvme->u.nvme.dir = 0;
+       nvme->u.nvme.dl = 0;
+       nvme->u.nvme.timeout_sec = 0;
+       nvme->u.nvme.cmd_dma = fd_resp->rspdma;
+       nvme->u.nvme.cmd_len = fd_resp->rsplen;
+       nvme->u.nvme.rsp_len = 0;
+       nvme->u.nvme.rsp_dma = 0;
+       nvme->u.nvme.exchange_address = uctx->exchange_address;
+       nvme->u.nvme.nport_handle = uctx->nport_handle;
+       nvme->u.nvme.ox_id = uctx->ox_id;
+       dma_sync_single_for_device(&ha->pdev->dev, nvme->u.nvme.cmd_dma,
+                                  le32_to_cpu(fd_resp->rsplen), DMA_TO_DEVICE);
+
+       ql_dbg(ql_dbg_unsol, vha, 0x2122,
+              "Unsol lsreq portid=%06x %8phC exchange_address 0x%x ox_id 0x%x hdl 0x%x\n",
+              fcport->d_id.b24, fcport->port_name, uctx->exchange_address,
+              uctx->ox_id, uctx->nport_handle);
+retry:
+       rval = qla2x00_start_sp(sp);
+       switch (rval) {
+       case QLA_SUCCESS:
+               break;
+       case EAGAIN:
+               msleep(PURLS_MSLEEP_INTERVAL);
+               cnt++;
+               if (cnt < PURLS_RETRY_COUNT)
+                       goto retry;
+
+               fallthrough;
+       default:
+               ql_dbg(ql_log_warn, vha, 0x2123,
+                      "Failed to xmit Unsol ls response = %d\n", rval);
+               rval = -EIO;
+               qla2x00_rel_sp(sp);
+               goto out;
+       }
+
+       return 0;
+out:
+       memset((void *)&a, 0, sizeof(a));
+       a.vp_idx = vha->vp_idx;
+       a.nport_handle = uctx->nport_handle;
+       a.xchg_address = uctx->exchange_address;
+       qla_nvme_ls_reject_iocb(vha, ha->base_qpair, &a, true);
+       kfree(uctx);
+       return rval;
+}
+
 static void qla_nvme_ls_abort(struct nvme_fc_local_port *lport,
     struct nvme_fc_remote_port *rport, struct nvmefc_ls_req *fd)
 {
@@ -355,7 +510,7 @@ static int qla_nvme_ls_req(struct nvme_fc_local_port *lport,
        nvme->u.nvme.timeout_sec = fd->timeout;
        nvme->u.nvme.cmd_dma = fd->rqstdma;
        dma_sync_single_for_device(&ha->pdev->dev, nvme->u.nvme.cmd_dma,
-           fd->rqstlen, DMA_TO_DEVICE);
+           le32_to_cpu(fd->rqstlen), DMA_TO_DEVICE);
 
        rval = qla2x00_start_sp(sp);
        if (rval != QLA_SUCCESS) {
@@ -720,6 +875,7 @@ static struct nvme_fc_port_template qla_nvme_fc_transport = {
        .ls_abort       = qla_nvme_ls_abort,
        .fcp_io         = qla_nvme_post_cmd,
        .fcp_abort      = qla_nvme_fcp_abort,
+       .xmt_ls_rsp     = qla_nvme_xmt_ls_rsp,
        .map_queues     = qla_nvme_map_queues,
        .max_hw_queues  = DEF_NVME_HW_QUEUES,
        .max_sgl_segments = 1024,
@@ -924,3 +1080,246 @@ inline void qla_wait_nvme_release_cmd_kref(srb_t *orig_sp)
                return;
        kref_put(&orig_sp->cmd_kref, orig_sp->put_fn);
 }
+
+static void qla_nvme_fc_format_rjt(void *buf, u8 ls_cmd, u8 reason,
+                                  u8 explanation, u8 vendor)
+{
+       struct fcnvme_ls_rjt *rjt = buf;
+
+       rjt->w0.ls_cmd = FCNVME_LSDESC_RQST;
+       rjt->desc_list_len = fcnvme_lsdesc_len(sizeof(struct fcnvme_ls_rjt));
+       rjt->rqst.desc_tag = cpu_to_be32(FCNVME_LSDESC_RQST);
+       rjt->rqst.desc_len =
+               fcnvme_lsdesc_len(sizeof(struct fcnvme_lsdesc_rqst));
+       rjt->rqst.w0.ls_cmd = ls_cmd;
+       rjt->rjt.desc_tag = cpu_to_be32(FCNVME_LSDESC_RJT);
+       rjt->rjt.desc_len = fcnvme_lsdesc_len(sizeof(struct fcnvme_lsdesc_rjt));
+       rjt->rjt.reason_code = reason;
+       rjt->rjt.reason_explanation = explanation;
+       rjt->rjt.vendor = vendor;
+}
+
+static void qla_nvme_lsrjt_pt_iocb(struct scsi_qla_host *vha,
+                                  struct pt_ls4_request *lsrjt_iocb,
+                                  struct qla_nvme_lsrjt_pt_arg *a)
+{
+       lsrjt_iocb->entry_type = PT_LS4_REQUEST;
+       lsrjt_iocb->entry_count = 1;
+       lsrjt_iocb->sys_define = 0;
+       lsrjt_iocb->entry_status = 0;
+       lsrjt_iocb->handle = QLA_SKIP_HANDLE;
+       lsrjt_iocb->nport_handle = a->nport_handle;
+       lsrjt_iocb->exchange_address = a->xchg_address;
+       lsrjt_iocb->vp_index = a->vp_idx;
+
+       lsrjt_iocb->control_flags = cpu_to_le16(a->control_flags);
+
+       put_unaligned_le64(a->tx_addr, &lsrjt_iocb->dsd[0].address);
+       lsrjt_iocb->dsd[0].length = cpu_to_le32(a->tx_byte_count);
+       lsrjt_iocb->tx_dseg_count = cpu_to_le16(1);
+       lsrjt_iocb->tx_byte_count = cpu_to_le32(a->tx_byte_count);
+
+       put_unaligned_le64(a->rx_addr, &lsrjt_iocb->dsd[1].address);
+       lsrjt_iocb->dsd[1].length = 0;
+       lsrjt_iocb->rx_dseg_count = 0;
+       lsrjt_iocb->rx_byte_count = 0;
+}
+
+static int
+qla_nvme_ls_reject_iocb(struct scsi_qla_host *vha, struct qla_qpair *qp,
+                       struct qla_nvme_lsrjt_pt_arg *a, bool is_xchg_terminate)
+{
+       struct pt_ls4_request *lsrjt_iocb;
+
+       lsrjt_iocb = __qla2x00_alloc_iocbs(qp, NULL);
+       if (!lsrjt_iocb) {
+               ql_log(ql_log_warn, vha, 0x210e,
+                      "qla2x00_alloc_iocbs failed.\n");
+               return QLA_FUNCTION_FAILED;
+       }
+
+       if (!is_xchg_terminate) {
+               qla_nvme_fc_format_rjt((void *)vha->hw->lsrjt.c, a->opcode,
+                                      a->reason, a->explanation, 0);
+
+               a->tx_byte_count = sizeof(struct fcnvme_ls_rjt);
+               a->tx_addr = vha->hw->lsrjt.cdma;
+               a->control_flags = CF_LS4_RESPONDER << CF_LS4_SHIFT;
+
+               ql_dbg(ql_dbg_unsol, vha, 0x211f,
+                      "Sending nvme fc ls reject ox_id %04x op %04x\n",
+                      a->ox_id, a->opcode);
+               ql_dump_buffer(ql_dbg_unsol + ql_dbg_verbose, vha, 0x210f,
+                              vha->hw->lsrjt.c, sizeof(*vha->hw->lsrjt.c));
+       } else {
+               a->tx_byte_count = 0;
+               a->control_flags = CF_LS4_RESPONDER_TERM << CF_LS4_SHIFT;
+               ql_dbg(ql_dbg_unsol, vha, 0x2110,
+                      "Terminate nvme ls xchg 0x%x\n", a->xchg_address);
+       }
+
+       qla_nvme_lsrjt_pt_iocb(vha, lsrjt_iocb, a);
+       /* flush iocb to mem before notifying hw doorbell */
+       wmb();
+       qla2x00_start_iocbs(vha, qp->req);
+       return 0;
+}
+
+/*
+ * qla2xxx_process_purls_pkt() - Pass-up Unsolicited
+ * Received FC-NVMe Link Service pkt to nvme_fc_rcv_ls_req().
+ * LLDD need to provide memory for response buffer, which
+ * will be used to reference the exchange corresponding
+ * to the LS when issuing an ls response. LLDD will have to free
+ * response buffer in lport->ops->xmt_ls_rsp().
+ *
+ * @vha: SCSI qla host
+ * @item: ptr to purex_item
+ */
+static void
+qla2xxx_process_purls_pkt(struct scsi_qla_host *vha, struct purex_item *item)
+{
+       struct qla_nvme_unsol_ctx *uctx = item->purls_context;
+       fc_port_t *fcport = uctx->fcport;
+       struct qla_nvme_lsrjt_pt_arg a;
+       int ret;
+
+       ret = nvme_fc_rcv_ls_req(fcport->nvme_remote_port, &uctx->lsrsp,
+                                &item->iocb, item->size);
+       if (ret) {
+               ql_dbg(ql_dbg_unsol, vha, 0x2125, "NVMe tranport ls_req failed\n");
+               memset((void *)&a, 0, sizeof(a));
+               a.vp_idx = vha->vp_idx;
+               a.nport_handle = uctx->nport_handle;
+               a.xchg_address = uctx->exchange_address;
+               qla_nvme_ls_reject_iocb(vha, vha->hw->base_qpair, &a, true);
+               list_del(&uctx->elem);
+               kfree(uctx);
+       }
+}
+
+static scsi_qla_host_t *
+qla2xxx_get_vha_from_vp_idx(struct qla_hw_data *ha, uint16_t vp_index)
+{
+       scsi_qla_host_t *base_vha, *vha, *tvp;
+       unsigned long flags;
+
+       base_vha = pci_get_drvdata(ha->pdev);
+
+       if (!vp_index && !ha->num_vhosts)
+               return base_vha;
+
+       spin_lock_irqsave(&ha->vport_slock, flags);
+       list_for_each_entry_safe(vha, tvp, &ha->vp_list, list) {
+               if (vha->vp_idx == vp_index) {
+                       spin_unlock_irqrestore(&ha->vport_slock, flags);
+                       return vha;
+               }
+       }
+       spin_unlock_irqrestore(&ha->vport_slock, flags);
+
+       return NULL;
+}
+
+void qla2xxx_process_purls_iocb(void **pkt, struct rsp_que **rsp)
+{
+       struct nvme_fc_remote_port *rport;
+       struct qla_nvme_rport *qla_rport;
+       struct qla_nvme_lsrjt_pt_arg a;
+       struct pt_ls4_rx_unsol *p = *pkt;
+       struct qla_nvme_unsol_ctx *uctx;
+       struct rsp_que *rsp_q = *rsp;
+       struct qla_hw_data *ha;
+       scsi_qla_host_t *vha;
+       fc_port_t *fcport = NULL;
+       struct purex_item *item;
+       port_id_t d_id = {0};
+       port_id_t id = {0};
+       u8 *opcode;
+       bool xmt_reject = false;
+
+       ha = rsp_q->hw;
+
+       vha = qla2xxx_get_vha_from_vp_idx(ha, p->vp_index);
+       if (!vha) {
+               ql_log(ql_log_warn, NULL, 0x2110, "Invalid vp index %d\n", p->vp_index);
+               WARN_ON_ONCE(1);
+               return;
+       }
+
+       memset((void *)&a, 0, sizeof(a));
+       opcode = (u8 *)&p->payload[0];
+       a.opcode = opcode[3];
+       a.vp_idx = p->vp_index;
+       a.nport_handle = p->nport_handle;
+       a.ox_id = p->ox_id;
+       a.xchg_address = p->exchange_address;
+
+       id.b.domain = p->s_id.domain;
+       id.b.area   = p->s_id.area;
+       id.b.al_pa  = p->s_id.al_pa;
+       d_id.b.domain = p->d_id[2];
+       d_id.b.area   = p->d_id[1];
+       d_id.b.al_pa  = p->d_id[0];
+
+       fcport = qla2x00_find_fcport_by_nportid(vha, &id, 0);
+       if (!fcport) {
+               ql_dbg(ql_dbg_unsol, vha, 0x211e,
+                      "Failed to find sid=%06x did=%06x\n",
+                      id.b24, d_id.b24);
+               a.reason = FCNVME_RJT_RC_INV_ASSOC;
+               a.explanation = FCNVME_RJT_EXP_NONE;
+               xmt_reject = true;
+               goto out;
+       }
+       rport = fcport->nvme_remote_port;
+       qla_rport = rport->private;
+
+       item = qla27xx_copy_multiple_pkt(vha, pkt, rsp, true, false);
+       if (!item) {
+               a.reason = FCNVME_RJT_RC_LOGIC;
+               a.explanation = FCNVME_RJT_EXP_NONE;
+               xmt_reject = true;
+               goto out;
+       }
+
+       uctx = kzalloc(sizeof(*uctx), GFP_ATOMIC);
+       if (!uctx) {
+               ql_log(ql_log_info, vha, 0x2126, "Failed allocate memory\n");
+               a.reason = FCNVME_RJT_RC_LOGIC;
+               a.explanation = FCNVME_RJT_EXP_NONE;
+               xmt_reject = true;
+               kfree(item);
+               goto out;
+       }
+
+       uctx->vha = vha;
+       uctx->fcport = fcport;
+       uctx->exchange_address = p->exchange_address;
+       uctx->nport_handle = p->nport_handle;
+       uctx->ox_id = p->ox_id;
+       qla_rport->uctx = uctx;
+       INIT_LIST_HEAD(&uctx->elem);
+       list_add_tail(&uctx->elem, &fcport->unsol_ctx_head);
+       item->purls_context = (void *)uctx;
+
+       ql_dbg(ql_dbg_unsol, vha, 0x2121,
+              "PURLS OP[%01x] size %d xchg addr 0x%x portid %06x\n",
+              item->iocb.iocb[3], item->size, uctx->exchange_address,
+              fcport->d_id.b24);
+       /* +48    0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
+        * ----- -----------------------------------------------
+        * 0000: 00 00 00 05 28 00 00 00 07 00 00 00 08 00 00 00
+        * 0010: ab ec 0f cc 00 00 8d 7d 05 00 00 00 10 00 00 00
+        * 0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+        */
+       ql_dump_buffer(ql_dbg_unsol + ql_dbg_verbose, vha, 0x2120,
+                      &item->iocb, item->size);
+
+       qla24xx_queue_purex_item(vha, item, qla2xxx_process_purls_pkt);
+out:
+       if (xmt_reject) {
+               qla_nvme_ls_reject_iocb(vha, (*rsp)->qpair, &a, false);
+               __qla_consume_iocb(vha, pkt, rsp);
+       }
+}
index d299478371b21b3c3f11f2a23fb3494a3d697cec..a253ac55171bdda8cdc75cdc95cfde6418fdc11c 100644 (file)
@@ -21,6 +21,7 @@
 #define Q2T_NVME_NUM_TAGS 2048
 #define QLA_MAX_FC_SEGMENTS 64
 
+struct qla_nvme_unsol_ctx;
 struct scsi_qla_host;
 struct qla_hw_data;
 struct req_que;
@@ -37,6 +38,7 @@ struct nvme_private {
 
 struct qla_nvme_rport {
        struct fc_port *fcport;
+       struct qla_nvme_unsol_ctx *uctx;
 };
 
 #define COMMAND_NVME    0x88            /* Command Type FC-NVMe IOCB */
@@ -75,6 +77,9 @@ struct cmd_nvme {
        struct dsd64 nvme_dsd;
 };
 
+#define PURLS_MSLEEP_INTERVAL  1
+#define PURLS_RETRY_COUNT      5
+
 #define PT_LS4_REQUEST 0x89    /* Link Service pass-through IOCB (request) */
 struct pt_ls4_request {
        uint8_t entry_type;
@@ -118,21 +123,19 @@ struct pt_ls4_rx_unsol {
        __le32  exchange_address;
        uint8_t d_id[3];
        uint8_t r_ctl;
-       be_id_t s_id;
+       le_id_t s_id;
        uint8_t cs_ctl;
        uint8_t f_ctl[3];
        uint8_t type;
        __le16  seq_cnt;
        uint8_t df_ctl;
        uint8_t seq_id;
-       __le16  rx_id;
-       __le16  ox_id;
-       __le32  param;
-       __le32  desc0;
+       __le16 rx_id;
+       __le16 ox_id;
+       __le32  desc0;
 #define PT_LS4_PAYLOAD_OFFSET 0x2c
 #define PT_LS4_FIRST_PACKET_LEN 20
-       __le32  desc_len;
-       __le32  payload[3];
+       __le32 payload[5];
 };
 
 /*
index f31d0ead891927490a6759b5116fe8ba3ce36f35..aa492489a4b84cf785c6ea917c1d7c3c0c71919f 100644 (file)
@@ -4457,8 +4457,9 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
 
        ha->elsrej.size = sizeof(struct fc_els_ls_rjt) + 16;
        ha->elsrej.c = dma_alloc_coherent(&ha->pdev->dev,
-           ha->elsrej.size, &ha->elsrej.cdma, GFP_KERNEL);
-
+                                         ha->elsrej.size,
+                                         &ha->elsrej.cdma,
+                                         GFP_KERNEL);
        if (!ha->elsrej.c) {
                ql_dbg_pci(ql_dbg_init, ha->pdev, 0xffff,
                    "Alloc failed for els reject cmd.\n");
@@ -4467,8 +4468,21 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
        ha->elsrej.c->er_cmd = ELS_LS_RJT;
        ha->elsrej.c->er_reason = ELS_RJT_LOGIC;
        ha->elsrej.c->er_explan = ELS_EXPL_UNAB_DATA;
+
+       ha->lsrjt.size = sizeof(struct fcnvme_ls_rjt);
+       ha->lsrjt.c = dma_alloc_coherent(&ha->pdev->dev, ha->lsrjt.size,
+                       &ha->lsrjt.cdma, GFP_KERNEL);
+       if (!ha->lsrjt.c) {
+               ql_dbg_pci(ql_dbg_init, ha->pdev, 0xffff,
+                          "Alloc failed for nvme fc reject cmd.\n");
+               goto fail_lsrjt;
+       }
+
        return 0;
 
+fail_lsrjt:
+       dma_free_coherent(&ha->pdev->dev, ha->elsrej.size,
+                         ha->elsrej.c, ha->elsrej.cdma);
 fail_elsrej:
        dma_pool_destroy(ha->purex_dma_pool);
 fail_flt:
@@ -4998,6 +5012,12 @@ qla2x00_mem_free(struct qla_hw_data *ha)
                ha->elsrej.c = NULL;
        }
 
+       if (ha->lsrjt.c) {
+               dma_free_coherent(&ha->pdev->dev, ha->lsrjt.size, ha->lsrjt.c,
+                                 ha->lsrjt.cdma);
+               ha->lsrjt.c = NULL;
+       }
+
        ha->init_cb = NULL;
        ha->init_cb_dma = 0;
 
index 4109f1bd61285b3ada18af6ec26f34380162cff4..f6ef8cf5d7741f924f92600ad7dbb9bcf5fb7227 100644 (file)
 struct nvmefc_ls_req {
        void                    *rqstaddr;
        dma_addr_t              rqstdma;
-       u32                     rqstlen;
+       __le32                  rqstlen;
        void                    *rspaddr;
        dma_addr_t              rspdma;
-       u32                     rsplen;
+       __le32                  rsplen;
        u32                     timeout;
 
        void                    *private;
@@ -120,7 +120,7 @@ struct nvmefc_ls_req {
 struct nvmefc_ls_rsp {
        void            *rspbuf;
        dma_addr_t      rspdma;
-       u16             rsplen;
+       __le32          rsplen;
 
        void (*done)(struct nvmefc_ls_rsp *rsp);
        void            *nvme_fc_private;       /* LLDD is not to access !! */