18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * drivers/net/team/team_mode_activebackup.c - Active-backup mode for team
48c2ecf20Sopenharmony_ci * Copyright (c) 2011 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 <net/rtnetlink.h>
148c2ecf20Sopenharmony_ci#include <linux/if_team.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cistruct ab_priv {
178c2ecf20Sopenharmony_ci	struct team_port __rcu *active_port;
188c2ecf20Sopenharmony_ci	struct team_option_inst_info *ap_opt_inst_info;
198c2ecf20Sopenharmony_ci};
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic struct ab_priv *ab_priv(struct team *team)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	return (struct ab_priv *) &team->mode_priv;
248c2ecf20Sopenharmony_ci}
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic rx_handler_result_t ab_receive(struct team *team, struct team_port *port,
278c2ecf20Sopenharmony_ci				      struct sk_buff *skb) {
288c2ecf20Sopenharmony_ci	struct team_port *active_port;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	active_port = rcu_dereference(ab_priv(team)->active_port);
318c2ecf20Sopenharmony_ci	if (active_port != port)
328c2ecf20Sopenharmony_ci		return RX_HANDLER_EXACT;
338c2ecf20Sopenharmony_ci	return RX_HANDLER_ANOTHER;
348c2ecf20Sopenharmony_ci}
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic bool ab_transmit(struct team *team, struct sk_buff *skb)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	struct team_port *active_port;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	active_port = rcu_dereference_bh(ab_priv(team)->active_port);
418c2ecf20Sopenharmony_ci	if (unlikely(!active_port))
428c2ecf20Sopenharmony_ci		goto drop;
438c2ecf20Sopenharmony_ci	if (team_dev_queue_xmit(team, active_port, skb))
448c2ecf20Sopenharmony_ci		return false;
458c2ecf20Sopenharmony_ci	return true;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cidrop:
488c2ecf20Sopenharmony_ci	dev_kfree_skb_any(skb);
498c2ecf20Sopenharmony_ci	return false;
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic void ab_port_leave(struct team *team, struct team_port *port)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	if (ab_priv(team)->active_port == port) {
558c2ecf20Sopenharmony_ci		RCU_INIT_POINTER(ab_priv(team)->active_port, NULL);
568c2ecf20Sopenharmony_ci		team_option_inst_set_change(ab_priv(team)->ap_opt_inst_info);
578c2ecf20Sopenharmony_ci	}
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic int ab_active_port_init(struct team *team,
618c2ecf20Sopenharmony_ci			       struct team_option_inst_info *info)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	ab_priv(team)->ap_opt_inst_info = info;
648c2ecf20Sopenharmony_ci	return 0;
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic int ab_active_port_get(struct team *team, struct team_gsetter_ctx *ctx)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	struct team_port *active_port;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	active_port = rcu_dereference_protected(ab_priv(team)->active_port,
728c2ecf20Sopenharmony_ci						lockdep_is_held(&team->lock));
738c2ecf20Sopenharmony_ci	if (active_port)
748c2ecf20Sopenharmony_ci		ctx->data.u32_val = active_port->dev->ifindex;
758c2ecf20Sopenharmony_ci	else
768c2ecf20Sopenharmony_ci		ctx->data.u32_val = 0;
778c2ecf20Sopenharmony_ci	return 0;
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic int ab_active_port_set(struct team *team, struct team_gsetter_ctx *ctx)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	struct team_port *port;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	list_for_each_entry(port, &team->port_list, list) {
858c2ecf20Sopenharmony_ci		if (port->dev->ifindex == ctx->data.u32_val) {
868c2ecf20Sopenharmony_ci			rcu_assign_pointer(ab_priv(team)->active_port, port);
878c2ecf20Sopenharmony_ci			return 0;
888c2ecf20Sopenharmony_ci		}
898c2ecf20Sopenharmony_ci	}
908c2ecf20Sopenharmony_ci	return -ENOENT;
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic const struct team_option ab_options[] = {
948c2ecf20Sopenharmony_ci	{
958c2ecf20Sopenharmony_ci		.name = "activeport",
968c2ecf20Sopenharmony_ci		.type = TEAM_OPTION_TYPE_U32,
978c2ecf20Sopenharmony_ci		.init = ab_active_port_init,
988c2ecf20Sopenharmony_ci		.getter = ab_active_port_get,
998c2ecf20Sopenharmony_ci		.setter = ab_active_port_set,
1008c2ecf20Sopenharmony_ci	},
1018c2ecf20Sopenharmony_ci};
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic int ab_init(struct team *team)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	return team_options_register(team, ab_options, ARRAY_SIZE(ab_options));
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic void ab_exit(struct team *team)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	team_options_unregister(team, ab_options, ARRAY_SIZE(ab_options));
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic const struct team_mode_ops ab_mode_ops = {
1148c2ecf20Sopenharmony_ci	.init			= ab_init,
1158c2ecf20Sopenharmony_ci	.exit			= ab_exit,
1168c2ecf20Sopenharmony_ci	.receive		= ab_receive,
1178c2ecf20Sopenharmony_ci	.transmit		= ab_transmit,
1188c2ecf20Sopenharmony_ci	.port_leave		= ab_port_leave,
1198c2ecf20Sopenharmony_ci};
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic const struct team_mode ab_mode = {
1228c2ecf20Sopenharmony_ci	.kind		= "activebackup",
1238c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
1248c2ecf20Sopenharmony_ci	.priv_size	= sizeof(struct ab_priv),
1258c2ecf20Sopenharmony_ci	.ops		= &ab_mode_ops,
1268c2ecf20Sopenharmony_ci	.lag_tx_type	= NETDEV_LAG_TX_TYPE_ACTIVEBACKUP,
1278c2ecf20Sopenharmony_ci};
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic int __init ab_init_module(void)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	return team_mode_register(&ab_mode);
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic void __exit ab_cleanup_module(void)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	team_mode_unregister(&ab_mode);
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cimodule_init(ab_init_module);
1408c2ecf20Sopenharmony_cimodule_exit(ab_cleanup_module);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
1438c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jiri Pirko <jpirko@redhat.com>");
1448c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Active-backup mode for team");
1458c2ecf20Sopenharmony_ciMODULE_ALIAS_TEAM_MODE("activebackup");
146