diff mbox

[v2,10/18] nfsd: handle NFSD_MAY_NOT_BREAK_LEASE in open file cache

Message ID 1438809216-4846-11-git-send-email-jeff.layton@primarydata.com (mailing list archive)
State New, archived
Headers show

Commit Message

Jeff Layton Aug. 5, 2015, 9:13 p.m. UTC
The NFSD_MAY_NOT_BREAK_LEASE flag needs special handling. If we open a
file in order to do (e.g.) a COMMIT then we don't want to break any
leases, but subsequent READ/WRITE operations must break the leases.

If we construct a new cache entry with a set of may flags that have
NFSD_MAY_NOT_BREAK_LEASE set, then set flags in the cache entry that
indicate that subsequent users of this file must break leases before
using it if they do not have NFSD_MAY_NOT_BREAK_LEASE set.

Note that because NFSD_MAY_READ opens do not break read leases, we
must track what sort of lease breaks have been done. If we're breaking
leases for read, then we still need to do a lease break for write if
it's a R/W open and a writer comes along. Lease breaks for write
however imply a read lease break so we can clear both flags in that
event.

Signed-off-by: Jeff Layton <jeff.layton@primarydata.com>
---
 fs/nfsd/filecache.c | 40 ++++++++++++++++++++++++++++++++++++----
 fs/nfsd/filecache.h |  2 ++
 fs/nfsd/vfs.c       |  3 ++-
 fs/nfsd/vfs.h       |  1 +
 4 files changed, 41 insertions(+), 5 deletions(-)
diff mbox

Patch

diff --git a/fs/nfsd/filecache.c b/fs/nfsd/filecache.c
index 084e56726318..0adcc674441d 100644
--- a/fs/nfsd/filecache.c
+++ b/fs/nfsd/filecache.c
@@ -68,6 +68,12 @@  nfsd_file_alloc(struct knfsd_fh *fh, unsigned int may, unsigned int hashval)
 		nf->nf_hashval = hashval;
 		atomic_set(&nf->nf_ref, 1);
 		nf->nf_may = NFSD_FILE_MAY_MASK & may;
+		if (may & NFSD_MAY_NOT_BREAK_LEASE) {
+			if (may & NFSD_MAY_WRITE)
+				__set_bit(NFSD_FILE_BREAK_WRITE, &nf->nf_flags);
+			if (may & NFSD_MAY_READ)
+				__set_bit(NFSD_FILE_BREAK_READ, &nf->nf_flags);
+		}
 	}
 	return nf;
 }
@@ -269,8 +275,6 @@  nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
 	struct knfsd_fh *fh = &fhp->fh_handle;
 	unsigned int hashval = file_hashval(fh);
 
-	/* Mask off any extraneous bits */
-	may_flags &= NFSD_FILE_MAY_MASK;
 retry:
 	rcu_read_lock();
 	nf = nfsd_file_find_locked(fh, may_flags, hashval);
@@ -311,7 +315,7 @@  wait_for_construction:
 		 * MAY flags are equal. Otherwise, we put the reference and try
 		 * again.
 		 */
-		if (may_flags != nf->nf_may) {
+		if ((may_flags & NFSD_FILE_MAY_MASK) != nf->nf_may) {
 			nfsd_file_put(nf);
 			goto retry;
 		}
@@ -319,6 +323,18 @@  wait_for_construction:
 		/* try to take over construction for this file */
 		if (test_and_set_bit(NFSD_FILE_PENDING, &nf->nf_flags))
 			goto wait_for_construction;
+
+		/* sync up the BREAK_* flags with our may_flags */
+		if (may_flags & NFSD_MAY_NOT_BREAK_LEASE) {
+			if (may_flags & NFSD_MAY_WRITE)
+				set_bit(NFSD_FILE_BREAK_WRITE, &nf->nf_flags);
+			if (may_flags & NFSD_MAY_READ)
+				set_bit(NFSD_FILE_BREAK_READ, &nf->nf_flags);
+		} else {
+			clear_bit(NFSD_FILE_BREAK_WRITE, &nf->nf_flags);
+			clear_bit(NFSD_FILE_BREAK_READ, &nf->nf_flags);
+		}
+
 		goto open_file;
 	}
 
@@ -330,7 +346,23 @@  wait_for_construction:
 	status = fh_verify(rqstp, fhp, S_IFREG, may_flags);
 	if (status == nfs_ok)
 		status = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry,
-						may_flags|NFSD_MAY_OWNER_OVERRIDE);
+					may_flags|NFSD_MAY_OWNER_OVERRIDE);
+
+	if (status == nfs_ok && !(may_flags & NFSD_MAY_NOT_BREAK_LEASE)) {
+		bool write = (may_flags & NFSD_MAY_WRITE);
+
+		if (test_bit(NFSD_FILE_BREAK_READ, &nf->nf_flags) ||
+		    (test_bit(NFSD_FILE_BREAK_WRITE, &nf->nf_flags) && write)) {
+			status = nfserrno(nfsd_open_break_lease(
+					file_inode(nf->nf_file), may_flags));
+			if (status == nfs_ok) {
+				clear_bit(NFSD_FILE_BREAK_READ, &nf->nf_flags);
+				if (write)
+					clear_bit(NFSD_FILE_BREAK_WRITE,
+						  &nf->nf_flags);
+			}
+		}
+	}
 out:
 	if (status == nfs_ok)
 		*pnf = nf;
diff --git a/fs/nfsd/filecache.h b/fs/nfsd/filecache.h
index 98976f71caa8..b8a59d615729 100644
--- a/fs/nfsd/filecache.h
+++ b/fs/nfsd/filecache.h
@@ -31,6 +31,8 @@  struct nfsd_file {
 	unsigned long		nf_time;
 #define NFSD_FILE_HASHED	(0)
 #define NFSD_FILE_PENDING	(1)
+#define NFSD_FILE_BREAK_READ	(2)
+#define NFSD_FILE_BREAK_WRITE	(3)
 	unsigned long		nf_flags;
 	struct knfsd_fh		nf_handle;
 	unsigned int		nf_hashval;
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index fd688c86af66..79a881589ca7 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -618,7 +618,8 @@  nfsd_access(struct svc_rqst *rqstp, struct svc_fh *fhp, u32 *access, u32 *suppor
 }
 #endif /* CONFIG_NFSD_V3 */
 
-static int nfsd_open_break_lease(struct inode *inode, int access)
+int
+nfsd_open_break_lease(struct inode *inode, int access)
 {
 	unsigned int mode;
 
diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h
index 78b5527cba93..a3ec59830297 100644
--- a/fs/nfsd/vfs.h
+++ b/fs/nfsd/vfs.h
@@ -69,6 +69,7 @@  __be32		do_nfsd_create(struct svc_rqst *, struct svc_fh *,
 __be32		nfsd_commit(struct svc_rqst *, struct svc_fh *,
 				loff_t, unsigned long);
 #endif /* CONFIG_NFSD_V3 */
+int		nfsd_open_break_lease(struct inode *, int);
 __be32		nfsd_open(struct svc_rqst *, struct svc_fh *, umode_t,
 				int, struct file **);
 struct raparms;