From: Dai Ngo <dai.ngo@oracle.com>
To: Chuck Lever <chuck.lever@oracle.com>
Cc: jlayton@kernel.org, linux-nfs@vger.kernel.org
Subject: Re: [PATCH 1/1] NFSD: cancel CB_RECALL_ANY call when nfs4_client is about to be destroyed
Date: Thu, 28 Mar 2024 17:31:02 -0700 [thread overview]
Message-ID: <c97be8b9-c0ba-4f2d-9340-78368008ba4b@oracle.com> (raw)
In-Reply-To: <88fac8af-c194-452b-94eb-7658b9056246@oracle.com>
On 3/28/24 11:14 AM, Dai Ngo wrote:
>
> On 3/28/24 7:08 AM, Chuck Lever wrote:
>> On Wed, Mar 27, 2024 at 06:09:28PM -0700, Dai Ngo wrote:
>>> On 3/26/24 11:27 AM, Chuck Lever wrote:
>>>> On Tue, Mar 26, 2024 at 11:13:29AM -0700, Dai Ngo wrote:
>>>>> Currently when a nfs4_client is destroyed we wait for the
>>>>> cb_recall_any
>>>>> callback to complete before proceed. This adds unnecessary delay
>>>>> to the
>>>>> __destroy_client call if there is problem communicating with the
>>>>> client.
>>>> By "unnecessary delay" do you mean only the seven-second RPC
>>>> retransmit timeout, or is there something else?
>>> when the client network interface is down, the RPC task takes ~9s to
>>> send the callback, waits for the reply and gets ETIMEDOUT. This process
>>> repeats in a loop with the same RPC task before being stopped by
>>> rpc_shutdown_client after client lease expires.
>> I'll have to review this code again, but rpc_shutdown_client
>> should cause these RPCs to terminate immediately and safely. Can't
>> we use that?
>
> rpc_shutdown_client works, it terminated the RPC call to stop the loop.
>
>>
>>
>>> It takes a total of about 1m20s before the CB_RECALL is terminated.
>>> For CB_RECALL_ANY and CB_OFFLOAD, this process gets in to a infinite
>>> loop since there is no delegation conflict and the client is allowed
>>> to stay in courtesy state.
>>>
>>> The loop happens because in nfsd4_cb_sequence_done if cb_seq_status
>>> is 1 (an RPC Reply was never received) it calls nfsd4_mark_cb_fault
>>> to set the NFSD4_CB_FAULT bit. It then sets cb_need_restart to true.
>>> When nfsd4_cb_release is called, it checks cb_need_restart bit and
>>> re-queues the work again.
>> Something in the sequence_done path should check if the server is
>> tearing down this callback connection. If it doesn't, that is a bug
>> IMO.
TCP terminated the connection after retrying for 16 minutes and
notified the RPC layer which deleted the nfsd4_conn.
But when nfsd4_run_cb_work ran again, it got into the infinite
loop caused by:
/*
* XXX: Ideally, we could wait for the client to
* reconnect, but I haven't figured out how
* to do that yet.
*/
nfsd4_queue_cb_delayed(cb, 25);
which was introduced by c1ccfcf1a9bf. Note that I'm using 6.9-rc1.
-Dai
>
> I will check to see if TCP eventually closes the connection and
> notifies the RPC layer. From network traces, I see TCP stopped
> retrying after about 7 minutes. But even 7 minutes it's a long
> time we should not be hanging around waiting for it.
>
>>
>> Btw, have you checked NFSv4.0 behavior?
>
> Not yet.
>
>>
>>
>>>> I can see that a server shutdown might want to cancel these, but why
>>>> is this a problem when destroying an nfs4_client?
>>> Destroying an nfs4_client is called when the export is unmounted.
>> Ah, agreed. Thanks for reminding me.
>>
>>
>>> Cancelling these calls just make the process a bit quicker when there
>>> is problem with the client connection, or preventing the unmount to
>>> hang if there is problem at the workqueue and a callback work is
>>> pending there.
>>>
>>> For CB_RECALL, even if we wait for the call to complete the client
>>> won't be able to return any delegations since the nfs4_client is
>>> already been destroyed. It just serves as a notice to the client that
>>> there is a delegation conflict so it can take appropriate actions.
>>>
>>>>> This patch addresses this issue by cancelling the CB_RECALL_ANY
>>>>> call from
>>>>> the workqueue when the nfs4_client is about to be destroyed.
>>>> Does CB_OFFLOAD need similar treatment?
>>> Probably. The copy is already done anyway, this is just a notification.
>> It would be a nicer design if all outstanding callback RPCs could
>> be handled with one mechanism instead of building a separate
>> shutdown method for each operation type.
>
> cb_recall ties to the individual delegation and cb_recall_any ties
> to the nfs4_client. We can check the delegation and the client to
> see if there are pending callbacks. Currently cb_offload is stand-alone
> and not tied to anything, kzalloc the callback on the fly and send
> it out so there is no way to find out if there is pending callback.
>
> -Dai
>
>>
>>
>>> -Dai
>>>
>>>>
>>>>> Signed-off-by: Dai Ngo <dai.ngo@oracle.com>
>>>>> ---
>>>>> fs/nfsd/nfs4callback.c | 10 ++++++++++
>>>>> fs/nfsd/nfs4state.c | 10 +++++++++-
>>>>> fs/nfsd/state.h | 1 +
>>>>> 3 files changed, 20 insertions(+), 1 deletion(-)
>>>>>
>>>>> diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
>>>>> index 87c9547989f6..e5b50c96be6a 100644
>>>>> --- a/fs/nfsd/nfs4callback.c
>>>>> +++ b/fs/nfsd/nfs4callback.c
>>>>> @@ -1568,3 +1568,13 @@ bool nfsd4_run_cb(struct nfsd4_callback *cb)
>>>>> nfsd41_cb_inflight_end(clp);
>>>>> return queued;
>>>>> }
>>>>> +
>>>>> +void nfsd41_cb_recall_any_cancel(struct nfs4_client *clp)
>>>>> +{
>>>>> + if (test_bit(NFSD4_CLIENT_CB_RECALL_ANY, &clp->cl_flags) &&
>>>>> + cancel_delayed_work(&clp->cl_ra->ra_cb.cb_work)) {
>>>>> + clear_bit(NFSD4_CLIENT_CB_RECALL_ANY, &clp->cl_flags);
>>>>> + atomic_add_unless(&clp->cl_rpc_users, -1, 0);
>>>>> + nfsd41_cb_inflight_end(clp);
>>>>> + }
>>>>> +}
>>>>> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
>>>>> index 1a93c7fcf76c..0e1db57c9a19 100644
>>>>> --- a/fs/nfsd/nfs4state.c
>>>>> +++ b/fs/nfsd/nfs4state.c
>>>>> @@ -2402,6 +2402,7 @@ __destroy_client(struct nfs4_client *clp)
>>>>> }
>>>>> nfsd4_return_all_client_layouts(clp);
>>>>> nfsd4_shutdown_copy(clp);
>>>>> + nfsd41_cb_recall_any_cancel(clp);
>>>>> nfsd4_shutdown_callback(clp);
>>>>> if (clp->cl_cb_conn.cb_xprt)
>>>>> svc_xprt_put(clp->cl_cb_conn.cb_xprt);
>>>>> @@ -2980,6 +2981,12 @@ static void force_expire_client(struct
>>>>> nfs4_client *clp)
>>>>> clp->cl_time = 0;
>>>>> spin_unlock(&nn->client_lock);
>>>>> + /*
>>>>> + * no need to send and wait for CB_RECALL_ANY
>>>>> + * when client is about to be destroyed
>>>>> + */
>>>>> + nfsd41_cb_recall_any_cancel(clp);
>>>>> +
>>>>> wait_event(expiry_wq, atomic_read(&clp->cl_rpc_users) == 0);
>>>>> spin_lock(&nn->client_lock);
>>>>> already_expired = list_empty(&clp->cl_lru);
>>>>> @@ -6617,7 +6624,8 @@ deleg_reaper(struct nfsd_net *nn)
>>>>> clp->cl_ra->ra_bmval[0] = BIT(RCA4_TYPE_MASK_RDATA_DLG) |
>>>>> BIT(RCA4_TYPE_MASK_WDATA_DLG);
>>>>> trace_nfsd_cb_recall_any(clp->cl_ra);
>>>>> - nfsd4_run_cb(&clp->cl_ra->ra_cb);
>>>>> + if (!nfsd4_run_cb(&clp->cl_ra->ra_cb))
>>>>> + clear_bit(NFSD4_CLIENT_CB_RECALL_ANY, &clp->cl_flags);
>>>>> }
>>>>> }
>>>>> diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
>>>>> index 01c6f3445646..259b4af7d226 100644
>>>>> --- a/fs/nfsd/state.h
>>>>> +++ b/fs/nfsd/state.h
>>>>> @@ -735,6 +735,7 @@ extern void nfsd4_change_callback(struct
>>>>> nfs4_client *clp, struct nfs4_cb_conn *
>>>>> extern void nfsd4_init_cb(struct nfsd4_callback *cb, struct
>>>>> nfs4_client *clp,
>>>>> const struct nfsd4_callback_ops *ops, enum nfsd4_cb_op
>>>>> op);
>>>>> extern bool nfsd4_run_cb(struct nfsd4_callback *cb);
>>>>> +extern void nfsd41_cb_recall_any_cancel(struct nfs4_client *clp);
>>>>> extern int nfsd4_create_callback_queue(void);
>>>>> extern void nfsd4_destroy_callback_queue(void);
>>>>> extern void nfsd4_shutdown_callback(struct nfs4_client *);
>>>>> --
>>>>> 2.39.3
>>>>>
>
--
Cell: (949) 378-1341
next prev parent reply other threads:[~2024-03-29 0:31 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-03-26 18:13 [PATCH 1/1] NFSD: cancel CB_RECALL_ANY call when nfs4_client is about to be destroyed Dai Ngo
2024-03-26 18:27 ` Chuck Lever
2024-03-28 1:09 ` Dai Ngo
2024-03-28 14:08 ` Chuck Lever
2024-03-28 18:14 ` Dai Ngo
2024-03-29 0:31 ` Dai Ngo [this message]
2024-03-29 14:55 ` Chuck Lever
2024-03-29 17:57 ` Dai Ngo
2024-03-29 23:42 ` Chuck Lever
2024-03-30 17:46 ` Dai Ngo
2024-03-30 18:28 ` Chuck Lever
2024-03-30 23:30 ` Dai Ngo
2024-04-01 12:49 ` Jeff Layton
2024-04-01 13:34 ` Chuck Lever
2024-04-01 16:00 ` Dai Ngo
2024-04-01 16:46 ` Dai Ngo
2024-04-01 17:49 ` Chuck Lever
2024-04-01 19:55 ` Dai Ngo
2024-04-01 20:17 ` Dai Ngo
2024-04-02 13:58 ` Chuck Lever
2024-04-02 14:29 ` Dai Ngo
2024-04-01 16:11 ` Jeff Layton
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=c97be8b9-c0ba-4f2d-9340-78368008ba4b@oracle.com \
--to=dai.ngo@oracle.com \
--cc=chuck.lever@oracle.com \
--cc=jlayton@kernel.org \
--cc=linux-nfs@vger.kernel.org \
/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 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.