18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * drivers/net/team/team_mode_loadbalance.c - Load-balancing mode for team 48c2ecf20Sopenharmony_ci * Copyright (c) 2012 Jiri Pirko <jpirko@redhat.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/kernel.h> 88c2ecf20Sopenharmony_ci#include <linux/types.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/errno.h> 128c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 138c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 148c2ecf20Sopenharmony_ci#include <linux/filter.h> 158c2ecf20Sopenharmony_ci#include <linux/if_team.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistatic rx_handler_result_t lb_receive(struct team *team, struct team_port *port, 188c2ecf20Sopenharmony_ci struct sk_buff *skb) 198c2ecf20Sopenharmony_ci{ 208c2ecf20Sopenharmony_ci if (unlikely(skb->protocol == htons(ETH_P_SLOW))) { 218c2ecf20Sopenharmony_ci /* LACPDU packets should go to exact delivery */ 228c2ecf20Sopenharmony_ci const unsigned char *dest = eth_hdr(skb)->h_dest; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci if (is_link_local_ether_addr(dest) && dest[5] == 0x02) 258c2ecf20Sopenharmony_ci return RX_HANDLER_EXACT; 268c2ecf20Sopenharmony_ci } 278c2ecf20Sopenharmony_ci return RX_HANDLER_ANOTHER; 288c2ecf20Sopenharmony_ci} 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistruct lb_priv; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_citypedef struct team_port *lb_select_tx_port_func_t(struct team *, 338c2ecf20Sopenharmony_ci struct lb_priv *, 348c2ecf20Sopenharmony_ci struct sk_buff *, 358c2ecf20Sopenharmony_ci unsigned char); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define LB_TX_HASHTABLE_SIZE 256 /* hash is a char */ 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistruct lb_stats { 408c2ecf20Sopenharmony_ci u64 tx_bytes; 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistruct lb_pcpu_stats { 448c2ecf20Sopenharmony_ci struct lb_stats hash_stats[LB_TX_HASHTABLE_SIZE]; 458c2ecf20Sopenharmony_ci struct u64_stats_sync syncp; 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistruct lb_stats_info { 498c2ecf20Sopenharmony_ci struct lb_stats stats; 508c2ecf20Sopenharmony_ci struct lb_stats last_stats; 518c2ecf20Sopenharmony_ci struct team_option_inst_info *opt_inst_info; 528c2ecf20Sopenharmony_ci}; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistruct lb_port_mapping { 558c2ecf20Sopenharmony_ci struct team_port __rcu *port; 568c2ecf20Sopenharmony_ci struct team_option_inst_info *opt_inst_info; 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistruct lb_priv_ex { 608c2ecf20Sopenharmony_ci struct team *team; 618c2ecf20Sopenharmony_ci struct lb_port_mapping tx_hash_to_port_mapping[LB_TX_HASHTABLE_SIZE]; 628c2ecf20Sopenharmony_ci struct sock_fprog_kern *orig_fprog; 638c2ecf20Sopenharmony_ci struct { 648c2ecf20Sopenharmony_ci unsigned int refresh_interval; /* in tenths of second */ 658c2ecf20Sopenharmony_ci struct delayed_work refresh_dw; 668c2ecf20Sopenharmony_ci struct lb_stats_info info[LB_TX_HASHTABLE_SIZE]; 678c2ecf20Sopenharmony_ci } stats; 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistruct lb_priv { 718c2ecf20Sopenharmony_ci struct bpf_prog __rcu *fp; 728c2ecf20Sopenharmony_ci lb_select_tx_port_func_t __rcu *select_tx_port_func; 738c2ecf20Sopenharmony_ci struct lb_pcpu_stats __percpu *pcpu_stats; 748c2ecf20Sopenharmony_ci struct lb_priv_ex *ex; /* priv extension */ 758c2ecf20Sopenharmony_ci}; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic struct lb_priv *get_lb_priv(struct team *team) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci return (struct lb_priv *) &team->mode_priv; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistruct lb_port_priv { 838c2ecf20Sopenharmony_ci struct lb_stats __percpu *pcpu_stats; 848c2ecf20Sopenharmony_ci struct lb_stats_info stats_info; 858c2ecf20Sopenharmony_ci}; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic struct lb_port_priv *get_lb_port_priv(struct team_port *port) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci return (struct lb_port_priv *) &port->mode_priv; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci#define LB_HTPM_PORT_BY_HASH(lp_priv, hash) \ 938c2ecf20Sopenharmony_ci (lb_priv)->ex->tx_hash_to_port_mapping[hash].port 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci#define LB_HTPM_OPT_INST_INFO_BY_HASH(lp_priv, hash) \ 968c2ecf20Sopenharmony_ci (lb_priv)->ex->tx_hash_to_port_mapping[hash].opt_inst_info 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic void lb_tx_hash_to_port_mapping_null_port(struct team *team, 998c2ecf20Sopenharmony_ci struct team_port *port) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct lb_priv *lb_priv = get_lb_priv(team); 1028c2ecf20Sopenharmony_ci bool changed = false; 1038c2ecf20Sopenharmony_ci int i; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci for (i = 0; i < LB_TX_HASHTABLE_SIZE; i++) { 1068c2ecf20Sopenharmony_ci struct lb_port_mapping *pm; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci pm = &lb_priv->ex->tx_hash_to_port_mapping[i]; 1098c2ecf20Sopenharmony_ci if (rcu_access_pointer(pm->port) == port) { 1108c2ecf20Sopenharmony_ci RCU_INIT_POINTER(pm->port, NULL); 1118c2ecf20Sopenharmony_ci team_option_inst_set_change(pm->opt_inst_info); 1128c2ecf20Sopenharmony_ci changed = true; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci if (changed) 1168c2ecf20Sopenharmony_ci team_options_change_check(team); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci/* Basic tx selection based solely by hash */ 1208c2ecf20Sopenharmony_cistatic struct team_port *lb_hash_select_tx_port(struct team *team, 1218c2ecf20Sopenharmony_ci struct lb_priv *lb_priv, 1228c2ecf20Sopenharmony_ci struct sk_buff *skb, 1238c2ecf20Sopenharmony_ci unsigned char hash) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci int port_index = team_num_to_port_index(team, hash); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci return team_get_port_by_index_rcu(team, port_index); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci/* Hash to port mapping select tx port */ 1318c2ecf20Sopenharmony_cistatic struct team_port *lb_htpm_select_tx_port(struct team *team, 1328c2ecf20Sopenharmony_ci struct lb_priv *lb_priv, 1338c2ecf20Sopenharmony_ci struct sk_buff *skb, 1348c2ecf20Sopenharmony_ci unsigned char hash) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct team_port *port; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci port = rcu_dereference_bh(LB_HTPM_PORT_BY_HASH(lb_priv, hash)); 1398c2ecf20Sopenharmony_ci if (likely(port)) 1408c2ecf20Sopenharmony_ci return port; 1418c2ecf20Sopenharmony_ci /* If no valid port in the table, fall back to simple hash */ 1428c2ecf20Sopenharmony_ci return lb_hash_select_tx_port(team, lb_priv, skb, hash); 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistruct lb_select_tx_port { 1468c2ecf20Sopenharmony_ci char *name; 1478c2ecf20Sopenharmony_ci lb_select_tx_port_func_t *func; 1488c2ecf20Sopenharmony_ci}; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic const struct lb_select_tx_port lb_select_tx_port_list[] = { 1518c2ecf20Sopenharmony_ci { 1528c2ecf20Sopenharmony_ci .name = "hash", 1538c2ecf20Sopenharmony_ci .func = lb_hash_select_tx_port, 1548c2ecf20Sopenharmony_ci }, 1558c2ecf20Sopenharmony_ci { 1568c2ecf20Sopenharmony_ci .name = "hash_to_port_mapping", 1578c2ecf20Sopenharmony_ci .func = lb_htpm_select_tx_port, 1588c2ecf20Sopenharmony_ci }, 1598c2ecf20Sopenharmony_ci}; 1608c2ecf20Sopenharmony_ci#define LB_SELECT_TX_PORT_LIST_COUNT ARRAY_SIZE(lb_select_tx_port_list) 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic char *lb_select_tx_port_get_name(lb_select_tx_port_func_t *func) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci int i; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci for (i = 0; i < LB_SELECT_TX_PORT_LIST_COUNT; i++) { 1678c2ecf20Sopenharmony_ci const struct lb_select_tx_port *item; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci item = &lb_select_tx_port_list[i]; 1708c2ecf20Sopenharmony_ci if (item->func == func) 1718c2ecf20Sopenharmony_ci return item->name; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci return NULL; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic lb_select_tx_port_func_t *lb_select_tx_port_get_func(const char *name) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci int i; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci for (i = 0; i < LB_SELECT_TX_PORT_LIST_COUNT; i++) { 1818c2ecf20Sopenharmony_ci const struct lb_select_tx_port *item; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci item = &lb_select_tx_port_list[i]; 1848c2ecf20Sopenharmony_ci if (!strcmp(item->name, name)) 1858c2ecf20Sopenharmony_ci return item->func; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci return NULL; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic unsigned int lb_get_skb_hash(struct lb_priv *lb_priv, 1918c2ecf20Sopenharmony_ci struct sk_buff *skb) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct bpf_prog *fp; 1948c2ecf20Sopenharmony_ci uint32_t lhash; 1958c2ecf20Sopenharmony_ci unsigned char *c; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci fp = rcu_dereference_bh(lb_priv->fp); 1988c2ecf20Sopenharmony_ci if (unlikely(!fp)) 1998c2ecf20Sopenharmony_ci return 0; 2008c2ecf20Sopenharmony_ci lhash = BPF_PROG_RUN(fp, skb); 2018c2ecf20Sopenharmony_ci c = (char *) &lhash; 2028c2ecf20Sopenharmony_ci return c[0] ^ c[1] ^ c[2] ^ c[3]; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic void lb_update_tx_stats(unsigned int tx_bytes, struct lb_priv *lb_priv, 2068c2ecf20Sopenharmony_ci struct lb_port_priv *lb_port_priv, 2078c2ecf20Sopenharmony_ci unsigned char hash) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct lb_pcpu_stats *pcpu_stats; 2108c2ecf20Sopenharmony_ci struct lb_stats *port_stats; 2118c2ecf20Sopenharmony_ci struct lb_stats *hash_stats; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci pcpu_stats = this_cpu_ptr(lb_priv->pcpu_stats); 2148c2ecf20Sopenharmony_ci port_stats = this_cpu_ptr(lb_port_priv->pcpu_stats); 2158c2ecf20Sopenharmony_ci hash_stats = &pcpu_stats->hash_stats[hash]; 2168c2ecf20Sopenharmony_ci u64_stats_update_begin(&pcpu_stats->syncp); 2178c2ecf20Sopenharmony_ci port_stats->tx_bytes += tx_bytes; 2188c2ecf20Sopenharmony_ci hash_stats->tx_bytes += tx_bytes; 2198c2ecf20Sopenharmony_ci u64_stats_update_end(&pcpu_stats->syncp); 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic bool lb_transmit(struct team *team, struct sk_buff *skb) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci struct lb_priv *lb_priv = get_lb_priv(team); 2258c2ecf20Sopenharmony_ci lb_select_tx_port_func_t *select_tx_port_func; 2268c2ecf20Sopenharmony_ci struct team_port *port; 2278c2ecf20Sopenharmony_ci unsigned char hash; 2288c2ecf20Sopenharmony_ci unsigned int tx_bytes = skb->len; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci hash = lb_get_skb_hash(lb_priv, skb); 2318c2ecf20Sopenharmony_ci select_tx_port_func = rcu_dereference_bh(lb_priv->select_tx_port_func); 2328c2ecf20Sopenharmony_ci port = select_tx_port_func(team, lb_priv, skb, hash); 2338c2ecf20Sopenharmony_ci if (unlikely(!port)) 2348c2ecf20Sopenharmony_ci goto drop; 2358c2ecf20Sopenharmony_ci if (team_dev_queue_xmit(team, port, skb)) 2368c2ecf20Sopenharmony_ci return false; 2378c2ecf20Sopenharmony_ci lb_update_tx_stats(tx_bytes, lb_priv, get_lb_port_priv(port), hash); 2388c2ecf20Sopenharmony_ci return true; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cidrop: 2418c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 2428c2ecf20Sopenharmony_ci return false; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic int lb_bpf_func_get(struct team *team, struct team_gsetter_ctx *ctx) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci struct lb_priv *lb_priv = get_lb_priv(team); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (!lb_priv->ex->orig_fprog) { 2508c2ecf20Sopenharmony_ci ctx->data.bin_val.len = 0; 2518c2ecf20Sopenharmony_ci ctx->data.bin_val.ptr = NULL; 2528c2ecf20Sopenharmony_ci return 0; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci ctx->data.bin_val.len = lb_priv->ex->orig_fprog->len * 2558c2ecf20Sopenharmony_ci sizeof(struct sock_filter); 2568c2ecf20Sopenharmony_ci ctx->data.bin_val.ptr = lb_priv->ex->orig_fprog->filter; 2578c2ecf20Sopenharmony_ci return 0; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic int __fprog_create(struct sock_fprog_kern **pfprog, u32 data_len, 2618c2ecf20Sopenharmony_ci const void *data) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci struct sock_fprog_kern *fprog; 2648c2ecf20Sopenharmony_ci struct sock_filter *filter = (struct sock_filter *) data; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (data_len % sizeof(struct sock_filter)) 2678c2ecf20Sopenharmony_ci return -EINVAL; 2688c2ecf20Sopenharmony_ci fprog = kmalloc(sizeof(*fprog), GFP_KERNEL); 2698c2ecf20Sopenharmony_ci if (!fprog) 2708c2ecf20Sopenharmony_ci return -ENOMEM; 2718c2ecf20Sopenharmony_ci fprog->filter = kmemdup(filter, data_len, GFP_KERNEL); 2728c2ecf20Sopenharmony_ci if (!fprog->filter) { 2738c2ecf20Sopenharmony_ci kfree(fprog); 2748c2ecf20Sopenharmony_ci return -ENOMEM; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci fprog->len = data_len / sizeof(struct sock_filter); 2778c2ecf20Sopenharmony_ci *pfprog = fprog; 2788c2ecf20Sopenharmony_ci return 0; 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic void __fprog_destroy(struct sock_fprog_kern *fprog) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci kfree(fprog->filter); 2848c2ecf20Sopenharmony_ci kfree(fprog); 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic int lb_bpf_func_set(struct team *team, struct team_gsetter_ctx *ctx) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci struct lb_priv *lb_priv = get_lb_priv(team); 2908c2ecf20Sopenharmony_ci struct bpf_prog *fp = NULL; 2918c2ecf20Sopenharmony_ci struct bpf_prog *orig_fp = NULL; 2928c2ecf20Sopenharmony_ci struct sock_fprog_kern *fprog = NULL; 2938c2ecf20Sopenharmony_ci int err; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if (ctx->data.bin_val.len) { 2968c2ecf20Sopenharmony_ci err = __fprog_create(&fprog, ctx->data.bin_val.len, 2978c2ecf20Sopenharmony_ci ctx->data.bin_val.ptr); 2988c2ecf20Sopenharmony_ci if (err) 2998c2ecf20Sopenharmony_ci return err; 3008c2ecf20Sopenharmony_ci err = bpf_prog_create(&fp, fprog); 3018c2ecf20Sopenharmony_ci if (err) { 3028c2ecf20Sopenharmony_ci __fprog_destroy(fprog); 3038c2ecf20Sopenharmony_ci return err; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (lb_priv->ex->orig_fprog) { 3088c2ecf20Sopenharmony_ci /* Clear old filter data */ 3098c2ecf20Sopenharmony_ci __fprog_destroy(lb_priv->ex->orig_fprog); 3108c2ecf20Sopenharmony_ci orig_fp = rcu_dereference_protected(lb_priv->fp, 3118c2ecf20Sopenharmony_ci lockdep_is_held(&team->lock)); 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci rcu_assign_pointer(lb_priv->fp, fp); 3158c2ecf20Sopenharmony_ci lb_priv->ex->orig_fprog = fprog; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci if (orig_fp) { 3188c2ecf20Sopenharmony_ci synchronize_rcu(); 3198c2ecf20Sopenharmony_ci bpf_prog_destroy(orig_fp); 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci return 0; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic void lb_bpf_func_free(struct team *team) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci struct lb_priv *lb_priv = get_lb_priv(team); 3278c2ecf20Sopenharmony_ci struct bpf_prog *fp; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci if (!lb_priv->ex->orig_fprog) 3308c2ecf20Sopenharmony_ci return; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci __fprog_destroy(lb_priv->ex->orig_fprog); 3338c2ecf20Sopenharmony_ci fp = rcu_dereference_protected(lb_priv->fp, 3348c2ecf20Sopenharmony_ci lockdep_is_held(&team->lock)); 3358c2ecf20Sopenharmony_ci bpf_prog_destroy(fp); 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic int lb_tx_method_get(struct team *team, struct team_gsetter_ctx *ctx) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci struct lb_priv *lb_priv = get_lb_priv(team); 3418c2ecf20Sopenharmony_ci lb_select_tx_port_func_t *func; 3428c2ecf20Sopenharmony_ci char *name; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci func = rcu_dereference_protected(lb_priv->select_tx_port_func, 3458c2ecf20Sopenharmony_ci lockdep_is_held(&team->lock)); 3468c2ecf20Sopenharmony_ci name = lb_select_tx_port_get_name(func); 3478c2ecf20Sopenharmony_ci BUG_ON(!name); 3488c2ecf20Sopenharmony_ci ctx->data.str_val = name; 3498c2ecf20Sopenharmony_ci return 0; 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic int lb_tx_method_set(struct team *team, struct team_gsetter_ctx *ctx) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci struct lb_priv *lb_priv = get_lb_priv(team); 3558c2ecf20Sopenharmony_ci lb_select_tx_port_func_t *func; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci func = lb_select_tx_port_get_func(ctx->data.str_val); 3588c2ecf20Sopenharmony_ci if (!func) 3598c2ecf20Sopenharmony_ci return -EINVAL; 3608c2ecf20Sopenharmony_ci rcu_assign_pointer(lb_priv->select_tx_port_func, func); 3618c2ecf20Sopenharmony_ci return 0; 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic int lb_tx_hash_to_port_mapping_init(struct team *team, 3658c2ecf20Sopenharmony_ci struct team_option_inst_info *info) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci struct lb_priv *lb_priv = get_lb_priv(team); 3688c2ecf20Sopenharmony_ci unsigned char hash = info->array_index; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci LB_HTPM_OPT_INST_INFO_BY_HASH(lb_priv, hash) = info; 3718c2ecf20Sopenharmony_ci return 0; 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic int lb_tx_hash_to_port_mapping_get(struct team *team, 3758c2ecf20Sopenharmony_ci struct team_gsetter_ctx *ctx) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci struct lb_priv *lb_priv = get_lb_priv(team); 3788c2ecf20Sopenharmony_ci struct team_port *port; 3798c2ecf20Sopenharmony_ci unsigned char hash = ctx->info->array_index; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci port = LB_HTPM_PORT_BY_HASH(lb_priv, hash); 3828c2ecf20Sopenharmony_ci ctx->data.u32_val = port ? port->dev->ifindex : 0; 3838c2ecf20Sopenharmony_ci return 0; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic int lb_tx_hash_to_port_mapping_set(struct team *team, 3878c2ecf20Sopenharmony_ci struct team_gsetter_ctx *ctx) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci struct lb_priv *lb_priv = get_lb_priv(team); 3908c2ecf20Sopenharmony_ci struct team_port *port; 3918c2ecf20Sopenharmony_ci unsigned char hash = ctx->info->array_index; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci list_for_each_entry(port, &team->port_list, list) { 3948c2ecf20Sopenharmony_ci if (ctx->data.u32_val == port->dev->ifindex && 3958c2ecf20Sopenharmony_ci team_port_enabled(port)) { 3968c2ecf20Sopenharmony_ci rcu_assign_pointer(LB_HTPM_PORT_BY_HASH(lb_priv, hash), 3978c2ecf20Sopenharmony_ci port); 3988c2ecf20Sopenharmony_ci return 0; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci return -ENODEV; 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic int lb_hash_stats_init(struct team *team, 4058c2ecf20Sopenharmony_ci struct team_option_inst_info *info) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci struct lb_priv *lb_priv = get_lb_priv(team); 4088c2ecf20Sopenharmony_ci unsigned char hash = info->array_index; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci lb_priv->ex->stats.info[hash].opt_inst_info = info; 4118c2ecf20Sopenharmony_ci return 0; 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic int lb_hash_stats_get(struct team *team, struct team_gsetter_ctx *ctx) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci struct lb_priv *lb_priv = get_lb_priv(team); 4178c2ecf20Sopenharmony_ci unsigned char hash = ctx->info->array_index; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci ctx->data.bin_val.ptr = &lb_priv->ex->stats.info[hash].stats; 4208c2ecf20Sopenharmony_ci ctx->data.bin_val.len = sizeof(struct lb_stats); 4218c2ecf20Sopenharmony_ci return 0; 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic int lb_port_stats_init(struct team *team, 4258c2ecf20Sopenharmony_ci struct team_option_inst_info *info) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci struct team_port *port = info->port; 4288c2ecf20Sopenharmony_ci struct lb_port_priv *lb_port_priv = get_lb_port_priv(port); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci lb_port_priv->stats_info.opt_inst_info = info; 4318c2ecf20Sopenharmony_ci return 0; 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic int lb_port_stats_get(struct team *team, struct team_gsetter_ctx *ctx) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci struct team_port *port = ctx->info->port; 4378c2ecf20Sopenharmony_ci struct lb_port_priv *lb_port_priv = get_lb_port_priv(port); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci ctx->data.bin_val.ptr = &lb_port_priv->stats_info.stats; 4408c2ecf20Sopenharmony_ci ctx->data.bin_val.len = sizeof(struct lb_stats); 4418c2ecf20Sopenharmony_ci return 0; 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic void __lb_stats_info_refresh_prepare(struct lb_stats_info *s_info) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci memcpy(&s_info->last_stats, &s_info->stats, sizeof(struct lb_stats)); 4478c2ecf20Sopenharmony_ci memset(&s_info->stats, 0, sizeof(struct lb_stats)); 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistatic bool __lb_stats_info_refresh_check(struct lb_stats_info *s_info, 4518c2ecf20Sopenharmony_ci struct team *team) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci if (memcmp(&s_info->last_stats, &s_info->stats, 4548c2ecf20Sopenharmony_ci sizeof(struct lb_stats))) { 4558c2ecf20Sopenharmony_ci team_option_inst_set_change(s_info->opt_inst_info); 4568c2ecf20Sopenharmony_ci return true; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci return false; 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_cistatic void __lb_one_cpu_stats_add(struct lb_stats *acc_stats, 4628c2ecf20Sopenharmony_ci struct lb_stats *cpu_stats, 4638c2ecf20Sopenharmony_ci struct u64_stats_sync *syncp) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci unsigned int start; 4668c2ecf20Sopenharmony_ci struct lb_stats tmp; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci do { 4698c2ecf20Sopenharmony_ci start = u64_stats_fetch_begin_irq(syncp); 4708c2ecf20Sopenharmony_ci tmp.tx_bytes = cpu_stats->tx_bytes; 4718c2ecf20Sopenharmony_ci } while (u64_stats_fetch_retry_irq(syncp, start)); 4728c2ecf20Sopenharmony_ci acc_stats->tx_bytes += tmp.tx_bytes; 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic void lb_stats_refresh(struct work_struct *work) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci struct team *team; 4788c2ecf20Sopenharmony_ci struct lb_priv *lb_priv; 4798c2ecf20Sopenharmony_ci struct lb_priv_ex *lb_priv_ex; 4808c2ecf20Sopenharmony_ci struct lb_pcpu_stats *pcpu_stats; 4818c2ecf20Sopenharmony_ci struct lb_stats *stats; 4828c2ecf20Sopenharmony_ci struct lb_stats_info *s_info; 4838c2ecf20Sopenharmony_ci struct team_port *port; 4848c2ecf20Sopenharmony_ci bool changed = false; 4858c2ecf20Sopenharmony_ci int i; 4868c2ecf20Sopenharmony_ci int j; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci lb_priv_ex = container_of(work, struct lb_priv_ex, 4898c2ecf20Sopenharmony_ci stats.refresh_dw.work); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci team = lb_priv_ex->team; 4928c2ecf20Sopenharmony_ci lb_priv = get_lb_priv(team); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci if (!mutex_trylock(&team->lock)) { 4958c2ecf20Sopenharmony_ci schedule_delayed_work(&lb_priv_ex->stats.refresh_dw, 0); 4968c2ecf20Sopenharmony_ci return; 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci for (j = 0; j < LB_TX_HASHTABLE_SIZE; j++) { 5008c2ecf20Sopenharmony_ci s_info = &lb_priv->ex->stats.info[j]; 5018c2ecf20Sopenharmony_ci __lb_stats_info_refresh_prepare(s_info); 5028c2ecf20Sopenharmony_ci for_each_possible_cpu(i) { 5038c2ecf20Sopenharmony_ci pcpu_stats = per_cpu_ptr(lb_priv->pcpu_stats, i); 5048c2ecf20Sopenharmony_ci stats = &pcpu_stats->hash_stats[j]; 5058c2ecf20Sopenharmony_ci __lb_one_cpu_stats_add(&s_info->stats, stats, 5068c2ecf20Sopenharmony_ci &pcpu_stats->syncp); 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci changed |= __lb_stats_info_refresh_check(s_info, team); 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci list_for_each_entry(port, &team->port_list, list) { 5128c2ecf20Sopenharmony_ci struct lb_port_priv *lb_port_priv = get_lb_port_priv(port); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci s_info = &lb_port_priv->stats_info; 5158c2ecf20Sopenharmony_ci __lb_stats_info_refresh_prepare(s_info); 5168c2ecf20Sopenharmony_ci for_each_possible_cpu(i) { 5178c2ecf20Sopenharmony_ci pcpu_stats = per_cpu_ptr(lb_priv->pcpu_stats, i); 5188c2ecf20Sopenharmony_ci stats = per_cpu_ptr(lb_port_priv->pcpu_stats, i); 5198c2ecf20Sopenharmony_ci __lb_one_cpu_stats_add(&s_info->stats, stats, 5208c2ecf20Sopenharmony_ci &pcpu_stats->syncp); 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci changed |= __lb_stats_info_refresh_check(s_info, team); 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci if (changed) 5268c2ecf20Sopenharmony_ci team_options_change_check(team); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci schedule_delayed_work(&lb_priv_ex->stats.refresh_dw, 5298c2ecf20Sopenharmony_ci (lb_priv_ex->stats.refresh_interval * HZ) / 10); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci mutex_unlock(&team->lock); 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic int lb_stats_refresh_interval_get(struct team *team, 5358c2ecf20Sopenharmony_ci struct team_gsetter_ctx *ctx) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci struct lb_priv *lb_priv = get_lb_priv(team); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci ctx->data.u32_val = lb_priv->ex->stats.refresh_interval; 5408c2ecf20Sopenharmony_ci return 0; 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_cistatic int lb_stats_refresh_interval_set(struct team *team, 5448c2ecf20Sopenharmony_ci struct team_gsetter_ctx *ctx) 5458c2ecf20Sopenharmony_ci{ 5468c2ecf20Sopenharmony_ci struct lb_priv *lb_priv = get_lb_priv(team); 5478c2ecf20Sopenharmony_ci unsigned int interval; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci interval = ctx->data.u32_val; 5508c2ecf20Sopenharmony_ci if (lb_priv->ex->stats.refresh_interval == interval) 5518c2ecf20Sopenharmony_ci return 0; 5528c2ecf20Sopenharmony_ci lb_priv->ex->stats.refresh_interval = interval; 5538c2ecf20Sopenharmony_ci if (interval) 5548c2ecf20Sopenharmony_ci schedule_delayed_work(&lb_priv->ex->stats.refresh_dw, 0); 5558c2ecf20Sopenharmony_ci else 5568c2ecf20Sopenharmony_ci cancel_delayed_work(&lb_priv->ex->stats.refresh_dw); 5578c2ecf20Sopenharmony_ci return 0; 5588c2ecf20Sopenharmony_ci} 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_cistatic const struct team_option lb_options[] = { 5618c2ecf20Sopenharmony_ci { 5628c2ecf20Sopenharmony_ci .name = "bpf_hash_func", 5638c2ecf20Sopenharmony_ci .type = TEAM_OPTION_TYPE_BINARY, 5648c2ecf20Sopenharmony_ci .getter = lb_bpf_func_get, 5658c2ecf20Sopenharmony_ci .setter = lb_bpf_func_set, 5668c2ecf20Sopenharmony_ci }, 5678c2ecf20Sopenharmony_ci { 5688c2ecf20Sopenharmony_ci .name = "lb_tx_method", 5698c2ecf20Sopenharmony_ci .type = TEAM_OPTION_TYPE_STRING, 5708c2ecf20Sopenharmony_ci .getter = lb_tx_method_get, 5718c2ecf20Sopenharmony_ci .setter = lb_tx_method_set, 5728c2ecf20Sopenharmony_ci }, 5738c2ecf20Sopenharmony_ci { 5748c2ecf20Sopenharmony_ci .name = "lb_tx_hash_to_port_mapping", 5758c2ecf20Sopenharmony_ci .array_size = LB_TX_HASHTABLE_SIZE, 5768c2ecf20Sopenharmony_ci .type = TEAM_OPTION_TYPE_U32, 5778c2ecf20Sopenharmony_ci .init = lb_tx_hash_to_port_mapping_init, 5788c2ecf20Sopenharmony_ci .getter = lb_tx_hash_to_port_mapping_get, 5798c2ecf20Sopenharmony_ci .setter = lb_tx_hash_to_port_mapping_set, 5808c2ecf20Sopenharmony_ci }, 5818c2ecf20Sopenharmony_ci { 5828c2ecf20Sopenharmony_ci .name = "lb_hash_stats", 5838c2ecf20Sopenharmony_ci .array_size = LB_TX_HASHTABLE_SIZE, 5848c2ecf20Sopenharmony_ci .type = TEAM_OPTION_TYPE_BINARY, 5858c2ecf20Sopenharmony_ci .init = lb_hash_stats_init, 5868c2ecf20Sopenharmony_ci .getter = lb_hash_stats_get, 5878c2ecf20Sopenharmony_ci }, 5888c2ecf20Sopenharmony_ci { 5898c2ecf20Sopenharmony_ci .name = "lb_port_stats", 5908c2ecf20Sopenharmony_ci .per_port = true, 5918c2ecf20Sopenharmony_ci .type = TEAM_OPTION_TYPE_BINARY, 5928c2ecf20Sopenharmony_ci .init = lb_port_stats_init, 5938c2ecf20Sopenharmony_ci .getter = lb_port_stats_get, 5948c2ecf20Sopenharmony_ci }, 5958c2ecf20Sopenharmony_ci { 5968c2ecf20Sopenharmony_ci .name = "lb_stats_refresh_interval", 5978c2ecf20Sopenharmony_ci .type = TEAM_OPTION_TYPE_U32, 5988c2ecf20Sopenharmony_ci .getter = lb_stats_refresh_interval_get, 5998c2ecf20Sopenharmony_ci .setter = lb_stats_refresh_interval_set, 6008c2ecf20Sopenharmony_ci }, 6018c2ecf20Sopenharmony_ci}; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cistatic int lb_init(struct team *team) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci struct lb_priv *lb_priv = get_lb_priv(team); 6068c2ecf20Sopenharmony_ci lb_select_tx_port_func_t *func; 6078c2ecf20Sopenharmony_ci int i, err; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci /* set default tx port selector */ 6108c2ecf20Sopenharmony_ci func = lb_select_tx_port_get_func("hash"); 6118c2ecf20Sopenharmony_ci BUG_ON(!func); 6128c2ecf20Sopenharmony_ci rcu_assign_pointer(lb_priv->select_tx_port_func, func); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci lb_priv->ex = kzalloc(sizeof(*lb_priv->ex), GFP_KERNEL); 6158c2ecf20Sopenharmony_ci if (!lb_priv->ex) 6168c2ecf20Sopenharmony_ci return -ENOMEM; 6178c2ecf20Sopenharmony_ci lb_priv->ex->team = team; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci lb_priv->pcpu_stats = alloc_percpu(struct lb_pcpu_stats); 6208c2ecf20Sopenharmony_ci if (!lb_priv->pcpu_stats) { 6218c2ecf20Sopenharmony_ci err = -ENOMEM; 6228c2ecf20Sopenharmony_ci goto err_alloc_pcpu_stats; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci for_each_possible_cpu(i) { 6268c2ecf20Sopenharmony_ci struct lb_pcpu_stats *team_lb_stats; 6278c2ecf20Sopenharmony_ci team_lb_stats = per_cpu_ptr(lb_priv->pcpu_stats, i); 6288c2ecf20Sopenharmony_ci u64_stats_init(&team_lb_stats->syncp); 6298c2ecf20Sopenharmony_ci } 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&lb_priv->ex->stats.refresh_dw, lb_stats_refresh); 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci err = team_options_register(team, lb_options, ARRAY_SIZE(lb_options)); 6358c2ecf20Sopenharmony_ci if (err) 6368c2ecf20Sopenharmony_ci goto err_options_register; 6378c2ecf20Sopenharmony_ci return 0; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_cierr_options_register: 6408c2ecf20Sopenharmony_ci free_percpu(lb_priv->pcpu_stats); 6418c2ecf20Sopenharmony_cierr_alloc_pcpu_stats: 6428c2ecf20Sopenharmony_ci kfree(lb_priv->ex); 6438c2ecf20Sopenharmony_ci return err; 6448c2ecf20Sopenharmony_ci} 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_cistatic void lb_exit(struct team *team) 6478c2ecf20Sopenharmony_ci{ 6488c2ecf20Sopenharmony_ci struct lb_priv *lb_priv = get_lb_priv(team); 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci team_options_unregister(team, lb_options, 6518c2ecf20Sopenharmony_ci ARRAY_SIZE(lb_options)); 6528c2ecf20Sopenharmony_ci lb_bpf_func_free(team); 6538c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&lb_priv->ex->stats.refresh_dw); 6548c2ecf20Sopenharmony_ci free_percpu(lb_priv->pcpu_stats); 6558c2ecf20Sopenharmony_ci kfree(lb_priv->ex); 6568c2ecf20Sopenharmony_ci} 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_cistatic int lb_port_enter(struct team *team, struct team_port *port) 6598c2ecf20Sopenharmony_ci{ 6608c2ecf20Sopenharmony_ci struct lb_port_priv *lb_port_priv = get_lb_port_priv(port); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci lb_port_priv->pcpu_stats = alloc_percpu(struct lb_stats); 6638c2ecf20Sopenharmony_ci if (!lb_port_priv->pcpu_stats) 6648c2ecf20Sopenharmony_ci return -ENOMEM; 6658c2ecf20Sopenharmony_ci return 0; 6668c2ecf20Sopenharmony_ci} 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cistatic void lb_port_leave(struct team *team, struct team_port *port) 6698c2ecf20Sopenharmony_ci{ 6708c2ecf20Sopenharmony_ci struct lb_port_priv *lb_port_priv = get_lb_port_priv(port); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci free_percpu(lb_port_priv->pcpu_stats); 6738c2ecf20Sopenharmony_ci} 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_cistatic void lb_port_disabled(struct team *team, struct team_port *port) 6768c2ecf20Sopenharmony_ci{ 6778c2ecf20Sopenharmony_ci lb_tx_hash_to_port_mapping_null_port(team, port); 6788c2ecf20Sopenharmony_ci} 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_cistatic const struct team_mode_ops lb_mode_ops = { 6818c2ecf20Sopenharmony_ci .init = lb_init, 6828c2ecf20Sopenharmony_ci .exit = lb_exit, 6838c2ecf20Sopenharmony_ci .port_enter = lb_port_enter, 6848c2ecf20Sopenharmony_ci .port_leave = lb_port_leave, 6858c2ecf20Sopenharmony_ci .port_disabled = lb_port_disabled, 6868c2ecf20Sopenharmony_ci .receive = lb_receive, 6878c2ecf20Sopenharmony_ci .transmit = lb_transmit, 6888c2ecf20Sopenharmony_ci}; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_cistatic const struct team_mode lb_mode = { 6918c2ecf20Sopenharmony_ci .kind = "loadbalance", 6928c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 6938c2ecf20Sopenharmony_ci .priv_size = sizeof(struct lb_priv), 6948c2ecf20Sopenharmony_ci .port_priv_size = sizeof(struct lb_port_priv), 6958c2ecf20Sopenharmony_ci .ops = &lb_mode_ops, 6968c2ecf20Sopenharmony_ci .lag_tx_type = NETDEV_LAG_TX_TYPE_HASH, 6978c2ecf20Sopenharmony_ci}; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_cistatic int __init lb_init_module(void) 7008c2ecf20Sopenharmony_ci{ 7018c2ecf20Sopenharmony_ci return team_mode_register(&lb_mode); 7028c2ecf20Sopenharmony_ci} 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_cistatic void __exit lb_cleanup_module(void) 7058c2ecf20Sopenharmony_ci{ 7068c2ecf20Sopenharmony_ci team_mode_unregister(&lb_mode); 7078c2ecf20Sopenharmony_ci} 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_cimodule_init(lb_init_module); 7108c2ecf20Sopenharmony_cimodule_exit(lb_cleanup_module); 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 7138c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jiri Pirko <jpirko@redhat.com>"); 7148c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Load-balancing mode for team"); 7158c2ecf20Sopenharmony_ciMODULE_ALIAS_TEAM_MODE("loadbalance"); 716