File Index
Files and directories
Sign in
FILE

xfs_btree_mem.c

File preview with syntax highlighting.

linux-6.19.14/fs/xfs/libxfs/xfs_btree_mem.c C
  1/* SPDX-License-Identifier: GPL-2.0 */
  2/*
  3 * Copyright (c) 2021-2024 Oracle.  All Rights Reserved.
  4 * Author: Darrick J. Wong <djwong@kernel.org>
  5 */
  6#include "xfs.h"
  7#include "xfs_fs.h"
  8#include "xfs_shared.h"
  9#include "xfs_format.h"
 10#include "xfs_log_format.h"
 11#include "xfs_trans_resv.h"
 12#include "xfs_mount.h"
 13#include "xfs_trans.h"
 14#include "xfs_btree.h"
 15#include "xfs_error.h"
 16#include "xfs_buf_mem.h"
 17#include "xfs_btree_mem.h"
 18#include "xfs_ag.h"
 19#include "xfs_buf_item.h"
 20#include "xfs_trace.h"
 21#include "xfs_rtgroup.h"
 22
 23/* Set the root of an in-memory btree. */
 24void
 25xfbtree_set_root(
 26	struct xfs_btree_cur		*cur,
 27	const union xfs_btree_ptr	*ptr,
 28	int				inc)
 29{
 30	ASSERT(cur->bc_ops->type == XFS_BTREE_TYPE_MEM);
 31
 32	cur->bc_mem.xfbtree->root = *ptr;
 33	cur->bc_mem.xfbtree->nlevels += inc;
 34}
 35
 36/* Initialize a pointer from the in-memory btree header. */
 37void
 38xfbtree_init_ptr_from_cur(
 39	struct xfs_btree_cur		*cur,
 40	union xfs_btree_ptr		*ptr)
 41{
 42	ASSERT(cur->bc_ops->type == XFS_BTREE_TYPE_MEM);
 43
 44	*ptr = cur->bc_mem.xfbtree->root;
 45}
 46
 47/* Duplicate an in-memory btree cursor. */
 48struct xfs_btree_cur *
 49xfbtree_dup_cursor(
 50	struct xfs_btree_cur		*cur)
 51{
 52	struct xfs_btree_cur		*ncur;
 53
 54	ASSERT(cur->bc_ops->type == XFS_BTREE_TYPE_MEM);
 55
 56	ncur = xfs_btree_alloc_cursor(cur->bc_mp, cur->bc_tp, cur->bc_ops,
 57			cur->bc_maxlevels, cur->bc_cache);
 58	ncur->bc_flags = cur->bc_flags;
 59	ncur->bc_nlevels = cur->bc_nlevels;
 60	ncur->bc_mem.xfbtree = cur->bc_mem.xfbtree;
 61	if (cur->bc_group)
 62		ncur->bc_group = xfs_group_hold(cur->bc_group);
 63	return ncur;
 64}
 65
 66/* Close the btree xfile and release all resources. */
 67void
 68xfbtree_destroy(
 69	struct xfbtree		*xfbt)
 70{
 71	xfs_buftarg_drain(xfbt->target);
 72}
 73
 74/* Compute the number of bytes available for records. */
 75static inline unsigned int
 76xfbtree_rec_bytes(
 77	struct xfs_mount		*mp,
 78	const struct xfs_btree_ops	*ops)
 79{
 80	return XMBUF_BLOCKSIZE - XFS_BTREE_LBLOCK_CRC_LEN;
 81}
 82
 83/* Initialize an empty leaf block as the btree root. */
 84STATIC int
 85xfbtree_init_leaf_block(
 86	struct xfs_mount		*mp,
 87	struct xfbtree			*xfbt,
 88	const struct xfs_btree_ops	*ops)
 89{
 90	struct xfs_buf			*bp;
 91	xfbno_t				bno = xfbt->highest_bno++;
 92	int				error;
 93
 94	error = xfs_buf_get(xfbt->target, xfbno_to_daddr(bno), XFBNO_BBSIZE,
 95			&bp);
 96	if (error)
 97		return error;
 98
 99	trace_xfbtree_create_root_buf(xfbt, bp);
100
101	bp->b_ops = ops->buf_ops;
102	xfs_btree_init_buf(mp, bp, ops, 0, 0, xfbt->owner);
103	xfs_buf_relse(bp);
104
105	xfbt->root.l = cpu_to_be64(bno);
106	return 0;
107}
108
109/*
110 * Create an in-memory btree root that can be used with the given xmbuf.
111 * Callers must set xfbt->owner.
112 */
113int
114xfbtree_init(
115	struct xfs_mount		*mp,
116	struct xfbtree			*xfbt,
117	struct xfs_buftarg		*btp,
118	const struct xfs_btree_ops	*ops)
119{
120	unsigned int			blocklen = xfbtree_rec_bytes(mp, ops);
121	unsigned int			keyptr_len;
122	int				error;
123
124	/* Requires a long-format CRC-format btree */
125	if (!xfs_has_crc(mp)) {
126		ASSERT(xfs_has_crc(mp));
127		return -EINVAL;
128	}
129	if (ops->ptr_len != XFS_BTREE_LONG_PTR_LEN) {
130		ASSERT(ops->ptr_len == XFS_BTREE_LONG_PTR_LEN);
131		return -EINVAL;
132	}
133
134	memset(xfbt, 0, sizeof(*xfbt));
135	xfbt->target = btp;
136
137	/* Set up min/maxrecs for this btree. */
138	keyptr_len = ops->key_len + sizeof(__be64);
139	xfbt->maxrecs[0] = blocklen / ops->rec_len;
140	xfbt->maxrecs[1] = blocklen / keyptr_len;
141	xfbt->minrecs[0] = xfbt->maxrecs[0] / 2;
142	xfbt->minrecs[1] = xfbt->maxrecs[1] / 2;
143	xfbt->highest_bno = 0;
144	xfbt->nlevels = 1;
145
146	/* Initialize the empty btree. */
147	error = xfbtree_init_leaf_block(mp, xfbt, ops);
148	if (error)
149		goto err_freesp;
150
151	trace_xfbtree_init(mp, xfbt, ops);
152
153	return 0;
154
155err_freesp:
156	xfs_buftarg_drain(xfbt->target);
157	return error;
158}
159
160/* Allocate a block to our in-memory btree. */
161int
162xfbtree_alloc_block(
163	struct xfs_btree_cur		*cur,
164	const union xfs_btree_ptr	*start,
165	union xfs_btree_ptr		*new,
166	int				*stat)
167{
168	struct xfbtree			*xfbt = cur->bc_mem.xfbtree;
169	xfbno_t				bno = xfbt->highest_bno++;
170
171	ASSERT(cur->bc_ops->type == XFS_BTREE_TYPE_MEM);
172
173	trace_xfbtree_alloc_block(xfbt, cur, bno);
174
175	/* Fail if the block address exceeds the maximum for the buftarg. */
176	if (!xfbtree_verify_bno(xfbt, bno)) {
177		ASSERT(xfbtree_verify_bno(xfbt, bno));
178		*stat = 0;
179		return 0;
180	}
181
182	new->l = cpu_to_be64(bno);
183	*stat = 1;
184	return 0;
185}
186
187/* Free a block from our in-memory btree. */
188int
189xfbtree_free_block(
190	struct xfs_btree_cur	*cur,
191	struct xfs_buf		*bp)
192{
193	struct xfbtree		*xfbt = cur->bc_mem.xfbtree;
194	xfs_daddr_t		daddr = xfs_buf_daddr(bp);
195	xfbno_t			bno = xfs_daddr_to_xfbno(daddr);
196
197	ASSERT(cur->bc_ops->type == XFS_BTREE_TYPE_MEM);
198
199	trace_xfbtree_free_block(xfbt, cur, bno);
200
201	if (bno + 1 == xfbt->highest_bno)
202		xfbt->highest_bno--;
203
204	return 0;
205}
206
207/* Return the minimum number of records for a btree block. */
208int
209xfbtree_get_minrecs(
210	struct xfs_btree_cur	*cur,
211	int			level)
212{
213	struct xfbtree		*xfbt = cur->bc_mem.xfbtree;
214
215	return xfbt->minrecs[level != 0];
216}
217
218/* Return the maximum number of records for a btree block. */
219int
220xfbtree_get_maxrecs(
221	struct xfs_btree_cur	*cur,
222	int			level)
223{
224	struct xfbtree		*xfbt = cur->bc_mem.xfbtree;
225
226	return xfbt->maxrecs[level != 0];
227}
228
229/* If this log item is a buffer item that came from the xfbtree, return it. */
230static inline struct xfs_buf *
231xfbtree_buf_match(
232	struct xfbtree			*xfbt,
233	const struct xfs_log_item	*lip)
234{
235	const struct xfs_buf_log_item	*bli;
236	struct xfs_buf			*bp;
237
238	if (lip->li_type != XFS_LI_BUF)
239		return NULL;
240
241	bli = container_of(lip, struct xfs_buf_log_item, bli_item);
242	bp = bli->bli_buf;
243	if (bp->b_target != xfbt->target)
244		return NULL;
245
246	return bp;
247}
248
249/*
250 * Commit changes to the incore btree immediately by writing all dirty xfbtree
251 * buffers to the backing xfile.  This detaches all xfbtree buffers from the
252 * transaction, even on failure.  The buffer locks are dropped between the
253 * delwri queue and submit, so the caller must synchronize btree access.
254 *
255 * Normally we'd let the buffers commit with the transaction and get written to
256 * the xfile via the log, but online repair stages ephemeral btrees in memory
257 * and uses the btree_staging functions to write new btrees to disk atomically.
258 * The in-memory btree (and its backing store) are discarded at the end of the
259 * repair phase, which means that xfbtree buffers cannot commit with the rest
260 * of a transaction.
261 *
262 * In other words, online repair only needs the transaction to collect buffer
263 * pointers and to avoid buffer deadlocks, not to guarantee consistency of
264 * updates.
265 */
266int
267xfbtree_trans_commit(
268	struct xfbtree		*xfbt,
269	struct xfs_trans	*tp)
270{
271	struct xfs_log_item	*lip, *n;
272	bool			tp_dirty = false;
273	int			error = 0;
274
275	/*
276	 * For each xfbtree buffer attached to the transaction, write the dirty
277	 * buffers to the xfile and release them.
278	 */
279	list_for_each_entry_safe(lip, n, &tp->t_items, li_trans) {
280		struct xfs_buf	*bp = xfbtree_buf_match(xfbt, lip);
281
282		if (!bp) {
283			if (test_bit(XFS_LI_DIRTY, &lip->li_flags))
284				tp_dirty |= true;
285			continue;
286		}
287
288		trace_xfbtree_trans_commit_buf(xfbt, bp);
289
290		xmbuf_trans_bdetach(tp, bp);
291
292		/*
293		 * If the buffer fails verification, note the failure but
294		 * continue walking the transaction items so that we remove all
295		 * ephemeral btree buffers.
296		 */
297		if (!error)
298			error = xmbuf_finalize(bp);
299
300		xfs_buf_relse(bp);
301	}
302
303	/*
304	 * Reset the transaction's dirty flag to reflect the dirty state of the
305	 * log items that are still attached.
306	 */
307	tp->t_flags = (tp->t_flags & ~XFS_TRANS_DIRTY) |
308			(tp_dirty ? XFS_TRANS_DIRTY : 0);
309
310	return error;
311}
312
313/*
314 * Cancel changes to the incore btree by detaching all the xfbtree buffers.
315 * Changes are not undone, so callers must not access the btree ever again.
316 */
317void
318xfbtree_trans_cancel(
319	struct xfbtree		*xfbt,
320	struct xfs_trans	*tp)
321{
322	struct xfs_log_item	*lip, *n;
323	bool			tp_dirty = false;
324
325	list_for_each_entry_safe(lip, n, &tp->t_items, li_trans) {
326		struct xfs_buf	*bp = xfbtree_buf_match(xfbt, lip);
327
328		if (!bp) {
329			if (test_bit(XFS_LI_DIRTY, &lip->li_flags))
330				tp_dirty |= true;
331			continue;
332		}
333
334		trace_xfbtree_trans_cancel_buf(xfbt, bp);
335
336		xmbuf_trans_bdetach(tp, bp);
337		xfs_buf_relse(bp);
338	}
339
340	/*
341	 * Reset the transaction's dirty flag to reflect the dirty state of the
342	 * log items that are still attached.
343	 */
344	tp->t_flags = (tp->t_flags & ~XFS_TRANS_DIRTY) |
345			(tp_dirty ? XFS_TRANS_DIRTY : 0);
346}
~/ Read-only file preview.