aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Dike <jdike@addtoit.com>2007-05-06 17:51:04 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-05-07 15:13:00 -0400
commitf34d9d2dcb7f17b64124841345b23adc0843e7a5 (patch)
tree0bb200273bcc37da8dd32945ae25c213c9efe2c2
parentb16895b63c504698b0c3ab26ca3c41a4fa162a42 (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.c5
-rw-r--r--arch/um/drivers/mcast_user.c3
-rw-r--r--arch/um/drivers/net_kern.c81
-rw-r--r--arch/um/drivers/pcap_user.c5
-rw-r--r--arch/um/drivers/slip_user.c3
-rw-r--r--arch/um/drivers/slirp_user.c3
-rw-r--r--arch/um/include/net_user.h2
-rw-r--r--arch/um/os-Linux/drivers/ethertap_user.c3
-rw-r--r--arch/um/os-Linux/drivers/tuntap_user.c3
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
126static void daemon_user_init(void *data, void *dev) 126static 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
152static int daemon_open(void *data) 155static 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
45static void mcast_user_init(void *data, void *dev) 45static 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
53static void mcast_remove(void *data) 54static 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};
326static int driver_registered; 326static int driver_registered;
327 327
328static int eth_configure(int n, void *init, char *mac, 328static 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 = &uml_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 = &uml_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
449out_undo_user_init:
450 if (transport->user->init != NULL)
451 (*transport->user->remove)(&lp->user);
452out_unregister:
453 platform_device_unregister(&device->pdev);
454out_free_netdev:
455 free_netdev(dev);
456out_free_device: ;
457 kfree(device);
447} 458}
448 459
449static struct uml_net *find_device(int n) 460static 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
21static void pcap_user_init(void *data, void *dev) 21static 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
38static int pcap_open(void *data) 39static 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
20void slip_user_init(void *data, void *dev) 20static 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
27static int set_up_tty(int fd) 28static 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
18void slirp_user_init(void *data, void *dev) 18static 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
25struct slirp_pre_exec_data { 26struct 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
16struct net_user_info { 16struct 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
27void etap_user_init(void *data, void *dev) 27static 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
34struct addr_change { 35struct 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
27void tuntap_user_init(void *data, void *dev) 27static 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
34static void tuntap_add_addr(unsigned char *addr, unsigned char *netmask, 35static void tuntap_add_addr(unsigned char *addr, unsigned char *netmask,