Commit | Line | Data |
---|---|---|
3d249d4c | 1 | /* |
0d572e45 | 2 | * drivers/net/team/team_mode_activebackup.c - Active-backup mode for team |
3d249d4c JP |
3 | * Copyright (c) 2011 Jiri Pirko <jpirko@redhat.com> |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; either version 2 of the License, or | |
8 | * (at your option) any later version. | |
9 | */ | |
10 | ||
11 | #include <linux/kernel.h> | |
12 | #include <linux/types.h> | |
13 | #include <linux/module.h> | |
14 | #include <linux/init.h> | |
15 | #include <linux/errno.h> | |
16 | #include <linux/netdevice.h> | |
17 | #include <net/rtnetlink.h> | |
18 | #include <linux/if_team.h> | |
19 | ||
20 | struct ab_priv { | |
21 | struct team_port __rcu *active_port; | |
675b8044 | 22 | struct team_option_inst_info *ap_opt_inst_info; |
3d249d4c JP |
23 | }; |
24 | ||
25 | static struct ab_priv *ab_priv(struct team *team) | |
26 | { | |
27 | return (struct ab_priv *) &team->mode_priv; | |
28 | } | |
29 | ||
30 | static rx_handler_result_t ab_receive(struct team *team, struct team_port *port, | |
31 | struct sk_buff *skb) { | |
32 | struct team_port *active_port; | |
33 | ||
34 | active_port = rcu_dereference(ab_priv(team)->active_port); | |
35 | if (active_port != port) | |
36 | return RX_HANDLER_EXACT; | |
37 | return RX_HANDLER_ANOTHER; | |
38 | } | |
39 | ||
40 | static bool ab_transmit(struct team *team, struct sk_buff *skb) | |
41 | { | |
42 | struct team_port *active_port; | |
43 | ||
d1904fbd | 44 | active_port = rcu_dereference_bh(ab_priv(team)->active_port); |
3d249d4c JP |
45 | if (unlikely(!active_port)) |
46 | goto drop; | |
bd2d0837 | 47 | if (team_dev_queue_xmit(team, active_port, skb)) |
3d249d4c JP |
48 | return false; |
49 | return true; | |
50 | ||
51 | drop: | |
52 | dev_kfree_skb_any(skb); | |
53 | return false; | |
54 | } | |
55 | ||
56 | static void ab_port_leave(struct team *team, struct team_port *port) | |
57 | { | |
675b8044 | 58 | if (ab_priv(team)->active_port == port) { |
2cfa5a04 | 59 | RCU_INIT_POINTER(ab_priv(team)->active_port, NULL); |
675b8044 JP |
60 | team_option_inst_set_change(ab_priv(team)->ap_opt_inst_info); |
61 | } | |
62 | } | |
63 | ||
64 | static int ab_active_port_init(struct team *team, | |
65 | struct team_option_inst_info *info) | |
66 | { | |
67 | ab_priv(team)->ap_opt_inst_info = info; | |
68 | return 0; | |
3d249d4c JP |
69 | } |
70 | ||
80f7c668 | 71 | static int ab_active_port_get(struct team *team, struct team_gsetter_ctx *ctx) |
3d249d4c | 72 | { |
6dab015c JP |
73 | struct team_port *active_port; |
74 | ||
75 | active_port = rcu_dereference_protected(ab_priv(team)->active_port, | |
76 | lockdep_is_held(&team->lock)); | |
77 | if (active_port) | |
78 | ctx->data.u32_val = active_port->dev->ifindex; | |
80f7c668 JP |
79 | else |
80 | ctx->data.u32_val = 0; | |
3d249d4c JP |
81 | return 0; |
82 | } | |
83 | ||
80f7c668 | 84 | static int ab_active_port_set(struct team *team, struct team_gsetter_ctx *ctx) |
3d249d4c | 85 | { |
3d249d4c JP |
86 | struct team_port *port; |
87 | ||
679b1607 | 88 | list_for_each_entry(port, &team->port_list, list) { |
80f7c668 | 89 | if (port->dev->ifindex == ctx->data.u32_val) { |
3d249d4c JP |
90 | rcu_assign_pointer(ab_priv(team)->active_port, port); |
91 | return 0; | |
92 | } | |
93 | } | |
94 | return -ENOENT; | |
95 | } | |
96 | ||
358b8382 | 97 | static const struct team_option ab_options[] = { |
3d249d4c JP |
98 | { |
99 | .name = "activeport", | |
100 | .type = TEAM_OPTION_TYPE_U32, | |
675b8044 | 101 | .init = ab_active_port_init, |
3d249d4c JP |
102 | .getter = ab_active_port_get, |
103 | .setter = ab_active_port_set, | |
104 | }, | |
105 | }; | |
106 | ||
cade4555 | 107 | static int ab_init(struct team *team) |
3d249d4c | 108 | { |
358b8382 | 109 | return team_options_register(team, ab_options, ARRAY_SIZE(ab_options)); |
3d249d4c JP |
110 | } |
111 | ||
cade4555 | 112 | static void ab_exit(struct team *team) |
3d249d4c JP |
113 | { |
114 | team_options_unregister(team, ab_options, ARRAY_SIZE(ab_options)); | |
115 | } | |
116 | ||
117 | static const struct team_mode_ops ab_mode_ops = { | |
118 | .init = ab_init, | |
119 | .exit = ab_exit, | |
120 | .receive = ab_receive, | |
121 | .transmit = ab_transmit, | |
122 | .port_leave = ab_port_leave, | |
123 | }; | |
124 | ||
0402788a | 125 | static const struct team_mode ab_mode = { |
3d249d4c JP |
126 | .kind = "activebackup", |
127 | .owner = THIS_MODULE, | |
128 | .priv_size = sizeof(struct ab_priv), | |
129 | .ops = &ab_mode_ops, | |
130 | }; | |
131 | ||
132 | static int __init ab_init_module(void) | |
133 | { | |
134 | return team_mode_register(&ab_mode); | |
135 | } | |
136 | ||
137 | static void __exit ab_cleanup_module(void) | |
138 | { | |
139 | team_mode_unregister(&ab_mode); | |
140 | } | |
141 | ||
142 | module_init(ab_init_module); | |
143 | module_exit(ab_cleanup_module); | |
144 | ||
145 | MODULE_LICENSE("GPL v2"); | |
146 | MODULE_AUTHOR("Jiri Pirko <jpirko@redhat.com>"); | |
147 | MODULE_DESCRIPTION("Active-backup mode for team"); | |
148 | MODULE_ALIAS("team-mode-activebackup"); |