Skip to contentSkip to conversation

Skipped unlocks

PATCH Superseded Benjamin Coddington 3 participants 16 messages Apr 1, 2017 – Apr 6, 2017

Subsystems (5)

v4 of 6 Superseded Latest is v6 →

lore
Share & tools
lore
#0 lore raw

v4 cover Skipped unlocks

Well over a year ago I made other attempts to fix the problem of NFS failing to send an unlock when signalled. Those attempts were terrible.

Here's yet another version, this time using the approach suggested by Jeff Layton to utilize an RPC waitqueue to defer unlock tasks until IO has completed. In order to do this for NLM without violating all the layers, a new nlmclnt_operations struct is defined and passed in to allow the NLM client to call back out to NFS at various stages.

Comments and review are welcomed.

since v1:
- add Christoph's reviewed-by on 1/4 and 2/4 and fixup switch indentation on 2/4
since v2:
- don't sleep in rpciod to wait for I/O completion, just send the unlock immediately for both v3 and v4.
since v3:
- don't just skip the wait for FL_CLOSE. Instead, build new machinery into NLM to allow waiting in rpc_call_prepare().

Benjamin Coddington (6):
NFS4: remove a redundant lock range check
NFS: Move the flock open mode check into nfs_flock()
locks: Set FL_CLOSE when removing flock locks on close()
NFS: Add an iocounter wait function for async RPC tasks
lockd: Introduce nlmclnt_operations
NFS: Always wait for I/O completion before unlock

 fs/fuse/file.c              |  2 +-
 fs/lockd/clntproc.c         | 26 +++++++++++++++++++++++++-
 fs/locks.c                  |  2 +-
 fs/nfs/client.c             |  1 +
 fs/nfs/file.c               | 28 +++++++++++++++++++++-------
 fs/nfs/nfs3proc.c           | 38 +++++++++++++++++++++++++++++++++++++-
 fs/nfs/nfs4proc.c           | 27 +++++++--------------------
 fs/nfs/pagelist.c           | 26 +++++++++++++++++++++++++-
 fs/nfs/proc.c               |  2 +-
 include/linux/fs.h          |  2 ++
 include/linux/lockd/bind.h  | 11 +++++++++--
 include/linux/lockd/lockd.h |  2 ++
 include/linux/nfs_fs.h      |  1 +
 include/linux/nfs_fs_sb.h   |  1 +
 include/linux/nfs_page.h    |  1 +
 15 files changed, 135 insertions(+), 35 deletions(-)
Signature · 1 line
2.9.3
#1 lore raw

PATCH v4 1/6 NFS4: remove a redundant lock range check

flock64_to_posix_lock() is already doing this check

---
 fs/nfs/nfs4proc.c | 3 ---
 1 file changed, 3 deletions(-)
fs/nfs/nfs4proc.c +0 -3
index 0a0eaecf9676..9388899e4050 100644
@@ -6570,9 +6570,6 @@ nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request)
 	ctx = nfs_file_open_context(filp);
 	state = ctx->state;
 
-	if (request->fl_start < 0 || request->fl_end < 0)
-		return -EINVAL;
-
 	if (IS_GETLK(cmd)) {
 		if (state != NULL)
 			return nfs4_proc_getlk(state, F_GETLK, request);
Signature · 1 line
2.9.3
Reviewed-by on v4 1/6
#2 lore raw

Re: [PATCH 1/6] NFS4: remove a redundant lock range check

Quoted 14 lines · Benjamin Coddington

flock64_to_posix_lock() is already doing this check

---
 fs/nfs/nfs4proc.c | 3 ---
 1 file changed, 3 deletions(-)
fs/nfs/nfs4proc.c +0 -0
index 0a0eaecf9676..9388899e4050 100644

fs/nfs/nfs4proc.c nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request) · line 6575

Quoted 9 lines
fs/nfs/nfs4proc.c +0 -3
@@ -6570,9 +6570,6 @@ nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request)
 	ctx = nfs_file_open_context(filp);
 	state = ctx->state;
 
-	if (request->fl_start < 0 || request->fl_end < 0)
-		return -EINVAL;
-
 	if (IS_GETLK(cmd)) {
 		if (state != NULL)
 			return nfs4_proc_getlk(state, F_GETLK, request);
#3 lore raw

PATCH v4 2/6 NFS: Move the flock open mode check into nfs_flock()

In reply to Benjamin Coddington

We only need to check lock exclusive/shared types against open mode when flock() is used on NFS, so move it into the flock-specific path instead of checking it for all locks.

---
 fs/nfs/file.c     | 18 ++++++++++++++++--
 fs/nfs/nfs4proc.c | 14 --------------
 2 files changed, 16 insertions(+), 16 deletions(-)
fs/nfs/file.c +16 -2
index 26dbe8b0c10d..a490f45df4db 100644
@@ -820,9 +820,23 @@ int nfs_flock(struct file *filp, int cmd, struct file_lock *fl)
 	if (NFS_SERVER(inode)->flags & NFS_MOUNT_LOCAL_FLOCK)
 		is_local = 1;
 
-	/* We're simulating flock() locks using posix locks on the server */
-	if (fl->fl_type == F_UNLCK)
+	/*
+	 * VFS doesn't require the open mode to match a flock() lock's type.
+	 * NFS, however, may simulate flock() locking with posix locking which
+	 * requires the open mode to match the lock type.
+	 */
+	switch (fl->fl_type) {
+	case F_UNLCK:
 		return do_unlk(filp, cmd, fl, is_local);
+	case F_RDLCK:
+		if (!(filp->f_mode & FMODE_READ))
+			return -EBADF;
+		break;
+	case F_WRLCK:
+		if (!(filp->f_mode & FMODE_WRITE))
+			return -EBADF;
+	}
+
 	return do_setlk(filp, cmd, fl, is_local);
 }
 EXPORT_SYMBOL_GPL(nfs_flock);
fs/nfs/nfs4proc.c +0 -14
index 9388899e4050..91f88bfbbe79 100644
@@ -6592,20 +6592,6 @@ nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request)
 	    !test_bit(NFS_STATE_POSIX_LOCKS, &state->flags))
 		return -ENOLCK;
 
-	/*
-	 * Don't rely on the VFS having checked the file open mode,
-	 * since it won't do this for flock() locks.
-	 */
-	switch (request->fl_type) {
-	case F_RDLCK:
-		if (!(filp->f_mode & FMODE_READ))
-			return -EBADF;
-		break;
-	case F_WRLCK:
-		if (!(filp->f_mode & FMODE_WRITE))
-			return -EBADF;
-	}
-
 	status = nfs4_set_lock_state(state, request);
 	if (status != 0)
 		return status;
Signature · 1 line
2.9.3
Reviewed-by on v4 2/6
#4 lore raw

Re: [PATCH 2/6] NFS: Move the flock open mode check into nfs_flock()

Quoted 57 lines · Benjamin Coddington

We only need to check lock exclusive/shared types against open mode when flock() is used on NFS, so move it into the flock-specific path instead of checking it for all locks.

---
 fs/nfs/file.c     | 18 ++++++++++++++++--
 fs/nfs/nfs4proc.c | 14 --------------
 2 files changed, 16 insertions(+), 16 deletions(-)
fs/nfs/file.c +16 -2
index 26dbe8b0c10d..a490f45df4db 100644
@@ -820,9 +820,23 @@ int nfs_flock(struct file *filp, int cmd, struct file_lock *fl)
 	if (NFS_SERVER(inode)->flags & NFS_MOUNT_LOCAL_FLOCK)
 		is_local = 1;
 
-	/* We're simulating flock() locks using posix locks on the server */
-	if (fl->fl_type == F_UNLCK)
+	/*
+	 * VFS doesn't require the open mode to match a flock() lock's type.
+	 * NFS, however, may simulate flock() locking with posix locking which
+	 * requires the open mode to match the lock type.
+	 */
+	switch (fl->fl_type) {
+	case F_UNLCK:
 		return do_unlk(filp, cmd, fl, is_local);
+	case F_RDLCK:
+		if (!(filp->f_mode & FMODE_READ))
+			return -EBADF;
+		break;
+	case F_WRLCK:
+		if (!(filp->f_mode & FMODE_WRITE))
+			return -EBADF;
+	}
+
 	return do_setlk(filp, cmd, fl, is_local);
 }
 EXPORT_SYMBOL_GPL(nfs_flock);
fs/nfs/nfs4proc.c +0 -7
index 9388899e4050..91f88bfbbe79 100644
@@ -6592,20 +6592,6 @@ nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request)
 	    !test_bit(NFS_STATE_POSIX_LOCKS, &state->flags))
 		return -ENOLCK;
 
-	/*
-	 * Don't rely on the VFS having checked the file open mode,
-	 * since it won't do this for flock() locks.
-	 */
-	switch (request->fl_type) {
-	case F_RDLCK:
-		if (!(filp->f_mode & FMODE_READ))

fs/nfs/nfs4proc.c nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request) · line 6597

Quoted 10 lines
fs/nfs/nfs4proc.c +0 -7
-			return -EBADF;
-		break;
-	case F_WRLCK:
-		if (!(filp->f_mode & FMODE_WRITE))
-			return -EBADF;
-	}
-
 	status = nfs4_set_lock_state(state, request);
 	if (status != 0)
 		return status;
#5 lore raw

PATCH v4 3/6 locks: Set FL_CLOSE when removing flock locks on close()

In reply to Benjamin Coddington

Set FL_CLOSE in fl_flags as in locks_remove_posix() when clearing locks. NFS will check for this flag to ensure an unlock is sent in a following patch.

---
 fs/fuse/file.c     | 2 +-
 fs/locks.c         | 2 +-
 include/linux/fs.h | 2 ++
 3 files changed, 4 insertions(+), 2 deletions(-)
fs/fuse/file.c +1 -1
index 2401c5dabb2a..79e1a3a048a8 100644
@@ -2172,7 +2172,7 @@ static int fuse_setlk(struct file *file, struct file_lock *fl, int flock)
 	}
 
 	/* Unlock on close is handled by the flush method */
-	if (fl->fl_flags & FL_CLOSE)
+	if (fl->fl_flags & FL_CLOSE_POSIX == FL_CLOSE_POSIX)
 		return 0;
 
 	fuse_lk_fill(&args, file, fl, opcode, pid, flock, &inarg);
fs/locks.c +1 -1
index 26811321d39b..af2031a1fcff 100644
@@ -2504,7 +2504,7 @@ locks_remove_flock(struct file *filp, struct file_lock_context *flctx)
 		.fl_owner = filp,
 		.fl_pid = current->tgid,
 		.fl_file = filp,
-		.fl_flags = FL_FLOCK,
+		.fl_flags = FL_FLOCK | FL_CLOSE,
 		.fl_type = F_UNLCK,
 		.fl_end = OFFSET_MAX,
 	};
include/linux/fs.h +2 -0
index 2ba074328894..e964ae0a9c29 100644
@@ -903,6 +903,8 @@ static inline struct file *get_file(struct file *f)
 #define FL_OFDLCK	1024	/* lock is "owned" by struct file */
 #define FL_LAYOUT	2048	/* outstanding pNFS layout */
 
+#define FL_CLOSE_POSIX (FL_POSIX | FL_CLOSE)
+
 /*
  * Special return value from posix_lock_file() and vfs_lock_file() for
  * asynchronous locking.
Signature · 1 line
2.9.3
Reviewed-by on v4 3/6
#6 lore raw

Re: [PATCH 3/6] locks: Set FL_CLOSE when removing flock locks on close()

Quoted 4 lines · Benjamin Coddington

Set FL_CLOSE in fl_flags as in locks_remove_posix() when clearing locks. NFS will check for this flag to ensure an unlock is sent in a following patch.

Might be nice to add some verbiage here about why fuse needs to be changed, just for posterity. I recall the discussion a while back so I think the change itself looks fine.

Quoted 38 lines
---
 fs/fuse/file.c     | 2 +-
 fs/locks.c         | 2 +-
 include/linux/fs.h | 2 ++
 3 files changed, 4 insertions(+), 2 deletions(-)
fs/fuse/file.c +1 -1
index 2401c5dabb2a..79e1a3a048a8 100644
@@ -2172,7 +2172,7 @@ static int fuse_setlk(struct file *file, struct file_lock *fl, int flock)
 	}
 
 	/* Unlock on close is handled by the flush method */
-	if (fl->fl_flags & FL_CLOSE)
+	if (fl->fl_flags & FL_CLOSE_POSIX == FL_CLOSE_POSIX)
 		return 0;
 
 	fuse_lk_fill(&args, file, fl, opcode, pid, flock, &inarg);
fs/locks.c +1 -1
index 26811321d39b..af2031a1fcff 100644
@@ -2504,7 +2504,7 @@ locks_remove_flock(struct file *filp, struct file_lock_context *flctx)
 		.fl_owner = filp,
 		.fl_pid = current->tgid,
 		.fl_file = filp,
-		.fl_flags = FL_FLOCK,
+		.fl_flags = FL_FLOCK | FL_CLOSE,
 		.fl_type = F_UNLCK,
 		.fl_end = OFFSET_MAX,
 	};
include/linux/fs.h +0 -0
index 2ba074328894..e964ae0a9c29 100644

include/linux/fs.h static inline struct file *get_file(struct file *f) · line 910

Quoted 8 lines
include/linux/fs.h +2 -0
@@ -903,6 +903,8 @@ static inline struct file *get_file(struct file *f)
 #define FL_OFDLCK	1024	/* lock is "owned" by struct file */
 #define FL_LAYOUT	2048	/* outstanding pNFS layout */
 
+#define FL_CLOSE_POSIX (FL_POSIX | FL_CLOSE)
+
 /*
  * Special return value from posix_lock_file() and vfs_lock_file() for
  * asynchronous locking.
#7 lore raw

Re: [PATCH 3/6] locks: Set FL_CLOSE when removing flock locks on close()

Quoted 9 lines · Jeff Layton
Quoted 4 lines · Benjamin Coddington

Set FL_CLOSE in fl_flags as in locks_remove_posix() when clearing locks. NFS will check for this flag to ensure an unlock is sent in a following patch.

Might be nice to add some verbiage here about why fuse needs to be changed, just for posterity. I recall the discussion a while back so I think the change itself looks fine.

OK, I'll do that.

Ben

#8 lore raw

PATCH v4 4/6 NFS: Add an iocounter wait function for async RPC tasks

In reply to Benjamin Coddington

By sleeping on a new NFS Unlock-On-Close waitqueue, rpc tasks may wait for a lock context's iocounter to reach zero. The rpc waitqueue is only woken when the open_context has the NFS_CONTEXT_UNLOCK flag set in order to mitigate spurious wake-ups for any iocounter reaching zero.

---
 fs/nfs/client.c           |  1 +
 fs/nfs/pagelist.c         | 26 +++++++++++++++++++++++++-
 include/linux/nfs_fs.h    |  1 +
 include/linux/nfs_fs_sb.h |  1 +
 include/linux/nfs_page.h  |  1 +
 5 files changed, 29 insertions(+), 1 deletion(-)
fs/nfs/client.c +1 -0
index 91a8d610ba0f..c335c6dce285 100644
@@ -218,6 +218,7 @@ static void nfs_cb_idr_remove_locked(struct nfs_client *clp)
 static void pnfs_init_server(struct nfs_server *server)
 {
 	rpc_init_wait_queue(&server->roc_rpcwaitq, "pNFS ROC");
+	rpc_init_wait_queue(&server->uoc_rpcwaitq, "NFS UOC");
 }
 
 #else
fs/nfs/pagelist.c +25 -1
index 6e629b856a00..d163819e4416 100644
@@ -115,6 +115,27 @@ nfs_iocounter_wait(struct nfs_lock_context *l_ctx)
 			TASK_KILLABLE);
 }
 
+bool
+nfs_async_iocounter_wait(struct rpc_task *task, void *data)
+{
+	struct nfs_lock_context *l_ctx = data;
+	struct inode *inode = d_inode(l_ctx->open_context->dentry);
+	bool ret = false;
+
+	if (atomic_read(&l_ctx->io_count) > 0) {
+		rpc_sleep_on(&NFS_SERVER(inode)->uoc_rpcwaitq, task, NULL);
+		ret = true;
+	}
+
+	if (atomic_read(&l_ctx->io_count) == 0) {
+		rpc_wake_up_queued_task(&NFS_SERVER(inode)->uoc_rpcwaitq, task);
+		ret = false;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(nfs_async_iocounter_wait);
+
 /*
  * nfs_page_group_lock - lock the head of the page group
  * @req - request in group that is to be locked
@@ -398,8 +419,11 @@ static void nfs_clear_request(struct nfs_page *req)
 		req->wb_page = NULL;
 	}
 	if (l_ctx != NULL) {
-		if (atomic_dec_and_test(&l_ctx->io_count))
+		if (atomic_dec_and_test(&l_ctx->io_count)) {
 			wake_up_atomic_t(&l_ctx->io_count);
+			if (test_bit(NFS_CONTEXT_UNLOCK, &ctx->flags))
+				rpc_wake_up(&NFS_SERVER(d_inode(ctx->dentry))->uoc_rpcwaitq);
+		}
 		nfs_put_lock_context(l_ctx);
 		req->wb_lock_context = NULL;
 	}
include/linux/nfs_fs.h +1 -0
index f1da8c8dd473..3ce3e42beead 100644
@@ -76,6 +76,7 @@ struct nfs_open_context {
 #define NFS_CONTEXT_ERROR_WRITE		(0)
 #define NFS_CONTEXT_RESEND_WRITES	(1)
 #define NFS_CONTEXT_BAD			(2)
+#define NFS_CONTEXT_UNLOCK	(3)
 	int error;
 
 	struct list_head list;
include/linux/nfs_fs_sb.h +1 -0
index b34097c67848..a62cc2b0d30d 100644
@@ -196,6 +196,7 @@ struct nfs_server {
 						   this filesystem */
 	struct pnfs_layoutdriver_type  *pnfs_curr_ld; /* Active layout driver */
 	struct rpc_wait_queue	roc_rpcwaitq;
+	struct rpc_wait_queue	uoc_rpcwaitq;
 	void			*pnfs_ld_data;	/* per mount point data */
 
 	/* the following fields are protected by nfs_client->cl_lock */
include/linux/nfs_page.h +1 -0
index 957049f72290..d39ebcba52c8 100644
@@ -141,6 +141,7 @@ extern int nfs_page_group_lock(struct nfs_page *, bool);
 extern void nfs_page_group_lock_wait(struct nfs_page *);
 extern void nfs_page_group_unlock(struct nfs_page *);
 extern bool nfs_page_group_sync_on_bit(struct nfs_page *, unsigned int);
+extern bool nfs_async_iocounter_wait(struct rpc_task *, void *);
 
 /*
  * Lock the page of an asynchronous request
Signature · 1 line
2.9.3
#9 lore raw

Re: [PATCH 4/6] NFS: Add an iocounter wait function for async RPC tasks

reproduce: wget https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # save the attached .config to linux build tree make.cross…

Hi Benjamin,

[auto build test ERROR on nfs/linux-next]
[also build test ERROR on v4.11-rc4 next-20170331]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Benjamin-Coddington/Skipped-unlocks/20170402-195846
base:   git://git.linux-nfs.org/projects/trondmy/linux-nfs.git linux-next
config: cris-etrax-100lx_v2_defconfig (attached as .config)
compiler: cris-linux-gcc (GCC) 6.2.0

reproduce:
wget https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=cris

All errors (new ones prefixed by >>):

fs/nfs/pagelist.c: In function 'nfs_async_iocounter_wait':

>> fs/nfs/pagelist.c:126:34: error: 'struct nfs_server' has no member named 'uoc_rpcwaitq'
      rpc_sleep_on(&NFS_SERVER(inode)->uoc_rpcwaitq, task, NULL);
                                     ^~
   fs/nfs/pagelist.c:131:45: error: 'struct nfs_server' has no member named 'uoc_rpcwaitq'
      rpc_wake_up_queued_task(&NFS_SERVER(inode)->uoc_rpcwaitq, task);
                                                ^~
   fs/nfs/pagelist.c: In function 'nfs_clear_request':
   fs/nfs/pagelist.c:425:50: error: 'struct nfs_server' has no member named 'uoc_rpcwaitq'
        rpc_wake_up(&NFS_SERVER(d_inode(ctx->dentry))->uoc_rpcwaitq);
                                                     ^~

vim +126 fs/nfs/pagelist.c

120 {
121 struct nfs_lock_context *l_ctx = data;
122 struct inode *inode = d_inode(l_ctx->open_context->dentry);
123 bool ret = false;
124
125 if (atomic_read(&l_ctx->io_count) > 0) {

 > 126			rpc_sleep_on(&NFS_SERVER(inode)->uoc_rpcwaitq, task, NULL);

127 ret = true;
128 }
129

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation

#10 lore raw

Re: [PATCH 4/6] NFS: Add an iocounter wait function for async RPC tasks

Ah, thanks robot. I didn't notice the:

172 #if IS_ENABLED(CONFIG_NFS_V4)
173 u32 attr_bitmask[3];/* V4 bitmask representing the set
...
199 struct rpc_wait_queue uoc_rpcwaitq;

I will re-post this one.

Quoted 50 lines · kbuild test robot

Hi Benjamin,

[auto build test ERROR on nfs/linux-next]
[also build test ERROR on v4.11-rc4 next-20170331]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Benjamin-Coddington/Skipped-unlocks/20170402-195846
base:   git://git.linux-nfs.org/projects/trondmy/linux-nfs.git linux-next
config: cris-etrax-100lx_v2_defconfig (attached as .config)
compiler: cris-linux-gcc (GCC) 6.2.0

reproduce:
wget
https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross
-O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree make.cross ARCH=cris

All errors (new ones prefixed by >>):

fs/nfs/pagelist.c: In function 'nfs_async_iocounter_wait':

Quoted 1 line
Quoted 1 line

fs/nfs/pagelist.c:126:34: error: 'struct nfs_server' has no member named 'uoc_rpcwaitq'

      rpc_sleep_on(&NFS_SERVER(inode)->uoc_rpcwaitq, task, NULL);
                                     ^~
   fs/nfs/pagelist.c:131:45: error: 'struct nfs_server' has no member 
named 'uoc_rpcwaitq'
      rpc_wake_up_queued_task(&NFS_SERVER(inode)->uoc_rpcwaitq, task);
                                                ^~
   fs/nfs/pagelist.c: In function 'nfs_clear_request':
   fs/nfs/pagelist.c:425:50: error: 'struct nfs_server' has no member 
named 'uoc_rpcwaitq'
        rpc_wake_up(&NFS_SERVER(d_inode(ctx->dentry))->uoc_rpcwaitq);
                                                     ^~

vim +126 fs/nfs/pagelist.c

   120	{
   121		struct nfs_lock_context *l_ctx = data;
   122		struct inode *inode = d_inode(l_ctx->open_context->dentry);
   123		bool ret = false;
   124	
   125		if (atomic_read(&l_ctx->io_count) > 0) {
Quoted 1 line

126 rpc_sleep_on(&NFS_SERVER(inode)->uoc_rpcwaitq, task, NULL);

   127			ret = true;
   128		}
   129	

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation

#11 lore raw

PATCH v4 5/6 lockd: Introduce nlmclnt_operations

In reply to Benjamin Coddington

NFS would enjoy the ability to modify the behavior of the NLM client's unlock RPC task in order to delay the transmission of the unlock until IO that was submitted under that lock has completed. This ability can ensure that the NLM client will always complete the transmission of an unlock even if the waiting caller has been interrupted with fatal signal.

A struct nlmclnt_operations and callback data pointer can be passed to nlmclnt_proc(). The struct nlmclnt_operations defines three callback operations that will be used in a following patch:

nlmclnt_alloc_call - used to call back after a successful allocation of
a struct nlm_rqst in nlmclnt_proc().

nlmclnt_unlock_prepare - used to call back during NLM unlock's
rpc_call_prepare. The NLM client defers calling rpc_call_start() until this callback returns false.

nlmclnt_release_call - used to call back when the NLM client's struct
nlm_rqst is freed.

---
 fs/lockd/clntproc.c         | 26 +++++++++++++++++++++++++-
 fs/nfs/nfs3proc.c           |  2 +-
 fs/nfs/proc.c               |  2 +-
 include/linux/lockd/bind.h  | 11 +++++++++--
 include/linux/lockd/lockd.h |  2 ++
 5 files changed, 38 insertions(+), 5 deletions(-)
fs/lockd/clntproc.c +25 -1
index 112952037933..d2744f4960ed 100644
@@ -150,9 +150,12 @@ static void nlmclnt_release_lockargs(struct nlm_rqst *req)
  * @host: address of a valid nlm_host context representing the NLM server
  * @cmd: fcntl-style file lock operation to perform
  * @fl: address of arguments for the lock operation
+ * @nlmclnt_ops: operations to call back out of nlm
+ * @data: address of data to be sent along with callback
  *
  */
-int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl)
+int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl,
+		const struct nlmclnt_operations *nlmclnt_ops, void *data)
 {
 	struct nlm_rqst		*call;
 	int			status;
@@ -161,6 +164,9 @@ int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl)
 	if (call == NULL)
 		return -ENOMEM;
 
+	if (nlmclnt_ops && nlmclnt_ops->nlmclnt_alloc_call)
+		nlmclnt_ops->nlmclnt_alloc_call(data);
+
 	nlmclnt_locks_init_private(fl, host);
 	if (!fl->fl_u.nfs_fl.owner) {
 		/* lockowner allocation has failed */
@@ -169,6 +175,8 @@ int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl)
 	}
 	/* Set up the argument struct */
 	nlmclnt_setlockargs(call, fl);
+	call->nlmclnt_ops = nlmclnt_ops;
+	call->callback_data = data;
 
 	if (IS_SETLK(cmd) || IS_SETLKW(cmd)) {
 		if (fl->fl_type != F_UNLCK) {
@@ -216,6 +224,8 @@ void nlmclnt_release_call(struct nlm_rqst *call)
 {
 	if (!atomic_dec_and_test(&call->a_count))
 		return;
+	if (call->nlmclnt_ops && call->nlmclnt_ops->nlmclnt_release_call)
+		call->nlmclnt_ops->nlmclnt_release_call(call->callback_data);
 	nlmclnt_release_host(call->a_host);
 	nlmclnt_release_lockargs(call);
 	kfree(call);
@@ -687,6 +697,19 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl)
 	return status;
 }
 
+static void nlmclnt_unlock_prepare(struct rpc_task *task, void *data)
+{
+	struct nlm_rqst	*req = data;
+	const struct nlmclnt_operations *nlmclnt_ops = req->nlmclnt_ops;
+	bool defer_call = false;
+
+	if (nlmclnt_ops && nlmclnt_ops->nlmclnt_unlock_prepare)
+		defer_call = nlmclnt_ops->nlmclnt_unlock_prepare(task, req->callback_data);
+
+	if (!defer_call)
+		rpc_call_start(task);
+}
+
 static void nlmclnt_unlock_callback(struct rpc_task *task, void *data)
 {
 	struct nlm_rqst	*req = data;
@@ -720,6 +743,7 @@ static void nlmclnt_unlock_callback(struct rpc_task *task, void *data)
 }
 
 static const struct rpc_call_ops nlmclnt_unlock_ops = {
+	.rpc_call_prepare = nlmclnt_unlock_prepare,
 	.rpc_call_done = nlmclnt_unlock_callback,
 	.rpc_release = nlmclnt_rpc_release,
 };
fs/nfs/nfs3proc.c +1 -1
index dc925b531f32..086623ab07b1 100644
@@ -870,7 +870,7 @@ nfs3_proc_lock(struct file *filp, int cmd, struct file_lock *fl)
 {
 	struct inode *inode = file_inode(filp);
 
-	return nlmclnt_proc(NFS_SERVER(inode)->nlm_host, cmd, fl);
+	return nlmclnt_proc(NFS_SERVER(inode)->nlm_host, cmd, fl, NULL, NULL);
 }
 
 static int nfs3_have_delegation(struct inode *inode, fmode_t flags)
fs/nfs/proc.c +1 -1
index b7bca8303989..fb8cbdf0a980 100644
@@ -638,7 +638,7 @@ nfs_proc_lock(struct file *filp, int cmd, struct file_lock *fl)
 {
 	struct inode *inode = file_inode(filp);
 
-	return nlmclnt_proc(NFS_SERVER(inode)->nlm_host, cmd, fl);
+	return nlmclnt_proc(NFS_SERVER(inode)->nlm_host, cmd, fl, NULL, NULL);
 }
 
 /* Helper functions for NFS lock bounds checking */
include/linux/lockd/bind.h +9 -2
index 140edab64446..00c3c2a62bb5 100644
@@ -18,6 +18,7 @@
 
 /* Dummy declarations */
 struct svc_rqst;
+struct rpc_task;
 
 /*
  * This is the set of functions for lockd->nfsd communication
@@ -52,8 +53,14 @@ struct nlmclnt_initdata {
 extern struct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init);
 extern void	nlmclnt_done(struct nlm_host *host);
 
-extern int	nlmclnt_proc(struct nlm_host *host, int cmd,
-					struct file_lock *fl);
+struct nlmclnt_operations {
+	void (*nlmclnt_alloc_call)(void *);
+	bool (*nlmclnt_unlock_prepare)(struct rpc_task*, void *);
+	void (*nlmclnt_release_call)(void *);
+};
+
+extern int	nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl,
+	const struct nlmclnt_operations *nlmclnt_ops, void *data);
 extern int	lockd_up(struct net *net);
 extern void	lockd_down(struct net *net);
 
include/linux/lockd/lockd.h +2 -0
index c15373894a42..0c6f777dde53 100644
@@ -142,6 +142,8 @@ struct nlm_rqst {
 	struct nlm_block *	a_block;
 	unsigned int		a_retries;	/* Retry count */
 	u8			a_owner[NLMCLNT_OHSIZE];
+	const struct nlmclnt_operations * nlmclnt_ops;
+	void * callback_data;
 };
 
 /*
Signature · 1 line
2.9.3
#12 lore raw

Re: [PATCH 5/6] lockd: Introduce nlmclnt_operations

Quoted 34 lines · Benjamin Coddington

NFS would enjoy the ability to modify the behavior of the NLM client's unlock RPC task in order to delay the transmission of the unlock until IO that was submitted under that lock has completed. This ability can ensure that the NLM client will always complete the transmission of an unlock even if the waiting caller has been interrupted with fatal signal.

A struct nlmclnt_operations and callback data pointer can be passed to nlmclnt_proc(). The struct nlmclnt_operations defines three callback operations that will be used in a following patch:

nlmclnt_alloc_call - used to call back after a successful allocation of
a struct nlm_rqst in nlmclnt_proc().

nlmclnt_unlock_prepare - used to call back during NLM unlock's
rpc_call_prepare. The NLM client defers calling rpc_call_start() until this callback returns false.

nlmclnt_release_call - used to call back when the NLM client's struct
nlm_rqst is freed.

---
 fs/lockd/clntproc.c         | 26 +++++++++++++++++++++++++-
 fs/nfs/nfs3proc.c           |  2 +-
 fs/nfs/proc.c               |  2 +-
 include/linux/lockd/bind.h  | 11 +++++++++--
 include/linux/lockd/lockd.h |  2 ++
 5 files changed, 38 insertions(+), 5 deletions(-)
fs/lockd/clntproc.c +0 -0
index 112952037933..d2744f4960ed 100644

fs/lockd/clntproc.c static void nlmclnt_release_lockargs(struct nlm_rqst *req) · line 158

Quoted 10 lines
fs/lockd/clntproc.c +4 -1
@@ -150,9 +150,12 @@ static void nlmclnt_release_lockargs(struct nlm_rqst *req)
  * @host: address of a valid nlm_host context representing the NLM server
  * @cmd: fcntl-style file lock operation to perform
  * @fl: address of arguments for the lock operation
+ * @nlmclnt_ops: operations to call back out of nlm
+ * @data: address of data to be sent along with callback
  *
  */
-int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl)
+int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl,
+		const struct nlmclnt_operations *nlmclnt_ops, void *data)

Might it be cleaner to put the nlmclnt_operations into the nlm_host? Could pass a pointer to it in the nlmclnt_initdata when creating the nfs_server.

Quoted 128 lines
fs/lockd/clntproc.c +0 -0
 { You would probably need some way to indicate the NLM version you

want to use, but

fs/lockd/clntproc.c +21 -0
 	struct nlm_rqst		*call;
 	int			status;
@@ -161,6 +164,9 @@ int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl)
 	if (call == NULL)
 		return -ENOMEM;
 
+	if (nlmclnt_ops && nlmclnt_ops->nlmclnt_alloc_call)
+		nlmclnt_ops->nlmclnt_alloc_call(data);
+
 	nlmclnt_locks_init_private(fl, host);
 	if (!fl->fl_u.nfs_fl.owner) {
 		/* lockowner allocation has failed */
@@ -169,6 +175,8 @@ int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl)
 	}
 	/* Set up the argument struct */
 	nlmclnt_setlockargs(call, fl);
+	call->nlmclnt_ops = nlmclnt_ops;
+	call->callback_data = data;
 
 	if (IS_SETLK(cmd) || IS_SETLKW(cmd)) {
 		if (fl->fl_type != F_UNLCK) {
@@ -216,6 +224,8 @@ void nlmclnt_release_call(struct nlm_rqst *call)
 {
 	if (!atomic_dec_and_test(&call->a_count))
 		return;
+	if (call->nlmclnt_ops && call->nlmclnt_ops->nlmclnt_release_call)
+		call->nlmclnt_ops->nlmclnt_release_call(call->callback_data);
 	nlmclnt_release_host(call->a_host);
 	nlmclnt_release_lockargs(call);
 	kfree(call);
@@ -687,6 +697,19 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl)
 	return status;
 }
 
+static void nlmclnt_unlock_prepare(struct rpc_task *task, void *data)
+{
+	struct nlm_rqst	*req = data;
+	const struct nlmclnt_operations *nlmclnt_ops = req->nlmclnt_ops;
+	bool defer_call = false;
+
+	if (nlmclnt_ops && nlmclnt_ops->nlmclnt_unlock_prepare)
+		defer_call = nlmclnt_ops->nlmclnt_unlock_prepare(task, req->callback_data);
+
+	if (!defer_call)
+		rpc_call_start(task);
+}
+
 static void nlmclnt_unlock_callback(struct rpc_task *task, void *data)
 {
 	struct nlm_rqst	*req = data;
@@ -720,6 +743,7 @@ static void nlmclnt_unlock_callback(struct rpc_task *task, void *data)
 }
 
 static const struct rpc_call_ops nlmclnt_unlock_ops = {
+	.rpc_call_prepare = nlmclnt_unlock_prepare,
 	.rpc_call_done = nlmclnt_unlock_callback,
 	.rpc_release = nlmclnt_rpc_release,
 };
fs/nfs/nfs3proc.c +1 -1
index dc925b531f32..086623ab07b1 100644
@@ -870,7 +870,7 @@ nfs3_proc_lock(struct file *filp, int cmd, struct file_lock *fl)
 {
 	struct inode *inode = file_inode(filp);
 
-	return nlmclnt_proc(NFS_SERVER(inode)->nlm_host, cmd, fl);
+	return nlmclnt_proc(NFS_SERVER(inode)->nlm_host, cmd, fl, NULL, NULL);
 }
 
 static int nfs3_have_delegation(struct inode *inode, fmode_t flags)
fs/nfs/proc.c +1 -1
index b7bca8303989..fb8cbdf0a980 100644
@@ -638,7 +638,7 @@ nfs_proc_lock(struct file *filp, int cmd, struct file_lock *fl)
 {
 	struct inode *inode = file_inode(filp);
 
-	return nlmclnt_proc(NFS_SERVER(inode)->nlm_host, cmd, fl);
+	return nlmclnt_proc(NFS_SERVER(inode)->nlm_host, cmd, fl, NULL, NULL);
 }
 
 /* Helper functions for NFS lock bounds checking */
include/linux/lockd/bind.h +9 -2
index 140edab64446..00c3c2a62bb5 100644
@@ -18,6 +18,7 @@
 
 /* Dummy declarations */
 struct svc_rqst;
+struct rpc_task;
 
 /*
  * This is the set of functions for lockd->nfsd communication
@@ -52,8 +53,14 @@ struct nlmclnt_initdata {
 extern struct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init);
 extern void	nlmclnt_done(struct nlm_host *host);
 
-extern int	nlmclnt_proc(struct nlm_host *host, int cmd,
-					struct file_lock *fl);
+struct nlmclnt_operations {
+	void (*nlmclnt_alloc_call)(void *);
+	bool (*nlmclnt_unlock_prepare)(struct rpc_task*, void *);
+	void (*nlmclnt_release_call)(void *);
+};
+
+extern int	nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl,
+	const struct nlmclnt_operations *nlmclnt_ops, void *data);
 extern int	lockd_up(struct net *net);
 extern void	lockd_down(struct net *net);
 
include/linux/lockd/lockd.h +2 -0
index c15373894a42..0c6f777dde53 100644
@@ -142,6 +142,8 @@ struct nlm_rqst {
 	struct nlm_block *	a_block;
 	unsigned int		a_retries;	/* Retry count */
 	u8			a_owner[NLMCLNT_OHSIZE];
+	const struct nlmclnt_operations * nlmclnt_ops;
+	void * callback_data;
 };
 
 /*
Signature · 1 line
Jeff Layton <[email protected]>
#13 lore raw

Re: [PATCH 5/6] lockd: Introduce nlmclnt_operations

Quoted 45 lines · Jeff Layton
Quoted 44 lines · Benjamin Coddington

NFS would enjoy the ability to modify the behavior of the NLM client's unlock RPC task in order to delay the transmission of the unlock until IO that was submitted under that lock has completed. This ability can ensure that the NLM client will always complete the transmission of an unlock even if the waiting caller has been interrupted with fatal signal.

A struct nlmclnt_operations and callback data pointer can be passed to nlmclnt_proc(). The struct nlmclnt_operations defines three callback operations that will be used in a following patch:

nlmclnt_alloc_call - used to call back after a successful allocation of
a struct nlm_rqst in nlmclnt_proc().

nlmclnt_unlock_prepare - used to call back during NLM unlock's
rpc_call_prepare. The NLM client defers calling rpc_call_start() until this callback returns false.

nlmclnt_release_call - used to call back when the NLM client's struct
nlm_rqst is freed.

---
 fs/lockd/clntproc.c         | 26 +++++++++++++++++++++++++-
 fs/nfs/nfs3proc.c           |  2 +-
 fs/nfs/proc.c               |  2 +-
 include/linux/lockd/bind.h  | 11 +++++++++--
 include/linux/lockd/lockd.h |  2 ++
 5 files changed, 38 insertions(+), 5 deletions(-)
fs/lockd/clntproc.c +0 -0
index 112952037933..d2744f4960ed 100644
@@ -150,9 +150,12 @@ static void nlmclnt_release_lockargs(struct 
 nlm_rqst *req)
  * @host: address of a valid nlm_host context representing the NLM 

server
* @cmd: fcntl-style file lock operation to perform
* @fl: address of arguments for the lock operation
+ * @nlmclnt_ops: operations to call back out of nlm
+ * @data: address of data to be sent along with callback *
*/
-int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock

Quoted 8 lines

fs/lockd/clntproc.c static void nlmclnt_release_lockargs(struct nlm_rqst *req) · line 158

Quoted 4 lines

*fl)
+int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl,

fs/lockd/clntproc.c +1 -0
+		const struct nlmclnt_operations *nlmclnt_ops, void *data)

Might it be cleaner to put the nlmclnt_operations into the nlm_host? Could pass a pointer to it in the nlmclnt_initdata when creating the nfs_server.

Maybe, if 5 args here is too much. But passing nlmclnt_operations into nlm_init in nfs_start_lockd() would mean the function pointers in nlmclnt_operations would need to be exposed in client.c. While this has more args, I think it is a bit neater from the NFS side.

Ben

#14 lore raw

Re: [PATCH 5/6] lockd: Introduce nlmclnt_operations

Quoted 56 lines · Benjamin Coddington
Quoted 45 lines · Jeff Layton
Quoted 44 lines · Benjamin Coddington

NFS would enjoy the ability to modify the behavior of the NLM client's unlock RPC task in order to delay the transmission of the unlock until IO that was submitted under that lock has completed. This ability can ensure that the NLM client will always complete the transmission of an unlock even if the waiting caller has been interrupted with fatal signal.

A struct nlmclnt_operations and callback data pointer can be passed to nlmclnt_proc(). The struct nlmclnt_operations defines three callback operations that will be used in a following patch:

nlmclnt_alloc_call - used to call back after a successful allocation of
a struct nlm_rqst in nlmclnt_proc().

nlmclnt_unlock_prepare - used to call back during NLM unlock's
rpc_call_prepare. The NLM client defers calling rpc_call_start() until this callback returns false.

nlmclnt_release_call - used to call back when the NLM client's struct
nlm_rqst is freed.

---
 fs/lockd/clntproc.c         | 26 +++++++++++++++++++++++++-
 fs/nfs/nfs3proc.c           |  2 +-
 fs/nfs/proc.c               |  2 +-
 include/linux/lockd/bind.h  | 11 +++++++++--
 include/linux/lockd/lockd.h |  2 ++
 5 files changed, 38 insertions(+), 5 deletions(-)
fs/lockd/clntproc.c +0 -0
index 112952037933..d2744f4960ed 100644
@@ -150,9 +150,12 @@ static void nlmclnt_release_lockargs(struct 
 nlm_rqst *req)
  * @host: address of a valid nlm_host context representing the NLM 

server
* @cmd: fcntl-style file lock operation to perform
* @fl: address of arguments for the lock operation
+ * @nlmclnt_ops: operations to call back out of nlm
+ * @data: address of data to be sent along with callback *
*/
-int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock

Quoted 8 lines

fs/lockd/clntproc.c static void nlmclnt_release_lockargs(struct nlm_rqst *req) · line 158

Quoted 4 lines

*fl)
+int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl,

fs/lockd/clntproc.c +1 -0
+		const struct nlmclnt_operations *nlmclnt_ops, void *data)

Might it be cleaner to put the nlmclnt_operations into the nlm_host? Could pass a pointer to it in the nlmclnt_initdata when creating the nfs_server.

Quoted 4 lines

Maybe, if 5 args here is too much. But passing nlmclnt_operations into nlm_init in nfs_start_lockd() would mean the function pointers in nlmclnt_operations would need to be exposed in client.c. While this has more args, I think it is a bit neater from the NFS side.

Jeff, I looked at this again this morning, and I can't find a cleaner way to do this by putting the ops on the host. It will mean plumbing the ops into nfs_start_lockd. The other reason is that the ops are specific to the proc being passed in, so for LOCK we might use different operations. I'm open to suggestions, but I am going to send another version without this change.

Ben

#15 lore raw

PATCH v4 6/6 NFS: Always wait for I/O completion before unlock

In reply to Benjamin Coddington

NFS attempts to wait for read and write completion before unlocking in order to ensure that the data returned was protected by the lock. When this waiting is interrupted by a signal, the unlock may be skipped, and messages similar to the following are seen in the kernel ring buffer:

[20.167876] Leaked locks on dev=0x0:0x2b ino=0x8dd4c3:
[20.168286] POSIX: fl_owner=ffff880078b06940 fl_flags=0x1 fl_type=0x0 fl_pid=20183
[20.168727] POSIX: fl_owner=ffff880078b06680 fl_flags=0x1 fl_type=0x0 fl_pid=20185

For NFSv3, the missing unlock will cause the server to refuse conflicting locks indefinitely. For NFSv4, the leftover lock will be removed by the server after the lease timeout.

This patch fixes this issue by skipping the usual wait in nfs_iocounter_wait if the FL_CLOSE flag is set when signaled. Instead, the wait happens in the unlock RPC task on the NFS UOC rpc_waitqueue.

For NFSv3, use lockd's new nlmclnt_operations along with nfs_async_iocounter_wait to defer NLM's unlock task until the lock context's iocounter reaches zero.

For NFSv4, call nfs_async_iocounter_wait() directly from unlock's current rpc_call_prepare.

---
 fs/nfs/file.c     | 10 +++++-----
 fs/nfs/nfs3proc.c | 38 +++++++++++++++++++++++++++++++++++++-
 fs/nfs/nfs4proc.c | 10 +++++++---
 3 files changed, 49 insertions(+), 9 deletions(-)
fs/nfs/file.c +5 -5
index a490f45df4db..df695f52bb9d 100644
@@ -697,14 +697,14 @@ do_unlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)
 	if (!IS_ERR(l_ctx)) {
 		status = nfs_iocounter_wait(l_ctx);
 		nfs_put_lock_context(l_ctx);
-		if (status < 0)
+		/*  NOTE: special case
+		 * 	If we're signalled while cleaning up locks on process exit, we
+		 * 	still need to complete the unlock.
+		 */
+		if (status < 0 && !(fl->fl_flags & FL_CLOSE))
 			return status;
 	}
 
-	/* NOTE: special case
-	 * 	If we're signalled while cleaning up locks on process exit, we
-	 * 	still need to complete the unlock.
-	 */
 	/*
 	 * Use local locking if mounted with "-onolock" or with appropriate
 	 * "-olocal_lock="
fs/nfs/nfs3proc.c +37 -1
index 086623ab07b1..0e21306bfed9 100644
@@ -865,12 +865,48 @@ static void nfs3_proc_commit_setup(struct nfs_commit_data *data, struct rpc_mess
 	msg->rpc_proc = &nfs3_procedures[NFS3PROC_COMMIT];
 }
 
+void nfs3_nlm_alloc_call(void *data)
+{
+	struct nfs_lock_context *l_ctx = data;
+	nfs_get_lock_context(l_ctx->open_context);
+}
+
+void nfs3_nlm_release_call(void *data)
+{
+	struct nfs_lock_context *l_ctx = data;
+	nfs_put_lock_context(l_ctx);
+}
+
+const struct nlmclnt_operations nlmclnt_fl_close_lock_ops = {
+	.nlmclnt_alloc_call = nfs3_nlm_alloc_call,
+	.nlmclnt_unlock_prepare = nfs_async_iocounter_wait,
+	.nlmclnt_release_call = nfs3_nlm_release_call,
+};
+
 static int
 nfs3_proc_lock(struct file *filp, int cmd, struct file_lock *fl)
 {
 	struct inode *inode = file_inode(filp);
+	struct nfs_lock_context *l_ctx = NULL;
+	const struct nlmclnt_operations *nlmclnt_ops = NULL;
+	int status;
 
-	return nlmclnt_proc(NFS_SERVER(inode)->nlm_host, cmd, fl, NULL, NULL);
+	if (fl->fl_flags & FL_CLOSE) {
+		l_ctx = nfs_get_lock_context(nfs_file_open_context(filp));
+		if (IS_ERR(l_ctx)) {
+			l_ctx = NULL;
+		} else {
+			set_bit(NFS_CONTEXT_UNLOCK, &l_ctx->open_context->flags);
+			nlmclnt_ops = &nlmclnt_fl_close_lock_ops;
+		}
+	}
+
+	status = nlmclnt_proc(NFS_SERVER(inode)->nlm_host, cmd, fl,
+				nlmclnt_ops, l_ctx);
+	if (l_ctx)
+		nfs_put_lock_context(l_ctx);
+
+	return status;
 }
 
 static int nfs3_have_delegation(struct inode *inode, fmode_t flags)
fs/nfs/nfs4proc.c +7 -3
index 91f88bfbbe79..e46cc2be60e2 100644
@@ -5905,7 +5905,7 @@ struct nfs4_unlockdata {
 	struct nfs_locku_args arg;
 	struct nfs_locku_res res;
 	struct nfs4_lock_state *lsp;
-	struct nfs_open_context *ctx;
+	struct nfs_lock_context *l_ctx;
 	struct file_lock fl;
 	struct nfs_server *server;
 	unsigned long timestamp;
@@ -5929,7 +5929,7 @@ static struct nfs4_unlockdata *nfs4_alloc_unlockdata(struct file_lock *fl,
 	p->lsp = lsp;
 	atomic_inc(&lsp->ls_count);
 	/* Ensure we don't close file until we're done freeing locks! */
-	p->ctx = get_nfs_open_context(ctx);
+	p->l_ctx = nfs_get_lock_context(ctx);
 	memcpy(&p->fl, fl, sizeof(p->fl));
 	p->server = NFS_SERVER(inode);
 	return p;
@@ -5940,7 +5940,7 @@ static void nfs4_locku_release_calldata(void *data)
 	struct nfs4_unlockdata *calldata = data;
 	nfs_free_seqid(calldata->arg.seqid);
 	nfs4_put_lock_state(calldata->lsp);
-	put_nfs_open_context(calldata->ctx);
+	nfs_put_lock_context(calldata->l_ctx);
 	kfree(calldata);
 }
 
@@ -5981,6 +5981,10 @@ static void nfs4_locku_prepare(struct rpc_task *task, void *data)
 {
 	struct nfs4_unlockdata *calldata = data;
 
+	if (calldata->fl.fl_flags & FL_CLOSE &&
+		nfs_async_iocounter_wait(task, calldata->l_ctx))
+		return;
+
 	if (nfs_wait_on_sequence(calldata->arg.seqid, task) != 0)
 		goto out_wait;
 	nfs4_stateid_copy(&calldata->arg.stateid, &calldata->lsp->ls_stateid);
Signature · 1 line
2.9.3