Home Home > GIT Browse
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2017-06-02 15:35:00 +0200
committerTakashi Iwai <tiwai@suse.de>2017-06-02 15:35:00 +0200
commit175675bcdee1e38c99f3b5c7c361a5225cf78743 (patch)
tree15ee82d89f43aa5f0c88b4cac7bb8a2bc848e835
parent9e92120115cc7e66ebac3d18344f2896a865346d (diff)
parentd129feca7130ea36aed530a34945ca1b154e9b0a (diff)
Merge branch 'users/jthumshirn/SLE12-SP3/for-next' into SLE12-SP3rpm-4.4.70-1
Pull lpfc fixes from Johannes Thumshirn (bsc#1042257).
-rw-r--r--patches.drivers/0444-nvme_fc-fix-confused-queue-size-handling.patch58
-rw-r--r--patches.drivers/0445-nvme_fc-on-lldd-transport-io-error-terminate-associa.patch91
-rw-r--r--patches.drivers/0446-lpfc-Fix-defects-reported-by-Coverity-Scan.patch132
-rw-r--r--patches.drivers/0447-lpfc-Add-auto-EQ-delay-logic.patch483
-rw-r--r--series.conf4
5 files changed, 768 insertions, 0 deletions
diff --git a/patches.drivers/0444-nvme_fc-fix-confused-queue-size-handling.patch b/patches.drivers/0444-nvme_fc-fix-confused-queue-size-handling.patch
new file mode 100644
index 0000000000..9928ec19f2
--- /dev/null
+++ b/patches.drivers/0444-nvme_fc-fix-confused-queue-size-handling.patch
@@ -0,0 +1,58 @@
+From ee284e99a64682f95d3660e08862e2108dd3182a Mon Sep 17 00:00:00 2001
+From: James Smart <jsmart2021@gmail.com>
+Date: Thu, 1 Jun 2017 22:56:13 -0700
+Subject: nvme_fc: fix confused queue size handling
+References: bsc#1042268
+Patch-mainline: Submitted, http://lists.infradead.org/pipermail/linux-nvme/2017-June/010659.html
+
+The code gets confused whether to use queue_size (the 1's) value
+or the ctrl.sqsize (the 0's value) in places.
+
+In init_io_queues - should use the 1's value.
+if queue size reduced due to MQES or MAXCMD, need to reduce both
+queue_size and ctrl.sqsize.
+
+Signed-off-by: Dick Kennedy <dick.kennedy@broadcom.com>
+Signed-off-by: James Smart <james.smart@broadcom.com>
+Signed-off-by: Johannes Thumshirn <jthumshirn@suse.de>
+---
+ drivers/nvme/host/fc.c | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c
+index 111ea89..d87d23a 100644
+--- a/drivers/nvme/host/fc.c
++++ b/drivers/nvme/host/fc.c
+@@ -1934,7 +1934,7 @@ nvme_fc_init_io_queues(struct nvme_fc_ctrl *ctrl)
+ int i;
+
+ for (i = 1; i < ctrl->queue_count; i++)
+- nvme_fc_init_queue(ctrl, i, ctrl->ctrl.sqsize);
++ nvme_fc_init_queue(ctrl, i, ctrl->ctrl.opts->queue_size);
+ }
+
+ static void
+@@ -2586,8 +2586,10 @@ nvme_fc_create_association(struct nvme_fc_ctrl *ctrl)
+ goto out_disconnect_admin_queue;
+ }
+
+- ctrl->ctrl.sqsize =
+- min_t(int, NVME_CAP_MQES(ctrl->cap) + 1, ctrl->ctrl.sqsize);
++ if (NVME_CAP_MQES(ctrl->cap) < ctrl->ctrl.sqsize) {
++ ctrl->ctrl.sqsize = NVME_CAP_MQES(ctrl->cap);
++ opts->queue_size = ctrl->ctrl.sqsize + 1;
++ }
+
+ ret = nvme_enable_ctrl(&ctrl->ctrl, ctrl->cap);
+ if (ret)
+@@ -2621,6 +2623,7 @@ nvme_fc_create_association(struct nvme_fc_ctrl *ctrl)
+ "to queue_size\n",
+ opts->queue_size, ctrl->ctrl.maxcmd);
+ opts->queue_size = ctrl->ctrl.maxcmd;
++ ctrl->ctrl.sqsize = opts->queue_size - 1;
+ }
+
+ ret = nvme_fc_init_aen_ops(ctrl);
+--
+1.8.5.6
+
diff --git a/patches.drivers/0445-nvme_fc-on-lldd-transport-io-error-terminate-associa.patch b/patches.drivers/0445-nvme_fc-on-lldd-transport-io-error-terminate-associa.patch
new file mode 100644
index 0000000000..4b4cf55a0c
--- /dev/null
+++ b/patches.drivers/0445-nvme_fc-on-lldd-transport-io-error-terminate-associa.patch
@@ -0,0 +1,91 @@
+From 3c9b50935103db6e607b716accf2c18120802e33 Mon Sep 17 00:00:00 2001
+From: James Smart <jsmart2021@gmail.com>
+Date: Thu, 1 Jun 2017 22:56:55 -0700
+Subject: nvme_fc: on lldd/transport io error, terminate association
+References: bsc#1042268
+Patch-mainline: Submitted, http://lists.infradead.org/pipermail/linux-nvme/2017-June/010660.html
+
+Per FC-NVME, when lldd or transport detects an i/o error, the
+connection must be terminated, which in turn requires the association
+to be termianted. Currently the transport simply creates a nvme
+completion status of transport error and returns the io. The FC-NVME
+spec makes the mandate as initiator and host, depending on the error,
+can get out of sync on outstanding io counts (sqhd/sqtail).
+
+Implement the association teardown on lldd or transport detected
+errors.
+
+Signed-off-by: James Smart <james.smart@broadcom.com>
+Signed-off-by: Johannes Thumshirn <jthumshirn@suse.de>
+---
+ drivers/nvme/host/fc.c | 19 +++++++++++++++++--
+ 1 file changed, 17 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c
+index d87d23a..950499b 100644
+--- a/drivers/nvme/host/fc.c
++++ b/drivers/nvme/host/fc.c
+@@ -1388,6 +1388,7 @@ nvme_fc_xmt_disconnect_assoc(struct nvme_fc_ctrl *ctrl)
+ /* *********************** NVME Ctrl Routines **************************** */
+
+ static void __nvme_fc_final_op_cleanup(struct request *rq);
++static void nvme_fc_error_recovery(struct nvme_fc_ctrl *ctrl, char *errmsg);
+
+ static int
+ nvme_fc_reinit_request(void *data, struct request *rq)
+@@ -1516,7 +1517,7 @@ nvme_fc_fcpio_done(struct nvmefc_fcp_req *req)
+ struct nvme_command *sqe = &op->cmd_iu.sqe;
+ __le16 status = cpu_to_le16(NVME_SC_SUCCESS << 1);
+ union nvme_result result;
+- bool complete_rq;
++ bool complete_rq, terminate_assoc = true;
+
+ /*
+ * WARNING:
+@@ -1545,6 +1546,14 @@ nvme_fc_fcpio_done(struct nvmefc_fcp_req *req)
+ * fabricate a CQE, the following fields will not be set as they
+ * are not referenced:
+ * cqe.sqid, cqe.sqhd, cqe.command_id
++ *
++ * Failure or error of an individual i/o, in a transport
++ * detected fashion unrelated to the nvme completion status,
++ * potentially cause the initiator and target sides to get out
++ * of sync on SQ head/tail (aka outstanding io count allowed).
++ * Per FC-NVME spec, failure of an individual command requires
++ * the connection to be terminated, which in turn requires the
++ * association to be terminated.
+ */
+
+ fc_dma_sync_single_for_cpu(ctrl->lport->dev, op->fcp_req.rspdma,
+@@ -1610,6 +1619,8 @@ nvme_fc_fcpio_done(struct nvmefc_fcp_req *req)
+ goto done;
+ }
+
++ terminate_assoc = false;
++
+ done:
+ if (op->flags & FCOP_FLAGS_AEN) {
+ nvme_complete_async_event(&queue->ctrl->ctrl, status, &result);
+@@ -1617,7 +1628,7 @@ done:
+ atomic_set(&op->state, FCPOP_STATE_IDLE);
+ op->flags = FCOP_FLAGS_AEN; /* clear other flags */
+ nvme_fc_ctrl_put(ctrl);
+- return;
++ goto check_error;
+ }
+
+ complete_rq = __nvme_fc_fcpop_chk_teardowns(ctrl, op);
+@@ -1630,6 +1641,10 @@ done:
+ nvme_end_request(rq, status, result);
+ } else
+ __nvme_fc_final_op_cleanup(rq);
++
++check_error:
++ if (terminate_assoc)
++ nvme_fc_error_recovery(ctrl, "transport detected io error");
+ }
+
+ static int
+--
+1.8.5.6
+
diff --git a/patches.drivers/0446-lpfc-Fix-defects-reported-by-Coverity-Scan.patch b/patches.drivers/0446-lpfc-Fix-defects-reported-by-Coverity-Scan.patch
new file mode 100644
index 0000000000..71ce66d3e7
--- /dev/null
+++ b/patches.drivers/0446-lpfc-Fix-defects-reported-by-Coverity-Scan.patch
@@ -0,0 +1,132 @@
+From 7a26fa847477e8bf8157592642148ecbe3a568b2 Mon Sep 17 00:00:00 2001
+From: James Smart <jsmart2021@gmail.com>
+Date: Thu, 1 Jun 2017 16:07:47 -0700
+Subject: [PATCH 1/2] lpfc: Fix defects reported by Coverity Scan
+References: bsc#1042257
+Patch-mainline: Submitted, http://marc.info/?l=linux-scsi&m=149637644911123&w=2
+
+Addressed the following reported defects:
+
+** CID 1411552: Control flow issues (MISSING_BREAK)
+/drivers/scsi/lpfc/lpfc_sli.c: 13259 in lpfc_sli4_nvmet_handle_rcqe()
+
+** CID 1411553: Memory - illegal accesses (OVERRUN)
+/drivers/scsi/lpfc/lpfc_sli.c: 16218 in lpfc_fc_frame_check()
+
+** CID 1411553: Memory - illegal accesses (OVERRUN)
+ Overrunning array "lpfc_rctl_names" of 202 8-byte elements at element
+ index 244 (byte offset 1952) using index "fc_hdr->fh_r_ctl" (which
+ evaluates to 244).
+
+** CID 1411554: Null pointer dereferences (REVERSE_INULL)
+/drivers/scsi/lpfc/lpfc_nvmet.c: 2131 in lpfc_nvmet_unsol_fcp_abort_cmp()
+
+** CID 1411555: Memory - illegal accesses (UNINIT)
+/drivers/scsi/lpfc/lpfc_nvmet.c: 180 in lpfc_nvmet_ctxbuf_post()
+
+Signed-off-by: Dick Kennedy <dick.kennedy@broadcom.com>
+Signed-off-by: James Smart <james.smart@broadcom.com>
+Signed-off-by: Johannes Thumshirn <jthumshirn@suse.de>
+---
+ drivers/scsi/lpfc/lpfc_nvmet.c | 10 ++++------
+ drivers/scsi/lpfc/lpfc_sli.c | 24 +++++++++---------------
+ 2 files changed, 13 insertions(+), 21 deletions(-)
+
+diff --git a/drivers/scsi/lpfc/lpfc_nvmet.c b/drivers/scsi/lpfc/lpfc_nvmet.c
+index a43b757341fa..b0d81e321e80 100644
+--- a/drivers/scsi/lpfc/lpfc_nvmet.c
++++ b/drivers/scsi/lpfc/lpfc_nvmet.c
+@@ -169,7 +169,6 @@ lpfc_nvmet_ctxbuf_post(struct lpfc_hba *phba, struct lpfc_nvmet_ctxbuf *ctx_buf)
+ struct lpfc_nvmet_tgtport *tgtp;
+ struct fc_frame_header *fc_hdr;
+ struct rqb_dmabuf *nvmebuf;
+- struct lpfc_dmabuf *hbufp;
+ uint32_t *payload;
+ uint32_t size, oxid, sid, rc;
+ unsigned long iflag;
+@@ -190,7 +189,6 @@ lpfc_nvmet_ctxbuf_post(struct lpfc_hba *phba, struct lpfc_nvmet_ctxbuf *ctx_buf)
+
+ spin_lock_irqsave(&phba->sli4_hba.nvmet_io_wait_lock, iflag);
+ if (phba->sli4_hba.nvmet_io_wait_cnt) {
+- hbufp = &nvmebuf->hbuf;
+ list_remove_head(&phba->sli4_hba.lpfc_nvmet_io_wait_list,
+ nvmebuf, struct rqb_dmabuf,
+ hbuf.list);
+@@ -2163,10 +2161,6 @@ lpfc_nvmet_unsol_fcp_abort_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe,
+ status = bf_get(lpfc_wcqe_c_status, wcqe);
+ result = wcqe->parameter;
+
+- tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
+- if (ctxp->flag & LPFC_NVMET_ABORT_OP)
+- atomic_inc(&tgtp->xmt_fcp_abort_cmpl);
+-
+ if (!ctxp) {
+ /* if context is clear, related io alrady complete */
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS,
+@@ -2176,6 +2170,10 @@ lpfc_nvmet_unsol_fcp_abort_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe,
+ return;
+ }
+
++ tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
++ if (ctxp->flag & LPFC_NVMET_ABORT_OP)
++ atomic_inc(&tgtp->xmt_fcp_abort_cmpl);
++
+ /* Sanity check */
+ if (ctxp->state != LPFC_NVMET_STE_ABORT) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_NVME_ABTS,
+diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
+index dcd67190c774..9b5c90a4f4b3 100644
+--- a/drivers/scsi/lpfc/lpfc_sli.c
++++ b/drivers/scsi/lpfc/lpfc_sli.c
+@@ -13267,6 +13267,7 @@ lpfc_sli4_nvmet_handle_rcqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
+ case FC_STATUS_RQ_BUF_LEN_EXCEEDED:
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "6126 Receive Frame Truncated!!\n");
++ /* Drop thru */
+ case FC_STATUS_RQ_SUCCESS:
+ lpfc_sli4_rq_release(hrq, drq);
+ spin_lock_irqsave(&phba->hbalock, iflags);
+@@ -16137,9 +16138,6 @@ lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *phba,
+ return rc;
+ }
+
+-static char *lpfc_rctl_names[] = FC_RCTL_NAMES_INIT;
+-static char *lpfc_type_names[] = FC_TYPE_NAMES_INIT;
+-
+ /**
+ * lpfc_fc_frame_check - Check that this frame is a valid frame to handle
+ * @phba: pointer to lpfc_hba struct that the frame was received on
+@@ -16214,22 +16212,18 @@ lpfc_fc_frame_check(struct lpfc_hba *phba, struct fc_frame_header *fc_hdr)
+ }
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+- "2538 Received frame rctl:%s (x%x), type:%s (x%x), "
++ "2538 Received frame rctl:x%x, type:x%x, "
+ "frame Data:%08x %08x %08x %08x %08x %08x %08x\n",
+- (fc_hdr->fh_r_ctl == FC_RCTL_MDS_DIAGS) ? "MDS Diags" :
+- lpfc_rctl_names[fc_hdr->fh_r_ctl], fc_hdr->fh_r_ctl,
+- (fc_hdr->fh_type == FC_TYPE_VENDOR_UNIQUE) ?
+- "Vendor Unique" : lpfc_type_names[fc_hdr->fh_type],
+- fc_hdr->fh_type, be32_to_cpu(header[0]),
+- be32_to_cpu(header[1]), be32_to_cpu(header[2]),
+- be32_to_cpu(header[3]), be32_to_cpu(header[4]),
+- be32_to_cpu(header[5]), be32_to_cpu(header[6]));
++ fc_hdr->fh_r_ctl, fc_hdr->fh_type,
++ be32_to_cpu(header[0]), be32_to_cpu(header[1]),
++ be32_to_cpu(header[2]), be32_to_cpu(header[3]),
++ be32_to_cpu(header[4]), be32_to_cpu(header[5]),
++ be32_to_cpu(header[6]));
+ return 0;
+ drop:
+ lpfc_printf_log(phba, KERN_WARNING, LOG_ELS,
+- "2539 Dropped frame rctl:%s type:%s\n",
+- lpfc_rctl_names[fc_hdr->fh_r_ctl],
+- lpfc_type_names[fc_hdr->fh_type]);
++ "2539 Dropped frame rctl:x%x type:x%x\n",
++ fc_hdr->fh_r_ctl, fc_hdr->fh_type);
+ return 1;
+ }
+
+--
+2.11.0
+
diff --git a/patches.drivers/0447-lpfc-Add-auto-EQ-delay-logic.patch b/patches.drivers/0447-lpfc-Add-auto-EQ-delay-logic.patch
new file mode 100644
index 0000000000..aebbb60f6d
--- /dev/null
+++ b/patches.drivers/0447-lpfc-Add-auto-EQ-delay-logic.patch
@@ -0,0 +1,483 @@
+From d5844aa4722b3a39064f3acb10713be6950b578f Mon Sep 17 00:00:00 2001
+From: James Smart <jsmart2021@gmail.com>
+Date: Thu, 1 Jun 2017 16:09:00 -0700
+Subject: lpfc: Add auto EQ delay logic
+References: bsc#1042257
+Patch-mainline: Submitted, http://marc.info/?l=linux-scsi&m=149637644911123&w=2
+
+Administrator intervention is currently required to get good numbers
+when switching from running latency tests to IOPS tests.
+
+The configured interrupt coalescing values will greatly effect
+the results of these tests. Currently, the driver has a single
+coalescing value set by values of the module attribute. This
+patch changes the driver to support auto-configuration of the
+coalescing value based on the total number of outstanding IOs
+and average number of CQEs processed per interrupt for an EQ.
+Values are checked every 5 seconds.
+
+The driver defaults to the automatic selection. Automatic selection
+can be disabled by the new lpfc_auto_imax module_parameter.
+
+Older hardware can only change interrupt coalescing by mailbox
+command. Newer hardware supports change via a register. The patch
+support both.
+
+Signed-off-by: Dick Kennedy <dick.kennedy@broadcom.com>
+Signed-off-by: James Smart <james.smart@broadcom.com>
+Signed-off-by: Johannes Thumshirn <jthumshirn@suse.de>
+---
+ drivers/scsi/lpfc/lpfc.h | 3 ++
+ drivers/scsi/lpfc/lpfc_attr.c | 20 +++++++-
+ drivers/scsi/lpfc/lpfc_debugfs.c | 4 +-
+ drivers/scsi/lpfc/lpfc_hw4.h | 14 ++++++
+ drivers/scsi/lpfc/lpfc_init.c | 104 ++++++++++++++++++++++++++++++++++++++-
+ drivers/scsi/lpfc/lpfc_sli.c | 36 +++++++++++---
+ drivers/scsi/lpfc/lpfc_sli.h | 1 +
+ drivers/scsi/lpfc/lpfc_sli4.h | 8 +--
+ 8 files changed, 177 insertions(+), 13 deletions(-)
+
+diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h
+index cb3b8990df87..8089eda27667 100644
+--- a/drivers/scsi/lpfc/lpfc.h
++++ b/drivers/scsi/lpfc/lpfc.h
+@@ -756,6 +756,7 @@ struct lpfc_hba {
+ uint8_t nvmet_support; /* driver supports NVMET */
+ #define LPFC_NVMET_MAX_PORTS 32
+ uint8_t mds_diags_support;
++ uint32_t initial_imax;
+
+ /* HBA Config Parameters */
+ uint32_t cfg_ack0;
+@@ -777,6 +778,7 @@ struct lpfc_hba {
+ uint32_t cfg_poll_tmo;
+ uint32_t cfg_task_mgmt_tmo;
+ uint32_t cfg_use_msi;
++ uint32_t cfg_auto_imax;
+ uint32_t cfg_fcp_imax;
+ uint32_t cfg_fcp_cpu_map;
+ uint32_t cfg_fcp_io_channel;
+@@ -1052,6 +1054,7 @@ struct lpfc_hba {
+
+ uint8_t temp_sensor_support;
+ /* Fields used for heart beat. */
++ unsigned long last_eqdelay_time;
+ unsigned long last_completion_time;
+ unsigned long skipped_hb;
+ struct timer_list hb_tmofunc;
+diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
+index 503315bac98e..962eaaa570fe 100644
+--- a/drivers/scsi/lpfc/lpfc_attr.c
++++ b/drivers/scsi/lpfc/lpfc_attr.c
+@@ -4484,9 +4484,11 @@ lpfc_fcp_imax_store(struct device *dev, struct device_attribute *attr,
+ return -EINVAL;
+
+ phba->cfg_fcp_imax = (uint32_t)val;
++ phba->initial_imax = phba->cfg_fcp_imax;
+
+ for (i = 0; i < phba->io_channel_irqs; i += LPFC_MAX_EQ_DELAY_EQID_CNT)
+- lpfc_modify_hba_eq_delay(phba, i);
++ lpfc_modify_hba_eq_delay(phba, i, LPFC_MAX_EQ_DELAY_EQID_CNT,
++ val);
+
+ return strlen(buf);
+ }
+@@ -4541,6 +4543,16 @@ lpfc_fcp_imax_init(struct lpfc_hba *phba, int val)
+ static DEVICE_ATTR(lpfc_fcp_imax, S_IRUGO | S_IWUSR,
+ lpfc_fcp_imax_show, lpfc_fcp_imax_store);
+
++/*
++ * lpfc_auto_imax: Controls Auto-interrupt coalescing values support.
++ * 0 No auto_imax support
++ * 1 auto imax on
++ * Auto imax will change the value of fcp_imax on a per EQ basis, using
++ * the EQ Delay Multiplier, depending on the activity for that EQ.
++ * Value range [0,1]. Default value is 1.
++ */
++LPFC_ATTR_RW(auto_imax, 1, 0, 1, "Enable Auto imax");
++
+ /**
+ * lpfc_state_show - Display current driver CPU affinity
+ * @dev: class converted to a Scsi_host structure.
+@@ -5167,6 +5179,7 @@ struct device_attribute *lpfc_hba_attrs[] = {
+ &dev_attr_lpfc_task_mgmt_tmo,
+ &dev_attr_lpfc_use_msi,
+ &dev_attr_lpfc_nvme_oas,
++ &dev_attr_lpfc_auto_imax,
+ &dev_attr_lpfc_fcp_imax,
+ &dev_attr_lpfc_fcp_cpu_map,
+ &dev_attr_lpfc_fcp_io_channel,
+@@ -6185,6 +6198,7 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
+ lpfc_enable_SmartSAN_init(phba, lpfc_enable_SmartSAN);
+ lpfc_use_msi_init(phba, lpfc_use_msi);
+ lpfc_nvme_oas_init(phba, lpfc_nvme_oas);
++ lpfc_auto_imax_init(phba, lpfc_auto_imax);
+ lpfc_fcp_imax_init(phba, lpfc_fcp_imax);
+ lpfc_fcp_cpu_map_init(phba, lpfc_fcp_cpu_map);
+ lpfc_enable_hba_reset_init(phba, lpfc_enable_hba_reset);
+@@ -6229,6 +6243,10 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
+ phba->cfg_enable_fc4_type |= LPFC_ENABLE_FCP;
+ }
+
++ if (phba->cfg_auto_imax && !phba->cfg_fcp_imax)
++ phba->cfg_auto_imax = 0;
++ phba->initial_imax = phba->cfg_fcp_imax;
++
+ /* A value of 0 means use the number of CPUs found in the system */
+ if (phba->cfg_fcp_io_channel == 0)
+ phba->cfg_fcp_io_channel = phba->sli4_hba.num_present_cpu;
+diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c
+index d1ad3900b5c4..155c0e4016a0 100644
+--- a/drivers/scsi/lpfc/lpfc_debugfs.c
++++ b/drivers/scsi/lpfc/lpfc_debugfs.c
+@@ -3265,9 +3265,9 @@ __lpfc_idiag_print_eq(struct lpfc_queue *qp, char *eqtype,
+
+ len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
+ "\n%s EQ info: EQ-STAT[max:x%x noE:x%x "
+- "bs:x%x proc:x%llx]\n",
++ "bs:x%x proc:x%llx eqd %d]\n",
+ eqtype, qp->q_cnt_1, qp->q_cnt_2, qp->q_cnt_3,
+- (unsigned long long)qp->q_cnt_4);
++ (unsigned long long)qp->q_cnt_4, qp->q_mode);
+ len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
+ "EQID[%02d], QE-CNT[%04d], QE-SZ[%04d], "
+ "HST-IDX[%04d], PRT-IDX[%04d], PST[%03d]",
+diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h
+index e0a5fce416ae..bb4715705fa3 100644
+--- a/drivers/scsi/lpfc/lpfc_hw4.h
++++ b/drivers/scsi/lpfc/lpfc_hw4.h
+@@ -197,6 +197,7 @@ struct lpfc_sli_intf {
+
+ /* Delay Multiplier constant */
+ #define LPFC_DMULT_CONST 651042
++#define LPFC_DMULT_MAX 1023
+
+ /* Configuration of Interrupts / sec for entire HBA port */
+ #define LPFC_MIN_IMAX 5000
+@@ -657,6 +658,15 @@ struct lpfc_register {
+ #define LPFC_CTL_PORT_ER1_OFFSET 0x40C
+ #define LPFC_CTL_PORT_ER2_OFFSET 0x410
+
++#define LPFC_CTL_PORT_EQ_DELAY_OFFSET 0x418
++#define lpfc_sliport_eqdelay_delay_SHIFT 16
++#define lpfc_sliport_eqdelay_delay_MASK 0xffff
++#define lpfc_sliport_eqdelay_delay_WORD word0
++#define lpfc_sliport_eqdelay_id_SHIFT 0
++#define lpfc_sliport_eqdelay_id_MASK 0xfff
++#define lpfc_sliport_eqdelay_id_WORD word0
++#define LPFC_SEC_TO_USEC 1000000
++
+ /* The following Registers apply to SLI4 if_type 0 UCNAs. They typically
+ * reside in BAR 2.
+ */
+@@ -3258,6 +3268,10 @@ struct lpfc_sli4_parameters {
+ #define cfg_xib_SHIFT 4
+ #define cfg_xib_MASK 0x00000001
+ #define cfg_xib_WORD word19
++#define cfg_eqdr_SHIFT 8
++#define cfg_eqdr_MASK 0x00000001
++#define cfg_eqdr_WORD word19
++#define LPFC_NODELAY_MAX_IO 32
+ };
+
+ #define LPFC_SET_UE_RECOVERY 0x10
+diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
+index 0cac6ec4de2b..ec17c8a58ce7 100644
+--- a/drivers/scsi/lpfc/lpfc_init.c
++++ b/drivers/scsi/lpfc/lpfc_init.c
+@@ -1249,6 +1249,12 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba)
+ int retval, i;
+ struct lpfc_sli *psli = &phba->sli;
+ LIST_HEAD(completions);
++ struct lpfc_queue *qp;
++ unsigned long time_elapsed;
++ uint32_t tick_cqe, max_cqe, val;
++ uint64_t tot, data1, data2, data3;
++ struct lpfc_register reg_data;
++ void __iomem *eqdreg = phba->sli4_hba.u.if_type2.EQDregaddr;
+
+ vports = lpfc_create_vport_work_array(phba);
+ if (vports != NULL)
+@@ -1263,6 +1269,95 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba)
+ (phba->pport->fc_flag & FC_OFFLINE_MODE))
+ return;
+
++ if (phba->cfg_auto_imax) {
++ if (!phba->last_eqdelay_time) {
++ phba->last_eqdelay_time = jiffies;
++ goto skip_eqdelay;
++ }
++ time_elapsed = jiffies - phba->last_eqdelay_time;
++ phba->last_eqdelay_time = jiffies;
++
++ tot = 0xffff;
++ /* Check outstanding IO count */
++ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
++ if (phba->nvmet_support) {
++ spin_lock(&phba->sli4_hba.nvmet_io_lock);
++ tot = phba->sli4_hba.nvmet_xri_cnt -
++ phba->sli4_hba.nvmet_ctx_cnt;
++ spin_unlock(&phba->sli4_hba.nvmet_io_lock);
++ } else {
++ tot = atomic_read(&phba->fc4NvmeIoCmpls);
++ data1 = atomic_read(
++ &phba->fc4NvmeInputRequests);
++ data2 = atomic_read(
++ &phba->fc4NvmeOutputRequests);
++ data3 = atomic_read(
++ &phba->fc4NvmeControlRequests);
++ tot = (data1 + data2 + data3) - tot;
++ }
++ }
++
++ /* Interrupts per sec per EQ */
++ val = phba->cfg_fcp_imax / phba->io_channel_irqs;
++ tick_cqe = val / CONFIG_HZ; /* Per tick per EQ */
++
++ /* Assume 1 CQE/ISR, calc max CQEs allowed for time duration */
++ max_cqe = time_elapsed * tick_cqe;
++
++ for (i = 0; i < phba->io_channel_irqs; i++) {
++ /* Fast-path EQ */
++ qp = phba->sli4_hba.hba_eq[i];
++ if (!qp)
++ continue;
++
++ /* Use no EQ delay if we don't have many outstanding
++ * IOs, or if we are only processing 1 CQE/ISR or less.
++ * Otherwise, assume we can process up to lpfc_fcp_imax
++ * interrupts per HBA.
++ */
++ if ((tot < LPFC_NODELAY_MAX_IO) ||
++ (qp->EQ_cqe_cnt <= max_cqe))
++ val = 0;
++ else
++ val = phba->cfg_fcp_imax;
++
++ if (phba->sli.sli_flag & LPFC_SLI_USE_EQDR) {
++ /* Use EQ Delay Register method */
++
++ /* Convert for EQ Delay register */
++ if (val) {
++ /* First, interrupts per sec per EQ */
++ val = phba->cfg_fcp_imax /
++ phba->io_channel_irqs;
++
++ /* us delay between each interrupt */
++ val = LPFC_SEC_TO_USEC / val;
++ }
++ if (val != qp->q_mode) {
++ reg_data.word0 = 0;
++ bf_set(lpfc_sliport_eqdelay_id,
++ &reg_data, qp->queue_id);
++ bf_set(lpfc_sliport_eqdelay_delay,
++ &reg_data, val);
++ writel(reg_data.word0, eqdreg);
++ }
++ } else {
++ /* Use mbox command method */
++ if (val != qp->q_mode)
++ lpfc_modify_hba_eq_delay(phba, i,
++ 1, val);
++ }
++
++ /*
++ * val is cfg_fcp_imax or 0 for mbox delay or us delay
++ * between interrupts for EQDR.
++ */
++ qp->q_mode = val;
++ qp->EQ_cqe_cnt = 0;
++ }
++ }
++
++skip_eqdelay:
+ spin_lock_irq(&phba->pport->work_port_lock);
+
+ if (time_after(phba->last_completion_time +
+@@ -7274,6 +7369,9 @@ lpfc_sli4_bar0_register_memmap(struct lpfc_hba *phba, uint32_t if_type)
+ phba->sli4_hba.conf_regs_memmap_p + LPFC_SLI_INTF;
+ break;
+ case LPFC_SLI_INTF_IF_TYPE_2:
++ phba->sli4_hba.u.if_type2.EQDregaddr =
++ phba->sli4_hba.conf_regs_memmap_p +
++ LPFC_CTL_PORT_EQ_DELAY_OFFSET;
+ phba->sli4_hba.u.if_type2.ERR1regaddr =
+ phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_CTL_PORT_ER1_OFFSET;
+@@ -8800,7 +8898,8 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba)
+ }
+
+ for (qidx = 0; qidx < io_channel; qidx += LPFC_MAX_EQ_DELAY_EQID_CNT)
+- lpfc_modify_hba_eq_delay(phba, qidx);
++ lpfc_modify_hba_eq_delay(phba, qidx, LPFC_MAX_EQ_DELAY_EQID_CNT,
++ phba->cfg_fcp_imax);
+
+ return 0;
+
+@@ -10388,6 +10487,9 @@ lpfc_get_sli4_parameters(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
+ if (bf_get(cfg_xib, mbx_sli4_parameters) && phba->cfg_suppress_rsp)
+ phba->sli.sli_flag |= LPFC_SLI_SUPPRESS_RSP;
+
++ if (bf_get(cfg_eqdr, mbx_sli4_parameters))
++ phba->sli.sli_flag |= LPFC_SLI_USE_EQDR;
++
+ /* Make sure that sge_supp_len can be handled by the driver */
+ if (sli4_params->sge_supp_len > LPFC_MAX_SGE_SIZE)
+ sli4_params->sge_supp_len = LPFC_MAX_SGE_SIZE;
+diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
+index 9b5c90a4f4b3..d28007cd537e 100644
+--- a/drivers/scsi/lpfc/lpfc_sli.c
++++ b/drivers/scsi/lpfc/lpfc_sli.c
+@@ -13478,6 +13478,7 @@ process_cq:
+ /* Track the max number of CQEs processed in 1 EQ */
+ if (ecount > cq->CQ_max_cqe)
+ cq->CQ_max_cqe = ecount;
++ cq->assoc_qp->EQ_cqe_cnt += ecount;
+
+ /* Catch the no cq entry condition */
+ if (unlikely(ecount == 0))
+@@ -13569,6 +13570,7 @@ lpfc_sli4_fof_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe)
+ /* Track the max number of CQEs processed in 1 EQ */
+ if (ecount > cq->CQ_max_cqe)
+ cq->CQ_max_cqe = ecount;
++ cq->assoc_qp->EQ_cqe_cnt += ecount;
+
+ /* Catch the no cq entry condition */
+ if (unlikely(ecount == 0))
+@@ -13629,7 +13631,6 @@ lpfc_sli4_fof_intr_handler(int irq, void *dev_id)
+
+ /* Check device state for handling interrupt */
+ if (unlikely(lpfc_intr_state_check(phba))) {
+- eq->EQ_badstate++;
+ /* Check again for link_state with lock held */
+ spin_lock_irqsave(&phba->hbalock, iflag);
+ if (phba->link_state < LPFC_LINK_DOWN)
+@@ -13741,7 +13742,6 @@ lpfc_sli4_hba_intr_handler(int irq, void *dev_id)
+
+ /* Check device state for handling interrupt */
+ if (unlikely(lpfc_intr_state_check(phba))) {
+- fpeq->EQ_badstate++;
+ /* Check again for link_state with lock held */
+ spin_lock_irqsave(&phba->hbalock, iflag);
+ if (phba->link_state < LPFC_LINK_DOWN)
+@@ -14000,14 +14000,15 @@ lpfc_dual_chute_pci_bar_map(struct lpfc_hba *phba, uint16_t pci_barset)
+ * fails this function will return -ENXIO.
+ **/
+ int
+-lpfc_modify_hba_eq_delay(struct lpfc_hba *phba, uint32_t startq)
++lpfc_modify_hba_eq_delay(struct lpfc_hba *phba, uint32_t startq,
++ uint32_t numq, uint32_t imax)
+ {
+ struct lpfc_mbx_modify_eq_delay *eq_delay;
+ LPFC_MBOXQ_t *mbox;
+ struct lpfc_queue *eq;
+ int cnt, rc, length, status = 0;
+ uint32_t shdr_status, shdr_add_status;
+- uint32_t result;
++ uint32_t result, val;
+ int qidx;
+ union lpfc_sli4_cfg_shdr *shdr;
+ uint16_t dmult;
+@@ -14026,22 +14027,45 @@ lpfc_modify_hba_eq_delay(struct lpfc_hba *phba, uint32_t startq)
+ eq_delay = &mbox->u.mqe.un.eq_delay;
+
+ /* Calculate delay multiper from maximum interrupt per second */
+- result = phba->cfg_fcp_imax / phba->io_channel_irqs;
++ result = imax / phba->io_channel_irqs;
+ if (result > LPFC_DMULT_CONST || result == 0)
+ dmult = 0;
+ else
+ dmult = LPFC_DMULT_CONST/result - 1;
++ if (dmult > LPFC_DMULT_MAX)
++ dmult = LPFC_DMULT_MAX;
+
+ cnt = 0;
+ for (qidx = startq; qidx < phba->io_channel_irqs; qidx++) {
+ eq = phba->sli4_hba.hba_eq[qidx];
+ if (!eq)
+ continue;
++ eq->q_mode = imax;
+ eq_delay->u.request.eq[cnt].eq_id = eq->queue_id;
+ eq_delay->u.request.eq[cnt].phase = 0;
+ eq_delay->u.request.eq[cnt].delay_multi = dmult;
+ cnt++;
+- if (cnt >= LPFC_MAX_EQ_DELAY_EQID_CNT)
++
++ /* q_mode is only used for auto_imax */
++ if (phba->sli.sli_flag & LPFC_SLI_USE_EQDR) {
++ /* Use EQ Delay Register method for q_mode */
++
++ /* Convert for EQ Delay register */
++ val = phba->cfg_fcp_imax;
++ if (val) {
++ /* First, interrupts per sec per EQ */
++ val = phba->cfg_fcp_imax /
++ phba->io_channel_irqs;
++
++ /* us delay between each interrupt */
++ val = LPFC_SEC_TO_USEC / val;
++ }
++ eq->q_mode = val;
++ } else {
++ eq->q_mode = imax;
++ }
++
++ if (cnt >= numq)
+ break;
+ }
+ eq_delay->u.request.num_eq = cnt;
+diff --git a/drivers/scsi/lpfc/lpfc_sli.h b/drivers/scsi/lpfc/lpfc_sli.h
+index 9085306ddd78..a3b1b5145d2b 100644
+--- a/drivers/scsi/lpfc/lpfc_sli.h
++++ b/drivers/scsi/lpfc/lpfc_sli.h
+@@ -321,6 +321,7 @@ struct lpfc_sli {
+ #define LPFC_MENLO_MAINT 0x1000 /* need for menl fw download */
+ #define LPFC_SLI_ASYNC_MBX_BLK 0x2000 /* Async mailbox is blocked */
+ #define LPFC_SLI_SUPPRESS_RSP 0x4000 /* Suppress RSP feature is supported */
++#define LPFC_SLI_USE_EQDR 0x8000 /* EQ Delay Register is supported */
+
+ struct lpfc_sli_ring *sli3_ring;
+
+diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h
+index cb2d1e7fb4f2..7437cfd307e8 100644
+--- a/drivers/scsi/lpfc/lpfc_sli4.h
++++ b/drivers/scsi/lpfc/lpfc_sli4.h
+@@ -168,7 +168,7 @@ struct lpfc_queue {
+ struct lpfc_sli_ring *pring; /* ptr to io ring associated with q */
+ struct lpfc_rqb *rqbp; /* ptr to RQ buffers */
+
+- uint16_t sgl_list_cnt;
++ uint32_t q_mode;
+ uint16_t db_format;
+ #define LPFC_DB_RING_FORMAT 0x01
+ #define LPFC_DB_LIST_FORMAT 0x02
+@@ -181,7 +181,7 @@ struct lpfc_queue {
+ /* defines for EQ stats */
+ #define EQ_max_eqe q_cnt_1
+ #define EQ_no_entry q_cnt_2
+-#define EQ_badstate q_cnt_3
++#define EQ_cqe_cnt q_cnt_3
+ #define EQ_processed q_cnt_4
+
+ /* defines for CQ stats */
+@@ -523,6 +523,7 @@ struct lpfc_sli4_hba {
+ #define SLIPORT_ERR2_REG_FAILURE_CQ 0x4
+ #define SLIPORT_ERR2_REG_FAILURE_BUS 0x5
+ #define SLIPORT_ERR2_REG_FAILURE_RQ 0x6
++ void __iomem *EQDregaddr;
+ } if_type2;
+ } u;
+
+@@ -756,7 +757,8 @@ struct lpfc_queue *lpfc_sli4_queue_alloc(struct lpfc_hba *, uint32_t,
+ uint32_t);
+ void lpfc_sli4_queue_free(struct lpfc_queue *);
+ int lpfc_eq_create(struct lpfc_hba *, struct lpfc_queue *, uint32_t);
+-int lpfc_modify_hba_eq_delay(struct lpfc_hba *phba, uint32_t startq);
++int lpfc_modify_hba_eq_delay(struct lpfc_hba *phba, uint32_t startq,
++ uint32_t numq, uint32_t imax);
+ int lpfc_cq_create(struct lpfc_hba *, struct lpfc_queue *,
+ struct lpfc_queue *, uint32_t, uint32_t);
+ int lpfc_cq_create_set(struct lpfc_hba *phba, struct lpfc_queue **cqp,
+--
+2.11.0
+
diff --git a/series.conf b/series.conf
index b74fec38f6..f4935581d2 100644
--- a/series.conf
+++ b/series.conf
@@ -5780,6 +5780,10 @@
patches.drivers/0441-lpfc-Fix-vports-not-logging-into-target.patch
patches.drivers/0442-lpfc-update-to-revision-to-11.4.0.0.patch
patches.drivers/0443-lpfc-tie-lpfc-dev_loss_tmo-dynamic-changes-to-transp.patch
+ patches.drivers/0444-nvme_fc-fix-confused-queue-size-handling.patch
+ patches.drivers/0445-nvme_fc-on-lldd-transport-io-error-terminate-associa.patch
+ patches.drivers/0446-lpfc-Fix-defects-reported-by-Coverity-Scan.patch
+ patches.drivers/0447-lpfc-Add-auto-EQ-delay-logic.patch
patches.drivers/0005-lightnvm-move-ppa-erase-logic-to-core.patch
patches.drivers/0006-lightnvm-refactor-rqd-ppa-list-into-set-free.patch