mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-14 08:36:12 -04:00
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
This commit is contained in:
parent
907c73bef1
commit
56e622954d
3 changed files with 39 additions and 7 deletions
|
|
@ -1131,7 +1131,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
|
||||
|
|
@ -1441,6 +1444,7 @@ struct hdd_adapter {
|
|||
qdf_work_t netdev_features_update_work;
|
||||
/* 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)
|
||||
|
|
|
|||
|
|
@ -5001,11 +5001,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) {
|
||||
hdd_err("soc handle is NULL");
|
||||
|
|
@ -5027,11 +5031,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue