]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - arch/um/drivers/net_kern.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/roland...
[linux-2.6-omap-h63xx.git] / arch / um / drivers / net_kern.c
index 59811cc880e0fa18066f76fba8ba8fa76798b8a6..1e8f41a9951142e536e22312d25e106b1cf1297e 100644 (file)
@@ -34,6 +34,46 @@ static inline void set_ether_mac(struct net_device *dev, unsigned char *addr)
 static DEFINE_SPINLOCK(opened_lock);
 static LIST_HEAD(opened);
 
+/*
+ * The drop_skb is used when we can't allocate an skb.  The
+ * packet is read into drop_skb in order to get the data off the
+ * connection to the host.
+ * It is reallocated whenever a maximum packet size is seen which is
+ * larger than any seen before.  update_drop_skb is called from
+ * eth_configure when a new interface is added.
+ */
+static DEFINE_SPINLOCK(drop_lock);
+static struct sk_buff *drop_skb;
+static int drop_max;
+
+static int update_drop_skb(int max)
+{
+       struct sk_buff *new;
+       unsigned long flags;
+       int err = 0;
+
+       spin_lock_irqsave(&drop_lock, flags);
+
+       if (max <= drop_max)
+               goto out;
+
+       err = -ENOMEM;
+       new = dev_alloc_skb(max);
+       if (new == NULL)
+               goto out;
+
+       skb_put(new, max);
+
+       kfree_skb(drop_skb);
+       drop_skb = new;
+       drop_max = max;
+       err = 0;
+out:
+       spin_unlock_irqrestore(&drop_lock, flags);
+
+       return err;
+}
+
 static int uml_net_rx(struct net_device *dev)
 {
        struct uml_net_private *lp = dev->priv;
@@ -43,6 +83,9 @@ static int uml_net_rx(struct net_device *dev)
        /* If we can't allocate memory, try again next round. */
        skb = dev_alloc_skb(lp->max_packet);
        if (skb == NULL) {
+               drop_skb->dev = dev;
+               /* Read a packet into drop_skb and don't do anything with it. */
+               (*lp->read)(lp->fd, drop_skb, lp);
                lp->stats.rx_dropped++;
                return 0;
        }
@@ -55,10 +98,10 @@ static int uml_net_rx(struct net_device *dev)
        if (pkt_len > 0) {
                skb_trim(skb, pkt_len);
                skb->protocol = (*lp->protocol)(skb);
-               netif_rx(skb);
 
                lp->stats.rx_bytes += skb->len;
                lp->stats.rx_packets++;
+               netif_rx(skb);
                return pkt_len;
        }
 
@@ -275,7 +318,7 @@ static void setup_etheraddr(char *str, unsigned char *addr, char *name)
        if (str == NULL)
                goto random;
 
-       for (i = 0;i < 6; i++) {
+       for (i = 0; i < 6; i++) {
                addr[i] = simple_strtoul(str, &end, 16);
                if ((end == str) ||
                   ((*end != ':') && (*end != ',') && (*end != '\0'))) {
@@ -300,14 +343,13 @@ static void setup_etheraddr(char *str, unsigned char *addr, char *name)
        }
        if (!is_local_ether_addr(addr)) {
                printk(KERN_WARNING
-                      "Warning: attempt to assign a globally valid ethernet "
+                      "Warning: Assigning a globally valid ethernet "
                       "address to a device\n");
-               printk(KERN_WARNING "You should better enable the 2nd "
-                      "rightmost bit in the first byte of the MAC,\n");
+               printk(KERN_WARNING "You should set the 2nd rightmost bit in "
+                      "the first byte of the MAC,\n");
                printk(KERN_WARNING "i.e. %02x:%02x:%02x:%02x:%02x:%02x\n",
                       addr[0] | 0x02, addr[1], addr[2], addr[3], addr[4],
                       addr[5]);
-               goto random;
        }
        return;
 
@@ -325,7 +367,6 @@ static struct platform_driver uml_net_driver = {
                .name  = DRIVER_NAME,
        },
 };
-static int driver_registered;
 
 static void net_device_release(struct device *dev)
 {
@@ -340,6 +381,12 @@ static void net_device_release(struct device *dev)
        free_netdev(netdev);
 }
 
+/*
+ * Ensures that platform_driver_register is called only once by
+ * eth_configure.  Will be set in an initcall.
+ */
+static int driver_registered;
+
 static void eth_configure(int n, void *init, char *mac,
                          struct transport *transport)
 {
@@ -447,6 +494,10 @@ static void eth_configure(int n, void *init, char *mac,
        dev->watchdog_timeo = (HZ >> 1);
        dev->irq = UM_ETH_IRQ;
 
+       err = update_drop_skb(lp->max_packet);
+       if (err)
+               goto out_undo_user_init;
+
        rtnl_lock();
        err = register_netdevice(dev);
        rtnl_unlock();
@@ -706,6 +757,7 @@ static struct mc_device net_mc = {
        .remove         = net_remove,
 };
 
+#ifdef CONFIG_INET
 static int uml_inetaddr_event(struct notifier_block *this, unsigned long event,
                              void *ptr)
 {
@@ -742,14 +794,13 @@ struct notifier_block uml_inetaddr_notifier = {
        .notifier_call          = uml_inetaddr_event,
 };
 
-static int uml_net_init(void)
+static void inet_register(void)
 {
        struct list_head *ele;
        struct uml_net_private *lp;
        struct in_device *ip;
        struct in_ifaddr *in;
 
-       mconsole_register_dev(&net_mc);
        register_inetaddr_notifier(&uml_inetaddr_notifier);
 
        /* Devices may have been opened already, so the uml_inetaddr_notifier
@@ -769,7 +820,17 @@ static int uml_net_init(void)
                }
        }
        spin_unlock(&opened_lock);
+}
+#else
+static inline void inet_register(void)
+{
+}
+#endif
 
+static int uml_net_init(void)
+{
+       mconsole_register_dev(&net_mc);
+       inet_register();
        return 0;
 }