Skip to content

Commit e5e9b24

Browse files
jtlaytonchucklever
authored andcommitted
nfsd: freeze c/mtime updates with outstanding WRITE_ATTRS delegation
Instead of allowing the ctime to roll backward with a WRITE_ATTRS delegation, set FMODE_NOCMTIME on the file and have it skip mtime and ctime updates. It is possible that the client will never send a SETATTR to set the times before returning the delegation. Add two new bools to struct nfs4_delegation: dl_written: tracks whether the file has been written since the delegation was granted. This is set in the WRITE and LAYOUTCOMMIT handlers. dl_setattr: tracks whether the client has sent at least one valid mtime that can also update the ctime in a SETATTR. When unlocking the lease for the delegation, clear FMODE_NOCMTIME. If the file has been written, but no setattr for the delegated mtime and ctime has been done, update the timestamps to current_time(). Suggested-by: NeilBrown <[email protected]> Signed-off-by: Jeff Layton <[email protected]> Signed-off-by: Chuck Lever <[email protected]>
1 parent b40b1ba commit e5e9b24

File tree

3 files changed

+69
-3
lines changed

3 files changed

+69
-3
lines changed

fs/nfsd/nfs4proc.c

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1151,7 +1151,9 @@ vet_deleg_attrs(struct nfsd4_setattr *setattr, struct nfs4_delegation *dp)
11511151
if (setattr->sa_bmval[2] & FATTR4_WORD2_TIME_DELEG_MODIFY) {
11521152
if (nfsd4_vet_deleg_time(&iattr->ia_mtime, &dp->dl_mtime, &now)) {
11531153
iattr->ia_ctime = iattr->ia_mtime;
1154-
if (!nfsd4_vet_deleg_time(&iattr->ia_ctime, &dp->dl_ctime, &now))
1154+
if (nfsd4_vet_deleg_time(&iattr->ia_ctime, &dp->dl_ctime, &now))
1155+
dp->dl_setattr = true;
1156+
else
11551157
iattr->ia_valid &= ~(ATTR_CTIME | ATTR_CTIME_SET);
11561158
} else {
11571159
iattr->ia_valid &= ~(ATTR_CTIME | ATTR_CTIME_SET |
@@ -1238,12 +1240,26 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
12381240
return status;
12391241
}
12401242

1243+
static void nfsd4_file_mark_deleg_written(struct nfs4_file *fi)
1244+
{
1245+
spin_lock(&fi->fi_lock);
1246+
if (!list_empty(&fi->fi_delegations)) {
1247+
struct nfs4_delegation *dp = list_first_entry(&fi->fi_delegations,
1248+
struct nfs4_delegation, dl_perfile);
1249+
1250+
if (dp->dl_type == OPEN_DELEGATE_WRITE_ATTRS_DELEG)
1251+
dp->dl_written = true;
1252+
}
1253+
spin_unlock(&fi->fi_lock);
1254+
}
1255+
12411256
static __be32
12421257
nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
12431258
union nfsd4_op_u *u)
12441259
{
12451260
struct nfsd4_write *write = &u->write;
12461261
stateid_t *stateid = &write->wr_stateid;
1262+
struct nfs4_stid *stid = NULL;
12471263
struct nfsd_file *nf = NULL;
12481264
__be32 status = nfs_ok;
12491265
unsigned long cnt;
@@ -1256,10 +1272,15 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
12561272
trace_nfsd_write_start(rqstp, &cstate->current_fh,
12571273
write->wr_offset, cnt);
12581274
status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
1259-
stateid, WR_STATE, &nf, NULL);
1275+
stateid, WR_STATE, &nf, &stid);
12601276
if (status)
12611277
return status;
12621278

1279+
if (stid) {
1280+
nfsd4_file_mark_deleg_written(stid->sc_file);
1281+
nfs4_put_stid(stid);
1282+
}
1283+
12631284
write->wr_how_written = write->wr_stable_how;
12641285
status = nfsd_vfs_write(rqstp, &cstate->current_fh, nf,
12651286
write->wr_offset, &write->wr_payload,
@@ -2550,6 +2571,7 @@ nfsd4_layoutcommit(struct svc_rqst *rqstp,
25502571
mutex_unlock(&ls->ls_mutex);
25512572

25522573
nfserr = ops->proc_layoutcommit(inode, rqstp, lcp);
2574+
nfsd4_file_mark_deleg_written(ls->ls_stid.sc_file);
25532575
nfs4_put_stid(&ls->ls_stid);
25542576
out:
25552577
return nfserr;

fs/nfsd/nfs4state.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1222,13 +1222,50 @@ static void put_deleg_file(struct nfs4_file *fp)
12221222
nfs4_file_put_access(fp, NFS4_SHARE_ACCESS_READ);
12231223
}
12241224

1225+
static void nfsd4_finalize_deleg_timestamps(struct nfs4_delegation *dp, struct file *f)
1226+
{
1227+
struct iattr ia = { .ia_valid = ATTR_ATIME | ATTR_CTIME | ATTR_MTIME };
1228+
struct inode *inode = file_inode(f);
1229+
int ret;
1230+
1231+
/* don't do anything if FMODE_NOCMTIME isn't set */
1232+
if ((READ_ONCE(f->f_mode) & FMODE_NOCMTIME) == 0)
1233+
return;
1234+
1235+
spin_lock(&f->f_lock);
1236+
f->f_mode &= ~FMODE_NOCMTIME;
1237+
spin_unlock(&f->f_lock);
1238+
1239+
/* was it never written? */
1240+
if (!dp->dl_written)
1241+
return;
1242+
1243+
/* did it get a setattr for the timestamps at some point? */
1244+
if (dp->dl_setattr)
1245+
return;
1246+
1247+
/* Stamp everything to "now" */
1248+
inode_lock(inode);
1249+
ret = notify_change(&nop_mnt_idmap, f->f_path.dentry, &ia, NULL);
1250+
inode_unlock(inode);
1251+
if (ret) {
1252+
struct inode *inode = file_inode(f);
1253+
1254+
pr_notice_ratelimited("Unable to update timestamps on inode %02x:%02x:%lu: %d\n",
1255+
MAJOR(inode->i_sb->s_dev),
1256+
MINOR(inode->i_sb->s_dev),
1257+
inode->i_ino, ret);
1258+
}
1259+
}
1260+
12251261
static void nfs4_unlock_deleg_lease(struct nfs4_delegation *dp)
12261262
{
12271263
struct nfs4_file *fp = dp->dl_stid.sc_file;
12281264
struct nfsd_file *nf = fp->fi_deleg_file;
12291265

12301266
WARN_ON_ONCE(!fp->fi_delegees);
12311267

1268+
nfsd4_finalize_deleg_timestamps(dp, nf->nf_file);
12321269
kernel_setlease(nf->nf_file, F_UNLCK, NULL, (void **)&dp);
12331270
put_deleg_file(fp);
12341271
}
@@ -6265,6 +6302,8 @@ nfs4_open_delegation(struct svc_rqst *rqstp, struct nfsd4_open *open,
62656302
memcpy(&open->op_delegate_stateid, &dp->dl_stid.sc_stateid, sizeof(dp->dl_stid.sc_stateid));
62666303

62676304
if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) {
6305+
struct file *f = dp->dl_stid.sc_file->fi_deleg_file->nf_file;
6306+
62686307
if (!nfsd4_add_rdaccess_to_wrdeleg(rqstp, open, fh, stp) ||
62696308
!nfs4_delegation_stat(dp, currentfh, &stat)) {
62706309
nfs4_put_stid(&dp->dl_stid);
@@ -6278,6 +6317,9 @@ nfs4_open_delegation(struct svc_rqst *rqstp, struct nfsd4_open *open,
62786317
dp->dl_atime = stat.atime;
62796318
dp->dl_ctime = stat.ctime;
62806319
dp->dl_mtime = stat.mtime;
6320+
spin_lock(&f->f_lock);
6321+
f->f_mode |= FMODE_NOCMTIME;
6322+
spin_unlock(&f->f_lock);
62816323
trace_nfsd_deleg_write(&dp->dl_stid.sc_stateid);
62826324
} else {
62836325
open->op_delegate_type = deleg_ts && nfs4_delegation_stat(dp, currentfh, &stat) ?

fs/nfsd/state.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,10 +217,12 @@ struct nfs4_delegation {
217217
struct nfs4_clnt_odstate *dl_clnt_odstate;
218218
time64_t dl_time;
219219
u32 dl_type;
220-
/* For recall: */
220+
/* For recall: */
221221
int dl_retries;
222222
struct nfsd4_callback dl_recall;
223223
bool dl_recalled;
224+
bool dl_written;
225+
bool dl_setattr;
224226

225227
/* for CB_GETATTR */
226228
struct nfs4_cb_fattr dl_cb_fattr;

0 commit comments

Comments
 (0)