]> err.no Git - linux-2.6/blobdiff - arch/um/drivers/net_kern.c
[PATCH] uml: assign random MACs to interfaces if necessary
[linux-2.6] / arch / um / drivers / net_kern.c
index fb1f9fb9b8717ce74fe95104d711642e4c152a02..684a1ef93c87e888fae13ad8989f399b0fe39501 100644 (file)
 #include "irq_user.h"
 #include "irq_kern.h"
 
+static inline void set_ether_mac(struct net_device *dev, unsigned char *addr)
+{
+       memcpy(dev->dev_addr, addr, ETH_ALEN);
+}
+
 #define DRIVER_NAME "uml-netdev"
 
 static DEFINE_SPINLOCK(opened_lock);
@@ -68,6 +73,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 +90,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);
 }
@@ -98,8 +114,6 @@ static int uml_net_open(struct net_device *dev)
        struct uml_net_private *lp = dev->priv;
        int err;
 
-       spin_lock(&lp->lock);
-
        if(lp->fd >= 0){
                err = -ENXIO;
                goto out;
@@ -117,12 +131,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 +147,16 @@ 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:
+       return err;
 }
 
 static int uml_net_close(struct net_device *dev)
@@ -144,15 +164,16 @@ static int uml_net_close(struct net_device *dev)
        struct uml_net_private *lp = dev->priv;
        
        netif_stop_queue(dev);
-       spin_lock(&lp->lock);
 
        free_irq(dev->irq, dev);
        if(lp->close != NULL)
                (*lp->close)(lp->fd, &lp->user);
        lp->fd = -1;
+
+       spin_lock(&opened_lock);
        list_del(&lp->list);
+       spin_unlock(&opened_lock);
 
-       spin_unlock(&lp->lock);
        return 0;
 }
 
@@ -217,9 +238,9 @@ static int uml_net_set_mac(struct net_device *dev, void *addr)
        struct uml_net_private *lp = dev->priv;
        struct sockaddr *hwaddr = addr;
 
-       spin_lock(&lp->lock);
-       memcpy(dev->dev_addr, hwaddr->sa_data, ETH_ALEN);
-       spin_unlock(&lp->lock);
+       spin_lock_irq(&lp->lock);
+       set_ether_mac(dev, hwaddr->sa_data);
+       spin_unlock_irq(&lp->lock);
 
        return(0);
 }
@@ -229,7 +250,7 @@ static int uml_net_change_mtu(struct net_device *dev, int new_mtu)
        struct uml_net_private *lp = dev->priv;
        int err = 0;
 
-       spin_lock(&lp->lock);
+       spin_lock_irq(&lp->lock);
 
        new_mtu = (*lp->set_mtu)(new_mtu, &lp->user);
        if(new_mtu < 0){
@@ -240,7 +261,7 @@ static int uml_net_change_mtu(struct net_device *dev, int new_mtu)
        dev->mtu = new_mtu;
 
  out:
-       spin_unlock(&lp->lock);
+       spin_unlock_irq(&lp->lock);
        return err;
 }
 
@@ -317,6 +338,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);
@@ -358,7 +384,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
@@ -395,11 +420,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)
@@ -540,12 +561,13 @@ static int eth_setup(char *str)
        int n, err;
 
        err = eth_parse(str, &n, &str);
-       if(err) return(1);
+       if(err)
+               return 1;
 
-       new = alloc_bootmem(sizeof(new));
+       new = alloc_bootmem(sizeof(*new));
        if (new == NULL){
                printk("eth_init : alloc_bootmem failed\n");
-               return(1);
+               return 1;
        }
 
        INIT_LIST_HEAD(&new->list);
@@ -553,7 +575,7 @@ static int eth_setup(char *str)
        new->init = str;
 
        list_add_tail(&new->list, &eth_cmd_line);
-       return(1);
+       return 1;
 }
 
 __setup("eth", eth_setup);
@@ -731,7 +753,8 @@ int setup_etheraddr(char *str, unsigned char *addr)
        int i;
 
        if(str == NULL)
-               return(0);
+               goto random;
+
        for(i=0;i<6;i++){
                addr[i] = simple_strtoul(str, &end, 16);
                if((end == str) ||
@@ -739,7 +762,7 @@ int setup_etheraddr(char *str, unsigned char *addr)
                        printk(KERN_ERR 
                               "setup_etheraddr: failed to parse '%s' "
                               "as an ethernet address\n", str);
-                       return(0);
+                       goto random;
                }
                str = end + 1;
        }
@@ -747,9 +770,15 @@ int setup_etheraddr(char *str, unsigned char *addr)
                printk(KERN_ERR 
                       "Attempt to assign a broadcast ethernet address to a "
                       "device disallowed\n");
-               return(0);
+               goto random;
        }
-       return(1);
+       return 1;
+
+random:
+       addr[0] = 0xfe;
+       addr[1] = 0xfd;
+       random_mac(addr);
+       return 1;
 }
 
 void dev_ip_addr(void *d, unsigned char *bin_buf)
@@ -766,13 +795,6 @@ void dev_ip_addr(void *d, unsigned char *bin_buf)
        memcpy(bin_buf, &in->ifa_address, sizeof(in->ifa_address));
 }
 
-void set_ether_mac(void *d, unsigned char *addr)
-{
-       struct net_device *dev = d;
-
-       memcpy(dev->dev_addr, addr, ETH_ALEN);  
-}
-
 struct sk_buff *ether_adjust_skb(struct sk_buff *skb, int extra)
 {
        if((skb != NULL) && (skb_tailroom(skb) < extra)){
@@ -810,7 +832,7 @@ int dev_netmask(void *d, void *m)
        struct net_device *dev = d;
        struct in_device *ip = dev->ip_ptr;
        struct in_ifaddr *in;
-       __u32 *mask_out = m;
+       __be32 *mask_out = m;
 
        if(ip == NULL) 
                return(1);