18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2016 Oracle.  All Rights Reserved.
48c2ecf20Sopenharmony_ci * Author: Darrick J. Wong <darrick.wong@oracle.com>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci#include "xfs.h"
78c2ecf20Sopenharmony_ci#include "xfs_fs.h"
88c2ecf20Sopenharmony_ci#include "xfs_shared.h"
98c2ecf20Sopenharmony_ci#include "xfs_format.h"
108c2ecf20Sopenharmony_ci#include "xfs_log_format.h"
118c2ecf20Sopenharmony_ci#include "xfs_trans_resv.h"
128c2ecf20Sopenharmony_ci#include "xfs_mount.h"
138c2ecf20Sopenharmony_ci#include "xfs_defer.h"
148c2ecf20Sopenharmony_ci#include "xfs_trans.h"
158c2ecf20Sopenharmony_ci#include "xfs_buf_item.h"
168c2ecf20Sopenharmony_ci#include "xfs_inode.h"
178c2ecf20Sopenharmony_ci#include "xfs_inode_item.h"
188c2ecf20Sopenharmony_ci#include "xfs_trace.h"
198c2ecf20Sopenharmony_ci#include "xfs_icache.h"
208c2ecf20Sopenharmony_ci#include "xfs_log.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/*
238c2ecf20Sopenharmony_ci * Deferred Operations in XFS
248c2ecf20Sopenharmony_ci *
258c2ecf20Sopenharmony_ci * Due to the way locking rules work in XFS, certain transactions (block
268c2ecf20Sopenharmony_ci * mapping and unmapping, typically) have permanent reservations so that
278c2ecf20Sopenharmony_ci * we can roll the transaction to adhere to AG locking order rules and
288c2ecf20Sopenharmony_ci * to unlock buffers between metadata updates.  Prior to rmap/reflink,
298c2ecf20Sopenharmony_ci * the mapping code had a mechanism to perform these deferrals for
308c2ecf20Sopenharmony_ci * extents that were going to be freed; this code makes that facility
318c2ecf20Sopenharmony_ci * more generic.
328c2ecf20Sopenharmony_ci *
338c2ecf20Sopenharmony_ci * When adding the reverse mapping and reflink features, it became
348c2ecf20Sopenharmony_ci * necessary to perform complex remapping multi-transactions to comply
358c2ecf20Sopenharmony_ci * with AG locking order rules, and to be able to spread a single
368c2ecf20Sopenharmony_ci * refcount update operation (an operation on an n-block extent can
378c2ecf20Sopenharmony_ci * update as many as n records!) among multiple transactions.  XFS can
388c2ecf20Sopenharmony_ci * roll a transaction to facilitate this, but using this facility
398c2ecf20Sopenharmony_ci * requires us to log "intent" items in case log recovery needs to
408c2ecf20Sopenharmony_ci * redo the operation, and to log "done" items to indicate that redo
418c2ecf20Sopenharmony_ci * is not necessary.
428c2ecf20Sopenharmony_ci *
438c2ecf20Sopenharmony_ci * Deferred work is tracked in xfs_defer_pending items.  Each pending
448c2ecf20Sopenharmony_ci * item tracks one type of deferred work.  Incoming work items (which
458c2ecf20Sopenharmony_ci * have not yet had an intent logged) are attached to a pending item
468c2ecf20Sopenharmony_ci * on the dop_intake list, where they wait for the caller to finish
478c2ecf20Sopenharmony_ci * the deferred operations.
488c2ecf20Sopenharmony_ci *
498c2ecf20Sopenharmony_ci * Finishing a set of deferred operations is an involved process.  To
508c2ecf20Sopenharmony_ci * start, we define "rolling a deferred-op transaction" as follows:
518c2ecf20Sopenharmony_ci *
528c2ecf20Sopenharmony_ci * > For each xfs_defer_pending item on the dop_intake list,
538c2ecf20Sopenharmony_ci *   - Sort the work items in AG order.  XFS locking
548c2ecf20Sopenharmony_ci *     order rules require us to lock buffers in AG order.
558c2ecf20Sopenharmony_ci *   - Create a log intent item for that type.
568c2ecf20Sopenharmony_ci *   - Attach it to the pending item.
578c2ecf20Sopenharmony_ci *   - Move the pending item from the dop_intake list to the
588c2ecf20Sopenharmony_ci *     dop_pending list.
598c2ecf20Sopenharmony_ci * > Roll the transaction.
608c2ecf20Sopenharmony_ci *
618c2ecf20Sopenharmony_ci * NOTE: To avoid exceeding the transaction reservation, we limit the
628c2ecf20Sopenharmony_ci * number of items that we attach to a given xfs_defer_pending.
638c2ecf20Sopenharmony_ci *
648c2ecf20Sopenharmony_ci * The actual finishing process looks like this:
658c2ecf20Sopenharmony_ci *
668c2ecf20Sopenharmony_ci * > For each xfs_defer_pending in the dop_pending list,
678c2ecf20Sopenharmony_ci *   - Roll the deferred-op transaction as above.
688c2ecf20Sopenharmony_ci *   - Create a log done item for that type, and attach it to the
698c2ecf20Sopenharmony_ci *     log intent item.
708c2ecf20Sopenharmony_ci *   - For each work item attached to the log intent item,
718c2ecf20Sopenharmony_ci *     * Perform the described action.
728c2ecf20Sopenharmony_ci *     * Attach the work item to the log done item.
738c2ecf20Sopenharmony_ci *     * If the result of doing the work was -EAGAIN, ->finish work
748c2ecf20Sopenharmony_ci *       wants a new transaction.  See the "Requesting a Fresh
758c2ecf20Sopenharmony_ci *       Transaction while Finishing Deferred Work" section below for
768c2ecf20Sopenharmony_ci *       details.
778c2ecf20Sopenharmony_ci *
788c2ecf20Sopenharmony_ci * The key here is that we must log an intent item for all pending
798c2ecf20Sopenharmony_ci * work items every time we roll the transaction, and that we must log
808c2ecf20Sopenharmony_ci * a done item as soon as the work is completed.  With this mechanism
818c2ecf20Sopenharmony_ci * we can perform complex remapping operations, chaining intent items
828c2ecf20Sopenharmony_ci * as needed.
838c2ecf20Sopenharmony_ci *
848c2ecf20Sopenharmony_ci * Requesting a Fresh Transaction while Finishing Deferred Work
858c2ecf20Sopenharmony_ci *
868c2ecf20Sopenharmony_ci * If ->finish_item decides that it needs a fresh transaction to
878c2ecf20Sopenharmony_ci * finish the work, it must ask its caller (xfs_defer_finish) for a
888c2ecf20Sopenharmony_ci * continuation.  The most likely cause of this circumstance are the
898c2ecf20Sopenharmony_ci * refcount adjust functions deciding that they've logged enough items
908c2ecf20Sopenharmony_ci * to be at risk of exceeding the transaction reservation.
918c2ecf20Sopenharmony_ci *
928c2ecf20Sopenharmony_ci * To get a fresh transaction, we want to log the existing log done
938c2ecf20Sopenharmony_ci * item to prevent the log intent item from replaying, immediately log
948c2ecf20Sopenharmony_ci * a new log intent item with the unfinished work items, roll the
958c2ecf20Sopenharmony_ci * transaction, and re-call ->finish_item wherever it left off.  The
968c2ecf20Sopenharmony_ci * log done item and the new log intent item must be in the same
978c2ecf20Sopenharmony_ci * transaction or atomicity cannot be guaranteed; defer_finish ensures
988c2ecf20Sopenharmony_ci * that this happens.
998c2ecf20Sopenharmony_ci *
1008c2ecf20Sopenharmony_ci * This requires some coordination between ->finish_item and
1018c2ecf20Sopenharmony_ci * defer_finish.  Upon deciding to request a new transaction,
1028c2ecf20Sopenharmony_ci * ->finish_item should update the current work item to reflect the
1038c2ecf20Sopenharmony_ci * unfinished work.  Next, it should reset the log done item's list
1048c2ecf20Sopenharmony_ci * count to the number of items finished, and return -EAGAIN.
1058c2ecf20Sopenharmony_ci * defer_finish sees the -EAGAIN, logs the new log intent item
1068c2ecf20Sopenharmony_ci * with the remaining work items, and leaves the xfs_defer_pending
1078c2ecf20Sopenharmony_ci * item at the head of the dop_work queue.  Then it rolls the
1088c2ecf20Sopenharmony_ci * transaction and picks up processing where it left off.  It is
1098c2ecf20Sopenharmony_ci * required that ->finish_item must be careful to leave enough
1108c2ecf20Sopenharmony_ci * transaction reservation to fit the new log intent item.
1118c2ecf20Sopenharmony_ci *
1128c2ecf20Sopenharmony_ci * This is an example of remapping the extent (E, E+B) into file X at
1138c2ecf20Sopenharmony_ci * offset A and dealing with the extent (C, C+B) already being mapped
1148c2ecf20Sopenharmony_ci * there:
1158c2ecf20Sopenharmony_ci * +-------------------------------------------------+
1168c2ecf20Sopenharmony_ci * | Unmap file X startblock C offset A length B     | t0
1178c2ecf20Sopenharmony_ci * | Intent to reduce refcount for extent (C, B)     |
1188c2ecf20Sopenharmony_ci * | Intent to remove rmap (X, C, A, B)              |
1198c2ecf20Sopenharmony_ci * | Intent to free extent (D, 1) (bmbt block)       |
1208c2ecf20Sopenharmony_ci * | Intent to map (X, A, B) at startblock E         |
1218c2ecf20Sopenharmony_ci * +-------------------------------------------------+
1228c2ecf20Sopenharmony_ci * | Map file X startblock E offset A length B       | t1
1238c2ecf20Sopenharmony_ci * | Done mapping (X, E, A, B)                       |
1248c2ecf20Sopenharmony_ci * | Intent to increase refcount for extent (E, B)   |
1258c2ecf20Sopenharmony_ci * | Intent to add rmap (X, E, A, B)                 |
1268c2ecf20Sopenharmony_ci * +-------------------------------------------------+
1278c2ecf20Sopenharmony_ci * | Reduce refcount for extent (C, B)               | t2
1288c2ecf20Sopenharmony_ci * | Done reducing refcount for extent (C, 9)        |
1298c2ecf20Sopenharmony_ci * | Intent to reduce refcount for extent (C+9, B-9) |
1308c2ecf20Sopenharmony_ci * | (ran out of space after 9 refcount updates)     |
1318c2ecf20Sopenharmony_ci * +-------------------------------------------------+
1328c2ecf20Sopenharmony_ci * | Reduce refcount for extent (C+9, B+9)           | t3
1338c2ecf20Sopenharmony_ci * | Done reducing refcount for extent (C+9, B-9)    |
1348c2ecf20Sopenharmony_ci * | Increase refcount for extent (E, B)             |
1358c2ecf20Sopenharmony_ci * | Done increasing refcount for extent (E, B)      |
1368c2ecf20Sopenharmony_ci * | Intent to free extent (C, B)                    |
1378c2ecf20Sopenharmony_ci * | Intent to free extent (F, 1) (refcountbt block) |
1388c2ecf20Sopenharmony_ci * | Intent to remove rmap (F, 1, REFC)              |
1398c2ecf20Sopenharmony_ci * +-------------------------------------------------+
1408c2ecf20Sopenharmony_ci * | Remove rmap (X, C, A, B)                        | t4
1418c2ecf20Sopenharmony_ci * | Done removing rmap (X, C, A, B)                 |
1428c2ecf20Sopenharmony_ci * | Add rmap (X, E, A, B)                           |
1438c2ecf20Sopenharmony_ci * | Done adding rmap (X, E, A, B)                   |
1448c2ecf20Sopenharmony_ci * | Remove rmap (F, 1, REFC)                        |
1458c2ecf20Sopenharmony_ci * | Done removing rmap (F, 1, REFC)                 |
1468c2ecf20Sopenharmony_ci * +-------------------------------------------------+
1478c2ecf20Sopenharmony_ci * | Free extent (C, B)                              | t5
1488c2ecf20Sopenharmony_ci * | Done freeing extent (C, B)                      |
1498c2ecf20Sopenharmony_ci * | Free extent (D, 1)                              |
1508c2ecf20Sopenharmony_ci * | Done freeing extent (D, 1)                      |
1518c2ecf20Sopenharmony_ci * | Free extent (F, 1)                              |
1528c2ecf20Sopenharmony_ci * | Done freeing extent (F, 1)                      |
1538c2ecf20Sopenharmony_ci * +-------------------------------------------------+
1548c2ecf20Sopenharmony_ci *
1558c2ecf20Sopenharmony_ci * If we should crash before t2 commits, log recovery replays
1568c2ecf20Sopenharmony_ci * the following intent items:
1578c2ecf20Sopenharmony_ci *
1588c2ecf20Sopenharmony_ci * - Intent to reduce refcount for extent (C, B)
1598c2ecf20Sopenharmony_ci * - Intent to remove rmap (X, C, A, B)
1608c2ecf20Sopenharmony_ci * - Intent to free extent (D, 1) (bmbt block)
1618c2ecf20Sopenharmony_ci * - Intent to increase refcount for extent (E, B)
1628c2ecf20Sopenharmony_ci * - Intent to add rmap (X, E, A, B)
1638c2ecf20Sopenharmony_ci *
1648c2ecf20Sopenharmony_ci * In the process of recovering, it should also generate and take care
1658c2ecf20Sopenharmony_ci * of these intent items:
1668c2ecf20Sopenharmony_ci *
1678c2ecf20Sopenharmony_ci * - Intent to free extent (C, B)
1688c2ecf20Sopenharmony_ci * - Intent to free extent (F, 1) (refcountbt block)
1698c2ecf20Sopenharmony_ci * - Intent to remove rmap (F, 1, REFC)
1708c2ecf20Sopenharmony_ci *
1718c2ecf20Sopenharmony_ci * Note that the continuation requested between t2 and t3 is likely to
1728c2ecf20Sopenharmony_ci * reoccur.
1738c2ecf20Sopenharmony_ci */
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic const struct xfs_defer_op_type *defer_op_types[] = {
1768c2ecf20Sopenharmony_ci	[XFS_DEFER_OPS_TYPE_BMAP]	= &xfs_bmap_update_defer_type,
1778c2ecf20Sopenharmony_ci	[XFS_DEFER_OPS_TYPE_REFCOUNT]	= &xfs_refcount_update_defer_type,
1788c2ecf20Sopenharmony_ci	[XFS_DEFER_OPS_TYPE_RMAP]	= &xfs_rmap_update_defer_type,
1798c2ecf20Sopenharmony_ci	[XFS_DEFER_OPS_TYPE_FREE]	= &xfs_extent_free_defer_type,
1808c2ecf20Sopenharmony_ci	[XFS_DEFER_OPS_TYPE_AGFL_FREE]	= &xfs_agfl_free_defer_type,
1818c2ecf20Sopenharmony_ci};
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic void
1848c2ecf20Sopenharmony_cixfs_defer_create_intent(
1858c2ecf20Sopenharmony_ci	struct xfs_trans		*tp,
1868c2ecf20Sopenharmony_ci	struct xfs_defer_pending	*dfp,
1878c2ecf20Sopenharmony_ci	bool				sort)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	const struct xfs_defer_op_type	*ops = defer_op_types[dfp->dfp_type];
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	if (!dfp->dfp_intent)
1928c2ecf20Sopenharmony_ci		dfp->dfp_intent = ops->create_intent(tp, &dfp->dfp_work,
1938c2ecf20Sopenharmony_ci						     dfp->dfp_count, sort);
1948c2ecf20Sopenharmony_ci}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci/*
1978c2ecf20Sopenharmony_ci * For each pending item in the intake list, log its intent item and the
1988c2ecf20Sopenharmony_ci * associated extents, then add the entire intake list to the end of
1998c2ecf20Sopenharmony_ci * the pending list.
2008c2ecf20Sopenharmony_ci */
2018c2ecf20Sopenharmony_ciSTATIC void
2028c2ecf20Sopenharmony_cixfs_defer_create_intents(
2038c2ecf20Sopenharmony_ci	struct xfs_trans		*tp)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	struct xfs_defer_pending	*dfp;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	list_for_each_entry(dfp, &tp->t_dfops, dfp_list) {
2088c2ecf20Sopenharmony_ci		trace_xfs_defer_create_intent(tp->t_mountp, dfp);
2098c2ecf20Sopenharmony_ci		xfs_defer_create_intent(tp, dfp, true);
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci/* Abort all the intents that were committed. */
2148c2ecf20Sopenharmony_ciSTATIC void
2158c2ecf20Sopenharmony_cixfs_defer_trans_abort(
2168c2ecf20Sopenharmony_ci	struct xfs_trans		*tp,
2178c2ecf20Sopenharmony_ci	struct list_head		*dop_pending)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	struct xfs_defer_pending	*dfp;
2208c2ecf20Sopenharmony_ci	const struct xfs_defer_op_type	*ops;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	trace_xfs_defer_trans_abort(tp, _RET_IP_);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	/* Abort intent items that don't have a done item. */
2258c2ecf20Sopenharmony_ci	list_for_each_entry(dfp, dop_pending, dfp_list) {
2268c2ecf20Sopenharmony_ci		ops = defer_op_types[dfp->dfp_type];
2278c2ecf20Sopenharmony_ci		trace_xfs_defer_pending_abort(tp->t_mountp, dfp);
2288c2ecf20Sopenharmony_ci		if (dfp->dfp_intent && !dfp->dfp_done) {
2298c2ecf20Sopenharmony_ci			ops->abort_intent(dfp->dfp_intent);
2308c2ecf20Sopenharmony_ci			dfp->dfp_intent = NULL;
2318c2ecf20Sopenharmony_ci		}
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci/* Roll a transaction so we can do some deferred op processing. */
2368c2ecf20Sopenharmony_ciSTATIC int
2378c2ecf20Sopenharmony_cixfs_defer_trans_roll(
2388c2ecf20Sopenharmony_ci	struct xfs_trans		**tpp)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	struct xfs_trans		*tp = *tpp;
2418c2ecf20Sopenharmony_ci	struct xfs_buf_log_item		*bli;
2428c2ecf20Sopenharmony_ci	struct xfs_inode_log_item	*ili;
2438c2ecf20Sopenharmony_ci	struct xfs_log_item		*lip;
2448c2ecf20Sopenharmony_ci	struct xfs_buf			*bplist[XFS_DEFER_OPS_NR_BUFS];
2458c2ecf20Sopenharmony_ci	struct xfs_inode		*iplist[XFS_DEFER_OPS_NR_INODES];
2468c2ecf20Sopenharmony_ci	unsigned int			ordered = 0; /* bitmap */
2478c2ecf20Sopenharmony_ci	int				bpcount = 0, ipcount = 0;
2488c2ecf20Sopenharmony_ci	int				i;
2498c2ecf20Sopenharmony_ci	int				error;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	BUILD_BUG_ON(NBBY * sizeof(ordered) < XFS_DEFER_OPS_NR_BUFS);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	list_for_each_entry(lip, &tp->t_items, li_trans) {
2548c2ecf20Sopenharmony_ci		switch (lip->li_type) {
2558c2ecf20Sopenharmony_ci		case XFS_LI_BUF:
2568c2ecf20Sopenharmony_ci			bli = container_of(lip, struct xfs_buf_log_item,
2578c2ecf20Sopenharmony_ci					   bli_item);
2588c2ecf20Sopenharmony_ci			if (bli->bli_flags & XFS_BLI_HOLD) {
2598c2ecf20Sopenharmony_ci				if (bpcount >= XFS_DEFER_OPS_NR_BUFS) {
2608c2ecf20Sopenharmony_ci					ASSERT(0);
2618c2ecf20Sopenharmony_ci					return -EFSCORRUPTED;
2628c2ecf20Sopenharmony_ci				}
2638c2ecf20Sopenharmony_ci				if (bli->bli_flags & XFS_BLI_ORDERED)
2648c2ecf20Sopenharmony_ci					ordered |= (1U << bpcount);
2658c2ecf20Sopenharmony_ci				else
2668c2ecf20Sopenharmony_ci					xfs_trans_dirty_buf(tp, bli->bli_buf);
2678c2ecf20Sopenharmony_ci				bplist[bpcount++] = bli->bli_buf;
2688c2ecf20Sopenharmony_ci			}
2698c2ecf20Sopenharmony_ci			break;
2708c2ecf20Sopenharmony_ci		case XFS_LI_INODE:
2718c2ecf20Sopenharmony_ci			ili = container_of(lip, struct xfs_inode_log_item,
2728c2ecf20Sopenharmony_ci					   ili_item);
2738c2ecf20Sopenharmony_ci			if (ili->ili_lock_flags == 0) {
2748c2ecf20Sopenharmony_ci				if (ipcount >= XFS_DEFER_OPS_NR_INODES) {
2758c2ecf20Sopenharmony_ci					ASSERT(0);
2768c2ecf20Sopenharmony_ci					return -EFSCORRUPTED;
2778c2ecf20Sopenharmony_ci				}
2788c2ecf20Sopenharmony_ci				xfs_trans_log_inode(tp, ili->ili_inode,
2798c2ecf20Sopenharmony_ci						    XFS_ILOG_CORE);
2808c2ecf20Sopenharmony_ci				iplist[ipcount++] = ili->ili_inode;
2818c2ecf20Sopenharmony_ci			}
2828c2ecf20Sopenharmony_ci			break;
2838c2ecf20Sopenharmony_ci		default:
2848c2ecf20Sopenharmony_ci			break;
2858c2ecf20Sopenharmony_ci		}
2868c2ecf20Sopenharmony_ci	}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	trace_xfs_defer_trans_roll(tp, _RET_IP_);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	/*
2918c2ecf20Sopenharmony_ci	 * Roll the transaction.  Rolling always given a new transaction (even
2928c2ecf20Sopenharmony_ci	 * if committing the old one fails!) to hand back to the caller, so we
2938c2ecf20Sopenharmony_ci	 * join the held resources to the new transaction so that we always
2948c2ecf20Sopenharmony_ci	 * return with the held resources joined to @tpp, no matter what
2958c2ecf20Sopenharmony_ci	 * happened.
2968c2ecf20Sopenharmony_ci	 */
2978c2ecf20Sopenharmony_ci	error = xfs_trans_roll(tpp);
2988c2ecf20Sopenharmony_ci	tp = *tpp;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	/* Rejoin the joined inodes. */
3018c2ecf20Sopenharmony_ci	for (i = 0; i < ipcount; i++)
3028c2ecf20Sopenharmony_ci		xfs_trans_ijoin(tp, iplist[i], 0);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	/* Rejoin the buffers and dirty them so the log moves forward. */
3058c2ecf20Sopenharmony_ci	for (i = 0; i < bpcount; i++) {
3068c2ecf20Sopenharmony_ci		xfs_trans_bjoin(tp, bplist[i]);
3078c2ecf20Sopenharmony_ci		if (ordered & (1U << i))
3088c2ecf20Sopenharmony_ci			xfs_trans_ordered_buf(tp, bplist[i]);
3098c2ecf20Sopenharmony_ci		xfs_trans_bhold(tp, bplist[i]);
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	if (error)
3138c2ecf20Sopenharmony_ci		trace_xfs_defer_trans_roll_error(tp, error);
3148c2ecf20Sopenharmony_ci	return error;
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci/*
3188c2ecf20Sopenharmony_ci * Free up any items left in the list.
3198c2ecf20Sopenharmony_ci */
3208c2ecf20Sopenharmony_cistatic void
3218c2ecf20Sopenharmony_cixfs_defer_cancel_list(
3228c2ecf20Sopenharmony_ci	struct xfs_mount		*mp,
3238c2ecf20Sopenharmony_ci	struct list_head		*dop_list)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	struct xfs_defer_pending	*dfp;
3268c2ecf20Sopenharmony_ci	struct xfs_defer_pending	*pli;
3278c2ecf20Sopenharmony_ci	struct list_head		*pwi;
3288c2ecf20Sopenharmony_ci	struct list_head		*n;
3298c2ecf20Sopenharmony_ci	const struct xfs_defer_op_type	*ops;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	/*
3328c2ecf20Sopenharmony_ci	 * Free the pending items.  Caller should already have arranged
3338c2ecf20Sopenharmony_ci	 * for the intent items to be released.
3348c2ecf20Sopenharmony_ci	 */
3358c2ecf20Sopenharmony_ci	list_for_each_entry_safe(dfp, pli, dop_list, dfp_list) {
3368c2ecf20Sopenharmony_ci		ops = defer_op_types[dfp->dfp_type];
3378c2ecf20Sopenharmony_ci		trace_xfs_defer_cancel_list(mp, dfp);
3388c2ecf20Sopenharmony_ci		list_del(&dfp->dfp_list);
3398c2ecf20Sopenharmony_ci		list_for_each_safe(pwi, n, &dfp->dfp_work) {
3408c2ecf20Sopenharmony_ci			list_del(pwi);
3418c2ecf20Sopenharmony_ci			dfp->dfp_count--;
3428c2ecf20Sopenharmony_ci			ops->cancel_item(pwi);
3438c2ecf20Sopenharmony_ci		}
3448c2ecf20Sopenharmony_ci		ASSERT(dfp->dfp_count == 0);
3458c2ecf20Sopenharmony_ci		kmem_free(dfp);
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci/*
3508c2ecf20Sopenharmony_ci * Prevent a log intent item from pinning the tail of the log by logging a
3518c2ecf20Sopenharmony_ci * done item to release the intent item; and then log a new intent item.
3528c2ecf20Sopenharmony_ci * The caller should provide a fresh transaction and roll it after we're done.
3538c2ecf20Sopenharmony_ci */
3548c2ecf20Sopenharmony_cistatic int
3558c2ecf20Sopenharmony_cixfs_defer_relog(
3568c2ecf20Sopenharmony_ci	struct xfs_trans		**tpp,
3578c2ecf20Sopenharmony_ci	struct list_head		*dfops)
3588c2ecf20Sopenharmony_ci{
3598c2ecf20Sopenharmony_ci	struct xlog			*log = (*tpp)->t_mountp->m_log;
3608c2ecf20Sopenharmony_ci	struct xfs_defer_pending	*dfp;
3618c2ecf20Sopenharmony_ci	xfs_lsn_t			threshold_lsn = NULLCOMMITLSN;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	ASSERT((*tpp)->t_flags & XFS_TRANS_PERM_LOG_RES);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	list_for_each_entry(dfp, dfops, dfp_list) {
3678c2ecf20Sopenharmony_ci		/*
3688c2ecf20Sopenharmony_ci		 * If the log intent item for this deferred op is not a part of
3698c2ecf20Sopenharmony_ci		 * the current log checkpoint, relog the intent item to keep
3708c2ecf20Sopenharmony_ci		 * the log tail moving forward.  We're ok with this being racy
3718c2ecf20Sopenharmony_ci		 * because an incorrect decision means we'll be a little slower
3728c2ecf20Sopenharmony_ci		 * at pushing the tail.
3738c2ecf20Sopenharmony_ci		 */
3748c2ecf20Sopenharmony_ci		if (dfp->dfp_intent == NULL ||
3758c2ecf20Sopenharmony_ci		    xfs_log_item_in_current_chkpt(dfp->dfp_intent))
3768c2ecf20Sopenharmony_ci			continue;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci		/*
3798c2ecf20Sopenharmony_ci		 * Figure out where we need the tail to be in order to maintain
3808c2ecf20Sopenharmony_ci		 * the minimum required free space in the log.  Only sample
3818c2ecf20Sopenharmony_ci		 * the log threshold once per call.
3828c2ecf20Sopenharmony_ci		 */
3838c2ecf20Sopenharmony_ci		if (threshold_lsn == NULLCOMMITLSN) {
3848c2ecf20Sopenharmony_ci			threshold_lsn = xlog_grant_push_threshold(log, 0);
3858c2ecf20Sopenharmony_ci			if (threshold_lsn == NULLCOMMITLSN)
3868c2ecf20Sopenharmony_ci				break;
3878c2ecf20Sopenharmony_ci		}
3888c2ecf20Sopenharmony_ci		if (XFS_LSN_CMP(dfp->dfp_intent->li_lsn, threshold_lsn) >= 0)
3898c2ecf20Sopenharmony_ci			continue;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci		trace_xfs_defer_relog_intent((*tpp)->t_mountp, dfp);
3928c2ecf20Sopenharmony_ci		XFS_STATS_INC((*tpp)->t_mountp, defer_relog);
3938c2ecf20Sopenharmony_ci		dfp->dfp_intent = xfs_trans_item_relog(dfp->dfp_intent, *tpp);
3948c2ecf20Sopenharmony_ci	}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	if ((*tpp)->t_flags & XFS_TRANS_DIRTY)
3978c2ecf20Sopenharmony_ci		return xfs_defer_trans_roll(tpp);
3988c2ecf20Sopenharmony_ci	return 0;
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci/*
4028c2ecf20Sopenharmony_ci * Log an intent-done item for the first pending intent, and finish the work
4038c2ecf20Sopenharmony_ci * items.
4048c2ecf20Sopenharmony_ci */
4058c2ecf20Sopenharmony_cistatic int
4068c2ecf20Sopenharmony_cixfs_defer_finish_one(
4078c2ecf20Sopenharmony_ci	struct xfs_trans		*tp,
4088c2ecf20Sopenharmony_ci	struct xfs_defer_pending	*dfp)
4098c2ecf20Sopenharmony_ci{
4108c2ecf20Sopenharmony_ci	const struct xfs_defer_op_type	*ops = defer_op_types[dfp->dfp_type];
4118c2ecf20Sopenharmony_ci	struct xfs_btree_cur		*state = NULL;
4128c2ecf20Sopenharmony_ci	struct list_head		*li, *n;
4138c2ecf20Sopenharmony_ci	int				error;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	trace_xfs_defer_pending_finish(tp->t_mountp, dfp);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	dfp->dfp_done = ops->create_done(tp, dfp->dfp_intent, dfp->dfp_count);
4188c2ecf20Sopenharmony_ci	list_for_each_safe(li, n, &dfp->dfp_work) {
4198c2ecf20Sopenharmony_ci		list_del(li);
4208c2ecf20Sopenharmony_ci		dfp->dfp_count--;
4218c2ecf20Sopenharmony_ci		error = ops->finish_item(tp, dfp->dfp_done, li, &state);
4228c2ecf20Sopenharmony_ci		if (error == -EAGAIN) {
4238c2ecf20Sopenharmony_ci			/*
4248c2ecf20Sopenharmony_ci			 * Caller wants a fresh transaction; put the work item
4258c2ecf20Sopenharmony_ci			 * back on the list and log a new log intent item to
4268c2ecf20Sopenharmony_ci			 * replace the old one.  See "Requesting a Fresh
4278c2ecf20Sopenharmony_ci			 * Transaction while Finishing Deferred Work" above.
4288c2ecf20Sopenharmony_ci			 */
4298c2ecf20Sopenharmony_ci			list_add(li, &dfp->dfp_work);
4308c2ecf20Sopenharmony_ci			dfp->dfp_count++;
4318c2ecf20Sopenharmony_ci			dfp->dfp_done = NULL;
4328c2ecf20Sopenharmony_ci			dfp->dfp_intent = NULL;
4338c2ecf20Sopenharmony_ci			xfs_defer_create_intent(tp, dfp, false);
4348c2ecf20Sopenharmony_ci		}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci		if (error)
4378c2ecf20Sopenharmony_ci			goto out;
4388c2ecf20Sopenharmony_ci	}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	/* Done with the dfp, free it. */
4418c2ecf20Sopenharmony_ci	list_del(&dfp->dfp_list);
4428c2ecf20Sopenharmony_ci	kmem_free(dfp);
4438c2ecf20Sopenharmony_ciout:
4448c2ecf20Sopenharmony_ci	if (ops->finish_cleanup)
4458c2ecf20Sopenharmony_ci		ops->finish_cleanup(tp, state, error);
4468c2ecf20Sopenharmony_ci	return error;
4478c2ecf20Sopenharmony_ci}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci/*
4508c2ecf20Sopenharmony_ci * Finish all the pending work.  This involves logging intent items for
4518c2ecf20Sopenharmony_ci * any work items that wandered in since the last transaction roll (if
4528c2ecf20Sopenharmony_ci * one has even happened), rolling the transaction, and finishing the
4538c2ecf20Sopenharmony_ci * work items in the first item on the logged-and-pending list.
4548c2ecf20Sopenharmony_ci *
4558c2ecf20Sopenharmony_ci * If an inode is provided, relog it to the new transaction.
4568c2ecf20Sopenharmony_ci */
4578c2ecf20Sopenharmony_ciint
4588c2ecf20Sopenharmony_cixfs_defer_finish_noroll(
4598c2ecf20Sopenharmony_ci	struct xfs_trans		**tp)
4608c2ecf20Sopenharmony_ci{
4618c2ecf20Sopenharmony_ci	struct xfs_defer_pending	*dfp;
4628c2ecf20Sopenharmony_ci	int				error = 0;
4638c2ecf20Sopenharmony_ci	LIST_HEAD(dop_pending);
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	ASSERT((*tp)->t_flags & XFS_TRANS_PERM_LOG_RES);
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	trace_xfs_defer_finish(*tp, _RET_IP_);
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	/* Until we run out of pending work to finish... */
4708c2ecf20Sopenharmony_ci	while (!list_empty(&dop_pending) || !list_empty(&(*tp)->t_dfops)) {
4718c2ecf20Sopenharmony_ci		/*
4728c2ecf20Sopenharmony_ci		 * Deferred items that are created in the process of finishing
4738c2ecf20Sopenharmony_ci		 * other deferred work items should be queued at the head of
4748c2ecf20Sopenharmony_ci		 * the pending list, which puts them ahead of the deferred work
4758c2ecf20Sopenharmony_ci		 * that was created by the caller.  This keeps the number of
4768c2ecf20Sopenharmony_ci		 * pending work items to a minimum, which decreases the amount
4778c2ecf20Sopenharmony_ci		 * of time that any one intent item can stick around in memory,
4788c2ecf20Sopenharmony_ci		 * pinning the log tail.
4798c2ecf20Sopenharmony_ci		 */
4808c2ecf20Sopenharmony_ci		xfs_defer_create_intents(*tp);
4818c2ecf20Sopenharmony_ci		list_splice_init(&(*tp)->t_dfops, &dop_pending);
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci		error = xfs_defer_trans_roll(tp);
4848c2ecf20Sopenharmony_ci		if (error)
4858c2ecf20Sopenharmony_ci			goto out_shutdown;
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci		/* Possibly relog intent items to keep the log moving. */
4888c2ecf20Sopenharmony_ci		error = xfs_defer_relog(tp, &dop_pending);
4898c2ecf20Sopenharmony_ci		if (error)
4908c2ecf20Sopenharmony_ci			goto out_shutdown;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci		dfp = list_first_entry(&dop_pending, struct xfs_defer_pending,
4938c2ecf20Sopenharmony_ci				       dfp_list);
4948c2ecf20Sopenharmony_ci		error = xfs_defer_finish_one(*tp, dfp);
4958c2ecf20Sopenharmony_ci		if (error && error != -EAGAIN)
4968c2ecf20Sopenharmony_ci			goto out_shutdown;
4978c2ecf20Sopenharmony_ci	}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	trace_xfs_defer_finish_done(*tp, _RET_IP_);
5008c2ecf20Sopenharmony_ci	return 0;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ciout_shutdown:
5038c2ecf20Sopenharmony_ci	xfs_defer_trans_abort(*tp, &dop_pending);
5048c2ecf20Sopenharmony_ci	xfs_force_shutdown((*tp)->t_mountp, SHUTDOWN_CORRUPT_INCORE);
5058c2ecf20Sopenharmony_ci	trace_xfs_defer_finish_error(*tp, error);
5068c2ecf20Sopenharmony_ci	xfs_defer_cancel_list((*tp)->t_mountp, &dop_pending);
5078c2ecf20Sopenharmony_ci	xfs_defer_cancel(*tp);
5088c2ecf20Sopenharmony_ci	return error;
5098c2ecf20Sopenharmony_ci}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ciint
5128c2ecf20Sopenharmony_cixfs_defer_finish(
5138c2ecf20Sopenharmony_ci	struct xfs_trans	**tp)
5148c2ecf20Sopenharmony_ci{
5158c2ecf20Sopenharmony_ci	int			error;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	/*
5188c2ecf20Sopenharmony_ci	 * Finish and roll the transaction once more to avoid returning to the
5198c2ecf20Sopenharmony_ci	 * caller with a dirty transaction.
5208c2ecf20Sopenharmony_ci	 */
5218c2ecf20Sopenharmony_ci	error = xfs_defer_finish_noroll(tp);
5228c2ecf20Sopenharmony_ci	if (error)
5238c2ecf20Sopenharmony_ci		return error;
5248c2ecf20Sopenharmony_ci	if ((*tp)->t_flags & XFS_TRANS_DIRTY) {
5258c2ecf20Sopenharmony_ci		error = xfs_defer_trans_roll(tp);
5268c2ecf20Sopenharmony_ci		if (error) {
5278c2ecf20Sopenharmony_ci			xfs_force_shutdown((*tp)->t_mountp,
5288c2ecf20Sopenharmony_ci					   SHUTDOWN_CORRUPT_INCORE);
5298c2ecf20Sopenharmony_ci			return error;
5308c2ecf20Sopenharmony_ci		}
5318c2ecf20Sopenharmony_ci	}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	/* Reset LOWMODE now that we've finished all the dfops. */
5348c2ecf20Sopenharmony_ci	ASSERT(list_empty(&(*tp)->t_dfops));
5358c2ecf20Sopenharmony_ci	(*tp)->t_flags &= ~XFS_TRANS_LOWMODE;
5368c2ecf20Sopenharmony_ci	return 0;
5378c2ecf20Sopenharmony_ci}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_civoid
5408c2ecf20Sopenharmony_cixfs_defer_cancel(
5418c2ecf20Sopenharmony_ci	struct xfs_trans	*tp)
5428c2ecf20Sopenharmony_ci{
5438c2ecf20Sopenharmony_ci	struct xfs_mount	*mp = tp->t_mountp;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	trace_xfs_defer_cancel(tp, _RET_IP_);
5468c2ecf20Sopenharmony_ci	xfs_defer_cancel_list(mp, &tp->t_dfops);
5478c2ecf20Sopenharmony_ci}
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci/* Add an item for later deferred processing. */
5508c2ecf20Sopenharmony_civoid
5518c2ecf20Sopenharmony_cixfs_defer_add(
5528c2ecf20Sopenharmony_ci	struct xfs_trans		*tp,
5538c2ecf20Sopenharmony_ci	enum xfs_defer_ops_type		type,
5548c2ecf20Sopenharmony_ci	struct list_head		*li)
5558c2ecf20Sopenharmony_ci{
5568c2ecf20Sopenharmony_ci	struct xfs_defer_pending	*dfp = NULL;
5578c2ecf20Sopenharmony_ci	const struct xfs_defer_op_type	*ops;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);
5608c2ecf20Sopenharmony_ci	BUILD_BUG_ON(ARRAY_SIZE(defer_op_types) != XFS_DEFER_OPS_TYPE_MAX);
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	/*
5638c2ecf20Sopenharmony_ci	 * Add the item to a pending item at the end of the intake list.
5648c2ecf20Sopenharmony_ci	 * If the last pending item has the same type, reuse it.  Else,
5658c2ecf20Sopenharmony_ci	 * create a new pending item at the end of the intake list.
5668c2ecf20Sopenharmony_ci	 */
5678c2ecf20Sopenharmony_ci	if (!list_empty(&tp->t_dfops)) {
5688c2ecf20Sopenharmony_ci		dfp = list_last_entry(&tp->t_dfops,
5698c2ecf20Sopenharmony_ci				struct xfs_defer_pending, dfp_list);
5708c2ecf20Sopenharmony_ci		ops = defer_op_types[dfp->dfp_type];
5718c2ecf20Sopenharmony_ci		if (dfp->dfp_type != type ||
5728c2ecf20Sopenharmony_ci		    (ops->max_items && dfp->dfp_count >= ops->max_items))
5738c2ecf20Sopenharmony_ci			dfp = NULL;
5748c2ecf20Sopenharmony_ci	}
5758c2ecf20Sopenharmony_ci	if (!dfp) {
5768c2ecf20Sopenharmony_ci		dfp = kmem_alloc(sizeof(struct xfs_defer_pending),
5778c2ecf20Sopenharmony_ci				KM_NOFS);
5788c2ecf20Sopenharmony_ci		dfp->dfp_type = type;
5798c2ecf20Sopenharmony_ci		dfp->dfp_intent = NULL;
5808c2ecf20Sopenharmony_ci		dfp->dfp_done = NULL;
5818c2ecf20Sopenharmony_ci		dfp->dfp_count = 0;
5828c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&dfp->dfp_work);
5838c2ecf20Sopenharmony_ci		list_add_tail(&dfp->dfp_list, &tp->t_dfops);
5848c2ecf20Sopenharmony_ci	}
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	list_add_tail(li, &dfp->dfp_work);
5878c2ecf20Sopenharmony_ci	dfp->dfp_count++;
5888c2ecf20Sopenharmony_ci}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci/*
5918c2ecf20Sopenharmony_ci * Move deferred ops from one transaction to another and reset the source to
5928c2ecf20Sopenharmony_ci * initial state. This is primarily used to carry state forward across
5938c2ecf20Sopenharmony_ci * transaction rolls with pending dfops.
5948c2ecf20Sopenharmony_ci */
5958c2ecf20Sopenharmony_civoid
5968c2ecf20Sopenharmony_cixfs_defer_move(
5978c2ecf20Sopenharmony_ci	struct xfs_trans	*dtp,
5988c2ecf20Sopenharmony_ci	struct xfs_trans	*stp)
5998c2ecf20Sopenharmony_ci{
6008c2ecf20Sopenharmony_ci	list_splice_init(&stp->t_dfops, &dtp->t_dfops);
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	/*
6038c2ecf20Sopenharmony_ci	 * Low free space mode was historically controlled by a dfops field.
6048c2ecf20Sopenharmony_ci	 * This meant that low mode state potentially carried across multiple
6058c2ecf20Sopenharmony_ci	 * transaction rolls. Transfer low mode on a dfops move to preserve
6068c2ecf20Sopenharmony_ci	 * that behavior.
6078c2ecf20Sopenharmony_ci	 */
6088c2ecf20Sopenharmony_ci	dtp->t_flags |= (stp->t_flags & XFS_TRANS_LOWMODE);
6098c2ecf20Sopenharmony_ci	stp->t_flags &= ~XFS_TRANS_LOWMODE;
6108c2ecf20Sopenharmony_ci}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci/*
6138c2ecf20Sopenharmony_ci * Prepare a chain of fresh deferred ops work items to be completed later.  Log
6148c2ecf20Sopenharmony_ci * recovery requires the ability to put off until later the actual finishing
6158c2ecf20Sopenharmony_ci * work so that it can process unfinished items recovered from the log in
6168c2ecf20Sopenharmony_ci * correct order.
6178c2ecf20Sopenharmony_ci *
6188c2ecf20Sopenharmony_ci * Create and log intent items for all the work that we're capturing so that we
6198c2ecf20Sopenharmony_ci * can be assured that the items will get replayed if the system goes down
6208c2ecf20Sopenharmony_ci * before log recovery gets a chance to finish the work it put off.  The entire
6218c2ecf20Sopenharmony_ci * deferred ops state is transferred to the capture structure and the
6228c2ecf20Sopenharmony_ci * transaction is then ready for the caller to commit it.  If there are no
6238c2ecf20Sopenharmony_ci * intent items to capture, this function returns NULL.
6248c2ecf20Sopenharmony_ci *
6258c2ecf20Sopenharmony_ci * If capture_ip is not NULL, the capture structure will obtain an extra
6268c2ecf20Sopenharmony_ci * reference to the inode.
6278c2ecf20Sopenharmony_ci */
6288c2ecf20Sopenharmony_cistatic struct xfs_defer_capture *
6298c2ecf20Sopenharmony_cixfs_defer_ops_capture(
6308c2ecf20Sopenharmony_ci	struct xfs_trans		*tp,
6318c2ecf20Sopenharmony_ci	struct xfs_inode		*capture_ip)
6328c2ecf20Sopenharmony_ci{
6338c2ecf20Sopenharmony_ci	struct xfs_defer_capture	*dfc;
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	if (list_empty(&tp->t_dfops))
6368c2ecf20Sopenharmony_ci		return NULL;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	/* Create an object to capture the defer ops. */
6398c2ecf20Sopenharmony_ci	dfc = kmem_zalloc(sizeof(*dfc), KM_NOFS);
6408c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&dfc->dfc_list);
6418c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&dfc->dfc_dfops);
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	xfs_defer_create_intents(tp);
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	/* Move the dfops chain and transaction state to the capture struct. */
6468c2ecf20Sopenharmony_ci	list_splice_init(&tp->t_dfops, &dfc->dfc_dfops);
6478c2ecf20Sopenharmony_ci	dfc->dfc_tpflags = tp->t_flags & XFS_TRANS_LOWMODE;
6488c2ecf20Sopenharmony_ci	tp->t_flags &= ~XFS_TRANS_LOWMODE;
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	/* Capture the remaining block reservations along with the dfops. */
6518c2ecf20Sopenharmony_ci	dfc->dfc_blkres = tp->t_blk_res - tp->t_blk_res_used;
6528c2ecf20Sopenharmony_ci	dfc->dfc_rtxres = tp->t_rtx_res - tp->t_rtx_res_used;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	/* Preserve the log reservation size. */
6558c2ecf20Sopenharmony_ci	dfc->dfc_logres = tp->t_log_res;
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	/*
6588c2ecf20Sopenharmony_ci	 * Grab an extra reference to this inode and attach it to the capture
6598c2ecf20Sopenharmony_ci	 * structure.
6608c2ecf20Sopenharmony_ci	 */
6618c2ecf20Sopenharmony_ci	if (capture_ip) {
6628c2ecf20Sopenharmony_ci		ihold(VFS_I(capture_ip));
6638c2ecf20Sopenharmony_ci		dfc->dfc_capture_ip = capture_ip;
6648c2ecf20Sopenharmony_ci	}
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	return dfc;
6678c2ecf20Sopenharmony_ci}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci/* Release all resources that we used to capture deferred ops. */
6708c2ecf20Sopenharmony_civoid
6718c2ecf20Sopenharmony_cixfs_defer_ops_release(
6728c2ecf20Sopenharmony_ci	struct xfs_mount		*mp,
6738c2ecf20Sopenharmony_ci	struct xfs_defer_capture	*dfc)
6748c2ecf20Sopenharmony_ci{
6758c2ecf20Sopenharmony_ci	xfs_defer_cancel_list(mp, &dfc->dfc_dfops);
6768c2ecf20Sopenharmony_ci	if (dfc->dfc_capture_ip)
6778c2ecf20Sopenharmony_ci		xfs_irele(dfc->dfc_capture_ip);
6788c2ecf20Sopenharmony_ci	kmem_free(dfc);
6798c2ecf20Sopenharmony_ci}
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci/*
6828c2ecf20Sopenharmony_ci * Capture any deferred ops and commit the transaction.  This is the last step
6838c2ecf20Sopenharmony_ci * needed to finish a log intent item that we recovered from the log.  If any
6848c2ecf20Sopenharmony_ci * of the deferred ops operate on an inode, the caller must pass in that inode
6858c2ecf20Sopenharmony_ci * so that the reference can be transferred to the capture structure.  The
6868c2ecf20Sopenharmony_ci * caller must hold ILOCK_EXCL on the inode, and must unlock it before calling
6878c2ecf20Sopenharmony_ci * xfs_defer_ops_continue.
6888c2ecf20Sopenharmony_ci */
6898c2ecf20Sopenharmony_ciint
6908c2ecf20Sopenharmony_cixfs_defer_ops_capture_and_commit(
6918c2ecf20Sopenharmony_ci	struct xfs_trans		*tp,
6928c2ecf20Sopenharmony_ci	struct xfs_inode		*capture_ip,
6938c2ecf20Sopenharmony_ci	struct list_head		*capture_list)
6948c2ecf20Sopenharmony_ci{
6958c2ecf20Sopenharmony_ci	struct xfs_mount		*mp = tp->t_mountp;
6968c2ecf20Sopenharmony_ci	struct xfs_defer_capture	*dfc;
6978c2ecf20Sopenharmony_ci	int				error;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	ASSERT(!capture_ip || xfs_isilocked(capture_ip, XFS_ILOCK_EXCL));
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	/* If we don't capture anything, commit transaction and exit. */
7028c2ecf20Sopenharmony_ci	dfc = xfs_defer_ops_capture(tp, capture_ip);
7038c2ecf20Sopenharmony_ci	if (!dfc)
7048c2ecf20Sopenharmony_ci		return xfs_trans_commit(tp);
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	/* Commit the transaction and add the capture structure to the list. */
7078c2ecf20Sopenharmony_ci	error = xfs_trans_commit(tp);
7088c2ecf20Sopenharmony_ci	if (error) {
7098c2ecf20Sopenharmony_ci		xfs_defer_ops_release(mp, dfc);
7108c2ecf20Sopenharmony_ci		return error;
7118c2ecf20Sopenharmony_ci	}
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	list_add_tail(&dfc->dfc_list, capture_list);
7148c2ecf20Sopenharmony_ci	return 0;
7158c2ecf20Sopenharmony_ci}
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci/*
7188c2ecf20Sopenharmony_ci * Attach a chain of captured deferred ops to a new transaction and free the
7198c2ecf20Sopenharmony_ci * capture structure.  If an inode was captured, it will be passed back to the
7208c2ecf20Sopenharmony_ci * caller with ILOCK_EXCL held and joined to the transaction with lockflags==0.
7218c2ecf20Sopenharmony_ci * The caller now owns the inode reference.
7228c2ecf20Sopenharmony_ci */
7238c2ecf20Sopenharmony_civoid
7248c2ecf20Sopenharmony_cixfs_defer_ops_continue(
7258c2ecf20Sopenharmony_ci	struct xfs_defer_capture	*dfc,
7268c2ecf20Sopenharmony_ci	struct xfs_trans		*tp,
7278c2ecf20Sopenharmony_ci	struct xfs_inode		**captured_ipp)
7288c2ecf20Sopenharmony_ci{
7298c2ecf20Sopenharmony_ci	ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);
7308c2ecf20Sopenharmony_ci	ASSERT(!(tp->t_flags & XFS_TRANS_DIRTY));
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	/* Lock and join the captured inode to the new transaction. */
7338c2ecf20Sopenharmony_ci	if (dfc->dfc_capture_ip) {
7348c2ecf20Sopenharmony_ci		xfs_ilock(dfc->dfc_capture_ip, XFS_ILOCK_EXCL);
7358c2ecf20Sopenharmony_ci		xfs_trans_ijoin(tp, dfc->dfc_capture_ip, 0);
7368c2ecf20Sopenharmony_ci	}
7378c2ecf20Sopenharmony_ci	*captured_ipp = dfc->dfc_capture_ip;
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	/* Move captured dfops chain and state to the transaction. */
7408c2ecf20Sopenharmony_ci	list_splice_init(&dfc->dfc_dfops, &tp->t_dfops);
7418c2ecf20Sopenharmony_ci	tp->t_flags |= dfc->dfc_tpflags;
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	kmem_free(dfc);
7448c2ecf20Sopenharmony_ci}
745