From: Bruce Fields <bfields@fieldses.org>
To: dai.ngo@oracle.com
Cc: Chuck Lever III <chuck.lever@oracle.com>,
Jeff Layton <jlayton@redhat.com>,
Al Viro <viro@zeniv.linux.org.uk>,
Linux NFS Mailing List <linux-nfs@vger.kernel.org>,
"linux-fsdevel@vger.kernel.org" <linux-fsdevel@vger.kernel.org>
Subject: Re: [PATCH RFC v19 06/11] NFSD: Update find_clp_in_name_tree() to handle courtesy client
Date: Sun, 17 Apr 2022 15:07:27 -0400 [thread overview]
Message-ID: <20220417190727.GA18120@fieldses.org> (raw)
In-Reply-To: <20220413125550.GA29176@fieldses.org>
On Wed, Apr 13, 2022 at 08:55:50AM -0400, Bruce Fields wrote:
> On Fri, Apr 01, 2022 at 12:11:34PM -0700, dai.ngo@oracle.com wrote:
> > On 4/1/22 8:57 AM, Chuck Lever III wrote:
> > >>(And to be honest I'd still prefer the original approach where we expire
> > >>clients from the posix locking code and then retry. It handles an
> > >>additional case (the one where reboot happens after a long network
> > >>partition), and I don't think it requires adding these new client
> > >>states....)
> > >The locking of the earlier approach was unworkable.
> > >
> > >But, I'm happy to consider that again if you can come up with a way
> > >of handling it properly and simply.
> >
> > I will wait for feedback from Bruce before sending v20 with the
> > above change.
>
> OK, I'd like to tweak the design in that direction.
>
> I'd like to handle the case where the network goes down for a while, and
> the server gets power-cycled before the network comes back up. I think
> that could easily happen. There's no reason clients couldn't reclaim
> all their state in that case. We should let them.
>
> To handle that case, we have to delay removing the client's stable
> storage record until there's a lock conflict. That means code that
> checks for conflicts must be able to sleep.
>
> In each case (opens, locks, delegations), conflicts are first detected
> while holding a spinlock. So we need to unlock before waiting, and then
> retry if necessary.
>
> We decided instead to remove the stable-storage record when first
> converting a client to a courtesy client--then we can handle a conflict
> by just setting a flag on the client that indicates it should no longer
> be used, no need to drop any locks.
>
> That leaves the client in a state where it's still on a bunch of global
> data structures, but has to be treated as if it no longer exists. That
> turns out to require more special handling than expected. You've shown
> admirable persistance in handling those cases, but I'm still not
> completely convinced this is correct.
>
> We could avoid that complication, and also solve the
> server-reboot-during-network-partition problem, if we went back to the
> first plan and allowed ourselves to sleep at the time we detect a
> conflict. I don't think it's that complicated.
>
> We end up using a lot of the same logic regardless, so don't throw away
> the existing patches.
>
> My basic plan is:
>
> Keep the client state, but with only three values: ACTIVE, COURTESY, and
> EXPIRABLE.
>
> ACTIVE is the initial state, which we return to whenever we renew. The
> laundromat sets COURTESY whenever a client isn't renewed for a lease
> period. When we run into a conflict with a lock held by a client, we
> call
>
> static bool try_to_expire_client(struct nfs4_client *clp)
> {
> return COURTESY == cmpxchg(clp->cl_state, COURTESY, EXPIRABLE);
> }
>
> If it returns true, that tells us the client was a courtesy client. We
> then call queue_work(laundry_wq, &nn->laundromat_work) to tell the
> laundromat to actually expire the client. Then if needed we can drop
> locks, wait for the laundromat to do the work with
> flush_workqueue(laundry_wq), and retry.
>
> All the EXPIRABLE state does is tell the laundromat to expire this
> client. It does *not* prevent the client from being renewed and
> acquiring new locks--if that happens before the laundromat gets to the
> client, that's fine, we let it return to ACTIVE state and if someone
> retries the conflicing lock they'll just get a denial.
>
> Here's a suggested a rough patch ordering. If you want to go above and
> beyond, I also suggest some tests that should pass after each step:
>
>
> PATCH 1
> -------
>
> Implement courtesy behavior *only* for clients that have
> delegations, but no actual opens or locks:
>
> Define new cl_state field with values ACTIVE, COURTESY, and EXPIRABLE.
> Set to ACTIVE on renewal. Modify the laundromat so that instead of
> expiring any client that's too old, it first checks if a client has
> state consisting only of unconflicted delegations, and, if so, it sets
> COURTESY.
>
> Define try_to_expire_client as above. In nfsd_break_deleg_cb, call
> try_to_expire_client and queue_work. (But also continue scheduling the
> recall as we do in the current code, there's no harm to that.)
>
> Modify the laundromat to try to expire old clients with EXPIRED set.
>
> TESTS:
> - Establish a client, open a file, get a delegation, close the
> file, wait 2 lease periods, verify that you can still use the
> delegation.
> - Establish a client, open a file, get a delegation, close the
> file, wait 2 lease periods, establish a second client, request
> a conflicting open, verify that the open succeeds and that the
> first client is no longer able to use its delegation.
>
>
> PATCH 2
> -------
>
> Extend courtesy client behavior to clients that have opens or
> delegations, but no locks:
>
> Modify the laundromat to set COURTESY on old clients with state
> consisting only of opens or unconflicted delegations.
>
> Add in nfs4_resolve_deny_conflicts_locked and friends as in your patch
> "Update nfs4_get_vfs_file()...", but in the case of a conflict, call
> try_to_expire_client and queue_work(), then modify e.g.
> nfs4_get_vfs_file to flush_workqueue() and then retry after unlocking
> fi_lock.
>
> TESTS:
> - establish a client, open a file, wait 2 lease periods, verify
> that you can still use the open stateid.
> - establish a client, open a file, wait 2 lease periods,
> establish a second client, request an open with a share mode
> conflicting with the first open, verify that the open succeeds
> and that first client is no longer able to use its open.
>
> PATCH 3
> -------
>
> Minor tweak to prevent the laundromat from being freed out from
> under a thread processing a conflicting lock:
>
> Create and destroy the laundromat workqueue in init_nfsd/exit_nfsd
> instead of where it's done currently.
>
> (That makes the laundromat's lifetime longer than strictly necessary.
> We could do better with a little more work; I think this is OK for now.)
>
> TESTS:
> - just rerun any regression tests; this patch shouldn't change
> behavior.
>
> PATCH 4
> -------
>
> Extend courtesy client behavior to any client with state, including
> locks:
>
> Modify the laundromat to set COURTESY on any old client with state.
>
> Add two new lock manager callbacks:
>
> void * (*lm_lock_expirable)(struct file_lock *);
> bool (*lm_expire_lock)(void *);
>
> If lm_lock_expirable() is called and returns non-NULL, posix_lock_inode
> should drop flc_lock, call lm_expire_lock() with the value returned from
> lm_lock_expirable, and then restart the loop over flc_posix from the
> beginning.
>
> For now, nfsd's lm_lock_expirable will basically just be
>
> if (try_to_expire_client()) {
> queue_work()
> return get_net();
Correction: I forgot that the laundromat is global, not per-net. So, we
can skip the put_net/get_net. Also, lm_lock_expirable can just return
bool instead of void *, and lm_expire_lock needs no arguments.
--b.
> }
> return NULL;
>
> and lm_expire_lock will:
>
> flush_workqueue()
> put_net()
>
> One more subtlety: the moment we drop the flc_lock, it's possible
> another task could race in and free it. Worse, the nfsd module could be
> removed entirely--so nfsd's lm_expire_lock code could disappear out from
> under us. To prevent this, I think we need to add a struct module
> *owner field to struct lock_manager_operations, and use it like:
>
> owner = fl->fl_lmops->owner;
> __get_module(owner);
> expire_lock = fl->fl_lmops->lm_expire_lock;
> spin_unlock(&ctx->flc_lock);
> expire_lock(...);
> module_put(owner);
>
> Maybe there's some simpler way, but I don't see it.
>
> TESTS:
> - retest courtesy client behavior using file locks this time.
>
> --
>
> That's the basic idea. I think it should work--though I may have
> overlooked something.
>
> This has us flush the laundromat workqueue while holding mutexes in a
> couple cases. We could avoid that with a little more work, I think.
> But those mutexes should only be associated with the client requesting a
> new open/lock, and such a client shouldn't be touched by the laundromat,
> so I think we're OK.
>
> It'd also be helpful to update the info file with courtesy client
> information, as you do in your current patches.
>
> Does this make sense?
>
> --b.
next prev parent reply other threads:[~2022-04-17 19:07 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-03-31 16:01 [PATCH RFC v19 0/11] NFSD: Initial implementation of NFSv4 Courteous Server Dai Ngo
2022-03-31 16:01 ` [PATCH RFC v19 01/11] fs/lock: add helper locks_owner_has_blockers to check for blockers Dai Ngo
2022-03-31 16:17 ` Chuck Lever III
2022-03-31 16:29 ` dai.ngo
2022-03-31 16:02 ` [PATCH RFC v19 02/11] NFSD: Add courtesy client state, macro and spinlock to support courteous server Dai Ngo
2022-03-31 16:02 ` [PATCH RFC v19 03/11] NFSD: Add lm_lock_expired call out Dai Ngo
2022-03-31 16:02 ` [PATCH RFC v19 04/11] NFSD: Update nfsd_breaker_owns_lease() to handle courtesy clients Dai Ngo
2022-03-31 16:02 ` [PATCH RFC v19 05/11] NFSD: Update nfs4_get_vfs_file() to handle courtesy client Dai Ngo
2022-03-31 16:02 ` [PATCH RFC v19 06/11] NFSD: Update find_clp_in_name_tree() " Dai Ngo
2022-04-01 15:21 ` J. Bruce Fields
2022-04-01 15:57 ` Chuck Lever III
2022-04-01 19:11 ` dai.ngo
2022-04-13 12:55 ` Bruce Fields
2022-04-13 18:28 ` dai.ngo
2022-04-13 18:42 ` Bruce Fields
2022-04-17 19:07 ` Bruce Fields [this message]
2022-04-18 1:18 ` dai.ngo
2022-03-31 16:02 ` [PATCH RFC v19 07/11] NFSD: Update find_in_sessionid_hashtbl() " Dai Ngo
2022-03-31 16:02 ` [PATCH RFC v19 08/11] NFSD: Update find_client_in_id_table() " Dai Ngo
2022-03-31 16:02 ` [PATCH RFC v19 09/11] NFSD: Refactor nfsd4_laundromat() Dai Ngo
2022-03-31 16:02 ` [PATCH RFC v19 10/11] NFSD: Update laundromat to handle courtesy clients Dai Ngo
2022-03-31 16:02 ` [PATCH RFC v19 11/11] NFSD: Show state of courtesy clients in client info Dai Ngo
2022-04-02 10:35 ` [PATCH RFC v19 0/11] NFSD: Initial implementation of NFSv4 Courteous Server Jeff Layton
2022-04-02 15:10 ` Chuck Lever III
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=20220417190727.GA18120@fieldses.org \
--to=bfields@fieldses.org \
--cc=chuck.lever@oracle.com \
--cc=dai.ngo@oracle.com \
--cc=jlayton@redhat.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-nfs@vger.kernel.org \
--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).