Commit 46fa311e authored by James Smart's avatar James Smart Committed by James Bottomley

[SCSI] lpfc 8.1.12 : Rework offline path to solve HBA reset issues

Rework offline path to solve HBA reset issues
Signed-off-by: default avatarJames Smart <James.Smart@emulex.com>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@SteelEye.com>
parent 07951076
...@@ -244,6 +244,7 @@ struct lpfc_hba { ...@@ -244,6 +244,7 @@ struct lpfc_hba {
#define FC_FABRIC 0x100 /* We are fabric attached */ #define FC_FABRIC 0x100 /* We are fabric attached */
#define FC_ESTABLISH_LINK 0x200 /* Reestablish Link */ #define FC_ESTABLISH_LINK 0x200 /* Reestablish Link */
#define FC_RSCN_DISCOVERY 0x400 /* Authenticate all devices after RSCN*/ #define FC_RSCN_DISCOVERY 0x400 /* Authenticate all devices after RSCN*/
#define FC_BLOCK_MGMT_IO 0x800 /* Don't allow mgmt mbx or iocb cmds */
#define FC_LOADING 0x1000 /* HBA in process of loading drvr */ #define FC_LOADING 0x1000 /* HBA in process of loading drvr */
#define FC_UNLOADING 0x2000 /* HBA in process of unloading drvr */ #define FC_UNLOADING 0x2000 /* HBA in process of unloading drvr */
#define FC_SCSI_SCAN_TMO 0x4000 /* scsi scan timer running */ #define FC_SCSI_SCAN_TMO 0x4000 /* scsi scan timer running */
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
*******************************************************************/ *******************************************************************/
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
...@@ -213,6 +214,7 @@ lpfc_issue_lip(struct Scsi_Host *host) ...@@ -213,6 +214,7 @@ lpfc_issue_lip(struct Scsi_Host *host)
int mbxstatus = MBXERR_ERROR; int mbxstatus = MBXERR_ERROR;
if ((phba->fc_flag & FC_OFFLINE_MODE) || if ((phba->fc_flag & FC_OFFLINE_MODE) ||
(phba->fc_flag & FC_BLOCK_MGMT_IO) ||
(phba->hba_state != LPFC_HBA_READY)) (phba->hba_state != LPFC_HBA_READY))
return -EPERM; return -EPERM;
...@@ -247,19 +249,62 @@ lpfc_issue_lip(struct Scsi_Host *host) ...@@ -247,19 +249,62 @@ lpfc_issue_lip(struct Scsi_Host *host)
} }
static int static int
lpfc_selective_reset(struct lpfc_hba *phba) lpfc_do_offline(struct lpfc_hba *phba, uint32_t type)
{ {
struct completion online_compl; struct completion online_compl;
struct lpfc_sli_ring *pring;
struct lpfc_sli *psli;
int status = 0; int status = 0;
int cnt = 0;
int i;
init_completion(&online_compl); init_completion(&online_compl);
lpfc_workq_post_event(phba, &status, &online_compl, lpfc_workq_post_event(phba, &status, &online_compl,
LPFC_EVT_OFFLINE); LPFC_EVT_OFFLINE_PREP);
wait_for_completion(&online_compl);
if (status != 0)
return -EIO;
psli = &phba->sli;
for (i = 0; i < psli->num_rings; i++) {
pring = &psli->ring[i];
/* The linkdown event takes 30 seconds to timeout. */
while (pring->txcmplq_cnt) {
msleep(10);
if (cnt++ > 3000) {
lpfc_printf_log(phba,
KERN_WARNING, LOG_INIT,
"%d:0466 Outstanding IO when "
"bringing Adapter offline\n",
phba->brd_no);
break;
}
}
}
init_completion(&online_compl);
lpfc_workq_post_event(phba, &status, &online_compl, type);
wait_for_completion(&online_compl); wait_for_completion(&online_compl);
if (status != 0) if (status != 0)
return -EIO; return -EIO;
return 0;
}
static int
lpfc_selective_reset(struct lpfc_hba *phba)
{
struct completion online_compl;
int status = 0;
status = lpfc_do_offline(phba, LPFC_EVT_OFFLINE);
if (status != 0)
return status;
init_completion(&online_compl); init_completion(&online_compl);
lpfc_workq_post_event(phba, &status, &online_compl, lpfc_workq_post_event(phba, &status, &online_compl,
LPFC_EVT_ONLINE); LPFC_EVT_ONLINE);
...@@ -324,23 +369,19 @@ lpfc_board_mode_store(struct class_device *cdev, const char *buf, size_t count) ...@@ -324,23 +369,19 @@ lpfc_board_mode_store(struct class_device *cdev, const char *buf, size_t count)
init_completion(&online_compl); init_completion(&online_compl);
if(strncmp(buf, "online", sizeof("online") - 1) == 0) if(strncmp(buf, "online", sizeof("online") - 1) == 0) {
lpfc_workq_post_event(phba, &status, &online_compl, lpfc_workq_post_event(phba, &status, &online_compl,
LPFC_EVT_ONLINE); LPFC_EVT_ONLINE);
else if (strncmp(buf, "offline", sizeof("offline") - 1) == 0) wait_for_completion(&online_compl);
lpfc_workq_post_event(phba, &status, &online_compl, } else if (strncmp(buf, "offline", sizeof("offline") - 1) == 0)
LPFC_EVT_OFFLINE); status = lpfc_do_offline(phba, LPFC_EVT_OFFLINE);
else if (strncmp(buf, "warm", sizeof("warm") - 1) == 0) else if (strncmp(buf, "warm", sizeof("warm") - 1) == 0)
lpfc_workq_post_event(phba, &status, &online_compl, status = lpfc_do_offline(phba, LPFC_EVT_WARM_START);
LPFC_EVT_WARM_START); else if (strncmp(buf, "error", sizeof("error") - 1) == 0)
else if (strncmp(buf, "error", sizeof("error") - 1) == 0) status = lpfc_do_offline(phba, LPFC_EVT_KILL);
lpfc_workq_post_event(phba, &status, &online_compl,
LPFC_EVT_KILL);
else else
return -EINVAL; return -EINVAL;
wait_for_completion(&online_compl);
if (!status) if (!status)
return strlen(buf); return strlen(buf);
else else
...@@ -645,9 +686,7 @@ lpfc_soft_wwpn_store(struct class_device *cdev, const char *buf, size_t count) ...@@ -645,9 +686,7 @@ lpfc_soft_wwpn_store(struct class_device *cdev, const char *buf, size_t count)
dev_printk(KERN_NOTICE, &phba->pcidev->dev, dev_printk(KERN_NOTICE, &phba->pcidev->dev,
"lpfc%d: Reinitializing to use soft_wwpn\n", phba->brd_no); "lpfc%d: Reinitializing to use soft_wwpn\n", phba->brd_no);
init_completion(&online_compl); stat1 = lpfc_do_offline(phba, LPFC_EVT_OFFLINE);
lpfc_workq_post_event(phba, &stat1, &online_compl, LPFC_EVT_OFFLINE);
wait_for_completion(&online_compl);
if (stat1) if (stat1)
lpfc_printf_log(phba, KERN_ERR, LOG_INIT, lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"%d:0463 lpfc_soft_wwpn attribute set failed to reinit " "%d:0463 lpfc_soft_wwpn attribute set failed to reinit "
...@@ -1307,6 +1346,12 @@ sysfs_mbox_read(struct kobject *kobj, char *buf, loff_t off, size_t count) ...@@ -1307,6 +1346,12 @@ sysfs_mbox_read(struct kobject *kobj, char *buf, loff_t off, size_t count)
return -EPERM; return -EPERM;
} }
if (phba->fc_flag & FC_BLOCK_MGMT_IO) {
sysfs_mbox_idle(phba);
spin_unlock_irq(host->host_lock);
return -EAGAIN;
}
if ((phba->fc_flag & FC_OFFLINE_MODE) || if ((phba->fc_flag & FC_OFFLINE_MODE) ||
(!(phba->sli.sli_flag & LPFC_SLI2_ACTIVE))){ (!(phba->sli.sli_flag & LPFC_SLI2_ACTIVE))){
...@@ -1551,6 +1596,9 @@ lpfc_get_stats(struct Scsi_Host *shost) ...@@ -1551,6 +1596,9 @@ lpfc_get_stats(struct Scsi_Host *shost)
unsigned long seconds; unsigned long seconds;
int rc = 0; int rc = 0;
if (phba->fc_flag & FC_BLOCK_MGMT_IO)
return NULL;
pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!pmboxq) if (!pmboxq)
return NULL; return NULL;
...@@ -1651,6 +1699,9 @@ lpfc_reset_stats(struct Scsi_Host *shost) ...@@ -1651,6 +1699,9 @@ lpfc_reset_stats(struct Scsi_Host *shost)
MAILBOX_t *pmb; MAILBOX_t *pmb;
int rc = 0; int rc = 0;
if (phba->fc_flag & FC_BLOCK_MGMT_IO)
return;
pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!pmboxq) if (!pmboxq)
return; return;
......
...@@ -112,7 +112,10 @@ void lpfc_hba_init(struct lpfc_hba *, uint32_t *); ...@@ -112,7 +112,10 @@ void lpfc_hba_init(struct lpfc_hba *, uint32_t *);
int lpfc_post_buffer(struct lpfc_hba *, struct lpfc_sli_ring *, int, int); int lpfc_post_buffer(struct lpfc_hba *, struct lpfc_sli_ring *, int, int);
void lpfc_decode_firmware_rev(struct lpfc_hba *, char *, int); void lpfc_decode_firmware_rev(struct lpfc_hba *, char *, int);
int lpfc_online(struct lpfc_hba *); int lpfc_online(struct lpfc_hba *);
int lpfc_offline(struct lpfc_hba *); void lpfc_block_mgmt_io(struct lpfc_hba *);
void lpfc_unblock_mgmt_io(struct lpfc_hba *);
void lpfc_offline_prep(struct lpfc_hba *);
void lpfc_offline(struct lpfc_hba *);
int lpfc_sli_setup(struct lpfc_hba *); int lpfc_sli_setup(struct lpfc_hba *);
int lpfc_sli_queue_setup(struct lpfc_hba *); int lpfc_sli_queue_setup(struct lpfc_hba *);
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
/* worker thread events */ /* worker thread events */
enum lpfc_work_type { enum lpfc_work_type {
LPFC_EVT_ONLINE, LPFC_EVT_ONLINE,
LPFC_EVT_OFFLINE_PREP,
LPFC_EVT_OFFLINE, LPFC_EVT_OFFLINE,
LPFC_EVT_WARM_START, LPFC_EVT_WARM_START,
LPFC_EVT_KILL, LPFC_EVT_KILL,
......
...@@ -185,29 +185,35 @@ lpfc_work_list_done(struct lpfc_hba * phba) ...@@ -185,29 +185,35 @@ lpfc_work_list_done(struct lpfc_hba * phba)
*(int *)(evtp->evt_arg1) = 0; *(int *)(evtp->evt_arg1) = 0;
complete((struct completion *)(evtp->evt_arg2)); complete((struct completion *)(evtp->evt_arg2));
break; break;
case LPFC_EVT_OFFLINE: case LPFC_EVT_OFFLINE_PREP:
if (phba->hba_state >= LPFC_LINK_DOWN) if (phba->hba_state >= LPFC_LINK_DOWN)
lpfc_offline(phba); lpfc_offline_prep(phba);
*(int *)(evtp->evt_arg1) = 0;
complete((struct completion *)(evtp->evt_arg2));
break;
case LPFC_EVT_OFFLINE:
lpfc_offline(phba);
lpfc_sli_brdrestart(phba); lpfc_sli_brdrestart(phba);
*(int *)(evtp->evt_arg1) = *(int *)(evtp->evt_arg1) =
lpfc_sli_brdready(phba,HS_FFRDY | HS_MBRDY); lpfc_sli_brdready(phba, HS_FFRDY | HS_MBRDY);
lpfc_unblock_mgmt_io(phba);
complete((struct completion *)(evtp->evt_arg2)); complete((struct completion *)(evtp->evt_arg2));
break; break;
case LPFC_EVT_WARM_START: case LPFC_EVT_WARM_START:
if (phba->hba_state >= LPFC_LINK_DOWN) lpfc_offline(phba);
lpfc_offline(phba);
lpfc_reset_barrier(phba); lpfc_reset_barrier(phba);
lpfc_sli_brdreset(phba); lpfc_sli_brdreset(phba);
lpfc_hba_down_post(phba); lpfc_hba_down_post(phba);
*(int *)(evtp->evt_arg1) = *(int *)(evtp->evt_arg1) =
lpfc_sli_brdready(phba, HS_MBRDY); lpfc_sli_brdready(phba, HS_MBRDY);
lpfc_unblock_mgmt_io(phba);
complete((struct completion *)(evtp->evt_arg2)); complete((struct completion *)(evtp->evt_arg2));
break; break;
case LPFC_EVT_KILL: case LPFC_EVT_KILL:
if (phba->hba_state >= LPFC_LINK_DOWN) lpfc_offline(phba);
lpfc_offline(phba);
*(int *)(evtp->evt_arg1) *(int *)(evtp->evt_arg1)
= (phba->stopped) ? 0 : lpfc_sli_brdkill(phba); = (phba->stopped) ? 0 : lpfc_sli_brdkill(phba);
lpfc_unblock_mgmt_io(phba);
complete((struct completion *)(evtp->evt_arg2)); complete((struct completion *)(evtp->evt_arg2));
break; break;
} }
......
...@@ -549,12 +549,15 @@ lpfc_handle_eratt(struct lpfc_hba * phba) ...@@ -549,12 +549,15 @@ lpfc_handle_eratt(struct lpfc_hba * phba)
* There was a firmware error. Take the hba offline and then * There was a firmware error. Take the hba offline and then
* attempt to restart it. * attempt to restart it.
*/ */
lpfc_offline_prep(phba);
lpfc_offline(phba); lpfc_offline(phba);
lpfc_sli_brdrestart(phba); lpfc_sli_brdrestart(phba);
if (lpfc_online(phba) == 0) { /* Initialize the HBA */ if (lpfc_online(phba) == 0) { /* Initialize the HBA */
mod_timer(&phba->fc_estabtmo, jiffies + HZ * 60); mod_timer(&phba->fc_estabtmo, jiffies + HZ * 60);
lpfc_unblock_mgmt_io(phba);
return; return;
} }
lpfc_unblock_mgmt_io(phba);
} else { } else {
/* The if clause above forces this code path when the status /* The if clause above forces this code path when the status
* failure is a value other than FFER6. Do not call the offline * failure is a value other than FFER6. Do not call the offline
...@@ -572,7 +575,9 @@ lpfc_handle_eratt(struct lpfc_hba * phba) ...@@ -572,7 +575,9 @@ lpfc_handle_eratt(struct lpfc_hba * phba)
SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX); SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
psli->sli_flag &= ~LPFC_SLI2_ACTIVE; psli->sli_flag &= ~LPFC_SLI2_ACTIVE;
lpfc_offline_prep(phba);
lpfc_offline(phba); lpfc_offline(phba);
lpfc_unblock_mgmt_io(phba);
phba->hba_state = LPFC_HBA_ERROR; phba->hba_state = LPFC_HBA_ERROR;
lpfc_hba_down_post(phba); lpfc_hba_down_post(phba);
} }
...@@ -1286,55 +1291,87 @@ lpfc_online(struct lpfc_hba * phba) ...@@ -1286,55 +1291,87 @@ lpfc_online(struct lpfc_hba * phba)
"%d:0458 Bring Adapter online\n", "%d:0458 Bring Adapter online\n",
phba->brd_no); phba->brd_no);
if (!lpfc_sli_queue_setup(phba)) lpfc_block_mgmt_io(phba);
if (!lpfc_sli_queue_setup(phba)) {
lpfc_unblock_mgmt_io(phba);
return 1; return 1;
}
if (lpfc_sli_hba_setup(phba)) /* Initialize the HBA */ if (lpfc_sli_hba_setup(phba)) { /* Initialize the HBA */
lpfc_unblock_mgmt_io(phba);
return 1; return 1;
}
spin_lock_irq(phba->host->host_lock); spin_lock_irq(phba->host->host_lock);
phba->fc_flag &= ~FC_OFFLINE_MODE; phba->fc_flag &= ~FC_OFFLINE_MODE;
spin_unlock_irq(phba->host->host_lock); spin_unlock_irq(phba->host->host_lock);
lpfc_unblock_mgmt_io(phba);
return 0; return 0;
} }
int void
lpfc_offline(struct lpfc_hba * phba) lpfc_block_mgmt_io(struct lpfc_hba * phba)
{ {
struct lpfc_sli_ring *pring;
struct lpfc_sli *psli;
unsigned long iflag; unsigned long iflag;
int i;
int cnt = 0;
if (!phba) spin_lock_irqsave(phba->host->host_lock, iflag);
return 0; phba->fc_flag |= FC_BLOCK_MGMT_IO;
spin_unlock_irqrestore(phba->host->host_lock, iflag);
}
void
lpfc_unblock_mgmt_io(struct lpfc_hba * phba)
{
unsigned long iflag;
spin_lock_irqsave(phba->host->host_lock, iflag);
phba->fc_flag &= ~FC_BLOCK_MGMT_IO;
spin_unlock_irqrestore(phba->host->host_lock, iflag);
}
void
lpfc_offline_prep(struct lpfc_hba * phba)
{
struct lpfc_nodelist *ndlp, *next_ndlp;
struct list_head *listp, *node_list[7];
int i;
if (phba->fc_flag & FC_OFFLINE_MODE) if (phba->fc_flag & FC_OFFLINE_MODE)
return 0; return;
psli = &phba->sli; lpfc_block_mgmt_io(phba);
lpfc_linkdown(phba); lpfc_linkdown(phba);
lpfc_sli_flush_mbox_queue(phba);
for (i = 0; i < psli->num_rings; i++) { /* Issue an unreg_login to all nodes */
pring = &psli->ring[i]; node_list[0] = &phba->fc_npr_list; /* MUST do this list first */
/* The linkdown event takes 30 seconds to timeout. */ node_list[1] = &phba->fc_nlpmap_list;
while (pring->txcmplq_cnt) { node_list[2] = &phba->fc_nlpunmap_list;
msleep(10); node_list[3] = &phba->fc_prli_list;
if (cnt++ > 3000) { node_list[4] = &phba->fc_reglogin_list;
lpfc_printf_log(phba, node_list[5] = &phba->fc_adisc_list;
KERN_WARNING, LOG_INIT, node_list[6] = &phba->fc_plogi_list;
"%d:0466 Outstanding IO when " for (i = 0; i < 7; i++) {
"bringing Adapter offline\n", listp = node_list[i];
phba->brd_no); if (list_empty(listp))
break; continue;
}
} list_for_each_entry_safe(ndlp, next_ndlp, listp, nlp_listp)
lpfc_unreg_rpi(phba, ndlp);
} }
lpfc_sli_flush_mbox_queue(phba);
}
void
lpfc_offline(struct lpfc_hba * phba)
{
unsigned long iflag;
if (phba->fc_flag & FC_OFFLINE_MODE)
return;
/* stop all timers associated with this hba */ /* stop all timers associated with this hba */
lpfc_stop_timer(phba); lpfc_stop_timer(phba);
...@@ -1354,7 +1391,6 @@ lpfc_offline(struct lpfc_hba * phba) ...@@ -1354,7 +1391,6 @@ lpfc_offline(struct lpfc_hba * phba)
spin_lock_irqsave(phba->host->host_lock, iflag); spin_lock_irqsave(phba->host->host_lock, iflag);
phba->fc_flag |= FC_OFFLINE_MODE; phba->fc_flag |= FC_OFFLINE_MODE;
spin_unlock_irqrestore(phba->host->host_lock, iflag); spin_unlock_irqrestore(phba->host->host_lock, iflag);
return 0;
} }
/****************************************************************************** /******************************************************************************
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment