diff --git a/components/dsc/inc/wlan_dsc_vdev.h b/components/dsc/inc/wlan_dsc_vdev.h index 846c3c45ce..7701dc3f21 100644 --- a/components/dsc/inc/wlan_dsc_vdev.h +++ b/components/dsc/inc/wlan_dsc_vdev.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2018-2019, 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 @@ -143,4 +143,27 @@ void _dsc_vdev_op_stop(struct dsc_vdev *vdev, const char *func); */ void dsc_vdev_wait_for_ops(struct dsc_vdev *vdev); +/** + * dsc_vdev_get_cached_cmd() - Get north bound cmd cached during SSR + * @vdev: Pointer to the dsc vdev + * + * This api will be invoked after completion of SSR re-initialization to get + * the last north bound command received during SSR + * + * Return: North bound command ID + */ +uint8_t dsc_vdev_get_cached_cmd(struct dsc_vdev *vdev); + +/** + * dsc_vdev_cache_command() - Cache north bound command during SSR + * @vdev: Pointer to the dsc vdev corresponding to the network interface + * @cmd_id: North bound command ID + * + * This api will be invoked when a north bound command is received during SSR + * and it should be handled after SSR re-initialization. + * + * Return: None + */ +void dsc_vdev_cache_command(struct dsc_vdev *vdev, uint8_t cmd_id); + #endif /* __WLAN_DSC_VDEV_H */ diff --git a/components/dsc/src/__wlan_dsc.h b/components/dsc/src/__wlan_dsc.h index bd9de76854..34cae5ee6d 100644 --- a/components/dsc/src/__wlan_dsc.h +++ b/components/dsc/src/__wlan_dsc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2018-2019, 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 @@ -172,12 +172,14 @@ struct dsc_psoc { * @psoc: parent psoc context * @trans: transition tracking container for this node * @ops: operations in flight tracking container for this node + * @nb_cmd_during_ssr: north bound command id */ struct dsc_vdev { qdf_list_node_t node; struct dsc_psoc *psoc; struct dsc_trans trans; struct dsc_ops ops; + uint8_t nb_cmd_during_ssr; }; #define dsc_for_each_driver_psoc(driver_ptr, psoc_cursor) \ diff --git a/components/dsc/src/wlan_dsc_vdev.c b/components/dsc/src/wlan_dsc_vdev.c index b045ffd533..edd7f81775 100644 --- a/components/dsc/src/wlan_dsc_vdev.c +++ b/components/dsc/src/wlan_dsc_vdev.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2018-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 @@ -376,3 +376,13 @@ void dsc_vdev_wait_for_ops(struct dsc_vdev *vdev) __dsc_vdev_wait_for_ops(vdev); } +uint8_t dsc_vdev_get_cached_cmd(struct dsc_vdev *vdev) +{ + return vdev->nb_cmd_during_ssr; +} + +void dsc_vdev_cache_command(struct dsc_vdev *vdev, uint8_t cmd_id) +{ + vdev->nb_cmd_during_ssr = cmd_id; +} + diff --git a/core/hdd/inc/wlan_hdd_main.h b/core/hdd/inc/wlan_hdd_main.h index af85d4ea8a..9e515274ce 100644 --- a/core/hdd/inc/wlan_hdd_main.h +++ b/core/hdd/inc/wlan_hdd_main.h @@ -245,7 +245,6 @@ static inline bool in_compat_syscall(void) { return is_compat_task(); } * @DEVICE_IFACE_OPENED: Adapter has been "opened" via the kernel * @SOFTAP_INIT_DONE: Software Access Point (SAP) is initialized * @VENDOR_ACS_RESPONSE_PENDING: Waiting for event for vendor acs - * @DOWN_DURING_SSR: Mark interface is down during SSR */ enum hdd_adapter_flags { NET_DEVICE_REGISTERED, @@ -256,7 +255,16 @@ enum hdd_adapter_flags { DEVICE_IFACE_OPENED, SOFTAP_INIT_DONE, VENDOR_ACS_RESPONSE_PENDING, - DOWN_DURING_SSR, +}; + +/** + * enum hdd_nb_cmd_id - North bound command IDs received during SSR + * @NO_COMMAND - No NB command received during SSR + * @INTERFACE_DOWN - Received interface down during SSR + */ +enum hdd_nb_cmd_id { + NO_COMMAND, + INTERFACE_DOWN }; #define WLAN_WAIT_DISCONNECT_ALREADY_IN_PROGRESS 1000 @@ -4918,6 +4926,17 @@ static inline unsigned long wlan_hdd_get_pm_qos_cpu_latency(void) */ void hdd_netdev_update_features(struct hdd_adapter *adapter); +/** + * hdd_stop_no_trans() - HDD stop function + * @dev: Pointer to net_device structure + * + * This is called in response to ifconfig down. Vdev sync transaction + * should be started before calling this API. + * + * Return: 0 for success; non-zero for failure + */ +int hdd_stop_no_trans(struct net_device *dev); + #if defined(CLD_PM_QOS) /** * wlan_hdd_set_pm_qos_request() - Function to set pm_qos config in wlm mode diff --git a/core/hdd/inc/wlan_hdd_power.h b/core/hdd/inc/wlan_hdd_power.h index 6e4f2b9472..7bec38767e 100644 --- a/core/hdd/inc/wlan_hdd_power.h +++ b/core/hdd/inc/wlan_hdd_power.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2014-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2012, 2014-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 @@ -186,6 +186,16 @@ QDF_STATUS hdd_wlan_shutdown(void); */ QDF_STATUS hdd_wlan_re_init(void); +/** + * hdd_handle_cached_commands() - Handle north bound commands during SSR + * + * This api will be invoked afte SSR re-initialization to execute the north + * bound commands received during SSR. + * + * Return: None + */ +void hdd_handle_cached_commands(void); + /** * hdd_enable_arp_offload() - API to enable ARP offload * @adapter: Adapter context for which ARP offload is to be configured diff --git a/core/hdd/src/wlan_hdd_driver_ops.c b/core/hdd/src/wlan_hdd_driver_ops.c index 2f9b3870e5..37931b8908 100644 --- a/core/hdd/src/wlan_hdd_driver_ops.c +++ b/core/hdd/src/wlan_hdd_driver_ops.c @@ -704,8 +704,10 @@ static int __hdd_soc_recovery_reinit(struct device *dev, * So check if FW is down then don't reset the recovery * in progress */ - if (!qdf_is_fw_down()) + if (!qdf_is_fw_down()) { cds_set_recovery_in_progress(false); + hdd_handle_cached_commands(); + } hdd_soc_load_unlock(dev); hdd_start_complete(0); diff --git a/core/hdd/src/wlan_hdd_hostapd.c b/core/hdd/src/wlan_hdd_hostapd.c index aaaa62367a..bbaf894d2f 100644 --- a/core/hdd/src/wlan_hdd_hostapd.c +++ b/core/hdd/src/wlan_hdd_hostapd.c @@ -491,21 +491,14 @@ static int hdd_hostapd_open(struct net_device *net_dev) return errno; errno = __hdd_hostapd_open(net_dev); - + if (!errno) + osif_vdev_cache_command(vdev_sync, NO_COMMAND); osif_vdev_sync_trans_stop(vdev_sync); return errno; } -/** - * __hdd_hostapd_stop() - hdd stop function for hostapd interface - * This is called in response to ifconfig down - * - * @dev: pointer to net_device structure - * - * Return - 0 for success non-zero for failure - */ -static int __hdd_hostapd_stop(struct net_device *dev) +int hdd_hostapd_stop_no_trans(struct net_device *dev) { struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); @@ -518,10 +511,8 @@ static int __hdd_hostapd_stop(struct net_device *dev) NO_SESSION, 0); ret = wlan_hdd_validate_context(hdd_ctx); - if (ret) { - set_bit(DOWN_DURING_SSR, &adapter->event_flags); + if (ret) return ret; - } /* * Some tests requires to do "ifconfig down" only to bring @@ -561,10 +552,13 @@ int hdd_hostapd_stop(struct net_device *net_dev) struct osif_vdev_sync *vdev_sync; errno = osif_vdev_sync_trans_start(net_dev, &vdev_sync); - if (errno) + if (errno) { + if (vdev_sync) + osif_vdev_cache_command(vdev_sync, INTERFACE_DOWN); return errno; + } - errno = __hdd_hostapd_stop(net_dev); + errno = hdd_hostapd_stop_no_trans(net_dev); osif_vdev_sync_trans_stop(vdev_sync); diff --git a/core/hdd/src/wlan_hdd_hostapd.h b/core/hdd/src/wlan_hdd_hostapd.h index f34140ea84..9ae43eb495 100644 --- a/core/hdd/src/wlan_hdd_hostapd.h +++ b/core/hdd/src/wlan_hdd_hostapd.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2013-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 @@ -213,6 +213,17 @@ bool hdd_sap_destroy_ctx(struct hdd_adapter *adapter); */ void hdd_sap_destroy_ctx_all(struct hdd_context *hdd_ctx, bool is_ssr); +/** + * hdd_hostapd_stop_no_trans() - hdd stop function for hostapd interface + * @dev: pointer to net_device structure + * + * This is called in response to ifconfig down. Vdev sync transaction + * should be started before calling this API. + * + * Return - 0 for success non-zero for failure + */ +int hdd_hostapd_stop_no_trans(struct net_device *dev); + int hdd_hostapd_stop(struct net_device *dev); int hdd_sap_context_init(struct hdd_context *hdd_ctx); void hdd_sap_context_destroy(struct hdd_context *hdd_ctx); diff --git a/core/hdd/src/wlan_hdd_main.c b/core/hdd/src/wlan_hdd_main.c index 3f675545c6..fdb6d452a9 100644 --- a/core/hdd/src/wlan_hdd_main.c +++ b/core/hdd/src/wlan_hdd_main.c @@ -4416,21 +4416,15 @@ static int hdd_open(struct net_device *net_dev) return errno; errno = __hdd_open(net_dev); + if (!errno) + osif_vdev_cache_command(vdev_sync, NO_COMMAND); osif_vdev_sync_trans_stop(vdev_sync); return errno; } -/** - * __hdd_stop() - HDD stop function - * @dev: Pointer to net_device structure - * - * This is called in response to ifconfig down - * - * Return: 0 for success; non-zero for failure - */ -static int __hdd_stop(struct net_device *dev) +int hdd_stop_no_trans(struct net_device *dev) { struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); @@ -4444,10 +4438,8 @@ static int __hdd_stop(struct net_device *dev) adapter->vdev_id, adapter->device_mode); ret = wlan_hdd_validate_context(hdd_ctx); - if (ret) { - set_bit(DOWN_DURING_SSR, &adapter->event_flags); + if (ret) return ret; - } /* Nothing to be done if the interface is not opened */ if (false == test_bit(DEVICE_IFACE_OPENED, &adapter->event_flags)) { @@ -4545,10 +4537,13 @@ static int hdd_stop(struct net_device *net_dev) struct osif_vdev_sync *vdev_sync; errno = osif_vdev_sync_trans_start(net_dev, &vdev_sync); - if (errno) + if (errno) { + if (vdev_sync) + osif_vdev_cache_command(vdev_sync, INTERFACE_DOWN); return errno; + } - errno = __hdd_stop(net_dev); + errno = hdd_stop_no_trans(net_dev); osif_vdev_sync_trans_stop(vdev_sync); @@ -8560,19 +8555,9 @@ QDF_STATUS hdd_start_all_adapters(struct hdd_context *hdd_ctx) hdd_start_station_adapter(adapter); hdd_set_mon_rx_cb(adapter->dev); - /* - * Do not set channel for monitor mode if monitor iface - * went down during SSR, as for set channels host sends - * vdev start command to FW. For the interfaces went - * down during SSR, host stops those adapters by sending - * vdev stop/down/delete commands to FW. So FW doesn't - * sends response for vdev start and vdev start response - * timer expires and thus host triggers ASSERT. - */ - if (!test_bit(DOWN_DURING_SSR, &adapter->event_flags)) - wlan_hdd_set_mon_chan( - adapter, adapter->mon_chan_freq, - adapter->mon_bandwidth); + wlan_hdd_set_mon_chan( + adapter, adapter->mon_chan_freq, + adapter->mon_bandwidth); break; case QDF_NDI_MODE: hdd_ndi_start(adapter->dev->name, 0); diff --git a/core/hdd/src/wlan_hdd_power.c b/core/hdd/src/wlan_hdd_power.c index 53d3c17b36..1348c0dfa8 100644 --- a/core/hdd/src/wlan_hdd_power.c +++ b/core/hdd/src/wlan_hdd_power.c @@ -1584,36 +1584,6 @@ static void hdd_send_default_scan_ies(struct hdd_context *hdd_ctx) } } -/** - * hdd_is_interface_down_during_ssr - Check if the interface went down during - * SSR - * @hdd_ctx: HDD context - * - * Check if any of the interface went down while the device is recovering. - * If the interface went down close the session. - */ -static void hdd_is_interface_down_during_ssr(struct hdd_context *hdd_ctx) -{ - struct hdd_adapter *adapter = NULL, *pnext = NULL; - QDF_STATUS status; - - hdd_enter(); - - status = hdd_get_front_adapter(hdd_ctx, &adapter); - while (adapter && status == QDF_STATUS_SUCCESS) { - if (test_bit(DOWN_DURING_SSR, &adapter->event_flags)) { - clear_bit(DOWN_DURING_SSR, &adapter->event_flags); - hdd_stop_adapter(hdd_ctx, adapter); - hdd_deinit_adapter(hdd_ctx, adapter, true); - clear_bit(DEVICE_IFACE_OPENED, &adapter->event_flags); - } - status = hdd_get_next_adapter(hdd_ctx, adapter, &pnext); - adapter = pnext; - } - - hdd_exit(); -} - /** * hdd_restore_sar_config - Restore the saved SAR config after SSR * @hdd_ctx: HDD context @@ -1635,6 +1605,58 @@ static void hdd_restore_sar_config(struct hdd_context *hdd_ctx) hdd_err("Unable to configured SAR after SSR"); } +void hdd_handle_cached_commands(void) +{ + struct net_device *net_dev; + struct hdd_adapter *adapter = NULL; + struct hdd_context *hdd_ctx; + struct osif_vdev_sync *vdev_sync_arr = osif_get_vdev_sync_arr(); + struct osif_vdev_sync *vdev_sync; + int i; + uint8_t cmd_id; + + /* Get the HDD context */ + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) + return; + + for (i = 0; i < WLAN_MAX_VDEVS; i++) { + vdev_sync = vdev_sync_arr + i; + if (!vdev_sync || !vdev_sync->in_use) + continue; + + cmd_id = osif_vdev_get_cached_cmd(vdev_sync); + net_dev = vdev_sync->net_dev; + if (net_dev) { + adapter = WLAN_HDD_GET_PRIV_PTR( + (struct net_device *)net_dev); + if (!adapter) + continue; + } else { + continue; + } + + switch (cmd_id) { + case NO_COMMAND: + break; + case INTERFACE_DOWN: + hdd_debug("Handling cached interface down command for %s", + adapter->dev->name); + + if (adapter->device_mode == QDF_SAP_MODE || + adapter->device_mode == QDF_P2P_GO_MODE) + hdd_hostapd_stop_no_trans(net_dev); + else + hdd_stop_no_trans(net_dev); + + osif_vdev_cache_command(vdev_sync, NO_COMMAND); + break; + default: + break; + } + } +} + QDF_STATUS hdd_wlan_re_init(void) { struct hdd_context *hdd_ctx = NULL; @@ -1694,7 +1716,6 @@ QDF_STATUS hdd_wlan_re_init(void) ucfg_mlme_get_sap_internal_restart(hdd_ctx->psoc, &value); if (value) hdd_ssr_restart_sap(hdd_ctx); - hdd_is_interface_down_during_ssr(hdd_ctx); hdd_wlan_ssr_reinit_event(); return QDF_STATUS_SUCCESS; diff --git a/os_if/sync/inc/osif_vdev_sync.h b/os_if/sync/inc/osif_vdev_sync.h index 0993cb037e..45e6c786ce 100644 --- a/os_if/sync/inc/osif_vdev_sync.h +++ b/os_if/sync/inc/osif_vdev_sync.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2018-2019, 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 @@ -24,9 +24,16 @@ #include "qdf_types.h" /** - * struct osif_vdev_sync - opaque synchronization handle for a vdev + * struct osif_vdev_sync - vdev synchronization context + * @net_dev: the net_device used as a lookup key + * @dsc_vdev: the dsc_vdev used for synchronization + * @in_use: indicates if the context is being used */ -struct osif_vdev_sync; +struct osif_vdev_sync { + struct net_device *net_dev; + struct dsc_vdev *dsc_vdev; + bool in_use; +}; /** * osif_vdev_sync_create() - create a vdev synchronization context @@ -168,5 +175,35 @@ void __osif_vdev_sync_op_stop(struct osif_vdev_sync *vdev_sync, */ void osif_vdev_sync_wait_for_ops(struct osif_vdev_sync *vdev_sync); +/** + * osif_vdev_get_cached_cmd() - Get north bound cmd cached during SSR + * @vdev_sync: osif vdev sync corresponding to the network interface + * + * This api will be invoked after completion of SSR re-initialization to get + * the last north bound command received during SSR + * + * Return: North bound command ID + */ +uint8_t osif_vdev_get_cached_cmd(struct osif_vdev_sync *vdev_sync); + +/** + * osif_vdev_cache_command() - Cache north bound command during SSR + * @vdev_sync: osif vdev sync corresponding to the network interface + * @cmd_id: North bound command ID + * + * This api will be invoked when a north bound command is received during SSR + * and it should be handled after SSR re-initialization. + * + * Return: None + */ +void osif_vdev_cache_command(struct osif_vdev_sync *vdev_sync, uint8_t cmd_id); + +/** + * osif_get_vdev_sync_arr() - Get vdev sync array base pointer + * + * Return: Base pointer to the vdev sync array + */ +struct osif_vdev_sync *osif_get_vdev_sync_arr(void); + #endif /* __OSIF_VDEV_SYNC_H */ diff --git a/os_if/sync/src/osif_vdev_sync.c b/os_if/sync/src/osif_vdev_sync.c index b1a1eba7bf..07a8b5b977 100644 --- a/os_if/sync/src/osif_vdev_sync.c +++ b/os_if/sync/src/osif_vdev_sync.c @@ -25,18 +25,6 @@ #include "qdf_status.h" #include "qdf_types.h" -/** - * struct osif_vdev_sync - a vdev synchronization context - * @net_dev: the net_device used as a lookup key - * @dsc_vdev: the dsc_vdev used for synchronization - * @in_use: indicates if the context is being used - */ -struct osif_vdev_sync { - struct net_device *net_dev; - struct dsc_vdev *dsc_vdev; - bool in_use; -}; - static struct osif_vdev_sync __osif_vdev_sync_arr[WLAN_MAX_VDEVS]; static qdf_spinlock_t __osif_vdev_sync_lock; @@ -63,6 +51,11 @@ static struct osif_vdev_sync *osif_vdev_sync_lookup(struct net_device *net_dev) return NULL; } +struct osif_vdev_sync *osif_get_vdev_sync_arr(void) +{ + return __osif_vdev_sync_arr; +} + static struct osif_vdev_sync *osif_vdev_sync_get(void) { int i; @@ -206,12 +199,12 @@ __osif_vdev_sync_start_callback(struct net_device *net_dev, if (!vdev_sync) return -EAGAIN; + *out_vdev_sync = vdev_sync; + status = vdev_start_cb(vdev_sync->dsc_vdev, desc); if (QDF_IS_STATUS_ERROR(status)) return qdf_status_to_os_return(status); - *out_vdev_sync = vdev_sync; - return 0; } @@ -323,3 +316,13 @@ void osif_vdev_sync_deinit(void) osif_vdev_sync_lock_destroy(); } +uint8_t osif_vdev_get_cached_cmd(struct osif_vdev_sync *vdev_sync) +{ + return dsc_vdev_get_cached_cmd(vdev_sync->dsc_vdev); +} + +void osif_vdev_cache_command(struct osif_vdev_sync *vdev_sync, uint8_t cmd_id) +{ + dsc_vdev_cache_command(vdev_sync->dsc_vdev, cmd_id); +} +