bridge: use vlist instead of list
[project/netifd.git] / bridge.c
index 70ea0f0..86787c7 100644 (file)
--- a/bridge.c
+++ b/bridge.c
@@ -65,20 +65,19 @@ struct bridge_state {
        struct bridge_config config;
        struct blob_attr *ifnames;
        bool active;
+       bool force_active;
 
-       struct list_head members;
+       struct vlist_tree members;
        int n_present;
 };
 
 struct bridge_member {
-       struct list_head list;
+       struct vlist_node node;
        struct bridge_state *bst;
        struct device_user dev;
        bool present;
 };
 
-static void bridge_free_member(struct bridge_member *bm);
-
 static int
 bridge_disable_member(struct bridge_member *bm)
 {
@@ -119,6 +118,24 @@ error:
 }
 
 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);
+
+       bst->force_active = false;
+       if (bst->n_present == 0)
+               device_set_present(&bst->dev, false);
+}
+
+static void
 bridge_member_cb(struct device_user *dev, enum device_event ev)
 {
        struct bridge_member *bm = container_of(dev, struct bridge_member, dev);
@@ -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);
+                       vlist_delete(&bst->members, &bm->node);
+               else
+                       bridge_remove_member(bm);
+
                break;
        default:
                return;
@@ -164,7 +176,7 @@ bridge_set_down(struct bridge_state *bst)
 
        bst->set_state(&bst->dev, false);
 
-       list_for_each_entry(bm, &bst->members, list)
+       vlist_for_each_element(&bst->members, bm, node)
                bridge_disable_member(bm);
 
        system_bridge_delbr(&bst->dev);
@@ -178,17 +190,17 @@ bridge_set_up(struct bridge_state *bst)
        struct bridge_member *bm;
        int ret;
 
-       if (!bst->n_present)
+       if (!bst->force_active && !bst->n_present)
                return -ENOENT;
 
        ret = system_bridge_addbr(&bst->dev, &bst->config);
        if (ret < 0)
                goto out;
 
-       list_for_each_entry(bm, &bst->members, list)
+       vlist_for_each_element(&bst->members, bm, node)
                bridge_enable_member(bm);
 
-       if (!bst->n_present) {
+       if (!bst->force_active && !bst->n_present) {
                /* initialization of all member interfaces failed */
                system_bridge_delbr(&bst->dev);
                device_set_present(&bst->dev, false);
@@ -220,12 +232,15 @@ static struct bridge_member *
 bridge_create_member(struct bridge_state *bst, struct device *dev, bool hotplug)
 {
        struct bridge_member *bm;
+       char *name;
 
-       bm = calloc(1, sizeof(*bm));
+       bm = calloc(1, sizeof(*bm) + strlen(dev->ifname) + 1);
        bm->bst = bst;
        bm->dev.cb = bridge_member_cb;
        bm->dev.hotplug = hotplug;
-       list_add_tail(&bm->list, &bst->members);
+       name = (char *) (bm + 1);
+       strcpy(name, dev->ifname);
+       vlist_add(&bst->members, &bm->node, name);
 
        device_add_user(&bm->dev, dev);
 
@@ -233,20 +248,27 @@ bridge_create_member(struct bridge_state *bst, struct device *dev, bool hotplug)
 }
 
 static void
-bridge_free_member(struct bridge_member *bm)
+bridge_member_update(struct vlist_tree *tree, struct vlist_node *node_new,
+                    struct vlist_node *node_old)
 {
-       if (bm->present) {
-               bridge_member_cb(&bm->dev, DEV_EVENT_REMOVE);
-               bm->bst->n_present--;
-               if (bm->bst->dev.active)
-                       bridge_disable_member(bm);
+       struct bridge_member *bm;
+
+       if (node_new && node_old)
+               return;
+
+       if (node_old) {
+               bm = container_of(node_old, struct bridge_member, node);
+               bridge_remove_member(bm);
+               device_remove_user(&bm->dev);
+               free(bm);
        }
 
-       list_del(&bm->list);
-       device_remove_user(&bm->dev);
-       free(bm);
+       if (node_new) {
+               bm = container_of(node_new, struct bridge_member, node);
+       }
 }
 
+
 static void
 bridge_add_member(struct bridge_state *bst, const char *name)
 {
@@ -275,18 +297,28 @@ bridge_hotplug_del(struct device *dev, struct device *member)
        struct bridge_state *bst = container_of(dev, struct bridge_state, dev);
        struct bridge_member *bm;
 
-       list_for_each_entry(bm, &bst->members, list) {
-               if (bm->dev.dev != member)
-                       continue;
+       bm = vlist_find(&bst->members, member->ifname, bm, node);
+       if (!bm)
+               return UBUS_STATUS_NOT_FOUND;
 
-               bridge_free_member(bm);
-               return 0;
-       }
+       vlist_delete(&bst->members, &bm->node);
+       return 0;
+}
+
+static int
+bridge_hotplug_prepare(struct device *dev)
+{
+       struct bridge_state *bst;
+
+       bst = container_of(dev, struct bridge_state, dev);
+       bst->force_active = true;
+       device_set_present(&bst->dev, true);
 
-       return -ENOENT;
+       return 0;
 }
 
 static const struct device_hotplug_ops bridge_ops = {
+       .prepare = bridge_hotplug_prepare,
        .add = bridge_hotplug_add,
        .del = bridge_hotplug_del
 };
@@ -295,14 +327,10 @@ static void
 bridge_free(struct device *dev)
 {
        struct bridge_state *bst;
-       struct bridge_member *bm;
 
        device_cleanup(dev);
        bst = container_of(dev, struct bridge_state, dev);
-       while (!list_empty(&bst->members)) {
-               bm = list_first_entry(&bst->members, struct bridge_member, list);
-               bridge_free_member(bm);
-       }
+       vlist_flush_all(&bst->members);
        free(bst);
 }
 
@@ -315,10 +343,12 @@ bridge_dump_info(struct device *dev, struct blob_buf *b)
 
        bst = container_of(dev, struct bridge_state, dev);
 
+       system_if_dump_info(dev, b);
        list = blobmsg_open_array(b, "bridge-members");
-       list_for_each_entry(bm, &bst->members, list) {
+
+       vlist_for_each_element(&bst->members, bm, node)
                blobmsg_add_string(b, NULL, bm->dev.dev->ifname);
-       }
+
        blobmsg_close_array(b, list);
 }
 
@@ -330,6 +360,10 @@ bridge_config_init(struct device *dev)
        int rem;
 
        bst = container_of(dev, struct bridge_state, dev);
+
+       if (!bst->ifnames)
+               return;
+
        blobmsg_for_each_attr(cur, bst->ifnames, rem) {
                bridge_add_member(bst, blobmsg_data(cur));
        }
@@ -384,9 +418,6 @@ bridge_create(const char *name, struct blob_attr *attr)
        blobmsg_parse(bridge_attrs, __BRIDGE_ATTR_MAX, tb_br,
                blob_data(attr), blob_len(attr));
 
-       if (!tb_br[BRIDGE_ATTR_IFNAME])
-               return NULL;
-
        bst = calloc(1, sizeof(*bst));
        if (!bst)
                return NULL;
@@ -403,7 +434,7 @@ bridge_create(const char *name, struct blob_attr *attr)
 
        dev->hotplug_ops = &bridge_ops;
 
-       INIT_LIST_HEAD(&bst->members);
+       vlist_init(&bst->members, avl_strcmp, bridge_member_update);
 
        return dev;
 }