X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fnfsd%2Fnfs4state.c;h=1ca8ce8f6680bb5b0741657473190d680ee2c574;hb=c7b5ebbddf7bcd3651947760f423e3783bbe6573;hp=848c4851d43d83f26a7a22429c04eb45bb3f6da4;hpb=9213980e6a70d8473e0ffd4b39ab5b6caaba9ff5;p=linux-2.6.git diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 848c4851d..1ca8ce8f6 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -51,6 +51,9 @@ #define NFSDDBG_FACILITY NFSDDBG_PROC /* Globals */ +static time_t lease_time = 90; /* default lease time */ +static time_t old_lease_time = 90; /* past incarnation lease time */ +static u32 nfs4_reclaim_init = 0; time_t boot_time; static time_t grace_end = 0; static u32 current_clientid = 1; @@ -82,7 +85,7 @@ struct nfs4_stateid * find_stateid(stateid_t *stid, int flags); * protects clientid_hashtbl[], clientstr_hashtbl[], * unconfstr_hashtbl[], uncofid_hashtbl[]. */ -static struct semaphore client_sema; +static DECLARE_MUTEX(client_sema); void nfs4_lock_state(void) @@ -131,8 +134,11 @@ static void release_file(struct nfs4_file *fp); ((id) & CLIENT_HASH_MASK) #define clientstr_hashval(name, namelen) \ (opaque_hashval((name), (namelen)) & CLIENT_HASH_MASK) - -/* conf_id_hashtbl[], and conf_str_hashtbl[] hold confirmed +/* + * 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 @@ -144,6 +150,8 @@ static void release_file(struct nfs4_file *fp); * 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; 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]; @@ -228,7 +236,7 @@ static struct nfs4_client * create_client(struct xdr_netobj name) { struct nfs4_client *clp; - if(!(clp = alloc_client(name))) + if (!(clp = alloc_client(name))) goto out; INIT_LIST_HEAD(&clp->cl_idhash); INIT_LIST_HEAD(&clp->cl_strhash); @@ -260,7 +268,7 @@ copy_cred(struct svc_cred *target, struct svc_cred *source) { static int cmp_name(struct xdr_netobj *n1, struct xdr_netobj *n2) { - if(!n1 || !n2) + if (!n1 || !n2) return 0; return((n1->len == n2->len) && !memcmp(n1->data, n2->data, n2->len)); } @@ -339,6 +347,95 @@ move_to_confirmed(struct nfs4_client *clp, unsigned int idhashval) renew_client(clp); } + +/* a helper function for parse_callback */ +static int +parse_octet(unsigned int *lenp, char **addrp) +{ + unsigned int len = *lenp; + char *p = *addrp; + int n = -1; + char c; + + for (;;) { + if (!len) + break; + len--; + c = *p++; + if (c == '.') + break; + if ((c < '0') || (c > '9')) { + n = -1; + break; + } + if (n < 0) + n = 0; + n = (n * 10) + (c - '0'); + if (n > 255) { + n = -1; + break; + } + } + *lenp = len; + *addrp = p; + return n; +} + +/* parse and set the setclientid ipv4 callback address */ +int +parse_ipv4(unsigned int addr_len, char *addr_val, unsigned int *cbaddrp, unsigned short *cbportp) +{ + int temp = 0; + u32 cbaddr = 0; + u16 cbport = 0; + u32 addrlen = addr_len; + char *addr = addr_val; + int i, shift; + + /* ipaddress */ + shift = 24; + for(i = 4; i > 0 ; i--) { + if ((temp = parse_octet(&addrlen, &addr)) < 0) { + return 0; + } + cbaddr |= (temp << shift); + if (shift > 0) + shift -= 8; + } + *cbaddrp = cbaddr; + + /* port */ + shift = 8; + for(i = 2; i > 0 ; i--) { + if ((temp = parse_octet(&addrlen, &addr)) < 0) { + return 0; + } + cbport |= (temp << shift); + if (shift > 0) + shift -= 8; + } + *cbportp = cbport; + return 1; +} + +void +gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se) +{ + struct nfs4_callback *cb = &clp->cl_callback; + + if ( !(parse_ipv4(se->se_callback_addr_len, se->se_callback_addr_val, + &cb->cb_addr, &cb->cb_port))) { + printk(KERN_INFO "NFSD: BAD callback address. client will not receive delegations\n"); + cb->cb_parsed = 0; + return; + } + cb->cb_netid.len = se->se_callback_netid_len; + cb->cb_netid.data = se->se_callback_netid_val; + cb->cb_prog = se->se_callback_prog; + cb->cb_ident = se->se_callback_ident; + cb->cb_parsed = 1; +} + /* * RFC 3010 has a complex implmentation description of processing a * SETCLIENTID request consisting of 5 bullets, labeled as @@ -450,6 +547,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_setclientid *setclid) copy_cred(&new->cl_cred,&rqstp->rq_cred); gen_clid(new); gen_confirm(new); + gen_callback(new, setclid); add_to_unconfirmed(new, strhashval); } else if (cmp_verf(&conf->cl_verifier, &clverifier)) { /* @@ -477,6 +575,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_setclientid *setclid) copy_cred(&new->cl_cred,&rqstp->rq_cred); copy_clid(new, conf); gen_confirm(new); + gen_callback(new, setclid); add_to_unconfirmed(new,strhashval); } else if (!unconf) { /* @@ -494,6 +593,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_setclientid *setclid) copy_cred(&new->cl_cred,&rqstp->rq_cred); gen_clid(new); gen_confirm(new); + gen_callback(new, setclid); add_to_unconfirmed(new, strhashval); } else if (!cmp_verf(&conf->cl_confirm, &unconf->cl_confirm)) { /* @@ -519,6 +619,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_setclientid *setclid) copy_cred(&new->cl_cred,&rqstp->rq_cred); gen_clid(new); gen_confirm(new); + gen_callback(new, setclid); add_to_unconfirmed(new, strhashval); } else { /* No cases hit !!! */ @@ -529,7 +630,6 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_setclientid *setclid) 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)); - printk(KERN_INFO "NFSD: this client will not receive delegations\n"); status = nfs_ok; out: nfs4_unlock_state(); @@ -712,7 +812,7 @@ alloc_init_file(unsigned int hashval, struct inode *ino) { alloc_file++; return fp; } - return (struct nfs4_file *)NULL; + return NULL; } static void @@ -725,7 +825,7 @@ release_all_files(void) while (!list_empty(&file_hashtbl[i])) { fp = list_entry(file_hashtbl[i].next, struct nfs4_file, fi_hash); /* this should never be more than once... */ - if(!list_empty(&fp->fi_perfile)) { + if (!list_empty(&fp->fi_perfile)) { printk("ERROR: release_all_files: file %p is open, creating dangling state !!!\n",fp); } release_file(fp); @@ -739,20 +839,20 @@ alloc_stateowner(struct xdr_netobj *owner) struct nfs4_stateowner *sop; if ((sop = kmalloc(sizeof(struct nfs4_stateowner),GFP_KERNEL))) { - if((sop->so_owner.data = kmalloc(owner->len, GFP_KERNEL))) { + if ((sop->so_owner.data = kmalloc(owner->len, GFP_KERNEL))) { memcpy(sop->so_owner.data, owner->data, owner->len); sop->so_owner.len = owner->len; return sop; } kfree(sop); } - return (struct nfs4_stateowner *)NULL; + return NULL; } /* should use a slab cache */ static void free_stateowner(struct nfs4_stateowner *sop) { - if(sop) { + if (sop) { kfree(sop->so_owner.data); kfree(sop); sop = NULL; @@ -767,7 +867,7 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, str unsigned int idhashval; if (!(sop = alloc_stateowner(&open->op_owner))) - return (struct nfs4_stateowner *)NULL; + return NULL; idhashval = ownerid_hashval(current_ownerid); INIT_LIST_HEAD(&sop->so_idhash); INIT_LIST_HEAD(&sop->so_strhash); @@ -808,7 +908,7 @@ release_stateid_lockowner(struct nfs4_stateid *open_stp) } static void -release_stateowner(struct nfs4_stateowner *sop) +unhash_stateowner(struct nfs4_stateowner *sop) { struct nfs4_stateid *stp; @@ -816,16 +916,22 @@ release_stateowner(struct nfs4_stateowner *sop) list_del(&sop->so_strhash); list_del(&sop->so_perclient); list_del(&sop->so_perlockowner); - list_del(&sop->so_close_lru); del_perclient++; while (!list_empty(&sop->so_perfilestate)) { stp = list_entry(sop->so_perfilestate.next, struct nfs4_stateid, st_perfilestate); - if(sop->so_is_open_owner) + if (sop->so_is_open_owner) release_stateid(stp, OPEN_STATE); else release_stateid(stp, LOCK_STATE); } +} + +static void +release_stateowner(struct nfs4_stateowner *sop) +{ + unhash_stateowner(sop); + list_del(&sop->so_close_lru); free_stateowner(sop); } @@ -860,14 +966,12 @@ release_stateid(struct nfs4_stateid *stp, int flags) { list_del_perfile++; list_del(&stp->st_perfile); list_del(&stp->st_perfilestate); - if((stp->st_vfs_set) && (flags & OPEN_STATE)) { + if ((stp->st_vfs_set) && (flags & OPEN_STATE)) { release_stateid_lockowner(stp); - nfsd_close(&stp->st_vfs_file); + nfsd_close(stp->st_vfs_file); vfsclose++; - dput(stp->st_vfs_file.f_dentry); - mntput(stp->st_vfs_file.f_vfsmnt); } else if ((stp->st_vfs_set) && (flags & LOCK_STATE)) { - struct file *filp = &stp->st_vfs_file; + struct file *filp = stp->st_vfs_file; locks_remove_posix(filp, (fl_owner_t) stp->st_stateowner); } @@ -888,13 +992,10 @@ void move_to_close_lru(struct nfs4_stateowner *sop) { dprintk("NFSD: move_to_close_lru nfs4_stateowner %p\n", sop); - /* remove stateowner from all other hash lists except perclient */ - list_del_init(&sop->so_idhash); - list_del_init(&sop->so_strhash); - list_del_init(&sop->so_perlockowner); - list_add_tail(&sop->so_close_lru, &close_lru); - sop->so_time = get_seconds(); + unhash_stateowner(sop); + list_add_tail(&sop->so_close_lru, &close_lru); + sop->so_time = get_seconds(); } void @@ -932,7 +1033,7 @@ find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open, struct nf struct nfs4_stateowner *local = NULL; list_for_each_entry(local, &ownerstr_hashtbl[hashval], so_strhash) { - if(!cmp_owner_str(local, &open->op_owner, &open->op_clientid)) + if (!cmp_owner_str(local, &open->op_owner, &open->op_clientid)) continue; *op = local; return(1); @@ -980,7 +1081,7 @@ set_access(unsigned int *access, unsigned long bmap) { *access = 0; for (i = 1; i < 4; i++) { - if(test_bit(i, &bmap)) + if (test_bit(i, &bmap)) *access |= i; } } @@ -991,7 +1092,7 @@ set_deny(unsigned int *deny, unsigned long bmap) { *deny = 0; for (i = 0; i < 4; i++) { - if(test_bit(i, &bmap)) + if (test_bit(i, &bmap)) *deny |= i ; } } @@ -1040,10 +1141,9 @@ int status; if (share_access & NFS4_SHARE_ACCESS_WRITE) { status = get_write_access(filp->f_dentry->d_inode); - if (!status) - filp->f_mode = FMODE_WRITE; - else + if (status) return nfserrno(status); + filp->f_mode = (filp->f_mode | FMODE_WRITE) & ~FMODE_READ; } return nfs_ok; } @@ -1053,7 +1153,7 @@ nfs4_file_downgrade(struct file *filp, unsigned int share_access) { if (share_access & NFS4_SHARE_ACCESS_WRITE) { put_write_access(filp->f_dentry->d_inode); - filp->f_mode = FMODE_READ; + filp->f_mode = (filp->f_mode | FMODE_READ) & ~FMODE_WRITE; } } @@ -1181,7 +1281,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf /* Search for conflicting share reservations */ status = nfserr_share_denied; list_for_each_entry(stq, &fp->fi_perfile, st_perfile) { - if(stq->st_stateowner == sop) { + if (stq->st_stateowner == sop) { stp = stq; continue; } @@ -1216,8 +1316,6 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf goto out_free; vfsopen++; - dget(stp->st_vfs_file.f_dentry); - mntget(stp->st_vfs_file.f_vfsmnt); init_stateid(stp, fp, sop, open); stp->st_vfs_set = 1; @@ -1232,7 +1330,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf share_access &= open->op_share_access; /* update the struct file */ - if ((status = nfs4_file_upgrade(&stp->st_vfs_file, share_access))) + if ((status = nfs4_file_upgrade(stp->st_vfs_file, share_access))) goto out; /* remember the open */ set_bit(open->op_share_access, &stp->st_access_bmap); @@ -1361,7 +1459,8 @@ nfs4_laundromat(void) } dprintk("NFSD: purging unused open stateowner (so_id %d)\n", sop->so_id); - release_stateowner(sop); + list_del(&sop->so_close_lru); + free_stateowner(sop); } if (clientid_val < NFSD_LAUNDROMAT_MINTIMEOUT) clientid_val = NFSD_LAUNDROMAT_MINTIMEOUT; @@ -1389,7 +1488,7 @@ find_openstateowner_id(u32 st_id, int flags) { dprintk("NFSD: find_openstateowner_id %d\n", st_id); if (flags & CLOSE_STATE) { list_for_each_entry(local, &close_lru, so_close_lru) { - if(local->so_id == st_id) + if (local->so_id == st_id) return local; } } @@ -1400,7 +1499,7 @@ static inline int nfs4_check_fh(struct svc_fh *fhp, struct nfs4_stateid *stp) { return (stp->st_vfs_set == 0 || - fhp->fh_dentry->d_inode != stp->st_vfs_file.f_dentry->d_inode); + fhp->fh_dentry->d_inode != stp->st_vfs_file->f_dentry->d_inode); } static int @@ -1510,10 +1609,12 @@ nfs4_preprocess_seqid_op(struct svc_fh *current_fh, u32 seqid, stateid_t *statei status = nfserr_bad_stateid; - /* for new lock stateowners, check that the lock->v.new.open_stateid - * refers to an open stateowner, and that the lockclid - * (nfs4_lock->v.new.clientid) is the same as the - * open_stateid->st_stateowner->so_client->clientid + /* for new lock stateowners: + * check that the lock->v.new.open_stateid + * refers to an open stateowner + * + * check that the lockclid (nfs4_lock->v.new.clientid) is the same + * as the open_stateid->st_stateowner->so_client->clientid */ if (lockclid) { struct nfs4_stateowner *sop = stp->st_stateowner; @@ -1598,6 +1699,17 @@ check_replay: goto out; } +/* + * eventually, this will perform an upcall to the 'state daemon' as well as + * set the cl_first_state field. + */ +void +first_state(struct nfs4_client *clp) +{ + if (!clp->cl_first_state) + clp->cl_first_state = get_seconds(); +} + /* * nfs4_unlock_state(); called in encode */ @@ -1635,6 +1747,7 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfs stp->st_stateid.si_fileid, stp->st_stateid.si_generation); status = nfs_ok; + first_state(sop->so_client); out: return status; } @@ -1703,7 +1816,7 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct n goto out; } set_access(&share_access, stp->st_access_bmap); - nfs4_file_downgrade(&stp->st_vfs_file, + nfs4_file_downgrade(stp->st_vfs_file, share_access & ~od->od_share_access); reset_union_bmap_access(od->od_share_access, &stp->st_access_bmap); @@ -1779,16 +1892,16 @@ find_stateid(stateid_t *stid, int flags) if ((flags & LOCK_STATE) || (flags & RDWR_STATE)) { hashval = stateid_hashval(st_id, f_id); list_for_each_entry(local, &lockstateid_hashtbl[hashval], st_hash) { - if((local->st_stateid.si_stateownerid == st_id) && - (local->st_stateid.si_fileid == f_id)) + if ((local->st_stateid.si_stateownerid == st_id) && + (local->st_stateid.si_fileid == f_id)) return local; } } if ((flags & OPEN_STATE) || (flags & RDWR_STATE)) { hashval = stateid_hashval(st_id, f_id); list_for_each_entry(local, &stateid_hashtbl[hashval], st_hash) { - if((local->st_stateid.si_stateownerid == st_id) && - (local->st_stateid.si_fileid == f_id)) + if ((local->st_stateid.si_stateownerid == st_id) && + (local->st_stateid.si_fileid == f_id)) return local; } } else @@ -1837,9 +1950,10 @@ static inline void nfs4_set_lock_denied(struct file_lock *fl, struct nfsd4_lock_denied *deny) { struct nfs4_stateowner *sop = (struct nfs4_stateowner *) fl->fl_owner; + unsigned int hval = lockownerid_hashval(sop->so_id); deny->ld_sop = NULL; - if (nfs4_verify_lock_stateowner(sop, fl->fl_pid)) + if (nfs4_verify_lock_stateowner(sop, hval)) deny->ld_sop = sop; deny->ld_start = fl->fl_start; deny->ld_length = ~(u64)0; @@ -1850,13 +1964,28 @@ nfs4_set_lock_denied(struct file_lock *fl, struct nfsd4_lock_denied *deny) deny->ld_type = NFS4_WRITE_LT; } +static struct nfs4_stateowner * +find_lockstateowner(struct xdr_netobj *owner, clientid_t *clid) +{ + struct nfs4_stateowner *local = NULL; + int i; + + for (i = 0; i < LOCK_HASH_SIZE; i++) { + list_for_each_entry(local, &lock_ownerid_hashtbl[i], so_idhash) { + if (!cmp_owner_str(local, owner, clid)) + continue; + return local; + } + } + return NULL; +} static int find_lockstateowner_str(unsigned int hashval, struct xdr_netobj *owner, clientid_t *clid, struct nfs4_stateowner **op) { struct nfs4_stateowner *local = NULL; list_for_each_entry(local, &lock_ownerstr_hashtbl[hashval], so_strhash) { - if(!cmp_owner_str(local, owner, clid)) + if (!cmp_owner_str(local, owner, clid)) continue; *op = local; return(1); @@ -1881,7 +2010,7 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, str unsigned int idhashval; if (!(sop = alloc_stateowner(&lock->lk_new_owner))) - return (struct nfs4_stateowner *)NULL; + return NULL; idhashval = lockownerid_hashval(current_ownerid); INIT_LIST_HEAD(&sop->so_idhash); INIT_LIST_HEAD(&sop->so_strhash); @@ -1931,7 +2060,7 @@ alloc_init_lock_stateid(struct nfs4_stateowner *sop, struct nfs4_file *fp, struc stp->st_stateid.si_stateownerid = sop->so_id; stp->st_stateid.si_fileid = fp->fi_id; stp->st_stateid.si_generation = 0; - stp->st_vfs_file = open_stp->st_vfs_file; + stp->st_vfs_file = open_stp->st_vfs_file; /* FIXME refcount?? */ stp->st_vfs_set = open_stp->st_vfs_set; stp->st_access_bmap = open_stp->st_access_bmap; stp->st_deny_bmap = open_stp->st_deny_bmap; @@ -1944,7 +2073,7 @@ int check_lock_length(u64 offset, u64 length) { return ((length == 0) || ((length != ~(u64)0) && - LOFF_OVERFLOW(offset, length))); + LOFF_OVERFLOW(offset, length))); } /* @@ -1969,7 +2098,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock if (nfs4_in_grace() && !lock->lk_reclaim) return nfserr_grace; - if (nfs4_in_no_grace() && lock->lk_reclaim) + if (!nfs4_in_grace() && lock->lk_reclaim) return nfserr_no_grace; if (check_lock_length(lock->lk_offset, lock->lk_length)) @@ -1992,7 +2121,11 @@ nfsd4_lock(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock printk("NFSD: nfsd4_lock: clientid is stale!\n"); goto out; } - /* does the clientid in the lock owner own the open stateid? */ + + /* is the new lock seqid presented by the client zero? */ + status = nfserr_bad_seqid; + if (lock->v.new.lock_seqid != 0) + goto out; /* validate and update open stateid and open seqid */ status = nfs4_preprocess_seqid_op(current_fh, @@ -2011,22 +2144,24 @@ nfsd4_lock(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock strhashval = lock_ownerstr_hashval(fp->fi_inode, open_sop->so_client->cl_clientid.cl_id, lock->v.new.owner); - /* * If we already have this lock owner, the client is in * error (or our bookeeping is wrong!) * for asking for a 'new lock'. */ status = nfserr_bad_stateid; - if (find_lockstateowner_str(strhashval, &lock->v.new.owner, - &lock->v.new.clientid, &lock_sop)) + lock_sop = find_lockstateowner(&lock->v.new.owner, + &lock->v.new.clientid); + if (lock_sop) goto out; status = nfserr_resource; if (!(lock->lk_stateowner = alloc_init_lock_stateowner(strhashval, open_sop->so_client, open_stp, lock))) goto out; if ((lock_stp = alloc_init_lock_stateid(lock->lk_stateowner, - fp, open_stp)) == NULL) + fp, open_stp)) == NULL) { + release_stateowner(lock->lk_stateowner); goto out; + } /* bump the open seqid used to create the lock */ open_sop->so_seqid++; } else { @@ -2040,13 +2175,14 @@ nfsd4_lock(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock goto out; } /* lock->lk_stateowner and lock_stp have been created or found */ - filp = &lock_stp->st_vfs_file; + filp = lock_stp->st_vfs_file; if ((status = fh_verify(rqstp, current_fh, S_IFREG, MAY_LOCK))) { printk("NFSD: nfsd4_lock: permission denied!\n"); goto out; } + locks_init_lock(&file_lock); switch (lock->lk_type) { case NFS4_READ_LT: case NFS4_READW_LT: @@ -2061,12 +2197,9 @@ nfsd4_lock(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock goto out; } file_lock.fl_owner = (fl_owner_t) lock->lk_stateowner; - file_lock.fl_pid = lockownerid_hashval(lock->lk_stateowner->so_id); + file_lock.fl_pid = current->tgid; file_lock.fl_file = filp; file_lock.fl_flags = FL_POSIX; - file_lock.fl_notify = NULL; - file_lock.fl_insert = NULL; - file_lock.fl_remove = NULL; file_lock.fl_start = lock->lk_offset; if ((lock->lk_length == ~(u64)0) || @@ -2082,7 +2215,9 @@ nfsd4_lock(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock */ status = posix_lock_file(filp, &file_lock); - dprintk("NFSD: nfsd4_lock: posix_test_lock passed. posix_lock_file status %d\n",status); + if (file_lock.fl_ops && file_lock.fl_ops->fl_release_private) + file_lock.fl_ops->fl_release_private(&file_lock); + dprintk("NFSD: nfsd4_lock: posix_lock_file status %d\n",status); switch (-status) { case 0: /* success! */ update_stateid(&lock_stp->st_stateid); @@ -2133,7 +2268,6 @@ int nfsd4_lockt(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lockt *lockt) { struct inode *inode; - struct nfs4_stateowner *sop; struct file file; struct file_lock file_lock; struct file_lock *conflicting_lock; @@ -2163,6 +2297,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock } inode = current_fh->fh_dentry->d_inode; + locks_init_lock(&file_lock); switch (lockt->lt_type) { case NFS4_READ_LT: case NFS4_READW_LT: @@ -2184,14 +2319,9 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock find_lockstateowner_str(strhashval, &lockt->lt_owner, &lockt->lt_clientid, &lockt->lt_stateowner); - sop = lockt->lt_stateowner; - if (sop) { - file_lock.fl_owner = (fl_owner_t) sop; - file_lock.fl_pid = lockownerid_hashval(sop->so_id); - } else { - file_lock.fl_owner = NULL; - file_lock.fl_pid = 0; - } + if (lockt->lt_stateowner) + file_lock.fl_owner = (fl_owner_t)lockt->lt_stateowner; + file_lock.fl_pid = current->tgid; file_lock.fl_flags = FL_POSIX; file_lock.fl_start = lockt->lt_offset; @@ -2246,16 +2376,14 @@ nfsd4_locku(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock &locku->lu_stateowner, &stp, NULL))) goto out; - filp = &stp->st_vfs_file; + filp = stp->st_vfs_file; BUG_ON(!filp); + locks_init_lock(&file_lock); file_lock.fl_type = F_UNLCK; file_lock.fl_owner = (fl_owner_t) locku->lu_stateowner; - file_lock.fl_pid = lockownerid_hashval(locku->lu_stateowner->so_id); + file_lock.fl_pid = current->tgid; file_lock.fl_file = filp; file_lock.fl_flags = FL_POSIX; - file_lock.fl_notify = NULL; - file_lock.fl_insert = NULL; - file_lock.fl_remove = NULL; file_lock.fl_start = locku->lu_offset; if ((locku->lu_length == ~(u64)0) || LOFF_OVERFLOW(locku->lu_offset, locku->lu_length)) @@ -2268,6 +2396,8 @@ nfsd4_locku(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock * Try to unlock the file in the VFS. */ status = posix_lock_file(filp, &file_lock); + if (file_lock.fl_ops && file_lock.fl_ops->fl_release_private) + file_lock.fl_ops->fl_release_private(&file_lock); if (status) { printk("NFSD: nfs4_locku: posix_lock_file failed!\n"); goto out_nfserr; @@ -2315,7 +2445,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, struct nfsd4_release_lockowner * clientid_t *clid = &rlockowner->rl_clientid; struct nfs4_stateowner *local = NULL; struct xdr_netobj *owner = &rlockowner->rl_owner; - int status, i; + int status; dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n", clid->cl_boot, clid->cl_id); @@ -2330,34 +2460,136 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, struct nfsd4_release_lockowner * nfs4_lock_state(); - /* find the lockowner */ - status = nfs_ok; - for (i=0; i < LOCK_HASH_SIZE; i++) - list_for_each_entry(local, &lock_ownerstr_hashtbl[i], so_strhash) - if(cmp_owner_str(local, owner, clid)) { - struct nfs4_stateid *stp; - - /* check for any locks held by any stateid - * associated with the (lock) stateowner */ - status = nfserr_locks_held; - list_for_each_entry(stp, &local->so_perfilestate, - st_perfilestate) { - if(stp->st_vfs_set) { - if (check_for_locks(&stp->st_vfs_file, - local)) - goto out; - } - } - /* no locks held by (lock) stateowner */ - status = nfs_ok; - release_stateowner(local); - goto out; + status = nfs_ok; + local = find_lockstateowner(owner, clid); + if (local) { + struct nfs4_stateid *stp; + + /* check for any locks held by any stateid + * associated with the (lock) stateowner */ + status = nfserr_locks_held; + list_for_each_entry(stp, &local->so_perfilestate, + st_perfilestate) { + if (stp->st_vfs_set) { + if (check_for_locks(stp->st_vfs_file, local)) + goto out; } + } + /* no locks held by (lock) stateowner */ + status = nfs_ok; + release_stateowner(local); + } out: nfs4_unlock_state(); return status; } +static inline struct nfs4_client_reclaim * +alloc_reclaim(int namelen) +{ + struct nfs4_client_reclaim *crp = NULL; + + crp = kmalloc(sizeof(struct nfs4_client_reclaim), GFP_KERNEL); + if (!crp) + return NULL; + crp->cr_name.data = kmalloc(namelen, GFP_KERNEL); + if (!crp->cr_name.data) { + kfree(crp); + return NULL; + } + return crp; +} + +/* + * failure => all reset bets are off, nfserr_no_grace... + */ +static int +nfs4_client_to_reclaim(struct nfs4_client *clp) +{ + unsigned int strhashval; + struct nfs4_client_reclaim *crp = NULL; + + crp = alloc_reclaim(clp->cl_name.len); + if (!crp) + return 0; + strhashval = clientstr_hashval(clp->cl_name.data, clp->cl_name.len); + INIT_LIST_HEAD(&crp->cr_strhash); + list_add(&crp->cr_strhash, &reclaim_str_hashtbl[strhashval]); + memcpy(crp->cr_name.data, clp->cl_name.data, clp->cl_name.len); + crp->cr_name.len = clp->cl_name.len; + crp->cr_first_state = clp->cl_first_state; + crp->cr_expired = 0; + return 1; +} + +static void +nfs4_release_reclaim(void) +{ + struct nfs4_client_reclaim *crp = NULL; + int i; + + BUG_ON(!nfs4_reclaim_init); + for (i = 0; i < CLIENT_HASH_SIZE; i++) { + while (!list_empty(&reclaim_str_hashtbl[i])) { + crp = list_entry(reclaim_str_hashtbl[i].next, + struct nfs4_client_reclaim, cr_strhash); + list_del(&crp->cr_strhash); + kfree(crp->cr_name.data); + kfree(crp); + reclaim_str_hashtbl_size--; + } + } + BUG_ON(reclaim_str_hashtbl_size); +} + +/* + * called from OPEN, CLAIM_PREVIOUS with a new clientid. */ +struct nfs4_client_reclaim * +nfs4_find_reclaim_client(clientid_t *clid) +{ + unsigned int idhashval = clientid_hashval(clid->cl_id); + unsigned int strhashval; + struct nfs4_client *clp, *client = NULL; + struct nfs4_client_reclaim *crp = NULL; + + + /* find clientid in conf_id_hashtbl */ + list_for_each_entry(clp, &conf_id_hashtbl[idhashval], cl_idhash) { + if (cmp_clid(&clp->cl_clientid, clid)) { + client = clp; + break; + } + } + if (!client) + return NULL; + + /* find clp->cl_name in reclaim_str_hashtbl */ + strhashval = clientstr_hashval(client->cl_name.data, + client->cl_name.len); + list_for_each_entry(crp, &reclaim_str_hashtbl[strhashval], cr_strhash) { + if (cmp_name(&crp->cr_name, &client->cl_name)) { + return crp; + } + } + return NULL; +} + +/* +* Called from OPEN. Look for clientid in reclaim list. +*/ +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; + if (crp->cr_expired) + return nfserr_no_grace; + return nfs_ok; +} + + /* * Start and stop routines */ @@ -2366,10 +2598,16 @@ void nfs4_state_init(void) { int i; - time_t start = get_seconds(); + time_t grace_time; if (nfs4_init) return; + 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]); @@ -2396,27 +2634,36 @@ nfs4_state_init(void) INIT_LIST_HEAD(&close_lru); INIT_LIST_HEAD(&client_lru); - init_MUTEX(&client_sema); - boot_time = start; - grace_end = start + NFSD_LEASE_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_end = boot_time + grace_time; INIT_WORK(&laundromat_work,laundromat_main, NULL); schedule_delayed_work(&laundromat_work, NFSD_LEASE_TIME*HZ); nfs4_init = 1; - } int nfs4_in_grace(void) { - return time_before(get_seconds(), (unsigned long)grace_end); + return get_seconds() < grace_end; } -int -nfs4_in_no_grace(void) +void +set_no_grace(void) { - return (grace_end < get_seconds()); + printk("NFSD: ERROR in reboot recovery. State reclaims will fail.\n"); + grace_end = get_seconds(); } +time_t +nfs4_lease_time(void) +{ + return lease_time; +} static void __nfs4_state_shutdown(void) @@ -2454,6 +2701,61 @@ void nfs4_state_shutdown(void) { nfs4_lock_state(); + nfs4_release_reclaim(); + __nfs4_state_shutdown(); + nfs4_unlock_state(); +} + +/* + * 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. + */ +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)) { + nfs4_release_reclaim(); + goto init_state; + } + reclaim_str_hashtbl_size++; + } + } +init_state: __nfs4_state_shutdown(); + nfs4_state_init(); nfs4_unlock_state(); } +