From 10741b4974b85c825036eb0717ddc7112f96f5fb Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 26 Oct 2011 20:12:32 +0200 Subject: [PATCH] recognize stacked interfaces (e.g. dsa) and handle their dependencies --- device.c | 44 ++++++++++++++++++++++++++++++++++---------- device.h | 57 ++++++++++++++++++++++++++++++--------------------------- system-dummy.c | 6 ++++++ system-linux.c | 31 +++++++++++++++++++++++++++++++ system.h | 1 + 5 files changed, 102 insertions(+), 37 deletions(-) diff --git a/device.c b/device.c index 428d001..b2db291 100644 --- a/device.c +++ b/device.c @@ -55,6 +55,36 @@ void device_unlock(void) device_free_unused(NULL); } +static int set_device_state(struct device *dev, bool state) +{ + if (state) + system_if_up(dev); + else + system_if_down(dev); + + return 0; +} + +static int +simple_device_set_state(struct device *dev, bool state) +{ + int ret = 0; + + if (state && !dev->parent.dev) + dev->parent.dev = system_if_get_parent(dev); + + if (dev->parent.dev) { + if (state) + ret = device_claim(&dev->parent); + else + device_release(&dev->parent); + + if (ret < 0) + return ret; + } + return set_device_state(dev, state); +} + static struct device * simple_device_create(const char *name, struct blob_attr *attr) { @@ -66,6 +96,7 @@ simple_device_create(const char *name, struct blob_attr *attr) if (!dev) return NULL; + dev->set_state = simple_device_set_state; device_init_settings(dev, tb); return dev; @@ -73,6 +104,8 @@ simple_device_create(const char *name, struct blob_attr *attr) static void simple_device_free(struct device *dev) { + if (dev->parent.dev) + device_remove_user(&dev->parent); device_cleanup(dev); free(dev); } @@ -209,16 +242,6 @@ alias_notify_device(const char *name, struct device *dev) device_unlock(); } -static int set_device_state(struct device *dev, bool state) -{ - if (state) - system_if_up(dev); - else - system_if_down(dev); - - return 0; -} - int device_claim(struct device_user *dep) { struct device *dev = dep->dev; @@ -315,6 +338,7 @@ device_create_default(const char *name, bool external) D(DEVICE, "Create simple device '%s'\n", name); dev = calloc(1, sizeof(*dev)); dev->external = external; + dev->set_state = simple_device_set_state; device_init(dev, &simple_device_type, name); dev->default_config = true; return dev; diff --git a/device.h b/device.h index 057506b..a3728f3 100644 --- a/device.h +++ b/device.h @@ -5,6 +5,7 @@ #include struct device; +struct device_user; struct device_hotplug_ops; typedef int (*device_state_cb)(struct device *, bool up); @@ -45,6 +46,33 @@ enum { DEV_OPT_TXQUEUELEN = (1 << 2) }; +/* events broadcasted to all users of a device */ +enum device_event { + DEV_EVENT_ADD, + DEV_EVENT_REMOVE, + + DEV_EVENT_SETUP, + DEV_EVENT_TEARDOWN, + DEV_EVENT_UP, + DEV_EVENT_DOWN, + + DEV_EVENT_LINK_UP, + DEV_EVENT_LINK_DOWN, +}; + +/* + * device dependency with callbacks + */ +struct device_user { + struct list_head list; + + bool claimed; + bool hotplug; + + struct device *dev; + void (*cb)(struct device_user *, enum device_event); +}; + /* * link layer device. typically represents a linux network device. * can be used to support VLANs as well @@ -72,6 +100,8 @@ struct device { const struct device_hotplug_ops *hotplug_ops; + struct device_user parent; + /* settings */ unsigned int flags; @@ -80,33 +110,6 @@ struct device { uint8_t macaddr[6]; }; -/* events broadcasted to all users of a device */ -enum device_event { - DEV_EVENT_ADD, - DEV_EVENT_REMOVE, - - DEV_EVENT_SETUP, - DEV_EVENT_TEARDOWN, - DEV_EVENT_UP, - DEV_EVENT_DOWN, - - DEV_EVENT_LINK_UP, - DEV_EVENT_LINK_DOWN, -}; - -/* - * device dependency with callbacks - */ -struct device_user { - struct list_head list; - - bool claimed; - bool hotplug; - - struct device *dev; - void (*cb)(struct device_user *, enum device_event); -}; - struct device_hotplug_ops { int (*add)(struct device *main, struct device *member); int (*del)(struct device *main, struct device *member); diff --git a/system-dummy.c b/system-dummy.c index d518d6f..7d651a5 100644 --- a/system-dummy.c +++ b/system-dummy.c @@ -79,6 +79,12 @@ int system_if_check(struct device *dev) return 0; } +struct device * +system_if_get_parent(struct device *dev) +{ + return NULL; +} + int system_if_dump_stats(struct device *dev, struct blob_buf *b) { return 0; diff --git a/system-linux.c b/system-linux.c index 3ac5588..d26f181 100644 --- a/system-linux.c +++ b/system-linux.c @@ -607,6 +607,37 @@ int system_if_check(struct device *dev) return 0; } +struct device * +system_if_get_parent(struct device *dev) +{ + char buf[64], *devname; + int ifindex, iflink, len; + FILE *f; + + snprintf(buf, sizeof(buf), "/sys/class/net/%s/iflink", dev->ifname); + f = fopen(buf, "r"); + if (!f) + return NULL; + + len = fread(buf, 1, sizeof(buf) - 1, f); + fclose(f); + + if (len <= 0) + return NULL; + + buf[len] = 0; + iflink = strtoul(buf, NULL, 0); + ifindex = system_if_resolve(dev); + if (!iflink || iflink == ifindex) + return NULL; + + devname = if_indextoname(iflink, buf); + if (!devname) + return NULL; + + return device_get(devname, true); +} + int system_if_dump_stats(struct device *dev, struct blob_buf *b) { const char *const counters[] = { diff --git a/system.h b/system.h index dee7137..639dbc8 100644 --- a/system.h +++ b/system.h @@ -39,6 +39,7 @@ int system_if_up(struct device *dev); int system_if_down(struct device *dev); int system_if_check(struct device *dev); int system_if_dump_stats(struct device *dev, struct blob_buf *b); +struct device *system_if_get_parent(struct device *dev); int system_add_address(struct device *dev, struct device_addr *addr); int system_del_address(struct device *dev, struct device_addr *addr); -- 2.11.0