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

nfsd: use boottime for lease expiry calculation

A couple of time_t variables are only used to track the state of the
lease time and its expiration. The code correctly uses the 'time_after()'
macro to make this work on 32-bit architectures even beyond year 2038,
but the get_seconds() function and the time_t type itself are deprecated
as they behave inconsistently between 32-bit and 64-bit architectures
and often lead to code that is not y2038 safe.

As a minor issue, using get_seconds() leads to problems with concurrent
settimeofday() or clock_settime() calls, in the worst case timeout never
triggering after the time has been set backwards.

Change nfsd to use time64_t and ktime_get_boottime_seconds() here. This
is clearly excessive, as boottime by itself means we never go beyond 32
bits, but it does mean we handle this correctly and consistently without
having to worry about corner cases and should be no more expensive than
the previous implementation on 64-bit architectures.

The max_cb_time() function gets changed in order to avoid an expensive
64-bit division operation, but as the lease time is at most one hour,
there is no change in behavior.

Also do the same for server-to-server copy expiration time.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
[bfields@redhat.com: fix up copy expiration]
Signed-off-by: J. Bruce Fields <bfields@redhat.com>

authored by

Arnd Bergmann and committed by
J. Bruce Fields
20b7d86f 9594497f

+42 -37
+2 -2
fs/nfsd/netns.h
··· 92 92 bool in_grace; 93 93 const struct nfsd4_client_tracking_ops *client_tracking_ops; 94 94 95 - time_t nfsd4_lease; 96 - time_t nfsd4_grace; 95 + time64_t nfsd4_lease; 96 + time64_t nfsd4_grace; 97 97 bool somebody_reclaimed; 98 98 99 99 bool track_reclaim_completes;
+10 -1
fs/nfsd/nfs4callback.c
··· 823 823 static int max_cb_time(struct net *net) 824 824 { 825 825 struct nfsd_net *nn = net_generic(net, nfsd_net_id); 826 - return max(nn->nfsd4_lease/10, (time_t)1) * HZ; 826 + 827 + /* 828 + * nfsd4_lease is set to at most one hour in __nfsd4_write_time, 829 + * so we can use 32-bit math on it. Warn if that assumption 830 + * ever stops being true. 831 + */ 832 + if (WARN_ON_ONCE(nn->nfsd4_lease > 3600)) 833 + return 360 * HZ; 834 + 835 + return max(((u32)nn->nfsd4_lease)/10, 1u) * HZ; 827 836 } 828 837 829 838 static struct workqueue_struct *callback_wq;
+22 -26
fs/nfsd/nfs4state.c
··· 171 171 clp->cl_clientid.cl_boot, 172 172 clp->cl_clientid.cl_id); 173 173 list_move_tail(&clp->cl_lru, &nn->client_lru); 174 - clp->cl_time = get_seconds(); 174 + clp->cl_time = ktime_get_boottime_seconds(); 175 175 } 176 176 177 177 static void put_client_renew_locked(struct nfs4_client *clp) ··· 776 776 cps = kzalloc(sizeof(struct nfs4_cpntf_state), GFP_KERNEL); 777 777 if (!cps) 778 778 return NULL; 779 - cps->cpntf_time = get_seconds(); 779 + cps->cpntf_time = ktime_get_boottime_seconds(); 780 780 refcount_set(&cps->cp_stateid.sc_count, 1); 781 781 if (!nfs4_init_cp_state(nn, &cps->cp_stateid, NFS4_COPYNOTIFY_STID)) 782 782 goto out_free; ··· 2661 2661 gen_clid(clp, nn); 2662 2662 kref_init(&clp->cl_nfsdfs.cl_ref); 2663 2663 nfsd4_init_cb(&clp->cl_cb_null, clp, NULL, NFSPROC4_CLNT_CB_NULL); 2664 - clp->cl_time = get_seconds(); 2664 + clp->cl_time = ktime_get_boottime_seconds(); 2665 2665 clear_bit(0, &clp->cl_cb_slot_busy); 2666 2666 copy_verf(clp, verf); 2667 2667 memcpy(&clp->cl_addr, sa, sizeof(struct sockaddr_storage)); ··· 4331 4331 last = oo->oo_last_closed_stid; 4332 4332 oo->oo_last_closed_stid = s; 4333 4333 list_move_tail(&oo->oo_close_lru, &nn->close_lru); 4334 - oo->oo_time = get_seconds(); 4334 + oo->oo_time = ktime_get_boottime_seconds(); 4335 4335 spin_unlock(&nn->client_lock); 4336 4336 if (last) 4337 4337 nfs4_put_stid(&last->st_stid); ··· 4426 4426 */ 4427 4427 spin_lock(&state_lock); 4428 4428 if (dp->dl_time == 0) { 4429 - dp->dl_time = get_seconds(); 4429 + dp->dl_time = ktime_get_boottime_seconds(); 4430 4430 list_add_tail(&dp->dl_recall_lru, &nn->del_recall_lru); 4431 4431 } 4432 4432 spin_unlock(&state_lock); ··· 5233 5233 */ 5234 5234 static bool clients_still_reclaiming(struct nfsd_net *nn) 5235 5235 { 5236 - unsigned long now = (unsigned long) ktime_get_real_seconds(); 5237 - unsigned long double_grace_period_end = (unsigned long)nn->boot_time + 5238 - 2 * (unsigned long)nn->nfsd4_lease; 5236 + time64_t double_grace_period_end = nn->boot_time + 5237 + 2 * nn->nfsd4_lease; 5239 5238 5240 5239 if (nn->track_reclaim_completes && 5241 5240 atomic_read(&nn->nr_reclaim_complete) == ··· 5247 5248 * If we've given them *two* lease times to reclaim, and they're 5248 5249 * still not done, give up: 5249 5250 */ 5250 - if (time_after(now, double_grace_period_end)) 5251 + if (ktime_get_boottime_seconds() > double_grace_period_end) 5251 5252 return false; 5252 5253 return true; 5253 5254 } 5254 5255 5255 - static time_t 5256 + static time64_t 5256 5257 nfs4_laundromat(struct nfsd_net *nn) 5257 5258 { 5258 5259 struct nfs4_client *clp; ··· 5261 5262 struct nfs4_ol_stateid *stp; 5262 5263 struct nfsd4_blocked_lock *nbl; 5263 5264 struct list_head *pos, *next, reaplist; 5264 - time_t cutoff = get_seconds() - nn->nfsd4_lease; 5265 - time_t t, new_timeo = nn->nfsd4_lease; 5265 + time64_t cutoff = ktime_get_boottime_seconds() - nn->nfsd4_lease; 5266 + time64_t t, new_timeo = nn->nfsd4_lease; 5266 5267 struct nfs4_cpntf_state *cps; 5267 5268 copy_stateid_t *cps_t; 5268 5269 int i; ··· 5281 5282 idr_for_each_entry(&nn->s2s_cp_stateids, cps_t, i) { 5282 5283 cps = container_of(cps_t, struct nfs4_cpntf_state, cp_stateid); 5283 5284 if (cps->cp_stateid.sc_type == NFS4_COPYNOTIFY_STID && 5284 - !time_after((unsigned long)cps->cpntf_time, 5285 - (unsigned long)cutoff)) 5285 + cps->cpntf_time > cutoff) 5286 5286 _free_cpntf_state_locked(nn, cps); 5287 5287 } 5288 5288 spin_unlock(&nn->s2s_cp_lock); ··· 5289 5291 spin_lock(&nn->client_lock); 5290 5292 list_for_each_safe(pos, next, &nn->client_lru) { 5291 5293 clp = list_entry(pos, struct nfs4_client, cl_lru); 5292 - if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) { 5294 + if (clp->cl_time > cutoff) { 5293 5295 t = clp->cl_time - cutoff; 5294 5296 new_timeo = min(new_timeo, t); 5295 5297 break; ··· 5312 5314 spin_lock(&state_lock); 5313 5315 list_for_each_safe(pos, next, &nn->del_recall_lru) { 5314 5316 dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); 5315 - if (time_after((unsigned long)dp->dl_time, (unsigned long)cutoff)) { 5317 + if (dp->dl_time > cutoff) { 5316 5318 t = dp->dl_time - cutoff; 5317 5319 new_timeo = min(new_timeo, t); 5318 5320 break; ··· 5332 5334 while (!list_empty(&nn->close_lru)) { 5333 5335 oo = list_first_entry(&nn->close_lru, struct nfs4_openowner, 5334 5336 oo_close_lru); 5335 - if (time_after((unsigned long)oo->oo_time, 5336 - (unsigned long)cutoff)) { 5337 + if (oo->oo_time > cutoff) { 5337 5338 t = oo->oo_time - cutoff; 5338 5339 new_timeo = min(new_timeo, t); 5339 5340 break; ··· 5362 5365 while (!list_empty(&nn->blocked_locks_lru)) { 5363 5366 nbl = list_first_entry(&nn->blocked_locks_lru, 5364 5367 struct nfsd4_blocked_lock, nbl_lru); 5365 - if (time_after((unsigned long)nbl->nbl_time, 5366 - (unsigned long)cutoff)) { 5368 + if (nbl->nbl_time > cutoff) { 5367 5369 t = nbl->nbl_time - cutoff; 5368 5370 new_timeo = min(new_timeo, t); 5369 5371 break; ··· 5379 5383 free_blocked_lock(nbl); 5380 5384 } 5381 5385 out: 5382 - new_timeo = max_t(time_t, new_timeo, NFSD_LAUNDROMAT_MINTIMEOUT); 5386 + new_timeo = max_t(time64_t, new_timeo, NFSD_LAUNDROMAT_MINTIMEOUT); 5383 5387 return new_timeo; 5384 5388 } 5385 5389 ··· 5389 5393 static void 5390 5394 laundromat_main(struct work_struct *laundry) 5391 5395 { 5392 - time_t t; 5396 + time64_t t; 5393 5397 struct delayed_work *dwork = to_delayed_work(laundry); 5394 5398 struct nfsd_net *nn = container_of(dwork, struct nfsd_net, 5395 5399 laundromat_work); 5396 5400 5397 5401 t = nfs4_laundromat(nn); 5398 - dprintk("NFSD: laundromat_main - sleeping for %ld seconds\n", t); 5402 + dprintk("NFSD: laundromat_main - sleeping for %lld seconds\n", t); 5399 5403 queue_delayed_work(laundry_wq, &nn->laundromat_work, t*HZ); 5400 5404 } 5401 5405 ··· 5719 5723 if (status) 5720 5724 return status; 5721 5725 5722 - cps->cpntf_time = get_seconds(); 5726 + cps->cpntf_time = ktime_get_boottime_seconds(); 5723 5727 memset(&cstate, 0, sizeof(cstate)); 5724 5728 status = lookup_clientid(&cps->cp_p_clid, &cstate, nn, true); 5725 5729 if (status) ··· 6696 6700 } 6697 6701 6698 6702 if (fl_flags & FL_SLEEP) { 6699 - nbl->nbl_time = get_seconds(); 6703 + nbl->nbl_time = ktime_get_boottime_seconds(); 6700 6704 spin_lock(&nn->blocked_locks_lock); 6701 6705 list_add_tail(&nbl->nbl_list, &lock_sop->lo_blocked); 6702 6706 list_add_tail(&nbl->nbl_lru, &nn->blocked_locks_lru); ··· 7857 7861 nfsd4_client_tracking_init(net); 7858 7862 if (nn->track_reclaim_completes && nn->reclaim_str_hashtbl_size == 0) 7859 7863 goto skip_grace; 7860 - printk(KERN_INFO "NFSD: starting %ld-second grace period (net %x)\n", 7864 + printk(KERN_INFO "NFSD: starting %lld-second grace period (net %x)\n", 7861 7865 nn->nfsd4_grace, net->ns.inum); 7862 7866 queue_delayed_work(laundry_wq, &nn->laundromat_work, nn->nfsd4_grace * HZ); 7863 7867 return 0;
+3 -3
fs/nfsd/nfsctl.c
··· 956 956 957 957 #ifdef CONFIG_NFSD_V4 958 958 static ssize_t __nfsd4_write_time(struct file *file, char *buf, size_t size, 959 - time_t *time, struct nfsd_net *nn) 959 + time64_t *time, struct nfsd_net *nn) 960 960 { 961 961 char *mesg = buf; 962 962 int rv, i; ··· 984 984 *time = i; 985 985 } 986 986 987 - return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%ld\n", *time); 987 + return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%lld\n", *time); 988 988 } 989 989 990 990 static ssize_t nfsd4_write_time(struct file *file, char *buf, size_t size, 991 - time_t *time, struct nfsd_net *nn) 991 + time64_t *time, struct nfsd_net *nn) 992 992 { 993 993 ssize_t rv; 994 994
+5 -5
fs/nfsd/state.h
··· 121 121 struct list_head cp_list; /* per parent nfs4_stid */ 122 122 stateid_t cp_p_stateid; /* copy of parent's stateid */ 123 123 clientid_t cp_p_clid; /* copy of parent's clid */ 124 - time_t cpntf_time; /* last time stateid used */ 124 + time64_t cpntf_time; /* last time stateid used */ 125 125 }; 126 126 127 127 /* ··· 152 152 struct list_head dl_recall_lru; /* delegation recalled */ 153 153 struct nfs4_clnt_odstate *dl_clnt_odstate; 154 154 u32 dl_type; 155 - time_t dl_time; 155 + time64_t dl_time; 156 156 /* For recall: */ 157 157 int dl_retries; 158 158 struct nfsd4_callback dl_recall; ··· 330 330 #endif 331 331 struct xdr_netobj cl_name; /* id generated by client */ 332 332 nfs4_verifier cl_verifier; /* generated by client */ 333 - time_t cl_time; /* time of last lease renewal */ 333 + time64_t cl_time; /* time of last lease renewal */ 334 334 struct sockaddr_storage cl_addr; /* client ipaddress */ 335 335 bool cl_mach_cred; /* SP4_MACH_CRED in force */ 336 336 struct svc_cred cl_cred; /* setclientid principal */ ··· 469 469 */ 470 470 struct list_head oo_close_lru; 471 471 struct nfs4_ol_stateid *oo_last_closed_stid; 472 - time_t oo_time; /* time of placement on so_close_lru */ 472 + time64_t oo_time; /* time of placement on so_close_lru */ 473 473 #define NFS4_OO_CONFIRMED 1 474 474 unsigned char oo_flags; 475 475 }; ··· 626 626 struct nfsd4_blocked_lock { 627 627 struct list_head nbl_list; 628 628 struct list_head nbl_lru; 629 - time_t nbl_time; 629 + time64_t nbl_time; 630 630 struct file_lock nbl_lock; 631 631 struct knfsd_fh nbl_fh; 632 632 struct nfsd4_callback nbl_cb;