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