vserver 1.9.3
[linux-2.6.git] / fs / nfsd / nfs4state.c
index 848c485..1ca8ce8 100644 (file)
@@ -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();
 }
+