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

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 <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>

authored by

Jeff Layton and committed by
Chuck Lever
6ae30d6e cee9b4ef

+152 -23
+38 -4
fs/nfsd/nfs4callback.c
··· 42 42 #include "trace.h" 43 43 #include "xdr4cb.h" 44 44 #include "xdr4.h" 45 + #include "nfs4xdr_gen.h" 45 46 46 47 #define NFSDDBG_FACILITY NFSDDBG_PROC 47 48 ··· 94 93 { 95 94 fattr->ncf_cb_change = 0; 96 95 fattr->ncf_cb_fsize = 0; 96 + fattr->ncf_cb_atime.tv_sec = 0; 97 + fattr->ncf_cb_atime.tv_nsec = 0; 98 + fattr->ncf_cb_mtime.tv_sec = 0; 99 + fattr->ncf_cb_mtime.tv_nsec = 0; 100 + 97 101 if (bitmap[0] & FATTR4_WORD0_CHANGE) 98 102 if (xdr_stream_decode_u64(xdr, &fattr->ncf_cb_change) < 0) 99 103 return -NFSERR_BAD_XDR; 100 104 if (bitmap[0] & FATTR4_WORD0_SIZE) 101 105 if (xdr_stream_decode_u64(xdr, &fattr->ncf_cb_fsize) < 0) 102 106 return -NFSERR_BAD_XDR; 107 + if (bitmap[2] & FATTR4_WORD2_TIME_DELEG_ACCESS) { 108 + fattr4_time_deleg_access access; 109 + 110 + if (!xdrgen_decode_fattr4_time_deleg_access(xdr, &access)) 111 + return -NFSERR_BAD_XDR; 112 + fattr->ncf_cb_atime.tv_sec = access.seconds; 113 + fattr->ncf_cb_atime.tv_nsec = access.nseconds; 114 + 115 + } 116 + if (bitmap[2] & FATTR4_WORD2_TIME_DELEG_MODIFY) { 117 + fattr4_time_deleg_modify modify; 118 + 119 + if (!xdrgen_decode_fattr4_time_deleg_modify(xdr, &modify)) 120 + return -NFSERR_BAD_XDR; 121 + fattr->ncf_cb_mtime.tv_sec = modify.seconds; 122 + fattr->ncf_cb_mtime.tv_nsec = modify.nseconds; 123 + 124 + } 103 125 return 0; 104 126 } 105 127 ··· 388 364 struct nfs4_delegation *dp = container_of(fattr, struct nfs4_delegation, dl_cb_fattr); 389 365 struct knfsd_fh *fh = &dp->dl_stid.sc_file->fi_fhandle; 390 366 struct nfs4_cb_fattr *ncf = &dp->dl_cb_fattr; 391 - u32 bmap[1]; 367 + u32 bmap_size = 1; 368 + u32 bmap[3]; 392 369 393 370 bmap[0] = FATTR4_WORD0_SIZE; 394 371 if (!ncf->ncf_file_modified) 395 372 bmap[0] |= FATTR4_WORD0_CHANGE; 396 373 374 + if (deleg_attrs_deleg(dp->dl_type)) { 375 + bmap[1] = 0; 376 + bmap[2] = FATTR4_WORD2_TIME_DELEG_ACCESS | FATTR4_WORD2_TIME_DELEG_MODIFY; 377 + bmap_size = 3; 378 + } 397 379 encode_nfs_cb_opnum4(xdr, OP_CB_GETATTR); 398 380 encode_nfs_fh4(xdr, fh); 399 - encode_bitmap4(xdr, bmap, ARRAY_SIZE(bmap)); 381 + encode_bitmap4(xdr, bmap, bmap_size); 400 382 hdr->nops++; 401 383 } 402 384 ··· 666 636 struct nfs4_cb_compound_hdr hdr; 667 637 int status; 668 638 u32 bitmap[3] = {0}; 669 - u32 attrlen; 639 + u32 attrlen, maxlen; 670 640 struct nfs4_cb_fattr *ncf = 671 641 container_of(cb, struct nfs4_cb_fattr, ncf_getattr); 672 642 ··· 685 655 return -NFSERR_BAD_XDR; 686 656 if (xdr_stream_decode_u32(xdr, &attrlen) < 0) 687 657 return -NFSERR_BAD_XDR; 688 - if (attrlen > (sizeof(ncf->ncf_cb_change) + sizeof(ncf->ncf_cb_fsize))) 658 + maxlen = sizeof(ncf->ncf_cb_change) + sizeof(ncf->ncf_cb_fsize); 659 + if (bitmap[2] != 0) 660 + maxlen += (sizeof(ncf->ncf_cb_mtime.tv_sec) + 661 + sizeof(ncf->ncf_cb_mtime.tv_nsec)) * 2; 662 + if (attrlen > maxlen) 689 663 return -NFSERR_BAD_XDR; 690 664 status = decode_cb_fattr4(xdr, bitmap, ncf); 691 665 return status;
+85 -14
fs/nfsd/nfs4state.c
··· 5951 5951 nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp, 5952 5952 struct svc_fh *parent) 5953 5953 { 5954 - int status = 0; 5954 + bool deleg_ts = open->op_deleg_want & OPEN4_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS; 5955 5955 struct nfs4_client *clp = stp->st_stid.sc_client; 5956 5956 struct nfs4_file *fp = stp->st_stid.sc_file; 5957 5957 struct nfs4_clnt_odstate *odstate = stp->st_clnt_odstate; 5958 5958 struct nfs4_delegation *dp; 5959 5959 struct nfsd_file *nf = NULL; 5960 5960 struct file_lease *fl; 5961 + int status = 0; 5961 5962 u32 dl_type; 5962 5963 5963 5964 /* ··· 5983 5982 */ 5984 5983 if ((open->op_share_access & NFS4_SHARE_ACCESS_BOTH) == NFS4_SHARE_ACCESS_BOTH) { 5985 5984 nf = find_rw_file(fp); 5986 - dl_type = OPEN_DELEGATE_WRITE; 5985 + dl_type = deleg_ts ? OPEN_DELEGATE_WRITE_ATTRS_DELEG : OPEN_DELEGATE_WRITE; 5987 5986 } 5988 5987 5989 5988 /* ··· 5992 5991 */ 5993 5992 if (!nf && (open->op_share_access & NFS4_SHARE_ACCESS_READ)) { 5994 5993 nf = find_readable_file(fp); 5995 - dl_type = OPEN_DELEGATE_READ; 5994 + dl_type = deleg_ts ? OPEN_DELEGATE_READ_ATTRS_DELEG : OPEN_DELEGATE_READ; 5996 5995 } 5997 5996 5998 5997 if (!nf) ··· 6150 6149 nfs4_open_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp, 6151 6150 struct svc_fh *currentfh) 6152 6151 { 6153 - struct nfs4_delegation *dp; 6152 + bool deleg_ts = open->op_deleg_want & OPEN4_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS; 6154 6153 struct nfs4_openowner *oo = openowner(stp->st_stateowner); 6155 6154 struct nfs4_client *clp = stp->st_stid.sc_client; 6156 6155 struct svc_fh *parent = NULL; 6157 - int cb_up; 6158 - int status = 0; 6156 + struct nfs4_delegation *dp; 6159 6157 struct kstat stat; 6158 + int status = 0; 6159 + int cb_up; 6160 6160 6161 6161 cb_up = nfsd4_cb_channel_good(oo->oo_owner.so_client); 6162 6162 open->op_recall = false; ··· 6198 6196 destroy_delegation(dp); 6199 6197 goto out_no_deleg; 6200 6198 } 6201 - open->op_delegate_type = OPEN_DELEGATE_WRITE; 6199 + open->op_delegate_type = deleg_ts ? OPEN_DELEGATE_WRITE_ATTRS_DELEG : 6200 + OPEN_DELEGATE_WRITE; 6202 6201 dp->dl_cb_fattr.ncf_cur_fsize = stat.size; 6203 6202 dp->dl_cb_fattr.ncf_initial_cinfo = nfsd4_change_attribute(&stat); 6204 6203 trace_nfsd_deleg_write(&dp->dl_stid.sc_stateid); 6205 6204 } else { 6206 - open->op_delegate_type = OPEN_DELEGATE_READ; 6205 + open->op_delegate_type = deleg_ts ? OPEN_DELEGATE_READ_ATTRS_DELEG : 6206 + OPEN_DELEGATE_READ; 6207 6207 trace_nfsd_deleg_read(&dp->dl_stid.sc_stateid); 6208 6208 } 6209 6209 nfs4_put_stid(&dp->dl_stid); ··· 9022 9018 } 9023 9019 9024 9020 /** 9021 + * set_cb_time - vet and set the timespec for a cb_getattr update 9022 + * @cb: timestamp from the CB_GETATTR response 9023 + * @orig: original timestamp in the inode 9024 + * @now: current time 9025 + * 9026 + * Given a timestamp in a CB_GETATTR response, check it against the 9027 + * current timestamp in the inode and the current time. Returns true 9028 + * if the inode's timestamp needs to be updated, and false otherwise. 9029 + * @cb may also be changed if the timestamp needs to be clamped. 9030 + */ 9031 + static bool set_cb_time(struct timespec64 *cb, const struct timespec64 *orig, 9032 + const struct timespec64 *now) 9033 + { 9034 + 9035 + /* 9036 + * "When the time presented is before the original time, then the 9037 + * update is ignored." Also no need to update if there is no change. 9038 + */ 9039 + if (timespec64_compare(cb, orig) <= 0) 9040 + return false; 9041 + 9042 + /* 9043 + * "When the time presented is in the future, the server can either 9044 + * clamp the new time to the current time, or it may 9045 + * return NFS4ERR_DELAY to the client, allowing it to retry." 9046 + */ 9047 + if (timespec64_compare(cb, now) > 0) { 9048 + /* clamp it */ 9049 + *cb = *now; 9050 + } 9051 + 9052 + return true; 9053 + } 9054 + 9055 + static int cb_getattr_update_times(struct dentry *dentry, struct nfs4_delegation *dp) 9056 + { 9057 + struct inode *inode = d_inode(dentry); 9058 + struct timespec64 now = current_time(inode); 9059 + struct nfs4_cb_fattr *ncf = &dp->dl_cb_fattr; 9060 + struct iattr attrs = { }; 9061 + int ret; 9062 + 9063 + if (deleg_attrs_deleg(dp->dl_type)) { 9064 + struct timespec64 atime = inode_get_atime(inode); 9065 + struct timespec64 mtime = inode_get_mtime(inode); 9066 + 9067 + attrs.ia_atime = ncf->ncf_cb_atime; 9068 + attrs.ia_mtime = ncf->ncf_cb_mtime; 9069 + 9070 + if (set_cb_time(&attrs.ia_atime, &atime, &now)) 9071 + attrs.ia_valid |= ATTR_ATIME | ATTR_ATIME_SET; 9072 + 9073 + if (set_cb_time(&attrs.ia_mtime, &mtime, &now)) { 9074 + attrs.ia_valid |= ATTR_CTIME | ATTR_MTIME | ATTR_MTIME_SET; 9075 + attrs.ia_ctime = attrs.ia_mtime; 9076 + } 9077 + } else { 9078 + attrs.ia_valid |= ATTR_MTIME | ATTR_CTIME; 9079 + attrs.ia_mtime = attrs.ia_ctime = now; 9080 + } 9081 + 9082 + if (!attrs.ia_valid) 9083 + return 0; 9084 + 9085 + attrs.ia_valid |= ATTR_DELEG; 9086 + inode_lock(inode); 9087 + ret = notify_change(&nop_mnt_idmap, dentry, &attrs, NULL); 9088 + inode_unlock(inode); 9089 + return ret; 9090 + } 9091 + 9092 + /** 9025 9093 * nfsd4_deleg_getattr_conflict - Recall if GETATTR causes conflict 9026 9094 * @rqstp: RPC transaction context 9027 9095 * @dentry: dentry of inode to be checked for a conflict ··· 9119 9043 struct file_lock_context *ctx; 9120 9044 struct nfs4_delegation *dp = NULL; 9121 9045 struct file_lease *fl; 9122 - struct iattr attrs; 9123 9046 struct nfs4_cb_fattr *ncf; 9124 9047 struct inode *inode = d_inode(dentry); 9125 9048 ··· 9180 9105 * not update the file's metadata with the client's 9181 9106 * modified size 9182 9107 */ 9183 - attrs.ia_mtime = attrs.ia_ctime = current_time(inode); 9184 - attrs.ia_valid = ATTR_MTIME | ATTR_CTIME | ATTR_DELEG; 9185 - inode_lock(inode); 9186 - err = notify_change(&nop_mnt_idmap, dentry, &attrs, NULL); 9187 - inode_unlock(inode); 9108 + err = cb_getattr_update_times(dentry, dp); 9188 9109 if (err) { 9189 9110 status = nfserrno(err); 9190 9111 goto out_status;
+13 -2
fs/nfsd/nfs4xdr.c
··· 3400 3400 3401 3401 #define NFSD_OA_SHARE_ACCESS_WANT (BIT(OPEN_ARGS_SHARE_ACCESS_WANT_ANY_DELEG) | \ 3402 3402 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)) 3404 3405 3405 3406 #define NFSD_OA_OPEN_CLAIM (BIT(OPEN_ARGS_OPEN_CLAIM_NULL) | \ 3406 3407 BIT(OPEN_ARGS_OPEN_CLAIM_PREVIOUS) | \ ··· 3594 3593 if (status) 3595 3594 goto out; 3596 3595 } 3597 - if (attrmask[0] & (FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE)) { 3596 + if ((attrmask[0] & (FATTR4_WORD0_CHANGE | 3597 + FATTR4_WORD0_SIZE)) || 3598 + (attrmask[1] & (FATTR4_WORD1_TIME_ACCESS | 3599 + FATTR4_WORD1_TIME_MODIFY | 3600 + FATTR4_WORD1_TIME_METADATA))) { 3598 3601 status = nfsd4_deleg_getattr_conflict(rqstp, dentry, &dp); 3599 3602 if (status) 3600 3603 goto out; ··· 3613 3608 if (ncf->ncf_file_modified) { 3614 3609 ++ncf->ncf_initial_cinfo; 3615 3610 args.stat.size = ncf->ncf_cur_fsize; 3611 + if (!timespec64_is_epoch(&ncf->ncf_cb_mtime)) 3612 + args.stat.mtime = ncf->ncf_cb_mtime; 3616 3613 } 3617 3614 args.change_attr = ncf->ncf_initial_cinfo; 3615 + 3616 + if (!timespec64_is_epoch(&ncf->ncf_cb_atime)) 3617 + args.stat.atime = ncf->ncf_cb_atime; 3618 + 3618 3619 nfs4_put_stid(&dp->dl_stid); 3619 3620 } else { 3620 3621 args.change_attr = nfsd4_change_attribute(&args.stat);
+2
fs/nfsd/nfsd.h
··· 456 456 FATTR4_WORD2_MODE_UMASK | \ 457 457 NFSD4_2_SECURITY_ATTRS | \ 458 458 FATTR4_WORD2_XATTR_SUPPORT | \ 459 + FATTR4_WORD2_TIME_DELEG_ACCESS | \ 460 + FATTR4_WORD2_TIME_DELEG_MODIFY | \ 459 461 FATTR4_WORD2_OPEN_ARGUMENTS) 460 462 461 463 extern const u32 nfsd_suppattrs[3][3];
+2
fs/nfsd/state.h
··· 159 159 /* from CB_GETATTR reply */ 160 160 u64 ncf_cb_change; 161 161 u64 ncf_cb_fsize; 162 + struct timespec64 ncf_cb_mtime; 163 + struct timespec64 ncf_cb_atime; 162 164 163 165 unsigned long ncf_cb_flags; 164 166 bool ncf_file_modified;
+7 -3
fs/nfsd/xdr4cb.h
··· 59 59 * 1: CB_GETATTR opcode (32-bit) 60 60 * N: file_handle 61 61 * 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) 63 63 */ 64 64 #define NFS4_enc_cb_getattr_sz (cb_compound_enc_hdr_sz + \ 65 65 cb_sequence_enc_sz + \ 66 - 1 + enc_nfs4_fh_sz + 1 + 1) 66 + 1 + enc_nfs4_fh_sz + 1 + 3) 67 67 /* 68 68 * 4: fattr_bitmap_maxsz 69 69 * 1: attribute array len 70 70 * 2: change attr (64-bit) 71 71 * 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) 72 76 */ 73 77 #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)
+5
include/linux/time64.h
··· 49 49 return (a->tv_sec == b->tv_sec) && (a->tv_nsec == b->tv_nsec); 50 50 } 51 51 52 + static inline bool timespec64_is_epoch(const struct timespec64 *ts) 53 + { 54 + return ts->tv_sec == 0 && ts->tv_nsec == 0; 55 + } 56 + 52 57 /* 53 58 * lhs < rhs: return <0 54 59 * lhs == rhs: return 0