bridge: fix cleanup path, avoid double free on hotplug device remove
authorFelix Fietkau <nbd@openwrt.org>
Thu, 3 Nov 2011 23:47:40 +0000 (00:47 +0100)
committerFelix Fietkau <nbd@openwrt.org>
Thu, 3 Nov 2011 23:47:40 +0000 (00:47 +0100)
bridge.c

index 54526f74494c85011d020f0a5290fcaa6ca3e7d8..5ff7081cad5d63af4724df32ede73e25c1114bd8 100644 (file)
--- a/bridge.c
+++ b/bridge.c
@@ -118,6 +118,23 @@ error:
        return ret;
 }
 
+static void
+bridge_remove_member(struct bridge_member *bm)
+{
+       struct bridge_state *bst = bm->bst;
+
+       if (!bm->present)
+               return;
+
+       bm->present = false;
+       bm->bst->n_present--;
+       if (bst->dev.active)
+               bridge_disable_member(bm);
+
+       if (bst->n_present == 0)
+               device_set_present(&bst->dev, false);
+}
+
 static void
 bridge_member_cb(struct device_user *dev, enum device_event ev)
 {
@@ -141,16 +158,11 @@ bridge_member_cb(struct device_user *dev, enum device_event ev)
                if (!bm->present)
                        return;
 
-               if (bst->dev.active)
-                       bridge_disable_member(bm);
-
-               bm->present = false;
-               bm->bst->n_present--;
-               if (bst->n_present == 0)
-                       device_set_present(&bst->dev, false);
-
                if (dev->hotplug)
                        bridge_free_member(bm);
+               else
+                       bridge_remove_member(bm);
+
                break;
        default:
                return;
@@ -235,15 +247,9 @@ bridge_create_member(struct bridge_state *bst, struct device *dev, bool hotplug)
 static void
 bridge_free_member(struct bridge_member *bm)
 {
-       if (bm->present) {
-               bridge_member_cb(&bm->dev, DEV_EVENT_REMOVE);
-               bm->bst->n_present--;
-               if (bm->bst->dev.active)
-                       bridge_disable_member(bm);
-       }
-
-       list_del(&bm->list);
+       bridge_remove_member(bm);
        device_remove_user(&bm->dev);
+       list_del(&bm->list);
        free(bm);
 }