When dioread_nolock and delay_alloc are both enaled, the bio_endio() will trigger ext4-rsv-conversion work queue to do ext4_do_flush_completed_IO(). The current work queue is one-by-one updating for EXT4_IO_END_UNWRITTEN extend block at io_end->list_vec which added by ext4_writepages(). So if the BIO has high performance, and only one thread to do EXT4 flush will be an bottleneck. So we simple allow more thread and with a semaphore protection, since the "ext4-rsv-conversion" this workqueue is only for updating the EXT4_IO_END_UNWRITTEN extend block(only exist on dioread_unlock and delay_alloc options are set). --- fs/ext4/ext4.h | 1 + fs/ext4/extents.c | 2 ++ fs/ext4/super.c | 3 ++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 0a2d55faa095..15d8d7a1810e 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1134,6 +1134,7 @@ struct ext4_inode_info { atomic_t i_unwritten; /* Nr. of inflight conversions pending */ spinlock_t i_block_reservation_lock; + struct rw_semaphore i_rsv_unwritten_sem; /* * Transactions that contain inode's metadata needed to complete diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index e4115d338f10..dbd3f69853cf 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -4807,6 +4807,7 @@ int ext4_convert_unwritten_extents(handle_t *handle, struct inode *inode, break; } } + down_write(&EXT4_I(inode)->i_rsv_unwritten_sem); ret = ext4_map_blocks(handle, inode, &map, EXT4_GET_BLOCKS_IO_CONVERT_EXT); if (ret <= 0) @@ -4815,6 +4816,7 @@ int ext4_convert_unwritten_extents(handle_t *handle, struct inode *inode, "ext4_ext_map_blocks returned %d", inode->i_ino, map.m_lblk, map.m_len, ret); + up_write(&EXT4_I(inode)->i_rsv_unwritten_sem); ret2 = ext4_mark_inode_dirty(handle, inode); if (credits) { ret3 = ext4_journal_stop(handle); diff --git a/fs/ext4/super.c b/fs/ext4/super.c index c94ebf704616..af2af5173424 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -1466,6 +1466,7 @@ static void init_once(void *foo) INIT_LIST_HEAD(&ei->i_orphan); init_rwsem(&ei->xattr_sem); init_rwsem(&ei->i_data_sem); + init_rwsem(&ei->i_rsv_unwritten_sem); inode_init_once(&ei->vfs_inode); ext4_fc_init_inode(&ei->vfs_inode); } @@ -5452,7 +5453,7 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb) * concurrency isn't really necessary. Limit it to 1. */ EXT4_SB(sb)->rsv_conversion_wq = - alloc_workqueue("ext4-rsv-conversion", WQ_MEM_RECLAIM | WQ_UNBOUND, 1); + alloc_workqueue("ext4-rsv-conversion", WQ_MEM_RECLAIM | WQ_UNBOUND, 0); if (!EXT4_SB(sb)->rsv_conversion_wq) { printk(KERN_ERR "EXT4-fs: failed to create workqueue\n"); err = -ENOMEM; -- 2.40.1