diff options
Diffstat (limited to 'fs/nfsd/nfs4state.c')
| -rw-r--r-- | fs/nfsd/nfs4state.c | 1309 | 
1 files changed, 762 insertions, 547 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index cc894eda385..ac8ed96c419 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -44,16 +44,11 @@  #include "xdr4.h"  #include "vfs.h"  #include "current_stateid.h" -#include "fault_inject.h"  #include "netns.h"  #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}  static const stateid_t one_stateid = {  	.si_generation = ~0, @@ -176,8 +171,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) @@ -192,7 +185,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]);  } @@ -251,7 +244,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 @@ -340,7 +333,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;  } @@ -390,14 +383,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); - -/* 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,31 +394,6 @@ 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 - * setclientid_confirmed info.  - * - * unconf_str_hastbl[] and unconf_id_hashtbl[] hold unconfirmed  - * setclientid info. - * - * 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. - */ -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 list_head client_lru; -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   * only what share bits are currently in force, but also what @@ -526,7 +486,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 */ @@ -652,9 +613,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)  { @@ -758,7 +716,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; @@ -785,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, @@ -852,35 +813,28 @@ 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)  { -	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)  		/* 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) { +	if (conn->cn_flags & NFS4_CDFC4_BACK) {  		/* 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) +static struct nfsd4_conn *alloc_conn_from_crses(struct svc_rqst *rqstp, struct nfsd4_create_session *cses)  {  	u32 dir = NFS4_CDFC4_FORE; -	if (ses->se_flags & SESSION4_BACK_CHAN) +	if (cses->flags & SESSION4_BACK_CHAN)  		dir |= NFS4_CDFC4_BACK; - -	return nfsd4_new_conn(rqstp, ses, dir); +	return alloc_conn(rqstp, dir);  }  /* must be called under client_lock */ @@ -903,37 +857,40 @@ 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; +	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); -	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)  { -	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_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 nfsd_net *nn)  {  	struct nfsd4_session *new; -	struct nfsd4_channel_attrs *fchan = &cses->fore_channel;  	int numslots, slotsize; -	__be32 status; -	int idx; -  	/*  	 * Note decreasing slot size below client's request may  	 * make it difficult for client to function correctly, whereas @@ -946,12 +903,19 @@ 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); +	init_forechannel_attrs(&new->se_fchannel, fchan, numslots, slotsize, nn); +	return new; +} + +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); @@ -961,23 +925,16 @@ static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct n  	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); -	list_add(&new->se_hash, &sessionid_hashtbl[idx]); +	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); -	status = nfsd4_new_conn_from_crses(rqstp, new); -	/* whoops: benny points out, status is ignored! (err, or bogus) */ -	if (status) { -		spin_lock(&client_lock); -		free_session(&new->se_ref); -		spin_unlock(&client_lock); -		return NULL; -	}  	if (cses->flags & SESSION4_BACK_CHAN) {  		struct sockaddr *sa = svc_addr(rqstp);  		/* @@ -990,21 +947,20 @@ static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct n  		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;  }  /* 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; @@ -1029,6 +985,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", @@ -1041,16 +999,18 @@ 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();  }  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 */ @@ -1088,7 +1048,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, @@ -1105,15 +1067,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 */ @@ -1131,11 +1094,12 @@ 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;  	struct list_head reaplist; +	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);  	INIT_LIST_HEAD(&reaplist);  	spin_lock(&recall_lock); @@ -1157,12 +1121,21 @@ expire_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); -	spin_lock(&client_lock); +	if (test_bit(NFSD4_CLIENT_CONFIRMED, &clp->cl_flags)) +		rb_erase(&clp->cl_namenode, &nn->conf_name_tree); +	else +		rb_erase(&clp->cl_namenode, &nn->unconf_name_tree); +	spin_lock(&nn->client_lock);  	unhash_client_locked(clp);  	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) +{ +	nfsd4_client_record_remove(clp); +	destroy_client(clp);  }  static void copy_verf(struct nfs4_client *target, nfs4_verifier *source) @@ -1194,6 +1167,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); @@ -1223,10 +1207,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)) @@ -1238,10 +1238,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++;  @@ -1274,12 +1273,14 @@ 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;  	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) @@ -1288,23 +1289,21 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir,  	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); -	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); -	INIT_LIST_HEAD(&clp->cl_strhash);  	INIT_LIST_HEAD(&clp->cl_openowners);  	INIT_LIST_HEAD(&clp->cl_delegations);  	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"); @@ -1312,17 +1311,60 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir,  	rpc_copy_addr((struct sockaddr *) &clp->cl_addr, sa);  	gen_confirm(clp);  	clp->cl_cb_session = NULL; +	clp->net = net;  	return clp;  }  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; +	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); -	list_add(&clp->cl_strhash, &unconf_str_hashtbl[strhashval]); +	clear_bit(NFSD4_CLIENT_CONFIRMED, &clp->cl_flags); +	add_clp_to_name_tree(clp, &nn->unconf_name_tree);  	idhashval = clientid_hashval(clp->cl_clientid.cl_id); -	list_add(&clp->cl_idhash, &unconf_id_hashtbl[idhashval]); +	list_add(&clp->cl_idhash, &nn->unconf_id_hashtbl[idhashval]);  	renew_client(clp);  } @@ -1330,23 +1372,26 @@ static void  move_to_confirmed(struct nfs4_client *clp)  {  	unsigned int idhashval = clientid_hashval(clp->cl_clientid.cl_id); -	unsigned int strhashval; +	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]); -	strhashval = clientstr_hashval(clp->cl_recdir); -	list_move(&clp->cl_strhash, &conf_str_hashtbl[strhashval]); +	list_move(&clp->cl_idhash, &nn->conf_id_hashtbl[idhashval]); +	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);  }  static struct nfs4_client * -find_confirmed_client(clientid_t *clid) +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;  			renew_client(clp);  			return clp;  		} @@ -1355,14 +1400,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 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) { -		if (same_clid(&clp->cl_clientid, clid)) +	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;  			return clp; +		}  	}  	return NULL;  } @@ -1373,27 +1421,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 nfsd_net *nn)  { -	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, &nn->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 nfsd_net *nn)  { -	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, &nn->unconf_name_tree);  }  static void @@ -1414,7 +1450,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)); @@ -1558,12 +1594,11 @@ 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;  	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 " @@ -1578,24 +1613,16 @@ 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 :-/ */  	} -	status = nfs4_make_rec_clidname(dname, &exid->clname); - -	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, nn);  	if (conf) {  		bool creds_match = same_creds(&conf->cl_cred, &rqstp->rq_cred);  		bool verfs_match = same_verf(&verf, &conf->cl_verifier); @@ -1640,20 +1667,21 @@ 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, nn);  	if (unconf) /* case 4, possible retry or client restart */  		expire_client(unconf);  	/* 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;  	} +	new->cl_minorversion = 1; -	gen_clid(new); -	add_to_unconfirmed(new, strhashval); +	gen_clid(new, nn); +	add_to_unconfirmed(new);  out_copy:  	exid->clientid.cl_boot = new->cl_clientid.cl_boot;  	exid->clientid.cl_id = new->cl_clientid.cl_id; @@ -1743,67 +1771,70 @@ 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; +	struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);  	if (cr_ses->flags & ~SESSION4_FLAG_MASK_A)  		return nfserr_inval; +	if (check_forechannel_attrs(cr_ses->fore_channel)) +		return nfserr_toosmall; +	new = alloc_session(&cr_ses->fore_channel, nn); +	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); -	conf = find_confirmed_client(&cr_ses->clientid); +	unconf = find_unconfirmed_client(&cr_ses->clientid, true, nn); +	conf = find_confirmed_client(&cr_ses->clientid, true, nn);  	if (conf) {  		cs_slot = &conf->cl_cs_slot;  		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) { +		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; -			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; +		old = find_confirmed_client_by_name(&unconf->cl_name, nn); +		if (old) +			expire_client(old); +		move_to_confirmed(unconf);  		conf = unconf;  	} else {  		status = nfserr_stale_clientid; -		goto out; +		goto out_free_conn;  	} - -	/* -	 * XXX: we should probably set this at creation time, and check -	 * for consistent minorversion use throughout: -	 */ -	conf->cl_minorversion = 1; +	status = nfs_ok;  	/*  	 * We do not support RDMA or persistent sessions  	 */  	cr_ses->flags &= ~SESSION4_PERSIST;  	cr_ses->flags &= ~SESSION4_RDMA; -	status = nfserr_toosmall; -	if (check_forechannel_attrs(cr_ses->fore_channel)) -		goto out; +	init_session(rqstp, new, conf, cr_ses); +	nfsd4_init_conn(rqstp, conn, new); -	status = nfserr_jukebox; -	new = alloc_init_session(rqstp, conf, cr_ses); -	if (!new) -		goto out; -	status = nfs_ok;  	memcpy(cr_ses->sessionid.data, new->se_sessionid.data,  	       NFS4_MAX_SESSIONID_LEN);  	memcpy(&cr_ses->fore_channel, &new->se_fchannel, @@ -1813,26 +1844,15 @@ 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));  	return status; -} - -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; +out_free_conn: +	free_conn(conn); +out_free_session: +	__free_session(new); +	goto out;  }  static __be32 nfsd4_map_bcts_dir(u32 *dir) @@ -1849,30 +1869,51 @@ 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; +	struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); + +	spin_lock(&nn->client_lock); +	session->se_cb_prog = bc->bc_cb_program; +	session->se_cb_sec = bc->bc_cb_sec; +	spin_unlock(&nn->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)  {  	__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); -	cstate->session = find_in_sessionid_hashtbl(&bcts->sessionid); +	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: */  	if (cstate->session) {  		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;  	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); +	return nfs_ok;  }  static bool nfsd4_compound_in_session(struct nfsd4_session *session, struct nfs4_sessionid *sid) @@ -1889,6 +1930,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 @@ -1902,24 +1944,24 @@ nfsd4_destroy_session(struct svc_rqst *r,  			return nfserr_not_only_op;  	}  	dump_sessionid(__func__, &sessionid->sessionid); -	spin_lock(&client_lock); -	ses = find_in_sessionid_hashtbl(&sessionid->sessionid); +	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)); @@ -1985,6 +2027,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; @@ -1997,9 +2040,9 @@ 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); +	session = find_in_sessionid_hashtbl(&seq->sessionid, SVC_NET(rqstp));  	if (!session)  		goto out; @@ -2073,7 +2116,7 @@ out:  		}  	}  	kfree(conn); -	spin_unlock(&client_lock); +	spin_unlock(&nn->client_lock);  	dprintk("%s: return %d\n", __func__, ntohl(status));  	return status;  } @@ -2083,10 +2126,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); -	conf = find_confirmed_client(&dc->clientid); +	unconf = find_unconfirmed_client(&dc->clientid, true, nn); +	conf = find_confirmed_client(&dc->clientid, true, nn);  	if (conf) {  		clp = conf; @@ -2160,20 +2204,13 @@ 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]; -	 -	status = nfs4_make_rec_clidname(dname, &clname); -	if (status) -		return status; - -	strhashval = clientstr_hashval(dname); +	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_str(dname, strhashval); +	conf = find_confirmed_client_by_name(&clname, nn);  	if (conf) {  		/* case 0: */  		status = nfserr_clid_inuse; @@ -2188,25 +2225,21 @@ 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, nn);  	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))  		/* case 1: probable callback update */  		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: -	 */ +		gen_clid(new, nn);  	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)); @@ -2226,14 +2259,14 @@ 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;  	nfs4_lock_state(); -	conf = find_confirmed_client(clid); -	unconf = find_unconfirmed_client(clid); +	conf = find_confirmed_client(clid, false, nn); +	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, @@ -2259,13 +2292,9 @@ 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); -		if (conf) { -			nfsd4_client_record_remove(conf); +		conf = find_confirmed_client_by_name(&unconf->cl_name, nn); +		if (conf)  			expire_client(conf); -		}  		move_to_confirmed(unconf);  		nfsd4_probe_callback(unconf);  	} @@ -2325,7 +2354,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", @@ -2389,7 +2418,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);  } @@ -2429,11 +2460,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();  } @@ -2447,16 +2480,21 @@ 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 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)) {  			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;  		} @@ -2536,9 +2574,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 @@ -2580,14 +2623,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; @@ -2600,10 +2642,11 @@ 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, nn);  	open->op_openowner = oo;  	if (!oo) { -		clp = find_confirmed_client(clientid); +		clp = find_confirmed_client(clientid, cstate->minorversion, +					    nn);  		if (clp == NULL)  			return nfserr_expired;  		goto new_owner; @@ -2705,11 +2748,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; @@ -2837,8 +2875,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; @@ -2878,7 +2915,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);  		}  	}  } @@ -2946,6 +2983,7 @@ out:  	}  	return;  out_free: +	unhash_stid(&dp->dl_stid);  	nfs4_put_delegation(dp);  out_no_deleg:  	flag = NFS4_OPEN_DELEGATE_NONE; @@ -3088,7 +3126,19 @@ 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); +} + +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 @@ -3097,21 +3147,14 @@ 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",   			clid->cl_boot, clid->cl_id); -	status = nfserr_stale_clientid; -	if (STALE_CLIENTID(clid, nn)) -		goto out; -	clp = find_confirmed_client(clid); -	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) @@ -3123,44 +3166,42 @@ 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  	 * 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; +	nn->nfsd4_grace = nn->nfsd4_lease;  }  static time_t -nfs4_laundromat(void) +nfs4_laundromat(struct nfsd_net *nn)  {  	struct nfs4_client *clp;  	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();  	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, &client_lru) { +	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)) {  			t = clp->cl_time - cutoff; @@ -3176,17 +3217,18 @@ nfs4_laundromat(void)  		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",  			clp->cl_clientid.cl_id); -		nfsd4_client_record_remove(clp);  		expire_client(clp);  	}  	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) @@ -3200,8 +3242,8 @@ nfs4_laundromat(void)  		dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);  		unhash_delegation(dp);  	} -	test_val = nfsd4_lease; -	list_for_each_safe(pos, next, &close_lru) { +	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)) {  			u = oo->oo_time - cutoff; @@ -3219,16 +3261,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) @@ -3373,16 +3418,17 @@ 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 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;  	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, nn);  	if (!cl)  		return nfserr_expired;  	*s = find_stateid_by_type(cl, stateid, typemask); @@ -3404,6 +3450,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) @@ -3415,7 +3462,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); +	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)); @@ -3429,7 +3477,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: @@ -3556,7 +3608,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; @@ -3565,7 +3618,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); +	status = nfsd4_lookup_stateid(stateid, typemask, &s, +				      cstate->minorversion, nn);  	if (status)  		return status;  	*stpp = openlockstateid(s); @@ -3574,13 +3628,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); @@ -3596,6 +3651,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, @@ -3609,7 +3665,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); @@ -3652,7 +3708,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);  	}  } @@ -3673,6 +3729,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, @@ -3685,7 +3742,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; @@ -3748,6 +3805,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, @@ -3757,7 +3816,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); @@ -3766,6 +3825,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)) { @@ -3778,7 +3838,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: @@ -3794,15 +3854,15 @@ 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; +	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; -	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, nn);  	if (status)  		goto out;  	dp = delegstateid(s); @@ -3820,8 +3880,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 @@ -3839,7 +3897,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;  } @@ -3851,8 +3909,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 @@ -3918,12 +3974,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;  	} @@ -3935,9 +3991,10 @@ 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_ino_hash, &lockowner_ino_hashtbl[inohash]); +	list_add(&lo->lo_owner.so_strhash, &nn->ownerstr_hashtbl[strhashval]); +	list_add(&lo->lo_owner_ino_hash, &nn->lockowner_ino_hashtbl[inohash]);  	list_add(&lo->lo_perstateid, &open_stp->st_lockowners);  } @@ -4011,8 +4068,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; @@ -4046,13 +4105,14 @@ 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;  	int err; -	struct nfsd_net *nn = net_generic(&init_net, 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, @@ -4086,7 +4146,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); @@ -4100,7 +4160,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); @@ -4111,27 +4171,34 @@ 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; -	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,22 +4208,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); -	/* -	* Try to lock the file in the VFS. -	* Note: locks.c uses the BKL to protect the inode's lock list. -	*/ +	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); @@ -4167,7 +4235,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; @@ -4182,6 +4250,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;  } @@ -4210,10 +4282,10 @@ 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); +	struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);  	if (locks_in_grace(SVC_NET(rqstp)))  		return nfserr_grace; @@ -4223,23 +4295,31 @@ 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;  	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"); @@ -4247,27 +4327,29 @@ 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; -	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;  } @@ -4277,10 +4359,11 @@ 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; -						         +	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); @@ -4291,7 +4374,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); @@ -4299,23 +4383,29 @@ 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); -	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 = 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); +	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; @@ -4329,6 +4419,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: @@ -4373,23 +4465,21 @@ 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); -	/* 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); -	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)) @@ -4425,73 +4515,74 @@ alloc_reclaim(void)  	return kmalloc(sizeof(struct nfs4_client_reclaim), GFP_KERNEL);  } -int -nfs4_has_reclaimed_state(const char *name, bool use_exchange_id) +bool +nfs4_has_reclaimed_state(const char *name, struct nfsd_net *nn)  { -	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, nn); +	return (crp && crp->cr_clp);  }  /*   * failure => all reset bets are off, nfserr_no_grace...   */ -int -nfs4_client_to_reclaim(const char *name) +struct nfs4_client_reclaim * +nfs4_client_to_reclaim(const char *name, struct nfsd_net *nn)  {  	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, &nn->reclaim_str_hashtbl[strhashval]); +		memcpy(crp->cr_recdir, name, HEXDIR_LEN); +		crp->cr_clp = NULL; +		nn->reclaim_str_hashtbl_size++; +	} +	return crp;  }  void -nfs4_release_reclaim(void) +nfs4_remove_reclaim_record(struct nfs4_client_reclaim *crp, struct nfsd_net *nn) +{ +	list_del(&crp->cr_strhash); +	kfree(crp); +	nn->reclaim_str_hashtbl_size--; +} + +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); -			list_del(&crp->cr_strhash); -			kfree(crp); -			reclaim_str_hashtbl_size--; +			nfs4_remove_reclaim_record(crp, nn);  		}  	} -	BUG_ON(reclaim_str_hashtbl_size); +	WARN_ON_ONCE(nn->reclaim_str_hashtbl_size);  }  /*   * 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, struct nfsd_net *nn)  {  	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); -	list_for_each_entry(crp, &reclaim_str_hashtbl[strhashval], cr_strhash) { -		if (same_name(crp->cr_recdir, clp->cl_recdir)) { +	strhashval = clientstr_hashval(recdir); +	list_for_each_entry(crp, &nn->reclaim_str_hashtbl[strhashval], cr_strhash) { +		if (same_name(crp->cr_recdir, recdir)) {  			return crp;  		}  	} @@ -4502,12 +4593,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 nfsd_net *nn)  {  	struct nfs4_client *clp;  	/* find clientid in conf_id_hashtbl */ -	clp = find_confirmed_client(clid); +	clp = find_confirmed_client(clid, sessions, nn);  	if (clp == NULL)  		return nfserr_reclaim_bad; @@ -4516,125 +4607,177 @@ nfs4_check_open_reclaim(clientid_t *clid)  #ifdef CONFIG_NFSD_FAULT_INJECTION -void nfsd_forget_clients(u64 num) +u64 nfsd_forget_client(struct nfs4_client *clp, u64 max)  { -	struct nfs4_client *clp, *next; -	int count = 0; - -	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; -	} -	nfs4_unlock_state(); - -	printk(KERN_INFO "NFSD: Forgot %d clients", count); +	expire_client(clp); +	return 1;  } -static void release_lockowner_sop(struct nfs4_stateowner *sop) +u64 nfsd_print_client(struct nfs4_client *clp, u64 num)  { -	release_lockowner(lockowner(sop)); +	char buf[INET6_ADDRSTRLEN]; +	rpc_ntop((struct sockaddr *)&clp->cl_addr, buf, sizeof(buf)); +	printk(KERN_INFO "NFS Client: %s\n", buf); +	return 1;  } -static void release_openowner_sop(struct nfs4_stateowner *sop) +static void nfsd_print_count(struct nfs4_client *clp, unsigned int count, +			     const char *type)  { -	release_openowner(openowner(sop)); +	char buf[INET6_ADDRSTRLEN]; +	rpc_ntop((struct sockaddr *)&clp->cl_addr, buf, sizeof(buf)); +	printk(KERN_INFO "NFS Client: %s has %u %s\n", buf, count, type);  } -static int nfsd_release_n_owners(u64 num, bool is_open_owner, -				void (*release_sop)(struct nfs4_stateowner *)) +static u64 nfsd_foreach_client_lock(struct nfs4_client *clp, u64 max, void (*func)(struct nfs4_lockowner *))  { -	int i, count = 0; -	struct nfs4_stateowner *sop, *next; +	struct nfs4_openowner *oop; +	struct nfs4_lockowner *lop, *lo_next; +	struct nfs4_ol_stateid *stp, *st_next; +	u64 count = 0; -	for (i = 0; i < OWNER_HASH_SIZE; i++) { -		list_for_each_entry_safe(sop, next, &ownerstr_hashtbl[i], so_strhash) { -			if (sop->so_is_open_owner != is_open_owner) -				continue; -			release_sop(sop); -			if (++count == num) -				return count; +	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;  } -void nfsd_forget_locks(u64 num) +u64 nfsd_forget_client_locks(struct nfs4_client *clp, u64 max)  { -	int count; - -	nfs4_lock_state(); -	count = nfsd_release_n_owners(num, false, release_lockowner_sop); -	nfs4_unlock_state(); +	return nfsd_foreach_client_lock(clp, max, release_lockowner); +} -	printk(KERN_INFO "NFSD: Forgot %d locks", count); +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;  } -void nfsd_forget_openowners(u64 num) +static u64 nfsd_foreach_client_open(struct nfs4_client *clp, u64 max, void (*func)(struct nfs4_openowner *))  { -	int count; +	struct nfs4_openowner *oop, *next; +	u64 count = 0; -	nfs4_lock_state(); -	count = nfsd_release_n_owners(num, true, release_openowner_sop); -	nfs4_unlock_state(); +	list_for_each_entry_safe(oop, next, &clp->cl_openowners, oo_perclient) { +		if (func) +			func(oop); +		if (++count == max) +			break; +	} -	printk(KERN_INFO "NFSD: Forgot %d open owners", count); +	return count;  } -int nfsd_process_n_delegations(u64 num, struct list_head *list) +u64 nfsd_forget_client_openowners(struct nfs4_client *clp, u64 max)  { -	int i, count = 0; -	struct nfs4_file *fp, *fnext; -	struct nfs4_delegation *dp, *dnext; +	return nfsd_foreach_client_open(clp, max, release_openowner); +} -	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; -			} -		} -	} +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) +{ +	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;  } -void nfsd_forget_delegations(u64 num) +u64 nfsd_forget_client_delegations(struct nfs4_client *clp, u64 max)  { -	unsigned int count; +	struct nfs4_delegation *dp, *next;  	LIST_HEAD(victims); -	struct nfs4_delegation *dp, *dnext; +	u64 count;  	spin_lock(&recall_lock); -	count = nfsd_process_n_delegations(num, &victims); +	count = nfsd_find_all_delegations(clp, max, &victims);  	spin_unlock(&recall_lock); -	nfs4_lock_state(); -	list_for_each_entry_safe(dp, dnext, &victims, dl_recall_lru) +	list_for_each_entry_safe(dp, next, &victims, dl_recall_lru)  		unhash_delegation(dp); -	nfs4_unlock_state(); -	printk(KERN_INFO "NFSD: Forgot %d delegations", count); +	return count;  } -void nfsd_recall_delegations(u64 num) +u64 nfsd_recall_client_delegations(struct nfs4_client *clp, u64 max)  { -	unsigned int count; +	struct nfs4_delegation *dp, *next;  	LIST_HEAD(victims); -	struct nfs4_delegation *dp, *dnext; +	u64 count;  	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); +	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); -	printk(KERN_INFO "NFSD: Recalled %d delegations", count); +	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; +	u64 count = 0; +	struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, nfsd_net_id); + +	if (!nfsd_netns_ready(nn)) +		return 0; + +	list_for_each_entry_safe(clp, next, &nn->client_lru, cl_lru) { +		count += func(clp, max - count); +		if ((max != 0) && (count >= max)) +			break; +	} + +	return count; +} + +struct nfs4_client *nfsd_find_client(struct sockaddr_storage *addr, size_t addr_size) +{ +	struct nfs4_client *clp; +	struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, nfsd_net_id); + +	if (!nfsd_netns_ready(nn)) +		return NULL; + +	list_for_each_entry(clp, &nn->client_lru, cl_lru) { +		if (memcmp(&clp->cl_addr, addr, addr_size) == 0) +			return clp; +	} +	return NULL;  }  #endif /* CONFIG_NFSD_FAULT_INJECTION */ @@ -4646,27 +4789,10 @@ nfs4_state_init(void)  {  	int i; -	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]); -	} -	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]);  	} -	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); -	INIT_LIST_HEAD(&client_lru);  	INIT_LIST_HEAD(&del_recall_lru); -	reclaim_str_hashtbl_size = 0;  }  /* @@ -4690,12 +4816,100 @@ set_max_delegations(void)  	max_delegations = nr_free_buffer_pages() >> (20 - 2 - PAGE_SHIFT);  } -/* initialization to perform when the nfsd service is started: */ +static int nfs4_state_create_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) +		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; +	nn->ownerstr_hashtbl = kmalloc(sizeof(struct list_head) * +			OWNER_HASH_SIZE, GFP_KERNEL); +	if (!nn->ownerstr_hashtbl) +		goto err_ownerstr; +	nn->lockowner_ino_hashtbl = kmalloc(sizeof(struct list_head) * +			LOCKOWNER_INO_HASH_SIZE, GFP_KERNEL); +	if (!nn->lockowner_ino_hashtbl) +		goto err_lockowner_ino; +	nn->sessionid_hashtbl = kmalloc(sizeof(struct list_head) * +			SESSION_HASH_SIZE, GFP_KERNEL); +	if (!nn->sessionid_hashtbl) +		goto err_sessionid; + +	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]); +	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; +	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); +	get_net(net); + +	return 0; + +err_sessionid: +	kfree(nn->lockowner_ino_hashtbl); +err_lockowner_ino: +	kfree(nn->ownerstr_hashtbl); +err_ownerstr: +	kfree(nn->unconf_id_hashtbl); +err_unconf_id: +	kfree(nn->conf_id_hashtbl); +err: +	return -ENOMEM; +} + +static void +nfs4_state_destroy_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])) { +			clp = list_entry(nn->conf_id_hashtbl[i].next, struct nfs4_client, cl_idhash); +			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->sessionid_hashtbl); +	kfree(nn->lockowner_ino_hashtbl); +	kfree(nn->ownerstr_hashtbl); +	kfree(nn->unconf_id_hashtbl); +	kfree(nn->conf_id_hashtbl); +	put_net(net); +}  int -nfs4_state_start(void) +nfs4_state_start_net(struct net *net)  { -	struct net *net = &init_net;  	struct nfsd_net *nn = net_generic(net, nfsd_net_id);  	int ret; @@ -4706,18 +4920,32 @@ nfs4_state_start(void)  	 * to that instead and then do most of the rest of this on a per-net  	 * basis.  	 */ -	get_net(net); +	if (net != &init_net) +		return -EINVAL; + +	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", +	       nn->nfsd4_grace, net); +	queue_delayed_work(laundry_wq, &nn->laundromat_work, nn->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; @@ -4726,39 +4954,34 @@ 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); +  	set_max_delegations(); +  	return 0; +  out_free_laundry:  	destroy_workqueue(laundry_wq);  out_recovery: -	nfsd4_client_tracking_exit(net); -	put_net(net);  	return ret;  } -static void -__nfs4_state_shutdown(void) +/* should be called with the state lock held */ +void +nfs4_state_shutdown_net(struct net *net)  { -	int i; -	struct nfs4_client *clp = NULL;  	struct nfs4_delegation *dp = NULL;  	struct list_head *pos, *next, reaplist; +	struct nfsd_net *nn = net_generic(net, nfsd_net_id); + +	cancel_delayed_work_sync(&nn->laundromat_work); +	locks_end_grace(&nn->nfsd4_manager); -	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); -		} -		while (!list_empty(&unconf_str_hashtbl[i])) { -			clp = list_entry(unconf_str_hashtbl[i].next, struct nfs4_client, cl_strhash); -			expire_client(clp); -		} -	}  	INIT_LIST_HEAD(&reaplist);  	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); @@ -4767,22 +4990,14 @@ __nfs4_state_shutdown(void)  		unhash_delegation(dp);  	} -	nfsd4_client_tracking_exit(&init_net); -	put_net(&init_net); +	nfsd4_client_tracking_exit(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(&laundromat_work);  	destroy_workqueue(laundry_wq); -	locks_end_grace(&nn->nfsd4_manager); -	nfs4_lock_state(); -	__nfs4_state_shutdown(); -	nfs4_unlock_state();  	nfsd4_destroy_callback_queue();  }  |