Skip to content

Commit a078a7d

Browse files
neilbrownchucklever
authored andcommitted
nfsd: untangle code in nfsd4_deleg_getattr_conflict()
The code in nfsd4_deleg_getattr_conflict() is convoluted and buggy. With this patch we: - properly handle non-nfsd leases. We must not assume flc_owner is a delegation unless fl_lmops == &nfsd_lease_mng_ops - move the main code out of the for loop - have a single exit which calls nfs4_put_stid() (and other exits which don't need to call that) [ jlayton: refactored on top of Neil's other patch: nfsd: fix nfsd4_deleg_getattr_conflict in presence of third party lease ] Fixes: c596772 ("NFSD: handle GETATTR conflict with write delegation") Signed-off-by: NeilBrown <[email protected]> Signed-off-by: Jeff Layton <[email protected]> Signed-off-by: Chuck Lever <[email protected]>
1 parent 5559c15 commit a078a7d

File tree

1 file changed

+62
-69
lines changed

1 file changed

+62
-69
lines changed

fs/nfsd/nfs4state.c

Lines changed: 62 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -8834,6 +8834,7 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry,
88348834
__be32 status;
88358835
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
88368836
struct file_lock_context *ctx;
8837+
struct nfs4_delegation *dp = NULL;
88378838
struct file_lease *fl;
88388839
struct iattr attrs;
88398840
struct nfs4_cb_fattr *ncf;
@@ -8843,84 +8844,76 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry,
88438844
ctx = locks_inode_context(inode);
88448845
if (!ctx)
88458846
return 0;
8847+
8848+
#define NON_NFSD_LEASE ((void *)1)
8849+
88468850
spin_lock(&ctx->flc_lock);
88478851
for_each_file_lock(fl, &ctx->flc_lease) {
8848-
unsigned char type = fl->c.flc_type;
8849-
88508852
if (fl->c.flc_flags == FL_LAYOUT)
88518853
continue;
8852-
if (fl->fl_lmops != &nfsd_lease_mng_ops) {
8853-
/*
8854-
* non-nfs lease, if it's a lease with F_RDLCK then
8855-
* we are done; there isn't any write delegation
8856-
* on this inode
8857-
*/
8858-
if (type == F_RDLCK)
8859-
break;
8860-
8861-
nfsd_stats_wdeleg_getattr_inc(nn);
8862-
spin_unlock(&ctx->flc_lock);
8863-
8864-
status = nfserrno(nfsd_open_break_lease(inode, NFSD_MAY_READ));
8854+
if (fl->c.flc_type == F_WRLCK) {
8855+
if (fl->fl_lmops == &nfsd_lease_mng_ops)
8856+
dp = fl->c.flc_owner;
8857+
else
8858+
dp = NON_NFSD_LEASE;
8859+
}
8860+
break;
8861+
}
8862+
if (dp == NULL || dp == NON_NFSD_LEASE ||
8863+
dp->dl_recall.cb_clp == *(rqstp->rq_lease_breaker)) {
8864+
spin_unlock(&ctx->flc_lock);
8865+
if (dp == NON_NFSD_LEASE) {
8866+
status = nfserrno(nfsd_open_break_lease(inode,
8867+
NFSD_MAY_READ));
88658868
if (status != nfserr_jukebox ||
88668869
!nfsd_wait_for_delegreturn(rqstp, inode))
88678870
return status;
8868-
return 0;
88698871
}
8870-
if (type == F_WRLCK) {
8871-
struct nfs4_delegation *dp = fl->c.flc_owner;
8872+
return 0;
8873+
}
88728874

8873-
if (dp->dl_recall.cb_clp == *(rqstp->rq_lease_breaker)) {
8874-
spin_unlock(&ctx->flc_lock);
8875-
return 0;
8876-
}
8877-
nfsd_stats_wdeleg_getattr_inc(nn);
8878-
dp = fl->c.flc_owner;
8879-
refcount_inc(&dp->dl_stid.sc_count);
8880-
ncf = &dp->dl_cb_fattr;
8881-
nfs4_cb_getattr(&dp->dl_cb_fattr);
8882-
spin_unlock(&ctx->flc_lock);
8883-
wait_on_bit_timeout(&ncf->ncf_cb_flags, CB_GETATTR_BUSY,
8884-
TASK_INTERRUPTIBLE, NFSD_CB_GETATTR_TIMEOUT);
8885-
if (ncf->ncf_cb_status) {
8886-
/* Recall delegation only if client didn't respond */
8887-
status = nfserrno(nfsd_open_break_lease(inode, NFSD_MAY_READ));
8888-
if (status != nfserr_jukebox ||
8889-
!nfsd_wait_for_delegreturn(rqstp, inode)) {
8890-
nfs4_put_stid(&dp->dl_stid);
8891-
return status;
8892-
}
8893-
}
8894-
if (!ncf->ncf_file_modified &&
8895-
(ncf->ncf_initial_cinfo != ncf->ncf_cb_change ||
8896-
ncf->ncf_cur_fsize != ncf->ncf_cb_fsize))
8897-
ncf->ncf_file_modified = true;
8898-
if (ncf->ncf_file_modified) {
8899-
int err;
8900-
8901-
/*
8902-
* Per section 10.4.3 of RFC 8881, the server would
8903-
* not update the file's metadata with the client's
8904-
* modified size
8905-
*/
8906-
attrs.ia_mtime = attrs.ia_ctime = current_time(inode);
8907-
attrs.ia_valid = ATTR_MTIME | ATTR_CTIME | ATTR_DELEG;
8908-
inode_lock(inode);
8909-
err = notify_change(&nop_mnt_idmap, dentry, &attrs, NULL);
8910-
inode_unlock(inode);
8911-
if (err) {
8912-
nfs4_put_stid(&dp->dl_stid);
8913-
return nfserrno(err);
8914-
}
8915-
ncf->ncf_cur_fsize = ncf->ncf_cb_fsize;
8916-
*size = ncf->ncf_cur_fsize;
8917-
*modified = true;
8918-
}
8919-
nfs4_put_stid(&dp->dl_stid);
8920-
return 0;
8875+
nfsd_stats_wdeleg_getattr_inc(nn);
8876+
refcount_inc(&dp->dl_stid.sc_count);
8877+
ncf = &dp->dl_cb_fattr;
8878+
nfs4_cb_getattr(&dp->dl_cb_fattr);
8879+
spin_unlock(&ctx->flc_lock);
8880+
8881+
wait_on_bit_timeout(&ncf->ncf_cb_flags, CB_GETATTR_BUSY,
8882+
TASK_INTERRUPTIBLE, NFSD_CB_GETATTR_TIMEOUT);
8883+
if (ncf->ncf_cb_status) {
8884+
/* Recall delegation only if client didn't respond */
8885+
status = nfserrno(nfsd_open_break_lease(inode, NFSD_MAY_READ));
8886+
if (status != nfserr_jukebox ||
8887+
!nfsd_wait_for_delegreturn(rqstp, inode))
8888+
goto out_status;
8889+
}
8890+
if (!ncf->ncf_file_modified &&
8891+
(ncf->ncf_initial_cinfo != ncf->ncf_cb_change ||
8892+
ncf->ncf_cur_fsize != ncf->ncf_cb_fsize))
8893+
ncf->ncf_file_modified = true;
8894+
if (ncf->ncf_file_modified) {
8895+
int err;
8896+
8897+
/*
8898+
* Per section 10.4.3 of RFC 8881, the server would
8899+
* not update the file's metadata with the client's
8900+
* modified size
8901+
*/
8902+
attrs.ia_mtime = attrs.ia_ctime = current_time(inode);
8903+
attrs.ia_valid = ATTR_MTIME | ATTR_CTIME | ATTR_DELEG;
8904+
inode_lock(inode);
8905+
err = notify_change(&nop_mnt_idmap, dentry, &attrs, NULL);
8906+
inode_unlock(inode);
8907+
if (err) {
8908+
status = nfserrno(err);
8909+
goto out_status;
89218910
}
8922-
break;
8911+
ncf->ncf_cur_fsize = ncf->ncf_cb_fsize;
8912+
*size = ncf->ncf_cur_fsize;
8913+
*modified = true;
89238914
}
8924-
spin_unlock(&ctx->flc_lock);
8925-
return 0;
8915+
status = 0;
8916+
out_status:
8917+
nfs4_put_stid(&dp->dl_stid);
8918+
return status;
89268919
}

0 commit comments

Comments
 (0)