diff options
author | David Decotigny <decot@google.com> | 2011-04-27 14:32:43 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-04-29 17:03:03 -0400 |
commit | 14ad2513ed5b709e566a853f4b515d91c5d83311 (patch) | |
tree | 19ce2e6deefaa0725533612df85c51b99d2513cb | |
parent | fbef7139a8b89a7f49ba1410593ed894b4c8b017 (diff) |
net/igb/e1000/e1000e: more robust ethtool duplex/speed configuration
This makes sure that one cannot request a 99Mbps full-duplex and get a
100Mbps half-duplex configuration in return due to the way the
speed/duplex parameters are handled internally.
Tested: e1000 works
Signed-off-by: David Decotigny <decot@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/e1000/e1000.h | 2 | ||||
-rw-r--r-- | drivers/net/e1000/e1000_ethtool.c | 2 | ||||
-rw-r--r-- | drivers/net/e1000/e1000_main.c | 42 | ||||
-rw-r--r-- | drivers/net/e1000e/ethtool.c | 24 | ||||
-rw-r--r-- | drivers/net/igb/igb.h | 2 | ||||
-rw-r--r-- | drivers/net/igb/igb_ethtool.c | 2 | ||||
-rw-r--r-- | drivers/net/igb/igb_main.c | 23 |
7 files changed, 59 insertions, 38 deletions
diff --git a/drivers/net/e1000/e1000.h b/drivers/net/e1000/e1000.h index a881dd0093b..b1b23ddd4ee 100644 --- a/drivers/net/e1000/e1000.h +++ b/drivers/net/e1000/e1000.h | |||
@@ -349,7 +349,7 @@ extern int e1000_up(struct e1000_adapter *adapter); | |||
349 | extern void e1000_down(struct e1000_adapter *adapter); | 349 | extern void e1000_down(struct e1000_adapter *adapter); |
350 | extern void e1000_reinit_locked(struct e1000_adapter *adapter); | 350 | extern void e1000_reinit_locked(struct e1000_adapter *adapter); |
351 | extern void e1000_reset(struct e1000_adapter *adapter); | 351 | extern void e1000_reset(struct e1000_adapter *adapter); |
352 | extern int e1000_set_spd_dplx(struct e1000_adapter *adapter, u16 spddplx); | 352 | extern int e1000_set_spd_dplx(struct e1000_adapter *adapter, u32 spd, u8 dplx); |
353 | extern int e1000_setup_all_rx_resources(struct e1000_adapter *adapter); | 353 | extern int e1000_setup_all_rx_resources(struct e1000_adapter *adapter); |
354 | extern int e1000_setup_all_tx_resources(struct e1000_adapter *adapter); | 354 | extern int e1000_setup_all_tx_resources(struct e1000_adapter *adapter); |
355 | extern void e1000_free_all_rx_resources(struct e1000_adapter *adapter); | 355 | extern void e1000_free_all_rx_resources(struct e1000_adapter *adapter); |
diff --git a/drivers/net/e1000/e1000_ethtool.c b/drivers/net/e1000/e1000_ethtool.c index 127fef4fce4..4fa727ce837 100644 --- a/drivers/net/e1000/e1000_ethtool.c +++ b/drivers/net/e1000/e1000_ethtool.c | |||
@@ -199,7 +199,7 @@ static int e1000_set_settings(struct net_device *netdev, | |||
199 | ecmd->advertising = hw->autoneg_advertised; | 199 | ecmd->advertising = hw->autoneg_advertised; |
200 | } else { | 200 | } else { |
201 | u32 speed = ethtool_cmd_speed(ecmd); | 201 | u32 speed = ethtool_cmd_speed(ecmd); |
202 | if (e1000_set_spd_dplx(adapter, speed + ecmd->duplex)) { | 202 | if (e1000_set_spd_dplx(adapter, speed, ecmd->duplex)) { |
203 | clear_bit(__E1000_RESETTING, &adapter->flags); | 203 | clear_bit(__E1000_RESETTING, &adapter->flags); |
204 | return -EINVAL; | 204 | return -EINVAL; |
205 | } | 205 | } |
diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index 477e066a1cf..c18cb8e883d 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c | |||
@@ -96,7 +96,6 @@ int e1000_up(struct e1000_adapter *adapter); | |||
96 | void e1000_down(struct e1000_adapter *adapter); | 96 | void e1000_down(struct e1000_adapter *adapter); |
97 | void e1000_reinit_locked(struct e1000_adapter *adapter); | 97 | void e1000_reinit_locked(struct e1000_adapter *adapter); |
98 | void e1000_reset(struct e1000_adapter *adapter); | 98 | void e1000_reset(struct e1000_adapter *adapter); |
99 | int e1000_set_spd_dplx(struct e1000_adapter *adapter, u16 spddplx); | ||
100 | int e1000_setup_all_tx_resources(struct e1000_adapter *adapter); | 99 | int e1000_setup_all_tx_resources(struct e1000_adapter *adapter); |
101 | int e1000_setup_all_rx_resources(struct e1000_adapter *adapter); | 100 | int e1000_setup_all_rx_resources(struct e1000_adapter *adapter); |
102 | void e1000_free_all_tx_resources(struct e1000_adapter *adapter); | 101 | void e1000_free_all_tx_resources(struct e1000_adapter *adapter); |
@@ -4385,7 +4384,6 @@ static int e1000_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, | |||
4385 | struct mii_ioctl_data *data = if_mii(ifr); | 4384 | struct mii_ioctl_data *data = if_mii(ifr); |
4386 | int retval; | 4385 | int retval; |
4387 | u16 mii_reg; | 4386 | u16 mii_reg; |
4388 | u16 spddplx; | ||
4389 | unsigned long flags; | 4387 | unsigned long flags; |
4390 | 4388 | ||
4391 | if (hw->media_type != e1000_media_type_copper) | 4389 | if (hw->media_type != e1000_media_type_copper) |
@@ -4424,17 +4422,18 @@ static int e1000_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, | |||
4424 | hw->autoneg = 1; | 4422 | hw->autoneg = 1; |
4425 | hw->autoneg_advertised = 0x2F; | 4423 | hw->autoneg_advertised = 0x2F; |
4426 | } else { | 4424 | } else { |
4425 | u32 speed; | ||
4427 | if (mii_reg & 0x40) | 4426 | if (mii_reg & 0x40) |
4428 | spddplx = SPEED_1000; | 4427 | speed = SPEED_1000; |
4429 | else if (mii_reg & 0x2000) | 4428 | else if (mii_reg & 0x2000) |
4430 | spddplx = SPEED_100; | 4429 | speed = SPEED_100; |
4431 | else | 4430 | else |
4432 | spddplx = SPEED_10; | 4431 | speed = SPEED_10; |
4433 | spddplx += (mii_reg & 0x100) | 4432 | retval = e1000_set_spd_dplx( |
4434 | ? DUPLEX_FULL : | 4433 | adapter, speed, |
4435 | DUPLEX_HALF; | 4434 | ((mii_reg & 0x100) |
4436 | retval = e1000_set_spd_dplx(adapter, | 4435 | ? DUPLEX_FULL : |
4437 | spddplx); | 4436 | DUPLEX_HALF)); |
4438 | if (retval) | 4437 | if (retval) |
4439 | return retval; | 4438 | return retval; |
4440 | } | 4439 | } |
@@ -4596,20 +4595,24 @@ static void e1000_restore_vlan(struct e1000_adapter *adapter) | |||
4596 | } | 4595 | } |
4597 | } | 4596 | } |
4598 | 4597 | ||
4599 | int e1000_set_spd_dplx(struct e1000_adapter *adapter, u16 spddplx) | 4598 | int e1000_set_spd_dplx(struct e1000_adapter *adapter, u32 spd, u8 dplx) |
4600 | { | 4599 | { |
4601 | struct e1000_hw *hw = &adapter->hw; | 4600 | struct e1000_hw *hw = &adapter->hw; |
4602 | 4601 | ||
4603 | hw->autoneg = 0; | 4602 | hw->autoneg = 0; |
4604 | 4603 | ||
4604 | /* Make sure dplx is at most 1 bit and lsb of speed is not set | ||
4605 | * for the switch() below to work */ | ||
4606 | if ((spd & 1) || (dplx & ~1)) | ||
4607 | goto err_inval; | ||
4608 | |||
4605 | /* Fiber NICs only allow 1000 gbps Full duplex */ | 4609 | /* Fiber NICs only allow 1000 gbps Full duplex */ |
4606 | if ((hw->media_type == e1000_media_type_fiber) && | 4610 | if ((hw->media_type == e1000_media_type_fiber) && |
4607 | spddplx != (SPEED_1000 + DUPLEX_FULL)) { | 4611 | spd != SPEED_1000 && |
4608 | e_err(probe, "Unsupported Speed/Duplex configuration\n"); | 4612 | dplx != DUPLEX_FULL) |
4609 | return -EINVAL; | 4613 | goto err_inval; |
4610 | } | ||
4611 | 4614 | ||
4612 | switch (spddplx) { | 4615 | switch (spd + dplx) { |
4613 | case SPEED_10 + DUPLEX_HALF: | 4616 | case SPEED_10 + DUPLEX_HALF: |
4614 | hw->forced_speed_duplex = e1000_10_half; | 4617 | hw->forced_speed_duplex = e1000_10_half; |
4615 | break; | 4618 | break; |
@@ -4628,10 +4631,13 @@ int e1000_set_spd_dplx(struct e1000_adapter *adapter, u16 spddplx) | |||
4628 | break; | 4631 | break; |
4629 | case SPEED_1000 + DUPLEX_HALF: /* not supported */ | 4632 | case SPEED_1000 + DUPLEX_HALF: /* not supported */ |
4630 | default: | 4633 | default: |
4631 | e_err(probe, "Unsupported Speed/Duplex configuration\n"); | 4634 | goto err_inval; |
4632 | return -EINVAL; | ||
4633 | } | 4635 | } |
4634 | return 0; | 4636 | return 0; |
4637 | |||
4638 | err_inval: | ||
4639 | e_err(probe, "Unsupported Speed/Duplex configuration\n"); | ||
4640 | return -EINVAL; | ||
4635 | } | 4641 | } |
4636 | 4642 | ||
4637 | static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake) | 4643 | static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake) |
diff --git a/drivers/net/e1000e/ethtool.c b/drivers/net/e1000e/ethtool.c index 12f1ee25052..859d0d3af6c 100644 --- a/drivers/net/e1000e/ethtool.c +++ b/drivers/net/e1000e/ethtool.c | |||
@@ -200,20 +200,25 @@ static int e1000_get_settings(struct net_device *netdev, | |||
200 | return 0; | 200 | return 0; |
201 | } | 201 | } |
202 | 202 | ||
203 | static int e1000_set_spd_dplx(struct e1000_adapter *adapter, u16 spddplx) | 203 | static int e1000_set_spd_dplx(struct e1000_adapter *adapter, u32 spd, u8 dplx) |
204 | { | 204 | { |
205 | struct e1000_mac_info *mac = &adapter->hw.mac; | 205 | struct e1000_mac_info *mac = &adapter->hw.mac; |
206 | 206 | ||
207 | mac->autoneg = 0; | 207 | mac->autoneg = 0; |
208 | 208 | ||
209 | /* Make sure dplx is at most 1 bit and lsb of speed is not set | ||
210 | * for the switch() below to work */ | ||
211 | if ((spd & 1) || (dplx & ~1)) | ||
212 | goto err_inval; | ||
213 | |||
209 | /* Fiber NICs only allow 1000 gbps Full duplex */ | 214 | /* Fiber NICs only allow 1000 gbps Full duplex */ |
210 | if ((adapter->hw.phy.media_type == e1000_media_type_fiber) && | 215 | if ((adapter->hw.phy.media_type == e1000_media_type_fiber) && |
211 | spddplx != (SPEED_1000 + DUPLEX_FULL)) { | 216 | spd != SPEED_1000 && |
212 | e_err("Unsupported Speed/Duplex configuration\n"); | 217 | dplx != DUPLEX_FULL) { |
213 | return -EINVAL; | 218 | goto err_inval; |
214 | } | 219 | } |
215 | 220 | ||
216 | switch (spddplx) { | 221 | switch (spd + dplx) { |
217 | case SPEED_10 + DUPLEX_HALF: | 222 | case SPEED_10 + DUPLEX_HALF: |
218 | mac->forced_speed_duplex = ADVERTISE_10_HALF; | 223 | mac->forced_speed_duplex = ADVERTISE_10_HALF; |
219 | break; | 224 | break; |
@@ -232,10 +237,13 @@ static int e1000_set_spd_dplx(struct e1000_adapter *adapter, u16 spddplx) | |||
232 | break; | 237 | break; |
233 | case SPEED_1000 + DUPLEX_HALF: /* not supported */ | 238 | case SPEED_1000 + DUPLEX_HALF: /* not supported */ |
234 | default: | 239 | default: |
235 | e_err("Unsupported Speed/Duplex configuration\n"); | 240 | goto err_inval; |
236 | return -EINVAL; | ||
237 | } | 241 | } |
238 | return 0; | 242 | return 0; |
243 | |||
244 | err_inval: | ||
245 | e_err("Unsupported Speed/Duplex configuration\n"); | ||
246 | return -EINVAL; | ||
239 | } | 247 | } |
240 | 248 | ||
241 | static int e1000_set_settings(struct net_device *netdev, | 249 | static int e1000_set_settings(struct net_device *netdev, |
@@ -272,7 +280,7 @@ static int e1000_set_settings(struct net_device *netdev, | |||
272 | hw->fc.requested_mode = e1000_fc_default; | 280 | hw->fc.requested_mode = e1000_fc_default; |
273 | } else { | 281 | } else { |
274 | u32 speed = ethtool_cmd_speed(ecmd); | 282 | u32 speed = ethtool_cmd_speed(ecmd); |
275 | if (e1000_set_spd_dplx(adapter, speed + ecmd->duplex)) { | 283 | if (e1000_set_spd_dplx(adapter, speed, ecmd->duplex)) { |
276 | clear_bit(__E1000_RESETTING, &adapter->state); | 284 | clear_bit(__E1000_RESETTING, &adapter->state); |
277 | return -EINVAL; | 285 | return -EINVAL; |
278 | } | 286 | } |
diff --git a/drivers/net/igb/igb.h b/drivers/net/igb/igb.h index 1c687e298d5..f4fa4b1751c 100644 --- a/drivers/net/igb/igb.h +++ b/drivers/net/igb/igb.h | |||
@@ -360,7 +360,7 @@ extern int igb_up(struct igb_adapter *); | |||
360 | extern void igb_down(struct igb_adapter *); | 360 | extern void igb_down(struct igb_adapter *); |
361 | extern void igb_reinit_locked(struct igb_adapter *); | 361 | extern void igb_reinit_locked(struct igb_adapter *); |
362 | extern void igb_reset(struct igb_adapter *); | 362 | extern void igb_reset(struct igb_adapter *); |
363 | extern int igb_set_spd_dplx(struct igb_adapter *, u16); | 363 | extern int igb_set_spd_dplx(struct igb_adapter *, u32, u8); |
364 | extern int igb_setup_tx_resources(struct igb_ring *); | 364 | extern int igb_setup_tx_resources(struct igb_ring *); |
365 | extern int igb_setup_rx_resources(struct igb_ring *); | 365 | extern int igb_setup_rx_resources(struct igb_ring *); |
366 | extern void igb_free_tx_resources(struct igb_ring *); | 366 | extern void igb_free_tx_resources(struct igb_ring *); |
diff --git a/drivers/net/igb/igb_ethtool.c b/drivers/net/igb/igb_ethtool.c index 023aa9b1065..6e29634b1fb 100644 --- a/drivers/net/igb/igb_ethtool.c +++ b/drivers/net/igb/igb_ethtool.c | |||
@@ -224,7 +224,7 @@ static int igb_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) | |||
224 | hw->fc.requested_mode = e1000_fc_default; | 224 | hw->fc.requested_mode = e1000_fc_default; |
225 | } else { | 225 | } else { |
226 | u32 speed = ethtool_cmd_speed(ecmd); | 226 | u32 speed = ethtool_cmd_speed(ecmd); |
227 | if (igb_set_spd_dplx(adapter, speed + ecmd->duplex)) { | 227 | if (igb_set_spd_dplx(adapter, speed, ecmd->duplex)) { |
228 | clear_bit(__IGB_RESETTING, &adapter->state); | 228 | clear_bit(__IGB_RESETTING, &adapter->state); |
229 | return -EINVAL; | 229 | return -EINVAL; |
230 | } | 230 | } |
diff --git a/drivers/net/igb/igb_main.c b/drivers/net/igb/igb_main.c index cdfd5727105..ce7838e5582 100644 --- a/drivers/net/igb/igb_main.c +++ b/drivers/net/igb/igb_main.c | |||
@@ -6349,21 +6349,25 @@ static void igb_restore_vlan(struct igb_adapter *adapter) | |||
6349 | } | 6349 | } |
6350 | } | 6350 | } |
6351 | 6351 | ||
6352 | int igb_set_spd_dplx(struct igb_adapter *adapter, u16 spddplx) | 6352 | int igb_set_spd_dplx(struct igb_adapter *adapter, u32 spd, u8 dplx) |
6353 | { | 6353 | { |
6354 | struct pci_dev *pdev = adapter->pdev; | 6354 | struct pci_dev *pdev = adapter->pdev; |
6355 | struct e1000_mac_info *mac = &adapter->hw.mac; | 6355 | struct e1000_mac_info *mac = &adapter->hw.mac; |
6356 | 6356 | ||
6357 | mac->autoneg = 0; | 6357 | mac->autoneg = 0; |
6358 | 6358 | ||
6359 | /* Make sure dplx is at most 1 bit and lsb of speed is not set | ||
6360 | * for the switch() below to work */ | ||
6361 | if ((spd & 1) || (dplx & ~1)) | ||
6362 | goto err_inval; | ||
6363 | |||
6359 | /* Fiber NIC's only allow 1000 Gbps Full duplex */ | 6364 | /* Fiber NIC's only allow 1000 Gbps Full duplex */ |
6360 | if ((adapter->hw.phy.media_type == e1000_media_type_internal_serdes) && | 6365 | if ((adapter->hw.phy.media_type == e1000_media_type_internal_serdes) && |
6361 | spddplx != (SPEED_1000 + DUPLEX_FULL)) { | 6366 | spd != SPEED_1000 && |
6362 | dev_err(&pdev->dev, "Unsupported Speed/Duplex configuration\n"); | 6367 | dplx != DUPLEX_FULL) |
6363 | return -EINVAL; | 6368 | goto err_inval; |
6364 | } | ||
6365 | 6369 | ||
6366 | switch (spddplx) { | 6370 | switch (spd + dplx) { |
6367 | case SPEED_10 + DUPLEX_HALF: | 6371 | case SPEED_10 + DUPLEX_HALF: |
6368 | mac->forced_speed_duplex = ADVERTISE_10_HALF; | 6372 | mac->forced_speed_duplex = ADVERTISE_10_HALF; |
6369 | break; | 6373 | break; |
@@ -6382,10 +6386,13 @@ int igb_set_spd_dplx(struct igb_adapter *adapter, u16 spddplx) | |||
6382 | break; | 6386 | break; |
6383 | case SPEED_1000 + DUPLEX_HALF: /* not supported */ | 6387 | case SPEED_1000 + DUPLEX_HALF: /* not supported */ |
6384 | default: | 6388 | default: |
6385 | dev_err(&pdev->dev, "Unsupported Speed/Duplex configuration\n"); | 6389 | goto err_inval; |
6386 | return -EINVAL; | ||
6387 | } | 6390 | } |
6388 | return 0; | 6391 | return 0; |
6392 | |||
6393 | err_inval: | ||
6394 | dev_err(&pdev->dev, "Unsupported Speed/Duplex configuration\n"); | ||
6395 | return -EINVAL; | ||
6389 | } | 6396 | } |
6390 | 6397 | ||
6391 | static int __igb_shutdown(struct pci_dev *pdev, bool *enable_wake) | 6398 | static int __igb_shutdown(struct pci_dev *pdev, bool *enable_wake) |