forked from Minki/linux
ext4: avoid ext4_error()'s caused by ENOMEM in the truncate path
We can't fail in the truncate path without requiring an fsck. Add work around for this by using a combination of retry loops and the __GFP_NOFAIL flag. From: Theodore Ts'o <tytso@mit.edu> Signed-off-by: Theodore Ts'o <tytso@mit.edu> Signed-off-by: Anna Pendleton <pendleton@google.com> Reviewed-by: Harshad Shirwadkar <harshadshirwadkar@gmail.com> Link: https://lore.kernel.org/r/20200507175028.15061-1-pendleton@google.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
This commit is contained in:
parent
08adf452e6
commit
73c384c0cd
@ -630,6 +630,7 @@ enum {
|
||||
*/
|
||||
#define EXT4_EX_NOCACHE 0x40000000
|
||||
#define EXT4_EX_FORCE_CACHE 0x20000000
|
||||
#define EXT4_EX_NOFAIL 0x10000000
|
||||
|
||||
/*
|
||||
* Flags used by ext4_free_blocks
|
||||
|
@ -297,11 +297,14 @@ ext4_force_split_extent_at(handle_t *handle, struct inode *inode,
|
||||
{
|
||||
struct ext4_ext_path *path = *ppath;
|
||||
int unwritten = ext4_ext_is_unwritten(path[path->p_depth].p_ext);
|
||||
int flags = EXT4_EX_NOCACHE | EXT4_GET_BLOCKS_PRE_IO;
|
||||
|
||||
if (nofail)
|
||||
flags |= EXT4_GET_BLOCKS_METADATA_NOFAIL | EXT4_EX_NOFAIL;
|
||||
|
||||
return ext4_split_extent_at(handle, inode, ppath, lblk, unwritten ?
|
||||
EXT4_EXT_MARK_UNWRIT1|EXT4_EXT_MARK_UNWRIT2 : 0,
|
||||
EXT4_EX_NOCACHE | EXT4_GET_BLOCKS_PRE_IO |
|
||||
(nofail ? EXT4_GET_BLOCKS_METADATA_NOFAIL:0));
|
||||
flags);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -487,8 +490,12 @@ __read_extent_tree_block(const char *function, unsigned int line,
|
||||
{
|
||||
struct buffer_head *bh;
|
||||
int err;
|
||||
gfp_t gfp_flags = __GFP_MOVABLE | GFP_NOFS;
|
||||
|
||||
bh = sb_getblk_gfp(inode->i_sb, pblk, __GFP_MOVABLE | GFP_NOFS);
|
||||
if (flags & EXT4_EX_NOFAIL)
|
||||
gfp_flags |= __GFP_NOFAIL;
|
||||
|
||||
bh = sb_getblk_gfp(inode->i_sb, pblk, gfp_flags);
|
||||
if (unlikely(!bh))
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
@ -837,6 +844,10 @@ ext4_find_extent(struct inode *inode, ext4_lblk_t block,
|
||||
struct ext4_ext_path *path = orig_path ? *orig_path : NULL;
|
||||
short int depth, i, ppos = 0;
|
||||
int ret;
|
||||
gfp_t gfp_flags = GFP_NOFS;
|
||||
|
||||
if (flags & EXT4_EX_NOFAIL)
|
||||
gfp_flags |= __GFP_NOFAIL;
|
||||
|
||||
eh = ext_inode_hdr(inode);
|
||||
depth = ext_depth(inode);
|
||||
@ -857,7 +868,7 @@ ext4_find_extent(struct inode *inode, ext4_lblk_t block,
|
||||
if (!path) {
|
||||
/* account possible depth increase */
|
||||
path = kcalloc(depth + 2, sizeof(struct ext4_ext_path),
|
||||
GFP_NOFS);
|
||||
gfp_flags);
|
||||
if (unlikely(!path))
|
||||
return ERR_PTR(-ENOMEM);
|
||||
path[0].p_maxdepth = depth + 1;
|
||||
@ -1007,9 +1018,13 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode,
|
||||
ext4_fsblk_t newblock, oldblock;
|
||||
__le32 border;
|
||||
ext4_fsblk_t *ablocks = NULL; /* array of allocated blocks */
|
||||
gfp_t gfp_flags = GFP_NOFS;
|
||||
int err = 0;
|
||||
size_t ext_size = 0;
|
||||
|
||||
if (flags & EXT4_EX_NOFAIL)
|
||||
gfp_flags |= __GFP_NOFAIL;
|
||||
|
||||
/* make decision: where to split? */
|
||||
/* FIXME: now decision is simplest: at current extent */
|
||||
|
||||
@ -1043,7 +1058,7 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode,
|
||||
* We need this to handle errors and free blocks
|
||||
* upon them.
|
||||
*/
|
||||
ablocks = kcalloc(depth, sizeof(ext4_fsblk_t), GFP_NOFS);
|
||||
ablocks = kcalloc(depth, sizeof(ext4_fsblk_t), gfp_flags);
|
||||
if (!ablocks)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -2019,7 +2034,7 @@ prepend:
|
||||
if (next != EXT_MAX_BLOCKS) {
|
||||
ext_debug("next leaf block - %u\n", next);
|
||||
BUG_ON(npath != NULL);
|
||||
npath = ext4_find_extent(inode, next, NULL, 0);
|
||||
npath = ext4_find_extent(inode, next, NULL, gb_flags);
|
||||
if (IS_ERR(npath))
|
||||
return PTR_ERR(npath);
|
||||
BUG_ON(npath->p_depth != path->p_depth);
|
||||
@ -2792,7 +2807,8 @@ again:
|
||||
ext4_fsblk_t pblk;
|
||||
|
||||
/* find extent for or closest extent to this block */
|
||||
path = ext4_find_extent(inode, end, NULL, EXT4_EX_NOCACHE);
|
||||
path = ext4_find_extent(inode, end, NULL,
|
||||
EXT4_EX_NOCACHE | EXT4_EX_NOFAIL);
|
||||
if (IS_ERR(path)) {
|
||||
ext4_journal_stop(handle);
|
||||
return PTR_ERR(path);
|
||||
@ -2878,7 +2894,7 @@ again:
|
||||
le16_to_cpu(path[k].p_hdr->eh_entries)+1;
|
||||
} else {
|
||||
path = kcalloc(depth + 1, sizeof(struct ext4_ext_path),
|
||||
GFP_NOFS);
|
||||
GFP_NOFS | __GFP_NOFAIL);
|
||||
if (path == NULL) {
|
||||
ext4_journal_stop(handle);
|
||||
return -ENOMEM;
|
||||
@ -3303,7 +3319,7 @@ static int ext4_split_extent(handle_t *handle,
|
||||
* Update path is required because previous ext4_split_extent_at() may
|
||||
* result in split of original leaf or extent zeroout.
|
||||
*/
|
||||
path = ext4_find_extent(inode, map->m_lblk, ppath, 0);
|
||||
path = ext4_find_extent(inode, map->m_lblk, ppath, flags);
|
||||
if (IS_ERR(path))
|
||||
return PTR_ERR(path);
|
||||
depth = ext_depth(inode);
|
||||
@ -4365,7 +4381,14 @@ retry:
|
||||
}
|
||||
if (err)
|
||||
return err;
|
||||
return ext4_ext_remove_space(inode, last_block, EXT_MAX_BLOCKS - 1);
|
||||
retry_remove_space:
|
||||
err = ext4_ext_remove_space(inode, last_block, EXT_MAX_BLOCKS - 1);
|
||||
if (err == -ENOMEM) {
|
||||
cond_resched();
|
||||
congestion_wait(BLK_RW_ASYNC, HZ/50);
|
||||
goto retry_remove_space;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset,
|
||||
|
Loading…
Reference in New Issue
Block a user