aboutsummaryrefslogtreecommitdiffstats
path: root/include/linux
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2012-05-03 08:48:03 -0400
committerFengguang Wu <fengguang.wu@intel.com>2012-05-06 01:43:41 -0400
commit169ebd90131b2ffca74bb2dbe7eeacd39fb83714 (patch)
tree513136e110970da388680947c225ef8ca05a6dd7 /include/linux
parentdbd5768f87ff6fb0a4fe09c4d7b6c4a24de99430 (diff)
writeback: Avoid iput() from flusher thread
Doing iput() from flusher thread (writeback_sb_inodes()) can create problems because iput() can do a lot of work - for example truncate the inode if it's the last iput on unlinked file. Some filesystems depend on flusher thread progressing (e.g. because they need to flush delay allocated blocks to reduce allocation uncertainty) and so flusher thread doing truncate creates interesting dependencies and possibilities for deadlocks. We get rid of iput() in flusher thread by using the fact that I_SYNC inode flag effectively pins the inode in memory. So if we take care to either hold i_lock or have I_SYNC set, we can get away without taking inode reference in writeback_sb_inodes(). As a side effect of these changes, we also fix possible use-after-free in wb_writeback() because inode_wait_for_writeback() call could try to reacquire i_lock on the inode that was already free. Signed-off-by: Jan Kara <jack@suse.cz> Signed-off-by: Fengguang Wu <fengguang.wu@intel.com>
Diffstat (limited to 'include/linux')
-rw-r--r--include/linux/fs.h7
-rw-r--r--include/linux/writeback.h7
2 files changed, 5 insertions, 9 deletions
diff --git a/include/linux/fs.h b/include/linux/fs.h
index c79316c79ee..1c71e7f4d23 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1753,9 +1753,10 @@ struct super_operations {
1753 * anew. Other functions will just ignore such inodes, 1753 * anew. Other functions will just ignore such inodes,
1754 * if appropriate. I_NEW is used for waiting. 1754 * if appropriate. I_NEW is used for waiting.
1755 * 1755 *
1756 * I_SYNC Synchonized write of dirty inode data. The bits is 1756 * I_SYNC Writeback of inode is running. The bit is set during
1757 * set during data writeback, and cleared with a wakeup 1757 * data writeback, and cleared with a wakeup on the bit
1758 * on the bit address once it is done. 1758 * address once it is done. The bit is also used to pin
1759 * the inode in memory for flusher thread.
1759 * 1760 *
1760 * I_REFERENCED Marks the inode as recently references on the LRU list. 1761 * I_REFERENCED Marks the inode as recently references on the LRU list.
1761 * 1762 *
diff --git a/include/linux/writeback.h b/include/linux/writeback.h
index 3309736ff05..6d0a0fcd80e 100644
--- a/include/linux/writeback.h
+++ b/include/linux/writeback.h
@@ -95,6 +95,7 @@ long writeback_inodes_wb(struct bdi_writeback *wb, long nr_pages,
95 enum wb_reason reason); 95 enum wb_reason reason);
96long wb_do_writeback(struct bdi_writeback *wb, int force_wait); 96long wb_do_writeback(struct bdi_writeback *wb, int force_wait);
97void wakeup_flusher_threads(long nr_pages, enum wb_reason reason); 97void wakeup_flusher_threads(long nr_pages, enum wb_reason reason);
98void inode_wait_for_writeback(struct inode *inode);
98 99
99/* writeback.h requires fs.h; it, too, is not included from here. */ 100/* writeback.h requires fs.h; it, too, is not included from here. */
100static inline void wait_on_inode(struct inode *inode) 101static inline void wait_on_inode(struct inode *inode)
@@ -102,12 +103,6 @@ static inline void wait_on_inode(struct inode *inode)
102 might_sleep(); 103 might_sleep();
103 wait_on_bit(&inode->i_state, __I_NEW, inode_wait, TASK_UNINTERRUPTIBLE); 104 wait_on_bit(&inode->i_state, __I_NEW, inode_wait, TASK_UNINTERRUPTIBLE);
104} 105}
105static inline void inode_sync_wait(struct inode *inode)
106{
107 might_sleep();
108 wait_on_bit(&inode->i_state, __I_SYNC, inode_wait,
109 TASK_UNINTERRUPTIBLE);
110}
111 106
112 107
113/* 108/*