All of lore.kernel.org
 help / color / mirror / Atom feed
From: Hao Ge <hao.ge@linux.dev>
To: Suren Baghdasaryan <surenb@google.com>,
	=Kent Overstreet <kent.overstreet@linux.dev>,
	Luis Chamberlain <mcgrof@kernel.org>,
	Petr Pavlu <petr.pavlu@suse.com>,
	Daniel Gomez <da.gomez@kernel.org>,
	Sami Tolvanen <samitolvanen@google.com>,
	Aaron Tomlin <atomlin@atomlin.com>,
	Andrew Morton <akpm@linux-foundation.org>,
	Alexander Potapenko <glider@google.com>,
	Marco Elver <elver@google.com>,
	Dmitry Vyukov <dvyukov@google.com>,
	Vlastimil Babka <vbabka@kernel.org>,
	Michal Hocko <mhocko@suse.com>,
	Brendan Jackman <brendan.jackman@linux.dev>,
	Johannes Weiner <hannes@cmpxchg.org>, Zi Yan <ziy@nvidia.com>,
	Uladzislau Rezki <urezki@gmail.com>
Cc: linux-mm@kvack.org, linux-kernel@vger.kernel.org,
	linux-modules@vger.kernel.org, kasan-dev@googlegroups.com,
	Hao Ge <hao.ge@linux.dev>, Sashiko <sashiko-bot@kernel.org>,
	stable@vger.kernel.org
Subject: [PATCH v11 7/7] alloc_tag: fix the /proc/allocinfo lifecycle
Date: Tue, 29 Sep 2026 16:20:14 +0800	[thread overview]
Message-ID: <20260929082014.160587-8-hao.ge@linux.dev> (raw)
In-Reply-To: <20260929082014.160587-1-hao.ge@linux.dev>

shutdown_mem_profiling() calls remove_proc_entry() from
reserve_module_tags(), which runs under mod_lock held for write.
remove_proc_entry() waits for readers, and a reader takes mod_lock for
read in allocinfo_start():

  CPU0 (insmod)                      CPU1 (read /proc/allocinfo)
  ----------------                   ----------------------------
  reserve_module_tags()
    down_write(&mod_lock)  [held]
                                     use_pde()            [in_use++]
                                     allocinfo_start()
                                       down_read(&mod_lock)  <- blocks
    shutdown_mem_profiling()
      remove_proc_entry()
        wait for in_use == 0         <- blocks

Move remove_proc_entry() to a workqueue.

The deferred removal also affects alloc_tag_init(). The file is
created before the type, so on a failure it is still there with
alloc_tag_cttype NULL or an error pointer, and a reader panics in
allocinfo_start(). Create the file at the end of alloc_tag_init()
instead, a failed init leaves nothing behind.

If proc_create() fails, the codetag type and the module tags memory
leak. Call codetag_unregister_type() and free the memory.

alloc_tag_cttype can now be freed at runtime. alloc_tag_top_users()
reads it from __show_mem() without locks, read the pointer under
rcu_read_lock() and take mod_lock before dropping the RCU lock, the
type stays alive until then. The only caller never sleeps, drop the
can_sleep argument.

Reported-by: Sashiko <sashiko-bot@kernel.org>
Fixes: 4835f747d3ed ("alloc_tag: support for page allocation tag compression")
Cc: stable@vger.kernel.org
Signed-off-by: Hao Ge <hao.ge@linux.dev>
---
 include/linux/alloc_tag.h |  2 +-
 include/linux/codetag.h   |  2 ++
 lib/codetag.c             | 26 ++++++++++++++++++++
 mm/alloc_tag.c            | 52 +++++++++++++++++++++++++++------------
 mm/show_mem.c             |  2 +-
 5 files changed, 66 insertions(+), 18 deletions(-)

diff --git a/include/linux/alloc_tag.h b/include/linux/alloc_tag.h
index 7f2d80a59792..852dc10c00ee 100644
--- a/include/linux/alloc_tag.h
+++ b/include/linux/alloc_tag.h
@@ -81,7 +81,7 @@ struct codetag_bytes {
 	s64 bytes;
 };
 
-size_t alloc_tag_top_users(struct codetag_bytes *tags, size_t count, bool can_sleep);
+size_t alloc_tag_top_users(struct codetag_bytes *tags, size_t count);
 
 static inline struct alloc_tag *ct_to_alloc_tag(struct codetag *ct)
 {
diff --git a/include/linux/codetag.h b/include/linux/codetag.h
index a25a085c2df1..0c4e0337b474 100644
--- a/include/linux/codetag.h
+++ b/include/linux/codetag.h
@@ -87,6 +87,8 @@ void codetag_to_text(struct seq_buf *out, struct codetag *ct);
 struct codetag_type *
 codetag_register_type(const struct codetag_type_desc *desc);
 
+void codetag_unregister_type(struct codetag_type *cttype);
+
 #if defined(CONFIG_CODE_TAGGING) && defined(CONFIG_MODULES)
 
 bool codetag_needs_module_section(struct module *mod, const char *name,
diff --git a/lib/codetag.c b/lib/codetag.c
index a0b600720afc..46d0904b08b3 100644
--- a/lib/codetag.c
+++ b/lib/codetag.c
@@ -429,3 +429,29 @@ codetag_register_type(const struct codetag_type_desc *desc)
 
 	return cttype;
 }
+
+/**
+ * codetag_unregister_type - unregister a codetag type
+ * @cttype: the codetag type to unregister
+ *
+ * Undo codetag_register_type() and free @cttype. The caller must make
+ * sure no lockless reader still uses @cttype, e.g. clear the pointer
+ * to it and wait for an RCU grace period first.
+ */
+void __init codetag_unregister_type(struct codetag_type *cttype)
+{
+	struct codetag_module *cmod;
+	unsigned long id, tmp;
+
+	mutex_lock(&codetag_lock);
+	list_del(&cttype->link);
+	mutex_unlock(&codetag_lock);
+
+	down_write(&cttype->mod_lock);
+	idr_for_each_entry_ul(&cttype->mod_idr, cmod, tmp, id)
+		kfree(cmod);
+	idr_destroy(&cttype->mod_idr);
+	up_write(&cttype->mod_lock);
+
+	kfree(cttype);
+}
diff --git a/mm/alloc_tag.c b/mm/alloc_tag.c
index ba8a651769e3..b9af5fe5bba2 100644
--- a/mm/alloc_tag.c
+++ b/mm/alloc_tag.c
@@ -15,6 +15,7 @@
 #include <linux/seq_file.h>
 #include <linux/string_choices.h>
 #include <linux/vmalloc.h>
+#include <linux/workqueue.h>
 #include <linux/kmemleak.h>
 #include <uapi/linux/alloc_tag.h>
 
@@ -484,22 +485,28 @@ static const struct proc_ops allocinfo_proc_ops = {
 #endif
 };
 
-size_t alloc_tag_top_users(struct codetag_bytes *tags, size_t count, bool can_sleep)
+size_t alloc_tag_top_users(struct codetag_bytes *tags, size_t count)
 {
 	struct codetag_iterator iter;
+	struct codetag_type *cttype;
 	struct codetag *ct;
 	struct codetag_bytes n;
 	unsigned int i, nr = 0;
+	bool locked;
 
-	if (IS_ERR_OR_NULL(alloc_tag_cttype))
+	rcu_read_lock();
+	cttype = READ_ONCE(alloc_tag_cttype);
+	if (IS_ERR_OR_NULL(cttype)) {
+		rcu_read_unlock();
 		return 0;
+	}
 
-	if (can_sleep)
-		codetag_lock_module_list(alloc_tag_cttype);
-	else if (!codetag_trylock_module_list(alloc_tag_cttype))
+	locked = codetag_trylock_module_list(cttype);
+	rcu_read_unlock();
+	if (!locked)
 		return 0;
 
-	iter = codetag_get_ct_iter(alloc_tag_cttype);
+	iter = codetag_get_ct_iter(cttype);
 	while ((ct = codetag_next_ct(&iter))) {
 		struct alloc_tag_counters counter = alloc_tag_read(ct_to_alloc_tag(ct));
 
@@ -520,7 +527,7 @@ size_t alloc_tag_top_users(struct codetag_bytes *tags, size_t count, bool can_sl
 		}
 	}
 
-	codetag_unlock_module_list(alloc_tag_cttype);
+	codetag_unlock_module_list(cttype);
 
 	return nr;
 }
@@ -591,6 +598,13 @@ void pgalloc_tag_swap(struct folio *new, struct folio *old)
 	put_page_tag_ref(handle_new);
 }
 
+static void remove_allocinfo_file(struct work_struct *work)
+{
+	remove_proc_entry(ALLOCINFO_FILE_NAME, NULL);
+}
+
+static DECLARE_WORK(remove_allocinfo_work, remove_allocinfo_file);
+
 static void shutdown_mem_profiling(bool remove_file)
 {
 	if (mem_alloc_profiling_enabled())
@@ -600,7 +614,7 @@ static void shutdown_mem_profiling(bool remove_file)
 		return;
 
 	if (remove_file)
-		remove_proc_entry(ALLOCINFO_FILE_NAME, NULL);
+		schedule_work(&remove_allocinfo_work);
 	mem_profiling_support = false;
 }
 
@@ -1351,16 +1365,10 @@ static int __init alloc_tag_init(void)
 		return 0;
 	}
 
-	if (!proc_create(ALLOCINFO_FILE_NAME, 0400, NULL, &allocinfo_proc_ops)) {
-		pr_err("Failed to create %s file\n", ALLOCINFO_FILE_NAME);
-		shutdown_mem_profiling(false);
-		return -ENOMEM;
-	}
-
 	res = alloc_mod_tags_mem();
 	if (res) {
 		pr_err("Failed to reserve address space for module tags, errno = %d\n", res);
-		shutdown_mem_profiling(true);
+		shutdown_mem_profiling(false);
 		return res;
 	}
 
@@ -1368,10 +1376,22 @@ static int __init alloc_tag_init(void)
 	if (IS_ERR(alloc_tag_cttype)) {
 		pr_err("Allocation tags registration failed, errno = %pe\n", alloc_tag_cttype);
 		free_mod_tags_mem();
-		shutdown_mem_profiling(true);
+		shutdown_mem_profiling(false);
 		return PTR_ERR(alloc_tag_cttype);
 	}
 
+	if (!proc_create(ALLOCINFO_FILE_NAME, 0400, NULL, &allocinfo_proc_ops)) {
+		struct codetag_type *cttype = alloc_tag_cttype;
+
+		pr_err("Failed to create %s file\n", ALLOCINFO_FILE_NAME);
+		shutdown_mem_profiling(false);
+		WRITE_ONCE(alloc_tag_cttype, NULL);
+		synchronize_rcu();
+		codetag_unregister_type(cttype);
+		free_mod_tags_mem();
+		return -ENOMEM;
+	}
+
 	return 0;
 }
 module_init(alloc_tag_init);
diff --git a/mm/show_mem.c b/mm/show_mem.c
index b938cbcd774a..a2e710404a48 100644
--- a/mm/show_mem.c
+++ b/mm/show_mem.c
@@ -439,7 +439,7 @@ void __show_mem(unsigned int filter, const nodemask_t *nodemask,
 		struct codetag_bytes tags[10];
 		size_t i, nr;
 
-		nr = alloc_tag_top_users(tags, ARRAY_SIZE(tags), false);
+		nr = alloc_tag_top_users(tags, ARRAY_SIZE(tags));
 		if (nr) {
 			pr_notice("Memory allocations (profiling is currently turned %s):\n",
 				mem_alloc_profiling_enabled() ? "on" : "off");
-- 
2.25.1



  parent reply	other threads:[~2026-09-29  8:20 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-29  8:20 [PATCH v11 0/7] alloc_tag and module codetag section fixes Hao Ge
2026-09-29  8:20 ` [PATCH v11 1/7] alloc_tag: move release_module_tags() above reserve_module_tags() Hao Ge
2026-09-29  8:20 ` [PATCH v11 2/7] mm/vmalloc: undo partial mappings inside the mapping functions Hao Ge
2026-09-29  8:33   ` sashiko-bot
2026-09-30 10:12   ` Uladzislau Rezki
2026-10-04 17:42     ` Suren Baghdasaryan
2026-10-09  8:23       ` Hao Ge
2026-10-08  6:53     ` Hao Ge
2026-10-08  8:09       ` Hao Ge
2026-10-08  8:13       ` Hao Ge
2026-09-29  8:20 ` [PATCH v11 3/7] alloc_tag: clean up the populate failure path Hao Ge
2026-10-04 17:55   ` Suren Baghdasaryan
2026-09-29  8:20 ` [PATCH v11 4/7] module: introduce SH_ENTSIZE_STANDALONE for separately allocated sections Hao Ge
2026-09-29  8:20 ` [PATCH v11 5/7] module: allocate codetag sections before the regular module layout Hao Ge
2026-09-29  8:20 ` [PATCH v11 6/7] alloc_tag: skip percpu counter allocation when profiling is disabled Hao Ge
2026-09-29  8:40   ` sashiko-bot
2026-09-29  8:20 ` Hao Ge [this message]
2026-10-04 18:23   ` [PATCH v11 7/7] alloc_tag: fix the /proc/allocinfo lifecycle Suren Baghdasaryan
2026-10-09 11:16     ` Hao Ge
2026-09-29 20:44 ` [PATCH v11 0/7] alloc_tag and module codetag section fixes Andrew Morton
2026-09-30  2:39   ` Suren Baghdasaryan
2026-09-30  3:26     ` Hao Ge
2026-09-30  8:34       ` Uladzislau Rezki
2026-10-04 17:42       ` Suren Baghdasaryan
2026-10-04 18:37       ` Suren Baghdasaryan
2026-10-09 11:30         ` Hao Ge

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=20260929082014.160587-8-hao.ge@linux.dev \
    --to=hao.ge@linux.dev \
    --cc=akpm@linux-foundation.org \
    --cc=atomlin@atomlin.com \
    --cc=brendan.jackman@linux.dev \
    --cc=da.gomez@kernel.org \
    --cc=dvyukov@google.com \
    --cc=elver@google.com \
    --cc=glider@google.com \
    --cc=hannes@cmpxchg.org \
    --cc=kasan-dev@googlegroups.com \
    --cc=kent.overstreet@linux.dev \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=linux-modules@vger.kernel.org \
    --cc=mcgrof@kernel.org \
    --cc=mhocko@suse.com \
    --cc=petr.pavlu@suse.com \
    --cc=samitolvanen@google.com \
    --cc=sashiko-bot@kernel.org \
    --cc=stable@vger.kernel.org \
    --cc=surenb@google.com \
    --cc=urezki@gmail.com \
    --cc=vbabka@kernel.org \
    --cc=ziy@nvidia.com \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.