Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.17.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Copyright (c) 2022 Fujitsu.  All Rights Reserved.
  4 */
  5
  6#include "xfs.h"
  7#include "xfs_shared.h"
  8#include "xfs_format.h"
  9#include "xfs_log_format.h"
 10#include "xfs_trans_resv.h"
 11#include "xfs_mount.h"
 12#include "xfs_alloc.h"
 13#include "xfs_bit.h"
 14#include "xfs_btree.h"
 15#include "xfs_inode.h"
 16#include "xfs_icache.h"
 17#include "xfs_rmap.h"
 18#include "xfs_rmap_btree.h"
 19#include "xfs_rtalloc.h"
 20#include "xfs_trans.h"
 21#include "xfs_ag.h"
 22
 23#include <linux/mm.h>
 24#include <linux/dax.h>
 25
 26struct xfs_failure_info {
 27	xfs_agblock_t		startblock;
 28	xfs_extlen_t		blockcount;
 29	int			mf_flags;
 30	bool			want_shutdown;
 31};
 32
 33static pgoff_t
 34xfs_failure_pgoff(
 35	struct xfs_mount		*mp,
 36	const struct xfs_rmap_irec	*rec,
 37	const struct xfs_failure_info	*notify)
 38{
 39	loff_t				pos = XFS_FSB_TO_B(mp, rec->rm_offset);
 40
 41	if (notify->startblock > rec->rm_startblock)
 42		pos += XFS_FSB_TO_B(mp,
 43				notify->startblock - rec->rm_startblock);
 44	return pos >> PAGE_SHIFT;
 45}
 46
 47static unsigned long
 48xfs_failure_pgcnt(
 49	struct xfs_mount		*mp,
 50	const struct xfs_rmap_irec	*rec,
 51	const struct xfs_failure_info	*notify)
 52{
 53	xfs_agblock_t			end_rec;
 54	xfs_agblock_t			end_notify;
 55	xfs_agblock_t			start_cross;
 56	xfs_agblock_t			end_cross;
 57
 58	start_cross = max(rec->rm_startblock, notify->startblock);
 59
 60	end_rec = rec->rm_startblock + rec->rm_blockcount;
 61	end_notify = notify->startblock + notify->blockcount;
 62	end_cross = min(end_rec, end_notify);
 63
 64	return XFS_FSB_TO_B(mp, end_cross - start_cross) >> PAGE_SHIFT;
 65}
 66
 67static int
 68xfs_dax_failure_fn(
 69	struct xfs_btree_cur		*cur,
 70	const struct xfs_rmap_irec	*rec,
 71	void				*data)
 72{
 73	struct xfs_mount		*mp = cur->bc_mp;
 74	struct xfs_inode		*ip;
 75	struct xfs_failure_info		*notify = data;
 76	int				error = 0;
 77
 78	if (XFS_RMAP_NON_INODE_OWNER(rec->rm_owner) ||
 79	    (rec->rm_flags & (XFS_RMAP_ATTR_FORK | XFS_RMAP_BMBT_BLOCK))) {
 80		notify->want_shutdown = true;
 81		return 0;
 82	}
 83
 84	/* Get files that incore, filter out others that are not in use. */
 85	error = xfs_iget(mp, cur->bc_tp, rec->rm_owner, XFS_IGET_INCORE,
 86			 0, &ip);
 87	/* Continue the rmap query if the inode isn't incore */
 88	if (error == -ENODATA)
 89		return 0;
 90	if (error) {
 91		notify->want_shutdown = true;
 92		return 0;
 93	}
 94
 95	error = mf_dax_kill_procs(VFS_I(ip)->i_mapping,
 96				  xfs_failure_pgoff(mp, rec, notify),
 97				  xfs_failure_pgcnt(mp, rec, notify),
 98				  notify->mf_flags);
 99	xfs_irele(ip);
100	return error;
101}
102
103static int
104xfs_dax_notify_ddev_failure(
105	struct xfs_mount	*mp,
106	xfs_daddr_t		daddr,
107	xfs_daddr_t		bblen,
108	int			mf_flags)
109{
110	struct xfs_failure_info	notify = { .mf_flags = mf_flags };
111	struct xfs_trans	*tp = NULL;
112	struct xfs_btree_cur	*cur = NULL;
113	struct xfs_buf		*agf_bp = NULL;
114	int			error = 0;
115	xfs_fsblock_t		fsbno = XFS_DADDR_TO_FSB(mp, daddr);
116	xfs_agnumber_t		agno = XFS_FSB_TO_AGNO(mp, fsbno);
117	xfs_fsblock_t		end_fsbno = XFS_DADDR_TO_FSB(mp, daddr + bblen);
118	xfs_agnumber_t		end_agno = XFS_FSB_TO_AGNO(mp, end_fsbno);
119
120	error = xfs_trans_alloc_empty(mp, &tp);
121	if (error)
122		return error;
123
124	for (; agno <= end_agno; agno++) {
125		struct xfs_rmap_irec	ri_low = { };
126		struct xfs_rmap_irec	ri_high;
127		struct xfs_agf		*agf;
128		xfs_agblock_t		agend;
129		struct xfs_perag	*pag;
130
131		pag = xfs_perag_get(mp, agno);
132		error = xfs_alloc_read_agf(pag, tp, 0, &agf_bp);
133		if (error) {
134			xfs_perag_put(pag);
135			break;
136		}
137
138		cur = xfs_rmapbt_init_cursor(mp, tp, agf_bp, pag);
139
140		/*
141		 * Set the rmap range from ri_low to ri_high, which represents
142		 * a [start, end] where we looking for the files or metadata.
143		 */
144		memset(&ri_high, 0xFF, sizeof(ri_high));
145		ri_low.rm_startblock = XFS_FSB_TO_AGBNO(mp, fsbno);
146		if (agno == end_agno)
147			ri_high.rm_startblock = XFS_FSB_TO_AGBNO(mp, end_fsbno);
148
149		agf = agf_bp->b_addr;
150		agend = min(be32_to_cpu(agf->agf_length),
151				ri_high.rm_startblock);
152		notify.startblock = ri_low.rm_startblock;
153		notify.blockcount = agend - ri_low.rm_startblock;
154
155		error = xfs_rmap_query_range(cur, &ri_low, &ri_high,
156				xfs_dax_failure_fn, &notify);
157		xfs_btree_del_cursor(cur, error);
158		xfs_trans_brelse(tp, agf_bp);
159		xfs_perag_put(pag);
160		if (error)
161			break;
162
163		fsbno = XFS_AGB_TO_FSB(mp, agno + 1, 0);
164	}
165
166	xfs_trans_cancel(tp);
167	if (error || notify.want_shutdown) {
168		xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_ONDISK);
169		if (!error)
170			error = -EFSCORRUPTED;
171	}
172	return error;
173}
174
175static int
176xfs_dax_notify_failure(
177	struct dax_device	*dax_dev,
178	u64			offset,
179	u64			len,
180	int			mf_flags)
181{
182	struct xfs_mount	*mp = dax_holder(dax_dev);
183	u64			ddev_start;
184	u64			ddev_end;
185
186	if (!(mp->m_super->s_flags & SB_BORN)) {
187		xfs_warn(mp, "filesystem is not ready for notify_failure()!");
188		return -EIO;
189	}
190
191	if (mp->m_rtdev_targp && mp->m_rtdev_targp->bt_daxdev == dax_dev) {
192		xfs_debug(mp,
193			 "notify_failure() not supported on realtime device!");
194		return -EOPNOTSUPP;
195	}
196
197	if (mp->m_logdev_targp && mp->m_logdev_targp->bt_daxdev == dax_dev &&
198	    mp->m_logdev_targp != mp->m_ddev_targp) {
199		xfs_err(mp, "ondisk log corrupt, shutting down fs!");
200		xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_ONDISK);
201		return -EFSCORRUPTED;
202	}
203
204	if (!xfs_has_rmapbt(mp)) {
205		xfs_debug(mp, "notify_failure() needs rmapbt enabled!");
206		return -EOPNOTSUPP;
207	}
208
209	ddev_start = mp->m_ddev_targp->bt_dax_part_off;
210	ddev_end = ddev_start + bdev_nr_bytes(mp->m_ddev_targp->bt_bdev) - 1;
211
212	/* Ignore the range out of filesystem area */
213	if (offset + len < ddev_start)
214		return -ENXIO;
215	if (offset > ddev_end)
216		return -ENXIO;
217
218	/* Calculate the real range when it touches the boundary */
219	if (offset > ddev_start)
220		offset -= ddev_start;
221	else {
222		len -= ddev_start - offset;
223		offset = 0;
224	}
225	if (offset + len > ddev_end)
226		len -= ddev_end - offset;
227
228	return xfs_dax_notify_ddev_failure(mp, BTOBB(offset), BTOBB(len),
229			mf_flags);
230}
231
232const struct dax_holder_operations xfs_dax_holder_operations = {
233	.notify_failure		= xfs_dax_notify_failure,
234};