[RH7,4/4] ploop: io_kaio: Protect ->fastmap against EXT4_IOC_MOVE_EXT and punch hole

Submitted by Kirill Tkhai on Aug. 6, 2020, 11:55 a.m.

Details

Message ID 159671494429.36238.1379278173608724775.stgit@localhost.localdomain
State New
Series "ploop: Protect fastmap against defrag and punch hole"
Headers show

Commit Message

Kirill Tkhai Aug. 6, 2020, 11:55 a.m.
This takes ->i_dio_count during fastmap submitted IO,
so defrag and punch hole can't make this extent disappeared.

Here are protection from ext4's .read_iter and .write_iter,
a littly bit simplified (truncate() is still not allowed).

Signed-off-by: Kirill Tkhai <ktkhai@virtuozzo.com>
---
 drivers/block/ploop/io_kaio.c |   19 +++++++++++++++++--
 fs/ext4/file.c                |   41 +++++++++++++++++++++++++++++++++++++++--
 2 files changed, 56 insertions(+), 4 deletions(-)

Patch hide | download patch | download mbox

diff --git a/drivers/block/ploop/io_kaio.c b/drivers/block/ploop/io_kaio.c
index 9cbe923b14bf..36f4a6291c59 100644
--- a/drivers/block/ploop/io_kaio.c
+++ b/drivers/block/ploop/io_kaio.c
@@ -1229,7 +1229,7 @@  kaio_fastmap(struct ploop_io *io, struct bio *orig_bio,
 	q = bdev_get_queue(bio->bi_bdev);
 
 	if (q->merge_bvec_fn == NULL)
-		return 0;
+		goto out;
 
 	bio->bi_size = 0;
 	bio->bi_vcnt = 0;
@@ -1244,12 +1244,26 @@  kaio_fastmap(struct ploop_io *io, struct bio *orig_bio,
 		};
 		if (q->merge_bvec_fn(q, &bm_data, bv) < bv->bv_len) {
 			io->plo->st.fast_neg_backing++;
-			return 1;
+			goto err_end_io;
 		}
 		bio->bi_size += bv->bv_len;
 		bio->bi_vcnt++;
 	}
+out:
 	return 0;
+
+err_end_io:
+	io->ops->fastmap_end_io(io, orig_bio);
+	return 1;
+}
+
+static void kaio_fastmap_end_io(struct ploop_io *io, struct bio *orig_bio)
+{
+	struct inode *inode = io->files.inode;
+
+	if (orig_bio->bi_size == 0)
+		return;
+	inode_dio_end(inode);
 }
 
 static struct ploop_io_ops ploop_io_ops_kaio =
@@ -1288,6 +1302,7 @@  static struct ploop_io_ops ploop_io_ops_kaio =
 
 	.autodetect     =       kaio_autodetect,
 	.fastmap	=	kaio_fastmap,
+	.fastmap_end_io	=	kaio_fastmap_end_io,
 };
 
 static int __init pio_kaio_mod_init(void)
diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index 57a8cc2b8e3f..cc39aff54e03 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -128,21 +128,58 @@  static bool ext4_overwrite_io(struct inode *inode, loff_t pos, loff_t len)
 static int ext4_fastmap(struct inode *inode, sector_t lblk_sec,
 			unsigned int len, sector_t *pblk_sec, bool write)
 {
+	struct address_space *mapping = inode->i_mapping;
+	bool unaligned_aio, found, locked = false;
 	struct ext4_map_blocks map;
 	loff_t pos = lblk_sec << 9;
-	bool unaligned_aio, found;
+
+	if (!S_ISREG(inode->i_mode))
+		return -ENOENT;
+	if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)))
+		return -ENOENT;
+	if (ext4_should_journal_data(inode))
+		return -ENOENT;
 
 	unaligned_aio = ext4_unaligned_aio(inode, len, pos);
 	if (unaligned_aio)
 		return -ENOENT;
 
+	if (write) {
+		if (!mutex_trylock(&inode->i_mutex))
+			return -ENOENT;
+		locked = true;
+	}
+
+	if (unlikely(mapping_needs_writeback(mapping)))
+		goto err_maybe_unlock;
+
+	inode_dio_begin(inode);
+	smp_mb();
+
+	if (locked) {
+		mutex_unlock(&inode->i_mutex);
+		locked = false;
+	}
+
+	if (unlikely(ext4_test_inode_state(inode,
+				EXT4_STATE_DIOREAD_LOCK))) {
+		goto err_dio_end;
+	}
+
 	found = __ext4_overwrite_io(inode, lblk_sec << 9, len, &map,
 				    EXT4_GET_BLOCKS_EXTENT_TREE_ONLY_NONBLOCK);
 	if (!found)
-		return -ENOENT;
+		goto err_dio_end;
 
 	*pblk_sec = map.m_pblk << (inode->i_blkbits - 9);
 	return 0;
+
+err_dio_end:
+	inode_dio_end(inode);
+err_maybe_unlock:
+	if (locked)
+		mutex_unlock(&inode->i_mutex);
+	return -ENOENT;
 }
 
 static ssize_t ext4_write_checks(struct kiocb *iocb, struct iov_iter *iter, loff_t *pos)