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