1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * drivers/net/team/team_mode_activebackup.c - Active-backup mode for team |
4 | * 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 | |
16 | struct ab_priv { |
17 | struct team_port __rcu *active_port; |
18 | struct team_option_inst_info *ap_opt_inst_info; |
19 | }; |
20 | |
21 | static struct ab_priv *ab_priv(struct team *team) |
22 | { |
23 | return (struct ab_priv *) &team->mode_priv; |
24 | } |
25 | |
26 | static 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 | |
36 | static 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, port: active_port, skb)) |
44 | return false; |
45 | return true; |
46 | |
47 | drop: |
48 | dev_kfree_skb_any(skb); |
49 | return false; |
50 | } |
51 | |
52 | static 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(opt_inst_info: ab_priv(team)->ap_opt_inst_info); |
57 | } |
58 | } |
59 | |
60 | static 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 | |
66 | static 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 | else |
75 | ctx->data.u32_val = 0; |
76 | } |
77 | |
78 | static 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 | |
91 | static 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 | |
101 | static int ab_init(struct team *team) |
102 | { |
103 | return team_options_register(team, option: ab_options, ARRAY_SIZE(ab_options)); |
104 | } |
105 | |
106 | static void ab_exit(struct team *team) |
107 | { |
108 | team_options_unregister(team, option: ab_options, ARRAY_SIZE(ab_options)); |
109 | } |
110 | |
111 | static 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 | |
119 | static 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 | |
127 | static int __init ab_init_module(void) |
128 | { |
129 | return team_mode_register(mode: &ab_mode); |
130 | } |
131 | |
132 | static void __exit ab_cleanup_module(void) |
133 | { |
134 | team_mode_unregister(mode: &ab_mode); |
135 | } |
136 | |
137 | module_init(ab_init_module); |
138 | module_exit(ab_cleanup_module); |
139 | |
140 | MODULE_LICENSE("GPL v2" ); |
141 | MODULE_AUTHOR("Jiri Pirko <jpirko@redhat.com>" ); |
142 | MODULE_DESCRIPTION("Active-backup mode for team" ); |
143 | MODULE_ALIAS_TEAM_MODE("activebackup" ); |
144 | |