Skip to content
This repository was archived by the owner on Nov 8, 2023. It is now read-only.

Commit d7d3997

Browse files
committed
Merge de63017 ("Merge tag 'iversion-v6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/jlayton/linux") into android-mainline
Steps on the way to 6.3-rc1 Change-Id: I8b50568a7c4170d05941858e0c8ff0344b091d4d Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
2 parents 5532fa9 + de63017 commit d7d3997

File tree

12 files changed

+157
-87
lines changed

12 files changed

+157
-87
lines changed

fs/ceph/inode.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2417,10 +2417,10 @@ static int statx_to_caps(u32 want, umode_t mode)
24172417
{
24182418
int mask = 0;
24192419

2420-
if (want & (STATX_MODE|STATX_UID|STATX_GID|STATX_CTIME|STATX_BTIME))
2420+
if (want & (STATX_MODE|STATX_UID|STATX_GID|STATX_CTIME|STATX_BTIME|STATX_CHANGE_COOKIE))
24212421
mask |= CEPH_CAP_AUTH_SHARED;
24222422

2423-
if (want & (STATX_NLINK|STATX_CTIME)) {
2423+
if (want & (STATX_NLINK|STATX_CTIME|STATX_CHANGE_COOKIE)) {
24242424
/*
24252425
* The link count for directories depends on inode->i_subdirs,
24262426
* and that is only updated when Fs caps are held.
@@ -2431,11 +2431,10 @@ static int statx_to_caps(u32 want, umode_t mode)
24312431
mask |= CEPH_CAP_LINK_SHARED;
24322432
}
24332433

2434-
if (want & (STATX_ATIME|STATX_MTIME|STATX_CTIME|STATX_SIZE|
2435-
STATX_BLOCKS))
2434+
if (want & (STATX_ATIME|STATX_MTIME|STATX_CTIME|STATX_SIZE|STATX_BLOCKS|STATX_CHANGE_COOKIE))
24362435
mask |= CEPH_CAP_FILE_SHARED;
24372436

2438-
if (want & (STATX_CTIME))
2437+
if (want & (STATX_CTIME|STATX_CHANGE_COOKIE))
24392438
mask |= CEPH_CAP_XATTR_SHARED;
24402439

24412440
return mask;
@@ -2478,6 +2477,11 @@ int ceph_getattr(struct user_namespace *mnt_userns, const struct path *path,
24782477
valid_mask |= STATX_BTIME;
24792478
}
24802479

2480+
if (request_mask & STATX_CHANGE_COOKIE) {
2481+
stat->change_cookie = inode_peek_iversion_raw(inode);
2482+
valid_mask |= STATX_CHANGE_COOKIE;
2483+
}
2484+
24812485
if (ceph_snap(inode) == CEPH_NOSNAP)
24822486
stat->dev = sb->s_dev;
24832487
else
@@ -2519,6 +2523,8 @@ int ceph_getattr(struct user_namespace *mnt_userns, const struct path *path,
25192523
stat->nlink = 1 + 1 + ci->i_subdirs;
25202524
}
25212525

2526+
stat->attributes_mask |= STATX_ATTR_CHANGE_MONOTONIC;
2527+
stat->attributes |= STATX_ATTR_CHANGE_MONOTONIC;
25222528
stat->result_mask = request_mask & valid_mask;
25232529
return err;
25242530
}

fs/libfs.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1582,3 +1582,39 @@ bool inode_maybe_inc_iversion(struct inode *inode, bool force)
15821582
return true;
15831583
}
15841584
EXPORT_SYMBOL(inode_maybe_inc_iversion);
1585+
1586+
/**
1587+
* inode_query_iversion - read i_version for later use
1588+
* @inode: inode from which i_version should be read
1589+
*
1590+
* Read the inode i_version counter. This should be used by callers that wish
1591+
* to store the returned i_version for later comparison. This will guarantee
1592+
* that a later query of the i_version will result in a different value if
1593+
* anything has changed.
1594+
*
1595+
* In this implementation, we fetch the current value, set the QUERIED flag and
1596+
* then try to swap it into place with a cmpxchg, if it wasn't already set. If
1597+
* that fails, we try again with the newly fetched value from the cmpxchg.
1598+
*/
1599+
u64 inode_query_iversion(struct inode *inode)
1600+
{
1601+
u64 cur, new;
1602+
1603+
cur = inode_peek_iversion_raw(inode);
1604+
do {
1605+
/* If flag is already set, then no need to swap */
1606+
if (cur & I_VERSION_QUERIED) {
1607+
/*
1608+
* This barrier (and the implicit barrier in the
1609+
* cmpxchg below) pairs with the barrier in
1610+
* inode_maybe_inc_iversion().
1611+
*/
1612+
smp_mb();
1613+
break;
1614+
}
1615+
1616+
new = cur | I_VERSION_QUERIED;
1617+
} while (!atomic64_try_cmpxchg(&inode->i_version, &cur, new));
1618+
return cur >> I_VERSION_QUERIED_SHIFT;
1619+
}
1620+
EXPORT_SYMBOL(inode_query_iversion);

fs/nfs/export.c

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -145,17 +145,10 @@ nfs_get_parent(struct dentry *dentry)
145145
return parent;
146146
}
147147

148-
static u64 nfs_fetch_iversion(struct inode *inode)
149-
{
150-
nfs_revalidate_inode(inode, NFS_INO_INVALID_CHANGE);
151-
return inode_peek_iversion_raw(inode);
152-
}
153-
154148
const struct export_operations nfs_export_ops = {
155149
.encode_fh = nfs_encode_fh,
156150
.fh_to_dentry = nfs_fh_to_dentry,
157151
.get_parent = nfs_get_parent,
158-
.fetch_iversion = nfs_fetch_iversion,
159152
.flags = EXPORT_OP_NOWCC|EXPORT_OP_NOSUBTREECHK|
160153
EXPORT_OP_CLOSE_BEFORE_UNLINK|EXPORT_OP_REMOTE_FS|
161154
EXPORT_OP_NOATOMIC_ATTR,

fs/nfs/inode.c

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,8 @@ static u32 nfs_get_valid_attrmask(struct inode *inode)
825825
reply_mask |= STATX_UID | STATX_GID;
826826
if (!(cache_validity & NFS_INO_INVALID_BLOCKS))
827827
reply_mask |= STATX_BLOCKS;
828+
if (!(cache_validity & NFS_INO_INVALID_CHANGE))
829+
reply_mask |= STATX_CHANGE_COOKIE;
828830
return reply_mask;
829831
}
830832

@@ -843,16 +845,17 @@ int nfs_getattr(struct user_namespace *mnt_userns, const struct path *path,
843845

844846
request_mask &= STATX_TYPE | STATX_MODE | STATX_NLINK | STATX_UID |
845847
STATX_GID | STATX_ATIME | STATX_MTIME | STATX_CTIME |
846-
STATX_INO | STATX_SIZE | STATX_BLOCKS;
848+
STATX_INO | STATX_SIZE | STATX_BLOCKS | STATX_BTIME |
849+
STATX_CHANGE_COOKIE;
847850

848851
if ((query_flags & AT_STATX_DONT_SYNC) && !force_sync) {
849852
if (readdirplus_enabled)
850853
nfs_readdirplus_parent_cache_hit(path->dentry);
851854
goto out_no_revalidate;
852855
}
853856

854-
/* Flush out writes to the server in order to update c/mtime. */
855-
if ((request_mask & (STATX_CTIME | STATX_MTIME)) &&
857+
/* Flush out writes to the server in order to update c/mtime/version. */
858+
if ((request_mask & (STATX_CTIME | STATX_MTIME | STATX_CHANGE_COOKIE)) &&
856859
S_ISREG(inode->i_mode))
857860
filemap_write_and_wait(inode->i_mapping);
858861

@@ -872,7 +875,8 @@ int nfs_getattr(struct user_namespace *mnt_userns, const struct path *path,
872875
/* Is the user requesting attributes that might need revalidation? */
873876
if (!(request_mask & (STATX_MODE|STATX_NLINK|STATX_ATIME|STATX_CTIME|
874877
STATX_MTIME|STATX_UID|STATX_GID|
875-
STATX_SIZE|STATX_BLOCKS)))
878+
STATX_SIZE|STATX_BLOCKS|
879+
STATX_CHANGE_COOKIE)))
876880
goto out_no_revalidate;
877881

878882
/* Check whether the cached attributes are stale */
@@ -910,6 +914,10 @@ int nfs_getattr(struct user_namespace *mnt_userns, const struct path *path,
910914

911915
generic_fillattr(&init_user_ns, inode, stat);
912916
stat->ino = nfs_compat_user_ino64(NFS_FILEID(inode));
917+
stat->change_cookie = inode_peek_iversion_raw(inode);
918+
stat->attributes_mask |= STATX_ATTR_CHANGE_MONOTONIC;
919+
if (server->change_attr_type != NFS4_CHANGE_TYPE_IS_UNDEFINED)
920+
stat->attributes |= STATX_ATTR_CHANGE_MONOTONIC;
913921
if (S_ISDIR(inode->i_mode))
914922
stat->blksize = NFS_SERVER(inode)->dtsize;
915923
out:

fs/nfsd/nfs4xdr.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2965,7 +2965,9 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
29652965
goto out;
29662966
}
29672967

2968-
err = vfs_getattr(&path, &stat, STATX_BASIC_STATS, AT_STATX_SYNC_AS_STAT);
2968+
err = vfs_getattr(&path, &stat,
2969+
STATX_BASIC_STATS | STATX_BTIME | STATX_CHANGE_COOKIE,
2970+
AT_STATX_SYNC_AS_STAT);
29692971
if (err)
29702972
goto out_nfserr;
29712973
if (!(stat.result_mask & STATX_BTIME))

fs/nfsd/nfsfh.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,10 @@ void fh_fill_pre_attrs(struct svc_fh *fhp)
628628
stat.mtime = inode->i_mtime;
629629
stat.ctime = inode->i_ctime;
630630
stat.size = inode->i_size;
631+
if (v4 && IS_I_VERSION(inode)) {
632+
stat.change_cookie = inode_query_iversion(inode);
633+
stat.result_mask |= STATX_CHANGE_COOKIE;
634+
}
631635
}
632636
if (v4)
633637
fhp->fh_pre_change = nfsd4_change_attribute(&stat, inode);
@@ -659,6 +663,10 @@ void fh_fill_post_attrs(struct svc_fh *fhp)
659663
if (err) {
660664
fhp->fh_post_saved = false;
661665
fhp->fh_post_attr.ctime = inode->i_ctime;
666+
if (v4 && IS_I_VERSION(inode)) {
667+
fhp->fh_post_attr.change_cookie = inode_query_iversion(inode);
668+
fhp->fh_post_attr.result_mask |= STATX_CHANGE_COOKIE;
669+
}
662670
} else
663671
fhp->fh_post_saved = true;
664672
if (v4)
@@ -748,3 +756,37 @@ enum fsid_source fsid_source(const struct svc_fh *fhp)
748756
return FSIDSOURCE_UUID;
749757
return FSIDSOURCE_DEV;
750758
}
759+
760+
/*
761+
* We could use i_version alone as the change attribute. However, i_version
762+
* can go backwards on a regular file after an unclean shutdown. On its own
763+
* that doesn't necessarily cause a problem, but if i_version goes backwards
764+
* and then is incremented again it could reuse a value that was previously
765+
* used before boot, and a client who queried the two values might incorrectly
766+
* assume nothing changed.
767+
*
768+
* By using both ctime and the i_version counter we guarantee that as long as
769+
* time doesn't go backwards we never reuse an old value. If the filesystem
770+
* advertises STATX_ATTR_CHANGE_MONOTONIC, then this mitigation is not
771+
* needed.
772+
*
773+
* We only need to do this for regular files as well. For directories, we
774+
* assume that the new change attr is always logged to stable storage in some
775+
* fashion before the results can be seen.
776+
*/
777+
u64 nfsd4_change_attribute(struct kstat *stat, struct inode *inode)
778+
{
779+
u64 chattr;
780+
781+
if (stat->result_mask & STATX_CHANGE_COOKIE) {
782+
chattr = stat->change_cookie;
783+
if (S_ISREG(inode->i_mode) &&
784+
!(stat->attributes & STATX_ATTR_CHANGE_MONOTONIC)) {
785+
chattr += (u64)stat->ctime.tv_sec << 30;
786+
chattr += stat->ctime.tv_nsec;
787+
}
788+
} else {
789+
chattr = time_to_chattr(&stat->ctime);
790+
}
791+
return chattr;
792+
}

fs/nfsd/nfsfh.h

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -293,34 +293,7 @@ static inline void fh_clear_pre_post_attrs(struct svc_fh *fhp)
293293
fhp->fh_pre_saved = false;
294294
}
295295

296-
/*
297-
* We could use i_version alone as the change attribute. However,
298-
* i_version can go backwards after a reboot. On its own that doesn't
299-
* necessarily cause a problem, but if i_version goes backwards and then
300-
* is incremented again it could reuse a value that was previously used
301-
* before boot, and a client who queried the two values might
302-
* incorrectly assume nothing changed.
303-
*
304-
* By using both ctime and the i_version counter we guarantee that as
305-
* long as time doesn't go backwards we never reuse an old value.
306-
*/
307-
static inline u64 nfsd4_change_attribute(struct kstat *stat,
308-
struct inode *inode)
309-
{
310-
if (inode->i_sb->s_export_op->fetch_iversion)
311-
return inode->i_sb->s_export_op->fetch_iversion(inode);
312-
else if (IS_I_VERSION(inode)) {
313-
u64 chattr;
314-
315-
chattr = stat->ctime.tv_sec;
316-
chattr <<= 30;
317-
chattr += stat->ctime.tv_nsec;
318-
chattr += inode_query_iversion(inode);
319-
return chattr;
320-
} else
321-
return time_to_chattr(&stat->ctime);
322-
}
323-
296+
u64 nfsd4_change_attribute(struct kstat *stat, struct inode *inode);
324297
extern void fh_fill_pre_attrs(struct svc_fh *fhp);
325298
extern void fh_fill_post_attrs(struct svc_fh *fhp);
326299
extern void fh_fill_both_attrs(struct svc_fh *fhp);

fs/nfsd/vfs.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,14 @@ static inline void fh_drop_write(struct svc_fh *fh)
170170

171171
static inline __be32 fh_getattr(const struct svc_fh *fh, struct kstat *stat)
172172
{
173+
u32 request_mask = STATX_BASIC_STATS;
173174
struct path p = {.mnt = fh->fh_export->ex_path.mnt,
174175
.dentry = fh->fh_dentry};
175-
return nfserrno(vfs_getattr(&p, stat, STATX_BASIC_STATS,
176+
177+
if (fh->fh_maxsize == NFS4_FHSIZE)
178+
request_mask |= (STATX_BTIME | STATX_CHANGE_COOKIE);
179+
180+
return nfserrno(vfs_getattr(&p, stat, request_mask,
176181
AT_STATX_SYNC_AS_STAT));
177182
}
178183

fs/stat.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <linux/syscalls.h>
1919
#include <linux/pagemap.h>
2020
#include <linux/compat.h>
21+
#include <linux/iversion.h>
2122

2223
#include <linux/uaccess.h>
2324
#include <asm/unistd.h>
@@ -122,6 +123,11 @@ int vfs_getattr_nosec(const struct path *path, struct kstat *stat,
122123
stat->attributes_mask |= (STATX_ATTR_AUTOMOUNT |
123124
STATX_ATTR_DAX);
124125

126+
if ((request_mask & STATX_CHANGE_COOKIE) && IS_I_VERSION(inode)) {
127+
stat->result_mask |= STATX_CHANGE_COOKIE;
128+
stat->change_cookie = inode_query_iversion(inode);
129+
}
130+
125131
mnt_userns = mnt_user_ns(path->mnt);
126132
if (inode->i_op->getattr)
127133
return inode->i_op->getattr(mnt_userns, path, stat,
@@ -602,9 +608,11 @@ cp_statx(const struct kstat *stat, struct statx __user *buffer)
602608

603609
memset(&tmp, 0, sizeof(tmp));
604610

605-
tmp.stx_mask = stat->result_mask;
611+
/* STATX_CHANGE_COOKIE is kernel-only for now */
612+
tmp.stx_mask = stat->result_mask & ~STATX_CHANGE_COOKIE;
606613
tmp.stx_blksize = stat->blksize;
607-
tmp.stx_attributes = stat->attributes;
614+
/* STATX_ATTR_CHANGE_MONOTONIC is kernel-only for now */
615+
tmp.stx_attributes = stat->attributes & ~STATX_ATTR_CHANGE_MONOTONIC;
608616
tmp.stx_nlink = stat->nlink;
609617
tmp.stx_uid = from_kuid_munged(current_user_ns(), stat->uid);
610618
tmp.stx_gid = from_kgid_munged(current_user_ns(), stat->gid);
@@ -643,6 +651,11 @@ int do_statx(int dfd, struct filename *filename, unsigned int flags,
643651
if ((flags & AT_STATX_SYNC_TYPE) == AT_STATX_SYNC_TYPE)
644652
return -EINVAL;
645653

654+
/* STATX_CHANGE_COOKIE is kernel-only for now. Ignore requests
655+
* from userland.
656+
*/
657+
mask &= ~STATX_CHANGE_COOKIE;
658+
646659
error = vfs_statx(dfd, filename, flags, &stat, mask);
647660
if (error)
648661
return error;

include/linux/exportfs.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,6 @@ struct export_operations {
213213
bool write, u32 *device_generation);
214214
int (*commit_blocks)(struct inode *inode, struct iomap *iomaps,
215215
int nr_iomaps, struct iattr *iattr);
216-
u64 (*fetch_iversion)(struct inode *);
217216
#define EXPORT_OP_NOWCC (0x1) /* don't collect v3 wcc data */
218217
#define EXPORT_OP_NOSUBTREECHK (0x2) /* no subtree checking */
219218
#define EXPORT_OP_CLOSE_BEFORE_UNLINK (0x4) /* close files before unlink */

0 commit comments

Comments
 (0)