[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#853968: linux-image-3.16.0-4-amd64: 'ip link set dev $interface down' will not set PHY power off for Intel X540 NIC



Package: src:linux
Version: 3.16.39-1
Severity: important

Dear Maintainer,

Please consider backporting upstream commit 961fac889c0f [1], fixing setting PHY power off when issuing 'ip link set dev $interface down' with Intel X540.

When bringing an ixgbe interface down with 'ip link set dev $interface down' on 3.16 and the underlying NIC port is copper, although link is in 'state DOWN' for the operating system, PHY power is still on.

In other words a switch on the other side of the server's port considers physical link is up. This can cause subtle connectivity problems, e.g. in case port is member of LACP bond and configured as 'force-up' or 'no-suspend-individual' from the switch side.

Applying a slightly modified version of 961fac889c0f on top of 3.16.39 fixes the problem for us.

The aforementioned upstream patch won't apply as is against 3.16.19, yet minor changes are needed. For clarity we provide the debian patch we applied and fixed the problem [2].

Additional information can be provided if needed.
Thanks.


[1]: https://github.com/torvalds/linux/commit/961fac889c0f2e1930092b6de00043cdd1cb2942


[2]:


Description: ixgbe: Add a PHY power state method
 This new method will control the PHY power state. You pass in the
 state you wish to change to (ether on or off).  For cases where this
 method is not used the current PHY power state behavior is maintained.

Signed-off-by: Don Skidmore <donald.c.skidmore@intel.com>
Origin: 961fac889c0f2e1930092b6de00043cdd1cb2942

---

--- linux-3.16.39.orig/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c
+++ linux-3.16.39/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c
@@ -1346,7 +1346,7 @@ static struct ixgbe_phy_operations phy_o
 	.setup_link_speed	= &ixgbe_setup_phy_link_speed_generic,
 	.read_i2c_sff8472	= &ixgbe_read_i2c_sff8472_82598,
 	.read_i2c_eeprom	= &ixgbe_read_i2c_eeprom_82598,
-	.check_overtemp   = &ixgbe_tn_check_overtemp,
+	.check_overtemp		= &ixgbe_tn_check_overtemp,
 };

 struct ixgbe_info ixgbe_82598_info = {
--- linux-3.16.39.orig/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ linux-3.16.39/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -4809,6 +4809,9 @@ static void ixgbe_up_complete(struct ixg
 	if (hw->mac.ops.enable_tx_laser)
 		hw->mac.ops.enable_tx_laser(hw);

+	if (hw->phy.ops.set_phy_power)
+		hw->phy.ops.set_phy_power(hw, true);
+
 	smp_mb__before_atomic();
 	clear_bit(__IXGBE_DOWN, &adapter->state);
 	ixgbe_napi_enable_all(adapter);
@@ -4928,6 +4931,13 @@ void ixgbe_reset(struct ixgbe_adapter *a

 	if (test_bit(__IXGBE_PTP_RUNNING, &adapter->state))
 		ixgbe_ptp_reset(adapter);
+
+	if (hw->phy.ops.set_phy_power) {
+		if (!netif_running(adapter->netdev) && !adapter->wol)
+			hw->phy.ops.set_phy_power(hw, false);
+		else
+			hw->phy.ops.set_phy_power(hw, true);
+	}
 }

 /**
@@ -5600,6 +5610,7 @@ static int ixgbe_change_mtu(struct net_d
 static int ixgbe_open(struct net_device *netdev)
 {
 	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+	struct ixgbe_hw *hw = &adapter->hw;
 	int err, queues;

 	/* disallow open during test */
@@ -5653,6 +5664,8 @@ err_set_queues:
 	ixgbe_free_irq(adapter);
 err_req_irq:
 	ixgbe_free_all_rx_resources(adapter);
+	if (hw->phy.ops.set_phy_power && !adapter->wol)
+		hw->phy.ops.set_phy_power(&adapter->hw, false);
 err_setup_rx:
 	ixgbe_free_all_tx_resources(adapter);
 err_setup_tx:
@@ -5811,6 +5824,8 @@ static int __ixgbe_shutdown(struct pci_d
 	}

 	*enable_wake = !!wufc;
+	if (hw->phy.ops.set_phy_power && !*enable_wake)
+		hw->phy.ops.set_phy_power(hw, false);

 	ixgbe_release_hw_control(adapter);

--- linux-3.16.39.orig/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c
+++ linux-3.16.39/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c
@@ -2055,3 +2055,36 @@ s32 ixgbe_tn_check_overtemp(struct ixgbe
 out:
 	return status;
 }
+
+/** ixgbe_set_copper_phy_power - Control power for copper phy
+ *  @hw: pointer to hardware structure
+ *  @on: true for on, false for off
+ **/
+s32 ixgbe_set_copper_phy_power(struct ixgbe_hw *hw, bool on)
+{
+	u32 status;
+	u16 reg;
+
+	/* Bail if we don't have copper phy */
+	if (hw->mac.ops.get_media_type(hw) != ixgbe_media_type_copper)
+		return 0;
+
+	status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_VENDOR_SPECIFIC_1_CONTROL,
+				      IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
+				      &reg);
+	if (status)
+		return status;
+
+	if (on) {
+		reg &= ~IXGBE_MDIO_PHY_SET_LOW_POWER_MODE;
+	} else {
+		if (ixgbe_check_reset_blocked(hw))
+			return 0;
+		reg |= IXGBE_MDIO_PHY_SET_LOW_POWER_MODE;
+	}
+
+	status = hw->phy.ops.write_reg(hw, IXGBE_MDIO_VENDOR_SPECIFIC_1_CONTROL,
+				       IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
+				       reg);
+	return status;
+}
--- linux-3.16.39.orig/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h
+++ linux-3.16.39/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h
@@ -141,6 +141,7 @@ s32 ixgbe_get_phy_firmware_version_gener
 					   u16 *firmware_version);

 s32 ixgbe_reset_phy_nl(struct ixgbe_hw *hw);
+s32 ixgbe_set_copper_phy_power(struct ixgbe_hw *hw, bool on);
 s32 ixgbe_identify_module_generic(struct ixgbe_hw *hw);
 s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw);
 s32 ixgbe_get_sfp_init_sequence_offsets(struct ixgbe_hw *hw,
--- linux-3.16.39.orig/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h
+++ linux-3.16.39/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h
@@ -1133,6 +1133,9 @@ struct ixgbe_thermal_sensor_data {
#define IXGBE_MDIO_PMA_PMD_SDA_SCL_DATA 0xC30B /* PHY_XS SDA/SCL Data Reg */ #define IXGBE_MDIO_PMA_PMD_SDA_SCL_STAT 0xC30C /* PHY_XS SDA/SCL Status Reg */

+#define IXGBE_MDIO_PHY_SET_LOW_POWER_MODE 0x0800 /* Set low power mode */
+#define IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE   0x1E   /* Device 30 */
+
 /* MII clause 22/28 definitions */
#define IXGBE_MII_AUTONEG_VENDOR_PROVISION_1_REG 0xC400 /* 1G Provisioning 1 */ #define IXGBE_MII_AUTONEG_XNP_TX_REG 0x17 /* 1G XNP Transmit */
@@ -2942,6 +2945,7 @@ struct ixgbe_phy_operations {
 	s32 (*read_i2c_eeprom)(struct ixgbe_hw *, u8 , u8 *);
 	s32 (*write_i2c_eeprom)(struct ixgbe_hw *, u8, u8);
 	s32 (*check_overtemp)(struct ixgbe_hw *);
+	s32 (*set_phy_power)(struct ixgbe_hw *, bool on);
 };

 struct ixgbe_eeprom_info {
--- linux-3.16.39.orig/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c
+++ linux-3.16.39/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c
@@ -884,6 +884,7 @@ static struct ixgbe_phy_operations phy_o
 	.read_i2c_eeprom        = &ixgbe_read_i2c_eeprom_generic,
 	.write_i2c_eeprom       = &ixgbe_write_i2c_eeprom_generic,
 	.check_overtemp         = &ixgbe_tn_check_overtemp,
+	.set_phy_power          = &ixgbe_set_copper_phy_power,
 	.get_firmware_version   = &ixgbe_get_phy_firmware_version_generic,
 };


Reply to: