summaryrefslogtreecommitdiff
path: root/include/linux/iversion.h
blob: f268828f9f7e2c617a5df6c8834b9727b3e67164 (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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _LINUX_IVERSION_H
#define _LINUX_IVERSION_H

#include <linux/fs.h>

/*
 * The change attribute (i_version) is mandated by NFSv4 and is mostly for
 * knfsd, but is also used for other purposes (e.g. IMA). The i_version must
 * appear different to observers if there was a change to the inode's data or
 * metadata since it was last queried.
 *
 * Observers see the i_version as a 64-bit number that never decreases. If it
 * remains the same since it was last checked, then nothing has changed in the
 * inode. If it's different then something has changed. Observers cannot infer
 * anything about the nature or magnitude of the changes from the value, only
 * that the inode has changed in some fashion.
 *
 * Not all filesystems properly implement the i_version counter. Subsystems that
 * want to use i_version field on an inode should first check whether the
 * filesystem sets the SB_I_VERSION flag (usually via the IS_I_VERSION macro).
 *
 * Those that set SB_I_VERSION will automatically have their i_version counter
 * incremented on writes to normal files. If the SB_I_VERSION is not set, then
 * the VFS will not touch it on writes, and the filesystem can use it how it
 * wishes. Note that the filesystem is always responsible for updating the
 * i_version on namespace changes in directories (mkdir, rmdir, unlink, etc.).
 * We consider these sorts of filesystems to have a kernel-managed i_version.
 *
 * It may be impractical for filesystems to keep i_version updates atomic with
 * respect to the changes that cause them.  They should, however, guarantee
 * that i_version updates are never visible before the changes that caused
 * them.  Also, i_version updates should never be delayed longer than it takes
 * the original change to reach disk.
 *
 * Note that some filesystems (e.g. NFS and AFS) just use the field to store
 * a server-provided value (for the most part). For that reason, those
 * filesystems do not set SB_I_VERSION. These filesystems are considered to
 * have a self-managed i_version.
 */

/**
 * inode_set_iversion_raw - set i_version to the specified raw value
 * @inode: inode to set
 * @new: new i_version value to set
 *
 * Set @inode's i_version field to @new. This function is for use by
 * filesystems that self-manage the i_version.
 *
 * For example, the NFS client stores its NFSv4 change attribute in this way,
 * and the AFS client stores the data_version from the server here.
 */
static inline void
inode_set_iversion_raw(struct inode *inode, u64 new)
{
	inode->i_version = new;
}

/**
 * inode_set_iversion - set i_version to a particular value
 * @inode: inode to set
 * @new: new i_version value to set
 *
 * Set @inode's i_version field to @new. This function is for filesystems with
 * a kernel-managed i_version.
 *
 * For now, this just does the same thing as the _raw variant.
 */
static inline void
inode_set_iversion(struct inode *inode, u64 new)
{
	inode_set_iversion_raw(inode, new);
}

/**
 * inode_set_iversion_queried - set i_version to a particular value and set
 *                              flag to indicate that it has been viewed
 * @inode: inode to set
 * @new: new i_version value to set
 *
 * When loading in an i_version value from a backing store, we typically don't
 * know whether it was previously viewed before being stored or not. Thus, we
 * must assume that it was, to ensure that any changes will result in the
 * value changing.
 *
 * This function will set the inode's i_version, and possibly flag the value
 * as if it has already been viewed at least once.
 *
 * For now, this just does what inode_set_iversion does.
 */
static inline void
inode_set_iversion_queried(struct inode *inode, u64 new)
{
	inode_set_iversion(inode, new);
}

/**
 * inode_maybe_inc_iversion - increments i_version
 * @inode: inode with the i_version that should be updated
 * @force: increment the counter even if it's not necessary
 *
 * Every time the inode is modified, the i_version field must be seen to have
 * changed by any observer.
 *
 * In this implementation, we always increment it after taking the i_lock to
 * ensure that we don't race with other incrementors.
 *
 * Returns true if counter was bumped, and false if it wasn't.
 */
static inline bool
inode_maybe_inc_iversion(struct inode *inode, bool force)
{
	atomic64_t *ivp = (atomic64_t *)&inode->i_version;

	atomic64_inc(ivp);
	return true;
}


/**
 * inode_inc_iversion - forcibly increment i_version
 * @inode: inode that needs to be updated
 *
 * Forcbily increment the i_version field. This always results in a change to
 * the observable value.
 */
static inline void
inode_inc_iversion(struct inode *inode)
{
	inode_maybe_inc_iversion(inode, true);
}

/**
 * inode_iversion_need_inc - is the i_version in need of being incremented?
 * @inode: inode to check
 *
 * Returns whether the inode->i_version counter needs incrementing on the next
 * change.
 *
 * For now, we assume that it always does.
 */
static inline bool
inode_iversion_need_inc(struct inode *inode)
{
	return true;
}

/**
 * inode_peek_iversion_raw - grab a "raw" iversion value
 * @inode: inode from which i_version should be read
 *
 * Grab a "raw" inode->i_version value and return it. The i_version is not
 * flagged or converted in any way. This is mostly used to access a self-managed
 * i_version.
 *
 * With those filesystems, we want to treat the i_version as an entirely
 * opaque value.
 */
static inline u64
inode_peek_iversion_raw(const struct inode *inode)
{
	return inode->i_version;
}

/**
 * inode_inc_iversion_raw - forcibly increment raw i_version
 * @inode: inode that needs to be updated
 *
 * Forcbily increment the raw i_version field. This always results in a change
 * to the raw value.
 *
 * NFS will use the i_version field to store the value from the server. It
 * mostly treats it as opaque, but in the case where it holds a write
 * delegation, it must increment the value itself. This function does that.
 */
static inline void
inode_inc_iversion_raw(struct inode *inode)
{
	inode_inc_iversion(inode);
}

/**
 * inode_peek_iversion - read i_version without flagging it to be incremented
 * @inode: inode from which i_version should be read
 *
 * Read the inode i_version counter for an inode without registering it as a
 * query.
 *
 * This is typically used by local filesystems that need to store an i_version
 * on disk. In that situation, it's not necessary to flag it as having been
 * viewed, as the result won't be used to gauge changes from that point.
 */
static inline u64
inode_peek_iversion(const struct inode *inode)
{
	return inode_peek_iversion_raw(inode);
}

/**
 * inode_query_iversion - read i_version for later use
 * @inode: inode from which i_version should be read
 *
 * Read the inode i_version counter. This should be used by callers that wish
 * to store the returned i_version for later comparison. This will guarantee
 * that a later query of the i_version will result in a different value if
 * anything has changed.
 *
 * This implementation just does a peek.
 */
static inline u64
inode_query_iversion(struct inode *inode)
{
	return inode_peek_iversion(inode);
}

/**
 * inode_cmp_iversion_raw - check whether the raw i_version counter has changed
 * @inode: inode to check
 * @old: old value to check against its i_version
 *
 * Compare the current raw i_version counter with a previous one. Returns 0 if
 * they are the same or non-zero if they are different.
 */
static inline s64
inode_cmp_iversion_raw(const struct inode *inode, u64 old)
{
	return (s64)inode_peek_iversion_raw(inode) - (s64)old;
}

/**
 * inode_cmp_iversion - check whether the i_version counter has changed
 * @inode: inode to check
 * @old: old value to check against its i_version
 *
 * Compare an i_version counter with a previous one. Returns 0 if they are
 * the same or non-zero if they are different.
 */
static inline s64
inode_cmp_iversion(const struct inode *inode, u64 old)
{
	return (s64)inode_peek_iversion(inode) - (s64)old;
}
#endif