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 | |
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>
-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, |