Commit de630176 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull i_version updates from Jeff Layton:
 "This overhauls how we handle i_version queries from nfsd.

  Instead of having special routines and grabbing the i_version field
  directly out of the inode in some cases, we've moved most of the
  handling into the various filesystems' getattr operations. As a bonus,
  this makes ceph's change attribute usable by knfsd as well.

  This should pave the way for future work to make this value queryable
  by userland, and to make it more resilient against rolling back on a
  crash"

* tag 'iversion-v6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/jlayton/linux:
  nfsd: remove fetch_iversion export operation
  nfsd: use the getattr operation to fetch i_version
  nfsd: move nfsd4_change_attribute to nfsfh.c
  ceph: report the inode version in getattr if requested
  nfs: report the inode version in getattr if requested
  vfs: plumb i_version handling into struct kstat
  fs: clarify when the i_version counter must be updated
  fs: uninline inode_query_iversion
parents 575a7e0f 58a033c9
Loading
Loading
Loading
Loading
+11 −5
Original line number Diff line number Diff line
@@ -2417,10 +2417,10 @@ static int statx_to_caps(u32 want, umode_t mode)
{
	int mask = 0;

	if (want & (STATX_MODE|STATX_UID|STATX_GID|STATX_CTIME|STATX_BTIME))
	if (want & (STATX_MODE|STATX_UID|STATX_GID|STATX_CTIME|STATX_BTIME|STATX_CHANGE_COOKIE))
		mask |= CEPH_CAP_AUTH_SHARED;

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

	if (want & (STATX_ATIME|STATX_MTIME|STATX_CTIME|STATX_SIZE|
		    STATX_BLOCKS))
	if (want & (STATX_ATIME|STATX_MTIME|STATX_CTIME|STATX_SIZE|STATX_BLOCKS|STATX_CHANGE_COOKIE))
		mask |= CEPH_CAP_FILE_SHARED;

	if (want & (STATX_CTIME))
	if (want & (STATX_CTIME|STATX_CHANGE_COOKIE))
		mask |= CEPH_CAP_XATTR_SHARED;

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

	if (request_mask & STATX_CHANGE_COOKIE) {
		stat->change_cookie = inode_peek_iversion_raw(inode);
		valid_mask |= STATX_CHANGE_COOKIE;
	}

	if (ceph_snap(inode) == CEPH_NOSNAP)
		stat->dev = sb->s_dev;
	else
@@ -2519,6 +2523,8 @@ int ceph_getattr(struct user_namespace *mnt_userns, const struct path *path,
			stat->nlink = 1 + 1 + ci->i_subdirs;
	}

	stat->attributes_mask |= STATX_ATTR_CHANGE_MONOTONIC;
	stat->attributes |= STATX_ATTR_CHANGE_MONOTONIC;
	stat->result_mask = request_mask & valid_mask;
	return err;
}
+36 −0
Original line number Diff line number Diff line
@@ -1582,3 +1582,39 @@ bool inode_maybe_inc_iversion(struct inode *inode, bool force)
	return true;
}
EXPORT_SYMBOL(inode_maybe_inc_iversion);

/**
 * 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.
 *
 * In this implementation, we fetch the current value, set the QUERIED flag and
 * then try to swap it into place with a cmpxchg, if it wasn't already set. If
 * that fails, we try again with the newly fetched value from the cmpxchg.
 */
u64 inode_query_iversion(struct inode *inode)
{
	u64 cur, new;

	cur = inode_peek_iversion_raw(inode);
	do {
		/* If flag is already set, then no need to swap */
		if (cur & I_VERSION_QUERIED) {
			/*
			 * This barrier (and the implicit barrier in the
			 * cmpxchg below) pairs with the barrier in
			 * inode_maybe_inc_iversion().
			 */
			smp_mb();
			break;
		}

		new = cur | I_VERSION_QUERIED;
	} while (!atomic64_try_cmpxchg(&inode->i_version, &cur, new));
	return cur >> I_VERSION_QUERIED_SHIFT;
}
EXPORT_SYMBOL(inode_query_iversion);
+0 −7
Original line number Diff line number Diff line
@@ -145,17 +145,10 @@ nfs_get_parent(struct dentry *dentry)
	return parent;
}

static u64 nfs_fetch_iversion(struct inode *inode)
{
	nfs_revalidate_inode(inode, NFS_INO_INVALID_CHANGE);
	return inode_peek_iversion_raw(inode);
}

const struct export_operations nfs_export_ops = {
	.encode_fh = nfs_encode_fh,
	.fh_to_dentry = nfs_fh_to_dentry,
	.get_parent = nfs_get_parent,
	.fetch_iversion = nfs_fetch_iversion,
	.flags = EXPORT_OP_NOWCC|EXPORT_OP_NOSUBTREECHK|
		EXPORT_OP_CLOSE_BEFORE_UNLINK|EXPORT_OP_REMOTE_FS|
		EXPORT_OP_NOATOMIC_ATTR,
+12 −4
Original line number Diff line number Diff line
@@ -825,6 +825,8 @@ static u32 nfs_get_valid_attrmask(struct inode *inode)
		reply_mask |= STATX_UID | STATX_GID;
	if (!(cache_validity & NFS_INO_INVALID_BLOCKS))
		reply_mask |= STATX_BLOCKS;
	if (!(cache_validity & NFS_INO_INVALID_CHANGE))
		reply_mask |= STATX_CHANGE_COOKIE;
	return reply_mask;
}

@@ -843,7 +845,8 @@ int nfs_getattr(struct user_namespace *mnt_userns, const struct path *path,

	request_mask &= STATX_TYPE | STATX_MODE | STATX_NLINK | STATX_UID |
			STATX_GID | STATX_ATIME | STATX_MTIME | STATX_CTIME |
			STATX_INO | STATX_SIZE | STATX_BLOCKS;
			STATX_INO | STATX_SIZE | STATX_BLOCKS | STATX_BTIME |
			STATX_CHANGE_COOKIE;

	if ((query_flags & AT_STATX_DONT_SYNC) && !force_sync) {
		if (readdirplus_enabled)
@@ -851,8 +854,8 @@ int nfs_getattr(struct user_namespace *mnt_userns, const struct path *path,
		goto out_no_revalidate;
	}

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

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

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

	generic_fillattr(&init_user_ns, inode, stat);
	stat->ino = nfs_compat_user_ino64(NFS_FILEID(inode));
	stat->change_cookie = inode_peek_iversion_raw(inode);
	stat->attributes_mask |= STATX_ATTR_CHANGE_MONOTONIC;
	if (server->change_attr_type != NFS4_CHANGE_TYPE_IS_UNDEFINED)
		stat->attributes |= STATX_ATTR_CHANGE_MONOTONIC;
	if (S_ISDIR(inode->i_mode))
		stat->blksize = NFS_SERVER(inode)->dtsize;
out:
+3 −1
Original line number Diff line number Diff line
@@ -2965,7 +2965,9 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
			goto out;
	}

	err = vfs_getattr(&path, &stat, STATX_BASIC_STATS, AT_STATX_SYNC_AS_STAT);
	err = vfs_getattr(&path, &stat,
			  STATX_BASIC_STATS | STATX_BTIME | STATX_CHANGE_COOKIE,
			  AT_STATX_SYNC_AS_STAT);
	if (err)
		goto out_nfserr;
	if (!(stat.result_mask & STATX_BTIME))
Loading