Sfoglia il codice sorgente

qcacld-3.0: Hold mutex lock in WQ while channel switch notify

From the kernel, it is expected to hold the mutex lock while
channel switch notify.
Below are the cases:

1. As of now, in case of SAP, driver did not acquire the lock
hence kernel assert happened during channel switch notify.

2. In case of STA CSA, driver tried to acquire the mutex lock
and before that NB disconnect come and  nl80211_disconnect is
holding dev->ieee80211_ptr->mtx during disconnect path and
waiting for disconnect completion, at the same time from
scheduler thread callback driver can’t acquire the same mutex
again since its already held by nl80211_disconnect.

The NB operation is dependent on scheduler to complete the
operations which is resulting into deadlock, as both are
waiting for each other to get completed (scheduler waiting
on the lock, while NB waiting on scheduler).

To fix it, as work queue is independent thread (No NB
operation depending on it), driver will not have this
deadlock and NB operation can complete its operation via
scheduler thread and release the lock, which then will
be acquired by WQ, which was waiting on the lock, to
continue its operation.

CRs-Fixed: 3618311
Change-Id: I7606ca51801ccca0aa464ec98dc9428af2ac5dd8
Deeksha Gupta 1 anno fa
parent
commit
72547547c7

+ 11 - 10
core/hdd/inc/wlan_hdd_main.h

@@ -888,6 +888,7 @@ struct hdd_fw_txrx_stats {
  * @client_count: client count per dot11_mode
  * @country_ie_updated: country ie is updated or not by hdd hostapd
  * @during_auth_offload: auth mgmt frame is offloading to hostapd
+ * @reg_punc_bitmap: puncturing bitmap
  */
 struct hdd_ap_ctx {
 	struct hdd_hostapd_state hostapd_state;
@@ -910,6 +911,9 @@ struct hdd_ap_ctx {
 	uint16_t client_count[QCA_WLAN_802_11_MODE_INVALID];
 	bool country_ie_updated;
 	bool during_auth_offload;
+#ifdef WLAN_FEATURE_11BE
+	uint16_t reg_punc_bitmap;
+#endif
 };
 
 /**
@@ -1108,6 +1112,7 @@ enum udp_qos_upgrade {
  * @mscs_prev_tx_vo_pkts: count of prev VO AC packets transmitted
  * @mscs_counter: Counter on MSCS action frames sent
  * @link_flags: a bitmap of hdd_link_flags
+ * @chan_change_notify_work: Channel change notify work
  */
 struct wlan_hdd_link_info {
 	struct hdd_adapter *adapter;
@@ -1148,6 +1153,7 @@ struct wlan_hdd_link_info {
 #endif /* WLAN_FEATURE_MSCS */
 
 	unsigned long link_flags;
+	qdf_work_t chan_change_notify_work;
 };
 
 /**
@@ -3834,21 +3840,16 @@ int hdd_update_config(struct hdd_context *hdd_ctx);
 int hdd_update_components_config(struct hdd_context *hdd_ctx);
 
 /**
- * hdd_chan_change_notify() - Function to notify hostapd about channel change
- * @link_info:          Link info pointer in HDD adapter
- * @dev:		Net device structure
- * @chan_change:	New channel change parameters
- * @legacy_phymode:	is the phymode legacy
+ * hdd_chan_change_notify_work_handler() - Function to notify hostapd about
+ * channel change
+ * @work: work pointer
  *
  * This function is used to notify hostapd about the channel change
  *
- * Return: Success on intimating userspace
+ * Return: None
  *
  */
-QDF_STATUS hdd_chan_change_notify(struct wlan_hdd_link_info *link_info,
-				  struct net_device *dev,
-				  struct hdd_chan_change_params chan_change,
-				  bool legacy_phymode);
+void hdd_chan_change_notify_work_handler(void *work);
 
 int wlan_hdd_set_channel(struct wiphy *wiphy,
 		struct net_device *dev,

+ 1 - 6
core/hdd/src/wlan_hdd_assoc.c

@@ -2352,12 +2352,7 @@ hdd_roam_channel_switch_handler(struct wlan_hdd_link_info *link_info,
 			notify = false;
 	}
 	if (notify) {
-		status = hdd_chan_change_notify(link_info,
-						adapter->dev, chan_change,
-						roam_info->mode ==
-						SIR_SME_PHY_MODE_LEGACY);
-		if (QDF_IS_STATUS_ERROR(status))
-			hdd_err("channel change notification failed");
+		qdf_sched_work(0, &link_info->chan_change_notify_work);
 	} else {
 		hdd_err("BSS "QDF_MAC_ADDR_FMT" no connected with vdev %d (%d)",
 			QDF_MAC_ADDR_REF(sta_ctx->conn_info.bssid.bytes),

+ 146 - 104
core/hdd/src/wlan_hdd_hostapd.c

@@ -907,14 +907,14 @@ static int hdd_stop_bss_link(struct hdd_adapter *adapter)
 #if defined(WLAN_FEATURE_11BE) && defined(CFG80211_11BE_BASIC)
 static void
 wlan_hdd_set_chandef_320mhz(struct cfg80211_chan_def *chandef,
-			    struct hdd_chan_change_params chan_change)
+			    struct wlan_channel *chan)
 {
-	if (chan_change.chan_params.ch_width != CH_WIDTH_320MHZ)
+	if (chan->ch_width != CH_WIDTH_320MHZ)
 		return;
 
 	chandef->width = NL80211_CHAN_WIDTH_320;
-	if (chan_change.chan_params.mhz_freq_seg1)
-		chandef->center_freq1 = chan_change.chan_params.mhz_freq_seg1;
+	if (chan->ch_cfreq2)
+		chandef->center_freq1 = chan->ch_cfreq2;
 }
 
 static void wlan_hdd_set_chandef_width(struct cfg80211_chan_def *chandef,
@@ -930,14 +930,21 @@ static inline bool wlan_hdd_is_chwidth_320mhz(enum phy_ch_width ch_width)
 }
 
 static uint16_t
-wlan_hdd_get_puncture_bitmap(struct hdd_chan_change_params chan_change)
+wlan_hdd_get_puncture_bitmap(struct wlan_hdd_link_info *link_info)
 {
-	return chan_change.chan_params.reg_punc_bitmap;
+	struct hdd_adapter *adapter = link_info->adapter;
+	struct hdd_ap_ctx *ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(link_info);
+
+	if (adapter->device_mode == QDF_SAP_MODE ||
+	    adapter->device_mode == QDF_P2P_CLIENT_MODE)
+		return ap_ctx->reg_punc_bitmap;
+
+	return 0;
 }
 #else /* !WLAN_FEATURE_11BE */
 static inline
 void wlan_hdd_set_chandef_320mhz(struct cfg80211_chan_def *chandef,
-				 struct hdd_chan_change_params chan_change)
+				 struct wlan_channel *chan)
 {
 }
 
@@ -952,134 +959,169 @@ static inline bool wlan_hdd_is_chwidth_320mhz(enum phy_ch_width ch_width)
 }
 
 static inline uint16_t
-wlan_hdd_get_puncture_bitmap(struct hdd_chan_change_params chan_change)
+wlan_hdd_get_puncture_bitmap(struct wlan_hdd_link_info *link_info)
 {
 	return 0;
 }
 #endif /* WLAN_FEATURE_11BE */
 
-QDF_STATUS hdd_chan_change_notify(struct wlan_hdd_link_info *link_info,
-				  struct net_device *dev,
-				  struct hdd_chan_change_params chan_change,
-				  bool legacy_phymode)
+static QDF_STATUS hdd_create_chandef(struct hdd_adapter *adapter,
+				     struct wlan_channel *wlan_chan,
+				     struct cfg80211_chan_def *chandef)
 {
 	struct ieee80211_channel *chan;
-	struct cfg80211_chan_def chandef;
 	enum nl80211_channel_type channel_type;
 	uint32_t freq;
-	struct hdd_adapter *adapter = link_info->adapter;
-	mac_handle_t mac_handle = adapter->hdd_ctx->mac_handle;
-	struct wlan_objmgr_vdev *vdev;
-	uint16_t link_id = 0;
-	uint16_t puncture_bitmap = 0;
-	struct hdd_adapter *assoc_adapter;
+	bool legacy_phymode = true;
 
-	if (!mac_handle) {
-		hdd_err("mac_handle is NULL");
-		return QDF_STATUS_E_FAILURE;
-	}
-
-	freq = chan_change.chan_freq;
+	freq = wlan_chan->ch_freq;
 	chan = ieee80211_get_channel(adapter->wdev.wiphy, freq);
-
 	if (!chan) {
 		hdd_err("Invalid input frequency %d for channel conversion",
 			freq);
 		return QDF_STATUS_E_FAILURE;
 	}
 
+	if (IS_WLAN_PHYMODE_HT(wlan_chan->ch_phymode) ||
+	    IS_WLAN_PHYMODE_VHT(wlan_chan->ch_phymode) ||
+	    IS_WLAN_PHYMODE_HE(wlan_chan->ch_phymode) ||
+	    IS_WLAN_PHYMODE_EHT(wlan_chan->ch_phymode))
+		legacy_phymode = false;
+
 	if (legacy_phymode) {
 		channel_type = NL80211_CHAN_NO_HT;
 	} else {
-		switch (chan_change.chan_params.sec_ch_offset) {
-		case PHY_SINGLE_CHANNEL_CENTERED:
+		if (!wlan_chan->ch_cfreq1 ||
+		    wlan_chan->ch_cfreq1 == wlan_chan->ch_freq)
 			channel_type = NL80211_CHAN_HT20;
-			break;
-		case PHY_DOUBLE_CHANNEL_HIGH_PRIMARY:
-			channel_type = NL80211_CHAN_HT40MINUS;
-			break;
-		case PHY_DOUBLE_CHANNEL_LOW_PRIMARY:
+		else if (wlan_chan->ch_cfreq1 > wlan_chan->ch_freq)
 			channel_type = NL80211_CHAN_HT40PLUS;
-			break;
-		default:
-			channel_type = NL80211_CHAN_NO_HT;
-			break;
-		}
+		else
+			channel_type = NL80211_CHAN_HT40MINUS;
 	}
 
-	cfg80211_chandef_create(&chandef, chan, channel_type);
+	cfg80211_chandef_create(chandef, chan, channel_type);
 
 	/* cfg80211_chandef_create() does update of width and center_freq1
 	 * only for NL80211_CHAN_NO_HT, NL80211_CHAN_HT20, NL80211_CHAN_HT40PLUS
 	 * and NL80211_CHAN_HT40MINUS.
 	 */
-	switch (chan_change.chan_params.ch_width) {
+	switch (wlan_chan->ch_width) {
 	case CH_WIDTH_80MHZ:
-		chandef.width = NL80211_CHAN_WIDTH_80;
+		chandef->width = NL80211_CHAN_WIDTH_80;
 		break;
 	case CH_WIDTH_80P80MHZ:
-		chandef.width = NL80211_CHAN_WIDTH_80P80;
-		if (chan_change.chan_params.mhz_freq_seg1)
-			chandef.center_freq2 =
-				chan_change.chan_params.mhz_freq_seg1;
+		chandef->width = NL80211_CHAN_WIDTH_80P80;
+		if (wlan_chan->ch_cfreq2)
+			chandef->center_freq2 = wlan_chan->ch_cfreq2;
 		break;
 	case CH_WIDTH_160MHZ:
-		chandef.width = NL80211_CHAN_WIDTH_160;
-		if (chan_change.chan_params.mhz_freq_seg1)
-			chandef.center_freq1 =
-				chan_change.chan_params.mhz_freq_seg1;
+		chandef->width = NL80211_CHAN_WIDTH_160;
+		if (wlan_chan->ch_cfreq2)
+			chandef->center_freq1 = wlan_chan->ch_cfreq2;
 		break;
 	default:
 		break;
 	}
 
-	wlan_hdd_set_chandef_320mhz(&chandef, chan_change);
+	wlan_hdd_set_chandef_320mhz(chandef, wlan_chan);
 
-	if ((chan_change.chan_params.ch_width == CH_WIDTH_80MHZ) ||
-	    (chan_change.chan_params.ch_width == CH_WIDTH_80P80MHZ)) {
-		if (chan_change.chan_params.mhz_freq_seg0)
-			chandef.center_freq1 =
-				chan_change.chan_params.mhz_freq_seg0;
+	if (wlan_chan->ch_width == CH_WIDTH_80MHZ ||
+	    wlan_chan->ch_width == CH_WIDTH_80P80MHZ) {
+		if (wlan_chan->ch_cfreq1)
+			chandef->center_freq1 = wlan_chan->ch_cfreq1;
 	}
 
-	vdev = hdd_objmgr_get_vdev_by_user(link_info, WLAN_OSIF_ID);
-	if (!vdev)
-		return -EINVAL;
+	return QDF_STATUS_SUCCESS;
+}
 
-	if (wlan_vdev_mlme_is_mlo_vdev(vdev))
-		link_id = wlan_vdev_get_link_id(vdev);
-	hdd_debug("link_id is %d", link_id);
+static void hdd_chan_change_notify_update(struct wlan_hdd_link_info *link_info)
+{
+	struct hdd_adapter *adapter = link_info->adapter;
+	mac_handle_t mac_handle = adapter->hdd_ctx->mac_handle;
+	struct wlan_objmgr_vdev *vdev;
+	uint16_t link_id = 0;
+	struct hdd_adapter *assoc_adapter;
+	struct wlan_channel *chan;
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+	struct net_device *dev;
+	struct cfg80211_chan_def chandef;
+	uint16_t puncture_bitmap = 0;
+	uint8_t vdev_id;
 
-	puncture_bitmap = wlan_hdd_get_puncture_bitmap(chan_change);
+	if (!mac_handle) {
+		hdd_err("mac_handle is NULL");
+		return;
+	}
 
-	hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_ID);
-	hdd_debug("notify: chan:%d width:%d freq1:%d freq2:%d punct 0x%x",
-		  chandef.chan->center_freq, chandef.width,
-		  chandef.center_freq1, chandef.center_freq2,
-		  puncture_bitmap);
+	vdev = hdd_objmgr_get_vdev_by_user(link_info, WLAN_OSIF_ID);
+	if (!vdev)
+		return;
 
+	dev = adapter->dev;
+	vdev_id = wlan_vdev_get_id(vdev);
 	if (hdd_adapter_is_link_adapter(adapter)) {
 		hdd_debug("replace link adapter dev with ml adapter dev");
 		assoc_adapter = hdd_adapter_get_mlo_adapter_from_link(adapter);
 		if (!assoc_adapter) {
 			hdd_err("Assoc adapter is NULL");
-			return -EINVAL;
+			hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_ID);
+			return;
 		}
 		dev = assoc_adapter->dev;
 	}
 
-	if (adapter->device_mode == QDF_STA_MODE ||
-	    adapter->device_mode == QDF_P2P_CLIENT_MODE)
-		mutex_lock(&dev->ieee80211_ptr->mtx);
+	mutex_lock(&dev->ieee80211_ptr->mtx);
+	if (wlan_vdev_mlme_is_active(vdev) != QDF_STATUS_SUCCESS) {
+		hdd_debug("Vdev %d mode %d not UP", vdev_id,
+			  adapter->device_mode);
+		goto exit;
+	}
 
-	wlan_cfg80211_ch_switch_notify(dev, &chandef, link_id,
-				       puncture_bitmap);
+	if ((adapter->device_mode == QDF_STA_MODE ||
+	     adapter->device_mode == QDF_P2P_CLIENT_MODE) &&
+	    !ucfg_cm_is_vdev_active(vdev)) {
+		hdd_debug("Vdev %d is not connected", vdev_id);
+		goto exit;
+	}
 
-	if (adapter->device_mode == QDF_STA_MODE ||
-	    adapter->device_mode == QDF_P2P_CLIENT_MODE)
-		mutex_unlock(&dev->ieee80211_ptr->mtx);
+	if (wlan_vdev_mlme_is_mlo_vdev(vdev))
+		link_id = wlan_vdev_get_link_id(vdev);
 
-	return QDF_STATUS_SUCCESS;
+	chan = wlan_vdev_get_active_channel(vdev);
+
+	if (!chan)
+		goto exit;
+
+	status = hdd_create_chandef(adapter, chan, &chandef);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		hdd_debug("Vdev %d failed to create channel def", vdev_id);
+		goto exit;
+	}
+
+	puncture_bitmap = wlan_hdd_get_puncture_bitmap(link_info);
+
+	hdd_debug("notify: vdev %d chan:%d width:%d freq1:%d freq2:%d punct 0x%x",
+		  vdev_id, chandef.chan->center_freq, chandef.width,
+		  chandef.center_freq1, chandef.center_freq2,
+		  puncture_bitmap);
+
+	wlan_cfg80211_ch_switch_notify(dev, &chandef, link_id, puncture_bitmap);
+
+exit:
+	mutex_unlock(&dev->ieee80211_ptr->mtx);
+	hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_ID);
+}
+
+void hdd_chan_change_notify_work_handler(void *data)
+{
+	struct wlan_hdd_link_info *link_info =
+			(struct wlan_hdd_link_info *)data;
+
+	if (!link_info)
+		return;
+
+	hdd_chan_change_notify_update(link_info);
 }
 
 /**
@@ -1742,15 +1784,15 @@ static void hdd_hostapd_set_sap_key(struct hdd_adapter *adapter)
 
 #ifdef WLAN_FEATURE_11BE
 static void
-hdd_fill_channel_change_puncture(struct hdd_chan_change_params *chan_change,
+hdd_fill_channel_change_puncture(struct hdd_ap_ctx *ap_ctx,
 				 struct ch_params *sap_ch_param)
 {
-	chan_change->chan_params.reg_punc_bitmap =
+	ap_ctx->reg_punc_bitmap =
 			sap_ch_param->reg_punc_bitmap;
 }
 #else
 static void
-hdd_fill_channel_change_puncture(struct hdd_chan_change_params *chan_change,
+hdd_fill_channel_change_puncture(struct hdd_ap_ctx *ap_ctx,
 				 struct ch_params *sap_ch_param)
 {
 }
@@ -1766,7 +1808,6 @@ hdd_fill_channel_change_puncture(struct hdd_chan_change_params *chan_change,
 static QDF_STATUS hdd_hostapd_chan_change(struct wlan_hdd_link_info *link_info,
 					  struct sap_event *sap_event)
 {
-	struct hdd_chan_change_params chan_change = {0};
 	struct ch_params sap_ch_param = {0};
 	eCsrPhyMode phy_mode;
 	bool legacy_phymode;
@@ -1775,6 +1816,7 @@ static QDF_STATUS hdd_hostapd_chan_change(struct wlan_hdd_link_info *link_info,
 	struct sap_ch_selected_s *sap_chan_selected;
 	struct sap_config *sap_config =
 				&link_info->session.ap.sap_config;
+	struct hdd_ap_ctx *ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(link_info);
 
 	if (sap_event->sapHddEventCode == eSAP_CHANNEL_CHANGE_RESP)
 		sap_chan_selected =
@@ -1815,18 +1857,10 @@ static QDF_STATUS hdd_hostapd_chan_change(struct wlan_hdd_link_info *link_info,
 		break;
 	}
 
-	chan_change.chan_freq = sap_chan_selected->pri_ch_freq;
-	chan_change.chan_params.ch_width = sap_chan_selected->ch_width;
-	chan_change.chan_params.sec_ch_offset =
-		sap_ch_param.sec_ch_offset;
-	chan_change.chan_params.mhz_freq_seg0 =
-			sap_chan_selected->vht_seg0_center_ch_freq;
-	chan_change.chan_params.mhz_freq_seg1 =
-			sap_chan_selected->vht_seg1_center_ch_freq;
-	hdd_fill_channel_change_puncture(&chan_change, &sap_ch_param);
+	hdd_fill_channel_change_puncture(ap_ctx, &sap_ch_param);
+	qdf_sched_work(0, &link_info->chan_change_notify_work);
 
-	return hdd_chan_change_notify(link_info, adapter->dev,
-				      chan_change, legacy_phymode);
+	return QDF_STATUS_SUCCESS;
 }
 
 static inline void
@@ -7652,13 +7686,17 @@ void wlan_hdd_configure_twt_responder(struct hdd_context *hdd_ctx,
 {}
 #endif
 
-#ifdef CFG80211_SINGLE_NETDEV_MULTI_LINK_SUPPORT
 static inline uint32_t
-wlan_util_get_centre_freq(struct wireless_dev *wdev, unsigned int link_id)
+wlan_util_get_centre_freq(struct wlan_hdd_link_info *link_info)
 {
-	return wdev->links[link_id].ap.chandef.chan->center_freq;
+	struct wlan_channel *chan;
+
+	chan = wlan_vdev_get_active_channel(link_info->vdev);
+
+	return chan->ch_freq;
 }
 
+#ifdef CFG80211_SINGLE_NETDEV_MULTI_LINK_SUPPORT
 static inline struct cfg80211_chan_def
 wlan_util_get_chan_def(struct wireless_dev *wdev, unsigned int link_id)
 {
@@ -7670,12 +7708,6 @@ wlan_util_get_chan_def(struct wireless_dev *wdev, unsigned int link_id)
 {
 	return wdev->chandef;
 }
-
-static inline uint32_t
-wlan_util_get_centre_freq(struct wireless_dev *wdev, unsigned int link_id)
-{
-	return wdev->chandef.chan->center_freq;
-}
 #endif
 
 #if defined(WLAN_FEATURE_SR) && \
@@ -7726,6 +7758,16 @@ static inline void hdd_update_he_obss_pd(struct wlan_hdd_link_info *link_info,
 }
 #endif
 
+static void hdd_update_param_chandef(struct wlan_hdd_link_info *link_info,
+				     struct cfg80211_chan_def *chandef)
+{
+	struct wlan_channel *chan;
+
+	chan = wlan_vdev_get_active_channel(link_info->vdev);
+
+	hdd_create_chandef(link_info->adapter, chan, chandef);
+}
+
 /**
  * __wlan_hdd_cfg80211_start_ap() - start soft ap mode
  * @wiphy: Pointer to wiphy structure
@@ -7747,7 +7789,6 @@ static int __wlan_hdd_cfg80211_start_ap(struct wiphy *wiphy,
 	qdf_freq_t freq;
 	uint16_t sta_cnt, sap_cnt;
 	bool val;
-	struct wireless_dev *wdev = dev->ieee80211_ptr;
 	struct cfg80211_chan_def new_chandef;
 	struct cfg80211_chan_def *chandef;
 	bool srd_channel_allowed, disable_nan = true;
@@ -8075,9 +8116,10 @@ static int __wlan_hdd_cfg80211_start_ap(struct wiphy *wiphy,
 
 		hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_ID);
 
-		if (wlan_util_get_centre_freq(wdev, link_id) !=
+		if (wlan_util_get_centre_freq(link_info) !=
 				params->chandef.chan->center_freq)
-			params->chandef = wlan_util_get_chan_def(wdev, link_id);
+			hdd_update_param_chandef(link_info, &params->chandef);
+
 		/*
 		 * If Do_Not_Break_Stream enabled send avoid channel list
 		 * to application.

+ 7 - 0
core/hdd/src/wlan_hdd_main.c

@@ -8555,6 +8555,9 @@ static void hdd_adapter_init_link_info(struct hdd_adapter *adapter)
 
 		idx_pos = hdd_adapter_get_index_of_link_info(link_info);
 		adapter->curr_link_info_map[idx_pos] = idx_pos;
+		qdf_create_work(0, &link_info->chan_change_notify_work,
+				hdd_chan_change_notify_work_handler,
+				link_info);
 	}
 }
 
@@ -8866,6 +8869,10 @@ static void __hdd_close_adapter(struct hdd_context *hdd_ctx,
 		hdd_adapter_for_each_link_info(adapter, link_info)
 			hdd_cleanup_conn_info(link_info);
 	}
+
+	hdd_adapter_for_each_link_info(adapter, link_info)
+		qdf_flush_work(&link_info->chan_change_notify_work);
+
 	qdf_list_destroy(&adapter->blocked_scan_request_q);
 	qdf_mutex_destroy(&adapter->blocked_scan_request_q_lock);
 	policy_mgr_clear_concurrency_mode(hdd_ctx->psoc, adapter->device_mode);