diff mbox series

ext4: avoid ext4_error()'s caused by ENOMEM in the truncate path

Message ID 20200507175028.15061-1-pendleton@google.com
State Accepted
Headers show
Series ext4: avoid ext4_error()'s caused by ENOMEM in the truncate path | expand

Commit Message

Anna Pendleton May 7, 2020, 5:50 p.m. UTC
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(-)

Comments

kernel test robot May 12, 2020, 6:37 a.m. UTC | #1
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
Theodore Ts'o May 21, 2020, 4:55 p.m. UTC | #2
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
diff mbox series

Patch

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,