Commit 2a02f837 authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller

ipv6: use xa_array iterator to implement inet6_netconf_dump_devconf()

1) inet6_netconf_dump_devconf() can run under RCU protection
   instead of RTNL.

2) properly return 0 at the end of a dump, avoiding an
   an extra recvmsg() system call.

3) Do not use inet6_base_seq() anymore, for_each_netdev_dump()
   has nice properties. Restarting a GETDEVCONF dump if a device has
   been added/removed or if net->ipv6.dev_addr_genid has changed is moot.
Signed-off-by: default avatarEric Dumazet <edumazet@google.com>
Reviewed-by: default avatarJiri Pirko <jiri@nvidia.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 2f0ff05a
...@@ -727,17 +727,18 @@ static u32 inet6_base_seq(const struct net *net) ...@@ -727,17 +727,18 @@ static u32 inet6_base_seq(const struct net *net)
return res; return res;
} }
static int inet6_netconf_dump_devconf(struct sk_buff *skb, static int inet6_netconf_dump_devconf(struct sk_buff *skb,
struct netlink_callback *cb) struct netlink_callback *cb)
{ {
const struct nlmsghdr *nlh = cb->nlh; const struct nlmsghdr *nlh = cb->nlh;
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
int h, s_h; struct {
int idx, s_idx; unsigned long ifindex;
unsigned int all_default;
} *ctx = (void *)cb->ctx;
struct net_device *dev; struct net_device *dev;
struct inet6_dev *idev; struct inet6_dev *idev;
struct hlist_head *head; int err = 0;
if (cb->strict_check) { if (cb->strict_check) {
struct netlink_ext_ack *extack = cb->extack; struct netlink_ext_ack *extack = cb->extack;
...@@ -754,64 +755,48 @@ static int inet6_netconf_dump_devconf(struct sk_buff *skb, ...@@ -754,64 +755,48 @@ static int inet6_netconf_dump_devconf(struct sk_buff *skb,
} }
} }
s_h = cb->args[0]; rcu_read_lock();
s_idx = idx = cb->args[1]; for_each_netdev_dump(net, dev, ctx->ifindex) {
idev = __in6_dev_get(dev);
for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { if (!idev)
idx = 0; continue;
head = &net->dev_index_head[h]; err = inet6_netconf_fill_devconf(skb, dev->ifindex,
rcu_read_lock(); &idev->cnf,
cb->seq = inet6_base_seq(net); NETLINK_CB(cb->skb).portid,
hlist_for_each_entry_rcu(dev, head, index_hlist) { nlh->nlmsg_seq,
if (idx < s_idx) RTM_NEWNETCONF,
goto cont; NLM_F_MULTI,
idev = __in6_dev_get(dev); NETCONFA_ALL);
if (!idev) if (err < 0)
goto cont; goto done;
if (inet6_netconf_fill_devconf(skb, dev->ifindex,
&idev->cnf,
NETLINK_CB(cb->skb).portid,
nlh->nlmsg_seq,
RTM_NEWNETCONF,
NLM_F_MULTI,
NETCONFA_ALL) < 0) {
rcu_read_unlock();
goto done;
}
nl_dump_check_consistent(cb, nlmsg_hdr(skb));
cont:
idx++;
}
rcu_read_unlock();
} }
if (h == NETDEV_HASHENTRIES) { if (ctx->all_default == 0) {
if (inet6_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL, err = inet6_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL,
net->ipv6.devconf_all, net->ipv6.devconf_all,
NETLINK_CB(cb->skb).portid, NETLINK_CB(cb->skb).portid,
nlh->nlmsg_seq, nlh->nlmsg_seq,
RTM_NEWNETCONF, NLM_F_MULTI, RTM_NEWNETCONF, NLM_F_MULTI,
NETCONFA_ALL) < 0) NETCONFA_ALL);
if (err < 0)
goto done; goto done;
else ctx->all_default++;
h++; }
} if (ctx->all_default == 1) {
if (h == NETDEV_HASHENTRIES + 1) { err = inet6_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT,
if (inet6_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT, net->ipv6.devconf_dflt,
net->ipv6.devconf_dflt, NETLINK_CB(cb->skb).portid,
NETLINK_CB(cb->skb).portid, nlh->nlmsg_seq,
nlh->nlmsg_seq, RTM_NEWNETCONF, NLM_F_MULTI,
RTM_NEWNETCONF, NLM_F_MULTI, NETCONFA_ALL);
NETCONFA_ALL) < 0) if (err < 0)
goto done; goto done;
else ctx->all_default++;
h++;
} }
done: done:
cb->args[0] = h; if (err < 0 && likely(skb->len))
cb->args[1] = idx; err = skb->len;
rcu_read_unlock();
return skb->len; return err;
} }
#ifdef CONFIG_SYSCTL #ifdef CONFIG_SYSCTL
...@@ -7503,7 +7488,8 @@ int __init addrconf_init(void) ...@@ -7503,7 +7488,8 @@ int __init addrconf_init(void)
err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETNETCONF, err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETNETCONF,
inet6_netconf_get_devconf, inet6_netconf_get_devconf,
inet6_netconf_dump_devconf, inet6_netconf_dump_devconf,
RTNL_FLAG_DOIT_UNLOCKED); RTNL_FLAG_DOIT_UNLOCKED |
RTNL_FLAG_DUMP_UNLOCKED);
if (err < 0) if (err < 0)
goto errout; goto errout;
err = ipv6_addr_label_rtnl_register(); err = ipv6_addr_label_rtnl_register();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment