diff mbox series

[14/16] hostapd: MLO: de-initialize/disable link BSS properly

Message ID 20240306173947.2611965-15-quic_adisi@quicinc.com
State Accepted
Headers show
Series hostapd: scale up MLO flows | expand

Commit Message

Aditya Kumar Singh March 6, 2024, 5:39 p.m. UTC
Currently, if first link BSS of an interface is getting de-initialized/
disabled, then whole MLD is brought down. All other links are made
to stop beaconing and links are removed. And if, non-first link BSS
is de-initialized/disabled, nothing happens. Even beaconing is not
stopped which is wrong.

Fix the above by properly bringing down the intended link alone from the
interface.

Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
---
 hostapd/main.c               |   1 +
 src/ap/hostapd.c             | 100 +++++++++++++++++++++--------------
 src/drivers/driver.h         |  13 +++++
 src/drivers/driver_nl80211.c |  33 ++++++++++++
 4 files changed, 107 insertions(+), 40 deletions(-)
diff mbox series

Patch

diff --git a/hostapd/main.c b/hostapd/main.c
index 97e9704c7046..1ac9b4be30fc 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -195,6 +195,7 @@  static int hostapd_driver_init(struct hostapd_iface *iface)
 		}
 
 		hapd->drv_priv = h_hapd->drv_priv;
+		hapd->interface_added = h_hapd->interface_added;
 
 		/*
 		 * All interfaces participating in the AP MLD would have
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 0171649b21cc..9dd9e6c3fd8d 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -401,28 +401,6 @@  static int hostapd_broadcast_wep_set(struct hostapd_data *hapd)
 
 #endif /* CONFIG_WEP */
 
-
-static void hostapd_clear_drv_priv(struct hostapd_data *hapd)
-{
-#ifdef CONFIG_IEEE80211BE
-	unsigned int i;
-
-	for (i = 0; i < hapd->iface->interfaces->count; i++) {
-		struct hostapd_iface *iface = hapd->iface->interfaces->iface[i];
-
-		if (hapd->iface == iface || !iface)
-			continue;
-
-		if (iface->bss && iface->bss[0] &&
-		    hostapd_mld_get_first_bss(iface->bss[0]) == hapd)
-			iface->bss[0]->drv_priv = NULL;
-	}
-#endif /* CONFIG_IEEE80211BE */
-
-	hapd->drv_priv = NULL;
-}
-
-
 #ifdef CONFIG_IEEE80211BE
 #ifdef CONFIG_TESTING_OPTIONS
 
@@ -561,10 +539,22 @@  void hostapd_free_hapd_data(struct hostapd_data *hapd)
 			 * driver wrapper may have removed its internal instance
 			 * and hapd->drv_priv is not valid anymore.
 			 */
-			hostapd_clear_drv_priv(hapd);
+			hapd->drv_priv = NULL;
 		}
 	}
 
+#ifdef CONFIG_IEEE80211BE
+	/* If interface was not added as well as it is not the first bss, then
+	 * at least link should be removed here since deinit will take care only
+	 * for the first bss.
+	 */
+	if (hapd->conf->mld_ap && !hapd->interface_added &&
+	    hapd->iface->bss[0] != hapd) {
+		hostapd_if_link_remove(hapd, WPA_IF_AP_BSS, hapd->conf->iface,
+				       hapd->mld_link_id);
+	}
+#endif /* CONFIG_IEEE80211BE */
+
 	wpabuf_free(hapd->time_adv);
 	hapd->time_adv = NULL;
 
@@ -3305,6 +3295,41 @@  hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
 	return iface;
 }
 
+static void hostapd_cleanup_driver(const struct wpa_driver_ops *driver,
+				   void *drv_priv,
+				   struct hostapd_iface *iface)
+{
+	bool ap_mld = false;
+
+	if (!driver || !driver->hapd_deinit || !drv_priv)
+		return;
+
+#ifdef CONFIG_IEEE80211BE
+	ap_mld = !!iface->bss[0]->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
+
+	/* In case of non-ML operation, de-init. But, if ML operation exist, then
+	 * even if thats the last BSS in the interface, the driver (drv) could
+	 * be in use for a different MLD. Hence, need to check if drv is still
+	 * being used by some other bss before de-initiallizing
+	 */
+	if (!ap_mld)
+		driver->hapd_deinit(drv_priv);
+#ifdef CONFIG_IEEE80211BE
+	else if (hostapd_mld_is_first_bss(iface->bss[0]) &&
+		 driver->get_drv_shared_status &&
+		 !driver->get_drv_shared_status(drv_priv, iface->bss[0])) {
+		driver->hapd_deinit(drv_priv);
+	} else if (hostapd_if_link_remove(iface->bss[0],
+					 WPA_IF_AP_BSS,
+					 iface->bss[0]->conf->iface,
+					 iface->bss[0]->mld_link_id)) {
+		wpa_printf(MSG_WARNING, "Failed to remove BSS interface %s",
+			   iface->bss[0]->conf->iface);
+	}
+#endif /* CONFIG_IEEE80211BE */
+	iface->bss[0]->drv_priv = NULL;
+}
 
 void hostapd_interface_deinit_free(struct hostapd_iface *iface)
 {
@@ -3322,13 +3347,7 @@  void hostapd_interface_deinit_free(struct hostapd_iface *iface)
 	hostapd_interface_deinit(iface);
 	wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
 		   __func__, driver, drv_priv);
-	if (driver && driver->hapd_deinit && drv_priv) {
-#ifdef CONFIG_IEEE80211BE
-		if (hostapd_mld_is_first_bss(iface->bss[0]))
-#endif /* CONFIG_IEEE80211BE */
-			driver->hapd_deinit(drv_priv);
-		hostapd_clear_drv_priv(iface->bss[0]);
-	}
+	hostapd_cleanup_driver(driver, drv_priv, iface);
 	hostapd_interface_free(iface);
 }
 
@@ -3341,21 +3360,22 @@  static void hostapd_deinit_driver(const struct wpa_driver_ops *driver,
 
 	wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
 		   __func__, driver, drv_priv);
+
+	hostapd_cleanup_driver(driver, drv_priv, hapd_iface);
+
 	if (driver && driver->hapd_deinit && drv_priv) {
-#ifdef CONFIG_IEEE80211BE
-		if (hostapd_mld_is_first_bss(hapd_iface->bss[0]))
-#endif /* CONFIG_IEEE80211BE */
-			driver->hapd_deinit(drv_priv);
 		for (j = 0; j < hapd_iface->num_bss; j++) {
 			wpa_printf(MSG_DEBUG, "%s:bss[%d]->drv_priv=%p",
 				   __func__, (int) j,
 				   hapd_iface->bss[j]->drv_priv);
-			if (hapd_iface->bss[j]->drv_priv == drv_priv) {
-				hostapd_clear_drv_priv(hapd_iface->bss[j]);
-				hapd_iface->extended_capa = NULL;
-				hapd_iface->extended_capa_mask = NULL;
-				hapd_iface->extended_capa_len = 0;
-			}
+
+			if (hapd_iface->bss[j]->drv_priv != drv_priv)
+				continue;
+
+			hapd_iface->bss[j]->drv_priv = NULL;
+			hapd_iface->extended_capa = NULL;
+			hapd_iface->extended_capa_mask = NULL;
+			hapd_iface->extended_capa_len = 0;
 		}
 	}
 }
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 6616397fb5d9..d50cbcdb26d1 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -5154,6 +5154,19 @@  struct wpa_driver_ops {
 	 */
 	int (*if_link_remove)(void *priv, enum wpa_driver_if_type type,
 			      const char *ifname, s8 link_id);
+
+	/**
+	 * get_drv_shared_status - Get shared status of driver interface
+	 * @priv: private driver interface data from init()
+	 * @bss_ctx: BSS context for %WPA_IF_AP_BSS interfaces
+	 *
+	 * Checks whether driver interface is being used by other partner
+	 * BSS(s) or not. This is used to decide whether driver interface
+	 * needs to be deinitilized when one iface is getting deinitialized.
+	 *
+	 * Returns: True if it is being used or else False.
+	 */
+	bool (*get_drv_shared_status)(void *priv, void *bss_ctx);
 #endif /* CONFIG_IEEE80211BE */
 
 #ifdef CONFIG_TESTING_OPTIONS
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index d5f7cc7d041c..a0616ce39192 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -10755,6 +10755,38 @@  static int driver_nl80211_if_link_remove(void *priv, enum wpa_driver_if_type typ
 
 	return wpa_driver_nl80211_if_link_remove(bss, type, ifname, link_id);
 }
+
+static bool wpa_driver_get_shared_status(void *priv, void *bss_ctx)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int num_bss = 0;
+
+	/* If any other bss exist, means someone else is using since at this
+	 * time, we would have removed all bss created by this driver and only
+	 * this bss should be remaining if driver is not shared by anyone
+	 */
+	for (bss = drv->first_bss; bss; bss = bss->next)
+		num_bss++;
+
+	if (num_bss > 1)
+		return true;
+
+	/* This is the only bss present */
+	bss = priv;
+
+	/* If only one/no link is there then no one is sharing */
+	if (bss->valid_links <= 1)
+		return false;
+
+	/* More than one links means some one is still using. To check if
+	 * only 1 bit is set, power of 2 condition can be checked
+	 */
+	if (!(bss->valid_links & (bss->valid_links - 1)))
+		return false;
+
+	return true;
+}
 #endif /* CONFIG_IEEE80211BE */
 
 static int driver_nl80211_send_mlme(void *priv, const u8 *data,
@@ -14087,6 +14119,7 @@  const struct wpa_driver_ops wpa_driver_nl80211_ops = {
 	.link_add = nl80211_link_add,
 #ifdef CONFIG_IEEE80211BE
 	.if_link_remove = driver_nl80211_if_link_remove,
+	.get_drv_shared_status = wpa_driver_get_shared_status,
 #endif /* CONFIG_IEEE80211BE */
 #ifdef CONFIG_TESTING_OPTIONS
 	.register_frame = testing_nl80211_register_frame,