]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ipmr: Convert ipmr_rtm_dumplink() to RCU.
authorKuniyuki Iwashima <kuniyu@google.com>
Sat, 28 Feb 2026 22:17:21 +0000 (22:17 +0000)
committerJakub Kicinski <kuba@kernel.org>
Tue, 3 Mar 2026 02:49:39 +0000 (18:49 -0800)
net->ipv4.mr_tables is updated under RTNL and can be read
safely under RCU.

Once created, the multicast route tables are not removed
until netns dismantle.

ipmr_rtm_dumplink() does not need RTNL protection for
ipmr_for_each_table() and ipmr_fill_table() if RCU is held.

Even if mrt->maxvif changes concurrently, ipmr_fill_vif()
returns true to continue dumping the next table.

Let's convert it to RCU.

Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Link: https://patch.msgid.link/20260228221800.1082070-4-kuniyu@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/ipv4/ipmr.c

index 970f173654c7f6496604a819c28fd72a7d1f93ec..eecc79a835d17513d60ae116cafc427477193624 100644 (file)
@@ -2901,12 +2901,13 @@ static bool ipmr_fill_table(struct mr_table *mrt, struct sk_buff *skb)
        if (nla_put_u32(skb, IPMRA_TABLE_ID, mrt->id) ||
            nla_put_u32(skb, IPMRA_TABLE_CACHE_RES_QUEUE_LEN, queue_len) ||
            nla_put_s32(skb, IPMRA_TABLE_MROUTE_REG_VIF_NUM,
-                       mrt->mroute_reg_vif_num) ||
+                       READ_ONCE(mrt->mroute_reg_vif_num)) ||
            nla_put_u8(skb, IPMRA_TABLE_MROUTE_DO_ASSERT,
-                      mrt->mroute_do_assert) ||
-           nla_put_u8(skb, IPMRA_TABLE_MROUTE_DO_PIM, mrt->mroute_do_pim) ||
+                      READ_ONCE(mrt->mroute_do_assert)) ||
+           nla_put_u8(skb, IPMRA_TABLE_MROUTE_DO_PIM,
+                      READ_ONCE(mrt->mroute_do_pim)) ||
            nla_put_u8(skb, IPMRA_TABLE_MROUTE_DO_WRVIFWHOLE,
-                      mrt->mroute_do_wrvifwhole))
+                      READ_ONCE(mrt->mroute_do_wrvifwhole)))
                return false;
 
        return true;
@@ -2919,7 +2920,7 @@ static bool ipmr_fill_vif(struct mr_table *mrt, u32 vifid, struct sk_buff *skb)
        struct vif_device *vif;
 
        vif = &mrt->vif_table[vifid];
-       vif_dev = rtnl_dereference(vif->dev);
+       vif_dev = vif_dev_read(vif);
        /* if the VIF doesn't exist just continue */
        if (!vif_dev)
                return true;
@@ -2928,16 +2929,16 @@ static bool ipmr_fill_vif(struct mr_table *mrt, u32 vifid, struct sk_buff *skb)
        if (!vif_nest)
                return false;
 
-       if (nla_put_u32(skb, IPMRA_VIFA_IFINDEX, vif_dev->ifindex) ||
+       if (nla_put_u32(skb, IPMRA_VIFA_IFINDEX, READ_ONCE(vif_dev->ifindex)) ||
            nla_put_u32(skb, IPMRA_VIFA_VIF_ID, vifid) ||
            nla_put_u16(skb, IPMRA_VIFA_FLAGS, vif->flags) ||
-           nla_put_u64_64bit(skb, IPMRA_VIFA_BYTES_IN, vif->bytes_in,
+           nla_put_u64_64bit(skb, IPMRA_VIFA_BYTES_IN, READ_ONCE(vif->bytes_in),
                              IPMRA_VIFA_PAD) ||
-           nla_put_u64_64bit(skb, IPMRA_VIFA_BYTES_OUT, vif->bytes_out,
+           nla_put_u64_64bit(skb, IPMRA_VIFA_BYTES_OUT, READ_ONCE(vif->bytes_out),
                              IPMRA_VIFA_PAD) ||
-           nla_put_u64_64bit(skb, IPMRA_VIFA_PACKETS_IN, vif->pkt_in,
+           nla_put_u64_64bit(skb, IPMRA_VIFA_PACKETS_IN, READ_ONCE(vif->pkt_in),
                              IPMRA_VIFA_PAD) ||
-           nla_put_u64_64bit(skb, IPMRA_VIFA_PACKETS_OUT, vif->pkt_out,
+           nla_put_u64_64bit(skb, IPMRA_VIFA_PACKETS_OUT, READ_ONCE(vif->pkt_out),
                              IPMRA_VIFA_PAD) ||
            nla_put_be32(skb, IPMRA_VIFA_LOCAL_ADDR, vif->local) ||
            nla_put_be32(skb, IPMRA_VIFA_REMOTE_ADDR, vif->remote)) {
@@ -2992,6 +2993,8 @@ static int ipmr_rtm_dumplink(struct sk_buff *skb, struct netlink_callback *cb)
        s_t = cb->args[0];
        s_e = cb->args[1];
 
+       rcu_read_lock();
+
        ipmr_for_each_table(mrt, net) {
                struct nlattr *vifs, *af;
                struct ifinfomsg *hdr;
@@ -3026,7 +3029,7 @@ static int ipmr_rtm_dumplink(struct sk_buff *skb, struct netlink_callback *cb)
                        nlmsg_end(skb, nlh);
                        goto out;
                }
-               for (i = 0; i < mrt->maxvif; i++) {
+               for (i = 0; i < READ_ONCE(mrt->maxvif); i++) {
                        if (e < s_e)
                                goto skip_entry;
                        if (!ipmr_fill_vif(mrt, i, skb)) {
@@ -3048,6 +3051,8 @@ skip_table:
        }
 
 out:
+       rcu_read_unlock();
+
        cb->args[1] = e;
        cb->args[0] = t;
 
@@ -3287,7 +3292,7 @@ static struct pernet_operations ipmr_net_ops = {
 
 static const struct rtnl_msg_handler ipmr_rtnl_msg_handlers[] __initconst = {
        {.protocol = RTNL_FAMILY_IPMR, .msgtype = RTM_GETLINK,
-        .dumpit = ipmr_rtm_dumplink},
+        .dumpit = ipmr_rtm_dumplink, .flags = RTNL_FLAG_DUMP_UNLOCKED},
        {.protocol = RTNL_FAMILY_IPMR, .msgtype = RTM_NEWROUTE,
         .doit = ipmr_rtm_route},
        {.protocol = RTNL_FAMILY_IPMR, .msgtype = RTM_DELROUTE,