diff options
| author | Stephen Hemminger <shemminger@vyatta.com> | 2009-04-27 06:04:58 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2009-04-27 06:04:58 -0400 |
| commit | ae0e8e82205c903978a79ebf5e31c670b61fa5b4 (patch) | |
| tree | 78d54aa285b9a6275e4628ed1fa0965e74417161 | |
| parent | 6a783c9067e3f71aac61a9262fe42c1f68efd4fc (diff) | |
veth: prevent oops caused by netdev destructor
From: Stephen Hemminger <shemminger@vyatta.com>
The veth driver will oops if sysfs hooks are open while module is removed.
The net device destructor can not point to code in a module; basically
there are only two possible safe values: NULL - no destructor, or
free_netdev - free on last use
Signed-off-by: David S. Miller <davem@davemloft.net>
| -rw-r--r-- | drivers/net/veth.c | 41 |
1 files changed, 16 insertions, 25 deletions
diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 015db1cece72..8e56fcf0a0e3 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c | |||
| @@ -210,14 +210,11 @@ rx_drop: | |||
| 210 | 210 | ||
| 211 | static struct net_device_stats *veth_get_stats(struct net_device *dev) | 211 | static struct net_device_stats *veth_get_stats(struct net_device *dev) |
| 212 | { | 212 | { |
| 213 | struct veth_priv *priv; | 213 | struct veth_priv *priv = netdev_priv(dev); |
| 214 | struct net_device_stats *dev_stats; | 214 | struct net_device_stats *dev_stats = &dev->stats; |
| 215 | int cpu; | 215 | unsigned int cpu; |
| 216 | struct veth_net_stats *stats; | 216 | struct veth_net_stats *stats; |
| 217 | 217 | ||
| 218 | priv = netdev_priv(dev); | ||
| 219 | dev_stats = &dev->stats; | ||
| 220 | |||
| 221 | dev_stats->rx_packets = 0; | 218 | dev_stats->rx_packets = 0; |
| 222 | dev_stats->tx_packets = 0; | 219 | dev_stats->tx_packets = 0; |
| 223 | dev_stats->rx_bytes = 0; | 220 | dev_stats->rx_bytes = 0; |
| @@ -225,16 +222,17 @@ static struct net_device_stats *veth_get_stats(struct net_device *dev) | |||
| 225 | dev_stats->tx_dropped = 0; | 222 | dev_stats->tx_dropped = 0; |
| 226 | dev_stats->rx_dropped = 0; | 223 | dev_stats->rx_dropped = 0; |
| 227 | 224 | ||
| 228 | for_each_online_cpu(cpu) { | 225 | if (priv->stats) |
| 229 | stats = per_cpu_ptr(priv->stats, cpu); | 226 | for_each_online_cpu(cpu) { |
| 227 | stats = per_cpu_ptr(priv->stats, cpu); | ||
| 230 | 228 | ||
| 231 | dev_stats->rx_packets += stats->rx_packets; | 229 | dev_stats->rx_packets += stats->rx_packets; |
| 232 | dev_stats->tx_packets += stats->tx_packets; | 230 | dev_stats->tx_packets += stats->tx_packets; |
| 233 | dev_stats->rx_bytes += stats->rx_bytes; | 231 | dev_stats->rx_bytes += stats->rx_bytes; |
| 234 | dev_stats->tx_bytes += stats->tx_bytes; | 232 | dev_stats->tx_bytes += stats->tx_bytes; |
| 235 | dev_stats->tx_dropped += stats->tx_dropped; | 233 | dev_stats->tx_dropped += stats->tx_dropped; |
| 236 | dev_stats->rx_dropped += stats->rx_dropped; | 234 | dev_stats->rx_dropped += stats->rx_dropped; |
| 237 | } | 235 | } |
| 238 | 236 | ||
| 239 | return dev_stats; | 237 | return dev_stats; |
| 240 | } | 238 | } |
| @@ -261,6 +259,8 @@ static int veth_close(struct net_device *dev) | |||
| 261 | netif_carrier_off(dev); | 259 | netif_carrier_off(dev); |
| 262 | netif_carrier_off(priv->peer); | 260 | netif_carrier_off(priv->peer); |
| 263 | 261 | ||
| 262 | free_percpu(priv->stats); | ||
| 263 | priv->stats = NULL; | ||
| 264 | return 0; | 264 | return 0; |
| 265 | } | 265 | } |
| 266 | 266 | ||
| @@ -291,15 +291,6 @@ static int veth_dev_init(struct net_device *dev) | |||
| 291 | return 0; | 291 | return 0; |
| 292 | } | 292 | } |
| 293 | 293 | ||
| 294 | static void veth_dev_free(struct net_device *dev) | ||
| 295 | { | ||
| 296 | struct veth_priv *priv; | ||
| 297 | |||
| 298 | priv = netdev_priv(dev); | ||
| 299 | free_percpu(priv->stats); | ||
| 300 | free_netdev(dev); | ||
| 301 | } | ||
| 302 | |||
| 303 | static const struct net_device_ops veth_netdev_ops = { | 294 | static const struct net_device_ops veth_netdev_ops = { |
| 304 | .ndo_init = veth_dev_init, | 295 | .ndo_init = veth_dev_init, |
| 305 | .ndo_open = veth_open, | 296 | .ndo_open = veth_open, |
| @@ -317,7 +308,7 @@ static void veth_setup(struct net_device *dev) | |||
| 317 | dev->netdev_ops = &veth_netdev_ops; | 308 | dev->netdev_ops = &veth_netdev_ops; |
| 318 | dev->ethtool_ops = &veth_ethtool_ops; | 309 | dev->ethtool_ops = &veth_ethtool_ops; |
| 319 | dev->features |= NETIF_F_LLTX; | 310 | dev->features |= NETIF_F_LLTX; |
| 320 | dev->destructor = veth_dev_free; | 311 | dev->destructor = free_netdev; |
| 321 | } | 312 | } |
| 322 | 313 | ||
| 323 | /* | 314 | /* |
