* instantiating the inode (v9fs_inode_from_fid)
         */
        acl = get_cached_acl(inode, type);
-       BUG_ON(acl == ACL_NOT_CACHED);
+       BUG_ON(is_uncached_acl(acl));
        return acl;
 }
 
 
        }
        kfree(value);
 
-       if (!IS_ERR(acl))
-               set_cached_acl(inode, type, acl);
-
        return acl;
 }
 
 
        spin_lock(&ci->i_ceph_lock);
        if (__ceph_caps_issued_mask(ci, CEPH_CAP_XATTR_SHARED, 0))
                set_cached_acl(inode, type, acl);
+       else
+               forget_cached_acl(inode, type);
        spin_unlock(&ci->i_ceph_lock);
 }
 
 
                acl = ERR_PTR(retval);
        kfree(value);
 
-       if (!IS_ERR(acl))
-               set_cached_acl(inode, type, acl);
-
        return acl;
 }
 
 
                acl = ERR_PTR(retval);
        kfree(value);
 
-       if (!IS_ERR(acl))
-               set_cached_acl(inode, type, acl);
-
        return acl;
 }
 
 
                acl = ERR_PTR(retval);
        kfree(value);
 
-       if (!IS_ERR(acl))
-               set_cached_acl(inode, type, acl);
-
        return acl;
 }
 
 
 
        hfsplus_destroy_attr_entry((hfsplus_attr_entry *)value);
 
-       if (!IS_ERR(acl))
-               set_cached_acl(inode, type, acl);
-
        return acl;
 }
 
 
        }
 
 #ifdef CONFIG_FS_POSIX_ACL
-       if (inode->i_acl && inode->i_acl != ACL_NOT_CACHED)
+       if (inode->i_acl && !is_uncached_acl(inode->i_acl))
                posix_acl_release(inode->i_acl);
-       if (inode->i_default_acl && inode->i_default_acl != ACL_NOT_CACHED)
+       if (inode->i_default_acl && !is_uncached_acl(inode->i_default_acl))
                posix_acl_release(inode->i_default_acl);
 #endif
        this_cpu_dec(nr_inodes);
 
                acl = ERR_PTR(rc);
        }
        kfree(value);
-       if (!IS_ERR(acl))
-               set_cached_acl(inode, type, acl);
        return acl;
 }
 
 
                acl = posix_acl_from_xattr(&init_user_ns, value, size);
        }
        kfree(value);
-       if (!IS_ERR(acl))
-               set_cached_acl(inode, type, acl);
        return acl;
 }
 
 
                if (!acl)
                        return -EAGAIN;
                /* no ->get_acl() calls in RCU mode... */
-               if (acl == ACL_NOT_CACHED)
+               if (is_uncached_acl(acl))
                        return -ECHILD;
                return posix_acl_permission(inode, acl, mask & ~MAY_NOT_BLOCK);
        }
 
 
 #define NFSDBG_FACILITY        NFSDBG_PROC
 
+/*
+ * nfs3_prepare_get_acl, nfs3_complete_get_acl, nfs3_abort_get_acl: Helpers for
+ * caching get_acl results in a race-free way.  See fs/posix_acl.c:get_acl()
+ * for explanations.
+ */
+static void nfs3_prepare_get_acl(struct posix_acl **p)
+{
+       struct posix_acl *sentinel = uncached_acl_sentinel(current);
+
+       if (cmpxchg(p, ACL_NOT_CACHED, sentinel) != ACL_NOT_CACHED) {
+               /* Not the first reader or sentinel already in place. */
+       }
+}
+
+static void nfs3_complete_get_acl(struct posix_acl **p, struct posix_acl *acl)
+{
+       struct posix_acl *sentinel = uncached_acl_sentinel(current);
+
+       /* Only cache the ACL if our sentinel is still in place. */
+       posix_acl_dup(acl);
+       if (cmpxchg(p, sentinel, acl) != sentinel)
+               posix_acl_release(acl);
+}
+
+static void nfs3_abort_get_acl(struct posix_acl **p)
+{
+       struct posix_acl *sentinel = uncached_acl_sentinel(current);
+
+       /* Remove our sentinel upon failure. */
+       cmpxchg(p, sentinel, ACL_NOT_CACHED);
+}
+
 struct posix_acl *nfs3_get_acl(struct inode *inode, int type)
 {
        struct nfs_server *server = NFS_SERVER(inode);
        if (res.fattr == NULL)
                return ERR_PTR(-ENOMEM);
 
+       if (args.mask & NFS_ACL)
+               nfs3_prepare_get_acl(&inode->i_acl);
+       if (args.mask & NFS_DFACL)
+               nfs3_prepare_get_acl(&inode->i_default_acl);
+
        status = rpc_call_sync(server->client_acl, &msg, 0);
        dprintk("NFS reply getacl: %d\n", status);
 
        }
 
        if (res.mask & NFS_ACL)
-               set_cached_acl(inode, ACL_TYPE_ACCESS, res.acl_access);
+               nfs3_complete_get_acl(&inode->i_acl, res.acl_access);
        else
                forget_cached_acl(inode, ACL_TYPE_ACCESS);
 
        if (res.mask & NFS_DFACL)
-               set_cached_acl(inode, ACL_TYPE_DEFAULT, res.acl_default);
+               nfs3_complete_get_acl(&inode->i_default_acl, res.acl_default);
        else
                forget_cached_acl(inode, ACL_TYPE_DEFAULT);
 
        }
 
 getout:
+       nfs3_abort_get_acl(&inode->i_acl);
+       nfs3_abort_get_acl(&inode->i_default_acl);
        posix_acl_release(res.acl_access);
        posix_acl_release(res.acl_default);
        nfs_free_fattr(res.fattr);
 
 #include "uptodate.h"
 #include "quota.h"
 #include "refcounttree.h"
+#include "acl.h"
 
 #include "buffer_head_io.h"
 
                filemap_fdatawait(mapping);
        }
 
+       forget_all_cached_acls(inode);
+
 out:
        return UNBLOCK_CONTINUE;
 }
 
 struct posix_acl *get_cached_acl(struct inode *inode, int type)
 {
        struct posix_acl **p = acl_by_type(inode, type);
-       struct posix_acl *acl = ACCESS_ONCE(*p);
-       if (acl) {
-               spin_lock(&inode->i_lock);
-               acl = *p;
-               if (acl != ACL_NOT_CACHED)
-                       acl = posix_acl_dup(acl);
-               spin_unlock(&inode->i_lock);
+       struct posix_acl *acl;
+
+       for (;;) {
+               rcu_read_lock();
+               acl = rcu_dereference(*p);
+               if (!acl || is_uncached_acl(acl) ||
+                   atomic_inc_not_zero(&acl->a_refcount))
+                       break;
+               rcu_read_unlock();
+               cpu_relax();
        }
+       rcu_read_unlock();
        return acl;
 }
 EXPORT_SYMBOL(get_cached_acl);
 {
        struct posix_acl **p = acl_by_type(inode, type);
        struct posix_acl *old;
-       spin_lock(&inode->i_lock);
-       old = *p;
-       rcu_assign_pointer(*p, posix_acl_dup(acl));
-       spin_unlock(&inode->i_lock);
-       if (old != ACL_NOT_CACHED)
+
+       old = xchg(p, posix_acl_dup(acl));
+       if (!is_uncached_acl(old))
                posix_acl_release(old);
 }
 EXPORT_SYMBOL(set_cached_acl);
 
-void forget_cached_acl(struct inode *inode, int type)
+static void __forget_cached_acl(struct posix_acl **p)
 {
-       struct posix_acl **p = acl_by_type(inode, type);
        struct posix_acl *old;
-       spin_lock(&inode->i_lock);
-       old = *p;
-       *p = ACL_NOT_CACHED;
-       spin_unlock(&inode->i_lock);
-       if (old != ACL_NOT_CACHED)
+
+       old = xchg(p, ACL_NOT_CACHED);
+       if (!is_uncached_acl(old))
                posix_acl_release(old);
 }
+
+void forget_cached_acl(struct inode *inode, int type)
+{
+       __forget_cached_acl(acl_by_type(inode, type));
+}
 EXPORT_SYMBOL(forget_cached_acl);
 
 void forget_all_cached_acls(struct inode *inode)
 {
-       struct posix_acl *old_access, *old_default;
-       spin_lock(&inode->i_lock);
-       old_access = inode->i_acl;
-       old_default = inode->i_default_acl;
-       inode->i_acl = inode->i_default_acl = ACL_NOT_CACHED;
-       spin_unlock(&inode->i_lock);
-       if (old_access != ACL_NOT_CACHED)
-               posix_acl_release(old_access);
-       if (old_default != ACL_NOT_CACHED)
-               posix_acl_release(old_default);
+       __forget_cached_acl(&inode->i_acl);
+       __forget_cached_acl(&inode->i_default_acl);
 }
 EXPORT_SYMBOL(forget_all_cached_acls);
 
 struct posix_acl *get_acl(struct inode *inode, int type)
 {
+       void *sentinel;
+       struct posix_acl **p;
        struct posix_acl *acl;
 
+       /*
+        * The sentinel is used to detect when another operation like
+        * set_cached_acl() or forget_cached_acl() races with get_acl().
+        * It is guaranteed that is_uncached_acl(sentinel) is true.
+        */
+
        acl = get_cached_acl(inode, type);
-       if (acl != ACL_NOT_CACHED)
+       if (!is_uncached_acl(acl))
                return acl;
 
        if (!IS_POSIXACL(inode))
                return NULL;
 
+       sentinel = uncached_acl_sentinel(current);
+       p = acl_by_type(inode, type);
+
        /*
-        * A filesystem can force a ACL callback by just never filling the
-        * ACL cache. But normally you'd fill the cache either at inode
-        * instantiation time, or on the first ->get_acl call.
+        * If the ACL isn't being read yet, set our sentinel.  Otherwise, the
+        * current value of the ACL will not be ACL_NOT_CACHED and so our own
+        * sentinel will not be set; another task will update the cache.  We
+        * could wait for that other task to complete its job, but it's easier
+        * to just call ->get_acl to fetch the ACL ourself.  (This is going to
+        * be an unlikely race.)
+        */
+       if (cmpxchg(p, ACL_NOT_CACHED, sentinel) != ACL_NOT_CACHED)
+               /* fall through */ ;
+
+       /*
+        * Normally, the ACL returned by ->get_acl will be cached.
+        * A filesystem can prevent that by calling
+        * forget_cached_acl(inode, type) in ->get_acl.
         *
         * If the filesystem doesn't have a get_acl() function at all, we'll
         * just create the negative cache entry.
                set_cached_acl(inode, type, NULL);
                return NULL;
        }
-       return inode->i_op->get_acl(inode, type);
+       acl = inode->i_op->get_acl(inode, type);
+
+       if (IS_ERR(acl)) {
+               /*
+                * Remove our sentinel so that we don't block future attempts
+                * to cache the ACL.
+                */
+               cmpxchg(p, sentinel, ACL_NOT_CACHED);
+               return acl;
+       }
+
+       /*
+        * Cache the result, but only if our sentinel is still in place.
+        */
+       posix_acl_dup(acl);
+       if (unlikely(cmpxchg(p, sentinel, acl) != sentinel))
+               posix_acl_release(acl);
+       return acl;
 }
 EXPORT_SYMBOL(get_acl);
 
 
 
        size = reiserfs_xattr_get(inode, name, NULL, 0);
        if (size < 0) {
-               if (size == -ENODATA || size == -ENOSYS) {
-                       set_cached_acl(inode, type, NULL);
+               if (size == -ENODATA || size == -ENOSYS)
                        return NULL;
-               }
                return ERR_PTR(size);
        }
 
        } else {
                acl = reiserfs_posix_acl_from_disk(value, retval);
        }
-       if (!IS_ERR(acl))
-               set_cached_acl(inode, type, acl);
 
        kfree(value);
        return acl;
 
        if (error) {
                /*
                 * If the attribute doesn't exist make sure we have a negative
-                * cache entry, for any other error assume it is transient and
-                * leave the cache entry as ACL_NOT_CACHED.
+                * cache entry, for any other error assume it is transient.
                 */
-               if (error == -ENOATTR)
-                       goto out_update_cache;
-               acl = ERR_PTR(error);
-               goto out;
+               if (error != -ENOATTR)
+                       acl = ERR_PTR(error);
+       } else  {
+               acl = xfs_acl_from_disk(xfs_acl, len,
+                                       XFS_ACL_MAX_ENTRIES(ip->i_mount));
        }
-
-       acl = xfs_acl_from_disk(xfs_acl, len, XFS_ACL_MAX_ENTRIES(ip->i_mount));
-       if (IS_ERR(acl))
-               goto out;
-
-out_update_cache:
-       set_cached_acl(inode, type, acl);
-out:
        kmem_free(xfs_acl);
        return acl;
 }
 
 struct posix_acl;
 #define ACL_NOT_CACHED ((void *)(-1))
 
+static inline struct posix_acl *
+uncached_acl_sentinel(struct task_struct *task)
+{
+       return (void *)task + 1;
+}
+
+static inline bool
+is_uncached_acl(struct posix_acl *acl)
+{
+       return (long)acl & 1;
+}
+
 #define IOP_FASTPERM   0x0001
 #define IOP_LOOKUP     0x0002
 #define IOP_NOFOLLOW   0x0004