diff options
Diffstat (limited to 'drivers/net/team')
-rw-r--r-- | drivers/net/team/team.c | 198 |
1 files changed, 116 insertions, 82 deletions
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index bc76f946e071..9e9d3e57a2f6 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c | |||
@@ -1571,102 +1571,128 @@ err_fill: | |||
1571 | return err; | 1571 | return err; |
1572 | } | 1572 | } |
1573 | 1573 | ||
1574 | typedef int team_nl_send_func_t(struct sk_buff *skb, | ||
1575 | struct team *team, u32 pid); | ||
1576 | |||
1577 | static int team_nl_send_unicast(struct sk_buff *skb, struct team *team, u32 pid) | ||
1578 | { | ||
1579 | return genlmsg_unicast(dev_net(team->dev), skb, pid); | ||
1580 | } | ||
1581 | |||
1574 | static int team_nl_fill_one_option_get(struct sk_buff *skb, struct team *team, | 1582 | static int team_nl_fill_one_option_get(struct sk_buff *skb, struct team *team, |
1575 | struct team_option_inst *opt_inst) | 1583 | struct team_option_inst *opt_inst) |
1576 | { | 1584 | { |
1577 | struct nlattr *option_item; | 1585 | struct nlattr *option_item; |
1578 | struct team_option *option = opt_inst->option; | 1586 | struct team_option *option = opt_inst->option; |
1579 | struct team_option_inst_info *opt_inst_info; | 1587 | struct team_option_inst_info *opt_inst_info = &opt_inst->info; |
1580 | struct team_gsetter_ctx ctx; | 1588 | struct team_gsetter_ctx ctx; |
1581 | int err; | 1589 | int err; |
1582 | 1590 | ||
1591 | ctx.info = opt_inst_info; | ||
1592 | err = team_option_get(team, opt_inst, &ctx); | ||
1593 | if (err) | ||
1594 | return err; | ||
1595 | |||
1583 | option_item = nla_nest_start(skb, TEAM_ATTR_ITEM_OPTION); | 1596 | option_item = nla_nest_start(skb, TEAM_ATTR_ITEM_OPTION); |
1584 | if (!option_item) | 1597 | if (!option_item) |
1585 | goto nla_put_failure; | 1598 | return -EMSGSIZE; |
1586 | if (nla_put_string(skb, TEAM_ATTR_OPTION_NAME, option->name)) | ||
1587 | goto nla_put_failure; | ||
1588 | if (opt_inst->changed) { | ||
1589 | if (nla_put_flag(skb, TEAM_ATTR_OPTION_CHANGED)) | ||
1590 | goto nla_put_failure; | ||
1591 | opt_inst->changed = false; | ||
1592 | } | ||
1593 | if (opt_inst->removed && nla_put_flag(skb, TEAM_ATTR_OPTION_REMOVED)) | ||
1594 | goto nla_put_failure; | ||
1595 | 1599 | ||
1596 | opt_inst_info = &opt_inst->info; | 1600 | if (nla_put_string(skb, TEAM_ATTR_OPTION_NAME, option->name)) |
1601 | goto nest_cancel; | ||
1597 | if (opt_inst_info->port && | 1602 | if (opt_inst_info->port && |
1598 | nla_put_u32(skb, TEAM_ATTR_OPTION_PORT_IFINDEX, | 1603 | nla_put_u32(skb, TEAM_ATTR_OPTION_PORT_IFINDEX, |
1599 | opt_inst_info->port->dev->ifindex)) | 1604 | opt_inst_info->port->dev->ifindex)) |
1600 | goto nla_put_failure; | 1605 | goto nest_cancel; |
1601 | if (opt_inst->option->array_size && | 1606 | if (opt_inst->option->array_size && |
1602 | nla_put_u32(skb, TEAM_ATTR_OPTION_ARRAY_INDEX, | 1607 | nla_put_u32(skb, TEAM_ATTR_OPTION_ARRAY_INDEX, |
1603 | opt_inst_info->array_index)) | 1608 | opt_inst_info->array_index)) |
1604 | goto nla_put_failure; | 1609 | goto nest_cancel; |
1605 | ctx.info = opt_inst_info; | ||
1606 | 1610 | ||
1607 | switch (option->type) { | 1611 | switch (option->type) { |
1608 | case TEAM_OPTION_TYPE_U32: | 1612 | case TEAM_OPTION_TYPE_U32: |
1609 | if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32)) | 1613 | if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32)) |
1610 | goto nla_put_failure; | 1614 | goto nest_cancel; |
1611 | err = team_option_get(team, opt_inst, &ctx); | ||
1612 | if (err) | ||
1613 | goto errout; | ||
1614 | if (nla_put_u32(skb, TEAM_ATTR_OPTION_DATA, ctx.data.u32_val)) | 1615 | if (nla_put_u32(skb, TEAM_ATTR_OPTION_DATA, ctx.data.u32_val)) |
1615 | goto nla_put_failure; | 1616 | goto nest_cancel; |
1616 | break; | 1617 | break; |
1617 | case TEAM_OPTION_TYPE_STRING: | 1618 | case TEAM_OPTION_TYPE_STRING: |
1618 | if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_STRING)) | 1619 | if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_STRING)) |
1619 | goto nla_put_failure; | 1620 | goto nest_cancel; |
1620 | err = team_option_get(team, opt_inst, &ctx); | ||
1621 | if (err) | ||
1622 | goto errout; | ||
1623 | if (nla_put_string(skb, TEAM_ATTR_OPTION_DATA, | 1621 | if (nla_put_string(skb, TEAM_ATTR_OPTION_DATA, |
1624 | ctx.data.str_val)) | 1622 | ctx.data.str_val)) |
1625 | goto nla_put_failure; | 1623 | goto nest_cancel; |
1626 | break; | 1624 | break; |
1627 | case TEAM_OPTION_TYPE_BINARY: | 1625 | case TEAM_OPTION_TYPE_BINARY: |
1628 | if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_BINARY)) | 1626 | if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_BINARY)) |
1629 | goto nla_put_failure; | 1627 | goto nest_cancel; |
1630 | err = team_option_get(team, opt_inst, &ctx); | ||
1631 | if (err) | ||
1632 | goto errout; | ||
1633 | if (nla_put(skb, TEAM_ATTR_OPTION_DATA, ctx.data.bin_val.len, | 1628 | if (nla_put(skb, TEAM_ATTR_OPTION_DATA, ctx.data.bin_val.len, |
1634 | ctx.data.bin_val.ptr)) | 1629 | ctx.data.bin_val.ptr)) |
1635 | goto nla_put_failure; | 1630 | goto nest_cancel; |
1636 | break; | 1631 | break; |
1637 | case TEAM_OPTION_TYPE_BOOL: | 1632 | case TEAM_OPTION_TYPE_BOOL: |
1638 | if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_FLAG)) | 1633 | if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_FLAG)) |
1639 | goto nla_put_failure; | 1634 | goto nest_cancel; |
1640 | err = team_option_get(team, opt_inst, &ctx); | ||
1641 | if (err) | ||
1642 | goto errout; | ||
1643 | if (ctx.data.bool_val && | 1635 | if (ctx.data.bool_val && |
1644 | nla_put_flag(skb, TEAM_ATTR_OPTION_DATA)) | 1636 | nla_put_flag(skb, TEAM_ATTR_OPTION_DATA)) |
1645 | goto nla_put_failure; | 1637 | goto nest_cancel; |
1646 | break; | 1638 | break; |
1647 | default: | 1639 | default: |
1648 | BUG(); | 1640 | BUG(); |
1649 | } | 1641 | } |
1642 | if (opt_inst->removed && nla_put_flag(skb, TEAM_ATTR_OPTION_REMOVED)) | ||
1643 | goto nest_cancel; | ||
1644 | if (opt_inst->changed) { | ||
1645 | if (nla_put_flag(skb, TEAM_ATTR_OPTION_CHANGED)) | ||
1646 | goto nest_cancel; | ||
1647 | opt_inst->changed = false; | ||
1648 | } | ||
1650 | nla_nest_end(skb, option_item); | 1649 | nla_nest_end(skb, option_item); |
1651 | return 0; | 1650 | return 0; |
1652 | 1651 | ||
1653 | nla_put_failure: | 1652 | nest_cancel: |
1654 | err = -EMSGSIZE; | 1653 | nla_nest_cancel(skb, option_item); |
1655 | errout: | 1654 | return -EMSGSIZE; |
1656 | return err; | 1655 | } |
1656 | |||
1657 | static int __send_and_alloc_skb(struct sk_buff **pskb, | ||
1658 | struct team *team, u32 pid, | ||
1659 | team_nl_send_func_t *send_func) | ||
1660 | { | ||
1661 | int err; | ||
1662 | |||
1663 | if (*pskb) { | ||
1664 | err = send_func(*pskb, team, pid); | ||
1665 | if (err) | ||
1666 | return err; | ||
1667 | } | ||
1668 | *pskb = genlmsg_new(NLMSG_DEFAULT_SIZE - GENL_HDRLEN, GFP_KERNEL); | ||
1669 | if (!*pskb) | ||
1670 | return -ENOMEM; | ||
1671 | return 0; | ||
1657 | } | 1672 | } |
1658 | 1673 | ||
1659 | static int team_nl_fill_options_get(struct sk_buff *skb, | 1674 | static int team_nl_send_options_get(struct team *team, u32 pid, u32 seq, |
1660 | u32 pid, u32 seq, int flags, | 1675 | int flags, team_nl_send_func_t *send_func, |
1661 | struct team *team, | ||
1662 | struct list_head *sel_opt_inst_list) | 1676 | struct list_head *sel_opt_inst_list) |
1663 | { | 1677 | { |
1664 | struct nlattr *option_list; | 1678 | struct nlattr *option_list; |
1679 | struct nlmsghdr *nlh; | ||
1665 | void *hdr; | 1680 | void *hdr; |
1666 | struct team_option_inst *opt_inst; | 1681 | struct team_option_inst *opt_inst; |
1667 | int err; | 1682 | int err; |
1683 | struct sk_buff *skb = NULL; | ||
1684 | bool incomplete; | ||
1685 | int i; | ||
1668 | 1686 | ||
1669 | hdr = genlmsg_put(skb, pid, seq, &team_nl_family, flags, | 1687 | opt_inst = list_first_entry(sel_opt_inst_list, |
1688 | struct team_option_inst, tmp_list); | ||
1689 | |||
1690 | start_again: | ||
1691 | err = __send_and_alloc_skb(&skb, team, pid, send_func); | ||
1692 | if (err) | ||
1693 | return err; | ||
1694 | |||
1695 | hdr = genlmsg_put(skb, pid, seq, &team_nl_family, flags | NLM_F_MULTI, | ||
1670 | TEAM_CMD_OPTIONS_GET); | 1696 | TEAM_CMD_OPTIONS_GET); |
1671 | if (IS_ERR(hdr)) | 1697 | if (IS_ERR(hdr)) |
1672 | return PTR_ERR(hdr); | 1698 | return PTR_ERR(hdr); |
@@ -1677,46 +1703,62 @@ static int team_nl_fill_options_get(struct sk_buff *skb, | |||
1677 | if (!option_list) | 1703 | if (!option_list) |
1678 | goto nla_put_failure; | 1704 | goto nla_put_failure; |
1679 | 1705 | ||
1680 | list_for_each_entry(opt_inst, sel_opt_inst_list, tmp_list) { | 1706 | i = 0; |
1707 | incomplete = false; | ||
1708 | list_for_each_entry_from(opt_inst, sel_opt_inst_list, tmp_list) { | ||
1681 | err = team_nl_fill_one_option_get(skb, team, opt_inst); | 1709 | err = team_nl_fill_one_option_get(skb, team, opt_inst); |
1682 | if (err) | 1710 | if (err) { |
1711 | if (err == -EMSGSIZE) { | ||
1712 | if (!i) | ||
1713 | goto errout; | ||
1714 | incomplete = true; | ||
1715 | break; | ||
1716 | } | ||
1683 | goto errout; | 1717 | goto errout; |
1718 | } | ||
1719 | i++; | ||
1684 | } | 1720 | } |
1685 | 1721 | ||
1686 | nla_nest_end(skb, option_list); | 1722 | nla_nest_end(skb, option_list); |
1687 | return genlmsg_end(skb, hdr); | 1723 | genlmsg_end(skb, hdr); |
1724 | if (incomplete) | ||
1725 | goto start_again; | ||
1726 | |||
1727 | send_done: | ||
1728 | nlh = nlmsg_put(skb, pid, seq, NLMSG_DONE, 0, flags | NLM_F_MULTI); | ||
1729 | if (!nlh) { | ||
1730 | err = __send_and_alloc_skb(&skb, team, pid, send_func); | ||
1731 | if (err) | ||
1732 | goto errout; | ||
1733 | goto send_done; | ||
1734 | } | ||
1735 | |||
1736 | return send_func(skb, team, pid); | ||
1688 | 1737 | ||
1689 | nla_put_failure: | 1738 | nla_put_failure: |
1690 | err = -EMSGSIZE; | 1739 | err = -EMSGSIZE; |
1691 | errout: | 1740 | errout: |
1692 | genlmsg_cancel(skb, hdr); | 1741 | genlmsg_cancel(skb, hdr); |
1742 | nlmsg_free(skb); | ||
1693 | return err; | 1743 | return err; |
1694 | } | 1744 | } |
1695 | 1745 | ||
1696 | static int team_nl_fill_options_get_all(struct sk_buff *skb, | ||
1697 | struct genl_info *info, int flags, | ||
1698 | struct team *team) | ||
1699 | { | ||
1700 | struct team_option_inst *opt_inst; | ||
1701 | LIST_HEAD(sel_opt_inst_list); | ||
1702 | |||
1703 | list_for_each_entry(opt_inst, &team->option_inst_list, list) | ||
1704 | list_add_tail(&opt_inst->tmp_list, &sel_opt_inst_list); | ||
1705 | return team_nl_fill_options_get(skb, info->snd_pid, | ||
1706 | info->snd_seq, NLM_F_ACK, | ||
1707 | team, &sel_opt_inst_list); | ||
1708 | } | ||
1709 | |||
1710 | static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info) | 1746 | static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info) |
1711 | { | 1747 | { |
1712 | struct team *team; | 1748 | struct team *team; |
1749 | struct team_option_inst *opt_inst; | ||
1713 | int err; | 1750 | int err; |
1751 | LIST_HEAD(sel_opt_inst_list); | ||
1714 | 1752 | ||
1715 | team = team_nl_team_get(info); | 1753 | team = team_nl_team_get(info); |
1716 | if (!team) | 1754 | if (!team) |
1717 | return -EINVAL; | 1755 | return -EINVAL; |
1718 | 1756 | ||
1719 | err = team_nl_send_generic(info, team, team_nl_fill_options_get_all); | 1757 | list_for_each_entry(opt_inst, &team->option_inst_list, list) |
1758 | list_add_tail(&opt_inst->tmp_list, &sel_opt_inst_list); | ||
1759 | err = team_nl_send_options_get(team, info->snd_pid, info->snd_seq, | ||
1760 | NLM_F_ACK, team_nl_send_unicast, | ||
1761 | &sel_opt_inst_list); | ||
1720 | 1762 | ||
1721 | team_nl_team_put(team); | 1763 | team_nl_team_put(team); |
1722 | 1764 | ||
@@ -1963,28 +2005,18 @@ static struct genl_multicast_group team_change_event_mcgrp = { | |||
1963 | .name = TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME, | 2005 | .name = TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME, |
1964 | }; | 2006 | }; |
1965 | 2007 | ||
2008 | static int team_nl_send_multicast(struct sk_buff *skb, | ||
2009 | struct team *team, u32 pid) | ||
2010 | { | ||
2011 | return genlmsg_multicast_netns(dev_net(team->dev), skb, 0, | ||
2012 | team_change_event_mcgrp.id, GFP_KERNEL); | ||
2013 | } | ||
2014 | |||
1966 | static int team_nl_send_event_options_get(struct team *team, | 2015 | static int team_nl_send_event_options_get(struct team *team, |
1967 | struct list_head *sel_opt_inst_list) | 2016 | struct list_head *sel_opt_inst_list) |
1968 | { | 2017 | { |
1969 | struct sk_buff *skb; | 2018 | return team_nl_send_options_get(team, 0, 0, 0, team_nl_send_multicast, |
1970 | int err; | 2019 | sel_opt_inst_list); |
1971 | struct net *net = dev_net(team->dev); | ||
1972 | |||
1973 | skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); | ||
1974 | if (!skb) | ||
1975 | return -ENOMEM; | ||
1976 | |||
1977 | err = team_nl_fill_options_get(skb, 0, 0, 0, team, sel_opt_inst_list); | ||
1978 | if (err < 0) | ||
1979 | goto err_fill; | ||
1980 | |||
1981 | err = genlmsg_multicast_netns(net, skb, 0, team_change_event_mcgrp.id, | ||
1982 | GFP_KERNEL); | ||
1983 | return err; | ||
1984 | |||
1985 | err_fill: | ||
1986 | nlmsg_free(skb); | ||
1987 | return err; | ||
1988 | } | 2020 | } |
1989 | 2021 | ||
1990 | static int team_nl_send_event_port_list_get(struct team *team) | 2022 | static int team_nl_send_event_port_list_get(struct team *team) |
@@ -2053,7 +2085,8 @@ static void __team_options_change_check(struct team *team) | |||
2053 | } | 2085 | } |
2054 | err = team_nl_send_event_options_get(team, &sel_opt_inst_list); | 2086 | err = team_nl_send_event_options_get(team, &sel_opt_inst_list); |
2055 | if (err) | 2087 | if (err) |
2056 | netdev_warn(team->dev, "Failed to send options change via netlink\n"); | 2088 | netdev_warn(team->dev, "Failed to send options change via netlink (err %d)\n", |
2089 | err); | ||
2057 | } | 2090 | } |
2058 | 2091 | ||
2059 | static void __team_option_inst_change(struct team *team, | 2092 | static void __team_option_inst_change(struct team *team, |
@@ -2066,7 +2099,8 @@ static void __team_option_inst_change(struct team *team, | |||
2066 | list_add(&sel_opt_inst->tmp_list, &sel_opt_inst_list); | 2099 | list_add(&sel_opt_inst->tmp_list, &sel_opt_inst_list); |
2067 | err = team_nl_send_event_options_get(team, &sel_opt_inst_list); | 2100 | err = team_nl_send_event_options_get(team, &sel_opt_inst_list); |
2068 | if (err) | 2101 | if (err) |
2069 | netdev_warn(team->dev, "Failed to send option change via netlink\n"); | 2102 | netdev_warn(team->dev, "Failed to send option change via netlink (err %d)\n", |
2103 | err); | ||
2070 | } | 2104 | } |
2071 | 2105 | ||
2072 | /* rtnl lock is held */ | 2106 | /* rtnl lock is held */ |