summaryrefslogtreecommitdiffstats
path: root/fs/nfsd/state.h
diff options
context:
space:
mode:
authorJeff Layton2014-07-10 20:07:34 +0200
committerJ. Bruce Fields2014-07-11 17:06:32 +0200
commitbaeb4ff0e50281db6925223a096a506f02993b88 (patch)
treec0efdbc1d506b5e611cee607a860c1056ef2fb94 /fs/nfsd/state.h
parentnfsd: always hold the fi_lock when bumping fi_access refcounts (diff)
downloadkernel-qcow2-linux-baeb4ff0e50281db6925223a096a506f02993b88.tar.gz
kernel-qcow2-linux-baeb4ff0e50281db6925223a096a506f02993b88.tar.xz
kernel-qcow2-linux-baeb4ff0e50281db6925223a096a506f02993b88.zip
nfsd: make deny mode enforcement more efficient and close races in it
The current enforcement of deny modes is both inefficient and scattered across several places, which makes it hard to guarantee atomicity. The inefficiency is a problem now, and the lack of atomicity will mean races once the client_mutex is removed. First, we address the inefficiency. We have to track deny modes on a per-stateid basis to ensure that open downgrades are sane, but when the server goes to enforce them it has to walk the entire list of stateids and check against each one. Instead of doing that, maintain a per-nfs4_file deny mode. When a file is opened, we simply set any deny bits in that mode that were specified in the OPEN call. We can then use that unified deny mode to do a simple check to see whether there are any conflicts without needing to walk the entire stateid list. The only time we'll need to walk the entire list of stateids is when a stateid that has a deny mode on it is being released, or one is having its deny mode downgraded. In that case, we must walk the entire list and recalculate the fi_share_deny field. Since deny modes are pretty rare today, this should be very rare under normal workloads. To address the potential for races once the client_mutex is removed, protect fi_share_deny with the fi_lock. In nfs4_get_vfs_file, check to make sure that any deny mode we want to apply won't conflict with existing access. If that's ok, then have nfs4_file_get_access check that new access to the file won't conflict with existing deny modes. If that also passes, then get file access references, set the correct access and deny bits in the stateid, and update the fi_share_deny field. If opening the file or truncating it fails, then unwind the whole mess and return the appropriate error. Signed-off-by: Jeff Layton <jlayton@primarydata.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs/nfsd/state.h')
-rw-r--r--fs/nfsd/state.h1
1 files changed, 1 insertions, 0 deletions
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 72aee4b4f1ae..015b972da8ba 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -391,6 +391,7 @@ struct nfs4_file {
* + 1 to both of the above if NFS4_SHARE_ACCESS_BOTH is set.
*/
atomic_t fi_access[2];
+ u32 fi_share_deny;
struct file *fi_deleg_file;
struct file_lock *fi_lease;
atomic_t fi_delegees;