diff options
author | Jeff Dike <jdike@addtoit.com> | 2007-05-06 17:51:04 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-05-07 15:13:00 -0400 |
commit | f34d9d2dcb7f17b64124841345b23adc0843e7a5 (patch) | |
tree | 0bb200273bcc37da8dd32945ae25c213c9efe2c2 /arch/um/drivers/net_kern.c | |
parent | b16895b63c504698b0c3ab26ca3c41a4fa162a42 (diff) |
uml: network interface hotplug error handling
This fixes a number of problems associated with network interface hotplug.
The userspace initialization function can fail in some cases, but the
failure was never passed back to eth_configure, which proceeded with the
configuration. This results in a zombie device that is present, but can't
work. This is fixed by allowing the initialization routines to return an
error, which is checked, and the configuration aborted on failure.
eth_configure failed to check for many failures. Even when it did check,
it didn't undo whatever initializations has already happened, so a present,
but partially initialized and non-working device could result. It now
checks everything that can fail, and bails out, undoing whatever had been
done.
The return value of eth_configure was always ignored, so it is now just
void.
Signed-off-by: Jeff Dike <jdike@linux.intel.com>
Cc: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Cc: Jeff Garzik <jeff@garzik.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/um/drivers/net_kern.c')
-rw-r--r-- | arch/um/drivers/net_kern.c | 81 |
1 files changed, 46 insertions, 35 deletions
diff --git a/arch/um/drivers/net_kern.c b/arch/um/drivers/net_kern.c index 859303730b2f..ac746fb5d10f 100644 --- a/arch/um/drivers/net_kern.c +++ b/arch/um/drivers/net_kern.c | |||
@@ -325,8 +325,8 @@ static struct platform_driver uml_net_driver = { | |||
325 | }; | 325 | }; |
326 | static int driver_registered; | 326 | static int driver_registered; |
327 | 327 | ||
328 | static int eth_configure(int n, void *init, char *mac, | 328 | static void eth_configure(int n, void *init, char *mac, |
329 | struct transport *transport) | 329 | struct transport *transport) |
330 | { | 330 | { |
331 | struct uml_net *device; | 331 | struct uml_net *device; |
332 | struct net_device *dev; | 332 | struct net_device *dev; |
@@ -339,16 +339,12 @@ static int eth_configure(int n, void *init, char *mac, | |||
339 | device = kzalloc(sizeof(*device), GFP_KERNEL); | 339 | device = kzalloc(sizeof(*device), GFP_KERNEL); |
340 | if (device == NULL) { | 340 | if (device == NULL) { |
341 | printk(KERN_ERR "eth_configure failed to allocate uml_net\n"); | 341 | printk(KERN_ERR "eth_configure failed to allocate uml_net\n"); |
342 | return(1); | 342 | return; |
343 | } | 343 | } |
344 | 344 | ||
345 | INIT_LIST_HEAD(&device->list); | 345 | INIT_LIST_HEAD(&device->list); |
346 | device->index = n; | 346 | device->index = n; |
347 | 347 | ||
348 | spin_lock(&devices_lock); | ||
349 | list_add(&device->list, &devices); | ||
350 | spin_unlock(&devices_lock); | ||
351 | |||
352 | setup_etheraddr(mac, device->mac); | 348 | setup_etheraddr(mac, device->mac); |
353 | 349 | ||
354 | printk(KERN_INFO "Netdevice %d ", n); | 350 | printk(KERN_INFO "Netdevice %d ", n); |
@@ -360,7 +356,7 @@ static int eth_configure(int n, void *init, char *mac, | |||
360 | dev = alloc_etherdev(size); | 356 | dev = alloc_etherdev(size); |
361 | if (dev == NULL) { | 357 | if (dev == NULL) { |
362 | printk(KERN_ERR "eth_configure: failed to allocate device\n"); | 358 | printk(KERN_ERR "eth_configure: failed to allocate device\n"); |
363 | return 1; | 359 | goto out_free_device; |
364 | } | 360 | } |
365 | 361 | ||
366 | lp = dev->priv; | 362 | lp = dev->priv; |
@@ -376,7 +372,8 @@ static int eth_configure(int n, void *init, char *mac, | |||
376 | } | 372 | } |
377 | device->pdev.id = n; | 373 | device->pdev.id = n; |
378 | device->pdev.name = DRIVER_NAME; | 374 | device->pdev.name = DRIVER_NAME; |
379 | platform_device_register(&device->pdev); | 375 | if(platform_device_register(&device->pdev)) |
376 | goto out_free_netdev; | ||
380 | SET_NETDEV_DEV(dev,&device->pdev.dev); | 377 | SET_NETDEV_DEV(dev,&device->pdev.dev); |
381 | 378 | ||
382 | /* If this name ends up conflicting with an existing registered | 379 | /* If this name ends up conflicting with an existing registered |
@@ -386,31 +383,12 @@ static int eth_configure(int n, void *init, char *mac, | |||
386 | snprintf(dev->name, sizeof(dev->name), "eth%d", n); | 383 | snprintf(dev->name, sizeof(dev->name), "eth%d", n); |
387 | device->dev = dev; | 384 | device->dev = dev; |
388 | 385 | ||
386 | /* | ||
387 | * These just fill in a data structure, so there's no failure | ||
388 | * to be worried about. | ||
389 | */ | ||
389 | (*transport->kern->init)(dev, init); | 390 | (*transport->kern->init)(dev, init); |
390 | 391 | ||
391 | dev->mtu = transport->user->max_packet; | ||
392 | dev->open = uml_net_open; | ||
393 | dev->hard_start_xmit = uml_net_start_xmit; | ||
394 | dev->stop = uml_net_close; | ||
395 | dev->get_stats = uml_net_get_stats; | ||
396 | dev->set_multicast_list = uml_net_set_multicast_list; | ||
397 | dev->tx_timeout = uml_net_tx_timeout; | ||
398 | dev->set_mac_address = uml_net_set_mac; | ||
399 | dev->change_mtu = uml_net_change_mtu; | ||
400 | dev->ethtool_ops = ¨_net_ethtool_ops; | ||
401 | dev->watchdog_timeo = (HZ >> 1); | ||
402 | dev->irq = UM_ETH_IRQ; | ||
403 | |||
404 | rtnl_lock(); | ||
405 | err = register_netdevice(dev); | ||
406 | rtnl_unlock(); | ||
407 | if (err) { | ||
408 | device->dev = NULL; | ||
409 | /* XXX: should we call ->remove() here? */ | ||
410 | free_netdev(dev); | ||
411 | return 1; | ||
412 | } | ||
413 | |||
414 | /* lp.user is the first four bytes of the transport data, which | 392 | /* lp.user is the first four bytes of the transport data, which |
415 | * has already been initialized. This structure assignment will | 393 | * has already been initialized. This structure assignment will |
416 | * overwrite that, so we make sure that .user gets overwritten with | 394 | * overwrite that, so we make sure that .user gets overwritten with |
@@ -438,12 +416,45 @@ static int eth_configure(int n, void *init, char *mac, | |||
438 | lp->tl.function = uml_net_user_timer_expire; | 416 | lp->tl.function = uml_net_user_timer_expire; |
439 | memcpy(lp->mac, device->mac, sizeof(lp->mac)); | 417 | memcpy(lp->mac, device->mac, sizeof(lp->mac)); |
440 | 418 | ||
441 | if (transport->user->init) | 419 | if ((transport->user->init != NULL) && |
442 | (*transport->user->init)(&lp->user, dev); | 420 | ((*transport->user->init)(&lp->user, dev) != 0)) |
421 | goto out_unregister; | ||
443 | 422 | ||
444 | set_ether_mac(dev, device->mac); | 423 | set_ether_mac(dev, device->mac); |
424 | dev->mtu = transport->user->max_packet; | ||
425 | dev->open = uml_net_open; | ||
426 | dev->hard_start_xmit = uml_net_start_xmit; | ||
427 | dev->stop = uml_net_close; | ||
428 | dev->get_stats = uml_net_get_stats; | ||
429 | dev->set_multicast_list = uml_net_set_multicast_list; | ||
430 | dev->tx_timeout = uml_net_tx_timeout; | ||
431 | dev->set_mac_address = uml_net_set_mac; | ||
432 | dev->change_mtu = uml_net_change_mtu; | ||
433 | dev->ethtool_ops = ¨_net_ethtool_ops; | ||
434 | dev->watchdog_timeo = (HZ >> 1); | ||
435 | dev->irq = UM_ETH_IRQ; | ||
445 | 436 | ||
446 | return 0; | 437 | rtnl_lock(); |
438 | err = register_netdevice(dev); | ||
439 | rtnl_unlock(); | ||
440 | if (err) | ||
441 | goto out_undo_user_init; | ||
442 | |||
443 | spin_lock(&devices_lock); | ||
444 | list_add(&device->list, &devices); | ||
445 | spin_unlock(&devices_lock); | ||
446 | |||
447 | return; | ||
448 | |||
449 | out_undo_user_init: | ||
450 | if (transport->user->init != NULL) | ||
451 | (*transport->user->remove)(&lp->user); | ||
452 | out_unregister: | ||
453 | platform_device_unregister(&device->pdev); | ||
454 | out_free_netdev: | ||
455 | free_netdev(dev); | ||
456 | out_free_device: ; | ||
457 | kfree(device); | ||
447 | } | 458 | } |
448 | 459 | ||
449 | static struct uml_net *find_device(int n) | 460 | static struct uml_net *find_device(int n) |