Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ipv6: remove RTNL protection from ip6addrlbl_dump()

No longer hold RTNL while calling ip6addrlbl_dump()
("ip addrlabel show")

ip6addrlbl_dump() was already mostly relying on RCU anyway.

Add READ_ONCE()/WRITE_ONCE() annotations around
net->ipv6.ip6addrlbl_table.seq

Note that ifal_seq value is currently ignored in iproute2,
and a bit weak.

We might user later cb->seq / nl_dump_check_consistent()
protocol if needed.

Also change return value for a completed dump,
so that NLMSG_DONE can be appended to current skb,
saving one recvmsg() system call.

v2: read net->ipv6.ip6addrlbl_table.seq once, (David Ahern)

Signed-off-by: Eric Dumazet <edumazet@google.com>
Link:https://lore.kernel.org/netdev/67f5cb70-14a4-4455-8372-f039da2f15c2@kernel.org/
Reviewed-by: David Ahern <dsahern@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Eric Dumazet and committed by
David S. Miller
eec53cc3 802e12ff

+11 -7
+11 -7
net/ipv6/addrlabel.c
··· 234 234 hlist_add_head_rcu(&newp->list, &net->ipv6.ip6addrlbl_table.head); 235 235 out: 236 236 if (!ret) 237 - net->ipv6.ip6addrlbl_table.seq++; 237 + WRITE_ONCE(net->ipv6.ip6addrlbl_table.seq, 238 + net->ipv6.ip6addrlbl_table.seq + 1); 238 239 return ret; 239 240 } 240 241 ··· 446 445 }; 447 446 448 447 static int ip6addrlbl_fill(struct sk_buff *skb, 449 - struct ip6addrlbl_entry *p, 448 + const struct ip6addrlbl_entry *p, 450 449 u32 lseq, 451 450 u32 portid, u32 seq, int event, 452 451 unsigned int flags) ··· 499 498 struct net *net = sock_net(skb->sk); 500 499 struct ip6addrlbl_entry *p; 501 500 int idx = 0, s_idx = cb->args[0]; 502 - int err; 501 + int err = 0; 502 + u32 lseq; 503 503 504 504 if (cb->strict_check) { 505 505 err = ip6addrlbl_valid_dump_req(nlh, cb->extack); ··· 509 507 } 510 508 511 509 rcu_read_lock(); 510 + lseq = READ_ONCE(net->ipv6.ip6addrlbl_table.seq); 512 511 hlist_for_each_entry_rcu(p, &net->ipv6.ip6addrlbl_table.head, list) { 513 512 if (idx >= s_idx) { 514 513 err = ip6addrlbl_fill(skb, p, 515 - net->ipv6.ip6addrlbl_table.seq, 514 + lseq, 516 515 NETLINK_CB(cb->skb).portid, 517 516 nlh->nlmsg_seq, 518 517 RTM_NEWADDRLABEL, ··· 525 522 } 526 523 rcu_read_unlock(); 527 524 cb->args[0] = idx; 528 - return skb->len; 525 + return err; 529 526 } 530 527 531 528 static inline int ip6addrlbl_msgsize(void) ··· 617 614 618 615 rcu_read_lock(); 619 616 p = __ipv6_addr_label(net, addr, ipv6_addr_type(addr), ifal->ifal_index); 620 - lseq = net->ipv6.ip6addrlbl_table.seq; 617 + lseq = READ_ONCE(net->ipv6.ip6addrlbl_table.seq); 621 618 if (p) 622 619 err = ip6addrlbl_fill(skb, p, lseq, 623 620 NETLINK_CB(in_skb).portid, ··· 650 647 return ret; 651 648 ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETADDRLABEL, 652 649 ip6addrlbl_get, 653 - ip6addrlbl_dump, RTNL_FLAG_DOIT_UNLOCKED); 650 + ip6addrlbl_dump, RTNL_FLAG_DOIT_UNLOCKED | 651 + RTNL_FLAG_DUMP_UNLOCKED); 654 652 return ret; 655 653 }