From f87baeb3e0096a43d4647f549078c53bec68543f Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 10 Jun 2009 14:37:36 +0000 Subject: [PATCH] wprobe: move measurement task to the kernel, add some configurability (work in progress) SVN-Revision: 16402 --- package/wprobe/Makefile | 5 +- package/wprobe/files/wprobe.config | 7 + package/wprobe/files/wprobe.init | 23 ++- package/wprobe/src/exporter/wprobe-export.c | 44 ++--- package/wprobe/src/kernel/linux/wprobe.h | 39 +++- package/wprobe/src/kernel/wprobe-core.c | 196 +++++++++++++++++--- package/wprobe/src/user/wprobe-info.c | 117 ++++++++---- package/wprobe/src/user/wprobe.c | 139 +++++++++++--- package/wprobe/src/user/wprobe.h | 60 +++--- 9 files changed, 475 insertions(+), 155 deletions(-) create mode 100644 package/wprobe/files/wprobe.config diff --git a/package/wprobe/Makefile b/package/wprobe/Makefile index fe96512e29..a28ad930d5 100644 --- a/package/wprobe/Makefile +++ b/package/wprobe/Makefile @@ -45,7 +45,7 @@ endef define Package/wprobe-export SECTION:=net CATEGORY:=Network - DEPENDS:=+kmod-wprobe +libnl-tiny + DEPENDS:=+wprobe-info TITLE:=Wireless measurement data exporter endef @@ -113,8 +113,9 @@ define Package/wprobe-info/install endef define Package/wprobe-export/install - $(INSTALL_DIR) $(1)/sbin $(1)/etc/init.d + $(INSTALL_DIR) $(1)/sbin $(1)/etc/init.d $(1)/etc/config $(INSTALL_BIN) ./files/wprobe.init $(1)/etc/init.d/ + $(INSTALL_BIN) ./files/wprobe.config $(1)/etc/config/wprobe $(INSTALL_BIN) $(PKG_BUILD_DIR)/exporter/wprobe-export $(1)/sbin/ endef diff --git a/package/wprobe/files/wprobe.config b/package/wprobe/files/wprobe.config new file mode 100644 index 0000000000..8494bccb57 --- /dev/null +++ b/package/wprobe/files/wprobe.config @@ -0,0 +1,7 @@ +config export + # uncomment this line to enable ipfix export: + # option type ipfix + option ifname ath0 + option host ipfix-col + option proto tcp + diff --git a/package/wprobe/files/wprobe.init b/package/wprobe/files/wprobe.init index 3c62a03066..cf0b162181 100755 --- a/package/wprobe/files/wprobe.init +++ b/package/wprobe/files/wprobe.init @@ -1,10 +1,11 @@ #!/bin/sh /etc/rc.common START=90 +EXPORTER=/usr/sbin/wprobe-ipfix wprobe_ssd() { local cfg="$1"; shift local cmd="$1"; shift - start-stop-daemon "$cmd" -p "/var/run/wprobe-$cfg.pid" -b -x /sbin/wprobe-export -m -- "$@" + start-stop-daemon "$cmd" -p "/var/run/wprobe-$cfg.pid" -b -x "$EXPORTER" -m -- "$@" } stop_wprobe() { @@ -13,7 +14,14 @@ stop_wprobe() { rm -f "/var/run/wprobe-$cfg.pid" } -start_wprobe() { +config_wprobe() { + config_get ifname "$cfg" ifname + config_get interval "$cfg" interval + [ -n "$interval" ] || interval=100 + wprobe-info "$ifname" -c -i "$interval" +} + +start_ipfix() { local cfg="$1" config_get ifname "$cfg" interface config_get host "$cfg" host @@ -29,9 +37,17 @@ start_wprobe() { echo "wprobe-export: missing host or interface name in config $cfg" return } + config_wprobe "$cfg" wprobe_ssd "$cfg" -S "$proto" -i "$ifname" -c "$host" -p "${port:-4739}" } +start_export() { + config_get export_type "$cfg" type + case "$export_type" in + ipfix) start_ipfix "$cfg";; + esac +} + stop() { for f in /var/run/wprobe-*.pid; do CFG="${f%%.pid}" @@ -42,5 +58,6 @@ stop() { start() { config_load wprobe - config_foreach start_wprobe wprobe + config_foreach config_wprobe interface + [ -x "$EXPORTER" ] && config_foreach start_export export } diff --git a/package/wprobe/src/exporter/wprobe-export.c b/package/wprobe/src/exporter/wprobe-export.c index e9aa3de8c4..fa32e8b288 100644 --- a/package/wprobe/src/exporter/wprobe-export.c +++ b/package/wprobe/src/exporter/wprobe-export.c @@ -65,8 +65,8 @@ static struct wprobe_mapping map_globals[] = { }; static struct wprobe_mapping map_perlink[] = { - WMAP(IEEE_TX_RATE, "tx_rate", .scale = 10.0f), - WMAP(IEEE_RX_RATE, "rx_rate", .scale = 10.0f), + WMAP(IEEE_TX_RATE, "tx_rate"), + WMAP(IEEE_RX_RATE, "rx_rate"), WMAP(RSSI, "rssi"), WMAP(SIGNAL, "signal"), WMAP(RETRANSMIT_200, "retransmit_200"), @@ -77,9 +77,6 @@ static struct wprobe_mapping map_perlink[] = { static unsigned char link_local[6]; static char link_default[6]; -static LIST_HEAD(global_attr); -static LIST_HEAD(link_attr); -static LIST_HEAD(links); static int nfields = 0; #define FOKUS_USERID 12325 @@ -166,20 +163,20 @@ add_template_fields(ipfix_t *handle, ipfix_template_t *t, struct wprobe_mapping } static void -wprobe_dump_data(ipfix_t *ipfixh, ipfix_template_t *ipfixt, const char *ifname, struct list_head *gl, struct list_head *ll, struct list_head *ls) +wprobe_dump_data(ipfix_t *ipfixh, ipfix_template_t *ipfixt, struct wprobe_iface *dev) { struct wprobe_link *link; - wprobe_update_links(ifname, ls); - wprobe_request_data(ifname, gl, NULL, 2); - if (list_empty(ls)) { + wprobe_update_links(dev); + wprobe_request_data(dev, NULL); + if (list_empty(&dev->links)) { g_data.addrs[1] = link_default; ipfix_export_array(ipfixh, ipfixt, g_data.maxfields, g_data.addrs, g_data.lens); ipfix_export_flush(ipfixh); } - list_for_each_entry(link, ls, list) { + list_for_each_entry(link, &dev->links, list) { g_data.addrs[1] = link->addr; - wprobe_request_data(ifname, ll, link->addr, 2); + wprobe_request_data(dev, link->addr); ipfix_export_array(ipfixh, ipfixt, g_data.maxfields, g_data.addrs, g_data.lens); ipfix_export_flush(ipfixh); } @@ -187,6 +184,7 @@ wprobe_dump_data(ipfix_t *ipfixh, ipfix_template_t *ipfixt, const char *ifname, int main ( int argc, char **argv ) { + struct wprobe_iface *dev = NULL; ipfix_template_t *ipfixt = NULL; ipfix_t *ipfixh = NULL; int protocol = IPFIX_PROTO_TCP; @@ -254,20 +252,14 @@ int main ( int argc, char **argv ) return -1; } - if (wprobe_init() != 0) { - fprintf(stderr, "wprobe init failed\n"); - return -1; - } - - wprobe_dump_attributes(ifname, false, &global_attr, (char *) link_local); - wprobe_dump_attributes(ifname, true, &link_attr, NULL); - if (list_empty(&global_attr) && list_empty(&link_attr)) { + dev = wprobe_get_dev(ifname); + if (!dev || (list_empty(&dev->global_attr) && list_empty(&dev->link_attr))) { fprintf(stderr, "Cannot connect to wprobe on interface '%s'\n", ifname); return -1; } - match_template(map_globals, ARRAY_SIZE(map_globals), &global_attr); - match_template(map_perlink, ARRAY_SIZE(map_perlink), &link_attr); + match_template(map_globals, ARRAY_SIZE(map_globals), &dev->global_attr); + match_template(map_perlink, ARRAY_SIZE(map_perlink), &dev->link_attr); if (nfields == 0) { fprintf(stderr, "No usable attributes found\n"); return -1; @@ -300,14 +292,8 @@ int main ( int argc, char **argv ) add_template_fields(ipfixh, ipfixt, map_perlink, ARRAY_SIZE(map_perlink)); while (!do_close) { - usleep(100 * 1000); - wprobe_measure(ifname); - - if (i-- > 0) - continue; - - i = 10; - wprobe_dump_data(ipfixh, ipfixt, ifname, &global_attr, &link_attr, &links); + sleep(1); + wprobe_dump_data(ipfixh, ipfixt, dev); } ipfix_delete_template( ipfixh, ipfixt ); diff --git a/package/wprobe/src/kernel/linux/wprobe.h b/package/wprobe/src/kernel/linux/wprobe.h index f145195cd4..42bfbd0568 100644 --- a/package/wprobe/src/kernel/linux/wprobe.h +++ b/package/wprobe/src/kernel/linux/wprobe.h @@ -32,13 +32,12 @@ * * @WPROBE_ATTR_INTERFACE: interface name to process query on (NLA_STRING) * @WPROBE_ATTR_MAC: mac address (used for wireless links) (NLA_STRING) - * @WPROBE_ATTR_FLAGS: interface/link/attribute flags (see enum wprobe_flags) (NLA_U32) - * @WPROBE_ATTR_DURATION: sampling duration (in milliseconds) (NLA_MSECS) + * @WPROBE_ATTR_FLAGS: interface/link/attribute flags (see enum wprobe_flags) (NLA_U32)a + * @WPROBE_ATTR_DURATION: sampling duration (in milliseconds) (NLA_MSECS) * * @WPROBE_ATTR_ID: attribute id (NLA_U32) * @WPROBE_ATTR_NAME: attribute name (NLA_STRING) * @WPROBE_ATTR_TYPE: attribute type (NLA_U8) - * @WPROBE_ATTR_SCALE: attribute scale factor (NLA_U32) * * attribute values: * @@ -56,22 +55,29 @@ * @WPROBE_VAL_SUM: sum of all samples * @WPROBE_VAL_SUM_SQ: sum of all samples^2 * @WPROBE_VAL_SAMPLES: number of samples + * @WPROBE_VAL_SCALE_TIME: last time the samples were scaled down + * + * configuration: + * @WPROBE_ATTR_INTERVAL: (measurement interval in milliseconds) (NLA_MSECS) + * @WPROBE_ATTR_SAMPLES_MIN: minimum samples to keep during inactivity (NLA_U32) + * @WPROBE_ATTR_SAMPLES_MAX: maximum samples to keep before scaling down (NLA_U32) + * @WPROBE_ATTR_SAMPLES_SCALE_M: multiplier for scaling down samples (NLA_U32) + * @WPROBE_ATTR_SAMPLES_SCALE_D: divisor for scaling down samples (NLA_U32) * * @WPROBE_ATTR_LAST: unused */ enum wprobe_attr { + /* query attributes */ WPROBE_ATTR_UNSPEC, WPROBE_ATTR_INTERFACE, WPROBE_ATTR_MAC, WPROBE_ATTR_FLAGS, - WPROBE_ATTR_DURATION, - WPROBE_ATTR_SCALE, - /* end of query attributes */ /* response data */ WPROBE_ATTR_ID, WPROBE_ATTR_NAME, WPROBE_ATTR_TYPE, + WPROBE_ATTR_DURATION, /* value type attributes */ WPROBE_VAL_STRING, @@ -88,6 +94,14 @@ enum wprobe_attr { WPROBE_VAL_SUM, WPROBE_VAL_SUM_SQ, WPROBE_VAL_SAMPLES, + WPROBE_VAL_SCALE_TIME, + + /* config attributes */ + WPROBE_ATTR_INTERVAL, + WPROBE_ATTR_SAMPLES_MIN, + WPROBE_ATTR_SAMPLES_MAX, + WPROBE_ATTR_SAMPLES_SCALE_M, + WPROBE_ATTR_SAMPLES_SCALE_D, WPROBE_ATTR_LAST }; @@ -117,6 +131,7 @@ enum wprobe_cmd { WPROBE_CMD_SET_FLAGS, WPROBE_CMD_MEASURE, WPROBE_CMD_GET_LINKS, + WPROBE_CMD_CONFIG, WPROBE_CMD_LAST }; @@ -192,6 +207,7 @@ struct wprobe_value { /* timestamps */ u64 first, last; + u64 scale_timestamp; }; /** @@ -225,7 +241,8 @@ struct wprobe_iface { const struct wprobe_item *global_items; int n_global_items; - int (*sync_data)(struct wprobe_iface *dev, struct wprobe_link *l, struct wprobe_value *val, bool measure); + int (*sync_data)(struct wprobe_iface *dev, struct wprobe_link *l, + struct wprobe_value *val, bool measure); void *priv; /* handled by the wprobe core */ @@ -234,6 +251,14 @@ struct wprobe_iface { spinlock_t lock; void *val; void *query_val; + + u32 measure_interval; + struct timer_list measure_timer; + + u32 scale_min; + u32 scale_max; + u32 scale_m; + u32 scale_d; }; #define WPROBE_FILL_BEGIN(_ptr, _list) do { \ diff --git a/package/wprobe/src/kernel/wprobe-core.c b/package/wprobe/src/kernel/wprobe-core.c index 798cd7ddec..51ee7bc1da 100644 --- a/package/wprobe/src/kernel/wprobe-core.c +++ b/package/wprobe/src/kernel/wprobe-core.c @@ -34,6 +34,8 @@ #define list_for_each_rcu __list_for_each_rcu #endif +#define WPROBE_MIN_INTERVAL 100 /* minimum measurement interval in msecs */ + static struct list_head wprobe_if; static spinlock_t wprobe_lock; @@ -43,10 +45,11 @@ static struct genl_family wprobe_fam = { .hdrsize = 0, .version = 1, /* only the first set of attributes is used for queries */ - .maxattr = WPROBE_ATTR_ID, + .maxattr = WPROBE_ATTR_LAST, }; static void wprobe_update_stats(struct wprobe_iface *dev, struct wprobe_link *l); +static int wprobe_sync_data(struct wprobe_iface *dev, struct wprobe_link *l, bool query); int wprobe_add_link(struct wprobe_iface *s, struct wprobe_link *l, const char *addr) @@ -81,6 +84,19 @@ wprobe_remove_link(struct wprobe_iface *s, struct wprobe_link *l) } EXPORT_SYMBOL(wprobe_remove_link); +static void +wprobe_measure_timer(unsigned long data) +{ + struct wprobe_iface *dev = (struct wprobe_iface *) data; + + /* set next measurement interval */ + mod_timer(&dev->measure_timer, jiffies + + msecs_to_jiffies(dev->measure_interval)); + + /* perform measurement */ + wprobe_sync_data(dev, NULL, false); +} + int wprobe_add_iface(struct wprobe_iface *s) { @@ -93,6 +109,7 @@ wprobe_add_iface(struct wprobe_iface *s) BUG_ON(!s->name); INIT_LIST_HEAD(&s->list); INIT_LIST_HEAD(&s->links); + setup_timer(&s->measure_timer, wprobe_measure_timer, (unsigned long) s); vsize = max(s->n_link_items, s->n_global_items); s->val = kzalloc(sizeof(struct wprobe_value) * vsize, GFP_ATOMIC); @@ -103,6 +120,15 @@ wprobe_add_iface(struct wprobe_iface *s) if (!s->query_val) goto error; + /* initialize defaults to be able to handle overflow, + * user space will need to handle this if it keeps an + * internal histogram */ + s->scale_min = 20; + s->scale_max = (1 << 31); + + s->scale_m = 1; + s->scale_d = 10; + spin_lock_irqsave(&wprobe_lock, flags); list_add_rcu(&s->list, &wprobe_if); spin_unlock_irqrestore(&wprobe_lock, flags); @@ -123,6 +149,7 @@ wprobe_remove_iface(struct wprobe_iface *s) BUG_ON(!list_empty(&s->links)); + del_timer_sync(&s->measure_timer); spin_lock_irqsave(&wprobe_lock, flags); list_del_rcu(&s->list); spin_unlock_irqrestore(&wprobe_lock, flags); @@ -160,7 +187,7 @@ wprobe_get_dev(struct nlattr *attr) return dev; } -int +static int wprobe_sync_data(struct wprobe_iface *dev, struct wprobe_link *l, bool query) { struct wprobe_value *val; @@ -190,11 +217,40 @@ done: } EXPORT_SYMBOL(wprobe_sync_data); +static void +wprobe_scale_stats(struct wprobe_iface *dev, const struct wprobe_item *item, + struct wprobe_value *val, int n) +{ + u64 scale_ts = jiffies_64; + int i; + + for (i = 0; i < n; i++) { + if (!(item[i].flags & WPROBE_F_KEEPSTAT)) + continue; + + if (val[i].n <= dev->scale_min) + continue; + + /* FIXME: div_s64 seems to be very imprecise here, even when + * the values are scaled up */ + val[i].s *= dev->scale_m; + val[i].s = div_s64(val[i].s, dev->scale_d); + + val[i].ss *= dev->scale_m; + val[i].ss = div_s64(val[i].ss, dev->scale_d); + + val[i].n = (val[i].n * dev->scale_m) / dev->scale_d; + val[i].scale_timestamp = scale_ts; + } +} + + void wprobe_update_stats(struct wprobe_iface *dev, struct wprobe_link *l) { const struct wprobe_item *item; struct wprobe_value *val; + bool scale_stats = false; int i, n; if (l) { @@ -215,6 +271,10 @@ wprobe_update_stats(struct wprobe_iface *dev, struct wprobe_link *l) continue; val[i].n++; + if ((item[i].flags & WPROBE_F_KEEPSTAT) && + (dev->scale_max > 0) && (val[i].n > dev->scale_max)) { + scale_stats = true; + } switch(item[i].type) { case WPROBE_VAL_S8: @@ -249,15 +309,22 @@ wprobe_update_stats(struct wprobe_iface *dev, struct wprobe_link *l) val[i].ss += v * v; val[i].pending = false; } + if (scale_stats) + wprobe_scale_stats(dev, item, val, n); } EXPORT_SYMBOL(wprobe_update_stats); -static const struct nla_policy wprobe_policy[WPROBE_ATTR_ID+1] = { +static const struct nla_policy wprobe_policy[WPROBE_ATTR_LAST+1] = { [WPROBE_ATTR_INTERFACE] = { .type = NLA_NUL_STRING }, [WPROBE_ATTR_MAC] = { .type = NLA_STRING }, - [WPROBE_ATTR_DURATION] = { .type = NLA_MSECS }, [WPROBE_ATTR_FLAGS] = { .type = NLA_U32 }, - [WPROBE_ATTR_SCALE] = { .type = NLA_U32 }, + + /* config */ + [WPROBE_ATTR_INTERVAL] = { .type = NLA_MSECS }, + [WPROBE_ATTR_SAMPLES_MIN] = { .type = NLA_U32 }, + [WPROBE_ATTR_SAMPLES_MAX] = { .type = NLA_U32 }, + [WPROBE_ATTR_SAMPLES_SCALE_M] = { .type = NLA_U32 }, + [WPROBE_ATTR_SAMPLES_SCALE_D] = { .type = NLA_U32 }, }; static bool @@ -322,6 +389,7 @@ wprobe_send_item_value(struct sk_buff *msg, struct netlink_callback *cb, NLA_PUT_U64(msg, WPROBE_VAL_SUM, val[i].s); NLA_PUT_U64(msg, WPROBE_VAL_SUM_SQ, val[i].ss); NLA_PUT_U32(msg, WPROBE_VAL_SAMPLES, (u32) val[i].n); + NLA_PUT_MSECS(msg, WPROBE_VAL_SCALE_TIME, val[i].scale_timestamp); } done: genlmsg_end(msg, hdr); @@ -432,29 +500,6 @@ wprobe_dump_links(struct sk_buff *skb, struct netlink_callback *cb) done: return err; } -static void -wprobe_scale_stats(const struct wprobe_item *item, struct wprobe_value *val, int n, u32 flags) -{ - u32 scale = 0; - int i; - - for (i = 0; i < n; i++) { - if (!(item[i].flags & WPROBE_F_KEEPSTAT)) - continue; - - /* reset statistics, if requested */ - if (flags & WPROBE_F_RESET) - scale = val[i].n; - else if (wprobe_fam.attrbuf[WPROBE_ATTR_SCALE]) - scale = nla_get_u32(wprobe_fam.attrbuf[WPROBE_ATTR_SCALE]); - - if ((scale > 0) && (val[i].n > scale)) { - val[i].s = div_s64(val[i].s, scale); - val[i].ss = div_s64(val[i].ss, scale); - val[i].n = val[i].n / scale + 1; - } - } -} #define WPROBE_F_LINK (1 << 31) /* for internal use */ static int @@ -515,7 +560,6 @@ wprobe_dump_info(struct sk_buff *skb, struct netlink_callback *cb) err = wprobe_sync_data(dev, l, true); if (!err) memcpy(dev->query_val, val, n * sizeof(struct wprobe_value)); - wprobe_scale_stats(item, val, n, flags); spin_unlock_irqrestore(&dev->lock, flags); if (err) @@ -582,6 +626,25 @@ done: } #undef WPROBE_F_LINK +static int +wprobe_update_auto_measurement(struct wprobe_iface *dev, u32 interval) +{ + if (interval && (interval < WPROBE_MIN_INTERVAL)) + return -EINVAL; + + if (!interval && dev->measure_interval) + del_timer_sync(&dev->measure_timer); + + dev->measure_interval = interval; + if (!interval) + return 0; + + /* kick of a new measurement immediately */ + mod_timer(&dev->measure_timer, jiffies + 1); + + return 0; +} + static int wprobe_measure(struct sk_buff *skb, struct genl_info *info) { @@ -607,6 +670,75 @@ done: return err; } +static int +wprobe_set_config(struct sk_buff *skb, struct genl_info *info) +{ + struct wprobe_iface *dev; + unsigned long flags; + int err = -ENOENT; + u32 scale_min, scale_max; + u32 scale_m, scale_d; + + rcu_read_lock(); + dev = wprobe_get_dev(info->attrs[WPROBE_ATTR_INTERFACE]); + if (!dev) + goto done_unlocked; + + err = -EINVAL; + spin_lock_irqsave(&dev->lock, flags); + if (info->attrs[WPROBE_ATTR_MAC]) { + /* not supported yet */ + goto done; + } + + if (info->attrs[WPROBE_ATTR_SAMPLES_MIN] || + info->attrs[WPROBE_ATTR_SAMPLES_MAX]) { + if (info->attrs[WPROBE_ATTR_SAMPLES_MIN]) + scale_min = nla_get_u32(info->attrs[WPROBE_ATTR_SAMPLES_MIN]); + else + scale_min = dev->scale_min; + + if (info->attrs[WPROBE_ATTR_SAMPLES_MAX]) + scale_max = nla_get_u32(info->attrs[WPROBE_ATTR_SAMPLES_MAX]); + else + scale_max = dev->scale_max; + + if ((!scale_min && !scale_max) || + (scale_min && scale_max && (scale_min < scale_max))) { + dev->scale_min = scale_min; + dev->scale_max = scale_max; + } else { + goto done; + } + } + + if (info->attrs[WPROBE_ATTR_SAMPLES_SCALE_M] && + info->attrs[WPROBE_ATTR_SAMPLES_SCALE_D]) { + + scale_m = nla_get_u32(info->attrs[WPROBE_ATTR_SAMPLES_SCALE_M]); + scale_d = nla_get_u32(info->attrs[WPROBE_ATTR_SAMPLES_SCALE_D]); + + if (!scale_d || (scale_m > scale_d)) + goto done; + + dev->scale_m = scale_m; + dev->scale_d = scale_d; + } + + err = 0; + if (info->attrs[WPROBE_ATTR_INTERVAL]) { + /* change of measurement interval requested */ + err = wprobe_update_auto_measurement(dev, + (u32) nla_get_u64(info->attrs[WPROBE_ATTR_INTERVAL])); + } + +done: + spin_unlock_irqrestore(&dev->lock, flags); +done_unlocked: + rcu_read_unlock(); + return err; +} + static struct genl_ops wprobe_ops[] = { { .cmd = WPROBE_CMD_GET_INFO, @@ -627,7 +759,11 @@ static struct genl_ops wprobe_ops[] = { .cmd = WPROBE_CMD_GET_LINKS, .dumpit = wprobe_dump_links, .policy = wprobe_policy, - } + }, + { + .cmd = WPROBE_CMD_CONFIG, + .doit = wprobe_set_config, + }, }; static void __exit diff --git a/package/wprobe/src/user/wprobe-info.c b/package/wprobe/src/user/wprobe-info.c index b8918711c6..8361c02754 100644 --- a/package/wprobe/src/user/wprobe-info.c +++ b/package/wprobe/src/user/wprobe-info.c @@ -64,15 +64,15 @@ wprobe_dump_value(struct wprobe_attribute *attr) static void -wprobe_dump_data(const char *ifname, struct list_head *gl, struct list_head *ll, struct list_head *ls) +wprobe_dump_data(struct wprobe_iface *dev) { struct wprobe_attribute *attr; struct wprobe_link *link; bool first = true; fprintf(stderr, "\n"); - wprobe_request_data(ifname, gl, NULL, 2); - list_for_each_entry(attr, gl, list) { + wprobe_request_data(dev, NULL); + list_for_each_entry(attr, &dev->global_attr, list) { fprintf(stderr, (first ? "Global: %s=%s\n" : " %s=%s\n"), @@ -82,10 +82,10 @@ wprobe_dump_data(const char *ifname, struct list_head *gl, struct list_head *ll, first = false; } - list_for_each_entry(link, ls, list) { + list_for_each_entry(link, &dev->links, list) { first = true; - wprobe_request_data(ifname, ll, link->addr, 2); - list_for_each_entry(attr, ll, list) { + wprobe_request_data(dev, link->addr); + list_for_each_entry(attr, &dev->link_attr, list) { if (first) { fprintf(stderr, "%02x:%02x:%02x:%02x:%02x:%02x: %s=%s\n", @@ -119,57 +119,92 @@ static const char *attr_typestr[] = { static int usage(const char *prog) { - fprintf(stderr, "Usage: %s \n", prog); - return 1; + fprintf(stderr, + "Usage: %s [options]\n" + "\n" + "Options:\n" + " -c: Only apply configuration\n" + " -h: This help text\n" + " -i : Set measurement interval\n" + " -m: Run measurement loop\n" + "\n" + , prog); + exit(1); } -int main(int argc, char **argv) +static void show_attributes(struct wprobe_iface *dev) { struct wprobe_attribute *attr; - const char *ifname; - LIST_HEAD(global_attr); - LIST_HEAD(link_attr); - LIST_HEAD(links); - int i = 0; + list_for_each_entry(attr, &dev->global_attr, list) { + fprintf(stderr, "Global attribute: '%s' (%s)\n", + attr->name, attr_typestr[attr->type]); + } + list_for_each_entry(attr, &dev->link_attr, list) { + fprintf(stderr, "Link attribute: '%s' (%s)\n", + attr->name, attr_typestr[attr->type]); + } +} - if (argc < 2) - return usage(argv[0]); +static void loop_measurement(struct wprobe_iface *dev) +{ + while (1) { + sleep(1); + wprobe_update_links(dev); + wprobe_dump_data(dev); + } +} - ifname = argv[1]; +int main(int argc, char **argv) +{ + struct wprobe_iface *dev; + const char *ifname; + const char *prog = argv[0]; + enum { + CMD_NONE, + CMD_CONFIG, + CMD_MEASURE, + } cmd = CMD_NONE; + int ch; - if (wprobe_init() != 0) - return -1; + if ((argc < 2) || (argv[1][0] == '-')) + return usage(prog); - wprobe_dump_attributes(ifname, false, &global_attr, NULL); - wprobe_dump_attributes(ifname, true, &link_attr, NULL); + ifname = argv[1]; + dev = wprobe_get_dev(ifname); + argv++; + argc--; - if (list_empty(&global_attr) && - list_empty(&link_attr)) { + if (!dev || (list_empty(&dev->global_attr) && + list_empty(&dev->link_attr))) { fprintf(stderr, "Interface '%s' not found\n", ifname); return -1; } - list_for_each_entry(attr, &global_attr, list) { - fprintf(stderr, "Global attribute: '%s' (%s)\n", - attr->name, attr_typestr[attr->type]); - } - list_for_each_entry(attr, &link_attr, list) { - fprintf(stderr, "Link attribute: '%s' (%s)\n", - attr->name, attr_typestr[attr->type]); + while ((ch = getopt(argc, argv, "chi:m")) != -1) { + switch(ch) { + case 'c': + cmd = CMD_CONFIG; + break; + case 'm': + cmd = CMD_MEASURE; + break; + case 'i': + dev->interval = strtoul(optarg, NULL, 10); + break; + case 'h': + default: + usage(prog); + break; + } } - while (1) { - usleep(100 * 1000); - wprobe_measure(ifname); - - if (i-- > 0) - continue; + wprobe_apply_config(dev); + if (cmd != CMD_CONFIG) + show_attributes(dev); + if (cmd == CMD_MEASURE) + loop_measurement(dev); - i = 10; - wprobe_update_links(ifname, &links); - wprobe_dump_data(ifname, &global_attr, &link_attr, &links); - } - wprobe_free(); + wprobe_free_dev(dev); return 0; } diff --git a/package/wprobe/src/user/wprobe.c b/package/wprobe/src/user/wprobe.c index 1f8df6c7e9..e48f768e54 100644 --- a/package/wprobe/src/user/wprobe.c +++ b/package/wprobe/src/user/wprobe.c @@ -35,6 +35,7 @@ #define DPRINTF(fmt, ...) do {} while (0) #endif +static int n_devs = 0; static struct nl_sock *handle = NULL; static struct nl_cache *cache = NULL; static struct genl_family *family = NULL; @@ -83,9 +84,16 @@ ack_handler(struct nl_msg *msg, void *arg) } -void +static void wprobe_free(void) { + /* should not happen */ + if (n_devs == 0) + return; + + if (--n_devs != 0) + return; + if (cache) nl_cache_free(cache); if (handle) @@ -94,11 +102,14 @@ wprobe_free(void) cache = NULL; } -int +static int wprobe_init(void) { int ret; + if (n_devs++ > 0) + return 0; + handle = nl_socket_alloc(); if (!handle) { DPRINTF("Failed to create handle\n"); @@ -233,8 +244,8 @@ save_attribute_handler(struct nl_msg *msg, void *arg) } -int -wprobe_dump_attributes(const char *ifname, bool link, struct list_head *list, char *addr) +static int +dump_attributes(const char *ifname, bool link, struct list_head *list, char *addr) { struct nl_msg *msg; struct wprobe_attr_cb cb; @@ -255,6 +266,64 @@ nla_put_failure: return -EINVAL; } +struct wprobe_iface * +wprobe_get_dev(const char *ifname) +{ + struct wprobe_iface *dev; + + if (wprobe_init() != 0) + return NULL; + + dev = malloc(sizeof(struct wprobe_iface)); + if (!dev) + return NULL; + + memset(dev, 0, sizeof(struct wprobe_iface)); + dev->ifname = strdup(ifname); + if (!dev->ifname) + goto error; + + dev->interval = -1; + dev->scale_min = -1; + dev->scale_max = -1; + dev->scale_m = -1; + dev->scale_d = -1; + + INIT_LIST_HEAD(&dev->global_attr); + INIT_LIST_HEAD(&dev->link_attr); + INIT_LIST_HEAD(&dev->links); + + dump_attributes(ifname, false, &dev->global_attr, NULL); + dump_attributes(ifname, true, &dev->link_attr, NULL); + + return dev; + +error: + free(dev); + return NULL; +} + +static void +free_attr_list(struct list_head *list) +{ + struct wprobe_attribute *attr, *tmp; + + list_for_each_entry_safe(attr, tmp, list, list) { + list_del(&attr->list); + free(attr); + } +} + +void +wprobe_free_dev(struct wprobe_iface *dev) +{ + wprobe_free(); + free_attr_list(&dev->global_attr); + free_attr_list(&dev->link_attr); + free((void *)dev->ifname); + free(dev); +} + static struct wprobe_link * get_link(struct list_head *list, const char *addr) { @@ -313,7 +382,7 @@ save_link_handler(struct nl_msg *msg, void *arg) int -wprobe_update_links(const char *ifname, struct list_head *list) +wprobe_update_links(struct wprobe_iface *dev) { struct wprobe_link *l, *tmp; struct nl_msg *msg; @@ -321,10 +390,10 @@ wprobe_update_links(const char *ifname, struct list_head *list) int err; INIT_LIST_HEAD(&cb.old_list); - list_splice_init(list, &cb.old_list); - cb.list = list; + list_splice_init(&dev->links, &cb.old_list); + cb.list = &dev->links; - msg = wprobe_new_msg(ifname, WPROBE_CMD_GET_LINKS, true); + msg = wprobe_new_msg(dev->ifname, WPROBE_CMD_GET_LINKS, true); if (!msg) return -ENOMEM; @@ -340,16 +409,37 @@ wprobe_update_links(const char *ifname, struct list_head *list) return 0; } -void -wprobe_measure(const char *ifname) +int +wprobe_apply_config(struct wprobe_iface *dev) { struct nl_msg *msg; - msg = wprobe_new_msg(ifname, WPROBE_CMD_MEASURE, false); + msg = wprobe_new_msg(dev->ifname, WPROBE_CMD_CONFIG, false); if (!msg) - return; + return -ENOMEM; + + if (dev->interval >= 0) + NLA_PUT_MSECS(msg, WPROBE_ATTR_INTERVAL, dev->interval); + + wprobe_send_msg(msg, NULL, NULL); + return 0; + +nla_put_failure: + nlmsg_free(msg); + return -ENOMEM; +} + +int +wprobe_measure(struct wprobe_iface *dev) +{ + struct nl_msg *msg; + + msg = wprobe_new_msg(dev->ifname, WPROBE_CMD_MEASURE, false); + if (!msg) + return -ENOMEM; wprobe_send_msg(msg, NULL, NULL); + return 0; } struct wprobe_request_cb { @@ -431,6 +521,10 @@ found: if (attr->val.n > 0) { float avg = ((float) attr->val.s) / attr->val.n; float stdev = sqrt((((float) attr->val.ss) / attr->val.n) - (avg * avg)); + if (isnan(stdev)) + stdev = 0.0f; + if (isnan(avg)) + avg = 0.0f; attr->val.avg = avg; attr->val.stdev = stdev; } @@ -443,25 +537,24 @@ out: int -wprobe_request_data(const char *ifname, struct list_head *attrs, const unsigned char *addr, int scale) +wprobe_request_data(struct wprobe_iface *dev, const unsigned char *addr) { struct wprobe_request_cb cb; + struct list_head *attrs; struct nl_msg *msg; int err; - msg = wprobe_new_msg(ifname, WPROBE_CMD_GET_INFO, true); + msg = wprobe_new_msg(dev->ifname, WPROBE_CMD_GET_INFO, true); if (!msg) return -ENOMEM; - if (scale < 0) - NLA_PUT_U32(msg, WPROBE_ATTR_FLAGS, WPROBE_F_RESET); - else if (scale > 0) - NLA_PUT_U32(msg, WPROBE_ATTR_SCALE, scale); - - if (addr) + if (addr) { + attrs = &dev->link_attr; NLA_PUT(msg, WPROBE_ATTR_MAC, 6, addr); + } else { + attrs = &dev->global_attr; + } -nla_put_failure: INIT_LIST_HEAD(&cb.old_list); list_splice_init(attrs, &cb.old_list); cb.list = attrs; @@ -469,6 +562,10 @@ nla_put_failure: err = wprobe_send_msg(msg, save_attrdata_handler, &cb); list_splice(&cb.old_list, attrs->prev); return err; + +nla_put_failure: + nlmsg_free(msg); + return -ENOMEM; } diff --git a/package/wprobe/src/user/wprobe.h b/package/wprobe/src/user/wprobe.h index e0c33faef2..c0b4902f3c 100644 --- a/package/wprobe/src/user/wprobe.h +++ b/package/wprobe/src/user/wprobe.h @@ -87,15 +87,21 @@ struct wprobe_link { unsigned char addr[6]; }; -/** - * wprobe_init: initialize internal data structures and connect to the wprobe netlink api - */ -extern int wprobe_init(void); +struct wprobe_iface { + const char *ifname; + char addr[6]; -/** - * wprobe_free: free all internally allocated data structures - */ -extern void wprobe_free(void); + struct list_head global_attr; + struct list_head link_attr; + struct list_head links; + + /* config */ + int interval; + int scale_min; + int scale_max; + int scale_m; + int scale_d; +}; /** * wprobe_update_links: get a list of all link partners @@ -105,7 +111,7 @@ extern void wprobe_free(void); * when wprobe_update_links is called multiple times, the linked list * is updated with new link partners, old entries are automatically expired */ -extern int wprobe_update_links(const char *ifname, struct list_head *list); +extern int wprobe_update_links(struct wprobe_iface *dev); /** * wprobe_measure: start a measurement request for all global attributes @@ -115,29 +121,39 @@ extern int wprobe_update_links(const char *ifname, struct list_head *list); * it may be desirable to control the sampling interval from user space * you can use this function to do that. */ -extern void wprobe_measure(const char *ifname); +extern int wprobe_measure(struct wprobe_iface *dev); /** - * wprobe_dump_attributes: create a linked list of available attributes + * wprobe_get_dev: get device information * @ifname: name of the wprobe interface - * @link: false: get the list of global attributes; true: get the list of per-link attributes - * @list: linked list to store the attributes in - * @addr: buffer to store the interface's mac address in (optional) * - * attributes must be freed by the caller + * queries the wprobe interface for all attributes + * must be freed with wprobe_free_dev + */ +extern struct wprobe_iface *wprobe_get_dev(const char *ifname); + +/** + * wprobe_get_dev: free all device information + * @dev: wprobe device structure + */ +extern void wprobe_free_dev(struct wprobe_iface *dev); + +/** + * wprobe_apply_config: apply configuration data + * @dev: wprobe device structure + * + * uploads all configuration values from @dev that are not set to -1 */ -extern int wprobe_dump_attributes(const char *ifname, bool link, struct list_head *list, char *addr); +extern int wprobe_apply_config(struct wprobe_iface *dev); /** * wprobe_request_data: request new sampling values for the given list of attributes - * @ifname: name of the wprobe interface - * @attrs: attribute list + * @dev: wprobe device structure * @addr: (optional) mac address of the link partner - * @scale: scale down values by a factor (scale < 0: reset statistics entirely) * - * if addr is unset, attrs must point to the list of global attributes, - * if addr is set, attrs must point to the list of per-link attributes + * if addr is unset, global values are stored in the global attributes list + * if addr is set, per-link values for the given address are stored in the link attributes list */ -extern int wprobe_request_data(const char *ifname, struct list_head *attrs, const unsigned char *addr, int scale); +extern int wprobe_request_data(struct wprobe_iface *dev, const unsigned char *addr); #endif -- 2.30.2