📄 journal.c
字号:
return; if (!journal->j_errno) journal->j_errno = errno; __jbd2_journal_abort_hard(journal); if (errno) jbd2_journal_update_superblock(journal, 1);}/** * void jbd2_journal_abort () - Shutdown the journal immediately. * @journal: the journal to shutdown. * @errno: an error number to record in the journal indicating * the reason for the shutdown. * * Perform a complete, immediate shutdown of the ENTIRE * journal (not of a single transaction). This operation cannot be * undone without closing and reopening the journal. * * The jbd2_journal_abort function is intended to support higher level error * recovery mechanisms such as the ext2/ext3 remount-readonly error * mode. * * 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 * jbd2_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 jbd2_journal_stop, which will receive the -EIO error. * * Finally, the jbd2_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 jbd2_journal_abort(journal_t *journal, int errno){ __journal_abort_soft(journal, errno);}/** * int jbd2_journal_errno () - returns the journal's error state. * @journal: journal to examine. * * This is the errno numbet set with jbd2_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 jbd2_journal_errno(journal_t *journal){ int err; spin_lock(&journal->j_state_lock); if (journal->j_flags & JBD2_ABORT) err = -EROFS; else err = journal->j_errno; spin_unlock(&journal->j_state_lock); return err;}/** * int jbd2_journal_clear_err () - clears the journal's error state * @journal: journal to act on. * * An error must be cleared or Acked to take a FS out of readonly * mode. */int jbd2_journal_clear_err(journal_t *journal){ int err = 0; spin_lock(&journal->j_state_lock); if (journal->j_flags & JBD2_ABORT) err = -EROFS; else journal->j_errno = 0; spin_unlock(&journal->j_state_lock); return err;}/** * void jbd2_journal_ack_err() - Ack journal err. * @journal: journal to act on. * * An error must be cleared or Acked to take a FS out of readonly * mode. */void jbd2_journal_ack_err(journal_t *journal){ spin_lock(&journal->j_state_lock); if (journal->j_errno) journal->j_flags |= JBD2_ACK_ERR; spin_unlock(&journal->j_state_lock);}int jbd2_journal_blocks_per_page(struct inode *inode){ return 1 << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits);}/* * helper functions to deal with 32 or 64bit block numbers. */size_t journal_tag_bytes(journal_t *journal){ if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_64BIT)) return JBD2_TAG_SIZE64; else return JBD2_TAG_SIZE32;}/* * Journal_head storage management */static struct kmem_cache *jbd2_journal_head_cache;#ifdef CONFIG_JBD2_DEBUGstatic atomic_t nr_journal_heads = ATOMIC_INIT(0);#endifstatic int journal_init_jbd2_journal_head_cache(void){ int retval; J_ASSERT(jbd2_journal_head_cache == 0); jbd2_journal_head_cache = kmem_cache_create("jbd2_journal_head", sizeof(struct journal_head), 0, /* offset */ 0, /* flags */ NULL); /* ctor */ retval = 0; if (jbd2_journal_head_cache == 0) { retval = -ENOMEM; printk(KERN_EMERG "JBD: no memory for journal_head cache\n"); } return retval;}static void jbd2_journal_destroy_jbd2_journal_head_cache(void){ J_ASSERT(jbd2_journal_head_cache != NULL); kmem_cache_destroy(jbd2_journal_head_cache); jbd2_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_JBD2_DEBUG atomic_inc(&nr_journal_heads);#endif ret = kmem_cache_alloc(jbd2_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(jbd2_journal_head_cache, GFP_NOFS); } } return ret;}static void journal_free_journal_head(struct journal_head *jh){#ifdef CONFIG_JBD2_DEBUG atomic_dec(&nr_journal_heads); memset(jh, JBD2_POISON_FREE, sizeof(*jh));#endif kmem_cache_free(jbd2_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 jbd2_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, jbd2_journal_add_journal_head elevates the * journal_head's b_jcount refcount by one. The caller must call * jbd2_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 = jbd2_journal_add_journal_head(bh); * ... * jh->b_transaction = xxx; * jbd2_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 *jbd2_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 *jbd2_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_JH(jh, jh->b_jlist == BJ_None); 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__); jbd2_free(jh->b_frozen_data, bh->b_size); } if (jh->b_committed_data) { printk(KERN_WARNING "%s: freeing " "b_committed_data\n", __FUNCTION__); jbd2_free(jh->b_committed_data, bh->b_size); } 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"); } }}/* * jbd2_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 jbd2_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 jbd2_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 jbd2_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);}/* * debugfs tunables */#ifdef CONFIG_JBD2_DEBUGu8 jbd2_journal_enable_debug __read_mostly;EXPORT_SYMBOL(jbd2_journal_enable_debug);#define JBD2_DEBUG_NAME "jbd2-debug"static struct dentry *jbd2_debugfs_dir;static struct dentry *jbd2_debug;static void __init jbd2_create_debugfs_entry(void){ jbd2_debugfs_dir = debugfs_create_dir("jbd2", NULL); if (jbd2_debugfs_dir) jbd2_debug = debugfs_create_u8(JBD2_DEBUG_NAME, S_IRUGO, jbd2_debugfs_dir, &jbd2_journal_enable_debug);}static void __exit jbd2_remove_debugfs_entry(void){ debugfs_remove(jbd2_debug); debugfs_remove(jbd2_debugfs_dir);}#elsestatic void __init jbd2_create_debugfs_entry(void){}static void __exit jbd2_remove_debugfs_entry(void){}#endifstruct kmem_cache *jbd2_handle_cache;static int __init journal_init_handle_cache(void){ jbd2_handle_cache = kmem_cache_create("jbd2_journal_handle", sizeof(handle_t), 0, /* offset */ 0, /* flags */ NULL); /* ctor */ if (jbd2_handle_cache == NULL) { printk(KERN_EMERG "JBD: failed to create handle cache\n"); return -ENOMEM; } return 0;}static void jbd2_journal_destroy_handle_cache(void){ if (jbd2_handle_cache) kmem_cache_destroy(jbd2_handle_cache);}/* * Module startup and shutdown */static int __init journal_init_caches(void){ int ret; ret = jbd2_journal_init_revoke_caches(); if (ret == 0) ret = journal_init_jbd2_journal_head_cache(); if (ret == 0) ret = journal_init_handle_cache(); return ret;}static void jbd2_journal_destroy_caches(void){ jbd2_journal_destroy_revoke_caches(); jbd2_journal_destroy_jbd2_journal_head_cache(); jbd2_journal_destroy_handle_cache();}static int __init journal_init(void){ int ret; BUILD_BUG_ON(sizeof(struct journal_superblock_s) != 1024); ret = journal_init_caches(); if (ret != 0) jbd2_journal_destroy_caches(); jbd2_create_debugfs_entry(); return ret;}static void __exit journal_exit(void){#ifdef CONFIG_JBD2_DEBUG int n = atomic_read(&nr_journal_heads); if (n) printk(KERN_EMERG "JBD: leaked %d journal_heads!\n", n);#endif jbd2_remove_debugfs_entry(); jbd2_journal_destroy_caches();}MODULE_LICENSE("GPL");module_init(journal_init);module_exit(journal_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -