diff options
author | Theodore Ts'o <tytso@mit.edu> | 2010-07-27 11:56:05 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2010-07-27 11:56:05 -0400 |
commit | 47def82672b3ba4e7c5e9a4fe48a556f8684d0d6 (patch) | |
tree | a0cfdaca8be99980c09d2b38ff676834c85ab2c4 | |
parent | 40389687382bf0ae71458e7c0f828137a438a956 (diff) |
jbd2: Remove __GFP_NOFAIL from jbd2 layer
__GFP_NOFAIL is going away, so add our own retry loop. Also add
jbd2__journal_start() and jbd2__journal_restart() which take a gfp
mask, so that file systems can optionally (re)start transaction
handles using GFP_KERNEL. If they do this, then they need to be
prepared to handle receiving an PTR_ERR(-ENOMEM) error, and be ready
to reflect that error up to userspace.
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
-rw-r--r-- | fs/jbd2/journal.c | 15 | ||||
-rw-r--r-- | fs/jbd2/transaction.c | 61 | ||||
-rw-r--r-- | include/linux/jbd2.h | 4 |
3 files changed, 57 insertions, 23 deletions
diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index f7bf15787d68..a79d3345b55a 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c | |||
@@ -41,6 +41,7 @@ | |||
41 | #include <linux/hash.h> | 41 | #include <linux/hash.h> |
42 | #include <linux/log2.h> | 42 | #include <linux/log2.h> |
43 | #include <linux/vmalloc.h> | 43 | #include <linux/vmalloc.h> |
44 | #include <linux/backing-dev.h> | ||
44 | 45 | ||
45 | #define CREATE_TRACE_POINTS | 46 | #define CREATE_TRACE_POINTS |
46 | #include <trace/events/jbd2.h> | 47 | #include <trace/events/jbd2.h> |
@@ -48,8 +49,6 @@ | |||
48 | #include <asm/uaccess.h> | 49 | #include <asm/uaccess.h> |
49 | #include <asm/page.h> | 50 | #include <asm/page.h> |
50 | 51 | ||
51 | EXPORT_SYMBOL(jbd2_journal_start); | ||
52 | EXPORT_SYMBOL(jbd2_journal_restart); | ||
53 | EXPORT_SYMBOL(jbd2_journal_extend); | 52 | EXPORT_SYMBOL(jbd2_journal_extend); |
54 | EXPORT_SYMBOL(jbd2_journal_stop); | 53 | EXPORT_SYMBOL(jbd2_journal_stop); |
55 | EXPORT_SYMBOL(jbd2_journal_lock_updates); | 54 | EXPORT_SYMBOL(jbd2_journal_lock_updates); |
@@ -311,7 +310,17 @@ int jbd2_journal_write_metadata_buffer(transaction_t *transaction, | |||
311 | */ | 310 | */ |
312 | J_ASSERT_BH(bh_in, buffer_jbddirty(bh_in)); | 311 | J_ASSERT_BH(bh_in, buffer_jbddirty(bh_in)); |
313 | 312 | ||
314 | new_bh = alloc_buffer_head(GFP_NOFS|__GFP_NOFAIL); | 313 | retry_alloc: |
314 | new_bh = alloc_buffer_head(GFP_NOFS); | ||
315 | if (!new_bh) { | ||
316 | /* | ||
317 | * Failure is not an option, but __GFP_NOFAIL is going | ||
318 | * away; so we retry ourselves here. | ||
319 | */ | ||
320 | congestion_wait(BLK_RW_ASYNC, HZ/50); | ||
321 | goto retry_alloc; | ||
322 | } | ||
323 | |||
315 | /* keep subsequent assertions sane */ | 324 | /* keep subsequent assertions sane */ |
316 | new_bh->b_state = 0; | 325 | new_bh->b_state = 0; |
317 | init_buffer(new_bh, NULL, NULL); | 326 | init_buffer(new_bh, NULL, NULL); |
diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index e214d68620ac..001e95fb0fe1 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c | |||
@@ -26,6 +26,8 @@ | |||
26 | #include <linux/mm.h> | 26 | #include <linux/mm.h> |
27 | #include <linux/highmem.h> | 27 | #include <linux/highmem.h> |
28 | #include <linux/hrtimer.h> | 28 | #include <linux/hrtimer.h> |
29 | #include <linux/backing-dev.h> | ||
30 | #include <linux/module.h> | ||
29 | 31 | ||
30 | static void __jbd2_journal_temp_unlink_buffer(struct journal_head *jh); | 32 | static void __jbd2_journal_temp_unlink_buffer(struct journal_head *jh); |
31 | 33 | ||
@@ -83,30 +85,38 @@ jbd2_get_transaction(journal_t *journal, transaction_t *transaction) | |||
83 | * transaction's buffer credits. | 85 | * transaction's buffer credits. |
84 | */ | 86 | */ |
85 | 87 | ||
86 | static int start_this_handle(journal_t *journal, handle_t *handle) | 88 | static int start_this_handle(journal_t *journal, handle_t *handle, |
89 | int gfp_mask) | ||
87 | { | 90 | { |
88 | transaction_t *transaction; | 91 | transaction_t *transaction; |
89 | int needed; | 92 | int needed; |
90 | int nblocks = handle->h_buffer_credits; | 93 | int nblocks = handle->h_buffer_credits; |
91 | transaction_t *new_transaction = NULL; | 94 | transaction_t *new_transaction = NULL; |
92 | int ret = 0; | ||
93 | unsigned long ts = jiffies; | 95 | unsigned long ts = jiffies; |
94 | 96 | ||
95 | if (nblocks > journal->j_max_transaction_buffers) { | 97 | if (nblocks > journal->j_max_transaction_buffers) { |
96 | printk(KERN_ERR "JBD: %s wants too many credits (%d > %d)\n", | 98 | printk(KERN_ERR "JBD: %s wants too many credits (%d > %d)\n", |
97 | current->comm, nblocks, | 99 | current->comm, nblocks, |
98 | journal->j_max_transaction_buffers); | 100 | journal->j_max_transaction_buffers); |
99 | ret = -ENOSPC; | 101 | return -ENOSPC; |
100 | goto out; | ||
101 | } | 102 | } |
102 | 103 | ||
103 | alloc_transaction: | 104 | alloc_transaction: |
104 | if (!journal->j_running_transaction) { | 105 | if (!journal->j_running_transaction) { |
105 | new_transaction = kzalloc(sizeof(*new_transaction), | 106 | new_transaction = kzalloc(sizeof(*new_transaction), gfp_mask); |
106 | GFP_NOFS|__GFP_NOFAIL); | ||
107 | if (!new_transaction) { | 107 | if (!new_transaction) { |
108 | ret = -ENOMEM; | 108 | /* |
109 | goto out; | 109 | * If __GFP_FS is not present, then we may be |
110 | * being called from inside the fs writeback | ||
111 | * layer, so we MUST NOT fail. Since | ||
112 | * __GFP_NOFAIL is going away, we will arrange | ||
113 | * to retry the allocation ourselves. | ||
114 | */ | ||
115 | if ((gfp_mask & __GFP_FS) == 0) { | ||
116 | congestion_wait(BLK_RW_ASYNC, HZ/50); | ||
117 | goto alloc_transaction; | ||
118 | } | ||
119 | return -ENOMEM; | ||
110 | } | 120 | } |
111 | } | 121 | } |
112 | 122 | ||
@@ -123,8 +133,8 @@ repeat_locked: | |||
123 | if (is_journal_aborted(journal) || | 133 | if (is_journal_aborted(journal) || |
124 | (journal->j_errno != 0 && !(journal->j_flags & JBD2_ACK_ERR))) { | 134 | (journal->j_errno != 0 && !(journal->j_flags & JBD2_ACK_ERR))) { |
125 | spin_unlock(&journal->j_state_lock); | 135 | spin_unlock(&journal->j_state_lock); |
126 | ret = -EROFS; | 136 | kfree(new_transaction); |
127 | goto out; | 137 | return -EROFS; |
128 | } | 138 | } |
129 | 139 | ||
130 | /* Wait on the journal's transaction barrier if necessary */ | 140 | /* Wait on the journal's transaction barrier if necessary */ |
@@ -240,10 +250,8 @@ repeat_locked: | |||
240 | spin_unlock(&journal->j_state_lock); | 250 | spin_unlock(&journal->j_state_lock); |
241 | 251 | ||
242 | lock_map_acquire(&handle->h_lockdep_map); | 252 | lock_map_acquire(&handle->h_lockdep_map); |
243 | out: | 253 | kfree(new_transaction); |
244 | if (unlikely(new_transaction)) /* It's usually NULL */ | 254 | return 0; |
245 | kfree(new_transaction); | ||
246 | return ret; | ||
247 | } | 255 | } |
248 | 256 | ||
249 | static struct lock_class_key jbd2_handle_key; | 257 | static struct lock_class_key jbd2_handle_key; |
@@ -278,7 +286,7 @@ static handle_t *new_handle(int nblocks) | |||
278 | * | 286 | * |
279 | * Return a pointer to a newly allocated handle, or NULL on failure | 287 | * Return a pointer to a newly allocated handle, or NULL on failure |
280 | */ | 288 | */ |
281 | handle_t *jbd2_journal_start(journal_t *journal, int nblocks) | 289 | handle_t *jbd2__journal_start(journal_t *journal, int nblocks, int gfp_mask) |
282 | { | 290 | { |
283 | handle_t *handle = journal_current_handle(); | 291 | handle_t *handle = journal_current_handle(); |
284 | int err; | 292 | int err; |
@@ -298,7 +306,7 @@ handle_t *jbd2_journal_start(journal_t *journal, int nblocks) | |||
298 | 306 | ||
299 | current->journal_info = handle; | 307 | current->journal_info = handle; |
300 | 308 | ||
301 | err = start_this_handle(journal, handle); | 309 | err = start_this_handle(journal, handle, gfp_mask); |
302 | if (err < 0) { | 310 | if (err < 0) { |
303 | jbd2_free_handle(handle); | 311 | jbd2_free_handle(handle); |
304 | current->journal_info = NULL; | 312 | current->journal_info = NULL; |
@@ -308,6 +316,15 @@ handle_t *jbd2_journal_start(journal_t *journal, int nblocks) | |||
308 | out: | 316 | out: |
309 | return handle; | 317 | return handle; |
310 | } | 318 | } |
319 | EXPORT_SYMBOL(jbd2__journal_start); | ||
320 | |||
321 | |||
322 | handle_t *jbd2_journal_start(journal_t *journal, int nblocks) | ||
323 | { | ||
324 | return jbd2__journal_start(journal, nblocks, GFP_NOFS); | ||
325 | } | ||
326 | EXPORT_SYMBOL(jbd2_journal_start); | ||
327 | |||
311 | 328 | ||
312 | /** | 329 | /** |
313 | * int jbd2_journal_extend() - extend buffer credits. | 330 | * int jbd2_journal_extend() - extend buffer credits. |
@@ -394,8 +411,7 @@ out: | |||
394 | * transaction capabable of guaranteeing the requested number of | 411 | * transaction capabable of guaranteeing the requested number of |
395 | * credits. | 412 | * credits. |
396 | */ | 413 | */ |
397 | 414 | int jbd2__journal_restart(handle_t *handle, int nblocks, int gfp_mask) | |
398 | int jbd2_journal_restart(handle_t *handle, int nblocks) | ||
399 | { | 415 | { |
400 | transaction_t *transaction = handle->h_transaction; | 416 | transaction_t *transaction = handle->h_transaction; |
401 | journal_t *journal = transaction->t_journal; | 417 | journal_t *journal = transaction->t_journal; |
@@ -428,10 +444,17 @@ int jbd2_journal_restart(handle_t *handle, int nblocks) | |||
428 | 444 | ||
429 | lock_map_release(&handle->h_lockdep_map); | 445 | lock_map_release(&handle->h_lockdep_map); |
430 | handle->h_buffer_credits = nblocks; | 446 | handle->h_buffer_credits = nblocks; |
431 | ret = start_this_handle(journal, handle); | 447 | ret = start_this_handle(journal, handle, gfp_mask); |
432 | return ret; | 448 | return ret; |
433 | } | 449 | } |
450 | EXPORT_SYMBOL(jbd2__journal_restart); | ||
451 | |||
434 | 452 | ||
453 | int jbd2_journal_restart(handle_t *handle, int nblocks) | ||
454 | { | ||
455 | return jbd2__journal_restart(handle, nblocks, GFP_NOFS); | ||
456 | } | ||
457 | EXPORT_SYMBOL(jbd2_journal_restart); | ||
435 | 458 | ||
436 | /** | 459 | /** |
437 | * void jbd2_journal_lock_updates () - establish a transaction barrier. | 460 | * void jbd2_journal_lock_updates () - establish a transaction barrier. |
diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index a4d2e9f7088a..5a72bc75b273 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h | |||
@@ -1081,7 +1081,9 @@ static inline handle_t *journal_current_handle(void) | |||
1081 | */ | 1081 | */ |
1082 | 1082 | ||
1083 | extern handle_t *jbd2_journal_start(journal_t *, int nblocks); | 1083 | extern handle_t *jbd2_journal_start(journal_t *, int nblocks); |
1084 | extern int jbd2_journal_restart (handle_t *, int nblocks); | 1084 | extern handle_t *jbd2__journal_start(journal_t *, int nblocks, int gfp_mask); |
1085 | extern int jbd2_journal_restart(handle_t *, int nblocks); | ||
1086 | extern int jbd2__journal_restart(handle_t *, int nblocks, int gfp_mask); | ||
1085 | extern int jbd2_journal_extend (handle_t *, int nblocks); | 1087 | extern int jbd2_journal_extend (handle_t *, int nblocks); |
1086 | extern int jbd2_journal_get_write_access(handle_t *, struct buffer_head *); | 1088 | extern int jbd2_journal_get_write_access(handle_t *, struct buffer_head *); |
1087 | extern int jbd2_journal_get_create_access (handle_t *, struct buffer_head *); | 1089 | extern int jbd2_journal_get_create_access (handle_t *, struct buffer_head *); |