📄 replay.c
字号:
if (!journal)
goto fail;
init_waitqueue_head(&journal->j_wait_transaction_locked);
init_waitqueue_head(&journal->j_wait_logspace);
init_waitqueue_head(&journal->j_wait_done_commit);
init_waitqueue_head(&journal->j_wait_checkpoint);
init_waitqueue_head(&journal->j_wait_commit);
init_waitqueue_head(&journal->j_wait_updates);
mutex_init(&journal->j_barrier);
mutex_init(&journal->j_checkpoint_mutex);
spin_lock_init(&journal->j_revoke_lock);
spin_lock_init(&journal->j_list_lock);
spin_lock_init(&journal->j_state_lock);
journal->j_commit_interval = (HZ * JBD_DEFAULT_MAX_COMMIT_AGE);
/* The journal is marked for error until we succeed with recovery! */
journal->j_flags = JFS_ABORT;
/* Set up a default-sized revoke table for the new mount. */
err = journal_init_revoke(journal, JOURNAL_REVOKE_DEFAULT_HASH);
if (err) {
kfree(journal);
goto fail;
}
return journal;
fail:
return NULL;
}
/**
* journal_t * journal_init_inode () - creates a journal which maps to a inode.
* @inode: An inode to create the journal in
*
* journal_init_inode creates a journal which maps an on-disk inode as
* the journal. The inode must exist already, must support bmap() and
* must have all data blocks preallocated.
*/
journal_t * journal_init_inode (struct inode *inode)
{
struct buffer_head *bh;
journal_t *journal = journal_init_common();
int err;
int n;
unsigned long blocknr;
if (!journal)
return NULL;
journal->j_dev = journal->j_fs_dev = inode->i_sb->s_bdev;
journal->j_inode = inode;
jbd_debug(1,
"journal %p: inode %s/%ld, size %Ld, bits %d, blksize %ld\n",
journal, inode->i_sb->s_id, inode->i_ino,
(s64) inode->i_size,
inode->i_sb->s_blocksize_bits, inode->i_sb->s_blocksize);
journal->j_maxlen = (unsigned int)(inode->i_size >> inode->i_sb->s_blocksize_bits);
journal->j_blocksize = inode->i_sb->s_blocksize;
/* journal descriptor can store up to n blocks -bzzz */
n = journal->j_blocksize / sizeof(journal_block_tag_t);
journal->j_wbufsize = n;
journal->j_wbuf = kmalloc(n * sizeof(struct buffer_head*), GFP_KERNEL);
if (!journal->j_wbuf) {
printk(KERN_ERR "%s: Cant allocate bhs for commit thread\n",
__FUNCTION__);
J_ASSERT(journal->j_revoke != NULL);
if (journal->j_revoke)
journal_destroy_revoke(journal);
kfree(journal);
return NULL;
}
err = journal_bmap(journal, 0, &blocknr);
/* If that failed, give up */
if (err) {
printk(KERN_ERR "%s: Cannnot locate journal superblock\n",
__FUNCTION__);
J_ASSERT(journal->j_revoke != NULL);
if (journal->j_revoke)
journal_destroy_revoke(journal);
J_ASSERT(journal->j_wbuf != NULL);
kfree(journal->j_wbuf);
kfree(journal);
return NULL;
}
bh = __getblk(journal->j_dev, blocknr, journal->j_blocksize);
J_ASSERT(bh != NULL);
journal->j_sb_buffer = bh;
journal->j_superblock = (journal_superblock_t *)bh->b_data;
return journal;
}
/**
*
* wipe all journal data ...
*
*/
void journal_wipe_recovery(journal_t *journal)
{
/* We can now mark the journal as empty. */
journal->j_tail = 0;
if (journal->j_sb_buffer) {
journal_update_superblock(journal, 0);
brelse(journal->j_sb_buffer);
journal->j_sb_buffer = NULL;
}
}
/**
* void journal_destroy() - Release a journal_t structure.
* @journal: Journal to act on.
*
* Release a journal_t structure once it is no longer in use by the
* journaled object.
*/
void journal_destroy(journal_t *journal)
{
#if 0
/* Wait for the commit thread to wake up and die. */
journal_kill_thread(journal);
/* Force a final log commit */
if (journal->j_running_transaction)
journal_commit_transaction(journal);
/* Force any old transactions to disk */
/* Totally anal locking here... */
spin_lock(&journal->j_list_lock);
while (journal->j_checkpoint_transactions != NULL) {
spin_unlock(&journal->j_list_lock);
log_do_checkpoint(journal);
spin_lock(&journal->j_list_lock);
}
J_ASSERT(journal->j_running_transaction == NULL);
J_ASSERT(journal->j_committing_transaction == NULL);
J_ASSERT(journal->j_checkpoint_transactions == NULL);
spin_unlock(&journal->j_list_lock);
/* We can now mark the journal as empty. */
journal->j_tail = 0;
journal->j_tail_sequence = ++journal->j_transaction_sequence;
if (journal->j_sb_buffer) {
journal_update_superblock(journal, 1);
brelse(journal->j_sb_buffer);
}
#endif
if (journal->j_sb_buffer) {
brelse(journal->j_sb_buffer);
}
if (journal->j_inode)
iput(journal->j_inode);
if (journal->j_revoke)
journal_destroy_revoke(journal);
kfree(journal->j_wbuf);
kfree(journal);
}
/**
*int journal_check_used_features () - Check if features specified are used.
* @journal: Journal to check.
* @compat: bitmask of compatible features
* @ro: bitmask of features that force read-only mount
* @incompat: bitmask of incompatible features
*
* Check whether the journal uses all of a given set of
* features. Return true (non-zero) if it does.
**/
int journal_check_used_features (journal_t *journal, unsigned long compat,
unsigned long ro, unsigned long incompat)
{
journal_superblock_t *sb;
if (!compat && !ro && !incompat)
return 1;
if (journal->j_format_version == 1)
return 0;
sb = journal->j_superblock;
if (((be32_to_cpu(sb->s_feature_compat) & compat) == compat) &&
((be32_to_cpu(sb->s_feature_ro_compat) & ro) == ro) &&
((be32_to_cpu(sb->s_feature_incompat) & incompat) == incompat))
return 1;
return 0;
}
/**
* int journal_check_available_features() - Check feature set in journalling layer
* @journal: Journal to check.
* @compat: bitmask of compatible features
* @ro: bitmask of features that force read-only mount
* @incompat: bitmask of incompatible features
*
* Check whether the journaling code supports the use of
* all of a given set of features on this journal. Return true
* (non-zero) if it can. */
int journal_check_available_features (journal_t *journal, unsigned long compat,
unsigned long ro, unsigned long incompat)
{
journal_superblock_t *sb;
if (!compat && !ro && !incompat)
return 1;
sb = journal->j_superblock;
/* We can support any known requested features iff the
* superblock is in version 2. Otherwise we fail to support any
* extended sb features. */
if (journal->j_format_version != 2)
return 0;
if ((compat & JFS_KNOWN_COMPAT_FEATURES) == compat &&
(ro & JFS_KNOWN_ROCOMPAT_FEATURES) == ro &&
(incompat & JFS_KNOWN_INCOMPAT_FEATURES) == incompat)
return 1;
return 0;
}
/**
* int journal_set_features () - Mark a given journal feature in the superblock
* @journal: Journal to act on.
* @compat: bitmask of compatible features
* @ro: bitmask of features that force read-only mount
* @incompat: bitmask of incompatible features
*
* Mark a given journal feature as present on the
* superblock. Returns true if the requested features could be set.
*
*/
int journal_set_features (journal_t *journal, unsigned long compat,
unsigned long ro, unsigned long incompat)
{
journal_superblock_t *sb;
if (journal_check_used_features(journal, compat, ro, incompat))
return 1;
if (!journal_check_available_features(journal, compat, ro, incompat))
return 0;
jbd_debug(1, "Setting new features 0x%lx/0x%lx/0x%lx\n",
compat, ro, incompat);
sb = journal->j_superblock;
sb->s_feature_compat |= cpu_to_be32(compat);
sb->s_feature_ro_compat |= cpu_to_be32(ro);
sb->s_feature_incompat |= cpu_to_be32(incompat);
return 1;
}
static int journal_convert_superblock_v1(journal_t *journal,
journal_superblock_t *sb)
{
int offset, blocksize;
struct buffer_head *bh;
printk(KERN_WARNING
"JBD: Converting superblock from version 1 to 2.\n");
/* Pre-initialise new fields to zero */
offset = (INT)(((INT_PTR) &(sb->s_feature_compat)) - ((INT_PTR) sb));
blocksize = be32_to_cpu(sb->s_blocksize);
memset(&sb->s_feature_compat, 0, blocksize-offset);
sb->s_nr_users = cpu_to_be32(1);
sb->s_header.h_blocktype = cpu_to_be32(JFS_SUPERBLOCK_V2);
journal->j_format_version = 2;
bh = journal->j_sb_buffer;
BUFFER_TRACE(bh, "marking dirty");
mark_buffer_dirty(bh);
sync_dirty_buffer(bh);
return 0;
}
/*
* If the journal init or create aborts, we need to mark the journal
* superblock as being NULL to prevent the journal destroy from writing
* back a bogus superblock.
*/
static void journal_fail_superblock (journal_t *journal)
{
struct buffer_head *bh = journal->j_sb_buffer;
brelse(bh);
journal->j_sb_buffer = NULL;
}
/*
* Read the superblock for a given journal, performing initial
* validation of the format.
*/
static int journal_get_superblock(journal_t *journal)
{
struct buffer_head *bh;
journal_superblock_t *sb;
int err = -EIO;
bh = journal->j_sb_buffer;
J_ASSERT(bh != NULL);
if (!buffer_uptodate(bh)) {
ll_rw_block(READ, 1, &bh);
wait_on_buffer(bh);
if (!buffer_uptodate(bh)) {
printk (KERN_ERR
"JBD: IO error reading journal superblock\n");
goto out;
}
}
sb = journal->j_superblock;
err = -EINVAL;
if (sb->s_header.h_magic != cpu_to_be32(JFS_MAGIC_NUMBER) ||
sb->s_blocksize != cpu_to_be32(journal->j_blocksize)) {
printk(KERN_WARNING "JBD: no valid journal superblock found\n");
goto out;
}
switch(be32_to_cpu(sb->s_header.h_blocktype)) {
case JFS_SUPERBLOCK_V1:
journal->j_format_version = 1;
break;
case JFS_SUPERBLOCK_V2:
journal->j_format_version = 2;
break;
default:
printk(KERN_WARNING "JBD: unrecognised superblock format ID\n");
goto out;
}
if (be32_to_cpu(sb->s_maxlen) < journal->j_maxlen)
journal->j_maxlen = be32_to_cpu(sb->s_maxlen);
else if (be32_to_cpu(sb->s_maxlen) > journal->j_maxlen) {
printk (KERN_WARNING "JBD: journal file too short\n");
goto out;
}
return 0;
out:
journal_fail_superblock(journal);
return err;
}
/*
* Load the on-disk journal superblock and read the key fields into the
* journal_t.
*/
static int load_superblock(journal_t *journal)
{
int err;
journal_superblock_t *sb;
err = journal_get_superblock(journal);
if (err)
return err;
sb = journal->j_superblock;
journal->j_tail_sequence = be32_to_cpu(sb->s_sequence);
journal->j_tail = be32_to_cpu(sb->s_start);
journal->j_first = be32_to_cpu(sb->s_first);
journal->j_last = be32_to_cpu(sb->s_maxlen);
journal->j_errno = be32_to_cpu(sb->s_errno);
return 0;
}
/**
* int journal_wipe() - Wipe journal contents
* @journal: Journal to act on.
* @write: flag (see below)
*
* Wipe out all of the contents of a journal, safely. This will produce
* a warning if the journal contains any valid recovery information.
* Must be called between journal_init_*() and journal_load().
*
* If 'write' is non-zero, then we wipe out the journal on disk; otherwise
* we merely suppress recovery.
*/
int journal_wipe(journal_t *journal, int write)
{
journal_superblock_t *sb;
int err = 0;
J_ASSERT (!(journal->j_flags & JFS_LOADED));
err = load_superblock(journal);
if (err)
return err;
sb = journal->j_superblock;
if (!journal->j_tail)
goto no_recovery;
printk (KERN_WARNING "JBD: %s recovery information on journal\n",
write ? "Clearing" : "Ignoring");
err = journal_skip_recovery(journal);
if (write)
journal_update_superblock(journal, 1);
no_recovery:
return err;
}
/**
* int journal_update_format () - Update on-disk journal structure.
* @journal: Journal to act on.
*
* Given an initialised but unloaded journal struct, poke about in the
* on-disk structure to update it to the most recent supported version.
*/
int journal_update_format (journal_t *journal)
{
journal_superblock_t *sb;
int err;
err = journal_get_superblock(journal);
if (err)
return err;
sb = journal->j_superblock;
switch (be32_to_cpu(sb->s_header.h_blocktype)) {
case JFS_SUPERBLOCK_V2:
return 0;
case JFS_SUPERBLOCK_V1:
return journal_convert_superblock_v1(journal, sb);
default:
break;
}
return -EINVAL;
}
/**
* void journal_update_superblock() - Update journal sb on disk.
* @journal: The journal to update.
* @wait: Set to '0' if you don't want to wait for IO completion.
*
* Update a journal's dynamic superblock fields and write it to disk,
* optionally waiting for the IO to complete.
*/
void journal_update_superblock(journal_t *journal, int wait)
{
journal_superblock_t *sb = journal->j_superblock;
struct buffer_head *bh = journal->j_sb_buffer;
/*
* As a special case, if the on-disk copy is already marked as needing
* no recovery (s_start == 0) and there are no outstanding transactions
* in the filesystem, then we can safely defer the superblock update
* until the next commit by setting JFS_FLUSHED. This avoids
* attempting a write to a potential-readonly device.
*/
if (sb->s_start == 0 && journal->j_tail_sequence ==
journal->j_transaction_sequence) {
jbd_debug(1,"JBD: Skipping superblock update on recovered sb "
"(start %ld, seq %d, errno %d)\n",
journal->j_tail, journal->j_tail_sequence,
journal->j_errno);
goto out;
}
spin_lock(&journal->j_state_lock);
jbd_debug(1,"JBD: updating superblock (start %ld, seq %d, errno %d)\n",
journal->j_tail, journal->j_tail_sequence, journal->j_errno);
sb->s_sequence = cpu_to_be32(journal->j_tail_sequence);
sb->s_start = cpu_to_be32(journal->j_tail);
sb->s_errno = cpu_to_be32(journal->j_errno);
spin_unlock(&journal->j_state_lock);
BUFFER_TRACE(bh, "marking dirty");
mark_buffer_dirty(bh);
if (wait)
sync_dirty_buffer(bh);
else
ll_rw_block(SWRITE, 1, &bh);
out:
/* If we have just flushed the log (by marking s_start==0), then
* any future commit will have to be careful to update the
* superblock again to re-record the true start of the log. */
spin_lock(&journal->j_state_lock);
if (sb->s_start)
journal->j_flags &= ~JFS_FLUSHED;
else
journal->j_flags |= JFS_FLUSHED;
spin_unlock(&journal->j_state_lock);
}
/*
* Given a journal_t structure, initialise the various fields for
* startup of a new journaling session. We use this both when creating
* a journal, and after recovering an old journal to reset it for
* subsequent use.
*/
static int journal_reset(journal_t *journal)
{
journal_superblock_t *sb = journal->j_superblock;
unsigned long first, last;
first = be32_to_cpu(sb->s_first);
last = be32_to_cpu(sb->s_maxlen);
journal->j_first = first;
journal->j_last = last;
journal->j_head = first;
journal->j_tail = first;
journal->j_free = last - first;
journal->j_tail_sequence = journal->j_transaction_sequence;
journal->j_commit_sequence = journal->j_transaction_sequence - 1;
journal->j_commit_request = journal->j_commit_sequence;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -