Skip to content

Commit 6c41d9a

Browse files
daimngochucklever
authored andcommitted
NFSD: handle GETATTR conflict with write delegation
If the GETATTR request on a file that has write delegation in effect and the request attributes include the change info and size attribute then the request is handled as below: Server sends CB_GETATTR to client to get the latest change info and file size. If these values are the same as the server's cached values then the GETATTR proceeds as normal. If either the change info or file size is different from the server's cached values, or the file was already marked as modified, then: . update time_modify and time_metadata into file's metadata with current time . encode GETATTR as normal except the file size is encoded with the value returned from CB_GETATTR . mark the file as modified If the CB_GETATTR fails for any reasons, the delegation is recalled and NFS4ERR_DELAY is returned for the GETATTR. Signed-off-by: Dai Ngo <[email protected]> Reviewed-by: Jeff Layton <[email protected]> Signed-off-by: Chuck Lever <[email protected]>
1 parent 738401a commit 6c41d9a

File tree

3 files changed

+121
-14
lines changed

3 files changed

+121
-14
lines changed

fs/nfsd/nfs4state.c

Lines changed: 103 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ static void free_session(struct nfsd4_session *);
127127

128128
static const struct nfsd4_callback_ops nfsd4_cb_recall_ops;
129129
static const struct nfsd4_callback_ops nfsd4_cb_notify_lock_ops;
130+
static const struct nfsd4_callback_ops nfsd4_cb_getattr_ops;
130131

131132
static struct workqueue_struct *laundry_wq;
132133

@@ -1187,6 +1188,10 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_file *fp,
11871188
dp->dl_recalled = false;
11881189
nfsd4_init_cb(&dp->dl_recall, dp->dl_stid.sc_client,
11891190
&nfsd4_cb_recall_ops, NFSPROC4_CLNT_CB_RECALL);
1191+
nfsd4_init_cb(&dp->dl_cb_fattr.ncf_getattr, dp->dl_stid.sc_client,
1192+
&nfsd4_cb_getattr_ops, NFSPROC4_CLNT_CB_GETATTR);
1193+
dp->dl_cb_fattr.ncf_file_modified = false;
1194+
dp->dl_cb_fattr.ncf_cb_bmap[0] = FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE;
11901195
get_nfs4_file(fp);
11911196
dp->dl_stid.sc_file = fp;
11921197
return dp;
@@ -2894,11 +2899,56 @@ nfsd4_cb_recall_any_release(struct nfsd4_callback *cb)
28942899
spin_unlock(&nn->client_lock);
28952900
}
28962901

2902+
static int
2903+
nfsd4_cb_getattr_done(struct nfsd4_callback *cb, struct rpc_task *task)
2904+
{
2905+
struct nfs4_cb_fattr *ncf =
2906+
container_of(cb, struct nfs4_cb_fattr, ncf_getattr);
2907+
2908+
ncf->ncf_cb_status = task->tk_status;
2909+
switch (task->tk_status) {
2910+
case -NFS4ERR_DELAY:
2911+
rpc_delay(task, 2 * HZ);
2912+
return 0;
2913+
default:
2914+
return 1;
2915+
}
2916+
}
2917+
2918+
static void
2919+
nfsd4_cb_getattr_release(struct nfsd4_callback *cb)
2920+
{
2921+
struct nfs4_cb_fattr *ncf =
2922+
container_of(cb, struct nfs4_cb_fattr, ncf_getattr);
2923+
struct nfs4_delegation *dp =
2924+
container_of(ncf, struct nfs4_delegation, dl_cb_fattr);
2925+
2926+
nfs4_put_stid(&dp->dl_stid);
2927+
clear_bit(CB_GETATTR_BUSY, &ncf->ncf_cb_flags);
2928+
wake_up_bit(&ncf->ncf_cb_flags, CB_GETATTR_BUSY);
2929+
}
2930+
28972931
static const struct nfsd4_callback_ops nfsd4_cb_recall_any_ops = {
28982932
.done = nfsd4_cb_recall_any_done,
28992933
.release = nfsd4_cb_recall_any_release,
29002934
};
29012935

2936+
static const struct nfsd4_callback_ops nfsd4_cb_getattr_ops = {
2937+
.done = nfsd4_cb_getattr_done,
2938+
.release = nfsd4_cb_getattr_release,
2939+
};
2940+
2941+
void nfs4_cb_getattr(struct nfs4_cb_fattr *ncf)
2942+
{
2943+
struct nfs4_delegation *dp =
2944+
container_of(ncf, struct nfs4_delegation, dl_cb_fattr);
2945+
2946+
if (test_and_set_bit(CB_GETATTR_BUSY, &ncf->ncf_cb_flags))
2947+
return;
2948+
refcount_inc(&dp->dl_stid.sc_count);
2949+
nfsd4_run_cb(&ncf->ncf_getattr);
2950+
}
2951+
29022952
static struct nfs4_client *create_client(struct xdr_netobj name,
29032953
struct svc_rqst *rqstp, nfs4_verifier *verf)
29042954
{
@@ -5634,6 +5684,8 @@ nfs4_open_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
56345684
struct svc_fh *parent = NULL;
56355685
int cb_up;
56365686
int status = 0;
5687+
struct kstat stat;
5688+
struct path path;
56375689

56385690
cb_up = nfsd4_cb_channel_good(oo->oo_owner.so_client);
56395691
open->op_recall = 0;
@@ -5671,6 +5723,18 @@ nfs4_open_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
56715723
if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) {
56725724
open->op_delegate_type = NFS4_OPEN_DELEGATE_WRITE;
56735725
trace_nfsd_deleg_write(&dp->dl_stid.sc_stateid);
5726+
path.mnt = currentfh->fh_export->ex_path.mnt;
5727+
path.dentry = currentfh->fh_dentry;
5728+
if (vfs_getattr(&path, &stat,
5729+
(STATX_SIZE | STATX_CTIME | STATX_CHANGE_COOKIE),
5730+
AT_STATX_SYNC_AS_STAT)) {
5731+
nfs4_put_stid(&dp->dl_stid);
5732+
destroy_delegation(dp);
5733+
goto out_no_deleg;
5734+
}
5735+
dp->dl_cb_fattr.ncf_cur_fsize = stat.size;
5736+
dp->dl_cb_fattr.ncf_initial_cinfo =
5737+
nfsd4_change_attribute(&stat, d_inode(currentfh->fh_dentry));
56745738
} else {
56755739
open->op_delegate_type = NFS4_OPEN_DELEGATE_READ;
56765740
trace_nfsd_deleg_read(&dp->dl_stid.sc_stateid);
@@ -8407,6 +8471,8 @@ nfsd4_get_writestateid(struct nfsd4_compound_state *cstate,
84078471
* nfsd4_deleg_getattr_conflict - Recall if GETATTR causes conflict
84088472
* @rqstp: RPC transaction context
84098473
* @inode: file to be checked for a conflict
8474+
* @modified: return true if file was modified
8475+
* @size: new size of file if modified is true
84108476
*
84118477
* This function is called when there is a conflict between a write
84128478
* delegation and a change/size GETATTR from another client. The server
@@ -8415,21 +8481,23 @@ nfsd4_get_writestateid(struct nfsd4_compound_state *cstate,
84158481
* delegation before replying to the GETATTR. See RFC 8881 section
84168482
* 18.7.4.
84178483
*
8418-
* The current implementation does not support CB_GETATTR yet. However
8419-
* this can avoid recalling the delegation could be added in follow up
8420-
* work.
8421-
*
84228484
* Returns 0 if there is no conflict; otherwise an nfs_stat
84238485
* code is returned.
84248486
*/
84258487
__be32
8426-
nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct inode *inode)
8488+
nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct inode *inode,
8489+
bool *modified, u64 *size)
84278490
{
8428-
__be32 status;
84298491
struct file_lock_context *ctx;
8430-
struct file_lock *fl;
84318492
struct nfs4_delegation *dp;
8493+
struct nfs4_cb_fattr *ncf;
8494+
struct file_lock *fl;
8495+
struct iattr attrs;
8496+
__be32 status;
8497+
8498+
might_sleep();
84328499

8500+
*modified = false;
84338501
ctx = locks_inode_context(inode);
84348502
if (!ctx)
84358503
return 0;
@@ -8456,10 +8524,34 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct inode *inode)
84568524
break_lease:
84578525
spin_unlock(&ctx->flc_lock);
84588526
nfsd_stats_wdeleg_getattr_inc();
8459-
status = nfserrno(nfsd_open_break_lease(inode, NFSD_MAY_READ));
8460-
if (status != nfserr_jukebox ||
8461-
!nfsd_wait_for_delegreturn(rqstp, inode))
8462-
return status;
8527+
8528+
dp = fl->fl_owner;
8529+
ncf = &dp->dl_cb_fattr;
8530+
nfs4_cb_getattr(&dp->dl_cb_fattr);
8531+
wait_on_bit(&ncf->ncf_cb_flags, CB_GETATTR_BUSY, TASK_INTERRUPTIBLE);
8532+
if (ncf->ncf_cb_status) {
8533+
status = nfserrno(nfsd_open_break_lease(inode, NFSD_MAY_READ));
8534+
if (status != nfserr_jukebox ||
8535+
!nfsd_wait_for_delegreturn(rqstp, inode))
8536+
return status;
8537+
}
8538+
if (!ncf->ncf_file_modified &&
8539+
(ncf->ncf_initial_cinfo != ncf->ncf_cb_change ||
8540+
ncf->ncf_cur_fsize != ncf->ncf_cb_fsize))
8541+
ncf->ncf_file_modified = true;
8542+
if (ncf->ncf_file_modified) {
8543+
/*
8544+
* The server would not update the file's metadata
8545+
* with the client's modified size.
8546+
*/
8547+
attrs.ia_mtime = attrs.ia_ctime = current_time(inode);
8548+
attrs.ia_valid = ATTR_MTIME | ATTR_CTIME;
8549+
setattr_copy(&nop_mnt_idmap, inode, &attrs);
8550+
mark_inode_dirty(inode);
8551+
ncf->ncf_cur_fsize = ncf->ncf_cb_fsize;
8552+
*size = ncf->ncf_cur_fsize;
8553+
*modified = true;
8554+
}
84638555
return 0;
84648556
}
84658557
break;

fs/nfsd/nfs4xdr.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2975,6 +2975,8 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
29752975
.dentry = dentry,
29762976
};
29772977
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
2978+
bool file_modified;
2979+
u64 size = 0;
29782980

29792981
BUG_ON(bmval1 & NFSD_WRITEONLY_ATTRS_WORD1);
29802982
BUG_ON(!nfsd_attrs_supported(minorversion, bmval));
@@ -2985,7 +2987,8 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
29852987
goto out;
29862988
}
29872989
if (bmval0 & (FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE)) {
2988-
status = nfsd4_deleg_getattr_conflict(rqstp, d_inode(dentry));
2990+
status = nfsd4_deleg_getattr_conflict(rqstp, d_inode(dentry),
2991+
&file_modified, &size);
29892992
if (status)
29902993
goto out;
29912994
}
@@ -3112,7 +3115,10 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
31123115
p = xdr_reserve_space(xdr, 8);
31133116
if (!p)
31143117
goto out_resource;
3115-
p = xdr_encode_hyper(p, stat.size);
3118+
if (file_modified)
3119+
p = xdr_encode_hyper(p, size);
3120+
else
3121+
p = xdr_encode_hyper(p, stat.size);
31163122
}
31173123
if (bmval0 & FATTR4_WORD0_LINK_SUPPORT) {
31183124
p = xdr_reserve_space(xdr, 4);

fs/nfsd/state.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,16 @@ struct nfs4_cb_fattr {
125125
/* from CB_GETATTR reply */
126126
u64 ncf_cb_change;
127127
u64 ncf_cb_fsize;
128+
129+
unsigned long ncf_cb_flags;
130+
bool ncf_file_modified;
131+
u64 ncf_initial_cinfo;
132+
u64 ncf_cur_fsize;
128133
};
129134

135+
/* bits for ncf_cb_flags */
136+
#define CB_GETATTR_BUSY 0
137+
130138
/*
131139
* Represents a delegation stateid. The nfs4_client holds references to these
132140
* and they are put when it is being destroyed or when the delegation is
@@ -748,5 +756,6 @@ static inline bool try_to_expire_client(struct nfs4_client *clp)
748756
}
749757

750758
extern __be32 nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp,
751-
struct inode *inode);
759+
struct inode *inode, bool *file_modified, u64 *size);
760+
extern void nfs4_cb_getattr(struct nfs4_cb_fattr *ncf);
752761
#endif /* NFSD4_STATE_H */

0 commit comments

Comments
 (0)