mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-11-20 16:46:23 +08:00
[SCSI] lpfc 8.2.4 : Miscellaneous Discovery/ELS Fixes
Miscellaneous Discovery/ELS Fixes: - Delay free's of ELS requests if adapter reject conditions - Fix concurrent PLOGI vs ADISC state handling - Add retry mechanism for GFF_ID - Correct some illegal state transitions around RSCN timeouts - Fix missing return in FAN handling Signed-off-by: James Smart <James.Smart@emulex.com> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
This commit is contained in:
parent
b18268fc63
commit
0ff10d46cf
@ -583,6 +583,11 @@ struct lpfc_hba {
|
||||
atomic_t slow_ring_trc_cnt;
|
||||
#endif
|
||||
|
||||
/* Used for deferred freeing of ELS data buffers */
|
||||
struct list_head elsbuf;
|
||||
int elsbuf_cnt;
|
||||
int elsbuf_prev_cnt;
|
||||
|
||||
uint8_t temp_sensor_support;
|
||||
/* Fields used for heart beat. */
|
||||
unsigned long last_completion_time;
|
||||
|
@ -89,6 +89,7 @@ int lpfc_check_sparm(struct lpfc_vport *, struct lpfc_nodelist *,
|
||||
struct serv_parm *, uint32_t);
|
||||
int lpfc_els_abort(struct lpfc_hba *, struct lpfc_nodelist *);
|
||||
void lpfc_more_plogi(struct lpfc_vport *);
|
||||
void lpfc_more_adisc(struct lpfc_vport *);
|
||||
void lpfc_end_rscn(struct lpfc_vport *);
|
||||
int lpfc_els_chk_latt(struct lpfc_vport *);
|
||||
int lpfc_els_abort_flogi(struct lpfc_hba *);
|
||||
|
@ -426,6 +426,7 @@ lpfc_ns_rsp(struct lpfc_vport *vport, struct lpfc_dmabuf *mp, uint32_t Size)
|
||||
|
||||
lpfc_set_disctmo(vport);
|
||||
vport->num_disc_nodes = 0;
|
||||
vport->fc_ns_retry = 0;
|
||||
|
||||
|
||||
list_add_tail(&head, &mp->list);
|
||||
@ -506,7 +507,17 @@ lpfc_ns_rsp(struct lpfc_vport *vport, struct lpfc_dmabuf *mp, uint32_t Size)
|
||||
Did, vport->fc_flag,
|
||||
vport->fc_rscn_id_cnt);
|
||||
|
||||
if (lpfc_ns_cmd(vport,
|
||||
/* This NPortID was previously
|
||||
* a FCP target, * Don't even
|
||||
* bother to send GFF_ID.
|
||||
*/
|
||||
ndlp = lpfc_findnode_did(vport,
|
||||
Did);
|
||||
if (ndlp && (ndlp->nlp_type &
|
||||
NLP_FCP_TARGET))
|
||||
lpfc_setup_disc_node
|
||||
(vport, Did);
|
||||
else if (lpfc_ns_cmd(vport,
|
||||
SLI_CTNS_GFF_ID,
|
||||
0, Did) == 0)
|
||||
vport->num_disc_nodes++;
|
||||
@ -554,7 +565,7 @@ lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
|
||||
struct lpfc_dmabuf *outp;
|
||||
struct lpfc_sli_ct_request *CTrsp;
|
||||
struct lpfc_nodelist *ndlp;
|
||||
int rc;
|
||||
int rc, retry;
|
||||
|
||||
/* First save ndlp, before we overwrite it */
|
||||
ndlp = cmdiocb->context_un.ndlp;
|
||||
@ -585,14 +596,35 @@ lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
|
||||
if (irsp->ulpStatus) {
|
||||
/* Check for retry */
|
||||
if (vport->fc_ns_retry < LPFC_MAX_NS_RETRY) {
|
||||
if ((irsp->ulpStatus != IOSTAT_LOCAL_REJECT) ||
|
||||
(irsp->un.ulpWord[4] != IOERR_NO_RESOURCES))
|
||||
retry = 1;
|
||||
if (irsp->ulpStatus == IOSTAT_LOCAL_REJECT) {
|
||||
switch (irsp->un.ulpWord[4]) {
|
||||
case IOERR_NO_RESOURCES:
|
||||
/* We don't increment the retry
|
||||
* count for this case.
|
||||
*/
|
||||
break;
|
||||
case IOERR_LINK_DOWN:
|
||||
case IOERR_SLI_ABORTED:
|
||||
case IOERR_SLI_DOWN:
|
||||
retry = 0;
|
||||
break;
|
||||
default:
|
||||
vport->fc_ns_retry++;
|
||||
}
|
||||
}
|
||||
else
|
||||
vport->fc_ns_retry++;
|
||||
/* CT command is being retried */
|
||||
rc = lpfc_ns_cmd(vport, SLI_CTNS_GID_FT,
|
||||
|
||||
if (retry) {
|
||||
/* CT command is being retried */
|
||||
rc = lpfc_ns_cmd(vport, SLI_CTNS_GID_FT,
|
||||
vport->fc_ns_retry, 0);
|
||||
if (rc == 0)
|
||||
goto out;
|
||||
if (rc == 0) {
|
||||
/* success */
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
lpfc_vport_set_state(vport, FC_VPORT_FAILED);
|
||||
lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
|
||||
@ -698,7 +730,7 @@ lpfc_cmpl_ct_cmd_gff_id(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
|
||||
struct lpfc_dmabuf *inp = (struct lpfc_dmabuf *) cmdiocb->context1;
|
||||
struct lpfc_dmabuf *outp = (struct lpfc_dmabuf *) cmdiocb->context2;
|
||||
struct lpfc_sli_ct_request *CTrsp;
|
||||
int did;
|
||||
int did, rc, retry;
|
||||
uint8_t fbits;
|
||||
struct lpfc_nodelist *ndlp;
|
||||
|
||||
@ -729,6 +761,39 @@ lpfc_cmpl_ct_cmd_gff_id(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Check for retry */
|
||||
if (cmdiocb->retry < LPFC_MAX_NS_RETRY) {
|
||||
retry = 1;
|
||||
if (irsp->ulpStatus == IOSTAT_LOCAL_REJECT) {
|
||||
switch (irsp->un.ulpWord[4]) {
|
||||
case IOERR_NO_RESOURCES:
|
||||
/* We don't increment the retry
|
||||
* count for this case.
|
||||
*/
|
||||
break;
|
||||
case IOERR_LINK_DOWN:
|
||||
case IOERR_SLI_ABORTED:
|
||||
case IOERR_SLI_DOWN:
|
||||
retry = 0;
|
||||
break;
|
||||
default:
|
||||
cmdiocb->retry++;
|
||||
}
|
||||
}
|
||||
else
|
||||
cmdiocb->retry++;
|
||||
|
||||
if (retry) {
|
||||
/* CT command is being retried */
|
||||
rc = lpfc_ns_cmd(vport, SLI_CTNS_GFF_ID,
|
||||
cmdiocb->retry, did);
|
||||
if (rc == 0) {
|
||||
/* success */
|
||||
lpfc_ct_free_iocb(phba, cmdiocb);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
|
||||
"0267 NameServer GFF Rsp "
|
||||
"x%x Error (%d %d) Data: x%x x%x\n",
|
||||
|
@ -783,6 +783,8 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
|
||||
{
|
||||
struct lpfc_vport *vport = ndlp->vport;
|
||||
struct lpfc_nodelist *new_ndlp;
|
||||
struct lpfc_rport_data *rdata;
|
||||
struct fc_rport *rport;
|
||||
struct serv_parm *sp;
|
||||
uint8_t name[sizeof(struct lpfc_name)];
|
||||
uint32_t rc;
|
||||
@ -819,6 +821,11 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
|
||||
lpfc_unreg_rpi(vport, new_ndlp);
|
||||
new_ndlp->nlp_DID = ndlp->nlp_DID;
|
||||
new_ndlp->nlp_prev_state = ndlp->nlp_prev_state;
|
||||
|
||||
if (ndlp->nlp_flag & NLP_NPR_2B_DISC)
|
||||
new_ndlp->nlp_flag |= NLP_NPR_2B_DISC;
|
||||
ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
|
||||
|
||||
lpfc_nlp_set_state(vport, new_ndlp, ndlp->nlp_state);
|
||||
|
||||
/* Move this back to NPR state */
|
||||
@ -826,6 +833,20 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
|
||||
/* The new_ndlp is replacing ndlp totally, so we need
|
||||
* to put ndlp on UNUSED list and try to free it.
|
||||
*/
|
||||
|
||||
/* Fix up the rport accordingly */
|
||||
rport = ndlp->rport;
|
||||
if (rport) {
|
||||
rdata = rport->dd_data;
|
||||
if (rdata->pnode == ndlp) {
|
||||
lpfc_nlp_put(ndlp);
|
||||
ndlp->rport = NULL;
|
||||
rdata->pnode = lpfc_nlp_get(new_ndlp);
|
||||
new_ndlp->rport = rport;
|
||||
}
|
||||
new_ndlp->nlp_type = ndlp->nlp_type;
|
||||
}
|
||||
|
||||
lpfc_drop_node(vport, ndlp);
|
||||
}
|
||||
else {
|
||||
@ -1149,7 +1170,7 @@ lpfc_issue_els_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
lpfc_more_adisc(struct lpfc_vport *vport)
|
||||
{
|
||||
int sentadisc;
|
||||
@ -2100,8 +2121,35 @@ lpfc_els_free_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *elsiocb)
|
||||
}
|
||||
/* context2 = cmd, context2->next = rsp, context3 = bpl */
|
||||
if (elsiocb->context2) {
|
||||
buf_ptr1 = (struct lpfc_dmabuf *) elsiocb->context2;
|
||||
lpfc_els_free_data(phba, buf_ptr1);
|
||||
if (elsiocb->iocb_flag & LPFC_DELAY_MEM_FREE) {
|
||||
/* Firmware could still be in progress of DMAing
|
||||
* payload, so don't free data buffer till after
|
||||
* a hbeat.
|
||||
*/
|
||||
elsiocb->iocb_flag &= ~LPFC_DELAY_MEM_FREE;
|
||||
buf_ptr = elsiocb->context2;
|
||||
elsiocb->context2 = NULL;
|
||||
if (buf_ptr) {
|
||||
buf_ptr1 = NULL;
|
||||
spin_lock_irq(&phba->hbalock);
|
||||
if (!list_empty(&buf_ptr->list)) {
|
||||
list_remove_head(&buf_ptr->list,
|
||||
buf_ptr1, struct lpfc_dmabuf,
|
||||
list);
|
||||
INIT_LIST_HEAD(&buf_ptr1->list);
|
||||
list_add_tail(&buf_ptr1->list,
|
||||
&phba->elsbuf);
|
||||
phba->elsbuf_cnt++;
|
||||
}
|
||||
INIT_LIST_HEAD(&buf_ptr->list);
|
||||
list_add_tail(&buf_ptr->list, &phba->elsbuf);
|
||||
phba->elsbuf_cnt++;
|
||||
spin_unlock_irq(&phba->hbalock);
|
||||
}
|
||||
} else {
|
||||
buf_ptr1 = (struct lpfc_dmabuf *) elsiocb->context2;
|
||||
lpfc_els_free_data(phba, buf_ptr1);
|
||||
}
|
||||
}
|
||||
|
||||
if (elsiocb->context3) {
|
||||
@ -3027,6 +3075,8 @@ lpfc_els_handle_rscn(struct lpfc_vport *vport)
|
||||
|
||||
/* To process RSCN, first compare RSCN data with NameServer */
|
||||
vport->fc_ns_retry = 0;
|
||||
vport->num_disc_nodes = 0;
|
||||
|
||||
ndlp = lpfc_findnode_did(vport, NameServer_DID);
|
||||
if (ndlp && ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) {
|
||||
/* Good ndlp, issue CT Request to NameServer */
|
||||
|
@ -2564,6 +2564,7 @@ lpfc_disc_timeout_handler(struct lpfc_vport *vport)
|
||||
}
|
||||
if (vport->port_state != LPFC_FLOGI) {
|
||||
lpfc_initial_flogi(vport);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -547,8 +547,10 @@ void
|
||||
lpfc_hb_timeout_handler(struct lpfc_hba *phba)
|
||||
{
|
||||
LPFC_MBOXQ_t *pmboxq;
|
||||
struct lpfc_dmabuf *buf_ptr;
|
||||
int retval;
|
||||
struct lpfc_sli *psli = &phba->sli;
|
||||
LIST_HEAD(completions);
|
||||
|
||||
if ((phba->link_state == LPFC_HBA_ERROR) ||
|
||||
(phba->pport->load_flag & FC_UNLOADING) ||
|
||||
@ -575,6 +577,24 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba)
|
||||
}
|
||||
spin_unlock_irq(&phba->pport->work_port_lock);
|
||||
|
||||
if (phba->elsbuf_cnt &&
|
||||
(phba->elsbuf_cnt == phba->elsbuf_prev_cnt)) {
|
||||
spin_lock_irq(&phba->hbalock);
|
||||
list_splice_init(&phba->elsbuf, &completions);
|
||||
phba->elsbuf_cnt = 0;
|
||||
phba->elsbuf_prev_cnt = 0;
|
||||
spin_unlock_irq(&phba->hbalock);
|
||||
|
||||
while (!list_empty(&completions)) {
|
||||
list_remove_head(&completions, buf_ptr,
|
||||
struct lpfc_dmabuf, list);
|
||||
lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys);
|
||||
kfree(buf_ptr);
|
||||
}
|
||||
}
|
||||
phba->elsbuf_prev_cnt = phba->elsbuf_cnt;
|
||||
|
||||
|
||||
/* If there is no heart beat outstanding, issue a heartbeat command */
|
||||
if (!phba->hb_outstanding) {
|
||||
pmboxq = mempool_alloc(phba->mbox_mem_pool,GFP_KERNEL);
|
||||
@ -1999,6 +2019,9 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
|
||||
/* Initialize list of fabric iocbs */
|
||||
INIT_LIST_HEAD(&phba->fabric_iocb_list);
|
||||
|
||||
/* Initialize list to save ELS buffers */
|
||||
INIT_LIST_HEAD(&phba->elsbuf);
|
||||
|
||||
vport = lpfc_create_port(phba, phba->brd_no, &phba->pcidev->dev);
|
||||
if (!vport)
|
||||
goto out_kthread_stop;
|
||||
|
@ -442,7 +442,27 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
|
||||
spin_lock_irq(shost->host_lock);
|
||||
ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
|
||||
spin_unlock_irq(shost->host_lock);
|
||||
if (vport->num_disc_nodes) {
|
||||
|
||||
if ((ndlp->nlp_flag & NLP_ADISC_SND) &&
|
||||
(vport->num_disc_nodes)) {
|
||||
/* Check to see if there are more
|
||||
* ADISCs to be sent
|
||||
*/
|
||||
lpfc_more_adisc(vport);
|
||||
|
||||
if ((vport->num_disc_nodes == 0) &&
|
||||
(vport->fc_npr_cnt))
|
||||
lpfc_els_disc_plogi(vport);
|
||||
|
||||
if (vport->num_disc_nodes == 0) {
|
||||
spin_lock_irq(shost->host_lock);
|
||||
vport->fc_flag &= ~FC_NDISC_ACTIVE;
|
||||
spin_unlock_irq(shost->host_lock);
|
||||
lpfc_can_disctmo(vport);
|
||||
lpfc_end_rscn(vport);
|
||||
}
|
||||
}
|
||||
else if (vport->num_disc_nodes) {
|
||||
/* Check to see if there are more
|
||||
* PLOGIs to be sent
|
||||
*/
|
||||
@ -813,6 +833,7 @@ lpfc_cmpl_plogi_plogi_issue(struct lpfc_vport *vport,
|
||||
uint32_t evt)
|
||||
{
|
||||
struct lpfc_hba *phba = vport->phba;
|
||||
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
|
||||
struct lpfc_iocbq *cmdiocb, *rspiocb;
|
||||
struct lpfc_dmabuf *pcmd, *prsp, *mp;
|
||||
uint32_t *lp;
|
||||
@ -930,10 +951,26 @@ out:
|
||||
"0261 Cannot Register NameServer login\n");
|
||||
}
|
||||
|
||||
spin_lock_irq(shost->host_lock);
|
||||
ndlp->nlp_flag |= NLP_DEFER_RM;
|
||||
spin_unlock_irq(shost->host_lock);
|
||||
return NLP_STE_FREED_NODE;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
lpfc_cmpl_logo_plogi_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
|
||||
void *arg, uint32_t evt)
|
||||
{
|
||||
return ndlp->nlp_state;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
lpfc_cmpl_reglogin_plogi_issue(struct lpfc_vport *vport,
|
||||
struct lpfc_nodelist *ndlp, void *arg, uint32_t evt)
|
||||
{
|
||||
return ndlp->nlp_state;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
lpfc_device_rm_plogi_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
|
||||
void *arg, uint32_t evt)
|
||||
@ -2006,9 +2043,9 @@ static uint32_t (*lpfc_disc_action[NLP_STE_MAX_STATE * NLP_EVT_MAX_EVENT])
|
||||
lpfc_rcv_els_plogi_issue, /* RCV_PRLO */
|
||||
lpfc_cmpl_plogi_plogi_issue, /* CMPL_PLOGI */
|
||||
lpfc_disc_illegal, /* CMPL_PRLI */
|
||||
lpfc_disc_illegal, /* CMPL_LOGO */
|
||||
lpfc_cmpl_logo_plogi_issue, /* CMPL_LOGO */
|
||||
lpfc_disc_illegal, /* CMPL_ADISC */
|
||||
lpfc_disc_illegal, /* CMPL_REG_LOGIN */
|
||||
lpfc_cmpl_reglogin_plogi_issue,/* CMPL_REG_LOGIN */
|
||||
lpfc_device_rm_plogi_issue, /* DEVICE_RM */
|
||||
lpfc_device_recov_plogi_issue, /* DEVICE_RECOVERY */
|
||||
|
||||
|
@ -1147,6 +1147,12 @@ lpfc_sli_process_sol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
|
||||
IOSTAT_LOCAL_REJECT;
|
||||
saveq->iocb.un.ulpWord[4] =
|
||||
IOERR_SLI_ABORTED;
|
||||
|
||||
/* Firmware could still be in progress
|
||||
* of DMAing payload, so don't free data
|
||||
* buffer till after a hbeat.
|
||||
*/
|
||||
saveq->iocb_flag |= LPFC_DELAY_MEM_FREE;
|
||||
}
|
||||
}
|
||||
(cmdiocbp->iocb_cmpl) (phba, cmdiocbp, saveq);
|
||||
@ -3281,6 +3287,7 @@ lpfc_sli_hba_down(struct lpfc_hba *phba)
|
||||
LIST_HEAD(completions);
|
||||
struct lpfc_sli *psli = &phba->sli;
|
||||
struct lpfc_sli_ring *pring;
|
||||
struct lpfc_dmabuf *buf_ptr;
|
||||
LPFC_MBOXQ_t *pmb;
|
||||
struct lpfc_iocbq *iocb;
|
||||
IOCB_t *cmd = NULL;
|
||||
@ -3320,6 +3327,19 @@ lpfc_sli_hba_down(struct lpfc_hba *phba)
|
||||
}
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&phba->hbalock, flags);
|
||||
list_splice_init(&phba->elsbuf, &completions);
|
||||
phba->elsbuf_cnt = 0;
|
||||
phba->elsbuf_prev_cnt = 0;
|
||||
spin_unlock_irqrestore(&phba->hbalock, flags);
|
||||
|
||||
while (!list_empty(&completions)) {
|
||||
list_remove_head(&completions, buf_ptr,
|
||||
struct lpfc_dmabuf, list);
|
||||
lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys);
|
||||
kfree(buf_ptr);
|
||||
}
|
||||
|
||||
/* Return any active mbox cmds */
|
||||
del_timer_sync(&psli->mbox_tmo);
|
||||
spin_lock_irqsave(&phba->hbalock, flags);
|
||||
@ -3490,6 +3510,12 @@ lpfc_sli_abort_els_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
|
||||
pring->txcmplq_cnt--;
|
||||
spin_unlock_irq(&phba->hbalock);
|
||||
|
||||
/* Firmware could still be in progress of DMAing
|
||||
* payload, so don't free data buffer till after
|
||||
* a hbeat.
|
||||
*/
|
||||
abort_iocb->iocb_flag |= LPFC_DELAY_MEM_FREE;
|
||||
|
||||
abort_iocb->iocb_flag &= ~LPFC_DRIVER_ABORTED;
|
||||
abort_iocb->iocb.ulpStatus = IOSTAT_LOCAL_REJECT;
|
||||
abort_iocb->iocb.un.ulpWord[4] = IOERR_SLI_ABORTED;
|
||||
|
@ -44,6 +44,7 @@ struct lpfc_iocbq {
|
||||
#define LPFC_IO_FCP 4 /* FCP command -- iocbq in scsi_buf */
|
||||
#define LPFC_DRIVER_ABORTED 8 /* driver aborted this request */
|
||||
#define LPFC_IO_FABRIC 0x10 /* Iocb send using fabric scheduler */
|
||||
#define LPFC_DELAY_MEM_FREE 0x20 /* Defer free'ing of FC data */
|
||||
|
||||
uint8_t abort_count;
|
||||
uint8_t rsvd2;
|
||||
|
Loading…
Reference in New Issue
Block a user