journal.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,967 行 · 第 1/4 页

C
1,967
字号
 * * Journal abort has very specific semantics.  Any existing dirty, * unjournaled buffers in the main filesystem will still be written to * disk by bdflush, but the journaling mechanism will be suspended * immediately and no further transaction commits will be honoured. * * Any dirty, journaled buffers will be written back to disk without * hitting the journal.  Atomicity cannot be guaranteed on an aborted * filesystem, but we _do_ attempt to leave as much data as possible * behind for fsck to use for cleanup. * * Any attempt to get a new transaction handle on a journal which is in * ABORT state will just result in an -EROFS error return.  A * journal_stop on an existing handle will return -EIO if we have * entered abort state during the update. * * Recursive transactions are not disturbed by journal abort until the * final journal_stop, which will receive the -EIO error. * * Finally, the journal_abort call allows the caller to supply an errno * which will be recorded (if possible) in the journal superblock.  This * allows a client to record failure conditions in the middle of a * transaction without having to complete the transaction to record the * failure to disk.  ext3_error, for example, now uses this * functionality. * * Errors which originate from within the journaling layer will NOT * supply an errno; a null errno implies that absolutely no further * writes are done to the journal (unless there are any already in * progress). *  */void journal_abort(journal_t *journal, int errno){	__journal_abort_soft(journal, errno);}/**  * int journal_errno () - returns the journal's error state. * @journal: journal to examine. * * This is the errno numbet set with journal_abort(), the last * time the journal was mounted - if the journal was stopped * without calling abort this will be 0. * * If the journal has been aborted on this mount time -EROFS will * be returned. */int journal_errno(journal_t *journal){	int err;	spin_lock(&journal->j_state_lock);	if (journal->j_flags & JFS_ABORT)		err = -EROFS;	else		err = journal->j_errno;	spin_unlock(&journal->j_state_lock);	return err;}/**  * int journal_clear_err () - clears the journal's error state * * An error must be cleared or Acked to take a FS out of readonly * mode. */int journal_clear_err(journal_t *journal){	int err = 0;	spin_lock(&journal->j_state_lock);	if (journal->j_flags & JFS_ABORT)		err = -EROFS;	else		journal->j_errno = 0;	spin_unlock(&journal->j_state_lock);	return err;}/**  * void journal_ack_err() - Ack journal err. * * An error must be cleared or Acked to take a FS out of readonly * mode. */void journal_ack_err(journal_t *journal){	spin_lock(&journal->j_state_lock);	if (journal->j_errno)		journal->j_flags |= JFS_ACK_ERR;	spin_unlock(&journal->j_state_lock);}int journal_blocks_per_page(struct inode *inode){	return 1 << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits);}/* * Simple support for retying memory allocations.  Introduced to help to * debug different VM deadlock avoidance strategies.  *//* * Simple support for retying memory allocations.  Introduced to help to * debug different VM deadlock avoidance strategies.  */void * __jbd_kmalloc (const char *where, size_t size, int flags, int retry){	return kmalloc(size, flags | (retry ? __GFP_NOFAIL : 0));}/* * Journal_head storage management */static kmem_cache_t *journal_head_cache;#ifdef CONFIG_JBD_DEBUGstatic atomic_t nr_journal_heads = ATOMIC_INIT(0);#endifstatic int journal_init_journal_head_cache(void){	int retval;	J_ASSERT(journal_head_cache == 0);	journal_head_cache = kmem_cache_create("journal_head",				sizeof(struct journal_head),				0,		/* offset */				0,		/* flags */				NULL,		/* ctor */				NULL);		/* dtor */	retval = 0;	if (journal_head_cache == 0) {		retval = -ENOMEM;		printk(KERN_EMERG "JBD: no memory for journal_head cache\n");	}	return retval;}static void journal_destroy_journal_head_cache(void){	J_ASSERT(journal_head_cache != NULL);	kmem_cache_destroy(journal_head_cache);	journal_head_cache = NULL;}/* * journal_head splicing and dicing */static struct journal_head *journal_alloc_journal_head(void){	struct journal_head *ret;	static unsigned long last_warning;#ifdef CONFIG_JBD_DEBUG	atomic_inc(&nr_journal_heads);#endif	ret = kmem_cache_alloc(journal_head_cache, GFP_NOFS);	if (ret == 0) {		jbd_debug(1, "out of memory for journal_head\n");		if (time_after(jiffies, last_warning + 5*HZ)) {			printk(KERN_NOTICE "ENOMEM in %s, retrying.\n",			       __FUNCTION__);			last_warning = jiffies;		}		while (ret == 0) {			yield();			ret = kmem_cache_alloc(journal_head_cache, GFP_NOFS);		}	}	return ret;}static void journal_free_journal_head(struct journal_head *jh){#ifdef CONFIG_JBD_DEBUG	atomic_dec(&nr_journal_heads);	memset(jh, 0x5b, sizeof(*jh));#endif	kmem_cache_free(journal_head_cache, jh);}/* * A journal_head is attached to a buffer_head whenever JBD has an * interest in the buffer. * * Whenever a buffer has an attached journal_head, its ->b_state:BH_JBD bit * is set.  This bit is tested in core kernel code where we need to take * JBD-specific actions.  Testing the zeroness of ->b_private is not reliable * there. * * When a buffer has its BH_JBD bit set, its ->b_count is elevated by one. * * When a buffer has its BH_JBD bit set it is immune from being released by * core kernel code, mainly via ->b_count. * * A journal_head may be detached from its buffer_head when the journal_head's * b_transaction, b_cp_transaction and b_next_transaction pointers are NULL. * Various places in JBD call journal_remove_journal_head() to indicate that the * journal_head can be dropped if needed. * * Various places in the kernel want to attach a journal_head to a buffer_head * _before_ attaching the journal_head to a transaction.  To protect the * journal_head in this situation, journal_add_journal_head elevates the * journal_head's b_jcount refcount by one.  The caller must call * journal_put_journal_head() to undo this. * * So the typical usage would be: * *	(Attach a journal_head if needed.  Increments b_jcount) *	struct journal_head *jh = journal_add_journal_head(bh); *	... *	jh->b_transaction = xxx; *	journal_put_journal_head(jh); * * Now, the journal_head's b_jcount is zero, but it is safe from being released * because it has a non-zero b_transaction. *//* * Give a buffer_head a journal_head. * * Doesn't need the journal lock. * May sleep. */struct journal_head *journal_add_journal_head(struct buffer_head *bh){	struct journal_head *jh;	struct journal_head *new_jh = NULL;repeat:	if (!buffer_jbd(bh)) {		new_jh = journal_alloc_journal_head();		memset(new_jh, 0, sizeof(*new_jh));	}	jbd_lock_bh_journal_head(bh);	if (buffer_jbd(bh)) {		jh = bh2jh(bh);	} else {		J_ASSERT_BH(bh,			(atomic_read(&bh->b_count) > 0) ||			(bh->b_page && bh->b_page->mapping));		if (!new_jh) {			jbd_unlock_bh_journal_head(bh);			goto repeat;		}		jh = new_jh;		new_jh = NULL;		/* We consumed it */		set_buffer_jbd(bh);		bh->b_private = jh;		jh->b_bh = bh;		get_bh(bh);		BUFFER_TRACE(bh, "added journal_head");	}	jh->b_jcount++;	jbd_unlock_bh_journal_head(bh);	if (new_jh)		journal_free_journal_head(new_jh);	return bh->b_private;}/* * Grab a ref against this buffer_head's journal_head.  If it ended up not * having a journal_head, return NULL */struct journal_head *journal_grab_journal_head(struct buffer_head *bh){	struct journal_head *jh = NULL;	jbd_lock_bh_journal_head(bh);	if (buffer_jbd(bh)) {		jh = bh2jh(bh);		jh->b_jcount++;	}	jbd_unlock_bh_journal_head(bh);	return jh;}static void __journal_remove_journal_head(struct buffer_head *bh){	struct journal_head *jh = bh2jh(bh);	J_ASSERT_JH(jh, jh->b_jcount >= 0);	get_bh(bh);	if (jh->b_jcount == 0) {		if (jh->b_transaction == NULL &&				jh->b_next_transaction == NULL &&				jh->b_cp_transaction == NULL) {			J_ASSERT_BH(bh, buffer_jbd(bh));			J_ASSERT_BH(bh, jh2bh(jh) == bh);			BUFFER_TRACE(bh, "remove journal_head");			if (jh->b_frozen_data) {				printk(KERN_WARNING "%s: freeing "						"b_frozen_data\n",						__FUNCTION__);				kfree(jh->b_frozen_data);			}			if (jh->b_committed_data) {				printk(KERN_WARNING "%s: freeing "						"b_committed_data\n",						__FUNCTION__);				kfree(jh->b_committed_data);			}			bh->b_private = NULL;			jh->b_bh = NULL;	/* debug, really */			clear_buffer_jbd(bh);			__brelse(bh);			journal_free_journal_head(jh);		} else {			BUFFER_TRACE(bh, "journal_head was locked");		}	}}/* * journal_remove_journal_head(): if the buffer isn't attached to a transaction * and has a zero b_jcount then remove and release its journal_head.   If we did * see that the buffer is not used by any transaction we also "logically" * decrement ->b_count. * * We in fact take an additional increment on ->b_count as a convenience, * because the caller usually wants to do additional things with the bh * after calling here. * The caller of journal_remove_journal_head() *must* run __brelse(bh) at some * time.  Once the caller has run __brelse(), the buffer is eligible for * reaping by try_to_free_buffers(). */void journal_remove_journal_head(struct buffer_head *bh){	jbd_lock_bh_journal_head(bh);	__journal_remove_journal_head(bh);	jbd_unlock_bh_journal_head(bh);}/* * Drop a reference on the passed journal_head.  If it fell to zero then try to * release the journal_head from the buffer_head. */void journal_put_journal_head(struct journal_head *jh){	struct buffer_head *bh = jh2bh(jh);	jbd_lock_bh_journal_head(bh);	J_ASSERT_JH(jh, jh->b_jcount > 0);	--jh->b_jcount;	if (!jh->b_jcount && !jh->b_transaction) {		__journal_remove_journal_head(bh);		__brelse(bh);	}	jbd_unlock_bh_journal_head(bh);}/* * /proc tunables */#if defined(CONFIG_JBD_DEBUG)int journal_enable_debug;EXPORT_SYMBOL(journal_enable_debug);#endif#if defined(CONFIG_JBD_DEBUG) && defined(CONFIG_PROC_FS)static struct proc_dir_entry *proc_jbd_debug;int read_jbd_debug(char *page, char **start, off_t off,			  int count, int *eof, void *data){	int ret;	ret = sprintf(page + off, "%d\n", journal_enable_debug);	*eof = 1;	return ret;}int write_jbd_debug(struct file *file, const char __user *buffer,			   unsigned long count, void *data){	char buf[32];	if (count > ARRAY_SIZE(buf) - 1)		count = ARRAY_SIZE(buf) - 1;	if (copy_from_user(buf, buffer, count))		return -EFAULT;	buf[ARRAY_SIZE(buf) - 1] = '\0';	journal_enable_debug = simple_strtoul(buf, NULL, 10);	return count;}#define JBD_PROC_NAME "sys/fs/jbd-debug"static void __init create_jbd_proc_entry(void){	proc_jbd_debug = create_proc_entry(JBD_PROC_NAME, 0644, NULL);	if (proc_jbd_debug) {		/* Why is this so hard? */		proc_jbd_debug->read_proc = read_jbd_debug;		proc_jbd_debug->write_proc = write_jbd_debug;	}}static void __exit remove_jbd_proc_entry(void){	if (proc_jbd_debug)		remove_proc_entry(JBD_PROC_NAME, NULL);}#else#define create_jbd_proc_entry() do {} while (0)#define remove_jbd_proc_entry() do {} while (0)#endifkmem_cache_t *jbd_handle_cache;static int __init journal_init_handle_cache(void){	jbd_handle_cache = kmem_cache_create("journal_handle",				sizeof(handle_t),				0,		/* offset */				0,		/* flags */				NULL,		/* ctor */				NULL);		/* dtor */	if (jbd_handle_cache == NULL) {		printk(KERN_EMERG "JBD: failed to create handle cache\n");		return -ENOMEM;	}	return 0;}static void journal_destroy_handle_cache(void){	if (jbd_handle_cache)		kmem_cache_destroy(jbd_handle_cache);}/* * Module startup and shutdown */static int __init journal_init_caches(void){	int ret;	ret = journal_init_revoke_caches();	if (ret == 0)		ret = journal_init_journal_head_cache();	if (ret == 0)		ret = journal_init_handle_cache();	return ret;}static void journal_destroy_caches(void){	journal_destroy_revoke_caches();	journal_destroy_journal_head_cache();	journal_destroy_handle_cache();}static int __init journal_init(void){	int ret;	ret = journal_init_caches();	if (ret != 0)		journal_destroy_caches();	create_jbd_proc_entry();	return ret;}static void __exit journal_exit(void){#ifdef CONFIG_JBD_DEBUG	int n = atomic_read(&nr_journal_heads);	if (n)		printk(KERN_EMERG "JBD: leaked %d journal_heads!\n", n);#endif	remove_jbd_proc_entry();	journal_destroy_caches();}MODULE_LICENSE("GPL");module_init(journal_init);module_exit(journal_exit);

⌨️ 快捷键说明

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