Merge tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4
Pull ext4 updates from Ted Ts'o: "A lot of bug fixes and cleanups for ext4, including: - Fix performance problems found in dioread_nolock now that it is the default, caused by transaction leaks. - Clean up fiemap handling in ext4 - Clean up and refactor multiple block allocator (mballoc) code - Fix a problem with mballoc with a smaller file systems running out of blocks because they couldn't properly use blocks that had been reserved by inode preallocation. - Fixed a race in ext4_sync_parent() versus rename() - Simplify the error handling in the extent manipulation code - Make sure all metadata I/O errors are felected to ext4_ext_dirty()'s and ext4_make_inode_dirty()'s callers. - Avoid passing an error pointer to brelse in ext4_xattr_set() - Fix race which could result to freeing an inode on the dirty last in data=journal mode. - Fix refcount handling if ext4_iget() fails - Fix a crash in generic/019 caused by a corrupted extent node" * tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: (58 commits) ext4: avoid unnecessary transaction starts during writeback ext4: don't block for O_DIRECT if IOCB_NOWAIT is set ext4: remove the access_ok() check in ext4_ioctl_get_es_cache fs: remove the access_ok() check in ioctl_fiemap fs: handle FIEMAP_FLAG_SYNC in fiemap_prep fs: move fiemap range validation into the file systems instances iomap: fix the iomap_fiemap prototype fs: move the fiemap definitions out of fs.h fs: mark __generic_block_fiemap static ext4: remove the call to fiemap_check_flags in ext4_fiemap ext4: split _ext4_fiemap ext4: fix fiemap size checks for bitmap files ext4: fix EXT4_MAX_LOGICAL_BLOCK macro add comment for ext4_dir_entry_2 file_type member jbd2: avoid leaking transaction credits when unreserving handle ext4: drop ext4_journal_free_reserved() ext4: mballoc: use lock for checking free blocks while retrying ext4: mballoc: refactor ext4_mb_good_group() ext4: mballoc: introduce pcpu seqcnt for freeing PA to improve ENOSPC handling ext4: mballoc: refactor ext4_mb_discard_preallocations() ...
This commit is contained in:
108
fs/ext4/inode.c
108
fs/ext4/inode.c
@@ -220,6 +220,16 @@ void ext4_evict_inode(struct inode *inode)
|
||||
ext4_begin_ordered_truncate(inode, 0);
|
||||
truncate_inode_pages_final(&inode->i_data);
|
||||
|
||||
/*
|
||||
* For inodes with journalled data, transaction commit could have
|
||||
* dirtied the inode. Flush worker is ignoring it because of I_FREEING
|
||||
* flag but we still need to remove the inode from the writeback lists.
|
||||
*/
|
||||
if (!list_empty_careful(&inode->i_io_list)) {
|
||||
WARN_ON_ONCE(!ext4_should_journal_data(inode));
|
||||
inode_io_list_del(inode);
|
||||
}
|
||||
|
||||
/*
|
||||
* Protect us against freezing - iput() caller didn't have to have any
|
||||
* protection against it
|
||||
@@ -432,11 +442,9 @@ static void ext4_map_blocks_es_recheck(handle_t *handle,
|
||||
*/
|
||||
down_read(&EXT4_I(inode)->i_data_sem);
|
||||
if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) {
|
||||
retval = ext4_ext_map_blocks(handle, inode, map, flags &
|
||||
EXT4_GET_BLOCKS_KEEP_SIZE);
|
||||
retval = ext4_ext_map_blocks(handle, inode, map, 0);
|
||||
} else {
|
||||
retval = ext4_ind_map_blocks(handle, inode, map, flags &
|
||||
EXT4_GET_BLOCKS_KEEP_SIZE);
|
||||
retval = ext4_ind_map_blocks(handle, inode, map, 0);
|
||||
}
|
||||
up_read((&EXT4_I(inode)->i_data_sem));
|
||||
|
||||
@@ -493,9 +501,8 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
|
||||
#endif
|
||||
|
||||
map->m_flags = 0;
|
||||
ext_debug("ext4_map_blocks(): inode %lu, flag %d, max_blocks %u,"
|
||||
"logical block %lu\n", inode->i_ino, flags, map->m_len,
|
||||
(unsigned long) map->m_lblk);
|
||||
ext_debug(inode, "flag 0x%x, max_blocks %u, logical block %lu\n",
|
||||
flags, map->m_len, (unsigned long) map->m_lblk);
|
||||
|
||||
/*
|
||||
* ext4_map_blocks returns an int, and m_len is an unsigned int
|
||||
@@ -541,11 +548,9 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
|
||||
*/
|
||||
down_read(&EXT4_I(inode)->i_data_sem);
|
||||
if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) {
|
||||
retval = ext4_ext_map_blocks(handle, inode, map, flags &
|
||||
EXT4_GET_BLOCKS_KEEP_SIZE);
|
||||
retval = ext4_ext_map_blocks(handle, inode, map, 0);
|
||||
} else {
|
||||
retval = ext4_ind_map_blocks(handle, inode, map, flags &
|
||||
EXT4_GET_BLOCKS_KEEP_SIZE);
|
||||
retval = ext4_ind_map_blocks(handle, inode, map, 0);
|
||||
}
|
||||
if (retval > 0) {
|
||||
unsigned int status;
|
||||
@@ -726,6 +731,9 @@ out_sem:
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (retval < 0)
|
||||
ext_debug(inode, "failed with err %d\n", retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -1296,7 +1304,7 @@ static int ext4_write_end(struct file *file,
|
||||
* filesystems.
|
||||
*/
|
||||
if (i_size_changed || inline_data)
|
||||
ext4_mark_inode_dirty(handle, inode);
|
||||
ret = ext4_mark_inode_dirty(handle, inode);
|
||||
|
||||
if (pos + len > inode->i_size && !verity && ext4_can_truncate(inode))
|
||||
/* if we have allocated more blocks and copied
|
||||
@@ -1526,6 +1534,7 @@ struct mpage_da_data {
|
||||
struct ext4_map_blocks map;
|
||||
struct ext4_io_submit io_submit; /* IO submission data */
|
||||
unsigned int do_map:1;
|
||||
unsigned int scanned_until_end:1;
|
||||
};
|
||||
|
||||
static void mpage_release_unused_pages(struct mpage_da_data *mpd,
|
||||
@@ -1541,6 +1550,7 @@ static void mpage_release_unused_pages(struct mpage_da_data *mpd,
|
||||
if (mpd->first_page >= mpd->next_page)
|
||||
return;
|
||||
|
||||
mpd->scanned_until_end = 0;
|
||||
index = mpd->first_page;
|
||||
end = mpd->next_page - 1;
|
||||
if (invalidate) {
|
||||
@@ -1681,8 +1691,7 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock,
|
||||
invalid_block = ~0;
|
||||
|
||||
map->m_flags = 0;
|
||||
ext_debug("ext4_da_map_blocks(): inode %lu, max_blocks %u,"
|
||||
"logical block %lu\n", inode->i_ino, map->m_len,
|
||||
ext_debug(inode, "max_blocks %u, logical block %lu\n", map->m_len,
|
||||
(unsigned long) map->m_lblk);
|
||||
|
||||
/* Lookup extent status tree firstly */
|
||||
@@ -2078,7 +2087,7 @@ static int mpage_submit_page(struct mpage_da_data *mpd, struct page *page)
|
||||
return err;
|
||||
}
|
||||
|
||||
#define BH_FLAGS ((1 << BH_Unwritten) | (1 << BH_Delay))
|
||||
#define BH_FLAGS (BIT(BH_Unwritten) | BIT(BH_Delay))
|
||||
|
||||
/*
|
||||
* mballoc gives us at most this number of blocks...
|
||||
@@ -2188,7 +2197,11 @@ static int mpage_process_page_bufs(struct mpage_da_data *mpd,
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
return lblk < blocks;
|
||||
if (lblk >= blocks) {
|
||||
mpd->scanned_until_end = 1;
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2311,7 +2324,7 @@ static int mpage_map_and_submit_buffers(struct mpage_da_data *mpd)
|
||||
* mapping, or maybe the page was submitted for IO.
|
||||
* So we return to call further extent mapping.
|
||||
*/
|
||||
if (err < 0 || map_bh == true)
|
||||
if (err < 0 || map_bh)
|
||||
goto out;
|
||||
/* Page fully mapped - let IO run! */
|
||||
err = mpage_submit_page(mpd, page);
|
||||
@@ -2358,7 +2371,7 @@ static int mpage_map_one_extent(handle_t *handle, struct mpage_da_data *mpd)
|
||||
dioread_nolock = ext4_should_dioread_nolock(inode);
|
||||
if (dioread_nolock)
|
||||
get_blocks_flags |= EXT4_GET_BLOCKS_IO_CREATE_EXT;
|
||||
if (map->m_flags & (1 << BH_Delay))
|
||||
if (map->m_flags & BIT(BH_Delay))
|
||||
get_blocks_flags |= EXT4_GET_BLOCKS_DELALLOC_RESERVE;
|
||||
|
||||
err = ext4_map_blocks(handle, inode, map, get_blocks_flags);
|
||||
@@ -2546,7 +2559,7 @@ static int mpage_prepare_extent_to_map(struct mpage_da_data *mpd)
|
||||
nr_pages = pagevec_lookup_range_tag(&pvec, mapping, &index, end,
|
||||
tag);
|
||||
if (nr_pages == 0)
|
||||
goto out;
|
||||
break;
|
||||
|
||||
for (i = 0; i < nr_pages; i++) {
|
||||
struct page *page = pvec.pages[i];
|
||||
@@ -2601,6 +2614,7 @@ static int mpage_prepare_extent_to_map(struct mpage_da_data *mpd)
|
||||
pagevec_release(&pvec);
|
||||
cond_resched();
|
||||
}
|
||||
mpd->scanned_until_end = 1;
|
||||
return 0;
|
||||
out:
|
||||
pagevec_release(&pvec);
|
||||
@@ -2619,7 +2633,6 @@ static int ext4_writepages(struct address_space *mapping,
|
||||
struct inode *inode = mapping->host;
|
||||
int needed_blocks, rsv_blocks = 0, ret = 0;
|
||||
struct ext4_sb_info *sbi = EXT4_SB(mapping->host->i_sb);
|
||||
bool done;
|
||||
struct blk_plug plug;
|
||||
bool give_up_on_write = false;
|
||||
|
||||
@@ -2705,7 +2718,6 @@ static int ext4_writepages(struct address_space *mapping,
|
||||
retry:
|
||||
if (wbc->sync_mode == WB_SYNC_ALL || wbc->tagged_writepages)
|
||||
tag_pages_for_writeback(mapping, mpd.first_page, mpd.last_page);
|
||||
done = false;
|
||||
blk_start_plug(&plug);
|
||||
|
||||
/*
|
||||
@@ -2715,6 +2727,7 @@ retry:
|
||||
* started.
|
||||
*/
|
||||
mpd.do_map = 0;
|
||||
mpd.scanned_until_end = 0;
|
||||
mpd.io_submit.io_end = ext4_init_io_end(inode, GFP_KERNEL);
|
||||
if (!mpd.io_submit.io_end) {
|
||||
ret = -ENOMEM;
|
||||
@@ -2730,7 +2743,7 @@ retry:
|
||||
if (ret < 0)
|
||||
goto unplug;
|
||||
|
||||
while (!done && mpd.first_page <= mpd.last_page) {
|
||||
while (!mpd.scanned_until_end && wbc->nr_to_write > 0) {
|
||||
/* For each extent of pages we use new io_end */
|
||||
mpd.io_submit.io_end = ext4_init_io_end(inode, GFP_KERNEL);
|
||||
if (!mpd.io_submit.io_end) {
|
||||
@@ -2765,20 +2778,9 @@ retry:
|
||||
|
||||
trace_ext4_da_write_pages(inode, mpd.first_page, mpd.wbc);
|
||||
ret = mpage_prepare_extent_to_map(&mpd);
|
||||
if (!ret) {
|
||||
if (mpd.map.m_len)
|
||||
ret = mpage_map_and_submit_extent(handle, &mpd,
|
||||
if (!ret && mpd.map.m_len)
|
||||
ret = mpage_map_and_submit_extent(handle, &mpd,
|
||||
&give_up_on_write);
|
||||
else {
|
||||
/*
|
||||
* We scanned the whole range (or exhausted
|
||||
* nr_to_write), submitted what was mapped and
|
||||
* didn't find anything needing mapping. We are
|
||||
* done.
|
||||
*/
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Caution: If the handle is synchronous,
|
||||
* ext4_journal_stop() can wait for transaction commit
|
||||
@@ -3077,7 +3079,7 @@ static int ext4_da_write_end(struct file *file,
|
||||
* new_i_size is less that inode->i_size
|
||||
* bu greater than i_disksize.(hint delalloc)
|
||||
*/
|
||||
ext4_mark_inode_dirty(handle, inode);
|
||||
ret = ext4_mark_inode_dirty(handle, inode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3094,7 +3096,7 @@ static int ext4_da_write_end(struct file *file,
|
||||
if (ret2 < 0)
|
||||
ret = ret2;
|
||||
ret2 = ext4_journal_stop(handle);
|
||||
if (!ret)
|
||||
if (unlikely(ret2 && !ret))
|
||||
ret = ret2;
|
||||
|
||||
return ret ? ret : copied;
|
||||
@@ -3883,6 +3885,8 @@ int ext4_update_disksize_before_punch(struct inode *inode, loff_t offset,
|
||||
loff_t len)
|
||||
{
|
||||
handle_t *handle;
|
||||
int ret;
|
||||
|
||||
loff_t size = i_size_read(inode);
|
||||
|
||||
WARN_ON(!inode_is_locked(inode));
|
||||
@@ -3896,10 +3900,10 @@ int ext4_update_disksize_before_punch(struct inode *inode, loff_t offset,
|
||||
if (IS_ERR(handle))
|
||||
return PTR_ERR(handle);
|
||||
ext4_update_i_disksize(inode, size);
|
||||
ext4_mark_inode_dirty(handle, inode);
|
||||
ret = ext4_mark_inode_dirty(handle, inode);
|
||||
ext4_journal_stop(handle);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ext4_wait_dax_page(struct ext4_inode_info *ei)
|
||||
@@ -3951,7 +3955,7 @@ int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length)
|
||||
loff_t first_block_offset, last_block_offset;
|
||||
handle_t *handle;
|
||||
unsigned int credits;
|
||||
int ret = 0;
|
||||
int ret = 0, ret2 = 0;
|
||||
|
||||
trace_ext4_punch_hole(inode, offset, length, 0);
|
||||
|
||||
@@ -4074,7 +4078,9 @@ int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length)
|
||||
ext4_handle_sync(handle);
|
||||
|
||||
inode->i_mtime = inode->i_ctime = current_time(inode);
|
||||
ext4_mark_inode_dirty(handle, inode);
|
||||
ret2 = ext4_mark_inode_dirty(handle, inode);
|
||||
if (unlikely(ret2))
|
||||
ret = ret2;
|
||||
if (ret >= 0)
|
||||
ext4_update_inode_fsync_trans(handle, inode, 1);
|
||||
out_stop:
|
||||
@@ -4143,7 +4149,7 @@ int ext4_truncate(struct inode *inode)
|
||||
{
|
||||
struct ext4_inode_info *ei = EXT4_I(inode);
|
||||
unsigned int credits;
|
||||
int err = 0;
|
||||
int err = 0, err2;
|
||||
handle_t *handle;
|
||||
struct address_space *mapping = inode->i_mapping;
|
||||
|
||||
@@ -4231,7 +4237,9 @@ out_stop:
|
||||
ext4_orphan_del(handle, inode);
|
||||
|
||||
inode->i_mtime = inode->i_ctime = current_time(inode);
|
||||
ext4_mark_inode_dirty(handle, inode);
|
||||
err2 = ext4_mark_inode_dirty(handle, inode);
|
||||
if (unlikely(err2 && !err))
|
||||
err = err2;
|
||||
ext4_journal_stop(handle);
|
||||
|
||||
trace_ext4_truncate_exit(inode);
|
||||
@@ -5289,6 +5297,8 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
|
||||
inode->i_gid = attr->ia_gid;
|
||||
error = ext4_mark_inode_dirty(handle, inode);
|
||||
ext4_journal_stop(handle);
|
||||
if (unlikely(error))
|
||||
return error;
|
||||
}
|
||||
|
||||
if (attr->ia_valid & ATTR_SIZE) {
|
||||
@@ -5774,7 +5784,8 @@ out_unlock:
|
||||
* Whenever the user wants stuff synced (sys_sync, sys_msync, sys_fsync)
|
||||
* we start and wait on commits.
|
||||
*/
|
||||
int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode)
|
||||
int __ext4_mark_inode_dirty(handle_t *handle, struct inode *inode,
|
||||
const char *func, unsigned int line)
|
||||
{
|
||||
struct ext4_iloc iloc;
|
||||
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
|
||||
@@ -5784,13 +5795,18 @@ int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode)
|
||||
trace_ext4_mark_inode_dirty(inode, _RET_IP_);
|
||||
err = ext4_reserve_inode_write(handle, inode, &iloc);
|
||||
if (err)
|
||||
return err;
|
||||
goto out;
|
||||
|
||||
if (EXT4_I(inode)->i_extra_isize < sbi->s_want_extra_isize)
|
||||
ext4_try_to_expand_extra_isize(inode, sbi->s_want_extra_isize,
|
||||
iloc, handle);
|
||||
|
||||
return ext4_mark_iloc_dirty(handle, inode, &iloc);
|
||||
err = ext4_mark_iloc_dirty(handle, inode, &iloc);
|
||||
out:
|
||||
if (unlikely(err))
|
||||
ext4_error_inode_err(inode, func, line, 0, err,
|
||||
"mark_inode_dirty error");
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
|
Reference in New Issue
Block a user