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, ¬ify); 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