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

📄 transaction.c

📁 嵌入式系统设计与实例开发实验教材二源码 多线程应用程序设计 串行端口程序设计 AD接口实验 CAN总线通信实验 GPS通信实验 Linux内核移植与编译实验 IC卡读写实验 SD驱动使
💻 C
📖 第 1 页 / 共 5 页
字号:
/* * linux/fs/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/sched.h>#include <linux/fs.h>#include <linux/jbd.h>#include <linux/errno.h>#include <linux/slab.h>#include <linux/locks.h>#include <linux/timer.h>#include <linux/smp_lock.h>#include <linux/mm.h>extern spinlock_t journal_datalist_lock;/* * 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. */static transaction_t * get_transaction (journal_t * journal, int is_try){	transaction_t * transaction;	transaction = jbd_kmalloc (sizeof (transaction_t), GFP_NOFS);	if (!transaction)		return NULL;		memset (transaction, 0, sizeof (transaction_t));		transaction->t_journal = journal;	transaction->t_state = T_RUNNING;	transaction->t_tid = journal->j_transaction_sequence++;	transaction->t_expires = jiffies + journal->j_commit_interval;	/* Set up the commit timer for the new transaction. */	J_ASSERT (!journal->j_commit_timer_active);	journal->j_commit_timer_active = 1;	journal->j_commit_timer->expires = 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;		jbd_debug(3, "New handle %p going live.\n", handle);repeat:	lock_journal(journal);repeat_locked:	if (is_journal_aborted(journal) ||	    (journal->j_errno != 0 && !(journal->j_flags & JFS_ACK_ERR))) {		unlock_journal(journal);		return -EROFS; 	}	/* Wait on the journal's transaction barrier if necessary */	if (journal->j_barrier_count) {		unlock_journal(journal);		sleep_on(&journal->j_wait_transaction_locked);		goto repeat;	}		if (!journal->j_running_transaction)		get_transaction(journal, 0);	/* @@@ Error? */	J_ASSERT(journal->j_running_transaction);		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) {		unlock_journal(journal);		jbd_debug(3, "Handle %p stalling...\n", handle);		sleep_on(&journal->j_wait_transaction_locked);		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. */	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. */				jbd_debug(2, "Handle %p starting new commit...\n", handle);		log_start_commit(journal, transaction);		unlock_journal(journal);		sleep_on(&journal->j_wait_transaction_locked);		lock_journal(journal);		goto repeat_locked;	}	/* 	 * 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().	 */	needed = journal->j_max_transaction_buffers;	if (journal->j_committing_transaction) 		needed += journal->j_committing_transaction->					t_outstanding_credits;		if (log_space_left(journal) < needed) {		jbd_debug(2, "Handle %p waiting for checkpoint...\n", handle);		log_wait_for_space(journal, needed);		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));	unlock_journal(journal);		return 0;}/* * Obtain a new handle.   * * 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 ext2fs), 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 = jbd_kmalloc(sizeof (handle_t), GFP_NOFS);	if (!handle)		return ERR_PTR(-ENOMEM);	memset (handle, 0, sizeof (handle_t));	handle->h_buffer_credits = nblocks;	handle->h_ref = 1;	current->journal_info = handle;	err = start_this_handle(journal, handle);	if (err < 0) {		kfree(handle);		current->journal_info = NULL;		return ERR_PTR(err);	}	return handle;}/* * Return zero on success */static int try_start_this_handle(journal_t *journal, handle_t *handle){	transaction_t *transaction;	int needed;	int nblocks = handle->h_buffer_credits;	int ret = 0;	jbd_debug(3, "New handle %p maybe going live.\n", handle);	lock_journal(journal);	if (is_journal_aborted(journal) ||	    (journal->j_errno != 0 && !(journal->j_flags & JFS_ACK_ERR))) {		ret = -EROFS;		goto fail_unlock;	}	if (journal->j_barrier_count)		goto fail_unlock;	if (!journal->j_running_transaction && get_transaction(journal, 1) == 0)		goto fail_unlock;		transaction = journal->j_running_transaction;	if (transaction->t_state == T_LOCKED)		goto fail_unlock;		needed = transaction->t_outstanding_credits + nblocks;	/* We could run log_start_commit here */	if (needed > journal->j_max_transaction_buffers)		goto fail_unlock;	needed = journal->j_max_transaction_buffers;	if (journal->j_committing_transaction) 		needed += journal->j_committing_transaction->						t_outstanding_credits;		if (log_space_left(journal) < needed)		goto fail_unlock;	handle->h_transaction = transaction;	transaction->t_outstanding_credits += nblocks;	transaction->t_updates++;	jbd_debug(4, "Handle %p given %d credits (total %d, free %d)\n",		  handle, nblocks, transaction->t_outstanding_credits,		  log_space_left(journal));	unlock_journal(journal);	return 0;fail_unlock:	unlock_journal(journal);	if (ret >= 0)		ret = -1;	return ret;}/* * Try to start a handle, but non-blockingly.  If we weren't able * to, return an ERR_PTR value. */handle_t *journal_try_start(journal_t *journal, int nblocks){	handle_t *handle = journal_current_handle();	int err;		if (!journal)		return ERR_PTR(-EROFS);	if (handle) {		jbd_debug(4, "h_ref %d -> %d\n",				handle->h_ref,				handle->h_ref + 1);		J_ASSERT(handle->h_transaction->t_journal == journal);		if (is_handle_aborted(handle))			return ERR_PTR(-EIO);		handle->h_ref++;		return handle;	} else {		jbd_debug(4, "no current transaction\n");	}		if (is_journal_aborted(journal))		return ERR_PTR(-EIO);		handle = jbd_kmalloc(sizeof (handle_t), GFP_NOFS);	if (!handle)		return ERR_PTR(-ENOMEM);	memset (handle, 0, sizeof (handle_t));	handle->h_buffer_credits = nblocks;	handle->h_ref = 1;	current->journal_info = handle;	err = try_start_this_handle(journal, handle);	if (err < 0) {		kfree(handle);		current->journal_info = NULL;		return ERR_PTR(err);	}	return handle;}/* * journal_extend: extend buffer credits. * * 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;	lock_journal (journal);	result = -EIO;	if (is_handle_aborted(handle))		goto error_out;	result = 1;	       	/* 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;	}		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 error_out;	}	if (wanted > log_space_left(journal)) {		jbd_debug(3, "denied handle %p %d blocks: "			  "insufficient log space\n", handle, nblocks);		goto error_out;	}		handle->h_buffer_credits += nblocks;	transaction->t_outstanding_credits += nblocks;

⌨️ 快捷键说明

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