mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2024-11-25 09:39:25 +01:00
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'
This commit is contained in:
parent
ea3fd01ebb
commit
b7f873bbbc
@ -414,6 +414,7 @@ struct radio_info_rec {
|
|||||||
int channels;
|
int channels;
|
||||||
uint8_t addrs[2][ETH_ALEN];
|
uint8_t addrs[2][ETH_ALEN];
|
||||||
char *name;
|
char *name;
|
||||||
|
bool ap_only;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct interface_info_rec {
|
struct interface_info_rec {
|
||||||
@ -776,6 +777,27 @@ err_free_radio:
|
|||||||
dbus_error_failed(pending_create_msg));
|
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)
|
static void get_wiphy_callback(struct l_genl_msg *msg, void *user_data)
|
||||||
{
|
{
|
||||||
const char *name;
|
const char *name;
|
||||||
@ -888,6 +910,8 @@ static void get_interface_callback(struct l_genl_msg *msg, void *user_data)
|
|||||||
HWSIM_INTERFACE_INTERFACE,
|
HWSIM_INTERFACE_INTERFACE,
|
||||||
"Name");
|
"Name");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
radio_rec->ap_only = radio_ap_only(radio_rec);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool interface_info_destroy_by_radio(void *data, void *user_data)
|
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",
|
l_debug("Interface iftype changed for ifindex: %u, iftype: %u",
|
||||||
ifindex, iftype);
|
ifindex, iftype);
|
||||||
interface->iftype = 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)
|
static void del_interface_event(struct l_genl_msg *msg)
|
||||||
@ -1396,20 +1422,6 @@ error:
|
|||||||
return false;
|
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)
|
static void frame_delay_callback(struct l_timeout *timeout, void *user_data)
|
||||||
{
|
{
|
||||||
struct send_frame_info *send_info = 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;
|
const struct l_queue_entry *entry;
|
||||||
bool drop_mcast = false;
|
bool drop_mcast = false;
|
||||||
|
bool beacon = false;
|
||||||
|
|
||||||
if (util_is_broadcast_address(frame->dst_ether_addr))
|
if (util_is_broadcast_address(frame->dst_ether_addr))
|
||||||
process_rules(frame->src_radio, NULL, frame, false,
|
process_rules(frame->src_radio, NULL, frame, false,
|
||||||
&drop_mcast, NULL);
|
&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;
|
for (entry = l_queue_get_entries(radio_info); entry;
|
||||||
entry = entry->next) {
|
entry = entry->next) {
|
||||||
struct radio_info_rec *radio = entry->data;
|
struct radio_info_rec *radio = entry->data;
|
||||||
struct send_frame_info *send_info;
|
struct send_frame_info *send_info;
|
||||||
bool drop = drop_mcast;
|
bool drop = drop_mcast;
|
||||||
uint32_t delay = 0;
|
uint32_t delay = 0;
|
||||||
|
const struct l_queue_entry *i;
|
||||||
|
|
||||||
if (radio == frame->src_radio)
|
if (radio == frame->src_radio)
|
||||||
continue;
|
continue;
|
||||||
@ -1464,16 +1483,20 @@ static void process_frame(struct hwsim_frame *frame)
|
|||||||
* at least one interface with this specific address.
|
* at least one interface with this specific address.
|
||||||
*/
|
*/
|
||||||
if (!util_is_broadcast_address(frame->dst_ether_addr)) {
|
if (!util_is_broadcast_address(frame->dst_ether_addr)) {
|
||||||
struct interface_match_data match_data = {
|
for (i = l_queue_get_entries(interface_info);
|
||||||
radio,
|
i; i = i->next) {
|
||||||
frame->dst_ether_addr,
|
struct interface_info_rec *interface = i->data;
|
||||||
};
|
|
||||||
struct interface_info_rec *interface =
|
|
||||||
l_queue_find(interface_info,
|
|
||||||
interface_info_match_dst,
|
|
||||||
&match_data);
|
|
||||||
|
|
||||||
if (!interface)
|
if (interface->radio_rec != radio)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!memcmp(interface->addr,
|
||||||
|
frame->dst_ether_addr,
|
||||||
|
ETH_ALEN))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!i)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1483,6 +1506,13 @@ static void process_frame(struct hwsim_frame *frame)
|
|||||||
if (drop)
|
if (drop)
|
||||||
continue;
|
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 = l_new(struct send_frame_info, 1);
|
||||||
send_info->radio = radio;
|
send_info->radio = radio;
|
||||||
send_info->frame = hwsim_frame_ref(frame);
|
send_info->frame = hwsim_frame_ref(frame);
|
||||||
|
Loading…
Reference in New Issue
Block a user