net: dst: add net device refcount tracking to dst_entry
authorEric Dumazet <edumazet@google.com>
Sun, 5 Dec 2021 04:22:03 +0000 (20:22 -0800)
committerJakub Kicinski <kuba@kernel.org>
Tue, 7 Dec 2021 00:05:10 +0000 (16:05 -0800)
We want to track all dev_hold()/dev_put() to ease leak hunting.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
include/linux/netdevice.h
include/net/dst.h
net/core/dst.c
net/ipv4/route.c
net/ipv6/route.c

index b4f704337f657ebc46e02c9e5e7f5d2c2c64685e..afed3b10491b92da880a8cd13181ff041cc54673 100644 (file)
@@ -3883,6 +3883,23 @@ static inline void dev_put_track(struct net_device *dev,
        }
 }
 
+static inline void dev_replace_track(struct net_device *odev,
+                                    struct net_device *ndev,
+                                    netdevice_tracker *tracker,
+                                    gfp_t gfp)
+{
+#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
+       if (odev)
+               ref_tracker_free(&odev->refcnt_tracker, tracker);
+#endif
+       dev_hold(ndev);
+       dev_put(odev);
+#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
+       if (ndev)
+               ref_tracker_alloc(&ndev->refcnt_tracker, tracker, gfp);
+#endif
+}
+
 /* Carrier loss detection, dial on demand. The functions netif_carrier_on
  * and _off may be called from IRQ context, but it is caller
  * who is responsible for serialization of these calls.
index a057319aabefac52075edb038358439ceec23a60..6aa252c3fc55ccaee58faebf265510469e91d780 100644 (file)
@@ -77,6 +77,7 @@ struct dst_entry {
 #ifndef CONFIG_64BIT
        atomic_t                __refcnt;       /* 32-bit offset 64 */
 #endif
+       netdevice_tracker       dev_tracker;
 };
 
 struct dst_metrics {
index 497ef9b3fc6abb5fd9c74e5730c3aedc3277f850..d16c2c9bfebd3dadd4c8dbc4f14836574bb52bbe 100644 (file)
@@ -49,7 +49,7 @@ void dst_init(struct dst_entry *dst, struct dst_ops *ops,
              unsigned short flags)
 {
        dst->dev = dev;
-       dev_hold(dev);
+       dev_hold_track(dev, &dst->dev_tracker, GFP_ATOMIC);
        dst->ops = ops;
        dst_init_metrics(dst, dst_default_metrics.metrics, true);
        dst->expires = 0UL;
@@ -117,7 +117,7 @@ struct dst_entry *dst_destroy(struct dst_entry * dst)
 
        if (dst->ops->destroy)
                dst->ops->destroy(dst);
-       dev_put(dst->dev);
+       dev_put_track(dst->dev, &dst->dev_tracker);
 
        lwtstate_put(dst->lwtstate);
 
@@ -159,8 +159,8 @@ void dst_dev_put(struct dst_entry *dst)
        dst->input = dst_discard;
        dst->output = dst_discard_out;
        dst->dev = blackhole_netdev;
-       dev_hold(dst->dev);
-       dev_put(dev);
+       dev_replace_track(dev, blackhole_netdev, &dst->dev_tracker,
+                         GFP_ATOMIC);
 }
 EXPORT_SYMBOL(dst_dev_put);
 
index 243a0c52be42b60226a38dce980956b33e583d80..843a7a3699feeb24f3b9af5efaff87724214cbce 100644 (file)
@@ -1531,8 +1531,9 @@ void rt_flush_dev(struct net_device *dev)
                        if (rt->dst.dev != dev)
                                continue;
                        rt->dst.dev = blackhole_netdev;
-                       dev_hold(rt->dst.dev);
-                       dev_put(dev);
+                       dev_replace_track(dev, blackhole_netdev,
+                                         &rt->dst.dev_tracker,
+                                         GFP_ATOMIC);
                }
                spin_unlock_bh(&ul->lock);
        }
@@ -2819,7 +2820,7 @@ struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_or
                new->output = dst_discard_out;
 
                new->dev = net->loopback_dev;
-               dev_hold(new->dev);
+               dev_hold_track(new->dev, &new->dev_tracker, GFP_ATOMIC);
 
                rt->rt_is_input = ort->rt_is_input;
                rt->rt_iif = ort->rt_iif;
index f0d29fcb20945b5a48097c89dc57daedeed9d177..ba4dc94d76d63c98ff49c41b712231f81eb8af40 100644 (file)
@@ -182,8 +182,9 @@ static void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev)
 
                        if (rt_dev == dev) {
                                rt->dst.dev = blackhole_netdev;
-                               dev_hold(rt->dst.dev);
-                               dev_put(rt_dev);
+                               dev_replace_track(rt_dev, blackhole_netdev,
+                                                 &rt->dst.dev_tracker,
+                                                 GFP_ATOMIC);
                        }
                }
                spin_unlock_bh(&ul->lock);