summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@arm.linux.org.uk>2014-07-08 12:40:33 +0100
committerDavid S. Miller <davem@davemloft.net>2014-07-08 20:02:59 -0700
commit8506fa1d8eb36fbf96ab5e2a7e4b87826853f1be (patch)
tree2b3af7fd76f0ecf576552742ac272cd683adf415 /drivers
parent9a7ba4381af4defa7834c73c8a015532c06f4e8e (diff)
net: fec: quiesce packet processing before changing features
Changing the features (receive checksumming) requires the hardware to be reprogrammed, and also changes the checks in the receive packet processing. The current implementation has a race - fec_set_features() changes the flags which alter the receive packet processing while the adapter is active, and potentially receiving frames. Only after we've modified the software flag do we shutdown and reconfigure the hardware. This can lead to packets being received and marked with a valid checksum (via CHECKSUM_UNNECESSARY) when the hardware checksum validation has not yet been enabled. We must quiesce the device, then change the software configuration for this feature, and then resume the device if it was previously running. The resulting code structure also allows us to add other configuration features in this path without having to quiesce and resume the network interface and device. Acked-by: Fugang Duan <B38611@freescale.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/ethernet/freescale/fec_main.c25
1 files changed, 16 insertions, 9 deletions
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 2945cf6e65cf..124d3c5f8046 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -2346,12 +2346,21 @@ static void fec_poll_controller(struct net_device *dev)
}
#endif
+#define FEATURES_NEED_QUIESCE NETIF_F_RXCSUM
+
static int fec_set_features(struct net_device *netdev,
netdev_features_t features)
{
struct fec_enet_private *fep = netdev_priv(netdev);
netdev_features_t changed = features ^ netdev->features;
+ /* Quiesce the device if necessary */
+ if (netif_running(netdev) && changed & FEATURES_NEED_QUIESCE) {
+ napi_disable(&fep->napi);
+ netif_tx_lock_bh(netdev);
+ fec_stop(netdev);
+ }
+
netdev->features = features;
/* Receive checksum has been changed */
@@ -2360,16 +2369,14 @@ static int fec_set_features(struct net_device *netdev,
fep->csum_flags |= FLAG_RX_CSUM_ENABLED;
else
fep->csum_flags &= ~FLAG_RX_CSUM_ENABLED;
+ }
- if (netif_running(netdev)) {
- napi_disable(&fep->napi);
- netif_tx_lock_bh(netdev);
- fec_stop(netdev);
- fec_restart(netdev, fep->phy_dev->duplex);
- netif_wake_queue(netdev);
- netif_tx_unlock_bh(netdev);
- napi_enable(&fep->napi);
- }
+ /* Resume the device after updates */
+ if (netif_running(netdev) && changed & FEATURES_NEED_QUIESCE) {
+ fec_restart(netdev, fep->phy_dev->duplex);
+ netif_wake_queue(netdev);
+ netif_tx_unlock_bh(netdev);
+ napi_enable(&fep->napi);
}
return 0;