From 2411967305dbfb8930b9b9c11f55f6c1ef7361e1 Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Thu, 24 May 2012 15:42:17 -0400 Subject: nfsd: probe the back channel on new connections Initiate a CB probe when a new connection with the correct direction is added to a session (IFF backchannel is marked as down). Without this a BIND_CONN_TO_SESSION has no effect on the internal backchannel state, which causes the server to reply to every SEQUENCE op with the SEQ4_STATUS_CB_PATH_DOWN flag set until DESTROY_SESSION. Signed-off-by: Weston Andros Adamson Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 94effd5bc4a..8b80a10d4fc 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -862,6 +862,11 @@ static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses, if (ret) /* oops; xprt is already down: */ nfsd4_conn_lost(&conn->cn_xpt_user); + if (ses->se_client->cl_cb_state == NFSD4_CB_DOWN && + dir & NFS4_CDFC4_BACK) { + /* callback channel may be back up */ + nfsd4_probe_callback(ses->se_client); + } return nfs_ok; } -- cgit v1.2.3 From 7df302f75ee28a6a87436e93b625ef60d37d098e Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 29 May 2012 13:56:37 -0400 Subject: NFSD: TEST_STATEID should not return NFS4ERR_STALE_STATEID According to RFC 5661, the TEST_STATEID operation is not allowed to return NFS4ERR_STALE_STATEID. In addition, RFC 5661 says: 15.1.16.5. NFS4ERR_STALE_STATEID (Error Code 10023) A stateid generated by an earlier server instance was used. This error is moot in NFSv4.1 because all operations that take a stateid MUST be preceded by the SEQUENCE operation, and the earlier server instance is detected by the session infrastructure that supports SEQUENCE. I triggered NFS4ERR_STALE_STATEID while testing the Linux client's NOGRACE recovery. Bruce suggested an additional test that could be useful to client developers. Lastly, RFC 5661, section 18.48.3 has this: o Special stateids are always considered invalid (they result in the error code NFS4ERR_BAD_STATEID). An explicit check is made for those state IDs to avoid printk noise. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 8b80a10d4fc..59b9efc9d69 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include "xdr4.h" @@ -3338,18 +3339,26 @@ static __be32 check_stateid_generation(stateid_t *in, stateid_t *ref, bool has_s return nfserr_old_stateid; } -__be32 nfs4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid) +static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid) { struct nfs4_stid *s; struct nfs4_ol_stateid *ols; __be32 status; - if (STALE_STATEID(stateid)) - return nfserr_stale_stateid; - + if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) + return nfserr_bad_stateid; + /* Client debugging aid. */ + if (!same_clid(&stateid->si_opaque.so_clid, &cl->cl_clientid)) { + char addr_str[INET6_ADDRSTRLEN]; + rpc_ntop((struct sockaddr *)&cl->cl_addr, addr_str, + sizeof(addr_str)); + pr_warn_ratelimited("NFSD: client %s testing state ID " + "with incorrect client ID\n", addr_str); + return nfserr_bad_stateid; + } s = find_stateid(cl, stateid); if (!s) - return nfserr_stale_stateid; + return nfserr_bad_stateid; status = check_stateid_generation(stateid, &s->sc_stateid, 1); if (status) return status; @@ -3468,7 +3477,8 @@ nfsd4_test_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, nfs4_lock_state(); list_for_each_entry(stateid, &test_stateid->ts_stateid_list, ts_id_list) - stateid->ts_id_status = nfs4_validate_stateid(cl, &stateid->ts_id_stateid); + stateid->ts_id_status = + nfsd4_validate_stateid(cl, &stateid->ts_id_stateid); nfs4_unlock_state(); return nfs_ok; -- cgit v1.2.3 From e1aaa8916f19d23d39d88e985bc6933a74159e91 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 6 Jun 2012 16:01:37 -0400 Subject: nfsd4: nfsd4_lock() cleanup Share a little common logic. And note the comments here are a little out of date (e.g. we don't always create new state in the "new" case any more.) Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 59b9efc9d69..de8f7e45102 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4059,11 +4059,6 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, nfs4_lock_state(); if (lock->lk_is_new) { - /* - * Client indicates that this is a new lockowner. - * Use open owner and open stateid to create lock owner and - * lock stateid. - */ struct nfs4_ol_stateid *open_stp = NULL; if (nfsd4_has_session(cstate)) @@ -4090,17 +4085,13 @@ 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); - if (status) - goto out; - } else { - /* lock (lock owner + lock stateid) already exists */ + } else status = nfs4_preprocess_seqid_op(cstate, lock->lk_old_lock_seqid, &lock->lk_old_lock_stateid, NFS4_LOCK_STID, &lock_stp); - if (status) - goto out; - } + if (status) + goto out; lock_sop = lockowner(lock_stp->st_stateowner); lkflg = setlkflg(lock->lk_type); -- cgit v1.2.3 From 4af825041b06c2ef9b5933288267a11e029eb360 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Thu, 7 Jun 2012 17:30:45 -0400 Subject: nfsd4: process_open2 cleanup Note we can simplify the error handling a little by doing the truncate earlier. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index de8f7e45102..9efa4055b5a 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3011,16 +3011,14 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf goto out; } else { 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); - status = nfsd4_truncate(rqstp, current_fh, 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)); -- cgit v1.2.3 From 74dbafaf5d84b5187e50dbe82442ec8df66d55b3 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 6 Jun 2012 12:53:48 -0400 Subject: nfsd4: release openowners on free in >=4.1 case We don't need to keep openowners around in the >=4.1 case, because they aren't needed to handle CLOSE replays any more (that's a problem for sessions). And doing so causes unexpected failures on a subsequent destroy_clientid to fail. We probably also need something comparable for lock owners on last unlock. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 9efa4055b5a..e404fca0826 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3763,12 +3763,19 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, nfsd4_close_open_stateid(stp); oo->oo_last_closed_stid = stp; - /* place unused nfs4_stateowners on so_close_lru list to be - * released by the laundromat service after the lease period - * to enable us to handle CLOSE replay - */ - if (list_empty(&oo->oo_owner.so_stateids)) - move_to_close_lru(oo); + if (list_empty(&oo->oo_owner.so_stateids)) { + if (cstate->minorversion) { + release_openowner(oo); + cstate->replay_owner = NULL; + } else { + /* + * 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); + } + } out: if (!cstate->replay_owner) nfs4_unlock_state(); -- cgit v1.2.3 From 5559b50acdcdcad7e362882d3261bf934c9436f6 Mon Sep 17 00:00:00 2001 From: Vivek Trivedi Date: Tue, 24 Jul 2012 21:18:20 +0530 Subject: nfsd4: fix cr_principal comparison check in same_creds This fixes a wrong check for same cr_principal in same_creds Introduced by 8fbba96e5b327665265ad02b7f331b68536828bf "nfsd4: stricter cred comparison for setclientid/exchange_id". Cc: stable@vger.kernel.org Signed-off-by: Vivek Trivedi Signed-off-by: Namjae Jeon Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index e404fca0826..fe96015fbfc 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1221,7 +1221,7 @@ static bool groups_equal(struct group_info *g1, struct group_info *g2) return true; } -static int +static bool same_creds(struct svc_cred *cr1, struct svc_cred *cr2) { if ((cr1->cr_flavor != cr2->cr_flavor) @@ -1233,7 +1233,7 @@ same_creds(struct svc_cred *cr1, struct svc_cred *cr2) return true; if (!cr1->cr_principal || !cr2->cr_principal) return false; - return 0 == strcmp(cr1->cr_principal, cr1->cr_principal); + return 0 == strcmp(cr1->cr_principal, cr2->cr_principal); } static void gen_clid(struct nfs4_client *clp) -- cgit v1.2.3 From a6d88f293ecd1b7444e128777f4a893e7a998852 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Fri, 25 May 2012 18:38:50 +0400 Subject: NFSd: fix locking in nfsd_forget_delegations() This patch adds recall_lock hold to nfsd_forget_delegations() to protect nfsd_process_n_delegations() call. Also, looks like it would be better to collect delegations to some local on-stack list, and then unhash collected list. This split allows to simplify locking, because delegation traversing is protected by recall_lock, when delegation unhash is protected by client_mutex. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index fe96015fbfc..d10ad8bc47a 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4575,7 +4575,7 @@ void nfsd_forget_openowners(u64 num) printk(KERN_INFO "NFSD: Forgot %d open owners", count); } -int nfsd_process_n_delegations(u64 num, void (*deleg_func)(struct nfs4_delegation *)) +int nfsd_process_n_delegations(u64 num, struct list_head *list) { int i, count = 0; struct nfs4_file *fp, *fnext; @@ -4584,7 +4584,7 @@ int nfsd_process_n_delegations(u64 num, void (*deleg_func)(struct nfs4_delegatio for (i = 0; i < FILE_HASH_SIZE; i++) { list_for_each_entry_safe(fp, fnext, &file_hashtbl[i], fi_hash) { list_for_each_entry_safe(dp, dnext, &fp->fi_delegations, dl_perfile) { - deleg_func(dp); + list_move(&dp->dl_recall_lru, list); if (++count == num) return count; } @@ -4597,9 +4597,16 @@ int nfsd_process_n_delegations(u64 num, void (*deleg_func)(struct nfs4_delegatio void nfsd_forget_delegations(u64 num) { unsigned int count; + LIST_HEAD(victims); + struct nfs4_delegation *dp, *dnext; + + spin_lock(&recall_lock); + count = nfsd_process_n_delegations(num, &victims); + spin_unlock(&recall_lock); nfs4_lock_state(); - count = nfsd_process_n_delegations(num, unhash_delegation); + list_for_each_entry_safe(dp, dnext, &victims, dl_recall_lru) + unhash_delegation(dp); nfs4_unlock_state(); printk(KERN_INFO "NFSD: Forgot %d delegations", count); @@ -4608,12 +4615,16 @@ void nfsd_forget_delegations(u64 num) void nfsd_recall_delegations(u64 num) { unsigned int count; + LIST_HEAD(victims); + struct nfs4_delegation *dp, *dnext; - nfs4_lock_state(); spin_lock(&recall_lock); - count = nfsd_process_n_delegations(num, nfsd_break_one_deleg); + count = nfsd_process_n_delegations(num, &victims); + list_for_each_entry_safe(dp, dnext, &victims, dl_recall_lru) { + list_del(&dp->dl_recall_lru); + nfsd_break_one_deleg(dp); + } spin_unlock(&recall_lock); - nfs4_unlock_state(); printk(KERN_INFO "NFSD: Recalled %d delegations", count); } -- cgit v1.2.3 From 99dbb8fe0992ecefd061e5efa7604b92eab58ccc Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 27 Jul 2012 16:30:12 -0400 Subject: nfsd4: fix missing fault_inject.h include Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index d10ad8bc47a..fddb18b2e87 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -44,6 +44,7 @@ #include "xdr4.h" #include "vfs.h" #include "current_stateid.h" +#include "fault_inject.h" #define NFSDDBG_FACILITY NFSDDBG_PROC -- cgit v1.2.3 From 5e1533c7880bb0df98f71fa683979ec296aa947d Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 25 Jul 2012 16:56:58 +0400 Subject: NFSd: make nfsd4_manager allocated per network namespace context. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index fddb18b2e87..4a44b50c2f5 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -46,6 +46,8 @@ #include "current_stateid.h" #include "fault_inject.h" +#include "netns.h" + #define NFSDDBG_FACILITY NFSDDBG_PROC /* Globals */ @@ -3116,22 +3118,21 @@ out: return status; } -static struct lock_manager nfsd4_manager = { -}; - static bool grace_ended; static void -nfsd4_end_grace(void) +nfsd4_end_grace(struct net *net) { + struct nfsd_net *nn = net_generic(net, nfsd_net_id); + /* do nothing if grace period already ended */ if (grace_ended) return; dprintk("NFSD: end of grace period\n"); grace_ended = true; - nfsd4_record_grace_done(&init_net, boot_time); - locks_end_grace(&nfsd4_manager); + nfsd4_record_grace_done(net, boot_time); + locks_end_grace(&nn->nfsd4_manager); /* * Now that every NFSv4 client has had the chance to recover and * to see the (possibly new, possibly shorter) lease time, we @@ -3154,7 +3155,7 @@ nfs4_laundromat(void) nfs4_lock_state(); dprintk("NFSD: laundromat service - starting\n"); - nfsd4_end_grace(); + nfsd4_end_grace(&init_net); INIT_LIST_HEAD(&reaplist); spin_lock(&client_lock); list_for_each_safe(pos, next, &client_lru) { @@ -4688,6 +4689,8 @@ set_max_delegations(void) int nfs4_state_start(void) { + struct net *net = &init_net; + struct nfsd_net *nn = net_generic(net, nfsd_net_id); int ret; /* @@ -4697,10 +4700,10 @@ nfs4_state_start(void) * to that instead and then do most of the rest of this on a per-net * basis. */ - get_net(&init_net); - nfsd4_client_tracking_init(&init_net); + get_net(net); + nfsd4_client_tracking_init(net); boot_time = get_seconds(); - locks_start_grace(&nfsd4_manager); + locks_start_grace(&nn->nfsd4_manager); grace_ended = false; printk(KERN_INFO "NFSD: starting %ld-second grace period\n", nfsd4_grace); @@ -4723,8 +4726,8 @@ nfs4_state_start(void) out_free_laundry: destroy_workqueue(laundry_wq); out_recovery: - nfsd4_client_tracking_exit(&init_net); - put_net(&init_net); + nfsd4_client_tracking_exit(net); + put_net(net); return ret; } @@ -4765,9 +4768,12 @@ __nfs4_state_shutdown(void) void nfs4_state_shutdown(void) { + struct net *net = &init_net; + struct nfsd_net *nn = net_generic(net, nfsd_net_id); + cancel_delayed_work_sync(&laundromat_work); destroy_workqueue(laundry_wq); - locks_end_grace(&nfsd4_manager); + locks_end_grace(&nn->nfsd4_manager); nfs4_lock_state(); __nfs4_state_shutdown(); nfs4_unlock_state(); -- cgit v1.2.3 From 5ccb0066f2d561549cc4d73d7f56b4ce3ca7a8a1 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 25 Jul 2012 16:57:22 +0400 Subject: LockD: pass actual network namespace to grace period management functions Passed network namespace replaced hard-coded init_net Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 4a44b50c2f5..34f65f10fa4 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2885,7 +2885,8 @@ static void nfsd4_open_deleg_none_ext(struct nfsd4_open *open, int status) * Attempt to hand out a delegation. */ static void -nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_ol_stateid *stp) +nfs4_open_delegation(struct net *net, struct svc_fh *fh, + struct nfsd4_open *open, struct nfs4_ol_stateid *stp) { struct nfs4_delegation *dp; struct nfs4_openowner *oo = container_of(stp->st_stateowner, struct nfs4_openowner, oo_owner); @@ -2906,7 +2907,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_ol_ case NFS4_OPEN_CLAIM_NULL: /* Let's not give out any delegations till everyone's * had the chance to reclaim theirs.... */ - if (locks_in_grace()) + if (locks_in_grace(net)) goto out; if (!cb_up || !(oo->oo_flags & NFS4_OO_CONFIRMED)) goto out; @@ -3040,7 +3041,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf * Attempt to hand out a delegation. No error return, because the * OPEN succeeds even if we fail. */ - nfs4_open_delegation(current_fh, open, stp); + nfs4_open_delegation(SVC_NET(rqstp), current_fh, open, stp); nodeleg: status = nfs_ok; @@ -3279,11 +3280,11 @@ out: } static inline __be32 -check_special_stateids(svc_fh *current_fh, stateid_t *stateid, int flags) +check_special_stateids(struct net *net, svc_fh *current_fh, stateid_t *stateid, int flags) { if (ONE_STATEID(stateid) && (flags & RD_STATE)) return nfs_ok; - else if (locks_in_grace()) { + else if (locks_in_grace(net)) { /* Answer in remaining cases depends on existence of * conflicting state; so we must wait out the grace period. */ return nfserr_grace; @@ -3300,9 +3301,9 @@ check_special_stateids(svc_fh *current_fh, stateid_t *stateid, int flags) * that are not able to provide mandatory locking. */ static inline int -grace_disallows_io(struct inode *inode) +grace_disallows_io(struct net *net, struct inode *inode) { - return locks_in_grace() && mandatory_lock(inode); + return locks_in_grace(net) && mandatory_lock(inode); } /* Returns true iff a is later than b: */ @@ -3393,7 +3394,7 @@ static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask, s * Checks for stateid operations */ __be32 -nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, +nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate, stateid_t *stateid, int flags, struct file **filpp) { struct nfs4_stid *s; @@ -3406,11 +3407,11 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, if (filpp) *filpp = NULL; - if (grace_disallows_io(ino)) + if (grace_disallows_io(net, ino)) return nfserr_grace; if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) - return check_special_stateids(current_fh, stateid, flags); + return check_special_stateids(net, current_fh, stateid, flags); status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID, &s); if (status) @@ -4107,10 +4108,10 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, goto out; status = nfserr_grace; - if (locks_in_grace() && !lock->lk_reclaim) + if (locks_in_grace(SVC_NET(rqstp)) && !lock->lk_reclaim) goto out; status = nfserr_no_grace; - if (!locks_in_grace() && lock->lk_reclaim) + if (!locks_in_grace(SVC_NET(rqstp)) && lock->lk_reclaim) goto out; locks_init_lock(&file_lock); @@ -4210,7 +4211,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfs4_lockowner *lo; __be32 status; - if (locks_in_grace()) + if (locks_in_grace(SVC_NET(rqstp))) return nfserr_grace; if (check_lock_length(lockt->lt_offset, lockt->lt_length)) @@ -4703,7 +4704,7 @@ nfs4_state_start(void) get_net(net); nfsd4_client_tracking_init(net); boot_time = get_seconds(); - locks_start_grace(&nn->nfsd4_manager); + locks_start_grace(net, &nn->nfsd4_manager); grace_ended = false; printk(KERN_INFO "NFSD: starting %ld-second grace period\n", nfsd4_grace); -- cgit v1.2.3 From a51c84ed502c25fed996afb7696fd7db2fa32fe2 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 25 Jul 2012 16:57:37 +0400 Subject: NFSd: make grace end flag per network namespace Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 34f65f10fa4..aebb58d3ac4 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3119,19 +3119,17 @@ out: return status; } -static bool grace_ended; - static void nfsd4_end_grace(struct net *net) { struct nfsd_net *nn = net_generic(net, nfsd_net_id); /* do nothing if grace period already ended */ - if (grace_ended) + if (nn->grace_ended) return; dprintk("NFSD: end of grace period\n"); - grace_ended = true; + nn->grace_ended = true; nfsd4_record_grace_done(net, boot_time); locks_end_grace(&nn->nfsd4_manager); /* @@ -4705,7 +4703,7 @@ nfs4_state_start(void) nfsd4_client_tracking_init(net); boot_time = get_seconds(); locks_start_grace(net, &nn->nfsd4_manager); - grace_ended = false; + nn->grace_ended = false; printk(KERN_INFO "NFSD: starting %ld-second grace period\n", nfsd4_grace); ret = set_callback_cred(); -- cgit v1.2.3 From 2c142baa7b237584bae7dc28630851701497e1ef Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 25 Jul 2012 16:57:45 +0400 Subject: NFSd: make boot_time variable per network namespace NFSd's boot_time represents grace period start point in time. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index aebb58d3ac4..cc894eda385 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -53,7 +53,6 @@ /* Globals */ time_t nfsd4_lease = 90; /* default lease time */ time_t nfsd4_grace = 90; -static time_t boot_time; #define all_ones {{~0,~0},~0} static const stateid_t one_stateid = { @@ -1056,12 +1055,12 @@ renew_client(struct nfs4_client *clp) /* SETCLIENTID and SETCLIENTID_CONFIRM Helper functions */ static int -STALE_CLIENTID(clientid_t *clid) +STALE_CLIENTID(clientid_t *clid, struct nfsd_net *nn) { - if (clid->cl_boot == boot_time) + if (clid->cl_boot == nn->boot_time) return 0; dprintk("NFSD stale clientid (%08x/%08x) boot_time %08lx\n", - clid->cl_boot, clid->cl_id, boot_time); + clid->cl_boot, clid->cl_id, nn->boot_time); return 1; } @@ -1242,8 +1241,9 @@ same_creds(struct svc_cred *cr1, struct svc_cred *cr2) static void gen_clid(struct nfs4_client *clp) { static u32 current_clientid = 1; + struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); - clp->cl_clientid.cl_boot = boot_time; + clp->cl_clientid.cl_boot = nn->boot_time; clp->cl_clientid.cl_id = current_clientid++; } @@ -2226,8 +2226,9 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, nfs4_verifier confirm = setclientid_confirm->sc_confirm; clientid_t * clid = &setclientid_confirm->sc_clientid; __be32 status; + struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); - if (STALE_CLIENTID(clid)) + if (STALE_CLIENTID(clid, nn)) return nfserr_stale_clientid; nfs4_lock_state(); @@ -2586,8 +2587,9 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate, unsigned int strhashval; struct nfs4_openowner *oo = NULL; __be32 status; + struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); - if (STALE_CLIENTID(&open->op_clientid)) + if (STALE_CLIENTID(&open->op_clientid, nn)) return nfserr_stale_clientid; /* * In case we need it later, after we've already created the @@ -3095,12 +3097,13 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, { struct nfs4_client *clp; __be32 status; + struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); nfs4_lock_state(); dprintk("process_renew(%08x/%08x): starting\n", clid->cl_boot, clid->cl_id); status = nfserr_stale_clientid; - if (STALE_CLIENTID(clid)) + if (STALE_CLIENTID(clid, nn)) goto out; clp = find_confirmed_client(clid); status = nfserr_expired; @@ -3130,7 +3133,7 @@ nfsd4_end_grace(struct net *net) dprintk("NFSD: end of grace period\n"); nn->grace_ended = true; - nfsd4_record_grace_done(net, boot_time); + nfsd4_record_grace_done(net, nn->boot_time); locks_end_grace(&nn->nfsd4_manager); /* * Now that every NFSv4 client has had the chance to recover and @@ -3236,9 +3239,9 @@ static inline __be32 nfs4_check_fh(struct svc_fh *fhp, struct nfs4_ol_stateid *s } static int -STALE_STATEID(stateid_t *stateid) +STALE_STATEID(stateid_t *stateid, struct nfsd_net *nn) { - if (stateid->si_opaque.so_clid.cl_boot == boot_time) + if (stateid->si_opaque.so_clid.cl_boot == nn->boot_time) return 0; dprintk("NFSD: stale stateid " STATEID_FMT "!\n", STATEID_VAL(stateid)); @@ -3373,10 +3376,11 @@ 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) { struct nfs4_client *cl; + struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) return nfserr_bad_stateid; - if (STALE_STATEID(stateid)) + if (STALE_STATEID(stateid, nn)) return nfserr_stale_stateid; cl = find_confirmed_client(&stateid->si_opaque.so_clid); if (!cl) @@ -4048,6 +4052,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, bool new_state = false; int lkflg; int err; + struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); dprintk("NFSD: nfsd4_lock: start=%Ld length=%Ld\n", (long long) lock->lk_offset, @@ -4074,7 +4079,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, sizeof(clientid_t)); status = nfserr_stale_clientid; - if (STALE_CLIENTID(&lock->lk_new_clientid)) + if (STALE_CLIENTID(&lock->lk_new_clientid, nn)) goto out; /* validate and update open stateid and open seqid */ @@ -4208,6 +4213,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct file_lock file_lock; struct nfs4_lockowner *lo; __be32 status; + struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); if (locks_in_grace(SVC_NET(rqstp))) return nfserr_grace; @@ -4218,7 +4224,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, nfs4_lock_state(); status = nfserr_stale_clientid; - if (!nfsd4_has_session(cstate) && STALE_CLIENTID(&lockt->lt_clientid)) + if (!nfsd4_has_session(cstate) && STALE_CLIENTID(&lockt->lt_clientid, nn)) goto out; if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0))) @@ -4367,6 +4373,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, struct list_head matches; unsigned int hashval = ownerstr_hashval(clid->cl_id, owner); __be32 status; + struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n", clid->cl_boot, clid->cl_id); @@ -4374,7 +4381,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, /* XXX check for lease expiration */ status = nfserr_stale_clientid; - if (STALE_CLIENTID(clid)) + if (STALE_CLIENTID(clid, nn)) return status; nfs4_lock_state(); @@ -4701,7 +4708,7 @@ nfs4_state_start(void) */ get_net(net); nfsd4_client_tracking_init(net); - boot_time = get_seconds(); + nn->boot_time = get_seconds(); locks_start_grace(net, &nn->nfsd4_manager); nn->grace_ended = false; printk(KERN_INFO "NFSD: starting %ld-second grace period\n", -- cgit v1.2.3 From 1696c47ce2f42e671f6a17682b533273f4ac7963 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 6 Aug 2012 11:00:27 -0400 Subject: nfsd: trivial comment updates locks.c doesn't use the BKL anymore and there is no fi_perfile field. Signed-off-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index cc894eda385..566a9b75fa0 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4151,11 +4151,6 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, file_lock.fl_end = last_byte_offset(lock->lk_offset, lock->lk_length); nfs4_transform_lock_offset(&file_lock); - /* - * Try to lock the file in the VFS. - * Note: locks.c uses the BKL to protect the inode's lock list. - */ - err = vfs_lock_file(filp, F_SETLK, &file_lock, &conflock); switch (-err) { case 0: /* success! */ -- cgit v1.2.3 From da5c80a935d422bcc8a69b362f5d1b47ef94d3de Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 14 Aug 2012 16:09:31 -0400 Subject: nfsd4: nfsd_process_n_delegations should be static Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 566a9b75fa0..f393da8c26e 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4578,7 +4578,7 @@ void nfsd_forget_openowners(u64 num) printk(KERN_INFO "NFSD: Forgot %d open owners", count); } -int nfsd_process_n_delegations(u64 num, struct list_head *list) +static int nfsd_process_n_delegations(u64 num, struct list_head *list) { int i, count = 0; struct nfs4_file *fp, *fnext; -- cgit v1.2.3 From 5592a3f3978c57175ef817a4f773bc0d6d8949b9 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 21 Aug 2012 08:02:31 -0400 Subject: knfsd: remove bogus BUG_ON() call from nfsd4_locku The code checks for a NULL filp and handles it gracefully just before this BUG_ON. Signed-off-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 1 - 1 file changed, 1 deletion(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index f393da8c26e..123b47105c0 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4294,7 +4294,6 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, status = nfserr_lock_range; goto out; } - BUG_ON(!filp); locks_init_lock(&file_lock); file_lock.fl_type = F_UNLCK; file_lock.fl_owner = (fl_owner_t)lockowner(stp->st_stateowner); -- cgit v1.2.3 From 21179d81f1de37c93435dce10d2a4378c370ecca Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 21 Aug 2012 08:03:32 -0400 Subject: knfsd: don't allocate file_locks on the stack struct file_lock is pretty large and really ought not live on the stack. On my x86_64 machine, they're almost 200 bytes each. (gdb) p sizeof(struct file_lock) $1 = 192 ...allocate them dynamically instead. Reported-by: Al Viro Signed-off-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 118 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 76 insertions(+), 42 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 123b47105c0..5f1a91a9cd3 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4046,8 +4046,8 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfs4_lockowner *lock_sop = NULL; struct nfs4_ol_stateid *lock_stp; struct file *filp = NULL; - struct file_lock file_lock; - struct file_lock conflock; + struct file_lock *file_lock = NULL; + struct file_lock *conflock = NULL; __be32 status = 0; bool new_state = false; int lkflg; @@ -4117,21 +4117,28 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (!locks_in_grace(SVC_NET(rqstp)) && lock->lk_reclaim) goto out; - locks_init_lock(&file_lock); + file_lock = locks_alloc_lock(); + if (!file_lock) { + dprintk("NFSD: %s: unable to allocate lock!\n", __func__); + status = nfserr_jukebox; + goto out; + } + + locks_init_lock(file_lock); switch (lock->lk_type) { case NFS4_READ_LT: case NFS4_READW_LT: filp = find_readable_file(lock_stp->st_file); if (filp) get_lock_access(lock_stp, NFS4_SHARE_ACCESS_READ); - file_lock.fl_type = F_RDLCK; + file_lock->fl_type = F_RDLCK; break; case NFS4_WRITE_LT: case NFS4_WRITEW_LT: filp = find_writeable_file(lock_stp->st_file); if (filp) get_lock_access(lock_stp, NFS4_SHARE_ACCESS_WRITE); - file_lock.fl_type = F_WRLCK; + file_lock->fl_type = F_WRLCK; break; default: status = nfserr_inval; @@ -4141,17 +4148,23 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, status = nfserr_openmode; goto out; } - file_lock.fl_owner = (fl_owner_t)lock_sop; - file_lock.fl_pid = current->tgid; - file_lock.fl_file = filp; - file_lock.fl_flags = FL_POSIX; - file_lock.fl_lmops = &nfsd_posix_mng_ops; - - file_lock.fl_start = lock->lk_offset; - file_lock.fl_end = last_byte_offset(lock->lk_offset, lock->lk_length); - nfs4_transform_lock_offset(&file_lock); + file_lock->fl_owner = (fl_owner_t)lock_sop; + file_lock->fl_pid = current->tgid; + file_lock->fl_file = filp; + file_lock->fl_flags = FL_POSIX; + file_lock->fl_lmops = &nfsd_posix_mng_ops; + file_lock->fl_start = lock->lk_offset; + file_lock->fl_end = last_byte_offset(lock->lk_offset, lock->lk_length); + nfs4_transform_lock_offset(file_lock); + + conflock = locks_alloc_lock(); + if (!conflock) { + dprintk("NFSD: %s: unable to allocate lock!\n", __func__); + status = nfserr_jukebox; + goto out; + } - err = vfs_lock_file(filp, F_SETLK, &file_lock, &conflock); + err = vfs_lock_file(filp, F_SETLK, file_lock, conflock); switch (-err) { case 0: /* success! */ update_stateid(&lock_stp->st_stid.sc_stateid); @@ -4162,7 +4175,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, case (EAGAIN): /* conflock holds conflicting lock */ status = nfserr_denied; dprintk("NFSD: nfsd4_lock: conflicting lock found!\n"); - nfs4_set_lock_denied(&conflock, &lock->lk_denied); + nfs4_set_lock_denied(conflock, &lock->lk_denied); break; case (EDEADLK): status = nfserr_deadlock; @@ -4177,6 +4190,10 @@ out: release_lockowner(lock_sop); if (!cstate->replay_owner) nfs4_unlock_state(); + if (file_lock) + locks_free_lock(file_lock); + if (conflock) + locks_free_lock(conflock); return status; } @@ -4205,7 +4222,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_lockt *lockt) { struct inode *inode; - struct file_lock file_lock; + struct file_lock *file_lock = NULL; struct nfs4_lockowner *lo; __be32 status; struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); @@ -4226,15 +4243,21 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, goto out; inode = cstate->current_fh.fh_dentry->d_inode; - locks_init_lock(&file_lock); + file_lock = locks_alloc_lock(); + if (!file_lock) { + dprintk("NFSD: %s: unable to allocate lock!\n", __func__); + status = nfserr_jukebox; + goto out; + } + locks_init_lock(file_lock); switch (lockt->lt_type) { case NFS4_READ_LT: case NFS4_READW_LT: - file_lock.fl_type = F_RDLCK; + file_lock->fl_type = F_RDLCK; break; case NFS4_WRITE_LT: case NFS4_WRITEW_LT: - file_lock.fl_type = F_WRLCK; + file_lock->fl_type = F_WRLCK; break; default: dprintk("NFSD: nfs4_lockt: bad lock type!\n"); @@ -4244,25 +4267,27 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, lo = find_lockowner_str(inode, &lockt->lt_clientid, &lockt->lt_owner); if (lo) - file_lock.fl_owner = (fl_owner_t)lo; - file_lock.fl_pid = current->tgid; - file_lock.fl_flags = FL_POSIX; + file_lock->fl_owner = (fl_owner_t)lo; + file_lock->fl_pid = current->tgid; + file_lock->fl_flags = FL_POSIX; - file_lock.fl_start = lockt->lt_offset; - file_lock.fl_end = last_byte_offset(lockt->lt_offset, lockt->lt_length); + file_lock->fl_start = lockt->lt_offset; + file_lock->fl_end = last_byte_offset(lockt->lt_offset, lockt->lt_length); - nfs4_transform_lock_offset(&file_lock); + nfs4_transform_lock_offset(file_lock); - status = nfsd_test_lock(rqstp, &cstate->current_fh, &file_lock); + status = nfsd_test_lock(rqstp, &cstate->current_fh, file_lock); if (status) goto out; - if (file_lock.fl_type != F_UNLCK) { + if (file_lock->fl_type != F_UNLCK) { status = nfserr_denied; - nfs4_set_lock_denied(&file_lock, &lockt->lt_denied); + nfs4_set_lock_denied(file_lock, &lockt->lt_denied); } out: nfs4_unlock_state(); + if (file_lock) + locks_free_lock(file_lock); return status; } @@ -4272,7 +4297,7 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, { struct nfs4_ol_stateid *stp; struct file *filp = NULL; - struct file_lock file_lock; + struct file_lock *file_lock = NULL; __be32 status; int err; @@ -4294,22 +4319,29 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, status = nfserr_lock_range; goto out; } - locks_init_lock(&file_lock); - file_lock.fl_type = F_UNLCK; - file_lock.fl_owner = (fl_owner_t)lockowner(stp->st_stateowner); - file_lock.fl_pid = current->tgid; - file_lock.fl_file = filp; - file_lock.fl_flags = FL_POSIX; - file_lock.fl_lmops = &nfsd_posix_mng_ops; - file_lock.fl_start = locku->lu_offset; - - file_lock.fl_end = last_byte_offset(locku->lu_offset, locku->lu_length); - nfs4_transform_lock_offset(&file_lock); + file_lock = locks_alloc_lock(); + if (!file_lock) { + dprintk("NFSD: %s: unable to allocate lock!\n", __func__); + status = nfserr_jukebox; + goto out; + } + locks_init_lock(file_lock); + file_lock->fl_type = F_UNLCK; + file_lock->fl_owner = (fl_owner_t)lockowner(stp->st_stateowner); + file_lock->fl_pid = current->tgid; + file_lock->fl_file = filp; + file_lock->fl_flags = FL_POSIX; + file_lock->fl_lmops = &nfsd_posix_mng_ops; + file_lock->fl_start = locku->lu_offset; + + file_lock->fl_end = last_byte_offset(locku->lu_offset, + locku->lu_length); + nfs4_transform_lock_offset(file_lock); /* * Try to unlock the file in the VFS. */ - err = vfs_lock_file(filp, F_SETLK, &file_lock, NULL); + err = vfs_lock_file(filp, F_SETLK, file_lock, NULL); if (err) { dprintk("NFSD: nfs4_locku: vfs_lock_file failed!\n"); goto out_nfserr; @@ -4323,6 +4355,8 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, out: if (!cstate->replay_owner) nfs4_unlock_state(); + if (file_lock) + locks_free_lock(file_lock); return status; out_nfserr: -- cgit v1.2.3 From cf9182e90b2af04245ac4fae497fe73fc71285b4 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 29 Aug 2012 15:21:58 -0700 Subject: nfsd4: fix nfs4 stateid leak Processes that open and close multiple files may end up setting this oo_last_closed_stid without freeing what was previously pointed to. This can result in a major leak, visible for example by watching the nfsd4_stateids line of /proc/slabinfo. Reported-by: Cyril B. Tested-by: Cyril B. Cc: stable@vger.kernel.org Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 5f1a91a9cd3..6686e747223 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3766,6 +3766,7 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, memcpy(&close->cl_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t)); nfsd4_close_open_stateid(stp); + release_last_closed_stateid(oo); oo->oo_last_closed_stid = stp; if (list_empty(&oo->oo_owner.so_stateids)) { -- cgit v1.2.3 From ef79859e04bd67638f72373bb5514211d6f6d5b3 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 29 Aug 2012 10:32:54 -0700 Subject: nfsd4: eliminate redundant nfs4_free_stateid Somehow we ended up with identical functions "nfs4_free_stateid" and "free_generic_stateid". Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 6686e747223..5122e1704cd 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2705,11 +2705,6 @@ nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_ol_st return nfs_ok; } -static void nfs4_free_stateid(struct nfs4_ol_stateid *s) -{ - kmem_cache_free(stateid_slab, s); -} - static inline int nfs4_access_to_access(u32 nfs4_access) { int flags = 0; @@ -3088,7 +3083,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) - nfs4_free_stateid(open->op_stp); + free_generic_stateid(open->op_stp); } __be32 -- cgit v1.2.3 From cb0942b81249798e15c3f04eee2946ef543e8115 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 27 Aug 2012 14:48:26 -0400 Subject: make get_file() return its argument simplifies a bunch of callers... Signed-off-by: Al Viro --- fs/nfsd/nfs4state.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index cc894eda385..48a1bad3733 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2837,8 +2837,7 @@ static int nfs4_setlease(struct nfs4_delegation *dp, int flag) return -ENOMEM; } fp->fi_lease = fl; - fp->fi_deleg_file = fl->fl_file; - get_file(fp->fi_deleg_file); + fp->fi_deleg_file = get_file(fl->fl_file); atomic_set(&fp->fi_delegees, 1); list_add(&dp->dl_perfile, &fp->fi_delegations); return 0; -- cgit v1.2.3 From 68eb35081e297b37db49d854cda144c6a3397699 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 21 Aug 2012 12:48:30 -0400 Subject: nfsd4: don't pin clientids to pseudoflavors I added cr_flavor to the data compared in same_creds without any justification, in d5497fc693a446ce9100fcf4117c3f795ddfd0d2 "nfsd4: move rq_flavor into svc_cred". Recent client changes then started making mount -osec=krb5 server:/export /mnt/ echo "hello" >/mnt/TMP umount /mnt/ mount -osec=krb5i server:/export /mnt/ echo "hello" >/mnt/TMP to fail due to a clid_inuse on the second open. Mounting sequentially like this with different flavors probably isn't that common outside artificial tests. Also, the real bug here may be that the server isn't just destroying the former clientid in this case (because it isn't good enough at recognizing when the old state is gone). But it prompted some discussion and a look back at the spec, and I think the check was probably wrong. Fix and document. Cc: stable@kernel.org Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 5122e1704cd..0f8d7e7922e 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1223,10 +1223,26 @@ static bool groups_equal(struct group_info *g1, struct group_info *g2) return true; } +/* + * RFC 3530 language requires clid_inuse be returned when the + * "principal" associated with a requests differs from that previously + * used. We use uid, gid's, and gss principal string as our best + * approximation. We also don't want to allow non-gss use of a client + * established using gss: in theory cr_principal should catch that + * change, but in practice cr_principal can be null even in the gss case + * since gssd doesn't always pass down a principal string. + */ +static bool is_gss_cred(struct svc_cred *cr) +{ + /* Is cr_flavor one of the gss "pseudoflavors"?: */ + return (cr->cr_flavor > RPC_AUTH_MAXFLAVOR); +} + + static bool same_creds(struct svc_cred *cr1, struct svc_cred *cr2) { - if ((cr1->cr_flavor != cr2->cr_flavor) + if ((is_gss_cred(cr1) != is_gss_cred(cr2)) || (cr1->cr_uid != cr2->cr_uid) || (cr1->cr_gid != cr2->cr_gid) || !groups_equal(cr1->cr_group_info, cr2->cr_group_info)) -- cgit v1.2.3 From c116a0af76424c72d91ebff7646639cb1287bf63 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 11 Sep 2012 14:53:09 -0400 Subject: nfsd4: set cl_minorversion at create time And remove some mostly obsolete comments. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 0f8d7e7922e..73029cd0c5b 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1667,6 +1667,7 @@ out_new: status = nfserr_jukebox; goto out; } + new->cl_minorversion = 1; gen_clid(new); add_to_unconfirmed(new, strhashval); @@ -1799,12 +1800,6 @@ nfsd4_create_session(struct svc_rqst *rqstp, status = nfserr_stale_clientid; goto out; } - - /* - * XXX: we should probably set this at creation time, and check - * for consistent minorversion use throughout: - */ - conf->cl_minorversion = 1; /* * We do not support RDMA or persistent sessions */ @@ -2216,10 +2211,6 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, copy_clid(new, conf); else /* case 4 (new client) or cases 2, 3 (client reboot): */ gen_clid(new); - /* - * XXX: we should probably set this at creation time, and check - * for consistent minorversion use throughout: - */ new->cl_minorversion = 0; gen_callback(new, setclid, rqstp); add_to_unconfirmed(new, strhashval); -- cgit v1.2.3 From d15c077e442d3c4167aaac87b3b7fe8ccad67a1f Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Thu, 13 Sep 2012 16:19:31 -0400 Subject: nfsd4: enforce per-client sessions/no-sessions distinction Something like creating a client with setclientid and then trying to confirm it with create_session may not crash the server, but I'm not completely positive of that, and in any case it's obviously bad client behavior. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 49 +++++++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 20 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 73029cd0c5b..55078421903 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1356,13 +1356,15 @@ move_to_confirmed(struct nfs4_client *clp) } static struct nfs4_client * -find_confirmed_client(clientid_t *clid) +find_confirmed_client(clientid_t *clid, bool sessions) { struct nfs4_client *clp; unsigned int idhashval = clientid_hashval(clid->cl_id); list_for_each_entry(clp, &conf_id_hashtbl[idhashval], cl_idhash) { if (same_clid(&clp->cl_clientid, clid)) { + if ((bool)clp->cl_minorversion != sessions) + return NULL; renew_client(clp); return clp; } @@ -1371,14 +1373,17 @@ find_confirmed_client(clientid_t *clid) } static struct nfs4_client * -find_unconfirmed_client(clientid_t *clid) +find_unconfirmed_client(clientid_t *clid, bool sessions) { struct nfs4_client *clp; unsigned int idhashval = clientid_hashval(clid->cl_id); list_for_each_entry(clp, &unconf_id_hashtbl[idhashval], cl_idhash) { - if (same_clid(&clp->cl_clientid, clid)) + if (same_clid(&clp->cl_clientid, clid)) { + if ((bool)clp->cl_minorversion != sessions) + return NULL; return clp; + } } return NULL; } @@ -1768,8 +1773,8 @@ nfsd4_create_session(struct svc_rqst *rqstp, return nfserr_inval; nfs4_lock_state(); - unconf = find_unconfirmed_client(&cr_ses->clientid); - conf = find_confirmed_client(&cr_ses->clientid); + unconf = find_unconfirmed_client(&cr_ses->clientid, true); + conf = find_confirmed_client(&cr_ses->clientid, true); if (conf) { cs_slot = &conf->cl_cs_slot; @@ -2096,8 +2101,8 @@ nfsd4_destroy_clientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *csta __be32 status = 0; nfs4_lock_state(); - unconf = find_unconfirmed_client(&dc->clientid); - conf = find_confirmed_client(&dc->clientid); + unconf = find_unconfirmed_client(&dc->clientid, true); + conf = find_confirmed_client(&dc->clientid, true); if (conf) { clp = conf; @@ -2239,8 +2244,8 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, return nfserr_stale_clientid; nfs4_lock_state(); - conf = find_confirmed_client(clid); - unconf = find_unconfirmed_client(clid); + conf = find_confirmed_client(clid, false); + unconf = find_unconfirmed_client(clid, false); /* * We try hard to give out unique clientid's, so if we get an * attempt to confirm the same clientid with a different cred, @@ -2454,16 +2459,20 @@ 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(unsigned int hashval, struct nfsd4_open *open, bool sessions) { struct nfs4_stateowner *so; struct nfs4_openowner *oo; + struct nfs4_client *clp; list_for_each_entry(so, &ownerstr_hashtbl[hashval], so_strhash) { if (!so->so_is_open_owner) continue; if (same_owner_str(so, &open->op_owner, &open->op_clientid)) { oo = openowner(so); + clp = oo->oo_owner.so_client; + if ((bool)clp->cl_minorversion != sessions) + return NULL; renew_client(oo->oo_owner.so_client); return oo; } @@ -2607,10 +2616,10 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate, return nfserr_jukebox; strhashval = ownerstr_hashval(clientid->cl_id, &open->op_owner); - oo = find_openstateowner_str(strhashval, open); + oo = find_openstateowner_str(strhashval, open, cstate->minorversion); open->op_openowner = oo; if (!oo) { - clp = find_confirmed_client(clientid); + clp = find_confirmed_client(clientid, cstate->minorversion); if (clp == NULL) return nfserr_expired; goto new_owner; @@ -3107,7 +3116,7 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, status = nfserr_stale_clientid; if (STALE_CLIENTID(clid, nn)) goto out; - clp = find_confirmed_client(clid); + clp = find_confirmed_client(clid, cstate->minorversion); status = nfserr_expired; if (clp == NULL) { /* We assume the client took too long to RENEW. */ @@ -3375,7 +3384,7 @@ static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid) return nfs_ok; } -static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask, struct nfs4_stid **s) +static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask, struct nfs4_stid **s, bool sessions) { struct nfs4_client *cl; struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); @@ -3384,7 +3393,7 @@ static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask, s return nfserr_bad_stateid; if (STALE_STATEID(stateid, nn)) return nfserr_stale_stateid; - cl = find_confirmed_client(&stateid->si_opaque.so_clid); + cl = find_confirmed_client(&stateid->si_opaque.so_clid, sessions); if (!cl) return nfserr_expired; *s = find_stateid_by_type(cl, stateid, typemask); @@ -3417,7 +3426,7 @@ 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); - status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID, &s); + status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID, &s, cstate->minorversion); if (status) return status; status = check_stateid_generation(stateid, &s->sc_stateid, nfsd4_has_session(cstate)); @@ -3567,7 +3576,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); + status = nfsd4_lookup_stateid(stateid, typemask, &s, cstate->minorversion); if (status) return status; *stpp = openlockstateid(s); @@ -3805,7 +3814,7 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, inode = cstate->current_fh.fh_dentry->d_inode; nfs4_lock_state(); - status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID, &s); + status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID, &s, cstate->minorversion); if (status) goto out; dp = delegstateid(s); @@ -4533,12 +4542,12 @@ nfsd4_find_reclaim_client(struct nfs4_client *clp) * Called from OPEN. Look for clientid in reclaim list. */ __be32 -nfs4_check_open_reclaim(clientid_t *clid) +nfs4_check_open_reclaim(clientid_t *clid, bool sessions) { struct nfs4_client *clp; /* find clientid in conf_id_hashtbl */ - clp = find_confirmed_client(clid); + clp = find_confirmed_client(clid, sessions); if (clp == NULL) return nfserr_reclaim_bad; -- cgit v1.2.3 From 497305014864ea9e32adf7e8a06081209a5e7c42 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 11 Sep 2012 15:33:21 -0400 Subject: nfsd4: reject bad forechannel attrs earlier This could simplify the logic a little later. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 55078421903..64938ca05c6 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1771,6 +1771,8 @@ nfsd4_create_session(struct svc_rqst *rqstp, if (cr_ses->flags & ~SESSION4_FLAG_MASK_A) return nfserr_inval; + if (check_forechannel_attrs(cr_ses->fore_channel)) + return nfserr_toosmall; nfs4_lock_state(); unconf = find_unconfirmed_client(&cr_ses->clientid, true); @@ -1811,10 +1813,6 @@ nfsd4_create_session(struct svc_rqst *rqstp, cr_ses->flags &= ~SESSION4_PERSIST; cr_ses->flags &= ~SESSION4_RDMA; - status = nfserr_toosmall; - if (check_forechannel_attrs(cr_ses->fore_channel)) - goto out; - status = nfserr_jukebox; new = alloc_init_session(rqstp, conf, cr_ses); if (!new) -- cgit v1.2.3 From 3ba6367124f3f07c91ea45a3ea24bbf4c5429bfc Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 11 Sep 2012 15:37:40 -0400 Subject: nfsd4: separate connection allocation and initialization It'll be useful to have connection allocation and initialization as separate functions. Also, note we'd been ignoring the alloc_conn error return in bind_conn_to_session. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 64938ca05c6..101ccd856e6 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -852,14 +852,10 @@ static int nfsd4_register_conn(struct nfsd4_conn *conn) return register_xpt_user(conn->cn_xprt, &conn->cn_xpt_user); } -static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses, u32 dir) +static void nfsd4_init_conn(struct svc_rqst *rqstp, struct nfsd4_conn *conn, struct nfsd4_session *ses, u32 dir) { - struct nfsd4_conn *conn; int ret; - conn = alloc_conn(rqstp, dir); - if (!conn) - return nfserr_jukebox; nfsd4_hash_conn(conn, ses); ret = nfsd4_register_conn(conn); if (ret) @@ -870,17 +866,21 @@ static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses, /* callback channel may be back up */ nfsd4_probe_callback(ses->se_client); } - return nfs_ok; } static __be32 nfsd4_new_conn_from_crses(struct svc_rqst *rqstp, struct nfsd4_session *ses) { + struct nfsd4_conn *conn; u32 dir = NFS4_CDFC4_FORE; if (ses->se_flags & SESSION4_BACK_CHAN) dir |= NFS4_CDFC4_BACK; - return nfsd4_new_conn(rqstp, ses, dir); + conn = alloc_conn(rqstp, dir); + if (!conn) + return nfserr_jukebox; + nfsd4_init_conn(rqstp, conn, ses, dir); + return nfs_ok; } /* must be called under client_lock */ @@ -1868,6 +1868,7 @@ __be32 nfsd4_bind_conn_to_session(struct svc_rqst *rqstp, struct nfsd4_bind_conn_to_session *bcts) { __be32 status; + struct nfsd4_conn *conn; if (!nfsd4_last_compound_op(rqstp)) return nfserr_not_only_op; @@ -1884,9 +1885,13 @@ __be32 nfsd4_bind_conn_to_session(struct svc_rqst *rqstp, return nfserr_badsession; status = nfsd4_map_bcts_dir(&bcts->dir); - if (!status) - nfsd4_new_conn(rqstp, cstate->session, bcts->dir); - return status; + if (status) + return status; + conn = alloc_conn(rqstp, bcts->dir); + if (!conn) + return nfserr_jukebox; + nfsd4_init_conn(rqstp, conn, cstate->session, bcts->dir); + return nfs_ok; } static bool nfsd4_compound_in_session(struct nfsd4_session *session, struct nfs4_sessionid *sid) -- cgit v1.2.3 From e1ff371f9da57c1965400b9cc6607705d76a67bd Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 11 Sep 2012 17:10:25 -0400 Subject: nfsd4: new_conn_from_crses should only allocate Do the initialization in the caller, and clarify that the only failure ever possible here was due to allocation. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 101ccd856e6..8b7f8f84d5a 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -852,7 +852,7 @@ static int nfsd4_register_conn(struct nfsd4_conn *conn) return register_xpt_user(conn->cn_xprt, &conn->cn_xpt_user); } -static void nfsd4_init_conn(struct svc_rqst *rqstp, struct nfsd4_conn *conn, struct nfsd4_session *ses, u32 dir) +static void nfsd4_init_conn(struct svc_rqst *rqstp, struct nfsd4_conn *conn, struct nfsd4_session *ses) { int ret; @@ -862,25 +862,19 @@ static void nfsd4_init_conn(struct svc_rqst *rqstp, struct nfsd4_conn *conn, str /* oops; xprt is already down: */ nfsd4_conn_lost(&conn->cn_xpt_user); if (ses->se_client->cl_cb_state == NFSD4_CB_DOWN && - dir & NFS4_CDFC4_BACK) { + conn->cn_flags & NFS4_CDFC4_BACK) { /* callback channel may be back up */ nfsd4_probe_callback(ses->se_client); } } -static __be32 nfsd4_new_conn_from_crses(struct svc_rqst *rqstp, struct nfsd4_session *ses) +static struct nfsd4_conn *alloc_conn_from_crses(struct svc_rqst *rqstp, struct nfsd4_create_session *cses) { - struct nfsd4_conn *conn; u32 dir = NFS4_CDFC4_FORE; - if (ses->se_flags & SESSION4_BACK_CHAN) + if (cses->flags & SESSION4_BACK_CHAN) dir |= NFS4_CDFC4_BACK; - - conn = alloc_conn(rqstp, dir); - if (!conn) - return nfserr_jukebox; - nfsd4_init_conn(rqstp, conn, ses, dir); - return nfs_ok; + return alloc_conn(rqstp, dir); } /* must be called under client_lock */ @@ -929,9 +923,9 @@ void nfsd4_put_session(struct nfsd4_session *ses) static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp, struct nfsd4_create_session *cses) { struct nfsd4_session *new; + struct nfsd4_conn *conn; struct nfsd4_channel_attrs *fchan = &cses->fore_channel; int numslots, slotsize; - __be32 status; int idx; /* @@ -970,14 +964,14 @@ static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct n spin_unlock(&clp->cl_lock); spin_unlock(&client_lock); - status = nfsd4_new_conn_from_crses(rqstp, new); - /* whoops: benny points out, status is ignored! (err, or bogus) */ - if (status) { + conn = alloc_conn_from_crses(rqstp, cses); + if (!conn) { spin_lock(&client_lock); free_session(&new->se_ref); spin_unlock(&client_lock); return NULL; } + nfsd4_init_conn(rqstp, conn, new); if (cses->flags & SESSION4_BACK_CHAN) { struct sockaddr *sa = svc_addr(rqstp); /* @@ -1890,7 +1884,7 @@ __be32 nfsd4_bind_conn_to_session(struct svc_rqst *rqstp, conn = alloc_conn(rqstp, bcts->dir); if (!conn) return nfserr_jukebox; - nfsd4_init_conn(rqstp, conn, cstate->session, bcts->dir); + nfsd4_init_conn(rqstp, conn, cstate->session); return nfs_ok; } -- cgit v1.2.3 From 1377b69e68241826761d257ac26dd4285c3f9e9b Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 11 Sep 2012 21:42:40 -0400 Subject: nfsd4: minor free_session cleanup Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 8b7f8f84d5a..7536a88063a 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -897,20 +897,21 @@ static void nfsd4_del_conns(struct nfsd4_session *s) spin_unlock(&clp->cl_lock); } +static void __free_session(struct nfsd4_session *ses) +{ + nfsd4_put_drc_mem(slot_bytes(&ses->se_fchannel), ses->se_fchannel.maxreqs); + free_session_slots(ses); + kfree(ses); +} + static void free_session(struct kref *kref) { struct nfsd4_session *ses; - int mem; lockdep_assert_held(&client_lock); ses = container_of(kref, struct nfsd4_session, se_ref); nfsd4_del_conns(ses); - spin_lock(&nfsd_drc_lock); - mem = ses->se_fchannel.maxreqs * slot_bytes(&ses->se_fchannel); - nfsd_drc_mem_used -= mem; - spin_unlock(&nfsd_drc_lock); - free_session_slots(ses); - kfree(ses); + __free_session(ses); } void nfsd4_put_session(struct nfsd4_session *ses) @@ -966,9 +967,7 @@ static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct n conn = alloc_conn_from_crses(rqstp, cses); if (!conn) { - spin_lock(&client_lock); - free_session(&new->se_ref); - spin_unlock(&client_lock); + __free_session(new); return NULL; } nfsd4_init_conn(rqstp, conn, new); -- cgit v1.2.3 From a827bcb242ecc884d38af8145e1d9a112b739b35 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 12 Sep 2012 09:51:34 -0400 Subject: nfsd4: clean up session allocation Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 7536a88063a..79f1fdb22dc 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -758,7 +758,7 @@ static void nfsd4_put_drc_mem(int slotsize, int num) spin_unlock(&nfsd_drc_lock); } -static struct nfsd4_session *alloc_session(int slotsize, int numslots) +static struct nfsd4_session *__alloc_session(int slotsize, int numslots) { struct nfsd4_session *new; int mem, i; @@ -921,14 +921,10 @@ void nfsd4_put_session(struct nfsd4_session *ses) spin_unlock(&client_lock); } -static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp, struct nfsd4_create_session *cses) +static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fchan) { struct nfsd4_session *new; - struct nfsd4_conn *conn; - struct nfsd4_channel_attrs *fchan = &cses->fore_channel; int numslots, slotsize; - int idx; - /* * Note decreasing slot size below client's request may * make it difficult for client to function correctly, whereas @@ -941,13 +937,30 @@ static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct n if (numslots < 1) return NULL; - new = alloc_session(slotsize, numslots); + new = __alloc_session(slotsize, numslots); if (!new) { nfsd4_put_drc_mem(slotsize, fchan->maxreqs); return NULL; } init_forechannel_attrs(&new->se_fchannel, fchan, numslots, slotsize); + return new; +} +static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp, struct nfsd4_create_session *cses) +{ + struct nfsd4_session *new; + struct nfsd4_conn *conn; + int idx; + + new = alloc_session(&cses->fore_channel); + if (!new) + return NULL; + + conn = alloc_conn_from_crses(rqstp, cses); + if (!conn) { + __free_session(new); + return NULL; + } new->se_client = clp; gen_sessionid(new); @@ -965,11 +978,6 @@ static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct n spin_unlock(&clp->cl_lock); spin_unlock(&client_lock); - conn = alloc_conn_from_crses(rqstp, cses); - if (!conn) { - __free_session(new); - return NULL; - } nfsd4_init_conn(rqstp, conn, new); if (cses->flags & SESSION4_BACK_CHAN) { struct sockaddr *sa = svc_addr(rqstp); -- cgit v1.2.3 From 81f0b2a4965da2a48aed5e992f2bc859e5d35066 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 12 Sep 2012 11:04:33 -0400 Subject: nfsd4: separate session allocation and initialization This will allow some further simplification. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 79f1fdb22dc..1cd80d57695 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -946,21 +946,10 @@ static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fchan) return new; } -static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp, struct nfsd4_create_session *cses) +static struct nfsd4_session *init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, struct nfs4_client *clp, struct nfsd4_create_session *cses) { - struct nfsd4_session *new; - struct nfsd4_conn *conn; int idx; - new = alloc_session(&cses->fore_channel); - if (!new) - return NULL; - - conn = alloc_conn_from_crses(rqstp, cses); - if (!conn) { - __free_session(new); - return NULL; - } new->se_client = clp; gen_sessionid(new); @@ -978,7 +967,6 @@ static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct n spin_unlock(&clp->cl_lock); spin_unlock(&client_lock); - nfsd4_init_conn(rqstp, conn, new); if (cses->flags & SESSION4_BACK_CHAN) { struct sockaddr *sa = svc_addr(rqstp); /* @@ -1766,6 +1754,7 @@ nfsd4_create_session(struct svc_rqst *rqstp, struct sockaddr *sa = svc_addr(rqstp); struct nfs4_client *conf, *unconf; struct nfsd4_session *new; + struct nfsd4_conn *conn; struct nfsd4_clid_slot *cs_slot = NULL; bool confirm_me = false; __be32 status = 0; @@ -1774,6 +1763,13 @@ nfsd4_create_session(struct svc_rqst *rqstp, return nfserr_inval; if (check_forechannel_attrs(cr_ses->fore_channel)) return nfserr_toosmall; + new = alloc_session(&cr_ses->fore_channel); + if (!new) + return nfserr_jukebox; + status = nfserr_jukebox; + conn = alloc_conn_from_crses(rqstp, cr_ses); + if (!conn) + goto out_free_session; nfs4_lock_state(); unconf = find_unconfirmed_client(&cr_ses->clientid, true); @@ -1784,41 +1780,40 @@ nfsd4_create_session(struct svc_rqst *rqstp, status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0); if (status == nfserr_replay_cache) { status = nfsd4_replay_create_session(cr_ses, cs_slot); - goto out; + goto out_free_conn; } else if (cr_ses->seqid != cs_slot->sl_seqid + 1) { status = nfserr_seq_misordered; - goto out; + goto out_free_conn; } } else if (unconf) { if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred) || !rpc_cmp_addr(sa, (struct sockaddr *) &unconf->cl_addr)) { status = nfserr_clid_inuse; - goto out; + goto out_free_conn; } cs_slot = &unconf->cl_cs_slot; status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0); if (status) { /* an unconfirmed replay returns misordered */ status = nfserr_seq_misordered; - goto out; + goto out_free_conn; } confirm_me = true; conf = unconf; } else { status = nfserr_stale_clientid; - goto out; + goto out_free_conn; } + status = nfs_ok; /* * We do not support RDMA or persistent sessions */ cr_ses->flags &= ~SESSION4_PERSIST; cr_ses->flags &= ~SESSION4_RDMA; - status = nfserr_jukebox; - new = alloc_init_session(rqstp, conf, cr_ses); - if (!new) - goto out; - status = nfs_ok; + init_session(rqstp, new, conf, cr_ses); + nfsd4_init_conn(rqstp, conn, new); + memcpy(cr_ses->sessionid.data, new->se_sessionid.data, NFS4_MAX_SESSIONID_LEN); memcpy(&cr_ses->fore_channel, &new->se_fchannel, @@ -1840,6 +1835,11 @@ out: nfs4_unlock_state(); dprintk("%s returns %d\n", __func__, ntohl(status)); return status; +out_free_conn: + free_conn(conn); +out_free_session: + __free_session(new); + goto out; } static bool nfsd4_last_compound_op(struct svc_rqst *rqstp) -- cgit v1.2.3 From 8f9d3d3b7cd25763efec91db23a1cce1971147c2 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 12 Sep 2012 14:41:31 -0400 Subject: nfsd4: expire old client earlier Before we had to delay expiring a client till we'd found out whether the session and connection allocations would succeed. That's no longer necessary. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 1cd80d57695..592cd7fb6f0 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1756,7 +1756,6 @@ nfsd4_create_session(struct svc_rqst *rqstp, struct nfsd4_session *new; struct nfsd4_conn *conn; struct nfsd4_clid_slot *cs_slot = NULL; - bool confirm_me = false; __be32 status = 0; if (cr_ses->flags & ~SESSION4_FLAG_MASK_A) @@ -1786,6 +1785,8 @@ nfsd4_create_session(struct svc_rqst *rqstp, goto out_free_conn; } } else if (unconf) { + unsigned int hash; + 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; @@ -1798,7 +1799,11 @@ nfsd4_create_session(struct svc_rqst *rqstp, status = nfserr_seq_misordered; goto out_free_conn; } - confirm_me = true; + hash = clientstr_hashval(unconf->cl_recdir); + old = find_confirmed_client_by_str(unconf->cl_recdir, hash); + if (old) + expire_client(old); + move_to_confirmed(unconf); conf = unconf; } else { status = nfserr_stale_clientid; @@ -1823,14 +1828,6 @@ nfsd4_create_session(struct svc_rqst *rqstp, /* cache solo and embedded create sessions under the state lock */ nfsd4_cache_create_session(cr_ses, cs_slot, status); - if (confirm_me) { - unsigned int hash = clientstr_hashval(unconf->cl_recdir); - struct nfs4_client *old = - find_confirmed_client_by_str(conf->cl_recdir, hash); - if (old) - expire_client(old); - move_to_confirmed(conf); - } out: nfs4_unlock_state(); dprintk("%s returns %d\n", __func__, ntohl(status)); -- cgit v1.2.3 From 6a3b15634279aa6740f9b829d25db32024b2ca7c Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 12 Sep 2012 09:59:17 -0400 Subject: nfsd4: remove redundant callback probe Both nfsd4_init_conn and alloc_init_session are probing the callback channel, harmless but pointless. Also, nfsd4_init_conn should probably be probing in the "unknown" case as well. In fact I don't see any harm to just doing it unconditionally when we get a new backchannel connection. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 592cd7fb6f0..773b903c07c 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -861,8 +861,7 @@ static void nfsd4_init_conn(struct svc_rqst *rqstp, struct nfsd4_conn *conn, str if (ret) /* oops; xprt is already down: */ nfsd4_conn_lost(&conn->cn_xpt_user); - if (ses->se_client->cl_cb_state == NFSD4_CB_DOWN && - conn->cn_flags & NFS4_CDFC4_BACK) { + if (conn->cn_flags & NFS4_CDFC4_BACK) { /* callback channel may be back up */ nfsd4_probe_callback(ses->se_client); } @@ -979,7 +978,6 @@ static struct nfsd4_session *init_session(struct svc_rqst *rqstp, struct nfsd4_s rpc_copy_addr((struct sockaddr *)&clp->cl_cb_conn.cb_addr, sa); clp->cl_cb_conn.cb_addrlen = svc_addr_len(sa); } - nfsd4_probe_callback(clp); return new; } -- cgit v1.2.3 From 0d22f68f02c10d5d10ec5712917e5828b001a822 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 26 Sep 2012 11:36:16 -0400 Subject: nfsd4: don't allow reclaims of expired clients When a confirmed client expires, we normally also need to expire any stable storage record which would allow that client to reclaim state on the next boot. We forgot to do this in some cases. (For example, in destroy_clientid, and in the cases in exchange_id and create_session that destroy and existing confirmed client.) But in most other cases, there's really no harm to calling nfsd4_client_record_remove(), because it is a no-op in the case the client doesn't have an existing The single exception is destroying a client on shutdown, when we want to keep the stable storage records so we can recognize which clients will be allowed to reclaim when we come back up. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 773b903c07c..412b888faec 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1118,7 +1118,7 @@ unhash_client_locked(struct nfs4_client *clp) } static void -expire_client(struct nfs4_client *clp) +destroy_client(struct nfs4_client *clp) { struct nfs4_openowner *oo; struct nfs4_delegation *dp; @@ -1152,6 +1152,12 @@ expire_client(struct nfs4_client *clp) spin_unlock(&client_lock); } +static void expire_client(struct nfs4_client *clp) +{ + nfsd4_client_record_remove(clp); + destroy_client(clp); +} + static void copy_verf(struct nfs4_client *target, nfs4_verifier *source) { memcpy(target->cl_verifier.data, source->data, @@ -2273,10 +2279,8 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, unsigned int hash = clientstr_hashval(unconf->cl_recdir); conf = find_confirmed_client_by_str(unconf->cl_recdir, hash); - if (conf) { - nfsd4_client_record_remove(conf); + if (conf) expire_client(conf); - } move_to_confirmed(unconf); nfsd4_probe_callback(unconf); } @@ -3191,7 +3195,6 @@ nfs4_laundromat(void) clp = list_entry(pos, struct nfs4_client, cl_lru); dprintk("NFSD: purging unused client (clientid %08x)\n", clp->cl_clientid.cl_id); - nfsd4_client_record_remove(clp); expire_client(clp); } spin_lock(&recall_lock); @@ -4562,7 +4565,6 @@ void nfsd_forget_clients(u64 num) nfs4_lock_state(); list_for_each_entry_safe(clp, next, &client_lru, cl_lru) { - nfsd4_client_record_remove(clp); expire_client(clp); if (++count == num) break; @@ -4787,11 +4789,11 @@ __nfs4_state_shutdown(void) for (i = 0; i < CLIENT_HASH_SIZE; i++) { while (!list_empty(&conf_id_hashtbl[i])) { clp = list_entry(conf_id_hashtbl[i].next, struct nfs4_client, cl_idhash); - expire_client(clp); + destroy_client(clp); } while (!list_empty(&unconf_str_hashtbl[i])) { clp = list_entry(unconf_str_hashtbl[i].next, struct nfs4_client, cl_strhash); - expire_client(clp); + destroy_client(clp); } } INIT_LIST_HEAD(&reaplist); -- cgit v1.2.3 From 01f6c8fd949f3a25a2617e6e1579a5c974b1cabf Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 18 Oct 2012 22:44:21 +0800 Subject: nfsd4: remove unused variable in nfsd4_delegreturn() The variable inode is initialized but never used otherwise, so remove the unused variable. dpatch engine is used to auto generate this patch. (https://github.com/weiyj/dpatch) Signed-off-by: Wei Yongjun Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index d0237f872cc..620ff814375 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3807,12 +3807,10 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfs4_delegation *dp; stateid_t *stateid = &dr->dr_stateid; struct nfs4_stid *s; - struct inode *inode; __be32 status; if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0))) return status; - inode = cstate->current_fh.fh_dentry->d_inode; nfs4_lock_state(); status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID, &s, cstate->minorversion); -- cgit v1.2.3 From 3c40794b2dd0f355ef4e6bf8d85af5dcd7da7ece Mon Sep 17 00:00:00 2001 From: Yanchuan Nian Date: Wed, 24 Oct 2012 14:44:19 +0800 Subject: nfs: fix wrong object type in lockowner_slab The object type in the cache of lockowner_slab is wrong, and it is better to fix it. Cc: stable@vger.kernel.org Signed-off-by: Yanchuan Nian Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 620ff814375..fba2996ed51 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2340,7 +2340,7 @@ nfsd4_init_slabs(void) if (openowner_slab == NULL) goto out_nomem; lockowner_slab = kmem_cache_create("nfsd4_lockowners", - sizeof(struct nfs4_openowner), 0, 0, NULL); + sizeof(struct nfs4_lockowner), 0, 0, NULL); if (lockowner_slab == NULL) goto out_nomem; file_slab = kmem_cache_create("nfsd4_files", -- cgit v1.2.3 From 7c1f8b65af4bda8eb53cdfe4965cbcfd7fb20c7d Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Thu, 1 Nov 2012 16:54:01 -0400 Subject: nfsd4: remove unused init_session return Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index fba2996ed51..bc8507c2352 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -945,7 +945,7 @@ static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fchan) return new; } -static struct nfsd4_session *init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, struct nfs4_client *clp, struct nfsd4_create_session *cses) +void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, struct nfs4_client *clp, struct nfsd4_create_session *cses) { int idx; @@ -978,7 +978,6 @@ static struct nfsd4_session *init_session(struct svc_rqst *rqstp, struct nfsd4_s rpc_copy_addr((struct sockaddr *)&clp->cl_cb_conn.cb_addr, sa); clp->cl_cb_conn.cb_addrlen = svc_addr_len(sa); } - return new; } /* caller must hold client_lock */ -- cgit v1.2.3 From 7fa10cd12df3ec0873a5db0d8dc8e978423b87dc Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 16 Oct 2012 12:39:33 -0400 Subject: nfsd4: don't BUG in delegation break callback These conditions would indeed indicate bugs in the code, but if we want to hear about them we're likely better off warning and returning than immediately dying while holding file_lock_lock. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index bc8507c2352..db7258c1342 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2554,9 +2554,14 @@ static void nfsd_break_deleg_cb(struct file_lock *fl) struct nfs4_file *fp = (struct nfs4_file *)fl->fl_owner; struct nfs4_delegation *dp; - BUG_ON(!fp); - /* We assume break_lease is only called once per lease: */ - BUG_ON(fp->fi_had_conflict); + if (!fp) { + WARN(1, "(%p)->fl_owner NULL\n", fl); + return; + } + if (fp->fi_had_conflict) { + WARN(1, "duplicate break on %p\n", fp); + return; + } /* * We don't want the locks code to timeout the lease for us; * we'll remove it ourself if a delegation isn't returned -- cgit v1.2.3 From c6bb3ca27d78b902baa143b931a8d9ef53298afa Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Thu, 1 Nov 2012 16:31:02 -0400 Subject: nfsd4: use callback security parameters in create_session We're currently ignoring the callback security parameters specified in create_session, and just assuming the client wants auth_sys, because that's all the current linux client happens to care about. But this could cause us callbacks to fail to a client that wanted something different. For now, all we're doing is no longer ignoring the uid and gid passed in the auth_sys case. Further patches will add support for auth_null and gss (and possibly use more of the auth_sys information; the spec wants us to use exactly the credential we're passed, though it's hard to imagine why a client would care). Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index db7258c1342..dbbbd2fe523 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -957,6 +957,7 @@ void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, struct nfs4 new->se_cb_seq_nr = 1; new->se_flags = cses->flags; new->se_cb_prog = cses->callback_prog; + new->se_cb_sec = cses->cb_sec; kref_init(&new->se_ref); idx = hash_sessionid(&new->se_sessionid); spin_lock(&client_lock); -- cgit v1.2.3 From cb73a9f4649bf63c0397e565a15abf8a91ecf56f Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Thu, 1 Nov 2012 18:09:48 -0400 Subject: nfsd4: implement backchannel_ctl operation This operation is mandatory for servers to implement. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index dbbbd2fe523..4023e77687e 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1865,6 +1865,20 @@ static __be32 nfsd4_map_bcts_dir(u32 *dir) return nfserr_inval; } +__be32 nfsd4_backchannel_ctl(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_backchannel_ctl *bc) +{ + struct nfsd4_session *session = cstate->session; + + spin_lock(&client_lock); + session->se_cb_prog = bc->bc_cb_program; + session->se_cb_sec = bc->bc_cb_sec; + spin_unlock(&client_lock); + + nfsd4_probe_callback(session->se_client); + + return nfs_ok; +} + __be32 nfsd4_bind_conn_to_session(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_bind_conn_to_session *bcts) -- cgit v1.2.3 From 57725155dc1b8c78b7a96886d5cdc69dc89e9c54 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 5 Nov 2012 15:10:26 -0500 Subject: nfsd4: common helper to initialize callback work I've found it confusing having the only references to nfsd4_do_callback_rpc() in a different file. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 4023e77687e..13f3471b02a 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -340,7 +340,7 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv fh_copy_shallow(&dp->dl_fh, ¤t_fh->fh_handle); dp->dl_time = 0; atomic_set(&dp->dl_count, 1); - INIT_WORK(&dp->dl_recall.cb_work, nfsd4_do_callback_rpc); + nfsd4_init_callback(&dp->dl_recall); return dp; } @@ -1313,7 +1313,7 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir, INIT_LIST_HEAD(&clp->cl_lru); INIT_LIST_HEAD(&clp->cl_callbacks); spin_lock_init(&clp->cl_lock); - INIT_WORK(&clp->cl_cb_null.cb_work, nfsd4_do_callback_rpc); + nfsd4_init_callback(&clp->cl_cb_null); clp->cl_time = get_seconds(); clear_bit(0, &clp->cl_cb_slot_busy); rpc_init_wait_queue(&clp->cl_cb_waitq, "Backchannel slot table"); -- cgit v1.2.3 From a0af710a6510213672d28f83681c391d36a7555e Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 9 Nov 2012 15:06:38 -0500 Subject: nfsd: remove unused argument to nfs4_has_reclaimed_state Signed-off-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 13f3471b02a..d6b602a9265 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4484,7 +4484,7 @@ alloc_reclaim(void) } int -nfs4_has_reclaimed_state(const char *name, bool use_exchange_id) +nfs4_has_reclaimed_state(const char *name) { unsigned int strhashval = clientstr_hashval(name); struct nfs4_client *clp; -- cgit v1.2.3 From 278c931cb05ae624df8c82b6bdfbb0e03392cde7 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 12 Nov 2012 15:00:52 -0500 Subject: nfsd: have nfsd4_find_reclaim_client take a char * argument Currently, it takes a client pointer, but later we're going to need to search for these records without knowing whether a matching client even exists. Signed-off-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index d6b602a9265..18e554942da 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4537,19 +4537,16 @@ nfs4_release_reclaim(void) /* * called from OPEN, CLAIM_PREVIOUS with a new clientid. */ struct nfs4_client_reclaim * -nfsd4_find_reclaim_client(struct nfs4_client *clp) +nfsd4_find_reclaim_client(const char *recdir) { unsigned int strhashval; struct nfs4_client_reclaim *crp = NULL; - dprintk("NFSD: nfs4_find_reclaim_client for %.*s with recdir %s\n", - clp->cl_name.len, clp->cl_name.data, - clp->cl_recdir); + dprintk("NFSD: nfs4_find_reclaim_client for recdir %s\n", recdir); - /* find clp->cl_name in reclaim_str_hashtbl */ - strhashval = clientstr_hashval(clp->cl_recdir); + strhashval = clientstr_hashval(recdir); list_for_each_entry(crp, &reclaim_str_hashtbl[strhashval], cr_strhash) { - if (same_name(crp->cr_recdir, clp->cl_recdir)) { + if (same_name(crp->cr_recdir, recdir)) { return crp; } } -- cgit v1.2.3 From ce30e5392fcb26b6aa53bb16d06da1d7d8bb0863 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 12 Nov 2012 15:00:53 -0500 Subject: nfsd: break out reclaim record removal into separate function We'll need to be able to call this from nfs4recover.c eventually. Signed-off-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 18e554942da..24dcda2b327 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4516,6 +4516,14 @@ nfs4_client_to_reclaim(const char *name) return 1; } +void +nfs4_remove_reclaim_record(struct nfs4_client_reclaim *crp) +{ + list_del(&crp->cr_strhash); + kfree(crp); + reclaim_str_hashtbl_size--; +} + void nfs4_release_reclaim(void) { @@ -4526,9 +4534,7 @@ nfs4_release_reclaim(void) while (!list_empty(&reclaim_str_hashtbl[i])) { crp = list_entry(reclaim_str_hashtbl[i].next, struct nfs4_client_reclaim, cr_strhash); - list_del(&crp->cr_strhash); - kfree(crp); - reclaim_str_hashtbl_size--; + nfs4_remove_reclaim_record(crp); } } BUG_ON(reclaim_str_hashtbl_size); -- cgit v1.2.3 From 772a9bbbb5769c646c74452ef21df538bbe2ebf0 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 12 Nov 2012 15:00:54 -0500 Subject: nfsd: make nfs4_client_to_reclaim return a pointer to the reclaim record Later callers will need to make changes to the record. Signed-off-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 24dcda2b327..1c6f82e4335 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4498,22 +4498,22 @@ nfs4_has_reclaimed_state(const char *name) /* * failure => all reset bets are off, nfserr_no_grace... */ -int +struct nfs4_client_reclaim * nfs4_client_to_reclaim(const char *name) { unsigned int strhashval; - struct nfs4_client_reclaim *crp = NULL; + struct nfs4_client_reclaim *crp; dprintk("NFSD nfs4_client_to_reclaim NAME: %.*s\n", HEXDIR_LEN, name); crp = alloc_reclaim(); - if (!crp) - return 0; - strhashval = clientstr_hashval(name); - INIT_LIST_HEAD(&crp->cr_strhash); - list_add(&crp->cr_strhash, &reclaim_str_hashtbl[strhashval]); - memcpy(crp->cr_recdir, name, HEXDIR_LEN); - reclaim_str_hashtbl_size++; - return 1; + if (crp) { + strhashval = clientstr_hashval(name); + INIT_LIST_HEAD(&crp->cr_strhash); + list_add(&crp->cr_strhash, &reclaim_str_hashtbl[strhashval]); + memcpy(crp->cr_recdir, name, HEXDIR_LEN); + reclaim_str_hashtbl_size++; + } + return crp; } void -- cgit v1.2.3 From 0ce0c2b5d23080eec39ccc52354be1eea326ed5f Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 12 Nov 2012 15:00:55 -0500 Subject: nfsd: don't search for client by hash on legacy reboot recovery gracedone When nfsd starts, the legacy reboot recovery code creates a tracking struct for each directory in the v4recoverydir. When the grace period ends, it basically does a "readdir" on the directory again, and matches each dentry in there to an existing client id to see if it should be removed or not. If the matching client doesn't exist, or hasn't reclaimed its state then it will remove that dentry. This is pretty inefficient since it involves doing a lot of hash-bucket searching. It also means that we have to keep relying on being able to search for a nfs4_client by md5 hashed cl_recdir name. Instead, add a pointer to the nfs4_client that indicates the association between the nfs4_client_reclaim and nfs4_client. When a reclaim operation comes in, we set the pointer to make that association. On gracedone, the legacy client tracker will keep the recdir around iff: 1/ there is a reclaim record for the directory ...and... 2/ there's an association between the reclaim record and a client record -- that is, a create or check operation was performed on the client that matches that directory. Signed-off-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 1c6f82e4335..559ab574d46 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4483,16 +4483,13 @@ alloc_reclaim(void) return kmalloc(sizeof(struct nfs4_client_reclaim), GFP_KERNEL); } -int +bool nfs4_has_reclaimed_state(const char *name) { - unsigned int strhashval = clientstr_hashval(name); - struct nfs4_client *clp; + struct nfs4_client_reclaim *crp; - clp = find_confirmed_client_by_str(name, strhashval); - if (!clp) - return 0; - return test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); + crp = nfsd4_find_reclaim_client(name); + return (crp && crp->cr_clp); } /* @@ -4511,6 +4508,7 @@ nfs4_client_to_reclaim(const char *name) INIT_LIST_HEAD(&crp->cr_strhash); list_add(&crp->cr_strhash, &reclaim_str_hashtbl[strhashval]); memcpy(crp->cr_recdir, name, HEXDIR_LEN); + crp->cr_clp = NULL; reclaim_str_hashtbl_size++; } return crp; -- cgit v1.2.3 From ac55fdc408039b425a2fa3cbcaed7444e5339f9a Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 12 Nov 2012 15:00:56 -0500 Subject: nfsd: move the confirmed and unconfirmed hlists to a rbtree The current code requires that we md5 hash the name in order to store the client in the confirmed and unconfirmed trees. Change it instead to store the clients in a pair of rbtrees, and simply compare the cl_names directly instead of hashing them. This also necessitates that we add a new flag to the clp->cl_flags field to indicate which tree the client is currently in. Signed-off-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 144 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 93 insertions(+), 51 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 559ab574d46..99998a1eb42 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -412,10 +412,10 @@ static unsigned int clientstr_hashval(const char *name) * reclaim_str_hashtbl[] holds known client info from previous reset/reboot * used in reboot/reset lease grace period processing * - * conf_id_hashtbl[], and conf_str_hashtbl[] hold confirmed + * conf_id_hashtbl[], and conf_name_tree hold confirmed * setclientid_confirmed info. * - * unconf_str_hastbl[] and unconf_id_hashtbl[] hold unconfirmed + * unconf_id_hashtbl[] and unconf_name_tree hold unconfirmed * setclientid info. * * client_lru holds client queue ordered by nfs4_client.cl_time @@ -423,13 +423,15 @@ static unsigned int clientstr_hashval(const char *name) * * close_lru holds (open) stateowner queue ordered by nfs4_stateowner.so_time * for last close replay. + * + * All of the above fields are protected by the client_mutex. */ static struct list_head reclaim_str_hashtbl[CLIENT_HASH_SIZE]; static int reclaim_str_hashtbl_size = 0; static struct list_head conf_id_hashtbl[CLIENT_HASH_SIZE]; -static struct list_head conf_str_hashtbl[CLIENT_HASH_SIZE]; -static struct list_head unconf_str_hashtbl[CLIENT_HASH_SIZE]; static struct list_head unconf_id_hashtbl[CLIENT_HASH_SIZE]; +static struct rb_root conf_name_tree; +static struct rb_root unconf_name_tree; static struct list_head client_lru; static struct list_head close_lru; @@ -1144,7 +1146,10 @@ destroy_client(struct nfs4_client *clp) if (clp->cl_cb_conn.cb_xprt) svc_xprt_put(clp->cl_cb_conn.cb_xprt); list_del(&clp->cl_idhash); - list_del(&clp->cl_strhash); + if (test_bit(NFSD4_CLIENT_CONFIRMED, &clp->cl_flags)) + rb_erase(&clp->cl_namenode, &conf_name_tree); + else + rb_erase(&clp->cl_namenode, &unconf_name_tree); spin_lock(&client_lock); unhash_client_locked(clp); if (atomic_read(&clp->cl_refcount) == 0) @@ -1187,6 +1192,17 @@ static int copy_cred(struct svc_cred *target, struct svc_cred *source) return 0; } +static long long +compare_blob(const struct xdr_netobj *o1, const struct xdr_netobj *o2) +{ + long long res; + + res = o1->len - o2->len; + if (res) + return res; + return (long long)memcmp(o1->data, o2->data, o1->len); +} + static int same_name(const char *n1, const char *n2) { return 0 == memcmp(n1, n2, HEXDIR_LEN); @@ -1307,7 +1323,6 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir, atomic_set(&clp->cl_refcount, 0); clp->cl_cb_state = NFSD4_CB_UNKNOWN; INIT_LIST_HEAD(&clp->cl_idhash); - INIT_LIST_HEAD(&clp->cl_strhash); INIT_LIST_HEAD(&clp->cl_openowners); INIT_LIST_HEAD(&clp->cl_delegations); INIT_LIST_HEAD(&clp->cl_lru); @@ -1325,11 +1340,52 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir, } static void -add_to_unconfirmed(struct nfs4_client *clp, unsigned int strhashval) +add_clp_to_name_tree(struct nfs4_client *new_clp, struct rb_root *root) +{ + struct rb_node **new = &(root->rb_node), *parent = NULL; + struct nfs4_client *clp; + + while (*new) { + clp = rb_entry(*new, struct nfs4_client, cl_namenode); + parent = *new; + + if (compare_blob(&clp->cl_name, &new_clp->cl_name) > 0) + new = &((*new)->rb_left); + else + new = &((*new)->rb_right); + } + + rb_link_node(&new_clp->cl_namenode, parent, new); + rb_insert_color(&new_clp->cl_namenode, root); +} + +static struct nfs4_client * +find_clp_in_name_tree(struct xdr_netobj *name, struct rb_root *root) +{ + long long cmp; + struct rb_node *node = root->rb_node; + struct nfs4_client *clp; + + while (node) { + clp = rb_entry(node, struct nfs4_client, cl_namenode); + cmp = compare_blob(&clp->cl_name, name); + if (cmp > 0) + node = node->rb_left; + else if (cmp < 0) + node = node->rb_right; + else + return clp; + } + return NULL; +} + +static void +add_to_unconfirmed(struct nfs4_client *clp) { unsigned int idhashval; - list_add(&clp->cl_strhash, &unconf_str_hashtbl[strhashval]); + clear_bit(NFSD4_CLIENT_CONFIRMED, &clp->cl_flags); + add_clp_to_name_tree(clp, &unconf_name_tree); idhashval = clientid_hashval(clp->cl_clientid.cl_id); list_add(&clp->cl_idhash, &unconf_id_hashtbl[idhashval]); renew_client(clp); @@ -1339,12 +1395,12 @@ static void move_to_confirmed(struct nfs4_client *clp) { unsigned int idhashval = clientid_hashval(clp->cl_clientid.cl_id); - unsigned int strhashval; dprintk("NFSD: move_to_confirm nfs4_client %p\n", clp); list_move(&clp->cl_idhash, &conf_id_hashtbl[idhashval]); - strhashval = clientstr_hashval(clp->cl_recdir); - list_move(&clp->cl_strhash, &conf_str_hashtbl[strhashval]); + rb_erase(&clp->cl_namenode, &unconf_name_tree); + add_clp_to_name_tree(clp, &conf_name_tree); + set_bit(NFSD4_CLIENT_CONFIRMED, &clp->cl_flags); renew_client(clp); } @@ -1387,27 +1443,15 @@ static bool clp_used_exchangeid(struct nfs4_client *clp) } static struct nfs4_client * -find_confirmed_client_by_str(const char *dname, unsigned int hashval) +find_confirmed_client_by_name(struct xdr_netobj *name) { - struct nfs4_client *clp; - - list_for_each_entry(clp, &conf_str_hashtbl[hashval], cl_strhash) { - if (same_name(clp->cl_recdir, dname)) - return clp; - } - return NULL; + return find_clp_in_name_tree(name, &conf_name_tree); } static struct nfs4_client * -find_unconfirmed_client_by_str(const char *dname, unsigned int hashval) +find_unconfirmed_client_by_name(struct xdr_netobj *name) { - struct nfs4_client *clp; - - list_for_each_entry(clp, &unconf_str_hashtbl[hashval], cl_strhash) { - if (same_name(clp->cl_recdir, dname)) - return clp; - } - return NULL; + return find_clp_in_name_tree(name, &unconf_name_tree); } static void @@ -1572,7 +1616,6 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, { struct nfs4_client *unconf, *conf, *new; __be32 status; - unsigned int strhashval; char dname[HEXDIR_LEN]; char addr_str[INET6_ADDRSTRLEN]; nfs4_verifier verf = exid->verifier; @@ -1605,11 +1648,9 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, if (status) return status; - strhashval = clientstr_hashval(dname); - /* Cases below refer to rfc 5661 section 18.35.4: */ nfs4_lock_state(); - conf = find_confirmed_client_by_str(dname, strhashval); + conf = find_confirmed_client_by_name(&exid->clname); if (conf) { bool creds_match = same_creds(&conf->cl_cred, &rqstp->rq_cred); bool verfs_match = same_verf(&verf, &conf->cl_verifier); @@ -1654,7 +1695,7 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, goto out; } - unconf = find_unconfirmed_client_by_str(dname, strhashval); + unconf = find_unconfirmed_client_by_name(&exid->clname); if (unconf) /* case 4, possible retry or client restart */ expire_client(unconf); @@ -1668,7 +1709,7 @@ out_new: new->cl_minorversion = 1; gen_clid(new); - add_to_unconfirmed(new, strhashval); + add_to_unconfirmed(new); out_copy: exid->clientid.cl_boot = new->cl_clientid.cl_boot; exid->clientid.cl_id = new->cl_clientid.cl_id; @@ -1789,7 +1830,6 @@ nfsd4_create_session(struct svc_rqst *rqstp, goto out_free_conn; } } else if (unconf) { - unsigned int hash; struct nfs4_client *old; if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred) || !rpc_cmp_addr(sa, (struct sockaddr *) &unconf->cl_addr)) { @@ -1803,8 +1843,7 @@ nfsd4_create_session(struct svc_rqst *rqstp, status = nfserr_seq_misordered; goto out_free_conn; } - hash = clientstr_hashval(unconf->cl_recdir); - old = find_confirmed_client_by_str(unconf->cl_recdir, hash); + old = find_confirmed_client_by_name(&unconf->cl_name); if (old) expire_client(old); move_to_confirmed(unconf); @@ -2195,7 +2234,6 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, { struct xdr_netobj clname = setclid->se_name; nfs4_verifier clverifier = setclid->se_verf; - unsigned int strhashval; struct nfs4_client *conf, *unconf, *new; __be32 status; char dname[HEXDIR_LEN]; @@ -2204,11 +2242,9 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (status) return status; - strhashval = clientstr_hashval(dname); - /* Cases below refer to rfc 3530 section 14.2.33: */ nfs4_lock_state(); - conf = find_confirmed_client_by_str(dname, strhashval); + conf = find_confirmed_client_by_name(&clname); if (conf) { /* case 0: */ status = nfserr_clid_inuse; @@ -2223,7 +2259,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, goto out; } } - unconf = find_unconfirmed_client_by_str(dname, strhashval); + unconf = find_unconfirmed_client_by_name(&clname); if (unconf) expire_client(unconf); status = nfserr_jukebox; @@ -2237,7 +2273,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, gen_clid(new); new->cl_minorversion = 0; gen_callback(new, setclid, rqstp); - add_to_unconfirmed(new, strhashval); + add_to_unconfirmed(new); 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)); @@ -2290,9 +2326,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, nfsd4_probe_callback(conf); expire_client(unconf); } else { /* case 3: normal case; new or rebooted client */ - unsigned int hash = clientstr_hashval(unconf->cl_recdir); - - conf = find_confirmed_client_by_str(unconf->cl_recdir, hash); + conf = find_confirmed_client_by_name(&unconf->cl_name); if (conf) expire_client(conf); move_to_confirmed(unconf); @@ -4706,11 +4740,11 @@ nfs4_state_init(void) for (i = 0; i < CLIENT_HASH_SIZE; i++) { INIT_LIST_HEAD(&conf_id_hashtbl[i]); - INIT_LIST_HEAD(&conf_str_hashtbl[i]); - INIT_LIST_HEAD(&unconf_str_hashtbl[i]); INIT_LIST_HEAD(&unconf_id_hashtbl[i]); INIT_LIST_HEAD(&reclaim_str_hashtbl[i]); } + conf_name_tree = RB_ROOT; + unconf_name_tree = RB_ROOT; for (i = 0; i < SESSION_HASH_SIZE; i++) INIT_LIST_HEAD(&sessionid_hashtbl[i]); for (i = 0; i < FILE_HASH_SIZE; i++) { @@ -4795,6 +4829,7 @@ out_recovery: return ret; } +/* should be called with the state lock held */ static void __nfs4_state_shutdown(void) { @@ -4802,17 +4837,24 @@ __nfs4_state_shutdown(void) struct nfs4_client *clp = NULL; struct nfs4_delegation *dp = NULL; struct list_head *pos, *next, reaplist; + struct rb_node *node, *tmp; for (i = 0; i < CLIENT_HASH_SIZE; i++) { while (!list_empty(&conf_id_hashtbl[i])) { clp = list_entry(conf_id_hashtbl[i].next, struct nfs4_client, cl_idhash); destroy_client(clp); } - while (!list_empty(&unconf_str_hashtbl[i])) { - clp = list_entry(unconf_str_hashtbl[i].next, struct nfs4_client, cl_strhash); - destroy_client(clp); - } } + + node = rb_first(&unconf_name_tree); + while (node != NULL) { + tmp = node; + node = rb_next(tmp); + clp = rb_entry(tmp, struct nfs4_client, cl_namenode); + rb_erase(tmp, &unconf_name_tree); + destroy_client(clp); + } + INIT_LIST_HEAD(&reaplist); spin_lock(&recall_lock); list_for_each_safe(pos, next, &del_recall_lru) { -- cgit v1.2.3 From 2216d449a97927cc105912e337d169cd4d4db548 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 12 Nov 2012 15:00:57 -0500 Subject: nfsd: get rid of cl_recdir field Remove the cl_recdir field from the nfs4_client struct. Instead, just compute it on the fly when and if it's needed, which is now only when the legacy client tracking code is in effect. The error handling in the legacy client tracker is also changed to handle the case where md5 is unavailable. In that case, we'll warn the admin with a KERN_ERR message and disable the client tracking. Signed-off-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 99998a1eb42..37b19f7948e 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1299,7 +1299,7 @@ static struct nfs4_stid *find_stateid_by_type(struct nfs4_client *cl, stateid_t return NULL; } -static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir, +static struct nfs4_client *create_client(struct xdr_netobj name, struct svc_rqst *rqstp, nfs4_verifier *verf) { struct nfs4_client *clp; @@ -1319,7 +1319,6 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir, return NULL; } idr_init(&clp->cl_stateids); - memcpy(clp->cl_recdir, recdir, HEXDIR_LEN); atomic_set(&clp->cl_refcount, 0); clp->cl_cb_state = NFSD4_CB_UNKNOWN; INIT_LIST_HEAD(&clp->cl_idhash); @@ -1616,7 +1615,6 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, { struct nfs4_client *unconf, *conf, *new; __be32 status; - char dname[HEXDIR_LEN]; char addr_str[INET6_ADDRSTRLEN]; nfs4_verifier verf = exid->verifier; struct sockaddr *sa = svc_addr(rqstp); @@ -1643,11 +1641,6 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, return nfserr_serverfault; /* no excuse :-/ */ } - status = nfs4_make_rec_clidname(dname, &exid->clname); - - if (status) - return status; - /* Cases below refer to rfc 5661 section 18.35.4: */ nfs4_lock_state(); conf = find_confirmed_client_by_name(&exid->clname); @@ -1701,7 +1694,7 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, /* case 1 (normal case) */ out_new: - new = create_client(exid->clname, dname, rqstp, &verf); + new = create_client(exid->clname, rqstp, &verf); if (new == NULL) { status = nfserr_jukebox; goto out; @@ -2236,12 +2229,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, nfs4_verifier clverifier = setclid->se_verf; struct nfs4_client *conf, *unconf, *new; __be32 status; - char dname[HEXDIR_LEN]; - status = nfs4_make_rec_clidname(dname, &clname); - if (status) - return status; - /* Cases below refer to rfc 3530 section 14.2.33: */ nfs4_lock_state(); conf = find_confirmed_client_by_name(&clname); @@ -2263,7 +2251,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (unconf) expire_client(unconf); status = nfserr_jukebox; - new = create_client(clname, dname, rqstp, &clverifier); + new = create_client(clname, rqstp, &clverifier); if (new == NULL) goto out; if (conf && same_verf(&conf->cl_verifier, &clverifier)) -- cgit v1.2.3 From 135ae8270d233067c8be426e2a59d0733ba74723 Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Sat, 10 Nov 2012 07:20:25 -0500 Subject: nfsd4: init_session should be declared static Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 37b19f7948e..7de9ba00a71 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -947,7 +947,7 @@ static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fchan) return new; } -void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, struct nfs4_client *clp, struct nfsd4_create_session *cses) +static void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, struct nfs4_client *clp, struct nfsd4_create_session *cses) { int idx; -- cgit v1.2.3 From 7f2210fa6b791c290e36d8b3c8af7aaf22b2aaf0 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 14 Nov 2012 18:21:05 +0300 Subject: nfsd: use service net instead of hard-coded net where possible Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 7de9ba00a71..207b9afbbac 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2281,7 +2281,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, nfs4_verifier confirm = setclientid_confirm->sc_confirm; clientid_t * clid = &setclientid_confirm->sc_clientid; __be32 status; - struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); + struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); if (STALE_CLIENTID(clid, nn)) return nfserr_stale_clientid; @@ -3151,7 +3151,7 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, { struct nfs4_client *clp; __be32 status; - struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); + struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); nfs4_lock_state(); dprintk("process_renew(%08x/%08x): starting\n", @@ -4104,7 +4104,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, bool new_state = false; int lkflg; int err; - struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); + struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); dprintk("NFSD: nfsd4_lock: start=%Ld length=%Ld\n", (long long) lock->lk_offset, @@ -4277,7 +4277,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct file_lock *file_lock = NULL; struct nfs4_lockowner *lo; __be32 status; - struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); + struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); if (locks_in_grace(SVC_NET(rqstp))) return nfserr_grace; @@ -4453,7 +4453,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, struct list_head matches; unsigned int hashval = ownerstr_hashval(clid->cl_id, owner); __be32 status; - struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); + struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n", clid->cl_boot, clid->cl_id); -- cgit v1.2.3 From c212cecfa21b3d30cd5cc2389754a46973ad9027 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 14 Nov 2012 18:21:10 +0300 Subject: nfsd: make nfs4_client network namespace dependent And use it's net where possible. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 207b9afbbac..001bbc99d7a 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1263,10 +1263,9 @@ same_creds(struct svc_cred *cr1, struct svc_cred *cr2) return 0 == strcmp(cr1->cr_principal, cr2->cr_principal); } -static void gen_clid(struct nfs4_client *clp) +static void gen_clid(struct nfs4_client *clp, struct nfsd_net *nn) { static u32 current_clientid = 1; - struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); clp->cl_clientid.cl_boot = nn->boot_time; clp->cl_clientid.cl_id = current_clientid++; @@ -1305,6 +1304,7 @@ static struct nfs4_client *create_client(struct xdr_netobj name, struct nfs4_client *clp; struct sockaddr *sa = svc_addr(rqstp); int ret; + struct net *net = SVC_NET(rqstp); clp = alloc_client(name); if (clp == NULL) @@ -1335,6 +1335,7 @@ static struct nfs4_client *create_client(struct xdr_netobj name, rpc_copy_addr((struct sockaddr *) &clp->cl_addr, sa); gen_confirm(clp); clp->cl_cb_session = NULL; + clp->net = net; return clp; } @@ -1471,7 +1472,7 @@ gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se, struct svc_r else goto out_err; - conn->cb_addrlen = rpc_uaddr2sockaddr(&init_net, se->se_callback_addr_val, + conn->cb_addrlen = rpc_uaddr2sockaddr(clp->net, se->se_callback_addr_val, se->se_callback_addr_len, (struct sockaddr *)&conn->cb_addr, sizeof(conn->cb_addr)); @@ -1619,6 +1620,7 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, nfs4_verifier verf = exid->verifier; struct sockaddr *sa = svc_addr(rqstp); bool update = exid->flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A; + struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); rpc_ntop(sa, addr_str, sizeof(addr_str)); dprintk("%s rqstp=%p exid=%p clname.len=%u clname.data=%p " @@ -1701,7 +1703,7 @@ out_new: } new->cl_minorversion = 1; - gen_clid(new); + gen_clid(new, nn); add_to_unconfirmed(new); out_copy: exid->clientid.cl_boot = new->cl_clientid.cl_boot; @@ -2229,7 +2231,8 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, nfs4_verifier clverifier = setclid->se_verf; struct nfs4_client *conf, *unconf, *new; __be32 status; - + struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); + /* Cases below refer to rfc 3530 section 14.2.33: */ nfs4_lock_state(); conf = find_confirmed_client_by_name(&clname); @@ -2258,7 +2261,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, /* case 1: probable callback update */ copy_clid(new, conf); else /* case 4 (new client) or cases 2, 3 (client reboot): */ - gen_clid(new); + gen_clid(new, nn); new->cl_minorversion = 0; gen_callback(new, setclid, rqstp); add_to_unconfirmed(new); -- cgit v1.2.3 From 52e19c09a183d82d99f10c284bc8b27933b1d1fc Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 14 Nov 2012 18:21:16 +0300 Subject: nfsd: make reclaim_str_hashtbl allocated per net This hash holds nfs4_clients info, which are network namespace aware. So let's make it allocated per network namespace. Note: this hash is used only by legacy tracker. So let's allocate hash in tracker init. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 42 +++++++++++++++--------------------------- 1 file changed, 15 insertions(+), 27 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 001bbc99d7a..ba478555950 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -393,11 +393,6 @@ unhash_delegation(struct nfs4_delegation *dp) /* client_lock protects the client lru list and session hash table */ static DEFINE_SPINLOCK(client_lock); -/* Hash tables for nfs4_clientid state */ -#define CLIENT_HASH_BITS 4 -#define CLIENT_HASH_SIZE (1 << CLIENT_HASH_BITS) -#define CLIENT_HASH_MASK (CLIENT_HASH_SIZE - 1) - static unsigned int clientid_hashval(u32 id) { return id & CLIENT_HASH_MASK; @@ -409,11 +404,8 @@ static unsigned int clientstr_hashval(const char *name) } /* - * reclaim_str_hashtbl[] holds known client info from previous reset/reboot - * used in reboot/reset lease grace period processing - * * conf_id_hashtbl[], and conf_name_tree hold confirmed - * setclientid_confirmed info. + * setclientid_confirmed info. * * unconf_id_hashtbl[] and unconf_name_tree hold unconfirmed * setclientid info. @@ -426,8 +418,6 @@ static unsigned int clientstr_hashval(const char *name) * * All of the above fields are protected by the client_mutex. */ -static struct list_head reclaim_str_hashtbl[CLIENT_HASH_SIZE]; -static int reclaim_str_hashtbl_size = 0; static struct list_head conf_id_hashtbl[CLIENT_HASH_SIZE]; static struct list_head unconf_id_hashtbl[CLIENT_HASH_SIZE]; static struct rb_root conf_name_tree; @@ -4509,11 +4499,11 @@ alloc_reclaim(void) } bool -nfs4_has_reclaimed_state(const char *name) +nfs4_has_reclaimed_state(const char *name, struct nfsd_net *nn) { struct nfs4_client_reclaim *crp; - crp = nfsd4_find_reclaim_client(name); + crp = nfsd4_find_reclaim_client(name, nn); return (crp && crp->cr_clp); } @@ -4521,7 +4511,7 @@ nfs4_has_reclaimed_state(const char *name) * failure => all reset bets are off, nfserr_no_grace... */ struct nfs4_client_reclaim * -nfs4_client_to_reclaim(const char *name) +nfs4_client_to_reclaim(const char *name, struct nfsd_net *nn) { unsigned int strhashval; struct nfs4_client_reclaim *crp; @@ -4531,42 +4521,42 @@ nfs4_client_to_reclaim(const char *name) if (crp) { strhashval = clientstr_hashval(name); INIT_LIST_HEAD(&crp->cr_strhash); - list_add(&crp->cr_strhash, &reclaim_str_hashtbl[strhashval]); + list_add(&crp->cr_strhash, &nn->reclaim_str_hashtbl[strhashval]); memcpy(crp->cr_recdir, name, HEXDIR_LEN); crp->cr_clp = NULL; - reclaim_str_hashtbl_size++; + nn->reclaim_str_hashtbl_size++; } return crp; } void -nfs4_remove_reclaim_record(struct nfs4_client_reclaim *crp) +nfs4_remove_reclaim_record(struct nfs4_client_reclaim *crp, struct nfsd_net *nn) { list_del(&crp->cr_strhash); kfree(crp); - reclaim_str_hashtbl_size--; + nn->reclaim_str_hashtbl_size--; } void -nfs4_release_reclaim(void) +nfs4_release_reclaim(struct nfsd_net *nn) { struct nfs4_client_reclaim *crp = NULL; int i; for (i = 0; i < CLIENT_HASH_SIZE; i++) { - while (!list_empty(&reclaim_str_hashtbl[i])) { - crp = list_entry(reclaim_str_hashtbl[i].next, + while (!list_empty(&nn->reclaim_str_hashtbl[i])) { + crp = list_entry(nn->reclaim_str_hashtbl[i].next, struct nfs4_client_reclaim, cr_strhash); - nfs4_remove_reclaim_record(crp); + nfs4_remove_reclaim_record(crp, nn); } } - BUG_ON(reclaim_str_hashtbl_size); + BUG_ON(nn->reclaim_str_hashtbl_size); } /* * called from OPEN, CLAIM_PREVIOUS with a new clientid. */ struct nfs4_client_reclaim * -nfsd4_find_reclaim_client(const char *recdir) +nfsd4_find_reclaim_client(const char *recdir, struct nfsd_net *nn) { unsigned int strhashval; struct nfs4_client_reclaim *crp = NULL; @@ -4574,7 +4564,7 @@ nfsd4_find_reclaim_client(const char *recdir) dprintk("NFSD: nfs4_find_reclaim_client for recdir %s\n", recdir); strhashval = clientstr_hashval(recdir); - list_for_each_entry(crp, &reclaim_str_hashtbl[strhashval], cr_strhash) { + list_for_each_entry(crp, &nn->reclaim_str_hashtbl[strhashval], cr_strhash) { if (same_name(crp->cr_recdir, recdir)) { return crp; } @@ -4732,7 +4722,6 @@ nfs4_state_init(void) for (i = 0; i < CLIENT_HASH_SIZE; i++) { INIT_LIST_HEAD(&conf_id_hashtbl[i]); INIT_LIST_HEAD(&unconf_id_hashtbl[i]); - INIT_LIST_HEAD(&reclaim_str_hashtbl[i]); } conf_name_tree = RB_ROOT; unconf_name_tree = RB_ROOT; @@ -4749,7 +4738,6 @@ nfs4_state_init(void) INIT_LIST_HEAD(&close_lru); INIT_LIST_HEAD(&client_lru); INIT_LIST_HEAD(&del_recall_lru); - reclaim_str_hashtbl_size = 0; } /* -- cgit v1.2.3 From 8daae4dc0d09d44d38194f72bc91740b46a6ce53 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 14 Nov 2012 18:21:21 +0300 Subject: nfsd: make conf_id_hashtbl allocated per net This hash holds nfs4_clients info, which are network namespace aware. So let's make it allocated per network namespace. Note: this hash can be allocated in per-net operations. But it looks better to allocate it on nfsd state start and thus don't waste resources if server is not running. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 75 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 21 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index ba478555950..6df427773d0 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -418,7 +418,6 @@ static unsigned int clientstr_hashval(const char *name) * * All of the above fields are protected by the client_mutex. */ -static struct list_head conf_id_hashtbl[CLIENT_HASH_SIZE]; static struct list_head unconf_id_hashtbl[CLIENT_HASH_SIZE]; static struct rb_root conf_name_tree; static struct rb_root unconf_name_tree; @@ -1385,9 +1384,10 @@ static void 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); dprintk("NFSD: move_to_confirm nfs4_client %p\n", clp); - list_move(&clp->cl_idhash, &conf_id_hashtbl[idhashval]); + list_move(&clp->cl_idhash, &nn->conf_id_hashtbl[idhashval]); rb_erase(&clp->cl_namenode, &unconf_name_tree); add_clp_to_name_tree(clp, &conf_name_tree); set_bit(NFSD4_CLIENT_CONFIRMED, &clp->cl_flags); @@ -1395,12 +1395,12 @@ move_to_confirmed(struct nfs4_client *clp) } static struct nfs4_client * -find_confirmed_client(clientid_t *clid, bool sessions) +find_confirmed_client(clientid_t *clid, bool sessions, struct nfsd_net *nn) { struct nfs4_client *clp; unsigned int idhashval = clientid_hashval(clid->cl_id); - list_for_each_entry(clp, &conf_id_hashtbl[idhashval], cl_idhash) { + list_for_each_entry(clp, &nn->conf_id_hashtbl[idhashval], cl_idhash) { if (same_clid(&clp->cl_clientid, clid)) { if ((bool)clp->cl_minorversion != sessions) return NULL; @@ -1787,6 +1787,7 @@ nfsd4_create_session(struct svc_rqst *rqstp, struct nfsd4_conn *conn; struct nfsd4_clid_slot *cs_slot = NULL; __be32 status = 0; + struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); if (cr_ses->flags & ~SESSION4_FLAG_MASK_A) return nfserr_inval; @@ -1802,7 +1803,7 @@ nfsd4_create_session(struct svc_rqst *rqstp, nfs4_lock_state(); unconf = find_unconfirmed_client(&cr_ses->clientid, true); - conf = find_confirmed_client(&cr_ses->clientid, true); + conf = find_confirmed_client(&cr_ses->clientid, true, nn); if (conf) { cs_slot = &conf->cl_cs_slot; @@ -2142,10 +2143,11 @@ nfsd4_destroy_clientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *csta { struct nfs4_client *conf, *unconf, *clp; __be32 status = 0; + struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); nfs4_lock_state(); unconf = find_unconfirmed_client(&dc->clientid, true); - conf = find_confirmed_client(&dc->clientid, true); + conf = find_confirmed_client(&dc->clientid, true, nn); if (conf) { clp = conf; @@ -2280,7 +2282,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, return nfserr_stale_clientid; nfs4_lock_state(); - conf = find_confirmed_client(clid, false); + conf = find_confirmed_client(clid, false, nn); unconf = find_unconfirmed_client(clid, false); /* * We try hard to give out unique clientid's, so if we get an @@ -2656,7 +2658,8 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate, oo = find_openstateowner_str(strhashval, open, cstate->minorversion); open->op_openowner = oo; if (!oo) { - clp = find_confirmed_client(clientid, cstate->minorversion); + clp = find_confirmed_client(clientid, cstate->minorversion, + nn); if (clp == NULL) return nfserr_expired; goto new_owner; @@ -3152,7 +3155,7 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, status = nfserr_stale_clientid; if (STALE_CLIENTID(clid, nn)) goto out; - clp = find_confirmed_client(clid, cstate->minorversion); + clp = find_confirmed_client(clid, cstate->minorversion, nn); status = nfserr_expired; if (clp == NULL) { /* We assume the client took too long to RENEW. */ @@ -3428,7 +3431,7 @@ static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask, s return nfserr_bad_stateid; if (STALE_STATEID(stateid, nn)) return nfserr_stale_stateid; - cl = find_confirmed_client(&stateid->si_opaque.so_clid, sessions); + cl = find_confirmed_client(&stateid->si_opaque.so_clid, sessions, nn); if (!cl) return nfserr_expired; *s = find_stateid_by_type(cl, stateid, typemask); @@ -4579,9 +4582,10 @@ __be32 nfs4_check_open_reclaim(clientid_t *clid, bool sessions) { struct nfs4_client *clp; + struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); /* find clientid in conf_id_hashtbl */ - clp = find_confirmed_client(clid, sessions); + clp = find_confirmed_client(clid, sessions, nn); if (clp == NULL) return nfserr_reclaim_bad; @@ -4720,7 +4724,6 @@ nfs4_state_init(void) int i; for (i = 0; i < CLIENT_HASH_SIZE; i++) { - INIT_LIST_HEAD(&conf_id_hashtbl[i]); INIT_LIST_HEAD(&unconf_id_hashtbl[i]); } conf_name_tree = RB_ROOT; @@ -4761,6 +4764,38 @@ set_max_delegations(void) max_delegations = nr_free_buffer_pages() >> (20 - 2 - PAGE_SHIFT); } +static int nfs4_state_start_net(struct net *net) +{ + struct nfsd_net *nn = net_generic(net, nfsd_net_id); + int i; + + nn->conf_id_hashtbl = kmalloc(sizeof(struct list_head) * + CLIENT_HASH_SIZE, GFP_KERNEL); + if (!nn->conf_id_hashtbl) + return -ENOMEM; + + for (i = 0; i < CLIENT_HASH_SIZE; i++) + INIT_LIST_HEAD(&nn->conf_id_hashtbl[i]); + + return 0; +} + +static void +__nfs4_state_shutdown_net(struct net *net) +{ + int i; + struct nfs4_client *clp = NULL; + struct nfsd_net *nn = net_generic(net, nfsd_net_id); + + for (i = 0; i < CLIENT_HASH_SIZE; i++) { + while (!list_empty(&nn->conf_id_hashtbl[i])) { + clp = list_entry(nn->conf_id_hashtbl[i].next, struct nfs4_client, cl_idhash); + destroy_client(clp); + } + } + kfree(nn->conf_id_hashtbl); +} + /* initialization to perform when the nfsd service is started: */ int @@ -4778,6 +4813,9 @@ nfs4_state_start(void) * basis. */ get_net(net); + ret = nfs4_state_start_net(net); + if (ret) + return ret; nfsd4_client_tracking_init(net); nn->boot_time = get_seconds(); locks_start_grace(net, &nn->nfsd4_manager); @@ -4804,26 +4842,21 @@ out_free_laundry: destroy_workqueue(laundry_wq); out_recovery: nfsd4_client_tracking_exit(net); + __nfs4_state_shutdown_net(net); put_net(net); return ret; } /* should be called with the state lock held */ static void -__nfs4_state_shutdown(void) +__nfs4_state_shutdown(struct net *net) { - int i; struct nfs4_client *clp = NULL; struct nfs4_delegation *dp = NULL; struct list_head *pos, *next, reaplist; struct rb_node *node, *tmp; - for (i = 0; i < CLIENT_HASH_SIZE; i++) { - while (!list_empty(&conf_id_hashtbl[i])) { - clp = list_entry(conf_id_hashtbl[i].next, struct nfs4_client, cl_idhash); - destroy_client(clp); - } - } + __nfs4_state_shutdown_net(net); node = rb_first(&unconf_name_tree); while (node != NULL) { @@ -4860,7 +4893,7 @@ nfs4_state_shutdown(void) destroy_workqueue(laundry_wq); locks_end_grace(&nn->nfsd4_manager); nfs4_lock_state(); - __nfs4_state_shutdown(); + __nfs4_state_shutdown(net); nfs4_unlock_state(); nfsd4_destroy_callback_queue(); } -- cgit v1.2.3 From 382a62e76cbf91fb364a4cd8732761e4ecf62153 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 14 Nov 2012 18:21:26 +0300 Subject: nfsd: make conf_name_tree per net This tree holds nfs4_clients info, which are network namespace aware. So let's make it per network namespace. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 6df427773d0..d40e57b9051 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -404,9 +404,6 @@ static unsigned int clientstr_hashval(const char *name) } /* - * conf_id_hashtbl[], and conf_name_tree hold confirmed - * setclientid_confirmed info. - * * unconf_id_hashtbl[] and unconf_name_tree hold unconfirmed * setclientid info. * @@ -419,7 +416,6 @@ static unsigned int clientstr_hashval(const char *name) * All of the above fields are protected by the client_mutex. */ static struct list_head unconf_id_hashtbl[CLIENT_HASH_SIZE]; -static struct rb_root conf_name_tree; static struct rb_root unconf_name_tree; static struct list_head client_lru; static struct list_head close_lru; @@ -1114,6 +1110,7 @@ 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(&recall_lock); @@ -1136,7 +1133,7 @@ destroy_client(struct nfs4_client *clp) 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, &conf_name_tree); + rb_erase(&clp->cl_namenode, &nn->conf_name_tree); else rb_erase(&clp->cl_namenode, &unconf_name_tree); spin_lock(&client_lock); @@ -1389,7 +1386,7 @@ move_to_confirmed(struct nfs4_client *clp) 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, &unconf_name_tree); - add_clp_to_name_tree(clp, &conf_name_tree); + add_clp_to_name_tree(clp, &nn->conf_name_tree); set_bit(NFSD4_CLIENT_CONFIRMED, &clp->cl_flags); renew_client(clp); } @@ -1433,9 +1430,9 @@ static bool clp_used_exchangeid(struct nfs4_client *clp) } static struct nfs4_client * -find_confirmed_client_by_name(struct xdr_netobj *name) +find_confirmed_client_by_name(struct xdr_netobj *name, struct nfsd_net *nn) { - return find_clp_in_name_tree(name, &conf_name_tree); + return find_clp_in_name_tree(name, &nn->conf_name_tree); } static struct nfs4_client * @@ -1635,7 +1632,7 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, /* Cases below refer to rfc 5661 section 18.35.4: */ nfs4_lock_state(); - conf = find_confirmed_client_by_name(&exid->clname); + conf = find_confirmed_client_by_name(&exid->clname, nn); if (conf) { bool creds_match = same_creds(&conf->cl_cred, &rqstp->rq_cred); bool verfs_match = same_verf(&verf, &conf->cl_verifier); @@ -1829,7 +1826,7 @@ nfsd4_create_session(struct svc_rqst *rqstp, status = nfserr_seq_misordered; goto out_free_conn; } - old = find_confirmed_client_by_name(&unconf->cl_name); + old = find_confirmed_client_by_name(&unconf->cl_name, nn); if (old) expire_client(old); move_to_confirmed(unconf); @@ -2227,7 +2224,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, /* Cases below refer to rfc 3530 section 14.2.33: */ nfs4_lock_state(); - conf = find_confirmed_client_by_name(&clname); + conf = find_confirmed_client_by_name(&clname, nn); if (conf) { /* case 0: */ status = nfserr_clid_inuse; @@ -2309,7 +2306,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, 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); + conf = find_confirmed_client_by_name(&unconf->cl_name, nn); if (conf) expire_client(conf); move_to_confirmed(unconf); @@ -4726,7 +4723,6 @@ nfs4_state_init(void) for (i = 0; i < CLIENT_HASH_SIZE; i++) { INIT_LIST_HEAD(&unconf_id_hashtbl[i]); } - conf_name_tree = RB_ROOT; unconf_name_tree = RB_ROOT; for (i = 0; i < SESSION_HASH_SIZE; i++) INIT_LIST_HEAD(&sessionid_hashtbl[i]); @@ -4772,12 +4768,17 @@ static int nfs4_state_start_net(struct net *net) nn->conf_id_hashtbl = kmalloc(sizeof(struct list_head) * CLIENT_HASH_SIZE, GFP_KERNEL); if (!nn->conf_id_hashtbl) - return -ENOMEM; + goto err; - for (i = 0; i < CLIENT_HASH_SIZE; i++) + for (i = 0; i < CLIENT_HASH_SIZE; i++) { INIT_LIST_HEAD(&nn->conf_id_hashtbl[i]); + } + nn->conf_name_tree = RB_ROOT; return 0; + +err: + return -ENOMEM; } static void -- cgit v1.2.3 From 0a7ec37727dcc3293cd4c9958b25c43f3a797d47 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 14 Nov 2012 18:21:31 +0300 Subject: nfsd: make unconf_id_hashtbl allocated per net This hash holds nfs4_clients info, which are network namespace aware. So let's make it allocated per network namespace. Note: this hash can be allocated in per-net operations. But it looks better to allocate it on nfsd state start and thus don't waste resources if server is not running. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index d40e57b9051..f33bbfbdc24 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -415,7 +415,6 @@ static unsigned int clientstr_hashval(const char *name) * * All of the above fields are protected by the client_mutex. */ -static struct list_head unconf_id_hashtbl[CLIENT_HASH_SIZE]; static struct rb_root unconf_name_tree; static struct list_head client_lru; static struct list_head close_lru; @@ -1369,11 +1368,12 @@ static void add_to_unconfirmed(struct nfs4_client *clp) { unsigned int idhashval; + struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); clear_bit(NFSD4_CLIENT_CONFIRMED, &clp->cl_flags); add_clp_to_name_tree(clp, &unconf_name_tree); idhashval = clientid_hashval(clp->cl_clientid.cl_id); - list_add(&clp->cl_idhash, &unconf_id_hashtbl[idhashval]); + list_add(&clp->cl_idhash, &nn->unconf_id_hashtbl[idhashval]); renew_client(clp); } @@ -1409,12 +1409,12 @@ find_confirmed_client(clientid_t *clid, bool sessions, struct nfsd_net *nn) } static struct nfs4_client * -find_unconfirmed_client(clientid_t *clid, bool sessions) +find_unconfirmed_client(clientid_t *clid, bool sessions, struct nfsd_net *nn) { struct nfs4_client *clp; unsigned int idhashval = clientid_hashval(clid->cl_id); - list_for_each_entry(clp, &unconf_id_hashtbl[idhashval], cl_idhash) { + list_for_each_entry(clp, &nn->unconf_id_hashtbl[idhashval], cl_idhash) { if (same_clid(&clp->cl_clientid, clid)) { if ((bool)clp->cl_minorversion != sessions) return NULL; @@ -1799,7 +1799,7 @@ nfsd4_create_session(struct svc_rqst *rqstp, goto out_free_session; nfs4_lock_state(); - unconf = find_unconfirmed_client(&cr_ses->clientid, true); + unconf = find_unconfirmed_client(&cr_ses->clientid, true, nn); conf = find_confirmed_client(&cr_ses->clientid, true, nn); if (conf) { @@ -2143,7 +2143,7 @@ nfsd4_destroy_clientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *csta struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); nfs4_lock_state(); - unconf = find_unconfirmed_client(&dc->clientid, true); + unconf = find_unconfirmed_client(&dc->clientid, true, nn); conf = find_confirmed_client(&dc->clientid, true, nn); if (conf) { @@ -2280,7 +2280,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, nfs4_lock_state(); conf = find_confirmed_client(clid, false, nn); - unconf = find_unconfirmed_client(clid, false); + unconf = find_unconfirmed_client(clid, false, nn); /* * We try hard to give out unique clientid's, so if we get an * attempt to confirm the same clientid with a different cred, @@ -4720,9 +4720,6 @@ nfs4_state_init(void) { int i; - for (i = 0; i < CLIENT_HASH_SIZE; i++) { - INIT_LIST_HEAD(&unconf_id_hashtbl[i]); - } unconf_name_tree = RB_ROOT; for (i = 0; i < SESSION_HASH_SIZE; i++) INIT_LIST_HEAD(&sessionid_hashtbl[i]); @@ -4769,14 +4766,21 @@ static int nfs4_state_start_net(struct net *net) CLIENT_HASH_SIZE, GFP_KERNEL); if (!nn->conf_id_hashtbl) goto err; + nn->unconf_id_hashtbl = kmalloc(sizeof(struct list_head) * + CLIENT_HASH_SIZE, GFP_KERNEL); + if (!nn->unconf_id_hashtbl) + goto err_unconf_id; for (i = 0; i < CLIENT_HASH_SIZE; i++) { INIT_LIST_HEAD(&nn->conf_id_hashtbl[i]); + INIT_LIST_HEAD(&nn->unconf_id_hashtbl[i]); } nn->conf_name_tree = RB_ROOT; return 0; +err_unconf_id: + kfree(nn->conf_id_hashtbl); err: return -ENOMEM; } @@ -4794,6 +4798,7 @@ __nfs4_state_shutdown_net(struct net *net) destroy_client(clp); } } + kfree(nn->unconf_id_hashtbl); kfree(nn->conf_id_hashtbl); } -- cgit v1.2.3 From a99454aa4ff1241a19dcb486fa302d3e8cc09e5b Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 14 Nov 2012 18:21:36 +0300 Subject: nfsd: make unconf_name_tree per net This hash holds nfs4_clients info, which are network namespace aware. So let's make it allocated per network namespace. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 42 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 23 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index f33bbfbdc24..b35329199e3 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -404,9 +404,6 @@ static unsigned int clientstr_hashval(const char *name) } /* - * unconf_id_hashtbl[] and unconf_name_tree hold unconfirmed - * setclientid info. - * * client_lru holds client queue ordered by nfs4_client.cl_time * for lease renewal. * @@ -415,7 +412,6 @@ static unsigned int clientstr_hashval(const char *name) * * All of the above fields are protected by the client_mutex. */ -static struct rb_root unconf_name_tree; static struct list_head client_lru; static struct list_head close_lru; @@ -1134,7 +1130,7 @@ destroy_client(struct nfs4_client *clp) if (test_bit(NFSD4_CLIENT_CONFIRMED, &clp->cl_flags)) rb_erase(&clp->cl_namenode, &nn->conf_name_tree); else - rb_erase(&clp->cl_namenode, &unconf_name_tree); + rb_erase(&clp->cl_namenode, &nn->unconf_name_tree); spin_lock(&client_lock); unhash_client_locked(clp); if (atomic_read(&clp->cl_refcount) == 0) @@ -1371,7 +1367,7 @@ add_to_unconfirmed(struct nfs4_client *clp) struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); clear_bit(NFSD4_CLIENT_CONFIRMED, &clp->cl_flags); - add_clp_to_name_tree(clp, &unconf_name_tree); + 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); @@ -1385,7 +1381,7 @@ move_to_confirmed(struct nfs4_client *clp) 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, &unconf_name_tree); + 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); @@ -1436,9 +1432,9 @@ find_confirmed_client_by_name(struct xdr_netobj *name, struct nfsd_net *nn) } static struct nfs4_client * -find_unconfirmed_client_by_name(struct xdr_netobj *name) +find_unconfirmed_client_by_name(struct xdr_netobj *name, struct nfsd_net *nn) { - return find_clp_in_name_tree(name, &unconf_name_tree); + return find_clp_in_name_tree(name, &nn->unconf_name_tree); } static void @@ -1677,7 +1673,7 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, goto out; } - unconf = find_unconfirmed_client_by_name(&exid->clname); + unconf = find_unconfirmed_client_by_name(&exid->clname, nn); if (unconf) /* case 4, possible retry or client restart */ expire_client(unconf); @@ -2239,7 +2235,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, goto out; } } - unconf = find_unconfirmed_client_by_name(&clname); + unconf = find_unconfirmed_client_by_name(&clname, nn); if (unconf) expire_client(unconf); status = nfserr_jukebox; @@ -4720,7 +4716,6 @@ nfs4_state_init(void) { int i; - unconf_name_tree = RB_ROOT; for (i = 0; i < SESSION_HASH_SIZE; i++) INIT_LIST_HEAD(&sessionid_hashtbl[i]); for (i = 0; i < FILE_HASH_SIZE; i++) { @@ -4776,6 +4771,7 @@ static int nfs4_state_start_net(struct net *net) INIT_LIST_HEAD(&nn->unconf_id_hashtbl[i]); } nn->conf_name_tree = RB_ROOT; + nn->unconf_name_tree = RB_ROOT; return 0; @@ -4791,6 +4787,7 @@ __nfs4_state_shutdown_net(struct net *net) int i; struct nfs4_client *clp = NULL; struct nfsd_net *nn = net_generic(net, nfsd_net_id); + struct rb_node *node, *tmp; for (i = 0; i < CLIENT_HASH_SIZE; i++) { while (!list_empty(&nn->conf_id_hashtbl[i])) { @@ -4798,6 +4795,16 @@ __nfs4_state_shutdown_net(struct net *net) destroy_client(clp); } } + + node = rb_first(&nn->unconf_name_tree); + while (node != NULL) { + tmp = node; + node = rb_next(tmp); + clp = rb_entry(tmp, struct nfs4_client, cl_namenode); + rb_erase(tmp, &nn->unconf_name_tree); + destroy_client(clp); + } + kfree(nn->unconf_id_hashtbl); kfree(nn->conf_id_hashtbl); } @@ -4857,22 +4864,11 @@ out_recovery: static void __nfs4_state_shutdown(struct net *net) { - struct nfs4_client *clp = NULL; struct nfs4_delegation *dp = NULL; struct list_head *pos, *next, reaplist; - struct rb_node *node, *tmp; __nfs4_state_shutdown_net(net); - node = rb_first(&unconf_name_tree); - while (node != NULL) { - tmp = node; - node = rb_next(tmp); - clp = rb_entry(tmp, struct nfs4_client, cl_namenode); - rb_erase(tmp, &unconf_name_tree); - destroy_client(clp); - } - INIT_LIST_HEAD(&reaplist); spin_lock(&recall_lock); list_for_each_safe(pos, next, &del_recall_lru) { -- cgit v1.2.3 From 9b5311374057e5c87017ea3756e566047c9b61e7 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 14 Nov 2012 18:21:41 +0300 Subject: nfsd: make ownerstr_hashtbl allocated per net This hash holds open owner state and closely associated with nfs4_clients info, which are network namespace aware. So let's make it allocated per network namespace too. Note: this hash can be allocated in per-net operations. But it looks better to allocate it on nfsd state start and thus don't waste resources if server is not running. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index b35329199e3..f68514d8210 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -176,8 +176,6 @@ static unsigned int ownerstr_hashval(u32 clientid, struct xdr_netobj *ownername) return ret & OWNER_HASH_MASK; } -static struct list_head ownerstr_hashtbl[OWNER_HASH_SIZE]; - /* hash table for nfs4_file */ #define FILE_HASH_BITS 8 #define FILE_HASH_SIZE (1 << FILE_HASH_BITS) @@ -2428,7 +2426,9 @@ static inline void *alloc_stateowner(struct kmem_cache *slab, struct xdr_netobj static void hash_openowner(struct nfs4_openowner *oo, struct nfs4_client *clp, unsigned int strhashval) { - list_add(&oo->oo_owner.so_strhash, &ownerstr_hashtbl[strhashval]); + struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); + + list_add(&oo->oo_owner.so_strhash, &nn->ownerstr_hashtbl[strhashval]); list_add(&oo->oo_perclient, &clp->cl_openowners); } @@ -2486,13 +2486,14 @@ 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, bool sessions) +find_openstateowner_str(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; - list_for_each_entry(so, &ownerstr_hashtbl[hashval], so_strhash) { + list_for_each_entry(so, &nn->ownerstr_hashtbl[hashval], so_strhash) { if (!so->so_is_open_owner) continue; if (same_owner_str(so, &open->op_owner, &open->op_clientid)) { @@ -2648,7 +2649,7 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate, return nfserr_jukebox; strhashval = ownerstr_hashval(clientid->cl_id, &open->op_owner); - oo = find_openstateowner_str(strhashval, open, cstate->minorversion); + oo = find_openstateowner_str(strhashval, open, cstate->minorversion, nn); open->op_openowner = oo; if (!oo) { clp = find_confirmed_client(clientid, cstate->minorversion, @@ -3976,8 +3977,9 @@ static void hash_lockowner(struct nfs4_lockowner *lo, unsigned int strhashval, s struct inode *inode = open_stp->st_file->fi_inode; unsigned int inohash = lockowner_ino_hashval(inode, clp->cl_clientid.cl_id, &lo->lo_owner.so_owner); + struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); - list_add(&lo->lo_owner.so_strhash, &ownerstr_hashtbl[strhashval]); + list_add(&lo->lo_owner.so_strhash, &nn->ownerstr_hashtbl[strhashval]); list_add(&lo->lo_owner_ino_hash, &lockowner_ino_hashtbl[inohash]); list_add(&lo->lo_perstateid, &open_stp->st_lockowners); } @@ -4458,7 +4460,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, status = nfserr_locks_held; INIT_LIST_HEAD(&matches); - list_for_each_entry(sop, &ownerstr_hashtbl[hashval], so_strhash) { + 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)) @@ -4614,13 +4616,14 @@ static void release_openowner_sop(struct nfs4_stateowner *sop) } static int nfsd_release_n_owners(u64 num, bool is_open_owner, - void (*release_sop)(struct nfs4_stateowner *)) + void (*release_sop)(struct nfs4_stateowner *), + struct nfsd_net *nn) { int i, count = 0; struct nfs4_stateowner *sop, *next; for (i = 0; i < OWNER_HASH_SIZE; i++) { - list_for_each_entry_safe(sop, next, &ownerstr_hashtbl[i], so_strhash) { + list_for_each_entry_safe(sop, next, &nn->ownerstr_hashtbl[i], so_strhash) { if (sop->so_is_open_owner != is_open_owner) continue; release_sop(sop); @@ -4634,9 +4637,10 @@ static int nfsd_release_n_owners(u64 num, bool is_open_owner, void nfsd_forget_locks(u64 num) { int count; + struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); nfs4_lock_state(); - count = nfsd_release_n_owners(num, false, release_lockowner_sop); + count = nfsd_release_n_owners(num, false, release_lockowner_sop, nn); nfs4_unlock_state(); printk(KERN_INFO "NFSD: Forgot %d locks", count); @@ -4645,9 +4649,10 @@ void nfsd_forget_locks(u64 num) void nfsd_forget_openowners(u64 num) { int count; + struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); nfs4_lock_state(); - count = nfsd_release_n_owners(num, true, release_openowner_sop); + count = nfsd_release_n_owners(num, true, release_openowner_sop, nn); nfs4_unlock_state(); printk(KERN_INFO "NFSD: Forgot %d open owners", count); @@ -4721,9 +4726,6 @@ nfs4_state_init(void) for (i = 0; i < FILE_HASH_SIZE; i++) { INIT_LIST_HEAD(&file_hashtbl[i]); } - for (i = 0; i < OWNER_HASH_SIZE; i++) { - INIT_LIST_HEAD(&ownerstr_hashtbl[i]); - } for (i = 0; i < LOCKOWNER_INO_HASH_SIZE; i++) INIT_LIST_HEAD(&lockowner_ino_hashtbl[i]); INIT_LIST_HEAD(&close_lru); @@ -4765,16 +4767,24 @@ static int nfs4_state_start_net(struct net *net) CLIENT_HASH_SIZE, GFP_KERNEL); if (!nn->unconf_id_hashtbl) goto err_unconf_id; + nn->ownerstr_hashtbl = kmalloc(sizeof(struct list_head) * + OWNER_HASH_SIZE, GFP_KERNEL); + if (!nn->ownerstr_hashtbl) + goto err_ownerstr; for (i = 0; i < CLIENT_HASH_SIZE; i++) { INIT_LIST_HEAD(&nn->conf_id_hashtbl[i]); INIT_LIST_HEAD(&nn->unconf_id_hashtbl[i]); } + for (i = 0; i < OWNER_HASH_SIZE; i++) + INIT_LIST_HEAD(&nn->ownerstr_hashtbl[i]); nn->conf_name_tree = RB_ROOT; nn->unconf_name_tree = RB_ROOT; return 0; +err_ownerstr: + kfree(nn->unconf_id_hashtbl); err_unconf_id: kfree(nn->conf_id_hashtbl); err: @@ -4805,6 +4815,7 @@ __nfs4_state_shutdown_net(struct net *net) destroy_client(clp); } + kfree(nn->ownerstr_hashtbl); kfree(nn->unconf_id_hashtbl); kfree(nn->conf_id_hashtbl); } -- cgit v1.2.3 From 20e9e2bc98b907efe82621797c561f6169d63d96 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 14 Nov 2012 18:21:46 +0300 Subject: nfsd: make lockowner_ino_hashtbl allocated per net This hash holds file lock owners and closely associated with nfs4_clients info, which are network namespace aware. So let's make it allocated per network namespace too. Note: this hash can be allocated in per-net operations. But it looks better to allocate it on nfsd state start and thus don't waste resources if server is not running. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index f68514d8210..1e76d55a3e9 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3862,8 +3862,6 @@ out: #define LOFF_OVERFLOW(start, len) ((u64)(len) > ~(u64)(start)) -#define LOCKOWNER_INO_HASH_BITS 8 -#define LOCKOWNER_INO_HASH_SIZE (1 << LOCKOWNER_INO_HASH_BITS) #define LOCKOWNER_INO_HASH_MASK (LOCKOWNER_INO_HASH_SIZE - 1) static inline u64 @@ -3893,8 +3891,6 @@ static unsigned int lockowner_ino_hashval(struct inode *inode, u32 cl_id, struct & LOCKOWNER_INO_HASH_MASK; } -static struct list_head lockowner_ino_hashtbl[LOCKOWNER_INO_HASH_SIZE]; - /* * TODO: Linux file offsets are _signed_ 64-bit quantities, which means that * we can't properly handle lock requests that go beyond the (2^63 - 1)-th @@ -3960,12 +3956,12 @@ static bool same_lockowner_ino(struct nfs4_lockowner *lo, struct inode *inode, c static struct nfs4_lockowner * find_lockowner_str(struct inode *inode, clientid_t *clid, - struct xdr_netobj *owner) + struct xdr_netobj *owner, struct nfsd_net *nn) { unsigned int hashval = lockowner_ino_hashval(inode, clid->cl_id, owner); struct nfs4_lockowner *lo; - list_for_each_entry(lo, &lockowner_ino_hashtbl[hashval], lo_owner_ino_hash) { + list_for_each_entry(lo, &nn->lockowner_ino_hashtbl[hashval], lo_owner_ino_hash) { if (same_lockowner_ino(lo, inode, clid, owner)) return lo; } @@ -3980,7 +3976,7 @@ static void hash_lockowner(struct nfs4_lockowner *lo, unsigned int strhashval, s struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); list_add(&lo->lo_owner.so_strhash, &nn->ownerstr_hashtbl[strhashval]); - list_add(&lo->lo_owner_ino_hash, &lockowner_ino_hashtbl[inohash]); + list_add(&lo->lo_owner_ino_hash, &nn->lockowner_ino_hashtbl[inohash]); list_add(&lo->lo_perstateid, &open_stp->st_lockowners); } @@ -4054,8 +4050,10 @@ static __be32 lookup_or_create_lock_state(struct nfsd4_compound_state *cstate, s struct nfs4_client *cl = oo->oo_owner.so_client; struct nfs4_lockowner *lo; unsigned int strhashval; + struct nfsd_net *nn = net_generic(cl->net, nfsd_net_id); - lo = find_lockowner_str(fi->fi_inode, &cl->cl_clientid, &lock->v.new.owner); + lo = find_lockowner_str(fi->fi_inode, &cl->cl_clientid, + &lock->v.new.owner, nn); if (lo) { if (!cstate->minorversion) return nfserr_bad_seqid; @@ -4308,7 +4306,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, goto out; } - lo = find_lockowner_str(inode, &lockt->lt_clientid, &lockt->lt_owner); + lo = find_lockowner_str(inode, &lockt->lt_clientid, &lockt->lt_owner, nn); if (lo) file_lock->fl_owner = (fl_owner_t)lo; file_lock->fl_pid = current->tgid; @@ -4726,8 +4724,6 @@ nfs4_state_init(void) for (i = 0; i < FILE_HASH_SIZE; i++) { INIT_LIST_HEAD(&file_hashtbl[i]); } - for (i = 0; i < LOCKOWNER_INO_HASH_SIZE; i++) - INIT_LIST_HEAD(&lockowner_ino_hashtbl[i]); INIT_LIST_HEAD(&close_lru); INIT_LIST_HEAD(&client_lru); INIT_LIST_HEAD(&del_recall_lru); @@ -4771,6 +4767,10 @@ static int nfs4_state_start_net(struct net *net) OWNER_HASH_SIZE, GFP_KERNEL); if (!nn->ownerstr_hashtbl) goto err_ownerstr; + nn->lockowner_ino_hashtbl = kmalloc(sizeof(struct list_head) * + LOCKOWNER_INO_HASH_SIZE, GFP_KERNEL); + if (!nn->lockowner_ino_hashtbl) + goto err_lockowner_ino; for (i = 0; i < CLIENT_HASH_SIZE; i++) { INIT_LIST_HEAD(&nn->conf_id_hashtbl[i]); @@ -4778,11 +4778,15 @@ static int nfs4_state_start_net(struct net *net) } for (i = 0; i < OWNER_HASH_SIZE; i++) INIT_LIST_HEAD(&nn->ownerstr_hashtbl[i]); + for (i = 0; i < LOCKOWNER_INO_HASH_SIZE; i++) + INIT_LIST_HEAD(&nn->lockowner_ino_hashtbl[i]); nn->conf_name_tree = RB_ROOT; nn->unconf_name_tree = RB_ROOT; return 0; +err_lockowner_ino: + kfree(nn->ownerstr_hashtbl); err_ownerstr: kfree(nn->unconf_id_hashtbl); err_unconf_id: @@ -4815,6 +4819,7 @@ __nfs4_state_shutdown_net(struct net *net) destroy_client(clp); } + kfree(nn->lockowner_ino_hashtbl); kfree(nn->ownerstr_hashtbl); kfree(nn->unconf_id_hashtbl); kfree(nn->conf_id_hashtbl); -- cgit v1.2.3 From 1872de0e8171904612ee85de218fa045bc473cad Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 14 Nov 2012 18:21:51 +0300 Subject: nfsd: make sessionid_hashtbl allocated per net This hash holds established sessions state and closely associated with nfs4_clients info, which are network namespace aware. So let's make it allocated per network namespace too. Note: this hash can be allocated in per-net operations. But it looks better to allocate it on nfsd state start and thus don't waste resources if server is not running. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 1e76d55a3e9..248f217a00b 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -632,9 +632,6 @@ static void release_openowner(struct nfs4_openowner *oo) nfs4_free_openowner(oo); } -#define SESSION_HASH_SIZE 512 -static struct list_head sessionid_hashtbl[SESSION_HASH_SIZE]; - static inline int hash_sessionid(struct nfs4_sessionid *sessionid) { @@ -928,6 +925,7 @@ static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fchan) static void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, struct nfs4_client *clp, struct nfsd4_create_session *cses) { int idx; + struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); new->se_client = clp; gen_sessionid(new); @@ -941,7 +939,7 @@ static void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, stru kref_init(&new->se_ref); idx = hash_sessionid(&new->se_sessionid); spin_lock(&client_lock); - list_add(&new->se_hash, &sessionid_hashtbl[idx]); + 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); @@ -963,15 +961,16 @@ 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) +find_in_sessionid_hashtbl(struct nfs4_sessionid *sessionid, struct net *net) { struct nfsd4_session *elem; int idx; + struct nfsd_net *nn = net_generic(net, nfsd_net_id); dump_sessionid(__func__, sessionid); idx = hash_sessionid(sessionid); /* Search in the appropriate list */ - list_for_each_entry(elem, &sessionid_hashtbl[idx], se_hash) { + list_for_each_entry(elem, &nn->sessionid_hashtbl[idx], se_hash) { if (!memcmp(elem->se_sessionid.data, sessionid->data, NFS4_MAX_SESSIONID_LEN)) { return elem; @@ -1905,7 +1904,7 @@ __be32 nfsd4_bind_conn_to_session(struct svc_rqst *rqstp, if (!nfsd4_last_compound_op(rqstp)) return nfserr_not_only_op; spin_lock(&client_lock); - cstate->session = find_in_sessionid_hashtbl(&bcts->sessionid); + cstate->session = find_in_sessionid_hashtbl(&bcts->sessionid, SVC_NET(rqstp)); /* Sorta weird: we only need the refcnt'ing because new_conn acquires * client_lock iself: */ if (cstate->session) { @@ -1954,7 +1953,7 @@ nfsd4_destroy_session(struct svc_rqst *r, } dump_sessionid(__func__, &sessionid->sessionid); spin_lock(&client_lock); - ses = find_in_sessionid_hashtbl(&sessionid->sessionid); + ses = find_in_sessionid_hashtbl(&sessionid->sessionid, SVC_NET(r)); if (!ses) { spin_unlock(&client_lock); goto out; @@ -2050,7 +2049,7 @@ nfsd4_sequence(struct svc_rqst *rqstp, spin_lock(&client_lock); status = nfserr_badsession; - session = find_in_sessionid_hashtbl(&seq->sessionid); + session = find_in_sessionid_hashtbl(&seq->sessionid, SVC_NET(rqstp)); if (!session) goto out; @@ -4719,8 +4718,6 @@ nfs4_state_init(void) { int i; - for (i = 0; i < SESSION_HASH_SIZE; i++) - INIT_LIST_HEAD(&sessionid_hashtbl[i]); for (i = 0; i < FILE_HASH_SIZE; i++) { INIT_LIST_HEAD(&file_hashtbl[i]); } @@ -4771,6 +4768,10 @@ static int nfs4_state_start_net(struct net *net) LOCKOWNER_INO_HASH_SIZE, GFP_KERNEL); if (!nn->lockowner_ino_hashtbl) goto err_lockowner_ino; + nn->sessionid_hashtbl = kmalloc(sizeof(struct list_head) * + SESSION_HASH_SIZE, GFP_KERNEL); + if (!nn->sessionid_hashtbl) + goto err_sessionid; for (i = 0; i < CLIENT_HASH_SIZE; i++) { INIT_LIST_HEAD(&nn->conf_id_hashtbl[i]); @@ -4780,11 +4781,15 @@ static int nfs4_state_start_net(struct net *net) INIT_LIST_HEAD(&nn->ownerstr_hashtbl[i]); for (i = 0; i < LOCKOWNER_INO_HASH_SIZE; i++) INIT_LIST_HEAD(&nn->lockowner_ino_hashtbl[i]); + for (i = 0; i < SESSION_HASH_SIZE; i++) + INIT_LIST_HEAD(&nn->sessionid_hashtbl[i]); nn->conf_name_tree = RB_ROOT; nn->unconf_name_tree = RB_ROOT; return 0; +err_sessionid: + kfree(nn->lockowner_ino_hashtbl); err_lockowner_ino: kfree(nn->ownerstr_hashtbl); err_ownerstr: @@ -4819,6 +4824,7 @@ __nfs4_state_shutdown_net(struct net *net) destroy_client(clp); } + kfree(nn->sessionid_hashtbl); kfree(nn->lockowner_ino_hashtbl); kfree(nn->ownerstr_hashtbl); kfree(nn->unconf_id_hashtbl); -- cgit v1.2.3 From 5ed58bb243484e01e82ffca8451907403168e262 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 14 Nov 2012 18:21:56 +0300 Subject: nfsd: make client_lru list per net This list holds nfs4 clients queue for lease renewal, which are network namespace aware. So let's make this list per network namespace too. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 248f217a00b..9cf7e9bf369 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -402,15 +402,11 @@ static unsigned int clientstr_hashval(const char *name) } /* - * client_lru holds client queue ordered by nfs4_client.cl_time - * for lease renewal. - * * close_lru holds (open) stateowner queue ordered by nfs4_stateowner.so_time * for last close replay. * * All of the above fields are protected by the client_mutex. */ -static struct list_head client_lru; static struct list_head close_lru; /* @@ -995,6 +991,8 @@ unhash_session(struct nfsd4_session *ses) static inline void renew_client_locked(struct nfs4_client *clp) { + struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); + if (is_client_expired(clp)) { WARN_ON(1); printk("%s: client (clientid %08x/%08x) already expired\n", @@ -1007,7 +1005,7 @@ renew_client_locked(struct nfs4_client *clp) dprintk("renewing client (clientid %08x/%08x)\n", clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id); - list_move_tail(&clp->cl_lru, &client_lru); + list_move_tail(&clp->cl_lru, &nn->client_lru); clp->cl_time = get_seconds(); } @@ -3196,6 +3194,7 @@ nfs4_laundromat(void) time_t cutoff = get_seconds() - nfsd4_lease; time_t t, clientid_val = nfsd4_lease; time_t u, test_val = nfsd4_lease; + struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); nfs4_lock_state(); @@ -3203,7 +3202,7 @@ nfs4_laundromat(void) nfsd4_end_grace(&init_net); INIT_LIST_HEAD(&reaplist); spin_lock(&client_lock); - list_for_each_safe(pos, next, &client_lru) { + list_for_each_safe(pos, next, &nn->client_lru) { clp = list_entry(pos, struct nfs4_client, cl_lru); if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) { t = clp->cl_time - cutoff; @@ -4590,9 +4589,10 @@ void nfsd_forget_clients(u64 num) { struct nfs4_client *clp, *next; int count = 0; + struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); nfs4_lock_state(); - list_for_each_entry_safe(clp, next, &client_lru, cl_lru) { + list_for_each_entry_safe(clp, next, &nn->client_lru, cl_lru) { expire_client(clp); if (++count == num) break; @@ -4722,7 +4722,6 @@ nfs4_state_init(void) INIT_LIST_HEAD(&file_hashtbl[i]); } INIT_LIST_HEAD(&close_lru); - INIT_LIST_HEAD(&client_lru); INIT_LIST_HEAD(&del_recall_lru); } @@ -4785,6 +4784,7 @@ static int nfs4_state_start_net(struct net *net) INIT_LIST_HEAD(&nn->sessionid_hashtbl[i]); nn->conf_name_tree = RB_ROOT; nn->unconf_name_tree = RB_ROOT; + INIT_LIST_HEAD(&nn->client_lru); return 0; -- cgit v1.2.3 From 73758fed711b847d833b9b0db59137eaeed06485 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 14 Nov 2012 18:22:01 +0300 Subject: nfsd: make close_lru list per net This list holds nfs4 clients (open) stateowner queue for last close replay, which are network namespace aware. So let's make this list per network namespace too. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 9cf7e9bf369..a8e406449ef 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -401,14 +401,6 @@ static unsigned int clientstr_hashval(const char *name) return opaque_hashval(name, 8) & CLIENT_HASH_MASK; } -/* - * close_lru holds (open) stateowner queue ordered by nfs4_stateowner.so_time - * for last close replay. - * - * All of the above fields are protected by the client_mutex. - */ -static struct list_head close_lru; - /* * We store the NONE, READ, WRITE, and BOTH bits separately in the * st_{access,deny}_bmap field of the stateid, in order to track not @@ -2465,11 +2457,13 @@ static void init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp, } static void -move_to_close_lru(struct nfs4_openowner *oo) +move_to_close_lru(struct nfs4_openowner *oo, struct net *net) { + struct nfsd_net *nn = net_generic(net, nfsd_net_id); + dprintk("NFSD: move_to_close_lru nfs4_openowner %p\n", oo); - list_move_tail(&oo->oo_close_lru, &close_lru); + list_move_tail(&oo->oo_close_lru, &nn->close_lru); oo->oo_time = get_seconds(); } @@ -3242,7 +3236,7 @@ nfs4_laundromat(void) unhash_delegation(dp); } test_val = nfsd4_lease; - list_for_each_safe(pos, next, &close_lru) { + 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)) { u = oo->oo_time - cutoff; @@ -3820,7 +3814,7 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, * little while to handle CLOSE replay. */ if (list_empty(&oo->oo_owner.so_stateids)) - move_to_close_lru(oo); + move_to_close_lru(oo, SVC_NET(rqstp)); } } out: @@ -4721,7 +4715,6 @@ nfs4_state_init(void) for (i = 0; i < FILE_HASH_SIZE; i++) { INIT_LIST_HEAD(&file_hashtbl[i]); } - INIT_LIST_HEAD(&close_lru); INIT_LIST_HEAD(&del_recall_lru); } @@ -4785,6 +4778,7 @@ static int nfs4_state_start_net(struct net *net) nn->conf_name_tree = RB_ROOT; nn->unconf_name_tree = RB_ROOT; INIT_LIST_HEAD(&nn->client_lru); + INIT_LIST_HEAD(&nn->close_lru); return 0; -- cgit v1.2.3 From 3320fef19b542b8df9606bd8e63990dc2a3fb330 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 14 Nov 2012 18:22:07 +0300 Subject: nfsd: use service net instead of hard-coded init_net This patch replaces init_net by SVC_NET(), where possible and also passes proper context to nested functions where required. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 63 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 25 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index a8e406449ef..996a8a58944 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2620,14 +2620,13 @@ static __be32 nfsd4_check_seqid(struct nfsd4_compound_state *cstate, struct nfs4 __be32 nfsd4_process_open1(struct nfsd4_compound_state *cstate, - struct nfsd4_open *open) + struct nfsd4_open *open, struct nfsd_net *nn) { clientid_t *clientid = &open->op_clientid; struct nfs4_client *clp = NULL; unsigned int strhashval; struct nfs4_openowner *oo = NULL; __be32 status; - struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); if (STALE_CLIENTID(&open->op_clientid, nn)) return nfserr_stale_clientid; @@ -3408,10 +3407,11 @@ static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid) return nfs_ok; } -static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask, struct nfs4_stid **s, bool sessions) +static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask, + struct nfs4_stid **s, bool sessions, + struct nfsd_net *nn) { struct nfs4_client *cl; - struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) return nfserr_bad_stateid; @@ -3439,6 +3439,7 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate, struct nfs4_delegation *dp = NULL; struct svc_fh *current_fh = &cstate->current_fh; struct inode *ino = current_fh->fh_dentry->d_inode; + struct nfsd_net *nn = net_generic(net, nfsd_net_id); __be32 status; if (filpp) @@ -3450,7 +3451,8 @@ 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); - status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID, &s, cstate->minorversion); + status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID, + &s, cstate->minorversion, nn); if (status) return status; status = check_stateid_generation(stateid, &s->sc_stateid, nfsd4_has_session(cstate)); @@ -3591,7 +3593,8 @@ static __be32 nfs4_seqid_op_checks(struct nfsd4_compound_state *cstate, stateid_ static __be32 nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, stateid_t *stateid, char typemask, - struct nfs4_ol_stateid **stpp) + struct nfs4_ol_stateid **stpp, + struct nfsd_net *nn) { __be32 status; struct nfs4_stid *s; @@ -3600,7 +3603,8 @@ 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); + status = nfsd4_lookup_stateid(stateid, typemask, &s, + cstate->minorversion, nn); if (status) return status; *stpp = openlockstateid(s); @@ -3609,13 +3613,14 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, return nfs4_seqid_op_checks(cstate, stateid, seqid, *stpp); } -static __be32 nfs4_preprocess_confirmed_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, stateid_t *stateid, struct nfs4_ol_stateid **stpp) +static __be32 nfs4_preprocess_confirmed_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, + stateid_t *stateid, struct nfs4_ol_stateid **stpp, struct nfsd_net *nn) { __be32 status; struct nfs4_openowner *oo; status = nfs4_preprocess_seqid_op(cstate, seqid, stateid, - NFS4_OPEN_STID, stpp); + NFS4_OPEN_STID, stpp, nn); if (status) return status; oo = openowner((*stpp)->st_stateowner); @@ -3631,6 +3636,7 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, __be32 status; struct nfs4_openowner *oo; struct nfs4_ol_stateid *stp; + struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); dprintk("NFSD: nfsd4_open_confirm on file %.*s\n", (int)cstate->current_fh.fh_dentry->d_name.len, @@ -3644,7 +3650,7 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, status = nfs4_preprocess_seqid_op(cstate, oc->oc_seqid, &oc->oc_req_stateid, - NFS4_OPEN_STID, &stp); + NFS4_OPEN_STID, &stp, nn); if (status) goto out; oo = openowner(stp->st_stateowner); @@ -3708,6 +3714,7 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp, { __be32 status; struct nfs4_ol_stateid *stp; + struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); dprintk("NFSD: nfsd4_open_downgrade on file %.*s\n", (int)cstate->current_fh.fh_dentry->d_name.len, @@ -3720,7 +3727,7 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp, nfs4_lock_state(); status = nfs4_preprocess_confirmed_seqid_op(cstate, od->od_seqid, - &od->od_stateid, &stp); + &od->od_stateid, &stp, nn); if (status) goto out; status = nfserr_inval; @@ -3783,6 +3790,8 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, __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); dprintk("NFSD: nfsd4_close on file %.*s\n", (int)cstate->current_fh.fh_dentry->d_name.len, @@ -3792,7 +3801,7 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, status = nfs4_preprocess_seqid_op(cstate, close->cl_seqid, &close->cl_stateid, NFS4_OPEN_STID|NFS4_CLOSED_STID, - &stp); + &stp, nn); if (status) goto out; oo = openowner(stp->st_stateowner); @@ -3831,12 +3840,14 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, stateid_t *stateid = &dr->dr_stateid; struct nfs4_stid *s; __be32 status; + struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0))) return status; nfs4_lock_state(); - status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID, &s, cstate->minorversion); + status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID, &s, + cstate->minorversion, nn); if (status) goto out; dp = delegstateid(s); @@ -4085,7 +4096,8 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, bool new_state = false; int lkflg; int err; - 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); dprintk("NFSD: nfsd4_lock: start=%Ld length=%Ld\n", (long long) lock->lk_offset, @@ -4119,7 +4131,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, status = nfs4_preprocess_confirmed_seqid_op(cstate, lock->lk_new_open_seqid, &lock->lk_new_open_stateid, - &open_stp); + &open_stp, nn); if (status) goto out; open_sop = openowner(open_stp->st_stateowner); @@ -4133,7 +4145,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, status = nfs4_preprocess_seqid_op(cstate, lock->lk_old_lock_seqid, &lock->lk_old_lock_stateid, - NFS4_LOCK_STID, &lock_stp); + NFS4_LOCK_STID, &lock_stp, nn); if (status) goto out; lock_sop = lockowner(lock_stp->st_stateowner); @@ -4144,10 +4156,10 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, goto out; status = nfserr_grace; - if (locks_in_grace(SVC_NET(rqstp)) && !lock->lk_reclaim) + if (locks_in_grace(net) && !lock->lk_reclaim) goto out; status = nfserr_no_grace; - if (!locks_in_grace(SVC_NET(rqstp)) && lock->lk_reclaim) + if (!locks_in_grace(net) && lock->lk_reclaim) goto out; file_lock = locks_alloc_lock(); @@ -4333,7 +4345,8 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct file_lock *file_lock = NULL; __be32 status; int err; - + struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); + dprintk("NFSD: nfsd4_locku: start=%Ld length=%Ld\n", (long long) locku->lu_offset, (long long) locku->lu_length); @@ -4344,7 +4357,8 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, nfs4_lock_state(); status = nfs4_preprocess_seqid_op(cstate, locku->lu_seqid, - &locku->lu_stateid, NFS4_LOCK_STID, &stp); + &locku->lu_stateid, NFS4_LOCK_STID, + &stp, nn); if (status) goto out; filp = find_any_file(stp->st_file); @@ -4564,10 +4578,9 @@ 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) +nfs4_check_open_reclaim(clientid_t *clid, bool sessions, struct nfsd_net *nn) { struct nfs4_client *clp; - struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); /* find clientid in conf_id_hashtbl */ clp = find_confirmed_client(clid, sessions, nn); @@ -4583,7 +4596,7 @@ void nfsd_forget_clients(u64 num) { struct nfs4_client *clp, *next; int count = 0; - struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); + struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, nfsd_net_id); nfs4_lock_state(); list_for_each_entry_safe(clp, next, &nn->client_lru, cl_lru) { @@ -4897,8 +4910,8 @@ __nfs4_state_shutdown(struct net *net) unhash_delegation(dp); } - nfsd4_client_tracking_exit(&init_net); - put_net(&init_net); + nfsd4_client_tracking_exit(net); + put_net(net); } void -- cgit v1.2.3 From 12760c6685624d65f8de078485c21b6a08e83409 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 14 Nov 2012 18:22:12 +0300 Subject: nfsd: pass nfsd_net instead of net to grace enders Passing net context looks as overkill. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 996a8a58944..2e4ed691255 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3157,17 +3157,15 @@ out: } static void -nfsd4_end_grace(struct net *net) +nfsd4_end_grace(struct nfsd_net *nn) { - struct nfsd_net *nn = net_generic(net, nfsd_net_id); - /* do nothing if grace period already ended */ if (nn->grace_ended) return; dprintk("NFSD: end of grace period\n"); nn->grace_ended = true; - nfsd4_record_grace_done(net, nn->boot_time); + nfsd4_record_grace_done(nn, nn->boot_time); locks_end_grace(&nn->nfsd4_manager); /* * Now that every NFSv4 client has had the chance to recover and @@ -3192,7 +3190,7 @@ nfs4_laundromat(void) nfs4_lock_state(); dprintk("NFSD: laundromat service - starting\n"); - nfsd4_end_grace(&init_net); + nfsd4_end_grace(nn); INIT_LIST_HEAD(&reaplist); spin_lock(&client_lock); list_for_each_safe(pos, next, &nn->client_lru) { -- cgit v1.2.3 From 0912128149e86b48ed946371298d7fe61120d627 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 14 Nov 2012 18:22:17 +0300 Subject: nfsd: make laundromat network namespace aware This patch moves laundromat_work to nfsd per-net context, thus allowing to run multiple laundries. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 2e4ed691255..e75872f81e1 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3176,7 +3176,7 @@ nfsd4_end_grace(struct nfsd_net *nn) } static time_t -nfs4_laundromat(void) +nfs4_laundromat(struct nfsd_net *nn) { struct nfs4_client *clp; struct nfs4_openowner *oo; @@ -3185,7 +3185,6 @@ nfs4_laundromat(void) time_t cutoff = get_seconds() - nfsd4_lease; time_t t, clientid_val = nfsd4_lease; time_t u, test_val = nfsd4_lease; - struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); nfs4_lock_state(); @@ -3251,16 +3250,19 @@ nfs4_laundromat(void) static struct workqueue_struct *laundry_wq; static void laundromat_main(struct work_struct *); -static DECLARE_DELAYED_WORK(laundromat_work, laundromat_main); static void -laundromat_main(struct work_struct *not_used) +laundromat_main(struct work_struct *laundry) { time_t t; + struct delayed_work *dwork = container_of(laundry, struct delayed_work, + work); + struct nfsd_net *nn = container_of(dwork, struct nfsd_net, + laundromat_work); - t = nfs4_laundromat(); + t = nfs4_laundromat(nn); dprintk("NFSD: laundromat_main - sleeping for %ld seconds\n", t); - queue_delayed_work(laundry_wq, &laundromat_work, t*HZ); + queue_delayed_work(laundry_wq, &nn->laundromat_work, t*HZ); } static inline __be32 nfs4_check_fh(struct svc_fh *fhp, struct nfs4_ol_stateid *stp) @@ -4791,6 +4793,8 @@ static int nfs4_state_start_net(struct net *net) INIT_LIST_HEAD(&nn->client_lru); INIT_LIST_HEAD(&nn->close_lru); + INIT_DELAYED_WORK(&nn->laundromat_work, laundromat_main); + return 0; err_sessionid: @@ -4875,7 +4879,8 @@ nfs4_state_start(void) ret = nfsd4_create_callback_queue(); if (ret) goto out_free_laundry; - queue_delayed_work(laundry_wq, &laundromat_work, nfsd4_grace * HZ); + + queue_delayed_work(laundry_wq, &nn->laundromat_work, nfsd4_grace * HZ); set_max_delegations(); return 0; out_free_laundry: @@ -4918,7 +4923,7 @@ nfs4_state_shutdown(void) struct net *net = &init_net; struct nfsd_net *nn = net_generic(net, nfsd_net_id); - cancel_delayed_work_sync(&laundromat_work); + cancel_delayed_work_sync(&nn->laundromat_work); destroy_workqueue(laundry_wq); locks_end_grace(&nn->nfsd4_manager); nfs4_lock_state(); -- cgit v1.2.3 From 063b0fb9fadadc0caaea6c8f31e3f6bc978a4904 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sun, 25 Nov 2012 14:48:10 -0500 Subject: nfsd4: downgrade some fs/nfsd/nfs4state.c BUG's Linus has pointed out that indiscriminate use of BUG's can make it harder to diagnose bugs because they can bring a machine down, often before we manage to get any useful debugging information to the logs. (Consider, for example, a BUG() that fires in a workqueue, or while holding a spinlock). Most of these BUG's won't do much more than kill an nfsd thread, but it would still probably be safer to get out the warning without dying. There's still more of this to do in nfsd/. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index e75872f81e1..41d2aed8ed0 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -190,7 +190,7 @@ static struct list_head file_hashtbl[FILE_HASH_SIZE]; static void __nfs4_file_get_access(struct nfs4_file *fp, int oflag) { - BUG_ON(!(fp->fi_fds[oflag] || fp->fi_fds[O_RDWR])); + WARN_ON_ONCE(!(fp->fi_fds[oflag] || fp->fi_fds[O_RDWR])); atomic_inc(&fp->fi_access[oflag]); } @@ -249,7 +249,7 @@ static inline int get_new_stid(struct nfs4_stid *stid) * preallocations that can exist at a time, but the state lock * prevents anyone from using ours before we get here: */ - BUG_ON(error); + WARN_ON_ONCE(error); /* * It shouldn't be a problem to reuse an opaque stateid value. * I don't think it is for 4.1. But with 4.0 I worry that, for @@ -494,7 +494,8 @@ static int nfs4_access_to_omode(u32 access) case NFS4_SHARE_ACCESS_BOTH: return O_RDWR; } - BUG(); + WARN_ON_ONCE(1); + return O_RDONLY; } /* release all access and file references for a given stateid */ @@ -1605,10 +1606,9 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, switch (exid->spa_how) { case SP4_NONE: break; + default: /* checked by xdr code */ + WARN_ON_ONCE(1); case SP4_SSV: - return nfserr_serverfault; - default: - BUG(); /* checked by xdr code */ case SP4_MACH_CRED: return nfserr_serverfault; /* no excuse :-/ */ } @@ -2912,7 +2912,7 @@ static void nfsd4_open_deleg_none_ext(struct nfsd4_open *open, int status) open->op_why_no_deleg = WND4_CANCELLED; break; case NFS4_SHARE_WANT_NO_DELEG: - BUG(); /* not supposed to get here */ + WARN_ON_ONCE(1); } } } @@ -3466,7 +3466,11 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate, goto out; if (filpp) { *filpp = dp->dl_file->fi_deleg_file; - BUG_ON(!*filpp); + if (!*filpp) { + WARN_ON_ONCE(1); + status = nfserr_serverfault; + goto out; + } } break; case NFS4_OPEN_STID: @@ -3693,7 +3697,7 @@ static inline void nfs4_stateid_downgrade(struct nfs4_ol_stateid *stp, u32 to_ac case NFS4_SHARE_ACCESS_BOTH: break; default: - BUG(); + WARN_ON_ONCE(1); } } @@ -3882,7 +3886,7 @@ last_byte_offset(u64 start, u64 len) { u64 end; - BUG_ON(!len); + WARN_ON_ONCE(!len); end = start + len; return end > start ? end - 1: NFS4_MAX_UINT64; } @@ -4552,7 +4556,7 @@ nfs4_release_reclaim(struct nfsd_net *nn) nfs4_remove_reclaim_record(crp, nn); } } - BUG_ON(nn->reclaim_str_hashtbl_size); + WARN_ON_ONCE(nn->reclaim_str_hashtbl_size); } /* -- cgit v1.2.3 From ec28e02ca5f2a4287c19c585f8be2d9b3ba123ea Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 21 Nov 2012 18:07:38 +0300 Subject: nfsd4: remove state lock from nfs4_state_shutdown Protection of __nfs4_state_shutdown() with nfs4_lock_state() looks redundant. This function is called by the last NFSd thread on it's exit and state lock protects actually two functions (del_recall_lru is protected by recall_lock): 1) nfsd4_client_tracking_exit 2) __nfs4_state_shutdown_net "nfsd4_client_tracking_exit" doesn't require state lock protection, because it's state can be modified only by tracker callbacks. Here a re they: 1) create: is called only from nfsd4_proc_compound. 2) remove: is called from either nfsd4_proc_compound or nfs4_laundromat. 3) check: is called only from nfsd4_proc_compound. 4) grace_done; called only from nfs4_laundromat. nfsd4_proc_compound is called onll by NFSd kthread, which is exiting right now. nfs4_laundromat is called by laundry_wq. But laundromat_work was canceled already. "__nfs4_state_shutdown_net" also doesn't require state lock protection, because all NFSd kthreads are dead, and no race can happen with NFSd start, because "nfsd_up" flag is still set. Moreover, all Nfsd shutdown is protected with global nfsd_mutex. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 41d2aed8ed0..ffec73cdfac 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4930,9 +4930,7 @@ nfs4_state_shutdown(void) cancel_delayed_work_sync(&nn->laundromat_work); destroy_workqueue(laundry_wq); locks_end_grace(&nn->nfsd4_manager); - nfs4_lock_state(); __nfs4_state_shutdown(net); - nfs4_unlock_state(); nfsd4_destroy_callback_queue(); } -- cgit v1.2.3 From c9a4962881929df7f1ef6e63e1b9da304faca4dd Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Mon, 26 Nov 2012 15:21:58 +0300 Subject: nfsd: make client_lock per net This lock protects the client lru list and session hash table, which are allocated per network namespace already. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 73 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 30 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index ffec73cdfac..0e7e174de20 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -388,9 +388,6 @@ unhash_delegation(struct nfs4_delegation *dp) * SETCLIENTID state */ -/* client_lock protects the client lru list and session hash table */ -static DEFINE_SPINLOCK(client_lock); - static unsigned int clientid_hashval(u32 id) { return id & CLIENT_HASH_MASK; @@ -872,18 +869,23 @@ static void __free_session(struct nfsd4_session *ses) static void free_session(struct kref *kref) { struct nfsd4_session *ses; + struct nfsd_net *nn; - lockdep_assert_held(&client_lock); ses = container_of(kref, struct nfsd4_session, se_ref); + nn = net_generic(ses->se_client->net, nfsd_net_id); + + lockdep_assert_held(&nn->client_lock); nfsd4_del_conns(ses); __free_session(ses); } void nfsd4_put_session(struct nfsd4_session *ses) { - spin_lock(&client_lock); + struct nfsd_net *nn = net_generic(ses->se_client->net, nfsd_net_id); + + spin_lock(&nn->client_lock); nfsd4_put_session_locked(ses); - spin_unlock(&client_lock); + spin_unlock(&nn->client_lock); } static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fchan) @@ -927,12 +929,12 @@ static void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, stru new->se_cb_sec = cses->cb_sec; kref_init(&new->se_ref); idx = hash_sessionid(&new->se_sessionid); - spin_lock(&client_lock); + 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(&client_lock); + spin_unlock(&nn->client_lock); if (cses->flags & SESSION4_BACK_CHAN) { struct sockaddr *sa = svc_addr(rqstp); @@ -1005,9 +1007,11 @@ renew_client_locked(struct nfs4_client *clp) static inline void renew_client(struct nfs4_client *clp) { - spin_lock(&client_lock); + struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); + + spin_lock(&nn->client_lock); renew_client_locked(clp); - spin_unlock(&client_lock); + spin_unlock(&nn->client_lock); } /* SETCLIENTID and SETCLIENTID_CONFIRM Helper functions */ @@ -1045,7 +1049,9 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name) static inline void free_client(struct nfs4_client *clp) { - lockdep_assert_held(&client_lock); + struct nfsd_net *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, @@ -1062,15 +1068,16 @@ void release_session_client(struct nfsd4_session *session) { struct nfs4_client *clp = session->se_client; + struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); - if (!atomic_dec_and_lock(&clp->cl_refcount, &client_lock)) + if (!atomic_dec_and_lock(&clp->cl_refcount, &nn->client_lock)) return; if (is_client_expired(clp)) { free_client(clp); session->se_client = NULL; } else renew_client_locked(clp); - spin_unlock(&client_lock); + spin_unlock(&nn->client_lock); } /* must be called under the client_lock */ @@ -1119,11 +1126,11 @@ destroy_client(struct nfs4_client *clp) rb_erase(&clp->cl_namenode, &nn->conf_name_tree); else rb_erase(&clp->cl_namenode, &nn->unconf_name_tree); - spin_lock(&client_lock); + spin_lock(&nn->client_lock); unhash_client_locked(clp); if (atomic_read(&clp->cl_refcount) == 0) free_client(clp); - spin_unlock(&client_lock); + spin_unlock(&nn->client_lock); } static void expire_client(struct nfs4_client *clp) @@ -1274,6 +1281,7 @@ 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) @@ -1282,9 +1290,9 @@ static struct nfs4_client *create_client(struct xdr_netobj name, INIT_LIST_HEAD(&clp->cl_sessions); ret = copy_cred(&clp->cl_cred, &rqstp->rq_cred); if (ret) { - spin_lock(&client_lock); + spin_lock(&nn->client_lock); free_client(clp); - spin_unlock(&client_lock); + spin_unlock(&nn->client_lock); return NULL; } idr_init(&clp->cl_stateids); @@ -1873,11 +1881,12 @@ static __be32 nfsd4_map_bcts_dir(u32 *dir) __be32 nfsd4_backchannel_ctl(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_backchannel_ctl *bc) { struct nfsd4_session *session = cstate->session; + struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); - spin_lock(&client_lock); + spin_lock(&nn->client_lock); session->se_cb_prog = bc->bc_cb_program; session->se_cb_sec = bc->bc_cb_sec; - spin_unlock(&client_lock); + spin_unlock(&nn->client_lock); nfsd4_probe_callback(session->se_client); @@ -1890,10 +1899,11 @@ __be32 nfsd4_bind_conn_to_session(struct svc_rqst *rqstp, { __be32 status; struct nfsd4_conn *conn; + struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); if (!nfsd4_last_compound_op(rqstp)) return nfserr_not_only_op; - spin_lock(&client_lock); + spin_lock(&nn->client_lock); cstate->session = find_in_sessionid_hashtbl(&bcts->sessionid, SVC_NET(rqstp)); /* Sorta weird: we only need the refcnt'ing because new_conn acquires * client_lock iself: */ @@ -1901,7 +1911,7 @@ __be32 nfsd4_bind_conn_to_session(struct svc_rqst *rqstp, nfsd4_get_session(cstate->session); atomic_inc(&cstate->session->se_client->cl_refcount); } - spin_unlock(&client_lock); + spin_unlock(&nn->client_lock); if (!cstate->session) return nfserr_badsession; @@ -1929,6 +1939,7 @@ nfsd4_destroy_session(struct svc_rqst *r, { struct nfsd4_session *ses; __be32 status = nfserr_badsession; + struct nfsd_net *nn = net_generic(SVC_NET(r), nfsd_net_id); /* Notes: * - The confirmed nfs4_client->cl_sessionid holds destroyed sessinid @@ -1942,24 +1953,24 @@ nfsd4_destroy_session(struct svc_rqst *r, return nfserr_not_only_op; } dump_sessionid(__func__, &sessionid->sessionid); - spin_lock(&client_lock); + spin_lock(&nn->client_lock); ses = find_in_sessionid_hashtbl(&sessionid->sessionid, SVC_NET(r)); if (!ses) { - spin_unlock(&client_lock); + spin_unlock(&nn->client_lock); goto out; } unhash_session(ses); - spin_unlock(&client_lock); + spin_unlock(&nn->client_lock); nfs4_lock_state(); nfsd4_probe_callback_sync(ses->se_client); nfs4_unlock_state(); - spin_lock(&client_lock); + spin_lock(&nn->client_lock); nfsd4_del_conns(ses); nfsd4_put_session_locked(ses); - spin_unlock(&client_lock); + spin_unlock(&nn->client_lock); status = nfs_ok; out: dprintk("%s returns %d\n", __func__, ntohl(status)); @@ -2025,6 +2036,7 @@ nfsd4_sequence(struct svc_rqst *rqstp, struct nfsd4_slot *slot; struct nfsd4_conn *conn; __be32 status; + struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); if (resp->opcnt != 1) return nfserr_sequence_pos; @@ -2037,7 +2049,7 @@ nfsd4_sequence(struct svc_rqst *rqstp, if (!conn) return nfserr_jukebox; - spin_lock(&client_lock); + spin_lock(&nn->client_lock); status = nfserr_badsession; session = find_in_sessionid_hashtbl(&seq->sessionid, SVC_NET(rqstp)); if (!session) @@ -2113,7 +2125,7 @@ out: } } kfree(conn); - spin_unlock(&client_lock); + spin_unlock(&nn->client_lock); dprintk("%s: return %d\n", __func__, ntohl(status)); return status; } @@ -3191,7 +3203,7 @@ nfs4_laundromat(struct nfsd_net *nn) dprintk("NFSD: laundromat service - starting\n"); nfsd4_end_grace(nn); INIT_LIST_HEAD(&reaplist); - spin_lock(&client_lock); + spin_lock(&nn->client_lock); list_for_each_safe(pos, next, &nn->client_lru) { clp = list_entry(pos, struct nfs4_client, cl_lru); if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) { @@ -3208,7 +3220,7 @@ nfs4_laundromat(struct nfsd_net *nn) unhash_client_locked(clp); list_add(&clp->cl_lru, &reaplist); } - spin_unlock(&client_lock); + 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", @@ -4796,6 +4808,7 @@ static int nfs4_state_start_net(struct net *net) nn->unconf_name_tree = RB_ROOT; INIT_LIST_HEAD(&nn->client_lru); INIT_LIST_HEAD(&nn->close_lru); + spin_lock_init(&nn->client_lock); INIT_DELAYED_WORK(&nn->laundromat_work, laundromat_main); -- cgit v1.2.3 From 4e37a7c2075baa2a15a2ab90fcc44173888016ed Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Mon, 26 Nov 2012 15:22:03 +0300 Subject: nfsd: make delegations shutdown network namespace aware NFSv4 delegations are stored in global list. But they are nfs4_client dependent, which is network namespace aware already. State shutdown and laundromat are done per network namespace as well. So, delegations unhash have to be done in network namespace context. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 0e7e174de20..bc2fc9f076f 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3230,6 +3230,8 @@ nfs4_laundromat(struct nfsd_net *nn) spin_lock(&recall_lock); list_for_each_safe(pos, next, &del_recall_lru) { dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); + if (net_generic(dp->dl_stid.sc_client->net, nfsd_net_id) != nn) + continue; if (time_after((unsigned long)dp->dl_time, (unsigned long)cutoff)) { u = dp->dl_time - cutoff; if (test_val > u) @@ -4922,6 +4924,8 @@ __nfs4_state_shutdown(struct net *net) spin_lock(&recall_lock); list_for_each_safe(pos, next, &del_recall_lru) { dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); + if (dp->dl_stid.sc_client->net != net) + continue; list_move(&dp->dl_recall_lru, &reaplist); } spin_unlock(&recall_lock); -- cgit v1.2.3 From 4dce0ac9069bbebfd34f890f599ccdb92fa76e9f Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Mon, 26 Nov 2012 15:22:08 +0300 Subject: nfsd: cleanup NFSd state shutdown a bit This patch renames __nfs4_state_shutdown_net() into nfs4_state_shutdown_net(), __nfs4_state_shutdown() into nfs4_state_shutdown_net() and moves all network related shutdown operations to nfs4_state_shutdown_net(). Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index bc2fc9f076f..84a27a24b80 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4829,7 +4829,7 @@ err: } static void -__nfs4_state_shutdown_net(struct net *net) +nfs4_state_destroy_net(struct net *net) { int i; struct nfs4_client *clp = NULL; @@ -4857,6 +4857,7 @@ __nfs4_state_shutdown_net(struct net *net) kfree(nn->ownerstr_hashtbl); kfree(nn->unconf_id_hashtbl); kfree(nn->conf_id_hashtbl); + put_net(net); } /* initialization to perform when the nfsd service is started: */ @@ -4906,19 +4907,20 @@ out_free_laundry: destroy_workqueue(laundry_wq); out_recovery: nfsd4_client_tracking_exit(net); - __nfs4_state_shutdown_net(net); - put_net(net); + nfs4_state_destroy_net(net); return ret; } /* should be called with the state lock held */ static void -__nfs4_state_shutdown(struct net *net) +nfs4_state_shutdown_net(struct net *net) { struct nfs4_delegation *dp = NULL; struct list_head *pos, *next, reaplist; + struct nfsd_net *nn = net_generic(net, nfsd_net_id); - __nfs4_state_shutdown_net(net); + cancel_delayed_work_sync(&nn->laundromat_work); + locks_end_grace(&nn->nfsd4_manager); INIT_LIST_HEAD(&reaplist); spin_lock(&recall_lock); @@ -4935,19 +4937,16 @@ __nfs4_state_shutdown(struct net *net) } nfsd4_client_tracking_exit(net); - put_net(net); + nfs4_state_destroy_net(net); } void nfs4_state_shutdown(void) { struct net *net = &init_net; - struct nfsd_net *nn = net_generic(net, nfsd_net_id); - cancel_delayed_work_sync(&nn->laundromat_work); + nfs4_state_shutdown_net(net); destroy_workqueue(laundry_wq); - locks_end_grace(&nn->nfsd4_manager); - __nfs4_state_shutdown(net); nfsd4_destroy_callback_queue(); } -- cgit v1.2.3 From d85ed443052570b25ea4b5f5fa70c57e0129fbc4 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Mon, 26 Nov 2012 15:22:13 +0300 Subject: nfsd: cleanup NFSd state start a bit This patch renames nfs4_state_start_net() into nfs4_state_create_net(), where get_net() now performed. Also it introduces new nfs4_state_start_net(), which is now responsible for state creation and initializing all per-net data and which is now called from nfs4_state_start(). Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 59 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 24 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 84a27a24b80..6f5798623eb 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4770,7 +4770,7 @@ set_max_delegations(void) max_delegations = nr_free_buffer_pages() >> (20 - 2 - PAGE_SHIFT); } -static int nfs4_state_start_net(struct net *net) +static int nfs4_state_create_net(struct net *net) { struct nfsd_net *nn = net_generic(net, nfsd_net_id); int i; @@ -4813,6 +4813,7 @@ static int nfs4_state_start_net(struct net *net) spin_lock_init(&nn->client_lock); INIT_DELAYED_WORK(&nn->laundromat_work, laundromat_main); + get_net(net); return 0; @@ -4860,37 +4861,35 @@ nfs4_state_destroy_net(struct net *net) put_net(net); } -/* initialization to perform when the nfsd service is started: */ - -int -nfs4_state_start(void) +static int +nfs4_state_start_net(struct net *net) { - struct net *net = &init_net; struct nfsd_net *nn = net_generic(net, nfsd_net_id); int ret; - /* - * FIXME: For now, we hang most of the pernet global stuff off of - * init_net until nfsd is fully containerized. Eventually, we'll - * need to pass a net pointer into this function, take a reference - * to that instead and then do most of the rest of this on a per-net - * basis. - */ - get_net(net); - ret = nfs4_state_start_net(net); + ret = nfs4_state_create_net(net); if (ret) return ret; nfsd4_client_tracking_init(net); nn->boot_time = get_seconds(); locks_start_grace(net, &nn->nfsd4_manager); nn->grace_ended = false; - printk(KERN_INFO "NFSD: starting %ld-second grace period\n", - nfsd4_grace); + printk(KERN_INFO "NFSD: starting %ld-second grace period (net %p)\n", + nfsd4_grace, net); + queue_delayed_work(laundry_wq, &nn->laundromat_work, nfsd4_grace * HZ); + return 0; +} + +/* initialization to perform when the nfsd service is started: */ + +int +nfs4_state_start(void) +{ + int ret; + ret = set_callback_cred(); - if (ret) { - ret = -ENOMEM; - goto out_recovery; - } + if (ret) + return -ENOMEM; laundry_wq = create_singlethread_workqueue("nfsd4"); if (laundry_wq == NULL) { ret = -ENOMEM; @@ -4900,14 +4899,26 @@ nfs4_state_start(void) if (ret) goto out_free_laundry; - queue_delayed_work(laundry_wq, &nn->laundromat_work, nfsd4_grace * HZ); set_max_delegations(); + + /* + * FIXME: For now, we hang most of the pernet global stuff off of + * init_net until nfsd is fully containerized. Eventually, we'll + * need to pass a net pointer into this function, take a reference + * to that instead and then do most of the rest of this on a per-net + * basis. + */ + ret = nfs4_state_start_net(&init_net); + if (ret) + goto out_free_callback; + return 0; + +out_free_callback: + nfsd4_destroy_callback_queue(); out_free_laundry: destroy_workqueue(laundry_wq); out_recovery: - nfsd4_client_tracking_exit(net); - nfs4_state_destroy_net(net); return ret; } -- cgit v1.2.3 From f252bc6806a9428f2e3a429e4cdffbd012de9839 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Mon, 26 Nov 2012 15:22:18 +0300 Subject: nfsd: call state init and shutdown twice Split NFSv4 state init and shutdown into two different calls: per-net one and generic one. Per-net cwinit/shutdown pair have to be called for any namespace, generic pair - only once on NSFd kthreads start and shutdown respectively. Refresh of diff-nfsd-call-state-init-twice Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 6f5798623eb..fb98f291aac 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4861,12 +4861,22 @@ nfs4_state_destroy_net(struct net *net) put_net(net); } -static int +int nfs4_state_start_net(struct net *net) { struct nfsd_net *nn = net_generic(net, nfsd_net_id); int ret; + /* + * FIXME: For now, we hang most of the pernet global stuff off of + * init_net until nfsd is fully containerized. Eventually, we'll + * need to pass a net pointer into this function, take a reference + * to that instead and then do most of the rest of this on a per-net + * basis. + */ + if (net != &init_net) + return -EINVAL; + ret = nfs4_state_create_net(net); if (ret) return ret; @@ -4901,21 +4911,8 @@ nfs4_state_start(void) set_max_delegations(); - /* - * FIXME: For now, we hang most of the pernet global stuff off of - * init_net until nfsd is fully containerized. Eventually, we'll - * need to pass a net pointer into this function, take a reference - * to that instead and then do most of the rest of this on a per-net - * basis. - */ - ret = nfs4_state_start_net(&init_net); - if (ret) - goto out_free_callback; - return 0; -out_free_callback: - nfsd4_destroy_callback_queue(); out_free_laundry: destroy_workqueue(laundry_wq); out_recovery: @@ -4923,7 +4920,7 @@ out_recovery: } /* should be called with the state lock held */ -static void +void nfs4_state_shutdown_net(struct net *net) { struct nfs4_delegation *dp = NULL; @@ -4954,9 +4951,6 @@ nfs4_state_shutdown_net(struct net *net) void nfs4_state_shutdown(void) { - struct net *net = &init_net; - - nfs4_state_shutdown_net(net); destroy_workqueue(laundry_wq); nfsd4_destroy_callback_queue(); } -- cgit v1.2.3 From 3d7337115d06f21970e23684f4d2e62e3a44c572 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Tue, 27 Nov 2012 14:11:44 +0300 Subject: nfsd: make NFSv4 lease time per net Lease time is a part of NFSv4 state engine, which is constructed per network namespace. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index fb98f291aac..932b2ca6f20 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -51,7 +51,6 @@ #define NFSDDBG_FACILITY NFSDDBG_PROC /* Globals */ -time_t nfsd4_lease = 90; /* default lease time */ time_t nfsd4_grace = 90; #define all_ones {{~0,~0},~0} @@ -3184,7 +3183,7 @@ nfsd4_end_grace(struct nfsd_net *nn) * to see the (possibly new, possibly shorter) lease time, we * can safely set the next grace time to the current lease time: */ - nfsd4_grace = nfsd4_lease; + nfsd4_grace = nn->nfsd4_lease; } static time_t @@ -3194,9 +3193,9 @@ nfs4_laundromat(struct nfsd_net *nn) struct nfs4_openowner *oo; struct nfs4_delegation *dp; struct list_head *pos, *next, reaplist; - time_t cutoff = get_seconds() - nfsd4_lease; - time_t t, clientid_val = nfsd4_lease; - time_t u, test_val = nfsd4_lease; + time_t cutoff = get_seconds() - nn->nfsd4_lease; + time_t t, clientid_val = nn->nfsd4_lease; + time_t u, test_val = nn->nfsd4_lease; nfs4_lock_state(); @@ -3245,7 +3244,7 @@ nfs4_laundromat(struct nfsd_net *nn) dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); unhash_delegation(dp); } - test_val = nfsd4_lease; + test_val = nn->nfsd4_lease; 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)) { -- cgit v1.2.3 From 5284b44e438580a50e8cc5189297a73a48a45ecb Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Tue, 27 Nov 2012 14:11:49 +0300 Subject: nfsd: make NFSv4 grace time per net Grace time is a part of NFSv4 state engine, which is constructed per network namespace. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 932b2ca6f20..3db7617e6d3 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -50,9 +50,6 @@ #define NFSDDBG_FACILITY NFSDDBG_PROC -/* Globals */ -time_t nfsd4_grace = 90; - #define all_ones {{~0,~0},~0} static const stateid_t one_stateid = { .si_generation = ~0, @@ -3183,7 +3180,7 @@ nfsd4_end_grace(struct nfsd_net *nn) * to see the (possibly new, possibly shorter) lease time, we * can safely set the next grace time to the current lease time: */ - nfsd4_grace = nn->nfsd4_lease; + nn->nfsd4_grace = nn->nfsd4_lease; } static time_t @@ -4884,8 +4881,8 @@ nfs4_state_start_net(struct net *net) locks_start_grace(net, &nn->nfsd4_manager); nn->grace_ended = false; printk(KERN_INFO "NFSD: starting %ld-second grace period (net %p)\n", - nfsd4_grace, net); - queue_delayed_work(laundry_wq, &nn->laundromat_work, nfsd4_grace * HZ); + nn->nfsd4_grace, net); + queue_delayed_work(laundry_wq, &nn->laundromat_work, nn->nfsd4_grace * HZ); return 0; } -- cgit v1.2.3 From f3c7521fe53a7892d8c8c4715f7c0f4add7b2e19 Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Tue, 27 Nov 2012 09:35:10 -0500 Subject: NFSD: Fold fault_inject.h into state.h There were only a small number of functions in this file and since they all affect stored state I think it makes sense to put them in state.h instead. I also dropped most static inline declarations since there are no callers when fault injection is not enabled. Signed-off-by: Bryan Schumaker Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 1 - 1 file changed, 1 deletion(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 3db7617e6d3..b1aa577dd86 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -44,7 +44,6 @@ #include "xdr4.h" #include "vfs.h" #include "current_stateid.h" -#include "fault_inject.h" #include "netns.h" -- cgit v1.2.3 From 043958395a6b91863046b0cd7cae9c67fa845144 Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Thu, 29 Nov 2012 11:40:38 -0500 Subject: NFSD: Lock state before calling fault injection function Each function touches state in some way, so getting the lock earlier can help simplify code. Signed-off-by: Bryan Schumaker Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index b1aa577dd86..8e19c692649 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4611,13 +4611,11 @@ void nfsd_forget_clients(u64 num) int count = 0; struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, nfsd_net_id); - nfs4_lock_state(); list_for_each_entry_safe(clp, next, &nn->client_lru, cl_lru) { expire_client(clp); if (++count == num) break; } - nfs4_unlock_state(); printk(KERN_INFO "NFSD: Forgot %d clients", count); } @@ -4653,25 +4651,15 @@ static int nfsd_release_n_owners(u64 num, bool is_open_owner, void nfsd_forget_locks(u64 num) { - int count; struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); - - nfs4_lock_state(); - count = nfsd_release_n_owners(num, false, release_lockowner_sop, nn); - nfs4_unlock_state(); - + int count = nfsd_release_n_owners(num, false, release_lockowner_sop, nn); printk(KERN_INFO "NFSD: Forgot %d locks", count); } void nfsd_forget_openowners(u64 num) { - int count; struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); - - nfs4_lock_state(); - count = nfsd_release_n_owners(num, true, release_openowner_sop, nn); - nfs4_unlock_state(); - + int count = nfsd_release_n_owners(num, true, release_openowner_sop, nn); printk(KERN_INFO "NFSD: Forgot %d open owners", count); } @@ -4704,10 +4692,8 @@ void nfsd_forget_delegations(u64 num) count = nfsd_process_n_delegations(num, &victims); spin_unlock(&recall_lock); - nfs4_lock_state(); list_for_each_entry_safe(dp, dnext, &victims, dl_recall_lru) unhash_delegation(dp); - nfs4_unlock_state(); printk(KERN_INFO "NFSD: Forgot %d delegations", count); } -- cgit v1.2.3 From 44e34da60b24ca14666534b61cc9579aa4e1eac5 Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Thu, 29 Nov 2012 11:40:39 -0500 Subject: NFSD: Clean up forgetting clients I added in a generic for-each loop that takes a pass over the client_lru list for the current net namespace and calls some function. The next few patches will update other operations to use this function as well. A value of 0 still means "forget everything that is found". Signed-off-by: Bryan Schumaker Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 8e19c692649..2478c8996bd 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4605,19 +4605,34 @@ nfs4_check_open_reclaim(clientid_t *clid, bool sessions, struct nfsd_net *nn) #ifdef CONFIG_NFSD_FAULT_INJECTION -void nfsd_forget_clients(u64 num) +u64 nfsd_forget_client(struct nfs4_client *clp, u64 max) +{ + expire_client(clp); + return 1; +} + +u64 nfsd_for_n_state(u64 max, u64 (*func)(struct nfs4_client *, u64)) { struct nfs4_client *clp, *next; - int count = 0; + u64 count = 0; struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, nfsd_net_id); + if (!nfsd_netns_ready(nn)) + return 0; + list_for_each_entry_safe(clp, next, &nn->client_lru, cl_lru) { - expire_client(clp); - if (++count == num) + count += func(clp, max - count); + if ((max != 0) && (count >= max)) break; } - printk(KERN_INFO "NFSD: Forgot %d clients", count); + return count; +} + +void nfsd_forget_clients(u64 num) +{ + u64 count = nfsd_for_n_state(num, nfsd_forget_client); + printk(KERN_INFO "NFSD: Forgot %llu clients", count); } static void release_lockowner_sop(struct nfs4_stateowner *sop) -- cgit v1.2.3 From fc29171f5b3257694bf508cf4ae51970c97af78c Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Thu, 29 Nov 2012 11:40:40 -0500 Subject: NFSD: Clean up forgetting locks I use the new "forget_n_state()" function to iterate through each client first when searching for locks. This may slow down forgetting locks a little bit, but it implements most of the code needed to forget a specified client's locks. Signed-off-by: Bryan Schumaker Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 2478c8996bd..46bece40b1c 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4611,6 +4611,32 @@ u64 nfsd_forget_client(struct nfs4_client *clp, u64 max) return 1; } +static u64 nfsd_foreach_client_lock(struct nfs4_client *clp, u64 max, void (*func)(struct nfs4_lockowner *)) +{ + struct nfs4_openowner *oop; + struct nfs4_lockowner *lop, *lo_next; + struct nfs4_ol_stateid *stp, *st_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) { + if (func) + func(lop); + if (++count == max) + return count; + } + } + } + + return count; +} + +u64 nfsd_forget_client_locks(struct nfs4_client *clp, u64 max) +{ + return nfsd_foreach_client_lock(clp, max, release_lockowner); +} + u64 nfsd_for_n_state(u64 max, u64 (*func)(struct nfs4_client *, u64)) { struct nfs4_client *clp, *next; @@ -4635,11 +4661,6 @@ void nfsd_forget_clients(u64 num) printk(KERN_INFO "NFSD: Forgot %llu clients", count); } -static void release_lockowner_sop(struct nfs4_stateowner *sop) -{ - release_lockowner(lockowner(sop)); -} - static void release_openowner_sop(struct nfs4_stateowner *sop) { release_openowner(openowner(sop)); @@ -4666,9 +4687,8 @@ static int nfsd_release_n_owners(u64 num, bool is_open_owner, void nfsd_forget_locks(u64 num) { - struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); - int count = nfsd_release_n_owners(num, false, release_lockowner_sop, nn); - printk(KERN_INFO "NFSD: Forgot %d locks", count); + u64 count = nfsd_for_n_state(num, nfsd_forget_client_locks); + printk(KERN_INFO "NFSD: Forgot %llu locks", count); } void nfsd_forget_openowners(u64 num) -- cgit v1.2.3 From 4dbdbda84f963312e0b5dfdf2dfbf64de047dd44 Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Thu, 29 Nov 2012 11:40:41 -0500 Subject: NFSD: Clean up forgetting openowners Using "forget_n_state()" forces me to implement the code needed to forget a specific client's openowners. Signed-off-by: Bryan Schumaker Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 49 ++++++++++++++++++++++--------------------------- 1 file changed, 22 insertions(+), 27 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 46bece40b1c..00d4398e232 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4637,6 +4637,26 @@ u64 nfsd_forget_client_locks(struct nfs4_client *clp, u64 max) return nfsd_foreach_client_lock(clp, max, release_lockowner); } +static u64 nfsd_foreach_client_open(struct nfs4_client *clp, u64 max, void (*func)(struct nfs4_openowner *)) +{ + struct nfs4_openowner *oop, *next; + u64 count = 0; + + list_for_each_entry_safe(oop, next, &clp->cl_openowners, oo_perclient) { + if (func) + func(oop); + if (++count == max) + break; + } + + return count; +} + +u64 nfsd_forget_client_openowners(struct nfs4_client *clp, u64 max) +{ + return nfsd_foreach_client_open(clp, max, release_openowner); +} + u64 nfsd_for_n_state(u64 max, u64 (*func)(struct nfs4_client *, u64)) { struct nfs4_client *clp, *next; @@ -4661,30 +4681,6 @@ void nfsd_forget_clients(u64 num) printk(KERN_INFO "NFSD: Forgot %llu clients", count); } -static void release_openowner_sop(struct nfs4_stateowner *sop) -{ - release_openowner(openowner(sop)); -} - -static int nfsd_release_n_owners(u64 num, bool is_open_owner, - void (*release_sop)(struct nfs4_stateowner *), - struct nfsd_net *nn) -{ - int i, count = 0; - struct nfs4_stateowner *sop, *next; - - for (i = 0; i < OWNER_HASH_SIZE; i++) { - list_for_each_entry_safe(sop, next, &nn->ownerstr_hashtbl[i], so_strhash) { - if (sop->so_is_open_owner != is_open_owner) - continue; - release_sop(sop); - if (++count == num) - return count; - } - } - return count; -} - void nfsd_forget_locks(u64 num) { u64 count = nfsd_for_n_state(num, nfsd_forget_client_locks); @@ -4693,9 +4689,8 @@ void nfsd_forget_locks(u64 num) void nfsd_forget_openowners(u64 num) { - struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); - int count = nfsd_release_n_owners(num, true, release_openowner_sop, nn); - printk(KERN_INFO "NFSD: Forgot %d open owners", count); + u64 count = nfsd_for_n_state(num, nfsd_forget_client_openowners); + printk(KERN_INFO "NFSD: Forgot %llu open owners", count); } static int nfsd_process_n_delegations(u64 num, struct list_head *list) -- cgit v1.2.3 From 269de30f10604710dde8d544748b5b6c748b7de8 Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Thu, 29 Nov 2012 11:40:42 -0500 Subject: NFSD: Clean up forgetting and recalling delegations Once I have a client, I can easily use its delegation list rather than searching the file hash table for delegations to remove. Signed-off-by: Bryan Schumaker Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 94 ++++++++++++++++++++++++++++------------------------- 1 file changed, 50 insertions(+), 44 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 00d4398e232..dc7c22f14fe 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4657,6 +4657,52 @@ u64 nfsd_forget_client_openowners(struct nfs4_client *clp, u64 max) return nfsd_foreach_client_open(clp, max, release_openowner); } +static u64 nfsd_find_all_delegations(struct nfs4_client *clp, u64 max, + struct list_head *victims) +{ + struct nfs4_delegation *dp, *next; + u64 count = 0; + + list_for_each_entry_safe(dp, next, &clp->cl_delegations, dl_perclnt) { + if (victims) + list_move(&dp->dl_recall_lru, victims); + if (++count == max) + break; + } + return count; +} + +u64 nfsd_forget_client_delegations(struct nfs4_client *clp, u64 max) +{ + struct nfs4_delegation *dp, *next; + LIST_HEAD(victims); + u64 count; + + spin_lock(&recall_lock); + count = nfsd_find_all_delegations(clp, max, &victims); + spin_unlock(&recall_lock); + + list_for_each_entry_safe(dp, next, &victims, dl_recall_lru) + unhash_delegation(dp); + + return count; +} + +u64 nfsd_recall_client_delegations(struct nfs4_client *clp, u64 max) +{ + struct nfs4_delegation *dp, *next; + LIST_HEAD(victims); + u64 count; + + spin_lock(&recall_lock); + count = nfsd_find_all_delegations(clp, max, &victims); + list_for_each_entry_safe(dp, next, &victims, dl_recall_lru) + nfsd_break_one_deleg(dp); + spin_unlock(&recall_lock); + + return count; +} + u64 nfsd_for_n_state(u64 max, u64 (*func)(struct nfs4_client *, u64)) { struct nfs4_client *clp, *next; @@ -4693,56 +4739,16 @@ void nfsd_forget_openowners(u64 num) printk(KERN_INFO "NFSD: Forgot %llu open owners", count); } -static int nfsd_process_n_delegations(u64 num, struct list_head *list) -{ - int i, count = 0; - struct nfs4_file *fp, *fnext; - struct nfs4_delegation *dp, *dnext; - - for (i = 0; i < FILE_HASH_SIZE; i++) { - list_for_each_entry_safe(fp, fnext, &file_hashtbl[i], fi_hash) { - list_for_each_entry_safe(dp, dnext, &fp->fi_delegations, dl_perfile) { - list_move(&dp->dl_recall_lru, list); - if (++count == num) - return count; - } - } - } - - return count; -} - void nfsd_forget_delegations(u64 num) { - unsigned int count; - LIST_HEAD(victims); - struct nfs4_delegation *dp, *dnext; - - spin_lock(&recall_lock); - count = nfsd_process_n_delegations(num, &victims); - spin_unlock(&recall_lock); - - list_for_each_entry_safe(dp, dnext, &victims, dl_recall_lru) - unhash_delegation(dp); - - printk(KERN_INFO "NFSD: Forgot %d delegations", count); + u64 count = nfsd_for_n_state(num, nfsd_forget_client_delegations); + printk(KERN_INFO "NFSD: Forgot %llu delegations", count); } void nfsd_recall_delegations(u64 num) { - unsigned int count; - LIST_HEAD(victims); - struct nfs4_delegation *dp, *dnext; - - spin_lock(&recall_lock); - count = nfsd_process_n_delegations(num, &victims); - list_for_each_entry_safe(dp, dnext, &victims, dl_recall_lru) { - list_del(&dp->dl_recall_lru); - nfsd_break_one_deleg(dp); - } - spin_unlock(&recall_lock); - - printk(KERN_INFO "NFSD: Recalled %d delegations", count); + u64 count = nfsd_for_n_state(num, nfsd_recall_client_delegations); + printk(KERN_INFO "NFSD: Recalled %llu delegations", count); } #endif /* CONFIG_NFSD_FAULT_INJECTION */ -- cgit v1.2.3 From 8ce54e0d82730ece61737c9fd7b61b28ab8c3390 Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Thu, 29 Nov 2012 11:40:43 -0500 Subject: NFSD: Fault injection operations take a per-client forget function The eventual goal is to forget state based on ip address, so it makes sense to call this function in a for-each-client loop until the correct amount of state is forgotten. I also use this patch as an opportunity to rename the forget function from "func()" to "forget()". Signed-off-by: Bryan Schumaker Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 30 ------------------------------ 1 file changed, 30 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index dc7c22f14fe..ab45cdd7b3d 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4721,36 +4721,6 @@ u64 nfsd_for_n_state(u64 max, u64 (*func)(struct nfs4_client *, u64)) return count; } -void nfsd_forget_clients(u64 num) -{ - u64 count = nfsd_for_n_state(num, nfsd_forget_client); - printk(KERN_INFO "NFSD: Forgot %llu clients", count); -} - -void nfsd_forget_locks(u64 num) -{ - u64 count = nfsd_for_n_state(num, nfsd_forget_client_locks); - printk(KERN_INFO "NFSD: Forgot %llu locks", count); -} - -void nfsd_forget_openowners(u64 num) -{ - u64 count = nfsd_for_n_state(num, nfsd_forget_client_openowners); - printk(KERN_INFO "NFSD: Forgot %llu open owners", count); -} - -void nfsd_forget_delegations(u64 num) -{ - u64 count = nfsd_for_n_state(num, nfsd_forget_client_delegations); - printk(KERN_INFO "NFSD: Forgot %llu delegations", count); -} - -void nfsd_recall_delegations(u64 num) -{ - u64 count = nfsd_for_n_state(num, nfsd_recall_client_delegations); - printk(KERN_INFO "NFSD: Recalled %llu delegations", count); -} - #endif /* CONFIG_NFSD_FAULT_INJECTION */ /* initialization to perform at module load time: */ -- cgit v1.2.3 From 184c18471f7d0963ad5752692c4b441a546d88f1 Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Thu, 29 Nov 2012 11:40:44 -0500 Subject: NFSD: Reading a fault injection file prints a state count I also log basic information that I can figure out about the type of state (such as number of locks for each client IP address). This can be useful for checking that state was actually dropped and later for checking if the client was able to recover. Signed-off-by: Bryan Schumaker Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index ab45cdd7b3d..9fb8e52580f 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4611,6 +4611,22 @@ 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, 129); + 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) +{ + char buf[INET6_ADDRSTRLEN]; + rpc_ntop((struct sockaddr *)&clp->cl_addr, buf, 129); + 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 *)) { struct nfs4_openowner *oop; @@ -4637,6 +4653,13 @@ u64 nfsd_forget_client_locks(struct nfs4_client *clp, u64 max) return nfsd_foreach_client_lock(clp, max, release_lockowner); } +u64 nfsd_print_client_locks(struct nfs4_client *clp, u64 max) +{ + u64 count = nfsd_foreach_client_lock(clp, max, NULL); + nfsd_print_count(clp, count, "locked files"); + return count; +} + static u64 nfsd_foreach_client_open(struct nfs4_client *clp, u64 max, void (*func)(struct nfs4_openowner *)) { struct nfs4_openowner *oop, *next; @@ -4657,6 +4680,13 @@ u64 nfsd_forget_client_openowners(struct nfs4_client *clp, u64 max) return nfsd_foreach_client_open(clp, max, release_openowner); } +u64 nfsd_print_client_openowners(struct nfs4_client *clp, u64 max) +{ + u64 count = nfsd_foreach_client_open(clp, max, NULL); + nfsd_print_count(clp, count, "open files"); + return count; +} + static u64 nfsd_find_all_delegations(struct nfs4_client *clp, u64 max, struct list_head *victims) { @@ -4703,6 +4733,18 @@ u64 nfsd_recall_client_delegations(struct nfs4_client *clp, u64 max) return count; } +u64 nfsd_print_client_delegations(struct nfs4_client *clp, u64 max) +{ + u64 count = 0; + + spin_lock(&recall_lock); + count = nfsd_find_all_delegations(clp, max, NULL); + spin_unlock(&recall_lock); + + nfsd_print_count(clp, count, "delegations"); + return count; +} + u64 nfsd_for_n_state(u64 max, u64 (*func)(struct nfs4_client *, u64)) { struct nfs4_client *clp, *next; -- cgit v1.2.3 From 6c1e82a4b74ad0c8b45c833a4409f153199d9be4 Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Thu, 29 Nov 2012 11:40:46 -0500 Subject: NFSD: Forget state for a specific client Write the client's ip address to any state file and all appropriate state for that client will be forgotten. Signed-off-by: Bryan Schumaker Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 9fb8e52580f..eff73403343 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4763,6 +4763,21 @@ u64 nfsd_for_n_state(u64 max, u64 (*func)(struct nfs4_client *, u64)) return count; } +struct nfs4_client *nfsd_find_client(struct sockaddr_storage *addr, size_t addr_size) +{ + struct nfs4_client *clp; + struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, nfsd_net_id); + + if (!nfsd_netns_ready(nn)) + return NULL; + + list_for_each_entry(clp, &nn->client_lru, cl_lru) { + if (memcmp(&clp->cl_addr, addr, addr_size) == 0) + return clp; + } + return NULL; +} + #endif /* CONFIG_NFSD_FAULT_INJECTION */ /* initialization to perform at module load time: */ -- cgit v1.2.3 From 9b2ef62b1541f176ea1b1f6e13b16df14bb16e99 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 3 Dec 2012 17:24:41 -0500 Subject: nfsd4: lockt, release_lockowner should renew clients Fix nfsd4_lockt and release_lockowner to lookup the referenced client, so that it can renew it, or correctly return "expired", as appropriate. Also share some code while we're here. Reported-by: Frank Filz Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index eff73403343..16e954c1c91 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3132,6 +3132,18 @@ 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) @@ -3143,16 +3155,9 @@ 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 = nfserr_stale_clientid; - if (STALE_CLIENTID(clid, nn)) - goto out; - clp = find_confirmed_client(clid, cstate->minorversion, nn); - status = nfserr_expired; - if (clp == NULL) { - /* We assume the client took too long to RENEW. */ - dprintk("nfsd4_renew: clientid not found!\n"); + status = lookup_clientid(clid, cstate->minorversion, nn, &clp); + if (status) goto out; - } status = nfserr_cb_path_down; if (!list_empty(&clp->cl_delegations) && clp->cl_cb_state != NFSD4_CB_UP) @@ -4293,9 +4298,11 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, nfs4_lock_state(); - status = nfserr_stale_clientid; - if (!nfsd4_has_session(cstate) && STALE_CLIENTID(&lockt->lt_clientid, nn)) - goto out; + if (!nfsd4_has_session(cstate)) { + status = lookup_clientid(&lockt->lt_clientid, false, nn, NULL); + if (status) + goto out; + } if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0))) goto out; @@ -4466,14 +4473,12 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n", clid->cl_boot, clid->cl_id); - /* XXX check for lease expiration */ - - status = nfserr_stale_clientid; - if (STALE_CLIENTID(clid, nn)) - return status; - nfs4_lock_state(); + status = lookup_clientid(clid, cstate->minorversion, nn, NULL); + if (status) + goto out; + status = nfserr_locks_held; INIT_LIST_HEAD(&matches); -- cgit v1.2.3 From 9dd9845f084cda07ce00cca32a5ba8fbcbbfbcaf Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Thu, 6 Dec 2012 14:23:24 +0300 Subject: nfsd: make NFSd service structure allocated per net This patch makes main step in NFSd containerisation. There could be different approaches to how to make NFSd able to handle incoming RPC request from different network namespaces. The two main options are: 1) Share NFSd kthreads betwween all network namespaces. 2) Create separated pool of threads for each namespace. While first approach looks more flexible, second one is simpler and non-racy. This patch implements the second option. To make it possible to allocate separate pools of threads, we have to make it possible to allocate separate NFSd service structures per net. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 16e954c1c91..3d27f08e229 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -743,9 +743,12 @@ out_free: return NULL; } -static void init_forechannel_attrs(struct nfsd4_channel_attrs *new, struct nfsd4_channel_attrs *req, int numslots, int slotsize) +static void init_forechannel_attrs(struct nfsd4_channel_attrs *new, + struct nfsd4_channel_attrs *req, + int numslots, int slotsize, + struct nfsd_net *nn) { - u32 maxrpc = nfsd_serv->sv_max_mesg; + u32 maxrpc = nn->nfsd_serv->sv_max_mesg; new->maxreqs = numslots; new->maxresp_cached = min_t(u32, req->maxresp_cached, @@ -883,7 +886,8 @@ void nfsd4_put_session(struct nfsd4_session *ses) spin_unlock(&nn->client_lock); } -static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fchan) +static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fchan, + struct nfsd_net *nn) { struct nfsd4_session *new; int numslots, slotsize; @@ -904,7 +908,7 @@ static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fchan) nfsd4_put_drc_mem(slotsize, fchan->maxreqs); return NULL; } - init_forechannel_attrs(&new->se_fchannel, fchan, numslots, slotsize); + init_forechannel_attrs(&new->se_fchannel, fchan, numslots, slotsize, nn); return new; } @@ -1776,7 +1780,7 @@ nfsd4_create_session(struct svc_rqst *rqstp, return nfserr_inval; if (check_forechannel_attrs(cr_ses->fore_channel)) return nfserr_toosmall; - new = alloc_session(&cr_ses->fore_channel); + new = alloc_session(&cr_ses->fore_channel, nn); if (!new) return nfserr_jukebox; status = nfserr_jukebox; -- cgit v1.2.3 From 0a5c33e23c4d781ecc815002c54f1f91012c703d Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Fri, 7 Dec 2012 16:17:28 -0500 Subject: NFSD: Pass correct buffer size to rpc_ntop I honestly have no idea where I got 129 from, but it's a much bigger value than the actual buffer size (INET6_ADDRSTRLEN). Signed-off-by: Bryan Schumaker Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 3d27f08e229..8e255511296 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4623,7 +4623,7 @@ u64 nfsd_forget_client(struct nfs4_client *clp, u64 max) u64 nfsd_print_client(struct nfs4_client *clp, u64 num) { char buf[INET6_ADDRSTRLEN]; - rpc_ntop((struct sockaddr *)&clp->cl_addr, buf, 129); + rpc_ntop((struct sockaddr *)&clp->cl_addr, buf, sizeof(buf)); printk(KERN_INFO "NFS Client: %s\n", buf); return 1; } @@ -4632,7 +4632,7 @@ static void nfsd_print_count(struct nfs4_client *clp, unsigned int count, const char *type) { char buf[INET6_ADDRSTRLEN]; - rpc_ntop((struct sockaddr *)&clp->cl_addr, buf, 129); + rpc_ntop((struct sockaddr *)&clp->cl_addr, buf, sizeof(buf)); printk(KERN_INFO "NFS Client: %s has %u %s\n", buf, count, type); } -- cgit v1.2.3 From 9b3234b9220aae5387b60bc35a424ab6748b2b59 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 4 Dec 2012 18:03:46 -0500 Subject: nfsd4: disable zero-copy on non-final read ops To ensure ordering of read data with any following operations, turn off zero copy if the read is not the final operation in the compound. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 8e255511296..8e127b39d32 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1855,14 +1855,6 @@ out_free_session: goto out; } -static bool nfsd4_last_compound_op(struct svc_rqst *rqstp) -{ - struct nfsd4_compoundres *resp = rqstp->rq_resp; - struct nfsd4_compoundargs *argp = rqstp->rq_argp; - - return argp->opcnt == resp->opcnt; -} - static __be32 nfsd4_map_bcts_dir(u32 *dir) { switch (*dir) { -- cgit v1.2.3 From 24ffb93872f7363a01ad639e3c8a9889b46c3f0a Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 12 Dec 2012 15:24:12 -0500 Subject: nfsd4: don't leave freed stateid hashed Note the stateid is hashed early on in init_stid(), but isn't currently being unhashed on error paths. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs/nfsd/nfs4state.c') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 8e127b39d32..ac8ed96c419 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2983,6 +2983,7 @@ out: } return; out_free: + unhash_stid(&dp->dl_stid); nfs4_put_delegation(dp); out_no_deleg: flag = NFS4_OPEN_DELEGATE_NONE; -- cgit v1.2.3