AP MLD: Clean up MLD when not required any further
Currently, whenever a new BSS is created, if it is an EHT BSS it is tied to a corresponding MLD structure. If the structure does not exist already, a new one is created and tied to it. Accordingly, the link ID is assigned as well. However, when the BSS is deleted, the MLD structure is not freed and when it is again created the next time, the link ID is incremented further and the BSS gets a wrong link ID. For example, 2.4 GHz single link AP MLD case: First ADD, link ID 0 would be assigned and MLD interface wlan0 would be created. When REMOVE is issued, the BSS would be deleted but MLD wlan0 will not. When ADD is issued again, the BSS will tie back to MLD wlan0 but this time the link ID will be incremented again and 1 would be assigned. Hence, at subsequent REMOVE/ADD, the link ID keeps on incrementing. Since the link ID remains same for the full lifetime of the BSS and MLD, the next link ID counter cannot be just reset back to 0 when a BSS is deleted. Otherwise, in interleaved link enable/disable case, the link ID would be changed. To overcome this situation, whenever a BSS is deleted, if the MLD is not referenced by any other existing BSS, delete the MLD structure itself. To know how many BSSs are referring a given MLD, introduce a new member refcount in MLD. If the value is 0 it is safe to delete the MLD. Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
This commit is contained in:
parent
fac34688ad
commit
60e1dca1ef
2 changed files with 138 additions and 0 deletions
130
src/ap/hostapd.c
130
src/ap/hostapd.c
|
@ -2886,6 +2886,40 @@ void hostapd_interface_deinit(struct hostapd_iface *iface)
|
|||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_IEEE80211BE
|
||||
|
||||
static void hostapd_mld_ref_inc(struct hostapd_mld *mld)
|
||||
{
|
||||
if (!mld)
|
||||
return;
|
||||
|
||||
if (mld->refcount == HOSTAPD_MLD_MAX_REF_COUNT) {
|
||||
wpa_printf(MSG_ERROR, "AP MLD %s: Ref count overflow",
|
||||
mld->name);
|
||||
return;
|
||||
}
|
||||
|
||||
mld->refcount++;
|
||||
}
|
||||
|
||||
|
||||
static void hostapd_mld_ref_dec(struct hostapd_mld *mld)
|
||||
{
|
||||
if (!mld)
|
||||
return;
|
||||
|
||||
if (!mld->refcount) {
|
||||
wpa_printf(MSG_ERROR, "AP MLD %s: Ref count underflow",
|
||||
mld->name);
|
||||
return;
|
||||
}
|
||||
|
||||
mld->refcount--;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_IEEE80211BE */
|
||||
|
||||
|
||||
void hostapd_interface_free(struct hostapd_iface *iface)
|
||||
{
|
||||
size_t j;
|
||||
|
@ -2893,6 +2927,10 @@ void hostapd_interface_free(struct hostapd_iface *iface)
|
|||
for (j = 0; j < iface->num_bss; j++) {
|
||||
if (!iface->bss)
|
||||
break;
|
||||
#ifdef CONFIG_IEEE80211BE
|
||||
if (iface->bss[j])
|
||||
hostapd_mld_ref_dec(iface->bss[j]->mld);
|
||||
#endif /* CONFIG_IEEE80211BE */
|
||||
wpa_printf(MSG_DEBUG, "%s: free hapd %p",
|
||||
__func__, iface->bss[j]);
|
||||
os_free(iface->bss[j]);
|
||||
|
@ -2946,6 +2984,7 @@ static void hostapd_bss_setup_multi_link(struct hostapd_data *hapd,
|
|||
continue;
|
||||
|
||||
hapd->mld = mld;
|
||||
hostapd_mld_ref_inc(mld);
|
||||
hostapd_bss_alloc_link_id(hapd);
|
||||
break;
|
||||
}
|
||||
|
@ -2963,6 +3002,7 @@ static void hostapd_bss_setup_multi_link(struct hostapd_data *hapd,
|
|||
wpa_printf(MSG_DEBUG, "AP MLD %s created", mld->name);
|
||||
|
||||
hapd->mld = mld;
|
||||
hostapd_mld_ref_inc(mld);
|
||||
hostapd_bss_alloc_link_id(hapd);
|
||||
|
||||
all_mld = os_realloc_array(interfaces->mld, interfaces->mld_count + 1,
|
||||
|
@ -2986,6 +3026,84 @@ fail:
|
|||
}
|
||||
|
||||
|
||||
static void hostapd_cleanup_unused_mlds(struct hapd_interfaces *interfaces)
|
||||
{
|
||||
#ifdef CONFIG_IEEE80211BE
|
||||
struct hostapd_mld *mld, **all_mld;
|
||||
size_t i, j, num_mlds;
|
||||
bool forced_remove, remove;
|
||||
|
||||
if (!interfaces->mld)
|
||||
return;
|
||||
|
||||
num_mlds = interfaces->mld_count;
|
||||
|
||||
for (i = 0; i < interfaces->mld_count; i++) {
|
||||
mld = interfaces->mld[i];
|
||||
if (!mld)
|
||||
continue;
|
||||
|
||||
remove = false;
|
||||
forced_remove = false;
|
||||
|
||||
if (!mld->refcount)
|
||||
remove = true;
|
||||
|
||||
/* If MLD is still being referenced but the number of interfaces
|
||||
* is zero, it is safe to force its deletion. Normally, this
|
||||
* should not happen but even if it does, let us free the
|
||||
* memory.
|
||||
*/
|
||||
if (!remove && !interfaces->count)
|
||||
forced_remove = true;
|
||||
|
||||
if (!remove && !forced_remove)
|
||||
continue;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "AP MLD %s: Freed%s", mld->name,
|
||||
forced_remove ? " (forced)" : "");
|
||||
os_free(mld);
|
||||
interfaces->mld[i] = NULL;
|
||||
num_mlds--;
|
||||
}
|
||||
|
||||
if (!num_mlds) {
|
||||
interfaces->mld_count = 0;
|
||||
os_free(interfaces->mld);
|
||||
interfaces->mld = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
all_mld = os_zalloc(num_mlds * sizeof(struct hostapd_mld *));
|
||||
if (!all_mld) {
|
||||
wpa_printf(MSG_ERROR,
|
||||
"AP MLD: Failed to re-allocate the MLDs. Expect issues");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0, j = 0; i < interfaces->mld_count; i++) {
|
||||
mld = interfaces->mld[i];
|
||||
if (!mld)
|
||||
continue;
|
||||
|
||||
all_mld[j++] = mld;
|
||||
}
|
||||
|
||||
/* This should not happen */
|
||||
if (j != num_mlds) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"AP MLD: Some error occurred while reallocating MLDs. Expect issues.");
|
||||
os_free(all_mld);
|
||||
return;
|
||||
}
|
||||
|
||||
os_free(interfaces->mld);
|
||||
interfaces->mld = all_mld;
|
||||
interfaces->mld_count = num_mlds;
|
||||
#endif /* CONFIG_IEEE80211BE */
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* hostapd_init - Allocate and initialize per-interface data
|
||||
* @config_file: Path to the configuration file
|
||||
|
@ -3577,6 +3695,9 @@ int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf)
|
|||
__func__, hapd, hapd->conf->iface);
|
||||
hostapd_config_free_bss(hapd->conf);
|
||||
hapd->conf = NULL;
|
||||
#ifdef CONFIG_IEEE80211BE
|
||||
hostapd_mld_ref_dec(hapd->mld);
|
||||
#endif /* CONFIG_IEEE80211BE */
|
||||
os_free(hapd);
|
||||
return -1;
|
||||
}
|
||||
|
@ -3668,6 +3789,9 @@ fail:
|
|||
hapd->conf->iface);
|
||||
hostapd_bss_link_deinit(hapd);
|
||||
hostapd_cleanup(hapd);
|
||||
#ifdef CONFIG_IEEE80211BE
|
||||
hostapd_mld_ref_dec(hapd->mld);
|
||||
#endif /* CONFIG_IEEE80211BE */
|
||||
os_free(hapd);
|
||||
hapd_iface->bss[i] = NULL;
|
||||
}
|
||||
|
@ -3677,6 +3801,7 @@ fail:
|
|||
if (new_iface) {
|
||||
interfaces->count--;
|
||||
interfaces->iface[interfaces->count] = NULL;
|
||||
hostapd_cleanup_unused_mlds(interfaces);
|
||||
}
|
||||
hostapd_cleanup_iface(hapd_iface);
|
||||
}
|
||||
|
@ -3699,6 +3824,9 @@ static int hostapd_remove_bss(struct hostapd_iface *iface, unsigned int idx)
|
|||
__func__, hapd, hapd->conf->iface);
|
||||
hostapd_config_free_bss(hapd->conf);
|
||||
hapd->conf = NULL;
|
||||
#ifdef CONFIG_IEEE80211BE
|
||||
hostapd_mld_ref_dec(hapd->mld);
|
||||
#endif /* CONFIG_IEEE80211BE */
|
||||
os_free(hapd);
|
||||
|
||||
iface->num_bss--;
|
||||
|
@ -3741,6 +3869,8 @@ int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf)
|
|||
k++;
|
||||
}
|
||||
interfaces->count--;
|
||||
hostapd_cleanup_unused_mlds(interfaces);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -504,10 +504,18 @@ struct hostapd_mld {
|
|||
u8 mld_addr[ETH_ALEN];
|
||||
u8 next_link_id;
|
||||
u8 num_links;
|
||||
/* Number of hostapd_data (hapd) referencing this. num_links cannot be
|
||||
* used since num_links can go to 0 even when a BSS is disabled and
|
||||
* when it is re-enabled, the MLD should exist and hence it cannot be
|
||||
* freed when num_links is 0.
|
||||
*/
|
||||
u8 refcount;
|
||||
|
||||
struct hostapd_data *fbss;
|
||||
struct dl_list links; /* List head of all affiliated links */
|
||||
};
|
||||
|
||||
#define HOSTAPD_MLD_MAX_REF_COUNT 0xFF
|
||||
#endif /* CONFIG_IEEE80211BE */
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue