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>
This commit is contained in:
		
					parent
					
						
							
								4038968738
							
						
					
				
			
			
				commit
				
					
						47def82672
					
				
			
		
					 3 changed files with 57 additions and 23 deletions
				
			
		| 
						 | 
					@ -41,6 +41,7 @@
 | 
				
			||||||
#include <linux/hash.h>
 | 
					#include <linux/hash.h>
 | 
				
			||||||
#include <linux/log2.h>
 | 
					#include <linux/log2.h>
 | 
				
			||||||
#include <linux/vmalloc.h>
 | 
					#include <linux/vmalloc.h>
 | 
				
			||||||
 | 
					#include <linux/backing-dev.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define CREATE_TRACE_POINTS
 | 
					#define CREATE_TRACE_POINTS
 | 
				
			||||||
#include <trace/events/jbd2.h>
 | 
					#include <trace/events/jbd2.h>
 | 
				
			||||||
| 
						 | 
					@ -48,8 +49,6 @@
 | 
				
			||||||
#include <asm/uaccess.h>
 | 
					#include <asm/uaccess.h>
 | 
				
			||||||
#include <asm/page.h>
 | 
					#include <asm/page.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EXPORT_SYMBOL(jbd2_journal_start);
 | 
					 | 
				
			||||||
EXPORT_SYMBOL(jbd2_journal_restart);
 | 
					 | 
				
			||||||
EXPORT_SYMBOL(jbd2_journal_extend);
 | 
					EXPORT_SYMBOL(jbd2_journal_extend);
 | 
				
			||||||
EXPORT_SYMBOL(jbd2_journal_stop);
 | 
					EXPORT_SYMBOL(jbd2_journal_stop);
 | 
				
			||||||
EXPORT_SYMBOL(jbd2_journal_lock_updates);
 | 
					EXPORT_SYMBOL(jbd2_journal_lock_updates);
 | 
				
			||||||
| 
						 | 
					@ -311,7 +310,17 @@ int jbd2_journal_write_metadata_buffer(transaction_t *transaction,
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	J_ASSERT_BH(bh_in, buffer_jbddirty(bh_in));
 | 
						J_ASSERT_BH(bh_in, buffer_jbddirty(bh_in));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	new_bh = alloc_buffer_head(GFP_NOFS|__GFP_NOFAIL);
 | 
					retry_alloc:
 | 
				
			||||||
 | 
						new_bh = alloc_buffer_head(GFP_NOFS);
 | 
				
			||||||
 | 
						if (!new_bh) {
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * Failure is not an option, but __GFP_NOFAIL is going
 | 
				
			||||||
 | 
							 * away; so we retry ourselves here.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							congestion_wait(BLK_RW_ASYNC, HZ/50);
 | 
				
			||||||
 | 
							goto retry_alloc;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* keep subsequent assertions sane */
 | 
						/* keep subsequent assertions sane */
 | 
				
			||||||
	new_bh->b_state = 0;
 | 
						new_bh->b_state = 0;
 | 
				
			||||||
	init_buffer(new_bh, NULL, NULL);
 | 
						init_buffer(new_bh, NULL, NULL);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,6 +26,8 @@
 | 
				
			||||||
#include <linux/mm.h>
 | 
					#include <linux/mm.h>
 | 
				
			||||||
#include <linux/highmem.h>
 | 
					#include <linux/highmem.h>
 | 
				
			||||||
#include <linux/hrtimer.h>
 | 
					#include <linux/hrtimer.h>
 | 
				
			||||||
 | 
					#include <linux/backing-dev.h>
 | 
				
			||||||
 | 
					#include <linux/module.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void __jbd2_journal_temp_unlink_buffer(struct journal_head *jh);
 | 
					static void __jbd2_journal_temp_unlink_buffer(struct journal_head *jh);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -83,30 +85,38 @@ jbd2_get_transaction(journal_t *journal, transaction_t *transaction)
 | 
				
			||||||
 * transaction's buffer credits.
 | 
					 * transaction's buffer credits.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int start_this_handle(journal_t *journal, handle_t *handle)
 | 
					static int start_this_handle(journal_t *journal, handle_t *handle,
 | 
				
			||||||
 | 
								     int gfp_mask)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	transaction_t *transaction;
 | 
						transaction_t *transaction;
 | 
				
			||||||
	int needed;
 | 
						int needed;
 | 
				
			||||||
	int nblocks = handle->h_buffer_credits;
 | 
						int nblocks = handle->h_buffer_credits;
 | 
				
			||||||
	transaction_t *new_transaction = NULL;
 | 
						transaction_t *new_transaction = NULL;
 | 
				
			||||||
	int ret = 0;
 | 
					 | 
				
			||||||
	unsigned long ts = jiffies;
 | 
						unsigned long ts = jiffies;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (nblocks > journal->j_max_transaction_buffers) {
 | 
						if (nblocks > journal->j_max_transaction_buffers) {
 | 
				
			||||||
		printk(KERN_ERR "JBD: %s wants too many credits (%d > %d)\n",
 | 
							printk(KERN_ERR "JBD: %s wants too many credits (%d > %d)\n",
 | 
				
			||||||
		       current->comm, nblocks,
 | 
							       current->comm, nblocks,
 | 
				
			||||||
		       journal->j_max_transaction_buffers);
 | 
							       journal->j_max_transaction_buffers);
 | 
				
			||||||
		ret = -ENOSPC;
 | 
							return -ENOSPC;
 | 
				
			||||||
		goto out;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
alloc_transaction:
 | 
					alloc_transaction:
 | 
				
			||||||
	if (!journal->j_running_transaction) {
 | 
						if (!journal->j_running_transaction) {
 | 
				
			||||||
		new_transaction = kzalloc(sizeof(*new_transaction),
 | 
							new_transaction = kzalloc(sizeof(*new_transaction), gfp_mask);
 | 
				
			||||||
						GFP_NOFS|__GFP_NOFAIL);
 | 
					 | 
				
			||||||
		if (!new_transaction) {
 | 
							if (!new_transaction) {
 | 
				
			||||||
			ret = -ENOMEM;
 | 
								/*
 | 
				
			||||||
			goto out;
 | 
								 * If __GFP_FS is not present, then we may be
 | 
				
			||||||
 | 
								 * being called from inside the fs writeback
 | 
				
			||||||
 | 
								 * layer, so we MUST NOT fail.  Since
 | 
				
			||||||
 | 
								 * __GFP_NOFAIL is going away, we will arrange
 | 
				
			||||||
 | 
								 * to retry the allocation ourselves.
 | 
				
			||||||
 | 
								 */
 | 
				
			||||||
 | 
								if ((gfp_mask & __GFP_FS) == 0) {
 | 
				
			||||||
 | 
									congestion_wait(BLK_RW_ASYNC, HZ/50);
 | 
				
			||||||
 | 
									goto alloc_transaction;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return -ENOMEM;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -123,8 +133,8 @@ repeat_locked:
 | 
				
			||||||
	if (is_journal_aborted(journal) ||
 | 
						if (is_journal_aborted(journal) ||
 | 
				
			||||||
	    (journal->j_errno != 0 && !(journal->j_flags & JBD2_ACK_ERR))) {
 | 
						    (journal->j_errno != 0 && !(journal->j_flags & JBD2_ACK_ERR))) {
 | 
				
			||||||
		spin_unlock(&journal->j_state_lock);
 | 
							spin_unlock(&journal->j_state_lock);
 | 
				
			||||||
		ret = -EROFS;
 | 
							kfree(new_transaction);
 | 
				
			||||||
		goto out;
 | 
							return -EROFS;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Wait on the journal's transaction barrier if necessary */
 | 
						/* Wait on the journal's transaction barrier if necessary */
 | 
				
			||||||
| 
						 | 
					@ -240,10 +250,8 @@ repeat_locked:
 | 
				
			||||||
	spin_unlock(&journal->j_state_lock);
 | 
						spin_unlock(&journal->j_state_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	lock_map_acquire(&handle->h_lockdep_map);
 | 
						lock_map_acquire(&handle->h_lockdep_map);
 | 
				
			||||||
out:
 | 
						kfree(new_transaction);
 | 
				
			||||||
	if (unlikely(new_transaction))		/* It's usually NULL */
 | 
						return 0;
 | 
				
			||||||
		kfree(new_transaction);
 | 
					 | 
				
			||||||
	return ret;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct lock_class_key jbd2_handle_key;
 | 
					static struct lock_class_key jbd2_handle_key;
 | 
				
			||||||
| 
						 | 
					@ -278,7 +286,7 @@ static handle_t *new_handle(int nblocks)
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Return a pointer to a newly allocated handle, or NULL on failure
 | 
					 * Return a pointer to a newly allocated handle, or NULL on failure
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
handle_t *jbd2_journal_start(journal_t *journal, int nblocks)
 | 
					handle_t *jbd2__journal_start(journal_t *journal, int nblocks, int gfp_mask)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	handle_t *handle = journal_current_handle();
 | 
						handle_t *handle = journal_current_handle();
 | 
				
			||||||
	int err;
 | 
						int err;
 | 
				
			||||||
| 
						 | 
					@ -298,7 +306,7 @@ handle_t *jbd2_journal_start(journal_t *journal, int nblocks)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	current->journal_info = handle;
 | 
						current->journal_info = handle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = start_this_handle(journal, handle);
 | 
						err = start_this_handle(journal, handle, gfp_mask);
 | 
				
			||||||
	if (err < 0) {
 | 
						if (err < 0) {
 | 
				
			||||||
		jbd2_free_handle(handle);
 | 
							jbd2_free_handle(handle);
 | 
				
			||||||
		current->journal_info = NULL;
 | 
							current->journal_info = NULL;
 | 
				
			||||||
| 
						 | 
					@ -308,6 +316,15 @@ handle_t *jbd2_journal_start(journal_t *journal, int nblocks)
 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
	return handle;
 | 
						return handle;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL(jbd2__journal_start);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					handle_t *jbd2_journal_start(journal_t *journal, int nblocks)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return jbd2__journal_start(journal, nblocks, GFP_NOFS);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL(jbd2_journal_start);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * int jbd2_journal_extend() - extend buffer credits.
 | 
					 * int jbd2_journal_extend() - extend buffer credits.
 | 
				
			||||||
| 
						 | 
					@ -394,8 +411,7 @@ out:
 | 
				
			||||||
 * transaction capabable of guaranteeing the requested number of
 | 
					 * transaction capabable of guaranteeing the requested number of
 | 
				
			||||||
 * credits.
 | 
					 * credits.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					int jbd2__journal_restart(handle_t *handle, int nblocks, int gfp_mask)
 | 
				
			||||||
int jbd2_journal_restart(handle_t *handle, int nblocks)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	transaction_t *transaction = handle->h_transaction;
 | 
						transaction_t *transaction = handle->h_transaction;
 | 
				
			||||||
	journal_t *journal = transaction->t_journal;
 | 
						journal_t *journal = transaction->t_journal;
 | 
				
			||||||
| 
						 | 
					@ -428,11 +444,18 @@ int jbd2_journal_restart(handle_t *handle, int nblocks)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	lock_map_release(&handle->h_lockdep_map);
 | 
						lock_map_release(&handle->h_lockdep_map);
 | 
				
			||||||
	handle->h_buffer_credits = nblocks;
 | 
						handle->h_buffer_credits = nblocks;
 | 
				
			||||||
	ret = start_this_handle(journal, handle);
 | 
						ret = start_this_handle(journal, handle, gfp_mask);
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL(jbd2__journal_restart);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int jbd2_journal_restart(handle_t *handle, int nblocks)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return jbd2__journal_restart(handle, nblocks, GFP_NOFS);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL(jbd2_journal_restart);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * void jbd2_journal_lock_updates () - establish a transaction barrier.
 | 
					 * void jbd2_journal_lock_updates () - establish a transaction barrier.
 | 
				
			||||||
 * @journal:  Journal to establish a barrier on.
 | 
					 * @journal:  Journal to establish a barrier on.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1081,7 +1081,9 @@ static inline handle_t *journal_current_handle(void)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern handle_t *jbd2_journal_start(journal_t *, int nblocks);
 | 
					extern handle_t *jbd2_journal_start(journal_t *, int nblocks);
 | 
				
			||||||
extern int	 jbd2_journal_restart (handle_t *, int nblocks);
 | 
					extern handle_t *jbd2__journal_start(journal_t *, int nblocks, int gfp_mask);
 | 
				
			||||||
 | 
					extern int	 jbd2_journal_restart(handle_t *, int nblocks);
 | 
				
			||||||
 | 
					extern int	 jbd2__journal_restart(handle_t *, int nblocks, int gfp_mask);
 | 
				
			||||||
extern int	 jbd2_journal_extend (handle_t *, int nblocks);
 | 
					extern int	 jbd2_journal_extend (handle_t *, int nblocks);
 | 
				
			||||||
extern int	 jbd2_journal_get_write_access(handle_t *, struct buffer_head *);
 | 
					extern int	 jbd2_journal_get_write_access(handle_t *, struct buffer_head *);
 | 
				
			||||||
extern int	 jbd2_journal_get_create_access (handle_t *, struct buffer_head *);
 | 
					extern int	 jbd2_journal_get_create_access (handle_t *, struct buffer_head *);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue