diff options
Diffstat (limited to 'drivers/net/team/team.c')
| -rw-r--r-- | drivers/net/team/team.c | 136 |
1 files changed, 90 insertions, 46 deletions
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index ed2a862b835d..6b678f38e5ce 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c | |||
| @@ -92,9 +92,9 @@ struct team_option *__team_find_option(struct team *team, const char *opt_name) | |||
| 92 | return NULL; | 92 | return NULL; |
| 93 | } | 93 | } |
| 94 | 94 | ||
| 95 | int team_options_register(struct team *team, | 95 | int __team_options_register(struct team *team, |
| 96 | const struct team_option *option, | 96 | const struct team_option *option, |
| 97 | size_t option_count) | 97 | size_t option_count) |
| 98 | { | 98 | { |
| 99 | int i; | 99 | int i; |
| 100 | struct team_option **dst_opts; | 100 | struct team_option **dst_opts; |
| @@ -116,8 +116,11 @@ int team_options_register(struct team *team, | |||
| 116 | } | 116 | } |
| 117 | } | 117 | } |
| 118 | 118 | ||
| 119 | for (i = 0; i < option_count; i++) | 119 | for (i = 0; i < option_count; i++) { |
| 120 | dst_opts[i]->changed = true; | ||
| 121 | dst_opts[i]->removed = false; | ||
| 120 | list_add_tail(&dst_opts[i]->list, &team->option_list); | 122 | list_add_tail(&dst_opts[i]->list, &team->option_list); |
| 123 | } | ||
| 121 | 124 | ||
| 122 | kfree(dst_opts); | 125 | kfree(dst_opts); |
| 123 | return 0; | 126 | return 0; |
| @@ -130,10 +133,22 @@ rollback: | |||
| 130 | return err; | 133 | return err; |
| 131 | } | 134 | } |
| 132 | 135 | ||
| 133 | EXPORT_SYMBOL(team_options_register); | 136 | static void __team_options_mark_removed(struct team *team, |
| 137 | const struct team_option *option, | ||
| 138 | size_t option_count) | ||
| 139 | { | ||
| 140 | int i; | ||
| 141 | |||
| 142 | for (i = 0; i < option_count; i++, option++) { | ||
| 143 | struct team_option *del_opt; | ||
| 134 | 144 | ||
| 135 | static void __team_options_change_check(struct team *team, | 145 | del_opt = __team_find_option(team, option->name); |
| 136 | struct team_option *changed_option); | 146 | if (del_opt) { |
| 147 | del_opt->changed = true; | ||
| 148 | del_opt->removed = true; | ||
| 149 | } | ||
| 150 | } | ||
| 151 | } | ||
| 137 | 152 | ||
| 138 | static void __team_options_unregister(struct team *team, | 153 | static void __team_options_unregister(struct team *team, |
| 139 | const struct team_option *option, | 154 | const struct team_option *option, |
| @@ -152,12 +167,29 @@ static void __team_options_unregister(struct team *team, | |||
| 152 | } | 167 | } |
| 153 | } | 168 | } |
| 154 | 169 | ||
| 170 | static void __team_options_change_check(struct team *team); | ||
| 171 | |||
| 172 | int team_options_register(struct team *team, | ||
| 173 | const struct team_option *option, | ||
| 174 | size_t option_count) | ||
| 175 | { | ||
| 176 | int err; | ||
| 177 | |||
| 178 | err = __team_options_register(team, option, option_count); | ||
| 179 | if (err) | ||
| 180 | return err; | ||
| 181 | __team_options_change_check(team); | ||
| 182 | return 0; | ||
| 183 | } | ||
| 184 | EXPORT_SYMBOL(team_options_register); | ||
| 185 | |||
| 155 | void team_options_unregister(struct team *team, | 186 | void team_options_unregister(struct team *team, |
| 156 | const struct team_option *option, | 187 | const struct team_option *option, |
| 157 | size_t option_count) | 188 | size_t option_count) |
| 158 | { | 189 | { |
| 190 | __team_options_mark_removed(team, option, option_count); | ||
| 191 | __team_options_change_check(team); | ||
| 159 | __team_options_unregister(team, option, option_count); | 192 | __team_options_unregister(team, option, option_count); |
| 160 | __team_options_change_check(team, NULL); | ||
| 161 | } | 193 | } |
| 162 | EXPORT_SYMBOL(team_options_unregister); | 194 | EXPORT_SYMBOL(team_options_unregister); |
| 163 | 195 | ||
| @@ -176,7 +208,8 @@ static int team_option_set(struct team *team, struct team_option *option, | |||
| 176 | if (err) | 208 | if (err) |
| 177 | return err; | 209 | return err; |
| 178 | 210 | ||
| 179 | __team_options_change_check(team, option); | 211 | option->changed = true; |
| 212 | __team_options_change_check(team); | ||
| 180 | return err; | 213 | return err; |
| 181 | } | 214 | } |
| 182 | 215 | ||
| @@ -653,6 +686,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev) | |||
| 653 | return -ENOENT; | 686 | return -ENOENT; |
| 654 | } | 687 | } |
| 655 | 688 | ||
| 689 | port->removed = true; | ||
| 656 | __team_port_change_check(port, false); | 690 | __team_port_change_check(port, false); |
| 657 | team_port_list_del_port(team, port); | 691 | team_port_list_del_port(team, port); |
| 658 | team_adjust_ops(team); | 692 | team_adjust_ops(team); |
| @@ -1200,10 +1234,9 @@ err_fill: | |||
| 1200 | return err; | 1234 | return err; |
| 1201 | } | 1235 | } |
| 1202 | 1236 | ||
| 1203 | static int team_nl_fill_options_get_changed(struct sk_buff *skb, | 1237 | static int team_nl_fill_options_get(struct sk_buff *skb, |
| 1204 | u32 pid, u32 seq, int flags, | 1238 | u32 pid, u32 seq, int flags, |
| 1205 | struct team *team, | 1239 | struct team *team, bool fillall) |
| 1206 | struct team_option *changed_option) | ||
| 1207 | { | 1240 | { |
| 1208 | struct nlattr *option_list; | 1241 | struct nlattr *option_list; |
| 1209 | void *hdr; | 1242 | void *hdr; |
| @@ -1223,12 +1256,19 @@ static int team_nl_fill_options_get_changed(struct sk_buff *skb, | |||
| 1223 | struct nlattr *option_item; | 1256 | struct nlattr *option_item; |
| 1224 | long arg; | 1257 | long arg; |
| 1225 | 1258 | ||
| 1259 | /* Include only changed options if fill all mode is not on */ | ||
| 1260 | if (!fillall && !option->changed) | ||
| 1261 | continue; | ||
| 1226 | option_item = nla_nest_start(skb, TEAM_ATTR_ITEM_OPTION); | 1262 | option_item = nla_nest_start(skb, TEAM_ATTR_ITEM_OPTION); |
| 1227 | if (!option_item) | 1263 | if (!option_item) |
| 1228 | goto nla_put_failure; | 1264 | goto nla_put_failure; |
| 1229 | NLA_PUT_STRING(skb, TEAM_ATTR_OPTION_NAME, option->name); | 1265 | NLA_PUT_STRING(skb, TEAM_ATTR_OPTION_NAME, option->name); |
| 1230 | if (option == changed_option) | 1266 | if (option->changed) { |
| 1231 | NLA_PUT_FLAG(skb, TEAM_ATTR_OPTION_CHANGED); | 1267 | NLA_PUT_FLAG(skb, TEAM_ATTR_OPTION_CHANGED); |
| 1268 | option->changed = false; | ||
| 1269 | } | ||
| 1270 | if (option->removed) | ||
| 1271 | NLA_PUT_FLAG(skb, TEAM_ATTR_OPTION_REMOVED); | ||
| 1232 | switch (option->type) { | 1272 | switch (option->type) { |
| 1233 | case TEAM_OPTION_TYPE_U32: | 1273 | case TEAM_OPTION_TYPE_U32: |
| 1234 | NLA_PUT_U8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32); | 1274 | NLA_PUT_U8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32); |
| @@ -1255,13 +1295,13 @@ nla_put_failure: | |||
| 1255 | return -EMSGSIZE; | 1295 | return -EMSGSIZE; |
| 1256 | } | 1296 | } |
| 1257 | 1297 | ||
| 1258 | static int team_nl_fill_options_get(struct sk_buff *skb, | 1298 | static int team_nl_fill_options_get_all(struct sk_buff *skb, |
| 1259 | struct genl_info *info, int flags, | 1299 | struct genl_info *info, int flags, |
| 1260 | struct team *team) | 1300 | struct team *team) |
| 1261 | { | 1301 | { |
| 1262 | return team_nl_fill_options_get_changed(skb, info->snd_pid, | 1302 | return team_nl_fill_options_get(skb, info->snd_pid, |
| 1263 | info->snd_seq, NLM_F_ACK, | 1303 | info->snd_seq, NLM_F_ACK, |
| 1264 | team, NULL); | 1304 | team, true); |
| 1265 | } | 1305 | } |
| 1266 | 1306 | ||
| 1267 | static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info) | 1307 | static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info) |
| @@ -1273,7 +1313,7 @@ static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info) | |||
| 1273 | if (!team) | 1313 | if (!team) |
| 1274 | return -EINVAL; | 1314 | return -EINVAL; |
| 1275 | 1315 | ||
| 1276 | err = team_nl_send_generic(info, team, team_nl_fill_options_get); | 1316 | err = team_nl_send_generic(info, team, team_nl_fill_options_get_all); |
| 1277 | 1317 | ||
| 1278 | team_nl_team_put(team); | 1318 | team_nl_team_put(team); |
| 1279 | 1319 | ||
| @@ -1365,10 +1405,10 @@ team_put: | |||
| 1365 | return err; | 1405 | return err; |
| 1366 | } | 1406 | } |
| 1367 | 1407 | ||
| 1368 | static int team_nl_fill_port_list_get_changed(struct sk_buff *skb, | 1408 | static int team_nl_fill_port_list_get(struct sk_buff *skb, |
| 1369 | u32 pid, u32 seq, int flags, | 1409 | u32 pid, u32 seq, int flags, |
| 1370 | struct team *team, | 1410 | struct team *team, |
| 1371 | struct team_port *changed_port) | 1411 | bool fillall) |
| 1372 | { | 1412 | { |
| 1373 | struct nlattr *port_list; | 1413 | struct nlattr *port_list; |
| 1374 | void *hdr; | 1414 | void *hdr; |
| @@ -1387,12 +1427,19 @@ static int team_nl_fill_port_list_get_changed(struct sk_buff *skb, | |||
| 1387 | list_for_each_entry(port, &team->port_list, list) { | 1427 | list_for_each_entry(port, &team->port_list, list) { |
| 1388 | struct nlattr *port_item; | 1428 | struct nlattr *port_item; |
| 1389 | 1429 | ||
| 1430 | /* Include only changed ports if fill all mode is not on */ | ||
| 1431 | if (!fillall && !port->changed) | ||
| 1432 | continue; | ||
| 1390 | port_item = nla_nest_start(skb, TEAM_ATTR_ITEM_PORT); | 1433 | port_item = nla_nest_start(skb, TEAM_ATTR_ITEM_PORT); |
| 1391 | if (!port_item) | 1434 | if (!port_item) |
| 1392 | goto nla_put_failure; | 1435 | goto nla_put_failure; |
| 1393 | NLA_PUT_U32(skb, TEAM_ATTR_PORT_IFINDEX, port->dev->ifindex); | 1436 | NLA_PUT_U32(skb, TEAM_ATTR_PORT_IFINDEX, port->dev->ifindex); |
| 1394 | if (port == changed_port) | 1437 | if (port->changed) { |
| 1395 | NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_CHANGED); | 1438 | NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_CHANGED); |
| 1439 | port->changed = false; | ||
| 1440 | } | ||
| 1441 | if (port->removed) | ||
| 1442 | NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_REMOVED); | ||
| 1396 | if (port->linkup) | 1443 | if (port->linkup) |
| 1397 | NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_LINKUP); | 1444 | NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_LINKUP); |
| 1398 | NLA_PUT_U32(skb, TEAM_ATTR_PORT_SPEED, port->speed); | 1445 | NLA_PUT_U32(skb, TEAM_ATTR_PORT_SPEED, port->speed); |
| @@ -1408,13 +1455,13 @@ nla_put_failure: | |||
| 1408 | return -EMSGSIZE; | 1455 | return -EMSGSIZE; |
| 1409 | } | 1456 | } |
| 1410 | 1457 | ||
| 1411 | static int team_nl_fill_port_list_get(struct sk_buff *skb, | 1458 | static int team_nl_fill_port_list_get_all(struct sk_buff *skb, |
| 1412 | struct genl_info *info, int flags, | 1459 | struct genl_info *info, int flags, |
| 1413 | struct team *team) | 1460 | struct team *team) |
| 1414 | { | 1461 | { |
| 1415 | return team_nl_fill_port_list_get_changed(skb, info->snd_pid, | 1462 | return team_nl_fill_port_list_get(skb, info->snd_pid, |
| 1416 | info->snd_seq, NLM_F_ACK, | 1463 | info->snd_seq, NLM_F_ACK, |
| 1417 | team, NULL); | 1464 | team, true); |
| 1418 | } | 1465 | } |
| 1419 | 1466 | ||
| 1420 | static int team_nl_cmd_port_list_get(struct sk_buff *skb, | 1467 | static int team_nl_cmd_port_list_get(struct sk_buff *skb, |
| @@ -1427,7 +1474,7 @@ static int team_nl_cmd_port_list_get(struct sk_buff *skb, | |||
| 1427 | if (!team) | 1474 | if (!team) |
| 1428 | return -EINVAL; | 1475 | return -EINVAL; |
| 1429 | 1476 | ||
| 1430 | err = team_nl_send_generic(info, team, team_nl_fill_port_list_get); | 1477 | err = team_nl_send_generic(info, team, team_nl_fill_port_list_get_all); |
| 1431 | 1478 | ||
| 1432 | team_nl_team_put(team); | 1479 | team_nl_team_put(team); |
| 1433 | 1480 | ||
| @@ -1464,8 +1511,7 @@ static struct genl_multicast_group team_change_event_mcgrp = { | |||
| 1464 | .name = TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME, | 1511 | .name = TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME, |
| 1465 | }; | 1512 | }; |
| 1466 | 1513 | ||
| 1467 | static int team_nl_send_event_options_get(struct team *team, | 1514 | static int team_nl_send_event_options_get(struct team *team) |
| 1468 | struct team_option *changed_option) | ||
| 1469 | { | 1515 | { |
| 1470 | struct sk_buff *skb; | 1516 | struct sk_buff *skb; |
| 1471 | int err; | 1517 | int err; |
| @@ -1475,8 +1521,7 @@ static int team_nl_send_event_options_get(struct team *team, | |||
| 1475 | if (!skb) | 1521 | if (!skb) |
| 1476 | return -ENOMEM; | 1522 | return -ENOMEM; |
| 1477 | 1523 | ||
| 1478 | err = team_nl_fill_options_get_changed(skb, 0, 0, 0, team, | 1524 | err = team_nl_fill_options_get(skb, 0, 0, 0, team, false); |
| 1479 | changed_option); | ||
| 1480 | if (err < 0) | 1525 | if (err < 0) |
| 1481 | goto err_fill; | 1526 | goto err_fill; |
| 1482 | 1527 | ||
| @@ -1489,18 +1534,17 @@ err_fill: | |||
| 1489 | return err; | 1534 | return err; |
| 1490 | } | 1535 | } |
| 1491 | 1536 | ||
| 1492 | static int team_nl_send_event_port_list_get(struct team_port *port) | 1537 | static int team_nl_send_event_port_list_get(struct team *team) |
| 1493 | { | 1538 | { |
| 1494 | struct sk_buff *skb; | 1539 | struct sk_buff *skb; |
| 1495 | int err; | 1540 | int err; |
| 1496 | struct net *net = dev_net(port->team->dev); | 1541 | struct net *net = dev_net(team->dev); |
| 1497 | 1542 | ||
| 1498 | skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); | 1543 | skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); |
| 1499 | if (!skb) | 1544 | if (!skb) |
| 1500 | return -ENOMEM; | 1545 | return -ENOMEM; |
| 1501 | 1546 | ||
| 1502 | err = team_nl_fill_port_list_get_changed(skb, 0, 0, 0, | 1547 | err = team_nl_fill_port_list_get(skb, 0, 0, 0, team, false); |
| 1503 | port->team, port); | ||
| 1504 | if (err < 0) | 1548 | if (err < 0) |
| 1505 | goto err_fill; | 1549 | goto err_fill; |
| 1506 | 1550 | ||
| @@ -1544,12 +1588,11 @@ static void team_nl_fini(void) | |||
| 1544 | * Change checkers | 1588 | * Change checkers |
| 1545 | ******************/ | 1589 | ******************/ |
| 1546 | 1590 | ||
| 1547 | static void __team_options_change_check(struct team *team, | 1591 | static void __team_options_change_check(struct team *team) |
| 1548 | struct team_option *changed_option) | ||
| 1549 | { | 1592 | { |
| 1550 | int err; | 1593 | int err; |
| 1551 | 1594 | ||
| 1552 | err = team_nl_send_event_options_get(team, changed_option); | 1595 | err = team_nl_send_event_options_get(team); |
| 1553 | if (err) | 1596 | if (err) |
| 1554 | netdev_warn(team->dev, "Failed to send options change via netlink\n"); | 1597 | netdev_warn(team->dev, "Failed to send options change via netlink\n"); |
| 1555 | } | 1598 | } |
| @@ -1559,9 +1602,10 @@ static void __team_port_change_check(struct team_port *port, bool linkup) | |||
| 1559 | { | 1602 | { |
| 1560 | int err; | 1603 | int err; |
| 1561 | 1604 | ||
| 1562 | if (port->linkup == linkup) | 1605 | if (!port->removed && port->linkup == linkup) |
| 1563 | return; | 1606 | return; |
| 1564 | 1607 | ||
| 1608 | port->changed = true; | ||
| 1565 | port->linkup = linkup; | 1609 | port->linkup = linkup; |
| 1566 | if (linkup) { | 1610 | if (linkup) { |
| 1567 | struct ethtool_cmd ecmd; | 1611 | struct ethtool_cmd ecmd; |
| @@ -1577,7 +1621,7 @@ static void __team_port_change_check(struct team_port *port, bool linkup) | |||
| 1577 | port->duplex = 0; | 1621 | port->duplex = 0; |
| 1578 | 1622 | ||
| 1579 | send_event: | 1623 | send_event: |
| 1580 | err = team_nl_send_event_port_list_get(port); | 1624 | err = team_nl_send_event_port_list_get(port->team); |
| 1581 | if (err) | 1625 | if (err) |
| 1582 | netdev_warn(port->team->dev, "Failed to send port change of device %s via netlink\n", | 1626 | netdev_warn(port->team->dev, "Failed to send port change of device %s via netlink\n", |
| 1583 | port->dev->name); | 1627 | port->dev->name); |
