scst_vdisk: Use filesystem synchronization for fileio flushes

Writing and waiting for dirty pages does not guarantee that the data can
be recovered after a crash. For example, a write into a sparse backing
file can also require an allocation or journal update, and the backing
device can still hold the data in a volatile write cache. A successful
FUA or SYNCHRONIZE CACHE must not acknowledge only page writeback.

The 2009 Linux 2.6.32 port replaced sync_page_range(), which also performed
metadata synchronization, with filemap_write_and_wait_range(). The
alternative generic_write_sync() was left disabled; it would also be
insufficient for an ordinary write-back file because it checks O_DSYNC.

Use vfs_fsync_range() with datasync enabled to let the filesystem persist
the data and the metadata needed to retrieve it, including any required
backend flush. Keep the existing range, DIF-tag handling and SCSI error
reporting. The API is available in all kernels in the nightly matrix.

Remove the unused writeback test boundary and its stub. The private file
operations now record filesystem synchronization directly, so a missing
fsync still fails the existing flush-count assertions.

Fixes: 2b5833d90e ("Update for 2.6.32 + fix context of vdisk_t10_dev_id_rwlock")
Link: https://github.com/SCST-project/scst/issues/395
This commit is contained in:
Gleb Chesnokov
2026-10-14 17:39:14 +03:00
parent ce9819b847
commit 6d4751538b
4 changed files with 3 additions and 38 deletions
@@ -52,8 +52,7 @@ static ssize_t vdisk_test_read_iter(struct kiocb *iocb, struct iov_iter *iter)
return vdisk_test_iter(iocb, iter, false);
}
static int vdisk_test_record_sync(struct file *file, loff_t start, loff_t end,
enum vdisk_test_sync_op op)
static int vdisk_test_fsync(struct file *file, loff_t start, loff_t end, int datasync)
{
struct vdisk_test_file *backend = file->private_data;
struct vdisk_test_fixture *f = backend->fixture;
@@ -64,24 +63,11 @@ static int vdisk_test_record_sync(struct file *file, loff_t start, loff_t end,
return -EIO;
}
f->flushes[n] = (struct vdisk_test_flush) {
.file = file, .start = start, .end = end, .op = op,
.file = file, .start = start, .end = end,
};
return backend->sync_result;
}
static int vdisk_test_fsync(struct file *file, loff_t start, loff_t end, int datasync)
{
/* Both fsync and fdatasync satisfy the persistence requirement. */
return vdisk_test_record_sync(file, start, end, VDISK_TEST_FSYNC);
}
static int vdisk_test_writeback(struct address_space *mapping, loff_t start, loff_t end)
{
struct vdisk_test_file *backend = container_of(mapping, struct vdisk_test_file, mapping);
return vdisk_test_record_sync(&backend->file, start, end, VDISK_TEST_WRITEBACK);
}
static const struct file_operations vdisk_test_fops = {
.write_iter = vdisk_test_write_iter,
.read_iter = vdisk_test_read_iter,
@@ -184,7 +170,6 @@ int vdisk_test_init(struct kunit *test)
f->cmd.sg = f->sg;
f->cmd.sg_cnt = ARRAY_SIZE(f->sg);
kunit_activate_static_stub(test, vdisk_blockio_flush, vdisk_test_block_flush);
kunit_activate_static_stub(test, vdisk_writeback_range, vdisk_test_writeback);
return 0;
}
@@ -211,6 +196,4 @@ void vdisk_test_expect_flush(struct kunit *test, unsigned int n, struct file *fi
KUNIT_EXPECT_PTR_EQ(test, f->flushes[n].file, file);
KUNIT_EXPECT_EQ(test, f->flushes[n].start, start);
KUNIT_EXPECT_EQ(test, f->flushes[n].end, end);
KUNIT_EXPECT_EQ_MSG(test, f->flushes[n].op, VDISK_TEST_FSYNC,
"writeback alone does not provide filesystem synchronization");
}
@@ -16,16 +16,10 @@ struct vdisk_test_file {
int sync_result;
};
enum vdisk_test_sync_op {
VDISK_TEST_WRITEBACK,
VDISK_TEST_FSYNC,
};
struct vdisk_test_flush {
struct file *file;
loff_t start;
loff_t end;
enum vdisk_test_sync_op op;
};
struct vdisk_test_fixture {
+1 -12
View File
@@ -1386,13 +1386,6 @@ static void vdisk_detach_tgt(struct scst_tgt_dev *tgt_dev)
TRACE_EXIT();
}
SCST_KUNIT_VISIBLE
int vdisk_writeback_range(struct address_space *mapping, loff_t start, loff_t end)
{
SCST_KUNIT_REDIRECT(vdisk_writeback_range, mapping, start, end);
return filemap_write_and_wait_range(mapping, start, end);
}
SCST_KUNIT_VISIBLE
int __vdisk_fsync_fileio(loff_t loff, loff_t len, struct scst_device *dev,
struct scst_cmd *cmd, struct file *file)
@@ -1411,11 +1404,7 @@ int __vdisk_fsync_fileio(loff_t loff, loff_t len, struct scst_device *dev,
if (!len)
goto out;
#if 0 /* For sparse files we might need to sync metadata as well */
res = generic_write_sync(file, loff, len);
#else
res = vdisk_writeback_range(file->f_mapping, loff, loff + len - 1);
#endif
res = vfs_fsync_range(file, loff, loff + len - 1, 1);
if (unlikely(res != 0)) {
PRINT_ERROR("sync range failed (%d)", res);
if (cmd) {
-1
View File
@@ -10,7 +10,6 @@ struct vdisk_cmd_params;
int vdisk_blockio_flush(struct block_device *bdev, gfp_t gfp_mask, bool report_error,
struct scst_cmd *cmd, bool async);
int vdisk_writeback_range(struct address_space *mapping, loff_t start, loff_t end);
int __vdisk_fsync_fileio(loff_t loff, loff_t len, struct scst_device *dev,
struct scst_cmd *cmd, struct file *file);
int vdisk_fsync(loff_t loff, loff_t len, struct scst_device *dev, gfp_t gfp_flags,