Skip to content

Commit 66af257

Browse files
daimngochucklever
authored andcommitted
NFSD: add courteous server support for thread with only delegation
This patch provides courteous server support for delegation only. Only expired client with delegation but no conflict and no open or lock state is allowed to be in COURTESY state. Delegation conflict with COURTESY/EXPIRABLE client is resolved by setting it to EXPIRABLE, queue work for the laundromat and return delay to the caller. Conflict is resolved when the laudromat runs and expires the EXIRABLE client while the NFS client retries the OPEN request. Local thread request that gets conflict is doing the retry in _break_lease. Client in COURTESY or EXPIRABLE state is allowed to reconnect and continues to have access to its state. Access to the nfs4_client by the reconnecting thread and the laundromat is serialized via the client_lock. Reviewed-by: J. Bruce Fields <bfields@fieldses.org> Signed-off-by: Dai Ngo <dai.ngo@oracle.com> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
1 parent 983084b commit 66af257

File tree

3 files changed

+99
-15
lines changed

3 files changed

+99
-15
lines changed

fs/nfsd/nfs4state.c

Lines changed: 67 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ static void free_session(struct nfsd4_session *);
125125
static const struct nfsd4_callback_ops nfsd4_cb_recall_ops;
126126
static const struct nfsd4_callback_ops nfsd4_cb_notify_lock_ops;
127127

128+
static struct workqueue_struct *laundry_wq;
129+
128130
static bool is_session_dead(struct nfsd4_session *ses)
129131
{
130132
return ses->se_flags & NFS4_SESSION_DEAD;
@@ -152,6 +154,7 @@ static __be32 get_client_locked(struct nfs4_client *clp)
152154
if (is_client_expired(clp))
153155
return nfserr_expired;
154156
atomic_inc(&clp->cl_rpc_users);
157+
clp->cl_state = NFSD4_ACTIVE;
155158
return nfs_ok;
156159
}
157160

@@ -172,6 +175,7 @@ renew_client_locked(struct nfs4_client *clp)
172175

173176
list_move_tail(&clp->cl_lru, &nn->client_lru);
174177
clp->cl_time = ktime_get_boottime_seconds();
178+
clp->cl_state = NFSD4_ACTIVE;
175179
}
176180

177181
static void put_client_renew_locked(struct nfs4_client *clp)
@@ -1090,6 +1094,7 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_file *fp,
10901094
get_clnt_odstate(odstate);
10911095
dp->dl_type = NFS4_OPEN_DELEGATE_READ;
10921096
dp->dl_retries = 1;
1097+
dp->dl_recalled = false;
10931098
nfsd4_init_cb(&dp->dl_recall, dp->dl_stid.sc_client,
10941099
&nfsd4_cb_recall_ops, NFSPROC4_CLNT_CB_RECALL);
10951100
get_nfs4_file(fp);
@@ -2004,6 +2009,8 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name)
20042009
idr_init(&clp->cl_stateids);
20052010
atomic_set(&clp->cl_rpc_users, 0);
20062011
clp->cl_cb_state = NFSD4_CB_UNKNOWN;
2012+
clp->cl_state = NFSD4_ACTIVE;
2013+
atomic_set(&clp->cl_delegs_in_recall, 0);
20072014
INIT_LIST_HEAD(&clp->cl_idhash);
20082015
INIT_LIST_HEAD(&clp->cl_openowners);
20092016
INIT_LIST_HEAD(&clp->cl_delegations);
@@ -4694,9 +4701,18 @@ nfsd_break_deleg_cb(struct file_lock *fl)
46944701
bool ret = false;
46954702
struct nfs4_delegation *dp = (struct nfs4_delegation *)fl->fl_owner;
46964703
struct nfs4_file *fp = dp->dl_stid.sc_file;
4704+
struct nfs4_client *clp = dp->dl_stid.sc_client;
4705+
struct nfsd_net *nn;
46974706

46984707
trace_nfsd_cb_recall(&dp->dl_stid);
46994708

4709+
dp->dl_recalled = true;
4710+
atomic_inc(&clp->cl_delegs_in_recall);
4711+
if (try_to_expire_client(clp)) {
4712+
nn = net_generic(clp->net, nfsd_net_id);
4713+
mod_delayed_work(laundry_wq, &nn->laundromat_work, 0);
4714+
}
4715+
47004716
/*
47014717
* We don't want the locks code to timeout the lease for us;
47024718
* we'll remove it ourself if a delegation isn't returned
@@ -4739,9 +4755,14 @@ static int
47394755
nfsd_change_deleg_cb(struct file_lock *onlist, int arg,
47404756
struct list_head *dispose)
47414757
{
4742-
if (arg & F_UNLCK)
4758+
struct nfs4_delegation *dp = (struct nfs4_delegation *)onlist->fl_owner;
4759+
struct nfs4_client *clp = dp->dl_stid.sc_client;
4760+
4761+
if (arg & F_UNLCK) {
4762+
if (dp->dl_recalled)
4763+
atomic_dec(&clp->cl_delegs_in_recall);
47434764
return lease_modify(onlist, arg, dispose);
4744-
else
4765+
} else
47454766
return -EAGAIN;
47464767
}
47474768

@@ -5605,6 +5626,49 @@ static void nfsd4_ssc_expire_umount(struct nfsd_net *nn)
56055626
}
56065627
#endif
56075628

5629+
/*
5630+
* place holder for now, no check for lock blockers yet
5631+
*/
5632+
static bool
5633+
nfs4_anylock_blockers(struct nfs4_client *clp)
5634+
{
5635+
if (atomic_read(&clp->cl_delegs_in_recall) ||
5636+
client_has_openowners(clp) ||
5637+
!list_empty(&clp->async_copies))
5638+
return true;
5639+
return false;
5640+
}
5641+
5642+
static void
5643+
nfs4_get_client_reaplist(struct nfsd_net *nn, struct list_head *reaplist,
5644+
struct laundry_time *lt)
5645+
{
5646+
struct list_head *pos, *next;
5647+
struct nfs4_client *clp;
5648+
5649+
INIT_LIST_HEAD(reaplist);
5650+
spin_lock(&nn->client_lock);
5651+
list_for_each_safe(pos, next, &nn->client_lru) {
5652+
clp = list_entry(pos, struct nfs4_client, cl_lru);
5653+
if (clp->cl_state == NFSD4_EXPIRABLE)
5654+
goto exp_client;
5655+
if (!state_expired(lt, clp->cl_time))
5656+
break;
5657+
if (!atomic_read(&clp->cl_rpc_users))
5658+
clp->cl_state = NFSD4_COURTESY;
5659+
if (!client_has_state(clp) ||
5660+
ktime_get_boottime_seconds() >=
5661+
(clp->cl_time + NFSD_COURTESY_CLIENT_TIMEOUT))
5662+
goto exp_client;
5663+
if (nfs4_anylock_blockers(clp)) {
5664+
exp_client:
5665+
if (!mark_client_expired_locked(clp))
5666+
list_add(&clp->cl_lru, reaplist);
5667+
}
5668+
}
5669+
spin_unlock(&nn->client_lock);
5670+
}
5671+
56085672
static time64_t
56095673
nfs4_laundromat(struct nfsd_net *nn)
56105674
{
@@ -5627,7 +5691,6 @@ nfs4_laundromat(struct nfsd_net *nn)
56275691
goto out;
56285692
}
56295693
nfsd4_end_grace(nn);
5630-
INIT_LIST_HEAD(&reaplist);
56315694

56325695
spin_lock(&nn->s2s_cp_lock);
56335696
idr_for_each_entry(&nn->s2s_cp_stateids, cps_t, i) {
@@ -5637,17 +5700,7 @@ nfs4_laundromat(struct nfsd_net *nn)
56375700
_free_cpntf_state_locked(nn, cps);
56385701
}
56395702
spin_unlock(&nn->s2s_cp_lock);
5640-
5641-
spin_lock(&nn->client_lock);
5642-
list_for_each_safe(pos, next, &nn->client_lru) {
5643-
clp = list_entry(pos, struct nfs4_client, cl_lru);
5644-
if (!state_expired(&lt, clp->cl_time))
5645-
break;
5646-
if (mark_client_expired_locked(clp))
5647-
continue;
5648-
list_add(&clp->cl_lru, &reaplist);
5649-
}
5650-
spin_unlock(&nn->client_lock);
5703+
nfs4_get_client_reaplist(nn, &reaplist, &lt);
56515704
list_for_each_safe(pos, next, &reaplist) {
56525705
clp = list_entry(pos, struct nfs4_client, cl_lru);
56535706
trace_nfsd_clid_purged(&clp->cl_clientid);
@@ -5722,7 +5775,6 @@ nfs4_laundromat(struct nfsd_net *nn)
57225775
return max_t(time64_t, lt.new_timeo, NFSD_LAUNDROMAT_MINTIMEOUT);
57235776
}
57245777

5725-
static struct workqueue_struct *laundry_wq;
57265778
static void laundromat_main(struct work_struct *);
57275779

57285780
static void

fs/nfsd/nfsd.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,7 @@ void nfsd_lockd_shutdown(void);
336336
#define COMPOUND_ERR_SLACK_SPACE 16 /* OP_SETATTR */
337337

338338
#define NFSD_LAUNDROMAT_MINTIMEOUT 1 /* seconds */
339+
#define NFSD_COURTESY_CLIENT_TIMEOUT (24 * 60 * 60) /* seconds */
339340

340341
/*
341342
* The following attributes are currently not supported by the NFSv4 server:

fs/nfsd/state.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ struct nfs4_delegation {
149149
/* For recall: */
150150
int dl_retries;
151151
struct nfsd4_callback dl_recall;
152+
bool dl_recalled;
152153
};
153154

154155
#define cb_to_delegation(cb) \
@@ -282,6 +283,28 @@ struct nfsd4_sessionid {
282283

283284
#define HEXDIR_LEN 33 /* hex version of 16 byte md5 of cl_name plus '\0' */
284285

286+
/*
287+
* State Meaning Where set
288+
* --------------------------------------------------------------------------
289+
* | NFSD4_ACTIVE | Confirmed, active | Default |
290+
* |------------------- ----------------------------------------------------|
291+
* | NFSD4_COURTESY | Courtesy state. | nfs4_get_client_reaplist |
292+
* | | Lease/lock/share | |
293+
* | | reservation conflict | |
294+
* | | can cause Courtesy | |
295+
* | | client to be expired | |
296+
* |------------------------------------------------------------------------|
297+
* | NFSD4_EXPIRABLE | Courtesy client to be| nfs4_laundromat |
298+
* | | expired by Laundromat| try_to_expire_client |
299+
* | | due to conflict | |
300+
* |------------------------------------------------------------------------|
301+
*/
302+
enum {
303+
NFSD4_ACTIVE = 0,
304+
NFSD4_COURTESY,
305+
NFSD4_EXPIRABLE,
306+
};
307+
285308
/*
286309
* struct nfs4_client - one per client. Clientids live here.
287310
*
@@ -385,6 +408,9 @@ struct nfs4_client {
385408
struct list_head async_copies; /* list of async copies */
386409
spinlock_t async_lock; /* lock for async copies */
387410
atomic_t cl_cb_inflight; /* Outstanding callbacks */
411+
412+
unsigned int cl_state;
413+
atomic_t cl_delegs_in_recall;
388414
};
389415

390416
/* struct nfs4_client_reset
@@ -702,4 +728,9 @@ extern void nfsd4_client_record_remove(struct nfs4_client *clp);
702728
extern int nfsd4_client_record_check(struct nfs4_client *clp);
703729
extern void nfsd4_record_grace_done(struct nfsd_net *nn);
704730

731+
static inline bool try_to_expire_client(struct nfs4_client *clp)
732+
{
733+
cmpxchg(&clp->cl_state, NFSD4_COURTESY, NFSD4_EXPIRABLE);
734+
return clp->cl_state == NFSD4_EXPIRABLE;
735+
}
705736
#endif /* NFSD4_STATE_H */

0 commit comments

Comments
 (0)