netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Jakub Kicinski <kuba@kernel.org>
To: netdev@vger.kernel.org
Cc: davem@davemloft.net, eric.dumazet@gmail.com,
	Jakub Kicinski <kuba@kernel.org>
Subject: [RFC net-next 1/2] net: add netdev_refs debug
Date: Wed, 17 Nov 2021 09:47:22 -0800	[thread overview]
Message-ID: <20211117174723.2305681-2-kuba@kernel.org> (raw)
In-Reply-To: <20211117174723.2305681-1-kuba@kernel.org>

Debugging netdev ref leaks is still pretty hard. Eric added
optional use of a normal refcount which is useful for tracking
abuse of existing users.

For new code, however, it'd be great if we could actually track
the refs per-user. Allowing us to detect leaks where they happen.
This patch introduces a netdev_ref type and uses the debug_objects
infra to track refs being lost or misused.

In the future we can extend this structure to also catch those
who fail to release the ref on unregistering notification.

Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
 MAINTAINERS                 |   1 +
 include/linux/netdev_refs.h | 104 ++++++++++++++++++++++++++++++++++++
 lib/Kconfig.debug           |   7 +++
 net/core/dev.c              |   8 +++
 4 files changed, 120 insertions(+)
 create mode 100644 include/linux/netdev_refs.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 4c74516e4353..47fe27175c9f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -18482,6 +18482,7 @@ F:	include/uapi/linux/pkt_sched.h
 F:	include/uapi/linux/tc_act/
 F:	include/uapi/linux/tc_ematch/
 F:	net/sched/
+F:	tools/testing/selftests/tc-testing/
 
 TC90522 MEDIA DRIVER
 M:	Akihiro Tsukada <tskd08@gmail.com>
diff --git a/include/linux/netdev_refs.h b/include/linux/netdev_refs.h
new file mode 100644
index 000000000000..326772ea0a63
--- /dev/null
+++ b/include/linux/netdev_refs.h
@@ -0,0 +1,104 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef _LINUX_NETDEV_REFS_H
+#define _LINUX_NETDEV_REFS_H
+
+#include <linux/debugobjects.h>
+#include <linux/netdevice.h>
+
+/* Explicit netdevice references
+ * struct netdev_ref is a storage for a reference. It's equivalent
+ * to a netdev pointer, but when debug is enabled it performs extra checks.
+ * Most users will want to take a reference with netdev_hold(), access it
+ * via netdev_ref_ptr() and release with netdev_put().
+ */
+
+struct netdev_ref {
+	struct net_device *dev;
+#ifdef CONFIG_DEBUG_OBJECTS_NETDEV_REFS
+	refcount_t cnt;
+#endif
+};
+
+extern const struct debug_obj_descr netdev_ref_debug_descr;
+
+/* Store a raw, unprotected pointer */
+static inline void __netdev_ref_store(struct netdev_ref *ref,
+				      struct net_device *dev)
+{
+	ref->dev = dev;
+
+#ifdef CONFIG_DEBUG_OBJECTS_NETDEV_REFS
+	refcount_set(&ref->cnt, 0);
+	debug_object_init(ref, &netdev_ref_debug_descr);
+#endif
+}
+
+/* Convert a previously stored unprotected pointer to a normal ref */
+static inline void __netdev_hold_stored(struct netdev_ref *ref)
+{
+	dev_hold(ref->dev);
+
+#ifdef CONFIG_DEBUG_OBJECTS_NETDEV_REFS
+	refcount_set(&ref->cnt, 1);
+	debug_object_activate(ref, &netdev_ref_debug_descr);
+#endif
+}
+
+/* Take a reference on a netdev and store it in @ref */
+static inline void netdev_hold(struct netdev_ref *ref, struct net_device *dev)
+{
+	__netdev_ref_store(ref, dev);
+	__netdev_hold_stored(ref);
+}
+
+/* Release a reference on a netdev previously acquired by netdev_hold() */
+static inline void netdev_put(struct netdev_ref *ref)
+{
+	dev_put(ref->dev);
+
+#ifdef CONFIG_DEBUG_OBJECTS_NETDEV_REFS
+	WARN_ON(refcount_read(&ref->cnt) != 1);
+	debug_object_deactivate(ref, &netdev_ref_debug_descr);
+#endif
+}
+
+/* Increase refcount of a reference, reference must be valid -
+ * initialized by netdev_hold() or equivalent set of sub-functions.
+ */
+static inline void netdev_ref_get(struct netdev_ref *ref)
+{
+	dev_hold(ref->dev);
+
+#ifdef CONFIG_DEBUG_OBJECTS_NETDEV_REFS
+	refcount_inc(&ref->cnt);
+#endif
+}
+
+/* Release a reference with unknown number of refs */
+static inline void netdev_ref_put(struct netdev_ref *ref)
+{
+	dev_put(ref->dev);
+
+#ifdef CONFIG_DEBUG_OBJECTS_NETDEV_REFS
+	if (refcount_dec_and_test(&ref->cnt))
+		debug_object_deactivate(ref, &netdev_ref_debug_descr);
+#endif
+}
+
+/* Unprotected access to a pointer stored by __netdev_ref_store() */
+static inline struct net_device *__netdev_ref_ptr(const struct netdev_ref *ref)
+{
+	return ref->dev;
+}
+
+/* Netdev pointer access on a normal ref */
+static inline struct net_device *netdev_ref_ptr(const struct netdev_ref *ref)
+{
+#ifdef CONFIG_DEBUG_OBJECTS_NETDEV_REFS
+	WARN_ON(!refcount_read(&ref->cnt));
+#endif
+	return ref->dev;
+}
+
+#endif
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 9ef7ce18b4f5..e07b1cbb4228 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -655,6 +655,13 @@ config DEBUG_OBJECTS_PERCPU_COUNTER
 	  percpu counter routines to track the life time of percpu counter
 	  objects and validate the percpu counter operations.
 
+config DEBUG_OBJECTS_NETDEV_REFS
+	bool "Debug net_device references"
+	depends on DEBUG_OBJECTS
+	help
+	  If you say Y here, additional code will be inserted into the
+	  net_device reference routines to catch incorrect use.
+
 config DEBUG_OBJECTS_ENABLE_DEFAULT
 	int "debug_objects bootup default value (0-1)"
 	range 0 1
diff --git a/net/core/dev.c b/net/core/dev.c
index 92c9258cbf28..c8c9be59de89 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -150,6 +150,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/prandom.h>
 #include <linux/once_lite.h>
+#include <linux/netdev_refs.h>
 
 #include "net-sysfs.h"
 
@@ -158,6 +159,13 @@ static DEFINE_SPINLOCK(ptype_lock);
 struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly;
 struct list_head ptype_all __read_mostly;	/* Taps */
 
+const struct debug_obj_descr netdev_ref_debug_descr = {
+	.name		= "netdev_ref",
+};
+#ifdef CONFIG_DEBUG_OBJECTS_NETDEV_REFS
+EXPORT_SYMBOL(netdev_ref_debug_descr);
+#endif
+
 static int netif_rx_internal(struct sk_buff *skb);
 static int call_netdevice_notifiers_info(unsigned long val,
 					 struct netdev_notifier_info *info);
-- 
2.31.1


  reply	other threads:[~2021-11-17 17:47 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-11-17 17:47 [RFC net-next 0/2] explicit netdev refs Jakub Kicinski
2021-11-17 17:47 ` Jakub Kicinski [this message]
2021-11-17 18:24   ` [RFC net-next 1/2] net: add netdev_refs debug Leon Romanovsky
2021-11-17 18:35     ` Jakub Kicinski
2021-11-17 19:27       ` Leon Romanovsky
2021-11-17 19:13   ` Eric Dumazet
2021-11-17 17:47 ` [RFC net-next 2/2] vlan: use new netdev_refs infra Jakub Kicinski

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20211117174723.2305681-2-kuba@kernel.org \
    --to=kuba@kernel.org \
    --cc=davem@davemloft.net \
    --cc=eric.dumazet@gmail.com \
    --cc=netdev@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).