mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-16 01:54:00 +00:00
A lot of miscellaneous ext4 bug fixes and cleanups this cycle, most
notably in the journaling code, bufered I/O, and compiler warning cleanups. -----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEEK2m5VNv+CHkogTfJ8vlZVpUNgaMFAmc7NN4ACgkQ8vlZVpUN gaMJRAf+Oc3Tn/ZvuX0amkaBQI+ZNIeYD/U0WBSvarKb00bo1X39mM/0LovqV6ec c51iRgt8U6uDZDUm6zJtppkIUiqkHRj+TmTInueFtmUqhIg8jgfZIpxCn0QkFKnQ jI5EKCkvUqM0B347axH/s+dlOE9JBSlQNKgjkvCYOGknQ1PH6X8oMDt5QAqGEk3P Nsa4QChIxt2yujFvydgFT+RAbjvY3sNvrZ7D3B+KL3VSJpILChVZK/UdFrraSXxq mLO5j4txjtnr/OLgujCTHOfPsTiQReHHXErrSbKhnFhrTXLD0mZSUgJ6irpaxRQ5 wQHQzmsrVwqFfqPU3Hkl8FGeCR0owQ== =26/E -----END PGP SIGNATURE----- Merge tag 'ext4_for_linus-6.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4 Pull ext4 updates from Ted Ts'o: "A lot of miscellaneous ext4 bug fixes and cleanups this cycle, most notably in the journaling code, bufered I/O, and compiler warning cleanups" * tag 'ext4_for_linus-6.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: (33 commits) jbd2: Fix comment describing journal_init_common() ext4: prevent an infinite loop in the lazyinit thread ext4: use struct_size() to improve ext4_htree_store_dirent() ext4: annotate struct fname with __counted_by() jbd2: avoid dozens of -Wflex-array-member-not-at-end warnings ext4: use str_yes_no() helper function ext4: prevent delalloc to nodelalloc on remount jbd2: make b_frozen_data allocation always succeed ext4: cleanup variable name in ext4_fc_del() ext4: use string choices helpers jbd2: remove the 'success' parameter from the jbd2_do_replay() function jbd2: remove useless 'block_error' variable jbd2: factor out jbd2_do_replay() jbd2: refactor JBD2_COMMIT_BLOCK process in do_one_pass() jbd2: unified release of buffer_head in do_one_pass() jbd2: remove redundant judgments for check v1 checksum ext4: use ERR_CAST to return an error-valued pointer mm: zero range of eof folio exposed by inode size extension ext4: partial zero eof block on unaligned inode size extension ext4: disambiguate the return value of ext4_dio_write_end_io() ...
This commit is contained in:
commit
3e7447ab48
@ -550,7 +550,8 @@ ext4_read_block_bitmap_nowait(struct super_block *sb, ext4_group_t block_group,
|
|||||||
trace_ext4_read_block_bitmap_load(sb, block_group, ignore_locked);
|
trace_ext4_read_block_bitmap_load(sb, block_group, ignore_locked);
|
||||||
ext4_read_bh_nowait(bh, REQ_META | REQ_PRIO |
|
ext4_read_bh_nowait(bh, REQ_META | REQ_PRIO |
|
||||||
(ignore_locked ? REQ_RAHEAD : 0),
|
(ignore_locked ? REQ_RAHEAD : 0),
|
||||||
ext4_end_bitmap_read);
|
ext4_end_bitmap_read,
|
||||||
|
ext4_simulate_fail(sb, EXT4_SIM_BBITMAP_EIO));
|
||||||
return bh;
|
return bh;
|
||||||
verify:
|
verify:
|
||||||
err = ext4_validate_block_bitmap(sb, desc, block_group, bh);
|
err = ext4_validate_block_bitmap(sb, desc, block_group, bh);
|
||||||
@ -577,7 +578,6 @@ int ext4_wait_block_bitmap(struct super_block *sb, ext4_group_t block_group,
|
|||||||
if (!desc)
|
if (!desc)
|
||||||
return -EFSCORRUPTED;
|
return -EFSCORRUPTED;
|
||||||
wait_on_buffer(bh);
|
wait_on_buffer(bh);
|
||||||
ext4_simulate_fail_bh(sb, bh, EXT4_SIM_BBITMAP_EIO);
|
|
||||||
if (!buffer_uptodate(bh)) {
|
if (!buffer_uptodate(bh)) {
|
||||||
ext4_error_err(sb, EIO, "Cannot read block bitmap - "
|
ext4_error_err(sb, EIO, "Cannot read block bitmap - "
|
||||||
"block_group = %u, block_bitmap = %llu",
|
"block_group = %u, block_bitmap = %llu",
|
||||||
|
@ -418,7 +418,7 @@ struct fname {
|
|||||||
__u32 inode;
|
__u32 inode;
|
||||||
__u8 name_len;
|
__u8 name_len;
|
||||||
__u8 file_type;
|
__u8 file_type;
|
||||||
char name[];
|
char name[] __counted_by(name_len);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -471,14 +471,13 @@ int ext4_htree_store_dirent(struct file *dir_file, __u32 hash,
|
|||||||
struct rb_node **p, *parent = NULL;
|
struct rb_node **p, *parent = NULL;
|
||||||
struct fname *fname, *new_fn;
|
struct fname *fname, *new_fn;
|
||||||
struct dir_private_info *info;
|
struct dir_private_info *info;
|
||||||
int len;
|
|
||||||
|
|
||||||
info = dir_file->private_data;
|
info = dir_file->private_data;
|
||||||
p = &info->root.rb_node;
|
p = &info->root.rb_node;
|
||||||
|
|
||||||
/* Create and allocate the fname structure */
|
/* Create and allocate the fname structure */
|
||||||
len = sizeof(struct fname) + ent_name->len + 1;
|
new_fn = kzalloc(struct_size(new_fn, name, ent_name->len + 1),
|
||||||
new_fn = kzalloc(len, GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!new_fn)
|
if (!new_fn)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
new_fn->hash = hash;
|
new_fn->hash = hash;
|
||||||
|
@ -1869,14 +1869,6 @@ static inline bool ext4_simulate_fail(struct super_block *sb,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void ext4_simulate_fail_bh(struct super_block *sb,
|
|
||||||
struct buffer_head *bh,
|
|
||||||
unsigned long code)
|
|
||||||
{
|
|
||||||
if (!IS_ERR(bh) && ext4_simulate_fail(sb, code))
|
|
||||||
clear_buffer_uptodate(bh);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Error number codes for s_{first,last}_error_errno
|
* Error number codes for s_{first,last}_error_errno
|
||||||
*
|
*
|
||||||
@ -3104,9 +3096,9 @@ extern struct buffer_head *ext4_sb_bread(struct super_block *sb,
|
|||||||
extern struct buffer_head *ext4_sb_bread_unmovable(struct super_block *sb,
|
extern struct buffer_head *ext4_sb_bread_unmovable(struct super_block *sb,
|
||||||
sector_t block);
|
sector_t block);
|
||||||
extern void ext4_read_bh_nowait(struct buffer_head *bh, blk_opf_t op_flags,
|
extern void ext4_read_bh_nowait(struct buffer_head *bh, blk_opf_t op_flags,
|
||||||
bh_end_io_t *end_io);
|
bh_end_io_t *end_io, bool simu_fail);
|
||||||
extern int ext4_read_bh(struct buffer_head *bh, blk_opf_t op_flags,
|
extern int ext4_read_bh(struct buffer_head *bh, blk_opf_t op_flags,
|
||||||
bh_end_io_t *end_io);
|
bh_end_io_t *end_io, bool simu_fail);
|
||||||
extern int ext4_read_bh_lock(struct buffer_head *bh, blk_opf_t op_flags, bool wait);
|
extern int ext4_read_bh_lock(struct buffer_head *bh, blk_opf_t op_flags, bool wait);
|
||||||
extern void ext4_sb_breadahead_unmovable(struct super_block *sb, sector_t block);
|
extern void ext4_sb_breadahead_unmovable(struct super_block *sb, sector_t block);
|
||||||
extern int ext4_seq_options_show(struct seq_file *seq, void *offset);
|
extern int ext4_seq_options_show(struct seq_file *seq, void *offset);
|
||||||
|
@ -568,7 +568,7 @@ __read_extent_tree_block(const char *function, unsigned int line,
|
|||||||
|
|
||||||
if (!bh_uptodate_or_lock(bh)) {
|
if (!bh_uptodate_or_lock(bh)) {
|
||||||
trace_ext4_ext_load_extent(inode, pblk, _RET_IP_);
|
trace_ext4_ext_load_extent(inode, pblk, _RET_IP_);
|
||||||
err = ext4_read_bh(bh, 0, NULL);
|
err = ext4_read_bh(bh, 0, NULL, false);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto errout;
|
goto errout;
|
||||||
}
|
}
|
||||||
@ -3138,7 +3138,7 @@ static void ext4_zeroout_es(struct inode *inode, struct ext4_extent *ex)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
ext4_es_insert_extent(inode, ee_block, ee_len, ee_pblock,
|
ext4_es_insert_extent(inode, ee_block, ee_len, ee_pblock,
|
||||||
EXTENT_STATUS_WRITTEN, 0);
|
EXTENT_STATUS_WRITTEN, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FIXME!! we need to try to merge to left or right after zero-out */
|
/* FIXME!! we need to try to merge to left or right after zero-out */
|
||||||
@ -4158,7 +4158,7 @@ insert_hole:
|
|||||||
/* Put just found gap into cache to speed up subsequent requests */
|
/* Put just found gap into cache to speed up subsequent requests */
|
||||||
ext_debug(inode, " -> %u:%u\n", hole_start, len);
|
ext_debug(inode, " -> %u:%u\n", hole_start, len);
|
||||||
ext4_es_insert_extent(inode, hole_start, len, ~0,
|
ext4_es_insert_extent(inode, hole_start, len, ~0,
|
||||||
EXTENT_STATUS_HOLE, 0);
|
EXTENT_STATUS_HOLE, false);
|
||||||
|
|
||||||
/* Update hole_len to reflect hole size after lblk */
|
/* Update hole_len to reflect hole size after lblk */
|
||||||
if (hole_start != lblk)
|
if (hole_start != lblk)
|
||||||
@ -4482,7 +4482,7 @@ static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset,
|
|||||||
int depth = 0;
|
int depth = 0;
|
||||||
struct ext4_map_blocks map;
|
struct ext4_map_blocks map;
|
||||||
unsigned int credits;
|
unsigned int credits;
|
||||||
loff_t epos;
|
loff_t epos, old_size = i_size_read(inode);
|
||||||
|
|
||||||
BUG_ON(!ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS));
|
BUG_ON(!ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS));
|
||||||
map.m_lblk = offset;
|
map.m_lblk = offset;
|
||||||
@ -4541,6 +4541,11 @@ retry:
|
|||||||
if (ext4_update_inode_size(inode, epos) & 0x1)
|
if (ext4_update_inode_size(inode, epos) & 0x1)
|
||||||
inode_set_mtime_to_ts(inode,
|
inode_set_mtime_to_ts(inode,
|
||||||
inode_get_ctime(inode));
|
inode_get_ctime(inode));
|
||||||
|
if (epos > old_size) {
|
||||||
|
pagecache_isize_extended(inode, old_size, epos);
|
||||||
|
ext4_zero_partial_blocks(handle, inode,
|
||||||
|
old_size, epos - old_size);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ret2 = ext4_mark_inode_dirty(handle, inode);
|
ret2 = ext4_mark_inode_dirty(handle, inode);
|
||||||
ext4_update_inode_fsync_trans(handle, inode, 1);
|
ext4_update_inode_fsync_trans(handle, inode, 1);
|
||||||
|
@ -848,7 +848,7 @@ out:
|
|||||||
*/
|
*/
|
||||||
void ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk,
|
void ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk,
|
||||||
ext4_lblk_t len, ext4_fsblk_t pblk,
|
ext4_lblk_t len, ext4_fsblk_t pblk,
|
||||||
unsigned int status, int flags)
|
unsigned int status, bool delalloc_reserve_used)
|
||||||
{
|
{
|
||||||
struct extent_status newes;
|
struct extent_status newes;
|
||||||
ext4_lblk_t end = lblk + len - 1;
|
ext4_lblk_t end = lblk + len - 1;
|
||||||
@ -863,8 +863,8 @@ void ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk,
|
|||||||
if (EXT4_SB(inode->i_sb)->s_mount_state & EXT4_FC_REPLAY)
|
if (EXT4_SB(inode->i_sb)->s_mount_state & EXT4_FC_REPLAY)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
es_debug("add [%u/%u) %llu %x %x to extent status tree of inode %lu\n",
|
es_debug("add [%u/%u) %llu %x %d to extent status tree of inode %lu\n",
|
||||||
lblk, len, pblk, status, flags, inode->i_ino);
|
lblk, len, pblk, status, delalloc_reserve_used, inode->i_ino);
|
||||||
|
|
||||||
if (!len)
|
if (!len)
|
||||||
return;
|
return;
|
||||||
@ -945,7 +945,7 @@ error:
|
|||||||
resv_used += pending;
|
resv_used += pending;
|
||||||
if (resv_used)
|
if (resv_used)
|
||||||
ext4_da_update_reserve_space(inode, resv_used,
|
ext4_da_update_reserve_space(inode, resv_used,
|
||||||
flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE);
|
delalloc_reserve_used);
|
||||||
|
|
||||||
if (err1 || err2 || err3 < 0)
|
if (err1 || err2 || err3 < 0)
|
||||||
goto retry;
|
goto retry;
|
||||||
|
@ -135,7 +135,8 @@ extern void ext4_es_init_tree(struct ext4_es_tree *tree);
|
|||||||
|
|
||||||
extern void ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk,
|
extern void ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk,
|
||||||
ext4_lblk_t len, ext4_fsblk_t pblk,
|
ext4_lblk_t len, ext4_fsblk_t pblk,
|
||||||
unsigned int status, int flags);
|
unsigned int status,
|
||||||
|
bool delalloc_reserve_used);
|
||||||
extern void ext4_es_cache_extent(struct inode *inode, ext4_lblk_t lblk,
|
extern void ext4_es_cache_extent(struct inode *inode, ext4_lblk_t lblk,
|
||||||
ext4_lblk_t len, ext4_fsblk_t pblk,
|
ext4_lblk_t len, ext4_fsblk_t pblk,
|
||||||
unsigned int status);
|
unsigned int status);
|
||||||
|
@ -291,9 +291,9 @@ void ext4_fc_del(struct inode *inode)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
restart:
|
restart:
|
||||||
spin_lock(&EXT4_SB(inode->i_sb)->s_fc_lock);
|
spin_lock(&sbi->s_fc_lock);
|
||||||
if (list_empty(&ei->i_fc_list) && list_empty(&ei->i_fc_dilist)) {
|
if (list_empty(&ei->i_fc_list) && list_empty(&ei->i_fc_dilist)) {
|
||||||
spin_unlock(&EXT4_SB(inode->i_sb)->s_fc_lock);
|
spin_unlock(&sbi->s_fc_lock);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,9 +357,7 @@ void ext4_fc_mark_ineligible(struct super_block *sb, int reason, handle_t *handl
|
|||||||
}
|
}
|
||||||
spin_lock(&sbi->s_fc_lock);
|
spin_lock(&sbi->s_fc_lock);
|
||||||
is_ineligible = ext4_test_mount_flag(sb, EXT4_MF_FC_INELIGIBLE);
|
is_ineligible = ext4_test_mount_flag(sb, EXT4_MF_FC_INELIGIBLE);
|
||||||
if (has_transaction &&
|
if (has_transaction && (!is_ineligible || tid_gt(tid, sbi->s_fc_ineligible_tid)))
|
||||||
(!is_ineligible ||
|
|
||||||
(is_ineligible && tid_gt(tid, sbi->s_fc_ineligible_tid))))
|
|
||||||
sbi->s_fc_ineligible_tid = tid;
|
sbi->s_fc_ineligible_tid = tid;
|
||||||
ext4_set_mount_flag(sb, EXT4_MF_FC_INELIGIBLE);
|
ext4_set_mount_flag(sb, EXT4_MF_FC_INELIGIBLE);
|
||||||
spin_unlock(&sbi->s_fc_lock);
|
spin_unlock(&sbi->s_fc_lock);
|
||||||
|
@ -392,8 +392,9 @@ static int ext4_dio_write_end_io(struct kiocb *iocb, ssize_t size,
|
|||||||
*/
|
*/
|
||||||
if (pos + size <= READ_ONCE(EXT4_I(inode)->i_disksize) &&
|
if (pos + size <= READ_ONCE(EXT4_I(inode)->i_disksize) &&
|
||||||
pos + size <= i_size_read(inode))
|
pos + size <= i_size_read(inode))
|
||||||
return size;
|
return 0;
|
||||||
return ext4_handle_inode_extension(inode, pos, size, size);
|
error = ext4_handle_inode_extension(inode, pos, size, size);
|
||||||
|
return error < 0 ? error : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct iomap_dio_ops ext4_dio_write_ops = {
|
static const struct iomap_dio_ops ext4_dio_write_ops = {
|
||||||
@ -564,12 +565,9 @@ static ssize_t ext4_dio_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret = ext4_orphan_add(handle, inode);
|
ret = ext4_orphan_add(handle, inode);
|
||||||
if (ret) {
|
|
||||||
ext4_journal_stop(handle);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
ext4_journal_stop(handle);
|
ext4_journal_stop(handle);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ilock_shared && !unwritten)
|
if (ilock_shared && !unwritten)
|
||||||
|
@ -185,6 +185,56 @@ static inline ext4_fsblk_t ext4_fsmap_next_pblk(struct ext4_fsmap *fmr)
|
|||||||
return fmr->fmr_physical + fmr->fmr_length;
|
return fmr->fmr_physical + fmr->fmr_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ext4_getfsmap_meta_helper(struct super_block *sb,
|
||||||
|
ext4_group_t agno, ext4_grpblk_t start,
|
||||||
|
ext4_grpblk_t len, void *priv)
|
||||||
|
{
|
||||||
|
struct ext4_getfsmap_info *info = priv;
|
||||||
|
struct ext4_fsmap *p;
|
||||||
|
struct ext4_fsmap *tmp;
|
||||||
|
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
||||||
|
ext4_fsblk_t fsb, fs_start, fs_end;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
fs_start = fsb = (EXT4_C2B(sbi, start) +
|
||||||
|
ext4_group_first_block_no(sb, agno));
|
||||||
|
fs_end = fs_start + EXT4_C2B(sbi, len);
|
||||||
|
|
||||||
|
/* Return relevant extents from the meta_list */
|
||||||
|
list_for_each_entry_safe(p, tmp, &info->gfi_meta_list, fmr_list) {
|
||||||
|
if (p->fmr_physical < info->gfi_next_fsblk) {
|
||||||
|
list_del(&p->fmr_list);
|
||||||
|
kfree(p);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (p->fmr_physical <= fs_start ||
|
||||||
|
p->fmr_physical + p->fmr_length <= fs_end) {
|
||||||
|
/* Emit the retained free extent record if present */
|
||||||
|
if (info->gfi_lastfree.fmr_owner) {
|
||||||
|
error = ext4_getfsmap_helper(sb, info,
|
||||||
|
&info->gfi_lastfree);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
info->gfi_lastfree.fmr_owner = 0;
|
||||||
|
}
|
||||||
|
error = ext4_getfsmap_helper(sb, info, p);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
fsb = p->fmr_physical + p->fmr_length;
|
||||||
|
if (info->gfi_next_fsblk < fsb)
|
||||||
|
info->gfi_next_fsblk = fsb;
|
||||||
|
list_del(&p->fmr_list);
|
||||||
|
kfree(p);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (info->gfi_next_fsblk < fsb)
|
||||||
|
info->gfi_next_fsblk = fsb;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Transform a blockgroup's free record into a fsmap */
|
/* Transform a blockgroup's free record into a fsmap */
|
||||||
static int ext4_getfsmap_datadev_helper(struct super_block *sb,
|
static int ext4_getfsmap_datadev_helper(struct super_block *sb,
|
||||||
ext4_group_t agno, ext4_grpblk_t start,
|
ext4_group_t agno, ext4_grpblk_t start,
|
||||||
@ -539,6 +589,7 @@ static int ext4_getfsmap_datadev(struct super_block *sb,
|
|||||||
error = ext4_mballoc_query_range(sb, info->gfi_agno,
|
error = ext4_mballoc_query_range(sb, info->gfi_agno,
|
||||||
EXT4_B2C(sbi, info->gfi_low.fmr_physical),
|
EXT4_B2C(sbi, info->gfi_low.fmr_physical),
|
||||||
EXT4_B2C(sbi, info->gfi_high.fmr_physical),
|
EXT4_B2C(sbi, info->gfi_high.fmr_physical),
|
||||||
|
ext4_getfsmap_meta_helper,
|
||||||
ext4_getfsmap_datadev_helper, info);
|
ext4_getfsmap_datadev_helper, info);
|
||||||
if (error)
|
if (error)
|
||||||
goto err;
|
goto err;
|
||||||
@ -560,7 +611,8 @@ static int ext4_getfsmap_datadev(struct super_block *sb,
|
|||||||
|
|
||||||
/* Report any gaps at the end of the bg */
|
/* Report any gaps at the end of the bg */
|
||||||
info->gfi_last = true;
|
info->gfi_last = true;
|
||||||
error = ext4_getfsmap_datadev_helper(sb, end_ag, last_cluster, 0, info);
|
error = ext4_getfsmap_datadev_helper(sb, end_ag, last_cluster + 1,
|
||||||
|
0, info);
|
||||||
if (error)
|
if (error)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
|
@ -193,8 +193,9 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group)
|
|||||||
* submit the buffer_head for reading
|
* submit the buffer_head for reading
|
||||||
*/
|
*/
|
||||||
trace_ext4_load_inode_bitmap(sb, block_group);
|
trace_ext4_load_inode_bitmap(sb, block_group);
|
||||||
ext4_read_bh(bh, REQ_META | REQ_PRIO, ext4_end_bitmap_read);
|
ext4_read_bh(bh, REQ_META | REQ_PRIO,
|
||||||
ext4_simulate_fail_bh(sb, bh, EXT4_SIM_IBITMAP_EIO);
|
ext4_end_bitmap_read,
|
||||||
|
ext4_simulate_fail(sb, EXT4_SIM_IBITMAP_EIO));
|
||||||
if (!buffer_uptodate(bh)) {
|
if (!buffer_uptodate(bh)) {
|
||||||
put_bh(bh);
|
put_bh(bh);
|
||||||
ext4_error_err(sb, EIO, "Cannot read inode bitmap - "
|
ext4_error_err(sb, EIO, "Cannot read inode bitmap - "
|
||||||
|
@ -170,7 +170,7 @@ static Indirect *ext4_get_branch(struct inode *inode, int depth,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!bh_uptodate_or_lock(bh)) {
|
if (!bh_uptodate_or_lock(bh)) {
|
||||||
if (ext4_read_bh(bh, 0, NULL) < 0) {
|
if (ext4_read_bh(bh, 0, NULL, false) < 0) {
|
||||||
put_bh(bh);
|
put_bh(bh);
|
||||||
goto failure;
|
goto failure;
|
||||||
}
|
}
|
||||||
|
@ -483,7 +483,7 @@ static int ext4_map_query_blocks(handle_t *handle, struct inode *inode,
|
|||||||
status = map->m_flags & EXT4_MAP_UNWRITTEN ?
|
status = map->m_flags & EXT4_MAP_UNWRITTEN ?
|
||||||
EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN;
|
EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN;
|
||||||
ext4_es_insert_extent(inode, map->m_lblk, map->m_len,
|
ext4_es_insert_extent(inode, map->m_lblk, map->m_len,
|
||||||
map->m_pblk, status, 0);
|
map->m_pblk, status, false);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -563,8 +563,8 @@ static int ext4_map_create_blocks(handle_t *handle, struct inode *inode,
|
|||||||
|
|
||||||
status = map->m_flags & EXT4_MAP_UNWRITTEN ?
|
status = map->m_flags & EXT4_MAP_UNWRITTEN ?
|
||||||
EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN;
|
EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN;
|
||||||
ext4_es_insert_extent(inode, map->m_lblk, map->m_len,
|
ext4_es_insert_extent(inode, map->m_lblk, map->m_len, map->m_pblk,
|
||||||
map->m_pblk, status, flags);
|
status, flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE);
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
@ -856,7 +856,14 @@ struct buffer_head *ext4_getblk(handle_t *handle, struct inode *inode,
|
|||||||
if (nowait)
|
if (nowait)
|
||||||
return sb_find_get_block(inode->i_sb, map.m_pblk);
|
return sb_find_get_block(inode->i_sb, map.m_pblk);
|
||||||
|
|
||||||
bh = sb_getblk(inode->i_sb, map.m_pblk);
|
/*
|
||||||
|
* Since bh could introduce extra ref count such as referred by
|
||||||
|
* journal_head etc. Try to avoid using __GFP_MOVABLE here
|
||||||
|
* as it may fail the migration when journal_head remains.
|
||||||
|
*/
|
||||||
|
bh = getblk_unmovable(inode->i_sb->s_bdev, map.m_pblk,
|
||||||
|
inode->i_sb->s_blocksize);
|
||||||
|
|
||||||
if (unlikely(!bh))
|
if (unlikely(!bh))
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
if (map.m_flags & EXT4_MAP_NEW) {
|
if (map.m_flags & EXT4_MAP_NEW) {
|
||||||
@ -1307,8 +1314,10 @@ static int ext4_write_end(struct file *file,
|
|||||||
folio_unlock(folio);
|
folio_unlock(folio);
|
||||||
folio_put(folio);
|
folio_put(folio);
|
||||||
|
|
||||||
if (old_size < pos && !verity)
|
if (old_size < pos && !verity) {
|
||||||
pagecache_isize_extended(inode, old_size, pos);
|
pagecache_isize_extended(inode, old_size, pos);
|
||||||
|
ext4_zero_partial_blocks(handle, inode, old_size, pos - old_size);
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* Don't mark the inode dirty under folio lock. First, it unnecessarily
|
* Don't mark the inode dirty under folio lock. First, it unnecessarily
|
||||||
* makes the holding time of folio lock longer. Second, it forces lock
|
* makes the holding time of folio lock longer. Second, it forces lock
|
||||||
@ -1423,8 +1432,10 @@ static int ext4_journalled_write_end(struct file *file,
|
|||||||
folio_unlock(folio);
|
folio_unlock(folio);
|
||||||
folio_put(folio);
|
folio_put(folio);
|
||||||
|
|
||||||
if (old_size < pos && !verity)
|
if (old_size < pos && !verity) {
|
||||||
pagecache_isize_extended(inode, old_size, pos);
|
pagecache_isize_extended(inode, old_size, pos);
|
||||||
|
ext4_zero_partial_blocks(handle, inode, old_size, pos - old_size);
|
||||||
|
}
|
||||||
|
|
||||||
if (size_changed) {
|
if (size_changed) {
|
||||||
ret2 = ext4_mark_inode_dirty(handle, inode);
|
ret2 = ext4_mark_inode_dirty(handle, inode);
|
||||||
@ -2985,7 +2996,8 @@ static int ext4_da_do_write_end(struct address_space *mapping,
|
|||||||
struct inode *inode = mapping->host;
|
struct inode *inode = mapping->host;
|
||||||
loff_t old_size = inode->i_size;
|
loff_t old_size = inode->i_size;
|
||||||
bool disksize_changed = false;
|
bool disksize_changed = false;
|
||||||
loff_t new_i_size;
|
loff_t new_i_size, zero_len = 0;
|
||||||
|
handle_t *handle;
|
||||||
|
|
||||||
if (unlikely(!folio_buffers(folio))) {
|
if (unlikely(!folio_buffers(folio))) {
|
||||||
folio_unlock(folio);
|
folio_unlock(folio);
|
||||||
@ -3029,19 +3041,22 @@ static int ext4_da_do_write_end(struct address_space *mapping,
|
|||||||
folio_unlock(folio);
|
folio_unlock(folio);
|
||||||
folio_put(folio);
|
folio_put(folio);
|
||||||
|
|
||||||
if (old_size < pos)
|
if (pos > old_size) {
|
||||||
pagecache_isize_extended(inode, old_size, pos);
|
pagecache_isize_extended(inode, old_size, pos);
|
||||||
|
zero_len = pos - old_size;
|
||||||
if (disksize_changed) {
|
|
||||||
handle_t *handle;
|
|
||||||
|
|
||||||
handle = ext4_journal_start(inode, EXT4_HT_INODE, 2);
|
|
||||||
if (IS_ERR(handle))
|
|
||||||
return PTR_ERR(handle);
|
|
||||||
ext4_mark_inode_dirty(handle, inode);
|
|
||||||
ext4_journal_stop(handle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!disksize_changed && !zero_len)
|
||||||
|
return copied;
|
||||||
|
|
||||||
|
handle = ext4_journal_start(inode, EXT4_HT_INODE, 2);
|
||||||
|
if (IS_ERR(handle))
|
||||||
|
return PTR_ERR(handle);
|
||||||
|
if (zero_len)
|
||||||
|
ext4_zero_partial_blocks(handle, inode, old_size, zero_len);
|
||||||
|
ext4_mark_inode_dirty(handle, inode);
|
||||||
|
ext4_journal_stop(handle);
|
||||||
|
|
||||||
return copied;
|
return copied;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4514,10 +4529,10 @@ make_io:
|
|||||||
* Read the block from disk.
|
* Read the block from disk.
|
||||||
*/
|
*/
|
||||||
trace_ext4_load_inode(sb, ino);
|
trace_ext4_load_inode(sb, ino);
|
||||||
ext4_read_bh_nowait(bh, REQ_META | REQ_PRIO, NULL);
|
ext4_read_bh_nowait(bh, REQ_META | REQ_PRIO, NULL,
|
||||||
|
ext4_simulate_fail(sb, EXT4_SIM_INODE_EIO));
|
||||||
blk_finish_plug(&plug);
|
blk_finish_plug(&plug);
|
||||||
wait_on_buffer(bh);
|
wait_on_buffer(bh);
|
||||||
ext4_simulate_fail_bh(sb, bh, EXT4_SIM_INODE_EIO);
|
|
||||||
if (!buffer_uptodate(bh)) {
|
if (!buffer_uptodate(bh)) {
|
||||||
if (ret_block)
|
if (ret_block)
|
||||||
*ret_block = block;
|
*ret_block = block;
|
||||||
@ -5443,6 +5458,14 @@ int ext4_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (attr->ia_size != inode->i_size) {
|
if (attr->ia_size != inode->i_size) {
|
||||||
|
/* attach jbd2 jinode for EOF folio tail zeroing */
|
||||||
|
if (attr->ia_size & (inode->i_sb->s_blocksize - 1) ||
|
||||||
|
oldsize & (inode->i_sb->s_blocksize - 1)) {
|
||||||
|
error = ext4_inode_attach_jinode(inode);
|
||||||
|
if (error)
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
|
||||||
handle = ext4_journal_start(inode, EXT4_HT_INODE, 3);
|
handle = ext4_journal_start(inode, EXT4_HT_INODE, 3);
|
||||||
if (IS_ERR(handle)) {
|
if (IS_ERR(handle)) {
|
||||||
error = PTR_ERR(handle);
|
error = PTR_ERR(handle);
|
||||||
@ -5453,12 +5476,17 @@ int ext4_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
|||||||
orphan = 1;
|
orphan = 1;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* Update c/mtime on truncate up, ext4_truncate() will
|
* Update c/mtime and tail zero the EOF folio on
|
||||||
* update c/mtime in shrink case below
|
* truncate up. ext4_truncate() handles the shrink case
|
||||||
|
* below.
|
||||||
*/
|
*/
|
||||||
if (!shrink)
|
if (!shrink) {
|
||||||
inode_set_mtime_to_ts(inode,
|
inode_set_mtime_to_ts(inode,
|
||||||
inode_set_ctime_current(inode));
|
inode_set_ctime_current(inode));
|
||||||
|
if (oldsize & (inode->i_sb->s_blocksize - 1))
|
||||||
|
ext4_block_truncate_page(handle,
|
||||||
|
inode->i_mapping, oldsize);
|
||||||
|
}
|
||||||
|
|
||||||
if (shrink)
|
if (shrink)
|
||||||
ext4_fc_track_range(handle, inode,
|
ext4_fc_track_range(handle, inode,
|
||||||
|
@ -5711,7 +5711,7 @@ static void ext4_mb_show_ac(struct ext4_allocation_context *ac)
|
|||||||
(unsigned long)ac->ac_b_ex.fe_logical,
|
(unsigned long)ac->ac_b_ex.fe_logical,
|
||||||
(int)ac->ac_criteria);
|
(int)ac->ac_criteria);
|
||||||
mb_debug(sb, "%u found", ac->ac_found);
|
mb_debug(sb, "%u found", ac->ac_found);
|
||||||
mb_debug(sb, "used pa: %s, ", ac->ac_pa ? "yes" : "no");
|
mb_debug(sb, "used pa: %s, ", str_yes_no(ac->ac_pa));
|
||||||
if (ac->ac_pa)
|
if (ac->ac_pa)
|
||||||
mb_debug(sb, "pa_type %s\n", ac->ac_pa->pa_type == MB_GROUP_PA ?
|
mb_debug(sb, "pa_type %s\n", ac->ac_pa->pa_type == MB_GROUP_PA ?
|
||||||
"group pa" : "inode pa");
|
"group pa" : "inode pa");
|
||||||
@ -6056,7 +6056,7 @@ static bool ext4_mb_discard_preallocations_should_retry(struct super_block *sb,
|
|||||||
}
|
}
|
||||||
|
|
||||||
out_dbg:
|
out_dbg:
|
||||||
mb_debug(sb, "freed %d, retry ? %s\n", freed, ret ? "yes" : "no");
|
mb_debug(sb, "freed %d, retry ? %s\n", freed, str_yes_no(ret));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6999,13 +6999,14 @@ int
|
|||||||
ext4_mballoc_query_range(
|
ext4_mballoc_query_range(
|
||||||
struct super_block *sb,
|
struct super_block *sb,
|
||||||
ext4_group_t group,
|
ext4_group_t group,
|
||||||
ext4_grpblk_t start,
|
ext4_grpblk_t first,
|
||||||
ext4_grpblk_t end,
|
ext4_grpblk_t end,
|
||||||
|
ext4_mballoc_query_range_fn meta_formatter,
|
||||||
ext4_mballoc_query_range_fn formatter,
|
ext4_mballoc_query_range_fn formatter,
|
||||||
void *priv)
|
void *priv)
|
||||||
{
|
{
|
||||||
void *bitmap;
|
void *bitmap;
|
||||||
ext4_grpblk_t next;
|
ext4_grpblk_t start, next;
|
||||||
struct ext4_buddy e4b;
|
struct ext4_buddy e4b;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
@ -7016,10 +7017,19 @@ ext4_mballoc_query_range(
|
|||||||
|
|
||||||
ext4_lock_group(sb, group);
|
ext4_lock_group(sb, group);
|
||||||
|
|
||||||
start = max(e4b.bd_info->bb_first_free, start);
|
start = max(e4b.bd_info->bb_first_free, first);
|
||||||
if (end >= EXT4_CLUSTERS_PER_GROUP(sb))
|
if (end >= EXT4_CLUSTERS_PER_GROUP(sb))
|
||||||
end = EXT4_CLUSTERS_PER_GROUP(sb) - 1;
|
end = EXT4_CLUSTERS_PER_GROUP(sb) - 1;
|
||||||
|
if (meta_formatter && start != first) {
|
||||||
|
if (start > end)
|
||||||
|
start = end;
|
||||||
|
ext4_unlock_group(sb, group);
|
||||||
|
error = meta_formatter(sb, group, first, start - first,
|
||||||
|
priv);
|
||||||
|
if (error)
|
||||||
|
goto out_unload;
|
||||||
|
ext4_lock_group(sb, group);
|
||||||
|
}
|
||||||
while (start <= end) {
|
while (start <= end) {
|
||||||
start = mb_find_next_zero_bit(bitmap, end + 1, start);
|
start = mb_find_next_zero_bit(bitmap, end + 1, start);
|
||||||
if (start > end)
|
if (start > end)
|
||||||
|
@ -259,6 +259,7 @@ ext4_mballoc_query_range(
|
|||||||
ext4_group_t agno,
|
ext4_group_t agno,
|
||||||
ext4_grpblk_t start,
|
ext4_grpblk_t start,
|
||||||
ext4_grpblk_t end,
|
ext4_grpblk_t end,
|
||||||
|
ext4_mballoc_query_range_fn meta_formatter,
|
||||||
ext4_mballoc_query_range_fn formatter,
|
ext4_mballoc_query_range_fn formatter,
|
||||||
void *priv);
|
void *priv);
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ static int read_mmp_block(struct super_block *sb, struct buffer_head **bh,
|
|||||||
}
|
}
|
||||||
|
|
||||||
lock_buffer(*bh);
|
lock_buffer(*bh);
|
||||||
ret = ext4_read_bh(*bh, REQ_META | REQ_PRIO, NULL);
|
ret = ext4_read_bh(*bh, REQ_META | REQ_PRIO, NULL, false);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto warn_exit;
|
goto warn_exit;
|
||||||
|
|
||||||
|
@ -213,7 +213,7 @@ static int mext_page_mkuptodate(struct folio *folio, size_t from, size_t to)
|
|||||||
unlock_buffer(bh);
|
unlock_buffer(bh);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ext4_read_bh_nowait(bh, 0, NULL);
|
ext4_read_bh_nowait(bh, 0, NULL, false);
|
||||||
nr++;
|
nr++;
|
||||||
} while (block++, (bh = bh->b_this_page) != head);
|
} while (block++, (bh = bh->b_this_page) != head);
|
||||||
|
|
||||||
|
@ -1747,7 +1747,7 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
|
|||||||
#endif
|
#endif
|
||||||
frame = dx_probe(fname, dir, NULL, frames);
|
frame = dx_probe(fname, dir, NULL, frames);
|
||||||
if (IS_ERR(frame))
|
if (IS_ERR(frame))
|
||||||
return (struct buffer_head *) frame;
|
return ERR_CAST(frame);
|
||||||
do {
|
do {
|
||||||
block = dx_get_block(frame->at);
|
block = dx_get_block(frame->at);
|
||||||
bh = ext4_read_dirblock(dir, block, DIRENT_HTREE);
|
bh = ext4_read_dirblock(dir, block, DIRENT_HTREE);
|
||||||
@ -1952,7 +1952,7 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
|
|||||||
if (IS_ERR(bh2)) {
|
if (IS_ERR(bh2)) {
|
||||||
brelse(*bh);
|
brelse(*bh);
|
||||||
*bh = NULL;
|
*bh = NULL;
|
||||||
return (struct ext4_dir_entry_2 *) bh2;
|
return ERR_CAST(bh2);
|
||||||
}
|
}
|
||||||
|
|
||||||
BUFFER_TRACE(*bh, "get_write_access");
|
BUFFER_TRACE(*bh, "get_write_access");
|
||||||
@ -2000,8 +2000,17 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
|
|||||||
else
|
else
|
||||||
split = count/2;
|
split = count/2;
|
||||||
|
|
||||||
|
if (WARN_ON_ONCE(split == 0)) {
|
||||||
|
/* Should never happen, but avoid out-of-bounds access below */
|
||||||
|
ext4_error_inode_block(dir, (*bh)->b_blocknr, 0,
|
||||||
|
"bad indexed directory? hash=%08x:%08x count=%d move=%u",
|
||||||
|
hinfo->hash, hinfo->minor_hash, count, move);
|
||||||
|
err = -EFSCORRUPTED;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
hash2 = map[split].hash;
|
hash2 = map[split].hash;
|
||||||
continued = split > 0 ? hash2 == map[split - 1].hash : 0;
|
continued = hash2 == map[split - 1].hash;
|
||||||
dxtrace(printk(KERN_INFO "Split block %lu at %x, %i/%i\n",
|
dxtrace(printk(KERN_INFO "Split block %lu at %x, %i/%i\n",
|
||||||
(unsigned long)dx_get_block(frame->at),
|
(unsigned long)dx_get_block(frame->at),
|
||||||
hash2, split, count-split));
|
hash2, split, count-split));
|
||||||
@ -2043,10 +2052,11 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
|
|||||||
return de;
|
return de;
|
||||||
|
|
||||||
journal_error:
|
journal_error:
|
||||||
|
ext4_std_error(dir->i_sb, err);
|
||||||
|
out:
|
||||||
brelse(*bh);
|
brelse(*bh);
|
||||||
brelse(bh2);
|
brelse(bh2);
|
||||||
*bh = NULL;
|
*bh = NULL;
|
||||||
ext4_std_error(dir->i_sb, err);
|
|
||||||
return ERR_PTR(err);
|
return ERR_PTR(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -417,8 +417,10 @@ static void io_submit_add_bh(struct ext4_io_submit *io,
|
|||||||
submit_and_retry:
|
submit_and_retry:
|
||||||
ext4_io_submit(io);
|
ext4_io_submit(io);
|
||||||
}
|
}
|
||||||
if (io->io_bio == NULL)
|
if (io->io_bio == NULL) {
|
||||||
io_submit_init_bio(io, bh);
|
io_submit_init_bio(io, bh);
|
||||||
|
io->io_bio->bi_write_hint = inode->i_write_hint;
|
||||||
|
}
|
||||||
if (!bio_add_folio(io->io_bio, io_folio, bh->b_size, bh_offset(bh)))
|
if (!bio_add_folio(io->io_bio, io_folio, bh->b_size, bh_offset(bh)))
|
||||||
goto submit_and_retry;
|
goto submit_and_retry;
|
||||||
wbc_account_cgroup_owner(io->io_wbc, folio, bh->b_size);
|
wbc_account_cgroup_owner(io->io_wbc, folio, bh->b_size);
|
||||||
|
@ -1300,7 +1300,7 @@ static struct buffer_head *ext4_get_bitmap(struct super_block *sb, __u64 block)
|
|||||||
if (unlikely(!bh))
|
if (unlikely(!bh))
|
||||||
return NULL;
|
return NULL;
|
||||||
if (!bh_uptodate_or_lock(bh)) {
|
if (!bh_uptodate_or_lock(bh)) {
|
||||||
if (ext4_read_bh(bh, 0, NULL) < 0) {
|
if (ext4_read_bh(bh, 0, NULL, false) < 0) {
|
||||||
brelse(bh);
|
brelse(bh);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -161,8 +161,14 @@ MODULE_ALIAS("ext3");
|
|||||||
|
|
||||||
|
|
||||||
static inline void __ext4_read_bh(struct buffer_head *bh, blk_opf_t op_flags,
|
static inline void __ext4_read_bh(struct buffer_head *bh, blk_opf_t op_flags,
|
||||||
bh_end_io_t *end_io)
|
bh_end_io_t *end_io, bool simu_fail)
|
||||||
{
|
{
|
||||||
|
if (simu_fail) {
|
||||||
|
clear_buffer_uptodate(bh);
|
||||||
|
unlock_buffer(bh);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* buffer's verified bit is no longer valid after reading from
|
* buffer's verified bit is no longer valid after reading from
|
||||||
* disk again due to write out error, clear it to make sure we
|
* disk again due to write out error, clear it to make sure we
|
||||||
@ -176,7 +182,7 @@ static inline void __ext4_read_bh(struct buffer_head *bh, blk_opf_t op_flags,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ext4_read_bh_nowait(struct buffer_head *bh, blk_opf_t op_flags,
|
void ext4_read_bh_nowait(struct buffer_head *bh, blk_opf_t op_flags,
|
||||||
bh_end_io_t *end_io)
|
bh_end_io_t *end_io, bool simu_fail)
|
||||||
{
|
{
|
||||||
BUG_ON(!buffer_locked(bh));
|
BUG_ON(!buffer_locked(bh));
|
||||||
|
|
||||||
@ -184,10 +190,11 @@ void ext4_read_bh_nowait(struct buffer_head *bh, blk_opf_t op_flags,
|
|||||||
unlock_buffer(bh);
|
unlock_buffer(bh);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
__ext4_read_bh(bh, op_flags, end_io);
|
__ext4_read_bh(bh, op_flags, end_io, simu_fail);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ext4_read_bh(struct buffer_head *bh, blk_opf_t op_flags, bh_end_io_t *end_io)
|
int ext4_read_bh(struct buffer_head *bh, blk_opf_t op_flags,
|
||||||
|
bh_end_io_t *end_io, bool simu_fail)
|
||||||
{
|
{
|
||||||
BUG_ON(!buffer_locked(bh));
|
BUG_ON(!buffer_locked(bh));
|
||||||
|
|
||||||
@ -196,7 +203,7 @@ int ext4_read_bh(struct buffer_head *bh, blk_opf_t op_flags, bh_end_io_t *end_io
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
__ext4_read_bh(bh, op_flags, end_io);
|
__ext4_read_bh(bh, op_flags, end_io, simu_fail);
|
||||||
|
|
||||||
wait_on_buffer(bh);
|
wait_on_buffer(bh);
|
||||||
if (buffer_uptodate(bh))
|
if (buffer_uptodate(bh))
|
||||||
@ -208,10 +215,10 @@ int ext4_read_bh_lock(struct buffer_head *bh, blk_opf_t op_flags, bool wait)
|
|||||||
{
|
{
|
||||||
lock_buffer(bh);
|
lock_buffer(bh);
|
||||||
if (!wait) {
|
if (!wait) {
|
||||||
ext4_read_bh_nowait(bh, op_flags, NULL);
|
ext4_read_bh_nowait(bh, op_flags, NULL, false);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return ext4_read_bh(bh, op_flags, NULL);
|
return ext4_read_bh(bh, op_flags, NULL, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -266,7 +273,7 @@ void ext4_sb_breadahead_unmovable(struct super_block *sb, sector_t block)
|
|||||||
|
|
||||||
if (likely(bh)) {
|
if (likely(bh)) {
|
||||||
if (trylock_buffer(bh))
|
if (trylock_buffer(bh))
|
||||||
ext4_read_bh_nowait(bh, REQ_RAHEAD, NULL);
|
ext4_read_bh_nowait(bh, REQ_RAHEAD, NULL, false);
|
||||||
brelse(bh);
|
brelse(bh);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -346,9 +353,9 @@ __u32 ext4_free_group_clusters(struct super_block *sb,
|
|||||||
__u32 ext4_free_inodes_count(struct super_block *sb,
|
__u32 ext4_free_inodes_count(struct super_block *sb,
|
||||||
struct ext4_group_desc *bg)
|
struct ext4_group_desc *bg)
|
||||||
{
|
{
|
||||||
return le16_to_cpu(bg->bg_free_inodes_count_lo) |
|
return le16_to_cpu(READ_ONCE(bg->bg_free_inodes_count_lo)) |
|
||||||
(EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT ?
|
(EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT ?
|
||||||
(__u32)le16_to_cpu(bg->bg_free_inodes_count_hi) << 16 : 0);
|
(__u32)le16_to_cpu(READ_ONCE(bg->bg_free_inodes_count_hi)) << 16 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
__u32 ext4_used_dirs_count(struct super_block *sb,
|
__u32 ext4_used_dirs_count(struct super_block *sb,
|
||||||
@ -402,9 +409,9 @@ void ext4_free_group_clusters_set(struct super_block *sb,
|
|||||||
void ext4_free_inodes_set(struct super_block *sb,
|
void ext4_free_inodes_set(struct super_block *sb,
|
||||||
struct ext4_group_desc *bg, __u32 count)
|
struct ext4_group_desc *bg, __u32 count)
|
||||||
{
|
{
|
||||||
bg->bg_free_inodes_count_lo = cpu_to_le16((__u16)count);
|
WRITE_ONCE(bg->bg_free_inodes_count_lo, cpu_to_le16((__u16)count));
|
||||||
if (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT)
|
if (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT)
|
||||||
bg->bg_free_inodes_count_hi = cpu_to_le16(count >> 16);
|
WRITE_ONCE(bg->bg_free_inodes_count_hi, cpu_to_le16(count >> 16));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ext4_used_dirs_set(struct super_block *sb,
|
void ext4_used_dirs_set(struct super_block *sb,
|
||||||
@ -2096,16 +2103,16 @@ static int ext4_parse_test_dummy_encryption(const struct fs_parameter *param,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#define EXT4_SET_CTX(name) \
|
#define EXT4_SET_CTX(name) \
|
||||||
static inline void ctx_set_##name(struct ext4_fs_context *ctx, \
|
static inline __maybe_unused \
|
||||||
unsigned long flag) \
|
void ctx_set_##name(struct ext4_fs_context *ctx, unsigned long flag) \
|
||||||
{ \
|
{ \
|
||||||
ctx->mask_s_##name |= flag; \
|
ctx->mask_s_##name |= flag; \
|
||||||
ctx->vals_s_##name |= flag; \
|
ctx->vals_s_##name |= flag; \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define EXT4_CLEAR_CTX(name) \
|
#define EXT4_CLEAR_CTX(name) \
|
||||||
static inline void ctx_clear_##name(struct ext4_fs_context *ctx, \
|
static inline __maybe_unused \
|
||||||
unsigned long flag) \
|
void ctx_clear_##name(struct ext4_fs_context *ctx, unsigned long flag) \
|
||||||
{ \
|
{ \
|
||||||
ctx->mask_s_##name |= flag; \
|
ctx->mask_s_##name |= flag; \
|
||||||
ctx->vals_s_##name &= ~flag; \
|
ctx->vals_s_##name &= ~flag; \
|
||||||
@ -3030,6 +3037,9 @@ static int _ext4_show_options(struct seq_file *seq, struct super_block *sb,
|
|||||||
SEQ_OPTS_PUTS("mb_optimize_scan=1");
|
SEQ_OPTS_PUTS("mb_optimize_scan=1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (nodefs && !test_opt(sb, NO_PREFETCH_BLOCK_BITMAPS))
|
||||||
|
SEQ_OPTS_PUTS("prefetch_block_bitmaps");
|
||||||
|
|
||||||
ext4_show_quota_options(seq, sb);
|
ext4_show_quota_options(seq, sb);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -3709,12 +3719,12 @@ static int ext4_run_li_request(struct ext4_li_request *elr)
|
|||||||
ret = 1;
|
ret = 1;
|
||||||
|
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
start_time = ktime_get_real_ns();
|
start_time = ktime_get_ns();
|
||||||
ret = ext4_init_inode_table(sb, group,
|
ret = ext4_init_inode_table(sb, group,
|
||||||
elr->lr_timeout ? 0 : 1);
|
elr->lr_timeout ? 0 : 1);
|
||||||
trace_ext4_lazy_itable_init(sb, group);
|
trace_ext4_lazy_itable_init(sb, group);
|
||||||
if (elr->lr_timeout == 0) {
|
if (elr->lr_timeout == 0) {
|
||||||
elr->lr_timeout = nsecs_to_jiffies((ktime_get_real_ns() - start_time) *
|
elr->lr_timeout = nsecs_to_jiffies((ktime_get_ns() - start_time) *
|
||||||
EXT4_SB(elr->lr_super)->s_li_wait_mult);
|
EXT4_SB(elr->lr_super)->s_li_wait_mult);
|
||||||
}
|
}
|
||||||
elr->lr_next_sched = jiffies + elr->lr_timeout;
|
elr->lr_next_sched = jiffies + elr->lr_timeout;
|
||||||
@ -3774,8 +3784,9 @@ static int ext4_lazyinit_thread(void *arg)
|
|||||||
|
|
||||||
cont_thread:
|
cont_thread:
|
||||||
while (true) {
|
while (true) {
|
||||||
next_wakeup = MAX_JIFFY_OFFSET;
|
bool next_wakeup_initialized = false;
|
||||||
|
|
||||||
|
next_wakeup = 0;
|
||||||
mutex_lock(&eli->li_list_mtx);
|
mutex_lock(&eli->li_list_mtx);
|
||||||
if (list_empty(&eli->li_request_list)) {
|
if (list_empty(&eli->li_request_list)) {
|
||||||
mutex_unlock(&eli->li_list_mtx);
|
mutex_unlock(&eli->li_list_mtx);
|
||||||
@ -3788,8 +3799,11 @@ cont_thread:
|
|||||||
lr_request);
|
lr_request);
|
||||||
|
|
||||||
if (time_before(jiffies, elr->lr_next_sched)) {
|
if (time_before(jiffies, elr->lr_next_sched)) {
|
||||||
if (time_before(elr->lr_next_sched, next_wakeup))
|
if (!next_wakeup_initialized ||
|
||||||
|
time_before(elr->lr_next_sched, next_wakeup)) {
|
||||||
next_wakeup = elr->lr_next_sched;
|
next_wakeup = elr->lr_next_sched;
|
||||||
|
next_wakeup_initialized = true;
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (down_read_trylock(&elr->lr_super->s_umount)) {
|
if (down_read_trylock(&elr->lr_super->s_umount)) {
|
||||||
@ -3817,16 +3831,18 @@ cont_thread:
|
|||||||
elr->lr_next_sched = jiffies +
|
elr->lr_next_sched = jiffies +
|
||||||
get_random_u32_below(EXT4_DEF_LI_MAX_START_DELAY * HZ);
|
get_random_u32_below(EXT4_DEF_LI_MAX_START_DELAY * HZ);
|
||||||
}
|
}
|
||||||
if (time_before(elr->lr_next_sched, next_wakeup))
|
if (!next_wakeup_initialized ||
|
||||||
|
time_before(elr->lr_next_sched, next_wakeup)) {
|
||||||
next_wakeup = elr->lr_next_sched;
|
next_wakeup = elr->lr_next_sched;
|
||||||
|
next_wakeup_initialized = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mutex_unlock(&eli->li_list_mtx);
|
mutex_unlock(&eli->li_list_mtx);
|
||||||
|
|
||||||
try_to_freeze();
|
try_to_freeze();
|
||||||
|
|
||||||
cur = jiffies;
|
cur = jiffies;
|
||||||
if ((time_after_eq(cur, next_wakeup)) ||
|
if (!next_wakeup_initialized || time_after_eq(cur, next_wakeup)) {
|
||||||
(MAX_JIFFY_OFFSET == next_wakeup)) {
|
|
||||||
cond_resched();
|
cond_resched();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -6332,7 +6348,7 @@ static int ext4_sync_fs(struct super_block *sb, int wait)
|
|||||||
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
||||||
|
|
||||||
if (unlikely(ext4_forced_shutdown(sb)))
|
if (unlikely(ext4_forced_shutdown(sb)))
|
||||||
return 0;
|
return -EIO;
|
||||||
|
|
||||||
trace_ext4_sync_fs(sb, wait);
|
trace_ext4_sync_fs(sb, wait);
|
||||||
flush_workqueue(sbi->rsv_conversion_wq);
|
flush_workqueue(sbi->rsv_conversion_wq);
|
||||||
@ -6549,8 +6565,12 @@ static int __ext4_remount(struct fs_context *fc, struct super_block *sb)
|
|||||||
goto restore_opts;
|
goto restore_opts;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (test_opt2(sb, ABORT))
|
if ((old_opts.s_mount_opt & EXT4_MOUNT_DELALLOC) &&
|
||||||
ext4_abort(sb, ESHUTDOWN, "Abort forced by user");
|
!test_opt(sb, DELALLOC)) {
|
||||||
|
ext4_msg(sb, KERN_ERR, "can't disable delalloc during remount");
|
||||||
|
err = -EINVAL;
|
||||||
|
goto restore_opts;
|
||||||
|
}
|
||||||
|
|
||||||
sb->s_flags = (sb->s_flags & ~SB_POSIXACL) |
|
sb->s_flags = (sb->s_flags & ~SB_POSIXACL) |
|
||||||
(test_opt(sb, POSIX_ACL) ? SB_POSIXACL : 0);
|
(test_opt(sb, POSIX_ACL) ? SB_POSIXACL : 0);
|
||||||
@ -6720,6 +6740,14 @@ static int __ext4_remount(struct fs_context *fc, struct super_block *sb)
|
|||||||
if (!ext4_has_feature_mmp(sb) || sb_rdonly(sb))
|
if (!ext4_has_feature_mmp(sb) || sb_rdonly(sb))
|
||||||
ext4_stop_mmpd(sbi);
|
ext4_stop_mmpd(sbi);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle aborting the filesystem as the last thing during remount to
|
||||||
|
* avoid obsure errors during remount when some option changes fail to
|
||||||
|
* apply due to shutdown filesystem.
|
||||||
|
*/
|
||||||
|
if (test_opt2(sb, ABORT))
|
||||||
|
ext4_abort(sb, ESHUTDOWN, "Abort forced by user");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
restore_opts:
|
restore_opts:
|
||||||
|
@ -662,10 +662,6 @@ void jbd2_journal_commit_transaction(journal_t *journal)
|
|||||||
JBUFFER_TRACE(jh, "ph3: write metadata");
|
JBUFFER_TRACE(jh, "ph3: write metadata");
|
||||||
escape = jbd2_journal_write_metadata_buffer(commit_transaction,
|
escape = jbd2_journal_write_metadata_buffer(commit_transaction,
|
||||||
jh, &wbuf[bufs], blocknr);
|
jh, &wbuf[bufs], blocknr);
|
||||||
if (escape < 0) {
|
|
||||||
jbd2_journal_abort(journal, escape);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
jbd2_file_log_bh(&io_bufs, wbuf[bufs]);
|
jbd2_file_log_bh(&io_bufs, wbuf[bufs]);
|
||||||
|
|
||||||
/* Record the new block's tag in the current descriptor
|
/* Record the new block's tag in the current descriptor
|
||||||
|
@ -318,7 +318,6 @@ static inline void jbd2_data_do_escape(char *data)
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* Return value:
|
* Return value:
|
||||||
* <0: Error
|
|
||||||
* =0: Finished OK without escape
|
* =0: Finished OK without escape
|
||||||
* =1: Finished OK with escape
|
* =1: Finished OK with escape
|
||||||
*/
|
*/
|
||||||
@ -386,12 +385,7 @@ int jbd2_journal_write_metadata_buffer(transaction_t *transaction,
|
|||||||
goto escape_done;
|
goto escape_done;
|
||||||
|
|
||||||
spin_unlock(&jh_in->b_state_lock);
|
spin_unlock(&jh_in->b_state_lock);
|
||||||
tmp = jbd2_alloc(bh_in->b_size, GFP_NOFS);
|
tmp = jbd2_alloc(bh_in->b_size, GFP_NOFS | __GFP_NOFAIL);
|
||||||
if (!tmp) {
|
|
||||||
brelse(new_bh);
|
|
||||||
free_buffer_head(new_bh);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
spin_lock(&jh_in->b_state_lock);
|
spin_lock(&jh_in->b_state_lock);
|
||||||
if (jh_in->b_frozen_data) {
|
if (jh_in->b_frozen_data) {
|
||||||
jbd2_free(tmp, bh_in->b_size);
|
jbd2_free(tmp, bh_in->b_size);
|
||||||
@ -1518,9 +1512,10 @@ static int journal_load_superblock(journal_t *journal)
|
|||||||
* destroy journal_t structures, and to initialise and read existing
|
* destroy journal_t structures, and to initialise and read existing
|
||||||
* journal blocks from disk. */
|
* journal blocks from disk. */
|
||||||
|
|
||||||
/* First: create and setup a journal_t object in memory. We initialise
|
/* The journal_init_common() function creates and fills a journal_t object
|
||||||
* very few fields yet: that has to wait until we have created the
|
* in memory. It calls journal_load_superblock() to load the on-disk journal
|
||||||
* journal structures from from scratch, or loaded them from disk. */
|
* superblock and initialize the journal_t object.
|
||||||
|
*/
|
||||||
|
|
||||||
static journal_t *journal_init_common(struct block_device *bdev,
|
static journal_t *journal_init_common(struct block_device *bdev,
|
||||||
struct block_device *fs_dev,
|
struct block_device *fs_dev,
|
||||||
|
@ -485,6 +485,104 @@ static int jbd2_block_tag_csum_verify(journal_t *j, journal_block_tag_t *tag,
|
|||||||
return tag->t_checksum == cpu_to_be16(csum32);
|
return tag->t_checksum == cpu_to_be16(csum32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static __always_inline int jbd2_do_replay(journal_t *journal,
|
||||||
|
struct recovery_info *info,
|
||||||
|
struct buffer_head *bh,
|
||||||
|
unsigned long *next_log_block,
|
||||||
|
unsigned int next_commit_ID)
|
||||||
|
{
|
||||||
|
char *tagp;
|
||||||
|
int flags;
|
||||||
|
int ret = 0;
|
||||||
|
int tag_bytes = journal_tag_bytes(journal);
|
||||||
|
int descr_csum_size = 0;
|
||||||
|
unsigned long io_block;
|
||||||
|
journal_block_tag_t tag;
|
||||||
|
struct buffer_head *obh;
|
||||||
|
struct buffer_head *nbh;
|
||||||
|
|
||||||
|
if (jbd2_journal_has_csum_v2or3(journal))
|
||||||
|
descr_csum_size = sizeof(struct jbd2_journal_block_tail);
|
||||||
|
|
||||||
|
tagp = &bh->b_data[sizeof(journal_header_t)];
|
||||||
|
while (tagp - bh->b_data + tag_bytes <=
|
||||||
|
journal->j_blocksize - descr_csum_size) {
|
||||||
|
int err;
|
||||||
|
|
||||||
|
memcpy(&tag, tagp, sizeof(tag));
|
||||||
|
flags = be16_to_cpu(tag.t_flags);
|
||||||
|
|
||||||
|
io_block = (*next_log_block)++;
|
||||||
|
wrap(journal, *next_log_block);
|
||||||
|
err = jread(&obh, journal, io_block);
|
||||||
|
if (err) {
|
||||||
|
/* Recover what we can, but report failure at the end. */
|
||||||
|
ret = err;
|
||||||
|
pr_err("JBD2: IO error %d recovering block %lu in log\n",
|
||||||
|
err, io_block);
|
||||||
|
} else {
|
||||||
|
unsigned long long blocknr;
|
||||||
|
|
||||||
|
J_ASSERT(obh != NULL);
|
||||||
|
blocknr = read_tag_block(journal, &tag);
|
||||||
|
|
||||||
|
/* If the block has been revoked, then we're all done here. */
|
||||||
|
if (jbd2_journal_test_revoke(journal, blocknr,
|
||||||
|
next_commit_ID)) {
|
||||||
|
brelse(obh);
|
||||||
|
++info->nr_revoke_hits;
|
||||||
|
goto skip_write;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Look for block corruption */
|
||||||
|
if (!jbd2_block_tag_csum_verify(journal, &tag,
|
||||||
|
(journal_block_tag3_t *)tagp,
|
||||||
|
obh->b_data, next_commit_ID)) {
|
||||||
|
brelse(obh);
|
||||||
|
ret = -EFSBADCRC;
|
||||||
|
pr_err("JBD2: Invalid checksum recovering data block %llu in journal block %lu\n",
|
||||||
|
blocknr, io_block);
|
||||||
|
goto skip_write;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find a buffer for the new data being restored */
|
||||||
|
nbh = __getblk(journal->j_fs_dev, blocknr,
|
||||||
|
journal->j_blocksize);
|
||||||
|
if (nbh == NULL) {
|
||||||
|
pr_err("JBD2: Out of memory during recovery.\n");
|
||||||
|
brelse(obh);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock_buffer(nbh);
|
||||||
|
memcpy(nbh->b_data, obh->b_data, journal->j_blocksize);
|
||||||
|
if (flags & JBD2_FLAG_ESCAPE) {
|
||||||
|
*((__be32 *)nbh->b_data) =
|
||||||
|
cpu_to_be32(JBD2_MAGIC_NUMBER);
|
||||||
|
}
|
||||||
|
|
||||||
|
BUFFER_TRACE(nbh, "marking dirty");
|
||||||
|
set_buffer_uptodate(nbh);
|
||||||
|
mark_buffer_dirty(nbh);
|
||||||
|
BUFFER_TRACE(nbh, "marking uptodate");
|
||||||
|
++info->nr_replays;
|
||||||
|
unlock_buffer(nbh);
|
||||||
|
brelse(obh);
|
||||||
|
brelse(nbh);
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_write:
|
||||||
|
tagp += tag_bytes;
|
||||||
|
if (!(flags & JBD2_FLAG_SAME_UUID))
|
||||||
|
tagp += 16;
|
||||||
|
|
||||||
|
if (flags & JBD2_FLAG_LAST_TAG)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int do_one_pass(journal_t *journal,
|
static int do_one_pass(journal_t *journal,
|
||||||
struct recovery_info *info, enum passtype pass)
|
struct recovery_info *info, enum passtype pass)
|
||||||
{
|
{
|
||||||
@ -493,13 +591,10 @@ static int do_one_pass(journal_t *journal,
|
|||||||
int err, success = 0;
|
int err, success = 0;
|
||||||
journal_superblock_t * sb;
|
journal_superblock_t * sb;
|
||||||
journal_header_t * tmp;
|
journal_header_t * tmp;
|
||||||
struct buffer_head * bh;
|
struct buffer_head *bh = NULL;
|
||||||
unsigned int sequence;
|
unsigned int sequence;
|
||||||
int blocktype;
|
int blocktype;
|
||||||
int tag_bytes = journal_tag_bytes(journal);
|
|
||||||
__u32 crc32_sum = ~0; /* Transactional Checksums */
|
__u32 crc32_sum = ~0; /* Transactional Checksums */
|
||||||
int descr_csum_size = 0;
|
|
||||||
int block_error = 0;
|
|
||||||
bool need_check_commit_time = false;
|
bool need_check_commit_time = false;
|
||||||
__u64 last_trans_commit_time = 0, commit_time;
|
__u64 last_trans_commit_time = 0, commit_time;
|
||||||
|
|
||||||
@ -528,12 +623,6 @@ static int do_one_pass(journal_t *journal,
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
int flags;
|
|
||||||
char * tagp;
|
|
||||||
journal_block_tag_t tag;
|
|
||||||
struct buffer_head * obh;
|
|
||||||
struct buffer_head * nbh;
|
|
||||||
|
|
||||||
cond_resched();
|
cond_resched();
|
||||||
|
|
||||||
/* If we already know where to stop the log traversal,
|
/* If we already know where to stop the log traversal,
|
||||||
@ -552,6 +641,8 @@ static int do_one_pass(journal_t *journal,
|
|||||||
* record. */
|
* record. */
|
||||||
|
|
||||||
jbd2_debug(3, "JBD2: checking block %ld\n", next_log_block);
|
jbd2_debug(3, "JBD2: checking block %ld\n", next_log_block);
|
||||||
|
brelse(bh);
|
||||||
|
bh = NULL;
|
||||||
err = jread(&bh, journal, next_log_block);
|
err = jread(&bh, journal, next_log_block);
|
||||||
if (err)
|
if (err)
|
||||||
goto failed;
|
goto failed;
|
||||||
@ -567,20 +658,16 @@ static int do_one_pass(journal_t *journal,
|
|||||||
|
|
||||||
tmp = (journal_header_t *)bh->b_data;
|
tmp = (journal_header_t *)bh->b_data;
|
||||||
|
|
||||||
if (tmp->h_magic != cpu_to_be32(JBD2_MAGIC_NUMBER)) {
|
if (tmp->h_magic != cpu_to_be32(JBD2_MAGIC_NUMBER))
|
||||||
brelse(bh);
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
blocktype = be32_to_cpu(tmp->h_blocktype);
|
blocktype = be32_to_cpu(tmp->h_blocktype);
|
||||||
sequence = be32_to_cpu(tmp->h_sequence);
|
sequence = be32_to_cpu(tmp->h_sequence);
|
||||||
jbd2_debug(3, "Found magic %d, sequence %d\n",
|
jbd2_debug(3, "Found magic %d, sequence %d\n",
|
||||||
blocktype, sequence);
|
blocktype, sequence);
|
||||||
|
|
||||||
if (sequence != next_commit_ID) {
|
if (sequence != next_commit_ID)
|
||||||
brelse(bh);
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
/* OK, we have a valid descriptor block which matches
|
/* OK, we have a valid descriptor block which matches
|
||||||
* all of the sequence number checks. What are we going
|
* all of the sequence number checks. What are we going
|
||||||
@ -589,11 +676,7 @@ static int do_one_pass(journal_t *journal,
|
|||||||
switch(blocktype) {
|
switch(blocktype) {
|
||||||
case JBD2_DESCRIPTOR_BLOCK:
|
case JBD2_DESCRIPTOR_BLOCK:
|
||||||
/* Verify checksum first */
|
/* Verify checksum first */
|
||||||
if (jbd2_journal_has_csum_v2or3(journal))
|
if (!jbd2_descriptor_block_csum_verify(journal,
|
||||||
descr_csum_size =
|
|
||||||
sizeof(struct jbd2_journal_block_tail);
|
|
||||||
if (descr_csum_size > 0 &&
|
|
||||||
!jbd2_descriptor_block_csum_verify(journal,
|
|
||||||
bh->b_data)) {
|
bh->b_data)) {
|
||||||
/*
|
/*
|
||||||
* PASS_SCAN can see stale blocks due to lazy
|
* PASS_SCAN can see stale blocks due to lazy
|
||||||
@ -603,7 +686,6 @@ static int do_one_pass(journal_t *journal,
|
|||||||
pr_err("JBD2: Invalid checksum recovering block %lu in log\n",
|
pr_err("JBD2: Invalid checksum recovering block %lu in log\n",
|
||||||
next_log_block);
|
next_log_block);
|
||||||
err = -EFSBADCRC;
|
err = -EFSBADCRC;
|
||||||
brelse(bh);
|
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
need_check_commit_time = true;
|
need_check_commit_time = true;
|
||||||
@ -619,125 +701,39 @@ static int do_one_pass(journal_t *journal,
|
|||||||
if (pass != PASS_REPLAY) {
|
if (pass != PASS_REPLAY) {
|
||||||
if (pass == PASS_SCAN &&
|
if (pass == PASS_SCAN &&
|
||||||
jbd2_has_feature_checksum(journal) &&
|
jbd2_has_feature_checksum(journal) &&
|
||||||
!need_check_commit_time &&
|
|
||||||
!info->end_transaction) {
|
!info->end_transaction) {
|
||||||
if (calc_chksums(journal, bh,
|
if (calc_chksums(journal, bh,
|
||||||
&next_log_block,
|
&next_log_block,
|
||||||
&crc32_sum)) {
|
&crc32_sum))
|
||||||
put_bh(bh);
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
put_bh(bh);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
next_log_block += count_tags(journal, bh);
|
next_log_block += count_tags(journal, bh);
|
||||||
wrap(journal, next_log_block);
|
wrap(journal, next_log_block);
|
||||||
put_bh(bh);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* A descriptor block: we can now write all of
|
/*
|
||||||
* the data blocks. Yay, useful work is finally
|
* A descriptor block: we can now write all of the
|
||||||
* getting done here! */
|
* data blocks. Yay, useful work is finally getting
|
||||||
|
* done here!
|
||||||
tagp = &bh->b_data[sizeof(journal_header_t)];
|
*/
|
||||||
while ((tagp - bh->b_data + tag_bytes)
|
err = jbd2_do_replay(journal, info, bh, &next_log_block,
|
||||||
<= journal->j_blocksize - descr_csum_size) {
|
next_commit_ID);
|
||||||
unsigned long io_block;
|
if (err) {
|
||||||
|
if (err == -ENOMEM)
|
||||||
memcpy(&tag, tagp, sizeof(tag));
|
goto failed;
|
||||||
flags = be16_to_cpu(tag.t_flags);
|
success = err;
|
||||||
|
|
||||||
io_block = next_log_block++;
|
|
||||||
wrap(journal, next_log_block);
|
|
||||||
err = jread(&obh, journal, io_block);
|
|
||||||
if (err) {
|
|
||||||
/* Recover what we can, but
|
|
||||||
* report failure at the end. */
|
|
||||||
success = err;
|
|
||||||
printk(KERN_ERR
|
|
||||||
"JBD2: IO error %d recovering "
|
|
||||||
"block %lu in log\n",
|
|
||||||
err, io_block);
|
|
||||||
} else {
|
|
||||||
unsigned long long blocknr;
|
|
||||||
|
|
||||||
J_ASSERT(obh != NULL);
|
|
||||||
blocknr = read_tag_block(journal,
|
|
||||||
&tag);
|
|
||||||
|
|
||||||
/* If the block has been
|
|
||||||
* revoked, then we're all done
|
|
||||||
* here. */
|
|
||||||
if (jbd2_journal_test_revoke
|
|
||||||
(journal, blocknr,
|
|
||||||
next_commit_ID)) {
|
|
||||||
brelse(obh);
|
|
||||||
++info->nr_revoke_hits;
|
|
||||||
goto skip_write;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Look for block corruption */
|
|
||||||
if (!jbd2_block_tag_csum_verify(
|
|
||||||
journal, &tag, (journal_block_tag3_t *)tagp,
|
|
||||||
obh->b_data, be32_to_cpu(tmp->h_sequence))) {
|
|
||||||
brelse(obh);
|
|
||||||
success = -EFSBADCRC;
|
|
||||||
printk(KERN_ERR "JBD2: Invalid "
|
|
||||||
"checksum recovering "
|
|
||||||
"data block %llu in "
|
|
||||||
"journal block %lu\n",
|
|
||||||
blocknr, io_block);
|
|
||||||
block_error = 1;
|
|
||||||
goto skip_write;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Find a buffer for the new
|
|
||||||
* data being restored */
|
|
||||||
nbh = __getblk(journal->j_fs_dev,
|
|
||||||
blocknr,
|
|
||||||
journal->j_blocksize);
|
|
||||||
if (nbh == NULL) {
|
|
||||||
printk(KERN_ERR
|
|
||||||
"JBD2: Out of memory "
|
|
||||||
"during recovery.\n");
|
|
||||||
err = -ENOMEM;
|
|
||||||
brelse(bh);
|
|
||||||
brelse(obh);
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock_buffer(nbh);
|
|
||||||
memcpy(nbh->b_data, obh->b_data,
|
|
||||||
journal->j_blocksize);
|
|
||||||
if (flags & JBD2_FLAG_ESCAPE) {
|
|
||||||
*((__be32 *)nbh->b_data) =
|
|
||||||
cpu_to_be32(JBD2_MAGIC_NUMBER);
|
|
||||||
}
|
|
||||||
|
|
||||||
BUFFER_TRACE(nbh, "marking dirty");
|
|
||||||
set_buffer_uptodate(nbh);
|
|
||||||
mark_buffer_dirty(nbh);
|
|
||||||
BUFFER_TRACE(nbh, "marking uptodate");
|
|
||||||
++info->nr_replays;
|
|
||||||
unlock_buffer(nbh);
|
|
||||||
brelse(obh);
|
|
||||||
brelse(nbh);
|
|
||||||
}
|
|
||||||
|
|
||||||
skip_write:
|
|
||||||
tagp += tag_bytes;
|
|
||||||
if (!(flags & JBD2_FLAG_SAME_UUID))
|
|
||||||
tagp += 16;
|
|
||||||
|
|
||||||
if (flags & JBD2_FLAG_LAST_TAG)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
brelse(bh);
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case JBD2_COMMIT_BLOCK:
|
case JBD2_COMMIT_BLOCK:
|
||||||
|
if (pass != PASS_SCAN) {
|
||||||
|
next_commit_ID++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* How to differentiate between interrupted commit
|
/* How to differentiate between interrupted commit
|
||||||
* and journal corruption ?
|
* and journal corruption ?
|
||||||
*
|
*
|
||||||
@ -782,7 +778,6 @@ static int do_one_pass(journal_t *journal,
|
|||||||
pr_err("JBD2: Invalid checksum found in transaction %u\n",
|
pr_err("JBD2: Invalid checksum found in transaction %u\n",
|
||||||
next_commit_ID);
|
next_commit_ID);
|
||||||
err = -EFSBADCRC;
|
err = -EFSBADCRC;
|
||||||
brelse(bh);
|
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
ignore_crc_mismatch:
|
ignore_crc_mismatch:
|
||||||
@ -792,7 +787,6 @@ static int do_one_pass(journal_t *journal,
|
|||||||
*/
|
*/
|
||||||
jbd2_debug(1, "JBD2: Invalid checksum ignored in transaction %u, likely stale data\n",
|
jbd2_debug(1, "JBD2: Invalid checksum ignored in transaction %u, likely stale data\n",
|
||||||
next_commit_ID);
|
next_commit_ID);
|
||||||
brelse(bh);
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -802,8 +796,7 @@ static int do_one_pass(journal_t *journal,
|
|||||||
* much to do other than move on to the next sequence
|
* much to do other than move on to the next sequence
|
||||||
* number.
|
* number.
|
||||||
*/
|
*/
|
||||||
if (pass == PASS_SCAN &&
|
if (jbd2_has_feature_checksum(journal)) {
|
||||||
jbd2_has_feature_checksum(journal)) {
|
|
||||||
struct commit_header *cbh =
|
struct commit_header *cbh =
|
||||||
(struct commit_header *)bh->b_data;
|
(struct commit_header *)bh->b_data;
|
||||||
unsigned found_chksum =
|
unsigned found_chksum =
|
||||||
@ -812,7 +805,6 @@ static int do_one_pass(journal_t *journal,
|
|||||||
if (info->end_transaction) {
|
if (info->end_transaction) {
|
||||||
journal->j_failed_commit =
|
journal->j_failed_commit =
|
||||||
info->end_transaction;
|
info->end_transaction;
|
||||||
brelse(bh);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -828,36 +820,33 @@ static int do_one_pass(journal_t *journal,
|
|||||||
goto chksum_error;
|
goto chksum_error;
|
||||||
|
|
||||||
crc32_sum = ~0;
|
crc32_sum = ~0;
|
||||||
|
goto chksum_ok;
|
||||||
}
|
}
|
||||||
if (pass == PASS_SCAN &&
|
|
||||||
!jbd2_commit_block_csum_verify(journal,
|
|
||||||
bh->b_data)) {
|
|
||||||
if (jbd2_commit_block_csum_verify_partial(
|
|
||||||
journal,
|
|
||||||
bh->b_data)) {
|
|
||||||
pr_notice("JBD2: Find incomplete commit block in transaction %u block %lu\n",
|
|
||||||
next_commit_ID, next_log_block);
|
|
||||||
goto chksum_ok;
|
|
||||||
}
|
|
||||||
chksum_error:
|
|
||||||
if (commit_time < last_trans_commit_time)
|
|
||||||
goto ignore_crc_mismatch;
|
|
||||||
info->end_transaction = next_commit_ID;
|
|
||||||
info->head_block = head_block;
|
|
||||||
|
|
||||||
if (!jbd2_has_feature_async_commit(journal)) {
|
if (jbd2_commit_block_csum_verify(journal, bh->b_data))
|
||||||
journal->j_failed_commit =
|
goto chksum_ok;
|
||||||
next_commit_ID;
|
|
||||||
brelse(bh);
|
if (jbd2_commit_block_csum_verify_partial(journal,
|
||||||
break;
|
bh->b_data)) {
|
||||||
}
|
pr_notice("JBD2: Find incomplete commit block in transaction %u block %lu\n",
|
||||||
|
next_commit_ID, next_log_block);
|
||||||
|
goto chksum_ok;
|
||||||
}
|
}
|
||||||
if (pass == PASS_SCAN) {
|
|
||||||
chksum_ok:
|
chksum_error:
|
||||||
last_trans_commit_time = commit_time;
|
if (commit_time < last_trans_commit_time)
|
||||||
head_block = next_log_block;
|
goto ignore_crc_mismatch;
|
||||||
|
info->end_transaction = next_commit_ID;
|
||||||
|
info->head_block = head_block;
|
||||||
|
|
||||||
|
if (!jbd2_has_feature_async_commit(journal)) {
|
||||||
|
journal->j_failed_commit = next_commit_ID;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
brelse(bh);
|
|
||||||
|
chksum_ok:
|
||||||
|
last_trans_commit_time = commit_time;
|
||||||
|
head_block = next_log_block;
|
||||||
next_commit_ID++;
|
next_commit_ID++;
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -876,14 +865,11 @@ static int do_one_pass(journal_t *journal,
|
|||||||
|
|
||||||
/* If we aren't in the REVOKE pass, then we can
|
/* If we aren't in the REVOKE pass, then we can
|
||||||
* just skip over this block. */
|
* just skip over this block. */
|
||||||
if (pass != PASS_REVOKE) {
|
if (pass != PASS_REVOKE)
|
||||||
brelse(bh);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
err = scan_revoke_records(journal, bh,
|
err = scan_revoke_records(journal, bh,
|
||||||
next_commit_ID, info);
|
next_commit_ID, info);
|
||||||
brelse(bh);
|
|
||||||
if (err)
|
if (err)
|
||||||
goto failed;
|
goto failed;
|
||||||
continue;
|
continue;
|
||||||
@ -891,12 +877,12 @@ static int do_one_pass(journal_t *journal,
|
|||||||
default:
|
default:
|
||||||
jbd2_debug(3, "Unrecognised magic %d, end of scan.\n",
|
jbd2_debug(3, "Unrecognised magic %d, end of scan.\n",
|
||||||
blocktype);
|
blocktype);
|
||||||
brelse(bh);
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
|
brelse(bh);
|
||||||
/*
|
/*
|
||||||
* We broke out of the log scan loop: either we came to the
|
* We broke out of the log scan loop: either we came to the
|
||||||
* known end of the log or we found an unexpected block in the
|
* known end of the log or we found an unexpected block in the
|
||||||
@ -927,11 +913,10 @@ static int do_one_pass(journal_t *journal,
|
|||||||
success = err;
|
success = err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (block_error && success == 0)
|
|
||||||
success = -EIO;
|
|
||||||
return success;
|
return success;
|
||||||
|
|
||||||
failed:
|
failed:
|
||||||
|
brelse(bh);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1796,22 +1796,21 @@ static inline unsigned long jbd2_log_space_left(journal_t *journal)
|
|||||||
static inline u32 jbd2_chksum(journal_t *journal, u32 crc,
|
static inline u32 jbd2_chksum(journal_t *journal, u32 crc,
|
||||||
const void *address, unsigned int length)
|
const void *address, unsigned int length)
|
||||||
{
|
{
|
||||||
struct {
|
DEFINE_RAW_FLEX(struct shash_desc, desc, __ctx,
|
||||||
struct shash_desc shash;
|
DIV_ROUND_UP(JBD_MAX_CHECKSUM_SIZE,
|
||||||
char ctx[JBD_MAX_CHECKSUM_SIZE];
|
sizeof(*((struct shash_desc *)0)->__ctx)));
|
||||||
} desc;
|
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
BUG_ON(crypto_shash_descsize(journal->j_chksum_driver) >
|
BUG_ON(crypto_shash_descsize(journal->j_chksum_driver) >
|
||||||
JBD_MAX_CHECKSUM_SIZE);
|
JBD_MAX_CHECKSUM_SIZE);
|
||||||
|
|
||||||
desc.shash.tfm = journal->j_chksum_driver;
|
desc->tfm = journal->j_chksum_driver;
|
||||||
*(u32 *)desc.ctx = crc;
|
*(u32 *)desc->__ctx = crc;
|
||||||
|
|
||||||
err = crypto_shash_update(&desc.shash, address, length);
|
err = crypto_shash_update(desc, address, length);
|
||||||
BUG_ON(err);
|
BUG_ON(err);
|
||||||
|
|
||||||
return *(u32 *)desc.ctx;
|
return *(u32 *)desc->__ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return most recent uncommitted transaction */
|
/* Return most recent uncommitted transaction */
|
||||||
|
@ -796,6 +796,21 @@ void pagecache_isize_extended(struct inode *inode, loff_t from, loff_t to)
|
|||||||
*/
|
*/
|
||||||
if (folio_mkclean(folio))
|
if (folio_mkclean(folio))
|
||||||
folio_mark_dirty(folio);
|
folio_mark_dirty(folio);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The post-eof range of the folio must be zeroed before it is exposed
|
||||||
|
* to the file. Writeback normally does this, but since i_size has been
|
||||||
|
* increased we handle it here.
|
||||||
|
*/
|
||||||
|
if (folio_test_dirty(folio)) {
|
||||||
|
unsigned int offset, end;
|
||||||
|
|
||||||
|
offset = from - folio_pos(folio);
|
||||||
|
end = min_t(unsigned int, to - folio_pos(folio),
|
||||||
|
folio_size(folio));
|
||||||
|
folio_zero_segment(folio, offset, end);
|
||||||
|
}
|
||||||
|
|
||||||
folio_unlock(folio);
|
folio_unlock(folio);
|
||||||
folio_put(folio);
|
folio_put(folio);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user