Commit eb9f6744cbfa97674c13263802259b5aa0034594
Committed by
David S. Miller
1 parent
89c758fa47
Exists in
master
and in
7 other branches
sfc: Implement ethtool reset operation
Refactor efx_reset_down() and efx_reset_up() accordingly. Signed-off-by: Ben Hutchings <bhutchings@solarflare.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Showing 5 changed files with 84 additions and 50 deletions Side-by-side Diff
drivers/net/sfc/efx.c
... | ... | @@ -1754,59 +1754,50 @@ |
1754 | 1754 | rc = efx->type->init(efx); |
1755 | 1755 | if (rc) { |
1756 | 1756 | EFX_ERR(efx, "failed to initialise NIC\n"); |
1757 | - ok = false; | |
1757 | + goto fail; | |
1758 | 1758 | } |
1759 | 1759 | |
1760 | + if (!ok) | |
1761 | + goto fail; | |
1762 | + | |
1760 | 1763 | if (efx->port_initialized && method != RESET_TYPE_INVISIBLE) { |
1761 | - if (ok) { | |
1762 | - rc = efx->phy_op->init(efx); | |
1763 | - if (rc) | |
1764 | - ok = false; | |
1765 | - if (efx->phy_op->reconfigure(efx)) | |
1766 | - EFX_ERR(efx, "could not restore PHY settings\n"); | |
1767 | - } | |
1768 | - if (!ok) | |
1769 | - efx->port_initialized = false; | |
1764 | + rc = efx->phy_op->init(efx); | |
1765 | + if (rc) | |
1766 | + goto fail; | |
1767 | + if (efx->phy_op->reconfigure(efx)) | |
1768 | + EFX_ERR(efx, "could not restore PHY settings\n"); | |
1770 | 1769 | } |
1771 | 1770 | |
1772 | - if (ok) { | |
1773 | - efx->mac_op->reconfigure(efx); | |
1771 | + efx->mac_op->reconfigure(efx); | |
1774 | 1772 | |
1775 | - efx_init_channels(efx); | |
1776 | - } | |
1773 | + efx_init_channels(efx); | |
1777 | 1774 | |
1778 | 1775 | mutex_unlock(&efx->spi_lock); |
1779 | 1776 | mutex_unlock(&efx->mac_lock); |
1780 | 1777 | |
1781 | - if (ok) | |
1782 | - efx_start_all(efx); | |
1778 | + efx_start_all(efx); | |
1779 | + | |
1780 | + return 0; | |
1781 | + | |
1782 | +fail: | |
1783 | + efx->port_initialized = false; | |
1784 | + | |
1785 | + mutex_unlock(&efx->spi_lock); | |
1786 | + mutex_unlock(&efx->mac_lock); | |
1787 | + | |
1783 | 1788 | return rc; |
1784 | 1789 | } |
1785 | 1790 | |
1786 | -/* Reset the NIC as transparently as possible. Do not reset the PHY | |
1787 | - * Note that the reset may fail, in which case the card will be left | |
1788 | - * in a most-probably-unusable state. | |
1791 | +/* Reset the NIC using the specified method. Note that the reset may | |
1792 | + * fail, in which case the card will be left in an unusable state. | |
1789 | 1793 | * |
1790 | - * This function will sleep. You cannot reset from within an atomic | |
1791 | - * state; use efx_schedule_reset() instead. | |
1792 | - * | |
1793 | - * Grabs the rtnl_lock. | |
1794 | + * Caller must hold the rtnl_lock. | |
1794 | 1795 | */ |
1795 | -static int efx_reset(struct efx_nic *efx) | |
1796 | +int efx_reset(struct efx_nic *efx, enum reset_type method) | |
1796 | 1797 | { |
1797 | - enum reset_type method = efx->reset_pending; | |
1798 | - int rc = 0; | |
1798 | + int rc, rc2; | |
1799 | + bool disabled; | |
1799 | 1800 | |
1800 | - /* Serialise with kernel interfaces */ | |
1801 | - rtnl_lock(); | |
1802 | - | |
1803 | - /* If we're not RUNNING then don't reset. Leave the reset_pending | |
1804 | - * flag set so that efx_pci_probe_main will be retried */ | |
1805 | - if (efx->state != STATE_RUNNING) { | |
1806 | - EFX_INFO(efx, "scheduled reset quenched. NIC not RUNNING\n"); | |
1807 | - goto out_unlock; | |
1808 | - } | |
1809 | - | |
1810 | 1801 | EFX_INFO(efx, "resetting (%s)\n", RESET_TYPE(method)); |
1811 | 1802 | |
1812 | 1803 | efx_reset_down(efx, method); |
... | ... | @@ -1814,7 +1805,7 @@ |
1814 | 1805 | rc = efx->type->reset(efx, method); |
1815 | 1806 | if (rc) { |
1816 | 1807 | EFX_ERR(efx, "failed to reset hardware\n"); |
1817 | - goto out_disable; | |
1808 | + goto out; | |
1818 | 1809 | } |
1819 | 1810 | |
1820 | 1811 | /* Allow resets to be rescheduled. */ |
1821 | 1812 | |
1822 | 1813 | |
1823 | 1814 | |
1824 | 1815 | |
... | ... | @@ -1826,25 +1817,22 @@ |
1826 | 1817 | * can respond to requests. */ |
1827 | 1818 | pci_set_master(efx->pci_dev); |
1828 | 1819 | |
1820 | +out: | |
1829 | 1821 | /* Leave device stopped if necessary */ |
1830 | - if (method == RESET_TYPE_DISABLE) { | |
1831 | - efx_reset_up(efx, method, false); | |
1832 | - rc = -EIO; | |
1833 | - } else { | |
1834 | - rc = efx_reset_up(efx, method, true); | |
1822 | + disabled = rc || method == RESET_TYPE_DISABLE; | |
1823 | + rc2 = efx_reset_up(efx, method, !disabled); | |
1824 | + if (rc2) { | |
1825 | + disabled = true; | |
1826 | + if (!rc) | |
1827 | + rc = rc2; | |
1835 | 1828 | } |
1836 | 1829 | |
1837 | -out_disable: | |
1838 | - if (rc) { | |
1830 | + if (disabled) { | |
1839 | 1831 | EFX_ERR(efx, "has been disabled\n"); |
1840 | 1832 | efx->state = STATE_DISABLED; |
1841 | - dev_close(efx->net_dev); | |
1842 | 1833 | } else { |
1843 | 1834 | EFX_LOG(efx, "reset complete\n"); |
1844 | 1835 | } |
1845 | - | |
1846 | -out_unlock: | |
1847 | - rtnl_unlock(); | |
1848 | 1836 | return rc; |
1849 | 1837 | } |
1850 | 1838 | |
1851 | 1839 | |
... | ... | @@ -1853,9 +1841,19 @@ |
1853 | 1841 | */ |
1854 | 1842 | static void efx_reset_work(struct work_struct *data) |
1855 | 1843 | { |
1856 | - struct efx_nic *nic = container_of(data, struct efx_nic, reset_work); | |
1844 | + struct efx_nic *efx = container_of(data, struct efx_nic, reset_work); | |
1857 | 1845 | |
1858 | - efx_reset(nic); | |
1846 | + /* If we're not RUNNING then don't reset. Leave the reset_pending | |
1847 | + * flag set so that efx_pci_probe_main will be retried */ | |
1848 | + if (efx->state != STATE_RUNNING) { | |
1849 | + EFX_INFO(efx, "scheduled reset quenched. NIC not RUNNING\n"); | |
1850 | + return; | |
1851 | + } | |
1852 | + | |
1853 | + rtnl_lock(); | |
1854 | + if (efx_reset(efx, efx->reset_pending)) | |
1855 | + dev_close(efx->net_dev); | |
1856 | + rtnl_unlock(); | |
1859 | 1857 | } |
1860 | 1858 | |
1861 | 1859 | void efx_schedule_reset(struct efx_nic *efx, enum reset_type type) |
drivers/net/sfc/efx.h
... | ... | @@ -71,6 +71,7 @@ |
71 | 71 | extern const struct ethtool_ops efx_ethtool_ops; |
72 | 72 | |
73 | 73 | /* Reset handling */ |
74 | +extern int efx_reset(struct efx_nic *efx, enum reset_type method); | |
74 | 75 | extern void efx_reset_down(struct efx_nic *efx, enum reset_type method); |
75 | 76 | extern int efx_reset_up(struct efx_nic *efx, enum reset_type method, bool ok); |
76 | 77 |
drivers/net/sfc/ethtool.c
... | ... | @@ -754,6 +754,35 @@ |
754 | 754 | return efx->type->set_wol(efx, wol->wolopts); |
755 | 755 | } |
756 | 756 | |
757 | +extern int efx_ethtool_reset(struct net_device *net_dev, u32 *flags) | |
758 | +{ | |
759 | + struct efx_nic *efx = netdev_priv(net_dev); | |
760 | + enum reset_type method; | |
761 | + enum { | |
762 | + ETH_RESET_EFX_INVISIBLE = (ETH_RESET_DMA | ETH_RESET_FILTER | | |
763 | + ETH_RESET_OFFLOAD | ETH_RESET_MAC) | |
764 | + }; | |
765 | + | |
766 | + /* Check for minimal reset flags */ | |
767 | + if ((*flags & ETH_RESET_EFX_INVISIBLE) != ETH_RESET_EFX_INVISIBLE) | |
768 | + return -EINVAL; | |
769 | + *flags ^= ETH_RESET_EFX_INVISIBLE; | |
770 | + method = RESET_TYPE_INVISIBLE; | |
771 | + | |
772 | + if (*flags & ETH_RESET_PHY) { | |
773 | + *flags ^= ETH_RESET_PHY; | |
774 | + method = RESET_TYPE_ALL; | |
775 | + } | |
776 | + | |
777 | + if ((*flags & efx->type->reset_world_flags) == | |
778 | + efx->type->reset_world_flags) { | |
779 | + *flags ^= efx->type->reset_world_flags; | |
780 | + method = RESET_TYPE_WORLD; | |
781 | + } | |
782 | + | |
783 | + return efx_reset(efx, method); | |
784 | +} | |
785 | + | |
757 | 786 | const struct ethtool_ops efx_ethtool_ops = { |
758 | 787 | .get_settings = efx_ethtool_get_settings, |
759 | 788 | .set_settings = efx_ethtool_set_settings, |
... | ... | @@ -784,5 +813,6 @@ |
784 | 813 | .get_ethtool_stats = efx_ethtool_get_stats, |
785 | 814 | .get_wol = efx_ethtool_get_wol, |
786 | 815 | .set_wol = efx_ethtool_set_wol, |
816 | + .reset = efx_ethtool_reset, | |
787 | 817 | }; |
drivers/net/sfc/falcon.c
... | ... | @@ -3305,6 +3305,7 @@ |
3305 | 3305 | .phys_addr_channels = 4, |
3306 | 3306 | .tx_dc_base = 0x130000, |
3307 | 3307 | .rx_dc_base = 0x100000, |
3308 | + .reset_world_flags = ETH_RESET_IRQ, | |
3308 | 3309 | }; |
3309 | 3310 | |
3310 | 3311 | struct efx_nic_type falcon_b0_nic_type = { |
... | ... | @@ -3348,5 +3349,6 @@ |
3348 | 3349 | * channels */ |
3349 | 3350 | .tx_dc_base = 0x130000, |
3350 | 3351 | .rx_dc_base = 0x100000, |
3352 | + .reset_world_flags = ETH_RESET_IRQ, | |
3351 | 3353 | }; |
drivers/net/sfc/net_driver.h
... | ... | @@ -880,6 +880,8 @@ |
880 | 880 | * descriptors |
881 | 881 | * @tx_dc_base: Base address in SRAM of TX queue descriptor caches |
882 | 882 | * @rx_dc_base: Base address in SRAM of RX queue descriptor caches |
883 | + * @reset_world_flags: Flags for additional components covered by | |
884 | + * reset method RESET_TYPE_WORLD | |
883 | 885 | */ |
884 | 886 | struct efx_nic_type { |
885 | 887 | int (*probe)(struct efx_nic *efx); |
... | ... | @@ -915,6 +917,7 @@ |
915 | 917 | unsigned int phys_addr_channels; |
916 | 918 | unsigned int tx_dc_base; |
917 | 919 | unsigned int rx_dc_base; |
920 | + u32 reset_world_flags; | |
918 | 921 | }; |
919 | 922 | |
920 | 923 | /************************************************************************** |