⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 transaction.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* * linux/fs/jbd/transaction.c * * Written by Stephen C. Tweedie <sct@redhat.com>, 1998 * * Copyright 1998 Red Hat corp --- All Rights Reserved * * This file is part of the Linux kernel and is made available under * the terms of the GNU General Public License, version 2, or at your * option, any later version, incorporated herein by reference. * * Generic filesystem transaction handling code; part of the ext2fs * journaling system. * * This file manages transactions (compound commits managed by the * journaling code) and handles (individual atomic operations by the * filesystem). */#include <linux/time.h>#include <linux/fs.h>#include <linux/jbd.h>#include <linux/errno.h>#include <linux/slab.h>#include <linux/timer.h>#include <linux/mm.h>#include <linux/highmem.h>static void __journal_temp_unlink_buffer(struct journal_head *jh);/* * get_transaction: obtain a new transaction_t object. * * Simply allocate and initialise a new transaction.  Create it in * RUNNING state and add it to the current journal (which should not * have an existing running transaction: we only make a new transaction * once we have started to commit the old one). * * Preconditions: *	The journal MUST be locked.  We don't perform atomic mallocs on the *	new transaction	and we can't block without protecting against other *	processes trying to touch the journal while it is in transition. * * Called under j_state_lock */static transaction_t *get_transaction(journal_t *journal, transaction_t *transaction){	transaction->t_journal = journal;	transaction->t_state = T_RUNNING;	transaction->t_tid = journal->j_transaction_sequence++;	transaction->t_expires = jiffies + journal->j_commit_interval;	spin_lock_init(&transaction->t_handle_lock);	/* Set up the commit timer for the new transaction. */	journal->j_commit_timer.expires = round_jiffies(transaction->t_expires);	add_timer(&journal->j_commit_timer);	J_ASSERT(journal->j_running_transaction == NULL);	journal->j_running_transaction = transaction;	return transaction;}/* * Handle management. * * A handle_t is an object which represents a single atomic update to a * filesystem, and which tracks all of the modifications which form part * of that one update. *//* * start_this_handle: Given a handle, deal with any locking or stalling * needed to make sure that there is enough journal space for the handle * to begin.  Attach the handle to a transaction and set up the * transaction's buffer credits. */static int start_this_handle(journal_t *journal, handle_t *handle){	transaction_t *transaction;	int needed;	int nblocks = handle->h_buffer_credits;	transaction_t *new_transaction = NULL;	int ret = 0;	if (nblocks > journal->j_max_transaction_buffers) {		printk(KERN_ERR "JBD: %s wants too many credits (%d > %d)\n",		       current->comm, nblocks,		       journal->j_max_transaction_buffers);		ret = -ENOSPC;		goto out;	}alloc_transaction:	if (!journal->j_running_transaction) {		new_transaction = kzalloc(sizeof(*new_transaction),						GFP_NOFS|__GFP_NOFAIL);		if (!new_transaction) {			ret = -ENOMEM;			goto out;		}	}	jbd_debug(3, "New handle %p going live.\n", handle);repeat:	/*	 * We need to hold j_state_lock until t_updates has been incremented,	 * for proper journal barrier handling	 */	spin_lock(&journal->j_state_lock);repeat_locked:	if (is_journal_aborted(journal) ||	    (journal->j_errno != 0 && !(journal->j_flags & JFS_ACK_ERR))) {		spin_unlock(&journal->j_state_lock);		ret = -EROFS;		goto out;	}	/* Wait on the journal's transaction barrier if necessary */	if (journal->j_barrier_count) {		spin_unlock(&journal->j_state_lock);		wait_event(journal->j_wait_transaction_locked,				journal->j_barrier_count == 0);		goto repeat;	}	if (!journal->j_running_transaction) {		if (!new_transaction) {			spin_unlock(&journal->j_state_lock);			goto alloc_transaction;		}		get_transaction(journal, new_transaction);		new_transaction = NULL;	}	transaction = journal->j_running_transaction;	/*	 * If the current transaction is locked down for commit, wait for the	 * lock to be released.	 */	if (transaction->t_state == T_LOCKED) {		DEFINE_WAIT(wait);		prepare_to_wait(&journal->j_wait_transaction_locked,					&wait, TASK_UNINTERRUPTIBLE);		spin_unlock(&journal->j_state_lock);		schedule();		finish_wait(&journal->j_wait_transaction_locked, &wait);		goto repeat;	}	/*	 * If there is not enough space left in the log to write all potential	 * buffers requested by this operation, we need to stall pending a log	 * checkpoint to free some more log space.	 */	spin_lock(&transaction->t_handle_lock);	needed = transaction->t_outstanding_credits + nblocks;	if (needed > journal->j_max_transaction_buffers) {		/*		 * If the current transaction is already too large, then start		 * to commit it: we can then go back and attach this handle to		 * a new transaction.		 */		DEFINE_WAIT(wait);		jbd_debug(2, "Handle %p starting new commit...\n", handle);		spin_unlock(&transaction->t_handle_lock);		prepare_to_wait(&journal->j_wait_transaction_locked, &wait,				TASK_UNINTERRUPTIBLE);		__log_start_commit(journal, transaction->t_tid);		spin_unlock(&journal->j_state_lock);		schedule();		finish_wait(&journal->j_wait_transaction_locked, &wait);		goto repeat;	}	/*	 * The commit code assumes that it can get enough log space	 * without forcing a checkpoint.  This is *critical* for	 * correctness: a checkpoint of a buffer which is also	 * associated with a committing transaction creates a deadlock,	 * so commit simply cannot force through checkpoints.	 *	 * We must therefore ensure the necessary space in the journal	 * *before* starting to dirty potentially checkpointed buffers	 * in the new transaction.	 *	 * The worst part is, any transaction currently committing can	 * reduce the free space arbitrarily.  Be careful to account for	 * those buffers when checkpointing.	 */	/*	 * @@@ AKPM: This seems rather over-defensive.  We're giving commit	 * a _lot_ of headroom: 1/4 of the journal plus the size of	 * the committing transaction.  Really, we only need to give it	 * committing_transaction->t_outstanding_credits plus "enough" for	 * the log control blocks.	 * Also, this test is inconsitent with the matching one in	 * journal_extend().	 */	if (__log_space_left(journal) < jbd_space_needed(journal)) {		jbd_debug(2, "Handle %p waiting for checkpoint...\n", handle);		spin_unlock(&transaction->t_handle_lock);		__log_wait_for_space(journal);		goto repeat_locked;	}	/* OK, account for the buffers that this operation expects to	 * use and add the handle to the running transaction. */	handle->h_transaction = transaction;	transaction->t_outstanding_credits += nblocks;	transaction->t_updates++;	transaction->t_handle_count++;	jbd_debug(4, "Handle %p given %d credits (total %d, free %d)\n",		  handle, nblocks, transaction->t_outstanding_credits,		  __log_space_left(journal));	spin_unlock(&transaction->t_handle_lock);	spin_unlock(&journal->j_state_lock);out:	if (unlikely(new_transaction))		/* It's usually NULL */		kfree(new_transaction);	return ret;}static struct lock_class_key jbd_handle_key;/* Allocate a new handle.  This should probably be in a slab... */static handle_t *new_handle(int nblocks){	handle_t *handle = jbd_alloc_handle(GFP_NOFS);	if (!handle)		return NULL;	memset(handle, 0, sizeof(*handle));	handle->h_buffer_credits = nblocks;	handle->h_ref = 1;	lockdep_init_map(&handle->h_lockdep_map, "jbd_handle", &jbd_handle_key, 0);	return handle;}/** * handle_t *journal_start() - Obtain a new handle. * @journal: Journal to start transaction on. * @nblocks: number of block buffer we might modify * * We make sure that the transaction can guarantee at least nblocks of * modified buffers in the log.  We block until the log can guarantee * that much space. * * This function is visible to journal users (like ext3fs), so is not * called with the journal already locked. * * Return a pointer to a newly allocated handle, or NULL on failure */handle_t *journal_start(journal_t *journal, int nblocks){	handle_t *handle = journal_current_handle();	int err;	if (!journal)		return ERR_PTR(-EROFS);	if (handle) {		J_ASSERT(handle->h_transaction->t_journal == journal);		handle->h_ref++;		return handle;	}	handle = new_handle(nblocks);	if (!handle)		return ERR_PTR(-ENOMEM);	current->journal_info = handle;	err = start_this_handle(journal, handle);	if (err < 0) {		jbd_free_handle(handle);		current->journal_info = NULL;		handle = ERR_PTR(err);		goto out;	}	lock_acquire(&handle->h_lockdep_map, 0, 0, 0, 2, _THIS_IP_);out:	return handle;}/** * int journal_extend() - extend buffer credits. * @handle:  handle to 'extend' * @nblocks: nr blocks to try to extend by. * * Some transactions, such as large extends and truncates, can be done * atomically all at once or in several stages.  The operation requests * a credit for a number of buffer modications in advance, but can * extend its credit if it needs more. * * journal_extend tries to give the running handle more buffer credits. * It does not guarantee that allocation - this is a best-effort only. * The calling process MUST be able to deal cleanly with a failure to * extend here. * * Return 0 on success, non-zero on failure. * * return code < 0 implies an error * return code > 0 implies normal transaction-full status. */int journal_extend(handle_t *handle, int nblocks){	transaction_t *transaction = handle->h_transaction;	journal_t *journal = transaction->t_journal;	int result;	int wanted;	result = -EIO;	if (is_handle_aborted(handle))		goto out;	result = 1;	spin_lock(&journal->j_state_lock);	/* Don't extend a locked-down transaction! */	if (handle->h_transaction->t_state != T_RUNNING) {		jbd_debug(3, "denied handle %p %d blocks: "			  "transaction not running\n", handle, nblocks);		goto error_out;	}	spin_lock(&transaction->t_handle_lock);	wanted = transaction->t_outstanding_credits + nblocks;	if (wanted > journal->j_max_transaction_buffers) {		jbd_debug(3, "denied handle %p %d blocks: "			  "transaction too large\n", handle, nblocks);		goto unlock;	}	if (wanted > __log_space_left(journal)) {		jbd_debug(3, "denied handle %p %d blocks: "			  "insufficient log space\n", handle, nblocks);		goto unlock;	}	handle->h_buffer_credits += nblocks;	transaction->t_outstanding_credits += nblocks;	result = 0;	jbd_debug(3, "extended handle %p by %d\n", handle, nblocks);unlock:	spin_unlock(&transaction->t_handle_lock);error_out:	spin_unlock(&journal->j_state_lock);out:	return result;}/** * int journal_restart() - restart a handle . * @handle:  handle to restart * @nblocks: nr credits requested * * Restart a handle for a multi-transaction filesystem * operation. * * If the journal_extend() call above fails to grant new buffer credits * to a running handle, a call to journal_restart will commit the * handle's transaction so far and reattach the handle to a new * transaction capabable of guaranteeing the requested number of * credits. */int journal_restart(handle_t *handle, int nblocks){	transaction_t *transaction = handle->h_transaction;	journal_t *journal = transaction->t_journal;	int ret;	/* If we've had an abort of any type, don't even think about	 * actually doing the restart! */	if (is_handle_aborted(handle))		return 0;	/*	 * First unlink the handle from its current transaction, and start the	 * commit on that.	 */	J_ASSERT(transaction->t_updates > 0);	J_ASSERT(journal_current_handle() == handle);	spin_lock(&journal->j_state_lock);	spin_lock(&transaction->t_handle_lock);	transaction->t_outstanding_credits -= handle->h_buffer_credits;	transaction->t_updates--;	if (!transaction->t_updates)		wake_up(&journal->j_wait_updates);	spin_unlock(&transaction->t_handle_lock);	jbd_debug(2, "restarting handle %p\n", handle);	__log_start_commit(journal, transaction->t_tid);	spin_unlock(&journal->j_state_lock);	handle->h_buffer_credits = nblocks;	ret = start_this_handle(journal, handle);	return ret;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -