Skip to content

Commit

Permalink
rtnetlink: Compute and store minimum ifinfo dump size
Browse files Browse the repository at this point in the history
The message size allocated for rtnl ifinfo dumps was limited to
a single page.  This is not enough for additional interface info
available with devices that support SR-IOV and caused a bug in
which VF info would not be displayed if more than approximately
40 VFs were created per interface.

Implement a new function pointer for the rtnl_register service that will
calculate the amount of data required for the ifinfo dump and allocate
enough data to satisfy the request.

Signed-off-by: Greg Rose <[email protected]>
Signed-off-by: Jeff Kirsher <[email protected]>
  • Loading branch information
Greg Rose authored and Jeff Kirsher committed Jun 10, 2011
1 parent 929dd04 commit c7ac867
Show file tree
Hide file tree
Showing 30 changed files with 158 additions and 90 deletions.
2 changes: 1 addition & 1 deletion drivers/infiniband/core/netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ static int ibnl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
return -EINVAL;
return netlink_dump_start(nls, skb, nlh,
client->cb_table[op].dump,
NULL);
NULL, 0);
}
}

Expand Down
6 changes: 4 additions & 2 deletions include/linux/netlink.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,8 @@ struct netlink_callback {
int (*dump)(struct sk_buff * skb,
struct netlink_callback *cb);
int (*done)(struct netlink_callback *cb);
int family;
u16 family;
u16 min_dump_alloc;
long args[6];
};

Expand Down Expand Up @@ -259,7 +260,8 @@ __nlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, int type, int len, int flags)
extern int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
const struct nlmsghdr *nlh,
int (*dump)(struct sk_buff *skb, struct netlink_callback*),
int (*done)(struct netlink_callback*));
int (*done)(struct netlink_callback*),
u16 min_dump_alloc);


#define NL_NONROOT_RECV 0x1
Expand Down
7 changes: 5 additions & 2 deletions include/net/rtnetlink.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@

typedef int (*rtnl_doit_func)(struct sk_buff *, struct nlmsghdr *, void *);
typedef int (*rtnl_dumpit_func)(struct sk_buff *, struct netlink_callback *);
typedef u16 (*rtnl_calcit_func)(struct sk_buff *);

extern int __rtnl_register(int protocol, int msgtype,
rtnl_doit_func, rtnl_dumpit_func);
rtnl_doit_func, rtnl_dumpit_func,
rtnl_calcit_func);
extern void rtnl_register(int protocol, int msgtype,
rtnl_doit_func, rtnl_dumpit_func);
rtnl_doit_func, rtnl_dumpit_func,
rtnl_calcit_func);
extern int rtnl_unregister(int protocol, int msgtype);
extern void rtnl_unregister_all(int protocol);

Expand Down
15 changes: 10 additions & 5 deletions net/bridge/br_netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -218,19 +218,24 @@ int __init br_netlink_init(void)
if (err < 0)
goto err1;

err = __rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, br_dump_ifinfo);
err = __rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL,
br_dump_ifinfo, NULL);
if (err)
goto err2;
err = __rtnl_register(PF_BRIDGE, RTM_SETLINK, br_rtm_setlink, NULL);
err = __rtnl_register(PF_BRIDGE, RTM_SETLINK,
br_rtm_setlink, NULL, NULL);
if (err)
goto err3;
err = __rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, br_fdb_add, NULL);
err = __rtnl_register(PF_BRIDGE, RTM_NEWNEIGH,
br_fdb_add, NULL, NULL);
if (err)
goto err3;
err = __rtnl_register(PF_BRIDGE, RTM_DELNEIGH, br_fdb_delete, NULL);
err = __rtnl_register(PF_BRIDGE, RTM_DELNEIGH,
br_fdb_delete, NULL, NULL);
if (err)
goto err3;
err = __rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, br_fdb_dump);
err = __rtnl_register(PF_BRIDGE, RTM_GETNEIGH,
NULL, br_fdb_dump, NULL);
if (err)
goto err3;

Expand Down
6 changes: 3 additions & 3 deletions net/core/fib_rules.c
Original file line number Diff line number Diff line change
Expand Up @@ -740,9 +740,9 @@ static struct pernet_operations fib_rules_net_ops = {
static int __init fib_rules_init(void)
{
int err;
rtnl_register(PF_UNSPEC, RTM_NEWRULE, fib_nl_newrule, NULL);
rtnl_register(PF_UNSPEC, RTM_DELRULE, fib_nl_delrule, NULL);
rtnl_register(PF_UNSPEC, RTM_GETRULE, NULL, fib_nl_dumprule);
rtnl_register(PF_UNSPEC, RTM_NEWRULE, fib_nl_newrule, NULL, NULL);
rtnl_register(PF_UNSPEC, RTM_DELRULE, fib_nl_delrule, NULL, NULL);
rtnl_register(PF_UNSPEC, RTM_GETRULE, NULL, fib_nl_dumprule, NULL);

err = register_pernet_subsys(&fib_rules_net_ops);
if (err < 0)
Expand Down
11 changes: 6 additions & 5 deletions net/core/neighbour.c
Original file line number Diff line number Diff line change
Expand Up @@ -2909,12 +2909,13 @@ EXPORT_SYMBOL(neigh_sysctl_unregister);

static int __init neigh_init(void)
{
rtnl_register(PF_UNSPEC, RTM_NEWNEIGH, neigh_add, NULL);
rtnl_register(PF_UNSPEC, RTM_DELNEIGH, neigh_delete, NULL);
rtnl_register(PF_UNSPEC, RTM_GETNEIGH, NULL, neigh_dump_info);
rtnl_register(PF_UNSPEC, RTM_NEWNEIGH, neigh_add, NULL, NULL);
rtnl_register(PF_UNSPEC, RTM_DELNEIGH, neigh_delete, NULL, NULL);
rtnl_register(PF_UNSPEC, RTM_GETNEIGH, NULL, neigh_dump_info, NULL);

rtnl_register(PF_UNSPEC, RTM_GETNEIGHTBL, NULL, neightbl_dump_info);
rtnl_register(PF_UNSPEC, RTM_SETNEIGHTBL, neightbl_set, NULL);
rtnl_register(PF_UNSPEC, RTM_GETNEIGHTBL, NULL, neightbl_dump_info,
NULL);
rtnl_register(PF_UNSPEC, RTM_SETNEIGHTBL, neightbl_set, NULL, NULL);

return 0;
}
Expand Down
60 changes: 49 additions & 11 deletions net/core/rtnetlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,11 @@
struct rtnl_link {
rtnl_doit_func doit;
rtnl_dumpit_func dumpit;
rtnl_calcit_func calcit;
};

static DEFINE_MUTEX(rtnl_mutex);
static u16 min_ifinfo_dump_size;

void rtnl_lock(void)
{
Expand Down Expand Up @@ -144,12 +146,28 @@ static rtnl_dumpit_func rtnl_get_dumpit(int protocol, int msgindex)
return tab ? tab[msgindex].dumpit : NULL;
}

static rtnl_calcit_func rtnl_get_calcit(int protocol, int msgindex)
{
struct rtnl_link *tab;

if (protocol <= RTNL_FAMILY_MAX)
tab = rtnl_msg_handlers[protocol];
else
tab = NULL;

if (tab == NULL || tab[msgindex].calcit == NULL)
tab = rtnl_msg_handlers[PF_UNSPEC];

return tab ? tab[msgindex].calcit : NULL;
}

/**
* __rtnl_register - Register a rtnetlink message type
* @protocol: Protocol family or PF_UNSPEC
* @msgtype: rtnetlink message type
* @doit: Function pointer called for each request message
* @dumpit: Function pointer called for each dump request (NLM_F_DUMP) message
* @calcit: Function pointer to calc size of dump message
*
* Registers the specified function pointers (at least one of them has
* to be non-NULL) to be called whenever a request message for the
Expand All @@ -162,7 +180,8 @@ static rtnl_dumpit_func rtnl_get_dumpit(int protocol, int msgindex)
* Returns 0 on success or a negative error code.
*/
int __rtnl_register(int protocol, int msgtype,
rtnl_doit_func doit, rtnl_dumpit_func dumpit)
rtnl_doit_func doit, rtnl_dumpit_func dumpit,
rtnl_calcit_func calcit)
{
struct rtnl_link *tab;
int msgindex;
Expand All @@ -185,6 +204,9 @@ int __rtnl_register(int protocol, int msgtype,
if (dumpit)
tab[msgindex].dumpit = dumpit;

if (calcit)
tab[msgindex].calcit = calcit;

return 0;
}
EXPORT_SYMBOL_GPL(__rtnl_register);
Expand All @@ -199,9 +221,10 @@ EXPORT_SYMBOL_GPL(__rtnl_register);
* of memory implies no sense in continuing.
*/
void rtnl_register(int protocol, int msgtype,
rtnl_doit_func doit, rtnl_dumpit_func dumpit)
rtnl_doit_func doit, rtnl_dumpit_func dumpit,
rtnl_calcit_func calcit)
{
if (__rtnl_register(protocol, msgtype, doit, dumpit) < 0)
if (__rtnl_register(protocol, msgtype, doit, dumpit, calcit) < 0)
panic("Unable to register rtnetlink message handler, "
"protocol = %d, message type = %d\n",
protocol, msgtype);
Expand Down Expand Up @@ -1818,6 +1841,11 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
return err;
}

static u16 rtnl_calcit(struct sk_buff *skb)
{
return min_ifinfo_dump_size;
}

static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb)
{
int idx;
Expand Down Expand Up @@ -1847,11 +1875,14 @@ void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change)
struct net *net = dev_net(dev);
struct sk_buff *skb;
int err = -ENOBUFS;
size_t if_info_size;

skb = nlmsg_new(if_nlmsg_size(dev), GFP_KERNEL);
skb = nlmsg_new((if_info_size = if_nlmsg_size(dev)), GFP_KERNEL);
if (skb == NULL)
goto errout;

min_ifinfo_dump_size = max_t(u16, if_info_size, min_ifinfo_dump_size);

err = rtnl_fill_ifinfo(skb, dev, type, 0, 0, change, 0);
if (err < 0) {
/* -EMSGSIZE implies BUG in if_nlmsg_size() */
Expand Down Expand Up @@ -1902,14 +1933,20 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) {
struct sock *rtnl;
rtnl_dumpit_func dumpit;
rtnl_calcit_func calcit;
u16 min_dump_alloc = 0;

dumpit = rtnl_get_dumpit(family, type);
if (dumpit == NULL)
return -EOPNOTSUPP;
calcit = rtnl_get_calcit(family, type);
if (calcit)
min_dump_alloc = calcit(skb);

__rtnl_unlock();
rtnl = net->rtnl;
err = netlink_dump_start(rtnl, skb, nlh, dumpit, NULL);
err = netlink_dump_start(rtnl, skb, nlh, dumpit,
NULL, min_dump_alloc);
rtnl_lock();
return err;
}
Expand Down Expand Up @@ -2019,12 +2056,13 @@ void __init rtnetlink_init(void)
netlink_set_nonroot(NETLINK_ROUTE, NL_NONROOT_RECV);
register_netdevice_notifier(&rtnetlink_dev_notifier);

rtnl_register(PF_UNSPEC, RTM_GETLINK, rtnl_getlink, rtnl_dump_ifinfo);
rtnl_register(PF_UNSPEC, RTM_SETLINK, rtnl_setlink, NULL);
rtnl_register(PF_UNSPEC, RTM_NEWLINK, rtnl_newlink, NULL);
rtnl_register(PF_UNSPEC, RTM_DELLINK, rtnl_dellink, NULL);
rtnl_register(PF_UNSPEC, RTM_GETLINK, rtnl_getlink,
rtnl_dump_ifinfo, rtnl_calcit);
rtnl_register(PF_UNSPEC, RTM_SETLINK, rtnl_setlink, NULL, NULL);
rtnl_register(PF_UNSPEC, RTM_NEWLINK, rtnl_newlink, NULL, NULL);
rtnl_register(PF_UNSPEC, RTM_DELLINK, rtnl_dellink, NULL, NULL);

rtnl_register(PF_UNSPEC, RTM_GETADDR, NULL, rtnl_dump_all);
rtnl_register(PF_UNSPEC, RTM_GETROUTE, NULL, rtnl_dump_all);
rtnl_register(PF_UNSPEC, RTM_GETADDR, NULL, rtnl_dump_all, NULL);
rtnl_register(PF_UNSPEC, RTM_GETROUTE, NULL, rtnl_dump_all, NULL);
}

4 changes: 2 additions & 2 deletions net/dcb/dcbnl.c
Original file line number Diff line number Diff line change
Expand Up @@ -1819,8 +1819,8 @@ static int __init dcbnl_init(void)
{
INIT_LIST_HEAD(&dcb_app_list);

rtnl_register(PF_UNSPEC, RTM_GETDCB, dcb_doit, NULL);
rtnl_register(PF_UNSPEC, RTM_SETDCB, dcb_doit, NULL);
rtnl_register(PF_UNSPEC, RTM_GETDCB, dcb_doit, NULL, NULL);
rtnl_register(PF_UNSPEC, RTM_SETDCB, dcb_doit, NULL, NULL);

return 0;
}
Expand Down
6 changes: 3 additions & 3 deletions net/decnet/dn_dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -1414,9 +1414,9 @@ void __init dn_dev_init(void)

dn_dev_devices_on();

rtnl_register(PF_DECnet, RTM_NEWADDR, dn_nl_newaddr, NULL);
rtnl_register(PF_DECnet, RTM_DELADDR, dn_nl_deladdr, NULL);
rtnl_register(PF_DECnet, RTM_GETADDR, NULL, dn_nl_dump_ifaddr);
rtnl_register(PF_DECnet, RTM_NEWADDR, dn_nl_newaddr, NULL, NULL);
rtnl_register(PF_DECnet, RTM_DELADDR, dn_nl_deladdr, NULL, NULL);
rtnl_register(PF_DECnet, RTM_GETADDR, NULL, dn_nl_dump_ifaddr, NULL);

proc_net_fops_create(&init_net, "decnet_dev", S_IRUGO, &dn_dev_seq_fops);

Expand Down
4 changes: 2 additions & 2 deletions net/decnet/dn_fib.c
Original file line number Diff line number Diff line change
Expand Up @@ -763,8 +763,8 @@ void __init dn_fib_init(void)

register_dnaddr_notifier(&dn_fib_dnaddr_notifier);

rtnl_register(PF_DECnet, RTM_NEWROUTE, dn_fib_rtm_newroute, NULL);
rtnl_register(PF_DECnet, RTM_DELROUTE, dn_fib_rtm_delroute, NULL);
rtnl_register(PF_DECnet, RTM_NEWROUTE, dn_fib_rtm_newroute, NULL, NULL);
rtnl_register(PF_DECnet, RTM_DELROUTE, dn_fib_rtm_delroute, NULL, NULL);
}


5 changes: 3 additions & 2 deletions net/decnet/dn_route.c
Original file line number Diff line number Diff line change
Expand Up @@ -1841,10 +1841,11 @@ void __init dn_route_init(void)
proc_net_fops_create(&init_net, "decnet_cache", S_IRUGO, &dn_rt_cache_seq_fops);

#ifdef CONFIG_DECNET_ROUTER
rtnl_register(PF_DECnet, RTM_GETROUTE, dn_cache_getroute, dn_fib_dump);
rtnl_register(PF_DECnet, RTM_GETROUTE, dn_cache_getroute,
dn_fib_dump, NULL);
#else
rtnl_register(PF_DECnet, RTM_GETROUTE, dn_cache_getroute,
dn_cache_dump);
dn_cache_dump, NULL);
#endif
}

Expand Down
6 changes: 3 additions & 3 deletions net/ipv4/devinet.c
Original file line number Diff line number Diff line change
Expand Up @@ -1833,8 +1833,8 @@ void __init devinet_init(void)

rtnl_af_register(&inet_af_ops);

rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL);
rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL);
rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr);
rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, NULL);
rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, NULL);
rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, NULL);
}

6 changes: 3 additions & 3 deletions net/ipv4/fib_frontend.c
Original file line number Diff line number Diff line change
Expand Up @@ -1124,9 +1124,9 @@ static struct pernet_operations fib_net_ops = {

void __init ip_fib_init(void)
{
rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL);
rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL);
rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib);
rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL, NULL);
rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL, NULL);
rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib, NULL);

register_pernet_subsys(&fib_net_ops);
register_netdevice_notifier(&fib_netdev_notifier);
Expand Down
2 changes: 1 addition & 1 deletion net/ipv4/inet_diag.c
Original file line number Diff line number Diff line change
Expand Up @@ -871,7 +871,7 @@ static int inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
}

return netlink_dump_start(idiagnl, skb, nlh,
inet_diag_dump, NULL);
inet_diag_dump, NULL, 0);
}

return inet_diag_get_exact(skb, nlh);
Expand Down
3 changes: 2 additions & 1 deletion net/ipv4/ipmr.c
Original file line number Diff line number Diff line change
Expand Up @@ -2544,7 +2544,8 @@ int __init ip_mr_init(void)
goto add_proto_fail;
}
#endif
rtnl_register(RTNL_FAMILY_IPMR, RTM_GETROUTE, NULL, ipmr_rtm_dumproute);
rtnl_register(RTNL_FAMILY_IPMR, RTM_GETROUTE,
NULL, ipmr_rtm_dumproute, NULL);
return 0;

#ifdef CONFIG_IP_PIMSM_V2
Expand Down
2 changes: 1 addition & 1 deletion net/ipv4/route.c
Original file line number Diff line number Diff line change
Expand Up @@ -3295,7 +3295,7 @@ int __init ip_rt_init(void)
xfrm_init();
xfrm4_init(ip_rt_max_size);
#endif
rtnl_register(PF_INET, RTM_GETROUTE, inet_rtm_getroute, NULL);
rtnl_register(PF_INET, RTM_GETROUTE, inet_rtm_getroute, NULL, NULL);

#ifdef CONFIG_SYSCTL
register_pernet_subsys(&sysctl_route_ops);
Expand Down
16 changes: 10 additions & 6 deletions net/ipv6/addrconf.c
Original file line number Diff line number Diff line change
Expand Up @@ -4727,16 +4727,20 @@ int __init addrconf_init(void)
if (err < 0)
goto errout_af;

err = __rtnl_register(PF_INET6, RTM_GETLINK, NULL, inet6_dump_ifinfo);
err = __rtnl_register(PF_INET6, RTM_GETLINK, NULL, inet6_dump_ifinfo,
NULL);
if (err < 0)
goto errout;

/* Only the first call to __rtnl_register can fail */
__rtnl_register(PF_INET6, RTM_NEWADDR, inet6_rtm_newaddr, NULL);
__rtnl_register(PF_INET6, RTM_DELADDR, inet6_rtm_deladdr, NULL);
__rtnl_register(PF_INET6, RTM_GETADDR, inet6_rtm_getaddr, inet6_dump_ifaddr);
__rtnl_register(PF_INET6, RTM_GETMULTICAST, NULL, inet6_dump_ifmcaddr);
__rtnl_register(PF_INET6, RTM_GETANYCAST, NULL, inet6_dump_ifacaddr);
__rtnl_register(PF_INET6, RTM_NEWADDR, inet6_rtm_newaddr, NULL, NULL);
__rtnl_register(PF_INET6, RTM_DELADDR, inet6_rtm_deladdr, NULL, NULL);
__rtnl_register(PF_INET6, RTM_GETADDR, inet6_rtm_getaddr,
inet6_dump_ifaddr, NULL);
__rtnl_register(PF_INET6, RTM_GETMULTICAST, NULL,
inet6_dump_ifmcaddr, NULL);
__rtnl_register(PF_INET6, RTM_GETANYCAST, NULL,
inet6_dump_ifacaddr, NULL);

ipv6_addr_label_rtnl_register();

Expand Down
9 changes: 6 additions & 3 deletions net/ipv6/addrlabel.c
Original file line number Diff line number Diff line change
Expand Up @@ -592,8 +592,11 @@ static int ip6addrlbl_get(struct sk_buff *in_skb, struct nlmsghdr* nlh,

void __init ipv6_addr_label_rtnl_register(void)
{
__rtnl_register(PF_INET6, RTM_NEWADDRLABEL, ip6addrlbl_newdel, NULL);
__rtnl_register(PF_INET6, RTM_DELADDRLABEL, ip6addrlbl_newdel, NULL);
__rtnl_register(PF_INET6, RTM_GETADDRLABEL, ip6addrlbl_get, ip6addrlbl_dump);
__rtnl_register(PF_INET6, RTM_NEWADDRLABEL, ip6addrlbl_newdel,
NULL, NULL);
__rtnl_register(PF_INET6, RTM_DELADDRLABEL, ip6addrlbl_newdel,
NULL, NULL);
__rtnl_register(PF_INET6, RTM_GETADDRLABEL, ip6addrlbl_get,
ip6addrlbl_dump, NULL);
}

Loading

0 comments on commit c7ac867

Please sign in to comment.