diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index edfe7a267a4b67..d505acd0ebf714 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -5074,6 +5074,7 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev, WRITE_ONCE(ctrl->state, NVME_CTRL_NEW); ctrl->passthru_err_log_enabled = false; clear_bit(NVME_CTRL_FAILFAST_EXPIRED, &ctrl->flags); + clear_bit(NVME_CTRL_MARGINAL, &ctrl->flags); spin_lock_init(&ctrl->lock); mutex_init(&ctrl->namespaces_lock); diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c index 03987f497a5b55..87bfe34b4d52a0 100644 --- a/drivers/nvme/host/fc.c +++ b/drivers/nvme/host/fc.c @@ -786,6 +786,10 @@ nvme_fc_ctrl_connectivity_loss(struct nvme_fc_ctrl *ctrl) "Reconnect", ctrl->cnum); set_bit(ASSOC_FAILED, &ctrl->flags); + + /* clear 'marginal' flag as controller will be reset */ + clear_bit(NVME_CTRL_MARGINAL, &ctrl->flags); + nvme_reset_ctrl(&ctrl->ctrl); } @@ -3726,6 +3730,82 @@ static struct nvmf_transport_ops nvme_fc_transport = { .create_ctrl = nvme_fc_create_ctrl, }; +static struct nvme_fc_rport *nvme_fc_rport_from_wwpn(struct nvme_fc_lport *lport, + u64 rport_wwpn) +{ + struct nvme_fc_rport *rport; + unsigned long flags; + + spin_lock_irqsave(&nvme_fc_lock, flags); + list_for_each_entry(rport, &lport->endp_list, endp_list) { + if (!nvme_fc_rport_get(rport)) + continue; + if (rport->remoteport.port_name == rport_wwpn && + rport->remoteport.port_role & FC_PORT_ROLE_NVME_TARGET) { + spin_unlock_irqrestore(&nvme_fc_lock, flags); + return rport; + } + nvme_fc_rport_put(rport); + } + spin_unlock_irqrestore(&nvme_fc_lock, flags); + return NULL; +} + +static struct nvme_fc_lport * +nvme_fc_lport_from_wwpn(u64 wwpn) +{ + struct nvme_fc_lport *lport; + unsigned long flags; + + spin_lock_irqsave(&nvme_fc_lock, flags); + list_for_each_entry(lport, &nvme_fc_lport_list, port_list) { + if (lport->localport.port_name == wwpn && + lport->localport.port_state == FC_OBJSTATE_ONLINE) { + if (nvme_fc_lport_get(lport)) { + spin_unlock_irqrestore(&nvme_fc_lock, flags); + return lport; + } + } + } + spin_unlock_irqrestore(&nvme_fc_lock, flags); + return NULL; +} + +static void +nvme_fc_fpin_set_state(struct nvme_fc_lport *lport, u64 wwpn, bool marginal) +{ + struct nvme_fc_rport *rport; + struct nvme_fc_ctrl *ctrl; + + rport = nvme_fc_rport_from_wwpn(lport, wwpn); + if (!rport) + return; + + spin_lock_irq(&rport->lock); + list_for_each_entry(ctrl, &rport->ctrl_list, ctrl_list) { + if (marginal) + set_bit(NVME_CTRL_MARGINAL, &ctrl->ctrl.flags); + else + clear_bit(NVME_CTRL_MARGINAL, &ctrl->ctrl.flags); + } + spin_unlock_irq(&rport->lock); + nvme_fc_rport_put(rport); +} + +void +nvme_fc_modify_rport_fpin_state(u64 local_wwpn, u64 remote_wwpn, bool marginal) +{ + struct nvme_fc_lport *lport; + + lport = nvme_fc_lport_from_wwpn(local_wwpn); + if (!lport) + return; + + nvme_fc_fpin_set_state(lport, remote_wwpn, marginal); + nvme_fc_lport_put(lport); +} +EXPORT_SYMBOL_GPL(nvme_fc_modify_rport_fpin_state); + /* Arbitrary successive failures max. With lots of subsystems could be high */ #define DISCOVERY_MAX_FAIL 20 diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c index 3da980dc60d911..38e40dd88e5207 100644 --- a/drivers/nvme/host/multipath.c +++ b/drivers/nvme/host/multipath.c @@ -324,11 +324,14 @@ static struct nvme_ns *__nvme_find_path(struct nvme_ns_head *head, int node) switch (ns->ana_state) { case NVME_ANA_OPTIMIZED: - if (distance < found_distance) { - found_distance = distance; - found = ns; + if (!nvme_ctrl_is_marginal(ns->ctrl)) { + if (distance < found_distance) { + found_distance = distance; + found = ns; + } + break; } - break; + fallthrough; case NVME_ANA_NONOPTIMIZED: if (distance < fallback_distance) { fallback_distance = distance; @@ -381,7 +384,8 @@ static struct nvme_ns *nvme_round_robin_path(struct nvme_ns_head *head) if (ns->ana_state == NVME_ANA_OPTIMIZED) { found = ns; - goto out; + if (!nvme_ctrl_is_marginal(ns->ctrl)) + goto out; } if (ns->ana_state == NVME_ANA_NONOPTIMIZED) found = ns; @@ -416,6 +420,9 @@ static struct nvme_ns *nvme_queue_depth_path(struct nvme_ns_head *head) if (nvme_path_is_disabled(ns)) continue; + if (nvme_ctrl_is_marginal(ns->ctrl)) + continue; + depth = atomic_read(&ns->ctrl->nr_active); switch (ns->ana_state) { @@ -439,13 +446,16 @@ static struct nvme_ns *nvme_queue_depth_path(struct nvme_ns_head *head) return best_opt; } - return best_opt ? best_opt : best_nonopt; + best_opt = (best_opt) ? best_opt : best_nonopt; + + return best_opt ? best_opt : __nvme_find_path(head, numa_node_id()); } static inline bool nvme_path_is_optimized(struct nvme_ns *ns) { return nvme_ctrl_state(ns->ctrl) == NVME_CTRL_LIVE && - ns->ana_state == NVME_ANA_OPTIMIZED; + ns->ana_state == NVME_ANA_OPTIMIZED && + !nvme_ctrl_is_marginal(ns->ctrl); } static struct nvme_ns *nvme_numa_path(struct nvme_ns_head *head) diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index cfd2b5b90b9157..d71e6668f11c58 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -275,6 +275,7 @@ enum nvme_ctrl_flags { NVME_CTRL_SKIP_ID_CNS_CS = 4, NVME_CTRL_DIRTY_CAPABILITY = 5, NVME_CTRL_FROZEN = 6, + NVME_CTRL_MARGINAL = 7, }; struct nvme_ctrl { @@ -417,6 +418,11 @@ static inline enum nvme_ctrl_state nvme_ctrl_state(struct nvme_ctrl *ctrl) return READ_ONCE(ctrl->state); } +static inline bool nvme_ctrl_is_marginal(struct nvme_ctrl *ctrl) +{ + return test_bit(NVME_CTRL_MARGINAL, &ctrl->flags); +} + enum nvme_iopolicy { NVME_IOPOLICY_NUMA, NVME_IOPOLICY_RR, diff --git a/drivers/nvme/host/sysfs.c b/drivers/nvme/host/sysfs.c index 29430949ce2f0a..4a6135c2f9cb58 100644 --- a/drivers/nvme/host/sysfs.c +++ b/drivers/nvme/host/sysfs.c @@ -430,7 +430,9 @@ static ssize_t nvme_sysfs_show_state(struct device *dev, }; if (state < ARRAY_SIZE(state_name) && state_name[state]) - return sysfs_emit(buf, "%s\n", state_name[state]); + return sysfs_emit(buf, "%s\n", + (nvme_ctrl_is_marginal(ctrl)) ? "marginal" : + state_name[state]); return sysfs_emit(buf, "unknown state\n"); } diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index fca81e0c7c2e1a..bfd9ba4552f324 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -3937,7 +3937,7 @@ lpfc_cmpl_els_edc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, { IOCB_t *irsp_iocb; struct fc_els_edc_resp *edc_rsp; - struct fc_tlv_desc *tlv; + union fc_tlv_desc *tlv; struct fc_diag_cg_sig_desc *pcgd; struct fc_diag_lnkflt_desc *plnkflt; struct lpfc_dmabuf *pcmd, *prsp; @@ -4028,7 +4028,7 @@ lpfc_cmpl_els_edc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, goto out; } - dtag = be32_to_cpu(tlv->desc_tag); + dtag = be32_to_cpu(tlv->hdr.desc_tag); switch (dtag) { case ELS_DTAG_LNK_FAULT_CAP: if (bytes_remain < FC_TLV_DESC_SZ_FROM_LENGTH(tlv) || @@ -4043,7 +4043,7 @@ lpfc_cmpl_els_edc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, sizeof(struct fc_diag_lnkflt_desc)); goto out; } - plnkflt = (struct fc_diag_lnkflt_desc *)tlv; + plnkflt = &tlv->lnkflt; lpfc_printf_log(phba, KERN_INFO, LOG_ELS | LOG_LDS_EVENT, "4617 Link Fault Desc Data: 0x%08x 0x%08x " @@ -4070,7 +4070,7 @@ lpfc_cmpl_els_edc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, goto out; } - pcgd = (struct fc_diag_cg_sig_desc *)tlv; + pcgd = &tlv->cg_sig; lpfc_printf_log( phba, KERN_INFO, LOG_ELS | LOG_CGN_MGMT, "4616 CGN Desc Data: 0x%08x 0x%08x " @@ -4125,10 +4125,8 @@ lpfc_cmpl_els_edc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, } static void -lpfc_format_edc_lft_desc(struct lpfc_hba *phba, struct fc_tlv_desc *tlv) +lpfc_format_edc_lft_desc(struct lpfc_hba *phba, struct fc_diag_lnkflt_desc *lft) { - struct fc_diag_lnkflt_desc *lft = (struct fc_diag_lnkflt_desc *)tlv; - lft->desc_tag = cpu_to_be32(ELS_DTAG_LNK_FAULT_CAP); lft->desc_len = cpu_to_be32( FC_TLV_DESC_LENGTH_FROM_SZ(struct fc_diag_lnkflt_desc)); @@ -4141,10 +4139,8 @@ lpfc_format_edc_lft_desc(struct lpfc_hba *phba, struct fc_tlv_desc *tlv) } static void -lpfc_format_edc_cgn_desc(struct lpfc_hba *phba, struct fc_tlv_desc *tlv) +lpfc_format_edc_cgn_desc(struct lpfc_hba *phba, struct fc_diag_cg_sig_desc *cgd) { - struct fc_diag_cg_sig_desc *cgd = (struct fc_diag_cg_sig_desc *)tlv; - /* We are assuming cgd was zero'ed before calling this routine */ /* Configure the congestion detection capability */ @@ -4233,7 +4229,7 @@ lpfc_issue_els_edc(struct lpfc_vport *vport, uint8_t retry) struct lpfc_hba *phba = vport->phba; struct lpfc_iocbq *elsiocb; struct fc_els_edc *edc_req; - struct fc_tlv_desc *tlv; + union fc_tlv_desc *tlv; u16 cmdsize; struct lpfc_nodelist *ndlp; u8 *pcmd = NULL; @@ -4272,13 +4268,13 @@ lpfc_issue_els_edc(struct lpfc_vport *vport, uint8_t retry) tlv = edc_req->desc; if (cgn_desc_size) { - lpfc_format_edc_cgn_desc(phba, tlv); + lpfc_format_edc_cgn_desc(phba, &tlv->cg_sig); phba->cgn_sig_freq = lpfc_fabric_cgn_frequency; tlv = fc_tlv_next_desc(tlv); } if (lft_desc_size) - lpfc_format_edc_lft_desc(phba, tlv); + lpfc_format_edc_lft_desc(phba, &tlv->lnkflt); lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS | LOG_CGN_MGMT, "4623 Xmit EDC to remote " @@ -5824,7 +5820,7 @@ lpfc_issue_els_edc_rsp(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, { struct lpfc_hba *phba = vport->phba; struct fc_els_edc_resp *edc_rsp; - struct fc_tlv_desc *tlv; + union fc_tlv_desc *tlv; struct lpfc_iocbq *elsiocb; IOCB_t *icmd, *cmd; union lpfc_wqe128 *wqe; @@ -5868,10 +5864,10 @@ lpfc_issue_els_edc_rsp(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, FC_TLV_DESC_LENGTH_FROM_SZ(struct fc_els_lsri_desc)); edc_rsp->lsri.rqst_w0.cmd = ELS_EDC; tlv = edc_rsp->desc; - lpfc_format_edc_cgn_desc(phba, tlv); + lpfc_format_edc_cgn_desc(phba, &tlv->cg_sig); tlv = fc_tlv_next_desc(tlv); if (lft_desc_size) - lpfc_format_edc_lft_desc(phba, tlv); + lpfc_format_edc_lft_desc(phba, &tlv->lnkflt); lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_RSP, "Issue EDC ACC: did:x%x flg:x%lx refcnt %d", @@ -9263,7 +9259,7 @@ lpfc_els_rcv_edc(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, { struct lpfc_hba *phba = vport->phba; struct fc_els_edc *edc_req; - struct fc_tlv_desc *tlv; + union fc_tlv_desc *tlv; uint8_t *payload; uint32_t *ptr, dtag; const char *dtag_nm; @@ -9306,7 +9302,7 @@ lpfc_els_rcv_edc(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, goto out; } - dtag = be32_to_cpu(tlv->desc_tag); + dtag = be32_to_cpu(tlv->hdr.desc_tag); switch (dtag) { case ELS_DTAG_LNK_FAULT_CAP: if (bytes_remain < FC_TLV_DESC_SZ_FROM_LENGTH(tlv) || @@ -9321,7 +9317,7 @@ lpfc_els_rcv_edc(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, sizeof(struct fc_diag_lnkflt_desc)); goto out; } - plnkflt = (struct fc_diag_lnkflt_desc *)tlv; + plnkflt = &tlv->lnkflt; lpfc_printf_log(phba, KERN_INFO, LOG_ELS | LOG_LDS_EVENT, "4626 Link Fault Desc Data: x%08x len x%x " @@ -9358,7 +9354,7 @@ lpfc_els_rcv_edc(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, phba->cgn_sig_freq = lpfc_fabric_cgn_frequency; lpfc_least_capable_settings( - phba, (struct fc_diag_cg_sig_desc *)tlv); + phba, &tlv->cg_sig); break; default: dtag_nm = lpfc_get_tlv_dtag_nm(dtag); @@ -9949,14 +9945,13 @@ lpfc_display_fpin_wwpn(struct lpfc_hba *phba, __be64 *wwnlist, u32 cnt) /** * lpfc_els_rcv_fpin_li - Process an FPIN Link Integrity Event. * @phba: Pointer to phba object. - * @tlv: Pointer to the Link Integrity Notification Descriptor. + * @li: Pointer to the Link Integrity Notification Descriptor. * * This function processes a Link Integrity FPIN event by logging a message. **/ static void -lpfc_els_rcv_fpin_li(struct lpfc_hba *phba, struct fc_tlv_desc *tlv) +lpfc_els_rcv_fpin_li(struct lpfc_hba *phba, struct fc_fn_li_desc *li) { - struct fc_fn_li_desc *li = (struct fc_fn_li_desc *)tlv; const char *li_evt_str; u32 li_evt, cnt; @@ -9980,14 +9975,13 @@ lpfc_els_rcv_fpin_li(struct lpfc_hba *phba, struct fc_tlv_desc *tlv) /** * lpfc_els_rcv_fpin_del - Process an FPIN Delivery Event. * @phba: Pointer to hba object. - * @tlv: Pointer to the Delivery Notification Descriptor TLV + * @del: Pointer to the Delivery Notification Descriptor TLV * * This function processes a Delivery FPIN event by logging a message. **/ static void -lpfc_els_rcv_fpin_del(struct lpfc_hba *phba, struct fc_tlv_desc *tlv) +lpfc_els_rcv_fpin_del(struct lpfc_hba *phba, struct fc_fn_deli_desc *del) { - struct fc_fn_deli_desc *del = (struct fc_fn_deli_desc *)tlv; const char *del_rsn_str; u32 del_rsn; __be32 *frame; @@ -10018,14 +10012,14 @@ lpfc_els_rcv_fpin_del(struct lpfc_hba *phba, struct fc_tlv_desc *tlv) /** * lpfc_els_rcv_fpin_peer_cgn - Process a FPIN Peer Congestion Event. * @phba: Pointer to hba object. - * @tlv: Pointer to the Peer Congestion Notification Descriptor TLV + * @pc: Pointer to the Peer Congestion Notification Descriptor TLV * * This function processes a Peer Congestion FPIN event by logging a message. **/ static void -lpfc_els_rcv_fpin_peer_cgn(struct lpfc_hba *phba, struct fc_tlv_desc *tlv) +lpfc_els_rcv_fpin_peer_cgn(struct lpfc_hba *phba, + struct fc_fn_peer_congn_desc *pc) { - struct fc_fn_peer_congn_desc *pc = (struct fc_fn_peer_congn_desc *)tlv; const char *pc_evt_str; u32 pc_evt, cnt; @@ -10053,7 +10047,7 @@ lpfc_els_rcv_fpin_peer_cgn(struct lpfc_hba *phba, struct fc_tlv_desc *tlv) /** * lpfc_els_rcv_fpin_cgn - Process an FPIN Congestion notification * @phba: Pointer to hba object. - * @tlv: Pointer to the Congestion Notification Descriptor TLV + * @cgn: Pointer to the Congestion Notification Descriptor TLV * * This function processes an FPIN Congestion Notifiction. The notification * could be an Alarm or Warning. This routine feeds that data into driver's @@ -10062,10 +10056,9 @@ lpfc_els_rcv_fpin_peer_cgn(struct lpfc_hba *phba, struct fc_tlv_desc *tlv) * to the upper layer or 0 to indicate don't deliver it. **/ static int -lpfc_els_rcv_fpin_cgn(struct lpfc_hba *phba, struct fc_tlv_desc *tlv) +lpfc_els_rcv_fpin_cgn(struct lpfc_hba *phba, struct fc_fn_congn_desc *cgn) { struct lpfc_cgn_info *cp; - struct fc_fn_congn_desc *cgn = (struct fc_fn_congn_desc *)tlv; const char *cgn_evt_str; u32 cgn_evt; const char *cgn_sev_str; @@ -10168,7 +10161,7 @@ lpfc_els_rcv_fpin(struct lpfc_vport *vport, void *p, u32 fpin_length) { struct lpfc_hba *phba = vport->phba; struct fc_els_fpin *fpin = (struct fc_els_fpin *)p; - struct fc_tlv_desc *tlv, *first_tlv, *current_tlv; + union fc_tlv_desc *tlv, *first_tlv, *current_tlv; const char *dtag_nm; int desc_cnt = 0, bytes_remain, cnt; u32 dtag, deliver = 0; @@ -10193,7 +10186,7 @@ lpfc_els_rcv_fpin(struct lpfc_vport *vport, void *p, u32 fpin_length) return; } - tlv = (struct fc_tlv_desc *)&fpin->fpin_desc[0]; + tlv = &fpin->fpin_desc[0]; first_tlv = tlv; bytes_remain = fpin_length - offsetof(struct fc_els_fpin, fpin_desc); bytes_remain = min_t(u32, bytes_remain, be32_to_cpu(fpin->desc_len)); @@ -10201,22 +10194,22 @@ lpfc_els_rcv_fpin(struct lpfc_vport *vport, void *p, u32 fpin_length) /* process each descriptor separately */ while (bytes_remain >= FC_TLV_DESC_HDR_SZ && bytes_remain >= FC_TLV_DESC_SZ_FROM_LENGTH(tlv)) { - dtag = be32_to_cpu(tlv->desc_tag); + dtag = be32_to_cpu(tlv->hdr.desc_tag); switch (dtag) { case ELS_DTAG_LNK_INTEGRITY: - lpfc_els_rcv_fpin_li(phba, tlv); + lpfc_els_rcv_fpin_li(phba, &tlv->li); deliver = 1; break; case ELS_DTAG_DELIVERY: - lpfc_els_rcv_fpin_del(phba, tlv); + lpfc_els_rcv_fpin_del(phba, &tlv->deli); deliver = 1; break; case ELS_DTAG_PEER_CONGEST: - lpfc_els_rcv_fpin_peer_cgn(phba, tlv); + lpfc_els_rcv_fpin_peer_cgn(phba, &tlv->peer_congn); deliver = 1; break; case ELS_DTAG_CONGESTION: - deliver = lpfc_els_rcv_fpin_cgn(phba, tlv); + deliver = lpfc_els_rcv_fpin_cgn(phba, &tlv->congn); break; default: dtag_nm = lpfc_get_tlv_dtag_nm(dtag); @@ -10229,12 +10222,12 @@ lpfc_els_rcv_fpin(struct lpfc_vport *vport, void *p, u32 fpin_length) return; } lpfc_cgn_update_stat(phba, dtag); - cnt = be32_to_cpu(tlv->desc_len); + cnt = be32_to_cpu(tlv->hdr.desc_len); /* Sanity check descriptor length. The desc_len value does not * include space for the desc_tag and the desc_len fields. */ - len -= (cnt + sizeof(struct fc_tlv_desc)); + len -= (cnt + sizeof(struct fc_tlv_desc_hdr)); if (len < 0) { dtag_nm = lpfc_get_tlv_dtag_nm(dtag); lpfc_printf_log(phba, KERN_WARNING, LOG_CGN_MGMT, @@ -10263,9 +10256,14 @@ lpfc_els_rcv_fpin(struct lpfc_vport *vport, void *p, u32 fpin_length) fpin_length += sizeof(struct fc_els_fpin); /* the entire FPIN */ /* Send every descriptor individually to the upper layer */ - if (deliver) + if (deliver) { fc_host_fpin_rcv(lpfc_shost_from_vport(vport), fpin_length, (char *)fpin, 0); + if (vport->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { + fc_host_fpin_set_nvme_rport_marginal(lpfc_shost_from_vport(vport), + fpin_length, (char *)fpin); + } + } desc_cnt++; } } diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index cb95b7b12051da..604e66bead1e01 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -4890,9 +4890,7 @@ struct purex_item { struct purex_item *pkt); atomic_t in_use; uint16_t size; - struct { - uint8_t iocb[64]; - } iocb; + uint8_t iocb[] __counted_by(size); }; #include "qla_edif.h" @@ -5101,7 +5099,6 @@ typedef struct scsi_qla_host { struct list_head head; spinlock_t lock; } purex_list; - struct purex_item default_item; struct name_list_extended gnl; /* Count of active session/fcport */ @@ -5130,6 +5127,11 @@ typedef struct scsi_qla_host { #define DPORT_DIAG_IN_PROGRESS BIT_0 #define DPORT_DIAG_CHIP_RESET_IN_PROGRESS BIT_1 uint16_t dport_status; + + /* Must be last --ends in a flexible-array member. */ + TRAILING_OVERLAP(struct purex_item, default_item, iocb, + uint8_t __default_item_iocb[QLA_DEFAULT_PAYLOAD_SIZE]; + ); } scsi_qla_host_t; struct qla27xx_image_status { diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index c4c6b5c6658c07..ccb044693dcb23 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -46,6 +46,7 @@ qla27xx_process_purex_fpin(struct scsi_qla_host *vha, struct purex_item *item) pkt, pkt_size); fc_host_fpin_rcv(vha->host, pkt_size, (char *)pkt, 0); + fc_host_fpin_set_nvme_rport_marginal(vha->host, pkt_size, (char *)pkt); } const char *const port_state_str[] = { @@ -1077,17 +1078,17 @@ static struct purex_item * qla24xx_alloc_purex_item(scsi_qla_host_t *vha, uint16_t size) { struct purex_item *item = NULL; - uint8_t item_hdr_size = sizeof(*item); if (size > QLA_DEFAULT_PAYLOAD_SIZE) { - item = kzalloc(item_hdr_size + - (size - QLA_DEFAULT_PAYLOAD_SIZE), GFP_ATOMIC); + item = kzalloc(struct_size(item, iocb, size), GFP_ATOMIC); } else { if (atomic_inc_return(&vha->default_item.in_use) == 1) { item = &vha->default_item; goto initialize_purex_header; } else { - item = kzalloc(item_hdr_size, GFP_ATOMIC); + item = kzalloc( + struct_size(item, iocb, QLA_DEFAULT_PAYLOAD_SIZE), + GFP_ATOMIC); } } if (!item) { @@ -1127,17 +1128,16 @@ qla24xx_queue_purex_item(scsi_qla_host_t *vha, struct purex_item *pkt, * @vha: SCSI driver HA context * @pkt: ELS packet */ -static struct purex_item -*qla24xx_copy_std_pkt(struct scsi_qla_host *vha, void *pkt) +static struct purex_item * +qla24xx_copy_std_pkt(struct scsi_qla_host *vha, void *pkt) { struct purex_item *item; - item = qla24xx_alloc_purex_item(vha, - QLA_DEFAULT_PAYLOAD_SIZE); + item = qla24xx_alloc_purex_item(vha, QLA_DEFAULT_PAYLOAD_SIZE); if (!item) return item; - memcpy(&item->iocb, pkt, sizeof(item->iocb)); + memcpy(&item->iocb, pkt, QLA_DEFAULT_PAYLOAD_SIZE); return item; } diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c index 8ee2e337c9e1b7..92488890bc04e3 100644 --- a/drivers/scsi/qla2xxx/qla_nvme.c +++ b/drivers/scsi/qla2xxx/qla_nvme.c @@ -1308,7 +1308,7 @@ void qla2xxx_process_purls_iocb(void **pkt, struct rsp_que **rsp) ql_dbg(ql_dbg_unsol, vha, 0x2121, "PURLS OP[%01x] size %d xchg addr 0x%x portid %06x\n", - item->iocb.iocb[3], item->size, uctx->exchange_address, + item->iocb[3], item->size, uctx->exchange_address, fcport->d_id.b24); /* +48 0 1 2 3 4 5 6 7 8 9 A B C D E F * ----- ----------------------------------------------- diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index d4b484c0fd9d7a..32437bae1a303b 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -6459,9 +6459,12 @@ void qla24xx_process_purex_rdp(struct scsi_qla_host *vha, void qla24xx_free_purex_item(struct purex_item *item) { - if (item == &item->vha->default_item) - memset(&item->vha->default_item, 0, sizeof(struct purex_item)); - else + scsi_qla_host_t *base_vha = item->vha; + + if (item == &base_vha->default_item) { + memset(&base_vha->__default_item_iocb, 0, QLA_DEFAULT_PAYLOAD_SIZE); + memset(&base_vha->default_item, 0, sizeof(struct purex_item)); + } else kfree(item); } diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 3a821afee9bc18..811530037a1eff 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -25,6 +25,10 @@ #include #include "scsi_priv.h" +#if (IS_ENABLED(CONFIG_NVME_FC)) +void nvme_fc_modify_rport_fpin_state(u64 local_wwpn, u64 remote_wwpn, bool marginal); +#endif + static int fc_queue_work(struct Scsi_Host *, struct work_struct *); static void fc_vport_sched_delete(struct work_struct *work); static int fc_vport_setup(struct Scsi_Host *shost, int channel, @@ -743,13 +747,12 @@ fc_cn_stats_update(u16 event_type, struct fc_fpin_stats *stats) * */ static void -fc_fpin_li_stats_update(struct Scsi_Host *shost, struct fc_tlv_desc *tlv) +fc_fpin_li_stats_update(struct Scsi_Host *shost, struct fc_fn_li_desc *li_desc) { u8 i; struct fc_rport *rport = NULL; struct fc_rport *attach_rport = NULL; struct fc_host_attrs *fc_host = shost_to_fc_host(shost); - struct fc_fn_li_desc *li_desc = (struct fc_fn_li_desc *)tlv; u16 event_type = be16_to_cpu(li_desc->event_type); u64 wwpn; @@ -792,12 +795,11 @@ fc_fpin_li_stats_update(struct Scsi_Host *shost, struct fc_tlv_desc *tlv) */ static void fc_fpin_delivery_stats_update(struct Scsi_Host *shost, - struct fc_tlv_desc *tlv) + struct fc_fn_deli_desc *dn_desc) { struct fc_rport *rport = NULL; struct fc_rport *attach_rport = NULL; struct fc_host_attrs *fc_host = shost_to_fc_host(shost); - struct fc_fn_deli_desc *dn_desc = (struct fc_fn_deli_desc *)tlv; u32 reason_code = be32_to_cpu(dn_desc->deli_reason_code); rport = fc_find_rport_by_wwpn(shost, @@ -823,13 +825,11 @@ fc_fpin_delivery_stats_update(struct Scsi_Host *shost, */ static void fc_fpin_peer_congn_stats_update(struct Scsi_Host *shost, - struct fc_tlv_desc *tlv) + struct fc_fn_peer_congn_desc *pc_desc) { u8 i; struct fc_rport *rport = NULL; struct fc_rport *attach_rport = NULL; - struct fc_fn_peer_congn_desc *pc_desc = - (struct fc_fn_peer_congn_desc *)tlv; u16 event_type = be16_to_cpu(pc_desc->event_type); u64 wwpn; @@ -869,15 +869,91 @@ fc_fpin_peer_congn_stats_update(struct Scsi_Host *shost, */ static void fc_fpin_congn_stats_update(struct Scsi_Host *shost, - struct fc_tlv_desc *tlv) + struct fc_fn_congn_desc *congn) { struct fc_host_attrs *fc_host = shost_to_fc_host(shost); - struct fc_fn_congn_desc *congn = (struct fc_fn_congn_desc *)tlv; fc_cn_stats_update(be16_to_cpu(congn->event_type), &fc_host->fpin_stats); } +/** + * fc_host_fpin_set_nvme_rport_marginal + * - Set FC_PORTSTATE_MARGINAL for nvme rports in FPIN + * @shost: host the FPIN was received on + * @fpin_len: length of FPIN payload, in bytes + * @fpin_buf: pointer to FPIN payload + * + * This function processes a received FPIN and sets FC_PORTSTATE_MARGINAL on + * all remote ports that have FC_PORT_ROLE_NVME_TARGET set identified in the + * FPIN descriptors. + * + * Notes: + * This routine assumes no locks are held on entry. + */ +void +fc_host_fpin_set_nvme_rport_marginal(struct Scsi_Host *shost, u32 fpin_len, char *fpin_buf) +{ + struct fc_els_fpin *fpin = (struct fc_els_fpin *)fpin_buf; + struct fc_rport *rport; + union fc_tlv_desc *tlv; + u64 local_wwpn = fc_host_port_name(shost); + u64 wwpn, attached_wwpn; + u32 bytes_remain; + u32 dtag; + u8 i; + unsigned long flags; + + /* Parse FPIN descriptors */ + tlv = &fpin->fpin_desc[0]; + bytes_remain = fpin_len - offsetof(struct fc_els_fpin, fpin_desc); + bytes_remain = min_t(u32, bytes_remain, be32_to_cpu(fpin->desc_len)); + + while (bytes_remain >= FC_TLV_DESC_HDR_SZ && + bytes_remain >= FC_TLV_DESC_SZ_FROM_LENGTH(tlv)) { + dtag = be32_to_cpu(tlv->hdr.desc_tag); + switch (dtag) { + case ELS_DTAG_LNK_INTEGRITY: + struct fc_fn_li_desc *li_desc = &tlv->li; + + attached_wwpn = be64_to_cpu(li_desc->attached_wwpn); + + /* Set marginal state for WWPNs in pname_list */ + if (be32_to_cpu(li_desc->pname_count) > 0) { + for (i = 0; i < be32_to_cpu(li_desc->pname_count); i++) { + wwpn = be64_to_cpu(li_desc->pname_list[i]); + if (wwpn == attached_wwpn) + continue; + + rport = fc_find_rport_by_wwpn(shost, wwpn); + if (!rport) + continue; + + spin_lock_irqsave(shost->host_lock, flags); + + if (rport->port_state == FC_PORTSTATE_ONLINE && + rport->roles & FC_PORT_ROLE_NVME_TARGET) { + rport->port_state = FC_PORTSTATE_MARGINAL; + spin_unlock_irqrestore(shost->host_lock, flags); +#if (IS_ENABLED(CONFIG_NVME_FC)) + nvme_fc_modify_rport_fpin_state(local_wwpn, + wwpn, true); +#endif + } else + spin_unlock_irqrestore(shost->host_lock, flags); + + } + } + break; + default: + break; + } + bytes_remain -= FC_TLV_DESC_SZ_FROM_LENGTH(tlv); + tlv = fc_tlv_next_desc(tlv); + } +} +EXPORT_SYMBOL(fc_host_fpin_set_nvme_rport_marginal); + /** * fc_host_fpin_rcv - routine to process a received FPIN. * @shost: host the FPIN was received on @@ -892,32 +968,32 @@ fc_host_fpin_rcv(struct Scsi_Host *shost, u32 fpin_len, char *fpin_buf, u8 event_acknowledge) { struct fc_els_fpin *fpin = (struct fc_els_fpin *)fpin_buf; - struct fc_tlv_desc *tlv; + union fc_tlv_desc *tlv; u32 bytes_remain; u32 dtag; enum fc_host_event_code event_code = event_acknowledge ? FCH_EVT_LINK_FPIN_ACK : FCH_EVT_LINK_FPIN; /* Update Statistics */ - tlv = (struct fc_tlv_desc *)&fpin->fpin_desc[0]; + tlv = &fpin->fpin_desc[0]; bytes_remain = fpin_len - offsetof(struct fc_els_fpin, fpin_desc); bytes_remain = min_t(u32, bytes_remain, be32_to_cpu(fpin->desc_len)); while (bytes_remain >= FC_TLV_DESC_HDR_SZ && bytes_remain >= FC_TLV_DESC_SZ_FROM_LENGTH(tlv)) { - dtag = be32_to_cpu(tlv->desc_tag); + dtag = be32_to_cpu(tlv->hdr.desc_tag); switch (dtag) { case ELS_DTAG_LNK_INTEGRITY: - fc_fpin_li_stats_update(shost, tlv); + fc_fpin_li_stats_update(shost, &tlv->li); break; case ELS_DTAG_DELIVERY: - fc_fpin_delivery_stats_update(shost, tlv); + fc_fpin_delivery_stats_update(shost, &tlv->deli); break; case ELS_DTAG_PEER_CONGEST: - fc_fpin_peer_congn_stats_update(shost, tlv); + fc_fpin_peer_congn_stats_update(shost, &tlv->peer_congn); break; case ELS_DTAG_CONGESTION: - fc_fpin_congn_stats_update(shost, tlv); + fc_fpin_congn_stats_update(shost, &tlv->congn); } bytes_remain -= FC_TLV_DESC_SZ_FROM_LENGTH(tlv); @@ -1229,34 +1305,62 @@ static ssize_t fc_rport_set_marginal_state(struct device *dev, const char *buf, size_t count) { struct fc_rport *rport = transport_class_to_rport(dev); + struct Scsi_Host *shost = rport_to_shost(rport); + u64 local_wwpn = fc_host_port_name(shost); enum fc_port_state port_state; int ret = 0; + unsigned long flags; ret = get_fc_port_state_match(buf, &port_state); if (ret) return -EINVAL; - if (port_state == FC_PORTSTATE_MARGINAL) { + + spin_lock_irqsave(shost->host_lock, flags); + + switch (port_state) { + case FC_PORTSTATE_MARGINAL: /* * Change the state to Marginal only if the * current rport state is Online * Allow only Online->Marginal */ - if (rport->port_state == FC_PORTSTATE_ONLINE) + if (rport->port_state == FC_PORTSTATE_ONLINE) { rport->port_state = port_state; - else if (port_state != rport->port_state) - return -EINVAL; - } else if (port_state == FC_PORTSTATE_ONLINE) { + spin_unlock_irqrestore(shost->host_lock, flags); +#if (IS_ENABLED(CONFIG_NVME_FC)) + nvme_fc_modify_rport_fpin_state(local_wwpn, + rport->port_name, true); +#endif + return count; + } + break; + + case FC_PORTSTATE_ONLINE: /* * Change the state to Online only if the * current rport state is Marginal * Allow only Marginal->Online */ - if (rport->port_state == FC_PORTSTATE_MARGINAL) + if (rport->port_state == FC_PORTSTATE_MARGINAL) { rport->port_state = port_state; - else if (port_state != rport->port_state) - return -EINVAL; - } else + spin_unlock_irqrestore(shost->host_lock, flags); +#if (IS_ENABLED(CONFIG_NVME_FC)) + nvme_fc_modify_rport_fpin_state(local_wwpn, + rport->port_name, false); +#endif + return count; + } + break; + default: + break; + } + + if (port_state != rport->port_state) { + spin_unlock_irqrestore(shost->host_lock, flags); return -EINVAL; + } + + spin_unlock_irqrestore(shost->host_lock, flags); return count; } diff --git a/include/linux/nvme-fc-driver.h b/include/linux/nvme-fc-driver.h index 9f6acadfe0c868..b026e6312f8550 100644 --- a/include/linux/nvme-fc-driver.h +++ b/include/linux/nvme-fc-driver.h @@ -536,6 +536,8 @@ void nvme_fc_rescan_remoteport(struct nvme_fc_remote_port *remoteport); int nvme_fc_set_remoteport_devloss(struct nvme_fc_remote_port *remoteport, u32 dev_loss_tmo); +void nvme_fc_modify_rport_fpin_state(u64 local_wwpn, u64 remote_wwpn, bool marginal); + /* * Routine called to pass a NVME-FC LS request, received by the lldd, * to the nvme-fc transport. diff --git a/include/scsi/scsi_transport_fc.h b/include/scsi/scsi_transport_fc.h index b908aacfef4862..e8d46b71badab4 100644 --- a/include/scsi/scsi_transport_fc.h +++ b/include/scsi/scsi_transport_fc.h @@ -852,6 +852,7 @@ void fc_host_post_fc_event(struct Scsi_Host *shost, u32 event_number, */ void fc_host_fpin_rcv(struct Scsi_Host *shost, u32 fpin_len, char *fpin_buf, u8 event_acknowledge); +void fc_host_fpin_set_nvme_rport_marginal(struct Scsi_Host *shost, u32 fpin_len, char *fpin_buf); struct fc_vport *fc_vport_create(struct Scsi_Host *shost, int channel, struct fc_vport_identifiers *); int fc_vport_terminate(struct fc_vport *vport); diff --git a/include/uapi/scsi/fc/fc_els.h b/include/uapi/scsi/fc/fc_els.h index 16782c360de3c1..3598dc553f4dfa 100644 --- a/include/uapi/scsi/fc/fc_els.h +++ b/include/uapi/scsi/fc/fc_els.h @@ -253,12 +253,12 @@ enum fc_ls_tlv_dtag { /* - * Generic Link Service TLV Descriptor format + * Generic Link Service TLV Descriptor header * * This structure, as it defines no payload, will also be referred to * as the "tlv header" - which contains the tag and len fields. */ -struct fc_tlv_desc { +struct fc_tlv_desc_hdr { __be32 desc_tag; /* Notification Descriptor Tag */ __be32 desc_len; /* Length of Descriptor (in bytes). * Size of descriptor excluding @@ -267,36 +267,6 @@ struct fc_tlv_desc { __u8 desc_value[]; /* Descriptor Value */ }; -/* Descriptor tag and len fields are considered the mandatory header - * for a descriptor - */ -#define FC_TLV_DESC_HDR_SZ sizeof(struct fc_tlv_desc) - -/* - * Macro, used when initializing payloads, to return the descriptor length. - * Length is size of descriptor minus the tag and len fields. - */ -#define FC_TLV_DESC_LENGTH_FROM_SZ(desc) \ - (sizeof(desc) - FC_TLV_DESC_HDR_SZ) - -/* Macro, used on received payloads, to return the descriptor length */ -#define FC_TLV_DESC_SZ_FROM_LENGTH(tlv) \ - (__be32_to_cpu((tlv)->desc_len) + FC_TLV_DESC_HDR_SZ) - -/* - * This helper is used to walk descriptors in a descriptor list. - * Given the address of the current descriptor, which minimally contains a - * tag and len field, calculate the address of the next descriptor based - * on the len field. - */ -static inline void *fc_tlv_next_desc(void *desc) -{ - struct fc_tlv_desc *tlv = desc; - - return (desc + FC_TLV_DESC_SZ_FROM_LENGTH(tlv)); -} - - /* * Link Service Request Information Descriptor */ @@ -1094,19 +1064,6 @@ struct fc_fn_congn_desc { __u8 resv[3]; /* reserved - must be zero */ }; -/* - * ELS_FPIN - Fabric Performance Impact Notification - */ -struct fc_els_fpin { - __u8 fpin_cmd; /* command (0x16) */ - __u8 fpin_zero[3]; /* specified as zero - part of cmd */ - __be32 desc_len; /* Length of Descriptor List (in bytes). - * Size of ELS excluding fpin_cmd, - * fpin_zero and desc_len fields. - */ - struct fc_tlv_desc fpin_desc[]; /* Descriptor list */ -}; - /* Diagnostic Function Descriptor - FPIN Registration */ struct fc_df_desc_fpin_reg { __be32 desc_tag; /* FPIN Registration (0x00030001) */ @@ -1125,33 +1082,6 @@ struct fc_df_desc_fpin_reg { */ }; -/* - * ELS_RDF - Register Diagnostic Functions - */ -struct fc_els_rdf { - __u8 fpin_cmd; /* command (0x19) */ - __u8 fpin_zero[3]; /* specified as zero - part of cmd */ - __be32 desc_len; /* Length of Descriptor List (in bytes). - * Size of ELS excluding fpin_cmd, - * fpin_zero and desc_len fields. - */ - struct fc_tlv_desc desc[]; /* Descriptor list */ -}; - -/* - * ELS RDF LS_ACC Response. - */ -struct fc_els_rdf_resp { - struct fc_els_ls_acc acc_hdr; - __be32 desc_list_len; /* Length of response (in - * bytes). Excludes acc_hdr - * and desc_list_len fields. - */ - struct fc_els_lsri_desc lsri; - struct fc_tlv_desc desc[]; /* Supported Descriptor list */ -}; - - /* * Diagnostic Capability Descriptors for EDC ELS */ @@ -1221,6 +1151,65 @@ struct fc_diag_cg_sig_desc { struct fc_diag_cg_sig_freq rcv_signal_frequency; }; +/* + * Generic Link Service TLV Descriptor format + * + * This structure, as it defines no payload, will also be referred to + * as the "tlv header" - which contains the tag and len fields. + */ +union fc_tlv_desc { + struct fc_tlv_desc_hdr hdr; + struct fc_els_lsri_desc lsri; + struct fc_fn_li_desc li; + struct fc_fn_deli_desc deli; + struct fc_fn_peer_congn_desc peer_congn; + struct fc_fn_congn_desc congn; + struct fc_df_desc_fpin_reg fpin_reg; + struct fc_diag_lnkflt_desc lnkflt; + struct fc_diag_cg_sig_desc cg_sig; +}; + +/* Descriptor tag and len fields are considered the mandatory header + * for a descriptor + */ +#define FC_TLV_DESC_HDR_SZ sizeof(struct fc_tlv_desc_hdr) + +/* + * Macro, used when initializing payloads, to return the descriptor length. + * Length is size of descriptor minus the tag and len fields. + */ +#define FC_TLV_DESC_LENGTH_FROM_SZ(desc) \ + (sizeof(desc) - FC_TLV_DESC_HDR_SZ) + +/* Macro, used on received payloads, to return the descriptor length */ +#define FC_TLV_DESC_SZ_FROM_LENGTH(tlv) \ + (__be32_to_cpu((tlv)->hdr.desc_len) + FC_TLV_DESC_HDR_SZ) + +/* + * This helper is used to walk descriptors in a descriptor list. + * Given the address of the current descriptor, which minimally contains a + * tag and len field, calculate the address of the next descriptor based + * on the len field. + */ +static inline union fc_tlv_desc *fc_tlv_next_desc(union fc_tlv_desc *desc) +{ + return (union fc_tlv_desc *)((__u8 *)desc + FC_TLV_DESC_SZ_FROM_LENGTH(desc)); +} + + +/* + * ELS_FPIN - Fabric Performance Impact Notification + */ +struct fc_els_fpin { + __u8 fpin_cmd; /* command (0x16) */ + __u8 fpin_zero[3]; /* specified as zero - part of cmd */ + __be32 desc_len; /* Length of Descriptor List (in bytes). + * Size of ELS excluding fpin_cmd, + * fpin_zero and desc_len fields. + */ + union fc_tlv_desc fpin_desc[]; /* Descriptor list */ +}; + /* * ELS_EDC - Exchange Diagnostic Capabilities */ @@ -1231,10 +1220,37 @@ struct fc_els_edc { * Size of ELS excluding edc_cmd, * edc_zero and desc_len fields. */ - struct fc_tlv_desc desc[]; + union fc_tlv_desc desc[]; /* Diagnostic Descriptor list */ }; +/* + * ELS_RDF - Register Diagnostic Functions + */ +struct fc_els_rdf { + __u8 fpin_cmd; /* command (0x19) */ + __u8 fpin_zero[3]; /* specified as zero - part of cmd */ + __be32 desc_len; /* Length of Descriptor List (in bytes). + * Size of ELS excluding fpin_cmd, + * fpin_zero and desc_len fields. + */ + union fc_tlv_desc desc[]; /* Descriptor list */ +}; + +/* + * ELS RDF LS_ACC Response. + */ +struct fc_els_rdf_resp { + struct fc_els_ls_acc acc_hdr; + __be32 desc_list_len; /* Length of response (in + * bytes). Excludes acc_hdr + * and desc_list_len fields. + */ + struct fc_els_lsri_desc lsri; + union fc_tlv_desc desc[]; /* Supported Descriptor list */ +}; + + /* * ELS EDC LS_ACC Response. */ @@ -1245,9 +1261,8 @@ struct fc_els_edc_resp { * and desc_list_len fields. */ struct fc_els_lsri_desc lsri; - struct fc_tlv_desc desc[]; + union fc_tlv_desc desc[]; /* Supported Diagnostic Descriptor list */ }; - #endif /* _FC_ELS_H_ */