brcompat: Tolerate a race condition in deleting bridge ports.
authorBen Pfaff <blp@nicira.com>
Mon, 16 Mar 2009 20:14:18 +0000 (13:14 -0700)
committerBen Pfaff <blp@nicira.com>
Mon, 16 Mar 2009 20:14:18 +0000 (13:14 -0700)
When we delete a bridge port (e.g. "brctl delif"), the port could get
removed from the bridge we are interested in and then quickly added back
into another bridge while sleeping.  Return immediately in this case,
since the port must really have been deleted from the bridge in question.

There is a remaining race that the port could get deleted from the bridge
and then added back to the same one.

datapath/brcompat.c

index 709c838cc9b16a7ca2fb6fa41699c9425055898c..ce152a28da8b0917c513dc61242795c5b9443132 100644 (file)
@@ -402,15 +402,22 @@ int brc_send_port_add_del(struct net_device *dev, struct net_device *port,
                msleep(10);
                rtnl_lock();
 
+               /* Since we released the RTNL lock, we have to make sure that
+                * our devices still exist.  (But as long as they still exist,
+                * there's no point in refreshing 'dp' or 'dp_dev', since
+                * ifindexes are reused very slowly if ever.) */
                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;
+               if (add) {
+                       if (port->br_port)
+                               return port->br_port->dp == dp ? 0 : -EBUSY;
+               } else {
+                       if (!port->br_port || port->br_port->dp != dp)
+                               return 0;
+               }
        }
        return -ETIMEDOUT;