2014-07-21 15:03:02

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 00/40] nfsd: stateid and stateowner refcounting overhaul

Here is the next swath of patches for the nfsd client_mutex removal
series. The main focus of this series is to add refcounting to open and
lock stateids and to use those to drive their management and eventual
destruction. Additionally, we add refcounting to the open and lock
stateowners and use that refcount to manage their lifecycle as well.

Most of the new locking here is superfluous until we remove the
client_mutex. This series should be applied on top of the delegation
series I sent earlier today.

Benny Halevy (1):
nfsd4: use cl_lock to synchronize all stateid idr calls

Jeff Layton (17):
nfsd: Ensure atomicity of stateid destruction and idr tree removal
nfsd: Cleanup the freeing of stateids
nfsd: do filp_close in sc_free callback for lock stateids
nfsd: Add locking to protect the state owner lists
nfsd: clean up races in lock stateid searching and creation
nfsd: ensure atomicity in nfsd4_free_stateid and
nfsd4_validate_stateid
nfsd: clean up lockowner refcounting when finding them
nfsd: add an operation for unhashing a stateowner
nfsd: clean up refcounting for lockowners
nfsd: make openstateids hold references to their openowners
nfsd: don't allow CLOSE to proceed until refcount on stateid drops
nfsd: clean up and reorganize release_lockowner
nfsd: add locking to stateowner release
nfsd: optimize destroy_lockowner cl_lock thrashing
nfsd: close potential race in nfsd4_free_stateid
nfsd: reduce cl_lock thrashing in release_openowner
nfsd: don't thrash the cl_lock while freeing an open stateid

Trond Myklebust (22):
nfsd: Add reference counting to the lock and open stateids
nfsd: Add a struct nfs4_file field to struct nfs4_stid
nfsd: Replace nfs4_ol_stateid->st_file with the st_stid.sc_file
nfsd: Slight cleanup of find_stateid()
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: Add reference counting to state owners
nfsd: Add a mutex to protect the NFSv4.0 open owner replay cache
nfsd: Keep a reference to the open stateid for the NFSv4.0 replay
cache
nfsd: Make lock stateid take a reference to the lockowner
nfsd: Protect adding/removing open state owners using client_lock
nfsd: Protect adding/removing lock owners using client_lock
nfsd: Move the open owner hash table into struct nfs4_client

fs/nfsd/netns.h | 1 -
fs/nfsd/nfs4proc.c | 12 +-
fs/nfsd/nfs4state.c | 956 +++++++++++++++++++++++++++++++++++-----------------
fs/nfsd/nfs4xdr.c | 2 -
fs/nfsd/state.h | 12 +-
fs/nfsd/xdr4.h | 5 +-
6 files changed, 660 insertions(+), 328 deletions(-)

--
1.9.3



2014-07-21 15:03:15

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 11/40] nfsd: ensure atomicity in nfsd4_free_stateid and nfsd4_validate_stateid

Hold the cl_lock over the bulk of these functions. In addition to
ensuring that they aren't freed prematurely, this will also help prevent
a potential race that could be introduced later. Once we remove the
client_mutex, it'll be possible for FREE_STATEID and CLOSE to race and
for both to try to put the "persistent" reference to the stateid.

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index d83f1a27aded..70119d2a69fd 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1703,16 +1703,6 @@ find_stateid_locked(struct nfs4_client *cl, stateid_t *t)
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;
@@ -4115,10 +4105,10 @@ static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid)
{
struct nfs4_stid *s;
struct nfs4_ol_stateid *ols;
- __be32 status;
+ __be32 status = nfserr_bad_stateid;

if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
- return nfserr_bad_stateid;
+ return status;
/* Client debugging aid. */
if (!same_clid(&stateid->si_opaque.so_clid, &cl->cl_clientid)) {
char addr_str[INET6_ADDRSTRLEN];
@@ -4126,34 +4116,42 @@ static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid)
sizeof(addr_str));
pr_warn_ratelimited("NFSD: client %s testing state ID "
"with incorrect client ID\n", addr_str);
- return nfserr_bad_stateid;
+ return status;
}
- s = find_stateid(cl, stateid);
+ spin_lock(&cl->cl_lock);
+ s = find_stateid_locked(cl, stateid);
if (!s)
- return nfserr_bad_stateid;
+ goto out_unlock;
status = check_stateid_generation(stateid, &s->sc_stateid, 1);
if (status)
- return status;
+ goto out_unlock;
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);
/* Fallthrough */
case NFS4_CLOSED_STID:
case NFS4_CLOSED_DELEG_STID:
- return nfserr_bad_stateid;
+ status = nfserr_bad_stateid;
}
+out_unlock:
+ spin_unlock(&cl->cl_lock);
+ return status;
}

static __be32
@@ -4304,34 +4302,37 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
__be32 ret = nfserr_bad_stateid;

nfs4_lock_state();
- s = find_stateid(cl, stateid);
+ spin_lock(&cl->cl_lock);
+ s = find_stateid_locked(cl, stateid);
if (!s)
- goto out;
+ goto out_unlock;
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;
- if (s->sc_type == NFS4_LOCK_STID)
- ret = nfsd4_free_lock_stateid(openlockstateid(s));
- else
+ break;
+ if (s->sc_type != NFS4_LOCK_STID) {
ret = nfserr_locks_held;
- break;
+ break;
+ }
+ spin_unlock(&cl->cl_lock);
+ ret = nfsd4_free_lock_stateid(openlockstateid(s));
+ goto out;
case NFS4_REVOKED_DELEG_STID:
dp = delegstateid(s);
- spin_lock(&cl->cl_lock);
list_del_init(&dp->dl_recall_lru);
spin_unlock(&cl->cl_lock);
nfs4_put_delegation(dp);
ret = nfs_ok;
- break;
- default:
- ret = nfserr_bad_stateid;
+ goto out;
+ /* Default falls through and returns nfserr_bad_stateid */
}
+out_unlock:
+ spin_unlock(&cl->cl_lock);
out:
nfs4_unlock_state();
return ret;
--
1.9.3


2014-07-21 15:03:14

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 10/40] nfsd: Slight cleanup of find_stateid()

From: Trond Myklebust <[email protected]>

In preparation of reference counting.

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 5100a737f862..d83f1a27aded 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1692,28 +1692,37 @@ 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.3


2014-07-21 15:03:51

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 36/40] nfsd: add locking to stateowner release

Once we remove the client_mutex, we'll need to properly protect
the stateowner reference counts using the cl_lock.

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 95eb5547f46e..0281b87b77dd 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -3041,9 +3041,14 @@ static inline void *alloc_stateowner(struct kmem_cache *slab, struct xdr_netobj

static void nfs4_put_stateowner(struct nfs4_stateowner *sop)
{
- if (!atomic_dec_and_test(&sop->so_count))
+ struct nfs4_client *clp = sop->so_client;
+
+ might_lock(&clp->cl_lock);
+
+ if (!atomic_dec_and_lock(&sop->so_count, &clp->cl_lock))
return;
sop->so_unhash(sop);
+ spin_unlock(&clp->cl_lock);
sop->so_free(sop);
}

@@ -3058,11 +3063,7 @@ static void hash_openowner(struct nfs4_openowner *oo, struct nfs4_client *clp, u

static void nfs4_unhash_openowner(struct nfs4_stateowner *so)
{
- struct nfs4_client *clp = so->so_client;
-
- spin_lock(&clp->cl_lock);
unhash_openowner_locked(openowner(so));
- spin_unlock(&clp->cl_lock);
}

static void nfs4_free_openowner(struct nfs4_stateowner *so)
@@ -4861,11 +4862,7 @@ find_lockowner_str(clientid_t *clid, struct xdr_netobj *owner,

static void nfs4_unhash_lockowner(struct nfs4_stateowner *sop)
{
- struct nfs4_client *clp = sop->so_client;
-
- spin_lock(&clp->cl_lock);
unhash_lockowner_locked(lockowner(sop));
- spin_unlock(&clp->cl_lock);
}

static void nfs4_free_lockowner(struct nfs4_stateowner *sop)
--
1.9.3


2014-07-21 15:03:31

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 22/40] nfsd: Migrate the stateid reference into nfs4_find_stateid_by_type()

From: Trond Myklebust <[email protected]>

Allow nfs4_find_stateid_by_type to take the stateid reference, while
still holding the &cl->cl_lock. Necessary step toward client_mutex
removal.

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 92f58c36a419..8f724aa429ed 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1710,8 +1710,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;
}
@@ -3344,8 +3348,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);
}

@@ -4188,8 +4190,6 @@ nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate,
*s = find_stateid_by_type(cstate->clp, 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.3


2014-07-21 15:03:17

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 12/40] nfsd: Add reference counting to lock stateids

From: Trond Myklebust <[email protected]>

Ensure that nfsd4_lock() references the lock stateid while it is
manipulating it. Not currently necessary, but will be once the
client_mutex is removed.

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 70119d2a69fd..9a2f1ccb7502 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4745,6 +4745,7 @@ init_lock_stateid(struct nfs4_ol_stateid *stp, struct nfs4_lockowner *lo,

lockdep_assert_held(&clp->cl_lock);

+ atomic_inc(&stp->st_stid.sc_count);
stp->st_stid.sc_type = NFS4_LOCK_STID;
stp->st_stateowner = &lo->lo_owner;
get_nfs4_file(fp);
@@ -4769,8 +4770,10 @@ find_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp)
lockdep_assert_held(&clp->cl_lock);

list_for_each_entry(lst, &lo->lo_owner.so_stateids, st_perstateowner) {
- if (lst->st_stid.sc_file == fp)
+ if (lst->st_stid.sc_file == fp) {
+ atomic_inc(&lst->st_stid.sc_count);
return lst;
+ }
}
return NULL;
}
@@ -4865,7 +4868,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 nfs4_file *fp;
struct file *filp = NULL;
struct file_lock *file_lock = NULL;
@@ -4919,11 +4922,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);
@@ -5016,6 +5023,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_lock_stateid(lock_stp);
nfsd4_bump_seqid(cstate, status);
--
1.9.3


2014-07-21 15:03:03

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 02/40] nfsd: Add reference counting to the lock and open stateids

From: Trond Myklebust <[email protected]>

When we remove the client_mutex, we'll need to be able to ensure that
these objects aren't destroyed while we're not holding locks.

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 3e55df0d7504..76d28be3ef41 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -883,8 +883,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;
if (stp->st_file)
put_nfs4_file(stp->st_file);
remove_stid(&stp->st_stid);
@@ -902,7 +904,7 @@ static void __release_lock_stateid(struct nfs4_ol_stateid *stp)
if (file)
filp_close(file, (fl_owner_t)lockowner(stp->st_stateowner));
close_generic_stateid(stp);
- free_generic_stateid(stp);
+ put_generic_stateid(stp);
}

static void unhash_lockowner(struct nfs4_lockowner *lo)
@@ -965,7 +967,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)
@@ -986,7 +988,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;
}
}
@@ -3823,7 +3825,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);
}

__be32
@@ -4482,9 +4484,9 @@ static void nfsd4_close_open_stateid(struct nfs4_ol_stateid *s)
unhash_open_stateid(s);

if (clp->cl_minorversion) {
- free_generic_stateid(s);
if (list_empty(&oo->oo_owner.so_stateids))
release_openowner(oo);
+ put_generic_stateid(s);
} else {
if (s->st_file) {
put_nfs4_file(s->st_file);
--
1.9.3


2014-07-27 13:24:59

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH 03/40] nfsd: Add a struct nfs4_file field to struct nfs4_stid

On Mon, Jul 21, 2014 at 11:02:15AM -0400, Jeff Layton wrote:
> From: Trond Myklebust <[email protected]>
>
> All stateids are associated with a nfs4_file. Let's consolidate.
> Start by replacing delegation->dl_file with the dl_stid.sc_file
>
> Signed-off-by: Trond Myklebust <[email protected]>

Looks good,

Reviewed-by: Christoph Hellwig <[email protected]>

2014-07-21 15:03:07

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 05/40] nfsd: Ensure atomicity of stateid destruction and idr tree removal

Preparation for removal of the client_mutex. Ensure that they are done
while holding the clp->cl_lock.

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 076de12c3fa6..0243a19dbef4 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -604,13 +604,11 @@ out_dec:
return NULL;
}

-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;
+ lockdep_assert_held(&clp->cl_lock);

- spin_lock(&clp->cl_lock);
idr_remove(&clp->cl_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)
@@ -620,14 +618,25 @@ static void nfs4_free_stid(struct kmem_cache *slab, struct nfs4_stid *s)
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;
+
+ might_lock(&clp->cl_lock);
+
+ 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)) {
- remove_stid(&dp->dl_stid);
- nfs4_free_stid(deleg_slab, &dp->dl_stid);
+ if (nfs4_put_stid(deleg_slab, &dp->dl_stid))
atomic_long_dec(&num_delegations);
- }
}

static void nfs4_put_deleg_lease(struct nfs4_file *fp)
@@ -885,10 +894,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;
- remove_stid(&stp->st_stid);
- nfs4_free_stid(stateid_slab, &stp->st_stid);
+ nfs4_put_stid(stateid_slab, &stp->st_stid);
}

static void __release_lock_stateid(struct nfs4_ol_stateid *stp)
--
1.9.3


2014-07-21 15:03:44

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 31/40] nfsd: don't allow CLOSE to proceed until refcount on stateid drops

Once we remove client_mutex protection, it'll be possible to have an
in-flight operation using an openstateid when a CLOSE call comes in.
If that happens, we can't just put the sc_file reference and clear its
pointer without risking an oops.

Fix this by ensuring that v4.0 CLOSE operations wait for the refcount
to drop before proceeding to do so.

Signed-off-by: Jeff Layton <[email protected]>
---
fs/nfsd/nfs4state.c | 22 +++++++++++++++++++++-
1 file changed, 21 insertions(+), 1 deletion(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 60727b26e2d7..0cf457d23e64 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -86,6 +86,12 @@ static DEFINE_MUTEX(client_mutex);
*/
static DEFINE_SPINLOCK(state_lock);

+/*
+ * A waitqueue for all in-progress 4.0 CLOSE operations that are waiting for
+ * the refcount on the open stateid to drop.
+ */
+static DECLARE_WAIT_QUEUE_HEAD(close_wq);
+
static struct kmem_cache *openowner_slab;
static struct kmem_cache *lockowner_slab;
static struct kmem_cache *file_slab;
@@ -643,8 +649,10 @@ static void nfs4_put_stid(struct nfs4_stid *s)

might_lock(&clp->cl_lock);

- if (!atomic_dec_and_lock(&s->sc_count, &clp->cl_lock))
+ if (!atomic_dec_and_lock(&s->sc_count, &clp->cl_lock)) {
+ wake_up_all(&close_wq);
return;
+ }
remove_stid_locked(clp, s);
spin_unlock(&clp->cl_lock);
s->sc_free(s);
@@ -3084,11 +3092,23 @@ move_to_close_lru(struct nfs4_ol_stateid *s, struct net *net)

dprintk("NFSD: move_to_close_lru nfs4_openowner %p\n", oo);

+ /*
+ * We know that we hold one reference via nfsd4_close, and another
+ * "persistent" reference for the client. If the refcount is higher
+ * than 2, then there are still calls in progress that are using this
+ * stateid. We can't put the sc_file reference until they are finished.
+ * Wait for the refcount to drop to 2. Since it has been unhashed,
+ * there should be no danger of the refcount going back up again at
+ * this point.
+ */
+ wait_event(close_wq, atomic_read(&s->st_stid.sc_count) == 2);
+
release_all_access(s);
if (s->st_stid.sc_file) {
put_nfs4_file(s->st_stid.sc_file);
s->st_stid.sc_file = NULL;
}
+
release_last_closed_stateid(oo);
oo->oo_last_closed_stid = s;
list_move_tail(&oo->oo_close_lru, &nn->close_lru);
--
1.9.3


2014-07-27 13:37:01

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH 07/40] nfsd: do filp_close in sc_free callback for lock stateids

On Mon, Jul 21, 2014 at 11:02:19AM -0400, Jeff Layton wrote:
> Releasing locks when we unhash the stateid instead of doing so only when
> the stateid is actually released will be problematic in later patches
> when we need to protect the unhashing with spinlocks. Move it into the
> sc_free operation instead.
>
> Signed-off-by: Jeff Layton <[email protected]>

Looks reasonable except that sc_free is now initialized twice for
lock stateids.


2014-07-21 15:03:08

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 06/40] nfsd: Cleanup the freeing of stateids

Add a ->free() callback to the struct nfs4_stid, so that we can
release a reference to the stid without caring about the contents.

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 0243a19dbef4..7b329b28734c 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -71,6 +71,7 @@ static u64 current_sessionid = 1;

/* forward declarations */
static int check_for_locks(struct nfs4_file *filp, struct nfs4_lockowner *lowner);
+static void nfs4_free_generic_stateid(struct nfs4_stid *stid);

/* Locking: */

@@ -452,8 +453,15 @@ static void nfs4_file_put_access(struct nfs4_file *fp, u32 access)
__nfs4_file_put_access(fp, O_RDONLY);
}

-static struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl, struct
-kmem_cache *slab)
+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);
+}
+
+static struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl,
+ struct kmem_cache *slab)
{
struct nfs4_stid *stid;
int new_id;
@@ -492,7 +500,22 @@ out_free:

static struct nfs4_ol_stateid * nfs4_alloc_stateid(struct nfs4_client *clp)
{
- return openlockstateid(nfs4_alloc_stid(clp, stateid_slab));
+ struct nfs4_stid *stid;
+ struct nfs4_ol_stateid *stp;
+
+ stid = nfs4_alloc_stid(clp, stateid_slab);
+ if (!stid)
+ return NULL;
+
+ stp = openlockstateid(stid);
+ stp->st_stid.sc_free = nfs4_free_generic_stateid;
+ return stp;
+}
+
+static void nfs4_free_deleg(struct nfs4_stid *stid)
+{
+ nfs4_free_stid(deleg_slab, stid);
+ atomic_long_dec(&num_delegations);
}

/*
@@ -586,6 +609,8 @@ alloc_init_deleg(struct nfs4_client *clp, struct svc_fh *current_fh)
dp = delegstateid(nfs4_alloc_stid(clp, deleg_slab));
if (dp == NULL)
goto out_dec;
+
+ dp->dl_stid.sc_free = nfs4_free_deleg;
/*
* delegation seqid's are never incremented. The 4.1 special
* meaning of seqid 0 isn't meaningful, really, but let's avoid
@@ -611,32 +636,23 @@ static void remove_stid_locked(struct nfs4_client *clp, struct nfs4_stid *s)
idr_remove(&clp->cl_stateids, s->sc_stateid.si_opaque.so_id);
}

-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);
-}
-
-static bool nfs4_put_stid(struct kmem_cache *slab, struct nfs4_stid *s)
+static void nfs4_put_stid(struct nfs4_stid *s)
{
struct nfs4_client *clp = s->sc_client;

might_lock(&clp->cl_lock);

if (!atomic_dec_and_lock(&s->sc_count, &clp->cl_lock))
- return false;
+ return;
remove_stid_locked(clp, s);
spin_unlock(&clp->cl_lock);
- nfs4_free_stid(slab, s);
- return true;
+ s->sc_free(s);
}

void
nfs4_put_delegation(struct nfs4_delegation *dp)
{
- if (nfs4_put_stid(deleg_slab, &dp->dl_stid))
- atomic_long_dec(&num_delegations);
+ nfs4_put_stid(&dp->dl_stid);
}

static void nfs4_put_deleg_lease(struct nfs4_file *fp)
@@ -887,14 +903,17 @@ static void unhash_generic_stateid(struct nfs4_ol_stateid *stp)
list_del(&stp->st_perstateowner);
}

-static void close_generic_stateid(struct nfs4_ol_stateid *stp)
+static void nfs4_free_generic_stateid(struct nfs4_stid *stid)
{
+ struct nfs4_ol_stateid *stp = openlockstateid(stid);
+
release_all_access(stp);
+ nfs4_free_stid(stateid_slab, stid);
}

static void put_generic_stateid(struct nfs4_ol_stateid *stp)
{
- nfs4_put_stid(stateid_slab, &stp->st_stid);
+ nfs4_put_stid(&stp->st_stid);
}

static void __release_lock_stateid(struct nfs4_ol_stateid *stp)
@@ -907,7 +926,6 @@ static void __release_lock_stateid(struct nfs4_ol_stateid *stp)
file = find_any_file(stp->st_stid.sc_file);
if (file)
filp_close(file, (fl_owner_t)lockowner(stp->st_stateowner));
- close_generic_stateid(stp);
put_generic_stateid(stp);
}

@@ -965,7 +983,6 @@ static void unhash_open_stateid(struct nfs4_ol_stateid *stp)
{
unhash_generic_stateid(stp);
release_open_stateid_locks(stp);
- close_generic_stateid(stp);
}

static void release_open_stateid(struct nfs4_ol_stateid *stp)
@@ -4501,8 +4518,11 @@ static void nfsd4_close_open_stateid(struct nfs4_ol_stateid *s)
oo->oo_last_closed_stid = s;
/*
* In the 4.0 case we need to keep the owners around a
- * little while to handle CLOSE replay.
+ * little while to handle CLOSE replay. We still do need
+ * to release any file access that is held by them
+ * before returning however.
*/
+ release_all_access(s);
if (list_empty(&oo->oo_owner.so_stateids))
move_to_close_lru(oo, clp->net);
}
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 1be148ec9440..5c30e6064749 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -86,6 +86,8 @@ struct nfs4_stid {
stateid_t sc_stateid;
struct nfs4_client *sc_client;
struct nfs4_file *sc_file;
+
+ void (*sc_free)(struct nfs4_stid *);
};

struct nfs4_delegation {
--
1.9.3


2014-07-21 15:03:27

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 19/40] nfsd: Add reference counting to nfs4_preprocess_confirmed_seqid_op

From: Trond Myklebust <[email protected]>

Ensure that all the callers put the open stateid after use.
Necessary step toward client_mutex removal.

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 1e95727e5f01..f9a3fcd4e217 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4422,6 +4422,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;
@@ -4526,12 +4528,12 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp,
if (!test_access(od->od_share_access, stp)) {
dprintk("NFSD: access not a subset of current bitmap: 0x%hhx, 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 of current bitmap: 0x%hhx, 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);

@@ -4540,6 +4542,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);
if (!cstate->replay_owner)
@@ -4892,6 +4896,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 nfs4_file *fp;
struct file *filp = NULL;
struct file_lock *file_lock = NULL;
@@ -4919,8 +4924,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,
@@ -5048,6 +5051,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_lock_stateid(lock_stp);
nfsd4_bump_seqid(cstate, status);
--
1.9.3


2014-07-21 15:03:25

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 18/40] nfsd: nfsd4_open_confirm() must reference the open stateid

From: Trond Myklebust <[email protected]>

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

Necessary step toward client_mutex removal.

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 069ce04effc8..1e95727e5f01 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4451,10 +4451,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));
@@ -4463,6 +4465,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);
if (!cstate->replay_owner)
--
1.9.3


2014-07-27 13:25:32

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH 04/40] nfsd: Replace nfs4_ol_stateid->st_file with the st_stid.sc_file

On Mon, Jul 21, 2014 at 11:02:16AM -0400, Jeff Layton wrote:
> From: Trond Myklebust <[email protected]>
>
> Signed-off-by: Trond Myklebust <[email protected]>

Looks good,

Reviewed-by: Christoph Hellwig <[email protected]>

2014-07-21 15:03:18

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 13/40] nfsd: nfsd4_locku() must reference the lock stateid

From: Trond Myklebust <[email protected]>

Ensure that nfsd4_locku() keeps a reference to the lock stateid
until it is done working with it. Necessary step toward client_mutex
removal.

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 9a2f1ccb7502..0720e0888753 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -5156,10 +5156,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) {
@@ -5189,6 +5191,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);
if (!cstate->replay_owner)
--
1.9.3


2014-07-27 13:35:08

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH 06/40] nfsd: Cleanup the freeing of stateids

On Mon, Jul 21, 2014 at 11:02:18AM -0400, Jeff Layton wrote:
> Add a ->free() callback to the struct nfs4_stid, so that we can
> release a reference to the stid without caring about the contents.

Looks good, but I'd do it much earlier so that the nfs4_put_stid doesn't
have to change around again - IMHO adding the free callback should go
together with introducing the refcounting, as it's kinda required
to do the refcounting sanely.

Also when Trond first posted this I suggested to introduce an ops vector
instead of a free callback as there wil be a lot more things in the
stateid handling that could make use of it. I'm not going to insist on
the ops vector at this point, but I think it would make things quite
a bit cleaner.


> +static void nfs4_free_generic_stateid(struct nfs4_stid *stid);

s/generic/ol/

Also maybe just move it into the right place instead of the forward
declaration given how trivial it is?

> +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);
> +}

I think the layering here is wrong - the generic code should put the
file before calling into the ->free method as the file is in the
generic code. Then the free callback can simply opencode the
kmem_cache_free instead of adding this helper.

> +static struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl,
> + struct kmem_cache *slab)
> {
> struct nfs4_stid *stid;
> int new_id;
> @@ -492,7 +500,22 @@ out_free:
>
> static struct nfs4_ol_stateid * nfs4_alloc_stateid(struct nfs4_client *clp)
> {

Having and nfs4_alloc_stid and an nfs4_alloc_stateid is a very confusing
nameing scheme. I think nfs4_alloc_stateid should be rename to
nfs4_alloc_ol_stateid. And nfs4_alloc_stid might actually just be
redone to nfs4_init_stid, leaving the allocation to the caller similar
how the specific type handles the free in the ->free callback.

> void
> nfs4_put_delegation(struct nfs4_delegation *dp)
> {
> - if (nfs4_put_stid(deleg_slab, &dp->dl_stid))
> - atomic_long_dec(&num_delegations);
> + nfs4_put_stid(&dp->dl_stid);
> }

And reason to keep nfs4_put_delegation around?

> static void put_generic_stateid(struct nfs4_ol_stateid *stp)
> {
> - nfs4_put_stid(stateid_slab, &stp->st_stid);
> + nfs4_put_stid(&stp->st_stid);
> }

Ditto for put_generic_stateid.


2014-07-27 13:38:00

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH 10/40] nfsd: Slight cleanup of find_stateid()

On Mon, Jul 21, 2014 at 11:02:22AM -0400, Jeff Layton wrote:
> From: Trond Myklebust <[email protected]>
>
> In preparation of reference counting.

Looks good, but I'd suggest folding it into the patch introducing
the locking for the idr ops.

Reviewed-by: Christoph Hellwig <[email protected]>

2014-07-27 13:58:13

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH 23/40] nfsd: Add reference counting to state owners

> +static void nfs4_put_stateowner(struct nfs4_stateowner *sop);

Just move the trivial implementation of this one towards the top of the
file?

> + kfree(oo->oo_owner.so_owner.data);

Seems like freeing the owner data should stay in common code instead
of the instances.

> + void (*so_free)(struct nfs4_stateowner *);

This probably should be an ops vector aswell.


2014-07-27 13:46:53

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH 11/40] nfsd: ensure atomicity in nfsd4_free_stateid and nfsd4_validate_stateid

> case NFS4_OPEN_STID:
> case NFS4_LOCK_STID:
> ret = check_stateid_generation(stateid, &s->sc_stateid, 1);
> if (ret)
> - goto out;
> - if (s->sc_type == NFS4_LOCK_STID)
> - ret = nfsd4_free_lock_stateid(openlockstateid(s));
> - else
> + break;
> + if (s->sc_type != NFS4_LOCK_STID) {
> ret = nfserr_locks_held;
> - break;
> + break;
> + }
> + spin_unlock(&cl->cl_lock);
> + ret = nfsd4_free_lock_stateid(openlockstateid(s));
> + goto out;

Might be worth to split the open and lock stateid cases here.

Otherwise looks good,

Reviewed-by: Christoph Hellwig <[email protected]>


2014-07-21 15:03:30

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 21/40] nfsd: Migrate the stateid reference into nfs4_lookup_stateid()

From: Trond Myklebust <[email protected]>

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 a0e0882edd2b..92f58c36a419 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4188,6 +4188,8 @@ nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate,
*s = find_stateid_by_type(cstate->clp, stateid, typemask);
if (!*s)
return nfserr_bad_stateid;
+ /* FIXME: move into find_stateid_by_type */
+ atomic_inc(&(*s)->sc_count);
return nfs_ok;
}

@@ -4222,7 +4224,7 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,
NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID,
&s, nn);
if (status)
- goto out;
+ goto unlock_state;
status = check_stateid_generation(stateid, &s->sc_stateid, nfsd4_has_session(cstate));
if (status)
goto out;
@@ -4271,6 +4273,8 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,
if (file)
*filpp = file;
out:
+ nfs4_put_stid(s);
+unlock_state:
nfs4_unlock_state();
return status;
}
@@ -4407,11 +4411,10 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
cstate->replay_owner = 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;
}

@@ -4640,9 +4643,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;

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

--
1.9.3


2014-07-27 13:28:01

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH 05/40] nfsd: Ensure atomicity of stateid destruction and idr tree removal

On Mon, Jul 21, 2014 at 11:02:17AM -0400, Jeff Layton wrote:
> Preparation for removal of the client_mutex. Ensure that they are done
> while holding the clp->cl_lock.
>
> Signed-off-by: Trond Myklebust <[email protected]>

Looks good, although the orderig vs the earlier patches looks odd
to me. I'd introduce the nfs4_put_stid before the locking for the
stateid idr, which would then naturally fall into place with the
right atomicity.

Independent of that: is there any point in keeping the separate
remove_stid_locked helper instead of inlining it into the caller?

Reviewed-by: Christoph Hellwig <[email protected]>

2014-07-21 15:03:43

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 30/40] nfsd: make openstateids hold references to their openowners

Change it so that only openstateids hold persistent references to
openowners. References can still be held by compounds in progress.

With this, we can get rid of NFS4_OO_NEW. It's possible that we
will create a new openowner in the process of doing the open, but
something later fails. In the meantime, another task could find
that openowner and start using it on a successful open. If that
occurs we don't necessarily want to tear it down, just put the
reference that the failing compound holds.

Signed-off-by: Jeff Layton <[email protected]>
---
fs/nfsd/nfs4state.c | 71 +++++++++++++++++++++++------------------------------
fs/nfsd/state.h | 1 -
2 files changed, 31 insertions(+), 41 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 5df3577d62bf..60727b26e2d7 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -912,7 +912,7 @@ static void nfs4_free_generic_stateid(struct nfs4_stid *stid)
struct nfs4_ol_stateid *stp = openlockstateid(stid);

release_all_access(stp);
- if (stp->st_stateowner && stid->sc_type == NFS4_LOCK_STID)
+ if (stp->st_stateowner)
nfs4_put_stateowner(stp->st_stateowner);
nfs4_free_stid(stateid_slab, stid);
}
@@ -1009,8 +1009,9 @@ static void release_last_closed_stateid(struct nfs4_openowner *oo)
struct nfs4_ol_stateid *s = oo->oo_last_closed_stid;

if (s) {
- put_generic_stateid(s);
+ list_del_init(&oo->oo_close_lru);
oo->oo_last_closed_stid = NULL;
+ put_generic_stateid(s);
}
}

@@ -1029,7 +1030,6 @@ static void release_openowner(struct nfs4_openowner *oo)
{
unhash_openowner(oo);
release_openowner_stateids(oo);
- list_del(&oo->oo_close_lru);
release_last_closed_stateid(oo);
nfs4_put_stateowner(&oo->oo_owner);
}
@@ -1504,6 +1504,7 @@ destroy_client(struct nfs4_client *clp)
}
while (!list_empty(&clp->cl_openowners)) {
oo = list_entry(clp->cl_openowners.next, struct nfs4_openowner, oo_perclient);
+ atomic_inc(&oo->oo_owner.so_count);
release_openowner(oo);
}
nfsd4_shutdown_callback(clp);
@@ -3036,7 +3037,7 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfsd4_open *open,
oo->oo_owner.so_unhash = nfs4_unhash_openowner;
oo->oo_owner.so_is_open_owner = 1;
oo->oo_owner.so_seqid = open->op_seqid;
- oo->oo_flags = NFS4_OO_NEW;
+ oo->oo_flags = 0;
if (nfsd4_has_session(cstate))
oo->oo_flags |= NFS4_OO_CONFIRMED;
oo->oo_time = 0;
@@ -3053,6 +3054,7 @@ 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_locks);
stp->st_stateowner = &oo->oo_owner;
+ atomic_inc(&stp->st_stateowner->so_count);
get_nfs4_file(fp);
stp->st_stid.sc_file = fp;
stp->st_access_bmap = 0;
@@ -3068,13 +3070,27 @@ static void init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp,
spin_unlock(&oo->oo_owner.so_client->cl_lock);
}

+/*
+ * In the 4.0 case we need to keep the owners around a little while to handle
+ * CLOSE replay. We still do need to release any file access that is held by
+ * them before returning however.
+ */
static void
-move_to_close_lru(struct nfs4_openowner *oo, struct net *net)
+move_to_close_lru(struct nfs4_ol_stateid *s, struct net *net)
{
- struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+ struct nfs4_openowner *oo = openowner(s->st_stateowner);
+ struct nfsd_net *nn = net_generic(s->st_stid.sc_client->net,
+ nfsd_net_id);

dprintk("NFSD: move_to_close_lru nfs4_openowner %p\n", oo);

+ release_all_access(s);
+ if (s->st_stid.sc_file) {
+ put_nfs4_file(s->st_stid.sc_file);
+ s->st_stid.sc_file = NULL;
+ }
+ release_last_closed_stateid(oo);
+ oo->oo_last_closed_stid = s;
list_move_tail(&oo->oo_close_lru, &nn->close_lru);
oo->oo_time = get_seconds();
}
@@ -3105,6 +3121,7 @@ find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open,
if ((bool)clp->cl_minorversion != sessions)
return NULL;
renew_client(oo->oo_owner.so_client);
+ atomic_inc(&oo->oo_owner.so_count);
return oo;
}
}
@@ -3902,19 +3919,10 @@ void nfsd4_cleanup_open_state(struct nfsd4_compound_state *cstate,
struct nfsd4_open *open, __be32 status)
{
if (open->op_openowner) {
- struct nfs4_openowner *oo = open->op_openowner;
-
- if (!list_empty(&oo->oo_owner.so_stateids))
- list_del_init(&oo->oo_close_lru);
- if (oo->oo_flags & NFS4_OO_NEW) {
- if (status) {
- release_openowner(oo);
- open->op_openowner = NULL;
- } else
- oo->oo_flags &= ~NFS4_OO_NEW;
- }
- if (open->op_openowner)
- nfsd4_cstate_assign_replay(cstate, &oo->oo_owner);
+ struct nfs4_stateowner *so = &open->op_openowner->oo_owner;
+
+ nfsd4_cstate_assign_replay(cstate, so);
+ nfs4_put_stateowner(so);
}
if (open->op_file)
nfsd4_free_file(open->op_file);
@@ -4030,7 +4038,7 @@ nfs4_laundromat(struct nfsd_net *nn)
new_timeo = min(new_timeo, t);
break;
}
- release_openowner(oo);
+ release_last_closed_stateid(oo);
}
new_timeo = max_t(time_t, new_timeo, NFSD_LAUNDROMAT_MINTIMEOUT);
nfs4_unlock_state();
@@ -4594,31 +4602,14 @@ out:
static void nfsd4_close_open_stateid(struct nfs4_ol_stateid *s)
{
struct nfs4_client *clp = s->st_stid.sc_client;
- struct nfs4_openowner *oo = openowner(s->st_stateowner);

s->st_stid.sc_type = NFS4_CLOSED_STID;
unhash_open_stateid(s);

- if (clp->cl_minorversion) {
- if (list_empty(&oo->oo_owner.so_stateids))
- release_openowner(oo);
+ if (clp->cl_minorversion)
put_generic_stateid(s);
- } else {
- if (s->st_stid.sc_file) {
- put_nfs4_file(s->st_stid.sc_file);
- s->st_stid.sc_file = NULL;
- }
- oo->oo_last_closed_stid = s;
- /*
- * In the 4.0 case we need to keep the owners around a
- * little while to handle CLOSE replay. We still do need
- * to release any file access that is held by them
- * before returning however.
- */
- release_all_access(s);
- if (list_empty(&oo->oo_owner.so_stateids))
- move_to_close_lru(oo, clp->net);
- }
+ else
+ move_to_close_lru(s, clp->net);
}

/*
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index f011a76b7196..785a82ef2196 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -364,7 +364,6 @@ struct nfs4_openowner {
struct nfs4_ol_stateid *oo_last_closed_stid;
time_t oo_time; /* time of placement on so_close_lru */
#define NFS4_OO_CONFIRMED 1
-#define NFS4_OO_NEW 4
unsigned char oo_flags;
};

--
1.9.3


2014-07-27 13:42:19

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH 08/40] nfsd: Add locking to protect the state owner lists

On Mon, Jul 21, 2014 at 11:02:20AM -0400, Jeff Layton wrote:
> Change to using the clp->cl_lock for this. For now, there's a lot of
> cl_lock thrashing, but in later patches we'll eliminate that and close
> the potential races that can occur when releasing the cl_lock while
> walking the lists. For now, the client_mutex prevents those races.

I'll have to look at those later patches, but in general I'd prefer
not to have an intermediate stage like this. Maybe just merge
the later cleanup in, maybe do some of the required cleanups before
even adding the new locking.


2014-07-21 15:03:50

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 35/40] nfsd: clean up and reorganize release_lockowner

Do more within the main loop, and simplify the function a bit. Also,
there's no need to take a stateowner reference unless we're going to call
release_lockowner.

Signed-off-by: Jeff Layton <[email protected]>
---
fs/nfsd/nfs4state.c | 49 ++++++++++++++++++-------------------------------
1 file changed, 18 insertions(+), 31 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 58013a5e8a7c..95eb5547f46e 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -5427,8 +5427,8 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
struct nfsd4_release_lockowner *rlockowner)
{
clientid_t *clid = &rlockowner->rl_clientid;
- struct nfs4_stateowner *sop = NULL, *tmp;
- struct nfs4_lockowner *lo;
+ struct nfs4_stateowner *sop;
+ struct nfs4_lockowner *lo = NULL;
struct nfs4_ol_stateid *stp;
struct xdr_netobj *owner = &rlockowner->rl_owner;
unsigned int hashval = ownerstr_hashval(owner);
@@ -5445,45 +5445,32 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
if (status)
goto out;

- status = nfserr_locks_held;
-
clp = cstate->clp;
/* Find the matching lock stateowner */
spin_lock(&clp->cl_lock);
- list_for_each_entry(tmp, &clp->cl_ownerstr_hashtbl[hashval],
+ list_for_each_entry(sop, &clp->cl_ownerstr_hashtbl[hashval],
so_strhash) {
- if (tmp->so_is_open_owner)
- continue;
- if (same_owner_str(tmp, owner)) {
- sop = tmp;
- atomic_inc(&sop->so_count);
- break;
- }
- }

- /* No matching owner found, maybe a replay? Just declare victory... */
- if (!sop) {
- spin_unlock(&clp->cl_lock);
- status = nfs_ok;
- goto out;
- }
+ if (sop->so_is_open_owner || !same_owner_str(sop, owner))
+ continue;

- lo = lockowner(sop);
- /* see if there are still any locks associated with it */
- list_for_each_entry(stp, &sop->so_stateids, st_perstateowner) {
- if (check_for_locks(stp->st_stid.sc_file, lo)) {
- spin_unlock(&clp->cl_lock);
- goto out;
+ /* see if there are still any locks associated with it */
+ lo = lockowner(sop);
+ list_for_each_entry(stp, &sop->so_stateids, st_perstateowner) {
+ if (check_for_locks(stp->st_stid.sc_file, lo)) {
+ status = nfserr_locks_held;
+ spin_unlock(&clp->cl_lock);
+ goto out;
+ }
}
+
+ atomic_inc(&sop->so_count);
+ break;
}
spin_unlock(&clp->cl_lock);
-
- status = nfs_ok;
- sop = NULL;
- release_lockowner(lo);
+ if (lo)
+ release_lockowner(lo);
out:
- if (sop)
- nfs4_put_stateowner(sop);
nfs4_unlock_state();
return status;
}
--
1.9.3


2014-07-21 15:03:47

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 33/40] nfsd: Protect adding/removing lock owners using client_lock

From: Trond Myklebust <[email protected]>

Once we remove client mutex protection, we'll need to ensure that
stateowner lookup and creation are atomic between concurrent compounds.
Ensure that alloc_init_lock_stateowner checks the hashtable under the
client_lock before adding a new element.

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index a3fae742315d..2556499ed8d8 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -957,26 +957,42 @@ static void release_lock_stateid(struct nfs4_ol_stateid *stp)
put_generic_stateid(stp);
}

-static void unhash_lockowner(struct nfs4_lockowner *lo)
+static void unhash_lockowner_locked(struct nfs4_lockowner *lo)
{
+ struct nfsd_net *nn = net_generic(lo->lo_owner.so_client->net,
+ nfsd_net_id);
+
+ lockdep_assert_held(&nn->client_lock);
+
list_del_init(&lo->lo_owner.so_strhash);
}

static void release_lockowner_stateids(struct nfs4_lockowner *lo)
{
+ struct nfsd_net *nn = net_generic(lo->lo_owner.so_client->net,
+ nfsd_net_id);
struct nfs4_ol_stateid *stp;

+ lockdep_assert_held(&nn->client_lock);
+
while (!list_empty(&lo->lo_owner.so_stateids)) {
stp = list_first_entry(&lo->lo_owner.so_stateids,
struct nfs4_ol_stateid, st_perstateowner);
+ spin_unlock(&nn->client_lock);
release_lock_stateid(stp);
+ spin_lock(&nn->client_lock);
}
}

static void release_lockowner(struct nfs4_lockowner *lo)
{
- unhash_lockowner(lo);
+ struct nfsd_net *nn = net_generic(lo->lo_owner.so_client->net,
+ nfsd_net_id);
+
+ spin_lock(&nn->client_lock);
+ unhash_lockowner_locked(lo);
release_lockowner_stateids(lo);
+ spin_unlock(&nn->client_lock);
nfs4_put_stateowner(&lo->lo_owner);
}

@@ -4819,7 +4835,7 @@ nevermind:
}

static struct nfs4_lockowner *
-find_lockowner_str(clientid_t *clid, struct xdr_netobj *owner,
+find_lockowner_str_locked(clientid_t *clid, struct xdr_netobj *owner,
struct nfsd_net *nn)
{
unsigned int strhashval = ownerstr_hashval(clid->cl_id, owner);
@@ -4836,9 +4852,25 @@ find_lockowner_str(clientid_t *clid, struct xdr_netobj *owner,
return NULL;
}

+static struct nfs4_lockowner *
+find_lockowner_str(clientid_t *clid, struct xdr_netobj *owner,
+ struct nfsd_net *nn)
+{
+ struct nfs4_lockowner *lo;
+
+ spin_lock(&nn->client_lock);
+ lo = find_lockowner_str_locked(clid, owner, nn);
+ spin_unlock(&nn->client_lock);
+ return lo;
+}
+
static void nfs4_unhash_lockowner(struct nfs4_stateowner *sop)
{
- unhash_lockowner(lockowner(sop));
+ struct nfsd_net *nn = net_generic(sop->so_client->net, nfsd_net_id);
+
+ spin_lock(&nn->client_lock);
+ unhash_lockowner_locked(lockowner(sop));
+ spin_unlock(&nn->client_lock);
}

static void nfs4_free_lockowner(struct nfs4_stateowner *sop)
@@ -4857,9 +4889,12 @@ static void nfs4_free_lockowner(struct nfs4_stateowner *sop)
* strhashval = ownerstr_hashval
*/
static struct nfs4_lockowner *
-alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, struct nfs4_ol_stateid *open_stp, struct nfsd4_lock *lock) {
- struct nfs4_lockowner *lo;
+alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp,
+ struct nfs4_ol_stateid *open_stp,
+ struct nfsd4_lock *lock)
+{
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
+ struct nfs4_lockowner *lo, *ret;

lo = alloc_stateowner(lockowner_slab, &lock->lk_new_owner, clp);
if (!lo)
@@ -4869,7 +4904,16 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, str
lo->lo_owner.so_seqid = lock->lk_new_lock_seqid;
lo->lo_owner.so_free = nfs4_free_lockowner;
lo->lo_owner.so_unhash = nfs4_unhash_lockowner;
- list_add(&lo->lo_owner.so_strhash, &nn->ownerstr_hashtbl[strhashval]);
+ spin_lock(&nn->client_lock);
+ ret = find_lockowner_str_locked(&clp->cl_clientid,
+ &lock->lk_new_owner, nn);
+ if (ret == NULL) {
+ list_add(&lo->lo_owner.so_strhash,
+ &nn->ownerstr_hashtbl[strhashval]);
+ ret = lo;
+ } else
+ nfs4_free_lockowner(&lo->lo_owner);
+ spin_unlock(&nn->client_lock);
return lo;
}

@@ -5397,6 +5441,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
unsigned int hashval = ownerstr_hashval(clid->cl_id, owner);
__be32 status;
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
+ struct nfs4_client *clp;

dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n",
clid->cl_boot, clid->cl_id);
@@ -5410,6 +5455,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
status = nfserr_locks_held;

/* Find the matching lock stateowner */
+ spin_lock(&nn->client_lock);
list_for_each_entry(tmp, &nn->ownerstr_hashtbl[hashval], so_strhash) {
if (tmp->so_is_open_owner)
continue;
@@ -5419,6 +5465,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
break;
}
}
+ spin_unlock(&nn->client_lock);

/* No matching owner found, maybe a replay? Just declare victory... */
if (!sop) {
@@ -5428,16 +5475,22 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,

lo = lockowner(sop);
/* see if there are still any locks associated with it */
+ clp = cstate->clp;
+ spin_lock(&clp->cl_lock);
list_for_each_entry(stp, &sop->so_stateids, st_perstateowner) {
if (check_for_locks(stp->st_stid.sc_file, lo)) {
- nfs4_put_stateowner(sop);
+ spin_unlock(&clp->cl_lock);
goto out;
}
}
+ spin_unlock(&clp->cl_lock);

status = nfs_ok;
+ sop = NULL;
release_lockowner(lo);
out:
+ if (sop)
+ nfs4_put_stateowner(sop);
nfs4_unlock_state();
return status;
}
--
1.9.3


2014-07-21 15:03:28

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 20/40] nfsd: Migrate the stateid reference into nfs4_preprocess_seqid_op

From: Trond Myklebust <[email protected]>

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 f9a3fcd4e217..a0e0882edd2b 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4407,8 +4407,11 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
cstate->replay_owner = 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;
}

@@ -4417,16 +4420,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;
}

@@ -4453,8 +4458,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)
@@ -4604,8 +4607,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);
update_stateid(&stp->st_stid.sc_stateid);
memcpy(&close->cl_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));

@@ -4953,9 +4954,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;
@@ -5184,8 +5182,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.3


2014-07-21 15:03:49

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 34/40] nfsd: Move the open owner hash table into struct nfs4_client

From: Trond Myklebust <[email protected]>

Preparation for removing the client_mutex.

Convert the open owner hash table into a per-client table and protect it
using the nfs4_client->cl_lock spin lock.

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

diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h
index a71d14413d39..e1f479c162b5 100644
--- a/fs/nfsd/netns.h
+++ b/fs/nfsd/netns.h
@@ -63,7 +63,6 @@ struct nfsd_net {
struct rb_root conf_name_tree;
struct list_head *unconf_id_hashtbl;
struct rb_root unconf_name_tree;
- struct list_head *ownerstr_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 2556499ed8d8..58013a5e8a7c 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -74,7 +74,7 @@ static int check_for_locks(struct nfs4_file *filp, struct nfs4_lockowner *lowner
static void nfs4_free_generic_stateid(struct nfs4_stid *stid);
static struct nfs4_openowner *find_openstateowner_str_locked(
unsigned int hashval, struct nfsd4_open *open,
- bool sessions, struct nfsd_net *nn);
+ struct nfs4_client *clp);
static void nfs4_put_stateowner(struct nfs4_stateowner *sop);

/* Locking: */
@@ -366,12 +366,11 @@ unsigned long max_delegations;
#define OWNER_HASH_SIZE (1 << OWNER_HASH_BITS)
#define OWNER_HASH_MASK (OWNER_HASH_SIZE - 1)

-static unsigned int ownerstr_hashval(u32 clientid, struct xdr_netobj *ownername)
+static unsigned int ownerstr_hashval(struct xdr_netobj *ownername)
{
unsigned int ret;

ret = opaque_hashval(ownername->data, ownername->len);
- ret += clientid;
return ret & OWNER_HASH_MASK;
}

@@ -959,40 +958,37 @@ static void release_lock_stateid(struct nfs4_ol_stateid *stp)

static void unhash_lockowner_locked(struct nfs4_lockowner *lo)
{
- struct nfsd_net *nn = net_generic(lo->lo_owner.so_client->net,
- nfsd_net_id);
+ struct nfs4_client *clp = lo->lo_owner.so_client;

- lockdep_assert_held(&nn->client_lock);
+ lockdep_assert_held(&clp->cl_lock);

list_del_init(&lo->lo_owner.so_strhash);
}

static void release_lockowner_stateids(struct nfs4_lockowner *lo)
{
- struct nfsd_net *nn = net_generic(lo->lo_owner.so_client->net,
- nfsd_net_id);
+ struct nfs4_client *clp = lo->lo_owner.so_client;
struct nfs4_ol_stateid *stp;

- lockdep_assert_held(&nn->client_lock);
+ lockdep_assert_held(&clp->cl_lock);

while (!list_empty(&lo->lo_owner.so_stateids)) {
stp = list_first_entry(&lo->lo_owner.so_stateids,
struct nfs4_ol_stateid, st_perstateowner);
- spin_unlock(&nn->client_lock);
+ spin_unlock(&clp->cl_lock);
release_lock_stateid(stp);
- spin_lock(&nn->client_lock);
+ spin_lock(&clp->cl_lock);
}
}

static void release_lockowner(struct nfs4_lockowner *lo)
{
- struct nfsd_net *nn = net_generic(lo->lo_owner.so_client->net,
- nfsd_net_id);
+ struct nfs4_client *clp = lo->lo_owner.so_client;

- spin_lock(&nn->client_lock);
+ spin_lock(&clp->cl_lock);
unhash_lockowner_locked(lo);
release_lockowner_stateids(lo);
- spin_unlock(&nn->client_lock);
+ spin_unlock(&clp->cl_lock);
nfs4_put_stateowner(&lo->lo_owner);
}

@@ -1027,10 +1023,9 @@ static void release_open_stateid(struct nfs4_ol_stateid *stp)

static void unhash_openowner_locked(struct nfs4_openowner *oo)
{
- struct nfsd_net *nn = net_generic(oo->oo_owner.so_client->net,
- nfsd_net_id);
+ struct nfs4_client *clp = oo->oo_owner.so_client;

- lockdep_assert_held(&nn->client_lock);
+ lockdep_assert_held(&clp->cl_lock);

list_del_init(&oo->oo_owner.so_strhash);
list_del_init(&oo->oo_perclient);
@@ -1050,29 +1045,27 @@ static void release_last_closed_stateid(struct nfs4_openowner *oo)
static void release_openowner_stateids(struct nfs4_openowner *oo)
{
struct nfs4_ol_stateid *stp;
- struct nfsd_net *nn = net_generic(oo->oo_owner.so_client->net,
- nfsd_net_id);
+ struct nfs4_client *clp = oo->oo_owner.so_client;

- lockdep_assert_held(&nn->client_lock);
+ lockdep_assert_held(&clp->cl_lock);

while (!list_empty(&oo->oo_owner.so_stateids)) {
stp = list_first_entry(&oo->oo_owner.so_stateids,
struct nfs4_ol_stateid, st_perstateowner);
- spin_unlock(&nn->client_lock);
+ spin_unlock(&clp->cl_lock);
release_open_stateid(stp);
- spin_lock(&nn->client_lock);
+ spin_lock(&clp->cl_lock);
}
}

static void release_openowner(struct nfs4_openowner *oo)
{
- struct nfsd_net *nn = net_generic(oo->oo_owner.so_client->net,
- nfsd_net_id);
+ struct nfs4_client *clp = oo->oo_owner.so_client;

- spin_lock(&nn->client_lock);
+ spin_lock(&clp->cl_lock);
unhash_openowner_locked(oo);
release_openowner_stateids(oo);
- spin_unlock(&nn->client_lock);
+ spin_unlock(&clp->cl_lock);
release_last_closed_stateid(oo);
nfs4_put_stateowner(&oo->oo_owner);
}
@@ -1456,15 +1449,20 @@ STALE_CLIENTID(clientid_t *clid, struct nfsd_net *nn)
static struct nfs4_client *alloc_client(struct xdr_netobj name)
{
struct nfs4_client *clp;
+ int i;

clp = kzalloc(sizeof(struct nfs4_client), GFP_KERNEL);
if (clp == NULL)
return NULL;
clp->cl_name.data = kmemdup(name.data, name.len, GFP_KERNEL);
- if (clp->cl_name.data == NULL) {
- kfree(clp);
- return NULL;
- }
+ if (clp->cl_name.data == NULL)
+ goto err_no_name;
+ clp->cl_ownerstr_hashtbl = kmalloc(sizeof(struct list_head) *
+ OWNER_HASH_SIZE, GFP_KERNEL);
+ if (!clp->cl_ownerstr_hashtbl)
+ goto err_no_hashtbl;
+ for (i = 0; i < OWNER_HASH_SIZE; i++)
+ INIT_LIST_HEAD(&clp->cl_ownerstr_hashtbl[i]);
clp->cl_name.len = name.len;
INIT_LIST_HEAD(&clp->cl_sessions);
idr_init(&clp->cl_stateids);
@@ -1479,6 +1477,11 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name)
spin_lock_init(&clp->cl_lock);
rpc_init_wait_queue(&clp->cl_cb_waitq, "Backchannel slot table");
return clp;
+err_no_hashtbl:
+ kfree(clp->cl_name.data);
+err_no_name:
+ kfree(clp);
+ return NULL;
}

static void
@@ -1497,6 +1500,7 @@ free_client(struct nfs4_client *clp)
}
rpc_destroy_wait_queue(&clp->cl_cb_waitq);
free_svc_cred(&clp->cl_cred);
+ kfree(clp->cl_ownerstr_hashtbl);
kfree(clp->cl_name.data);
spin_lock(&clp->cl_lock);
idr_destroy(&clp->cl_stateids);
@@ -3045,20 +3049,20 @@ static void nfs4_put_stateowner(struct nfs4_stateowner *sop)

static void hash_openowner(struct nfs4_openowner *oo, struct nfs4_client *clp, unsigned int strhashval)
{
- struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
+ lockdep_assert_held(&clp->cl_lock);

- list_add(&oo->oo_owner.so_strhash, &nn->ownerstr_hashtbl[strhashval]);
+ list_add(&oo->oo_owner.so_strhash,
+ &clp->cl_ownerstr_hashtbl[strhashval]);
list_add(&oo->oo_perclient, &clp->cl_openowners);
}

static void nfs4_unhash_openowner(struct nfs4_stateowner *so)
{
- struct nfs4_openowner *oo = openowner(so);
- struct nfsd_net *nn = net_generic(so->so_client->net, nfsd_net_id);
+ struct nfs4_client *clp = so->so_client;

- spin_lock(&nn->client_lock);
- unhash_openowner_locked(oo);
- spin_unlock(&nn->client_lock);
+ spin_lock(&clp->cl_lock);
+ unhash_openowner_locked(openowner(so));
+ spin_unlock(&clp->cl_lock);
}

static void nfs4_free_openowner(struct nfs4_stateowner *so)
@@ -3074,7 +3078,6 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfsd4_open *open,
struct nfsd4_compound_state *cstate)
{
struct nfs4_client *clp = cstate->clp;
- struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
struct nfs4_openowner *oo, *ret;

oo = alloc_stateowner(openowner_slab, &open->op_owner, clp);
@@ -3090,15 +3093,14 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfsd4_open *open,
oo->oo_time = 0;
oo->oo_last_closed_stid = NULL;
INIT_LIST_HEAD(&oo->oo_close_lru);
- spin_lock(&nn->client_lock);
- ret = find_openstateowner_str_locked(strhashval,
- open, clp->cl_minorversion, nn);
+ spin_lock(&clp->cl_lock);
+ ret = find_openstateowner_str_locked(strhashval, open, clp);
if (ret == NULL) {
hash_openowner(oo, clp, strhashval);
ret = oo;
} else
nfs4_free_openowner(&oo->oo_owner);
- spin_unlock(&nn->client_lock);
+ spin_unlock(&clp->cl_lock);
return oo;
}

@@ -3163,35 +3165,27 @@ move_to_close_lru(struct nfs4_ol_stateid *s, struct net *net)
}

static int
-same_owner_str(struct nfs4_stateowner *sop, struct xdr_netobj *owner,
- clientid_t *clid)
+same_owner_str(struct nfs4_stateowner *sop, struct xdr_netobj *owner)
{
return (sop->so_owner.len == owner->len) &&
- 0 == memcmp(sop->so_owner.data, owner->data, owner->len) &&
- (sop->so_client->cl_clientid.cl_id == clid->cl_id);
+ 0 == memcmp(sop->so_owner.data, owner->data, owner->len);
}

static struct nfs4_openowner *
find_openstateowner_str_locked(unsigned int hashval, struct nfsd4_open *open,
- bool sessions, struct nfsd_net *nn)
+ struct nfs4_client *clp)
{
struct nfs4_stateowner *so;
- struct nfs4_openowner *oo;
- struct nfs4_client *clp;

- lockdep_assert_held(&nn->client_lock);
+ lockdep_assert_held(&clp->cl_lock);

- list_for_each_entry(so, &nn->ownerstr_hashtbl[hashval], so_strhash) {
+ list_for_each_entry(so, &clp->cl_ownerstr_hashtbl[hashval],
+ so_strhash) {
if (!so->so_is_open_owner)
continue;
- if (same_owner_str(so, &open->op_owner, &open->op_clientid)) {
- oo = openowner(so);
- clp = oo->oo_owner.so_client;
- if ((bool)clp->cl_minorversion != sessions)
- break;
- renew_client_locked(clp);
+ if (same_owner_str(so, &open->op_owner)) {
atomic_inc(&so->so_count);
- return oo;
+ return openowner(so);
}
}
return NULL;
@@ -3199,13 +3193,13 @@ find_openstateowner_str_locked(unsigned int hashval, struct nfsd4_open *open,

static struct nfs4_openowner *
find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open,
- bool sessions, struct nfsd_net *nn)
+ struct nfs4_client *clp)
{
struct nfs4_openowner *oo;

- spin_lock(&nn->client_lock);
- oo = find_openstateowner_str_locked(hashval, open, sessions, nn);
- spin_unlock(&nn->client_lock);
+ spin_lock(&clp->cl_lock);
+ oo = find_openstateowner_str_locked(hashval, open, clp);
+ spin_unlock(&clp->cl_lock);
return oo;
}

@@ -3431,8 +3425,8 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate,
return status;
clp = cstate->clp;

- strhashval = ownerstr_hashval(clientid->cl_id, &open->op_owner);
- oo = find_openstateowner_str(strhashval, open, cstate->minorversion, nn);
+ strhashval = ownerstr_hashval(&open->op_owner);
+ oo = find_openstateowner_str(strhashval, open, clp);
open->op_openowner = oo;
if (!oo) {
goto new_owner;
@@ -4836,15 +4830,16 @@ nevermind:

static struct nfs4_lockowner *
find_lockowner_str_locked(clientid_t *clid, struct xdr_netobj *owner,
- struct nfsd_net *nn)
+ struct nfs4_client *clp)
{
- unsigned int strhashval = ownerstr_hashval(clid->cl_id, owner);
+ unsigned int strhashval = ownerstr_hashval(owner);
struct nfs4_stateowner *so;

- list_for_each_entry(so, &nn->ownerstr_hashtbl[strhashval], so_strhash) {
+ list_for_each_entry(so, &clp->cl_ownerstr_hashtbl[strhashval],
+ so_strhash) {
if (so->so_is_open_owner)
continue;
- if (!same_owner_str(so, owner, clid))
+ if (!same_owner_str(so, owner))
continue;
atomic_inc(&so->so_count);
return lockowner(so);
@@ -4854,23 +4849,23 @@ find_lockowner_str_locked(clientid_t *clid, struct xdr_netobj *owner,

static struct nfs4_lockowner *
find_lockowner_str(clientid_t *clid, struct xdr_netobj *owner,
- struct nfsd_net *nn)
+ struct nfs4_client *clp)
{
struct nfs4_lockowner *lo;

- spin_lock(&nn->client_lock);
- lo = find_lockowner_str_locked(clid, owner, nn);
- spin_unlock(&nn->client_lock);
+ spin_lock(&clp->cl_lock);
+ lo = find_lockowner_str_locked(clid, owner, clp);
+ spin_unlock(&clp->cl_lock);
return lo;
}

static void nfs4_unhash_lockowner(struct nfs4_stateowner *sop)
{
- struct nfsd_net *nn = net_generic(sop->so_client->net, nfsd_net_id);
+ struct nfs4_client *clp = sop->so_client;

- spin_lock(&nn->client_lock);
+ spin_lock(&clp->cl_lock);
unhash_lockowner_locked(lockowner(sop));
- spin_unlock(&nn->client_lock);
+ spin_unlock(&clp->cl_lock);
}

static void nfs4_free_lockowner(struct nfs4_stateowner *sop)
@@ -4893,7 +4888,6 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp,
struct nfs4_ol_stateid *open_stp,
struct nfsd4_lock *lock)
{
- struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
struct nfs4_lockowner *lo, *ret;

lo = alloc_stateowner(lockowner_slab, &lock->lk_new_owner, clp);
@@ -4904,16 +4898,16 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp,
lo->lo_owner.so_seqid = lock->lk_new_lock_seqid;
lo->lo_owner.so_free = nfs4_free_lockowner;
lo->lo_owner.so_unhash = nfs4_unhash_lockowner;
- spin_lock(&nn->client_lock);
+ spin_lock(&clp->cl_lock);
ret = find_lockowner_str_locked(&clp->cl_clientid,
- &lock->lk_new_owner, nn);
+ &lock->lk_new_owner, clp);
if (ret == NULL) {
list_add(&lo->lo_owner.so_strhash,
- &nn->ownerstr_hashtbl[strhashval]);
+ &clp->cl_ownerstr_hashtbl[strhashval]);
ret = lo;
} else
nfs4_free_lockowner(&lo->lo_owner);
- spin_unlock(&nn->client_lock);
+ spin_unlock(&clp->cl_lock);
return lo;
}

@@ -5021,12 +5015,10 @@ static __be32 lookup_or_create_lock_state(struct nfsd4_compound_state *cstate,
struct nfs4_client *cl = oo->oo_owner.so_client;
struct nfs4_lockowner *lo;
unsigned int strhashval;
- struct nfsd_net *nn = net_generic(cl->net, nfsd_net_id);

- lo = find_lockowner_str(&cl->cl_clientid, &lock->v.new.owner, nn);
+ lo = find_lockowner_str(&cl->cl_clientid, &lock->v.new.owner, cl);
if (!lo) {
- strhashval = ownerstr_hashval(cl->cl_clientid.cl_id,
- &lock->v.new.owner);
+ strhashval = ownerstr_hashval(&lock->v.new.owner);
lo = alloc_init_lock_stateowner(strhashval, cl, ost, lock);
if (lo == NULL)
return nfserr_jukebox;
@@ -5304,7 +5296,8 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
goto out;
}

- lo = find_lockowner_str(&lockt->lt_clientid, &lockt->lt_owner, nn);
+ lo = find_lockowner_str(&lockt->lt_clientid, &lockt->lt_owner,
+ cstate->clp);
if (lo)
file_lock->fl_owner = (fl_owner_t)lo;
file_lock->fl_pid = current->tgid;
@@ -5438,7 +5431,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
struct nfs4_lockowner *lo;
struct nfs4_ol_stateid *stp;
struct xdr_netobj *owner = &rlockowner->rl_owner;
- unsigned int hashval = ownerstr_hashval(clid->cl_id, owner);
+ unsigned int hashval = ownerstr_hashval(owner);
__be32 status;
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
struct nfs4_client *clp;
@@ -5454,29 +5447,29 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,

status = nfserr_locks_held;

+ clp = cstate->clp;
/* Find the matching lock stateowner */
- spin_lock(&nn->client_lock);
- list_for_each_entry(tmp, &nn->ownerstr_hashtbl[hashval], so_strhash) {
+ spin_lock(&clp->cl_lock);
+ list_for_each_entry(tmp, &clp->cl_ownerstr_hashtbl[hashval],
+ so_strhash) {
if (tmp->so_is_open_owner)
continue;
- if (same_owner_str(tmp, owner, clid)) {
+ if (same_owner_str(tmp, owner)) {
sop = tmp;
atomic_inc(&sop->so_count);
break;
}
}
- spin_unlock(&nn->client_lock);

/* No matching owner found, maybe a replay? Just declare victory... */
if (!sop) {
+ spin_unlock(&clp->cl_lock);
status = nfs_ok;
goto out;
}

lo = lockowner(sop);
/* see if there are still any locks associated with it */
- clp = cstate->clp;
- spin_lock(&clp->cl_lock);
list_for_each_entry(stp, &sop->so_stateids, st_perstateowner) {
if (check_for_locks(stp->st_stid.sc_file, lo)) {
spin_unlock(&clp->cl_lock);
@@ -5834,10 +5827,6 @@ static int nfs4_state_create_net(struct net *net)
CLIENT_HASH_SIZE, GFP_KERNEL);
if (!nn->unconf_id_hashtbl)
goto err_unconf_id;
- nn->ownerstr_hashtbl = kmalloc(sizeof(struct list_head) *
- OWNER_HASH_SIZE, GFP_KERNEL);
- if (!nn->ownerstr_hashtbl)
- goto err_ownerstr;
nn->sessionid_hashtbl = kmalloc(sizeof(struct list_head) *
SESSION_HASH_SIZE, GFP_KERNEL);
if (!nn->sessionid_hashtbl)
@@ -5847,8 +5836,6 @@ static int nfs4_state_create_net(struct net *net)
INIT_LIST_HEAD(&nn->conf_id_hashtbl[i]);
INIT_LIST_HEAD(&nn->unconf_id_hashtbl[i]);
}
- for (i = 0; i < OWNER_HASH_SIZE; i++)
- INIT_LIST_HEAD(&nn->ownerstr_hashtbl[i]);
for (i = 0; i < SESSION_HASH_SIZE; i++)
INIT_LIST_HEAD(&nn->sessionid_hashtbl[i]);
nn->conf_name_tree = RB_ROOT;
@@ -5864,8 +5851,6 @@ static int nfs4_state_create_net(struct net *net)
return 0;

err_sessionid:
- kfree(nn->ownerstr_hashtbl);
-err_ownerstr:
kfree(nn->unconf_id_hashtbl);
err_unconf_id:
kfree(nn->conf_id_hashtbl);
@@ -5895,7 +5880,6 @@ nfs4_state_destroy_net(struct net *net)
}

kfree(nn->sessionid_hashtbl);
- kfree(nn->ownerstr_hashtbl);
kfree(nn->unconf_id_hashtbl);
kfree(nn->conf_id_hashtbl);
put_net(net);
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 785a82ef2196..f9a6dfb50c14 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -237,6 +237,7 @@ struct nfsd4_sessionid {
struct nfs4_client {
struct list_head cl_idhash; /* hash by cl_clientid.id */
struct rb_node cl_namenode; /* link into by-name trees */
+ struct list_head *cl_ownerstr_hashtbl;
struct list_head cl_openowners;
struct idr cl_stateids; /* stateid lookup */
struct list_head cl_delegations;
--
1.9.3


2014-07-21 15:03:21

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 15/40] nfsd: nfsd4_process_open2() must reference the delegation stateid

From: Trond Myklebust <[email protected]>

Ensure that nfsd4_process_open2() keeps a reference to the delegation
stateid until it is done working with it. Necessary step toward
client_mutex removal.

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 6587230ab4a2..2eaaceef11e8 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -3343,6 +3343,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);
}

@@ -3358,14 +3360,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;
@@ -3846,6 +3852,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.3


2014-07-21 15:03:57

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 40/40] nfsd: don't thrash the cl_lock while freeing an open stateid

When we remove the client_mutex, we'll have a potential race between
FREE_STATEID and CLOSE.

The root of the problem is that we are walking the st_locks list,
dropping the spinlock and then trying to release the persistent
reference to the lockstateid. In between, a FREE_STATEID call can come
along and take the lock, find the stateid and then try to put the
reference. That leads to a double put.

Fix this by not releasing the cl_lock in order to release each lock
stateid. Use put_generic_stateid_locked to unhash them and gather them
onto a list, and free_ol_stateid_reaplist to free any that end up on the
list.

Signed-off-by: Jeff Layton <[email protected]>
---
fs/nfsd/nfs4state.c | 34 +++++++++++++++++++---------------
1 file changed, 19 insertions(+), 15 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 918c09b45843..21bd52714c09 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1038,27 +1038,26 @@ static void release_lockowner(struct nfs4_lockowner *lo)
nfs4_put_stateowner(&lo->lo_owner);
}

-static void release_open_stateid_locks(struct nfs4_ol_stateid *open_stp)
- __releases(&open_stp->st_stateowner->so_client->cl_lock)
- __acquires(&open_stp->st_stateowner->so_client->cl_lock)
+static void release_open_stateid_locks(struct nfs4_ol_stateid *open_stp,
+ struct list_head *reaplist)
{
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);
- spin_unlock(&open_stp->st_stateowner->so_client->cl_lock);
- release_lock_stateid(stp);
- spin_lock(&open_stp->st_stateowner->so_client->cl_lock);
+ unhash_lock_stateid(stp);
+ put_generic_stateid_locked(stp, reaplist);
}
}

-static void unhash_open_stateid(struct nfs4_ol_stateid *stp)
+static void unhash_open_stateid(struct nfs4_ol_stateid *stp,
+ struct list_head *reaplist)
{
lockdep_assert_held(&stp->st_stid.sc_client->cl_lock);

unhash_generic_stateid(stp);
- release_open_stateid_locks(stp);
+ release_open_stateid_locks(stp, reaplist);
}

static void release_open_stateid(struct nfs4_ol_stateid *stp)
@@ -1066,7 +1065,7 @@ static void release_open_stateid(struct nfs4_ol_stateid *stp)
LIST_HEAD(reaplist);

spin_lock(&stp->st_stid.sc_client->cl_lock);
- unhash_open_stateid(stp);
+ unhash_open_stateid(stp, &reaplist);
put_generic_stateid_locked(stp, &reaplist);
spin_unlock(&stp->st_stid.sc_client->cl_lock);
free_ol_stateid_reaplist(&reaplist);
@@ -1106,7 +1105,7 @@ static void release_openowner(struct nfs4_openowner *oo)
while (!list_empty(&oo->oo_owner.so_stateids)) {
stp = list_first_entry(&oo->oo_owner.so_stateids,
struct nfs4_ol_stateid, st_perstateowner);
- unhash_open_stateid(stp);
+ unhash_open_stateid(stp, &reaplist);
put_generic_stateid_locked(stp, &reaplist);
}
spin_unlock(&clp->cl_lock);
@@ -4717,16 +4716,21 @@ out:
static void nfsd4_close_open_stateid(struct nfs4_ol_stateid *s)
{
struct nfs4_client *clp = s->st_stid.sc_client;
+ LIST_HEAD(reaplist);

s->st_stid.sc_type = NFS4_CLOSED_STID;
spin_lock(&clp->cl_lock);
- unhash_open_stateid(s);
- spin_unlock(&clp->cl_lock);
+ unhash_open_stateid(s, &reaplist);

- if (clp->cl_minorversion)
- put_generic_stateid(s);
- else
+ if (clp->cl_minorversion) {
+ put_generic_stateid_locked(s, &reaplist);
+ spin_unlock(&clp->cl_lock);
+ free_ol_stateid_reaplist(&reaplist);
+ } else {
+ spin_unlock(&clp->cl_lock);
+ free_ol_stateid_reaplist(&reaplist);
move_to_close_lru(s, clp->net);
+ }
}

/*
--
1.9.3


2014-07-27 13:21:01

by Christoph Hellwig

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

> static void nfs4_free_stid(struct kmem_cache *slab, struct nfs4_stid *s)
> @@ -1421,7 +1426,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);
> }

No need for locking around idr_destroy - the client structure better
be dead by the time we free it.


2014-07-21 15:03:10

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 07/40] nfsd: do filp_close in sc_free callback for lock stateids

Releasing locks when we unhash the stateid instead of doing so only when
the stateid is actually released will be problematic in later patches
when we need to protect the unhashing with spinlocks. Move it into the
sc_free operation instead.

Signed-off-by: Jeff Layton <[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 7b329b28734c..cedf86a3b9b7 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -911,6 +911,18 @@ static void nfs4_free_generic_stateid(struct nfs4_stid *stid)
nfs4_free_stid(stateid_slab, stid);
}

+static void nfs4_free_lock_stateid(struct nfs4_stid *stid)
+{
+ struct nfs4_ol_stateid *stp = openlockstateid(stid);
+ struct nfs4_lockowner *lo = lockowner(stp->st_stateowner);
+ struct file *file;
+
+ file = find_any_file(stp->st_stid.sc_file);
+ if (file)
+ filp_close(file, (fl_owner_t)lo);
+ nfs4_free_generic_stateid(stid);
+}
+
static void put_generic_stateid(struct nfs4_ol_stateid *stp)
{
nfs4_put_stid(&stp->st_stid);
@@ -918,14 +930,9 @@ static void put_generic_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_stid.sc_file);
- if (file)
- filp_close(file, (fl_owner_t)lockowner(stp->st_stateowner));
put_generic_stateid(stp);
}

@@ -4720,6 +4727,7 @@ alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, struct
stp->st_stateowner = &lo->lo_owner;
get_nfs4_file(fp);
stp->st_stid.sc_file = fp;
+ stp->st_stid.sc_free = nfs4_free_lock_stateid;
stp->st_access_bmap = 0;
stp->st_deny_bmap = open_stp->st_deny_bmap;
stp->st_openstp = open_stp;
--
1.9.3


2014-07-21 15:03:40

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 28/40] nfsd: Make lock stateid take a reference to the lockowner

From: Trond Myklebust <[email protected]>

A necessary step toward client_mutex removal.

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 14ae8d5d7312..348a8b20e546 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -912,6 +912,8 @@ static void nfs4_free_generic_stateid(struct nfs4_stid *stid)
struct nfs4_ol_stateid *stp = openlockstateid(stid);

release_all_access(stp);
+ if (stp->st_stateowner && stid->sc_type == NFS4_LOCK_STID)
+ nfs4_put_stateowner(stp->st_stateowner);
nfs4_free_stid(stateid_slab, stid);
}

@@ -4841,6 +4843,7 @@ init_lock_stateid(struct nfs4_ol_stateid *stp, struct nfs4_lockowner *lo,
atomic_inc(&stp->st_stid.sc_count);
stp->st_stid.sc_type = NFS4_LOCK_STID;
stp->st_stateowner = &lo->lo_owner;
+ atomic_inc(&lo->lo_owner.so_count);
get_nfs4_file(fp);
stp->st_stid.sc_file = fp;
stp->st_stid.sc_free = nfs4_free_lock_stateid;
--
1.9.3


2014-07-27 13:23:06

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH 02/40] nfsd: Add reference counting to the lock and open stateids

On Mon, Jul 21, 2014 at 11:02:14AM -0400, Jeff Layton wrote:
> From: Trond Myklebust <[email protected]>
>
> When we remove the client_mutex, we'll need to be able to ensure that
> these objects aren't destroyed while we're not holding locks.
>
> 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 3e55df0d7504..76d28be3ef41 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -883,8 +883,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)

Can you please call this put_ol_stateid while you're at it? And throw
in a patch to rename the other two generic_stateid names when you touch
them or at the end of the series?

Otherwise looks good to me,

Reviewed-by: Christoph Hellwig <[email protected]>

2014-07-21 15:03:24

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 17/40] nfsd: Prepare nfsd4_close() for open stateid referencing

From: Trond Myklebust <[email protected]>

Prepare nfsd4_close for a future where nfs4_preprocess_seqid_op()
hands it a fully referenced open stateid. Necessary step toward
client_mutex removal.

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 1022d11031f0..069ce04effc8 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4596,10 +4596,15 @@ 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);
update_stateid(&stp->st_stid.sc_stateid);
memcpy(&close->cl_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));

nfsd4_close_open_stateid(stp);
+
+ /* put reference from nfs4_preprocess_seqid_op */
+ put_generic_stateid(stp);
out:
if (!cstate->replay_owner)
nfs4_unlock_state();
--
1.9.3


2014-07-21 15:03:38

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 27/40] nfsd: add an operation for unhashing a stateowner

Allow stateowners to be unhashed and destroyed when the last reference
is put. The unhashing must be idempotent. In a future patch, we'll add
some locking around it, but for now it's only protected by the
client_mutex.

Signed-off-by: Jeff Layton <[email protected]>
---
fs/nfsd/nfs4state.c | 45 +++++++++++++++++++++++++++++++++++----------
fs/nfsd/state.h | 1 +
2 files changed, 36 insertions(+), 10 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index f2e843b471f2..14ae8d5d7312 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -946,9 +946,13 @@ static void __release_lock_stateid(struct nfs4_ol_stateid *stp)

static void unhash_lockowner(struct nfs4_lockowner *lo)
{
+ list_del_init(&lo->lo_owner.so_strhash);
+}
+
+static void release_lockowner_stateids(struct nfs4_lockowner *lo)
+{
struct nfs4_ol_stateid *stp;

- list_del(&lo->lo_owner.so_strhash);
while (!list_empty(&lo->lo_owner.so_stateids)) {
stp = list_first_entry(&lo->lo_owner.so_stateids,
struct nfs4_ol_stateid, st_perstateowner);
@@ -959,6 +963,7 @@ static void unhash_lockowner(struct nfs4_lockowner *lo)
static void release_lockowner(struct nfs4_lockowner *lo)
{
unhash_lockowner(lo);
+ release_lockowner_stateids(lo);
nfs4_put_stateowner(&lo->lo_owner);
}

@@ -1008,15 +1013,8 @@ static void release_open_stateid(struct nfs4_ol_stateid *stp)

static void unhash_openowner(struct nfs4_openowner *oo)
{
- struct nfs4_ol_stateid *stp;
-
- list_del(&oo->oo_owner.so_strhash);
- list_del(&oo->oo_perclient);
- while (!list_empty(&oo->oo_owner.so_stateids)) {
- stp = list_first_entry(&oo->oo_owner.so_stateids,
- struct nfs4_ol_stateid, st_perstateowner);
- release_open_stateid(stp);
- }
+ list_del_init(&oo->oo_owner.so_strhash);
+ list_del_init(&oo->oo_perclient);
}

static void release_last_closed_stateid(struct nfs4_openowner *oo)
@@ -1029,9 +1027,21 @@ static void release_last_closed_stateid(struct nfs4_openowner *oo)
}
}

+static void release_openowner_stateids(struct nfs4_openowner *oo)
+{
+ struct nfs4_ol_stateid *stp;
+
+ while (!list_empty(&oo->oo_owner.so_stateids)) {
+ stp = list_first_entry(&oo->oo_owner.so_stateids,
+ struct nfs4_ol_stateid, st_perstateowner);
+ release_open_stateid(stp);
+ }
+}
+
static void release_openowner(struct nfs4_openowner *oo)
{
unhash_openowner(oo);
+ release_openowner_stateids(oo);
list_del(&oo->oo_close_lru);
release_last_closed_stateid(oo);
nfs4_put_stateowner(&oo->oo_owner);
@@ -2998,6 +3008,7 @@ static void nfs4_put_stateowner(struct nfs4_stateowner *sop)
{
if (!atomic_dec_and_test(&sop->so_count))
return;
+ sop->so_unhash(sop);
sop->so_free(sop);
}

@@ -3009,6 +3020,13 @@ static void hash_openowner(struct nfs4_openowner *oo, struct nfs4_client *clp, u
list_add(&oo->oo_perclient, &clp->cl_openowners);
}

+static void nfs4_unhash_openowner(struct nfs4_stateowner *so)
+{
+ struct nfs4_openowner *oo = openowner(so);
+
+ unhash_openowner(oo);
+}
+
static void nfs4_free_openowner(struct nfs4_stateowner *so)
{
struct nfs4_openowner *oo = openowner(so);
@@ -3028,6 +3046,7 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfsd4_open *open,
if (!oo)
return NULL;
oo->oo_owner.so_free = nfs4_free_openowner;
+ oo->oo_owner.so_unhash = nfs4_unhash_openowner;
oo->oo_owner.so_is_open_owner = 1;
oo->oo_owner.so_seqid = open->op_seqid;
oo->oo_flags = NFS4_OO_NEW;
@@ -4774,6 +4793,11 @@ find_lockowner_str(clientid_t *clid, struct xdr_netobj *owner,
return NULL;
}

+static void nfs4_unhash_lockowner(struct nfs4_stateowner *sop)
+{
+ unhash_lockowner(lockowner(sop));
+}
+
static void nfs4_free_lockowner(struct nfs4_stateowner *sop)
{
struct nfs4_lockowner *lo = lockowner(sop);
@@ -4801,6 +4825,7 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, str
lo->lo_owner.so_is_open_owner = 0;
lo->lo_owner.so_seqid = lock->lk_new_lock_seqid;
lo->lo_owner.so_free = nfs4_free_lockowner;
+ lo->lo_owner.so_unhash = nfs4_unhash_lockowner;
list_add(&lo->lo_owner.so_strhash, &nn->ownerstr_hashtbl[strhashval]);
return lo;
}
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 945f38c0f3a9..f011a76b7196 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -347,6 +347,7 @@ struct nfs4_stateowner {
bool so_is_open_owner;

void (*so_free)(struct nfs4_stateowner *);
+ void (*so_unhash)(struct nfs4_stateowner *);
};

struct nfs4_openowner {
--
1.9.3


2014-07-21 15:03:12

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 08/40] nfsd: Add locking to protect the state owner lists

Change to using the clp->cl_lock for this. For now, there's a lot of
cl_lock thrashing, but in later patches we'll eliminate that and close
the potential races that can occur when releasing the cl_lock while
walking the lists. For now, the client_mutex prevents those races.

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index cedf86a3b9b7..c61f9475f1c6 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -897,6 +897,8 @@ static void unhash_generic_stateid(struct nfs4_ol_stateid *stp)
{
struct nfs4_file *fp = stp->st_stid.sc_file;

+ lockdep_assert_held(&stp->st_stateowner->so_client->cl_lock);
+
spin_lock(&fp->fi_lock);
list_del(&stp->st_perfile);
spin_unlock(&fp->fi_lock);
@@ -930,9 +932,13 @@ static void put_generic_stateid(struct nfs4_ol_stateid *stp)

static void __release_lock_stateid(struct nfs4_ol_stateid *stp)
{
+ struct nfs4_openowner *oo = openowner(stp->st_openstp->st_stateowner);
+
+ spin_lock(&oo->oo_owner.so_client->cl_lock);
list_del(&stp->st_locks);
unhash_generic_stateid(stp);
unhash_stid(&stp->st_stid);
+ spin_unlock(&oo->oo_owner.so_client->cl_lock);
put_generic_stateid(stp);
}

@@ -976,20 +982,26 @@ static void release_lock_stateid(struct nfs4_ol_stateid *stp)
}

static void release_open_stateid_locks(struct nfs4_ol_stateid *open_stp)
+ __releases(&open_stp->st_stateowner->so_client->cl_lock)
+ __acquires(&open_stp->st_stateowner->so_client->cl_lock)
{
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);
+ spin_unlock(&open_stp->st_stateowner->so_client->cl_lock);
release_lock_stateid(stp);
+ spin_lock(&open_stp->st_stateowner->so_client->cl_lock);
}
}

static void unhash_open_stateid(struct nfs4_ol_stateid *stp)
{
+ spin_lock(&stp->st_stateowner->so_client->cl_lock);
unhash_generic_stateid(stp);
release_open_stateid_locks(stp);
+ spin_unlock(&stp->st_stateowner->so_client->cl_lock);
}

static void release_open_stateid(struct nfs4_ol_stateid *stp)
@@ -3001,7 +3013,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_locks);
- list_add(&stp->st_perstateowner, &oo->oo_owner.so_stateids);
stp->st_stateowner = &oo->oo_owner;
get_nfs4_file(fp);
stp->st_stid.sc_file = fp;
@@ -3010,9 +3021,12 @@ 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(&oo->oo_owner.so_client->cl_lock);
+ list_add(&stp->st_perstateowner, &oo->oo_owner.so_stateids);
spin_lock(&fp->fi_lock);
list_add(&stp->st_perfile, &fp->fi_stateids);
spin_unlock(&fp->fi_lock);
+ spin_unlock(&oo->oo_owner.so_client->cl_lock);
}

static void
@@ -4716,6 +4730,7 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, str
static struct nfs4_ol_stateid *
alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, struct nfs4_ol_stateid *open_stp)
{
+ struct nfs4_openowner *oo = openowner(open_stp->st_stateowner);
struct nfs4_ol_stateid *stp;
struct nfs4_client *clp = lo->lo_owner.so_client;

@@ -4723,7 +4738,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_perstateowner, &lo->lo_owner.so_stateids);
stp->st_stateowner = &lo->lo_owner;
get_nfs4_file(fp);
stp->st_stid.sc_file = fp;
@@ -4731,10 +4745,13 @@ 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(&oo->oo_owner.so_client->cl_lock);
list_add(&stp->st_locks, &open_stp->st_locks);
+ list_add(&stp->st_perstateowner, &lo->lo_owner.so_stateids);
spin_lock(&fp->fi_lock);
list_add(&stp->st_perfile, &fp->fi_stateids);
spin_unlock(&fp->fi_lock);
+ spin_unlock(&oo->oo_owner.so_client->cl_lock);
return stp;
}

--
1.9.3


2014-07-21 15:03:33

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 23/40] nfsd: Add reference counting to state owners

From: Trond Myklebust <[email protected]>

The way stateowners are managed today is somewhat awkward. They need to
be explicitly destroyed, even though the stateids reference them. This
will be particularly problematic when we remove the client_mutex.

We may create a new stateowner and attempt to open a file or set a lock,
and have that fail. In the meantime, another RPC may come in that uses
that same stateowner and succeed. We can't have the first task tearing
down the stateowner in that situation.

To fix this, we need to change how stateowners are tracked altogether.
Refcount them and only destroy them once all stateids that reference
them have been destroyed. This patch starts by adding the refcounting
necessary to do that.

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 8f724aa429ed..d96737f72c46 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -72,6 +72,7 @@ static u64 current_sessionid = 1;
/* forward declarations */
static int check_for_locks(struct nfs4_file *filp, struct nfs4_lockowner *lowner);
static void nfs4_free_generic_stateid(struct nfs4_stid *stid);
+static void nfs4_put_stateowner(struct nfs4_stateowner *sop);

/* Locking: */

@@ -955,16 +956,10 @@ 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);
- nfs4_free_lockowner(lo);
+ nfs4_put_stateowner(&lo->lo_owner);
}

static void release_lockowner_if_empty(struct nfs4_lockowner *lo)
@@ -1034,18 +1029,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);
list_del(&oo->oo_close_lru);
release_last_closed_stateid(oo);
- nfs4_free_openowner(oo);
+ nfs4_put_stateowner(&oo->oo_owner);
}

static inline int
@@ -2979,9 +2968,17 @@ static inline void *alloc_stateowner(struct kmem_cache *slab, struct xdr_netobj
INIT_LIST_HEAD(&sop->so_stateids);
sop->so_client = clp;
init_nfs4_replay(&sop->so_replay);
+ atomic_set(&sop->so_count, 1);
return sop;
}

+static void nfs4_put_stateowner(struct nfs4_stateowner *sop)
+{
+ if (!atomic_dec_and_test(&sop->so_count))
+ return;
+ sop->so_free(sop);
+}
+
static void hash_openowner(struct nfs4_openowner *oo, struct nfs4_client *clp, unsigned int strhashval)
{
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
@@ -2990,6 +2987,14 @@ static void hash_openowner(struct nfs4_openowner *oo, struct nfs4_client *clp, u
list_add(&oo->oo_perclient, &clp->cl_openowners);
}

+static void nfs4_free_openowner(struct nfs4_stateowner *so)
+{
+ struct nfs4_openowner *oo = openowner(so);
+
+ kfree(oo->oo_owner.so_owner.data);
+ kmem_cache_free(openowner_slab, oo);
+}
+
static struct nfs4_openowner *
alloc_init_open_stateowner(unsigned int strhashval, struct nfsd4_open *open,
struct nfsd4_compound_state *cstate)
@@ -3000,6 +3005,7 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfsd4_open *open,
oo = alloc_stateowner(openowner_slab, &open->op_owner, clp);
if (!oo)
return NULL;
+ oo->oo_owner.so_free = nfs4_free_openowner;
oo->oo_owner.so_is_open_owner = 1;
oo->oo_owner.so_seqid = open->op_seqid;
oo->oo_flags = NFS4_OO_NEW;
@@ -4746,6 +4752,14 @@ find_lockowner_str(clientid_t *clid, struct xdr_netobj *owner,
return NULL;
}

+static void nfs4_free_lockowner(struct nfs4_stateowner *sop)
+{
+ struct nfs4_lockowner *lo = lockowner(sop);
+
+ kfree(lo->lo_owner.so_owner.data);
+ kmem_cache_free(lockowner_slab, lo);
+}
+
/*
* Alloc a lock owner structure.
* Called in nfsd4_lock - therefore, OPEN and OPEN_CONFIRM (if needed) has
@@ -4766,6 +4780,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;
+ lo->lo_owner.so_free = nfs4_free_lockowner;
list_add(&lo->lo_owner.so_strhash, &nn->ownerstr_hashtbl[strhashval]);
return lo;
}
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 5c30e6064749..3cb7283bc42a 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -339,10 +339,13 @@ struct nfs4_stateowner {
struct nfs4_client * so_client;
/* after increment in ENCODE_SEQID_OP_TAIL, represents the next
* sequence id expected from the client: */
+ atomic_t so_count;
u32 so_seqid;
struct xdr_netobj so_owner; /* open owner name */
struct nfs4_replay so_replay;
bool so_is_open_owner;
+
+ void (*so_free)(struct nfs4_stateowner *);
};

struct nfs4_openowner {
--
1.9.3


2014-07-21 15:03:21

by Jeff Layton

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

From: Trond Myklebust <[email protected]>

Ensure that nfs4_open_delegation() keeps a reference to the delegation
stateid until it is done working with it. Necessary step toward
client_mutex removal.

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 0720e0888753..6587230ab4a2 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -680,6 +680,7 @@ hash_delegation_locked(struct nfs4_delegation *dp, struct nfs4_file *fp)
lockdep_assert_held(&state_lock);
lockdep_assert_held(&fp->fi_lock);

+ atomic_inc(&dp->dl_stid.sc_count);
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);
@@ -3721,6 +3722,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open,
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_no_deleg:
open->op_delegate_type = NFS4_OPEN_DELEGATE_NONE;
--
1.9.3


2014-07-29 11:42:14

by Jeff Layton

[permalink] [raw]
Subject: Re: [PATCH 08/40] nfsd: Add locking to protect the state owner lists

On Sun, 27 Jul 2014 06:42:18 -0700
Christoph Hellwig <[email protected]> wrote:

> On Mon, Jul 21, 2014 at 11:02:20AM -0400, Jeff Layton wrote:
> > Change to using the clp->cl_lock for this. For now, there's a lot of
> > cl_lock thrashing, but in later patches we'll eliminate that and close
> > the potential races that can occur when releasing the cl_lock while
> > walking the lists. For now, the client_mutex prevents those races.
>
> I'll have to look at those later patches, but in general I'd prefer
> not to have an intermediate stage like this. Maybe just merge
> the later cleanup in, maybe do some of the required cleanups before
> even adding the new locking.
>

Thanks for the comments so far! FWIW, this set of patches needed to be
respun anyway to deal with the nfs4_file hashing changes.

I've incorporated most of your comments into the respun set, and I'll
repost it once I get some confirmation from Bruce about the latest
set of delegation patches that precede it.

The one thing I didn't address is your comment above. Adding this
locking is hairy enough that I think it's warranted to do it with an
intermediate step like this.

--
Jeff Layton <[email protected]>

2014-07-21 15:03:35

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 24/40] nfsd: Add a mutex to protect the NFSv4.0 open owner replay cache

From: Trond Myklebust <[email protected]>

We don't want to rely on the client_mutex 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/nfs4xdr.c | 2 --
fs/nfsd/state.h | 1 +
fs/nfsd/xdr4.h | 21 +++++++++++++++++++++
5 files changed, 35 insertions(+), 23 deletions(-)

diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 8611585f739d..7c82cc98e71b 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;
}

@@ -1395,10 +1395,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 d96737f72c46..6886135b82ec 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1071,7 +1071,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)
@@ -2948,6 +2948,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)
@@ -4413,8 +4414,7 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
if (status)
return status;
stp = openlockstateid(s);
- if (!nfsd4_has_session(cstate))
- cstate->replay_owner = stp->st_stateowner;
+ nfsd4_cstate_assign_replay(cstate, stp->st_stateowner);

status = nfs4_seqid_op_checks(cstate, stateid, seqid, stp);
if (!status)
@@ -4483,8 +4483,7 @@ put_stateid:
put_generic_stateid(stp);
out:
nfsd4_bump_seqid(cstate, status);
- if (!cstate->replay_owner)
- nfs4_unlock_state();
+ nfs4_unlock_state();
return status;
}

@@ -4558,8 +4557,7 @@ put_stateid:
put_generic_stateid(stp);
out:
nfsd4_bump_seqid(cstate, status);
- if (!cstate->replay_owner)
- nfs4_unlock_state();
+ nfs4_unlock_state();
return status;
}

@@ -4624,8 +4622,7 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
/* put reference from nfs4_preprocess_seqid_op */
put_generic_stateid(stp);
out:
- if (!cstate->replay_owner)
- nfs4_unlock_state();
+ nfs4_unlock_state();
return status;
}

@@ -5074,8 +5071,7 @@ out:
if (status && new_state)
release_lock_stateid(lock_stp);
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)
@@ -5239,8 +5235,7 @@ put_stateid:
put_generic_stateid(stp);
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/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 01023a595163..05fcd91bb041 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -3925,8 +3925,6 @@ status:
*
* XDR note: do not encode rp->rp_buflen: the buffer contains the
* previously sent already encoded operation.
- *
- * called with nfs4_lock_state() held
*/
void
nfsd4_encode_replay(struct xdr_stream *xdr, struct nfsd4_op *op)
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 3cb7283bc42a..945f38c0f3a9 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 5abf6c942ddf..7442dc7efd31 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -74,6 +74,27 @@ 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.3


2014-07-21 15:03:54

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 38/40] nfsd: close potential race in nfsd4_free_stateid

Once we remove the client_mutex, it'll be possible for the sc_type of a
lock stateid to change after it's found and checked, but before we can
go to destroy it. If that happens, we can end up putting the persistent
reference to the stateid more than once, and unhash it more than once.

Fix this by unhashing the lock stateid prior to dropping the cl_lock but
after finding it.

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 63ee3ebea21c..181c4cc36711 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4416,17 +4416,6 @@ unlock_state:
return status;
}

-static __be32
-nfsd4_free_lock_stateid(struct nfs4_ol_stateid *stp)
-{
- struct nfs4_lockowner *lo = lockowner(stp->st_stateowner);
-
- if (check_for_locks(stp->st_stid.sc_file, lo))
- return nfserr_locks_held;
- release_lock_stateid(stp);
- return nfs_ok;
-}
-
/*
* Test if the stateid is valid
*/
@@ -4453,6 +4442,7 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
stateid_t *stateid = &free_stateid->fr_stateid;
struct nfs4_stid *s;
struct nfs4_delegation *dp;
+ struct nfs4_ol_stateid *stp;
struct nfs4_client *cl = cstate->session->se_client;
__be32 ret = nfserr_bad_stateid;

@@ -4470,12 +4460,16 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
ret = check_stateid_generation(stateid, &s->sc_stateid, 1);
if (ret)
break;
- if (s->sc_type != NFS4_LOCK_STID) {
- ret = nfserr_locks_held;
+ stp = openlockstateid(s);
+ ret = nfserr_locks_held;
+ if (s->sc_type == NFS4_OPEN_STID ||
+ check_for_locks(s->sc_file,
+ lockowner(stp->st_stateowner)))
break;
- }
+ unhash_lock_stateid(stp);
spin_unlock(&cl->cl_lock);
- ret = nfsd4_free_lock_stateid(openlockstateid(s));
+ put_generic_stateid(stp);
+ ret = nfs_ok;
goto out;
case NFS4_REVOKED_DELEG_STID:
dp = delegstateid(s);
--
1.9.3


2014-07-21 15:03:55

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 39/40] nfsd: reduce cl_lock thrashing in release_openowner

Releasing an openowner is a bit inefficient as it can potentially thrash
the cl_lock if you have a lot of stateids attached to it. Once we remove
the client_mutex, it'll also potentially be dangerous to do this.

Add some functions to make it easier to defer the part of putting a
generic stateid reference that needs to be done outside the cl_lock while
doing the parts that must be done while holding it under a single lock.

First we unhash each open stateid. Then we call
put_generic_stateid_locked which will put the reference to an
nfs4_ol_stateid. If it turns out to be the last reference, it'll go
ahead and remove the stid from the IDR tree and put it onto the reaplist
using the st_locks list_head.

Then, after dropping the lock we'll call free_ol_stateid_reaplist to
walk the list of stateids that are fully unhashed and ready to be freed,
and free each of them. This function can sleep, so it must be done
outside any spinlocks.

Signed-off-by: Jeff Layton <[email protected]>
---
fs/nfsd/nfs4state.c | 95 +++++++++++++++++++++++++++++++++++------------------
1 file changed, 63 insertions(+), 32 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 181c4cc36711..918c09b45843 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -944,6 +944,30 @@ static void put_generic_stateid(struct nfs4_ol_stateid *stp)
nfs4_put_stid(&stp->st_stid);
}

+/*
+ * Put the persistent reference to an already unhashed generic stateid, while
+ * holding the cl_lock. If it's the last reference, then put it onto the
+ * reaplist for later destruction.
+ */
+static void put_generic_stateid_locked(struct nfs4_ol_stateid *stp,
+ struct list_head *reaplist)
+{
+ struct nfs4_stid *s = &stp->st_stid;
+ struct nfs4_client *clp = s->sc_client;
+
+ lockdep_assert_held(&clp->cl_lock);
+
+ WARN_ON_ONCE(!list_empty(&stp->st_locks));
+
+ if (!atomic_dec_and_test(&s->sc_count)) {
+ wake_up_all(&close_wq);
+ return;
+ }
+
+ remove_stid_locked(clp, s);
+ list_add(&stp->st_locks, reaplist);
+}
+
static void unhash_lock_stateid(struct nfs4_ol_stateid *stp)
{
struct nfs4_openowner *oo = openowner(stp->st_openstp->st_stateowner);
@@ -974,6 +998,25 @@ static void unhash_lockowner_locked(struct nfs4_lockowner *lo)
list_del_init(&lo->lo_owner.so_strhash);
}

+/*
+ * Free a list of generic stateids that were collected earlier after being
+ * fully unhashed.
+ */
+static void
+free_ol_stateid_reaplist(struct list_head *reaplist)
+{
+ struct nfs4_ol_stateid *stp;
+
+ might_sleep();
+
+ while (!list_empty(reaplist)) {
+ stp = list_first_entry(reaplist, struct nfs4_ol_stateid,
+ st_locks);
+ list_del(&stp->st_locks);
+ stp->st_stid.sc_free(&stp->st_stid);
+ }
+}
+
static void release_lockowner(struct nfs4_lockowner *lo)
{
struct nfs4_client *clp = lo->lo_owner.so_client;
@@ -988,23 +1031,10 @@ static void release_lockowner(struct nfs4_lockowner *lo)
stp = list_first_entry(&lo->lo_owner.so_stateids,
struct nfs4_ol_stateid, st_perstateowner);
unhash_lock_stateid(stp);
- /*
- * We now know that no new references can be added to the
- * stateid. If ours is the last one, finish the unhashing
- * and put it on the list to be reaped.
- */
- if (atomic_dec_and_test(&stp->st_stid.sc_count)) {
- remove_stid_locked(clp, &stp->st_stid);
- list_add(&stp->st_locks, &reaplist);
- }
+ put_generic_stateid_locked(stp, &reaplist);
}
spin_unlock(&clp->cl_lock);
- while (!list_empty(&reaplist)) {
- stp = list_first_entry(&reaplist, struct nfs4_ol_stateid,
- st_locks);
- list_del(&stp->st_locks);
- stp->st_stid.sc_free(&stp->st_stid);
- }
+ free_ol_stateid_reaplist(&reaplist);
nfs4_put_stateowner(&lo->lo_owner);
}

@@ -1025,16 +1055,21 @@ static void release_open_stateid_locks(struct nfs4_ol_stateid *open_stp)

static void unhash_open_stateid(struct nfs4_ol_stateid *stp)
{
- spin_lock(&stp->st_stateowner->so_client->cl_lock);
+ lockdep_assert_held(&stp->st_stid.sc_client->cl_lock);
+
unhash_generic_stateid(stp);
release_open_stateid_locks(stp);
- spin_unlock(&stp->st_stateowner->so_client->cl_lock);
}

static void release_open_stateid(struct nfs4_ol_stateid *stp)
{
+ LIST_HEAD(reaplist);
+
+ spin_lock(&stp->st_stid.sc_client->cl_lock);
unhash_open_stateid(stp);
- put_generic_stateid(stp);
+ put_generic_stateid_locked(stp, &reaplist);
+ spin_unlock(&stp->st_stid.sc_client->cl_lock);
+ free_ol_stateid_reaplist(&reaplist);
}

static void unhash_openowner_locked(struct nfs4_openowner *oo)
@@ -1058,30 +1093,24 @@ static void release_last_closed_stateid(struct nfs4_openowner *oo)
}
}

-static void release_openowner_stateids(struct nfs4_openowner *oo)
+static void release_openowner(struct nfs4_openowner *oo)
{
struct nfs4_ol_stateid *stp;
struct nfs4_client *clp = oo->oo_owner.so_client;
+ struct list_head reaplist;

- lockdep_assert_held(&clp->cl_lock);
+ INIT_LIST_HEAD(&reaplist);

+ spin_lock(&clp->cl_lock);
+ unhash_openowner_locked(oo);
while (!list_empty(&oo->oo_owner.so_stateids)) {
stp = list_first_entry(&oo->oo_owner.so_stateids,
struct nfs4_ol_stateid, st_perstateowner);
- spin_unlock(&clp->cl_lock);
- release_open_stateid(stp);
- spin_lock(&clp->cl_lock);
+ unhash_open_stateid(stp);
+ put_generic_stateid_locked(stp, &reaplist);
}
-}
-
-static void release_openowner(struct nfs4_openowner *oo)
-{
- struct nfs4_client *clp = oo->oo_owner.so_client;
-
- spin_lock(&clp->cl_lock);
- unhash_openowner_locked(oo);
- release_openowner_stateids(oo);
spin_unlock(&clp->cl_lock);
+ free_ol_stateid_reaplist(&reaplist);
release_last_closed_stateid(oo);
nfs4_put_stateowner(&oo->oo_owner);
}
@@ -4690,7 +4719,9 @@ static void nfsd4_close_open_stateid(struct nfs4_ol_stateid *s)
struct nfs4_client *clp = s->st_stid.sc_client;

s->st_stid.sc_type = NFS4_CLOSED_STID;
+ spin_lock(&clp->cl_lock);
unhash_open_stateid(s);
+ spin_unlock(&clp->cl_lock);

if (clp->cl_minorversion)
put_generic_stateid(s);
--
1.9.3


2014-07-21 15:03:46

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 32/40] nfsd: Protect adding/removing open state owners using client_lock

From: Trond Myklebust <[email protected]>

Once we remove client mutex protection, we'll need to ensure that
stateowner lookup and creation are atomic between concurrent compounds.
Ensure that alloc_init_open_stateowner checks the hashtable under the
client_lock before adding a new element.

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 0cf457d23e64..a3fae742315d 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -72,6 +72,9 @@ static u64 current_sessionid = 1;
/* forward declarations */
static int check_for_locks(struct nfs4_file *filp, struct nfs4_lockowner *lowner);
static void nfs4_free_generic_stateid(struct nfs4_stid *stid);
+static struct nfs4_openowner *find_openstateowner_str_locked(
+ unsigned int hashval, struct nfsd4_open *open,
+ bool sessions, struct nfsd_net *nn);
static void nfs4_put_stateowner(struct nfs4_stateowner *sop);

/* Locking: */
@@ -1006,8 +1009,13 @@ static void release_open_stateid(struct nfs4_ol_stateid *stp)
put_generic_stateid(stp);
}

-static void unhash_openowner(struct nfs4_openowner *oo)
+static void unhash_openowner_locked(struct nfs4_openowner *oo)
{
+ struct nfsd_net *nn = net_generic(oo->oo_owner.so_client->net,
+ nfsd_net_id);
+
+ lockdep_assert_held(&nn->client_lock);
+
list_del_init(&oo->oo_owner.so_strhash);
list_del_init(&oo->oo_perclient);
}
@@ -1026,18 +1034,29 @@ static void release_last_closed_stateid(struct nfs4_openowner *oo)
static void release_openowner_stateids(struct nfs4_openowner *oo)
{
struct nfs4_ol_stateid *stp;
+ struct nfsd_net *nn = net_generic(oo->oo_owner.so_client->net,
+ nfsd_net_id);
+
+ lockdep_assert_held(&nn->client_lock);

while (!list_empty(&oo->oo_owner.so_stateids)) {
stp = list_first_entry(&oo->oo_owner.so_stateids,
struct nfs4_ol_stateid, st_perstateowner);
+ spin_unlock(&nn->client_lock);
release_open_stateid(stp);
+ spin_lock(&nn->client_lock);
}
}

static void release_openowner(struct nfs4_openowner *oo)
{
- unhash_openowner(oo);
+ struct nfsd_net *nn = net_generic(oo->oo_owner.so_client->net,
+ nfsd_net_id);
+
+ spin_lock(&nn->client_lock);
+ unhash_openowner_locked(oo);
release_openowner_stateids(oo);
+ spin_unlock(&nn->client_lock);
release_last_closed_stateid(oo);
nfs4_put_stateowner(&oo->oo_owner);
}
@@ -3019,8 +3038,11 @@ static void hash_openowner(struct nfs4_openowner *oo, struct nfs4_client *clp, u
static void nfs4_unhash_openowner(struct nfs4_stateowner *so)
{
struct nfs4_openowner *oo = openowner(so);
+ struct nfsd_net *nn = net_generic(so->so_client->net, nfsd_net_id);

- unhash_openowner(oo);
+ spin_lock(&nn->client_lock);
+ unhash_openowner_locked(oo);
+ spin_unlock(&nn->client_lock);
}

static void nfs4_free_openowner(struct nfs4_stateowner *so)
@@ -3036,7 +3058,8 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfsd4_open *open,
struct nfsd4_compound_state *cstate)
{
struct nfs4_client *clp = cstate->clp;
- struct nfs4_openowner *oo;
+ struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
+ struct nfs4_openowner *oo, *ret;

oo = alloc_stateowner(openowner_slab, &open->op_owner, clp);
if (!oo)
@@ -3051,7 +3074,15 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfsd4_open *open,
oo->oo_time = 0;
oo->oo_last_closed_stid = NULL;
INIT_LIST_HEAD(&oo->oo_close_lru);
- hash_openowner(oo, clp, strhashval);
+ spin_lock(&nn->client_lock);
+ ret = find_openstateowner_str_locked(strhashval,
+ open, clp->cl_minorversion, nn);
+ if (ret == NULL) {
+ hash_openowner(oo, clp, strhashval);
+ ret = oo;
+ } else
+ nfs4_free_openowner(&oo->oo_owner);
+ spin_unlock(&nn->client_lock);
return oo;
}

@@ -3125,13 +3156,15 @@ same_owner_str(struct nfs4_stateowner *sop, struct xdr_netobj *owner,
}

static struct nfs4_openowner *
-find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open,
+find_openstateowner_str_locked(unsigned int hashval, struct nfsd4_open *open,
bool sessions, struct nfsd_net *nn)
{
struct nfs4_stateowner *so;
struct nfs4_openowner *oo;
struct nfs4_client *clp;

+ lockdep_assert_held(&nn->client_lock);
+
list_for_each_entry(so, &nn->ownerstr_hashtbl[hashval], so_strhash) {
if (!so->so_is_open_owner)
continue;
@@ -3139,15 +3172,27 @@ find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open,
oo = openowner(so);
clp = oo->oo_owner.so_client;
if ((bool)clp->cl_minorversion != sessions)
- return NULL;
- renew_client(oo->oo_owner.so_client);
- atomic_inc(&oo->oo_owner.so_count);
+ break;
+ renew_client_locked(clp);
+ atomic_inc(&so->so_count);
return oo;
}
}
return NULL;
}

+static struct nfs4_openowner *
+find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open,
+ bool sessions, struct nfsd_net *nn)
+{
+ struct nfs4_openowner *oo;
+
+ spin_lock(&nn->client_lock);
+ oo = find_openstateowner_str_locked(hashval, open, sessions, nn);
+ spin_unlock(&nn->client_lock);
+ return oo;
+}
+
/* search file_hashtbl[] for file */
static struct nfs4_file *
find_file_locked(struct inode *ino)
--
1.9.3


2014-07-21 15:03:22

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 16/40] nfsd: nfsd4_process_open2() must reference the open stateid

From: Trond Myklebust <[email protected]>

Ensure that nfsd4_process_open2() keeps a reference to the open
stateid until it is done working with it. Necessary step toward
client_mutex removal.

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 2eaaceef11e8..1022d11031f0 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -3011,6 +3011,7 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfsd4_open *open,
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);
stp->st_stateowner = &oo->oo_owner;
@@ -3394,6 +3395,7 @@ nfsd4_find_existing_open(struct nfs4_file *fp, struct nfsd4_open *open)
continue;
if (local->st_stateowner == &oo->oo_owner) {
ret = local;
+ atomic_inc(&ret->st_stid.sc_count);
break;
}
}
@@ -3854,6 +3856,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.3


2014-07-21 15:03:06

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 04/40] nfsd: Replace nfs4_ol_stateid->st_file with the st_stid.sc_file

From: Trond Myklebust <[email protected]>

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 55cb95154731..076de12c3fa6 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -848,7 +848,7 @@ reset_union_bmap_deny(u32 deny, struct nfs4_ol_stateid *stp)

/* Recalculate per-file deny mode if there was a change */
if (change)
- recalculate_deny_mode(stp->st_file);
+ recalculate_deny_mode(stp->st_stid.sc_file);
}

/* release all access and file references for a given stateid */
@@ -856,21 +856,21 @@ static void
release_all_access(struct nfs4_ol_stateid *stp)
{
int i;
- struct nfs4_file *fp = stp->st_file;
+ struct nfs4_file *fp = stp->st_stid.sc_file;

if (fp && stp->st_deny_bmap != 0)
recalculate_deny_mode(fp);

for (i = 1; i < 4; i++) {
if (test_access(i, stp))
- nfs4_file_put_access(stp->st_file, i);
+ nfs4_file_put_access(stp->st_stid.sc_file, i);
clear_access(i, 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);
@@ -887,8 +887,6 @@ static void put_generic_stateid(struct nfs4_ol_stateid *stp)
{
if (!atomic_dec_and_test(&stp->st_stid.sc_count))
return;
- if (stp->st_file)
- put_nfs4_file(stp->st_file);
remove_stid(&stp->st_stid);
nfs4_free_stid(stateid_slab, &stp->st_stid);
}
@@ -900,7 +898,7 @@ static void __release_lock_stateid(struct nfs4_ol_stateid *stp)
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)lockowner(stp->st_stateowner));
close_generic_stateid(stp);
@@ -2976,7 +2974,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);
@@ -3671,7 +3669,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open,
default:
goto out_no_deleg;
}
- dp = nfs4_set_delegation(clp, fh, stp->st_file);
+ dp = nfs4_set_delegation(clp, fh, stp->st_stid.sc_file);
if (IS_ERR(dp))
goto out_no_deleg;

@@ -3962,7 +3960,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;
}
@@ -4192,10 +4190,12 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,
if (status)
goto out;
if (filpp) {
+ struct nfs4_file *fp = stp->st_stid.sc_file;
+
if (flags & RD_STATE)
- file = find_readable_file(stp->st_file);
+ file = find_readable_file(fp);
else
- file = find_writeable_file(stp->st_file);
+ file = find_writeable_file(fp);
}
break;
default:
@@ -4215,7 +4215,7 @@ nfsd4_free_lock_stateid(struct nfs4_ol_stateid *stp)
{
struct nfs4_lockowner *lo = lockowner(stp->st_stateowner);

- if (check_for_locks(stp->st_file, lo))
+ if (check_for_locks(stp->st_stid.sc_file, lo))
return nfserr_locks_held;
release_lockowner_if_empty(lo);
return nfs_ok;
@@ -4406,7 +4406,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, access);
+ nfs4_file_put_access(stp->st_stid.sc_file, access);
clear_access(access, stp);
}

@@ -4488,9 +4488,9 @@ static void nfsd4_close_open_stateid(struct nfs4_ol_stateid *s)
release_openowner(oo);
put_generic_stateid(s);
} else {
- if (s->st_file) {
- put_nfs4_file(s->st_file);
- s->st_file = NULL;
+ if (s->st_stid.sc_file) {
+ put_nfs4_file(s->st_stid.sc_file);
+ s->st_stid.sc_file = NULL;
}
oo->oo_last_closed_stid = s;
/*
@@ -4693,7 +4693,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;
@@ -4710,7 +4710,7 @@ find_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp)
struct nfs4_ol_stateid *lst;

list_for_each_entry(lst, &lo->lo_owner.so_stateids, st_perstateowner) {
- if (lst->st_file == fp)
+ if (lst->st_stid.sc_file == fp)
return lst;
}
return NULL;
@@ -4726,7 +4726,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;

lockdep_assert_held(&fp->fi_lock);

@@ -4738,7 +4738,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;
@@ -4862,7 +4862,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
goto out;
}

- fp = lock_stp->st_file;
+ fp = lock_stp->st_stid.sc_file;
locks_init_lock(file_lock);
switch (lock->lk_type) {
case NFS4_READ_LT:
@@ -5062,7 +5062,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;
@@ -5176,7 +5176,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
lo = lockowner(sop);
/* see if there are still any locks associated with it */
list_for_each_entry(stp, &sop->so_stateids, st_perstateowner) {
- if (check_for_locks(stp->st_file, lo))
+ if (check_for_locks(stp->st_stid.sc_file, lo))
goto out;
}

diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index b14bb7407448..1be148ec9440 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -407,7 +407,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 char st_access_bmap;
unsigned char st_deny_bmap;
struct nfs4_ol_stateid * st_openstp;
--
1.9.3


2014-07-27 13:59:49

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH 25/40] nfsd: Keep a reference to the open stateid for the NFSv4.0 replay cache

> - nfsd4_cleanup_open_state(open, status);
> - if (open->op_openowner)
> - nfsd4_cstate_assign_replay(cstate,
> - &open->op_openowner->oo_owner);
> + nfsd4_cleanup_open_state(cstate, open, status);

This was just added in the last patch, seems like you should introduce this
form of nfsd4_cleanup_open_state in the earlier patch.

> index 6886135b82ec..4f518fb453b5 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -2951,6 +2951,27 @@ static void init_nfs4_replay(struct nfs4_replay *rp)
> mutex_init(&rp->rp_mutex);
> }
>
> +static void nfsd4_cstate_assign_replay(struct nfsd4_compound_state *cstate,
> + struct nfs4_stateowner *so)

> +void nfsd4_cstate_clear_replay(struct nfsd4_compound_state *cstate)

And keep these in nfs4state.c from the start.


2014-07-21 15:03:37

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 26/40] nfsd: clean up lockowner refcounting when finding them

Ensure that when finding or creating a lockowner, that we get a
reference to it. For now, we also take an extra reference when a
lockowner is created that can be put when release_lockowner is called,
but we'll remove that in a later patch once we change how references are
held.

Since we no longer destroy lockowners in the event of an error in
nfsd4_lock, we must change how the seqid gets bumped in the lk_is_new
case. Instead of doing so on creation, do it manually in nfsd4_lock.

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 4f518fb453b5..f2e843b471f2 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4768,6 +4768,7 @@ find_lockowner_str(clientid_t *clid, struct xdr_netobj *owner,
continue;
if (!same_owner_str(so, owner, clid))
continue;
+ atomic_inc(&so->so_count);
return lockowner(so);
}
return NULL;
@@ -4798,9 +4799,7 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, str
return NULL;
INIT_LIST_HEAD(&lo->lo_owner.so_stateids);
lo->lo_owner.so_is_open_owner = 0;
- /* 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;
+ lo->lo_owner.so_seqid = lock->lk_new_lock_seqid;
lo->lo_owner.so_free = nfs4_free_lockowner;
list_add(&lo->lo_owner.so_strhash, &nn->ownerstr_hashtbl[strhashval]);
return lo;
@@ -4897,8 +4896,13 @@ static void get_lock_access(struct nfs4_ol_stateid *lock_stp, u32 access)
set_access(access, lock_stp);
}

-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)
+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)
{
+ __be32 status;
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;
@@ -4913,19 +4917,26 @@ static __be32 lookup_or_create_lock_state(struct nfsd4_compound_state *cstate, s
lo = alloc_init_lock_stateowner(strhashval, cl, ost, lock);
if (lo == NULL)
return nfserr_jukebox;
+ /* FIXME: extra reference for new lockowners for the client */
+ atomic_inc(&lo->lo_owner.so_count);
} else {
/* with an existing lockowner, seqids must be the same */
+ status = nfserr_bad_seqid;
if (!cstate->minorversion &&
lock->lk_new_lock_seqid != lo->lo_owner.so_seqid)
- return nfserr_bad_seqid;
+ goto out;
}

*lst = find_or_create_lock_stateid(lo, fi, ost, new);
if (*lst == NULL) {
release_lockowner_if_empty(lo);
- return nfserr_jukebox;
+ status = nfserr_jukebox;
+ goto out;
}
- return nfs_ok;
+ status = nfs_ok;
+out:
+ nfs4_put_stateowner(&lo->lo_owner);
+ return status;
}

/*
@@ -4944,9 +4955,9 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct file_lock *file_lock = NULL;
struct file_lock *conflock = NULL;
__be32 status = 0;
- bool new_state = false;
int lkflg;
int err;
+ bool new = false;
struct net *net = SVC_NET(rqstp);
struct nfsd_net *nn = net_generic(net, nfsd_net_id);

@@ -4989,7 +5000,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
&lock->v.new.clientid))
goto out;
status = lookup_or_create_lock_state(cstate, open_stp, lock,
- &lock_stp, &new_state);
+ &lock_stp, &new);
} else {
status = nfs4_preprocess_seqid_op(cstate,
lock->lk_old_lock_seqid,
@@ -5088,12 +5099,24 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
out:
if (filp)
fput(filp);
- if (lock_stp)
+ if (lock_stp) {
+ /* Bump seqid manually if the 4.0 replay owner is openowner */
+ if (cstate->replay_owner &&
+ cstate->replay_owner != &lock_sop->lo_owner &&
+ seqid_mutating_err(ntohl(status)))
+ lock_sop->lo_owner.so_seqid++;
+
+ /*
+ * If this is a new, never-before-used stateid, and we are
+ * returning an error, then just go ahead and release it.
+ */
+ if (status && new)
+ release_lock_stateid(lock_stp);
+
put_generic_stateid(lock_stp);
+ }
if (open_stp)
put_generic_stateid(open_stp);
- if (status && new_state)
- release_lock_stateid(lock_stp);
nfsd4_bump_seqid(cstate, status);
nfs4_unlock_state();
if (file_lock)
@@ -5128,7 +5151,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfsd4_lockt *lockt)
{
struct file_lock *file_lock = NULL;
- struct nfs4_lockowner *lo;
+ struct nfs4_lockowner *lo = NULL;
__be32 status;
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);

@@ -5191,6 +5214,8 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
nfs4_set_lock_denied(file_lock, &lockt->lt_denied);
}
out:
+ if (lo)
+ nfs4_put_stateowner(&lo->lo_owner);
nfs4_unlock_state();
if (file_lock)
locks_free_lock(file_lock);
--
1.9.3


2014-07-21 15:03:04

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 03/40] nfsd: Add a struct nfs4_file field to struct nfs4_stid

From: Trond Myklebust <[email protected]>

All stateids are associated with a nfs4_file. Let's consolidate.
Start by replacing delegation->dl_file with the dl_stid.sc_file

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 76d28be3ef41..55cb95154731 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -615,6 +615,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);
}

@@ -622,8 +624,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);
remove_stid(&dp->dl_stid);
nfs4_free_stid(deleg_slab, &dp->dl_stid);
atomic_long_dec(&num_delegations);
@@ -663,7 +663,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;

lockdep_assert_held(&state_lock);

@@ -3100,8 +3100,8 @@ nfs4_share_conflict(struct svc_fh *current_fh, unsigned int deny_type)

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);
+ struct nfsd_net *nn = net_generic(dp->dl_stid.sc_client->net,
+ nfsd_net_id);

block_delegations(&dp->dl_fh);

@@ -3511,7 +3511,7 @@ static struct file_lock *nfs4_alloc_init_lease(struct nfs4_file *fp, int flag)

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;
struct file *filp;
int status = 0;
@@ -3575,7 +3575,7 @@ nfs4_set_delegation(struct nfs4_client *clp, struct svc_fh *fh,
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);
@@ -4170,7 +4170,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 e68a9ae30fd7..b14bb7407448 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 {
@@ -92,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.3


2014-07-21 15:03:36

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 25/40] nfsd: Keep a reference to the open stateid for the NFSv4.0 replay cache

From: Trond Myklebust <[email protected]>

Ensure that nfsd4_cstate_assign_replay/nfsd4_cstate_clear_replay take
a reference to the stateowner when they are using it for NFSv4.0
open and lock replay caching.

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

diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 7c82cc98e71b..29cf395b694e 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -469,10 +469,7 @@ out:
fh_put(resfh);
kfree(resfh);
}
- nfsd4_cleanup_open_state(open, status);
- if (open->op_openowner)
- nfsd4_cstate_assign_replay(cstate,
- &open->op_openowner->oo_owner);
+ nfsd4_cleanup_open_state(cstate, open, status);
nfsd4_bump_seqid(cstate, status);
nfs4_unlock_state();
return status;
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 6886135b82ec..4f518fb453b5 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -2951,6 +2951,27 @@ static void init_nfs4_replay(struct nfs4_replay *rp)
mutex_init(&rp->rp_mutex);
}

+static 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;
+ atomic_inc(&so->so_count);
+ }
+}
+
+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);
+ nfs4_put_stateowner(so);
+ }
+}
+
static inline void *alloc_stateowner(struct kmem_cache *slab, struct xdr_netobj *owner, struct nfs4_client *clp)
{
struct nfs4_stateowner *sop;
@@ -3871,7 +3892,8 @@ out:
return status;
}

-void nfsd4_cleanup_open_state(struct nfsd4_open *open, __be32 status)
+void nfsd4_cleanup_open_state(struct nfsd4_compound_state *cstate,
+ struct nfsd4_open *open, __be32 status)
{
if (open->op_openowner) {
struct nfs4_openowner *oo = open->op_openowner;
@@ -3885,6 +3907,8 @@ void nfsd4_cleanup_open_state(struct nfsd4_open *open, __be32 status)
} else
oo->oo_flags &= ~NFS4_OO_NEW;
}
+ if (open->op_openowner)
+ nfsd4_cstate_assign_replay(cstate, &oo->oo_owner);
}
if (open->op_file)
nfsd4_free_file(open->op_file);
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index 7442dc7efd31..465e7799742a 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -74,27 +74,6 @@ 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;
@@ -620,7 +599,9 @@ extern __be32 nfsd4_process_open1(struct nfsd4_compound_state *,
struct nfsd4_open *open, struct nfsd_net *nn);
extern __be32 nfsd4_process_open2(struct svc_rqst *rqstp,
struct svc_fh *current_fh, struct nfsd4_open *open);
-extern void nfsd4_cleanup_open_state(struct nfsd4_open *open, __be32 status);
+extern void nfsd4_cstate_clear_replay(struct nfsd4_compound_state *cstate);
+extern void nfsd4_cleanup_open_state(struct nfsd4_compound_state *cstate,
+ struct nfsd4_open *open, __be32 status);
extern __be32 nfsd4_open_confirm(struct svc_rqst *rqstp,
struct nfsd4_compound_state *, struct nfsd4_open_confirm *oc);
extern __be32 nfsd4_close(struct svc_rqst *rqstp,
@@ -651,6 +632,7 @@ extern __be32 nfsd4_test_stateid(struct svc_rqst *rqstp,
extern __be32 nfsd4_free_stateid(struct svc_rqst *rqstp,
struct nfsd4_compound_state *, struct nfsd4_free_stateid *free_stateid);
extern void nfsd4_bump_seqid(struct nfsd4_compound_state *, __be32 nfserr);
+
#endif

/*
--
1.9.3


2014-07-21 15:03:41

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 29/40] nfsd: clean up refcounting for lockowners

Ensure that lockowner references are only held by lockstateids and
operations that are in-progress. With this, we can get rid of
release_lockowner_if_empty, which will be racy once we remove
client_mutex protection.

Signed-off-by: Jeff Layton <[email protected]>
---
fs/nfsd/nfs4state.c | 29 +++++++----------------------
1 file changed, 7 insertions(+), 22 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 348a8b20e546..5df3577d62bf 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -934,7 +934,7 @@ static void put_generic_stateid(struct nfs4_ol_stateid *stp)
nfs4_put_stid(&stp->st_stid);
}

-static void __release_lock_stateid(struct nfs4_ol_stateid *stp)
+static void release_lock_stateid(struct nfs4_ol_stateid *stp)
{
struct nfs4_openowner *oo = openowner(stp->st_openstp->st_stateowner);

@@ -958,7 +958,7 @@ static void release_lockowner_stateids(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(stp);
}
}

@@ -969,21 +969,6 @@ static void release_lockowner(struct nfs4_lockowner *lo)
nfs4_put_stateowner(&lo->lo_owner);
}

-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;
-
- 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)
__releases(&open_stp->st_stateowner->so_client->cl_lock)
__acquires(&open_stp->st_stateowner->so_client->cl_lock)
@@ -4338,7 +4323,7 @@ nfsd4_free_lock_stateid(struct nfs4_ol_stateid *stp)

if (check_for_locks(stp->st_stid.sc_file, lo))
return nfserr_locks_held;
- release_lockowner_if_empty(lo);
+ release_lock_stateid(stp);
return nfs_ok;
}

@@ -4945,8 +4930,6 @@ static __be32 lookup_or_create_lock_state(struct nfsd4_compound_state *cstate,
lo = alloc_init_lock_stateowner(strhashval, cl, ost, lock);
if (lo == NULL)
return nfserr_jukebox;
- /* FIXME: extra reference for new lockowners for the client */
- atomic_inc(&lo->lo_owner.so_count);
} else {
/* with an existing lockowner, seqids must be the same */
status = nfserr_bad_seqid;
@@ -4957,7 +4940,6 @@ static __be32 lookup_or_create_lock_state(struct nfsd4_compound_state *cstate,

*lst = find_or_create_lock_stateid(lo, fi, ost, new);
if (*lst == NULL) {
- release_lockowner_if_empty(lo);
status = nfserr_jukebox;
goto out;
}
@@ -5377,6 +5359,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
continue;
if (same_owner_str(tmp, owner, clid)) {
sop = tmp;
+ atomic_inc(&sop->so_count);
break;
}
}
@@ -5390,8 +5373,10 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
lo = lockowner(sop);
/* see if there are still any locks associated with it */
list_for_each_entry(stp, &sop->so_stateids, st_perstateowner) {
- if (check_for_locks(stp->st_stid.sc_file, lo))
+ if (check_for_locks(stp->st_stid.sc_file, lo)) {
+ nfs4_put_stateowner(sop);
goto out;
+ }
}

status = nfs_ok;
--
1.9.3


2014-07-21 15:03:02

by Jeff Layton

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

From: Benny Halevy <[email protected]>

Currently, this is serialized by the client_mutex, which is slated for
removal. Add finer-grained locking here.

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 3946a5a9459c..3e55df0d7504 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -455,7 +455,6 @@ static void nfs4_file_put_access(struct nfs4_file *fp, u32 access)
static struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl, struct
kmem_cache *slab)
{
- struct idr *stateids = &cl->cl_stateids;
struct nfs4_stid *stid;
int new_id;

@@ -463,7 +462,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(&cl->cl_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;
@@ -603,9 +606,11 @@ out_dec:

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

- idr_remove(stateids, s->sc_stateid.si_opaque.so_id);
+ spin_lock(&clp->cl_lock);
+ idr_remove(&clp->cl_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)
@@ -1421,7 +1426,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);
}

@@ -1647,7 +1654,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.3


2014-07-21 15:03:52

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 37/40] nfsd: optimize destroy_lockowner cl_lock thrashing

Reduce the cl_lock trashing in destroy_lockowner. Unhash all of the
lockstateids on the lockowner's list. Put the reference under the lock
and see if it was the last one. If so, then add it to a private list
to be destroyed after we drop the lock.

Signed-off-by: Jeff Layton <[email protected]>
---
fs/nfsd/nfs4state.c | 50 +++++++++++++++++++++++++++++++++-----------------
1 file changed, 33 insertions(+), 17 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 0281b87b77dd..63ee3ebea21c 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -944,14 +944,23 @@ static void put_generic_stateid(struct nfs4_ol_stateid *stp)
nfs4_put_stid(&stp->st_stid);
}

-static void release_lock_stateid(struct nfs4_ol_stateid *stp)
+static void unhash_lock_stateid(struct nfs4_ol_stateid *stp)
{
struct nfs4_openowner *oo = openowner(stp->st_openstp->st_stateowner);

- spin_lock(&oo->oo_owner.so_client->cl_lock);
- list_del(&stp->st_locks);
+ lockdep_assert_held(&oo->oo_owner.so_client->cl_lock);
+
+ list_del_init(&stp->st_locks);
unhash_generic_stateid(stp);
unhash_stid(&stp->st_stid);
+}
+
+static void release_lock_stateid(struct nfs4_ol_stateid *stp)
+{
+ struct nfs4_openowner *oo = openowner(stp->st_openstp->st_stateowner);
+
+ spin_lock(&oo->oo_owner.so_client->cl_lock);
+ unhash_lock_stateid(stp);
spin_unlock(&oo->oo_owner.so_client->cl_lock);
put_generic_stateid(stp);
}
@@ -965,30 +974,37 @@ static void unhash_lockowner_locked(struct nfs4_lockowner *lo)
list_del_init(&lo->lo_owner.so_strhash);
}

-static void release_lockowner_stateids(struct nfs4_lockowner *lo)
+static void release_lockowner(struct nfs4_lockowner *lo)
{
struct nfs4_client *clp = lo->lo_owner.so_client;
struct nfs4_ol_stateid *stp;
+ struct list_head reaplist;

- lockdep_assert_held(&clp->cl_lock);
+ INIT_LIST_HEAD(&reaplist);

+ spin_lock(&clp->cl_lock);
+ unhash_lockowner_locked(lo);
while (!list_empty(&lo->lo_owner.so_stateids)) {
stp = list_first_entry(&lo->lo_owner.so_stateids,
struct nfs4_ol_stateid, st_perstateowner);
- spin_unlock(&clp->cl_lock);
- release_lock_stateid(stp);
- spin_lock(&clp->cl_lock);
+ unhash_lock_stateid(stp);
+ /*
+ * We now know that no new references can be added to the
+ * stateid. If ours is the last one, finish the unhashing
+ * and put it on the list to be reaped.
+ */
+ if (atomic_dec_and_test(&stp->st_stid.sc_count)) {
+ remove_stid_locked(clp, &stp->st_stid);
+ list_add(&stp->st_locks, &reaplist);
+ }
}
-}
-
-static void release_lockowner(struct nfs4_lockowner *lo)
-{
- struct nfs4_client *clp = lo->lo_owner.so_client;
-
- spin_lock(&clp->cl_lock);
- unhash_lockowner_locked(lo);
- release_lockowner_stateids(lo);
spin_unlock(&clp->cl_lock);
+ while (!list_empty(&reaplist)) {
+ stp = list_first_entry(&reaplist, struct nfs4_ol_stateid,
+ st_locks);
+ list_del(&stp->st_locks);
+ stp->st_stid.sc_free(&stp->st_stid);
+ }
nfs4_put_stateowner(&lo->lo_owner);
}

--
1.9.3


2014-07-21 15:03:12

by Jeff Layton

[permalink] [raw]
Subject: [PATCH 09/40] nfsd: clean up races in lock stateid searching and creation

Preparation for removal of the client_mutex.

Currently, no lock aside from the client_mutex is held when calling
find_lock_state. Ensure that the cl_lock is held by adding a lockdep
assertion.

Once we remove the client_mutex, it'll be possible for another thread to
race in and insert a lock state for the same file after we search but
before we insert a new one. Ensure that doesn't happen by redoing the
search after allocating a new stid that we plan to insert. If one is
found just put the one we just allocated.

Signed-off-by: Jeff Layton <[email protected]>
---
fs/nfsd/nfs4state.c | 58 +++++++++++++++++++++++++++++++++++++----------------
1 file changed, 41 insertions(+), 17 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index c61f9475f1c6..5100a737f862 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4727,16 +4727,14 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, str
return lo;
}

-static struct nfs4_ol_stateid *
-alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, struct nfs4_ol_stateid *open_stp)
+static void
+init_lock_stateid(struct nfs4_ol_stateid *stp, struct nfs4_lockowner *lo,
+ struct nfs4_file *fp, struct nfs4_ol_stateid *open_stp)
{
- struct nfs4_openowner *oo = openowner(open_stp->st_stateowner);
- struct nfs4_ol_stateid *stp;
struct nfs4_client *clp = lo->lo_owner.so_client;

- stp = nfs4_alloc_stateid(clp);
- if (stp == NULL)
- return NULL;
+ lockdep_assert_held(&clp->cl_lock);
+
stp->st_stid.sc_type = NFS4_LOCK_STID;
stp->st_stateowner = &lo->lo_owner;
get_nfs4_file(fp);
@@ -4745,20 +4743,20 @@ 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(&oo->oo_owner.so_client->cl_lock);
list_add(&stp->st_locks, &open_stp->st_locks);
list_add(&stp->st_perstateowner, &lo->lo_owner.so_stateids);
spin_lock(&fp->fi_lock);
list_add(&stp->st_perfile, &fp->fi_stateids);
spin_unlock(&fp->fi_lock);
- spin_unlock(&oo->oo_owner.so_client->cl_lock);
- return stp;
}

static struct nfs4_ol_stateid *
find_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp)
{
struct nfs4_ol_stateid *lst;
+ struct nfs4_client *clp = lo->lo_owner.so_client;
+
+ lockdep_assert_held(&clp->cl_lock);

list_for_each_entry(lst, &lo->lo_owner.so_stateids, st_perstateowner) {
if (lst->st_stid.sc_file == fp)
@@ -4767,6 +4765,36 @@ find_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp)
return NULL;
}

+static struct nfs4_ol_stateid *
+find_or_create_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fi,
+ struct nfs4_ol_stateid *ost, bool *new)
+{
+ struct nfs4_ol_stateid *lst, *nst = NULL;
+ struct nfs4_openowner *oo = openowner(ost->st_stateowner);
+ struct nfs4_client *clp = oo->oo_owner.so_client;
+
+ spin_lock(&clp->cl_lock);
+ lst = find_lock_stateid(lo, fi);
+ if (lst == NULL) {
+ spin_unlock(&clp->cl_lock);
+ nst = nfs4_alloc_stateid(clp);
+ if (nst == NULL)
+ return NULL;
+
+ spin_lock(&clp->cl_lock);
+ lst = find_lock_stateid(lo, fi);
+ if (likely(!lst)) {
+ init_lock_stateid(nst, lo, fi, ost);
+ lst = nst;
+ nst = NULL;
+ *new = true;
+ }
+ }
+ spin_unlock(&clp->cl_lock);
+ if (nst)
+ put_generic_stateid(nst);
+ return lst;
+}

static int
check_lock_length(u64 offset, u64 length)
@@ -4810,14 +4838,10 @@ static __be32 lookup_or_create_lock_state(struct nfsd4_compound_state *cstate, s
return nfserr_bad_seqid;
}

- *lst = find_lock_stateid(lo, fi);
+ *lst = find_or_create_lock_stateid(lo, fi, ost, new);
if (*lst == NULL) {
- *lst = alloc_init_lock_stateid(lo, fi, ost);
- if (*lst == NULL) {
- release_lockowner_if_empty(lo);
- return nfserr_jukebox;
- }
- *new = true;
+ release_lockowner_if_empty(lo);
+ return nfserr_jukebox;
}
return nfs_ok;
}
--
1.9.3