From: Viktor Barna <viktor.barna@xxxxxxxxxx> (Part of the split. Please, take a look at the cover letter for more details). Signed-off-by: Viktor Barna <viktor.barna@xxxxxxxxxx> --- drivers/net/wireless/celeno/cl8k/traffic.c | 254 +++++++++++++++++++++ 1 file changed, 254 insertions(+) create mode 100644 drivers/net/wireless/celeno/cl8k/traffic.c diff --git a/drivers/net/wireless/celeno/cl8k/traffic.c b/drivers/net/wireless/celeno/cl8k/traffic.c new file mode 100644 index 000000000000..788fd02e28a8 --- /dev/null +++ b/drivers/net/wireless/celeno/cl8k/traffic.c @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* Copyright(c) 2019-2022, Celeno Communications Ltd. */ + +#include <linux/ip.h> +#include <linux/ipv6.h> + +#include "bf.h" +#include "tx.h" +#include "radio.h" +#include "utils.h" +#include "debug.h" +#include "hw.h" +#include "traffic.h" + +#define TRAFFIC_CNTR_ACTIVE_THR 3 /* 3 * 100ms = 300ms */ +#define TRAFFIC_CNTR_IDLE_THR 20 /* 20 * 100ms = 2sec */ + +/* Threshold in bytes */ +#define TRAFFIC_ACTIVE_THR_DRV 1920 /* = 150Kbit/sec (150 * 1024 / 8 / 10) */ +#define TRAFFIC_ACTIVE_THR_BF 26214 /* = 2mbit/sec (2 * 1024 * 1024 / 8 / 10) */ +#define TRAFFIC_ACTIVE_THR_MU 131070 /* = 10mbit/sec (10 * 1024 * 1024 / 8 / 10) */ +#define TRAFFIC_ACTIVE_THR_EDCA_6G 2621440 /* = 200mbit/sec (200 * 1024 * 1024 / 8 / 10) */ +#define TRAFFIC_ACTIVE_THR_EDCA_5G 2621440 /* = 200mbit/sec (200 * 1024 * 1024 / 8 / 10) */ +#define TRAFFIC_ACTIVE_THR_EDCA_24G 655360 /* = 50mbit/sec (50 * 1024 * 1024 / 8 / 10) */ +#define TRAFFIC_ACTIVE_THR_DFS 13107 /* = 1mbit/sec (1 * 1024 * 1024 / 8 / 10) */ + +static void cl_traffic_mon_ipv4(struct sk_buff *skb, struct cl_sta *cl_sta, + enum cl_traffic_mon_direction direction) +{ + struct iphdr *iphdr = ip_hdr(skb); + + if (iphdr->protocol == IPPROTO_UDP) + cl_sta->traffic_mon[CL_TRFC_MON_PROT_UDP][direction].bytes += + ntohs(iphdr->tot_len) - IPV4_HDR_LEN(iphdr->ihl) - sizeof(struct udphdr); + else if (iphdr->protocol == IPPROTO_TCP) + cl_sta->traffic_mon[CL_TRFC_MON_PROT_TCP][direction].bytes += + ntohs(iphdr->tot_len) - IPV4_HDR_LEN(iphdr->ihl) - sizeof(struct tcphdr); +} + +static void cl_traffic_mon_ipv6(struct sk_buff *skb, struct cl_sta *cl_sta, + enum cl_traffic_mon_direction direction) +{ + struct ipv6hdr *ipv6hdr = ipv6_hdr(skb); + + if (ipv6hdr->nexthdr == IPPROTO_UDP) + cl_sta->traffic_mon[CL_TRFC_MON_PROT_UDP][direction].bytes += + ntohs(ipv6hdr->payload_len) - sizeof(struct udphdr); + else if (ipv6hdr->nexthdr == IPPROTO_TCP) + cl_sta->traffic_mon[CL_TRFC_MON_PROT_TCP][direction].bytes += + ntohs(ipv6hdr->payload_len) - sizeof(struct tcphdr); +} + +static void cl_traffic_mon_calc(struct sk_buff *skb, struct cl_sta *cl_sta, + enum cl_traffic_mon_direction direction) +{ + if (cl_set_network_header_if_proto(skb, ETH_P_IP)) + cl_traffic_mon_ipv4(skb, cl_sta, direction); + else if (cl_set_network_header_if_proto(skb, ETH_P_IPV6)) + cl_traffic_mon_ipv6(skb, cl_sta, direction); +} + +void cl_traffic_mon_tx(struct cl_sta *cl_sta, struct sk_buff *skb) +{ + struct cl_hw *cl_hw = cl_sta->cl_vif->cl_hw; + + if (cl_hw->conf->ci_traffic_mon_en) + cl_traffic_mon_calc(skb, cl_sta, CL_TRFC_MON_DIR_DL); +} + +void cl_traffic_mon_rx(struct cl_sta *cl_sta, struct sk_buff *skb) +{ + struct cl_hw *cl_hw = cl_sta->cl_vif->cl_hw; + + if (cl_hw->conf->ci_traffic_mon_en) + cl_traffic_mon_calc(skb, cl_sta, CL_TRFC_MON_DIR_UL); +} + +void cl_traffic_mon_sta_maintenance(struct cl_hw *cl_hw, struct cl_sta *cl_sta) +{ + u8 i, j; + + for (i = 0; i < CL_TRFC_MON_PROT_MAX; i++) + for (j = 0; j < CL_TRFC_MON_DIR_MAX; j++) { + cl_sta->traffic_mon[i][j].bytes_per_sec = cl_sta->traffic_mon[i][j].bytes; + cl_sta->traffic_mon[i][j].bytes = 0; + } +} + +static void cl_traffic_sta_start(struct cl_hw *cl_hw, struct cl_sta *cl_sta, + enum cl_traffic_level level, enum cl_traffic_direction direction) +{ + cl_hw->traffic_db.num_active_sta_dir[direction][level]++; + + /* If other direction is not active increase num_active_sta */ + if (!cl_sta->traffic_db[1 - direction].activity_db[level].is_active) + cl_hw->traffic_db.num_active_sta[level]++; + + if (level == TRAFFIC_LEVEL_DRV) { + /* + * Dynamic CTS: + * If protection mode is disabled, environment is clean, + * and station threshold was reached switch to CTS. + */ + if (cl_hw->traffic_db.num_active_sta[TRAFFIC_LEVEL_DRV] == + cl_hw->conf->ci_dyn_cts_sta_thr) { + if (cl_prot_mode_get(cl_hw) == TXL_NO_PROT) { + cl_hw->traffic_db.dynamic_cts = true; + cl_prot_mode_set(cl_hw, TXL_PROT_CTS); + } + } + } else if (level == TRAFFIC_LEVEL_BF) { + if (direction == TRAFFIC_DIRECTION_TX) + cl_bf_sta_active(cl_hw, cl_sta, true); + } +} + +static void cl_traffic_sta_stop(struct cl_hw *cl_hw, struct cl_sta *cl_sta, + enum cl_traffic_level level, enum cl_traffic_direction direction) +{ + cl_hw->traffic_db.num_active_sta_dir[direction][level]--; + + /* If other direction is not active decrease num_active_sta */ + if (!cl_sta->traffic_db[1 - direction].activity_db[level].is_active) + cl_hw->traffic_db.num_active_sta[level]--; + + if (level == TRAFFIC_LEVEL_DRV) { + /* Dynamic CTS: + * If it was turned on and active station count became lower than + * threshold --> return to no protection + */ + if (cl_hw->traffic_db.dynamic_cts) { + if (cl_hw->traffic_db.num_active_sta[TRAFFIC_LEVEL_DRV] == + (cl_hw->conf->ci_dyn_cts_sta_thr - 1)) { + cl_hw->traffic_db.dynamic_cts = false; + cl_prot_mode_set(cl_hw, TXL_NO_PROT); + } + } + } else if (level == TRAFFIC_LEVEL_BF) { + if (direction == TRAFFIC_DIRECTION_TX) + cl_bf_sta_active(cl_hw, cl_sta, false); + } +} + +static void cl_traffic_check_activity(struct cl_hw *cl_hw, struct cl_sta *cl_sta, + enum cl_traffic_level level, + enum cl_traffic_direction direction) +{ + struct cl_traffic_activity *activity_db = + &cl_sta->traffic_db[direction].activity_db[level]; + u32 num_bytes = cl_sta->traffic_db[direction].num_bytes; + + if (num_bytes > cl_hw->traffic_db.active_bytes_thr[level]) { + activity_db->cntr_active++; + activity_db->cntr_idle = 0; + + /* If traffic is above threshold for X consective times change state to active */ + if (!activity_db->is_active && + activity_db->cntr_active >= TRAFFIC_CNTR_ACTIVE_THR) { + activity_db->is_active = true; + cl_traffic_sta_start(cl_hw, cl_sta, level, direction); + } + } else { + activity_db->cntr_active = 0; + activity_db->cntr_idle++; + + /* If traffic is below threshold for Y consective times change state to idle */ + if (activity_db->is_active && activity_db->cntr_idle >= TRAFFIC_CNTR_IDLE_THR) { + activity_db->is_active = false; + cl_traffic_sta_stop(cl_hw, cl_sta, level, direction); + } + } +} + +static void cl_traffic_monitor_sta_traffic(struct cl_hw *cl_hw, struct cl_sta *cl_sta) +{ + enum cl_traffic_level level = 0; + + /* Check Tx & Rx activity in all levels */ + for (level = 0; level < TRAFFIC_LEVEL_MAX; level++) { + cl_traffic_check_activity(cl_hw, cl_sta, level, TRAFFIC_DIRECTION_TX); + cl_traffic_check_activity(cl_hw, cl_sta, level, TRAFFIC_DIRECTION_RX); + } + + /* Save previous Tx num bytes */ + cl_sta->traffic_db[TRAFFIC_DIRECTION_TX].num_bytes_prev = + cl_sta->traffic_db[TRAFFIC_DIRECTION_TX].num_bytes; + + /* Reset num_bytes */ + cl_sta->traffic_db[TRAFFIC_DIRECTION_TX].num_bytes = 0; + cl_sta->traffic_db[TRAFFIC_DIRECTION_RX].num_bytes = 0; +} + +void cl_traffic_init(struct cl_hw *cl_hw) +{ + struct cl_traffic_main *traffic_db = &cl_hw->traffic_db; + + traffic_db->active_bytes_thr[TRAFFIC_LEVEL_DRV] = TRAFFIC_ACTIVE_THR_DRV; + traffic_db->active_bytes_thr[TRAFFIC_LEVEL_BF] = TRAFFIC_ACTIVE_THR_BF; + traffic_db->active_bytes_thr[TRAFFIC_LEVEL_MU] = TRAFFIC_ACTIVE_THR_MU; + + if (cl_band_is_6g(cl_hw)) + traffic_db->active_bytes_thr[TRAFFIC_LEVEL_EDCA] = TRAFFIC_ACTIVE_THR_EDCA_6G; + else if (cl_band_is_5g(cl_hw)) + traffic_db->active_bytes_thr[TRAFFIC_LEVEL_EDCA] = TRAFFIC_ACTIVE_THR_EDCA_5G; + else + traffic_db->active_bytes_thr[TRAFFIC_LEVEL_EDCA] = TRAFFIC_ACTIVE_THR_EDCA_24G; + + traffic_db->active_bytes_thr[TRAFFIC_LEVEL_DFS] = TRAFFIC_ACTIVE_THR_DFS; +} + +void cl_traffic_tx_handler(struct cl_hw *cl_hw, struct cl_sta *cl_sta, u32 num_bytes) +{ + cl_sta->traffic_db[TRAFFIC_DIRECTION_TX].num_bytes += num_bytes; + cl_sta->tx_bytes += num_bytes; +} + +void cl_traffic_rx_handler(struct cl_hw *cl_hw, struct cl_sta *cl_sta, u32 num_bytes) +{ + cl_sta->traffic_db[TRAFFIC_DIRECTION_RX].num_bytes += num_bytes; + cl_sta->rx_bytes += num_bytes; +} + +void cl_traffic_maintenance(struct cl_hw *cl_hw) +{ + cl_sta_loop(cl_hw, cl_traffic_monitor_sta_traffic); +} + +void cl_traffic_sta_remove(struct cl_hw *cl_hw, struct cl_sta *cl_sta) +{ + /* Check if station disconnected during traffic */ + enum cl_traffic_level level = 0; + enum cl_traffic_direction direction = 0; + + for (direction = 0; direction < TRAFFIC_DIRECTION_MAX; direction++) { + for (level = 0; level < TRAFFIC_LEVEL_MAX; level++) { + if (cl_sta->traffic_db[direction].activity_db[level].is_active) + cl_traffic_sta_stop(cl_hw, cl_sta, level, direction); + } + + memset(&cl_sta->traffic_db, 0, sizeof(cl_sta->traffic_db)); + } +} + +bool cl_traffic_is_sta_active(struct cl_hw *cl_hw, struct cl_sta *cl_sta) +{ + return (cl_sta->traffic_db[TRAFFIC_DIRECTION_TX].activity_db[TRAFFIC_LEVEL_DRV].is_active || + cl_sta->traffic_db[TRAFFIC_DIRECTION_RX].activity_db[TRAFFIC_LEVEL_DRV].is_active); +} + +bool cl_traffic_is_sta_tx_exist(struct cl_hw *cl_hw, struct cl_sta *cl_sta) +{ + return ((cl_sta->traffic_db[TRAFFIC_DIRECTION_TX].num_bytes != 0) || + (cl_sta->traffic_db[TRAFFIC_DIRECTION_TX].num_bytes_prev != 0)); +} -- 2.36.1