add code for versioned lists and use it to manage addresses and routes
authorFelix Fietkau <nbd@openwrt.org>
Wed, 7 Sep 2011 12:37:18 +0000 (14:37 +0200)
committerFelix Fietkau <nbd@openwrt.org>
Wed, 7 Sep 2011 12:37:18 +0000 (14:37 +0200)
interface-ip.c
interface-ip.h
interface.c
interface.h
proto-static.c
utils.c
utils.h

index b86acb5856033d240724a80e022f83b1d8e132c7..baa8b0e81dad85f25ee491c48cf8e497aacaecb3 100644 (file)
 #include "ubus.h"
 #include "system.h"
 
-int interface_add_address(struct interface *iface, struct device_addr *addr)
+static void
+interface_update_proto_addr(struct vlist_tree *tree,
+                           struct vlist_node *node_new,
+                           struct vlist_node *node_old)
 {
-       int family;
+       struct interface *iface;
+       struct device *dev;
+       struct device_addr *addr;
 
-       if (addr->flags & DEVADDR_INET6)
-               family = AF_INET6;
-       else
-               family = AF_INET;
+       iface = container_of(tree, struct interface, proto_addr);
+       dev = iface->l3_iface->dev;
 
-       list_add_tail(&addr->list, &iface->address);
-       return system_add_address(iface->l3_iface->dev, addr);
-}
-
-void interface_del_address(struct interface *iface, struct device_addr *addr)
-{
-       int family;
-
-       if (addr->flags & DEVADDR_INET6)
-               family = AF_INET6;
-       else
-               family = AF_INET;
+       if (node_old) {
+               addr = container_of(node_old, struct device_addr, node);
+               system_del_address(dev, addr);
+               free(addr);
+       }
 
-       list_del(&addr->list);
-       system_del_address(iface->l3_iface->dev, addr);
-       free(addr);
+       if (node_new) {
+               addr = container_of(node_new, struct device_addr, node);
+               system_add_address(dev, addr);
+       }
 }
 
-void interface_del_ctx_addr(struct interface *iface, void *ctx)
+static void
+interface_update_proto_route(struct vlist_tree *tree,
+                            struct vlist_node *node_new,
+                            struct vlist_node *node_old)
 {
-       struct device_addr *addr, *tmp;
+       struct interface *iface;
+       struct device *dev;
+       struct device_route *route;
 
-       list_for_each_entry_safe(addr, tmp, &iface->address, list) {
-               if (ctx && addr->ctx != ctx)
-                       continue;
+       iface = container_of(tree, struct interface, proto_route);
+       dev = iface->l3_iface->dev;
 
-               interface_del_address(iface, addr);
+       if (node_old) {
+               route = container_of(node_old, struct device_route, node);
+               system_del_route(dev, route);
+               free(route);
        }
-}
 
-int interface_add_route(struct interface *iface, struct device_route *route)
-{
-       list_add_tail(&route->list, &iface->routes);
-       return system_add_route(iface->l3_iface->dev, route);
+       if (node_new) {
+               route = container_of(node_new, struct device_route, node);
+               system_add_route(dev, route);
+       }
 }
 
-void interface_del_route(struct interface *iface, struct device_route *route)
+void
+interface_ip_init(struct interface *iface)
 {
-       list_del(&route->list);
-       system_del_route(iface->l3_iface->dev, route);
-       if (!route->keep)
-               free(route);
+       vlist_init(&iface->proto_route, interface_update_proto_route,
+                  struct device_route, node, mask, addr);
+       vlist_init(&iface->proto_addr, interface_update_proto_addr,
+                  struct device_addr, node, mask, addr);
 }
 
-void interface_del_all_routes(struct interface *iface)
-{
-       struct device_route *route, *tmp;
-
-       list_for_each_entry_safe(route, tmp, &iface->routes, list)
-               interface_del_route(iface, route);
-}
index e83e32726312d6b09f690841cb6290ce854c4d40..92667278096da88f4c8f41b4ea03f807c89a0307 100644 (file)
@@ -17,8 +17,7 @@ union if_addr {
 };
 
 struct device_addr {
-       struct list_head list;
-       void *ctx;
+       struct vlist_node node;
 
        enum device_addr_flags flags;
 
@@ -27,8 +26,7 @@ struct device_addr {
 };
 
 struct device_route {
-       struct list_head list;
-       void *ctx;
+       struct vlist_node node;
 
        enum device_addr_flags flags;
        bool keep;
@@ -38,12 +36,6 @@ struct device_route {
        union if_addr nexthop;
 };
 
-int interface_add_address(struct interface *iface, struct device_addr *addr);
-void interface_del_address(struct interface *iface, struct device_addr *addr);
-void interface_del_ctx_addr(struct interface *iface, void *ctx);
-
-int interface_add_route(struct interface *iface, struct device_route *route);
-void interface_del_route(struct interface *iface, struct device_route *route);
-void interface_del_all_routes(struct interface *iface);
+void interface_ip_init(struct interface *iface);
 
 #endif
index 387d3491dc602118f7c761e5563810a12c59e253..031959ccb4a52e9c18c77acb52499b7e8f054f86 100644 (file)
@@ -89,8 +89,8 @@ interface_event(struct interface *iface, enum interface_event ev)
 static void
 mark_interface_down(struct interface *iface)
 {
-       interface_del_all_routes(iface);
-       interface_del_ctx_addr(iface, NULL);
+       vlist_flush_all(&iface->proto_addr);
+       vlist_flush_all(&iface->proto_route);
        device_release(&iface->main_dev);
        iface->state = IFS_DOWN;
 }
@@ -129,8 +129,6 @@ __interface_set_down(struct interface *iface, bool force)
 
        iface->state = IFS_TEARDOWN;
        interface_event(iface, IFEV_DOWN);
-
-       interface_del_all_routes(iface);
        interface_proto_event(iface->proto, PROTO_CMD_TEARDOWN, force);
 }
 
@@ -221,8 +219,8 @@ interface_alloc(const char *name, struct blob_attr *attr)
        strncpy(iface->name, name, sizeof(iface->name) - 1);
        list_add_tail(&iface->list, &interfaces);
        INIT_LIST_HEAD(&iface->errors);
-       INIT_LIST_HEAD(&iface->address);
-       INIT_LIST_HEAD(&iface->routes);
+
+       interface_ip_init(iface);
 
        blobmsg_parse(iface_attrs, IFACE_ATTR_MAX, tb,
                      blob_data(attr), blob_len(attr));
index 0f2eb85c191dcdbff6891b3f67177287ec08383c..36bde4195c784e38892ed9ff3384f6699de04c89 100644 (file)
@@ -52,7 +52,8 @@ struct interface {
        const struct proto_handler *proto_handler;
        struct interface_proto_state *proto;
 
-       struct list_head address, routes;
+       struct vlist_tree proto_addr;
+       struct vlist_tree proto_route;
 
        /* errors/warnings while trying to bring up the interface */
        struct list_head errors;
index 04e2118058c2f3dbb7fe7c1e210a9f0131605ec8..3868ab77bc5e78d426adf480e8111e2aadc5e943 100644 (file)
@@ -89,14 +89,13 @@ parse_addr(struct interface_proto_state *state, const char *str, bool v6, int ma
 
        addr = calloc(1, sizeof(*addr));
        addr->flags = v6 ? DEVADDR_INET6 : DEVADDR_INET4;
-       addr->ctx = state;
        addr->mask = mask;
        if (!parse_ip_and_netmask(af, str, &addr->addr, &addr->mask)) {
                interface_add_error(state->iface, "proto-static", "INVALID_ADDRESS", &str, 1);
                free(addr);
                return false;
        }
-       interface_add_address(state->iface, addr);
+       vlist_add(&state->iface->proto_addr, &addr->node);
        return true;
 }
 
@@ -132,7 +131,7 @@ parse_gateway_option(struct interface_proto_state *state, struct blob_attr *attr
        }
        route->mask = 0;
        route->flags = DEVADDR_DEVICE | (v6 ? DEVADDR_INET6 : DEVADDR_INET4);
-       interface_add_route(state->iface, route);
+       vlist_add(&state->iface->proto_route, &route->node);
 
        return true;
 }
@@ -211,7 +210,6 @@ static_handler(struct interface_proto_state *proto,
 
                /* fall through */
        case PROTO_CMD_TEARDOWN:
-               interface_del_ctx_addr(state->proto.iface, proto);
                break;
        }
        return ret;
diff --git a/utils.c b/utils.c
index cf0718a1d598385da33eb2fc0388a0d8d390ad90..15f0525941b9b31792dc3a467f720cea7e1f9df2 100644 (file)
--- a/utils.c
+++ b/utils.c
@@ -1,8 +1,72 @@
 #include <string.h>
 #include "utils.h"
 
-int avl_strcmp(const void *k1, const void *k2, void *ptr)
+int
+avl_strcmp(const void *k1, const void *k2, void *ptr)
 {
        return strcmp(k1, k2);
 }
 
+static int
+vlist_cmp(const void *k1, const void *k2, void *ptr)
+{
+       struct vlist_tree *vl = ptr;
+       return memcmp(k1, k2, vl->data_len);
+}
+
+void
+__vlist_init(struct vlist_tree *tree, vlist_update_cb update, int offset, int len)
+{
+       tree->data_offset = offset;
+       tree->data_len = len;
+       tree->update = update;
+       tree->version = 1;
+
+       avl_init(&tree->avl, vlist_cmp, 0, tree);
+}
+
+void
+vlist_delete(struct vlist_tree *tree, struct vlist_node *node)
+{
+       avl_delete(&tree->avl, &node->avl);
+       tree->update(tree, NULL, node);
+}
+
+void
+vlist_add(struct vlist_tree *tree, struct vlist_node *node)
+{
+       struct vlist_node *old_node = NULL;
+       struct avl_node *anode;
+
+       node->avl.key = (char *) node + tree->data_offset;
+       node->version = tree->version;
+
+       anode = avl_find(&tree->avl, (char *) node + tree->data_offset);
+       if (anode) {
+               old_node = container_of(anode, struct vlist_node, avl);
+               avl_delete(&tree->avl, anode);
+       }
+
+       avl_insert(&tree->avl, &node->avl);
+       tree->update(tree, node, old_node);
+}
+
+void
+vlist_flush(struct vlist_tree *tree)
+{
+       struct vlist_node *node, *tmp;
+
+       avl_for_each_element_safe(&tree->avl, node, avl, tmp) {
+               if (node->version == tree->version)
+                       continue;
+
+               vlist_delete(tree, node);
+       }
+}
+
+void
+vlist_flush_all(struct vlist_tree *tree)
+{
+       tree->version++;
+       vlist_flush(tree);
+}
diff --git a/utils.h b/utils.h
index 09df8c6f3e03c795ff8610b275c6bc59c9a58fe1..e872dc55fff872080035fe550c798f280ca9ac78 100644 (file)
--- a/utils.h
+++ b/utils.h
@@ -2,6 +2,7 @@
 #define __NETIFD_UTILS_H
 
 #include <libubox/list.h>
+#include <libubox/avl.h>
 
 #ifdef DEBUG
 #define DPRINTF(format, ...) fprintf(stderr, "%s(%d): " format, __func__, __LINE__, ## __VA_ARGS__)
@@ -15,6 +16,40 @@ static inline void no_debug(const char *fmt, ...)
 
 #define __init __attribute__((constructor))
 
+struct vlist_tree;
+struct vlist_node;
+
+typedef void (*vlist_update_cb)(struct vlist_tree *tree,
+                               struct vlist_node *node_new,
+                               struct vlist_node *node_old);
+
+struct vlist_tree {
+       struct avl_tree avl;
+
+       vlist_update_cb update;
+
+       int data_offset;
+       int data_len;
+
+       int version;
+};
+
+struct vlist_node {
+       struct avl_node avl;
+       int version;
+};
+
+void __vlist_init(struct vlist_tree *tree, vlist_update_cb update, int offset, int len);
+
+#define vlist_init(tree, update, type, vlist, first, last) \
+       __vlist_init(tree, update, offsetof(type, first) - offsetof(type, vlist), \
+                    offsetof(type, last) - offsetof(type, first) + sizeof(((type *) 0)->last))
+
+void vlist_add(struct vlist_tree *tree, struct vlist_node *node);
+void vlist_delete(struct vlist_tree *tree, struct vlist_node *node);
+void vlist_flush(struct vlist_tree *tree);
+void vlist_flush_all(struct vlist_tree *tree);
+
 #ifdef __linux__
 static inline int fls(int x)
 {