brcompat: When adding or removing datapath ports, wait for them to appear/disappear.
authorBen Pfaff <blp@nicira.com>
Sun, 15 Mar 2009 01:01:41 +0000 (18:01 -0700)
committerBen Pfaff <blp@nicira.com>
Sun, 15 Mar 2009 01:01:41 +0000 (18:01 -0700)
datapath/brcompat.c
datapath/linux-2.6/compat-2.6/compat26.h

index 02a15b82e3ff3071da6a2c2242cb5ba26486833f..10d8c8cece24232fa257910dfbe82e5363a78fdb 100644 (file)
@@ -1,10 +1,12 @@
 #include <linux/kernel.h>
 #include <asm/uaccess.h>
 #include <linux/completion.h>
+#include <linux/delay.h>
 #include <linux/etherdevice.h>
 #include <linux/if_bridge.h>
 #include <linux/rculist.h>
 #include <linux/netdevice.h>
+#include <linux/rtnetlink.h>
 #include <net/genetlink.h>
 
 #include "compat.h"
@@ -157,12 +159,11 @@ old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
                struct net_device *port;
                int err;
 
-               port = dev_get_by_index(&init_net, args[1]);
+               port = __dev_get_by_index(&init_net, args[1]);
                if (!port) 
                        return -EINVAL;
 
                err = brc_send_port_add_del(dev, port, args[0] == BRCTL_ADD_IF);
-               dev_put(port);
                return err;
        }
 
@@ -225,12 +226,11 @@ brc_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
                case SIOCBRDELIF: {
                        struct net_device *port;
 
-                       port = dev_get_by_index(&init_net, rq->ifr_ifindex);
+                       port = __dev_get_by_index(&init_net, rq->ifr_ifindex);
                        if (!port) 
                                return -EINVAL;
 
                        err = brc_send_port_add_del(dev, port, cmd == SIOCBRADDIF);
-                       dev_put(port);
                        break;
                }
 
@@ -358,7 +358,9 @@ int brc_send_port_add_del(struct net_device *dev, struct net_device *port,
        struct dp_dev *dp_dev = netdev_priv(dev);
        struct datapath *dp = dp_dev->dp;
        struct sk_buff *skb;
+       int error;
        void *data;
+       int i;
 
        skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
        if (skb == NULL)
@@ -388,7 +390,29 @@ int brc_send_port_add_del(struct net_device *dev, struct net_device *port,
        NLA_PUT_STRING(skb, BRC_GENL_A_PORT_NAME, port->name);
 
        genlmsg_end(skb, data);
-       return genlmsg_multicast(skb, 0, brc_mc_group.id, GFP_KERNEL);
+       error = genlmsg_multicast(skb, 0, brc_mc_group.id, GFP_KERNEL);
+       if (error)
+               return error;
+
+       for (i = 0; i < 100; i++) {
+               int dev_ifindex = dev->ifindex;
+               int port_ifindex = port->ifindex;
+
+               rtnl_unlock();
+               msleep(10);
+               rtnl_lock();
+
+               dev = __dev_get_by_index(&init_net, dev_ifindex);
+               port = __dev_get_by_index(&init_net, port_ifindex);
+               if (!dev || !port)
+                       return ENODEV;
+
+               if (add && port->br_port)
+                       return port->br_port->dev ? 0 : -EBUSY;
+               else if (!add && !port->br_port)
+                       return 0;
+       }
+       return -ETIMEDOUT;
 
 nla_put_failure:
 err:
index 37f6a4f8dfae2058e242cbc61a9d6cd42e7d2996..61448d635370c74dd518f5380bf0390221e2aa1d 100644 (file)
 #define dev_get_by_index(net, ifindex) \
                dev_get_by_index((ifindex))
 
+#define __dev_get_by_name(net, name) \
+               __dev_get_by_name((name))
+
+#define __dev_get_by_index(net, ifindex) \
+               __dev_get_by_index((ifindex))
+
 #endif /* linux kernel <= 2.6.23 */