From: NeilBrown Allow the lease to be set from /proc/fs/nfs/nfsv4leasetime. To comply with rfc3530, this appears as a server reboot from the point of view of the client, which must reclaim state with the grace period. From: Andy Adamson Signed-off-by: J. Bruce Fields Signed-off-by: Neil Brown Signed-off-by: Andrew Morton --- 25-akpm/fs/nfsd/nfs4proc.c | 9 - 25-akpm/fs/nfsd/nfs4state.c | 222 ++++++++++++++++++++++++++++++++++--- 25-akpm/fs/nfsd/nfsctl.c | 31 +++++ 25-akpm/include/linux/nfsd/nfsd.h | 6 - 25-akpm/include/linux/nfsd/state.h | 15 ++ 5 files changed, 263 insertions(+), 20 deletions(-) diff -puN fs/nfsd/nfs4proc.c~knfsd-allow-user-to-set-nfsv4-lease-time fs/nfsd/nfs4proc.c --- 25/fs/nfsd/nfs4proc.c~knfsd-allow-user-to-set-nfsv4-lease-time 2004-06-23 22:12:23.577082752 -0700 +++ 25-akpm/fs/nfsd/nfs4proc.c 2004-06-23 22:12:23.588081080 -0700 @@ -135,9 +135,11 @@ do_open_fhandle(struct svc_rqst *rqstp, { int status; - dprintk("NFSD: do_open_fhandle\n"); + /* Only reclaims from previously confirmed clients are valid */ + if ((status = nfs4_check_open_reclaim(&open->op_clientid))) + return status; - /* we don't know the target directory, and therefore can not + /* We don't know the target directory, and therefore can not * set the change info */ @@ -172,8 +174,7 @@ nfsd4_open(struct svc_rqst *rqstp, struc if (nfs4_in_grace() && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS) return nfserr_grace; - if (nfs4_in_no_grace() && - open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS) + if (!nfs4_in_grace() && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS) return nfserr_no_grace; /* This check required by spec. */ diff -puN fs/nfsd/nfs4state.c~knfsd-allow-user-to-set-nfsv4-lease-time fs/nfsd/nfs4state.c --- 25/fs/nfsd/nfs4state.c~knfsd-allow-user-to-set-nfsv4-lease-time 2004-06-23 22:12:23.579082448 -0700 +++ 25-akpm/fs/nfsd/nfs4state.c 2004-06-23 22:12:23.591080624 -0700 @@ -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(state * 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_fil ((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_fil * 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]; @@ -1693,6 +1701,17 @@ check_replay: } /* + * 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 */ int @@ -1729,6 +1748,7 @@ nfsd4_open_confirm(struct svc_rqst *rqst stp->st_stateid.si_fileid, stp->st_stateid.si_generation); status = nfs_ok; + first_state(sop->so_client); out: return status; } @@ -2078,7 +2098,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struc 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)) @@ -2467,6 +2487,112 @@ out: 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 */ @@ -2475,10 +2601,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]); @@ -2505,27 +2637,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) @@ -2563,6 +2704,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(); +} + diff -puN fs/nfsd/nfsctl.c~knfsd-allow-user-to-set-nfsv4-lease-time fs/nfsd/nfsctl.c --- 25/fs/nfsd/nfsctl.c~knfsd-allow-user-to-set-nfsv4-lease-time 2004-06-23 22:12:23.580082296 -0700 +++ 25-akpm/fs/nfsd/nfsctl.c 2004-06-23 22:12:23.592080472 -0700 @@ -36,7 +36,7 @@ #include /* - * We have a single directory with 8 nodes in it. + * We have a single directory with 9 nodes in it. */ enum { NFSD_Root = 1, @@ -50,6 +50,7 @@ enum { NFSD_List, NFSD_Fh, NFSD_Threads, + NFSD_Leasetime, }; /* @@ -64,6 +65,7 @@ static ssize_t write_getfd(struct file * static ssize_t write_getfs(struct file *file, char *buf, size_t size); static ssize_t write_filehandle(struct file *file, char *buf, size_t size); static ssize_t write_threads(struct file *file, char *buf, size_t size); +static ssize_t write_leasetime(struct file *file, char *buf, size_t size); static ssize_t (*write_op[])(struct file *, char *, size_t) = { [NFSD_Svc] = write_svc, @@ -75,6 +77,7 @@ static ssize_t (*write_op[])(struct file [NFSD_Getfs] = write_getfs, [NFSD_Fh] = write_filehandle, [NFSD_Threads] = write_threads, + [NFSD_Leasetime] = write_leasetime, }; /* an argresp is stored in an allocated page and holds the @@ -393,6 +396,29 @@ static ssize_t write_threads(struct file return strlen(buf); } +extern time_t nfs4_leasetime(void); + +static ssize_t write_leasetime(struct file *file, char *buf, size_t size) +{ + /* if size > 10 seconds, call + * nfs4_reset_lease() then write out the new lease (seconds) as reply + */ + char *mesg = buf; + int rv; + + if (size > 0) { + int lease; + rv = get_int(&mesg, &lease); + if (rv) + return rv; + if (lease < 10 || lease > 3600) + return -EINVAL; + nfs4_reset_lease(lease); + } + sprintf(buf, "%ld\n", nfs4_lease_time()); + return strlen(buf); +} + /*----------------------------------------------------------------------------*/ /* * populating the filesystem. @@ -411,6 +437,9 @@ static int nfsd_fill_super(struct super_ [NFSD_List] = {"exports", &exports_operations, S_IRUGO}, [NFSD_Fh] = {"filehandle", &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR}, +#ifdef CONFIG_NFSD_V4 + [NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR}, +#endif /* last one */ {""} }; return simple_fill_super(sb, 0x6e667364, nfsd_files); diff -puN include/linux/nfsd/nfsd.h~knfsd-allow-user-to-set-nfsv4-lease-time include/linux/nfsd/nfsd.h --- 25/include/linux/nfsd/nfsd.h~knfsd-allow-user-to-set-nfsv4-lease-time 2004-06-23 22:12:23.582081992 -0700 +++ 25-akpm/include/linux/nfsd/nfsd.h 2004-06-23 22:12:23.593080320 -0700 @@ -126,9 +126,13 @@ int nfsd_permission(struct svc_export * #ifdef CONFIG_NFSD_V4 void nfs4_state_init(void); void nfs4_state_shutdown(void); +time_t nfs4_lease_time(void); +void nfs4_reset_lease(time_t leasetime); #else void static inline nfs4_state_init(void){} void static inline nfs4_state_shutdown(void){} +time_t static inline nfs4_lease_time(void){return 0;} +void static inline nfs4_reset_lease(time_t leasetime){} #endif /* @@ -249,7 +253,7 @@ static inline int is_fsid(struct svc_fh #define COMPOUND_SLACK_SPACE 140 /* OP_GETFH */ #define COMPOUND_ERR_SLACK_SPACE 12 /* OP_SETATTR */ -#define NFSD_LEASE_TIME 60 /* seconds */ +#define NFSD_LEASE_TIME (nfs4_lease_time()) #define NFSD_LAUNDROMAT_MINTIMEOUT 10 /* seconds */ /* diff -puN include/linux/nfsd/state.h~knfsd-allow-user-to-set-nfsv4-lease-time include/linux/nfsd/state.h --- 25/include/linux/nfsd/state.h~knfsd-allow-user-to-set-nfsv4-lease-time 2004-06-23 22:12:23.584081688 -0700 +++ 25-akpm/include/linux/nfsd/state.h 2004-06-23 22:12:23.593080320 -0700 @@ -105,6 +105,19 @@ struct nfs4_client { clientid_t cl_clientid; /* generated by server */ nfs4_verifier cl_confirm; /* generated by server */ struct nfs4_callback cl_callback; /* callback info */ + time_t cl_first_state; /* first state aquisition*/ +}; + +/* struct nfs4_client_reset + * one per old client. Populates reset_str_hashtbl. Filled from conf_id_hashtbl + * upon lease reset, or from upcall to state_daemon (to read in state + * from non-volitile storage) upon reboot. + */ +struct nfs4_client_reclaim { + struct list_head cr_strhash; /* hash by cr_name */ + struct xdr_netobj cr_name; /* id generated by client */ + time_t cr_first_state; /* first state aquisition */ + u32 cr_expired; /* boolean: lease expired? */ }; static inline void @@ -234,5 +247,5 @@ extern int nfs4_share_conflict(struct sv extern void nfs4_lock_state(void); extern void nfs4_unlock_state(void); extern int nfs4_in_grace(void); -extern int nfs4_in_no_grace(void); +extern int nfs4_check_open_reclaim(clientid_t *clid); #endif /* NFSD4_STATE_H */ _