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 | |
| 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')
| -rw-r--r-- | arch/um/drivers/daemon_user.c | 5 | ||||
| -rw-r--r-- | arch/um/drivers/mcast_user.c | 3 | ||||
| -rw-r--r-- | arch/um/drivers/net_kern.c | 81 | ||||
| -rw-r--r-- | arch/um/drivers/pcap_user.c | 5 | ||||
| -rw-r--r-- | arch/um/drivers/slip_user.c | 3 | ||||
| -rw-r--r-- | arch/um/drivers/slirp_user.c | 3 | ||||
| -rw-r--r-- | arch/um/include/net_user.h | 2 | ||||
| -rw-r--r-- | arch/um/os-Linux/drivers/ethertap_user.c | 3 | ||||
| -rw-r--r-- | arch/um/os-Linux/drivers/tuntap_user.c | 3 |
9 files changed, 64 insertions, 44 deletions
diff --git a/arch/um/drivers/daemon_user.c b/arch/um/drivers/daemon_user.c index 09d1de90297c..d0b656a517d3 100644 --- a/arch/um/drivers/daemon_user.c +++ b/arch/um/drivers/daemon_user.c | |||
| @@ -123,7 +123,7 @@ static int connect_to_switch(struct daemon_data *pri) | |||
| 123 | return err; | 123 | return err; |
| 124 | } | 124 | } |
| 125 | 125 | ||
| 126 | static void daemon_user_init(void *data, void *dev) | 126 | static int daemon_user_init(void *data, void *dev) |
| 127 | { | 127 | { |
| 128 | struct daemon_data *pri = data; | 128 | struct daemon_data *pri = data; |
| 129 | struct timeval tv; | 129 | struct timeval tv; |
| @@ -146,7 +146,10 @@ static void daemon_user_init(void *data, void *dev) | |||
| 146 | if(pri->fd < 0){ | 146 | if(pri->fd < 0){ |
| 147 | kfree(pri->local_addr); | 147 | kfree(pri->local_addr); |
| 148 | pri->local_addr = NULL; | 148 | pri->local_addr = NULL; |
| 149 | return pri->fd; | ||
| 149 | } | 150 | } |
| 151 | |||
| 152 | return 0; | ||
| 150 | } | 153 | } |
| 151 | 154 | ||
| 152 | static int daemon_open(void *data) | 155 | static int daemon_open(void *data) |
diff --git a/arch/um/drivers/mcast_user.c b/arch/um/drivers/mcast_user.c index cfaa2cc43139..0f64d9467286 100644 --- a/arch/um/drivers/mcast_user.c +++ b/arch/um/drivers/mcast_user.c | |||
| @@ -42,12 +42,13 @@ static struct sockaddr_in *new_addr(char *addr, unsigned short port) | |||
| 42 | return sin; | 42 | return sin; |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | static void mcast_user_init(void *data, void *dev) | 45 | static int mcast_user_init(void *data, void *dev) |
| 46 | { | 46 | { |
| 47 | struct mcast_data *pri = data; | 47 | struct mcast_data *pri = data; |
| 48 | 48 | ||
| 49 | pri->mcast_addr = new_addr(pri->addr, pri->port); | 49 | pri->mcast_addr = new_addr(pri->addr, pri->port); |
| 50 | pri->dev = dev; | 50 | pri->dev = dev; |
| 51 | return 0; | ||
| 51 | } | 52 | } |
| 52 | 53 | ||
| 53 | static void mcast_remove(void *data) | 54 | static void mcast_remove(void *data) |
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) |
diff --git a/arch/um/drivers/pcap_user.c b/arch/um/drivers/pcap_user.c index a1747dc0ff6f..dc0a903ef9a6 100644 --- a/arch/um/drivers/pcap_user.c +++ b/arch/um/drivers/pcap_user.c | |||
| @@ -18,7 +18,7 @@ | |||
| 18 | 18 | ||
| 19 | #define PCAP_FD(p) (*(int *)(p)) | 19 | #define PCAP_FD(p) (*(int *)(p)) |
| 20 | 20 | ||
| 21 | static void pcap_user_init(void *data, void *dev) | 21 | static int pcap_user_init(void *data, void *dev) |
| 22 | { | 22 | { |
| 23 | struct pcap_data *pri = data; | 23 | struct pcap_data *pri = data; |
| 24 | pcap_t *p; | 24 | pcap_t *p; |
| @@ -28,11 +28,12 @@ static void pcap_user_init(void *data, void *dev) | |||
| 28 | if(p == NULL){ | 28 | if(p == NULL){ |
| 29 | printk("pcap_user_init : pcap_open_live failed - '%s'\n", | 29 | printk("pcap_user_init : pcap_open_live failed - '%s'\n", |
| 30 | errors); | 30 | errors); |
| 31 | return; | 31 | return -EINVAL; |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | pri->dev = dev; | 34 | pri->dev = dev; |
| 35 | pri->pcap = p; | 35 | pri->pcap = p; |
| 36 | return 0; | ||
| 36 | } | 37 | } |
| 37 | 38 | ||
| 38 | static int pcap_open(void *data) | 39 | static int pcap_open(void *data) |
diff --git a/arch/um/drivers/slip_user.c b/arch/um/drivers/slip_user.c index 7eddacc53b6e..329c072d17d1 100644 --- a/arch/um/drivers/slip_user.c +++ b/arch/um/drivers/slip_user.c | |||
| @@ -17,11 +17,12 @@ | |||
| 17 | #include "os.h" | 17 | #include "os.h" |
| 18 | #include "um_malloc.h" | 18 | #include "um_malloc.h" |
| 19 | 19 | ||
| 20 | void slip_user_init(void *data, void *dev) | 20 | static int slip_user_init(void *data, void *dev) |
| 21 | { | 21 | { |
| 22 | struct slip_data *pri = data; | 22 | struct slip_data *pri = data; |
| 23 | 23 | ||
| 24 | pri->dev = dev; | 24 | pri->dev = dev; |
| 25 | return 0; | ||
| 25 | } | 26 | } |
| 26 | 27 | ||
| 27 | static int set_up_tty(int fd) | 28 | static int set_up_tty(int fd) |
diff --git a/arch/um/drivers/slirp_user.c b/arch/um/drivers/slirp_user.c index ce5e85d1de3d..f0a40abc8abf 100644 --- a/arch/um/drivers/slirp_user.c +++ b/arch/um/drivers/slirp_user.c | |||
| @@ -15,11 +15,12 @@ | |||
| 15 | #include "slip_common.h" | 15 | #include "slip_common.h" |
| 16 | #include "os.h" | 16 | #include "os.h" |
| 17 | 17 | ||
| 18 | void slirp_user_init(void *data, void *dev) | 18 | static int slirp_user_init(void *data, void *dev) |
| 19 | { | 19 | { |
| 20 | struct slirp_data *pri = data; | 20 | struct slirp_data *pri = data; |
| 21 | 21 | ||
| 22 | pri->dev = dev; | 22 | pri->dev = dev; |
| 23 | return 0; | ||
| 23 | } | 24 | } |
| 24 | 25 | ||
| 25 | struct slirp_pre_exec_data { | 26 | struct slirp_pre_exec_data { |
diff --git a/arch/um/include/net_user.h b/arch/um/include/net_user.h index 19f207cd70fe..cfe7c50634b9 100644 --- a/arch/um/include/net_user.h +++ b/arch/um/include/net_user.h | |||
| @@ -14,7 +14,7 @@ | |||
| 14 | #define UML_NET_VERSION (4) | 14 | #define UML_NET_VERSION (4) |
| 15 | 15 | ||
| 16 | struct net_user_info { | 16 | struct net_user_info { |
| 17 | void (*init)(void *, void *); | 17 | int (*init)(void *, void *); |
| 18 | int (*open)(void *); | 18 | int (*open)(void *); |
| 19 | void (*close)(int, void *); | 19 | void (*close)(int, void *); |
| 20 | void (*remove)(void *); | 20 | void (*remove)(void *); |
diff --git a/arch/um/os-Linux/drivers/ethertap_user.c b/arch/um/os-Linux/drivers/ethertap_user.c index f3ad0dac7cdc..2cc2d3ea2e6d 100644 --- a/arch/um/os-Linux/drivers/ethertap_user.c +++ b/arch/um/os-Linux/drivers/ethertap_user.c | |||
| @@ -24,11 +24,12 @@ | |||
| 24 | 24 | ||
| 25 | #define MAX_PACKET ETH_MAX_PACKET | 25 | #define MAX_PACKET ETH_MAX_PACKET |
| 26 | 26 | ||
| 27 | void etap_user_init(void *data, void *dev) | 27 | static int etap_user_init(void *data, void *dev) |
| 28 | { | 28 | { |
| 29 | struct ethertap_data *pri = data; | 29 | struct ethertap_data *pri = data; |
| 30 | 30 | ||
| 31 | pri->dev = dev; | 31 | pri->dev = dev; |
| 32 | return 0; | ||
| 32 | } | 33 | } |
| 33 | 34 | ||
| 34 | struct addr_change { | 35 | struct addr_change { |
diff --git a/arch/um/os-Linux/drivers/tuntap_user.c b/arch/um/os-Linux/drivers/tuntap_user.c index ab63fb6f9992..506ef09d83a0 100644 --- a/arch/um/os-Linux/drivers/tuntap_user.c +++ b/arch/um/os-Linux/drivers/tuntap_user.c | |||
| @@ -24,11 +24,12 @@ | |||
| 24 | 24 | ||
| 25 | #define MAX_PACKET ETH_MAX_PACKET | 25 | #define MAX_PACKET ETH_MAX_PACKET |
| 26 | 26 | ||
| 27 | void tuntap_user_init(void *data, void *dev) | 27 | static int tuntap_user_init(void *data, void *dev) |
| 28 | { | 28 | { |
| 29 | struct tuntap_data *pri = data; | 29 | struct tuntap_data *pri = data; |
| 30 | 30 | ||
| 31 | pri->dev = dev; | 31 | pri->dev = dev; |
| 32 | return 0; | ||
| 32 | } | 33 | } |
| 33 | 34 | ||
| 34 | static void tuntap_add_addr(unsigned char *addr, unsigned char *netmask, | 35 | static void tuntap_add_addr(unsigned char *addr, unsigned char *netmask, |
