Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ext4: serialize dio nonlocked reads with defrag workers

Inode's block defrag and ext4_change_inode_journal_flag() may
affect nonlocked DIO reads result, so proper synchronization
required.

- Add missed inode_dio_wait() calls where appropriate
- Check inode state under extra i_dio_count reference.

Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: Dmitry Monakhov <dmonakhov@openvz.org>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>

authored by

Dmitry Monakhov and committed by
Theodore Ts'o
17335dcc 28a535f9

+44
+17
fs/ext4/ext4.h
··· 1358 1358 EXT4_STATE_DIO_UNWRITTEN, /* need convert on dio done*/ 1359 1359 EXT4_STATE_NEWENTRY, /* File just added to dir */ 1360 1360 EXT4_STATE_DELALLOC_RESERVED, /* blks already reserved for delalloc */ 1361 + EXT4_STATE_DIOREAD_LOCK, /* Disable support for dio read 1362 + nolocking */ 1361 1363 }; 1362 1364 1363 1365 #define EXT4_INODE_BIT_FNS(name, field, offset) \ ··· 2469 2467 static inline void set_bitmap_uptodate(struct buffer_head *bh) 2470 2468 { 2471 2469 set_bit(BH_BITMAP_UPTODATE, &(bh)->b_state); 2470 + } 2471 + 2472 + /* 2473 + * Disable DIO read nolock optimization, so new dioreaders will be forced 2474 + * to grab i_mutex 2475 + */ 2476 + static inline void ext4_inode_block_unlocked_dio(struct inode *inode) 2477 + { 2478 + ext4_set_inode_state(inode, EXT4_STATE_DIOREAD_LOCK); 2479 + smp_mb(); 2480 + } 2481 + static inline void ext4_inode_resume_unlocked_dio(struct inode *inode) 2482 + { 2483 + smp_mb(); 2484 + ext4_clear_inode_state(inode, EXT4_STATE_DIOREAD_LOCK); 2472 2485 } 2473 2486 2474 2487 #define in_range(b, first, len) ((b) >= (first) && (b) <= (first) + (len) - 1)
+14
fs/ext4/indirect.c
··· 810 810 if (unlikely(!list_empty(&ei->i_completed_io_list))) 811 811 ext4_flush_completed_IO(inode); 812 812 813 + /* 814 + * Nolock dioread optimization may be dynamically disabled 815 + * via ext4_inode_block_unlocked_dio(). Check inode's state 816 + * while holding extra i_dio_count ref. 817 + */ 818 + atomic_inc(&inode->i_dio_count); 819 + smp_mb(); 820 + if (unlikely(ext4_test_inode_state(inode, 821 + EXT4_STATE_DIOREAD_LOCK))) { 822 + inode_dio_done(inode); 823 + goto locked; 824 + } 813 825 ret = __blockdev_direct_IO(rw, iocb, inode, 814 826 inode->i_sb->s_bdev, iov, 815 827 offset, nr_segs, 816 828 ext4_get_block, NULL, NULL, 0); 829 + inode_dio_done(inode); 817 830 } else { 831 + locked: 818 832 ret = blockdev_direct_IO(rw, iocb, inode, iov, 819 833 offset, nr_segs, ext4_get_block); 820 834
+5
fs/ext4/inode.c
··· 4720 4720 return err; 4721 4721 } 4722 4722 4723 + /* Wait for all existing dio workers */ 4724 + ext4_inode_block_unlocked_dio(inode); 4725 + inode_dio_wait(inode); 4726 + 4723 4727 jbd2_journal_lock_updates(journal); 4724 4728 4725 4729 /* ··· 4743 4739 ext4_set_aops(inode); 4744 4740 4745 4741 jbd2_journal_unlock_updates(journal); 4742 + ext4_inode_resume_unlocked_dio(inode); 4746 4743 4747 4744 /* Finally we can mark the inode as dirty. */ 4748 4745
+8
fs/ext4/move_extent.c
··· 1323 1323 /* Protect orig and donor inodes against a truncate */ 1324 1324 mext_inode_double_lock(orig_inode, donor_inode); 1325 1325 1326 + /* Wait for all existing dio workers */ 1327 + ext4_inode_block_unlocked_dio(orig_inode); 1328 + ext4_inode_block_unlocked_dio(donor_inode); 1329 + inode_dio_wait(orig_inode); 1330 + inode_dio_wait(donor_inode); 1331 + 1326 1332 /* Protect extent tree against block allocations via delalloc */ 1327 1333 double_down_write_data_sem(orig_inode, donor_inode); 1328 1334 /* Check the filesystem environment whether move_extent can be done */ ··· 1527 1521 kfree(holecheck_path); 1528 1522 } 1529 1523 double_up_write_data_sem(orig_inode, donor_inode); 1524 + ext4_inode_resume_unlocked_dio(orig_inode); 1525 + ext4_inode_resume_unlocked_dio(donor_inode); 1530 1526 mext_inode_double_unlock(orig_inode, donor_inode); 1531 1527 1532 1528 return ret;