diff mbox series

[v12,6/7] xfs: Implement ->notify_failure() for XFS

Message ID 20220410160904.3758789-7-ruansy.fnst@fujitsu.com (mailing list archive)
State New, archived
Headers show
Series fsdax: introduce fs query to support reflink | expand

Commit Message

Shiyang Ruan April 10, 2022, 4:09 p.m. UTC
Introduce xfs_notify_failure.c to handle failure related works, such as
implement ->notify_failure(), register/unregister dax holder in xfs, and
so on.

If the rmap feature of XFS enabled, we can query it to find files and
metadata which are associated with the corrupt data.  For now all we do
is kill processes with that file mapped into their address spaces, but
future patches could actually do something about corrupt metadata.

After that, the memory failure needs to notify the processes who are
using those files.

Signed-off-by: Shiyang Ruan <ruansy.fnst@fujitsu.com>
---
 fs/xfs/Makefile             |   5 +
 fs/xfs/xfs_buf.c            |   7 +-
 fs/xfs/xfs_fsops.c          |   3 +
 fs/xfs/xfs_mount.h          |   1 +
 fs/xfs/xfs_notify_failure.c | 219 ++++++++++++++++++++++++++++++++++++
 fs/xfs/xfs_super.h          |   1 +
 6 files changed, 233 insertions(+), 3 deletions(-)
 create mode 100644 fs/xfs/xfs_notify_failure.c

Comments

kernel test robot April 10, 2022, 6:58 p.m. UTC | #1
Hi Shiyang,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on hnaz-mm/master]
[also build test ERROR on next-20220408]
[cannot apply to xfs-linux/for-next linus/master linux/master v5.18-rc1]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/intel-lab-lkp/linux/commits/Shiyang-Ruan/fsdax-introduce-fs-query-to-support-reflink/20220411-001048
base:   https://github.com/hnaz/linux-mm master
config: s390-defconfig (https://download.01.org/0day-ci/archive/20220411/202204110240.oa3G7lsW-lkp@intel.com/config)
compiler: s390-linux-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/bf68be0c39b8ecc4223b948a9ee126af167d74f0
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Shiyang-Ruan/fsdax-introduce-fs-query-to-support-reflink/20220411-001048
        git checkout bf68be0c39b8ecc4223b948a9ee126af167d74f0
        # save the config file to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=s390 SHELL=/bin/bash

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   s390-linux-ld: fs/xfs/xfs_buf.o: in function `xfs_alloc_buftarg':
>> fs/xfs/xfs_buf.c:1968: undefined reference to `xfs_dax_holder_operations'
   pahole: .tmp_vmlinux.btf: No such file or directory
   .btf.vmlinux.bin.o: file not recognized: file format not recognized


vim +1968 fs/xfs/xfs_buf.c

  1955	
  1956	struct xfs_buftarg *
  1957	xfs_alloc_buftarg(
  1958		struct xfs_mount	*mp,
  1959		struct block_device	*bdev)
  1960	{
  1961		xfs_buftarg_t		*btp;
  1962	
  1963		btp = kmem_zalloc(sizeof(*btp), KM_NOFS);
  1964	
  1965		btp->bt_mount = mp;
  1966		btp->bt_dev =  bdev->bd_dev;
  1967		btp->bt_bdev = bdev;
> 1968		btp->bt_daxdev = fs_dax_get_by_bdev(bdev, &btp->bt_dax_part_off, mp,
  1969						    &xfs_dax_holder_operations);
  1970	
  1971		/*
  1972		 * Buffer IO error rate limiting. Limit it to no more than 10 messages
  1973		 * per 30 seconds so as to not spam logs too much on repeated errors.
  1974		 */
  1975		ratelimit_state_init(&btp->bt_ioerror_rl, 30 * HZ,
  1976				     DEFAULT_RATELIMIT_BURST);
  1977	
  1978		if (xfs_setsize_buftarg_early(btp, bdev))
  1979			goto error_free;
  1980	
  1981		if (list_lru_init(&btp->bt_lru))
  1982			goto error_free;
  1983	
  1984		if (percpu_counter_init(&btp->bt_io_count, 0, GFP_KERNEL))
  1985			goto error_lru;
  1986	
  1987		btp->bt_shrinker.count_objects = xfs_buftarg_shrink_count;
  1988		btp->bt_shrinker.scan_objects = xfs_buftarg_shrink_scan;
  1989		btp->bt_shrinker.seeks = DEFAULT_SEEKS;
  1990		btp->bt_shrinker.flags = SHRINKER_NUMA_AWARE;
  1991		if (register_shrinker(&btp->bt_shrinker))
  1992			goto error_pcpu;
  1993		return btp;
  1994	
  1995	error_pcpu:
  1996		percpu_counter_destroy(&btp->bt_io_count);
  1997	error_lru:
  1998		list_lru_destroy(&btp->bt_lru);
  1999	error_free:
  2000		kmem_free(btp);
  2001		return NULL;
  2002	}
  2003
kernel test robot April 10, 2022, 11:54 p.m. UTC | #2
Hi Shiyang,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on hnaz-mm/master]
[also build test ERROR on next-20220408]
[cannot apply to xfs-linux/for-next linus/master linux/master v5.18-rc1]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/intel-lab-lkp/linux/commits/Shiyang-Ruan/fsdax-introduce-fs-query-to-support-reflink/20220411-001048
base:   https://github.com/hnaz/linux-mm master
config: s390-allyesconfig (https://download.01.org/0day-ci/archive/20220411/202204110700.66Eh1XZg-lkp@intel.com/config)
compiler: s390-linux-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/bf68be0c39b8ecc4223b948a9ee126af167d74f0
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Shiyang-Ruan/fsdax-introduce-fs-query-to-support-reflink/20220411-001048
        git checkout bf68be0c39b8ecc4223b948a9ee126af167d74f0
        # save the config file to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=s390 SHELL=/bin/bash

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   s390-linux-ld: fs/xfs/xfs_buf.o: in function `xfs_alloc_buftarg':
>> xfs_buf.c:(.text+0x9920): undefined reference to `xfs_dax_holder_operations'
Christoph Hellwig April 11, 2022, 6:39 a.m. UTC | #3
> --- a/fs/xfs/xfs_super.h
> +++ b/fs/xfs/xfs_super.h
> @@ -93,6 +93,7 @@ extern xfs_agnumber_t xfs_set_inode_alloc(struct xfs_mount *,
>  extern const struct export_operations xfs_export_operations;
>  extern const struct xattr_handler *xfs_xattr_handlers[];
>  extern const struct quotactl_ops xfs_quotactl_operations;


> +extern const struct dax_holder_operations xfs_dax_holder_operations;

This needs to be defined to NULL if at least one of CONFIG_FS_DAX or
CONFIG_MEMORY_FAILURE is not set.

Otherwise looks good:

Reviewed-by: Christoph Hellwig <hch@lst.de>
Dave Chinner April 13, 2022, 12:04 a.m. UTC | #4
On Mon, Apr 11, 2022 at 12:09:03AM +0800, Shiyang Ruan wrote:
> Introduce xfs_notify_failure.c to handle failure related works, such as
> implement ->notify_failure(), register/unregister dax holder in xfs, and
> so on.
> 
> If the rmap feature of XFS enabled, we can query it to find files and
> metadata which are associated with the corrupt data.  For now all we do
> is kill processes with that file mapped into their address spaces, but
> future patches could actually do something about corrupt metadata.
> 
> After that, the memory failure needs to notify the processes who are
> using those files.
> 
> Signed-off-by: Shiyang Ruan <ruansy.fnst@fujitsu.com>
> ---
>  fs/xfs/Makefile             |   5 +
>  fs/xfs/xfs_buf.c            |   7 +-
>  fs/xfs/xfs_fsops.c          |   3 +
>  fs/xfs/xfs_mount.h          |   1 +
>  fs/xfs/xfs_notify_failure.c | 219 ++++++++++++++++++++++++++++++++++++
>  fs/xfs/xfs_super.h          |   1 +
>  6 files changed, 233 insertions(+), 3 deletions(-)
>  create mode 100644 fs/xfs/xfs_notify_failure.c
> 
> diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
> index 04611a1068b4..09f5560e29f2 100644
> --- a/fs/xfs/Makefile
> +++ b/fs/xfs/Makefile
> @@ -128,6 +128,11 @@ xfs-$(CONFIG_SYSCTL)		+= xfs_sysctl.o
>  xfs-$(CONFIG_COMPAT)		+= xfs_ioctl32.o
>  xfs-$(CONFIG_EXPORTFS_BLOCK_OPS)	+= xfs_pnfs.o
>  
> +# notify failure
> +ifeq ($(CONFIG_MEMORY_FAILURE),y)
> +xfs-$(CONFIG_FS_DAX)		+= xfs_notify_failure.o
> +endif
> +
>  # online scrub/repair
>  ifeq ($(CONFIG_XFS_ONLINE_SCRUB),y)
>  
> diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c
> index f9ca08398d32..9064b8dfbc66 100644
> --- a/fs/xfs/xfs_buf.c
> +++ b/fs/xfs/xfs_buf.c
> @@ -5,6 +5,7 @@
>   */
>  #include "xfs.h"
>  #include <linux/backing-dev.h>
> +#include <linux/dax.h>
>  
>  #include "xfs_shared.h"
>  #include "xfs_format.h"
> @@ -1911,7 +1912,7 @@ xfs_free_buftarg(
>  	list_lru_destroy(&btp->bt_lru);
>  
>  	blkdev_issue_flush(btp->bt_bdev);
> -	fs_put_dax(btp->bt_daxdev, NULL);
> +	fs_put_dax(btp->bt_daxdev, btp->bt_mount);
>  
>  	kmem_free(btp);
>  }
> @@ -1964,8 +1965,8 @@ xfs_alloc_buftarg(
>  	btp->bt_mount = mp;
>  	btp->bt_dev =  bdev->bd_dev;
>  	btp->bt_bdev = bdev;
> -	btp->bt_daxdev = fs_dax_get_by_bdev(bdev, &btp->bt_dax_part_off, NULL,
> -					    NULL);
> +	btp->bt_daxdev = fs_dax_get_by_bdev(bdev, &btp->bt_dax_part_off, mp,
> +					    &xfs_dax_holder_operations);

I see a problem with this: we are setting up notify callbacks before
we've even read in the superblock during mount. i.e. we don't even
kow yet if we've got an XFS filesystem on this block device.

Hence if we get a notification immediately after registering this
notification callback....

[...]

> +
> +static int
> +xfs_dax_notify_ddev_failure(
> +	struct xfs_mount	*mp,
> +	xfs_daddr_t		daddr,
> +	xfs_daddr_t		bblen,
> +	int			mf_flags)
> +{
> +	struct xfs_trans	*tp = NULL;
> +	struct xfs_btree_cur	*cur = NULL;
> +	struct xfs_buf		*agf_bp = NULL;
> +	int			error = 0;
> +	xfs_fsblock_t		fsbno = XFS_DADDR_TO_FSB(mp, daddr);
> +	xfs_agnumber_t		agno = XFS_FSB_TO_AGNO(mp, fsbno);
> +	xfs_fsblock_t		end_fsbno = XFS_DADDR_TO_FSB(mp, daddr + bblen);
> +	xfs_agnumber_t		end_agno = XFS_FSB_TO_AGNO(mp, end_fsbno);

.... none of this code is going to function correctly because it
is dependent on the superblock having been read, validated and
copied to the in-memory superblock.

> +	error = xfs_trans_alloc_empty(mp, &tp);
> +	if (error)
> +		return error;

... and it's not valid to use transactions (even empty ones) before
log recovery has completed and set the log up correctly.

> +
> +	for (; agno <= end_agno; agno++) {
> +		struct xfs_rmap_irec	ri_low = { };
> +		struct xfs_rmap_irec	ri_high;
> +		struct failure_info	notify;
> +		struct xfs_agf		*agf;
> +		xfs_agblock_t		agend;
> +
> +		error = xfs_alloc_read_agf(mp, tp, agno, 0, &agf_bp);
> +		if (error)
> +			break;
> +
> +		cur = xfs_rmapbt_init_cursor(mp, tp, agf_bp, agf_bp->b_pag);

... and none of the structures this rmapbt walk is dependent on
(e.g. perag structures) have been initialised yet so there's null
pointer dereferences going to happen here.

Perhaps even worse is that the rmapbt is not guaranteed to be in
consistent state until after log recovery has completed, so this
walk could get stuck forever in a stale on-disk cycle that
recovery would have corrected....

Hence these notifications need to be delayed until after the
filesystem is mounted, all the internal structures have been set up
and log recovery has completed.

Cheers,

Dave.
Dan Williams April 13, 2022, 2:06 a.m. UTC | #5
On Tue, Apr 12, 2022 at 5:04 PM Dave Chinner <david@fromorbit.com> wrote:
>
> On Mon, Apr 11, 2022 at 12:09:03AM +0800, Shiyang Ruan wrote:
> > Introduce xfs_notify_failure.c to handle failure related works, such as
> > implement ->notify_failure(), register/unregister dax holder in xfs, and
> > so on.
> >
> > If the rmap feature of XFS enabled, we can query it to find files and
> > metadata which are associated with the corrupt data.  For now all we do
> > is kill processes with that file mapped into their address spaces, but
> > future patches could actually do something about corrupt metadata.
> >
> > After that, the memory failure needs to notify the processes who are
> > using those files.
> >
> > Signed-off-by: Shiyang Ruan <ruansy.fnst@fujitsu.com>
> > ---
> >  fs/xfs/Makefile             |   5 +
> >  fs/xfs/xfs_buf.c            |   7 +-
> >  fs/xfs/xfs_fsops.c          |   3 +
> >  fs/xfs/xfs_mount.h          |   1 +
> >  fs/xfs/xfs_notify_failure.c | 219 ++++++++++++++++++++++++++++++++++++
> >  fs/xfs/xfs_super.h          |   1 +
> >  6 files changed, 233 insertions(+), 3 deletions(-)
> >  create mode 100644 fs/xfs/xfs_notify_failure.c
> >
> > diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
> > index 04611a1068b4..09f5560e29f2 100644
> > --- a/fs/xfs/Makefile
> > +++ b/fs/xfs/Makefile
> > @@ -128,6 +128,11 @@ xfs-$(CONFIG_SYSCTL)             += xfs_sysctl.o
> >  xfs-$(CONFIG_COMPAT)         += xfs_ioctl32.o
> >  xfs-$(CONFIG_EXPORTFS_BLOCK_OPS)     += xfs_pnfs.o
> >
> > +# notify failure
> > +ifeq ($(CONFIG_MEMORY_FAILURE),y)
> > +xfs-$(CONFIG_FS_DAX)         += xfs_notify_failure.o
> > +endif
> > +
> >  # online scrub/repair
> >  ifeq ($(CONFIG_XFS_ONLINE_SCRUB),y)
> >
> > diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c
> > index f9ca08398d32..9064b8dfbc66 100644
> > --- a/fs/xfs/xfs_buf.c
> > +++ b/fs/xfs/xfs_buf.c
> > @@ -5,6 +5,7 @@
> >   */
> >  #include "xfs.h"
> >  #include <linux/backing-dev.h>
> > +#include <linux/dax.h>
> >
> >  #include "xfs_shared.h"
> >  #include "xfs_format.h"
> > @@ -1911,7 +1912,7 @@ xfs_free_buftarg(
> >       list_lru_destroy(&btp->bt_lru);
> >
> >       blkdev_issue_flush(btp->bt_bdev);
> > -     fs_put_dax(btp->bt_daxdev, NULL);
> > +     fs_put_dax(btp->bt_daxdev, btp->bt_mount);
> >
> >       kmem_free(btp);
> >  }
> > @@ -1964,8 +1965,8 @@ xfs_alloc_buftarg(
> >       btp->bt_mount = mp;
> >       btp->bt_dev =  bdev->bd_dev;
> >       btp->bt_bdev = bdev;
> > -     btp->bt_daxdev = fs_dax_get_by_bdev(bdev, &btp->bt_dax_part_off, NULL,
> > -                                         NULL);
> > +     btp->bt_daxdev = fs_dax_get_by_bdev(bdev, &btp->bt_dax_part_off, mp,
> > +                                         &xfs_dax_holder_operations);
>
> I see a problem with this: we are setting up notify callbacks before
> we've even read in the superblock during mount. i.e. we don't even
> kow yet if we've got an XFS filesystem on this block device.
>
> Hence if we get a notification immediately after registering this
> notification callback....
>
> [...]
>
> > +
> > +static int
> > +xfs_dax_notify_ddev_failure(
> > +     struct xfs_mount        *mp,
> > +     xfs_daddr_t             daddr,
> > +     xfs_daddr_t             bblen,
> > +     int                     mf_flags)
> > +{
> > +     struct xfs_trans        *tp = NULL;
> > +     struct xfs_btree_cur    *cur = NULL;
> > +     struct xfs_buf          *agf_bp = NULL;
> > +     int                     error = 0;
> > +     xfs_fsblock_t           fsbno = XFS_DADDR_TO_FSB(mp, daddr);
> > +     xfs_agnumber_t          agno = XFS_FSB_TO_AGNO(mp, fsbno);
> > +     xfs_fsblock_t           end_fsbno = XFS_DADDR_TO_FSB(mp, daddr + bblen);
> > +     xfs_agnumber_t          end_agno = XFS_FSB_TO_AGNO(mp, end_fsbno);
>
> .... none of this code is going to function correctly because it
> is dependent on the superblock having been read, validated and
> copied to the in-memory superblock.
>
> > +     error = xfs_trans_alloc_empty(mp, &tp);
> > +     if (error)
> > +             return error;
>
> ... and it's not valid to use transactions (even empty ones) before
> log recovery has completed and set the log up correctly.
>
> > +
> > +     for (; agno <= end_agno; agno++) {
> > +             struct xfs_rmap_irec    ri_low = { };
> > +             struct xfs_rmap_irec    ri_high;
> > +             struct failure_info     notify;
> > +             struct xfs_agf          *agf;
> > +             xfs_agblock_t           agend;
> > +
> > +             error = xfs_alloc_read_agf(mp, tp, agno, 0, &agf_bp);
> > +             if (error)
> > +                     break;
> > +
> > +             cur = xfs_rmapbt_init_cursor(mp, tp, agf_bp, agf_bp->b_pag);
>
> ... and none of the structures this rmapbt walk is dependent on
> (e.g. perag structures) have been initialised yet so there's null
> pointer dereferences going to happen here.
>
> Perhaps even worse is that the rmapbt is not guaranteed to be in
> consistent state until after log recovery has completed, so this
> walk could get stuck forever in a stale on-disk cycle that
> recovery would have corrected....
>
> Hence these notifications need to be delayed until after the
> filesystem is mounted, all the internal structures have been set up
> and log recovery has completed.

So I think this gets back to the fact that there will eventually be 2
paths into this notifier. One will be the currently proposed
synchronous / CPU-consumes-poison while accessing the filesystem
(potentially even while recovery is running), and another will be in
response to some asynchronous background scanning. I am thinking that
the latter would be driven from a userspace daemon reconciling
background scan events and notifying the filesystem and any other
interested party.

All that to say, I think it is ok / expected for the filesystem to
drop notifications on the floor when it is not ready to handle them.
For example there are no processes to send SIGBUS to if the filesystem
has not even finished mount. It is then up to userspace to replay any
relevant error notifications that may be pending after mount completes
to sync the filesystem with the current state of the hardware.
Dave Chinner April 13, 2022, 6:09 a.m. UTC | #6
On Tue, Apr 12, 2022 at 07:06:40PM -0700, Dan Williams wrote:
> On Tue, Apr 12, 2022 at 5:04 PM Dave Chinner <david@fromorbit.com> wrote:
> > On Mon, Apr 11, 2022 at 12:09:03AM +0800, Shiyang Ruan wrote:
> > > Introduce xfs_notify_failure.c to handle failure related works, such as
> > > implement ->notify_failure(), register/unregister dax holder in xfs, and
> > > so on.
> > >
> > > If the rmap feature of XFS enabled, we can query it to find files and
> > > metadata which are associated with the corrupt data.  For now all we do
> > > is kill processes with that file mapped into their address spaces, but
> > > future patches could actually do something about corrupt metadata.
> > >
> > > After that, the memory failure needs to notify the processes who are
> > > using those files.
...
> > > @@ -1964,8 +1965,8 @@ xfs_alloc_buftarg(
> > >       btp->bt_mount = mp;
> > >       btp->bt_dev =  bdev->bd_dev;
> > >       btp->bt_bdev = bdev;
> > > -     btp->bt_daxdev = fs_dax_get_by_bdev(bdev, &btp->bt_dax_part_off, NULL,
> > > -                                         NULL);
> > > +     btp->bt_daxdev = fs_dax_get_by_bdev(bdev, &btp->bt_dax_part_off, mp,
> > > +                                         &xfs_dax_holder_operations);
> >
> > I see a problem with this: we are setting up notify callbacks before
> > we've even read in the superblock during mount. i.e. we don't even
> > kow yet if we've got an XFS filesystem on this block device.
> > Hence these notifications need to be delayed until after the
> > filesystem is mounted, all the internal structures have been set up
> > and log recovery has completed.
> 
> So I think this gets back to the fact that there will eventually be 2
> paths into this notifier.

I'm not really concerned by how the notifications are generated;
my concern is purely that notifications can be handled safely.

> All that to say, I think it is ok / expected for the filesystem to
> drop notifications on the floor when it is not ready to handle them.

Well, yes. The whole point of notifications is the consumer makes
the decision on what to do with the notification it receives - the
producer of the notification does not (and can not) dictate what
policy the consumer(s) implement...

> For example there are no processes to send SIGBUS to if the filesystem
> has not even finished mount.

There may be not processes to send SIGBUS to even if the filesystem
has finished mount. But we still want the notifications to be
delivered and we still need to handle them safely.

IOWs, while we might start by avoiding notifications during mount,
this doesn't mean we will never have reason to process events during
mount. What we do with this notification is going to evolve over
time as we add new and adapt existing functionality....

Cheers,

Dave.
Dan Williams April 13, 2022, 5:09 p.m. UTC | #7
On Tue, Apr 12, 2022 at 11:10 PM Dave Chinner <david@fromorbit.com> wrote:
>
> On Tue, Apr 12, 2022 at 07:06:40PM -0700, Dan Williams wrote:
> > On Tue, Apr 12, 2022 at 5:04 PM Dave Chinner <david@fromorbit.com> wrote:
> > > On Mon, Apr 11, 2022 at 12:09:03AM +0800, Shiyang Ruan wrote:
> > > > Introduce xfs_notify_failure.c to handle failure related works, such as
> > > > implement ->notify_failure(), register/unregister dax holder in xfs, and
> > > > so on.
> > > >
> > > > If the rmap feature of XFS enabled, we can query it to find files and
> > > > metadata which are associated with the corrupt data.  For now all we do
> > > > is kill processes with that file mapped into their address spaces, but
> > > > future patches could actually do something about corrupt metadata.
> > > >
> > > > After that, the memory failure needs to notify the processes who are
> > > > using those files.
> ...
> > > > @@ -1964,8 +1965,8 @@ xfs_alloc_buftarg(
> > > >       btp->bt_mount = mp;
> > > >       btp->bt_dev =  bdev->bd_dev;
> > > >       btp->bt_bdev = bdev;
> > > > -     btp->bt_daxdev = fs_dax_get_by_bdev(bdev, &btp->bt_dax_part_off, NULL,
> > > > -                                         NULL);
> > > > +     btp->bt_daxdev = fs_dax_get_by_bdev(bdev, &btp->bt_dax_part_off, mp,
> > > > +                                         &xfs_dax_holder_operations);
> > >
> > > I see a problem with this: we are setting up notify callbacks before
> > > we've even read in the superblock during mount. i.e. we don't even
> > > kow yet if we've got an XFS filesystem on this block device.
> > > Hence these notifications need to be delayed until after the
> > > filesystem is mounted, all the internal structures have been set up
> > > and log recovery has completed.
> >
> > So I think this gets back to the fact that there will eventually be 2
> > paths into this notifier.
>
> I'm not really concerned by how the notifications are generated;
> my concern is purely that notifications can be handled safely.
>
> > All that to say, I think it is ok / expected for the filesystem to
> > drop notifications on the floor when it is not ready to handle them.
>
> Well, yes. The whole point of notifications is the consumer makes
> the decision on what to do with the notification it receives - the
> producer of the notification does not (and can not) dictate what
> policy the consumer(s) implement...
>
> > For example there are no processes to send SIGBUS to if the filesystem
> > has not even finished mount.
>
> There may be not processes to send SIGBUS to even if the filesystem
> has finished mount. But we still want the notifications to be
> delivered and we still need to handle them safely.
>
> IOWs, while we might start by avoiding notifications during mount,
> this doesn't mean we will never have reason to process events during
> mount. What we do with this notification is going to evolve over
> time as we add new and adapt existing functionality....

Yes, sounds like we're on the same page. I had mistakenly interpreted
"Hence these notifications need to be delayed until after the
filesystem is mounted" as something the producer would need to handle,
but yes, consumer is free to drop if the notification arrives at an
inopportune time.
Christoph Hellwig April 13, 2022, 5:12 p.m. UTC | #8
On Wed, Apr 13, 2022 at 10:09:40AM -0700, Dan Williams wrote:
> Yes, sounds like we're on the same page. I had mistakenly interpreted
> "Hence these notifications need to be delayed until after the
> filesystem is mounted" as something the producer would need to handle,
> but yes, consumer is free to drop if the notification arrives at an
> inopportune time.

A SB_BORN check might be all that we need.
kernel test robot April 14, 2022, 1:22 p.m. UTC | #9
Greeting,

FYI, we noticed the following commit (built with gcc-11):

commit: bf68be0c39b8ecc4223b948a9ee126af167d74f0 ("[PATCH v12 6/7] xfs: Implement ->notify_failure() for XFS")
url: https://github.com/intel-lab-lkp/linux/commits/Shiyang-Ruan/fsdax-introduce-fs-query-to-support-reflink/20220411-001048
base: https://github.com/hnaz/linux-mm master
patch link: https://lore.kernel.org/lkml/20220410160904.3758789-7-ruansy.fnst@fujitsu.com

in testcase: xfstests
version: xfstests-x86_64-1de1db8-1_20220217
with following parameters:

	disk: 4HDD
	fs: xfs
	test: xfs-group-05
	ucode: 0x21

test-description: xfstests is a regression test suite for xfs and other files ystems.
test-url: git://git.kernel.org/pub/scm/fs/xfs/xfstests-dev.git


on test machine: 4 threads 1 sockets Intel(R) Core(TM) i3-3220 CPU @ 3.30GHz with 8G memory

caused below changes (please refer to attached dmesg/kmsg for entire log/backtrace):



If you fix the issue, kindly add following tag
Reported-by: kernel test robot <oliver.sang@intel.com>


[ 62.111233][ T1606] BUG: KASAN: null-ptr-deref in fs_put_dax (drivers/dax/super.c:116 (discriminator 1)) 
[   62.117884][ T1606] Write of size 8 at addr 00000000000002f0 by task umount/1606
[   62.125379][ T1606]
[   62.127616][ T1606] CPU: 2 PID: 1606 Comm: umount Not tainted 5.18.0-rc1-mm1-00194-gbf68be0c39b8 #1
[   62.136760][ T1606] Hardware name: Hewlett-Packard HP Pro 3340 MT/17A1, BIOS 8.07 01/24/2013
[   62.145339][ T1606] Call Trace:
[   62.148554][ T1606]  <TASK>
[ 62.151404][ T1606] ? fs_put_dax (drivers/dax/super.c:116 (discriminator 1)) 
[ 62.155651][ T1606] dump_stack_lvl (lib/dump_stack.c:107 (discriminator 1)) 
[ 62.160110][ T1606] kasan_report (mm/kasan/report.c:162 mm/kasan/report.c:493) 
[ 62.164447][ T1606] ? fs_put_dax (drivers/dax/super.c:116 (discriminator 1)) 
[ 62.168677][ T1606] kasan_check_range (mm/kasan/generic.c:190) 
[ 62.173427][ T1606] fs_put_dax (drivers/dax/super.c:116 (discriminator 1)) 
[ 62.177519][ T1606] xfs_free_buftarg (fs/xfs/kmem.h:62 fs/xfs/xfs_buf.c:1917) xfs
[ 62.182900][ T1606] xfs_fs_put_super (fs/xfs/xfs_super.c:1101) xfs
[ 62.188326][ T1606] generic_shutdown_super (fs/super.c:464) 
[ 62.193636][ T1606] kill_block_super (fs/super.c:1395) 
[ 62.198325][ T1606] deactivate_locked_super (fs/super.c:339) 
[ 62.203656][ T1606] cleanup_mnt (fs/namespace.c:138 fs/namespace.c:1187) 
[ 62.208023][ T1606] ? path_umount (fs/namespace.c:1808) 
[ 62.212530][ T1606] task_work_run (kernel/task_work.c:166 (discriminator 1)) 
[ 62.216932][ T1606] exit_to_user_mode_loop (include/linux/resume_user_mode.h:49 kernel/entry/common.c:169) 
[ 62.222253][ T1606] exit_to_user_mode_prepare (kernel/entry/common.c:201) 
[ 62.227749][ T1606] syscall_exit_to_user_mode (arch/x86/include/asm/jump_label.h:27 include/linux/context_tracking_state.h:31 include/linux/context_tracking.h:40 kernel/entry/common.c:132 kernel/entry/common.c:296) 
[ 62.233149][ T1606] do_syscall_64 (arch/x86/entry/common.c:87) 
[ 62.237447][ T1606] entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:115) 
[   62.243288][ T1606] RIP: 0033:0x7fa858fee507
[ 62.247649][ T1606] Code: 19 0c 00 f7 d8 64 89 01 48 83 c8 ff c3 66 0f 1f 44 00 00 31 f6 e9 09 00 00 00 66 0f 1f 84 00 00 00 00 00 b8 a6 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 59 19 0c 00 f7 d8 64 89 01 48
All code
========
   0:	19 0c 00             	sbb    %ecx,(%rax,%rax,1)
   3:	f7 d8                	neg    %eax
   5:	64 89 01             	mov    %eax,%fs:(%rcx)
   8:	48 83 c8 ff          	or     $0xffffffffffffffff,%rax
   c:	c3                   	retq   
   d:	66 0f 1f 44 00 00    	nopw   0x0(%rax,%rax,1)
  13:	31 f6                	xor    %esi,%esi
  15:	e9 09 00 00 00       	jmpq   0x23
  1a:	66 0f 1f 84 00 00 00 	nopw   0x0(%rax,%rax,1)
  21:	00 00 
  23:	b8 a6 00 00 00       	mov    $0xa6,%eax
  28:	0f 05                	syscall 
  2a:*	48 3d 01 f0 ff ff    	cmp    $0xfffffffffffff001,%rax		<-- trapping instruction
  30:	73 01                	jae    0x33
  32:	c3                   	retq   
  33:	48 8b 0d 59 19 0c 00 	mov    0xc1959(%rip),%rcx        # 0xc1993
  3a:	f7 d8                	neg    %eax
  3c:	64 89 01             	mov    %eax,%fs:(%rcx)
  3f:	48                   	rex.W

Code starting with the faulting instruction
===========================================
   0:	48 3d 01 f0 ff ff    	cmp    $0xfffffffffffff001,%rax
   6:	73 01                	jae    0x9
   8:	c3                   	retq   
   9:	48 8b 0d 59 19 0c 00 	mov    0xc1959(%rip),%rcx        # 0xc1969
  10:	f7 d8                	neg    %eax
  12:	64 89 01             	mov    %eax,%fs:(%rcx)
  15:	48                   	rex.W
[   62.267385][ T1606] RSP: 002b:00007ffe344b8b68 EFLAGS: 00000246 ORIG_RAX: 00000000000000a6
[   62.275814][ T1606] RAX: 0000000000000000 RBX: 00005639c92b5970 RCX: 00007fa858fee507
[   62.283744][ T1606] RDX: 0000000000000001 RSI: 0000000000000000 RDI: 00005639c92b5b80
[   62.291682][ T1606] RBP: 0000000000000000 R08: 00005639c92b5ba0 R09: 00007fa85906fe80
[   62.299622][ T1606] R10: 0000000000000000 R11: 0000000000000246 R12: 00005639c92b5b80
[   62.307568][ T1606] R13: 00007fa8591141c4 R14: 00005639c92b5a68 R15: 0000000000000000
[   62.315510][ T1606]  </TASK>
[   62.318445][ T1606] ==================================================================
[   62.326514][ T1606] Disabling lock debugging due to kernel taint
[   62.332634][ T1606] BUG: kernel NULL pointer dereference, address: 00000000000002f0
[   62.340410][ T1606] #PF: supervisor write access in kernel mode
[   62.346422][ T1606] #PF: error_code(0x0002) - not-present page
[   62.352357][ T1606] PGD 0 P4D 0
[   62.355658][ T1606] Oops: 0002 [#1] SMP KASAN PTI
[   62.360475][ T1606] CPU: 2 PID: 1606 Comm: umount Tainted: G    B             5.18.0-rc1-mm1-00194-gbf68be0c39b8 #1
[   62.371045][ T1606] Hardware name: Hewlett-Packard HP Pro 3340 MT/17A1, BIOS 8.07 01/24/2013
[ 62.379598][ T1606] RIP: 0010:fs_put_dax (drivers/dax/super.c:116 (discriminator 1)) 
[ 62.384466][ T1606] Code: 40 00 0f 1f 44 00 00 55 48 89 fd 53 48 85 f6 74 27 48 89 f3 48 8d bf f0 02 00 00 be 08 00 00 00 e8 9d a8 29 ff 48 89 d8 31 d2 <f0> 48 0f b1 95 f0 02 00 00 48 39 c3 74 12 48 85 ed 74 0a 48 89 ef
All code
========
   0:	40 00 0f             	add    %cl,(%rdi)
   3:	1f                   	(bad)  
   4:	44 00 00             	add    %r8b,(%rax)
   7:	55                   	push   %rbp
   8:	48 89 fd             	mov    %rdi,%rbp
   b:	53                   	push   %rbx
   c:	48 85 f6             	test   %rsi,%rsi
   f:	74 27                	je     0x38
  11:	48 89 f3             	mov    %rsi,%rbx
  14:	48 8d bf f0 02 00 00 	lea    0x2f0(%rdi),%rdi
  1b:	be 08 00 00 00       	mov    $0x8,%esi
  20:	e8 9d a8 29 ff       	callq  0xffffffffff29a8c2
  25:	48 89 d8             	mov    %rbx,%rax
  28:	31 d2                	xor    %edx,%edx
  2a:*	f0 48 0f b1 95 f0 02 	lock cmpxchg %rdx,0x2f0(%rbp)		<-- trapping instruction
  31:	00 00 
  33:	48 39 c3             	cmp    %rax,%rbx
  36:	74 12                	je     0x4a
  38:	48 85 ed             	test   %rbp,%rbp
  3b:	74 0a                	je     0x47
  3d:	48 89 ef             	mov    %rbp,%rdi

Code starting with the faulting instruction
===========================================
   0:	f0 48 0f b1 95 f0 02 	lock cmpxchg %rdx,0x2f0(%rbp)
   7:	00 00 
   9:	48 39 c3             	cmp    %rax,%rbx
   c:	74 12                	je     0x20
   e:	48 85 ed             	test   %rbp,%rbp
  11:	74 0a                	je     0x1d
  13:	48 89 ef             	mov    %rbp,%rdi
[   62.404142][ T1606] RSP: 0018:ffffc90000f5fd90 EFLAGS: 00010246
[   62.410137][ T1606] RAX: ffff888140f34000 RBX: ffff888140f34000 RCX: ffffffff811992e6
[   62.418085][ T1606] RDX: 0000000000000000 RSI: 0000000000000008 RDI: ffffffff85c0b600
[   62.426032][ T1606] RBP: 0000000000000000 R08: 0000000000000001 R09: ffffffff85c0b607
[   62.433997][ T1606] R10: fffffbfff0b816c0 R11: 0000000000000000 R12: ffff8882189e80b8
[   62.441943][ T1606] R13: ffff888140f34180 R14: ffff888140f34188 R15: ffff8881312f4180
[   62.449876][ T1606] FS:  00007fa858bc8080(0000) GS:ffff8881aad00000(0000) knlGS:0000000000000000
[   62.458774][ T1606] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[   62.465317][ T1606] CR2: 00000000000002f0 CR3: 0000000134b6a002 CR4: 00000000001706e0
[   62.473283][ T1606] Call Trace:
[   62.476463][ T1606]  <TASK>
[ 62.479331][ T1606] xfs_free_buftarg (fs/xfs/kmem.h:62 fs/xfs/xfs_buf.c:1917) xfs
[ 62.484688][ T1606] xfs_fs_put_super (fs/xfs/xfs_super.c:1101) xfs
[ 62.490091][ T1606] generic_shutdown_super (fs/super.c:464) 
[ 62.495390][ T1606] kill_block_super (fs/super.c:1395) 
[ 62.500072][ T1606] deactivate_locked_super (fs/super.c:339) 
[ 62.505394][ T1606] cleanup_mnt (fs/namespace.c:138 fs/namespace.c:1187) 
[ 62.509717][ T1606] ? path_umount (fs/namespace.c:1808) 
[ 62.514233][ T1606] task_work_run (kernel/task_work.c:166 (discriminator 1)) 
[ 62.518679][ T1606] exit_to_user_mode_loop (include/linux/resume_user_mode.h:49 kernel/entry/common.c:169) 
[ 62.524009][ T1606] exit_to_user_mode_prepare (kernel/entry/common.c:201) 
[ 62.529493][ T1606] syscall_exit_to_user_mode (arch/x86/include/asm/jump_label.h:27 include/linux/context_tracking_state.h:31 include/linux/context_tracking.h:40 kernel/entry/common.c:132 kernel/entry/common.c:296) 
[ 62.534902][ T1606] do_syscall_64 (arch/x86/entry/common.c:87) 
[ 62.539220][ T1606] entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:115) 
[   62.545042][ T1606] RIP: 0033:0x7fa858fee507
[ 62.549388][ T1606] Code: 19 0c 00 f7 d8 64 89 01 48 83 c8 ff c3 66 0f 1f 44 00 00 31 f6 e9 09 00 00 00 66 0f 1f 84 00 00 00 00 00 b8 a6 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 59 19 0c 00 f7 d8 64 89 01 48
All code
========
   0:	19 0c 00             	sbb    %ecx,(%rax,%rax,1)
   3:	f7 d8                	neg    %eax
   5:	64 89 01             	mov    %eax,%fs:(%rcx)
   8:	48 83 c8 ff          	or     $0xffffffffffffffff,%rax
   c:	c3                   	retq   
   d:	66 0f 1f 44 00 00    	nopw   0x0(%rax,%rax,1)
  13:	31 f6                	xor    %esi,%esi
  15:	e9 09 00 00 00       	jmpq   0x23
  1a:	66 0f 1f 84 00 00 00 	nopw   0x0(%rax,%rax,1)
  21:	00 00 
  23:	b8 a6 00 00 00       	mov    $0xa6,%eax
  28:	0f 05                	syscall 
  2a:*	48 3d 01 f0 ff ff    	cmp    $0xfffffffffffff001,%rax		<-- trapping instruction
  30:	73 01                	jae    0x33
  32:	c3                   	retq   
  33:	48 8b 0d 59 19 0c 00 	mov    0xc1959(%rip),%rcx        # 0xc1993
  3a:	f7 d8                	neg    %eax
  3c:	64 89 01             	mov    %eax,%fs:(%rcx)
  3f:	48                   	rex.W

Code starting with the faulting instruction
===========================================
   0:	48 3d 01 f0 ff ff    	cmp    $0xfffffffffffff001,%rax
   6:	73 01                	jae    0x9
   8:	c3                   	retq   
   9:	48 8b 0d 59 19 0c 00 	mov    0xc1959(%rip),%rcx        # 0xc1969
  10:	f7 d8                	neg    %eax
  12:	64 89 01             	mov    %eax,%fs:(%rcx)
  15:	48                   	rex.W
[   62.569097][ T1606] RSP: 002b:00007ffe344b8b68 EFLAGS: 00000246 ORIG_RAX: 00000000000000a6
[   62.577467][ T1606] RAX: 0000000000000000 RBX: 00005639c92b5970 RCX: 00007fa858fee507
[   62.585386][ T1606] RDX: 0000000000000001 RSI: 0000000000000000 RDI: 00005639c92b5b80
[   62.593265][ T1606] RBP: 0000000000000000 R08: 00005639c92b5ba0 R09: 00007fa85906fe80
[   62.601248][ T1606] R10: 0000000000000000 R11: 0000000000000246 R12: 00005639c92b5b80
[   62.609151][ T1606] R13: 00007fa8591141c4 R14: 00005639c92b5a68 R15: 0000000000000000
[   62.617075][ T1606]  </TASK>
[   62.619995][ T1606] Modules linked in: xfs dm_mod netconsole btrfs blake2b_generic xor raid6_pq zstd_compress libcrc32c sd_mod t10_pi crc64_rocksoft_generic intel_rapl_msr crc64_rocksoft intel_rapl_common crc64 sg x86_pkg_temp_thermal intel_powerclamp coretemp ipmi_devintf i915 ipmi_msghandler kvm_intel kvm intel_gtt drm_buddy drm_dp_helper ttm irqbypass crct10dif_pclmul crc32_pclmul drm_kms_helper wmi_bmof crc32c_intel syscopyarea ghash_clmulni_intel rapl intel_cstate sysfillrect sysimgblt ahci fb_sys_fops libahci intel_uncore mei_me drm libata mei video wmi ip_tables
[   62.670932][ T1606] CR2: 00000000000002f0
[   62.675025][ T1606] ---[ end trace 0000000000000000 ]---
[ 62.680557][ T1606] RIP: 0010:fs_put_dax (drivers/dax/super.c:116 (discriminator 1)) 
[ 62.685457][ T1606] Code: 40 00 0f 1f 44 00 00 55 48 89 fd 53 48 85 f6 74 27 48 89 f3 48 8d bf f0 02 00 00 be 08 00 00 00 e8 9d a8 29 ff 48 89 d8 31 d2 <f0> 48 0f b1 95 f0 02 00 00 48 39 c3 74 12 48 85 ed 74 0a 48 89 ef
All code
========
   0:	40 00 0f             	add    %cl,(%rdi)
   3:	1f                   	(bad)  
   4:	44 00 00             	add    %r8b,(%rax)
   7:	55                   	push   %rbp
   8:	48 89 fd             	mov    %rdi,%rbp
   b:	53                   	push   %rbx
   c:	48 85 f6             	test   %rsi,%rsi
   f:	74 27                	je     0x38
  11:	48 89 f3             	mov    %rsi,%rbx
  14:	48 8d bf f0 02 00 00 	lea    0x2f0(%rdi),%rdi
  1b:	be 08 00 00 00       	mov    $0x8,%esi
  20:	e8 9d a8 29 ff       	callq  0xffffffffff29a8c2
  25:	48 89 d8             	mov    %rbx,%rax
  28:	31 d2                	xor    %edx,%edx
  2a:*	f0 48 0f b1 95 f0 02 	lock cmpxchg %rdx,0x2f0(%rbp)		<-- trapping instruction
  31:	00 00 
  33:	48 39 c3             	cmp    %rax,%rbx
  36:	74 12                	je     0x4a
  38:	48 85 ed             	test   %rbp,%rbp
  3b:	74 0a                	je     0x47
  3d:	48 89 ef             	mov    %rbp,%rdi

Code starting with the faulting instruction
===========================================
   0:	f0 48 0f b1 95 f0 02 	lock cmpxchg %rdx,0x2f0(%rbp)
   7:	00 00 
   9:	48 39 c3             	cmp    %rax,%rbx
   c:	74 12                	je     0x20
   e:	48 85 ed             	test   %rbp,%rbp
  11:	74 0a                	je     0x1d
  13:	48 89 ef             	mov    %rbp,%rdi


To reproduce:

        git clone https://github.com/intel/lkp-tests.git
        cd lkp-tests
        sudo bin/lkp install job.yaml           # job file is attached in this email
        bin/lkp split-job --compatible job.yaml # generate the yaml file for lkp run
        sudo bin/lkp run generated-yaml-file

        # if come across any failure that blocks the test,
        # please remove ~/.lkp and /lkp dir to run from a clean state.
diff mbox series

Patch

diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
index 04611a1068b4..09f5560e29f2 100644
--- a/fs/xfs/Makefile
+++ b/fs/xfs/Makefile
@@ -128,6 +128,11 @@  xfs-$(CONFIG_SYSCTL)		+= xfs_sysctl.o
 xfs-$(CONFIG_COMPAT)		+= xfs_ioctl32.o
 xfs-$(CONFIG_EXPORTFS_BLOCK_OPS)	+= xfs_pnfs.o
 
+# notify failure
+ifeq ($(CONFIG_MEMORY_FAILURE),y)
+xfs-$(CONFIG_FS_DAX)		+= xfs_notify_failure.o
+endif
+
 # online scrub/repair
 ifeq ($(CONFIG_XFS_ONLINE_SCRUB),y)
 
diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c
index f9ca08398d32..9064b8dfbc66 100644
--- a/fs/xfs/xfs_buf.c
+++ b/fs/xfs/xfs_buf.c
@@ -5,6 +5,7 @@ 
  */
 #include "xfs.h"
 #include <linux/backing-dev.h>
+#include <linux/dax.h>
 
 #include "xfs_shared.h"
 #include "xfs_format.h"
@@ -1911,7 +1912,7 @@  xfs_free_buftarg(
 	list_lru_destroy(&btp->bt_lru);
 
 	blkdev_issue_flush(btp->bt_bdev);
-	fs_put_dax(btp->bt_daxdev, NULL);
+	fs_put_dax(btp->bt_daxdev, btp->bt_mount);
 
 	kmem_free(btp);
 }
@@ -1964,8 +1965,8 @@  xfs_alloc_buftarg(
 	btp->bt_mount = mp;
 	btp->bt_dev =  bdev->bd_dev;
 	btp->bt_bdev = bdev;
-	btp->bt_daxdev = fs_dax_get_by_bdev(bdev, &btp->bt_dax_part_off, NULL,
-					    NULL);
+	btp->bt_daxdev = fs_dax_get_by_bdev(bdev, &btp->bt_dax_part_off, mp,
+					    &xfs_dax_holder_operations);
 
 	/*
 	 * Buffer IO error rate limiting. Limit it to no more than 10 messages
diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
index 68f74549fa22..56530900bb86 100644
--- a/fs/xfs/xfs_fsops.c
+++ b/fs/xfs/xfs_fsops.c
@@ -536,6 +536,9 @@  xfs_do_force_shutdown(
 	} else if (flags & SHUTDOWN_CORRUPT_INCORE) {
 		tag = XFS_PTAG_SHUTDOWN_CORRUPT;
 		why = "Corruption of in-memory data";
+	} else if (flags & SHUTDOWN_CORRUPT_ONDISK) {
+		tag = XFS_PTAG_SHUTDOWN_CORRUPT;
+		why = "Corruption of on-disk metadata";
 	} else {
 		tag = XFS_PTAG_SHUTDOWN_IOERROR;
 		why = "Metadata I/O Error";
diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
index f6dc19de8322..9237cc159542 100644
--- a/fs/xfs/xfs_mount.h
+++ b/fs/xfs/xfs_mount.h
@@ -435,6 +435,7 @@  void xfs_do_force_shutdown(struct xfs_mount *mp, int flags, char *fname,
 #define SHUTDOWN_LOG_IO_ERROR	0x0002	/* write attempt to the log failed */
 #define SHUTDOWN_FORCE_UMOUNT	0x0004	/* shutdown from a forced unmount */
 #define SHUTDOWN_CORRUPT_INCORE	0x0008	/* corrupt in-memory data structures */
+#define SHUTDOWN_CORRUPT_ONDISK	0x0010  /* corrupt metadata on device */
 
 #define XFS_SHUTDOWN_STRINGS \
 	{ SHUTDOWN_META_IO_ERROR,	"metadata_io" }, \
diff --git a/fs/xfs/xfs_notify_failure.c b/fs/xfs/xfs_notify_failure.c
new file mode 100644
index 000000000000..aac44f54feb4
--- /dev/null
+++ b/fs/xfs/xfs_notify_failure.c
@@ -0,0 +1,219 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 Fujitsu.  All Rights Reserved.
+ */
+
+#include "xfs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_alloc.h"
+#include "xfs_bit.h"
+#include "xfs_btree.h"
+#include "xfs_inode.h"
+#include "xfs_icache.h"
+#include "xfs_rmap.h"
+#include "xfs_rmap_btree.h"
+#include "xfs_rtalloc.h"
+#include "xfs_trans.h"
+
+#include <linux/mm.h>
+#include <linux/dax.h>
+
+struct failure_info {
+	xfs_agblock_t		startblock;
+	xfs_extlen_t		blockcount;
+	int			mf_flags;
+};
+
+static pgoff_t
+xfs_failure_pgoff(
+	struct xfs_mount		*mp,
+	const struct xfs_rmap_irec	*rec,
+	const struct failure_info	*notify)
+{
+	uint64_t			pos = rec->rm_offset;
+
+	if (notify->startblock > rec->rm_startblock)
+		pos += XFS_FSB_TO_B(mp,
+				notify->startblock - rec->rm_startblock);
+	return pos >> PAGE_SHIFT;
+}
+
+static unsigned long
+xfs_failure_pgcnt(
+	struct xfs_mount		*mp,
+	const struct xfs_rmap_irec	*rec,
+	const struct failure_info	*notify)
+{
+	xfs_agblock_t			end_rec;
+	xfs_agblock_t			end_notify;
+	xfs_agblock_t			start_cross;
+	xfs_agblock_t			end_cross;
+
+	start_cross = max(rec->rm_startblock, notify->startblock);
+
+	end_rec = rec->rm_startblock + rec->rm_blockcount;
+	end_notify = notify->startblock + notify->blockcount;
+	end_cross = min(end_rec, end_notify);
+
+	return XFS_FSB_TO_B(mp, end_cross - start_cross) >> PAGE_SHIFT;
+}
+
+static int
+xfs_dax_failure_fn(
+	struct xfs_btree_cur		*cur,
+	const struct xfs_rmap_irec	*rec,
+	void				*data)
+{
+	struct xfs_mount		*mp = cur->bc_mp;
+	struct xfs_inode		*ip;
+	struct failure_info		*notify = data;
+	int				error = 0;
+
+	if (XFS_RMAP_NON_INODE_OWNER(rec->rm_owner) ||
+	    (rec->rm_flags & (XFS_RMAP_ATTR_FORK | XFS_RMAP_BMBT_BLOCK))) {
+		xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_ONDISK);
+		return -EFSCORRUPTED;
+	}
+
+	/* Get files that incore, filter out others that are not in use. */
+	error = xfs_iget(mp, cur->bc_tp, rec->rm_owner, XFS_IGET_INCORE,
+			 0, &ip);
+	/* Continue the rmap query if the inode isn't incore */
+	if (error == -ENODATA)
+		return 0;
+	if (error)
+		return error;
+
+	error = mf_dax_kill_procs(VFS_I(ip)->i_mapping,
+				  xfs_failure_pgoff(mp, rec, notify),
+				  xfs_failure_pgcnt(mp, rec, notify),
+				  notify->mf_flags);
+	xfs_irele(ip);
+	return error;
+}
+
+static int
+xfs_dax_notify_ddev_failure(
+	struct xfs_mount	*mp,
+	xfs_daddr_t		daddr,
+	xfs_daddr_t		bblen,
+	int			mf_flags)
+{
+	struct xfs_trans	*tp = NULL;
+	struct xfs_btree_cur	*cur = NULL;
+	struct xfs_buf		*agf_bp = NULL;
+	int			error = 0;
+	xfs_fsblock_t		fsbno = XFS_DADDR_TO_FSB(mp, daddr);
+	xfs_agnumber_t		agno = XFS_FSB_TO_AGNO(mp, fsbno);
+	xfs_fsblock_t		end_fsbno = XFS_DADDR_TO_FSB(mp, daddr + bblen);
+	xfs_agnumber_t		end_agno = XFS_FSB_TO_AGNO(mp, end_fsbno);
+
+	error = xfs_trans_alloc_empty(mp, &tp);
+	if (error)
+		return error;
+
+	for (; agno <= end_agno; agno++) {
+		struct xfs_rmap_irec	ri_low = { };
+		struct xfs_rmap_irec	ri_high;
+		struct failure_info	notify;
+		struct xfs_agf		*agf;
+		xfs_agblock_t		agend;
+
+		error = xfs_alloc_read_agf(mp, tp, agno, 0, &agf_bp);
+		if (error)
+			break;
+
+		cur = xfs_rmapbt_init_cursor(mp, tp, agf_bp, agf_bp->b_pag);
+
+		/*
+		 * Set the rmap range from ri_low to ri_high, which represents
+		 * a [start, end] where we looking for the files or metadata.
+		 * The part of range out of a AG will be ignored.  So, it's fine
+		 * to set ri_low to "startblock" in all loops.  When it reaches
+		 * the last AG, set the ri_high to "endblock" to make sure we
+		 * actually end at the end.
+		 */
+		memset(&ri_high, 0xFF, sizeof(ri_high));
+		ri_low.rm_startblock = XFS_FSB_TO_AGBNO(mp, fsbno);
+		if (agno == end_agno)
+			ri_high.rm_startblock = XFS_FSB_TO_AGBNO(mp, end_fsbno);
+
+		agf = agf_bp->b_addr;
+		agend = min(be32_to_cpu(agf->agf_length),
+				ri_high.rm_startblock);
+		notify.startblock = ri_low.rm_startblock;
+		notify.blockcount = agend - ri_low.rm_startblock;
+
+		error = xfs_rmap_query_range(cur, &ri_low, &ri_high,
+				xfs_dax_failure_fn, &notify);
+		xfs_btree_del_cursor(cur, error);
+		xfs_trans_brelse(tp, agf_bp);
+		if (error)
+			break;
+
+		fsbno = XFS_AGB_TO_FSB(mp, agno + 1, 0);
+	}
+
+	xfs_trans_cancel(tp);
+	return error;
+}
+
+static int
+xfs_dax_notify_failure(
+	struct dax_device	*dax_dev,
+	u64			offset,
+	u64			len,
+	int			mf_flags)
+{
+	struct xfs_mount	*mp = dax_holder(dax_dev);
+	u64			ddev_start;
+	u64			ddev_end;
+
+	if (mp->m_rtdev_targp && mp->m_rtdev_targp->bt_daxdev == dax_dev) {
+		xfs_warn(mp,
+			 "notify_failure() not supported on realtime device!");
+		return -EOPNOTSUPP;
+	}
+
+	if (mp->m_logdev_targp && mp->m_logdev_targp->bt_daxdev == dax_dev &&
+	    mp->m_logdev_targp != mp->m_ddev_targp) {
+		xfs_err(mp, "ondisk log corrupt, shutting down fs!");
+		xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_ONDISK);
+		return -EFSCORRUPTED;
+	}
+
+	if (!xfs_has_rmapbt(mp)) {
+		xfs_warn(mp, "notify_failure() needs rmapbt enabled!");
+		return -EOPNOTSUPP;
+	}
+
+	ddev_start = mp->m_ddev_targp->bt_dax_part_off;
+	ddev_end = ddev_start + bdev_nr_bytes(mp->m_ddev_targp->bt_bdev) - 1;
+
+	/* Ignore the range out of filesystem area */
+	if (offset + len < ddev_start)
+		return -ENXIO;
+	if (offset > ddev_end)
+		return -ENXIO;
+
+	/* Calculate the real range when it touches the boundary */
+	if (offset > ddev_start)
+		offset -= ddev_start;
+	else {
+		len -= ddev_start - offset;
+		offset = 0;
+	}
+	if (offset + len > ddev_end)
+		len -= ddev_end - offset;
+
+	return xfs_dax_notify_ddev_failure(mp, BTOBB(offset), BTOBB(len),
+			mf_flags);
+}
+
+const struct dax_holder_operations xfs_dax_holder_operations = {
+	.notify_failure		= xfs_dax_notify_failure,
+};
diff --git a/fs/xfs/xfs_super.h b/fs/xfs/xfs_super.h
index 167d23f92ffe..27ab5087d0b3 100644
--- a/fs/xfs/xfs_super.h
+++ b/fs/xfs/xfs_super.h
@@ -93,6 +93,7 @@  extern xfs_agnumber_t xfs_set_inode_alloc(struct xfs_mount *,
 extern const struct export_operations xfs_export_operations;
 extern const struct xattr_handler *xfs_xattr_handlers[];
 extern const struct quotactl_ops xfs_quotactl_operations;
+extern const struct dax_holder_operations xfs_dax_holder_operations;
 
 extern void xfs_reinit_percpu_counters(struct xfs_mount *mp);