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

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
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -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
+8 −0
Original line number Diff line number Diff line
@@ -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
+10 −0
Original line number Diff line number Diff line
@@ -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 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
	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;