From af9b01d23006a365488b1be31d01afa2cce977b0 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Wed, 21 May 2025 15:35:26 -0700 Subject: [PATCH] libext2fs: fix livelock in the unix io manager generic/441 found a livelock in the unix IO manager. Let's say that write_primary_superblock decides to call io_channel_set_blksize in the process of writing the primary super. unix_set_blksize then takes the cache and bounce mutexes, and calls flush_cached_blocks. If there are dirty blocks in the cache, they will be written with raw_write_blk. Unfortunately, that function tries to take the bounce mutex, which we already hold. At that point, we livelock fuse2fs. Cc: linux-ext4@vger.kernel.org # v1.46.0 Fixes: f20627cc639ab6 ("libext2fs: add threading support to the I/O manager abstraction") Signed-off-by: Darrick J. Wong Link: https://lore.kernel.org/r/174786677584.1383760.1107858755678329558.stgit@frogsfrogsfrogs Signed-off-by: Theodore Ts'o --- lib/ext2fs/unix_io.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/lib/ext2fs/unix_io.c b/lib/ext2fs/unix_io.c index 0ab9de6..c9736f6 100644 --- a/lib/ext2fs/unix_io.c +++ b/lib/ext2fs/unix_io.c @@ -344,7 +344,8 @@ error_unlock: return retval; } -#define RAW_WRITE_NO_HANDLER 1 +#define RAW_WRITE_NO_HANDLER (1U << 0) +#define RAW_WRITE_NOLOCK (1U << 1) static errcode_t raw_write_blk(io_channel channel, struct unix_private_data *data, @@ -404,13 +405,15 @@ static errcode_t raw_write_blk(io_channel channel, (IS_ALIGNED(buf, channel->align) && IS_ALIGNED(location, channel->align) && IS_ALIGNED(size, channel->align))) { - mutex_lock(data, BOUNCE_MTX); + if (!(flags & RAW_WRITE_NOLOCK)) + mutex_lock(data, BOUNCE_MTX); if (ext2fs_llseek(data->dev, location, SEEK_SET) < 0) { retval = errno ? errno : EXT2_ET_LLSEEK_FAILED; goto error_unlock; } actual = write(data->dev, buf, size); - mutex_unlock(data, BOUNCE_MTX); + if (!(flags & RAW_WRITE_NOLOCK)) + mutex_unlock(data, BOUNCE_MTX); if (actual < 0) { retval = errno; goto error_out; @@ -445,7 +448,8 @@ bounce_write: while (size > 0) { int actual_w; - mutex_lock(data, BOUNCE_MTX); + if (!(flags & RAW_WRITE_NOLOCK)) + mutex_lock(data, BOUNCE_MTX); if (size < align_size || offset) { if (ext2fs_llseek(data->dev, aligned_blk * align_size, SEEK_SET) < 0) { @@ -474,7 +478,8 @@ bounce_write: goto error_unlock; } actual_w = write(data->dev, data->bounce, align_size); - mutex_unlock(data, BOUNCE_MTX); + if (!(flags & RAW_WRITE_NOLOCK)) + mutex_unlock(data, BOUNCE_MTX); if (actual_w < 0) { retval = errno; goto error_out; @@ -490,7 +495,8 @@ bounce_write: return 0; error_unlock: - mutex_unlock(data, BOUNCE_MTX); + if (!(flags & RAW_WRITE_NOLOCK)) + mutex_unlock(data, BOUNCE_MTX); error_out: if (((flags & RAW_WRITE_NO_HANDLER) == 0) && channel->write_error) retval = (channel->write_error)(channel, block, count, buf, @@ -673,9 +679,14 @@ static errcode_t flush_cached_blocks(io_channel channel, if (!cache->in_use) continue; if (cache->dirty) { + int raw_flags = RAW_WRITE_NO_HANDLER; + + if (flags & FLUSH_NOLOCK) + raw_flags |= RAW_WRITE_NOLOCK; + retval = raw_write_blk(channel, data, cache->block, 1, cache->buf, - RAW_WRITE_NO_HANDLER); + raw_flags); if (retval) { cache->write_err = 1; errors_found = 1; -- 1.8.3.1