A number of bug fixes for ext4:
* For the new fast_commit feature * Fix some error handling codepaths in whiteout handling and mountpoint sampling * Fix how we write ext4_error information so it goes through the journal when journalling is active, to avoid races that can lead to lost error information, superblock checksum failures, or DIF/DIX features. -----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEEK2m5VNv+CHkogTfJ8vlZVpUNgaMFAmAB8eMACgkQ8vlZVpUN gaMUxAf+MW22dceTto2RO0ox9OEBNoZDFiVnlEuUaIOxkqOlovIWaqX7wwuF/121 +FaNeDVzqNSS/QjQSB5lHF5OfHCD2u1Ef/bGzCm9cQyeN2/n0sCsStfPCcyLHy/0 4R8PsjF0xhhbCETLcAc0U/YBFEoqSn1i7DG5nnpx63Wt1S/SSMmTAXzafWbzisEZ XNsz3CEPCDDSmSzOt3qMMHxkSoOZhYcLe7fCoKkhZ2pvTyrQsHrne6NNLtxc+sDL AcKkaI0EWFiFRhebowQO/5ouq6nnGKLCsukuZN9//Br8ht5gNcFpuKNVFl+LOiM6 ud4H3qcRokcdPPAn3uwI0AJKFXqLvg== =Dgdj -----END PGP SIGNATURE----- Merge tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4 Pull ext4 fixes from Ted Ts'o: "A number of bug fixes for ext4: - Fix for the new fast_commit feature - Fix some error handling codepaths in whiteout handling and mountpoint sampling - Fix how we write ext4_error information so it goes through the journal when journalling is active, to avoid races that can lead to lost error information, superblock checksum failures, or DIF/DIX features" * tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: ext4: remove expensive flush on fast commit ext4: fix bug for rename with RENAME_WHITEOUT ext4: fix wrong list_splice in ext4_fc_cleanup ext4: use IS_ERR instead of IS_ERR_OR_NULL and set inode null when IS_ERR ext4: don't leak old mountpoint samples ext4: drop ext4_handle_dirty_super() ext4: fix superblock checksum failure when setting password salt ext4: use sbi instead of EXT4_SB(sb) in ext4_update_super() ext4: save error info to sb through journal if available ext4: protect superblock modifications with a buffer lock ext4: drop sync argument of ext4_commit_super() ext4: combine ext4_handle_error() and save_error_info()
This commit is contained in:
commit
0bc9bc1d8b
@ -372,20 +372,3 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int __ext4_handle_dirty_super(const char *where, unsigned int line,
|
||||
handle_t *handle, struct super_block *sb)
|
||||
{
|
||||
struct buffer_head *bh = EXT4_SB(sb)->s_sbh;
|
||||
int err = 0;
|
||||
|
||||
ext4_superblock_csum_set(sb);
|
||||
if (ext4_handle_valid(handle)) {
|
||||
err = jbd2_journal_dirty_metadata(handle, bh);
|
||||
if (err)
|
||||
ext4_journal_abort_handle(where, line, __func__,
|
||||
bh, handle, err);
|
||||
} else
|
||||
mark_buffer_dirty(bh);
|
||||
return err;
|
||||
}
|
||||
|
@ -244,9 +244,6 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
|
||||
handle_t *handle, struct inode *inode,
|
||||
struct buffer_head *bh);
|
||||
|
||||
int __ext4_handle_dirty_super(const char *where, unsigned int line,
|
||||
handle_t *handle, struct super_block *sb);
|
||||
|
||||
#define ext4_journal_get_write_access(handle, bh) \
|
||||
__ext4_journal_get_write_access(__func__, __LINE__, (handle), (bh))
|
||||
#define ext4_forget(handle, is_metadata, inode, bh, block_nr) \
|
||||
@ -257,8 +254,6 @@ int __ext4_handle_dirty_super(const char *where, unsigned int line,
|
||||
#define ext4_handle_dirty_metadata(handle, inode, bh) \
|
||||
__ext4_handle_dirty_metadata(__func__, __LINE__, (handle), (inode), \
|
||||
(bh))
|
||||
#define ext4_handle_dirty_super(handle, sb) \
|
||||
__ext4_handle_dirty_super(__func__, __LINE__, (handle), (sb))
|
||||
|
||||
handle_t *__ext4_journal_start_sb(struct super_block *sb, unsigned int line,
|
||||
int type, int blocks, int rsv_blocks,
|
||||
|
@ -604,13 +604,13 @@ void ext4_fc_track_range(handle_t *handle, struct inode *inode, ext4_lblk_t star
|
||||
trace_ext4_fc_track_range(inode, start, end, ret);
|
||||
}
|
||||
|
||||
static void ext4_fc_submit_bh(struct super_block *sb)
|
||||
static void ext4_fc_submit_bh(struct super_block *sb, bool is_tail)
|
||||
{
|
||||
int write_flags = REQ_SYNC;
|
||||
struct buffer_head *bh = EXT4_SB(sb)->s_fc_bh;
|
||||
|
||||
/* TODO: REQ_FUA | REQ_PREFLUSH is unnecessarily expensive. */
|
||||
if (test_opt(sb, BARRIER))
|
||||
/* Add REQ_FUA | REQ_PREFLUSH only its tail */
|
||||
if (test_opt(sb, BARRIER) && is_tail)
|
||||
write_flags |= REQ_FUA | REQ_PREFLUSH;
|
||||
lock_buffer(bh);
|
||||
set_buffer_dirty(bh);
|
||||
@ -684,7 +684,7 @@ static u8 *ext4_fc_reserve_space(struct super_block *sb, int len, u32 *crc)
|
||||
*crc = ext4_chksum(sbi, *crc, tl, sizeof(*tl));
|
||||
if (pad_len > 0)
|
||||
ext4_fc_memzero(sb, tl + 1, pad_len, crc);
|
||||
ext4_fc_submit_bh(sb);
|
||||
ext4_fc_submit_bh(sb, false);
|
||||
|
||||
ret = jbd2_fc_get_buf(EXT4_SB(sb)->s_journal, &bh);
|
||||
if (ret)
|
||||
@ -741,7 +741,7 @@ static int ext4_fc_write_tail(struct super_block *sb, u32 crc)
|
||||
tail.fc_crc = cpu_to_le32(crc);
|
||||
ext4_fc_memcpy(sb, dst, &tail.fc_crc, sizeof(tail.fc_crc), NULL);
|
||||
|
||||
ext4_fc_submit_bh(sb);
|
||||
ext4_fc_submit_bh(sb, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1268,7 +1268,7 @@ static void ext4_fc_cleanup(journal_t *journal, int full)
|
||||
list_splice_init(&sbi->s_fc_dentry_q[FC_Q_STAGING],
|
||||
&sbi->s_fc_dentry_q[FC_Q_MAIN]);
|
||||
list_splice_init(&sbi->s_fc_q[FC_Q_STAGING],
|
||||
&sbi->s_fc_q[FC_Q_STAGING]);
|
||||
&sbi->s_fc_q[FC_Q_MAIN]);
|
||||
|
||||
ext4_clear_mount_flag(sb, EXT4_MF_FC_COMMITTING);
|
||||
ext4_clear_mount_flag(sb, EXT4_MF_FC_INELIGIBLE);
|
||||
@ -1318,14 +1318,14 @@ static int ext4_fc_replay_unlink(struct super_block *sb, struct ext4_fc_tl *tl)
|
||||
entry.len = darg.dname_len;
|
||||
inode = ext4_iget(sb, darg.ino, EXT4_IGET_NORMAL);
|
||||
|
||||
if (IS_ERR_OR_NULL(inode)) {
|
||||
if (IS_ERR(inode)) {
|
||||
jbd_debug(1, "Inode %d not found", darg.ino);
|
||||
return 0;
|
||||
}
|
||||
|
||||
old_parent = ext4_iget(sb, darg.parent_ino,
|
||||
EXT4_IGET_NORMAL);
|
||||
if (IS_ERR_OR_NULL(old_parent)) {
|
||||
if (IS_ERR(old_parent)) {
|
||||
jbd_debug(1, "Dir with inode %d not found", darg.parent_ino);
|
||||
iput(inode);
|
||||
return 0;
|
||||
@ -1410,7 +1410,7 @@ static int ext4_fc_replay_link(struct super_block *sb, struct ext4_fc_tl *tl)
|
||||
darg.parent_ino, darg.dname_len);
|
||||
|
||||
inode = ext4_iget(sb, darg.ino, EXT4_IGET_NORMAL);
|
||||
if (IS_ERR_OR_NULL(inode)) {
|
||||
if (IS_ERR(inode)) {
|
||||
jbd_debug(1, "Inode not found.");
|
||||
return 0;
|
||||
}
|
||||
@ -1466,10 +1466,11 @@ static int ext4_fc_replay_inode(struct super_block *sb, struct ext4_fc_tl *tl)
|
||||
trace_ext4_fc_replay(sb, tag, ino, 0, 0);
|
||||
|
||||
inode = ext4_iget(sb, ino, EXT4_IGET_NORMAL);
|
||||
if (!IS_ERR_OR_NULL(inode)) {
|
||||
if (!IS_ERR(inode)) {
|
||||
ext4_ext_clear_bb(inode);
|
||||
iput(inode);
|
||||
}
|
||||
inode = NULL;
|
||||
|
||||
ext4_fc_record_modified_inode(sb, ino);
|
||||
|
||||
@ -1512,7 +1513,7 @@ static int ext4_fc_replay_inode(struct super_block *sb, struct ext4_fc_tl *tl)
|
||||
|
||||
/* Given that we just wrote the inode on disk, this SHOULD succeed. */
|
||||
inode = ext4_iget(sb, ino, EXT4_IGET_NORMAL);
|
||||
if (IS_ERR_OR_NULL(inode)) {
|
||||
if (IS_ERR(inode)) {
|
||||
jbd_debug(1, "Inode not found.");
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
@ -1564,7 +1565,7 @@ static int ext4_fc_replay_create(struct super_block *sb, struct ext4_fc_tl *tl)
|
||||
goto out;
|
||||
|
||||
inode = ext4_iget(sb, darg.ino, EXT4_IGET_NORMAL);
|
||||
if (IS_ERR_OR_NULL(inode)) {
|
||||
if (IS_ERR(inode)) {
|
||||
jbd_debug(1, "inode %d not found.", darg.ino);
|
||||
inode = NULL;
|
||||
ret = -EINVAL;
|
||||
@ -1577,7 +1578,7 @@ static int ext4_fc_replay_create(struct super_block *sb, struct ext4_fc_tl *tl)
|
||||
* dot and dot dot dirents are setup properly.
|
||||
*/
|
||||
dir = ext4_iget(sb, darg.parent_ino, EXT4_IGET_NORMAL);
|
||||
if (IS_ERR_OR_NULL(dir)) {
|
||||
if (IS_ERR(dir)) {
|
||||
jbd_debug(1, "Dir %d not found.", darg.ino);
|
||||
goto out;
|
||||
}
|
||||
@ -1653,7 +1654,7 @@ static int ext4_fc_replay_add_range(struct super_block *sb,
|
||||
|
||||
inode = ext4_iget(sb, le32_to_cpu(fc_add_ex->fc_ino),
|
||||
EXT4_IGET_NORMAL);
|
||||
if (IS_ERR_OR_NULL(inode)) {
|
||||
if (IS_ERR(inode)) {
|
||||
jbd_debug(1, "Inode not found.");
|
||||
return 0;
|
||||
}
|
||||
@ -1777,7 +1778,7 @@ ext4_fc_replay_del_range(struct super_block *sb, struct ext4_fc_tl *tl)
|
||||
le32_to_cpu(lrange->fc_ino), cur, remaining);
|
||||
|
||||
inode = ext4_iget(sb, le32_to_cpu(lrange->fc_ino), EXT4_IGET_NORMAL);
|
||||
if (IS_ERR_OR_NULL(inode)) {
|
||||
if (IS_ERR(inode)) {
|
||||
jbd_debug(1, "Inode %d not found", le32_to_cpu(lrange->fc_ino));
|
||||
return 0;
|
||||
}
|
||||
@ -1832,7 +1833,7 @@ static void ext4_fc_set_bitmaps_and_counters(struct super_block *sb)
|
||||
for (i = 0; i < state->fc_modified_inodes_used; i++) {
|
||||
inode = ext4_iget(sb, state->fc_modified_inodes[i],
|
||||
EXT4_IGET_NORMAL);
|
||||
if (IS_ERR_OR_NULL(inode)) {
|
||||
if (IS_ERR(inode)) {
|
||||
jbd_debug(1, "Inode %d not found.",
|
||||
state->fc_modified_inodes[i]);
|
||||
continue;
|
||||
@ -1849,7 +1850,7 @@ static void ext4_fc_set_bitmaps_and_counters(struct super_block *sb)
|
||||
|
||||
if (ret > 0) {
|
||||
path = ext4_find_extent(inode, map.m_lblk, NULL, 0);
|
||||
if (!IS_ERR_OR_NULL(path)) {
|
||||
if (!IS_ERR(path)) {
|
||||
for (j = 0; j < path->p_depth; j++)
|
||||
ext4_mb_mark_bb(inode->i_sb,
|
||||
path[j].p_block, 1, 1);
|
||||
|
@ -809,9 +809,12 @@ static int ext4_sample_last_mounted(struct super_block *sb,
|
||||
err = ext4_journal_get_write_access(handle, sbi->s_sbh);
|
||||
if (err)
|
||||
goto out_journal;
|
||||
strlcpy(sbi->s_es->s_last_mounted, cp,
|
||||
lock_buffer(sbi->s_sbh);
|
||||
strncpy(sbi->s_es->s_last_mounted, cp,
|
||||
sizeof(sbi->s_es->s_last_mounted));
|
||||
ext4_handle_dirty_super(handle, sb);
|
||||
ext4_superblock_csum_set(sb);
|
||||
unlock_buffer(sbi->s_sbh);
|
||||
ext4_handle_dirty_metadata(handle, NULL, sbi->s_sbh);
|
||||
out_journal:
|
||||
ext4_journal_stop(handle);
|
||||
out:
|
||||
|
@ -5150,9 +5150,13 @@ static int ext4_do_update_inode(handle_t *handle,
|
||||
err = ext4_journal_get_write_access(handle, EXT4_SB(sb)->s_sbh);
|
||||
if (err)
|
||||
goto out_brelse;
|
||||
lock_buffer(EXT4_SB(sb)->s_sbh);
|
||||
ext4_set_feature_large_file(sb);
|
||||
ext4_superblock_csum_set(sb);
|
||||
unlock_buffer(EXT4_SB(sb)->s_sbh);
|
||||
ext4_handle_sync(handle);
|
||||
err = ext4_handle_dirty_super(handle, sb);
|
||||
err = ext4_handle_dirty_metadata(handle, NULL,
|
||||
EXT4_SB(sb)->s_sbh);
|
||||
}
|
||||
ext4_update_inode_fsync_trans(handle, inode, need_datasync);
|
||||
out_brelse:
|
||||
|
@ -1157,7 +1157,10 @@ resizefs_out:
|
||||
err = ext4_journal_get_write_access(handle, sbi->s_sbh);
|
||||
if (err)
|
||||
goto pwsalt_err_journal;
|
||||
lock_buffer(sbi->s_sbh);
|
||||
generate_random_uuid(sbi->s_es->s_encrypt_pw_salt);
|
||||
ext4_superblock_csum_set(sb);
|
||||
unlock_buffer(sbi->s_sbh);
|
||||
err = ext4_handle_dirty_metadata(handle, NULL,
|
||||
sbi->s_sbh);
|
||||
pwsalt_err_journal:
|
||||
|
@ -2976,14 +2976,17 @@ int ext4_orphan_add(handle_t *handle, struct inode *inode)
|
||||
(le32_to_cpu(sbi->s_es->s_inodes_count))) {
|
||||
/* Insert this inode at the head of the on-disk orphan list */
|
||||
NEXT_ORPHAN(inode) = le32_to_cpu(sbi->s_es->s_last_orphan);
|
||||
lock_buffer(sbi->s_sbh);
|
||||
sbi->s_es->s_last_orphan = cpu_to_le32(inode->i_ino);
|
||||
ext4_superblock_csum_set(sb);
|
||||
unlock_buffer(sbi->s_sbh);
|
||||
dirty = true;
|
||||
}
|
||||
list_add(&EXT4_I(inode)->i_orphan, &sbi->s_orphan);
|
||||
mutex_unlock(&sbi->s_orphan_lock);
|
||||
|
||||
if (dirty) {
|
||||
err = ext4_handle_dirty_super(handle, sb);
|
||||
err = ext4_handle_dirty_metadata(handle, NULL, sbi->s_sbh);
|
||||
rc = ext4_mark_iloc_dirty(handle, inode, &iloc);
|
||||
if (!err)
|
||||
err = rc;
|
||||
@ -3059,9 +3062,12 @@ int ext4_orphan_del(handle_t *handle, struct inode *inode)
|
||||
mutex_unlock(&sbi->s_orphan_lock);
|
||||
goto out_brelse;
|
||||
}
|
||||
lock_buffer(sbi->s_sbh);
|
||||
sbi->s_es->s_last_orphan = cpu_to_le32(ino_next);
|
||||
ext4_superblock_csum_set(inode->i_sb);
|
||||
unlock_buffer(sbi->s_sbh);
|
||||
mutex_unlock(&sbi->s_orphan_lock);
|
||||
err = ext4_handle_dirty_super(handle, inode->i_sb);
|
||||
err = ext4_handle_dirty_metadata(handle, NULL, sbi->s_sbh);
|
||||
} else {
|
||||
struct ext4_iloc iloc2;
|
||||
struct inode *i_prev =
|
||||
@ -3593,9 +3599,6 @@ static int ext4_setent(handle_t *handle, struct ext4_renament *ent,
|
||||
return retval2;
|
||||
}
|
||||
}
|
||||
brelse(ent->bh);
|
||||
ent->bh = NULL;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
@ -3794,6 +3797,7 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
}
|
||||
}
|
||||
|
||||
old_file_type = old.de->file_type;
|
||||
if (IS_DIRSYNC(old.dir) || IS_DIRSYNC(new.dir))
|
||||
ext4_handle_sync(handle);
|
||||
|
||||
@ -3821,7 +3825,6 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
force_reread = (new.dir->i_ino == old.dir->i_ino &&
|
||||
ext4_test_inode_flag(new.dir, EXT4_INODE_INLINE_DATA));
|
||||
|
||||
old_file_type = old.de->file_type;
|
||||
if (whiteout) {
|
||||
/*
|
||||
* Do this before adding a new entry, so the old entry is sure
|
||||
@ -3919,15 +3922,19 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
retval = 0;
|
||||
|
||||
end_rename:
|
||||
if (whiteout) {
|
||||
if (retval) {
|
||||
ext4_setent(handle, &old,
|
||||
old.inode->i_ino, old_file_type);
|
||||
drop_nlink(whiteout);
|
||||
}
|
||||
unlock_new_inode(whiteout);
|
||||
iput(whiteout);
|
||||
|
||||
}
|
||||
brelse(old.dir_bh);
|
||||
brelse(old.bh);
|
||||
brelse(new.bh);
|
||||
if (whiteout) {
|
||||
if (retval)
|
||||
drop_nlink(whiteout);
|
||||
unlock_new_inode(whiteout);
|
||||
iput(whiteout);
|
||||
}
|
||||
if (handle)
|
||||
ext4_journal_stop(handle);
|
||||
return retval;
|
||||
|
@ -899,8 +899,11 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
|
||||
EXT4_SB(sb)->s_gdb_count++;
|
||||
ext4_kvfree_array_rcu(o_group_desc);
|
||||
|
||||
lock_buffer(EXT4_SB(sb)->s_sbh);
|
||||
le16_add_cpu(&es->s_reserved_gdt_blocks, -1);
|
||||
err = ext4_handle_dirty_super(handle, sb);
|
||||
ext4_superblock_csum_set(sb);
|
||||
unlock_buffer(EXT4_SB(sb)->s_sbh);
|
||||
err = ext4_handle_dirty_metadata(handle, NULL, EXT4_SB(sb)->s_sbh);
|
||||
if (err)
|
||||
ext4_std_error(sb, err);
|
||||
return err;
|
||||
@ -1384,6 +1387,7 @@ static void ext4_update_super(struct super_block *sb,
|
||||
reserved_blocks *= blocks_count;
|
||||
do_div(reserved_blocks, 100);
|
||||
|
||||
lock_buffer(sbi->s_sbh);
|
||||
ext4_blocks_count_set(es, ext4_blocks_count(es) + blocks_count);
|
||||
ext4_free_blocks_count_set(es, ext4_free_blocks_count(es) + free_blocks);
|
||||
le32_add_cpu(&es->s_inodes_count, EXT4_INODES_PER_GROUP(sb) *
|
||||
@ -1421,6 +1425,8 @@ static void ext4_update_super(struct super_block *sb,
|
||||
* active. */
|
||||
ext4_r_blocks_count_set(es, ext4_r_blocks_count(es) +
|
||||
reserved_blocks);
|
||||
ext4_superblock_csum_set(sb);
|
||||
unlock_buffer(sbi->s_sbh);
|
||||
|
||||
/* Update the free space counts */
|
||||
percpu_counter_add(&sbi->s_freeclusters_counter,
|
||||
@ -1515,7 +1521,7 @@ static int ext4_flex_group_add(struct super_block *sb,
|
||||
|
||||
ext4_update_super(sb, flex_gd);
|
||||
|
||||
err = ext4_handle_dirty_super(handle, sb);
|
||||
err = ext4_handle_dirty_metadata(handle, NULL, sbi->s_sbh);
|
||||
|
||||
exit_journal:
|
||||
err2 = ext4_journal_stop(handle);
|
||||
@ -1717,15 +1723,18 @@ static int ext4_group_extend_no_check(struct super_block *sb,
|
||||
goto errout;
|
||||
}
|
||||
|
||||
lock_buffer(EXT4_SB(sb)->s_sbh);
|
||||
ext4_blocks_count_set(es, o_blocks_count + add);
|
||||
ext4_free_blocks_count_set(es, ext4_free_blocks_count(es) + add);
|
||||
ext4_superblock_csum_set(sb);
|
||||
unlock_buffer(EXT4_SB(sb)->s_sbh);
|
||||
ext4_debug("freeing blocks %llu through %llu\n", o_blocks_count,
|
||||
o_blocks_count + add);
|
||||
/* We add the blocks to the bitmap and set the group need init bit */
|
||||
err = ext4_group_add_blocks(handle, sb, o_blocks_count, add);
|
||||
if (err)
|
||||
goto errout;
|
||||
ext4_handle_dirty_super(handle, sb);
|
||||
ext4_handle_dirty_metadata(handle, NULL, EXT4_SB(sb)->s_sbh);
|
||||
ext4_debug("freed blocks %llu through %llu\n", o_blocks_count,
|
||||
o_blocks_count + add);
|
||||
errout:
|
||||
@ -1874,12 +1883,15 @@ static int ext4_convert_meta_bg(struct super_block *sb, struct inode *inode)
|
||||
if (err)
|
||||
goto errout;
|
||||
|
||||
lock_buffer(sbi->s_sbh);
|
||||
ext4_clear_feature_resize_inode(sb);
|
||||
ext4_set_feature_meta_bg(sb);
|
||||
sbi->s_es->s_first_meta_bg =
|
||||
cpu_to_le32(num_desc_blocks(sb, sbi->s_groups_count));
|
||||
ext4_superblock_csum_set(sb);
|
||||
unlock_buffer(sbi->s_sbh);
|
||||
|
||||
err = ext4_handle_dirty_super(handle, sb);
|
||||
err = ext4_handle_dirty_metadata(handle, NULL, sbi->s_sbh);
|
||||
if (err) {
|
||||
ext4_std_error(sb, err);
|
||||
goto errout;
|
||||
|
190
fs/ext4/super.c
190
fs/ext4/super.c
@ -65,7 +65,8 @@ static struct ratelimit_state ext4_mount_msg_ratelimit;
|
||||
static int ext4_load_journal(struct super_block *, struct ext4_super_block *,
|
||||
unsigned long journal_devnum);
|
||||
static int ext4_show_options(struct seq_file *seq, struct dentry *root);
|
||||
static int ext4_commit_super(struct super_block *sb, int sync);
|
||||
static void ext4_update_super(struct super_block *sb);
|
||||
static int ext4_commit_super(struct super_block *sb);
|
||||
static int ext4_mark_recovery_complete(struct super_block *sb,
|
||||
struct ext4_super_block *es);
|
||||
static int ext4_clear_journal_err(struct super_block *sb,
|
||||
@ -586,15 +587,12 @@ static int ext4_errno_to_code(int errno)
|
||||
return EXT4_ERR_UNKNOWN;
|
||||
}
|
||||
|
||||
static void __save_error_info(struct super_block *sb, int error,
|
||||
__u32 ino, __u64 block,
|
||||
const char *func, unsigned int line)
|
||||
static void save_error_info(struct super_block *sb, int error,
|
||||
__u32 ino, __u64 block,
|
||||
const char *func, unsigned int line)
|
||||
{
|
||||
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
||||
|
||||
EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
|
||||
if (bdev_read_only(sb->s_bdev))
|
||||
return;
|
||||
/* We default to EFSCORRUPTED error... */
|
||||
if (error == 0)
|
||||
error = EFSCORRUPTED;
|
||||
@ -618,15 +616,6 @@ static void __save_error_info(struct super_block *sb, int error,
|
||||
spin_unlock(&sbi->s_error_lock);
|
||||
}
|
||||
|
||||
static void save_error_info(struct super_block *sb, int error,
|
||||
__u32 ino, __u64 block,
|
||||
const char *func, unsigned int line)
|
||||
{
|
||||
__save_error_info(sb, error, ino, block, func, line);
|
||||
if (!bdev_read_only(sb->s_bdev))
|
||||
ext4_commit_super(sb, 1);
|
||||
}
|
||||
|
||||
/* Deal with the reporting of failure conditions on a filesystem such as
|
||||
* inconsistencies detected or read IO failures.
|
||||
*
|
||||
@ -647,19 +636,40 @@ static void save_error_info(struct super_block *sb, int error,
|
||||
* used to deal with unrecoverable failures such as journal IO errors or ENOMEM
|
||||
* at a critical moment in log management.
|
||||
*/
|
||||
static void ext4_handle_error(struct super_block *sb, bool force_ro)
|
||||
static void ext4_handle_error(struct super_block *sb, bool force_ro, int error,
|
||||
__u32 ino, __u64 block,
|
||||
const char *func, unsigned int line)
|
||||
{
|
||||
journal_t *journal = EXT4_SB(sb)->s_journal;
|
||||
bool continue_fs = !force_ro && test_opt(sb, ERRORS_CONT);
|
||||
|
||||
EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
|
||||
if (test_opt(sb, WARN_ON_ERROR))
|
||||
WARN_ON_ONCE(1);
|
||||
|
||||
if (sb_rdonly(sb) || (!force_ro && test_opt(sb, ERRORS_CONT)))
|
||||
if (!continue_fs && !sb_rdonly(sb)) {
|
||||
ext4_set_mount_flag(sb, EXT4_MF_FS_ABORTED);
|
||||
if (journal)
|
||||
jbd2_journal_abort(journal, -EIO);
|
||||
}
|
||||
|
||||
if (!bdev_read_only(sb->s_bdev)) {
|
||||
save_error_info(sb, error, ino, block, func, line);
|
||||
/*
|
||||
* In case the fs should keep running, we need to writeout
|
||||
* superblock through the journal. Due to lock ordering
|
||||
* constraints, it may not be safe to do it right here so we
|
||||
* defer superblock flushing to a workqueue.
|
||||
*/
|
||||
if (continue_fs)
|
||||
schedule_work(&EXT4_SB(sb)->s_error_work);
|
||||
else
|
||||
ext4_commit_super(sb);
|
||||
}
|
||||
|
||||
if (sb_rdonly(sb) || continue_fs)
|
||||
return;
|
||||
|
||||
ext4_set_mount_flag(sb, EXT4_MF_FS_ABORTED);
|
||||
if (journal)
|
||||
jbd2_journal_abort(journal, -EIO);
|
||||
/*
|
||||
* We force ERRORS_RO behavior when system is rebooting. Otherwise we
|
||||
* could panic during 'reboot -f' as the underlying device got already
|
||||
@ -682,8 +692,39 @@ static void flush_stashed_error_work(struct work_struct *work)
|
||||
{
|
||||
struct ext4_sb_info *sbi = container_of(work, struct ext4_sb_info,
|
||||
s_error_work);
|
||||
journal_t *journal = sbi->s_journal;
|
||||
handle_t *handle;
|
||||
|
||||
ext4_commit_super(sbi->s_sb, 1);
|
||||
/*
|
||||
* If the journal is still running, we have to write out superblock
|
||||
* through the journal to avoid collisions of other journalled sb
|
||||
* updates.
|
||||
*
|
||||
* We use directly jbd2 functions here to avoid recursing back into
|
||||
* ext4 error handling code during handling of previous errors.
|
||||
*/
|
||||
if (!sb_rdonly(sbi->s_sb) && journal) {
|
||||
handle = jbd2_journal_start(journal, 1);
|
||||
if (IS_ERR(handle))
|
||||
goto write_directly;
|
||||
if (jbd2_journal_get_write_access(handle, sbi->s_sbh)) {
|
||||
jbd2_journal_stop(handle);
|
||||
goto write_directly;
|
||||
}
|
||||
ext4_update_super(sbi->s_sb);
|
||||
if (jbd2_journal_dirty_metadata(handle, sbi->s_sbh)) {
|
||||
jbd2_journal_stop(handle);
|
||||
goto write_directly;
|
||||
}
|
||||
jbd2_journal_stop(handle);
|
||||
return;
|
||||
}
|
||||
write_directly:
|
||||
/*
|
||||
* Write through journal failed. Write sb directly to get error info
|
||||
* out and hope for the best.
|
||||
*/
|
||||
ext4_commit_super(sbi->s_sb);
|
||||
}
|
||||
|
||||
#define ext4_error_ratelimit(sb) \
|
||||
@ -710,8 +751,7 @@ void __ext4_error(struct super_block *sb, const char *function,
|
||||
sb->s_id, function, line, current->comm, &vaf);
|
||||
va_end(args);
|
||||
}
|
||||
save_error_info(sb, error, 0, block, function, line);
|
||||
ext4_handle_error(sb, force_ro);
|
||||
ext4_handle_error(sb, force_ro, error, 0, block, function, line);
|
||||
}
|
||||
|
||||
void __ext4_error_inode(struct inode *inode, const char *function,
|
||||
@ -741,9 +781,8 @@ void __ext4_error_inode(struct inode *inode, const char *function,
|
||||
current->comm, &vaf);
|
||||
va_end(args);
|
||||
}
|
||||
save_error_info(inode->i_sb, error, inode->i_ino, block,
|
||||
function, line);
|
||||
ext4_handle_error(inode->i_sb, false);
|
||||
ext4_handle_error(inode->i_sb, false, error, inode->i_ino, block,
|
||||
function, line);
|
||||
}
|
||||
|
||||
void __ext4_error_file(struct file *file, const char *function,
|
||||
@ -780,9 +819,8 @@ void __ext4_error_file(struct file *file, const char *function,
|
||||
current->comm, path, &vaf);
|
||||
va_end(args);
|
||||
}
|
||||
save_error_info(inode->i_sb, EFSCORRUPTED, inode->i_ino, block,
|
||||
function, line);
|
||||
ext4_handle_error(inode->i_sb, false);
|
||||
ext4_handle_error(inode->i_sb, false, EFSCORRUPTED, inode->i_ino, block,
|
||||
function, line);
|
||||
}
|
||||
|
||||
const char *ext4_decode_error(struct super_block *sb, int errno,
|
||||
@ -849,8 +887,7 @@ void __ext4_std_error(struct super_block *sb, const char *function,
|
||||
sb->s_id, function, line, errstr);
|
||||
}
|
||||
|
||||
save_error_info(sb, -errno, 0, 0, function, line);
|
||||
ext4_handle_error(sb, false);
|
||||
ext4_handle_error(sb, false, -errno, 0, 0, function, line);
|
||||
}
|
||||
|
||||
void __ext4_msg(struct super_block *sb,
|
||||
@ -944,13 +981,16 @@ __acquires(bitlock)
|
||||
if (test_opt(sb, ERRORS_CONT)) {
|
||||
if (test_opt(sb, WARN_ON_ERROR))
|
||||
WARN_ON_ONCE(1);
|
||||
__save_error_info(sb, EFSCORRUPTED, ino, block, function, line);
|
||||
schedule_work(&EXT4_SB(sb)->s_error_work);
|
||||
EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
|
||||
if (!bdev_read_only(sb->s_bdev)) {
|
||||
save_error_info(sb, EFSCORRUPTED, ino, block, function,
|
||||
line);
|
||||
schedule_work(&EXT4_SB(sb)->s_error_work);
|
||||
}
|
||||
return;
|
||||
}
|
||||
ext4_unlock_group(sb, grp);
|
||||
save_error_info(sb, EFSCORRUPTED, ino, block, function, line);
|
||||
ext4_handle_error(sb, false);
|
||||
ext4_handle_error(sb, false, EFSCORRUPTED, ino, block, function, line);
|
||||
/*
|
||||
* We only get here in the ERRORS_RO case; relocking the group
|
||||
* may be dangerous, but nothing bad will happen since the
|
||||
@ -1152,7 +1192,7 @@ static void ext4_put_super(struct super_block *sb)
|
||||
es->s_state = cpu_to_le16(sbi->s_mount_state);
|
||||
}
|
||||
if (!sb_rdonly(sb))
|
||||
ext4_commit_super(sb, 1);
|
||||
ext4_commit_super(sb);
|
||||
|
||||
rcu_read_lock();
|
||||
group_desc = rcu_dereference(sbi->s_group_desc);
|
||||
@ -2642,7 +2682,7 @@ static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es,
|
||||
if (sbi->s_journal)
|
||||
ext4_set_feature_journal_needs_recovery(sb);
|
||||
|
||||
err = ext4_commit_super(sb, 1);
|
||||
err = ext4_commit_super(sb);
|
||||
done:
|
||||
if (test_opt(sb, DEBUG))
|
||||
printk(KERN_INFO "[EXT4 FS bs=%lu, gc=%u, "
|
||||
@ -4868,7 +4908,7 @@ no_journal:
|
||||
if (DUMMY_ENCRYPTION_ENABLED(sbi) && !sb_rdonly(sb) &&
|
||||
!ext4_has_feature_encrypt(sb)) {
|
||||
ext4_set_feature_encrypt(sb);
|
||||
ext4_commit_super(sb, 1);
|
||||
ext4_commit_super(sb);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -5418,7 +5458,7 @@ static int ext4_load_journal(struct super_block *sb,
|
||||
es->s_journal_dev = cpu_to_le32(journal_devnum);
|
||||
|
||||
/* Make sure we flush the recovery flag to disk. */
|
||||
ext4_commit_super(sb, 1);
|
||||
ext4_commit_super(sb);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -5428,16 +5468,14 @@ err_out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ext4_commit_super(struct super_block *sb, int sync)
|
||||
/* Copy state of EXT4_SB(sb) into buffer for on-disk superblock */
|
||||
static void ext4_update_super(struct super_block *sb)
|
||||
{
|
||||
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
||||
struct ext4_super_block *es = EXT4_SB(sb)->s_es;
|
||||
struct buffer_head *sbh = EXT4_SB(sb)->s_sbh;
|
||||
int error = 0;
|
||||
|
||||
if (!sbh || block_device_ejected(sb))
|
||||
return error;
|
||||
struct ext4_super_block *es = sbi->s_es;
|
||||
struct buffer_head *sbh = sbi->s_sbh;
|
||||
|
||||
lock_buffer(sbh);
|
||||
/*
|
||||
* If the file system is mounted read-only, don't update the
|
||||
* superblock write time. This avoids updating the superblock
|
||||
@ -5451,17 +5489,17 @@ static int ext4_commit_super(struct super_block *sb, int sync)
|
||||
if (!(sb->s_flags & SB_RDONLY))
|
||||
ext4_update_tstamp(es, s_wtime);
|
||||
es->s_kbytes_written =
|
||||
cpu_to_le64(EXT4_SB(sb)->s_kbytes_written +
|
||||
cpu_to_le64(sbi->s_kbytes_written +
|
||||
((part_stat_read(sb->s_bdev, sectors[STAT_WRITE]) -
|
||||
EXT4_SB(sb)->s_sectors_written_start) >> 1));
|
||||
if (percpu_counter_initialized(&EXT4_SB(sb)->s_freeclusters_counter))
|
||||
sbi->s_sectors_written_start) >> 1));
|
||||
if (percpu_counter_initialized(&sbi->s_freeclusters_counter))
|
||||
ext4_free_blocks_count_set(es,
|
||||
EXT4_C2B(EXT4_SB(sb), percpu_counter_sum_positive(
|
||||
&EXT4_SB(sb)->s_freeclusters_counter)));
|
||||
if (percpu_counter_initialized(&EXT4_SB(sb)->s_freeinodes_counter))
|
||||
EXT4_C2B(sbi, percpu_counter_sum_positive(
|
||||
&sbi->s_freeclusters_counter)));
|
||||
if (percpu_counter_initialized(&sbi->s_freeinodes_counter))
|
||||
es->s_free_inodes_count =
|
||||
cpu_to_le32(percpu_counter_sum_positive(
|
||||
&EXT4_SB(sb)->s_freeinodes_counter));
|
||||
&sbi->s_freeinodes_counter));
|
||||
/* Copy error information to the on-disk superblock */
|
||||
spin_lock(&sbi->s_error_lock);
|
||||
if (sbi->s_add_error_count > 0) {
|
||||
@ -5502,10 +5540,20 @@ static int ext4_commit_super(struct super_block *sb, int sync)
|
||||
}
|
||||
spin_unlock(&sbi->s_error_lock);
|
||||
|
||||
BUFFER_TRACE(sbh, "marking dirty");
|
||||
ext4_superblock_csum_set(sb);
|
||||
if (sync)
|
||||
lock_buffer(sbh);
|
||||
unlock_buffer(sbh);
|
||||
}
|
||||
|
||||
static int ext4_commit_super(struct super_block *sb)
|
||||
{
|
||||
struct buffer_head *sbh = EXT4_SB(sb)->s_sbh;
|
||||
int error = 0;
|
||||
|
||||
if (!sbh || block_device_ejected(sb))
|
||||
return error;
|
||||
|
||||
ext4_update_super(sb);
|
||||
|
||||
if (buffer_write_io_error(sbh) || !buffer_uptodate(sbh)) {
|
||||
/*
|
||||
* Oh, dear. A previous attempt to write the
|
||||
@ -5520,17 +5568,15 @@ static int ext4_commit_super(struct super_block *sb, int sync)
|
||||
clear_buffer_write_io_error(sbh);
|
||||
set_buffer_uptodate(sbh);
|
||||
}
|
||||
BUFFER_TRACE(sbh, "marking dirty");
|
||||
mark_buffer_dirty(sbh);
|
||||
if (sync) {
|
||||
unlock_buffer(sbh);
|
||||
error = __sync_dirty_buffer(sbh,
|
||||
REQ_SYNC | (test_opt(sb, BARRIER) ? REQ_FUA : 0));
|
||||
if (buffer_write_io_error(sbh)) {
|
||||
ext4_msg(sb, KERN_ERR, "I/O error while writing "
|
||||
"superblock");
|
||||
clear_buffer_write_io_error(sbh);
|
||||
set_buffer_uptodate(sbh);
|
||||
}
|
||||
error = __sync_dirty_buffer(sbh,
|
||||
REQ_SYNC | (test_opt(sb, BARRIER) ? REQ_FUA : 0));
|
||||
if (buffer_write_io_error(sbh)) {
|
||||
ext4_msg(sb, KERN_ERR, "I/O error while writing "
|
||||
"superblock");
|
||||
clear_buffer_write_io_error(sbh);
|
||||
set_buffer_uptodate(sbh);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
@ -5561,7 +5607,7 @@ static int ext4_mark_recovery_complete(struct super_block *sb,
|
||||
|
||||
if (ext4_has_feature_journal_needs_recovery(sb) && sb_rdonly(sb)) {
|
||||
ext4_clear_feature_journal_needs_recovery(sb);
|
||||
ext4_commit_super(sb, 1);
|
||||
ext4_commit_super(sb);
|
||||
}
|
||||
out:
|
||||
jbd2_journal_unlock_updates(journal);
|
||||
@ -5603,7 +5649,7 @@ static int ext4_clear_journal_err(struct super_block *sb,
|
||||
|
||||
EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
|
||||
es->s_state |= cpu_to_le16(EXT4_ERROR_FS);
|
||||
ext4_commit_super(sb, 1);
|
||||
ext4_commit_super(sb);
|
||||
|
||||
jbd2_journal_clear_err(journal);
|
||||
jbd2_journal_update_sb_errno(journal);
|
||||
@ -5705,7 +5751,7 @@ static int ext4_freeze(struct super_block *sb)
|
||||
ext4_clear_feature_journal_needs_recovery(sb);
|
||||
}
|
||||
|
||||
error = ext4_commit_super(sb, 1);
|
||||
error = ext4_commit_super(sb);
|
||||
out:
|
||||
if (journal)
|
||||
/* we rely on upper layer to stop further updates */
|
||||
@ -5727,7 +5773,7 @@ static int ext4_unfreeze(struct super_block *sb)
|
||||
ext4_set_feature_journal_needs_recovery(sb);
|
||||
}
|
||||
|
||||
ext4_commit_super(sb, 1);
|
||||
ext4_commit_super(sb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -5987,7 +6033,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
|
||||
}
|
||||
|
||||
if (sbi->s_journal == NULL && !(old_sb_flags & SB_RDONLY)) {
|
||||
err = ext4_commit_super(sb, 1);
|
||||
err = ext4_commit_super(sb);
|
||||
if (err)
|
||||
goto restore_opts;
|
||||
}
|
||||
|
@ -792,8 +792,11 @@ static void ext4_xattr_update_super_block(handle_t *handle,
|
||||
|
||||
BUFFER_TRACE(EXT4_SB(sb)->s_sbh, "get_write_access");
|
||||
if (ext4_journal_get_write_access(handle, EXT4_SB(sb)->s_sbh) == 0) {
|
||||
lock_buffer(EXT4_SB(sb)->s_sbh);
|
||||
ext4_set_feature_xattr(sb);
|
||||
ext4_handle_dirty_super(handle, sb);
|
||||
ext4_superblock_csum_set(sb);
|
||||
unlock_buffer(EXT4_SB(sb)->s_sbh);
|
||||
ext4_handle_dirty_metadata(handle, NULL, EXT4_SB(sb)->s_sbh);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user