X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=fs%2Fnfsd%2Fnfs4state.c;h=1f68ce36e724f59851df09c5a9c26f36a526cf3a;hb=ac4d8ff2a57179de3ef7834c6ab3fac430b0a05d;hp=927d2007d5abaee83162d3a38e89e47c9a07bd4a;hpb=5b2d21c1965859acc881dd862b6ebbfae67cdc14;p=safe%2Fjmp%2Flinux-2.6 diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 927d200..1f68ce3 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -53,7 +53,7 @@ /* Globals */ static time_t lease_time = 90; /* default lease time */ -static time_t old_lease_time = 90; /* past incarnation lease time */ +static time_t user_lease_time = 90; static u32 nfs4_reclaim_init = 0; time_t boot_time; static time_t grace_end = 0; @@ -65,14 +65,6 @@ static u32 nfs4_init; stateid_t zerostateid; /* bits all 0 */ stateid_t onestateid; /* bits all 1 */ -/* debug counters */ -u32 list_add_perfile = 0; -u32 list_del_perfile = 0; -u32 add_perclient = 0; -u32 del_perclient = 0; -u32 vfsopen = 0; -u32 vfsclose = 0; - /* forward declarations */ struct nfs4_stateid * find_stateid(stateid_t *stid, int flags); static struct nfs4_delegation * find_delegation_stateid(struct inode *ino, stateid_t *stid); @@ -119,16 +111,36 @@ opaque_hashval(const void *ptr, int nbytes) /* forward declarations */ static void release_stateowner(struct nfs4_stateowner *sop); static void release_stateid(struct nfs4_stateid *stp, int flags); -static void release_file(struct nfs4_file *fp); /* * Delegation state */ /* recall_lock protects the del_recall_lru */ -spinlock_t recall_lock; +spinlock_t recall_lock = SPIN_LOCK_UNLOCKED; static struct list_head del_recall_lru; +static void +free_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 void +put_nfs4_file(struct nfs4_file *fi) +{ + kref_put(&fi->fi_ref, free_nfs4_file); +} + +static inline void +get_nfs4_file(struct nfs4_file *fi) +{ + kref_get(&fi->fi_ref); +} + static struct nfs4_delegation * alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_fh *current_fh, u32 type) { @@ -144,6 +156,7 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f INIT_LIST_HEAD(&dp->dl_del_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); @@ -161,7 +174,7 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f current_fh->fh_handle.fh_size); dp->dl_time = 0; atomic_set(&dp->dl_count, 1); - list_add(&dp->dl_del_perfile, &fp->fi_del_perfile); + list_add(&dp->dl_del_perfile, &fp->fi_delegations); list_add(&dp->dl_del_perclnt, &clp->cl_del_perclnt); return dp; } @@ -171,6 +184,7 @@ nfs4_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); } } @@ -192,7 +206,6 @@ nfs4_close_delegation(struct nfs4_delegation *dp) if (dp->dl_flock) setlease(filp, F_UNLCK, &dp->dl_flock); nfsd_close(filp); - vfsclose++; } /* Called under the state lock. */ @@ -962,9 +975,10 @@ alloc_init_file(struct inode *ino) fp = kmem_cache_alloc(file_slab, GFP_KERNEL); if (fp) { + kref_init(&fp->fi_ref); INIT_LIST_HEAD(&fp->fi_hash); - INIT_LIST_HEAD(&fp->fi_perfile); - INIT_LIST_HEAD(&fp->fi_del_perfile); + INIT_LIST_HEAD(&fp->fi_stateids); + INIT_LIST_HEAD(&fp->fi_delegations); list_add(&fp->fi_hash, &file_hashtbl[hashval]); fp->fi_inode = igrab(ino); fp->fi_id = current_fileid++; @@ -974,24 +988,6 @@ alloc_init_file(struct inode *ino) } static void -release_all_files(void) -{ - int i; - struct nfs4_file *fp; - - for (i=0;ifi_perfile) || !list_empty(&fp->fi_del_perfile)) { - printk("ERROR: release_all_files: file %p is open, creating dangling state !!!\n",fp); - } - release_file(fp); - } - } -} - -static void nfsd4_free_slab(kmem_cache_t **slab) { int status; @@ -1083,7 +1079,6 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, str list_add(&sop->so_idhash, &ownerid_hashtbl[idhashval]); list_add(&sop->so_strhash, &ownerstr_hashtbl[strhashval]); list_add(&sop->so_perclient, &clp->cl_perclient); - add_perclient++; sop->so_is_open_owner = 1; sop->so_id = current_ownerid++; sop->so_client = clp; @@ -1117,10 +1112,8 @@ unhash_stateowner(struct nfs4_stateowner *sop) list_del(&sop->so_idhash); list_del(&sop->so_strhash); - if (sop->so_is_open_owner) { + if (sop->so_is_open_owner) list_del(&sop->so_perclient); - del_perclient++; - } list_del(&sop->so_perlockowner); while (!list_empty(&sop->so_perfilestate)) { stp = list_entry(sop->so_perfilestate.next, @@ -1151,9 +1144,9 @@ init_stateid(struct nfs4_stateid *stp, struct nfs4_file *fp, struct nfsd4_open * INIT_LIST_HEAD(&stp->st_perfile); list_add(&stp->st_hash, &stateid_hashtbl[hashval]); list_add(&stp->st_perfilestate, &sop->so_perfilestate); - list_add_perfile++; - list_add(&stp->st_perfile, &fp->fi_perfile); + list_add(&stp->st_perfile, &fp->fi_stateids); stp->st_stateowner = sop; + get_nfs4_file(fp); stp->st_file = fp; stp->st_stateid.si_boot = boot_time; stp->st_stateid.si_stateownerid = sop->so_id; @@ -1171,28 +1164,19 @@ release_stateid(struct nfs4_stateid *stp, int flags) struct file *filp = stp->st_vfs_file; list_del(&stp->st_hash); - list_del_perfile++; list_del(&stp->st_perfile); list_del(&stp->st_perfilestate); if (flags & OPEN_STATE) { release_stateid_lockowners(stp); stp->st_vfs_file = NULL; nfsd_close(filp); - vfsclose++; } else if (flags & LOCK_STATE) locks_remove_posix(filp, (fl_owner_t) stp->st_stateowner); + put_nfs4_file(stp->st_file); kmem_cache_free(stateid_slab, stp); stp = NULL; } -static void -release_file(struct nfs4_file *fp) -{ - list_del(&fp->fi_hash); - iput(fp->fi_inode); - kmem_cache_free(file_slab, fp); -} - void move_to_close_lru(struct nfs4_stateowner *sop) { @@ -1207,7 +1191,6 @@ void release_state_owner(struct nfs4_stateid *stp, int flag) { struct nfs4_stateowner *sop = stp->st_stateowner; - struct nfs4_file *fp = stp->st_file; dprintk("NFSD: release_state_owner\n"); release_stateid(stp, flag); @@ -1218,10 +1201,6 @@ release_state_owner(struct nfs4_stateid *stp, int flag) */ if (sop->so_confirmed && list_empty(&sop->so_perfilestate)) move_to_close_lru(sop); - /* unused nfs4_file's are releseed. XXX slab cache? */ - if (list_empty(&fp->fi_perfile) && list_empty(&fp->fi_del_perfile)) { - release_file(fp); - } } static int @@ -1251,8 +1230,10 @@ find_file(struct inode *ino) struct nfs4_file *fp; list_for_each_entry(fp, &file_hashtbl[hashval], fi_hash) { - if (fp->fi_inode == ino) + if (fp->fi_inode == ino) { + get_nfs4_file(fp); return fp; + } } return NULL; } @@ -1303,19 +1284,24 @@ nfs4_share_conflict(struct svc_fh *current_fh, unsigned int deny_type) struct inode *ino = current_fh->fh_dentry->d_inode; struct nfs4_file *fp; struct nfs4_stateid *stp; + int ret; dprintk("NFSD: nfs4_share_conflict\n"); fp = find_file(ino); - if (fp) { + if (!fp) + return nfs_ok; + ret = nfserr_share_denied; /* Search for conflicting share reservations */ - list_for_each_entry(stp, &fp->fi_perfile, st_perfile) { - if (test_bit(deny_type, &stp->st_deny_bmap) || - test_bit(NFS4_SHARE_DENY_BOTH, &stp->st_deny_bmap)) - return nfserr_share_denied; - } + list_for_each_entry(stp, &fp->fi_stateids, st_perfile) { + if (test_bit(deny_type, &stp->st_deny_bmap) || + test_bit(NFS4_SHARE_DENY_BOTH, &stp->st_deny_bmap)) + goto out; } - return nfs_ok; + ret = nfs_ok; +out: + put_nfs4_file(fp); + return ret; } static inline void @@ -1560,7 +1546,7 @@ find_delegation_file(struct nfs4_file *fp, stateid_t *stid) { struct nfs4_delegation *dp; - list_for_each_entry(dp, &fp->fi_del_perfile, dl_del_perfile) { + list_for_each_entry(dp, &fp->fi_delegations, dl_del_perfile) { if (dp->dl_stateid.si_stateownerid == stid->si_stateownerid) return dp; } @@ -1598,7 +1584,7 @@ nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_state int status = nfserr_share_denied; struct nfs4_stateowner *sop = open->op_stateowner; - list_for_each_entry(local, &fp->fi_perfile, st_perfile) { + list_for_each_entry(local, &fp->fi_stateids, st_perfile) { /* ignore lock owners */ if (local->st_stateowner->so_is_open_owner == 0) continue; @@ -1645,7 +1631,6 @@ nfs4_new_open(struct svc_rqst *rqstp, struct nfs4_stateid **stpp, return status; } } - vfsopen++; *stpp = stp; return 0; } @@ -1724,14 +1709,30 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta int status, flag = 0; flag = NFS4_OPEN_DELEGATE_NONE; - if (open->op_claim_type != NFS4_OPEN_CLAIM_NULL - || !atomic_read(&cb->cb_set) || !sop->so_confirmed) - goto out; - - if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) - flag = NFS4_OPEN_DELEGATE_WRITE; - else - flag = NFS4_OPEN_DELEGATE_READ; + open->op_recall = 0; + switch (open->op_claim_type) { + case NFS4_OPEN_CLAIM_PREVIOUS: + if (!atomic_read(&cb->cb_set)) + open->op_recall = 1; + flag = open->op_delegate_type; + if (flag == NFS4_OPEN_DELEGATE_NONE) + goto out; + break; + case NFS4_OPEN_CLAIM_NULL: + /* Let's not give out any delegations till everyone's + * had the chance to reclaim theirs.... */ + if (nfs4_in_grace()) + goto out; + if (!atomic_read(&cb->cb_set) || !sop->so_confirmed) + goto out; + if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) + flag = NFS4_OPEN_DELEGATE_WRITE; + else + flag = NFS4_OPEN_DELEGATE_READ; + break; + default: + goto out; + } dp = alloc_init_deleg(sop->so_client, stp, fh, flag); if (dp == NULL) { @@ -1765,6 +1766,10 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta dp->dl_stateid.si_fileid, dp->dl_stateid.si_generation); out: + if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS + && flag == NFS4_OPEN_DELEGATE_NONE + && open->op_delegate_type != NFS4_OPEN_DELEGATE_NONE) + printk("NFSD: WARNING: refusing delegation reclaim\n"); open->op_delegate_type = flag; } @@ -1845,10 +1850,8 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf stp->st_stateid.si_boot, stp->st_stateid.si_stateownerid, stp->st_stateid.si_fileid, stp->st_stateid.si_generation); out: - /* take the opportunity to clean up unused state */ - if (fp && list_empty(&fp->fi_perfile) && list_empty(&fp->fi_del_perfile)) - release_file(fp); - + if (fp) + put_nfs4_file(fp); /* CLAIM_PREVIOUS has different error returns */ nfs4_set_claim_prev(open, &status); /* @@ -1861,6 +1864,7 @@ out: return status; } +static struct workqueue_struct *laundry_wq; static struct work_struct laundromat_work; static void laundromat_main(void *); static DECLARE_WORK(laundromat_work, laundromat_main, NULL); @@ -1968,7 +1972,7 @@ laundromat_main(void *not_used) t = nfs4_laundromat(); dprintk("NFSD: laundromat_main - sleeping for %ld seconds\n", t); - schedule_delayed_work(&laundromat_work, t*HZ); + queue_delayed_work(laundry_wq, &laundromat_work, t*HZ); } /* search ownerid_hashtbl[] and close_lru for stateid owner @@ -2496,16 +2500,19 @@ find_stateid(stateid_t *stid, int flags) static struct nfs4_delegation * find_delegation_stateid(struct inode *ino, stateid_t *stid) { - struct nfs4_file *fp = NULL; + struct nfs4_file *fp; + struct nfs4_delegation *dl; dprintk("NFSD:find_delegation_stateid stateid=(%08x/%08x/%08x/%08x)\n", stid->si_boot, stid->si_stateownerid, stid->si_fileid, stid->si_generation); fp = find_file(ino); - if (fp) - return find_delegation_file(fp, stid); - return NULL; + if (!fp) + return NULL; + dl = find_delegation_file(fp, stid); + put_nfs4_file(fp); + return dl; } /* @@ -2649,10 +2656,10 @@ alloc_init_lock_stateid(struct nfs4_stateowner *sop, struct nfs4_file *fp, struc INIT_LIST_HEAD(&stp->st_perfilestate); INIT_LIST_HEAD(&stp->st_perlockowner); /* not used */ list_add(&stp->st_hash, &lockstateid_hashtbl[hashval]); - list_add(&stp->st_perfile, &fp->fi_perfile); - list_add_perfile++; + list_add(&stp->st_perfile, &fp->fi_stateids); list_add(&stp->st_perfilestate, &sop->so_perfilestate); stp->st_stateowner = sop; + get_nfs4_file(fp); stp->st_file = fp; stp->st_stateid.si_boot = boot_time; stp->st_stateid.si_stateownerid = sop->so_id; @@ -3169,30 +3176,16 @@ nfs4_find_reclaim_client(clientid_t *clid) int nfs4_check_open_reclaim(clientid_t *clid) { - struct nfs4_client_reclaim *crp; - - if ((crp = nfs4_find_reclaim_client(clid)) == NULL) - return nfserr_reclaim_bad; - return nfs_ok; + return nfs4_find_reclaim_client(clid) ? nfs_ok : nfserr_reclaim_bad; } +/* initialization to perform at module load time: */ -/* - * Start and stop routines - */ - -static void -__nfs4_state_init(void) +void +nfs4_state_init(void) { int i; - time_t grace_time; - if (!nfs4_reclaim_init) { - for (i = 0; i < CLIENT_HASH_SIZE; i++) - INIT_LIST_HEAD(&reclaim_str_hashtbl[i]); - reclaim_str_hashtbl_size = 0; - nfs4_reclaim_init = 1; - } for (i = 0; i < CLIENT_HASH_SIZE; i++) { INIT_LIST_HEAD(&conf_id_hashtbl[i]); INIT_LIST_HEAD(&conf_str_hashtbl[i]); @@ -3214,26 +3207,34 @@ __nfs4_state_init(void) INIT_LIST_HEAD(&lock_ownerid_hashtbl[i]); INIT_LIST_HEAD(&lock_ownerstr_hashtbl[i]); } - memset(&zerostateid, 0, sizeof(stateid_t)); memset(&onestateid, ~0, sizeof(stateid_t)); - INIT_LIST_HEAD(&close_lru); INIT_LIST_HEAD(&client_lru); INIT_LIST_HEAD(&del_recall_lru); - spin_lock_init(&recall_lock); + for (i = 0; i < CLIENT_HASH_SIZE; i++) + INIT_LIST_HEAD(&reclaim_str_hashtbl[i]); + reclaim_str_hashtbl_size = 0; + nfs4_reclaim_init = 1; +} + +/* initialization to perform when the nfsd service is started: */ + +static void +__nfs4_state_start(void) +{ + time_t grace_time; + boot_time = get_seconds(); - grace_time = max(old_lease_time, lease_time); - if (reclaim_str_hashtbl_size == 0) - grace_time = 0; - if (grace_time) - printk("NFSD: starting %ld-second grace period\n", grace_time); + grace_time = max(user_lease_time, lease_time); + lease_time = user_lease_time; + printk("NFSD: starting %ld-second grace period\n", grace_time); grace_end = boot_time + grace_time; - INIT_WORK(&laundromat_work,laundromat_main, NULL); - schedule_delayed_work(&laundromat_work, NFSD_LEASE_TIME*HZ); + laundry_wq = create_singlethread_workqueue("nfsd4"); + queue_delayed_work(laundry_wq, &laundromat_work, NFSD_LEASE_TIME*HZ); } int -nfs4_state_init(void) +nfs4_state_start(void) { int status; @@ -3242,7 +3243,7 @@ nfs4_state_init(void) status = nfsd4_init_slabs(); if (status) return status; - __nfs4_state_init(); + __nfs4_state_start(); nfs4_init = 1; return 0; } @@ -3304,16 +3305,10 @@ __nfs4_state_shutdown(void) unhash_delegation(dp); } - release_all_files(); cancel_delayed_work(&laundromat_work); - flush_scheduled_work(); + flush_workqueue(laundry_wq); + destroy_workqueue(laundry_wq); nfs4_init = 0; - dprintk("NFSD: list_add_perfile %d list_del_perfile %d\n", - list_add_perfile, list_del_perfile); - dprintk("NFSD: add_perclient %d del_perclient %d\n", - add_perclient, del_perclient); - dprintk("NFSD: vfsopen %d vfsclose %d\n", - vfsopen, vfsclose); } void @@ -3329,53 +3324,16 @@ nfs4_state_shutdown(void) /* * Called when leasetime is changed. * - * if nfsd is not started, simply set the global lease. - * - * if nfsd(s) are running, lease change requires nfsv4 state to be reset. - * e.g: boot_time is reset, existing nfs4_client structs are - * used to fill reclaim_str_hashtbl, then all state (except for the - * reclaim_str_hashtbl) is re-initialized. - * - * if the old lease time is greater than the new lease time, the grace - * period needs to be set to the old lease time to allow clients to reclaim - * their state. XXX - we may want to set the grace period == lease time - * after an initial grace period == old lease time - * - * if an error occurs in this process, the new lease is set, but the server - * will not honor OPEN or LOCK reclaims, and will return nfserr_no_grace - * which means OPEN/LOCK/READ/WRITE will fail during grace period. - * - * clients will attempt to reset all state with SETCLIENTID/CONFIRM, and - * OPEN and LOCK reclaims. + * The only way the protocol gives us to handle on-the-fly lease changes is to + * simulate a reboot. Instead of doing that, we just wait till the next time + * we start to register any changes in lease time. If the administrator + * really wants to change the lease time *now*, they can go ahead and bring + * nfsd down and then back up again after changing the lease time. */ void nfs4_reset_lease(time_t leasetime) { - struct nfs4_client *clp; - int i; - - printk("NFSD: New leasetime %ld\n",leasetime); - if (!nfs4_init) - return; - nfs4_lock_state(); - old_lease_time = lease_time; - lease_time = leasetime; - - nfs4_release_reclaim(); - - /* populate reclaim_str_hashtbl with current confirmed nfs4_clientid */ - for (i = 0; i < CLIENT_HASH_SIZE; i++) { - list_for_each_entry(clp, &conf_id_hashtbl[i], cl_idhash) { - if (!nfs4_client_to_reclaim(clp->cl_name.data, - clp->cl_name.len)) { - nfs4_release_reclaim(); - goto init_state; - } - } - } -init_state: - __nfs4_state_shutdown(); - __nfs4_state_init(); - nfs4_unlock_state(); + lock_kernel(); + user_lease_time = leasetime; + unlock_kernel(); } -