* [PATCH 01/12] fs: add kiocb_start_write_trylock()
2026-11-14 10:47 [PATCH 00/12] fs: take freeze protection for writes to backing files Christian Brauner
@ 2026-11-14 10:47 ` Christian Brauner
2026-11-14 10:47 ` [PATCH 02/12] zloop: take freeze protection for writes Christian Brauner
` (11 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Christian Brauner @ 2026-11-14 10:47 UTC (permalink / raw)
To: Jan Kara
Cc: Shakeel Butt, Alexander Viro, Jens Axboe, linux-fsdevel,
linux-kernel, io-uring, Christoph Hellwig, linux-block,
linux-nvme, linux-scsi, target-devel, linux-unionfs, bpf,
linux-nfs, Christian Brauner (Amutable), stable
kiocb_start_write() waits until a frozen filesystem is thawed. A
nonblocking submission can't use it. io_uring open-codes the trylock
variant for its IOCB_NOWAIT writes.
Add kiocb_start_write_trylock() next to kiocb_start_write() and use it
in io_uring. The nvme target's file backend is going to need it as
well.
No functional changes.
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
include/linux/fs.h | 22 ++++++++++++++++++++++
io_uring/rw.c | 9 +--------
2 files changed, 23 insertions(+), 8 deletions(-)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 99caef43ff3c4..eefc2c9d4af19 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2801,6 +2801,28 @@ static inline void kiocb_start_write(struct kiocb *iocb)
__sb_writers_release(inode->i_sb, SB_FREEZE_WRITE);
}
+/**
+ * kiocb_start_write_trylock - try to get write access to a superblock for
+ * async file io
+ * @iocb: the io context we want to submit the write with
+ *
+ * This is kiocb_start_write() for a submission that must not wait until a
+ * frozen filesystem is thawed. Should be matched with a call to
+ * kiocb_end_write() if it succeeds.
+ *
+ * Return: true if write access was acquired, false if it wasn't.
+ */
+static inline bool kiocb_start_write_trylock(struct kiocb *iocb)
+{
+ struct inode *inode = file_inode(iocb->ki_filp);
+
+ if (!sb_start_write_trylock(inode->i_sb))
+ return false;
+ /* Fool lockdep just like kiocb_start_write() does. */
+ __sb_writers_release(inode->i_sb, SB_FREEZE_WRITE);
+ return true;
+}
+
/**
* kiocb_end_write - drop write access to a superblock after async file io
* @iocb: the io context we sumbitted the write with
diff --git a/io_uring/rw.c b/io_uring/rw.c
index 3a8559ff4dc66..a0a16fc521455 100644
--- a/io_uring/rw.c
+++ b/io_uring/rw.c
@@ -1095,9 +1095,6 @@ int io_read_mshot(struct io_kiocb *req, unsigned int issue_flags)
static bool io_kiocb_start_write(struct io_kiocb *req, struct kiocb *kiocb)
{
- struct inode *inode;
- bool ret;
-
if (!(req->flags & REQ_F_ISREG))
return true;
if (!(kiocb->ki_flags & IOCB_NOWAIT)) {
@@ -1105,11 +1102,7 @@ static bool io_kiocb_start_write(struct io_kiocb *req, struct kiocb *kiocb)
return true;
}
- inode = file_inode(kiocb->ki_filp);
- ret = sb_start_write_trylock(inode->i_sb);
- if (ret)
- __sb_writers_release(inode->i_sb, SB_FREEZE_WRITE);
- return ret;
+ return kiocb_start_write_trylock(kiocb);
}
int io_write(struct io_kiocb *req, unsigned int issue_flags)
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH 02/12] zloop: take freeze protection for writes
2026-11-14 10:47 [PATCH 00/12] fs: take freeze protection for writes to backing files Christian Brauner
2026-11-14 10:47 ` [PATCH 01/12] fs: add kiocb_start_write_trylock() Christian Brauner
@ 2026-11-14 10:47 ` Christian Brauner
2026-11-14 10:47 ` [PATCH 03/12] nvmet: take freeze protection for writes to files Christian Brauner
` (10 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Christian Brauner @ 2026-11-14 10:47 UTC (permalink / raw)
To: Jan Kara
Cc: Shakeel Butt, Alexander Viro, Jens Axboe, linux-fsdevel,
linux-kernel, io-uring, Christoph Hellwig, linux-block,
linux-nvme, linux-scsi, target-devel, linux-unionfs, bpf,
linux-nfs, Christian Brauner (Amutable), stable
zloop calls ->write_iter() of the zone files without taking freeze
protection. So a write to a zloop device goes through to a filesystem
that is frozen. With the zone files on ext4:
WARNING: fs/ext4/ext4_jbd2.c:90 at ext4_journal_check_start+0x1f6/0x2d0, CPU#3: kworker/u16:4/507
Workqueue: zloop0 zloop_cmd_workfn
Call Trace:
<TASK>
__ext4_journal_start_sb+0x7a/0x470
ext4_dirty_inode+0xa1/0x120
__mark_inode_dirty+0x154/0x1110
file_update_time_flags+0x3ab/0x4b0
ext4_buffered_write_iter+0x143/0x630
ext4_file_write_iter+0x7b9/0x1570
zloop_do_rw+0x8ac/0xe10
zloop_cmd_workfn+0xac5/0x1ff0
process_one_work+0x946/0x1820
worker_thread+0x4f1/0xd60
kthread+0x364/0x460
ret_from_fork+0x525/0x770
ret_from_fork_asm+0x1a/0x30
</TASK>
Take freeze protection around the write like loop does. A write that
wasn't queued drops it right away. A write that was queued drops it in
the completion callback.
Note that zloop_rw() also called the completion callback for I/O that
wasn't queued or that failed before it was submitted. It completes
these itself now so the callback only ever sees queued I/O.
Fixes: eb0570c7df23 ("block: new zoned loop block device driver")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
drivers/block/zloop.c | 22 +++++++++++++++++-----
1 file changed, 17 insertions(+), 5 deletions(-)
diff --git a/drivers/block/zloop.c b/drivers/block/zloop.c
index 65634a39b0bfc..34ed925e86f1a 100644
--- a/drivers/block/zloop.c
+++ b/drivers/block/zloop.c
@@ -514,6 +514,10 @@ static void zloop_rw_complete(struct kiocb *iocb, long ret)
{
struct zloop_cmd *cmd = container_of(iocb, struct zloop_cmd, iocb);
+ /* A queued write still holds freeze protection. */
+ if (req_op(blk_mq_rq_from_pdu(cmd)) != REQ_OP_READ)
+ kiocb_end_write(iocb);
+
cmd->ret = ret;
zloop_put_cmd(cmd);
}
@@ -527,6 +531,7 @@ static int zloop_do_rw(struct zloop_cmd *cmd)
struct zloop_zone *zone = &zlo->zones[rq_zone_no(rq)];
struct req_iterator rq_iter;
struct iov_iter iter;
+ int ret;
if (rq->bio != rq->biotail) {
struct bio_vec tmp, *bvec;
@@ -566,9 +571,14 @@ static int zloop_do_rw(struct zloop_cmd *cmd)
cmd->iocb.ki_flags = IOCB_DIRECT;
cmd->iocb.ki_ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_NONE, 0);
- if (rw == ITER_SOURCE)
- return zone->file->f_op->write_iter(&cmd->iocb, &iter);
- return zone->file->f_op->read_iter(&cmd->iocb, &iter);
+ if (rw == ITER_DEST)
+ return zone->file->f_op->read_iter(&cmd->iocb, &iter);
+
+ kiocb_start_write(&cmd->iocb);
+ ret = zone->file->f_op->write_iter(&cmd->iocb, &iter);
+ if (ret != -EIOCBQUEUED)
+ kiocb_end_write(&cmd->iocb);
+ return ret;
}
static int zloop_seq_write_prep(struct zloop_cmd *cmd)
@@ -690,8 +700,10 @@ static void zloop_rw(struct zloop_cmd *cmd)
ret = zloop_do_rw(cmd);
}
out:
- if (ret != -EIOCBQUEUED)
- zloop_rw_complete(&cmd->iocb, ret);
+ if (ret != -EIOCBQUEUED) {
+ cmd->ret = ret;
+ zloop_put_cmd(cmd);
+ }
zloop_put_cmd(cmd);
}
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH 03/12] nvmet: take freeze protection for writes to files
2026-11-14 10:47 [PATCH 00/12] fs: take freeze protection for writes to backing files Christian Brauner
2026-11-14 10:47 ` [PATCH 01/12] fs: add kiocb_start_write_trylock() Christian Brauner
2026-11-14 10:47 ` [PATCH 02/12] zloop: take freeze protection for writes Christian Brauner
@ 2026-11-14 10:47 ` Christian Brauner
2026-11-14 10:47 ` [PATCH 04/12] target: fileio: don't free the aio command under the filesystem Christian Brauner
` (9 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Christian Brauner @ 2026-11-14 10:47 UTC (permalink / raw)
To: Jan Kara
Cc: Shakeel Butt, Alexander Viro, Jens Axboe, linux-fsdevel,
linux-kernel, io-uring, Christoph Hellwig, linux-block,
linux-nvme, linux-scsi, target-devel, linux-unionfs, bpf,
linux-nfs, Christian Brauner (Amutable), stable
The file backend calls ->write_iter() of the backing file without
taking freeze protection. So a write to a namespace goes through to a
filesystem that is frozen. With the backing file on ext4 and
buffered_io:
WARNING: fs/ext4/ext4_jbd2.c:90 at ext4_journal_check_start+0x1f6/0x2d0, CPU#1: kworker/1:1/56
Workqueue: nvmet-buffered-io-wq nvmet_file_buffered_io_work
Call Trace:
<TASK>
__ext4_journal_start_sb+0x7a/0x470
ext4_dirty_inode+0xa1/0x120
__mark_inode_dirty+0x154/0x1110
file_update_time_flags+0x3ab/0x4b0
ext4_buffered_write_iter+0x143/0x630
ext4_file_write_iter+0x7b9/0x1570
nvmet_file_submit_bvec+0x2e7/0x590
nvmet_file_execute_io+0x460/0x830
process_one_work+0x946/0x1820
worker_thread+0x4f1/0xd60
kthread+0x364/0x460
ret_from_fork+0x525/0x770
ret_from_fork_asm+0x1a/0x30
</TASK>
The same happens with direct I/O.
Take freeze protection around the write. A write that wasn't queued
drops it right away. A write that was queued drops it in the completion
callback. nvmet_file_io_done() is also called for I/O that wasn't queued
so queued I/O gets its own callback.
Note that buffered I/O is tried with IOCB_NOWAIT first. That attempt
must not wait until the filesystem is thawed. So it only tries to get
freeze protection and fails with -EAGAIN otherwise. The write is then
retried from the workqueue like any other write that would have
blocked.
Fixes: d5eff33ee6f8 ("nvmet: add simple file backed ns support")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
drivers/nvme/target/io-cmd-file.c | 37 ++++++++++++++++++++++++++++++-------
1 file changed, 30 insertions(+), 7 deletions(-)
diff --git a/drivers/nvme/target/io-cmd-file.c b/drivers/nvme/target/io-cmd-file.c
index 695963dd932ee..2a2be95263733 100644
--- a/drivers/nvme/target/io-cmd-file.c
+++ b/drivers/nvme/target/io-cmd-file.c
@@ -77,27 +77,39 @@ static ssize_t nvmet_file_submit_bvec(struct nvmet_req *req, loff_t pos,
unsigned long nr_segs, size_t count, int ki_flags)
{
struct kiocb *iocb = &req->f.iocb;
- ssize_t (*call_iter)(struct kiocb *iocb, struct iov_iter *iter);
+ struct file *file = req->ns->file;
struct iov_iter iter;
+ ssize_t ret;
int rw;
if (req->cmd->rw.opcode == nvme_cmd_write) {
if (req->cmd->rw.control & cpu_to_le16(NVME_RW_FUA))
ki_flags |= IOCB_DSYNC;
- call_iter = req->ns->file->f_op->write_iter;
rw = ITER_SOURCE;
} else {
- call_iter = req->ns->file->f_op->read_iter;
rw = ITER_DEST;
}
iov_iter_bvec(&iter, rw, req->f.bvec, nr_segs, count);
iocb->ki_pos = pos;
- iocb->ki_filp = req->ns->file;
- iocb->ki_flags = ki_flags | iocb->ki_filp->f_iocb_flags;
+ iocb->ki_filp = file;
+ iocb->ki_flags = ki_flags | file->f_iocb_flags;
- return call_iter(iocb, &iter);
+ if (rw == ITER_DEST)
+ return file->f_op->read_iter(iocb, &iter);
+
+ /* The IOCB_NOWAIT attempt must not wait for a thaw. */
+ if (ki_flags & IOCB_NOWAIT) {
+ if (!kiocb_start_write_trylock(iocb))
+ return -EAGAIN;
+ } else {
+ kiocb_start_write(iocb);
+ }
+ ret = file->f_op->write_iter(iocb, &iter);
+ if (ret != -EIOCBQUEUED)
+ kiocb_end_write(iocb);
+ return ret;
}
static void nvmet_file_io_done(struct kiocb *iocb, long ret)
@@ -117,6 +129,17 @@ static void nvmet_file_io_done(struct kiocb *iocb, long ret)
nvmet_req_complete(req, status);
}
+static void nvmet_file_io_complete(struct kiocb *iocb, long ret)
+{
+ struct nvmet_req *req = container_of(iocb, struct nvmet_req, f.iocb);
+
+ /* A queued write still holds freeze protection. */
+ if (req->cmd->rw.opcode == nvme_cmd_write)
+ kiocb_end_write(iocb);
+
+ nvmet_file_io_done(iocb, ret);
+}
+
static bool nvmet_file_execute_io(struct nvmet_req *req, int ki_flags)
{
ssize_t nr_bvec = req->sg_cnt;
@@ -175,7 +198,7 @@ static bool nvmet_file_execute_io(struct nvmet_req *req, int ki_flags)
* for the IOCB_NOWAIT case.
*/
if (!(ki_flags & IOCB_NOWAIT))
- req->f.iocb.ki_complete = nvmet_file_io_done;
+ req->f.iocb.ki_complete = nvmet_file_io_complete;
ret = nvmet_file_submit_bvec(req, pos, bv_cnt, total_len, ki_flags);
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH 04/12] target: fileio: don't free the aio command under the filesystem
2026-11-14 10:47 [PATCH 00/12] fs: take freeze protection for writes to backing files Christian Brauner
` (2 preceding siblings ...)
2026-11-14 10:47 ` [PATCH 03/12] nvmet: take freeze protection for writes to files Christian Brauner
@ 2026-11-14 10:47 ` Christian Brauner
2026-11-14 10:47 ` [PATCH 05/12] target: fileio: take freeze protection for aio writes Christian Brauner
` (8 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Christian Brauner @ 2026-11-14 10:47 UTC (permalink / raw)
To: Jan Kara
Cc: Shakeel Butt, Alexander Viro, Jens Axboe, linux-fsdevel,
linux-kernel, io-uring, Christoph Hellwig, linux-block,
linux-nvme, linux-scsi, target-devel, linux-unionfs, bpf,
linux-nfs, Christian Brauner (Amutable), stable
With fd_async_io=1 the fileio backend allocates a command that holds the
kiocb for every read and write and frees it in the completion callback.
Nothing keeps the command alive until ->read_iter() or ->write_iter()
return. But a filesystem may use the kiocb after it queued the I/O. ext4
does that for direct reads:
BUG: KASAN: slab-use-after-free in ext4_dio_read_iter+0x5e1/0x690
Workqueue: target_submission target_queued_submit_work
Call Trace:
<TASK>
dump_stack_lvl+0x5d/0x80
print_report+0x14b/0x4b0
kasan_report+0x108/0x130
ext4_dio_read_iter+0x5e1/0x690
fd_execute_rw+0xd30/0x1700
__target_execute_cmd+0x53/0x190
transport_generic_new_cmd+0x65a/0xdb0
__target_submit+0x12c/0x380
target_queued_submit_work+0x73/0x270
process_one_work+0x946/0x1820
worker_thread+0x4f1/0xd60
kthread+0x364/0x460
ret_from_fork+0x525/0x770
ret_from_fork_asm+0x1a/0x30
</TASK>
Allocated by task 59:
__kmalloc_noprof+0x223/0x830
fd_execute_rw+0x766/0x1700
__target_execute_cmd+0x53/0x190
transport_generic_new_cmd+0x65a/0xdb0
__target_submit+0x12c/0x380
target_queued_submit_work+0x73/0x270
Freed by task 14:
kfree+0x267/0x6a0
blk_update_request+0x3f5/0x1260
blk_mq_end_request+0x59/0x450
blk_complete_reqs+0xb0/0x130
handle_softirqs+0x20c/0x860
run_ksoftirqd+0x31/0x60
The read completed and freed the command before ext4_dio_read_iter() got
to file_accessed(iocb->ki_filp).
Do what loop, zloop and fs/backing-file.c do and hold a reference to the
command until the I/O is submitted.
Fixes: 769031c92c8b ("scsi: target: target/file: Add support of direct and async I/O")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
drivers/target/target_core_file.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c
index ab9824a4852f1..4dace29b6d662 100644
--- a/drivers/target/target_core_file.c
+++ b/drivers/target/target_core_file.c
@@ -15,6 +15,7 @@
#include <linux/timer.h>
#include <linux/blkdev.h>
#include <linux/slab.h>
+#include <linux/refcount.h>
#include <linux/spinlock.h>
#include <linux/module.h>
#include <linux/vmalloc.h>
@@ -244,10 +245,17 @@ static void fd_destroy_device(struct se_device *dev)
struct target_core_file_cmd {
unsigned long len;
struct se_cmd *cmd;
+ refcount_t ref;
struct kiocb iocb;
struct bio_vec bvecs[];
};
+static void cmd_rw_aio_put(struct target_core_file_cmd *cmd)
+{
+ if (refcount_dec_and_test(&cmd->ref))
+ kfree(cmd);
+}
+
static void cmd_rw_aio_complete(struct kiocb *iocb, long ret)
{
struct target_core_file_cmd *cmd;
@@ -259,7 +267,7 @@ static void cmd_rw_aio_complete(struct kiocb *iocb, long ret)
else
target_complete_cmd(cmd->cmd, SAM_STAT_GOOD);
- kfree(cmd);
+ cmd_rw_aio_put(cmd);
}
static sense_reason_t
@@ -288,6 +296,8 @@ fd_execute_rw_aio(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents,
iov_iter_bvec(&iter, is_write, aio_cmd->bvecs, sgl_nents, len);
+ /* The filesystem may still use the kiocb after it queued the I/O. */
+ refcount_set(&aio_cmd->ref, 2);
aio_cmd->cmd = cmd;
aio_cmd->len = len;
aio_cmd->iocb.ki_pos = cmd->t_task_lba * dev->dev_attrib.block_size;
@@ -305,6 +315,7 @@ fd_execute_rw_aio(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents,
if (ret != -EIOCBQUEUED)
cmd_rw_aio_complete(&aio_cmd->iocb, ret);
+ cmd_rw_aio_put(aio_cmd);
return 0;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH 05/12] target: fileio: take freeze protection for aio writes
2026-11-14 10:47 [PATCH 00/12] fs: take freeze protection for writes to backing files Christian Brauner
` (3 preceding siblings ...)
2026-11-14 10:47 ` [PATCH 04/12] target: fileio: don't free the aio command under the filesystem Christian Brauner
@ 2026-11-14 10:47 ` Christian Brauner
2026-11-14 10:47 ` [PATCH 06/12] loop: take freeze protection for discard and write zeroes Christian Brauner
` (7 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Christian Brauner @ 2026-11-14 10:47 UTC (permalink / raw)
To: Jan Kara
Cc: Shakeel Butt, Alexander Viro, Jens Axboe, linux-fsdevel,
linux-kernel, io-uring, Christoph Hellwig, linux-block,
linux-nvme, linux-scsi, target-devel, linux-unionfs, bpf,
linux-nfs, Christian Brauner (Amutable), stable
With fd_async_io=1 the fileio backend calls ->write_iter() of the
backing file without taking freeze protection. So a write to the LUN
goes through to a filesystem that is frozen. With the backing file on
ext4:
WARNING: fs/ext4/ext4_jbd2.c:90 at ext4_journal_check_start+0x1f6/0x2d0, CPU#0: kworker/0:2/40
Workqueue: target_submission target_queued_submit_work
Call Trace:
<TASK>
__ext4_journal_start_sb+0x7a/0x470
ext4_dirty_inode+0xa1/0x120
__mark_inode_dirty+0x154/0x1110
file_update_time_flags+0x3ab/0x4b0
ext4_file_write_iter+0xf11/0x1570
fd_execute_rw+0xb01/0x1700
__target_execute_cmd+0x53/0x190
tcm_loop_write_pending+0xe/0x20
transport_generic_new_cmd+0x612/0xdb0
__target_submit+0x12c/0x380
target_queued_submit_work+0x73/0x270
process_one_work+0x946/0x1820
worker_thread+0x4f1/0xd60
kthread+0x364/0x460
ret_from_fork+0x525/0x770
ret_from_fork_asm+0x1a/0x30
</TASK>
The synchronous path doesn't have that problem. vfs_iter_write() takes
freeze protection.
Take freeze protection around the write. A write that wasn't queued
drops it right away. A write that was queued drops it in the completion
callback.
Note that the completion callback can't tell from the command whether
it completes a write. COMPARE AND WRITE is a write but reads the data to
compare first. So remember the direction of the I/O in the aio command.
Fixes: 769031c92c8b ("scsi: target: target/file: Add support of direct and async I/O")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
drivers/target/target_core_file.c | 31 +++++++++++++++++++++++--------
1 file changed, 23 insertions(+), 8 deletions(-)
diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c
index 4dace29b6d662..27b266a092b18 100644
--- a/drivers/target/target_core_file.c
+++ b/drivers/target/target_core_file.c
@@ -246,6 +246,7 @@ struct target_core_file_cmd {
unsigned long len;
struct se_cmd *cmd;
refcount_t ref;
+ bool is_write;
struct kiocb iocb;
struct bio_vec bvecs[];
};
@@ -256,12 +257,8 @@ static void cmd_rw_aio_put(struct target_core_file_cmd *cmd)
kfree(cmd);
}
-static void cmd_rw_aio_complete(struct kiocb *iocb, long ret)
+static void cmd_rw_aio_done(struct target_core_file_cmd *cmd, long ret)
{
- struct target_core_file_cmd *cmd;
-
- cmd = container_of(iocb, struct target_core_file_cmd, iocb);
-
if (ret != cmd->len)
target_complete_cmd(cmd->cmd, SAM_STAT_CHECK_CONDITION);
else
@@ -270,6 +267,19 @@ static void cmd_rw_aio_complete(struct kiocb *iocb, long ret)
cmd_rw_aio_put(cmd);
}
+static void cmd_rw_aio_complete(struct kiocb *iocb, long ret)
+{
+ struct target_core_file_cmd *cmd;
+
+ cmd = container_of(iocb, struct target_core_file_cmd, iocb);
+
+ /* A queued write still holds freeze protection. */
+ if (cmd->is_write)
+ kiocb_end_write(iocb);
+
+ cmd_rw_aio_done(cmd, ret);
+}
+
static sense_reason_t
fd_execute_rw_aio(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents,
enum dma_data_direction data_direction)
@@ -300,6 +310,7 @@ fd_execute_rw_aio(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents,
refcount_set(&aio_cmd->ref, 2);
aio_cmd->cmd = cmd;
aio_cmd->len = len;
+ aio_cmd->is_write = is_write;
aio_cmd->iocb.ki_pos = cmd->t_task_lba * dev->dev_attrib.block_size;
aio_cmd->iocb.ki_filp = file;
aio_cmd->iocb.ki_complete = cmd_rw_aio_complete;
@@ -308,13 +319,17 @@ fd_execute_rw_aio(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents,
if (is_write && (cmd->se_cmd_flags & SCF_FUA))
aio_cmd->iocb.ki_flags |= IOCB_DSYNC;
- if (is_write)
+ if (is_write) {
+ kiocb_start_write(&aio_cmd->iocb);
ret = file->f_op->write_iter(&aio_cmd->iocb, &iter);
- else
+ if (ret != -EIOCBQUEUED)
+ kiocb_end_write(&aio_cmd->iocb);
+ } else {
ret = file->f_op->read_iter(&aio_cmd->iocb, &iter);
+ }
if (ret != -EIOCBQUEUED)
- cmd_rw_aio_complete(&aio_cmd->iocb, ret);
+ cmd_rw_aio_done(aio_cmd, ret);
cmd_rw_aio_put(aio_cmd);
return 0;
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH 06/12] loop: take freeze protection for discard and write zeroes
2026-11-14 10:47 [PATCH 00/12] fs: take freeze protection for writes to backing files Christian Brauner
` (4 preceding siblings ...)
2026-11-14 10:47 ` [PATCH 05/12] target: fileio: take freeze protection for aio writes Christian Brauner
@ 2026-11-14 10:47 ` Christian Brauner
2026-11-14 10:47 ` [PATCH 07/12] zloop: take freeze protection when recording write pointers Christian Brauner
` (6 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Christian Brauner @ 2026-11-14 10:47 UTC (permalink / raw)
To: Jan Kara
Cc: Shakeel Butt, Alexander Viro, Jens Axboe, linux-fsdevel,
linux-kernel, io-uring, Christoph Hellwig, linux-block,
linux-nvme, linux-scsi, target-devel, linux-unionfs, bpf,
linux-nfs, Christian Brauner (Amutable), stable
Discard and write zeroes requests end up in lo_fallocate() which calls
->fallocate() of the backing file directly. That skips the freeze
protection vfs_fallocate() would have taken. So they go straight
through to a filesystem that is frozen. With the backing file on ext4:
WARNING: fs/ext4/ext4_jbd2.c:90 at ext4_journal_check_start+0x1f6/0x2d0, CPU#3: kworker/u16:1/44
Workqueue: loop1 loop_rootcg_workfn
Call Trace:
<TASK>
__ext4_journal_start_sb+0x7a/0x470
ext4_dirty_inode+0xa1/0x120
__mark_inode_dirty+0x154/0x1110
file_update_time_flags+0x3ab/0x4b0
ext4_fallocate+0x286/0x3440
lo_fallocate+0x13e/0x290
loop_process_work+0x486/0xe70
process_one_work+0x946/0x1820
worker_thread+0x4f1/0xd60
kthread+0x364/0x460
ret_from_fork+0x525/0x770
ret_from_fork_asm+0x1a/0x30
</TASK>
Take freeze protection around ->fallocate() like vfs_fallocate() does.
Fixes: dfaa2ef68e80 ("loop: add discard support for loop devices")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
drivers/block/loop.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index a3325ceab484a..77f0601475bb9 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -259,7 +259,9 @@ static int lo_fallocate(struct loop_device *lo, struct request *rq, loff_t pos,
if (!bdev_max_discard_sectors(lo->lo_device))
return -EOPNOTSUPP;
+ file_start_write(file);
ret = file->f_op->fallocate(file, mode, pos, blk_rq_bytes(rq));
+ file_end_write(file);
if (unlikely(ret && ret != -EINVAL && ret != -EOPNOTSUPP))
return -EIO;
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH 07/12] zloop: take freeze protection when recording write pointers
2026-11-14 10:47 [PATCH 00/12] fs: take freeze protection for writes to backing files Christian Brauner
` (5 preceding siblings ...)
2026-11-14 10:47 ` [PATCH 06/12] loop: take freeze protection for discard and write zeroes Christian Brauner
@ 2026-11-14 10:47 ` Christian Brauner
2026-11-14 10:47 ` [PATCH 08/12] target: fileio: take freeze protection for unmap Christian Brauner
` (5 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Christian Brauner @ 2026-11-14 10:47 UTC (permalink / raw)
To: Jan Kara
Cc: Shakeel Butt, Alexander Viro, Jens Axboe, linux-fsdevel,
linux-kernel, io-uring, Christoph Hellwig, linux-block,
linux-nvme, linux-scsi, target-devel, linux-unionfs, bpf,
linux-nfs, Christian Brauner (Amutable), stable
With discard_write_cache a flush records the write pointer of each
active zone in an xattr of its zone file. zloop_record_safe_wps() uses
vfs_setxattr() for that which leaves freeze protection to the caller.
So a flush rewrites the xattrs on a filesystem that is frozen. With the
zone files on ext4:
WARNING: fs/ext4/ext4_jbd2.c:90 at ext4_journal_check_start+0x1f6/0x2d0, CPU#0: kworker/u16:2/46
Workqueue: zloop0 zloop_cmd_workfn
Call Trace:
<TASK>
__ext4_journal_start_sb+0x7a/0x470
ext4_xattr_set+0x20d/0x380
__vfs_setxattr+0x10f/0x170
__vfs_setxattr_noperm+0xf4/0x5e0
vfs_setxattr+0x14d/0x3d0
zloop_cmd_workfn+0x384/0x1ff0
process_one_work+0x946/0x1820
worker_thread+0x4f1/0xd60
kthread+0x364/0x460
ret_from_fork+0x525/0x770
ret_from_fork_asm+0x1a/0x30
</TASK>
Take freeze protection around vfs_setxattr() with mnt_want_write_file()
like fsetxattr() does.
Fixes: 829def1e35ca ("zloop: forget write cache on force removal")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
drivers/block/zloop.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/drivers/block/zloop.c b/drivers/block/zloop.c
index 34ed925e86f1a..a695aa8405d22 100644
--- a/drivers/block/zloop.c
+++ b/drivers/block/zloop.c
@@ -730,8 +730,13 @@ static int zloop_record_safe_wps(struct zloop_device *zlo)
if (!zloop_zone_is_active(zone))
continue;
- ret = vfs_setxattr(file_mnt_idmap(file), file_dentry(file),
- "user.zloop.wp", &zone->wp, sizeof(zone->wp), 0);
+ ret = mnt_want_write_file(file);
+ if (!ret) {
+ ret = vfs_setxattr(file_mnt_idmap(file),
+ file_dentry(file), "user.zloop.wp",
+ &zone->wp, sizeof(zone->wp), 0);
+ mnt_drop_write_file(file);
+ }
if (ret) {
pr_err("%pg: failed to record write pointer (%d)\n",
zlo->disk->part0, ret);
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH 08/12] target: fileio: take freeze protection for unmap
2026-11-14 10:47 [PATCH 00/12] fs: take freeze protection for writes to backing files Christian Brauner
` (6 preceding siblings ...)
2026-11-14 10:47 ` [PATCH 07/12] zloop: take freeze protection when recording write pointers Christian Brauner
@ 2026-11-14 10:47 ` Christian Brauner
2026-11-14 10:47 ` [PATCH 09/12] coredump: take freeze protection for the truncate Christian Brauner
` (4 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Christian Brauner @ 2026-11-14 10:47 UTC (permalink / raw)
To: Jan Kara
Cc: Shakeel Butt, Alexander Viro, Jens Axboe, linux-fsdevel,
linux-kernel, io-uring, Christoph Hellwig, linux-block,
linux-nvme, linux-scsi, target-devel, linux-unionfs, bpf,
linux-nfs, Christian Brauner (Amutable), stable
UNMAP and WRITE SAME with UNMAP=1 end up in fd_execute_unmap() which
calls ->fallocate() of the backing file directly. That skips the freeze
protection vfs_fallocate() would have taken. So they go straight
through to a filesystem that is frozen, with and without fd_async_io.
With the backing file on ext4:
WARNING: fs/ext4/ext4_jbd2.c:90 at ext4_journal_check_start+0x1f6/0x2d0, CPU#1: kworker/1:1/50
Workqueue: target_submission target_queued_submit_work
Call Trace:
<TASK>
__ext4_journal_start_sb+0x7a/0x470
ext4_dirty_inode+0xa1/0x120
__mark_inode_dirty+0x154/0x1110
file_update_time_flags+0x3ab/0x4b0
ext4_fallocate+0x286/0x3440
fd_execute_unmap+0x1eb/0x6e0
sbc_execute_write_same_unmap+0x93/0xe0
__target_execute_cmd+0x53/0x190
tcm_loop_write_pending+0xe/0x20
transport_generic_new_cmd+0x612/0xdb0
__target_submit+0x12c/0x380
target_queued_submit_work+0x73/0x270
process_one_work+0x946/0x1820
worker_thread+0x4f1/0xd60
kthread+0x364/0x460
ret_from_fork+0x525/0x770
ret_from_fork_asm+0x1a/0x30
</TASK>
Take freeze protection around ->fallocate() like vfs_fallocate() does.
Fixes: 70d3ae5c8075 ("target/file: Add WRITE_SAME w/ UNMAP=1 emulation support")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
drivers/target/target_core_file.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c
index 27b266a092b18..3fe4035b22a94 100644
--- a/drivers/target/target_core_file.c
+++ b/drivers/target/target_core_file.c
@@ -596,7 +596,9 @@ fd_execute_unmap(struct se_cmd *cmd, sector_t lba, sector_t nolb)
if (!file->f_op->fallocate)
return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ file_start_write(file);
ret = file->f_op->fallocate(file, mode, pos, len);
+ file_end_write(file);
if (ret < 0) {
pr_warn("FILEIO: fallocate() failed: %d\n", ret);
return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH 09/12] coredump: take freeze protection for the truncate
2026-11-14 10:47 [PATCH 00/12] fs: take freeze protection for writes to backing files Christian Brauner
` (7 preceding siblings ...)
2026-11-14 10:47 ` [PATCH 08/12] target: fileio: take freeze protection for unmap Christian Brauner
@ 2026-11-14 10:47 ` Christian Brauner
2026-11-14 10:47 ` [PATCH 10/12] ovl: take freeze protection when storing the uuid Christian Brauner
` (3 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Christian Brauner @ 2026-11-14 10:47 UTC (permalink / raw)
To: Jan Kara
Cc: Shakeel Butt, Alexander Viro, Jens Axboe, linux-fsdevel,
linux-kernel, io-uring, Christoph Hellwig, linux-block,
linux-nvme, linux-scsi, target-devel, linux-unionfs, bpf,
linux-nfs, Christian Brauner (Amutable), stable
coredump_file() truncates the core file after filp_open() returned.
The open took freeze protection for the create but dropped it again
before returning and coredump_write() only takes it for the dump
itself. So a freeze that lands in between lets the truncate modify a
frozen filesystem.
Take freeze protection around the truncate like do_ftruncate() does.
Fixes: 14da9200140f ("fs: Protect write paths by sb_start_write - sb_end_write")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
fs/coredump.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/fs/coredump.c b/fs/coredump.c
index 82ef9d115999a..cc6a0c7e48ce2 100644
--- a/fs/coredump.c
+++ b/fs/coredump.c
@@ -1060,8 +1060,10 @@ static bool coredump_file(struct core_name *cn, struct coredump_params *cprm,
}
if (!(file->f_mode & FMODE_CAN_WRITE))
return false;
- if (do_truncate(idmap, file->f_path.dentry, 0, 0, file))
- return false;
+ scoped_guard(super_write, inode->i_sb) {
+ if (do_truncate(idmap, file->f_path.dentry, 0, 0, file))
+ return false;
+ }
cprm->file = no_free_ptr(file);
return true;
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH 10/12] ovl: take freeze protection when storing the uuid
2026-11-14 10:47 [PATCH 00/12] fs: take freeze protection for writes to backing files Christian Brauner
` (8 preceding siblings ...)
2026-11-14 10:47 ` [PATCH 09/12] coredump: take freeze protection for the truncate Christian Brauner
@ 2026-11-14 10:47 ` Christian Brauner
2026-11-14 12:19 ` Amir Goldstein
2026-11-14 10:47 ` [PATCH 11/12] bpf: fs/xattr: take freeze protection in the xattr kfuncs Christian Brauner
` (2 subsequent siblings)
12 siblings, 1 reply; 15+ messages in thread
From: Christian Brauner @ 2026-11-14 10:47 UTC (permalink / raw)
To: Jan Kara
Cc: Shakeel Butt, Alexander Viro, Jens Axboe, linux-fsdevel,
linux-kernel, io-uring, Christoph Hellwig, linux-block,
linux-nvme, linux-scsi, target-devel, linux-unionfs, bpf,
linux-nfs, Christian Brauner (Amutable), stable
ovl_init_uuid_xattr() stores the persistent uuid in an xattr on the
upper root. That happens after ovl_make_workdir() dropped its write
access to the upper mount and before ovl_get_indexdir() takes it
again. So a freeze of the upper filesystem that lands in between lets
the mount modify a frozen filesystem.
Take write access to the upper mount around the xattr update.
Fixes: d9544c1b0d9e ("ovl: store persistent uuid/fsid with uuid=on")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
fs/overlayfs/util.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c
index 521717209b2e4..67aa7b71e9c83 100644
--- a/fs/overlayfs/util.c
+++ b/fs/overlayfs/util.c
@@ -829,8 +829,12 @@ bool ovl_init_uuid_xattr(struct super_block *sb, struct ovl_fs *ofs,
/* Try to store persistent uuid */
set = true;
- res = ovl_setxattr(ofs, upperpath->dentry, OVL_XATTR_UUID, uuid.b,
- UUID_SIZE);
+ res = mnt_want_write(ovl_upper_mnt(ofs));
+ if (!res) {
+ res = ovl_setxattr(ofs, upperpath->dentry, OVL_XATTR_UUID,
+ uuid.b, UUID_SIZE);
+ mnt_drop_write(ovl_upper_mnt(ofs));
+ }
if (res)
goto fail;
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PATCH 10/12] ovl: take freeze protection when storing the uuid
2026-11-14 10:47 ` [PATCH 10/12] ovl: take freeze protection when storing the uuid Christian Brauner
@ 2026-11-14 12:19 ` Amir Goldstein
0 siblings, 0 replies; 15+ messages in thread
From: Amir Goldstein @ 2026-11-14 12:19 UTC (permalink / raw)
To: Christian Brauner
Cc: Jan Kara, Shakeel Butt, Alexander Viro, Jens Axboe, linux-fsdevel,
linux-kernel, io-uring, Christoph Hellwig, linux-block,
linux-nvme, linux-scsi, target-devel, linux-unionfs, bpf,
linux-nfs, stable
On Sat, Nov 14, 2026 at 11:47:17AM +0100, Christian Brauner wrote:
> ovl_init_uuid_xattr() stores the persistent uuid in an xattr on the
> upper root. That happens after ovl_make_workdir() dropped its write
> access to the upper mount and before ovl_get_indexdir() takes it
> again. So a freeze of the upper filesystem that lands in between lets
> the mount modify a frozen filesystem.
>
> Take write access to the upper mount around the xattr update.
>
> Fixes: d9544c1b0d9e ("ovl: store persistent uuid/fsid with uuid=on")
> Cc: stable@vger.kernel.org
> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Thanks,
Amir.
> ---
> fs/overlayfs/util.c | 8 ++++++--
> 1 file changed, 6 insertions(+), 2 deletions(-)
>
> diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c
> index 521717209b2e4..67aa7b71e9c83 100644
> --- a/fs/overlayfs/util.c
> +++ b/fs/overlayfs/util.c
> @@ -829,8 +829,12 @@ bool ovl_init_uuid_xattr(struct super_block *sb, struct ovl_fs *ofs,
>
> /* Try to store persistent uuid */
> set = true;
> - res = ovl_setxattr(ofs, upperpath->dentry, OVL_XATTR_UUID, uuid.b,
> - UUID_SIZE);
> + res = mnt_want_write(ovl_upper_mnt(ofs));
> + if (!res) {
> + res = ovl_setxattr(ofs, upperpath->dentry, OVL_XATTR_UUID,
> + uuid.b, UUID_SIZE);
> + mnt_drop_write(ovl_upper_mnt(ofs));
> + }
> if (res)
> goto fail;
>
>
> --
> 2.53.0
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 11/12] bpf: fs/xattr: take freeze protection in the xattr kfuncs
2026-11-14 10:47 [PATCH 00/12] fs: take freeze protection for writes to backing files Christian Brauner
` (9 preceding siblings ...)
2026-11-14 10:47 ` [PATCH 10/12] ovl: take freeze protection when storing the uuid Christian Brauner
@ 2026-11-14 10:47 ` Christian Brauner
2026-11-14 10:47 ` [PATCH 12/12] nfsd: take freeze protection for the truncate on open Christian Brauner
2026-11-14 12:31 ` [PATCH 00/12] fs: take freeze protection for writes to backing files Amir Goldstein
12 siblings, 0 replies; 15+ messages in thread
From: Christian Brauner @ 2026-11-14 10:47 UTC (permalink / raw)
To: Jan Kara
Cc: Shakeel Butt, Alexander Viro, Jens Axboe, linux-fsdevel,
linux-kernel, io-uring, Christoph Hellwig, linux-block,
linux-nvme, linux-scsi, target-devel, linux-unionfs, bpf,
linux-nfs, Christian Brauner (Amutable), stable
bpf_set_dentry_xattr() and bpf_remove_dentry_xattr() go through
__vfs_setxattr() and __vfs_removexattr() which leave freeze protection
to the caller. BPF LSM programs call them from sleepable hooks such as
file_open where nobody holds it. So a program can modify a frozen
filesystem.
The hook may already hold freeze protection or locks a frozen
filesystem would make us wait under. So don't wait for a thaw. Fail
with -EBUSY if the filesystem is frozen.
The _locked variants are only used from hooks that run inside setattr,
setxattr, removexattr, unlink and rmdir. Their callers hold freeze
protection already.
Fixes: 56467292794b ("bpf: fs/xattr: Add BPF kfuncs to set and remove xattrs")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
fs/bpf_fs_kfuncs.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/fs/bpf_fs_kfuncs.c b/fs/bpf_fs_kfuncs.c
index 35ccd98a4de64..7f61e2efa08ba 100644
--- a/fs/bpf_fs_kfuncs.c
+++ b/fs/bpf_fs_kfuncs.c
@@ -295,9 +295,13 @@ __bpf_kfunc int bpf_set_dentry_xattr(struct dentry *dentry, const char *name__st
if (!inode)
return -EINVAL;
+ /* The hook may hold locks or freeze protection already. */
+ if (!sb_start_write_trylock(inode->i_sb))
+ return -EBUSY;
inode_lock(inode);
ret = bpf_set_dentry_xattr_locked(dentry, name__str, value_p, flags);
inode_unlock(inode);
+ sb_end_write(inode->i_sb);
return ret;
}
@@ -323,9 +327,13 @@ __bpf_kfunc int bpf_remove_dentry_xattr(struct dentry *dentry, const char *name_
if (!inode)
return -EINVAL;
+ /* The hook may hold locks or freeze protection already. */
+ if (!sb_start_write_trylock(inode->i_sb))
+ return -EBUSY;
inode_lock(inode);
ret = bpf_remove_dentry_xattr_locked(dentry, name__str);
inode_unlock(inode);
+ sb_end_write(inode->i_sb);
return ret;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH 12/12] nfsd: take freeze protection for the truncate on open
2026-11-14 10:47 [PATCH 00/12] fs: take freeze protection for writes to backing files Christian Brauner
` (10 preceding siblings ...)
2026-11-14 10:47 ` [PATCH 11/12] bpf: fs/xattr: take freeze protection in the xattr kfuncs Christian Brauner
@ 2026-11-14 10:47 ` Christian Brauner
2026-11-14 12:31 ` [PATCH 00/12] fs: take freeze protection for writes to backing files Amir Goldstein
12 siblings, 0 replies; 15+ messages in thread
From: Christian Brauner @ 2026-11-14 10:47 UTC (permalink / raw)
To: Jan Kara
Cc: Shakeel Butt, Alexander Viro, Jens Axboe, linux-fsdevel,
linux-kernel, io-uring, Christoph Hellwig, linux-block,
linux-nvme, linux-scsi, target-devel, linux-unionfs, bpf,
linux-nfs, Christian Brauner (Amutable), stable
An OPEN that truncates an existing file ends up in nfsd4_truncate()
which calls nfsd_setattr() on the already verified file handle. For a
verified file handle nfsd_setattr() leaves fh_want_write() to the
caller but nfsd4_truncate() never takes it, and do_lookup_open() has
dropped its own write access by then. So a freeze of the exported
filesystem that lands in between lets the truncate modify a frozen
filesystem.
Take write access around nfsd_setattr() like nfsd4_setattr() does.
Fixes: 9f67f189939e ("nfsd: notify_change needs elevated write count")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
fs/nfsd/nfs4state.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 1de6c6d757c3e..e561903b9180f 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -6681,11 +6681,19 @@ nfsd4_truncate(struct svc_rqst *rqstp, struct svc_fh *fh,
struct nfsd_attrs attrs = {
.na_iattr = &iattr,
};
+ __be32 status;
+ int err;
+
if (!open->op_truncate)
return 0;
if (!(open->op_share_access & NFS4_SHARE_ACCESS_WRITE))
return nfserr_inval;
- return nfsd_setattr(rqstp, fh, &attrs, NULL);
+ err = fh_want_write(fh);
+ if (err)
+ return nfserrno(err);
+ status = nfsd_setattr(rqstp, fh, &attrs, NULL);
+ fh_drop_write(fh);
+ return status;
}
static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp,
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PATCH 00/12] fs: take freeze protection for writes to backing files
2026-11-14 10:47 [PATCH 00/12] fs: take freeze protection for writes to backing files Christian Brauner
` (11 preceding siblings ...)
2026-11-14 10:47 ` [PATCH 12/12] nfsd: take freeze protection for the truncate on open Christian Brauner
@ 2026-11-14 12:31 ` Amir Goldstein
12 siblings, 0 replies; 15+ messages in thread
From: Amir Goldstein @ 2026-11-14 12:31 UTC (permalink / raw)
To: Christian Brauner
Cc: Jan Kara, Shakeel Butt, Alexander Viro, Jens Axboe, linux-fsdevel,
linux-kernel, io-uring, Christoph Hellwig, linux-block,
linux-nvme, linux-scsi, target-devel, linux-unionfs, bpf,
linux-nfs, stable
On Sat, Nov 14, 2026 at 11:47:07AM +0100, Christian Brauner wrote:
> While testing the pseudo filesystem series I noticed that zloop, the
> nvme target's file backend and the fileio backend of the scsi target
> with fd_async_io=1 call ->write_iter() of their backing files without
> taking freeze protection. So their writes go straight through to a
> frozen filesystem. ext4 complains loudly:
>
> WARNING: fs/ext4/ext4_jbd2.c:90 at ext4_journal_check_start+0x1f6/0x2d0
>
> Loop doesn't get it right for discard and write zeroes. Those end up
> calling ->fallocate() of the backing file directly. The fileio backend
> does the same for UNMAP. And zloop records its write pointers on flush
> with vfs_setxattr() which leaves freeze protection to the caller. So all
> of them modify a frozen filesystem just the same. Fix them too.
>
> Looking for more of the same turned up a few simple ones. coredump
> truncates the core file, overlayfs stores its uuid xattr on mount, nfsd
> truncates on OPEN and the BPF xattr kfuncs modify xattrs, all without
> freeze protection. Fix those as well. The ones that need more work in
> their subsystems I have reported separately.
>
> The fileio backend also frees its aio command in the completion handler
> while the filesystem may still be using the kiocb. KASAN catches ext4
> doing that for direct reads on v7.4-rc1. ext4 gets its own fix (cf. [1])
> but fileio should hold a reference across the submission like loop,
> zloop and fs/backing-file.c do.
>
> [1]: https://lore.kernel.org/r/20261113-work-ext4-dio_read_kiocb-v1-1-172a88be803c@kernel.org
>
Claude says you missed another one.
nfs_local_write_iocb_done() does not end_write
its ended too early right after submission.
Thanks,
Amir.
> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
> ---
> Christian Brauner (12):
> fs: add kiocb_start_write_trylock()
> zloop: take freeze protection for writes
> nvmet: take freeze protection for writes to files
> target: fileio: don't free the aio command under the filesystem
> target: fileio: take freeze protection for aio writes
> loop: take freeze protection for discard and write zeroes
> zloop: take freeze protection when recording write pointers
> target: fileio: take freeze protection for unmap
> coredump: take freeze protection for the truncate
> ovl: take freeze protection when storing the uuid
> bpf: fs/xattr: take freeze protection in the xattr kfuncs
> nfsd: take freeze protection for the truncate on open
>
> drivers/block/loop.c | 2 ++
> drivers/block/zloop.c | 31 ++++++++++++++++++++-------
> drivers/nvme/target/io-cmd-file.c | 37 +++++++++++++++++++++++++-------
> drivers/target/target_core_file.c | 44 ++++++++++++++++++++++++++++++++-------
> fs/bpf_fs_kfuncs.c | 8 +++++++
> fs/coredump.c | 6 ++++--
> fs/nfsd/nfs4state.c | 10 ++++++++-
> fs/overlayfs/util.c | 8 +++++--
> include/linux/fs.h | 22 ++++++++++++++++++++
> io_uring/rw.c | 9 +-------
> 10 files changed, 142 insertions(+), 35 deletions(-)
> ---
> base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
> change-id: 20261113-work-fs-frozen_writes-042ca762bfb8
>
^ permalink raw reply [flat|nested] 15+ messages in thread