144 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * drivers/net/team/team_mode_activebackup.c - Active-backup mode for team4 * Copyright (c) 2011 Jiri Pirko <jpirko@redhat.com>5 */6 7#include <linux/kernel.h>8#include <linux/types.h>9#include <linux/module.h>10#include <linux/init.h>11#include <linux/errno.h>12#include <linux/netdevice.h>13#include <net/rtnetlink.h>14#include <linux/if_team.h>15 16struct ab_priv {17 struct team_port __rcu *active_port;18 struct team_option_inst_info *ap_opt_inst_info;19};20 21static struct ab_priv *ab_priv(struct team *team)22{23 return (struct ab_priv *) &team->mode_priv;24}25 26static rx_handler_result_t ab_receive(struct team *team, struct team_port *port,27 struct sk_buff *skb) {28 struct team_port *active_port;29 30 active_port = rcu_dereference(ab_priv(team)->active_port);31 if (active_port != port)32 return RX_HANDLER_EXACT;33 return RX_HANDLER_ANOTHER;34}35 36static bool ab_transmit(struct team *team, struct sk_buff *skb)37{38 struct team_port *active_port;39 40 active_port = rcu_dereference_bh(ab_priv(team)->active_port);41 if (unlikely(!active_port))42 goto drop;43 if (team_dev_queue_xmit(team, active_port, skb))44 return false;45 return true;46 47drop:48 dev_kfree_skb_any(skb);49 return false;50}51 52static void ab_port_leave(struct team *team, struct team_port *port)53{54 if (ab_priv(team)->active_port == port) {55 RCU_INIT_POINTER(ab_priv(team)->active_port, NULL);56 team_option_inst_set_change(ab_priv(team)->ap_opt_inst_info);57 }58}59 60static void ab_active_port_init(struct team *team,61 struct team_option_inst_info *info)62{63 ab_priv(team)->ap_opt_inst_info = info;64}65 66static void ab_active_port_get(struct team *team, struct team_gsetter_ctx *ctx)67{68 struct team_port *active_port;69 70 active_port = rcu_dereference_protected(ab_priv(team)->active_port,71 lockdep_is_held(&team->lock));72 if (active_port)73 ctx->data.u32_val = active_port->dev->ifindex;74 else75 ctx->data.u32_val = 0;76}77 78static int ab_active_port_set(struct team *team, struct team_gsetter_ctx *ctx)79{80 struct team_port *port;81 82 list_for_each_entry(port, &team->port_list, list) {83 if (port->dev->ifindex == ctx->data.u32_val) {84 rcu_assign_pointer(ab_priv(team)->active_port, port);85 return 0;86 }87 }88 return -ENOENT;89}90 91static const struct team_option ab_options[] = {92 {93 .name = "activeport",94 .type = TEAM_OPTION_TYPE_U32,95 .init = ab_active_port_init,96 .getter = ab_active_port_get,97 .setter = ab_active_port_set,98 },99};100 101static int ab_init(struct team *team)102{103 return team_options_register(team, ab_options, ARRAY_SIZE(ab_options));104}105 106static void ab_exit(struct team *team)107{108 team_options_unregister(team, ab_options, ARRAY_SIZE(ab_options));109}110 111static const struct team_mode_ops ab_mode_ops = {112 .init = ab_init,113 .exit = ab_exit,114 .receive = ab_receive,115 .transmit = ab_transmit,116 .port_leave = ab_port_leave,117};118 119static const struct team_mode ab_mode = {120 .kind = "activebackup",121 .owner = THIS_MODULE,122 .priv_size = sizeof(struct ab_priv),123 .ops = &ab_mode_ops,124 .lag_tx_type = NETDEV_LAG_TX_TYPE_ACTIVEBACKUP,125};126 127static int __init ab_init_module(void)128{129 return team_mode_register(&ab_mode);130}131 132static void __exit ab_cleanup_module(void)133{134 team_mode_unregister(&ab_mode);135}136 137module_init(ab_init_module);138module_exit(ab_cleanup_module);139 140MODULE_LICENSE("GPL v2");141MODULE_AUTHOR("Jiri Pirko <jpirko@redhat.com>");142MODULE_DESCRIPTION("Active-backup mode for team");143MODULE_ALIAS_TEAM_MODE("activebackup");144