aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Decotigny <decot@google.com>2011-04-27 14:32:43 -0400
committerDavid S. Miller <davem@davemloft.net>2011-04-29 17:03:03 -0400
commit14ad2513ed5b709e566a853f4b515d91c5d83311 (patch)
tree19ce2e6deefaa0725533612df85c51b99d2513cb
parentfbef7139a8b89a7f49ba1410593ed894b4c8b017 (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.h2
-rw-r--r--drivers/net/e1000/e1000_ethtool.c2
-rw-r--r--drivers/net/e1000/e1000_main.c42
-rw-r--r--drivers/net/e1000e/ethtool.c24
-rw-r--r--drivers/net/igb/igb.h2
-rw-r--r--drivers/net/igb/igb_ethtool.c2
-rw-r--r--drivers/net/igb/igb_main.c23
7 files changed, 59 insertions, 38 deletions
diff --git a/drivers/net/e1000/e1000.h b/drivers/net/e1000/e1000.h
index a881dd0093bd..b1b23ddd4eed 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);
349extern void e1000_down(struct e1000_adapter *adapter); 349extern void e1000_down(struct e1000_adapter *adapter);
350extern void e1000_reinit_locked(struct e1000_adapter *adapter); 350extern void e1000_reinit_locked(struct e1000_adapter *adapter);
351extern void e1000_reset(struct e1000_adapter *adapter); 351extern void e1000_reset(struct e1000_adapter *adapter);
352extern int e1000_set_spd_dplx(struct e1000_adapter *adapter, u16 spddplx); 352extern int e1000_set_spd_dplx(struct e1000_adapter *adapter, u32 spd, u8 dplx);
353extern int e1000_setup_all_rx_resources(struct e1000_adapter *adapter); 353extern int e1000_setup_all_rx_resources(struct e1000_adapter *adapter);
354extern int e1000_setup_all_tx_resources(struct e1000_adapter *adapter); 354extern int e1000_setup_all_tx_resources(struct e1000_adapter *adapter);
355extern void e1000_free_all_rx_resources(struct e1000_adapter *adapter); 355extern 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 127fef4fce49..4fa727ce8374 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 477e066a1cf0..c18cb8e883dd 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);
96void e1000_down(struct e1000_adapter *adapter); 96void e1000_down(struct e1000_adapter *adapter);
97void e1000_reinit_locked(struct e1000_adapter *adapter); 97void e1000_reinit_locked(struct e1000_adapter *adapter);
98void e1000_reset(struct e1000_adapter *adapter); 98void e1000_reset(struct e1000_adapter *adapter);
99int e1000_set_spd_dplx(struct e1000_adapter *adapter, u16 spddplx);
100int e1000_setup_all_tx_resources(struct e1000_adapter *adapter); 99int e1000_setup_all_tx_resources(struct e1000_adapter *adapter);
101int e1000_setup_all_rx_resources(struct e1000_adapter *adapter); 100int e1000_setup_all_rx_resources(struct e1000_adapter *adapter);
102void e1000_free_all_tx_resources(struct e1000_adapter *adapter); 101void 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
4599int e1000_set_spd_dplx(struct e1000_adapter *adapter, u16 spddplx) 4598int 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
4638err_inval:
4639 e_err(probe, "Unsupported Speed/Duplex configuration\n");
4640 return -EINVAL;
4635} 4641}
4636 4642
4637static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake) 4643static 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 12f1ee250522..859d0d3af6c9 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
203static int e1000_set_spd_dplx(struct e1000_adapter *adapter, u16 spddplx) 203static 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
244err_inval:
245 e_err("Unsupported Speed/Duplex configuration\n");
246 return -EINVAL;
239} 247}
240 248
241static int e1000_set_settings(struct net_device *netdev, 249static 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 1c687e298d5e..f4fa4b1751cf 100644
--- a/drivers/net/igb/igb.h
+++ b/drivers/net/igb/igb.h
@@ -360,7 +360,7 @@ extern int igb_up(struct igb_adapter *);
360extern void igb_down(struct igb_adapter *); 360extern void igb_down(struct igb_adapter *);
361extern void igb_reinit_locked(struct igb_adapter *); 361extern void igb_reinit_locked(struct igb_adapter *);
362extern void igb_reset(struct igb_adapter *); 362extern void igb_reset(struct igb_adapter *);
363extern int igb_set_spd_dplx(struct igb_adapter *, u16); 363extern int igb_set_spd_dplx(struct igb_adapter *, u32, u8);
364extern int igb_setup_tx_resources(struct igb_ring *); 364extern int igb_setup_tx_resources(struct igb_ring *);
365extern int igb_setup_rx_resources(struct igb_ring *); 365extern int igb_setup_rx_resources(struct igb_ring *);
366extern void igb_free_tx_resources(struct igb_ring *); 366extern 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 023aa9b10654..6e29634b1fb5 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 cdfd57271051..ce7838e55827 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
6352int igb_set_spd_dplx(struct igb_adapter *adapter, u16 spddplx) 6352int 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
6393err_inval:
6394 dev_err(&pdev->dev, "Unsupported Speed/Duplex configuration\n");
6395 return -EINVAL;
6389} 6396}
6390 6397
6391static int __igb_shutdown(struct pci_dev *pdev, bool *enable_wake) 6398static int __igb_shutdown(struct pci_dev *pdev, bool *enable_wake)