| // SPDX-License-Identifier: GPL-2.0-or-later | 
 | /* | 
 |  * drivers/net/team/team_mode_activebackup.c - Active-backup mode for team | 
 |  * Copyright (c) 2011 Jiri Pirko <jpirko@redhat.com> | 
 |  */ | 
 |  | 
 | #include <linux/kernel.h> | 
 | #include <linux/types.h> | 
 | #include <linux/module.h> | 
 | #include <linux/init.h> | 
 | #include <linux/errno.h> | 
 | #include <linux/netdevice.h> | 
 | #include <net/rtnetlink.h> | 
 | #include <linux/if_team.h> | 
 |  | 
 | struct ab_priv { | 
 | 	struct team_port __rcu *active_port; | 
 | 	struct team_option_inst_info *ap_opt_inst_info; | 
 | }; | 
 |  | 
 | static struct ab_priv *ab_priv(struct team *team) | 
 | { | 
 | 	return (struct ab_priv *) &team->mode_priv; | 
 | } | 
 |  | 
 | static rx_handler_result_t ab_receive(struct team *team, struct team_port *port, | 
 | 				      struct sk_buff *skb) { | 
 | 	struct team_port *active_port; | 
 |  | 
 | 	active_port = rcu_dereference(ab_priv(team)->active_port); | 
 | 	if (active_port != port) | 
 | 		return RX_HANDLER_EXACT; | 
 | 	return RX_HANDLER_ANOTHER; | 
 | } | 
 |  | 
 | static bool ab_transmit(struct team *team, struct sk_buff *skb) | 
 | { | 
 | 	struct team_port *active_port; | 
 |  | 
 | 	active_port = rcu_dereference_bh(ab_priv(team)->active_port); | 
 | 	if (unlikely(!active_port)) | 
 | 		goto drop; | 
 | 	if (team_dev_queue_xmit(team, active_port, skb)) | 
 | 		return false; | 
 | 	return true; | 
 |  | 
 | drop: | 
 | 	dev_kfree_skb_any(skb); | 
 | 	return false; | 
 | } | 
 |  | 
 | static void ab_port_leave(struct team *team, struct team_port *port) | 
 | { | 
 | 	if (ab_priv(team)->active_port == port) { | 
 | 		RCU_INIT_POINTER(ab_priv(team)->active_port, NULL); | 
 | 		team_option_inst_set_change(ab_priv(team)->ap_opt_inst_info); | 
 | 	} | 
 | } | 
 |  | 
 | static void ab_active_port_init(struct team *team, | 
 | 				struct team_option_inst_info *info) | 
 | { | 
 | 	ab_priv(team)->ap_opt_inst_info = info; | 
 | } | 
 |  | 
 | static void ab_active_port_get(struct team *team, struct team_gsetter_ctx *ctx) | 
 | { | 
 | 	struct team_port *active_port; | 
 |  | 
 | 	active_port = rcu_dereference_protected(ab_priv(team)->active_port, | 
 | 						lockdep_is_held(&team->lock)); | 
 | 	if (active_port) | 
 | 		ctx->data.u32_val = active_port->dev->ifindex; | 
 | 	else | 
 | 		ctx->data.u32_val = 0; | 
 | } | 
 |  | 
 | static int ab_active_port_set(struct team *team, struct team_gsetter_ctx *ctx) | 
 | { | 
 | 	struct team_port *port; | 
 |  | 
 | 	list_for_each_entry(port, &team->port_list, list) { | 
 | 		if (port->dev->ifindex == ctx->data.u32_val) { | 
 | 			rcu_assign_pointer(ab_priv(team)->active_port, port); | 
 | 			return 0; | 
 | 		} | 
 | 	} | 
 | 	return -ENOENT; | 
 | } | 
 |  | 
 | static const struct team_option ab_options[] = { | 
 | 	{ | 
 | 		.name = "activeport", | 
 | 		.type = TEAM_OPTION_TYPE_U32, | 
 | 		.init = ab_active_port_init, | 
 | 		.getter = ab_active_port_get, | 
 | 		.setter = ab_active_port_set, | 
 | 	}, | 
 | }; | 
 |  | 
 | static int ab_init(struct team *team) | 
 | { | 
 | 	return team_options_register(team, ab_options, ARRAY_SIZE(ab_options)); | 
 | } | 
 |  | 
 | static void ab_exit(struct team *team) | 
 | { | 
 | 	team_options_unregister(team, ab_options, ARRAY_SIZE(ab_options)); | 
 | } | 
 |  | 
 | static const struct team_mode_ops ab_mode_ops = { | 
 | 	.init			= ab_init, | 
 | 	.exit			= ab_exit, | 
 | 	.receive		= ab_receive, | 
 | 	.transmit		= ab_transmit, | 
 | 	.port_leave		= ab_port_leave, | 
 | }; | 
 |  | 
 | static const struct team_mode ab_mode = { | 
 | 	.kind		= "activebackup", | 
 | 	.owner		= THIS_MODULE, | 
 | 	.priv_size	= sizeof(struct ab_priv), | 
 | 	.ops		= &ab_mode_ops, | 
 | 	.lag_tx_type	= NETDEV_LAG_TX_TYPE_ACTIVEBACKUP, | 
 | }; | 
 |  | 
 | static int __init ab_init_module(void) | 
 | { | 
 | 	return team_mode_register(&ab_mode); | 
 | } | 
 |  | 
 | static void __exit ab_cleanup_module(void) | 
 | { | 
 | 	team_mode_unregister(&ab_mode); | 
 | } | 
 |  | 
 | module_init(ab_init_module); | 
 | module_exit(ab_cleanup_module); | 
 |  | 
 | MODULE_LICENSE("GPL v2"); | 
 | MODULE_AUTHOR("Jiri Pirko <jpirko@redhat.com>"); | 
 | MODULE_DESCRIPTION("Active-backup mode for team"); | 
 | MODULE_ALIAS_TEAM_MODE("activebackup"); |