📄 nfs4state.c
字号:
/** linux/fs/nfsd/nfs4state.c** Copyright (c) 2001 The Regents of the University of Michigan.* All rights reserved.** Kendrick Smith <kmsmith@umich.edu>* Andy Adamson <kandros@umich.edu>** Redistribution and use in source and binary forms, with or without* modification, are permitted provided that the following conditions* are met:** 1. Redistributions of source code must retain the above copyright* notice, this list of conditions and the following disclaimer.* 2. Redistributions in binary form must reproduce the above copyright* notice, this list of conditions and the following disclaimer in the* documentation and/or other materials provided with the distribution.* 3. Neither the name of the University nor the names of its* contributors may be used to endorse or promote products derived* from this software without specific prior written permission.** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.**/#include <linux/param.h>#include <linux/major.h>#include <linux/slab.h>#include <linux/sunrpc/svc.h>#include <linux/nfsd/nfsd.h>#include <linux/nfsd/cache.h>#include <linux/mount.h>#include <linux/workqueue.h>#include <linux/smp_lock.h>#include <linux/kthread.h>#include <linux/nfs4.h>#include <linux/nfsd/state.h>#include <linux/nfsd/xdr4.h>#include <linux/namei.h>#include <linux/swap.h>#include <linux/mutex.h>#include <linux/lockd/bind.h>#include <linux/module.h>#define NFSDDBG_FACILITY NFSDDBG_PROC/* Globals */static time_t lease_time = 90; /* default lease time */static time_t user_lease_time = 90;static time_t boot_time;static int in_grace = 1;static u32 current_clientid = 1;static u32 current_ownerid = 1;static u32 current_fileid = 1;static u32 current_delegid = 1;static u32 nfs4_init;static stateid_t zerostateid; /* bits all 0 */static stateid_t onestateid; /* bits all 1 */#define ZERO_STATEID(stateid) (!memcmp((stateid), &zerostateid, sizeof(stateid_t)))#define ONE_STATEID(stateid) (!memcmp((stateid), &onestateid, sizeof(stateid_t)))/* forward declarations */static struct nfs4_stateid * find_stateid(stateid_t *stid, int flags);static struct nfs4_delegation * find_delegation_stateid(struct inode *ino, stateid_t *stid);static void release_stateid_lockowners(struct nfs4_stateid *open_stp);static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery";static void nfs4_set_recdir(char *recdir);/* Locking: * * client_mutex: * protects clientid_hashtbl[], clientstr_hashtbl[], * unconfstr_hashtbl[], uncofid_hashtbl[]. */static DEFINE_MUTEX(client_mutex);static struct kmem_cache *stateowner_slab = NULL;static struct kmem_cache *file_slab = NULL;static struct kmem_cache *stateid_slab = NULL;static struct kmem_cache *deleg_slab = NULL;voidnfs4_lock_state(void){ mutex_lock(&client_mutex);}voidnfs4_unlock_state(void){ mutex_unlock(&client_mutex);}static inline u32opaque_hashval(const void *ptr, int nbytes){ unsigned char *cptr = (unsigned char *) ptr; u32 x = 0; while (nbytes--) { x *= 37; x += *cptr++; } return x;}/* forward declarations */static void release_stateowner(struct nfs4_stateowner *sop);static void release_stateid(struct nfs4_stateid *stp, int flags);/* * Delegation state *//* recall_lock protects the del_recall_lru */static DEFINE_SPINLOCK(recall_lock);static struct list_head del_recall_lru;static voidfree_nfs4_file(struct kref *kref){ struct nfs4_file *fp = container_of(kref, struct nfs4_file, fi_ref); list_del(&fp->fi_hash); iput(fp->fi_inode); kmem_cache_free(file_slab, fp);}static inline voidput_nfs4_file(struct nfs4_file *fi){ kref_put(&fi->fi_ref, free_nfs4_file);}static inline voidget_nfs4_file(struct nfs4_file *fi){ kref_get(&fi->fi_ref);}static int num_delegations;unsigned int max_delegations;/* * Open owner state (share locks) *//* hash tables for nfs4_stateowner */#define OWNER_HASH_BITS 8#define OWNER_HASH_SIZE (1 << OWNER_HASH_BITS)#define OWNER_HASH_MASK (OWNER_HASH_SIZE - 1)#define ownerid_hashval(id) \ ((id) & OWNER_HASH_MASK)#define ownerstr_hashval(clientid, ownername) \ (((clientid) + opaque_hashval((ownername.data), (ownername.len))) & OWNER_HASH_MASK)static struct list_head ownerid_hashtbl[OWNER_HASH_SIZE];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)#define FILE_HASH_MASK (FILE_HASH_SIZE - 1)/* hash table for (open)nfs4_stateid */#define STATEID_HASH_BITS 10#define STATEID_HASH_SIZE (1 << STATEID_HASH_BITS)#define STATEID_HASH_MASK (STATEID_HASH_SIZE - 1)#define file_hashval(x) \ hash_ptr(x, FILE_HASH_BITS)#define stateid_hashval(owner_id, file_id) \ (((owner_id) + (file_id)) & STATEID_HASH_MASK)static struct list_head file_hashtbl[FILE_HASH_SIZE];static struct list_head stateid_hashtbl[STATEID_HASH_SIZE];static struct nfs4_delegation *alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_fh *current_fh, u32 type){ struct nfs4_delegation *dp; struct nfs4_file *fp = stp->st_file; struct nfs4_callback *cb = &stp->st_stateowner->so_client->cl_callback; dprintk("NFSD alloc_init_deleg\n"); if (fp->fi_had_conflict) return NULL; if (num_delegations > max_delegations) return NULL; dp = kmem_cache_alloc(deleg_slab, GFP_KERNEL); if (dp == NULL) return dp; num_delegations++; INIT_LIST_HEAD(&dp->dl_perfile); INIT_LIST_HEAD(&dp->dl_perclnt); INIT_LIST_HEAD(&dp->dl_recall_lru); dp->dl_client = clp; get_nfs4_file(fp); dp->dl_file = fp; dp->dl_flock = NULL; get_file(stp->st_vfs_file); dp->dl_vfs_file = stp->st_vfs_file; dp->dl_type = type; dp->dl_recall.cbr_dp = NULL; dp->dl_recall.cbr_ident = cb->cb_ident; dp->dl_recall.cbr_trunc = 0; dp->dl_stateid.si_boot = boot_time; dp->dl_stateid.si_stateownerid = current_delegid++; dp->dl_stateid.si_fileid = 0; dp->dl_stateid.si_generation = 0; dp->dl_fhlen = current_fh->fh_handle.fh_size; memcpy(dp->dl_fhval, ¤t_fh->fh_handle.fh_base, current_fh->fh_handle.fh_size); dp->dl_time = 0; atomic_set(&dp->dl_count, 1); list_add(&dp->dl_perfile, &fp->fi_delegations); list_add(&dp->dl_perclnt, &clp->cl_delegations); return dp;}voidnfs4_put_delegation(struct nfs4_delegation *dp){ if (atomic_dec_and_test(&dp->dl_count)) { dprintk("NFSD: freeing dp %p\n",dp); put_nfs4_file(dp->dl_file); kmem_cache_free(deleg_slab, dp); num_delegations--; }}/* Remove the associated file_lock first, then remove the delegation. * lease_modify() is called to remove the FS_LEASE file_lock from * the i_flock list, eventually calling nfsd's lock_manager * fl_release_callback. */static voidnfs4_close_delegation(struct nfs4_delegation *dp){ struct file *filp = dp->dl_vfs_file; dprintk("NFSD: close_delegation dp %p\n",dp); dp->dl_vfs_file = NULL; /* The following nfsd_close may not actually close the file, * but we want to remove the lease in any case. */ if (dp->dl_flock) vfs_setlease(filp, F_UNLCK, &dp->dl_flock); nfsd_close(filp);}/* Called under the state lock. */static voidunhash_delegation(struct nfs4_delegation *dp){ list_del_init(&dp->dl_perfile); list_del_init(&dp->dl_perclnt); spin_lock(&recall_lock); list_del_init(&dp->dl_recall_lru); spin_unlock(&recall_lock); nfs4_close_delegation(dp); nfs4_put_delegation(dp);}/* * SETCLIENTID state *//* 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)#define clientid_hashval(id) \ ((id) & CLIENT_HASH_MASK)#define clientstr_hashval(name) \ (opaque_hashval((name), 8) & CLIENT_HASH_MASK)/* * 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;static inline voidrenew_client(struct nfs4_client *clp){ /* * Move client to the end to the LRU list. */ 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); clp->cl_time = get_seconds();}/* SETCLIENTID and SETCLIENTID_CONFIRM Helper functions */static intSTALE_CLIENTID(clientid_t *clid){ if (clid->cl_boot == boot_time) return 0; dprintk("NFSD stale clientid (%08x/%08x)\n", clid->cl_boot, clid->cl_id); return 1;}/* * XXX Should we use a slab cache ? * This type of memory management is somewhat inefficient, but we use it * anyway since SETCLIENTID is not a common operation. */static inline struct nfs4_client *alloc_client(struct xdr_netobj name){ struct nfs4_client *clp; if ((clp = kzalloc(sizeof(struct nfs4_client), GFP_KERNEL))!= NULL) { if ((clp->cl_name.data = kmalloc(name.len, GFP_KERNEL)) != NULL) { memcpy(clp->cl_name.data, name.data, name.len); clp->cl_name.len = name.len; } else { kfree(clp); clp = NULL; } } return clp;}static voidshutdown_callback_client(struct nfs4_client *clp){ struct rpc_clnt *clnt = clp->cl_callback.cb_client; /* shutdown rpc client, ending any outstanding recall rpcs */ if (clnt) { clp->cl_callback.cb_client = NULL; rpc_shutdown_client(clnt); }}static inline voidfree_client(struct nfs4_client *clp){ shutdown_callback_client(clp); if (clp->cl_cred.cr_group_info) put_group_info(clp->cl_cred.cr_group_info); kfree(clp->cl_name.data); kfree(clp);}voidput_nfs4_client(struct nfs4_client *clp){ if (atomic_dec_and_test(&clp->cl_count)) free_client(clp);}static voidexpire_client(struct nfs4_client *clp){ struct nfs4_stateowner *sop; struct nfs4_delegation *dp; struct list_head reaplist; dprintk("NFSD: expire_client cl_count %d\n", atomic_read(&clp->cl_count)); INIT_LIST_HEAD(&reaplist); spin_lock(&recall_lock); while (!list_empty(&clp->cl_delegations)) { dp = list_entry(clp->cl_delegations.next, struct nfs4_delegation, dl_perclnt); dprintk("NFSD: expire client. dp %p, fp %p\n", dp, dp->dl_flock); list_del_init(&dp->dl_perclnt); list_move(&dp->dl_recall_lru, &reaplist); } spin_unlock(&recall_lock); while (!list_empty(&reaplist)) { dp = list_entry(reaplist.next, struct nfs4_delegation, dl_recall_lru); list_del_init(&dp->dl_recall_lru); unhash_delegation(dp); } list_del(&clp->cl_idhash); list_del(&clp->cl_strhash); list_del(&clp->cl_lru); while (!list_empty(&clp->cl_openowners)) { sop = list_entry(clp->cl_openowners.next, struct nfs4_stateowner, so_perclient); release_stateowner(sop); } put_nfs4_client(clp);}static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir) { struct nfs4_client *clp; if (!(clp = alloc_client(name))) goto out; memcpy(clp->cl_recdir, recdir, HEXDIR_LEN); atomic_set(&clp->cl_count, 1); atomic_set(&clp->cl_callback.cb_set, 0); 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);out: return clp;}static voidcopy_verf(struct nfs4_client *target, nfs4_verifier *source) { memcpy(target->cl_verifier.data, source->data, sizeof(target->cl_verifier.data));}static voidcopy_clid(struct nfs4_client *target, struct nfs4_client *source) { target->cl_clientid.cl_boot = source->cl_clientid.cl_boot; target->cl_clientid.cl_id = source->cl_clientid.cl_id; }static voidcopy_cred(struct svc_cred *target, struct svc_cred *source) { target->cr_uid = source->cr_uid; target->cr_gid = source->cr_gid; target->cr_group_info = source->cr_group_info; get_group_info(target->cr_group_info);}static inline intsame_name(const char *n1, const char *n2){ return 0 == memcmp(n1, n2, HEXDIR_LEN);}static intsame_verf(nfs4_verifier *v1, nfs4_verifier *v2){ return 0 == memcmp(v1->data, v2->data, sizeof(v1->data));}static intsame_clid(clientid_t *cl1, clientid_t *cl2){ return (cl1->cl_boot == cl2->cl_boot) && (cl1->cl_id == cl2->cl_id);}/* XXX what about NGROUP */static intsame_creds(struct svc_cred *cr1, struct svc_cred *cr2){ return cr1->cr_uid == cr2->cr_uid;}static voidgen_clid(struct nfs4_client *clp) { clp->cl_clientid.cl_boot = boot_time;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -