Merge branch 'rtnetlink_vf_ports'

David Gibson says:

====================
Fix problems with with IFLA_VF_PORTS (v2)

I've had a customer encounter a problem with getifaddrs(3) freezing up
on a system with a Cisco enic device.

I've discovered that the problem is caused by an enic device with a
large number of SR-IOV virtual functions overflowing the normal sized
packet buffer for netlink, leading to interfaces not being reported
from an RTM_GETLINK request.

The first patch here just makes the problem easier to locate if it
occurs again in a different way, by adding a WARN_ON() when we run out
of room in a netlink packet in this manner.

The second patch actually fixes the problem, by only reporting
IFLA_VF_PORTS information when the RTEXT_FILTER_VF flag is specified.

v2: Corrected some CodingStyle problems
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2014-04-24 13:53:01 -04:00
commit fc5e88354d

View File

@ -774,7 +774,8 @@ static inline int rtnl_vfinfo_size(const struct net_device *dev,
return 0; return 0;
} }
static size_t rtnl_port_size(const struct net_device *dev) static size_t rtnl_port_size(const struct net_device *dev,
u32 ext_filter_mask)
{ {
size_t port_size = nla_total_size(4) /* PORT_VF */ size_t port_size = nla_total_size(4) /* PORT_VF */
+ nla_total_size(PORT_PROFILE_MAX) /* PORT_PROFILE */ + nla_total_size(PORT_PROFILE_MAX) /* PORT_PROFILE */
@ -790,7 +791,8 @@ static size_t rtnl_port_size(const struct net_device *dev)
size_t port_self_size = nla_total_size(sizeof(struct nlattr)) size_t port_self_size = nla_total_size(sizeof(struct nlattr))
+ port_size; + port_size;
if (!dev->netdev_ops->ndo_get_vf_port || !dev->dev.parent) if (!dev->netdev_ops->ndo_get_vf_port || !dev->dev.parent ||
!(ext_filter_mask & RTEXT_FILTER_VF))
return 0; return 0;
if (dev_num_vf(dev->dev.parent)) if (dev_num_vf(dev->dev.parent))
return port_self_size + vf_ports_size + return port_self_size + vf_ports_size +
@ -826,7 +828,7 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev,
+ nla_total_size(ext_filter_mask + nla_total_size(ext_filter_mask
& RTEXT_FILTER_VF ? 4 : 0) /* IFLA_NUM_VF */ & RTEXT_FILTER_VF ? 4 : 0) /* IFLA_NUM_VF */
+ rtnl_vfinfo_size(dev, ext_filter_mask) /* IFLA_VFINFO_LIST */ + rtnl_vfinfo_size(dev, ext_filter_mask) /* IFLA_VFINFO_LIST */
+ rtnl_port_size(dev) /* IFLA_VF_PORTS + IFLA_PORT_SELF */ + rtnl_port_size(dev, ext_filter_mask) /* IFLA_VF_PORTS + IFLA_PORT_SELF */
+ rtnl_link_get_size(dev) /* IFLA_LINKINFO */ + rtnl_link_get_size(dev) /* IFLA_LINKINFO */
+ rtnl_link_get_af_size(dev) /* IFLA_AF_SPEC */ + rtnl_link_get_af_size(dev) /* IFLA_AF_SPEC */
+ nla_total_size(MAX_PHYS_PORT_ID_LEN); /* IFLA_PHYS_PORT_ID */ + nla_total_size(MAX_PHYS_PORT_ID_LEN); /* IFLA_PHYS_PORT_ID */
@ -888,11 +890,13 @@ static int rtnl_port_self_fill(struct sk_buff *skb, struct net_device *dev)
return 0; return 0;
} }
static int rtnl_port_fill(struct sk_buff *skb, struct net_device *dev) static int rtnl_port_fill(struct sk_buff *skb, struct net_device *dev,
u32 ext_filter_mask)
{ {
int err; int err;
if (!dev->netdev_ops->ndo_get_vf_port || !dev->dev.parent) if (!dev->netdev_ops->ndo_get_vf_port || !dev->dev.parent ||
!(ext_filter_mask & RTEXT_FILTER_VF))
return 0; return 0;
err = rtnl_port_self_fill(skb, dev); err = rtnl_port_self_fill(skb, dev);
@ -1079,7 +1083,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
nla_nest_end(skb, vfinfo); nla_nest_end(skb, vfinfo);
} }
if (rtnl_port_fill(skb, dev)) if (rtnl_port_fill(skb, dev, ext_filter_mask))
goto nla_put_failure; goto nla_put_failure;
if (dev->rtnl_link_ops || rtnl_have_link_slave_info(dev)) { if (dev->rtnl_link_ops || rtnl_have_link_slave_info(dev)) {
@ -1198,6 +1202,7 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
struct hlist_head *head; struct hlist_head *head;
struct nlattr *tb[IFLA_MAX+1]; struct nlattr *tb[IFLA_MAX+1];
u32 ext_filter_mask = 0; u32 ext_filter_mask = 0;
int err;
s_h = cb->args[0]; s_h = cb->args[0];
s_idx = cb->args[1]; s_idx = cb->args[1];
@ -1218,11 +1223,17 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
hlist_for_each_entry_rcu(dev, head, index_hlist) { hlist_for_each_entry_rcu(dev, head, index_hlist) {
if (idx < s_idx) if (idx < s_idx)
goto cont; goto cont;
if (rtnl_fill_ifinfo(skb, dev, RTM_NEWLINK, err = rtnl_fill_ifinfo(skb, dev, RTM_NEWLINK,
NETLINK_CB(cb->skb).portid, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, 0, cb->nlh->nlmsg_seq, 0,
NLM_F_MULTI, NLM_F_MULTI,
ext_filter_mask) <= 0) ext_filter_mask);
/* If we ran out of room on the first message,
* we're in trouble
*/
WARN_ON((err == -EMSGSIZE) && (skb->len == 0));
if (err <= 0)
goto out; goto out;
nl_dump_check_consistent(cb, nlmsg_hdr(skb)); nl_dump_check_consistent(cb, nlmsg_hdr(skb));