diff options
author | Jiri Pirko <jpirko@redhat.com> | 2012-04-10 01:15:42 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-04-11 10:03:51 -0400 |
commit | 80f7c6683fe0e891ef1db7c967d538b5fdddd22c (patch) | |
tree | 7252117971ea67654ede4e716b771bb1fe824e36 | |
parent | 7a5cc24277b57ce38eb0afa6634b71d4d5cc671e (diff) |
team: add support for per-port options
This patch allows to create per-port options. That becomes handy for all
sorts of stuff, for example for userspace driven link-state, 802.3ad
implementation and so on.
Signed-off-by: Jiri Pirko <jpirko@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/team/team.c | 306 | ||||
-rw-r--r-- | drivers/net/team/team_mode_activebackup.c | 14 | ||||
-rw-r--r-- | drivers/net/team/team_mode_loadbalance.c | 28 | ||||
-rw-r--r-- | include/linux/if_team.h | 30 |
4 files changed, 278 insertions, 100 deletions
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index ea96f822de52..eaf8441753ce 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c | |||
@@ -81,7 +81,16 @@ EXPORT_SYMBOL(team_port_set_team_mac); | |||
81 | * Options handling | 81 | * Options handling |
82 | *******************/ | 82 | *******************/ |
83 | 83 | ||
84 | struct team_option *__team_find_option(struct team *team, const char *opt_name) | 84 | struct team_option_inst { /* One for each option instance */ |
85 | struct list_head list; | ||
86 | struct team_option *option; | ||
87 | struct team_port *port; /* != NULL if per-port */ | ||
88 | bool changed; | ||
89 | bool removed; | ||
90 | }; | ||
91 | |||
92 | static struct team_option *__team_find_option(struct team *team, | ||
93 | const char *opt_name) | ||
85 | { | 94 | { |
86 | struct team_option *option; | 95 | struct team_option *option; |
87 | 96 | ||
@@ -92,9 +101,121 @@ struct team_option *__team_find_option(struct team *team, const char *opt_name) | |||
92 | return NULL; | 101 | return NULL; |
93 | } | 102 | } |
94 | 103 | ||
95 | int __team_options_register(struct team *team, | 104 | static int __team_option_inst_add(struct team *team, struct team_option *option, |
96 | const struct team_option *option, | 105 | struct team_port *port) |
97 | size_t option_count) | 106 | { |
107 | struct team_option_inst *opt_inst; | ||
108 | |||
109 | opt_inst = kmalloc(sizeof(*opt_inst), GFP_KERNEL); | ||
110 | if (!opt_inst) | ||
111 | return -ENOMEM; | ||
112 | opt_inst->option = option; | ||
113 | opt_inst->port = port; | ||
114 | opt_inst->changed = true; | ||
115 | opt_inst->removed = false; | ||
116 | list_add_tail(&opt_inst->list, &team->option_inst_list); | ||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | static void __team_option_inst_del(struct team_option_inst *opt_inst) | ||
121 | { | ||
122 | list_del(&opt_inst->list); | ||
123 | kfree(opt_inst); | ||
124 | } | ||
125 | |||
126 | static void __team_option_inst_del_option(struct team *team, | ||
127 | struct team_option *option) | ||
128 | { | ||
129 | struct team_option_inst *opt_inst, *tmp; | ||
130 | |||
131 | list_for_each_entry_safe(opt_inst, tmp, &team->option_inst_list, list) { | ||
132 | if (opt_inst->option == option) | ||
133 | __team_option_inst_del(opt_inst); | ||
134 | } | ||
135 | } | ||
136 | |||
137 | static int __team_option_inst_add_option(struct team *team, | ||
138 | struct team_option *option) | ||
139 | { | ||
140 | struct team_port *port; | ||
141 | int err; | ||
142 | |||
143 | if (!option->per_port) | ||
144 | return __team_option_inst_add(team, option, 0); | ||
145 | |||
146 | list_for_each_entry(port, &team->port_list, list) { | ||
147 | err = __team_option_inst_add(team, option, port); | ||
148 | if (err) | ||
149 | goto inst_del_option; | ||
150 | } | ||
151 | return 0; | ||
152 | |||
153 | inst_del_option: | ||
154 | __team_option_inst_del_option(team, option); | ||
155 | return err; | ||
156 | } | ||
157 | |||
158 | static void __team_option_inst_mark_removed_option(struct team *team, | ||
159 | struct team_option *option) | ||
160 | { | ||
161 | struct team_option_inst *opt_inst; | ||
162 | |||
163 | list_for_each_entry(opt_inst, &team->option_inst_list, list) { | ||
164 | if (opt_inst->option == option) { | ||
165 | opt_inst->changed = true; | ||
166 | opt_inst->removed = true; | ||
167 | } | ||
168 | } | ||
169 | } | ||
170 | |||
171 | static void __team_option_inst_del_port(struct team *team, | ||
172 | struct team_port *port) | ||
173 | { | ||
174 | struct team_option_inst *opt_inst, *tmp; | ||
175 | |||
176 | list_for_each_entry_safe(opt_inst, tmp, &team->option_inst_list, list) { | ||
177 | if (opt_inst->option->per_port && | ||
178 | opt_inst->port == port) | ||
179 | __team_option_inst_del(opt_inst); | ||
180 | } | ||
181 | } | ||
182 | |||
183 | static int __team_option_inst_add_port(struct team *team, | ||
184 | struct team_port *port) | ||
185 | { | ||
186 | struct team_option *option; | ||
187 | int err; | ||
188 | |||
189 | list_for_each_entry(option, &team->option_list, list) { | ||
190 | if (!option->per_port) | ||
191 | continue; | ||
192 | err = __team_option_inst_add(team, option, port); | ||
193 | if (err) | ||
194 | goto inst_del_port; | ||
195 | } | ||
196 | return 0; | ||
197 | |||
198 | inst_del_port: | ||
199 | __team_option_inst_del_port(team, port); | ||
200 | return err; | ||
201 | } | ||
202 | |||
203 | static void __team_option_inst_mark_removed_port(struct team *team, | ||
204 | struct team_port *port) | ||
205 | { | ||
206 | struct team_option_inst *opt_inst; | ||
207 | |||
208 | list_for_each_entry(opt_inst, &team->option_inst_list, list) { | ||
209 | if (opt_inst->port == port) { | ||
210 | opt_inst->changed = true; | ||
211 | opt_inst->removed = true; | ||
212 | } | ||
213 | } | ||
214 | } | ||
215 | |||
216 | static int __team_options_register(struct team *team, | ||
217 | const struct team_option *option, | ||
218 | size_t option_count) | ||
98 | { | 219 | { |
99 | int i; | 220 | int i; |
100 | struct team_option **dst_opts; | 221 | struct team_option **dst_opts; |
@@ -107,26 +228,32 @@ int __team_options_register(struct team *team, | |||
107 | for (i = 0; i < option_count; i++, option++) { | 228 | for (i = 0; i < option_count; i++, option++) { |
108 | if (__team_find_option(team, option->name)) { | 229 | if (__team_find_option(team, option->name)) { |
109 | err = -EEXIST; | 230 | err = -EEXIST; |
110 | goto rollback; | 231 | goto alloc_rollback; |
111 | } | 232 | } |
112 | dst_opts[i] = kmemdup(option, sizeof(*option), GFP_KERNEL); | 233 | dst_opts[i] = kmemdup(option, sizeof(*option), GFP_KERNEL); |
113 | if (!dst_opts[i]) { | 234 | if (!dst_opts[i]) { |
114 | err = -ENOMEM; | 235 | err = -ENOMEM; |
115 | goto rollback; | 236 | goto alloc_rollback; |
116 | } | 237 | } |
117 | } | 238 | } |
118 | 239 | ||
119 | for (i = 0; i < option_count; i++) { | 240 | for (i = 0; i < option_count; i++) { |
120 | dst_opts[i]->changed = true; | 241 | err = __team_option_inst_add_option(team, dst_opts[i]); |
121 | dst_opts[i]->removed = false; | 242 | if (err) |
243 | goto inst_rollback; | ||
122 | list_add_tail(&dst_opts[i]->list, &team->option_list); | 244 | list_add_tail(&dst_opts[i]->list, &team->option_list); |
123 | } | 245 | } |
124 | 246 | ||
125 | kfree(dst_opts); | 247 | kfree(dst_opts); |
126 | return 0; | 248 | return 0; |
127 | 249 | ||
128 | rollback: | 250 | inst_rollback: |
129 | for (i = 0; i < option_count; i++) | 251 | for (i--; i >= 0; i--) |
252 | __team_option_inst_del_option(team, dst_opts[i]); | ||
253 | |||
254 | i = option_count - 1; | ||
255 | alloc_rollback: | ||
256 | for (i--; i >= 0; i--) | ||
130 | kfree(dst_opts[i]); | 257 | kfree(dst_opts[i]); |
131 | 258 | ||
132 | kfree(dst_opts); | 259 | kfree(dst_opts); |
@@ -143,10 +270,8 @@ static void __team_options_mark_removed(struct team *team, | |||
143 | struct team_option *del_opt; | 270 | struct team_option *del_opt; |
144 | 271 | ||
145 | del_opt = __team_find_option(team, option->name); | 272 | del_opt = __team_find_option(team, option->name); |
146 | if (del_opt) { | 273 | if (del_opt) |
147 | del_opt->changed = true; | 274 | __team_option_inst_mark_removed_option(team, del_opt); |
148 | del_opt->removed = true; | ||
149 | } | ||
150 | } | 275 | } |
151 | } | 276 | } |
152 | 277 | ||
@@ -161,6 +286,7 @@ static void __team_options_unregister(struct team *team, | |||
161 | 286 | ||
162 | del_opt = __team_find_option(team, option->name); | 287 | del_opt = __team_find_option(team, option->name); |
163 | if (del_opt) { | 288 | if (del_opt) { |
289 | __team_option_inst_del_option(team, del_opt); | ||
164 | list_del(&del_opt->list); | 290 | list_del(&del_opt->list); |
165 | kfree(del_opt); | 291 | kfree(del_opt); |
166 | } | 292 | } |
@@ -193,22 +319,42 @@ void team_options_unregister(struct team *team, | |||
193 | } | 319 | } |
194 | EXPORT_SYMBOL(team_options_unregister); | 320 | EXPORT_SYMBOL(team_options_unregister); |
195 | 321 | ||
196 | static int team_option_get(struct team *team, struct team_option *option, | 322 | static int team_option_port_add(struct team *team, struct team_port *port) |
197 | void *arg) | ||
198 | { | 323 | { |
199 | return option->getter(team, arg); | 324 | int err; |
325 | |||
326 | err = __team_option_inst_add_port(team, port); | ||
327 | if (err) | ||
328 | return err; | ||
329 | __team_options_change_check(team); | ||
330 | return 0; | ||
200 | } | 331 | } |
201 | 332 | ||
202 | static int team_option_set(struct team *team, struct team_option *option, | 333 | static void team_option_port_del(struct team *team, struct team_port *port) |
203 | void *arg) | 334 | { |
335 | __team_option_inst_mark_removed_port(team, port); | ||
336 | __team_options_change_check(team); | ||
337 | __team_option_inst_del_port(team, port); | ||
338 | } | ||
339 | |||
340 | static int team_option_get(struct team *team, | ||
341 | struct team_option_inst *opt_inst, | ||
342 | struct team_gsetter_ctx *ctx) | ||
343 | { | ||
344 | return opt_inst->option->getter(team, ctx); | ||
345 | } | ||
346 | |||
347 | static int team_option_set(struct team *team, | ||
348 | struct team_option_inst *opt_inst, | ||
349 | struct team_gsetter_ctx *ctx) | ||
204 | { | 350 | { |
205 | int err; | 351 | int err; |
206 | 352 | ||
207 | err = option->setter(team, arg); | 353 | err = opt_inst->option->setter(team, ctx); |
208 | if (err) | 354 | if (err) |
209 | return err; | 355 | return err; |
210 | 356 | ||
211 | option->changed = true; | 357 | opt_inst->changed = true; |
212 | __team_options_change_check(team); | 358 | __team_options_change_check(team); |
213 | return err; | 359 | return err; |
214 | } | 360 | } |
@@ -642,6 +788,13 @@ static int team_port_add(struct team *team, struct net_device *port_dev) | |||
642 | goto err_handler_register; | 788 | goto err_handler_register; |
643 | } | 789 | } |
644 | 790 | ||
791 | err = team_option_port_add(team, port); | ||
792 | if (err) { | ||
793 | netdev_err(dev, "Device %s failed to add per-port options\n", | ||
794 | portname); | ||
795 | goto err_option_port_add; | ||
796 | } | ||
797 | |||
645 | team_port_list_add_port(team, port); | 798 | team_port_list_add_port(team, port); |
646 | team_adjust_ops(team); | 799 | team_adjust_ops(team); |
647 | __team_compute_features(team); | 800 | __team_compute_features(team); |
@@ -651,6 +804,9 @@ static int team_port_add(struct team *team, struct net_device *port_dev) | |||
651 | 804 | ||
652 | return 0; | 805 | return 0; |
653 | 806 | ||
807 | err_option_port_add: | ||
808 | netdev_rx_handler_unregister(port_dev); | ||
809 | |||
654 | err_handler_register: | 810 | err_handler_register: |
655 | netdev_set_master(port_dev, NULL); | 811 | netdev_set_master(port_dev, NULL); |
656 | 812 | ||
@@ -690,6 +846,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev) | |||
690 | __team_port_change_check(port, false); | 846 | __team_port_change_check(port, false); |
691 | team_port_list_del_port(team, port); | 847 | team_port_list_del_port(team, port); |
692 | team_adjust_ops(team); | 848 | team_adjust_ops(team); |
849 | team_option_port_del(team, port); | ||
693 | netdev_rx_handler_unregister(port_dev); | 850 | netdev_rx_handler_unregister(port_dev); |
694 | netdev_set_master(port_dev, NULL); | 851 | netdev_set_master(port_dev, NULL); |
695 | vlan_vids_del_by_dev(port_dev, dev); | 852 | vlan_vids_del_by_dev(port_dev, dev); |
@@ -712,19 +869,15 @@ static int team_port_del(struct team *team, struct net_device *port_dev) | |||
712 | 869 | ||
713 | static const char team_no_mode_kind[] = "*NOMODE*"; | 870 | static const char team_no_mode_kind[] = "*NOMODE*"; |
714 | 871 | ||
715 | static int team_mode_option_get(struct team *team, void *arg) | 872 | static int team_mode_option_get(struct team *team, struct team_gsetter_ctx *ctx) |
716 | { | 873 | { |
717 | const char **str = arg; | 874 | ctx->data.str_val = team->mode ? team->mode->kind : team_no_mode_kind; |
718 | |||
719 | *str = team->mode ? team->mode->kind : team_no_mode_kind; | ||
720 | return 0; | 875 | return 0; |
721 | } | 876 | } |
722 | 877 | ||
723 | static int team_mode_option_set(struct team *team, void *arg) | 878 | static int team_mode_option_set(struct team *team, struct team_gsetter_ctx *ctx) |
724 | { | 879 | { |
725 | const char **str = arg; | 880 | return team_change_mode(team, ctx->data.str_val); |
726 | |||
727 | return team_change_mode(team, *str); | ||
728 | } | 881 | } |
729 | 882 | ||
730 | static const struct team_option team_options[] = { | 883 | static const struct team_option team_options[] = { |
@@ -756,6 +909,7 @@ static int team_init(struct net_device *dev) | |||
756 | team_adjust_ops(team); | 909 | team_adjust_ops(team); |
757 | 910 | ||
758 | INIT_LIST_HEAD(&team->option_list); | 911 | INIT_LIST_HEAD(&team->option_list); |
912 | INIT_LIST_HEAD(&team->option_inst_list); | ||
759 | err = team_options_register(team, team_options, ARRAY_SIZE(team_options)); | 913 | err = team_options_register(team, team_options, ARRAY_SIZE(team_options)); |
760 | if (err) | 914 | if (err) |
761 | goto err_options_register; | 915 | goto err_options_register; |
@@ -1238,7 +1392,8 @@ static int team_nl_fill_options_get(struct sk_buff *skb, | |||
1238 | { | 1392 | { |
1239 | struct nlattr *option_list; | 1393 | struct nlattr *option_list; |
1240 | void *hdr; | 1394 | void *hdr; |
1241 | struct team_option *option; | 1395 | struct team_option_inst *opt_inst; |
1396 | int err; | ||
1242 | 1397 | ||
1243 | hdr = genlmsg_put(skb, pid, seq, &team_nl_family, flags, | 1398 | hdr = genlmsg_put(skb, pid, seq, &team_nl_family, flags, |
1244 | TEAM_CMD_OPTIONS_GET); | 1399 | TEAM_CMD_OPTIONS_GET); |
@@ -1251,50 +1406,61 @@ static int team_nl_fill_options_get(struct sk_buff *skb, | |||
1251 | if (!option_list) | 1406 | if (!option_list) |
1252 | return -EMSGSIZE; | 1407 | return -EMSGSIZE; |
1253 | 1408 | ||
1254 | list_for_each_entry(option, &team->option_list, list) { | 1409 | list_for_each_entry(opt_inst, &team->option_inst_list, list) { |
1255 | struct nlattr *option_item; | 1410 | struct nlattr *option_item; |
1256 | long arg; | 1411 | struct team_option *option = opt_inst->option; |
1257 | struct team_option_binary tbinary; | 1412 | struct team_gsetter_ctx ctx; |
1258 | 1413 | ||
1259 | /* Include only changed options if fill all mode is not on */ | 1414 | /* Include only changed options if fill all mode is not on */ |
1260 | if (!fillall && !option->changed) | 1415 | if (!fillall && !opt_inst->changed) |
1261 | continue; | 1416 | continue; |
1262 | option_item = nla_nest_start(skb, TEAM_ATTR_ITEM_OPTION); | 1417 | option_item = nla_nest_start(skb, TEAM_ATTR_ITEM_OPTION); |
1263 | if (!option_item) | 1418 | if (!option_item) |
1264 | goto nla_put_failure; | 1419 | goto nla_put_failure; |
1265 | if (nla_put_string(skb, TEAM_ATTR_OPTION_NAME, option->name)) | 1420 | if (nla_put_string(skb, TEAM_ATTR_OPTION_NAME, option->name)) |
1266 | goto nla_put_failure; | 1421 | goto nla_put_failure; |
1267 | if (option->changed) { | 1422 | if (opt_inst->changed) { |
1268 | if (nla_put_flag(skb, TEAM_ATTR_OPTION_CHANGED)) | 1423 | if (nla_put_flag(skb, TEAM_ATTR_OPTION_CHANGED)) |
1269 | goto nla_put_failure; | 1424 | goto nla_put_failure; |
1270 | option->changed = false; | 1425 | opt_inst->changed = false; |
1271 | } | 1426 | } |
1272 | if (option->removed && | 1427 | if (opt_inst->removed && |
1273 | nla_put_flag(skb, TEAM_ATTR_OPTION_REMOVED)) | 1428 | nla_put_flag(skb, TEAM_ATTR_OPTION_REMOVED)) |
1274 | goto nla_put_failure; | 1429 | goto nla_put_failure; |
1430 | if (opt_inst->port && | ||
1431 | nla_put_u32(skb, TEAM_ATTR_OPTION_PORT_IFINDEX, | ||
1432 | opt_inst->port->dev->ifindex)) | ||
1433 | goto nla_put_failure; | ||
1434 | ctx.port = opt_inst->port; | ||
1275 | switch (option->type) { | 1435 | switch (option->type) { |
1276 | case TEAM_OPTION_TYPE_U32: | 1436 | case TEAM_OPTION_TYPE_U32: |
1277 | if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32)) | 1437 | if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32)) |
1278 | goto nla_put_failure; | 1438 | goto nla_put_failure; |
1279 | team_option_get(team, option, &arg); | 1439 | err = team_option_get(team, opt_inst, &ctx); |
1280 | if (nla_put_u32(skb, TEAM_ATTR_OPTION_DATA, arg)) | 1440 | if (err) |
1441 | goto errout; | ||
1442 | if (nla_put_u32(skb, TEAM_ATTR_OPTION_DATA, | ||
1443 | ctx.data.u32_val)) | ||
1281 | goto nla_put_failure; | 1444 | goto nla_put_failure; |
1282 | break; | 1445 | break; |
1283 | case TEAM_OPTION_TYPE_STRING: | 1446 | case TEAM_OPTION_TYPE_STRING: |
1284 | if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_STRING)) | 1447 | if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_STRING)) |
1285 | goto nla_put_failure; | 1448 | goto nla_put_failure; |
1286 | team_option_get(team, option, &arg); | 1449 | err = team_option_get(team, opt_inst, &ctx); |
1450 | if (err) | ||
1451 | goto errout; | ||
1287 | if (nla_put_string(skb, TEAM_ATTR_OPTION_DATA, | 1452 | if (nla_put_string(skb, TEAM_ATTR_OPTION_DATA, |
1288 | (char *) arg)) | 1453 | ctx.data.str_val)) |
1289 | goto nla_put_failure; | 1454 | goto nla_put_failure; |
1290 | break; | 1455 | break; |
1291 | case TEAM_OPTION_TYPE_BINARY: | 1456 | case TEAM_OPTION_TYPE_BINARY: |
1292 | if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_BINARY)) | 1457 | if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_BINARY)) |
1293 | goto nla_put_failure; | 1458 | goto nla_put_failure; |
1294 | arg = (long) &tbinary; | 1459 | err = team_option_get(team, opt_inst, &ctx); |
1295 | team_option_get(team, option, &arg); | 1460 | if (err) |
1461 | goto errout; | ||
1296 | if (nla_put(skb, TEAM_ATTR_OPTION_DATA, | 1462 | if (nla_put(skb, TEAM_ATTR_OPTION_DATA, |
1297 | tbinary.data_len, tbinary.data)) | 1463 | ctx.data.bin_val.len, ctx.data.bin_val.ptr)) |
1298 | goto nla_put_failure; | 1464 | goto nla_put_failure; |
1299 | break; | 1465 | break; |
1300 | default: | 1466 | default: |
@@ -1307,8 +1473,10 @@ static int team_nl_fill_options_get(struct sk_buff *skb, | |||
1307 | return genlmsg_end(skb, hdr); | 1473 | return genlmsg_end(skb, hdr); |
1308 | 1474 | ||
1309 | nla_put_failure: | 1475 | nla_put_failure: |
1476 | err = -EMSGSIZE; | ||
1477 | errout: | ||
1310 | genlmsg_cancel(skb, hdr); | 1478 | genlmsg_cancel(skb, hdr); |
1311 | return -EMSGSIZE; | 1479 | return err; |
1312 | } | 1480 | } |
1313 | 1481 | ||
1314 | static int team_nl_fill_options_get_all(struct sk_buff *skb, | 1482 | static int team_nl_fill_options_get_all(struct sk_buff *skb, |
@@ -1354,9 +1522,11 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) | |||
1354 | } | 1522 | } |
1355 | 1523 | ||
1356 | nla_for_each_nested(nl_option, info->attrs[TEAM_ATTR_LIST_OPTION], i) { | 1524 | nla_for_each_nested(nl_option, info->attrs[TEAM_ATTR_LIST_OPTION], i) { |
1357 | struct nlattr *mode_attrs[TEAM_ATTR_OPTION_MAX + 1]; | 1525 | struct nlattr *opt_attrs[TEAM_ATTR_OPTION_MAX + 1]; |
1526 | struct nlattr *attr_port_ifindex; | ||
1358 | enum team_option_type opt_type; | 1527 | enum team_option_type opt_type; |
1359 | struct team_option *option; | 1528 | int opt_port_ifindex = 0; /* != 0 for per-port options */ |
1529 | struct team_option_inst *opt_inst; | ||
1360 | char *opt_name; | 1530 | char *opt_name; |
1361 | bool opt_found = false; | 1531 | bool opt_found = false; |
1362 | 1532 | ||
@@ -1364,17 +1534,17 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) | |||
1364 | err = -EINVAL; | 1534 | err = -EINVAL; |
1365 | goto team_put; | 1535 | goto team_put; |
1366 | } | 1536 | } |
1367 | err = nla_parse_nested(mode_attrs, TEAM_ATTR_OPTION_MAX, | 1537 | err = nla_parse_nested(opt_attrs, TEAM_ATTR_OPTION_MAX, |
1368 | nl_option, team_nl_option_policy); | 1538 | nl_option, team_nl_option_policy); |
1369 | if (err) | 1539 | if (err) |
1370 | goto team_put; | 1540 | goto team_put; |
1371 | if (!mode_attrs[TEAM_ATTR_OPTION_NAME] || | 1541 | if (!opt_attrs[TEAM_ATTR_OPTION_NAME] || |
1372 | !mode_attrs[TEAM_ATTR_OPTION_TYPE] || | 1542 | !opt_attrs[TEAM_ATTR_OPTION_TYPE] || |
1373 | !mode_attrs[TEAM_ATTR_OPTION_DATA]) { | 1543 | !opt_attrs[TEAM_ATTR_OPTION_DATA]) { |
1374 | err = -EINVAL; | 1544 | err = -EINVAL; |
1375 | goto team_put; | 1545 | goto team_put; |
1376 | } | 1546 | } |
1377 | switch (nla_get_u8(mode_attrs[TEAM_ATTR_OPTION_TYPE])) { | 1547 | switch (nla_get_u8(opt_attrs[TEAM_ATTR_OPTION_TYPE])) { |
1378 | case NLA_U32: | 1548 | case NLA_U32: |
1379 | opt_type = TEAM_OPTION_TYPE_U32; | 1549 | opt_type = TEAM_OPTION_TYPE_U32; |
1380 | break; | 1550 | break; |
@@ -1388,39 +1558,47 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) | |||
1388 | goto team_put; | 1558 | goto team_put; |
1389 | } | 1559 | } |
1390 | 1560 | ||
1391 | opt_name = nla_data(mode_attrs[TEAM_ATTR_OPTION_NAME]); | 1561 | opt_name = nla_data(opt_attrs[TEAM_ATTR_OPTION_NAME]); |
1392 | list_for_each_entry(option, &team->option_list, list) { | 1562 | attr_port_ifindex = opt_attrs[TEAM_ATTR_OPTION_PORT_IFINDEX]; |
1393 | long arg; | 1563 | if (attr_port_ifindex) |
1564 | opt_port_ifindex = nla_get_u32(attr_port_ifindex); | ||
1565 | |||
1566 | list_for_each_entry(opt_inst, &team->option_inst_list, list) { | ||
1567 | struct team_option *option = opt_inst->option; | ||
1394 | struct nlattr *opt_data_attr; | 1568 | struct nlattr *opt_data_attr; |
1395 | struct team_option_binary tbinary; | 1569 | struct team_gsetter_ctx ctx; |
1396 | int data_len; | 1570 | int data_len; |
1571 | int tmp_ifindex; | ||
1397 | 1572 | ||
1573 | tmp_ifindex = opt_inst->port ? | ||
1574 | opt_inst->port->dev->ifindex : 0; | ||
1398 | if (option->type != opt_type || | 1575 | if (option->type != opt_type || |
1399 | strcmp(option->name, opt_name)) | 1576 | strcmp(option->name, opt_name) || |
1577 | tmp_ifindex != opt_port_ifindex) | ||
1400 | continue; | 1578 | continue; |
1401 | opt_found = true; | 1579 | opt_found = true; |
1402 | opt_data_attr = mode_attrs[TEAM_ATTR_OPTION_DATA]; | 1580 | opt_data_attr = opt_attrs[TEAM_ATTR_OPTION_DATA]; |
1403 | data_len = nla_len(opt_data_attr); | 1581 | data_len = nla_len(opt_data_attr); |
1582 | ctx.port = opt_inst->port; | ||
1404 | switch (opt_type) { | 1583 | switch (opt_type) { |
1405 | case TEAM_OPTION_TYPE_U32: | 1584 | case TEAM_OPTION_TYPE_U32: |
1406 | arg = nla_get_u32(opt_data_attr); | 1585 | ctx.data.u32_val = nla_get_u32(opt_data_attr); |
1407 | break; | 1586 | break; |
1408 | case TEAM_OPTION_TYPE_STRING: | 1587 | case TEAM_OPTION_TYPE_STRING: |
1409 | if (data_len > TEAM_STRING_MAX_LEN) { | 1588 | if (data_len > TEAM_STRING_MAX_LEN) { |
1410 | err = -EINVAL; | 1589 | err = -EINVAL; |
1411 | goto team_put; | 1590 | goto team_put; |
1412 | } | 1591 | } |
1413 | arg = (long) nla_data(opt_data_attr); | 1592 | ctx.data.str_val = nla_data(opt_data_attr); |
1414 | break; | 1593 | break; |
1415 | case TEAM_OPTION_TYPE_BINARY: | 1594 | case TEAM_OPTION_TYPE_BINARY: |
1416 | tbinary.data_len = data_len; | 1595 | ctx.data.bin_val.len = data_len; |
1417 | tbinary.data = nla_data(opt_data_attr); | 1596 | ctx.data.bin_val.ptr = nla_data(opt_data_attr); |
1418 | arg = (long) &tbinary; | ||
1419 | break; | 1597 | break; |
1420 | default: | 1598 | default: |
1421 | BUG(); | 1599 | BUG(); |
1422 | } | 1600 | } |
1423 | err = team_option_set(team, option, &arg); | 1601 | err = team_option_set(team, opt_inst, &ctx); |
1424 | if (err) | 1602 | if (err) |
1425 | goto team_put; | 1603 | goto team_put; |
1426 | } | 1604 | } |
diff --git a/drivers/net/team/team_mode_activebackup.c b/drivers/net/team/team_mode_activebackup.c index f4d960e82e29..6cde1ab8fff0 100644 --- a/drivers/net/team/team_mode_activebackup.c +++ b/drivers/net/team/team_mode_activebackup.c | |||
@@ -59,23 +59,21 @@ static void ab_port_leave(struct team *team, struct team_port *port) | |||
59 | RCU_INIT_POINTER(ab_priv(team)->active_port, NULL); | 59 | RCU_INIT_POINTER(ab_priv(team)->active_port, NULL); |
60 | } | 60 | } |
61 | 61 | ||
62 | static int ab_active_port_get(struct team *team, void *arg) | 62 | static int ab_active_port_get(struct team *team, struct team_gsetter_ctx *ctx) |
63 | { | 63 | { |
64 | u32 *ifindex = arg; | ||
65 | |||
66 | *ifindex = 0; | ||
67 | if (ab_priv(team)->active_port) | 64 | if (ab_priv(team)->active_port) |
68 | *ifindex = ab_priv(team)->active_port->dev->ifindex; | 65 | ctx->data.u32_val = ab_priv(team)->active_port->dev->ifindex; |
66 | else | ||
67 | ctx->data.u32_val = 0; | ||
69 | return 0; | 68 | return 0; |
70 | } | 69 | } |
71 | 70 | ||
72 | static int ab_active_port_set(struct team *team, void *arg) | 71 | static int ab_active_port_set(struct team *team, struct team_gsetter_ctx *ctx) |
73 | { | 72 | { |
74 | u32 *ifindex = arg; | ||
75 | struct team_port *port; | 73 | struct team_port *port; |
76 | 74 | ||
77 | list_for_each_entry_rcu(port, &team->port_list, list) { | 75 | list_for_each_entry_rcu(port, &team->port_list, list) { |
78 | if (port->dev->ifindex == *ifindex) { | 76 | if (port->dev->ifindex == ctx->data.u32_val) { |
79 | rcu_assign_pointer(ab_priv(team)->active_port, port); | 77 | rcu_assign_pointer(ab_priv(team)->active_port, port); |
80 | return 0; | 78 | return 0; |
81 | } | 79 | } |
diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c index ed20f395be6f..167cdb4fe76e 100644 --- a/drivers/net/team/team_mode_loadbalance.c +++ b/drivers/net/team/team_mode_loadbalance.c | |||
@@ -52,22 +52,21 @@ drop: | |||
52 | return false; | 52 | return false; |
53 | } | 53 | } |
54 | 54 | ||
55 | static int lb_bpf_func_get(struct team *team, void *arg) | 55 | static int lb_bpf_func_get(struct team *team, struct team_gsetter_ctx *ctx) |
56 | { | 56 | { |
57 | struct team_option_binary *tbinary = team_optarg_tbinary(arg); | 57 | if (!lb_priv(team)->orig_fprog) { |
58 | 58 | ctx->data.bin_val.len = 0; | |
59 | memset(tbinary, 0, sizeof(*tbinary)); | 59 | ctx->data.bin_val.ptr = NULL; |
60 | if (!lb_priv(team)->orig_fprog) | ||
61 | return 0; | 60 | return 0; |
62 | 61 | } | |
63 | tbinary->data_len = lb_priv(team)->orig_fprog->len * | 62 | ctx->data.bin_val.len = lb_priv(team)->orig_fprog->len * |
64 | sizeof(struct sock_filter); | 63 | sizeof(struct sock_filter); |
65 | tbinary->data = lb_priv(team)->orig_fprog->filter; | 64 | ctx->data.bin_val.ptr = lb_priv(team)->orig_fprog->filter; |
66 | return 0; | 65 | return 0; |
67 | } | 66 | } |
68 | 67 | ||
69 | static int __fprog_create(struct sock_fprog **pfprog, u32 data_len, | 68 | static int __fprog_create(struct sock_fprog **pfprog, u32 data_len, |
70 | void *data) | 69 | const void *data) |
71 | { | 70 | { |
72 | struct sock_fprog *fprog; | 71 | struct sock_fprog *fprog; |
73 | struct sock_filter *filter = (struct sock_filter *) data; | 72 | struct sock_filter *filter = (struct sock_filter *) data; |
@@ -93,16 +92,15 @@ static void __fprog_destroy(struct sock_fprog *fprog) | |||
93 | kfree(fprog); | 92 | kfree(fprog); |
94 | } | 93 | } |
95 | 94 | ||
96 | static int lb_bpf_func_set(struct team *team, void *arg) | 95 | static int lb_bpf_func_set(struct team *team, struct team_gsetter_ctx *ctx) |
97 | { | 96 | { |
98 | struct team_option_binary *tbinary = team_optarg_tbinary(arg); | ||
99 | struct sk_filter *fp = NULL; | 97 | struct sk_filter *fp = NULL; |
100 | struct sock_fprog *fprog = NULL; | 98 | struct sock_fprog *fprog = NULL; |
101 | int err; | 99 | int err; |
102 | 100 | ||
103 | if (tbinary->data_len) { | 101 | if (ctx->data.bin_val.len) { |
104 | err = __fprog_create(&fprog, tbinary->data_len, | 102 | err = __fprog_create(&fprog, ctx->data.bin_val.len, |
105 | tbinary->data); | 103 | ctx->data.bin_val.ptr); |
106 | if (err) | 104 | if (err) |
107 | return err; | 105 | return err; |
108 | err = sk_unattached_filter_create(&fp, fprog); | 106 | err = sk_unattached_filter_create(&fp, fprog); |
diff --git a/include/linux/if_team.h b/include/linux/if_team.h index 41163ac14ab4..6f27c841c9a8 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h | |||
@@ -71,25 +71,27 @@ enum team_option_type { | |||
71 | TEAM_OPTION_TYPE_BINARY, | 71 | TEAM_OPTION_TYPE_BINARY, |
72 | }; | 72 | }; |
73 | 73 | ||
74 | struct team_gsetter_ctx { | ||
75 | union { | ||
76 | u32 u32_val; | ||
77 | const char *str_val; | ||
78 | struct { | ||
79 | const void *ptr; | ||
80 | u32 len; | ||
81 | } bin_val; | ||
82 | } data; | ||
83 | struct team_port *port; | ||
84 | }; | ||
85 | |||
74 | struct team_option { | 86 | struct team_option { |
75 | struct list_head list; | 87 | struct list_head list; |
76 | const char *name; | 88 | const char *name; |
89 | bool per_port; | ||
77 | enum team_option_type type; | 90 | enum team_option_type type; |
78 | int (*getter)(struct team *team, void *arg); | 91 | int (*getter)(struct team *team, struct team_gsetter_ctx *ctx); |
79 | int (*setter)(struct team *team, void *arg); | 92 | int (*setter)(struct team *team, struct team_gsetter_ctx *ctx); |
80 | |||
81 | /* Custom gennetlink interface related flags */ | ||
82 | bool changed; | ||
83 | bool removed; | ||
84 | }; | 93 | }; |
85 | 94 | ||
86 | struct team_option_binary { | ||
87 | u32 data_len; | ||
88 | void *data; | ||
89 | }; | ||
90 | |||
91 | #define team_optarg_tbinary(arg) (*((struct team_option_binary **) arg)) | ||
92 | |||
93 | struct team_mode { | 95 | struct team_mode { |
94 | struct list_head list; | 96 | struct list_head list; |
95 | const char *kind; | 97 | const char *kind; |
@@ -118,6 +120,7 @@ struct team { | |||
118 | struct list_head port_list; | 120 | struct list_head port_list; |
119 | 121 | ||
120 | struct list_head option_list; | 122 | struct list_head option_list; |
123 | struct list_head option_inst_list; /* list of option instances */ | ||
121 | 124 | ||
122 | const struct team_mode *mode; | 125 | const struct team_mode *mode; |
123 | struct team_mode_ops ops; | 126 | struct team_mode_ops ops; |
@@ -224,6 +227,7 @@ enum { | |||
224 | TEAM_ATTR_OPTION_TYPE, /* u8 */ | 227 | TEAM_ATTR_OPTION_TYPE, /* u8 */ |
225 | TEAM_ATTR_OPTION_DATA, /* dynamic */ | 228 | TEAM_ATTR_OPTION_DATA, /* dynamic */ |
226 | TEAM_ATTR_OPTION_REMOVED, /* flag */ | 229 | TEAM_ATTR_OPTION_REMOVED, /* flag */ |
230 | TEAM_ATTR_OPTION_PORT_IFINDEX, /* u32 */ /* for per-port options */ | ||
227 | 231 | ||
228 | __TEAM_ATTR_OPTION_MAX, | 232 | __TEAM_ATTR_OPTION_MAX, |
229 | TEAM_ATTR_OPTION_MAX = __TEAM_ATTR_OPTION_MAX - 1, | 233 | TEAM_ATTR_OPTION_MAX = __TEAM_ATTR_OPTION_MAX - 1, |