From: Stefan Assmann <sassmann@redhat.com> Date: Wed, 18 Nov 2009 12:04:12 -0500 Subject: [net] igb: fix kexec with igb controller Message-id: <4B03E2BC.3050705@redhat.com> Patchwork-id: 21412 O-Subject: [RHEL 5.5 PATCH v2] igb: Fix kexec with igb Bugzilla: 527424 RH-Acked-by: Neil Horman <nhorman@redhat.com> RH-Acked-by: David S. Miller <davem@redhat.com> RH-Acked-by: Andy Gospodarek <gospo@redhat.com> RH-Acked-by: Danny Feng <dfeng@redhat.com> Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=527424 Description: Yinghai Lu found one system with 82575EB where, in the kernel that is kexeced, probe igb failed with -2, the reason being that the adapter could not be brought back from D3 by the kexec kernel, most probably due to quirky hardware (it looks like the same behavior happened on forcedeth). Prevent igb from putting the adapter into D3 during shutdown except when we going to power off the system. For this purpose, seperate igb_shutdown() from igb_suspend() and use the appropriate PCI PM callbacks in both of them. Replaced calls for pci_wake_from_d3 and pci_prepare_to_sleep with implementation from earlier post of the patch (kexec with igb rev. 2) because of lacking infrastructure. Addressed comments from dfeng. This version also removes the call to device_set_wakeup_enable in igb_probe as this seems to improve link detection after kexec/WoL. Upstream Status: http://git.kernel.org/linus/3fe7c4c9dca4fbbff92eb61a660690dad7029ec3 http://kerneltrap.org/mailarchive/linux-netdev/2009/7/22/6245123 Brew Build: https://brewweb.devel.redhat.com/taskinfo?taskID=2079676 Test Status: Successfully tested-by-me, igb operates normal after kexec. Retested Wake-on-Lan without finding any regressions. Stefan diff --git a/drivers/net/igb/igb_main.c b/drivers/net/igb/igb_main.c index 78945c3..c83cd84 100644 --- a/drivers/net/igb/igb_main.c +++ b/drivers/net/igb/igb_main.c @@ -124,8 +124,8 @@ static inline int igb_set_vf_rlpml(struct igb_adapter *, int, int); static int igb_set_vf_mac(struct igb_adapter *adapter, int, unsigned char *); static void igb_restore_vf_multicasts(struct igb_adapter *adapter); -static int igb_suspend(struct pci_dev *, pm_message_t); #ifdef CONFIG_PM +static int igb_suspend(struct pci_dev *, pm_message_t); static int igb_resume(struct pci_dev *); #endif static void igb_shutdown(struct pci_dev *); @@ -1369,7 +1369,6 @@ static int __devinit igb_probe(struct pci_dev *pdev, /* initialize the wol settings based on the eeprom settings */ adapter->wol = adapter->eeprom_wol; device_init_wakeup(&adapter->pdev->dev, adapter->wol); - device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol); /* reset the hardware with the new settings */ igb_reset(adapter); @@ -4615,7 +4614,7 @@ int igb_set_spd_dplx(struct igb_adapter *adapter, u16 spddplx) return 0; } -static int igb_suspend(struct pci_dev *pdev, pm_message_t state) +static int __igb_shutdown(struct pci_dev *pdev, bool *enable_wake) { struct net_device *netdev = pci_get_drvdata(pdev); struct igb_adapter *adapter = netdev_priv(netdev); @@ -4674,15 +4673,9 @@ static int igb_suspend(struct pci_dev *pdev, pm_message_t state) wr32(E1000_WUFC, 0); } - /* make sure adapter isn't asleep if manageability/wol is enabled */ - if (wufc || adapter->en_mng_pt) { - pci_enable_wake(pdev, PCI_D3hot, 1); - pci_enable_wake(pdev, PCI_D3cold, 1); - } else { + *enable_wake = wufc || adapter->en_mng_pt; + if (!*enable_wake) igb_shutdown_fiber_serdes_link_82575(hw); - pci_enable_wake(pdev, PCI_D3hot, 0); - pci_enable_wake(pdev, PCI_D3cold, 0); - } /* Release control of h/w to f/w. If f/w is AMT enabled, this * would have already happened in close and is redundant. */ @@ -4690,12 +4683,31 @@ static int igb_suspend(struct pci_dev *pdev, pm_message_t state) pci_disable_device(pdev); - pci_set_power_state(pdev, pci_choose_state(pdev, state)); - return 0; } #ifdef CONFIG_PM +static int igb_suspend(struct pci_dev *pdev, pm_message_t state) +{ + int retval; + bool wake; + + retval = __igb_shutdown(pdev, &wake); + if (retval) + return retval; + + if (wake) { + pci_enable_wake(pdev, PCI_D3hot, 1); + pci_enable_wake(pdev, PCI_D3cold, 1); + } else { + pci_enable_wake(pdev, PCI_D3hot, 0); + pci_enable_wake(pdev, PCI_D3cold, 0); + } + pci_set_power_state(pdev, PCI_D3hot); + + return 0; +} + static int igb_resume(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); @@ -4748,7 +4760,20 @@ static int igb_resume(struct pci_dev *pdev) static void igb_shutdown(struct pci_dev *pdev) { - igb_suspend(pdev, PMSG_SUSPEND); + bool wake; + + __igb_shutdown(pdev, &wake); + + if (system_state == SYSTEM_POWER_OFF) { + if (wake) { + pci_enable_wake(pdev, PCI_D3hot, 1); + pci_enable_wake(pdev, PCI_D3cold, 1); + } else { + pci_enable_wake(pdev, PCI_D3hot, 0); + pci_enable_wake(pdev, PCI_D3cold, 0); + } + pci_set_power_state(pdev, PCI_D3hot); + } } #ifdef CONFIG_NET_POLL_CONTROLLER