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

Configure Feed

Select the types of activity you want to include in your feed.

ipv6: move IFA_F_PERMANENT percpu allocation in process scope

Observed at boot time:

CPU: 43 UID: 0 PID: 3595 Comm: (t-daemon) Not tainted 6.12.0 #1
Call Trace:
<TASK>
dump_stack_lvl+0x4e/0x70
pcpu_alloc_noprof.cold+0x1f/0x4b
fib_nh_common_init+0x4c/0x110
fib6_nh_init+0x387/0x740
ip6_route_info_create+0x46d/0x640
addrconf_f6i_alloc+0x13b/0x180
addrconf_permanent_addr+0xd0/0x220
addrconf_notify+0x93/0x540
notifier_call_chain+0x5a/0xd0
__dev_notify_flags+0x5c/0xf0
dev_change_flags+0x54/0x70
do_setlink+0x36c/0xce0
rtnl_setlink+0x11f/0x1d0
rtnetlink_rcv_msg+0x142/0x3f0
netlink_rcv_skb+0x50/0x100
netlink_unicast+0x242/0x390
netlink_sendmsg+0x21b/0x470
__sys_sendto+0x1dc/0x1f0
__x64_sys_sendto+0x24/0x30
do_syscall_64+0x7d/0x160
entry_SYSCALL_64_after_hwframe+0x76/0x7e
RIP: 0033:0x7f5c3852f127
Code: 0c 00 f7 d8 64 89 02 48 c7 c0 ff ff ff ff eb b8 0f 1f 00 f3 0f 1e fa 80 3d 85 ef 0c 00 00 41 89 ca 74 10 b8 2c 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 71 c3 55 48 83 ec 30 44 89 4c 24 2c 4c 89 44
RSP: 002b:00007ffe86caf4c8 EFLAGS: 00000202 ORIG_RAX: 000000000000002c
RAX: ffffffffffffffda RBX: 0000556c5cd93210 RCX: 00007f5c3852f127
RDX: 0000000000000020 RSI: 0000556c5cd938b0 RDI: 0000000000000003
RBP: 00007ffe86caf5a0 R08: 00007ffe86caf4e0 R09: 0000000000000080
R10: 0000000000000000 R11: 0000000000000202 R12: 0000556c5cd932d0
R13: 00000000021d05d1 R14: 00000000021d05d1 R15: 0000000000000001

IFA_F_PERMANENT addresses require the allocation of a bunch of percpu
pointers, currently in atomic scope.

Similar to commit 51454ea42c1a ("ipv6: fix locking issues with loops
over idev->addr_list"), move fixup_permanent_addr() outside the
&idev->lock scope, and do the allocations with GFP_KERNEL. With such
change fixup_permanent_addr() is invoked with the BH enabled, and the
ifp lock acquired there needs the BH variant.

Note that we don't need to acquire a reference to the permanent
addresses before releasing the mentioned write lock, because
addrconf_permanent_addr() runs under RTNL and ifa removal always happens
under RTNL, too.

Also the PERMANENT flag is constant in the relevant scope, as it can be
cleared only by inet6_addr_modify() under the RTNL lock.

Reviewed-by: David Ahern <dsahern@kernel.org>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Link: https://patch.msgid.link/46a7a030727e236af2dc7752994cd4f04f4a91d2.1775658924.git.pabeni@redhat.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Paolo Abeni and committed by
Jakub Kicinski
8e6405f8 9addea5d

+19 -12
+19 -12
net/ipv6/addrconf.c
··· 3585 3585 struct fib6_info *f6i, *prev; 3586 3586 3587 3587 f6i = addrconf_f6i_alloc(net, idev, &ifp->addr, false, 3588 - GFP_ATOMIC, NULL); 3588 + GFP_KERNEL, NULL); 3589 3589 if (IS_ERR(f6i)) 3590 3590 return PTR_ERR(f6i); 3591 3591 3592 3592 /* ifp->rt can be accessed outside of rtnl */ 3593 - spin_lock(&ifp->lock); 3593 + spin_lock_bh(&ifp->lock); 3594 3594 prev = ifp->rt; 3595 3595 ifp->rt = f6i; 3596 - spin_unlock(&ifp->lock); 3596 + spin_unlock_bh(&ifp->lock); 3597 3597 3598 3598 fib6_info_release(prev); 3599 3599 } ··· 3601 3601 if (!(ifp->flags & IFA_F_NOPREFIXROUTE)) { 3602 3602 addrconf_prefix_route(&ifp->addr, ifp->prefix_len, 3603 3603 ifp->rt_priority, idev->dev, 0, 0, 3604 - GFP_ATOMIC); 3604 + GFP_KERNEL); 3605 3605 } 3606 3606 3607 3607 if (ifp->state == INET6_IFADDR_STATE_PREDAD) ··· 3612 3612 3613 3613 static void addrconf_permanent_addr(struct net *net, struct net_device *dev) 3614 3614 { 3615 - struct inet6_ifaddr *ifp, *tmp; 3615 + struct inet6_ifaddr *ifp; 3616 + LIST_HEAD(tmp_addr_list); 3616 3617 struct inet6_dev *idev; 3618 + 3619 + /* Mutual exclusion with other if_list_aux users. */ 3620 + ASSERT_RTNL(); 3617 3621 3618 3622 idev = __in6_dev_get(dev); 3619 3623 if (!idev) 3620 3624 return; 3621 3625 3622 3626 write_lock_bh(&idev->lock); 3627 + list_for_each_entry(ifp, &idev->addr_list, if_list) { 3628 + if (ifp->flags & IFA_F_PERMANENT) 3629 + list_add_tail(&ifp->if_list_aux, &tmp_addr_list); 3630 + } 3631 + write_unlock_bh(&idev->lock); 3623 3632 3624 - list_for_each_entry_safe(ifp, tmp, &idev->addr_list, if_list) { 3625 - if ((ifp->flags & IFA_F_PERMANENT) && 3626 - fixup_permanent_addr(net, idev, ifp) < 0) { 3627 - write_unlock_bh(&idev->lock); 3633 + while (!list_empty(&tmp_addr_list)) { 3634 + ifp = list_first_entry(&tmp_addr_list, 3635 + struct inet6_ifaddr, if_list_aux); 3636 + list_del(&ifp->if_list_aux); 3628 3637 3638 + if (fixup_permanent_addr(net, idev, ifp) < 0) { 3629 3639 net_info_ratelimited("%s: Failed to add prefix route for address %pI6c; dropping\n", 3630 3640 idev->dev->name, &ifp->addr); 3631 3641 in6_ifa_hold(ifp); 3632 3642 ipv6_del_addr(ifp); 3633 - write_lock_bh(&idev->lock); 3634 3643 } 3635 3644 } 3636 - 3637 - write_unlock_bh(&idev->lock); 3638 3645 } 3639 3646 3640 3647 static int addrconf_notify(struct notifier_block *this, unsigned long event,