2014-04-18 18:45:06

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 00/70] NFSd lock scalability patches

Hi Bruce,

In accordance with the traditional Good Friday theme of "suffering", here
is the first draft of the NFSd lock scalability patches for your perusal.
I've only done light testing so far, but since there is a total of 70
patches to review, I figured you might want to start sooner rather than
waiting for the QA folks to finish testing.

During the process, I did notice a bug or two in the existing NFSd code,
and so the first 3 patches are bugfixes that you may want to apply
irrespectively.

In general, what I've tried to do is add reference counting wherever
there is a need to carry structures around outside locks, and to
ensure that we make structures invisible to lookups before we attempt
to free them.

There are still a few optimisations that might be worth considering in
order to reduce the frequency of global locks that are taken. Suggestions
include:
1) Caching the nfs4_client in the struct nfsd4_compound_state
2) Caching the current/save_stateid as references to the actual nfs4_stid in the struct nfsd4_compound_state
3) Converting the stateid/sessionid/clientid global table lookups to use RCU.
4) Converting the file_hashtbl lookups to use RCU

Cheers
Trond

Benny Halevy (4):
nfsd4: rename recall_lock to state_lock
nfsd4: use cl_lock to synchronize all stateid idr calls
nfsd4: hash deleg stateid only on successful nfs4_set_delegation
nfsd4: use state_lock for delegation hashing

Trond Myklebust (66):
NFSd: Ensure we clear the cstate->slot in nfsd4_proc_compound
NFSd: Move default initialisers from create_client() to alloc_client()
NFSd: call rpc_destroy_wait_queue() from free_client()
NFSd: Remove 'inline' designation for free_client()
NFSd: Mark nfs4_free_lockowner and nfs4_free_openowner as static
functions
NFSd: Avoid taking state_lock while holding inode lock in
nfsd_break_one_deleg
NFSd: Ensure delegation setup is safe w.r.t. break_lease()
NFSd: Add fine grained protection for the nfs4_file->fi_stateids list
NFSd: Clean up nfs4_preprocess_stateid_op
NFSd: Add a mutex to protect the NFSv4.0 open owner replay cache
NFSd: Add locking to the nfs4_file->fi_fds[] array
NFSd: Protect the nfs4_file delegation fields using the fi_lock
NFSd: Lock owners are not per open stateid
NFSd: Clean up helper __release_lock_stateid
NFSd: Allow lockowners to hold several stateids
NFSd: NFSv4 lock-owners are not associated to a specific file
NFSd: Get rid of the lockowner_ino_hashtbl
NFSd: Cleanup nfs4svc_encode_compoundres
NFSd: Don't get a session reference without a client reference
NFSd: Move the delegation reference counter into the struct nfs4_stid
NFSd: Simplify stateid management
NFSd: Fix delegation revocation
NFSd: Don't let the laundromat reap clients that are referenced
NFSd: Add reference counting to the lock and open stateids
NFSd: Add a struct nfs4_file field to struct nfs4_stid
NFSd: Replace delegation->dl_file with the dl_stid.sc_file
NFSd: Replace nfs4_ol_stateid->st_file with the st_stid.sc_file
NFSd: Ensure stateids remain unique until they are freed
NFSd: Ensure atomicity of stateid destruction and idr tree removal
NFSd: Fix atomicity of delegation counter
NFSd: Slight cleanup of find_stateid()
NFSd: Add reference counting to find_stateid
NFSd: nfs4_preprocess_seqid_op should only set *stpp on success
NFSd: Add reference counting to lock stateids
NFSd: nfsd4_locku() must reference the lock stateid
NFSd: Ensure that nfs4_open_delegation() references the delegation
stateid
NFSd: nfsd4_process_open2() must reference the delegation stateid
NFSd: nfsd4_process_open2() must reference the open stateid
NFSd: Prepare nfsd4_close() for open stateid referencing
NFSd: nfsd4_open_confirm() must reference the open stateid
NFSd: Add reference counting to nfs4_preprocess_confirmed_seqid_op
NFSd: Migrate the stateid reference into nfs4_preprocess_seqid_op
NFSd: Migrate the stateid reference into nfs4_lookup_stateid()
NFSd: Migrate the stateid reference into nfs4_find_stateid_by_type()
NFSd: Cleanup - Let nfsd4_lookup_stateid() take a cstate argument
NFSd: Use the session->se_client in lookup_clientid()
NFSd: Convert nfsd4_process_open1() to work with lookup_clientid()
NFSd: Convert nfs4_check_open_reclaim() to work with lookup_clientid()
NFSd: Ensure struct nfs4_client is unhashed before we try to destroy
it
NFSd: Ensure that the laundromat unhashes the client before releasing
locks
NFSd: Don't require client_lock in free_client
NFSd: Move create_client() call outside the lock
NFSd: Protect unconfirmed client creation using client_lock
NFSd: Protect session creation and client confirm using client_lock
NFSd: Protect nfsd4_destroy_clientid using client_lock
NFSd: Ensure lookup_clientid() takes client_lock
NFSd: Remove nfs4_lock_state(): nfs4_preprocess_stateid_op()
NFSd: Remove nfs4_lock_state(): nfsd4_test_stateid/nfsd4_free_stateid
NFSd: Remove nfs4_lock_state(): nfsd4_release_lockowner
NFSd: Remove nfs4_lock_state(): nfsd4_lock/locku/lockt()
NFSd: Remove nfs4_lock_state(): nfsd4_open_downgrade + nfsd4_close
NFSd: Remove nfs4_lock_state(): nfsd4_delegreturn()
NFSd: Remove nfs4_lock_state(): nfsd4_open and nfsd4_open_confirm
NFSd: Remove nfs4_lock_state(): exchange_id, create/destroy_session()
NFSd: Remove nfs4_lock_state(): setclientid, setclientid_confirm,
renew
NFSd: Remove nfs4_lock_state(): reclaim_complete()

fs/nfsd/netns.h | 4 -
fs/nfsd/nfs4callback.c | 18 +-
fs/nfsd/nfs4proc.c | 28 +-
fs/nfsd/nfs4state.c | 1239 ++++++++++++++++++++++++++++++------------------
fs/nfsd/nfs4xdr.c | 15 +-
fs/nfsd/state.h | 46 +-
fs/nfsd/xdr4.h | 21 +-
7 files changed, 819 insertions(+), 552 deletions(-)

--
1.9.0



2014-04-18 18:46:54

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 67/70] NFSd: Remove nfs4_lock_state(): nfsd4_open and nfsd4_open_confirm

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4proc.c | 3 ---
fs/nfsd/nfs4state.c | 6 ------
2 files changed, 9 deletions(-)

diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 1101bf6b63c8..47aa08ed92aa 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -385,8 +385,6 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (nfsd4_has_session(cstate))
copy_clientid(&open->op_clientid, cstate->session);

- nfs4_lock_state();
-
/* check seqid for replay. set nfs4_owner */
resp = rqstp->rq_resp;
status = nfsd4_process_open1(&resp->cstate, open, nn);
@@ -474,7 +472,6 @@ out:
nfsd4_cstate_assign_replay(cstate,
&open->op_openowner->oo_owner);
nfsd4_bump_seqid(cstate, status);
- nfs4_unlock_state();
return status;
}

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 5f0facdde981..188eb4cbea95 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -3494,9 +3494,6 @@ static void nfsd4_deleg_xgrade_none_ext(struct nfsd4_open *open,
*/
}

-/*
- * called with nfs4_lock_state() held.
- */
__be32
nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open)
{
@@ -4208,8 +4205,6 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (status)
return status;

- nfs4_lock_state();
-
status = nfs4_preprocess_seqid_op(cstate,
oc->oc_seqid, &oc->oc_req_stateid,
NFS4_OPEN_STID, &stp, nn);
@@ -4231,7 +4226,6 @@ put_stateid:
put_generic_stateid(stp);
out:
nfsd4_bump_seqid(cstate, status);
- nfs4_unlock_state();
return status;
}

--
1.9.0


2014-04-18 18:46:51

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 65/70] NFSd: Remove nfs4_lock_state(): nfsd4_open_downgrade + nfsd4_close

---
fs/nfsd/nfs4state.c | 4 ----
1 file changed, 4 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 1e0f5301e2b3..681b25deac02 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4288,7 +4288,6 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp,
dprintk("NFSD: %s: od_deleg_want=0x%x ignored\n", __func__,
od->od_deleg_want);

- nfs4_lock_state();
status = nfs4_preprocess_confirmed_seqid_op(cstate, od->od_seqid,
&od->od_stateid, &stp, nn);
if (status)
@@ -4315,7 +4314,6 @@ put_stateid:
put_generic_stateid(stp);
out:
nfsd4_bump_seqid(cstate, status);
- nfs4_unlock_state();
return status;
}

@@ -4341,7 +4339,6 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
dprintk("NFSD: nfsd4_close on file %pd\n",
cstate->current_fh.fh_dentry);

- nfs4_lock_state();
status = nfs4_preprocess_seqid_op(cstate, close->cl_seqid,
&close->cl_stateid,
NFS4_OPEN_STID|NFS4_CLOSED_STID,
@@ -4375,7 +4372,6 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
}
}
out:
- nfs4_unlock_state();
return status;
}

--
1.9.0


2014-04-18 18:45:30

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 16/70] NFSd: Protect the nfs4_file delegation fields using the fi_lock

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 33 ++++++++++++++++++++++++++-------
1 file changed, 26 insertions(+), 7 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 73e7da308d37..5105dbc76888 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -479,6 +479,7 @@ nfs4_put_delegation(struct nfs4_delegation *dp)
}
}

+/* Call under fp->fi_lock */
static void nfs4_put_deleg_lease(struct nfs4_file *fp)
{
if (atomic_dec_and_test(&fp->fi_delegees)) {
@@ -511,6 +512,7 @@ unhash_delegation(struct nfs4_delegation *dp)
struct nfs4_file *fp = dp->dl_file;

spin_lock(&state_lock);
+ spin_lock(&fp->fi_lock);
list_del_init(&dp->dl_perclnt);
list_del_init(&dp->dl_recall_lru);
if (!list_empty(&dp->dl_perfile)) {
@@ -518,10 +520,11 @@ unhash_delegation(struct nfs4_delegation *dp)
list_del_init(&dp->dl_perfile);
spin_unlock(&fp->fi_inode->i_lock);
}
- spin_unlock(&state_lock);
nfs4_put_deleg_lease(fp);
- put_nfs4_file(fp);
dp->dl_file = NULL;
+ spin_unlock(&fp->fi_lock);
+ spin_unlock(&state_lock);
+ put_nfs4_file(fp);
}


@@ -3153,23 +3156,33 @@ static int nfs4_setlease(struct nfs4_delegation *dp)
{
struct nfs4_file *fp = dp->dl_file;
struct file_lock *fl;
- int status;
+ int status = 0;

fl = nfs4_alloc_init_lease(dp, NFS4_OPEN_DELEGATE_READ);
if (!fl)
return -ENOMEM;
fl->fl_file = find_readable_file(fp);
+ spin_lock(&state_lock);
+ spin_lock(&fp->fi_lock);
+ /* Race breaker */
+ if (fp->fi_lease) {
+ atomic_inc(&fp->fi_delegees);
+ hash_delegation_locked(dp, fp);
+ goto out_free;
+ }
status = vfs_setlease(fl->fl_file, fl->fl_type, &fl);
if (status)
goto out_free;
fp->fi_lease = fl;
fp->fi_deleg_file = fl->fl_file;
atomic_set(&fp->fi_delegees, 1);
- spin_lock(&state_lock);
hash_delegation_locked(dp, fp);
+ spin_unlock(&fp->fi_lock);
spin_unlock(&state_lock);
return 0;
out_free:
+ spin_unlock(&fp->fi_lock);
+ spin_unlock(&state_lock);
if (fl->fl_file)
fput(fl->fl_file);
locks_free_lock(fl);
@@ -3183,23 +3196,29 @@ static int nfs4_set_delegation(struct nfs4_delegation *dp, struct nfs4_file *fp)
if (fp->fi_had_conflict)
return -EAGAIN;
get_nfs4_file(fp);
+ spin_lock(&state_lock);
+ spin_lock(&fp->fi_lock);
dp->dl_file = fp;
if (!fp->fi_lease) {
+ spin_unlock(&fp->fi_lock);
+ spin_unlock(&state_lock);
status = nfs4_setlease(dp);
if (status)
goto out_free;
return 0;
}
- spin_lock(&state_lock);
if (fp->fi_had_conflict) {
- spin_unlock(&state_lock);
status = -EAGAIN;
- goto out_free;
+ goto unlock_and_free;
}
atomic_inc(&fp->fi_delegees);
hash_delegation_locked(dp, fp);
+ spin_unlock(&fp->fi_lock);
spin_unlock(&state_lock);
return 0;
+unlock_and_free:
+ spin_unlock(&fp->fi_lock);
+ spin_unlock(&state_lock);
out_free:
put_nfs4_file(fp);
dp->dl_file = fp;
--
1.9.0


2014-04-18 18:46:12

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 43/70] NFSd: Prepare nfsd4_close() for open stateid referencing

Prepare nfsd4_close for a future where nfs4_preprocess_seqid_op()
hands it a fully referenced open stateid.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 5 +++++
1 file changed, 5 insertions(+)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index ec7cc90de03a..78dfc3ce98a1 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4274,6 +4274,8 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
nfsd4_bump_seqid(cstate, status);
if (status)
goto out;
+ /* FIXME: move into nfs4_preprocess_seqid_op */
+ atomic_inc(&stp->st_stid.sc_count);
oo = openowner(stp->st_stateowner);
update_stateid(&stp->st_stid.sc_stateid);
memcpy(&close->cl_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));
@@ -4285,6 +4287,9 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
else
oo->oo_last_closed_stid = stp;

+ /* put reference from nfs4_preprocess_seqid_op */
+ put_generic_stateid(stp);
+
if (list_empty(&oo->oo_owner.so_stateids)) {
if (cstate->minorversion)
release_openowner(oo);
--
1.9.0


2014-04-18 18:46:59

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 70/70] NFSd: Remove nfs4_lock_state(): reclaim_complete()

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 2 --
1 file changed, 2 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 36ccc79702dd..a71dbf2a7a61 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -2568,7 +2568,6 @@ nfsd4_reclaim_complete(struct svc_rqst *rqstp, struct nfsd4_compound_state *csta
return nfs_ok;
}

- nfs4_lock_state();
status = nfserr_complete_already;
if (test_and_set_bit(NFSD4_CLIENT_RECLAIM_COMPLETE,
&cstate->session->se_client->cl_flags))
@@ -2588,7 +2587,6 @@ nfsd4_reclaim_complete(struct svc_rqst *rqstp, struct nfsd4_compound_state *csta
status = nfs_ok;
nfsd4_client_record_create(cstate->session->se_client);
out:
- nfs4_unlock_state();
return status;
}

--
1.9.0


2014-04-18 18:46:26

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 52/70] NFSd: Convert nfs4_check_open_reclaim() to work with lookup_clientid()

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4proc.c | 2 +-
fs/nfsd/nfs4state.c | 7 ++++---
fs/nfsd/state.h | 2 +-
3 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index a23da87b384e..1101bf6b63c8 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -432,7 +432,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
case NFS4_OPEN_CLAIM_PREVIOUS:
open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED;
status = nfs4_check_open_reclaim(&open->op_clientid,
- cstate->minorversion,
+ cstate->session,
nn);
if (status)
goto out;
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index d316fe10b9aa..1708f6b7e9e7 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -5068,13 +5068,14 @@ nfsd4_find_reclaim_client(const char *recdir, struct nfsd_net *nn)
* Called from OPEN. Look for clientid in reclaim list.
*/
__be32
-nfs4_check_open_reclaim(clientid_t *clid, bool sessions, struct nfsd_net *nn)
+nfs4_check_open_reclaim(clientid_t *clid, struct nfsd4_session *session, struct nfsd_net *nn)
{
struct nfs4_client *clp;
+ __be32 status;

/* find clientid in conf_id_hashtbl */
- clp = find_confirmed_client(clid, sessions, nn);
- if (clp == NULL)
+ status = lookup_clientid(clid, session, nn, &clp);
+ if (status)
return nfserr_reclaim_bad;

return nfsd4_client_record_check(clp) ? nfserr_reclaim_bad : nfs_ok;
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index a4ac75a0de21..b6187676d991 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -435,7 +435,7 @@ void nfs4_remove_reclaim_record(struct nfs4_client_reclaim *, struct nfsd_net *)
extern void nfs4_release_reclaim(struct nfsd_net *);
extern struct nfs4_client_reclaim *nfsd4_find_reclaim_client(const char *recdir,
struct nfsd_net *nn);
-extern __be32 nfs4_check_open_reclaim(clientid_t *clid, bool sessions, struct nfsd_net *nn);
+extern __be32 nfs4_check_open_reclaim(clientid_t *clid, struct nfsd4_session *session, struct nfsd_net *nn);
extern int set_callback_cred(void);
extern void nfsd4_init_callback(struct nfsd4_callback *);
extern void nfsd4_probe_callback(struct nfs4_client *clp);
--
1.9.0


2014-04-21 15:01:14

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH 00/70] NFSd lock scalability patches

I think the series introduces some find vs alloc races where a function
first tries to find an existing structure in a lookup structure, then
allocates a new one, but doesn't check for another allocation that could
have happened in the meantime.

- lookup_or_create_lock_state for lock stateids
- I can't find any protection for the creation/addition of files
and open stateids in the whole open machinery
(nfsd4_open, nfsd4_process_open1 and nfsd4_process_open2), but
I also generally have trouble following that code.
- similarly I can't find much of a protection against this sort of
race for the various state owners.

2014-04-19 14:50:27

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH 36/70] NFSd: Add reference counting to find_stateid

> +static void nfs4_put_stateid(struct nfs4_stid *s)
> +{
> + if (s == NULL)
> + return;
> + switch (s->sc_type) {
> + case NFS4_OPEN_STID:
> + case NFS4_LOCK_STID:
> + case NFS4_CLOSED_STID:
> + put_generic_stateid(openlockstateid(s));
> + break;
> + case NFS4_DELEG_STID:
> + case NFS4_REVOKED_DELEG_STID:
> + case NFS4_CLOSED_DELEG_STID:
> + nfs4_put_delegation(delegstateid(s));
> + }
> +}

I really don't like the way the inheritance for the stateids works,
a pure put operation shouldn't need this. I think all this can be
fixed by adding a ->free function pointer to struct nfs4_stid. At this
point the braindamage of passing a kmem_cache pointer to various
function can be removed (similar to how nfs4_alloc_stid should be
replaced with a nfs4_init_stid that takes an already allocated stid),
and nothing in the normal refcounting path should need these switches.

> @@ -3804,26 +3823,33 @@ static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid)
> return nfserr_bad_stateid;
> status = check_stateid_generation(stateid, &s->sc_stateid, 1);
> if (status)
> - return status;
> + goto out_put_stid;
> switch (s->sc_type) {
> case NFS4_DELEG_STID:
> - return nfs_ok;
> + status = nfs_ok;
> + break;
> case NFS4_REVOKED_DELEG_STID:
> - return nfserr_deleg_revoked;
> + status = nfserr_deleg_revoked;
> + break;
> case NFS4_OPEN_STID:
> case NFS4_LOCK_STID:
> ols = openlockstateid(s);
> if (ols->st_stateowner->so_is_open_owner
> && !(openowner(ols->st_stateowner)->oo_flags
> & NFS4_OO_CONFIRMED))
> - return nfserr_bad_stateid;
> - return nfs_ok;
> + status = nfserr_bad_stateid;
> + else
> + status = nfs_ok;
> + break;

Not quite as urgent as for the refcounting, but I think moving more
of these switches on the type into proper methods would improve the
stateid code a lot.


2014-04-18 18:45:14

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 06/70] nfsd4: use cl_lock to synchronize all stateid idr calls

From: Benny Halevy <[email protected]>

Signed-off-by: Benny Halevy <[email protected]>
---
fs/nfsd/nfs4state.c | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 34273ca482c3..626f310a74a8 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -334,7 +334,11 @@ kmem_cache *slab)
if (!stid)
return NULL;

- new_id = idr_alloc_cyclic(stateids, stid, 0, 0, GFP_KERNEL);
+ idr_preload(GFP_KERNEL);
+ spin_lock(&cl->cl_lock);
+ new_id = idr_alloc_cyclic(stateids, stid, 0, 0, GFP_NOWAIT);
+ spin_unlock(&cl->cl_lock);
+ idr_preload_end();
if (new_id < 0)
goto out_free;
stid->sc_client = cl;
@@ -397,9 +401,12 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv

static void remove_stid(struct nfs4_stid *s)
{
- struct idr *stateids = &s->sc_client->cl_stateids;
+ struct nfs4_client *clp = s->sc_client;
+ struct idr *stateids = &clp->cl_stateids;

+ spin_lock(&clp->cl_lock);
idr_remove(stateids, s->sc_stateid.si_opaque.so_id);
+ spin_unlock(&clp->cl_lock);
}

static void nfs4_free_stid(struct kmem_cache *slab, struct nfs4_stid *s)
@@ -1110,7 +1117,9 @@ free_client(struct nfs4_client *clp)
rpc_destroy_wait_queue(&clp->cl_cb_waitq);
free_svc_cred(&clp->cl_cred);
kfree(clp->cl_name.data);
+ spin_lock(&clp->cl_lock);
idr_destroy(&clp->cl_stateids);
+ spin_unlock(&clp->cl_lock);
kfree(clp);
}

@@ -1329,7 +1338,9 @@ static struct nfs4_stid *find_stateid(struct nfs4_client *cl, stateid_t *t)
{
struct nfs4_stid *ret;

+ spin_lock(&cl->cl_lock);
ret = idr_find(&cl->cl_stateids, t->si_opaque.so_id);
+ spin_unlock(&cl->cl_lock);
if (!ret || !ret->sc_type)
return NULL;
return ret;
--
1.9.0


2014-04-18 18:45:12

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 04/70] NFSd: Remove 'inline' designation for free_client()

It is large, it is used in more than one place, and it is not performance
critical. Let gcc figure out whether it should be inlined...

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 32b699bebb9c..841495aa9170 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1093,7 +1093,7 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name)
return clp;
}

-static inline void
+static void
free_client(struct nfs4_client *clp)
{
struct nfsd_net __maybe_unused *nn = net_generic(clp->net, nfsd_net_id);
--
1.9.0


2014-04-18 18:45:39

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 22/70] NFSd: Cleanup nfs4svc_encode_compoundres

Move the slot return, put session etc into a helper in fs/nfsd/nfs4state.c
instead of open coding in nfs4svc_encode_compoundres.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 35 ++++++++++++++++++++++-------------
fs/nfsd/nfs4xdr.c | 15 +--------------
fs/nfsd/state.h | 1 -
fs/nfsd/xdr4.h | 2 +-
4 files changed, 24 insertions(+), 29 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 38ca008c3d41..9421e56454a2 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -202,18 +202,6 @@ static void put_client_renew_locked(struct nfs4_client *clp)
renew_client_locked(clp);
}

-void put_client_renew(struct nfs4_client *clp)
-{
- struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
-
- if (!atomic_dec_and_lock(&clp->cl_refcount, &nn->client_lock))
- return;
- if (!is_client_expired(clp))
- renew_client_locked(clp);
- spin_unlock(&nn->client_lock);
-}
-
-
static inline u32
opaque_hashval(const void *ptr, int nbytes)
{
@@ -1657,7 +1645,7 @@ out_err:
/*
* Cache a reply. nfsd4_check_resp_size() has bounded the cache size.
*/
-void
+static void
nfsd4_store_cache_entry(struct nfsd4_compoundres *resp)
{
struct nfsd4_slot *slot = resp->cstate.slot;
@@ -2414,6 +2402,27 @@ out_put_client:
goto out_no_session;
}

+void
+nfsd4_sequence_done(struct nfsd4_compoundres *resp)
+{
+ struct nfsd4_compound_state *cs = &resp->cstate;
+
+ if (nfsd4_has_session(cs)) {
+ struct nfs4_client *clp = cs->session->se_client;
+ struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
+
+ if (cs->status != nfserr_replay_cache) {
+ nfsd4_store_cache_entry(resp);
+ cs->slot->sl_flags &= ~NFSD4_SLOT_INUSE;
+ }
+ /* Renew the clientid on success and on replay */
+ spin_lock(&nn->client_lock);
+ nfsd4_put_session(cs->session);
+ put_client_renew_locked(clp);
+ spin_unlock(&nn->client_lock);
+ }
+}
+
__be32
nfsd4_destroy_clientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_destroy_clientid *dc)
{
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 2723c1badd01..8f6e7a4dc8b0 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -3727,7 +3727,6 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compo
/*
* All that remains is to write the tag and operation count...
*/
- struct nfsd4_compound_state *cs = &resp->cstate;
struct kvec *iov;
p = resp->tagp;
*p++ = htonl(resp->taglen);
@@ -3741,19 +3740,7 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compo
iov = &rqstp->rq_res.head[0];
iov->iov_len = ((char*)resp->p) - (char*)iov->iov_base;
BUG_ON(iov->iov_len > PAGE_SIZE);
- if (nfsd4_has_session(cs)) {
- struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
- struct nfs4_client *clp = cs->session->se_client;
- if (cs->status != nfserr_replay_cache) {
- nfsd4_store_cache_entry(resp);
- cs->slot->sl_flags &= ~NFSD4_SLOT_INUSE;
- }
- /* Renew the clientid on success and on replay */
- spin_lock(&nn->client_lock);
- nfsd4_put_session(cs->session);
- spin_unlock(&nn->client_lock);
- put_client_renew(clp);
- }
+ nfsd4_sequence_done(resp);
return 1;
}

diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 02fc6685ab3f..e5444e0aedad 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -452,7 +452,6 @@ extern void nfs4_put_delegation(struct nfs4_delegation *dp);
extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(const char *name,
struct nfsd_net *nn);
extern bool nfs4_has_reclaimed_state(const char *name, struct nfsd_net *nn);
-extern void put_client_renew(struct nfs4_client *clp);

/* nfs4recover operations */
extern int nfsd4_client_tracking_init(struct net *net);
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index 2f887c944048..a617f1a3ba83 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -592,7 +592,6 @@ extern __be32 nfsd4_setclientid(struct svc_rqst *rqstp,
extern __be32 nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
struct nfsd4_compound_state *,
struct nfsd4_setclientid_confirm *setclientid_confirm);
-extern void nfsd4_store_cache_entry(struct nfsd4_compoundres *resp);
extern __be32 nfsd4_exchange_id(struct svc_rqst *rqstp,
struct nfsd4_compound_state *, struct nfsd4_exchange_id *);
extern __be32 nfsd4_backchannel_ctl(struct svc_rqst *, struct nfsd4_compound_state *, struct nfsd4_backchannel_ctl *);
@@ -603,6 +602,7 @@ extern __be32 nfsd4_create_session(struct svc_rqst *,
extern __be32 nfsd4_sequence(struct svc_rqst *,
struct nfsd4_compound_state *,
struct nfsd4_sequence *);
+extern void nfsd4_sequence_done(struct nfsd4_compoundres *resp);
extern __be32 nfsd4_destroy_session(struct svc_rqst *,
struct nfsd4_compound_state *,
struct nfsd4_destroy_session *);
--
1.9.0


2014-04-18 18:46:45

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 61/70] NFSd: Remove nfs4_lock_state(): nfs4_preprocess_stateid_op()

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index ad79dca31cc4..67daa70dd9a2 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -3990,13 +3990,11 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,
if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
return check_special_stateids(net, current_fh, stateid, flags);

- nfs4_lock_state();
-
status = nfsd4_lookup_stateid(cstate, stateid,
NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID,
&s, nn);
if (status)
- goto unlock_state;
+ return status;
status = check_stateid_generation(stateid, &s->sc_stateid, nfsd4_has_session(cstate));
if (status)
goto out;
@@ -4044,8 +4042,6 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,
*filpp = file;
out:
nfs4_put_stateid(s);
-unlock_state:
- nfs4_unlock_state();
return status;
}

--
1.9.0


2014-04-18 18:45:27

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 13/70] NFSd: Clean up nfs4_preprocess_stateid_op

Move the state locking and file descriptor reference out from the
callers and into nfs4_preprocess_stateid_op() itself.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4proc.c | 11 -----------
fs/nfsd/nfs4state.c | 19 +++++++++++++------
2 files changed, 13 insertions(+), 17 deletions(-)

diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 8eabbfb25441..480323443efe 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -786,7 +786,6 @@ nfsd4_read(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (!nfsd4_last_compound_op(rqstp))
rqstp->rq_splice_ok = false;

- nfs4_lock_state();
/* check stateid */
if ((status = nfs4_preprocess_stateid_op(SVC_NET(rqstp),
cstate, &read->rd_stateid,
@@ -794,11 +793,8 @@ nfsd4_read(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
dprintk("NFSD: nfsd4_read: couldn't process stateid!\n");
goto out;
}
- if (read->rd_filp)
- get_file(read->rd_filp);
status = nfs_ok;
out:
- nfs4_unlock_state();
read->rd_rqstp = rqstp;
read->rd_fhp = &cstate->current_fh;
return status;
@@ -937,10 +933,8 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
int err;

if (setattr->sa_iattr.ia_valid & ATTR_SIZE) {
- nfs4_lock_state();
status = nfs4_preprocess_stateid_op(SVC_NET(rqstp), cstate,
&setattr->sa_stateid, WR_STATE, NULL);
- nfs4_unlock_state();
if (status) {
dprintk("NFSD: nfsd4_setattr: couldn't process stateid!\n");
return status;
@@ -1006,17 +1000,12 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (write->wr_offset >= OFFSET_MAX)
return nfserr_inval;

- nfs4_lock_state();
status = nfs4_preprocess_stateid_op(SVC_NET(rqstp),
cstate, stateid, WR_STATE, &filp);
if (status) {
- nfs4_unlock_state();
dprintk("NFSD: nfsd4_write: couldn't process stateid!\n");
return status;
}
- if (filp)
- get_file(filp);
- nfs4_unlock_state();

cnt = write->wr_buflen;
write->wr_how_written = write->wr_stable_how;
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index f77cc9206373..6827e8698767 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -3699,6 +3699,7 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,
struct svc_fh *current_fh = &cstate->current_fh;
struct inode *ino = current_fh->fh_dentry->d_inode;
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+ struct file *file = NULL;
__be32 status;

if (filpp)
@@ -3710,10 +3711,12 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,
if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
return check_special_stateids(net, current_fh, stateid, flags);

+ nfs4_lock_state();
+
status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID,
&s, cstate->minorversion, nn);
if (status)
- return status;
+ goto out;
status = check_stateid_generation(stateid, &s->sc_stateid, nfsd4_has_session(cstate));
if (status)
goto out;
@@ -3724,8 +3727,8 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,
if (status)
goto out;
if (filpp) {
- *filpp = dp->dl_file->fi_deleg_file;
- if (!*filpp) {
+ file = dp->dl_file->fi_deleg_file;
+ if (!file) {
WARN_ON_ONCE(1);
status = nfserr_serverfault;
goto out;
@@ -3746,16 +3749,20 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,
goto out;
if (filpp) {
if (flags & RD_STATE)
- *filpp = find_readable_file(stp->st_file);
+ file = find_readable_file(stp->st_file);
else
- *filpp = find_writeable_file(stp->st_file);
+ file = find_writeable_file(stp->st_file);
}
break;
default:
- return nfserr_bad_stateid;
+ status = nfserr_bad_stateid;
+ goto out;
}
status = nfs_ok;
+ if (file)
+ *filpp = get_file(file);
out:
+ nfs4_unlock_state();
return status;
}

--
1.9.0


2014-04-19 14:40:31

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH 30/70] NFSd: Replace delegation->dl_file with the dl_stid.sc_file

Instead of adding a mostly unused file field to the stid in the previous
patch and starting to use it here I'd recommend merging the two patches.


2014-04-19 20:58:16

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH 00/70] NFSd lock scalability patches

On Fri, Apr 18, 2014 at 02:43:54PM -0400, Trond Myklebust wrote:
> In accordance with the traditional Good Friday theme of "suffering", here
> is the first draft of the NFSd lock scalability patches for your perusal.
> I've only done light testing so far, but since there is a total of 70
> patches to review, I figured you might want to start sooner rather than
> waiting for the QA folks to finish testing.

Thanks!

Yeah, this sounds like too much suffering for vacation time, but I'll at
least make a stab at merging the initial bug fixes next week, then try
to plow through these (or any revised version (thanks, Christoph, for
help reviewing!)) after I get back in a couple more weeks.

--b.

>
> During the process, I did notice a bug or two in the existing NFSd code,
> and so the first 3 patches are bugfixes that you may want to apply
> irrespectively.
>
> In general, what I've tried to do is add reference counting wherever
> there is a need to carry structures around outside locks, and to
> ensure that we make structures invisible to lookups before we attempt
> to free them.
>
> There are still a few optimisations that might be worth considering in
> order to reduce the frequency of global locks that are taken. Suggestions
> include:
> 1) Caching the nfs4_client in the struct nfsd4_compound_state
> 2) Caching the current/save_stateid as references to the actual nfs4_stid in the struct nfsd4_compound_state
> 3) Converting the stateid/sessionid/clientid global table lookups to use RCU.
> 4) Converting the file_hashtbl lookups to use RCU
>
> Cheers
> Trond
>
> Benny Halevy (4):
> nfsd4: rename recall_lock to state_lock
> nfsd4: use cl_lock to synchronize all stateid idr calls
> nfsd4: hash deleg stateid only on successful nfs4_set_delegation
> nfsd4: use state_lock for delegation hashing
>
> Trond Myklebust (66):
> NFSd: Ensure we clear the cstate->slot in nfsd4_proc_compound
> NFSd: Move default initialisers from create_client() to alloc_client()
> NFSd: call rpc_destroy_wait_queue() from free_client()
> NFSd: Remove 'inline' designation for free_client()
> NFSd: Mark nfs4_free_lockowner and nfs4_free_openowner as static
> functions
> NFSd: Avoid taking state_lock while holding inode lock in
> nfsd_break_one_deleg
> NFSd: Ensure delegation setup is safe w.r.t. break_lease()
> NFSd: Add fine grained protection for the nfs4_file->fi_stateids list
> NFSd: Clean up nfs4_preprocess_stateid_op
> NFSd: Add a mutex to protect the NFSv4.0 open owner replay cache
> NFSd: Add locking to the nfs4_file->fi_fds[] array
> NFSd: Protect the nfs4_file delegation fields using the fi_lock
> NFSd: Lock owners are not per open stateid
> NFSd: Clean up helper __release_lock_stateid
> NFSd: Allow lockowners to hold several stateids
> NFSd: NFSv4 lock-owners are not associated to a specific file
> NFSd: Get rid of the lockowner_ino_hashtbl
> NFSd: Cleanup nfs4svc_encode_compoundres
> NFSd: Don't get a session reference without a client reference
> NFSd: Move the delegation reference counter into the struct nfs4_stid
> NFSd: Simplify stateid management
> NFSd: Fix delegation revocation
> NFSd: Don't let the laundromat reap clients that are referenced
> NFSd: Add reference counting to the lock and open stateids
> NFSd: Add a struct nfs4_file field to struct nfs4_stid
> NFSd: Replace delegation->dl_file with the dl_stid.sc_file
> NFSd: Replace nfs4_ol_stateid->st_file with the st_stid.sc_file
> NFSd: Ensure stateids remain unique until they are freed
> NFSd: Ensure atomicity of stateid destruction and idr tree removal
> NFSd: Fix atomicity of delegation counter
> NFSd: Slight cleanup of find_stateid()
> NFSd: Add reference counting to find_stateid
> NFSd: nfs4_preprocess_seqid_op should only set *stpp on success
> NFSd: Add reference counting to lock stateids
> NFSd: nfsd4_locku() must reference the lock stateid
> NFSd: Ensure that nfs4_open_delegation() references the delegation
> stateid
> NFSd: nfsd4_process_open2() must reference the delegation stateid
> NFSd: nfsd4_process_open2() must reference the open stateid
> NFSd: Prepare nfsd4_close() for open stateid referencing
> NFSd: nfsd4_open_confirm() must reference the open stateid
> NFSd: Add reference counting to nfs4_preprocess_confirmed_seqid_op
> NFSd: Migrate the stateid reference into nfs4_preprocess_seqid_op
> NFSd: Migrate the stateid reference into nfs4_lookup_stateid()
> NFSd: Migrate the stateid reference into nfs4_find_stateid_by_type()
> NFSd: Cleanup - Let nfsd4_lookup_stateid() take a cstate argument
> NFSd: Use the session->se_client in lookup_clientid()
> NFSd: Convert nfsd4_process_open1() to work with lookup_clientid()
> NFSd: Convert nfs4_check_open_reclaim() to work with lookup_clientid()
> NFSd: Ensure struct nfs4_client is unhashed before we try to destroy
> it
> NFSd: Ensure that the laundromat unhashes the client before releasing
> locks
> NFSd: Don't require client_lock in free_client
> NFSd: Move create_client() call outside the lock
> NFSd: Protect unconfirmed client creation using client_lock
> NFSd: Protect session creation and client confirm using client_lock
> NFSd: Protect nfsd4_destroy_clientid using client_lock
> NFSd: Ensure lookup_clientid() takes client_lock
> NFSd: Remove nfs4_lock_state(): nfs4_preprocess_stateid_op()
> NFSd: Remove nfs4_lock_state(): nfsd4_test_stateid/nfsd4_free_stateid
> NFSd: Remove nfs4_lock_state(): nfsd4_release_lockowner
> NFSd: Remove nfs4_lock_state(): nfsd4_lock/locku/lockt()
> NFSd: Remove nfs4_lock_state(): nfsd4_open_downgrade + nfsd4_close
> NFSd: Remove nfs4_lock_state(): nfsd4_delegreturn()
> NFSd: Remove nfs4_lock_state(): nfsd4_open and nfsd4_open_confirm
> NFSd: Remove nfs4_lock_state(): exchange_id, create/destroy_session()
> NFSd: Remove nfs4_lock_state(): setclientid, setclientid_confirm,
> renew
> NFSd: Remove nfs4_lock_state(): reclaim_complete()
>
> fs/nfsd/netns.h | 4 -
> fs/nfsd/nfs4callback.c | 18 +-
> fs/nfsd/nfs4proc.c | 28 +-
> fs/nfsd/nfs4state.c | 1239 ++++++++++++++++++++++++++++++------------------
> fs/nfsd/nfs4xdr.c | 15 +-
> fs/nfsd/state.h | 46 +-
> fs/nfsd/xdr4.h | 21 +-
> 7 files changed, 819 insertions(+), 552 deletions(-)
>
> --
> 1.9.0
>

2014-04-18 18:46:28

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 53/70] NFSd: Ensure struct nfs4_client is unhashed before we try to destroy it

When we remove the nfs4_lock_state() protection, we will need to ensure
that it can't be found by other threads while we're destroying it.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 43 +++++++++++++++++++++++++++++++++----------
1 file changed, 33 insertions(+), 10 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 1708f6b7e9e7..19f7739a156e 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1273,12 +1273,23 @@ free_client(struct nfs4_client *clp)
}

/* must be called under the client_lock */
-static inline void
+static void
unhash_client_locked(struct nfs4_client *clp)
{
+ struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
struct nfsd4_session *ses;

- list_del(&clp->cl_lru);
+ /* Mark the client as expired! */
+ clp->cl_time = 0;
+ /* Make it invisible */
+ if (!list_empty(&clp->cl_idhash)) {
+ list_del_init(&clp->cl_idhash);
+ if (test_bit(NFSD4_CLIENT_CONFIRMED, &clp->cl_flags))
+ rb_erase(&clp->cl_namenode, &nn->conf_name_tree);
+ else
+ rb_erase(&clp->cl_namenode, &nn->unconf_name_tree);
+ }
+ list_del_init(&clp->cl_lru);
spin_lock(&clp->cl_lock);
list_for_each_entry(ses, &clp->cl_sessions, se_perclnt)
list_del_init(&ses->se_hash);
@@ -1286,7 +1297,17 @@ unhash_client_locked(struct nfs4_client *clp)
}

static void
-destroy_client(struct nfs4_client *clp)
+unhash_client(struct nfs4_client *clp)
+{
+ struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
+
+ spin_lock(&nn->client_lock);
+ unhash_client_locked(clp);
+ spin_unlock(&nn->client_lock);
+}
+
+static void
+__destroy_client(struct nfs4_client *clp)
{
struct nfs4_openowner *oo;
struct nfs4_delegation *dp;
@@ -1318,22 +1339,24 @@ destroy_client(struct nfs4_client *clp)
nfsd4_shutdown_callback(clp);
if (clp->cl_cb_conn.cb_xprt)
svc_xprt_put(clp->cl_cb_conn.cb_xprt);
- list_del(&clp->cl_idhash);
- if (test_bit(NFSD4_CLIENT_CONFIRMED, &clp->cl_flags))
- rb_erase(&clp->cl_namenode, &nn->conf_name_tree);
- else
- rb_erase(&clp->cl_namenode, &nn->unconf_name_tree);
spin_lock(&nn->client_lock);
- unhash_client_locked(clp);
WARN_ON_ONCE(atomic_read(&clp->cl_refcount));
free_client(clp);
spin_unlock(&nn->client_lock);
}

+static void
+destroy_client(struct nfs4_client *clp)
+{
+ unhash_client(clp);
+ __destroy_client(clp);
+}
+
static void expire_client(struct nfs4_client *clp)
{
+ unhash_client(clp);
nfsd4_client_record_remove(clp);
- destroy_client(clp);
+ __destroy_client(clp);
}

static void copy_verf(struct nfs4_client *target, nfs4_verifier *source)
--
1.9.0


2014-04-19 14:56:41

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH 50/70] NFSd: Use the session->se_client in lookup_clientid()

> -static __be32 lookup_clientid(clientid_t *clid, bool session, struct nfsd_net *nn, struct nfs4_client **clp)
> +static __be32 lookup_clientid(clientid_t *clid, struct nfsd4_session *session, struct nfsd_net *nn, struct nfs4_client **clp)

Please break lines after 80 characters.


2014-04-18 18:46:13

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 44/70] NFSd: nfsd4_open_confirm() must reference the open stateid

Ensure that nfsd4_open_confirm() keeps a reference to the open
stateid until it is done working with it.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 78dfc3ce98a1..517e08818df6 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4144,10 +4144,12 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
NFS4_OPEN_STID, &stp, nn);
if (status)
goto out;
+ /* FIXME: move into nfs4_preprocess_seqid_op */
+ atomic_inc(&stp->st_stid.sc_count);
oo = openowner(stp->st_stateowner);
status = nfserr_bad_stateid;
if (oo->oo_flags & NFS4_OO_CONFIRMED)
- goto out;
+ goto put_stateid;
oo->oo_flags |= NFS4_OO_CONFIRMED;
update_stateid(&stp->st_stid.sc_stateid);
memcpy(&oc->oc_resp_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));
@@ -4156,6 +4158,8 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,

nfsd4_client_record_create(oo->oo_owner.so_client);
status = nfs_ok;
+put_stateid:
+ put_generic_stateid(stp);
out:
nfsd4_bump_seqid(cstate, status);
nfs4_unlock_state();
--
1.9.0


2014-04-18 18:45:36

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 20/70] NFSd: NFSv4 lock-owners are not associated to a specific file

Just like open-owners, lock-owners are associated with a name, a clientid
and, in the case of minor version 0, a sequence id. There is no association
to a file.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 38 +++++++++++++-------------------------
1 file changed, 13 insertions(+), 25 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index dad2f7b511b8..be48dbc32f97 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4249,8 +4249,7 @@ last_byte_offset(u64 start, u64 len)

static unsigned int lockowner_ino_hashval(struct inode *inode, u32 cl_id, struct xdr_netobj *ownername)
{
- return (file_hashval(inode) + cl_id
- + opaque_hashval(ownername->data, ownername->len))
+ return (cl_id + opaque_hashval(ownername->data, ownername->len))
& LOCKOWNER_INO_HASH_MASK;
}

@@ -4306,27 +4305,19 @@ nevermind:
deny->ld_type = NFS4_WRITE_LT;
}

-static bool same_lockowner_ino(struct nfs4_lockowner *lo, struct inode *inode, clientid_t *clid, struct xdr_netobj *owner)
-{
- struct nfs4_ol_stateid *lst;
-
- if (!same_owner_str(&lo->lo_owner, owner, clid))
- return false;
- lst = list_first_entry(&lo->lo_owner.so_stateids,
- struct nfs4_ol_stateid, st_perstateowner);
- return lst->st_file->fi_inode == inode;
-}
-
static struct nfs4_lockowner *
-find_lockowner_str(struct inode *inode, clientid_t *clid,
- struct xdr_netobj *owner, struct nfsd_net *nn)
+find_lockowner_str(clientid_t *clid, struct xdr_netobj *owner,
+ struct nfsd_net *nn)
{
- unsigned int hashval = lockowner_ino_hashval(inode, clid->cl_id, owner);
- struct nfs4_lockowner *lo;
+ unsigned int strhashval = ownerstr_hashval(clid->cl_id, owner);
+ struct nfs4_stateowner *so;

- list_for_each_entry(lo, &nn->lockowner_ino_hashtbl[hashval], lo_owner_ino_hash) {
- if (same_lockowner_ino(lo, inode, clid, owner))
- return lo;
+ list_for_each_entry(so, &nn->ownerstr_hashtbl[strhashval], so_strhash) {
+ if (so->so_is_open_owner)
+ continue;
+ if (!same_owner_str(so, owner, clid))
+ continue;
+ return lockowner(so);
}
return NULL;
}
@@ -4430,8 +4421,7 @@ static __be32 lookup_or_create_lock_state(struct nfsd4_compound_state *cstate, s
unsigned int strhashval;
struct nfsd_net *nn = net_generic(cl->net, nfsd_net_id);

- lo = find_lockowner_str(fi->fi_inode, &cl->cl_clientid,
- &lock->v.new.owner, nn);
+ lo = find_lockowner_str(&cl->cl_clientid, &lock->v.new.owner, nn);
if (!lo) {
strhashval = ownerstr_hashval(cl->cl_clientid.cl_id,
&lock->v.new.owner);
@@ -4642,7 +4632,6 @@ __be32
nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfsd4_lockt *lockt)
{
- struct inode *inode;
struct file_lock *file_lock = NULL;
struct nfs4_lockowner *lo;
__be32 status;
@@ -4665,7 +4654,6 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0)))
goto out;

- inode = cstate->current_fh.fh_dentry->d_inode;
file_lock = locks_alloc_lock();
if (!file_lock) {
dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
@@ -4688,7 +4676,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
goto out;
}

- lo = find_lockowner_str(inode, &lockt->lt_clientid, &lockt->lt_owner, nn);
+ lo = find_lockowner_str(&lockt->lt_clientid, &lockt->lt_owner, nn);
if (lo)
file_lock->fl_owner = (fl_owner_t)lo;
file_lock->fl_pid = current->tgid;
--
1.9.0


2014-04-18 18:45:24

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 12/70] NFSd: Add fine grained protection for the nfs4_file->fi_stateids list

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 21 ++++++++++++++++++---
fs/nfsd/state.h | 1 +
2 files changed, 19 insertions(+), 3 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 3656980b5d19..f77cc9206373 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -623,7 +623,11 @@ release_all_access(struct nfs4_ol_stateid *stp)

static void unhash_generic_stateid(struct nfs4_ol_stateid *stp)
{
+ struct nfs4_file *fp = stp->st_file;
+
+ spin_lock(&fp->fi_lock);
list_del(&stp->st_perfile);
+ spin_unlock(&fp->fi_lock);
list_del(&stp->st_perstateowner);
}

@@ -2530,6 +2534,7 @@ static void nfsd4_init_file(struct nfs4_file *fp, struct inode *ino)
unsigned int hashval = file_hashval(ino);

atomic_set(&fp->fi_ref, 1);
+ spin_lock_init(&fp->fi_lock);
INIT_LIST_HEAD(&fp->fi_stateids);
INIT_LIST_HEAD(&fp->fi_delegations);
fp->fi_inode = igrab(ino);
@@ -2650,7 +2655,6 @@ static void init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp,
stp->st_stid.sc_type = NFS4_OPEN_STID;
INIT_LIST_HEAD(&stp->st_lockowners);
list_add(&stp->st_perstateowner, &oo->oo_owner.so_stateids);
- list_add(&stp->st_perfile, &fp->fi_stateids);
stp->st_stateowner = &oo->oo_owner;
get_nfs4_file(fp);
stp->st_file = fp;
@@ -2659,6 +2663,9 @@ static void init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp,
set_access(open->op_share_access, stp);
set_deny(open->op_share_deny, stp);
stp->st_openstp = NULL;
+ spin_lock(&fp->fi_lock);
+ list_add(&stp->st_perfile, &fp->fi_stateids);
+ spin_unlock(&fp->fi_lock);
}

static void
@@ -2740,6 +2747,7 @@ nfs4_share_conflict(struct svc_fh *current_fh, unsigned int deny_type)
return nfs_ok;
ret = nfserr_locked;
/* Search for conflicting share reservations */
+ spin_lock(&fp->fi_lock);
list_for_each_entry(stp, &fp->fi_stateids, st_perfile) {
if (test_deny(deny_type, stp) ||
test_deny(NFS4_SHARE_DENY_BOTH, stp))
@@ -2747,6 +2755,7 @@ nfs4_share_conflict(struct svc_fh *current_fh, unsigned int deny_type)
}
ret = nfs_ok;
out:
+ spin_unlock(&fp->fi_lock);
put_nfs4_file(fp);
return ret;
}
@@ -2943,6 +2952,7 @@ nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_ol_st
struct nfs4_ol_stateid *local;
struct nfs4_openowner *oo = open->op_openowner;

+ spin_lock(&fp->fi_lock);
list_for_each_entry(local, &fp->fi_stateids, st_perfile) {
/* ignore lock owners */
if (local->st_stateowner->so_is_open_owner == 0)
@@ -2951,9 +2961,12 @@ nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_ol_st
if (local->st_stateowner == &oo->oo_owner)
*stpp = local;
/* check for conflicting share reservations */
- if (!test_share(local, open))
+ if (!test_share(local, open)) {
+ spin_unlock(&fp->fi_lock);
return nfserr_share_denied;
+ }
}
+ spin_unlock(&fp->fi_lock);
return nfs_ok;
}

@@ -4256,7 +4269,6 @@ alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, struct
if (stp == NULL)
return NULL;
stp->st_stid.sc_type = NFS4_LOCK_STID;
- list_add(&stp->st_perfile, &fp->fi_stateids);
list_add(&stp->st_perstateowner, &lo->lo_owner.so_stateids);
stp->st_stateowner = &lo->lo_owner;
get_nfs4_file(fp);
@@ -4264,6 +4276,9 @@ alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, struct
stp->st_access_bmap = 0;
stp->st_deny_bmap = open_stp->st_deny_bmap;
stp->st_openstp = open_stp;
+ spin_lock(&fp->fi_lock);
+ list_add(&stp->st_perfile, &fp->fi_stateids);
+ spin_unlock(&fp->fi_lock);
return stp;
}

diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 02c5a203c738..83d9721d9644 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -383,6 +383,7 @@ static inline struct nfs4_lockowner * lockowner(struct nfs4_stateowner *so)
/* nfs4_file: a file opened by some number of (open) nfs4_stateowners. */
struct nfs4_file {
atomic_t fi_ref;
+ spinlock_t fi_lock;
struct hlist_node fi_hash; /* hash by "struct inode *" */
struct list_head fi_stateids;
struct list_head fi_delegations;
--
1.9.0


2014-04-18 18:46:50

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 64/70] NFSd: Remove nfs4_lock_state(): nfsd4_lock/locku/lockt()

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 9 ---------
1 file changed, 9 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 67895511cc05..1e0f5301e2b3 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4656,8 +4656,6 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
return status;
}

- nfs4_lock_state();
-
if (lock->lk_is_new) {
if (nfsd4_has_session(cstate))
/* See rfc 5661 18.10.3: given clientid is ignored: */
@@ -4783,7 +4781,6 @@ out:
if (status && new_state)
release_lockowner_if_empty(lock_sop);
nfsd4_bump_seqid(cstate, status);
- nfs4_unlock_state();
if (file_lock)
locks_free_lock(file_lock);
if (conflock)
@@ -4826,8 +4823,6 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (check_lock_length(lockt->lt_offset, lockt->lt_length))
return nfserr_inval;

- nfs4_lock_state();
-
if (!nfsd4_has_session(cstate)) {
status = lookup_clientid(&lockt->lt_clientid, NULL, nn, NULL);
if (status)
@@ -4879,7 +4874,6 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
nfs4_set_lock_denied(file_lock, &lockt->lt_denied);
}
out:
- nfs4_unlock_state();
if (file_lock)
locks_free_lock(file_lock);
return status;
@@ -4903,8 +4897,6 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (check_lock_length(locku->lu_offset, locku->lu_length))
return nfserr_inval;

- nfs4_lock_state();
-
status = nfs4_preprocess_seqid_op(cstate, locku->lu_seqid,
&locku->lu_stateid, NFS4_LOCK_STID,
&stp, nn);
@@ -4947,7 +4939,6 @@ put_stateid:
put_generic_stateid(stp);
out:
nfsd4_bump_seqid(cstate, status);
- nfs4_unlock_state();
if (file_lock)
locks_free_lock(file_lock);
return status;
--
1.9.0


2014-04-18 18:46:21

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 49/70] NFSd: Cleanup - Let nfsd4_lookup_stateid() take a cstate argument

The cstate already holds information about the session, and hence
the client id, so it makes more sense to pass that information
rather than the current practice of passing a 'minor version' number.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index a3b6391a07b7..888acf114e40 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -3872,12 +3872,14 @@ out_put_stid:
return status;
}

-static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask,
- struct nfs4_stid **s, bool sessions,
+static __be32 nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate,
+ stateid_t *stateid, unsigned char typemask,
+ struct nfs4_stid **s,
struct nfsd_net *nn)
{
struct nfs4_client *cl;
__be32 status;
+ bool sessions = cstate->minorversion != 0;

if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
return nfserr_bad_stateid;
@@ -3923,8 +3925,9 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,

nfs4_lock_state();

- status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID,
- &s, cstate->minorversion, nn);
+ status = nfsd4_lookup_stateid(cstate, stateid,
+ NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID,
+ &s, nn);
if (status)
goto unlock_state;
status = check_stateid_generation(stateid, &s->sc_stateid, nfsd4_has_session(cstate));
@@ -4096,8 +4099,7 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
seqid, STATEID_VAL(stateid));

*stpp = NULL;
- status = nfsd4_lookup_stateid(stateid, typemask, &s,
- cstate->minorversion, nn);
+ status = nfsd4_lookup_stateid(cstate, stateid, typemask, &s, nn);
if (status)
return status;
stp = openlockstateid(s);
@@ -4332,8 +4334,7 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
return status;

nfs4_lock_state();
- status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID, &s,
- cstate->minorversion, nn);
+ status = nfsd4_lookup_stateid(cstate, stateid, NFS4_DELEG_STID, &s, nn);
if (status)
goto out;
dp = delegstateid(s);
--
1.9.0


2014-04-19 14:41:54

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH 35/70] NFSd: Slight cleanup of find_stateid()

> +static struct nfs4_stid *find_stateid_locked(struct nfs4_client *cl, stateid_t *t)
> {
> struct nfs4_stid *ret;
>
> - spin_lock(&cl->cl_lock);
> ret = idr_find(&cl->cl_stateids, t->si_opaque.so_id);
> - spin_unlock(&cl->cl_lock);
> if (!ret || !ret->sc_type)
> return NULL;

Just curious, how can !ret->sc_type happen?


2014-04-18 18:45:07

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 01/70] NFSd: Ensure we clear the cstate->slot in nfsd4_proc_compound

Otherwise, we may end up triggering all those nfsd4_has_session()
tests.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4proc.c | 1 +
1 file changed, 1 insertion(+)

diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index d543222babf3..8eabbfb25441 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -1293,6 +1293,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
cstate->minorversion = args->minorversion;
cstate->replay_owner = NULL;
cstate->session = NULL;
+ cstate->slot = NULL;
fh_init(current_fh, NFS4_FHSIZE);
fh_init(save_fh, NFS4_FHSIZE);
/*
--
1.9.0


2014-04-19 15:04:34

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH 00/70] NFSd lock scalability patches

So after this series there are 5 callers of nfs4_lock_state() left,
and I can't see any coherent scheme of what it still protects, but this:

--- snip ---
/* Currently used for almost all code touching nfsv4 state: */
static DEFINE_MUTEX(client_mutex);
--- snip ---

is clearly wrong. I'd suggest you go all the way and remove it entirely.


2014-04-18 18:46:23

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 50/70] NFSd: Use the session->se_client in lookup_clientid()

In NFSv4.x with x>0, we want to use the session's pointer to the
nfs4_client in order to optimise away the extra lookup of the clid.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 25 +++++++++++++++----------
1 file changed, 15 insertions(+), 10 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 888acf114e40..6e2d348d3367 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -3571,13 +3571,19 @@ void nfsd4_cleanup_open_state(struct nfsd4_open *open, __be32 status)
put_generic_stateid(open->op_stp);
}

-static __be32 lookup_clientid(clientid_t *clid, bool session, struct nfsd_net *nn, struct nfs4_client **clp)
+static __be32 lookup_clientid(clientid_t *clid, struct nfsd4_session *session, struct nfsd_net *nn, struct nfs4_client **clp)
{
struct nfs4_client *found;

- if (STALE_CLIENTID(clid, nn))
- return nfserr_stale_clientid;
- found = find_confirmed_client(clid, session, nn);
+ if (session != NULL) {
+ found = session->se_client;
+ if (!same_clid(&found->cl_clientid, clid))
+ return nfserr_stale_clientid;
+ } else {
+ if (STALE_CLIENTID(clid, nn))
+ return nfserr_stale_clientid;
+ found = find_confirmed_client(clid, false, nn);
+ }
if (clp)
*clp = found;
return found ? nfs_ok : nfserr_expired;
@@ -3594,7 +3600,7 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
nfs4_lock_state();
dprintk("process_renew(%08x/%08x): starting\n",
clid->cl_boot, clid->cl_id);
- status = lookup_clientid(clid, cstate->minorversion, nn, &clp);
+ status = lookup_clientid(clid, NULL, nn, &clp);
if (status)
goto out;
status = nfserr_cb_path_down;
@@ -3879,14 +3885,13 @@ static __be32 nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate,
{
struct nfs4_client *cl;
__be32 status;
- bool sessions = cstate->minorversion != 0;

if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
return nfserr_bad_stateid;
- status = lookup_clientid(&stateid->si_opaque.so_clid, sessions,
+ status = lookup_clientid(&stateid->si_opaque.so_clid, cstate->session,
nn, &cl);
if (status == nfserr_stale_clientid) {
- if (sessions)
+ if (cstate->session)
return nfserr_bad_stateid;
return nfserr_stale_stateid;
}
@@ -4770,7 +4775,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
nfs4_lock_state();

if (!nfsd4_has_session(cstate)) {
- status = lookup_clientid(&lockt->lt_clientid, false, nn, NULL);
+ status = lookup_clientid(&lockt->lt_clientid, NULL, nn, NULL);
if (status)
goto out;
}
@@ -4942,7 +4947,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,

nfs4_lock_state();

- status = lookup_clientid(clid, cstate->minorversion, nn, NULL);
+ status = lookup_clientid(clid, NULL, nn, NULL);
if (status)
goto out;

--
1.9.0


2014-04-18 18:45:22

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 11/70] NFSd: Ensure delegation setup is safe w.r.t. break_lease()

Ensure that we add/remove the dl_perfile under the inode->i_lock

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 85fcbc9ebd40..3656980b5d19 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -441,7 +441,9 @@ static void
hash_delegation_locked(struct nfs4_delegation *dp, struct nfs4_file *fp)
{
dp->dl_stid.sc_type = NFS4_DELEG_STID;
+ spin_lock(&fp->fi_inode->i_lock);
list_add(&dp->dl_perfile, &fp->fi_delegations);
+ spin_unlock(&fp->fi_inode->i_lock);
list_add(&dp->dl_perclnt, &dp->dl_stid.sc_client->cl_delegations);
}

@@ -449,13 +451,19 @@ hash_delegation_locked(struct nfs4_delegation *dp, struct nfs4_file *fp)
static void
unhash_delegation(struct nfs4_delegation *dp)
{
+ struct nfs4_file *fp = dp->dl_file;
+
spin_lock(&state_lock);
list_del_init(&dp->dl_perclnt);
- list_del_init(&dp->dl_perfile);
list_del_init(&dp->dl_recall_lru);
+ if (!list_empty(&dp->dl_perfile)) {
+ spin_lock(&fp->fi_inode->i_lock);
+ list_del_init(&dp->dl_perfile);
+ spin_unlock(&fp->fi_inode->i_lock);
+ }
spin_unlock(&state_lock);
- nfs4_put_deleg_lease(dp->dl_file);
- put_nfs4_file(dp->dl_file);
+ nfs4_put_deleg_lease(fp);
+ put_nfs4_file(fp);
dp->dl_file = NULL;
}

--
1.9.0


2014-04-18 18:45:44

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 25/70] NFSd: Simplify stateid management

Don't allow stateids to clear the open file pointer until they are
being destroyed.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index afb767319f66..51ef179339ae 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -394,7 +394,7 @@ kmem_cache *slab)
struct nfs4_stid *stid;
int new_id;

- stid = kmem_cache_alloc(slab, GFP_KERNEL);
+ stid = kmem_cache_zalloc(slab, GFP_KERNEL);
if (!stid)
return NULL;

@@ -481,6 +481,8 @@ void
nfs4_put_delegation(struct nfs4_delegation *dp)
{
if (atomic_dec_and_test(&dp->dl_stid.sc_count)) {
+ if (dp->dl_file)
+ put_nfs4_file(dp->dl_file);
nfs4_free_stid(deleg_slab, &dp->dl_stid);
num_delegations--;
}
@@ -528,10 +530,8 @@ unhash_delegation(struct nfs4_delegation *dp)
spin_unlock(&fp->fi_inode->i_lock);
}
nfs4_put_deleg_lease(fp);
- dp->dl_file = NULL;
spin_unlock(&fp->fi_lock);
spin_unlock(&state_lock);
- put_nfs4_file(fp);
}


@@ -701,13 +701,13 @@ static void unhash_generic_stateid(struct nfs4_ol_stateid *stp)
static void close_generic_stateid(struct nfs4_ol_stateid *stp)
{
release_all_access(stp);
- put_nfs4_file(stp->st_file);
- stp->st_file = NULL;
}

static void free_generic_stateid(struct nfs4_ol_stateid *stp)
{
remove_stid(&stp->st_stid);
+ if (stp->st_file)
+ put_nfs4_file(stp->st_file);
nfs4_free_stid(stateid_slab, &stp->st_stid);
}

@@ -3265,8 +3265,6 @@ unlock_and_free:
spin_unlock(&fp->fi_lock);
spin_unlock(&state_lock);
out_free:
- put_nfs4_file(fp);
- dp->dl_file = fp;
return status;
}

--
1.9.0


2014-04-21 13:01:40

by Trond Myklebust

[permalink] [raw]
Subject: Re: [PATCH 15/70] NFSd: Add locking to the nfs4_file->fi_fds[] array

Hi Christoph,

Thanks for reviewing these!

On Sat, 2014-04-19 at 07:35 -0700, Christoph Hellwig wrote:
> > +/* XXX: for first cut may fall back on returning file that doesn't work
> > + * at all? */
>
> I think moving this code around might be a good opportunity to remove
> this confusing comment.

Will do.

> > +static struct file *find_writeable_file(struct nfs4_file *f)
> > +{
> > + struct file *ret;
> > +
> > + spin_lock(&f->fi_lock);
> > + ret = __nfs4_get_fd(f, O_WRONLY);
> > + if (!ret)
> > + ret = __nfs4_get_fd(f, O_RDWR);
> > + spin_unlock(&f->fi_lock);
> > + return ret;
> > +}
> > +
> > +static struct file *find_readable_file(struct nfs4_file *f)
> > +{
> > + struct file *ret;
> > +
> > + spin_lock(&f->fi_lock);
> > + ret = __nfs4_get_fd(f, O_RDONLY);
> > + if (!ret)
> > + ret = __nfs4_get_fd(f, O_RDWR);
> > + spin_unlock(&f->fi_lock);
> > + return ret;
>
> Seems like these two functions could be easily consolidated by passing
> a single flags argument.

Yes, but we'd have to invent a new set of flags for just this function
and so I'm not sure that the end result would be more readable.
We can't reuse O_RDONLY/O_WRONLY/O_RDWR since they can't be bitwise ORed
to produce the 'read or read/write', 'write or read/write' combinations
that we need.

Cheers
Trond

--
Trond Myklebust
Linux NFS client maintainer, PrimaryData
[email protected]



2014-04-19 15:56:48

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH 08/70] nfsd4: use state_lock for delegation hashing

> +static void
> +hash_delegation_locked(struct nfs4_delegation *dp, struct nfs4_file *fp)
> +{

This series adds a lot of _locked function. I would really like to
see an assert_spin_locked or equivalent in each of them, to a) prove
we got the locking right, and to b) document the needed lock for new
code using them.


2014-04-18 18:46:48

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 63/70] NFSd: Remove nfs4_lock_state(): nfsd4_release_lockowner

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 3 ---
1 file changed, 3 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 6eac3ce68a8b..67895511cc05 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4999,8 +4999,6 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n",
clid->cl_boot, clid->cl_id);

- nfs4_lock_state();
-
status = lookup_clientid(clid, NULL, nn, NULL);
if (status)
goto out;
@@ -5034,7 +5032,6 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
release_lockowner(lo);
}
out:
- nfs4_unlock_state();
return status;
}

--
1.9.0


2014-04-18 18:45:59

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 34/70] NFSd: Fix atomicity of delegation counter

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 21 ++++++++++++++-------
1 file changed, 14 insertions(+), 7 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index f463aaf9cd13..87464802126a 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -304,7 +304,7 @@ static struct file *find_any_file(struct nfs4_file *f)
return ret;
}

-static int num_delegations;
+static atomic_long_t num_delegations;
unsigned long max_delegations;

/*
@@ -439,18 +439,19 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv
struct nfs4_delegation *dp;

dprintk("NFSD alloc_init_deleg\n");
- if (num_delegations > max_delegations)
- return NULL;
+ atomic_long_inc(&num_delegations);
+ smp_mb__after_atomic_inc();
+ if (atomic_long_read(&num_delegations) > max_delegations)
+ goto out_dec;
dp = delegstateid(nfs4_alloc_stid(clp, deleg_slab));
if (dp == NULL)
- return dp;
+ goto out_dec;
/*
* delegation seqid's are never incremented. The 4.1 special
* meaning of seqid 0 isn't meaningful, really, but let's avoid
* 0 anyway just for consistency and use 1:
*/
dp->dl_stid.sc_stateid.si_generation = 1;
- num_delegations++;
INIT_LIST_HEAD(&dp->dl_perfile);
INIT_LIST_HEAD(&dp->dl_perclnt);
INIT_LIST_HEAD(&dp->dl_recall_lru);
@@ -459,6 +460,10 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv
dp->dl_time = 0;
nfsd4_init_callback(&dp->dl_recall);
return dp;
+out_dec:
+ atomic_long_dec(&num_delegations);
+ smp_mb__after_atomic_dec();
+ return NULL;
}

static void remove_stid_locked(struct nfs4_client *clp, struct nfs4_stid *s)
@@ -490,8 +495,10 @@ static bool nfs4_put_stid(struct kmem_cache *slab, struct nfs4_stid *s)
void
nfs4_put_delegation(struct nfs4_delegation *dp)
{
- if (nfs4_put_stid(deleg_slab, &dp->dl_stid))
- num_delegations--;
+ if (nfs4_put_stid(deleg_slab, &dp->dl_stid)) {
+ atomic_long_dec(&num_delegations);
+ smp_mb__after_atomic_dec();
+ }
}

/* Call under fp->fi_lock */
--
1.9.0


2014-04-18 18:45:52

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 30/70] NFSd: Replace delegation->dl_file with the dl_stid.sc_file

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 13 +++++--------
fs/nfsd/state.h | 1 -
2 files changed, 5 insertions(+), 9 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 5bbef4720e7c..48979ee79888 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -454,7 +454,6 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv
INIT_LIST_HEAD(&dp->dl_perfile);
INIT_LIST_HEAD(&dp->dl_perclnt);
INIT_LIST_HEAD(&dp->dl_recall_lru);
- dp->dl_file = NULL;
dp->dl_type = NFS4_OPEN_DELEGATE_READ;
fh_copy_shallow(&dp->dl_fh, &current_fh->fh_handle);
dp->dl_time = 0;
@@ -483,8 +482,6 @@ void
nfs4_put_delegation(struct nfs4_delegation *dp)
{
if (atomic_dec_and_test(&dp->dl_stid.sc_count)) {
- if (dp->dl_file)
- put_nfs4_file(dp->dl_file);
nfs4_free_stid(deleg_slab, &dp->dl_stid);
num_delegations--;
}
@@ -520,7 +517,7 @@ hash_delegation_locked(struct nfs4_delegation *dp, struct nfs4_file *fp)
static void
unhash_delegation_locked(struct nfs4_delegation *dp)
{
- struct nfs4_file *fp = dp->dl_file;
+ struct nfs4_file *fp = dp->dl_stid.sc_file;

spin_lock(&fp->fi_lock);
list_del_init(&dp->dl_perclnt);
@@ -3204,14 +3201,14 @@ static struct file_lock *nfs4_alloc_init_lease(struct nfs4_delegation *dp, int f
fl->fl_flags = FL_DELEG;
fl->fl_type = flag == NFS4_OPEN_DELEGATE_READ? F_RDLCK: F_WRLCK;
fl->fl_end = OFFSET_MAX;
- fl->fl_owner = (fl_owner_t)(dp->dl_file);
+ fl->fl_owner = (fl_owner_t)(dp->dl_stid.sc_file);
fl->fl_pid = current->tgid;
return fl;
}

static int nfs4_setlease(struct nfs4_delegation *dp)
{
- struct nfs4_file *fp = dp->dl_file;
+ struct nfs4_file *fp = dp->dl_stid.sc_file;
struct file_lock *fl;
int status = 0;

@@ -3255,7 +3252,7 @@ static int nfs4_set_delegation(struct nfs4_delegation *dp, struct nfs4_file *fp)
get_nfs4_file(fp);
spin_lock(&state_lock);
spin_lock(&fp->fi_lock);
- dp->dl_file = fp;
+ dp->dl_stid.sc_file = fp;
if (!fp->fi_lease) {
spin_unlock(&fp->fi_lock);
spin_unlock(&state_lock);
@@ -3877,7 +3874,7 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,
if (status)
goto out;
if (filpp) {
- file = dp->dl_file->fi_deleg_file;
+ file = dp->dl_stid.sc_file->fi_deleg_file;
if (!file) {
WARN_ON_ONCE(1);
status = nfserr_serverfault;
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index c6deef936693..de198600616b 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -93,7 +93,6 @@ struct nfs4_delegation {
struct list_head dl_perfile;
struct list_head dl_perclnt;
struct list_head dl_recall_lru; /* delegation recalled */
- struct nfs4_file *dl_file;
u32 dl_type;
time_t dl_time;
/* For recall: */
--
1.9.0


2014-04-18 18:45:18

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 08/70] nfsd4: use state_lock for delegation hashing

From: Benny Halevy <[email protected]>

Signed-off-by: Benny Halevy <[email protected]>
---
fs/nfsd/nfs4state.c | 21 ++++++++++++++-------
1 file changed, 14 insertions(+), 7 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index fe8e1bc9578b..45c4b692e30d 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -437,12 +437,20 @@ static void unhash_stid(struct nfs4_stid *s)
s->sc_type = 0;
}

+static void
+hash_delegation_locked(struct nfs4_delegation *dp, struct nfs4_file *fp)
+{
+ dp->dl_stid.sc_type = NFS4_DELEG_STID;
+ list_add(&dp->dl_perfile, &fp->fi_delegations);
+ list_add(&dp->dl_perclnt, &dp->dl_stid.sc_client->cl_delegations);
+}
+
/* Called under the state lock. */
static void
unhash_delegation(struct nfs4_delegation *dp)
{
- list_del_init(&dp->dl_perclnt);
spin_lock(&state_lock);
+ list_del_init(&dp->dl_perclnt);
list_del_init(&dp->dl_perfile);
list_del_init(&dp->dl_recall_lru);
spin_unlock(&state_lock);
@@ -3058,12 +3066,12 @@ static int nfs4_setlease(struct nfs4_delegation *dp)
status = vfs_setlease(fl->fl_file, fl->fl_type, &fl);
if (status)
goto out_free;
- list_add(&dp->dl_perclnt, &dp->dl_stid.sc_client->cl_delegations);
fp->fi_lease = fl;
fp->fi_deleg_file = get_file(fl->fl_file);
atomic_set(&fp->fi_delegees, 1);
- dp->dl_stid.sc_type = NFS4_DELEG_STID;
- list_add(&dp->dl_perfile, &fp->fi_delegations);
+ spin_lock(&state_lock);
+ hash_delegation_locked(dp, fp);
+ spin_unlock(&state_lock);
return 0;
out_free:
locks_free_lock(fl);
@@ -3091,10 +3099,8 @@ static int nfs4_set_delegation(struct nfs4_delegation *dp, struct nfs4_file *fp)
goto out_free;
}
atomic_inc(&fp->fi_delegees);
- dp->dl_stid.sc_type = NFS4_DELEG_STID;
- list_add(&dp->dl_perfile, &fp->fi_delegations);
+ hash_delegation_locked(dp, fp);
spin_unlock(&state_lock);
- list_add(&dp->dl_perclnt, &dp->dl_stid.sc_client->cl_delegations);
return 0;
out_free:
put_nfs4_file(fp);
@@ -4898,6 +4904,7 @@ static u64 nfsd_find_all_delegations(struct nfs4_client *clp, u64 max,
struct nfs4_delegation *dp, *next;
u64 count = 0;

+ lockdep_assert_held(&state_lock);
list_for_each_entry_safe(dp, next, &clp->cl_delegations, dl_perclnt) {
if (victims)
list_move(&dp->dl_recall_lru, victims);
--
1.9.0


2014-04-18 18:45:08

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 02/70] NFSd: Move default initialisers from create_client() to alloc_client()

Aside from making it clearer what is non-trivial in create_client(), it
also fixes a bug whereby we can call free_client() before idr_init()
has been called.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 3ba65979a3cd..230d21cb1717 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1078,6 +1078,18 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name)
return NULL;
}
clp->cl_name.len = name.len;
+ INIT_LIST_HEAD(&clp->cl_sessions);
+ idr_init(&clp->cl_stateids);
+ atomic_set(&clp->cl_refcount, 0);
+ clp->cl_cb_state = NFSD4_CB_UNKNOWN;
+ INIT_LIST_HEAD(&clp->cl_idhash);
+ INIT_LIST_HEAD(&clp->cl_openowners);
+ INIT_LIST_HEAD(&clp->cl_delegations);
+ INIT_LIST_HEAD(&clp->cl_lru);
+ INIT_LIST_HEAD(&clp->cl_callbacks);
+ INIT_LIST_HEAD(&clp->cl_revoked);
+ spin_lock_init(&clp->cl_lock);
+ rpc_init_wait_queue(&clp->cl_cb_waitq, "Backchannel slot table");
return clp;
}

@@ -1347,7 +1359,6 @@ static struct nfs4_client *create_client(struct xdr_netobj name,
if (clp == NULL)
return NULL;

- INIT_LIST_HEAD(&clp->cl_sessions);
ret = copy_cred(&clp->cl_cred, &rqstp->rq_cred);
if (ret) {
spin_lock(&nn->client_lock);
@@ -1355,20 +1366,9 @@ static struct nfs4_client *create_client(struct xdr_netobj name,
spin_unlock(&nn->client_lock);
return NULL;
}
- idr_init(&clp->cl_stateids);
- atomic_set(&clp->cl_refcount, 0);
- clp->cl_cb_state = NFSD4_CB_UNKNOWN;
- INIT_LIST_HEAD(&clp->cl_idhash);
- INIT_LIST_HEAD(&clp->cl_openowners);
- INIT_LIST_HEAD(&clp->cl_delegations);
- INIT_LIST_HEAD(&clp->cl_lru);
- INIT_LIST_HEAD(&clp->cl_callbacks);
- INIT_LIST_HEAD(&clp->cl_revoked);
- spin_lock_init(&clp->cl_lock);
nfsd4_init_callback(&clp->cl_cb_null);
clp->cl_time = get_seconds();
clear_bit(0, &clp->cl_cb_slot_busy);
- rpc_init_wait_queue(&clp->cl_cb_waitq, "Backchannel slot table");
copy_verf(clp, verf);
rpc_copy_addr((struct sockaddr *) &clp->cl_addr, sa);
gen_confirm(clp);
--
1.9.0


2014-04-18 18:46:39

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 57/70] NFSd: Protect unconfirmed client creation using client_lock

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 31 +++++++++++++++++++++----------
1 file changed, 21 insertions(+), 10 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 79948fd2ad9e..9c24031757d2 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1633,7 +1633,7 @@ add_to_unconfirmed(struct nfs4_client *clp)
add_clp_to_name_tree(clp, &nn->unconf_name_tree);
idhashval = clientid_hashval(clp->cl_clientid.cl_id);
list_add(&clp->cl_idhash, &nn->unconf_id_hashtbl[idhashval]);
- renew_client(clp);
+ renew_client_locked(clp);
}

static void
@@ -1647,7 +1647,7 @@ move_to_confirmed(struct nfs4_client *clp)
rb_erase(&clp->cl_namenode, &nn->unconf_name_tree);
add_clp_to_name_tree(clp, &nn->conf_name_tree);
set_bit(NFSD4_CLIENT_CONFIRMED, &clp->cl_flags);
- renew_client(clp);
+ renew_client_locked(clp);
}

static struct nfs4_client *
@@ -1660,7 +1660,7 @@ find_client_in_id_table(struct list_head *tbl, clientid_t *clid, bool sessions)
if (same_clid(&clp->cl_clientid, clid)) {
if ((bool)clp->cl_minorversion != sessions)
return NULL;
- renew_client(clp);
+ renew_client_locked(clp);
return clp;
}
}
@@ -1859,7 +1859,8 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
struct nfsd4_compound_state *cstate,
struct nfsd4_exchange_id *exid)
{
- struct nfs4_client *unconf, *conf, *new;
+ struct nfs4_client *conf, *new;
+ struct nfs4_client *unconf = NULL;
__be32 status;
char addr_str[INET6_ADDRSTRLEN];
nfs4_verifier verf = exid->verifier;
@@ -1894,6 +1895,7 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,

/* Cases below refer to rfc 5661 section 18.35.4: */
nfs4_lock_state();
+ spin_lock(&nn->client_lock);
conf = find_confirmed_client_by_name(&exid->clname, nn);
if (conf) {
bool creds_match = same_creds(&conf->cl_cred, &rqstp->rq_cred);
@@ -1943,17 +1945,18 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,

unconf = find_unconfirmed_client_by_name(&exid->clname, nn);
if (unconf) /* case 4, possible retry or client restart */
- expire_client(unconf);
+ unhash_client_locked(unconf);

/* case 1 (normal case) */
out_new:
+ if (conf)
+ unhash_client_locked(conf);
new->cl_minorversion = cstate->minorversion;
new->cl_mach_cred = (exid->spa_how == SP4_MACH_CRED);

gen_clid(new, nn);
add_to_unconfirmed(new);
- conf = new;
- new = NULL;
+ swap(new, conf);
out_copy:
exid->clientid.cl_boot = conf->cl_clientid.cl_boot;
exid->clientid.cl_id = conf->cl_clientid.cl_id;
@@ -1966,9 +1969,12 @@ out_copy:
status = nfs_ok;

out:
+ spin_unlock(&nn->client_lock);
nfs4_unlock_state();
if (new)
- free_client(new);
+ expire_client(new);
+ if (unconf)
+ expire_client(unconf);
return status;
}

@@ -2592,7 +2598,8 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
{
struct xdr_netobj clname = setclid->se_name;
nfs4_verifier clverifier = setclid->se_verf;
- struct nfs4_client *conf, *unconf, *new;
+ struct nfs4_client *conf, *new;
+ struct nfs4_client *unconf = NULL;
__be32 status;
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);

@@ -2601,6 +2608,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
return nfserr_jukebox;
/* Cases below refer to rfc 3530 section 14.2.33: */
nfs4_lock_state();
+ spin_lock(&nn->client_lock);
conf = find_confirmed_client_by_name(&clname, nn);
if (conf) {
/* case 0: */
@@ -2618,7 +2626,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
}
unconf = find_unconfirmed_client_by_name(&clname, nn);
if (unconf)
- expire_client(unconf);
+ unhash_client_locked(unconf);
if (conf && same_verf(&conf->cl_verifier, &clverifier))
/* case 1: probable callback update */
copy_clid(new, conf);
@@ -2633,9 +2641,12 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
new = NULL;
status = nfs_ok;
out:
+ spin_unlock(&nn->client_lock);
nfs4_unlock_state();
if (new)
free_client(new);
+ if (unconf)
+ expire_client(unconf);
return status;
}

--
1.9.0


2014-04-18 18:45:17

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 07/70] nfsd4: hash deleg stateid only on successful nfs4_set_delegation

From: Benny Halevy <[email protected]>

We don't want the stateid to be found in the hash table before the delegation
is granted.

Signed-off-by: Benny Halevy <[email protected]>
---
fs/nfsd/nfs4state.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 626f310a74a8..fe8e1bc9578b 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -379,7 +379,6 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv
dp = delegstateid(nfs4_alloc_stid(clp, deleg_slab));
if (dp == NULL)
return dp;
- dp->dl_stid.sc_type = NFS4_DELEG_STID;
/*
* delegation seqid's are never incremented. The 4.1 special
* meaning of seqid 0 isn't meaningful, really, but let's avoid
@@ -3063,6 +3062,7 @@ static int nfs4_setlease(struct nfs4_delegation *dp)
fp->fi_lease = fl;
fp->fi_deleg_file = get_file(fl->fl_file);
atomic_set(&fp->fi_delegees, 1);
+ dp->dl_stid.sc_type = NFS4_DELEG_STID;
list_add(&dp->dl_perfile, &fp->fi_delegations);
return 0;
out_free:
@@ -3091,6 +3091,7 @@ static int nfs4_set_delegation(struct nfs4_delegation *dp, struct nfs4_file *fp)
goto out_free;
}
atomic_inc(&fp->fi_delegees);
+ dp->dl_stid.sc_type = NFS4_DELEG_STID;
list_add(&dp->dl_perfile, &fp->fi_delegations);
spin_unlock(&state_lock);
list_add(&dp->dl_perclnt, &dp->dl_stid.sc_client->cl_delegations);
--
1.9.0


2014-04-18 18:45:33

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 18/70] NFSd: Clean up helper __release_lock_stateid

Use filp_close() instead of open coding

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 8a12d59c8f36..0809e8355577 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -704,7 +704,8 @@ static void free_generic_stateid(struct nfs4_ol_stateid *stp)
nfs4_free_stid(stateid_slab, &stp->st_stid);
}

-static void __release_lock_stateid(struct nfs4_ol_stateid *stp)
+static void __release_lock_stateid(struct nfs4_lockowner *lo,
+ struct nfs4_ol_stateid *stp)
{
struct file *file;

@@ -712,10 +713,8 @@ static void __release_lock_stateid(struct nfs4_ol_stateid *stp)
unhash_generic_stateid(stp);
unhash_stid(&stp->st_stid);
file = find_any_file(stp->st_file);
- if (file) {
- locks_remove_posix(file, (fl_owner_t)lockowner(stp->st_stateowner));
- fput(file);
- }
+ if (file)
+ filp_close(file, (fl_owner_t)lo);
close_generic_stateid(stp);
free_generic_stateid(stp);
}
@@ -729,7 +728,7 @@ static void unhash_lockowner(struct nfs4_lockowner *lo)
while (!list_empty(&lo->lo_owner.so_stateids)) {
stp = list_first_entry(&lo->lo_owner.so_stateids,
struct nfs4_ol_stateid, st_perstateowner);
- __release_lock_stateid(stp);
+ __release_lock_stateid(lo, stp);
}
}

@@ -756,7 +755,7 @@ static void release_lock_stateid(struct nfs4_ol_stateid *stp)
struct nfs4_lockowner *lo;

lo = lockowner(stp->st_stateowner);
- __release_lock_stateid(stp);
+ __release_lock_stateid(lo, stp);
release_lockowner_if_empty(lo);
}

--
1.9.0


2014-04-18 18:46:20

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 48/70] NFSd: Migrate the stateid reference into nfs4_find_stateid_by_type()

Allow nfs4_find_stateid_by_type to take the stateid reference, while
still holding the &cl->cl_lock.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 08d8fb501e81..a3b6391a07b7 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1528,8 +1528,12 @@ static struct nfs4_stid *find_stateid_by_type(struct nfs4_client *cl, stateid_t

spin_lock(&cl->cl_lock);
s = find_stateid_locked(cl, t);
- if (s != NULL && !(typemask & s->sc_type))
- s = NULL;
+ if (s != NULL) {
+ if (typemask & s->sc_type)
+ atomic_inc(&s->sc_count);
+ else
+ s = NULL;
+ }
spin_unlock(&cl->cl_lock);
return s;
}
@@ -3069,8 +3073,6 @@ static struct nfs4_delegation *find_deleg_stateid(struct nfs4_client *cl, statei
ret = find_stateid_by_type(cl, s, NFS4_DELEG_STID);
if (!ret)
return NULL;
- /* FIXME: move into find_stateid_by_type */
- atomic_inc(&ret->sc_count);
return delegstateid(ret);
}

@@ -3891,8 +3893,6 @@ static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask,
*s = find_stateid_by_type(cl, stateid, typemask);
if (!*s)
return nfserr_bad_stateid;
- /* FIXME: move into find_stateid_by_type */
- atomic_inc(&(*s)->sc_count);
return nfs_ok;
}

--
1.9.0


2014-04-18 18:46:55

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 68/70] NFSd: Remove nfs4_lock_state(): exchange_id, create/destroy_session()

Also destroy_clientid and bind_conn_to_session.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 11 -----------
1 file changed, 11 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 188eb4cbea95..3a0a3e8c9982 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1890,7 +1890,6 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
return nfserr_jukebox;

/* Cases below refer to rfc 5661 section 18.35.4: */
- nfs4_lock_state();
spin_lock(&nn->client_lock);
conf = find_confirmed_client_by_name(&exid->clname, nn);
if (conf) {
@@ -1966,7 +1965,6 @@ out_copy:

out:
spin_unlock(&nn->client_lock);
- nfs4_unlock_state();
if (new)
expire_client(new);
if (unconf)
@@ -2140,7 +2138,6 @@ nfsd4_create_session(struct svc_rqst *rqstp,
if (!conn)
goto out_free_session;

- nfs4_lock_state();
spin_lock(&nn->client_lock);
unconf = find_unconfirmed_client(&cr_ses->clientid, true, nn);
conf = find_confirmed_client(&cr_ses->clientid, true, nn);
@@ -2209,13 +2206,11 @@ nfsd4_create_session(struct svc_rqst *rqstp,
/* init connection and backchannel */
nfsd4_init_conn(rqstp, conn, new);
nfsd4_put_session(new);
- nfs4_unlock_state();
if (old)
expire_client(old);
return status;
out_free_conn:
spin_unlock(&nn->client_lock);
- nfs4_unlock_state();
free_conn(conn);
if (old)
expire_client(old);
@@ -2271,7 +2266,6 @@ __be32 nfsd4_bind_conn_to_session(struct svc_rqst *rqstp,

if (!nfsd4_last_compound_op(rqstp))
return nfserr_not_only_op;
- nfs4_lock_state();
spin_lock(&nn->client_lock);
session = find_in_sessionid_hashtbl(&bcts->sessionid, net, &status);
spin_unlock(&nn->client_lock);
@@ -2292,7 +2286,6 @@ __be32 nfsd4_bind_conn_to_session(struct svc_rqst *rqstp,
out:
nfsd4_put_session(session);
out_no_session:
- nfs4_unlock_state();
return status;
}

@@ -2314,7 +2307,6 @@ nfsd4_destroy_session(struct svc_rqst *r,
struct net *net = SVC_NET(r);
struct nfsd_net *nn = net_generic(net, nfsd_net_id);

- nfs4_lock_state();
status = nfserr_not_only_op;
if (nfsd4_compound_in_session(cstate->session, &sessionid->sessionid)) {
if (!nfsd4_last_compound_op(r))
@@ -2344,7 +2336,6 @@ out_put_session:
out_client_lock:
spin_unlock(&nn->client_lock);
out:
- nfs4_unlock_state();
return status;
}

@@ -2532,7 +2523,6 @@ nfsd4_destroy_clientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *csta
__be32 status = 0;
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);

- nfs4_lock_state();
spin_lock(&nn->client_lock);
unconf = find_unconfirmed_client(&dc->clientid, true, nn);
conf = find_confirmed_client(&dc->clientid, true, nn);
@@ -2558,7 +2548,6 @@ nfsd4_destroy_clientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *csta
unhash_client_locked(clp);
out:
spin_unlock(&nn->client_lock);
- nfs4_unlock_state();
if (clp)
expire_client(clp);
return status;
--
1.9.0


2014-04-18 18:45:32

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 17/70] NFSd: Lock owners are not per open stateid

In the NFSv4 spec, lock stateids are per-file objects. Lockowners are not.
This patch replaces the current list of lock owners in the open statids
with a list of lock stateids.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 48 +++++++++++++++++++++++++++++++-----------------
fs/nfsd/state.h | 3 +--
2 files changed, 32 insertions(+), 19 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 5105dbc76888..8a12d59c8f36 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -704,10 +704,11 @@ static void free_generic_stateid(struct nfs4_ol_stateid *stp)
nfs4_free_stid(stateid_slab, &stp->st_stid);
}

-static void release_lock_stateid(struct nfs4_ol_stateid *stp)
+static void __release_lock_stateid(struct nfs4_ol_stateid *stp)
{
struct file *file;

+ list_del(&stp->st_locks);
unhash_generic_stateid(stp);
unhash_stid(&stp->st_stid);
file = find_any_file(stp->st_file);
@@ -724,12 +725,11 @@ static void unhash_lockowner(struct nfs4_lockowner *lo)
struct nfs4_ol_stateid *stp;

list_del(&lo->lo_owner.so_strhash);
- list_del(&lo->lo_perstateid);
list_del(&lo->lo_owner_ino_hash);
while (!list_empty(&lo->lo_owner.so_stateids)) {
stp = list_first_entry(&lo->lo_owner.so_stateids,
struct nfs4_ol_stateid, st_perstateowner);
- release_lock_stateid(stp);
+ __release_lock_stateid(stp);
}
}

@@ -745,22 +745,36 @@ static void release_lockowner(struct nfs4_lockowner *lo)
nfs4_free_lockowner(lo);
}

-static void
-release_stateid_lockowners(struct nfs4_ol_stateid *open_stp)
+static void release_lockowner_if_empty(struct nfs4_lockowner *lo)
+{
+ if (list_empty(&lo->lo_owner.so_stateids))
+ release_lockowner(lo);
+}
+
+static void release_lock_stateid(struct nfs4_ol_stateid *stp)
{
struct nfs4_lockowner *lo;

- while (!list_empty(&open_stp->st_lockowners)) {
- lo = list_entry(open_stp->st_lockowners.next,
- struct nfs4_lockowner, lo_perstateid);
- release_lockowner(lo);
+ lo = lockowner(stp->st_stateowner);
+ __release_lock_stateid(stp);
+ release_lockowner_if_empty(lo);
+}
+
+static void release_open_stateid_locks(struct nfs4_ol_stateid *open_stp)
+{
+ struct nfs4_ol_stateid *stp;
+
+ while (!list_empty(&open_stp->st_locks)) {
+ stp = list_entry(open_stp->st_locks.next,
+ struct nfs4_ol_stateid, st_locks);
+ release_lock_stateid(stp);
}
}

static void unhash_open_stateid(struct nfs4_ol_stateid *stp)
{
unhash_generic_stateid(stp);
- release_stateid_lockowners(stp);
+ release_open_stateid_locks(stp);
close_generic_stateid(stp);
}

@@ -2716,7 +2730,7 @@ static void init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp,
struct nfs4_openowner *oo = open->op_openowner;

stp->st_stid.sc_type = NFS4_OPEN_STID;
- INIT_LIST_HEAD(&stp->st_lockowners);
+ INIT_LIST_HEAD(&stp->st_locks);
list_add(&stp->st_perstateowner, &oo->oo_owner.so_stateids);
stp->st_stateowner = &oo->oo_owner;
get_nfs4_file(fp);
@@ -4327,7 +4341,6 @@ static void hash_lockowner(struct nfs4_lockowner *lo, unsigned int strhashval, s

list_add(&lo->lo_owner.so_strhash, &nn->ownerstr_hashtbl[strhashval]);
list_add(&lo->lo_owner_ino_hash, &nn->lockowner_ino_hashtbl[inohash]);
- list_add(&lo->lo_perstateid, &open_stp->st_lockowners);
}

/*
@@ -4371,6 +4384,7 @@ alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, struct
stp->st_access_bmap = 0;
stp->st_deny_bmap = open_stp->st_deny_bmap;
stp->st_openstp = open_stp;
+ list_add(&stp->st_locks, &open_stp->st_locks);
spin_lock(&fp->fi_lock);
list_add(&stp->st_perfile, &fp->fi_stateids);
spin_unlock(&fp->fi_lock);
@@ -4962,18 +4976,18 @@ static void nfsd_print_count(struct nfs4_client *clp, unsigned int count,
printk(KERN_INFO "NFS Client: %s has %u %s\n", buf, count, type);
}

-static u64 nfsd_foreach_client_lock(struct nfs4_client *clp, u64 max, void (*func)(struct nfs4_lockowner *))
+static u64 nfsd_foreach_client_lock(struct nfs4_client *clp, u64 max, void (*func)(struct nfs4_ol_stateid *))
{
struct nfs4_openowner *oop;
- struct nfs4_lockowner *lop, *lo_next;
struct nfs4_ol_stateid *stp, *st_next;
+ struct nfs4_ol_stateid *lst, *lst_next;
u64 count = 0;

list_for_each_entry(oop, &clp->cl_openowners, oo_perclient) {
list_for_each_entry_safe(stp, st_next, &oop->oo_owner.so_stateids, st_perstateowner) {
- list_for_each_entry_safe(lop, lo_next, &stp->st_lockowners, lo_perstateid) {
+ list_for_each_entry_safe(lst, lst_next, &stp->st_locks, st_locks) {
if (func)
- func(lop);
+ func(lst);
if (++count == max)
return count;
}
@@ -4985,7 +4999,7 @@ static u64 nfsd_foreach_client_lock(struct nfs4_client *clp, u64 max, void (*fun

u64 nfsd_forget_client_locks(struct nfs4_client *clp, u64 max)
{
- return nfsd_foreach_client_lock(clp, max, release_lockowner);
+ return nfsd_foreach_client_lock(clp, max, release_lock_stateid);
}

u64 nfsd_print_client_locks(struct nfs4_client *clp, u64 max)
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 004733885cca..51df8df04d6a 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -367,7 +367,6 @@ struct nfs4_openowner {
struct nfs4_lockowner {
struct nfs4_stateowner lo_owner; /* must be first element */
struct list_head lo_owner_ino_hash; /* hash by owner,file */
- struct list_head lo_perstateid;
struct list_head lo_list; /* for temporary uses */
};

@@ -410,7 +409,7 @@ struct nfs4_ol_stateid {
struct nfs4_stid st_stid; /* must be first field */
struct list_head st_perfile;
struct list_head st_perstateowner;
- struct list_head st_lockowners;
+ struct list_head st_locks;
struct nfs4_stateowner * st_stateowner;
struct nfs4_file * st_file;
unsigned long st_access_bmap;
--
1.9.0


2014-04-18 18:45:57

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 32/70] NFSd: Ensure stateids remain unique until they are freed

Add an extra delegation state to allow the stateid to remain in the idr
tree until the last reference has been released.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 8 ++++----
fs/nfsd/state.h | 1 +
2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 2cb4124276d0..7b897ff03a27 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -473,6 +473,7 @@ static void remove_stid(struct nfs4_stid *s)

static void nfs4_free_stid(struct kmem_cache *slab, struct nfs4_stid *s)
{
+ remove_stid(s);
if (s->sc_file)
put_nfs4_file(s->sc_file);
kmem_cache_free(slab, s);
@@ -541,7 +542,7 @@ unhash_delegation(struct nfs4_delegation *dp)

static void destroy_delegation(struct nfs4_delegation *dp)
{
- remove_stid(&dp->dl_stid);
+ dp->dl_stid.sc_type = NFS4_CLOSED_DELEG_STID;
nfs4_put_delegation(dp);
}

@@ -554,7 +555,7 @@ static void unhash_and_destroy_delegation(struct nfs4_delegation *dp)
static void destroy_revoked_delegation(struct nfs4_delegation *dp)
{
list_del_init(&dp->dl_recall_lru);
- destroy_delegation(dp);
+ nfs4_put_delegation(dp);
}

static void revoke_delegation(struct nfs4_delegation *dp)
@@ -713,7 +714,6 @@ static void put_generic_stateid(struct nfs4_ol_stateid *stp)
{
if (!atomic_dec_and_test(&stp->st_stid.sc_count))
return;
- remove_stid(&stp->st_stid);
nfs4_free_stid(stateid_slab, &stp->st_stid);
}

@@ -3359,7 +3359,6 @@ nfs4_open_delegation(struct net *net, struct svc_fh *fh,
open->op_delegate_type = NFS4_OPEN_DELEGATE_READ;
return;
out_free:
- remove_stid(&dp->dl_stid);
nfs4_put_delegation(dp);
out_no_deleg:
open->op_delegate_type = NFS4_OPEN_DELEGATE_NONE;
@@ -3803,6 +3802,7 @@ static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid)
default:
printk("unknown stateid type %x\n", s->sc_type);
case NFS4_CLOSED_STID:
+ case NFS4_CLOSED_DELEG_STID:
return nfserr_bad_stateid;
}
}
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 0539db97f7d3..a4ac75a0de21 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -82,6 +82,7 @@ struct nfs4_stid {
#define NFS4_CLOSED_STID 8
/* For a deleg stateid kept around only to process free_stateid's: */
#define NFS4_REVOKED_DELEG_STID 16
+#define NFS4_CLOSED_DELEG_STID 32
unsigned char sc_type;
stateid_t sc_stateid;
struct nfs4_client *sc_client;
--
1.9.0


2014-04-21 13:16:36

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH 15/70] NFSd: Add locking to the nfs4_file->fi_fds[] array

On Mon, Apr 21, 2014 at 06:14:12AM -0700, Christoph Hellwig wrote:
> One option would be:

Doesn't work because O_RDONLY is defined as 0. But if we use
FMODE_READ and FMODE_WRITE as flag it should work.


2014-04-18 18:45:35

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 19/70] NFSd: Allow lockowners to hold several stateids

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 46 +++++++++++++++++++++++++++++-----------------
1 file changed, 29 insertions(+), 17 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 0809e8355577..dad2f7b511b8 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4390,6 +4390,19 @@ alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, struct
return stp;
}

+static struct nfs4_ol_stateid *
+find_lock_stateid(struct nfs4_lockowner *lo, struct inode *inode)
+{
+ struct nfs4_ol_stateid *lst;
+
+ list_for_each_entry(lst, &lo->lo_owner.so_stateids, st_perstateowner) {
+ if (lst->st_file->fi_inode == inode)
+ return lst;
+ }
+ return NULL;
+}
+
+
static int
check_lock_length(u64 offset, u64 length)
{
@@ -4419,25 +4432,24 @@ static __be32 lookup_or_create_lock_state(struct nfsd4_compound_state *cstate, s

lo = find_lockowner_str(fi->fi_inode, &cl->cl_clientid,
&lock->v.new.owner, nn);
- if (lo) {
- if (!cstate->minorversion)
- return nfserr_bad_seqid;
- /* XXX: a lockowner always has exactly one stateid: */
- *lst = list_first_entry(&lo->lo_owner.so_stateids,
- struct nfs4_ol_stateid, st_perstateowner);
- return nfs_ok;
+ if (!lo) {
+ strhashval = ownerstr_hashval(cl->cl_clientid.cl_id,
+ &lock->v.new.owner);
+ lo = alloc_init_lock_stateowner(strhashval, cl, ost, lock);
+ if (lo == NULL)
+ return nfserr_jukebox;
}
- strhashval = ownerstr_hashval(cl->cl_clientid.cl_id,
- &lock->v.new.owner);
- lo = alloc_init_lock_stateowner(strhashval, cl, ost, lock);
- if (lo == NULL)
- return nfserr_jukebox;
- *lst = alloc_init_lock_stateid(lo, fi, ost);
+ if (!cstate->minorversion)
+ return nfserr_bad_seqid;
+ *lst = find_lock_stateid(lo, fi->fi_inode);
if (*lst == NULL) {
- release_lockowner(lo);
- return nfserr_jukebox;
+ *lst = alloc_init_lock_stateid(lo, fi, ost);
+ if (*lst == NULL) {
+ release_lockowner_if_empty(lo);
+ return nfserr_jukebox;
+ }
+ *new = true;
}
- *new = true;
return nfs_ok;
}

@@ -4596,7 +4608,7 @@ out:
if (filp)
fput(filp);
if (status && new_state)
- release_lockowner(lock_sop);
+ release_lockowner_if_empty(lock_sop);
nfsd4_bump_seqid(cstate, status);
nfs4_unlock_state();
if (file_lock)
--
1.9.0


2014-04-21 13:31:30

by Trond Myklebust

[permalink] [raw]
Subject: Re: [PATCH 35/70] NFSd: Slight cleanup of find_stateid()

On Sat, 2014-04-19 at 07:41 -0700, Christoph Hellwig wrote:
> > +static struct nfs4_stid *find_stateid_locked(struct nfs4_client *cl, stateid_t *t)
> > {
> > struct nfs4_stid *ret;
> >
> > - spin_lock(&cl->cl_lock);
> > ret = idr_find(&cl->cl_stateids, t->si_opaque.so_id);
> > - spin_unlock(&cl->cl_lock);
> > if (!ret || !ret->sc_type)
> > return NULL;
>
> Just curious, how can !ret->sc_type happen?
>

At least for open and lock stateids, the ret->sc_type field isn't
initialised until nfsd4_process_open2() and alloc_init_lock_stateid() so
there is a small window where they are visible to the idr tree while not
being fully initialised.

--
Trond Myklebust
Linux NFS client maintainer, PrimaryData
[email protected]



2014-04-18 18:45:47

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 27/70] NFSd: Don't let the laundromat reap clients that are referenced

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 3 +++
1 file changed, 3 insertions(+)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 273bdddb2458..41314e493110 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -3600,6 +3600,9 @@ nfs4_laundromat(struct nfsd_net *nn)
clp->cl_clientid.cl_id);
continue;
}
+ /* Hey, I'm busy with this guy! */
+ if (atomic_read(&clp->cl_refcount) != 0)
+ continue;
list_move(&clp->cl_lru, &reaplist);
}
spin_unlock(&nn->client_lock);
--
1.9.0


2014-04-18 18:46:47

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 62/70] NFSd: Remove nfs4_lock_state(): nfsd4_test_stateid/nfsd4_free_stateid

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 4 ----
1 file changed, 4 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 67daa70dd9a2..6eac3ce68a8b 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4064,11 +4064,9 @@ nfsd4_test_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfsd4_test_stateid_id *stateid;
struct nfs4_client *cl = cstate->session->se_client;

- nfs4_lock_state();
list_for_each_entry(stateid, &test_stateid->ts_stateid_list, ts_id_list)
stateid->ts_id_status =
nfsd4_validate_stateid(cl, &stateid->ts_id_stateid);
- nfs4_unlock_state();

return nfs_ok;
}
@@ -4083,7 +4081,6 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfs4_client *cl = cstate->session->se_client;
__be32 ret = nfserr_bad_stateid;

- nfs4_lock_state();
s = find_stateid(cl, stateid);
if (!s)
goto out;
@@ -4111,7 +4108,6 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
}
nfs4_put_stateid(s);
out:
- nfs4_unlock_state();
return ret;
}

--
1.9.0


2014-04-18 18:45:55

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 31/70] NFSd: Replace nfs4_ol_stateid->st_file with the st_stid.sc_file

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 38 ++++++++++++++++++--------------------
fs/nfsd/state.h | 1 -
2 files changed, 18 insertions(+), 21 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 48979ee79888..2cb4124276d0 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -688,7 +688,7 @@ release_all_access(struct nfs4_ol_stateid *stp)

for (i = 1; i < 4; i++) {
if (test_access(i, stp))
- nfs4_file_put_access(stp->st_file,
+ nfs4_file_put_access(stp->st_stid.sc_file,
nfs4_access_to_omode(i));
clear_access(i, stp);
}
@@ -696,7 +696,7 @@ release_all_access(struct nfs4_ol_stateid *stp)

static void unhash_generic_stateid(struct nfs4_ol_stateid *stp)
{
- struct nfs4_file *fp = stp->st_file;
+ struct nfs4_file *fp = stp->st_stid.sc_file;

spin_lock(&fp->fi_lock);
list_del(&stp->st_perfile);
@@ -714,8 +714,6 @@ static void put_generic_stateid(struct nfs4_ol_stateid *stp)
if (!atomic_dec_and_test(&stp->st_stid.sc_count))
return;
remove_stid(&stp->st_stid);
- if (stp->st_file)
- put_nfs4_file(stp->st_file);
nfs4_free_stid(stateid_slab, &stp->st_stid);
}

@@ -727,7 +725,7 @@ static void __release_lock_stateid(struct nfs4_lockowner *lo,
list_del(&stp->st_locks);
unhash_generic_stateid(stp);
unhash_stid(&stp->st_stid);
- file = find_any_file(stp->st_file);
+ file = find_any_file(stp->st_stid.sc_file);
if (file)
filp_close(file, (fl_owner_t)lo);
close_generic_stateid(stp);
@@ -2774,7 +2772,7 @@ static void init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp,
list_add(&stp->st_perstateowner, &oo->oo_owner.so_stateids);
stp->st_stateowner = &oo->oo_owner;
get_nfs4_file(fp);
- stp->st_file = fp;
+ stp->st_stid.sc_file = fp;
stp->st_access_bmap = 0;
stp->st_deny_bmap = 0;
set_access(open->op_share_access, stp);
@@ -3350,7 +3348,7 @@ nfs4_open_delegation(struct net *net, struct svc_fh *fh,
dp = alloc_init_deleg(oo->oo_owner.so_client, stp, fh);
if (dp == NULL)
goto out_no_deleg;
- status = nfs4_set_delegation(dp, stp->st_file);
+ status = nfs4_set_delegation(dp, stp->st_stid.sc_file);
if (status)
goto out_free;

@@ -3668,7 +3666,7 @@ laundromat_main(struct work_struct *laundry)

static inline __be32 nfs4_check_fh(struct svc_fh *fhp, struct nfs4_ol_stateid *stp)
{
- if (fhp->fh_dentry->d_inode != stp->st_file->fi_inode)
+ if (fhp->fh_dentry->d_inode != stp->st_stid.sc_file->fi_inode)
return nfserr_bad_stateid;
return nfs_ok;
}
@@ -3897,9 +3895,9 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,
goto out;
if (filpp) {
if (flags & RD_STATE)
- file = find_readable_file(stp->st_file);
+ file = find_readable_file(stp->st_stid.sc_file);
else
- file = find_writeable_file(stp->st_file);
+ file = find_writeable_file(stp->st_stid.sc_file);
}
break;
default:
@@ -3917,7 +3915,7 @@ out:
static __be32
nfsd4_free_lock_stateid(struct nfs4_ol_stateid *stp)
{
- if (check_for_locks(stp->st_file, lockowner(stp->st_stateowner)))
+ if (check_for_locks(stp->st_stid.sc_file, lockowner(stp->st_stateowner)))
return nfserr_locks_held;
release_lock_stateid(stp);
return nfs_ok;
@@ -4100,7 +4098,7 @@ static inline void nfs4_stateid_downgrade_bit(struct nfs4_ol_stateid *stp, u32 a
{
if (!test_access(access, stp))
return;
- nfs4_file_put_access(stp->st_file, nfs4_access_to_omode(access));
+ nfs4_file_put_access(stp->st_stid.sc_file, nfs4_access_to_omode(access));
clear_access(access, stp);
}

@@ -4401,7 +4399,7 @@ alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, struct
list_add(&stp->st_perstateowner, &lo->lo_owner.so_stateids);
stp->st_stateowner = &lo->lo_owner;
get_nfs4_file(fp);
- stp->st_file = fp;
+ stp->st_stid.sc_file = fp;
stp->st_access_bmap = 0;
stp->st_deny_bmap = open_stp->st_deny_bmap;
stp->st_openstp = open_stp;
@@ -4418,7 +4416,7 @@ find_lock_stateid(struct nfs4_lockowner *lo, struct inode *inode)
struct nfs4_ol_stateid *lst;

list_for_each_entry(lst, &lo->lo_owner.so_stateids, st_perstateowner) {
- if (lst->st_file->fi_inode == inode)
+ if (lst->st_stid.sc_file->fi_inode == inode)
return lst;
}
return NULL;
@@ -4434,7 +4432,7 @@ check_lock_length(u64 offset, u64 length)

static void get_lock_access(struct nfs4_ol_stateid *lock_stp, u32 access)
{
- struct nfs4_file *fp = lock_stp->st_file;
+ struct nfs4_file *fp = lock_stp->st_stid.sc_file;
int oflag = nfs4_access_to_omode(access);

if (test_access(access, lock_stp))
@@ -4445,7 +4443,7 @@ static void get_lock_access(struct nfs4_ol_stateid *lock_stp, u32 access)

static __be32 lookup_or_create_lock_state(struct nfsd4_compound_state *cstate, struct nfs4_ol_stateid *ost, struct nfsd4_lock *lock, struct nfs4_ol_stateid **lst, bool *new)
{
- struct nfs4_file *fi = ost->st_file;
+ struct nfs4_file *fi = ost->st_stid.sc_file;
struct nfs4_openowner *oo = openowner(ost->st_stateowner);
struct nfs4_client *cl = oo->oo_owner.so_client;
struct nfs4_lockowner *lo;
@@ -4568,14 +4566,14 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
switch (lock->lk_type) {
case NFS4_READ_LT:
case NFS4_READW_LT:
- filp = find_readable_file(lock_stp->st_file);
+ filp = find_readable_file(lock_stp->st_stid.sc_file);
if (filp)
get_lock_access(lock_stp, NFS4_SHARE_ACCESS_READ);
file_lock->fl_type = F_RDLCK;
break;
case NFS4_WRITE_LT:
case NFS4_WRITEW_LT:
- filp = find_writeable_file(lock_stp->st_file);
+ filp = find_writeable_file(lock_stp->st_stid.sc_file);
if (filp)
get_lock_access(lock_stp, NFS4_SHARE_ACCESS_WRITE);
file_lock->fl_type = F_WRLCK;
@@ -4758,7 +4756,7 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
&stp, nn);
if (status)
goto out;
- filp = find_any_file(stp->st_file);
+ filp = find_any_file(stp->st_stid.sc_file);
if (!filp) {
status = nfserr_lock_range;
goto out;
@@ -4862,7 +4860,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
list_for_each_entry(stp, &sop->so_stateids,
st_perstateowner) {
lo = lockowner(sop);
- if (check_for_locks(stp->st_file, lo))
+ if (check_for_locks(stp->st_stid.sc_file, lo))
goto out;
list_add(&lo->lo_list, &matches);
}
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index de198600616b..0539db97f7d3 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -408,7 +408,6 @@ struct nfs4_ol_stateid {
struct list_head st_perstateowner;
struct list_head st_locks;
struct nfs4_stateowner * st_stateowner;
- struct nfs4_file * st_file;
unsigned long st_access_bmap;
unsigned long st_deny_bmap;
struct nfs4_ol_stateid * st_openstp;
--
1.9.0


2014-04-18 18:46:04

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 37/70] NFSd: nfs4_preprocess_seqid_op should only set *stpp on success

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index b9d6da652fb1..03a3f51d2828 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4068,6 +4068,7 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
{
__be32 status;
struct nfs4_stid *s;
+ struct nfs4_ol_stateid *stp = NULL;

dprintk("NFSD: %s: seqid=%d stateid = " STATEID_FMT "\n", __func__,
seqid, STATEID_VAL(stateid));
@@ -4077,10 +4078,13 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
cstate->minorversion, nn);
if (status)
return status;
- *stpp = openlockstateid(s);
- nfsd4_cstate_assign_replay(cstate, (*stpp)->st_stateowner);
+ stp = openlockstateid(s);
+ nfsd4_cstate_assign_replay(cstate, stp->st_stateowner);

- return nfs4_seqid_op_checks(cstate, stateid, seqid, *stpp);
+ status = nfs4_seqid_op_checks(cstate, stateid, seqid, stp);
+ if (!status)
+ *stpp = stp;
+ return status;
}

static __be32 nfs4_preprocess_confirmed_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
--
1.9.0


2014-04-18 18:46:08

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 40/70] NFSd: Ensure that nfs4_open_delegation() references the delegation stateid

Ensure that nfs4_open_delegation() keeps a reference to the delegation
stateid until it is done working with it.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 2 ++
1 file changed, 2 insertions(+)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 822eed0de95d..d1e4f0e07f09 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -520,6 +520,7 @@ static void unhash_stid(struct nfs4_stid *s)
static void
hash_delegation_locked(struct nfs4_delegation *dp, struct nfs4_file *fp)
{
+ atomic_inc(&dp->dl_stid.sc_count);
dp->dl_stid.sc_type = NFS4_DELEG_STID;
spin_lock(&fp->fi_inode->i_lock);
list_add(&dp->dl_perfile, &fp->fi_delegations);
@@ -3395,6 +3396,7 @@ nfs4_open_delegation(struct net *net, struct svc_fh *fh,
dprintk("NFSD: delegation stateid=" STATEID_FMT "\n",
STATEID_VAL(&dp->dl_stid.sc_stateid));
open->op_delegate_type = NFS4_OPEN_DELEGATE_READ;
+ nfs4_put_delegation(dp);
return;
out_free:
nfs4_put_delegation(dp);
--
1.9.0


2014-04-21 13:08:14

by Trond Myklebust

[permalink] [raw]
Subject: Re: [PATCH 21/70] NFSd: Get rid of the lockowner_ino_hashtbl

On Sat, 2014-04-19 at 07:38 -0700, Christoph Hellwig wrote:
> On Fri, Apr 18, 2014 at 02:44:15PM -0400, Trond Myklebust wrote:
> > It is no longer used.
>
> I think this should be folded into the previous patch, which is the
> reason why it is unused now.
>

Ack and done...
--
Trond Myklebust
Linux NFS client maintainer, PrimaryData
[email protected]



2014-04-18 18:46:15

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 45/70] NFSd: Add reference counting to nfs4_preprocess_confirmed_seqid_op

Ensure that all the callers put the open stateid after use.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 517e08818df6..ce35edcef193 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4115,6 +4115,8 @@ static __be32 nfs4_preprocess_confirmed_seqid_op(struct nfsd4_compound_state *cs
NFS4_OPEN_STID, stpp, nn);
if (status)
return status;
+ /* FIXME: move into nfs4_preprocess_seqid_op */
+ atomic_inc(&(*stpp)->st_stid.sc_count);
oo = openowner((*stpp)->st_stateowner);
if (!(oo->oo_flags & NFS4_OO_CONFIRMED))
return nfserr_bad_stateid;
@@ -4228,12 +4230,12 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp,
if (!test_access(od->od_share_access, stp)) {
dprintk("NFSD: access not a subset current bitmap: 0x%lx, input access=%08x\n",
stp->st_access_bmap, od->od_share_access);
- goto out;
+ goto put_stateid;
}
if (!test_deny(od->od_share_deny, stp)) {
dprintk("NFSD:deny not a subset current bitmap: 0x%lx, input deny=%08x\n",
stp->st_deny_bmap, od->od_share_deny);
- goto out;
+ goto put_stateid;
}
nfs4_stateid_downgrade(stp, od->od_share_access);

@@ -4242,6 +4244,8 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp,
update_stateid(&stp->st_stid.sc_stateid);
memcpy(&od->od_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));
status = nfs_ok;
+put_stateid:
+ put_generic_stateid(stp);
out:
nfsd4_bump_seqid(cstate, status);
nfs4_unlock_state();
@@ -4562,6 +4566,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfs4_openowner *open_sop = NULL;
struct nfs4_lockowner *lock_sop = NULL;
struct nfs4_ol_stateid *lock_stp = NULL;
+ struct nfs4_ol_stateid *open_stp = NULL;
struct file *filp = NULL;
struct file_lock *file_lock = NULL;
struct file_lock *conflock = NULL;
@@ -4588,8 +4593,6 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
nfs4_lock_state();

if (lock->lk_is_new) {
- struct nfs4_ol_stateid *open_stp = NULL;
-
if (nfsd4_has_session(cstate))
/* See rfc 5661 18.10.3: given clientid is ignored: */
memcpy(&lock->v.new.clientid,
@@ -4712,6 +4715,8 @@ out:
fput(filp);
if (lock_stp)
put_generic_stateid(lock_stp);
+ if (open_stp)
+ put_generic_stateid(open_stp);
if (status && new_state)
release_lockowner_if_empty(lock_sop);
nfsd4_bump_seqid(cstate, status);
--
1.9.0


2014-04-18 18:45:50

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 29/70] NFSd: Add a struct nfs4_file field to struct nfs4_stid

All stateids are associated with a nfs4_file. Let's consolidate...

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 2 ++
fs/nfsd/state.h | 1 +
2 files changed, 3 insertions(+)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 32ab3f1c83f8..5bbef4720e7c 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -474,6 +474,8 @@ static void remove_stid(struct nfs4_stid *s)

static void nfs4_free_stid(struct kmem_cache *slab, struct nfs4_stid *s)
{
+ if (s->sc_file)
+ put_nfs4_file(s->sc_file);
kmem_cache_free(slab, s);
}

diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 9d0088c244a8..c6deef936693 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -85,6 +85,7 @@ struct nfs4_stid {
unsigned char sc_type;
stateid_t sc_stateid;
struct nfs4_client *sc_client;
+ struct nfs4_file *sc_file;
};

struct nfs4_delegation {
--
1.9.0


2014-04-18 18:45:27

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 14/70] NFSd: Add a mutex to protect the NFSv4.0 open owner replay cache

We don't want to rely on the state_lock() for protection in the
case of NFSv4 open owners. Instead, we add a mutex that will
only be taken for NFSv4.0 state mutating operations, and
that will be released once the entire compound is done.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4proc.c | 13 +++++--------
fs/nfsd/nfs4state.c | 21 ++++++++-------------
fs/nfsd/state.h | 1 +
fs/nfsd/xdr4.h | 19 +++++++++++++++++++
4 files changed, 33 insertions(+), 21 deletions(-)

diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 480323443efe..a23da87b384e 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -470,11 +470,11 @@ out:
kfree(resfh);
}
nfsd4_cleanup_open_state(open, status);
- if (open->op_openowner && !nfsd4_has_session(cstate))
- cstate->replay_owner = &open->op_openowner->oo_owner;
+ if (open->op_openowner)
+ nfsd4_cstate_assign_replay(cstate,
+ &open->op_openowner->oo_owner);
nfsd4_bump_seqid(cstate, status);
- if (!cstate->replay_owner)
- nfs4_unlock_state();
+ nfs4_unlock_state();
return status;
}

@@ -1400,10 +1400,7 @@ encode_op:
args->ops, args->opcnt, resp->opcnt, op->opnum,
be32_to_cpu(status));

- if (cstate->replay_owner) {
- nfs4_unlock_state();
- cstate->replay_owner = NULL;
- }
+ nfsd4_cstate_clear_replay(cstate);
/* XXX Ugh, we need to get rid of this kind of special case: */
if (op->opnum == OP_READ && op->u.read.rd_filp)
fput(op->u.read.rd_filp);
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 6827e8698767..ad7d21e06c36 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -779,7 +779,7 @@ void nfsd4_bump_seqid(struct nfsd4_compound_state *cstate, __be32 nfserr)
return;

if (!seqid_mutating_err(ntohl(nfserr))) {
- cstate->replay_owner = NULL;
+ nfsd4_cstate_clear_replay(cstate);
return;
}
if (!so)
@@ -2601,6 +2601,7 @@ static void init_nfs4_replay(struct nfs4_replay *rp)
rp->rp_status = nfserr_serverfault;
rp->rp_buflen = 0;
rp->rp_buf = rp->rp_ibuf;
+ mutex_init(&rp->rp_mutex);
}

static inline void *alloc_stateowner(struct kmem_cache *slab, struct xdr_netobj *owner, struct nfs4_client *clp)
@@ -3886,8 +3887,7 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
if (status)
return status;
*stpp = openlockstateid(s);
- if (!nfsd4_has_session(cstate))
- cstate->replay_owner = (*stpp)->st_stateowner;
+ nfsd4_cstate_assign_replay(cstate, (*stpp)->st_stateowner);

return nfs4_seqid_op_checks(cstate, stateid, seqid, *stpp);
}
@@ -3945,8 +3945,7 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
status = nfs_ok;
out:
nfsd4_bump_seqid(cstate, status);
- if (!cstate->replay_owner)
- nfs4_unlock_state();
+ nfs4_unlock_state();
return status;
}

@@ -4028,8 +4027,7 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp,
status = nfs_ok;
out:
nfsd4_bump_seqid(cstate, status);
- if (!cstate->replay_owner)
- nfs4_unlock_state();
+ nfs4_unlock_state();
return status;
}

@@ -4086,8 +4084,7 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
}
}
out:
- if (!cstate->replay_owner)
- nfs4_unlock_state();
+ nfs4_unlock_state();
return status;
}

@@ -4495,8 +4492,7 @@ out:
if (status && new_state)
release_lockowner(lock_sop);
nfsd4_bump_seqid(cstate, status);
- if (!cstate->replay_owner)
- nfs4_unlock_state();
+ nfs4_unlock_state();
if (file_lock)
locks_free_lock(file_lock);
if (conflock)
@@ -4659,8 +4655,7 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,

out:
nfsd4_bump_seqid(cstate, status);
- if (!cstate->replay_owner)
- nfs4_unlock_state();
+ nfs4_unlock_state();
if (file_lock)
locks_free_lock(file_lock);
return status;
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 83d9721d9644..80789aa5b5b4 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -330,6 +330,7 @@ struct nfs4_replay {
unsigned int rp_buflen;
char *rp_buf;
struct knfsd_fh rp_openfh;
+ struct mutex rp_mutex;
char rp_ibuf[NFSD4_REPLAY_ISIZE];
};

diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index 5ea7df305083..2f887c944048 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -73,6 +73,25 @@ static inline bool nfsd4_has_session(struct nfsd4_compound_state *cs)
return cs->slot != NULL;
}

+static inline void nfsd4_cstate_assign_replay(struct nfsd4_compound_state *cstate,
+ struct nfs4_stateowner *so)
+{
+ if (!nfsd4_has_session(cstate)) {
+ mutex_lock(&so->so_replay.rp_mutex);
+ cstate->replay_owner = so;
+ }
+}
+
+static inline void nfsd4_cstate_clear_replay(struct nfsd4_compound_state *cstate)
+{
+ struct nfs4_stateowner *so = cstate->replay_owner;
+
+ if (so != NULL) {
+ cstate->replay_owner = NULL;
+ mutex_unlock(&so->so_replay.rp_mutex);
+ }
+}
+
struct nfsd4_change_info {
u32 atomic;
bool change_supported;
--
1.9.0


2014-04-18 18:46:07

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 39/70] NFSd: nfsd4_locku() must reference the lock stateid

Ensure that nfsd4_locku() keeps a reference to the lock stateid
until it is done working with it.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index d69d96a7d299..822eed0de95d 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4815,10 +4815,12 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
&stp, nn);
if (status)
goto out;
+ /* FIXME: move into nfs4_preprocess_seqid_op */
+ atomic_inc(&stp->st_stid.sc_count);
filp = find_any_file(stp->st_stid.sc_file);
if (!filp) {
status = nfserr_lock_range;
- goto out;
+ goto put_stateid;
}
file_lock = locks_alloc_lock();
if (!file_lock) {
@@ -4848,6 +4850,8 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
memcpy(&locku->lu_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));
fput:
fput(filp);
+put_stateid:
+ put_generic_stateid(stp);
out:
nfsd4_bump_seqid(cstate, status);
nfs4_unlock_state();
--
1.9.0


2014-04-18 18:46:30

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 54/70] NFSd: Ensure that the laundromat unhashes the client before releasing locks

If we leave the client on the confirmed/unconfirmed tables, and leave
the sessions visible on the sessionid_hashtbl, then someone might
find them before we've had a chance to destroy them.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 19f7739a156e..55a99ebaf6c9 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -3688,13 +3688,15 @@ nfs4_laundromat(struct nfsd_net *nn)
/* Hey, I'm busy with this guy! */
if (atomic_read(&clp->cl_refcount) != 0)
continue;
- list_move(&clp->cl_lru, &reaplist);
+ unhash_client_locked(clp);
+ list_add(&clp->cl_lru, &reaplist);
}
spin_unlock(&nn->client_lock);
list_for_each_safe(pos, next, &reaplist) {
clp = list_entry(pos, struct nfs4_client, cl_lru);
dprintk("NFSD: purging unused client (clientid %08x)\n",
clp->cl_clientid.cl_id);
+ list_del_init(&clp->cl_lru);
expire_client(clp);
}
spin_lock(&state_lock);
--
1.9.0


2014-04-18 18:46:44

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 60/70] NFSd: Ensure lookup_clientid() takes client_lock

Ensure that the client lookup is done safely under the client_lock.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 35 +++++++++++++++++++++++++++++------
1 file changed, 29 insertions(+), 6 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 6a0c201c670f..ad79dca31cc4 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -178,6 +178,15 @@ static void put_client_renew_locked(struct nfs4_client *clp)
renew_client_locked(clp);
}

+static void put_client_renew(struct nfs4_client *clp)
+{
+ struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
+
+ spin_lock(&nn->client_lock);
+ put_client_renew_locked(clp);
+ spin_unlock(&nn->client_lock);
+}
+
static __be32 nfsd4_get_session_locked(struct nfsd4_session *ses)
{
__be32 status;
@@ -3614,18 +3623,27 @@ static __be32 lookup_clientid(clientid_t *clid, struct nfsd4_session *session, s
{
struct nfs4_client *found;

+ spin_lock(&nn->client_lock);
if (session != NULL) {
found = session->se_client;
if (!same_clid(&found->cl_clientid, clid))
- return nfserr_stale_clientid;
+ goto out_stale;
} else {
if (STALE_CLIENTID(clid, nn))
- return nfserr_stale_clientid;
+ goto out_stale;
found = find_confirmed_client(clid, false, nn);
}
- if (clp)
- *clp = found;
+ if (clp) {
+ if (get_client_locked(found) == nfs_ok)
+ *clp = found;
+ else
+ found = NULL;
+ }
+ spin_unlock(&nn->client_lock);
return found ? nfs_ok : nfserr_expired;
+out_stale:
+ spin_unlock(&nn->client_lock);
+ return nfserr_stale_clientid;
}

__be32
@@ -3645,8 +3663,10 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
status = nfserr_cb_path_down;
if (!list_empty(&clp->cl_delegations)
&& clp->cl_cb_state != NFSD4_CB_UP)
- goto out;
+ goto put_client;
status = nfs_ok;
+put_client:
+ put_client_renew(clp);
out:
nfs4_unlock_state();
return status;
@@ -3939,6 +3959,7 @@ static __be32 nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate,
if (status)
return status;
*s = find_stateid_by_type(cl, stateid, typemask);
+ put_client_renew(cl);
if (!*s)
return nfserr_bad_stateid;
return nfs_ok;
@@ -5119,7 +5140,9 @@ nfs4_check_open_reclaim(clientid_t *clid, struct nfsd4_session *session, struct
if (status)
return nfserr_reclaim_bad;

- return nfsd4_client_record_check(clp) ? nfserr_reclaim_bad : nfs_ok;
+ status = nfsd4_client_record_check(clp) ? nfserr_reclaim_bad : nfs_ok;
+ put_client_renew(clp);
+ return status;
}

#ifdef CONFIG_NFSD_FAULT_INJECTION
--
1.9.0


2014-04-21 13:13:44

by Trond Myklebust

[permalink] [raw]
Subject: Re: [PATCH 30/70] NFSd: Replace delegation->dl_file with the dl_stid.sc_file

On Sat, 2014-04-19 at 07:40 -0700, Christoph Hellwig wrote:
> Instead of adding a mostly unused file field to the stid in the previous
> patch and starting to use it here I'd recommend merging the two patches.
>
Ack and done.

--
Trond Myklebust
Linux NFS client maintainer, PrimaryData
[email protected]



2014-04-18 18:46:35

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 55/70] NFSd: Don't require client_lock in free_client

The struct nfs_client is supposed to be invisible and unreferenced
before it gets here.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 10 ----------
1 file changed, 10 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 55a99ebaf6c9..b2406f20b529 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1252,9 +1252,6 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name)
static void
free_client(struct nfs4_client *clp)
{
- struct nfsd_net __maybe_unused *nn = net_generic(clp->net, nfsd_net_id);
-
- lockdep_assert_held(&nn->client_lock);
while (!list_empty(&clp->cl_sessions)) {
struct nfsd4_session *ses;
ses = list_entry(clp->cl_sessions.next, struct nfsd4_session,
@@ -1312,7 +1309,6 @@ __destroy_client(struct nfs4_client *clp)
struct nfs4_openowner *oo;
struct nfs4_delegation *dp;
struct list_head reaplist;
- struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);

INIT_LIST_HEAD(&reaplist);
spin_lock(&state_lock);
@@ -1339,10 +1335,7 @@ __destroy_client(struct nfs4_client *clp)
nfsd4_shutdown_callback(clp);
if (clp->cl_cb_conn.cb_xprt)
svc_xprt_put(clp->cl_cb_conn.cb_xprt);
- spin_lock(&nn->client_lock);
- WARN_ON_ONCE(atomic_read(&clp->cl_refcount));
free_client(clp);
- spin_unlock(&nn->client_lock);
}

static void
@@ -1569,7 +1562,6 @@ static struct nfs4_client *create_client(struct xdr_netobj name,
struct sockaddr *sa = svc_addr(rqstp);
int ret;
struct net *net = SVC_NET(rqstp);
- struct nfsd_net *nn = net_generic(net, nfsd_net_id);

clp = alloc_client(name);
if (clp == NULL)
@@ -1577,9 +1569,7 @@ static struct nfs4_client *create_client(struct xdr_netobj name,

ret = copy_cred(&clp->cl_cred, &rqstp->rq_cred);
if (ret) {
- spin_lock(&nn->client_lock);
free_client(clp);
- spin_unlock(&nn->client_lock);
return NULL;
}
nfsd4_init_callback(&clp->cl_cb_null);
--
1.9.0


2014-04-18 18:46:06

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 38/70] NFSd: Add reference counting to lock stateids

Ensure that nfsd4_lock() references the lock stateid while it is
manipulating it.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 03a3f51d2828..d69d96a7d299 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4445,6 +4445,7 @@ alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, struct
stp = nfs4_alloc_stateid(clp);
if (stp == NULL)
return NULL;
+ atomic_inc(&stp->st_stid.sc_count);
stp->st_stid.sc_type = NFS4_LOCK_STID;
list_add(&stp->st_perstateowner, &lo->lo_owner.so_stateids);
stp->st_stateowner = &lo->lo_owner;
@@ -4466,8 +4467,10 @@ find_lock_stateid(struct nfs4_lockowner *lo, struct inode *inode)
struct nfs4_ol_stateid *lst;

list_for_each_entry(lst, &lo->lo_owner.so_stateids, st_perstateowner) {
- if (lst->st_stid.sc_file->fi_inode == inode)
+ if (lst->st_stid.sc_file->fi_inode == inode) {
+ atomic_inc(&lst->st_stid.sc_count);
return lst;
+ }
}
return NULL;
}
@@ -4531,7 +4534,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
{
struct nfs4_openowner *open_sop = NULL;
struct nfs4_lockowner *lock_sop = NULL;
- struct nfs4_ol_stateid *lock_stp;
+ struct nfs4_ol_stateid *lock_stp = NULL;
struct file *filp = NULL;
struct file_lock *file_lock = NULL;
struct file_lock *conflock = NULL;
@@ -4584,11 +4587,15 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
goto out;
status = lookup_or_create_lock_state(cstate, open_stp, lock,
&lock_stp, &new_state);
- } else
+ } else {
status = nfs4_preprocess_seqid_op(cstate,
lock->lk_old_lock_seqid,
&lock->lk_old_lock_stateid,
NFS4_LOCK_STID, &lock_stp, nn);
+ /* FIXME: move into nfs4_preprocess_seqid_op */
+ if (!status)
+ atomic_inc(&lock_stp->st_stid.sc_count);
+ }
if (status)
goto out;
lock_sop = lockowner(lock_stp->st_stateowner);
@@ -4676,6 +4683,8 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
out:
if (filp)
fput(filp);
+ if (lock_stp)
+ put_generic_stateid(lock_stp);
if (status && new_state)
release_lockowner_if_empty(lock_sop);
nfsd4_bump_seqid(cstate, status);
--
1.9.0


2014-04-18 18:45:42

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 23/70] NFSd: Don't get a session reference without a client reference

If the client were to disappear from underneath us while we're holding
a session reference, things would be bad. This cleanup ensures that
it cannot.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 112 +++++++++++++++++++++++++++++++---------------------
fs/nfsd/state.h | 2 -
2 files changed, 68 insertions(+), 46 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 9421e56454a2..ab66f73643a9 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -102,12 +102,6 @@ static bool is_session_dead(struct nfsd4_session *ses)
return ses->se_flags & NFS4_SESSION_DEAD;
}

-void nfsd4_put_session(struct nfsd4_session *ses)
-{
- if (atomic_dec_and_test(&ses->se_ref) && is_session_dead(ses))
- free_session(ses);
-}
-
static __be32 mark_session_dead_locked(struct nfsd4_session *ses, int ref_held_by_me)
{
if (atomic_read(&ses->se_ref) > ref_held_by_me)
@@ -116,14 +110,6 @@ static __be32 mark_session_dead_locked(struct nfsd4_session *ses, int ref_held_b
return nfs_ok;
}

-static __be32 nfsd4_get_session_locked(struct nfsd4_session *ses)
-{
- if (is_session_dead(ses))
- return nfserr_badsession;
- atomic_inc(&ses->se_ref);
- return nfs_ok;
-}
-
void
nfs4_unlock_state(void)
{
@@ -202,6 +188,39 @@ static void put_client_renew_locked(struct nfs4_client *clp)
renew_client_locked(clp);
}

+static __be32 nfsd4_get_session_locked(struct nfsd4_session *ses)
+{
+ __be32 status;
+
+ if (is_session_dead(ses))
+ return nfserr_badsession;
+ status = get_client_locked(ses->se_client);
+ if (status)
+ return status;
+ atomic_inc(&ses->se_ref);
+ return nfs_ok;
+}
+
+static void nfsd4_put_session_locked(struct nfsd4_session *ses)
+{
+ struct nfs4_client *clp = ses->se_client;
+
+ if (atomic_dec_and_test(&ses->se_ref) && is_session_dead(ses))
+ free_session(ses);
+ put_client_renew_locked(clp);
+}
+
+static void nfsd4_put_session(struct nfsd4_session *ses)
+{
+ struct nfs4_client *clp = ses->se_client;
+ struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
+
+ spin_lock(&nn->client_lock);
+ nfsd4_put_session_locked(ses);
+ spin_unlock(&nn->client_lock);
+}
+
+
static inline u32
opaque_hashval(const void *ptr, int nbytes)
{
@@ -1119,7 +1138,7 @@ static void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, stru

/* caller must hold client_lock */
static struct nfsd4_session *
-find_in_sessionid_hashtbl(struct nfs4_sessionid *sessionid, struct net *net)
+__find_in_sessionid_hashtbl(struct nfs4_sessionid *sessionid, struct net *net)
{
struct nfsd4_session *elem;
int idx;
@@ -1139,6 +1158,24 @@ find_in_sessionid_hashtbl(struct nfs4_sessionid *sessionid, struct net *net)
return NULL;
}

+static struct nfsd4_session *
+find_in_sessionid_hashtbl(struct nfs4_sessionid *sessionid, struct net *net,
+ __be32 *ret)
+{
+ struct nfsd4_session *session;
+ __be32 status = nfserr_badsession;
+
+ session = __find_in_sessionid_hashtbl(sessionid, net);
+ if (!session)
+ goto out;
+ status = nfsd4_get_session_locked(session);
+ if (status)
+ session = NULL;
+out:
+ *ret = status;
+ return session;
+}
+
/* caller must hold client_lock */
static void
unhash_session(struct nfsd4_session *ses)
@@ -2152,17 +2189,17 @@ __be32 nfsd4_bind_conn_to_session(struct svc_rqst *rqstp,
__be32 status;
struct nfsd4_conn *conn;
struct nfsd4_session *session;
- struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
+ struct net *net = SVC_NET(rqstp);
+ struct nfsd_net *nn = net_generic(net, nfsd_net_id);

if (!nfsd4_last_compound_op(rqstp))
return nfserr_not_only_op;
nfs4_lock_state();
spin_lock(&nn->client_lock);
- session = find_in_sessionid_hashtbl(&bcts->sessionid, SVC_NET(rqstp));
+ session = find_in_sessionid_hashtbl(&bcts->sessionid, net, &status);
spin_unlock(&nn->client_lock);
- status = nfserr_badsession;
if (!session)
- goto out;
+ goto out_no_session;
status = nfserr_wrong_cred;
if (!mach_creds_match(session->se_client, rqstp))
goto out;
@@ -2176,6 +2213,8 @@ __be32 nfsd4_bind_conn_to_session(struct svc_rqst *rqstp,
nfsd4_init_conn(rqstp, conn, session);
status = nfs_ok;
out:
+ nfsd4_put_session(session);
+out_no_session:
nfs4_unlock_state();
return status;
}
@@ -2195,7 +2234,8 @@ nfsd4_destroy_session(struct svc_rqst *r,
struct nfsd4_session *ses;
__be32 status;
int ref_held_by_me = 0;
- struct nfsd_net *nn = net_generic(SVC_NET(r), nfsd_net_id);
+ struct net *net = SVC_NET(r);
+ struct nfsd_net *nn = net_generic(net, nfsd_net_id);

nfs4_lock_state();
status = nfserr_not_only_op;
@@ -2206,14 +2246,12 @@ nfsd4_destroy_session(struct svc_rqst *r,
}
dump_sessionid(__func__, &sessionid->sessionid);
spin_lock(&nn->client_lock);
- ses = find_in_sessionid_hashtbl(&sessionid->sessionid, SVC_NET(r));
- status = nfserr_badsession;
+ ses = find_in_sessionid_hashtbl(&sessionid->sessionid, net, &status);
if (!ses)
goto out_client_lock;
status = nfserr_wrong_cred;
if (!mach_creds_match(ses->se_client, r))
- goto out_client_lock;
- nfsd4_get_session_locked(ses);
+ goto out_put_session;
status = mark_session_dead_locked(ses, 1 + ref_held_by_me);
if (status)
goto out_put_session;
@@ -2225,7 +2263,7 @@ nfsd4_destroy_session(struct svc_rqst *r,
spin_lock(&nn->client_lock);
status = nfs_ok;
out_put_session:
- nfsd4_put_session(ses);
+ nfsd4_put_session_locked(ses);
out_client_lock:
spin_unlock(&nn->client_lock);
out:
@@ -2298,7 +2336,8 @@ nfsd4_sequence(struct svc_rqst *rqstp,
struct nfsd4_slot *slot;
struct nfsd4_conn *conn;
__be32 status;
- struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
+ struct net *net = SVC_NET(rqstp);
+ struct nfsd_net *nn = net_generic(net, nfsd_net_id);

if (resp->opcnt != 1)
return nfserr_sequence_pos;
@@ -2312,17 +2351,10 @@ nfsd4_sequence(struct svc_rqst *rqstp,
return nfserr_jukebox;

spin_lock(&nn->client_lock);
- status = nfserr_badsession;
- session = find_in_sessionid_hashtbl(&seq->sessionid, SVC_NET(rqstp));
+ session = find_in_sessionid_hashtbl(&seq->sessionid, net, &status);
if (!session)
goto out_no_session;
clp = session->se_client;
- status = get_client_locked(clp);
- if (status)
- goto out_no_session;
- status = nfsd4_get_session_locked(session);
- if (status)
- goto out_put_client;

status = nfserr_too_many_ops;
if (nfsd4_session_too_many_ops(rqstp, session))
@@ -2396,9 +2428,7 @@ out_no_session:
spin_unlock(&nn->client_lock);
return status;
out_put_session:
- nfsd4_put_session(session);
-out_put_client:
- put_client_renew_locked(clp);
+ nfsd4_put_session_locked(session);
goto out_no_session;
}

@@ -2408,18 +2438,12 @@ nfsd4_sequence_done(struct nfsd4_compoundres *resp)
struct nfsd4_compound_state *cs = &resp->cstate;

if (nfsd4_has_session(cs)) {
- struct nfs4_client *clp = cs->session->se_client;
- struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
-
if (cs->status != nfserr_replay_cache) {
nfsd4_store_cache_entry(resp);
cs->slot->sl_flags &= ~NFSD4_SLOT_INUSE;
}
- /* Renew the clientid on success and on replay */
- spin_lock(&nn->client_lock);
+ /* Drop session reference that was taken in nfsd4_sequence() */
nfsd4_put_session(cs->session);
- put_client_renew_locked(clp);
- spin_unlock(&nn->client_lock);
}
}

diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index e5444e0aedad..559134675b5a 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -213,8 +213,6 @@ struct nfsd4_session {
struct nfsd4_slot *se_slots[]; /* forward channel slots */
};

-extern void nfsd4_put_session(struct nfsd4_session *ses);
-
/* formatted contents of nfs4_sessionid */
struct nfsd4_sessionid {
clientid_t clientid;
--
1.9.0


2014-04-18 18:45:10

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 03/70] NFSd: call rpc_destroy_wait_queue() from free_client()

Mainly to ensure that we don't leave any hanging timers.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 1 +
1 file changed, 1 insertion(+)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 230d21cb1717..32b699bebb9c 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1107,6 +1107,7 @@ free_client(struct nfs4_client *clp)
WARN_ON_ONCE(atomic_read(&ses->se_ref));
free_session(ses);
}
+ rpc_destroy_wait_queue(&clp->cl_cb_waitq);
free_svc_cred(&clp->cl_cred);
kfree(clp->cl_name.data);
idr_destroy(&clp->cl_stateids);
--
1.9.0


2014-04-21 13:14:13

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH 15/70] NFSd: Add locking to the nfs4_file->fi_fds[] array

On Mon, Apr 21, 2014 at 09:01:37AM -0400, Trond Myklebust wrote:
> > > +static struct file *find_readable_file(struct nfs4_file *f)
> > > +{
> > > + struct file *ret;
> > > +
> > > + spin_lock(&f->fi_lock);
> > > + ret = __nfs4_get_fd(f, O_RDONLY);
> > > + if (!ret)
> > > + ret = __nfs4_get_fd(f, O_RDWR);
> > > + spin_unlock(&f->fi_lock);
> > > + return ret;
> >
> > Seems like these two functions could be easily consolidated by passing
> > a single flags argument.
>
> Yes, but we'd have to invent a new set of flags for just this function
> and so I'm not sure that the end result would be more readable.
> We can't reuse O_RDONLY/O_WRONLY/O_RDWR since they can't be bitwise ORed
> to produce the 'read or read/write', 'write or read/write' combinations
> that we need.

One option would be:

static struct file *__nfs4_get_fd(struct nfs4_file *f, int oflag,
int type)
{
if ((oflag & type) && f->fi_fds[type])
return get_file(f->fi_fds[type]);
return NULL;
}

struct file *nfsd4_find_file(struct nfs4_file *f, unsigned int oflag)
{
struct file *ret;

BUG_ON(oflag & ~(O_RDONLY|O_WRONLY|O_RDWR);

spin_lock(&f->fi_lock);
ret = __nfs4_get_fd(f, oflag, O_RDONLY);
if (!ret) {
ret = __nfs4_get_fd(f, oflag, O_WRONLY);
if (!ret)
ret = __nfs4_get_fd(f, oflag, O_RDWR);
}
spin_unlock(&f->fi_lock);
return ret;
}

2014-04-18 18:46:16

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 46/70] NFSd: Migrate the stateid reference into nfs4_preprocess_seqid_op

Allow nfs4_preprocess_seqid_op to take the stateid reference, instead
of having all the callers do so.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 26 +++++++++++---------------
1 file changed, 11 insertions(+), 15 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index ce35edcef193..8e2e519f9b94 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4100,8 +4100,11 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
nfsd4_cstate_assign_replay(cstate, stp->st_stateowner);

status = nfs4_seqid_op_checks(cstate, stateid, seqid, stp);
- if (!status)
+ if (!status) {
+ /* FIXME: move into find_stateid_by_type */
+ atomic_inc(&stp->st_stid.sc_count);
*stpp = stp;
+ }
return status;
}

@@ -4110,16 +4113,18 @@ static __be32 nfs4_preprocess_confirmed_seqid_op(struct nfsd4_compound_state *cs
{
__be32 status;
struct nfs4_openowner *oo;
+ struct nfs4_ol_stateid *stp;

status = nfs4_preprocess_seqid_op(cstate, seqid, stateid,
- NFS4_OPEN_STID, stpp, nn);
+ NFS4_OPEN_STID, &stp, nn);
if (status)
return status;
- /* FIXME: move into nfs4_preprocess_seqid_op */
- atomic_inc(&(*stpp)->st_stid.sc_count);
- oo = openowner((*stpp)->st_stateowner);
- if (!(oo->oo_flags & NFS4_OO_CONFIRMED))
+ oo = openowner(stp->st_stateowner);
+ if (!(oo->oo_flags & NFS4_OO_CONFIRMED)) {
+ put_generic_stateid(stp);
return nfserr_bad_stateid;
+ }
+ *stpp = stp;
return nfs_ok;
}

@@ -4146,8 +4151,6 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
NFS4_OPEN_STID, &stp, nn);
if (status)
goto out;
- /* FIXME: move into nfs4_preprocess_seqid_op */
- atomic_inc(&stp->st_stid.sc_count);
oo = openowner(stp->st_stateowner);
status = nfserr_bad_stateid;
if (oo->oo_flags & NFS4_OO_CONFIRMED)
@@ -4282,8 +4285,6 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
nfsd4_bump_seqid(cstate, status);
if (status)
goto out;
- /* FIXME: move into nfs4_preprocess_seqid_op */
- atomic_inc(&stp->st_stid.sc_count);
oo = openowner(stp->st_stateowner);
update_stateid(&stp->st_stid.sc_stateid);
memcpy(&close->cl_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));
@@ -4622,9 +4623,6 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
lock->lk_old_lock_seqid,
&lock->lk_old_lock_stateid,
NFS4_LOCK_STID, &lock_stp, nn);
- /* FIXME: move into nfs4_preprocess_seqid_op */
- if (!status)
- atomic_inc(&lock_stp->st_stid.sc_count);
}
if (status)
goto out;
@@ -4847,8 +4845,6 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
&stp, nn);
if (status)
goto out;
- /* FIXME: move into nfs4_preprocess_seqid_op */
- atomic_inc(&stp->st_stid.sc_count);
filp = find_any_file(stp->st_stid.sc_file);
if (!filp) {
status = nfserr_lock_range;
--
1.9.0


2014-04-18 18:45:20

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 09/70] NFSd: Mark nfs4_free_lockowner and nfs4_free_openowner as static functions

They do not need to be used outside fs/nfsd/nfs4state.c

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 24 ++++++++++++------------
fs/nfsd/state.h | 2 --
2 files changed, 12 insertions(+), 14 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 45c4b692e30d..6d691aa2bb27 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -659,6 +659,12 @@ static void unhash_lockowner(struct nfs4_lockowner *lo)
}
}

+static void nfs4_free_lockowner(struct nfs4_lockowner *lo)
+{
+ kfree(lo->lo_owner.so_owner.data);
+ kmem_cache_free(lockowner_slab, lo);
+}
+
static void release_lockowner(struct nfs4_lockowner *lo)
{
unhash_lockowner(lo);
@@ -713,6 +719,12 @@ static void release_last_closed_stateid(struct nfs4_openowner *oo)
}
}

+static void nfs4_free_openowner(struct nfs4_openowner *oo)
+{
+ kfree(oo->oo_owner.so_owner.data);
+ kmem_cache_free(openowner_slab, oo);
+}
+
static void release_openowner(struct nfs4_openowner *oo)
{
unhash_openowner(oo);
@@ -2571,18 +2583,6 @@ out_nomem:
return -ENOMEM;
}

-void nfs4_free_openowner(struct nfs4_openowner *oo)
-{
- kfree(oo->oo_owner.so_owner.data);
- kmem_cache_free(openowner_slab, oo);
-}
-
-void nfs4_free_lockowner(struct nfs4_lockowner *lo)
-{
- kfree(lo->lo_owner.so_owner.data);
- kmem_cache_free(lockowner_slab, lo);
-}
-
static void init_nfs4_replay(struct nfs4_replay *rp)
{
rp->rp_status = nfserr_serverfault;
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 424d8f5f2317..1aa22f39fc65 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -464,8 +464,6 @@ extern void nfs4_release_reclaim(struct nfsd_net *);
extern struct nfs4_client_reclaim *nfsd4_find_reclaim_client(const char *recdir,
struct nfsd_net *nn);
extern __be32 nfs4_check_open_reclaim(clientid_t *clid, bool sessions, struct nfsd_net *nn);
-extern void nfs4_free_openowner(struct nfs4_openowner *);
-extern void nfs4_free_lockowner(struct nfs4_lockowner *);
extern int set_callback_cred(void);
extern void nfsd4_init_callback(struct nfsd4_callback *);
extern void nfsd4_probe_callback(struct nfs4_client *clp);
--
1.9.0


2014-04-18 18:45:42

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 24/70] NFSd: Move the delegation reference counter into the struct nfs4_stid

We will want to add reference counting to the lock stateid and open
stateids too.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 6 +++---
fs/nfsd/state.h | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index ab66f73643a9..afb767319f66 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -411,6 +411,7 @@ kmem_cache *slab)
stid->sc_stateid.si_opaque.so_clid = cl->cl_clientid;
/* Will be incremented before return to client: */
stid->sc_stateid.si_generation = 0;
+ atomic_set(&stid->sc_count, 1);

/*
* It shouldn't be a problem to reuse an opaque stateid value.
@@ -457,7 +458,6 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv
dp->dl_type = NFS4_OPEN_DELEGATE_READ;
fh_copy_shallow(&dp->dl_fh, &current_fh->fh_handle);
dp->dl_time = 0;
- atomic_set(&dp->dl_count, 1);
nfsd4_init_callback(&dp->dl_recall);
return dp;
}
@@ -480,7 +480,7 @@ static void nfs4_free_stid(struct kmem_cache *slab, struct nfs4_stid *s)
void
nfs4_put_delegation(struct nfs4_delegation *dp)
{
- if (atomic_dec_and_test(&dp->dl_count)) {
+ if (atomic_dec_and_test(&dp->dl_stid.sc_count)) {
nfs4_free_stid(deleg_slab, &dp->dl_stid);
num_delegations--;
}
@@ -2892,7 +2892,7 @@ static void nfsd_break_one_deleg(struct nfs4_delegation *dp)
* callback (and since the lease code is serialized by the kernel
* lock) we know the server hasn't removed the lease yet, we know
* it's safe to take a reference: */
- atomic_inc(&dp->dl_count);
+ atomic_inc(&dp->dl_stid.sc_count);
nfsd4_cb_recall(dp);
}

diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 559134675b5a..9d0088c244a8 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -74,6 +74,7 @@ struct nfsd4_callback {
};

struct nfs4_stid {
+ atomic_t sc_count;
#define NFS4_OPEN_STID 1
#define NFS4_LOCK_STID 2
#define NFS4_DELEG_STID 4
@@ -91,7 +92,6 @@ struct nfs4_delegation {
struct list_head dl_perfile;
struct list_head dl_perclnt;
struct list_head dl_recall_lru; /* delegation recalled */
- atomic_t dl_count; /* ref count */
struct nfs4_file *dl_file;
u32 dl_type;
time_t dl_time;
--
1.9.0


2014-04-21 15:37:47

by Trond Myklebust

[permalink] [raw]
Subject: Re: [PATCH 36/70] NFSd: Add reference counting to find_stateid

On Sat, 2014-04-19 at 07:50 -0700, Christoph Hellwig wrote:
> > +static void nfs4_put_stateid(struct nfs4_stid *s)
> > +{
> > + if (s == NULL)
> > + return;
> > + switch (s->sc_type) {
> > + case NFS4_OPEN_STID:
> > + case NFS4_LOCK_STID:
> > + case NFS4_CLOSED_STID:
> > + put_generic_stateid(openlockstateid(s));
> > + break;
> > + case NFS4_DELEG_STID:
> > + case NFS4_REVOKED_DELEG_STID:
> > + case NFS4_CLOSED_DELEG_STID:
> > + nfs4_put_delegation(delegstateid(s));
> > + }
> > +}
>
> I really don't like the way the inheritance for the stateids works,
> a pure put operation shouldn't need this. I think all this can be
> fixed by adding a ->free function pointer to struct nfs4_stid. At this
> point the braindamage of passing a kmem_cache pointer to various
> function can be removed (similar to how nfs4_alloc_stid should be
> replaced with a nfs4_init_stid that takes an already allocated stid),
> and nothing in the normal refcounting path should need these switches.

Ack. I've added a free() pointer to nfs4_stid.

> > @@ -3804,26 +3823,33 @@ static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid)
> > return nfserr_bad_stateid;
> > status = check_stateid_generation(stateid, &s->sc_stateid, 1);
> > if (status)
> > - return status;
> > + goto out_put_stid;
> > switch (s->sc_type) {
> > case NFS4_DELEG_STID:
> > - return nfs_ok;
> > + status = nfs_ok;
> > + break;
> > case NFS4_REVOKED_DELEG_STID:
> > - return nfserr_deleg_revoked;
> > + status = nfserr_deleg_revoked;
> > + break;
> > case NFS4_OPEN_STID:
> > case NFS4_LOCK_STID:
> > ols = openlockstateid(s);
> > if (ols->st_stateowner->so_is_open_owner
> > && !(openowner(ols->st_stateowner)->oo_flags
> > & NFS4_OO_CONFIRMED))
> > - return nfserr_bad_stateid;
> > - return nfs_ok;
> > + status = nfserr_bad_stateid;
> > + else
> > + status = nfs_ok;
> > + break;
>
> Not quite as urgent as for the refcounting, but I think moving more
> of these switches on the type into proper methods would improve the
> stateid code a lot.

Agreed, but I'm leaving that as an exercise for the reader (at least as
far as this patch series is concerned).

--
Trond Myklebust
Linux NFS client maintainer, PrimaryData
[email protected]



2014-04-18 18:46:57

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 69/70] NFSd: Remove nfs4_lock_state(): setclientid, setclientid_confirm, renew

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 6 ------
1 file changed, 6 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 3a0a3e8c9982..36ccc79702dd 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -2607,7 +2607,6 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (new == NULL)
return nfserr_jukebox;
/* Cases below refer to rfc 3530 section 14.2.33: */
- nfs4_lock_state();
spin_lock(&nn->client_lock);
conf = find_confirmed_client_by_name(&clname, nn);
if (conf) {
@@ -2642,7 +2641,6 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
status = nfs_ok;
out:
spin_unlock(&nn->client_lock);
- nfs4_unlock_state();
if (new)
free_client(new);
if (unconf)
@@ -2665,7 +2663,6 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,

if (STALE_CLIENTID(clid, nn))
return nfserr_stale_clientid;
- nfs4_lock_state();

spin_lock(&nn->client_lock);
conf = find_confirmed_client(clid, false, nn);
@@ -2714,7 +2711,6 @@ out:
spin_unlock(&nn->client_lock);
if (old)
expire_client(old);
- nfs4_unlock_state();
return status;
}

@@ -3640,7 +3636,6 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
__be32 status;
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);

- nfs4_lock_state();
dprintk("process_renew(%08x/%08x): starting\n",
clid->cl_boot, clid->cl_id);
status = lookup_clientid(clid, NULL, nn, &clp);
@@ -3654,7 +3649,6 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
put_client:
put_client_renew(clp);
out:
- nfs4_unlock_state();
return status;
}

--
1.9.0


2014-04-19 15:51:15

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH 34/70] NFSd: Fix atomicity of delegation counter

On Fri, Apr 18, 2014 at 02:44:28PM -0400, Trond Myklebust wrote:
> Signed-off-by: Trond Myklebust <[email protected]>

I think at this point nfs4_lock_state() is always held so it's not
quite a fix yet. If that's not true the patch should be moved earlier
in the series.

> -static int num_delegations;
> +static atomic_long_t num_delegations;

Why the switch from a int to an (atomic) long here? If that was
intentional it should be documented in the patch description.

> - if (num_delegations > max_delegations)
> - return NULL;
> + atomic_long_inc(&num_delegations);
> + smp_mb__after_atomic_inc();
> + if (atomic_long_read(&num_delegations) > max_delegations)
> + goto out_dec;

Just use atomic_long_inc_return here.

> +out_dec:
> + atomic_long_dec(&num_delegations);
> + smp_mb__after_atomic_dec();

I can't see any point for having these barriers.


2014-04-18 18:46:09

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 41/70] NFSd: nfsd4_process_open2() must reference the delegation stateid

Ensure that nfsd4_process_open2() keeps a reference to the delegation
stateid until it is done working with it.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index d1e4f0e07f09..634cd4de9d32 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -3068,6 +3068,8 @@ static struct nfs4_delegation *find_deleg_stateid(struct nfs4_client *cl, statei
ret = find_stateid_by_type(cl, s, NFS4_DELEG_STID);
if (!ret)
return NULL;
+ /* FIXME: move into find_stateid_by_type */
+ atomic_inc(&ret->sc_count);
return delegstateid(ret);
}

@@ -3083,14 +3085,18 @@ nfs4_check_deleg(struct nfs4_client *cl, struct nfsd4_open *open,
{
int flags;
__be32 status = nfserr_bad_stateid;
+ struct nfs4_delegation *deleg;

- *dp = find_deleg_stateid(cl, &open->op_delegate_stateid);
- if (*dp == NULL)
+ deleg = find_deleg_stateid(cl, &open->op_delegate_stateid);
+ if (deleg == NULL)
goto out;
flags = share_access_to_flags(open->op_share_access);
- status = nfs4_check_delegmode(*dp, flags);
- if (status)
- *dp = NULL;
+ status = nfs4_check_delegmode(deleg, flags);
+ if (status) {
+ nfs4_put_delegation(deleg);
+ goto out;
+ }
+ *dp = deleg;
out:
if (!nfsd4_is_deleg_cur(open))
return nfs_ok;
@@ -3528,6 +3534,8 @@ out:
if (!(open->op_openowner->oo_flags & NFS4_OO_CONFIRMED) &&
!nfsd4_has_session(&resp->cstate))
open->op_rflags |= NFS4_OPEN_RESULT_CONFIRM;
+ if (dp)
+ nfs4_put_delegation(dp);

return status;
}
--
1.9.0


2014-04-18 18:46:02

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 36/70] NFSd: Add reference counting to find_stateid

Ensure the stateids won't be freed while we're inspecting them.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 43 +++++++++++++++++++++++++++++++++++--------
1 file changed, 35 insertions(+), 8 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 063ff9aba5d4..b9d6da652fb1 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1482,6 +1482,23 @@ static void gen_confirm(struct nfs4_client *clp)
memcpy(clp->cl_confirm.data, verf, sizeof(clp->cl_confirm.data));
}

+static void nfs4_put_stateid(struct nfs4_stid *s)
+{
+ if (s == NULL)
+ return;
+ switch (s->sc_type) {
+ case NFS4_OPEN_STID:
+ case NFS4_LOCK_STID:
+ case NFS4_CLOSED_STID:
+ put_generic_stateid(openlockstateid(s));
+ break;
+ case NFS4_DELEG_STID:
+ case NFS4_REVOKED_DELEG_STID:
+ case NFS4_CLOSED_DELEG_STID:
+ nfs4_put_delegation(delegstateid(s));
+ }
+}
+
static struct nfs4_stid *find_stateid_locked(struct nfs4_client *cl, stateid_t *t)
{
struct nfs4_stid *ret;
@@ -1498,6 +1515,8 @@ static struct nfs4_stid *find_stateid(struct nfs4_client *cl, stateid_t *t)

spin_lock(&cl->cl_lock);
ret = find_stateid_locked(cl, t);
+ if (ret != NULL)
+ atomic_inc(&ret->sc_count);
spin_unlock(&cl->cl_lock);
return ret;
}
@@ -3804,26 +3823,33 @@ static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid)
return nfserr_bad_stateid;
status = check_stateid_generation(stateid, &s->sc_stateid, 1);
if (status)
- return status;
+ goto out_put_stid;
switch (s->sc_type) {
case NFS4_DELEG_STID:
- return nfs_ok;
+ status = nfs_ok;
+ break;
case NFS4_REVOKED_DELEG_STID:
- return nfserr_deleg_revoked;
+ status = nfserr_deleg_revoked;
+ break;
case NFS4_OPEN_STID:
case NFS4_LOCK_STID:
ols = openlockstateid(s);
if (ols->st_stateowner->so_is_open_owner
&& !(openowner(ols->st_stateowner)->oo_flags
& NFS4_OO_CONFIRMED))
- return nfserr_bad_stateid;
- return nfs_ok;
+ status = nfserr_bad_stateid;
+ else
+ status = nfs_ok;
+ break;
default:
printk("unknown stateid type %x\n", s->sc_type);
case NFS4_CLOSED_STID:
case NFS4_CLOSED_DELEG_STID:
- return nfserr_bad_stateid;
+ status = nfserr_bad_stateid;
}
+out_put_stid:
+ nfs4_put_stateid(s);
+ return status;
}

static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask,
@@ -3976,12 +4002,12 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
switch (s->sc_type) {
case NFS4_DELEG_STID:
ret = nfserr_locks_held;
- goto out;
+ break;
case NFS4_OPEN_STID:
case NFS4_LOCK_STID:
ret = check_stateid_generation(stateid, &s->sc_stateid, 1);
if (ret)
- goto out;
+ break;
if (s->sc_type == NFS4_LOCK_STID)
ret = nfsd4_free_lock_stateid(openlockstateid(s));
else
@@ -3995,6 +4021,7 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
default:
ret = nfserr_bad_stateid;
}
+ nfs4_put_stateid(s);
out:
nfs4_unlock_state();
return ret;
--
1.9.0


2014-04-18 18:46:40

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 58/70] NFSd: Protect session creation and client confirm using client_lock

In particular, we want to ensure that the move_to_confirmed() is
protected by the nn->client_lock spin lock, so that we can use that
when looking up the clientid etc.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 55 ++++++++++++++++++++++++++++++-----------------------
1 file changed, 31 insertions(+), 24 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 9c24031757d2..f047341678c0 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -130,17 +130,6 @@ static __be32 mark_client_expired_locked(struct nfs4_client *clp)
return nfs_ok;
}

-static __be32 mark_client_expired(struct nfs4_client *clp)
-{
- struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
- __be32 ret;
-
- spin_lock(&nn->client_lock);
- ret = mark_client_expired_locked(clp);
- spin_unlock(&nn->client_lock);
- return ret;
-}
-
static __be32 get_client_locked(struct nfs4_client *clp)
{
if (is_client_expired(clp))
@@ -1134,12 +1123,10 @@ static void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, stru
new->se_cb_sec = cses->cb_sec;
atomic_set(&new->se_ref, 0);
idx = hash_sessionid(&new->se_sessionid);
- spin_lock(&nn->client_lock);
list_add(&new->se_hash, &nn->sessionid_hashtbl[idx]);
spin_lock(&clp->cl_lock);
list_add(&new->se_perclnt, &clp->cl_sessions);
spin_unlock(&clp->cl_lock);
- spin_unlock(&nn->client_lock);

if (cses->flags & SESSION4_BACK_CHAN) {
struct sockaddr *sa = svc_addr(rqstp);
@@ -2118,6 +2105,7 @@ nfsd4_create_session(struct svc_rqst *rqstp,
{
struct sockaddr *sa = svc_addr(rqstp);
struct nfs4_client *conf, *unconf;
+ struct nfs4_client *old = NULL;
struct nfsd4_session *new;
struct nfsd4_conn *conn;
struct nfsd4_clid_slot *cs_slot = NULL;
@@ -2144,6 +2132,7 @@ nfsd4_create_session(struct svc_rqst *rqstp,
goto out_free_session;

nfs4_lock_state();
+ spin_lock(&nn->client_lock);
unconf = find_unconfirmed_client(&cr_ses->clientid, true, nn);
conf = find_confirmed_client(&cr_ses->clientid, true, nn);
WARN_ON_ONCE(conf && unconf);
@@ -2162,7 +2151,6 @@ nfsd4_create_session(struct svc_rqst *rqstp,
goto out_free_conn;
}
} else if (unconf) {
- struct nfs4_client *old;
if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred) ||
!rpc_cmp_addr(sa, (struct sockaddr *) &unconf->cl_addr)) {
status = nfserr_clid_inuse;
@@ -2180,10 +2168,10 @@ nfsd4_create_session(struct svc_rqst *rqstp,
}
old = find_confirmed_client_by_name(&unconf->cl_name, nn);
if (old) {
- status = mark_client_expired(old);
+ status = mark_client_expired_locked(old);
if (status)
goto out_free_conn;
- expire_client(old);
+ unhash_client_locked(old);
}
move_to_confirmed(unconf);
conf = unconf;
@@ -2199,7 +2187,7 @@ nfsd4_create_session(struct svc_rqst *rqstp,
cr_ses->flags &= ~SESSION4_RDMA;

init_session(rqstp, new, conf, cr_ses);
- nfsd4_init_conn(rqstp, conn, new);
+ nfsd4_get_session_locked(new);

memcpy(cr_ses->sessionid.data, new->se_sessionid.data,
NFS4_MAX_SESSIONID_LEN);
@@ -2208,11 +2196,20 @@ nfsd4_create_session(struct svc_rqst *rqstp,

/* cache solo and embedded create sessions under the state lock */
nfsd4_cache_create_session(cr_ses, cs_slot, status);
+ spin_unlock(&nn->client_lock);
+ /* init connection and backchannel */
+ nfsd4_init_conn(rqstp, conn, new);
+ nfsd4_put_session(new);
nfs4_unlock_state();
+ if (old)
+ expire_client(old);
return status;
out_free_conn:
+ spin_unlock(&nn->client_lock);
nfs4_unlock_state();
free_conn(conn);
+ if (old)
+ expire_client(old);
out_free_session:
__free_session(new);
out_release_drc_mem:
@@ -2657,6 +2654,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
struct nfsd4_setclientid_confirm *setclientid_confirm)
{
struct nfs4_client *conf, *unconf;
+ struct nfs4_client *old = NULL;
nfs4_verifier confirm = setclientid_confirm->sc_confirm;
clientid_t * clid = &setclientid_confirm->sc_clientid;
__be32 status;
@@ -2666,6 +2664,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
return nfserr_stale_clientid;
nfs4_lock_state();

+ spin_lock(&nn->client_lock);
conf = find_confirmed_client(clid, false, nn);
unconf = find_unconfirmed_client(clid, false, nn);
/*
@@ -2689,21 +2688,29 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
}
status = nfs_ok;
if (conf) { /* case 1: callback update */
+ old = unconf;
+ unhash_client_locked(old);
nfsd4_change_callback(conf, &unconf->cl_cb_conn);
- nfsd4_probe_callback(conf);
- expire_client(unconf);
} else { /* case 3: normal case; new or rebooted client */
- conf = find_confirmed_client_by_name(&unconf->cl_name, nn);
- if (conf) {
- status = mark_client_expired(conf);
+ old = find_confirmed_client_by_name(&unconf->cl_name, nn);
+ if (old) {
+ status = mark_client_expired_locked(old);
if (status)
goto out;
- expire_client(conf);
+ unhash_client_locked(old);
}
move_to_confirmed(unconf);
- nfsd4_probe_callback(unconf);
+ conf = unconf;
}
+ get_client_locked(conf);
+ spin_unlock(&nn->client_lock);
+ nfsd4_probe_callback(conf);
+ spin_lock(&nn->client_lock);
+ put_client_renew_locked(conf);
out:
+ spin_unlock(&nn->client_lock);
+ if (old)
+ expire_client(old);
nfs4_unlock_state();
return status;
}
--
1.9.0


2014-04-19 14:38:06

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH 21/70] NFSd: Get rid of the lockowner_ino_hashtbl

On Fri, Apr 18, 2014 at 02:44:15PM -0400, Trond Myklebust wrote:
> It is no longer used.

I think this should be folded into the previous patch, which is the
reason why it is unused now.


2014-04-18 18:46:01

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 35/70] NFSd: Slight cleanup of find_stateid()

In preparation of reference counting...

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 26 +++++++++++++++++---------
1 file changed, 17 insertions(+), 9 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 87464802126a..063ff9aba5d4 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1482,28 +1482,36 @@ static void gen_confirm(struct nfs4_client *clp)
memcpy(clp->cl_confirm.data, verf, sizeof(clp->cl_confirm.data));
}

-static struct nfs4_stid *find_stateid(struct nfs4_client *cl, stateid_t *t)
+static struct nfs4_stid *find_stateid_locked(struct nfs4_client *cl, stateid_t *t)
{
struct nfs4_stid *ret;

- spin_lock(&cl->cl_lock);
ret = idr_find(&cl->cl_stateids, t->si_opaque.so_id);
- spin_unlock(&cl->cl_lock);
if (!ret || !ret->sc_type)
return NULL;
return ret;
}

+static struct nfs4_stid *find_stateid(struct nfs4_client *cl, stateid_t *t)
+{
+ struct nfs4_stid *ret;
+
+ spin_lock(&cl->cl_lock);
+ ret = find_stateid_locked(cl, t);
+ spin_unlock(&cl->cl_lock);
+ return ret;
+}
+
static struct nfs4_stid *find_stateid_by_type(struct nfs4_client *cl, stateid_t *t, char typemask)
{
struct nfs4_stid *s;

- s = find_stateid(cl, t);
- if (!s)
- return NULL;
- if (typemask & s->sc_type)
- return s;
- return NULL;
+ spin_lock(&cl->cl_lock);
+ s = find_stateid_locked(cl, t);
+ if (s != NULL && !(typemask & s->sc_type))
+ s = NULL;
+ spin_unlock(&cl->cl_lock);
+ return s;
}

static struct nfs4_client *create_client(struct xdr_netobj name,
--
1.9.0


2014-04-18 18:45:37

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 21/70] NFSd: Get rid of the lockowner_ino_hashtbl

It is no longer used.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/netns.h | 4 ----
fs/nfsd/nfs4state.c | 26 ++------------------------
fs/nfsd/state.h | 1 -
3 files changed, 2 insertions(+), 29 deletions(-)

diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h
index d32b3aa6600d..beaceac90ad3 100644
--- a/fs/nfsd/netns.h
+++ b/fs/nfsd/netns.h
@@ -29,9 +29,6 @@
#define CLIENT_HASH_SIZE (1 << CLIENT_HASH_BITS)
#define CLIENT_HASH_MASK (CLIENT_HASH_SIZE - 1)

-#define LOCKOWNER_INO_HASH_BITS 8
-#define LOCKOWNER_INO_HASH_SIZE (1 << LOCKOWNER_INO_HASH_BITS)
-
#define SESSION_HASH_SIZE 512

struct cld_net;
@@ -67,7 +64,6 @@ struct nfsd_net {
struct list_head *unconf_id_hashtbl;
struct rb_root unconf_name_tree;
struct list_head *ownerstr_hashtbl;
- struct list_head *lockowner_ino_hashtbl;
struct list_head *sessionid_hashtbl;
/*
* client_lru holds client queue ordered by nfs4_client.cl_time
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index be48dbc32f97..38ca008c3d41 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -724,7 +724,6 @@ static void unhash_lockowner(struct nfs4_lockowner *lo)
struct nfs4_ol_stateid *stp;

list_del(&lo->lo_owner.so_strhash);
- list_del(&lo->lo_owner_ino_hash);
while (!list_empty(&lo->lo_owner.so_stateids)) {
stp = list_first_entry(&lo->lo_owner.so_stateids,
struct nfs4_ol_stateid, st_perstateowner);
@@ -4225,8 +4224,6 @@ out:

#define LOFF_OVERFLOW(start, len) ((u64)(len) > ~(u64)(start))

-#define LOCKOWNER_INO_HASH_MASK (LOCKOWNER_INO_HASH_SIZE - 1)
-
static inline u64
end_offset(u64 start, u64 len)
{
@@ -4247,12 +4244,6 @@ last_byte_offset(u64 start, u64 len)
return end > start ? end - 1: NFS4_MAX_UINT64;
}

-static unsigned int lockowner_ino_hashval(struct inode *inode, u32 cl_id, struct xdr_netobj *ownername)
-{
- return (cl_id + opaque_hashval(ownername->data, ownername->len))
- & LOCKOWNER_INO_HASH_MASK;
-}
-
/*
* TODO: Linux file offsets are _signed_ 64-bit quantities, which means that
* we can't properly handle lock requests that go beyond the (2^63 - 1)-th
@@ -4322,15 +4313,11 @@ find_lockowner_str(clientid_t *clid, struct xdr_netobj *owner,
return NULL;
}

-static void hash_lockowner(struct nfs4_lockowner *lo, unsigned int strhashval, struct nfs4_client *clp, struct nfs4_ol_stateid *open_stp)
+static void hash_lockowner(struct nfs4_lockowner *lo, unsigned int strhashval, struct nfs4_client *clp)
{
- struct inode *inode = open_stp->st_file->fi_inode;
- unsigned int inohash = lockowner_ino_hashval(inode,
- clp->cl_clientid.cl_id, &lo->lo_owner.so_owner);
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);

list_add(&lo->lo_owner.so_strhash, &nn->ownerstr_hashtbl[strhashval]);
- list_add(&lo->lo_owner_ino_hash, &nn->lockowner_ino_hashtbl[inohash]);
}

/*
@@ -4353,7 +4340,7 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, str
/* It is the openowner seqid that will be incremented in encode in the
* case of new lockowners; so increment the lock seqid manually: */
lo->lo_owner.so_seqid = lock->lk_new_lock_seqid + 1;
- hash_lockowner(lo, strhashval, clp, open_stp);
+ hash_lockowner(lo, strhashval, clp);
return lo;
}

@@ -5174,10 +5161,6 @@ static int nfs4_state_create_net(struct net *net)
OWNER_HASH_SIZE, GFP_KERNEL);
if (!nn->ownerstr_hashtbl)
goto err_ownerstr;
- nn->lockowner_ino_hashtbl = kmalloc(sizeof(struct list_head) *
- LOCKOWNER_INO_HASH_SIZE, GFP_KERNEL);
- if (!nn->lockowner_ino_hashtbl)
- goto err_lockowner_ino;
nn->sessionid_hashtbl = kmalloc(sizeof(struct list_head) *
SESSION_HASH_SIZE, GFP_KERNEL);
if (!nn->sessionid_hashtbl)
@@ -5189,8 +5172,6 @@ static int nfs4_state_create_net(struct net *net)
}
for (i = 0; i < OWNER_HASH_SIZE; i++)
INIT_LIST_HEAD(&nn->ownerstr_hashtbl[i]);
- for (i = 0; i < LOCKOWNER_INO_HASH_SIZE; i++)
- INIT_LIST_HEAD(&nn->lockowner_ino_hashtbl[i]);
for (i = 0; i < SESSION_HASH_SIZE; i++)
INIT_LIST_HEAD(&nn->sessionid_hashtbl[i]);
nn->conf_name_tree = RB_ROOT;
@@ -5206,8 +5187,6 @@ static int nfs4_state_create_net(struct net *net)
return 0;

err_sessionid:
- kfree(nn->lockowner_ino_hashtbl);
-err_lockowner_ino:
kfree(nn->ownerstr_hashtbl);
err_ownerstr:
kfree(nn->unconf_id_hashtbl);
@@ -5239,7 +5218,6 @@ nfs4_state_destroy_net(struct net *net)
}

kfree(nn->sessionid_hashtbl);
- kfree(nn->lockowner_ino_hashtbl);
kfree(nn->ownerstr_hashtbl);
kfree(nn->unconf_id_hashtbl);
kfree(nn->conf_id_hashtbl);
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 51df8df04d6a..02fc6685ab3f 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -366,7 +366,6 @@ struct nfs4_openowner {

struct nfs4_lockowner {
struct nfs4_stateowner lo_owner; /* must be first element */
- struct list_head lo_owner_ino_hash; /* hash by owner,file */
struct list_head lo_list; /* for temporary uses */
};

--
1.9.0


2014-04-18 18:45:21

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 10/70] NFSd: Avoid taking state_lock while holding inode lock in nfsd_break_one_deleg

state_lock is a heavily contended global lock. We don't want to grab
that while simultaneously holding the inode->i_lock
Instead do the list manipulations from the work queue context prior to
starting the rpc call.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4callback.c | 18 ++++++++++++++++--
fs/nfsd/nfs4state.c | 25 +++++++++++++++----------
fs/nfsd/state.h | 1 +
3 files changed, 32 insertions(+), 12 deletions(-)

diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index 39c8ef875f91..8626cd6af4d1 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -1009,9 +1009,8 @@ static void nfsd4_process_cb_update(struct nfsd4_callback *cb)
run_nfsd4_cb(cb);
}

-static void nfsd4_do_callback_rpc(struct work_struct *w)
+static void nfsd4_run_callback_rpc(struct nfsd4_callback *cb)
{
- struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, cb_work);
struct nfs4_client *clp = cb->cb_clp;
struct rpc_clnt *clnt;

@@ -1029,11 +1028,25 @@ static void nfsd4_do_callback_rpc(struct work_struct *w)
cb->cb_ops, cb);
}

+static void nfsd4_do_callback_rpc(struct work_struct *w)
+{
+ struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, cb_work);
+ nfsd4_run_callback_rpc(cb);
+}
+
void nfsd4_init_callback(struct nfsd4_callback *cb)
{
INIT_WORK(&cb->cb_work, nfsd4_do_callback_rpc);
}

+static void nfsd4_do_cb_recall(struct work_struct *w)
+{
+ struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, cb_work);
+
+ nfsd4_prepare_cb_recall(cb->cb_op);
+ nfsd4_run_callback_rpc(cb);
+}
+
void nfsd4_cb_recall(struct nfs4_delegation *dp)
{
struct nfsd4_callback *cb = &dp->dl_recall;
@@ -1050,6 +1063,7 @@ void nfsd4_cb_recall(struct nfs4_delegation *dp)

INIT_LIST_HEAD(&cb->cb_per_client);
cb->cb_done = true;
+ INIT_WORK(&cb->cb_work, nfsd4_do_cb_recall);

run_nfsd4_cb(&dp->dl_recall);
}
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 6d691aa2bb27..85fcbc9ebd40 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -2743,24 +2743,31 @@ out:
return ret;
}

-static void nfsd_break_one_deleg(struct nfs4_delegation *dp)
+void nfsd4_prepare_cb_recall(struct nfs4_delegation *dp)
{
struct nfs4_client *clp = dp->dl_stid.sc_client;
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);

- lockdep_assert_held(&state_lock);
+ /*
+ * We can't do this in nfsd_break_deleg_cb because it is
+ * already holding inode->i_lock
+ */
+ spin_lock(&state_lock);
+ if (list_empty(&dp->dl_recall_lru)) {
+ dp->dl_time = get_seconds();
+ list_add_tail(&dp->dl_recall_lru, &nn->del_recall_lru);
+ }
+ spin_unlock(&state_lock);
+}
+
+static void nfsd_break_one_deleg(struct nfs4_delegation *dp)
+{
/* We're assuming the state code never drops its reference
* without first removing the lease. Since we're in this lease
* callback (and since the lease code is serialized by the kernel
* lock) we know the server hasn't removed the lease yet, we know
* it's safe to take a reference: */
atomic_inc(&dp->dl_count);
-
- list_add_tail(&dp->dl_recall_lru, &nn->del_recall_lru);
-
- /* Only place dl_time is set; protected by i_lock: */
- dp->dl_time = get_seconds();
-
nfsd4_cb_recall(dp);
}

@@ -2785,11 +2792,9 @@ static void nfsd_break_deleg_cb(struct file_lock *fl)
*/
fl->fl_break_time = 0;

- spin_lock(&state_lock);
fp->fi_had_conflict = true;
list_for_each_entry(dp, &fp->fi_delegations, dl_perfile)
nfsd_break_one_deleg(dp);
- spin_unlock(&state_lock);
}

static
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 1aa22f39fc65..02c5a203c738 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -473,6 +473,7 @@ extern void nfsd4_cb_recall(struct nfs4_delegation *dp);
extern int nfsd4_create_callback_queue(void);
extern void nfsd4_destroy_callback_queue(void);
extern void nfsd4_shutdown_callback(struct nfs4_client *);
+extern void nfsd4_prepare_cb_recall(struct nfs4_delegation *dp);
extern void nfs4_put_delegation(struct nfs4_delegation *dp);
extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(const char *name,
struct nfsd_net *nn);
--
1.9.0


2014-04-18 18:45:58

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 33/70] NFSd: Ensure atomicity of stateid destruction and idr tree removal

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 26 +++++++++++++++-----------
1 file changed, 15 insertions(+), 11 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 7b897ff03a27..f463aaf9cd13 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -461,31 +461,37 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv
return dp;
}

-static void remove_stid(struct nfs4_stid *s)
+static void remove_stid_locked(struct nfs4_client *clp, struct nfs4_stid *s)
{
- struct nfs4_client *clp = s->sc_client;
struct idr *stateids = &clp->cl_stateids;

- spin_lock(&clp->cl_lock);
idr_remove(stateids, s->sc_stateid.si_opaque.so_id);
- spin_unlock(&clp->cl_lock);
}

static void nfs4_free_stid(struct kmem_cache *slab, struct nfs4_stid *s)
{
- remove_stid(s);
if (s->sc_file)
put_nfs4_file(s->sc_file);
kmem_cache_free(slab, s);
}

+static bool nfs4_put_stid(struct kmem_cache *slab, struct nfs4_stid *s)
+{
+ struct nfs4_client *clp = s->sc_client;
+
+ if (!atomic_dec_and_lock(&s->sc_count, &clp->cl_lock))
+ return false;
+ remove_stid_locked(clp, s);
+ spin_unlock(&clp->cl_lock);
+ nfs4_free_stid(slab, s);
+ return true;
+}
+
void
nfs4_put_delegation(struct nfs4_delegation *dp)
{
- if (atomic_dec_and_test(&dp->dl_stid.sc_count)) {
- nfs4_free_stid(deleg_slab, &dp->dl_stid);
+ if (nfs4_put_stid(deleg_slab, &dp->dl_stid))
num_delegations--;
- }
}

/* Call under fp->fi_lock */
@@ -712,9 +718,7 @@ static void close_generic_stateid(struct nfs4_ol_stateid *stp)

static void put_generic_stateid(struct nfs4_ol_stateid *stp)
{
- if (!atomic_dec_and_test(&stp->st_stid.sc_count))
- return;
- nfs4_free_stid(stateid_slab, &stp->st_stid);
+ nfs4_put_stid(stateid_slab, &stp->st_stid);
}

static void __release_lock_stateid(struct nfs4_lockowner *lo,
--
1.9.0


2014-04-18 18:46:18

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 47/70] NFSd: Migrate the stateid reference into nfs4_lookup_stateid()

Allow nfs4_lookup_stateid to take the stateid reference, instead
of having all the callers do so.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 8e2e519f9b94..08d8fb501e81 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -3891,6 +3891,8 @@ static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask,
*s = find_stateid_by_type(cl, stateid, typemask);
if (!*s)
return nfserr_bad_stateid;
+ /* FIXME: move into find_stateid_by_type */
+ atomic_inc(&(*s)->sc_count);
return nfs_ok;
}

@@ -3924,7 +3926,7 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,
status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID,
&s, cstate->minorversion, nn);
if (status)
- goto out;
+ goto unlock_state;
status = check_stateid_generation(stateid, &s->sc_stateid, nfsd4_has_session(cstate));
if (status)
goto out;
@@ -3971,6 +3973,8 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,
if (file)
*filpp = file;
out:
+ nfs4_put_stateid(s);
+unlock_state:
nfs4_unlock_state();
return status;
}
@@ -4100,11 +4104,10 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
nfsd4_cstate_assign_replay(cstate, stp->st_stateowner);

status = nfs4_seqid_op_checks(cstate, stateid, seqid, stp);
- if (!status) {
- /* FIXME: move into find_stateid_by_type */
- atomic_inc(&stp->st_stid.sc_count);
+ if (!status)
*stpp = stp;
- }
+ else
+ put_generic_stateid(stp);
return status;
}

@@ -4336,9 +4339,11 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
dp = delegstateid(s);
status = check_stateid_generation(stateid, &dp->dl_stid.sc_stateid, nfsd4_has_session(cstate));
if (status)
- goto out;
+ goto put_stateid;

unhash_and_destroy_delegation(dp);
+put_stateid:
+ nfs4_put_delegation(dp);
out:
nfs4_unlock_state();

--
1.9.0


2014-04-18 18:45:13

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 05/70] nfsd4: rename recall_lock to state_lock

From: Benny Halevy <[email protected]>

Signed-off-by: Benny Halevy <[email protected]>
---
fs/nfsd/nfs4state.c | 55 +++++++++++++++++++++++++++--------------------------
1 file changed, 28 insertions(+), 27 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 841495aa9170..34273ca482c3 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -81,7 +81,7 @@ static DEFINE_MUTEX(client_mutex);
* effort to decrease the scope of the client_mutex, this spinlock may
* eventually cover more:
*/
-static DEFINE_SPINLOCK(recall_lock);
+static DEFINE_SPINLOCK(state_lock);

static struct kmem_cache *openowner_slab = NULL;
static struct kmem_cache *lockowner_slab = NULL;
@@ -235,9 +235,9 @@ static void nfsd4_free_file(struct nfs4_file *f)
static inline void
put_nfs4_file(struct nfs4_file *fi)
{
- if (atomic_dec_and_lock(&fi->fi_ref, &recall_lock)) {
+ if (atomic_dec_and_lock(&fi->fi_ref, &state_lock)) {
hlist_del(&fi->fi_hash);
- spin_unlock(&recall_lock);
+ spin_unlock(&state_lock);
iput(fi->fi_inode);
nfsd4_free_file(fi);
}
@@ -436,10 +436,10 @@ static void
unhash_delegation(struct nfs4_delegation *dp)
{
list_del_init(&dp->dl_perclnt);
- spin_lock(&recall_lock);
+ spin_lock(&state_lock);
list_del_init(&dp->dl_perfile);
list_del_init(&dp->dl_recall_lru);
- spin_unlock(&recall_lock);
+ spin_unlock(&state_lock);
nfs4_put_deleg_lease(dp->dl_file);
put_nfs4_file(dp->dl_file);
dp->dl_file = NULL;
@@ -1136,13 +1136,13 @@ destroy_client(struct nfs4_client *clp)
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);

INIT_LIST_HEAD(&reaplist);
- spin_lock(&recall_lock);
+ spin_lock(&state_lock);
while (!list_empty(&clp->cl_delegations)) {
dp = list_entry(clp->cl_delegations.next, struct nfs4_delegation, dl_perclnt);
list_del_init(&dp->dl_perclnt);
list_move(&dp->dl_recall_lru, &reaplist);
}
- spin_unlock(&recall_lock);
+ spin_unlock(&state_lock);
while (!list_empty(&reaplist)) {
dp = list_entry(reaplist.next, struct nfs4_delegation, dl_recall_lru);
destroy_delegation(dp);
@@ -2499,9 +2499,9 @@ static void nfsd4_init_file(struct nfs4_file *fp, struct inode *ino)
fp->fi_lease = NULL;
memset(fp->fi_fds, 0, sizeof(fp->fi_fds));
memset(fp->fi_access, 0, sizeof(fp->fi_access));
- spin_lock(&recall_lock);
+ spin_lock(&state_lock);
hlist_add_head(&fp->fi_hash, &file_hashtbl[hashval]);
- spin_unlock(&recall_lock);
+ spin_unlock(&state_lock);
}

static void
@@ -2685,15 +2685,15 @@ find_file(struct inode *ino)
unsigned int hashval = file_hashval(ino);
struct nfs4_file *fp;

- spin_lock(&recall_lock);
+ spin_lock(&state_lock);
hlist_for_each_entry(fp, &file_hashtbl[hashval], fi_hash) {
if (fp->fi_inode == ino) {
get_nfs4_file(fp);
- spin_unlock(&recall_lock);
+ spin_unlock(&state_lock);
return fp;
}
}
- spin_unlock(&recall_lock);
+ spin_unlock(&state_lock);
return NULL;
}

@@ -2730,6 +2730,7 @@ static void nfsd_break_one_deleg(struct nfs4_delegation *dp)
struct nfs4_client *clp = dp->dl_stid.sc_client;
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);

+ lockdep_assert_held(&state_lock);
/* We're assuming the state code never drops its reference
* without first removing the lease. Since we're in this lease
* callback (and since the lease code is serialized by the kernel
@@ -2766,11 +2767,11 @@ static void nfsd_break_deleg_cb(struct file_lock *fl)
*/
fl->fl_break_time = 0;

- spin_lock(&recall_lock);
+ spin_lock(&state_lock);
fp->fi_had_conflict = true;
list_for_each_entry(dp, &fp->fi_delegations, dl_perfile)
nfsd_break_one_deleg(dp);
- spin_unlock(&recall_lock);
+ spin_unlock(&state_lock);
}

static
@@ -3072,15 +3073,15 @@ static int nfs4_set_delegation(struct nfs4_delegation *dp, struct nfs4_file *fp)
goto out_free;
return 0;
}
- spin_lock(&recall_lock);
+ spin_lock(&state_lock);
if (fp->fi_had_conflict) {
- spin_unlock(&recall_lock);
+ spin_unlock(&state_lock);
status = -EAGAIN;
goto out_free;
}
atomic_inc(&fp->fi_delegees);
list_add(&dp->dl_perfile, &fp->fi_delegations);
- spin_unlock(&recall_lock);
+ spin_unlock(&state_lock);
list_add(&dp->dl_perclnt, &dp->dl_stid.sc_client->cl_delegations);
return 0;
out_free:
@@ -3422,7 +3423,7 @@ nfs4_laundromat(struct nfsd_net *nn)
clp->cl_clientid.cl_id);
expire_client(clp);
}
- spin_lock(&recall_lock);
+ spin_lock(&state_lock);
list_for_each_safe(pos, next, &nn->del_recall_lru) {
dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);
if (net_generic(dp->dl_stid.sc_client->net, nfsd_net_id) != nn)
@@ -3435,7 +3436,7 @@ nfs4_laundromat(struct nfsd_net *nn)
}
list_move(&dp->dl_recall_lru, &reaplist);
}
- spin_unlock(&recall_lock);
+ spin_unlock(&state_lock);
list_for_each_safe(pos, next, &reaplist) {
dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);
revoke_delegation(dp);
@@ -4900,9 +4901,9 @@ u64 nfsd_forget_client_delegations(struct nfs4_client *clp, u64 max)
LIST_HEAD(victims);
u64 count;

- spin_lock(&recall_lock);
+ spin_lock(&state_lock);
count = nfsd_find_all_delegations(clp, max, &victims);
- spin_unlock(&recall_lock);
+ spin_unlock(&state_lock);

list_for_each_entry_safe(dp, next, &victims, dl_recall_lru)
revoke_delegation(dp);
@@ -4916,11 +4917,11 @@ u64 nfsd_recall_client_delegations(struct nfs4_client *clp, u64 max)
LIST_HEAD(victims);
u64 count;

- spin_lock(&recall_lock);
+ spin_lock(&state_lock);
count = nfsd_find_all_delegations(clp, max, &victims);
list_for_each_entry_safe(dp, next, &victims, dl_recall_lru)
nfsd_break_one_deleg(dp);
- spin_unlock(&recall_lock);
+ spin_unlock(&state_lock);

return count;
}
@@ -4929,9 +4930,9 @@ u64 nfsd_print_client_delegations(struct nfs4_client *clp, u64 max)
{
u64 count = 0;

- spin_lock(&recall_lock);
+ spin_lock(&state_lock);
count = nfsd_find_all_delegations(clp, max, NULL);
- spin_unlock(&recall_lock);
+ spin_unlock(&state_lock);

nfsd_print_count(clp, count, "delegations");
return count;
@@ -5149,12 +5150,12 @@ nfs4_state_shutdown_net(struct net *net)

nfs4_lock_state();
INIT_LIST_HEAD(&reaplist);
- spin_lock(&recall_lock);
+ spin_lock(&state_lock);
list_for_each_safe(pos, next, &nn->del_recall_lru) {
dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);
list_move(&dp->dl_recall_lru, &reaplist);
}
- spin_unlock(&recall_lock);
+ spin_unlock(&state_lock);
list_for_each_safe(pos, next, &reaplist) {
dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);
destroy_delegation(dp);
--
1.9.0


2014-04-18 18:45:49

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 28/70] NFSd: Add reference counting to the lock and open stateids

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 41314e493110..32ab3f1c83f8 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -710,8 +710,10 @@ static void close_generic_stateid(struct nfs4_ol_stateid *stp)
release_all_access(stp);
}

-static void free_generic_stateid(struct nfs4_ol_stateid *stp)
+static void put_generic_stateid(struct nfs4_ol_stateid *stp)
{
+ if (!atomic_dec_and_test(&stp->st_stid.sc_count))
+ return;
remove_stid(&stp->st_stid);
if (stp->st_file)
put_nfs4_file(stp->st_file);
@@ -730,7 +732,7 @@ static void __release_lock_stateid(struct nfs4_lockowner *lo,
if (file)
filp_close(file, (fl_owner_t)lo);
close_generic_stateid(stp);
- free_generic_stateid(stp);
+ put_generic_stateid(stp);
}

static void unhash_lockowner(struct nfs4_lockowner *lo)
@@ -793,7 +795,7 @@ static void unhash_open_stateid(struct nfs4_ol_stateid *stp)
static void release_open_stateid(struct nfs4_ol_stateid *stp)
{
unhash_open_stateid(stp);
- free_generic_stateid(stp);
+ put_generic_stateid(stp);
}

static void unhash_openowner(struct nfs4_openowner *oo)
@@ -814,7 +816,7 @@ static void release_last_closed_stateid(struct nfs4_openowner *oo)
struct nfs4_ol_stateid *s = oo->oo_last_closed_stid;

if (s) {
- free_generic_stateid(s);
+ put_generic_stateid(s);
oo->oo_last_closed_stid = NULL;
}
}
@@ -3512,7 +3514,7 @@ void nfsd4_cleanup_open_state(struct nfsd4_open *open, __be32 status)
if (open->op_file)
nfsd4_free_file(open->op_file);
if (open->op_stp)
- free_generic_stateid(open->op_stp);
+ put_generic_stateid(open->op_stp);
}

static __be32 lookup_clientid(clientid_t *clid, bool session, struct nfsd_net *nn, struct nfs4_client **clp)
@@ -4214,7 +4216,7 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
nfsd4_close_open_stateid(stp);

if (cstate->minorversion)
- free_generic_stateid(stp);
+ put_generic_stateid(stp);
else
oo->oo_last_closed_stid = stp;

--
1.9.0


2014-04-21 15:58:55

by Trond Myklebust

[permalink] [raw]
Subject: Re: [PATCH 34/70] NFSd: Fix atomicity of delegation counter

On Sat, 2014-04-19 at 08:51 -0700, Christoph Hellwig wrote:
> On Fri, Apr 18, 2014 at 02:44:28PM -0400, Trond Myklebust wrote:
> > Signed-off-by: Trond Myklebust <[email protected]>
>
> I think at this point nfs4_lock_state() is always held so it's not
> quite a fix yet. If that's not true the patch should be moved earlier
> in the series.

Fair enough...

>
> > -static int num_delegations;
> > +static atomic_long_t num_delegations;
>
> Why the switch from a int to an (atomic) long here? If that was
> intentional it should be documented in the patch description.

It is intentional. The max_delegations that we're comparing to below is
of type 'unsigned long'.

>
> > - if (num_delegations > max_delegations)
> > - return NULL;
> > + atomic_long_inc(&num_delegations);
> > + smp_mb__after_atomic_inc();
> > + if (atomic_long_read(&num_delegations) > max_delegations)
> > + goto out_dec;
>
> Just use atomic_long_inc_return here.
>
> > +out_dec:
> > + atomic_long_dec(&num_delegations);
> > + smp_mb__after_atomic_dec();
>
> I can't see any point for having these barriers.

Agreed. I can't remember why I thought they were necessary.

--
Trond Myklebust
Linux NFS client maintainer, PrimaryData
[email protected]



2014-04-18 18:46:52

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 66/70] NFSd: Remove nfs4_lock_state(): nfsd4_delegreturn()

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 3 ---
1 file changed, 3 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 681b25deac02..5f0facdde981 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4388,7 +4388,6 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0)))
return status;

- nfs4_lock_state();
status = nfsd4_lookup_stateid(cstate, stateid, NFS4_DELEG_STID, &s, nn);
if (status)
goto out;
@@ -4401,8 +4400,6 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
put_stateid:
nfs4_put_delegation(dp);
out:
- nfs4_unlock_state();
-
return status;
}

--
1.9.0


2014-04-18 18:46:41

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 59/70] NFSd: Protect nfsd4_destroy_clientid using client_lock

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index f047341678c0..6a0c201c670f 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -2518,22 +2518,23 @@ nfsd4_sequence_done(struct nfsd4_compoundres *resp)
__be32
nfsd4_destroy_clientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_destroy_clientid *dc)
{
- struct nfs4_client *conf, *unconf, *clp;
+ struct nfs4_client *conf, *unconf;
+ struct nfs4_client *clp= NULL;
__be32 status = 0;
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);

nfs4_lock_state();
+ spin_lock(&nn->client_lock);
unconf = find_unconfirmed_client(&dc->clientid, true, nn);
conf = find_confirmed_client(&dc->clientid, true, nn);
WARN_ON_ONCE(conf && unconf);

if (conf) {
- clp = conf;
-
if (client_has_state(conf)) {
status = nfserr_clientid_busy;
goto out;
}
+ clp = conf;
} else if (unconf)
clp = unconf;
else {
@@ -2541,12 +2542,16 @@ nfsd4_destroy_clientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *csta
goto out;
}
if (!mach_creds_match(clp, rqstp)) {
+ clp = NULL;
status = nfserr_wrong_cred;
goto out;
}
- expire_client(clp);
+ unhash_client_locked(clp);
out:
+ spin_unlock(&nn->client_lock);
nfs4_unlock_state();
+ if (clp)
+ expire_client(clp);
return status;
}

--
1.9.0


2014-04-18 18:45:47

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 26/70] NFSd: Fix delegation revocation

Ensure that the delegations cannot be found by the laundromat etc once
we add them to the various 'revoke' lists.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 51 ++++++++++++++++++++++++++++++++-------------------
1 file changed, 32 insertions(+), 19 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 51ef179339ae..273bdddb2458 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -516,11 +516,10 @@ hash_delegation_locked(struct nfs4_delegation *dp, struct nfs4_file *fp)

/* Called under the state lock. */
static void
-unhash_delegation(struct nfs4_delegation *dp)
+unhash_delegation_locked(struct nfs4_delegation *dp)
{
struct nfs4_file *fp = dp->dl_file;

- spin_lock(&state_lock);
spin_lock(&fp->fi_lock);
list_del_init(&dp->dl_perclnt);
list_del_init(&dp->dl_recall_lru);
@@ -531,23 +530,32 @@ unhash_delegation(struct nfs4_delegation *dp)
}
nfs4_put_deleg_lease(fp);
spin_unlock(&fp->fi_lock);
- spin_unlock(&state_lock);
}

+static void
+unhash_delegation(struct nfs4_delegation *dp)
+{
+ spin_lock(&state_lock);
+ unhash_delegation_locked(dp);
+ spin_unlock(&state_lock);
+}

-
-static void destroy_revoked_delegation(struct nfs4_delegation *dp)
+static void destroy_delegation(struct nfs4_delegation *dp)
{
- list_del_init(&dp->dl_recall_lru);
remove_stid(&dp->dl_stid);
nfs4_put_delegation(dp);
}

-static void destroy_delegation(struct nfs4_delegation *dp)
+static void unhash_and_destroy_delegation(struct nfs4_delegation *dp)
{
unhash_delegation(dp);
- remove_stid(&dp->dl_stid);
- nfs4_put_delegation(dp);
+ destroy_delegation(dp);
+}
+
+static void destroy_revoked_delegation(struct nfs4_delegation *dp)
+{
+ list_del_init(&dp->dl_recall_lru);
+ destroy_delegation(dp);
}

static void revoke_delegation(struct nfs4_delegation *dp)
@@ -555,11 +563,10 @@ static void revoke_delegation(struct nfs4_delegation *dp)
struct nfs4_client *clp = dp->dl_stid.sc_client;

if (clp->cl_minorversion == 0)
- destroy_delegation(dp);
+ destroy_revoked_delegation(dp);
else {
- unhash_delegation(dp);
dp->dl_stid.sc_type = NFS4_REVOKED_DELEG_STID;
- list_add(&dp->dl_recall_lru, &clp->cl_revoked);
+ list_move(&dp->dl_recall_lru, &clp->cl_revoked);
}
}

@@ -1278,12 +1285,13 @@ destroy_client(struct nfs4_client *clp)
spin_lock(&state_lock);
while (!list_empty(&clp->cl_delegations)) {
dp = list_entry(clp->cl_delegations.next, struct nfs4_delegation, dl_perclnt);
- list_del_init(&dp->dl_perclnt);
- list_move(&dp->dl_recall_lru, &reaplist);
+ unhash_delegation_locked(dp);
+ list_add(&dp->dl_recall_lru, &reaplist);
}
spin_unlock(&state_lock);
while (!list_empty(&reaplist)) {
dp = list_entry(reaplist.next, struct nfs4_delegation, dl_recall_lru);
+ list_del_init(&dp->dl_recall_lru);
destroy_delegation(dp);
}
list_splice_init(&clp->cl_revoked, &reaplist);
@@ -3612,7 +3620,8 @@ nfs4_laundromat(struct nfsd_net *nn)
test_val = u;
break;
}
- list_move(&dp->dl_recall_lru, &reaplist);
+ unhash_delegation_locked(dp);
+ list_add(&dp->dl_recall_lru, &reaplist);
}
spin_unlock(&state_lock);
list_for_each_safe(pos, next, &reaplist) {
@@ -4245,7 +4254,7 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (status)
goto out;

- destroy_delegation(dp);
+ unhash_and_destroy_delegation(dp);
out:
nfs4_unlock_state();

@@ -5061,8 +5070,10 @@ static u64 nfsd_find_all_delegations(struct nfs4_client *clp, u64 max,

lockdep_assert_held(&state_lock);
list_for_each_entry_safe(dp, next, &clp->cl_delegations, dl_perclnt) {
- if (victims)
- list_move(&dp->dl_recall_lru, victims);
+ if (victims) {
+ unhash_delegation_locked(dp);
+ list_add(&dp->dl_recall_lru, victims);
+ }
if (++count == max)
break;
}
@@ -5318,11 +5329,13 @@ nfs4_state_shutdown_net(struct net *net)
spin_lock(&state_lock);
list_for_each_safe(pos, next, &nn->del_recall_lru) {
dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);
- list_move(&dp->dl_recall_lru, &reaplist);
+ unhash_delegation_locked(dp);
+ list_add(&dp->dl_recall_lru, &reaplist);
}
spin_unlock(&state_lock);
list_for_each_safe(pos, next, &reaplist) {
dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);
+ list_del_init(&dp->dl_recall_lru);
destroy_delegation(dp);
}

--
1.9.0


2014-04-18 18:46:11

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 42/70] NFSd: nfsd4_process_open2() must reference the open stateid

Ensure that nfsd4_process_open2() keeps a reference to the open
stateid until it is done working with it.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 634cd4de9d32..ec7cc90de03a 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -2806,6 +2806,7 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, str
static void init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp, struct nfsd4_open *open) {
struct nfs4_openowner *oo = open->op_openowner;

+ atomic_inc(&stp->st_stid.sc_count);
stp->st_stid.sc_type = NFS4_OPEN_STID;
INIT_LIST_HEAD(&stp->st_locks);
list_add(&stp->st_perstateowner, &oo->oo_owner.so_stateids);
@@ -3111,6 +3112,7 @@ nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_ol_st
{
struct nfs4_ol_stateid *local;
struct nfs4_openowner *oo = open->op_openowner;
+ struct nfs4_ol_stateid *ret = NULL;

spin_lock(&fp->fi_lock);
list_for_each_entry(local, &fp->fi_stateids, st_perfile) {
@@ -3119,13 +3121,17 @@ nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_ol_st
continue;
/* remember if we have seen this open owner */
if (local->st_stateowner == &oo->oo_owner)
- *stpp = local;
+ ret = local;
/* check for conflicting share reservations */
if (!test_share(local, open)) {
spin_unlock(&fp->fi_lock);
return nfserr_share_denied;
}
}
+ if (ret) {
+ atomic_inc(&ret->st_stid.sc_count);
+ *stpp = ret;
+ }
spin_unlock(&fp->fi_lock);
return nfs_ok;
}
@@ -3536,6 +3542,8 @@ out:
open->op_rflags |= NFS4_OPEN_RESULT_CONFIRM;
if (dp)
nfs4_put_delegation(dp);
+ if (stp)
+ put_generic_stateid(stp);

return status;
}
--
1.9.0


2014-04-18 18:45:28

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 15/70] NFSd: Add locking to the nfs4_file->fi_fds[] array

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 107 ++++++++++++++++++++++++++++++++++++++++++++--------
fs/nfsd/state.h | 26 -------------
2 files changed, 91 insertions(+), 42 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index ad7d21e06c36..73e7da308d37 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -249,6 +249,54 @@ get_nfs4_file(struct nfs4_file *fi)
atomic_inc(&fi->fi_ref);
}

+static struct file *__nfs4_get_fd(struct nfs4_file *f, int oflag)
+{
+ if (f->fi_fds[oflag])
+ return get_file(f->fi_fds[oflag]);
+ return NULL;
+}
+
+/* XXX: for first cut may fall back on returning file that doesn't work
+ * at all? */
+static struct file *find_writeable_file(struct nfs4_file *f)
+{
+ struct file *ret;
+
+ spin_lock(&f->fi_lock);
+ ret = __nfs4_get_fd(f, O_WRONLY);
+ if (!ret)
+ ret = __nfs4_get_fd(f, O_RDWR);
+ spin_unlock(&f->fi_lock);
+ return ret;
+}
+
+static struct file *find_readable_file(struct nfs4_file *f)
+{
+ struct file *ret;
+
+ spin_lock(&f->fi_lock);
+ ret = __nfs4_get_fd(f, O_RDONLY);
+ if (!ret)
+ ret = __nfs4_get_fd(f, O_RDWR);
+ spin_unlock(&f->fi_lock);
+ return ret;
+}
+
+static struct file *find_any_file(struct nfs4_file *f)
+{
+ struct file *ret;
+
+ spin_lock(&f->fi_lock);
+ ret = __nfs4_get_fd(f, O_RDWR);
+ if (!ret) {
+ ret = __nfs4_get_fd(f, O_WRONLY);
+ if (!ret)
+ ret = __nfs4_get_fd(f, O_RDONLY);
+ }
+ spin_unlock(&f->fi_lock);
+ return ret;
+}
+
static int num_delegations;
unsigned long max_delegations;

@@ -297,20 +345,29 @@ static void nfs4_file_get_access(struct nfs4_file *fp, int oflag)
__nfs4_file_get_access(fp, oflag);
}

-static void nfs4_file_put_fd(struct nfs4_file *fp, int oflag)
+static struct file *nfs4_file_put_fd(struct nfs4_file *fp, int oflag)
{
- if (fp->fi_fds[oflag]) {
- fput(fp->fi_fds[oflag]);
- fp->fi_fds[oflag] = NULL;
- }
+ struct file *filp;
+
+ filp = fp->fi_fds[oflag];
+ fp->fi_fds[oflag] = NULL;
+ return filp;
}

static void __nfs4_file_put_access(struct nfs4_file *fp, int oflag)
{
- if (atomic_dec_and_test(&fp->fi_access[oflag])) {
- nfs4_file_put_fd(fp, oflag);
+ if (atomic_dec_and_lock(&fp->fi_access[oflag], &fp->fi_lock)) {
+ struct file *f1 = NULL;
+ struct file *f2 = NULL;
+
+ f1 = nfs4_file_put_fd(fp, oflag);
if (atomic_read(&fp->fi_access[1 - oflag]) == 0)
- nfs4_file_put_fd(fp, O_RDWR);
+ f2 = nfs4_file_put_fd(fp, O_RDWR);
+ spin_unlock(&fp->fi_lock);
+ if (f1)
+ fput(f1);
+ if (f2)
+ fput(f2);
}
}

@@ -651,8 +708,10 @@ static void release_lock_stateid(struct nfs4_ol_stateid *stp)
unhash_generic_stateid(stp);
unhash_stid(&stp->st_stid);
file = find_any_file(stp->st_file);
- if (file)
+ if (file) {
locks_remove_posix(file, (fl_owner_t)lockowner(stp->st_stateowner));
+ fput(file);
+ }
close_generic_stateid(stp);
free_generic_stateid(stp);
}
@@ -2985,17 +3044,27 @@ static inline int nfs4_access_to_access(u32 nfs4_access)
static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp,
struct svc_fh *cur_fh, struct nfsd4_open *open)
{
+ struct file *filp = NULL;
__be32 status;
int oflag = nfs4_access_to_omode(open->op_share_access);
int access = nfs4_access_to_access(open->op_share_access);

+ spin_lock(&fp->fi_lock);
if (!fp->fi_fds[oflag]) {
- status = nfsd_open(rqstp, cur_fh, S_IFREG, access,
- &fp->fi_fds[oflag]);
+ spin_unlock(&fp->fi_lock);
+ status = nfsd_open(rqstp, cur_fh, S_IFREG, access, &filp);
if (status)
return status;
+ spin_lock(&fp->fi_lock);
+ if (!fp->fi_fds[oflag]) {
+ fp->fi_fds[oflag] = filp;
+ filp = NULL;
+ }
}
nfs4_file_get_access(fp, oflag);
+ spin_unlock(&fp->fi_lock);
+ if (filp)
+ fput(filp);

return nfs_ok;
}
@@ -3094,13 +3163,15 @@ static int nfs4_setlease(struct nfs4_delegation *dp)
if (status)
goto out_free;
fp->fi_lease = fl;
- fp->fi_deleg_file = get_file(fl->fl_file);
+ fp->fi_deleg_file = fl->fl_file;
atomic_set(&fp->fi_delegees, 1);
spin_lock(&state_lock);
hash_delegation_locked(dp, fp);
spin_unlock(&state_lock);
return 0;
out_free:
+ if (fl->fl_file)
+ fput(fl->fl_file);
locks_free_lock(fl);
return status;
}
@@ -3734,6 +3805,7 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,
status = nfserr_serverfault;
goto out;
}
+ get_file(file);
}
break;
case NFS4_OPEN_STID:
@@ -3761,7 +3833,7 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,
}
status = nfs_ok;
if (file)
- *filpp = get_file(file);
+ *filpp = file;
out:
nfs4_unlock_state();
return status;
@@ -4489,6 +4561,8 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
break;
}
out:
+ if (filp)
+ fput(filp);
if (status && new_state)
release_lockowner(lock_sop);
nfsd4_bump_seqid(cstate, status);
@@ -4630,7 +4704,7 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (!file_lock) {
dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
status = nfserr_jukebox;
- goto out;
+ goto fput;
}
locks_init_lock(file_lock);
file_lock->fl_type = F_UNLCK;
@@ -4652,7 +4726,8 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
}
update_stateid(&stp->st_stid.sc_stateid);
memcpy(&locku->lu_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));
-
+fput:
+ fput(filp);
out:
nfsd4_bump_seqid(cstate, status);
nfs4_unlock_state();
@@ -4662,7 +4737,7 @@ out:

out_nfserr:
status = nfserrno(err);
- goto out;
+ goto fput;
}

/*
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 80789aa5b5b4..004733885cca 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -405,32 +405,6 @@ struct nfs4_file {
bool fi_had_conflict;
};

-/* XXX: for first cut may fall back on returning file that doesn't work
- * at all? */
-static inline struct file *find_writeable_file(struct nfs4_file *f)
-{
- if (f->fi_fds[O_WRONLY])
- return f->fi_fds[O_WRONLY];
- return f->fi_fds[O_RDWR];
-}
-
-static inline struct file *find_readable_file(struct nfs4_file *f)
-{
- if (f->fi_fds[O_RDONLY])
- return f->fi_fds[O_RDONLY];
- return f->fi_fds[O_RDWR];
-}
-
-static inline struct file *find_any_file(struct nfs4_file *f)
-{
- if (f->fi_fds[O_RDWR])
- return f->fi_fds[O_RDWR];
- else if (f->fi_fds[O_WRONLY])
- return f->fi_fds[O_WRONLY];
- else
- return f->fi_fds[O_RDONLY];
-}
-
/* "ol" stands for "Open or Lock". Better suggestions welcome. */
struct nfs4_ol_stateid {
struct nfs4_stid st_stid; /* must be first field */
--
1.9.0


2014-04-18 18:46:37

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 56/70] NFSd: Move create_client() call outside the lock

For efficiency reasons, and because we want to use spin locks.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 35 +++++++++++++++++++----------------
1 file changed, 19 insertions(+), 16 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index b2406f20b529..79948fd2ad9e 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1888,6 +1888,10 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
return nfserr_encr_alg_unsupp;
}

+ new = create_client(exid->clname, rqstp, &verf);
+ if (new == NULL)
+ return nfserr_jukebox;
+
/* Cases below refer to rfc 5661 section 18.35.4: */
nfs4_lock_state();
conf = find_confirmed_client_by_name(&exid->clname, nn);
@@ -1914,7 +1918,6 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
}
/* case 6 */
exid->flags |= EXCHGID4_FLAG_CONFIRMED_R;
- new = conf;
goto out_copy;
}
if (!creds_match) { /* case 3 */
@@ -1927,7 +1930,6 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
}
if (verfs_match) { /* case 2 */
conf->cl_exchange_flags |= EXCHGID4_FLAG_CONFIRMED_R;
- new = conf;
goto out_copy;
}
/* case 5, client reboot */
@@ -1945,29 +1947,28 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,

/* case 1 (normal case) */
out_new:
- new = create_client(exid->clname, rqstp, &verf);
- if (new == NULL) {
- status = nfserr_jukebox;
- goto out;
- }
new->cl_minorversion = cstate->minorversion;
new->cl_mach_cred = (exid->spa_how == SP4_MACH_CRED);

gen_clid(new, nn);
add_to_unconfirmed(new);
+ conf = new;
+ new = NULL;
out_copy:
- exid->clientid.cl_boot = new->cl_clientid.cl_boot;
- exid->clientid.cl_id = new->cl_clientid.cl_id;
+ exid->clientid.cl_boot = conf->cl_clientid.cl_boot;
+ exid->clientid.cl_id = conf->cl_clientid.cl_id;

- exid->seqid = new->cl_cs_slot.sl_seqid + 1;
- nfsd4_set_ex_flags(new, exid);
+ exid->seqid = conf->cl_cs_slot.sl_seqid + 1;
+ nfsd4_set_ex_flags(conf, exid);

dprintk("nfsd4_exchange_id seqid %d flags %x\n",
- new->cl_cs_slot.sl_seqid, new->cl_exchange_flags);
+ conf->cl_cs_slot.sl_seqid, conf->cl_exchange_flags);
status = nfs_ok;

out:
nfs4_unlock_state();
+ if (new)
+ free_client(new);
return status;
}

@@ -2595,6 +2596,9 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
__be32 status;
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);

+ new = create_client(clname, rqstp, &clverifier);
+ if (new == NULL)
+ return nfserr_jukebox;
/* Cases below refer to rfc 3530 section 14.2.33: */
nfs4_lock_state();
conf = find_confirmed_client_by_name(&clname, nn);
@@ -2615,10 +2619,6 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
unconf = find_unconfirmed_client_by_name(&clname, nn);
if (unconf)
expire_client(unconf);
- status = nfserr_jukebox;
- new = create_client(clname, rqstp, &clverifier);
- if (new == NULL)
- goto out;
if (conf && same_verf(&conf->cl_verifier, &clverifier))
/* case 1: probable callback update */
copy_clid(new, conf);
@@ -2630,9 +2630,12 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
setclid->se_clientid.cl_boot = new->cl_clientid.cl_boot;
setclid->se_clientid.cl_id = new->cl_clientid.cl_id;
memcpy(setclid->se_confirm.data, new->cl_confirm.data, sizeof(setclid->se_confirm.data));
+ new = NULL;
status = nfs_ok;
out:
nfs4_unlock_state();
+ if (new)
+ free_client(new);
return status;
}

--
1.9.0


2014-04-18 18:46:24

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 51/70] NFSd: Convert nfsd4_process_open1() to work with lookup_clientid()

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4state.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 6e2d348d3367..d316fe10b9aa 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -70,6 +70,7 @@ static u64 current_sessionid = 1;

/* forward declarations */
static int check_for_locks(struct nfs4_file *filp, struct nfs4_lockowner *lowner);
+static __be32 lookup_clientid(clientid_t *clid, struct nfsd4_session *session, struct nfsd_net *nn, struct nfs4_client **clp);

/* Locking: */

@@ -3022,10 +3023,9 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate,
oo = find_openstateowner_str(strhashval, open, cstate->minorversion, nn);
open->op_openowner = oo;
if (!oo) {
- clp = find_confirmed_client(clientid, cstate->minorversion,
- nn);
- if (clp == NULL)
- return nfserr_expired;
+ status = lookup_clientid(clientid, cstate->session, nn, &clp);
+ if (status)
+ return status;
goto new_owner;
}
if (!(oo->oo_flags & NFS4_OO_CONFIRMED)) {
--
1.9.0


2014-04-19 14:35:51

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH 15/70] NFSd: Add locking to the nfs4_file->fi_fds[] array

> +/* XXX: for first cut may fall back on returning file that doesn't work
> + * at all? */

I think moving this code around might be a good opportunity to remove
this confusing comment.

> +static struct file *find_writeable_file(struct nfs4_file *f)
> +{
> + struct file *ret;
> +
> + spin_lock(&f->fi_lock);
> + ret = __nfs4_get_fd(f, O_WRONLY);
> + if (!ret)
> + ret = __nfs4_get_fd(f, O_RDWR);
> + spin_unlock(&f->fi_lock);
> + return ret;
> +}
> +
> +static struct file *find_readable_file(struct nfs4_file *f)
> +{
> + struct file *ret;
> +
> + spin_lock(&f->fi_lock);
> + ret = __nfs4_get_fd(f, O_RDONLY);
> + if (!ret)
> + ret = __nfs4_get_fd(f, O_RDWR);
> + spin_unlock(&f->fi_lock);
> + return ret;

Seems like these two functions could be easily consolidated by passing
a single flags argument.


2014-05-06 23:41:42

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH 11/70] NFSd: Ensure delegation setup is safe w.r.t. break_lease()

On Fri, Apr 18, 2014 at 02:44:05PM -0400, Trond Myklebust wrote:
> Ensure that we add/remove the dl_perfile under the inode->i_lock

Looks like maybe this addresses my question on the previous patch--in
which case it should really be merged with the previous patch for
bisectability?

--b.

>
> Signed-off-by: Trond Myklebust <[email protected]>
> ---
> fs/nfsd/nfs4state.c | 14 +++++++++++---
> 1 file changed, 11 insertions(+), 3 deletions(-)
>
> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> index 85fcbc9ebd40..3656980b5d19 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -441,7 +441,9 @@ static void
> hash_delegation_locked(struct nfs4_delegation *dp, struct nfs4_file *fp)
> {
> dp->dl_stid.sc_type = NFS4_DELEG_STID;
> + spin_lock(&fp->fi_inode->i_lock);
> list_add(&dp->dl_perfile, &fp->fi_delegations);
> + spin_unlock(&fp->fi_inode->i_lock);
> list_add(&dp->dl_perclnt, &dp->dl_stid.sc_client->cl_delegations);
> }
>
> @@ -449,13 +451,19 @@ hash_delegation_locked(struct nfs4_delegation *dp, struct nfs4_file *fp)
> static void
> unhash_delegation(struct nfs4_delegation *dp)
> {
> + struct nfs4_file *fp = dp->dl_file;
> +
> spin_lock(&state_lock);
> list_del_init(&dp->dl_perclnt);
> - list_del_init(&dp->dl_perfile);
> list_del_init(&dp->dl_recall_lru);
> + if (!list_empty(&dp->dl_perfile)) {
> + spin_lock(&fp->fi_inode->i_lock);
> + list_del_init(&dp->dl_perfile);
> + spin_unlock(&fp->fi_inode->i_lock);
> + }
> spin_unlock(&state_lock);
> - nfs4_put_deleg_lease(dp->dl_file);
> - put_nfs4_file(dp->dl_file);
> + nfs4_put_deleg_lease(fp);
> + put_nfs4_file(fp);
> dp->dl_file = NULL;
> }
>
> --
> 1.9.0
>

2014-05-06 16:36:56

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH 01/70] NFSd: Ensure we clear the cstate->slot in nfsd4_proc_compound

On Tue, May 06, 2014 at 11:32:00AM -0400, Bruce Fields wrote:
> On Fri, Apr 18, 2014 at 02:43:55PM -0400, Trond Myklebust wrote:
> > Otherwise, we may end up triggering all those nfsd4_has_session()
> > tests.
>
> Surely we'd have seen failures if this was actually not zeroed....
>
> Right, I think the
>
> memset(rqstp->rq_argp, 0, procp->pc_argsize);
>
> in svc_process_common() handles this.
>
> But apparently the lack of an explicit assignment was confusing. So
> maybe it's better to have one.
>
> (Or if not, to get rid of the other unnecessary assignments.)

(Dropping, assuming you'll resend with updated changelog if you feel
strongly about it....).

>
> --b.
>
> >
> > Signed-off-by: Trond Myklebust <[email protected]>
> > ---
> > fs/nfsd/nfs4proc.c | 1 +
> > 1 file changed, 1 insertion(+)
> >
> > diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
> > index d543222babf3..8eabbfb25441 100644
> > --- a/fs/nfsd/nfs4proc.c
> > +++ b/fs/nfsd/nfs4proc.c
> > @@ -1293,6 +1293,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
> > cstate->minorversion = args->minorversion;
> > cstate->replay_owner = NULL;
> > cstate->session = NULL;
> > + cstate->slot = NULL;
> > fh_init(current_fh, NFS4_FHSIZE);
> > fh_init(save_fh, NFS4_FHSIZE);
> > /*
> > --
> > 1.9.0
> >

2014-05-06 15:32:00

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH 01/70] NFSd: Ensure we clear the cstate->slot in nfsd4_proc_compound

On Fri, Apr 18, 2014 at 02:43:55PM -0400, Trond Myklebust wrote:
> Otherwise, we may end up triggering all those nfsd4_has_session()
> tests.

Surely we'd have seen failures if this was actually not zeroed....

Right, I think the

memset(rqstp->rq_argp, 0, procp->pc_argsize);

in svc_process_common() handles this.

But apparently the lack of an explicit assignment was confusing. So
maybe it's better to have one.

(Or if not, to get rid of the other unnecessary assignments.)

--b.

>
> Signed-off-by: Trond Myklebust <[email protected]>
> ---
> fs/nfsd/nfs4proc.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
> index d543222babf3..8eabbfb25441 100644
> --- a/fs/nfsd/nfs4proc.c
> +++ b/fs/nfsd/nfs4proc.c
> @@ -1293,6 +1293,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
> cstate->minorversion = args->minorversion;
> cstate->replay_owner = NULL;
> cstate->session = NULL;
> + cstate->slot = NULL;
> fh_init(current_fh, NFS4_FHSIZE);
> fh_init(save_fh, NFS4_FHSIZE);
> /*
> --
> 1.9.0
>

2014-05-08 19:48:51

by Trond Myklebust

[permalink] [raw]
Subject: Re: [PATCH 37/70] NFSd: nfs4_preprocess_seqid_op should only set *stpp on success

On Wed, May 7, 2014 at 3:58 PM, Bruce Fields <[email protected]> wrote:
> Patch-sequencing nit: I'm guessing this wasn't actually a problem until
> previous patches added sequenceid reference counting, turning this case
> into a reference leak.
>
> Therefore to avoid a temporary regression we should probably make this
> change before introducing the reference counting rather than after.

No, there should be no regression. This patch is a cleanup in
preparation for the introduction of reference counting.

> --b.
>
> On Fri, Apr 18, 2014 at 02:44:31PM -0400, Trond Myklebust wrote:
>> Signed-off-by: Trond Myklebust <[email protected]>
>> ---
>> fs/nfsd/nfs4state.c | 10 +++++++---
>> 1 file changed, 7 insertions(+), 3 deletions(-)
>>
>> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
>> index b9d6da652fb1..03a3f51d2828 100644
>> --- a/fs/nfsd/nfs4state.c
>> +++ b/fs/nfsd/nfs4state.c
>> @@ -4068,6 +4068,7 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
>> {
>> __be32 status;
>> struct nfs4_stid *s;
>> + struct nfs4_ol_stateid *stp = NULL;
>>
>> dprintk("NFSD: %s: seqid=%d stateid = " STATEID_FMT "\n", __func__,
>> seqid, STATEID_VAL(stateid));
>> @@ -4077,10 +4078,13 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
>> cstate->minorversion, nn);
>> if (status)
>> return status;
>> - *stpp = openlockstateid(s);
>> - nfsd4_cstate_assign_replay(cstate, (*stpp)->st_stateowner);
>> + stp = openlockstateid(s);
>> + nfsd4_cstate_assign_replay(cstate, stp->st_stateowner);
>>
>> - return nfs4_seqid_op_checks(cstate, stateid, seqid, *stpp);
>> + status = nfs4_seqid_op_checks(cstate, stateid, seqid, stp);
>> + if (!status)
>> + *stpp = stp;
>> + return status;
>> }
>>
>> static __be32 nfs4_preprocess_confirmed_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
>> --
>> 1.9.0
>>



--
Trond Myklebust

Linux NFS client maintainer, PrimaryData

[email protected]

2014-05-06 23:40:42

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH 10/70] NFSd: Avoid taking state_lock while holding inode lock in nfsd_break_one_deleg

On Fri, Apr 18, 2014 at 02:44:04PM -0400, Trond Myklebust wrote:
> state_lock is a heavily contended global lock. We don't want to grab
> that while simultaneously holding the inode->i_lock
> Instead do the list manipulations from the work queue context prior to
> starting the rpc call.
>
> Signed-off-by: Trond Myklebust <[email protected]>
> ---
> fs/nfsd/nfs4callback.c | 18 ++++++++++++++++--
> fs/nfsd/nfs4state.c | 25 +++++++++++++++----------
> fs/nfsd/state.h | 1 +
> 3 files changed, 32 insertions(+), 12 deletions(-)
>
> diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
> index 39c8ef875f91..8626cd6af4d1 100644
> --- a/fs/nfsd/nfs4callback.c
> +++ b/fs/nfsd/nfs4callback.c
> @@ -1009,9 +1009,8 @@ static void nfsd4_process_cb_update(struct nfsd4_callback *cb)
> run_nfsd4_cb(cb);
> }
>
> -static void nfsd4_do_callback_rpc(struct work_struct *w)
> +static void nfsd4_run_callback_rpc(struct nfsd4_callback *cb)
> {
> - struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, cb_work);
> struct nfs4_client *clp = cb->cb_clp;
> struct rpc_clnt *clnt;
>
> @@ -1029,11 +1028,25 @@ static void nfsd4_do_callback_rpc(struct work_struct *w)
> cb->cb_ops, cb);
> }
>
> +static void nfsd4_do_callback_rpc(struct work_struct *w)
> +{
> + struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, cb_work);
> + nfsd4_run_callback_rpc(cb);
> +}
> +
> void nfsd4_init_callback(struct nfsd4_callback *cb)
> {
> INIT_WORK(&cb->cb_work, nfsd4_do_callback_rpc);
> }
>
> +static void nfsd4_do_cb_recall(struct work_struct *w)
> +{
> + struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, cb_work);
> +
> + nfsd4_prepare_cb_recall(cb->cb_op);
> + nfsd4_run_callback_rpc(cb);
> +}
> +
> void nfsd4_cb_recall(struct nfs4_delegation *dp)
> {
> struct nfsd4_callback *cb = &dp->dl_recall;
> @@ -1050,6 +1063,7 @@ void nfsd4_cb_recall(struct nfs4_delegation *dp)
>
> INIT_LIST_HEAD(&cb->cb_per_client);
> cb->cb_done = true;
> + INIT_WORK(&cb->cb_work, nfsd4_do_cb_recall);
>
> run_nfsd4_cb(&dp->dl_recall);
> }
> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> index 6d691aa2bb27..85fcbc9ebd40 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -2743,24 +2743,31 @@ out:
> return ret;
> }
>
> -static void nfsd_break_one_deleg(struct nfs4_delegation *dp)
> +void nfsd4_prepare_cb_recall(struct nfs4_delegation *dp)
> {
> struct nfs4_client *clp = dp->dl_stid.sc_client;
> struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
>
> - lockdep_assert_held(&state_lock);
> + /*
> + * We can't do this in nfsd_break_deleg_cb because it is
> + * already holding inode->i_lock
> + */
> + spin_lock(&state_lock);
> + if (list_empty(&dp->dl_recall_lru)) {
> + dp->dl_time = get_seconds();
> + list_add_tail(&dp->dl_recall_lru, &nn->del_recall_lru);
> + }
> + spin_unlock(&state_lock);
> +}
> +
> +static void nfsd_break_one_deleg(struct nfs4_delegation *dp)
> +{
> /* We're assuming the state code never drops its reference
> * without first removing the lease. Since we're in this lease
> * callback (and since the lease code is serialized by the kernel
> * lock) we know the server hasn't removed the lease yet, we know
> * it's safe to take a reference: */
> atomic_inc(&dp->dl_count);
> -
> - list_add_tail(&dp->dl_recall_lru, &nn->del_recall_lru);
> -
> - /* Only place dl_time is set; protected by i_lock: */
> - dp->dl_time = get_seconds();
> -
> nfsd4_cb_recall(dp);
> }
>
> @@ -2785,11 +2792,9 @@ static void nfsd_break_deleg_cb(struct file_lock *fl)
> */
> fl->fl_break_time = 0;
>
> - spin_lock(&state_lock);
> fp->fi_had_conflict = true;
> list_for_each_entry(dp, &fp->fi_delegations, dl_perfile)
> nfsd_break_one_deleg(dp);
> - spin_unlock(&state_lock);

Why do we know it's safe to traverse this list without holding
state_lock?

--b.

> }
>
> static
> diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
> index 1aa22f39fc65..02c5a203c738 100644
> --- a/fs/nfsd/state.h
> +++ b/fs/nfsd/state.h
> @@ -473,6 +473,7 @@ extern void nfsd4_cb_recall(struct nfs4_delegation *dp);
> extern int nfsd4_create_callback_queue(void);
> extern void nfsd4_destroy_callback_queue(void);
> extern void nfsd4_shutdown_callback(struct nfs4_client *);
> +extern void nfsd4_prepare_cb_recall(struct nfs4_delegation *dp);
> extern void nfs4_put_delegation(struct nfs4_delegation *dp);
> extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(const char *name,
> struct nfsd_net *nn);
> --
> 1.9.0
>

2014-05-07 19:25:34

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH 29/70] NFSd: Add a struct nfs4_file field to struct nfs4_stid

We're adding a put without adding a corresponding get, so was there a
leak before this patch?

--b.

On Fri, Apr 18, 2014 at 02:44:23PM -0400, Trond Myklebust wrote:
> All stateids are associated with a nfs4_file. Let's consolidate...
>
> Signed-off-by: Trond Myklebust <[email protected]>
> ---
> fs/nfsd/nfs4state.c | 2 ++
> fs/nfsd/state.h | 1 +
> 2 files changed, 3 insertions(+)
>
> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> index 32ab3f1c83f8..5bbef4720e7c 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -474,6 +474,8 @@ static void remove_stid(struct nfs4_stid *s)
>
> static void nfs4_free_stid(struct kmem_cache *slab, struct nfs4_stid *s)
> {
> + if (s->sc_file)
> + put_nfs4_file(s->sc_file);
> kmem_cache_free(slab, s);
> }
>
> diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
> index 9d0088c244a8..c6deef936693 100644
> --- a/fs/nfsd/state.h
> +++ b/fs/nfsd/state.h
> @@ -85,6 +85,7 @@ struct nfs4_stid {
> unsigned char sc_type;
> stateid_t sc_stateid;
> struct nfs4_client *sc_client;
> + struct nfs4_file *sc_file;
> };
>
> struct nfs4_delegation {
> --
> 1.9.0
>

2014-05-06 16:37:18

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH 02/70] NFSd: Move default initialisers from create_client() to alloc_client()

On Fri, Apr 18, 2014 at 02:43:56PM -0400, Trond Myklebust wrote:
> Aside from making it clearer what is non-trivial in create_client(), it
> also fixes a bug whereby we can call free_client() before idr_init()
> has been called.

Thanks, applying for 3.15 and stable.

--b.

>
> Signed-off-by: Trond Myklebust <[email protected]>
> ---
> fs/nfsd/nfs4state.c | 24 ++++++++++++------------
> 1 file changed, 12 insertions(+), 12 deletions(-)
>
> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> index 3ba65979a3cd..230d21cb1717 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -1078,6 +1078,18 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name)
> return NULL;
> }
> clp->cl_name.len = name.len;
> + INIT_LIST_HEAD(&clp->cl_sessions);
> + idr_init(&clp->cl_stateids);
> + atomic_set(&clp->cl_refcount, 0);
> + clp->cl_cb_state = NFSD4_CB_UNKNOWN;
> + INIT_LIST_HEAD(&clp->cl_idhash);
> + INIT_LIST_HEAD(&clp->cl_openowners);
> + INIT_LIST_HEAD(&clp->cl_delegations);
> + INIT_LIST_HEAD(&clp->cl_lru);
> + INIT_LIST_HEAD(&clp->cl_callbacks);
> + INIT_LIST_HEAD(&clp->cl_revoked);
> + spin_lock_init(&clp->cl_lock);
> + rpc_init_wait_queue(&clp->cl_cb_waitq, "Backchannel slot table");
> return clp;
> }
>
> @@ -1347,7 +1359,6 @@ static struct nfs4_client *create_client(struct xdr_netobj name,
> if (clp == NULL)
> return NULL;
>
> - INIT_LIST_HEAD(&clp->cl_sessions);
> ret = copy_cred(&clp->cl_cred, &rqstp->rq_cred);
> if (ret) {
> spin_lock(&nn->client_lock);
> @@ -1355,20 +1366,9 @@ static struct nfs4_client *create_client(struct xdr_netobj name,
> spin_unlock(&nn->client_lock);
> return NULL;
> }
> - idr_init(&clp->cl_stateids);
> - atomic_set(&clp->cl_refcount, 0);
> - clp->cl_cb_state = NFSD4_CB_UNKNOWN;
> - INIT_LIST_HEAD(&clp->cl_idhash);
> - INIT_LIST_HEAD(&clp->cl_openowners);
> - INIT_LIST_HEAD(&clp->cl_delegations);
> - INIT_LIST_HEAD(&clp->cl_lru);
> - INIT_LIST_HEAD(&clp->cl_callbacks);
> - INIT_LIST_HEAD(&clp->cl_revoked);
> - spin_lock_init(&clp->cl_lock);
> nfsd4_init_callback(&clp->cl_cb_null);
> clp->cl_time = get_seconds();
> clear_bit(0, &clp->cl_cb_slot_busy);
> - rpc_init_wait_queue(&clp->cl_cb_waitq, "Backchannel slot table");
> copy_verf(clp, verf);
> rpc_copy_addr((struct sockaddr *) &clp->cl_addr, sa);
> gen_confirm(clp);
> --
> 1.9.0
>

2014-05-07 19:58:24

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH 37/70] NFSd: nfs4_preprocess_seqid_op should only set *stpp on success

Patch-sequencing nit: I'm guessing this wasn't actually a problem until
previous patches added sequenceid reference counting, turning this case
into a reference leak.

Therefore to avoid a temporary regression we should probably make this
change before introducing the reference counting rather than after.

--b.

On Fri, Apr 18, 2014 at 02:44:31PM -0400, Trond Myklebust wrote:
> Signed-off-by: Trond Myklebust <[email protected]>
> ---
> fs/nfsd/nfs4state.c | 10 +++++++---
> 1 file changed, 7 insertions(+), 3 deletions(-)
>
> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> index b9d6da652fb1..03a3f51d2828 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -4068,6 +4068,7 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
> {
> __be32 status;
> struct nfs4_stid *s;
> + struct nfs4_ol_stateid *stp = NULL;
>
> dprintk("NFSD: %s: seqid=%d stateid = " STATEID_FMT "\n", __func__,
> seqid, STATEID_VAL(stateid));
> @@ -4077,10 +4078,13 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
> cstate->minorversion, nn);
> if (status)
> return status;
> - *stpp = openlockstateid(s);
> - nfsd4_cstate_assign_replay(cstate, (*stpp)->st_stateowner);
> + stp = openlockstateid(s);
> + nfsd4_cstate_assign_replay(cstate, stp->st_stateowner);
>
> - return nfs4_seqid_op_checks(cstate, stateid, seqid, *stpp);
> + status = nfs4_seqid_op_checks(cstate, stateid, seqid, stp);
> + if (!status)
> + *stpp = stp;
> + return status;
> }
>
> static __be32 nfs4_preprocess_confirmed_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
> --
> 1.9.0
>

2014-05-07 20:29:32

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH 00/70] NFSd lock scalability patches

On Sat, Apr 19, 2014 at 04:58:15PM -0400, Bruce Fields wrote:
> On Fri, Apr 18, 2014 at 02:43:54PM -0400, Trond Myklebust wrote:
> > In accordance with the traditional Good Friday theme of "suffering", here
> > is the first draft of the NFSd lock scalability patches for your perusal.
> > I've only done light testing so far, but since there is a total of 70
> > patches to review, I figured you might want to start sooner rather than
> > waiting for the QA folks to finish testing.
>
> Thanks!
>
> Yeah, this sounds like too much suffering for vacation time, but I'll at
> least make a stab at merging the initial bug fixes next week, then try
> to plow through these (or any revised version (thanks, Christoph, for
> help reviewing!)) after I get back in a couple more weeks.

I've at least skimmed through them all, and that's all the suffering I
can handle for now. I'm assuming you'll send a new version soon.

I'm applying a few of these to my for-3.16 branch, could you rebase
future versions to that to avoid resending already-reviewed patches?

How are you testing?

Thanks again for taking on this difficult and long-overdue project....

--b.

2014-05-06 16:48:30

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH 06/70] nfsd4: use cl_lock to synchronize all stateid idr calls

Might be worth adding to the changelog something like "not currently
necessary, but will be once we drop the state lock here" to this and
similar patches (assuming that's accurate), just to make it clear what's
a bugfix and what isn't.

--b.

On Fri, Apr 18, 2014 at 02:44:00PM -0400, Trond Myklebust wrote:
> From: Benny Halevy <[email protected]>
>
> Signed-off-by: Benny Halevy <[email protected]>
> ---
> fs/nfsd/nfs4state.c | 15 +++++++++++++--
> 1 file changed, 13 insertions(+), 2 deletions(-)
>
> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> index 34273ca482c3..626f310a74a8 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -334,7 +334,11 @@ kmem_cache *slab)
> if (!stid)
> return NULL;
>
> - new_id = idr_alloc_cyclic(stateids, stid, 0, 0, GFP_KERNEL);
> + idr_preload(GFP_KERNEL);
> + spin_lock(&cl->cl_lock);
> + new_id = idr_alloc_cyclic(stateids, stid, 0, 0, GFP_NOWAIT);
> + spin_unlock(&cl->cl_lock);
> + idr_preload_end();
> if (new_id < 0)
> goto out_free;
> stid->sc_client = cl;
> @@ -397,9 +401,12 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv
>
> static void remove_stid(struct nfs4_stid *s)
> {
> - struct idr *stateids = &s->sc_client->cl_stateids;
> + struct nfs4_client *clp = s->sc_client;
> + struct idr *stateids = &clp->cl_stateids;
>
> + spin_lock(&clp->cl_lock);
> idr_remove(stateids, s->sc_stateid.si_opaque.so_id);
> + spin_unlock(&clp->cl_lock);
> }
>
> static void nfs4_free_stid(struct kmem_cache *slab, struct nfs4_stid *s)
> @@ -1110,7 +1117,9 @@ free_client(struct nfs4_client *clp)
> rpc_destroy_wait_queue(&clp->cl_cb_waitq);
> free_svc_cred(&clp->cl_cred);
> kfree(clp->cl_name.data);
> + spin_lock(&clp->cl_lock);
> idr_destroy(&clp->cl_stateids);
> + spin_unlock(&clp->cl_lock);
> kfree(clp);
> }
>
> @@ -1329,7 +1338,9 @@ static struct nfs4_stid *find_stateid(struct nfs4_client *cl, stateid_t *t)
> {
> struct nfs4_stid *ret;
>
> + spin_lock(&cl->cl_lock);
> ret = idr_find(&cl->cl_stateids, t->si_opaque.so_id);
> + spin_unlock(&cl->cl_lock);
> if (!ret || !ret->sc_type)
> return NULL;
> return ret;
> --
> 1.9.0
>

2014-05-06 16:37:34

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH 03/70] NFSd: call rpc_destroy_wait_queue() from free_client()

On Fri, Apr 18, 2014 at 02:43:57PM -0400, Trond Myklebust wrote:
> Mainly to ensure that we don't leave any hanging timers.

Thanks, applying for 3.15 and stable.

--b.

>
> Signed-off-by: Trond Myklebust <[email protected]>
> ---
> fs/nfsd/nfs4state.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> index 230d21cb1717..32b699bebb9c 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -1107,6 +1107,7 @@ free_client(struct nfs4_client *clp)
> WARN_ON_ONCE(atomic_read(&ses->se_ref));
> free_session(ses);
> }
> + rpc_destroy_wait_queue(&clp->cl_cb_waitq);
> free_svc_cred(&clp->cl_cred);
> kfree(clp->cl_name.data);
> idr_destroy(&clp->cl_stateids);
> --
> 1.9.0
>

2014-05-07 19:20:06

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH 27/70] NFSd: Don't let the laundromat reap clients that are referenced

This looks redundant with the check just made in
mark_client_expired_locked().

--b.

On Fri, Apr 18, 2014 at 02:44:21PM -0400, Trond Myklebust wrote:
> Signed-off-by: Trond Myklebust <[email protected]>
> ---
> fs/nfsd/nfs4state.c | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> index 273bdddb2458..41314e493110 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -3600,6 +3600,9 @@ nfs4_laundromat(struct nfsd_net *nn)
> clp->cl_clientid.cl_id);
> continue;
> }
> + /* Hey, I'm busy with this guy! */
> + if (atomic_read(&clp->cl_refcount) != 0)
> + continue;
> list_move(&clp->cl_lru, &reaplist);
> }
> spin_unlock(&nn->client_lock);
> --
> 1.9.0
>

2014-05-06 16:40:10

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH 04/70] NFSd: Remove 'inline' designation for free_client()

On Fri, Apr 18, 2014 at 02:43:58PM -0400, Trond Myklebust wrote:
> It is large, it is used in more than one place, and it is not performance
> critical. Let gcc figure out whether it should be inlined...

Thanks, applying for 3.16.

--b.

>
> Signed-off-by: Trond Myklebust <[email protected]>
> ---
> fs/nfsd/nfs4state.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> index 32b699bebb9c..841495aa9170 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -1093,7 +1093,7 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name)
> return clp;
> }
>
> -static inline void
> +static void
> free_client(struct nfs4_client *clp)
> {
> struct nfsd_net __maybe_unused *nn = net_generic(clp->net, nfsd_net_id);
> --
> 1.9.0
>

2014-05-09 07:34:10

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH 29/70] NFSd: Add a struct nfs4_file field to struct nfs4_stid

On Thu, May 08, 2014 at 03:40:49PM -0400, Trond Myklebust wrote:
> On Wed, May 7, 2014 at 3:25 PM, Bruce Fields <[email protected]> wrote:
> > We're adding a put without adding a corresponding get, so was there a
> > leak before this patch?
>
> No. The struct sc_file is added by this patch, and since the entire
> stid is zeroed on allocation (through the use of kmem_cache_zalloc),
> it is safe to add the put() in this patch.

It's safe, but it would still be nicer to have a single patch that just
moves the file from the delegation to the generic stateid.


2014-05-07 15:20:07

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH 19/70] NFSd: Allow lockowners to hold several stateids

Then did "NFSd: Lock owners are not per open stateid" introduce a
temporary regression?

--b.

On Fri, Apr 18, 2014 at 02:44:13PM -0400, Trond Myklebust wrote:
> Signed-off-by: Trond Myklebust <[email protected]>
> ---
> fs/nfsd/nfs4state.c | 46 +++++++++++++++++++++++++++++-----------------
> 1 file changed, 29 insertions(+), 17 deletions(-)
>
> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> index 0809e8355577..dad2f7b511b8 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -4390,6 +4390,19 @@ alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, struct
> return stp;
> }
>
> +static struct nfs4_ol_stateid *
> +find_lock_stateid(struct nfs4_lockowner *lo, struct inode *inode)
> +{
> + struct nfs4_ol_stateid *lst;
> +
> + list_for_each_entry(lst, &lo->lo_owner.so_stateids, st_perstateowner) {
> + if (lst->st_file->fi_inode == inode)
> + return lst;
> + }
> + return NULL;
> +}
> +
> +
> static int
> check_lock_length(u64 offset, u64 length)
> {
> @@ -4419,25 +4432,24 @@ static __be32 lookup_or_create_lock_state(struct nfsd4_compound_state *cstate, s
>
> lo = find_lockowner_str(fi->fi_inode, &cl->cl_clientid,
> &lock->v.new.owner, nn);
> - if (lo) {
> - if (!cstate->minorversion)
> - return nfserr_bad_seqid;
> - /* XXX: a lockowner always has exactly one stateid: */
> - *lst = list_first_entry(&lo->lo_owner.so_stateids,
> - struct nfs4_ol_stateid, st_perstateowner);
> - return nfs_ok;
> + if (!lo) {
> + strhashval = ownerstr_hashval(cl->cl_clientid.cl_id,
> + &lock->v.new.owner);
> + lo = alloc_init_lock_stateowner(strhashval, cl, ost, lock);
> + if (lo == NULL)
> + return nfserr_jukebox;
> }
> - strhashval = ownerstr_hashval(cl->cl_clientid.cl_id,
> - &lock->v.new.owner);
> - lo = alloc_init_lock_stateowner(strhashval, cl, ost, lock);
> - if (lo == NULL)
> - return nfserr_jukebox;
> - *lst = alloc_init_lock_stateid(lo, fi, ost);
> + if (!cstate->minorversion)
> + return nfserr_bad_seqid;
> + *lst = find_lock_stateid(lo, fi->fi_inode);
> if (*lst == NULL) {
> - release_lockowner(lo);
> - return nfserr_jukebox;
> + *lst = alloc_init_lock_stateid(lo, fi, ost);
> + if (*lst == NULL) {
> + release_lockowner_if_empty(lo);
> + return nfserr_jukebox;
> + }
> + *new = true;
> }
> - *new = true;
> return nfs_ok;
> }
>
> @@ -4596,7 +4608,7 @@ out:
> if (filp)
> fput(filp);
> if (status && new_state)
> - release_lockowner(lock_sop);
> + release_lockowner_if_empty(lock_sop);
> nfsd4_bump_seqid(cstate, status);
> nfs4_unlock_state();
> if (file_lock)
> --
> 1.9.0
>

2014-05-05 08:53:22

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH 60/70] NFSd: Ensure lookup_clientid() takes client_lock

> static __be32 nfsd4_get_session_locked(struct nfsd4_session *ses)
> {
> __be32 status;
> @@ -3614,18 +3623,27 @@ static __be32 lookup_clientid(clientid_t *clid, struct nfsd4_session *session, s
> {
> struct nfs4_client *found;
>
> + spin_lock(&nn->client_lock);
> if (session != NULL) {
> found = session->se_client;
> if (!same_clid(&found->cl_clientid, clid))
> - return nfserr_stale_clientid;
> + goto out_stale;

Do we really need the lock for the sessions case? I don't tink
se_client can change under us.

Btw, I wonder if it makes sense to split the current lookup_clientid
into two helpers for the sessions vs non-sessions case as all but one
caller already have conditionals for 4.0 vs later anyway, so something
like:

static struct nfs4_client *client_from_session(struct nfsd4_session *session,
clientid_t *clid)
{
if (!same_clid(&session->se_client->cl_clientid, clid))
return NULL;
return session->se_client;
}

static __be32 lookup_40_client(clientid_t *clid, struct nfsd_net *nn,
struct nfs4_client **clp)
{
...
}


2014-05-05 09:01:53

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH 30/70] NFSd: Replace delegation->dl_file with the dl_stid.sc_file

Btw, I think it would also be useful to move the dl_perfile and
st_perfile lists into nfs4_stid, and track the file associatation in
common code.


2014-05-22 12:20:52

by Jeff Layton

[permalink] [raw]
Subject: Re: [PATCH 19/70] NFSd: Allow lockowners to hold several stateids

On Wed, 7 May 2014 11:20:06 -0400
Bruce Fields <[email protected]> wrote:

> Then did "NFSd: Lock owners are not per open stateid" introduce a
> temporary regression?
>
> --b.
>

No, I don't think it does. Even after that patch, you still only have
one lock stateid per lockowner. That patch is just adding some plumbing
that will eventually allow you to break that limitation.

That said, there are some problems with locking during the traversal of
the so_stateids list in this set that I'm looking at today.

> On Fri, Apr 18, 2014 at 02:44:13PM -0400, Trond Myklebust wrote:
> > Signed-off-by: Trond Myklebust <[email protected]>
> > ---
> > fs/nfsd/nfs4state.c | 46 +++++++++++++++++++++++++++++-----------------
> > 1 file changed, 29 insertions(+), 17 deletions(-)
> >
> > diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> > index 0809e8355577..dad2f7b511b8 100644
> > --- a/fs/nfsd/nfs4state.c
> > +++ b/fs/nfsd/nfs4state.c
> > @@ -4390,6 +4390,19 @@ alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, struct
> > return stp;
> > }
> >
> > +static struct nfs4_ol_stateid *
> > +find_lock_stateid(struct nfs4_lockowner *lo, struct inode *inode)
> > +{
> > + struct nfs4_ol_stateid *lst;
> > +
> > + list_for_each_entry(lst, &lo->lo_owner.so_stateids, st_perstateowner) {
> > + if (lst->st_file->fi_inode == inode)
> > + return lst;
> > + }
> > + return NULL;
> > +}
> > +
> > +
> > static int
> > check_lock_length(u64 offset, u64 length)
> > {
> > @@ -4419,25 +4432,24 @@ static __be32 lookup_or_create_lock_state(struct nfsd4_compound_state *cstate, s
> >
> > lo = find_lockowner_str(fi->fi_inode, &cl->cl_clientid,
> > &lock->v.new.owner, nn);
> > - if (lo) {
> > - if (!cstate->minorversion)
> > - return nfserr_bad_seqid;
> > - /* XXX: a lockowner always has exactly one stateid: */
> > - *lst = list_first_entry(&lo->lo_owner.so_stateids,
> > - struct nfs4_ol_stateid, st_perstateowner);
> > - return nfs_ok;
> > + if (!lo) {
> > + strhashval = ownerstr_hashval(cl->cl_clientid.cl_id,
> > + &lock->v.new.owner);
> > + lo = alloc_init_lock_stateowner(strhashval, cl, ost, lock);
> > + if (lo == NULL)
> > + return nfserr_jukebox;
> > }
> > - strhashval = ownerstr_hashval(cl->cl_clientid.cl_id,
> > - &lock->v.new.owner);
> > - lo = alloc_init_lock_stateowner(strhashval, cl, ost, lock);
> > - if (lo == NULL)
> > - return nfserr_jukebox;
> > - *lst = alloc_init_lock_stateid(lo, fi, ost);
> > + if (!cstate->minorversion)
> > + return nfserr_bad_seqid;
> > + *lst = find_lock_stateid(lo, fi->fi_inode);
> > if (*lst == NULL) {
> > - release_lockowner(lo);
> > - return nfserr_jukebox;
> > + *lst = alloc_init_lock_stateid(lo, fi, ost);
> > + if (*lst == NULL) {
> > + release_lockowner_if_empty(lo);
> > + return nfserr_jukebox;
> > + }
> > + *new = true;
> > }
> > - *new = true;
> > return nfs_ok;
> > }
> >
> > @@ -4596,7 +4608,7 @@ out:
> > if (filp)
> > fput(filp);
> > if (status && new_state)
> > - release_lockowner(lock_sop);
> > + release_lockowner_if_empty(lock_sop);
> > nfsd4_bump_seqid(cstate, status);
> > nfs4_unlock_state();
> > if (file_lock)
> > --
> > 1.9.0
> >
> --
> To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html


--
Jeff Layton <[email protected]>

2014-05-07 16:21:43

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH 25/70] NFSd: Simplify stateid management

On Fri, Apr 18, 2014 at 02:44:19PM -0400, Trond Myklebust wrote:
> Don't allow stateids to clear the open file pointer until they are
> being destroyed.

It looks like this means the stateid will continue to reference the file
after close in the nfsv4.0 case when we keep the stateid around a little
while just to handle replays.

Ditto in the 4.1 case when we keep a revoked delegation around for
test_stateid/free_stateid.

I'd consider that a regression. People do occasionally notice when e.g.
a large unlinked file isn't promptly deallocated on close, and there may
be other cases where this matters.

--b.

>
> Signed-off-by: Trond Myklebust <[email protected]>
> ---
> fs/nfsd/nfs4state.c | 12 +++++-------
> 1 file changed, 5 insertions(+), 7 deletions(-)
>
> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> index afb767319f66..51ef179339ae 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -394,7 +394,7 @@ kmem_cache *slab)
> struct nfs4_stid *stid;
> int new_id;
>
> - stid = kmem_cache_alloc(slab, GFP_KERNEL);
> + stid = kmem_cache_zalloc(slab, GFP_KERNEL);
> if (!stid)
> return NULL;
>
> @@ -481,6 +481,8 @@ void
> nfs4_put_delegation(struct nfs4_delegation *dp)
> {
> if (atomic_dec_and_test(&dp->dl_stid.sc_count)) {
> + if (dp->dl_file)
> + put_nfs4_file(dp->dl_file);
> nfs4_free_stid(deleg_slab, &dp->dl_stid);
> num_delegations--;
> }
> @@ -528,10 +530,8 @@ unhash_delegation(struct nfs4_delegation *dp)
> spin_unlock(&fp->fi_inode->i_lock);
> }
> nfs4_put_deleg_lease(fp);
> - dp->dl_file = NULL;
> spin_unlock(&fp->fi_lock);
> spin_unlock(&state_lock);
> - put_nfs4_file(fp);
> }
>
>
> @@ -701,13 +701,13 @@ static void unhash_generic_stateid(struct nfs4_ol_stateid *stp)
> static void close_generic_stateid(struct nfs4_ol_stateid *stp)
> {
> release_all_access(stp);
> - put_nfs4_file(stp->st_file);
> - stp->st_file = NULL;
> }
>
> static void free_generic_stateid(struct nfs4_ol_stateid *stp)
> {
> remove_stid(&stp->st_stid);
> + if (stp->st_file)
> + put_nfs4_file(stp->st_file);
> nfs4_free_stid(stateid_slab, &stp->st_stid);
> }
>
> @@ -3265,8 +3265,6 @@ unlock_and_free:
> spin_unlock(&fp->fi_lock);
> spin_unlock(&state_lock);
> out_free:
> - put_nfs4_file(fp);
> - dp->dl_file = fp;
> return status;
> }
>
> --
> 1.9.0
>

2014-05-08 20:16:45

by Trond Myklebust

[permalink] [raw]
Subject: Re: [PATCH 00/70] NFSd lock scalability patches

On Wed, May 7, 2014 at 4:29 PM, Bruce Fields <[email protected]> wrote:
> On Sat, Apr 19, 2014 at 04:58:15PM -0400, Bruce Fields wrote:
>> On Fri, Apr 18, 2014 at 02:43:54PM -0400, Trond Myklebust wrote:
>> > In accordance with the traditional Good Friday theme of "suffering", here
>> > is the first draft of the NFSd lock scalability patches for your perusal.
>> > I've only done light testing so far, but since there is a total of 70
>> > patches to review, I figured you might want to start sooner rather than
>> > waiting for the QA folks to finish testing.
>>
>> Thanks!
>>
>> Yeah, this sounds like too much suffering for vacation time, but I'll at
>> least make a stab at merging the initial bug fixes next week, then try
>> to plow through these (or any revised version (thanks, Christoph, for
>> help reviewing!)) after I get back in a couple more weeks.

Yes. Thanks very much Christoph! I was worried that I would not get
enough reviewers for these patches.

> I've at least skimmed through them all, and that's all the suffering I
> can handle for now. I'm assuming you'll send a new version soon.

In addition to the issues that you and Christoph have raised, I've
also had to rethink some of the open() call. I'm not seeing how to
avoid moving the open share/deny locks into do_nfsd_create() without
getting into weird races, for instance.

> I'm applying a few of these to my for-3.16 branch, could you rebase
> future versions to that to avoid resending already-reviewed patches?

OK.

> How are you testing?

Mainly through plenty of runs of Connectathon tests for now. I want to
pass the code on to our QA folks for more extensive testing when it is
ready.

> Thanks again for taking on this difficult and long-overdue project....
>
> --b.



--
Trond Myklebust

Linux NFS client maintainer, PrimaryData

[email protected]

2014-05-16 18:19:25

by Jeff Layton

[permalink] [raw]
Subject: Re: [PATCH 58/70] NFSd: Protect session creation and client confirm using client_lock

On Fri, 18 Apr 2014 14:44:52 -0400
Trond Myklebust <[email protected]> wrote:

> In particular, we want to ensure that the move_to_confirmed() is
> protected by the nn->client_lock spin lock, so that we can use that
> when looking up the clientid etc.
>
> Signed-off-by: Trond Myklebust <[email protected]>
> ---
> fs/nfsd/nfs4state.c | 55 ++++++++++++++++++++++++++++++-----------------------
> 1 file changed, 31 insertions(+), 24 deletions(-)
>
> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> index 9c24031757d2..f047341678c0 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -130,17 +130,6 @@ static __be32 mark_client_expired_locked(struct nfs4_client *clp)
> return nfs_ok;
> }
>
> -static __be32 mark_client_expired(struct nfs4_client *clp)
> -{
> - struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
> - __be32 ret;
> -
> - spin_lock(&nn->client_lock);
> - ret = mark_client_expired_locked(clp);
> - spin_unlock(&nn->client_lock);
> - return ret;
> -}
>

This patch breaks the build when CONFIG_NFSD_FAULT_INJECTION is enabled
as mark_client_expired is still referenced by nfsd_forget_clients. The
attached patch should do the right thing.

Trond, feel free to roll it into your patch on the next iteration.


> static __be32 get_client_locked(struct nfs4_client *clp)
> {
> if (is_client_expired(clp))
> @@ -1134,12 +1123,10 @@ static void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, stru
> new->se_cb_sec = cses->cb_sec;
> atomic_set(&new->se_ref, 0);
> idx = hash_sessionid(&new->se_sessionid);
> - spin_lock(&nn->client_lock);
> list_add(&new->se_hash, &nn->sessionid_hashtbl[idx]);
> spin_lock(&clp->cl_lock);
> list_add(&new->se_perclnt, &clp->cl_sessions);
> spin_unlock(&clp->cl_lock);
> - spin_unlock(&nn->client_lock);
>
> if (cses->flags & SESSION4_BACK_CHAN) {
> struct sockaddr *sa = svc_addr(rqstp);
> @@ -2118,6 +2105,7 @@ nfsd4_create_session(struct svc_rqst *rqstp,
> {
> struct sockaddr *sa = svc_addr(rqstp);
> struct nfs4_client *conf, *unconf;
> + struct nfs4_client *old = NULL;
> struct nfsd4_session *new;
> struct nfsd4_conn *conn;
> struct nfsd4_clid_slot *cs_slot = NULL;
> @@ -2144,6 +2132,7 @@ nfsd4_create_session(struct svc_rqst *rqstp,
> goto out_free_session;
>
> nfs4_lock_state();
> + spin_lock(&nn->client_lock);
> unconf = find_unconfirmed_client(&cr_ses->clientid, true, nn);
> conf = find_confirmed_client(&cr_ses->clientid, true, nn);
> WARN_ON_ONCE(conf && unconf);
> @@ -2162,7 +2151,6 @@ nfsd4_create_session(struct svc_rqst *rqstp,
> goto out_free_conn;
> }
> } else if (unconf) {
> - struct nfs4_client *old;
> if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred) ||
> !rpc_cmp_addr(sa, (struct sockaddr *) &unconf->cl_addr)) {
> status = nfserr_clid_inuse;
> @@ -2180,10 +2168,10 @@ nfsd4_create_session(struct svc_rqst *rqstp,
> }
> old = find_confirmed_client_by_name(&unconf->cl_name, nn);
> if (old) {
> - status = mark_client_expired(old);
> + status = mark_client_expired_locked(old);
> if (status)
> goto out_free_conn;
> - expire_client(old);
> + unhash_client_locked(old);
> }
> move_to_confirmed(unconf);
> conf = unconf;
> @@ -2199,7 +2187,7 @@ nfsd4_create_session(struct svc_rqst *rqstp,
> cr_ses->flags &= ~SESSION4_RDMA;
>
> init_session(rqstp, new, conf, cr_ses);
> - nfsd4_init_conn(rqstp, conn, new);
> + nfsd4_get_session_locked(new);
>
> memcpy(cr_ses->sessionid.data, new->se_sessionid.data,
> NFS4_MAX_SESSIONID_LEN);
> @@ -2208,11 +2196,20 @@ nfsd4_create_session(struct svc_rqst *rqstp,
>
> /* cache solo and embedded create sessions under the state lock */
> nfsd4_cache_create_session(cr_ses, cs_slot, status);
> + spin_unlock(&nn->client_lock);
> + /* init connection and backchannel */
> + nfsd4_init_conn(rqstp, conn, new);
> + nfsd4_put_session(new);
> nfs4_unlock_state();
> + if (old)
> + expire_client(old);
> return status;
> out_free_conn:
> + spin_unlock(&nn->client_lock);
> nfs4_unlock_state();
> free_conn(conn);
> + if (old)
> + expire_client(old);
> out_free_session:
> __free_session(new);
> out_release_drc_mem:
> @@ -2657,6 +2654,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
> struct nfsd4_setclientid_confirm *setclientid_confirm)
> {
> struct nfs4_client *conf, *unconf;
> + struct nfs4_client *old = NULL;
> nfs4_verifier confirm = setclientid_confirm->sc_confirm;
> clientid_t * clid = &setclientid_confirm->sc_clientid;
> __be32 status;
> @@ -2666,6 +2664,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
> return nfserr_stale_clientid;
> nfs4_lock_state();
>
> + spin_lock(&nn->client_lock);
> conf = find_confirmed_client(clid, false, nn);
> unconf = find_unconfirmed_client(clid, false, nn);
> /*
> @@ -2689,21 +2688,29 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
> }
> status = nfs_ok;
> if (conf) { /* case 1: callback update */
> + old = unconf;
> + unhash_client_locked(old);
> nfsd4_change_callback(conf, &unconf->cl_cb_conn);
> - nfsd4_probe_callback(conf);
> - expire_client(unconf);
> } else { /* case 3: normal case; new or rebooted client */
> - conf = find_confirmed_client_by_name(&unconf->cl_name, nn);
> - if (conf) {
> - status = mark_client_expired(conf);
> + old = find_confirmed_client_by_name(&unconf->cl_name, nn);
> + if (old) {
> + status = mark_client_expired_locked(old);
> if (status)
> goto out;
> - expire_client(conf);
> + unhash_client_locked(old);
> }
> move_to_confirmed(unconf);
> - nfsd4_probe_callback(unconf);
> + conf = unconf;
> }
> + get_client_locked(conf);
> + spin_unlock(&nn->client_lock);
> + nfsd4_probe_callback(conf);
> + spin_lock(&nn->client_lock);
> + put_client_renew_locked(conf);
> out:
> + spin_unlock(&nn->client_lock);
> + if (old)
> + expire_client(old);
> nfs4_unlock_state();
> return status;
> }


--
Jeff Layton <[email protected]>


Attachments:
(No filename) (5.91 kB)
0001-nfsd-fix-build-when-CONFIG_NFSD_FAULT_INJECTION-is-e.patch (1.17 kB)
Download all attachments

2014-05-09 00:56:24

by Trond Myklebust

[permalink] [raw]
Subject: Re: [PATCH 11/70] NFSd: Ensure delegation setup is safe w.r.t. break_lease()

On Tue, May 6, 2014 at 7:41 PM, Bruce Fields <[email protected]> wrote:
> On Fri, Apr 18, 2014 at 02:44:05PM -0400, Trond Myklebust wrote:
>> Ensure that we add/remove the dl_perfile under the inode->i_lock
>
> Looks like maybe this addresses my question on the previous patch--in
> which case it should really be merged with the previous patch for
> bisectability?
>

We could, but the client_mutex will continue to mask any conflicts. In
the case where it doesn't, it would be because the existing code is
already borken.

It really is quite non-trivial to work out all the relationships that
are at work here, and to order the patches in a sane way. I'm quite
open to suggestions for improvements, but when in doubt, I'm relying
on the 'big umbrella' being up there until it is removed...


--
Trond Myklebust

Linux NFS client maintainer, PrimaryData

[email protected]

2014-05-30 12:05:16

by Jeff Layton

[permalink] [raw]
Subject: Re: [PATCH 10/70] NFSd: Avoid taking state_lock while holding inode lock in nfsd_break_one_deleg

On Thu, 8 May 2014 20:50:12 -0400
Trond Myklebust <[email protected]> wrote:

> On Tue, May 6, 2014 at 7:40 PM, Bruce Fields <[email protected]> wrote:
> > On Fri, Apr 18, 2014 at 02:44:04PM -0400, Trond Myklebust wrote:
> >> state_lock is a heavily contended global lock. We don't want to grab
> >> that while simultaneously holding the inode->i_lock
> >> Instead do the list manipulations from the work queue context prior to
> >> starting the rpc call.
> >>
> >> Signed-off-by: Trond Myklebust <[email protected]>
> >> ---
> >> fs/nfsd/nfs4callback.c | 18 ++++++++++++++++--
> >> fs/nfsd/nfs4state.c | 25 +++++++++++++++----------
> >> fs/nfsd/state.h | 1 +
> >> 3 files changed, 32 insertions(+), 12 deletions(-)
> >>
> >> diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
> >> index 39c8ef875f91..8626cd6af4d1 100644
> >> --- a/fs/nfsd/nfs4callback.c
> >> +++ b/fs/nfsd/nfs4callback.c
> >> @@ -1009,9 +1009,8 @@ static void nfsd4_process_cb_update(struct nfsd4_callback *cb)
> >> run_nfsd4_cb(cb);
> >> }
> >>
> >> -static void nfsd4_do_callback_rpc(struct work_struct *w)
> >> +static void nfsd4_run_callback_rpc(struct nfsd4_callback *cb)
> >> {
> >> - struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, cb_work);
> >> struct nfs4_client *clp = cb->cb_clp;
> >> struct rpc_clnt *clnt;
> >>
> >> @@ -1029,11 +1028,25 @@ static void nfsd4_do_callback_rpc(struct work_struct *w)
> >> cb->cb_ops, cb);
> >> }
> >>
> >> +static void nfsd4_do_callback_rpc(struct work_struct *w)
> >> +{
> >> + struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, cb_work);
> >> + nfsd4_run_callback_rpc(cb);
> >> +}
> >> +
> >> void nfsd4_init_callback(struct nfsd4_callback *cb)
> >> {
> >> INIT_WORK(&cb->cb_work, nfsd4_do_callback_rpc);
> >> }
> >>
> >> +static void nfsd4_do_cb_recall(struct work_struct *w)
> >> +{
> >> + struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, cb_work);
> >> +
> >> + nfsd4_prepare_cb_recall(cb->cb_op);
> >> + nfsd4_run_callback_rpc(cb);
> >> +}
> >> +
> >> void nfsd4_cb_recall(struct nfs4_delegation *dp)
> >> {
> >> struct nfsd4_callback *cb = &dp->dl_recall;
> >> @@ -1050,6 +1063,7 @@ void nfsd4_cb_recall(struct nfs4_delegation *dp)
> >>
> >> INIT_LIST_HEAD(&cb->cb_per_client);
> >> cb->cb_done = true;
> >> + INIT_WORK(&cb->cb_work, nfsd4_do_cb_recall);
> >>
> >> run_nfsd4_cb(&dp->dl_recall);
> >> }
> >> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> >> index 6d691aa2bb27..85fcbc9ebd40 100644
> >> --- a/fs/nfsd/nfs4state.c
> >> +++ b/fs/nfsd/nfs4state.c
> >> @@ -2743,24 +2743,31 @@ out:
> >> return ret;
> >> }
> >>
> >> -static void nfsd_break_one_deleg(struct nfs4_delegation *dp)
> >> +void nfsd4_prepare_cb_recall(struct nfs4_delegation *dp)
> >> {
> >> struct nfs4_client *clp = dp->dl_stid.sc_client;
> >> struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
> >>
> >> - lockdep_assert_held(&state_lock);
> >> + /*
> >> + * We can't do this in nfsd_break_deleg_cb because it is
> >> + * already holding inode->i_lock
> >> + */
> >> + spin_lock(&state_lock);
> >> + if (list_empty(&dp->dl_recall_lru)) {
> >> + dp->dl_time = get_seconds();
> >> + list_add_tail(&dp->dl_recall_lru, &nn->del_recall_lru);
> >> + }
> >> + spin_unlock(&state_lock);
> >> +}
> >> +
> >> +static void nfsd_break_one_deleg(struct nfs4_delegation *dp)
> >> +{
> >> /* We're assuming the state code never drops its reference
> >> * without first removing the lease. Since we're in this lease
> >> * callback (and since the lease code is serialized by the kernel
> >> * lock) we know the server hasn't removed the lease yet, we know
> >> * it's safe to take a reference: */
> >> atomic_inc(&dp->dl_count);
> >> -
> >> - list_add_tail(&dp->dl_recall_lru, &nn->del_recall_lru);
> >> -
> >> - /* Only place dl_time is set; protected by i_lock: */
> >> - dp->dl_time = get_seconds();
> >> -
> >> nfsd4_cb_recall(dp);
> >> }
> >>
> >> @@ -2785,11 +2792,9 @@ static void nfsd_break_deleg_cb(struct file_lock *fl)
> >> */
> >> fl->fl_break_time = 0;
> >>
> >> - spin_lock(&state_lock);
> >> fp->fi_had_conflict = true;
> >> list_for_each_entry(dp, &fp->fi_delegations, dl_perfile)
> >> nfsd_break_one_deleg(dp);
> >> - spin_unlock(&state_lock);
> >
> > Why do we know it's safe to traverse this list without holding
> > state_lock?
>
> We're in a callback that is already holding the inode->i_lock, and so
> this is why we need to involve inode->i_lock when hashing/unhashing
> the delegation.
>

Trond,

Yesterday on the town hall call, you mentioned that this patch would
prevent an ABBA deadlock in the existing code. I don't see that. The
existing code doesn't protect the fi_delegations list with the i_lock,
so we don't have any potential for deadlock there, AFAICT. Am I missing
something?

There *is* an existing bug in nfs4_setlease however. It adds to the
fi_delegations list without taking the recall_lock. Patch #8 in this
series fixes that.

I'll go ahead and send out that patch so we can get that fixed up, but
I'll hold off on this one though until the larger set is more ready as
it doesn't appear to be urgent.

> > --b.
> >
> >> }
> >>
> >> static
> >> diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
> >> index 1aa22f39fc65..02c5a203c738 100644
> >> --- a/fs/nfsd/state.h
> >> +++ b/fs/nfsd/state.h
> >> @@ -473,6 +473,7 @@ extern void nfsd4_cb_recall(struct nfs4_delegation *dp);
> >> extern int nfsd4_create_callback_queue(void);
> >> extern void nfsd4_destroy_callback_queue(void);
> >> extern void nfsd4_shutdown_callback(struct nfs4_client *);
> >> +extern void nfsd4_prepare_cb_recall(struct nfs4_delegation *dp);
> >> extern void nfs4_put_delegation(struct nfs4_delegation *dp);
> >> extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(const char *name,
> >> struct nfsd_net *nn);
> >> --
> >> 1.9.0
> >>
>
>
>


--
Jeff Layton <[email protected]>

2014-05-07 20:18:54

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH 50/70] NFSd: Use the session->se_client in lookup_clientid()

On Fri, Apr 18, 2014 at 02:44:44PM -0400, Trond Myklebust wrote:
> In NFSv4.x with x>0, we want to use the session's pointer to the
> nfs4_client in order to optimise away the extra lookup of the clid.
>
> Signed-off-by: Trond Myklebust <[email protected]>
> ---
> fs/nfsd/nfs4state.c | 25 +++++++++++++++----------
> 1 file changed, 15 insertions(+), 10 deletions(-)
>
> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> index 888acf114e40..6e2d348d3367 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -3571,13 +3571,19 @@ void nfsd4_cleanup_open_state(struct nfsd4_open *open, __be32 status)
> put_generic_stateid(open->op_stp);
> }
>
> -static __be32 lookup_clientid(clientid_t *clid, bool session, struct nfsd_net *nn, struct nfs4_client **clp)
> +static __be32 lookup_clientid(clientid_t *clid, struct nfsd4_session *session, struct nfsd_net *nn, struct nfs4_client **clp)
> {
> struct nfs4_client *found;
>
> - if (STALE_CLIENTID(clid, nn))
> - return nfserr_stale_clientid;
> - found = find_confirmed_client(clid, session, nn);
> + if (session != NULL) {
> + found = session->se_client;
> + if (!same_clid(&found->cl_clientid, clid))
> + return nfserr_stale_clientid;

Is that the right error? After your
a8a7c6776f8d74780348bef639581421d85a4376 "nfsd: Don't return
NFS4ERR_STALE_STATEID for NFSv4.1+", nfsd4_lookup_stateid will just
immediately map this to BAD_STATEID. (And the other callers aren't
relevant in the minorversion > 0 case.)

--b.

> + } else {
> + if (STALE_CLIENTID(clid, nn))
> + return nfserr_stale_clientid;
> + found = find_confirmed_client(clid, false, nn);
> + }
> if (clp)
> *clp = found;
> return found ? nfs_ok : nfserr_expired;
> @@ -3594,7 +3600,7 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
> nfs4_lock_state();
> dprintk("process_renew(%08x/%08x): starting\n",
> clid->cl_boot, clid->cl_id);
> - status = lookup_clientid(clid, cstate->minorversion, nn, &clp);
> + status = lookup_clientid(clid, NULL, nn, &clp);
> if (status)
> goto out;
> status = nfserr_cb_path_down;
> @@ -3879,14 +3885,13 @@ static __be32 nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate,
> {
> struct nfs4_client *cl;
> __be32 status;
> - bool sessions = cstate->minorversion != 0;
>
> if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
> return nfserr_bad_stateid;
> - status = lookup_clientid(&stateid->si_opaque.so_clid, sessions,
> + status = lookup_clientid(&stateid->si_opaque.so_clid, cstate->session,
> nn, &cl);
> if (status == nfserr_stale_clientid) {
> - if (sessions)
> + if (cstate->session)
> return nfserr_bad_stateid;
> return nfserr_stale_stateid;
> }
> @@ -4770,7 +4775,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
> nfs4_lock_state();
>
> if (!nfsd4_has_session(cstate)) {
> - status = lookup_clientid(&lockt->lt_clientid, false, nn, NULL);
> + status = lookup_clientid(&lockt->lt_clientid, NULL, nn, NULL);
> if (status)
> goto out;
> }
> @@ -4942,7 +4947,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
>
> nfs4_lock_state();
>
> - status = lookup_clientid(clid, cstate->minorversion, nn, NULL);
> + status = lookup_clientid(clid, NULL, nn, NULL);
> if (status)
> goto out;
>
> --
> 1.9.0
>

2014-05-09 00:50:13

by Trond Myklebust

[permalink] [raw]
Subject: Re: [PATCH 10/70] NFSd: Avoid taking state_lock while holding inode lock in nfsd_break_one_deleg

On Tue, May 6, 2014 at 7:40 PM, Bruce Fields <[email protected]> wrote:
> On Fri, Apr 18, 2014 at 02:44:04PM -0400, Trond Myklebust wrote:
>> state_lock is a heavily contended global lock. We don't want to grab
>> that while simultaneously holding the inode->i_lock
>> Instead do the list manipulations from the work queue context prior to
>> starting the rpc call.
>>
>> Signed-off-by: Trond Myklebust <[email protected]>
>> ---
>> fs/nfsd/nfs4callback.c | 18 ++++++++++++++++--
>> fs/nfsd/nfs4state.c | 25 +++++++++++++++----------
>> fs/nfsd/state.h | 1 +
>> 3 files changed, 32 insertions(+), 12 deletions(-)
>>
>> diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
>> index 39c8ef875f91..8626cd6af4d1 100644
>> --- a/fs/nfsd/nfs4callback.c
>> +++ b/fs/nfsd/nfs4callback.c
>> @@ -1009,9 +1009,8 @@ static void nfsd4_process_cb_update(struct nfsd4_callback *cb)
>> run_nfsd4_cb(cb);
>> }
>>
>> -static void nfsd4_do_callback_rpc(struct work_struct *w)
>> +static void nfsd4_run_callback_rpc(struct nfsd4_callback *cb)
>> {
>> - struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, cb_work);
>> struct nfs4_client *clp = cb->cb_clp;
>> struct rpc_clnt *clnt;
>>
>> @@ -1029,11 +1028,25 @@ static void nfsd4_do_callback_rpc(struct work_struct *w)
>> cb->cb_ops, cb);
>> }
>>
>> +static void nfsd4_do_callback_rpc(struct work_struct *w)
>> +{
>> + struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, cb_work);
>> + nfsd4_run_callback_rpc(cb);
>> +}
>> +
>> void nfsd4_init_callback(struct nfsd4_callback *cb)
>> {
>> INIT_WORK(&cb->cb_work, nfsd4_do_callback_rpc);
>> }
>>
>> +static void nfsd4_do_cb_recall(struct work_struct *w)
>> +{
>> + struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, cb_work);
>> +
>> + nfsd4_prepare_cb_recall(cb->cb_op);
>> + nfsd4_run_callback_rpc(cb);
>> +}
>> +
>> void nfsd4_cb_recall(struct nfs4_delegation *dp)
>> {
>> struct nfsd4_callback *cb = &dp->dl_recall;
>> @@ -1050,6 +1063,7 @@ void nfsd4_cb_recall(struct nfs4_delegation *dp)
>>
>> INIT_LIST_HEAD(&cb->cb_per_client);
>> cb->cb_done = true;
>> + INIT_WORK(&cb->cb_work, nfsd4_do_cb_recall);
>>
>> run_nfsd4_cb(&dp->dl_recall);
>> }
>> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
>> index 6d691aa2bb27..85fcbc9ebd40 100644
>> --- a/fs/nfsd/nfs4state.c
>> +++ b/fs/nfsd/nfs4state.c
>> @@ -2743,24 +2743,31 @@ out:
>> return ret;
>> }
>>
>> -static void nfsd_break_one_deleg(struct nfs4_delegation *dp)
>> +void nfsd4_prepare_cb_recall(struct nfs4_delegation *dp)
>> {
>> struct nfs4_client *clp = dp->dl_stid.sc_client;
>> struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
>>
>> - lockdep_assert_held(&state_lock);
>> + /*
>> + * We can't do this in nfsd_break_deleg_cb because it is
>> + * already holding inode->i_lock
>> + */
>> + spin_lock(&state_lock);
>> + if (list_empty(&dp->dl_recall_lru)) {
>> + dp->dl_time = get_seconds();
>> + list_add_tail(&dp->dl_recall_lru, &nn->del_recall_lru);
>> + }
>> + spin_unlock(&state_lock);
>> +}
>> +
>> +static void nfsd_break_one_deleg(struct nfs4_delegation *dp)
>> +{
>> /* We're assuming the state code never drops its reference
>> * without first removing the lease. Since we're in this lease
>> * callback (and since the lease code is serialized by the kernel
>> * lock) we know the server hasn't removed the lease yet, we know
>> * it's safe to take a reference: */
>> atomic_inc(&dp->dl_count);
>> -
>> - list_add_tail(&dp->dl_recall_lru, &nn->del_recall_lru);
>> -
>> - /* Only place dl_time is set; protected by i_lock: */
>> - dp->dl_time = get_seconds();
>> -
>> nfsd4_cb_recall(dp);
>> }
>>
>> @@ -2785,11 +2792,9 @@ static void nfsd_break_deleg_cb(struct file_lock *fl)
>> */
>> fl->fl_break_time = 0;
>>
>> - spin_lock(&state_lock);
>> fp->fi_had_conflict = true;
>> list_for_each_entry(dp, &fp->fi_delegations, dl_perfile)
>> nfsd_break_one_deleg(dp);
>> - spin_unlock(&state_lock);
>
> Why do we know it's safe to traverse this list without holding
> state_lock?

We're in a callback that is already holding the inode->i_lock, and so
this is why we need to involve inode->i_lock when hashing/unhashing
the delegation.

> --b.
>
>> }
>>
>> static
>> diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
>> index 1aa22f39fc65..02c5a203c738 100644
>> --- a/fs/nfsd/state.h
>> +++ b/fs/nfsd/state.h
>> @@ -473,6 +473,7 @@ extern void nfsd4_cb_recall(struct nfs4_delegation *dp);
>> extern int nfsd4_create_callback_queue(void);
>> extern void nfsd4_destroy_callback_queue(void);
>> extern void nfsd4_shutdown_callback(struct nfs4_client *);
>> +extern void nfsd4_prepare_cb_recall(struct nfs4_delegation *dp);
>> extern void nfs4_put_delegation(struct nfs4_delegation *dp);
>> extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(const char *name,
>> struct nfsd_net *nn);
>> --
>> 1.9.0
>>



--
Trond Myklebust

Linux NFS client maintainer, PrimaryData

[email protected]

2014-05-08 19:40:50

by Trond Myklebust

[permalink] [raw]
Subject: Re: [PATCH 29/70] NFSd: Add a struct nfs4_file field to struct nfs4_stid

On Wed, May 7, 2014 at 3:25 PM, Bruce Fields <[email protected]> wrote:
> We're adding a put without adding a corresponding get, so was there a
> leak before this patch?

No. The struct sc_file is added by this patch, and since the entire
stid is zeroed on allocation (through the use of kmem_cache_zalloc),
it is safe to add the put() in this patch.

>
> --b.
>
> On Fri, Apr 18, 2014 at 02:44:23PM -0400, Trond Myklebust wrote:
>> All stateids are associated with a nfs4_file. Let's consolidate...
>>
>> Signed-off-by: Trond Myklebust <[email protected]>
>> ---
>> fs/nfsd/nfs4state.c | 2 ++
>> fs/nfsd/state.h | 1 +
>> 2 files changed, 3 insertions(+)
>>
>> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
>> index 32ab3f1c83f8..5bbef4720e7c 100644
>> --- a/fs/nfsd/nfs4state.c
>> +++ b/fs/nfsd/nfs4state.c
>> @@ -474,6 +474,8 @@ static void remove_stid(struct nfs4_stid *s)
>>
>> static void nfs4_free_stid(struct kmem_cache *slab, struct nfs4_stid *s)
>> {
>> + if (s->sc_file)
>> + put_nfs4_file(s->sc_file);
>> kmem_cache_free(slab, s);
>> }
>>
>> diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
>> index 9d0088c244a8..c6deef936693 100644
>> --- a/fs/nfsd/state.h
>> +++ b/fs/nfsd/state.h
>> @@ -85,6 +85,7 @@ struct nfs4_stid {
>> unsigned char sc_type;
>> stateid_t sc_stateid;
>> struct nfs4_client *sc_client;
>> + struct nfs4_file *sc_file;
>> };
>>
>> struct nfs4_delegation {
>> --
>> 1.9.0
>>



--
Trond Myklebust

Linux NFS client maintainer, PrimaryData

[email protected]

2014-05-07 20:21:05

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH 50/70] NFSd: Use the session->se_client in lookup_clientid()

On Wed, May 07, 2014 at 04:18:54PM -0400, Bruce Fields wrote:
> On Fri, Apr 18, 2014 at 02:44:44PM -0400, Trond Myklebust wrote:
> > In NFSv4.x with x>0, we want to use the session's pointer to the
> > nfs4_client in order to optimise away the extra lookup of the clid.
> >
> > Signed-off-by: Trond Myklebust <[email protected]>
> > ---
> > fs/nfsd/nfs4state.c | 25 +++++++++++++++----------
> > 1 file changed, 15 insertions(+), 10 deletions(-)
> >
> > diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> > index 888acf114e40..6e2d348d3367 100644
> > --- a/fs/nfsd/nfs4state.c
> > +++ b/fs/nfsd/nfs4state.c
> > @@ -3571,13 +3571,19 @@ void nfsd4_cleanup_open_state(struct nfsd4_open *open, __be32 status)
> > put_generic_stateid(open->op_stp);
> > }
> >
> > -static __be32 lookup_clientid(clientid_t *clid, bool session, struct nfsd_net *nn, struct nfs4_client **clp)
> > +static __be32 lookup_clientid(clientid_t *clid, struct nfsd4_session *session, struct nfsd_net *nn, struct nfs4_client **clp)
> > {
> > struct nfs4_client *found;
> >
> > - if (STALE_CLIENTID(clid, nn))
> > - return nfserr_stale_clientid;
> > - found = find_confirmed_client(clid, session, nn);
> > + if (session != NULL) {
> > + found = session->se_client;
> > + if (!same_clid(&found->cl_clientid, clid))
> > + return nfserr_stale_clientid;
>
> Is that the right error? After your
> a8a7c6776f8d74780348bef639581421d85a4376 "nfsd: Don't return
> NFS4ERR_STALE_STATEID for NFSv4.1+", nfsd4_lookup_stateid will just
> immediately map this to BAD_STATEID. (And the other callers aren't
> relevant in the minorversion > 0 case.)

(OK, never mind, I guess STALE must be the right error for the callers
added in later patches.)

--b.

2014-05-07 18:46:42

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH 26/70] NFSd: Fix delegation revocation

On Fri, Apr 18, 2014 at 02:44:20PM -0400, Trond Myklebust wrote:
> Ensure that the delegations cannot be found by the laundromat etc once
> we add them to the various 'revoke' lists.

The current code does already attempt to do that... I guess the point
is that there's a race in that it leaves a window after a delegation is
added to one of the local lists but before it's actually unhashed, as
here:

> @@ -3612,7 +3620,8 @@ nfs4_laundromat(struct nfsd_net *nn)
> test_val = u;
> break;
> }
> - list_move(&dp->dl_recall_lru, &reaplist);
> + unhash_delegation_locked(dp);
> + list_add(&dp->dl_recall_lru, &reaplist);
> }
> spin_unlock(&state_lock);

where we've been dropping a lock with the thing still hashed.

OK, good, but maybe the changelog could be a little more precise about
what's being fixed.

Also it would be nice if this could be done earlier in the series as
it's a bugfix that people will probably want backported.

--b.