linux/drivers/net/team/team_mode_activebackup.c
Stanislav Fomichev bfb4fb77f9 team: replace team lock with rtnl lock
syszbot reports various ordering issues for lower instance locks and
team lock. Switch to using rtnl lock for protecting team device,
similar to bonding. Based on the patch by Tetsuo Handa.

Cc: Jiri Pirko <jiri@resnulli.us>
Cc: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Reported-by: syzbot+705c61d60b091ef42c04@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=705c61d60b091ef42c04
Reported-by: syzbot+71fd22ae4b81631e22fd@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=71fd22ae4b81631e22fd
Fixes: 6b1d3c5f67 ("team: grab team lock during team_change_rx_flags")
Link: https://lkml.kernel.org/r/ZoZ2RH9BcahEB9Sb@nanopsycho.orion
Signed-off-by: Stanislav Fomichev <sdf@fomichev.me>
Link: https://patch.msgid.link/20250623153147.3413631-1-sdf@fomichev.me
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2025-06-25 15:23:06 -07:00

142 lines
3.3 KiB
C

// 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 = rtnl_dereference(ab_priv(team)->active_port);
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");