[PATCH 03/10] libext2fs: only fsync the unix fd if we wrote to the device

Darrick J. Wong
2025-05-22T00:09:01Z

As an optimization, only fsync the block device fd if we tried to write
to the io channel.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
lib/ext2fs/unix_io.c | 48 ++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 42 insertions(+), 6 deletions(-)

diff --git a/lib/ext2fs/unix_io.c b/lib/ext2fs/unix_io.c
index 7c5cb075d6b6b6..0fc83e471ca0fe 100644
--- a/lib/ext2fs/unix_io.c
+++ b/lib/ext2fs/unix_io.c
@@ -129,10 +129,13 @@ struct unix_cache {
#define WRITE_DIRECT_SIZE 4 /* Must be smaller than CACHE_SIZE */
#define READ_DIRECT_SIZE 4 /* Should be smaller than CACHE_SIZE */

+#define UNIX_STATE_DIRTY (1U << 0) /* device needs fsyncing */
+
struct unix_private_data {
int magic;
int dev;
int flags;
+ unsigned int state; /* UNIX_STATE_* */
int align;
int access_time;
ext2_loff_t offset;
@@ -1121,10 +1124,37 @@ static errcode_t unix_open(const char *name, int flags,
return unix_open_channel(name, fd, flags, channel, unix_io_manager);
}

+static void mark_dirty(io_channel channel)
+{
+ struct unix_private_data *data =
+ (struct unix_private_data *) channel->private_data;
+
+ mutex_lock(data, CACHE_MTX);
+ data->state |= UNIX_STATE_DIRTY;
+ mutex_unlock(data, CACHE_MTX);
+}
+
+static errcode_t maybe_fsync(io_channel channel)
+{
+ struct unix_private_data *data =
+ (struct unix_private_data *) channel->private_data;
+ int was_dirty;
+
+ mutex_lock(data, CACHE_MTX);
+ was_dirty = data->state & UNIX_STATE_DIRTY;
+ data->state &= ~UNIX_STATE_DIRTY;
+ mutex_unlock(data, CACHE_MTX);
+
+ if (was_dirty && fsync(data->dev) != 0)
+ return errno;
+
+ return 0;
+}
+
static errcode_t unix_close(io_channel channel)
{
struct unix_private_data *data;
- errcode_t retval = 0;
+ errcode_t retval = 0, retval2;

EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
data = (struct unix_private_data *) channel->private_data;
@@ -1137,8 +1167,9 @@ static errcode_t unix_close(io_channel channel)
retval = flush_cached_blocks(channel, data, 0);
#endif
/* always fsync the device, even if flushing our own cache failed */
- if (fsync(data->dev) != 0 && !retval)
- retval = errno;
+ retval2 = maybe_fsync(channel);
+ if (retval2 && !retval)
+ retval = retval2;

if (close(data->dev) < 0 && !retval)
retval = errno;
@@ -1306,6 +1337,8 @@ static errcode_t unix_write_blk64(io_channel channel, unsigned long long block,
data = (struct unix_private_data *) channel->private_data;
EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);

+ mark_dirty(channel);
+
#ifdef NO_IO_CACHE
return raw_write_blk(channel, data, block, count, buf, 0);
#else
@@ -1430,6 +1463,8 @@ static errcode_t unix_write_byte(io_channel channel, unsigned long offset,
if (lseek(data->dev, offset + data->offset, SEEK_SET) < 0)
return errno;

+ mark_dirty(channel);
+
actual = write(data->dev, buf, size);
if (actual < 0)
return errno;
@@ -1445,7 +1480,7 @@ static errcode_t unix_write_byte(io_channel channel, unsigned long offset,
static errcode_t unix_flush(io_channel channel)
{
struct unix_private_data *data;
- errcode_t retval = 0;
+ errcode_t retval = 0, retval2;

EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
data = (struct unix_private_data *) channel->private_data;
@@ -1456,8 +1491,9 @@ static errcode_t unix_flush(io_channel channel)
#endif
#ifdef HAVE_FSYNC
/* always fsync the device, even if flushing our own cache failed */
- if (fsync(data->dev) != 0 && !retval)
- return errno;
+ retval2 = maybe_fsync(channel);
+ if (retval2 && !retval)
+ retval = retval2;
#endif
return retval;
}