RE: [PATCH] scsi: bfa: convert to strlcpy/strlcat
Acked-by: Sudarsana Kalluru-Original Message- From: Arnd Bergmann [mailto:a...@arndb.de] Sent: 04 December 2017 20:17 To: Gurumurthy, Anil ; Kalluru, Sudarsana ; James E.J. Bottomley ; Martin K. Petersen Cc: Arnd Bergmann ; Hannes Reinecke ; Kees Cook ; Benjamin Poirier ; Mody, Rasesh ; Johannes Thumshirn ; linux-scsi@vger.kernel.org; linux-ker...@vger.kernel.org Subject: [PATCH] scsi: bfa: convert to strlcpy/strlcat The bfa driver has a number of real issues with string termination that gcc-8 now points out: drivers/scsi/bfa/bfad_bsg.c: In function 'bfad_iocmd_port_get_attr': drivers/scsi/bfa/bfad_bsg.c:320:9: error: argument to 'sizeof' in 'strncpy' call is the same expression as the source; did you mean to use the size of the destination? [-Werror=sizeof-pointer-memaccess] drivers/scsi/bfa/bfa_fcs.c: In function 'bfa_fcs_fabric_psymb_init': drivers/scsi/bfa/bfa_fcs.c:775:9: error: argument to 'sizeof' in 'strncat' call is the same expression as the source; did you mean to use the size of the destination? [-Werror=sizeof-pointer-memaccess] drivers/scsi/bfa/bfa_fcs.c:781:9: error: argument to 'sizeof' in 'strncat' call is the same expression as the source; did you mean to use the size of the destination? [-Werror=sizeof-pointer-memaccess] drivers/scsi/bfa/bfa_fcs.c:788:9: error: argument to 'sizeof' in 'strncat' call is the same expression as the source; did you mean to use the size of the destination? [-Werror=sizeof-pointer-memaccess] drivers/scsi/bfa/bfa_fcs.c:801:10: error: argument to 'sizeof' in 'strncat' call is the same expression as the source; did you mean to use the size of the destination? [-Werror=sizeof-pointer-memaccess] drivers/scsi/bfa/bfa_fcs.c:808:10: error: argument to 'sizeof' in 'strncat' call is the same expression as the source; did you mean to use the size of the destination? [-Werror=sizeof-pointer-memaccess] drivers/scsi/bfa/bfa_fcs.c: In function 'bfa_fcs_fabric_nsymb_init': drivers/scsi/bfa/bfa_fcs.c:837:10: error: argument to 'sizeof' in 'strncat' call is the same expression as the source; did you mean to use the size of the destination? [-Werror=sizeof-pointer-memaccess] drivers/scsi/bfa/bfa_fcs.c:844:10: error: argument to 'sizeof' in 'strncat' call is the same expression as the source; did you mean to use the size of the destination? [-Werror=sizeof-pointer-memaccess] drivers/scsi/bfa/bfa_fcs.c:852:10: error: argument to 'sizeof' in 'strncat' call is the same expression as the source; did you mean to use the size of the destination? [-Werror=sizeof-pointer-memaccess] drivers/scsi/bfa/bfa_fcs.c: In function 'bfa_fcs_fabric_psymb_init': drivers/scsi/bfa/bfa_fcs.c:778:2: error: 'strncat' output may be truncated copying 10 bytes from a string of length 63 [-Werror=stringop-truncation] drivers/scsi/bfa/bfa_fcs.c:784:2: error: 'strncat' output may be truncated copying 30 bytes from a string of length 63 [-Werror=stringop-truncation] drivers/scsi/bfa/bfa_fcs.c:803:3: error: 'strncat' output may be truncated copying 44 bytes from a string of length 63 [-Werror=stringop-truncation] drivers/scsi/bfa/bfa_fcs.c:811:3: error: 'strncat' output may be truncated copying 16 bytes from a string of length 63 [-Werror=stringop-truncation] drivers/scsi/bfa/bfa_fcs.c: In function 'bfa_fcs_fabric_nsymb_init': drivers/scsi/bfa/bfa_fcs.c:840:2: error: 'strncat' output may be truncated copying 10 bytes from a string of length 63 [-Werror=stringop-truncation] drivers/scsi/bfa/bfa_fcs.c:847:2: error: 'strncat' output may be truncated copying 30 bytes from a string of length 63 [-Werror=stringop-truncation] drivers/scsi/bfa/bfa_fcs_lport.c: In function 'bfa_fcs_fdmi_get_hbaattr': drivers/scsi/bfa/bfa_fcs_lport.c:2657:10: error: argument to 'sizeof' in 'strncat' call is the same expression as the source; did you mean to use the size of the destination? [-Werror=sizeof-pointer-memaccess] drivers/scsi/bfa/bfa_fcs_lport.c:2659:11: error: argument to 'sizeof' in 'strncat' call is the same expression as the source; did you mean to use the size of the destination? [-Werror=sizeof-pointer-memaccess] drivers/scsi/bfa/bfa_fcs_lport.c: In function 'bfa_fcs_lport_ms_gmal_response': drivers/scsi/bfa/bfa_fcs_lport.c:3232:5: error: 'strncpy' output may be truncated copying 16 bytes from a string of length 247 [-Werror=stringop-truncation] drivers/scsi/bfa/bfa_fcs_lport.c: In function 'bfa_fcs_lport_ns_send_rspn_id': drivers/scsi/bfa/bfa_fcs_lport.c:4670:3: error: 'strncpy' output truncated before terminating nul copying as many bytes from a string as its length [-Werror=stringop-truncation] drivers/scsi/bfa/bfa_fcs_lport.c:4682:3: error: 'strncat' output truncated before terminating nul copying as many bytes
Re: [PATCH] scsi: libiscsi: Allow sd_shutdown on bad transport
Hello Bart, I am returning BLK_EH_HANDLED in iscsi_eh_cmd_timed_out(). Do you mean something different ? That paragraph means that I have tried to return BLK_EH_NOT_HANDLED first, because that would be the other option instead of BLK_EH_RESET_TIMER (which is causing this issue), but if I did it, the EH logic would try scsi_abort_command() and - successful or not - it would try to get sense before completion, causing more traffic on a bad state transport. Best way to allow faster completion was, indeed, returning BLK_EH_HANDLED, but changing result to DID_NO_CONNECT, because that will tell block layer not to retry, allowing the completion to happen in the SOFTIRQ handler, informing result to the upper layer. For the queue, simply now allowing queueing on such condition (shutdown + state != logged in) seemed correct. Let me know if you want me to try something else. I would be happy to. Best, -Rafael > On 08/12/2017, at 09:12 PM, Bart Van Asschewrote: > > On Thu, 2017-12-07 at 19:59 -0200, Rafael David Tinoco wrote: >> This happens because iscsi_eh_cmd_timed_out(), the transport layer >> timeout helper, would tell the queue timeout function (scsi_times_out) >> to reset the request timer over and over, until the session state is >> back to logged in state. Unfortunately, during server shutdown, this >> might never happen again. > > Hello Rafael, > > Have you considered to make iscsi_eh_cmd_timed_out() return BLK_EH_HANDLED > if system_state != SYSTEM_RUNNING? That could result in slower shutdown than > with your patch but such a change would probably be really easy to review. > > Thanks, > > Bart.
[PATCH 7/9] lpfc: Fix infinite wait when driver unregisters a remote NVME port.
When unregistering a remote port the lpfc driver would eventually wait for the remoteport_unreg done callback. But the driver never completed the io aborts that would allow the connections to terminate thus the unreg done callback was never issued. Turns out the coding style of the driver allowed for the wait to occur on the same cpu that the deferred isr is called on. The blocking for the wait, blocked the isr, and as the isr didn't run, the io aborts wouldn't finish. Turns out there was never a good reason to block waiting for the unreg done in the first place. The driver can continue execution and the ref counting within the driver will do the right thing. Resolve by removing the wait and patching up a few cases where the ref counting didn't look right - mainly cases where the remote port comes back before the aborts had completed and the unreg done had been called. Additionally, a few places which used pointer values to guide driver actions weren't protected by lock, so correct those. Signed-off-by: Dick KennedySigned-off-by: James Smart --- drivers/scsi/lpfc/lpfc_nvme.c | 135 -- 1 file changed, 51 insertions(+), 84 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index 1097ca5a7a8e..4b2a73ebd116 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -201,16 +201,19 @@ lpfc_nvme_remoteport_delete(struct nvme_fc_remote_port *remoteport) * calling state machine to remove the node. */ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC, - "6146 remoteport delete complete %p\n", + "6146 remoteport delete of remoteport %p\n", remoteport); + spin_lock_irq(>phba->hbalock); ndlp->nrport = NULL; + spin_unlock_irq(>phba->hbalock); + + /* Remove original register reference. The host transport +* won't reference this rport/remoteport any further. +*/ lpfc_nlp_put(ndlp); rport_err: - /* This call has to execute as long as the rport is valid. -* Release any threads waiting for the unreg to complete. -*/ - complete(>rport_unreg_done); + return; } static void @@ -966,16 +969,10 @@ lpfc_nvme_io_cmd_wqe_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn, /* NVME targets need completion held off until the abort exchange * completes unless the NVME Rport is getting unregistered. */ - if (!(lpfc_ncmd->flags & LPFC_SBUF_XBUSY) || - ndlp->upcall_flags & NLP_WAIT_FOR_UNREG) { - /* Clear the XBUSY flag to prevent double completions. -* The nvme rport is getting unregistered and there is -* no need to defer the IO. -*/ - if (lpfc_ncmd->flags & LPFC_SBUF_XBUSY) - lpfc_ncmd->flags &= ~LPFC_SBUF_XBUSY; + if (!(lpfc_ncmd->flags & LPFC_SBUF_XBUSY)) { nCmd->done(nCmd); + lpfc_ncmd->nvmeCmd = NULL; } spin_lock_irqsave(>hbalock, flags); @@ -2494,6 +2491,9 @@ lpfc_nvme_register_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) rpinfo.port_name = wwn_to_u64(ndlp->nlp_portname.u.wwn); rpinfo.node_name = wwn_to_u64(ndlp->nlp_nodename.u.wwn); + if (!ndlp->nrport) + lpfc_nlp_get(ndlp); + ret = nvme_fc_register_remoteport(localport, , _port); if (!ret) { /* If the ndlp already has an nrport, this is just @@ -2502,23 +2502,33 @@ lpfc_nvme_register_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) */ rport = remote_port->private; if (ndlp->nrport) { - lpfc_printf_vlog(ndlp->vport, KERN_INFO, -LOG_NVME_DISC, -"6014 Rebinding lport to " -"rport wwpn 0x%llx, " -"Data: x%x x%x x%x x%06x\n", -remote_port->port_name, -remote_port->port_id, -remote_port->port_role, -ndlp->nlp_type, -ndlp->nlp_DID); + if (ndlp->nrport == remote_port->private) { + /* Same remoteport. Just reuse. */ + lpfc_printf_vlog(ndlp->vport, KERN_INFO, +LOG_NVME_DISC, +"6014 Rebinding lport to " +"remoteport %p wwpn 0x%llx, " +"Data: x%x x%x %p x%x x%06x\n",
[PATCH 4/9] lpfc: Increase SCSI CQ and WQ sizes.
Increased the sizes of the SCSI WQ's and CQ's so that SCSI operation is similar to that used by NVME. However, size increase restricted only to those newer adapters that can support the larger WQE size, thus bigger queue sizes. Signed-off-by: Dick KennedySigned-off-by: James Smart --- drivers/scsi/lpfc/lpfc_init.c | 65 --- drivers/scsi/lpfc/lpfc_nvme.h | 2 -- drivers/scsi/lpfc/lpfc_sli4.h | 6 ++-- 3 files changed, 46 insertions(+), 27 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 44a98bc913f5..f539c554588c 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -7983,9 +7983,9 @@ lpfc_alloc_nvme_wq_cq(struct lpfc_hba *phba, int wqidx) { struct lpfc_queue *qdesc; - qdesc = lpfc_sli4_queue_alloc(phba, LPFC_NVME_PAGE_SIZE, + qdesc = lpfc_sli4_queue_alloc(phba, LPFC_EXPANDED_PAGE_SIZE, phba->sli4_hba.cq_esize, - LPFC_NVME_CQSIZE); + LPFC_CQE_EXP_COUNT); if (!qdesc) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0508 Failed allocate fast-path NVME CQ (%d)\n", @@ -7994,8 +7994,8 @@ lpfc_alloc_nvme_wq_cq(struct lpfc_hba *phba, int wqidx) } phba->sli4_hba.nvme_cq[wqidx] = qdesc; - qdesc = lpfc_sli4_queue_alloc(phba, LPFC_NVME_PAGE_SIZE, - LPFC_WQE128_SIZE, LPFC_NVME_WQSIZE); + qdesc = lpfc_sli4_queue_alloc(phba, LPFC_EXPANDED_PAGE_SIZE, + LPFC_WQE128_SIZE, LPFC_WQE_EXP_COUNT); if (!qdesc) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0509 Failed allocate fast-path NVME WQ (%d)\n", @@ -8011,12 +8011,18 @@ static int lpfc_alloc_fcp_wq_cq(struct lpfc_hba *phba, int wqidx) { struct lpfc_queue *qdesc; - uint32_t wqesize; /* Create Fast Path FCP CQs */ - qdesc = lpfc_sli4_queue_alloc(phba, LPFC_DEFAULT_PAGE_SIZE, - phba->sli4_hba.cq_esize, - phba->sli4_hba.cq_ecount); + if (phba->fcp_embed_io) + /* Increase the CQ size when WQEs contain an embedded cdb */ + qdesc = lpfc_sli4_queue_alloc(phba, LPFC_EXPANDED_PAGE_SIZE, + phba->sli4_hba.cq_esize, + LPFC_CQE_EXP_COUNT); + + else + qdesc = lpfc_sli4_queue_alloc(phba, LPFC_DEFAULT_PAGE_SIZE, + phba->sli4_hba.cq_esize, + phba->sli4_hba.cq_ecount); if (!qdesc) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0499 Failed allocate fast-path FCP CQ (%d)\n", wqidx); @@ -8025,10 +8031,15 @@ lpfc_alloc_fcp_wq_cq(struct lpfc_hba *phba, int wqidx) phba->sli4_hba.fcp_cq[wqidx] = qdesc; /* Create Fast Path FCP WQs */ - wqesize = (phba->fcp_embed_io) ? - LPFC_WQE128_SIZE : phba->sli4_hba.wq_esize; - qdesc = lpfc_sli4_queue_alloc(phba, LPFC_DEFAULT_PAGE_SIZE, - wqesize, phba->sli4_hba.wq_ecount); + if (phba->fcp_embed_io) + /* Increase the WQ size when WQEs contain an embedded cdb */ + qdesc = lpfc_sli4_queue_alloc(phba, LPFC_EXPANDED_PAGE_SIZE, + LPFC_WQE128_SIZE, + LPFC_WQE_EXP_COUNT); + else + qdesc = lpfc_sli4_queue_alloc(phba, LPFC_DEFAULT_PAGE_SIZE, + phba->sli4_hba.wq_esize, + phba->sli4_hba.wq_ecount); if (!qdesc) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0503 Failed allocate fast-path FCP WQ (%d)\n", @@ -12216,7 +12227,6 @@ int lpfc_fof_queue_create(struct lpfc_hba *phba) { struct lpfc_queue *qdesc; - uint32_t wqesize; /* Create FOF EQ */ qdesc = lpfc_sli4_queue_alloc(phba, LPFC_DEFAULT_PAGE_SIZE, @@ -12230,21 +12240,32 @@ lpfc_fof_queue_create(struct lpfc_hba *phba) if (phba->cfg_fof) { /* Create OAS CQ */ - qdesc = lpfc_sli4_queue_alloc(phba, LPFC_DEFAULT_PAGE_SIZE, - phba->sli4_hba.cq_esize, - phba->sli4_hba.cq_ecount); + if (phba->fcp_embed_io) + qdesc = lpfc_sli4_queue_alloc(phba, + LPFC_EXPANDED_PAGE_SIZE, + phba->sli4_hba.cq_esize,
[PATCH 8/9] lpfc: Beef up stat counters for debug
If log verbose in not turned on, its hard to tell when certain error paths get hit. Add stats counters and corresponding logic to debugfs/sysfs to aid understanding what paths were traversed. Signed-off-by: Dick KennedySigned-off-by: James Smart --- drivers/scsi/lpfc/lpfc_attr.c| 46 - drivers/scsi/lpfc/lpfc_debugfs.c | 49 +--- drivers/scsi/lpfc/lpfc_nvme.c| 43 +++ drivers/scsi/lpfc/lpfc_nvme.h| 13 ++- drivers/scsi/lpfc/lpfc_nvmet.c | 34 drivers/scsi/lpfc/lpfc_nvmet.h | 5 6 files changed, 171 insertions(+), 19 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 0eef5aa52fc0..797bb42a6306 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -148,6 +148,7 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, struct lpfc_hba *phba = vport->phba; struct lpfc_nvmet_tgtport *tgtp; struct nvme_fc_local_port *localport; + struct lpfc_nvme_lport *lport; struct lpfc_nodelist *ndlp; struct nvme_fc_remote_port *nrport; uint64_t data1, data2, data3, tot; @@ -198,10 +199,15 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, } len += snprintf(buf+len, PAGE_SIZE-len, - "LS: Xmt %08x Drop %08x Cmpl %08x Err %08x\n", + "LS: Xmt %08x Drop %08x Cmpl %08x\n", atomic_read(>xmt_ls_rsp), atomic_read(>xmt_ls_drop), - atomic_read(>xmt_ls_rsp_cmpl), + atomic_read(>xmt_ls_rsp_cmpl)); + + len += snprintf(buf + len, PAGE_SIZE - len, + "LS: RSP Abort %08x xb %08x Err %08x\n", + atomic_read(>xmt_ls_rsp_aborted), + atomic_read(>xmt_ls_rsp_xb_set), atomic_read(>xmt_ls_rsp_error)); len += snprintf(buf+len, PAGE_SIZE-len, @@ -236,6 +242,12 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, atomic_read(>xmt_fcp_rsp_drop)); len += snprintf(buf+len, PAGE_SIZE-len, + "FCP Rsp Abort: %08x xb %08x xricqe %08x\n", + atomic_read(>xmt_fcp_rsp_aborted), + atomic_read(>xmt_fcp_rsp_xb_set), + atomic_read(>xmt_fcp_xri_abort_cqe)); + + len += snprintf(buf + len, PAGE_SIZE - len, "ABORT: Xmt %08x Cmpl %08x\n", atomic_read(>xmt_fcp_abort), atomic_read(>xmt_fcp_abort_cmpl)); @@ -265,6 +277,7 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, } localport = vport->localport; + lport = (struct lpfc_nvme_lport *)localport->private; if (!localport) { len = snprintf(buf, PAGE_SIZE, "NVME Initiator x%llx is not allocated\n", @@ -347,9 +360,16 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, len += snprintf(buf + len, PAGE_SIZE - len, "\nNVME Statistics\n"); len += snprintf(buf+len, PAGE_SIZE-len, - "LS: Xmt %016x Cmpl %016x\n", + "LS: Xmt %010x Cmpl %010x Abort %08x\n", atomic_read(>fc4NvmeLsRequests), - atomic_read(>fc4NvmeLsCmpls)); + atomic_read(>fc4NvmeLsCmpls), + atomic_read(>xmt_ls_abort)); + + len += snprintf(buf + len, PAGE_SIZE - len, + "LS XMIT: Err %08x CMPL: xb %08x Err %08x\n", + atomic_read(>xmt_ls_err), + atomic_read(>cmpl_ls_xb), + atomic_read(>cmpl_ls_err)); tot = atomic_read(>fc4NvmeIoCmpls); data1 = atomic_read(>fc4NvmeInputRequests); @@ -360,8 +380,22 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, data1, data2, data3); len += snprintf(buf+len, PAGE_SIZE-len, - "Cmpl %016llx Outstanding %016llx\n", - tot, (data1 + data2 + data3) - tot); + "noxri %08x nondlp %08x qdepth %08x " + "wqerr %08x\n", + atomic_read(>xmt_fcp_noxri), + atomic_read(>xmt_fcp_bad_ndlp), + atomic_read(>xmt_fcp_qdepth), + atomic_read(>xmt_fcp_wqerr)); + + len += snprintf(buf + len,
[PATCH 6/9] lpfc: Fix issues connecting with nvme initiator
In the lpfc discovery engine, when as a nvme target, where the driver was performing mailbox io with the adapter for port login when a NVME PRLI is received from the host. Rather than queue and eventually get back to sending a response after the mailbox traffic, the driver rejected the io with an error response. Turns out this particular initiator didn't like the rejection values (unable to process command/command in progress) so it never attempted a retry of the PRLI. Thus the host never established nvme connectivity with the lpfc target. By changing the rejection values (to Logical Busy/nothing more), the initiator accepted the response and would retry the PRLI, resulting in nvme connectivity. Signed-off-by: Dick KennedySigned-off-by: James Smart --- drivers/scsi/lpfc/lpfc_nportdisc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c index 283382ac0456..d841aa42f607 100644 --- a/drivers/scsi/lpfc/lpfc_nportdisc.c +++ b/drivers/scsi/lpfc/lpfc_nportdisc.c @@ -1603,8 +1603,8 @@ lpfc_rcv_prli_reglogin_issue(struct lpfc_vport *vport, * rpi registration does complete. */ memset(, 0, sizeof(struct ls_rjt)); - stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; - stat.un.b.lsRjtRsnCodeExp = LSEXP_CMD_IN_PROGRESS; + stat.un.b.lsRjtRsnCode = LSRJT_LOGICAL_BSY; + stat.un.b.lsRjtRsnCodeExp = LSEXP_NOTHING_MORE; lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp, NULL); return ndlp->nlp_state; -- 2.13.1
[PATCH 9/9] lpfc: update driver version to 11.4.0.6
Update the driver version to 11.4.0.6 Signed-off-by: Dick KennedySigned-off-by: James Smart --- drivers/scsi/lpfc/lpfc_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h index cc2f5cec98c5..c232bf0e8998 100644 --- a/drivers/scsi/lpfc/lpfc_version.h +++ b/drivers/scsi/lpfc/lpfc_version.h @@ -20,7 +20,7 @@ * included with this package. * ***/ -#define LPFC_DRIVER_VERSION "11.4.0.5" +#define LPFC_DRIVER_VERSION "11.4.0.6" #define LPFC_DRIVER_NAME "lpfc" /* Used for SLI 2/3 */ -- 2.13.1
[PATCH 5/9] lpfc: Fix SCSI LUN discovery when SCSI and NVME enabled
When enabled for both SCSI and NVME support, and connected pt2pt to a SCSI only target, the driver nodelist entry for the remote port is left in PRLI_ISSUE state and no SCSI LUNs are discovered. Works fine if only configured for SCSI support. Error was due to some of the prli points still reflecting the need to send only 1 PRLI. On a lot of fabric configs, targets were NVME only, which meant the fabric-reported protocol attributes were only telling the driver one protocol or the other. Thus things worked fine. With pt2pt, the driver must send a PRLI for both protocols as there are no hints on what the target supports. Thus pt2pt targets were hitting the multiple PRLI issues. Complete the dual PRLI support. Track explicitly whether scsi (fcp) or nvme prli's have been sent. Accurately track protocol support detected on each node as reported by the fabric or probed by PRLI traffic. Signed-off-by: Dick KennedySigned-off-by: James Smart --- drivers/scsi/lpfc/lpfc_ct.c| 1 + drivers/scsi/lpfc/lpfc_els.c | 30 -- drivers/scsi/lpfc/lpfc_nportdisc.c | 30 +- 3 files changed, 34 insertions(+), 27 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c index 2c1fe5ab3128..9d20d2c208c7 100644 --- a/drivers/scsi/lpfc/lpfc_ct.c +++ b/drivers/scsi/lpfc/lpfc_ct.c @@ -471,6 +471,7 @@ lpfc_prep_node_fc4type(struct lpfc_vport *vport, uint32_t Did, uint8_t fc4_type) "Parse GID_FTrsp: did:x%x flg:x%x x%x", Did, ndlp->nlp_flag, vport->fc_flag); + ndlp->nlp_fc4_type &= ~(NLP_FC4_FCP | NLP_FC4_NVME); /* By default, the driver expects to support FCP FC4 */ if (fc4_type == FC_TYPE_FCP) ndlp->nlp_fc4_type |= NLP_FC4_FCP; diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index 6ffd65a935c4..dfb21d9efb0d 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -2094,6 +2094,10 @@ lpfc_cmpl_els_prli(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, ndlp = (struct lpfc_nodelist *) cmdiocb->context1; spin_lock_irq(shost->host_lock); ndlp->nlp_flag &= ~NLP_PRLI_SND; + + /* Driver supports multiple FC4 types. Counters matter. */ + vport->fc_prli_sent--; + ndlp->fc4_prli_sent--; spin_unlock_irq(shost->host_lock); lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD, @@ -2101,9 +2105,6 @@ lpfc_cmpl_els_prli(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, irsp->ulpStatus, irsp->un.ulpWord[4], ndlp->nlp_DID); - /* Ddriver supports multiple FC4 types. Counters matter. */ - vport->fc_prli_sent--; - /* PRLI completes to NPort */ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, "0103 PRLI completes to NPort x%06x " @@ -2117,7 +2118,6 @@ lpfc_cmpl_els_prli(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, if (irsp->ulpStatus) { /* Check for retry */ - ndlp->fc4_prli_sent--; if (lpfc_els_retry(phba, cmdiocb, rspiocb)) { /* ELS command is being retried */ goto out; @@ -2196,6 +2196,15 @@ lpfc_issue_els_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, ndlp->nlp_fc4_type |= NLP_FC4_NVME; local_nlp_type = ndlp->nlp_fc4_type; + /* This routine will issue 1 or 2 PRLIs, so zero all the ndlp +* fields here before any of them can complete. +*/ + ndlp->nlp_type &= ~(NLP_FCP_TARGET | NLP_FCP_INITIATOR); + ndlp->nlp_type &= ~(NLP_NVME_TARGET | NLP_NVME_INITIATOR); + ndlp->nlp_fcp_info &= ~NLP_FCP_2_DEVICE; + ndlp->nlp_flag &= ~NLP_FIRSTBURST; + ndlp->nvme_fb_size = 0; + send_next_prli: if (local_nlp_type & NLP_FC4_FCP) { /* Payload is 4 + 16 = 20 x14 bytes. */ @@ -2304,6 +2313,13 @@ lpfc_issue_els_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, elsiocb->iocb_cmpl = lpfc_cmpl_els_prli; spin_lock_irq(shost->host_lock); ndlp->nlp_flag |= NLP_PRLI_SND; + + /* The vport counters are used for lpfc_scan_finished, but +* the ndlp is used to track outstanding PRLIs for different +* FC4 types. +*/ + vport->fc_prli_sent++; + ndlp->fc4_prli_sent++; spin_unlock_irq(shost->host_lock); if (lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0) == IOCB_ERROR) { @@ -2314,12 +2330,6 @@ lpfc_issue_els_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, return 1; } - /* The vport counters are used for lpfc_scan_finished, but -* the ndlp is used to track outstanding PRLIs for different -* FC4
[PATCH 1/9] lpfc: Fix random heartbeat timeouts during heavy IO
NVME targets appear to randomly disconnect from the initiator when running heavy IO. The error is due to the host aggregate (across all controllers) io load was beyond the maximum exchange count for nvme on the adapter. The driver was properly returning a resource busy status, but the io load was so great heartbeat commands would be bounced and not have a successful retry within the fuzz amount for the nvme heartbeat (yes, a very high io load!). Thus the target was terminating the controller due to a keep alive failure. Resolve by reserving a few exchanges (by counters) which can be used when the adapter is out of normal exchanges and the command is a NVME heartbeat command. As counters are used, while the reserved command is outstanding, as soon as any other exchange completes, the counters are adjusted and the reserved count is replenished. The heartbeat completes execution in a normal fashion. Signed-off-by: Dick KennedySigned-off-by: James Smart --- drivers/scsi/lpfc/lpfc.h | 2 ++ drivers/scsi/lpfc/lpfc_init.c | 16 ++- drivers/scsi/lpfc/lpfc_nvme.c | 66 +-- drivers/scsi/lpfc/lpfc_nvme.h | 1 + 4 files changed, 63 insertions(+), 22 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index dd2191c83052..61fb46da05d4 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -945,6 +945,8 @@ struct lpfc_hba { struct list_head lpfc_nvme_buf_list_get; struct list_head lpfc_nvme_buf_list_put; uint32_t total_nvme_bufs; + uint32_t get_nvme_bufs; + uint32_t put_nvme_bufs; struct list_head lpfc_iocb_list; uint32_t total_iocbq_bufs; struct list_head active_rrq_list; diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index fa211550a32a..44a98bc913f5 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -1034,6 +1034,7 @@ lpfc_hba_down_post_s4(struct lpfc_hba *phba) LIST_HEAD(nvmet_aborts); unsigned long iflag = 0; struct lpfc_sglq *sglq_entry = NULL; + int cnt; lpfc_sli_hbqbuf_free_all(phba); @@ -1090,11 +1091,14 @@ lpfc_hba_down_post_s4(struct lpfc_hba *phba) spin_unlock_irqrestore(>scsi_buf_list_put_lock, iflag); if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { + cnt = 0; list_for_each_entry_safe(psb, psb_next, _aborts, list) { psb->pCmd = NULL; psb->status = IOSTAT_SUCCESS; + cnt++; } spin_lock_irqsave(>nvme_buf_list_put_lock, iflag); + phba->put_nvme_bufs += cnt; list_splice(_aborts, >lpfc_nvme_buf_list_put); spin_unlock_irqrestore(>nvme_buf_list_put_lock, iflag); @@ -3339,6 +3343,7 @@ lpfc_nvme_free(struct lpfc_hba *phba) list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, >lpfc_nvme_buf_list_put, list) { list_del(_ncmd->list); + phba->put_nvme_bufs--; dma_pool_free(phba->lpfc_sg_dma_buf_pool, lpfc_ncmd->data, lpfc_ncmd->dma_handle); kfree(lpfc_ncmd); @@ -3350,6 +3355,7 @@ lpfc_nvme_free(struct lpfc_hba *phba) list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, >lpfc_nvme_buf_list_get, list) { list_del(_ncmd->list); + phba->get_nvme_bufs--; dma_pool_free(phba->lpfc_sg_dma_buf_pool, lpfc_ncmd->data, lpfc_ncmd->dma_handle); kfree(lpfc_ncmd); @@ -3754,9 +3760,11 @@ lpfc_sli4_nvme_sgl_update(struct lpfc_hba *phba) uint16_t i, lxri, els_xri_cnt; uint16_t nvme_xri_cnt, nvme_xri_max; LIST_HEAD(nvme_sgl_list); - int rc; + int rc, cnt; phba->total_nvme_bufs = 0; + phba->get_nvme_bufs = 0; + phba->put_nvme_bufs = 0; if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)) return 0; @@ -3780,6 +3788,9 @@ lpfc_sli4_nvme_sgl_update(struct lpfc_hba *phba) spin_lock(>nvme_buf_list_put_lock); list_splice_init(>lpfc_nvme_buf_list_get, _sgl_list); list_splice(>lpfc_nvme_buf_list_put, _sgl_list); + cnt = phba->get_nvme_bufs + phba->put_nvme_bufs; + phba->get_nvme_bufs = 0; + phba->put_nvme_bufs = 0; spin_unlock(>nvme_buf_list_put_lock); spin_unlock_irq(>nvme_buf_list_get_lock); @@ -3824,6 +3835,7 @@ lpfc_sli4_nvme_sgl_update(struct lpfc_hba *phba) spin_lock_irq(>nvme_buf_list_get_lock); spin_lock(>nvme_buf_list_put_lock); list_splice_init(_sgl_list, >lpfc_nvme_buf_list_get); + phba->get_nvme_bufs = cnt; INIT_LIST_HEAD(>lpfc_nvme_buf_list_put); spin_unlock(>nvme_buf_list_put_lock);
[PATCH 3/9] lpfc: Fix receive PRLI handling
Handling a rcv'ed PRLI incorrectly can cause the ndlp to end up in the wrong state or the driver to ACC and PRLI when it should send LS_RJT. The cause was due to the driver not properly looking at the PRLI type and taking the multiple protocol support into consideration. Resolved by adding checks in the various PRLI receive points to validate PRLI type and reject if not valid for the enabled protocols and mode (host vs target). Signed-off-by: Dick KennedySigned-off-by: James Smart --- drivers/scsi/lpfc/lpfc_els.c | 7 - drivers/scsi/lpfc/lpfc_nportdisc.c | 54 +- 2 files changed, 47 insertions(+), 14 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index 4a14f3c82a07..6ffd65a935c4 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -8063,13 +8063,6 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, rjt_exp = LSEXP_NOTHING_MORE; break; } - - /* NVMET accepts NVME PRLI only. Reject FCP PRLI */ - if (cmd == ELS_CMD_PRLI && phba->nvmet_support) { - rjt_err = LSRJT_CMD_UNSUPPORTED; - rjt_exp = LSEXP_REQ_UNSUPPORTED; - break; - } lpfc_disc_state_machine(vport, ndlp, elsiocb, NLP_EVT_RCV_PRLI); break; case ELS_CMD_LIRR: diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c index b6957d944b9a..df050b211e0b 100644 --- a/drivers/scsi/lpfc/lpfc_nportdisc.c +++ b/drivers/scsi/lpfc/lpfc_nportdisc.c @@ -727,6 +727,41 @@ lpfc_rcv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, return 0; } +static uint32_t +lpfc_rcv_prli_support_check(struct lpfc_vport *vport, + struct lpfc_nodelist *ndlp, + struct lpfc_iocbq *cmdiocb) +{ + struct ls_rjt stat; + uint32_t *payload; + uint32_t cmd; + + payload = ((struct lpfc_dmabuf *)cmdiocb->context2)->virt; + cmd = *payload; + if (vport->phba->nvmet_support) { + /* Must be a NVME PRLI */ + if (cmd == ELS_CMD_PRLI) + goto out; + } else { + /* Initiator mode. */ + if (!vport->nvmei_support && (cmd == ELS_CMD_NVMEPRLI)) + goto out; + } + return 1; +out: + lpfc_printf_vlog(vport, KERN_WARNING, LOG_NVME_DISC, +"6115 Rcv PRLI (%x) check failed: ndlp rpi %d " +"state x%x flags x%x\n", +cmd, ndlp->nlp_rpi, ndlp->nlp_state, +ndlp->nlp_flag); + memset(, 0, sizeof(struct ls_rjt)); + stat.un.b.lsRjtRsnCode = LSRJT_CMD_UNSUPPORTED; + stat.un.b.lsRjtRsnCodeExp = LSEXP_REQ_UNSUPPORTED; + lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, + ndlp, NULL); + return 0; +} + static void lpfc_rcv_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, struct lpfc_iocbq *cmdiocb) @@ -1373,7 +1408,8 @@ lpfc_rcv_prli_adisc_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, { struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *) arg; - lpfc_els_rsp_prli_acc(vport, cmdiocb, ndlp); + if (lpfc_rcv_prli_support_check(vport, ndlp, cmdiocb)) + lpfc_els_rsp_prli_acc(vport, cmdiocb, ndlp); return ndlp->nlp_state; } @@ -1544,6 +1580,9 @@ lpfc_rcv_prli_reglogin_issue(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *) arg; struct ls_rjt stat; + if (!lpfc_rcv_prli_support_check(vport, ndlp, cmdiocb)) { + return ndlp->nlp_state; + } if (vport->phba->nvmet_support) { /* NVME Target mode. Handle and respond to the PRLI and * transition to UNMAPPED provided the RPI has completed @@ -1558,11 +1597,6 @@ lpfc_rcv_prli_reglogin_issue(struct lpfc_vport *vport, * to prevent an illegal state transition when the * rpi registration does complete. */ - lpfc_printf_vlog(vport, KERN_WARNING, LOG_NVME_DISC, -"6115 NVMET ndlp rpi %d state " -"unknown, state x%x flags x%08x\n", -ndlp->nlp_rpi, ndlp->nlp_state, -ndlp->nlp_flag); memset(, 0, sizeof(struct ls_rjt)); stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; stat.un.b.lsRjtRsnCodeExp = LSEXP_CMD_IN_PROGRESS; @@ -1573,7 +1607,6 @@ lpfc_rcv_prli_reglogin_issue(struct
[PATCH 2/9] lpfc: Fix -EOVERFLOW behavior for NVMET and defer_rcv
The driver is all set to handle the defer_rcv api for the nvmet_fc transport, yet didn't properly recognize the return status when the defer_rcv occurred. The driver treated it simply as an error and aborted the io. Several residual issues occurred at that point. Finish the defer_rcv support: recognize the return status when the io request is being handled in a deferred style. This stops the rogue aborts; Replenish the async cmd rcv buffer in the deferred receive if needed. Signed-off-by: Dick KennedySigned-off-by: James Smart --- drivers/scsi/lpfc/lpfc_nvmet.c | 24 ++-- drivers/scsi/lpfc/lpfc_nvmet.h | 1 + drivers/scsi/lpfc/lpfc_sli.c | 24 +--- 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_nvmet.c b/drivers/scsi/lpfc/lpfc_nvmet.c index d80cd1def3b9..02a1cfa10f72 100644 --- a/drivers/scsi/lpfc/lpfc_nvmet.c +++ b/drivers/scsi/lpfc/lpfc_nvmet.c @@ -38,6 +38,7 @@ #include <../drivers/nvme/host/nvme.h> #include +#include #include "lpfc_version.h" #include "lpfc_hw4.h" @@ -218,6 +219,7 @@ lpfc_nvmet_ctxbuf_post(struct lpfc_hba *phba, struct lpfc_nvmet_ctxbuf *ctx_buf) ctxp->entry_cnt = 1; ctxp->flag = 0; ctxp->ctxbuf = ctx_buf; + ctxp->rqb_buffer = (void *)nvmebuf; spin_lock_init(>ctxlock); #ifdef CONFIG_SCSI_LPFC_DEBUG_FS @@ -253,6 +255,17 @@ lpfc_nvmet_ctxbuf_post(struct lpfc_hba *phba, struct lpfc_nvmet_ctxbuf *ctx_buf) return; } + /* Processing of FCP command is deferred */ + if (rc == -EOVERFLOW) { + lpfc_nvmeio_data(phba, +"NVMET RCV BUSY: xri x%x sz %d " +"from %06x\n", +oxid, size, sid); + /* defer repost rcv buffer till .defer_rcv callback */ + ctxp->flag &= ~LPFC_NVMET_DEFER_RCV_REPOST; + atomic_inc(>rcv_fcp_cmd_out); + return; + } atomic_inc(>rcv_fcp_cmd_drop); lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, "2582 FCP Drop IO x%x: err x%x: x%x x%x x%x\n", @@ -921,7 +934,11 @@ lpfc_nvmet_defer_rcv(struct nvmet_fc_target_port *tgtport, tgtp = phba->targetport->private; atomic_inc(>rcv_fcp_cmd_defer); - lpfc_rq_buf_free(phba, >hbuf); /* repost */ + if (ctxp->flag & LPFC_NVMET_DEFER_RCV_REPOST) + lpfc_rq_buf_free(phba, >hbuf); /* repost */ + else + nvmebuf->hrq->rqbp->rqb_free_buffer(phba, nvmebuf); + ctxp->flag &= ~LPFC_NVMET_DEFER_RCV_REPOST; } static struct nvmet_fc_target_template lpfc_tgttemplate = { @@ -1693,6 +1710,7 @@ lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba, ctxp->entry_cnt = 1; ctxp->flag = 0; ctxp->ctxbuf = ctx_buf; + ctxp->rqb_buffer = (void *)nvmebuf; spin_lock_init(>ctxlock); #ifdef CONFIG_SCSI_LPFC_DEBUG_FS @@ -1726,6 +1744,7 @@ lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba, /* Process FCP command */ if (rc == 0) { + ctxp->rqb_buffer = NULL; atomic_inc(>rcv_fcp_cmd_out); lpfc_rq_buf_free(phba, >hbuf); /* repost */ return; @@ -1737,10 +1756,11 @@ lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba, "NVMET RCV BUSY: xri x%x sz %d from %06x\n", oxid, size, sid); /* defer reposting rcv buffer till .defer_rcv callback */ - ctxp->rqb_buffer = nvmebuf; + ctxp->flag |= LPFC_NVMET_DEFER_RCV_REPOST; atomic_inc(>rcv_fcp_cmd_out); return; } + ctxp->rqb_buffer = nvmebuf; atomic_inc(>rcv_fcp_cmd_drop); lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, diff --git a/drivers/scsi/lpfc/lpfc_nvmet.h b/drivers/scsi/lpfc/lpfc_nvmet.h index 6723e7b81946..03096024e073 100644 --- a/drivers/scsi/lpfc/lpfc_nvmet.h +++ b/drivers/scsi/lpfc/lpfc_nvmet.h @@ -126,6 +126,7 @@ struct lpfc_nvmet_rcv_ctx { #define LPFC_NVMET_XBUSY 0x4 /* XB bit set on IO cmpl */ #define LPFC_NVMET_CTX_RLS 0x8 /* ctx free requested */ #define LPFC_NVMET_ABTS_RCV0x10 /* ABTS received on exchange */ +#define LPFC_NVMET_DEFER_RCV_REPOST0x20 /* repost to RQ on defer rcv */ struct rqb_dmabuf *rqb_buffer; struct lpfc_nvmet_ctxbuf *ctxbuf; diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 1d489b89954e..5f5528a12308 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -475,28 +475,30 @@ lpfc_sli4_rq_put(struct lpfc_queue *hq, struct lpfc_queue *dq,
[PATCH 0/9] lpfc updates for 11.4.0.6
This patch set provides a number of bug fixes and 1 addition to the driver. The patches were cut against the Martin's 4.16/scsi-queue tree. There are no outside dependencies and are expected to be pulled via Martins tree. James Smart (9): lpfc: Fix random heartbeat timeouts during heavy IO lpfc: Fix -EOVERFLOW behavior for NVMET and defer_rcv lpfc: Fix receive PRLI handling lpfc: Increase SCSI CQ and WQ sizes. lpfc: Fix SCSI LUN discovery when SCSI and NVME enabled lpfc: Fix issues connecting with nvme initiator lpfc: Fix infinite wait when driver unregisters a remote NVME port. lpfc: Beef up stat counters for debug lpfc: update driver version to 11.4.0.6 drivers/scsi/lpfc/lpfc.h | 2 + drivers/scsi/lpfc/lpfc_attr.c | 46 ++- drivers/scsi/lpfc/lpfc_ct.c| 1 + drivers/scsi/lpfc/lpfc_debugfs.c | 49 +++- drivers/scsi/lpfc/lpfc_els.c | 37 +++--- drivers/scsi/lpfc/lpfc_init.c | 81 drivers/scsi/lpfc/lpfc_nportdisc.c | 88 + drivers/scsi/lpfc/lpfc_nvme.c | 244 - drivers/scsi/lpfc/lpfc_nvme.h | 16 ++- drivers/scsi/lpfc/lpfc_nvmet.c | 58 +++-- drivers/scsi/lpfc/lpfc_nvmet.h | 6 + drivers/scsi/lpfc/lpfc_sli.c | 24 ++-- drivers/scsi/lpfc/lpfc_sli4.h | 6 +- drivers/scsi/lpfc/lpfc_version.h | 2 +- 14 files changed, 451 insertions(+), 209 deletions(-) -- 2.13.1
[PATCH][next] scsi: arcmsr: remove redundant check for secs < 0
From: Colin Ian KingThe check for secs being less than zero is redundant for two reasons. Firstly, secs is unsigned so the check is always going to be false. Secondly, if secs was signed the proceeding calculation of secs is never going to be negative. Hence we can remove this redundant check and day and secs re-adjustment. Detected by static analysis with smatch: arcmsr_set_iop_datetime() warn: unsigned 'secs' is never less than zero. Signed-off-by: Colin Ian King --- drivers/scsi/arcmsr/arcmsr_hba.c | 4 1 file changed, 4 deletions(-) diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c index 0707a60bf5c0..e4258b69f4be 100644 --- a/drivers/scsi/arcmsr/arcmsr_hba.c +++ b/drivers/scsi/arcmsr/arcmsr_hba.c @@ -3679,10 +3679,6 @@ static void arcmsr_set_iop_datetime(struct timer_list *t) secs = (u32)(tv.tv_sec - (sys_tz.tz_minuteswest * 60)); days = secs / 86400; secs = secs - 86400 * days; - if (secs < 0) { - days = days - 1; - secs = secs + 86400; - } j = days / 146097; i = days - 146097 * j; a = i + 719468; -- 2.14.1
Re: [PATCH] scsi: libiscsi: Allow sd_shutdown on bad transport
On Thu, 2017-12-07 at 19:59 -0200, Rafael David Tinoco wrote: > This happens because iscsi_eh_cmd_timed_out(), the transport layer > timeout helper, would tell the queue timeout function (scsi_times_out) > to reset the request timer over and over, until the session state is > back to logged in state. Unfortunately, during server shutdown, this > might never happen again. Hello Rafael, Have you considered to make iscsi_eh_cmd_timed_out() return BLK_EH_HANDLED if system_state != SYSTEM_RUNNING? That could result in slower shutdown than with your patch but such a change would probably be really easy to review. Thanks, Bart.
[PATCH] sd: Increase SCSI disk probing concurrency
The scsi_sd_probe_domain allows to wait until all disk-probing activity has finished system-wide. This slows down SCSI host removal that occurs concurrently with SCSI disk probing because sd_remove() waits on scsi_sd_probe_domain. Additionally, since each function that waits on scsi_sd_probe_domain specifies for which disk to wait until probing has finished, replace waiting on scsi_sd_probe_domain by waiting until probing for a specific disk has finished. Introduce a .sync() function pointer in struct scsi_driver to make it possible for the SCSI power management code to wait until probing of a specific disk has finished. Signed-off-by: Bart Van AsscheCc: Christoph Hellwig Cc: Hannes Reinecke Cc: Johannes Thumshirn --- drivers/scsi/scsi.c| 5 - drivers/scsi/scsi_pm.c | 6 -- drivers/scsi/scsi_priv.h | 1 - drivers/scsi/sd.c | 26 +- drivers/scsi/sd.h | 1 + include/scsi/scsi_driver.h | 1 + 6 files changed, 27 insertions(+), 13 deletions(-) diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index a7e4fba724b7..e6d69e647f6a 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -85,10 +85,6 @@ unsigned int scsi_logging_level; EXPORT_SYMBOL(scsi_logging_level); #endif -/* sd, scsi core and power management need to coordinate flushing async actions */ -ASYNC_DOMAIN(scsi_sd_probe_domain); -EXPORT_SYMBOL(scsi_sd_probe_domain); - /* * Separate domain (from scsi_sd_probe_domain) to maximize the benefit of * asynchronous system resume operations. It is marked 'exclusive' to avoid @@ -839,7 +835,6 @@ static void __exit exit_scsi(void) scsi_exit_devinfo(); scsi_exit_procfs(); scsi_exit_queue(); - async_unregister_domain(_sd_probe_domain); } subsys_initcall(init_scsi); diff --git a/drivers/scsi/scsi_pm.c b/drivers/scsi/scsi_pm.c index b44c1bb687a2..d8e43c2f4d40 100644 --- a/drivers/scsi/scsi_pm.c +++ b/drivers/scsi/scsi_pm.c @@ -171,9 +171,11 @@ static int scsi_bus_resume_common(struct device *dev, static int scsi_bus_prepare(struct device *dev) { if (scsi_is_sdev_device(dev)) { - /* sd probing uses async_schedule. Wait until it finishes. */ - async_synchronize_full_domain(_sd_probe_domain); + struct scsi_driver *drv = to_scsi_driver(dev->driver); + /* sd probing happens asynchronously. Wait until it finishes. */ + if (drv->sync) + drv->sync(dev); } else if (scsi_is_host_device(dev)) { /* Wait until async scanning is finished */ scsi_complete_async_scans(); diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index 99f1db5e467e..0d88f6b85f5b 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -176,7 +176,6 @@ static inline void scsi_autopm_put_host(struct Scsi_Host *h) {} #endif /* CONFIG_PM */ extern struct async_domain scsi_sd_pm_domain; -extern struct async_domain scsi_sd_probe_domain; /* scsi_dh.c */ #ifdef CONFIG_SCSI_DH diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index ab75ebd518a7..53ec383e10d1 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -112,6 +112,7 @@ static void sd_shutdown(struct device *); static int sd_suspend_system(struct device *); static int sd_suspend_runtime(struct device *); static int sd_resume(struct device *); +static void sd_sync_probe_domain(struct device *dev); static void sd_rescan(struct device *); static int sd_init_command(struct scsi_cmnd *SCpnt); static void sd_uninit_command(struct scsi_cmnd *SCpnt); @@ -564,6 +565,7 @@ static struct scsi_driver sd_template = { .shutdown = sd_shutdown, .pm = _pm_ops, }, + .sync = sd_sync_probe_domain, .rescan = sd_rescan, .init_command = sd_init_command, .uninit_command = sd_uninit_command, @@ -3254,9 +3256,9 @@ static int sd_format_disk_name(char *prefix, int index, char *buf, int buflen) /* * The asynchronous part of sd_probe */ -static void sd_probe_async(void *data, async_cookie_t cookie) +static void sd_probe_async(struct work_struct *work) { - struct scsi_disk *sdkp = data; + struct scsi_disk *sdkp = container_of(work, typeof(*sdkp), probe_work); struct scsi_device *sdp; struct gendisk *gd; u32 index; @@ -3359,6 +3361,8 @@ static int sd_probe(struct device *dev) if (!sdkp) goto out; + INIT_WORK(>probe_work, sd_probe_async); + gd = alloc_disk(SD_MINORS); if (!gd) goto out_free; @@ -3410,8 +3414,8 @@ static int sd_probe(struct device *dev) get_device(dev); dev_set_drvdata(dev, sdkp); - get_device(>dev); /* prevent release before async_schedule */ -
[PATCH 01/19] scsi: hisi_sas: initialize dq spinlock before use
From: Xiang ChenIt is required to initialize the dq spinlock before use, which was not being done, so fix it. This issue can be detected when CONFIG_DEBUG_SPINLOCK is enabled. Signed-off-by: Xiang Chen Signed-off-by: John Garry --- drivers/scsi/hisi_sas/hisi_sas_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 5f503cb..359ec52 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -1657,6 +1657,7 @@ int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost) cq->hisi_hba = hisi_hba; /* Delivery queue structure */ + spin_lock_init(>lock); dq->id = i; dq->hisi_hba = hisi_hba; -- 1.9.1
[PATCH 08/19] scsi: hisi_sas: change ncq process for v3 hw
From: Xiang ChenFor v3 hw, each NCQ will return a CQ, so it is no need to acquire IPTT from ITCT, just acquire it from IPTT field of CQ. Signed-off-by: Xiang Chen Signed-off-by: John Garry --- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 40 +- 1 file changed, 6 insertions(+), 34 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 44f07bc..69aa7bc 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -1653,9 +1653,8 @@ static void cq_tasklet_v3_hw(unsigned long val) struct hisi_sas_cq *cq = (struct hisi_sas_cq *)val; struct hisi_hba *hisi_hba = cq->hisi_hba; struct hisi_sas_slot *slot; - struct hisi_sas_itct *itct; struct hisi_sas_complete_v3_hdr *complete_queue; - u32 rd_point = cq->rd_point, wr_point, dev_id; + u32 rd_point = cq->rd_point, wr_point; int queue = cq->id; struct hisi_sas_dq *dq = _hba->dq[queue]; @@ -1671,38 +1670,11 @@ static void cq_tasklet_v3_hw(unsigned long val) complete_hdr = _queue[rd_point]; - /* Check for NCQ completion */ - if (complete_hdr->act) { - u32 act_tmp = complete_hdr->act; - int ncq_tag_count = ffs(act_tmp); - - dev_id = (complete_hdr->dw1 & CMPLT_HDR_DEV_ID_MSK) >> -CMPLT_HDR_DEV_ID_OFF; - itct = _hba->itct[dev_id]; - - /* The NCQ tags are held in the itct header */ - while (ncq_tag_count) { - __le64 *ncq_tag = >qw4_15[0]; - - ncq_tag_count -= 1; - iptt = (ncq_tag[ncq_tag_count / 5] - >> (ncq_tag_count % 5) * 12) & 0xfff; - - slot = _hba->slot_info[iptt]; - slot->cmplt_queue_slot = rd_point; - slot->cmplt_queue = queue; - slot_complete_v3_hw(hisi_hba, slot); - - act_tmp &= ~(1 << ncq_tag_count); - ncq_tag_count = ffs(act_tmp); - } - } else { - iptt = (complete_hdr->dw1) & CMPLT_HDR_IPTT_MSK; - slot = _hba->slot_info[iptt]; - slot->cmplt_queue_slot = rd_point; - slot->cmplt_queue = queue; - slot_complete_v3_hw(hisi_hba, slot); - } + iptt = (complete_hdr->dw1) & CMPLT_HDR_IPTT_MSK; + slot = _hba->slot_info[iptt]; + slot->cmplt_queue_slot = rd_point; + slot->cmplt_queue = queue; + slot_complete_v3_hw(hisi_hba, slot); if (++rd_point >= HISI_SAS_QUEUE_SLOTS) rd_point = 0; -- 1.9.1
[PATCH 00/19] hisi_sas: PM, RAS, and other misc changes
This patchset contains support for some new features, and also some modifications and other fixes. Headline changes include: - v3 hw Suspend and Resume support - v3 hw RAS (PCI AER) support - v2 hw HW port error handling support - other misc fixes and tidy-up Xiang Chen (8): scsi: hisi_sas: initialize dq spinlock before use scsi: hisi_sas: fix dma_unmap_sg() parameter scsi: hisi_sas: modify hisi_sas_dev_gone() for reset scsi: hisi_sas: change ncq process for v3 hw scsi: hisi_sas: add some print to enhance debugging scsi: hisi_sas: fix SAS_QUEUE_FULL problem while running IO scsi: hisi_sas: re-add the lldd_port_deformed() scsi: hisi_sas: add v3 hw suspend and resume Xiaofei Tan (11): scsi: hisi_sas: relocate clearing ITCT and freeing device scsi: hisi_sas: optimise port id refresh function scsi: hisi_sas: some optimizations of host controller reset scsi: hisi_sas: add an mechanism to do reset work synchronously scsi: hisi_sas: add RAS feature for v3 hw scsi: hisi_sas: improve int_chnl_int_v2_hw() consistenty with v3 hw scsi: hisi_sas: add v2 hw port AXI error handling support scsi: hisi_sas: use an general way to delay PHY work scsi: hisi_sas: do link reset for some CHL_INT2 ints scsi: hisi_sas: judge result of internal abort scsi: hisi_sas: add internal abort dev in some places drivers/scsi/hisi_sas/hisi_sas.h | 40 +++- drivers/scsi/hisi_sas/hisi_sas_main.c | 222 +- drivers/scsi/hisi_sas/hisi_sas_v1_hw.c | 6 +- drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 153 ++- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 330 - 5 files changed, 610 insertions(+), 141 deletions(-) -- 1.9.1
[PATCH 06/19] scsi: hisi_sas: modify hisi_sas_dev_gone() for reset
From: Xiang ChenDo a couple of changes for when HISI_SAS_RESET_BIT is set for HBA: - Clearing ITCT is not necessary - Remove internal abort as it will fail during reset Flag sas_dev->dev_type is kept as SAS_PHY_UNUSED. Signed-off-by: Xiang Chen Signed-off-by: John Garry --- drivers/scsi/hisi_sas/hisi_sas_main.c | 14 -- 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 64d51a8..e4b3092 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -738,17 +738,19 @@ static void hisi_sas_dev_gone(struct domain_device *device) dev_info(dev, "found dev[%d:%x] is gone\n", sas_dev->device_id, sas_dev->dev_type); - hisi_sas_internal_task_abort(hisi_hba, device, + if (!test_bit(HISI_SAS_RESET_BIT, _hba->flags)) { + hisi_sas_internal_task_abort(hisi_hba, device, HISI_SAS_INT_ABT_DEV, 0); - hisi_sas_dereg_device(hisi_hba, device); + hisi_sas_dereg_device(hisi_hba, device); + + hisi_hba->hw->clear_itct(hisi_hba, sas_dev); + device->lldd_dev = NULL; + memset(sas_dev, 0, sizeof(*sas_dev)); + } - hisi_hba->hw->clear_itct(hisi_hba, sas_dev); if (hisi_hba->hw->free_device) hisi_hba->hw->free_device(sas_dev); - - device->lldd_dev = NULL; - memset(sas_dev, 0, sizeof(*sas_dev)); sas_dev->dev_type = SAS_PHY_UNUSED; } -- 1.9.1
[PATCH 07/19] scsi: hisi_sas: add an mechanism to do reset work synchronously
From: Xiaofei TanSometimes it is required to know when the controller reset has completed and also if it has completed successfully. For such places, we call hisi_sas_controller_reset() directly before. That may lead to multiple calls to this function. This patch create a per-reset structure which contains a completion structure and status flag to know when the reset completes and also the status. It is also in hisi_hba.wq to do reset work. As all host reset works are done in hisi_hba.wq, we don't worry multiple calls to hisi_sas_controller_reset(). Signed-off-by: Xiaofei Tan Signed-off-by: John Garry --- drivers/scsi/hisi_sas/hisi_sas.h | 26 ++ drivers/scsi/hisi_sas/hisi_sas_main.c | 19 ++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index b2534ca..71bc8ea 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -99,6 +99,31 @@ struct hisi_sas_hw_error { const struct hisi_sas_hw_error *sub; }; +struct hisi_sas_rst { + struct hisi_hba *hisi_hba; + struct completion *completion; + struct work_struct work; + bool done; +}; + +#define HISI_SAS_RST_WORK_INIT(r, c) \ + { .hisi_hba = hisi_hba, \ + .completion = , \ + .work = __WORK_INITIALIZER(r.work, \ + hisi_sas_sync_rst_work_handler), \ + .done = false, \ + } + +#define HISI_SAS_DECLARE_RST_WORK_ON_STACK(r) \ + DECLARE_COMPLETION_ONSTACK(c); \ + DECLARE_WORK(w, hisi_sas_sync_rst_work_handler); \ + struct hisi_sas_rst r = HISI_SAS_RST_WORK_INIT(r, c) + +enum hisi_sas_bit_err_type { + HISI_SAS_ERR_SINGLE_BIT_ECC = 0x0, + HISI_SAS_ERR_MULTI_BIT_ECC = 0x1, +}; + struct hisi_sas_phy { struct hisi_hba *hisi_hba; struct hisi_sas_port*port; @@ -426,5 +451,6 @@ extern void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot); extern void hisi_sas_init_mem(struct hisi_hba *hisi_hba); extern void hisi_sas_rst_work_handler(struct work_struct *work); +extern void hisi_sas_sync_rst_work_handler(struct work_struct *work); extern void hisi_sas_kill_tasklets(struct hisi_hba *hisi_hba); #endif diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index e4b3092..fb162c0 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -1299,8 +1299,14 @@ static int hisi_sas_lu_reset(struct domain_device *device, u8 *lun) static int hisi_sas_clear_nexus_ha(struct sas_ha_struct *sas_ha) { struct hisi_hba *hisi_hba = sas_ha->lldd_ha; + HISI_SAS_DECLARE_RST_WORK_ON_STACK(r); - return hisi_sas_controller_reset(hisi_hba); + queue_work(hisi_hba->wq, ); + wait_for_completion(r.completion); + if (r.done) + return TMF_RESP_FUNC_COMPLETE; + + return TMF_RESP_FUNC_FAILED; } static int hisi_sas_query_task(struct sas_task *task) @@ -1820,6 +1826,17 @@ void hisi_sas_rst_work_handler(struct work_struct *work) } EXPORT_SYMBOL_GPL(hisi_sas_rst_work_handler); +void hisi_sas_sync_rst_work_handler(struct work_struct *work) +{ + struct hisi_sas_rst *rst = + container_of(work, struct hisi_sas_rst, work); + + if (!hisi_sas_controller_reset(rst->hisi_hba)) + rst->done = true; + complete(rst->completion); +} +EXPORT_SYMBOL_GPL(hisi_sas_sync_rst_work_handler); + int hisi_sas_get_fw_info(struct hisi_hba *hisi_hba) { struct device *dev = hisi_hba->dev; -- 1.9.1
[PATCH 04/19] scsi: hisi_sas: optimise port id refresh function
From: Xiaofei TanCurrently refreshing the PHY port id after reset is done in the rescan topology function, which is quite late in the reset process. It could be moved earlier in the process, as the port id can be refreshed once the PHYs become ready. In addition to this, we should set the hisi_sas_dev port id to 0xff (invalid port id) if all PHYs of this port remain down for the same device. Signed-off-by: Xiaofei Tan Signed-off-by: John Garry --- drivers/scsi/hisi_sas/hisi_sas_main.c | 48 ++- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 6446ce2..326ecb2 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -990,27 +990,42 @@ static int hisi_sas_debug_issue_ssp_tmf(struct domain_device *device, sizeof(ssp_task), tmf); } -static void hisi_sas_refresh_port_id(struct hisi_hba *hisi_hba, - struct asd_sas_port *sas_port, enum sas_linkrate linkrate) +static void hisi_sas_refresh_port_id(struct hisi_hba *hisi_hba) { - struct hisi_sas_device *sas_dev; - struct domain_device *device; + u32 state = hisi_hba->hw->get_phys_state(hisi_hba); int i; for (i = 0; i < HISI_SAS_MAX_DEVICES; i++) { - sas_dev = _hba->devices[i]; - device = sas_dev->sas_device; + struct hisi_sas_device *sas_dev = _hba->devices[i]; + struct domain_device *device = sas_dev->sas_device; + struct asd_sas_port *sas_port; + struct hisi_sas_port *port; + struct hisi_sas_phy *phy = NULL; + struct asd_sas_phy *sas_phy; + if ((sas_dev->dev_type == SAS_PHY_UNUSED) - || !device || (device->port != sas_port)) + || !device || !device->port) continue; - hisi_hba->hw->clear_itct(hisi_hba, sas_dev); + sas_port = device->port; + port = to_hisi_sas_port(sas_port); + + list_for_each_entry(sas_phy, _port->phy_list, port_phy_el) + if (state & BIT(sas_phy->id)) { + phy = sas_phy->lldd_phy; + break; + } + + if (phy) { + port->id = phy->port_id; - /* Update linkrate of directly attached device. */ - if (!device->parent) - device->linkrate = linkrate; + /* Update linkrate of directly attached device. */ + if (!device->parent) + device->linkrate = phy->sas_phy.linkrate; - hisi_hba->hw->setup_itct(hisi_hba, sas_dev); + hisi_hba->hw->setup_itct(hisi_hba, sas_dev); + } else + port->id = 0xff; } } @@ -1025,21 +1040,17 @@ static void hisi_sas_rescan_topology(struct hisi_hba *hisi_hba, u32 old_state, struct hisi_sas_phy *phy = _hba->phy[phy_no]; struct asd_sas_phy *sas_phy = >sas_phy; struct asd_sas_port *sas_port = sas_phy->port; - struct hisi_sas_port *port = to_hisi_sas_port(sas_port); bool do_port_check = !!(_sas_port != sas_port); if (!sas_phy->phy->enabled) continue; /* Report PHY state change to libsas */ - if (state & (1 << phy_no)) { - if (do_port_check && sas_port) { + if (state & BIT(phy_no)) { + if (do_port_check && sas_port && sas_port->port_dev) { struct domain_device *dev = sas_port->port_dev; _sas_port = sas_port; - port->id = phy->port_id; - hisi_sas_refresh_port_id(hisi_hba, - sas_port, sas_phy->linkrate); if (DEV_IS_EXPANDER(dev->dev_type)) sas_ha->notify_port_event(sas_phy, @@ -1088,6 +1099,7 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba) /* Init and wait for PHYs to come up and all libsas event finished. */ hisi_hba->hw->phys_init(hisi_hba); msleep(1000); + hisi_sas_refresh_port_id(hisi_hba); drain_workqueue(hisi_hba->wq); drain_workqueue(shost->work_q); -- 1.9.1
[PATCH 10/19] scsi: hisi_sas: add some print to enhance debugging
From: Xiang ChenAdd some print at some places such as error info and cq of exception IO, device found etc, and also adjust some log levels. All this to assist debugging ability. Signed-off-by: Xiang Chen Signed-off-by: John Garry --- drivers/scsi/hisi_sas/hisi_sas_main.c | 15 ++- drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 24 +++- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 22 +- 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index fb162c0..1f6f063 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -580,6 +580,9 @@ static int hisi_sas_dev_found(struct domain_device *device) } } + dev_info(dev, "dev[%d:%x] found\n", + sas_dev->device_id, sas_dev->dev_type); + return 0; } @@ -735,7 +738,7 @@ static void hisi_sas_dev_gone(struct domain_device *device) struct hisi_hba *hisi_hba = dev_to_hisi_hba(device); struct device *dev = hisi_hba->dev; - dev_info(dev, "found dev[%d:%x] is gone\n", + dev_info(dev, "dev[%d:%x] is gone\n", sas_dev->device_id, sas_dev->dev_type); if (!test_bit(HISI_SAS_RESET_BIT, _hba->flags)) { @@ -866,12 +869,13 @@ static int hisi_sas_exec_internal_tmf_task(struct domain_device *device, if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { struct hisi_sas_slot *slot = task->lldd_task; - dev_err(dev, "abort tmf: TMF task timeout\n"); + dev_err(dev, "abort tmf: TMF task timeout and not done\n"); if (slot) slot->task = NULL; goto ex_err; - } + } else + dev_err(dev, "abort tmf: TMF task timeout\n"); } if (task->task_status.resp == SAS_TASK_COMPLETE && @@ -1495,9 +1499,10 @@ static int hisi_sas_query_task(struct sas_task *task) if (slot) slot->task = NULL; - dev_err(dev, "internal task abort: timeout.\n"); + dev_err(dev, "internal task abort: timeout and not done.\n"); goto exit; - } + } else + dev_err(dev, "internal task abort: timeout.\n"); } if (task->task_status.resp == SAS_TASK_COMPLETE && diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index cd9cd84..8d6886a 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -2361,6 +2361,7 @@ static void slot_err_v2_hw(struct hisi_hba *hisi_hba, ts->resp = SAS_TASK_COMPLETE; if (unlikely(aborted)) { + dev_dbg(dev, "slot_complete: task(%p) aborted\n", task); ts->stat = SAS_ABORTED_TASK; spin_lock_irqsave(_hba->lock, flags); hisi_sas_slot_task_free(hisi_hba, task, slot); @@ -2405,6 +2406,7 @@ static void slot_err_v2_hw(struct hisi_hba *hisi_hba, (!(complete_hdr->dw0 & CMPLT_HDR_RSPNS_XFRD_MSK))) { u32 err_phase = (complete_hdr->dw0 & CMPLT_HDR_ERR_PHASE_MSK) >> CMPLT_HDR_ERR_PHASE_OFF; + u32 *error_info = hisi_sas_status_buf_addr_mem(slot); /* Analyse error happens on which phase TX or RX */ if (ERR_ON_TX_PHASE(err_phase)) @@ -2412,6 +2414,16 @@ static void slot_err_v2_hw(struct hisi_hba *hisi_hba, else if (ERR_ON_RX_PHASE(err_phase)) slot_err_v2_hw(hisi_hba, task, slot, 2); + if (ts->stat != SAS_DATA_UNDERRUN) + dev_info(dev, "erroneous completion iptt=%d task=%p " + "CQ hdr: 0x%x 0x%x 0x%x 0x%x " + "Error info: 0x%x 0x%x 0x%x 0x%x\n", + slot->idx, task, + complete_hdr->dw0, complete_hdr->dw1, + complete_hdr->act, complete_hdr->dw3, + error_info[0], error_info[1], + error_info[2], error_info[3]); + if (unlikely(slot->abort)) return ts->stat; goto out; @@ -2461,7 +2473,7 @@ static void slot_err_v2_hw(struct hisi_hba *hisi_hba, } if (!slot->port->port_attached) { - dev_err(dev, "slot complete: port %d has removed\n", + dev_warn(dev, "slot complete: port %d has removed\n",
[PATCH 14/19] scsi: hisi_sas: do link reset for some CHL_INT2 ints
From: Xiaofei TanWe should do link reset of PHY when identify timeout or STP link timeout. They are internal events of SOC and are notified to driver through interrupts of CHL_INT2. Besides, we should add an delay work to do link reset as it needs sleep. So, this patch add an new PHY event HISI_PHYE_LINK_RESET for this. Notes: v2 HW doesn't report the event of STP link timeout. So, we only need to handle event of identify timeout for v2 HW. Signed-off-by: Xiaofei Tan Signed-off-by: John Garry --- drivers/scsi/hisi_sas/hisi_sas.h | 1 + drivers/scsi/hisi_sas/hisi_sas_main.c | 12 drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 18 ++ drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 29 +++-- 4 files changed, 54 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index aa14638..4343c4c 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -126,6 +126,7 @@ enum hisi_sas_bit_err_type { enum hisi_sas_phy_event { HISI_PHYE_PHY_UP = 0U, + HISI_PHYE_LINK_RESET, HISI_PHYES_NUM, }; diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 326dc81..7446a39 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -22,6 +22,8 @@ static int hisi_sas_debug_issue_ssp_tmf(struct domain_device *device, struct domain_device *device, int abort_flag, int tag); static int hisi_sas_softreset_ata_disk(struct domain_device *device); +static int hisi_sas_control_phy(struct asd_sas_phy *sas_phy, enum phy_func func, + void *funcdata); u8 hisi_sas_get_ata_protocol(u8 cmd, int direction) { @@ -631,8 +633,18 @@ static void hisi_sas_phyup_work(struct work_struct *work) hisi_sas_bytes_dmaed(hisi_hba, phy_no); } +static void hisi_sas_linkreset_work(struct work_struct *work) +{ + struct hisi_sas_phy *phy = + container_of(work, typeof(*phy), works[HISI_PHYE_LINK_RESET]); + struct asd_sas_phy *sas_phy = >sas_phy; + + hisi_sas_control_phy(sas_phy, PHY_FUNC_LINK_RESET, NULL); +} + static const work_func_t hisi_sas_phye_fns[HISI_PHYES_NUM] = { [HISI_PHYE_PHY_UP] = hisi_sas_phyup_work, + [HISI_PHYE_LINK_RESET] = hisi_sas_linkreset_work, }; bool hisi_sas_notify_phy_event(struct hisi_sas_phy *phy, diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index e521c42..b8fe08d 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -245,6 +245,7 @@ #define CHL_INT1_DMAC_RX_AXI_WR_ERR_OFF21 #define CHL_INT1_DMAC_RX_AXI_RD_ERR_OFF22 #define CHL_INT2 (PORT_BASE + 0x1bc) +#define CHL_INT2_SL_IDAF_TOUT_CONF_OFF 0 #define CHL_INT0_MSK (PORT_BASE + 0x1c0) #define CHL_INT1_MSK (PORT_BASE + 0x1c4) #define CHL_INT2_MSK (PORT_BASE + 0x1c8) @@ -1187,7 +1188,7 @@ static void init_reg_v2_hw(struct hisi_hba *hisi_hba) hisi_sas_phy_write32(hisi_hba, i, CHL_INT2, 0xfff87fff); hisi_sas_phy_write32(hisi_hba, i, RXOP_CHECK_CFG_H, 0x1000); hisi_sas_phy_write32(hisi_hba, i, CHL_INT1_MSK, 0xff857fff); - hisi_sas_phy_write32(hisi_hba, i, CHL_INT2_MSK, 0x8bff); + hisi_sas_phy_write32(hisi_hba, i, CHL_INT2_MSK, 0x8bfe); hisi_sas_phy_write32(hisi_hba, i, SL_CFG, 0x13f801fc); hisi_sas_phy_write32(hisi_hba, i, PHY_CTRL_RDY_MSK, 0x0); hisi_sas_phy_write32(hisi_hba, i, PHYCTRL_NOT_RDY_MSK, 0x0); @@ -2905,10 +2906,19 @@ static irqreturn_t int_chnl_int_v2_hw(int irq_no, void *p) CHL_INT1, irq_value1); } - if ((irq_msk & (1 << phy_no)) && irq_value2) - hisi_sas_phy_write32(hisi_hba, phy_no, -CHL_INT2, irq_value2); + if ((irq_msk & (1 << phy_no)) && irq_value2) { + struct hisi_sas_phy *phy = _hba->phy[phy_no]; + + if (irq_value2 & BIT(CHL_INT2_SL_IDAF_TOUT_CONF_OFF)) { + dev_warn(dev, "phy%d identify timeout\n", + phy_no); + hisi_sas_notify_phy_event(phy, + HISI_PHYE_LINK_RESET); + } + hisi_sas_phy_write32(hisi_hba, phy_no, +CHL_INT2, irq_value2); + } if ((irq_msk & (1 << phy_no)) && irq_value0) { if
[PATCH 09/19] scsi: hisi_sas: add RAS feature for v3 hw
From: Xiaofei TanWe use PCIe AER to support RAS feature for v3 hw. This driver should do following two things to support this: 1. Enable RAS interrupts, so that errors can be reported to RAS module. 2. Realize err_handler for sas_v3_pci_driver. Then if non-fatal error is detected, print error source and try to recover SAS controller. Signed-off-by: Xiaofei Tan Signed-off-by: John Garry --- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 139 + 1 file changed, 139 insertions(+) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 69aa7bc..d356e12 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -204,6 +204,13 @@ #define AM_ROB_ECC_MULBIT_ERR_ADDR_OFF 8 #define AM_ROB_ECC_MULBIT_ERR_ADDR_MSK (0xff << AM_ROB_ECC_MULBIT_ERR_ADDR_OFF) +/* RAS registers need init */ +#define RAS_BASE (0x6000) +#define SAS_RAS_INTR0 (RAS_BASE) +#define SAS_RAS_INTR1 (RAS_BASE + 0x04) +#define SAS_RAS_INTR0_MASK (RAS_BASE + 0x08) +#define SAS_RAS_INTR1_MASK (RAS_BASE + 0x0c) + /* HW dma structures */ /* Delivery queue header */ /* dw0 */ @@ -496,6 +503,10 @@ static void init_reg_v3_hw(struct hisi_hba *hisi_hba) hisi_sas_write32(hisi_hba, SATA_INITI_D2H_STORE_ADDR_HI, upper_32_bits(hisi_hba->initial_fis_dma)); + + /* RAS registers init */ + hisi_sas_write32(hisi_hba, SAS_RAS_INTR0_MASK, 0x0); + hisi_sas_write32(hisi_hba, SAS_RAS_INTR1_MASK, 0x0); } static void config_phy_opt_mode_v3_hw(struct hisi_hba *hisi_hba, int phy_no) @@ -2129,6 +2140,127 @@ static void hisi_sas_v3_remove(struct pci_dev *pdev) scsi_host_put(shost); } +static const struct hisi_sas_hw_error sas_ras_intr0_nfe[] = { + { .irq_msk = BIT(19), .msg = "HILINK_INT" }, + { .irq_msk = BIT(20), .msg = "HILINK_PLL0_OUT_OF_LOCK" }, + { .irq_msk = BIT(21), .msg = "HILINK_PLL1_OUT_OF_LOCK" }, + { .irq_msk = BIT(22), .msg = "HILINK_LOSS_OF_REFCLK0" }, + { .irq_msk = BIT(23), .msg = "HILINK_LOSS_OF_REFCLK1" }, + { .irq_msk = BIT(24), .msg = "DMAC0_TX_POISON" }, + { .irq_msk = BIT(25), .msg = "DMAC1_TX_POISON" }, + { .irq_msk = BIT(26), .msg = "DMAC2_TX_POISON" }, + { .irq_msk = BIT(27), .msg = "DMAC3_TX_POISON" }, + { .irq_msk = BIT(28), .msg = "DMAC4_TX_POISON" }, + { .irq_msk = BIT(29), .msg = "DMAC5_TX_POISON" }, + { .irq_msk = BIT(30), .msg = "DMAC6_TX_POISON" }, + { .irq_msk = BIT(31), .msg = "DMAC7_TX_POISON" }, +}; + +static const struct hisi_sas_hw_error sas_ras_intr1_nfe[] = { + { .irq_msk = BIT(0), .msg = "RXM_CFG_MEM3_ECC2B_INTR" }, + { .irq_msk = BIT(1), .msg = "RXM_CFG_MEM2_ECC2B_INTR" }, + { .irq_msk = BIT(2), .msg = "RXM_CFG_MEM1_ECC2B_INTR" }, + { .irq_msk = BIT(3), .msg = "RXM_CFG_MEM0_ECC2B_INTR" }, + { .irq_msk = BIT(4), .msg = "HGC_CQE_ECC2B_INTR" }, + { .irq_msk = BIT(5), .msg = "LM_CFG_IOSTL_ECC2B_INTR" }, + { .irq_msk = BIT(6), .msg = "LM_CFG_ITCTL_ECC2B_INTR" }, + { .irq_msk = BIT(7), .msg = "HGC_ITCT_ECC2B_INTR" }, + { .irq_msk = BIT(8), .msg = "HGC_IOST_ECC2B_INTR" }, + { .irq_msk = BIT(9), .msg = "HGC_DQE_ECC2B_INTR" }, + { .irq_msk = BIT(10), .msg = "DMAC0_RAM_ECC2B_INTR" }, + { .irq_msk = BIT(11), .msg = "DMAC1_RAM_ECC2B_INTR" }, + { .irq_msk = BIT(12), .msg = "DMAC2_RAM_ECC2B_INTR" }, + { .irq_msk = BIT(13), .msg = "DMAC3_RAM_ECC2B_INTR" }, + { .irq_msk = BIT(14), .msg = "DMAC4_RAM_ECC2B_INTR" }, + { .irq_msk = BIT(15), .msg = "DMAC5_RAM_ECC2B_INTR" }, + { .irq_msk = BIT(16), .msg = "DMAC6_RAM_ECC2B_INTR" }, + { .irq_msk = BIT(17), .msg = "DMAC7_RAM_ECC2B_INTR" }, + { .irq_msk = BIT(18), .msg = "OOO_RAM_ECC2B_INTR" }, + { .irq_msk = BIT(20), .msg = "HGC_DQE_POISON_INTR" }, + { .irq_msk = BIT(21), .msg = "HGC_IOST_POISON_INTR" }, + { .irq_msk = BIT(22), .msg = "HGC_ITCT_POISON_INTR" }, + { .irq_msk = BIT(23), .msg = "HGC_ITCT_NCQ_POISON_INTR" }, + { .irq_msk = BIT(24), .msg = "DMAC0_RX_POISON" }, + { .irq_msk = BIT(25), .msg = "DMAC1_RX_POISON" }, + { .irq_msk = BIT(26), .msg = "DMAC2_RX_POISON" }, + { .irq_msk = BIT(27), .msg = "DMAC3_RX_POISON" }, + { .irq_msk = BIT(28), .msg = "DMAC4_RX_POISON" }, + { .irq_msk = BIT(29), .msg = "DMAC5_RX_POISON" }, + { .irq_msk = BIT(30), .msg = "DMAC6_RX_POISON" }, + { .irq_msk = BIT(31), .msg = "DMAC7_RX_POISON" }, +}; + +static bool process_non_fatal_error_v3_hw(struct hisi_hba *hisi_hba) +{ + struct device *dev = hisi_hba->dev; + const struct hisi_sas_hw_error *ras_error; + bool need_reset = false; + u32 irq_value; + int i; + + irq_value = hisi_sas_read32(hisi_hba,
[PATCH 05/19] scsi: hisi_sas: some optimizations of host controller reset
From: Xiaofei TanThis patch do following optimizations to host controller reset: 1. Unblock scsi requests before rescanning topology, as SCSI command need be used if new device is found during rescanning topology. 2. Remove drain_workqueue(hisi_hba->wq) and drain_workqueue(shost->work_q), as there is no need to ensure that all PHYs event are done before exiting host reset. 3. Improve message print level of host reset. Host reset is an important and very few occurrence event. We should know its progress even when not debugging. Signed-off-by: Xiaofei Tan Signed-off-by: John Garry --- drivers/scsi/hisi_sas/hisi_sas_main.c | 11 --- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 326ecb2..64d51a8 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -1061,8 +1061,6 @@ static void hisi_sas_rescan_topology(struct hisi_hba *hisi_hba, u32 old_state, hisi_sas_phy_down(hisi_hba, phy_no, 0); } - - drain_workqueue(hisi_hba->shost->work_q); } static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba) @@ -1079,7 +1077,7 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba) if (test_and_set_bit(HISI_SAS_RESET_BIT, _hba->flags)) return -1; - dev_dbg(dev, "controller resetting...\n"); + dev_info(dev, "controller resetting...\n"); old_state = hisi_hba->hw->get_phys_state(hisi_hba); scsi_block_requests(shost); @@ -1088,6 +1086,7 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba) if (rc) { dev_warn(dev, "controller reset failed (%d)\n", rc); clear_bit(HISI_SAS_REJECT_CMD_BIT, _hba->flags); + scsi_unblock_requests(shost); goto out; } spin_lock_irqsave(_hba->lock, flags); @@ -1100,15 +1099,13 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba) hisi_hba->hw->phys_init(hisi_hba); msleep(1000); hisi_sas_refresh_port_id(hisi_hba); - drain_workqueue(hisi_hba->wq); - drain_workqueue(shost->work_q); + scsi_unblock_requests(shost); state = hisi_hba->hw->get_phys_state(hisi_hba); hisi_sas_rescan_topology(hisi_hba, old_state, state); - dev_dbg(dev, "controller reset complete\n"); + dev_info(dev, "controller reset complete\n"); out: - scsi_unblock_requests(shost); clear_bit(HISI_SAS_RESET_BIT, _hba->flags); return rc; -- 1.9.1
[PATCH 18/19] scsi: hisi_sas: re-add the lldd_port_deformed()
From: Xiang ChenIn function sas_suspend_devices(), it requires callback lldd_port_deformed callback to be implemented if lldd_port_deformed is implemented. So add a stub for lldd_port_deformed. Callback lldd_port_deformed was not required as the port deformation is done elsewhere in the LLDD. Signed-off-by: Xiang Chen Signed-off-by: John Garry --- drivers/scsi/hisi_sas/hisi_sas_main.c | 5 + 1 file changed, 5 insertions(+) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 9bd98e5..ad12237 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -1613,6 +1613,10 @@ static void hisi_sas_port_formed(struct asd_sas_phy *sas_phy) hisi_sas_port_notify_formed(sas_phy); } +static void hisi_sas_port_deformed(struct asd_sas_phy *sas_phy) +{ +} + static void hisi_sas_phy_disconnected(struct hisi_sas_phy *phy) { phy->phy_attached = 0; @@ -1703,6 +1707,7 @@ void hisi_sas_kill_tasklets(struct hisi_hba *hisi_hba) .lldd_query_task= hisi_sas_query_task, .lldd_clear_nexus_ha = hisi_sas_clear_nexus_ha, .lldd_port_formed = hisi_sas_port_formed, + .lldd_port_deformed = hisi_sas_port_deformed, }; void hisi_sas_init_mem(struct hisi_hba *hisi_hba) -- 1.9.1
[PATCH 17/19] scsi: hisi_sas: fix SAS_QUEUE_FULL problem while running IO
From: Xiang ChenThis patch fix SAS_QUEUE_FULL problem. The test situation is close port while running IO. In sas_eh_handle_sas_errors(), SCSI EH will free sas_task of the device if lldd_I_T_nexus_reset() return TMF_RESP_FUNC_COMPLETE or -ENODEV. But in our SAS driver, we only free slots of the device when the return value is TMF_RESP_FUNC_COMPLETE. So if the return value is -ENODEV, the slot resource will not free any more. As an solution, we should also free slots of the device in lldd_I_T_nexus_reset() if the return value is -ENODEV. Signed-off-by: Xiaofei Tan Signed-off-by: John Garry --- drivers/scsi/hisi_sas/hisi_sas_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 302da84..9bd98e5 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -1308,7 +1308,7 @@ static int hisi_sas_I_T_nexus_reset(struct domain_device *device) rc = hisi_sas_debug_I_T_nexus_reset(device); - if (rc == TMF_RESP_FUNC_COMPLETE) { + if ((rc == TMF_RESP_FUNC_COMPLETE) || (rc == -ENODEV)) { spin_lock_irqsave(_hba->lock, flags); hisi_sas_release_task(hisi_hba, device); spin_unlock_irqrestore(_hba->lock, flags); -- 1.9.1
[PATCH 03/19] scsi: hisi_sas: relocate clearing ITCT and freeing device
From: Xiaofei TanIn certain scenarios we may just want to clear the ITCT for a device, and not free other resources like the SATA bitmap using in v2 hw. To facilitate this, this patch relocates the code of clearing ITCT from free_device() to an new hw interface clear_itct(). Then for some hw, we should not realise free_device() if there's nothing left to do for it. Signed-off-by: Xiaofei Tan Signed-off-by: John Garry --- drivers/scsi/hisi_sas/hisi_sas.h | 3 ++- drivers/scsi/hisi_sas/hisi_sas_main.c | 7 +-- drivers/scsi/hisi_sas/hisi_sas_v1_hw.c | 4 ++-- drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 16 +++- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 4 ++-- 5 files changed, 22 insertions(+), 12 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index 83357b03..b2534ca 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -205,8 +205,9 @@ struct hisi_sas_hw { void (*phy_set_linkrate)(struct hisi_hba *hisi_hba, int phy_no, struct sas_phy_linkrates *linkrates); enum sas_linkrate (*phy_get_max_linkrate)(void); - void (*free_device)(struct hisi_hba *hisi_hba, + void (*clear_itct)(struct hisi_hba *hisi_hba, struct hisi_sas_device *dev); + void (*free_device)(struct hisi_sas_device *sas_dev); int (*get_wideport_bitmap)(struct hisi_hba *hisi_hba, int port_id); void (*dereg_device)(struct hisi_hba *hisi_hba, struct domain_device *device); diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index d842530..6446ce2 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -743,7 +743,10 @@ static void hisi_sas_dev_gone(struct domain_device *device) hisi_sas_dereg_device(hisi_hba, device); - hisi_hba->hw->free_device(hisi_hba, sas_dev); + hisi_hba->hw->clear_itct(hisi_hba, sas_dev); + if (hisi_hba->hw->free_device) + hisi_hba->hw->free_device(sas_dev); + device->lldd_dev = NULL; memset(sas_dev, 0, sizeof(*sas_dev)); sas_dev->dev_type = SAS_PHY_UNUSED; @@ -1001,7 +1004,7 @@ static void hisi_sas_refresh_port_id(struct hisi_hba *hisi_hba, || !device || (device->port != sas_port)) continue; - hisi_hba->hw->free_device(hisi_hba, sas_dev); + hisi_hba->hw->clear_itct(hisi_hba, sas_dev); /* Update linkrate of directly attached device. */ if (!device->parent) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c index dc6eca8..8cb9061 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c @@ -544,7 +544,7 @@ static void setup_itct_v1_hw(struct hisi_hba *hisi_hba, (0xff00ULL << ITCT_HDR_REJ_OPEN_TL_OFF)); } -static void free_device_v1_hw(struct hisi_hba *hisi_hba, +static void clear_itct_v1_hw(struct hisi_hba *hisi_hba, struct hisi_sas_device *sas_dev) { u64 dev_id = sas_dev->device_id; @@ -1850,7 +1850,7 @@ static int hisi_sas_v1_init(struct hisi_hba *hisi_hba) .hw_init = hisi_sas_v1_init, .setup_itct = setup_itct_v1_hw, .sl_notify = sl_notify_v1_hw, - .free_device = free_device_v1_hw, + .clear_itct = clear_itct_v1_hw, .prep_smp = prep_smp_v1_hw, .prep_ssp = prep_ssp_v1_hw, .get_free_slot = get_free_slot_v1_hw, diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index 5d3467f..cd9cd84 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -952,7 +952,7 @@ static void setup_itct_v2_hw(struct hisi_hba *hisi_hba, (0x1ULL << ITCT_HDR_RTOLT_OFF)); } -static void free_device_v2_hw(struct hisi_hba *hisi_hba, +static void clear_itct_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_device *sas_dev) { DECLARE_COMPLETION_ONSTACK(completion); @@ -963,10 +963,6 @@ static void free_device_v2_hw(struct hisi_hba *hisi_hba, sas_dev->completion = - /* SoC bug workaround */ - if (dev_is_sata(sas_dev->sas_device)) - clear_bit(sas_dev->sata_idx, hisi_hba->sata_dev_bitmap); - /* clear the itct interrupt state */ if (ENT_INT_SRC3_ITC_INT_MSK & reg_val) hisi_sas_write32(hisi_hba, ENT_INT_SRC3, @@ -981,6 +977,15 @@ static void free_device_v2_hw(struct hisi_hba *hisi_hba, } } +static void free_device_v2_hw(struct hisi_sas_device *sas_dev) +{ + struct hisi_hba *hisi_hba = sas_dev->hisi_hba; + + /* SoC bug
[PATCH 16/19] scsi: hisi_sas: add internal abort dev in some places
From: Xiaofei TanWe should do internal abort dev before TMF_ABORT_TASK_SET and TMF_LU_RESET. Because we may only have done internal abort for single IO in the earlier part of SCSI EH process. Even the internal abort to the single IO, we also don't know whether it is successful. Besides, we should release slots of the device in hisi_sas_abort_task_set() if the abort is successful. Signed-off-by: Xiaofei Tan Signed-off-by: John Garry --- drivers/scsi/hisi_sas/hisi_sas_main.c | 25 + 1 file changed, 25 insertions(+) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 1b9c48c..302da84 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -1238,12 +1238,29 @@ static int hisi_sas_abort_task(struct sas_task *task) static int hisi_sas_abort_task_set(struct domain_device *device, u8 *lun) { + struct hisi_hba *hisi_hba = dev_to_hisi_hba(device); + struct device *dev = hisi_hba->dev; struct hisi_sas_tmf_task tmf_task; int rc = TMF_RESP_FUNC_FAILED; + unsigned long flags; + + rc = hisi_sas_internal_task_abort(hisi_hba, device, + HISI_SAS_INT_ABT_DEV, 0); + if (rc < 0) { + dev_err(dev, "abort task set: internal abort rc=%d\n", rc); + return TMF_RESP_FUNC_FAILED; + } + hisi_sas_dereg_device(hisi_hba, device); tmf_task.tmf = TMF_ABORT_TASK_SET; rc = hisi_sas_debug_issue_ssp_tmf(device, lun, _task); + if (rc == TMF_RESP_FUNC_COMPLETE) { + spin_lock_irqsave(_hba->lock, flags); + hisi_sas_release_task(hisi_hba, device); + spin_unlock_irqrestore(_hba->lock, flags); + } + return rc; } @@ -1333,6 +1350,14 @@ static int hisi_sas_lu_reset(struct domain_device *device, u8 *lun) } else { struct hisi_sas_tmf_task tmf_task = { .tmf = TMF_LU_RESET }; + rc = hisi_sas_internal_task_abort(hisi_hba, device, + HISI_SAS_INT_ABT_DEV, 0); + if (rc < 0) { + dev_err(dev, "lu_reset: internal abort failed\n"); + goto out; + } + hisi_sas_dereg_device(hisi_hba, device); + rc = hisi_sas_debug_issue_ssp_tmf(device, lun, _task); if (rc == TMF_RESP_FUNC_COMPLETE) { spin_lock_irqsave(_hba->lock, flags); -- 1.9.1
[PATCH 13/19] scsi: hisi_sas: use an general way to delay PHY work
From: Xiaofei TanUse an general way to do delay work for a PHY. Then it will be easier to add new delayed work for a PHY in future. Signed-off-by: Xiaofei Tan Signed-off-by: John Garry --- drivers/scsi/hisi_sas/hisi_sas.h | 9 - drivers/scsi/hisi_sas/hisi_sas_main.c | 22 -- drivers/scsi/hisi_sas/hisi_sas_v1_hw.c | 2 +- drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 4 ++-- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 2 +- 5 files changed, 32 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index 71bc8ea..aa14638 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -124,12 +124,17 @@ enum hisi_sas_bit_err_type { HISI_SAS_ERR_MULTI_BIT_ECC = 0x1, }; +enum hisi_sas_phy_event { + HISI_PHYE_PHY_UP = 0U, + HISI_PHYES_NUM, +}; + struct hisi_sas_phy { + struct work_struct works[HISI_PHYES_NUM]; struct hisi_hba *hisi_hba; struct hisi_sas_port*port; struct asd_sas_phy sas_phy; struct sas_identify identify; - struct work_struct phyup_ws; u64 port_id; /* from hw */ u64 dev_sas_addr; u64 frame_rcvd_size; @@ -453,4 +458,6 @@ extern void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba, extern void hisi_sas_rst_work_handler(struct work_struct *work); extern void hisi_sas_sync_rst_work_handler(struct work_struct *work); extern void hisi_sas_kill_tasklets(struct hisi_hba *hisi_hba); +extern bool hisi_sas_notify_phy_event(struct hisi_sas_phy *phy, + enum hisi_sas_phy_event event); #endif diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 1f6f063..326dc81 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -622,7 +622,7 @@ static int hisi_sas_scan_finished(struct Scsi_Host *shost, unsigned long time) static void hisi_sas_phyup_work(struct work_struct *work) { struct hisi_sas_phy *phy = - container_of(work, struct hisi_sas_phy, phyup_ws); + container_of(work, typeof(*phy), works[HISI_PHYE_PHY_UP]); struct hisi_hba *hisi_hba = phy->hisi_hba; struct asd_sas_phy *sas_phy = >sas_phy; int phy_no = sas_phy->id; @@ -631,10 +631,27 @@ static void hisi_sas_phyup_work(struct work_struct *work) hisi_sas_bytes_dmaed(hisi_hba, phy_no); } +static const work_func_t hisi_sas_phye_fns[HISI_PHYES_NUM] = { + [HISI_PHYE_PHY_UP] = hisi_sas_phyup_work, +}; + +bool hisi_sas_notify_phy_event(struct hisi_sas_phy *phy, + enum hisi_sas_phy_event event) +{ + struct hisi_hba *hisi_hba = phy->hisi_hba; + + if (WARN_ON(event >= HISI_PHYES_NUM)) + return false; + + return queue_work(hisi_hba->wq, >works[event]); +} +EXPORT_SYMBOL_GPL(hisi_sas_notify_phy_event); + static void hisi_sas_phy_init(struct hisi_hba *hisi_hba, int phy_no) { struct hisi_sas_phy *phy = _hba->phy[phy_no]; struct asd_sas_phy *sas_phy = >sas_phy; + int i; phy->hisi_hba = hisi_hba; phy->port = NULL; @@ -652,7 +669,8 @@ static void hisi_sas_phy_init(struct hisi_hba *hisi_hba, int phy_no) sas_phy->ha = (struct sas_ha_struct *)hisi_hba->shost->hostdata; sas_phy->lldd_phy = phy; - INIT_WORK(>phyup_ws, hisi_sas_phyup_work); + for (i = 0; i < HISI_PHYES_NUM; i++) + INIT_WORK(>works[i], hisi_sas_phye_fns[i]); } static void hisi_sas_port_notify_formed(struct asd_sas_phy *sas_phy) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c index 8cb9061..679e76f 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c @@ -1482,7 +1482,7 @@ static irqreturn_t int_phyup_v1_hw(int irq_no, void *p) else if (phy->identify.device_type != SAS_PHY_UNUSED) phy->identify.target_port_protocols = SAS_PROTOCOL_SMP; - queue_work(hisi_hba->wq, >phyup_ws); + hisi_sas_notify_phy_event(phy, HISI_PHYE_PHY_UP); end: hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT2, diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index 7257311..e521c42 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -2708,7 +2708,7 @@ static int phy_up_v2_hw(int phy_no, struct hisi_hba *hisi_hba) if (!timer_pending(_hba->timer)) set_link_timer_quirk(hisi_hba); } - queue_work(hisi_hba->wq, >phyup_ws); + hisi_sas_notify_phy_event(phy, HISI_PHYE_PHY_UP); end: hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0, @@ -3262,7 +3262,7 @@ static
[PATCH 11/19] scsi: hisi_sas: improve int_chnl_int_v2_hw() consistency with v3 hw
From: Xiaofei TanChange code format of int_chnl_int_v2_hw() to be consistent with v3 hw to reduce an tag indent. Signed-off-by: Xiaofei Tan Signed-off-by: John Garry --- drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 58 -- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index 8d6886a..4c4a000 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -2848,40 +2848,38 @@ static irqreturn_t int_chnl_int_v2_hw(int irq_no, void *p) HGC_INVLD_DQE_INFO_FB_CH3_OFF) & 0x1ff; while (irq_msk) { - if (irq_msk & (1 << phy_no)) { - u32 irq_value0 = hisi_sas_phy_read32(hisi_hba, phy_no, -CHL_INT0); - u32 irq_value1 = hisi_sas_phy_read32(hisi_hba, phy_no, -CHL_INT1); - u32 irq_value2 = hisi_sas_phy_read32(hisi_hba, phy_no, -CHL_INT2); - - if (irq_value1) { - if (irq_value1 & (CHL_INT1_DMAC_RX_ECC_ERR_MSK | - CHL_INT1_DMAC_TX_ECC_ERR_MSK)) - panic("%s: DMAC RX/TX ecc bad error!\ - (0x%x)", - dev_name(dev), irq_value1); - - hisi_sas_phy_write32(hisi_hba, phy_no, -CHL_INT1, irq_value1); - } + u32 irq_value0 = hisi_sas_phy_read32(hisi_hba, phy_no, +CHL_INT0); + u32 irq_value1 = hisi_sas_phy_read32(hisi_hba, phy_no, +CHL_INT1); + u32 irq_value2 = hisi_sas_phy_read32(hisi_hba, phy_no, +CHL_INT2); + + if ((irq_msk & (1 << phy_no)) && irq_value1) { + if (irq_value1 & (CHL_INT1_DMAC_RX_ECC_ERR_MSK | + CHL_INT1_DMAC_TX_ECC_ERR_MSK)) + panic("%s: DMAC RX/TX ecc bad error!\ + (0x%x)", + dev_name(dev), irq_value1); - if (irq_value2) - hisi_sas_phy_write32(hisi_hba, phy_no, -CHL_INT2, irq_value2); + hisi_sas_phy_write32(hisi_hba, phy_no, +CHL_INT1, irq_value1); + } + if ((irq_msk & (1 << phy_no)) && irq_value2) + hisi_sas_phy_write32(hisi_hba, phy_no, +CHL_INT2, irq_value2); - if (irq_value0) { - if (irq_value0 & CHL_INT0_SL_RX_BCST_ACK_MSK) - phy_bcast_v2_hw(phy_no, hisi_hba); - hisi_sas_phy_write32(hisi_hba, phy_no, - CHL_INT0, irq_value0 - & (~CHL_INT0_HOTPLUG_TOUT_MSK) - & (~CHL_INT0_SL_PHY_ENABLE_MSK) - & (~CHL_INT0_NOT_RDY_MSK)); - } + if ((irq_msk & (1 << phy_no)) && irq_value0) { + if (irq_value0 & CHL_INT0_SL_RX_BCST_ACK_MSK) + phy_bcast_v2_hw(phy_no, hisi_hba); + + hisi_sas_phy_write32(hisi_hba, phy_no, + CHL_INT0, irq_value0 + & (~CHL_INT0_HOTPLUG_TOUT_MSK) + & (~CHL_INT0_SL_PHY_ENABLE_MSK) + & (~CHL_INT0_NOT_RDY_MSK)); } irq_msk &= ~(1 << phy_no); phy_no++; -- 1.9.1
[PATCH 19/19] scsi: hisi_sas: add v3 hw suspend and resume
From: Xiang ChenFor v3 hw SAS, it supports configuring power state from D0 to D3 for entering Low Power status and power state from D3 to D0 for quit Low Power status. When power state from D0 to D3, HW will send FLR to clear the registers of ECAM and BAR space, and when power state from D3 to D0, it will clear the registers of ECAM space only. So when suspend, need to do like controller reset (including disable interrupts/DQ/PHY/BUS), and also release slots after FLR. When resume, re-config the registers of BAR space. Signed-off-by: Xiang Chen Signed-off-by: John Garry --- drivers/scsi/hisi_sas/hisi_sas.h | 1 + drivers/scsi/hisi_sas/hisi_sas_main.c | 3 +- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 94 ++ 3 files changed, 97 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index 4343c4c..cc05029 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -461,4 +461,5 @@ extern void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba, extern void hisi_sas_kill_tasklets(struct hisi_hba *hisi_hba); extern bool hisi_sas_notify_phy_event(struct hisi_sas_phy *phy, enum hisi_sas_phy_event event); +extern void hisi_sas_release_tasks(struct hisi_hba *hisi_hba); #endif diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index ad12237..04e1172b 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -737,7 +737,7 @@ static void hisi_sas_release_task(struct hisi_hba *hisi_hba, hisi_sas_do_release_task(hisi_hba, slot->task, slot); } -static void hisi_sas_release_tasks(struct hisi_hba *hisi_hba) +void hisi_sas_release_tasks(struct hisi_hba *hisi_hba) { struct hisi_sas_device *sas_dev; struct domain_device *device; @@ -754,6 +754,7 @@ static void hisi_sas_release_tasks(struct hisi_hba *hisi_hba) hisi_sas_release_task(hisi_hba, device); } } +EXPORT_SYMBOL_GPL(hisi_sas_release_tasks); static void hisi_sas_dereg_device(struct hisi_hba *hisi_hba, struct domain_device *device) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 9e32105..6a408d2 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -2303,6 +2303,98 @@ enum { hip08, }; +static int hisi_sas_v3_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct sas_ha_struct *sha = pci_get_drvdata(pdev); + struct hisi_hba *hisi_hba = sha->lldd_ha; + struct device *dev = hisi_hba->dev; + struct Scsi_Host *shost = hisi_hba->shost; + u32 device_state, status; + int rc; + u32 reg_val; + unsigned long flags; + + if (!pdev->pm_cap) { + dev_err(dev, "PCI PM not supported\n"); + return -ENODEV; + } + + set_bit(HISI_SAS_RESET_BIT, _hba->flags); + scsi_block_requests(shost); + set_bit(HISI_SAS_REJECT_CMD_BIT, _hba->flags); + flush_workqueue(hisi_hba->wq); + /* disable DQ/PHY/bus */ + interrupt_disable_v3_hw(hisi_hba); + hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE, 0x0); + hisi_sas_kill_tasklets(hisi_hba); + + hisi_sas_stop_phys(hisi_hba); + + reg_val = hisi_sas_read32(hisi_hba, AXI_MASTER_CFG_BASE + + AM_CTRL_GLOBAL); + reg_val |= 0x1; + hisi_sas_write32(hisi_hba, AXI_MASTER_CFG_BASE + + AM_CTRL_GLOBAL, reg_val); + + /* wait until bus idle */ + rc = readl_poll_timeout(hisi_hba->regs + AXI_MASTER_CFG_BASE + + AM_CURR_TRANS_RETURN, status, status == 0x3, 10, 100); + if (rc) { + dev_err(dev, "axi bus is not idle, rc = %d\n", rc); + clear_bit(HISI_SAS_REJECT_CMD_BIT, _hba->flags); + clear_bit(HISI_SAS_RESET_BIT, _hba->flags); + scsi_unblock_requests(shost); + return rc; + } + + hisi_sas_init_mem(hisi_hba); + + device_state = pci_choose_state(pdev, state); + dev_warn(dev, "entering operating state [D%d]\n", + device_state); + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, device_state); + + spin_lock_irqsave(_hba->lock, flags); + hisi_sas_release_tasks(hisi_hba); + spin_unlock_irqrestore(_hba->lock, flags); + + sas_suspend_ha(sha); + return 0; +} + +static int hisi_sas_v3_resume(struct pci_dev *pdev) +{ + struct sas_ha_struct *sha = pci_get_drvdata(pdev); + struct hisi_hba *hisi_hba = sha->lldd_ha; + struct Scsi_Host *shost = hisi_hba->shost; + struct device *dev = hisi_hba->dev; + unsigned int rc; + u32
[PATCH 15/19] scsi: hisi_sas: judge result of internal abort
From: Xiaofei TanNormally, hardware should ensure that internal abort timeout will never happen. If happen, it would be an SoC failure. What's more, HW will not process any other commands if an internal abort hasn't return CQ, and they will time out also. So, we should judge the result of internal abort in SCSI EH, if it is failed, we should give up to do TMF/softreset and return failure to the upper layer directly. This patch do following things to achieve this. 1. When internal abort timeout happened, we set return value to -EIO in hisi_sas_internal_task_abort(). 2. If prep_abort() is not support, let hisi_sas_internal_task_abort() return TMF_RESP_FUNC_FAILED. 3. If hisi_sas_internal_task_abort() return an negative number, it can be thought that it not executed properly or internal abort timeout. Then we won't do behind TMF or softreset, and return failure directly. Signed-off-by: Xiaofei Tan Signed-off-by: John Garry --- drivers/scsi/hisi_sas/hisi_sas_main.c | 38 --- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 7446a39..1b9c48c 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -1184,6 +1184,11 @@ static int hisi_sas_abort_task(struct sas_task *task) rc2 = hisi_sas_internal_task_abort(hisi_hba, device, HISI_SAS_INT_ABT_CMD, tag); + if (rc2 < 0) { + dev_err(dev, "abort task: internal abort (%d)\n", rc2); + return TMF_RESP_FUNC_FAILED; + } + /* * If the TMF finds that the IO is not in the device and also * the internal abort does not succeed, then it is safe to @@ -1201,8 +1206,12 @@ static int hisi_sas_abort_task(struct sas_task *task) } else if (task->task_proto & SAS_PROTOCOL_SATA || task->task_proto & SAS_PROTOCOL_STP) { if (task->dev->dev_type == SAS_SATA_DEV) { - hisi_sas_internal_task_abort(hisi_hba, device, -HISI_SAS_INT_ABT_DEV, 0); + rc = hisi_sas_internal_task_abort(hisi_hba, device, + HISI_SAS_INT_ABT_DEV, 0); + if (rc < 0) { + dev_err(dev, "abort task: internal abort failed\n"); + goto out; + } hisi_sas_dereg_device(hisi_hba, device); rc = hisi_sas_softreset_ata_disk(device); } @@ -1213,7 +1222,8 @@ static int hisi_sas_abort_task(struct sas_task *task) rc = hisi_sas_internal_task_abort(hisi_hba, device, HISI_SAS_INT_ABT_CMD, tag); - if (rc == TMF_RESP_FUNC_FAILED && task->lldd_task) { + if (((rc < 0) || (rc == TMF_RESP_FUNC_FAILED)) && + task->lldd_task) { spin_lock_irqsave(_hba->lock, flags); hisi_sas_do_release_task(hisi_hba, task, slot); spin_unlock_irqrestore(_hba->lock, flags); @@ -1263,15 +1273,20 @@ static int hisi_sas_I_T_nexus_reset(struct domain_device *device) { struct hisi_sas_device *sas_dev = device->lldd_dev; struct hisi_hba *hisi_hba = dev_to_hisi_hba(device); - unsigned long flags; + struct device *dev = hisi_hba->dev; int rc = TMF_RESP_FUNC_FAILED; + unsigned long flags; if (sas_dev->dev_status != HISI_SAS_DEV_EH) return TMF_RESP_FUNC_FAILED; sas_dev->dev_status = HISI_SAS_DEV_NORMAL; - hisi_sas_internal_task_abort(hisi_hba, device, + rc = hisi_sas_internal_task_abort(hisi_hba, device, HISI_SAS_INT_ABT_DEV, 0); + if (rc < 0) { + dev_err(dev, "I_T nexus reset: internal abort (%d)\n", rc); + return TMF_RESP_FUNC_FAILED; + } hisi_sas_dereg_device(hisi_hba, device); rc = hisi_sas_debug_I_T_nexus_reset(device); @@ -1299,8 +1314,10 @@ static int hisi_sas_lu_reset(struct domain_device *device, u8 *lun) /* Clear internal IO and then hardreset */ rc = hisi_sas_internal_task_abort(hisi_hba, device, HISI_SAS_INT_ABT_DEV, 0); - if (rc == TMF_RESP_FUNC_FAILED) + if (rc < 0) { + dev_err(dev, "lu_reset: internal abort failed\n"); goto out; + } hisi_sas_dereg_device(hisi_hba, device); phy = sas_get_local_phy(device); @@ -1497,8
[PATCH 12/19] scsi: hisi_sas: add v2 hw port AXI error handling support
From: Xiaofei TanAdd port AXI errors handling for v2 hw. We do host controller reset for such errors. Besides, change port muli-bits ECC error handling, and we should also do host reset for such error. So, this patch put them in the same struct with port AXI error. Signed-off-by: Xiaofei Tan Signed-off-by: John Garry --- drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 51 ++ 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index 4c4a000..7257311 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -240,6 +240,10 @@ #define CHL_INT1_DMAC_TX_ECC_ERR_MSK (0x1 << CHL_INT1_DMAC_TX_ECC_ERR_OFF) #define CHL_INT1_DMAC_RX_ECC_ERR_OFF 17 #define CHL_INT1_DMAC_RX_ECC_ERR_MSK (0x1 << CHL_INT1_DMAC_RX_ECC_ERR_OFF) +#define CHL_INT1_DMAC_TX_AXI_WR_ERR_OFF19 +#define CHL_INT1_DMAC_TX_AXI_RD_ERR_OFF20 +#define CHL_INT1_DMAC_RX_AXI_WR_ERR_OFF21 +#define CHL_INT1_DMAC_RX_AXI_RD_ERR_OFF22 #define CHL_INT2 (PORT_BASE + 0x1bc) #define CHL_INT0_MSK (PORT_BASE + 0x1c0) #define CHL_INT1_MSK (PORT_BASE + 0x1c4) @@ -1182,7 +1186,7 @@ static void init_reg_v2_hw(struct hisi_hba *hisi_hba) hisi_sas_phy_write32(hisi_hba, i, CHL_INT1, 0x); hisi_sas_phy_write32(hisi_hba, i, CHL_INT2, 0xfff87fff); hisi_sas_phy_write32(hisi_hba, i, RXOP_CHECK_CFG_H, 0x1000); - hisi_sas_phy_write32(hisi_hba, i, CHL_INT1_MSK, 0x); + hisi_sas_phy_write32(hisi_hba, i, CHL_INT1_MSK, 0xff857fff); hisi_sas_phy_write32(hisi_hba, i, CHL_INT2_MSK, 0x8bff); hisi_sas_phy_write32(hisi_hba, i, SL_CFG, 0x13f801fc); hisi_sas_phy_write32(hisi_hba, i, PHY_CTRL_RDY_MSK, 0x0); @@ -2832,6 +2836,33 @@ static void phy_bcast_v2_hw(int phy_no, struct hisi_hba *hisi_hba) hisi_sas_phy_write32(hisi_hba, phy_no, SL_RX_BCAST_CHK_MSK, 0); } +static const struct hisi_sas_hw_error port_ecc_axi_error[] = { + { + .irq_msk = BIT(CHL_INT1_DMAC_TX_ECC_ERR_OFF), + .msg = "dmac_tx_ecc_bad_err", + }, + { + .irq_msk = BIT(CHL_INT1_DMAC_RX_ECC_ERR_OFF), + .msg = "dmac_rx_ecc_bad_err", + }, + { + .irq_msk = BIT(CHL_INT1_DMAC_TX_AXI_WR_ERR_OFF), + .msg = "dma_tx_axi_wr_err", + }, + { + .irq_msk = BIT(CHL_INT1_DMAC_TX_AXI_RD_ERR_OFF), + .msg = "dma_tx_axi_rd_err", + }, + { + .irq_msk = BIT(CHL_INT1_DMAC_RX_AXI_WR_ERR_OFF), + .msg = "dma_rx_axi_wr_err", + }, + { + .irq_msk = BIT(CHL_INT1_DMAC_RX_AXI_RD_ERR_OFF), + .msg = "dma_rx_axi_rd_err", + }, +}; + static irqreturn_t int_chnl_int_v2_hw(int irq_no, void *p) { struct hisi_hba *hisi_hba = p; @@ -2856,11 +2887,19 @@ static irqreturn_t int_chnl_int_v2_hw(int irq_no, void *p) CHL_INT2); if ((irq_msk & (1 << phy_no)) && irq_value1) { - if (irq_value1 & (CHL_INT1_DMAC_RX_ECC_ERR_MSK | - CHL_INT1_DMAC_TX_ECC_ERR_MSK)) - panic("%s: DMAC RX/TX ecc bad error!\ - (0x%x)", - dev_name(dev), irq_value1); + int i; + + for (i = 0; i < ARRAY_SIZE(port_ecc_axi_error); i++) { + const struct hisi_sas_hw_error *error = + _ecc_axi_error[i]; + + if (!(irq_value1 & error->irq_msk)) + continue; + + dev_warn(dev, "%s error (phy%d 0x%x) found!\n", + error->msg, phy_no, irq_value1); + queue_work(hisi_hba->wq, _hba->rst_work); + } hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT1, irq_value1); -- 1.9.1
[PATCH 02/19] scsi: hisi_sas: fix dma_unmap_sg() parameter
From: Xiang ChenFor function dma_unmap_sg(), the parameter should be number of elements in the scatterlist prior to the mapping, not after the mapping. Fix this usage. Signed-off-by: Xiang Chen Signed-off-by: John Garry --- drivers/scsi/hisi_sas/hisi_sas_main.c | 6 -- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 359ec52..d842530 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -192,7 +192,8 @@ void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba, struct sas_task *task, if (!sas_protocol_ata(task->task_proto)) if (slot->n_elem) - dma_unmap_sg(dev, task->scatter, slot->n_elem, + dma_unmap_sg(dev, task->scatter, +task->num_scatter, task->data_dir); if (sas_dev) @@ -431,7 +432,8 @@ static int hisi_sas_task_prep(struct sas_task *task, struct hisi_sas_dq dev_err(dev, "task prep: failed[%d]!\n", rc); if (!sas_protocol_ata(task->task_proto)) if (n_elem) - dma_unmap_sg(dev, task->scatter, n_elem, + dma_unmap_sg(dev, task->scatter, +task->num_scatter, task->data_dir); prep_out: return rc; -- 1.9.1
Re: [PATCH] scsi: bfa: convert to strlcpy/strlcat
Looks good, Reviewed-by: Johannes Thumshirn-- Johannes Thumshirn Storage jthumsh...@suse.de+49 911 74053 689 SUSE LINUX GmbH, Maxfeldstr. 5, 90409 Nürnberg GF: Felix Imendörffer, Jane Smithard, Graham Norton HRB 21284 (AG Nürnberg) Key fingerprint = EC38 9CAB C2C4 F25D 8600 D0D0 0393 969D 2D76 0850
Re: [PATCH] scsi: libiscsi: Allow sd_shutdown on bad transport
Lee, Chris, Some test results. - Single unmounted disk, with transport connection wiped before final logout: http://pastebin.ubuntu.com/26139576/ - Multiple mounted disks, multipath dev-mapper, all transport connections were wiped before the final logout, with heavy write workload: http://pastebin.ubuntu.com/26139620/ Considering sd_shutdown logic - sd_shutdown, sd_sync_cache for each scsi_disk, 3 attempts of scsi_execute with SYNCHRONIZE_CACHE cmd each - you can see that, because transport was down, first SYNC_CACHE cmd waits for the request timeout and for the abort_timeout. All other cmds fail in the enqueuing phase, because of the transport failure + previous timeout + server shutdown happening simultaneously, so you don't have to wait for timeout on each command again. This change also suits any pending requests, not only those coming from sd_shutdown, and it allows OS to reboot and shutdown, back again, independently of how bad userland was configured. Thank you in advance for considering it. -Rafael > On 07/12/2017, at 07:59 PM, Rafael David Tinoco> wrote: > > If, for any reason, userland shuts down iscsi transport interfaces > before proper logouts - like when logging in to LUNs manually, > without logging out on server shutdown, or when automated scripts > can't umount/logout from logged LUNs - kernel will hang forever on > its sd_sync_cache() logic, after issuing the SYNCHRONIZE_CACHE cmd > to all still existent paths. > > PID: 1 TASK: 8801a69b8000 CPU: 1 COMMAND: "systemd-shutdow" > #0 [8801a69c3a30] __schedule at 8183e9ee > #1 [8801a69c3a80] schedule at 8183f0d5 > #2 [8801a69c3a98] schedule_timeout at 81842199 > #3 [8801a69c3b40] io_schedule_timeout at 8183e604 > #4 [8801a69c3b70] wait_for_completion_io_timeout at 8183fc6c > #5 [8801a69c3bd0] blk_execute_rq at 813cfe10 > #6 [8801a69c3c88] scsi_execute at 815c3fc7 > #7 [8801a69c3cc8] scsi_execute_req_flags at 815c60fe > #8 [8801a69c3d30] sd_sync_cache at 815d37d7 > #9 [8801a69c3da8] sd_shutdown at 815d3c3c > > This happens because iscsi_eh_cmd_timed_out(), the transport layer > timeout helper, would tell the queue timeout function (scsi_times_out) > to reset the request timer over and over, until the session state is > back to logged in state. Unfortunately, during server shutdown, this > might never happen again. > > Other option would be "not to handle" the issue in the transport > layer. That would trigger the error handler logic, which would also > need the session state to be logged in again. > > Best option, for such case, is to tell upper layers that the command > was handled during the transport layer error handler helper, marking > it as DID_NO_CONNECT, which will allow completion and inform about > the problem. > > After the session was marked as ISCSI_STATE_FAILED, due to the first > timeout during the server shutdown phase, all subsequent cmds will > fail to be queued, allowing upper logic to fail faster. > > Signed-off-by: Rafael David Tinoco
Re: [PATCH v5 3/7] scsi: libsas: make the event threshold configurable
On 08/12/2017 09:42, Jason Yan wrote: Add a sysfs attr that LLDD can configure it for every host. We made a example in hisi_sas. Other LLDDs using libsas can implement it if they want. Suggested-by: Hannes ReineckeSigned-off-by: Jason Yan CC: John Garry CC: Johannes Thumshirn CC: Ewan Milne CC: Christoph Hellwig CC: Tomas Henzl CC: Dan Williams Acked-by: John Garry #for hisi_sas part --- drivers/scsi/hisi_sas/hisi_sas_main.c | 6 ++
Re: [PATCH v2 1/3] scsi: Fix a scsi_show_rq() NULL pointer dereference
Hi Martin, On Fri, Dec 08, 2017 at 04:44:55PM +0800, Ming Lei wrote: > Hi Martin, > > On Thu, Dec 07, 2017 at 09:46:21PM -0500, Martin K. Petersen wrote: > > > > Ming, > > > > > As I explained in [1], the use-after-free is inevitable no matter if > > > clearing 'SCpnt->cmnd' before mempool_free() in sd_uninit_command() or > > > not, so we need to comment the fact that cdb may point to garbage > > > data, and this function(especially __scsi_format_command() has to > > > survive that, so that people won't be surprised when kasan complains > > > use-after-free, and guys will be careful when they try to change the > > > code in future. > > > > Longer term we really need to get rid of the separate CDB allocation. It > > was a necessary evil when I did it. And not much of a concern since I > > did not expect anybody sane to use Type 2 (it's designed for use inside > > disk arrays). > > > > However, I keep hearing about people using Type 2 drives. Some vendors > > source drives formatted that way and use the same SKU for arrays and > > standalone servers. > > > > So we should really look into making it possible for a queue to have a > > bigger than 16-byte built-in CDB. For Type 2 devices, 32-byte reads and > > writes are a prerequisite. So it would be nice to be able to switch a > > queue to a larger allocation post creation (we won't know the type until > > after READ CAPACITY(16) has been sent). > > I am wondering why you don't make __cmd[] in scsi_request defined as 32bytes? > Even for some hosts with thousands of tag, the memory waste is still not > too much. > > Or if you prefer to do post creation, we have sbitmap_queue now, which can > help to build a pre-allocated memory pool easily, and its allocation/free is > pretty efficient. Or something like the following patch? I run several IO tests over scsi_debug(dif=2, dix=1), and looks it works without any problem. >From 7731af623af164c6be451d9c543ce6b70e7e66b8 Mon Sep 17 00:00:00 2001 From: Ming LeiDate: Fri, 8 Dec 2017 17:35:18 +0800 Subject: [PATCH] SCSI: pre-allocate command buffer for T10_PI_TYPE2_PROTECTION This patch allocates one array for T10_PI_TYPE2_PROTECTION command, size of each element is SD_EXT_CDB_SIZE, and the length is host->can_queue, then we can retrieve one command buffer runtime via rq->tag. So we can avoid to allocate the command buffer runtime, also the recent use-after-free report[1] in scsi_show_rq() can be fixed too. [1] https://marc.info/?l=linux-block=151030452715642=2 Signed-off-by: Ming Lei --- drivers/scsi/hosts.c | 1 + drivers/scsi/sd.c| 55 drivers/scsi/sd.h| 4 ++-- drivers/scsi/sd_dif.c| 32 ++-- include/scsi/scsi_host.h | 2 ++ 5 files changed, 49 insertions(+), 45 deletions(-) diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index fe3a0da3ec97..74f55b8f16fe 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -350,6 +350,7 @@ static void scsi_host_dev_release(struct device *dev) if (parent) put_device(parent); + kfree(shost->cmd_ext_buf); kfree(shost); } diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 24fe68522716..853eb57ad4ad 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -131,9 +131,6 @@ static DEFINE_IDA(sd_index_ida); * object after last put) */ static DEFINE_MUTEX(sd_ref_mutex); -static struct kmem_cache *sd_cdb_cache; -static mempool_t *sd_cdb_pool; - static const char *sd_cache_types[] = { "write through", "none", "write back", "write back, no read (daft)" @@ -1026,6 +1023,13 @@ static int sd_setup_flush_cmnd(struct scsi_cmnd *cmd) return BLKPREP_OK; } +static char *sd_get_ext_buf(struct scsi_device *sdp, struct scsi_cmnd *SCpnt) +{ + struct request *rq = SCpnt->request; + + return >host->cmd_ext_buf[rq->tag * SD_EXT_CDB_SIZE]; +} + static int sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) { struct request *rq = SCpnt->request; @@ -1168,12 +1172,7 @@ static int sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) protect = 0; if (protect && sdkp->protection_type == T10_PI_TYPE2_PROTECTION) { - SCpnt->cmnd = mempool_alloc(sd_cdb_pool, GFP_ATOMIC); - - if (unlikely(SCpnt->cmnd == NULL)) { - ret = BLKPREP_DEFER; - goto out; - } + SCpnt->cmnd = sd_get_ext_buf(sdp, SCpnt); SCpnt->cmd_len = SD_EXT_CDB_SIZE; memset(SCpnt->cmnd, 0, SCpnt->cmd_len); @@ -1318,12 +1317,6 @@ static void sd_uninit_command(struct scsi_cmnd *SCpnt) if (rq->rq_flags & RQF_SPECIAL_PAYLOAD) __free_page(rq->special_vec.bv_page); - - if (SCpnt->cmnd != scsi_req(rq)->cmd) { - mempool_free(SCpnt->cmnd, sd_cdb_pool); - SCpnt->cmnd = NULL;
[PATCH] scsi_dh_alua: skip RTPG for devices only supporting active/optimized
From: Hannes ReineckeFor hardware only supporting active/optimized there's no point in ever re-issuing RTPG as the only new state we can possibly read is active/optimized. This avoid spurious errors during path failover on such arrays. Signed-off-by: Hannes Reinecke --- drivers/scsi/device_handler/scsi_dh_alua.c | 35 ++ 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index fd22dc6..b09c12b 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -40,6 +40,7 @@ #define TPGS_SUPPORT_LBA_DEPENDENT 0x10 #define TPGS_SUPPORT_OFFLINE 0x40 #define TPGS_SUPPORT_TRANSITION0x80 +#define TPGS_SUPPORT_ALL 0xdf #define RTPG_FMT_MASK 0x70 #define RTPG_FMT_EXT_HDR 0x10 @@ -81,6 +82,7 @@ struct alua_port_group { int tpgs; int state; int pref; + int valid_states; unsignedflags; /* used for optimizing STPG */ unsigned char transition_tmo; unsigned long expiry; @@ -243,6 +245,7 @@ static struct alua_port_group *alua_alloc_pg(struct scsi_device *sdev, pg->group_id = group_id; pg->tpgs = tpgs; pg->state = SCSI_ACCESS_STATE_OPTIMAL; + pg->valid_states = TPGS_SUPPORT_ALL; if (optimize_stpg) pg->flags |= ALUA_OPTIMIZE_STPG; kref_init(>kref); @@ -516,7 +519,7 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg) { struct scsi_sense_hdr sense_hdr; struct alua_port_group *tmp_pg; - int len, k, off, valid_states = 0, bufflen = ALUA_RTPG_SIZE; + int len, k, off, bufflen = ALUA_RTPG_SIZE; unsigned char *desc, *buff; unsigned err, retval; unsigned int tpg_desc_tbl_off; @@ -541,6 +544,20 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg) retval = submit_rtpg(sdev, buff, bufflen, _hdr, pg->flags); if (retval) { + /* +* If the target only supports active/optimized there's +* not much we can do; it's not that we can switch paths +* or somesuch. +* So ignore any errors to avoid spurious failures during +* path failover. +*/ + if ((pg->valid_states & ~TPGS_SUPPORT_OPTIMIZED) == 0) { + sdev_printk(KERN_INFO, sdev, + "%s: ignoring rtpg result %d\n", + ALUA_DH_NAME, retval); + kfree(buff); + return SCSI_DH_OK; + } if (!scsi_sense_valid(_hdr)) { sdev_printk(KERN_INFO, sdev, "%s: rtpg failed, result %d\n", @@ -652,7 +669,7 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg) rcu_read_unlock(); } if (tmp_pg == pg) - valid_states = desc[1]; + tmp_pg->valid_states = desc[1]; spin_unlock_irqrestore(_pg->lock, flags); } kref_put(_pg->kref, release_port_group); @@ -665,13 +682,13 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg) "%s: port group %02x state %c %s supports %c%c%c%c%c%c%c\n", ALUA_DH_NAME, pg->group_id, print_alua_state(pg->state), pg->pref ? "preferred" : "non-preferred", - valid_states_SUPPORT_TRANSITION?'T':'t', - valid_states_SUPPORT_OFFLINE?'O':'o', - valid_states_SUPPORT_LBA_DEPENDENT?'L':'l', - valid_states_SUPPORT_UNAVAILABLE?'U':'u', - valid_states_SUPPORT_STANDBY?'S':'s', - valid_states_SUPPORT_NONOPTIMIZED?'N':'n', - valid_states_SUPPORT_OPTIMIZED?'A':'a'); + pg->valid_states_SUPPORT_TRANSITION?'T':'t', + pg->valid_states_SUPPORT_OFFLINE?'O':'o', + pg->valid_states_SUPPORT_LBA_DEPENDENT?'L':'l', + pg->valid_states_SUPPORT_UNAVAILABLE?'U':'u', + pg->valid_states_SUPPORT_STANDBY?'S':'s', + pg->valid_states_SUPPORT_NONOPTIMIZED?'N':'n', + pg->valid_states_SUPPORT_OPTIMIZED?'A':'a'); switch (pg->state) { case SCSI_ACCESS_STATE_TRANSITIONING: -- 1.8.5.6
[PATCH v5 5/7] scsi: libsas: use flush_workqueue to process disco events synchronously
Now we are processing sas event and discover event in different workqueues. It's safe to wait the discover event done in the sas event work. Use flush_workqueue() to insure the disco and revalidate events processed synchronously so that the whole discover and revalidate process will not be interrupted by other events. Signed-off-by: Jason YanCC: John Garry CC: Johannes Thumshirn CC: Ewan Milne CC: Christoph Hellwig CC: Tomas Henzl CC: Dan Williams --- drivers/scsi/libsas/sas_port.c | 4 1 file changed, 4 insertions(+) diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c index 9326628..64722f4 100644 --- a/drivers/scsi/libsas/sas_port.c +++ b/drivers/scsi/libsas/sas_port.c @@ -192,6 +192,7 @@ static void sas_form_port(struct asd_sas_phy *phy) si->dft->lldd_port_formed(phy); sas_discover_event(phy->port, DISCE_DISCOVER_DOMAIN); + flush_workqueue(sas_ha->disco_q); } /** @@ -277,6 +278,9 @@ void sas_porte_broadcast_rcvd(struct work_struct *work) SAS_DPRINTK("broadcast received: %d\n", prim); sas_discover_event(phy->port, DISCE_REVALIDATE_DOMAIN); + + if (phy->port) + flush_workqueue(phy->port->ha->disco_q); } void sas_porte_link_reset_err(struct work_struct *work) -- 2.9.5
[PATCH v5 7/7] scsi: libsas: notify event PORTE_BROADCAST_RCVD in sas_enable_revalidation()
There are two places queuing the disco event DISCE_REVALIDATE_DOMAIN. One is in sas_porte_broadcast_rcvd() and uses sas_chain_event() to queue the event. The other is in sas_enable_revalidation() and uses sas_queue_event() to queue the event. We have diffrent work queues for event and discovery now, so the DISCE_REVALIDATE_DOMAIN event may be processed in both event queue and discovery queue. Now since we do synchronous event handling, we cannot do it in discovery queue, so have to trigger a fake broadcast event to re-trigger the revalidation from event queue. Signed-off-by: Jason YanCC: John Garry CC: Johannes Thumshirn CC: Ewan Milne CC: Christoph Hellwig CC: Tomas Henzl CC: Dan Williams --- drivers/scsi/libsas/sas_event.c | 8 +++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/libsas/sas_event.c b/drivers/scsi/libsas/sas_event.c index 8c82c00..ae923eb 100644 --- a/drivers/scsi/libsas/sas_event.c +++ b/drivers/scsi/libsas/sas_event.c @@ -116,11 +116,17 @@ void sas_enable_revalidation(struct sas_ha_struct *ha) struct asd_sas_port *port = ha->sas_port[i]; const int ev = DISCE_REVALIDATE_DOMAIN; struct sas_discovery *d = >disc; + struct asd_sas_phy *sas_phy; if (!test_and_clear_bit(ev, >pending)) continue; - sas_queue_event(ev, >disc_work[ev].work, ha); + if (list_empty(>phy_list)) + continue; + + sas_phy = container_of(port->phy_list.next, struct asd_sas_phy, + port_phy_el); + ha->notify_port_event(sas_phy, PORTE_BROADCAST_RCVD); } mutex_unlock(>disco_mutex); } -- 2.9.5
[PATCH v5 6/7] scsi: libsas: direct call probe and destruct
In commit 87c8331fcf72 ("[SCSI] libsas: prevent domain rediscovery competing with ata error handling") introduced disco mutex to prevent rediscovery competing with ata error handling and put the whole revalidation in the mutex. But the rphy add/remove needs to wait for the error handling which also grabs the disco mutex. This may leads to dead lock.So the probe and destruct event were introduce to do the rphy add/remove asynchronously and out of the lock. The asynchronously processed workers makes the whole discovery process not atomic, the other events may interrupt the process. For example, if a loss of signal event inserted before the probe event, the sas_deform_port() is called and the port will be deleted. And sas_port_delete() may run before the destruct event, but the port-x:x is the top parent of end device or expander. This leads to a kernel WARNING such as: [ 82.042979] sysfs group 'power' not found for kobject 'phy-1:0:22' [ 82.042983] [ cut here ] [ 82.042986] WARNING: CPU: 54 PID: 1714 at fs/sysfs/group.c:237 sysfs_remove_group+0x94/0xa0 [ 82.043059] Call trace: [ 82.043082] [] sysfs_remove_group+0x94/0xa0 [ 82.043085] [] dpm_sysfs_remove+0x60/0x70 [ 82.043086] [] device_del+0x138/0x308 [ 82.043089] [] sas_phy_delete+0x38/0x60 [ 82.043091] [] do_sas_phy_delete+0x6c/0x80 [ 82.043093] [] device_for_each_child+0x58/0xa0 [ 82.043095] [] sas_remove_children+0x40/0x50 [ 82.043100] [] sas_destruct_devices+0x64/0xa0 [ 82.043102] [] process_one_work+0x1fc/0x4b0 [ 82.043104] [] worker_thread+0x50/0x490 [ 82.043105] [] kthread+0xfc/0x128 [ 82.043107] [] ret_from_fork+0x10/0x50 Make probe and destruct a direct call in the disco and revalidate function, but put them outside the lock. The whole discovery or revalidate won't be interrupted by other events. And the DISCE_PROBE and DISCE_DESTRUCT event are deleted as a result of the direct call. Introduce a new list to destruct the sas_port and put the port delete after the destruct. This makes sure the right order of destroying the sysfs kobject and fix the warning above. In sas_ex_revalidate_domain() have a loop to find all broadcasted device, and sometimes we have a chance to find the same expander twice. Because the sas_port will be deleted at the end of the whole revalidate process, sas_port with the same name cannot be added before this. Otherwise the sysfs will complain of creating duplicate filename. Since the LLDD will send broadcast for every device change, we can only process one expander's revalidation. Signed-off-by: Jason YanCC: John Garry CC: Johannes Thumshirn CC: Ewan Milne CC: Christoph Hellwig CC: Tomas Henzl CC: Dan Williams --- drivers/scsi/libsas/sas_ata.c | 1 - drivers/scsi/libsas/sas_discover.c | 32 ++-- drivers/scsi/libsas/sas_expander.c | 8 +++- drivers/scsi/libsas/sas_internal.h | 1 + drivers/scsi/libsas/sas_port.c | 3 +++ include/scsi/libsas.h | 3 +-- include/scsi/scsi_transport_sas.h | 1 + 7 files changed, 27 insertions(+), 22 deletions(-) diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 70be442..2b3637b 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -730,7 +730,6 @@ int sas_discover_sata(struct domain_device *dev) if (res) return res; - sas_discover_event(dev->port, DISCE_PROBE); return 0; } diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 14f714d..190108e 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -212,13 +212,9 @@ void sas_notify_lldd_dev_gone(struct domain_device *dev) } } -static void sas_probe_devices(struct work_struct *work) +static void sas_probe_devices(struct asd_sas_port *port) { struct domain_device *dev, *n; - struct sas_discovery_event *ev = to_sas_discovery_event(work); - struct asd_sas_port *port = ev->port; - - clear_bit(DISCE_PROBE, >disc.pending); /* devices must be domain members before link recovery and probe */ list_for_each_entry(dev, >disco_list, disco_list_node) { @@ -294,7 +290,6 @@ int sas_discover_end_dev(struct domain_device *dev) res = sas_notify_lldd_dev_found(dev); if (res) return res; - sas_discover_event(dev->port, DISCE_PROBE); return 0; } @@ -353,13 +348,9 @@ static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_d sas_put_device(dev); } -static void sas_destruct_devices(struct work_struct *work) +void sas_destruct_devices(struct asd_sas_port *port) { struct domain_device *dev, *n; - struct sas_discovery_event *ev = to_sas_discovery_event(work); - struct
[PATCH v5 4/7] scsi: libsas: Use new workqueue to run sas event and disco event
Now all libsas works are queued to scsi host workqueue, include sas event work post by LLDD and sas discovery work, and a sas hotplug flow may be divided into several works, e.g libsas receive a PORTE_BYTES_DMAED event, currently we process it as following steps: sas_form_port --- run in work in shost workq sas_discover_domain --- run in another work in shost workq ... sas_probe_devices --- run in new work in shost workq We found during hot-add a device, libsas may need run several works in same workqueue to add device in system, the process is not atomic, it may interrupt by other sas event works, like PHYE_LOSS_OF_SIGNAL. This patch is preparation of execute libsas sas event in sync. We need to use different workqueue to run sas event and disco event. Otherwise the work will be blocked for waiting another chained work in the same workqueue. Signed-off-by: Yijing WangCC: John Garry CC: Johannes Thumshirn CC: Ewan Milne CC: Christoph Hellwig CC: Tomas Henzl CC: Dan Williams Signed-off-by: Jason Yan --- drivers/scsi/libsas/sas_discover.c | 2 +- drivers/scsi/libsas/sas_event.c| 6 +++--- drivers/scsi/libsas/sas_init.c | 18 ++ include/scsi/libsas.h | 3 +++ 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 60de662..14f714d 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -534,7 +534,7 @@ static void sas_chain_work(struct sas_ha_struct *ha, struct sas_work *sw) * workqueue, or known to be submitted from a context that is * not racing against draining */ - scsi_queue_work(ha->core.shost, >work); + queue_work(ha->disco_q, >work); } static void sas_chain_event(int event, unsigned long *pending, diff --git a/drivers/scsi/libsas/sas_event.c b/drivers/scsi/libsas/sas_event.c index 5d7254a..8c82c00 100644 --- a/drivers/scsi/libsas/sas_event.c +++ b/drivers/scsi/libsas/sas_event.c @@ -40,7 +40,7 @@ int sas_queue_work(struct sas_ha_struct *ha, struct sas_work *sw) if (list_empty(>drain_node)) list_add_tail(>drain_node, >defer_q); } else - rc = scsi_queue_work(ha->core.shost, >work); + rc = queue_work(ha->event_q, >work); return rc; } @@ -61,7 +61,6 @@ static int sas_queue_event(int event, struct sas_work *work, void __sas_drain_work(struct sas_ha_struct *ha) { - struct workqueue_struct *wq = ha->core.shost->work_q; struct sas_work *sw, *_sw; int ret; @@ -70,7 +69,8 @@ void __sas_drain_work(struct sas_ha_struct *ha) spin_lock_irq(>lock); spin_unlock_irq(>lock); - drain_workqueue(wq); + drain_workqueue(ha->event_q); + drain_workqueue(ha->disco_q); spin_lock_irq(>lock); clear_bit(SAS_HA_DRAINING, >state); diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index afd928b..c81a63b 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -110,6 +110,7 @@ void sas_hash_addr(u8 *hashed, const u8 *sas_addr) int sas_register_ha(struct sas_ha_struct *sas_ha) { + char name[64]; int error = 0; mutex_init(_ha->disco_mutex); @@ -143,10 +144,24 @@ int sas_register_ha(struct sas_ha_struct *sas_ha) goto Undo_ports; } + error = -ENOMEM; + snprintf(name, sizeof(name), "%s_event_q", dev_name(sas_ha->dev)); + sas_ha->event_q = create_singlethread_workqueue(name); + if (!sas_ha->event_q) + goto Undo_ports; + + snprintf(name, sizeof(name), "%s_disco_q", dev_name(sas_ha->dev)); + sas_ha->disco_q = create_singlethread_workqueue(name); + if (!sas_ha->disco_q) + goto Undo_event_q; + INIT_LIST_HEAD(_ha->eh_done_q); INIT_LIST_HEAD(_ha->eh_ata_q); return 0; + +Undo_event_q: + destroy_workqueue(sas_ha->event_q); Undo_ports: sas_unregister_ports(sas_ha); Undo_phys: @@ -177,6 +192,9 @@ int sas_unregister_ha(struct sas_ha_struct *sas_ha) __sas_drain_work(sas_ha); mutex_unlock(_ha->drain_mutex); + destroy_workqueue(sas_ha->disco_q); + destroy_workqueue(sas_ha->event_q); + return 0; } diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 701c67f..fa93c41 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -389,6 +389,9 @@ struct sas_ha_struct { struct device *dev; /* should be set */ struct module *lldd_module; /* should be set */ + struct workqueue_struct *event_q; + struct workqueue_struct *disco_q; + u8 *sas_addr; /*
[PATCH v5 2/7] scsi: libsas: shut down the PHY if events reached the threshold
If the PHY burst too many events, we will alloc a lot of events for the worker. This may leads to memory exhaustion. Dan Williams suggested to shut down the PHY if the events reached the threshold, because in this case the PHY may have gone into some erroneous state. Users can re-enable the PHY by sysfs if they want. We cannot use the fixed memory pool because if we run out of events, the shut down event and loss of signal event will lost too. The events still need to be allocated and processed in this case. Suggested-by: Dan WilliamsSigned-off-by: Jason Yan CC: John Garry CC: Johannes Thumshirn CC: Ewan Milne CC: Christoph Hellwig CC: Tomas Henzl --- drivers/scsi/libsas/sas_init.c | 33 - drivers/scsi/libsas/sas_phy.c | 27 ++- include/scsi/libsas.h | 6 ++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index e04f6d6..22bfc02 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -123,6 +123,8 @@ int sas_register_ha(struct sas_ha_struct *sas_ha) INIT_LIST_HEAD(_ha->defer_q); INIT_LIST_HEAD(_ha->eh_dev_q); + sas_ha->event_thres = SAS_PHY_SHUTDOWN_THRES; + error = sas_register_phys(sas_ha); if (error) { printk(KERN_NOTICE "couldn't register sas phys:%d\n", error); @@ -557,14 +559,43 @@ EXPORT_SYMBOL_GPL(sas_domain_attach_transport); struct asd_sas_event *sas_alloc_event(struct asd_sas_phy *phy) { + struct asd_sas_event *event; gfp_t flags = in_interrupt() ? GFP_ATOMIC : GFP_KERNEL; + struct sas_ha_struct *sas_ha = phy->ha; + struct sas_internal *i = + to_sas_internal(sas_ha->core.shost->transportt); + + event = kmem_cache_zalloc(sas_event_cache, flags); + if (!event) + return NULL; - return kmem_cache_zalloc(sas_event_cache, flags); + atomic_inc(>event_nr); + + if (atomic_read(>event_nr) > phy->ha->event_thres) { + if (i->dft->lldd_control_phy) { + if (cmpxchg(>in_shutdown, 0, 1) == 0) { + sas_printk("The phy%02d bursting events, shut it down.\n", + phy->id); + sas_notify_phy_event(phy, PHYE_SHUTDOWN); + } + } else { + /* Do not support PHY control, stop allocating events */ + WARN_ONCE(1, "PHY control not supported.\n"); + kmem_cache_free(sas_event_cache, event); + atomic_dec(>event_nr); + event = NULL; + } + } + + return event; } void sas_free_event(struct asd_sas_event *event) { + struct asd_sas_phy *phy = event->phy; + kmem_cache_free(sas_event_cache, event); + atomic_dec(>event_nr); } /* -- SAS Class register/unregister -- */ diff --git a/drivers/scsi/libsas/sas_phy.c b/drivers/scsi/libsas/sas_phy.c index 59f8292..bf3e1b9 100644 --- a/drivers/scsi/libsas/sas_phy.c +++ b/drivers/scsi/libsas/sas_phy.c @@ -35,6 +35,7 @@ static void sas_phye_loss_of_signal(struct work_struct *work) struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; + phy->in_shutdown = 0; phy->error = 0; sas_deform_port(phy, 1); } @@ -44,6 +45,7 @@ static void sas_phye_oob_done(struct work_struct *work) struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; + phy->in_shutdown = 0; phy->error = 0; } @@ -105,6 +107,28 @@ static void sas_phye_resume_timeout(struct work_struct *work) } +static void sas_phye_shutdown(struct work_struct *work) +{ + struct asd_sas_event *ev = to_asd_sas_event(work); + struct asd_sas_phy *phy = ev->phy; + struct sas_ha_struct *sas_ha = phy->ha; + struct sas_internal *i = + to_sas_internal(sas_ha->core.shost->transportt); + + if (phy->enabled) { + int ret; + + phy->error = 0; + phy->enabled = 0; + ret = i->dft->lldd_control_phy(phy, PHY_FUNC_DISABLE, NULL); + if (ret) + sas_printk("lldd disable phy%02d returned %d\n", + phy->id, ret); + } else + sas_printk("phy%02d is not enabled, cannot shutdown\n", + phy->id); +} + /* -- Phy class registration -- */ int sas_register_phys(struct sas_ha_struct *sas_ha) @@ -116,6 +140,7 @@ int sas_register_phys(struct sas_ha_struct *sas_ha) struct asd_sas_phy *phy =
[PATCH v5 0/7] Enhance libsas hotplug feature
Now the libsas hotplug has some issues, Dan Williams report a similar bug here before https://www.mail-archive.com/linux-scsi@vger.kernel.org/msg39187.html The issues we have found 1. if LLDD burst reports lots of phy-up/phy-down sas events, some events may lost because a same sas events is pending now, finally libsas topo may different the hardware. 2. receive a phy down sas event, libsas call sas_deform_port to remove devices, it would first delete the sas port, then put a destruction discovery event in a new work, and queue it at the tail of workqueue, once the sas port be deleted, its children device will be deleted too, when the destruction work start, it will found the target device has been removed, and report a sysfs warnning. 3. since a hotplug process will be devided into several works, if a phy up sas event insert into phydown works, like destruction work ---> PORTE_BYTES_DMAED (sas_form_port) >PHYE_LOSS_OF_SIGNAL the hot remove flow would broken by PORTE_BYTES_DMAED event, it's not we expected, and issues would occur. v4->v5: -process only one expander's revalidation in sas_ex_revalidate_domain() -notify event PORTE_BROADCAST_RCVD in sas_enable_revalidation() v3->v4: -use dynamic alloced work and support shutting down the phy if active event reached the threshold -use flush_workqueue instead of wait-completion to process discover events synchronously -direct call probe and destruct function v2->v3: some code improvements suggested by Johannes and John, split v2 patch 2 into several small pathes. v1->v2: some code improvements suggested by John Garry Jason Yan (7): scsi: libsas: Use dynamic alloced work to avoid sas event lost scsi: libsas: shut down the PHY if events reached the threshold scsi: libsas: make the event threshold configurable scsi: libsas: Use new workqueue to run sas event and disco event scsi: libsas: use flush_workqueue to process disco events synchronously scsi: libsas: direct call probe and destruct scsi: libsas: notify event PORTE_BROADCAST_RCVD in sas_enable_revalidation() drivers/scsi/hisi_sas/hisi_sas_main.c | 6 ++ drivers/scsi/libsas/sas_ata.c | 1 - drivers/scsi/libsas/sas_discover.c| 34 ++- drivers/scsi/libsas/sas_event.c | 86 --- drivers/scsi/libsas/sas_expander.c| 8 +-- drivers/scsi/libsas/sas_init.c| 107 +- drivers/scsi/libsas/sas_internal.h| 7 +++ drivers/scsi/libsas/sas_phy.c | 69 +++--- drivers/scsi/libsas/sas_port.c| 25 include/scsi/libsas.h | 30 +++--- include/scsi/scsi_transport_sas.h | 1 + 11 files changed, 277 insertions(+), 97 deletions(-) -- 2.9.5
Re: [PATCH v2] scsi: libsas: fix length error in sas_smp_handler()
On 07/12/2017 10:57, Jason Yan wrote: The bsg_job_done() requires the length of payload received, but we give it the untransferred residual. As I understand, this patches fixes (SES) enclosure management for libsas, so it's quite an important patch. Thanks, John Fixes: 651a01364994 ("scsi: scsi_transport_sas: switch to bsg-lib for SMP passthrough") Reported-and-tested-by: chenqilinSigned-off-by: Jason Yan CC: Christoph Hellwig --- drivers/scsi/libsas/sas_expander.c | 10 +- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 50cb0f3..6c40ecc 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -2143,7 +2143,7 @@ void sas_smp_handler(struct bsg_job *job, struct Scsi_Host *shost, struct sas_rphy *rphy) { struct domain_device *dev; - unsigned int reslen = 0; + unsigned int rcvlen = 0; int ret = -EINVAL; /* no rphy means no smp target support (ie aic94xx host) */ @@ -2177,12 +2177,12 @@ void sas_smp_handler(struct bsg_job *job, struct Scsi_Host *shost, ret = smp_execute_task_sg(dev, job->request_payload.sg_list, job->reply_payload.sg_list); - if (ret > 0) { - /* positive number is the untransferred residual */ - reslen = ret; + if (ret >= 0) { + /* bsg_job_done() requires the length received */ + rcvlen = job->reply_payload.payload_len - ret; ret = 0; } out: - bsg_job_done(job, ret, reslen); + bsg_job_done(job, ret, rcvlen); }
[PATCH v5 1/7] scsi: libsas: Use dynamic alloced work to avoid sas event lost
Now libsas hotplug work is static, every sas event type has its own static work, LLDD driver queues the hotplug work into shost->work_q. If LLDD driver burst posts lots hotplug events to libsas, the hotplug events may pending in the workqueue like shost->work_q new work[PORTE_BYTES_DMAED] --> |[PHYE_LOSS_OF_SIGNAL][PORTE_BYTES_DMAED] -> processing |<---wait worker to process>| In this case, a new PORTE_BYTES_DMAED event coming, libsas try to queue it to shost->work_q, but this work is already pending, so it would be lost. Finally, libsas delete the related sas port and sas devices, but LLDD driver expect libsas add the sas port and devices(last sas event). This patch use dynamic allocated work to avoid this issue. Signed-off-by: Yijing WangCC: John Garry CC: Johannes Thumshirn CC: Ewan Milne CC: Christoph Hellwig CC: Tomas Henzl CC: Dan Williams Signed-off-by: Jason Yan --- drivers/scsi/libsas/sas_event.c| 74 +- drivers/scsi/libsas/sas_init.c | 27 -- drivers/scsi/libsas/sas_internal.h | 6 drivers/scsi/libsas/sas_phy.c | 44 +-- drivers/scsi/libsas/sas_port.c | 18 +- include/scsi/libsas.h | 17 + 6 files changed, 115 insertions(+), 71 deletions(-) diff --git a/drivers/scsi/libsas/sas_event.c b/drivers/scsi/libsas/sas_event.c index 0bb9eef..5d7254a 100644 --- a/drivers/scsi/libsas/sas_event.c +++ b/drivers/scsi/libsas/sas_event.c @@ -29,7 +29,8 @@ int sas_queue_work(struct sas_ha_struct *ha, struct sas_work *sw) { - int rc = 0; + /* it's added to the defer_q when draining so return succeed */ + int rc = 1; if (!test_bit(SAS_HA_REGISTERED, >state)) return 0; @@ -44,19 +45,15 @@ int sas_queue_work(struct sas_ha_struct *ha, struct sas_work *sw) return rc; } -static int sas_queue_event(int event, unsigned long *pending, - struct sas_work *work, +static int sas_queue_event(int event, struct sas_work *work, struct sas_ha_struct *ha) { - int rc = 0; + unsigned long flags; + int rc; - if (!test_and_set_bit(event, pending)) { - unsigned long flags; - - spin_lock_irqsave(>lock, flags); - rc = sas_queue_work(ha, work); - spin_unlock_irqrestore(>lock, flags); - } + spin_lock_irqsave(>lock, flags); + rc = sas_queue_work(ha, work); + spin_unlock_irqrestore(>lock, flags); return rc; } @@ -66,6 +63,7 @@ void __sas_drain_work(struct sas_ha_struct *ha) { struct workqueue_struct *wq = ha->core.shost->work_q; struct sas_work *sw, *_sw; + int ret; set_bit(SAS_HA_DRAINING, >state); /* flush submitters */ @@ -78,7 +76,10 @@ void __sas_drain_work(struct sas_ha_struct *ha) clear_bit(SAS_HA_DRAINING, >state); list_for_each_entry_safe(sw, _sw, >defer_q, drain_node) { list_del_init(>drain_node); - sas_queue_work(ha, sw); + ret = sas_queue_work(ha, sw); + if (ret != 1) + sas_free_event(to_asd_sas_event(>work)); + } spin_unlock_irq(>lock); } @@ -119,29 +120,68 @@ void sas_enable_revalidation(struct sas_ha_struct *ha) if (!test_and_clear_bit(ev, >pending)) continue; - sas_queue_event(ev, >pending, >disc_work[ev].work, ha); + sas_queue_event(ev, >disc_work[ev].work, ha); } mutex_unlock(>disco_mutex); } + +static void sas_port_event_worker(struct work_struct *work) +{ + struct asd_sas_event *ev = to_asd_sas_event(work); + + sas_port_event_fns[ev->event](work); + sas_free_event(ev); +} + +static void sas_phy_event_worker(struct work_struct *work) +{ + struct asd_sas_event *ev = to_asd_sas_event(work); + + sas_phy_event_fns[ev->event](work); + sas_free_event(ev); +} + static int sas_notify_port_event(struct asd_sas_phy *phy, enum port_event event) { + struct asd_sas_event *ev; struct sas_ha_struct *ha = phy->ha; + int ret; BUG_ON(event >= PORT_NUM_EVENTS); - return sas_queue_event(event, >port_events_pending, - >port_events[event].work, ha); + ev = sas_alloc_event(phy); + if (!ev) + return -ENOMEM; + + INIT_SAS_EVENT(ev, sas_port_event_worker, phy, event); + + ret = sas_queue_event(event, >work, ha); + if (ret != 1) + sas_free_event(ev); + + return ret; } int sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event) { + struct asd_sas_event *ev;
[PATCH v5 3/7] scsi: libsas: make the event threshold configurable
Add a sysfs attr that LLDD can configure it for every host. We made a example in hisi_sas. Other LLDDs using libsas can implement it if they want. Suggested-by: Hannes ReineckeSigned-off-by: Jason Yan CC: John Garry CC: Johannes Thumshirn CC: Ewan Milne CC: Christoph Hellwig CC: Tomas Henzl CC: Dan Williams --- drivers/scsi/hisi_sas/hisi_sas_main.c | 6 ++ drivers/scsi/libsas/sas_init.c| 31 +++ include/scsi/libsas.h | 1 + 3 files changed, 38 insertions(+) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 5f503cb..b5ce64a 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -1561,6 +1561,11 @@ EXPORT_SYMBOL_GPL(hisi_sas_kill_tasklets); struct scsi_transport_template *hisi_sas_stt; EXPORT_SYMBOL_GPL(hisi_sas_stt); +struct device_attribute *host_attrs[] = { + _attr_phy_event_threshold, + NULL, +}; + static struct scsi_host_template _hisi_sas_sht = { .module = THIS_MODULE, .name = DRV_NAME, @@ -1580,6 +1585,7 @@ static struct scsi_host_template _hisi_sas_sht = { .eh_target_reset_handler = sas_eh_target_reset_handler, .target_destroy = sas_target_destroy, .ioctl = sas_ioctl, + .shost_attrs= host_attrs, }; struct scsi_host_template *hisi_sas_sht = &_hisi_sas_sht; EXPORT_SYMBOL_GPL(hisi_sas_sht); diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index 22bfc02..afd928b 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -538,6 +538,37 @@ static struct sas_function_template sft = { .smp_handler = sas_smp_handler, }; +static inline ssize_t phy_event_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + + return scnprintf(buf, PAGE_SIZE, "%u\n", sha->event_thres); +} + +static inline ssize_t phy_event_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + + sha->event_thres = simple_strtol(buf, NULL, 10); + + /* threshold cannot be set too small */ + if (sha->event_thres < 32) + sha->event_thres = 32; + + return count; +} + +DEVICE_ATTR(phy_event_threshold, + S_IRUGO|S_IWUSR, + phy_event_threshold_show, + phy_event_threshold_store); +EXPORT_SYMBOL_GPL(dev_attr_phy_event_threshold); + struct scsi_transport_template * sas_domain_attach_transport(struct sas_domain_function_template *dft) { diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 26683e2..701c67f 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -681,6 +681,7 @@ extern int sas_bios_param(struct scsi_device *, sector_t capacity, int *hsc); extern struct scsi_transport_template * sas_domain_attach_transport(struct sas_domain_function_template *); +extern struct device_attribute dev_attr_phy_event_threshold; int sas_discover_root_expander(struct domain_device *); -- 2.9.5
Re: [PATCH v2 1/3] scsi: Fix a scsi_show_rq() NULL pointer dereference
Hi Martin, On Thu, Dec 07, 2017 at 09:46:21PM -0500, Martin K. Petersen wrote: > > Ming, > > > As I explained in [1], the use-after-free is inevitable no matter if > > clearing 'SCpnt->cmnd' before mempool_free() in sd_uninit_command() or > > not, so we need to comment the fact that cdb may point to garbage > > data, and this function(especially __scsi_format_command() has to > > survive that, so that people won't be surprised when kasan complains > > use-after-free, and guys will be careful when they try to change the > > code in future. > > Longer term we really need to get rid of the separate CDB allocation. It > was a necessary evil when I did it. And not much of a concern since I > did not expect anybody sane to use Type 2 (it's designed for use inside > disk arrays). > > However, I keep hearing about people using Type 2 drives. Some vendors > source drives formatted that way and use the same SKU for arrays and > standalone servers. > > So we should really look into making it possible for a queue to have a > bigger than 16-byte built-in CDB. For Type 2 devices, 32-byte reads and > writes are a prerequisite. So it would be nice to be able to switch a > queue to a larger allocation post creation (we won't know the type until > after READ CAPACITY(16) has been sent). I am wondering why you don't make __cmd[] in scsi_request defined as 32bytes? Even for some hosts with thousands of tag, the memory waste is still not too much. Or if you prefer to do post creation, we have sbitmap_queue now, which can help to build a pre-allocated memory pool easily, and its allocation/free is pretty efficient. Thanks, Ming