]> bbs.cooldavid.org Git - net-next-2.6.git/commitdiff
[SCSI] lpfc 8.2.8 : Add support for PCI-EEH permanent disabling
authorJames Smart <James.Smart@Emulex.Com>
Mon, 25 Aug 2008 01:50:11 +0000 (21:50 -0400)
committerJames Bottomley <James.Bottomley@HansenPartnership.com>
Mon, 13 Oct 2008 13:28:54 +0000 (09:28 -0400)
Add support for PCI-EEH permanent-disabling a device via lpfc_pci_remove_one()

Signed-off-by: James Smart <james.smart@emulex.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
drivers/scsi/lpfc/lpfc_crtn.h
drivers/scsi/lpfc/lpfc_hbadisc.c
drivers/scsi/lpfc/lpfc_init.c
drivers/scsi/lpfc/lpfc_scsi.c
drivers/scsi/lpfc/lpfc_sli.c

index 189ce9f8a7b1cf87b1b6b032a7074e35df3f3c76..495afd06936b1a851573c394139dc1e9e0c750ae 100644 (file)
@@ -199,6 +199,7 @@ int lpfc_sli_issue_iocb(struct lpfc_hba *, struct lpfc_sli_ring *,
                        struct lpfc_iocbq *, uint32_t);
 void lpfc_sli_pcimem_bcopy(void *, void *, uint32_t);
 void lpfc_sli_abort_iocb_ring(struct lpfc_hba *, struct lpfc_sli_ring *);
+void lpfc_sli_flush_fcp_rings(struct lpfc_hba *);
 int lpfc_sli_ringpostbuf_put(struct lpfc_hba *, struct lpfc_sli_ring *,
                             struct lpfc_dmabuf *);
 struct lpfc_dmabuf *lpfc_sli_ringpostbuf_get(struct lpfc_hba *,
@@ -290,6 +291,7 @@ void lpfc_unblock_fabric_iocbs(struct lpfc_hba *);
 void lpfc_adjust_queue_depth(struct lpfc_hba *);
 void lpfc_ramp_down_queue_handler(struct lpfc_hba *);
 void lpfc_ramp_up_queue_handler(struct lpfc_hba *);
+void lpfc_scsi_dev_block(struct lpfc_hba *);
 
 #define ScsiResult(host_code, scsi_code) (((host_code) << 16) | scsi_code)
 #define HBA_EVENT_RSCN                   5
index b4ef836235328f2a1a2ba84d4e96d7526f3562af..897ef7d7a8e9b52dabdf4a14560aa841ac1f2df2 100644 (file)
@@ -237,8 +237,6 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp)
                lpfc_sli_abort_iocb(vport, &phba->sli.ring[phba->sli.fcp_ring],
                                    ndlp->nlp_sid, 0, LPFC_CTX_TGT);
        }
-       if (vport->load_flag & FC_UNLOADING)
-               warn_on = 0;
 
        if (warn_on) {
                lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
index 41a8c13e6950fa477a48a115aa88023f44be78ff..333166b1790856c22eeada9f0549acc36c2e504a 100644 (file)
@@ -2661,8 +2661,15 @@ static pci_ers_result_t lpfc_io_error_detected(struct pci_dev *pdev,
        struct lpfc_sli *psli = &phba->sli;
        struct lpfc_sli_ring  *pring;
 
-       if (state == pci_channel_io_perm_failure)
+       if (state == pci_channel_io_perm_failure) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0472 PCI channel I/O permanent failure\n");
+               /* Block all SCSI devices' I/Os on the host */
+               lpfc_scsi_dev_block(phba);
+               /* Clean up all driver's outstanding SCSI I/Os */
+               lpfc_sli_flush_fcp_rings(phba);
                return PCI_ERS_RESULT_DISCONNECT;
+       }
 
        pci_disable_device(pdev);
        /*
index b73968b2b8b4b39b65eabf95fe43a09f7e1c0b33..3606b7098fc1f6f6e63e373450a806a5dcc4c30a 100644 (file)
@@ -183,6 +183,35 @@ lpfc_ramp_up_queue_handler(struct lpfc_hba *phba)
        atomic_set(&phba->num_cmd_success, 0);
 }
 
+/**
+ * lpfc_scsi_dev_block: set all scsi hosts to block state.
+ * @phba: Pointer to HBA context object.
+ *
+ * This function walks vport list and set each SCSI host to block state
+ * by invoking fc_remote_port_delete() routine. This function is invoked
+ * with EEH when device's PCI slot has been permanently disabled.
+ **/
+void
+lpfc_scsi_dev_block(struct lpfc_hba *phba)
+{
+       struct lpfc_vport **vports;
+       struct Scsi_Host  *shost;
+       struct scsi_device *sdev;
+       struct fc_rport *rport;
+       int i;
+
+       vports = lpfc_create_vport_work_array(phba);
+       if (vports != NULL)
+               for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
+                       shost = lpfc_shost_from_vport(vports[i]);
+                       shost_for_each_device(sdev, shost) {
+                               rport = starget_to_rport(scsi_target(sdev));
+                               fc_remote_port_delete(rport);
+                       }
+               }
+       lpfc_destroy_vport_work_array(phba, vports);
+}
+
 /*
  * This routine allocates a scsi buffer, which contains all the necessary
  * information needed to initiate a SCSI I/O.  The non-DMAable buffer region
index 857bc0a57c4788ec815e80f9b2b1df0f012d27fb..1812e18246d549bf856b1b92c793185068755c4f 100644 (file)
@@ -2376,6 +2376,70 @@ lpfc_sli_abort_iocb_ring(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
        }
 }
 
+/**
+ * lpfc_sli_flush_fcp_rings: flush all iocbs in the fcp ring.
+ * @phba: Pointer to HBA context object.
+ *
+ * This function flushes all iocbs in the fcp ring and frees all the iocb
+ * objects in txq and txcmplq. This function will not issue abort iocbs
+ * for all the iocb commands in txcmplq, they will just be returned with
+ * IOERR_SLI_DOWN. This function is invoked with EEH when device's PCI
+ * slot has been permanently disabled.
+ **/
+void
+lpfc_sli_flush_fcp_rings(struct lpfc_hba *phba)
+{
+       LIST_HEAD(txq);
+       LIST_HEAD(txcmplq);
+       struct lpfc_iocbq *iocb;
+       IOCB_t *cmd = NULL;
+       struct lpfc_sli *psli = &phba->sli;
+       struct lpfc_sli_ring  *pring;
+
+       /* Currently, only one fcp ring */
+       pring = &psli->ring[psli->fcp_ring];
+
+       spin_lock_irq(&phba->hbalock);
+       /* Retrieve everything on txq */
+       list_splice_init(&pring->txq, &txq);
+       pring->txq_cnt = 0;
+
+       /* Retrieve everything on the txcmplq */
+       list_splice_init(&pring->txcmplq, &txcmplq);
+       pring->txcmplq_cnt = 0;
+       spin_unlock_irq(&phba->hbalock);
+
+       /* Flush the txq */
+       while (!list_empty(&txq)) {
+               iocb = list_get_first(&txq, struct lpfc_iocbq, list);
+               cmd = &iocb->iocb;
+               list_del_init(&iocb->list);
+
+               if (!iocb->iocb_cmpl)
+                       lpfc_sli_release_iocbq(phba, iocb);
+               else {
+                       cmd->ulpStatus = IOSTAT_LOCAL_REJECT;
+                       cmd->un.ulpWord[4] = IOERR_SLI_DOWN;
+                       (iocb->iocb_cmpl) (phba, iocb, iocb);
+               }
+       }
+
+       /* Flush the txcmpq */
+       while (!list_empty(&txcmplq)) {
+               iocb = list_get_first(&txcmplq, struct lpfc_iocbq, list);
+               cmd = &iocb->iocb;
+               list_del_init(&iocb->list);
+
+               if (!iocb->iocb_cmpl)
+                       lpfc_sli_release_iocbq(phba, iocb);
+               else {
+                       cmd->ulpStatus = IOSTAT_LOCAL_REJECT;
+                       cmd->un.ulpWord[4] = IOERR_SLI_DOWN;
+                       (iocb->iocb_cmpl) (phba, iocb, iocb);
+               }
+       }
+}
+
 /**
  * lpfc_sli_brdready: Check for host status bits.
  * @phba: Pointer to HBA context object.