Commit 20b7d86f authored by Arnd Bergmann's avatar Arnd Bergmann Committed by J. Bruce Fields

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: default avatarArnd Bergmann <arnd@arndb.de>
[bfields@redhat.com: fix up copy expiration]
Signed-off-by: default avatarJ. Bruce Fields <bfields@redhat.com>
parent 9594497f
...@@ -92,8 +92,8 @@ struct nfsd_net { ...@@ -92,8 +92,8 @@ struct nfsd_net {
bool in_grace; bool in_grace;
const struct nfsd4_client_tracking_ops *client_tracking_ops; const struct nfsd4_client_tracking_ops *client_tracking_ops;
time_t nfsd4_lease; time64_t nfsd4_lease;
time_t nfsd4_grace; time64_t nfsd4_grace;
bool somebody_reclaimed; bool somebody_reclaimed;
bool track_reclaim_completes; bool track_reclaim_completes;
......
...@@ -823,7 +823,16 @@ static const struct rpc_program cb_program = { ...@@ -823,7 +823,16 @@ static const struct rpc_program cb_program = {
static int max_cb_time(struct net *net) static int max_cb_time(struct net *net)
{ {
struct nfsd_net *nn = net_generic(net, nfsd_net_id); struct nfsd_net *nn = net_generic(net, nfsd_net_id);
return max(nn->nfsd4_lease/10, (time_t)1) * HZ;
/*
* nfsd4_lease is set to at most one hour in __nfsd4_write_time,
* so we can use 32-bit math on it. Warn if that assumption
* ever stops being true.
*/
if (WARN_ON_ONCE(nn->nfsd4_lease > 3600))
return 360 * HZ;
return max(((u32)nn->nfsd4_lease)/10, 1u) * HZ;
} }
static struct workqueue_struct *callback_wq; static struct workqueue_struct *callback_wq;
......
...@@ -171,7 +171,7 @@ renew_client_locked(struct nfs4_client *clp) ...@@ -171,7 +171,7 @@ renew_client_locked(struct nfs4_client *clp)
clp->cl_clientid.cl_boot, clp->cl_clientid.cl_boot,
clp->cl_clientid.cl_id); clp->cl_clientid.cl_id);
list_move_tail(&clp->cl_lru, &nn->client_lru); list_move_tail(&clp->cl_lru, &nn->client_lru);
clp->cl_time = get_seconds(); clp->cl_time = ktime_get_boottime_seconds();
} }
static void put_client_renew_locked(struct nfs4_client *clp) static void put_client_renew_locked(struct nfs4_client *clp)
...@@ -776,7 +776,7 @@ struct nfs4_cpntf_state *nfs4_alloc_init_cpntf_state(struct nfsd_net *nn, ...@@ -776,7 +776,7 @@ struct nfs4_cpntf_state *nfs4_alloc_init_cpntf_state(struct nfsd_net *nn,
cps = kzalloc(sizeof(struct nfs4_cpntf_state), GFP_KERNEL); cps = kzalloc(sizeof(struct nfs4_cpntf_state), GFP_KERNEL);
if (!cps) if (!cps)
return NULL; return NULL;
cps->cpntf_time = get_seconds(); cps->cpntf_time = ktime_get_boottime_seconds();
refcount_set(&cps->cp_stateid.sc_count, 1); refcount_set(&cps->cp_stateid.sc_count, 1);
if (!nfs4_init_cp_state(nn, &cps->cp_stateid, NFS4_COPYNOTIFY_STID)) if (!nfs4_init_cp_state(nn, &cps->cp_stateid, NFS4_COPYNOTIFY_STID))
goto out_free; goto out_free;
...@@ -2661,7 +2661,7 @@ static struct nfs4_client *create_client(struct xdr_netobj name, ...@@ -2661,7 +2661,7 @@ static struct nfs4_client *create_client(struct xdr_netobj name,
gen_clid(clp, nn); gen_clid(clp, nn);
kref_init(&clp->cl_nfsdfs.cl_ref); kref_init(&clp->cl_nfsdfs.cl_ref);
nfsd4_init_cb(&clp->cl_cb_null, clp, NULL, NFSPROC4_CLNT_CB_NULL); nfsd4_init_cb(&clp->cl_cb_null, clp, NULL, NFSPROC4_CLNT_CB_NULL);
clp->cl_time = get_seconds(); clp->cl_time = ktime_get_boottime_seconds();
clear_bit(0, &clp->cl_cb_slot_busy); clear_bit(0, &clp->cl_cb_slot_busy);
copy_verf(clp, verf); copy_verf(clp, verf);
memcpy(&clp->cl_addr, sa, sizeof(struct sockaddr_storage)); memcpy(&clp->cl_addr, sa, sizeof(struct sockaddr_storage));
...@@ -4331,7 +4331,7 @@ move_to_close_lru(struct nfs4_ol_stateid *s, struct net *net) ...@@ -4331,7 +4331,7 @@ move_to_close_lru(struct nfs4_ol_stateid *s, struct net *net)
last = oo->oo_last_closed_stid; last = oo->oo_last_closed_stid;
oo->oo_last_closed_stid = s; oo->oo_last_closed_stid = s;
list_move_tail(&oo->oo_close_lru, &nn->close_lru); list_move_tail(&oo->oo_close_lru, &nn->close_lru);
oo->oo_time = get_seconds(); oo->oo_time = ktime_get_boottime_seconds();
spin_unlock(&nn->client_lock); spin_unlock(&nn->client_lock);
if (last) if (last)
nfs4_put_stid(&last->st_stid); nfs4_put_stid(&last->st_stid);
...@@ -4426,7 +4426,7 @@ static void nfsd4_cb_recall_prepare(struct nfsd4_callback *cb) ...@@ -4426,7 +4426,7 @@ static void nfsd4_cb_recall_prepare(struct nfsd4_callback *cb)
*/ */
spin_lock(&state_lock); spin_lock(&state_lock);
if (dp->dl_time == 0) { if (dp->dl_time == 0) {
dp->dl_time = get_seconds(); dp->dl_time = ktime_get_boottime_seconds();
list_add_tail(&dp->dl_recall_lru, &nn->del_recall_lru); list_add_tail(&dp->dl_recall_lru, &nn->del_recall_lru);
} }
spin_unlock(&state_lock); spin_unlock(&state_lock);
...@@ -5233,9 +5233,8 @@ nfsd4_end_grace(struct nfsd_net *nn) ...@@ -5233,9 +5233,8 @@ nfsd4_end_grace(struct nfsd_net *nn)
*/ */
static bool clients_still_reclaiming(struct nfsd_net *nn) static bool clients_still_reclaiming(struct nfsd_net *nn)
{ {
unsigned long now = (unsigned long) ktime_get_real_seconds(); time64_t double_grace_period_end = nn->boot_time +
unsigned long double_grace_period_end = (unsigned long)nn->boot_time + 2 * nn->nfsd4_lease;
2 * (unsigned long)nn->nfsd4_lease;
if (nn->track_reclaim_completes && if (nn->track_reclaim_completes &&
atomic_read(&nn->nr_reclaim_complete) == atomic_read(&nn->nr_reclaim_complete) ==
...@@ -5248,12 +5247,12 @@ static bool clients_still_reclaiming(struct nfsd_net *nn) ...@@ -5248,12 +5247,12 @@ static bool clients_still_reclaiming(struct nfsd_net *nn)
* If we've given them *two* lease times to reclaim, and they're * If we've given them *two* lease times to reclaim, and they're
* still not done, give up: * still not done, give up:
*/ */
if (time_after(now, double_grace_period_end)) if (ktime_get_boottime_seconds() > double_grace_period_end)
return false; return false;
return true; return true;
} }
static time_t static time64_t
nfs4_laundromat(struct nfsd_net *nn) nfs4_laundromat(struct nfsd_net *nn)
{ {
struct nfs4_client *clp; struct nfs4_client *clp;
...@@ -5262,8 +5261,8 @@ nfs4_laundromat(struct nfsd_net *nn) ...@@ -5262,8 +5261,8 @@ nfs4_laundromat(struct nfsd_net *nn)
struct nfs4_ol_stateid *stp; struct nfs4_ol_stateid *stp;
struct nfsd4_blocked_lock *nbl; struct nfsd4_blocked_lock *nbl;
struct list_head *pos, *next, reaplist; struct list_head *pos, *next, reaplist;
time_t cutoff = get_seconds() - nn->nfsd4_lease; time64_t cutoff = ktime_get_boottime_seconds() - nn->nfsd4_lease;
time_t t, new_timeo = nn->nfsd4_lease; time64_t t, new_timeo = nn->nfsd4_lease;
struct nfs4_cpntf_state *cps; struct nfs4_cpntf_state *cps;
copy_stateid_t *cps_t; copy_stateid_t *cps_t;
int i; int i;
...@@ -5282,8 +5281,7 @@ nfs4_laundromat(struct nfsd_net *nn) ...@@ -5282,8 +5281,7 @@ nfs4_laundromat(struct nfsd_net *nn)
idr_for_each_entry(&nn->s2s_cp_stateids, cps_t, i) { idr_for_each_entry(&nn->s2s_cp_stateids, cps_t, i) {
cps = container_of(cps_t, struct nfs4_cpntf_state, cp_stateid); cps = container_of(cps_t, struct nfs4_cpntf_state, cp_stateid);
if (cps->cp_stateid.sc_type == NFS4_COPYNOTIFY_STID && if (cps->cp_stateid.sc_type == NFS4_COPYNOTIFY_STID &&
!time_after((unsigned long)cps->cpntf_time, cps->cpntf_time > cutoff)
(unsigned long)cutoff))
_free_cpntf_state_locked(nn, cps); _free_cpntf_state_locked(nn, cps);
} }
spin_unlock(&nn->s2s_cp_lock); spin_unlock(&nn->s2s_cp_lock);
...@@ -5291,7 +5289,7 @@ nfs4_laundromat(struct nfsd_net *nn) ...@@ -5291,7 +5289,7 @@ nfs4_laundromat(struct nfsd_net *nn)
spin_lock(&nn->client_lock); spin_lock(&nn->client_lock);
list_for_each_safe(pos, next, &nn->client_lru) { list_for_each_safe(pos, next, &nn->client_lru) {
clp = list_entry(pos, struct nfs4_client, cl_lru); clp = list_entry(pos, struct nfs4_client, cl_lru);
if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) { if (clp->cl_time > cutoff) {
t = clp->cl_time - cutoff; t = clp->cl_time - cutoff;
new_timeo = min(new_timeo, t); new_timeo = min(new_timeo, t);
break; break;
...@@ -5314,7 +5312,7 @@ nfs4_laundromat(struct nfsd_net *nn) ...@@ -5314,7 +5312,7 @@ nfs4_laundromat(struct nfsd_net *nn)
spin_lock(&state_lock); spin_lock(&state_lock);
list_for_each_safe(pos, next, &nn->del_recall_lru) { list_for_each_safe(pos, next, &nn->del_recall_lru) {
dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);
if (time_after((unsigned long)dp->dl_time, (unsigned long)cutoff)) { if (dp->dl_time > cutoff) {
t = dp->dl_time - cutoff; t = dp->dl_time - cutoff;
new_timeo = min(new_timeo, t); new_timeo = min(new_timeo, t);
break; break;
...@@ -5334,8 +5332,7 @@ nfs4_laundromat(struct nfsd_net *nn) ...@@ -5334,8 +5332,7 @@ nfs4_laundromat(struct nfsd_net *nn)
while (!list_empty(&nn->close_lru)) { while (!list_empty(&nn->close_lru)) {
oo = list_first_entry(&nn->close_lru, struct nfs4_openowner, oo = list_first_entry(&nn->close_lru, struct nfs4_openowner,
oo_close_lru); oo_close_lru);
if (time_after((unsigned long)oo->oo_time, if (oo->oo_time > cutoff) {
(unsigned long)cutoff)) {
t = oo->oo_time - cutoff; t = oo->oo_time - cutoff;
new_timeo = min(new_timeo, t); new_timeo = min(new_timeo, t);
break; break;
...@@ -5365,8 +5362,7 @@ nfs4_laundromat(struct nfsd_net *nn) ...@@ -5365,8 +5362,7 @@ nfs4_laundromat(struct nfsd_net *nn)
while (!list_empty(&nn->blocked_locks_lru)) { while (!list_empty(&nn->blocked_locks_lru)) {
nbl = list_first_entry(&nn->blocked_locks_lru, nbl = list_first_entry(&nn->blocked_locks_lru,
struct nfsd4_blocked_lock, nbl_lru); struct nfsd4_blocked_lock, nbl_lru);
if (time_after((unsigned long)nbl->nbl_time, if (nbl->nbl_time > cutoff) {
(unsigned long)cutoff)) {
t = nbl->nbl_time - cutoff; t = nbl->nbl_time - cutoff;
new_timeo = min(new_timeo, t); new_timeo = min(new_timeo, t);
break; break;
...@@ -5383,7 +5379,7 @@ nfs4_laundromat(struct nfsd_net *nn) ...@@ -5383,7 +5379,7 @@ nfs4_laundromat(struct nfsd_net *nn)
free_blocked_lock(nbl); free_blocked_lock(nbl);
} }
out: out:
new_timeo = max_t(time_t, new_timeo, NFSD_LAUNDROMAT_MINTIMEOUT); new_timeo = max_t(time64_t, new_timeo, NFSD_LAUNDROMAT_MINTIMEOUT);
return new_timeo; return new_timeo;
} }
...@@ -5393,13 +5389,13 @@ static void laundromat_main(struct work_struct *); ...@@ -5393,13 +5389,13 @@ static void laundromat_main(struct work_struct *);
static void static void
laundromat_main(struct work_struct *laundry) laundromat_main(struct work_struct *laundry)
{ {
time_t t; time64_t t;
struct delayed_work *dwork = to_delayed_work(laundry); struct delayed_work *dwork = to_delayed_work(laundry);
struct nfsd_net *nn = container_of(dwork, struct nfsd_net, struct nfsd_net *nn = container_of(dwork, struct nfsd_net,
laundromat_work); laundromat_work);
t = nfs4_laundromat(nn); t = nfs4_laundromat(nn);
dprintk("NFSD: laundromat_main - sleeping for %ld seconds\n", t); dprintk("NFSD: laundromat_main - sleeping for %lld seconds\n", t);
queue_delayed_work(laundry_wq, &nn->laundromat_work, t*HZ); queue_delayed_work(laundry_wq, &nn->laundromat_work, t*HZ);
} }
...@@ -5723,7 +5719,7 @@ static __be32 find_cpntf_state(struct nfsd_net *nn, stateid_t *st, ...@@ -5723,7 +5719,7 @@ static __be32 find_cpntf_state(struct nfsd_net *nn, stateid_t *st,
if (status) if (status)
return status; return status;
cps->cpntf_time = get_seconds(); cps->cpntf_time = ktime_get_boottime_seconds();
memset(&cstate, 0, sizeof(cstate)); memset(&cstate, 0, sizeof(cstate));
status = lookup_clientid(&cps->cp_p_clid, &cstate, nn, true); status = lookup_clientid(&cps->cp_p_clid, &cstate, nn, true);
if (status) if (status)
...@@ -6700,7 +6696,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -6700,7 +6696,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
} }
if (fl_flags & FL_SLEEP) { if (fl_flags & FL_SLEEP) {
nbl->nbl_time = get_seconds(); nbl->nbl_time = ktime_get_boottime_seconds();
spin_lock(&nn->blocked_locks_lock); spin_lock(&nn->blocked_locks_lock);
list_add_tail(&nbl->nbl_list, &lock_sop->lo_blocked); list_add_tail(&nbl->nbl_list, &lock_sop->lo_blocked);
list_add_tail(&nbl->nbl_lru, &nn->blocked_locks_lru); list_add_tail(&nbl->nbl_lru, &nn->blocked_locks_lru);
...@@ -7861,7 +7857,7 @@ nfs4_state_start_net(struct net *net) ...@@ -7861,7 +7857,7 @@ nfs4_state_start_net(struct net *net)
nfsd4_client_tracking_init(net); nfsd4_client_tracking_init(net);
if (nn->track_reclaim_completes && nn->reclaim_str_hashtbl_size == 0) if (nn->track_reclaim_completes && nn->reclaim_str_hashtbl_size == 0)
goto skip_grace; goto skip_grace;
printk(KERN_INFO "NFSD: starting %ld-second grace period (net %x)\n", printk(KERN_INFO "NFSD: starting %lld-second grace period (net %x)\n",
nn->nfsd4_grace, net->ns.inum); nn->nfsd4_grace, net->ns.inum);
queue_delayed_work(laundry_wq, &nn->laundromat_work, nn->nfsd4_grace * HZ); queue_delayed_work(laundry_wq, &nn->laundromat_work, nn->nfsd4_grace * HZ);
return 0; return 0;
......
...@@ -956,7 +956,7 @@ static ssize_t write_maxconn(struct file *file, char *buf, size_t size) ...@@ -956,7 +956,7 @@ static ssize_t write_maxconn(struct file *file, char *buf, size_t size)
#ifdef CONFIG_NFSD_V4 #ifdef CONFIG_NFSD_V4
static ssize_t __nfsd4_write_time(struct file *file, char *buf, size_t size, static ssize_t __nfsd4_write_time(struct file *file, char *buf, size_t size,
time_t *time, struct nfsd_net *nn) time64_t *time, struct nfsd_net *nn)
{ {
char *mesg = buf; char *mesg = buf;
int rv, i; int rv, i;
...@@ -984,11 +984,11 @@ static ssize_t __nfsd4_write_time(struct file *file, char *buf, size_t size, ...@@ -984,11 +984,11 @@ static ssize_t __nfsd4_write_time(struct file *file, char *buf, size_t size,
*time = i; *time = i;
} }
return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%ld\n", *time); return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%lld\n", *time);
} }
static ssize_t nfsd4_write_time(struct file *file, char *buf, size_t size, static ssize_t nfsd4_write_time(struct file *file, char *buf, size_t size,
time_t *time, struct nfsd_net *nn) time64_t *time, struct nfsd_net *nn)
{ {
ssize_t rv; ssize_t rv;
......
...@@ -121,7 +121,7 @@ struct nfs4_cpntf_state { ...@@ -121,7 +121,7 @@ struct nfs4_cpntf_state {
struct list_head cp_list; /* per parent nfs4_stid */ struct list_head cp_list; /* per parent nfs4_stid */
stateid_t cp_p_stateid; /* copy of parent's stateid */ stateid_t cp_p_stateid; /* copy of parent's stateid */
clientid_t cp_p_clid; /* copy of parent's clid */ clientid_t cp_p_clid; /* copy of parent's clid */
time_t cpntf_time; /* last time stateid used */ time64_t cpntf_time; /* last time stateid used */
}; };
/* /*
...@@ -152,7 +152,7 @@ struct nfs4_delegation { ...@@ -152,7 +152,7 @@ struct nfs4_delegation {
struct list_head dl_recall_lru; /* delegation recalled */ struct list_head dl_recall_lru; /* delegation recalled */
struct nfs4_clnt_odstate *dl_clnt_odstate; struct nfs4_clnt_odstate *dl_clnt_odstate;
u32 dl_type; u32 dl_type;
time_t dl_time; time64_t dl_time;
/* For recall: */ /* For recall: */
int dl_retries; int dl_retries;
struct nfsd4_callback dl_recall; struct nfsd4_callback dl_recall;
...@@ -330,7 +330,7 @@ struct nfs4_client { ...@@ -330,7 +330,7 @@ struct nfs4_client {
#endif #endif
struct xdr_netobj cl_name; /* id generated by client */ struct xdr_netobj cl_name; /* id generated by client */
nfs4_verifier cl_verifier; /* generated by client */ nfs4_verifier cl_verifier; /* generated by client */
time_t cl_time; /* time of last lease renewal */ time64_t cl_time; /* time of last lease renewal */
struct sockaddr_storage cl_addr; /* client ipaddress */ struct sockaddr_storage cl_addr; /* client ipaddress */
bool cl_mach_cred; /* SP4_MACH_CRED in force */ bool cl_mach_cred; /* SP4_MACH_CRED in force */
struct svc_cred cl_cred; /* setclientid principal */ struct svc_cred cl_cred; /* setclientid principal */
...@@ -469,7 +469,7 @@ struct nfs4_openowner { ...@@ -469,7 +469,7 @@ struct nfs4_openowner {
*/ */
struct list_head oo_close_lru; struct list_head oo_close_lru;
struct nfs4_ol_stateid *oo_last_closed_stid; struct nfs4_ol_stateid *oo_last_closed_stid;
time_t oo_time; /* time of placement on so_close_lru */ time64_t oo_time; /* time of placement on so_close_lru */
#define NFS4_OO_CONFIRMED 1 #define NFS4_OO_CONFIRMED 1
unsigned char oo_flags; unsigned char oo_flags;
}; };
...@@ -626,7 +626,7 @@ static inline bool nfsd4_stateid_generation_after(stateid_t *a, stateid_t *b) ...@@ -626,7 +626,7 @@ static inline bool nfsd4_stateid_generation_after(stateid_t *a, stateid_t *b)
struct nfsd4_blocked_lock { struct nfsd4_blocked_lock {
struct list_head nbl_list; struct list_head nbl_list;
struct list_head nbl_lru; struct list_head nbl_lru;
time_t nbl_time; time64_t nbl_time;
struct file_lock nbl_lock; struct file_lock nbl_lock;
struct knfsd_fh nbl_fh; struct knfsd_fh nbl_fh;
struct nfsd4_callback nbl_cb; struct nfsd4_callback nbl_cb;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment