📄 replay.c
字号:
journal->j_max_transaction_buffers = journal->j_maxlen / 4;
/* Add the dynamic fields and write it to disk. */
journal_update_superblock(journal, 1);
return 0;
}
/**
* int journal_load() - Read journal from disk.
* @journal: Journal to act on.
*
* Given a journal_t structure which tells us which disk blocks contain
* a journal, read the journal from disk to initialise the in-memory
* structures.
*/
int journal_load(journal_t *journal)
{
int err;
journal_superblock_t *sb;
err = load_superblock(journal);
if (err)
return err;
sb = journal->j_superblock;
/* If this is a V2 superblock, then we have to check the
* features flags on it. */
if (journal->j_format_version >= 2) {
if ((sb->s_feature_ro_compat &
~cpu_to_be32(JFS_KNOWN_ROCOMPAT_FEATURES)) ||
(sb->s_feature_incompat &
~cpu_to_be32(JFS_KNOWN_INCOMPAT_FEATURES))) {
printk (KERN_WARNING
"JBD: Unrecognised features on journal\n");
return -EINVAL;
}
}
/* Let the recovery code check whether it needs to recover any
* data from the journal. */
if (journal_recover(journal))
goto recovery_error;
/* OK, we've finished with the dynamic journal bits:
* reinitialise the dynamic contents of the superblock in memory
* and reset them on disk. */
if (journal_reset(journal))
goto recovery_error;
journal->j_flags &= ~JFS_ABORT;
journal->j_flags |= JFS_LOADED;
return 0;
recovery_error:
printk (KERN_WARNING "JBD: recovery failed\n");
return -EIO;
}
//
// transactions routines
//
/*
*
* List management code snippets: various functions for manipulating the
* transaction buffer lists.
*
*/
/*
* Append a buffer to a transaction list, given the transaction's list head
* pointer.
*
* j_list_lock is held.
*
* jbd_lock_bh_state(jh2bh(jh)) is held.
*/
static inline void
__blist_add_buffer(struct journal_head **list, struct journal_head *jh)
{
if (!*list) {
jh->b_tnext = jh->b_tprev = jh;
*list = jh;
} else {
/* Insert at the tail of the list to preserve order */
struct journal_head *first = *list, *last = first->b_tprev;
jh->b_tprev = last;
jh->b_tnext = first;
last->b_tnext = first->b_tprev = jh;
}
}
/*
* Remove a buffer from a transaction list, given the transaction's list
* head pointer.
*
* Called with j_list_lock held, and the journal may not be locked.
*
* jbd_lock_bh_state(jh2bh(jh)) is held.
*/
static inline void
__blist_del_buffer(struct journal_head **list, struct journal_head *jh)
{
if (*list == jh) {
*list = jh->b_tnext;
if (*list == jh)
*list = NULL;
}
jh->b_tprev->b_tnext = jh->b_tnext;
jh->b_tnext->b_tprev = jh->b_tprev;
}
/*
* Remove a buffer from the appropriate transaction list.
*
* Note that this function can *change* the value of
* bh->b_transaction->t_sync_datalist, t_buffers, t_forget,
* t_iobuf_list, t_shadow_list, t_log_list or t_reserved_list. If the caller
* is holding onto a copy of one of thee pointers, it could go bad.
* Generally the caller needs to re-read the pointer from the transaction_t.
*
* Called under j_list_lock. The journal may not be locked.
*/
static void __journal_temp_unlink_buffer(struct journal_head *jh)
{
struct journal_head **list = NULL;
transaction_t *transaction;
struct buffer_head *bh = jh2bh(jh);
J_ASSERT_JH(jh, jbd_is_locked_bh_state(bh));
transaction = jh->b_transaction;
if (transaction)
assert_spin_locked(&transaction->t_journal->j_list_lock);
J_ASSERT_JH(jh, jh->b_jlist < BJ_Types);
if (jh->b_jlist != BJ_None)
J_ASSERT_JH(jh, transaction != NULL);
switch (jh->b_jlist) {
case BJ_None:
return;
case BJ_SyncData:
list = &transaction->t_sync_datalist;
break;
case BJ_Metadata:
transaction->t_nr_buffers--;
J_ASSERT_JH(jh, transaction->t_nr_buffers >= 0);
list = &transaction->t_buffers;
break;
case BJ_Forget:
list = &transaction->t_forget;
break;
case BJ_IO:
list = &transaction->t_iobuf_list;
break;
case BJ_Shadow:
list = &transaction->t_shadow_list;
break;
case BJ_LogCtl:
list = &transaction->t_log_list;
break;
case BJ_Reserved:
list = &transaction->t_reserved_list;
break;
case BJ_Locked:
list = &transaction->t_locked_list;
break;
}
__blist_del_buffer(list, jh);
jh->b_jlist = BJ_None;
if (test_clear_buffer_jbddirty(bh))
mark_buffer_dirty(bh); /* Expose it to the VM */
}
void __journal_unfile_buffer(struct journal_head *jh)
{
__journal_temp_unlink_buffer(jh);
jh->b_transaction = NULL;
}
void journal_unfile_buffer(journal_t *journal, struct journal_head *jh)
{
jbd_lock_bh_state(jh2bh(jh));
spin_lock(&journal->j_list_lock);
__journal_unfile_buffer(jh);
spin_unlock(&journal->j_list_lock);
jbd_unlock_bh_state(jh2bh(jh));
}
/*
* This buffer is no longer needed. If it is on an older transaction's
* checkpoint list we need to record it on this transaction's forget list
* to pin this buffer (and hence its checkpointing transaction) down until
* this transaction commits. If the buffer isn't on a checkpoint list, we
* release it.
* Returns non-zero if JBD no longer has an interest in the buffer.
*
* Called under j_list_lock.
*
* Called under jbd_lock_bh_state(bh).
*/
static int __dispose_buffer(struct journal_head *jh, transaction_t *transaction)
{
int may_free = 1;
struct buffer_head *bh = jh2bh(jh);
__journal_unfile_buffer(jh);
if (jh->b_cp_transaction) {
JBUFFER_TRACE(jh, "on running+cp transaction");
__journal_file_buffer(jh, transaction, BJ_Forget);
clear_buffer_jbddirty(bh);
may_free = 0;
} else {
JBUFFER_TRACE(jh, "on running transaction");
journal_remove_journal_head(bh);
__brelse(bh);
}
return may_free;
}
/*
* File a buffer on the given transaction list.
*/
void __journal_file_buffer(struct journal_head *jh,
transaction_t *transaction, int jlist)
{
struct journal_head **list = NULL;
int was_dirty = 0;
struct buffer_head *bh = jh2bh(jh);
J_ASSERT_JH(jh, jbd_is_locked_bh_state(bh));
assert_spin_locked(&transaction->t_journal->j_list_lock);
J_ASSERT_JH(jh, jh->b_jlist < BJ_Types);
J_ASSERT_JH(jh, jh->b_transaction == transaction ||
jh->b_transaction == NULL);
if (jh->b_transaction && jh->b_jlist == (unsigned) jlist)
return;
/* The following list of buffer states needs to be consistent
* with __jbd_unexpected_dirty_buffer()'s handling of dirty
* state. */
if (jlist == BJ_Metadata || jlist == BJ_Reserved ||
jlist == BJ_Shadow || jlist == BJ_Forget) {
if (test_clear_buffer_dirty(bh) ||
test_clear_buffer_jbddirty(bh))
was_dirty = 1;
}
if (jh->b_transaction)
__journal_temp_unlink_buffer(jh);
jh->b_transaction = transaction;
switch (jlist) {
case BJ_None:
J_ASSERT_JH(jh, !jh->b_committed_data);
J_ASSERT_JH(jh, !jh->b_frozen_data);
return;
case BJ_SyncData:
list = &transaction->t_sync_datalist;
break;
case BJ_Metadata:
transaction->t_nr_buffers++;
list = &transaction->t_buffers;
break;
case BJ_Forget:
list = &transaction->t_forget;
break;
case BJ_IO:
list = &transaction->t_iobuf_list;
break;
case BJ_Shadow:
list = &transaction->t_shadow_list;
break;
case BJ_LogCtl:
list = &transaction->t_log_list;
break;
case BJ_Reserved:
list = &transaction->t_reserved_list;
break;
case BJ_Locked:
list = &transaction->t_locked_list;
break;
}
__blist_add_buffer(list, jh);
jh->b_jlist = jlist;
if (was_dirty)
set_buffer_jbddirty(bh);
}
void journal_file_buffer(struct journal_head *jh,
transaction_t *transaction, int jlist)
{
jbd_lock_bh_state(jh2bh(jh));
spin_lock(&transaction->t_journal->j_list_lock);
__journal_file_buffer(jh, transaction, jlist);
spin_unlock(&transaction->t_journal->j_list_lock);
jbd_unlock_bh_state(jh2bh(jh));
}
/*
* journal_release_buffer: undo a get_write_access without any buffer
* updates, if the update decided in the end that it didn't need access.
*
*/
void
journal_release_buffer(handle_t *handle, struct buffer_head *bh)
{
BUFFER_TRACE(bh, "entry");
}
/**
* void journal_forget() - bforget() for potentially-journaled buffers.
* @handle: transaction handle
* @bh: bh to 'forget'
*
* We can only do the bforget if there are no commits pending against the
* buffer. If the buffer is dirty in the current running transaction we
* can safely unlink it.
*
* bh may not be a journalled buffer at all - it may be a non-JBD
* buffer which came off the hashtable. Check for this.
*
* Decrements bh->b_count by one.
*
* Allow this call even if the handle has aborted --- it may be part of
* the caller's cleanup after an abort.
*/
int journal_forget (handle_t *handle, struct buffer_head *bh)
{
transaction_t *transaction = handle->h_transaction;
journal_t *journal = transaction->t_journal;
struct journal_head *jh;
int drop_reserve = 0;
int err = 0;
BUFFER_TRACE(bh, "entry");
jbd_lock_bh_state(bh);
spin_lock(&journal->j_list_lock);
if (!buffer_jbd(bh))
goto not_jbd;
jh = bh2jh(bh);
/* Critical error: attempting to delete a bitmap buffer, maybe?
* Don't do any jbd operations, and return an error. */
if (!J_EXPECT_JH(jh, !jh->b_committed_data,
"inconsistent data on disk")) {
err = -EIO;
goto not_jbd;
}
/*
* The buffer's going from the transaction, we must drop
* all references -bzzz
*/
jh->b_modified = 0;
if (jh->b_transaction == handle->h_transaction) {
J_ASSERT_JH(jh, !jh->b_frozen_data);
/* If we are forgetting a buffer which is already part
* of this transaction, then we can just drop it from
* the transaction immediately. */
clear_buffer_dirty(bh);
clear_buffer_jbddirty(bh);
JBUFFER_TRACE(jh, "belongs to current transaction: unfile");
drop_reserve = 1;
/*
* We are no longer going to journal this buffer.
* However, the commit of this transaction is still
* important to the buffer: the delete that we are now
* processing might obsolete an old log entry, so by
* committing, we can satisfy the buffer's checkpoint.
*
* So, if we have a checkpoint on the buffer, we should
* now refile the buffer on our BJ_Forget list so that
* we know to remove the checkpoint after we commit.
*/
if (jh->b_cp_transaction) {
__journal_temp_unlink_buffer(jh);
__journal_file_buffer(jh, transaction, BJ_Forget);
} else {
__journal_unfile_buffer(jh);
journal_remove_journal_head(bh);
__brelse(bh);
if (!buffer_jbd(bh)) {
spin_unlock(&journal->j_list_lock);
jbd_unlock_bh_state(bh);
__bforget(bh);
goto drop;
}
}
} else if (jh->b_transaction) {
J_ASSERT_JH(jh, (jh->b_transaction ==
journal->j_committing_transaction));
/* However, if the buffer is still owned by a prior
* (committing) transaction, we can't drop it yet... */
JBUFFER_TRACE(jh, "belongs to older transaction");
/* ... but we CAN drop it from the new transaction if we
* have also modified it since the original commit. */
if (jh->b_next_transaction) {
J_ASSERT(jh->b_next_transaction == transaction);
jh->b_next_transaction = NULL;
drop_reserve = 1;
}
}
not_jbd:
spin_unlock(&journal->j_list_lock);
jbd_unlock_bh_state(bh);
__brelse(bh);
drop:
if (drop_reserve) {
/* no need to reserve log space for this block -bzzz */
handle->h_buffer_credits++;
}
return err;
}
/*
* debugfs tunables
*/
#ifdef CONFIG_JBD_DEBUG
u8 journal_enable_debug __read_mostly;
EXPORT_SYMBOL(journal_enable_debug);
static struct dentry *jbd_debugfs_dir;
static struct dentry *jbd_debug;
static void __init jbd_create_debugfs_entry(void)
{
jbd_debugfs_dir = debugfs_create_dir("jbd", NULL);
if (jbd_debugfs_dir)
jbd_debug = debugfs_create_u8("jbd-debug", S_IRUGO,
jbd_debugfs_dir,
&journal_enable_debug);
}
static void __exit jbd_remove_debugfs_entry(void)
{
debugfs_remove(jbd_debug);
debugfs_remove(jbd_debugfs_dir);
}
#else
static inline void jbd_create_debugfs_entry(void)
{
}
static inline void jbd_remove_debugfs_entry(void)
{
}
#endif
struct kmem_cache *jbd_handle_cache = NULL;
static int __init journal_init_handle_cache(void)
{
jbd_handle_cache = kmem_cache_create("journal_handle",
sizeof(handle_t),
0, /* offset */
SLAB_TEMPORARY, /* flags */
NULL); /* ctor */
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;
J_ASSERT(sizeof(struct journal_superblock_s) == 1024);
ret = journal_init_caches();
if (ret != 0)
journal_destroy_caches();
jbd_create_debugfs_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
jbd_remove_debugfs_entry();
journal_destroy_caches();
}
MODULE_LICENSE("GPL");
module_init(journal_init);
module_exit(journal_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -