[SCSI] lpfc 8.3.0 : Add support for Power Management Suspend/Resume operations
Implement lpfc_pci_suspend_one() and lpfc_pci_resume_one() and register them in the pci_driver table. Signed-off-by: James Smart <James.Smart@emulex.com> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
This commit is contained in:
Родитель
ddcc50f0f3
Коммит
3a55b5327b
|
@ -585,20 +585,25 @@ lpfc_do_work(void *p)
|
|||
set_user_nice(current, -20);
|
||||
phba->data_flags = 0;
|
||||
|
||||
while (1) {
|
||||
while (!kthread_should_stop()) {
|
||||
/* wait and check worker queue activities */
|
||||
rc = wait_event_interruptible(phba->work_waitq,
|
||||
(test_and_clear_bit(LPFC_DATA_READY,
|
||||
&phba->data_flags)
|
||||
|| kthread_should_stop()));
|
||||
BUG_ON(rc);
|
||||
|
||||
if (kthread_should_stop())
|
||||
/* Signal wakeup shall terminate the worker thread */
|
||||
if (rc) {
|
||||
lpfc_printf_log(phba, KERN_ERR, LOG_ELS,
|
||||
"0433 Wakeup on signal: rc=x%x\n", rc);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Attend pending lpfc data processing */
|
||||
lpfc_work_done(phba);
|
||||
}
|
||||
phba->worker_thread = NULL;
|
||||
lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
|
||||
"0432 Worker thread stopped.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -2708,7 +2708,7 @@ out:
|
|||
* @pdev: pointer to PCI device
|
||||
*
|
||||
* This routine is to be registered to the kernel's PCI subsystem. When an
|
||||
* Emulex HBA is removed from PCI bus. It perform all the necessary cleanup
|
||||
* Emulex HBA is removed from PCI bus, it performs all the necessary cleanup
|
||||
* for the HBA device to be removed from the PCI subsystem properly.
|
||||
**/
|
||||
static void __devexit
|
||||
|
@ -2784,6 +2784,111 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
|
|||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
/**
|
||||
* lpfc_pci_suspend_one: lpfc PCI func to suspend device for power management.
|
||||
* @pdev: pointer to PCI device
|
||||
* @msg: power management message
|
||||
*
|
||||
* This routine is to be registered to the kernel's PCI subsystem to support
|
||||
* system Power Management (PM). When PM invokes this method, it quiesces the
|
||||
* device by stopping the driver's worker thread for the device, turning off
|
||||
* device's interrupt and DMA, and bring the device offline. Note that as the
|
||||
* driver implements the minimum PM requirements to a power-aware driver's PM
|
||||
* support for suspend/resume -- all the possible PM messages (SUSPEND,
|
||||
* HIBERNATE, FREEZE) to the suspend() method call will be treated as SUSPEND
|
||||
* and the driver will fully reinitialize its device during resume() method
|
||||
* call, the driver will set device to PCI_D3hot state in PCI config space
|
||||
* instead of setting it according to the @msg provided by the PM.
|
||||
*
|
||||
* Return code
|
||||
* 0 - driver suspended the device
|
||||
* Error otherwise
|
||||
**/
|
||||
static int
|
||||
lpfc_pci_suspend_one(struct pci_dev *pdev, pm_message_t msg)
|
||||
{
|
||||
struct Scsi_Host *shost = pci_get_drvdata(pdev);
|
||||
struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
|
||||
|
||||
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
|
||||
"0473 PCI device Power Management suspend.\n");
|
||||
|
||||
/* Bring down the device */
|
||||
lpfc_offline_prep(phba);
|
||||
lpfc_offline(phba);
|
||||
kthread_stop(phba->worker_thread);
|
||||
|
||||
/* Disable interrupt from device */
|
||||
lpfc_disable_intr(phba);
|
||||
|
||||
/* Save device state to PCI config space */
|
||||
pci_save_state(pdev);
|
||||
pci_set_power_state(pdev, PCI_D3hot);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* lpfc_pci_resume_one: lpfc PCI func to resume device for power management.
|
||||
* @pdev: pointer to PCI device
|
||||
*
|
||||
* This routine is to be registered to the kernel's PCI subsystem to support
|
||||
* system Power Management (PM). When PM invokes this method, it restores
|
||||
* the device's PCI config space state and fully reinitializes the device
|
||||
* and brings it online. Note that as the driver implements the minimum PM
|
||||
* requirements to a power-aware driver's PM for suspend/resume -- all
|
||||
* the possible PM messages (SUSPEND, HIBERNATE, FREEZE) to the suspend()
|
||||
* method call will be treated as SUSPEND and the driver will fully
|
||||
* reinitialize its device during resume() method call, the device will be
|
||||
* set to PCI_D0 directly in PCI config space before restoring the state.
|
||||
*
|
||||
* Return code
|
||||
* 0 - driver suspended the device
|
||||
* Error otherwise
|
||||
**/
|
||||
static int
|
||||
lpfc_pci_resume_one(struct pci_dev *pdev)
|
||||
{
|
||||
struct Scsi_Host *shost = pci_get_drvdata(pdev);
|
||||
struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
|
||||
int error;
|
||||
|
||||
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
|
||||
"0452 PCI device Power Management resume.\n");
|
||||
|
||||
/* Restore device state from PCI config space */
|
||||
pci_set_power_state(pdev, PCI_D0);
|
||||
pci_restore_state(pdev);
|
||||
if (pdev->is_busmaster)
|
||||
pci_set_master(pdev);
|
||||
|
||||
/* Startup the kernel thread for this host adapter. */
|
||||
phba->worker_thread = kthread_run(lpfc_do_work, phba,
|
||||
"lpfc_worker_%d", phba->brd_no);
|
||||
if (IS_ERR(phba->worker_thread)) {
|
||||
error = PTR_ERR(phba->worker_thread);
|
||||
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
||||
"0434 PM resume failed to start worker "
|
||||
"thread: error=x%x.\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Enable interrupt from device */
|
||||
error = lpfc_enable_intr(phba);
|
||||
if (error) {
|
||||
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
||||
"0430 PM resume Failed to enable interrupt: "
|
||||
"error=x%x.\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Restart HBA and bring it online */
|
||||
lpfc_sli_brdrestart(phba);
|
||||
lpfc_online(phba);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* lpfc_io_error_detected: Driver method for handling PCI I/O error detected.
|
||||
* @pdev: pointer to PCI device.
|
||||
|
@ -3036,6 +3141,8 @@ static struct pci_driver lpfc_driver = {
|
|||
.id_table = lpfc_id_table,
|
||||
.probe = lpfc_pci_probe_one,
|
||||
.remove = __devexit_p(lpfc_pci_remove_one),
|
||||
.suspend = lpfc_pci_suspend_one,
|
||||
.resume = lpfc_pci_resume_one,
|
||||
.err_handler = &lpfc_err_handler,
|
||||
};
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче