Merge git://git.infradead.org/mtd-2.6
[pandora-kernel.git] / arch / um / drivers / net_kern.c
index 84c73a3..501f956 100644 (file)
@@ -34,7 +34,7 @@
 #define DRIVER_NAME "uml-netdev"
 
 static DEFINE_SPINLOCK(opened_lock);
-LIST_HEAD(opened);
+static LIST_HEAD(opened);
 
 static int uml_net_rx(struct net_device *dev)
 {
@@ -68,6 +68,11 @@ static int uml_net_rx(struct net_device *dev)
        return pkt_len;
 }
 
+static void uml_dev_close(void* dev)
+{
+       dev_close( (struct net_device *) dev);
+}
+
 irqreturn_t uml_net_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
        struct net_device *dev = dev_id;
@@ -80,15 +85,21 @@ irqreturn_t uml_net_interrupt(int irq, void *dev_id, struct pt_regs *regs)
        spin_lock(&lp->lock);
        while((err = uml_net_rx(dev)) > 0) ;
        if(err < 0) {
+               DECLARE_WORK(close_work, uml_dev_close, dev);
                printk(KERN_ERR 
                       "Device '%s' read returned %d, shutting it down\n", 
                       dev->name, err);
-               dev_close(dev);
+               /* dev_close can't be called in interrupt context, and takes
+                * again lp->lock.
+                * And dev_close() can be safely called multiple times on the
+                * same device, since it tests for (dev->flags & IFF_UP). So
+                * there's no harm in delaying the device shutdown. */
+               schedule_work(&close_work);
                goto out;
        }
        reactivate_fd(lp->fd, UM_ETH_IRQ);
 
- out:
+out:
        spin_unlock(&lp->lock);
        return(IRQ_HANDLED);
 }
@@ -117,12 +128,11 @@ static int uml_net_open(struct net_device *dev)
        }
 
        err = um_request_irq(dev->irq, lp->fd, IRQ_READ, uml_net_interrupt,
-                            SA_INTERRUPT | SA_SHIRQ, dev->name, dev);
+                            IRQF_DISABLED | IRQF_SHARED, dev->name, dev);
        if(err != 0){
                printk(KERN_ERR "uml_net_open: failed to get irq(%d)\n", err);
-               if(lp->close != NULL) (*lp->close)(lp->fd, &lp->user);
-               lp->fd = -1;
                err = -ENETUNREACH;
+               goto out_close;
        }
 
        lp->tl.data = (unsigned long) &lp->user;
@@ -134,9 +144,19 @@ static int uml_net_open(struct net_device *dev)
         */
        while((err = uml_net_rx(dev)) > 0) ;
 
- out:
        spin_unlock(&lp->lock);
-       return(err);
+
+       spin_lock(&opened_lock);
+       list_add(&lp->list, &opened);
+       spin_unlock(&opened_lock);
+
+       return 0;
+out_close:
+       if(lp->close != NULL) (*lp->close)(lp->fd, &lp->user);
+       lp->fd = -1;
+out:
+       spin_unlock(&lp->lock);
+       return err;
 }
 
 static int uml_net_close(struct net_device *dev)
@@ -152,6 +172,11 @@ static int uml_net_close(struct net_device *dev)
        lp->fd = -1;
 
        spin_unlock(&lp->lock);
+
+       spin_lock(&opened_lock);
+       list_del(&lp->list);
+       spin_unlock(&opened_lock);
+
        return 0;
 }
 
@@ -266,7 +291,7 @@ void uml_net_user_timer_expire(unsigned long _conn)
 }
 
 static DEFINE_SPINLOCK(devices_lock);
-static struct list_head devices = LIST_HEAD_INIT(devices);
+static LIST_HEAD(devices);
 
 static struct platform_driver uml_net_driver = {
        .driver = {
@@ -316,6 +341,11 @@ static int eth_configure(int n, void *init, char *mac,
                return 1;
        }
 
+       lp = dev->priv;
+       /* This points to the transport private data. It's still clear, but we
+        * must memset it to 0 *now*. Let's help the drivers. */
+       memset(lp, 0, size);
+
        /* sysfs register */
        if (!driver_registered) {
                platform_driver_register(&uml_net_driver);
@@ -357,7 +387,6 @@ static int eth_configure(int n, void *init, char *mac,
                free_netdev(dev);
                return 1;
        }
-       lp = dev->priv;
 
        /* lp.user is the first four bytes of the transport data, which
         * has already been initialized.  This structure assignment will
@@ -394,11 +423,7 @@ static int eth_configure(int n, void *init, char *mac,
        if (device->have_mac)
                set_ether_mac(dev, device->mac);
 
-       spin_lock(&opened_lock);
-       list_add(&lp->list, &opened);
-       spin_unlock(&opened_lock);
-
-       return(0);
+       return 0;
 }
 
 static struct uml_net *find_device(int n)
@@ -586,7 +611,7 @@ static int net_config(char *str)
        err = eth_parse(str, &n, &str);
        if(err) return(err);
 
-       str = uml_strdup(str);
+       str = kstrdup(str, GFP_KERNEL);
        if(str == NULL){
                printk(KERN_ERR "net_config failed to strdup string\n");
                return(-1);
@@ -715,6 +740,7 @@ static void close_devices(void)
 
        list_for_each(ele, &opened){
                lp = list_entry(ele, struct uml_net_private, list);
+               free_irq(lp->dev->irq, lp->dev);
                if((lp->close != NULL) && (lp->fd >= 0))
                        (*lp->close)(lp->fd, &lp->user);
                if(lp->remove != NULL) (*lp->remove)(&lp->user);