mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2024-12-28 00:33:16 +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);
|
||||
ext4_read_bh_nowait(bh, REQ_META | REQ_PRIO |
|
||||
(ignore_locked ? REQ_RAHEAD : 0),
|
||||
ext4_end_bitmap_read);
|
||||
ext4_end_bitmap_read,
|
||||
ext4_simulate_fail(sb, EXT4_SIM_BBITMAP_EIO));
|
||||
return bh;
|
||||
verify:
|
||||
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)
|
||||
return -EFSCORRUPTED;
|
||||
wait_on_buffer(bh);
|
||||
ext4_simulate_fail_bh(sb, bh, EXT4_SIM_BBITMAP_EIO);
|
||||
if (!buffer_uptodate(bh)) {
|
||||
ext4_error_err(sb, EIO, "Cannot read block bitmap - "
|
||||
"block_group = %u, block_bitmap = %llu",
|
||||
|
@ -418,7 +418,7 @@ struct fname {
|
||||
__u32 inode;
|
||||
__u8 name_len;
|
||||
__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 fname *fname, *new_fn;
|
||||
struct dir_private_info *info;
|
||||
int len;
|
||||
|
||||
info = dir_file->private_data;
|
||||
p = &info->root.rb_node;
|
||||
|
||||
/* Create and allocate the fname structure */
|
||||
len = sizeof(struct fname) + ent_name->len + 1;
|
||||
new_fn = kzalloc(len, GFP_KERNEL);
|
||||
new_fn = kzalloc(struct_size(new_fn, name, ent_name->len + 1),
|
||||
GFP_KERNEL);
|
||||
if (!new_fn)
|
||||
return -ENOMEM;
|
||||
new_fn->hash = hash;
|
||||
|
@ -1869,14 +1869,6 @@ static inline bool ext4_simulate_fail(struct super_block *sb,
|
||||
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
|
||||
*
|
||||
@ -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,
|
||||
sector_t block);
|
||||
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,
|
||||
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 void ext4_sb_breadahead_unmovable(struct super_block *sb, sector_t block);
|
||||
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)) {
|
||||
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)
|
||||
goto errout;
|
||||
}
|
||||
@ -3138,7 +3138,7 @@ static void ext4_zeroout_es(struct inode *inode, struct ext4_extent *ex)
|
||||
return;
|
||||
|
||||
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 */
|
||||
@ -4158,7 +4158,7 @@ static ext4_lblk_t ext4_ext_determine_insert_hole(struct inode *inode,
|
||||
/* Put just found gap into cache to speed up subsequent requests */
|
||||
ext_debug(inode, " -> %u:%u\n", hole_start, len);
|
||||
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 */
|
||||
if (hole_start != lblk)
|
||||
@ -4482,7 +4482,7 @@ static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset,
|
||||
int depth = 0;
|
||||
struct ext4_map_blocks map;
|
||||
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));
|
||||
map.m_lblk = offset;
|
||||
@ -4541,6 +4541,11 @@ static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset,
|
||||
if (ext4_update_inode_size(inode, epos) & 0x1)
|
||||
inode_set_mtime_to_ts(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);
|
||||
ext4_update_inode_fsync_trans(handle, inode, 1);
|
||||
|
@ -848,7 +848,7 @@ static int __es_insert_extent(struct inode *inode, struct extent_status *newes,
|
||||
*/
|
||||
void ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk,
|
||||
ext4_lblk_t len, ext4_fsblk_t pblk,
|
||||
unsigned int status, int flags)
|
||||
unsigned int status, bool delalloc_reserve_used)
|
||||
{
|
||||
struct extent_status newes;
|
||||
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)
|
||||
return;
|
||||
|
||||
es_debug("add [%u/%u) %llu %x %x to extent status tree of inode %lu\n",
|
||||
lblk, len, pblk, status, flags, inode->i_ino);
|
||||
es_debug("add [%u/%u) %llu %x %d to extent status tree of inode %lu\n",
|
||||
lblk, len, pblk, status, delalloc_reserve_used, inode->i_ino);
|
||||
|
||||
if (!len)
|
||||
return;
|
||||
@ -945,7 +945,7 @@ void ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk,
|
||||
resv_used += pending;
|
||||
if (resv_used)
|
||||
ext4_da_update_reserve_space(inode, resv_used,
|
||||
flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE);
|
||||
delalloc_reserve_used);
|
||||
|
||||
if (err1 || err2 || err3 < 0)
|
||||
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,
|
||||
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,
|
||||
ext4_lblk_t len, ext4_fsblk_t pblk,
|
||||
unsigned int status);
|
||||
|
@ -291,9 +291,9 @@ void ext4_fc_del(struct inode *inode)
|
||||
return;
|
||||
|
||||
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)) {
|
||||
spin_unlock(&EXT4_SB(inode->i_sb)->s_fc_lock);
|
||||
spin_unlock(&sbi->s_fc_lock);
|
||||
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);
|
||||
is_ineligible = ext4_test_mount_flag(sb, EXT4_MF_FC_INELIGIBLE);
|
||||
if (has_transaction &&
|
||||
(!is_ineligible ||
|
||||
(is_ineligible && tid_gt(tid, sbi->s_fc_ineligible_tid))))
|
||||
if (has_transaction && (!is_ineligible || tid_gt(tid, sbi->s_fc_ineligible_tid)))
|
||||
sbi->s_fc_ineligible_tid = tid;
|
||||
ext4_set_mount_flag(sb, EXT4_MF_FC_INELIGIBLE);
|
||||
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) &&
|
||||
pos + size <= i_size_read(inode))
|
||||
return size;
|
||||
return ext4_handle_inode_extension(inode, pos, size, size);
|
||||
return 0;
|
||||
error = ext4_handle_inode_extension(inode, pos, size, size);
|
||||
return error < 0 ? error : 0;
|
||||
}
|
||||
|
||||
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);
|
||||
if (ret) {
|
||||
ext4_journal_stop(handle);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ext4_journal_stop(handle);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 */
|
||||
static int ext4_getfsmap_datadev_helper(struct super_block *sb,
|
||||
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,
|
||||
EXT4_B2C(sbi, info->gfi_low.fmr_physical),
|
||||
EXT4_B2C(sbi, info->gfi_high.fmr_physical),
|
||||
ext4_getfsmap_meta_helper,
|
||||
ext4_getfsmap_datadev_helper, info);
|
||||
if (error)
|
||||
goto err;
|
||||
@ -560,7 +611,8 @@ static int ext4_getfsmap_datadev(struct super_block *sb,
|
||||
|
||||
/* Report any gaps at the end of the bg */
|
||||
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)
|
||||
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
|
||||
*/
|
||||
trace_ext4_load_inode_bitmap(sb, block_group);
|
||||
ext4_read_bh(bh, REQ_META | REQ_PRIO, ext4_end_bitmap_read);
|
||||
ext4_simulate_fail_bh(sb, bh, EXT4_SIM_IBITMAP_EIO);
|
||||
ext4_read_bh(bh, REQ_META | REQ_PRIO,
|
||||
ext4_end_bitmap_read,
|
||||
ext4_simulate_fail(sb, EXT4_SIM_IBITMAP_EIO));
|
||||
if (!buffer_uptodate(bh)) {
|
||||
put_bh(bh);
|
||||
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 (ext4_read_bh(bh, 0, NULL) < 0) {
|
||||
if (ext4_read_bh(bh, 0, NULL, false) < 0) {
|
||||
put_bh(bh);
|
||||
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 ?
|
||||
EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN;
|
||||
ext4_es_insert_extent(inode, map->m_lblk, map->m_len,
|
||||
map->m_pblk, status, 0);
|
||||
map->m_pblk, status, false);
|
||||
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 ?
|
||||
EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN;
|
||||
ext4_es_insert_extent(inode, map->m_lblk, map->m_len,
|
||||
map->m_pblk, status, flags);
|
||||
ext4_es_insert_extent(inode, map->m_lblk, map->m_len, map->m_pblk,
|
||||
status, flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE);
|
||||
|
||||
return retval;
|
||||
}
|
||||
@ -856,7 +856,14 @@ struct buffer_head *ext4_getblk(handle_t *handle, struct inode *inode,
|
||||
if (nowait)
|
||||
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))
|
||||
return ERR_PTR(-ENOMEM);
|
||||
if (map.m_flags & EXT4_MAP_NEW) {
|
||||
@ -1307,8 +1314,10 @@ static int ext4_write_end(struct file *file,
|
||||
folio_unlock(folio);
|
||||
folio_put(folio);
|
||||
|
||||
if (old_size < pos && !verity)
|
||||
if (old_size < pos && !verity) {
|
||||
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
|
||||
* 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_put(folio);
|
||||
|
||||
if (old_size < pos && !verity)
|
||||
if (old_size < pos && !verity) {
|
||||
pagecache_isize_extended(inode, old_size, pos);
|
||||
ext4_zero_partial_blocks(handle, inode, old_size, pos - old_size);
|
||||
}
|
||||
|
||||
if (size_changed) {
|
||||
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;
|
||||
loff_t old_size = inode->i_size;
|
||||
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))) {
|
||||
folio_unlock(folio);
|
||||
@ -3029,19 +3041,22 @@ static int ext4_da_do_write_end(struct address_space *mapping,
|
||||
folio_unlock(folio);
|
||||
folio_put(folio);
|
||||
|
||||
if (old_size < pos)
|
||||
if (pos > old_size) {
|
||||
pagecache_isize_extended(inode, old_size, pos);
|
||||
|
||||
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);
|
||||
zero_len = pos - old_size;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -4514,10 +4529,10 @@ static int __ext4_get_inode_loc(struct super_block *sb, unsigned long ino,
|
||||
* Read the block from disk.
|
||||
*/
|
||||
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);
|
||||
wait_on_buffer(bh);
|
||||
ext4_simulate_fail_bh(sb, bh, EXT4_SIM_INODE_EIO);
|
||||
if (!buffer_uptodate(bh)) {
|
||||
if (ret_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) {
|
||||
/* 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);
|
||||
if (IS_ERR(handle)) {
|
||||
error = PTR_ERR(handle);
|
||||
@ -5453,12 +5476,17 @@ int ext4_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
orphan = 1;
|
||||
}
|
||||
/*
|
||||
* Update c/mtime on truncate up, ext4_truncate() will
|
||||
* update c/mtime in shrink case below
|
||||
* Update c/mtime and tail zero the EOF folio on
|
||||
* truncate up. ext4_truncate() handles the shrink case
|
||||
* below.
|
||||
*/
|
||||
if (!shrink)
|
||||
if (!shrink) {
|
||||
inode_set_mtime_to_ts(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)
|
||||
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,
|
||||
(int)ac->ac_criteria);
|
||||
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)
|
||||
mb_debug(sb, "pa_type %s\n", ac->ac_pa->pa_type == MB_GROUP_PA ?
|
||||
"group pa" : "inode pa");
|
||||
@ -6056,7 +6056,7 @@ static bool ext4_mb_discard_preallocations_should_retry(struct super_block *sb,
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -6999,13 +6999,14 @@ int
|
||||
ext4_mballoc_query_range(
|
||||
struct super_block *sb,
|
||||
ext4_group_t group,
|
||||
ext4_grpblk_t start,
|
||||
ext4_grpblk_t first,
|
||||
ext4_grpblk_t end,
|
||||
ext4_mballoc_query_range_fn meta_formatter,
|
||||
ext4_mballoc_query_range_fn formatter,
|
||||
void *priv)
|
||||
{
|
||||
void *bitmap;
|
||||
ext4_grpblk_t next;
|
||||
ext4_grpblk_t start, next;
|
||||
struct ext4_buddy e4b;
|
||||
int error;
|
||||
|
||||
@ -7016,10 +7017,19 @@ ext4_mballoc_query_range(
|
||||
|
||||
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))
|
||||
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) {
|
||||
start = mb_find_next_zero_bit(bitmap, end + 1, start);
|
||||
if (start > end)
|
||||
|
@ -259,6 +259,7 @@ ext4_mballoc_query_range(
|
||||
ext4_group_t agno,
|
||||
ext4_grpblk_t start,
|
||||
ext4_grpblk_t end,
|
||||
ext4_mballoc_query_range_fn meta_formatter,
|
||||
ext4_mballoc_query_range_fn formatter,
|
||||
void *priv);
|
||||
|
||||
|
@ -94,7 +94,7 @@ static int read_mmp_block(struct super_block *sb, struct buffer_head **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)
|
||||
goto warn_exit;
|
||||
|
||||
|
@ -213,7 +213,7 @@ static int mext_page_mkuptodate(struct folio *folio, size_t from, size_t to)
|
||||
unlock_buffer(bh);
|
||||
continue;
|
||||
}
|
||||
ext4_read_bh_nowait(bh, 0, NULL);
|
||||
ext4_read_bh_nowait(bh, 0, NULL, false);
|
||||
nr++;
|
||||
} while (block++, (bh = bh->b_this_page) != head);
|
||||
|
||||
|
@ -1747,7 +1747,7 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
|
||||
#endif
|
||||
frame = dx_probe(fname, dir, NULL, frames);
|
||||
if (IS_ERR(frame))
|
||||
return (struct buffer_head *) frame;
|
||||
return ERR_CAST(frame);
|
||||
do {
|
||||
block = dx_get_block(frame->at);
|
||||
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)) {
|
||||
brelse(*bh);
|
||||
*bh = NULL;
|
||||
return (struct ext4_dir_entry_2 *) bh2;
|
||||
return ERR_CAST(bh2);
|
||||
}
|
||||
|
||||
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
|
||||
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;
|
||||
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",
|
||||
(unsigned long)dx_get_block(frame->at),
|
||||
hash2, split, count-split));
|
||||
@ -2043,10 +2052,11 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
|
||||
return de;
|
||||
|
||||
journal_error:
|
||||
ext4_std_error(dir->i_sb, err);
|
||||
out:
|
||||
brelse(*bh);
|
||||
brelse(bh2);
|
||||
*bh = NULL;
|
||||
ext4_std_error(dir->i_sb, err);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
|
@ -417,8 +417,10 @@ static void io_submit_add_bh(struct ext4_io_submit *io,
|
||||
submit_and_retry:
|
||||
ext4_io_submit(io);
|
||||
}
|
||||
if (io->io_bio == NULL)
|
||||
if (io->io_bio == NULL) {
|
||||
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)))
|
||||
goto submit_and_retry;
|
||||
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))
|
||||
return NULL;
|
||||
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);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -161,8 +161,14 @@ MODULE_ALIAS("ext3");
|
||||
|
||||
|
||||
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
|
||||
* 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,
|
||||
bh_end_io_t *end_io)
|
||||
bh_end_io_t *end_io, bool simu_fail)
|
||||
{
|
||||
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);
|
||||
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));
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
__ext4_read_bh(bh, op_flags, end_io);
|
||||
__ext4_read_bh(bh, op_flags, end_io, simu_fail);
|
||||
|
||||
wait_on_buffer(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);
|
||||
if (!wait) {
|
||||
ext4_read_bh_nowait(bh, op_flags, NULL);
|
||||
ext4_read_bh_nowait(bh, op_flags, NULL, false);
|
||||
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 (trylock_buffer(bh))
|
||||
ext4_read_bh_nowait(bh, REQ_RAHEAD, NULL);
|
||||
ext4_read_bh_nowait(bh, REQ_RAHEAD, NULL, false);
|
||||
brelse(bh);
|
||||
}
|
||||
}
|
||||
@ -346,9 +353,9 @@ __u32 ext4_free_group_clusters(struct super_block *sb,
|
||||
__u32 ext4_free_inodes_count(struct super_block *sb,
|
||||
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 ?
|
||||
(__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,
|
||||
@ -402,9 +409,9 @@ void ext4_free_group_clusters_set(struct super_block *sb,
|
||||
void ext4_free_inodes_set(struct super_block *sb,
|
||||
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)
|
||||
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,
|
||||
@ -2096,16 +2103,16 @@ static int ext4_parse_test_dummy_encryption(const struct fs_parameter *param,
|
||||
}
|
||||
|
||||
#define EXT4_SET_CTX(name) \
|
||||
static inline void ctx_set_##name(struct ext4_fs_context *ctx, \
|
||||
unsigned long flag) \
|
||||
static inline __maybe_unused \
|
||||
void ctx_set_##name(struct ext4_fs_context *ctx, unsigned long flag) \
|
||||
{ \
|
||||
ctx->mask_s_##name |= flag; \
|
||||
ctx->vals_s_##name |= flag; \
|
||||
}
|
||||
|
||||
#define EXT4_CLEAR_CTX(name) \
|
||||
static inline void ctx_clear_##name(struct ext4_fs_context *ctx, \
|
||||
unsigned long flag) \
|
||||
static inline __maybe_unused \
|
||||
void ctx_clear_##name(struct ext4_fs_context *ctx, unsigned long flag) \
|
||||
{ \
|
||||
ctx->mask_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");
|
||||
}
|
||||
|
||||
if (nodefs && !test_opt(sb, NO_PREFETCH_BLOCK_BITMAPS))
|
||||
SEQ_OPTS_PUTS("prefetch_block_bitmaps");
|
||||
|
||||
ext4_show_quota_options(seq, sb);
|
||||
return 0;
|
||||
}
|
||||
@ -3709,12 +3719,12 @@ static int ext4_run_li_request(struct ext4_li_request *elr)
|
||||
ret = 1;
|
||||
|
||||
if (!ret) {
|
||||
start_time = ktime_get_real_ns();
|
||||
start_time = ktime_get_ns();
|
||||
ret = ext4_init_inode_table(sb, group,
|
||||
elr->lr_timeout ? 0 : 1);
|
||||
trace_ext4_lazy_itable_init(sb, group);
|
||||
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);
|
||||
}
|
||||
elr->lr_next_sched = jiffies + elr->lr_timeout;
|
||||
@ -3774,8 +3784,9 @@ static int ext4_lazyinit_thread(void *arg)
|
||||
|
||||
cont_thread:
|
||||
while (true) {
|
||||
next_wakeup = MAX_JIFFY_OFFSET;
|
||||
bool next_wakeup_initialized = false;
|
||||
|
||||
next_wakeup = 0;
|
||||
mutex_lock(&eli->li_list_mtx);
|
||||
if (list_empty(&eli->li_request_list)) {
|
||||
mutex_unlock(&eli->li_list_mtx);
|
||||
@ -3788,8 +3799,11 @@ static int ext4_lazyinit_thread(void *arg)
|
||||
lr_request);
|
||||
|
||||
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_initialized = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (down_read_trylock(&elr->lr_super->s_umount)) {
|
||||
@ -3817,16 +3831,18 @@ static int ext4_lazyinit_thread(void *arg)
|
||||
elr->lr_next_sched = jiffies +
|
||||
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_initialized = true;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&eli->li_list_mtx);
|
||||
|
||||
try_to_freeze();
|
||||
|
||||
cur = jiffies;
|
||||
if ((time_after_eq(cur, next_wakeup)) ||
|
||||
(MAX_JIFFY_OFFSET == next_wakeup)) {
|
||||
if (!next_wakeup_initialized || time_after_eq(cur, next_wakeup)) {
|
||||
cond_resched();
|
||||
continue;
|
||||
}
|
||||
@ -6332,7 +6348,7 @@ static int ext4_sync_fs(struct super_block *sb, int wait)
|
||||
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
||||
|
||||
if (unlikely(ext4_forced_shutdown(sb)))
|
||||
return 0;
|
||||
return -EIO;
|
||||
|
||||
trace_ext4_sync_fs(sb, wait);
|
||||
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;
|
||||
}
|
||||
|
||||
if (test_opt2(sb, ABORT))
|
||||
ext4_abort(sb, ESHUTDOWN, "Abort forced by user");
|
||||
if ((old_opts.s_mount_opt & EXT4_MOUNT_DELALLOC) &&
|
||||
!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) |
|
||||
(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))
|
||||
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;
|
||||
|
||||
restore_opts:
|
||||
|
@ -662,10 +662,6 @@ void jbd2_journal_commit_transaction(journal_t *journal)
|
||||
JBUFFER_TRACE(jh, "ph3: write metadata");
|
||||
escape = jbd2_journal_write_metadata_buffer(commit_transaction,
|
||||
jh, &wbuf[bufs], blocknr);
|
||||
if (escape < 0) {
|
||||
jbd2_journal_abort(journal, escape);
|
||||
continue;
|
||||
}
|
||||
jbd2_file_log_bh(&io_bufs, wbuf[bufs]);
|
||||
|
||||
/* 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:
|
||||
* <0: Error
|
||||
* =0: Finished OK without escape
|
||||
* =1: Finished OK with escape
|
||||
*/
|
||||
@ -386,12 +385,7 @@ int jbd2_journal_write_metadata_buffer(transaction_t *transaction,
|
||||
goto escape_done;
|
||||
|
||||
spin_unlock(&jh_in->b_state_lock);
|
||||
tmp = jbd2_alloc(bh_in->b_size, GFP_NOFS);
|
||||
if (!tmp) {
|
||||
brelse(new_bh);
|
||||
free_buffer_head(new_bh);
|
||||
return -ENOMEM;
|
||||
}
|
||||
tmp = jbd2_alloc(bh_in->b_size, GFP_NOFS | __GFP_NOFAIL);
|
||||
spin_lock(&jh_in->b_state_lock);
|
||||
if (jh_in->b_frozen_data) {
|
||||
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
|
||||
* journal blocks from disk. */
|
||||
|
||||
/* First: create and setup a journal_t object in memory. We initialise
|
||||
* very few fields yet: that has to wait until we have created the
|
||||
* journal structures from from scratch, or loaded them from disk. */
|
||||
/* The journal_init_common() function creates and fills a journal_t object
|
||||
* in memory. It calls journal_load_superblock() to load the on-disk journal
|
||||
* superblock and initialize the journal_t object.
|
||||
*/
|
||||
|
||||
static journal_t *journal_init_common(struct block_device *bdev,
|
||||
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);
|
||||
}
|
||||
|
||||
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,
|
||||
struct recovery_info *info, enum passtype pass)
|
||||
{
|
||||
@ -493,13 +591,10 @@ static int do_one_pass(journal_t *journal,
|
||||
int err, success = 0;
|
||||
journal_superblock_t * sb;
|
||||
journal_header_t * tmp;
|
||||
struct buffer_head * bh;
|
||||
struct buffer_head *bh = NULL;
|
||||
unsigned int sequence;
|
||||
int blocktype;
|
||||
int tag_bytes = journal_tag_bytes(journal);
|
||||
__u32 crc32_sum = ~0; /* Transactional Checksums */
|
||||
int descr_csum_size = 0;
|
||||
int block_error = 0;
|
||||
bool need_check_commit_time = false;
|
||||
__u64 last_trans_commit_time = 0, commit_time;
|
||||
|
||||
@ -528,12 +623,6 @@ static int do_one_pass(journal_t *journal,
|
||||
*/
|
||||
|
||||
while (1) {
|
||||
int flags;
|
||||
char * tagp;
|
||||
journal_block_tag_t tag;
|
||||
struct buffer_head * obh;
|
||||
struct buffer_head * nbh;
|
||||
|
||||
cond_resched();
|
||||
|
||||
/* If we already know where to stop the log traversal,
|
||||
@ -552,6 +641,8 @@ static int do_one_pass(journal_t *journal,
|
||||
* record. */
|
||||
|
||||
jbd2_debug(3, "JBD2: checking block %ld\n", next_log_block);
|
||||
brelse(bh);
|
||||
bh = NULL;
|
||||
err = jread(&bh, journal, next_log_block);
|
||||
if (err)
|
||||
goto failed;
|
||||
@ -567,20 +658,16 @@ static int do_one_pass(journal_t *journal,
|
||||
|
||||
tmp = (journal_header_t *)bh->b_data;
|
||||
|
||||
if (tmp->h_magic != cpu_to_be32(JBD2_MAGIC_NUMBER)) {
|
||||
brelse(bh);
|
||||
if (tmp->h_magic != cpu_to_be32(JBD2_MAGIC_NUMBER))
|
||||
break;
|
||||
}
|
||||
|
||||
blocktype = be32_to_cpu(tmp->h_blocktype);
|
||||
sequence = be32_to_cpu(tmp->h_sequence);
|
||||
jbd2_debug(3, "Found magic %d, sequence %d\n",
|
||||
blocktype, sequence);
|
||||
|
||||
if (sequence != next_commit_ID) {
|
||||
brelse(bh);
|
||||
if (sequence != next_commit_ID)
|
||||
break;
|
||||
}
|
||||
|
||||
/* OK, we have a valid descriptor block which matches
|
||||
* all of the sequence number checks. What are we going
|
||||
@ -589,11 +676,7 @@ static int do_one_pass(journal_t *journal,
|
||||
switch(blocktype) {
|
||||
case JBD2_DESCRIPTOR_BLOCK:
|
||||
/* Verify checksum first */
|
||||
if (jbd2_journal_has_csum_v2or3(journal))
|
||||
descr_csum_size =
|
||||
sizeof(struct jbd2_journal_block_tail);
|
||||
if (descr_csum_size > 0 &&
|
||||
!jbd2_descriptor_block_csum_verify(journal,
|
||||
if (!jbd2_descriptor_block_csum_verify(journal,
|
||||
bh->b_data)) {
|
||||
/*
|
||||
* 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",
|
||||
next_log_block);
|
||||
err = -EFSBADCRC;
|
||||
brelse(bh);
|
||||
goto failed;
|
||||
}
|
||||
need_check_commit_time = true;
|
||||
@ -619,125 +701,39 @@ static int do_one_pass(journal_t *journal,
|
||||
if (pass != PASS_REPLAY) {
|
||||
if (pass == PASS_SCAN &&
|
||||
jbd2_has_feature_checksum(journal) &&
|
||||
!need_check_commit_time &&
|
||||
!info->end_transaction) {
|
||||
if (calc_chksums(journal, bh,
|
||||
&next_log_block,
|
||||
&crc32_sum)) {
|
||||
put_bh(bh);
|
||||
&crc32_sum))
|
||||
break;
|
||||
}
|
||||
put_bh(bh);
|
||||
continue;
|
||||
}
|
||||
next_log_block += count_tags(journal, bh);
|
||||
wrap(journal, next_log_block);
|
||||
put_bh(bh);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* A descriptor block: we can now write all of
|
||||
* the 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)
|
||||
<= journal->j_blocksize - descr_csum_size) {
|
||||
unsigned long io_block;
|
||||
|
||||
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. */
|
||||
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;
|
||||
/*
|
||||
* A descriptor block: we can now write all of the
|
||||
* data blocks. Yay, useful work is finally getting
|
||||
* done here!
|
||||
*/
|
||||
err = jbd2_do_replay(journal, info, bh, &next_log_block,
|
||||
next_commit_ID);
|
||||
if (err) {
|
||||
if (err == -ENOMEM)
|
||||
goto failed;
|
||||
success = err;
|
||||
}
|
||||
|
||||
brelse(bh);
|
||||
continue;
|
||||
|
||||
case JBD2_COMMIT_BLOCK:
|
||||
if (pass != PASS_SCAN) {
|
||||
next_commit_ID++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* How to differentiate between interrupted commit
|
||||
* 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",
|
||||
next_commit_ID);
|
||||
err = -EFSBADCRC;
|
||||
brelse(bh);
|
||||
goto failed;
|
||||
}
|
||||
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",
|
||||
next_commit_ID);
|
||||
brelse(bh);
|
||||
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
|
||||
* number.
|
||||
*/
|
||||
if (pass == PASS_SCAN &&
|
||||
jbd2_has_feature_checksum(journal)) {
|
||||
if (jbd2_has_feature_checksum(journal)) {
|
||||
struct commit_header *cbh =
|
||||
(struct commit_header *)bh->b_data;
|
||||
unsigned found_chksum =
|
||||
@ -812,7 +805,6 @@ static int do_one_pass(journal_t *journal,
|
||||
if (info->end_transaction) {
|
||||
journal->j_failed_commit =
|
||||
info->end_transaction;
|
||||
brelse(bh);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -828,36 +820,33 @@ static int do_one_pass(journal_t *journal,
|
||||
goto chksum_error;
|
||||
|
||||
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)) {
|
||||
journal->j_failed_commit =
|
||||
next_commit_ID;
|
||||
brelse(bh);
|
||||
break;
|
||||
}
|
||||
if (jbd2_commit_block_csum_verify(journal, bh->b_data))
|
||||
goto chksum_ok;
|
||||
|
||||
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;
|
||||
}
|
||||
if (pass == PASS_SCAN) {
|
||||
chksum_ok:
|
||||
last_trans_commit_time = commit_time;
|
||||
head_block = next_log_block;
|
||||
|
||||
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)) {
|
||||
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++;
|
||||
continue;
|
||||
|
||||
@ -876,14 +865,11 @@ static int do_one_pass(journal_t *journal,
|
||||
|
||||
/* If we aren't in the REVOKE pass, then we can
|
||||
* just skip over this block. */
|
||||
if (pass != PASS_REVOKE) {
|
||||
brelse(bh);
|
||||
if (pass != PASS_REVOKE)
|
||||
continue;
|
||||
}
|
||||
|
||||
err = scan_revoke_records(journal, bh,
|
||||
next_commit_ID, info);
|
||||
brelse(bh);
|
||||
if (err)
|
||||
goto failed;
|
||||
continue;
|
||||
@ -891,12 +877,12 @@ static int do_one_pass(journal_t *journal,
|
||||
default:
|
||||
jbd2_debug(3, "Unrecognised magic %d, end of scan.\n",
|
||||
blocktype);
|
||||
brelse(bh);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
brelse(bh);
|
||||
/*
|
||||
* 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
|
||||
@ -927,11 +913,10 @@ static int do_one_pass(journal_t *journal,
|
||||
success = err;
|
||||
}
|
||||
|
||||
if (block_error && success == 0)
|
||||
success = -EIO;
|
||||
return success;
|
||||
|
||||
failed:
|
||||
brelse(bh);
|
||||
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,
|
||||
const void *address, unsigned int length)
|
||||
{
|
||||
struct {
|
||||
struct shash_desc shash;
|
||||
char ctx[JBD_MAX_CHECKSUM_SIZE];
|
||||
} desc;
|
||||
DEFINE_RAW_FLEX(struct shash_desc, desc, __ctx,
|
||||
DIV_ROUND_UP(JBD_MAX_CHECKSUM_SIZE,
|
||||
sizeof(*((struct shash_desc *)0)->__ctx)));
|
||||
int err;
|
||||
|
||||
BUG_ON(crypto_shash_descsize(journal->j_chksum_driver) >
|
||||
JBD_MAX_CHECKSUM_SIZE);
|
||||
|
||||
desc.shash.tfm = journal->j_chksum_driver;
|
||||
*(u32 *)desc.ctx = crc;
|
||||
desc->tfm = journal->j_chksum_driver;
|
||||
*(u32 *)desc->__ctx = crc;
|
||||
|
||||
err = crypto_shash_update(&desc.shash, address, length);
|
||||
err = crypto_shash_update(desc, address, length);
|
||||
BUG_ON(err);
|
||||
|
||||
return *(u32 *)desc.ctx;
|
||||
return *(u32 *)desc->__ctx;
|
||||
}
|
||||
|
||||
/* 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))
|
||||
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_put(folio);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user