summaryrefslogtreecommitdiff
path: root/fs/xfs/libxfs/xfs_swapext.h
blob: 13824310f2a2607aae0d11810782afc33ece1765 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Copyright (C) 2022 Oracle.  All Rights Reserved.
 * Author: Darrick J. Wong <djwong@kernel.org>
 */
#ifndef __XFS_SWAPEXT_H_
#define __XFS_SWAPEXT_H_ 1

/*
 * Decide if this filesystem supports using log items to swap file extents and
 * restart the operation if the system fails before the operation completes.
 *
 * This can be done to individual file extents by using the block mapping log
 * intent items introduced with reflink and rmap; or to entire file ranges
 * using swapext log intent items to track the overall progress across multiple
 * extent mappings.
 */
static inline bool xfs_swapext_supported(struct xfs_mount *mp)
{
	return xfs_has_reflink(mp) || xfs_has_rmapbt(mp);
}

/*
 * In-core information about an extent swap request between ranges of two
 * inodes.
 */
struct xfs_swapext_intent {
	/* List of other incore deferred work. */
	struct list_head	sxi_list;

	/* Inodes participating in the operation. */
	struct xfs_inode	*sxi_ip1;
	struct xfs_inode	*sxi_ip2;

	/* File offset range information. */
	xfs_fileoff_t		sxi_startoff1;
	xfs_fileoff_t		sxi_startoff2;
	xfs_filblks_t		sxi_blockcount;

	/* Set these file sizes after the operation, unless negative. */
	xfs_fsize_t		sxi_isize1;
	xfs_fsize_t		sxi_isize2;

	/* XFS_SWAP_EXT_* log operation flags */
	unsigned int		sxi_flags;

	/* XFS_SWAP_EXT_OP_* flags */
	unsigned int		sxi_op_flags;
};

/* Use log intent items to track and restart the entire operation. */
#define XFS_SWAP_EXT_OP_LOGGED	(1U << 0)

/* Upgrade files to have large extent counts before proceeding. */
#define XFS_SWAP_EXT_OP_NREXT64	(1U << 1)

#define XFS_SWAP_EXT_OP_STRINGS \
	{ XFS_SWAP_EXT_OP_LOGGED,		"LOGGED" }, \
	{ XFS_SWAP_EXT_OP_NREXT64,		"NREXT64" }

static inline int
xfs_swapext_whichfork(const struct xfs_swapext_intent *sxi)
{
	if (sxi->sxi_flags & XFS_SWAP_EXT_ATTR_FORK)
		return XFS_ATTR_FORK;
	return XFS_DATA_FORK;
}

/* Parameters for a swapext request. */
struct xfs_swapext_req {
	/* Inodes participating in the operation. */
	struct xfs_inode	*ip1;
	struct xfs_inode	*ip2;

	/* File offset range information. */
	xfs_fileoff_t		startoff1;
	xfs_fileoff_t		startoff2;
	xfs_filblks_t		blockcount;

	/* Data or attr fork? */
	int			whichfork;

	/* XFS_SWAP_REQ_* operation flags */
	unsigned int		req_flags;

	/*
	 * Fields below this line are filled out by xfs_swapext_estimate;
	 * callers should initialize this part of the struct to zero.
	 */

	/*
	 * Data device blocks to be moved out of ip1, and free space needed to
	 * handle the bmbt changes.
	 */
	xfs_filblks_t		ip1_bcount;

	/*
	 * Data device blocks to be moved out of ip2, and free space needed to
	 * handle the bmbt changes.
	 */
	xfs_filblks_t		ip2_bcount;

	/* rt blocks to be moved out of ip1. */
	xfs_filblks_t		ip1_rtbcount;

	/* rt blocks to be moved out of ip2. */
	xfs_filblks_t		ip2_rtbcount;

	/* Free space needed to handle the bmbt changes */
	unsigned long long	resblks;

	/* Number of extent swaps needed to complete the operation */
	unsigned long long	nr_exchanges;
};

/* Caller has permission to use log intent items for the swapext operation. */
#define XFS_SWAP_REQ_LOGGED		(1U << 0)

/* Set the file sizes when finished. */
#define XFS_SWAP_REQ_SET_SIZES		(1U << 1)

/* Do not swap any part of the range where ip1's mapping is a hole. */
#define XFS_SWAP_REQ_SKIP_INO1_HOLES	(1U << 2)

/* Files need to be upgraded to have large extent counts. */
#define XFS_SWAP_REQ_NREXT64		(1U << 3)

/* Try to convert inode2's fork to local format, if possible. */
#define XFS_SWAP_REQ_CVT_INO2_SF	(1U << 4)

#define XFS_SWAP_REQ_FLAGS		(XFS_SWAP_REQ_LOGGED | \
					 XFS_SWAP_REQ_SET_SIZES | \
					 XFS_SWAP_REQ_SKIP_INO1_HOLES | \
					 XFS_SWAP_REQ_NREXT64 | \
					 XFS_SWAP_REQ_CVT_INO2_SF)

#define XFS_SWAP_REQ_STRINGS \
	{ XFS_SWAP_REQ_LOGGED,			"LOGGED" }, \
	{ XFS_SWAP_REQ_SET_SIZES,		"SETSIZES" }, \
	{ XFS_SWAP_REQ_SKIP_INO1_HOLES,		"SKIP_INO1_HOLES" }, \
	{ XFS_SWAP_REQ_NREXT64,			"NREXT64" }, \
	{ XFS_SWAP_REQ_CVT_INO2_SF,		"CVT_INO2_SF" }

unsigned int xfs_swapext_reflink_prep(const struct xfs_swapext_req *req);
void xfs_swapext_reflink_finish(struct xfs_trans *tp,
		const struct xfs_swapext_req *req, unsigned int reflink_state);

int xfs_swapext_estimate_overhead(struct xfs_swapext_req *req);
int xfs_swapext_estimate(struct xfs_swapext_req *req);

extern struct kmem_cache	*xfs_swapext_intent_cache;

int __init xfs_swapext_intent_init_cache(void);
void xfs_swapext_intent_destroy_cache(void);

struct xfs_swapext_intent *xfs_swapext_init_intent(
		const struct xfs_swapext_req *req, unsigned int *reflink_state);
void xfs_swapext_ensure_reflink(struct xfs_trans *tp,
		const struct xfs_swapext_intent *sxi, unsigned int reflink_state);

void xfs_swapext_schedule(struct xfs_trans *tp,
		struct xfs_swapext_intent *sxi);
int xfs_swapext_finish_one(struct xfs_trans *tp,
		struct xfs_swapext_intent *sxi);

int xfs_swapext_check_extents(struct xfs_mount *mp,
		const struct xfs_swapext_req *req);

void xfs_swapext(struct xfs_trans *tp, const struct xfs_swapext_req *req);

#endif /* __XFS_SWAPEXT_H_ */