From b7f873bbbcd54c5564f63f487c976868fd3f049d Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Mon, 27 Dec 2021 10:49:07 -0600 Subject: [PATCH] hwsim: Optimize frame forwarding Right now hwsim blindly tries to forward broadcast/multicast frames to all interfaces it knows about and relies on the kernel to reject the forwarding attempt if the frequency does not match. This results in multiple copies of the same message being added to the genl transmit queue. On slower systems this can cause a run-away memory consumption effect where the queued messages are not processed in time prior to a new message being received for forwarding. The likelyhood of this effect manifesting itself is directly related to the number of hostapd instances that are created and are beaconing simultaneously. Try to optimize frame forwarding by not sending beacon frames to those interfaces that are in AP mode (i.e. pure hostapd instances) since such interfaces are going to be operating on a different frequency and would not be interested in processing beacon frames anyway. This optimization cuts down peak memory use during certain tests by 30x or more (~33mb to ~1mb) when profiled with 'valgrind --tool=massif' --- tools/hwsim.c | 76 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 23 deletions(-) diff --git a/tools/hwsim.c b/tools/hwsim.c index 8c6b1c66..7c32b869 100644 --- a/tools/hwsim.c +++ b/tools/hwsim.c @@ -414,6 +414,7 @@ struct radio_info_rec { int channels; uint8_t addrs[2][ETH_ALEN]; char *name; + bool ap_only; }; struct interface_info_rec { @@ -776,6 +777,27 @@ err_free_radio: dbus_error_failed(pending_create_msg)); } +static bool radio_ap_only(struct radio_info_rec *rec) +{ + const struct l_queue_entry *i; + unsigned int n = 0; + bool have_ap = false; + + for (i = l_queue_get_entries(interface_info); i; i = i->next) { + struct interface_info_rec *interface = i->data; + + if (interface->radio_rec != rec) + continue; + + if (interface->iftype == NL80211_IFTYPE_AP) + have_ap = true; + + n += 1; + } + + return n == 1 && have_ap; +} + static void get_wiphy_callback(struct l_genl_msg *msg, void *user_data) { const char *name; @@ -888,6 +910,8 @@ static void get_interface_callback(struct l_genl_msg *msg, void *user_data) HWSIM_INTERFACE_INTERFACE, "Name"); } + + radio_rec->ap_only = radio_ap_only(radio_rec); } static bool interface_info_destroy_by_radio(void *data, void *user_data) @@ -964,6 +988,8 @@ static void set_interface_event(struct l_genl_msg *msg) l_debug("Interface iftype changed for ifindex: %u, iftype: %u", ifindex, iftype); interface->iftype = iftype; + + interface->radio_rec->ap_only = radio_ap_only(interface->radio_rec); } static void del_interface_event(struct l_genl_msg *msg) @@ -1396,20 +1422,6 @@ error: return false; } -struct interface_match_data { - struct radio_info_rec *radio; - const uint8_t *addr; -}; - -static bool interface_info_match_dst(const void *a, const void *b) -{ - const struct interface_info_rec *rec = a; - const struct interface_match_data *dst = b; - - return rec->radio_rec == dst->radio && - !memcmp(rec->addr, dst->addr, ETH_ALEN); -} - static void frame_delay_callback(struct l_timeout *timeout, void *user_data) { struct send_frame_info *send_info = user_data; @@ -1434,17 +1446,24 @@ static void process_frame(struct hwsim_frame *frame) { const struct l_queue_entry *entry; bool drop_mcast = false; + bool beacon = false; if (util_is_broadcast_address(frame->dst_ether_addr)) process_rules(frame->src_radio, NULL, frame, false, &drop_mcast, NULL); + if (frame->payload_len >= 2 && + frame->payload[0] == 0x80 && + frame->payload[1] == 0x00) + beacon = true; + for (entry = l_queue_get_entries(radio_info); entry; entry = entry->next) { struct radio_info_rec *radio = entry->data; struct send_frame_info *send_info; bool drop = drop_mcast; uint32_t delay = 0; + const struct l_queue_entry *i; if (radio == frame->src_radio) continue; @@ -1464,16 +1483,20 @@ static void process_frame(struct hwsim_frame *frame) * at least one interface with this specific address. */ if (!util_is_broadcast_address(frame->dst_ether_addr)) { - struct interface_match_data match_data = { - radio, - frame->dst_ether_addr, - }; - struct interface_info_rec *interface = - l_queue_find(interface_info, - interface_info_match_dst, - &match_data); + for (i = l_queue_get_entries(interface_info); + i; i = i->next) { + struct interface_info_rec *interface = i->data; - if (!interface) + if (interface->radio_rec != radio) + continue; + + if (!memcmp(interface->addr, + frame->dst_ether_addr, + ETH_ALEN)) + break; + } + + if (!i) continue; } @@ -1483,6 +1506,13 @@ static void process_frame(struct hwsim_frame *frame) if (drop) continue; + /* + * Don't bother sending beacons to other AP interfaces + * if the AP interface is the only one on this phy + */ + if (beacon && radio->ap_only) + continue; + send_info = l_new(struct send_frame_info, 1); send_info->radio = radio; send_info->frame = hwsim_frame_ref(frame);