Skip to content

Commit 6ae30d6

Browse files
jtlaytonchucklever
authored andcommitted
nfsd: add support for delegated timestamps
Add support for the delegated timestamps on write delegations. This allows the server to proxy timestamps from the delegation holder to other clients that are doing GETATTRs vs. the same inode. When OPEN4_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS bit is set in the OPEN call, set the dl_type to the *_ATTRS_DELEG flavor of delegation. Add timespec64 fields to nfs4_cb_fattr and decode the timestamps into those. Vet those timestamps according to the delstid spec and update the inode attrs if necessary. Signed-off-by: Jeff Layton <[email protected]> Signed-off-by: Chuck Lever <[email protected]>
1 parent cee9b4e commit 6ae30d6

File tree

7 files changed

+152
-23
lines changed

7 files changed

+152
-23
lines changed

fs/nfsd/nfs4callback.c

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include "trace.h"
4343
#include "xdr4cb.h"
4444
#include "xdr4.h"
45+
#include "nfs4xdr_gen.h"
4546

4647
#define NFSDDBG_FACILITY NFSDDBG_PROC
4748

@@ -93,12 +94,35 @@ static int decode_cb_fattr4(struct xdr_stream *xdr, uint32_t *bitmap,
9394
{
9495
fattr->ncf_cb_change = 0;
9596
fattr->ncf_cb_fsize = 0;
97+
fattr->ncf_cb_atime.tv_sec = 0;
98+
fattr->ncf_cb_atime.tv_nsec = 0;
99+
fattr->ncf_cb_mtime.tv_sec = 0;
100+
fattr->ncf_cb_mtime.tv_nsec = 0;
101+
96102
if (bitmap[0] & FATTR4_WORD0_CHANGE)
97103
if (xdr_stream_decode_u64(xdr, &fattr->ncf_cb_change) < 0)
98104
return -NFSERR_BAD_XDR;
99105
if (bitmap[0] & FATTR4_WORD0_SIZE)
100106
if (xdr_stream_decode_u64(xdr, &fattr->ncf_cb_fsize) < 0)
101107
return -NFSERR_BAD_XDR;
108+
if (bitmap[2] & FATTR4_WORD2_TIME_DELEG_ACCESS) {
109+
fattr4_time_deleg_access access;
110+
111+
if (!xdrgen_decode_fattr4_time_deleg_access(xdr, &access))
112+
return -NFSERR_BAD_XDR;
113+
fattr->ncf_cb_atime.tv_sec = access.seconds;
114+
fattr->ncf_cb_atime.tv_nsec = access.nseconds;
115+
116+
}
117+
if (bitmap[2] & FATTR4_WORD2_TIME_DELEG_MODIFY) {
118+
fattr4_time_deleg_modify modify;
119+
120+
if (!xdrgen_decode_fattr4_time_deleg_modify(xdr, &modify))
121+
return -NFSERR_BAD_XDR;
122+
fattr->ncf_cb_mtime.tv_sec = modify.seconds;
123+
fattr->ncf_cb_mtime.tv_nsec = modify.nseconds;
124+
125+
}
102126
return 0;
103127
}
104128

@@ -364,15 +388,21 @@ encode_cb_getattr4args(struct xdr_stream *xdr, struct nfs4_cb_compound_hdr *hdr,
364388
struct nfs4_delegation *dp = container_of(fattr, struct nfs4_delegation, dl_cb_fattr);
365389
struct knfsd_fh *fh = &dp->dl_stid.sc_file->fi_fhandle;
366390
struct nfs4_cb_fattr *ncf = &dp->dl_cb_fattr;
367-
u32 bmap[1];
391+
u32 bmap_size = 1;
392+
u32 bmap[3];
368393

369394
bmap[0] = FATTR4_WORD0_SIZE;
370395
if (!ncf->ncf_file_modified)
371396
bmap[0] |= FATTR4_WORD0_CHANGE;
372397

398+
if (deleg_attrs_deleg(dp->dl_type)) {
399+
bmap[1] = 0;
400+
bmap[2] = FATTR4_WORD2_TIME_DELEG_ACCESS | FATTR4_WORD2_TIME_DELEG_MODIFY;
401+
bmap_size = 3;
402+
}
373403
encode_nfs_cb_opnum4(xdr, OP_CB_GETATTR);
374404
encode_nfs_fh4(xdr, fh);
375-
encode_bitmap4(xdr, bmap, ARRAY_SIZE(bmap));
405+
encode_bitmap4(xdr, bmap, bmap_size);
376406
hdr->nops++;
377407
}
378408

@@ -636,7 +666,7 @@ static int nfs4_xdr_dec_cb_getattr(struct rpc_rqst *rqstp,
636666
struct nfs4_cb_compound_hdr hdr;
637667
int status;
638668
u32 bitmap[3] = {0};
639-
u32 attrlen;
669+
u32 attrlen, maxlen;
640670
struct nfs4_cb_fattr *ncf =
641671
container_of(cb, struct nfs4_cb_fattr, ncf_getattr);
642672

@@ -655,7 +685,11 @@ static int nfs4_xdr_dec_cb_getattr(struct rpc_rqst *rqstp,
655685
return -NFSERR_BAD_XDR;
656686
if (xdr_stream_decode_u32(xdr, &attrlen) < 0)
657687
return -NFSERR_BAD_XDR;
658-
if (attrlen > (sizeof(ncf->ncf_cb_change) + sizeof(ncf->ncf_cb_fsize)))
688+
maxlen = sizeof(ncf->ncf_cb_change) + sizeof(ncf->ncf_cb_fsize);
689+
if (bitmap[2] != 0)
690+
maxlen += (sizeof(ncf->ncf_cb_mtime.tv_sec) +
691+
sizeof(ncf->ncf_cb_mtime.tv_nsec)) * 2;
692+
if (attrlen > maxlen)
659693
return -NFSERR_BAD_XDR;
660694
status = decode_cb_fattr4(xdr, bitmap, ncf);
661695
return status;

fs/nfsd/nfs4state.c

Lines changed: 85 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5951,13 +5951,14 @@ static struct nfs4_delegation *
59515951
nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
59525952
struct svc_fh *parent)
59535953
{
5954-
int status = 0;
5954+
bool deleg_ts = open->op_deleg_want & OPEN4_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS;
59555955
struct nfs4_client *clp = stp->st_stid.sc_client;
59565956
struct nfs4_file *fp = stp->st_stid.sc_file;
59575957
struct nfs4_clnt_odstate *odstate = stp->st_clnt_odstate;
59585958
struct nfs4_delegation *dp;
59595959
struct nfsd_file *nf = NULL;
59605960
struct file_lease *fl;
5961+
int status = 0;
59615962
u32 dl_type;
59625963

59635964
/*
@@ -5982,7 +5983,7 @@ nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
59825983
*/
59835984
if ((open->op_share_access & NFS4_SHARE_ACCESS_BOTH) == NFS4_SHARE_ACCESS_BOTH) {
59845985
nf = find_rw_file(fp);
5985-
dl_type = OPEN_DELEGATE_WRITE;
5986+
dl_type = deleg_ts ? OPEN_DELEGATE_WRITE_ATTRS_DELEG : OPEN_DELEGATE_WRITE;
59865987
}
59875988

59885989
/*
@@ -5991,7 +5992,7 @@ nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
59915992
*/
59925993
if (!nf && (open->op_share_access & NFS4_SHARE_ACCESS_READ)) {
59935994
nf = find_readable_file(fp);
5994-
dl_type = OPEN_DELEGATE_READ;
5995+
dl_type = deleg_ts ? OPEN_DELEGATE_READ_ATTRS_DELEG : OPEN_DELEGATE_READ;
59955996
}
59965997

59975998
if (!nf)
@@ -6149,13 +6150,14 @@ static void
61496150
nfs4_open_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
61506151
struct svc_fh *currentfh)
61516152
{
6152-
struct nfs4_delegation *dp;
6153+
bool deleg_ts = open->op_deleg_want & OPEN4_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS;
61536154
struct nfs4_openowner *oo = openowner(stp->st_stateowner);
61546155
struct nfs4_client *clp = stp->st_stid.sc_client;
61556156
struct svc_fh *parent = NULL;
6156-
int cb_up;
6157-
int status = 0;
6157+
struct nfs4_delegation *dp;
61586158
struct kstat stat;
6159+
int status = 0;
6160+
int cb_up;
61596161

61606162
cb_up = nfsd4_cb_channel_good(oo->oo_owner.so_client);
61616163
open->op_recall = false;
@@ -6196,12 +6198,14 @@ nfs4_open_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
61966198
destroy_delegation(dp);
61976199
goto out_no_deleg;
61986200
}
6199-
open->op_delegate_type = OPEN_DELEGATE_WRITE;
6201+
open->op_delegate_type = deleg_ts ? OPEN_DELEGATE_WRITE_ATTRS_DELEG :
6202+
OPEN_DELEGATE_WRITE;
62006203
dp->dl_cb_fattr.ncf_cur_fsize = stat.size;
62016204
dp->dl_cb_fattr.ncf_initial_cinfo = nfsd4_change_attribute(&stat);
62026205
trace_nfsd_deleg_write(&dp->dl_stid.sc_stateid);
62036206
} else {
6204-
open->op_delegate_type = OPEN_DELEGATE_READ;
6207+
open->op_delegate_type = deleg_ts ? OPEN_DELEGATE_READ_ATTRS_DELEG :
6208+
OPEN_DELEGATE_READ;
62056209
trace_nfsd_deleg_read(&dp->dl_stid.sc_stateid);
62066210
}
62076211
nfs4_put_stid(&dp->dl_stid);
@@ -9017,6 +9021,78 @@ nfsd4_get_writestateid(struct nfsd4_compound_state *cstate,
90179021
get_stateid(cstate, &u->write.wr_stateid);
90189022
}
90199023

9024+
/**
9025+
* set_cb_time - vet and set the timespec for a cb_getattr update
9026+
* @cb: timestamp from the CB_GETATTR response
9027+
* @orig: original timestamp in the inode
9028+
* @now: current time
9029+
*
9030+
* Given a timestamp in a CB_GETATTR response, check it against the
9031+
* current timestamp in the inode and the current time. Returns true
9032+
* if the inode's timestamp needs to be updated, and false otherwise.
9033+
* @cb may also be changed if the timestamp needs to be clamped.
9034+
*/
9035+
static bool set_cb_time(struct timespec64 *cb, const struct timespec64 *orig,
9036+
const struct timespec64 *now)
9037+
{
9038+
9039+
/*
9040+
* "When the time presented is before the original time, then the
9041+
* update is ignored." Also no need to update if there is no change.
9042+
*/
9043+
if (timespec64_compare(cb, orig) <= 0)
9044+
return false;
9045+
9046+
/*
9047+
* "When the time presented is in the future, the server can either
9048+
* clamp the new time to the current time, or it may
9049+
* return NFS4ERR_DELAY to the client, allowing it to retry."
9050+
*/
9051+
if (timespec64_compare(cb, now) > 0) {
9052+
/* clamp it */
9053+
*cb = *now;
9054+
}
9055+
9056+
return true;
9057+
}
9058+
9059+
static int cb_getattr_update_times(struct dentry *dentry, struct nfs4_delegation *dp)
9060+
{
9061+
struct inode *inode = d_inode(dentry);
9062+
struct timespec64 now = current_time(inode);
9063+
struct nfs4_cb_fattr *ncf = &dp->dl_cb_fattr;
9064+
struct iattr attrs = { };
9065+
int ret;
9066+
9067+
if (deleg_attrs_deleg(dp->dl_type)) {
9068+
struct timespec64 atime = inode_get_atime(inode);
9069+
struct timespec64 mtime = inode_get_mtime(inode);
9070+
9071+
attrs.ia_atime = ncf->ncf_cb_atime;
9072+
attrs.ia_mtime = ncf->ncf_cb_mtime;
9073+
9074+
if (set_cb_time(&attrs.ia_atime, &atime, &now))
9075+
attrs.ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
9076+
9077+
if (set_cb_time(&attrs.ia_mtime, &mtime, &now)) {
9078+
attrs.ia_valid |= ATTR_CTIME | ATTR_MTIME | ATTR_MTIME_SET;
9079+
attrs.ia_ctime = attrs.ia_mtime;
9080+
}
9081+
} else {
9082+
attrs.ia_valid |= ATTR_MTIME | ATTR_CTIME;
9083+
attrs.ia_mtime = attrs.ia_ctime = now;
9084+
}
9085+
9086+
if (!attrs.ia_valid)
9087+
return 0;
9088+
9089+
attrs.ia_valid |= ATTR_DELEG;
9090+
inode_lock(inode);
9091+
ret = notify_change(&nop_mnt_idmap, dentry, &attrs, NULL);
9092+
inode_unlock(inode);
9093+
return ret;
9094+
}
9095+
90209096
/**
90219097
* nfsd4_deleg_getattr_conflict - Recall if GETATTR causes conflict
90229098
* @rqstp: RPC transaction context
@@ -9043,7 +9119,6 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry,
90439119
struct file_lock_context *ctx;
90449120
struct nfs4_delegation *dp = NULL;
90459121
struct file_lease *fl;
9046-
struct iattr attrs;
90479122
struct nfs4_cb_fattr *ncf;
90489123
struct inode *inode = d_inode(dentry);
90499124

@@ -9105,11 +9180,7 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry,
91059180
* not update the file's metadata with the client's
91069181
* modified size
91079182
*/
9108-
attrs.ia_mtime = attrs.ia_ctime = current_time(inode);
9109-
attrs.ia_valid = ATTR_MTIME | ATTR_CTIME | ATTR_DELEG;
9110-
inode_lock(inode);
9111-
err = notify_change(&nop_mnt_idmap, dentry, &attrs, NULL);
9112-
inode_unlock(inode);
9183+
err = cb_getattr_update_times(dentry, dp);
91139184
if (err) {
91149185
status = nfserrno(err);
91159186
goto out_status;

fs/nfsd/nfs4xdr.c

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3400,7 +3400,8 @@ static __be32 nfsd4_encode_fattr4_xattr_support(struct xdr_stream *xdr,
34003400

34013401
#define NFSD_OA_SHARE_ACCESS_WANT (BIT(OPEN_ARGS_SHARE_ACCESS_WANT_ANY_DELEG) | \
34023402
BIT(OPEN_ARGS_SHARE_ACCESS_WANT_NO_DELEG) | \
3403-
BIT(OPEN_ARGS_SHARE_ACCESS_WANT_CANCEL))
3403+
BIT(OPEN_ARGS_SHARE_ACCESS_WANT_CANCEL) | \
3404+
BIT(OPEN_ARGS_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS))
34043405

34053406
#define NFSD_OA_OPEN_CLAIM (BIT(OPEN_ARGS_OPEN_CLAIM_NULL) | \
34063407
BIT(OPEN_ARGS_OPEN_CLAIM_PREVIOUS) | \
@@ -3593,7 +3594,11 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
35933594
if (status)
35943595
goto out;
35953596
}
3596-
if (attrmask[0] & (FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE)) {
3597+
if ((attrmask[0] & (FATTR4_WORD0_CHANGE |
3598+
FATTR4_WORD0_SIZE)) ||
3599+
(attrmask[1] & (FATTR4_WORD1_TIME_ACCESS |
3600+
FATTR4_WORD1_TIME_MODIFY |
3601+
FATTR4_WORD1_TIME_METADATA))) {
35973602
status = nfsd4_deleg_getattr_conflict(rqstp, dentry, &dp);
35983603
if (status)
35993604
goto out;
@@ -3608,8 +3613,14 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
36083613
if (ncf->ncf_file_modified) {
36093614
++ncf->ncf_initial_cinfo;
36103615
args.stat.size = ncf->ncf_cur_fsize;
3616+
if (!timespec64_is_epoch(&ncf->ncf_cb_mtime))
3617+
args.stat.mtime = ncf->ncf_cb_mtime;
36113618
}
36123619
args.change_attr = ncf->ncf_initial_cinfo;
3620+
3621+
if (!timespec64_is_epoch(&ncf->ncf_cb_atime))
3622+
args.stat.atime = ncf->ncf_cb_atime;
3623+
36133624
nfs4_put_stid(&dp->dl_stid);
36143625
} else {
36153626
args.change_attr = nfsd4_change_attribute(&args.stat);

fs/nfsd/nfsd.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,8 @@ enum {
456456
FATTR4_WORD2_MODE_UMASK | \
457457
NFSD4_2_SECURITY_ATTRS | \
458458
FATTR4_WORD2_XATTR_SUPPORT | \
459+
FATTR4_WORD2_TIME_DELEG_ACCESS | \
460+
FATTR4_WORD2_TIME_DELEG_MODIFY | \
459461
FATTR4_WORD2_OPEN_ARGUMENTS)
460462

461463
extern const u32 nfsd_suppattrs[3][3];

fs/nfsd/state.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ struct nfs4_cb_fattr {
159159
/* from CB_GETATTR reply */
160160
u64 ncf_cb_change;
161161
u64 ncf_cb_fsize;
162+
struct timespec64 ncf_cb_mtime;
163+
struct timespec64 ncf_cb_atime;
162164

163165
unsigned long ncf_cb_flags;
164166
bool ncf_file_modified;

fs/nfsd/xdr4cb.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,20 @@
5959
* 1: CB_GETATTR opcode (32-bit)
6060
* N: file_handle
6161
* 1: number of entry in attribute array (32-bit)
62-
* 1: entry 0 in attribute array (32-bit)
62+
* 3: entry 0-2 in attribute array (32-bit * 3)
6363
*/
6464
#define NFS4_enc_cb_getattr_sz (cb_compound_enc_hdr_sz + \
6565
cb_sequence_enc_sz + \
66-
1 + enc_nfs4_fh_sz + 1 + 1)
66+
1 + enc_nfs4_fh_sz + 1 + 3)
6767
/*
6868
* 4: fattr_bitmap_maxsz
6969
* 1: attribute array len
7070
* 2: change attr (64-bit)
7171
* 2: size (64-bit)
72+
* 2: atime.seconds (64-bit)
73+
* 1: atime.nanoseconds (32-bit)
74+
* 2: mtime.seconds (64-bit)
75+
* 1: mtime.nanoseconds (32-bit)
7276
*/
7377
#define NFS4_dec_cb_getattr_sz (cb_compound_dec_hdr_sz + \
74-
cb_sequence_dec_sz + 4 + 1 + 2 + 2 + op_dec_sz)
78+
cb_sequence_dec_sz + 4 + 1 + 2 + 2 + 2 + 1 + 2 + 1 + op_dec_sz)

include/linux/time64.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ static inline int timespec64_equal(const struct timespec64 *a,
4949
return (a->tv_sec == b->tv_sec) && (a->tv_nsec == b->tv_nsec);
5050
}
5151

52+
static inline bool timespec64_is_epoch(const struct timespec64 *ts)
53+
{
54+
return ts->tv_sec == 0 && ts->tv_nsec == 0;
55+
}
56+
5257
/*
5358
* lhs < rhs: return <0
5459
* lhs == rhs: return 0

0 commit comments

Comments
 (0)