Commit a078a7dc authored by NeilBrown's avatar NeilBrown Committed by Chuck Lever

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: c5967721 ("NFSD: handle GETATTR conflict with write delegation")
Signed-off-by: default avatarNeilBrown <neilb@suse.de>
Signed-off-by: default avatarJeff Layton <jlayton@kernel.org>
Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
parent 5559c157
...@@ -8834,6 +8834,7 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry, ...@@ -8834,6 +8834,7 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry,
__be32 status; __be32 status;
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
struct file_lock_context *ctx; struct file_lock_context *ctx;
struct nfs4_delegation *dp = NULL;
struct file_lease *fl; struct file_lease *fl;
struct iattr attrs; struct iattr attrs;
struct nfs4_cb_fattr *ncf; struct nfs4_cb_fattr *ncf;
...@@ -8843,53 +8844,48 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry, ...@@ -8843,53 +8844,48 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry,
ctx = locks_inode_context(inode); ctx = locks_inode_context(inode);
if (!ctx) if (!ctx)
return 0; return 0;
#define NON_NFSD_LEASE ((void *)1)
spin_lock(&ctx->flc_lock); spin_lock(&ctx->flc_lock);
for_each_file_lock(fl, &ctx->flc_lease) { for_each_file_lock(fl, &ctx->flc_lease) {
unsigned char type = fl->c.flc_type;
if (fl->c.flc_flags == FL_LAYOUT) if (fl->c.flc_flags == FL_LAYOUT)
continue; continue;
if (fl->fl_lmops != &nfsd_lease_mng_ops) { if (fl->c.flc_type == F_WRLCK) {
/* if (fl->fl_lmops == &nfsd_lease_mng_ops)
* non-nfs lease, if it's a lease with F_RDLCK then dp = fl->c.flc_owner;
* we are done; there isn't any write delegation else
* on this inode dp = NON_NFSD_LEASE;
*/ }
if (type == F_RDLCK)
break; break;
}
nfsd_stats_wdeleg_getattr_inc(nn); if (dp == NULL || dp == NON_NFSD_LEASE ||
dp->dl_recall.cb_clp == *(rqstp->rq_lease_breaker)) {
spin_unlock(&ctx->flc_lock); spin_unlock(&ctx->flc_lock);
if (dp == NON_NFSD_LEASE) {
status = nfserrno(nfsd_open_break_lease(inode, NFSD_MAY_READ)); status = nfserrno(nfsd_open_break_lease(inode,
NFSD_MAY_READ));
if (status != nfserr_jukebox || if (status != nfserr_jukebox ||
!nfsd_wait_for_delegreturn(rqstp, inode)) !nfsd_wait_for_delegreturn(rqstp, inode))
return status; return status;
return 0;
} }
if (type == F_WRLCK) {
struct nfs4_delegation *dp = fl->c.flc_owner;
if (dp->dl_recall.cb_clp == *(rqstp->rq_lease_breaker)) {
spin_unlock(&ctx->flc_lock);
return 0; return 0;
} }
nfsd_stats_wdeleg_getattr_inc(nn); nfsd_stats_wdeleg_getattr_inc(nn);
dp = fl->c.flc_owner;
refcount_inc(&dp->dl_stid.sc_count); refcount_inc(&dp->dl_stid.sc_count);
ncf = &dp->dl_cb_fattr; ncf = &dp->dl_cb_fattr;
nfs4_cb_getattr(&dp->dl_cb_fattr); nfs4_cb_getattr(&dp->dl_cb_fattr);
spin_unlock(&ctx->flc_lock); spin_unlock(&ctx->flc_lock);
wait_on_bit_timeout(&ncf->ncf_cb_flags, CB_GETATTR_BUSY, wait_on_bit_timeout(&ncf->ncf_cb_flags, CB_GETATTR_BUSY,
TASK_INTERRUPTIBLE, NFSD_CB_GETATTR_TIMEOUT); TASK_INTERRUPTIBLE, NFSD_CB_GETATTR_TIMEOUT);
if (ncf->ncf_cb_status) { if (ncf->ncf_cb_status) {
/* Recall delegation only if client didn't respond */ /* Recall delegation only if client didn't respond */
status = nfserrno(nfsd_open_break_lease(inode, NFSD_MAY_READ)); status = nfserrno(nfsd_open_break_lease(inode, NFSD_MAY_READ));
if (status != nfserr_jukebox || if (status != nfserr_jukebox ||
!nfsd_wait_for_delegreturn(rqstp, inode)) { !nfsd_wait_for_delegreturn(rqstp, inode))
nfs4_put_stid(&dp->dl_stid); goto out_status;
return status;
}
} }
if (!ncf->ncf_file_modified && if (!ncf->ncf_file_modified &&
(ncf->ncf_initial_cinfo != ncf->ncf_cb_change || (ncf->ncf_initial_cinfo != ncf->ncf_cb_change ||
...@@ -8909,18 +8905,15 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry, ...@@ -8909,18 +8905,15 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry,
err = notify_change(&nop_mnt_idmap, dentry, &attrs, NULL); err = notify_change(&nop_mnt_idmap, dentry, &attrs, NULL);
inode_unlock(inode); inode_unlock(inode);
if (err) { if (err) {
nfs4_put_stid(&dp->dl_stid); status = nfserrno(err);
return nfserrno(err); goto out_status;
} }
ncf->ncf_cur_fsize = ncf->ncf_cb_fsize; ncf->ncf_cur_fsize = ncf->ncf_cb_fsize;
*size = ncf->ncf_cur_fsize; *size = ncf->ncf_cur_fsize;
*modified = true; *modified = true;
} }
status = 0;
out_status:
nfs4_put_stid(&dp->dl_stid); nfs4_put_stid(&dp->dl_stid);
return 0; return status;
}
break;
}
spin_unlock(&ctx->flc_lock);
return 0;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment