2014-06-19 14:50:58

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 000/104] nfsd: eliminate the client_mutex

Here it is. The long awaited removal of the client_mutex from knfsd.
As many of us are aware, one of the major bottlenecks in NFSv4 serving
is the fact that all compounds are processed while holding a single,
global mutex.

This has an obvious detrimental effect on scalability. I've heard
anecdotal reports of 10x slowdowns with v4 serving vs. v3 on the same
machine, primarily due to it.

This patchset eliminates that mutex and (hopefully!) the bottleneck
that it imposes. The basic idea is to add refcounting to most of the
objects that compounds deal with to ensure that they are pinned while
in use. Spinlocks are used to protect things like the hashtables and
trees that track the objects.

Benny started this set quite some time ago, and Trond took up the
torch early this spring. He then handed it to me to clean up the
remaining bits about a month ago.

I'd like to see this considered for v3.17. Obviously, getting it into
linux-next ASAP would be a good thing once it passes review.

Some other highlights/notes:

- use of lockdep has been greatly increased. I found it was the only
way to ensure that I didn't create lock inversion problems and such.

- clients are now only looked up once per compund and are cached in
the cstate. This keeps us from having to keep searching for the
same client for each compound op.

- openowners and lockowners are now handled more sanely. References
to them are only held by the stateids that are associated with them,
and they are destroyed when the last reference to them is put. Also,
lockowners can now have more than one stateid (in accordance with
the spec)

- the fault injection code has been overhauled. It should look the same
to users, but it needed major work to deal with the new locking.
It's probably possible to consolidate some of that code as well --
it more cut-and-pastey than I'd like.

- I've added a file to document how all of the moving parts fit
together. It probably can use more fleshing out, but it's a start.

- the lease handling is now a bit more complex than I'd like. Lease
removal in particular is currently called under a spinlock, which
I think we'll need to eventually clean up. For now, this is ok
since the first thing that vfs_setlease does is lock the i_lock,
but I think we'll eventually need to do an i_lock pushdown in the
lease handling code, and ensure that vfs_setlease isn't called
under a spinlock.

As always, comments and review are welcome.

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

Jeff Layton (40):
NFSd: Ensure that nfs4_file_get_access enforces share access modes
locks: add file_has_lease
NFSd: Protect the nfs4_file delegation fields using the fi_lock
NFSd: Allow lockowners to hold several stateids
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: clean up lockowner refcounting when finding them
nfsd: add an operation for unhashing a stateowner
nfsd: clean up nfs4_release_lockowner
nfsd: clean up refcounting for lockowners
nfsd: declare v4.1+ openowners confirmed on creation
nfsd: make openstateids hold references to their openowners
nfsd: don't allow CLOSE to proceed until refcount on stateid drops
lockdep: add lockdep_assert_not_held
nfsd: add locking to stateowner release
nfsd: optimize destroy_lockowner cl_lock thrashing
nfsd: reduce cl_lock trashing in release_openowner
NFSd: Protect session creation and client confirm using client_lock
nfsd: protect the close_lru list and oo_last_closed_stid with
client_lock
nfsd: ensure that clp->cl_revoked list is protected by clp->cl_lock
nfsd: move unhash_client_locked call into mark_client_expired_locked
nfsd: fix misleading comment
nfsd: don't destroy client if mark_client_expired_locked fails
nfsd: don't destroy clients that are busy
nfsd: abstract out the get and set routines into the fault injection
ops
nfsd: add a forget_clients "get" routine with proper locking
nfsd: add a forget_client set_clnt routine
nfsd: add nfsd_inject_forget_clients
nfsd: add a list_head arg to nfsd_foreach_client_lock
nfsd: add more granular locking to forget_locks fault injector
nfsd: add more granular locking to forget_openowners fault injector
nfsd: add more granular locking to *_delegations fault injectors
nfsd: remove old fault injection infrastructure
nfsd: remove nfs4_lock_state: nfs4_laundromat
nfsd: remove nfs4_lock_state: nfs4_state_shutdown_net
nfsd: remove the client_mutex and the nfs4_lock/unlock_state wrappers
nfsd: add file documenting new state object model

Trond Myklebust (63):
nfsd: Protect addition to the file_hashtbl
NFSd: Avoid taking state_lock while holding inode lock in
nfsd_break_one_deleg
NFSd: nfs4_preprocess_seqid_op should only set *stpp on success
NFSd: Add fine grained protection for the nfs4_file->fi_stateids list
NFSd: Add a mutex to protect the NFSv4.0 open owner replay cache
NFSd: Add locking to the nfs4_file->fi_fds[] array
NFSd: Lock owners are not per open stateid
NFSd: Clean up helper __release_lock_stateid
NFSd: NFSv4 lock-owners are not associated to a specific file
NFSd: Cleanup nfs4svc_encode_compoundres
NFSd: Don't get a session reference without a client reference
NFSd: Allow struct nfsd4_compound_state to cache the nfs4_client
NFSd: Move the delegation reference counter into the struct nfs4_stid
NFSd: Simplify stateid management
NFSd: Fix delegation revocation
NFSd: Add reference counting to the lock and open stateids
NFSd: clean up nfsd4_close_open_stateid
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: Ensure stateids remain unique until they are freed
NFSd: Convert delegation counter to an atomic_long_t type
NFSd: Slight cleanup of find_stateid()
NFSd: Add reference counting to 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: 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: Cleanup - Let nfsd4_lookup_stateid() take a cstate argument
NFSd: Cache the client that was looked up in lookup_clientid()
NFSd: Convert nfsd4_process_open1() to work with lookup_clientid()
NFSd: Always use lookup_clientid() in nfsd4_process_open1
NFSd: Move the open owner hash table into struct nfs4_client
NFSd: Convert nfs4_check_open_reclaim() to work with lookup_clientid()
NFSd: Ensure struct nfs4_client is unhashed before we try to destroy
it
NFSd: Ensure that the laundromat unhashes the client before releasing
locks
NFSd: Don't require client_lock in free_client
NFSd: Move create_client() call outside the lock
NFSd: Protect unconfirmed client creation using client_lock
NFSd: Protect nfsd4_destroy_clientid using client_lock
NFSd: Ensure lookup_clientid() takes client_lock
NFSd: Add assertions to document the nfs4_client/session locking
NFSd: Remove nfs4_lock_state(): nfs4_preprocess_stateid_op()
NFSd: Remove nfs4_lock_state(): nfsd4_test_stateid/nfsd4_free_stateid
NFSd: Remove nfs4_lock_state(): nfsd4_release_lockowner
NFSd: Remove nfs4_lock_state(): nfsd4_lock/locku/lockt()
NFSd: Remove nfs4_lock_state(): nfsd4_open_downgrade + nfsd4_close
NFSd: Remove nfs4_lock_state(): nfsd4_delegreturn()
NFSd: Remove nfs4_lock_state(): nfsd4_open and nfsd4_open_confirm
NFSd: Remove nfs4_lock_state(): exchange_id, create/destroy_session()
NFSd: Remove nfs4_lock_state(): setclientid, setclientid_confirm,
renew
NFSd: Remove nfs4_lock_state(): reclaim_complete()

.../filesystems/nfs/nfsd4-state-objects.txt | 110 +
fs/locks.c | 26 +
fs/nfsd/fault_inject.c | 129 +-
fs/nfsd/netns.h | 5 -
fs/nfsd/nfs4callback.c | 18 +-
fs/nfsd/nfs4proc.c | 16 +-
fs/nfsd/nfs4state.c | 2569 ++++++++++++++------
fs/nfsd/nfs4xdr.c | 17 +-
fs/nfsd/state.h | 86 +-
fs/nfsd/xdr4.h | 8 +-
include/linux/fs.h | 6 +
include/linux/lockdep.h | 6 +
12 files changed, 2052 insertions(+), 944 deletions(-)
create mode 100644 Documentation/filesystems/nfs/nfsd4-state-objects.txt

--
1.9.3



2014-06-19 14:51:51

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 037/104] 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.

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 cf3b19bb5839..4878faec4c68 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -3180,6 +3180,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);
}

@@ -3195,14 +3197,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;
@@ -3644,6 +3650,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-06-19 14:51:10

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 008/104] NFSd: Ensure that nfs4_file_get_access enforces share access modes

Lock atomicity requires us to check the share access mode when we
actually open the file. Note that ideally this would also be atomic
with file creation.

With the change to make nfs4_file_get_access enforce the share mode, we
now have a bogus WARN_ON that can fire. It's now normal to call
nfs4_file_get_access before populating the fi_fds field for the open
flag, so we should no longer warn on that situation.

The other case is a WARN_ON that can occur if there's a O_RDWR open
already present. I'm unclear on why we'd WARN_ON in that case. This
patch removes it, but please do enlighten me if there's some reason
we ought to keep it instead.

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index f17e3b999be6..17870de5989d 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -320,6 +320,20 @@ static unsigned int ownerstr_hashval(u32 clientid, struct xdr_netobj *ownername)
#define FILE_HASH_BITS 8
#define FILE_HASH_SIZE (1 << FILE_HASH_BITS)

+static int nfs4_access_to_omode(u32 access)
+{
+ switch (access & NFS4_SHARE_ACCESS_BOTH) {
+ case NFS4_SHARE_ACCESS_READ:
+ return O_RDONLY;
+ case NFS4_SHARE_ACCESS_WRITE:
+ return O_WRONLY;
+ case NFS4_SHARE_ACCESS_BOTH:
+ return O_RDWR;
+ }
+ WARN_ON_ONCE(1);
+ return O_RDONLY;
+}
+
static unsigned int file_hashval(struct inode *ino)
{
/* XXX: why are we hashing on inode pointer, anyway? */
@@ -328,19 +342,33 @@ static unsigned int file_hashval(struct inode *ino)

static struct hlist_head file_hashtbl[FILE_HASH_SIZE];

-static void __nfs4_file_get_access(struct nfs4_file *fp, int oflag)
+static __be32 nfs4_file_get_access(struct nfs4_file *fp, u32 access, u32 deny)
{
- WARN_ON_ONCE(!(fp->fi_fds[oflag] || fp->fi_fds[O_RDWR]));
- atomic_inc(&fp->fi_access[oflag]);
-}
+ int oflag = nfs4_access_to_omode(access);

-static void nfs4_file_get_access(struct nfs4_file *fp, int oflag)
-{
+ /* Note: relies on NFS4_SHARE_ACCESS_BOTH == READ|WRITE */
+ access &= (NFS4_SHARE_ACCESS_READ|NFS4_SHARE_ACCESS_WRITE);
+ if (access == 0)
+ return nfserr_inval;
+ if ((access & fp->fi_share_deny) != 0)
+ return nfserr_share_denied;
+ /* Note: relies on NFS4_SHARE_DENY_BOTH == READ|WRITE */
+ deny &= (NFS4_SHARE_DENY_READ|NFS4_SHARE_DENY_WRITE);
+ if (deny) {
+ if ((deny & NFS4_SHARE_DENY_READ) &&
+ atomic_read(&fp->fi_access[O_RDONLY]))
+ return nfserr_share_denied;
+ if ((deny & NFS4_SHARE_DENY_WRITE) &&
+ atomic_read(&fp->fi_access[O_WRONLY]))
+ return nfserr_share_denied;
+ fp->fi_share_deny |= deny;
+ }
if (oflag == O_RDWR) {
- __nfs4_file_get_access(fp, O_RDONLY);
- __nfs4_file_get_access(fp, O_WRONLY);
+ atomic_inc(&fp->fi_access[O_RDONLY]);
+ atomic_inc(&fp->fi_access[O_WRONLY]);
} else
- __nfs4_file_get_access(fp, oflag);
+ atomic_inc(&fp->fi_access[oflag]);
+ return nfs_ok;
}

static struct file *nfs4_file_put_fd(struct nfs4_file *fp, int oflag)
@@ -352,6 +380,17 @@ static struct file *nfs4_file_put_fd(struct nfs4_file *fp, int oflag)
return filp;
}

+static void __nfs4_file_put_deny(struct nfs4_file *fp, u32 deny)
+{
+ /* Note: relies on NFS4_SHARE_DENY_BOTH == READ|WRITE */
+ deny &= (NFS4_SHARE_DENY_READ|NFS4_SHARE_DENY_WRITE);
+ if (!deny)
+ return;
+ spin_lock(&fp->fi_lock);
+ fp->fi_share_deny &= ~deny;
+ spin_unlock(&fp->fi_lock);
+}
+
static void __nfs4_file_put_access(struct nfs4_file *fp, int oflag)
{
if (atomic_dec_and_lock(&fp->fi_access[oflag], &fp->fi_lock)) {
@@ -369,8 +408,16 @@ static void __nfs4_file_put_access(struct nfs4_file *fp, int oflag)
}
}

-static void nfs4_file_put_access(struct nfs4_file *fp, int oflag)
+static void nfs4_file_put_access(struct nfs4_file *fp, u32 access, u32 deny)
{
+ int oflag;
+
+ __nfs4_file_put_deny(fp, deny);
+ /* Note: relies on NFS4_SHARE_ACCESS_BOTH == READ|WRITE */
+ access &= (NFS4_SHARE_ACCESS_READ|NFS4_SHARE_ACCESS_WRITE);
+ if (!access)
+ return;
+ oflag = nfs4_access_to_omode(access);
if (oflag == O_RDWR) {
__nfs4_file_put_access(fp, O_RDONLY);
__nfs4_file_put_access(fp, O_WRONLY);
@@ -650,31 +697,21 @@ test_deny(u32 access, struct nfs4_ol_stateid *stp)
return test_bit(access, &stp->st_deny_bmap);
}

-static int nfs4_access_to_omode(u32 access)
-{
- switch (access & NFS4_SHARE_ACCESS_BOTH) {
- case NFS4_SHARE_ACCESS_READ:
- return O_RDONLY;
- case NFS4_SHARE_ACCESS_WRITE:
- return O_WRONLY;
- case NFS4_SHARE_ACCESS_BOTH:
- return O_RDWR;
- }
- WARN_ON_ONCE(1);
- return O_RDONLY;
-}
-
/* release all access and file references for a given stateid */
static void
release_all_access(struct nfs4_ol_stateid *stp)
{
- int i;
+ u32 i;

for (i = 1; i < 4; i++) {
- if (test_access(i, stp))
- nfs4_file_put_access(stp->st_file,
- nfs4_access_to_omode(i));
- clear_access(i, stp);
+ if (test_deny(i, stp)) {
+ nfs4_file_put_access(stp->st_file, 0, i);
+ clear_deny(i, stp);
+ }
+ if (test_access(i, stp)) {
+ nfs4_file_put_access(stp->st_file, i, 0);
+ clear_access(i, stp);
+ }
}
}

@@ -2623,6 +2660,7 @@ static void nfsd4_init_file(struct nfs4_file *fp, struct inode *ino)
fp->fi_lease = NULL;
memset(fp->fi_fds, 0, sizeof(fp->fi_fds));
memset(fp->fi_access, 0, sizeof(fp->fi_access));
+ fp->fi_share_deny = 0;
hlist_add_head(&fp->fi_hash, &file_hashtbl[hashval]);
}

@@ -3097,23 +3135,31 @@ static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp,
int access = nfs4_access_to_access(open->op_share_access);

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

- return nfs_ok;
+ return status;
}

static inline __be32
@@ -3146,10 +3192,9 @@ nfs4_upgrade_open(struct svc_rqst *rqstp, struct nfs4_file *fp, struct svc_fh *c
}
status = nfsd4_truncate(rqstp, cur_fh, open);
if (status) {
- if (new_access) {
- int oflag = nfs4_access_to_omode(op_share_access);
- nfs4_file_put_access(fp, oflag);
- }
+ if (new_access)
+ nfs4_file_put_access(fp, op_share_access,
+ open->op_share_deny);
return status;
}
/* remember the open */
@@ -4059,17 +4104,25 @@ out:
return status;
}

-static inline void nfs4_stateid_downgrade_bit(struct nfs4_ol_stateid *stp, u32 access)
+static void nfs4_stateid_downgrade_bit(struct nfs4_ol_stateid *stp, u32 access)
{
if (!test_access(access, stp))
return;
- nfs4_file_put_access(stp->st_file, nfs4_access_to_omode(access));
+ nfs4_file_put_access(stp->st_file, access, 0);
clear_access(access, stp);
}

-static inline void nfs4_stateid_downgrade(struct nfs4_ol_stateid *stp, u32 to_access)
+static void nfs4_stateid_downgrade_deny_bit(struct nfs4_ol_stateid *stp, u32 deny)
+{
+ if (!test_deny(deny, stp))
+ return;
+ nfs4_file_put_access(stp->st_file, 0, deny);
+ clear_deny(deny, stp);
+}
+
+static void nfs4_stateid_downgrade(struct nfs4_ol_stateid *stp, u32 to_access, u32 to_deny)
{
- switch (to_access) {
+ switch (to_access & NFS4_SHARE_ACCESS_BOTH) {
case NFS4_SHARE_ACCESS_READ:
nfs4_stateid_downgrade_bit(stp, NFS4_SHARE_ACCESS_WRITE);
nfs4_stateid_downgrade_bit(stp, NFS4_SHARE_ACCESS_BOTH);
@@ -4083,15 +4136,34 @@ static inline void nfs4_stateid_downgrade(struct nfs4_ol_stateid *stp, u32 to_ac
default:
WARN_ON_ONCE(1);
}
+
+ switch (to_deny & NFS4_SHARE_DENY_BOTH) {
+ case 0:
+ nfs4_stateid_downgrade_deny_bit(stp, NFS4_SHARE_DENY_BOTH);
+ nfs4_stateid_downgrade_deny_bit(stp, NFS4_SHARE_DENY_WRITE);
+ nfs4_stateid_downgrade_deny_bit(stp, NFS4_SHARE_DENY_READ);
+ break;
+ case NFS4_SHARE_DENY_READ:
+ nfs4_stateid_downgrade_deny_bit(stp, NFS4_SHARE_DENY_BOTH);
+ nfs4_stateid_downgrade_deny_bit(stp, NFS4_SHARE_DENY_WRITE);
+ break;
+ case NFS4_SHARE_DENY_WRITE:
+ nfs4_stateid_downgrade_deny_bit(stp, NFS4_SHARE_DENY_BOTH);
+ nfs4_stateid_downgrade_deny_bit(stp, NFS4_SHARE_DENY_READ);
+ }
}

static void
reset_union_bmap_deny(unsigned long deny, struct nfs4_ol_stateid *stp)
{
- int i;
- for (i = 0; i < 4; i++) {
- if ((i & deny) != i)
+ u32 i;
+ for (i = 3; i > 0; i--) {
+ if (i == deny)
+ continue;
+ if (test_deny(i, stp)) {
+ nfs4_file_put_access(stp->st_file, 0, i);
clear_deny(i, stp);
+ }
}
}

@@ -4128,7 +4200,7 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp,
stp->st_deny_bmap, od->od_share_deny);
goto out;
}
- nfs4_stateid_downgrade(stp, od->od_share_access);
+ nfs4_stateid_downgrade(stp, od->od_share_access, od->od_share_deny);

reset_union_bmap_deny(od->od_share_deny, stp);

@@ -4410,11 +4482,10 @@ 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;
- int oflag = nfs4_access_to_omode(access);

if (test_access(access, lock_stp))
return;
- nfs4_file_get_access(fp, oflag);
+ nfs4_file_get_access(fp, access, 0);
set_access(access, lock_stp);
}

diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 1c0190c0fd88..be5ab8151b0c 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -397,6 +397,7 @@ struct nfs4_file {
* + 1 to both of the above if NFS4_SHARE_ACCESS_BOTH is set.
*/
atomic_t fi_access[2];
+ u32 fi_share_deny;
struct file *fi_deleg_file;
struct file_lock *fi_lease;
atomic_t fi_delegees;
--
1.9.3


2014-06-23 16:10:27

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH v1 000/104] nfsd: eliminate the client_mutex

On Mon, Jun 23, 2014 at 09:00:01AM -0700, Christoph Hellwig wrote:
> On Mon, Jun 23, 2014 at 09:56:45AM -0400, Jeff Layton wrote:
> > > - there is some confusion of NFSd vs nfsd in the subsystem prefixes.
> > > While it seems odd and against the usual naming NFSd seems to be
> > > the common one for nfs patches.
> > >
> >
> > I tend to prefer "nfsd", but ok -- "NFSd" it is.
>
> I'd prefer nfsd as well, but in Rome do as the Romans do, so..

These Romans?:

$ git log --pretty=format:"%s" fs/nfsd|cut -d: -f1|grep -v '^Merge'|sed 's/\[PATCH\] //'|sort|uniq -c|sort -n|tail
8 sunrpc
12 fs
13 locks
15 NFSd
30 SUNRPC
89 nfsd41
107 NFSD
228 knfsd
449 nfsd
594 nfsd4

Anyway, I don't care.

--b.

2014-06-19 14:53:09

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 092/104] NFSd: Remove nfs4_lock_state(): nfsd4_test_stateid/nfsd4_free_stateid

From: Trond Myklebust <[email protected]>

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

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

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

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

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

--
1.9.3


2014-06-23 16:06:37

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH v1 007/104] NFSd: Add locking to the nfs4_file->fi_fds[] array

Missed the why the locking is added.

Also I pointed out a way to better factor this code in reply to one
of Tronds previous postings of this one.


2014-06-23 16:04:08

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH v1 005/104] NFSd: Add fine grained protection for the nfs4_file->fi_stateids list

On Thu, Jun 19, 2014 at 10:49:11AM -0400, Jeff Layton wrote:
> From: Trond Myklebust <[email protected]>
>
> Signed-off-by: Trond Myklebust <[email protected]>

Looks good, but should have a patch description that better tells
why this is done (looks like preparing for nfs_lock_state() removal
again, not fixing some existing race)

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

2014-06-19 14:52:50

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 078/104] nfsd: move unhash_client_locked call into mark_client_expired_locked

All the callers except for the fault injection code call it directly
afterward, and in the fault injection case it won't hurt to do so
anyway.

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 8267531ed455..cfb090b9bb21 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -69,6 +69,7 @@ static u64 current_sessionid = 1;
#define CURRENT_STATEID(stateid) (!memcmp((stateid), &currentstateid, sizeof(stateid_t)))

/* forward declarations */
+static void unhash_client_locked(struct nfs4_client *clp);
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(
@@ -139,7 +140,7 @@ static __be32 mark_client_expired_locked(struct nfs4_client *clp)
{
if (atomic_read(&clp->cl_refcount))
return nfserr_jukebox;
- clp->cl_time = 0;
+ unhash_client_locked(clp);
return nfs_ok;
}

@@ -2323,7 +2324,6 @@ nfsd4_create_session(struct svc_rqst *rqstp,
status = mark_client_expired_locked(old);
if (status)
goto out_free_conn;
- unhash_client_locked(old);
}
move_to_confirmed(unconf);
conf = unconf;
@@ -2869,7 +2869,6 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
status = mark_client_expired_locked(old);
if (status)
goto out;
- unhash_client_locked(old);
}
move_to_confirmed(unconf);
conf = unconf;
@@ -4008,7 +4007,6 @@ nfs4_laundromat(struct nfsd_net *nn)
clp->cl_clientid.cl_id);
continue;
}
- unhash_client_locked(clp);
list_add(&clp->cl_lru, &reaplist);
}
spin_unlock(&nn->client_lock);
--
1.9.3


2014-06-19 14:52:47

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 075/104] NFSd: Add assertions to document the nfs4_client/session locking

From: Trond Myklebust <[email protected]>

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 6e6d34111f5f..72ca1b62cf16 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -145,6 +145,10 @@ static __be32 mark_client_expired_locked(struct nfs4_client *clp)

static __be32 get_client_locked(struct nfs4_client *clp)
{
+ struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
+
+ lockdep_assert_held(&nn->client_lock);
+
if (is_client_expired(clp))
return nfserr_expired;
atomic_inc(&clp->cl_refcount);
@@ -185,6 +189,10 @@ renew_client(struct nfs4_client *clp)

static void put_client_renew_locked(struct nfs4_client *clp)
{
+ struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
+
+ lockdep_assert_held(&nn->client_lock);
+
if (!atomic_dec_and_test(&clp->cl_refcount))
return;
if (!is_client_expired(clp))
@@ -216,6 +224,9 @@ static __be32 nfsd4_get_session_locked(struct nfsd4_session *ses)
static void nfsd4_put_session_locked(struct nfsd4_session *ses)
{
struct nfs4_client *clp = ses->se_client;
+ struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
+
+ lockdep_assert_held(&nn->client_lock);

if (atomic_dec_and_test(&ses->se_ref) && is_session_dead(ses))
free_session(ses);
@@ -1262,6 +1273,8 @@ __find_in_sessionid_hashtbl(struct nfs4_sessionid *sessionid, struct net *net)
int idx;
struct nfsd_net *nn = net_generic(net, nfsd_net_id);

+ lockdep_assert_held(&nn->client_lock);
+
dump_sessionid(__func__, sessionid);
idx = hash_sessionid(sessionid);
/* Search in the appropriate list */
@@ -1298,6 +1311,11 @@ out:
static void
unhash_session(struct nfsd4_session *ses)
{
+ struct nfs4_client *clp = ses->se_client;
+ struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
+
+ lockdep_assert_held(&nn->client_lock);
+
list_del(&ses->se_hash);
spin_lock(&ses->se_client->cl_lock);
list_del(&ses->se_perclnt);
@@ -1386,6 +1404,8 @@ unhash_client_locked(struct nfs4_client *clp)
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
struct nfsd4_session *ses;

+ lockdep_assert_held(&nn->client_lock);
+
/* Mark the client as expired! */
clp->cl_time = 0;
/* Make it invisible */
@@ -1727,6 +1747,8 @@ add_to_unconfirmed(struct nfs4_client *clp)
unsigned int idhashval;
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);

+ lockdep_assert_held(&nn->client_lock);
+
clear_bit(NFSD4_CLIENT_CONFIRMED, &clp->cl_flags);
add_clp_to_name_tree(clp, &nn->unconf_name_tree);
idhashval = clientid_hashval(clp->cl_clientid.cl_id);
@@ -1740,6 +1762,8 @@ move_to_confirmed(struct nfs4_client *clp)
unsigned int idhashval = clientid_hashval(clp->cl_clientid.cl_id);
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);

+ lockdep_assert_held(&nn->client_lock);
+
dprintk("NFSD: move_to_confirm nfs4_client %p\n", clp);
list_move(&clp->cl_idhash, &nn->conf_id_hashtbl[idhashval]);
rb_erase(&clp->cl_namenode, &nn->unconf_name_tree);
@@ -1770,6 +1794,7 @@ find_confirmed_client(clientid_t *clid, bool sessions, struct nfsd_net *nn)
{
struct list_head *tbl = nn->conf_id_hashtbl;

+ lockdep_assert_held(&nn->client_lock);
return find_client_in_id_table(tbl, clid, sessions);
}

@@ -1778,6 +1803,7 @@ find_unconfirmed_client(clientid_t *clid, bool sessions, struct nfsd_net *nn)
{
struct list_head *tbl = nn->unconf_id_hashtbl;

+ lockdep_assert_held(&nn->client_lock);
return find_client_in_id_table(tbl, clid, sessions);
}

@@ -1789,12 +1815,14 @@ static bool clp_used_exchangeid(struct nfs4_client *clp)
static struct nfs4_client *
find_confirmed_client_by_name(struct xdr_netobj *name, struct nfsd_net *nn)
{
+ lockdep_assert_held(&nn->client_lock);
return find_clp_in_name_tree(name, &nn->conf_name_tree);
}

static struct nfs4_client *
find_unconfirmed_client_by_name(struct xdr_netobj *name, struct nfsd_net *nn)
{
+ lockdep_assert_held(&nn->client_lock);
return find_clp_in_name_tree(name, &nn->unconf_name_tree);
}

@@ -4758,6 +4786,8 @@ find_lockowner_str_locked(clientid_t *clid, struct xdr_netobj *owner,
unsigned int strhashval = ownerstr_hashval(owner);
struct nfs4_stateowner *so;

+ lockdep_assert_held(&clp->cl_lock);
+
list_for_each_entry(so, &clp->cl_ownerstr_hashtbl[strhashval], so_strhash) {
if (so->so_is_open_owner)
continue;
--
1.9.3


2014-06-19 14:51:09

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 007/104] NFSd: Add locking to the nfs4_file->fi_fds[] array

From: Trond Myklebust <[email protected]>

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 1daab96804a4..f17e3b999be6 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -249,6 +249,52 @@ get_nfs4_file(struct nfs4_file *fi)
atomic_inc(&fi->fi_ref);
}

+static struct file *__nfs4_get_fd(struct nfs4_file *f, int oflag)
+{
+ if (f->fi_fds[oflag])
+ return get_file(f->fi_fds[oflag]);
+ return NULL;
+}
+
+static struct file *find_writeable_file(struct nfs4_file *f)
+{
+ struct file *ret;
+
+ spin_lock(&f->fi_lock);
+ ret = __nfs4_get_fd(f, O_WRONLY);
+ if (!ret)
+ ret = __nfs4_get_fd(f, O_RDWR);
+ spin_unlock(&f->fi_lock);
+ return ret;
+}
+
+static struct file *find_readable_file(struct nfs4_file *f)
+{
+ struct file *ret;
+
+ spin_lock(&f->fi_lock);
+ ret = __nfs4_get_fd(f, O_RDONLY);
+ if (!ret)
+ ret = __nfs4_get_fd(f, O_RDWR);
+ spin_unlock(&f->fi_lock);
+ return ret;
+}
+
+static struct file *find_any_file(struct nfs4_file *f)
+{
+ struct file *ret;
+
+ spin_lock(&f->fi_lock);
+ ret = __nfs4_get_fd(f, O_RDWR);
+ if (!ret) {
+ ret = __nfs4_get_fd(f, O_WRONLY);
+ if (!ret)
+ ret = __nfs4_get_fd(f, O_RDONLY);
+ }
+ spin_unlock(&f->fi_lock);
+ return ret;
+}
+
static int num_delegations;
unsigned long max_delegations;

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

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

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

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

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

return nfs_ok;
}
@@ -3143,13 +3210,15 @@ static int nfs4_setlease(struct nfs4_delegation *dp)
if (status)
goto out_free;
fp->fi_lease = fl;
- fp->fi_deleg_file = get_file(fl->fl_file);
+ fp->fi_deleg_file = fl->fl_file;
atomic_set(&fp->fi_delegees, 1);
spin_lock(&state_lock);
hash_delegation_locked(dp, fp);
spin_unlock(&state_lock);
return 0;
out_free:
+ if (fl->fl_file)
+ fput(fl->fl_file);
locks_free_lock(fl);
return status;
}
@@ -3763,6 +3832,7 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,
status = nfserr_serverfault;
goto out;
}
+ get_file(file);
}
break;
case NFS4_OPEN_STID:
@@ -3790,7 +3860,7 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,
}
status = nfs_ok;
if (file)
- *filpp = get_file(file);
+ *filpp = file;
out:
nfs4_unlock_state();
return status;
@@ -4533,6 +4603,8 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
break;
}
out:
+ if (filp)
+ fput(filp);
if (status && new_state)
release_lockowner(lock_sop);
nfsd4_bump_seqid(cstate, status);
@@ -4674,7 +4746,7 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (!file_lock) {
dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
status = nfserr_jukebox;
- goto out;
+ goto fput;
}
locks_init_lock(file_lock);
file_lock->fl_type = F_UNLCK;
@@ -4696,7 +4768,8 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
}
update_stateid(&stp->st_stid.sc_stateid);
memcpy(&locku->lu_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));
-
+fput:
+ fput(filp);
out:
nfsd4_bump_seqid(cstate, status);
nfs4_unlock_state();
@@ -4706,7 +4779,7 @@ out:

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

/*
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 26234b106182..1c0190c0fd88 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -404,32 +404,6 @@ struct nfs4_file {
bool fi_had_conflict;
};

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


2014-06-19 14:53:08

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 091/104] NFSd: Remove nfs4_lock_state(): nfs4_preprocess_stateid_op()

From: Trond Myklebust <[email protected]>

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

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

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

--
1.9.3


2014-06-19 14:52:18

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 055/104] NFSd: Protect adding/removing open state owners using client_lock

From: Trond Myklebust <[email protected]>

* Ensure that alloc_init_open_stateowner() checks the hashtable
under the 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 e7f7e89f2b8c..73c76a895015 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -71,6 +71,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: */
@@ -876,8 +879,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);
}
@@ -896,18 +904,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);
}
@@ -2898,8 +2917,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)
@@ -2915,7 +2937,8 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp,
struct nfsd4_open *open,
struct nfsd4_compound_state *cstate)
{
- 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)
@@ -2930,7 +2953,15 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp,
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;
}

@@ -3004,13 +3035,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;
@@ -3018,15 +3051,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-06-19 14:52:57

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 083/104] nfsd: add a forget_clients "get" routine with proper locking

Signed-off-by: Jeff Layton <[email protected]>
---
fs/nfsd/fault_inject.c | 3 +--
fs/nfsd/nfs4state.c | 29 +++++++++++++++++++++--------
fs/nfsd/state.h | 4 +++-
3 files changed, 25 insertions(+), 11 deletions(-)

diff --git a/fs/nfsd/fault_inject.c b/fs/nfsd/fault_inject.c
index bbb00dcd926a..bbdd6eddb894 100644
--- a/fs/nfsd/fault_inject.c
+++ b/fs/nfsd/fault_inject.c
@@ -133,11 +133,10 @@ void nfsd_fault_inject_cleanup(void)
static struct nfsd_fault_inject_op inject_ops[] = {
{
.file = "forget_clients",
- .get = nfsd_inject_get,
+ .get = nfsd_inject_print_clients,
.set_val = nfsd_inject_set,
.set_clnt = nfsd_inject_set_client,
.forget = nfsd_forget_client,
- .print = nfsd_print_client,
},
{
.file = "forget_locks",
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 17fd48561cf8..3ca08358b142 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -5570,6 +5570,27 @@ nfs4_check_open_reclaim(clientid_t *clid,
}

#ifdef CONFIG_NFSD_FAULT_INJECTION
+u64
+nfsd_inject_print_clients(struct nfsd_fault_inject_op *op)
+{
+ struct nfs4_client *clp;
+ u64 count = 0;
+ struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, nfsd_net_id);
+ char buf[INET6_ADDRSTRLEN];
+
+ if (!nfsd_netns_ready(nn))
+ return 0;
+
+ spin_lock(&nn->client_lock);
+ list_for_each_entry(clp, &nn->client_lru, cl_lru) {
+ rpc_ntop((struct sockaddr *)&clp->cl_addr, buf, sizeof(buf));
+ printk(KERN_INFO "NFS Client: %s\n", buf);
+ ++count;
+ }
+ spin_unlock(&nn->client_lock);
+
+ return count;
+}

u64 nfsd_forget_client(struct nfs4_client *clp, u64 max)
{
@@ -5585,14 +5606,6 @@ u64 nfsd_forget_client(struct nfs4_client *clp, u64 max)
return 1;
}

-u64 nfsd_print_client(struct nfs4_client *clp, u64 num)
-{
- char buf[INET6_ADDRSTRLEN];
- rpc_ntop((struct sockaddr *)&clp->cl_addr, buf, sizeof(buf));
- printk(KERN_INFO "NFS Client: %s\n", buf);
- return 1;
-}
-
static void nfsd_print_count(struct nfs4_client *clp, unsigned int count,
const char *type)
{
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 880b5acd05f3..083ebd1c9733 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -467,18 +467,20 @@ extern void nfsd4_record_grace_done(struct nfsd_net *nn, time_t boot_time);

/* nfs fault injection functions */
#ifdef CONFIG_NFSD_FAULT_INJECTION
+struct nfsd_fault_inject_op;
+
int nfsd_fault_inject_init(void);
void nfsd_fault_inject_cleanup(void);
u64 nfsd_for_n_state(u64, u64 (*)(struct nfs4_client *, u64));
struct nfs4_client *nfsd_find_client(struct sockaddr_storage *, size_t);

+u64 nfsd_inject_print_clients(struct nfsd_fault_inject_op *op);
u64 nfsd_forget_client(struct nfs4_client *, u64);
u64 nfsd_forget_client_locks(struct nfs4_client*, u64);
u64 nfsd_forget_client_openowners(struct nfs4_client *, u64);
u64 nfsd_forget_client_delegations(struct nfs4_client *, u64);
u64 nfsd_recall_client_delegations(struct nfs4_client *, u64);

-u64 nfsd_print_client(struct nfs4_client *, u64);
u64 nfsd_print_client_locks(struct nfs4_client *, u64);
u64 nfsd_print_client_openowners(struct nfs4_client *, u64);
u64 nfsd_print_client_delegations(struct nfs4_client *, u64);
--
1.9.3


2014-06-19 14:52:15

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 053/104] 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.

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 28bfffbafb0c..46629846fd7d 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -782,7 +782,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);
}
@@ -879,8 +879,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);
}
}

@@ -899,7 +900,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);
}
@@ -1372,6 +1372,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);
@@ -2915,7 +2916,7 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp,
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;
@@ -2932,6 +2933,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;
@@ -2947,13 +2949,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();
}
@@ -2984,6 +3000,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;
}
}
@@ -3705,19 +3722,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);
@@ -3842,7 +3850,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();
@@ -4435,31 +4443,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 00f2951423cc..fd87df9f66ed 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-06-23 13:56:48

by Jeff Layton

[permalink] [raw]
Subject: Re: [PATCH v1 000/104] nfsd: eliminate the client_mutex

On Mon, 23 Jun 2014 06:39:26 -0700
Christoph Hellwig <[email protected]> wrote:

> Hi Jeff,
>
> thanks for bringing this forward. I've started looking at the series,
> but I'm a little overwhelmed as it's growing bigger and bigger with
> each posting. That also makes me a little worried about putting all
> of it into 3.17. I know you've started separating a few bits out and
> some of those actually went into 3.16, but maybe we really should
> start to some easier to split piece in first and make sure they get
> into 3.17 and then see if we can get through the rest of it in time.
>

Thanks for taking a look. The big problem with breaking this set up is
that it will likely result in at least some performance regression in
the interim. We're adding more granular locking inside of the
coarse-grained client_mutex, which is likely to mean at least some
slowdown until the client_mutex is removed. Maybe that's worth it
though.

> Besides the obvious fixes for bits that were racy before and don't
> just need better scalability one thing that strikes to mind are some
> of the higher level logic changes, like the changes fixes to various
> stateowner / stateid relations.
>

I'll have to think about how to break those out. Unfortunately, there
are strong dependencies between some of those changes, which makes it
hard to do this in pieces.

> Besides that some higher level comments from glacing over the series:
>
> - the patches that touch locks.c and the lockdep code should go
> first, and include the maintainer (at least for lockdep, locks.c is
> easy :)) and relevant mailing lists.

Fair enough. I meant to cc the lockdep folks on the
lockdep_assert_not_held patch. I don't expect pushback from them, but
you're correct that they should be Cc'ed.

> - lots of patches only have a subject line, but no explanation in the
> body at all. While this might be enough for trivial patches few
> of them are trivial enough that this would be enough.

Ok. I'll try to flesh them out for the next posting.

> - some patches have a signoff from Trond, but no From: line for him
> in the body. I suspect this just got lost somewhere.

Yeah. Some of them were originally written by Trond, but then I ended
up having to do large scale rework of them. I prob should have just
dropped his SOB lines from those. I'll go over those and do that or fix
the From: lines.

> - there is some confusion of NFSd vs nfsd in the subsystem prefixes.
> While it seems odd and against the usual naming NFSd seems to be
> the common one for nfs patches.
>

I tend to prefer "nfsd", but ok -- "NFSd" it is.

> checkpatch.pl also has various valid complains (mostly about too long
> lines) and a few silly ones about printks, might be worth to address
> them.
>

Ahh right. I need to fix those -- Steve French reported a few of those
too (and sent me patches privately).

> Last but not least I get a new compiler warning with the whole series
> applied, as put_client is only used by the fault injection code, but
> still compiled when the config option for it is not set.
>

Good catch. I'll fix that.

> I hope I'll have some more useful detailed reviews soon.
>

Thanks. I should also note that a couple of races were found in the
nfs4_ol_stateid handling while testing this set, so I'll be respinning
it to fix those up anyway.

--
Jeff Layton <[email protected]>

2014-06-19 14:51:02

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 002/104] NFSd: Avoid taking state_lock while holding inode lock in nfsd_break_one_deleg

From: Trond Myklebust <[email protected]>

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

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

diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index 2c73cae9899d..3d67bbf26cee 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -1011,9 +1011,8 @@ static void nfsd4_process_cb_update(struct nfsd4_callback *cb)
run_nfsd4_cb(cb);
}

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

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

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

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

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

run_nfsd4_cb(&dp->dl_recall);
}
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 4e4b054a545e..42c6d6ecc77d 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -438,7 +438,9 @@ hash_delegation_locked(struct nfs4_delegation *dp, struct nfs4_file *fp)
lockdep_assert_held(&state_lock);

dp->dl_stid.sc_type = NFS4_DELEG_STID;
+ spin_lock(&fp->fi_lock);
list_add(&dp->dl_perfile, &fp->fi_delegations);
+ spin_unlock(&fp->fi_lock);
list_add(&dp->dl_perclnt, &dp->dl_stid.sc_client->cl_delegations);
}

@@ -446,14 +448,18 @@ hash_delegation_locked(struct nfs4_delegation *dp, struct nfs4_file *fp)
static void
unhash_delegation(struct nfs4_delegation *dp)
{
+ struct nfs4_file *fp = dp->dl_file;
+
spin_lock(&state_lock);
list_del_init(&dp->dl_perclnt);
- list_del_init(&dp->dl_perfile);
list_del_init(&dp->dl_recall_lru);
+ spin_lock(&fp->fi_lock);
+ list_del_init(&dp->dl_perfile);
+ spin_unlock(&fp->fi_lock);
spin_unlock(&state_lock);
- if (dp->dl_file) {
- nfs4_put_deleg_lease(dp->dl_file);
- put_nfs4_file(dp->dl_file);
+ if (fp) {
+ nfs4_put_deleg_lease(fp);
+ put_nfs4_file(fp);
dp->dl_file = NULL;
}
}
@@ -2538,6 +2544,7 @@ static void nfsd4_init_file(struct nfs4_file *fp, struct inode *ino)
lockdep_assert_held(&state_lock);

atomic_set(&fp->fi_ref, 1);
+ spin_lock_init(&fp->fi_lock);
INIT_LIST_HEAD(&fp->fi_stateids);
INIT_LIST_HEAD(&fp->fi_delegations);
ihold(ino);
@@ -2783,24 +2790,31 @@ out:
return ret;
}

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

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

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

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

static
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 374c66283ac5..366887ab5204 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -382,6 +382,7 @@ static inline struct nfs4_lockowner * lockowner(struct nfs4_stateowner *so)
/* nfs4_file: a file opened by some number of (open) nfs4_stateowners. */
struct nfs4_file {
atomic_t fi_ref;
+ spinlock_t fi_lock;
struct hlist_node fi_hash; /* hash by "struct inode *" */
struct list_head fi_stateids;
struct list_head fi_delegations;
@@ -472,6 +473,7 @@ extern void nfsd4_cb_recall(struct nfs4_delegation *dp);
extern int nfsd4_create_callback_queue(void);
extern void nfsd4_destroy_callback_queue(void);
extern void nfsd4_shutdown_callback(struct nfs4_client *);
+extern void nfsd4_prepare_cb_recall(struct nfs4_delegation *dp);
extern void nfs4_put_delegation(struct nfs4_delegation *dp);
extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(const char *name,
struct nfsd_net *nn);
--
1.9.3


2014-06-19 14:51:57

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 041/104] 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.

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 3b0144269f9d..ff6e0f99413c 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4224,6 +4224,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;
@@ -4364,12 +4366,12 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp,
if (!test_access(od->od_share_access, stp)) {
dprintk("NFSD: access not a subset current bitmap: 0x%lx, input access=%08x\n",
stp->st_access_bmap, od->od_share_access);
- goto out;
+ goto put_stateid;
}
if (!test_deny(od->od_share_deny, stp)) {
dprintk("NFSD:deny not a subset current bitmap: 0x%lx, input deny=%08x\n",
stp->st_deny_bmap, od->od_share_deny);
- goto out;
+ goto put_stateid;
}
nfs4_stateid_downgrade(stp, od->od_share_access, od->od_share_deny);

@@ -4378,6 +4380,8 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp,
update_stateid(&stp->st_stid.sc_stateid);
memcpy(&od->od_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));
status = nfs_ok;
+put_stateid:
+ put_generic_stateid(stp);
out:
nfsd4_bump_seqid(cstate, status);
nfs4_unlock_state();
@@ -4734,6 +4738,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfs4_openowner *open_sop = NULL;
struct nfs4_lockowner *lock_sop = NULL;
struct nfs4_ol_stateid *lock_stp = NULL;
+ struct nfs4_ol_stateid *open_stp = NULL;
struct file *filp = NULL;
struct file_lock *file_lock = NULL;
struct file_lock *conflock = NULL;
@@ -4760,8 +4765,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,
@@ -4884,6 +4887,8 @@ out:
fput(filp);
if (lock_stp)
put_generic_stateid(lock_stp);
+ if (open_stp)
+ put_generic_stateid(open_stp);
if (status && new_state)
release_lockowner_if_empty(lock_sop);
nfsd4_bump_seqid(cstate, status);
--
1.9.3


2014-06-19 14:52:58

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 084/104] nfsd: add a forget_client set_clnt routine

Signed-off-by: Jeff Layton <[email protected]>
---
fs/nfsd/fault_inject.c | 2 +-
fs/nfsd/nfs4state.c | 27 +++++++++++++++++++++++++++
fs/nfsd/state.h | 2 ++
3 files changed, 30 insertions(+), 1 deletion(-)

diff --git a/fs/nfsd/fault_inject.c b/fs/nfsd/fault_inject.c
index bbdd6eddb894..e24d7f301b71 100644
--- a/fs/nfsd/fault_inject.c
+++ b/fs/nfsd/fault_inject.c
@@ -135,7 +135,7 @@ static struct nfsd_fault_inject_op inject_ops[] = {
.file = "forget_clients",
.get = nfsd_inject_print_clients,
.set_val = nfsd_inject_set,
- .set_clnt = nfsd_inject_set_client,
+ .set_clnt = nfsd_inject_forget_client,
.forget = nfsd_forget_client,
},
{
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 3ca08358b142..6da8766b87dd 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -5606,6 +5606,33 @@ u64 nfsd_forget_client(struct nfs4_client *clp, u64 max)
return 1;
}

+u64
+nfsd_inject_forget_client(struct nfsd_fault_inject_op *op,
+ struct sockaddr_storage *addr, size_t addr_size)
+{
+ u64 count = 0;
+ struct nfs4_client *clp;
+ struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, nfsd_net_id);
+
+ if (!nfsd_netns_ready(nn))
+ return count;
+
+ spin_lock(&nn->client_lock);
+ clp = nfsd_find_client(addr, addr_size);
+ if (clp) {
+ if (mark_client_expired_locked(clp) == nfs_ok)
+ ++count;
+ else
+ clp = NULL;
+ }
+ spin_unlock(&nn->client_lock);
+
+ if (clp)
+ expire_client(clp);
+
+ return count;
+}
+
static void nfsd_print_count(struct nfs4_client *clp, unsigned int count,
const char *type)
{
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 083ebd1c9733..dfcb72a936b3 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -476,6 +476,8 @@ struct nfs4_client *nfsd_find_client(struct sockaddr_storage *, size_t);

u64 nfsd_inject_print_clients(struct nfsd_fault_inject_op *op);
u64 nfsd_forget_client(struct nfs4_client *, u64);
+u64 nfsd_inject_forget_client(struct nfsd_fault_inject_op *, struct sockaddr_storage *, size_t);
+
u64 nfsd_forget_client_locks(struct nfs4_client*, u64);
u64 nfsd_forget_client_openowners(struct nfs4_client *, u64);
u64 nfsd_forget_client_delegations(struct nfs4_client *, u64);
--
1.9.3


2014-06-19 14:53:15

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 096/104] NFSd: Remove nfs4_lock_state(): nfsd4_delegreturn()

From: Trond Myklebust <[email protected]>

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

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

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

--
1.9.3


2014-06-19 14:51:20

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 015/104] NFSd: Cleanup nfs4svc_encode_compoundres

From: Trond Myklebust <[email protected]>

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

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

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

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

+void
+nfsd4_sequence_done(struct nfsd4_compoundres *resp)
+{
+ struct nfsd4_compound_state *cs = &resp->cstate;
+
+ if (nfsd4_has_session(cs)) {
+ struct nfs4_client *clp = cs->session->se_client;
+ struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
+
+ if (cs->status != nfserr_replay_cache) {
+ nfsd4_store_cache_entry(resp);
+ cs->slot->sl_flags &= ~NFSD4_SLOT_INUSE;
+ }
+ /* Renew the clientid on success and on replay */
+ spin_lock(&nn->client_lock);
+ nfsd4_put_session(cs->session);
+ put_client_renew_locked(clp);
+ spin_unlock(&nn->client_lock);
+ }
+}
+
__be32
nfsd4_destroy_clientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_destroy_clientid *dc)
{
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index bd0ebd7ff043..8aa15595b1a7 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -3996,7 +3996,6 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compo
/*
* All that remains is to write the tag and operation count...
*/
- struct nfsd4_compound_state *cs = &resp->cstate;
struct xdr_buf *buf = resp->xdr.buf;

WARN_ON_ONCE(buf->len != buf->head[0].iov_len + buf->page_len +
@@ -4010,19 +4009,7 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compo
p += XDR_QUADLEN(resp->taglen);
*p++ = htonl(resp->opcnt);

- if (nfsd4_has_session(cs)) {
- struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
- struct nfs4_client *clp = cs->session->se_client;
- if (cs->status != nfserr_replay_cache) {
- nfsd4_store_cache_entry(resp);
- cs->slot->sl_flags &= ~NFSD4_SLOT_INUSE;
- }
- /* Renew the clientid on success and on replay */
- spin_lock(&nn->client_lock);
- nfsd4_put_session(cs->session);
- spin_unlock(&nn->client_lock);
- put_client_renew(clp);
- }
+ nfsd4_sequence_done(resp);
return 1;
}

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

/* nfs4recover operations */
extern int nfsd4_client_tracking_init(struct net *net);
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index b577273224e7..570073aac50c 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -593,7 +593,6 @@ extern __be32 nfsd4_setclientid(struct svc_rqst *rqstp,
extern __be32 nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
struct nfsd4_compound_state *,
struct nfsd4_setclientid_confirm *setclientid_confirm);
-extern void nfsd4_store_cache_entry(struct nfsd4_compoundres *resp);
extern __be32 nfsd4_exchange_id(struct svc_rqst *rqstp,
struct nfsd4_compound_state *, struct nfsd4_exchange_id *);
extern __be32 nfsd4_backchannel_ctl(struct svc_rqst *, struct nfsd4_compound_state *, struct nfsd4_backchannel_ctl *);
@@ -604,6 +603,7 @@ extern __be32 nfsd4_create_session(struct svc_rqst *,
extern __be32 nfsd4_sequence(struct svc_rqst *,
struct nfsd4_compound_state *,
struct nfsd4_sequence *);
+extern void nfsd4_sequence_done(struct nfsd4_compoundres *resp);
extern __be32 nfsd4_destroy_session(struct svc_rqst *,
struct nfsd4_compound_state *,
struct nfsd4_destroy_session *);
--
1.9.3


2014-06-23 13:54:20

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH v1 002/104] NFSd: Avoid taking state_lock while holding inode lock in nfsd_break_one_deleg

On Thu, Jun 19, 2014 at 10:49:08AM -0400, Jeff Layton wrote:
> From: Trond Myklebust <[email protected]>
>
> state_lock is a heavily contended global lock. We don't want to grab
> that while simultaneously holding the inode->i_lock
> Instead do the list manipulations from the work queue context prior to
> starting the rpc call.

The description should mention that a new lock is added to facilitate this.

> void nfsd4_init_callback(struct nfsd4_callback *cb)
> {
> INIT_WORK(&cb->cb_work, nfsd4_do_callback_rpc);
> }

We still call nfsd4_init_callback for delegations after this patch,
and thus first initialize the work item to nfsd4_do_callback_rpc
and later overide it. But I think initializing the work item
before queueing up the callback is pretty silly anyway, so I'd suggest
adding a patch before this one to just kill nfsd4_init_callback and
initialize the item in do_probe_callback and nfsd4_cb_recall directly.

> -static void nfsd_break_one_deleg(struct nfs4_delegation *dp)
> +void nfsd4_prepare_cb_recall(struct nfs4_delegation *dp)
> {
> struct nfs4_client *clp = dp->dl_stid.sc_client;
> struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
>
> - lockdep_assert_held(&state_lock);
> + /*
> + * We can't do this in nfsd_break_deleg_cb because it is
> + * already holding inode->i_lock
> + */

Can't is a little too strong given that it did indeed work before.


2014-06-19 14:51:32

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 024/104] 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 | 50 ++++++++++++++++++++++++--------------------------
fs/nfsd/state.h | 1 -
2 files changed, 24 insertions(+), 27 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 068347ab4dd0..7568a05b76f1 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -725,11 +725,11 @@ release_all_access(struct nfs4_ol_stateid *stp)

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

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

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

@@ -768,7 +766,7 @@ static void __release_lock_stateid(struct nfs4_lockowner *lo,
list_del(&stp->st_locks);
unhash_generic_stateid(stp);
unhash_stid(&stp->st_stid);
- file = find_any_file(stp->st_file);
+ file = find_any_file(stp->st_stid.sc_file);
if (file)
filp_close(file, (fl_owner_t)lo);
close_generic_stateid(stp);
@@ -2838,7 +2836,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);
@@ -3449,7 +3447,7 @@ nfs4_open_delegation(struct net *net, struct svc_fh *fh,
dp = alloc_init_deleg(oo->oo_owner.so_client, stp, fh);
if (dp == NULL)
goto out_no_deleg;
- status = nfs4_set_delegation(dp, stp->st_file);
+ status = nfs4_set_delegation(dp, stp->st_stid.sc_file);
if (status)
goto out_free;

@@ -3755,7 +3753,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;
}
@@ -3984,9 +3982,9 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,
goto out;
if (filpp) {
if (flags & RD_STATE)
- file = find_readable_file(stp->st_file);
+ file = find_readable_file(stp->st_stid.sc_file);
else
- file = find_writeable_file(stp->st_file);
+ file = find_writeable_file(stp->st_stid.sc_file);
}
break;
default:
@@ -4006,7 +4004,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;
@@ -4193,7 +4191,7 @@ static void nfs4_stateid_downgrade_bit(struct nfs4_ol_stateid *stp, u32 access)
{
if (!test_access(access, stp))
return;
- nfs4_file_put_access(stp->st_file, access, 0);
+ nfs4_file_put_access(stp->st_stid.sc_file, access, 0);
clear_access(access, stp);
}

@@ -4201,7 +4199,7 @@ static void nfs4_stateid_downgrade_deny_bit(struct nfs4_ol_stateid *stp, u32 den
{
if (!test_deny(deny, stp))
return;
- nfs4_file_put_access(stp->st_file, 0, deny);
+ nfs4_file_put_access(stp->st_stid.sc_file, 0, deny);
clear_deny(deny, stp);
}

@@ -4246,7 +4244,7 @@ reset_union_bmap_deny(unsigned long deny, struct nfs4_ol_stateid *stp)
if (i == deny)
continue;
if (test_deny(i, stp)) {
- nfs4_file_put_access(stp->st_file, 0, i);
+ nfs4_file_put_access(stp->st_stid.sc_file, 0, i);
clear_deny(i, stp);
}
}
@@ -4311,9 +4309,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;
/*
@@ -4523,7 +4521,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;
@@ -4540,7 +4538,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;
@@ -4556,7 +4554,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;

if (test_access(access, lock_stp))
return;
@@ -4566,7 +4564,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;
@@ -4693,14 +4691,14 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
switch (lock->lk_type) {
case NFS4_READ_LT:
case NFS4_READW_LT:
- filp = find_readable_file(lock_stp->st_file);
+ filp = find_readable_file(lock_stp->st_stid.sc_file);
if (filp)
get_lock_access(lock_stp, NFS4_SHARE_ACCESS_READ);
file_lock->fl_type = F_RDLCK;
break;
case NFS4_WRITE_LT:
case NFS4_WRITEW_LT:
- filp = find_writeable_file(lock_stp->st_file);
+ filp = find_writeable_file(lock_stp->st_stid.sc_file);
if (filp)
get_lock_access(lock_stp, NFS4_SHARE_ACCESS_WRITE);
file_lock->fl_type = F_WRLCK;
@@ -4883,7 +4881,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;
@@ -4987,7 +4985,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
list_for_each_entry(stp, &sop->so_stateids,
st_perstateowner) {
lo = lockowner(sop);
- if (check_for_locks(stp->st_file, lo))
+ if (check_for_locks(stp->st_stid.sc_file, lo))
goto out;
list_add(&lo->lo_list, &matches);
}
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index eaddef9048e4..600c24e475d1 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -408,7 +408,6 @@ struct nfs4_ol_stateid {
struct list_head st_perstateowner;
struct list_head st_locks;
struct nfs4_stateowner * st_stateowner;
- struct nfs4_file * st_file;
unsigned long st_access_bmap;
unsigned long st_deny_bmap;
struct nfs4_ol_stateid * st_openstp;
--
1.9.3


2014-06-19 14:51:34

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 025/104] NFSd: Ensure stateids remain unique until they are freed

From: Trond Myklebust <[email protected]>

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

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 7568a05b76f1..6c04b5bb10f7 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -522,6 +522,7 @@ static void remove_stid(struct nfs4_stid *s)

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

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

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

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

@@ -3890,6 +3890,7 @@ static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid)
default:
printk("unknown stateid type %x\n", s->sc_type);
case NFS4_CLOSED_STID:
+ case NFS4_CLOSED_DELEG_STID:
return nfserr_bad_stateid;
}
}
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 600c24e475d1..78ed70bd666d 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -81,6 +81,7 @@ struct nfs4_stid {
#define NFS4_CLOSED_STID 8
/* For a deleg stateid kept around only to process free_stateid's: */
#define NFS4_REVOKED_DELEG_STID 16
+#define NFS4_CLOSED_DELEG_STID 32
unsigned char sc_type;
stateid_t sc_stateid;
struct nfs4_client *sc_client;
--
1.9.3


2014-06-23 16:24:22

by Jeff Layton

[permalink] [raw]
Subject: Re: [PATCH v1 006/104] NFSd: Add a mutex to protect the NFSv4.0 open owner replay cache

On Mon, 23 Jun 2014 09:05:49 -0700
Christoph Hellwig <[email protected]> wrote:

> On Thu, Jun 19, 2014 at 10:49:12AM -0400, Jeff Layton wrote:
> > From: Trond Myklebust <[email protected]>
> >
> > We don't want to rely on the state_lock() for protection in the
> > case of NFSv4 open owners. Instead, we add a mutex that will
> > only be taken for NFSv4.0 state mutating operations, and
> > that will be released once the entire compound is done.
> >
> > Signed-off-by: Trond Myklebust <[email protected]>
>
> Looks reasonable to me, but doesn't this create a lock order reversal
> with the client_lock until it is removed?
>

No, I don't think so. AFAICT, the new mutex is always taken inside of
the client_mutex until that point.

--
Jeff Layton <[email protected]>

2014-06-19 14:52:29

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 062/104] lockdep: add lockdep_assert_not_held

We currently have the ability to call lockdep_assert_held to throw a
warning when a spinlock isn't held in a codepath. There are also times
when we'd like to throw a warning when a lock is held (i.e. when there
is the potential for deadlock).

Signed-off-by: Jeff Layton <[email protected]>
---
include/linux/lockdep.h | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h
index 008388f920d7..ab0cc8e224ee 100644
--- a/include/linux/lockdep.h
+++ b/include/linux/lockdep.h
@@ -362,6 +362,10 @@ extern void lockdep_trace_alloc(gfp_t mask);
WARN_ON(debug_locks && !lockdep_is_held(l)); \
} while (0)

+#define lockdep_assert_not_held(l) do { \
+ WARN_ON(debug_locks && lockdep_is_held(l)); \
+ } while (0)
+
#define lockdep_recursing(tsk) ((tsk)->lockdep_recursion)

#else /* !CONFIG_LOCKDEP */
@@ -413,6 +417,8 @@ struct lock_class_key { };

#define lockdep_assert_held(l) do { (void)(l); } while (0)

+#define lockdep_assert_not_held(l) do { (void)(l); } while (0)
+
#define lockdep_recursing(tsk) (0)

#endif /* !LOCKDEP */
--
1.9.3


2014-06-23 16:32:35

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH v1 005/104] NFSd: Add fine grained protection for the nfs4_file->fi_stateids list

On Mon, Jun 23, 2014 at 12:24:58PM -0400, Jeff Layton wrote:
> On Mon, 23 Jun 2014 09:23:32 -0700
> Christoph Hellwig <[email protected]> wrote:
>
> > On Mon, Jun 23, 2014 at 12:20:39PM -0400, Jeff Layton wrote:
> > > Correct. I'll add that into the desciption if you think it's warranted,
> > > but again that's the case with many of the patches in this series.
> >
> > It might be fairly obvious in the context of the series, it's not
> > if someone goes back in history with a git-blame.
> >
> > And it's not a 100% obvious with the series either, at least earlier
> > versions also fixes pre-existing races, although all of that might be
> > upstream now.
> >
> > So as far as I am concerned a simple one-line blurb mentioning what
> > the new locking is for would be useful to be added to all patches
> > just changing the locking.
>
> Fair enough. I'll do that.

I appreciate that it's tedious but yes, thanks, that would be helpful.

--b.

2014-06-19 14:51:54

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 039/104] 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.

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 7730a7581798..cf6baac38d98 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4433,10 +4433,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:
nfs4_unlock_state();
return status;
--
1.9.3


2014-06-19 14:52:41

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 071/104] NFSd: Protect unconfirmed client creation using client_lock

From: Trond Myklebust <[email protected]>

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

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

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

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

/* Cases below refer to rfc 5661 section 18.35.4: */
nfs4_lock_state();
+ spin_lock(&nn->client_lock);
conf = find_confirmed_client_by_name(&exid->clname, nn);
if (conf) {
bool creds_match = same_creds(&conf->cl_cred, &rqstp->rq_cred);
@@ -2039,7 +2041,6 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
status = nfserr_clid_inuse;
goto out;
}
- expire_client(conf);
goto out_new;
}
if (verfs_match) { /* case 2 */
@@ -2047,6 +2048,7 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
goto out_copy;
}
/* case 5, client reboot */
+ conf = NULL;
goto out_new;
}

@@ -2057,17 +2059,18 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,

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

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

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

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

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

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

--
1.9.3


2014-06-19 14:53:04

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 088/104] nfsd: add more granular locking to forget_openowners fault injector

Also, fix up the printk output that is generated when the file is read.
It currently says that it's reporting the number of open files, but
it's actually reporting the number of openowners.

Signed-off-by: Jeff Layton <[email protected]>
---
fs/nfsd/fault_inject.c | 8 ++--
fs/nfsd/nfs4state.c | 113 +++++++++++++++++++++++++++++++++++++++++++++----
fs/nfsd/state.h | 6 ++-
3 files changed, 112 insertions(+), 15 deletions(-)

diff --git a/fs/nfsd/fault_inject.c b/fs/nfsd/fault_inject.c
index 675594cd316c..0c2ab9caf803 100644
--- a/fs/nfsd/fault_inject.c
+++ b/fs/nfsd/fault_inject.c
@@ -145,11 +145,9 @@ static struct nfsd_fault_inject_op inject_ops[] = {
},
{
.file = "forget_openowners",
- .get = nfsd_inject_get,
- .set_val = nfsd_inject_set,
- .set_clnt = nfsd_inject_set_client,
- .forget = nfsd_forget_client_openowners,
- .print = nfsd_print_client_openowners,
+ .get = nfsd_inject_print_openowners,
+ .set_val = nfsd_inject_forget_openowners,
+ .set_clnt = nfsd_inject_forget_client_openowners,
},
{
.file = "forget_delegations",
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index af726e934ea9..2f18de7dc913 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -5795,30 +5795,127 @@ nfsd_inject_forget_locks(struct nfsd_fault_inject_op *op, u64 max)
return count;
}

-static u64 nfsd_foreach_client_open(struct nfs4_client *clp, u64 max, void (*func)(struct nfs4_openowner *))
+static u64
+nfsd_foreach_client_openowner(struct nfs4_client *clp, u64 max,
+ struct list_head *collect, void (*func)(struct nfs4_openowner *))
{
struct nfs4_openowner *oop, *next;
+ struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, nfsd_net_id);
u64 count = 0;

+ lockdep_assert_held(&nn->client_lock);
+
+ spin_lock(&clp->cl_lock);
list_for_each_entry_safe(oop, next, &clp->cl_openowners, oo_perclient) {
- if (func)
+ if (func) {
func(oop);
- if (++count == max)
+ if (collect) {
+ atomic_inc(&clp->cl_refcount);
+ list_add(&oop->oo_perclient, collect);
+ }
+ }
+ ++count;
+ /*
+ * Despite the fact that these functions deal with
+ * 64-bit integers for "count", we must ensure that
+ * it doesn't blow up the clp->cl_refcount. Throw a
+ * warning if we start to approach INT_MAX here.
+ */
+ WARN_ON_ONCE(count == (INT_MAX / 2));
+ if (count == max)
break;
}
+ spin_unlock(&clp->cl_lock);
+
+ return count;
+}
+
+static u64
+nfsd_print_client_openowners(struct nfs4_client *clp)
+{
+ u64 count = nfsd_foreach_client_openowner(clp, 0, NULL, NULL);
+ nfsd_print_count(clp, count, "openowners");
+ return count;
+}
+
+static u64
+nfsd_collect_client_openowners(struct nfs4_client *clp, struct list_head *collect, u64 max)
+{
+ return nfsd_foreach_client_openowner(clp, max, collect, unhash_openowner_locked);
+}
+
+u64
+nfsd_inject_print_openowners(struct nfsd_fault_inject_op *op)
+{
+ struct nfs4_client *clp;
+ u64 count = 0;
+ struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, nfsd_net_id);
+
+ if (!nfsd_netns_ready(nn))
+ return 0;
+
+ spin_lock(&nn->client_lock);
+ list_for_each_entry(clp, &nn->client_lru, cl_lru)
+ count += nfsd_print_client_openowners(clp);
+ spin_unlock(&nn->client_lock);

return count;
}

-u64 nfsd_forget_client_openowners(struct nfs4_client *clp, u64 max)
+static void
+nfsd_reap_openowners(struct list_head *reaplist)
+{
+ struct nfs4_client *clp;
+ struct nfs4_openowner *oop, *next;
+
+ list_for_each_entry_safe(oop, next, reaplist, oo_perclient) {
+ list_del_init(&oop->oo_perclient);
+ clp = oop->oo_owner.so_client;
+ release_openowner(oop);
+ put_client(clp);
+ }
+}
+
+u64
+nfsd_inject_forget_client_openowners(struct nfsd_fault_inject_op *op,
+ struct sockaddr_storage *addr, size_t addr_size)
{
- return nfsd_foreach_client_open(clp, max, release_openowner);
+ unsigned int count = 0;
+ struct nfs4_client *clp;
+ struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, nfsd_net_id);
+ LIST_HEAD(reaplist);
+
+ if (!nfsd_netns_ready(nn))
+ return count;
+
+ spin_lock(&nn->client_lock);
+ clp = nfsd_find_client(addr, addr_size);
+ if (clp)
+ count = nfsd_collect_client_openowners(clp, &reaplist, 0);
+ spin_unlock(&nn->client_lock);
+ nfsd_reap_openowners(&reaplist);
+ return count;
}

-u64 nfsd_print_client_openowners(struct nfs4_client *clp, u64 max)
+u64
+nfsd_inject_forget_openowners(struct nfsd_fault_inject_op *op, u64 max)
{
- u64 count = nfsd_foreach_client_open(clp, max, NULL);
- nfsd_print_count(clp, count, "open files");
+ u64 count = 0;
+ struct nfs4_client *clp;
+ struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, nfsd_net_id);
+ LIST_HEAD(reaplist);
+
+ if (!nfsd_netns_ready(nn))
+ return count;
+
+ spin_lock(&nn->client_lock);
+ list_for_each_entry(clp, &nn->client_lru, cl_lru) {
+ count += nfsd_collect_client_openowners(clp, &reaplist, max - count);
+ if (max != 0 && count >= max)
+ break;
+ }
+ spin_unlock(&nn->client_lock);
+ nfsd_reap_openowners(&reaplist);
return count;
}

diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index fdac0c19cb3c..597841e691bb 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -482,11 +482,13 @@ u64 nfsd_inject_print_locks(struct nfsd_fault_inject_op *);
u64 nfsd_inject_forget_client_locks(struct nfsd_fault_inject_op *, struct sockaddr_storage *, size_t);
u64 nfsd_inject_forget_locks(struct nfsd_fault_inject_op *, u64);

-u64 nfsd_forget_client_openowners(struct nfs4_client *, u64);
+u64 nfsd_inject_print_openowners(struct nfsd_fault_inject_op *);
+u64 nfsd_inject_forget_client_openowners(struct nfsd_fault_inject_op *, struct sockaddr_storage *, size_t);
+u64 nfsd_inject_forget_openowners(struct nfsd_fault_inject_op *, u64);
+
u64 nfsd_forget_client_delegations(struct nfs4_client *, u64);
u64 nfsd_recall_client_delegations(struct nfs4_client *, u64);

-u64 nfsd_print_client_openowners(struct nfs4_client *, u64);
u64 nfsd_print_client_delegations(struct nfs4_client *, u64);
#else /* CONFIG_NFSD_FAULT_INJECTION */
static inline int nfsd_fault_inject_init(void) { return 0; }
--
1.9.3


2014-06-23 16:00:03

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH v1 000/104] nfsd: eliminate the client_mutex

On Mon, Jun 23, 2014 at 09:56:45AM -0400, Jeff Layton wrote:
> Thanks for taking a look. The big problem with breaking this set up is
> that it will likely result in at least some performance regression in
> the interim. We're adding more granular locking inside of the
> coarse-grained client_mutex, which is likely to mean at least some
> slowdown until the client_mutex is removed. Maybe that's worth it
> though.

That's why I didn't dare to ask for splitting the lock breakup, but
rather the surrounding fixes and logic changes. But yes, if that's not
doable either we'll have to bite the bullet.

> > - there is some confusion of NFSd vs nfsd in the subsystem prefixes.
> > While it seems odd and against the usual naming NFSd seems to be
> > the common one for nfs patches.
> >
>
> I tend to prefer "nfsd", but ok -- "NFSd" it is.

I'd prefer nfsd as well, but in Rome do as the Romans do, so..


2014-06-24 11:59:29

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH v1 014/104] NFSd: NFSv4 lock-owners are not associated to a specific file

> +static void hash_lockowner(struct nfs4_lockowner *lo, unsigned int strhashval, struct nfs4_client *clp)
> {
> struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
>
> list_add(&lo->lo_owner.so_strhash, &nn->ownerstr_hashtbl[strhashval]);
> }

I'd just merge this into the only caller.

Otherwise looks good,

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

2014-06-19 14:53:11

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 093/104] NFSd: Remove nfs4_lock_state(): nfsd4_release_lockowner

From: Trond Myklebust <[email protected]>

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 6aba06ec5dfc..254ae3e7f8b6 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -5423,11 +5423,9 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n",
clid->cl_boot, clid->cl_id);

- nfs4_lock_state();
-
status = lookup_clientid(clid, cstate, nn);
if (status)
- goto out;
+ return status;

status = nfserr_locks_held;
INIT_LIST_HEAD(&matches);
@@ -5468,7 +5466,6 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
out:
if (sop)
nfs4_put_stateowner(sop);
- nfs4_unlock_state();
return status;
}

--
1.9.3


2014-06-19 14:51:27

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 020/104] NFSd: Fix delegation revocation

From: Trond Myklebust <[email protected]>

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

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 6f9a0d2d0223..d708e7c807d5 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -566,13 +566,13 @@ hash_delegation_locked(struct nfs4_delegation *dp, struct nfs4_file *fp)
list_add(&dp->dl_perclnt, &dp->dl_stid.sc_client->cl_delegations);
}

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

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

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

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

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

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

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

@@ -1317,12 +1325,13 @@ destroy_client(struct nfs4_client *clp)
spin_lock(&state_lock);
while (!list_empty(&clp->cl_delegations)) {
dp = list_entry(clp->cl_delegations.next, struct nfs4_delegation, dl_perclnt);
- list_del_init(&dp->dl_perclnt);
- list_move(&dp->dl_recall_lru, &reaplist);
+ unhash_delegation_locked(dp);
+ list_add(&dp->dl_recall_lru, &reaplist);
}
spin_unlock(&state_lock);
while (!list_empty(&reaplist)) {
dp = list_entry(reaplist.next, struct nfs4_delegation, dl_recall_lru);
+ list_del_init(&dp->dl_recall_lru);
destroy_delegation(dp);
}
list_splice_init(&clp->cl_revoked, &reaplist);
@@ -3449,7 +3458,7 @@ nfs4_open_delegation(struct net *net, struct svc_fh *fh,
open->op_delegate_type = NFS4_OPEN_DELEGATE_READ;
return;
out_free:
- destroy_delegation(dp);
+ unhash_and_destroy_delegation(dp);
out_no_deleg:
open->op_delegate_type = NFS4_OPEN_DELEGATE_NONE;
if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS &&
@@ -3703,7 +3712,8 @@ nfs4_laundromat(struct nfsd_net *nn)
new_timeo = min(new_timeo, t);
break;
}
- list_move(&dp->dl_recall_lru, &reaplist);
+ unhash_delegation_locked(dp);
+ list_add(&dp->dl_recall_lru, &reaplist);
}
spin_unlock(&state_lock);
list_for_each_safe(pos, next, &reaplist) {
@@ -4371,7 +4381,7 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (status)
goto out;

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

@@ -5190,8 +5200,10 @@ static u64 nfsd_find_all_delegations(struct nfs4_client *clp, u64 max,

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

--
1.9.3


2014-06-19 14:51:48

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 035/104] 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.

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 d44f727fd9b5..08425b1bc3b1 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4987,10 +4987,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) {
@@ -5020,6 +5022,8 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
memcpy(&locku->lu_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));
fput:
fput(filp);
+put_stateid:
+ put_generic_stateid(stp);
out:
nfsd4_bump_seqid(cstate, status);
nfs4_unlock_state();
--
1.9.3


2014-06-19 14:50:59

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 001/104] nfsd: Protect addition to the file_hashtbl

From: Trond Myklebust <[email protected]>

Ensure that we only can have a single struct nfs4_file per inode
in the file_hashtbl and make addition atomic with respect to lookup.

To prevent an i_lock/state_lock inversion, change nfsd4_init_file to
use ihold instead if igrab. That's also more efficient anyway as we
definitely hold a reference to the inode at that point.

Signed-off-by: Trond Myklebust <[email protected]>
Signed-off-by: Jeff Layton <[email protected]>
Reviewed-by: Christoph Hellwig <[email protected]>
---
fs/nfsd/nfs4state.c | 49 +++++++++++++++++++++++++++++++++++++------------
1 file changed, 37 insertions(+), 12 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 0fea1da3dd24..4e4b054a545e 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -2535,17 +2535,18 @@ static void nfsd4_init_file(struct nfs4_file *fp, struct inode *ino)
{
unsigned int hashval = file_hashval(ino);

+ lockdep_assert_held(&state_lock);
+
atomic_set(&fp->fi_ref, 1);
INIT_LIST_HEAD(&fp->fi_stateids);
INIT_LIST_HEAD(&fp->fi_delegations);
- fp->fi_inode = igrab(ino);
+ ihold(ino);
+ fp->fi_inode = ino;
fp->fi_had_conflict = false;
fp->fi_lease = NULL;
memset(fp->fi_fds, 0, sizeof(fp->fi_fds));
memset(fp->fi_access, 0, sizeof(fp->fi_access));
- spin_lock(&state_lock);
hlist_add_head(&fp->fi_hash, &file_hashtbl[hashval]);
- spin_unlock(&state_lock);
}

void
@@ -2711,23 +2712,49 @@ find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open,

/* search file_hashtbl[] for file */
static struct nfs4_file *
-find_file(struct inode *ino)
+find_file_locked(struct inode *ino)
{
unsigned int hashval = file_hashval(ino);
struct nfs4_file *fp;

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

+static struct nfs4_file *
+find_file(struct inode *ino)
+{
+ struct nfs4_file *fp;
+
+ spin_lock(&state_lock);
+ fp = find_file_locked(ino);
+ spin_unlock(&state_lock);
+ return fp;
+}
+
+static struct nfs4_file *
+find_or_add_file(struct inode *ino, struct nfs4_file *new)
+{
+ struct nfs4_file *fp;
+
+ spin_lock(&state_lock);
+ fp = find_file_locked(ino);
+ if (fp == NULL) {
+ nfsd4_init_file(new, ino);
+ fp = new;
+ }
+ spin_unlock(&state_lock);
+
+ return fp;
+}
+
/*
* Called to check deny when READ with all zero stateid or
* WRITE with all zero or all one stateid
@@ -3246,21 +3273,19 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
* and check for delegations in the process of being recalled.
* If not found, create the nfs4_file struct
*/
- fp = find_file(ino);
- if (fp) {
+ fp = find_or_add_file(ino, open->op_file);
+ if (fp != open->op_file) {
if ((status = nfs4_check_open(fp, open, &stp)))
goto out;
status = nfs4_check_deleg(cl, open, &dp);
if (status)
goto out;
} else {
+ open->op_file = NULL;
status = nfserr_bad_stateid;
if (nfsd4_is_deleg_cur(open))
goto out;
status = nfserr_jukebox;
- fp = open->op_file;
- open->op_file = NULL;
- nfsd4_init_file(fp, ino);
}

/*
--
1.9.3


2014-06-19 14:53:07

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 090/104] nfsd: remove old fault injection infrastructure

Remove the old nfsd_for_n_state function and move nfsd_find_client
higher up into the file to get rid of forward declaration. Remove
the struct nfsd_fault_inject_op arguments from the operations as
they are no longer needed by any of them.

Finally, remove the old "standard" get and set routines, which
also eliminates the client_mutex from this code.

Signed-off-by: Jeff Layton <[email protected]>
---
fs/nfsd/fault_inject.c | 51 ++++---------------------------
fs/nfsd/nfs4state.c | 83 ++++++++++++++++++--------------------------------
fs/nfsd/state.h | 40 +++++++++++-------------
3 files changed, 54 insertions(+), 120 deletions(-)

diff --git a/fs/nfsd/fault_inject.c b/fs/nfsd/fault_inject.c
index aa114c893189..13886a1cefdc 100644
--- a/fs/nfsd/fault_inject.c
+++ b/fs/nfsd/fault_inject.c
@@ -17,52 +17,13 @@

struct nfsd_fault_inject_op {
char *file;
- u64 (*get)(struct nfsd_fault_inject_op *);
- u64 (*set_val)(struct nfsd_fault_inject_op *, u64);
- u64 (*set_clnt)(struct nfsd_fault_inject_op *,
- struct sockaddr_storage *, size_t);
- u64 (*forget)(struct nfs4_client *, u64);
- u64 (*print)(struct nfs4_client *, u64);
+ u64 (*get)(void);
+ u64 (*set_val)(u64);
+ u64 (*set_clnt)(struct sockaddr_storage *, size_t);
};

static struct dentry *debug_dir;

-static u64 nfsd_inject_set(struct nfsd_fault_inject_op *op, u64 val)
-{
- u64 count;
-
- nfs4_lock_state();
- count = nfsd_for_n_state(val, op->forget);
- nfs4_unlock_state();
- return count;
-}
-
-static u64 nfsd_inject_set_client(struct nfsd_fault_inject_op *op,
- struct sockaddr_storage *addr,
- size_t addr_size)
-{
- struct nfs4_client *clp;
- u64 count = 0;
-
- nfs4_lock_state();
- clp = nfsd_find_client(addr, addr_size);
- if (clp)
- count = op->forget(clp, 0);
- nfs4_unlock_state();
- return count;
-}
-
-static u64 nfsd_inject_get(struct nfsd_fault_inject_op *op)
-{
- u64 count;
-
- nfs4_lock_state();
- count = nfsd_for_n_state(0, op->print);
- nfs4_unlock_state();
-
- return count;
-}
-
static ssize_t fault_inject_read(struct file *file, char __user *buf,
size_t len, loff_t *ppos)
{
@@ -73,7 +34,7 @@ static ssize_t fault_inject_read(struct file *file, char __user *buf,
struct nfsd_fault_inject_op *op = file_inode(file)->i_private;

if (!pos)
- val = op->get(op);
+ val = op->get();
size = scnprintf(read_buf, sizeof(read_buf), "%llu\n", val);

return simple_read_from_buffer(buf, len, ppos, read_buf, size);
@@ -103,7 +64,7 @@ static ssize_t fault_inject_write(struct file *file, const char __user *buf,

size = rpc_pton(net, write_buf, size, (struct sockaddr *)&sa, sizeof(sa));
if (size > 0) {
- val = op->set_clnt(op, &sa, size);
+ val = op->set_clnt(&sa, size);
if (val)
printk(KERN_INFO "NFSD [%s]: Client %s had %llu state object(s)\n", op->file,
write_buf, val);
@@ -113,7 +74,7 @@ static ssize_t fault_inject_write(struct file *file, const char __user *buf,
printk(KERN_INFO "NFSD Fault Injection: %s (all)", op->file);
else
printk(KERN_INFO "NFSD Fault Injection: %s (n = %llu)", op->file, val);
- val = op->set_val(op, val);
+ val = op->set_val(val);
printk(KERN_INFO "NFSD: %s: found %llu", op->file, val);
}
return len; /* on success, claim we got the whole input */
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 3cae26492cf5..b9b5f9fee7fc 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -5580,8 +5580,24 @@ nfs4_check_open_reclaim(clientid_t *clid,
}

#ifdef CONFIG_NFSD_FAULT_INJECTION
+static struct nfs4_client *
+nfsd_find_client(struct sockaddr_storage *addr, size_t addr_size)
+{
+ struct nfs4_client *clp;
+ struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, nfsd_net_id);
+
+ if (!nfsd_netns_ready(nn))
+ return NULL;
+
+ list_for_each_entry(clp, &nn->client_lru, cl_lru) {
+ if (memcmp(&clp->cl_addr, addr, addr_size) == 0)
+ return clp;
+ }
+ return NULL;
+}
+
u64
-nfsd_inject_print_clients(struct nfsd_fault_inject_op *op)
+nfsd_inject_print_clients(void)
{
struct nfs4_client *clp;
u64 count = 0;
@@ -5603,8 +5619,7 @@ nfsd_inject_print_clients(struct nfsd_fault_inject_op *op)
}

u64
-nfsd_inject_forget_client(struct nfsd_fault_inject_op *op,
- struct sockaddr_storage *addr, size_t addr_size)
+nfsd_inject_forget_client(struct sockaddr_storage *addr, size_t addr_size)
{
u64 count = 0;
struct nfs4_client *clp;
@@ -5630,7 +5645,7 @@ nfsd_inject_forget_client(struct nfsd_fault_inject_op *op,
}

u64
-nfsd_inject_forget_clients(struct nfsd_fault_inject_op *op, u64 max)
+nfsd_inject_forget_clients(u64 max)
{
u64 count = 0;
struct nfs4_client *clp, *next;
@@ -5721,7 +5736,7 @@ nfsd_print_client_locks(struct nfs4_client *clp)
}

u64
-nfsd_inject_print_locks(struct nfsd_fault_inject_op *op)
+nfsd_inject_print_locks(void)
{
struct nfs4_client *clp;
u64 count = 0;
@@ -5753,8 +5768,7 @@ nfsd_reap_locks(struct list_head *reaplist)
}

u64
-nfsd_inject_forget_client_locks(struct nfsd_fault_inject_op *op,
- struct sockaddr_storage *addr, size_t addr_size)
+nfsd_inject_forget_client_locks(struct sockaddr_storage *addr, size_t addr_size)
{
unsigned int count = 0;
struct nfs4_client *clp;
@@ -5774,7 +5788,7 @@ nfsd_inject_forget_client_locks(struct nfsd_fault_inject_op *op,
}

u64
-nfsd_inject_forget_locks(struct nfsd_fault_inject_op *op, u64 max)
+nfsd_inject_forget_locks(u64 max)
{
u64 count = 0;
struct nfs4_client *clp;
@@ -5845,7 +5859,7 @@ nfsd_collect_client_openowners(struct nfs4_client *clp, struct list_head *collec
}

u64
-nfsd_inject_print_openowners(struct nfsd_fault_inject_op *op)
+nfsd_inject_print_openowners(void)
{
struct nfs4_client *clp;
u64 count = 0;
@@ -5877,8 +5891,7 @@ nfsd_reap_openowners(struct list_head *reaplist)
}

u64
-nfsd_inject_forget_client_openowners(struct nfsd_fault_inject_op *op,
- struct sockaddr_storage *addr, size_t addr_size)
+nfsd_inject_forget_client_openowners(struct sockaddr_storage *addr, size_t addr_size)
{
unsigned int count = 0;
struct nfs4_client *clp;
@@ -5898,7 +5911,7 @@ nfsd_inject_forget_client_openowners(struct nfsd_fault_inject_op *op,
}

u64
-nfsd_inject_forget_openowners(struct nfsd_fault_inject_op *op, u64 max)
+nfsd_inject_forget_openowners(u64 max)
{
u64 count = 0;
struct nfs4_client *clp;
@@ -5959,7 +5972,7 @@ nfsd_print_client_delegations(struct nfs4_client *clp)
}

u64
-nfsd_inject_print_delegations(struct nfsd_fault_inject_op *op)
+nfsd_inject_print_delegations(void)
{
struct nfs4_client *clp;
u64 count = 0;
@@ -5991,8 +6004,7 @@ nfsd_forget_delegations(struct list_head *reaplist)
}

u64
-nfsd_inject_forget_client_delegations(struct nfsd_fault_inject_op *op,
- struct sockaddr_storage *addr, size_t addr_size)
+nfsd_inject_forget_client_delegations(struct sockaddr_storage *addr, size_t addr_size)
{
u64 count = 0;
struct nfs4_client *clp;
@@ -6013,7 +6025,7 @@ nfsd_inject_forget_client_delegations(struct nfsd_fault_inject_op *op,
}

u64
-nfsd_inject_forget_delegations(struct nfsd_fault_inject_op *op, u64 max)
+nfsd_inject_forget_delegations(u64 max)
{
u64 count = 0;
struct nfs4_client *clp;
@@ -6049,8 +6061,7 @@ nfsd_recall_delegations(struct list_head *reaplist)
}

u64
-nfsd_inject_recall_client_delegations(struct nfsd_fault_inject_op *op,
- struct sockaddr_storage *addr, size_t addr_size)
+nfsd_inject_recall_client_delegations(struct sockaddr_storage *addr, size_t addr_size)
{
u64 count = 0;
struct nfs4_client *clp;
@@ -6071,7 +6082,7 @@ nfsd_inject_recall_client_delegations(struct nfsd_fault_inject_op *op,
}

u64
-nfsd_inject_recall_delegations(struct nfsd_fault_inject_op *op, u64 max)
+nfsd_inject_recall_delegations(u64 max)
{
u64 count = 0;
struct nfs4_client *clp, *next;
@@ -6091,40 +6102,6 @@ nfsd_inject_recall_delegations(struct nfsd_fault_inject_op *op, u64 max)
nfsd_recall_delegations(&reaplist);
return count;
}
-
-u64 nfsd_for_n_state(u64 max, u64 (*func)(struct nfs4_client *, u64))
-{
- struct nfs4_client *clp, *next;
- u64 count = 0;
- struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, nfsd_net_id);
-
- if (!nfsd_netns_ready(nn))
- return 0;
-
- list_for_each_entry_safe(clp, next, &nn->client_lru, cl_lru) {
- count += func(clp, max - count);
- if ((max != 0) && (count >= max))
- break;
- }
-
- return count;
-}
-
-struct nfs4_client *nfsd_find_client(struct sockaddr_storage *addr, size_t addr_size)
-{
- struct nfs4_client *clp;
- struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, nfsd_net_id);
-
- if (!nfsd_netns_ready(nn))
- return NULL;
-
- list_for_each_entry(clp, &nn->client_lru, cl_lru) {
- if (memcmp(&clp->cl_addr, addr, addr_size) == 0)
- return clp;
- }
- return NULL;
-}
-
#endif /* CONFIG_NFSD_FAULT_INJECTION */

/*
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 4f6b65189031..03cc1ca7b974 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -467,30 +467,26 @@ extern void nfsd4_record_grace_done(struct nfsd_net *nn, time_t boot_time);

/* nfs fault injection functions */
#ifdef CONFIG_NFSD_FAULT_INJECTION
-struct nfsd_fault_inject_op;
-
int nfsd_fault_inject_init(void);
void nfsd_fault_inject_cleanup(void);
-u64 nfsd_for_n_state(u64, u64 (*)(struct nfs4_client *, u64));
-struct nfs4_client *nfsd_find_client(struct sockaddr_storage *, size_t);
-
-u64 nfsd_inject_print_clients(struct nfsd_fault_inject_op *op);
-u64 nfsd_inject_forget_client(struct nfsd_fault_inject_op *, struct sockaddr_storage *, size_t);
-u64 nfsd_inject_forget_clients(struct nfsd_fault_inject_op *, u64);
-
-u64 nfsd_inject_print_locks(struct nfsd_fault_inject_op *);
-u64 nfsd_inject_forget_client_locks(struct nfsd_fault_inject_op *, struct sockaddr_storage *, size_t);
-u64 nfsd_inject_forget_locks(struct nfsd_fault_inject_op *, u64);
-
-u64 nfsd_inject_print_openowners(struct nfsd_fault_inject_op *);
-u64 nfsd_inject_forget_client_openowners(struct nfsd_fault_inject_op *, struct sockaddr_storage *, size_t);
-u64 nfsd_inject_forget_openowners(struct nfsd_fault_inject_op *, u64);
-
-u64 nfsd_inject_print_delegations(struct nfsd_fault_inject_op *);
-u64 nfsd_inject_forget_client_delegations(struct nfsd_fault_inject_op *, struct sockaddr_storage *, size_t);
-u64 nfsd_inject_forget_delegations(struct nfsd_fault_inject_op *, u64);
-u64 nfsd_inject_recall_client_delegations(struct nfsd_fault_inject_op *, struct sockaddr_storage *, size_t);
-u64 nfsd_inject_recall_delegations(struct nfsd_fault_inject_op *, u64);
+
+u64 nfsd_inject_print_clients(void);
+u64 nfsd_inject_forget_client(struct sockaddr_storage *, size_t);
+u64 nfsd_inject_forget_clients(u64);
+
+u64 nfsd_inject_print_locks(void);
+u64 nfsd_inject_forget_client_locks(struct sockaddr_storage *, size_t);
+u64 nfsd_inject_forget_locks(u64);
+
+u64 nfsd_inject_print_openowners(void);
+u64 nfsd_inject_forget_client_openowners(struct sockaddr_storage *, size_t);
+u64 nfsd_inject_forget_openowners(u64);
+
+u64 nfsd_inject_print_delegations(void);
+u64 nfsd_inject_forget_client_delegations(struct sockaddr_storage *, size_t);
+u64 nfsd_inject_forget_delegations(u64);
+u64 nfsd_inject_recall_client_delegations(struct sockaddr_storage *, size_t);
+u64 nfsd_inject_recall_delegations(u64);
#else /* CONFIG_NFSD_FAULT_INJECTION */
static inline int nfsd_fault_inject_init(void) { return 0; }
static inline void nfsd_fault_inject_cleanup(void) {}
--
1.9.3


2014-06-19 14:52:00

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 043/104] 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 7ce4edcc0e61..25af8b7628ae 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -3998,6 +3998,8 @@ static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask,
*s = find_stateid_by_type(cl, stateid, typemask);
if (!*s)
return nfserr_bad_stateid;
+ /* FIXME: move into find_stateid_by_type */
+ atomic_inc(&(*s)->sc_count);
return nfs_ok;
}

@@ -4031,7 +4033,7 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,
status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID,
&s, cstate->minorversion, nn);
if (status)
- goto out;
+ goto unlock_state;
status = check_stateid_generation(stateid, &s->sc_stateid, nfsd4_has_session(cstate));
if (status)
goto out;
@@ -4078,6 +4080,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;
}
@@ -4209,11 +4213,10 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
nfsd4_cstate_assign_replay(cstate, stp->st_stateowner);

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

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

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

--
1.9.3


2014-06-23 13:39:28

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH v1 000/104] nfsd: eliminate the client_mutex

Hi Jeff,

thanks for bringing this forward. I've started looking at the series,
but I'm a little overwhelmed as it's growing bigger and bigger with
each posting. That also makes me a little worried about putting all
of it into 3.17. I know you've started separating a few bits out and
some of those actually went into 3.16, but maybe we really should
start to some easier to split piece in first and make sure they get
into 3.17 and then see if we can get through the rest of it in time.

Besides the obvious fixes for bits that were racy before and don't
just need better scalability one thing that strikes to mind are some
of the higher level logic changes, like the changes fixes to various
stateowner / stateid relations.

Besides that some higher level comments from glacing over the series:

- the patches that touch locks.c and the lockdep code should go
first, and include the maintainer (at least for lockdep, locks.c is
easy :)) and relevant mailing lists.
- lots of patches only have a subject line, but no explanation in the
body at all. While this might be enough for trivial patches few
of them are trivial enough that this would be enough.
- some patches have a signoff from Trond, but no From: line for him
in the body. I suspect this just got lost somewhere.
- there is some confusion of NFSd vs nfsd in the subsystem prefixes.
While it seems odd and against the usual naming NFSd seems to be
the common one for nfs patches.

checkpatch.pl also has various valid complains (mostly about too long
lines) and a few silly ones about printks, might be worth to address
them.

Last but not least I get a new compiler warning with the whole series
applied, as put_client is only used by the fault injection code, but
still compiled when the config option for it is not set.

I hope I'll have some more useful detailed reviews soon.


2014-06-19 14:53:24

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 102/104] nfsd: remove nfs4_lock_state: nfs4_state_shutdown_net

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 55a5c238bb94..a37e9d2aa047 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -6199,7 +6199,6 @@ nfs4_state_shutdown_net(struct net *net)
cancel_delayed_work_sync(&nn->laundromat_work);
locks_end_grace(&nn->nfsd4_manager);

- nfs4_lock_state();
INIT_LIST_HEAD(&reaplist);
spin_lock(&state_lock);
list_for_each_safe(pos, next, &nn->del_recall_lru) {
@@ -6216,7 +6215,6 @@ nfs4_state_shutdown_net(struct net *net)

nfsd4_client_tracking_exit(net);
nfs4_state_destroy_net(net);
- nfs4_unlock_state();
}

void
--
1.9.3


2014-06-19 14:52:13

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 052/104] nfsd: declare v4.1+ openowners confirmed on creation

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index a80ba729e8b3..28bfffbafb0c 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -2902,7 +2902,10 @@ static void nfs4_free_openowner(struct nfs4_stateowner *so)
}

static struct nfs4_openowner *
-alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, struct nfsd4_open *open) {
+alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp,
+ struct nfsd4_open *open,
+ struct nfsd4_compound_state *cstate)
+{
struct nfs4_openowner *oo;

oo = alloc_stateowner(openowner_slab, &open->op_owner, clp);
@@ -2913,6 +2916,8 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, str
oo->oo_owner.so_is_open_owner = 1;
oo->oo_owner.so_seqid = open->op_seqid;
oo->oo_flags = NFS4_OO_NEW;
+ if (nfsd4_has_session(cstate))
+ oo->oo_flags |= NFS4_OO_CONFIRMED;
oo->oo_time = 0;
oo->oo_last_closed_stid = NULL;
INIT_LIST_HEAD(&oo->oo_close_lru);
@@ -3185,7 +3190,7 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate,
clp = oo->oo_owner.so_client;
goto alloc_stateid;
new_owner:
- oo = alloc_init_open_stateowner(strhashval, clp, open);
+ oo = alloc_init_open_stateowner(strhashval, clp, open, cstate);
if (oo == NULL)
return nfserr_jukebox;
open->op_openowner = oo;
@@ -3654,8 +3659,6 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
memcpy(&open->op_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));

if (nfsd4_has_session(&resp->cstate)) {
- open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED;
-
if (open->op_deleg_want & NFS4_SHARE_WANT_NO_DELEG) {
open->op_delegate_type = NFS4_OPEN_DELEGATE_NONE_EXT;
open->op_why_no_deleg = WND4_NOT_WANTED;
--
1.9.3


2014-06-19 14:52:11

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 050/104] nfsd: clean up nfs4_release_lockowner

Now that we know that we won't have several lockowners with the same,
owner->data, we can simplify nfs4_release_lockowner and get rid of
the lo_list in the process.

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index cb8bdadb176a..17a1136ad0a1 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -5190,7 +5190,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
struct nfsd4_release_lockowner *rlockowner)
{
clientid_t *clid = &rlockowner->rl_clientid;
- struct nfs4_stateowner *sop;
+ struct nfs4_stateowner *sop = NULL, *tmp;
struct nfs4_lockowner *lo;
struct nfs4_ol_stateid *stp;
struct xdr_netobj *owner = &rlockowner->rl_owner;
@@ -5211,31 +5211,33 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
status = nfserr_locks_held;
INIT_LIST_HEAD(&matches);

- list_for_each_entry(sop, &nn->ownerstr_hashtbl[hashval], so_strhash) {
- if (sop->so_is_open_owner)
- continue;
- if (!same_owner_str(sop, owner, clid))
+ /* Find the matching lock stateowner */
+ list_for_each_entry(tmp, &nn->ownerstr_hashtbl[hashval], so_strhash) {
+ if (tmp->so_is_open_owner)
continue;
- list_for_each_entry(stp, &sop->so_stateids,
- st_perstateowner) {
- lo = lockowner(sop);
- if (check_for_locks(stp->st_stid.sc_file, lo))
- goto out;
- list_add(&lo->lo_list, &matches);
+ if (same_owner_str(tmp, owner, clid)) {
+ sop = tmp;
+ atomic_inc(&sop->so_count);
+ break;
}
}
- /* Clients probably won't expect us to return with some (but not all)
- * of the lockowner state released; so don't release any until all
- * have been checked. */
- status = nfs_ok;
- while (!list_empty(&matches)) {
- lo = list_entry(matches.next, struct nfs4_lockowner,
- lo_list);
- /* unhash_stateowner deletes so_perclient only
- * for openowners. */
- list_del(&lo->lo_list);
- release_lockowner(lo);
+
+ /* No matching owner found, maybe a replay? Just declare victory... */
+ if (!sop) {
+ status = nfs_ok;
+ goto out;
}
+
+ 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))
+ goto out;
+ }
+
+ status = nfs_ok;
+ release_lockowner(lo);
+ nfs4_put_stateowner(sop);
out:
nfs4_unlock_state();
return status;
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 147d0c91b819..00f2951423cc 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -370,7 +370,6 @@ struct nfs4_openowner {

struct nfs4_lockowner {
struct nfs4_stateowner lo_owner; /* must be first element */
- struct list_head lo_list; /* for temporary uses */
};

static inline struct nfs4_openowner * openowner(struct nfs4_stateowner *so)
--
1.9.3


2014-06-19 14:52:27

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 061/104] NFSd: Move the open owner hash table into struct nfs4_client

From: Trond Myklebust <[email protected]>

* Convert the open owner hash table into a per-client table
* 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 | 185 +++++++++++++++++++++++-----------------------------
fs/nfsd/state.h | 1 +
3 files changed, 83 insertions(+), 104 deletions(-)

diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h
index beaceac90ad3..f89042ae0056 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 4b668d0430b8..858778cbcc7d 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -73,7 +73,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);
static __be32 lookup_clientid(clientid_t *clid,
struct nfsd4_compound_state *cstate,
@@ -337,12 +337,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;
}

@@ -832,40 +831,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 destroy_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);
}

@@ -900,10 +896,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);
@@ -923,29 +918,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);
}
@@ -1329,15 +1322,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);
@@ -1352,6 +1350,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
@@ -1370,6 +1373,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);
@@ -2927,20 +2931,19 @@ 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)
@@ -2956,7 +2959,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);
@@ -2972,15 +2974,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;
}

@@ -3045,35 +3046,26 @@ 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;
@@ -3081,13 +3073,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;
}

@@ -3273,8 +3265,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;
@@ -4701,15 +4693,15 @@ 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);
@@ -4719,30 +4711,30 @@ 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 hash_lockowner(struct nfs4_lockowner *lo, unsigned int strhashval, struct nfs4_client *clp)
{
- struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
+ lockdep_assert_held(&clp->cl_lock);

- list_add(&lo->lo_owner.so_strhash, &nn->ownerstr_hashtbl[strhashval]);
+ list_add(&lo->lo_owner.so_strhash, &clp->cl_ownerstr_hashtbl[strhashval]);
}

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)
@@ -4763,7 +4755,6 @@ static void nfs4_free_lockowner(struct nfs4_stateowner *sop)
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 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);
@@ -4774,15 +4765,15 @@ 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;
- 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) {
hash_lockowner(lo, strhashval, clp);
ret = lo;
} else
nfs4_free_lockowner(&lo->lo_owner);
- spin_unlock(&nn->client_lock);
+ spin_unlock(&clp->cl_lock);
return lo;
}

@@ -4888,12 +4879,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;
@@ -5165,7 +5154,7 @@ 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;
@@ -5300,7 +5289,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
struct nfs4_ol_stateid *stp;
struct xdr_netobj *owner = &rlockowner->rl_owner;
struct list_head matches;
- 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;
@@ -5317,29 +5306,28 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
status = nfserr_locks_held;
INIT_LIST_HEAD(&matches);

+ 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);
@@ -5670,10 +5658,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)
@@ -5683,8 +5667,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;
@@ -5700,8 +5682,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);
@@ -5731,7 +5711,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 fd87df9f66ed..747ab44b806a 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-06-19 14:51:37

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 027/104] 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 827e749e258e..b0e22d1ea6df 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -70,6 +70,7 @@ static u64 current_sessionid = 1;

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

/* Locking: */

@@ -441,8 +442,15 @@ static void nfs4_file_put_access(struct nfs4_file *fp, u32 access, u32 deny)
__nfs4_file_put_access(fp, oflag);
}

-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;
@@ -481,7 +489,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);
+ num_delegations--;
}

static struct nfs4_delegation *
@@ -495,6 +518,8 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv
dp = delegstateid(nfs4_alloc_stid(clp, deleg_slab));
if (dp == NULL)
return dp;
+
+ dp->dl_stid.sc_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
@@ -518,30 +543,21 @@ 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;

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))
- num_delegations--;
+ nfs4_put_stid(&dp->dl_stid);
}

static void nfs4_put_deleg_lease(struct nfs4_file *fp)
@@ -753,14 +769,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_lockowner *lo,
@@ -774,7 +793,6 @@ static void __release_lock_stateid(struct nfs4_lockowner *lo,
file = find_any_file(stp->st_stid.sc_file);
if (file)
filp_close(file, (fl_owner_t)lo);
- close_generic_stateid(stp);
put_generic_stateid(stp);
}

@@ -832,7 +850,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)
@@ -4322,8 +4339,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 78ed70bd666d..f5dee9e8550a 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-06-19 14:52:54

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 081/104] nfsd: don't destroy clients that are busy

It's possible that we'll have an in-progress call on some of the clients
while we may be destroying the thing. Be sure to try and mark the client
expired first, so that the refcount is respected.

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 8f7742d3ec7b..17fd48561cf8 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -2089,8 +2089,11 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,

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

@@ -2703,6 +2706,9 @@ nfsd4_destroy_clientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *csta
status = nfserr_clientid_busy;
goto out;
}
+ status = mark_client_expired_locked(conf);
+ if (status)
+ goto out;
clp = conf;
} else if (unconf)
clp = unconf;
--
1.9.3


2014-06-24 12:11:59

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH v1 104/104] nfsd: add file documenting new state object model

> +KNFSD4 State Object Model:
> +==========================
> +Written 2014 by Jeff Layton <[email protected]>
> +
> +Introduction:
> +-------------
> +Until recently, knfsd relied heavily on a global mutex to ensure that objects
> +didn't disappear while they were being operated on. That has proven to be a
> +scalability bottleneck however, so the code has been overhauled to make heavy
> +use of reference counting and spinlocks for tracking the different objects.

It's hard enough to keep documents uptodate with the current version of
the code, so I don't think this historical blurb buys us anything.

> +struct nfs4_client:
> +-------------------
> +The initial object created by an NFS client using SETCLIENTID (for NFSv4.0) or
> +EXCHANGE_ID (for NFSv4.1+). These objects are refcounted and timestamped. Each
> +nfsd_net_ns object contains a set of these and they are tracked via short and
> +long form clientid. They are hashed and searched for under the per-nfsd-net
> +client_lock spinlock.
> +
> +The lifecycle of these is a little strange. References to it are only held
> +during the processing of compounds, and in certain other operations. In their
> +"resting state" they have a refcount of 0. If they are not renewed within a
> +lease period, they become eligible for destruction by the laundromat.

That's the normal lifecycle of objects on a lru, so I'd strike the
"strange" part.

> +strict nfs4_stid:
> +-----------------
> +A core object that represents a "generic" stateid. These are generally embedded
> +within the different (more specific) stateid objects and contain fields that
> +are of general use to any stateid.

Maybe replace "generic" stateid with common stateid or similar? The
generic_stateid term is unfortunately is used in the code in a weird way
for open/lock stateids. Or better yet fix up those names in the code..


Given how documentation outside the source code gets out of data easily
maybe you should move these texts to comments above each of the
structures?

2014-06-19 14:51:29

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 022/104] NFSd: clean up nfsd4_close_open_stateid

From: Trond Myklebust <[email protected]>

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index c01f1628c989..2af6cd2564e7 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4300,8 +4300,29 @@ out:

static void nfsd4_close_open_stateid(struct nfs4_ol_stateid *s)
{
- unhash_open_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);
+ put_generic_stateid(s);
+ } else {
+ if (s->st_file) {
+ put_nfs4_file(s->st_file);
+ s->st_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.
+ */
+ if (list_empty(&oo->oo_owner.so_stateids))
+ move_to_close_lru(oo, clp->net);
+ }
}

/*
@@ -4312,7 +4333,6 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfsd4_close *close)
{
__be32 status;
- struct nfs4_openowner *oo;
struct nfs4_ol_stateid *stp;
struct net *net = SVC_NET(rqstp);
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
@@ -4328,33 +4348,10 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
nfsd4_bump_seqid(cstate, status);
if (status)
goto out;
- oo = openowner(stp->st_stateowner);
update_stateid(&stp->st_stid.sc_stateid);
memcpy(&close->cl_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));

nfsd4_close_open_stateid(stp);
-
- if (cstate->minorversion)
- put_generic_stateid(stp);
- else {
- if (stp->st_file) {
- put_nfs4_file(stp->st_file);
- stp->st_file = NULL;
- }
- oo->oo_last_closed_stid = stp;
- }
-
- if (list_empty(&oo->oo_owner.so_stateids)) {
- if (cstate->minorversion)
- release_openowner(oo);
- else {
- /*
- * In the 4.0 case we need to keep the owners around a
- * little while to handle CLOSE replay.
- */
- move_to_close_lru(oo, SVC_NET(rqstp));
- }
- }
out:
nfs4_unlock_state();
return status;
--
1.9.3


2014-06-19 14:51:15

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 012/104] NFSd: Clean up helper __release_lock_stateid

From: Trond Myklebust <[email protected]>

Use filp_close() instead of open coding

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

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

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

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

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

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

--
1.9.3


2014-06-24 11:56:22

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH v1 013/104] NFSd: Allow lockowners to hold several stateids

Needs a good explanation on why we do this instead of having to rely
on the context in the surrounding patches.

Otherwise looks good,

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

2014-06-24 11:51:01

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH v1 011/104] NFSd: Lock owners are not per open stateid

FYI, this is what I think should go into a separate inintial series.

Looks good,

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

2014-06-19 14:52:05

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 046/104] 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 | 24 ++++--------------------
3 files changed, 30 insertions(+), 25 deletions(-)

diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index df5fcae17591..72e673781e6b 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -468,10 +468,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 c96a9b7071e1..8152ad620e20 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -2831,6 +2831,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;
@@ -3672,7 +3693,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;
@@ -3686,6 +3708,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 c9bf09bebdae..634e016f50d3 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -74,25 +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;
@@ -614,7 +595,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,
@@ -645,6 +628,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-06-24 12:06:43

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH v1 011/104] NFSd: Lock owners are not per open stateid

On Tue, Jun 24, 2014 at 08:04:26AM -0400, Jeff Layton wrote:
> Yeah, I was thinking about that last night. I think the parts that could
> be broken out are:
>
> - the change to multiple stateids per lockowner

this one definitively should as it is a significant change in behavior.

The second batch that seems easily separatable is the improvements
of the client lookup and caching, which includes a few patches.

> - moving some of the fields into nfs4_stid
> - adding refcounting for stateids and stateowners and fixing the
> relationships between them

These are indeed deeply interwinded with the locking changes. I still
think having them earlier would be useful, but if it's too much work
we'll have to live with what we get.


2014-06-19 14:51:42

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 031/104] NFSd: Convert delegation counter to an atomic_long_t type

From: Trond Myklebust <[email protected]>

We want to convert to an atomic type so that we don't need to lock
across the call to alloc_init_deleg(). Then convert to a long type
so that we match the size of 'max_delegations'.

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

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

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

/*
@@ -504,20 +504,22 @@ static struct nfs4_ol_stateid * nfs4_alloc_stateid(struct nfs4_client *clp)
static void nfs4_free_deleg(struct nfs4_stid *stid)
{
nfs4_free_stid(deleg_slab, stid);
- num_delegations--;
+ atomic_long_dec(&num_delegations);
}

static struct nfs4_delegation *
alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct svc_fh *current_fh)
{
struct nfs4_delegation *dp;
+ long n;

dprintk("NFSD alloc_init_deleg\n");
- if (num_delegations > max_delegations)
- return NULL;
+ n = atomic_long_inc_return(&num_delegations);
+ if (n < 0 || n > max_delegations)
+ goto out_dec;
dp = delegstateid(nfs4_alloc_stid(clp, deleg_slab));
if (dp == NULL)
- return dp;
+ goto out_dec;

dp->dl_stid.sc_free = nfs4_free_deleg;
/*
@@ -526,7 +528,6 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv
* 0 anyway just for consistency and use 1:
*/
dp->dl_stid.sc_stateid.si_generation = 1;
- num_delegations++;
INIT_LIST_HEAD(&dp->dl_perfile);
INIT_LIST_HEAD(&dp->dl_perclnt);
INIT_LIST_HEAD(&dp->dl_recall_lru);
@@ -534,6 +535,9 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv
fh_copy_shallow(&dp->dl_fh, &current_fh->fh_handle);
nfsd4_init_callback(&dp->dl_recall);
return dp;
+out_dec:
+ atomic_long_dec(&num_delegations);
+ return NULL;
}

static void remove_stid_locked(struct nfs4_client *clp, struct nfs4_stid *s)
--
1.9.3


2014-06-19 14:52:43

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 073/104] NFSd: Protect nfsd4_destroy_clientid using client_lock

From: Trond Myklebust <[email protected]>

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 be84ac190052..955027d4dc85 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -2647,22 +2647,23 @@ nfsd4_sequence_done(struct nfsd4_compoundres *resp)
__be32
nfsd4_destroy_clientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_destroy_clientid *dc)
{
- struct nfs4_client *conf, *unconf, *clp;
+ struct nfs4_client *conf, *unconf;
+ struct nfs4_client *clp= NULL;
__be32 status = 0;
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);

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

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

--
1.9.3


2014-06-19 14:53:00

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 085/104] nfsd: add nfsd_inject_forget_clients

...and remove nfsd_forget_client.

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

diff --git a/fs/nfsd/fault_inject.c b/fs/nfsd/fault_inject.c
index e24d7f301b71..d7c79e984d16 100644
--- a/fs/nfsd/fault_inject.c
+++ b/fs/nfsd/fault_inject.c
@@ -134,9 +134,8 @@ static struct nfsd_fault_inject_op inject_ops[] = {
{
.file = "forget_clients",
.get = nfsd_inject_print_clients,
- .set_val = nfsd_inject_set,
+ .set_val = nfsd_inject_forget_clients,
.set_clnt = nfsd_inject_forget_client,
- .forget = nfsd_forget_client,
},
{
.file = "forget_locks",
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 6da8766b87dd..526aae43c8d3 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -5592,20 +5592,6 @@ nfsd_inject_print_clients(struct nfsd_fault_inject_op *op)
return count;
}

-u64 nfsd_forget_client(struct nfs4_client *clp, u64 max)
-{
- __be32 ret;
- struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
-
- spin_lock(&nn->client_lock);
- ret = mark_client_expired_locked(clp);
- spin_unlock(&nn->client_lock);
- if (ret != nfs_ok)
- return 0;
- expire_client(clp);
- return 1;
-}
-
u64
nfsd_inject_forget_client(struct nfsd_fault_inject_op *op,
struct sockaddr_storage *addr, size_t addr_size)
@@ -5633,6 +5619,33 @@ nfsd_inject_forget_client(struct nfsd_fault_inject_op *op,
return count;
}

+u64
+nfsd_inject_forget_clients(struct nfsd_fault_inject_op *op, u64 max)
+{
+ u64 count = 0;
+ struct nfs4_client *clp, *next;
+ struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, nfsd_net_id);
+ LIST_HEAD(reaplist);
+
+ if (!nfsd_netns_ready(nn))
+ return count;
+
+ spin_lock(&nn->client_lock);
+ list_for_each_entry_safe(clp, next, &nn->client_lru, cl_lru) {
+ if (mark_client_expired_locked(clp) == nfs_ok) {
+ list_add(&clp->cl_lru, &reaplist);
+ if (max != 0 && ++count >= max)
+ break;
+ }
+ }
+ spin_unlock(&nn->client_lock);
+
+ list_for_each_entry_safe(clp, next, &reaplist, cl_lru)
+ expire_client(clp);
+
+ return count;
+}
+
static void nfsd_print_count(struct nfs4_client *clp, unsigned int count,
const char *type)
{
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index dfcb72a936b3..0c4247b2bf8c 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -475,8 +475,8 @@ u64 nfsd_for_n_state(u64, u64 (*)(struct nfs4_client *, u64));
struct nfs4_client *nfsd_find_client(struct sockaddr_storage *, size_t);

u64 nfsd_inject_print_clients(struct nfsd_fault_inject_op *op);
-u64 nfsd_forget_client(struct nfs4_client *, u64);
u64 nfsd_inject_forget_client(struct nfsd_fault_inject_op *, struct sockaddr_storage *, size_t);
+u64 nfsd_inject_forget_clients(struct nfsd_fault_inject_op *, u64);

u64 nfsd_forget_client_locks(struct nfs4_client*, u64);
u64 nfsd_forget_client_openowners(struct nfs4_client *, u64);
--
1.9.3


2014-06-19 14:52:45

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 074/104] NFSd: Ensure lookup_clientid() takes client_lock

From: Trond Myklebust <[email protected]>

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

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 955027d4dc85..6e6d34111f5f 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -3880,12 +3880,14 @@ static __be32 lookup_clientid(clientid_t *clid,
* if we don't have one cached already then we know this is for
* is for v4.0 and "sessions" will be false.
*/
+ spin_lock(&nn->client_lock);
found = find_confirmed_client(clid, false, nn);
/* Cache the nfs4_client in cstate! */
if (found) {
cstate->clp = found;
atomic_inc(&found->cl_refcount);
}
+ spin_unlock(&nn->client_lock);
}
return found ? nfs_ok : nfserr_expired;
}
--
1.9.3


2014-06-23 17:18:18

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH v1 007/104] NFSd: Add locking to the nfs4_file->fi_fds[] array

On Mon, Jun 23, 2014 at 01:14:31PM -0400, Jeff Layton wrote:
> I saw your comments and even did some experimentation to that end.
> After playing around with some patches that tried to use FMODE flags
> instead, I tossed them out. It may have consolidated the code a bit, but
> the result was just too difficult to follow. I think it's better to go
> with clearer code here, even if it's a little more verbose.

ok..


2014-06-19 14:52:24

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 059/104] NFSd: Convert nfsd4_process_open1() to work with lookup_clientid()

From: Trond Myklebust <[email protected]>

...and have alloc_init_open_stateowner just use the cstate->clp pointer
instead of passing in a clp separately.

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 428944aef83f..42c5f487a7a5 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -75,6 +75,9 @@ 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);
+static __be32 lookup_clientid(clientid_t *clid,
+ struct nfsd4_compound_state *cstate,
+ struct nfsd_net *nn);

/* Locking: */

@@ -2949,10 +2952,10 @@ static void nfs4_free_openowner(struct nfs4_stateowner *so)
}

static struct nfs4_openowner *
-alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp,
- struct nfsd4_open *open,
+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;

@@ -3269,10 +3272,10 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate,
oo = find_openstateowner_str(strhashval, open, cstate->minorversion, nn);
open->op_openowner = oo;
if (!oo) {
- clp = find_confirmed_client(clientid, cstate->minorversion,
- nn);
- if (clp == NULL)
- return nfserr_expired;
+ status = lookup_clientid(clientid, cstate, nn);
+ if (status)
+ return status;
+ clp = cstate->clp;
goto new_owner;
}
if (!(oo->oo_flags & NFS4_OO_CONFIRMED)) {
@@ -3288,7 +3291,7 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate,
clp = oo->oo_owner.so_client;
goto alloc_stateid;
new_owner:
- oo = alloc_init_open_stateowner(strhashval, clp, open, cstate);
+ oo = alloc_init_open_stateowner(strhashval, open, cstate);
if (oo == NULL)
return nfserr_jukebox;
open->op_openowner = oo;
--
1.9.3


2014-06-19 14:52:08

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 048/104] 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 e40b8e92b65d..086decc72fcc 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -817,9 +817,13 @@ static void __release_lock_stateid(struct nfs4_lockowner *lo,

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);
@@ -830,6 +834,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);
}

@@ -879,15 +884,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)
@@ -900,9 +898,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);
@@ -2878,6 +2888,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);
}

@@ -2889,6 +2900,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);
@@ -2905,6 +2923,7 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, str
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;
@@ -4621,6 +4640,11 @@ static void hash_lockowner(struct nfs4_lockowner *lo, unsigned int strhashval, s
list_add(&lo->lo_owner.so_strhash, &nn->ownerstr_hashtbl[strhashval]);
}

+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);
@@ -4648,6 +4672,7 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, str
lo->lo_owner.so_seqid = lock->lk_new_lock_seqid;
hash_lockowner(lo, strhashval, clp);
lo->lo_owner.so_free = nfs4_free_lockowner;
+ lo->lo_owner.so_unhash = nfs4_unhash_lockowner;
return lo;
}

diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index ebd3d302a572..147d0c91b819 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-06-19 14:53:16

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 097/104] NFSd: Remove nfs4_lock_state(): nfsd4_open and nfsd4_open_confirm

From: Trond Myklebust <[email protected]>

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

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

- nfs4_lock_state();
-
/* check seqid for replay. set nfs4_owner */
resp = rqstp->rq_resp;
status = nfsd4_process_open1(&resp->cstate, open, nn);
@@ -469,7 +467,6 @@ out:
}
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 e19a5961bf62..0dfe24e0aa61 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -3809,9 +3809,6 @@ static void nfsd4_deleg_xgrade_none_ext(struct nfsd4_open *open,
*/
}

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

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

--
1.9.3


2014-06-19 14:53:01

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 086/104] nfsd: add a list_head arg to nfsd_foreach_client_lock

In a later patch, we'll want to collect the locks onto a list for later
destruction.

Signed-off-by: Jeff Layton <[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 526aae43c8d3..61102b93902c 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -5654,7 +5654,9 @@ static void nfsd_print_count(struct nfs4_client *clp, unsigned int count,
printk(KERN_INFO "NFS Client: %s has %u %s\n", buf, count, type);
}

-static u64 nfsd_foreach_client_lock(struct nfs4_client *clp, u64 max, void (*func)(struct nfs4_ol_stateid *))
+static u64 nfsd_foreach_client_lock(struct nfs4_client *clp, u64 max,
+ struct list_head *collect,
+ void (*func)(struct nfs4_ol_stateid *))
{
struct nfs4_openowner *oop;
struct nfs4_ol_stateid *stp, *st_next;
@@ -5664,8 +5666,11 @@ static u64 nfsd_foreach_client_lock(struct nfs4_client *clp, u64 max, void (*fun
list_for_each_entry(oop, &clp->cl_openowners, oo_perclient) {
list_for_each_entry_safe(stp, st_next, &oop->oo_owner.so_stateids, st_perstateowner) {
list_for_each_entry_safe(lst, lst_next, &stp->st_locks, st_locks) {
- if (func)
+ if (func) {
func(lst);
+ if (collect)
+ list_add(&lst->st_locks, collect);
+ }
if (++count == max)
return count;
}
@@ -5677,12 +5682,12 @@ static u64 nfsd_foreach_client_lock(struct nfs4_client *clp, u64 max, void (*fun

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

u64 nfsd_print_client_locks(struct nfs4_client *clp, u64 max)
{
- u64 count = nfsd_foreach_client_lock(clp, max, NULL);
+ u64 count = nfsd_foreach_client_lock(clp, max, NULL, NULL);
nfsd_print_count(clp, count, "locked files");
return count;
}
--
1.9.3


2014-06-23 16:12:23

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH v1 008/104] NFSd: Ensure that nfs4_file_get_access enforces share access modes

On Thu, Jun 19, 2014 at 10:49:14AM -0400, Jeff Layton wrote:
> Lock atomicity requires us to check the share access mode when we
> actually open the file. Note that ideally this would also be atomic
> with file creation.
>
> With the change to make nfs4_file_get_access enforce the share mode, we
> now have a bogus WARN_ON that can fire. It's now normal to call
> nfs4_file_get_access before populating the fi_fds field for the open
> flag, so we should no longer warn on that situation.

Which change is that? Seems like this is a previous commit, so it
would be good to refer to it explicitly.

> The other case is a WARN_ON that can occur if there's a O_RDWR open
> already present. I'm unclear on why we'd WARN_ON in that case. This
> patch removes it, but please do enlighten me if there's some reason
> we ought to keep it instead.

I have a really hard time mapping from what's in this description
to what happens in the patch.

Looking over the patch I can see that it:

adds a fi_share_deny field to struct nfs4_file, which gets set up in
nfs4_file_get_access, and cleared from nfs4_file_put_access, as well
as some general refactoring around nfs4_file_get_access. Can you
split the refactor into a first patch that has a description what's
going on, and keep the addition of the deny mask separate, again
including a description of what it's trying to help with?

2014-06-19 14:52:31

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 064/104] 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 | 49 ++++++++++++++++++++++++++++++++-----------------
1 file changed, 32 insertions(+), 17 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index e5e1a7abee06..62af2431f118 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -819,14 +819,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);
}
@@ -840,30 +849,36 @@ 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 destroy_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 destroy_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-06-19 14:52:26

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 060/104] NFSd: Always use lookup_clientid() in nfsd4_process_open1

From: Trond Myklebust <[email protected]>

Preparation for moving the stateowner table into the nfs4_client

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 42c5f487a7a5..4b668d0430b8 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -3268,19 +3268,19 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate,
if (open->op_file == NULL)
return nfserr_jukebox;

+ status = lookup_clientid(clientid, cstate, nn);
+ if (status)
+ return status;
+ clp = cstate->clp;
+
strhashval = ownerstr_hashval(clientid->cl_id, &open->op_owner);
oo = find_openstateowner_str(strhashval, open, cstate->minorversion, nn);
open->op_openowner = oo;
if (!oo) {
- status = lookup_clientid(clientid, cstate, nn);
- if (status)
- return status;
- clp = cstate->clp;
goto new_owner;
}
if (!(oo->oo_flags & NFS4_OO_CONFIRMED)) {
/* Replace unconfirmed owners without checking for replay. */
- clp = oo->oo_owner.so_client;
release_openowner(oo);
open->op_openowner = NULL;
goto new_owner;
@@ -3288,7 +3288,6 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate,
status = nfsd4_check_seqid(cstate, &oo->oo_owner, open->op_seqid);
if (status)
return status;
- clp = oo->oo_owner.so_client;
goto alloc_stateid;
new_owner:
oo = alloc_init_open_stateowner(strhashval, open, cstate);
--
1.9.3


2014-06-19 14:51:03

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 003/104] NFSd: nfs4_preprocess_seqid_op should only set *stpp on success

From: Trond Myklebust <[email protected]>

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

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

dprintk("NFSD: %s: seqid=%d stateid = " STATEID_FMT "\n", __func__,
seqid, STATEID_VAL(stateid));
@@ -3900,11 +3901,14 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
cstate->minorversion, nn);
if (status)
return status;
- *stpp = openlockstateid(s);
+ stp = openlockstateid(s);
if (!nfsd4_has_session(cstate))
- cstate->replay_owner = (*stpp)->st_stateowner;
+ cstate->replay_owner = stp->st_stateowner;

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

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


2014-06-19 14:52:56

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 082/104] nfsd: abstract out the get and set routines into the fault injection ops

For now they all use the standard ones. In later patches we'll add new
routines that can deal with more granular locking.

Also, move some of the printk routines into the callers to make the
results of the operations more uniform.

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

diff --git a/fs/nfsd/fault_inject.c b/fs/nfsd/fault_inject.c
index f1333fc35b33..bbb00dcd926a 100644
--- a/fs/nfsd/fault_inject.c
+++ b/fs/nfsd/fault_inject.c
@@ -17,79 +17,50 @@

struct nfsd_fault_inject_op {
char *file;
+ u64 (*get)(struct nfsd_fault_inject_op *);
+ u64 (*set_val)(struct nfsd_fault_inject_op *, u64);
+ u64 (*set_clnt)(struct nfsd_fault_inject_op *,
+ struct sockaddr_storage *, size_t);
u64 (*forget)(struct nfs4_client *, u64);
u64 (*print)(struct nfs4_client *, u64);
};

-static struct nfsd_fault_inject_op inject_ops[] = {
- {
- .file = "forget_clients",
- .forget = nfsd_forget_client,
- .print = nfsd_print_client,
- },
- {
- .file = "forget_locks",
- .forget = nfsd_forget_client_locks,
- .print = nfsd_print_client_locks,
- },
- {
- .file = "forget_openowners",
- .forget = nfsd_forget_client_openowners,
- .print = nfsd_print_client_openowners,
- },
- {
- .file = "forget_delegations",
- .forget = nfsd_forget_client_delegations,
- .print = nfsd_print_client_delegations,
- },
- {
- .file = "recall_delegations",
- .forget = nfsd_recall_client_delegations,
- .print = nfsd_print_client_delegations,
- },
-};
-
-static long int NUM_INJECT_OPS = sizeof(inject_ops) / sizeof(struct nfsd_fault_inject_op);
static struct dentry *debug_dir;

-static void nfsd_inject_set(struct nfsd_fault_inject_op *op, u64 val)
+static u64 nfsd_inject_set(struct nfsd_fault_inject_op *op, u64 val)
{
- u64 count = 0;
-
- if (val == 0)
- printk(KERN_INFO "NFSD Fault Injection: %s (all)", op->file);
- else
- printk(KERN_INFO "NFSD Fault Injection: %s (n = %llu)", op->file, val);
+ u64 count;

nfs4_lock_state();
count = nfsd_for_n_state(val, op->forget);
nfs4_unlock_state();
- printk(KERN_INFO "NFSD: %s: found %llu", op->file, count);
+ return count;
}

-static void nfsd_inject_set_client(struct nfsd_fault_inject_op *op,
+static u64 nfsd_inject_set_client(struct nfsd_fault_inject_op *op,
struct sockaddr_storage *addr,
size_t addr_size)
{
- char buf[INET6_ADDRSTRLEN];
struct nfs4_client *clp;
- u64 count;
+ u64 count = 0;

nfs4_lock_state();
clp = nfsd_find_client(addr, addr_size);
- if (clp) {
+ if (clp)
count = op->forget(clp, 0);
- rpc_ntop((struct sockaddr *)&clp->cl_addr, buf, sizeof(buf));
- printk(KERN_INFO "NFSD [%s]: Client %s had %llu state object(s)\n", op->file, buf, count);
- }
nfs4_unlock_state();
+ return count;
}

-static void nfsd_inject_get(struct nfsd_fault_inject_op *op, u64 *val)
+static u64 nfsd_inject_get(struct nfsd_fault_inject_op *op)
{
+ u64 count;
+
nfs4_lock_state();
- *val = nfsd_for_n_state(0, op->print);
+ count = nfsd_for_n_state(0, op->print);
nfs4_unlock_state();
+
+ return count;
}

static ssize_t fault_inject_read(struct file *file, char __user *buf,
@@ -99,9 +70,10 @@ static ssize_t fault_inject_read(struct file *file, char __user *buf,
char read_buf[25];
size_t size;
loff_t pos = *ppos;
+ struct nfsd_fault_inject_op *op = file_inode(file)->i_private;

if (!pos)
- nfsd_inject_get(file_inode(file)->i_private, &val);
+ val = op->get(op);
size = scnprintf(read_buf, sizeof(read_buf), "%llu\n", val);

return simple_read_from_buffer(buf, len, ppos, read_buf, size);
@@ -114,6 +86,7 @@ static ssize_t fault_inject_write(struct file *file, const char __user *buf,
size_t size = min(sizeof(write_buf) - 1, len);
struct net *net = current->nsproxy->net_ns;
struct sockaddr_storage sa;
+ struct nfsd_fault_inject_op *op = file_inode(file)->i_private;
u64 val;
char *nl;

@@ -129,11 +102,19 @@ static ssize_t fault_inject_write(struct file *file, const char __user *buf,
}

size = rpc_pton(net, write_buf, size, (struct sockaddr *)&sa, sizeof(sa));
- if (size > 0)
- nfsd_inject_set_client(file_inode(file)->i_private, &sa, size);
- else {
+ if (size > 0) {
+ val = op->set_clnt(op, &sa, size);
+ if (val)
+ printk(KERN_INFO "NFSD [%s]: Client %s had %llu state object(s)\n", op->file,
+ write_buf, val);
+ } else {
val = simple_strtoll(write_buf, NULL, 0);
- nfsd_inject_set(file_inode(file)->i_private, val);
+ if (val == 0)
+ printk(KERN_INFO "NFSD Fault Injection: %s (all)", op->file);
+ else
+ printk(KERN_INFO "NFSD Fault Injection: %s (n = %llu)", op->file, val);
+ val = op->set_val(op, val);
+ printk(KERN_INFO "NFSD: %s: found %llu", op->file, val);
}
return len; /* on success, claim we got the whole input */
}
@@ -149,6 +130,51 @@ void nfsd_fault_inject_cleanup(void)
debugfs_remove_recursive(debug_dir);
}

+static struct nfsd_fault_inject_op inject_ops[] = {
+ {
+ .file = "forget_clients",
+ .get = nfsd_inject_get,
+ .set_val = nfsd_inject_set,
+ .set_clnt = nfsd_inject_set_client,
+ .forget = nfsd_forget_client,
+ .print = nfsd_print_client,
+ },
+ {
+ .file = "forget_locks",
+ .get = nfsd_inject_get,
+ .set_val = nfsd_inject_set,
+ .set_clnt = nfsd_inject_set_client,
+ .forget = nfsd_forget_client_locks,
+ .print = nfsd_print_client_locks,
+ },
+ {
+ .file = "forget_openowners",
+ .get = nfsd_inject_get,
+ .set_val = nfsd_inject_set,
+ .set_clnt = nfsd_inject_set_client,
+ .forget = nfsd_forget_client_openowners,
+ .print = nfsd_print_client_openowners,
+ },
+ {
+ .file = "forget_delegations",
+ .get = nfsd_inject_get,
+ .set_val = nfsd_inject_set,
+ .set_clnt = nfsd_inject_set_client,
+ .forget = nfsd_forget_client_delegations,
+ .print = nfsd_print_client_delegations,
+ },
+ {
+ .file = "recall_delegations",
+ .get = nfsd_inject_get,
+ .set_val = nfsd_inject_set,
+ .set_clnt = nfsd_inject_set_client,
+ .forget = nfsd_recall_client_delegations,
+ .print = nfsd_print_client_delegations,
+ },
+};
+
+#define NUM_INJECT_OPS (sizeof(inject_ops) / sizeof(struct nfsd_fault_inject_op))
+
int nfsd_fault_inject_init(void)
{
unsigned int i;
--
1.9.3


2014-06-19 14:52:06

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 047/104] 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 8152ad620e20..e40b8e92b65d 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4608,6 +4608,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;
@@ -4644,9 +4645,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;
hash_lockowner(lo, strhashval, clp);
lo->lo_owner.so_free = nfs4_free_lockowner;
return lo;
@@ -4741,8 +4740,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;
@@ -4757,19 +4761,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;
}

/*
@@ -4787,9 +4798,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);

@@ -4832,7 +4843,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,
@@ -4926,12 +4937,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_lockowner_if_empty(lock_sop);
nfsd4_bump_seqid(cstate, status);
nfs4_unlock_state();
if (file_lock)
@@ -4966,7 +4989,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);

@@ -5029,6 +5052,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-06-19 14:53:12

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 094/104] NFSd: Remove nfs4_lock_state(): nfsd4_lock/locku/lockt()

From: Trond Myklebust <[email protected]>

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 254ae3e7f8b6..ecb0623d33a6 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -5065,8 +5065,6 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
return status;
}

- nfs4_lock_state();
-
if (lock->lk_is_new) {
if (nfsd4_has_session(cstate))
/* See rfc 5661 18.10.3: given clientid is ignored: */
@@ -5204,7 +5202,6 @@ out:
if (open_stp)
put_generic_stateid(open_stp);
nfsd4_bump_seqid(cstate, status);
- nfs4_unlock_state();
if (file_lock)
locks_free_lock(file_lock);
if (conflock)
@@ -5247,8 +5244,6 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (check_lock_length(lockt->lt_offset, lockt->lt_length))
return nfserr_inval;

- nfs4_lock_state();
-
if (!nfsd4_has_session(cstate)) {
status = lookup_clientid(&lockt->lt_clientid, cstate, nn);
if (status)
@@ -5302,7 +5297,6 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
out:
if (lo)
nfs4_put_stateowner(&lo->lo_owner);
- nfs4_unlock_state();
if (file_lock)
locks_free_lock(file_lock);
return status;
@@ -5326,8 +5320,6 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (check_lock_length(locku->lu_offset, locku->lu_length))
return nfserr_inval;

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


2014-06-19 14:52:17

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 054/104] 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 | 28 +++++++++++++++++++++++++++-
1 file changed, 27 insertions(+), 1 deletion(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 46629846fd7d..e7f7e89f2b8c 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -85,6 +85,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;
@@ -552,8 +558,10 @@ static void nfs4_put_stid(struct nfs4_stid *s)
{
struct nfs4_client *clp = s->sc_client;

- 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);
@@ -2963,11 +2971,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);
@@ -4447,6 +4467,12 @@ static void nfsd4_close_open_stateid(struct nfs4_ol_stateid *s)
s->st_stid.sc_type = NFS4_CLOSED_STID;
unhash_open_stateid(s);

+ /*
+ * We can't safely clear the sc_file pointer while there are still
+ * other calls in flight that might be using it. At this point, the
+ * stateid is unhashed, so no new references for anything but another
+ * CLOSE can be acquired. Wait for the refcount to drop to '2'
+ */
if (clp->cl_minorversion)
put_generic_stateid(s);
else
--
1.9.3


2014-06-19 14:51:18

by Jeff Layton

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

From: Trond Myklebust <[email protected]>

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

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

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

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

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

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

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

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

-static unsigned int lockowner_ino_hashval(struct inode *inode, u32 cl_id, struct xdr_netobj *ownername)
-{
- return (file_hashval(inode) + cl_id
- + opaque_hashval(ownername->data, ownername->len))
- & LOCKOWNER_INO_HASH_MASK;
-}
-
/*
* TODO: Linux file offsets are _signed_ 64-bit quantities, which means that
* we can't properly handle lock requests that go beyond the (2^63 - 1)-th
@@ -4417,44 +4407,28 @@ nevermind:
deny->ld_type = NFS4_WRITE_LT;
}

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

- list_for_each_entry(lo, &nn->lockowner_ino_hashtbl[hashval], lo_owner_ino_hash) {
- if (same_lockowner_ino(lo, inode, clid, owner))
- return lo;
+ list_for_each_entry(so, &nn->ownerstr_hashtbl[strhashval], so_strhash) {
+ if (so->so_is_open_owner)
+ continue;
+ if (!same_owner_str(so, owner, clid))
+ continue;
+ return lockowner(so);
}
return NULL;
}

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

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

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

@@ -4544,8 +4518,7 @@ static __be32 lookup_or_create_lock_state(struct nfsd4_compound_state *cstate, s
unsigned int strhashval;
struct nfsd_net *nn = net_generic(cl->net, nfsd_net_id);

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

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

- lo = find_lockowner_str(inode, &lockt->lt_clientid, &lockt->lt_owner, nn);
+ lo = find_lockowner_str(&lockt->lt_clientid, &lockt->lt_owner, nn);
if (lo)
file_lock->fl_owner = (fl_owner_t)lo;
file_lock->fl_pid = current->tgid;
@@ -5297,10 +5268,6 @@ static int nfs4_state_create_net(struct net *net)
OWNER_HASH_SIZE, GFP_KERNEL);
if (!nn->ownerstr_hashtbl)
goto err_ownerstr;
- nn->lockowner_ino_hashtbl = kmalloc(sizeof(struct list_head) *
- LOCKOWNER_INO_HASH_SIZE, GFP_KERNEL);
- if (!nn->lockowner_ino_hashtbl)
- goto err_lockowner_ino;
nn->sessionid_hashtbl = kmalloc(sizeof(struct list_head) *
SESSION_HASH_SIZE, GFP_KERNEL);
if (!nn->sessionid_hashtbl)
@@ -5312,8 +5279,6 @@ static int nfs4_state_create_net(struct net *net)
}
for (i = 0; i < OWNER_HASH_SIZE; i++)
INIT_LIST_HEAD(&nn->ownerstr_hashtbl[i]);
- for (i = 0; i < LOCKOWNER_INO_HASH_SIZE; i++)
- INIT_LIST_HEAD(&nn->lockowner_ino_hashtbl[i]);
for (i = 0; i < SESSION_HASH_SIZE; i++)
INIT_LIST_HEAD(&nn->sessionid_hashtbl[i]);
nn->conf_name_tree = RB_ROOT;
@@ -5329,8 +5294,6 @@ static int nfs4_state_create_net(struct net *net)
return 0;

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

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

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

--
1.9.3


2014-06-23 14:11:18

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH v1 000/104] nfsd: eliminate the client_mutex

On Mon, Jun 23, 2014 at 09:56:45AM -0400, Jeff Layton wrote:
> On Mon, 23 Jun 2014 06:39:26 -0700
> Christoph Hellwig <[email protected]> wrote:
>
> > Hi Jeff,
> >
> > thanks for bringing this forward. I've started looking at the series,
> > but I'm a little overwhelmed as it's growing bigger and bigger with
> > each posting. That also makes me a little worried about putting all
> > of it into 3.17. I know you've started separating a few bits out and
> > some of those actually went into 3.16, but maybe we really should
> > start to some easier to split piece in first and make sure they get
> > into 3.17 and then see if we can get through the rest of it in time.
> >
>
> Thanks for taking a look. The big problem with breaking this set up is
> that it will likely result in at least some performance regression in
> the interim. We're adding more granular locking inside of the
> coarse-grained client_mutex, which is likely to mean at least some
> slowdown until the client_mutex is removed. Maybe that's worth it
> though.

I also don't know if the in-between state (when we have both new and old
locking) is very interesting to test.

>
> > Besides the obvious fixes for bits that were racy before and don't
> > just need better scalability one thing that strikes to mind are some
> > of the higher level logic changes, like the changes fixes to various
> > stateowner / stateid relations.
> >
>
> I'll have to think about how to break those out. Unfortunately, there
> are strong dependencies between some of those changes, which makes it
> hard to do this in pieces.

But, yes, if there's anything here it would be possible to take first
that would help....

--b.

2014-06-19 14:51:06

by Jeff Layton

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

From: Trond Myklebust <[email protected]>

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 8dc1289a7b74..d1b975b29334 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -625,7 +625,11 @@ release_all_access(struct nfs4_ol_stateid *stp)

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

@@ -2672,7 +2676,6 @@ static void init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp,
stp->st_stid.sc_type = NFS4_OPEN_STID;
INIT_LIST_HEAD(&stp->st_lockowners);
list_add(&stp->st_perstateowner, &oo->oo_owner.so_stateids);
- list_add(&stp->st_perfile, &fp->fi_stateids);
stp->st_stateowner = &oo->oo_owner;
get_nfs4_file(fp);
stp->st_file = fp;
@@ -2681,6 +2684,9 @@ static void init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp,
set_access(open->op_share_access, stp);
set_deny(open->op_share_deny, stp);
stp->st_openstp = NULL;
+ spin_lock(&fp->fi_lock);
+ list_add(&stp->st_perfile, &fp->fi_stateids);
+ spin_unlock(&fp->fi_lock);
}

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

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

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

--
1.9.3


2014-06-19 14:51:39

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 029/104] NFSd: Add locking to protect the state owner lists

We use the clp->cl_lock for this. For now, there's a lot of cl_lock
thrashing, but in a later patch we'll reduce that.

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 99995d2537c3..6e8d3ec088d9 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -763,6 +763,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);
@@ -797,9 +799,13 @@ static void put_generic_stateid(struct nfs4_ol_stateid *stp)
static void __release_lock_stateid(struct nfs4_lockowner *lo,
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);
}

@@ -843,20 +849,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)
@@ -2862,7 +2874,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;
@@ -2871,9 +2882,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
@@ -4544,6 +4558,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;

@@ -4551,7 +4566,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;
@@ -4559,10 +4573,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-06-19 14:52:36

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 068/104] NFSd: Ensure that the laundromat unhashes the client before releasing locks

From: Trond Myklebust <[email protected]>

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

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 9d6cc32fd2fb..7982f44aae83 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -3949,13 +3949,15 @@ nfs4_laundromat(struct nfsd_net *nn)
clp->cl_clientid.cl_id);
continue;
}
- list_move(&clp->cl_lru, &reaplist);
+ unhash_client_locked(clp);
+ list_add(&clp->cl_lru, &reaplist);
}
spin_unlock(&nn->client_lock);
list_for_each_safe(pos, next, &reaplist) {
clp = list_entry(pos, struct nfs4_client, cl_lru);
dprintk("NFSD: purging unused client (clientid %08x)\n",
clp->cl_clientid.cl_id);
+ list_del_init(&clp->cl_lru);
expire_client(clp);
}
spin_lock(&state_lock);
--
1.9.3


2014-06-24 12:00:33

by Jeff Layton

[permalink] [raw]
Subject: Re: [PATCH v1 012/104] NFSd: Clean up helper __release_lock_stateid

On Tue, 24 Jun 2014 04:53:36 -0700
Christoph Hellwig <[email protected]> wrote:

> On Thu, Jun 19, 2014 at 10:49:18AM -0400, Jeff Layton wrote:
> > From: Trond Myklebust <[email protected]>
> >
> > Use filp_close() instead of open coding
>
> filp_close does more than what's currently opencoded here:
>
> - file_count debug check which seems pointless but harmless
> - calling ->flush. This doesn't do much on nfs exportable filesystem
> except for exofs, which does dumb shit in it that should be removed,
> but the maintainer refuse.
> - call into dnotify, which we probably should be doing here.
>

Yeah. It is a small change in behavior but seems like the right thing
to do. I'll flesh out the commit message to spell that out though.

> > -static void __release_lock_stateid(struct nfs4_ol_stateid *stp)
> > +static void __release_lock_stateid(struct nfs4_lockowner *lo,
> > + struct nfs4_ol_stateid *stp)
>
> But what's the point of explicitly passing the lock owner instead of
> deriving it?
>

I thought that was a little silly too, but didn't care enough to change
it. I can only assume that Trond figured that the callers all needed to
know what the lockowner was so you wouldn't need to do two lockowner()
calls. Still, those are just container_of(), so I don't see the point
in avoiding them either. I'll change it not to pass the owner.

Thanks,
--
Jeff Layton <[email protected]>

2014-06-24 12:02:04

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH v1 016/104] NFSd: Don't get a session reference without a client reference

Another first series candidate.

Looks good,

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

2014-06-19 14:52:52

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 080/104] nfsd: don't destroy client if mark_client_expired_locked fails

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index bff26c3d306f..8f7742d3ec7b 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -2322,8 +2322,10 @@ nfsd4_create_session(struct svc_rqst *rqstp,
old = find_confirmed_client_by_name(&unconf->cl_name, nn);
if (old) {
status = mark_client_expired_locked(old);
- if (status)
+ if (status) {
+ old = NULL;
goto out_free_conn;
+ }
}
move_to_confirmed(unconf);
conf = unconf;
@@ -2867,8 +2869,10 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
old = find_confirmed_client_by_name(&unconf->cl_name, nn);
if (old) {
status = mark_client_expired_locked(old);
- if (status)
+ if (status) {
+ old = NULL;
goto out;
+ }
}
move_to_confirmed(unconf);
conf = unconf;
--
1.9.3


2014-06-19 14:51:38

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 028/104] nfsd: do filp_close in sc_free callback for lock stateids

It seems wrong to release the locks on each put of the stateid, instead
of doing so only when the stateid is actually released, and doing so
complicates some later changes.

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 b0e22d1ea6df..99995d2537c3 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -777,6 +777,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);
@@ -785,14 +797,9 @@ static void put_generic_stateid(struct nfs4_ol_stateid *stp)
static void __release_lock_stateid(struct nfs4_lockowner *lo,
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)lo);
put_generic_stateid(stp);
}

@@ -4548,6 +4555,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-06-19 14:52:09

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 049/104] NFSd: Make lock stateid take a reference to the lockowner

From: Trond Myklebust <[email protected]>

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 086decc72fcc..cb8bdadb176a 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -782,6 +782,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);
}

@@ -4687,6 +4689,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-06-19 14:52:02

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 044/104] 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.

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 25af8b7628ae..4ddd7ec31d6c 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1590,8 +1590,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;
}
@@ -3181,8 +3185,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);
}

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

--
1.9.3


2014-06-23 16:19:53

by Jeff Layton

[permalink] [raw]
Subject: Re: [PATCH v1 004/104] nfsd4: use cl_lock to synchronize all stateid idr calls

On Mon, 23 Jun 2014 09:02:27 -0700
Christoph Hellwig <[email protected]> wrote:

> This looks reasonable to me. Am I right to assume this is just a
> preparation for the state lock removal? As far as I can tell currently
> all calls are under nfs4_lock_state().
>
> Reviewed-by: Christoph Hellwig <[email protected]>
>
> assuming it gets a proper patch description.
>

Yes, that's correct, and that's the case with pretty much all of the
patches that add locking here. I can explicitly spell that out in every
patch description but that may get tedious over >100 patches.

Also, let's be careful about terminology here...

What we're removing is the client_mutex (which has unfortunately named
wrappers around it called nfs4_lock_state and nfs4_unlock_state).

We have yet another spinlock called the "state_lock" which protects the
nfs4_file hashes and such, as well as some of the delegation code.

--
Jeff Layton <[email protected]>

2014-06-19 14:51:14

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 011/104] NFSd: Lock owners are not per open stateid

From: Trond Myklebust <[email protected]>

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


2014-06-23 16:05:50

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH v1 006/104] NFSd: Add a mutex to protect the NFSv4.0 open owner replay cache

On Thu, Jun 19, 2014 at 10:49:12AM -0400, Jeff Layton wrote:
> From: Trond Myklebust <[email protected]>
>
> We don't want to rely on the state_lock() for protection in the
> case of NFSv4 open owners. Instead, we add a mutex that will
> only be taken for NFSv4.0 state mutating operations, and
> that will be released once the entire compound is done.
>
> Signed-off-by: Trond Myklebust <[email protected]>

Looks reasonable to me, but doesn't this create a lock order reversal
with the client_lock until it is removed?


2014-06-23 16:40:07

by Jeff Layton

[permalink] [raw]
Subject: Re: [PATCH v1 008/104] NFSd: Ensure that nfs4_file_get_access enforces share access modes

On Mon, 23 Jun 2014 09:12:22 -0700
Christoph Hellwig <[email protected]> wrote:

> On Thu, Jun 19, 2014 at 10:49:14AM -0400, Jeff Layton wrote:
> > Lock atomicity requires us to check the share access mode when we
> > actually open the file. Note that ideally this would also be atomic
> > with file creation.
> >
> > With the change to make nfs4_file_get_access enforce the share mode, we
> > now have a bogus WARN_ON that can fire. It's now normal to call
> > nfs4_file_get_access before populating the fi_fds field for the open
> > flag, so we should no longer warn on that situation.
>
> Which change is that? Seems like this is a previous commit, so it
> would be good to refer to it explicitly.
>

Sorry it wasn't clear. The change is in this patch. This is just an
explanation of why the WARN_ON needed to be removed.

> > The other case is a WARN_ON that can occur if there's a O_RDWR open
> > already present. I'm unclear on why we'd WARN_ON in that case. This
> > patch removes it, but please do enlighten me if there's some reason
> > we ought to keep it instead.
>
> I have a really hard time mapping from what's in this description
> to what happens in the patch.
>
> Looking over the patch I can see that it:
>
> adds a fi_share_deny field to struct nfs4_file, which gets set up in
> nfs4_file_get_access, and cleared from nfs4_file_put_access, as well
> as some general refactoring around nfs4_file_get_access. Can you
> split the refactor into a first patch that has a description what's
> going on, and keep the addition of the deny mask separate, again
> including a description of what it's trying to help with?

Ok, I'll see if I can break that up.

--
Jeff Layton <[email protected]>

2014-06-23 16:20:42

by Jeff Layton

[permalink] [raw]
Subject: Re: [PATCH v1 005/104] NFSd: Add fine grained protection for the nfs4_file->fi_stateids list

On Mon, 23 Jun 2014 09:04:07 -0700
Christoph Hellwig <[email protected]> wrote:

> On Thu, Jun 19, 2014 at 10:49:11AM -0400, Jeff Layton wrote:
> > From: Trond Myklebust <[email protected]>
> >
> > Signed-off-by: Trond Myklebust <[email protected]>
>
> Looks good, but should have a patch description that better tells
> why this is done (looks like preparing for nfs_lock_state() removal
> again, not fixing some existing race)
>
> Reviewed-by: Christoph Hellwig <[email protected]>

Correct. I'll add that into the desciption if you think it's warranted,
but again that's the case with many of the patches in this series.

--
Jeff Layton <[email protected]>

2014-06-19 14:52:39

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 070/104] NFSd: Move create_client() call outside the lock

From: Trond Myklebust <[email protected]>

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

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 2b3897f313b8..ed568b10c35e 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -2002,6 +2002,10 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
return nfserr_encr_alg_unsupp;
}

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

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

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

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

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

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

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

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

--
1.9.3


2014-06-19 14:52:48

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 076/104] nfsd: protect the close_lru list and oo_last_closed_stid with client_lock

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 72ca1b62cf16..561c77a02920 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -917,13 +917,19 @@ static void unhash_openowner_locked(struct nfs4_openowner *oo)

static void release_last_closed_stateid(struct nfs4_openowner *oo)
{
- struct nfs4_ol_stateid *s = oo->oo_last_closed_stid;
+ struct nfsd_net *nn = net_generic(oo->oo_owner.so_client->net,
+ nfsd_net_id);
+ struct nfs4_ol_stateid *s;

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

static void release_openowner(struct nfs4_openowner *oo)
@@ -3098,6 +3104,7 @@ static void init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp,
static void
move_to_close_lru(struct nfs4_ol_stateid *s, struct net *net)
{
+ struct nfs4_ol_stateid *last;
struct nfs4_openowner *oo = openowner(s->st_stateowner);
struct nfsd_net *nn = net_generic(s->st_stid.sc_client->net,
nfsd_net_id);
@@ -3121,10 +3128,14 @@ move_to_close_lru(struct nfs4_ol_stateid *s, struct net *net)
s->st_stid.sc_file = NULL;
}

- release_last_closed_stateid(oo);
+ spin_lock(&nn->client_lock);
+ last = oo->oo_last_closed_stid;
oo->oo_last_closed_stid = s;
list_move_tail(&oo->oo_close_lru, &nn->close_lru);
oo->oo_time = get_seconds();
+ spin_unlock(&nn->client_lock);
+ if (last)
+ put_generic_stateid(last);
}

static int
@@ -3970,6 +3981,7 @@ nfs4_laundromat(struct nfsd_net *nn)
struct nfs4_client *clp;
struct nfs4_openowner *oo;
struct nfs4_delegation *dp;
+ struct nfs4_ol_stateid *stp;
struct list_head *pos, *next, reaplist;
time_t cutoff = get_seconds() - nn->nfsd4_lease;
time_t t, new_timeo = nn->nfsd4_lease;
@@ -4021,15 +4033,24 @@ nfs4_laundromat(struct nfsd_net *nn)
dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);
revoke_delegation(dp);
}
- list_for_each_safe(pos, next, &nn->close_lru) {
- oo = container_of(pos, struct nfs4_openowner, oo_close_lru);
+
+ spin_lock(&nn->client_lock);
+ while (!list_empty(&nn->close_lru)) {
+ oo = list_first_entry(&nn->close_lru, struct nfs4_openowner, oo_close_lru);
if (time_after((unsigned long)oo->oo_time, (unsigned long)cutoff)) {
t = oo->oo_time - cutoff;
new_timeo = min(new_timeo, t);
break;
}
- release_last_closed_stateid(oo);
+ list_del_init(&oo->oo_close_lru);
+ stp = oo->oo_last_closed_stid;
+ oo->oo_last_closed_stid = NULL;
+ spin_unlock(&nn->client_lock);
+ put_generic_stateid(stp);
+ spin_lock(&nn->client_lock);
}
+ spin_unlock(&nn->client_lock);
+
new_timeo = max_t(time_t, new_timeo, NFSD_LAUNDROMAT_MINTIMEOUT);
nfs4_unlock_state();
return new_timeo;
--
1.9.3


2014-06-19 14:51:52

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 038/104] 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.

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

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

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

spin_lock(&fp->fi_lock);
list_for_each_entry(local, &fp->fi_stateids, st_perfile) {
@@ -3231,13 +3233,17 @@ nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_ol_st
continue;
/* remember if we have seen this open owner */
if (local->st_stateowner == &oo->oo_owner)
- *stpp = local;
+ ret = local;
/* check for conflicting share reservations */
if (!test_share(local, open)) {
spin_unlock(&fp->fi_lock);
return nfserr_share_denied;
}
}
+ if (ret) {
+ atomic_inc(&ret->st_stid.sc_count);
+ *stpp = ret;
+ }
spin_unlock(&fp->fi_lock);
return nfs_ok;
}
@@ -3652,6 +3658,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-06-19 14:51:35

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 026/104] NFSd: Ensure atomicity of stateid destruction and idr tree removal

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 6c04b5bb10f7..827e749e258e 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -511,30 +511,37 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv
return dp;
}

-static void remove_stid(struct nfs4_stid *s)
+static void remove_stid_locked(struct nfs4_client *clp, struct nfs4_stid *s)
{
- struct nfs4_client *clp = s->sc_client;
+ 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)
{
- remove_stid(s);
if (s->sc_file)
put_nfs4_file(s->sc_file);
kmem_cache_free(slab, s);
}

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

static void nfs4_put_deleg_lease(struct nfs4_file *fp)
@@ -753,9 +760,7 @@ static void close_generic_stateid(struct nfs4_ol_stateid *stp)

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

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


2014-06-19 14:52:21

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 057/104] NFSd: Cleanup - Let nfsd4_lookup_stateid() take a cstate argument

From: Trond Myklebust <[email protected]>

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

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index a8969884f65e..7034db2d00b3 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4106,12 +4106,14 @@ out_put_stid:
return status;
}

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

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

nfs4_lock_state();

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

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

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


2014-06-19 14:53:05

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 089/104] nfsd: add more granular locking to *_delegations fault injectors

Signed-off-by: Jeff Layton <[email protected]>
---
fs/nfsd/fault_inject.c | 16 ++---
fs/nfsd/nfs4state.c | 156 ++++++++++++++++++++++++++++++++++++++++++-------
fs/nfsd/state.h | 9 +--
3 files changed, 145 insertions(+), 36 deletions(-)

diff --git a/fs/nfsd/fault_inject.c b/fs/nfsd/fault_inject.c
index 0c2ab9caf803..aa114c893189 100644
--- a/fs/nfsd/fault_inject.c
+++ b/fs/nfsd/fault_inject.c
@@ -151,19 +151,15 @@ static struct nfsd_fault_inject_op inject_ops[] = {
},
{
.file = "forget_delegations",
- .get = nfsd_inject_get,
- .set_val = nfsd_inject_set,
- .set_clnt = nfsd_inject_set_client,
- .forget = nfsd_forget_client_delegations,
- .print = nfsd_print_client_delegations,
+ .get = nfsd_inject_print_delegations,
+ .set_val = nfsd_inject_forget_delegations,
+ .set_clnt = nfsd_inject_forget_client_delegations,
},
{
.file = "recall_delegations",
- .get = nfsd_inject_get,
- .set_val = nfsd_inject_set,
- .set_clnt = nfsd_inject_set_client,
- .forget = nfsd_recall_client_delegations,
- .print = nfsd_print_client_delegations,
+ .get = nfsd_inject_print_delegations,
+ .set_val = nfsd_inject_recall_delegations,
+ .set_clnt = nfsd_inject_recall_client_delegations,
},
};

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 2f18de7dc913..3cae26492cf5 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -5923,60 +5923,172 @@ static u64 nfsd_find_all_delegations(struct nfs4_client *clp, u64 max,
struct list_head *victims)
{
struct nfs4_delegation *dp, *next;
+ struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, nfsd_net_id);
u64 count = 0;

- lockdep_assert_held(&state_lock);
+ lockdep_assert_held(&nn->client_lock);
+
+ spin_lock(&state_lock);
list_for_each_entry_safe(dp, next, &clp->cl_delegations, dl_perclnt) {
if (victims) {
+ atomic_inc(&clp->cl_refcount);
unhash_delegation_locked(dp);
list_add(&dp->dl_recall_lru, victims);
}
- if (++count == max)
+ ++count;
+ /*
+ * Despite the fact that these functions deal with
+ * 64-bit integers for "count", we must ensure that
+ * it doesn't blow up the clp->cl_refcount. Throw a
+ * warning if we start to approach INT_MAX here.
+ */
+ WARN_ON_ONCE(count == (INT_MAX / 2));
+ if (count == max)
break;
}
+ spin_unlock(&state_lock);
return count;
}

-u64 nfsd_forget_client_delegations(struct nfs4_client *clp, u64 max)
+static u64
+nfsd_print_client_delegations(struct nfs4_client *clp)
{
- struct nfs4_delegation *dp, *next;
- LIST_HEAD(victims);
- u64 count;
+ u64 count = nfsd_find_all_delegations(clp, 0, NULL);
+ nfsd_print_count(clp, count, "delegations");
+ return count;
+}

- spin_lock(&state_lock);
- count = nfsd_find_all_delegations(clp, max, &victims);
- spin_unlock(&state_lock);
+u64
+nfsd_inject_print_delegations(struct nfsd_fault_inject_op *op)
+{
+ struct nfs4_client *clp;
+ u64 count = 0;
+ struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, nfsd_net_id);
+
+ if (!nfsd_netns_ready(nn))
+ return 0;

- list_for_each_entry_safe(dp, next, &victims, dl_recall_lru)
+ spin_lock(&nn->client_lock);
+ list_for_each_entry(clp, &nn->client_lru, cl_lru)
+ count += nfsd_print_client_delegations(clp);
+ spin_unlock(&nn->client_lock);
+
+ return count;
+}
+
+static void
+nfsd_forget_delegations(struct list_head *reaplist)
+{
+ struct nfs4_client *clp;
+ struct nfs4_delegation *dp, *next;
+
+ list_for_each_entry_safe(dp, next, reaplist, dl_recall_lru) {
+ list_del_init(&dp->dl_recall_lru);
+ clp = dp->dl_stid.sc_client;
revoke_delegation(dp);
+ put_client(clp);
+ }
+}
+
+u64
+nfsd_inject_forget_client_delegations(struct nfsd_fault_inject_op *op,
+ struct sockaddr_storage *addr, size_t addr_size)
+{
+ u64 count = 0;
+ struct nfs4_client *clp;
+ struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, nfsd_net_id);
+ LIST_HEAD(reaplist);
+
+ if (!nfsd_netns_ready(nn))
+ return count;

+ spin_lock(&nn->client_lock);
+ clp = nfsd_find_client(addr, addr_size);
+ if (clp)
+ count = nfsd_find_all_delegations(clp, 0, &reaplist);
+ spin_unlock(&nn->client_lock);
+
+ nfsd_forget_delegations(&reaplist);
return count;
}

-u64 nfsd_recall_client_delegations(struct nfs4_client *clp, u64 max)
+u64
+nfsd_inject_forget_delegations(struct nfsd_fault_inject_op *op, u64 max)
{
+ u64 count = 0;
+ struct nfs4_client *clp;
+ struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, nfsd_net_id);
+ LIST_HEAD(reaplist);
+
+ if (!nfsd_netns_ready(nn))
+ return count;
+
+ spin_lock(&nn->client_lock);
+ list_for_each_entry(clp, &nn->client_lru, cl_lru) {
+ count += nfsd_find_all_delegations(clp, max - count, &reaplist);
+ if (max != 0 && count >= max)
+ break;
+ }
+ spin_unlock(&nn->client_lock);
+ nfsd_forget_delegations(&reaplist);
+ return count;
+}
+
+static void
+nfsd_recall_delegations(struct list_head *reaplist)
+{
+ struct nfs4_client *clp;
struct nfs4_delegation *dp, *next;
- LIST_HEAD(victims);
- u64 count;

- spin_lock(&state_lock);
- count = nfsd_find_all_delegations(clp, max, &victims);
- list_for_each_entry_safe(dp, next, &victims, dl_recall_lru)
+ list_for_each_entry_safe(dp, next, reaplist, dl_recall_lru) {
+ list_del_init(&dp->dl_recall_lru);
+ clp = dp->dl_stid.sc_client;
nfsd_break_one_deleg(dp);
- spin_unlock(&state_lock);
+ put_client(clp);
+ }
+}

+u64
+nfsd_inject_recall_client_delegations(struct nfsd_fault_inject_op *op,
+ struct sockaddr_storage *addr, size_t addr_size)
+{
+ u64 count = 0;
+ struct nfs4_client *clp;
+ struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, nfsd_net_id);
+ LIST_HEAD(reaplist);
+
+ if (!nfsd_netns_ready(nn))
+ return count;
+
+ spin_lock(&nn->client_lock);
+ clp = nfsd_find_client(addr, addr_size);
+ if (clp)
+ count = nfsd_find_all_delegations(clp, 0, &reaplist);
+ spin_unlock(&nn->client_lock);
+
+ nfsd_recall_delegations(&reaplist);
return count;
}

-u64 nfsd_print_client_delegations(struct nfs4_client *clp, u64 max)
+u64
+nfsd_inject_recall_delegations(struct nfsd_fault_inject_op *op, u64 max)
{
u64 count = 0;
+ struct nfs4_client *clp, *next;
+ struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, nfsd_net_id);
+ LIST_HEAD(reaplist);

- spin_lock(&state_lock);
- count = nfsd_find_all_delegations(clp, max, NULL);
- spin_unlock(&state_lock);
+ if (!nfsd_netns_ready(nn))
+ return count;

- nfsd_print_count(clp, count, "delegations");
+ spin_lock(&nn->client_lock);
+ list_for_each_entry_safe(clp, next, &nn->client_lru, cl_lru) {
+ count += nfsd_find_all_delegations(clp, max - count, &reaplist);
+ if (max != 0 && ++count >= max)
+ break;
+ }
+ spin_unlock(&nn->client_lock);
+ nfsd_recall_delegations(&reaplist);
return count;
}

diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 597841e691bb..4f6b65189031 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -486,10 +486,11 @@ u64 nfsd_inject_print_openowners(struct nfsd_fault_inject_op *);
u64 nfsd_inject_forget_client_openowners(struct nfsd_fault_inject_op *, struct sockaddr_storage *, size_t);
u64 nfsd_inject_forget_openowners(struct nfsd_fault_inject_op *, u64);

-u64 nfsd_forget_client_delegations(struct nfs4_client *, u64);
-u64 nfsd_recall_client_delegations(struct nfs4_client *, u64);
-
-u64 nfsd_print_client_delegations(struct nfs4_client *, u64);
+u64 nfsd_inject_print_delegations(struct nfsd_fault_inject_op *);
+u64 nfsd_inject_forget_client_delegations(struct nfsd_fault_inject_op *, struct sockaddr_storage *, size_t);
+u64 nfsd_inject_forget_delegations(struct nfsd_fault_inject_op *, u64);
+u64 nfsd_inject_recall_client_delegations(struct nfsd_fault_inject_op *, struct sockaddr_storage *, size_t);
+u64 nfsd_inject_recall_delegations(struct nfsd_fault_inject_op *, u64);
#else /* CONFIG_NFSD_FAULT_INJECTION */
static inline int nfsd_fault_inject_init(void) { return 0; }
static inline void nfsd_fault_inject_cleanup(void) {}
--
1.9.3


2014-06-19 14:51:13

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 010/104] NFSd: Protect the nfs4_file delegation fields using the fi_lock

The current code in nfs4_setlease calls vfs_setlease and uses the
client_mutex to ensure that it doesn't disappear before we can hash the
delegation. With the client_mutex gone, we'll have a potential race
condition.

It's possible that the delegation could be recalled after we acquire the
lease but before we ever get around to setting it up. If that happens,
then we'd have a nfs4_file that *thinks* it has a delegation, when it
actually has none.

Attempt to acquire a delegation. If that succeeds, take the state_lock
and recheck to make sure the lease is still there. If it is, then take
the fi_lock and set up the rest of the delegation fields.

Signed-off-by: Trond Myklebust <[email protected]>
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 17870de5989d..cf940210cc58 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -524,6 +524,8 @@ nfs4_put_delegation(struct nfs4_delegation *dp)

static void nfs4_put_deleg_lease(struct nfs4_file *fp)
{
+ lockdep_assert_held(&state_lock);
+
if (!fp->fi_lease)
return;
if (atomic_dec_and_test(&fp->fi_delegees)) {
@@ -543,11 +545,10 @@ static void
hash_delegation_locked(struct nfs4_delegation *dp, struct nfs4_file *fp)
{
lockdep_assert_held(&state_lock);
+ lockdep_assert_held(&fp->fi_lock);

dp->dl_stid.sc_type = NFS4_DELEG_STID;
- spin_lock(&fp->fi_lock);
list_add(&dp->dl_perfile, &fp->fi_delegations);
- spin_unlock(&fp->fi_lock);
list_add(&dp->dl_perclnt, &dp->dl_stid.sc_client->cl_delegations);
}

@@ -558,17 +559,17 @@ unhash_delegation(struct nfs4_delegation *dp)
struct nfs4_file *fp = dp->dl_file;

spin_lock(&state_lock);
+ spin_lock(&fp->fi_lock);
list_del_init(&dp->dl_perclnt);
list_del_init(&dp->dl_recall_lru);
- spin_lock(&fp->fi_lock);
list_del_init(&dp->dl_perfile);
spin_unlock(&fp->fi_lock);
- spin_unlock(&state_lock);
if (fp) {
nfs4_put_deleg_lease(fp);
- put_nfs4_file(fp);
dp->dl_file = NULL;
}
+ spin_unlock(&state_lock);
+ put_nfs4_file(fp);
}


@@ -3245,7 +3246,7 @@ static int nfs4_setlease(struct nfs4_delegation *dp)
{
struct nfs4_file *fp = dp->dl_file;
struct file_lock *fl;
- int status;
+ int status = 0;

fl = nfs4_alloc_init_lease(dp, NFS4_OPEN_DELEGATE_READ);
if (!fl)
@@ -3253,15 +3254,31 @@ static int nfs4_setlease(struct nfs4_delegation *dp)
fl->fl_file = find_readable_file(fp);
status = vfs_setlease(fl->fl_file, fl->fl_type, &fl);
if (status)
- goto out_free;
+ goto out_fput;
+ spin_lock(&state_lock);
+ /* Did the lease get broken before we took the lock? */
+ status = -EAGAIN;
+ if (!file_has_lease(fl->fl_file))
+ goto out_unlock;
+ spin_lock(&fp->fi_lock);
+ /* Race breaker */
+ if (fp->fi_lease) {
+ status = 0;
+ atomic_inc(&fp->fi_delegees);
+ hash_delegation_locked(dp, fp);
+ spin_unlock(&fp->fi_lock);
+ goto out_unlock;
+ }
fp->fi_lease = fl;
fp->fi_deleg_file = fl->fl_file;
atomic_set(&fp->fi_delegees, 1);
- spin_lock(&state_lock);
hash_delegation_locked(dp, fp);
+ spin_unlock(&fp->fi_lock);
spin_unlock(&state_lock);
return 0;
-out_free:
+out_unlock:
+ spin_unlock(&state_lock);
+out_fput:
if (fl->fl_file)
fput(fl->fl_file);
locks_free_lock(fl);
@@ -3270,19 +3287,27 @@ out_free:

static int nfs4_set_delegation(struct nfs4_delegation *dp, struct nfs4_file *fp)
{
+ int status = 0;
+
if (fp->fi_had_conflict)
return -EAGAIN;
get_nfs4_file(fp);
+ spin_lock(&state_lock);
+ spin_lock(&fp->fi_lock);
dp->dl_file = fp;
- if (!fp->fi_lease)
+ if (!fp->fi_lease) {
+ spin_unlock(&fp->fi_lock);
+ spin_unlock(&state_lock);
return nfs4_setlease(dp);
- spin_lock(&state_lock);
+ }
atomic_inc(&fp->fi_delegees);
if (fp->fi_had_conflict) {
- spin_unlock(&state_lock);
- return -EAGAIN;
+ status = -EAGAIN;
+ goto out_unlock;
}
hash_delegation_locked(dp, fp);
+out_unlock:
+ spin_unlock(&fp->fi_lock);
spin_unlock(&state_lock);
return 0;
}
--
1.9.3


2014-06-19 14:53:23

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 101/104] nfsd: remove nfs4_lock_state: nfs4_laundromat

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 6000da3285d7..55a5c238bb94 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -3987,8 +3987,6 @@ nfs4_laundromat(struct nfsd_net *nn)
time_t cutoff = get_seconds() - nn->nfsd4_lease;
time_t t, new_timeo = nn->nfsd4_lease;

- nfs4_lock_state();
-
dprintk("NFSD: laundromat service - starting\n");
nfsd4_end_grace(nn);
INIT_LIST_HEAD(&reaplist);
@@ -4052,7 +4050,6 @@ nfs4_laundromat(struct nfsd_net *nn)
spin_unlock(&nn->client_lock);

new_timeo = max_t(time_t, new_timeo, NFSD_LAUNDROMAT_MINTIMEOUT);
- nfs4_unlock_state();
return new_timeo;
}

--
1.9.3


2014-06-24 12:02:43

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH v1 017/104] NFSd: Allow struct nfsd4_compound_state to cache the nfs4_client

Another patch that doesn't seem to depend on the locking changes.

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

2014-06-19 14:52:20

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 056/104] NFSd: Protect adding/removing lock owners using client_lock

From: Trond Myklebust <[email protected]>

* Ensure that alloc_init_lock_stateowner() checks the hashtable
under the lock before adding a new element.

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 73c76a895015..a8969884f65e 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -827,26 +827,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 destroy_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);
}

@@ -4667,7 +4683,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);
@@ -4684,6 +4700,18 @@ 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 hash_lockowner(struct nfs4_lockowner *lo, unsigned int strhashval, struct nfs4_client *clp)
{
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
@@ -4693,7 +4721,11 @@ static void hash_lockowner(struct nfs4_lockowner *lo, unsigned int strhashval, s

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)
@@ -4712,8 +4744,10 @@ static void nfs4_free_lockowner(struct nfs4_stateowner *sop)
*/

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)
@@ -4721,9 +4755,17 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, str
INIT_LIST_HEAD(&lo->lo_owner.so_stateids);
lo->lo_owner.so_is_open_owner = 0;
lo->lo_owner.so_seqid = lock->lk_new_lock_seqid;
- hash_lockowner(lo, strhashval, clp);
lo->lo_owner.so_free = nfs4_free_lockowner;
lo->lo_owner.so_unhash = nfs4_unhash_lockowner;
+ spin_lock(&nn->client_lock);
+ ret = find_lockowner_str_locked(&clp->cl_clientid,
+ &lock->lk_new_owner, nn);
+ if (ret == NULL) {
+ hash_lockowner(lo, strhashval, clp);
+ ret = lo;
+ } else
+ nfs4_free_lockowner(&lo->lo_owner);
+ spin_unlock(&nn->client_lock);
return lo;
}

@@ -5244,6 +5286,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);
@@ -5258,6 +5301,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
INIT_LIST_HEAD(&matches);

/* 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;
@@ -5267,6 +5311,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) {
@@ -5276,16 +5321,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;
destroy_lockowner(lo);
out:
+ if (sop)
+ nfs4_put_stateowner(sop);
nfs4_unlock_state();
return status;
}
--
1.9.3


2014-06-19 14:51:55

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 040/104] 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.

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 cf6baac38d98..3b0144269f9d 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4253,10 +4253,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));
@@ -4265,6 +4267,8 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,

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


2014-06-19 14:51:40

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 030/104] nfsd: clean up races in lock stateid searching and creation

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

Also, it's 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 6e8d3ec088d9..a6c94e8a6ad8 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4555,16 +4555,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);
@@ -4573,20 +4571,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)
@@ -4595,6 +4593,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)
@@ -4636,14 +4664,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


2014-06-19 14:51:58

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 042/104] 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 ff6e0f99413c..7ce4edcc0e61 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4209,8 +4209,11 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
nfsd4_cstate_assign_replay(cstate, stp->st_stateowner);

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

@@ -4219,16 +4222,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;
}

@@ -4255,8 +4260,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)
@@ -4441,8 +4444,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));

@@ -4794,9 +4795,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;
@@ -5019,8 +5017,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-06-19 14:51:17

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 013/104] NFSd: Allow lockowners to hold several stateids

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index f4c71bc96970..77c1b62db4fd 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -3956,12 +3956,7 @@ nfsd4_free_lock_stateid(struct nfs4_ol_stateid *stp)

if (check_for_locks(stp->st_file, lo))
return nfserr_locks_held;
- /*
- * Currently there's a 1-1 lock stateid<->lockowner
- * correspondance, and we have to delete the lockowner when we
- * delete the lock stateid:
- */
- release_lockowner(lo);
+ release_lockowner_if_empty(lo);
return nfs_ok;
}

@@ -4510,6 +4505,19 @@ alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, struct
return stp;
}

+static struct nfs4_ol_stateid *
+find_lock_stateid(struct nfs4_lockowner *lo, struct 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)
+ return lst;
+ }
+ return NULL;
+}
+
+
static int
check_lock_length(u64 offset, u64 length)
{
@@ -4538,25 +4546,28 @@ static __be32 lookup_or_create_lock_state(struct nfsd4_compound_state *cstate, s

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

@@ -4715,7 +4726,7 @@ out:
if (filp)
fput(filp);
if (status && new_state)
- release_lockowner(lock_sop);
+ release_lockowner_if_empty(lock_sop);
nfsd4_bump_seqid(cstate, status);
nfs4_unlock_state();
if (file_lock)
--
1.9.3


2014-06-26 01:26:29

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH v1 000/104] nfsd: eliminate the client_mutex

I'm seeing this, by the way.--b.

[ 512.960652] nfsd: non-standard errno: -7
[ 650.442646] ------------[ cut here ]------------
[ 650.442754] WARNING: CPU: 3 PID: 647 at fs/nfsd/nfs4state.c:761
revoke_delegation+0xa7/0xb0 [nfsd]()
[ 650.442760] Modules linked in: rpcsec_gss_krb5 nfsd auth_rpcgss
oid_registry nfs_acl lockd sunrpc
[ 650.442792] CPU: 3 PID: 647 Comm: kworker/u8:2 Not tainted
3.16.0-rc2-00479-gb675b9c #2982
[ 650.442796] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011
[ 650.442814] Workqueue: nfsd4 laundromat_main [nfsd]
[ 650.442821] 0000000000000009 ffff880036287c48 ffffffff81a439d6
0000000000000001
[ 650.442836] 0000000000000000 ffff880036287c88 ffffffff8106d65c
ffff880036287c68
[ 650.442851] ffff8800312aee60 ffff88001bcfe800 ffff8800312aeeb0
ffffffff822c7cc0
[ 650.442865] Call Trace:
[ 650.442878] [<ffffffff81a439d6>] dump_stack+0x4f/0x7c
[ 650.442889] [<ffffffff8106d65c>] warn_slowpath_common+0x8c/0xc0
[ 650.442897] [<ffffffff8106d6aa>] warn_slowpath_null+0x1a/0x20
[ 650.442917] [<ffffffffa011a607>] revoke_delegation+0xa7/0xb0 [nfsd]
[ 650.442936] [<ffffffffa011aaec>] laundromat_main+0x2dc/0x480 [nfsd]
[ 650.442956] [<ffffffffa011a9f4>] ? laundromat_main+0x1e4/0x480
[nfsd]
[ 650.442967] [<ffffffff8108924e>] process_one_work+0x1ce/0x500
[ 650.442976] [<ffffffff810891db>] ? process_one_work+0x15b/0x500
[ 650.442985] [<ffffffff81089b9b>] worker_thread+0x11b/0x4f0
[ 650.442995] [<ffffffff810bba2d>] ? trace_hardirqs_on+0xd/0x10
[ 650.443004] [<ffffffff81a4c36b>] ?
_raw_spin_unlock_irqrestore+0x4b/0x80
[ 650.443013] [<ffffffff81089a80>] ? init_pwq+0x190/0x190
[ 650.443022] [<ffffffff81090ac4>] kthread+0xe4/0x100
[ 650.443032] [<ffffffff810909e0>] ? __init_kthread_worker+0x70/0x70
[ 650.443041] [<ffffffff81a4caac>] ret_from_fork+0x7c/0xb0
[ 650.443050] [<ffffffff810909e0>] ? __init_kthread_worker+0x70/0x70
[ 650.443055] ---[ end trace e3faa7f0516143d3 ]---

2014-06-19 14:53:27

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 104/104] nfsd: add file documenting new state object model

Signed-off-by: Jeff Layton <[email protected]>
---
.../filesystems/nfs/nfsd4-state-objects.txt | 110 +++++++++++++++++++++
1 file changed, 110 insertions(+)
create mode 100644 Documentation/filesystems/nfs/nfsd4-state-objects.txt

diff --git a/Documentation/filesystems/nfs/nfsd4-state-objects.txt b/Documentation/filesystems/nfs/nfsd4-state-objects.txt
new file mode 100644
index 000000000000..fa3f70608422
--- /dev/null
+++ b/Documentation/filesystems/nfs/nfsd4-state-objects.txt
@@ -0,0 +1,110 @@
+KNFSD4 State Object Model:
+==========================
+Written 2014 by Jeff Layton <[email protected]>
+
+Introduction:
+-------------
+Until recently, knfsd relied heavily on a global mutex to ensure that objects
+didn't disappear while they were being operated on. That has proven to be a
+scalability bottleneck however, so the code has been overhauled to make heavy
+use of reference counting and spinlocks for tracking the different objects.
+
+The state model in NFSv4 is quite complex. Thus, the relationship between the
+objects to track this state in knfsd is also complex. This document attempts to
+lay out how all of the different objects relate to one another, and which ones
+hold references to others.
+
+In addition to the "persistent" references documented here, references to these
+objects can also be taken during the processing of compounds in order to ensure
+that the objects don't disappear suddenly.
+
+struct nfsd_net:
+----------------
+Represents a nfsd "container". With respect to nfsv4 state tracking, the fields
+of interest are the *_id_hashtbls and the *_name_tree. These track the
+nfs4_client objects by either short or long form clientid.
+
+Each nfsd_net runs a nfs4_laundromat workqueue job every lease period to clean
+up expired clients and delegations within the container.
+
+struct nfs4_file:
+-----------------
+These objects are global. nfsd only keeps one instance of a nfs4_file per inode
+(though it may keep multiple file descriptors open per inode). These are
+tracked in the file_hashtbl which is protected by the state_lock spinlock.
+
+struct nfs4_client:
+-------------------
+The initial object created by an NFS client using SETCLIENTID (for NFSv4.0) or
+EXCHANGE_ID (for NFSv4.1+). These objects are refcounted and timestamped. Each
+nfsd_net_ns object contains a set of these and they are tracked via short and
+long form clientid. They are hashed and searched for under the per-nfsd-net
+client_lock spinlock.
+
+The lifecycle of these is a little strange. References to it are only held
+during the processing of compounds, and in certain other operations. In their
+"resting state" they have a refcount of 0. If they are not renewed within a
+lease period, they become eligible for destruction by the laundromat.
+
+These objects can also be destroyed prematurely by the fault injection code,
+or if the client sends certain forms of SETCLIENTID or EXCHANGE_ID updates.
+Care is taken *not* to do this however when the objects have an elevated
+refcount.
+
+struct nfsd4_session:
+---------------------
+Represents a v4.1+ session. These are refcounted in a similar fashion to
+the nfs4_client. References are only taken when the server is actively working
+on the object (primarily during the processing of compounds).
+
+struct nfs4_stateowner:
+-----------------------
+A core object that represents either an open or lock owner. The object and lock
+owner objects have one of these embedded within them. Refcounts and other
+fields common to both owner types are contained within these structures.
+
+struct nfs4_openowner:
+----------------------
+When a file is opened, the client provides an open state owner opaque string
+that indicates the "owner" of that open. These objects are refcounted.
+References to it are held by each open state associated with it. This object is
+a superset of the nfs4_stateowner struct.
+
+struct nfs4_lockowner:
+----------------------
+Represents a generic "lockowner". Similar to an openowner. References to it are
+held by the lock stateids that are created on its behalf. This object is a
+superset of the nfs4_stateowner struct.
+
+strict nfs4_stid:
+-----------------
+A core object that represents a "generic" stateid. These are generally embedded
+within the different (more specific) stateid objects and contain fields that
+are of general use to any stateid.
+
+struct nfs4_ol_stateid:
+-----------------------
+A generic struct representing either a open or lock stateid. The nfs4_client
+holds a reference to each of these objects, and they in turn hold a reference
+to their respective stateowners. The client's reference is released in response
+to a close or unlock (depending on whether it's an open or lock stateid) or
+when the client is being destroyed.
+
+In the case of v4.0, these objects are preserved for a little while after close
+in order to handle CLOSE replays. They are eventually reclaimed via a LRU
+scheme by the laundromat.
+
+This object is a superset of the nfs4_stid.
+
+struct nfs4_delegation:
+-----------------------
+Represents a delegation stateid. The nfs4_client holds references to these and
+they are put when it is being destroyed or when the delegation is returned by
+the client.
+
+If the server attempts to recall a delegation and the client doesn't do so
+before a timeout, the server may also revoke the delegation. In that case, the
+object will either be destroyed (v4.0) or moved to a per-client list of revoked
+delegations (v4.1+).
+
+This object is a superset of the nfs4_stid.
--
1.9.3


2014-06-24 11:53:38

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH v1 012/104] NFSd: Clean up helper __release_lock_stateid

On Thu, Jun 19, 2014 at 10:49:18AM -0400, Jeff Layton wrote:
> From: Trond Myklebust <[email protected]>
>
> Use filp_close() instead of open coding

filp_close does more than what's currently opencoded here:

- file_count debug check which seems pointless but harmless
- calling ->flush. This doesn't do much on nfs exportable filesystem
except for exofs, which does dumb shit in it that should be removed,
but the maintainer refuse.
- call into dnotify, which we probably should be doing here.

> -static void __release_lock_stateid(struct nfs4_ol_stateid *stp)
> +static void __release_lock_stateid(struct nfs4_lockowner *lo,
> + struct nfs4_ol_stateid *stp)

But what's the point of explicitly passing the lock owner instead of
deriving it?


2014-06-19 14:52:04

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 045/104] NFSd: Add reference counting to state owners

From: Trond Myklebust <[email protected]>

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 4ddd7ec31d6c..c96a9b7071e1 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);
+static void nfs4_put_stateowner(struct nfs4_stateowner *sop);

/* Locking: */

@@ -826,16 +827,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)
@@ -905,18 +900,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
@@ -2860,9 +2849,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);
@@ -2871,6 +2868,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 nfs4_client *clp, struct nfsd4_open *open) {
struct nfs4_openowner *oo;
@@ -2878,6 +2883,7 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, str
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;
@@ -4590,6 +4596,13 @@ static void hash_lockowner(struct nfs4_lockowner *lo, unsigned int strhashval, s
list_add(&lo->lo_owner.so_strhash, &nn->ownerstr_hashtbl[strhashval]);
}

+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
@@ -4611,6 +4624,7 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, str
* case of new lockowners; so increment the lock seqid manually: */
lo->lo_owner.so_seqid = lock->lk_new_lock_seqid + 1;
hash_lockowner(lo, strhashval, clp);
+ lo->lo_owner.so_free = nfs4_free_lockowner;
return lo;
}

diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index f5dee9e8550a..ebd3d302a572 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -340,10 +340,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-06-23 16:23:33

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH v1 005/104] NFSd: Add fine grained protection for the nfs4_file->fi_stateids list

On Mon, Jun 23, 2014 at 12:20:39PM -0400, Jeff Layton wrote:
> Correct. I'll add that into the desciption if you think it's warranted,
> but again that's the case with many of the patches in this series.

It might be fairly obvious in the context of the series, it's not
if someone goes back in history with a git-blame.

And it's not a 100% obvious with the series either, at least earlier
versions also fixes pre-existing races, although all of that might be
upstream now.

So as far as I am concerned a simple one-line blurb mentioning what
the new locking is for would be useful to be added to all patches
just changing the locking.

2014-06-23 16:48:33

by Jeff Layton

[permalink] [raw]
Subject: Re: [PATCH v1 009/104] locks: add file_has_lease

On Mon, 23 Jun 2014 09:12:54 -0700
Christoph Hellwig <[email protected]> wrote:

> On Thu, Jun 19, 2014 at 10:49:15AM -0400, Jeff Layton wrote:
> > Add a function that can tell us whether a file description has had a
> > lease set on it.
>
> The code looks good, but how is this information useful given that it
> will be stable as soon as i_lock is dropped?
>

The next patch is what uses it. I can squash the two together, but I
figured it was best to keep them separate since we're touching two
different subsystems here.

The basic idea here is to avoid calling vfs_setlease under a spinlock.
The next patch does that and then takes the spinlocks and hash the
delegation once the lease is set.

Problem: what if the delegation got broken before we could hash it?

The workqueue job that's queued by break_deleg will take the state_lock
spinlock before it can unhash the delegation. So, we take the
state_lock and then call file_has_lease. If the lease is still present
then we know that it hasn't been (and won't be) broken before we could
hash it.

--
Jeff Layton <[email protected]>

2014-06-19 14:52:22

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 058/104] NFSd: Cache the client that was looked up in lookup_clientid()

From: Trond Myklebust <[email protected]>

We want to use the nfsd4_compound_state to cache the nfs4_client
in order to optimise away extra lookups of the clid.

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 7034db2d00b3..428944aef83f 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -3814,15 +3814,31 @@ void nfsd4_cleanup_open_state(struct nfsd4_compound_state *cstate,
put_generic_stateid(open->op_stp);
}

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

- if (STALE_CLIENTID(clid, nn))
- return nfserr_stale_clientid;
- found = find_confirmed_client(clid, session, nn);
- if (clp)
- *clp = found;
+ if (cstate->clp != NULL) {
+ found = cstate->clp;
+ if (!same_clid(&found->cl_clientid, clid))
+ return nfserr_stale_clientid;
+ } else {
+ if (STALE_CLIENTID(clid, nn))
+ return nfserr_stale_clientid;
+ /*
+ * Usually for v4.1+ we get the client in the SEQUENCE op, so
+ * if we don't have one cached already then we know this is for
+ * is for v4.0 and "sessions" will be false.
+ */
+ found = find_confirmed_client(clid, false, nn);
+ /* Cache the nfs4_client in cstate! */
+ if (found) {
+ cstate->clp = found;
+ atomic_inc(&found->cl_refcount);
+ }
+ }
return found ? nfs_ok : nfserr_expired;
}

@@ -3837,9 +3853,10 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
nfs4_lock_state();
dprintk("process_renew(%08x/%08x): starting\n",
clid->cl_boot, clid->cl_id);
- status = lookup_clientid(clid, cstate->minorversion, nn, &clp);
+ status = lookup_clientid(clid, cstate, nn);
if (status)
goto out;
+ clp = cstate->clp;
status = nfserr_cb_path_down;
if (!list_empty(&clp->cl_delegations)
&& clp->cl_cb_state != NFSD4_CB_UP)
@@ -4111,22 +4128,19 @@ nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate,
stateid_t *stateid, unsigned char typemask,
struct nfs4_stid **s, struct nfsd_net *nn)
{
- struct nfs4_client *cl;
__be32 status;
- bool sessions = cstate->minorversion != 0;

if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
return nfserr_bad_stateid;
- status = lookup_clientid(&stateid->si_opaque.so_clid, sessions,
- nn, &cl);
+ status = lookup_clientid(&stateid->si_opaque.so_clid, cstate, nn);
if (status == nfserr_stale_clientid) {
- if (sessions)
+ if (cstate->session)
return nfserr_bad_stateid;
return nfserr_stale_stateid;
}
if (status)
return status;
- *s = find_stateid_by_type(cl, stateid, typemask);
+ *s = find_stateid_by_type(cstate->clp, stateid, typemask);
if (!*s)
return nfserr_bad_stateid;
return nfs_ok;
@@ -5119,7 +5133,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
nfs4_lock_state();

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

nfs4_lock_state();

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

--
1.9.3


2014-06-19 14:51:23

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 017/104] NFSd: Allow struct nfsd4_compound_state to cache the nfs4_client

From: Trond Myklebust <[email protected]>

This will be used later to accelerate lookups of the clientid

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

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

+static void put_client_renew(struct nfs4_client *clp)
+{
+ struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
+
+ spin_lock(&nn->client_lock);
+ put_client_renew_locked(clp);
+ spin_unlock(&nn->client_lock);
+}
+
static __be32 nfsd4_get_session_locked(struct nfsd4_session *ses)
{
__be32 status;
@@ -2428,6 +2437,7 @@ nfsd4_sequence(struct svc_rqst *rqstp,
goto out_put_session;
cstate->slot = slot;
cstate->session = session;
+ cstate->clp = clp;
/* Return the cached reply status and set cstate->status
* for nfsd4_proc_compound processing */
status = nfsd4_replay_cache_entry(resp, seq);
@@ -2462,6 +2472,7 @@ nfsd4_sequence(struct svc_rqst *rqstp,

cstate->slot = slot;
cstate->session = session;
+ cstate->clp = clp;

out:
switch (clp->cl_cb_state) {
@@ -2498,7 +2509,8 @@ nfsd4_sequence_done(struct nfsd4_compoundres *resp)
}
/* Drop session reference that was taken in nfsd4_sequence() */
nfsd4_put_session(cs->session);
- }
+ } else if (cs->clp)
+ put_client_renew(cs->clp);
}

__be32
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index 570073aac50c..c9bf09bebdae 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -55,6 +55,7 @@ struct nfsd4_compound_state {
struct svc_fh current_fh;
struct svc_fh save_fh;
struct nfs4_stateowner *replay_owner;
+ struct nfs4_client *clp;
/* For sessions DRC */
struct nfsd4_session *session;
struct nfsd4_slot *slot;
--
1.9.3


2014-06-19 14:52:42

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 072/104] NFSd: Protect session creation and client confirm using client_lock

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

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 1beea0b19744..be84ac190052 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -143,17 +143,6 @@ static __be32 mark_client_expired_locked(struct nfs4_client *clp)
return nfs_ok;
}

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

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

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

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

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

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

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

u64 nfsd_forget_client(struct nfs4_client *clp, u64 max)
{
- if (mark_client_expired(clp))
+ __be32 ret;
+ struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
+
+ spin_lock(&nn->client_lock);
+ ret = mark_client_expired_locked(clp);
+ spin_unlock(&nn->client_lock);
+ if (ret != nfs_ok)
return 0;
expire_client(clp);
return 1;
--
1.9.3


2014-06-19 14:51:46

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 034/104] 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.

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 019f07a78b73..d44f727fd9b5 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4585,6 +4585,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);
@@ -4609,8 +4610,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;
}
@@ -4703,7 +4706,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
{
struct nfs4_openowner *open_sop = NULL;
struct nfs4_lockowner *lock_sop = NULL;
- struct nfs4_ol_stateid *lock_stp;
+ struct nfs4_ol_stateid *lock_stp = NULL;
struct file *filp = NULL;
struct file_lock *file_lock = NULL;
struct file_lock *conflock = NULL;
@@ -4756,11 +4759,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);
@@ -4848,6 +4855,8 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
out:
if (filp)
fput(filp);
+ if (lock_stp)
+ put_generic_stateid(lock_stp);
if (status && new_state)
release_lockowner_if_empty(lock_sop);
nfsd4_bump_seqid(cstate, status);
--
1.9.3


2014-06-19 14:51:04

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 004/104] nfsd4: use cl_lock to synchronize all stateid idr calls

From: Benny Halevy <[email protected]>

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index d18fd8c7691c..8dc1289a7b74 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -326,7 +326,6 @@ static void nfs4_file_put_access(struct nfs4_file *fp, int oflag)
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;

@@ -334,7 +333,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;
@@ -396,9 +399,11 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv

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

- 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)
@@ -1141,7 +1146,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);
}

@@ -1364,7 +1371,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-06-19 14:52:12

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 051/104] 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 | 34 ++++++++--------------------------
1 file changed, 8 insertions(+), 26 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 17a1136ad0a1..a80ba729e8b3 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -804,8 +804,7 @@ static void put_generic_stateid(struct nfs4_ol_stateid *stp)
nfs4_put_stid(&stp->st_stid);
}

-static void __release_lock_stateid(struct nfs4_lockowner *lo,
- 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);

@@ -829,32 +828,17 @@ 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(lo, stp);
+ release_lock_stateid(stp);
}
}

-static void release_lockowner(struct nfs4_lockowner *lo)
+static void destroy_lockowner(struct nfs4_lockowner *lo)
{
unhash_lockowner(lo);
release_lockowner_stateids(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(lo, 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)
@@ -4144,7 +4128,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;
}

@@ -4789,8 +4773,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;
@@ -4801,7 +4783,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;
}
@@ -5231,13 +5212,14 @@ 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;
- release_lockowner(lo);
- nfs4_put_stateowner(sop);
+ destroy_lockowner(lo);
out:
nfs4_unlock_state();
return status;
--
1.9.3


2014-06-23 16:13:56

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH v1 000/104] nfsd: eliminate the client_mutex

On Mon, Jun 23, 2014 at 12:10:26PM -0400, J. Bruce Fields wrote:
> On Mon, Jun 23, 2014 at 09:00:01AM -0700, Christoph Hellwig wrote:
> > On Mon, Jun 23, 2014 at 09:56:45AM -0400, Jeff Layton wrote:
> > > > - there is some confusion of NFSd vs nfsd in the subsystem prefixes.
> > > > While it seems odd and against the usual naming NFSd seems to be
> > > > the common one for nfs patches.
> > > >
> > >
> > > I tend to prefer "nfsd", but ok -- "NFSd" it is.
> >
> > I'd prefer nfsd as well, but in Rome do as the Romans do, so..
>
> These Romans?:

Oops, sorry. It's mostly Trond for the client who is using the upper
case NFS*.


2014-06-19 14:51:21

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 016/104] NFSd: Don't get a session reference without a client reference

From: Trond Myklebust <[email protected]>

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

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

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

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

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

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

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

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

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

nfs4_lock_state();
status = nfserr_not_only_op;
@@ -2248,14 +2288,12 @@ nfsd4_destroy_session(struct svc_rqst *r,
}
dump_sessionid(__func__, &sessionid->sessionid);
spin_lock(&nn->client_lock);
- ses = find_in_sessionid_hashtbl(&sessionid->sessionid, SVC_NET(r));
- status = nfserr_badsession;
+ ses = find_in_sessionid_hashtbl(&sessionid->sessionid, net, &status);
if (!ses)
goto out_client_lock;
status = nfserr_wrong_cred;
if (!mach_creds_match(ses->se_client, r))
- goto out_client_lock;
- nfsd4_get_session_locked(ses);
+ goto out_put_session;
status = mark_session_dead_locked(ses, 1 + ref_held_by_me);
if (status)
goto out_put_session;
@@ -2267,7 +2305,7 @@ nfsd4_destroy_session(struct svc_rqst *r,
spin_lock(&nn->client_lock);
status = nfs_ok;
out_put_session:
- nfsd4_put_session(ses);
+ nfsd4_put_session_locked(ses);
out_client_lock:
spin_unlock(&nn->client_lock);
out:
@@ -2342,7 +2380,8 @@ nfsd4_sequence(struct svc_rqst *rqstp,
struct nfsd4_conn *conn;
__be32 status;
int buflen;
- struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
+ struct net *net = SVC_NET(rqstp);
+ struct nfsd_net *nn = net_generic(net, nfsd_net_id);

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

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

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

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

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

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

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


2014-06-23 18:26:23

by Jeff Layton

[permalink] [raw]
Subject: Re: [PATCH v1 000/104] nfsd: eliminate the client_mutex

On Mon, 23 Jun 2014 12:10:26 -0400
"J. Bruce Fields" <[email protected]> wrote:

> On Mon, Jun 23, 2014 at 09:00:01AM -0700, Christoph Hellwig wrote:
> > On Mon, Jun 23, 2014 at 09:56:45AM -0400, Jeff Layton wrote:
> > > > - there is some confusion of NFSd vs nfsd in the subsystem prefixes.
> > > > While it seems odd and against the usual naming NFSd seems to be
> > > > the common one for nfs patches.
> > > >
> > >
> > > I tend to prefer "nfsd", but ok -- "NFSd" it is.
> >
> > I'd prefer nfsd as well, but in Rome do as the Romans do, so..
>
> These Romans?:
>
> $ git log --pretty=format:"%s" fs/nfsd|cut -d: -f1|grep -v '^Merge'|sed 's/\[PATCH\] //'|sort|uniq -c|sort -n|tail
> 8 sunrpc
> 12 fs
> 13 locks
> 15 NFSd
> 30 SUNRPC
> 89 nfsd41
> 107 NFSD
> 228 knfsd
> 449 nfsd
> 594 nfsd4
>
> Anyway, I don't care.
>
> --b.

In that case, I'll go with what I prefer -- "nfsd:". But I will at
least go through and make them all consistent.

Thanks,
--
Jeff Layton <[email protected]>

2014-06-19 14:52:51

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 079/104] nfsd: fix misleading comment

...the client_lock now protects this.

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index cfb090b9bb21..bff26c3d306f 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -2346,7 +2346,7 @@ nfsd4_create_session(struct svc_rqst *rqstp,
cs_slot->sl_seqid++;
cr_ses->seqid = cs_slot->sl_seqid;

- /* cache solo and embedded create sessions under the state lock */
+ /* cache solo and embedded create sessions under the client_lock */
nfsd4_cache_create_session(cr_ses, cs_slot, status);
spin_unlock(&nn->client_lock);
/* init connection and backchannel */
--
1.9.3


2014-06-19 14:51:28

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 021/104] NFSd: Add reference counting to the lock and open stateids

From: Trond Myklebust <[email protected]>

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 d708e7c807d5..c01f1628c989 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -750,8 +750,10 @@ static void close_generic_stateid(struct nfs4_ol_stateid *stp)
release_all_access(stp);
}

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

static void unhash_lockowner(struct nfs4_lockowner *lo)
@@ -833,7 +835,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)
@@ -854,7 +856,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;
}
}
@@ -3607,7 +3609,7 @@ void nfsd4_cleanup_open_state(struct nfsd4_open *open, __be32 status)
if (open->op_file)
nfsd4_free_file(open->op_file);
if (open->op_stp)
- free_generic_stateid(open->op_stp);
+ put_generic_stateid(open->op_stp);
}

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

if (cstate->minorversion)
- free_generic_stateid(stp);
+ put_generic_stateid(stp);
else {
if (stp->st_file) {
put_nfs4_file(stp->st_file);
--
1.9.3


2014-06-19 14:51:07

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 006/104] 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 state_lock() for protection in the
case of NFSv4 open owners. Instead, we add a mutex that will
only be taken for NFSv4.0 state mutating operations, and
that will be released once the entire compound is done.

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

diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 8904c9cbcb89..df5fcae17591 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -469,11 +469,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;
}

@@ -1404,10 +1404,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 d1b975b29334..1daab96804a4 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -781,7 +781,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)
@@ -2622,6 +2622,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)
@@ -3923,8 +3924,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)
@@ -3985,8 +3985,7 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
status = nfs_ok;
out:
nfsd4_bump_seqid(cstate, status);
- if (!cstate->replay_owner)
- nfs4_unlock_state();
+ nfs4_unlock_state();
return status;
}

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

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

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

out:
nfsd4_bump_seqid(cstate, status);
- if (!cstate->replay_owner)
- nfs4_unlock_state();
+ nfs4_unlock_state();
if (file_lock)
locks_free_lock(file_lock);
return status;
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 2d305a121f37..bd0ebd7ff043 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -3923,8 +3923,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 366887ab5204..26234b106182 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -329,6 +329,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 18cbb6d9c8a9..b577273224e7 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -73,6 +73,25 @@ static inline bool nfsd4_has_session(struct nfsd4_compound_state *cs)
return cs->slot != NULL;
}

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


2014-06-19 14:52:49

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 077/104] nfsd: ensure that clp->cl_revoked list is protected by clp->cl_lock

Currently, both destroy_revoked_delegation and revoke_delegation
manipulate the cl_revoked list without any locking. Ensure that the
clp->cl_lock is held when manipulating it, except for the list walking
in destroy_client. At that point, the client should no longer be in use,
so we should be safe to walk the list without any locking, which also
means that we don't need to do the list_splice_init there either.

Also, the fact that destroy_revoked_delegation and revoke_delegation
delete dl_recall_lru without any locking makes it difficult to know
whether they're doing so safely in all cases. Move the list_del_init
calls into the callers, and add WARN_ONs in the event that these calls
are passed a delegation that has a non-empty list.

Signed-off-by: Jeff Layton <[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 561c77a02920..8267531ed455 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -649,7 +649,7 @@ static void unhash_and_destroy_delegation(struct nfs4_delegation *dp)

static void destroy_revoked_delegation(struct nfs4_delegation *dp)
{
- list_del_init(&dp->dl_recall_lru);
+ WARN_ON(!list_empty(&dp->dl_recall_lru));
nfs4_put_delegation(dp);
}

@@ -657,11 +657,15 @@ static void revoke_delegation(struct nfs4_delegation *dp)
{
struct nfs4_client *clp = dp->dl_stid.sc_client;

+ WARN_ON(!list_empty(&dp->dl_recall_lru));
+
if (clp->cl_minorversion == 0)
destroy_revoked_delegation(dp);
else {
dp->dl_stid.sc_type = NFS4_REVOKED_DELEG_STID;
- list_move(&dp->dl_recall_lru, &clp->cl_revoked);
+ spin_lock(&clp->cl_lock);
+ list_add(&dp->dl_recall_lru, &clp->cl_revoked);
+ spin_unlock(&clp->cl_lock);
}
}

@@ -1459,9 +1463,9 @@ __destroy_client(struct nfs4_client *clp)
list_del_init(&dp->dl_recall_lru);
destroy_delegation(dp);
}
- list_splice_init(&clp->cl_revoked, &reaplist);
- while (!list_empty(&reaplist)) {
+ while (!list_empty(&clp->cl_revoked)) {
dp = list_entry(reaplist.next, struct nfs4_delegation, dl_recall_lru);
+ list_del_init(&dp->dl_recall_lru);
destroy_revoked_delegation(dp);
}
while (!list_empty(&clp->cl_openowners)) {
@@ -4391,6 +4395,11 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
break;
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);
+
destroy_revoked_delegation(dp);
ret = nfs_ok;
break;
--
1.9.3


2014-06-23 16:12:55

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH v1 009/104] locks: add file_has_lease

On Thu, Jun 19, 2014 at 10:49:15AM -0400, Jeff Layton wrote:
> Add a function that can tell us whether a file description has had a
> lease set on it.

The code looks good, but how is this information useful given that it
will be stable as soon as i_lock is dropped?


2014-06-19 15:33:31

by Jeff Layton

[permalink] [raw]
Subject: Re: [PATCH v1 000/104] nfsd: eliminate the client_mutex

On Thu, 19 Jun 2014 10:49:06 -0400
Jeff Layton <[email protected]> wrote:

> Here it is. The long awaited removal of the client_mutex from knfsd.
> As many of us are aware, one of the major bottlenecks in NFSv4 serving
> is the fact that all compounds are processed while holding a single,
> global mutex.
>
> This has an obvious detrimental effect on scalability. I've heard
> anecdotal reports of 10x slowdowns with v4 serving vs. v3 on the same
> machine, primarily due to it.
>
> This patchset eliminates that mutex and (hopefully!) the bottleneck
> that it imposes. The basic idea is to add refcounting to most of the
> objects that compounds deal with to ensure that they are pinned while
> in use. Spinlocks are used to protect things like the hashtables and
> trees that track the objects.
>
> Benny started this set quite some time ago, and Trond took up the
> torch early this spring. He then handed it to me to clean up the
> remaining bits about a month ago.
>
> I'd like to see this considered for v3.17. Obviously, getting it into
> linux-next ASAP would be a good thing once it passes review.
>
> Some other highlights/notes:
>
> - use of lockdep has been greatly increased. I found it was the only
> way to ensure that I didn't create lock inversion problems and such.
>
> - clients are now only looked up once per compund and are cached in
> the cstate. This keeps us from having to keep searching for the
> same client for each compound op.
>
> - openowners and lockowners are now handled more sanely. References
> to them are only held by the stateids that are associated with them,
> and they are destroyed when the last reference to them is put. Also,
> lockowners can now have more than one stateid (in accordance with
> the spec)
>
> - the fault injection code has been overhauled. It should look the same
> to users, but it needed major work to deal with the new locking.
> It's probably possible to consolidate some of that code as well --
> it more cut-and-pastey than I'd like.
>
> - I've added a file to document how all of the moving parts fit
> together. It probably can use more fleshing out, but it's a start.
>
> - the lease handling is now a bit more complex than I'd like. Lease
> removal in particular is currently called under a spinlock, which
> I think we'll need to eventually clean up. For now, this is ok
> since the first thing that vfs_setlease does is lock the i_lock,
> but I think we'll eventually need to do an i_lock pushdown in the
> lease handling code, and ensure that vfs_setlease isn't called
> under a spinlock.
>
> As always, comments and review are welcome.
>
> Benny Halevy (1):
> nfsd4: use cl_lock to synchronize all stateid idr calls
>
> Jeff Layton (40):
> NFSd: Ensure that nfs4_file_get_access enforces share access modes
> locks: add file_has_lease
> NFSd: Protect the nfs4_file delegation fields using the fi_lock
> NFSd: Allow lockowners to hold several stateids
> 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: clean up lockowner refcounting when finding them
> nfsd: add an operation for unhashing a stateowner
> nfsd: clean up nfs4_release_lockowner
> nfsd: clean up refcounting for lockowners
> nfsd: declare v4.1+ openowners confirmed on creation
> nfsd: make openstateids hold references to their openowners
> nfsd: don't allow CLOSE to proceed until refcount on stateid drops
> lockdep: add lockdep_assert_not_held
> nfsd: add locking to stateowner release
> nfsd: optimize destroy_lockowner cl_lock thrashing
> nfsd: reduce cl_lock trashing in release_openowner
> NFSd: Protect session creation and client confirm using client_lock
> nfsd: protect the close_lru list and oo_last_closed_stid with
> client_lock
> nfsd: ensure that clp->cl_revoked list is protected by clp->cl_lock
> nfsd: move unhash_client_locked call into mark_client_expired_locked
> nfsd: fix misleading comment
> nfsd: don't destroy client if mark_client_expired_locked fails
> nfsd: don't destroy clients that are busy
> nfsd: abstract out the get and set routines into the fault injection
> ops
> nfsd: add a forget_clients "get" routine with proper locking
> nfsd: add a forget_client set_clnt routine
> nfsd: add nfsd_inject_forget_clients
> nfsd: add a list_head arg to nfsd_foreach_client_lock
> nfsd: add more granular locking to forget_locks fault injector
> nfsd: add more granular locking to forget_openowners fault injector
> nfsd: add more granular locking to *_delegations fault injectors
> nfsd: remove old fault injection infrastructure
> nfsd: remove nfs4_lock_state: nfs4_laundromat
> nfsd: remove nfs4_lock_state: nfs4_state_shutdown_net
> nfsd: remove the client_mutex and the nfs4_lock/unlock_state wrappers
> nfsd: add file documenting new state object model
>
> Trond Myklebust (63):
> nfsd: Protect addition to the file_hashtbl
> NFSd: Avoid taking state_lock while holding inode lock in
> nfsd_break_one_deleg
> NFSd: nfs4_preprocess_seqid_op should only set *stpp on success
> NFSd: Add fine grained protection for the nfs4_file->fi_stateids list
> NFSd: Add a mutex to protect the NFSv4.0 open owner replay cache
> NFSd: Add locking to the nfs4_file->fi_fds[] array
> NFSd: Lock owners are not per open stateid
> NFSd: Clean up helper __release_lock_stateid
> NFSd: NFSv4 lock-owners are not associated to a specific file
> NFSd: Cleanup nfs4svc_encode_compoundres
> NFSd: Don't get a session reference without a client reference
> NFSd: Allow struct nfsd4_compound_state to cache the nfs4_client
> NFSd: Move the delegation reference counter into the struct nfs4_stid
> NFSd: Simplify stateid management
> NFSd: Fix delegation revocation
> NFSd: Add reference counting to the lock and open stateids
> NFSd: clean up nfsd4_close_open_stateid
> 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: Ensure stateids remain unique until they are freed
> NFSd: Convert delegation counter to an atomic_long_t type
> NFSd: Slight cleanup of find_stateid()
> NFSd: Add reference counting to 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: 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: Cleanup - Let nfsd4_lookup_stateid() take a cstate argument
> NFSd: Cache the client that was looked up in lookup_clientid()
> NFSd: Convert nfsd4_process_open1() to work with lookup_clientid()
> NFSd: Always use lookup_clientid() in nfsd4_process_open1
> NFSd: Move the open owner hash table into struct nfs4_client
> NFSd: Convert nfs4_check_open_reclaim() to work with lookup_clientid()
> NFSd: Ensure struct nfs4_client is unhashed before we try to destroy
> it
> NFSd: Ensure that the laundromat unhashes the client before releasing
> locks
> NFSd: Don't require client_lock in free_client
> NFSd: Move create_client() call outside the lock
> NFSd: Protect unconfirmed client creation using client_lock
> NFSd: Protect nfsd4_destroy_clientid using client_lock
> NFSd: Ensure lookup_clientid() takes client_lock
> NFSd: Add assertions to document the nfs4_client/session locking
> NFSd: Remove nfs4_lock_state(): nfs4_preprocess_stateid_op()
> NFSd: Remove nfs4_lock_state(): nfsd4_test_stateid/nfsd4_free_stateid
> NFSd: Remove nfs4_lock_state(): nfsd4_release_lockowner
> NFSd: Remove nfs4_lock_state(): nfsd4_lock/locku/lockt()
> NFSd: Remove nfs4_lock_state(): nfsd4_open_downgrade + nfsd4_close
> NFSd: Remove nfs4_lock_state(): nfsd4_delegreturn()
> NFSd: Remove nfs4_lock_state(): nfsd4_open and nfsd4_open_confirm
> NFSd: Remove nfs4_lock_state(): exchange_id, create/destroy_session()
> NFSd: Remove nfs4_lock_state(): setclientid, setclientid_confirm,
> renew
> NFSd: Remove nfs4_lock_state(): reclaim_complete()
>
> .../filesystems/nfs/nfsd4-state-objects.txt | 110 +
> fs/locks.c | 26 +
> fs/nfsd/fault_inject.c | 129 +-
> fs/nfsd/netns.h | 5 -
> fs/nfsd/nfs4callback.c | 18 +-
> fs/nfsd/nfs4proc.c | 16 +-
> fs/nfsd/nfs4state.c | 2569 ++++++++++++++------
> fs/nfsd/nfs4xdr.c | 17 +-
> fs/nfsd/state.h | 86 +-
> fs/nfsd/xdr4.h | 8 +-
> include/linux/fs.h | 6 +
> include/linux/lockdep.h | 6 +
> 12 files changed, 2052 insertions(+), 944 deletions(-)
> create mode 100644 Documentation/filesystems/nfs/nfsd4-state-objects.txt
>

Oh, I should also mention that this set is also available in my
nfsd-devel branch at:

git://git.samba.org/jlayton/linux.git

I'll continue to push changes to the set to that branch as review comes
in.

Thanks!
--
Jeff Layton <[email protected]>

2014-06-19 14:51:11

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 009/104] locks: add file_has_lease

Add a function that can tell us whether a file description has had a
lease set on it.

Signed-off-by: Jeff Layton <[email protected]>
---
fs/locks.c | 26 ++++++++++++++++++++++++++
include/linux/fs.h | 6 ++++++
2 files changed, 32 insertions(+)

diff --git a/fs/locks.c b/fs/locks.c
index da57c9b7e844..402169f95502 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -1308,6 +1308,32 @@ static bool leases_conflict(struct file_lock *lease, struct file_lock *breaker)
}

/**
+ * file_has_lease - does the given file have a lease set on it?
+ * @file: struct file on which we want to check the lease
+ *
+ * Returns true if a lease was is set on the given file description,
+ * false otherwise.
+ */
+bool
+file_has_lease(struct file *file)
+{
+ bool ret = false;
+ struct inode *inode = file_inode(file);
+ struct file_lock *fl;
+
+ spin_lock(&inode->i_lock);
+ for (fl = inode->i_flock; fl && IS_LEASE(fl); fl = fl->fl_next) {
+ if (fl->fl_file == file) {
+ ret = true;
+ break;
+ }
+ }
+ spin_unlock(&inode->i_lock);
+ return ret;
+}
+EXPORT_SYMBOL(file_has_lease);
+
+/**
* __break_lease - revoke all outstanding leases on file
* @inode: the inode of the file to return
* @mode: O_RDONLY: break only write leases; O_WRONLY or O_RDWR:
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 338e6f758c6d..7937523c21ca 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -953,6 +953,7 @@ extern int vfs_test_lock(struct file *, struct file_lock *);
extern int vfs_lock_file(struct file *, unsigned int, struct file_lock *, struct file_lock *);
extern int vfs_cancel_lock(struct file *filp, struct file_lock *fl);
extern int flock_lock_file_wait(struct file *filp, struct file_lock *fl);
+extern bool file_has_lease(struct file *file);
extern int __break_lease(struct inode *inode, unsigned int flags, unsigned int type);
extern void lease_get_mtime(struct inode *, struct timespec *time);
extern int generic_setlease(struct file *, long, struct file_lock **);
@@ -1064,6 +1065,11 @@ static inline int flock_lock_file_wait(struct file *filp,
return -ENOLCK;
}

+static inline bool file_has_lease(struct file *file)
+{
+ return false;
+}
+
static inline int __break_lease(struct inode *inode, unsigned int mode, unsigned int type)
{
return 0;
--
1.9.3


2014-06-23 16:25:01

by Jeff Layton

[permalink] [raw]
Subject: Re: [PATCH v1 005/104] NFSd: Add fine grained protection for the nfs4_file->fi_stateids list

On Mon, 23 Jun 2014 09:23:32 -0700
Christoph Hellwig <[email protected]> wrote:

> On Mon, Jun 23, 2014 at 12:20:39PM -0400, Jeff Layton wrote:
> > Correct. I'll add that into the desciption if you think it's warranted,
> > but again that's the case with many of the patches in this series.
>
> It might be fairly obvious in the context of the series, it's not
> if someone goes back in history with a git-blame.
>
> And it's not a 100% obvious with the series either, at least earlier
> versions also fixes pre-existing races, although all of that might be
> upstream now.
>
> So as far as I am concerned a simple one-line blurb mentioning what
> the new locking is for would be useful to be added to all patches
> just changing the locking.

Fair enough. I'll do that.

Thanks,
--
Jeff Layton <[email protected]>

2014-06-19 14:51:26

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 019/104] NFSd: Simplify stateid management

From: Trond Myklebust <[email protected]>

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

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 427efcdb81e6..6f9a0d2d0223 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -447,7 +447,7 @@ kmem_cache *slab)
struct nfs4_stid *stid;
int new_id;

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

@@ -459,11 +459,9 @@ kmem_cache *slab)
if (new_id < 0)
goto out_free;
stid->sc_client = cl;
- stid->sc_type = 0;
stid->sc_stateid.si_opaque.so_id = new_id;
stid->sc_stateid.si_opaque.so_clid = cl->cl_clientid;
/* Will be incremented before return to client: */
- stid->sc_stateid.si_generation = 0;
atomic_set(&stid->sc_count, 1);

/*
@@ -507,10 +505,8 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv
INIT_LIST_HEAD(&dp->dl_perfile);
INIT_LIST_HEAD(&dp->dl_perclnt);
INIT_LIST_HEAD(&dp->dl_recall_lru);
- dp->dl_file = NULL;
dp->dl_type = NFS4_OPEN_DELEGATE_READ;
fh_copy_shallow(&dp->dl_fh, &current_fh->fh_handle);
- dp->dl_time = 0;
nfsd4_init_callback(&dp->dl_recall);
return dp;
}
@@ -533,6 +529,8 @@ void
nfs4_put_delegation(struct nfs4_delegation *dp)
{
if (atomic_dec_and_test(&dp->dl_stid.sc_count)) {
+ if (dp->dl_file)
+ put_nfs4_file(dp->dl_file);
nfs4_free_stid(deleg_slab, &dp->dl_stid);
num_delegations--;
}
@@ -580,12 +578,9 @@ unhash_delegation(struct nfs4_delegation *dp)
list_del_init(&dp->dl_recall_lru);
list_del_init(&dp->dl_perfile);
spin_unlock(&fp->fi_lock);
- if (fp) {
+ if (fp)
nfs4_put_deleg_lease(fp);
- dp->dl_file = NULL;
- }
spin_unlock(&state_lock);
- put_nfs4_file(fp);
}


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

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

@@ -4329,8 +4324,13 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,

if (cstate->minorversion)
free_generic_stateid(stp);
- else
+ else {
+ if (stp->st_file) {
+ put_nfs4_file(stp->st_file);
+ stp->st_file = NULL;
+ }
oo->oo_last_closed_stid = stp;
+ }

if (list_empty(&oo->oo_owner.so_stateids)) {
if (cstate->minorversion)
--
1.9.3


2014-06-19 14:52:35

by Jeff Layton

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

From: Trond Myklebust <[email protected]>

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

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index dd436ebf1123..9d6cc32fd2fb 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1399,12 +1399,23 @@ free_client(struct nfs4_client *clp)
}

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

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

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

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

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


2014-06-19 14:51:43

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 032/104] 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 | 26 +++++++++++++++++---------
1 file changed, 17 insertions(+), 9 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 020bc981ccd3..42ef5ecc1def 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1561,28 +1561,36 @@ static void gen_confirm(struct nfs4_client *clp)
memcpy(clp->cl_confirm.data, verf, sizeof(clp->cl_confirm.data));
}

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

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

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

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

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


2014-06-19 14:51:31

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 023/104] 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 | 18 +++++++++---------
fs/nfsd/state.h | 2 +-
2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 2af6cd2564e7..068347ab4dd0 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -522,6 +522,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);
}

@@ -529,8 +531,6 @@ void
nfs4_put_delegation(struct nfs4_delegation *dp)
{
if (atomic_dec_and_test(&dp->dl_stid.sc_count)) {
- if (dp->dl_file)
- put_nfs4_file(dp->dl_file);
nfs4_free_stid(deleg_slab, &dp->dl_stid);
num_delegations--;
}
@@ -569,7 +569,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);

@@ -2969,8 +2969,8 @@ out:

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

/*
* We can't do this in nfsd_break_deleg_cb because it is
@@ -3301,14 +3301,14 @@ static struct file_lock *nfs4_alloc_init_lease(struct nfs4_delegation *dp, int f
fl->fl_flags = FL_DELEG;
fl->fl_type = flag == NFS4_OPEN_DELEGATE_READ? F_RDLCK: F_WRLCK;
fl->fl_end = OFFSET_MAX;
- fl->fl_owner = (fl_owner_t)(dp->dl_file);
+ fl->fl_owner = (fl_owner_t)(dp->dl_stid.sc_file);
fl->fl_pid = current->tgid;
return fl;
}

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

@@ -3358,7 +3358,7 @@ static int nfs4_set_delegation(struct nfs4_delegation *dp, struct nfs4_file *fp)
get_nfs4_file(fp);
spin_lock(&state_lock);
spin_lock(&fp->fi_lock);
- dp->dl_file = fp;
+ dp->dl_stid.sc_file = fp;
if (!fp->fi_lease) {
spin_unlock(&fp->fi_lock);
spin_unlock(&state_lock);
@@ -3961,7 +3961,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 99d000b0b95e..eaddef9048e4 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -84,6 +84,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 {
@@ -91,7 +92,6 @@ struct nfs4_delegation {
struct list_head dl_perfile;
struct list_head dl_perclnt;
struct list_head dl_recall_lru; /* delegation recalled */
- struct nfs4_file *dl_file;
u32 dl_type;
time_t dl_time;
/* For recall: */
--
1.9.3


2014-06-19 14:53:20

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 099/104] NFSd: Remove nfs4_lock_state(): setclientid, setclientid_confirm, renew

From: Trond Myklebust <[email protected]>

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index d91247f86d6b..e159e316ecdb 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -2783,7 +2783,6 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (new == NULL)
return nfserr_jukebox;
/* Cases below refer to rfc 3530 section 14.2.33: */
- nfs4_lock_state();
spin_lock(&nn->client_lock);
conf = find_confirmed_client_by_name(&clname, nn);
if (conf) {
@@ -2818,7 +2817,6 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
status = nfs_ok;
out:
spin_unlock(&nn->client_lock);
- nfs4_unlock_state();
if (new)
free_client(new);
if (unconf)
@@ -2841,7 +2839,6 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,

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

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

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

- nfs4_lock_state();
dprintk("process_renew(%08x/%08x): starting\n",
clid->cl_boot, clid->cl_id);
status = lookup_clientid(clid, cstate, nn);
@@ -3961,7 +3956,6 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
goto out;
status = nfs_ok;
out:
- nfs4_unlock_state();
return status;
}

--
1.9.3


2014-06-19 14:53:25

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 103/104] nfsd: remove the client_mutex and the nfs4_lock/unlock_state wrappers

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index a37e9d2aa047..e3df298b7aee 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -82,9 +82,6 @@ static __be32 lookup_clientid(clientid_t *clid,

/* Locking: */

-/* Currently used for almost all code touching nfsv4 state: */
-static DEFINE_MUTEX(client_mutex);
-
/*
* Currently used for the del_recall_lru and file hash table. In an
* effort to decrease the scope of the client_mutex, this spinlock may
@@ -104,12 +101,6 @@ static struct kmem_cache *file_slab;
static struct kmem_cache *stateid_slab;
static struct kmem_cache *deleg_slab;

-void
-nfs4_lock_state(void)
-{
- mutex_lock(&client_mutex);
-}
-
static void free_session(struct nfsd4_session *);

static bool is_session_dead(struct nfsd4_session *ses)
@@ -125,12 +116,6 @@ static __be32 mark_session_dead_locked(struct nfsd4_session *ses, int ref_held_b
return nfs_ok;
}

-void
-nfs4_unlock_state(void)
-{
- mutex_unlock(&client_mutex);
-}
-
static bool is_client_expired(struct nfs4_client *clp)
{
return clp->cl_time == 0;
--
1.9.3


2014-06-19 14:51:49

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 036/104] 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.

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 08425b1bc3b1..cf3b19bb5839 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -589,6 +589,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);
@@ -3513,6 +3514,7 @@ nfs4_open_delegation(struct net *net, struct svc_fh *fh,
dprintk("NFSD: delegation stateid=" STATEID_FMT "\n",
STATEID_VAL(&dp->dl_stid.sc_stateid));
open->op_delegate_type = NFS4_OPEN_DELEGATE_READ;
+ nfs4_put_delegation(dp);
return;
out_free:
unhash_and_destroy_delegation(dp);
--
1.9.3


2014-06-19 14:53:13

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 095/104] NFSd: Remove nfs4_lock_state(): nfsd4_open_downgrade + nfsd4_close

From: Trond Myklebust <[email protected]>

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

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

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

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

- nfs4_lock_state();
status = nfs4_preprocess_seqid_op(cstate, close->cl_seqid,
&close->cl_stateid,
NFS4_OPEN_STID|NFS4_CLOSED_STID,
@@ -4709,7 +4706,6 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
/* put reference from nfs4_preprocess_seqid_op */
put_generic_stateid(stp);
out:
- nfs4_unlock_state();
return status;
}

--
1.9.3


2014-06-24 12:00:44

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH v1 015/104] NFSd: Cleanup nfs4svc_encode_compoundres

This seems like another candidate for the prep series.

Looks good,

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

2014-06-23 17:14:33

by Jeff Layton

[permalink] [raw]
Subject: Re: [PATCH v1 007/104] NFSd: Add locking to the nfs4_file->fi_fds[] array

On Mon, 23 Jun 2014 09:06:36 -0700
Christoph Hellwig <[email protected]> wrote:

> Missed the why the locking is added.
>
> Also I pointed out a way to better factor this code in reply to one
> of Tronds previous postings of this one.
>

I saw your comments and even did some experimentation to that end.
After playing around with some patches that tried to use FMODE flags
instead, I tossed them out. It may have consolidated the code a bit, but
the result was just too difficult to follow. I think it's better to go
with clearer code here, even if it's a little more verbose.

--
Jeff Layton <[email protected]>

2014-06-19 14:51:45

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 033/104] NFSd: Add reference counting to find_stateid

From: Trond Myklebust <[email protected]>

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

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 42ef5ecc1def..019f07a78b73 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1577,6 +1577,8 @@ static struct nfs4_stid *find_stateid(struct nfs4_client *cl, stateid_t *t)

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

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


2014-06-19 14:53:18

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 098/104] NFSd: Remove nfs4_lock_state(): exchange_id, create/destroy_session()

From: Trond Myklebust <[email protected]>

Also destroy_clientid and bind_conn_to_session.

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 0dfe24e0aa61..d91247f86d6b 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -2044,7 +2044,6 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
return nfserr_jukebox;

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

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

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

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

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

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

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

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


2014-06-19 14:53:02

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 087/104] nfsd: add more granular locking to forget_locks fault injector

Signed-off-by: Jeff Layton <[email protected]>
---
fs/nfsd/fault_inject.c | 8 ++--
fs/nfsd/nfs4state.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++---
fs/nfsd/state.h | 6 ++-
3 files changed, 117 insertions(+), 14 deletions(-)

diff --git a/fs/nfsd/fault_inject.c b/fs/nfsd/fault_inject.c
index d7c79e984d16..675594cd316c 100644
--- a/fs/nfsd/fault_inject.c
+++ b/fs/nfsd/fault_inject.c
@@ -139,11 +139,9 @@ static struct nfsd_fault_inject_op inject_ops[] = {
},
{
.file = "forget_locks",
- .get = nfsd_inject_get,
- .set_val = nfsd_inject_set,
- .set_clnt = nfsd_inject_set_client,
- .forget = nfsd_forget_client_locks,
- .print = nfsd_print_client_locks,
+ .get = nfsd_inject_print_locks,
+ .set_val = nfsd_inject_forget_locks,
+ .set_clnt = nfsd_inject_forget_client_locks,
},
{
.file = "forget_openowners",
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 61102b93902c..af726e934ea9 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -209,6 +209,16 @@ static void put_client_renew(struct nfs4_client *clp)
spin_unlock(&nn->client_lock);
}

+static void
+put_client(struct nfs4_client *clp)
+{
+ struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
+
+ spin_lock(&nn->client_lock);
+ atomic_dec(&clp->cl_refcount);
+ spin_unlock(&nn->client_lock);
+}
+
static __be32 nfsd4_get_session_locked(struct nfsd4_session *ses)
{
__be32 status;
@@ -5661,37 +5671,130 @@ static u64 nfsd_foreach_client_lock(struct nfs4_client *clp, u64 max,
struct nfs4_openowner *oop;
struct nfs4_ol_stateid *stp, *st_next;
struct nfs4_ol_stateid *lst, *lst_next;
+ struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, nfsd_net_id);
u64 count = 0;

+ spin_lock(&clp->cl_lock);
list_for_each_entry(oop, &clp->cl_openowners, oo_perclient) {
list_for_each_entry_safe(stp, st_next, &oop->oo_owner.so_stateids, st_perstateowner) {
list_for_each_entry_safe(lst, lst_next, &stp->st_locks, st_locks) {
if (func) {
func(lst);
- if (collect)
+ if (collect) {
+ lockdep_assert_held(&nn->client_lock);
+ atomic_inc(&clp->cl_refcount);
list_add(&lst->st_locks, collect);
+ }
}
- if (++count == max)
- return count;
+ ++count;
+ /*
+ * Despite the fact that these functions deal
+ * with 64-bit integers for "count", we must
+ * ensure that it doesn't blow up the
+ * clp->cl_refcount. Throw a warning if we
+ * start to approach INT_MAX here.
+ */
+ WARN_ON_ONCE(count == (INT_MAX / 2));
+ if (count == max)
+ goto out;
}
}
}
+out:
+ spin_unlock(&clp->cl_lock);

return count;
}

-u64 nfsd_forget_client_locks(struct nfs4_client *clp, u64 max)
+static u64
+nfsd_collect_client_locks(struct nfs4_client *clp, struct list_head *collect, u64 max)
{
- return nfsd_foreach_client_lock(clp, max, NULL, release_lock_stateid);
+ return nfsd_foreach_client_lock(clp, max, collect, unhash_lock_stateid);
}

-u64 nfsd_print_client_locks(struct nfs4_client *clp, u64 max)
+static u64
+nfsd_print_client_locks(struct nfs4_client *clp)
{
- u64 count = nfsd_foreach_client_lock(clp, max, NULL, NULL);
+ u64 count = nfsd_foreach_client_lock(clp, 0, NULL, NULL);
nfsd_print_count(clp, count, "locked files");
return count;
}

+u64
+nfsd_inject_print_locks(struct nfsd_fault_inject_op *op)
+{
+ struct nfs4_client *clp;
+ u64 count = 0;
+ struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, nfsd_net_id);
+
+ if (!nfsd_netns_ready(nn))
+ return 0;
+
+ spin_lock(&nn->client_lock);
+ list_for_each_entry(clp, &nn->client_lru, cl_lru)
+ count += nfsd_print_client_locks(clp);
+ spin_unlock(&nn->client_lock);
+
+ return count;
+}
+
+static void
+nfsd_reap_locks(struct list_head *reaplist)
+{
+ struct nfs4_client *clp;
+ struct nfs4_ol_stateid *stp, *next;
+
+ list_for_each_entry_safe(stp, next, reaplist, st_locks) {
+ list_del_init(&stp->st_locks);
+ clp = stp->st_stid.sc_client;
+ put_generic_stateid(stp);
+ put_client(clp);
+ }
+}
+
+u64
+nfsd_inject_forget_client_locks(struct nfsd_fault_inject_op *op,
+ struct sockaddr_storage *addr, size_t addr_size)
+{
+ unsigned int count = 0;
+ struct nfs4_client *clp;
+ struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, nfsd_net_id);
+ LIST_HEAD(reaplist);
+
+ if (!nfsd_netns_ready(nn))
+ return count;
+
+ spin_lock(&nn->client_lock);
+ clp = nfsd_find_client(addr, addr_size);
+ if (clp)
+ count = nfsd_collect_client_locks(clp, &reaplist, 0);
+ spin_unlock(&nn->client_lock);
+ nfsd_reap_locks(&reaplist);
+ return count;
+}
+
+u64
+nfsd_inject_forget_locks(struct nfsd_fault_inject_op *op, u64 max)
+{
+ u64 count = 0;
+ struct nfs4_client *clp;
+ struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, nfsd_net_id);
+ LIST_HEAD(reaplist);
+
+ if (!nfsd_netns_ready(nn))
+ return count;
+
+ spin_lock(&nn->client_lock);
+ list_for_each_entry(clp, &nn->client_lru, cl_lru) {
+ count += nfsd_collect_client_locks(clp, &reaplist, max - count);
+ if (max != 0 && count >= max)
+ break;
+ }
+ spin_unlock(&nn->client_lock);
+ nfsd_reap_locks(&reaplist);
+ return count;
+}
+
static u64 nfsd_foreach_client_open(struct nfs4_client *clp, u64 max, void (*func)(struct nfs4_openowner *))
{
struct nfs4_openowner *oop, *next;
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 0c4247b2bf8c..fdac0c19cb3c 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -478,12 +478,14 @@ u64 nfsd_inject_print_clients(struct nfsd_fault_inject_op *op);
u64 nfsd_inject_forget_client(struct nfsd_fault_inject_op *, struct sockaddr_storage *, size_t);
u64 nfsd_inject_forget_clients(struct nfsd_fault_inject_op *, u64);

-u64 nfsd_forget_client_locks(struct nfs4_client*, u64);
+u64 nfsd_inject_print_locks(struct nfsd_fault_inject_op *);
+u64 nfsd_inject_forget_client_locks(struct nfsd_fault_inject_op *, struct sockaddr_storage *, size_t);
+u64 nfsd_inject_forget_locks(struct nfsd_fault_inject_op *, u64);
+
u64 nfsd_forget_client_openowners(struct nfs4_client *, u64);
u64 nfsd_forget_client_delegations(struct nfs4_client *, u64);
u64 nfsd_recall_client_delegations(struct nfs4_client *, u64);

-u64 nfsd_print_client_locks(struct nfs4_client *, u64);
u64 nfsd_print_client_openowners(struct nfs4_client *, u64);
u64 nfsd_print_client_delegations(struct nfs4_client *, u64);
#else /* CONFIG_NFSD_FAULT_INJECTION */
--
1.9.3


2014-06-19 14:52:33

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 065/104] nfsd: reduce cl_lock trashing in release_openowner

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 62af2431f118..7bb7dd19e275 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -899,16 +899,10 @@ 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_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)
-{
- unhash_open_stateid(stp);
- put_generic_stateid(stp);
}

static void unhash_openowner_locked(struct nfs4_openowner *oo)
@@ -932,30 +926,36 @@ 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 the persistent reference to the stateid. If it's
+ * the last reference, then put it onto the reaplist
+ * for later destruction.
+ */
+ 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_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);
+ 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);
+ }
release_last_closed_stateid(oo);
nfs4_put_stateowner(&oo->oo_owner);
}
@@ -4554,7 +4554,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);

/*
* We can't safely clear the sc_file pointer while there are still
--
1.9.3


2014-06-19 14:52:33

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 066/104] NFSd: Convert nfs4_check_open_reclaim() to work with lookup_clientid()

From: Trond Myklebust <[email protected]>

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

diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 72e673781e6b..833c753e4775 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -431,8 +431,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
break;
case NFS4_OPEN_CLAIM_PREVIOUS:
status = nfs4_check_open_reclaim(&open->op_clientid,
- cstate->minorversion,
- nn);
+ cstate, nn);
if (status)
goto out;
open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED;
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 7bb7dd19e275..dd436ebf1123 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -5446,16 +5446,19 @@ nfsd4_find_reclaim_client(const char *recdir, struct nfsd_net *nn)
* Called from OPEN. Look for clientid in reclaim list.
*/
__be32
-nfs4_check_open_reclaim(clientid_t *clid, bool sessions, struct nfsd_net *nn)
+nfs4_check_open_reclaim(clientid_t *clid,
+ struct nfsd4_compound_state *cstate,
+ struct nfsd_net *nn)
{
- struct nfs4_client *clp;
+ __be32 status;

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

- return nfsd4_client_record_check(clp) ? nfserr_reclaim_bad : nfs_ok;
+ return nfsd4_client_record_check(cstate->clp) ?
+ nfserr_reclaim_bad : nfs_ok;
}

#ifdef CONFIG_NFSD_FAULT_INJECTION
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 747ab44b806a..880b5acd05f3 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -440,7 +440,8 @@ void nfs4_remove_reclaim_record(struct nfs4_client_reclaim *, struct nfsd_net *)
extern void nfs4_release_reclaim(struct nfsd_net *);
extern struct nfs4_client_reclaim *nfsd4_find_reclaim_client(const char *recdir,
struct nfsd_net *nn);
-extern __be32 nfs4_check_open_reclaim(clientid_t *clid, bool sessions, struct nfsd_net *nn);
+extern __be32 nfs4_check_open_reclaim(clientid_t *clid,
+ struct nfsd4_compound_state *cstate, struct nfsd_net *nn);
extern int set_callback_cred(void);
extern void nfsd4_init_callback(struct nfsd4_callback *);
extern void nfsd4_probe_callback(struct nfs4_client *clp);
--
1.9.3


2014-06-19 14:53:21

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 100/104] NFSd: Remove nfs4_lock_state(): reclaim_complete()

From: Trond Myklebust <[email protected]>

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index e159e316ecdb..6000da3285d7 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -2744,7 +2744,6 @@ nfsd4_reclaim_complete(struct svc_rqst *rqstp, struct nfsd4_compound_state *csta
return nfs_ok;
}

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

--
1.9.3


2014-06-19 14:52:38

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 069/104] NFSd: Don't require client_lock in free_client

From: Trond Myklebust <[email protected]>

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

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 7982f44aae83..2b3897f313b8 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1225,9 +1225,6 @@ static void __free_session(struct nfsd4_session *ses)

static void free_session(struct nfsd4_session *ses)
{
- struct nfsd_net *nn = net_generic(ses->se_client->net, nfsd_net_id);
-
- lockdep_assert_held(&nn->client_lock);
nfsd4_del_conns(ses);
nfsd4_put_drc_mem(&ses->se_fchannel);
__free_session(ses);
@@ -1377,9 +1374,6 @@ err_no_name:
static void
free_client(struct nfs4_client *clp)
{
- struct nfsd_net __maybe_unused *nn = net_generic(clp->net, nfsd_net_id);
-
- lockdep_assert_held(&nn->client_lock);
while (!list_empty(&clp->cl_sessions)) {
struct nfsd4_session *ses;
ses = list_entry(clp->cl_sessions.next, struct nfsd4_session,
@@ -1438,7 +1432,6 @@ __destroy_client(struct nfs4_client *clp)
struct nfs4_openowner *oo;
struct nfs4_delegation *dp;
struct list_head reaplist;
- struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);

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

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

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

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


2014-06-24 12:04:28

by Jeff Layton

[permalink] [raw]
Subject: Re: [PATCH v1 011/104] NFSd: Lock owners are not per open stateid

On Tue, 24 Jun 2014 04:50:58 -0700
Christoph Hellwig <[email protected]> wrote:

> FYI, this is what I think should go into a separate inintial series.
>
> Looks good,
>
> Reviewed-by: Christoph Hellwig <[email protected]>

Yeah, I was thinking about that last night. I think the parts that could
be broken out are:

- the change to multiple stateids per lockowner
- moving some of the fields into nfs4_stid
- adding refcounting for stateids and stateowners and fixing the
relationships between them

Unfortunately, that means a pretty significant respin of the set as a
lot of the changes now are mashed in with those that add locking around
those parts. I'll give it some more thought...
--
Jeff Layton <[email protected]>

2014-06-26 01:34:55

by Jeff Layton

[permalink] [raw]
Subject: Re: [PATCH v1 000/104] nfsd: eliminate the client_mutex

On Wed, 25 Jun 2014 21:26:29 -0400
"J. Bruce Fields" <[email protected]> wrote:

> I'm seeing this, by the way.--b.
>
> [ 512.960652] nfsd: non-standard errno: -7
> [ 650.442646] ------------[ cut here ]------------
> [ 650.442754] WARNING: CPU: 3 PID: 647 at fs/nfsd/nfs4state.c:761
> revoke_delegation+0xa7/0xb0 [nfsd]()
> [ 650.442760] Modules linked in: rpcsec_gss_krb5 nfsd auth_rpcgss
> oid_registry nfs_acl lockd sunrpc
> [ 650.442792] CPU: 3 PID: 647 Comm: kworker/u8:2 Not tainted
> 3.16.0-rc2-00479-gb675b9c #2982
> [ 650.442796] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011
> [ 650.442814] Workqueue: nfsd4 laundromat_main [nfsd]
> [ 650.442821] 0000000000000009 ffff880036287c48 ffffffff81a439d6
> 0000000000000001
> [ 650.442836] 0000000000000000 ffff880036287c88 ffffffff8106d65c
> ffff880036287c68
> [ 650.442851] ffff8800312aee60 ffff88001bcfe800 ffff8800312aeeb0
> ffffffff822c7cc0
> [ 650.442865] Call Trace:
> [ 650.442878] [<ffffffff81a439d6>] dump_stack+0x4f/0x7c
> [ 650.442889] [<ffffffff8106d65c>] warn_slowpath_common+0x8c/0xc0
> [ 650.442897] [<ffffffff8106d6aa>] warn_slowpath_null+0x1a/0x20
> [ 650.442917] [<ffffffffa011a607>] revoke_delegation+0xa7/0xb0 [nfsd]
> [ 650.442936] [<ffffffffa011aaec>] laundromat_main+0x2dc/0x480 [nfsd]
> [ 650.442956] [<ffffffffa011a9f4>] ? laundromat_main+0x1e4/0x480
> [nfsd]
> [ 650.442967] [<ffffffff8108924e>] process_one_work+0x1ce/0x500
> [ 650.442976] [<ffffffff810891db>] ? process_one_work+0x15b/0x500
> [ 650.442985] [<ffffffff81089b9b>] worker_thread+0x11b/0x4f0
> [ 650.442995] [<ffffffff810bba2d>] ? trace_hardirqs_on+0xd/0x10
> [ 650.443004] [<ffffffff81a4c36b>] ?
> _raw_spin_unlock_irqrestore+0x4b/0x80
> [ 650.443013] [<ffffffff81089a80>] ? init_pwq+0x190/0x190
> [ 650.443022] [<ffffffff81090ac4>] kthread+0xe4/0x100
> [ 650.443032] [<ffffffff810909e0>] ? __init_kthread_worker+0x70/0x70
> [ 650.443041] [<ffffffff81a4caac>] ret_from_fork+0x7c/0xb0
> [ 650.443050] [<ffffffff810909e0>] ? __init_kthread_worker+0x70/0x70
> [ 650.443055] ---[ end trace e3faa7f0516143d3 ]---

Thanks, I think I just whacked that bug a little while ago:

http://git.samba.org/?p=jlayton/linux.git;a=commitdiff;h=81771b60b6fd17dae7975a2e3199fc37d181dacf

...I've squashed quite a few others in the last few days as well. I'm
hoping to post an updated set in the next day or so.

--
Jeff Layton <[email protected]>

2014-06-19 14:51:24

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 018/104] NFSd: Move the delegation reference counter into the struct nfs4_stid

From: Trond Myklebust <[email protected]>

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

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

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

/*
* It shouldn't be a problem to reuse an opaque stateid value.
@@ -510,7 +511,6 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv
dp->dl_type = NFS4_OPEN_DELEGATE_READ;
fh_copy_shallow(&dp->dl_fh, &current_fh->fh_handle);
dp->dl_time = 0;
- atomic_set(&dp->dl_count, 1);
nfsd4_init_callback(&dp->dl_recall);
return dp;
}
@@ -532,7 +532,7 @@ static void nfs4_free_stid(struct kmem_cache *slab, struct nfs4_stid *s)
void
nfs4_put_delegation(struct nfs4_delegation *dp)
{
- if (atomic_dec_and_test(&dp->dl_count)) {
+ if (atomic_dec_and_test(&dp->dl_stid.sc_count)) {
nfs4_free_stid(deleg_slab, &dp->dl_stid);
num_delegations--;
}
@@ -2982,10 +2982,11 @@ static void nfsd_break_one_deleg(struct nfs4_delegation *dp)
{
/* We're assuming the state code never drops its reference
* without first removing the lease. Since we're in this lease
- * callback (and since the lease code is serialized by the kernel
- * lock) we know the server hasn't removed the lease yet, we know
- * it's safe to take a reference: */
- atomic_inc(&dp->dl_count);
+ * callback (and since the lease code is serialized by the i_lock
+ * we know the server hasn't removed the lease yet, we know it's
+ * safe to take a reference.
+ */
+ atomic_inc(&dp->dl_stid.sc_count);
nfsd4_cb_recall(dp);
}

diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index d928c444f91e..99d000b0b95e 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -73,6 +73,7 @@ struct nfsd4_callback {
};

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


2014-06-24 12:23:52

by Jeff Layton

[permalink] [raw]
Subject: Re: [PATCH v1 104/104] nfsd: add file documenting new state object model

On Tue, 24 Jun 2014 05:11:58 -0700
Christoph Hellwig <[email protected]> wrote:

> > +KNFSD4 State Object Model:
> > +==========================
> > +Written 2014 by Jeff Layton <[email protected]>
> > +
> > +Introduction:
> > +-------------
> > +Until recently, knfsd relied heavily on a global mutex to ensure that objects
> > +didn't disappear while they were being operated on. That has proven to be a
> > +scalability bottleneck however, so the code has been overhauled to make heavy
> > +use of reference counting and spinlocks for tracking the different objects.
>
> It's hard enough to keep documents uptodate with the current version of
> the code, so I don't think this historical blurb buys us anything.
>

OK

> > +struct nfs4_client:
> > +-------------------
> > +The initial object created by an NFS client using SETCLIENTID (for NFSv4.0) or
> > +EXCHANGE_ID (for NFSv4.1+). These objects are refcounted and timestamped. Each
> > +nfsd_net_ns object contains a set of these and they are tracked via short and
> > +long form clientid. They are hashed and searched for under the per-nfsd-net
> > +client_lock spinlock.
> > +
> > +The lifecycle of these is a little strange. References to it are only held
> > +during the processing of compounds, and in certain other operations. In their
> > +"resting state" they have a refcount of 0. If they are not renewed within a
> > +lease period, they become eligible for destruction by the laundromat.
>
> That's the normal lifecycle of objects on a lru, so I'd strike the
> "strange" part.
>

Fair enough.

> > +strict nfs4_stid:
> > +-----------------
> > +A core object that represents a "generic" stateid. These are generally embedded
> > +within the different (more specific) stateid objects and contain fields that
> > +are of general use to any stateid.
>
> Maybe replace "generic" stateid with common stateid or similar? The
> generic_stateid term is unfortunately is used in the code in a weird way
> for open/lock stateids. Or better yet fix up those names in the code..
>

Yeah, I didn't make that change here as we have enough "churn" going
on, but maybe it's a good idea to just do it. "generic" really ought to
refer to nfs4_stid, and we should rename the nfs4_ol_stateid to
something besides "generic".

>
> Given how documentation outside the source code gets out of data
> easily maybe you should move these texts to comments above each of the
> structures?

Yeah, probably so. This document started for my own benefit as I was
going through the code and documenting things. We can drop it if it's
not helpful but I figured I'd toss it on top of the pile in case it was.
--
Jeff Layton <[email protected]>

2014-06-23 16:02:28

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH v1 004/104] nfsd4: use cl_lock to synchronize all stateid idr calls

This looks reasonable to me. Am I right to assume this is just a
preparation for the state lock removal? As far as I can tell currently
all calls are under nfs4_lock_state().

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

assuming it gets a proper patch description.


2014-06-19 14:52:30

by Jeff Layton

[permalink] [raw]
Subject: [PATCH v1 063/104] nfsd: add locking to stateowner release

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

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 858778cbcc7d..e5e1a7abee06 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -563,6 +563,8 @@ static void nfs4_put_stid(struct nfs4_stid *s)
{
struct nfs4_client *clp = s->sc_client;

+ lockdep_assert_not_held(&clp->cl_lock);
+
if (!atomic_dec_and_lock(&s->sc_count, &clp->cl_lock)) {
wake_up_all(&close_wq);
return;
@@ -2923,9 +2925,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;
+
+ lockdep_assert_not_held(&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);
}

@@ -2939,11 +2946,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)
@@ -4730,11 +4733,7 @@ static void hash_lockowner(struct nfs4_lockowner *lo, unsigned int strhashval, s

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