nfsd: eliminate the client_mutex
Subsystems (5)
v3 cover nfsd: eliminate the client_mutex
v3 changes:
- rebased on top of Bruce's for-3.17 branch
- addressed a number of Christoph's review comments. I've generally kept the Reviewed-by's intact when I thought I was changing things along the lines that he suggested, but please to glance over the results to be sure that I did.
- some more reordering of patches. Some more have been moved near the front when they don't depend on other changes. I've also tried to group them a little more logically so that patches that touch related areas are together.
- second pass at overhauling deny handling. This one should close all of the potential races with the fi_share_deny field. There are also a number of related cleanups to the deny handling.
- st_access_bmap and st_deny_bmap have been shrunk to a byte each, which should help reduce the stateid memory footprint.
- scrapped the Documentation/ file and moved most of its content into comments above the respective data structures.
v2 changes:
- rebased on top of v3.16-rc2
- fixed up checkpatch warnings (I'm really starting to hate that 80 column limit warning)
- fleshed out patch descriptions. Most of them should now say they are a necessary step toward client_mutex removal when it's not otherwise obvious. Also, when things touch outside of fs/nfsd, I added Cc lines for the appropriate maintainers.
- reordered patches to put more of the ones that don't affect locking near the front of the queue. This may make it easier to merge this piecemeal.
- I think I have addressed all of Christoph's review comments -- let me know if I missed any. For now, I left the Documentation/ patch intact, but we don't need to merge it at all if it's objectionable. I may end up transplanting it into comments but I ran short of time so I'll defer it for now.
- fix race that can occur between concurrent FREE_STATEID and CLOSE. As part of that fix, the cl_lock thrashing (and ensuing races) that could occur when a stateowner was released has also been eliminated.
- overhaul of access/deny mode handling. Christoph was correct to be suspicious. It didn't properly handle the case where a stateid with a deny mode was released or downgraded. As a bonus, the new code should be much more efficient when you have a long list of stateids as we no longer need to walk the entire list to check for deny mode conflicts. I also did some cleanup of the file access handling.
- ensure that dl_recall_lru list entries are dequeued before calling revoke_delegation (potential memory corruptor).
- Included Christophs fix for the file access leak when nfsd4_truncate fails. I took the liberty of adding a commit log message for it and a SoB line. Let me know if that's a problem and we can rework it.
This time, I'm just posting what hasn't already been merged into Bruce's for-3.17 branch. I'll plan to keep the following branch updated with the latest set:
http://git.samba.org/?p=jlayton/linux.git;a=shortlog;h=refs/heads/nfsd-devel
Original cover letter text follows:
-----------------------[snip]--------------------------
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.
Benny Halevy (1):
nfsd4: use cl_lock to synchronize all stateid idr calls
Christoph Hellwig (1):
nfsd: fix file access refcount leak when nfsd4_truncate fails
Jeff Layton (53):
nfsd: wait to initialize work struct just prior to using it
nfsd: Avoid taking state_lock while holding inode lock in
nfsd_break_one_deleg
nfsd: declare v4.1+ openowners confirmed on creation
nfsd: Allow lockowners to hold several stateids
nfsd: clean up nfsd4_release_lockowner
nfsd: Allow struct nfsd4_compound_state to cache the nfs4_client
nfsd: refactor nfs4_file_get_access and nfs4_file_put_access
nfsd: remove nfs4_file_put_fd
nfsd: shrink st_access_bmap and st_deny_bmap
nfsd: set stateid access and deny bits in nfs4_get_vfs_file
nfsd: clean up reset_union_bmap_deny
nfsd: make deny mode enforcement more efficient and close races in it
nfsd: cleanup nfs4_check_open
locks: add file_has_lease to prevent delegation break races
nfsd: Protect the nfs4_file delegation fields using the fi_lock
nfsd: Ensure atomicity of stateid destruction and idr tree removal
nfsd: Cleanup the freeing of stateids
nfsd: do filp_close in sc_free callback for lock stateids
nfsd: Add locking to protect the state owner lists
nfsd: clean up races in lock stateid searching and creation
nfsd: ensure atomicity in nfsd4_free_stateid and
nfsd4_validate_stateid
nfsd: clean up lockowner refcounting when finding them
nfsd: add an operation for unhashing a stateowner
nfsd: clean up refcounting for lockowners
nfsd: make openstateids hold references to their openowners
nfsd: don't allow CLOSE to proceed until refcount on stateid drops
nfsd: clean up and reorganize release_lockowner
lockdep: add lockdep_assert_not_held
nfsd: add locking to stateowner release
nfsd: optimize destroy_lockowner cl_lock thrashing
nfsd: close potential race in nfsd4_free_stateid
nfsd: reduce cl_lock thrashing in release_openowner
nfsd: don't thrash the cl_lock while freeing an open stateid
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: don't destroy client if mark_client_expired_locked fails
nfsd: don't destroy clients that are busy
nfsd: protect clid and verifier generation with client_lock
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 some comments to the nfsd4 object definitions
Trond Myklebust (59):
nfsd: Protect addition to the file_hashtbl
nfsd: nfs4_preprocess_seqid_op should only set *stpp on success
nfsd: Cleanup nfs4svc_encode_compoundres
nfsd: clean up nfsd4_close_open_stateid
nfsd: lock owners are not per open stateid
nfsd: NFSv4 lock-owners are not associated to a specific file
nfsd: Don't get a session reference without a client reference
nfsd: Cleanup - Let nfsd4_lookup_stateid() take a cstate argument
nfsd: Convert nfsd4_process_open1() to work with lookup_clientid()
nfsd: Always use lookup_clientid() in nfsd4_process_open1
nfsd: Convert nfs4_check_open_reclaim() to work with lookup_clientid()
nfsd: Move the delegation reference counter into the struct nfs4_stid
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: clean up helper __release_lock_stateid
nfsd: Simplify stateid management
nfsd: Fix delegation revocation
nfsd: Add reference counting to the lock and open stateids
nfsd: Add a struct nfs4_file field to struct nfs4_stid
nfsd: Replace nfs4_ol_stateid->st_file with the st_stid.sc_file
nfsd: 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 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: Move the open owner hash table into struct nfs4_client
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 lockdep 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()
fs/locks.c | 26 + fs/nfsd/fault_inject.c | 130 +-- fs/nfsd/netns.h | 19 +- fs/nfsd/nfs4callback.c | 24 +- fs/nfsd/nfs4proc.c | 16 +- fs/nfsd/nfs4state.c | 2873 ++++++++++++++++++++++++++++++++--------------- fs/nfsd/nfs4xdr.c | 17 +- fs/nfsd/state.h | 182 ++- fs/nfsd/xdr4.h | 8 +- include/linux/fs.h | 6 + include/linux/lockdep.h | 4 + 11 files changed, 2221 insertions(+), 1084 deletions(-)
Signature · 1 line
1.9.3
PATCH v3 1/114 nfsd: fix file access refcount leak when nfsd4_truncate fails
From: Christoph Hellwig <[email protected]>
nfsd4_process_open2 will currently will get access to the file, and then call nfsd4_truncate to (possibly) truncate it. If that operation fails though, then the access references will never be released as the nfs4_ol_stateid is never initialized.
Fix by moving the nfsd4_truncate call into nfs4_get_vfs_file, ensuring that the refcounts are properly put if the truncate fails.
--- fs/nfsd/nfs4state.c | 62 ++++++++++++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 32 deletions(-)
fs/nfsd/nfs4state.c +30 -32
@@ -3046,6 +3046,21 @@ static inline int nfs4_access_to_access(u32 nfs4_access)return flags; } +static inline __be32 +nfsd4_truncate (struct svc_rqst *rqstp,struct svc_fh *fh, +struct nfsd4_open *open) +{ +struct iattr iattr = { + .ia_valid =ATTR_SIZE , + .ia_size =0 , + }; +if (!open->op_truncate) +return 0 ; +if (!(open->op_share_access &NFS4_SHARE_ACCESS_WRITE )) +return nfserr_inval; +return nfsd_setattr (rqstp, fh, &iattr,0 , (time_t )0 ); +} +static __be32 nfs4_get_vfs_file (struct svc_rqst *rqstp,struct nfs4_file *fp,struct svc_fh *cur_fh,struct nfsd4_open *open) {
@@ -3057,53 +3072,39 @@ static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp, status =nfsd_open (rqstp, cur_fh,S_IFREG , access, &fp->fi_fds[oflag]);if (status) -return status; + goto out; }nfs4_file_get_access (fp, oflag); + status =nfsd4_truncate (rqstp, cur_fh, open); +if (status) + goto out_put_access; +return nfs_ok; -} -static inline __be32 -nfsd4_truncate (struct svc_rqst *rqstp,struct svc_fh *fh, -struct nfsd4_open *open) -{ -struct iattr iattr = { - .ia_valid =ATTR_SIZE , - .ia_size =0 , - }; -if (!open->op_truncate) -return 0 ; -if (!(open->op_share_access &NFS4_SHARE_ACCESS_WRITE )) -return nfserr_inval; -return nfsd_setattr (rqstp, fh, &iattr,0 , (time_t )0 ); +out_put_access: +nfs4_file_put_access (fp, oflag); +out: +return status; }static __be32 nfs4_upgrade_open (struct svc_rqst *rqstp,struct nfs4_file *fp,struct svc_fh *cur_fh,struct nfs4_ol_stateid *stp,struct nfsd4_open *open) {u32 op_share_access = open->op_share_access; -bool new_access;__be32 status; - new_access = !test_access (op_share_access, stp); -if (new_access) { +if (!test_access (op_share_access, stp)) status =nfs4_get_vfs_file (rqstp, fp, cur_fh, open); -if (status) -return status; - } - 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); - } +else + status =nfsd4_truncate (rqstp, cur_fh, open); + +if (status)return status; - } +/* remember the open */ set_access (op_share_access, stp);set_deny (open->op_share_deny, stp); -return nfs_ok; }
@@ -3354,9 +3355,6 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf status =nfs4_get_vfs_file (rqstp, fp, current_fh, open);if (status) goto out; - status =nfsd4_truncate (rqstp, current_fh, open); -if (status) - goto out; stp = open->op_stp; open->op_stp =NULL ;init_open_stateid (stp, fp, open);
Signature · 1 line
1.9.3
PATCH v3 2/114 nfsd: Protect addition to the file_hashtbl
In reply to Jeff LaytonFrom: 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.
--- fs/nfsd/nfs4state.c | 49 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 12 deletions(-)
fs/nfsd/nfs4state.c +37 -12
@@ -2611,17 +2611,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
@@ -2787,23 +2788,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
@@ -3325,21 +3352,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); }/*
Signature · 1 line
1.9.3
Re: [PATCH v3 002/114] nfsd: Protect addition to the file_hashtbl
Quoted 4 lines · Jeff Layton
From: Trond Myklebust <[email protected]>
Ensure that we only can have a single struct
nfs4_fileper inode in thefile_hashtbland make addition atomic with respect to lookup.
This changelog gives the impression it's a bugfix, and it isn't. I understand it's tedious in this series, but really I think at a minimum a changelog should always make it clear whether a patch is a bugfix or not. Applying with changelog rewritten to:
Current code depends on the client_mutex to guarantee a single struct nfs4_file per inode in the file_hashtbl and make addition atomic with respect to lookup. Rely instead on the state_Lock, to make it easier to stop taking the client_mutex here later.
...
--b.
Quoted 120 lines
To prevent an i_lock/state_lock inversion, change
nfsd4_init_fileto 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(-)fs/nfsd/nfs4state.c +37
-12@@ -2611,17 +2611,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 @@ -2787,23 +2788,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 @@ -3325,21 +3352,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); }/* Signature · 1 line
1.9.3
Re: [PATCH v3 002/114] nfsd: Protect addition to the file_hashtbl
Quoted 11 lines · On Mon, 30 Jun 2014 16:28:19 -0400 "J. Bruce Fields" <[email protected]>…
Quoted 4 lines · Jeff Layton
From: Trond Myklebust <[email protected]>
Ensure that we only can have a single struct
nfs4_fileper inode in thefile_hashtbland make addition atomic with respect to lookup.This changelog gives the impression it's a bugfix, and it isn't. I understand it's tedious in this series, but really I think at a minimum a changelog should always make it clear whether a patch is a bugfix or not. Applying with changelog rewritten to:
Quoted 8 lines
Current code depends on the
client_mutexto guarantee a single structnfs4_fileper inode in thefile_hashtbland make addition atomic with respect to lookup. Rely instead on thestate_Lock, to make it easier to stop taking theclient_mutexhere later.
...--b.
Thanks and sorry, it's definitely correct that it's not a bugfix wrt the existing code. I thought I had updated that patch to spell it out, but I guess I missed that one...
Quoted 120 lines
Quoted 120 lines
To prevent an i_lock/state_lock inversion, change
nfsd4_init_fileto 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(-)fs/nfsd/nfs4state.c +37
-12@@ -2611,17 +2611,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 @@ -2787,23 +2788,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 @@ -3325,21 +3352,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); }/* Signature · 1 line
1.9.3
Signature · 1 line
Jeff Layton <[email protected]>
PATCH v3 3/114 nfsd: wait to initialize work struct just prior to using it
In reply to Jeff LaytonThere's a fair chance we won't ever need this work struct, so we might as well delay initializing it until just before we're going to use it. In a later patch, we'll need to intialize the work with a different function for delegation callbacks. By delaying this, we avoid having to do it twice.
--- fs/nfsd/nfs4callback.c | 10 +++++----- fs/nfsd/nfs4state.c | 2 -- fs/nfsd/state.h | 1 - 3 files changed, 5 insertions(+), 8 deletions(-)
fs/nfsd/nfs4callback.c +5 -5
@@ -43,6 +43,7 @@#define NFSDDBG_FACILITY NFSDDBG_PROCstatic void nfsd4_mark_cb_fault (struct nfs4_client *,int reason); +static void nfsd4_do_callback_rpc (struct work_struct *w);#define NFSPROC4_CB_NULL 0#define NFSPROC4_CB_COMPOUND 1
@@ -763,6 +764,8 @@ static void do_probe_callback(struct nfs4_client *clp) cb->cb_ops = &nfsd4_cb_probe_ops; +INIT_WORK (&cb->cb_work, nfsd4_do_callback_rpc); +run_nfsd4_cb (cb); }
@@ -1031,11 +1034,6 @@ static void nfsd4_do_callback_rpc(struct work_struct *w) cb->cb_ops, cb); } -void nfsd4_init_callback (struct nfsd4_callback *cb) -{ -INIT_WORK (&cb->cb_work, nfsd4_do_callback_rpc); -} -void nfsd4_cb_recall (struct nfs4_delegation *dp) {struct nfsd4_callback *cb = &dp->dl_recall;
@@ -1053,5 +1051,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_callback_rpc); +run_nfsd4_cb (&dp->dl_recall); }
fs/nfsd/nfs4state.c +0 -2
@@ -466,7 +466,6 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct svfh_copy_shallow (&dp->dl_fh, ¤t_fh->fh_handle); dp->dl_time =0 ;atomic_set (&dp->dl_count,1 ); -nfsd4_init_callback (&dp->dl_recall);return dp; }
@@ -1472,7 +1471,6 @@ static struct nfs4_client *create_client(struct xdr_netobj name,spin_unlock (&nn->client_lock);return NULL ; } -nfsd4_init_callback (&clp->cl_cb_null); clp->cl_time =get_seconds ();clear_bit (0 , &clp->cl_cb_slot_busy);copy_verf (clp, verf);
fs/nfsd/state.h +0 -1
@@ -464,7 +464,6 @@ 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 int set_callback_cred (void ); -extern void nfsd4_init_callback (struct nfsd4_callback *);extern void nfsd4_probe_callback (struct nfs4_client *clp);extern void nfsd4_probe_callback_sync (struct nfs4_client *clp);extern void nfsd4_change_callback (struct nfs4_client *clp,struct nfs4_cb_conn *);
Signature · 1 line
1.9.3
Re: [PATCH v3 003/114] nfsd: wait to initialize work struct just prior to using it
Quoted 5 lines · Jeff Layton
There's a fair chance we won't ever need this work struct, so we might as well delay initializing it until just before we're going to use it. In a later patch, we'll need to intialize the work with a different function for delegation callbacks. By delaying this, we avoid having to do it twice.
When I run
./nfs4.1/testserver.py f19:/exports/xfs/FUBAR --maketree --rundeps COMP1
I get crashes like:
BUG: unable to handle kernel NULL pointer dereference at 0000000000000008 IP: [<ffffffff810890b1>] process_one_work+0x31/0x500 PGD 0 Oops: 0000 [#1] PREEMPT SMP DEBUG_PAGEALLOC Modules linked in: nfsd auth_rpcgss oid_registry nfs_acl lockd sunrpc CPU: 2 PID: 30 Comm: kworker/u8:1 Not tainted 3.16.0-rc2-00023-g30c1d16 #3051 Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 task: ffff88003d9cc810 ti: ffff88003d9d0000 task.ti: ffff88003d9d0000 RIP: 0010:[<ffffffff810890b1>] [<ffffffff810890b1>] process_one_work+0x31/0x500 RSP: 0018:ffff88003d9d3d68 EFLAGS: 00010046 RAX: 0000000000000100 RBX: ffff88003d9caf80 RCX: dead000000200200 RDX: 0000000000000100 RSI: ffff880031429b38 RDI: ffff88003d9caf80 RBP: ffff88003d9d3dd8 R08: ffff88003dcb7800 R09: 0000000001c40000 R10: 0000000000000000 R11: 000000000006b5b0 R12: ffff88003dcb7800 R13: 0000000000000000 R14: ffff880031429b38 R15: ffff88003d9caf80 FS: 0000000000000000(0000) GS:ffff88003fb00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: 0000000000000138 CR3: 0000000027744000 CR4: 00000000000006e0 Stack: 000000003d9d3d88 ffff88003dcb7818 ffff88003d9cafb0 ffff88003dcb7800 ffff88003dcb7800 ffff88003dcb7800 ffff88003dcb7800 ffff88003d9cafb0 ffff88003d9d3dd8 ffff88003dcb7800 ffff88003dcb7848 ffff88003d9cafb0 Call Trace: [<ffffffff81089b9b>] worker_thread+0x11b/0x4f0 [<ffffffff81089a80>] ? init_pwq+0x190/0x190 [<ffffffff81090ac4>] kthread+0xe4/0x100 [<ffffffff810909e0>] ? __init_kthread_worker+0x70/0x70 [<ffffffff81a4ca2c>] ret_from_fork+0x7c/0xb0 [<ffffffff810909e0>] ? __init_kthread_worker+0x70/0x70 Code: 48 89 e5 41 57 41 56 49 89 f6 41 55 45 31 ed 41 54 53 48 89 fb 48 83 ec 48 48 8b 06 4c 8b 67 48 48 89 c2 30 d2 a8 04 4c 0f 45 ea <49> 8b 45 08 48 c7 45 b8 00 00 00 00 48 c7 45 c0 00 00 00 00 8b RIP [<ffffffff810890b1>] process_one_work+0x31/0x500 RSP <ffff88003d9d3d68>
CR2: 0000000000000008
and I'm suspicious of this:
fs/nfsd/nfs4callback.c static void do_probe_callback(struct nfs4_client *clp) · line 770
Quoted 8 lines
fs/nfsd/nfs4callback.c +2
-0@@ -763,6 +764,8 @@ static void do_probe_callback(struct nfs4_client *clp) cb->cb_ops = &nfsd4_cb_probe_ops; +INIT_WORK (&cb->cb_work, nfsd4_do_callback_rpc); +run_nfsd4_cb (cb); }
This is called all over the place, possibly multiple times on the same client, so we're calling INIT_WORK on something that may already be in use--I doubt that's safe.
I'm backing out this patch for now.
--b.
Re: [PATCH v3 003/114] nfsd: wait to initialize work struct just prior to using it
Quoted 53 lines · On Tue, 8 Jul 2014 17:11:34 -0400 "J. Bruce Fields" <[email protected]>…
Quoted 5 lines · Jeff Layton
There's a fair chance we won't ever need this work struct, so we might as well delay initializing it until just before we're going to use it. In a later patch, we'll need to intialize the work with a different function for delegation callbacks. By delaying this, we avoid having to do it twice.
When I run
./nfs4.1/testserver.py f19:/exports/xfs/FUBAR --maketree --rundeps COMP1I get crashes like:
BUG: unable to handle kernel NULL pointer dereference at 0000000000000008 IP: [<ffffffff810890b1>] process_one_work+0x31/0x500 PGD 0 Oops: 0000 [#1] PREEMPT SMP DEBUG_PAGEALLOC Modules linked in: nfsd auth_rpcgss oid_registry nfs_acl lockd sunrpc CPU: 2 PID: 30 Comm: kworker/u8:1 Not tainted 3.16.0-rc2-00023-g30c1d16 #3051 Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 task: ffff88003d9cc810 ti: ffff88003d9d0000 task.ti: ffff88003d9d0000 RIP: 0010:[<ffffffff810890b1>] [<ffffffff810890b1>] process_one_work+0x31/0x500 RSP: 0018:ffff88003d9d3d68 EFLAGS: 00010046 RAX: 0000000000000100 RBX: ffff88003d9caf80 RCX: dead000000200200 RDX: 0000000000000100 RSI: ffff880031429b38 RDI: ffff88003d9caf80 RBP: ffff88003d9d3dd8 R08: ffff88003dcb7800 R09: 0000000001c40000 R10: 0000000000000000 R11: 000000000006b5b0 R12: ffff88003dcb7800 R13: 0000000000000000 R14: ffff880031429b38 R15: ffff88003d9caf80 FS: 0000000000000000(0000) GS:ffff88003fb00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: 0000000000000138 CR3: 0000000027744000 CR4: 00000000000006e0 Stack: 000000003d9d3d88 ffff88003dcb7818 ffff88003d9cafb0 ffff88003dcb7800 ffff88003dcb7800 ffff88003dcb7800 ffff88003dcb7800 ffff88003d9cafb0 ffff88003d9d3dd8 ffff88003dcb7800 ffff88003dcb7848 ffff88003d9cafb0 Call Trace: [<ffffffff81089b9b>] worker_thread+0x11b/0x4f0 [<ffffffff81089a80>] ? init_pwq+0x190/0x190 [<ffffffff81090ac4>] kthread+0xe4/0x100 [<ffffffff810909e0>] ? __init_kthread_worker+0x70/0x70 [<ffffffff81a4ca2c>] ret_from_fork+0x7c/0xb0 [<ffffffff810909e0>] ? __init_kthread_worker+0x70/0x70 Code: 48 89 e5 41 57 41 56 49 89 f6 41 55 45 31 ed 41 54 53 48 89 fb 48 83 ec 48 48 8b 06 4c 8b 67 48 48 89 c2 30 d2 a8 04 4c 0f 45 ea <49> 8b 45 08 48 c7 45 b8 00 00 00 00 48 c7 45 c0 00 00 00 00 8b RIP [<ffffffff810890b1>] process_one_work+0x31/0x500 RSP <ffff88003d9d3d68>CR2: 0000000000000008
and I'm suspicious of this:
Quoted 4 lines
fs/nfsd/nfs4callback.c +0
-0@@ -763,6 +764,8 @@ static void do_probe_callback(struct nfs4_client *clp) cb->cb_ops = &nfsd4_cb_probe_ops;
Quoted 9 lines
fs/nfsd/nfs4callback.c static void do_probe_callback(struct nfs4_client *clp) · line 770
Quoted 4 lines
fs/nfsd/nfs4callback.c +2
-0+INIT_WORK (&cb->cb_work, nfsd4_do_callback_rpc); +run_nfsd4_cb (cb); }This is called all over the place, possibly multiple times on the same client, so we're calling
INIT_WORKon something that may already be in use--I doubt that's safe.
Doh! That makes total sense, and yes it would almost certainly wreak havoc to reinitialize a workqueue job that has already been queued.
Quoted 2 lines
I'm backing out this patch for now.
Yes, please do. Christoph, I think we'll just have to live with the double initialization here.
Cheers,
Signature · 1 line
Jeff Layton <[email protected]>
Re: [PATCH v3 003/114] nfsd: wait to initialize work struct just prior to using it
In reply to J. Bruce FieldsQuoted 49 lines · J. Bruce Fields
BUG: unable to handle kernel NULL pointer dereference at 0000000000000008 IP: [<ffffffff810890b1>] process_one_work+0x31/0x500 PGD 0 Oops: 0000 [#1] PREEMPT SMP DEBUG_PAGEALLOC Modules linked in: nfsd auth_rpcgss oid_registry nfs_acl lockd sunrpc CPU: 2 PID: 30 Comm: kworker/u8:1 Not tainted 3.16.0-rc2-00023-g30c1d16 #3051 Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 task: ffff88003d9cc810 ti: ffff88003d9d0000 task.ti: ffff88003d9d0000 RIP: 0010:[<ffffffff810890b1>] [<ffffffff810890b1>] process_one_work+0x31/0x500 RSP: 0018:ffff88003d9d3d68 EFLAGS: 00010046 RAX: 0000000000000100 RBX: ffff88003d9caf80 RCX: dead000000200200 RDX: 0000000000000100 RSI: ffff880031429b38 RDI: ffff88003d9caf80 RBP: ffff88003d9d3dd8 R08: ffff88003dcb7800 R09: 0000000001c40000 R10: 0000000000000000 R11: 000000000006b5b0 R12: ffff88003dcb7800 R13: 0000000000000000 R14: ffff880031429b38 R15: ffff88003d9caf80 FS: 0000000000000000(0000) GS:ffff88003fb00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: 0000000000000138 CR3: 0000000027744000 CR4: 00000000000006e0 Stack: 000000003d9d3d88 ffff88003dcb7818 ffff88003d9cafb0 ffff88003dcb7800 ffff88003dcb7800 ffff88003dcb7800 ffff88003dcb7800 ffff88003d9cafb0 ffff88003d9d3dd8 ffff88003dcb7800 ffff88003dcb7848 ffff88003d9cafb0 Call Trace: [<ffffffff81089b9b>] worker_thread+0x11b/0x4f0 [<ffffffff81089a80>] ? init_pwq+0x190/0x190 [<ffffffff81090ac4>] kthread+0xe4/0x100 [<ffffffff810909e0>] ? __init_kthread_worker+0x70/0x70 [<ffffffff81a4ca2c>] ret_from_fork+0x7c/0xb0 [<ffffffff810909e0>] ? __init_kthread_worker+0x70/0x70 Code: 48 89 e5 41 57 41 56 49 89 f6 41 55 45 31 ed 41 54 53 48 89 fb 48 83 ec 48 48 8b 06 4c 8b 67 48 48 89 c2 30 d2 a8 04 4c 0f 45 ea <49> 8b 45 08 48 c7 45 b8 00 00 00 00 48 c7 45 c0 00 00 00 00 8b RIP [<ffffffff810890b1>] process_one_work+0x31/0x500 RSP <ffff88003d9d3d68>CR2: 0000000000000008
and I'm suspicious of this:
fs/nfsd/nfs4callback.c static void do_probe_callback(struct nfs4_client *clp) · line 770
Quoted 8 lines
fs/nfsd/nfs4callback.c +2
-0@@ -763,6 +764,8 @@ static void do_probe_callback(struct nfs4_client *clp) cb->cb_ops = &nfsd4_cb_probe_ops; +INIT_WORK (&cb->cb_work, nfsd4_do_callback_rpc); +run_nfsd4_cb (cb); }This is called all over the place, possibly multiple times on the same client, so we're calling
INIT_WORKon something that may already be in use--I doubt that's safe.
Quoted 1 line
I'm backing out this patch for now.
If that's the case all of do_probe_callback seems very fishy to me, as it scrambles all over the callback structure. I guess we need to move more of it to an init function then, and have different init functions for the different callbacks.
Re: [PATCH v3 003/114] nfsd: wait to initialize work struct just prior to using it
Quoted 50 lines · Christoph Hellwig
Quoted 49 lines · J. Bruce Fields
BUG: unable to handle kernel NULL pointer dereference at 0000000000000008 IP: [<ffffffff810890b1>] process_one_work+0x31/0x500 PGD 0 Oops: 0000 [#1] PREEMPT SMP DEBUG_PAGEALLOC Modules linked in: nfsd auth_rpcgss oid_registry nfs_acl lockd sunrpc CPU: 2 PID: 30 Comm: kworker/u8:1 Not tainted 3.16.0-rc2-00023-g30c1d16 #3051 Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 task: ffff88003d9cc810 ti: ffff88003d9d0000 task.ti: ffff88003d9d0000 RIP: 0010:[<ffffffff810890b1>] [<ffffffff810890b1>] process_one_work+0x31/0x500 RSP: 0018:ffff88003d9d3d68 EFLAGS: 00010046 RAX: 0000000000000100 RBX: ffff88003d9caf80 RCX: dead000000200200 RDX: 0000000000000100 RSI: ffff880031429b38 RDI: ffff88003d9caf80 RBP: ffff88003d9d3dd8 R08: ffff88003dcb7800 R09: 0000000001c40000 R10: 0000000000000000 R11: 000000000006b5b0 R12: ffff88003dcb7800 R13: 0000000000000000 R14: ffff880031429b38 R15: ffff88003d9caf80 FS: 0000000000000000(0000) GS:ffff88003fb00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: 0000000000000138 CR3: 0000000027744000 CR4: 00000000000006e0 Stack: 000000003d9d3d88 ffff88003dcb7818 ffff88003d9cafb0 ffff88003dcb7800 ffff88003dcb7800 ffff88003dcb7800 ffff88003dcb7800 ffff88003d9cafb0 ffff88003d9d3dd8 ffff88003dcb7800 ffff88003dcb7848 ffff88003d9cafb0 Call Trace: [<ffffffff81089b9b>] worker_thread+0x11b/0x4f0 [<ffffffff81089a80>] ? init_pwq+0x190/0x190 [<ffffffff81090ac4>] kthread+0xe4/0x100 [<ffffffff810909e0>] ? __init_kthread_worker+0x70/0x70 [<ffffffff81a4ca2c>] ret_from_fork+0x7c/0xb0 [<ffffffff810909e0>] ? __init_kthread_worker+0x70/0x70 Code: 48 89 e5 41 57 41 56 49 89 f6 41 55 45 31 ed 41 54 53 48 89 fb 48 83 ec 48 48 8b 06 4c 8b 67 48 48 89 c2 30 d2 a8 04 4c 0f 45 ea <49> 8b 45 08 48 c7 45 b8 00 00 00 00 48 c7 45 c0 00 00 00 00 8b RIP [<ffffffff810890b1>] process_one_work+0x31/0x500 RSP <ffff88003d9d3d68>CR2: 0000000000000008
and I'm suspicious of this:
fs/nfsd/nfs4callback.c static void do_probe_callback(struct nfs4_client *clp) · line 770
Quoted 8 lines
fs/nfsd/nfs4callback.c +2
-0@@ -763,6 +764,8 @@ static void do_probe_callback(struct nfs4_client *clp) cb->cb_ops = &nfsd4_cb_probe_ops; +INIT_WORK (&cb->cb_work, nfsd4_do_callback_rpc); +run_nfsd4_cb (cb); }This is called all over the place, possibly multiple times on the same client, so we're calling
INIT_WORKon something that may already be in use--I doubt that's safe.
Quoted 6 lines
Quoted 1 line
I'm backing out this patch for now.
If that's the case all of
do_probe_callbackseems very fishy to me, as it scrambles all over the callback structure. I guess we need to move more of it to an init function then, and have different init functions for the different callbacks.
Taking a look.... It does look fishy, but those fields are constant, so I don't see a bug.
In delegation recall case (nfsd4_cb_recall()) those fields aren't constant, but we guarantee it's only called once.
--b.
Re: [PATCH v3 003/114] nfsd: wait to initialize work struct just prior to using it
Quoted 58 lines · On Wed, 9 Jul 2014 15:41:14 -0400 "J. Bruce Fields" <[email protected]>…
Quoted 50 lines · Christoph Hellwig
Quoted 49 lines · J. Bruce Fields
BUG: unable to handle kernel NULL pointer dereference at 0000000000000008 IP: [<ffffffff810890b1>] process_one_work+0x31/0x500 PGD 0 Oops: 0000 [#1] PREEMPT SMP DEBUG_PAGEALLOC Modules linked in: nfsd auth_rpcgss oid_registry nfs_acl lockd sunrpc CPU: 2 PID: 30 Comm: kworker/u8:1 Not tainted 3.16.0-rc2-00023-g30c1d16 #3051 Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 task: ffff88003d9cc810 ti: ffff88003d9d0000 task.ti: ffff88003d9d0000 RIP: 0010:[<ffffffff810890b1>] [<ffffffff810890b1>] process_one_work+0x31/0x500 RSP: 0018:ffff88003d9d3d68 EFLAGS: 00010046 RAX: 0000000000000100 RBX: ffff88003d9caf80 RCX: dead000000200200 RDX: 0000000000000100 RSI: ffff880031429b38 RDI: ffff88003d9caf80 RBP: ffff88003d9d3dd8 R08: ffff88003dcb7800 R09: 0000000001c40000 R10: 0000000000000000 R11: 000000000006b5b0 R12: ffff88003dcb7800 R13: 0000000000000000 R14: ffff880031429b38 R15: ffff88003d9caf80 FS: 0000000000000000(0000) GS:ffff88003fb00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: 0000000000000138 CR3: 0000000027744000 CR4: 00000000000006e0 Stack: 000000003d9d3d88 ffff88003dcb7818 ffff88003d9cafb0 ffff88003dcb7800 ffff88003dcb7800 ffff88003dcb7800 ffff88003dcb7800 ffff88003d9cafb0 ffff88003d9d3dd8 ffff88003dcb7800 ffff88003dcb7848 ffff88003d9cafb0 Call Trace: [<ffffffff81089b9b>] worker_thread+0x11b/0x4f0 [<ffffffff81089a80>] ? init_pwq+0x190/0x190 [<ffffffff81090ac4>] kthread+0xe4/0x100 [<ffffffff810909e0>] ? __init_kthread_worker+0x70/0x70 [<ffffffff81a4ca2c>] ret_from_fork+0x7c/0xb0 [<ffffffff810909e0>] ? __init_kthread_worker+0x70/0x70 Code: 48 89 e5 41 57 41 56 49 89 f6 41 55 45 31 ed 41 54 53 48 89 fb 48 83 ec 48 48 8b 06 4c 8b 67 48 48 89 c2 30 d2 a8 04 4c 0f 45 ea <49> 8b 45 08 48 c7 45 b8 00 00 00 00 48 c7 45 c0 00 00 00 00 8b RIP [<ffffffff810890b1>] process_one_work+0x31/0x500 RSP <ffff88003d9d3d68>CR2: 0000000000000008
and I'm suspicious of this:
fs/nfsd/nfs4callback.c static void do_probe_callback(struct nfs4_client *clp) · line 770
Quoted 8 lines
fs/nfsd/nfs4callback.c +2
-0@@ -763,6 +764,8 @@ static void do_probe_callback(struct nfs4_client *clp) cb->cb_ops = &nfsd4_cb_probe_ops; +INIT_WORK (&cb->cb_work, nfsd4_do_callback_rpc); +run_nfsd4_cb (cb); }This is called all over the place, possibly multiple times on the same client, so we're calling
INIT_WORKon something that may already be in use--I doubt that's safe.Quoted 6 lines
Quoted 1 line
I'm backing out this patch for now.
If that's the case all of
do_probe_callbackseems very fishy to me, as it scrambles all over the callback structure. I guess we need to move more of it to an init function then, and have different init functions for the different callbacks.
Quoted 6 lines
Taking a look.... It does look fishy, but those fields are constant, so I don't see a bug.
In delegation recall case (
nfsd4_cb_recall()) those fields aren't constant, but we guarantee it's only called once.
Yes. I'm inclined to leave well enough alone on this and to leave any cleanup in this area to Christoph since he said he was overhauling that code anyway.
I didn't see where you had reverted the patch in your repo, so I went ahead and did it and then rebased the series on top of the revert. There were some merge conflicts, but I at least was able to get rid of the double INIT_WORK calls in the case of a delegation (and do some related cleanup in this area):
http://git.samba.org/?p=jlayton/linux.git;a=commitdiff;h=60b61375d6a84e74bf1c3c7c230712721e14773d
http://git.samba.org/?p=jlayton/linux.git;a=commitdiff;h=491192c3e6f0966722c34ba36adfde7575640544
Unfortunately there are a few (fairly trivial) merge conflicts later in the series due to this change. Bruce, do you want me to repost the whole set, or would you rather just cherry-pick them from my updated branch?
Signature · 1 line
Jeff Layton <[email protected]>
Re: [PATCH v3 003/114] nfsd: wait to initialize work struct just prior to using it
Quoted 68 lines · Jeff Layton
Quoted 58 lines · On Wed, 9 Jul 2014 15:41:14 -0400 "J. Bruce Fields" <[email protected]>…
Quoted 50 lines · Christoph Hellwig
Quoted 49 lines · J. Bruce Fields
BUG: unable to handle kernel NULL pointer dereference at 0000000000000008 IP: [<ffffffff810890b1>] process_one_work+0x31/0x500 PGD 0 Oops: 0000 [#1] PREEMPT SMP DEBUG_PAGEALLOC Modules linked in: nfsd auth_rpcgss oid_registry nfs_acl lockd sunrpc CPU: 2 PID: 30 Comm: kworker/u8:1 Not tainted 3.16.0-rc2-00023-g30c1d16 #3051 Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 task: ffff88003d9cc810 ti: ffff88003d9d0000 task.ti: ffff88003d9d0000 RIP: 0010:[<ffffffff810890b1>] [<ffffffff810890b1>] process_one_work+0x31/0x500 RSP: 0018:ffff88003d9d3d68 EFLAGS: 00010046 RAX: 0000000000000100 RBX: ffff88003d9caf80 RCX: dead000000200200 RDX: 0000000000000100 RSI: ffff880031429b38 RDI: ffff88003d9caf80 RBP: ffff88003d9d3dd8 R08: ffff88003dcb7800 R09: 0000000001c40000 R10: 0000000000000000 R11: 000000000006b5b0 R12: ffff88003dcb7800 R13: 0000000000000000 R14: ffff880031429b38 R15: ffff88003d9caf80 FS: 0000000000000000(0000) GS:ffff88003fb00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: 0000000000000138 CR3: 0000000027744000 CR4: 00000000000006e0 Stack: 000000003d9d3d88 ffff88003dcb7818 ffff88003d9cafb0 ffff88003dcb7800 ffff88003dcb7800 ffff88003dcb7800 ffff88003dcb7800 ffff88003d9cafb0 ffff88003d9d3dd8 ffff88003dcb7800 ffff88003dcb7848 ffff88003d9cafb0 Call Trace: [<ffffffff81089b9b>] worker_thread+0x11b/0x4f0 [<ffffffff81089a80>] ? init_pwq+0x190/0x190 [<ffffffff81090ac4>] kthread+0xe4/0x100 [<ffffffff810909e0>] ? __init_kthread_worker+0x70/0x70 [<ffffffff81a4ca2c>] ret_from_fork+0x7c/0xb0 [<ffffffff810909e0>] ? __init_kthread_worker+0x70/0x70 Code: 48 89 e5 41 57 41 56 49 89 f6 41 55 45 31 ed 41 54 53 48 89 fb 48 83 ec 48 48 8b 06 4c 8b 67 48 48 89 c2 30 d2 a8 04 4c 0f 45 ea <49> 8b 45 08 48 c7 45 b8 00 00 00 00 48 c7 45 c0 00 00 00 00 8b RIP [<ffffffff810890b1>] process_one_work+0x31/0x500 RSP <ffff88003d9d3d68>CR2: 0000000000000008
and I'm suspicious of this:
fs/nfsd/nfs4callback.c static void do_probe_callback(struct nfs4_client *clp) · line 770
Quoted 8 lines
fs/nfsd/nfs4callback.c +2
-0@@ -763,6 +764,8 @@ static void do_probe_callback(struct nfs4_client *clp) cb->cb_ops = &nfsd4_cb_probe_ops; +INIT_WORK (&cb->cb_work, nfsd4_do_callback_rpc); +run_nfsd4_cb (cb); }This is called all over the place, possibly multiple times on the same client, so we're calling
INIT_WORKon something that may already be in use--I doubt that's safe.Quoted 6 lines
Quoted 1 line
I'm backing out this patch for now.
If that's the case all of
do_probe_callbackseems very fishy to me, as it scrambles all over the callback structure. I guess we need to move more of it to an init function then, and have different init functions for the different callbacks.Quoted 6 lines
Taking a look.... It does look fishy, but those fields are constant, so I don't see a bug.
In delegation recall case (
nfsd4_cb_recall()) those fields aren't constant, but we guarantee it's only called once.
Quoted 5 lines
Yes. I'm inclined to leave well enough alone on this and to leave any cleanup in this area to Christoph since he said he was overhauling that code anyway.
I didn't see where you had reverted the patch in your repo,
Yeah, reverted locally but I had a testing problem that's kept me from pushing it out yet.
Quoted 6 lines
so I went
ahead and did it and then rebased the series on top of the revert. There were some merge conflicts, but I at least was able to get rid of the doubleINIT_WORKcalls in the case of a delegation (and do some related cleanup in this area):
Quoted 7 lines
http://git.samba.org/?p=jlayton/linux.git;a=commitdiff;h=60b61375d6a84e74bf1c3c7c230712721e14773d
http://git.samba.org/?p=jlayton/linux.git;a=commitdiff;h=491192c3e6f0966722c34ba36adfde7575640544Unfortunately there are a few (fairly trivial) merge conflicts later in the series due to this change. Bruce, do you want me to repost the whole set, or would you rather just cherry-pick them from my updated branch?
Let's spare everyone a reposting and I'll see how far I can get with fixing up the conflicts myself and/or cherry-picking from your updated branch.
--b.
Re: [PATCH v3 003/114] nfsd: wait to initialize work struct just prior to using it
In reply to Jeff LaytonQuoted 5 lines · Jeff Layton
I didn't see where you had reverted the patch in your repo, so I went ahead and did it and then rebased the series on top of the revert. There were some merge conflicts, but I at least was able to get rid of the double
INIT_WORKcalls in the case of a delegation (and do some related cleanup in this area):
This looks reasonable to me.
Re: [PATCH v3 003/114] nfsd: wait to initialize work struct just prior to using it
In reply to Jeff LaytonQuoted 4 lines
Unfortunately there are a few (fairly trivial) merge conflicts later in the series due to this change. Bruce, do you want me to repost the whole set, or would you rather just cherry-pick them from my updated branch?
Can you resend just the whole fi_fds and access/deny mode patches as a series for the next step? This seems useful and complicated enough to do a standalone review. I also don't think the additional few spinlocks would have enough performance impact to avoid them until the big client lock is gone, although all those logic changes could probably be done easily enough without the new locking if someone cared enough (I don't).
Re: [PATCH v3 003/114] nfsd: wait to initialize work struct just prior to using it
Quoted 5 lines · On Thu, 10 Jul 2014 03:53:43 -0700 Christoph Hellwig <[email protected]> wrote:
Quoted 4 lines
Unfortunately there are a few (fairly trivial) merge conflicts later in the series due to this change. Bruce, do you want me to repost the whole set, or would you rather just cherry-pick them from my updated branch?
Quoted 7 lines
Can you resend just the whole
fi_fdsand access/deny mode patches as a series for the next step? This seems useful and complicated enough to do a standalone review. I also don't think the additional few spinlocks would have enough performance impact to avoid them until the big client lock is gone, although all those logic changes could probably be done easily enough without the new locking if someone cared enough (I don't).
Yes, that sounds reasonable. I'll go ahead and move the patches necessary for it to the front of the series and address your comments and we can see about getting them merged ahead of the rest.
I agree that the extra spinlocking probably won't matter while the client_mutex is in play. If it does, then not needing to walk the whole stateids list in order to do deny mode enforcement may help make up for it.
Signature · 1 line
Jeff Layton <[email protected]>
PATCH v3 4/114 nfsd: Avoid taking state_lock while holding inode lock in nfsd_break_one_deleg
In reply to Jeff Laytonstate_lock is a heavily contended global lock. We don't want to grab that while simultaneously holding the inode->i_lock.
Add a new per-nfs4_file lock that we can use to protect the per-nfs4_file delegation list. Hold that while walking the list in the break_deleg callback and queue the workqueue job for each one.
The workqueue job can then take the state_lock and do the list manipulations without the i_lock being held prior to starting the rpc call.
--- fs/nfsd/nfs4callback.c | 28 +++++++++++++++++++++------- fs/nfsd/nfs4state.c | 43 ++++++++++++++++++++++++++++--------------- fs/nfsd/state.h | 2 ++ 3 files changed, 51 insertions(+), 22 deletions(-)
fs/nfsd/nfs4callback.c +21 -7
@@ -43,7 +43,7 @@#define NFSDDBG_FACILITY NFSDDBG_PROCstatic void nfsd4_mark_cb_fault (struct nfs4_client *,int reason); -static void nfsd4_do_callback_rpc (struct work_struct *w); +static void nfsd4_run_cb_null (struct work_struct *w);#define NFSPROC4_CB_NULL 0#define NFSPROC4_CB_COMPOUND 1
@@ -764,7 +764,7 @@ static void do_probe_callback(struct nfs4_client *clp) cb->cb_ops = &nfsd4_cb_probe_ops; -INIT_WORK (&cb->cb_work, nfsd4_do_callback_rpc); +INIT_WORK (&cb->cb_work, nfsd4_run_cb_null);run_nfsd4_cb (cb); }
@@ -936,7 +936,7 @@ void nfsd4_shutdown_callback(struct nfs4_client *clp)set_bit (NFSD4_CLIENT_CB_KILL , &clp->cl_flags);/* * Note this won't actually result in a null callback; -* instead, nfsd4_do_callback_rpc() will detect the killed +* instead, nfsd4_run_cb_null() will detect the killed * client, destroy the rpc client, and stop: */ do_probe_callback (clp);
@@ -1014,9 +1014,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;
@@ -1034,6 +1033,22 @@ static void nfsd4_do_callback_rpc(struct work_struct *w) cb->cb_ops, cb); } +static void nfsd4_run_cb_null (struct work_struct *w) +{ + struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, + cb_work); +nfsd4_run_callback_rpc (cb); +} + +static void nfsd4_run_cb_recall (struct work_struct *w) +{ + struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, + cb_work); + +nfsd4_prepare_cb_recall (cb->cb_op); +nfsd4_run_callback_rpc (cb); +} +void nfsd4_cb_recall (struct nfs4_delegation *dp) {struct nfsd4_callback *cb = &dp->dl_recall;
@@ -1050,8 +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_callback_rpc); +INIT_WORK (&cb->cb_work, nfsd4_run_cb_recall);run_nfsd4_cb (&dp->dl_recall); }
fs/nfsd/nfs4state.c +28 -15
92 lines not on this page · raw message
fs/nfsd/state.h +2 -0
@@ -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;
@@ -471,6 +472,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);
Signature · 1 line
1.9.3
Re: [PATCH v3 004/114] nfsd: Avoid taking state_lock while holding inode lock in nfsd_break_one_deleg
Quoted 10 lines · Jeff Layton
state_lockis a heavily contended global lock. We don't want to grab that while simultaneously holding theinode->i_lock.Add a new per-nfs4_file lock that we can use to protect the per-nfs4_file delegation list. Hold that while walking the list in the
break_delegcallback and queue the workqueue job for each one.The workqueue job can then take the
state_lockand do the list manipulations without thei_lockbeing held prior to starting the rpc call.
The code tends to assume that the callback thread only works with the delegation struct itself and puts it when done but doesn't otherwise touch other state.
I wonder how this interacts with state shutdown....
E.g. in nfs4_state_shutdown_net() we walk the dl_recall_lru and destroy everything we find, but this callback workqueue is still running so I think another delegation could get added to that list after this? Does that cause bugs?
And it'd also be worth checking delegreturn and destroy_client.
Maybe there's no bug, or they just need to flush the workqueue at the appropriate point.
There's also a preexisting expire_client/laundromat vs break race:
- expire_client/laundromat adds a delegation to its local reaplist using the same dl_recall_lru field that a delegation uses to track its position on the recall lru and drops the state lock.
- a concurrent break_lease adds the delegation to the lru.
- expire/client/laundromat then walks it reaplist and sees the lru head as just another delegation on the list....
Possibly unrelated, but it might be good to fix that first.
--b.
Quoted 209 lines
Signed-off-by Trond Myklebust <[email protected]>Signed-off-by Jeff Layton <[email protected]>Reviewed-by Christoph Hellwig <[email protected]>--- fs/nfsd/nfs4callback.c | 28 +++++++++++++++++++++------- fs/nfsd/nfs4state.c | 43 ++++++++++++++++++++++++++++--------------- fs/nfsd/state.h | 2 ++ 3 files changed, 51 insertions(+), 22 deletions(-)fs/nfsd/nfs4callback.c +21
-7@@ -43,7 +43,7 @@#define NFSDDBG_FACILITY NFSDDBG_PROCstatic void nfsd4_mark_cb_fault (struct nfs4_client *,int reason); -static void nfsd4_do_callback_rpc (struct work_struct *w); +static void nfsd4_run_cb_null (struct work_struct *w);#define NFSPROC4_CB_NULL 0#define NFSPROC4_CB_COMPOUND 1@@ -764,7 +764,7 @@ static void do_probe_callback(struct nfs4_client *clp) cb->cb_ops = &nfsd4_cb_probe_ops; -INIT_WORK (&cb->cb_work, nfsd4_do_callback_rpc); +INIT_WORK (&cb->cb_work, nfsd4_run_cb_null);run_nfsd4_cb (cb); }@@ -936,7 +936,7 @@ void nfsd4_shutdown_callback(struct nfs4_client *clp)set_bit (NFSD4_CLIENT_CB_KILL , &clp->cl_flags);/* * Note this won't actually result in a null callback; -* instead, nfsd4_do_callback_rpc() will detect the killed +* instead, nfsd4_run_cb_null() will detect the killed * client, destroy the rpc client, and stop: */ do_probe_callback (clp);@@ -1014,9 +1014,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;@@ -1034,6 +1033,22 @@ static void nfsd4_do_callback_rpc(struct work_struct *w) cb->cb_ops, cb); } +static void nfsd4_run_cb_null (struct work_struct *w) +{ + struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, + cb_work); +nfsd4_run_callback_rpc (cb); +} + +static void nfsd4_run_cb_recall (struct work_struct *w) +{ + struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, + cb_work); + +nfsd4_prepare_cb_recall (cb->cb_op); +nfsd4_run_callback_rpc (cb); +} +void nfsd4_cb_recall (struct nfs4_delegation *dp) {struct nfsd4_callback *cb = &dp->dl_recall;@@ -1050,8 +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_callback_rpc); +INIT_WORK (&cb->cb_work, nfsd4_run_cb_recall);run_nfsd4_cb (&dp->dl_recall); }fs/nfsd/nfs4state.c +28
-15@@ -513,7 +513,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); }@@ -521,14 +523,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 ; } }@@ -2612,6 +2618,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);@@ -2857,26 +2864,32 @@ 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 (); -block_delegations (&dp->dl_fh); -nfsd4_cb_recall (dp); }@@ -2901,11 +2914,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); } staticfs/nfsd/state.h +2
-0@@ -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;@@ -471,6 +472,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);Signature · 1 line
1.9.3
Re: [PATCH v3 004/114] nfsd: Avoid taking state_lock while holding inode lock in nfsd_break_one_deleg
Quoted 16 lines · On Wed, 2 Jul 2014 17:14:24 -0400 "J. Bruce Fields" <[email protected]>…
Quoted 10 lines · Jeff Layton
state_lockis a heavily contended global lock. We don't want to grab that while simultaneously holding theinode->i_lock.Add a new per-nfs4_file lock that we can use to protect the per-nfs4_file delegation list. Hold that while walking the list in the
break_delegcallback and queue the workqueue job for each one.The workqueue job can then take the
state_lockand do the list manipulations without thei_lockbeing held prior to starting the rpc call.The code tends to assume that the callback thread only works with the delegation struct itself and puts it when done but doesn't otherwise touch other state.
Quoted 7 lines
I wonder how this interacts with state shutdown....
E.g. in
nfs4_state_shutdown_net()we walk thedl_recall_lruand destroy everything we find, but this callback workqueue is still running so I think another delegation could get added to that list after this? Does that cause bugs?
I don't see what would prevent those bugs today and I'm unclear on why you think this patch will make things worse. All this patch really does is protect the dl_perfile list manipulations with a new per-nfs4_file lock, and have it lock that when walking the perfile list. Then it has the workqueue callback do the work of adding it to the del_recall_lru list.
Why wouldn't you have the same problem if you do the queueing to the LRU list in break_deleg codepath with the code as it is today?
That said, the delegation code is horribly complex so it's possible I've missed something here.
Quoted 12 lines
And it'd also be worth checking delegreturn and
destroy_client.Maybe there's no bug, or they just need to flush the workqueue at the appropriate point.
There's also a preexisting expire_client/laundromat vs break race:
- expire_client/laundromat adds a delegation to its local reaplist using the same
dl_recall_lrufield that a delegation uses to track its position on the recall lru and drops the state lock.
Quoted 9 lines
- a concurrent
break_leaseadds the delegation to the lru.- expire/client/laundromat then walks it reaplist and sees the lru head as just another delegation on the list....
Possibly unrelated, but it might be good to fix that first.
--b.
I was thinking that that would be fixed up in a later patch:
nfsd: Fix delegation revocation
...but now I'm not so sure. Once you drop the fi_lock, you could end up with the race above.
Honestly, the locking around the delegation code is still a mess, even with this series. I don't care much for the state_lock/recall_lock at all. It seems like we ought to be able to do something more granular there. Let me give it some thought -- maybe I can come up with a better way to handle this.
Quoted 209 lines
Quoted 209 lines
Signed-off-by Trond Myklebust <[email protected]>Signed-off-by Jeff Layton <[email protected]>Reviewed-by Christoph Hellwig <[email protected]>--- fs/nfsd/nfs4callback.c | 28 +++++++++++++++++++++------- fs/nfsd/nfs4state.c | 43 ++++++++++++++++++++++++++++--------------- fs/nfsd/state.h | 2 ++ 3 files changed, 51 insertions(+), 22 deletions(-)fs/nfsd/nfs4callback.c +21
-7@@ -43,7 +43,7 @@#define NFSDDBG_FACILITY NFSDDBG_PROCstatic void nfsd4_mark_cb_fault (struct nfs4_client *,int reason); -static void nfsd4_do_callback_rpc (struct work_struct *w); +static void nfsd4_run_cb_null (struct work_struct *w);#define NFSPROC4_CB_NULL 0#define NFSPROC4_CB_COMPOUND 1@@ -764,7 +764,7 @@ static void do_probe_callback(struct nfs4_client *clp) cb->cb_ops = &nfsd4_cb_probe_ops; -INIT_WORK (&cb->cb_work, nfsd4_do_callback_rpc); +INIT_WORK (&cb->cb_work, nfsd4_run_cb_null);run_nfsd4_cb (cb); }@@ -936,7 +936,7 @@ void nfsd4_shutdown_callback(struct nfs4_client *clp)set_bit (NFSD4_CLIENT_CB_KILL , &clp->cl_flags);/* * Note this won't actually result in a null callback; -* instead, nfsd4_do_callback_rpc() will detect the killed +* instead, nfsd4_run_cb_null() will detect the killed * client, destroy the rpc client, and stop: */ do_probe_callback (clp);@@ -1014,9 +1014,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;@@ -1034,6 +1033,22 @@ static void nfsd4_do_callback_rpc(struct work_struct *w) cb->cb_ops, cb); } +static void nfsd4_run_cb_null (struct work_struct *w) +{ + struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, + cb_work); +nfsd4_run_callback_rpc (cb); +} + +static void nfsd4_run_cb_recall (struct work_struct *w) +{ + struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, + cb_work); + +nfsd4_prepare_cb_recall (cb->cb_op); +nfsd4_run_callback_rpc (cb); +} +void nfsd4_cb_recall (struct nfs4_delegation *dp) {struct nfsd4_callback *cb = &dp->dl_recall;@@ -1050,8 +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_callback_rpc); +INIT_WORK (&cb->cb_work, nfsd4_run_cb_recall);run_nfsd4_cb (&dp->dl_recall); }fs/nfsd/nfs4state.c +28
-15@@ -513,7 +513,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); }@@ -521,14 +523,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 ; } }@@ -2612,6 +2618,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);@@ -2857,26 +2864,32 @@ 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 (); -block_delegations (&dp->dl_fh); -nfsd4_cb_recall (dp); }@@ -2901,11 +2914,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); } staticfs/nfsd/state.h +2
-0@@ -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;@@ -471,6 +472,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);Signature · 1 line
1.9.3
Signature · 1 line
Jeff Layton <[email protected]>
Re: [PATCH v3 004/114] nfsd: Avoid taking state_lock while holding inode lock in nfsd_break_one_deleg
Quoted 34 lines · Jeff Layton
Quoted 16 lines · On Wed, 2 Jul 2014 17:14:24 -0400 "J. Bruce Fields" <[email protected]>…
Quoted 10 lines · Jeff Layton
state_lockis a heavily contended global lock. We don't want to grab that while simultaneously holding theinode->i_lock.Add a new per-nfs4_file lock that we can use to protect the per-nfs4_file delegation list. Hold that while walking the list in the
break_delegcallback and queue the workqueue job for each one.The workqueue job can then take the
state_lockand do the list manipulations without thei_lockbeing held prior to starting the rpc call.The code tends to assume that the callback thread only works with the delegation struct itself and puts it when done but doesn't otherwise touch other state.
Quoted 7 lines
I wonder how this interacts with state shutdown....
E.g. in
nfs4_state_shutdown_net()we walk thedl_recall_lruand destroy everything we find, but this callback workqueue is still running so I think another delegation could get added to that list after this? Does that cause bugs?I don't see what would prevent those bugs today and I'm unclear on why you think this patch will make things worse. All this patch really does is protect the
dl_perfilelist manipulations with a new per-nfs4_file lock, and have it lock that when walking the perfile list. Then it has the workqueue callback do the work of adding it to thedel_recall_lrulist.
Quoted 2 lines
Why wouldn't you have the same problem if you do the queueing to the LRU list in
break_delegcodepath with the code as it is today?
Yeah, I suppose so.
Quoted 29 lines
That said, the delegation code is horribly complex so it's possible I've missed something here.
Quoted 12 lines
And it'd also be worth checking delegreturn and
destroy_client.Maybe there's no bug, or they just need to flush the workqueue at the appropriate point.
There's also a preexisting expire_client/laundromat vs break race:
- expire_client/laundromat adds a delegation to its local reaplist using the same
dl_recall_lrufield that a delegation uses to track its position on the recall lru and drops the state lock.Quoted 9 lines
- a concurrent
break_leaseadds the delegation to the lru.- expire/client/laundromat then walks it reaplist and sees the lru head as just another delegation on the list....
Possibly unrelated, but it might be good to fix that first.
--b.
I was thinking that that would be fixed up in a later patch:
nfsd: Fix delegation revocation
Quoted 8 lines
...but now I'm not so sure. Once you drop the
fi_lock, you could end up with the race above.Honestly, the locking around the delegation code is still a mess, even with this series. I don't care much for the state_lock/recall_lock at all. It seems like we ought to be able to do something more granular there. Let me give it some thought -- maybe I can come up with a better way to handle this.
OK. I agree that it's too complicated.
--b.
PATCH v3 5/114 nfsd: nfs4_preprocess_seqid_op should only set *stpp on success
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
Not technically a bugfix, since nothing tries to use the return pointer if this function doesn't return success, but it could be a problem with some coming changes.
--- fs/nfsd/nfs4state.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
fs/nfsd/nfs4state.c +7 -3
@@ -3964,6 +3964,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));
@@ -3973,11 +3974,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,
Signature · 1 line
1.9.3
PATCH v3 6/114 nfsd: Cleanup nfs4svc_encode_compoundres
In reply to Jeff LaytonFrom: 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.
--- 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(-)
fs/nfsd/nfs4state.c +22 -13
@@ -203,18 +203,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) {
@@ -1650,7 +1638,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;
@@ -2422,6 +2410,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) {
fs/nfsd/nfs4xdr.c +1 -14
@@ -4006,7 +4006,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 +
@@ -4020,19 +4019,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 ; }
fs/nfsd/state.h +0 -1
@@ -477,7 +477,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);
fs/nfsd/xdr4.h +1 -1
@@ -574,7 +574,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 *);
@@ -585,6 +584,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 *);
Signature · 1 line
1.9.3
PATCH v3 7/114 nfsd: declare v4.1+ openowners confirmed on creation
In reply to Jeff LaytonThere's no need to confirm an openowner in v4.1 and above, so we can go ahead and set NFS4_OO_CONFIRMED when we create openowners in those versions. This will also be necessary when we remove the client_mutex, as it'll be possible for two concurrent opens to race in versions >4.0.
--- fs/nfsd/nfs4state.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-)
fs/nfsd/nfs4state.c +10 -6
@@ -2724,7 +2724,10 @@ static void hash_openowner(struct nfs4_openowner *oo, struct nfs4_client *clp, u }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);
@@ -2733,6 +2736,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);
@@ -2998,7 +3003,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;
@@ -3408,8 +3413,6 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfmemcpy (&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 ;
@@ -3803,8 +3806,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 (stateid, +NFS4_DELEG_STID |NFS4_OPEN_STID |NFS4_LOCK_STID , + &s, cstate->minorversion, nn);if (status) goto out; status =check_stateid_generation (stateid, &s->sc_stateid,nfsd4_has_session (cstate));
Signature · 1 line
1.9.3
PATCH v3 8/114 nfsd: clean up nfsd4_close_open_stateid
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
Minor cleanup that should introduce no behavioral changes.
Currently this function just unhashes the stateid and leaves the caller to do the work of the CLOSE processing.
Change nfsd4_close_open_stateid so that it handles doing all of the work of closing a stateid. Move the handling of the unhashed stateid into it instead of doing that work in nfsd4_close. This will help isolate some coming changes to stateid handling from nfsd4_close.
--- fs/nfsd/nfs4state.c | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-)
fs/nfsd/nfs4state.c +18 -20
@@ -4140,8 +4140,25 @@ 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) { +free_generic_stateid (s); +if (list_empty (&oo->oo_owner.so_stateids)) +release_openowner (oo); + }else { + 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); + } }/*
@@ -4152,7 +4169,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);
@@ -4168,28 +4184,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) -free_generic_stateid (stp); -else - 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:if (!cstate->replay_owner)nfs4_unlock_state ();
Signature · 1 line
1.9.3
Re: [PATCH v3 008/114] nfsd: clean up nfsd4_close_open_stateid
Quoted 7 lines · Jeff Layton
From: Trond Myklebust <[email protected]>
Minor cleanup that should introduce no behavioral changes.
Currently this function just unhashes the stateid and leaves the caller to do the work of the CLOSE processing.
Quoted 6 lines
Change
nfsd4_close_open_stateidso that it handles doing all of the work of closing a stateid. Move the handling of the unhashed stateid into it instead of doing that work innfsd4_close. This will help isolate some coming changes to stateid handling fromnfsd4_close.Signed-off-by Trond Myklebust <[email protected]>
Looks good,
PATCH v3 9/114 nfsd: lock owners are not per open stateid
In reply to Jeff LaytonFrom: 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.
--- fs/nfsd/nfs4state.c | 53 +++++++++++++++++++++++++++++++++++------------------ fs/nfsd/state.h | 3 +-- 2 files changed, 36 insertions(+), 20 deletions(-)
fs/nfsd/nfs4state.c +35 -18
@@ -700,10 +700,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);
@@ -718,12 +719,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); } }
@@ -739,22 +739,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); }
@@ -2749,7 +2763,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);list_add (&stp->st_perfile, &fp->fi_stateids); stp->st_stateowner = &oo->oo_owner;
@@ -4346,7 +4360,6 @@ static void hash_lockowner(struct nfs4_lockowner *lo, unsigned int strhashval, slist_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); }/*
@@ -4391,6 +4404,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);return stp; }
@@ -4978,18 +4992,21 @@ 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 (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 (lop); +func (lst);if (++count == max)return count; }
@@ -5001,7 +5018,7 @@ static u64 nfsd_foreach_client_lock(struct nfs4_client *clp, u64 max, void (*funu64 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)
fs/nfsd/state.h +1 -2
@@ -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_perstateid;struct list_head lo_list;/* for temporary uses */ };
@@ -434,7 +433,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;
Signature · 1 line
1.9.3
PATCH v3 10/114 nfsd: Allow lockowners to hold several stateids
In reply to Jeff LaytonA lockowner can have more than one lock stateid. For instance, if a process has more than one file open and has locks on both, then the same lockowner has more than one stateid associated with it. Change it so that this reality is better reflected by the objects that nfsd uses.
--- fs/nfsd/nfs4state.c | 55 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 22 deletions(-)
fs/nfsd/nfs4state.c +33 -22
@@ -3881,12 +3881,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; }
@@ -4408,6 +4403,19 @@ alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, structreturn 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) {
@@ -4437,25 +4445,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; }
@@ -4612,7 +4623,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, } out:if (status && new_state) -release_lockowner (lock_sop); +release_lockowner_if_empty (lock_sop);nfsd4_bump_seqid (cstate, status);if (!cstate->replay_owner)nfs4_unlock_state ();
Signature · 1 line
1.9.3
PATCH v3 11/114 nfsd: NFSv4 lock-owners are not associated to a specific file
In reply to Jeff LaytonFrom: 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.
--- fs/nfsd/netns.h | 4 --- fs/nfsd/nfs4state.c | 73 ++++++++++------------------------------------------- fs/nfsd/state.h | 1 - 3 files changed, 14 insertions(+), 64 deletions(-)
fs/nfsd/netns.h +0 -4
@@ -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 512struct 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
fs/nfsd/nfs4state.c +14 -59
167 lines not on this page · raw message
fs/nfsd/state.h +0 -1
@@ -364,7 +364,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 */ };
Signature · 1 line
1.9.3
PATCH v3 12/114 nfsd: clean up nfsd4_release_lockowner
In reply to Jeff LaytonNow that we know that we won't have several lockowners with the same, owner->data, we can simplify nfsd4_release_lockowner and get rid of the lo_list in the process.
--- fs/nfsd/nfs4state.c | 46 ++++++++++++++++++++++------------------------ fs/nfsd/state.h | 1 - 2 files changed, 22 insertions(+), 25 deletions(-)
fs/nfsd/nfs4state.c +22 -24
@@ -4794,11 +4794,10 @@ 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; -struct list_head matches;unsigned int hashval =ownerstr_hashval (clid->cl_id, owner);__be32 status;struct nfsd_net *nn =net_generic (SVC_NET (rqstp), nfsd_net_id);
@@ -4813,33 +4812,32 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, goto out; 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_file, lo)) - goto out; -list_add (&lo->lo_list, &matches); +if (same_owner_str (tmp, owner, clid)) { + sop = tmp; +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_file, lo)) + goto out; } + + status = nfs_ok; +release_lockowner (lo); out:nfs4_unlock_state ();return status;
fs/nfsd/state.h +0 -1
@@ -364,7 +364,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)
Signature · 1 line
1.9.3
Re: [PATCH v3 012/114] nfsd: clean up nfsd4_release_lockowner
Quoted 5 lines · Jeff Layton
Now that we know that we won't have several lockowners with the same,
owner->data, we can simplifynfsd4_release_lockownerand get rid of thelo_listin the process.Signed-off-by Jeff Layton <[email protected]>
Looks good,
PATCH v3 13/114 nfsd: Don't get a session reference without a client reference
In reply to Jeff LaytonFrom: 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 helps ensure that it cannot, which will be a possibility when the client_mutex is removed.
--- fs/nfsd/nfs4state.c | 112 +++++++++++++++++++++++++++++++--------------------- fs/nfsd/state.h | 2 - 2 files changed, 68 insertions(+), 46 deletions(-)
fs/nfsd/nfs4state.c +68 -44
@@ -103,12 +103,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)
@@ -117,14 +111,6 @@ static __be32 mark_session_dead_locked(struct nfsd4_session *ses, int ref_held_breturn 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 ) {
@@ -203,6 +189,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) {
@@ -1126,7 +1145,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;
@@ -1146,6 +1165,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)
@@ -2161,17 +2198,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;
@@ -2185,6 +2222,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; }
@@ -2204,7 +2243,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;
@@ -2215,14 +2255,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;
@@ -2234,7 +2272,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:
@@ -2309,7 +2347,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;
@@ -2323,17 +2362,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))
@@ -2417,9 +2449,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; }
@@ -2429,18 +2459,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); } }
fs/nfsd/state.h +0 -2
@@ -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;
Signature · 1 line
1.9.3
PATCH v3 14/114 nfsd: Cleanup - Let nfsd4_lookup_stateid() take a cstate argument
In reply to Jeff LaytonFrom: 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.
--- fs/nfsd/nfs4state.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-)
fs/nfsd/nfs4state.c +9 -9
@@ -3792,12 +3792,14 @@ static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid) } } -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;
@@ -3843,9 +3845,9 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,nfs4_lock_state (); - status =nfsd4_lookup_stateid (stateid, + status =nfsd4_lookup_stateid (cstate, stateid,NFS4_DELEG_STID |NFS4_OPEN_STID |NFS4_LOCK_STID , - &s, cstate->minorversion, nn); + &s, nn);if (status) goto out; status =check_stateid_generation (stateid, &s->sc_stateid,nfsd4_has_session (cstate));
@@ -4015,8 +4017,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);
@@ -4240,8 +4241,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);
Signature · 1 line
1.9.3
PATCH v3 15/114 nfsd: Allow struct nfsd4_compound_state to cache the nfs4_client
In reply to Jeff LaytonWe want to use the nfsd4_compound_state to cache the nfs4_client in order to optimise away extra lookups of the clid.
In the v4.0 case, we use this to ensure that we only have to look up the client at most once per compound for each call into lookup_clientid. For v4.1+ we set the pointer in the cstate during SEQUENCE processing so we should never need to do a search for it.
--- fs/nfsd/nfs4state.c | 74 +++++++++++++++++++++++++++++++++++++---------------- fs/nfsd/xdr4.h | 1 + 2 files changed, 53 insertions(+), 22 deletions(-)
fs/nfsd/nfs4state.c +52 -22
@@ -189,6 +189,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;
@@ -2395,6 +2404,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);
@@ -2429,6 +2439,7 @@ nfsd4_sequence(struct svc_rqst *rqstp, cstate->slot = slot; cstate->session = session; + cstate->clp = clp; out:switch (clp->cl_cb_state) {
@@ -2465,7 +2476,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
@@ -2997,6 +3009,38 @@ static __be32 nfsd4_check_seqid(struct nfsd4_compound_state *cstate, struct nfs4return nfserr_bad_seqid; } +static __be32 lookup_clientid (clientid_t *clid, +struct nfsd4_compound_state *cstate, +struct nfsd_net *nn) +{ +struct nfs4_client *found; + +if (cstate->clp) { + found = cstate->clp; +if (!same_clid (&found->cl_clientid, clid)) +return nfserr_stale_clientid; +return nfs_ok; + } + +if (STALE_CLIENTID (clid, nn)) +return nfserr_stale_clientid; + +/* +* For v4.1+ we get the client in the SEQUENCE op. If we don't have one +* cached already then we know this is for is for v4.0 and "sessions" +* will be false. +*/ +WARN_ON_ONCE (cstate->session); + found =find_confirmed_client (clid, false, nn); +if (!found) +return nfserr_expired; + +/* Cache the nfs4_client in cstate! */ + cstate->clp = found; +atomic_inc (&found->cl_refcount); +return nfs_ok; +} +__be32 nfsd4_process_open1 (struct nfsd4_compound_state *cstate,struct nfsd4_open *open,struct nfsd_net *nn)
@@ -3509,18 +3553,6 @@ void nfsd4_cleanup_open_state(struct nfsd4_open *open, __be32 status)free_generic_stateid (open->op_stp); } -static __be32 lookup_clientid (clientid_t *clid,bool session,struct nfsd_net *nn,struct nfs4_client **clp) -{ -struct nfs4_client *found; - -if (STALE_CLIENTID (clid, nn)) -return nfserr_stale_clientid; - found =find_confirmed_client (clid, session, nn); -if (clp) - *clp = found; -return found ? nfs_ok : nfserr_expired; -} -__be32 nfsd4_renew (struct svc_rqst *rqstp,struct nfsd4_compound_state *cstate,clientid_t *clid)
@@ -3532,9 +3564,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 )
@@ -3797,22 +3830,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;
@@ -4662,7 +4692,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; }
@@ -4831,7 +4861,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;
fs/nfsd/xdr4.h +1 -0
@@ -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;
Signature · 1 line
1.9.3
Re: [PATCH v3 015/114] nfsd: Allow struct nfsd4_compound_state to cache the nfs4_client
Quoted 9 lines · Jeff Layton
We want to use the
nfsd4_compound_stateto cache thenfs4_clientin order to optimise away extra lookups of the clid.In the v4.0 case, we use this to ensure that we only have to look up the client at most once per compound for each call into
lookup_clientid. For v4.1+ we set the pointer in the cstate during SEQUENCE processing so we should never need to do a search for it.Signed-off-by Trond Myklebust <[email protected]>
Looks good,
Re: [PATCH v3 015/114] nfsd: Allow struct nfsd4_compound_state to cache the nfs4_client
In reply to Jeff LaytonQuoted 4 lines · Jeff Layton
fs/nfsd/nfs4state.c +0
-0@@ -2997,6 +3009,38 @@ static __be32 nfsd4_check_seqid(struct nfsd4_compound_state *cstate, struct nfs4return nfserr_bad_seqid; }
fs/nfsd/nfs4state.c static __be32 nfsd4_check_seqid(struct nfsd4_compound_state *cstate, struct nfs4 · line 3021
Quoted 10 lines
fs/nfsd/nfs4state.c +10
-0+static __be32 lookup_clientid (clientid_t *clid, +struct nfsd4_compound_state *cstate, +struct nfsd_net *nn) +{ +struct nfs4_client *found; + +if (cstate->clp) { + found = cstate->clp; +if (!same_clid (&found->cl_clientid, clid)) +return nfserr_stale_clientid;
That's new behavior, isn't it?
But sending a single compound that references multiple clients sounds nuts even in the 4.0 case, OK. Applying.
(And I've merged all but the delegation locking change up through here so far.)
--b.
Quoted 115 lines
fs/nfsd/nfs4state.c +29
-21+return nfs_ok; + } + +if (STALE_CLIENTID (clid, nn)) +return nfserr_stale_clientid; + +/* +* For v4.1+ we get the client in the SEQUENCE op. If we don't have one +* cached already then we know this is for is for v4.0 and "sessions" +* will be false. +*/ +WARN_ON_ONCE (cstate->session); + found =find_confirmed_client (clid, false, nn); +if (!found) +return nfserr_expired; + +/* Cache the nfs4_client in cstate! */ + cstate->clp = found; +atomic_inc (&found->cl_refcount); +return nfs_ok; +} +__be32 nfsd4_process_open1 (struct nfsd4_compound_state *cstate,struct nfsd4_open *open,struct nfsd_net *nn)@@ -3509,18 +3553,6 @@ void nfsd4_cleanup_open_state(struct nfsd4_open *open, __be32 status)free_generic_stateid (open->op_stp); } -static __be32 lookup_clientid (clientid_t *clid,bool session,struct nfsd_net *nn,struct nfs4_client **clp) -{ -struct nfs4_client *found; - -if (STALE_CLIENTID (clid, nn)) -return nfserr_stale_clientid; - found =find_confirmed_client (clid, session, nn); -if (clp) - *clp = found; -return found ? nfs_ok : nfserr_expired; -} -__be32 nfsd4_renew (struct svc_rqst *rqstp,struct nfsd4_compound_state *cstate,clientid_t *clid)@@ -3532,9 +3564,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 )@@ -3797,22 +3830,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;@@ -4662,7 +4692,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; }@@ -4831,7 +4861,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;fs/nfsd/xdr4.h +1
-0@@ -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;Signature · 1 line
1.9.3
Re: [PATCH v3 015/114] nfsd: Allow struct nfsd4_compound_state to cache the nfs4_client
Quoted 5 lines · On Thu, 3 Jul 2014 11:18:19 -0400 "J. Bruce Fields" <[email protected]>…
Quoted 4 lines · Jeff Layton
fs/nfsd/nfs4state.c +0
-0@@ -2997,6 +3009,38 @@ static __be32 nfsd4_check_seqid(struct nfsd4_compound_state *cstate, struct nfs4return nfserr_bad_seqid; }
Quoted 13 lines
fs/nfsd/nfs4state.c static __be32 nfsd4_check_seqid(struct nfsd4_compound_state *cstate, struct nfs4 · line 3021
Quoted 10 lines
fs/nfsd/nfs4state.c +10
-0+static __be32 lookup_clientid (clientid_t *clid, +struct nfsd4_compound_state *cstate, +struct nfsd_net *nn) +{ +struct nfs4_client *found; + +if (cstate->clp) { + found = cstate->clp; +if (!same_clid (&found->cl_clientid, clid)) +return nfserr_stale_clientid;That's new behavior, isn't it?
Yeah, I suppose it is, but it's hard to understand a valid use-case for sending multiple ops in a compound with different clientids. Certainly no well-behaved client would do that, would it? (Hmm, that might be an interesting pynfs test, come to think of it).
Quoted 8 lines
But sending a single compound that references multiple clients sounds nuts even in the 4.0 case, OK. Applying.
(And I've merged all but the delegation locking change up through here so far.)
--b.
Thanks!
Quoted 121 lines
Quoted 121 lines
fs/nfsd/nfs4state.c +8
-0+return nfs_ok; + } + +if (STALE_CLIENTID (clid, nn)) +return nfserr_stale_clientid; + +/* +* For v4.1+ we get the client in the SEQUENCE op. If we don't have one
(quoted diff) +1
-0+ * cached already then we know this is for is for v4.0 and"sessions"
fs/nfsd/nfs4state.c +13
-11+* will be false. +*/ +WARN_ON_ONCE (cstate->session); + found =find_confirmed_client (clid, false, nn); +if (!found) +return nfserr_expired; + +/* Cache the nfs4_client in cstate! */ + cstate->clp = found; +atomic_inc (&found->cl_refcount); +return nfs_ok; +} +__be32 nfsd4_process_open1 (struct nfsd4_compound_state *cstate,struct nfsd4_open *open,struct nfsd_net *nn)@@ -3509,18 +3553,6 @@ void nfsd4_cleanup_open_state(struct nfsd4_open *open, __be32 status) free_generic_stateid(open->op_stp); } -static __be32 lookup_clientid (clientid_t *clid,bool session, struct nfsd_net *nn, struct nfs4_client **clp) -{ -struct nfs4_client *found; - -if (STALE_CLIENTID (clid, nn)) -return nfserr_stale_clientid; - found =find_confirmed_client (clid, session, nn); -if (clp) - *clp = found; -return found ? nfs_ok : nfserr_expired; -} - __be32 nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state*cstate,
clientid_t*clid)fs/nfsd/nfs4state.c +2
-1@@ -3532,9 +3564,10 @@ nfsd4_renew(struct svc_rqst *rqstp, structnfsd4_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)fs/nfsd/nfs4state.c +0
-0&& clp->cl_cb_state != NFSD4_CB_UP)@@ -3797,22 +3830,19 @@ nfsd4_lookup_stateid(struct
nfsd4_compound_state*cstate,stateid_t*stateid, unsigned char typemask, structnfs4_stid**s, structnfsd_net*nn)
{fs/nfsd/nfs4state.c +0
-3-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,
(quoted diff) +1
-1- nn, &cl); + status = lookup_clientid(&stateid->si_opaque.so_clid,cstate, nn); if (status ==
nfserr_stale_clientid) {fs/nfsd/nfs4state.c +3
-3-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;@@ -4662,7 +4692,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, structnfsd4_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;fs/nfsd/nfs4state.c +1
-1}@@ -4831,7 +4861,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;fs/nfsd/xdr4.h +1
-0@@ -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;Signature · 1 line
1.9.3
Signature · 1 line
Jeff Layton <[email protected]>
Re: [PATCH v3 015/114] nfsd: Allow struct nfsd4_compound_state to cache the nfs4_client
Quoted 22 lines · Jeff Layton
Quoted 5 lines · On Thu, 3 Jul 2014 11:18:19 -0400 "J. Bruce Fields" <[email protected]>…
Quoted 4 lines · Jeff Layton
fs/nfsd/nfs4state.c +0
-0@@ -2997,6 +3009,38 @@ static __be32 nfsd4_check_seqid(struct nfsd4_compound_state *cstate, struct nfs4return nfserr_bad_seqid; }Quoted 13 lines
fs/nfsd/nfs4state.c static __be32 nfsd4_check_seqid(struct nfsd4_compound_state *cstate, struct nfs4 · line 3021
Quoted 10 lines
fs/nfsd/nfs4state.c +10
-0+static __be32 lookup_clientid (clientid_t *clid, +struct nfsd4_compound_state *cstate, +struct nfsd_net *nn) +{ +struct nfs4_client *found; + +if (cstate->clp) { + found = cstate->clp; +if (!same_clid (&found->cl_clientid, clid)) +return nfserr_stale_clientid;That's new behavior, isn't it?
Quoted 4 lines
Yeah, I suppose it is, but it's hard to understand a valid use-case for sending multiple ops in a compound with different clientids. Certainly no well-behaved client would do that, would it? (Hmm, that might be an interesting pynfs test, come to think of it).
We could ask for a clarification in 3530bis if there's not already something there that clearly forbids this, but I'm not sure if it's even worth it.
--b.
Re: [PATCH v3 015/114] nfsd: Allow struct nfsd4_compound_state to cache the nfs4_client
Quoted 23 lines · On Thu, 3 Jul 2014 11:32:08 -0400 "J. Bruce Fields" <[email protected]>…
Quoted 22 lines · Jeff Layton
Quoted 18 lines · On Thu, 3 Jul 2014 11:18:19 -0400 "J. Bruce Fields" <[email protected]>…
Quoted 4 lines · Jeff Layton
fs/nfsd/nfs4state.c +0
-0@@ -2997,6 +3009,38 @@ static __be32 nfsd4_check_seqid(struct nfsd4_compound_state *cstate, struct nfs4return nfserr_bad_seqid; }fs/nfsd/nfs4state.c static __be32 nfsd4_check_seqid(struct nfsd4_compound_state *cstate, struct nfs4 · line 3021
Quoted 10 lines
fs/nfsd/nfs4state.c +10
-0+static __be32 lookup_clientid (clientid_t *clid, +struct nfsd4_compound_state *cstate, +struct nfsd_net *nn) +{ +struct nfs4_client *found; + +if (cstate->clp) { + found = cstate->clp; +if (!same_clid (&found->cl_clientid, clid)) +return nfserr_stale_clientid;That's new behavior, isn't it?
Quoted 9 lines
Quoted 4 lines
Yeah, I suppose it is, but it's hard to understand a valid use-case for sending multiple ops in a compound with different clientids. Certainly no well-behaved client would do that, would it? (Hmm, that might be an interesting pynfs test, come to think of it).
We could ask for a clarification in 3530bis if there's not already something there that clearly forbids this, but I'm not sure if it's even worth it.
I'm not inclined to pursue it, unless Tom thinks it's worthwhile. Tom, do you happen to care?
Signature · 1 line
Jeff Layton <[email protected]>
Re: [PATCH v3 015/114] nfsd: Allow struct nfsd4_compound_state to cache the nfs4_client
In reply to J. Bruce FieldsQuoted 10 lines · J. Bruce Fields
Quoted 10 lines
Quoted 9 lines
fs/nfsd/nfs4state.c static __be32 nfsd4_check_seqid(struct nfsd4_compound_state *cstate, struct nfs4 · line 3021
Quoted 6 lines
fs/nfsd/nfs4state.c +6
-0+struct nfs4_client *found; + +if (cstate->clp) { + found = cstate->clp; +if (!same_clid (&found->cl_clientid, clid)) +return nfserr_stale_clientid;That's new behavior, isn't it?
Quoted 8 lines
Quoted 4 lines
Yeah, I suppose it is, but it's hard to understand a valid use-case for sending multiple ops in a compound with different clientids. Certainly no well-behaved client would do that, would it? (Hmm, that might be an interesting pynfs test, come to think of it).
We could ask for a clarification in 3530bis if there's not already something there that clearly forbids this, but I'm not sure if it's even worth it.
Or just handle it and be done with me. After all we'd just need to put the existing client, and store the new one in the cstate.
Re: [PATCH v3 015/114] nfsd: Allow struct nfsd4_compound_state to cache the nfs4_client
Quoted 16 lines · Christoph Hellwig
Quoted 15 lines · J. Bruce Fields
Quoted 14 lines
Quoted 9 lines
fs/nfsd/nfs4state.c static __be32 nfsd4_check_seqid(struct nfsd4_compound_state *cstate, struct nfs4 · line 3021
Quoted 6 lines
fs/nfsd/nfs4state.c +6
-0+struct nfs4_client *found; + +if (cstate->clp) { + found = cstate->clp; +if (!same_clid (&found->cl_clientid, clid)) +return nfserr_stale_clientid;That's new behavior, isn't it?
Yeah, I suppose it is, but it's hard to understand a valid use-case for sending multiple ops in a compound with different clientids. Certainly no well-behaved client would do that, would it? (Hmm, that might be an interesting pynfs test, come to think of it).
Quoted 6 lines
Quoted 3 lines
We could ask for a clarification in 3530bis if there's not already something there that clearly forbids this, but I'm not sure if it's even worth it.
Or just handle it and be done with me. After all we'd just need to put the existing client, and store the new one in the cstate.
It is definitely not allowed in NFSv4.1. There is a special place in hell^W^W^Werrorcode NFS4ERR_SEQUENCE_POS that tells you that your compound is insane.
Signature · 5 lines
Trond Myklebust Linux NFS client maintainer, PrimaryData [email protected]
Re: [PATCH v3 015/114] nfsd: Allow struct nfsd4_compound_state to cache the nfs4_client
In reply to Christoph HellwigQuoted 16 lines · On Thu, 3 Jul 2014 09:11:40 -0700 Christoph Hellwig <[email protected]> wrote:
Quoted 15 lines · J. Bruce Fields
Quoted 14 lines
Quoted 9 lines
fs/nfsd/nfs4state.c static __be32 nfsd4_check_seqid(struct nfsd4_compound_state *cstate, struct nfs4 · line 3021
Quoted 6 lines
fs/nfsd/nfs4state.c +6
-0+struct nfs4_client *found; + +if (cstate->clp) { + found = cstate->clp; +if (!same_clid (&found->cl_clientid, clid)) +return nfserr_stale_clientid;That's new behavior, isn't it?
Yeah, I suppose it is, but it's hard to understand a valid use-case for sending multiple ops in a compound with different clientids. Certainly no well-behaved client would do that, would it? (Hmm, that might be an interesting pynfs test, come to think of it).
Quoted 7 lines
Quoted 3 lines
We could ask for a clarification in 3530bis if there's not already something there that clearly forbids this, but I'm not sure if it's even worth it.
Or just handle it and be done with me. After all we'd just need to put the existing client, and store the new one in the cstate.
Meh, ok... I suppose there's no harm in doing that and since that's the behavior that was there before, we should probably go with it for the v4.0 case. I'll have the next iteration do that instead.
Thanks,
Signature · 1 line
Jeff Layton <[email protected]>
Re: [PATCH v3 015/114] nfsd: Allow struct nfsd4_compound_state to cache the nfs4_client
In reply to Jeff LaytonQuoted 7 lines · Jeff Layton
We want to use the
nfsd4_compound_stateto cache thenfs4_clientin order to optimise away extra lookups of the clid.In the v4.0 case, we use this to ensure that we only have to look up the client at most once per compound for each call into
lookup_clientid. For v4.1+ we set the pointer in the cstate during SEQUENCE processing so we should never need to do a search for it.
The connectathon locking test is failing for me in the nfsv4/krb5i case as of this commit.
Which makes no sense to me whatsoever, so it's entirely possible this is some unrelated problem on my side. I'll let you know when I've figured out anything more.
--b.
Quoted 182 lines
Signed-off-by Trond Myklebust <[email protected]>--- fs/nfsd/nfs4state.c | 74 +++++++++++++++++++++++++++++++++++++---------------- fs/nfsd/xdr4.h | 1 + 2 files changed, 53 insertions(+), 22 deletions(-)fs/nfsd/nfs4state.c +52
-22@@ -189,6 +189,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;@@ -2395,6 +2404,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);@@ -2429,6 +2439,7 @@ nfsd4_sequence(struct svc_rqst *rqstp, cstate->slot = slot; cstate->session = session; + cstate->clp = clp; out:switch (clp->cl_cb_state) {@@ -2465,7 +2476,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@@ -2997,6 +3009,38 @@ static __be32 nfsd4_check_seqid(struct nfsd4_compound_state *cstate, struct nfs4return nfserr_bad_seqid; } +static __be32 lookup_clientid (clientid_t *clid, +struct nfsd4_compound_state *cstate, +struct nfsd_net *nn) +{ +struct nfs4_client *found; + +if (cstate->clp) { + found = cstate->clp; +if (!same_clid (&found->cl_clientid, clid)) +return nfserr_stale_clientid; +return nfs_ok; + } + +if (STALE_CLIENTID (clid, nn)) +return nfserr_stale_clientid; + +/* +* For v4.1+ we get the client in the SEQUENCE op. If we don't have one +* cached already then we know this is for is for v4.0 and "sessions" +* will be false. +*/ +WARN_ON_ONCE (cstate->session); + found =find_confirmed_client (clid, false, nn); +if (!found) +return nfserr_expired; + +/* Cache the nfs4_client in cstate! */ + cstate->clp = found; +atomic_inc (&found->cl_refcount); +return nfs_ok; +} +__be32 nfsd4_process_open1 (struct nfsd4_compound_state *cstate,struct nfsd4_open *open,struct nfsd_net *nn)@@ -3509,18 +3553,6 @@ void nfsd4_cleanup_open_state(struct nfsd4_open *open, __be32 status)free_generic_stateid (open->op_stp); } -static __be32 lookup_clientid (clientid_t *clid,bool session,struct nfsd_net *nn,struct nfs4_client **clp) -{ -struct nfs4_client *found; - -if (STALE_CLIENTID (clid, nn)) -return nfserr_stale_clientid; - found =find_confirmed_client (clid, session, nn); -if (clp) - *clp = found; -return found ? nfs_ok : nfserr_expired; -} -__be32 nfsd4_renew (struct svc_rqst *rqstp,struct nfsd4_compound_state *cstate,clientid_t *clid)@@ -3532,9 +3564,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 )@@ -3797,22 +3830,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;@@ -4662,7 +4692,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; }@@ -4831,7 +4861,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;fs/nfsd/xdr4.h +1
-0@@ -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;Signature · 1 line
1.9.3
Re: [PATCH v3 015/114] nfsd: Allow struct nfsd4_compound_state to cache the nfs4_client
Quoted 9 lines · J. Bruce Fields
Quoted 7 lines · Jeff Layton
We want to use the
nfsd4_compound_stateto cache thenfs4_clientin order to optimise away extra lookups of the clid.In the v4.0 case, we use this to ensure that we only have to look up the client at most once per compound for each call into
lookup_clientid. For v4.1+ we set the pointer in the cstate during SEQUENCE processing so we should never need to do a search for it.
Quoted 6 lines
The connectathon locking test is failing for me in the nfsv4/krb5i case as of this commit.
Which makes no sense to me whatsoever, so it's entirely possible this is some unrelated problem on my side. I'll let you know when I've figured out anything more.
It's intermittent.
I've reproduced it on the previous commit so I know at least that this one isn't at fault.
I doubt it's really dependent on krb5i, at most that's probably just making it more likely to reproduce.
--b.
Re: [PATCH v3 015/114] nfsd: Allow struct nfsd4_compound_state to cache the nfs4_client
Quoted 19 lines · On Thu, 3 Jul 2014 17:35:26 -0400 "J. Bruce Fields" <[email protected]>…
Quoted 9 lines · J. Bruce Fields
Quoted 7 lines · Jeff Layton
We want to use the
nfsd4_compound_stateto cache thenfs4_clientin order to optimise away extra lookups of the clid.In the v4.0 case, we use this to ensure that we only have to look up the client at most once per compound for each call into
lookup_clientid. For v4.1+ we set the pointer in the cstate during SEQUENCE processing so we should never need to do a search for it.Quoted 6 lines
The connectathon locking test is failing for me in the nfsv4/krb5i case as of this commit.
Which makes no sense to me whatsoever, so it's entirely possible this is some unrelated problem on my side. I'll let you know when I've figured out anything more.
It's intermittent.
Quoted 7 lines
I've reproduced it on the previous commit so I know at least that this one isn't at fault.
I doubt it's really dependent on krb5i, at most that's probably just making it more likely to reproduce.
--b.
I haven't been able to reproduce it yet, but I suspect you're hitting this check in lookup_or_create_lock_state:
/* 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)
goto out;Hmmm...there are some changes that go in in this patch wrt to lock seqid handling:
nfsd: clean up lockowner refcounting when finding them
Perhaps those need to go in earlier? Though when I looked at that originally, I figured that we wouldn't need those until the refcounting changes went in (which is why I didn't put them in). It might be interesting to look at traces and see whether they're consistent with hitting that check (or maybe put some debug printks in)?
Signature · 1 line
Jeff Layton <[email protected]>
Re: [PATCH v3 015/114] nfsd: Allow struct nfsd4_compound_state to cache the nfs4_client
In reply to J. Bruce FieldsQuoted 19 lines · On Thu, 3 Jul 2014 17:35:26 -0400 "J. Bruce Fields" <[email protected]>…
Quoted 9 lines · J. Bruce Fields
Quoted 7 lines · Jeff Layton
We want to use the
nfsd4_compound_stateto cache thenfs4_clientin order to optimise away extra lookups of the clid.In the v4.0 case, we use this to ensure that we only have to look up the client at most once per compound for each call into
lookup_clientid. For v4.1+ we set the pointer in the cstate during SEQUENCE processing so we should never need to do a search for it.Quoted 6 lines
The connectathon locking test is failing for me in the nfsv4/krb5i case as of this commit.
Which makes no sense to me whatsoever, so it's entirely possible this is some unrelated problem on my side. I'll let you know when I've figured out anything more.
It's intermittent.
Quoted 7 lines
I've reproduced it on the previous commit so I know at least that this one isn't at fault.
I doubt it's really dependent on krb5i, at most that's probably just making it more likely to reproduce.
--b.
Bruce,
Does this patch help? I suspect this is where the bug crept in, but I'm unclear on why it would be intermittent...
FWIW, this all gets cleaned up in a later patch that changes how the refcounting on lock and openowners works.
Signature · 1 line
Jeff Layton <[email protected]>
Quoted 1 line
From
84884320b983eaeb68c652de8fe4b48aad4052c3Mon Sep 17 00:00:00 2001
From: Jeff Layton <[email protected]>
Date: Thu, 3 Jul 2014 18:20:31 -0400
Subject: [PATCH] nfsd: fix lock stateid cleanup on error in nfsd4_lock
commit 43a1b041e3b changed nfsd4_lock to call release_lockowner_if_empty if the the lock stateid was new and the function was going to return an error.
This is wrong. The lockowner will never be empty in that situation since it still has the new lock stateid attached to it. Change it to call release_lock_stateid instead, which will destroy the lock stateid and then release the lockowner if it's empty at that point.
--- fs/nfsd/nfs4state.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/nfsd/nfs4state.c +1 -1
@@ -4600,7 +4600,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, } out:if (status && new_state) -release_lockowner_if_empty (lock_sop); +release_lock_stateid (lock_stp);nfsd4_bump_seqid (cstate, status);if (!cstate->replay_owner)nfs4_unlock_state ();
Signature · 1 line
1.9.3
Re: [PATCH v3 015/114] nfsd: Allow struct nfsd4_compound_state to cache the nfs4_client
Quoted 30 lines · On Thu, 3 Jul 2014 18:31:15 -0400 Jeff Layton <[email protected]>…
Quoted 19 lines · On Thu, 3 Jul 2014 17:35:26 -0400 "J. Bruce Fields" <[email protected]>…
Quoted 9 lines · J. Bruce Fields
Quoted 7 lines · Jeff Layton
We want to use the
nfsd4_compound_stateto cache thenfs4_clientin order to optimise away extra lookups of the clid.In the v4.0 case, we use this to ensure that we only have to look up the client at most once per compound for each call into
lookup_clientid. For v4.1+ we set the pointer in the cstate during SEQUENCE processing so we should never need to do a search for it.Quoted 6 lines
The connectathon locking test is failing for me in the nfsv4/krb5i case as of this commit.
Which makes no sense to me whatsoever, so it's entirely possible this is some unrelated problem on my side. I'll let you know when I've figured out anything more.
It's intermittent.
Quoted 7 lines
I've reproduced it on the previous commit so I know at least that this one isn't at fault.
I doubt it's really dependent on krb5i, at most that's probably just making it more likely to reproduce.
--b.
Quoted 8 lines
Bruce,
Does this patch help? I suspect this is where the bug crept in, but I'm unclear on why it would be intermittent...
FWIW, this all gets cleaned up in a later patch that changes how the refcounting on lock and openowners works.
I was finally able to reproduce this after a running the cthon lock tests in a loop, with krb5i. With the patch that I sent earlier, I was able to run 100 iterations of it without a failure, so I think that was the bug.
Cheers!
Signature · 1 line
Jeff Layton <[email protected]>
Re: [PATCH v3 015/114] nfsd: Allow struct nfsd4_compound_state to cache the nfs4_client
Quoted 38 lines · Jeff Layton
Quoted 35 lines · On Thu, 3 Jul 2014 18:31:15 -0400 Jeff Layton <[email protected]>…
Quoted 19 lines · On Thu, 3 Jul 2014 17:35:26 -0400 "J. Bruce Fields" <[email protected]>…
Quoted 9 lines · J. Bruce Fields
Quoted 7 lines · Jeff Layton
We want to use the
nfsd4_compound_stateto cache thenfs4_clientin order to optimise away extra lookups of the clid.In the v4.0 case, we use this to ensure that we only have to look up the client at most once per compound for each call into
lookup_clientid. For v4.1+ we set the pointer in the cstate during SEQUENCE processing so we should never need to do a search for it.Quoted 6 lines
The connectathon locking test is failing for me in the nfsv4/krb5i case as of this commit.
Which makes no sense to me whatsoever, so it's entirely possible this is some unrelated problem on my side. I'll let you know when I've figured out anything more.
It's intermittent.
Quoted 7 lines
I've reproduced it on the previous commit so I know at least that this one isn't at fault.
I doubt it's really dependent on krb5i, at most that's probably just making it more likely to reproduce.
--b.
Bruce,
Does this patch help? I suspect this is where the bug crept in, but I'm unclear on why it would be intermittent...
Quoted 8 lines
Quoted 3 lines
FWIW, this all gets cleaned up in a later patch that changes how the refcounting on lock and openowners works.
I was finally able to reproduce this after a running the cthon lock tests in a loop, with krb5i. With the patch that I sent earlier, I was able to run 100 iterations of it without a failure, so I think that was the bug.
Thanks! That seems to be holding up for me too.
I'll continue slowly applying your patches to for-3.17.
--b.
PATCH v3 16/114 nfsd: Convert nfsd4_process_open1() to work with lookup_clientid()
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
...and have alloc_init_open_stateowner just use the cstate->clp pointer instead of passing in a clp separately. This allows us to use the cached nfs4_client pointer in the cstate instead of having to look it up again.
--- fs/nfsd/nfs4state.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
fs/nfsd/nfs4state.c +7 -7
@@ -2773,10 +2773,10 @@ static void hash_openowner(struct nfs4_openowner *oo, struct nfs4_client *clp, u }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 nfs4_openowner *oo; oo =alloc_stateowner (openowner_slab, &open->op_owner, clp);
@@ -3065,10 +3065,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 )) {
@@ -3084,7 +3084,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;
Signature · 1 line
1.9.3
PATCH v3 17/114 nfsd: Always use lookup_clientid() in nfsd4_process_open1
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
In later patches, we'll be moving the stateowner table into the nfs4_client, and by doing this we ensure that we have a cached nfs4_client pointer.
--- fs/nfsd/nfs4state.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-)
fs/nfsd/nfs4state.c +5 -6
@@ -3061,19 +3061,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;
@@ -3081,7 +3081,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);
Signature · 1 line
1.9.3
PATCH v3 18/114 nfsd: Convert nfs4_check_open_reclaim() to work with lookup_clientid()
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
lookup_clientid is preferable to find_confirmed_client since it's able to use the cached client in the compound state.
--- fs/nfsd/nfs4proc.c | 3 +-- fs/nfsd/nfs4state.c | 15 ++++++++++----- fs/nfsd/state.h | 3 ++- 3 files changed, 13 insertions(+), 8 deletions(-)
fs/nfsd/nfs4proc.c +1 -2
@@ -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 ;
fs/nfsd/nfs4state.c +10 -5
@@ -4980,16 +4980,21 @@ 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; +if (nfsd4_client_record_check (cstate->clp)) +return nfserr_reclaim_bad; + +return nfs_ok; }#ifdef CONFIG_NFSD_FAULT_INJECTION
fs/nfsd/state.h +2 -1
@@ -458,7 +458,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_probe_callback (struct nfs4_client *clp);extern void nfsd4_probe_callback_sync (struct nfs4_client *clp);
Signature · 1 line
1.9.3
PATCH v3 19/114 nfsd: Move the delegation reference counter into the struct nfs4_stid
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
We will want to add reference counting to the lock stateid and open stateids too in later patches.
--- fs/nfsd/nfs4state.c | 13 +++++++------ fs/nfsd/state.h | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-)
fs/nfsd/nfs4state.c +7 -6
@@ -360,6 +360,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.
@@ -481,7 +482,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, ¤t_fh->fh_handle); dp->dl_time =0 ; -atomic_set (&dp->dl_count,1 );return dp; }
@@ -500,7 +500,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--; }
@@ -2948,10 +2948,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);block_delegations (&dp->dl_fh);nfsd4_cb_recall (dp); }
fs/nfsd/state.h +1 -1
@@ -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;
Signature · 1 line
1.9.3
PATCH v3 20/114 nfsd4: use cl_lock to synchronize all stateid idr calls
In reply to Jeff LaytonFrom: Benny Halevy <[email protected]>
Currently, this is serialized by the client_mutex, which is slated for removal. Add finer-grained locking here.
--- fs/nfsd/nfs4state.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-)
fs/nfsd/nfs4state.c +13 -4
@@ -343,7 +343,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;
@@ -351,7 +350,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;
@@ -487,9 +490,11 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct svstatic 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)
@@ -1263,7 +1268,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); }
@@ -1486,7 +1493,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;
Signature · 1 line
1.9.3
PATCH v3 21/114 nfsd: Add fine grained protection for the nfs4_file->fi_stateids list
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
Access to this list is currently serialized by the client_mutex. Add finer grained locking around this list in preparation for its removal.
--- fs/nfsd/nfs4state.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-)
fs/nfsd/nfs4state.c +17 -3
@@ -716,7 +716,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); }
@@ -2809,7 +2813,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); -list_add (&stp->st_perfile, &fp->fi_stateids); stp->st_stateowner = &oo->oo_owner;get_nfs4_file (fp); stp->st_file = fp;
@@ -2818,6 +2821,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
@@ -2925,6 +2931,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))
@@ -2932,6 +2939,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; }
@@ -3163,6 +3171,7 @@ nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_ol_ststruct 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 )
@@ -3171,9 +3180,12 @@ nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_ol_stif (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; }
@@ -4421,7 +4433,6 @@ alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, structif (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);
@@ -4430,6 +4441,9 @@ alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, struct 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);return stp; }
Signature · 1 line
1.9.3
PATCH v3 22/114 nfsd: Add a mutex to protect the NFSv4.0 open owner replay cache
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
We don't want to rely on the client_mutex for protection in the case of NFSv4 open owners. Instead, we add a mutex that will only be taken for NFSv4.0 state mutating operations, and that will be released once the entire compound is done.
--- fs/nfsd/nfs4proc.c | 13 +++++-------- fs/nfsd/nfs4state.c | 21 ++++++++------------- fs/nfsd/nfs4xdr.c | 2 -- fs/nfsd/state.h | 1 + fs/nfsd/xdr4.h | 21 +++++++++++++++++++++ 5 files changed, 35 insertions(+), 23 deletions(-)
fs/nfsd/nfs4proc.c +5 -8
@@ -468,11 +468,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; }
@@ -1394,10 +1394,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);
fs/nfsd/nfs4state.c +8 -13
@@ -885,7 +885,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)
@@ -2754,6 +2754,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)
@@ -4072,8 +4073,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)
@@ -4134,8 +4134,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; }
@@ -4217,8 +4216,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; }
@@ -4273,8 +4271,7 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,nfsd4_close_open_stateid (stp); out: -if (!cstate->replay_owner) -nfs4_unlock_state (); +nfs4_unlock_state ();return status; }
@@ -4668,8 +4665,7 @@ out:if (status && new_state)release_lockowner_if_empty (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)
@@ -4830,8 +4826,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;
fs/nfsd/nfs4xdr.c +0 -2
@@ -3931,8 +3931,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)
fs/nfsd/state.h +1 -0
@@ -327,6 +327,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 ]; };
fs/nfsd/xdr4.h +21 -0
@@ -74,6 +74,27 @@ static inline bool nfsd4_has_session(struct nfsd4_compound_state *cs)return cs->slot !=NULL ; } +static inline void +nfsd4_cstate_assign_replay (struct nfsd4_compound_state *cstate, +struct nfs4_stateowner *so) +{ +if (!nfsd4_has_session (cstate)) { +mutex_lock (&so->so_replay.rp_mutex); + cstate->replay_owner = so; + } +} + +static inline void +nfsd4_cstate_clear_replay (struct nfsd4_compound_state *cstate) +{ +struct nfs4_stateowner *so = cstate->replay_owner; + +if (so !=NULL ) { + cstate->replay_owner =NULL ; +mutex_unlock (&so->so_replay.rp_mutex); + } +} +struct nfsd4_change_info {u32 atomic;bool change_supported;
Signature · 1 line
1.9.3
PATCH v3 23/114 nfsd: Add locking to the nfs4_file->fi_fds[] array
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
Preparation for removal of the client_mutex, which currently protects this array.
--- fs/nfsd/nfs4state.c | 105 ++++++++++++++++++++++++++++++++++++++++++++-------- fs/nfsd/state.h | 26 ------------- 2 files changed, 89 insertions(+), 42 deletions(-)
fs/nfsd/nfs4state.c +89 -16
@@ -266,6 +266,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;
@@ -314,20 +360,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); } }
@@ -745,8 +800,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); }
@@ -3219,17 +3276,27 @@ nfsd4_truncate(struct svc_rqst *rqstp, struct svc_fh *fh,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) goto out; +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); status =nfsd4_truncate (rqstp, cur_fh, open);if (status)
@@ -3314,13 +3381,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; }
@@ -3918,6 +3987,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 :
@@ -3945,7 +4015,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;
@@ -4662,6 +4732,8 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,break ; } out: +if (filp) +fput (filp);if (status && new_state)release_lockowner_if_empty (lock_sop);nfsd4_bump_seqid (cstate, status);
@@ -4801,7 +4873,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 ;
@@ -4823,7 +4895,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 ();
@@ -4833,7 +4906,7 @@ out: out_nfserr: status =nfserrno (err); - goto out; + goto fput; }/*
fs/nfsd/state.h +0 -26
@@ -399,32 +399,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 */
Signature · 1 line
1.9.3
PATCH v3 24/114 nfsd: clean up helper __release_lock_stateid
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
Use filp_close instead of open coding. filp_close does a bit more than just release the locks and put the filp. It also calls ->flush and dnotify_flush, both of which should be done here anyway.
--- fs/nfsd/nfs4state.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-)
fs/nfsd/nfs4state.c +2 -4
@@ -800,10 +800,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 )lockowner (stp->st_stateowner));close_generic_stateid (stp);free_generic_stateid (stp); }
Signature · 1 line
1.9.3
PATCH v3 25/114 nfsd: refactor nfs4_file_get_access and nfs4_file_put_access
In reply to Jeff LaytonHave them take NFS4_SHARE_ACCESS_* flags instead of an open mode. This spares the callers from having to convert it themselves.
--- fs/nfsd/nfs4state.c | 58 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 23 deletions(-)
fs/nfsd/nfs4state.c +35 -23
@@ -337,6 +337,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? */
@@ -351,8 +365,15 @@ static void __nfs4_file_get_access(struct nfs4_file *fp, int oflag)atomic_inc (&fp->fi_access[oflag]); } -static void nfs4_file_get_access (struct nfs4_file *fp,int oflag) +static void nfs4_file_get_access (struct nfs4_file *fp,u32 access) { +int oflag =nfs4_access_to_omode (access); + +/* Note: relies on NFS4_SHARE_ACCESS_BOTH == READ|WRITE */ + access &= (NFS4_SHARE_ACCESS_READ |NFS4_SHARE_ACCESS_WRITE ); +if (access ==0 ) +return ; +if (oflag ==O_RDWR ) {__nfs4_file_get_access (fp,O_RDONLY );__nfs4_file_get_access (fp,O_WRONLY );
@@ -386,8 +407,15 @@ 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) { +int oflag; + + 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 );
@@ -741,20 +769,6 @@ 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)
@@ -763,8 +777,7 @@ release_all_access(struct nfs4_ol_stateid *stp)for (i =1 ; i <4 ; i++) {if (test_access (i, stp)) -nfs4_file_put_access (stp->st_file, -nfs4_access_to_omode (i)); +nfs4_file_put_access (stp->st_file, i);clear_access (i, stp); } }
@@ -3291,7 +3304,7 @@ static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp, filp =NULL ; } } -nfs4_file_get_access (fp, oflag); +nfs4_file_get_access (fp, open->op_share_access);spin_unlock (&fp->fi_lock);if (filp)fput (filp);
@@ -3303,7 +3316,7 @@ static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp,return nfs_ok; out_put_access: -nfs4_file_put_access (fp, oflag); +nfs4_file_put_access (fp, open->op_share_access); out:return status; }
@@ -4210,7 +4223,7 @@ static inline void nfs4_stateid_downgrade_bit(struct nfs4_ol_stateid *stp, u32 a {if (!test_access (access, stp))return ; -nfs4_file_put_access (stp->st_file,nfs4_access_to_omode (access)); +nfs4_file_put_access (stp->st_file, access);clear_access (access, stp); }
@@ -4535,11 +4548,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);set_access (access, lock_stp); }
Signature · 1 line
1.9.3
PATCH v3 26/114 nfsd: remove nfs4_file_put_fd
In reply to Jeff Layton...and replace it with a simple swap call.
--- fs/nfsd/nfs4state.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-)
fs/nfsd/nfs4state.c +2 -11
@@ -381,24 +381,15 @@ static void nfs4_file_get_access(struct nfs4_file *fp, u32 access)__nfs4_file_get_access (fp, oflag); } -static struct file *nfs4_file_put_fd (struct nfs4_file *fp,int oflag) -{ -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_lock (&fp->fi_access[oflag], &fp->fi_lock)) {struct file *f1 =NULL ;struct file *f2 =NULL ; - f1 =nfs4_file_put_fd (fp, oflag); +swap (f1, fp->fi_fds[oflag]);if (atomic_read (&fp->fi_access[1 - oflag]) ==0 ) - f2 =nfs4_file_put_fd (fp,O_RDWR ); +swap (f2, fp->fi_fds[O_RDWR ]);spin_unlock (&fp->fi_lock);if (f1)fput (f1);
Signature · 1 line
1.9.3
PATCH v3 27/114 nfsd: shrink st_access_bmap and st_deny_bmap
In reply to Jeff LaytonWe never use anything above bit #3, so an unsigned long for each is wasteful. Shrink them to a char each, and add some WARN_ON_ONCE calls if we try to set or clear bits that would go outside those sizes.
--- fs/nfsd/nfs4state.c | 26 +++++++++++++++----------- fs/nfsd/state.h | 4 ++-- 2 files changed, 17 insertions(+), 13 deletions(-)
fs/nfsd/nfs4state.c +15 -11
@@ -722,42 +722,46 @@ test_share(struct nfs4_ol_stateid *stp, struct nfsd4_open *open) {static inline void set_access (u32 access,struct nfs4_ol_stateid *stp) { -__set_bit (access, &stp->st_access_bmap); +WARN_ON_ONCE (access >NFS4_SHARE_ACCESS_BOTH ); +__set_bit (access, (unsigned long *)&stp->st_access_bmap); }/* clear share access for a given stateid */ static inline void clear_access (u32 access,struct nfs4_ol_stateid *stp) { -__clear_bit (access, &stp->st_access_bmap); +WARN_ON_ONCE (access >NFS4_SHARE_ACCESS_BOTH ); +__clear_bit (access, (unsigned long *)&stp->st_access_bmap); }/* test whether a given stateid has access */ static inline bool test_access (u32 access,struct nfs4_ol_stateid *stp) { -return test_bit (access, &stp->st_access_bmap); +return test_bit (access, (unsigned long *)&stp->st_access_bmap); }/* set share deny for a given stateid */ static inline void -set_deny (u32 access,struct nfs4_ol_stateid *stp) +set_deny (u32 deny,struct nfs4_ol_stateid *stp) { -__set_bit (access, &stp->st_deny_bmap); +WARN_ON_ONCE (deny >NFS4_SHARE_DENY_BOTH ); +__set_bit (deny, (unsigned long *)&stp->st_deny_bmap); }/* clear share deny for a given stateid */ static inline void -clear_deny (u32 access,struct nfs4_ol_stateid *stp) +clear_deny (u32 deny,struct nfs4_ol_stateid *stp) { -__clear_bit (access, &stp->st_deny_bmap); +WARN_ON_ONCE (deny >NFS4_SHARE_DENY_BOTH ); +__clear_bit (deny, (unsigned long *)&stp->st_deny_bmap); }/* test whether a given stateid is denying specific access */ static inline bool -test_deny (u32 access,struct nfs4_ol_stateid *stp) +test_deny (u32 deny,struct nfs4_ol_stateid *stp) { -return test_bit (access, &stp->st_deny_bmap); +return test_bit (deny, (unsigned long *)&stp->st_deny_bmap); }/* release all access and file references for a given stateid */
@@ -4270,12 +4274,12 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp, goto out; status = nfserr_inval;if (!test_access (od->od_share_access, stp)) { -dprintk ("NFSD: access not a subset current bitmap: 0x%lx, input access=%08x\n" , +dprintk ("NFSD: access not a subset of current bitmap: 0x%hhx, input access=%08x\n" , stp->st_access_bmap, od->od_share_access); goto out; }if (!test_deny (od->od_share_deny, stp)) { -dprintk ("NFSD:deny not a subset current bitmap: 0x%lx, input deny=%08x\n" , +dprintk ("NFSD: deny not a subset of current bitmap: 0x%hhx, input deny=%08x\n" , stp->st_deny_bmap, od->od_share_deny); goto out; }
fs/nfsd/state.h +2 -2
@@ -407,8 +407,8 @@ struct nfs4_ol_stateid {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; +unsigned char st_access_bmap; +unsigned char st_deny_bmap;struct nfs4_ol_stateid * st_openstp; };
Signature · 1 line
1.9.3
Re: [PATCH v3 027/114] nfsd: shrink st_access_bmap and st_deny_bmap
Quoted 15 lines · On Mon, 30 Jun 2014 11:48:56 -0400 Jeff Layton <[email protected]> wrote:
We never use anything above bit #3, so an unsigned long for each is wasteful. Shrink them to a char each, and add some
WARN_ON_ONCEcalls if we try to set or clear bits that would go outside those sizes.Signed-off-by Jeff Layton <[email protected]>--- fs/nfsd/nfs4state.c | 26 +++++++++++++++----------- fs/nfsd/state.h | 4 ++-- 2 files changed, 17 insertions(+), 13 deletions(-)fs/nfsd/nfs4state.c +0
-0
fs/nfsd/nfs4state.c test_share(struct nfs4_ol_stateid *stp, struct nfsd4_open *open) { · line 726
Quoted 6 lines
fs/nfsd/nfs4state.c +2
-1@@ -722,42 +722,46 @@ test_share(struct nfs4_ol_stateid *stp, struct nfsd4_open *open) {static inline void set_access (u32 access,struct nfs4_ol_stateid *stp) { -__set_bit (access, &stp->st_access_bmap); +WARN_ON_ONCE (access >NFS4_SHARE_ACCESS_BOTH ); +__set_bit (access, (unsigned long *)&stp->st_access_bmap);
Doh! Stupid braino on my part here, and self-NAK on this patch.
The above won't work (or be safe) on a big-endian machine. Probably, the thing to do is to convert this not to use atomic bitops since atomicity doesn't seem to be a huge factor anyway. Let me respin this patch and do that. Luckily the rest of the series shouldn't be greatly affected by this (aside from a few merge conflicts).
Quoted 79 lines
fs/nfsd/nfs4state.c +13
-10}/* clear share access for a given stateid */ static inline void clear_access (u32 access,struct nfs4_ol_stateid *stp) { -__clear_bit (access, &stp->st_access_bmap); +WARN_ON_ONCE (access >NFS4_SHARE_ACCESS_BOTH ); +__clear_bit (access, (unsigned long *)&stp->st_access_bmap); }/* test whether a given stateid has access */ static inline bool test_access (u32 access,struct nfs4_ol_stateid *stp) { -return test_bit (access, &stp->st_access_bmap); +return test_bit (access, (unsigned long *)&stp->st_access_bmap); }/* set share deny for a given stateid */ static inline void -set_deny (u32 access,struct nfs4_ol_stateid *stp) +set_deny (u32 deny,struct nfs4_ol_stateid *stp) { -__set_bit (access, &stp->st_deny_bmap); +WARN_ON_ONCE (deny >NFS4_SHARE_DENY_BOTH ); +__set_bit (deny, (unsigned long *)&stp->st_deny_bmap); }/* clear share deny for a given stateid */ static inline void -clear_deny (u32 access,struct nfs4_ol_stateid *stp) +clear_deny (u32 deny,struct nfs4_ol_stateid *stp) { -__clear_bit (access, &stp->st_deny_bmap); +WARN_ON_ONCE (deny >NFS4_SHARE_DENY_BOTH ); +__clear_bit (deny, (unsigned long *)&stp->st_deny_bmap); }/* test whether a given stateid is denying specific access */ static inline bool -test_deny (u32 access,struct nfs4_ol_stateid *stp) +test_deny (u32 deny,struct nfs4_ol_stateid *stp) { -return test_bit (access, &stp->st_deny_bmap); +return test_bit (deny, (unsigned long *)&stp->st_deny_bmap); }/* release all access and file references for a given stateid */ @@ -4270,12 +4274,12 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp, goto out; status = nfserr_inval;if (!test_access (od->od_share_access, stp)) { -dprintk ("NFSD: access not a subset current bitmap: 0x%lx, input access=%08x\n" , +dprintk ("NFSD: access not a subset of current bitmap: 0x%hhx, input access=%08x\n" , stp->st_access_bmap, od->od_share_access); goto out; }if (!test_deny (od->od_share_deny, stp)) { -dprintk ("NFSD:deny not a subset current bitmap: 0x%lx, input deny=%08x\n" , +dprintk ("NFSD: deny not a subset of current bitmap: 0x%hhx, input deny=%08x\n" , stp->st_deny_bmap, od->od_share_deny); goto out; }fs/nfsd/state.h +2
-2@@ -407,8 +407,8 @@ struct nfs4_ol_stateid {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; +unsigned char st_access_bmap; +unsigned char st_deny_bmap;struct nfs4_ol_stateid * st_openstp; };
Signature · 1 line
Jeff Layton <[email protected]>
PATCH v3 28/114 nfsd: set stateid access and deny bits in nfs4_get_vfs_file
In reply to Jeff LaytonCleanup -- ensure that the stateid bits are set at the same time that the file access refcounts are incremented. Keeping them coherent like this makes it easier to ensure that we account for all of the references.
Since the initialization of the st_*_bmap fields is done when it's hashed, we go ahead and hash the stateid before getting access to the file and unhash it if that function returns error. This will be necessary anyway in a follow-on patch that will overhaul deny mode handling.
--- fs/nfsd/nfs4state.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-)
fs/nfsd/nfs4state.c +12 -11
@@ -3280,7 +3280,8 @@ nfsd4_truncate(struct svc_rqst *rqstp, struct svc_fh *fh, }static __be32 nfs4_get_vfs_file (struct svc_rqst *rqstp,struct nfs4_file *fp, -struct svc_fh *cur_fh,struct nfsd4_open *open) +struct svc_fh *cur_fh,struct nfs4_ol_stateid *stp, +struct nfsd4_open *open) {struct file *filp =NULL ;__be32 status;
@@ -3308,6 +3309,9 @@ static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp,if (status) goto out_put_access; +/* Set access and deny bits in stateid */ +set_access (open->op_share_access, stp); +set_deny (open->op_share_deny, stp);return nfs_ok; out_put_access:
@@ -3319,20 +3323,15 @@ out:static __be32 nfs4_upgrade_open (struct svc_rqst *rqstp,struct nfs4_file *fp,struct svc_fh *cur_fh,struct nfs4_ol_stateid *stp,struct nfsd4_open *open) { -u32 op_share_access = open->op_share_access;__be32 status; -if (!test_access (op_share_access, stp)) - status =nfs4_get_vfs_file (rqstp, fp, cur_fh, open); +if (!test_access (open->op_share_access, stp)) + status =nfs4_get_vfs_file (rqstp, fp, cur_fh, stp, open);else status =nfsd4_truncate (rqstp, cur_fh, open);if (status)return status; - -/* remember the open */ -set_access (op_share_access, stp); -set_deny (open->op_share_deny, stp);return nfs_ok; }
@@ -3580,12 +3579,14 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfif (status) goto out; }else { - status =nfs4_get_vfs_file (rqstp, fp, current_fh, open); -if (status) - goto out; stp = open->op_stp; open->op_stp =NULL ;init_open_stateid (stp, fp, open); + status =nfs4_get_vfs_file (rqstp, fp, current_fh, stp, open); +if (status) { +release_open_stateid (stp); + goto out; + } }update_stateid (&stp->st_stid.sc_stateid);memcpy (&open->op_stateid, &stp->st_stid.sc_stateid,sizeof (stateid_t));
Signature · 1 line
1.9.3
PATCH v3 29/114 nfsd: clean up reset_union_bmap_deny
In reply to Jeff LaytonFix the "deny" argument type, and start the loop at 1. The 0 iteration is always a noop.
--- fs/nfsd/nfs4state.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
fs/nfsd/nfs4state.c +3 -2
@@ -4242,10 +4242,11 @@ static inline void nfs4_stateid_downgrade(struct nfs4_ol_stateid *stp, u32 to_ac }static void -reset_union_bmap_deny (unsigned long deny,struct nfs4_ol_stateid *stp) +reset_union_bmap_deny (u32 deny,struct nfs4_ol_stateid *stp) {int i; -for (i =0 ; i <4 ; i++) { + +for (i =1 ; i <4 ; i++) {if ((i & deny) != i)clear_deny (i, stp); }
Signature · 1 line
1.9.3
PATCH v3 30/114 nfsd: make deny mode enforcement more efficient and close races in it
In reply to Jeff LaytonThe current enforcement of deny modes is both inefficient and scattered across several places, which makes it hard to guarantee atomicity. The inefficiency is a problem now, and the lack of atomicity will mean races once the client_mutex is removed.
First, we address the inefficiency. We have to track deny modes on a per-stateid basis to ensure that open downgrades are sane, but when the server goes to enforce them it has to walk the entire list of stateids and check against each one.
Instead of doing that, maintain a per-nfs4_file deny mode. When a file is opened, we simply set any deny bits in that mode that were specified in the OPEN call. We can then use that unified deny mode to do a simple check to see whether there are any conflicts without needing to walk the entire stateid list.
The only time we'll need to walk the entire list of stateids is when a stateid that has a deny mode on it is being released, or one is having its deny mode downgraded. In that case, we must walk the entire list and recalculate the fi_share_deny field. Since deny modes are pretty rare today, this should be very rare under normal workloads.
To address the potential for races once the client_mutex is removed, protect fi_share_deny with the fi_lock. In nfs4_get_vfs_file, check to make sure that any deny mode we want to apply won't conflict with existing access. If that's ok, then have nfs4_file_get_access check that new access to the file won't conflict with existing deny modes.
If that also passes, then get file access references, set the correct access and deny bits in the stateid, and update the fi_share_deny field. If opening the file or truncating it fails, then unwind the whole mess and return the appropriate error.
--- fs/nfsd/nfs4state.c | 216 +++++++++++++++++++++++++++++++++++----------------- fs/nfsd/state.h | 1 + 2 files changed, 148 insertions(+), 69 deletions(-)
fs/nfsd/nfs4state.c +147 -69
@@ -359,26 +359,53 @@ 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 void +__nfs4_file_get_access (struct nfs4_file *fp,u32 access) { -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); + +if (oflag ==O_RDWR ) { +atomic_inc (&fp->fi_access[O_RDONLY ]); +atomic_inc (&fp->fi_access[O_WRONLY ]); + }else +atomic_inc (&fp->fi_access[oflag]); } -static void nfs4_file_get_access (struct nfs4_file *fp,u32 access) +static __be32 +nfs4_file_get_access (struct nfs4_file *fp,u32 access) { -int oflag =nfs4_access_to_omode (access); +lockdep_assert_held (&fp->fi_lock);/* Note: relies on NFS4_SHARE_ACCESS_BOTH == READ|WRITE */ - access &= (NFS4_SHARE_ACCESS_READ |NFS4_SHARE_ACCESS_WRITE ); + access &=NFS4_SHARE_ACCESS_BOTH ; + +/* Does this access mask make sense? */ if (access ==0 ) -return ; +return nfserr_inval; -if (oflag ==O_RDWR ) { -__nfs4_file_get_access (fp,O_RDONLY ); -__nfs4_file_get_access (fp,O_WRONLY ); - }else -__nfs4_file_get_access (fp, oflag); +/* Does it conflict with a deny mode already set? */ +if ((access & fp->fi_share_deny) !=0 ) +return nfserr_share_denied; + +__nfs4_file_get_access (fp, access); +return nfs_ok; +} + +static __be32 nfs4_file_check_deny (struct nfs4_file *fp,u32 deny) +{ +/* Common case is that there is no deny mode. */ + deny &=NFS4_SHARE_DENY_BOTH ; +if (deny) { +/* Note: relies on NFS4_SHARE_DENY_BOTH == READ|WRITE */ +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; + } +return nfs_ok; }static void __nfs4_file_put_access (struct nfs4_file *fp,int oflag)
@@ -707,17 +734,6 @@ bmap_to_share_mode(unsigned long bmap) {return access; } -static bool -test_share (struct nfs4_ol_stateid *stp,struct nfsd4_open *open) { -unsigned int access, deny; - - access =bmap_to_share_mode (stp->st_access_bmap); - deny =bmap_to_share_mode (stp->st_deny_bmap); -if ((access & open->op_share_deny) || (deny & open->op_share_access)) -return false; -return true; -} -/* set share access for a given stateid */ static inline void set_access (u32 access,struct nfs4_ol_stateid *stp)
@@ -764,11 +780,49 @@ test_deny(u32 deny, struct nfs4_ol_stateid *stp)return test_bit (deny, (unsigned long *)&stp->st_deny_bmap); } +/* +* A stateid that had a deny mode associated with it is being released +* or downgraded. Recalculate the deny mode on the file. +*/ +static void +recalculate_deny_mode (struct nfs4_file *fp) +{ +struct nfs4_ol_stateid *stp; + +spin_lock (&fp->fi_lock); + fp->fi_share_deny =0 ; +list_for_each_entry (stp, &fp->fi_stateids, st_perfile) + fp->fi_share_deny |=bmap_to_share_mode (stp->st_deny_bmap); +spin_unlock (&fp->fi_lock); +} + +static void +reset_union_bmap_deny (u32 deny,struct nfs4_ol_stateid *stp) +{ +int i; +bool change = false; + +for (i =1 ; i <4 ; i++) { +if ((i & deny) != i) { + change = true; +clear_deny (i, stp); + } + } + +/* Recalculate per-file deny mode if there was a change */ +if (change) +recalculate_deny_mode (stp->st_file); +} +/* release all access and file references for a given stateid */ static void release_all_access (struct nfs4_ol_stateid *stp) {int i; +struct nfs4_file *fp = stp->st_file; + +if (fp && stp->st_deny_bmap !=0 ) +recalculate_deny_mode (fp);for (i =1 ; i <4 ; i++) {if (test_access (i, stp))
@@ -2759,6 +2813,7 @@ static void nfsd4_init_file(struct nfs4_file *fp, struct inode *ino) fp->fi_inode = ino; fp->fi_had_conflict = false; fp->fi_lease =NULL ; + fp->fi_share_deny =0 ;memset (fp->fi_fds,0 ,sizeof (fp->fi_fds));memset (fp->fi_access,0 ,sizeof (fp->fi_access));hlist_add_head (&fp->fi_hash, &file_hashtbl[hashval]);
@@ -2987,22 +3042,15 @@ nfs4_share_conflict(struct svc_fh *current_fh, unsigned int deny_type) {struct inode *ino = current_fh->fh_dentry->d_inode;struct nfs4_file *fp; -struct nfs4_ol_stateid *stp; -__be32 ret; +__be32 ret = nfs_ok; fp =find_file (ino);if (!fp) -return nfs_ok; - ret = nfserr_locked; -/* Search for conflicting share reservations */ +return ret; +/* Check 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)) - goto out; - } - ret = nfs_ok; -out: +if (fp->fi_share_deny & deny_type) + ret = nfserr_locked;spin_unlock (&fp->fi_lock);put_nfs4_file (fp);return ret;
@@ -3241,12 +3289,9 @@ nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_ol_stif (local->st_stateowner->so_is_open_owner ==0 )continue ;/* remember if we have seen this open owner */ -if (local->st_stateowner == &oo->oo_owner) +if (local->st_stateowner == &oo->oo_owner) { *stpp = local; -/* check for conflicting share reservations */ -if (!test_share (local, open)) { -spin_unlock (&fp->fi_lock); -return nfserr_share_denied; +break ; } }spin_unlock (&fp->fi_lock);
@@ -3287,20 +3332,47 @@ static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp,__be32 status;int oflag =nfs4_access_to_omode (open->op_share_access);int access =nfs4_access_to_access (open->op_share_access); +unsigned char old_access_bmap, old_deny_bmap;spin_lock (&fp->fi_lock); + +/* +* Are we trying to set a deny mode that would conflict with +* current access? +*/ + status =nfs4_file_check_deny (fp, open->op_share_deny); +if (status != nfs_ok) { +spin_unlock (&fp->fi_lock); + goto out; + } + +/* set access to the file */ + status =nfs4_file_get_access (fp, open->op_share_access); +if (status != nfs_ok) { +spin_unlock (&fp->fi_lock); + goto out; + } + +/* Set access bits in stateid */ + old_access_bmap = stp->st_access_bmap; +set_access (open->op_share_access, stp); + +/* Set new deny mask */ + old_deny_bmap = stp->st_deny_bmap; +set_deny (open->op_share_deny, stp); + fp->fi_share_deny |= (open->op_share_deny &NFS4_SHARE_DENY_BOTH ); +if (!fp->fi_fds[oflag]) {spin_unlock (&fp->fi_lock); status =nfsd_open (rqstp, cur_fh,S_IFREG , access, &filp);if (status) - goto out; + goto out_put_access;spin_lock (&fp->fi_lock);if (!fp->fi_fds[oflag]) { fp->fi_fds[oflag] = filp; filp =NULL ; } } -nfs4_file_get_access (fp, open->op_share_access);spin_unlock (&fp->fi_lock);if (filp)fput (filp);
@@ -3308,33 +3380,43 @@ static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp, status =nfsd4_truncate (rqstp, cur_fh, open);if (status) goto out_put_access; - -/* Set access and deny bits in stateid */ -set_access (open->op_share_access, stp); -set_deny (open->op_share_deny, stp); -return nfs_ok; - -out_put_access: -nfs4_file_put_access (fp, open->op_share_access); out:return status; +out_put_access: + stp->st_access_bmap = old_access_bmap; +nfs4_file_put_access (fp, open->op_share_access); +reset_union_bmap_deny (bmap_to_share_mode (old_deny_bmap), stp); + goto out; }static __be32 nfs4_upgrade_open (struct svc_rqst *rqstp,struct nfs4_file *fp,struct svc_fh *cur_fh,struct nfs4_ol_stateid *stp,struct nfsd4_open *open) {__be32 status; +unsigned char old_deny_bmap;if (!test_access (open->op_share_access, stp)) - status =nfs4_get_vfs_file (rqstp, fp, cur_fh, stp, open); -else - status =nfsd4_truncate (rqstp, cur_fh, open); +return nfs4_get_vfs_file (rqstp, fp, cur_fh, stp, open); -if (status) +/* test and set deny mode */ +spin_lock (&fp->fi_lock); + status =nfs4_file_check_deny (fp, open->op_share_deny); +if (status == nfs_ok) { + old_deny_bmap = stp->st_deny_bmap; +set_deny (open->op_share_deny, stp); + fp->fi_share_deny |= + (open->op_share_deny &NFS4_SHARE_DENY_BOTH ); + } +spin_unlock (&fp->fi_lock); + +if (status != nfs_ok)return status; -return nfs_ok; -} + status =nfsd4_truncate (rqstp, cur_fh, open); +if (status != nfs_ok) +reset_union_bmap_deny (old_deny_bmap, stp); +return status; +}static void nfs4_set_claim_prev (struct nfsd4_open *open,bool has_session)
@@ -3556,7 +3638,8 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf*/ fp =find_or_add_file (ino, open->op_file);if (fp != open->op_file) { -if ((status =nfs4_check_open (fp, open, &stp))) + status =nfs4_check_open (fp, open, &stp); +if (status) goto out; status =nfs4_check_deleg (cl, open, &dp);if (status)
@@ -4241,17 +4324,6 @@ static inline void nfs4_stateid_downgrade(struct nfs4_ol_stateid *stp, u32 to_ac } } -static void -reset_union_bmap_deny (u32 deny,struct nfs4_ol_stateid *stp) -{ -int i; - -for (i =1 ; i <4 ; i++) { -if ((i & deny) != i) -clear_deny (i, stp); - } -} - __be32 nfsd4_open_downgrade(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
@@ -4546,9 +4618,15 @@ static void get_lock_access(struct nfs4_ol_stateid *lock_stp, u32 access) {struct nfs4_file *fp = lock_stp->st_file; +/* +* Lock stateids inherit the same access as the open stateid from which +* they are descended. We don't need to worry about holding the +* fp->fi_lock here since there is no danger of the access refcounts +* going to zero. +*/ if (test_access (access, lock_stp))return ; -nfs4_file_get_access (fp, access); +__nfs4_file_get_access (fp, access);set_access (access, lock_stp); }
fs/nfsd/state.h +1 -0
@@ -392,6 +392,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;
Signature · 1 line
1.9.3
PATCH v3 31/114 nfsd: cleanup nfs4_check_open
In reply to Jeff LaytonRename it to better describe what it does, and have it just return the stateid instead of a __be32 (which is now always nfs_ok). Also, do the search for an existing stateid after the delegation check, to reduce cleanup if the delegation check returns error.
--- fs/nfsd/nfs4state.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-)
fs/nfsd/nfs4state.c +6 -8
@@ -3277,10 +3277,10 @@ out:return nfs_ok; } -static __be32 -nfs4_check_open (struct nfs4_file *fp,struct nfsd4_open *open,struct nfs4_ol_stateid **stpp) +static struct nfs4_ol_stateid * +nfsd4_find_existing_open (struct nfs4_file *fp,struct nfsd4_open *open) { -struct nfs4_ol_stateid *local; +struct nfs4_ol_stateid *local, *ret =NULL ;struct nfs4_openowner *oo = open->op_openowner;spin_lock (&fp->fi_lock);
@@ -3290,12 +3290,12 @@ nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_ol_stcontinue ;/* remember if we have seen this open owner */ if (local->st_stateowner == &oo->oo_owner) { - *stpp = local; + ret = local;break ; } }spin_unlock (&fp->fi_lock); -return nfs_ok; +return ret; }static inline int nfs4_access_to_access (u32 nfs4_access)
@@ -3638,12 +3638,10 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf*/ fp =find_or_add_file (ino, open->op_file);if (fp != open->op_file) { - status =nfs4_check_open (fp, open, &stp); -if (status) - goto out; status =nfs4_check_deleg (cl, open, &dp);if (status) goto out; + stp =nfsd4_find_existing_open (fp, open); }else { open->op_file =NULL ; status = nfserr_bad_stateid;
Signature · 1 line
1.9.3
PATCH v3 32/114 locks: add file_has_lease to prevent delegation break races
In reply to Jeff LaytonOnce we remove the client_mutex, we'll have a potential race between setting a lease on a file and breaking the delegation. We may set the lease, but by the time we go to hash it, it may have already been broken. Currently, we know that this won't happen as the nfs4_laundromat takes the client_mutex, but we want to remove that.
As part of that fix, add a function that can tell us whether a particular file has a lease set on it. In a later nfsd patch, we'll use that to close the potential race window.
--- fs/locks.c | 26 ++++++++++++++++++++++++++ include/linux/fs.h | 6 ++++++ 2 files changed, 32 insertions(+)
fs/locks.c +26 -0
@@ -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:
include/linux/fs.h +6 -0
@@ -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 ;
Signature · 1 line
1.9.3
PATCH v3 33/114 nfsd: Protect the nfs4_file delegation fields using the fi_lock
In reply to Jeff LaytonMove more of the delegation fields to be protected by the fi_lock. It's more granular than the state_lock and in later patches we'll want to be able to rely on it in addition to the state_lock.
Also, 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 hashing it. 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. This prevents the race because the delegation break workqueue job must also take the state_lock.
--- fs/nfsd/nfs4state.c | 51 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 13 deletions(-)
fs/nfsd/nfs4state.c +38 -13
@@ -614,6 +614,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)) {
@@ -633,11 +635,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); }
@@ -648,17 +649,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); }
@@ -3458,7 +3459,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)
@@ -3466,15 +3467,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);
@@ -3483,19 +3500,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 ; }
Signature · 1 line
1.9.3
PATCH v3 34/114 nfsd: Simplify stateid management
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
Don't allow stateids to clear the open file pointer until they are being destroyed. Also, move to kzalloc and get rid of explicit zeroing of fields.
--- fs/nfsd/nfs4state.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-)
fs/nfsd/nfs4state.c +10 -11
@@ -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 );/*
@@ -582,10 +580,8 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct svINIT_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, ¤t_fh->fh_handle); - dp->dl_time =0 ;return dp; }
@@ -607,6 +603,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--; }
@@ -654,12 +652,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); }
@@ -845,13 +840,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); }
@@ -4406,6 +4401,10 @@ static void nfsd4_close_open_stateid(struct nfs4_ol_stateid *s)if (list_empty (&oo->oo_owner.so_stateids))release_openowner (oo); }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
Signature · 1 line
1.9.3
PATCH v3 35/114 nfsd: Fix delegation revocation
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
Ensure that the delegations cannot be found by the laundromat etc once we add them to the various 'revoke' lists.
--- fs/nfsd/nfs4state.c | 56 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 21 deletions(-)
fs/nfsd/nfs4state.c +35 -21
@@ -640,13 +640,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);
@@ -654,23 +654,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)
@@ -678,11 +687,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); } }
@@ -1416,12 +1424,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);
@@ -3604,7 +3613,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 &&
@@ -3843,7 +3852,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) {
@@ -4469,7 +4479,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 ();
@@ -5293,8 +5303,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 ; }
@@ -5543,11 +5555,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); }
Signature · 1 line
1.9.3
PATCH v3 36/114 nfsd: Add reference counting to the lock and open stateids
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
When we remove the client_mutex, we'll need to be able to ensure that these objects aren't destroyed while we're not holding locks.
--- fs/nfsd/nfs4state.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-)
fs/nfsd/nfs4state.c +8 -6
@@ -850,8 +850,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);
@@ -869,7 +871,7 @@ static void __release_lock_stateid(struct nfs4_ol_stateid *stp)if (file)filp_close (file, (fl_owner_t )lockowner (stp->st_stateowner));close_generic_stateid (stp); -free_generic_stateid (stp); +put_generic_stateid (stp); }static void unhash_lockowner (struct nfs4_lockowner *lo)
@@ -932,7 +934,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)
@@ -953,7 +955,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 ; } }
@@ -3758,7 +3760,7 @@ void nfsd4_cleanup_open_state(struct nfsd4_open *open, __be32 status)if (open->op_file)nfsd4_free_file (open->op_file);if (open->op_stp) -free_generic_stateid (open->op_stp); +put_generic_stateid (open->op_stp); } __be32
@@ -4407,9 +4409,9 @@ static void nfsd4_close_open_stateid(struct nfs4_ol_stateid *s)unhash_open_stateid (s);if (clp->cl_minorversion) { -free_generic_stateid (s);if (list_empty (&oo->oo_owner.so_stateids))release_openowner (oo); +put_generic_stateid (s); }else {if (s->st_file) {put_nfs4_file (s->st_file);
Signature · 1 line
1.9.3
PATCH v3 37/114 nfsd: Add a struct nfs4_file field to struct nfs4_stid
In reply to Jeff LaytonFrom: 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
--- fs/nfsd/nfs4state.c | 18 +++++++++--------- fs/nfsd/state.h | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-)
fs/nfsd/nfs4state.c +9 -9
@@ -596,6 +596,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); }
@@ -603,8 +605,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--; }
@@ -643,7 +643,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);
@@ -3065,8 +3065,8 @@ nfs4_share_conflict(struct svc_fh *current_fh, unsigned int deny_type)void nfsd4_prepare_cb_recall (struct nfs4_delegation *dp) { -struct nfs4_client *clp = dp->dl_stid.sc_client; -struct nfsd_net *nn =net_generic (clp->net, nfsd_net_id); +struct nfsd_net *nn =net_generic (dp->dl_stid.sc_client->net, + nfsd_net_id);/* * We can't do this in nfsd_break_deleg_cb because it is
@@ -3456,14 +3456,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 ;
@@ -3513,7 +3513,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);
@@ -4101,7 +4101,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;
fs/nfsd/state.h +1 -1
@@ -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: */
Signature · 1 line
1.9.3
PATCH v3 38/114 nfsd: Replace nfs4_ol_stateid->st_file with the st_stid.sc_file
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
--- fs/nfsd/nfs4state.c | 50 +++++++++++++++++++++++++------------------------- fs/nfsd/state.h | 1 - 2 files changed, 25 insertions(+), 26 deletions(-)
fs/nfsd/nfs4state.c +25 -25
@@ -815,7 +815,7 @@ reset_union_bmap_deny(u32 deny, struct nfs4_ol_stateid *stp)/* Recalculate per-file deny mode if there was a change */ if (change) -recalculate_deny_mode (stp->st_file); +recalculate_deny_mode (stp->st_stid.sc_file); }/* release all access and file references for a given stateid */
@@ -823,21 +823,21 @@ static void release_all_access(struct nfs4_ol_stateid *stp) {int i; -struct nfs4_file *fp = stp->st_file; +struct nfs4_file *fp = stp->st_stid.sc_file;if (fp && stp->st_deny_bmap !=0 )recalculate_deny_mode (fp);for (i =1 ; i <4 ; i++) {if (test_access (i, stp)) -nfs4_file_put_access (stp->st_file, i); +nfs4_file_put_access (stp->st_stid.sc_file, i);clear_access (i, stp); } }static void unhash_generic_stateid (struct nfs4_ol_stateid *stp) { -struct nfs4_file *fp = stp->st_file; +struct nfs4_file *fp = stp->st_stid.sc_file;spin_lock (&fp->fi_lock);list_del (&stp->st_perfile);
@@ -855,8 +855,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); }
@@ -867,7 +865,7 @@ static void __release_lock_stateid(struct nfs4_ol_stateid *stp)list_del (&stp->st_locks);unhash_generic_stateid (stp);unhash_stid (&stp->st_stid); - file =find_any_file (stp->st_file); + file =find_any_file (stp->st_stid.sc_file);if (file)filp_close (file, (fl_owner_t )lockowner (stp->st_stateowner));close_generic_stateid (stp);
@@ -2941,7 +2939,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);
@@ -3604,7 +3602,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;
@@ -3895,7 +3893,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; }
@@ -4123,10 +4121,12 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,if (status) goto out;if (filpp) { +struct nfs4_file *fp = stp->st_stid.sc_file; +if (flags &RD_STATE ) - file =find_readable_file (stp->st_file); + file =find_readable_file (fp);else - file =find_writeable_file (stp->st_file); + file =find_writeable_file (fp); }break ;default :
@@ -4146,7 +4146,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;
@@ -4332,7 +4332,7 @@ static inline void nfs4_stateid_downgrade_bit(struct nfs4_ol_stateid *stp, u32 a {if (!test_access (access, stp))return ; -nfs4_file_put_access (stp->st_file, access); +nfs4_file_put_access (stp->st_stid.sc_file, access);clear_access (access, stp); }
@@ -4413,9 +4413,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;/*
@@ -4617,7 +4617,7 @@ alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, structlist_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;
@@ -4634,7 +4634,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 ;
@@ -4650,7 +4650,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;/* * Lock stateids inherit the same access as the open stateid from which
@@ -4666,7 +4666,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;
@@ -4793,14 +4793,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 ;
@@ -4983,7 +4983,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;
@@ -5096,7 +5096,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, lo =lockowner (sop);/* see if there are still any locks associated with it */ list_for_each_entry (stp, &sop->so_stateids, st_perstateowner) { -if (check_for_locks (stp->st_file, lo)) +if (check_for_locks (stp->st_stid.sc_file, lo)) goto out; }
fs/nfsd/state.h +0 -1
@@ -407,7 +407,6 @@ struct nfs4_ol_stateid {struct list_head st_perstateowner;struct list_head st_locks;struct nfs4_stateowner * st_stateowner; -struct nfs4_file * st_file;unsigned char st_access_bmap;unsigned char st_deny_bmap;struct nfs4_ol_stateid * st_openstp;
Signature · 1 line
1.9.3
PATCH v3 39/114 nfsd: Ensure stateids remain unique until they are freed
In reply to Jeff LaytonFrom: 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. This will be necessary to ensure uniqueness once the client_mutex is removed.
--- fs/nfsd/nfs4state.c | 8 +++++--- fs/nfsd/state.h | 1 + 2 files changed, 6 insertions(+), 3 deletions(-)
fs/nfsd/nfs4state.c +5 -3
@@ -596,6 +596,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);
@@ -666,7 +667,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); }
@@ -679,7 +680,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)
@@ -854,7 +855,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); }
@@ -4029,7 +4029,9 @@ static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid)return nfs_ok;default :printk ("unknown stateid type %x\n" , s->sc_type); +/* Fallthrough */ case NFS4_CLOSED_STID : +case NFS4_CLOSED_DELEG_STID :return nfserr_bad_stateid; } }
fs/nfsd/state.h +1 -0
@@ -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 32unsigned char sc_type;stateid_t sc_stateid;struct nfs4_client *sc_client;
Signature · 1 line
1.9.3
PATCH v3 40/114 nfsd: Ensure atomicity of stateid destruction and idr tree removal
In reply to Jeff LaytonPreparation for removal of the client_mutex. Ensure that they are done while holding the clp->cl_lock.
--- fs/nfsd/nfs4state.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-)
fs/nfsd/nfs4state.c +16 -11
@@ -585,30 +585,37 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct svreturn 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)
@@ -853,9 +860,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_ol_stateid *stp)
Signature · 1 line
1.9.3
PATCH v3 41/114 nfsd: Cleanup the freeing of stateids
In reply to Jeff LaytonAdd a ->free() callback to the struct nfs4_stid, so that we can release a reference to the stid without caring about the contents.
--- fs/nfsd/nfs4state.c | 62 +++++++++++++++++++++++++++++++++++------------------ fs/nfsd/state.h | 2 ++ 2 files changed, 43 insertions(+), 21 deletions(-)
fs/nfsd/nfs4state.c +41 -21
@@ -71,6 +71,7 @@ static u64 current_sessionid = 1;/* forward declarations */ static int check_for_locks (struct nfs4_file *filp,struct nfs4_lockowner *lowner); +static void nfs4_free_generic_stateid (struct nfs4_stid *stid);/* Locking: */
@@ -441,8 +442,15 @@ static void nfs4_file_put_access(struct nfs4_file *fp, u32 access)__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--; }/*
@@ -570,6 +593,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
@@ -592,30 +617,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)
@@ -853,14 +869,17 @@ static void unhash_generic_stateid(struct nfs4_ol_stateid *stp)list_del (&stp->st_perstateowner); } -static void close_generic_stateid (struct nfs4_ol_stateid *stp) +static void nfs4_free_generic_stateid (struct nfs4_stid *stid) { +struct nfs4_ol_stateid *stp =openlockstateid (stid); +release_all_access (stp); +nfs4_free_stid (stateid_slab, stid); }static void put_generic_stateid (struct nfs4_ol_stateid *stp) { -nfs4_put_stid (stateid_slab, &stp->st_stid); +nfs4_put_stid (&stp->st_stid); }static void __release_lock_stateid (struct nfs4_ol_stateid *stp)
@@ -873,7 +892,6 @@ static void __release_lock_stateid(struct nfs4_ol_stateid *stp) file =find_any_file (stp->st_stid.sc_file);if (file)filp_close (file, (fl_owner_t )lockowner (stp->st_stateowner)); -close_generic_stateid (stp);put_generic_stateid (stp); }
@@ -931,7 +949,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)
@@ -4427,8 +4444,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); }
fs/nfsd/state.h +2 -0
@@ -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 {
Signature · 1 line
1.9.3
PATCH v3 42/114 nfsd: do filp_close in sc_free callback for lock stateids
In reply to Jeff LaytonReleasing locks when we unhash the stateid instead of doing so only when the stateid is actually released is problematic and will complicate some later changes.
--- fs/nfsd/nfs4state.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-)
fs/nfsd/nfs4state.c +13 -5
@@ -877,6 +877,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);
@@ -884,14 +896,9 @@ static void put_generic_stateid(struct nfs4_ol_stateid *stp)static void __release_lock_stateid (struct nfs4_ol_stateid *stp) { -struct file *file; -list_del (&stp->st_locks);unhash_generic_stateid (stp);unhash_stid (&stp->st_stid); - file =find_any_file (stp->st_stid.sc_file); -if (file) -filp_close (file, (fl_owner_t )lockowner (stp->st_stateowner));put_generic_stateid (stp); }
@@ -4645,6 +4652,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;
Signature · 1 line
1.9.3
PATCH v3 43/114 nfsd: Add locking to protect the state owner lists
In reply to Jeff LaytonChange to using the clp->cl_lock for this. For now, there's a lot of cl_lock thrashing, but in later patches we'll eliminate that and close the potential races that can occur when releasing the cl_lock while walking the lists. For now, the client_mutex prevents those races.
--- fs/nfsd/nfs4state.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-)
fs/nfsd/nfs4state.c +19 -2
@@ -863,6 +863,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);
@@ -896,9 +898,13 @@ static void put_generic_stateid(struct nfs4_ol_stateid *stp)static void __release_lock_stateid (struct nfs4_ol_stateid *stp) { +struct nfs4_openowner *oo =openowner (stp->st_openstp->st_stateowner); + +spin_lock (&oo->oo_owner.so_client->cl_lock);list_del (&stp->st_locks);unhash_generic_stateid (stp);unhash_stid (&stp->st_stid); +spin_unlock (&oo->oo_owner.so_client->cl_lock);put_generic_stateid (stp); }
@@ -942,20 +948,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)
@@ -2965,7 +2977,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;
@@ -2974,9 +2985,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
@@ -4641,6 +4655,7 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, strstatic 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;
@@ -4648,7 +4663,6 @@ alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, structif (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;
@@ -4656,10 +4670,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; }
Signature · 1 line
1.9.3
PATCH v3 44/114 nfsd: clean up races in lock stateid searching and creation
In reply to Jeff LaytonPreparation for removal of the client_mutex.
Currently, no lock aside from the client_mutex is held when calling find_lock_state. Ensure that the cl_lock is held by adding a lockdep assertion.
Once we remove the client_mutex, it'll be possible for another thread to race in and insert a lock state for the same file after we search but before we insert a new one. Ensure that doesn't happen by redoing the search after allocating a new stid that we plan to insert. If one is found just put the one we just allocated.
--- fs/nfsd/nfs4state.c | 58 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 17 deletions(-)
fs/nfsd/nfs4state.c +41 -17
@@ -4652,16 +4652,14 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, strreturn 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);
@@ -4670,20 +4668,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)
@@ -4692,6 +4690,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)
@@ -4739,14 +4767,10 @@ static __be32 lookup_or_create_lock_state(struct nfsd4_compound_state *cstate, sreturn 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; }
Signature · 1 line
1.9.3
PATCH v3 45/114 nfsd: Convert delegation counter to an atomic_long_t type
In reply to Jeff LaytonFrom: 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'.
None of this is a problem today, but it will be once we remove client_mutex protection.
--- fs/nfsd/nfs4state.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-)
fs/nfsd/nfs4state.c +11 -7
@@ -313,7 +313,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,7 +504,7 @@ 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); }/*
@@ -584,15 +584,17 @@ 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;if (delegation_blocked (¤t_fh->fh_handle)) -return NULL ; + 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;/*
@@ -601,13 +603,15 @@ 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); dp->dl_type =NFS4_OPEN_DELEGATE_READ ;fh_copy_shallow (&dp->dl_fh, ¤t_fh->fh_handle);return dp; +out_dec: +atomic_long_dec (&num_delegations); +return NULL ; }static void remove_stid_locked (struct nfs4_client *clp,struct nfs4_stid *s)
Signature · 1 line
1.9.3
PATCH v3 46/114 nfsd: Slight cleanup of find_stateid()
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
In preparation of reference counting.
--- fs/nfsd/nfs4state.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-)
fs/nfsd/nfs4state.c +18 -9
@@ -1660,28 +1660,37 @@ static void gen_confirm(struct nfs4_client *clp)memcpy (clp->cl_confirm.data, verf,sizeof (clp->cl_confirm.data)); } -static struct nfs4_stid *find_stateid (struct nfs4_client *cl,stateid_t *t) +static struct nfs4_stid * +find_stateid_locked (struct nfs4_client *cl,stateid_t *t) {struct nfs4_stid *ret; -spin_lock (&cl->cl_lock); ret =idr_find (&cl->cl_stateids, t->si_opaque.so_id); -spin_unlock (&cl->cl_lock);if (!ret || !ret->sc_type)return NULL ;return ret; } +static struct nfs4_stid *find_stateid (struct nfs4_client *cl,stateid_t *t) +{ +struct nfs4_stid *ret; + +spin_lock (&cl->cl_lock); + ret =find_stateid_locked (cl, t); +spin_unlock (&cl->cl_lock); +return ret; +} +static struct nfs4_stid *find_stateid_by_type (struct nfs4_client *cl,stateid_t *t,char typemask) {struct nfs4_stid *s; - s =find_stateid (cl, t); -if (!s) -return NULL ; -if (typemask & s->sc_type) -return s; -return NULL ; +spin_lock (&cl->cl_lock); + s =find_stateid_locked (cl, t); +if (s !=NULL && !(typemask & s->sc_type)) + s =NULL ; +spin_unlock (&cl->cl_lock); +return s; } static struct nfs4_client *create_client(struct xdr_netobj name,
Signature · 1 line
1.9.3
PATCH v3 47/114 nfsd: ensure atomicity in nfsd4_free_stateid and nfsd4_validate_stateid
In reply to Jeff LaytonHold the cl_lock over the bulk of these functions. In addition to ensuring that they aren't freed prematurely, this will also help prevent a potential race that could be introduced later. Once we remove the client_mutex, it'll be possible for FREE_STATEID and CLOSE to race and for both to try to put the "persistent" reference to the stateid.
--- fs/nfsd/nfs4state.c | 67 ++++++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 32 deletions(-)
fs/nfsd/nfs4state.c +35 -32
@@ -1671,16 +1671,6 @@ find_stateid_locked(struct nfs4_client *cl, stateid_t *t)return ret; } -static struct nfs4_stid *find_stateid (struct nfs4_client *cl,stateid_t *t) -{ -struct nfs4_stid *ret; - -spin_lock (&cl->cl_lock); - ret =find_stateid_locked (cl, t); -spin_unlock (&cl->cl_lock); -return ret; -} -static struct nfs4_stid *find_stateid_by_type (struct nfs4_client *cl,stateid_t *t,char typemask) {struct nfs4_stid *s;
@@ -4051,10 +4041,10 @@ static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid) {struct nfs4_stid *s;struct nfs4_ol_stateid *ols; -__be32 status; +__be32 status = nfserr_bad_stateid;if (ZERO_STATEID (stateid) ||ONE_STATEID (stateid)) -return nfserr_bad_stateid; +return status;/* Client debugging aid. */ if (!same_clid (&stateid->si_opaque.so_clid, &cl->cl_clientid)) {char addr_str[INET6_ADDRSTRLEN ];
@@ -4062,34 +4052,42 @@ static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid) sizeof(addr_str));pr_warn_ratelimited ("NFSD: client %s testing state ID " "with incorrect client ID\n" , addr_str); -return nfserr_bad_stateid; +return status; } - s =find_stateid (cl, stateid); +spin_lock (&cl->cl_lock); + s =find_stateid_locked (cl, stateid);if (!s) -return nfserr_bad_stateid; + goto out_unlock; status =check_stateid_generation (stateid, &s->sc_stateid,1 );if (status) -return status; + goto out_unlock;switch (s->sc_type) {case NFS4_DELEG_STID : -return nfs_ok; + status = nfs_ok; +break ;case NFS4_REVOKED_DELEG_STID : -return nfserr_deleg_revoked; + status = nfserr_deleg_revoked; +break ;case NFS4_OPEN_STID :case NFS4_LOCK_STID : ols =openlockstateid (s);if (ols->st_stateowner->so_is_open_owner && !(openowner (ols->st_stateowner)->oo_flags &NFS4_OO_CONFIRMED )) -return nfserr_bad_stateid; -return nfs_ok; + status = nfserr_bad_stateid; +else + status = nfs_ok; +break ;default :printk ("unknown stateid type %x\n" , s->sc_type);/* Fallthrough */ case NFS4_CLOSED_STID :case NFS4_CLOSED_DELEG_STID : -return nfserr_bad_stateid; + status = nfserr_bad_stateid; } +out_unlock: +spin_unlock (&cl->cl_lock); +return status; } static __be32
@@ -4240,31 +4238,36 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,__be32 ret = nfserr_bad_stateid;nfs4_lock_state (); - s =find_stateid (cl, stateid); +spin_lock (&cl->cl_lock); + s =find_stateid_locked (cl, stateid);if (!s) - goto out; + goto out_unlock;switch (s->sc_type) {case NFS4_DELEG_STID : ret = nfserr_locks_held; - goto out; +break ;case NFS4_OPEN_STID :case NFS4_LOCK_STID : ret =check_stateid_generation (stateid, &s->sc_stateid,1 );if (ret) - goto out; -if (s->sc_type ==NFS4_LOCK_STID ) - ret =nfsd4_free_lock_stateid (openlockstateid (s)); -else +break ; +if (s->sc_type !=NFS4_LOCK_STID ) { ret = nfserr_locks_held; -break ; +break ; + } +spin_unlock (&cl->cl_lock); + ret =nfsd4_free_lock_stateid (openlockstateid (s)); + goto out;case NFS4_REVOKED_DELEG_STID : +spin_unlock (&cl->cl_lock); dp =delegstateid (s);destroy_revoked_delegation (dp); ret = nfs_ok; -break ; -default : - ret = nfserr_bad_stateid; + goto out; +/* Default falls through and returns nfserr_bad_stateid */ } +out_unlock: +spin_unlock (&cl->cl_lock); out:nfs4_unlock_state ();return ret;
Signature · 1 line
1.9.3
PATCH v3 48/114 nfsd: Add reference counting to lock stateids
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
Ensure that nfsd4_lock() references the lock stateid while it is manipulating it. Not currently necessary, but will be once the client_mutex is removed.
--- fs/nfsd/nfs4state.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-)
fs/nfsd/nfs4state.c +12 -3
@@ -4676,6 +4676,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);
@@ -4700,8 +4701,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 ; }
@@ -4800,7 +4803,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 ;
@@ -4853,11 +4856,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);
@@ -4945,6 +4952,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);
Signature · 1 line
1.9.3
PATCH v3 49/114 nfsd: nfsd4_locku() must reference the lock stateid
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
Ensure that nfsd4_locku() keeps a reference to the lock stateid until it is done working with it. Necessary step toward client_mutex removal.
--- fs/nfsd/nfs4state.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
fs/nfsd/nfs4state.c +5 -1
@@ -5084,10 +5084,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) {
@@ -5117,6 +5119,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 ();
Signature · 1 line
1.9.3
PATCH v3 50/114 nfsd: Ensure that nfs4_open_delegation() references the delegation stateid
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
Ensure that nfs4_open_delegation() keeps a reference to the delegation stateid until it is done working with it. Necessary step toward client_mutex removal.
--- fs/nfsd/nfs4state.c | 2 ++ 1 file changed, 2 insertions(+)
fs/nfsd/nfs4state.c +2 -0
@@ -663,6 +663,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);
@@ -3657,6 +3658,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);
Signature · 1 line
1.9.3
PATCH v3 51/114 nfsd: nfsd4_process_open2() must reference the delegation stateid
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
Ensure that nfsd4_process_open2() keeps a reference to the delegation stateid until it is done working with it. Necessary step toward client_mutex removal.
--- fs/nfsd/nfs4state.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-)
fs/nfsd/nfs4state.c +13 -5
@@ -3297,6 +3297,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); }
@@ -3312,14 +3314,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;
@@ -3784,6 +3790,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; }
Signature · 1 line
1.9.3
PATCH v3 52/114 nfsd: nfsd4_process_open2() must reference the open stateid
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
Ensure that nfsd4_process_open2() keeps a reference to the open stateid until it is done working with it. Necessary step toward client_mutex removal.
--- fs/nfsd/nfs4state.c | 4 ++++ 1 file changed, 4 insertions(+)
fs/nfsd/nfs4state.c +4 -0
@@ -2979,6 +2979,7 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfsd4_open *open,static void init_open_stateid (struct nfs4_ol_stateid *stp,struct nfs4_file *fp,struct nfsd4_open *open) {struct nfs4_openowner *oo = open->op_openowner; +atomic_inc (&stp->st_stid.sc_count); stp->st_stid.sc_type =NFS4_OPEN_STID ;INIT_LIST_HEAD (&stp->st_locks); stp->st_stateowner = &oo->oo_owner;
@@ -3349,6 +3350,7 @@ nfsd4_find_existing_open(struct nfs4_file *fp, struct nfsd4_open *open)/* remember if we have seen this open owner */ if (local->st_stateowner == &oo->oo_owner) { ret = local; +atomic_inc (&ret->st_stid.sc_count);break ; } }
@@ -3792,6 +3794,8 @@ out: open->op_rflags |=NFS4_OPEN_RESULT_CONFIRM ;if (dp)nfs4_put_delegation (dp); +if (stp) +put_generic_stateid (stp);return status; }
Signature · 1 line
1.9.3
PATCH v3 53/114 nfsd: Prepare nfsd4_close() for open stateid referencing
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
Prepare nfsd4_close for a future where nfs4_preprocess_seqid_op() hands it a fully referenced open stateid. Necessary step toward client_mutex removal.
--- fs/nfsd/nfs4state.c | 5 +++++ 1 file changed, 5 insertions(+)
fs/nfsd/nfs4state.c +5 -0
@@ -4528,10 +4528,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;
Signature · 1 line
1.9.3
PATCH v3 54/114 nfsd: nfsd4_open_confirm() must reference the open stateid
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
Ensure that nfsd4_open_confirm() keeps a reference to the open stateid until it is done working with it.
Necessary step toward client_mutex removal.
--- fs/nfsd/nfs4state.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
fs/nfsd/nfs4state.c +5 -1
@@ -4385,10 +4385,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));
@@ -4397,6 +4399,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 ();
Signature · 1 line
1.9.3
PATCH v3 55/114 nfsd: Add reference counting to nfs4_preprocess_confirmed_seqid_op
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
Ensure that all the callers put the open stateid after use. Necessary step toward client_mutex removal.
--- fs/nfsd/nfs4state.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-)
fs/nfsd/nfs4state.c +9 -4
@@ -4356,6 +4356,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;
@@ -4459,12 +4461,12 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp,if (!test_access (od->od_share_access, stp)) {dprintk ("NFSD: access not a subset of current bitmap: 0x%hhx, input access=%08x\n" , stp->st_access_bmap, od->od_share_access); - goto out; + goto put_stateid; }if (!test_deny (od->od_share_deny, stp)) {dprintk ("NFSD: deny not a subset of current bitmap: 0x%hhx, input deny=%08x\n" , stp->st_deny_bmap, od->od_share_deny); - goto out; + goto put_stateid; }nfs4_stateid_downgrade (stp, od->od_share_access);
@@ -4473,6 +4475,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 ();
@@ -4827,6 +4831,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 ;
@@ -4853,8 +4858,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,
@@ -4977,6 +4980,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);
Signature · 1 line
1.9.3
PATCH v3 56/114 nfsd: Migrate the stateid reference into nfs4_preprocess_seqid_op
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
Allow nfs4_preprocess_seqid_op to take the stateid reference, instead of having all the callers do so.
--- fs/nfsd/nfs4state.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-)
fs/nfsd/nfs4state.c +11 -15
@@ -4341,8 +4341,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; }
@@ -4351,16 +4354,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; }
@@ -4387,8 +4392,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 )
@@ -4536,8 +4539,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));
@@ -4887,9 +4888,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;
@@ -5112,8 +5110,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;
Signature · 1 line
1.9.3
PATCH v3 57/114 nfsd: Migrate the stateid reference into nfs4_lookup_stateid()
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
Allow nfs4_lookup_stateid to take the stateid reference, instead of having all the callers do so.
--- fs/nfsd/nfs4state.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-)
fs/nfsd/nfs4state.c +11 -6
@@ -4124,6 +4124,8 @@ nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate, *s =find_stateid_by_type (cstate->clp, stateid, typemask);if (!*s)return nfserr_bad_stateid; +/* FIXME: move into find_stateid_by_type */ +atomic_inc (&(*s)->sc_count);return nfs_ok; }
@@ -4158,7 +4160,7 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,NFS4_DELEG_STID |NFS4_OPEN_STID |NFS4_LOCK_STID , &s, nn);if (status) - goto out; + goto unlock_state; status =check_stateid_generation (stateid, &s->sc_stateid,nfsd4_has_session (cstate));if (status) goto out;
@@ -4207,6 +4209,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; }
@@ -4341,11 +4345,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; }
@@ -4571,9 +4574,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 ();
Signature · 1 line
1.9.3
PATCH v3 58/114 nfsd: Migrate the stateid reference into nfs4_find_stateid_by_type()
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
Allow nfs4_find_stateid_by_type to take the stateid reference, while still holding the &cl->cl_lock. Necessary step toward client_mutex removal.
--- fs/nfsd/nfs4state.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
fs/nfsd/nfs4state.c +6 -6
@@ -1678,8 +1678,12 @@ static struct nfs4_stid *find_stateid_by_type(struct nfs4_client *cl, stateid_tspin_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; }
@@ -3298,8 +3302,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); }
@@ -4124,8 +4126,6 @@ nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate, *s =find_stateid_by_type (cstate->clp, stateid, typemask);if (!*s)return nfserr_bad_stateid; -/* FIXME: move into find_stateid_by_type */ -atomic_inc (&(*s)->sc_count);return nfs_ok; }
Signature · 1 line
1.9.3
PATCH v3 59/114 nfsd: Add reference counting to state owners
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
The way stateowners are managed today is somewhat awkward. They need to be explicitly destroyed, even though the stateids reference them. This will be particularly problematic when we remove the client_mutex.
We may create a new stateowner and attempt to open a file or set a lock, and have that fail. In the meantime, another RPC may come in that uses that same stateowner and succeed. We can't have the first task tearing down the stateowner in that situation.
To fix this, we need to change how stateowners are tracked altogether. Refcount them and only destroy them once all stateids that reference them have been destroyed. This patch starts by adding the refcounting necessary to do that.
--- fs/nfsd/nfs4state.c | 43 +++++++++++++++++++++++++++++-------------- fs/nfsd/state.h | 3 +++ 2 files changed, 32 insertions(+), 14 deletions(-)
fs/nfsd/nfs4state.c +29 -14
@@ -72,6 +72,7 @@ static u64 current_sessionid = 1;/* forward declarations */ static int check_for_locks (struct nfs4_file *filp,struct nfs4_lockowner *lowner);static void nfs4_free_generic_stateid (struct nfs4_stid *stid); +static void nfs4_put_stateowner (struct nfs4_stateowner *sop);/* Locking: */
@@ -925,16 +926,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)
@@ -1004,18 +999,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
@@ -2947,9 +2936,17 @@ static inline void *alloc_stateowner(struct kmem_cache *slab, struct xdr_netobjINIT_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);
@@ -2958,6 +2955,14 @@ static void hash_openowner(struct nfs4_openowner *oo, struct nfs4_client *clp, ulist_add (&oo->oo_perclient, &clp->cl_openowners); } +static void nfs4_free_openowner (struct nfs4_stateowner *so) +{ +struct nfs4_openowner *oo =openowner (so); + +kfree (oo->oo_owner.so_owner.data); +kmem_cache_free (openowner_slab, oo); +} +static struct nfs4_openowner *alloc_init_open_stateowner (unsigned int strhashval,struct nfsd4_open *open,struct nfsd4_compound_state *cstate)
@@ -2968,6 +2973,7 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfsd4_open *open, oo =alloc_stateowner (openowner_slab, &open->op_owner, clp);if (!oo)return NULL ; + oo->oo_owner.so_free = nfs4_free_openowner; oo->oo_owner.so_is_open_owner =1 ; oo->oo_owner.so_seqid = open->op_seqid; oo->oo_flags =NFS4_OO_NEW ;
@@ -4677,6 +4683,14 @@ find_lockowner_str(clientid_t *clid, struct xdr_netobj *owner,return NULL ; } +static void nfs4_free_lockowner (struct nfs4_stateowner *sop) +{ +struct nfs4_lockowner *lo =lockowner (sop); + +kfree (lo->lo_owner.so_owner.data); +kmem_cache_free (lockowner_slab, lo); +} +/* * Alloc a lock owner structure. * Called in nfsd4_lock - therefore, OPEN and OPEN_CONFIRM (if needed) has
@@ -4697,6 +4711,7 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, str/* It is the openowner seqid that will be incremented in encode in the * case of new lockowners; so increment the lock seqid manually: */ lo->lo_owner.so_seqid = lock->lk_new_lock_seqid +1 ; + lo->lo_owner.so_free = nfs4_free_lockowner;list_add (&lo->lo_owner.so_strhash, &nn->ownerstr_hashtbl[strhashval]);return lo; }
fs/nfsd/state.h +3 -0
@@ -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 {
Signature · 1 line
1.9.3
PATCH v3 60/114 nfsd: Keep a reference to the open stateid for the NFSv4.0 replay cache
In reply to Jeff LaytonFrom: 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.
--- fs/nfsd/nfs4proc.c | 5 +---- fs/nfsd/nfs4state.c | 26 +++++++++++++++++++++++++- fs/nfsd/xdr4.h | 26 ++++---------------------- 3 files changed, 30 insertions(+), 27 deletions(-)
fs/nfsd/nfs4proc.c +1 -4
@@ -467,10 +467,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;
fs/nfsd/nfs4state.c +25 -1
@@ -2918,6 +2918,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;
@@ -3808,7 +3829,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;
@@ -3822,6 +3844,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);
fs/nfsd/xdr4.h +4 -22
@@ -74,27 +74,6 @@ static inline bool nfsd4_has_session(struct nfsd4_compound_state *cs)return cs->slot !=NULL ; } -static inline void -nfsd4_cstate_assign_replay (struct nfsd4_compound_state *cstate, -struct nfs4_stateowner *so) -{ -if (!nfsd4_has_session (cstate)) { -mutex_lock (&so->so_replay.rp_mutex); - cstate->replay_owner = so; - } -} - -static inline void -nfsd4_cstate_clear_replay (struct nfsd4_compound_state *cstate) -{ -struct nfs4_stateowner *so = cstate->replay_owner; - -if (so !=NULL ) { - cstate->replay_owner =NULL ; -mutex_unlock (&so->so_replay.rp_mutex); - } -} -struct nfsd4_change_info {u32 atomic;bool change_supported;
@@ -616,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,
@@ -647,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 /*
Signature · 1 line
1.9.3
PATCH v3 61/114 nfsd: clean up lockowner refcounting when finding them
In reply to Jeff LaytonEnsure 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.
--- fs/nfsd/nfs4state.c | 51 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 13 deletions(-)
fs/nfsd/nfs4state.c +38 -13
@@ -4702,6 +4702,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 ;
@@ -4732,9 +4733,7 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, strreturn NULL ;INIT_LIST_HEAD (&lo->lo_owner.so_stateids); lo->lo_owner.so_is_open_owner =0 ; -/* It is the openowner seqid that will be incremented in encode in the -* case of new lockowners; so increment the lock seqid manually: */ - lo->lo_owner.so_seqid = lock->lk_new_lock_seqid +1 ; + lo->lo_owner.so_seqid = lock->lk_new_lock_seqid; lo->lo_owner.so_free = nfs4_free_lockowner;list_add (&lo->lo_owner.so_strhash, &nn->ownerstr_hashtbl[strhashval]);return lo;
@@ -4835,8 +4834,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;
@@ -4851,19 +4855,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; }/*
@@ -4881,9 +4892,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);
@@ -4926,7 +4937,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,
@@ -5020,12 +5031,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)
@@ -5060,7 +5083,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);
@@ -5123,6 +5146,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);
Signature · 1 line
1.9.3
PATCH v3 62/114 nfsd: add an operation for unhashing a stateowner
In reply to Jeff LaytonAllow 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.
--- fs/nfsd/nfs4state.c | 45 +++++++++++++++++++++++++++++++++++---------- fs/nfsd/state.h | 1 + 2 files changed, 36 insertions(+), 10 deletions(-)
fs/nfsd/nfs4state.c +35 -10
@@ -916,9 +916,13 @@ static void __release_lock_stateid(struct nfs4_ol_stateid *stp)static void unhash_lockowner (struct nfs4_lockowner *lo) { +list_del_init (&lo->lo_owner.so_strhash); +} + +static void release_lockowner_stateids (struct nfs4_lockowner *lo) +{struct nfs4_ol_stateid *stp; -list_del (&lo->lo_owner.so_strhash);while (!list_empty (&lo->lo_owner.so_stateids)) { stp =list_first_entry (&lo->lo_owner.so_stateids, struct nfs4_ol_stateid, st_perstateowner);
@@ -929,6 +933,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); }
@@ -978,15 +983,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)
@@ -999,9 +997,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);
@@ -2965,6 +2975,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); }
@@ -2976,6 +2987,13 @@ static void hash_openowner(struct nfs4_openowner *oo, struct nfs4_client *clp, ulist_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);
@@ -2995,6 +3013,7 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfsd4_open *open,if (!oo)return NULL ; oo->oo_owner.so_free = nfs4_free_openowner; + oo->oo_owner.so_unhash = nfs4_unhash_openowner; oo->oo_owner.so_is_open_owner =1 ; oo->oo_owner.so_seqid = open->op_seqid; oo->oo_flags =NFS4_OO_NEW ;
@@ -4708,6 +4727,11 @@ find_lockowner_str(clientid_t *clid, struct xdr_netobj *owner,return NULL ; } +static void nfs4_unhash_lockowner (struct nfs4_stateowner *sop) +{ +unhash_lockowner (lockowner (sop)); +} +static void nfs4_free_lockowner (struct nfs4_stateowner *sop) {struct nfs4_lockowner *lo =lockowner (sop);
@@ -4735,6 +4759,7 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, str lo->lo_owner.so_is_open_owner =0 ; lo->lo_owner.so_seqid = lock->lk_new_lock_seqid; lo->lo_owner.so_free = nfs4_free_lockowner; + lo->lo_owner.so_unhash = nfs4_unhash_lockowner;list_add (&lo->lo_owner.so_strhash, &nn->ownerstr_hashtbl[strhashval]);return lo; }
fs/nfsd/state.h +1 -0
@@ -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 {
Signature · 1 line
1.9.3
PATCH v3 63/114 nfsd: Make lock stateid take a reference to the lockowner
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
A necessary step toward client_mutex removal.
--- fs/nfsd/nfs4state.c | 3 +++ 1 file changed, 3 insertions(+)
fs/nfsd/nfs4state.c +3 -0
@@ -882,6 +882,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); }
@@ -4775,6 +4777,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;
Signature · 1 line
1.9.3
PATCH v3 64/114 nfsd: clean up refcounting for lockowners
In reply to Jeff LaytonEnsure 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.
--- fs/nfsd/nfs4state.c | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-)
fs/nfsd/nfs4state.c +7 -22
@@ -904,7 +904,7 @@ static void put_generic_stateid(struct nfs4_ol_stateid *stp)nfs4_put_stid (&stp->st_stid); } -static void __release_lock_stateid (struct nfs4_ol_stateid *stp) +static void release_lock_stateid (struct nfs4_ol_stateid *stp) {struct nfs4_openowner *oo =openowner (stp->st_openstp->st_stateowner);
@@ -928,7 +928,7 @@ static void release_lockowner_stateids(struct nfs4_lockowner *lo)while (!list_empty (&lo->lo_owner.so_stateids)) { stp =list_first_entry (&lo->lo_owner.so_stateids, struct nfs4_ol_stateid, st_perstateowner); -__release_lock_stateid (stp); +release_lock_stateid (stp); } }
@@ -939,21 +939,6 @@ static void release_lockowner(struct nfs4_lockowner *lo)nfs4_put_stateowner (&lo->lo_owner); } -static void release_lockowner_if_empty (struct nfs4_lockowner *lo) -{ -if (list_empty (&lo->lo_owner.so_stateids)) -release_lockowner (lo); -} - -static void release_lock_stateid (struct nfs4_ol_stateid *stp) -{ -struct nfs4_lockowner *lo; - - lo =lockowner (stp->st_stateowner); -__release_lock_stateid (stp); -release_lockowner_if_empty (lo); -} - static void release_open_stateid_locks(struct nfs4_ol_stateid *open_stp) __releases(&open_stp->st_stateowner->so_client->cl_lock) __acquires(&open_stp->st_stateowner->so_client->cl_lock)
@@ -4273,7 +4258,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; }
@@ -4883,8 +4868,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;
@@ -4895,7 +4878,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; }
@@ -5309,6 +5291,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,continue ;if (same_owner_str (tmp, owner, clid)) { sop = tmp; +atomic_inc (&sop->so_count);break ; } }
@@ -5322,8 +5305,10 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, lo =lockowner (sop);/* see if there are still any locks associated with it */ list_for_each_entry (stp, &sop->so_stateids, st_perstateowner) { -if (check_for_locks (stp->st_stid.sc_file, lo)) +if (check_for_locks (stp->st_stid.sc_file, lo)) { +nfs4_put_stateowner (sop); goto out; + } } status = nfs_ok;
Signature · 1 line
1.9.3
PATCH v3 65/114 nfsd: make openstateids hold references to their openowners
In reply to Jeff LaytonChange it so that only openstateids hold persistent references to openowners. References can still be held by compounds in progress.
With this, we can get rid of NFS4_OO_NEW. It's possible that we will create a new openowner in the process of doing the open, but something later fails. In the meantime, another task could find that openowner and start using it on a successful open. If that occurs we don't necessarily want to tear it down, just put the reference that the failing compound holds.
--- fs/nfsd/nfs4state.c | 71 +++++++++++++++++++++++------------------------------ fs/nfsd/state.h | 1 - 2 files changed, 31 insertions(+), 41 deletions(-)
fs/nfsd/nfs4state.c +31 -40
@@ -882,7 +882,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); }
@@ -979,8 +979,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); } }
@@ -999,7 +1000,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); }
@@ -1472,6 +1472,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);
@@ -3003,7 +3004,7 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfsd4_open *open, oo->oo_owner.so_unhash = nfs4_unhash_openowner; oo->oo_owner.so_is_open_owner =1 ; oo->oo_owner.so_seqid = open->op_seqid; - oo->oo_flags =NFS4_OO_NEW ; + oo->oo_flags =0 ;if (nfsd4_has_session (cstate)) oo->oo_flags |=NFS4_OO_CONFIRMED ; oo->oo_time =0 ;
@@ -3020,6 +3021,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 ;
@@ -3035,13 +3037,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 (); }
@@ -3072,6 +3088,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; } }
@@ -3839,19 +3856,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);
@@ -3965,7 +3973,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 ();
@@ -4528,31 +4536,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); }/*
fs/nfsd/state.h +0 -1
@@ -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 4unsigned char oo_flags; };
Signature · 1 line
1.9.3
PATCH v3 66/114 nfsd: don't allow CLOSE to proceed until refcount on stateid drops
In reply to Jeff LaytonOnce 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.
--- fs/nfsd/nfs4state.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-)
fs/nfsd/nfs4state.c +21 -1
@@ -86,6 +86,12 @@ static DEFINE_MUTEX(client_mutex);*/ static DEFINE_SPINLOCK (state_lock); +/* +* A waitqueue for all in-progress 4.0 CLOSE operations that are waiting for +* the refcount on the open stateid to drop. +*/ +static DECLARE_WAIT_QUEUE_HEAD (close_wq); +static struct kmem_cache *openowner_slab;static struct kmem_cache *lockowner_slab;static struct kmem_cache *file_slab;
@@ -626,8 +632,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);
@@ -3051,11 +3059,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);
Signature · 1 line
1.9.3
PATCH v3 67/114 nfsd: Protect adding/removing open state owners using client_lock
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
Once we remove client mutex protection, we'll need to ensure that stateowner lookup and creation are atomic between concurrent compounds. Ensure that alloc_init_open_stateowner checks the hashtable under the client_lock before adding a new element.
--- fs/nfsd/nfs4state.c | 63 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 9 deletions(-)
fs/nfsd/nfs4state.c +54 -9
@@ -72,6 +72,9 @@ static u64 current_sessionid = 1;/* forward declarations */ static int check_for_locks (struct nfs4_file *filp,struct nfs4_lockowner *lowner);static void nfs4_free_generic_stateid (struct nfs4_stid *stid); +static struct nfs4_openowner *find_openstateowner_str_locked ( +unsigned int hashval,struct nfsd4_open *open, +bool sessions,struct nfsd_net *nn);static void nfs4_put_stateowner (struct nfs4_stateowner *sop);/* Locking: */
@@ -976,8 +979,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); }
@@ -996,18 +1004,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); }
@@ -2986,8 +3005,11 @@ static void hash_openowner(struct nfs4_openowner *oo, struct nfs4_client *clp, ustatic 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)
@@ -3003,7 +3025,8 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfsd4_open *open, struct nfsd4_compound_state *cstate) {struct nfs4_client *clp = cstate->clp; -struct nfs4_openowner *oo; +struct nfsd_net *nn =net_generic (clp->net, nfsd_net_id); +struct nfs4_openowner *oo, *ret; oo =alloc_stateowner (openowner_slab, &open->op_owner, clp);if (!oo)
@@ -3018,7 +3041,15 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfsd4_open *open, oo->oo_time =0 ; oo->oo_last_closed_stid =NULL ;INIT_LIST_HEAD (&oo->oo_close_lru); -hash_openowner (oo, clp, strhashval); +spin_lock (&nn->client_lock); + ret =find_openstateowner_str_locked (strhashval, + open, clp->cl_minorversion, nn); +if (ret ==NULL ) { +hash_openowner (oo, clp, strhashval); + ret = oo; + }else +nfs4_free_openowner (&oo->oo_owner); +spin_unlock (&nn->client_lock);return oo; }
@@ -3092,13 +3123,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 ;
@@ -3106,15 +3139,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)
Signature · 1 line
1.9.3
PATCH v3 68/114 nfsd: Protect adding/removing lock owners using client_lock
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
Once we remove client mutex protection, we'll need to ensure that stateowner lookup and creation are atomic between concurrent compounds. Ensure that alloc_init_lock_stateowner checks the hashtable under the client_lock before adding a new element.
--- fs/nfsd/nfs4state.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 8 deletions(-)
fs/nfsd/nfs4state.c +61 -8
@@ -927,26 +927,42 @@ static void release_lock_stateid(struct nfs4_ol_stateid *stp)put_generic_stateid (stp); } -static void unhash_lockowner (struct nfs4_lockowner *lo) +static void unhash_lockowner_locked (struct nfs4_lockowner *lo) { +struct nfsd_net *nn =net_generic (lo->lo_owner.so_client->net, + nfsd_net_id); + +lockdep_assert_held (&nn->client_lock); +list_del_init (&lo->lo_owner.so_strhash); }static void release_lockowner_stateids (struct nfs4_lockowner *lo) { +struct nfsd_net *nn =net_generic (lo->lo_owner.so_client->net, + nfsd_net_id);struct nfs4_ol_stateid *stp; +lockdep_assert_held (&nn->client_lock); +while (!list_empty (&lo->lo_owner.so_stateids)) { stp =list_first_entry (&lo->lo_owner.so_stateids, struct nfs4_ol_stateid, st_perstateowner); +spin_unlock (&nn->client_lock);release_lock_stateid (stp); +spin_lock (&nn->client_lock); } }static void release_lockowner (struct nfs4_lockowner *lo) { -unhash_lockowner (lo); +struct nfsd_net *nn =net_generic (lo->lo_owner.so_client->net, + nfsd_net_id); + +spin_lock (&nn->client_lock); +unhash_lockowner_locked (lo);release_lockowner_stateids (lo); +spin_unlock (&nn->client_lock);nfs4_put_stateowner (&lo->lo_owner); }
@@ -4753,7 +4769,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);
@@ -4770,9 +4786,25 @@ find_lockowner_str(clientid_t *clid, struct xdr_netobj *owner,return NULL ; } +static struct nfs4_lockowner * +find_lockowner_str (clientid_t *clid,struct xdr_netobj *owner, +struct nfsd_net *nn) +{ +struct nfs4_lockowner *lo; + +spin_lock (&nn->client_lock); + lo =find_lockowner_str_locked (clid, owner, nn); +spin_unlock (&nn->client_lock); +return lo; +} +static void nfs4_unhash_lockowner (struct nfs4_stateowner *sop) { -unhash_lockowner (lockowner (sop)); +struct nfsd_net *nn =net_generic (sop->so_client->net, nfsd_net_id); + +spin_lock (&nn->client_lock); +unhash_lockowner_locked (lockowner (sop)); +spin_unlock (&nn->client_lock); }static void nfs4_free_lockowner (struct nfs4_stateowner *sop)
@@ -4791,9 +4823,12 @@ static void nfs4_free_lockowner(struct nfs4_stateowner *sop)* strhashval = ownerstr_hashval */ static struct nfs4_lockowner * -alloc_init_lock_stateowner (unsigned int strhashval,struct nfs4_client *clp,struct nfs4_ol_stateid *open_stp,struct nfsd4_lock *lock) { -struct nfs4_lockowner *lo; +alloc_init_lock_stateowner (unsigned int strhashval,struct nfs4_client *clp, +struct nfs4_ol_stateid *open_stp, +struct nfsd4_lock *lock) +{struct nfsd_net *nn =net_generic (clp->net, nfsd_net_id); +struct nfs4_lockowner *lo, *ret; lo =alloc_stateowner (lockowner_slab, &lock->lk_new_owner, clp);if (!lo)
@@ -4803,7 +4838,16 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, str lo->lo_owner.so_seqid = lock->lk_new_lock_seqid; lo->lo_owner.so_free = nfs4_free_lockowner; lo->lo_owner.so_unhash = nfs4_unhash_lockowner; -list_add (&lo->lo_owner.so_strhash, &nn->ownerstr_hashtbl[strhashval]); +spin_lock (&nn->client_lock); + ret =find_lockowner_str_locked (&clp->cl_clientid, + &lock->lk_new_owner, nn); +if (ret ==NULL ) { +list_add (&lo->lo_owner.so_strhash, + &nn->ownerstr_hashtbl[strhashval]); + ret = lo; + }else +nfs4_free_lockowner (&lo->lo_owner); +spin_unlock (&nn->client_lock);return lo; }
@@ -5329,6 +5373,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);
@@ -5342,6 +5387,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, status = nfserr_locks_held;/* Find the matching lock stateowner */ +spin_lock (&nn->client_lock);list_for_each_entry (tmp, &nn->ownerstr_hashtbl[hashval], so_strhash) {if (tmp->so_is_open_owner)continue ;
@@ -5351,6 +5397,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) {
@@ -5360,16 +5407,22 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, lo =lockowner (sop);/* see if there are still any locks associated with it */ + clp = cstate->clp; +spin_lock (&clp->cl_lock);list_for_each_entry (stp, &sop->so_stateids, st_perstateowner) {if (check_for_locks (stp->st_stid.sc_file, lo)) { -nfs4_put_stateowner (sop); +spin_unlock (&clp->cl_lock); goto out; } } +spin_unlock (&clp->cl_lock); status = nfs_ok; + sop =NULL ;release_lockowner (lo); out: +if (sop) +nfs4_put_stateowner (sop);nfs4_unlock_state ();return status; }
Signature · 1 line
1.9.3
PATCH v3 69/114 nfsd: Move the open owner hash table into struct nfs4_client
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
Preparation for removing the client_mutex.
Convert the open owner hash table into a per-client table and protect it using the nfs4_client->cl_lock spin lock.
--- fs/nfsd/netns.h | 1 - fs/nfsd/nfs4state.c | 188 ++++++++++++++++++++++++---------------------------- fs/nfsd/state.h | 1 + 3 files changed, 87 insertions(+), 103 deletions(-)
fs/nfsd/netns.h +0 -1
@@ -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
fs/nfsd/nfs4state.c +86 -102
458 lines not on this page · raw message
fs/nfsd/state.h +1 -0
@@ -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;
Signature · 1 line
1.9.3
PATCH v3 70/114 nfsd: clean up and reorganize release_lockowner
In reply to Jeff LaytonDo more within the main loop, and simplify the function a bit. Also, there's no need to take a stateowner reference unless we're going to call release_lockowner.
--- fs/nfsd/nfs4state.c | 49 ++++++++++++++++++------------------------------- 1 file changed, 18 insertions(+), 31 deletions(-)
fs/nfsd/nfs4state.c +18 -31
@@ -5359,8 +5359,8 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, struct nfsd4_release_lockowner *rlockowner) {clientid_t *clid = &rlockowner->rl_clientid; -struct nfs4_stateowner *sop =NULL , *tmp; -struct nfs4_lockowner *lo; +struct nfs4_stateowner *sop; +struct nfs4_lockowner *lo =NULL ;struct nfs4_ol_stateid *stp;struct xdr_netobj *owner = &rlockowner->rl_owner;unsigned int hashval =ownerstr_hashval (owner);
@@ -5377,45 +5377,32 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,if (status) goto out; - status = nfserr_locks_held; - clp = cstate->clp;/* Find the matching lock stateowner */ spin_lock (&clp->cl_lock); -list_for_each_entry (tmp, &clp->cl_ownerstr_hashtbl[hashval], +list_for_each_entry (sop, &clp->cl_ownerstr_hashtbl[hashval], so_strhash) { -if (tmp->so_is_open_owner) -continue ; -if (same_owner_str (tmp, owner)) { - sop = tmp; -atomic_inc (&sop->so_count); -break ; - } - } -/* No matching owner found, maybe a replay? Just declare victory... */ -if (!sop) { -spin_unlock (&clp->cl_lock); - status = nfs_ok; - goto out; - } +if (sop->so_is_open_owner || !same_owner_str (sop, owner)) +continue ; - lo =lockowner (sop); -/* see if there are still any locks associated with it */ -list_for_each_entry (stp, &sop->so_stateids, st_perstateowner) { -if (check_for_locks (stp->st_stid.sc_file, lo)) { -spin_unlock (&clp->cl_lock); - goto out; +/* see if there are still any locks associated with it */ + lo =lockowner (sop); +list_for_each_entry (stp, &sop->so_stateids, st_perstateowner) { +if (check_for_locks (stp->st_stid.sc_file, lo)) { + status = nfserr_locks_held; +spin_unlock (&clp->cl_lock); + goto out; + } } + +atomic_inc (&sop->so_count); +break ; }spin_unlock (&clp->cl_lock); - - status = nfs_ok; - sop =NULL ; -release_lockowner (lo); +if (lo) +release_lockowner (lo); out: -if (sop) -nfs4_put_stateowner (sop);nfs4_unlock_state ();return status; }
Signature · 1 line
1.9.3
PATCH v3 71/114 lockdep: add lockdep_assert_not_held
In reply to Jeff LaytonWe 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 with atomic_dec_and_lock or similar).
--- fs/nfsd/nfs4state.c | 4 ++++ include/linux/lockdep.h | 4 ++++ 2 files changed, 8 insertions(+)
fs/nfsd/nfs4state.c +4 -0
@@ -263,6 +263,8 @@ static void nfsd4_free_file(struct nfs4_file *f)static inline void put_nfs4_file (struct nfs4_file *fi) { +lockdep_assert_not_held (&state_lock); +if (atomic_dec_and_lock (&fi->fi_ref, &state_lock)) {hlist_del (&fi->fi_hash);spin_unlock (&state_lock);
@@ -420,6 +422,8 @@ static __be32 nfs4_file_check_deny(struct nfs4_file *fp, u32 deny)static void __nfs4_file_put_access (struct nfs4_file *fp,int oflag) { +lockdep_assert_not_held (&fp->fi_lock); +if (atomic_dec_and_lock (&fp->fi_access[oflag], &fp->fi_lock)) {struct file *f1 =NULL ;struct file *f2 =NULL ;
include/linux/lockdep.h +4 -0
@@ -362,6 +362,8 @@ extern void lockdep_trace_alloc(gfp_t mask);WARN_ON (debug_locks && !lockdep_is_held (l)); \ } while (0) +#define lockdep_assert_not_held (l) WARN_ON(debug_locks && lockdep_is_held(l)) +#define lockdep_recursing (tsk) ((tsk)->lockdep_recursion)#else /* !CONFIG_LOCKDEP */
@@ -413,6 +415,8 @@ struct lock_class_key { };#define lockdep_assert_held (l) do { (void)(l); } while (0) +#define lockdep_assert_not_held (l) (void)(l) +#define lockdep_recursing (tsk) (0)#endif /* !LOCKDEP */
Signature · 1 line
1.9.3
Peter Zijlstra
maintainerRe: [PATCH v3 071/114] lockdep: add lockdep_assert_not_held
Quoted 4 lines · Jeff Layton
We currently have the ability to call
lockdep_assert_heldto 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 withatomic_dec_and_lockor similar).
So I'm not getting it; if there's deadlock potential, lockdep would already report so, right?
That is, lockdep is very good a yelling when you try to acquire a lock you're already holding.
Peter Zijlstra
maintainerRe: [PATCH v3 071/114] lockdep: add lockdep_assert_not_held
Quoted 11 lines · Peter Zijlstra
Quoted 4 lines · Jeff Layton
We currently have the ability to call
lockdep_assert_heldto 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 withatomic_dec_and_lockor similar).So I'm not getting it; if there's deadlock potential, lockdep would already report so, right?
That is, lockdep is very good a yelling when you try to acquire a lock you're already holding.
Ah, I think I see what you're trying to do.
You're wanting to use might_lock() and might_lock_read(). Which are used to make conditional lock acquisitions unconditional, so as to more reliably trigger the deadlock detection.
Re: [PATCH v3 071/114] lockdep: add lockdep_assert_not_held
Quoted 10 lines · On Tue, 1 Jul 2014 12:11:08 +0200 Peter Zijlstra <[email protected]> wrote:
Quoted 9 lines · Peter Zijlstra
Quoted 4 lines · Jeff Layton
We currently have the ability to call
lockdep_assert_heldto 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 withatomic_dec_and_lockor similar).So I'm not getting it; if there's deadlock potential, lockdep would already report so, right?
Quoted 8 lines
Quoted 2 lines
That is, lockdep is very good a yelling when you try to acquire a lock you're already holding.
Ah, I think I see what you're trying to do.
You're wanting to use
might_lock()andmight_lock_read(). Which are used to make conditional lock acquisitions unconditional, so as to more reliably trigger the deadlock detection.
Right -- in the case of something like atomic_dec_and_lock, we only take the spinlock if we think the count might go to zero. So, we might miss catching some places that could deadlock if the refcounts don't go to zero in the testing we're doing.
might_lock may be what we need, but I don't see any callers of it, and at a quick glance it doesn't appear to be disabled if debug_locks is false.
Signature · 1 line
Jeff Layton <[email protected]>
Peter Zijlstra
maintainerRe: [PATCH v3 071/114] lockdep: add lockdep_assert_not_held
Quoted 6 lines · Jeff Layton
Right -- in the case of something like
atomic_dec_and_lock, we only take the spinlock if we think the count might go to zero. So, we might miss catching some places that could deadlock if the refcounts don't go to zero in the testing we're doing.
might_lockmay be what we need, but I don't see any callers of it,
might_fault()
might_lock_read(¤t->mm->mmap_sem)
I'm not sure we have others; but this is the one I remember providing this for.
Quoted 3 lines
and
at a quick glance it doesn't appear to be disabled ifdebug_locksis false.
The whole of lockdep gets killed, and that would include the lock_acquire/lock_release used to implement these.
Re: [PATCH v3 071/114] lockdep: add lockdep_assert_not_held
Quoted 14 lines · On Tue, 1 Jul 2014 13:07:18 +0200 Peter Zijlstra <[email protected]> wrote:
Quoted 6 lines · Jeff Layton
Right -- in the case of something like
atomic_dec_and_lock, we only take the spinlock if we think the count might go to zero. So, we might miss catching some places that could deadlock if the refcounts don't go to zero in the testing we're doing.
might_lockmay be what we need, but I don't see any callers of it,
might_fault()
might_lock_read(¤t->mm->mmap_sem)I'm not sure we have others; but this is the one I remember providing this for.
Quoted 6 lines
Quoted 3 lines
and
at a quick glance it doesn't appear to be disabled ifdebug_locksis false.The whole of lockdep gets killed, and that would include the lock_acquire/lock_release used to implement these.
Ok, good. Let's just drop this patch then and I'll plan to use might_lock instead of this call.
Thanks!
Signature · 1 line
Jeff Layton <[email protected]>
PATCH v3 72/114 nfsd: add locking to stateowner release
In reply to Jeff LaytonOnce we remove the client_mutex, we'll need to properly protect the stateowner reference counts using the cl_lock.
--- fs/nfsd/nfs4state.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-)
fs/nfsd/nfs4state.c +8 -9
@@ -638,6 +638,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 ;
@@ -3012,9 +3014,14 @@ static inline void *alloc_stateowner(struct kmem_cache *slab, struct xdr_netobjstatic 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); }
@@ -3029,11 +3036,7 @@ static void hash_openowner(struct nfs4_openowner *oo, struct nfs4_client *clp, ustatic 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)
@@ -4799,11 +4802,7 @@ find_lockowner_str(clientid_t *clid, struct xdr_netobj *owner,static void nfs4_unhash_lockowner (struct nfs4_stateowner *sop) { -struct nfs4_client *clp = sop->so_client; - -spin_lock (&clp->cl_lock);unhash_lockowner_locked (lockowner (sop)); -spin_unlock (&clp->cl_lock); }static void nfs4_free_lockowner (struct nfs4_stateowner *sop)
Signature · 1 line
1.9.3
PATCH v3 73/114 nfsd: optimize destroy_lockowner cl_lock thrashing
In reply to Jeff LaytonReduce 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.
--- fs/nfsd/nfs4state.c | 50 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 17 deletions(-)
fs/nfsd/nfs4state.c +33 -17
@@ -920,14 +920,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); }
@@ -941,30 +950,37 @@ static void unhash_lockowner_locked(struct nfs4_lockowner *lo)list_del_init (&lo->lo_owner.so_strhash); } -static void release_lockowner_stateids (struct nfs4_lockowner *lo) +static void release_lockowner (struct nfs4_lockowner *lo) {struct nfs4_client *clp = lo->lo_owner.so_client;struct nfs4_ol_stateid *stp; +struct list_head reaplist; -lockdep_assert_held (&clp->cl_lock); +INIT_LIST_HEAD (&reaplist); +spin_lock (&clp->cl_lock); +unhash_lockowner_locked (lo);while (!list_empty (&lo->lo_owner.so_stateids)) { stp =list_first_entry (&lo->lo_owner.so_stateids, struct nfs4_ol_stateid, st_perstateowner); -spin_unlock (&clp->cl_lock); -release_lock_stateid (stp); -spin_lock (&clp->cl_lock); +unhash_lock_stateid (stp); +/* +* We now know that no new references can be added to the +* stateid. If ours is the last one, finish the unhashing +* and put it on the list to be reaped. +*/ +if (atomic_dec_and_test (&stp->st_stid.sc_count)) { +remove_stid_locked (clp, &stp->st_stid); +list_add (&stp->st_locks, &reaplist); + } } -} - -static void release_lockowner (struct nfs4_lockowner *lo) -{ -struct nfs4_client *clp = lo->lo_owner.so_client; - -spin_lock (&clp->cl_lock); -unhash_lockowner_locked (lo); -release_lockowner_stateids (lo);spin_unlock (&clp->cl_lock); +while (!list_empty (&reaplist)) { + stp = list_first_entry(&reaplist, struct nfs4_ol_stateid, + st_locks); +list_del (&stp->st_locks); + stp->st_stid.sc_free (&stp->st_stid); + }nfs4_put_stateowner (&lo->lo_owner); }
Signature · 1 line
1.9.3
PATCH v3 74/114 nfsd: close potential race in nfsd4_free_stateid
In reply to Jeff LaytonOnce we remove the client_mutex, it'll be possible for the sc_type of a lock stateid to change after it's found and checked, but before we can go to destroy it. If that happens, we can end up putting the persistent reference to the stateid more than once, and unhash it more than once.
Fix this by unhashing the lock stateid prior to dropping the cl_lock but after finding it.
--- fs/nfsd/nfs4state.c | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-)
fs/nfsd/nfs4state.c +9 -15
@@ -4357,17 +4357,6 @@ unlock_state:return status; } -static __be32 -nfsd4_free_lock_stateid (struct nfs4_ol_stateid *stp) -{ -struct nfs4_lockowner *lo =lockowner (stp->st_stateowner); - -if (check_for_locks (stp->st_stid.sc_file, lo)) -return nfserr_locks_held; -release_lock_stateid (stp); -return nfs_ok; -} -/* * Test if the stateid is valid */
@@ -4394,6 +4383,7 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,stateid_t *stateid = &free_stateid->fr_stateid;struct nfs4_stid *s;struct nfs4_delegation *dp; +struct nfs4_ol_stateid *stp;struct nfs4_client *cl = cstate->session->se_client;__be32 ret = nfserr_bad_stateid;
@@ -4411,12 +4401,16 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ret =check_stateid_generation (stateid, &s->sc_stateid,1 );if (ret)break ; -if (s->sc_type !=NFS4_LOCK_STID ) { - ret = nfserr_locks_held; + stp =openlockstateid (s); + ret = nfserr_locks_held; +if (s->sc_type ==NFS4_OPEN_STID || +check_for_locks (s->sc_file, +lockowner (stp->st_stateowner)))break ; - } +unhash_lock_stateid (stp);spin_unlock (&cl->cl_lock); - ret =nfsd4_free_lock_stateid (openlockstateid (s)); +put_generic_stateid (stp); + ret = nfs_ok; goto out;case NFS4_REVOKED_DELEG_STID :spin_unlock (&cl->cl_lock);
Signature · 1 line
1.9.3
PATCH v3 75/114 nfsd: reduce cl_lock thrashing in release_openowner
In reply to Jeff LaytonReleasing an openowner is a bit inefficient as it can potentially thrash the cl_lock if you have a lot of stateids attached to it. Once we remove the client_mutex, it'll also potentially be dangerous to do this.
Add some functions to make it easier to defer the part of putting a generic stateid reference that needs to be done outside the cl_lock while doing the parts that must be done while holding it under a single lock.
First we unhash each open stateid. Then we call put_generic_stateid_locked which will put the reference to an nfs4_ol_stateid. If it turns out to be the last reference, it'll go ahead and remove the stid from the IDR tree and put it onto the reaplist using the st_locks list_head.
Then, after dropping the lock we'll call free_stateid_reaplist to walk the list of stateids that are fully unhashed and ready to be freed, and free each of them. This function can sleep, so it must be done outside any spinlocks.
--- fs/nfsd/nfs4state.c | 95 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 63 insertions(+), 32 deletions(-)
fs/nfsd/nfs4state.c +63 -32
@@ -920,6 +920,30 @@ static void put_generic_stateid(struct nfs4_ol_stateid *stp)nfs4_put_stid (&stp->st_stid); } +/* +* Put the persistent reference to an already unhashed generic stateid, while +* holding the cl_lock. If it's the last reference, then put it onto the +* reaplist for later destruction. +*/ +static void put_generic_stateid_locked (struct nfs4_ol_stateid *stp, +struct list_head *reaplist) +{ +struct nfs4_stid *s = &stp->st_stid; +struct nfs4_client *clp = s->sc_client; + +lockdep_assert_held (&clp->cl_lock); + +WARN_ON_ONCE (!list_empty (&stp->st_locks)); + +if (!atomic_dec_and_test (&s->sc_count)) { +wake_up_all (&close_wq); +return ; + } + +remove_stid_locked (clp, s); +list_add (&stp->st_locks, reaplist); +} +static void unhash_lock_stateid (struct nfs4_ol_stateid *stp) {struct nfs4_openowner *oo =openowner (stp->st_openstp->st_stateowner);
@@ -950,6 +974,25 @@ static void unhash_lockowner_locked(struct nfs4_lockowner *lo)list_del_init (&lo->lo_owner.so_strhash); } +/* +* Free a list of generic stateids that were collected earlier after being +* fully unhashed. +*/ +static void +free_stateid_reaplist (struct list_head *reaplist) +{ +struct nfs4_ol_stateid *stp; + +might_sleep (); + +while (!list_empty (reaplist)) { + stp = list_first_entry(reaplist, struct nfs4_ol_stateid, + st_locks); +list_del (&stp->st_locks); + stp->st_stid.sc_free (&stp->st_stid); + } +} +static void release_lockowner (struct nfs4_lockowner *lo) {struct nfs4_client *clp = lo->lo_owner.so_client;
@@ -964,23 +1007,10 @@ static void release_lockowner(struct nfs4_lockowner *lo) stp =list_first_entry (&lo->lo_owner.so_stateids, struct nfs4_ol_stateid, st_perstateowner);unhash_lock_stateid (stp); -/* -* We now know that no new references can be added to the -* stateid. If ours is the last one, finish the unhashing -* and put it on the list to be reaped. -*/ -if (atomic_dec_and_test (&stp->st_stid.sc_count)) { -remove_stid_locked (clp, &stp->st_stid); -list_add (&stp->st_locks, &reaplist); - } +put_generic_stateid_locked (stp, &reaplist); }spin_unlock (&clp->cl_lock); -while (!list_empty (&reaplist)) { - stp = list_first_entry(&reaplist, struct nfs4_ol_stateid, - st_locks); -list_del (&stp->st_locks); - stp->st_stid.sc_free (&stp->st_stid); - } +free_stateid_reaplist (&reaplist);nfs4_put_stateowner (&lo->lo_owner); }
@@ -1001,16 +1031,21 @@ static void release_open_stateid_locks(struct nfs4_ol_stateid *open_stp)static void unhash_open_stateid (struct nfs4_ol_stateid *stp) { -spin_lock (&stp->st_stateowner->so_client->cl_lock); +lockdep_assert_held (&stp->st_stid.sc_client->cl_lock); +unhash_generic_stateid (stp);release_open_stateid_locks (stp); -spin_unlock (&stp->st_stateowner->so_client->cl_lock); }static void release_open_stateid (struct nfs4_ol_stateid *stp) { +LIST_HEAD (reaplist); + +spin_lock (&stp->st_stid.sc_client->cl_lock);unhash_open_stateid (stp); -put_generic_stateid (stp); +put_generic_stateid_locked (stp, &reaplist); +spin_unlock (&stp->st_stid.sc_client->cl_lock); +free_stateid_reaplist (&reaplist); }static void unhash_openowner_locked (struct nfs4_openowner *oo)
@@ -1034,30 +1069,24 @@ static void release_last_closed_stateid(struct nfs4_openowner *oo) } } -static void release_openowner_stateids (struct nfs4_openowner *oo) +static void release_openowner (struct nfs4_openowner *oo) {struct nfs4_ol_stateid *stp;struct nfs4_client *clp = oo->oo_owner.so_client; +struct list_head reaplist; -lockdep_assert_held (&clp->cl_lock); +INIT_LIST_HEAD (&reaplist); +spin_lock (&clp->cl_lock); +unhash_openowner_locked (oo);while (!list_empty (&oo->oo_owner.so_stateids)) { stp =list_first_entry (&oo->oo_owner.so_stateids, struct nfs4_ol_stateid, st_perstateowner); -spin_unlock (&clp->cl_lock); -release_open_stateid (stp); -spin_lock (&clp->cl_lock); +unhash_open_stateid (stp); +put_generic_stateid_locked (stp, &reaplist); } -} - -static void release_openowner (struct nfs4_openowner *oo) -{ -struct nfs4_client *clp = oo->oo_owner.so_client; - -spin_lock (&clp->cl_lock); -unhash_openowner_locked (oo); -release_openowner_stateids (oo);spin_unlock (&clp->cl_lock); +free_stateid_reaplist (&reaplist);release_last_closed_stateid (oo);nfs4_put_stateowner (&oo->oo_owner); }
@@ -4630,7 +4659,9 @@ static void nfsd4_close_open_stateid(struct nfs4_ol_stateid *s)struct nfs4_client *clp = s->st_stid.sc_client; s->st_stid.sc_type =NFS4_CLOSED_STID ; +spin_lock (&clp->cl_lock);unhash_open_stateid (s); +spin_unlock (&clp->cl_lock);if (clp->cl_minorversion)put_generic_stateid (s);
Signature · 1 line
1.9.3
PATCH v3 76/114 nfsd: don't thrash the cl_lock while freeing an open stateid
In reply to Jeff LaytonWhen we remove the client_mutex, we'll have a potential race between FREE_STATEID and CLOSE.
The root of the problem is that we are walking the st_locks list, dropping the spinlock and then trying to release the persistent reference to the lockstateid. In between, a FREE_STATEID call can come along and take the lock, find the stateid and then try to put the reference. That leads to a double put.
Fix this by not releasing the cl_lock in order to release each lock stateid. Use put_generic_stateid_locked to unhash them and gather them onto a list, and free_stateid_reaplist to free any that end up on the list.
--- fs/nfsd/nfs4state.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-)
fs/nfsd/nfs4state.c +19 -15
@@ -1014,27 +1014,26 @@ static void release_lockowner(struct nfs4_lockowner *lo)nfs4_put_stateowner (&lo->lo_owner); } -static void release_open_stateid_locks (struct nfs4_ol_stateid *open_stp) - __releases(&open_stp->st_stateowner->so_client->cl_lock) - __acquires(&open_stp->st_stateowner->so_client->cl_lock) +static void release_open_stateid_locks (struct nfs4_ol_stateid *open_stp, +struct list_head *reaplist) {struct nfs4_ol_stateid *stp;while (!list_empty (&open_stp->st_locks)) { stp =list_entry (open_stp->st_locks.next, struct nfs4_ol_stateid, st_locks); -spin_unlock (&open_stp->st_stateowner->so_client->cl_lock); -release_lock_stateid (stp); -spin_lock (&open_stp->st_stateowner->so_client->cl_lock); +unhash_lock_stateid (stp); +put_generic_stateid_locked (stp, reaplist); } } -static void unhash_open_stateid (struct nfs4_ol_stateid *stp) +static void unhash_open_stateid (struct nfs4_ol_stateid *stp, +struct list_head *reaplist) {lockdep_assert_held (&stp->st_stid.sc_client->cl_lock);unhash_generic_stateid (stp); -release_open_stateid_locks (stp); +release_open_stateid_locks (stp, reaplist); }static void release_open_stateid (struct nfs4_ol_stateid *stp)
@@ -1042,7 +1041,7 @@ static void release_open_stateid(struct nfs4_ol_stateid *stp)LIST_HEAD (reaplist);spin_lock (&stp->st_stid.sc_client->cl_lock); -unhash_open_stateid (stp); +unhash_open_stateid (stp, &reaplist);put_generic_stateid_locked (stp, &reaplist);spin_unlock (&stp->st_stid.sc_client->cl_lock);free_stateid_reaplist (&reaplist);
@@ -1082,7 +1081,7 @@ static void release_openowner(struct nfs4_openowner *oo)while (!list_empty (&oo->oo_owner.so_stateids)) { stp =list_first_entry (&oo->oo_owner.so_stateids, struct nfs4_ol_stateid, st_perstateowner); -unhash_open_stateid (stp); +unhash_open_stateid (stp, &reaplist);put_generic_stateid_locked (stp, &reaplist); }spin_unlock (&clp->cl_lock);
@@ -4657,16 +4656,21 @@ out:static void nfsd4_close_open_stateid (struct nfs4_ol_stateid *s) {struct nfs4_client *clp = s->st_stid.sc_client; +LIST_HEAD (reaplist); s->st_stid.sc_type =NFS4_CLOSED_STID ;spin_lock (&clp->cl_lock); -unhash_open_stateid (s); -spin_unlock (&clp->cl_lock); +unhash_open_stateid (s, &reaplist); -if (clp->cl_minorversion) -put_generic_stateid (s); -else +if (clp->cl_minorversion) { +put_generic_stateid_locked (s, &reaplist); +spin_unlock (&clp->cl_lock); +free_stateid_reaplist (&reaplist); + }else { +spin_unlock (&clp->cl_lock); +free_stateid_reaplist (&reaplist);move_to_close_lru (s, clp->net); + } }/*
Signature · 1 line
1.9.3
PATCH v3 77/114 nfsd: Ensure struct nfs4_client is unhashed before we try to destroy it
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
When we remove the client_mutex protection, we will need to ensure that it can't be found by other threads while we're destroying it.
--- fs/nfsd/nfs4state.c | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-)
fs/nfsd/nfs4state.c +33 -10
@@ -1529,12 +1529,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);
@@ -1542,7 +1553,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;
@@ -1575,22 +1596,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)
Signature · 1 line
1.9.3
PATCH v3 78/114 nfsd: Ensure that the laundromat unhashes the client before releasing locks
In reply to Jeff LaytonFrom: 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.
--- fs/nfsd/nfs4state.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
fs/nfsd/nfs4state.c +3 -1
@@ -4088,13 +4088,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);
Signature · 1 line
1.9.3
PATCH v3 79/114 nfsd: Don't require client_lock in free_client
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
The struct nfs_client is supposed to be invisible and unreferenced before it gets here.
--- fs/nfsd/nfs4state.c | 13 ------------- 1 file changed, 13 deletions(-)
fs/nfsd/nfs4state.c +0 -13
@@ -1355,9 +1355,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);
@@ -1507,9 +1504,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,
@@ -1568,7 +1562,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);
@@ -1596,10 +1589,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
@@ -1802,7 +1792,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 )
@@ -1810,9 +1799,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 ; } clp->cl_time =get_seconds ();
Signature · 1 line
1.9.3
PATCH v3 80/114 nfsd: Move create_client() call outside the lock
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
For efficiency reasons, and because we want to use spin locks instead of relying on the client_mutex.
--- fs/nfsd/nfs4state.c | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-)
fs/nfsd/nfs4state.c +19 -16
@@ -2120,6 +2120,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);
@@ -2146,7 +2150,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 */
@@ -2159,7 +2162,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 */
@@ -2177,29 +2179,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; }
@@ -2842,6 +2843,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);
@@ -2862,10 +2866,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);
@@ -2877,9 +2877,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; }
Signature · 1 line
1.9.3
PATCH v3 81/114 nfsd: Protect unconfirmed client creation using client_lock
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
...instead of relying on the client_mutex.
--- fs/nfsd/nfs4state.c | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-)
fs/nfsd/nfs4state.c +22 -11
@@ -1862,7 +1862,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
@@ -1876,7 +1876,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 *
@@ -1889,7 +1889,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; } }
@@ -2091,7 +2091,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;
@@ -2126,6 +2127,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);
@@ -2157,7 +2159,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 */
@@ -2165,6 +2166,7 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, goto out_copy; }/* case 5, client reboot */ + conf =NULL ; goto out_new; }
@@ -2175,17 +2177,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;
@@ -2198,9 +2201,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; }
@@ -2839,7 +2845,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);
@@ -2848,6 +2855,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: */
@@ -2865,7 +2873,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);
@@ -2880,9 +2888,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; }
Signature · 1 line
1.9.3
PATCH v3 82/114 nfsd: Protect session creation and client confirm using client_lock
In reply to Jeff LaytonIn 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. instead of relying on the client_mutex.
--- fs/nfsd/nfs4state.c | 65 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 26 deletions(-)
fs/nfsd/nfs4state.c +39 -26
@@ -141,17 +141,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))
@@ -1376,12 +1365,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);
@@ -2350,6 +2337,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 ;
@@ -2376,6 +2364,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);
@@ -2394,7 +2383,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;
@@ -2412,10 +2400,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;
@@ -2431,20 +2419,29 @@ 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 ); 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 */ +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:
@@ -2904,6 +2901,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;
@@ -2913,6 +2911,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);/*
@@ -2936,21 +2935,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; }
@@ -5586,7 +5593,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 ;
Signature · 1 line
1.9.3
PATCH v3 83/114 nfsd: Protect nfsd4_destroy_clientid using client_lock
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
...instead of relying on the client_mutex.
--- fs/nfsd/nfs4state.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-)
fs/nfsd/nfs4state.c +9 -4
@@ -2765,22 +2765,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 {
@@ -2788,12 +2789,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; }
Signature · 1 line
1.9.3
PATCH v3 84/114 nfsd: Ensure lookup_clientid() takes client_lock
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
Ensure that the client lookup is done safely under the client_lock, so we're not relying on the client_mutex.
--- fs/nfsd/nfs4state.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
fs/nfsd/nfs4state.c +6 -2
@@ -3430,13 +3430,17 @@ static __be32 lookup_clientid(clientid_t *clid,* will be false. */ WARN_ON_ONCE (cstate->session); +spin_lock (&nn->client_lock); found =find_confirmed_client (clid, false, nn); -if (!found) +if (!found) { +spin_unlock (&nn->client_lock);return nfserr_expired; + } +atomic_inc (&found->cl_refcount); +spin_unlock (&nn->client_lock);/* Cache the nfs4_client in cstate! */ cstate->clp = found; -atomic_inc (&found->cl_refcount);return nfs_ok; }
Signature · 1 line
1.9.3
PATCH v3 85/114 nfsd: Add lockdep assertions to document the nfs4_client/session locking
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
--- fs/nfsd/nfs4state.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+)
fs/nfsd/nfs4state.c +30 -0
@@ -143,6 +143,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);
@@ -183,6 +187,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))
@@ -214,6 +222,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);
@@ -1392,6 +1403,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 */
@@ -1428,6 +1441,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);
@@ -1516,6 +1534,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 */
@@ -1845,6 +1865,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);
@@ -1858,6 +1880,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);
@@ -1888,6 +1912,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); }
@@ -1896,6 +1921,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); }
@@ -1907,12 +1933,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); }
@@ -4863,6 +4891,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)
Signature · 1 line
1.9.3
PATCH v3 86/114 nfsd: protect the close_lru list and oo_last_closed_stid with client_lock
In reply to Jeff LaytonCurrently, it's protected by the client_mutex. Move it so that the list and the fields in the openowner are protected by the client_lock.
--- fs/nfsd/nfs4state.c | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-)
fs/nfsd/nfs4state.c +30 -7
@@ -1059,13 +1059,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)
@@ -3217,6 +3223,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);
@@ -3240,10 +3247,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
@@ -4111,6 +4122,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;
@@ -4162,15 +4174,26 @@ 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); -if (time_after ((unsigned long )oo->oo_time, (unsigned long )cutoff)) { + +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;
Signature · 1 line
1.9.3
PATCH v3 87/114 nfsd: ensure that clp->cl_revoked list is protected by clp->cl_lock
In reply to Jeff LaytonCurrently, both destroy_revoked_delegation and revoke_delegation manipulate the cl_revoked list without any locking aside from the client_mutex. 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, and so it should be safe to walk the list without any locking. That 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_head.
--- fs/nfsd/nfs4state.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-)
fs/nfsd/nfs4state.c +14 -7
@@ -724,7 +724,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); }
@@ -732,11 +732,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); } }
@@ -1589,9 +1593,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)) {
@@ -4170,8 +4174,10 @@ nfs4_laundromat(struct nfsd_net *nn)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); +while (!list_empty (&reaplist)) { + dp = list_first_entry(&reaplist, struct nfs4_delegation, + dl_recall_lru); +list_del_init (&dp->dl_recall_lru);revoke_delegation (dp); }
@@ -4534,8 +4540,9 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ret = nfs_ok; goto out;case NFS4_REVOKED_DELEG_STID : -spin_unlock (&cl->cl_lock); dp =delegstateid (s); +list_del_init (&dp->dl_recall_lru); +spin_unlock (&cl->cl_lock);destroy_revoked_delegation (dp); ret = nfs_ok; goto out;
Signature · 1 line
1.9.3
PATCH v3 88/114 nfsd: move unhash_client_locked call into mark_client_expired_locked
In reply to Jeff LaytonAll 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.
--- fs/nfsd/nfs4state.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-)
fs/nfsd/nfs4state.c +2 -4
@@ -70,6 +70,7 @@ static u64 current_sessionid = 1;#define CURRENT_STATEID (stateid) (!memcmp((stateid), ¤tstateid, 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(
@@ -137,7 +138,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; }
@@ -2441,7 +2442,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;
@@ -2987,7 +2987,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;
@@ -4149,7 +4148,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);
Signature · 1 line
1.9.3
PATCH v3 89/114 nfsd: don't destroy client if mark_client_expired_locked fails
In reply to Jeff LaytonIf it fails, it means that the client is in use and so destroying it would be bad. Currently, the client_mutex prevents this from happening but once we remove it, we won't be able to do this.
--- fs/nfsd/nfs4state.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
fs/nfsd/nfs4state.c +6 -2
@@ -2440,8 +2440,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;
@@ -2985,8 +2987,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;
Signature · 1 line
1.9.3
PATCH v3 90/114 nfsd: don't destroy clients that are busy
In reply to Jeff LaytonIt's possible that we'll have an in-progress call on some of the clients while a rogue EXCHANGE_ID or DESTROY_CLIENTID call comes in. Be sure to try and mark the client expired first, so that the refcount is respected.
This will only be a problem once the client_mutex is removed.
--- fs/nfsd/nfs4state.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
fs/nfsd/nfs4state.c +8 -2
@@ -2207,8 +2207,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 );
@@ -2821,6 +2824,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;
Signature · 1 line
1.9.3
PATCH v3 91/114 nfsd: protect clid and verifier generation with client_lock
In reply to Jeff LaytonThe clid counter is a global counter currently. Move it to be a per-net property so that it can be properly protected by the nn->client_lock instead of relying on the client_mutex.
The verifier generator is also potentially racy if there are two simultaneous callers. Generate the verifier when we generate the clid value, so it's also created under the client_lock. With this, there's no need to keep two counters as they'd always be in sync anyway, so just use the clientid_counter for both.
As Trond points out, what would be best is to eventually move this code to use IDR instead of the hash tables. That would also help ensure uniqueness, but that's probably best done as a separate project.
--- fs/nfsd/netns.h | 6 +++--- fs/nfsd/nfs4state.c | 21 +++++++++------------ 2 files changed, 12 insertions(+), 15 deletions(-)
fs/nfsd/netns.h +3 -3
@@ -92,11 +92,11 @@ struct nfsd_net {bool nfsd_net_up;bool lockd_up; -/* -* Time of server startup -*/ +/* Time of server startup */ struct timeval nfssvc_boot; +u32 clientid_counter; +struct svc_serv *nfsd_serv; };
fs/nfsd/nfs4state.c +9 -12
@@ -1754,28 +1754,26 @@ static bool mach_creds_match(struct nfs4_client *cl, struct svc_rqst *rqstp)return 0 ==strcmp (cl->cl_cred.cr_principal, cr->cr_principal); } -static void gen_clid (struct nfs4_client *clp,struct nfsd_net *nn) -{ -static u32 current_clientid =1 ; - - clp->cl_clientid.cl_boot = nn->boot_time; - clp->cl_clientid.cl_id = current_clientid++; -} - -static void gen_confirm (struct nfs4_client *clp) +static void gen_confirm (struct nfs4_client *clp,struct nfsd_net *nn) {__be32 verf[2 ]; -static u32 i;/* * This is opaque to client, so no need to byte-swap. Use * __force to keep sparse happy */ verf[0 ] = (__force __be32 )get_seconds (); - verf[1 ] = (__force __be32 )i++; + verf[1 ] = (__force __be32 )nn->clientid_counter;memcpy (clp->cl_confirm.data, verf,sizeof (clp->cl_confirm.data)); } +static void gen_clid (struct nfs4_client *clp,struct nfsd_net *nn) +{ + clp->cl_clientid.cl_boot = nn->boot_time; + clp->cl_clientid.cl_id = nn->clientid_counter++; +gen_confirm (clp, nn); +} +static struct nfs4_stid *find_stateid_locked (struct nfs4_client *cl,stateid_t *t) {
@@ -1824,7 +1822,6 @@ static struct nfs4_client *create_client(struct xdr_netobj name,clear_bit (0 , &clp->cl_cb_slot_busy);copy_verf (clp, verf);rpc_copy_addr ((struct sockaddr *) &clp->cl_addr, sa); -gen_confirm (clp); clp->cl_cb_session =NULL ; clp->net = net;return clp;
Signature · 1 line
1.9.3
PATCH v3 92/114 nfsd: abstract out the get and set routines into the fault injection ops
In reply to Jeff LaytonNow that we've added more granular locking in other places, it's time to address the fault injection code. This code is currently quite reliant on the client_mutex for protection. Start to change this by adding a new set of fault injection op vectors.
For now they all use the legacy 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.
--- fs/nfsd/fault_inject.c | 129 ++++++++++++++++++++++++++++++------------------- 1 file changed, 78 insertions(+), 51 deletions(-)
fs/nfsd/fault_inject.c +78 -51
@@ -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,20 @@ 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) +pr_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 ) +pr_info ("NFSD Fault Injection: %s (all)" , op->file); +else +pr_info ("NFSD Fault Injection: %s (n = %llu)" , + op->file, val); + val = op->set_val (op, val); +pr_info ("NFSD: %s: found %llu" , op->file, val); }return len;/* on success, claim we got the whole input */ }
@@ -149,6 +131,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;
Signature · 1 line
1.9.3
PATCH v3 93/114 nfsd: add a forget_clients "get" routine with proper locking
In reply to Jeff LaytonAdd a new "get" routine for forget_clients that relies on the client_lock instead of the client_mutex.
--- fs/nfsd/fault_inject.c | 3 +-- fs/nfsd/nfs4state.c | 30 ++++++++++++++++++++++-------- fs/nfsd/state.h | 4 +++- 3 files changed, 26 insertions(+), 11 deletions(-)
fs/nfsd/fault_inject.c +1 -2
@@ -134,11 +134,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" ,
fs/nfsd/nfs4state.c +22 -8
@@ -5664,6 +5664,28 @@ 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)); +pr_info ("NFS Client: %s\n" , buf); + ++count; + } +spin_unlock (&nn->client_lock); + +return count; +}u64 nfsd_forget_client (struct nfs4_client *clp,u64 max) {
@@ -5679,14 +5701,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) {
fs/nfsd/state.h +3 -1
@@ -466,18 +466,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 );
Signature · 1 line
1.9.3
PATCH v3 94/114 nfsd: add a forget_client set_clnt routine
In reply to Jeff Layton...that relies on the client_lock instead of client_mutex.
--- fs/nfsd/fault_inject.c | 2 +- fs/nfsd/nfs4state.c | 28 ++++++++++++++++++++++++++++ fs/nfsd/state.h | 3 +++ 3 files changed, 32 insertions(+), 1 deletion(-)
fs/nfsd/fault_inject.c +1 -1
@@ -136,7 +136,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, }, {
fs/nfsd/nfs4state.c +28 -0
@@ -5701,6 +5701,34 @@ 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) {
fs/nfsd/state.h +3 -0
@@ -475,6 +475,9 @@ 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 );
Signature · 1 line
1.9.3
PATCH v3 95/114 nfsd: add nfsd_inject_forget_clients
In reply to Jeff Layton...which uses the client_lock for protection instead of client_mutex. Also remove nfsd_forget_client as there are no more callers.
--- fs/nfsd/fault_inject.c | 3 +-- fs/nfsd/nfs4state.c | 42 ++++++++++++++++++++++++++++-------------- fs/nfsd/state.h | 2 +- 3 files changed, 30 insertions(+), 17 deletions(-)
fs/nfsd/fault_inject.c +1 -2
@@ -135,9 +135,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",
fs/nfsd/nfs4state.c +28 -14
54 lines not on this page · raw message
fs/nfsd/state.h +1 -1
@@ -474,9 +474,9 @@ 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 );
Signature · 1 line
1.9.3
PATCH v3 96/114 nfsd: add a list_head arg to nfsd_foreach_client_lock
In reply to Jeff LaytonIn a later patch, we'll want to collect the locks onto a list for later destruction. If "func" is defined and "collect" is defined, then we'll add the lock stateid to the list.
--- fs/nfsd/nfs4state.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
fs/nfsd/nfs4state.c +8 -3
@@ -5752,6 +5752,7 @@ static void nfsd_print_count(struct nfs4_client *clp, unsigned int count, }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;
@@ -5764,8 +5765,12 @@ static u64 nfsd_foreach_client_lock(struct nfs4_client *clp, u64 max, &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; }
@@ -5777,12 +5782,12 @@ static u64 nfsd_foreach_client_lock(struct nfs4_client *clp, u64 max,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; }
Signature · 1 line
1.9.3
PATCH v3 97/114 nfsd: add more granular locking to forget_locks fault injector
In reply to Jeff Layton...instead of relying on the client_mutex.
--- fs/nfsd/fault_inject.c | 8 ++- fs/nfsd/nfs4state.c | 136 +++++++++++++++++++++++++++++++++++++++++++++---- fs/nfsd/state.h | 7 ++- 3 files changed, 135 insertions(+), 16 deletions(-)
fs/nfsd/fault_inject.c +3 -5
@@ -140,11 +140,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",
fs/nfsd/nfs4state.c +127 -9
176 lines not on this page · raw message
fs/nfsd/state.h +5 -2
@@ -478,12 +478,15 @@ 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 */
Signature · 1 line
1.9.3
PATCH v3 98/114 nfsd: add more granular locking to forget_openowners fault injector
In reply to Jeff Layton...instead of relying on the client_mutex.
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.
--- fs/nfsd/fault_inject.c | 8 ++-- fs/nfsd/nfs4state.c | 122 +++++++++++++++++++++++++++++++++++++++++++++---- fs/nfsd/state.h | 7 ++- 3 files changed, 122 insertions(+), 15 deletions(-)
fs/nfsd/fault_inject.c +3 -5
@@ -146,11 +146,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",
fs/nfsd/nfs4state.c +114 -8
144 lines not on this page · raw message
fs/nfsd/state.h +5 -2
@@ -483,11 +483,14 @@ 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 ; }
Signature · 1 line
1.9.3
PATCH v3 99/114 nfsd: add more granular locking to *_delegations fault injectors
In reply to Jeff Layton...instead of relying on the client_mutex.
--- fs/nfsd/fault_inject.c | 16 ++--- fs/nfsd/nfs4state.c | 167 ++++++++++++++++++++++++++++++++++++++++++------- fs/nfsd/state.h | 11 ++-- 3 files changed, 157 insertions(+), 37 deletions(-)
fs/nfsd/fault_inject.c +6 -10
@@ -152,19 +152,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, }, };
fs/nfsd/nfs4state.c +144 -23
211 lines not on this page · raw message
fs/nfsd/state.h +7 -4
@@ -488,10 +488,13 @@ 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 ) {}
Signature · 1 line
1.9.3
PATCH v3 100/114 nfsd: remove old fault injection infrastructure
In reply to Jeff LaytonRemove 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.
--- fs/nfsd/fault_inject.c | 51 ++++------------------------- fs/nfsd/nfs4state.c | 87 +++++++++++++++++++------------------------------- fs/nfsd/state.h | 45 +++++++++++--------------- 3 files changed, 57 insertions(+), 126 deletions(-)
fs/nfsd/fault_inject.c +6 -45
@@ -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)pr_info ("NFSD [%s]: Client %s had %llu state object(s)\n" , op->file, write_buf, val);
@@ -114,7 +75,7 @@ static ssize_t fault_inject_write(struct file *file, const char __user *buf,else pr_info ("NFSD Fault Injection: %s (n = %llu)" , op->file, val); - val = op->set_val (op, val); + val = op->set_val (val);pr_info ("NFSD: %s: found %llu" , op->file, val); }return len;/* on success, claim we got the whole input */
fs/nfsd/nfs4state.c +33 -54
178 lines not on this page · raw message
fs/nfsd/state.h +18 -27
53 lines not on this page · raw message
Signature · 1 line
1.9.3
PATCH v3 101/114 nfsd: Remove nfs4_lock_state(): nfs4_preprocess_stateid_op()
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
--- fs/nfsd/nfs4state.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-)
fs/nfsd/nfs4state.c +1 -5
@@ -4428,13 +4428,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;
@@ -4484,8 +4482,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; }
Signature · 1 line
1.9.3
PATCH v3 102/114 nfsd: Remove nfs4_lock_state(): nfsd4_test_stateid/nfsd4_free_stateid
In reply to Jeff LaytonFrom: Trond Myklebust <[email protected]>
--- fs/nfsd/nfs4state.c | 4 ---- 1 file changed, 4 deletions(-)
fs/nfsd/nfs4state.c +0 -4
@@ -4495,11 +4495,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; }
@@ -4515,7 +4513,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 ();spin_lock (&cl->cl_lock); s =find_stateid_locked (cl, stateid);if (!s)
@@ -4552,7 +4549,6 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, out_unlock:spin_unlock (&cl->cl_lock); out: -nfs4_unlock_state ();return ret; }
Signature · 1 line
1.9.3