Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

nfsd: common grace period control

Rewrite grace period code to unify management of grace period across
lockd and nfsd. The current code has lockd and nfsd cooperate to
compute a grace period which is satisfactory to them both, and then
individually enforce it. This creates a slight race condition, since
the enforcement is not coordinated. It's also more complicated than
necessary.

Here instead we have lockd and nfsd each inform common code when they
enter the grace period, and when they're ready to leave the grace
period, and allow normal locking only after both of them are ready to
leave.

We also expect the locks_start_grace()/locks_end_grace() interface here
to be simpler to build on for future cluster/high-availability work,
which may require (for example) putting individual filesystems into
grace, or enforcing grace periods across multiple cluster nodes.

Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>

+104 -61
+1 -1
fs/lockd/Makefile
··· 5 5 obj-$(CONFIG_LOCKD) += lockd.o 6 6 7 7 lockd-objs-y := clntlock.o clntproc.o host.o svc.o svclock.o svcshare.o \ 8 - svcproc.o svcsubs.o mon.o xdr.o 8 + svcproc.o svcsubs.o mon.o xdr.o grace.o 9 9 lockd-objs-$(CONFIG_LOCKD_V4) += xdr4.o svc4proc.o 10 10 lockd-objs := $(lockd-objs-y)
+59
fs/lockd/grace.c
··· 1 + /* 2 + * Common code for control of lockd and nfsv4 grace periods. 3 + */ 4 + 5 + #include <linux/module.h> 6 + #include <linux/lockd/bind.h> 7 + 8 + static LIST_HEAD(grace_list); 9 + static DEFINE_SPINLOCK(grace_lock); 10 + 11 + /** 12 + * locks_start_grace 13 + * @lm: who this grace period is for 14 + * 15 + * A grace period is a period during which locks should not be given 16 + * out. Currently grace periods are only enforced by the two lock 17 + * managers (lockd and nfsd), using the locks_in_grace() function to 18 + * check when they are in a grace period. 19 + * 20 + * This function is called to start a grace period. 21 + */ 22 + void locks_start_grace(struct lock_manager *lm) 23 + { 24 + spin_lock(&grace_lock); 25 + list_add(&lm->list, &grace_list); 26 + spin_unlock(&grace_lock); 27 + } 28 + EXPORT_SYMBOL_GPL(locks_start_grace); 29 + 30 + /** 31 + * locks_end_grace 32 + * @lm: who this grace period is for 33 + * 34 + * Call this function to state that the given lock manager is ready to 35 + * resume regular locking. The grace period will not end until all lock 36 + * managers that called locks_start_grace() also call locks_end_grace(). 37 + * Note that callers count on it being safe to call this more than once, 38 + * and the second call should be a no-op. 39 + */ 40 + void locks_end_grace(struct lock_manager *lm) 41 + { 42 + spin_lock(&grace_lock); 43 + list_del_init(&lm->list); 44 + spin_unlock(&grace_lock); 45 + } 46 + EXPORT_SYMBOL_GPL(locks_end_grace); 47 + 48 + /** 49 + * locks_in_grace 50 + * 51 + * Lock managers call this function to determine when it is OK for them 52 + * to answer ordinary lock requests, and when they should accept only 53 + * lock reclaims. 54 + */ 55 + int locks_in_grace(void) 56 + { 57 + return !list_empty(&grace_list); 58 + } 59 + EXPORT_SYMBOL_GPL(locks_in_grace);
+5 -15
fs/lockd/svc.c
··· 51 51 static unsigned int nlmsvc_users; 52 52 static struct task_struct *nlmsvc_task; 53 53 static struct svc_rqst *nlmsvc_rqst; 54 - int nlmsvc_grace_period; 55 54 unsigned long nlmsvc_timeout; 56 55 57 56 /* ··· 84 85 return nlm_timeout * 5 * HZ; 85 86 } 86 87 87 - unsigned long get_nfs_grace_period(void) 88 - { 89 - unsigned long lockdgrace = get_lockd_grace_period(); 90 - unsigned long nfsdgrace = 0; 91 - 92 - if (nlmsvc_ops) 93 - nfsdgrace = nlmsvc_ops->get_grace_period(); 94 - 95 - return max(lockdgrace, nfsdgrace); 96 - } 97 - EXPORT_SYMBOL(get_nfs_grace_period); 88 + static struct lock_manager lockd_manager = { 89 + }; 98 90 99 91 static void grace_ender(struct work_struct *not_used) 100 92 { 101 - nlmsvc_grace_period = 0; 93 + locks_end_grace(&lockd_manager); 102 94 } 103 95 104 96 static DECLARE_DELAYED_WORK(grace_period_end, grace_ender); 105 97 106 98 static void set_grace_period(void) 107 99 { 108 - unsigned long grace_period = get_nfs_grace_period() + jiffies; 100 + unsigned long grace_period = get_lockd_grace_period(); 109 101 110 - nlmsvc_grace_period = 1; 102 + locks_start_grace(&lockd_manager); 111 103 cancel_delayed_work_sync(&grace_period_end); 112 104 schedule_delayed_work(&grace_period_end, grace_period); 113 105 }
+6 -6
fs/lockd/svc4proc.c
··· 89 89 resp->cookie = argp->cookie; 90 90 91 91 /* Don't accept test requests during grace period */ 92 - if (nlmsvc_grace_period) { 92 + if (locks_in_grace()) { 93 93 resp->status = nlm_lck_denied_grace_period; 94 94 return rc; 95 95 } ··· 123 123 resp->cookie = argp->cookie; 124 124 125 125 /* Don't accept new lock requests during grace period */ 126 - if (nlmsvc_grace_period && !argp->reclaim) { 126 + if (locks_in_grace() && !argp->reclaim) { 127 127 resp->status = nlm_lck_denied_grace_period; 128 128 return rc; 129 129 } ··· 169 169 resp->cookie = argp->cookie; 170 170 171 171 /* Don't accept requests during grace period */ 172 - if (nlmsvc_grace_period) { 172 + if (locks_in_grace()) { 173 173 resp->status = nlm_lck_denied_grace_period; 174 174 return rpc_success; 175 175 } ··· 202 202 resp->cookie = argp->cookie; 203 203 204 204 /* Don't accept new lock requests during grace period */ 205 - if (nlmsvc_grace_period) { 205 + if (locks_in_grace()) { 206 206 resp->status = nlm_lck_denied_grace_period; 207 207 return rpc_success; 208 208 } ··· 341 341 resp->cookie = argp->cookie; 342 342 343 343 /* Don't accept new lock requests during grace period */ 344 - if (nlmsvc_grace_period && !argp->reclaim) { 344 + if (locks_in_grace() && !argp->reclaim) { 345 345 resp->status = nlm_lck_denied_grace_period; 346 346 return rpc_success; 347 347 } ··· 374 374 resp->cookie = argp->cookie; 375 375 376 376 /* Don't accept requests during grace period */ 377 - if (nlmsvc_grace_period) { 377 + if (locks_in_grace()) { 378 378 resp->status = nlm_lck_denied_grace_period; 379 379 return rpc_success; 380 380 }
+6 -6
fs/lockd/svcproc.c
··· 118 118 resp->cookie = argp->cookie; 119 119 120 120 /* Don't accept test requests during grace period */ 121 - if (nlmsvc_grace_period) { 121 + if (locks_in_grace()) { 122 122 resp->status = nlm_lck_denied_grace_period; 123 123 return rc; 124 124 } ··· 153 153 resp->cookie = argp->cookie; 154 154 155 155 /* Don't accept new lock requests during grace period */ 156 - if (nlmsvc_grace_period && !argp->reclaim) { 156 + if (locks_in_grace() && !argp->reclaim) { 157 157 resp->status = nlm_lck_denied_grace_period; 158 158 return rc; 159 159 } ··· 199 199 resp->cookie = argp->cookie; 200 200 201 201 /* Don't accept requests during grace period */ 202 - if (nlmsvc_grace_period) { 202 + if (locks_in_grace()) { 203 203 resp->status = nlm_lck_denied_grace_period; 204 204 return rpc_success; 205 205 } ··· 232 232 resp->cookie = argp->cookie; 233 233 234 234 /* Don't accept new lock requests during grace period */ 235 - if (nlmsvc_grace_period) { 235 + if (locks_in_grace()) { 236 236 resp->status = nlm_lck_denied_grace_period; 237 237 return rpc_success; 238 238 } ··· 373 373 resp->cookie = argp->cookie; 374 374 375 375 /* Don't accept new lock requests during grace period */ 376 - if (nlmsvc_grace_period && !argp->reclaim) { 376 + if (locks_in_grace() && !argp->reclaim) { 377 377 resp->status = nlm_lck_denied_grace_period; 378 378 return rpc_success; 379 379 } ··· 406 406 resp->cookie = argp->cookie; 407 407 408 408 /* Don't accept requests during grace period */ 409 - if (nlmsvc_grace_period) { 409 + if (locks_in_grace()) { 410 410 resp->status = nlm_lck_denied_grace_period; 411 411 return rpc_success; 412 412 }
-1
fs/nfsd/lockd.c
··· 70 70 static struct nlmsvc_binding nfsd_nlm_ops = { 71 71 .fopen = nlm_fopen, /* open file for locking */ 72 72 .fclose = nlm_fclose, /* close file */ 73 - .get_grace_period = get_nfs4_grace_period, 74 73 }; 75 74 76 75 void
+4 -4
fs/nfsd/nfs4proc.c
··· 201 201 /* Openowner is now set, so sequence id will get bumped. Now we need 202 202 * these checks before we do any creates: */ 203 203 status = nfserr_grace; 204 - if (nfs4_in_grace() && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS) 204 + if (locks_in_grace() && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS) 205 205 goto out; 206 206 status = nfserr_no_grace; 207 - if (!nfs4_in_grace() && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS) 207 + if (!locks_in_grace() && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS) 208 208 goto out; 209 209 210 210 switch (open->op_claim_type) { ··· 575 575 { 576 576 __be32 status; 577 577 578 - if (nfs4_in_grace()) 578 + if (locks_in_grace()) 579 579 return nfserr_grace; 580 580 status = nfsd_unlink(rqstp, &cstate->current_fh, 0, 581 581 remove->rm_name, remove->rm_namelen); ··· 596 596 597 597 if (!cstate->save_fh.fh_dentry) 598 598 return status; 599 - if (nfs4_in_grace() && !(cstate->save_fh.fh_export->ex_flags 599 + if (locks_in_grace() && !(cstate->save_fh.fh_export->ex_flags 600 600 & NFSEXP_NOSUBTREECHECK)) 601 601 return nfserr_grace; 602 602 status = nfsd_rename(rqstp, &cstate->save_fh, rename->rn_sname,
+15 -19
fs/nfsd/nfs4state.c
··· 61 61 static time_t lease_time = 90; /* default lease time */ 62 62 static time_t user_lease_time = 90; 63 63 static time_t boot_time; 64 - static int in_grace = 1; 65 64 static u32 current_ownerid = 1; 66 65 static u32 current_fileid = 1; 67 66 static u32 current_delegid = 1; ··· 1639 1640 case NFS4_OPEN_CLAIM_NULL: 1640 1641 /* Let's not give out any delegations till everyone's 1641 1642 * had the chance to reclaim theirs.... */ 1642 - if (nfs4_in_grace()) 1643 + if (locks_in_grace()) 1643 1644 goto out; 1644 1645 if (!atomic_read(&cb->cb_set) || !sop->so_confirmed) 1645 1646 goto out; ··· 1815 1816 return status; 1816 1817 } 1817 1818 1819 + struct lock_manager nfsd4_manager = { 1820 + }; 1821 + 1818 1822 static void 1819 - end_grace(void) 1823 + nfsd4_end_grace(void) 1820 1824 { 1821 1825 dprintk("NFSD: end of grace period\n"); 1822 1826 nfsd4_recdir_purge_old(); 1823 - in_grace = 0; 1827 + locks_end_grace(&nfsd4_manager); 1824 1828 } 1825 1829 1826 1830 static time_t ··· 1840 1838 nfs4_lock_state(); 1841 1839 1842 1840 dprintk("NFSD: laundromat service - starting\n"); 1843 - if (in_grace) 1844 - end_grace(); 1841 + if (locks_in_grace()) 1842 + nfsd4_end_grace(); 1845 1843 list_for_each_safe(pos, next, &client_lru) { 1846 1844 clp = list_entry(pos, struct nfs4_client, cl_lru); 1847 1845 if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) { ··· 1976 1974 return nfserr_bad_stateid; 1977 1975 else if (ONE_STATEID(stateid) && (flags & RD_STATE)) 1978 1976 return nfs_ok; 1979 - else if (nfs4_in_grace()) { 1977 + else if (locks_in_grace()) { 1980 1978 /* Answer in remaining cases depends on existance of 1981 1979 * conflicting state; so we must wait out the grace period. */ 1982 1980 return nfserr_grace; ··· 1995 1993 static inline int 1996 1994 io_during_grace_disallowed(struct inode *inode, int flags) 1997 1995 { 1998 - return nfs4_in_grace() && (flags & (RD_STATE | WR_STATE)) 1996 + return locks_in_grace() && (flags & (RD_STATE | WR_STATE)) 1999 1997 && mandatory_lock(inode); 2000 1998 } 2001 1999 ··· 2695 2693 filp = lock_stp->st_vfs_file; 2696 2694 2697 2695 status = nfserr_grace; 2698 - if (nfs4_in_grace() && !lock->lk_reclaim) 2696 + if (locks_in_grace() && !lock->lk_reclaim) 2699 2697 goto out; 2700 2698 status = nfserr_no_grace; 2701 - if (!nfs4_in_grace() && lock->lk_reclaim) 2699 + if (!locks_in_grace() && lock->lk_reclaim) 2702 2700 goto out; 2703 2701 2704 2702 locks_init_lock(&file_lock); ··· 2781 2779 int error; 2782 2780 __be32 status; 2783 2781 2784 - if (nfs4_in_grace()) 2782 + if (locks_in_grace()) 2785 2783 return nfserr_grace; 2786 2784 2787 2785 if (check_lock_length(lockt->lt_offset, lockt->lt_length)) ··· 3194 3192 unsigned long grace_time; 3195 3193 3196 3194 boot_time = get_seconds(); 3197 - grace_time = get_nfs_grace_period(); 3195 + grace_time = get_nfs4_grace_period(); 3198 3196 lease_time = user_lease_time; 3199 - in_grace = 1; 3197 + locks_start_grace(&nfsd4_manager); 3200 3198 printk(KERN_INFO "NFSD: starting %ld-second grace period\n", 3201 3199 grace_time/HZ); 3202 3200 laundry_wq = create_singlethread_workqueue("nfsd4"); ··· 3213 3211 __nfs4_state_start(); 3214 3212 nfs4_init = 1; 3215 3213 return; 3216 - } 3217 - 3218 - int 3219 - nfs4_in_grace(void) 3220 - { 3221 - return in_grace; 3222 3214 } 3223 3215 3224 3216 time_t
+8
include/linux/fs.h
··· 942 942 int (*fl_change)(struct file_lock **, int); 943 943 }; 944 944 945 + struct lock_manager { 946 + struct list_head list; 947 + }; 948 + 949 + void locks_start_grace(struct lock_manager *); 950 + void locks_end_grace(struct lock_manager *); 951 + int locks_in_grace(void); 952 + 945 953 /* that will die - we need it for nfs_lock_info */ 946 954 #include <linux/nfs_fs_i.h> 947 955
-9
include/linux/lockd/bind.h
··· 27 27 struct nfs_fh *, 28 28 struct file **); 29 29 void (*fclose)(struct file *); 30 - unsigned long (*get_grace_period)(void); 31 30 }; 32 31 33 32 extern struct nlmsvc_binding * nlmsvc_ops; ··· 54 55 struct file_lock *fl); 55 56 extern int lockd_up(int proto); 56 57 extern void lockd_down(void); 57 - 58 - unsigned long get_nfs_grace_period(void); 59 - 60 - #ifdef CONFIG_NFSD_V4 61 - unsigned long get_nfs4_grace_period(void); 62 - #else 63 - static inline unsigned long get_nfs4_grace_period(void) {return 0;} 64 - #endif 65 58 66 59 #endif /* LINUX_LOCKD_BIND_H */