Hi,
alfred uses the TQ from batman-adv to find its best alfred neighbor. This best neighbor information is used by slave servers to request/publish data.
This is done for each server announcement packet by:
* requesting the global translation table (netlink or debugfs) and then searching in it for the MAC address of the detected alfred server to find its originator address * requesting the originator table (netlink or debugfs) and then searching it it for the originator address of the detected alfred server to find its TQ value
This was previously done whenever a new announcement packet received by alfred. We've observed that this can be a problem on networks with a lot of master servers (~100) which can see each other, large translation tables and slow CPUs. alfred still worked but the CPU load by alfred was rather high (~20% on an 560MHz AR9344).
The idea is now to avoid this lookup for master servers. And (for slave servers) to process all servers at once. This is done before the wants to push its local data to a master server in an sync interval.
The process which was described earlier was now changed to:
* requesting the global translation table (netlink or debugfs) and then put MAC address and corresponding originator address in tg hash * requesting the originator table (netlink or debugfs) and then put originator address and corresponding TQ value orig hash * got through all servers: - search in tg hash for for the MAC address of the alfred server to find its originator address - search in orig hash for for the originator address of the alfred server to find its TQ value
These changes reduced the load on the previously mentioned devices significantly.
Kind regards, Sven
Sven Eckelmann (5): alfred: Move alfred specific netlink code in separate file alfred: Only query tq of remote master in slave mode alfred: Check the TQ of master servers before pushing data alfred: Cache the TQ values for each originator alfred: Cache the global translation table entries
Makefile | 1 + alfred.h | 1 - batadv_query.c | 227 +++++++++++++++++++++++++++++++++++++++++-------------- batadv_query.h | 25 +++++- batadv_querynl.c | 216 ++++++++++++++++++++++++++++++++++++++++++++++++++++ batadv_querynl.h | 34 +++++++++ netlink.c | 195 ----------------------------------------------- netlink.h | 7 -- recv.c | 15 +--- server.c | 56 +++++++++++++- 10 files changed, 501 insertions(+), 276 deletions(-) create mode 100644 batadv_querynl.c create mode 100644 batadv_querynl.h
The vis daemon doesn't require the same set of netlink functions as alfred daemon. But the netlink.c file is shared between both. Split the file to avoid a lot of dead code in vis.
Signed-off-by: Sven Eckelmann sven.eckelmann@openmesh.com --- Makefile | 1 + batadv_query.c | 2 +- batadv_querynl.c | 236 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ batadv_querynl.h | 35 +++++++++ netlink.c | 195 --------------------------------------------- netlink.h | 7 -- 6 files changed, 273 insertions(+), 203 deletions(-) create mode 100644 batadv_querynl.c create mode 100644 batadv_querynl.h
diff --git a/Makefile b/Makefile index 3c88e96..4c1c6b5 100755 --- a/Makefile +++ b/Makefile @@ -21,6 +21,7 @@ # alfred build BINARY_NAME = alfred OBJ += batadv_query.o +OBJ += batadv_querynl.o OBJ += client.o OBJ += debugfs.o OBJ += hash.o diff --git a/batadv_query.c b/batadv_query.c index 6ec086b..e68052b 100644 --- a/batadv_query.c +++ b/batadv_query.c @@ -34,7 +34,7 @@ #endif #include <sys/types.h>
-#include "netlink.h" +#include "batadv_querynl.h" #include "debugfs.h"
#define DEBUG_BATIF_PATH_FMT "%s/batman_adv/%s" diff --git a/batadv_querynl.c b/batadv_querynl.c new file mode 100644 index 0000000..8dab96e --- /dev/null +++ b/batadv_querynl.c @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2009-2017 B.A.T.M.A.N. contributors: + * + * Marek Lindner mareklindner@neomailbox.ch, Andrew Lunn andrew@lunn.ch + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include "batadv_querynl.h" + +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <net/ethernet.h> +#include <net/if.h> +#include <netlink/netlink.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/ctrl.h> +#include <net/ethernet.h> + +#include "batman_adv.h" +#include "netlink.h" + +#ifndef __unused +#define __unused __attribute__((unused)) +#endif + +static const int translate_mac_netlink_mandatory[] = { + BATADV_ATTR_TT_ADDRESS, + BATADV_ATTR_ORIG_ADDRESS, +}; + +struct translate_mac_netlink_opts { + struct ether_addr mac; + bool found; + struct nlquery_opts query_opts; +}; + +static int translate_mac_netlink_cb(struct nl_msg *msg, void *arg) +{ + struct nlattr *attrs[BATADV_ATTR_MAX + 1]; + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct nlquery_opts *query_opts = arg; + struct translate_mac_netlink_opts *opts; + struct genlmsghdr *ghdr; + uint8_t *addr; + uint8_t *orig; + + opts = container_of(query_opts, struct translate_mac_netlink_opts, + query_opts); + + if (!genlmsg_valid_hdr(nlh, 0)) + return NL_OK; + + ghdr = nlmsg_data(nlh); + + if (ghdr->cmd != BATADV_CMD_GET_TRANSTABLE_GLOBAL) + return NL_OK; + + if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), + genlmsg_len(ghdr), batadv_netlink_policy)) { + return NL_OK; + } + + if (missing_mandatory_attrs(attrs, translate_mac_netlink_mandatory, + ARRAY_SIZE(translate_mac_netlink_mandatory))) + return NL_OK; + + addr = nla_data(attrs[BATADV_ATTR_TT_ADDRESS]); + orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]); + + if (!attrs[BATADV_ATTR_FLAG_BEST]) + return NL_OK; + + if (memcmp(&opts->mac, addr, ETH_ALEN) != 0) + return NL_OK; + + memcpy(&opts->mac, orig, ETH_ALEN); + opts->found = true; + opts->query_opts.err = 0; + + return NL_STOP; +} + +int translate_mac_netlink(const char *mesh_iface, const struct ether_addr *mac, + struct ether_addr *mac_out) +{ + struct translate_mac_netlink_opts opts = { + .found = false, + .query_opts = { + .err = 0, + }, + }; + int ret; + + memcpy(&opts.mac, mac, ETH_ALEN); + + ret = netlink_query_common(mesh_iface, + BATADV_CMD_GET_TRANSTABLE_GLOBAL, + translate_mac_netlink_cb, &opts.query_opts); + if (ret < 0) + return ret; + + if (!opts.found) + return -ENOENT; + + memcpy(mac_out, &opts.mac, ETH_ALEN); + + return 0; +} + +static const int get_tq_netlink_mandatory[] = { + BATADV_ATTR_ORIG_ADDRESS, + BATADV_ATTR_TQ, +}; + +struct get_tq_netlink_opts { + struct ether_addr mac; + uint8_t tq; + bool found; + struct nlquery_opts query_opts; +}; + +static int get_tq_netlink_cb(struct nl_msg *msg, void *arg) +{ + struct nlattr *attrs[BATADV_ATTR_MAX + 1]; + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct nlquery_opts *query_opts = arg; + struct get_tq_netlink_opts *opts; + struct genlmsghdr *ghdr; + uint8_t *orig; + uint8_t tq; + + opts = container_of(query_opts, struct get_tq_netlink_opts, + query_opts); + + if (!genlmsg_valid_hdr(nlh, 0)) + return NL_OK; + + ghdr = nlmsg_data(nlh); + + if (ghdr->cmd != BATADV_CMD_GET_ORIGINATORS) + return NL_OK; + + if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), + genlmsg_len(ghdr), batadv_netlink_policy)) { + return NL_OK; + } + + if (missing_mandatory_attrs(attrs, get_tq_netlink_mandatory, + ARRAY_SIZE(get_tq_netlink_mandatory))) + return NL_OK; + + orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]); + tq = nla_get_u8(attrs[BATADV_ATTR_TQ]); + + if (!attrs[BATADV_ATTR_FLAG_BEST]) + return NL_OK; + + if (memcmp(&opts->mac, orig, ETH_ALEN) != 0) + return NL_OK; + + opts->tq = tq; + opts->found = true; + opts->query_opts.err = 0; + + return NL_STOP; +} + +int get_tq_netlink(const char *mesh_iface, const struct ether_addr *mac, + uint8_t *tq) +{ + struct get_tq_netlink_opts opts = { + .tq = 0, + .found = false, + .query_opts = { + .err = 0, + }, + }; + int ret; + + memcpy(&opts.mac, mac, ETH_ALEN); + + ret = netlink_query_common(mesh_iface, BATADV_CMD_GET_ORIGINATORS, + get_tq_netlink_cb, &opts.query_opts); + if (ret < 0) + return ret; + + if (!opts.found) + return -ENOENT; + + *tq = opts.tq; + + return 0; +} + +static int check_nlcmd_cb(struct nl_msg *msg __unused, void *arg __unused) +{ + return NL_STOP; +} + +int batadv_interface_check_netlink(const char *mesh_iface) +{ + struct nlquery_opts opts = { + .err = 0, + }; + int ret; + + ret = netlink_query_common(mesh_iface, BATADV_CMD_GET_ORIGINATORS, + check_nlcmd_cb, &opts); + if (ret < 0) + return ret; + + ret = netlink_query_common(mesh_iface, BATADV_CMD_GET_TRANSTABLE_GLOBAL, + check_nlcmd_cb, &opts); + if (ret < 0) + return ret; + + return 0; +} diff --git a/batadv_querynl.h b/batadv_querynl.h new file mode 100644 index 0000000..9b93a47 --- /dev/null +++ b/batadv_querynl.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2009-2017 B.A.T.M.A.N. contributors: + * + * Marek Lindner mareklindner@neomailbox.ch, Andrew Lunn andrew@lunn.ch + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#ifndef _BATADV_QUERYNL_H +#define _BATADV_QUERYNL_H + +#include <stdint.h> + +struct ether_addr; + +int translate_mac_netlink(const char *mesh_iface, const struct ether_addr *mac, + struct ether_addr *mac_out); +int get_tq_netlink(const char *mesh_iface, const struct ether_addr *mac, + uint8_t *tq); +int batadv_interface_check_netlink(const char *mesh_iface); + +#endif /* _BATADV_QUERYNL_H */ diff --git a/netlink.c b/netlink.c index 1964ab8..7ef4308 100644 --- a/netlink.c +++ b/netlink.c @@ -27,7 +27,6 @@ #include <string.h> #include <unistd.h> #include <errno.h> -#include <net/ethernet.h> #include <net/if.h> #include <netlink/netlink.h> #include <netlink/genl/genl.h> @@ -196,197 +195,3 @@ err_free_sock:
return query_opts->err; } - -static const int translate_mac_netlink_mandatory[] = { - BATADV_ATTR_TT_ADDRESS, - BATADV_ATTR_ORIG_ADDRESS, -}; - -struct translate_mac_netlink_opts { - struct ether_addr mac; - bool found; - struct nlquery_opts query_opts; -}; - -static int translate_mac_netlink_cb(struct nl_msg *msg, void *arg) -{ - struct nlattr *attrs[BATADV_ATTR_MAX+1]; - struct nlmsghdr *nlh = nlmsg_hdr(msg); - struct nlquery_opts *query_opts = arg; - struct translate_mac_netlink_opts *opts; - struct genlmsghdr *ghdr; - uint8_t *addr; - uint8_t *orig; - - opts = container_of(query_opts, struct translate_mac_netlink_opts, - query_opts); - - if (!genlmsg_valid_hdr(nlh, 0)) - return NL_OK; - - ghdr = nlmsg_data(nlh); - - if (ghdr->cmd != BATADV_CMD_GET_TRANSTABLE_GLOBAL) - return NL_OK; - - if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), - genlmsg_len(ghdr), batadv_netlink_policy)) { - return NL_OK; - } - - if (missing_mandatory_attrs(attrs, translate_mac_netlink_mandatory, - ARRAY_SIZE(translate_mac_netlink_mandatory))) - return NL_OK; - - addr = nla_data(attrs[BATADV_ATTR_TT_ADDRESS]); - orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]); - - if (!attrs[BATADV_ATTR_FLAG_BEST]) - return NL_OK; - - if (memcmp(&opts->mac, addr, ETH_ALEN) != 0) - return NL_OK; - - memcpy(&opts->mac, orig, ETH_ALEN); - opts->found = true; - opts->query_opts.err = 0; - - return NL_STOP; -} - -int translate_mac_netlink(const char *mesh_iface, const struct ether_addr *mac, - struct ether_addr *mac_out) -{ - struct translate_mac_netlink_opts opts = { - .found = false, - .query_opts = { - .err = 0, - }, - }; - int ret; - - memcpy(&opts.mac, mac, ETH_ALEN); - - ret = netlink_query_common(mesh_iface, - BATADV_CMD_GET_TRANSTABLE_GLOBAL, - translate_mac_netlink_cb, &opts.query_opts); - if (ret < 0) - return ret; - - if (!opts.found) - return -ENOENT; - - memcpy(mac_out, &opts.mac, ETH_ALEN); - - return 0; -} - -static const int get_tq_netlink_mandatory[] = { - BATADV_ATTR_ORIG_ADDRESS, - BATADV_ATTR_TQ, -}; - -struct get_tq_netlink_opts { - struct ether_addr mac; - uint8_t tq; - bool found; - struct nlquery_opts query_opts; -}; - -static int get_tq_netlink_cb(struct nl_msg *msg, void *arg) -{ - struct nlattr *attrs[BATADV_ATTR_MAX+1]; - struct nlmsghdr *nlh = nlmsg_hdr(msg); - struct nlquery_opts *query_opts = arg; - struct get_tq_netlink_opts *opts; - struct genlmsghdr *ghdr; - uint8_t *orig; - uint8_t tq; - - opts = container_of(query_opts, struct get_tq_netlink_opts, - query_opts); - - if (!genlmsg_valid_hdr(nlh, 0)) - return NL_OK; - - ghdr = nlmsg_data(nlh); - - if (ghdr->cmd != BATADV_CMD_GET_ORIGINATORS) - return NL_OK; - - if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), - genlmsg_len(ghdr), batadv_netlink_policy)) { - return NL_OK; - } - - if (missing_mandatory_attrs(attrs, get_tq_netlink_mandatory, - ARRAY_SIZE(get_tq_netlink_mandatory))) - return NL_OK; - - orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]); - tq = nla_get_u8(attrs[BATADV_ATTR_TQ]); - - if (!attrs[BATADV_ATTR_FLAG_BEST]) - return NL_OK; - - if (memcmp(&opts->mac, orig, ETH_ALEN) != 0) - return NL_OK; - - opts->tq = tq; - opts->found = true; - opts->query_opts.err = 0; - - return NL_STOP; -} - -int get_tq_netlink(const char *mesh_iface, const struct ether_addr *mac, - uint8_t *tq) -{ - struct get_tq_netlink_opts opts = { - .tq = 0, - .found = false, - .query_opts = { - .err = 0, - }, - }; - int ret; - - memcpy(&opts.mac, mac, ETH_ALEN); - - ret = netlink_query_common(mesh_iface, BATADV_CMD_GET_ORIGINATORS, - get_tq_netlink_cb, &opts.query_opts); - if (ret < 0) - return ret; - - if (!opts.found) - return -ENOENT; - - *tq = opts.tq; - - return 0; -} - -static int check_nlcmd_cb(struct nl_msg *msg __unused, void *arg __unused) -{ - return NL_STOP; -} - -int batadv_interface_check_netlink(const char *mesh_iface) -{ - struct nlquery_opts opts = { - .err = 0, - }; - int ret; - - ret = netlink_query_common(mesh_iface, BATADV_CMD_GET_ORIGINATORS, - check_nlcmd_cb, &opts); - if (ret < 0) - return ret; - - ret = netlink_query_common(mesh_iface, BATADV_CMD_GET_TRANSTABLE_GLOBAL, - check_nlcmd_cb, &opts); - if (ret < 0) - return ret; - - return 0; -} diff --git a/netlink.h b/netlink.h index 1c87695..a4471a1 100644 --- a/netlink.h +++ b/netlink.h @@ -26,8 +26,6 @@ #include <netlink/genl/ctrl.h> #include <stddef.h>
-struct ether_addr; - struct nlquery_opts { int err; }; @@ -45,11 +43,6 @@ int netlink_query_common(const char *mesh_iface, uint8_t nl_cmd, struct nlquery_opts *query_opts); int missing_mandatory_attrs(struct nlattr *attrs[], const int mandatory[], size_t num); -int translate_mac_netlink(const char *mesh_iface, const struct ether_addr *mac, - struct ether_addr *mac_out); -int get_tq_netlink(const char *mesh_iface, const struct ether_addr *mac, - uint8_t *tq); -int batadv_interface_check_netlink(const char *mesh_iface);
extern struct nla_policy batadv_netlink_policy[];
The querying of the originator mac address and tq values of the orignator address takes significant amount of time. It is therefore better to avoid the TQ retrieval code when possible.
The TQ will not be used in master mode and it can therefore be skipped together with the code which tries to find the new best server.
Signed-off-by: Sven Eckelmann sven.eckelmann@openmesh.com --- recv.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/recv.c b/recv.c index 3fd964d..8aa512d 100644 --- a/recv.c +++ b/recv.c @@ -304,6 +304,13 @@ process_alfred_announce_master(struct globals *globals, }
clock_gettime(CLOCK_MONOTONIC, &server->last_seen); + + /* TQ is not used for master sync mode */ + if (globals->opmode == OPMODE_MASTER) { + server->tq = 0; + return 0; + } + if (strcmp(globals->mesh_iface, "none") != 0) { macaddr = translate_mac(globals->mesh_iface, (struct ether_addr *)&server->hwaddr); @@ -315,8 +322,7 @@ process_alfred_announce_master(struct globals *globals, server->tq = 255; }
- if (globals->opmode == OPMODE_SLAVE) - set_best_server(globals); + set_best_server(globals);
return 0; }
The TQ value of the server might have changed dramatically since we received the last announcement of a master server. This can especially happen when node (which was previously the best server) moves a lot and its announcements are lost for a while.
Instead of querying the TQ values of each node when receiving an announcement, just query the TQ value before new data should be pushed to a master server.
Signed-off-by: Sven Eckelmann sven.eckelmann@openmesh.com --- alfred.h | 1 - recv.c | 21 +-------------------- server.c | 36 +++++++++++++++++++++++++++++++++++- 3 files changed, 36 insertions(+), 22 deletions(-)
diff --git a/alfred.h b/alfred.h index 5f7a98c..429953f 100644 --- a/alfred.h +++ b/alfred.h @@ -153,7 +153,6 @@ extern alfred_addr alfred_mcast;
/* server.c */ int alfred_server(struct globals *globals); -int set_best_server(struct globals *globals); void changed_data_type(struct globals *globals, uint8_t arg);
/* client.c */ diff --git a/recv.c b/recv.c index 8aa512d..12bb3f1 100644 --- a/recv.c +++ b/recv.c @@ -268,7 +268,6 @@ process_alfred_announce_master(struct globals *globals, struct alfred_announce_master_v0 *announce) { struct server *server; - struct ether_addr *macaddr; struct ether_addr mac; int ret; int len; @@ -296,6 +295,7 @@ process_alfred_announce_master(struct globals *globals,
memcpy(&server->hwaddr, &mac, ETH_ALEN); memcpy(&server->address, source, sizeof(*source)); + server->tq = 0;
if (hash_add(interface->server_hash, server)) { free(server); @@ -305,25 +305,6 @@ process_alfred_announce_master(struct globals *globals,
clock_gettime(CLOCK_MONOTONIC, &server->last_seen);
- /* TQ is not used for master sync mode */ - if (globals->opmode == OPMODE_MASTER) { - server->tq = 0; - return 0; - } - - if (strcmp(globals->mesh_iface, "none") != 0) { - macaddr = translate_mac(globals->mesh_iface, - (struct ether_addr *)&server->hwaddr); - if (macaddr) - server->tq = get_tq(globals->mesh_iface, macaddr); - else - server->tq = 0; - } else { - server->tq = 255; - } - - set_best_server(globals); - return 0; }
diff --git a/server.c b/server.c index 2c4042a..09acb80 100644 --- a/server.c +++ b/server.c @@ -113,7 +113,7 @@ static int create_hashes(struct globals *globals) return 0; }
-int set_best_server(struct globals *globals) +static int set_best_server(struct globals *globals) { struct hash_it_t *hashit = NULL; struct server *best_server = NULL; @@ -218,6 +218,39 @@ static int purge_data(struct globals *globals) return 0; }
+static void update_server_info(struct globals *globals) +{ + struct hash_it_t *hashit = NULL; + struct interface *interface; + struct ether_addr *macaddr; + + /* TQ is not used for master sync mode */ + if (globals->opmode == OPMODE_MASTER) + return; + + list_for_each_entry(interface, &globals->interfaces, list) { + while (NULL != (hashit = hash_iterate(interface->server_hash, + hashit))) { + struct server *server = hashit->bucket->data; + + if (strcmp(globals->mesh_iface, "none") == 0) { + server->tq = 255; + continue; + } + + macaddr = translate_mac(globals->mesh_iface, + &server->hwaddr); + if (macaddr) + server->tq = get_tq(globals->mesh_iface, + macaddr); + else + server->tq = 0; + } + } + + set_best_server(globals); +} + static void check_if_socket(struct interface *interface, struct globals *globals) { int sock; @@ -422,6 +455,7 @@ int alfred_server(struct globals *globals) sync_data(globals); } else { /* send local data to server */ + update_server_info(globals); push_local_data(globals); } purge_data(globals);
There is a single loop which goes over all servers to find the TQs for each server. The TQ value doesn't change often and it is relative unlikely that it is different after some milliseconds. It is therefore not necessary to re-request the TQ values from the kernel for each server in the server hash. Instead, the values can be retrieved ones from the kernel during each sync interval, cached and then retrieved from the cache again.
Signed-off-by: Sven Eckelmann sven.eckelmann@openmesh.com --- batadv_query.c | 108 ++++++++++++++++++++++++++++++++++++++++++------------- batadv_query.h | 14 +++++++- batadv_querynl.c | 29 +++++---------- batadv_querynl.h | 4 +-- server.c | 17 +++++++-- 5 files changed, 121 insertions(+), 51 deletions(-)
diff --git a/batadv_query.c b/batadv_query.c index e68052b..1123cf5 100644 --- a/batadv_query.c +++ b/batadv_query.c @@ -298,8 +298,7 @@ struct ether_addr *translate_mac(const char *mesh_iface, return mac_result; }
-static int get_tq_debugfs(const char *mesh_iface, struct ether_addr *mac, - uint8_t *tq) +static int get_tq_debugfs(const char *mesh_iface, struct hashtable_t *orig_hash) { enum { orig_mac, @@ -308,16 +307,13 @@ static int get_tq_debugfs(const char *mesh_iface, struct ether_addr *mac, orig_tqvalue, } pos; char full_path[MAX_PATH + 1]; - static struct ether_addr in_mac; struct ether_addr *mac_tmp; FILE *f = NULL; size_t len = 0; char *line = NULL; char *input, *saveptr, *token; int line_invalid; - bool found = false; - - memcpy(&in_mac, mac, sizeof(in_mac)); + uint8_t tq;
debugfs_make_path(DEBUG_BATIF_PATH_FMT "/" DEBUG_ORIGINATORS, mesh_iface, full_path, sizeof(full_path)); @@ -337,8 +333,7 @@ static int get_tq_debugfs(const char *mesh_iface, struct ether_addr *mac, switch (pos) { case orig_mac: mac_tmp = ether_aton(token); - if (!mac_tmp || memcmp(mac_tmp, &in_mac, - sizeof(in_mac)) != 0) + if (!mac_tmp) line_invalid = 1; else pos = orig_lastseen; @@ -365,9 +360,8 @@ static int get_tq_debugfs(const char *mesh_iface, struct ether_addr *mac, line_invalid = 1; } else { token[strlen(token) - 1] = '\0'; - *tq = strtol(token, NULL, 10); - found = true; - goto out; + tq = strtol(token, NULL, 10); + orig_hash_add(orig_hash, mac_tmp, tq); } break; } @@ -377,34 +371,98 @@ static int get_tq_debugfs(const char *mesh_iface, struct ether_addr *mac, } }
-out: if (f) fclose(f); free(line);
- if (found) - return 0; + return 0; +} + +static int orig_compare(void *d1, void *d2) +{ + struct orig_entry *s1 = d1, *s2 = d2; + + if (memcmp(&s1->mac, &s2->mac, sizeof(s1->mac)) == 0) + return 1; else - return -ENOENT; + return 0; }
-uint8_t get_tq(const char *mesh_iface, struct ether_addr *mac) +static int orig_choose(void *d1, int size) { - struct ether_addr in_mac; - uint8_t tq = 0; + struct orig_entry *s1 = d1; + uint32_t hash = 0; + size_t i; + + for (i = 0; i < sizeof(s1->mac); i++) { + hash += s1->mac.ether_addr_octet[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + + return hash % size; +} + +struct hashtable_t *orig_hash_new(const char *mesh_iface) +{ + struct hashtable_t *orig_hash; int ret;
- /* input mac has to be copied because it could be in the shared - * ether_aton buffer - */ - memcpy(&in_mac, mac, sizeof(in_mac)); + orig_hash = hash_new(64, orig_compare, orig_choose); + if (!orig_hash) + return NULL;
enable_net_admin_capability(1); - ret = get_tq_netlink(mesh_iface, &in_mac, &tq); + ret = get_tq_netlink(mesh_iface, orig_hash); enable_net_admin_capability(0);
+ ret = -EOPNOTSUPP; if (ret == -EOPNOTSUPP) - get_tq_debugfs(mesh_iface, &in_mac, &tq); + get_tq_debugfs(mesh_iface, orig_hash); + + return orig_hash; +} + +void orig_hash_free(struct hashtable_t *orig_hash) +{ + hash_delete(orig_hash, free); +} + +int orig_hash_add(struct hashtable_t *orig_hash, struct ether_addr *mac, + uint8_t tq) +{ + struct orig_entry *n; + + n = malloc(sizeof(*n)); + if (!n) + return -ENOMEM; + + n->mac = *mac; + n->tq = tq; + + if (hash_add(orig_hash, n)) { + free(n); + return -EEXIST; + } + + return 0; +} + +uint8_t get_tq(struct hashtable_t *orig_hash, struct ether_addr *mac) +{ + struct orig_entry search = { + .mac = *mac, + .tq = 0, + }; + struct orig_entry *found; + + found = hash_find(orig_hash, &search); + if (!found) + return 0;
- return tq; + return found->tq; } diff --git a/batadv_query.h b/batadv_query.h index 9aa4f0e..aa4d3f8 100644 --- a/batadv_query.h +++ b/batadv_query.h @@ -24,9 +24,21 @@ #include <stdint.h> #include <netinet/in.h>
+#include "hash.h" + +struct orig_entry { + struct ether_addr mac; + uint8_t tq; +}; + struct ether_addr *translate_mac(const char *mesh_iface, const struct ether_addr *mac); -uint8_t get_tq(const char *mesh_iface, struct ether_addr *mac); + +struct hashtable_t *orig_hash_new(const char *mesh_iface); +void orig_hash_free(struct hashtable_t *orig_hash); +int orig_hash_add(struct hashtable_t *orig_hash, struct ether_addr *mac, + uint8_t tq); +uint8_t get_tq(struct hashtable_t *orig_hash, struct ether_addr *mac); int batadv_interface_check(const char *mesh_iface); int mac_to_ipv6(const struct ether_addr *mac, alfred_addr *addr); int ipv6_to_mac(const alfred_addr *addr, struct ether_addr *mac); diff --git a/batadv_querynl.c b/batadv_querynl.c index 8dab96e..ba678ae 100644 --- a/batadv_querynl.c +++ b/batadv_querynl.c @@ -34,7 +34,9 @@ #include <netlink/genl/ctrl.h> #include <net/ethernet.h>
+#include "alfred.h" #include "batman_adv.h" +#include "batadv_query.h" #include "netlink.h"
#ifndef __unused @@ -131,9 +133,7 @@ static const int get_tq_netlink_mandatory[] = { };
struct get_tq_netlink_opts { - struct ether_addr mac; - uint8_t tq; - bool found; + struct hashtable_t *orig_hash; struct nlquery_opts query_opts; };
@@ -145,6 +145,7 @@ static int get_tq_netlink_cb(struct nl_msg *msg, void *arg) struct get_tq_netlink_opts *opts; struct genlmsghdr *ghdr; uint8_t *orig; + struct ether_addr mac; uint8_t tq;
opts = container_of(query_opts, struct get_tq_netlink_opts, @@ -173,40 +174,28 @@ static int get_tq_netlink_cb(struct nl_msg *msg, void *arg) if (!attrs[BATADV_ATTR_FLAG_BEST]) return NL_OK;
- if (memcmp(&opts->mac, orig, ETH_ALEN) != 0) - return NL_OK; - - opts->tq = tq; - opts->found = true; + memcpy(&mac, orig, sizeof(mac)); + orig_hash_add(opts->orig_hash, &mac, tq); opts->query_opts.err = 0;
- return NL_STOP; + return NL_OK; }
-int get_tq_netlink(const char *mesh_iface, const struct ether_addr *mac, - uint8_t *tq) +int get_tq_netlink(const char *mesh_iface, struct hashtable_t *orig_hash) { struct get_tq_netlink_opts opts = { - .tq = 0, - .found = false, + .orig_hash = orig_hash, .query_opts = { .err = 0, }, }; int ret;
- memcpy(&opts.mac, mac, ETH_ALEN); - ret = netlink_query_common(mesh_iface, BATADV_CMD_GET_ORIGINATORS, get_tq_netlink_cb, &opts.query_opts); if (ret < 0) return ret;
- if (!opts.found) - return -ENOENT; - - *tq = opts.tq; - return 0; }
diff --git a/batadv_querynl.h b/batadv_querynl.h index 9b93a47..f5c7e38 100644 --- a/batadv_querynl.h +++ b/batadv_querynl.h @@ -25,11 +25,11 @@ #include <stdint.h>
struct ether_addr; +struct hashtable_t;
int translate_mac_netlink(const char *mesh_iface, const struct ether_addr *mac, struct ether_addr *mac_out); -int get_tq_netlink(const char *mesh_iface, const struct ether_addr *mac, - uint8_t *tq); +int get_tq_netlink(const char *mesh_iface, struct hashtable_t *orig_hash); int batadv_interface_check_netlink(const char *mesh_iface);
#endif /* _BATADV_QUERYNL_H */ diff --git a/server.c b/server.c index 09acb80..91aa729 100644 --- a/server.c +++ b/server.c @@ -223,17 +223,26 @@ static void update_server_info(struct globals *globals) struct hash_it_t *hashit = NULL; struct interface *interface; struct ether_addr *macaddr; + struct hashtable_t *orig_hash;
/* TQ is not used for master sync mode */ if (globals->opmode == OPMODE_MASTER) return;
+ if (strcmp(globals->mesh_iface, "none") != 0) { + orig_hash = orig_hash_new(globals->mesh_iface); + if (!globals->data_hash) { + fprintf(stderr, "Failed to originator hash\n"); + return; + } + } + list_for_each_entry(interface, &globals->interfaces, list) { while (NULL != (hashit = hash_iterate(interface->server_hash, hashit))) { struct server *server = hashit->bucket->data;
- if (strcmp(globals->mesh_iface, "none") == 0) { + if (!orig_hash) { server->tq = 255; continue; } @@ -241,14 +250,16 @@ static void update_server_info(struct globals *globals) macaddr = translate_mac(globals->mesh_iface, &server->hwaddr); if (macaddr) - server->tq = get_tq(globals->mesh_iface, - macaddr); + server->tq = get_tq(orig_hash, macaddr); else server->tq = 0; } }
set_best_server(globals); + + if (orig_hash) + orig_hash_free(orig_hash); }
static void check_if_socket(struct interface *interface, struct globals *globals)
There is a single loop which goes over all servers to find the originator for each server. The originator usually doesn't change often because alfred is usually started on the nodes which is already running batman-adv. It is therefore not necessary to re-request the complete global translation table from the kernel for each server in the server hash. Instead, the values can be retrieved ones from the kernel during each sync interval, cached and then retrieved from the cache again.
Signed-off-by: Sven Eckelmann sven.eckelmann@openmesh.com --- batadv_query.c | 117 +++++++++++++++++++++++++++++++++++++++++-------------- batadv_query.h | 11 +++++- batadv_querynl.c | 25 ++++-------- batadv_querynl.h | 3 +- server.c | 15 +++++-- 5 files changed, 118 insertions(+), 53 deletions(-)
diff --git a/batadv_query.c b/batadv_query.c index 1123cf5..8580f1b 100644 --- a/batadv_query.c +++ b/batadv_query.c @@ -193,8 +193,7 @@ int batadv_interface_check(const char *mesh_iface) }
static int translate_mac_debugfs(const char *mesh_iface, - const struct ether_addr *mac, - struct ether_addr *mac_out) + struct hashtable_t *tg_hash) { enum { tg_start, @@ -204,12 +203,12 @@ static int translate_mac_debugfs(const char *mesh_iface, } pos; char full_path[MAX_PATH+1]; struct ether_addr *mac_tmp; + struct ether_addr mac; FILE *f = NULL; size_t len = 0; char *line = NULL; char *input, *saveptr, *token; int line_invalid; - bool found = false;
debugfs_make_path(DEBUG_BATIF_PATH_FMT "/" DEBUG_TRANSTABLE_GLOBAL, mesh_iface, full_path, sizeof(full_path)); @@ -235,11 +234,12 @@ static int translate_mac_debugfs(const char *mesh_iface, break; case tg_mac: mac_tmp = ether_aton(token); - if (!mac_tmp || memcmp(mac_tmp, mac, - ETH_ALEN) != 0) + if (!mac_tmp) { line_invalid = 1; - else + } else { + memcpy(&mac, mac_tmp, sizeof(mac)); pos = tg_via; + } break; case tg_via: if (strcmp(token, "via") == 0) @@ -247,13 +247,10 @@ static int translate_mac_debugfs(const char *mesh_iface, break; case tg_originator: mac_tmp = ether_aton(token); - if (!mac_tmp) { + if (!mac_tmp) line_invalid = 1; - } else { - memcpy(mac_out, mac_tmp, ETH_ALEN); - found = true; - goto out; - } + else + tg_hash_add(tg_hash, &mac, mac_tmp); break; }
@@ -262,40 +259,100 @@ static int translate_mac_debugfs(const char *mesh_iface, } }
-out: if (f) fclose(f); free(line);
- if (found) - return 0; + return 0; +} + +static int tg_compare(void *d1, void *d2) +{ + struct tg_entry *s1 = d1, *s2 = d2; + + if (memcmp(&s1->mac, &s2->mac, sizeof(s1->mac)) == 0) + return 1; else - return -ENOENT; + return 0; }
-struct ether_addr *translate_mac(const char *mesh_iface, - const struct ether_addr *mac) +static int tg_choose(void *d1, int size) { - struct ether_addr in_mac; - static struct ether_addr out_mac; - struct ether_addr *mac_result; + struct tg_entry *s1 = d1; + uint32_t hash = 0; + size_t i; + + for (i = 0; i < sizeof(s1->mac); i++) { + hash += s1->mac.ether_addr_octet[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + + return hash % size; +} + +struct hashtable_t *tg_hash_new(const char *mesh_iface) +{ + struct hashtable_t *tg_hash; int ret;
- /* input mac has to be copied because it could be in the shared - * ether_aton buffer - */ - memcpy(&in_mac, mac, sizeof(in_mac)); - memcpy(&out_mac, mac, sizeof(out_mac)); - mac_result = &out_mac; + tg_hash = hash_new(64, tg_compare, tg_choose); + if (!tg_hash) + return NULL;
enable_net_admin_capability(1); - ret = translate_mac_netlink(mesh_iface, &in_mac, mac_result); + ret = translate_mac_netlink(mesh_iface, tg_hash); enable_net_admin_capability(0);
+ ret = -EOPNOTSUPP; if (ret == -EOPNOTSUPP) - translate_mac_debugfs(mesh_iface, &in_mac, mac_result); + translate_mac_debugfs(mesh_iface, tg_hash); + + return tg_hash; +} + +void tg_hash_free(struct hashtable_t *tg_hash) +{ + hash_delete(tg_hash, free); +} + +int tg_hash_add(struct hashtable_t *tg_hash, struct ether_addr *mac, + struct ether_addr *originator) +{ + struct tg_entry *n; + + n = malloc(sizeof(*n)); + if (!n) + return -ENOMEM; + + n->mac = *mac; + n->originator = *originator; + + if (hash_add(tg_hash, n)) { + free(n); + return -EEXIST; + } + + return 0; +} + +struct ether_addr *translate_mac(struct hashtable_t *tg_hash, + const struct ether_addr *mac) +{ + struct tg_entry search = { + .mac = *mac, + }; + struct tg_entry *found; + + found = hash_find(tg_hash, &search); + if (!found) + return 0;
- return mac_result; + return &found->originator; }
static int get_tq_debugfs(const char *mesh_iface, struct hashtable_t *orig_hash) diff --git a/batadv_query.h b/batadv_query.h index aa4d3f8..dc2b135 100644 --- a/batadv_query.h +++ b/batadv_query.h @@ -31,7 +31,16 @@ struct orig_entry { uint8_t tq; };
-struct ether_addr *translate_mac(const char *mesh_iface, +struct tg_entry { + struct ether_addr mac; + struct ether_addr originator; +}; + +struct hashtable_t *tg_hash_new(const char *mesh_iface); +void tg_hash_free(struct hashtable_t *tg_hash); +int tg_hash_add(struct hashtable_t *tg_hash, struct ether_addr *mac, + struct ether_addr *originator); +struct ether_addr *translate_mac(struct hashtable_t *tg_hash, const struct ether_addr *mac);
struct hashtable_t *orig_hash_new(const char *mesh_iface); diff --git a/batadv_querynl.c b/batadv_querynl.c index ba678ae..ca9ee2c 100644 --- a/batadv_querynl.c +++ b/batadv_querynl.c @@ -49,8 +49,7 @@ static const int translate_mac_netlink_mandatory[] = { };
struct translate_mac_netlink_opts { - struct ether_addr mac; - bool found; + struct hashtable_t *tg_hash; struct nlquery_opts query_opts; };
@@ -61,6 +60,8 @@ static int translate_mac_netlink_cb(struct nl_msg *msg, void *arg) struct nlquery_opts *query_opts = arg; struct translate_mac_netlink_opts *opts; struct genlmsghdr *ghdr; + struct ether_addr mac_addr; + struct ether_addr mac_orig; uint8_t *addr; uint8_t *orig;
@@ -90,40 +91,30 @@ static int translate_mac_netlink_cb(struct nl_msg *msg, void *arg) if (!attrs[BATADV_ATTR_FLAG_BEST]) return NL_OK;
- if (memcmp(&opts->mac, addr, ETH_ALEN) != 0) - return NL_OK; - - memcpy(&opts->mac, orig, ETH_ALEN); - opts->found = true; + memcpy(&mac_addr, addr, sizeof(mac_addr)); + memcpy(&mac_orig, orig, sizeof(mac_orig)); + tg_hash_add(opts->tg_hash, &mac_addr, &mac_orig); opts->query_opts.err = 0;
return NL_STOP; }
-int translate_mac_netlink(const char *mesh_iface, const struct ether_addr *mac, - struct ether_addr *mac_out) +int translate_mac_netlink(const char *mesh_iface, struct hashtable_t *tg_hash) { struct translate_mac_netlink_opts opts = { - .found = false, + .tg_hash = tg_hash, .query_opts = { .err = 0, }, }; int ret;
- memcpy(&opts.mac, mac, ETH_ALEN); - ret = netlink_query_common(mesh_iface, BATADV_CMD_GET_TRANSTABLE_GLOBAL, translate_mac_netlink_cb, &opts.query_opts); if (ret < 0) return ret;
- if (!opts.found) - return -ENOENT; - - memcpy(mac_out, &opts.mac, ETH_ALEN); - return 0; }
diff --git a/batadv_querynl.h b/batadv_querynl.h index f5c7e38..4b42ed5 100644 --- a/batadv_querynl.h +++ b/batadv_querynl.h @@ -27,8 +27,7 @@ struct ether_addr; struct hashtable_t;
-int translate_mac_netlink(const char *mesh_iface, const struct ether_addr *mac, - struct ether_addr *mac_out); +int translate_mac_netlink(const char *mesh_iface, struct hashtable_t *tg_hash); int get_tq_netlink(const char *mesh_iface, struct hashtable_t *orig_hash); int batadv_interface_check_netlink(const char *mesh_iface);
diff --git a/server.c b/server.c index 91aa729..f28c89a 100644 --- a/server.c +++ b/server.c @@ -223,6 +223,7 @@ static void update_server_info(struct globals *globals) struct hash_it_t *hashit = NULL; struct interface *interface; struct ether_addr *macaddr; + struct hashtable_t *tg_hash; struct hashtable_t *orig_hash;
/* TQ is not used for master sync mode */ @@ -230,10 +231,16 @@ static void update_server_info(struct globals *globals) return;
if (strcmp(globals->mesh_iface, "none") != 0) { + tg_hash = tg_hash_new(globals->mesh_iface); + if (!globals->data_hash) { + fprintf(stderr, "Failed to translation hash\n"); + return; + } + orig_hash = orig_hash_new(globals->mesh_iface); if (!globals->data_hash) { fprintf(stderr, "Failed to originator hash\n"); - return; + goto free_tg_hash; } }
@@ -247,8 +254,7 @@ static void update_server_info(struct globals *globals) continue; }
- macaddr = translate_mac(globals->mesh_iface, - &server->hwaddr); + macaddr = translate_mac(tg_hash, &server->hwaddr); if (macaddr) server->tq = get_tq(orig_hash, macaddr); else @@ -260,6 +266,9 @@ static void update_server_info(struct globals *globals)
if (orig_hash) orig_hash_free(orig_hash); +free_tg_hash: + if (tg_hash) + tg_hash_free(tg_hash); }
static void check_if_socket(struct interface *interface, struct globals *globals)
On Wednesday, May 24, 2017 12:31:28 PM CEST Sven Eckelmann wrote:
Hi,
alfred uses the TQ from batman-adv to find its best alfred neighbor. This best neighbor information is used by slave servers to request/publish data.
This is done for each server announcement packet by:
- requesting the global translation table (netlink or debugfs) and then searching in it for the MAC address of the detected alfred server to find
its originator address
- requesting the originator table (netlink or debugfs) and then searching
it it for the originator address of the detected alfred server to find its TQ value
This was previously done whenever a new announcement packet received by alfred. We've observed that this can be a problem on networks with a lot of master servers (~100) which can see each other, large translation tables and slow CPUs. alfred still worked but the CPU load by alfred was rather high (~20% on an 560MHz AR9344).
The idea is now to avoid this lookup for master servers. And (for slave servers) to process all servers at once. This is done before the wants to push its local data to a master server in an sync interval.
The process which was described earlier was now changed to:
- requesting the global translation table (netlink or debugfs) and then put
MAC address and corresponding originator address in tg hash
- requesting the originator table (netlink or debugfs) and then put originator address and corresponding TQ value orig hash
- got through all servers:
its originator address
- search in tg hash for for the MAC address of the alfred server to find
- search in orig hash for for the originator address of the alfred server
to find its TQ value
These changes reduced the load on the previously mentioned devices significantly.
I've applied this patch series with some fixes in patch 4 and 5 regarding the hash initialization.
Thank you, Simon
On Mittwoch, 31. Mai 2017 17:24:45 CEST Simon Wunderlich wrote: [....]
I've applied this patch series with some fixes in patch 4 and 5 regarding the hash initialization.
Thank you, Simon
It looks like you haven't actually changed it. I will post fixes for it.
Kind regards, Sven
b.a.t.m.a.n@lists.open-mesh.org