From 95ea7c675f0aaf588edef8514ae039e22da537a1 Mon Sep 17 00:00:00 2001 From: Sourav Mohapatra Date: Wed, 24 Jun 2020 11:26:17 +0530 Subject: [PATCH] qcacld-3.0: Limit the number of times get_tx_power can come Currently in the driver, there is no limit on how many number of times get_tx_power can be requested by the userspace. This can lead to potential issues where the firmware crashes due to excessive stats request. Add a time based rate limiting logic wherein the function call only sends request to the firmware once in three seconds. In other cases, it returns the cached value present in the adapter. Change-Id: I65f23135d88c66e6438484715dafce3def14ddb3 CRs-Fixed: 2717633 --- core/hdd/inc/hdd_config.h | 26 ++++++++++++++++++++++++++ core/hdd/inc/wlan_hdd_cfg.h | 1 + core/hdd/inc/wlan_hdd_main.h | 23 +++++++++++++++++++++++ core/hdd/src/wlan_hdd_main.c | 2 ++ core/hdd/src/wlan_hdd_power.c | 8 ++++++-- 5 files changed, 58 insertions(+), 2 deletions(-) diff --git a/core/hdd/inc/hdd_config.h b/core/hdd/inc/hdd_config.h index c399d689703f..9c9fb5a2915f 100644 --- a/core/hdd/inc/hdd_config.h +++ b/core/hdd/inc/hdd_config.h @@ -1395,6 +1395,31 @@ struct dhcp_server { CFG_VALUE_OR_DEFAULT, \ "Disable wow feature") +/* + * + * nb_commands_interval - Used to rate limit nb commands from userspace + * + * @Min: 0 + * @Max: 10 + * Default: 3 + * + * This ini is used to specify the duration in which any supp. nb command from + * userspace will not be processed completely in driver. For ex, the default + * value of 3 seconds signifies that consecutive commands within that + * time will not be processed fully. + * + * Usage: Internal + * + * + */ +#define CFG_NB_COMMANDS_RATE_LIMIT CFG_INI_UINT( \ + "nb_commands_interval", \ + 0, \ + 10, \ + 3, \ + CFG_VALUE_OR_DEFAULT, \ + "Rate limiting for nb commands") + #ifdef WLAN_FEATURE_PERIODIC_STA_STATS /* * @@ -1606,6 +1631,7 @@ enum host_log_level { CFG(CFG_PRIVATE_WEXT_CONTROL) \ CFG(CFG_PROVISION_INTERFACE_POOL) \ CFG(CFG_TIMER_MULTIPLIER) \ + CFG(CFG_NB_COMMANDS_RATE_LIMIT) \ CFG(CFG_HDD_DOT11_MODE) \ CFG(CFG_ENABLE_DISABLE_CHANNEL) \ CFG(CFG_SAR_CONVERSION) \ diff --git a/core/hdd/inc/wlan_hdd_cfg.h b/core/hdd/inc/wlan_hdd_cfg.h index e66ea6dfbcb5..64327536efb9 100644 --- a/core/hdd/inc/wlan_hdd_cfg.h +++ b/core/hdd/inc/wlan_hdd_cfg.h @@ -253,6 +253,7 @@ struct hdd_config { /* Duration for which periodic logging should be done */ uint32_t periodic_stats_timer_duration; #endif /* WLAN_FEATURE_PERIODIC_STA_STATS */ + uint8_t nb_commands_interval; }; /** diff --git a/core/hdd/inc/wlan_hdd_main.h b/core/hdd/inc/wlan_hdd_main.h index 1b55f399a74d..6f2248f623bd 100644 --- a/core/hdd/inc/wlan_hdd_main.h +++ b/core/hdd/inc/wlan_hdd_main.h @@ -156,6 +156,29 @@ struct hdd_apf_context { #define NUM_TX_QUEUES 4 #endif +/* HDD_IS_RATE_LIMIT_REQ: Macro helper to implement rate limiting + * @flag: The flag to determine if limiting is required or not + * @rate: The number of seconds within which if multiple commands come, the + * flag will be set to true + * + * If the function in which this macro is used is called multiple times within + * "rate" number of seconds, the "flag" will be set to true which can be used + * to reject/take appropriate action. + */ +#define HDD_IS_RATE_LIMIT_REQ(flag, rate)\ + do {\ + static ulong __last_ticks;\ + ulong __ticks = jiffies;\ + flag = false; \ + if (!time_after(__ticks,\ + __last_ticks + rate * HZ)) {\ + flag = true; \ + } \ + else { \ + __last_ticks = __ticks;\ + } \ + } while (0) + /* * API in_compat_syscall() is introduced in 4.6 kernel to check whether we're * in a compat syscall or not. It is a new way to query the syscall type, which diff --git a/core/hdd/src/wlan_hdd_main.c b/core/hdd/src/wlan_hdd_main.c index f4ed4224a2ce..08e48cfaeca2 100644 --- a/core/hdd/src/wlan_hdd_main.c +++ b/core/hdd/src/wlan_hdd_main.c @@ -11315,6 +11315,8 @@ static void hdd_cfg_params_init(struct hdd_context *hdd_ctx) config->disable_channel = cfg_get(psoc, CFG_ENABLE_DISABLE_CHANNEL); config->enable_sar_conversion = cfg_get(psoc, CFG_SAR_CONVERSION); config->is_wow_disabled = cfg_get(psoc, CFG_WOW_DISABLE); + config->nb_commands_interval = + cfg_get(psoc, CFG_NB_COMMANDS_RATE_LIMIT); hdd_periodic_sta_stats_config(config, psoc); hdd_init_vc_mode_cfg_bitmap(config, psoc); diff --git a/core/hdd/src/wlan_hdd_power.c b/core/hdd/src/wlan_hdd_power.c index 2065adc0c92d..3359ac32ff46 100644 --- a/core/hdd/src/wlan_hdd_power.c +++ b/core/hdd/src/wlan_hdd_power.c @@ -2441,6 +2441,7 @@ static int __wlan_hdd_cfg80211_get_txpower(struct wiphy *wiphy, struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(ndev); int status; struct hdd_station_ctx *sta_ctx; + static bool is_rate_limited; hdd_enter_dev(ndev); @@ -2485,8 +2486,11 @@ static int __wlan_hdd_cfg80211_get_txpower(struct wiphy *wiphy, return 0; } - if (hdd_ctx->driver_status != DRIVER_MODULES_ENABLED) { - hdd_debug("Driver Module not enabled return success"); + HDD_IS_RATE_LIMIT_REQ(is_rate_limited, + hdd_ctx->config->nb_commands_interval); + if (hdd_ctx->driver_status != DRIVER_MODULES_ENABLED || + is_rate_limited) { + hdd_debug("Modules not enabled/rate limited, use cached stats"); /* Send cached data to upperlayer*/ *dbm = adapter->hdd_stats.class_a_stat.max_pwr; return 0;