Re: [PATCH v2] nilfs2: suppress false positive WARN_ONs for sufile after an FS error

Viacheslav Dubeyko <[email protected]>
Newsgroups gmane.comp.file-systems.nilfs.user,gmane.linux.kernel
Message-ID <[email protected]>
On Fri, 2026-08-07 at 03:13 +0900, Ryusuke Konishi wrote:
> After applying the commit associated with the Fixes tag, metadata
> file
> buffers can be evicted from memory even after being marked dirty.
> 
> Consequently, operations such as rolling back sufile changes upon
> error - which modify the buffer and were previously assumed incapable
> of failure - can now fail.
> 
> This behavior causes syzbot to trigger a WARN_ON check immediately
> following sufile function calls within the log writer.
> 
> Resolve this issue by introducing a macro,
> nilfs_sufile_warn_on_error(),
> which uses WARN_ONCE to report unexpected errors only when the
> filesystem
> has not degraded to read-only mode, returning -EIO or -EROFS
> accordingly.
> Replace existing WARN_ON checks for unexpected errors following
> sufile
> operations with this new macro.
> 
> Additionally, for nilfs_segctor_truncate_segments() - where an error
> must
> be propagated to halt log writing if a sufile operation fails -
> modify
> the function to return the error code appropriately.
> 
> Reported-by: syzbot+5957361606d7b750b874-Pl5Pbv+GP7P466ipTTIvnc23WoclnBCfAL8bYrjMMd8@public.gmane.org
> Closes: https://syzkaller.appspot.com/bug?extid=5957361606d7b750b874
> Fixes: 8c26c4e2694a ("nilfs2: fix issue with flush kernel thread
> after remount in RO mode because of driver's internal error or
> metadata corruption")
> Cc: <[email protected]> # Warning suppression primarily;
> will request backport individually if needed
> Signed-off-by: Ryusuke Konishi <[email protected]>
> ---
> v2: Fix nilfs_sufile_warn_on_error() macro to correctly return _err
>     instead of the boolean result of unlikely(_err), and add
> unlikely()
>     to the return value check in nilfs_segctor_truncate_segments().
>     (Thanks to Zhan Xusheng for pointing out the macro bug).
> 
> Hi Viacheslav,
> 
> Please apply this for the next cycle at your convenience.
> 
> This fixes an issue where a WARN_ON check is triggered by sufile
> functions within the log writer after the filesystem degrades to
> read-only
> mode.  This is a false-positive warning reported by syzbot this June
> (and likely reported previously as well), which can occur as a normal
> consequence after degradation.
> 
> This v2 patch fixes a macro bug pointed out, where the final
> expression
> incorrectly normalized the return value.
> 
> Thanks,
> Ryusuke Konishi
> 
>  fs/nilfs2/segment.c | 36 ++++++++++++++++++++----------------
>  fs/nilfs2/sufile.h  | 28 ++++++++++++++++++++++++++++
>  2 files changed, 48 insertions(+), 16 deletions(-)
> 
> diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c
> index 2189267894d2..5896fdae5669 100644
> --- a/fs/nilfs2/segment.c
> +++ b/fs/nilfs2/segment.c
> @@ -1433,7 +1433,7 @@ static int nilfs_segctor_extend_segments(struct
> nilfs_sc_info *sci,
>   failed:
>  	list_for_each_entry(segbuf, &list, sb_list) {
>  		ret = nilfs_sufile_free(sufile, segbuf->sb_nextnum);
> -		WARN_ON(ret); /* never fails */
> +		nilfs_sufile_warn_on_error(sufile, ret);
>  	}
>  	nilfs_destroy_logs(&list);
>  	return err;
> @@ -1449,7 +1449,7 @@ static void nilfs_free_incomplete_logs(struct
> list_head *logs,
>  	segbuf = NILFS_FIRST_SEGBUF(logs);
>  	if (nilfs->ns_nextnum != segbuf->sb_nextnum) {
>  		ret = nilfs_sufile_free(sufile, segbuf->sb_nextnum);
> -		WARN_ON(ret); /* never fails */
> +		nilfs_sufile_warn_on_error(sufile, ret);
>  	}
>  	if (atomic_read(&segbuf->sb_err)) {
>  		/* Case 1: The first segment failed */
> @@ -1468,7 +1468,7 @@ static void nilfs_free_incomplete_logs(struct
> list_head *logs,
>  	list_for_each_entry_continue(segbuf, logs, sb_list) {
>  		if (prev->sb_nextnum != segbuf->sb_nextnum) {
>  			ret = nilfs_sufile_free(sufile, segbuf-
> >sb_nextnum);
> -			WARN_ON(ret); /* never fails */
> +			nilfs_sufile_warn_on_error(sufile, ret);
>  		}
>  		if (atomic_read(&segbuf->sb_err) &&
>  		    segbuf->sb_segnum != nilfs->ns_nextnum)
> @@ -1491,7 +1491,7 @@ static void
> nilfs_segctor_update_segusage(struct nilfs_sc_info *sci,
>  		ret = nilfs_sufile_set_segment_usage(sufile, segbuf-
> >sb_segnum,
>  						     live_blocks,
>  						     sci-
> >sc_seg_ctime);
> -		WARN_ON(ret); /* always succeed because the segusage
> is dirty */
> +		nilfs_sufile_warn_on_error(sufile, ret);
>  	}
>  }
>  
> @@ -1504,28 +1504,32 @@ static void nilfs_cancel_segusage(struct
> list_head *logs, struct inode *sufile)
>  	ret = nilfs_sufile_set_segment_usage(sufile, segbuf-
> >sb_segnum,
>  					     segbuf->sb_pseg_start -
>  					     segbuf->sb_fseg_start,
> 0);
> -	WARN_ON(ret); /* always succeed because the segusage is
> dirty */
> +	nilfs_sufile_warn_on_error(sufile, ret);
>  
>  	list_for_each_entry_continue(segbuf, logs, sb_list) {
>  		ret = nilfs_sufile_set_segment_usage(sufile, segbuf-
> >sb_segnum,
>  						     0, 0);
> -		WARN_ON(ret); /* always succeed */
> +		nilfs_sufile_warn_on_error(sufile, ret);
>  	}
>  }
>  
> -static void nilfs_segctor_truncate_segments(struct nilfs_sc_info
> *sci,
> -					    struct
> nilfs_segment_buffer *last,
> -					    struct inode *sufile)
> +static int nilfs_segctor_truncate_segments(struct nilfs_sc_info
> *sci,
> +					struct nilfs_segment_buffer
> *last,
> +					struct inode *sufile)
>  {
>  	struct nilfs_segment_buffer *segbuf = last;
> -	int ret;
> +	int ret, err = 0;
>  
>  	list_for_each_entry_continue(segbuf, &sci->sc_segbufs,
> sb_list) {
>  		sci->sc_segbuf_nblocks -= segbuf->sb_rest_blocks;
> -		ret = nilfs_sufile_free(sufile, segbuf->sb_nextnum);
> -		WARN_ON(ret);
> +
> +		ret = nilfs_sufile_warn_on_error(
> +			sufile, nilfs_sufile_free(sufile, segbuf-
> >sb_nextnum));
> +		if (unlikely(ret) && err != -EROFS)
> +			err = ret;
>  	}
>  	nilfs_truncate_logs(&sci->sc_segbufs, last);
> +	return err;
>  }
>  
>  
> @@ -1564,7 +1568,7 @@ static int nilfs_segctor_collect(struct
> nilfs_sc_info *sci,
>  							sci-
> >sc_freesegs,
>  							sci-
> >sc_nfreesegs,
>  							NULL);
> -			WARN_ON(err); /* do not happen */
> +			nilfs_sufile_warn_on_error(nilfs->ns_sufile,
> err);
>  			sci->sc_stage.flags &= ~NILFS_CF_SUFREED;
>  		}
>  
> @@ -1576,8 +1580,8 @@ static int nilfs_segctor_collect(struct
> nilfs_sc_info *sci,
>  		sci->sc_stage = prev_stage;
>  	}
>  	nilfs_segctor_zeropad_segsum(sci);
> -	nilfs_segctor_truncate_segments(sci, sci->sc_curseg, nilfs-
> >ns_sufile);
> -	return 0;
> +	err = nilfs_segctor_truncate_segments(sci, sci->sc_curseg,
> +					nilfs->ns_sufile);
>  
>   failed:
>  	return err;
> @@ -1878,7 +1882,7 @@ static void
> nilfs_segctor_abort_construction(struct nilfs_sc_info *sci,
>  						sci->sc_freesegs,
>  						sci->sc_nfreesegs,
>  						NULL);
> -		WARN_ON(ret); /* do not happen */
> +		nilfs_sufile_warn_on_error(nilfs->ns_sufile, ret);
>  	}
>  
>  	nilfs_destroy_logs(&logs);
> diff --git a/fs/nilfs2/sufile.h b/fs/nilfs2/sufile.h
> index cd6f28ab3521..5888ed479c8b 100644
> --- a/fs/nilfs2/sufile.h
> +++ b/fs/nilfs2/sufile.h
> @@ -10,6 +10,7 @@
>  #ifndef _NILFS_SUFILE_H
>  #define _NILFS_SUFILE_H
>  
> +#include <linux/errno.h>
>  #include <linux/fs.h>
>  #include <linux/buffer_head.h>
>  #include "mdt.h"
> @@ -54,6 +55,33 @@ int nilfs_sufile_read(struct super_block *sb,
> size_t susize,
>  		      struct nilfs_inode *raw_inode, struct inode
> **inodep);
>  int nilfs_sufile_trim_fs(struct inode *sufile, struct fstrim_range
> *range);
>  
> +/**
> + * nilfs_sufile_warn_on_error - warn on unexpected sufile error
> + * @sufile: inode of segment usage file
> + * @err: status code returned by a sufile function
> + *
> + * Even if buffer heads of blocks containing segment usage entries
> have
> + * been dirtied in advance by calling functions such as
> + * nilfs_sufile_mark_dirty() or nilfs_sufile_{alloc,free}(), those
> buffers
> + * can be discarded from memory after the file system detects
> corruption and
> + * degrades to read-only mode, which may cause sufile operations,
> including
> + * cancel operations, to return errors. 
> nilfs_sufile_warn_on_error() is used
> + * to detect unexpected errors other than during read-only
> degradation.
> + *
> + * Return: 0 if @err is 0, %-EROFS if in read-only degraded mode,
> and %-EIO
> + * otherwise.
> + */
> +#define nilfs_sufile_warn_on_error(sufile,
> err)				\
> +	({							
> 	\
> +		int _err =
> (err);					\
> +								
> 	\
> +		if
> (unlikely(_err))					\
> +			_err = WARN_ONCE(!sb_rdonly((sufile)-
> >i_sb),	\
> +				"unexpected sufile error %d\n",
> _err) ? \
> +				-EIO : -
> EROFS;				\
> +		_err;						
> 	\
> +	})
> +
>  /**
>   * nilfs_sufile_scrap - make a segment garbage
>   * @sufile: inode of segment usage file

Applied.

Thanks,
Slava.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.