[PATCH 3/4] posix_acl: get_acl: Cleanups

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Move the prepare_get_acl, complete_get_acl, and abort_get_acl helpers
from fs/nfs/nfs3acl.c to fs/posix_acl.c: using the same helpers in
get_acl and in nfs3_get_acl makes the code easier to read.

In get_acl, move the check for NULL get_acl inode operations above
__prepare_get_acl.

Signed-off-by: Andreas Gruenbacher <agruenba@xxxxxxxxxx>
---
 fs/nfs/nfs3acl.c          |  44 +++---------------
 fs/posix_acl.c            | 116 ++++++++++++++++++++++++++++++++--------------
 include/linux/posix_acl.h |   4 ++
 3 files changed, 90 insertions(+), 74 deletions(-)

diff --git a/fs/nfs/nfs3acl.c b/fs/nfs/nfs3acl.c
index 720d92f5..b623801 100644
--- a/fs/nfs/nfs3acl.c
+++ b/fs/nfs/nfs3acl.c
@@ -11,38 +11,6 @@
 
 #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);
@@ -88,9 +56,9 @@ struct posix_acl *nfs3_get_acl(struct inode *inode, int type)
 		return ERR_PTR(-ENOMEM);
 
 	if (args.mask & NFS_ACL)
-		nfs3_prepare_get_acl(&inode->i_acl);
+		prepare_get_acl(inode, ACL_TYPE_ACCESS);
 	if (args.mask & NFS_DFACL)
-		nfs3_prepare_get_acl(&inode->i_default_acl);
+		prepare_get_acl(inode, ACL_TYPE_DEFAULT);
 
 	status = rpc_call_sync(server->client_acl, &msg, 0);
 	dprintk("NFS reply getacl: %d\n", status);
@@ -126,12 +94,12 @@ struct posix_acl *nfs3_get_acl(struct inode *inode, int type)
 	}
 
 	if (res.mask & NFS_ACL)
-		nfs3_complete_get_acl(&inode->i_acl, res.acl_access);
+		complete_get_acl(inode, ACL_TYPE_ACCESS, res.acl_access);
 	else
 		forget_cached_acl(inode, ACL_TYPE_ACCESS);
 
 	if (res.mask & NFS_DFACL)
-		nfs3_complete_get_acl(&inode->i_default_acl, res.acl_default);
+		complete_get_acl(inode, ACL_TYPE_DEFAULT, res.acl_default);
 	else
 		forget_cached_acl(inode, ACL_TYPE_DEFAULT);
 
@@ -145,8 +113,8 @@ struct posix_acl *nfs3_get_acl(struct inode *inode, int type)
 	}
 
 getout:
-	nfs3_abort_get_acl(&inode->i_acl);
-	nfs3_abort_get_acl(&inode->i_default_acl);
+	abort_get_acl(inode, ACL_TYPE_ACCESS);
+	abort_get_acl(inode, ACL_TYPE_DEFAULT);
 	posix_acl_release(res.acl_access);
 	posix_acl_release(res.acl_default);
 	nfs_free_fattr(res.fattr);
diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index 59d47ab0..0be840e 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -91,18 +91,80 @@ void forget_all_cached_acls(struct inode *inode)
 }
 EXPORT_SYMBOL(forget_all_cached_acls);
 
+static bool __prepare_get_acl(struct posix_acl **p)
+{
+	struct posix_acl *sentinel = uncached_acl_sentinel(current);
+
+	return cmpxchg(p, ACL_NOT_CACHED, sentinel) == ACL_NOT_CACHED;
+}
+
+bool prepare_get_acl(struct inode *inode, int type)
+{
+	return __prepare_get_acl(acl_by_type(inode, type));
+}
+EXPORT_SYMBOL(prepare_get_acl);
+
+/* Only cache the ACL if our sentinel is still in place. */
+static void __complete_get_acl(struct posix_acl **p, struct posix_acl *acl)
+{
+	struct posix_acl *sentinel = uncached_acl_sentinel(current);
+
+	posix_acl_dup(acl);
+	if (cmpxchg(p, sentinel, acl) != sentinel)
+		posix_acl_release(acl);
+}
+
+void complete_get_acl(struct inode *inode, int type, struct posix_acl *acl)
+{
+	return __complete_get_acl(acl_by_type(inode, type), acl);
+}
+EXPORT_SYMBOL(complete_get_acl);
+
+/* Remove our sentinel upon failure. */
+/*
+ * Remove our sentinel so that we don't block future attempts
+ * to cache the ACL.
+ */
+static void __abort_get_acl(struct posix_acl **p)
+{
+	struct posix_acl *sentinel = uncached_acl_sentinel(current);
+
+	cmpxchg(p, sentinel, ACL_NOT_CACHED);
+}
+
+void abort_get_acl(struct inode *inode, int type)
+{
+	__abort_get_acl(acl_by_type(inode, type));
+}
+EXPORT_SYMBOL(abort_get_acl);
+
+
+/**
+ * get_acl  -  Get the ACL of an inode
+ *
+ * This function is called without holding the inode mutex, so when fetching an
+ * uncached ACL from the filesystem, we can race with other get_acl or set_acl
+ * requests.  We must make sure not to override ACLs set with set_acl in the
+ * meantime.
+ *
+ * To do that, we change ->i_acl (or ->i_default_acl) from ACL_NOT_CACHED to a
+ * sentinel value that indicates that get_acl is in progress (prepare_get_acl).
+ * For these sentinel values, is_uncached_acl(sentinel) is always true.
+ *
+ * When we get back an ACL from the filesystem, we update the cached ACL only
+ * if our sentinel value is still in place ( complete_get_acl).
+ *
+ * A competing set_acl will replace our sentinel with the new ACL, and
+ * complete_get_acl will leave that new ACL untouched.
+ *
+ * When get_acl fails, we remove our sentinel so that future attempts to cache
+ * the ACL can succeed (abort_get_acl).
+ */
 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 (!is_uncached_acl(acl))
 		return acl;
@@ -110,25 +172,7 @@ struct posix_acl *get_acl(struct inode *inode, int type)
 	if (!IS_POSIXACL(inode))
 		return NULL;
 
-	sentinel = uncached_acl_sentinel(current);
-	p = acl_by_type(inode, type);
-
 	/*
-	 * 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.
 	 */
@@ -136,23 +180,23 @@ struct posix_acl *get_acl(struct inode *inode, int type)
 		set_cached_acl(inode, type, NULL);
 		return NULL;
 	}
+
+	p = acl_by_type(inode, type);
+	__prepare_get_acl(p);
+
+	/*
+	 * 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.
+	 */
 	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);
+		__abort_get_acl(p);
 		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);
+	__complete_get_acl(p, acl);
 	return acl;
 }
 EXPORT_SYMBOL(get_acl);
diff --git a/include/linux/posix_acl.h b/include/linux/posix_acl.h
index d5d3d74..b150e76 100644
--- a/include/linux/posix_acl.h
+++ b/include/linux/posix_acl.h
@@ -136,6 +136,10 @@ static inline void forget_all_cached_acls(struct inode *inode)
 }
 #endif /* CONFIG_FS_POSIX_ACL */
 
+bool prepare_get_acl(struct inode *, int);
+void complete_get_acl(struct inode *, int, struct posix_acl *);
+void abort_get_acl(struct inode *, int);
+
 struct posix_acl *get_acl(struct inode *inode, int type);
 
 #endif  /* __LINUX_POSIX_ACL_H */
-- 
2.7.4

--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux