162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2022 Fujitsu.  All Rights Reserved.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include "xfs.h"
762306a36Sopenharmony_ci#include "xfs_shared.h"
862306a36Sopenharmony_ci#include "xfs_format.h"
962306a36Sopenharmony_ci#include "xfs_log_format.h"
1062306a36Sopenharmony_ci#include "xfs_trans_resv.h"
1162306a36Sopenharmony_ci#include "xfs_mount.h"
1262306a36Sopenharmony_ci#include "xfs_alloc.h"
1362306a36Sopenharmony_ci#include "xfs_bit.h"
1462306a36Sopenharmony_ci#include "xfs_btree.h"
1562306a36Sopenharmony_ci#include "xfs_inode.h"
1662306a36Sopenharmony_ci#include "xfs_icache.h"
1762306a36Sopenharmony_ci#include "xfs_rmap.h"
1862306a36Sopenharmony_ci#include "xfs_rmap_btree.h"
1962306a36Sopenharmony_ci#include "xfs_rtalloc.h"
2062306a36Sopenharmony_ci#include "xfs_trans.h"
2162306a36Sopenharmony_ci#include "xfs_ag.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <linux/mm.h>
2462306a36Sopenharmony_ci#include <linux/dax.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistruct xfs_failure_info {
2762306a36Sopenharmony_ci	xfs_agblock_t		startblock;
2862306a36Sopenharmony_ci	xfs_extlen_t		blockcount;
2962306a36Sopenharmony_ci	int			mf_flags;
3062306a36Sopenharmony_ci	bool			want_shutdown;
3162306a36Sopenharmony_ci};
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic pgoff_t
3462306a36Sopenharmony_cixfs_failure_pgoff(
3562306a36Sopenharmony_ci	struct xfs_mount		*mp,
3662306a36Sopenharmony_ci	const struct xfs_rmap_irec	*rec,
3762306a36Sopenharmony_ci	const struct xfs_failure_info	*notify)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	loff_t				pos = XFS_FSB_TO_B(mp, rec->rm_offset);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	if (notify->startblock > rec->rm_startblock)
4262306a36Sopenharmony_ci		pos += XFS_FSB_TO_B(mp,
4362306a36Sopenharmony_ci				notify->startblock - rec->rm_startblock);
4462306a36Sopenharmony_ci	return pos >> PAGE_SHIFT;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic unsigned long
4862306a36Sopenharmony_cixfs_failure_pgcnt(
4962306a36Sopenharmony_ci	struct xfs_mount		*mp,
5062306a36Sopenharmony_ci	const struct xfs_rmap_irec	*rec,
5162306a36Sopenharmony_ci	const struct xfs_failure_info	*notify)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	xfs_agblock_t			end_rec;
5462306a36Sopenharmony_ci	xfs_agblock_t			end_notify;
5562306a36Sopenharmony_ci	xfs_agblock_t			start_cross;
5662306a36Sopenharmony_ci	xfs_agblock_t			end_cross;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	start_cross = max(rec->rm_startblock, notify->startblock);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	end_rec = rec->rm_startblock + rec->rm_blockcount;
6162306a36Sopenharmony_ci	end_notify = notify->startblock + notify->blockcount;
6262306a36Sopenharmony_ci	end_cross = min(end_rec, end_notify);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	return XFS_FSB_TO_B(mp, end_cross - start_cross) >> PAGE_SHIFT;
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic int
6862306a36Sopenharmony_cixfs_dax_failure_fn(
6962306a36Sopenharmony_ci	struct xfs_btree_cur		*cur,
7062306a36Sopenharmony_ci	const struct xfs_rmap_irec	*rec,
7162306a36Sopenharmony_ci	void				*data)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	struct xfs_mount		*mp = cur->bc_mp;
7462306a36Sopenharmony_ci	struct xfs_inode		*ip;
7562306a36Sopenharmony_ci	struct xfs_failure_info		*notify = data;
7662306a36Sopenharmony_ci	int				error = 0;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (XFS_RMAP_NON_INODE_OWNER(rec->rm_owner) ||
7962306a36Sopenharmony_ci	    (rec->rm_flags & (XFS_RMAP_ATTR_FORK | XFS_RMAP_BMBT_BLOCK))) {
8062306a36Sopenharmony_ci		notify->want_shutdown = true;
8162306a36Sopenharmony_ci		return 0;
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	/* Get files that incore, filter out others that are not in use. */
8562306a36Sopenharmony_ci	error = xfs_iget(mp, cur->bc_tp, rec->rm_owner, XFS_IGET_INCORE,
8662306a36Sopenharmony_ci			 0, &ip);
8762306a36Sopenharmony_ci	/* Continue the rmap query if the inode isn't incore */
8862306a36Sopenharmony_ci	if (error == -ENODATA)
8962306a36Sopenharmony_ci		return 0;
9062306a36Sopenharmony_ci	if (error) {
9162306a36Sopenharmony_ci		notify->want_shutdown = true;
9262306a36Sopenharmony_ci		return 0;
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	error = mf_dax_kill_procs(VFS_I(ip)->i_mapping,
9662306a36Sopenharmony_ci				  xfs_failure_pgoff(mp, rec, notify),
9762306a36Sopenharmony_ci				  xfs_failure_pgcnt(mp, rec, notify),
9862306a36Sopenharmony_ci				  notify->mf_flags);
9962306a36Sopenharmony_ci	xfs_irele(ip);
10062306a36Sopenharmony_ci	return error;
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic int
10462306a36Sopenharmony_cixfs_dax_notify_ddev_failure(
10562306a36Sopenharmony_ci	struct xfs_mount	*mp,
10662306a36Sopenharmony_ci	xfs_daddr_t		daddr,
10762306a36Sopenharmony_ci	xfs_daddr_t		bblen,
10862306a36Sopenharmony_ci	int			mf_flags)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct xfs_failure_info	notify = { .mf_flags = mf_flags };
11162306a36Sopenharmony_ci	struct xfs_trans	*tp = NULL;
11262306a36Sopenharmony_ci	struct xfs_btree_cur	*cur = NULL;
11362306a36Sopenharmony_ci	struct xfs_buf		*agf_bp = NULL;
11462306a36Sopenharmony_ci	int			error = 0;
11562306a36Sopenharmony_ci	xfs_fsblock_t		fsbno = XFS_DADDR_TO_FSB(mp, daddr);
11662306a36Sopenharmony_ci	xfs_agnumber_t		agno = XFS_FSB_TO_AGNO(mp, fsbno);
11762306a36Sopenharmony_ci	xfs_fsblock_t		end_fsbno = XFS_DADDR_TO_FSB(mp,
11862306a36Sopenharmony_ci							     daddr + bblen - 1);
11962306a36Sopenharmony_ci	xfs_agnumber_t		end_agno = XFS_FSB_TO_AGNO(mp, end_fsbno);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	error = xfs_trans_alloc_empty(mp, &tp);
12262306a36Sopenharmony_ci	if (error)
12362306a36Sopenharmony_ci		return error;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	for (; agno <= end_agno; agno++) {
12662306a36Sopenharmony_ci		struct xfs_rmap_irec	ri_low = { };
12762306a36Sopenharmony_ci		struct xfs_rmap_irec	ri_high;
12862306a36Sopenharmony_ci		struct xfs_agf		*agf;
12962306a36Sopenharmony_ci		struct xfs_perag	*pag;
13062306a36Sopenharmony_ci		xfs_agblock_t		range_agend;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci		pag = xfs_perag_get(mp, agno);
13362306a36Sopenharmony_ci		error = xfs_alloc_read_agf(pag, tp, 0, &agf_bp);
13462306a36Sopenharmony_ci		if (error) {
13562306a36Sopenharmony_ci			xfs_perag_put(pag);
13662306a36Sopenharmony_ci			break;
13762306a36Sopenharmony_ci		}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci		cur = xfs_rmapbt_init_cursor(mp, tp, agf_bp, pag);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci		/*
14262306a36Sopenharmony_ci		 * Set the rmap range from ri_low to ri_high, which represents
14362306a36Sopenharmony_ci		 * a [start, end] where we looking for the files or metadata.
14462306a36Sopenharmony_ci		 */
14562306a36Sopenharmony_ci		memset(&ri_high, 0xFF, sizeof(ri_high));
14662306a36Sopenharmony_ci		ri_low.rm_startblock = XFS_FSB_TO_AGBNO(mp, fsbno);
14762306a36Sopenharmony_ci		if (agno == end_agno)
14862306a36Sopenharmony_ci			ri_high.rm_startblock = XFS_FSB_TO_AGBNO(mp, end_fsbno);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci		agf = agf_bp->b_addr;
15162306a36Sopenharmony_ci		range_agend = min(be32_to_cpu(agf->agf_length) - 1,
15262306a36Sopenharmony_ci				ri_high.rm_startblock);
15362306a36Sopenharmony_ci		notify.startblock = ri_low.rm_startblock;
15462306a36Sopenharmony_ci		notify.blockcount = range_agend + 1 - ri_low.rm_startblock;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci		error = xfs_rmap_query_range(cur, &ri_low, &ri_high,
15762306a36Sopenharmony_ci				xfs_dax_failure_fn, &notify);
15862306a36Sopenharmony_ci		xfs_btree_del_cursor(cur, error);
15962306a36Sopenharmony_ci		xfs_trans_brelse(tp, agf_bp);
16062306a36Sopenharmony_ci		xfs_perag_put(pag);
16162306a36Sopenharmony_ci		if (error)
16262306a36Sopenharmony_ci			break;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci		fsbno = XFS_AGB_TO_FSB(mp, agno + 1, 0);
16562306a36Sopenharmony_ci	}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	xfs_trans_cancel(tp);
16862306a36Sopenharmony_ci	if (error || notify.want_shutdown) {
16962306a36Sopenharmony_ci		xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_ONDISK);
17062306a36Sopenharmony_ci		if (!error)
17162306a36Sopenharmony_ci			error = -EFSCORRUPTED;
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci	return error;
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic int
17762306a36Sopenharmony_cixfs_dax_notify_failure(
17862306a36Sopenharmony_ci	struct dax_device	*dax_dev,
17962306a36Sopenharmony_ci	u64			offset,
18062306a36Sopenharmony_ci	u64			len,
18162306a36Sopenharmony_ci	int			mf_flags)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	struct xfs_mount	*mp = dax_holder(dax_dev);
18462306a36Sopenharmony_ci	u64			ddev_start;
18562306a36Sopenharmony_ci	u64			ddev_end;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	if (!(mp->m_super->s_flags & SB_BORN)) {
18862306a36Sopenharmony_ci		xfs_warn(mp, "filesystem is not ready for notify_failure()!");
18962306a36Sopenharmony_ci		return -EIO;
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	if (mp->m_rtdev_targp && mp->m_rtdev_targp->bt_daxdev == dax_dev) {
19362306a36Sopenharmony_ci		xfs_debug(mp,
19462306a36Sopenharmony_ci			 "notify_failure() not supported on realtime device!");
19562306a36Sopenharmony_ci		return -EOPNOTSUPP;
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	if (mp->m_logdev_targp && mp->m_logdev_targp->bt_daxdev == dax_dev &&
19962306a36Sopenharmony_ci	    mp->m_logdev_targp != mp->m_ddev_targp) {
20062306a36Sopenharmony_ci		xfs_err(mp, "ondisk log corrupt, shutting down fs!");
20162306a36Sopenharmony_ci		xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_ONDISK);
20262306a36Sopenharmony_ci		return -EFSCORRUPTED;
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	if (!xfs_has_rmapbt(mp)) {
20662306a36Sopenharmony_ci		xfs_debug(mp, "notify_failure() needs rmapbt enabled!");
20762306a36Sopenharmony_ci		return -EOPNOTSUPP;
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	ddev_start = mp->m_ddev_targp->bt_dax_part_off;
21162306a36Sopenharmony_ci	ddev_end = ddev_start + bdev_nr_bytes(mp->m_ddev_targp->bt_bdev) - 1;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	/* Ignore the range out of filesystem area */
21462306a36Sopenharmony_ci	if (offset + len - 1 < ddev_start)
21562306a36Sopenharmony_ci		return -ENXIO;
21662306a36Sopenharmony_ci	if (offset > ddev_end)
21762306a36Sopenharmony_ci		return -ENXIO;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	/* Calculate the real range when it touches the boundary */
22062306a36Sopenharmony_ci	if (offset > ddev_start)
22162306a36Sopenharmony_ci		offset -= ddev_start;
22262306a36Sopenharmony_ci	else {
22362306a36Sopenharmony_ci		len -= ddev_start - offset;
22462306a36Sopenharmony_ci		offset = 0;
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci	if (offset + len - 1 > ddev_end)
22762306a36Sopenharmony_ci		len = ddev_end - offset + 1;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	return xfs_dax_notify_ddev_failure(mp, BTOBB(offset), BTOBB(len),
23062306a36Sopenharmony_ci			mf_flags);
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ciconst struct dax_holder_operations xfs_dax_holder_operations = {
23462306a36Sopenharmony_ci	.notify_failure		= xfs_dax_notify_failure,
23562306a36Sopenharmony_ci};
236