]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.19-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 17 Mar 2026 11:24:39 +0000 (12:24 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 17 Mar 2026 11:24:39 +0000 (12:24 +0100)
added patches:
kthread-consolidate-kthread-exit-paths-to-prevent-use-after-free.patch
liveupdate-luo_file-remember-retrieve-status.patch
mm-fix-a-hmm_range_fault-livelock-starvation-problem.patch
nsfs-tighten-permission-checks-for-ns-iteration-ioctls.patch
revert-ptdesc-remove-references-to-folios-from-__pagetable_ctor-and-pagetable_dtor.patch
revert-tcpm-allow-looking-for-role_sw-device-in-the-main-node.patch

queue-6.19/kthread-consolidate-kthread-exit-paths-to-prevent-use-after-free.patch [new file with mode: 0644]
queue-6.19/liveupdate-luo_file-remember-retrieve-status.patch [new file with mode: 0644]
queue-6.19/mm-fix-a-hmm_range_fault-livelock-starvation-problem.patch [new file with mode: 0644]
queue-6.19/nsfs-tighten-permission-checks-for-ns-iteration-ioctls.patch [new file with mode: 0644]
queue-6.19/revert-ptdesc-remove-references-to-folios-from-__pagetable_ctor-and-pagetable_dtor.patch [new file with mode: 0644]
queue-6.19/revert-tcpm-allow-looking-for-role_sw-device-in-the-main-node.patch [new file with mode: 0644]
queue-6.19/series

diff --git a/queue-6.19/kthread-consolidate-kthread-exit-paths-to-prevent-use-after-free.patch b/queue-6.19/kthread-consolidate-kthread-exit-paths-to-prevent-use-after-free.patch
new file mode 100644 (file)
index 0000000..374a352
--- /dev/null
@@ -0,0 +1,213 @@
+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;
diff --git a/queue-6.19/liveupdate-luo_file-remember-retrieve-status.patch b/queue-6.19/liveupdate-luo_file-remember-retrieve-status.patch
new file mode 100644 (file)
index 0000000..bc16f4d
--- /dev/null
@@ -0,0 +1,208 @@
+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);
diff --git a/queue-6.19/mm-fix-a-hmm_range_fault-livelock-starvation-problem.patch b/queue-6.19/mm-fix-a-hmm_range_fault-livelock-starvation-problem.patch
new file mode 100644 (file)
index 0000000..e8c6437
--- /dev/null
@@ -0,0 +1,223 @@
+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;
+               }
diff --git a/queue-6.19/nsfs-tighten-permission-checks-for-ns-iteration-ioctls.patch b/queue-6.19/nsfs-tighten-permission-checks-for-ns-iteration-ioctls.patch
new file mode 100644 (file)
index 0000000..12a1ae8
--- /dev/null
@@ -0,0 +1,78 @@
+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);
++}
diff --git a/queue-6.19/revert-ptdesc-remove-references-to-folios-from-__pagetable_ctor-and-pagetable_dtor.patch b/queue-6.19/revert-ptdesc-remove-references-to-folios-from-__pagetable_ctor-and-pagetable_dtor.patch
new file mode 100644 (file)
index 0000000..cf765a3
--- /dev/null
@@ -0,0 +1,83 @@
+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)
diff --git a/queue-6.19/revert-tcpm-allow-looking-for-role_sw-device-in-the-main-node.patch b/queue-6.19/revert-tcpm-allow-looking-for-role_sw-device-in-the-main-node.patch
new file mode 100644 (file)
index 0000000..31cdd94
--- /dev/null
@@ -0,0 +1,42 @@
+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);
index a42cf36b54957bfb46c37d61d60c7a400a26a369..2840705453f6ef861f279aa441c52e87e183a1d7 100644 (file)
@@ -208,3 +208,9 @@ libceph-reject-preamble-if-control-segment-is-empty.patch
 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