Commit f34d9d2d authored by Jeff Dike's avatar Jeff Dike Committed by Linus Torvalds

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: default avatarJeff Dike <jdike@linux.intel.com>
Cc: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Cc: Jeff Garzik <jeff@garzik.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent b16895b6
...@@ -123,7 +123,7 @@ static int connect_to_switch(struct daemon_data *pri) ...@@ -123,7 +123,7 @@ static int connect_to_switch(struct daemon_data *pri)
return err; return err;
} }
static void daemon_user_init(void *data, void *dev) static int daemon_user_init(void *data, void *dev)
{ {
struct daemon_data *pri = data; struct daemon_data *pri = data;
struct timeval tv; struct timeval tv;
...@@ -146,7 +146,10 @@ static void daemon_user_init(void *data, void *dev) ...@@ -146,7 +146,10 @@ static void daemon_user_init(void *data, void *dev)
if(pri->fd < 0){ if(pri->fd < 0){
kfree(pri->local_addr); kfree(pri->local_addr);
pri->local_addr = NULL; pri->local_addr = NULL;
return pri->fd;
} }
return 0;
} }
static int daemon_open(void *data) static int daemon_open(void *data)
......
...@@ -42,12 +42,13 @@ static struct sockaddr_in *new_addr(char *addr, unsigned short port) ...@@ -42,12 +42,13 @@ static struct sockaddr_in *new_addr(char *addr, unsigned short port)
return sin; return sin;
} }
static void mcast_user_init(void *data, void *dev) static int mcast_user_init(void *data, void *dev)
{ {
struct mcast_data *pri = data; struct mcast_data *pri = data;
pri->mcast_addr = new_addr(pri->addr, pri->port); pri->mcast_addr = new_addr(pri->addr, pri->port);
pri->dev = dev; pri->dev = dev;
return 0;
} }
static void mcast_remove(void *data) static void mcast_remove(void *data)
......
...@@ -325,7 +325,7 @@ static struct platform_driver uml_net_driver = { ...@@ -325,7 +325,7 @@ static struct platform_driver uml_net_driver = {
}; };
static int driver_registered; static int driver_registered;
static int eth_configure(int n, void *init, char *mac, static void eth_configure(int n, void *init, char *mac,
struct transport *transport) struct transport *transport)
{ {
struct uml_net *device; struct uml_net *device;
...@@ -339,16 +339,12 @@ static int eth_configure(int n, void *init, char *mac, ...@@ -339,16 +339,12 @@ static int eth_configure(int n, void *init, char *mac,
device = kzalloc(sizeof(*device), GFP_KERNEL); device = kzalloc(sizeof(*device), GFP_KERNEL);
if (device == NULL) { if (device == NULL) {
printk(KERN_ERR "eth_configure failed to allocate uml_net\n"); printk(KERN_ERR "eth_configure failed to allocate uml_net\n");
return(1); return;
} }
INIT_LIST_HEAD(&device->list); INIT_LIST_HEAD(&device->list);
device->index = n; device->index = n;
spin_lock(&devices_lock);
list_add(&device->list, &devices);
spin_unlock(&devices_lock);
setup_etheraddr(mac, device->mac); setup_etheraddr(mac, device->mac);
printk(KERN_INFO "Netdevice %d ", n); printk(KERN_INFO "Netdevice %d ", n);
...@@ -360,7 +356,7 @@ static int eth_configure(int n, void *init, char *mac, ...@@ -360,7 +356,7 @@ static int eth_configure(int n, void *init, char *mac,
dev = alloc_etherdev(size); dev = alloc_etherdev(size);
if (dev == NULL) { if (dev == NULL) {
printk(KERN_ERR "eth_configure: failed to allocate device\n"); printk(KERN_ERR "eth_configure: failed to allocate device\n");
return 1; goto out_free_device;
} }
lp = dev->priv; lp = dev->priv;
...@@ -376,7 +372,8 @@ static int eth_configure(int n, void *init, char *mac, ...@@ -376,7 +372,8 @@ static int eth_configure(int n, void *init, char *mac,
} }
device->pdev.id = n; device->pdev.id = n;
device->pdev.name = DRIVER_NAME; device->pdev.name = DRIVER_NAME;
platform_device_register(&device->pdev); if(platform_device_register(&device->pdev))
goto out_free_netdev;
SET_NETDEV_DEV(dev,&device->pdev.dev); SET_NETDEV_DEV(dev,&device->pdev.dev);
/* If this name ends up conflicting with an existing registered /* 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,31 +383,12 @@ static int eth_configure(int n, void *init, char *mac,
snprintf(dev->name, sizeof(dev->name), "eth%d", n); snprintf(dev->name, sizeof(dev->name), "eth%d", n);
device->dev = dev; device->dev = dev;
/*
* These just fill in a data structure, so there's no failure
* to be worried about.
*/
(*transport->kern->init)(dev, init); (*transport->kern->init)(dev, init);
dev->mtu = transport->user->max_packet;
dev->open = uml_net_open;
dev->hard_start_xmit = uml_net_start_xmit;
dev->stop = uml_net_close;
dev->get_stats = uml_net_get_stats;
dev->set_multicast_list = uml_net_set_multicast_list;
dev->tx_timeout = uml_net_tx_timeout;
dev->set_mac_address = uml_net_set_mac;
dev->change_mtu = uml_net_change_mtu;
dev->ethtool_ops = &uml_net_ethtool_ops;
dev->watchdog_timeo = (HZ >> 1);
dev->irq = UM_ETH_IRQ;
rtnl_lock();
err = register_netdevice(dev);
rtnl_unlock();
if (err) {
device->dev = NULL;
/* XXX: should we call ->remove() here? */
free_netdev(dev);
return 1;
}
/* lp.user is the first four bytes of the transport data, which /* lp.user is the first four bytes of the transport data, which
* has already been initialized. This structure assignment will * has already been initialized. This structure assignment will
* overwrite that, so we make sure that .user gets overwritten with * 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,12 +416,45 @@ static int eth_configure(int n, void *init, char *mac,
lp->tl.function = uml_net_user_timer_expire; lp->tl.function = uml_net_user_timer_expire;
memcpy(lp->mac, device->mac, sizeof(lp->mac)); memcpy(lp->mac, device->mac, sizeof(lp->mac));
if (transport->user->init) if ((transport->user->init != NULL) &&
(*transport->user->init)(&lp->user, dev); ((*transport->user->init)(&lp->user, dev) != 0))
goto out_unregister;
set_ether_mac(dev, device->mac); set_ether_mac(dev, device->mac);
dev->mtu = transport->user->max_packet;
dev->open = uml_net_open;
dev->hard_start_xmit = uml_net_start_xmit;
dev->stop = uml_net_close;
dev->get_stats = uml_net_get_stats;
dev->set_multicast_list = uml_net_set_multicast_list;
dev->tx_timeout = uml_net_tx_timeout;
dev->set_mac_address = uml_net_set_mac;
dev->change_mtu = uml_net_change_mtu;
dev->ethtool_ops = &uml_net_ethtool_ops;
dev->watchdog_timeo = (HZ >> 1);
dev->irq = UM_ETH_IRQ;
return 0; rtnl_lock();
err = register_netdevice(dev);
rtnl_unlock();
if (err)
goto out_undo_user_init;
spin_lock(&devices_lock);
list_add(&device->list, &devices);
spin_unlock(&devices_lock);
return;
out_undo_user_init:
if (transport->user->init != NULL)
(*transport->user->remove)(&lp->user);
out_unregister:
platform_device_unregister(&device->pdev);
out_free_netdev:
free_netdev(dev);
out_free_device: ;
kfree(device);
} }
static struct uml_net *find_device(int n) static struct uml_net *find_device(int n)
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
#define PCAP_FD(p) (*(int *)(p)) #define PCAP_FD(p) (*(int *)(p))
static void pcap_user_init(void *data, void *dev) static int pcap_user_init(void *data, void *dev)
{ {
struct pcap_data *pri = data; struct pcap_data *pri = data;
pcap_t *p; pcap_t *p;
...@@ -28,11 +28,12 @@ static void pcap_user_init(void *data, void *dev) ...@@ -28,11 +28,12 @@ static void pcap_user_init(void *data, void *dev)
if(p == NULL){ if(p == NULL){
printk("pcap_user_init : pcap_open_live failed - '%s'\n", printk("pcap_user_init : pcap_open_live failed - '%s'\n",
errors); errors);
return; return -EINVAL;
} }
pri->dev = dev; pri->dev = dev;
pri->pcap = p; pri->pcap = p;
return 0;
} }
static int pcap_open(void *data) static int pcap_open(void *data)
......
...@@ -17,11 +17,12 @@ ...@@ -17,11 +17,12 @@
#include "os.h" #include "os.h"
#include "um_malloc.h" #include "um_malloc.h"
void slip_user_init(void *data, void *dev) static int slip_user_init(void *data, void *dev)
{ {
struct slip_data *pri = data; struct slip_data *pri = data;
pri->dev = dev; pri->dev = dev;
return 0;
} }
static int set_up_tty(int fd) static int set_up_tty(int fd)
......
...@@ -15,11 +15,12 @@ ...@@ -15,11 +15,12 @@
#include "slip_common.h" #include "slip_common.h"
#include "os.h" #include "os.h"
void slirp_user_init(void *data, void *dev) static int slirp_user_init(void *data, void *dev)
{ {
struct slirp_data *pri = data; struct slirp_data *pri = data;
pri->dev = dev; pri->dev = dev;
return 0;
} }
struct slirp_pre_exec_data { struct slirp_pre_exec_data {
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
#define UML_NET_VERSION (4) #define UML_NET_VERSION (4)
struct net_user_info { struct net_user_info {
void (*init)(void *, void *); int (*init)(void *, void *);
int (*open)(void *); int (*open)(void *);
void (*close)(int, void *); void (*close)(int, void *);
void (*remove)(void *); void (*remove)(void *);
......
...@@ -24,11 +24,12 @@ ...@@ -24,11 +24,12 @@
#define MAX_PACKET ETH_MAX_PACKET #define MAX_PACKET ETH_MAX_PACKET
void etap_user_init(void *data, void *dev) static int etap_user_init(void *data, void *dev)
{ {
struct ethertap_data *pri = data; struct ethertap_data *pri = data;
pri->dev = dev; pri->dev = dev;
return 0;
} }
struct addr_change { struct addr_change {
......
...@@ -24,11 +24,12 @@ ...@@ -24,11 +24,12 @@
#define MAX_PACKET ETH_MAX_PACKET #define MAX_PACKET ETH_MAX_PACKET
void tuntap_user_init(void *data, void *dev) static int tuntap_user_init(void *data, void *dev)
{ {
struct tuntap_data *pri = data; struct tuntap_data *pri = data;
pri->dev = dev; pri->dev = dev;
return 0;
} }
static void tuntap_add_addr(unsigned char *addr, unsigned char *netmask, static void tuntap_add_addr(unsigned char *addr, unsigned char *netmask,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment