Commit 4c375272 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'net-add-preliminary-netdev-refcount-tracking'

Eric Dumazet says:

====================
net: add preliminary netdev refcount tracking

Two first patches add a generic infrastructure, that will be used
to get tracking of refcount increments/decrements.

The general idea is to be able to precisely pair each decrement with
a corresponding prior increment. Both share a cookie, basically
a pointer to private data storing stack traces.

The third patch adds dev_hold_track() and dev_put_track() helpers
(CONFIG_NET_DEV_REFCNT_TRACKER)

Then a series of 20 patches converts some dev_hold()/dev_put()
pairs to new hepers : dev_hold_track() and dev_put_track().

Hopefully this will be used by developpers and syzbot to
root cause bugs that cause netdevice dismantles freezes.

With CONFIG_PCPU_DEV_REFCNT=n option, we were able to detect
some class of bugs, but too late (when too many dev_put()
were happening).

Another series will be sent after this one is merged.

v3: moved NET_DEV_REFCNT_TRACKER to net/Kconfig.debug
    added "depends on DEBUG_KERNEL && STACKTRACE_SUPPORT"
    to hopefully get rid of kbuild reports for ARCH=nios2
    Reworded patch 3 changelog.
    Added missing htmldocs (Jakub)

v2: added four additional patches,
    added netdev_tracker_alloc() and netdev_tracker_free()
    addressed build error (kernel bots),
    use GFP_ATOMIC in test_ref_tracker_timer_func()
====================

Link: https://lore.kernel.org/r/20211205042217.982127-1-eric.dumazet@gmail.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents ce83278f 5fa5ae60
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -721,7 +721,7 @@ static int netconsole_netdev_event(struct notifier_block *this,
				__netpoll_cleanup(&nt->np);

				spin_lock_irqsave(&target_list_lock, flags);
				dev_put(nt->np.dev);
				dev_put_track(nt->np.dev, &nt->np.dev_tracker);
				nt->np.dev = NULL;
				nt->enabled = false;
				stopped = true;
+2 −0
Original line number Diff line number Diff line
@@ -24,6 +24,8 @@ struct ipv4_devconf {

struct in_device {
	struct net_device	*dev;
	netdevice_tracker	dev_tracker;

	refcount_t		refcnt;
	int			dead;
	struct in_ifaddr	__rcu *ifa_list;/* IP ifaddr chain		*/
+2 −0
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@
/**
 * struct vif_device - interface representor for multicast routing
 * @dev: network device being used
 * @dev_tracker: refcount tracker for @dev reference
 * @bytes_in: statistic; bytes ingressing
 * @bytes_out: statistic; bytes egresing
 * @pkt_in: statistic; packets ingressing
@@ -26,6 +27,7 @@
 */
struct vif_device {
	struct net_device *dev;
	netdevice_tracker dev_tracker;
	unsigned long bytes_in, bytes_out;
	unsigned long pkt_in, pkt_out;
	unsigned long rate_limit;
+68 −0
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@
#include <uapi/linux/pkt_cls.h>
#include <linux/hashtable.h>
#include <linux/rbtree.h>
#include <linux/ref_tracker.h>

struct netpoll_info;
struct device;
@@ -300,6 +301,12 @@ enum netdev_state_t {
};


#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
typedef struct ref_tracker *netdevice_tracker;
#else
typedef struct {} netdevice_tracker;
#endif

struct gro_list {
	struct list_head	list;
	int			count;
@@ -579,6 +586,8 @@ struct netdev_queue {
 * read-mostly part
 */
	struct net_device	*dev;
	netdevice_tracker	dev_tracker;

	struct Qdisc __rcu	*qdisc;
	struct Qdisc		*qdisc_sleeping;
#ifdef CONFIG_SYSFS
@@ -734,6 +743,8 @@ struct netdev_rx_queue {
#endif
	struct kobject			kobj;
	struct net_device		*dev;
	netdevice_tracker		dev_tracker;

#ifdef CONFIG_XDP_SOCKETS
	struct xsk_buff_pool            *pool;
#endif
@@ -1865,6 +1876,7 @@ enum netdev_ml_priv_type {
 *	@proto_down_reason:	reason a netdev interface is held down
 *	@pcpu_refcnt:		Number of references to this device
 *	@dev_refcnt:		Number of references to this device
 *	@refcnt_tracker:	Tracker directory for tracked references to this device
 *	@todo_list:		Delayed register/unregister
 *	@link_watch_list:	XXX: need comments on this one
 *
@@ -1938,6 +1950,7 @@ enum netdev_ml_priv_type {
 *			keep a list of interfaces to be deleted.
 *
 *	@dev_addr_shadow:	Copy of @dev_addr to catch direct writes.
 *	@linkwatch_dev_tracker:	refcount tracker used by linkwatch.
 *
 *	FIXME: cleanup struct net_device such that network protocol info
 *	moves out.
@@ -2178,6 +2191,7 @@ struct net_device {
#else
	refcount_t		dev_refcnt;
#endif
	struct ref_tracker_dir	refcnt_tracker;

	struct list_head	link_watch_list;

@@ -2267,6 +2281,7 @@ struct net_device {
	struct bpf_xdp_entity	xdp_state[__MAX_XDP_MODE];

	u8 dev_addr_shadow[MAX_ADDR_LEN];
	netdevice_tracker	linkwatch_dev_tracker;
};
#define to_net_dev(d) container_of(d, struct net_device, dev)

@@ -3805,6 +3820,7 @@ void netdev_run_todo(void);
 *	@dev: network device
 *
 * Release reference to device to allow it to be freed.
 * Try using dev_put_track() instead.
 */
static inline void dev_put(struct net_device *dev)
{
@@ -3822,6 +3838,7 @@ static inline void dev_put(struct net_device *dev)
 *	@dev: network device
 *
 * Hold reference to device to keep it from being freed.
 * Try using dev_hold_track() instead.
 */
static inline void dev_hold(struct net_device *dev)
{
@@ -3834,6 +3851,57 @@ static inline void dev_hold(struct net_device *dev)
	}
}

static inline void netdev_tracker_alloc(struct net_device *dev,
					netdevice_tracker *tracker, gfp_t gfp)
{
#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
	ref_tracker_alloc(&dev->refcnt_tracker, tracker, gfp);
#endif
}

static inline void netdev_tracker_free(struct net_device *dev,
				       netdevice_tracker *tracker)
{
#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
	ref_tracker_free(&dev->refcnt_tracker, tracker);
#endif
}

static inline void dev_hold_track(struct net_device *dev,
				  netdevice_tracker *tracker, gfp_t gfp)
{
	if (dev) {
		dev_hold(dev);
		netdev_tracker_alloc(dev, tracker, gfp);
	}
}

static inline void dev_put_track(struct net_device *dev,
				 netdevice_tracker *tracker)
{
	if (dev) {
		netdev_tracker_free(dev, tracker);
		dev_put(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.
+1 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ union inet_addr {

struct netpoll {
	struct net_device *dev;
	netdevice_tracker dev_tracker;
	char dev_name[IFNAMSIZ];
	const char *name;

Loading