[PATCH 3/7] arm64: hibernate: free MTE tag pages after saving

From: Bradley Morgan

Date: Tue Aug 25 2026 - 17:01:39 EST


swsusp_arch_suspend() calls swsusp_mte_save_tags() to stash the
userspace MTE tags, but we only free them on a successful resume in
swsusp_mte_restore_tags(). If hibernation fails partway, the tags stay
allocated, and the next time we try to hibernate,
swsusp_mte_save_tags() warns about duplicate entries in the mte_pages
xarray.

Reproducing it is easy, just make a hibernate fail twice in a row.

| # echo test_resume > /sys/power/disk
| # echo disk > /sys/power/state
...
| PM: Cannot find swap device, try swapon -a
| PM: Cannot get swap writer
...
| # echo disk > /sys/power/state
...
| ------------[ cut here ]------------
| swsusp: save_tags: Duplicate entry
| WARNING: CPU: 0 PID: 123 at arch/arm64/kernel/hibernate.c:234 \
| swsusp_arch_suspend+0x3f0/0x5b0
...

Free the tag storage right after swsusp_save() has copied it into the
hibernation image. Where hibernation succeeds, the tags come back from
the image and get consumed by swsusp_mte_restore_tags() as before.

Fixes: ee11f332af96 ("arm64: mte: Save tags when hibernating")
Signed-off-by: Bradley Morgan <brads@xxxxxxxxxxxxxx>
---
arch/arm64/kernel/hibernate.c | 10 ++++++++++
1 file changed, 10 insertions(+)

diff --git a/arch/arm64/kernel/hibernate.c b/arch/arm64/kernel/hibernate.c
index 7bf117427777..8ac29058a839 100644
--- a/arch/arm64/kernel/hibernate.c
+++ b/arch/arm64/kernel/hibernate.c
@@ -241,14 +241,18 @@ static int save_tags(struct page *page, unsigned long pfn)
static void swsusp_mte_free_storage(void)
{
XA_STATE(xa_state, &mte_pages, 0);
+ int n = 0;
void *tags;

xa_lock(&mte_pages);
xas_for_each(&xa_state, tags, ULONG_MAX) {
mte_free_tag_storage(tags);
+ n++;
}
xa_unlock(&mte_pages);

+ pr_info("Freed %d MTE pages\n", n);
+
xa_destroy(&mte_pages);
}

@@ -355,6 +359,12 @@ int swsusp_arch_suspend(void)

sleep_cpu = smp_processor_id();
ret = swsusp_save();
+
+ /*
+ * Hibernation can still fail from here, so free the tags now,
+ * a second attempt would warn on duplicate entries otherwise.
+ */
+ swsusp_mte_free_storage();
} else {
/* Clean kernel core startup/idle code to PoC*/
dcache_clean_inval_poc((unsigned long)__mmuoff_data_start,
--
2.47.3