Quellcode durchsuchen

qcacld-3.0: Fix deadlock between rtnl lock and vdev ops DSC

The below described scenario can lead to a deadlock
between wlan_hdd_del_virtual_intf and the work handler
to update netdev features.
- netdev features update work is scheduled and takes the
  vdev ops DSC lock.
- before the work is able to take the rtnl lock, a delete
  interface is called from kernel with rtnl lock held
- wlan_hdd_del_virtual_intf waits indefinitely for all the
  ops to be completed on this vdev.
- Now netdev feature update work is waiting for rtnl lock
  with vdev ops DSC held, and wlan_hdd_del_virtual_intf is
  waiting for vdev ops DSC with rtnl lock held.

In order to fix the above deadlock scenario, set a delete in
progress flag in wlan_hdd_del_virtual_intf and use rtnl_trylock
in the netdev features update work, only if the delete in
progress flag is not set. Using rtnl_trylock with a few equally
time spaced attempts to acquire rtnl lock will make sure the
features update is done it the work is unable to acquire the
rtnl lock due to any other netdev operation (apart from interface
deletion).

Change-Id: Iad6448d4d9bf7f4cbfcfc4c6fb3afb248afaeeec
CRs-Fixed: 2854713
Rakesh Pillai vor 4 Jahren
Ursprung
Commit
e8ab26341c
3 geänderte Dateien mit 39 neuen und 7 gelöschten Zeilen
  1. 5 1
      core/hdd/inc/wlan_hdd_main.h
  2. 29 5
      core/hdd/src/wlan_hdd_main.c
  3. 5 1
      core/hdd/src/wlan_hdd_p2p.c

+ 5 - 1
core/hdd/inc/wlan_hdd_main.h

@@ -1216,7 +1216,10 @@ struct hdd_context;
  * @handle_feature_update: Handle feature update only if it is triggered
  *			   by hdd_netdev_feature_update
  * @netdev_features_update_work: work for handling the netdev features update
-				 for the adapter.
+ *				 for the adapter.
+ * @delete_in_progress: Flag to indicate that the adapter delete is in
+ *			progress, and any operation using rtnl lock inside
+ *			the driver can be avoided/skipped.
  */
 struct hdd_adapter {
 	/* Magic cookie for adapter sanity verification.  Note that this
@@ -1527,6 +1530,7 @@ struct hdd_adapter {
 	uint8_t net_dev_hold_ref_count[NET_DEV_HOLD_ID_MAX];
 	/* Flag to indicate whether it is a pre cac adapter or not */
 	bool is_pre_cac_adapter;
+	bool delete_in_progress;
 };
 
 #define WLAN_HDD_GET_STATION_CTX_PTR(adapter) (&(adapter)->session.station)

+ 29 - 5
core/hdd/src/wlan_hdd_main.c

@@ -5081,11 +5081,15 @@ static int hdd_set_features(struct net_device *net_dev,
 	return errno;
 }
 
+#define HDD_NETDEV_FEATURES_UPDATE_MAX_WAIT_COUNT	10
+#define HDD_NETDEV_FEATURES_UPDATE_WAIT_INTERVAL_MS	20
+
 void hdd_netdev_update_features(struct hdd_adapter *adapter)
 {
 	struct net_device *net_dev = adapter->dev;
 	ol_txrx_soc_handle soc = cds_get_context(QDF_MODULE_ID_SOC);
 	bool request_feature_update = false;
+	int wait_count = HDD_NETDEV_FEATURES_UPDATE_MAX_WAIT_COUNT;
 
 	if (!soc)
 		return;
@@ -5105,11 +5109,31 @@ void hdd_netdev_update_features(struct hdd_adapter *adapter)
 	if (request_feature_update) {
 		hdd_debug("Update net_dev features for device mode %d",
 			  adapter->device_mode);
-		rtnl_lock();
-		adapter->handle_feature_update = true;
-		netdev_update_features(net_dev);
-		adapter->handle_feature_update = false;
-		rtnl_unlock();
+		while (!adapter->delete_in_progress) {
+			if (rtnl_trylock()) {
+				adapter->handle_feature_update = true;
+				netdev_update_features(net_dev);
+				adapter->handle_feature_update = false;
+				rtnl_unlock();
+				break;
+			}
+
+			if (wait_count--) {
+				qdf_sleep(
+				HDD_NETDEV_FEATURES_UPDATE_WAIT_INTERVAL_MS);
+			} else {
+				/*
+				 * We have failed to updated the netdev
+				 * features for very long, so enable the queues
+				 * now. The impact of not being able to update
+				 * the netdev feature is lower TPUT when
+				 * switching from legacy to non-legacy mode.
+				 */
+				hdd_err("Failed to update netdev features for device mode %d",
+					adapter->device_mode);
+				break;
+			}
+		}
 	}
 }
 

+ 5 - 1
core/hdd/src/wlan_hdd_p2p.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved.
  *
  * Permission to use, copy, modify, and/or distribute this software for
  * any purpose with or without fee is hereby granted, provided that the
@@ -810,6 +810,8 @@ struct wireless_dev *__wlan_hdd_add_virtual_intf(struct wiphy *wiphy,
 		return ERR_PTR(-ENOSPC);
 	}
 
+	adapter->delete_in_progress = false;
+
 	/* ensure physcial soc is up */
 	ret = hdd_trigger_psoc_idle_restart(hdd_ctx);
 	if (ret) {
@@ -968,7 +970,9 @@ int wlan_hdd_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
 {
 	int errno;
 	struct osif_vdev_sync *vdev_sync;
+	struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(wdev->netdev);
 
+	adapter->delete_in_progress = true;
 	errno = osif_vdev_sync_trans_start_wait(wdev->netdev, &vdev_sync);
 	if (errno)
 		return errno;