[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:
parent
ddcc50f0f3
commit
3a55b5327b
|
@ -585,20 +585,25 @@ lpfc_do_work(void *p)
|
||||||
set_user_nice(current, -20);
|
set_user_nice(current, -20);
|
||||||
phba->data_flags = 0;
|
phba->data_flags = 0;
|
||||||
|
|
||||||
while (1) {
|
while (!kthread_should_stop()) {
|
||||||
/* wait and check worker queue activities */
|
/* wait and check worker queue activities */
|
||||||
rc = wait_event_interruptible(phba->work_waitq,
|
rc = wait_event_interruptible(phba->work_waitq,
|
||||||
(test_and_clear_bit(LPFC_DATA_READY,
|
(test_and_clear_bit(LPFC_DATA_READY,
|
||||||
&phba->data_flags)
|
&phba->data_flags)
|
||||||
|| kthread_should_stop()));
|
|| kthread_should_stop()));
|
||||||
BUG_ON(rc);
|
/* Signal wakeup shall terminate the worker thread */
|
||||||
|
if (rc) {
|
||||||
if (kthread_should_stop())
|
lpfc_printf_log(phba, KERN_ERR, LOG_ELS,
|
||||||
|
"0433 Wakeup on signal: rc=x%x\n", rc);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/* Attend pending lpfc data processing */
|
/* Attend pending lpfc data processing */
|
||||||
lpfc_work_done(phba);
|
lpfc_work_done(phba);
|
||||||
}
|
}
|
||||||
|
phba->worker_thread = NULL;
|
||||||
|
lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
|
||||||
|
"0432 Worker thread stopped.\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2708,7 +2708,7 @@ out:
|
||||||
* @pdev: pointer to PCI device
|
* @pdev: pointer to PCI device
|
||||||
*
|
*
|
||||||
* This routine is to be registered to the kernel's PCI subsystem. When an
|
* 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.
|
* for the HBA device to be removed from the PCI subsystem properly.
|
||||||
**/
|
**/
|
||||||
static void __devexit
|
static void __devexit
|
||||||
|
@ -2784,6 +2784,111 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
|
||||||
pci_disable_device(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.
|
* lpfc_io_error_detected: Driver method for handling PCI I/O error detected.
|
||||||
* @pdev: pointer to PCI device.
|
* @pdev: pointer to PCI device.
|
||||||
|
@ -3036,6 +3141,8 @@ static struct pci_driver lpfc_driver = {
|
||||||
.id_table = lpfc_id_table,
|
.id_table = lpfc_id_table,
|
||||||
.probe = lpfc_pci_probe_one,
|
.probe = lpfc_pci_probe_one,
|
||||||
.remove = __devexit_p(lpfc_pci_remove_one),
|
.remove = __devexit_p(lpfc_pci_remove_one),
|
||||||
|
.suspend = lpfc_pci_suspend_one,
|
||||||
|
.resume = lpfc_pci_resume_one,
|
||||||
.err_handler = &lpfc_err_handler,
|
.err_handler = &lpfc_err_handler,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue