net: add CONFIG_PCPU_DEV_REFCNT
authorEric Dumazet <edumazet@google.com>
Fri, 19 Mar 2021 17:39:33 +0000 (10:39 -0700)
committerDavid S. Miller <davem@davemloft.net>
Fri, 19 Mar 2021 20:38:46 +0000 (13:38 -0700)
I was working on a syzbot issue, claiming one device could not be
dismantled because its refcount was -1

unregister_netdevice: waiting for sit0 to become free. Usage count = -1

It would be nice if syzbot could trigger a warning at the time
this reference count became negative.

This patch adds CONFIG_PCPU_DEV_REFCNT options which defaults
to per cpu variables (as before this patch) on SMP builds.

v2: free_dev label in alloc_netdev_mqs() is moved to avoid
    a compiler warning (-Wunused-label), as reported
    by kernel test robot <lkp@intel.com>

Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/netdevice.h
net/Kconfig
net/core/dev.c

index 4940509..8f00395 100644 (file)
@@ -2092,7 +2092,12 @@ struct net_device {
        u32                     proto_down_reason;
 
        struct list_head        todo_list;
+
+#ifdef CONFIG_PCPU_DEV_REFCNT
        int __percpu            *pcpu_refcnt;
+#else
+       refcount_t              dev_refcnt;
+#endif
 
        struct list_head        link_watch_list;
 
@@ -4044,7 +4049,11 @@ void netdev_run_todo(void);
  */
 static inline void dev_put(struct net_device *dev)
 {
+#ifdef CONFIG_PCPU_DEV_REFCNT
        this_cpu_dec(*dev->pcpu_refcnt);
+#else
+       refcount_dec(&dev->dev_refcnt);
+#endif
 }
 
 /**
@@ -4055,7 +4064,11 @@ static inline void dev_put(struct net_device *dev)
  */
 static inline void dev_hold(struct net_device *dev)
 {
+#ifdef CONFIG_PCPU_DEV_REFCNT
        this_cpu_inc(*dev->pcpu_refcnt);
+#else
+       refcount_inc(&dev->dev_refcnt);
+#endif
 }
 
 /* Carrier loss detection, dial on demand. The functions netif_carrier_on
index 0ead7ec..9c456ac 100644 (file)
@@ -245,6 +245,14 @@ source "net/l3mdev/Kconfig"
 source "net/qrtr/Kconfig"
 source "net/ncsi/Kconfig"
 
+config PCPU_DEV_REFCNT
+       bool "Use percpu variables to maintain network device refcount"
+       depends on SMP
+       default y
+       help
+         network device refcount are using per cpu variables if this option is set.
+         This can be forced to N to detect underflows (with a performance drop).
+
 config RPS
        bool
        depends on SMP && SYSFS
index 4961fc2..be941ed 100644 (file)
@@ -10312,11 +10312,15 @@ EXPORT_SYMBOL(register_netdev);
 
 int netdev_refcnt_read(const struct net_device *dev)
 {
+#ifdef CONFIG_PCPU_DEV_REFCNT
        int i, refcnt = 0;
 
        for_each_possible_cpu(i)
                refcnt += *per_cpu_ptr(dev->pcpu_refcnt, i);
        return refcnt;
+#else
+       return refcount_read(&dev->dev_refcnt);
+#endif
 }
 EXPORT_SYMBOL(netdev_refcnt_read);
 
@@ -10674,9 +10678,11 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
        dev = PTR_ALIGN(p, NETDEV_ALIGN);
        dev->padded = (char *)dev - (char *)p;
 
+#ifdef CONFIG_PCPU_DEV_REFCNT
        dev->pcpu_refcnt = alloc_percpu(int);
        if (!dev->pcpu_refcnt)
                goto free_dev;
+#endif
 
        if (dev_addr_init(dev))
                goto free_pcpu;
@@ -10740,8 +10746,10 @@ free_all:
        return NULL;
 
 free_pcpu:
+#ifdef CONFIG_PCPU_DEV_REFCNT
        free_percpu(dev->pcpu_refcnt);
 free_dev:
+#endif
        netdev_freemem(dev);
        return NULL;
 }
@@ -10783,8 +10791,10 @@ void free_netdev(struct net_device *dev)
        list_for_each_entry_safe(p, n, &dev->napi_list, dev_list)
                netif_napi_del(p);
 
+#ifdef CONFIG_PCPU_DEV_REFCNT
        free_percpu(dev->pcpu_refcnt);
        dev->pcpu_refcnt = NULL;
+#endif
        free_percpu(dev->xdp_bulkq);
        dev->xdp_bulkq = NULL;