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.

mpls: Hold dev refcnt for mpls_nh.

MPLS uses RTNL

1) to guarantee the lifetime of struct mpls_nh.nh_dev
2) to protect net->mpls.platform_label

, but neither actually requires RTNL.

If we do not call dev_put() in find_outdev() and call it
just before freeing struct mpls_route, we can drop RTNL for 1).

Let's hold the refcnt of mpls_nh.nh_dev and track it with
netdevice_tracker.

Two notable changes:

If mpls_nh_build_multi() fails to set up a neighbour, we need
to call netdev_put() for successfully created neighbours in
mpls_rt_free_rcu(), so the number of neighbours (rt->rt_nhn)
is now updated in each iteration.

When a dev is unregistered, mpls_ifdown() clones mpls_route
and replaces it with the clone, so the clone requires extra
netdev_hold().

Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
Reviewed-by: Guillaume Nault <gnault@redhat.com>
Link: https://patch.msgid.link/20251029173344.2934622-3-kuniyu@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Kuniyuki Iwashima and committed by
Jakub Kicinski
f0914b84 2214ca1f

+45 -19
+44 -19
net/mpls/af_mpls.c
··· 530 530 return rt; 531 531 } 532 532 533 + static void mpls_rt_free_rcu(struct rcu_head *head) 534 + { 535 + struct mpls_route *rt; 536 + 537 + rt = container_of(head, struct mpls_route, rt_rcu); 538 + 539 + change_nexthops(rt) { 540 + netdev_put(nh->nh_dev, &nh->nh_dev_tracker); 541 + } endfor_nexthops(rt); 542 + 543 + kfree(rt); 544 + } 545 + 533 546 static void mpls_rt_free(struct mpls_route *rt) 534 547 { 535 548 if (rt) 536 - kfree_rcu(rt, rt_rcu); 549 + call_rcu(&rt->rt_rcu, mpls_rt_free_rcu); 537 550 } 538 551 539 552 static void mpls_notify_route(struct net *net, unsigned index, ··· 600 587 601 588 #if IS_ENABLED(CONFIG_INET) 602 589 static struct net_device *inet_fib_lookup_dev(struct net *net, 590 + struct mpls_nh *nh, 603 591 const void *addr) 604 592 { 605 593 struct net_device *dev; ··· 613 599 return ERR_CAST(rt); 614 600 615 601 dev = rt->dst.dev; 616 - dev_hold(dev); 617 - 602 + netdev_hold(dev, &nh->nh_dev_tracker, GFP_KERNEL); 618 603 ip_rt_put(rt); 619 604 620 605 return dev; 621 606 } 622 607 #else 623 608 static struct net_device *inet_fib_lookup_dev(struct net *net, 609 + struct mpls_nh *nh, 624 610 const void *addr) 625 611 { 626 612 return ERR_PTR(-EAFNOSUPPORT); ··· 629 615 630 616 #if IS_ENABLED(CONFIG_IPV6) 631 617 static struct net_device *inet6_fib_lookup_dev(struct net *net, 618 + struct mpls_nh *nh, 632 619 const void *addr) 633 620 { 634 621 struct net_device *dev; ··· 646 631 return ERR_CAST(dst); 647 632 648 633 dev = dst->dev; 649 - dev_hold(dev); 634 + netdev_hold(dev, &nh->nh_dev_tracker, GFP_KERNEL); 650 635 dst_release(dst); 651 636 652 637 return dev; 653 638 } 654 639 #else 655 640 static struct net_device *inet6_fib_lookup_dev(struct net *net, 641 + struct mpls_nh *nh, 656 642 const void *addr) 657 643 { 658 644 return ERR_PTR(-EAFNOSUPPORT); ··· 669 653 if (!oif) { 670 654 switch (nh->nh_via_table) { 671 655 case NEIGH_ARP_TABLE: 672 - dev = inet_fib_lookup_dev(net, mpls_nh_via(rt, nh)); 656 + dev = inet_fib_lookup_dev(net, nh, mpls_nh_via(rt, nh)); 673 657 break; 674 658 case NEIGH_ND_TABLE: 675 - dev = inet6_fib_lookup_dev(net, mpls_nh_via(rt, nh)); 659 + dev = inet6_fib_lookup_dev(net, nh, mpls_nh_via(rt, nh)); 676 660 break; 677 661 case NEIGH_LINK_TABLE: 678 662 break; 679 663 } 680 664 } else { 681 - dev = dev_get_by_index(net, oif); 665 + dev = netdev_get_by_index(net, oif, 666 + &nh->nh_dev_tracker, GFP_KERNEL); 682 667 } 683 668 684 669 if (!dev) ··· 688 671 if (IS_ERR(dev)) 689 672 return dev; 690 673 691 - /* The caller is holding rtnl anyways, so release the dev reference */ 692 - dev_put(dev); 674 + nh->nh_dev = dev; 693 675 694 676 return dev; 695 677 } ··· 702 686 dev = find_outdev(net, rt, nh, oif); 703 687 if (IS_ERR(dev)) { 704 688 err = PTR_ERR(dev); 705 - dev = NULL; 706 689 goto errout; 707 690 } 708 691 709 692 /* Ensure this is a supported device */ 710 693 err = -EINVAL; 711 694 if (!mpls_dev_get(dev)) 712 - goto errout; 695 + goto errout_put; 713 696 714 697 if ((nh->nh_via_table == NEIGH_LINK_TABLE) && 715 698 (dev->addr_len != nh->nh_via_alen)) 716 - goto errout; 717 - 718 - nh->nh_dev = dev; 699 + goto errout_put; 719 700 720 701 if (!(dev->flags & IFF_UP)) { 721 702 nh->nh_flags |= RTNH_F_DEAD; ··· 726 713 727 714 return 0; 728 715 716 + errout_put: 717 + netdev_put(nh->nh_dev, &nh->nh_dev_tracker); 718 + nh->nh_dev = NULL; 729 719 errout: 730 720 return err; 731 721 } ··· 906 890 struct nlattr *nla_via, *nla_newdst; 907 891 int remaining = cfg->rc_mp_len; 908 892 int err = 0; 909 - u8 nhs = 0; 893 + 894 + rt->rt_nhn = 0; 910 895 911 896 change_nexthops(rt) { 912 897 int attrlen; ··· 943 926 rt->rt_nhn_alive--; 944 927 945 928 rtnh = rtnh_next(rtnh, &remaining); 946 - nhs++; 929 + rt->rt_nhn++; 947 930 } endfor_nexthops(rt); 948 - 949 - rt->rt_nhn = nhs; 950 931 951 932 return 0; 952 933 ··· 1538 1523 change_nexthops(rt) { 1539 1524 unsigned int nh_flags = nh->nh_flags; 1540 1525 1541 - if (nh->nh_dev != dev) 1526 + if (nh->nh_dev != dev) { 1527 + if (nh_del) 1528 + netdev_hold(nh->nh_dev, &nh->nh_dev_tracker, 1529 + GFP_KERNEL); 1542 1530 goto next; 1531 + } 1543 1532 1544 1533 switch (event) { 1545 1534 case NETDEV_DOWN: ··· 2537 2518 /* In case the predefined labels need to be populated */ 2538 2519 if (limit > MPLS_LABEL_IPV4NULL) { 2539 2520 struct net_device *lo = net->loopback_dev; 2521 + 2540 2522 rt0 = mpls_rt_alloc(1, lo->addr_len, 0); 2541 2523 if (IS_ERR(rt0)) 2542 2524 goto nort0; 2525 + 2543 2526 rt0->rt_nh->nh_dev = lo; 2527 + netdev_hold(lo, &rt0->rt_nh->nh_dev_tracker, GFP_KERNEL); 2544 2528 rt0->rt_protocol = RTPROT_KERNEL; 2545 2529 rt0->rt_payload_type = MPT_IPV4; 2546 2530 rt0->rt_ttl_propagate = MPLS_TTL_PROP_DEFAULT; ··· 2554 2532 } 2555 2533 if (limit > MPLS_LABEL_IPV6NULL) { 2556 2534 struct net_device *lo = net->loopback_dev; 2535 + 2557 2536 rt2 = mpls_rt_alloc(1, lo->addr_len, 0); 2558 2537 if (IS_ERR(rt2)) 2559 2538 goto nort2; 2539 + 2560 2540 rt2->rt_nh->nh_dev = lo; 2541 + netdev_hold(lo, &rt2->rt_nh->nh_dev_tracker, GFP_KERNEL); 2561 2542 rt2->rt_protocol = RTPROT_KERNEL; 2562 2543 rt2->rt_payload_type = MPT_IPV6; 2563 2544 rt2->rt_ttl_propagate = MPLS_TTL_PROP_DEFAULT;
+1
net/mpls/internal.h
··· 88 88 89 89 struct mpls_nh { /* next hop label forwarding entry */ 90 90 struct net_device *nh_dev; 91 + netdevice_tracker nh_dev_tracker; 91 92 92 93 /* nh_flags is accessed under RCU in the packet path; it is 93 94 * modified handling netdev events with rtnl lock held