145 lines
4.5 KiB
Diff
145 lines
4.5 KiB
Diff
|
From 994f567b2e99c82913a279ff438269c771b68a4b Mon Sep 17 00:00:00 2001
|
||
|
From: Dmitry Monakhov <dmonakhov@openvz.org>
|
||
|
Date: Sat, 29 Sep 2012 00:41:21 -0400
|
||
|
Subject: [PATCH 05/13] 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>
|
||
|
(cherry picked from commit 17335dcc471199717839b2fa3492ca36f70f1168)
|
||
|
|
||
|
Conflicts:
|
||
|
fs/ext4/move_extent.c
|
||
|
---
|
||
|
fs/ext4/ext4.h | 17 +++++++++++++++++
|
||
|
fs/ext4/indirect.c | 14 ++++++++++++++
|
||
|
fs/ext4/inode.c | 5 +++++
|
||
|
fs/ext4/move_extent.c | 8 ++++++++
|
||
|
4 files changed, 44 insertions(+)
|
||
|
|
||
|
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
|
||
|
index 7687d15..3e740e9 100644
|
||
|
--- a/fs/ext4/ext4.h
|
||
|
+++ b/fs/ext4/ext4.h
|
||
|
@@ -1352,6 +1352,8 @@ enum {
|
||
|
EXT4_STATE_DIO_UNWRITTEN, /* need convert on dio done*/
|
||
|
EXT4_STATE_NEWENTRY, /* File just added to dir */
|
||
|
EXT4_STATE_DELALLOC_RESERVED, /* blks already reserved for delalloc */
|
||
|
+ EXT4_STATE_DIOREAD_LOCK, /* Disable support for dio read
|
||
|
+ nolocking */
|
||
|
};
|
||
|
|
||
|
#define EXT4_INODE_BIT_FNS(name, field, offset) \
|
||
|
@@ -2459,6 +2461,21 @@ static inline void set_bitmap_uptodate(struct buffer_head *bh)
|
||
|
set_bit(BH_BITMAP_UPTODATE, &(bh)->b_state);
|
||
|
}
|
||
|
|
||
|
+/*
|
||
|
+ * Disable DIO read nolock optimization, so new dioreaders will be forced
|
||
|
+ * to grab i_mutex
|
||
|
+ */
|
||
|
+static inline void ext4_inode_block_unlocked_dio(struct inode *inode)
|
||
|
+{
|
||
|
+ ext4_set_inode_state(inode, EXT4_STATE_DIOREAD_LOCK);
|
||
|
+ smp_mb();
|
||
|
+}
|
||
|
+static inline void ext4_inode_resume_unlocked_dio(struct inode *inode)
|
||
|
+{
|
||
|
+ smp_mb();
|
||
|
+ ext4_clear_inode_state(inode, EXT4_STATE_DIOREAD_LOCK);
|
||
|
+}
|
||
|
+
|
||
|
#define in_range(b, first, len) ((b) >= (first) && (b) <= (first) + (len) - 1)
|
||
|
|
||
|
/* For ioend & aio unwritten conversion wait queues */
|
||
|
diff --git a/fs/ext4/indirect.c b/fs/ext4/indirect.c
|
||
|
index 61f13e5..8d849da 100644
|
||
|
--- a/fs/ext4/indirect.c
|
||
|
+++ b/fs/ext4/indirect.c
|
||
|
@@ -810,11 +810,25 @@ retry:
|
||
|
if (unlikely(!list_empty(&ei->i_completed_io_list)))
|
||
|
ext4_flush_completed_IO(inode);
|
||
|
|
||
|
+ /*
|
||
|
+ * Nolock dioread optimization may be dynamically disabled
|
||
|
+ * via ext4_inode_block_unlocked_dio(). Check inode's state
|
||
|
+ * while holding extra i_dio_count ref.
|
||
|
+ */
|
||
|
+ atomic_inc(&inode->i_dio_count);
|
||
|
+ smp_mb();
|
||
|
+ if (unlikely(ext4_test_inode_state(inode,
|
||
|
+ EXT4_STATE_DIOREAD_LOCK))) {
|
||
|
+ inode_dio_done(inode);
|
||
|
+ goto locked;
|
||
|
+ }
|
||
|
ret = __blockdev_direct_IO(rw, iocb, inode,
|
||
|
inode->i_sb->s_bdev, iov,
|
||
|
offset, nr_segs,
|
||
|
ext4_get_block, NULL, NULL, 0);
|
||
|
+ inode_dio_done(inode);
|
||
|
} else {
|
||
|
+locked:
|
||
|
ret = blockdev_direct_IO(rw, iocb, inode, iov,
|
||
|
offset, nr_segs, ext4_get_block);
|
||
|
|
||
|
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
|
||
|
index dd3fd23..2bd7526 100644
|
||
|
--- a/fs/ext4/inode.c
|
||
|
+++ b/fs/ext4/inode.c
|
||
|
@@ -4706,6 +4706,10 @@ int ext4_change_inode_journal_flag(struct inode *inode, int val)
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
+ /* Wait for all existing dio workers */
|
||
|
+ ext4_inode_block_unlocked_dio(inode);
|
||
|
+ inode_dio_wait(inode);
|
||
|
+
|
||
|
jbd2_journal_lock_updates(journal);
|
||
|
|
||
|
/*
|
||
|
@@ -4725,6 +4729,7 @@ int ext4_change_inode_journal_flag(struct inode *inode, int val)
|
||
|
ext4_set_aops(inode);
|
||
|
|
||
|
jbd2_journal_unlock_updates(journal);
|
||
|
+ ext4_inode_resume_unlocked_dio(inode);
|
||
|
|
||
|
/* Finally we can mark the inode as dirty. */
|
||
|
|
||
|
diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c
|
||
|
index c5826c6..fd1e32e 100644
|
||
|
--- a/fs/ext4/move_extent.c
|
||
|
+++ b/fs/ext4/move_extent.c
|
||
|
@@ -1214,6 +1214,12 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp,
|
||
|
/* Protect orig and donor inodes against a truncate */
|
||
|
mext_inode_double_lock(orig_inode, donor_inode);
|
||
|
|
||
|
+ /* Wait for all existing dio workers */
|
||
|
+ ext4_inode_block_unlocked_dio(orig_inode);
|
||
|
+ ext4_inode_block_unlocked_dio(donor_inode);
|
||
|
+ inode_dio_wait(orig_inode);
|
||
|
+ inode_dio_wait(donor_inode);
|
||
|
+
|
||
|
/* Protect extent tree against block allocations via delalloc */
|
||
|
double_down_write_data_sem(orig_inode, donor_inode);
|
||
|
/* Check the filesystem environment whether move_extent can be done */
|
||
|
@@ -1413,6 +1419,8 @@ out:
|
||
|
kfree(holecheck_path);
|
||
|
}
|
||
|
double_up_write_data_sem(orig_inode, donor_inode);
|
||
|
+ ext4_inode_resume_unlocked_dio(orig_inode);
|
||
|
+ ext4_inode_resume_unlocked_dio(donor_inode);
|
||
|
mext_inode_double_unlock(orig_inode, donor_inode);
|
||
|
|
||
|
return ret;
|
||
|
--
|
||
|
1.7.12.rc0.22.gcdd159b
|
||
|
|