From 08b9f2903b5c8c86fde9d26c9d73c3ca1d40c3d9 Mon Sep 17 00:00:00 2001 From: Nachiket Kukade Date: Fri, 17 Nov 2017 18:27:37 +0530 Subject: [PATCH] qcacld-3.0: Use new event infrastructure for session open/close events With current implementation, if sme_open_session sends down a command to the Firmware and an SSR/PDR occurs, the thread is stuck on waiting on an event. The thread also holds the rtnl lock and will keep blocking any other thread from acquiring it till timeout occurs. This can result in deadlock situation with IPA driver trying to execute driver ops during the SSR/PDR notification callback. Use the wait_for_event_completion API for waiting on event. With this the event will be purged when driver receives FW_DOWN indication. Change-Id: I2920fd36c0eb5bb5994e66e584d12a2a9d8f409a CRs-Fixed: 2120226 --- core/hdd/inc/wlan_hdd_main.h | 8 +-- core/hdd/src/wlan_hdd_cfg80211.c | 5 ++ core/hdd/src/wlan_hdd_hostapd.c | 20 +++++++- core/hdd/src/wlan_hdd_main.c | 83 +++++++++++++++++++++++--------- 4 files changed, 88 insertions(+), 28 deletions(-) diff --git a/core/hdd/inc/wlan_hdd_main.h b/core/hdd/inc/wlan_hdd_main.h index a6c03390eb3a..aae4f989bf63 100644 --- a/core/hdd/inc/wlan_hdd_main.h +++ b/core/hdd/inc/wlan_hdd_main.h @@ -1035,11 +1035,11 @@ struct hdd_adapter { uint8_t session_id; - /* Completion variable for session close */ - struct completion session_close_comp_var; + /* QDF event for session close */ + qdf_event_t qdf_session_close_event; - /* Completion variable for session open */ - struct completion session_open_comp_var; + /* QDF event for session open */ + qdf_event_t qdf_session_open_event; /* TODO: move these to sta ctx. These may not be used in AP */ /** completion variable for disconnect callback */ diff --git a/core/hdd/src/wlan_hdd_cfg80211.c b/core/hdd/src/wlan_hdd_cfg80211.c index 41d61fb90780..6f97149e615b 100644 --- a/core/hdd/src/wlan_hdd_cfg80211.c +++ b/core/hdd/src/wlan_hdd_cfg80211.c @@ -13932,6 +13932,11 @@ static int __wlan_hdd_cfg80211_change_iface(struct wiphy *wiphy, if (0 != status) return status; + if (cds_is_fw_down()) { + hdd_err("Ignore if FW is already down"); + return -EINVAL; + } + MTRACE(qdf_trace(QDF_MODULE_ID_HDD, TRACE_CODE_HDD_CFG80211_CHANGE_IFACE, adapter->session_id, type)); diff --git a/core/hdd/src/wlan_hdd_hostapd.c b/core/hdd/src/wlan_hdd_hostapd.c index 0773e7dea776..99b13e503559 100644 --- a/core/hdd/src/wlan_hdd_hostapd.c +++ b/core/hdd/src/wlan_hdd_hostapd.c @@ -6298,6 +6298,7 @@ struct hdd_adapter *hdd_wlan_create_ap_dev(struct hdd_context *hdd_ctx, { struct net_device *dev; struct hdd_adapter *adapter; + QDF_STATUS qdf_status; hdd_debug("iface_name = %s", iface_name); @@ -6350,8 +6351,23 @@ struct hdd_adapter *hdd_wlan_create_ap_dev(struct hdd_context *hdd_ctx, adapter->wdev.wiphy = hdd_ctx->wiphy; adapter->wdev.netdev = dev; hdd_set_tso_flags(hdd_ctx, dev); - init_completion(&adapter->session_close_comp_var); - init_completion(&adapter->session_open_comp_var); + + qdf_status = qdf_event_create( + &adapter->qdf_session_open_event); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("failed to create session open QDF event!"); + free_netdev(adapter->dev); + return NULL; + } + + qdf_status = qdf_event_create( + &adapter->qdf_session_close_event); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("failed to create session close QDF event!"); + free_netdev(adapter->dev); + return NULL; + } + init_completion(&adapter->tx_action_cnf_event); init_completion(&adapter->cancel_rem_on_chan_var); init_completion(&adapter->rem_on_chan_ready_event); diff --git a/core/hdd/src/wlan_hdd_main.c b/core/hdd/src/wlan_hdd_main.c index 43e74477f16d..b8ee8b665241 100644 --- a/core/hdd/src/wlan_hdd_main.c +++ b/core/hdd/src/wlan_hdd_main.c @@ -3145,6 +3145,7 @@ static struct hdd_adapter *hdd_alloc_station_adapter(struct hdd_context *hdd_ctx struct net_device *dev = NULL; struct hdd_adapter *adapter = NULL; struct hdd_station_ctx *sta_ctx; + QDF_STATUS qdf_status; /* * cfg80211 initialization and registration.... */ @@ -3171,8 +3172,22 @@ static struct hdd_adapter *hdd_alloc_station_adapter(struct hdd_context *hdd_ctx adapter->magic = WLAN_HDD_ADAPTER_MAGIC; adapter->session_id = HDD_SESSION_ID_INVALID; - init_completion(&adapter->session_open_comp_var); - init_completion(&adapter->session_close_comp_var); + qdf_status = qdf_event_create( + &adapter->qdf_session_open_event); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("Session open QDF event init failed!"); + free_netdev(adapter->dev); + return NULL; + } + + qdf_status = qdf_event_create( + &adapter->qdf_session_close_event); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("Session close QDF event init failed!"); + free_netdev(adapter->dev); + return NULL; + } + init_completion(&adapter->disconnect_comp_var); init_completion(&adapter->roaming_comp_var); init_completion(&adapter->linkup_event_var); @@ -3278,7 +3293,7 @@ QDF_STATUS hdd_sme_open_session_callback(uint8_t session_id) return QDF_STATUS_E_INVAL; } set_bit(SME_SESSION_OPENED, &adapter->event_flags); - complete(&adapter->session_open_comp_var); + qdf_event_set(&adapter->qdf_session_open_event); hdd_debug("session %d opened", adapter->session_id); return QDF_STATUS_SUCCESS; @@ -3323,7 +3338,7 @@ QDF_STATUS hdd_sme_close_session_callback(uint8_t session_id) * valid, before signaling completion */ if (WLAN_HDD_ADAPTER_MAGIC == adapter->magic) - complete(&adapter->session_close_comp_var); + qdf_event_set(&adapter->qdf_session_close_event); return QDF_STATUS_SUCCESS; } @@ -3353,7 +3368,6 @@ int hdd_vdev_destroy(struct hdd_adapter *adapter) QDF_STATUS status; int errno; struct hdd_context *hdd_ctx; - unsigned long rc; uint8_t vdev_id; vdev_id = adapter->session_id; @@ -3377,7 +3391,7 @@ int hdd_vdev_destroy(struct hdd_adapter *adapter) } /* close sme session (destroy vdev in firmware via legacy API) */ - INIT_COMPLETION(adapter->session_close_comp_var); + qdf_event_reset(&adapter->qdf_session_close_event); hdd_ctx = WLAN_HDD_GET_CTX(adapter); status = sme_close_session(hdd_ctx->hHal, adapter->session_id); if (QDF_IS_STATUS_ERROR(status)) { @@ -3386,15 +3400,24 @@ int hdd_vdev_destroy(struct hdd_adapter *adapter) } /* block on a completion variable until sme session is closed */ - rc = wait_for_completion_timeout( - &adapter->session_close_comp_var, - msecs_to_jiffies(WLAN_WAIT_TIME_SESSIONOPENCLOSE)); - if (!rc) { - hdd_err("timed out waiting for close sme session: %ld", rc); + status = qdf_wait_for_event_completion( + &adapter->qdf_session_close_event, + WLAN_WAIT_TIME_SESSIONOPENCLOSE); + if (QDF_STATUS_SUCCESS != status) { if (adapter->device_mode == QDF_NDI_MODE) hdd_ndp_session_end_handler(adapter); clear_bit(SME_SESSION_OPENED, &adapter->event_flags); - return -ETIMEDOUT; + adapter->session_id = HDD_SESSION_ID_INVALID; + if (QDF_STATUS_E_TIMEOUT != status) { + hdd_err("timed out waiting for close sme session: %u", status); + return -ETIMEDOUT; + } else if (adapter->qdf_session_close_event.force_set) { + hdd_err("Session close evt focefully set, SSR/PDR has occurred"); + return -EINVAL; + } else { + hdd_err("Failed to close sme session (%u)", status); + return -EINVAL; + } } release_vdev: /* do vdev logical destroy via objmgr */ @@ -3449,7 +3472,6 @@ int hdd_vdev_create(struct hdd_adapter *adapter, int errno; struct hdd_context *hdd_ctx; struct sme_session_params sme_session_params = {0}; - unsigned long rc; hdd_info("creating new vdev"); @@ -3462,7 +3484,11 @@ int hdd_vdev_create(struct hdd_adapter *adapter, } /* Open a SME session (prepare vdev in firmware via legacy API) */ - INIT_COMPLETION(adapter->session_open_comp_var); + status = qdf_event_reset(&adapter->qdf_session_open_event); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("failed to reinit session open event"); + return -EINVAL; + } errno = hdd_set_sme_session_param(adapter, &sme_session_params, callback, ctx); if (errno) { @@ -3478,14 +3504,27 @@ int hdd_vdev_create(struct hdd_adapter *adapter, } /* block on a completion variable until sme session is opened */ - rc = wait_for_completion_timeout( - &adapter->session_open_comp_var, - msecs_to_jiffies(WLAN_WAIT_TIME_SESSIONOPENCLOSE)); - if (!rc) { - hdd_err("timed out waiting for open sme session: %ld", rc); - errno = -ETIMEDOUT; - set_bit(SME_SESSION_OPENED, &adapter->event_flags); - goto hdd_vdev_destroy_procedure; + status = qdf_wait_for_event_completion(&adapter->qdf_session_open_event, + WLAN_WAIT_TIME_SESSIONOPENCLOSE); + if (QDF_STATUS_SUCCESS != status) { + if (adapter->qdf_session_open_event.force_set) { + /* + * SSR/PDR has caused shutdown, which has forcefully + * set the event. Return without the closing session. + */ + adapter->session_id = HDD_SESSION_ID_INVALID; + hdd_err("Session open event forcefully set"); + return -EINVAL; + } else { + if (QDF_STATUS_E_TIMEOUT == status) + hdd_err("Session failed to open within timeout period"); + else + hdd_err("Failed to wait for session open event(status-%d)", + status); + errno = -ETIMEDOUT; + set_bit(SME_SESSION_OPENED, &adapter->event_flags); + goto hdd_vdev_destroy_procedure; + } } /* firmware ready for component communication, raise vdev_ready event */