Commit 919067cc authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller

net: add CONFIG_PCPU_DEV_REFCNT

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: default avatarEric Dumazet <edumazet@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 942f0c6e
...@@ -2092,7 +2092,12 @@ struct net_device { ...@@ -2092,7 +2092,12 @@ struct net_device {
u32 proto_down_reason; u32 proto_down_reason;
struct list_head todo_list; struct list_head todo_list;
#ifdef CONFIG_PCPU_DEV_REFCNT
int __percpu *pcpu_refcnt; int __percpu *pcpu_refcnt;
#else
refcount_t dev_refcnt;
#endif
struct list_head link_watch_list; struct list_head link_watch_list;
...@@ -4044,7 +4049,11 @@ void netdev_run_todo(void); ...@@ -4044,7 +4049,11 @@ void netdev_run_todo(void);
*/ */
static inline void dev_put(struct net_device *dev) static inline void dev_put(struct net_device *dev)
{ {
#ifdef CONFIG_PCPU_DEV_REFCNT
this_cpu_dec(*dev->pcpu_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) ...@@ -4055,7 +4064,11 @@ static inline void dev_put(struct net_device *dev)
*/ */
static inline void dev_hold(struct net_device *dev) static inline void dev_hold(struct net_device *dev)
{ {
#ifdef CONFIG_PCPU_DEV_REFCNT
this_cpu_inc(*dev->pcpu_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 /* Carrier loss detection, dial on demand. The functions netif_carrier_on
......
...@@ -245,6 +245,14 @@ source "net/l3mdev/Kconfig" ...@@ -245,6 +245,14 @@ source "net/l3mdev/Kconfig"
source "net/qrtr/Kconfig" source "net/qrtr/Kconfig"
source "net/ncsi/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 config RPS
bool bool
depends on SMP && SYSFS depends on SMP && SYSFS
......
...@@ -10312,11 +10312,15 @@ EXPORT_SYMBOL(register_netdev); ...@@ -10312,11 +10312,15 @@ EXPORT_SYMBOL(register_netdev);
int netdev_refcnt_read(const struct net_device *dev) int netdev_refcnt_read(const struct net_device *dev)
{ {
#ifdef CONFIG_PCPU_DEV_REFCNT
int i, refcnt = 0; int i, refcnt = 0;
for_each_possible_cpu(i) for_each_possible_cpu(i)
refcnt += *per_cpu_ptr(dev->pcpu_refcnt, i); refcnt += *per_cpu_ptr(dev->pcpu_refcnt, i);
return refcnt; return refcnt;
#else
return refcount_read(&dev->dev_refcnt);
#endif
} }
EXPORT_SYMBOL(netdev_refcnt_read); EXPORT_SYMBOL(netdev_refcnt_read);
...@@ -10674,9 +10678,11 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, ...@@ -10674,9 +10678,11 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
dev = PTR_ALIGN(p, NETDEV_ALIGN); dev = PTR_ALIGN(p, NETDEV_ALIGN);
dev->padded = (char *)dev - (char *)p; dev->padded = (char *)dev - (char *)p;
#ifdef CONFIG_PCPU_DEV_REFCNT
dev->pcpu_refcnt = alloc_percpu(int); dev->pcpu_refcnt = alloc_percpu(int);
if (!dev->pcpu_refcnt) if (!dev->pcpu_refcnt)
goto free_dev; goto free_dev;
#endif
if (dev_addr_init(dev)) if (dev_addr_init(dev))
goto free_pcpu; goto free_pcpu;
...@@ -10740,8 +10746,10 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, ...@@ -10740,8 +10746,10 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
return NULL; return NULL;
free_pcpu: free_pcpu:
#ifdef CONFIG_PCPU_DEV_REFCNT
free_percpu(dev->pcpu_refcnt); free_percpu(dev->pcpu_refcnt);
free_dev: free_dev:
#endif
netdev_freemem(dev); netdev_freemem(dev);
return NULL; return NULL;
} }
...@@ -10783,8 +10791,10 @@ void free_netdev(struct net_device *dev) ...@@ -10783,8 +10791,10 @@ void free_netdev(struct net_device *dev)
list_for_each_entry_safe(p, n, &dev->napi_list, dev_list) list_for_each_entry_safe(p, n, &dev->napi_list, dev_list)
netif_napi_del(p); netif_napi_del(p);
#ifdef CONFIG_PCPU_DEV_REFCNT
free_percpu(dev->pcpu_refcnt); free_percpu(dev->pcpu_refcnt);
dev->pcpu_refcnt = NULL; dev->pcpu_refcnt = NULL;
#endif
free_percpu(dev->xdp_bulkq); free_percpu(dev->xdp_bulkq);
dev->xdp_bulkq = NULL; dev->xdp_bulkq = NULL;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment