[PATCH 02/29] libext2fs: fix livelock in the unix io manager

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Darrick J. Wong <djwong@xxxxxxxxxx>

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@xxxxxxxxxxxxxxx> # v1.46.0
Fixes: f20627cc639ab6 ("libext2fs: add threading support to the I/O manager abstraction")
Signed-off-by: "Darrick J. Wong" <djwong@xxxxxxxxxx>
---
 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 b98c44a84bb0af..be70fee38890c8 100644
--- a/lib/ext2fs/unix_io.c
+++ b/lib/ext2fs/unix_io.c
@@ -344,7 +344,8 @@ static errcode_t raw_read_blk(io_channel channel,
 	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 @@ static errcode_t raw_write_blk(io_channel channel,
 	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 @@ static errcode_t raw_write_blk(io_channel channel,
 			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 @@ static errcode_t raw_write_blk(io_channel channel,
 	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;





[Index of Archives]     [Reiser Filesystem Development]     [Ceph FS]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux FS]     [Yosemite National Park]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Device Mapper]     [Linux Media]

  Powered by Linux