diff options
author | Chris Mason <chris.mason@oracle.com> | 2008-06-25 16:01:31 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2008-09-25 11:04:03 -0400 |
commit | 89ce8a63d0c761fbb02089850605360f389477d8 (patch) | |
tree | a509b1daf6e41f7768eaf49235c573690f12ef9b | |
parent | 333db94cdde9e6dfdedab9290d04d812f83e0922 (diff) |
Add btrfs_end_transaction_throttle to force writers to wait for pending commits
The existing throttle mechanism was often not sufficient to prevent
new writers from coming in and making a given transaction run forever.
This adds an explicit wait at the end of most operations so they will
allow the current transaction to close.
There is no wait inside file_write, inode updates, or cow filling, all which
have different deadlock possibilities.
This is a temporary measure until better asynchronous commit support is
added. This code leads to stalls as it waits for data=ordered
writeback, and it really needs to be fixed.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
-rw-r--r-- | fs/btrfs/disk-io.c | 18 | ||||
-rw-r--r-- | fs/btrfs/disk-io.h | 1 | ||||
-rw-r--r-- | fs/btrfs/file.c | 1 | ||||
-rw-r--r-- | fs/btrfs/inode.c | 24 | ||||
-rw-r--r-- | fs/btrfs/ioctl.c | 2 | ||||
-rw-r--r-- | fs/btrfs/transaction.c | 66 | ||||
-rw-r--r-- | fs/btrfs/transaction.h | 2 |
7 files changed, 55 insertions, 59 deletions
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 3cc480b83819..52569b57692d 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c | |||
@@ -1672,24 +1672,6 @@ void btrfs_mark_buffer_dirty(struct extent_buffer *buf) | |||
1672 | set_extent_buffer_dirty(&BTRFS_I(btree_inode)->io_tree, buf); | 1672 | set_extent_buffer_dirty(&BTRFS_I(btree_inode)->io_tree, buf); |
1673 | } | 1673 | } |
1674 | 1674 | ||
1675 | void btrfs_throttle(struct btrfs_root *root) | ||
1676 | { | ||
1677 | #if 0 | ||
1678 | struct backing_dev_info *bdi; | ||
1679 | |||
1680 | bdi = &root->fs_info->bdi; | ||
1681 | if (atomic_read(&root->fs_info->throttles) && | ||
1682 | bdi_write_congested(bdi)) { | ||
1683 | #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18) | ||
1684 | congestion_wait(WRITE, HZ/20); | ||
1685 | #else | ||
1686 | blk_congestion_wait(WRITE, HZ/20); | ||
1687 | #endif | ||
1688 | |||
1689 | } | ||
1690 | #endif | ||
1691 | } | ||
1692 | |||
1693 | void btrfs_btree_balance_dirty(struct btrfs_root *root, unsigned long nr) | 1675 | void btrfs_btree_balance_dirty(struct btrfs_root *root, unsigned long nr) |
1694 | { | 1676 | { |
1695 | /* | 1677 | /* |
diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h index 2bc64fefe6ea..deff6b4815a7 100644 --- a/fs/btrfs/disk-io.h +++ b/fs/btrfs/disk-io.h | |||
@@ -70,7 +70,6 @@ int btrfs_clear_buffer_defrag_done(struct extent_buffer *buf); | |||
70 | int btrfs_read_buffer(struct extent_buffer *buf, u64 parent_transid); | 70 | int btrfs_read_buffer(struct extent_buffer *buf, u64 parent_transid); |
71 | u32 btrfs_csum_data(struct btrfs_root *root, char *data, u32 seed, size_t len); | 71 | u32 btrfs_csum_data(struct btrfs_root *root, char *data, u32 seed, size_t len); |
72 | void btrfs_csum_final(u32 crc, char *result); | 72 | void btrfs_csum_final(u32 crc, char *result); |
73 | void btrfs_throttle(struct btrfs_root *root); | ||
74 | int btrfs_open_device(struct btrfs_device *dev); | 73 | int btrfs_open_device(struct btrfs_device *dev); |
75 | int btrfs_verify_block_csum(struct btrfs_root *root, | 74 | int btrfs_verify_block_csum(struct btrfs_root *root, |
76 | struct extent_buffer *buf); | 75 | struct extent_buffer *buf); |
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index b7f8f92daf8a..ece221cba90c 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c | |||
@@ -934,7 +934,6 @@ static ssize_t btrfs_file_write(struct file *file, const char __user *buf, | |||
934 | balance_dirty_pages_ratelimited_nr(inode->i_mapping, num_pages); | 934 | balance_dirty_pages_ratelimited_nr(inode->i_mapping, num_pages); |
935 | if (num_pages < (root->leafsize >> PAGE_CACHE_SHIFT) + 1) | 935 | if (num_pages < (root->leafsize >> PAGE_CACHE_SHIFT) + 1) |
936 | btrfs_btree_balance_dirty(root, 1); | 936 | btrfs_btree_balance_dirty(root, 1); |
937 | btrfs_throttle(root); | ||
938 | cond_resched(); | 937 | cond_resched(); |
939 | } | 938 | } |
940 | out: | 939 | out: |
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index cf27b5984627..bbba3350d023 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c | |||
@@ -855,10 +855,9 @@ static int btrfs_unlink(struct inode *dir, struct dentry *dentry) | |||
855 | btrfs_del_ordered_inode(inode, 1); | 855 | btrfs_del_ordered_inode(inode, 1); |
856 | } | 856 | } |
857 | 857 | ||
858 | btrfs_end_transaction(trans, root); | 858 | btrfs_end_transaction_throttle(trans, root); |
859 | fail: | 859 | fail: |
860 | btrfs_btree_balance_dirty(root, nr); | 860 | btrfs_btree_balance_dirty(root, nr); |
861 | btrfs_throttle(root); | ||
862 | return ret; | 861 | return ret; |
863 | } | 862 | } |
864 | 863 | ||
@@ -889,10 +888,9 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry) | |||
889 | } | 888 | } |
890 | 889 | ||
891 | nr = trans->blocks_used; | 890 | nr = trans->blocks_used; |
892 | ret = btrfs_end_transaction(trans, root); | 891 | ret = btrfs_end_transaction_throttle(trans, root); |
893 | fail: | 892 | fail: |
894 | btrfs_btree_balance_dirty(root, nr); | 893 | btrfs_btree_balance_dirty(root, nr); |
895 | btrfs_throttle(root); | ||
896 | 894 | ||
897 | if (ret && !err) | 895 | if (ret && !err) |
898 | err = ret; | 896 | err = ret; |
@@ -1871,14 +1869,13 @@ static int btrfs_mknod(struct inode *dir, struct dentry *dentry, | |||
1871 | btrfs_update_inode_block_group(trans, dir); | 1869 | btrfs_update_inode_block_group(trans, dir); |
1872 | out_unlock: | 1870 | out_unlock: |
1873 | nr = trans->blocks_used; | 1871 | nr = trans->blocks_used; |
1874 | btrfs_end_transaction(trans, root); | 1872 | btrfs_end_transaction_throttle(trans, root); |
1875 | fail: | 1873 | fail: |
1876 | if (drop_inode) { | 1874 | if (drop_inode) { |
1877 | inode_dec_link_count(inode); | 1875 | inode_dec_link_count(inode); |
1878 | iput(inode); | 1876 | iput(inode); |
1879 | } | 1877 | } |
1880 | btrfs_btree_balance_dirty(root, nr); | 1878 | btrfs_btree_balance_dirty(root, nr); |
1881 | btrfs_throttle(root); | ||
1882 | return err; | 1879 | return err; |
1883 | } | 1880 | } |
1884 | 1881 | ||
@@ -1936,14 +1933,13 @@ static int btrfs_create(struct inode *dir, struct dentry *dentry, | |||
1936 | btrfs_update_inode_block_group(trans, dir); | 1933 | btrfs_update_inode_block_group(trans, dir); |
1937 | out_unlock: | 1934 | out_unlock: |
1938 | nr = trans->blocks_used; | 1935 | nr = trans->blocks_used; |
1939 | btrfs_end_transaction(trans, root); | 1936 | btrfs_end_transaction_throttle(trans, root); |
1940 | fail: | 1937 | fail: |
1941 | if (drop_inode) { | 1938 | if (drop_inode) { |
1942 | inode_dec_link_count(inode); | 1939 | inode_dec_link_count(inode); |
1943 | iput(inode); | 1940 | iput(inode); |
1944 | } | 1941 | } |
1945 | btrfs_btree_balance_dirty(root, nr); | 1942 | btrfs_btree_balance_dirty(root, nr); |
1946 | btrfs_throttle(root); | ||
1947 | return err; | 1943 | return err; |
1948 | } | 1944 | } |
1949 | 1945 | ||
@@ -1985,14 +1981,13 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir, | |||
1985 | drop_inode = 1; | 1981 | drop_inode = 1; |
1986 | 1982 | ||
1987 | nr = trans->blocks_used; | 1983 | nr = trans->blocks_used; |
1988 | btrfs_end_transaction(trans, root); | 1984 | btrfs_end_transaction_throttle(trans, root); |
1989 | fail: | 1985 | fail: |
1990 | if (drop_inode) { | 1986 | if (drop_inode) { |
1991 | inode_dec_link_count(inode); | 1987 | inode_dec_link_count(inode); |
1992 | iput(inode); | 1988 | iput(inode); |
1993 | } | 1989 | } |
1994 | btrfs_btree_balance_dirty(root, nr); | 1990 | btrfs_btree_balance_dirty(root, nr); |
1995 | btrfs_throttle(root); | ||
1996 | return err; | 1991 | return err; |
1997 | } | 1992 | } |
1998 | 1993 | ||
@@ -2055,13 +2050,12 @@ static int btrfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) | |||
2055 | 2050 | ||
2056 | out_fail: | 2051 | out_fail: |
2057 | nr = trans->blocks_used; | 2052 | nr = trans->blocks_used; |
2058 | btrfs_end_transaction(trans, root); | 2053 | btrfs_end_transaction_throttle(trans, root); |
2059 | 2054 | ||
2060 | out_unlock: | 2055 | out_unlock: |
2061 | if (drop_on_err) | 2056 | if (drop_on_err) |
2062 | iput(inode); | 2057 | iput(inode); |
2063 | btrfs_btree_balance_dirty(root, nr); | 2058 | btrfs_btree_balance_dirty(root, nr); |
2064 | btrfs_throttle(root); | ||
2065 | return err; | 2059 | return err; |
2066 | } | 2060 | } |
2067 | 2061 | ||
@@ -2587,10 +2581,9 @@ static void btrfs_truncate(struct inode *inode) | |||
2587 | btrfs_update_inode(trans, root, inode); | 2581 | btrfs_update_inode(trans, root, inode); |
2588 | nr = trans->blocks_used; | 2582 | nr = trans->blocks_used; |
2589 | 2583 | ||
2590 | ret = btrfs_end_transaction(trans, root); | 2584 | ret = btrfs_end_transaction_throttle(trans, root); |
2591 | BUG_ON(ret); | 2585 | BUG_ON(ret); |
2592 | btrfs_btree_balance_dirty(root, nr); | 2586 | btrfs_btree_balance_dirty(root, nr); |
2593 | btrfs_throttle(root); | ||
2594 | } | 2587 | } |
2595 | 2588 | ||
2596 | /* | 2589 | /* |
@@ -2912,14 +2905,13 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry, | |||
2912 | 2905 | ||
2913 | out_unlock: | 2906 | out_unlock: |
2914 | nr = trans->blocks_used; | 2907 | nr = trans->blocks_used; |
2915 | btrfs_end_transaction(trans, root); | 2908 | btrfs_end_transaction_throttle(trans, root); |
2916 | out_fail: | 2909 | out_fail: |
2917 | if (drop_inode) { | 2910 | if (drop_inode) { |
2918 | inode_dec_link_count(inode); | 2911 | inode_dec_link_count(inode); |
2919 | iput(inode); | 2912 | iput(inode); |
2920 | } | 2913 | } |
2921 | btrfs_btree_balance_dirty(root, nr); | 2914 | btrfs_btree_balance_dirty(root, nr); |
2922 | btrfs_throttle(root); | ||
2923 | return err; | 2915 | return err; |
2924 | } | 2916 | } |
2925 | 2917 | ||
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 6002eb64daf9..026039a2ac58 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c | |||
@@ -164,7 +164,6 @@ fail: | |||
164 | ret = err; | 164 | ret = err; |
165 | fail_commit: | 165 | fail_commit: |
166 | btrfs_btree_balance_dirty(root, nr); | 166 | btrfs_btree_balance_dirty(root, nr); |
167 | btrfs_throttle(root); | ||
168 | return ret; | 167 | return ret; |
169 | } | 168 | } |
170 | 169 | ||
@@ -206,7 +205,6 @@ static int create_snapshot(struct btrfs_root *root, char *name, int namelen) | |||
206 | 205 | ||
207 | fail_unlock: | 206 | fail_unlock: |
208 | btrfs_btree_balance_dirty(root, nr); | 207 | btrfs_btree_balance_dirty(root, nr); |
209 | btrfs_throttle(root); | ||
210 | return ret; | 208 | return ret; |
211 | } | 209 | } |
212 | 210 | ||
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 5a1ee0665ae8..69ed5f85a387 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c | |||
@@ -130,8 +130,27 @@ struct btrfs_trans_handle *btrfs_start_transaction(struct btrfs_root *root, | |||
130 | return h; | 130 | return h; |
131 | } | 131 | } |
132 | 132 | ||
133 | int btrfs_end_transaction(struct btrfs_trans_handle *trans, | 133 | static noinline int wait_for_commit(struct btrfs_root *root, |
134 | struct btrfs_root *root) | 134 | struct btrfs_transaction *commit) |
135 | { | ||
136 | DEFINE_WAIT(wait); | ||
137 | mutex_lock(&root->fs_info->trans_mutex); | ||
138 | while(!commit->commit_done) { | ||
139 | prepare_to_wait(&commit->commit_wait, &wait, | ||
140 | TASK_UNINTERRUPTIBLE); | ||
141 | if (commit->commit_done) | ||
142 | break; | ||
143 | mutex_unlock(&root->fs_info->trans_mutex); | ||
144 | schedule(); | ||
145 | mutex_lock(&root->fs_info->trans_mutex); | ||
146 | } | ||
147 | mutex_unlock(&root->fs_info->trans_mutex); | ||
148 | finish_wait(&commit->commit_wait, &wait); | ||
149 | return 0; | ||
150 | } | ||
151 | |||
152 | static int __btrfs_end_transaction(struct btrfs_trans_handle *trans, | ||
153 | struct btrfs_root *root, int throttle) | ||
135 | { | 154 | { |
136 | struct btrfs_transaction *cur_trans; | 155 | struct btrfs_transaction *cur_trans; |
137 | 156 | ||
@@ -140,8 +159,18 @@ int btrfs_end_transaction(struct btrfs_trans_handle *trans, | |||
140 | WARN_ON(cur_trans != trans->transaction); | 159 | WARN_ON(cur_trans != trans->transaction); |
141 | WARN_ON(cur_trans->num_writers < 1); | 160 | WARN_ON(cur_trans->num_writers < 1); |
142 | cur_trans->num_writers--; | 161 | cur_trans->num_writers--; |
162 | |||
143 | if (waitqueue_active(&cur_trans->writer_wait)) | 163 | if (waitqueue_active(&cur_trans->writer_wait)) |
144 | wake_up(&cur_trans->writer_wait); | 164 | wake_up(&cur_trans->writer_wait); |
165 | |||
166 | if (cur_trans->in_commit && throttle) { | ||
167 | int ret; | ||
168 | mutex_unlock(&root->fs_info->trans_mutex); | ||
169 | ret = wait_for_commit(root, cur_trans); | ||
170 | BUG_ON(ret); | ||
171 | mutex_lock(&root->fs_info->trans_mutex); | ||
172 | } | ||
173 | |||
145 | put_transaction(cur_trans); | 174 | put_transaction(cur_trans); |
146 | mutex_unlock(&root->fs_info->trans_mutex); | 175 | mutex_unlock(&root->fs_info->trans_mutex); |
147 | memset(trans, 0, sizeof(*trans)); | 176 | memset(trans, 0, sizeof(*trans)); |
@@ -149,6 +178,18 @@ int btrfs_end_transaction(struct btrfs_trans_handle *trans, | |||
149 | return 0; | 178 | return 0; |
150 | } | 179 | } |
151 | 180 | ||
181 | int btrfs_end_transaction(struct btrfs_trans_handle *trans, | ||
182 | struct btrfs_root *root) | ||
183 | { | ||
184 | return __btrfs_end_transaction(trans, root, 0); | ||
185 | } | ||
186 | |||
187 | int btrfs_end_transaction_throttle(struct btrfs_trans_handle *trans, | ||
188 | struct btrfs_root *root) | ||
189 | { | ||
190 | return __btrfs_end_transaction(trans, root, 1); | ||
191 | } | ||
192 | |||
152 | 193 | ||
153 | int btrfs_write_and_wait_transaction(struct btrfs_trans_handle *trans, | 194 | int btrfs_write_and_wait_transaction(struct btrfs_trans_handle *trans, |
154 | struct btrfs_root *root) | 195 | struct btrfs_root *root) |
@@ -240,25 +281,6 @@ int btrfs_commit_tree_roots(struct btrfs_trans_handle *trans, | |||
240 | return 0; | 281 | return 0; |
241 | } | 282 | } |
242 | 283 | ||
243 | static noinline int wait_for_commit(struct btrfs_root *root, | ||
244 | struct btrfs_transaction *commit) | ||
245 | { | ||
246 | DEFINE_WAIT(wait); | ||
247 | mutex_lock(&root->fs_info->trans_mutex); | ||
248 | while(!commit->commit_done) { | ||
249 | prepare_to_wait(&commit->commit_wait, &wait, | ||
250 | TASK_UNINTERRUPTIBLE); | ||
251 | if (commit->commit_done) | ||
252 | break; | ||
253 | mutex_unlock(&root->fs_info->trans_mutex); | ||
254 | schedule(); | ||
255 | mutex_lock(&root->fs_info->trans_mutex); | ||
256 | } | ||
257 | mutex_unlock(&root->fs_info->trans_mutex); | ||
258 | finish_wait(&commit->commit_wait, &wait); | ||
259 | return 0; | ||
260 | } | ||
261 | |||
262 | struct dirty_root { | 284 | struct dirty_root { |
263 | struct list_head list; | 285 | struct list_head list; |
264 | struct btrfs_root *root; | 286 | struct btrfs_root *root; |
@@ -680,6 +702,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, | |||
680 | root->fs_info->btree_inode->i_mapping, GFP_NOFS); | 702 | root->fs_info->btree_inode->i_mapping, GFP_NOFS); |
681 | 703 | ||
682 | trans->transaction->in_commit = 1; | 704 | trans->transaction->in_commit = 1; |
705 | printk("trans %Lu in commit\n", trans->transid); | ||
683 | cur_trans = trans->transaction; | 706 | cur_trans = trans->transaction; |
684 | if (cur_trans->list.prev != &root->fs_info->trans_list) { | 707 | if (cur_trans->list.prev != &root->fs_info->trans_list) { |
685 | prev_trans = list_entry(cur_trans->list.prev, | 708 | prev_trans = list_entry(cur_trans->list.prev, |
@@ -760,6 +783,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, | |||
760 | kfree(pinned_copy); | 783 | kfree(pinned_copy); |
761 | 784 | ||
762 | cur_trans->commit_done = 1; | 785 | cur_trans->commit_done = 1; |
786 | printk("trans %Lu done in commit\n", cur_trans->transid); | ||
763 | root->fs_info->last_trans_committed = cur_trans->transid; | 787 | root->fs_info->last_trans_committed = cur_trans->transid; |
764 | wake_up(&cur_trans->commit_wait); | 788 | wake_up(&cur_trans->commit_wait); |
765 | put_transaction(cur_trans); | 789 | put_transaction(cur_trans); |
diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h index c3172ddb3321..52559b51b181 100644 --- a/fs/btrfs/transaction.h +++ b/fs/btrfs/transaction.h | |||
@@ -101,4 +101,6 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, | |||
101 | struct btrfs_root *root); | 101 | struct btrfs_root *root); |
102 | int btrfs_write_ordered_inodes(struct btrfs_trans_handle *trans, | 102 | int btrfs_write_ordered_inodes(struct btrfs_trans_handle *trans, |
103 | struct btrfs_root *root); | 103 | struct btrfs_root *root); |
104 | int btrfs_end_transaction_throttle(struct btrfs_trans_handle *trans, | ||
105 | struct btrfs_root *root); | ||
104 | #endif | 106 | #endif |