All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/5] add CB_NOTIFY_LOCK support to knfsd
@ 2016-09-16 20:28 Jeff Layton
  2016-09-16 20:28 ` [PATCH v3 1/5] nfsd: plumb in a CB_NOTIFY_LOCK operation Jeff Layton
                   ` (5 more replies)
  0 siblings, 6 replies; 13+ messages in thread
From: Jeff Layton @ 2016-09-16 20:28 UTC (permalink / raw)
  To: bfields; +Cc: trond.myklebust, anna.schumaker, linux-nfs

v3:
- add NFS4_OPEN_RESULT_MAY_NOTIFY_LOCK in a separate patch

v2:
- small bugfixes

Very minor update to the patchset I sent a week or so ago. The only real
difference from the last is to move the addition of
NFS4_OPEN_RESULT_MAY_NOTIFY_LOCK to a separate patch.

The basic idea is to just add support for CB_NOTIFY_LOCK callbacks,
which just tell the client that it may want to retry a lock again
once it becomes available.

Tested in conjunction with the corresponding client-side patch
series.

Jeff Layton (5):
  nfsd: plumb in a CB_NOTIFY_LOCK operation
  nfsd: have nfsd4_lock use blocking locks for v4.1+ locks
  nfsd: add a LRU list for blocked locks
  nfs: add a new NFS4_OPEN_RESULT_MAY_NOTIFY_LOCK constant
  nfsd: set the MAY_NOTIFY_LOCK flag in OPEN replies

 fs/nfsd/netns.h           |   1 +
 fs/nfsd/nfs4callback.c    |  57 ++++++++++++
 fs/nfsd/nfs4state.c       | 232 ++++++++++++++++++++++++++++++++++++++++++----
 fs/nfsd/state.h           |  21 ++++-
 fs/nfsd/xdr4cb.h          |   9 ++
 include/uapi/linux/nfs4.h |   5 +-
 6 files changed, 301 insertions(+), 24 deletions(-)

-- 
2.7.4


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v3 1/5] nfsd: plumb in a CB_NOTIFY_LOCK operation
  2016-09-16 20:28 [PATCH v3 0/5] add CB_NOTIFY_LOCK support to knfsd Jeff Layton
@ 2016-09-16 20:28 ` Jeff Layton
  2016-09-16 20:28 ` [PATCH v3 2/5] nfsd: have nfsd4_lock use blocking locks for v4.1+ locks Jeff Layton
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 13+ messages in thread
From: Jeff Layton @ 2016-09-16 20:28 UTC (permalink / raw)
  To: bfields; +Cc: trond.myklebust, anna.schumaker, linux-nfs

Add the encoding/decoding for CB_NOTIFY_LOCK operations.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 fs/nfsd/nfs4callback.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/nfsd/state.h        |  7 +++++++
 fs/nfsd/xdr4cb.h       |  9 ++++++++
 3 files changed, 73 insertions(+)

diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index dbef646c6a2d..211dc2aed8e1 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -623,6 +623,62 @@ static int nfs4_xdr_dec_cb_layout(struct rpc_rqst *rqstp,
 }
 #endif /* CONFIG_NFSD_PNFS */
 
+static void encode_stateowner(struct xdr_stream *xdr, struct nfs4_stateowner *so)
+{
+	__be32	*p;
+
+	p = xdr_reserve_space(xdr, 8 + 4 + so->so_owner.len);
+	p = xdr_encode_opaque_fixed(p, &so->so_client->cl_clientid, 8);
+	xdr_encode_opaque(p, so->so_owner.data, so->so_owner.len);
+}
+
+static void nfs4_xdr_enc_cb_notify_lock(struct rpc_rqst *req,
+					struct xdr_stream *xdr,
+					const struct nfsd4_callback *cb)
+{
+	const struct nfsd4_blocked_lock *nbl =
+		container_of(cb, struct nfsd4_blocked_lock, nbl_cb);
+	struct nfs4_lockowner *lo = (struct nfs4_lockowner *)nbl->nbl_lock.fl_owner;
+	struct nfs4_cb_compound_hdr hdr = {
+		.ident = 0,
+		.minorversion = cb->cb_clp->cl_minorversion,
+	};
+
+	__be32 *p;
+
+	BUG_ON(hdr.minorversion == 0);
+
+	encode_cb_compound4args(xdr, &hdr);
+	encode_cb_sequence4args(xdr, cb, &hdr);
+
+	p = xdr_reserve_space(xdr, 4);
+	*p = cpu_to_be32(OP_CB_NOTIFY_LOCK);
+	encode_nfs_fh4(xdr, &nbl->nbl_fh);
+	encode_stateowner(xdr, &lo->lo_owner);
+	hdr.nops++;
+
+	encode_cb_nops(&hdr);
+}
+
+static int nfs4_xdr_dec_cb_notify_lock(struct rpc_rqst *rqstp,
+					struct xdr_stream *xdr,
+					struct nfsd4_callback *cb)
+{
+	struct nfs4_cb_compound_hdr hdr;
+	int status;
+
+	status = decode_cb_compound4res(xdr, &hdr);
+	if (unlikely(status))
+		return status;
+
+	if (cb) {
+		status = decode_cb_sequence4res(xdr, cb);
+		if (unlikely(status || cb->cb_seq_status))
+			return status;
+	}
+	return decode_cb_op_status(xdr, OP_CB_NOTIFY_LOCK, &cb->cb_status);
+}
+
 /*
  * RPC procedure tables
  */
@@ -643,6 +699,7 @@ static struct rpc_procinfo nfs4_cb_procedures[] = {
 #ifdef CONFIG_NFSD_PNFS
 	PROC(CB_LAYOUT,	COMPOUND,	cb_layout,	cb_layout),
 #endif
+	PROC(CB_NOTIFY_LOCK,	COMPOUND,	cb_notify_lock,	cb_notify_lock),
 };
 
 static struct rpc_version nfs_cb_version4 = {
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 0bdc79cb359c..88d029dd13aa 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -571,6 +571,7 @@ enum nfsd4_cb_op {
 	NFSPROC4_CLNT_CB_RECALL,
 	NFSPROC4_CLNT_CB_LAYOUT,
 	NFSPROC4_CLNT_CB_SEQUENCE,
+	NFSPROC4_CLNT_CB_NOTIFY_LOCK,
 };
 
 /* Returns true iff a is later than b: */
@@ -579,6 +580,12 @@ static inline bool nfsd4_stateid_generation_after(stateid_t *a, stateid_t *b)
 	return (s32)(a->si_generation - b->si_generation) > 0;
 }
 
+struct nfsd4_blocked_lock {
+	struct file_lock	nbl_lock;
+	struct knfsd_fh		nbl_fh;
+	struct nfsd4_callback	nbl_cb;
+};
+
 struct nfsd4_compound_state;
 struct nfsd_net;
 
diff --git a/fs/nfsd/xdr4cb.h b/fs/nfsd/xdr4cb.h
index c47f6fdb111a..49b719dfef95 100644
--- a/fs/nfsd/xdr4cb.h
+++ b/fs/nfsd/xdr4cb.h
@@ -28,3 +28,12 @@
 #define NFS4_dec_cb_layout_sz		(cb_compound_dec_hdr_sz  +      \
 					cb_sequence_dec_sz +            \
 					op_dec_sz)
+
+#define NFS4_enc_cb_notify_lock_sz	(cb_compound_enc_hdr_sz +        \
+					cb_sequence_enc_sz +             \
+					2 + 1 +				 \
+					XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + \
+					enc_nfs4_fh_sz)
+#define NFS4_dec_cb_notify_lock_sz	(cb_compound_dec_hdr_sz  +      \
+					cb_sequence_dec_sz +            \
+					op_dec_sz)
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v3 2/5] nfsd: have nfsd4_lock use blocking locks for v4.1+ locks
  2016-09-16 20:28 [PATCH v3 0/5] add CB_NOTIFY_LOCK support to knfsd Jeff Layton
  2016-09-16 20:28 ` [PATCH v3 1/5] nfsd: plumb in a CB_NOTIFY_LOCK operation Jeff Layton
@ 2016-09-16 20:28 ` Jeff Layton
  2016-09-23 21:19   ` J. Bruce Fields
  2016-09-16 20:28 ` [PATCH v3 3/5] nfsd: add a LRU list for blocked locks Jeff Layton
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 13+ messages in thread
From: Jeff Layton @ 2016-09-16 20:28 UTC (permalink / raw)
  To: bfields; +Cc: trond.myklebust, anna.schumaker, linux-nfs

Create a new per-lockowner+per-inode structure that contains a
file_lock. Have nfsd4_lock add this structure to the lockowner's list
prior to setting the lock. Then call the vfs and request a blocking lock
(by setting FL_SLEEP). If we get anything besides FILE_LOCK_DEFERRED
back, then we dequeue the block structure and free it. When the next
lock request comes in, we'll look for an existing block for the same
filehandle and dequeue and reuse it if there is one.

When the lock comes free (a'la an lm_notify call), we dequeue it
from the lockowner's list and kick off a CB_NOTIFY_LOCK callback to
inform the client that it should retry the lock request.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 fs/nfsd/nfs4state.c | 164 ++++++++++++++++++++++++++++++++++++++++++++++------
 fs/nfsd/state.h     |  12 +++-
 2 files changed, 156 insertions(+), 20 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index a204d7e109d4..ca0db4974e5b 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -99,6 +99,7 @@ static struct kmem_cache *odstate_slab;
 static void free_session(struct nfsd4_session *);
 
 static const struct nfsd4_callback_ops nfsd4_cb_recall_ops;
+static const struct nfsd4_callback_ops nfsd4_cb_notify_lock_ops;
 
 static bool is_session_dead(struct nfsd4_session *ses)
 {
@@ -210,6 +211,84 @@ static void nfsd4_put_session(struct nfsd4_session *ses)
 	spin_unlock(&nn->client_lock);
 }
 
+static struct nfsd4_blocked_lock *
+find_blocked_lock(struct nfs4_lockowner *lo, struct knfsd_fh *fh,
+			struct nfsd_net *nn)
+{
+	struct nfsd4_blocked_lock *cur, *found = NULL;
+
+	spin_lock(&nn->client_lock);
+	list_for_each_entry(cur, &lo->lo_blocked, nbl_list) {
+		if (fh_match(fh, &cur->nbl_fh)) {
+			list_del_init(&cur->nbl_list);
+			found = cur;
+			break;
+		}
+	}
+	spin_unlock(&nn->client_lock);
+	if (found)
+		posix_unblock_lock(&found->nbl_lock);
+	return found;
+}
+
+static struct nfsd4_blocked_lock *
+find_or_allocate_block(struct nfs4_lockowner *lo, struct knfsd_fh *fh,
+			struct nfsd_net *nn)
+{
+	struct nfsd4_blocked_lock *nbl;
+
+	nbl = find_blocked_lock(lo, fh, nn);
+	if (!nbl) {
+		nbl= kmalloc(sizeof(*nbl), GFP_KERNEL);
+		if (nbl) {
+			fh_copy_shallow(&nbl->nbl_fh, fh);
+			locks_init_lock(&nbl->nbl_lock);
+			nfsd4_init_cb(&nbl->nbl_cb, lo->lo_owner.so_client,
+					&nfsd4_cb_notify_lock_ops,
+					NFSPROC4_CLNT_CB_NOTIFY_LOCK);
+		}
+	}
+	return nbl;
+}
+
+static void
+free_blocked_lock(struct nfsd4_blocked_lock *nbl)
+{
+	locks_release_private(&nbl->nbl_lock);
+	kfree(nbl);
+}
+
+static int
+nfsd4_cb_notify_lock_done(struct nfsd4_callback *cb, struct rpc_task *task)
+{
+	/*
+	 * Since this is just an optimization, we don't try very hard if it
+	 * turns out not to succeed. We'll requeue it on NFS4ERR_DELAY, and
+	 * just quit trying on anything else.
+	 */
+	switch (task->tk_status) {
+	case -NFS4ERR_DELAY:
+		rpc_delay(task, 1 * HZ);
+		return 0;
+	default:
+		return 1;
+	}
+}
+
+static void
+nfsd4_cb_notify_lock_release(struct nfsd4_callback *cb)
+{
+	struct nfsd4_blocked_lock	*nbl = container_of(cb,
+						struct nfsd4_blocked_lock, nbl_cb);
+
+	free_blocked_lock(nbl);
+}
+
+static const struct nfsd4_callback_ops nfsd4_cb_notify_lock_ops = {
+	.done		= nfsd4_cb_notify_lock_done,
+	.release	= nfsd4_cb_notify_lock_release,
+};
+
 static inline struct nfs4_stateowner *
 nfs4_get_stateowner(struct nfs4_stateowner *sop)
 {
@@ -5309,7 +5388,29 @@ nfsd4_fl_put_owner(fl_owner_t owner)
 		nfs4_put_stateowner(&lo->lo_owner);
 }
 
+static void
+nfsd4_lm_notify(struct file_lock *fl)
+{
+	struct nfs4_lockowner		*lo = (struct nfs4_lockowner *)fl->fl_owner;
+	struct net			*net = lo->lo_owner.so_client->net;
+	struct nfsd_net			*nn = net_generic(net, nfsd_net_id);
+	struct nfsd4_blocked_lock	*nbl = container_of(fl,
+						struct nfsd4_blocked_lock, nbl_lock);
+	bool queue = false;
+
+	spin_lock(&nn->client_lock);
+	if (!list_empty(&nbl->nbl_list)) {
+		list_del_init(&nbl->nbl_list);
+		queue = true;
+	}
+	spin_unlock(&nn->client_lock);
+
+	if (queue)
+		nfsd4_run_cb(&nbl->nbl_cb);
+}
+
 static const struct lock_manager_operations nfsd_posix_mng_ops  = {
+	.lm_notify = nfsd4_lm_notify,
 	.lm_get_owner = nfsd4_fl_get_owner,
 	.lm_put_owner = nfsd4_fl_put_owner,
 };
@@ -5407,6 +5508,7 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp,
 	lo = alloc_stateowner(lockowner_slab, &lock->lk_new_owner, clp);
 	if (!lo)
 		return NULL;
+	INIT_LIST_HEAD(&lo->lo_blocked);
 	INIT_LIST_HEAD(&lo->lo_owner.so_stateids);
 	lo->lo_owner.so_is_open_owner = 0;
 	lo->lo_owner.so_seqid = lock->lk_new_lock_seqid;
@@ -5588,12 +5690,15 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 	struct nfs4_ol_stateid *open_stp = NULL;
 	struct nfs4_file *fp;
 	struct file *filp = NULL;
+	struct nfsd4_blocked_lock *nbl = NULL;
 	struct file_lock *file_lock = NULL;
 	struct file_lock *conflock = NULL;
 	__be32 status = 0;
 	int lkflg;
 	int err;
 	bool new = false;
+	unsigned char fl_type;
+	unsigned int fl_flags = FL_POSIX;
 	struct net *net = SVC_NET(rqstp);
 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
 
@@ -5658,46 +5763,55 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 	if (!locks_in_grace(net) && lock->lk_reclaim)
 		goto out;
 
-	file_lock = locks_alloc_lock();
-	if (!file_lock) {
-		dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
-		status = nfserr_jukebox;
-		goto out;
-	}
-
 	fp = lock_stp->st_stid.sc_file;
 	switch (lock->lk_type) {
-		case NFS4_READ_LT:
 		case NFS4_READW_LT:
+			if (nfsd4_has_session(cstate))
+				fl_flags |= FL_SLEEP;
+			/* Fallthrough */
+		case NFS4_READ_LT:
 			spin_lock(&fp->fi_lock);
 			filp = find_readable_file_locked(fp);
 			if (filp)
 				get_lock_access(lock_stp, NFS4_SHARE_ACCESS_READ);
 			spin_unlock(&fp->fi_lock);
-			file_lock->fl_type = F_RDLCK;
+			fl_type = F_RDLCK;
 			break;
-		case NFS4_WRITE_LT:
 		case NFS4_WRITEW_LT:
+			if (nfsd4_has_session(cstate))
+				fl_flags |= FL_SLEEP;
+			/* Fallthrough */
+		case NFS4_WRITE_LT:
 			spin_lock(&fp->fi_lock);
 			filp = find_writeable_file_locked(fp);
 			if (filp)
 				get_lock_access(lock_stp, NFS4_SHARE_ACCESS_WRITE);
 			spin_unlock(&fp->fi_lock);
-			file_lock->fl_type = F_WRLCK;
+			fl_type = F_WRLCK;
 			break;
 		default:
 			status = nfserr_inval;
 		goto out;
 	}
+
 	if (!filp) {
 		status = nfserr_openmode;
 		goto out;
 	}
 
+	nbl = find_or_allocate_block(lock_sop, &fp->fi_fhandle, nn);
+	if (!nbl) {
+		dprintk("NFSD: %s: unable to allocate block!\n", __func__);
+		status = nfserr_jukebox;
+		goto out;
+	}
+
+	file_lock = &nbl->nbl_lock;
+	file_lock->fl_type = fl_type;
 	file_lock->fl_owner = (fl_owner_t)lockowner(nfs4_get_stateowner(&lock_sop->lo_owner));
 	file_lock->fl_pid = current->tgid;
 	file_lock->fl_file = filp;
-	file_lock->fl_flags = FL_POSIX;
+	file_lock->fl_flags = fl_flags;
 	file_lock->fl_lmops = &nfsd_posix_mng_ops;
 	file_lock->fl_start = lock->lk_offset;
 	file_lock->fl_end = last_byte_offset(lock->lk_offset, lock->lk_length);
@@ -5710,18 +5824,27 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 		goto out;
 	}
 
+	if (fl_flags & FL_SLEEP) {
+		spin_lock(&nn->client_lock);
+		list_add_tail(&nbl->nbl_list, &lock_sop->lo_blocked);
+		spin_unlock(&nn->client_lock);
+	}
+
 	err = vfs_lock_file(filp, F_SETLK, file_lock, conflock);
-	switch (-err) {
+	switch (err) {
 	case 0: /* success! */
 		nfs4_inc_and_copy_stateid(&lock->lk_resp_stateid, &lock_stp->st_stid);
 		status = 0;
 		break;
-	case (EAGAIN):		/* conflock holds conflicting lock */
+	case FILE_LOCK_DEFERRED:
+		nbl = NULL;
+		/* Fallthrough */
+	case -EAGAIN:		/* conflock holds conflicting lock */
 		status = nfserr_denied;
 		dprintk("NFSD: nfsd4_lock: conflicting lock found!\n");
 		nfs4_set_lock_denied(conflock, &lock->lk_denied);
 		break;
-	case (EDEADLK):
+	case -EDEADLK:
 		status = nfserr_deadlock;
 		break;
 	default:
@@ -5730,6 +5853,15 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 		break;
 	}
 out:
+	if (nbl) {
+		/* dequeue it if we queued it before */
+		if (fl_flags & FL_SLEEP) {
+			spin_lock(&nn->client_lock);
+			list_del_init(&nbl->nbl_list);
+			spin_unlock(&nn->client_lock);
+		}
+		free_blocked_lock(nbl);
+	}
 	if (filp)
 		fput(filp);
 	if (lock_stp) {
@@ -5753,8 +5885,6 @@ out:
 	if (open_stp)
 		nfs4_put_stid(&open_stp->st_stid);
 	nfsd4_bump_seqid(cstate, status);
-	if (file_lock)
-		locks_free_lock(file_lock);
 	if (conflock)
 		locks_free_lock(conflock);
 	return status;
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 88d029dd13aa..e45c183a8bf7 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -440,11 +440,11 @@ struct nfs4_openowner {
 /*
  * Represents a generic "lockowner". Similar to an openowner. References to it
  * are held by the lock stateids that are created on its behalf. This object is
- * a superset of the nfs4_stateowner struct (or would be if it needed any extra
- * fields).
+ * a superset of the nfs4_stateowner struct.
  */
 struct nfs4_lockowner {
-	struct nfs4_stateowner	lo_owner; /* must be first element */
+	struct nfs4_stateowner	lo_owner;	/* must be first element */
+	struct list_head	lo_blocked;	/* blocked file_locks */
 };
 
 static inline struct nfs4_openowner * openowner(struct nfs4_stateowner *so)
@@ -580,7 +580,13 @@ static inline bool nfsd4_stateid_generation_after(stateid_t *a, stateid_t *b)
 	return (s32)(a->si_generation - b->si_generation) > 0;
 }
 
+/*
+ * When a client tries to get a lock on a file, we set one of these objects
+ * on the blocking lock. When the lock becomes free, we can then issue a
+ * CB_NOTIFY_LOCK to the server.
+ */
 struct nfsd4_blocked_lock {
+	struct list_head	nbl_list;
 	struct file_lock	nbl_lock;
 	struct knfsd_fh		nbl_fh;
 	struct nfsd4_callback	nbl_cb;
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v3 3/5] nfsd: add a LRU list for blocked locks
  2016-09-16 20:28 [PATCH v3 0/5] add CB_NOTIFY_LOCK support to knfsd Jeff Layton
  2016-09-16 20:28 ` [PATCH v3 1/5] nfsd: plumb in a CB_NOTIFY_LOCK operation Jeff Layton
  2016-09-16 20:28 ` [PATCH v3 2/5] nfsd: have nfsd4_lock use blocking locks for v4.1+ locks Jeff Layton
@ 2016-09-16 20:28 ` Jeff Layton
  2016-09-16 20:28 ` [PATCH v3 4/5] nfs: add a new NFS4_OPEN_RESULT_MAY_NOTIFY_LOCK constant Jeff Layton
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 13+ messages in thread
From: Jeff Layton @ 2016-09-16 20:28 UTC (permalink / raw)
  To: bfields; +Cc: trond.myklebust, anna.schumaker, linux-nfs

It's possible for a client to call in on a lock that is blocked for a
long time, but discontinue polling for it. A malicious client could
even set a lock on a file, and then spam the server with failing lock
requests from different lockowners that pile up in a DoS attack.

Add the blocked lock structures to a per-net namespace LRU when hashing
them, and timestamp them. If the lock request is not revisited after a
lease period, we'll drop it under the assumption that the client is no
longer interested.

This also gives us a mechanism to clean up these objects at server
shutdown time as well.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 fs/nfsd/netns.h     |  1 +
 fs/nfsd/nfs4state.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/nfsd/state.h     |  2 ++
 3 files changed, 65 insertions(+)

diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h
index 5fbf3bbd00d0..b10d557f9c9e 100644
--- a/fs/nfsd/netns.h
+++ b/fs/nfsd/netns.h
@@ -84,6 +84,7 @@ struct nfsd_net {
 	struct list_head client_lru;
 	struct list_head close_lru;
 	struct list_head del_recall_lru;
+	struct list_head blocked_locks_lru;
 
 	struct delayed_work laundromat_work;
 
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index ca0db4974e5b..6c74d9a45163 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -221,6 +221,7 @@ find_blocked_lock(struct nfs4_lockowner *lo, struct knfsd_fh *fh,
 	list_for_each_entry(cur, &lo->lo_blocked, nbl_list) {
 		if (fh_match(fh, &cur->nbl_fh)) {
 			list_del_init(&cur->nbl_list);
+			list_del_init(&cur->nbl_lru);
 			found = cur;
 			break;
 		}
@@ -4580,6 +4581,7 @@ nfs4_laundromat(struct nfsd_net *nn)
 	struct nfs4_openowner *oo;
 	struct nfs4_delegation *dp;
 	struct nfs4_ol_stateid *stp;
+	struct nfsd4_blocked_lock *nbl;
 	struct list_head *pos, *next, reaplist;
 	time_t cutoff = get_seconds() - nn->nfsd4_lease;
 	time_t t, new_timeo = nn->nfsd4_lease;
@@ -4648,6 +4650,41 @@ nfs4_laundromat(struct nfsd_net *nn)
 	}
 	spin_unlock(&nn->client_lock);
 
+	/*
+	 * It's possible for a client to try and acquire an already held lock
+	 * that is being held for a long time, and then lose interest in it.
+	 * So, we clean out any un-revisited request after a lease period
+	 * under the assumption that the client is no longer interested.
+	 *
+	 * RFC5661, sec. 9.6 states that the client must not rely on getting
+	 * notifications and must continue to poll for locks, even when the
+	 * server supports them. Thus this shouldn't lead to clients blocking
+	 * indefinitely once the lock does become free.
+	 */
+	BUG_ON(!list_empty(&reaplist));
+	spin_lock(&nn->client_lock);
+	while (!list_empty(&nn->blocked_locks_lru)) {
+		nbl = list_first_entry(&nn->blocked_locks_lru,
+					struct nfsd4_blocked_lock, nbl_lru);
+		if (time_after((unsigned long)nbl->nbl_time,
+			       (unsigned long)cutoff)) {
+			t = nbl->nbl_time - cutoff;
+			new_timeo = min(new_timeo, t);
+			break;
+		}
+		list_move(&nbl->nbl_lru, &reaplist);
+		list_del_init(&nbl->nbl_list);
+	}
+	spin_unlock(&nn->client_lock);
+
+	while (!list_empty(&reaplist)) {
+		nbl = list_first_entry(&nn->blocked_locks_lru,
+					struct nfsd4_blocked_lock, nbl_lru);
+		list_del_init(&nbl->nbl_lru);
+		posix_unblock_lock(&nbl->nbl_lock);
+		free_blocked_lock(nbl);
+	}
+
 	new_timeo = max_t(time_t, new_timeo, NFSD_LAUNDROMAT_MINTIMEOUT);
 	return new_timeo;
 }
@@ -5398,9 +5435,11 @@ nfsd4_lm_notify(struct file_lock *fl)
 						struct nfsd4_blocked_lock, nbl_lock);
 	bool queue = false;
 
+	/* An empty list means that something else is going to be using it */
 	spin_lock(&nn->client_lock);
 	if (!list_empty(&nbl->nbl_list)) {
 		list_del_init(&nbl->nbl_list);
+		list_del_init(&nbl->nbl_lru);
 		queue = true;
 	}
 	spin_unlock(&nn->client_lock);
@@ -5825,8 +5864,10 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 	}
 
 	if (fl_flags & FL_SLEEP) {
+		nbl->nbl_time = jiffies;
 		spin_lock(&nn->client_lock);
 		list_add_tail(&nbl->nbl_list, &lock_sop->lo_blocked);
+		list_add_tail(&nbl->nbl_lru, &nn->blocked_locks_lru);
 		spin_unlock(&nn->client_lock);
 	}
 
@@ -5858,6 +5899,7 @@ out:
 		if (fl_flags & FL_SLEEP) {
 			spin_lock(&nn->client_lock);
 			list_del_init(&nbl->nbl_list);
+			list_del_init(&nbl->nbl_lru);
 			spin_unlock(&nn->client_lock);
 		}
 		free_blocked_lock(nbl);
@@ -6898,6 +6940,7 @@ static int nfs4_state_create_net(struct net *net)
 	INIT_LIST_HEAD(&nn->client_lru);
 	INIT_LIST_HEAD(&nn->close_lru);
 	INIT_LIST_HEAD(&nn->del_recall_lru);
+	INIT_LIST_HEAD(&nn->blocked_locks_lru);
 	spin_lock_init(&nn->client_lock);
 
 	INIT_DELAYED_WORK(&nn->laundromat_work, laundromat_main);
@@ -6995,6 +7038,7 @@ nfs4_state_shutdown_net(struct net *net)
 	struct nfs4_delegation *dp = NULL;
 	struct list_head *pos, *next, reaplist;
 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+	struct nfsd4_blocked_lock *nbl;
 
 	cancel_delayed_work_sync(&nn->laundromat_work);
 	locks_end_grace(&nn->nfsd4_manager);
@@ -7015,6 +7059,24 @@ nfs4_state_shutdown_net(struct net *net)
 		nfs4_put_stid(&dp->dl_stid);
 	}
 
+	BUG_ON(!list_empty(&reaplist));
+	spin_lock(&nn->client_lock);
+	while (!list_empty(&nn->blocked_locks_lru)) {
+		nbl = list_first_entry(&nn->blocked_locks_lru,
+					struct nfsd4_blocked_lock, nbl_lru);
+		list_move(&nbl->nbl_lru, &reaplist);
+		list_del_init(&nbl->nbl_list);
+	}
+	spin_unlock(&nn->client_lock);
+
+	while (!list_empty(&reaplist)) {
+		nbl = list_first_entry(&nn->blocked_locks_lru,
+					struct nfsd4_blocked_lock, nbl_lru);
+		list_del_init(&nbl->nbl_lru);
+		posix_unblock_lock(&nbl->nbl_lock);
+		free_blocked_lock(nbl);
+	}
+
 	nfsd4_client_tracking_exit(net);
 	nfs4_state_destroy_net(net);
 }
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index e45c183a8bf7..c9399366f9df 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -587,6 +587,8 @@ static inline bool nfsd4_stateid_generation_after(stateid_t *a, stateid_t *b)
  */
 struct nfsd4_blocked_lock {
 	struct list_head	nbl_list;
+	struct list_head	nbl_lru;
+	unsigned long		nbl_time;
 	struct file_lock	nbl_lock;
 	struct knfsd_fh		nbl_fh;
 	struct nfsd4_callback	nbl_cb;
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v3 4/5] nfs: add a new NFS4_OPEN_RESULT_MAY_NOTIFY_LOCK constant
  2016-09-16 20:28 [PATCH v3 0/5] add CB_NOTIFY_LOCK support to knfsd Jeff Layton
                   ` (2 preceding siblings ...)
  2016-09-16 20:28 ` [PATCH v3 3/5] nfsd: add a LRU list for blocked locks Jeff Layton
@ 2016-09-16 20:28 ` Jeff Layton
  2016-09-16 20:28 ` [PATCH v3 5/5] nfsd: set the MAY_NOTIFY_LOCK flag in OPEN replies Jeff Layton
  2016-09-23 21:05 ` [PATCH v3 0/5] add CB_NOTIFY_LOCK support to knfsd J. Bruce Fields
  5 siblings, 0 replies; 13+ messages in thread
From: Jeff Layton @ 2016-09-16 20:28 UTC (permalink / raw)
  To: bfields; +Cc: trond.myklebust, anna.schumaker, linux-nfs

As defined in RFC 5661, section 18.16.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 include/uapi/linux/nfs4.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/include/uapi/linux/nfs4.h b/include/uapi/linux/nfs4.h
index 2b871e0858d9..4ae62796bfde 100644
--- a/include/uapi/linux/nfs4.h
+++ b/include/uapi/linux/nfs4.h
@@ -39,8 +39,9 @@
 #define NFS4_FH_VOL_MIGRATION		0x0004
 #define NFS4_FH_VOL_RENAME		0x0008
 
-#define NFS4_OPEN_RESULT_CONFIRM 0x0002
-#define NFS4_OPEN_RESULT_LOCKTYPE_POSIX 0x0004
+#define NFS4_OPEN_RESULT_CONFIRM		0x0002
+#define NFS4_OPEN_RESULT_LOCKTYPE_POSIX		0x0004
+#define NFS4_OPEN_RESULT_MAY_NOTIFY_LOCK	0x0020
 
 #define NFS4_SHARE_ACCESS_MASK	0x000F
 #define NFS4_SHARE_ACCESS_READ	0x0001
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v3 5/5] nfsd: set the MAY_NOTIFY_LOCK flag in OPEN replies
  2016-09-16 20:28 [PATCH v3 0/5] add CB_NOTIFY_LOCK support to knfsd Jeff Layton
                   ` (3 preceding siblings ...)
  2016-09-16 20:28 ` [PATCH v3 4/5] nfs: add a new NFS4_OPEN_RESULT_MAY_NOTIFY_LOCK constant Jeff Layton
@ 2016-09-16 20:28 ` Jeff Layton
  2016-09-23 21:05 ` [PATCH v3 0/5] add CB_NOTIFY_LOCK support to knfsd J. Bruce Fields
  5 siblings, 0 replies; 13+ messages in thread
From: Jeff Layton @ 2016-09-16 20:28 UTC (permalink / raw)
  To: bfields; +Cc: trond.myklebust, anna.schumaker, linux-nfs

If we are using v4.1+, then we can send notification when contended
locks become free. Inform the client of that fact.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 fs/nfsd/nfs4state.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 6c74d9a45163..4062f4fbfc6d 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4490,9 +4490,11 @@ out:
 	* To finish the open response, we just need to set the rflags.
 	*/
 	open->op_rflags = NFS4_OPEN_RESULT_LOCKTYPE_POSIX;
-	if (!(open->op_openowner->oo_flags & NFS4_OO_CONFIRMED) &&
-	    !nfsd4_has_session(&resp->cstate))
+	if (nfsd4_has_session(&resp->cstate))
+		open->op_rflags |= NFS4_OPEN_RESULT_MAY_NOTIFY_LOCK;
+	else if (!(open->op_openowner->oo_flags & NFS4_OO_CONFIRMED))
 		open->op_rflags |= NFS4_OPEN_RESULT_CONFIRM;
+
 	if (dp)
 		nfs4_put_stid(&dp->dl_stid);
 	if (stp)
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH v3 0/5] add CB_NOTIFY_LOCK support to knfsd
  2016-09-16 20:28 [PATCH v3 0/5] add CB_NOTIFY_LOCK support to knfsd Jeff Layton
                   ` (4 preceding siblings ...)
  2016-09-16 20:28 ` [PATCH v3 5/5] nfsd: set the MAY_NOTIFY_LOCK flag in OPEN replies Jeff Layton
@ 2016-09-23 21:05 ` J. Bruce Fields
  2016-09-24  0:48   ` Jeff Layton
  5 siblings, 1 reply; 13+ messages in thread
From: J. Bruce Fields @ 2016-09-23 21:05 UTC (permalink / raw)
  To: Jeff Layton; +Cc: trond.myklebust, anna.schumaker, linux-nfs

On Fri, Sep 16, 2016 at 04:28:22PM -0400, Jeff Layton wrote:
> v3:
> - add NFS4_OPEN_RESULT_MAY_NOTIFY_LOCK in a separate patch
> 
> v2:
> - small bugfixes
> 
> Very minor update to the patchset I sent a week or so ago. The only real
> difference from the last is to move the addition of
> NFS4_OPEN_RESULT_MAY_NOTIFY_LOCK to a separate patch.
> 
> The basic idea is to just add support for CB_NOTIFY_LOCK callbacks,
> which just tell the client that it may want to retry a lock again
> once it becomes available.
> 
> Tested in conjunction with the corresponding client-side patch
> series.

What sort of test were you doing?

Have you checked with wireshark's CB_NOTIFY_LOCK support is complete?

--b.

> 
> Jeff Layton (5):
>   nfsd: plumb in a CB_NOTIFY_LOCK operation
>   nfsd: have nfsd4_lock use blocking locks for v4.1+ locks
>   nfsd: add a LRU list for blocked locks
>   nfs: add a new NFS4_OPEN_RESULT_MAY_NOTIFY_LOCK constant
>   nfsd: set the MAY_NOTIFY_LOCK flag in OPEN replies
> 
>  fs/nfsd/netns.h           |   1 +
>  fs/nfsd/nfs4callback.c    |  57 ++++++++++++
>  fs/nfsd/nfs4state.c       | 232 ++++++++++++++++++++++++++++++++++++++++++----
>  fs/nfsd/state.h           |  21 ++++-
>  fs/nfsd/xdr4cb.h          |   9 ++
>  include/uapi/linux/nfs4.h |   5 +-
>  6 files changed, 301 insertions(+), 24 deletions(-)
> 
> -- 
> 2.7.4

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3 2/5] nfsd: have nfsd4_lock use blocking locks for v4.1+ locks
  2016-09-16 20:28 ` [PATCH v3 2/5] nfsd: have nfsd4_lock use blocking locks for v4.1+ locks Jeff Layton
@ 2016-09-23 21:19   ` J. Bruce Fields
  2016-09-24  0:43     ` Jeff Layton
  0 siblings, 1 reply; 13+ messages in thread
From: J. Bruce Fields @ 2016-09-23 21:19 UTC (permalink / raw)
  To: Jeff Layton; +Cc: trond.myklebust, anna.schumaker, linux-nfs

On Fri, Sep 16, 2016 at 04:28:24PM -0400, Jeff Layton wrote:
> Create a new per-lockowner+per-inode structure that contains a
> file_lock. Have nfsd4_lock add this structure to the lockowner's list
> prior to setting the lock. Then call the vfs and request a blocking lock
> (by setting FL_SLEEP). If we get anything besides FILE_LOCK_DEFERRED

That doesn't sound right.  FILE_LOCK_DEFERRED is a special case for
asynchronous locking--it means "I don't know whether there's a conflict
or not, it may take a while to check", not "there's a conflict".

(Ugh.  I apologize for the asynchronous locking code.)

--b.

> back, then we dequeue the block structure and free it. When the next
> lock request comes in, we'll look for an existing block for the same
> filehandle and dequeue and reuse it if there is one.
> 
> When the lock comes free (a'la an lm_notify call), we dequeue it
> from the lockowner's list and kick off a CB_NOTIFY_LOCK callback to
> inform the client that it should retry the lock request.
> 
> Signed-off-by: Jeff Layton <jlayton@redhat.com>
> ---
>  fs/nfsd/nfs4state.c | 164 ++++++++++++++++++++++++++++++++++++++++++++++------
>  fs/nfsd/state.h     |  12 +++-
>  2 files changed, 156 insertions(+), 20 deletions(-)
> 
> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> index a204d7e109d4..ca0db4974e5b 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -99,6 +99,7 @@ static struct kmem_cache *odstate_slab;
>  static void free_session(struct nfsd4_session *);
>  
>  static const struct nfsd4_callback_ops nfsd4_cb_recall_ops;
> +static const struct nfsd4_callback_ops nfsd4_cb_notify_lock_ops;
>  
>  static bool is_session_dead(struct nfsd4_session *ses)
>  {
> @@ -210,6 +211,84 @@ static void nfsd4_put_session(struct nfsd4_session *ses)
>  	spin_unlock(&nn->client_lock);
>  }
>  
> +static struct nfsd4_blocked_lock *
> +find_blocked_lock(struct nfs4_lockowner *lo, struct knfsd_fh *fh,
> +			struct nfsd_net *nn)
> +{
> +	struct nfsd4_blocked_lock *cur, *found = NULL;
> +
> +	spin_lock(&nn->client_lock);
> +	list_for_each_entry(cur, &lo->lo_blocked, nbl_list) {
> +		if (fh_match(fh, &cur->nbl_fh)) {
> +			list_del_init(&cur->nbl_list);
> +			found = cur;
> +			break;
> +		}
> +	}
> +	spin_unlock(&nn->client_lock);
> +	if (found)
> +		posix_unblock_lock(&found->nbl_lock);
> +	return found;
> +}
> +
> +static struct nfsd4_blocked_lock *
> +find_or_allocate_block(struct nfs4_lockowner *lo, struct knfsd_fh *fh,
> +			struct nfsd_net *nn)
> +{
> +	struct nfsd4_blocked_lock *nbl;
> +
> +	nbl = find_blocked_lock(lo, fh, nn);
> +	if (!nbl) {
> +		nbl= kmalloc(sizeof(*nbl), GFP_KERNEL);
> +		if (nbl) {
> +			fh_copy_shallow(&nbl->nbl_fh, fh);
> +			locks_init_lock(&nbl->nbl_lock);
> +			nfsd4_init_cb(&nbl->nbl_cb, lo->lo_owner.so_client,
> +					&nfsd4_cb_notify_lock_ops,
> +					NFSPROC4_CLNT_CB_NOTIFY_LOCK);
> +		}
> +	}
> +	return nbl;
> +}
> +
> +static void
> +free_blocked_lock(struct nfsd4_blocked_lock *nbl)
> +{
> +	locks_release_private(&nbl->nbl_lock);
> +	kfree(nbl);
> +}
> +
> +static int
> +nfsd4_cb_notify_lock_done(struct nfsd4_callback *cb, struct rpc_task *task)
> +{
> +	/*
> +	 * Since this is just an optimization, we don't try very hard if it
> +	 * turns out not to succeed. We'll requeue it on NFS4ERR_DELAY, and
> +	 * just quit trying on anything else.
> +	 */
> +	switch (task->tk_status) {
> +	case -NFS4ERR_DELAY:
> +		rpc_delay(task, 1 * HZ);
> +		return 0;
> +	default:
> +		return 1;
> +	}
> +}
> +
> +static void
> +nfsd4_cb_notify_lock_release(struct nfsd4_callback *cb)
> +{
> +	struct nfsd4_blocked_lock	*nbl = container_of(cb,
> +						struct nfsd4_blocked_lock, nbl_cb);
> +
> +	free_blocked_lock(nbl);
> +}
> +
> +static const struct nfsd4_callback_ops nfsd4_cb_notify_lock_ops = {
> +	.done		= nfsd4_cb_notify_lock_done,
> +	.release	= nfsd4_cb_notify_lock_release,
> +};
> +
>  static inline struct nfs4_stateowner *
>  nfs4_get_stateowner(struct nfs4_stateowner *sop)
>  {
> @@ -5309,7 +5388,29 @@ nfsd4_fl_put_owner(fl_owner_t owner)
>  		nfs4_put_stateowner(&lo->lo_owner);
>  }
>  
> +static void
> +nfsd4_lm_notify(struct file_lock *fl)
> +{
> +	struct nfs4_lockowner		*lo = (struct nfs4_lockowner *)fl->fl_owner;
> +	struct net			*net = lo->lo_owner.so_client->net;
> +	struct nfsd_net			*nn = net_generic(net, nfsd_net_id);
> +	struct nfsd4_blocked_lock	*nbl = container_of(fl,
> +						struct nfsd4_blocked_lock, nbl_lock);
> +	bool queue = false;
> +
> +	spin_lock(&nn->client_lock);
> +	if (!list_empty(&nbl->nbl_list)) {
> +		list_del_init(&nbl->nbl_list);
> +		queue = true;
> +	}
> +	spin_unlock(&nn->client_lock);
> +
> +	if (queue)
> +		nfsd4_run_cb(&nbl->nbl_cb);
> +}
> +
>  static const struct lock_manager_operations nfsd_posix_mng_ops  = {
> +	.lm_notify = nfsd4_lm_notify,
>  	.lm_get_owner = nfsd4_fl_get_owner,
>  	.lm_put_owner = nfsd4_fl_put_owner,
>  };
> @@ -5407,6 +5508,7 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp,
>  	lo = alloc_stateowner(lockowner_slab, &lock->lk_new_owner, clp);
>  	if (!lo)
>  		return NULL;
> +	INIT_LIST_HEAD(&lo->lo_blocked);
>  	INIT_LIST_HEAD(&lo->lo_owner.so_stateids);
>  	lo->lo_owner.so_is_open_owner = 0;
>  	lo->lo_owner.so_seqid = lock->lk_new_lock_seqid;
> @@ -5588,12 +5690,15 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
>  	struct nfs4_ol_stateid *open_stp = NULL;
>  	struct nfs4_file *fp;
>  	struct file *filp = NULL;
> +	struct nfsd4_blocked_lock *nbl = NULL;
>  	struct file_lock *file_lock = NULL;
>  	struct file_lock *conflock = NULL;
>  	__be32 status = 0;
>  	int lkflg;
>  	int err;
>  	bool new = false;
> +	unsigned char fl_type;
> +	unsigned int fl_flags = FL_POSIX;
>  	struct net *net = SVC_NET(rqstp);
>  	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
>  
> @@ -5658,46 +5763,55 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
>  	if (!locks_in_grace(net) && lock->lk_reclaim)
>  		goto out;
>  
> -	file_lock = locks_alloc_lock();
> -	if (!file_lock) {
> -		dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
> -		status = nfserr_jukebox;
> -		goto out;
> -	}
> -
>  	fp = lock_stp->st_stid.sc_file;
>  	switch (lock->lk_type) {
> -		case NFS4_READ_LT:
>  		case NFS4_READW_LT:
> +			if (nfsd4_has_session(cstate))
> +				fl_flags |= FL_SLEEP;
> +			/* Fallthrough */
> +		case NFS4_READ_LT:
>  			spin_lock(&fp->fi_lock);
>  			filp = find_readable_file_locked(fp);
>  			if (filp)
>  				get_lock_access(lock_stp, NFS4_SHARE_ACCESS_READ);
>  			spin_unlock(&fp->fi_lock);
> -			file_lock->fl_type = F_RDLCK;
> +			fl_type = F_RDLCK;
>  			break;
> -		case NFS4_WRITE_LT:
>  		case NFS4_WRITEW_LT:
> +			if (nfsd4_has_session(cstate))
> +				fl_flags |= FL_SLEEP;
> +			/* Fallthrough */
> +		case NFS4_WRITE_LT:
>  			spin_lock(&fp->fi_lock);
>  			filp = find_writeable_file_locked(fp);
>  			if (filp)
>  				get_lock_access(lock_stp, NFS4_SHARE_ACCESS_WRITE);
>  			spin_unlock(&fp->fi_lock);
> -			file_lock->fl_type = F_WRLCK;
> +			fl_type = F_WRLCK;
>  			break;
>  		default:
>  			status = nfserr_inval;
>  		goto out;
>  	}
> +
>  	if (!filp) {
>  		status = nfserr_openmode;
>  		goto out;
>  	}
>  
> +	nbl = find_or_allocate_block(lock_sop, &fp->fi_fhandle, nn);
> +	if (!nbl) {
> +		dprintk("NFSD: %s: unable to allocate block!\n", __func__);
> +		status = nfserr_jukebox;
> +		goto out;
> +	}
> +
> +	file_lock = &nbl->nbl_lock;
> +	file_lock->fl_type = fl_type;
>  	file_lock->fl_owner = (fl_owner_t)lockowner(nfs4_get_stateowner(&lock_sop->lo_owner));
>  	file_lock->fl_pid = current->tgid;
>  	file_lock->fl_file = filp;
> -	file_lock->fl_flags = FL_POSIX;
> +	file_lock->fl_flags = fl_flags;
>  	file_lock->fl_lmops = &nfsd_posix_mng_ops;
>  	file_lock->fl_start = lock->lk_offset;
>  	file_lock->fl_end = last_byte_offset(lock->lk_offset, lock->lk_length);
> @@ -5710,18 +5824,27 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
>  		goto out;
>  	}
>  
> +	if (fl_flags & FL_SLEEP) {
> +		spin_lock(&nn->client_lock);
> +		list_add_tail(&nbl->nbl_list, &lock_sop->lo_blocked);
> +		spin_unlock(&nn->client_lock);
> +	}
> +
>  	err = vfs_lock_file(filp, F_SETLK, file_lock, conflock);
> -	switch (-err) {
> +	switch (err) {
>  	case 0: /* success! */
>  		nfs4_inc_and_copy_stateid(&lock->lk_resp_stateid, &lock_stp->st_stid);
>  		status = 0;
>  		break;
> -	case (EAGAIN):		/* conflock holds conflicting lock */
> +	case FILE_LOCK_DEFERRED:
> +		nbl = NULL;
> +		/* Fallthrough */
> +	case -EAGAIN:		/* conflock holds conflicting lock */
>  		status = nfserr_denied;
>  		dprintk("NFSD: nfsd4_lock: conflicting lock found!\n");
>  		nfs4_set_lock_denied(conflock, &lock->lk_denied);
>  		break;
> -	case (EDEADLK):
> +	case -EDEADLK:
>  		status = nfserr_deadlock;
>  		break;
>  	default:
> @@ -5730,6 +5853,15 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
>  		break;
>  	}
>  out:
> +	if (nbl) {
> +		/* dequeue it if we queued it before */
> +		if (fl_flags & FL_SLEEP) {
> +			spin_lock(&nn->client_lock);
> +			list_del_init(&nbl->nbl_list);
> +			spin_unlock(&nn->client_lock);
> +		}
> +		free_blocked_lock(nbl);
> +	}
>  	if (filp)
>  		fput(filp);
>  	if (lock_stp) {
> @@ -5753,8 +5885,6 @@ out:
>  	if (open_stp)
>  		nfs4_put_stid(&open_stp->st_stid);
>  	nfsd4_bump_seqid(cstate, status);
> -	if (file_lock)
> -		locks_free_lock(file_lock);
>  	if (conflock)
>  		locks_free_lock(conflock);
>  	return status;
> diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
> index 88d029dd13aa..e45c183a8bf7 100644
> --- a/fs/nfsd/state.h
> +++ b/fs/nfsd/state.h
> @@ -440,11 +440,11 @@ struct nfs4_openowner {
>  /*
>   * Represents a generic "lockowner". Similar to an openowner. References to it
>   * are held by the lock stateids that are created on its behalf. This object is
> - * a superset of the nfs4_stateowner struct (or would be if it needed any extra
> - * fields).
> + * a superset of the nfs4_stateowner struct.
>   */
>  struct nfs4_lockowner {
> -	struct nfs4_stateowner	lo_owner; /* must be first element */
> +	struct nfs4_stateowner	lo_owner;	/* must be first element */
> +	struct list_head	lo_blocked;	/* blocked file_locks */
>  };
>  
>  static inline struct nfs4_openowner * openowner(struct nfs4_stateowner *so)
> @@ -580,7 +580,13 @@ static inline bool nfsd4_stateid_generation_after(stateid_t *a, stateid_t *b)
>  	return (s32)(a->si_generation - b->si_generation) > 0;
>  }
>  
> +/*
> + * When a client tries to get a lock on a file, we set one of these objects
> + * on the blocking lock. When the lock becomes free, we can then issue a
> + * CB_NOTIFY_LOCK to the server.
> + */
>  struct nfsd4_blocked_lock {
> +	struct list_head	nbl_list;
>  	struct file_lock	nbl_lock;
>  	struct knfsd_fh		nbl_fh;
>  	struct nfsd4_callback	nbl_cb;
> -- 
> 2.7.4

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3 2/5] nfsd: have nfsd4_lock use blocking locks for v4.1+ locks
  2016-09-23 21:19   ` J. Bruce Fields
@ 2016-09-24  0:43     ` Jeff Layton
  2016-09-24 15:02       ` J. Bruce Fields
  0 siblings, 1 reply; 13+ messages in thread
From: Jeff Layton @ 2016-09-24  0:43 UTC (permalink / raw)
  To: J. Bruce Fields; +Cc: trond.myklebust, anna.schumaker, linux-nfs

On Fri, 2016-09-23 at 17:19 -0400, J. Bruce Fields wrote:
> On Fri, Sep 16, 2016 at 04:28:24PM -0400, Jeff Layton wrote:
> > 
> > Create a new per-lockowner+per-inode structure that contains a
> > file_lock. Have nfsd4_lock add this structure to the lockowner's list
> > prior to setting the lock. Then call the vfs and request a blocking lock
> > (by setting FL_SLEEP). If we get anything besides FILE_LOCK_DEFERRED
> 
> That doesn't sound right.  FILE_LOCK_DEFERRED is a special case for
> asynchronous locking--it means "I don't know whether there's a conflict
> or not, it may take a while to check", not "there's a conflict".
> 
> (Ugh.  I apologize for the asynchronous locking code.)
> 
> --b.
> 

The local file locking code definitely uses this return code to mean
"This lock is blocked, and I'll call your lm_notify when it's
unblocked", which is exactly what we rely on here.

There is a place that uses it in the way you mention though, and that is
when DLM and svc lockd are interacting via dlm_posix_lock(). lockd can't
be in play here since this is all NFSv4, so I think the code does handle
this correctly.

That said...maybe should probably think about some way to disambiguate
those two states in the return code. It is horribly confusing.
 
> > back, then we dequeue the block structure and free it. When the next
> > lock request comes in, we'll look for an existing block for the same
> > filehandle and dequeue and reuse it if there is one.
> > 
> > When the lock comes free (a'la an lm_notify call), we dequeue it
> > from the lockowner's list and kick off a CB_NOTIFY_LOCK callback to
> > inform the client that it should retry the lock request.
> > 
> > Signed-off-by: Jeff Layton <jlayton@redhat.com>
> > ---
> >  fs/nfsd/nfs4state.c | 164 ++++++++++++++++++++++++++++++++++++++++++++++------
> >  fs/nfsd/state.h     |  12 +++-
> >  2 files changed, 156 insertions(+), 20 deletions(-)
> > 
> > diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> > index a204d7e109d4..ca0db4974e5b 100644
> > --- a/fs/nfsd/nfs4state.c
> > +++ b/fs/nfsd/nfs4state.c
> > @@ -99,6 +99,7 @@ static struct kmem_cache *odstate_slab;
> >  static void free_session(struct nfsd4_session *);
> >  
> >  static const struct nfsd4_callback_ops nfsd4_cb_recall_ops;
> > +static const struct nfsd4_callback_ops nfsd4_cb_notify_lock_ops;
> >  
> >  static bool is_session_dead(struct nfsd4_session *ses)
> >  {
> > @@ -210,6 +211,84 @@ static void nfsd4_put_session(struct nfsd4_session *ses)
> >  	spin_unlock(&nn->client_lock);
> >  }
> >  
> > +static struct nfsd4_blocked_lock *
> > +find_blocked_lock(struct nfs4_lockowner *lo, struct knfsd_fh *fh,
> > +			struct nfsd_net *nn)
> > +{
> > +	struct nfsd4_blocked_lock *cur, *found = NULL;
> > +
> > +	spin_lock(&nn->client_lock);
> > +	list_for_each_entry(cur, &lo->lo_blocked, nbl_list) {
> > +		if (fh_match(fh, &cur->nbl_fh)) {
> > +			list_del_init(&cur->nbl_list);
> > +			found = cur;
> > +			break;
> > +		}
> > +	}
> > +	spin_unlock(&nn->client_lock);
> > +	if (found)
> > +		posix_unblock_lock(&found->nbl_lock);
> > +	return found;
> > +}
> > +
> > +static struct nfsd4_blocked_lock *
> > +find_or_allocate_block(struct nfs4_lockowner *lo, struct knfsd_fh *fh,
> > +			struct nfsd_net *nn)
> > +{
> > +	struct nfsd4_blocked_lock *nbl;
> > +
> > +	nbl = find_blocked_lock(lo, fh, nn);
> > +	if (!nbl) {
> > +		nbl= kmalloc(sizeof(*nbl), GFP_KERNEL);
> > +		if (nbl) {
> > +			fh_copy_shallow(&nbl->nbl_fh, fh);
> > +			locks_init_lock(&nbl->nbl_lock);
> > +			nfsd4_init_cb(&nbl->nbl_cb, lo->lo_owner.so_client,
> > +					&nfsd4_cb_notify_lock_ops,
> > +					NFSPROC4_CLNT_CB_NOTIFY_LOCK);
> > +		}
> > +	}
> > +	return nbl;
> > +}
> > +
> > +static void
> > +free_blocked_lock(struct nfsd4_blocked_lock *nbl)
> > +{
> > +	locks_release_private(&nbl->nbl_lock);
> > +	kfree(nbl);
> > +}
> > +
> > +static int
> > +nfsd4_cb_notify_lock_done(struct nfsd4_callback *cb, struct rpc_task *task)
> > +{
> > +	/*
> > +	 * Since this is just an optimization, we don't try very hard if it
> > +	 * turns out not to succeed. We'll requeue it on NFS4ERR_DELAY, and
> > +	 * just quit trying on anything else.
> > +	 */
> > +	switch (task->tk_status) {
> > +	case -NFS4ERR_DELAY:
> > +		rpc_delay(task, 1 * HZ);
> > +		return 0;
> > +	default:
> > +		return 1;
> > +	}
> > +}
> > +
> > +static void
> > +nfsd4_cb_notify_lock_release(struct nfsd4_callback *cb)
> > +{
> > +	struct nfsd4_blocked_lock	*nbl = container_of(cb,
> > +						struct nfsd4_blocked_lock, nbl_cb);
> > +
> > +	free_blocked_lock(nbl);
> > +}
> > +
> > +static const struct nfsd4_callback_ops nfsd4_cb_notify_lock_ops = {
> > +	.done		= nfsd4_cb_notify_lock_done,
> > +	.release	= nfsd4_cb_notify_lock_release,
> > +};
> > +
> >  static inline struct nfs4_stateowner *
> >  nfs4_get_stateowner(struct nfs4_stateowner *sop)
> >  {
> > @@ -5309,7 +5388,29 @@ nfsd4_fl_put_owner(fl_owner_t owner)
> >  		nfs4_put_stateowner(&lo->lo_owner);
> >  }
> >  
> > +static void
> > +nfsd4_lm_notify(struct file_lock *fl)
> > +{
> > +	struct nfs4_lockowner		*lo = (struct nfs4_lockowner *)fl->fl_owner;
> > +	struct net			*net = lo->lo_owner.so_client->net;
> > +	struct nfsd_net			*nn = net_generic(net, nfsd_net_id);
> > +	struct nfsd4_blocked_lock	*nbl = container_of(fl,
> > +						struct nfsd4_blocked_lock, nbl_lock);
> > +	bool queue = false;
> > +
> > +	spin_lock(&nn->client_lock);
> > +	if (!list_empty(&nbl->nbl_list)) {
> > +		list_del_init(&nbl->nbl_list);
> > +		queue = true;
> > +	}
> > +	spin_unlock(&nn->client_lock);
> > +
> > +	if (queue)
> > +		nfsd4_run_cb(&nbl->nbl_cb);
> > +}
> > +
> >  static const struct lock_manager_operations nfsd_posix_mng_ops  = {
> > +	.lm_notify = nfsd4_lm_notify,
> >  	.lm_get_owner = nfsd4_fl_get_owner,
> >  	.lm_put_owner = nfsd4_fl_put_owner,
> >  };
> > @@ -5407,6 +5508,7 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp,
> >  	lo = alloc_stateowner(lockowner_slab, &lock->lk_new_owner, clp);
> >  	if (!lo)
> >  		return NULL;
> > +	INIT_LIST_HEAD(&lo->lo_blocked);
> >  	INIT_LIST_HEAD(&lo->lo_owner.so_stateids);
> >  	lo->lo_owner.so_is_open_owner = 0;
> >  	lo->lo_owner.so_seqid = lock->lk_new_lock_seqid;
> > @@ -5588,12 +5690,15 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
> >  	struct nfs4_ol_stateid *open_stp = NULL;
> >  	struct nfs4_file *fp;
> >  	struct file *filp = NULL;
> > +	struct nfsd4_blocked_lock *nbl = NULL;
> >  	struct file_lock *file_lock = NULL;
> >  	struct file_lock *conflock = NULL;
> >  	__be32 status = 0;
> >  	int lkflg;
> >  	int err;
> >  	bool new = false;
> > +	unsigned char fl_type;
> > +	unsigned int fl_flags = FL_POSIX;
> >  	struct net *net = SVC_NET(rqstp);
> >  	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
> >  
> > @@ -5658,46 +5763,55 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
> >  	if (!locks_in_grace(net) && lock->lk_reclaim)
> >  		goto out;
> >  
> > -	file_lock = locks_alloc_lock();
> > -	if (!file_lock) {
> > -		dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
> > -		status = nfserr_jukebox;
> > -		goto out;
> > -	}
> > -
> >  	fp = lock_stp->st_stid.sc_file;
> >  	switch (lock->lk_type) {
> > -		case NFS4_READ_LT:
> >  		case NFS4_READW_LT:
> > +			if (nfsd4_has_session(cstate))
> > +				fl_flags |= FL_SLEEP;
> > +			/* Fallthrough */
> > +		case NFS4_READ_LT:
> >  			spin_lock(&fp->fi_lock);
> >  			filp = find_readable_file_locked(fp);
> >  			if (filp)
> >  				get_lock_access(lock_stp, NFS4_SHARE_ACCESS_READ);
> >  			spin_unlock(&fp->fi_lock);
> > -			file_lock->fl_type = F_RDLCK;
> > +			fl_type = F_RDLCK;
> >  			break;
> > -		case NFS4_WRITE_LT:
> >  		case NFS4_WRITEW_LT:
> > +			if (nfsd4_has_session(cstate))
> > +				fl_flags |= FL_SLEEP;
> > +			/* Fallthrough */
> > +		case NFS4_WRITE_LT:
> >  			spin_lock(&fp->fi_lock);
> >  			filp = find_writeable_file_locked(fp);
> >  			if (filp)
> >  				get_lock_access(lock_stp, NFS4_SHARE_ACCESS_WRITE);
> >  			spin_unlock(&fp->fi_lock);
> > -			file_lock->fl_type = F_WRLCK;
> > +			fl_type = F_WRLCK;
> >  			break;
> >  		default:
> >  			status = nfserr_inval;
> >  		goto out;
> >  	}
> > +
> >  	if (!filp) {
> >  		status = nfserr_openmode;
> >  		goto out;
> >  	}
> >  
> > +	nbl = find_or_allocate_block(lock_sop, &fp->fi_fhandle, nn);
> > +	if (!nbl) {
> > +		dprintk("NFSD: %s: unable to allocate block!\n", __func__);
> > +		status = nfserr_jukebox;
> > +		goto out;
> > +	}
> > +
> > +	file_lock = &nbl->nbl_lock;
> > +	file_lock->fl_type = fl_type;
> >  	file_lock->fl_owner = (fl_owner_t)lockowner(nfs4_get_stateowner(&lock_sop->lo_owner));
> >  	file_lock->fl_pid = current->tgid;
> >  	file_lock->fl_file = filp;
> > -	file_lock->fl_flags = FL_POSIX;
> > +	file_lock->fl_flags = fl_flags;
> >  	file_lock->fl_lmops = &nfsd_posix_mng_ops;
> >  	file_lock->fl_start = lock->lk_offset;
> >  	file_lock->fl_end = last_byte_offset(lock->lk_offset, lock->lk_length);
> > @@ -5710,18 +5824,27 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
> >  		goto out;
> >  	}
> >  
> > +	if (fl_flags & FL_SLEEP) {
> > +		spin_lock(&nn->client_lock);
> > +		list_add_tail(&nbl->nbl_list, &lock_sop->lo_blocked);
> > +		spin_unlock(&nn->client_lock);
> > +	}
> > +
> >  	err = vfs_lock_file(filp, F_SETLK, file_lock, conflock);
> > -	switch (-err) {
> > +	switch (err) {
> >  	case 0: /* success! */
> >  		nfs4_inc_and_copy_stateid(&lock->lk_resp_stateid, &lock_stp->st_stid);
> >  		status = 0;
> >  		break;
> > -	case (EAGAIN):		/* conflock holds conflicting lock */
> > +	case FILE_LOCK_DEFERRED:
> > +		nbl = NULL;
> > +		/* Fallthrough */
> > +	case -EAGAIN:		/* conflock holds conflicting lock */
> >  		status = nfserr_denied;
> >  		dprintk("NFSD: nfsd4_lock: conflicting lock found!\n");
> >  		nfs4_set_lock_denied(conflock, &lock->lk_denied);
> >  		break;
> > -	case (EDEADLK):
> > +	case -EDEADLK:
> >  		status = nfserr_deadlock;
> >  		break;
> >  	default:
> > @@ -5730,6 +5853,15 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
> >  		break;
> >  	}
> >  out:
> > +	if (nbl) {
> > +		/* dequeue it if we queued it before */
> > +		if (fl_flags & FL_SLEEP) {
> > +			spin_lock(&nn->client_lock);
> > +			list_del_init(&nbl->nbl_list);
> > +			spin_unlock(&nn->client_lock);
> > +		}
> > +		free_blocked_lock(nbl);
> > +	}
> >  	if (filp)
> >  		fput(filp);
> >  	if (lock_stp) {
> > @@ -5753,8 +5885,6 @@ out:
> >  	if (open_stp)
> >  		nfs4_put_stid(&open_stp->st_stid);
> >  	nfsd4_bump_seqid(cstate, status);
> > -	if (file_lock)
> > -		locks_free_lock(file_lock);
> >  	if (conflock)
> >  		locks_free_lock(conflock);
> >  	return status;
> > diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
> > index 88d029dd13aa..e45c183a8bf7 100644
> > --- a/fs/nfsd/state.h
> > +++ b/fs/nfsd/state.h
> > @@ -440,11 +440,11 @@ struct nfs4_openowner {
> >  /*
> >   * Represents a generic "lockowner". Similar to an openowner. References to it
> >   * are held by the lock stateids that are created on its behalf. This object is
> > - * a superset of the nfs4_stateowner struct (or would be if it needed any extra
> > - * fields).
> > + * a superset of the nfs4_stateowner struct.
> >   */
> >  struct nfs4_lockowner {
> > -	struct nfs4_stateowner	lo_owner; /* must be first element */
> > +	struct nfs4_stateowner	lo_owner;	/* must be first element */
> > +	struct list_head	lo_blocked;	/* blocked file_locks */
> >  };
> >  
> >  static inline struct nfs4_openowner * openowner(struct nfs4_stateowner *so)
> > @@ -580,7 +580,13 @@ static inline bool nfsd4_stateid_generation_after(stateid_t *a, stateid_t *b)
> >  	return (s32)(a->si_generation - b->si_generation) > 0;
> >  }
> >  
> > +/*
> > + * When a client tries to get a lock on a file, we set one of these objects
> > + * on the blocking lock. When the lock becomes free, we can then issue a
> > + * CB_NOTIFY_LOCK to the server.
> > + */
> >  struct nfsd4_blocked_lock {
> > +	struct list_head	nbl_list;
> >  	struct file_lock	nbl_lock;
> >  	struct knfsd_fh		nbl_fh;
> >  	struct nfsd4_callback	nbl_cb;
> > -- 
> > 2.7.4

-- 
Jeff Layton <jlayton@redhat.com>

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3 0/5] add CB_NOTIFY_LOCK support to knfsd
  2016-09-23 21:05 ` [PATCH v3 0/5] add CB_NOTIFY_LOCK support to knfsd J. Bruce Fields
@ 2016-09-24  0:48   ` Jeff Layton
  2016-09-26 16:03     ` J. Bruce Fields
  0 siblings, 1 reply; 13+ messages in thread
From: Jeff Layton @ 2016-09-24  0:48 UTC (permalink / raw)
  To: J. Bruce Fields; +Cc: trond.myklebust, anna.schumaker, linux-nfs

On Fri, 2016-09-23 at 17:05 -0400, J. Bruce Fields wrote:
> On Fri, Sep 16, 2016 at 04:28:22PM -0400, Jeff Layton wrote:
> > 
> > v3:
> > - add NFS4_OPEN_RESULT_MAY_NOTIFY_LOCK in a separate patch
> > 
> > v2:
> > - small bugfixes
> > 
> > Very minor update to the patchset I sent a week or so ago. The only real
> > difference from the last is to move the addition of
> > NFS4_OPEN_RESULT_MAY_NOTIFY_LOCK to a separate patch.
> > 
> > The basic idea is to just add support for CB_NOTIFY_LOCK callbacks,
> > which just tell the client that it may want to retry a lock again
> > once it becomes available.
> > 
> > Tested in conjunction with the corresponding client-side patch
> > series.
> 
> What sort of test were you doing?
> 

I did several: cthon (of course), the lockperf suite (which still takes
quite a while to run). Mostly I just verified that I got callbacks when
expected and that the client acted on them using wireshark. Some testing
under more load would be great.

> Have you checked with wireshark's CB_NOTIFY_LOCK support is complete?
> 

Just recently fixed:

https://code.wireshark.org/review/gitweb?p=wireshark.git;a=commitdiff;h=1948f7bd7553f215dc2519a35dcd62e29e35a614


> > 
> > 
> > Jeff Layton (5):
> >   nfsd: plumb in a CB_NOTIFY_LOCK operation
> >   nfsd: have nfsd4_lock use blocking locks for v4.1+ locks
> >   nfsd: add a LRU list for blocked locks
> >   nfs: add a new NFS4_OPEN_RESULT_MAY_NOTIFY_LOCK constant
> >   nfsd: set the MAY_NOTIFY_LOCK flag in OPEN replies
> > 
> >  fs/nfsd/netns.h           |   1 +
> >  fs/nfsd/nfs4callback.c    |  57 ++++++++++++
> >  fs/nfsd/nfs4state.c       | 232 ++++++++++++++++++++++++++++++++++++++++++----
> >  fs/nfsd/state.h           |  21 ++++-
> >  fs/nfsd/xdr4cb.h          |   9 ++
> >  include/uapi/linux/nfs4.h |   5 +-
> >  6 files changed, 301 insertions(+), 24 deletions(-)
> > 
> > -- 
> > 2.7.4

-- 
Jeff Layton <jlayton@redhat.com>

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3 2/5] nfsd: have nfsd4_lock use blocking locks for v4.1+ locks
  2016-09-24  0:43     ` Jeff Layton
@ 2016-09-24 15:02       ` J. Bruce Fields
  2016-09-24 16:48         ` Jeff Layton
  0 siblings, 1 reply; 13+ messages in thread
From: J. Bruce Fields @ 2016-09-24 15:02 UTC (permalink / raw)
  To: Jeff Layton; +Cc: trond.myklebust, anna.schumaker, linux-nfs

On Fri, Sep 23, 2016 at 08:43:44PM -0400, Jeff Layton wrote:
> On Fri, 2016-09-23 at 17:19 -0400, J. Bruce Fields wrote:
> > On Fri, Sep 16, 2016 at 04:28:24PM -0400, Jeff Layton wrote:
> > > 
> > > Create a new per-lockowner+per-inode structure that contains a
> > > file_lock. Have nfsd4_lock add this structure to the lockowner's list
> > > prior to setting the lock. Then call the vfs and request a blocking lock
> > > (by setting FL_SLEEP). If we get anything besides FILE_LOCK_DEFERRED
> > 
> > That doesn't sound right.  FILE_LOCK_DEFERRED is a special case for
> > asynchronous locking--it means "I don't know whether there's a conflict
> > or not, it may take a while to check", not "there's a conflict".
> > 
> > (Ugh.  I apologize for the asynchronous locking code.)
> > 
> > --b.
> > 
> 
> The local file locking code definitely uses this return code to mean
> "This lock is blocked, and I'll call your lm_notify when it's
> unblocked", which is exactly what we rely on here.
> 
> There is a place that uses it in the way you mention though, and that is
> when DLM and svc lockd are interacting via dlm_posix_lock(). lockd can't
> be in play here since this is all NFSv4, so I think the code does handle
> this correctly.

Got it, my apologies!  I'll read some more....

The patches look fine as far as I can tell.

> That said...maybe should probably think about some way to disambiguate
> those two states in the return code. It is horribly confusing.

Yes.

Honestly maybe the asynchronous dlm case just shouldn't be there.  I
remember thinking multithreading lockd would accomplish the same.  But
maybe we already have that in the nfsv4 case, in which case who cares.
(Well, except maybe the locking effectively serializes locking anyway, I
haven't looked.)

--b.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3 2/5] nfsd: have nfsd4_lock use blocking locks for v4.1+ locks
  2016-09-24 15:02       ` J. Bruce Fields
@ 2016-09-24 16:48         ` Jeff Layton
  0 siblings, 0 replies; 13+ messages in thread
From: Jeff Layton @ 2016-09-24 16:48 UTC (permalink / raw)
  To: J. Bruce Fields; +Cc: trond.myklebust, anna.schumaker, linux-nfs

On Sat, 2016-09-24 at 11:02 -0400, J. Bruce Fields wrote:
> On Fri, Sep 23, 2016 at 08:43:44PM -0400, Jeff Layton wrote:
> > 
> > On Fri, 2016-09-23 at 17:19 -0400, J. Bruce Fields wrote:
> > > 
> > > On Fri, Sep 16, 2016 at 04:28:24PM -0400, Jeff Layton wrote:
> > > > 
> > > > 
> > > > Create a new per-lockowner+per-inode structure that contains a
> > > > file_lock. Have nfsd4_lock add this structure to the lockowner's list
> > > > prior to setting the lock. Then call the vfs and request a blocking lock
> > > > (by setting FL_SLEEP). If we get anything besides FILE_LOCK_DEFERRED
> > > 
> > > That doesn't sound right.  FILE_LOCK_DEFERRED is a special case for
> > > asynchronous locking--it means "I don't know whether there's a conflict
> > > or not, it may take a while to check", not "there's a conflict".
> > > 
> > > (Ugh.  I apologize for the asynchronous locking code.)
> > > 
> > > --b.
> > > 
> > 
> > The local file locking code definitely uses this return code to mean
> > "This lock is blocked, and I'll call your lm_notify when it's
> > unblocked", which is exactly what we rely on here.
> > 
> > There is a place that uses it in the way you mention though, and that is
> > when DLM and svc lockd are interacting via dlm_posix_lock(). lockd can't
> > be in play here since this is all NFSv4, so I think the code does handle
> > this correctly.
> 
> Got it, my apologies!  I'll read some more....
> 
> The patches look fine as far as I can tell.
> 

No worries. It is confusing code, especially once lockd is in the mix.
Thanks for having a look at the set.

> > 
> > That said...maybe should probably think about some way to disambiguate
> > those two states in the return code. It is horribly confusing.
> 
> Yes.
> 
> Honestly maybe the asynchronous dlm case just shouldn't be there.  I
> remember thinking multithreading lockd would accomplish the same.  But
> maybe we already have that in the nfsv4 case, in which case who cares.
> (Well, except maybe the locking effectively serializes locking anyway, I
> haven't looked.)
> 

Maybe, but it's hard to know who is currently relying on this, and as
far as I can tell, it's not broken. I'd hate to remove the functionality
without some way to gauge that.

What I was thinking was that we could add a new FILE_LOCK_BLOCKED error
code and use that everywhere but the DLM case. The DLM case could then
use FILE_LOCK_DEFERRED to convey the situation you mentioned before.

That said, I'd rather not do that in the context of this set. I'd need
to spend some time in the lockd code especially, to make sure that that 
wouldn't break anything.

-- 
Jeff Layton <jlayton@redhat.com>

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3 0/5] add CB_NOTIFY_LOCK support to knfsd
  2016-09-24  0:48   ` Jeff Layton
@ 2016-09-26 16:03     ` J. Bruce Fields
  0 siblings, 0 replies; 13+ messages in thread
From: J. Bruce Fields @ 2016-09-26 16:03 UTC (permalink / raw)
  To: Jeff Layton; +Cc: trond.myklebust, anna.schumaker, linux-nfs

On Fri, Sep 23, 2016 at 08:48:26PM -0400, Jeff Layton wrote:
> On Fri, 2016-09-23 at 17:05 -0400, J. Bruce Fields wrote:
> > On Fri, Sep 16, 2016 at 04:28:22PM -0400, Jeff Layton wrote:
> > > 
> > > v3:
> > > - add NFS4_OPEN_RESULT_MAY_NOTIFY_LOCK in a separate patch
> > > 
> > > v2:
> > > - small bugfixes
> > > 
> > > Very minor update to the patchset I sent a week or so ago. The only real
> > > difference from the last is to move the addition of
> > > NFS4_OPEN_RESULT_MAY_NOTIFY_LOCK to a separate patch.
> > > 
> > > The basic idea is to just add support for CB_NOTIFY_LOCK callbacks,
> > > which just tell the client that it may want to retry a lock again
> > > once it becomes available.
> > > 
> > > Tested in conjunction with the corresponding client-side patch
> > > series.
> > 
> > What sort of test were you doing?
> > 
> 
> I did several: cthon (of course), the lockperf suite (which still takes
> quite a while to run). Mostly I just verified that I got callbacks when
> expected and that the client acted on them using wireshark. Some testing
> under more load would be great.
> 
> > Have you checked with wireshark's CB_NOTIFY_LOCK support is complete?
> > 
> 
> Just recently fixed:
> 
> https://code.wireshark.org/review/gitweb?p=wireshark.git;a=commitdiff;h=1948f7bd7553f215dc2519a35dcd62e29e35a614

Excellent, thanks!

--b.

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2016-09-26 16:03 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-09-16 20:28 [PATCH v3 0/5] add CB_NOTIFY_LOCK support to knfsd Jeff Layton
2016-09-16 20:28 ` [PATCH v3 1/5] nfsd: plumb in a CB_NOTIFY_LOCK operation Jeff Layton
2016-09-16 20:28 ` [PATCH v3 2/5] nfsd: have nfsd4_lock use blocking locks for v4.1+ locks Jeff Layton
2016-09-23 21:19   ` J. Bruce Fields
2016-09-24  0:43     ` Jeff Layton
2016-09-24 15:02       ` J. Bruce Fields
2016-09-24 16:48         ` Jeff Layton
2016-09-16 20:28 ` [PATCH v3 3/5] nfsd: add a LRU list for blocked locks Jeff Layton
2016-09-16 20:28 ` [PATCH v3 4/5] nfs: add a new NFS4_OPEN_RESULT_MAY_NOTIFY_LOCK constant Jeff Layton
2016-09-16 20:28 ` [PATCH v3 5/5] nfsd: set the MAY_NOTIFY_LOCK flag in OPEN replies Jeff Layton
2016-09-23 21:05 ` [PATCH v3 0/5] add CB_NOTIFY_LOCK support to knfsd J. Bruce Fields
2016-09-24  0:48   ` Jeff Layton
2016-09-26 16:03     ` J. Bruce Fields

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.