From: Jeff Layton <jlayton@poochiereds.net>
To: Benjamin Coddington <bcodding@redhat.com>,
Trond Myklebust <trond.myklebust@primarydata.com>,
Anna Schumaker <anna.schumaker@netapp.com>,
bfields@fieldses.org, Miklos Szeredi <miklos@szeredi.hu>,
Alexander Viro <viro@zeniv.linux.org.uk>
Cc: linux-nfs@vger.kernel.org, linux-fsdevel@vger.kernel.org
Subject: Re: [PATCH 6/6] NFS: Always wait for I/O completion before unlock
Date: Fri, 07 Apr 2017 08:22:22 -0400 [thread overview]
Message-ID: <1491567742.2745.10.camel@poochiereds.net> (raw)
In-Reply-To: <0351835c423970c9171e446e918ac3d3d2fe854e.1491477181.git.bcodding@redhat.com>
On Thu, 2017-04-06 at 07:23 -0400, Benjamin Coddington wrote:
> 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.
>
Dare I ask about v2? :)
Hmm actually, it looks like v2 could use the same operations as v3. I
don't see anything protocol-specific there.
> Signed-off-by: Benjamin Coddington <bcodding@redhat.com>
> ---
> fs/nfs/file.c | 10 +++++-----
> fs/nfs/nfs3proc.c | 38 +++++++++++++++++++++++++++++++++++++-
> fs/nfs/nfs4proc.c | 10 +++++++---
> 3 files changed, 49 insertions(+), 9 deletions(-)
>
> diff --git a/fs/nfs/file.c b/fs/nfs/file.c
> index a490f45df4db..df695f52bb9d 100644
> --- a/fs/nfs/file.c
> +++ b/fs/nfs/file.c
> @@ -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="
> diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
> index 086623ab07b1..0e21306bfed9 100644
> --- a/fs/nfs/nfs3proc.c
> +++ b/fs/nfs/nfs3proc.c
> @@ -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;
FWIW, I'm not a huge fan of using the pointer to indicate whether to run
the operations or not. IMO, it'd be cleaner to:
1) store the pointer to the operations struct in the nlm_host, pass it a
pointer to it in the nlmclnt_initdata.
2) make the decision to do the operations or not based on the setting of
NFS_CONTEXT_UNLOCK in the callbacks themselves. If it's not set, just
return quickly without doing anything.
> + }
> + }
> +
> + 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)
> diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
> index 91f88bfbbe79..e46cc2be60e2 100644
> --- a/fs/nfs/nfs4proc.c
> +++ b/fs/nfs/nfs4proc.c
> @@ -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);
--
Jeff Layton <jlayton@poochiereds.net>
next prev parent reply other threads:[~2017-04-07 12:22 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-04-06 11:23 [PATCH v5 0/6] Skipped unlocks Benjamin Coddington
2017-04-06 11:23 ` [PATCH 1/6] NFS4: remove a redundant lock range check Benjamin Coddington
2017-04-06 11:23 ` [PATCH 2/6] NFS: Move the flock open mode check into nfs_flock() Benjamin Coddington
2017-04-06 11:23 ` [PATCH 3/6] locks: Set FL_CLOSE when removing flock locks on close() Benjamin Coddington
2017-04-06 18:17 ` Jeff Layton
2017-04-08 5:34 ` kbuild test robot
2017-04-06 11:23 ` [PATCH 4/6] NFS: Add an iocounter wait function for async RPC tasks Benjamin Coddington
2017-04-07 10:44 ` Jeff Layton
2017-04-07 11:26 ` Benjamin Coddington
2017-04-06 11:23 ` [PATCH 5/6] lockd: Introduce nlmclnt_operations Benjamin Coddington
2017-04-07 12:10 ` Jeff Layton
2017-04-06 11:23 ` [PATCH 6/6] NFS: Always wait for I/O completion before unlock Benjamin Coddington
2017-04-07 12:22 ` Jeff Layton [this message]
2017-04-07 13:34 ` Benjamin Coddington
-- strict thread matches above, loose matches on Subject: below --
2017-04-11 16:50 [PATCH v6 0/6] Skipped unlocks Benjamin Coddington
2017-04-11 16:50 ` [PATCH 6/6] NFS: Always wait for I/O completion before unlock Benjamin Coddington
2017-04-21 12:32 ` Jeff Layton
2017-04-01 3:15 [PATCH v4 0/6] Skipped unlocks Benjamin Coddington
2017-04-01 3:16 ` [PATCH 6/6] NFS: Always wait for I/O completion before unlock Benjamin Coddington
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1491567742.2745.10.camel@poochiereds.net \
--to=jlayton@poochiereds.net \
--cc=anna.schumaker@netapp.com \
--cc=bcodding@redhat.com \
--cc=bfields@fieldses.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-nfs@vger.kernel.org \
--cc=miklos@szeredi.hu \
--cc=trond.myklebust@primarydata.com \
--cc=viro@zeniv.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).