--- /dev/null
+From 28aaa9c39945b7925a1cc1d513c8f21ed38f5e4f Mon Sep 17 00:00:00 2001
+From: Christian Brauner <brauner@kernel.org>
+Date: Thu, 26 Feb 2026 10:43:55 +0100
+Subject: kthread: consolidate kthread exit paths to prevent use-after-free
+
+From: Christian Brauner <brauner@kernel.org>
+
+commit 28aaa9c39945b7925a1cc1d513c8f21ed38f5e4f upstream.
+
+Guillaume reported crashes via corrupted RCU callback function pointers
+during KUnit testing. The crash was traced back to the pidfs rhashtable
+conversion which replaced the 24-byte rb_node with an 8-byte rhash_head
+in struct pid, shrinking it from 160 to 144 bytes.
+
+struct kthread (without CONFIG_BLK_CGROUP) is also 144 bytes. With
+CONFIG_SLAB_MERGE_DEFAULT and SLAB_HWCACHE_ALIGN both round up to
+192 bytes and share the same slab cache. struct pid.rcu.func and
+struct kthread.affinity_node both sit at offset 0x78.
+
+When a kthread exits via make_task_dead() it bypasses kthread_exit() and
+misses the affinity_node cleanup. free_kthread_struct() frees the memory
+while the node is still linked into the global kthread_affinity_list. A
+subsequent list_del() by another kthread writes through dangling list
+pointers into the freed and reused memory, corrupting the pid's
+rcu.func pointer.
+
+Instead of patching free_kthread_struct() to handle the missed cleanup,
+consolidate all kthread exit paths. Turn kthread_exit() into a macro
+that calls do_exit() and add kthread_do_exit() which is called from
+do_exit() for any task with PF_KTHREAD set. This guarantees that
+kthread-specific cleanup always happens regardless of the exit path -
+make_task_dead(), direct do_exit(), or kthread_exit().
+
+Replace __to_kthread() with a new tsk_is_kthread() accessor in the
+public header. Export do_exit() since module code using the
+kthread_exit() macro now needs it directly.
+
+Reported-by: Guillaume Tucker <gtucker@gtucker.io>
+Tested-by: Guillaume Tucker <gtucker@gtucker.io>
+Tested-by: Mark Brown <broonie@kernel.org>
+Tested-by: David Gow <davidgow@google.com>
+Cc: <stable@vger.kernel.org>
+Link: https://lore.kernel.org/all/20260224-mittlerweile-besessen-2738831ae7f6@brauner
+Co-developed-by: Linus Torvalds <torvalds@linux-foundation.org>
+Fixes: 4d13f4304fa4 ("kthread: Implement preferred affinity")
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/kthread.h | 21 ++++++++++++++++++++-
+ kernel/exit.c | 6 ++++++
+ kernel/kthread.c | 41 +++++------------------------------------
+ 3 files changed, 31 insertions(+), 37 deletions(-)
+
+--- a/include/linux/kthread.h
++++ b/include/linux/kthread.h
+@@ -7,6 +7,24 @@
+
+ struct mm_struct;
+
++/* opaque kthread data */
++struct kthread;
++
++/*
++ * When "(p->flags & PF_KTHREAD)" is set the task is a kthread and will
++ * always remain a kthread. For kthreads p->worker_private always
++ * points to a struct kthread. For tasks that are not kthreads
++ * p->worker_private is used to point to other things.
++ *
++ * Return NULL for any task that is not a kthread.
++ */
++static inline struct kthread *tsk_is_kthread(struct task_struct *p)
++{
++ if (p->flags & PF_KTHREAD)
++ return p->worker_private;
++ return NULL;
++}
++
+ __printf(4, 5)
+ struct task_struct *kthread_create_on_node(int (*threadfn)(void *data),
+ void *data,
+@@ -98,8 +116,9 @@ void *kthread_probe_data(struct task_str
+ int kthread_park(struct task_struct *k);
+ void kthread_unpark(struct task_struct *k);
+ void kthread_parkme(void);
+-void kthread_exit(long result) __noreturn;
++#define kthread_exit(result) do_exit(result)
+ void kthread_complete_and_exit(struct completion *, long) __noreturn;
++void kthread_do_exit(struct kthread *, long);
+
+ int kthreadd(void *unused);
+ extern struct task_struct *kthreadd_task;
+--- a/kernel/exit.c
++++ b/kernel/exit.c
+@@ -896,11 +896,16 @@ static void synchronize_group_exit(struc
+ void __noreturn do_exit(long code)
+ {
+ struct task_struct *tsk = current;
++ struct kthread *kthread;
+ int group_dead;
+
+ WARN_ON(irqs_disabled());
+ WARN_ON(tsk->plug);
+
++ kthread = tsk_is_kthread(tsk);
++ if (unlikely(kthread))
++ kthread_do_exit(kthread, code);
++
+ kcov_task_exit(tsk);
+ kmsan_task_exit(tsk);
+
+@@ -1013,6 +1018,7 @@ void __noreturn do_exit(long code)
+ lockdep_free_task(tsk);
+ do_task_dead();
+ }
++EXPORT_SYMBOL(do_exit);
+
+ void __noreturn make_task_dead(int signr)
+ {
+--- a/kernel/kthread.c
++++ b/kernel/kthread.c
+@@ -85,24 +85,6 @@ static inline struct kthread *to_kthread
+ return k->worker_private;
+ }
+
+-/*
+- * Variant of to_kthread() that doesn't assume @p is a kthread.
+- *
+- * When "(p->flags & PF_KTHREAD)" is set the task is a kthread and will
+- * always remain a kthread. For kthreads p->worker_private always
+- * points to a struct kthread. For tasks that are not kthreads
+- * p->worker_private is used to point to other things.
+- *
+- * Return NULL for any task that is not a kthread.
+- */
+-static inline struct kthread *__to_kthread(struct task_struct *p)
+-{
+- void *kthread = p->worker_private;
+- if (kthread && !(p->flags & PF_KTHREAD))
+- kthread = NULL;
+- return kthread;
+-}
+-
+ void get_kthread_comm(char *buf, size_t buf_size, struct task_struct *tsk)
+ {
+ struct kthread *kthread = to_kthread(tsk);
+@@ -193,7 +175,7 @@ EXPORT_SYMBOL_GPL(kthread_should_park);
+
+ bool kthread_should_stop_or_park(void)
+ {
+- struct kthread *kthread = __to_kthread(current);
++ struct kthread *kthread = tsk_is_kthread(current);
+
+ if (!kthread)
+ return false;
+@@ -234,7 +216,7 @@ EXPORT_SYMBOL_GPL(kthread_freezable_shou
+ */
+ void *kthread_func(struct task_struct *task)
+ {
+- struct kthread *kthread = __to_kthread(task);
++ struct kthread *kthread = tsk_is_kthread(task);
+ if (kthread)
+ return kthread->threadfn;
+ return NULL;
+@@ -266,7 +248,7 @@ EXPORT_SYMBOL_GPL(kthread_data);
+ */
+ void *kthread_probe_data(struct task_struct *task)
+ {
+- struct kthread *kthread = __to_kthread(task);
++ struct kthread *kthread = tsk_is_kthread(task);
+ void *data = NULL;
+
+ if (kthread)
+@@ -309,19 +291,8 @@ void kthread_parkme(void)
+ }
+ EXPORT_SYMBOL_GPL(kthread_parkme);
+
+-/**
+- * kthread_exit - Cause the current kthread return @result to kthread_stop().
+- * @result: The integer value to return to kthread_stop().
+- *
+- * While kthread_exit can be called directly, it exists so that
+- * functions which do some additional work in non-modular code such as
+- * module_put_and_kthread_exit can be implemented.
+- *
+- * Does not return.
+- */
+-void __noreturn kthread_exit(long result)
++void kthread_do_exit(struct kthread *kthread, long result)
+ {
+- struct kthread *kthread = to_kthread(current);
+ kthread->result = result;
+ if (!list_empty(&kthread->hotplug_node)) {
+ mutex_lock(&kthreads_hotplug_lock);
+@@ -333,9 +304,7 @@ void __noreturn kthread_exit(long result
+ kthread->preferred_affinity = NULL;
+ }
+ }
+- do_exit(0);
+ }
+-EXPORT_SYMBOL(kthread_exit);
+
+ /**
+ * kthread_complete_and_exit - Exit the current kthread.
+@@ -680,7 +649,7 @@ void kthread_set_per_cpu(struct task_str
+
+ bool kthread_is_per_cpu(struct task_struct *p)
+ {
+- struct kthread *kthread = __to_kthread(p);
++ struct kthread *kthread = tsk_is_kthread(p);
+ if (!kthread)
+ return false;
+
--- /dev/null
+From f85b1c6af5bc3872f994df0a5688c1162de07a62 Mon Sep 17 00:00:00 2001
+From: "Pratyush Yadav (Google)" <pratyush@kernel.org>
+Date: Mon, 16 Feb 2026 14:22:19 +0100
+Subject: liveupdate: luo_file: remember retrieve() status
+
+From: Pratyush Yadav (Google) <pratyush@kernel.org>
+
+commit f85b1c6af5bc3872f994df0a5688c1162de07a62 upstream.
+
+LUO keeps track of successful retrieve attempts on a LUO file. It does so
+to avoid multiple retrievals of the same file. Multiple retrievals cause
+problems because once the file is retrieved, the serialized data
+structures are likely freed and the file is likely in a very different
+state from what the code expects.
+
+The retrieve boolean in struct luo_file keeps track of this, and is passed
+to the finish callback so it knows what work was already done and what it
+has left to do.
+
+All this works well when retrieve succeeds. When it fails,
+luo_retrieve_file() returns the error immediately, without ever storing
+anywhere that a retrieve was attempted or what its error code was. This
+results in an errored LIVEUPDATE_SESSION_RETRIEVE_FD ioctl to userspace,
+but nothing prevents it from trying this again.
+
+The retry is problematic for much of the same reasons listed above. The
+file is likely in a very different state than what the retrieve logic
+normally expects, and it might even have freed some serialization data
+structures. Attempting to access them or free them again is going to
+break things.
+
+For example, if memfd managed to restore 8 of its 10 folios, but fails on
+the 9th, a subsequent retrieve attempt will try to call
+kho_restore_folio() on the first folio again, and that will fail with a
+warning since it is an invalid operation.
+
+Apart from the retry, finish() also breaks. Since on failure the
+retrieved bool in luo_file is never touched, the finish() call on session
+close will tell the file handler that retrieve was never attempted, and it
+will try to access or free the data structures that might not exist, much
+in the same way as the retry attempt.
+
+There is no sane way of attempting the retrieve again. Remember the error
+retrieve returned and directly return it on a retry. Also pass this
+status code to finish() so it can make the right decision on the work it
+needs to do.
+
+This is done by changing the bool to an integer. A value of 0 means
+retrieve was never attempted, a positive value means it succeeded, and a
+negative value means it failed and the error code is the value.
+
+Link: https://lkml.kernel.org/r/20260216132221.987987-1-pratyush@kernel.org
+Fixes: 7c722a7f44e0 ("liveupdate: luo_file: implement file systems callbacks")
+Signed-off-by: Pratyush Yadav (Google) <pratyush@kernel.org>
+Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
+Cc: Pasha Tatashin <pasha.tatashin@soleen.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/liveupdate.h | 9 ++++++---
+ kernel/liveupdate/luo_file.c | 41 +++++++++++++++++++++++++----------------
+ mm/memfd_luo.c | 7 ++++++-
+ 3 files changed, 37 insertions(+), 20 deletions(-)
+
+--- a/include/linux/liveupdate.h
++++ b/include/linux/liveupdate.h
+@@ -20,8 +20,11 @@ struct file;
+ /**
+ * struct liveupdate_file_op_args - Arguments for file operation callbacks.
+ * @handler: The file handler being called.
+- * @retrieved: The retrieve status for the 'can_finish / finish'
+- * operation.
++ * @retrieve_status: The retrieve status for the 'can_finish / finish'
++ * operation. A value of 0 means the retrieve has not been
++ * attempted, a positive value means the retrieve was
++ * successful, and a negative value means the retrieve failed,
++ * and the value is the error code of the call.
+ * @file: The file object. For retrieve: [OUT] The callback sets
+ * this to the new file. For other ops: [IN] The caller sets
+ * this to the file being operated on.
+@@ -37,7 +40,7 @@ struct file;
+ */
+ struct liveupdate_file_op_args {
+ struct liveupdate_file_handler *handler;
+- bool retrieved;
++ int retrieve_status;
+ struct file *file;
+ u64 serialized_data;
+ void *private_data;
+--- a/kernel/liveupdate/luo_file.c
++++ b/kernel/liveupdate/luo_file.c
+@@ -133,9 +133,12 @@ static LIST_HEAD(luo_file_handler_list);
+ * state that is not preserved. Set by the handler's .preserve()
+ * callback, and must be freed in the handler's .unpreserve()
+ * callback.
+- * @retrieved: A flag indicating whether a user/kernel in the new kernel has
++ * @retrieve_status: Status code indicating whether a user/kernel in the new kernel has
+ * successfully called retrieve() on this file. This prevents
+- * multiple retrieval attempts.
++ * multiple retrieval attempts. A value of 0 means a retrieve()
++ * has not been attempted, a positive value means the retrieve()
++ * was successful, and a negative value means the retrieve()
++ * failed, and the value is the error code of the call.
+ * @mutex: A mutex that protects the fields of this specific instance
+ * (e.g., @retrieved, @file), ensuring that operations like
+ * retrieving or finishing a file are atomic.
+@@ -160,7 +163,7 @@ struct luo_file {
+ struct file *file;
+ u64 serialized_data;
+ void *private_data;
+- bool retrieved;
++ int retrieve_status;
+ struct mutex mutex;
+ struct list_head list;
+ u64 token;
+@@ -293,7 +296,6 @@ int luo_preserve_file(struct luo_file_se
+ luo_file->file = file;
+ luo_file->fh = fh;
+ luo_file->token = token;
+- luo_file->retrieved = false;
+ mutex_init(&luo_file->mutex);
+
+ args.handler = fh;
+@@ -569,7 +571,12 @@ int luo_retrieve_file(struct luo_file_se
+ return -ENOENT;
+
+ guard(mutex)(&luo_file->mutex);
+- if (luo_file->retrieved) {
++ if (luo_file->retrieve_status < 0) {
++ /* Retrieve was attempted and it failed. Return the error code. */
++ return luo_file->retrieve_status;
++ }
++
++ if (luo_file->retrieve_status > 0) {
+ /*
+ * Someone is asking for this file again, so get a reference
+ * for them.
+@@ -582,16 +589,19 @@ int luo_retrieve_file(struct luo_file_se
+ args.handler = luo_file->fh;
+ args.serialized_data = luo_file->serialized_data;
+ err = luo_file->fh->ops->retrieve(&args);
+- if (!err) {
+- luo_file->file = args.file;
+-
+- /* Get reference so we can keep this file in LUO until finish */
+- get_file(luo_file->file);
+- *filep = luo_file->file;
+- luo_file->retrieved = true;
++ if (err) {
++ /* Keep the error code for later use. */
++ luo_file->retrieve_status = err;
++ return err;
+ }
+
+- return err;
++ luo_file->file = args.file;
++ /* Get reference so we can keep this file in LUO until finish */
++ get_file(luo_file->file);
++ *filep = luo_file->file;
++ luo_file->retrieve_status = 1;
++
++ return 0;
+ }
+
+ static int luo_file_can_finish_one(struct luo_file_set *file_set,
+@@ -607,7 +617,7 @@ static int luo_file_can_finish_one(struc
+ args.handler = luo_file->fh;
+ args.file = luo_file->file;
+ args.serialized_data = luo_file->serialized_data;
+- args.retrieved = luo_file->retrieved;
++ args.retrieve_status = luo_file->retrieve_status;
+ can_finish = luo_file->fh->ops->can_finish(&args);
+ }
+
+@@ -624,7 +634,7 @@ static void luo_file_finish_one(struct l
+ args.handler = luo_file->fh;
+ args.file = luo_file->file;
+ args.serialized_data = luo_file->serialized_data;
+- args.retrieved = luo_file->retrieved;
++ args.retrieve_status = luo_file->retrieve_status;
+
+ luo_file->fh->ops->finish(&args);
+ }
+@@ -779,7 +789,6 @@ int luo_file_deserialize(struct luo_file
+ luo_file->file = NULL;
+ luo_file->serialized_data = file_ser[i].data;
+ luo_file->token = file_ser[i].token;
+- luo_file->retrieved = false;
+ mutex_init(&luo_file->mutex);
+ list_add_tail(&luo_file->list, &file_set->files_list);
+ }
+--- a/mm/memfd_luo.c
++++ b/mm/memfd_luo.c
+@@ -326,7 +326,12 @@ static void memfd_luo_finish(struct live
+ struct memfd_luo_folio_ser *folios_ser;
+ struct memfd_luo_ser *ser;
+
+- if (args->retrieved)
++ /*
++ * If retrieve was successful, nothing to do. If it failed, retrieve()
++ * already cleaned up everything it could. So nothing to do there
++ * either. Only need to clean up when retrieve was not called.
++ */
++ if (args->retrieve_status)
+ return;
+
+ ser = phys_to_virt(args->serialized_data);
--- /dev/null
+From b570f37a2ce480be26c665345c5514686a8a0274 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Thomas=20Hellstr=C3=B6m?= <thomas.hellstrom@linux.intel.com>
+Date: Tue, 10 Feb 2026 12:56:53 +0100
+Subject: mm: Fix a hmm_range_fault() livelock / starvation problem
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Thomas Hellström <thomas.hellstrom@linux.intel.com>
+
+commit b570f37a2ce480be26c665345c5514686a8a0274 upstream.
+
+If hmm_range_fault() fails a folio_trylock() in do_swap_page,
+trying to acquire the lock of a device-private folio for migration,
+to ram, the function will spin until it succeeds grabbing the lock.
+
+However, if the process holding the lock is depending on a work
+item to be completed, which is scheduled on the same CPU as the
+spinning hmm_range_fault(), that work item might be starved and
+we end up in a livelock / starvation situation which is never
+resolved.
+
+This can happen, for example if the process holding the
+device-private folio lock is stuck in
+ migrate_device_unmap()->lru_add_drain_all()
+sinc lru_add_drain_all() requires a short work-item
+to be run on all online cpus to complete.
+
+A prerequisite for this to happen is:
+a) Both zone device and system memory folios are considered in
+ migrate_device_unmap(), so that there is a reason to call
+ lru_add_drain_all() for a system memory folio while a
+ folio lock is held on a zone device folio.
+b) The zone device folio has an initial mapcount > 1 which causes
+ at least one migration PTE entry insertion to be deferred to
+ try_to_migrate(), which can happen after the call to
+ lru_add_drain_all().
+c) No or voluntary only preemption.
+
+This all seems pretty unlikely to happen, but indeed is hit by
+the "xe_exec_system_allocator" igt test.
+
+Resolve this by waiting for the folio to be unlocked if the
+folio_trylock() fails in do_swap_page().
+
+Rename migration_entry_wait_on_locked() to
+softleaf_entry_wait_unlock() and update its documentation to
+indicate the new use-case.
+
+Future code improvements might consider moving
+the lru_add_drain_all() call in migrate_device_unmap() to be
+called *after* all pages have migration entries inserted.
+That would eliminate also b) above.
+
+v2:
+- Instead of a cond_resched() in hmm_range_fault(),
+ eliminate the problem by waiting for the folio to be unlocked
+ in do_swap_page() (Alistair Popple, Andrew Morton)
+v3:
+- Add a stub migration_entry_wait_on_locked() for the
+ !CONFIG_MIGRATION case. (Kernel Test Robot)
+v4:
+- Rename migrate_entry_wait_on_locked() to
+ softleaf_entry_wait_on_locked() and update docs (Alistair Popple)
+v5:
+- Add a WARN_ON_ONCE() for the !CONFIG_MIGRATION
+ version of softleaf_entry_wait_on_locked().
+- Modify wording around function names in the commit message
+ (Andrew Morton)
+
+Suggested-by: Alistair Popple <apopple@nvidia.com>
+Fixes: 1afaeb8293c9 ("mm/migrate: Trylock device page in do_swap_page")
+Cc: Ralph Campbell <rcampbell@nvidia.com>
+Cc: Christoph Hellwig <hch@lst.de>
+Cc: Jason Gunthorpe <jgg@mellanox.com>
+Cc: Jason Gunthorpe <jgg@ziepe.ca>
+Cc: Leon Romanovsky <leon@kernel.org>
+Cc: Andrew Morton <akpm@linux-foundation.org>
+Cc: Matthew Brost <matthew.brost@intel.com>
+Cc: John Hubbard <jhubbard@nvidia.com>
+Cc: Alistair Popple <apopple@nvidia.com>
+Cc: linux-mm@kvack.org
+Cc: <dri-devel@lists.freedesktop.org>
+Signed-off-by: Thomas Hellström <thomas.hellstrom@linux.intel.com>
+Cc: <stable@vger.kernel.org> # v6.15+
+Reviewed-by: John Hubbard <jhubbard@nvidia.com> #v3
+Reviewed-by: Alistair Popple <apopple@nvidia.com>
+Link: https://patch.msgid.link/20260210115653.92413-1-thomas.hellstrom@linux.intel.com
+(cherry picked from commit a69d1ab971a624c6f112cea61536569d579c3215)
+Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/migrate.h | 10 +++++++++-
+ mm/filemap.c | 15 ++++++++++-----
+ mm/memory.c | 3 ++-
+ mm/migrate.c | 8 ++++----
+ mm/migrate_device.c | 2 +-
+ 5 files changed, 26 insertions(+), 12 deletions(-)
+
+--- a/include/linux/migrate.h
++++ b/include/linux/migrate.h
+@@ -65,7 +65,7 @@ bool isolate_folio_to_list(struct folio
+
+ int migrate_huge_page_move_mapping(struct address_space *mapping,
+ struct folio *dst, struct folio *src);
+-void migration_entry_wait_on_locked(softleaf_t entry, spinlock_t *ptl)
++void softleaf_entry_wait_on_locked(softleaf_t entry, spinlock_t *ptl)
+ __releases(ptl);
+ void folio_migrate_flags(struct folio *newfolio, struct folio *folio);
+ int folio_migrate_mapping(struct address_space *mapping,
+@@ -97,6 +97,14 @@ static inline int set_movable_ops(const
+ return -ENOSYS;
+ }
+
++static inline void softleaf_entry_wait_on_locked(softleaf_t entry, spinlock_t *ptl)
++ __releases(ptl)
++{
++ WARN_ON_ONCE(1);
++
++ spin_unlock(ptl);
++}
++
+ #endif /* CONFIG_MIGRATION */
+
+ #ifdef CONFIG_NUMA_BALANCING
+--- a/mm/filemap.c
++++ b/mm/filemap.c
+@@ -1379,14 +1379,16 @@ repeat:
+
+ #ifdef CONFIG_MIGRATION
+ /**
+- * migration_entry_wait_on_locked - Wait for a migration entry to be removed
+- * @entry: migration swap entry.
++ * softleaf_entry_wait_on_locked - Wait for a migration entry or
++ * device_private entry to be removed.
++ * @entry: migration or device_private swap entry.
+ * @ptl: already locked ptl. This function will drop the lock.
+ *
+- * Wait for a migration entry referencing the given page to be removed. This is
++ * Wait for a migration entry referencing the given page, or device_private
++ * entry referencing a dvice_private page to be unlocked. This is
+ * equivalent to folio_put_wait_locked(folio, TASK_UNINTERRUPTIBLE) except
+ * this can be called without taking a reference on the page. Instead this
+- * should be called while holding the ptl for the migration entry referencing
++ * should be called while holding the ptl for @entry referencing
+ * the page.
+ *
+ * Returns after unlocking the ptl.
+@@ -1394,7 +1396,7 @@ repeat:
+ * This follows the same logic as folio_wait_bit_common() so see the comments
+ * there.
+ */
+-void migration_entry_wait_on_locked(softleaf_t entry, spinlock_t *ptl)
++void softleaf_entry_wait_on_locked(softleaf_t entry, spinlock_t *ptl)
+ __releases(ptl)
+ {
+ struct wait_page_queue wait_page;
+@@ -1428,6 +1430,9 @@ void migration_entry_wait_on_locked(soft
+ * If a migration entry exists for the page the migration path must hold
+ * a valid reference to the page, and it must take the ptl to remove the
+ * migration entry. So the page is valid until the ptl is dropped.
++ * Similarly any path attempting to drop the last reference to a
++ * device-private page needs to grab the ptl to remove the device-private
++ * entry.
+ */
+ spin_unlock(ptl);
+
+--- a/mm/memory.c
++++ b/mm/memory.c
+@@ -4684,7 +4684,8 @@ vm_fault_t do_swap_page(struct vm_fault
+ unlock_page(vmf->page);
+ put_page(vmf->page);
+ } else {
+- pte_unmap_unlock(vmf->pte, vmf->ptl);
++ pte_unmap(vmf->pte);
++ softleaf_entry_wait_on_locked(entry, vmf->ptl);
+ }
+ } else if (softleaf_is_hwpoison(entry)) {
+ ret = VM_FAULT_HWPOISON;
+--- a/mm/migrate.c
++++ b/mm/migrate.c
+@@ -499,7 +499,7 @@ void migration_entry_wait(struct mm_stru
+ if (!softleaf_is_migration(entry))
+ goto out;
+
+- migration_entry_wait_on_locked(entry, ptl);
++ softleaf_entry_wait_on_locked(entry, ptl);
+ return;
+ out:
+ spin_unlock(ptl);
+@@ -531,10 +531,10 @@ void migration_entry_wait_huge(struct vm
+ * If migration entry existed, safe to release vma lock
+ * here because the pgtable page won't be freed without the
+ * pgtable lock released. See comment right above pgtable
+- * lock release in migration_entry_wait_on_locked().
++ * lock release in softleaf_entry_wait_on_locked().
+ */
+ hugetlb_vma_unlock_read(vma);
+- migration_entry_wait_on_locked(entry, ptl);
++ softleaf_entry_wait_on_locked(entry, ptl);
+ return;
+ }
+
+@@ -552,7 +552,7 @@ void pmd_migration_entry_wait(struct mm_
+ ptl = pmd_lock(mm, pmd);
+ if (!pmd_is_migration_entry(*pmd))
+ goto unlock;
+- migration_entry_wait_on_locked(softleaf_from_pmd(*pmd), ptl);
++ softleaf_entry_wait_on_locked(softleaf_from_pmd(*pmd), ptl);
+ return;
+ unlock:
+ spin_unlock(ptl);
+--- a/mm/migrate_device.c
++++ b/mm/migrate_device.c
+@@ -176,7 +176,7 @@ static int migrate_vma_collect_huge_pmd(
+ }
+
+ if (softleaf_is_migration(entry)) {
+- migration_entry_wait_on_locked(entry, ptl);
++ softleaf_entry_wait_on_locked(entry, ptl);
+ spin_unlock(ptl);
+ return -EAGAIN;
+ }
--- /dev/null
+From e6b899f08066e744f89df16ceb782e06868bd148 Mon Sep 17 00:00:00 2001
+From: Christian Brauner <brauner@kernel.org>
+Date: Thu, 26 Feb 2026 14:50:09 +0100
+Subject: nsfs: tighten permission checks for ns iteration ioctls
+
+From: Christian Brauner <brauner@kernel.org>
+
+commit e6b899f08066e744f89df16ceb782e06868bd148 upstream.
+
+Even privileged services should not necessarily be able to see other
+privileged service's namespaces so they can't leak information to each
+other. Use may_see_all_namespaces() helper that centralizes this policy
+until the nstree adapts.
+
+Link: https://patch.msgid.link/20260226-work-visibility-fixes-v1-1-d2c2853313bd@kernel.org
+Fixes: a1d220d9dafa ("nsfs: iterate through mount namespaces")
+Reviewed-by: Jeff Layton <jlayton@kernel.org>
+Cc: stable@kernel.org # v6.12+
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nsfs.c | 13 +++++++++++++
+ include/linux/ns_common.h | 2 ++
+ kernel/nscommon.c | 6 ++++++
+ 3 files changed, 21 insertions(+)
+
+--- a/fs/nsfs.c
++++ b/fs/nsfs.c
+@@ -186,6 +186,17 @@ static bool nsfs_ioctl_valid(unsigned in
+ return false;
+ }
+
++static bool may_use_nsfs_ioctl(unsigned int cmd)
++{
++ switch (_IOC_NR(cmd)) {
++ case _IOC_NR(NS_MNT_GET_NEXT):
++ fallthrough;
++ case _IOC_NR(NS_MNT_GET_PREV):
++ return may_see_all_namespaces();
++ }
++ return true;
++}
++
+ static long ns_ioctl(struct file *filp, unsigned int ioctl,
+ unsigned long arg)
+ {
+@@ -201,6 +212,8 @@ static long ns_ioctl(struct file *filp,
+
+ if (!nsfs_ioctl_valid(ioctl))
+ return -ENOIOCTLCMD;
++ if (!may_use_nsfs_ioctl(ioctl))
++ return -EPERM;
+
+ ns = get_proc_ns(file_inode(filp));
+ switch (ioctl) {
+--- a/include/linux/ns_common.h
++++ b/include/linux/ns_common.h
+@@ -55,6 +55,8 @@ static __always_inline bool is_ns_init_i
+
+ #define ns_common_free(__ns) __ns_common_free(to_ns_common((__ns)))
+
++bool may_see_all_namespaces(void);
++
+ static __always_inline __must_check int __ns_ref_active_read(const struct ns_common *ns)
+ {
+ return atomic_read(&ns->__ns_ref_active);
+--- a/kernel/nscommon.c
++++ b/kernel/nscommon.c
+@@ -309,3 +309,9 @@ void __ns_ref_active_get(struct ns_commo
+ return;
+ }
+ }
++
++bool may_see_all_namespaces(void)
++{
++ return (task_active_pid_ns(current) == &init_pid_ns) &&
++ ns_capable_noaudit(init_pid_ns.user_ns, CAP_SYS_ADMIN);
++}
--- /dev/null
+From 2d28ed588f8d7d0d41b0a4fad7f0d05e4bbf1797 Mon Sep 17 00:00:00 2001
+From: Axel Rasmussen <axelrasmussen@google.com>
+Date: Tue, 24 Feb 2026 16:24:34 -0800
+Subject: Revert "ptdesc: remove references to folios from __pagetable_ctor() and pagetable_dtor()"
+
+From: Axel Rasmussen <axelrasmussen@google.com>
+
+commit 2d28ed588f8d7d0d41b0a4fad7f0d05e4bbf1797 upstream.
+
+This change swapped out mod_node_page_state for lruvec_stat_add_folio.
+But, these two APIs are not interchangeable: the lruvec version also
+increments memcg stats, in addition to "global" pgdat stats.
+
+So after this change, the "pagetables" memcg stat in memory.stat always
+yields "0", which is a userspace visible regression.
+
+I tried to look for a refactor where we add a variant of
+lruvec_stat_mod_folio which takes a pgdat and a memcg instead of a folio,
+to try to adhere to the spirit of the original patch. But at the end of
+the day this just means we have to call folio_memcg(ptdesc_folio(ptdesc))
+anyway, which doesn't really accomplish much.
+
+This regression is visible in master as well as 6.18 stable, so CC stable
+too.
+
+Link: https://lkml.kernel.org/r/20260225002434.2953895-1-axelrasmussen@google.com
+Fixes: f0c92726e89f ("ptdesc: remove references to folios from __pagetable_ctor() and pagetable_dtor()")
+Signed-off-by: Axel Rasmussen <axelrasmussen@google.com>
+Acked-by: Shakeel Butt <shakeel.butt@linux.dev>
+Acked-by: Johannes Weiner <hannes@cmpxchg.org>
+Reviewed-by: Vishal Moola (Oracle) <vishal.moola@gmail.com>
+Cc: David Hildenbrand <david@kernel.org>
+Cc: Liam Howlett <liam.howlett@oracle.com>
+Cc: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
+Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
+Cc: Michal Hocko <mhocko@suse.com>
+Cc: Mike Rapoport <rppt@kernel.org>
+Cc: Suren Baghdasaryan <surenb@google.com>
+Cc: Vlastimil Babka <vbabka@suse.cz>
+Cc: Roman Gushchin <roman.gushchin@linux.dev>
+Cc: Muchun Song <muchun.song@linux.dev>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/mm.h | 17 ++++++-----------
+ 1 file changed, 6 insertions(+), 11 deletions(-)
+
+--- a/include/linux/mm.h
++++ b/include/linux/mm.h
+@@ -3304,26 +3304,21 @@ static inline bool ptlock_init(struct pt
+ static inline void ptlock_free(struct ptdesc *ptdesc) {}
+ #endif /* defined(CONFIG_SPLIT_PTE_PTLOCKS) */
+
+-static inline unsigned long ptdesc_nr_pages(const struct ptdesc *ptdesc)
+-{
+- return compound_nr(ptdesc_page(ptdesc));
+-}
+-
+ static inline void __pagetable_ctor(struct ptdesc *ptdesc)
+ {
+- pg_data_t *pgdat = NODE_DATA(memdesc_nid(ptdesc->pt_flags));
++ struct folio *folio = ptdesc_folio(ptdesc);
+
+- __SetPageTable(ptdesc_page(ptdesc));
+- mod_node_page_state(pgdat, NR_PAGETABLE, ptdesc_nr_pages(ptdesc));
++ __folio_set_pgtable(folio);
++ lruvec_stat_add_folio(folio, NR_PAGETABLE);
+ }
+
+ static inline void pagetable_dtor(struct ptdesc *ptdesc)
+ {
+- pg_data_t *pgdat = NODE_DATA(memdesc_nid(ptdesc->pt_flags));
++ struct folio *folio = ptdesc_folio(ptdesc);
+
+ ptlock_free(ptdesc);
+- __ClearPageTable(ptdesc_page(ptdesc));
+- mod_node_page_state(pgdat, NR_PAGETABLE, -ptdesc_nr_pages(ptdesc));
++ __folio_clear_pgtable(folio);
++ lruvec_stat_sub_folio(folio, NR_PAGETABLE);
+ }
+
+ static inline void pagetable_dtor_free(struct ptdesc *ptdesc)
--- /dev/null
+From 6b275bfaa16be3fb1689fa6794e445ecd127a1b4 Mon Sep 17 00:00:00 2001
+From: Xu Yang <xu.yang_2@nxp.com>
+Date: Mon, 9 Mar 2026 15:43:12 +0800
+Subject: Revert "tcpm: allow looking for role_sw device in the main node"
+
+From: Xu Yang <xu.yang_2@nxp.com>
+
+commit 6b275bfaa16be3fb1689fa6794e445ecd127a1b4 upstream.
+
+This reverts commit 1366cd228b0c67b60a2c0c26ef37fe9f7cfedb7f.
+
+The fwnode_usb_role_switch_get() returns NULL only if no connection is
+found, returns ERR_PTR(-EPROBE_DEFER) if connection is found but deferred
+probe is needed, or a valid pointer of usb_role_switch.
+
+When switching from a NULL check to IS_ERR_OR_NULL(), usb_role_switch_get()
+returns NULL and overwrites the ERR_PTR(-EPROBE_DEFER) returned by
+fwnode_usb_role_switch_get(). This causes the deferred probe indication to
+be lost, preventing the USB role switch from ever being retrieved.
+
+Fixes: 1366cd228b0c ("tcpm: allow looking for role_sw device in the main node")
+Cc: stable <stable@kernel.org>
+Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
+Tested-by: Arnaud Ferraris <arnaud.ferraris@collabora.com>
+Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Link: https://patch.msgid.link/20260309074313.2809867-2-xu.yang_2@nxp.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/typec/tcpm/tcpm.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/usb/typec/tcpm/tcpm.c
++++ b/drivers/usb/typec/tcpm/tcpm.c
+@@ -7890,7 +7890,7 @@ struct tcpm_port *tcpm_register_port(str
+ port->partner_desc.identity = &port->partner_ident;
+
+ port->role_sw = fwnode_usb_role_switch_get(tcpc->fwnode);
+- if (IS_ERR_OR_NULL(port->role_sw))
++ if (!port->role_sw)
+ port->role_sw = usb_role_switch_get(port->dev);
+ if (IS_ERR(port->role_sw)) {
+ err = PTR_ERR(port->role_sw);
libceph-prevent-potential-out-of-bounds-reads-in-process_message_header.patch
libceph-use-u32-for-non-negative-values-in-ceph_monmap_decode.patch
libceph-admit-message-frames-only-in-ceph_con_s_open-state.patch
+revert-tcpm-allow-looking-for-role_sw-device-in-the-main-node.patch
+revert-ptdesc-remove-references-to-folios-from-__pagetable_ctor-and-pagetable_dtor.patch
+mm-fix-a-hmm_range_fault-livelock-starvation-problem.patch
+nsfs-tighten-permission-checks-for-ns-iteration-ioctls.patch
+liveupdate-luo_file-remember-retrieve-status.patch
+kthread-consolidate-kthread-exit-paths-to-prevent-use-after-free.patch