diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index f594dac436a7..966f680ebbc8 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -2929,7 +2929,7 @@ static int access_cmp(const struct cred *a, const struct nfs_access_entry *b)
 	return 0;
 }
 
-static struct nfs_access_entry *nfs_access_search_rbtree(struct inode *inode, const struct cred *cred)
+struct nfs_access_entry *nfs_access_search_rbtree(struct inode *inode, const struct cred *cred)
 {
 	struct rb_node *n = NFS_I(inode)->access_cache.rb_node;
 
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 6b2cfa59a1a2..8f952f86b126 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -2177,6 +2177,18 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
 		nfsi->cache_validity |=
 			save_cache_validity & NFS_INO_INVALID_NLINK;
 
+	if (fattr->valid & NFS_ATTR_FATTR_ACCESS) {
+		if (!(invalid & NFS_INO_INVALID_ACCESS)) {
+			const struct cred *cred = current_cred();
+			struct nfs_access_entry *cache;
+
+			cache = nfs_access_search_rbtree(inode, cred);
+			if (cache != NULL && cache->mask != fattr->access) {
+				invalid |= NFS_INO_INVALID_ACCESS;
+			}
+		}
+	}
+
 	if (fattr->valid & NFS_ATTR_FATTR_SPACE_USED) {
 		/*
 		 * report the blocks in 512byte units
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 647fc3f547cb..10c271fe2c35 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -382,6 +382,8 @@ extern unsigned long nfs_access_cache_count(struct shrinker *shrink,
 					    struct shrink_control *sc);
 extern unsigned long nfs_access_cache_scan(struct shrinker *shrink,
 					   struct shrink_control *sc);
+extern struct nfs_access_entry *nfs_access_search_rbtree(struct inode *inode,
+					const struct cred *cred);
 struct dentry *nfs_lookup(struct inode *, struct dentry *, unsigned int);
 void nfs_d_prune_case_insensitive_aliases(struct inode *inode);
 int nfs_create(struct user_namespace *, struct inode *, struct dentry *,
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 86ed5c0142c3..7f8790ab5c7a 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -206,7 +206,8 @@ const u32 nfs4_fattr_bitmap[3] = {
 	| FATTR4_WORD1_TIME_ACCESS
 	| FATTR4_WORD1_TIME_METADATA
 	| FATTR4_WORD1_TIME_MODIFY
-	| FATTR4_WORD1_MOUNTED_ON_FILEID,
+	| FATTR4_WORD1_MOUNTED_ON_FILEID
+	| FATTR4_WORD1_ACCESS,
 #ifdef CONFIG_NFS_V4_SECURITY_LABEL
 	FATTR4_WORD2_SECURITY_LABEL
 #endif
@@ -3820,7 +3821,7 @@ static void nfs4_close_context(struct nfs_open_context *ctx, int is_sync)
 		nfs4_close_state(ctx->state, _nfs4_ctx_to_openmode(ctx));
 }
 
-#define FATTR4_WORD1_NFS40_MASK (2*FATTR4_WORD1_MOUNTED_ON_FILEID - 1UL)
+#define FATTR4_WORD1_NFS40_MASK (2*FATTR4_WORD1_ACCESS - 1UL)
 #define FATTR4_WORD2_NFS41_MASK (2*FATTR4_WORD2_SUPPATTR_EXCLCREAT - 1UL)
 #define FATTR4_WORD2_NFS42_MASK (2*FATTR4_WORD2_XATTR_SUPPORT - 1UL)
 
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index acfe5f4bda48..cf651e9b4a5d 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -4296,6 +4296,26 @@ static int decode_attr_xattrsupport(struct xdr_stream *xdr, uint32_t *bitmap,
 	return 0;
 }
 
+static int decode_attr_access(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *access)
+{
+	__be32 *p;
+	int ret = 0;
+
+	*access = 0;
+	if (unlikely(bitmap[1] & (FATTR4_WORD1_ACCESS - 1U)))
+		return -EIO;
+	if (likely(bitmap[1] & FATTR4_WORD1_ACCESS)) {
+		p = xdr_inline_decode(xdr, 4);
+		if (unlikely(!p))
+			return -EIO;
+		*access = be32_to_cpup(p);
+		bitmap[1] &= ~FATTR4_WORD1_ACCESS;
+		ret = NFS_ATTR_FATTR_ACCESS;
+	}
+	dprintk("%s: access=%u\n", __func__, (unsigned int)*access);
+	return ret;
+}
+
 static int verify_attr_len(struct xdr_stream *xdr, unsigned int savep, uint32_t attrlen)
 {
 	unsigned int attrwords = XDR_QUADLEN(attrlen);
@@ -4747,6 +4767,11 @@ static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap,
 		goto xdr_error;
 	fattr->valid |= status;
 
+	status = decode_attr_access(xdr, bitmap, &fattr->access);
+	if (status < 0)
+		goto xdr_error;
+	fattr->valid |= status;
+
 	status = -EIO;
 	if (unlikely(bitmap[1]))
 		goto xdr_error;
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 2b4ae858c89b..a338b3d9a872 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -3396,6 +3396,27 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
 		}
 		p = xdr_encode_hyper(p, ino);
 	}
+	if (bmval1 & FATTR4_WORD1_ACCESS) {
+		u32 access;
+		u32 supported;
+
+		access = NFS3_ACCESS_READ | NFS3_ACCESS_MODIFY | NFS3_ACCESS_EXTEND;
+		if (minorversion >= 2) {
+			access |= NFS4_ACCESS_XALIST | NFS4_ACCESS_XAREAD |
+					NFS4_ACCESS_XAWRITE;
+		}
+		if (S_ISDIR(d_inode(dentry)->i_mode))
+			access |= NFS3_ACCESS_DELETE | NFS3_ACCESS_LOOKUP;
+		else
+			access |= NFS3_ACCESS_EXECUTE;
+		status = nfsd_access(rqstp, fhp, &access, &supported);
+		if (status)
+			goto out;
+		p = xdr_reserve_space(xdr, 4);
+		if (!p)
+			goto out_resource;
+		*p++ = cpu_to_be32(access);
+	}
 #ifdef CONFIG_NFSD_PNFS
 	if (bmval1 & FATTR4_WORD1_FS_LAYOUT_TYPES) {
 		status = nfsd4_encode_layout_types(xdr, exp->ex_layout_types);
diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
index 93b42ef9ed91..648a4ec3e2d4 100644
--- a/fs/nfsd/nfsd.h
+++ b/fs/nfsd/nfsd.h
@@ -378,7 +378,8 @@ void		nfsd_lockd_shutdown(void);
  | FATTR4_WORD1_SPACE_AVAIL     | FATTR4_WORD1_SPACE_FREE   | FATTR4_WORD1_SPACE_TOTAL      \
  | FATTR4_WORD1_SPACE_USED      | FATTR4_WORD1_TIME_ACCESS  | FATTR4_WORD1_TIME_ACCESS_SET  \
  | FATTR4_WORD1_TIME_DELTA      | FATTR4_WORD1_TIME_METADATA   | FATTR4_WORD1_TIME_CREATE      \
- | FATTR4_WORD1_TIME_MODIFY     | FATTR4_WORD1_TIME_MODIFY_SET | FATTR4_WORD1_MOUNTED_ON_FILEID)
+ | FATTR4_WORD1_TIME_MODIFY     | FATTR4_WORD1_TIME_MODIFY_SET | FATTR4_WORD1_MOUNTED_ON_FILEID \
+ | FATTR4_WORD1_ACCESS)
 
 #define NFSD4_SUPPORTED_ATTRS_WORD2 0
 
diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index 730003c4f4af..63d0e31c6552 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -451,6 +451,7 @@ enum lock_type4 {
 #define FATTR4_WORD1_TIME_MODIFY        (1UL << 21)
 #define FATTR4_WORD1_TIME_MODIFY_SET    (1UL << 22)
 #define FATTR4_WORD1_MOUNTED_ON_FILEID  (1UL << 23)
+#define FATTR4_WORD1_ACCESS             (1UL << 24)
 #define FATTR4_WORD1_DACL               (1UL << 26)
 #define FATTR4_WORD1_SACL               (1UL << 27)
 #define FATTR4_WORD1_FS_LAYOUT_TYPES    (1UL << 30)
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index e86cf6642d21..4626e27588de 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -78,6 +78,7 @@ struct nfs_fattr {
 	struct nfs4_string	*group_name;
 	struct nfs4_threshold	*mdsthreshold;	/* pNFS threshold hints */
 	struct nfs4_label	*label;
+	__u32 access;
 };
 
 #define NFS_ATTR_FATTR_TYPE		(1U << 0)
@@ -106,6 +107,7 @@ struct nfs_fattr {
 #define NFS_ATTR_FATTR_OWNER_NAME	(1U << 23)
 #define NFS_ATTR_FATTR_GROUP_NAME	(1U << 24)
 #define NFS_ATTR_FATTR_V4_SECURITY_LABEL (1U << 25)
+#define NFS_ATTR_FATTR_ACCESS (1U << 26)
 
 #define NFS_ATTR_FATTR (NFS_ATTR_FATTR_TYPE \
 		| NFS_ATTR_FATTR_MODE \
