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 /drivers/net/veth.c | |
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>
Diffstat (limited to 'drivers/net/veth.c')
-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 | /* |