aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJiri Pirko <jpirko@redhat.com>2012-04-10 01:15:42 -0400
committerDavid S. Miller <davem@davemloft.net>2012-04-11 10:03:51 -0400
commit80f7c6683fe0e891ef1db7c967d538b5fdddd22c (patch)
tree7252117971ea67654ede4e716b771bb1fe824e36
parent7a5cc24277b57ce38eb0afa6634b71d4d5cc671e (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.c306
-rw-r--r--drivers/net/team/team_mode_activebackup.c14
-rw-r--r--drivers/net/team/team_mode_loadbalance.c28
-rw-r--r--include/linux/if_team.h30
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
84struct team_option *__team_find_option(struct team *team, const char *opt_name) 84struct 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
92static 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
95int __team_options_register(struct team *team, 104static 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
120static void __team_option_inst_del(struct team_option_inst *opt_inst)
121{
122 list_del(&opt_inst->list);
123 kfree(opt_inst);
124}
125
126static 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
137static 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
153inst_del_option:
154 __team_option_inst_del_option(team, option);
155 return err;
156}
157
158static 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
171static 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
183static 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
198inst_del_port:
199 __team_option_inst_del_port(team, port);
200 return err;
201}
202
203static 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
216static 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
128rollback: 250inst_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;
255alloc_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}
194EXPORT_SYMBOL(team_options_unregister); 320EXPORT_SYMBOL(team_options_unregister);
195 321
196static int team_option_get(struct team *team, struct team_option *option, 322static 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
202static int team_option_set(struct team *team, struct team_option *option, 333static 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
340static 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
347static 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
807err_option_port_add:
808 netdev_rx_handler_unregister(port_dev);
809
654err_handler_register: 810err_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
713static const char team_no_mode_kind[] = "*NOMODE*"; 870static const char team_no_mode_kind[] = "*NOMODE*";
714 871
715static int team_mode_option_get(struct team *team, void *arg) 872static 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
723static int team_mode_option_set(struct team *team, void *arg) 878static 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
730static const struct team_option team_options[] = { 883static 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
1309nla_put_failure: 1475nla_put_failure:
1476 err = -EMSGSIZE;
1477errout:
1310 genlmsg_cancel(skb, hdr); 1478 genlmsg_cancel(skb, hdr);
1311 return -EMSGSIZE; 1479 return err;
1312} 1480}
1313 1481
1314static int team_nl_fill_options_get_all(struct sk_buff *skb, 1482static 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
62static int ab_active_port_get(struct team *team, void *arg) 62static 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
72static int ab_active_port_set(struct team *team, void *arg) 71static 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
55static int lb_bpf_func_get(struct team *team, void *arg) 55static 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
69static int __fprog_create(struct sock_fprog **pfprog, u32 data_len, 68static 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
96static int lb_bpf_func_set(struct team *team, void *arg) 95static 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
74struct 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
74struct team_option { 86struct 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
86struct team_option_binary {
87 u32 data_len;
88 void *data;
89};
90
91#define team_optarg_tbinary(arg) (*((struct team_option_binary **) arg))
92
93struct team_mode { 95struct 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,