--- /dev/null
+From 5eac6abea01da6185af6280909203b6fe7dcece7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 30 Jun 2026 06:06:34 +0000
+Subject: ring-buffer: Remove ring_buffer_read_prepare_sync()
+
+From: Bjoern Doebel <doebel@amazon.de>
+
+[ Upstream commit 119a5d573622ae90ba730d18acfae9bb75d77b9a ]
+
+When the ring buffer was first introduced, reading the non-consuming
+"trace" file required disabling the writing of the ring buffer. To make
+sure the writing was fully disabled before iterating the buffer with a
+non-consuming read, it would set the disable flag of the buffer and then
+call an RCU synchronization to make sure all the buffers were
+synchronized.
+
+The function ring_buffer_read_start() originally would initialize the
+iterator and call an RCU synchronization, but this was for each individual
+per CPU buffer where this would get called many times on a machine with
+many CPUs before the trace file could be read. The commit 72c9ddfd4c5bf
+("ring-buffer: Make non-consuming read less expensive with lots of cpus.")
+separated ring_buffer_read_start into ring_buffer_read_prepare(),
+ring_buffer_read_sync() and then ring_buffer_read_start() to allow each of
+the per CPU buffers to be prepared, call the read_buffer_read_sync() once,
+and then the ring_buffer_read_start() for each of the CPUs which made
+things much faster.
+
+The commit 1039221cc278 ("ring-buffer: Do not disable recording when there
+is an iterator") removed the requirement of disabling the recording of the
+ring buffer in order to iterate it, but it did not remove the
+synchronization that was happening that was required to wait for all the
+buffers to have no more writers. It's now OK for the buffers to have
+writers and no synchronization is needed.
+
+Remove the synchronization and put back the interface for the ring buffer
+iterator back before commit 72c9ddfd4c5bf was applied.
+
+Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+Link: https://lore.kernel.org/20250630180440.3eabb514@batman.local.home
+Reported-by: David Howells <dhowells@redhat.com>
+Fixes: 1039221cc278 ("ring-buffer: Do not disable recording when there is an iterator")
+Tested-by: David Howells <dhowells@redhat.com>
+Reviewed-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
+
+Assisted-by: Kiro:claude-opus-4.8
+[doebel@amazon.de: move patch section using guard() macro into a
+separate block to address declaration after statement warning.]
+Signed-off-by: Bjoern Doebel <doebel@amazon.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/linux/ring_buffer.h | 4 +--
+ kernel/trace/ring_buffer.c | 72 ++++++++-----------------------------
+ kernel/trace/trace.c | 14 +++-----
+ kernel/trace/trace_kdb.c | 8 ++---
+ 4 files changed, 22 insertions(+), 76 deletions(-)
+
+diff --git a/include/linux/ring_buffer.h b/include/linux/ring_buffer.h
+index 7d5a78f49d43dd..be5c1209224695 100644
+--- a/include/linux/ring_buffer.h
++++ b/include/linux/ring_buffer.h
+@@ -128,9 +128,7 @@ ring_buffer_consume(struct trace_buffer *buffer, int cpu, u64 *ts,
+ unsigned long *lost_events);
+
+ struct ring_buffer_iter *
+-ring_buffer_read_prepare(struct trace_buffer *buffer, int cpu, gfp_t flags);
+-void ring_buffer_read_prepare_sync(void);
+-void ring_buffer_read_start(struct ring_buffer_iter *iter);
++ring_buffer_read_start(struct trace_buffer *buffer, int cpu, gfp_t flags);
+ void ring_buffer_read_finish(struct ring_buffer_iter *iter);
+
+ struct ring_buffer_event *
+diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
+index 03a7127efc5a8a..1089daa17b09e2 100644
+--- a/kernel/trace/ring_buffer.c
++++ b/kernel/trace/ring_buffer.c
+@@ -4858,28 +4858,20 @@ ring_buffer_consume(struct trace_buffer *buffer, int cpu, u64 *ts,
+ EXPORT_SYMBOL_GPL(ring_buffer_consume);
+
+ /**
+- * ring_buffer_read_prepare - Prepare for a non consuming read of the buffer
++ * ring_buffer_read_start - start a non consuming read of the buffer
+ * @buffer: The ring buffer to read from
+ * @cpu: The cpu buffer to iterate over
+ * @flags: gfp flags to use for memory allocation
+ *
+- * This performs the initial preparations necessary to iterate
+- * through the buffer. Memory is allocated, buffer recording
+- * is disabled, and the iterator pointer is returned to the caller.
+- *
+- * Disabling buffer recording prevents the reading from being
+- * corrupted. This is not a consuming read, so a producer is not
+- * expected.
+- *
+- * After a sequence of ring_buffer_read_prepare calls, the user is
+- * expected to make at least one call to ring_buffer_read_prepare_sync.
+- * Afterwards, ring_buffer_read_start is invoked to get things going
+- * for real.
++ * This creates an iterator to allow non-consuming iteration through
++ * the buffer. If the buffer is disabled for writing, it will produce
++ * the same information each time, but if the buffer is still writing
++ * then the first hit of a write will cause the iteration to stop.
+ *
+- * This overall must be paired with ring_buffer_read_finish.
++ * Must be paired with ring_buffer_read_finish.
+ */
+ struct ring_buffer_iter *
+-ring_buffer_read_prepare(struct trace_buffer *buffer, int cpu, gfp_t flags)
++ring_buffer_read_start(struct trace_buffer *buffer, int cpu, gfp_t flags)
+ {
+ struct ring_buffer_per_cpu *cpu_buffer;
+ struct ring_buffer_iter *iter;
+@@ -4904,51 +4896,15 @@ ring_buffer_read_prepare(struct trace_buffer *buffer, int cpu, gfp_t flags)
+
+ atomic_inc(&cpu_buffer->resize_disabled);
+
+- return iter;
+-}
+-EXPORT_SYMBOL_GPL(ring_buffer_read_prepare);
++ {
++ guard(raw_spinlock_irqsave)(&cpu_buffer->reader_lock);
+
+-/**
+- * ring_buffer_read_prepare_sync - Synchronize a set of prepare calls
+- *
+- * All previously invoked ring_buffer_read_prepare calls to prepare
+- * iterators will be synchronized. Afterwards, read_buffer_read_start
+- * calls on those iterators are allowed.
+- */
+-void
+-ring_buffer_read_prepare_sync(void)
+-{
+- synchronize_rcu();
+-}
+-EXPORT_SYMBOL_GPL(ring_buffer_read_prepare_sync);
+-
+-/**
+- * ring_buffer_read_start - start a non consuming read of the buffer
+- * @iter: The iterator returned by ring_buffer_read_prepare
+- *
+- * This finalizes the startup of an iteration through the buffer.
+- * The iterator comes from a call to ring_buffer_read_prepare and
+- * an intervening ring_buffer_read_prepare_sync must have been
+- * performed.
+- *
+- * Must be paired with ring_buffer_read_finish.
+- */
+-void
+-ring_buffer_read_start(struct ring_buffer_iter *iter)
+-{
+- struct ring_buffer_per_cpu *cpu_buffer;
+- unsigned long flags;
+-
+- if (!iter)
+- return;
+-
+- cpu_buffer = iter->cpu_buffer;
++ arch_spin_lock(&cpu_buffer->lock);
++ rb_iter_reset(iter);
++ arch_spin_unlock(&cpu_buffer->lock);
++ }
+
+- raw_spin_lock_irqsave(&cpu_buffer->reader_lock, flags);
+- arch_spin_lock(&cpu_buffer->lock);
+- rb_iter_reset(iter);
+- arch_spin_unlock(&cpu_buffer->lock);
+- raw_spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags);
++ return iter;
+ }
+ EXPORT_SYMBOL_GPL(ring_buffer_read_start);
+
+diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
+index 5bcd4cbeeb4fe1..ed32d3c4f0e76b 100644
+--- a/kernel/trace/trace.c
++++ b/kernel/trace/trace.c
+@@ -4480,21 +4480,15 @@ __tracing_open(struct inode *inode, struct file *file, bool snapshot)
+ if (iter->cpu_file == RING_BUFFER_ALL_CPUS) {
+ for_each_tracing_cpu(cpu) {
+ iter->buffer_iter[cpu] =
+- ring_buffer_read_prepare(iter->array_buffer->buffer,
+- cpu, GFP_KERNEL);
+- }
+- ring_buffer_read_prepare_sync();
+- for_each_tracing_cpu(cpu) {
+- ring_buffer_read_start(iter->buffer_iter[cpu]);
++ ring_buffer_read_start(iter->array_buffer->buffer,
++ cpu, GFP_KERNEL);
+ tracing_iter_reset(iter, cpu);
+ }
+ } else {
+ cpu = iter->cpu_file;
+ iter->buffer_iter[cpu] =
+- ring_buffer_read_prepare(iter->array_buffer->buffer,
+- cpu, GFP_KERNEL);
+- ring_buffer_read_prepare_sync();
+- ring_buffer_read_start(iter->buffer_iter[cpu]);
++ ring_buffer_read_start(iter->array_buffer->buffer,
++ cpu, GFP_KERNEL);
+ tracing_iter_reset(iter, cpu);
+ }
+
+diff --git a/kernel/trace/trace_kdb.c b/kernel/trace/trace_kdb.c
+index 9da76104f7a28b..18d1551db2b0d5 100644
+--- a/kernel/trace/trace_kdb.c
++++ b/kernel/trace/trace_kdb.c
+@@ -43,17 +43,15 @@ static void ftrace_dump_buf(int skip_entries, long cpu_file)
+ if (cpu_file == RING_BUFFER_ALL_CPUS) {
+ for_each_tracing_cpu(cpu) {
+ iter.buffer_iter[cpu] =
+- ring_buffer_read_prepare(iter.array_buffer->buffer,
+- cpu, GFP_ATOMIC);
+- ring_buffer_read_start(iter.buffer_iter[cpu]);
++ ring_buffer_read_start(iter.array_buffer->buffer,
++ cpu, GFP_ATOMIC);
+ tracing_iter_reset(&iter, cpu);
+ }
+ } else {
+ iter.cpu_file = cpu_file;
+ iter.buffer_iter[cpu_file] =
+- ring_buffer_read_prepare(iter.array_buffer->buffer,
++ ring_buffer_read_start(iter.array_buffer->buffer,
+ cpu_file, GFP_ATOMIC);
+- ring_buffer_read_start(iter.buffer_iter[cpu_file]);
+ tracing_iter_reset(&iter, cpu_file);
+ }
+
+--
+2.53.0
+
batman-adv-dat-prevent-false-sharing-between-vlans.patch
batman-adv-tvlv-enforce-2-byte-alignment.patch
batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch
+ring-buffer-remove-ring_buffer_read_prepare_sync.patch
--- /dev/null
+From e1a071f4798bf2dc9569a55421b67b6a3f163a35 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 30 Jun 2026 06:03:21 +0000
+Subject: ring-buffer: Remove ring_buffer_read_prepare_sync()
+
+From: Bjoern Doebel <doebel@amazon.de>
+
+[ Upstream commit 119a5d573622ae90ba730d18acfae9bb75d77b9a ]
+
+When the ring buffer was first introduced, reading the non-consuming
+"trace" file required disabling the writing of the ring buffer. To make
+sure the writing was fully disabled before iterating the buffer with a
+non-consuming read, it would set the disable flag of the buffer and then
+call an RCU synchronization to make sure all the buffers were
+synchronized.
+
+The function ring_buffer_read_start() originally would initialize the
+iterator and call an RCU synchronization, but this was for each individual
+per CPU buffer where this would get called many times on a machine with
+many CPUs before the trace file could be read. The commit 72c9ddfd4c5bf
+("ring-buffer: Make non-consuming read less expensive with lots of cpus.")
+separated ring_buffer_read_start into ring_buffer_read_prepare(),
+ring_buffer_read_sync() and then ring_buffer_read_start() to allow each of
+the per CPU buffers to be prepared, call the read_buffer_read_sync() once,
+and then the ring_buffer_read_start() for each of the CPUs which made
+things much faster.
+
+The commit 1039221cc278 ("ring-buffer: Do not disable recording when there
+is an iterator") removed the requirement of disabling the recording of the
+ring buffer in order to iterate it, but it did not remove the
+synchronization that was happening that was required to wait for all the
+buffers to have no more writers. It's now OK for the buffers to have
+writers and no synchronization is needed.
+
+Remove the synchronization and put back the interface for the ring buffer
+iterator back before commit 72c9ddfd4c5bf was applied.
+
+Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+Link: https://lore.kernel.org/20250630180440.3eabb514@batman.local.home
+Reported-by: David Howells <dhowells@redhat.com>
+Fixes: 1039221cc278 ("ring-buffer: Do not disable recording when there is an iterator")
+Tested-by: David Howells <dhowells@redhat.com>
+Reviewed-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
+
+Assisted-by: Kiro:claude-opus-4.8
+[doebel@amazon.de: move patch section using guard() macro into a
+separate block to address declaration after statement warning.]
+Signed-off-by: Bjoern Doebel <doebel@amazon.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/linux/ring_buffer.h | 4 +--
+ kernel/trace/ring_buffer.c | 72 ++++++++-----------------------------
+ kernel/trace/trace.c | 14 +++-----
+ kernel/trace/trace_kdb.c | 8 ++---
+ 4 files changed, 22 insertions(+), 76 deletions(-)
+
+diff --git a/include/linux/ring_buffer.h b/include/linux/ring_buffer.h
+index 3e7bfc0f65aee2..b53335ed2d0efc 100644
+--- a/include/linux/ring_buffer.h
++++ b/include/linux/ring_buffer.h
+@@ -130,9 +130,7 @@ ring_buffer_consume(struct trace_buffer *buffer, int cpu, u64 *ts,
+ unsigned long *lost_events);
+
+ struct ring_buffer_iter *
+-ring_buffer_read_prepare(struct trace_buffer *buffer, int cpu, gfp_t flags);
+-void ring_buffer_read_prepare_sync(void);
+-void ring_buffer_read_start(struct ring_buffer_iter *iter);
++ring_buffer_read_start(struct trace_buffer *buffer, int cpu, gfp_t flags);
+ void ring_buffer_read_finish(struct ring_buffer_iter *iter);
+
+ struct ring_buffer_event *
+diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
+index e44115db0efe35..e7c472729542d9 100644
+--- a/kernel/trace/ring_buffer.c
++++ b/kernel/trace/ring_buffer.c
+@@ -5037,28 +5037,20 @@ ring_buffer_consume(struct trace_buffer *buffer, int cpu, u64 *ts,
+ EXPORT_SYMBOL_GPL(ring_buffer_consume);
+
+ /**
+- * ring_buffer_read_prepare - Prepare for a non consuming read of the buffer
++ * ring_buffer_read_start - start a non consuming read of the buffer
+ * @buffer: The ring buffer to read from
+ * @cpu: The cpu buffer to iterate over
+ * @flags: gfp flags to use for memory allocation
+ *
+- * This performs the initial preparations necessary to iterate
+- * through the buffer. Memory is allocated, buffer recording
+- * is disabled, and the iterator pointer is returned to the caller.
+- *
+- * Disabling buffer recording prevents the reading from being
+- * corrupted. This is not a consuming read, so a producer is not
+- * expected.
+- *
+- * After a sequence of ring_buffer_read_prepare calls, the user is
+- * expected to make at least one call to ring_buffer_read_prepare_sync.
+- * Afterwards, ring_buffer_read_start is invoked to get things going
+- * for real.
++ * This creates an iterator to allow non-consuming iteration through
++ * the buffer. If the buffer is disabled for writing, it will produce
++ * the same information each time, but if the buffer is still writing
++ * then the first hit of a write will cause the iteration to stop.
+ *
+- * This overall must be paired with ring_buffer_read_finish.
++ * Must be paired with ring_buffer_read_finish.
+ */
+ struct ring_buffer_iter *
+-ring_buffer_read_prepare(struct trace_buffer *buffer, int cpu, gfp_t flags)
++ring_buffer_read_start(struct trace_buffer *buffer, int cpu, gfp_t flags)
+ {
+ struct ring_buffer_per_cpu *cpu_buffer;
+ struct ring_buffer_iter *iter;
+@@ -5083,51 +5075,15 @@ ring_buffer_read_prepare(struct trace_buffer *buffer, int cpu, gfp_t flags)
+
+ atomic_inc(&cpu_buffer->resize_disabled);
+
+- return iter;
+-}
+-EXPORT_SYMBOL_GPL(ring_buffer_read_prepare);
++ {
++ guard(raw_spinlock_irqsave)(&cpu_buffer->reader_lock);
+
+-/**
+- * ring_buffer_read_prepare_sync - Synchronize a set of prepare calls
+- *
+- * All previously invoked ring_buffer_read_prepare calls to prepare
+- * iterators will be synchronized. Afterwards, read_buffer_read_start
+- * calls on those iterators are allowed.
+- */
+-void
+-ring_buffer_read_prepare_sync(void)
+-{
+- synchronize_rcu();
+-}
+-EXPORT_SYMBOL_GPL(ring_buffer_read_prepare_sync);
+-
+-/**
+- * ring_buffer_read_start - start a non consuming read of the buffer
+- * @iter: The iterator returned by ring_buffer_read_prepare
+- *
+- * This finalizes the startup of an iteration through the buffer.
+- * The iterator comes from a call to ring_buffer_read_prepare and
+- * an intervening ring_buffer_read_prepare_sync must have been
+- * performed.
+- *
+- * Must be paired with ring_buffer_read_finish.
+- */
+-void
+-ring_buffer_read_start(struct ring_buffer_iter *iter)
+-{
+- struct ring_buffer_per_cpu *cpu_buffer;
+- unsigned long flags;
+-
+- if (!iter)
+- return;
+-
+- cpu_buffer = iter->cpu_buffer;
++ arch_spin_lock(&cpu_buffer->lock);
++ rb_iter_reset(iter);
++ arch_spin_unlock(&cpu_buffer->lock);
++ }
+
+- raw_spin_lock_irqsave(&cpu_buffer->reader_lock, flags);
+- arch_spin_lock(&cpu_buffer->lock);
+- rb_iter_reset(iter);
+- arch_spin_unlock(&cpu_buffer->lock);
+- raw_spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags);
++ return iter;
+ }
+ EXPORT_SYMBOL_GPL(ring_buffer_read_start);
+
+diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
+index 537360be8e4e0d..1a29a9d9e86853 100644
+--- a/kernel/trace/trace.c
++++ b/kernel/trace/trace.c
+@@ -4803,21 +4803,15 @@ __tracing_open(struct inode *inode, struct file *file, bool snapshot)
+ if (iter->cpu_file == RING_BUFFER_ALL_CPUS) {
+ for_each_tracing_cpu(cpu) {
+ iter->buffer_iter[cpu] =
+- ring_buffer_read_prepare(iter->array_buffer->buffer,
+- cpu, GFP_KERNEL);
+- }
+- ring_buffer_read_prepare_sync();
+- for_each_tracing_cpu(cpu) {
+- ring_buffer_read_start(iter->buffer_iter[cpu]);
++ ring_buffer_read_start(iter->array_buffer->buffer,
++ cpu, GFP_KERNEL);
+ tracing_iter_reset(iter, cpu);
+ }
+ } else {
+ cpu = iter->cpu_file;
+ iter->buffer_iter[cpu] =
+- ring_buffer_read_prepare(iter->array_buffer->buffer,
+- cpu, GFP_KERNEL);
+- ring_buffer_read_prepare_sync();
+- ring_buffer_read_start(iter->buffer_iter[cpu]);
++ ring_buffer_read_start(iter->array_buffer->buffer,
++ cpu, GFP_KERNEL);
+ tracing_iter_reset(iter, cpu);
+ }
+
+diff --git a/kernel/trace/trace_kdb.c b/kernel/trace/trace_kdb.c
+index 59857a1ee44cdf..628c25693cef2f 100644
+--- a/kernel/trace/trace_kdb.c
++++ b/kernel/trace/trace_kdb.c
+@@ -43,17 +43,15 @@ static void ftrace_dump_buf(int skip_entries, long cpu_file)
+ if (cpu_file == RING_BUFFER_ALL_CPUS) {
+ for_each_tracing_cpu(cpu) {
+ iter.buffer_iter[cpu] =
+- ring_buffer_read_prepare(iter.array_buffer->buffer,
+- cpu, GFP_ATOMIC);
+- ring_buffer_read_start(iter.buffer_iter[cpu]);
++ ring_buffer_read_start(iter.array_buffer->buffer,
++ cpu, GFP_ATOMIC);
+ tracing_iter_reset(&iter, cpu);
+ }
+ } else {
+ iter.cpu_file = cpu_file;
+ iter.buffer_iter[cpu_file] =
+- ring_buffer_read_prepare(iter.array_buffer->buffer,
++ ring_buffer_read_start(iter.array_buffer->buffer,
+ cpu_file, GFP_ATOMIC);
+- ring_buffer_read_start(iter.buffer_iter[cpu_file]);
+ tracing_iter_reset(&iter, cpu_file);
+ }
+
+--
+2.53.0
+
batman-adv-dat-prevent-false-sharing-between-vlans.patch
batman-adv-tvlv-enforce-2-byte-alignment.patch
batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch
+ring-buffer-remove-ring_buffer_read_prepare_sync.patch
--- /dev/null
+From ee4e158f36acd6c50b8863200f233cc26e868503 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 29 Jun 2026 15:03:36 +0800
+Subject: fs: constify file ptr in backing_file accessor helpers
+
+From: Amir Goldstein <amir73il@gmail.com>
+
+[ Upstream commit 4e301d858af17ae2ce56886296e5458c5a08219a ]
+
+Add internal helper backing_file_set_user_path() for the only
+two cases that need to modify backing_file fields.
+
+Signed-off-by: Amir Goldstein <amir73il@gmail.com>
+Link: https://lore.kernel.org/20250607115304.2521155-2-amir73il@gmail.com
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/backing-file.c | 4 ++--
+ fs/file_table.c | 13 ++++++++-----
+ fs/internal.h | 1 +
+ include/linux/fs.h | 6 +++---
+ 4 files changed, 14 insertions(+), 10 deletions(-)
+
+diff --git a/fs/backing-file.c b/fs/backing-file.c
+index 09a9be945d45e6..892361c31c3de9 100644
+--- a/fs/backing-file.c
++++ b/fs/backing-file.c
+@@ -41,7 +41,7 @@ struct file *backing_file_open(const struct path *user_path, int flags,
+ return f;
+
+ path_get(user_path);
+- *backing_file_user_path(f) = *user_path;
++ backing_file_set_user_path(f, user_path);
+ error = vfs_open(real_path, f);
+ if (error) {
+ fput(f);
+@@ -65,7 +65,7 @@ struct file *backing_tmpfile_open(const struct path *user_path, int flags,
+ return f;
+
+ path_get(user_path);
+- *backing_file_user_path(f) = *user_path;
++ backing_file_set_user_path(f, user_path);
+ error = vfs_tmpfile(real_idmap, real_parentpath, f, mode);
+ if (error) {
+ fput(f);
+diff --git a/fs/file_table.c b/fs/file_table.c
+index 2a08bc93b0b9c1..75a1908d51a9f5 100644
+--- a/fs/file_table.c
++++ b/fs/file_table.c
+@@ -48,17 +48,20 @@ struct backing_file {
+ struct path user_path;
+ };
+
+-static inline struct backing_file *backing_file(struct file *f)
+-{
+- return container_of(f, struct backing_file, file);
+-}
++#define backing_file(f) container_of(f, struct backing_file, file)
+
+-struct path *backing_file_user_path(struct file *f)
++struct path *backing_file_user_path(const struct file *f)
+ {
+ return &backing_file(f)->user_path;
+ }
+ EXPORT_SYMBOL_GPL(backing_file_user_path);
+
++void backing_file_set_user_path(struct file *f, const struct path *path)
++{
++ backing_file(f)->user_path = *path;
++}
++EXPORT_SYMBOL_GPL(backing_file_set_user_path);
++
+ static inline void backing_file_free(struct backing_file *ff)
+ {
+ path_put(&ff->user_path);
+diff --git a/fs/internal.h b/fs/internal.h
+index 8c1b7acbbe8faa..a4352d333c617a 100644
+--- a/fs/internal.h
++++ b/fs/internal.h
+@@ -100,6 +100,7 @@ extern void chroot_fs_refs(const struct path *, const struct path *);
+ struct file *alloc_empty_file(int flags, const struct cred *cred);
+ struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred);
+ struct file *alloc_empty_backing_file(int flags, const struct cred *cred);
++void backing_file_set_user_path(struct file *f, const struct path *path);
+
+ static inline void file_put_write_access(struct file *file)
+ {
+diff --git a/include/linux/fs.h b/include/linux/fs.h
+index 87720e1b54192c..70bbc00a2bd250 100644
+--- a/include/linux/fs.h
++++ b/include/linux/fs.h
+@@ -2738,7 +2738,7 @@ struct file *dentry_open(const struct path *path, int flags,
+ const struct cred *creds);
+ struct file *dentry_create(const struct path *path, int flags, umode_t mode,
+ const struct cred *cred);
+-struct path *backing_file_user_path(struct file *f);
++struct path *backing_file_user_path(const struct file *f);
+
+ /*
+ * When mmapping a file on a stackable filesystem (e.g., overlayfs), the file
+@@ -2750,14 +2750,14 @@ struct path *backing_file_user_path(struct file *f);
+ * by fstat() on that same fd.
+ */
+ /* Get the path to display in /proc/<pid>/maps */
+-static inline const struct path *file_user_path(struct file *f)
++static inline const struct path *file_user_path(const struct file *f)
+ {
+ if (unlikely(f->f_mode & FMODE_BACKING))
+ return backing_file_user_path(f);
+ return &f->f_path;
+ }
+ /* Get the inode whose inode number to display in /proc/<pid>/maps */
+-static inline const struct inode *file_user_inode(struct file *f)
++static inline const struct inode *file_user_inode(const struct file *f)
+ {
+ if (unlikely(f->f_mode & FMODE_BACKING))
+ return d_inode(backing_file_user_path(f)->dentry);
+--
+2.53.0
+
--- /dev/null
+From 60aebc08cbb02e6a3ec4d23a1428cd9b56ba7b01 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 3 Dec 2024 17:36:17 +0000
+Subject: inet: add indirect call wrapper for getfrag() calls
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit 5204ccbfa22358f95afd031a3f337e6d9a74baea ]
+
+UDP send path suffers from one indirect call to ip_generic_getfrag()
+
+We can use INDIRECT_CALL_1() to avoid it.
+
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Reviewed-by: Willem de Bruijn <willemb@google.com>
+Reviewed-by: Brian Vazquez <brianvv@google.com>
+Link: https://patch.msgid.link/20241203173617.2595451-1-edumazet@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: eca856950f7c ("ipv4: account for fraggap on the paged allocation path")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv4/ip_output.c | 13 +++++++++----
+ net/ipv6/ip6_output.c | 13 ++++++++-----
+ 2 files changed, 17 insertions(+), 9 deletions(-)
+
+diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
+index ba51fc42531c26..0d0d6ebdd3a849 100644
+--- a/net/ipv4/ip_output.c
++++ b/net/ipv4/ip_output.c
+@@ -1169,7 +1169,10 @@ static int __ip_append_data(struct sock *sk,
+ /* [!] NOTE: copy will be negative if pagedlen>0
+ * because then the equation reduces to -fraggap.
+ */
+- if (copy > 0 && getfrag(from, data + transhdrlen, offset, copy, fraggap, skb) < 0) {
++ if (copy > 0 &&
++ INDIRECT_CALL_1(getfrag, ip_generic_getfrag,
++ from, data + transhdrlen, offset,
++ copy, fraggap, skb) < 0) {
+ err = -EFAULT;
+ kfree_skb(skb);
+ goto error;
+@@ -1213,8 +1216,9 @@ static int __ip_append_data(struct sock *sk,
+ unsigned int off;
+
+ off = skb->len;
+- if (getfrag(from, skb_put(skb, copy),
+- offset, copy, off, skb) < 0) {
++ if (INDIRECT_CALL_1(getfrag, ip_generic_getfrag,
++ from, skb_put(skb, copy),
++ offset, copy, off, skb) < 0) {
+ __skb_trim(skb, off);
+ err = -EFAULT;
+ goto error;
+@@ -1254,7 +1258,8 @@ static int __ip_append_data(struct sock *sk,
+ get_page(pfrag->page);
+ }
+ copy = min_t(int, copy, pfrag->size - pfrag->offset);
+- if (getfrag(from,
++ if (INDIRECT_CALL_1(getfrag, ip_generic_getfrag,
++ from,
+ page_address(pfrag->page) + pfrag->offset,
+ offset, copy, skb->len, skb) < 0)
+ goto error_efault;
+diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
+index 3804ead05a35ae..50b41be5d38d0e 100644
+--- a/net/ipv6/ip6_output.c
++++ b/net/ipv6/ip6_output.c
+@@ -1695,8 +1695,9 @@ static int __ip6_append_data(struct sock *sk,
+ pskb_trim_unique(skb_prev, maxfraglen);
+ }
+ if (copy > 0 &&
+- getfrag(from, data + transhdrlen, offset,
+- copy, fraggap, skb) < 0) {
++ INDIRECT_CALL_1(getfrag, ip_generic_getfrag,
++ from, data + transhdrlen, offset,
++ copy, fraggap, skb) < 0) {
+ err = -EFAULT;
+ kfree_skb(skb);
+ goto error;
+@@ -1740,8 +1741,9 @@ static int __ip6_append_data(struct sock *sk,
+ unsigned int off;
+
+ off = skb->len;
+- if (getfrag(from, skb_put(skb, copy),
+- offset, copy, off, skb) < 0) {
++ if (INDIRECT_CALL_1(getfrag, ip_generic_getfrag,
++ from, skb_put(skb, copy),
++ offset, copy, off, skb) < 0) {
+ __skb_trim(skb, off);
+ err = -EFAULT;
+ goto error;
+@@ -1781,7 +1783,8 @@ static int __ip6_append_data(struct sock *sk,
+ get_page(pfrag->page);
+ }
+ copy = min_t(int, copy, pfrag->size - pfrag->offset);
+- if (getfrag(from,
++ if (INDIRECT_CALL_1(getfrag, ip_generic_getfrag,
++ from,
+ page_address(pfrag->page) + pfrag->offset,
+ offset, copy, skb->len, skb) < 0)
+ goto error_efault;
+--
+2.53.0
+
--- /dev/null
+From 692c70e7ed2b528cf703ce2f6263caa9380fc03a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 16 Jun 2026 22:38:29 +0900
+Subject: ipv4: account for fraggap on the paged allocation path
+
+From: Wongi Lee <qw3rtyp0@gmail.com>
+
+[ Upstream commit eca856950f7cb1a221e02b99d758409f2c5cec42 ]
+
+In __ip_append_data(), when the paged-allocation branch is taken,
+alloclen and pagedlen are computed as
+
+ alloclen = fragheaderlen + transhdrlen;
+ pagedlen = datalen - transhdrlen;
+
+datalen already includes fraggap, but the fraggap bytes carried over
+from the previous skb are copied into the new skb's linear area at
+offset transhdrlen by the subsequent skb_copy_and_csum_bits(). The
+linear area is therefore undersized by fraggap bytes while pagedlen is
+overstated by the same amount.
+
+The non-paged branch sets alloclen to fraglen, which already accounts
+for fraggap because datalen does. Bring the paged branch in line by
+adding fraggap to alloclen and subtracting it from pagedlen.
+
+After this adjustment, copy no longer collapses to -fraggap on the
+paged path, so remove the stale comment describing that old arithmetic.
+
+Fixes: 8eb77cc73977 ("ipv4: avoid partial copy for zc")
+Signed-off-by: Jungwoo Lee <jwlee2217@gmail.com>
+Signed-off-by: Wongi Lee <qw3rtyp0@gmail.com>
+Reviewed-by: Ido Schimmel <idosch@nvidia.com>
+Link: https://patch.msgid.link/ajFR1eLAIs42TN3g@DESKTOP-19IMU7U.localdomain
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv4/ip_output.c | 7 ++-----
+ 1 file changed, 2 insertions(+), 5 deletions(-)
+
+diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
+index 0d0d6ebdd3a849..41031c5c430334 100644
+--- a/net/ipv4/ip_output.c
++++ b/net/ipv4/ip_output.c
+@@ -1118,8 +1118,8 @@ static int __ip_append_data(struct sock *sk,
+ !(rt->dst.dev->features & NETIF_F_SG)))
+ alloclen = fraglen;
+ else {
+- alloclen = fragheaderlen + transhdrlen;
+- pagedlen = datalen - transhdrlen;
++ alloclen = fragheaderlen + transhdrlen + fraggap;
++ pagedlen = datalen - transhdrlen - fraggap;
+ }
+
+ alloclen += alloc_extra;
+@@ -1166,9 +1166,6 @@ static int __ip_append_data(struct sock *sk,
+ }
+
+ copy = datalen - transhdrlen - fraggap - pagedlen;
+- /* [!] NOTE: copy will be negative if pagedlen>0
+- * because then the equation reduces to -fraggap.
+- */
+ if (copy > 0 &&
+ INDIRECT_CALL_1(getfrag, ip_generic_getfrag,
+ from, data + transhdrlen, offset,
+--
+2.53.0
+
--- /dev/null
+From e76e97ac1e9aca9725530e959355309c863cf334 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 29 Jun 2026 15:03:37 +0800
+Subject: lsm: add backing_file LSM hooks
+
+From: Paul Moore <paul@paul-moore.com>
+
+[ Upstream commit 6af36aeb147a06dea47c49859cd6ca5659aeb987 ]
+
+Stacked filesystems such as overlayfs do not currently provide the
+necessary mechanisms for LSMs to properly enforce access controls on the
+mmap() and mprotect() operations. In order to resolve this gap, a LSM
+security blob is being added to the backing_file struct and the following
+new LSM hooks are being created:
+
+ security_backing_file_alloc()
+ security_backing_file_free()
+ security_mmap_backing_file()
+
+The first two hooks are to manage the lifecycle of the LSM security blob
+in the backing_file struct, while the third provides a new mmap() access
+control point for the underlying backing file. It is also expected that
+LSMs will likely want to update their security_file_mprotect() callback
+to address issues with their mprotect() controls, but that does not
+require a change to the security_file_mprotect() LSM hook.
+
+There are a three other small changes to support these new LSM hooks:
+* Pass the user file associated with a backing file down to
+alloc_empty_backing_file() so it can be included in the
+security_backing_file_alloc() hook.
+* Add getter and setter functions for the backing_file struct LSM blob
+as the backing_file struct remains private to fs/file_table.c.
+* Constify the file struct field in the LSM common_audit_data struct to
+better support LSMs that need to pass a const file struct pointer into
+the common LSM audit code.
+
+Thanks to Arnd Bergmann for identifying the missing EXPORT_SYMBOL_GPL()
+and supplying a fixup.
+
+Cc: stable@vger.kernel.org
+Cc: linux-fsdevel@vger.kernel.org
+Cc: linux-unionfs@vger.kernel.org
+Cc: linux-erofs@lists.ozlabs.org
+Reviewed-by: Amir Goldstein <amir73il@gmail.com>
+Reviewed-by: Serge Hallyn <serge@hallyn.com>
+Reviewed-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Paul Moore <paul@paul-moore.com>
+[ Mainline declares lsm_backing_file_cache in security/lsm.h. Linux 6.12.y
+does not have security/lsm_init.c or security/lsm.h; the cache variable
+is defined locally as static struct kmem_cache *lsm_backing_file_cache in
+security/security.c. ]
+Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/backing-file.c | 18 ++++--
+ fs/file_table.c | 27 +++++++--
+ fs/fuse/passthrough.c | 2 +-
+ fs/internal.h | 3 +-
+ fs/overlayfs/dir.c | 2 +-
+ fs/overlayfs/file.c | 3 +-
+ include/linux/backing-file.h | 4 +-
+ include/linux/fs.h | 13 ++++
+ include/linux/lsm_audit.h | 2 +-
+ include/linux/lsm_hook_defs.h | 5 ++
+ include/linux/lsm_hooks.h | 1 +
+ include/linux/security.h | 22 +++++++
+ security/security.c | 109 ++++++++++++++++++++++++++++++++++
+ 13 files changed, 195 insertions(+), 16 deletions(-)
+
+diff --git a/fs/backing-file.c b/fs/backing-file.c
+index 892361c31c3de9..53690754810f1d 100644
+--- a/fs/backing-file.c
++++ b/fs/backing-file.c
+@@ -12,6 +12,7 @@
+ #include <linux/backing-file.h>
+ #include <linux/splice.h>
+ #include <linux/mm.h>
++#include <linux/security.h>
+
+ #include "internal.h"
+
+@@ -29,14 +30,15 @@
+ * returned file into a container structure that also stores the stacked
+ * file's path, which can be retrieved using backing_file_user_path().
+ */
+-struct file *backing_file_open(const struct path *user_path, int flags,
++struct file *backing_file_open(const struct file *user_file, int flags,
+ const struct path *real_path,
+ const struct cred *cred)
+ {
++ const struct path *user_path = &user_file->f_path;
+ struct file *f;
+ int error;
+
+- f = alloc_empty_backing_file(flags, cred);
++ f = alloc_empty_backing_file(flags, cred, user_file);
+ if (IS_ERR(f))
+ return f;
+
+@@ -52,15 +54,16 @@ struct file *backing_file_open(const struct path *user_path, int flags,
+ }
+ EXPORT_SYMBOL_GPL(backing_file_open);
+
+-struct file *backing_tmpfile_open(const struct path *user_path, int flags,
++struct file *backing_tmpfile_open(const struct file *user_file, int flags,
+ const struct path *real_parentpath,
+ umode_t mode, const struct cred *cred)
+ {
+ struct mnt_idmap *real_idmap = mnt_idmap(real_parentpath->mnt);
++ const struct path *user_path = &user_file->f_path;
+ struct file *f;
+ int error;
+
+- f = alloc_empty_backing_file(flags, cred);
++ f = alloc_empty_backing_file(flags, cred, user_file);
+ if (IS_ERR(f))
+ return f;
+
+@@ -326,6 +329,7 @@ EXPORT_SYMBOL_GPL(backing_file_splice_write);
+ int backing_file_mmap(struct file *file, struct vm_area_struct *vma,
+ struct backing_file_ctx *ctx)
+ {
++ struct file *user_file = vma->vm_file;
+ const struct cred *old_cred;
+ int ret;
+
+@@ -339,6 +343,12 @@ int backing_file_mmap(struct file *file, struct vm_area_struct *vma,
+ vma_set_file(vma, file);
+
+ old_cred = override_creds(ctx->cred);
++ ret = security_mmap_backing_file(vma, file, user_file);
++ if (ret) {
++ revert_creds(old_cred);
++ return ret;
++ }
++
+ ret = call_mmap(vma->vm_file, vma);
+ revert_creds(old_cred);
+
+diff --git a/fs/file_table.c b/fs/file_table.c
+index 75a1908d51a9f5..f78bcff79f1a04 100644
+--- a/fs/file_table.c
++++ b/fs/file_table.c
+@@ -46,6 +46,9 @@ static struct percpu_counter nr_files __cacheline_aligned_in_smp;
+ struct backing_file {
+ struct file file;
+ struct path user_path;
++#ifdef CONFIG_SECURITY
++ void *security;
++#endif
+ };
+
+ #define backing_file(f) container_of(f, struct backing_file, file)
+@@ -62,8 +65,21 @@ void backing_file_set_user_path(struct file *f, const struct path *path)
+ }
+ EXPORT_SYMBOL_GPL(backing_file_set_user_path);
+
++#ifdef CONFIG_SECURITY
++void *backing_file_security(const struct file *f)
++{
++ return backing_file(f)->security;
++}
++
++void backing_file_set_security(struct file *f, void *security)
++{
++ backing_file(f)->security = security;
++}
++#endif /* CONFIG_SECURITY */
++
+ static inline void backing_file_free(struct backing_file *ff)
+ {
++ security_backing_file_free(&ff->file);
+ path_put(&ff->user_path);
+ kfree(ff);
+ }
+@@ -262,10 +278,12 @@ struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred)
+ return f;
+ }
+
+-static int init_backing_file(struct backing_file *ff)
++static int init_backing_file(struct backing_file *ff,
++ const struct file *user_file)
+ {
+ memset(&ff->user_path, 0, sizeof(ff->user_path));
+- return 0;
++ backing_file_set_security(&ff->file, NULL);
++ return security_backing_file_alloc(&ff->file, user_file);
+ }
+
+ /*
+@@ -275,7 +293,8 @@ static int init_backing_file(struct backing_file *ff)
+ * This is only for kernel internal use, and the allocate file must not be
+ * installed into file tables or such.
+ */
+-struct file *alloc_empty_backing_file(int flags, const struct cred *cred)
++struct file *alloc_empty_backing_file(int flags, const struct cred *cred,
++ const struct file *user_file)
+ {
+ struct backing_file *ff;
+ int error;
+@@ -292,7 +311,7 @@ struct file *alloc_empty_backing_file(int flags, const struct cred *cred)
+
+ /* The f_mode flags must be set before fput(). */
+ ff->file.f_mode |= FMODE_BACKING | FMODE_NOACCOUNT;
+- error = init_backing_file(ff);
++ error = init_backing_file(ff, user_file);
+ if (unlikely(error)) {
+ fput(&ff->file);
+ return ERR_PTR(error);
+diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c
+index 6bfd09dda9e3e6..140e150be0de85 100644
+--- a/fs/fuse/passthrough.c
++++ b/fs/fuse/passthrough.c
+@@ -326,7 +326,7 @@ struct fuse_backing *fuse_passthrough_open(struct file *file,
+ goto out;
+
+ /* Allocate backing file per fuse file to store fuse path */
+- backing_file = backing_file_open(&file->f_path, file->f_flags,
++ backing_file = backing_file_open(file, file->f_flags,
+ &fb->file->f_path, fb->cred);
+ err = PTR_ERR(backing_file);
+ if (IS_ERR(backing_file)) {
+diff --git a/fs/internal.h b/fs/internal.h
+index a4352d333c617a..997acc721f6175 100644
+--- a/fs/internal.h
++++ b/fs/internal.h
+@@ -99,7 +99,8 @@ extern void chroot_fs_refs(const struct path *, const struct path *);
+ */
+ struct file *alloc_empty_file(int flags, const struct cred *cred);
+ struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred);
+-struct file *alloc_empty_backing_file(int flags, const struct cred *cred);
++struct file *alloc_empty_backing_file(int flags, const struct cred *cred,
++ const struct file *user_file);
+ void backing_file_set_user_path(struct file *f, const struct path *path);
+
+ static inline void file_put_write_access(struct file *file)
+diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
+index ab65e98a1defdd..1c8009bf194bc1 100644
+--- a/fs/overlayfs/dir.c
++++ b/fs/overlayfs/dir.c
+@@ -1320,7 +1320,7 @@ static int ovl_create_tmpfile(struct file *file, struct dentry *dentry,
+ goto out_revert_creds;
+
+ ovl_path_upper(dentry->d_parent, &realparentpath);
+- realfile = backing_tmpfile_open(&file->f_path, flags, &realparentpath,
++ realfile = backing_tmpfile_open(file, flags, &realparentpath,
+ mode, current_cred());
+ err = PTR_ERR_OR_ZERO(realfile);
+ pr_debug("tmpfile/open(%pd2, 0%o) = %i\n", realparentpath.dentry, mode, err);
+diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c
+index 94095058da34ec..3765e1defa1981 100644
+--- a/fs/overlayfs/file.c
++++ b/fs/overlayfs/file.c
+@@ -47,8 +47,7 @@ static struct file *ovl_open_realfile(const struct file *file,
+ } else {
+ if (!inode_owner_or_capable(real_idmap, realinode))
+ flags &= ~O_NOATIME;
+-
+- realfile = backing_file_open(file_user_path((struct file *) file),
++ realfile = backing_file_open(file,
+ flags, realpath, current_cred());
+ }
+ revert_creds(old_cred);
+diff --git a/include/linux/backing-file.h b/include/linux/backing-file.h
+index 2eed0ffb5e8f83..cd18acd7ac5b29 100644
+--- a/include/linux/backing-file.h
++++ b/include/linux/backing-file.h
+@@ -19,10 +19,10 @@ struct backing_file_ctx {
+ void (*end_write)(struct file *, loff_t, ssize_t);
+ };
+
+-struct file *backing_file_open(const struct path *user_path, int flags,
++struct file *backing_file_open(const struct file *user_file, int flags,
+ const struct path *real_path,
+ const struct cred *cred);
+-struct file *backing_tmpfile_open(const struct path *user_path, int flags,
++struct file *backing_tmpfile_open(const struct file *user_file, int flags,
+ const struct path *real_parentpath,
+ umode_t mode, const struct cred *cred);
+ ssize_t backing_file_read_iter(struct file *file, struct iov_iter *iter,
+diff --git a/include/linux/fs.h b/include/linux/fs.h
+index 70bbc00a2bd250..0eb43147dc877e 100644
+--- a/include/linux/fs.h
++++ b/include/linux/fs.h
+@@ -2740,6 +2740,19 @@ struct file *dentry_create(const struct path *path, int flags, umode_t mode,
+ const struct cred *cred);
+ struct path *backing_file_user_path(const struct file *f);
+
++#ifdef CONFIG_SECURITY
++void *backing_file_security(const struct file *f);
++void backing_file_set_security(struct file *f, void *security);
++#else
++static inline void *backing_file_security(const struct file *f)
++{
++ return NULL;
++}
++static inline void backing_file_set_security(struct file *f, void *security)
++{
++}
++#endif /* CONFIG_SECURITY */
++
+ /*
+ * When mmapping a file on a stackable filesystem (e.g., overlayfs), the file
+ * stored in ->vm_file is a backing file whose f_inode is on the underlying
+diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h
+index 97a8b21eb03339..c0a2839253fa1f 100644
+--- a/include/linux/lsm_audit.h
++++ b/include/linux/lsm_audit.h
+@@ -93,7 +93,7 @@ struct common_audit_data {
+ #endif
+ char *kmod_name;
+ struct lsm_ioctlop_audit *op;
+- struct file *file;
++ const struct file *file;
+ struct lsm_ibpkey_audit *ibpkey;
+ struct lsm_ibendport_audit *ibendport;
+ int reason;
+diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
+index 9eca013aa5e1f6..addb34abffa18a 100644
+--- a/include/linux/lsm_hook_defs.h
++++ b/include/linux/lsm_hook_defs.h
+@@ -188,6 +188,9 @@ LSM_HOOK(int, 0, file_permission, struct file *file, int mask)
+ LSM_HOOK(int, 0, file_alloc_security, struct file *file)
+ LSM_HOOK(void, LSM_RET_VOID, file_release, struct file *file)
+ LSM_HOOK(void, LSM_RET_VOID, file_free_security, struct file *file)
++LSM_HOOK(int, 0, backing_file_alloc, struct file *backing_file,
++ const struct file *user_file)
++LSM_HOOK(void, LSM_RET_VOID, backing_file_free, struct file *backing_file)
+ LSM_HOOK(int, 0, file_ioctl, struct file *file, unsigned int cmd,
+ unsigned long arg)
+ LSM_HOOK(int, 0, file_ioctl_compat, struct file *file, unsigned int cmd,
+@@ -195,6 +198,8 @@ LSM_HOOK(int, 0, file_ioctl_compat, struct file *file, unsigned int cmd,
+ LSM_HOOK(int, 0, mmap_addr, unsigned long addr)
+ LSM_HOOK(int, 0, mmap_file, struct file *file, unsigned long reqprot,
+ unsigned long prot, unsigned long flags)
++LSM_HOOK(int, 0, mmap_backing_file, struct vm_area_struct *vma,
++ struct file *backing_file, struct file *user_file)
+ LSM_HOOK(int, 0, file_mprotect, struct vm_area_struct *vma,
+ unsigned long reqprot, unsigned long prot)
+ LSM_HOOK(int, 0, file_lock, struct file *file, unsigned int cmd)
+diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
+index 090d1d3e19fed6..0876cf11e200c6 100644
+--- a/include/linux/lsm_hooks.h
++++ b/include/linux/lsm_hooks.h
+@@ -104,6 +104,7 @@ struct security_hook_list {
+ struct lsm_blob_sizes {
+ int lbs_cred;
+ int lbs_file;
++ int lbs_backing_file;
+ int lbs_ib;
+ int lbs_inode;
+ int lbs_sock;
+diff --git a/include/linux/security.h b/include/linux/security.h
+index 2c6db949ad1a16..e4300f2ff11b55 100644
+--- a/include/linux/security.h
++++ b/include/linux/security.h
+@@ -421,11 +421,17 @@ int security_file_permission(struct file *file, int mask);
+ int security_file_alloc(struct file *file);
+ void security_file_release(struct file *file);
+ void security_file_free(struct file *file);
++int security_backing_file_alloc(struct file *backing_file,
++ const struct file *user_file);
++void security_backing_file_free(struct file *backing_file);
+ int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+ int security_file_ioctl_compat(struct file *file, unsigned int cmd,
+ unsigned long arg);
+ int security_mmap_file(struct file *file, unsigned long prot,
+ unsigned long flags);
++int security_mmap_backing_file(struct vm_area_struct *vma,
++ struct file *backing_file,
++ struct file *user_file);
+ int security_mmap_addr(unsigned long addr);
+ int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
+ unsigned long prot);
+@@ -1065,6 +1071,15 @@ static inline void security_file_release(struct file *file)
+ static inline void security_file_free(struct file *file)
+ { }
+
++static inline int security_backing_file_alloc(struct file *backing_file,
++ const struct file *user_file)
++{
++ return 0;
++}
++
++static inline void security_backing_file_free(struct file *backing_file)
++{ }
++
+ static inline int security_file_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+ {
+@@ -1084,6 +1099,13 @@ static inline int security_mmap_file(struct file *file, unsigned long prot,
+ return 0;
+ }
+
++static inline int security_mmap_backing_file(struct vm_area_struct *vma,
++ struct file *backing_file,
++ struct file *user_file)
++{
++ return 0;
++}
++
+ static inline int security_mmap_addr(unsigned long addr)
+ {
+ return cap_mmap_addr(addr);
+diff --git a/security/security.c b/security/security.c
+index 6e4deac6ec0737..dd6b922c12de05 100644
+--- a/security/security.c
++++ b/security/security.c
+@@ -95,6 +95,7 @@ const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX + 1] = {
+ static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain);
+
+ static struct kmem_cache *lsm_file_cache;
++static struct kmem_cache *lsm_backing_file_cache;
+ static struct kmem_cache *lsm_inode_cache;
+
+ char *lsm_names;
+@@ -266,6 +267,7 @@ static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
+
+ lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
+ lsm_set_blob_size(&needed->lbs_file, &blob_sizes.lbs_file);
++ lsm_set_blob_size(&needed->lbs_backing_file, &blob_sizes.lbs_backing_file);
+ lsm_set_blob_size(&needed->lbs_ib, &blob_sizes.lbs_ib);
+ /*
+ * The inode blob gets an rcu_head in addition to
+@@ -468,6 +470,7 @@ static void __init ordered_lsm_init(void)
+
+ init_debug("cred blob size = %d\n", blob_sizes.lbs_cred);
+ init_debug("file blob size = %d\n", blob_sizes.lbs_file);
++ init_debug("lsm_backing_file_cache = %d\n", blob_sizes.lbs_backing_file);
+ init_debug("ib blob size = %d\n", blob_sizes.lbs_ib);
+ init_debug("inode blob size = %d\n", blob_sizes.lbs_inode);
+ init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc);
+@@ -490,6 +493,11 @@ static void __init ordered_lsm_init(void)
+ lsm_file_cache = kmem_cache_create("lsm_file_cache",
+ blob_sizes.lbs_file, 0,
+ SLAB_PANIC, NULL);
++ if (blob_sizes.lbs_backing_file)
++ lsm_backing_file_cache = kmem_cache_create(
++ "lsm_backing_file_cache",
++ blob_sizes.lbs_backing_file,
++ 0, SLAB_PANIC, NULL);
+ if (blob_sizes.lbs_inode)
+ lsm_inode_cache = kmem_cache_create("lsm_inode_cache",
+ blob_sizes.lbs_inode, 0,
+@@ -666,6 +674,30 @@ int unregister_blocking_lsm_notifier(struct notifier_block *nb)
+ }
+ EXPORT_SYMBOL(unregister_blocking_lsm_notifier);
+
++/**
++ * lsm_backing_file_alloc - allocate a composite backing file blob
++ * @backing_file: the backing file
++ *
++ * Allocate the backing file blob for all the modules.
++ *
++ * Returns 0, or -ENOMEM if memory can't be allocated.
++ */
++static int lsm_backing_file_alloc(struct file *backing_file)
++{
++ void *blob;
++
++ if (!lsm_backing_file_cache) {
++ backing_file_set_security(backing_file, NULL);
++ return 0;
++ }
++
++ blob = kmem_cache_zalloc(lsm_backing_file_cache, GFP_KERNEL);
++ backing_file_set_security(backing_file, blob);
++ if (!blob)
++ return -ENOMEM;
++ return 0;
++}
++
+ /**
+ * lsm_blob_alloc - allocate a composite blob
+ * @dest: the destination for the blob
+@@ -2893,6 +2925,57 @@ void security_file_free(struct file *file)
+ }
+ }
+
++/**
++ * security_backing_file_alloc() - Allocate and setup a backing file blob
++ * @backing_file: the backing file
++ * @user_file: the associated user visible file
++ *
++ * Allocate a backing file LSM blob and perform any necessary initialization of
++ * the LSM blob. There will be some operations where the LSM will not have
++ * access to @user_file after this point, so any important state associated
++ * with @user_file that is important to the LSM should be captured in the
++ * backing file's LSM blob.
++ *
++ * LSM's should avoid taking a reference to @user_file in this hook as it will
++ * result in problems later when the system attempts to drop/put the file
++ * references due to a circular dependency.
++ *
++ * Return: Return 0 if the hook is successful, negative values otherwise.
++ */
++int security_backing_file_alloc(struct file *backing_file,
++ const struct file *user_file)
++{
++ int rc;
++
++ rc = lsm_backing_file_alloc(backing_file);
++ if (rc)
++ return rc;
++ rc = call_int_hook(backing_file_alloc, backing_file, user_file);
++ if (unlikely(rc))
++ security_backing_file_free(backing_file);
++
++ return rc;
++}
++
++/**
++ * security_backing_file_free() - Free a backing file blob
++ * @backing_file: the backing file
++ *
++ * Free any LSM state associate with a backing file's LSM blob, including the
++ * blob itself.
++ */
++void security_backing_file_free(struct file *backing_file)
++{
++ void *blob = backing_file_security(backing_file);
++
++ call_void_hook(backing_file_free, backing_file);
++
++ if (blob) {
++ backing_file_set_security(backing_file, NULL);
++ kmem_cache_free(lsm_backing_file_cache, blob);
++ }
++}
++
+ /**
+ * security_file_ioctl() - Check if an ioctl is allowed
+ * @file: associated file
+@@ -2981,6 +3064,32 @@ int security_mmap_file(struct file *file, unsigned long prot,
+ flags);
+ }
+
++/**
++ * security_mmap_backing_file - Check if mmap'ing a backing file is allowed
++ * @vma: the vm_area_struct for the mmap'd region
++ * @backing_file: the backing file being mmap'd
++ * @user_file: the user file being mmap'd
++ *
++ * Check permissions for a mmap operation on a stacked filesystem. This hook
++ * is called after the security_mmap_file() and is responsible for authorizing
++ * the mmap on @backing_file. It is important to note that the mmap operation
++ * on @user_file has already been authorized and the @vma->vm_file has been
++ * set to @backing_file.
++ *
++ * Return: Returns 0 if permission is granted.
++ */
++int security_mmap_backing_file(struct vm_area_struct *vma,
++ struct file *backing_file,
++ struct file *user_file)
++{
++ /* recommended by the stackable filesystem devs */
++ if (WARN_ON_ONCE(!(backing_file->f_mode & FMODE_BACKING)))
++ return -EIO;
++
++ return call_int_hook(mmap_backing_file, vma, backing_file, user_file);
++}
++EXPORT_SYMBOL_GPL(security_mmap_backing_file);
++
+ /**
+ * security_mmap_addr() - Check if mmap'ing an address is allowed
+ * @addr: address
+--
+2.53.0
+
--- /dev/null
+From e2a6ca4834e885271a38539223767282388191d2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 29 Jun 2026 15:03:38 +0800
+Subject: selinux: fix overlayfs mmap() and mprotect() access checks
+
+From: Paul Moore <paul@paul-moore.com>
+
+[ Upstream commit 82544d36b1729153c8aeb179e84750f0c085d3b1 ]
+
+The existing SELinux security model for overlayfs is to allow access if
+the current task is able to access the top level file (the "user" file)
+and the mounter's credentials are sufficient to access the lower
+level file (the "backing" file). Unfortunately, the current code does
+not properly enforce these access controls for both mmap() and mprotect()
+operations on overlayfs filesystems.
+
+This patch makes use of the newly created security_mmap_backing_file()
+LSM hook to provide the missing backing file enforcement for mmap()
+operations, and leverages the backing file API and new LSM blob to
+provide the necessary information to properly enforce the mprotect()
+access controls.
+
+Cc: stable@vger.kernel.org
+Acked-by: Amir Goldstein <amir73il@gmail.com>
+Signed-off-by: Paul Moore <paul@paul-moore.com>
+Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ security/selinux/hooks.c | 242 ++++++++++++++++++++++--------
+ security/selinux/include/objsec.h | 11 ++
+ 2 files changed, 189 insertions(+), 64 deletions(-)
+
+diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
+index 8e31d3b60fc62e..1b89c8d5fa2fc3 100644
+--- a/security/selinux/hooks.c
++++ b/security/selinux/hooks.c
+@@ -1724,49 +1724,72 @@ static inline int file_path_has_perm(const struct cred *cred,
+ static int bpf_fd_pass(const struct file *file, u32 sid);
+ #endif
+
+-/* Check whether a task can use an open file descriptor to
+- access an inode in a given way. Check access to the
+- descriptor itself, and then use dentry_has_perm to
+- check a particular permission to the file.
+- Access to the descriptor is implicitly granted if it
+- has the same SID as the process. If av is zero, then
+- access to the file is not checked, e.g. for cases
+- where only the descriptor is affected like seek. */
+-static int file_has_perm(const struct cred *cred,
+- struct file *file,
+- u32 av)
++static int __file_has_perm(const struct cred *cred, const struct file *file,
++ u32 av, bool bf_user_file)
++
+ {
+- struct file_security_struct *fsec = selinux_file(file);
+- struct inode *inode = file_inode(file);
+ struct common_audit_data ad;
+- u32 sid = cred_sid(cred);
++ struct inode *inode;
++ u32 ssid = cred_sid(cred);
++ u32 tsid_fd;
+ int rc;
+
+- ad.type = LSM_AUDIT_DATA_FILE;
+- ad.u.file = file;
++ if (bf_user_file) {
++ struct backing_file_security_struct *bfsec;
++ const struct path *path;
+
+- if (sid != fsec->sid) {
+- rc = avc_has_perm(sid, fsec->sid,
+- SECCLASS_FD,
+- FD__USE,
+- &ad);
++ if (WARN_ON(!(file->f_mode & FMODE_BACKING)))
++ return -EIO;
++
++ bfsec = selinux_backing_file(file);
++ path = backing_file_user_path(file);
++ tsid_fd = bfsec->uf_sid;
++ inode = d_inode(path->dentry);
++
++ ad.type = LSM_AUDIT_DATA_PATH;
++ ad.u.path = *path;
++ } else {
++ struct file_security_struct *fsec = selinux_file(file);
++
++ tsid_fd = fsec->sid;
++ inode = file_inode(file);
++
++ ad.type = LSM_AUDIT_DATA_FILE;
++ ad.u.file = file;
++ }
++
++ if (ssid != tsid_fd) {
++ rc = avc_has_perm(ssid, tsid_fd, SECCLASS_FD, FD__USE, &ad);
+ if (rc)
+- goto out;
++ return rc;
+ }
+
+ #ifdef CONFIG_BPF_SYSCALL
+- rc = bpf_fd_pass(file, cred_sid(cred));
++ /* regardless of backing vs user file, use the underlying file here */
++ rc = bpf_fd_pass(file, ssid);
+ if (rc)
+ return rc;
+ #endif
+
+ /* av is zero if only checking access to the descriptor. */
+- rc = 0;
+ if (av)
+- rc = inode_has_perm(cred, inode, av, &ad);
++ return inode_has_perm(cred, inode, av, &ad);
+
+-out:
+- return rc;
++ return 0;
++}
++
++/* Check whether a task can use an open file descriptor to
++ access an inode in a given way. Check access to the
++ descriptor itself, and then use dentry_has_perm to
++ check a particular permission to the file.
++ Access to the descriptor is implicitly granted if it
++ has the same SID as the process. If av is zero, then
++ access to the file is not checked, e.g. for cases
++ where only the descriptor is affected like seek. */
++static inline int file_has_perm(const struct cred *cred,
++ const struct file *file, u32 av)
++{
++ return __file_has_perm(cred, file, av, false);
+ }
+
+ /*
+@@ -3653,6 +3676,17 @@ static int selinux_file_alloc_security(struct file *file)
+ return 0;
+ }
+
++static int selinux_backing_file_alloc(struct file *backing_file,
++ const struct file *user_file)
++{
++ struct backing_file_security_struct *bfsec;
++
++ bfsec = selinux_backing_file(backing_file);
++ bfsec->uf_sid = selinux_file(user_file)->sid;
++
++ return 0;
++}
++
+ /*
+ * Check whether a task has the ioctl permission and cmd
+ * operation to an inode.
+@@ -3770,42 +3804,55 @@ static int selinux_file_ioctl_compat(struct file *file, unsigned int cmd,
+
+ static int default_noexec __ro_after_init;
+
+-static int file_map_prot_check(struct file *file, unsigned long prot, int shared)
++static int __file_map_prot_check(const struct cred *cred,
++ const struct file *file, unsigned long prot,
++ bool shared, bool bf_user_file)
+ {
+- const struct cred *cred = current_cred();
+- u32 sid = cred_sid(cred);
+- int rc = 0;
++ struct inode *inode = NULL;
++ bool prot_exec = prot & PROT_EXEC;
++ bool prot_write = prot & PROT_WRITE;
++
++ if (file) {
++ if (bf_user_file)
++ inode = d_inode(backing_file_user_path(file)->dentry);
++ else
++ inode = file_inode(file);
++ }
++
++ if (default_noexec && prot_exec &&
++ (!file || IS_PRIVATE(inode) || (!shared && prot_write))) {
++ int rc;
++ u32 sid = cred_sid(cred);
+
+- if (default_noexec &&
+- (prot & PROT_EXEC) && (!file || IS_PRIVATE(file_inode(file)) ||
+- (!shared && (prot & PROT_WRITE)))) {
+ /*
+- * We are making executable an anonymous mapping or a
+- * private file mapping that will also be writable.
+- * This has an additional check.
++ * We are making executable an anonymous mapping or a private
++ * file mapping that will also be writable.
+ */
+- rc = avc_has_perm(sid, sid, SECCLASS_PROCESS,
+- PROCESS__EXECMEM, NULL);
++ rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, PROCESS__EXECMEM,
++ NULL);
+ if (rc)
+- goto error;
++ return rc;
+ }
+
+ if (file) {
+- /* read access is always possible with a mapping */
++ /* "read" always possible, "write" only if shared */
+ u32 av = FILE__READ;
+-
+- /* write access only matters if the mapping is shared */
+- if (shared && (prot & PROT_WRITE))
++ if (shared && prot_write)
+ av |= FILE__WRITE;
+-
+- if (prot & PROT_EXEC)
++ if (prot_exec)
+ av |= FILE__EXECUTE;
+
+- return file_has_perm(cred, file, av);
++ return __file_has_perm(cred, file, av, bf_user_file);
+ }
+
+-error:
+- return rc;
++ return 0;
++}
++
++static inline int file_map_prot_check(const struct cred *cred,
++ const struct file *file,
++ unsigned long prot, bool shared)
++{
++ return __file_map_prot_check(cred, file, prot, shared, false);
+ }
+
+ static int selinux_mmap_addr(unsigned long addr)
+@@ -3821,36 +3868,80 @@ static int selinux_mmap_addr(unsigned long addr)
+ return rc;
+ }
+
+-static int selinux_mmap_file(struct file *file,
+- unsigned long reqprot __always_unused,
+- unsigned long prot, unsigned long flags)
++static int selinux_mmap_file_common(const struct cred *cred, struct file *file,
++ unsigned long prot, bool shared)
+ {
+- struct common_audit_data ad;
+- int rc;
+-
+ if (file) {
++ int rc;
++ struct common_audit_data ad;
++
+ ad.type = LSM_AUDIT_DATA_FILE;
+ ad.u.file = file;
+- rc = inode_has_perm(current_cred(), file_inode(file),
+- FILE__MAP, &ad);
++ rc = inode_has_perm(cred, file_inode(file), FILE__MAP, &ad);
+ if (rc)
+ return rc;
+ }
+
+- return file_map_prot_check(file, prot,
+- (flags & MAP_TYPE) == MAP_SHARED);
++ return file_map_prot_check(cred, file, prot, shared);
++}
++
++static int selinux_mmap_file(struct file *file,
++ unsigned long reqprot __always_unused,
++ unsigned long prot, unsigned long flags)
++{
++ return selinux_mmap_file_common(current_cred(), file, prot,
++ (flags & MAP_TYPE) == MAP_SHARED);
++}
++
++/**
++ * selinux_mmap_backing_file - Check mmap permissions on a backing file
++ * @vma: memory region
++ * @backing_file: stacked filesystem backing file
++ * @user_file: user visible file
++ *
++ * This is called after selinux_mmap_file() on stacked filesystems, and it
++ * is this function's responsibility to verify access to @backing_file and
++ * setup the SELinux state for possible later use in the mprotect() code path.
++ *
++ * By the time this function is called, mmap() access to @user_file has already
++ * been authorized and @vma->vm_file has been set to point to @backing_file.
++ *
++ * Return zero on success, negative values otherwise.
++ */
++static int selinux_mmap_backing_file(struct vm_area_struct *vma,
++ struct file *backing_file,
++ struct file *user_file __always_unused)
++{
++ unsigned long prot = 0;
++
++ /* translate vma->vm_flags perms into PROT perms */
++ if (vma->vm_flags & VM_READ)
++ prot |= PROT_READ;
++ if (vma->vm_flags & VM_WRITE)
++ prot |= PROT_WRITE;
++ if (vma->vm_flags & VM_EXEC)
++ prot |= PROT_EXEC;
++
++ return selinux_mmap_file_common(backing_file->f_cred, backing_file,
++ prot, vma->vm_flags & VM_SHARED);
+ }
+
+ static int selinux_file_mprotect(struct vm_area_struct *vma,
+ unsigned long reqprot __always_unused,
+ unsigned long prot)
+ {
++ int rc;
+ const struct cred *cred = current_cred();
+ u32 sid = cred_sid(cred);
++ const struct file *file = vma->vm_file;
++ bool backing_file;
++ bool shared = vma->vm_flags & VM_SHARED;
++
++ /* check if we need to trigger the "backing files are awful" mode */
++ backing_file = file && (file->f_mode & FMODE_BACKING);
+
+ if (default_noexec &&
+ (prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC)) {
+- int rc = 0;
+ /*
+ * We don't use the vma_is_initial_heap() helper as it has
+ * a history of problems and is currently broken on systems
+@@ -3864,11 +3955,15 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
+ vma->vm_end <= vma->vm_mm->brk) {
+ rc = avc_has_perm(sid, sid, SECCLASS_PROCESS,
+ PROCESS__EXECHEAP, NULL);
+- } else if (!vma->vm_file && (vma_is_initial_stack(vma) ||
++ if (rc)
++ return rc;
++ } else if (!file && (vma_is_initial_stack(vma) ||
+ vma_is_stack_for_current(vma))) {
+ rc = avc_has_perm(sid, sid, SECCLASS_PROCESS,
+ PROCESS__EXECSTACK, NULL);
+- } else if (vma->vm_file && vma->anon_vma) {
++ if (rc)
++ return rc;
++ } else if (file && vma->anon_vma) {
+ /*
+ * We are making executable a file mapping that has
+ * had some COW done. Since pages might have been
+@@ -3876,13 +3971,29 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
+ * modified content. This typically should only
+ * occur for text relocations.
+ */
+- rc = file_has_perm(cred, vma->vm_file, FILE__EXECMOD);
++ rc = __file_has_perm(cred, file, FILE__EXECMOD,
++ backing_file);
++ if (rc)
++ return rc;
++ if (backing_file) {
++ rc = file_has_perm(file->f_cred, file,
++ FILE__EXECMOD);
++ if (rc)
++ return rc;
++ }
+ }
++ }
++
++ rc = __file_map_prot_check(cred, file, prot, shared, backing_file);
++ if (rc)
++ return rc;
++ if (backing_file) {
++ rc = file_map_prot_check(file->f_cred, file, prot, shared);
+ if (rc)
+ return rc;
+ }
+
+- return file_map_prot_check(vma->vm_file, prot, vma->vm_flags&VM_SHARED);
++ return 0;
+ }
+
+ static int selinux_file_lock(struct file *file, unsigned int cmd)
+@@ -6960,6 +7071,7 @@ static void selinux_bpf_token_free(struct bpf_token *token)
+ struct lsm_blob_sizes selinux_blob_sizes __ro_after_init = {
+ .lbs_cred = sizeof(struct task_security_struct),
+ .lbs_file = sizeof(struct file_security_struct),
++ .lbs_backing_file = sizeof(struct backing_file_security_struct),
+ .lbs_inode = sizeof(struct inode_security_struct),
+ .lbs_ipc = sizeof(struct ipc_security_struct),
+ .lbs_key = sizeof(struct key_security_struct),
+@@ -7165,9 +7277,11 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
+
+ LSM_HOOK_INIT(file_permission, selinux_file_permission),
+ LSM_HOOK_INIT(file_alloc_security, selinux_file_alloc_security),
++ LSM_HOOK_INIT(backing_file_alloc, selinux_backing_file_alloc),
+ LSM_HOOK_INIT(file_ioctl, selinux_file_ioctl),
+ LSM_HOOK_INIT(file_ioctl_compat, selinux_file_ioctl_compat),
+ LSM_HOOK_INIT(mmap_file, selinux_mmap_file),
++ LSM_HOOK_INIT(mmap_backing_file, selinux_mmap_backing_file),
+ LSM_HOOK_INIT(mmap_addr, selinux_mmap_addr),
+ LSM_HOOK_INIT(file_mprotect, selinux_file_mprotect),
+ LSM_HOOK_INIT(file_lock, selinux_file_lock),
+diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
+index c88cae81ee4c52..dc42282a2c0521 100644
+--- a/security/selinux/include/objsec.h
++++ b/security/selinux/include/objsec.h
+@@ -61,6 +61,10 @@ struct file_security_struct {
+ u32 pseqno; /* Policy seqno at the time of file open */
+ };
+
++struct backing_file_security_struct {
++ u32 uf_sid; /* associated user file fsec->sid */
++};
++
+ struct superblock_security_struct {
+ u32 sid; /* SID of file system superblock */
+ u32 def_sid; /* default SID for labeling */
+@@ -159,6 +163,13 @@ static inline struct file_security_struct *selinux_file(const struct file *file)
+ return file->f_security + selinux_blob_sizes.lbs_file;
+ }
+
++static inline struct backing_file_security_struct *
++selinux_backing_file(const struct file *backing_file)
++{
++ void *blob = backing_file_security(backing_file);
++ return blob + selinux_blob_sizes.lbs_backing_file;
++}
++
+ static inline struct inode_security_struct *
+ selinux_inode(const struct inode *inode)
+ {
+--
+2.53.0
+
batman-adv-tvlv-enforce-2-byte-alignment.patch
batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch
ipv6-account-for-fraggap-on-the-paged-allocation-path.patch
+fs-constify-file-ptr-in-backing_file-accessor-helper.patch
+lsm-add-backing_file-lsm-hooks.patch
+selinux-fix-overlayfs-mmap-and-mprotect-access-check.patch
+inet-add-indirect-call-wrapper-for-getfrag-calls.patch
+ipv4-account-for-fraggap-on-the-paged-allocation-pat.patch
--- /dev/null
+From 24511df18c0b61698a053802753bf43a61f19eda Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 16 Jun 2026 22:38:29 +0900
+Subject: ipv4: account for fraggap on the paged allocation path
+
+From: Wongi Lee <qw3rtyp0@gmail.com>
+
+[ Upstream commit eca856950f7cb1a221e02b99d758409f2c5cec42 ]
+
+In __ip_append_data(), when the paged-allocation branch is taken,
+alloclen and pagedlen are computed as
+
+ alloclen = fragheaderlen + transhdrlen;
+ pagedlen = datalen - transhdrlen;
+
+datalen already includes fraggap, but the fraggap bytes carried over
+from the previous skb are copied into the new skb's linear area at
+offset transhdrlen by the subsequent skb_copy_and_csum_bits(). The
+linear area is therefore undersized by fraggap bytes while pagedlen is
+overstated by the same amount.
+
+The non-paged branch sets alloclen to fraglen, which already accounts
+for fraggap because datalen does. Bring the paged branch in line by
+adding fraggap to alloclen and subtracting it from pagedlen.
+
+After this adjustment, copy no longer collapses to -fraggap on the
+paged path, so remove the stale comment describing that old arithmetic.
+
+Fixes: 8eb77cc73977 ("ipv4: avoid partial copy for zc")
+Signed-off-by: Jungwoo Lee <jwlee2217@gmail.com>
+Signed-off-by: Wongi Lee <qw3rtyp0@gmail.com>
+Reviewed-by: Ido Schimmel <idosch@nvidia.com>
+Link: https://patch.msgid.link/ajFR1eLAIs42TN3g@DESKTOP-19IMU7U.localdomain
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv4/ip_output.c | 7 ++-----
+ 1 file changed, 2 insertions(+), 5 deletions(-)
+
+diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
+index 7c005263262ff3..7eaf35a6e24ba6 100644
+--- a/net/ipv4/ip_output.c
++++ b/net/ipv4/ip_output.c
+@@ -1117,8 +1117,8 @@ static int __ip_append_data(struct sock *sk,
+ !(rt->dst.dev->features & NETIF_F_SG)))
+ alloclen = fraglen;
+ else {
+- alloclen = fragheaderlen + transhdrlen;
+- pagedlen = datalen - transhdrlen;
++ alloclen = fragheaderlen + transhdrlen + fraggap;
++ pagedlen = datalen - transhdrlen - fraggap;
+ }
+
+ alloclen += alloc_extra;
+@@ -1165,9 +1165,6 @@ static int __ip_append_data(struct sock *sk,
+ }
+
+ copy = datalen - transhdrlen - fraggap - pagedlen;
+- /* [!] NOTE: copy will be negative if pagedlen>0
+- * because then the equation reduces to -fraggap.
+- */
+ if (copy > 0 &&
+ INDIRECT_CALL_1(getfrag, ip_generic_getfrag,
+ from, data + transhdrlen, offset,
+--
+2.53.0
+
batman-adv-tvlv-enforce-2-byte-alignment.patch
batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch
ipv6-account-for-fraggap-on-the-paged-allocation-path.patch
+ipv4-account-for-fraggap-on-the-paged-allocation-pat.patch
--- /dev/null
+From de1abd9a59aa57ee3f255cd3d4688745660c32c3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 3 Dec 2024 17:36:17 +0000
+Subject: inet: add indirect call wrapper for getfrag() calls
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit 5204ccbfa22358f95afd031a3f337e6d9a74baea ]
+
+UDP send path suffers from one indirect call to ip_generic_getfrag()
+
+We can use INDIRECT_CALL_1() to avoid it.
+
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Reviewed-by: Willem de Bruijn <willemb@google.com>
+Reviewed-by: Brian Vazquez <brianvv@google.com>
+Link: https://patch.msgid.link/20241203173617.2595451-1-edumazet@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: eca856950f7c ("ipv4: account for fraggap on the paged allocation path")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv4/ip_output.c | 13 +++++++++----
+ net/ipv6/ip6_output.c | 13 ++++++++-----
+ 2 files changed, 17 insertions(+), 9 deletions(-)
+
+diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
+index 305d0e2786a2a8..fb1d8be8c6757c 100644
+--- a/net/ipv4/ip_output.c
++++ b/net/ipv4/ip_output.c
+@@ -1168,7 +1168,10 @@ static int __ip_append_data(struct sock *sk,
+ /* [!] NOTE: copy will be negative if pagedlen>0
+ * because then the equation reduces to -fraggap.
+ */
+- if (copy > 0 && getfrag(from, data + transhdrlen, offset, copy, fraggap, skb) < 0) {
++ if (copy > 0 &&
++ INDIRECT_CALL_1(getfrag, ip_generic_getfrag,
++ from, data + transhdrlen, offset,
++ copy, fraggap, skb) < 0) {
+ err = -EFAULT;
+ kfree_skb(skb);
+ goto error;
+@@ -1212,8 +1215,9 @@ static int __ip_append_data(struct sock *sk,
+ unsigned int off;
+
+ off = skb->len;
+- if (getfrag(from, skb_put(skb, copy),
+- offset, copy, off, skb) < 0) {
++ if (INDIRECT_CALL_1(getfrag, ip_generic_getfrag,
++ from, skb_put(skb, copy),
++ offset, copy, off, skb) < 0) {
+ __skb_trim(skb, off);
+ err = -EFAULT;
+ goto error;
+@@ -1253,7 +1257,8 @@ static int __ip_append_data(struct sock *sk,
+ get_page(pfrag->page);
+ }
+ copy = min_t(int, copy, pfrag->size - pfrag->offset);
+- if (getfrag(from,
++ if (INDIRECT_CALL_1(getfrag, ip_generic_getfrag,
++ from,
+ page_address(pfrag->page) + pfrag->offset,
+ offset, copy, skb->len, skb) < 0)
+ goto error_efault;
+diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
+index 9a08ee6f52da0f..2e2cd0e0d48a5d 100644
+--- a/net/ipv6/ip6_output.c
++++ b/net/ipv6/ip6_output.c
+@@ -1695,8 +1695,9 @@ static int __ip6_append_data(struct sock *sk,
+ pskb_trim_unique(skb_prev, maxfraglen);
+ }
+ if (copy > 0 &&
+- getfrag(from, data + transhdrlen, offset,
+- copy, fraggap, skb) < 0) {
++ INDIRECT_CALL_1(getfrag, ip_generic_getfrag,
++ from, data + transhdrlen, offset,
++ copy, fraggap, skb) < 0) {
+ err = -EFAULT;
+ kfree_skb(skb);
+ goto error;
+@@ -1740,8 +1741,9 @@ static int __ip6_append_data(struct sock *sk,
+ unsigned int off;
+
+ off = skb->len;
+- if (getfrag(from, skb_put(skb, copy),
+- offset, copy, off, skb) < 0) {
++ if (INDIRECT_CALL_1(getfrag, ip_generic_getfrag,
++ from, skb_put(skb, copy),
++ offset, copy, off, skb) < 0) {
+ __skb_trim(skb, off);
+ err = -EFAULT;
+ goto error;
+@@ -1781,7 +1783,8 @@ static int __ip6_append_data(struct sock *sk,
+ get_page(pfrag->page);
+ }
+ copy = min_t(int, copy, pfrag->size - pfrag->offset);
+- if (getfrag(from,
++ if (INDIRECT_CALL_1(getfrag, ip_generic_getfrag,
++ from,
+ page_address(pfrag->page) + pfrag->offset,
+ offset, copy, skb->len, skb) < 0)
+ goto error_efault;
+--
+2.53.0
+
--- /dev/null
+From 167ae11f067edf463a14d0aa5c312fb48f311f4d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 16 Jun 2026 22:38:29 +0900
+Subject: ipv4: account for fraggap on the paged allocation path
+
+From: Wongi Lee <qw3rtyp0@gmail.com>
+
+[ Upstream commit eca856950f7cb1a221e02b99d758409f2c5cec42 ]
+
+In __ip_append_data(), when the paged-allocation branch is taken,
+alloclen and pagedlen are computed as
+
+ alloclen = fragheaderlen + transhdrlen;
+ pagedlen = datalen - transhdrlen;
+
+datalen already includes fraggap, but the fraggap bytes carried over
+from the previous skb are copied into the new skb's linear area at
+offset transhdrlen by the subsequent skb_copy_and_csum_bits(). The
+linear area is therefore undersized by fraggap bytes while pagedlen is
+overstated by the same amount.
+
+The non-paged branch sets alloclen to fraglen, which already accounts
+for fraggap because datalen does. Bring the paged branch in line by
+adding fraggap to alloclen and subtracting it from pagedlen.
+
+After this adjustment, copy no longer collapses to -fraggap on the
+paged path, so remove the stale comment describing that old arithmetic.
+
+Fixes: 8eb77cc73977 ("ipv4: avoid partial copy for zc")
+Signed-off-by: Jungwoo Lee <jwlee2217@gmail.com>
+Signed-off-by: Wongi Lee <qw3rtyp0@gmail.com>
+Reviewed-by: Ido Schimmel <idosch@nvidia.com>
+Link: https://patch.msgid.link/ajFR1eLAIs42TN3g@DESKTOP-19IMU7U.localdomain
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv4/ip_output.c | 7 ++-----
+ 1 file changed, 2 insertions(+), 5 deletions(-)
+
+diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
+index fb1d8be8c6757c..8502cd34b578c0 100644
+--- a/net/ipv4/ip_output.c
++++ b/net/ipv4/ip_output.c
+@@ -1117,8 +1117,8 @@ static int __ip_append_data(struct sock *sk,
+ !(rt->dst.dev->features & NETIF_F_SG)))
+ alloclen = fraglen;
+ else {
+- alloclen = fragheaderlen + transhdrlen;
+- pagedlen = datalen - transhdrlen;
++ alloclen = fragheaderlen + transhdrlen + fraggap;
++ pagedlen = datalen - transhdrlen - fraggap;
+ }
+
+ alloclen += alloc_extra;
+@@ -1165,9 +1165,6 @@ static int __ip_append_data(struct sock *sk,
+ }
+
+ copy = datalen - transhdrlen - fraggap - pagedlen;
+- /* [!] NOTE: copy will be negative if pagedlen>0
+- * because then the equation reduces to -fraggap.
+- */
+ if (copy > 0 &&
+ INDIRECT_CALL_1(getfrag, ip_generic_getfrag,
+ from, data + transhdrlen, offset,
+--
+2.53.0
+
batman-adv-tvlv-enforce-2-byte-alignment.patch
batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch
ipv6-account-for-fraggap-on-the-paged-allocation-path.patch
+inet-add-indirect-call-wrapper-for-getfrag-calls.patch
+ipv4-account-for-fraggap-on-the-paged-allocation-pat.patch
--- /dev/null
+From 1173171b2a9d13aa72313568288e9561bc378c15 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 28 Jun 2026 16:01:48 +0200
+Subject: drm/amd/display: Fix ISM dc_lock deadlock during suspend
+
+From: Ray Wu <ray.wu@amd.com>
+
+[ Upstream commit 3714fe242592e3699ac5e2c19d68b275a210be7d ]
+
+CachyOS users reported a regression in shutdown/reboot behavior on 7.1
+kernels: the display turns off, but the machine does not power down.
+Reverting ISM fixes the regression, and this upstream fix addresses the
+same ISM dc_lock/workqueue deadlock in the suspend/shutdown paths.
+
+[Why]
+System hang observed during suspend/resume while video is playing.
+amdgpu_dm_ism_disable() is called under dc_lock and waits for ISM
+delayed work via disable_delayed_work_sync(). The work handlers
+themselves take dc_lock, producing an ABBA deadlock when a worker is
+in flight at suspend time.
+
+[How]
+Split the disable path into two phases with opposite locking
+contracts:
+ 1. amdgpu_dm_ism_disable() -- quiesces workers, must NOT hold
+ dc_lock.
+ 2. amdgpu_dm_ism_force_full_power() (new) -- drives the ISM FSM
+ back to FULL_POWER_RUNNING, must hold dc_lock.
+
+Fixes: 754003486c3c ("drm/amd/display: Add Idle state manager(ISM)")
+Link: https://github.com/CachyOS/linux-cachyos/issues/900
+Cc: stable@vger.kernel.org # 7.1.y
+Reviewed-by: Sun peng (Leo) Li <sunpeng.li@amd.com>
+Signed-off-by: Ray Wu <ray.wu@amd.com>
+Signed-off-by: Ivan Lipski <ivan.lipski@amd.com>
+Tested-by: Dan Wheeler <daniel.wheeler@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+Signed-off-by: Peter Jung <admin@ptr1337.dev>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 25 +++++++--
+ .../drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c | 56 ++++++++++++++++---
+ .../drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h | 1 +
+ 3 files changed, 70 insertions(+), 12 deletions(-)
+
+diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+index f8c13bad4ac237..560ab329891146 100644
+--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+@@ -2260,9 +2260,16 @@ static void amdgpu_dm_fini(struct amdgpu_device *adev)
+ adev->dm.idle_workqueue = NULL;
+ }
+
+- /* Disable ISM before dc_destroy() invalidates dm->dc */
++ /*
++ * Disable ISM before dc_destroy() invalidates dm->dc.
++ *
++ * Quiesce workers first without dc_lock (they take dc_lock
++ * themselves, so syncing under it would deadlock), then drive the
++ * FSM back to FULL_POWER_RUNNING under dc_lock.
++ */
++ amdgpu_dm_ism_disable(&adev->dm);
+ scoped_guard(mutex, &adev->dm.dc_lock)
+- amdgpu_dm_ism_disable(&adev->dm);
++ amdgpu_dm_ism_force_full_power(&adev->dm);
+
+ amdgpu_dm_destroy_drm_device(&adev->dm);
+
+@@ -3290,9 +3297,14 @@ static int dm_suspend(struct amdgpu_ip_block *ip_block)
+ if (amdgpu_in_reset(adev)) {
+ enum dc_status res;
+
++ /* Quiesce ISM workers before taking dc_lock (workers take
++ * dc_lock themselves; syncing under it would deadlock).
++ */
++ amdgpu_dm_ism_disable(dm);
++
+ mutex_lock(&dm->dc_lock);
+
+- amdgpu_dm_ism_disable(dm);
++ amdgpu_dm_ism_force_full_power(dm);
+ dc_allow_idle_optimizations(adev->dm.dc, false);
+
+ dm->cached_dc_state = dc_state_create_copy(dm->dc->current_state);
+@@ -3326,8 +3338,13 @@ static int dm_suspend(struct amdgpu_ip_block *ip_block)
+
+ amdgpu_dm_irq_suspend(adev);
+
++ /*
++ * Quiesce ISM workers before taking dc_lock (workers take dc_lock
++ * themselves; syncing under it would deadlock).
++ */
++ amdgpu_dm_ism_disable(dm);
+ scoped_guard(mutex, &dm->dc_lock)
+- amdgpu_dm_ism_disable(dm);
++ amdgpu_dm_ism_force_full_power(dm);
+
+ hpd_rx_irq_work_suspend(dm);
+
+diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c
+index a64e95860e99b3..b32c8d3ac15234 100644
+--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c
++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c
+@@ -524,13 +524,20 @@ static void dm_ism_sso_delayed_work_func(struct work_struct *work)
+ }
+
+ /**
+- * amdgpu_dm_ism_disable - Disable the ISM
++ * amdgpu_dm_ism_disable - Quiesce ISM workers
+ *
+ * @dm: The amdgpu display manager
+ *
+- * Disable the idle state manager by disabling any ISM work, canceling pending
+- * work, and waiting for in-progress work to finish. After disabling, the system
+- * is left in DM_ISM_STATE_FULL_POWER_RUNNING state.
++ * Cancels and disables any pending or in-flight ISM delayed work and waits
++ * for in-progress work to finish. After this returns, no ISM worker can run
++ * and subsequent mod_delayed_work() calls become no-ops via
++ * clear_pending_if_disabled().
++ *
++ * Must NOT be called with dc_lock held: the workers themselves take dc_lock,
++ * so a synchronous wait under dc_lock would deadlock.
++ *
++ * The caller is responsible for driving the FSM back to FULL_POWER_RUNNING
++ * (under dc_lock) by calling amdgpu_dm_ism_force_full_power().
+ */
+ void amdgpu_dm_ism_disable(struct amdgpu_display_manager *dm)
+ {
+@@ -538,21 +545,54 @@ void amdgpu_dm_ism_disable(struct amdgpu_display_manager *dm)
+ struct amdgpu_crtc *acrtc;
+ struct amdgpu_dm_ism *ism;
+
+- ASSERT(mutex_is_locked(&dm->dc_lock));
++ /*
++ * Caller must NOT hold dc_lock: the ISM delayed work handlers
++ * acquire dc_lock themselves, so waiting for them via
++ * disable_delayed_work_sync() while holding dc_lock would
++ * self-deadlock against an in-flight worker.
++ */
++ lockdep_assert_not_held(&dm->dc_lock);
+
+ drm_for_each_crtc(crtc, dm->ddev) {
+ acrtc = to_amdgpu_crtc(crtc);
+ ism = &acrtc->ism;
+
+- /* Cancel and disable any pending work */
+ disable_delayed_work_sync(&ism->delayed_work);
+ disable_delayed_work_sync(&ism->sso_delayed_work);
++ }
++}
++
++/**
++ * amdgpu_dm_ism_force_full_power - Force every CRTC's ISM FSM to FULL_POWER
++ *
++ * @dm: The amdgpu display manager
++ *
++ * Sends DM_ISM_EVENT_EXIT_IDLE_REQUESTED to every CRTC's ISM, leaving each
++ * FSM in FULL_POWER_RUNNING. Intended to be paired with
++ * amdgpu_dm_ism_disable(): callers should first quiesce workers (without
++ * dc_lock), then take dc_lock and call this helper.
++ *
++ * Must be called with dc_lock held.
++ */
++void amdgpu_dm_ism_force_full_power(struct amdgpu_display_manager *dm)
++{
++ struct drm_crtc *crtc;
++ struct amdgpu_crtc *acrtc;
++
++ /*
++ * Caller must hold dc_lock: commit_event() drives the FSM and
++ * may touch dc state via dc_allow_idle_optimizations() etc.
++ */
++ lockdep_assert_held(&dm->dc_lock);
++
++ drm_for_each_crtc(crtc, dm->ddev) {
++ acrtc = to_amdgpu_crtc(crtc);
+
+ /*
+ * When disabled, leave in FULL_POWER_RUNNING state.
+- * EXIT_IDLE will not queue any work
++ * EXIT_IDLE will not queue any work.
+ */
+- amdgpu_dm_ism_commit_event(ism,
++ amdgpu_dm_ism_commit_event(&acrtc->ism,
+ DM_ISM_EVENT_EXIT_IDLE_REQUESTED);
+ }
+ }
+diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h
+index fde0ddc8d4e4bc..964408cd9a839c 100644
+--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h
++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h
+@@ -146,6 +146,7 @@ void amdgpu_dm_ism_fini(struct amdgpu_dm_ism *ism);
+ void amdgpu_dm_ism_commit_event(struct amdgpu_dm_ism *ism,
+ enum amdgpu_dm_ism_event event);
+ void amdgpu_dm_ism_disable(struct amdgpu_display_manager *dm);
++void amdgpu_dm_ism_force_full_power(struct amdgpu_display_manager *dm);
+ void amdgpu_dm_ism_enable(struct amdgpu_display_manager *dm);
+
+ #endif
+--
+2.53.0
+
--- /dev/null
+From dbf0e7a1bc3c0226f961bcee64b2815392e10a59 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 16 Jun 2026 22:38:29 +0900
+Subject: ipv4: account for fraggap on the paged allocation path
+
+From: Wongi Lee <qw3rtyp0@gmail.com>
+
+[ Upstream commit eca856950f7cb1a221e02b99d758409f2c5cec42 ]
+
+In __ip_append_data(), when the paged-allocation branch is taken,
+alloclen and pagedlen are computed as
+
+ alloclen = fragheaderlen + transhdrlen;
+ pagedlen = datalen - transhdrlen;
+
+datalen already includes fraggap, but the fraggap bytes carried over
+from the previous skb are copied into the new skb's linear area at
+offset transhdrlen by the subsequent skb_copy_and_csum_bits(). The
+linear area is therefore undersized by fraggap bytes while pagedlen is
+overstated by the same amount.
+
+The non-paged branch sets alloclen to fraglen, which already accounts
+for fraggap because datalen does. Bring the paged branch in line by
+adding fraggap to alloclen and subtracting it from pagedlen.
+
+After this adjustment, copy no longer collapses to -fraggap on the
+paged path, so remove the stale comment describing that old arithmetic.
+
+Fixes: 8eb77cc73977 ("ipv4: avoid partial copy for zc")
+Signed-off-by: Jungwoo Lee <jwlee2217@gmail.com>
+Signed-off-by: Wongi Lee <qw3rtyp0@gmail.com>
+Reviewed-by: Ido Schimmel <idosch@nvidia.com>
+Link: https://patch.msgid.link/ajFR1eLAIs42TN3g@DESKTOP-19IMU7U.localdomain
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv4/ip_output.c | 7 ++-----
+ 1 file changed, 2 insertions(+), 5 deletions(-)
+
+diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
+index 5bcd73cbdb41c0..ec790bad1679a6 100644
+--- a/net/ipv4/ip_output.c
++++ b/net/ipv4/ip_output.c
+@@ -1117,8 +1117,8 @@ static int __ip_append_data(struct sock *sk,
+ !(rt->dst.dev->features & NETIF_F_SG)))
+ alloclen = fraglen;
+ else {
+- alloclen = fragheaderlen + transhdrlen;
+- pagedlen = datalen - transhdrlen;
++ alloclen = fragheaderlen + transhdrlen + fraggap;
++ pagedlen = datalen - transhdrlen - fraggap;
+ }
+
+ alloclen += alloc_extra;
+@@ -1165,9 +1165,6 @@ static int __ip_append_data(struct sock *sk,
+ }
+
+ copy = datalen - transhdrlen - fraggap - pagedlen;
+- /* [!] NOTE: copy will be negative if pagedlen>0
+- * because then the equation reduces to -fraggap.
+- */
+ if (copy > 0 &&
+ INDIRECT_CALL_1(getfrag, ip_generic_getfrag,
+ from, data + transhdrlen, offset,
+--
+2.53.0
+
batman-adv-tvlv-enforce-2-byte-alignment.patch
batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch
ipv6-account-for-fraggap-on-the-paged-allocation-path.patch
+ipv4-account-for-fraggap-on-the-paged-allocation-pat.patch