From: Theodore Ts'o <tytso@mit.edu> 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> --- fs/ext4/ext4.h | 1 + fs/ext4/extents.c | 43 +++++++++++++++++++++++++++++++++---------- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 91eb4381cae5..af010f5009e4 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -632,6 +632,7 @@ enum { */ #define EXT4_EX_NOCACHE 0x40000000 #define EXT4_EX_FORCE_CACHE 0x20000000 +#define EXT4_EX_NOFAIL 0x10000000 /* * Flags used by ext4_free_blocks diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index f2b577b315a0..c2f665f8109e 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -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; + int gfp_flags = __GFP_MOVABLE | GFP_NOFS; + + if (flags & EXT4_EX_NOFAIL) + gfp_flags |= __GFP_NOFAIL; - bh = sb_getblk_gfp(inode->i_sb, pblk, __GFP_MOVABLE | GFP_NOFS); + bh = sb_getblk_gfp(inode->i_sb, pblk, gfp_flags); if (unlikely(!bh)) return ERR_PTR(-ENOMEM); @@ -838,6 +845,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; + int gfp_flags = GFP_NOFS; + + if (flags & EXT4_EX_NOFAIL) + gfp_flags |= __GFP_NOFAIL; eh = ext_inode_hdr(inode); depth = ext_depth(inode); @@ -858,7 +869,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; @@ -1008,9 +1019,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 */ + int 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 */ @@ -1044,7 +1059,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; @@ -2020,7 +2035,7 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode, 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); @@ -2793,7 +2808,8 @@ int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start, 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); @@ -2879,7 +2895,7 @@ int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start, 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; @@ -3300,7 +3316,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); @@ -4353,7 +4369,14 @@ int ext4_ext_truncate(handle_t *handle, struct inode *inode) } 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, -- 2.26.2.526.g744177e7f7-goog
Hi Anna, I love your patch! Perhaps something to improve: [auto build test WARNING on ext4/dev] [also build test WARNING on tytso-fscrypt/master v5.7-rc4 next-20200508] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982] url: https://github.com/0day-ci/linux/commits/Anna-Pendleton/ext4-avoid-ext4_error-s-caused-by-ENOMEM-in-the-truncate-path/20200508-062229 base: https://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4.git dev reproduce: # apt-get install sparse # sparse version: make ARCH=x86_64 allmodconfig make C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' :::::: branch date: 22 hours ago :::::: commit date: 22 hours ago If you fix the issue, kindly add following tag as appropriate Reported-by: kbuild test robot <lkp@intel.com> sparse warnings: (new ones prefixed by >>) fs/ext4/extents.c:493:67: sparse: warning: incorrect type in initializer (different base types) >> fs/ext4/extents.c:493:67: sparse: expected int gfp_flags >> fs/ext4/extents.c:493:67: sparse: got restricted gfp_t fs/ext4/extents.c:496:27: sparse: warning: invalid assignment: |= >> fs/ext4/extents.c:496:27: sparse: left side has type int >> fs/ext4/extents.c:496:27: sparse: right side has type restricted gfp_t fs/ext4/extents.c:498:47: sparse: warning: incorrect type in argument 3 (different base types) >> fs/ext4/extents.c:498:47: sparse: expected restricted gfp_t [usertype] gfp >> fs/ext4/extents.c:498:47: sparse: got int gfp_flags fs/ext4/extents.c:848:25: sparse: warning: incorrect type in initializer (different base types) fs/ext4/extents.c:848:25: sparse: expected int gfp_flags fs/ext4/extents.c:848:25: sparse: got restricted gfp_t fs/ext4/extents.c:851:27: sparse: warning: invalid assignment: |= fs/ext4/extents.c:851:27: sparse: left side has type int fs/ext4/extents.c:851:27: sparse: right side has type restricted gfp_t fs/ext4/extents.c:872:33: sparse: warning: incorrect type in argument 3 (different base types) >> fs/ext4/extents.c:872:33: sparse: expected restricted gfp_t [usertype] flags fs/ext4/extents.c:872:33: sparse: got int gfp_flags fs/ext4/extents.c:1022:25: sparse: warning: incorrect type in initializer (different base types) fs/ext4/extents.c:1022:25: sparse: expected int gfp_flags fs/ext4/extents.c:1022:25: sparse: got restricted gfp_t fs/ext4/extents.c:1027:27: sparse: warning: invalid assignment: |= fs/ext4/extents.c:1027:27: sparse: left side has type int fs/ext4/extents.c:1027:27: sparse: right side has type restricted gfp_t fs/ext4/extents.c:1062:56: sparse: warning: incorrect type in argument 3 (different base types) fs/ext4/extents.c:1062:56: sparse: expected restricted gfp_t [usertype] flags fs/ext4/extents.c:1062:56: sparse: got int gfp_flags # https://github.com/0day-ci/linux/commit/7d07f7d72800b68a55822a5705514d3c3bc82b63 git remote add linux-review https://github.com/0day-ci/linux git remote update linux-review git checkout 7d07f7d72800b68a55822a5705514d3c3bc82b63 vim +493 fs/ext4/extents.c 4068664e3cd231 Dmitry Monakhov 2019-11-06 485 7d7ea89e756ea1 Theodore Ts'o 2013-08-16 486 static struct buffer_head * 7d7ea89e756ea1 Theodore Ts'o 2013-08-16 487 __read_extent_tree_block(const char *function, unsigned int line, 107a7bd31ac003 Theodore Ts'o 2013-08-16 488 struct inode *inode, ext4_fsblk_t pblk, int depth, 107a7bd31ac003 Theodore Ts'o 2013-08-16 489 int flags) f84891289e62a7 Darrick J. Wong 2012-04-29 490 { 7d7ea89e756ea1 Theodore Ts'o 2013-08-16 491 struct buffer_head *bh; 7d7ea89e756ea1 Theodore Ts'o 2013-08-16 492 int err; 7d07f7d72800b6 Theodore Ts'o 2020-05-07 @493 int gfp_flags = __GFP_MOVABLE | GFP_NOFS; f84891289e62a7 Darrick J. Wong 2012-04-29 494 7d07f7d72800b6 Theodore Ts'o 2020-05-07 495 if (flags & EXT4_EX_NOFAIL) 7d07f7d72800b6 Theodore Ts'o 2020-05-07 @496 gfp_flags |= __GFP_NOFAIL; 7d07f7d72800b6 Theodore Ts'o 2020-05-07 497 7d07f7d72800b6 Theodore Ts'o 2020-05-07 @498 bh = sb_getblk_gfp(inode->i_sb, pblk, gfp_flags); 7d7ea89e756ea1 Theodore Ts'o 2013-08-16 499 if (unlikely(!bh)) 7d7ea89e756ea1 Theodore Ts'o 2013-08-16 500 return ERR_PTR(-ENOMEM); 7d7ea89e756ea1 Theodore Ts'o 2013-08-16 501 7d7ea89e756ea1 Theodore Ts'o 2013-08-16 502 if (!bh_uptodate_or_lock(bh)) { 7d7ea89e756ea1 Theodore Ts'o 2013-08-16 503 trace_ext4_ext_load_extent(inode, pblk, _RET_IP_); 7d7ea89e756ea1 Theodore Ts'o 2013-08-16 504 err = bh_submit_read(bh); 7d7ea89e756ea1 Theodore Ts'o 2013-08-16 505 if (err < 0) 7d7ea89e756ea1 Theodore Ts'o 2013-08-16 506 goto errout; 7d7ea89e756ea1 Theodore Ts'o 2013-08-16 507 } 7869a4a6c5caa7 Theodore Ts'o 2013-08-16 508 if (buffer_verified(bh) && !(flags & EXT4_EX_FORCE_CACHE)) 7d7ea89e756ea1 Theodore Ts'o 2013-08-16 509 return bh; 0a944e8a6c66ca Theodore Ts'o 2019-05-22 510 if (!ext4_has_feature_journal(inode->i_sb) || 0a944e8a6c66ca Theodore Ts'o 2019-05-22 511 (inode->i_ino != 0a944e8a6c66ca Theodore Ts'o 2019-05-22 512 le32_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_journal_inum))) { 7d7ea89e756ea1 Theodore Ts'o 2013-08-16 513 err = __ext4_ext_check(function, line, inode, c349179b4808f7 Theodore Ts'o 2013-08-16 514 ext_block_hdr(bh), depth, pblk); 7d7ea89e756ea1 Theodore Ts'o 2013-08-16 515 if (err) 7d7ea89e756ea1 Theodore Ts'o 2013-08-16 516 goto errout; 0a944e8a6c66ca Theodore Ts'o 2019-05-22 517 } f84891289e62a7 Darrick J. Wong 2012-04-29 518 set_buffer_verified(bh); 107a7bd31ac003 Theodore Ts'o 2013-08-16 519 /* 107a7bd31ac003 Theodore Ts'o 2013-08-16 520 * If this is a leaf block, cache all of its entries 107a7bd31ac003 Theodore Ts'o 2013-08-16 521 */ 107a7bd31ac003 Theodore Ts'o 2013-08-16 522 if (!(flags & EXT4_EX_NOCACHE) && depth == 0) { 107a7bd31ac003 Theodore Ts'o 2013-08-16 523 struct ext4_extent_header *eh = ext_block_hdr(bh); 4068664e3cd231 Dmitry Monakhov 2019-11-06 524 ext4_cache_extents(inode, eh); 107a7bd31ac003 Theodore Ts'o 2013-08-16 525 } 7d7ea89e756ea1 Theodore Ts'o 2013-08-16 526 return bh; 7d7ea89e756ea1 Theodore Ts'o 2013-08-16 527 errout: 7d7ea89e756ea1 Theodore Ts'o 2013-08-16 528 put_bh(bh); 7d7ea89e756ea1 Theodore Ts'o 2013-08-16 529 return ERR_PTR(err); 7d7ea89e756ea1 Theodore Ts'o 2013-08-16 530 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
On Thu, May 07, 2020 at 10:50:28AM -0700, Anna Pendleton wrote:
> From: Theodore Ts'o <tytso@mit.edu>
>
> 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>
Thanks, applied.
Per the feedback from the kbuild test robot, I changed "int gfp_flags =..." to
"gfp_t gfp_flags=..." in three places in the patches.
Cheers,
- Ted