Skip to content

Commit 94415b0

Browse files
J. Bruce Fieldschucklever
authored andcommitted
nfsd4: a client's own opens needn't prevent delegations
We recently fixed lease breaking so that a client's actions won't break its own delegations. But we still have an unnecessary self-conflict when granting delegations: a client's own write opens will prevent us from handing out a read delegation even when no other client has the file open for write. Fix that by turning off the checks for conflicting opens under vfs_setlease, and instead performing those checks in the nfsd code. We don't depend much on locks here: instead we acquire the delegation, then check for conflicts, and drop the delegation again if we find any. The check beforehand is an optimization of sorts, just to avoid acquiring the delegation unnecessarily. There's a race where the first check could cause us to deny the delegation when we could have granted it. But, that's OK, delegation grants are optional (and probably not even a good idea in that case). Signed-off-by: J. Bruce Fields <[email protected]> Signed-off-by: Chuck Lever <[email protected]>
1 parent 0b7cd9d commit 94415b0

File tree

2 files changed

+43
-14
lines changed

2 files changed

+43
-14
lines changed

fs/locks.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1807,6 +1807,9 @@ check_conflicting_open(struct file *filp, const long arg, int flags)
18071807

18081808
if (flags & FL_LAYOUT)
18091809
return 0;
1810+
if (flags & FL_DELEG)
1811+
/* We leave these checks to the caller. */
1812+
return 0;
18101813

18111814
if (arg == F_RDLCK)
18121815
return inode_is_open_for_write(inode) ? -EAGAIN : 0;

fs/nfsd/nfs4state.c

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4922,6 +4922,32 @@ static struct file_lock *nfs4_alloc_init_lease(struct nfs4_delegation *dp,
49224922
return fl;
49234923
}
49244924

4925+
static int nfsd4_check_conflicting_opens(struct nfs4_client *clp,
4926+
struct nfs4_file *fp)
4927+
{
4928+
struct nfs4_clnt_odstate *co;
4929+
struct file *f = fp->fi_deleg_file->nf_file;
4930+
struct inode *ino = locks_inode(f);
4931+
int writes = atomic_read(&ino->i_writecount);
4932+
4933+
if (fp->fi_fds[O_WRONLY])
4934+
writes--;
4935+
if (fp->fi_fds[O_RDWR])
4936+
writes--;
4937+
WARN_ON_ONCE(writes < 0);
4938+
if (writes > 0)
4939+
return -EAGAIN;
4940+
spin_lock(&fp->fi_lock);
4941+
list_for_each_entry(co, &fp->fi_clnt_odstate, co_perfile) {
4942+
if (co->co_client != clp) {
4943+
spin_unlock(&fp->fi_lock);
4944+
return -EAGAIN;
4945+
}
4946+
}
4947+
spin_unlock(&fp->fi_lock);
4948+
return 0;
4949+
}
4950+
49254951
static struct nfs4_delegation *
49264952
nfs4_set_delegation(struct nfs4_client *clp, struct svc_fh *fh,
49274953
struct nfs4_file *fp, struct nfs4_clnt_odstate *odstate)
@@ -4941,9 +4967,12 @@ nfs4_set_delegation(struct nfs4_client *clp, struct svc_fh *fh,
49414967

49424968
nf = find_readable_file(fp);
49434969
if (!nf) {
4944-
/* We should always have a readable file here */
4945-
WARN_ON_ONCE(1);
4946-
return ERR_PTR(-EBADF);
4970+
/*
4971+
* We probably could attempt another open and get a read
4972+
* delegation, but for now, don't bother until the
4973+
* client actually sends us one.
4974+
*/
4975+
return ERR_PTR(-EAGAIN);
49474976
}
49484977
spin_lock(&state_lock);
49494978
spin_lock(&fp->fi_lock);
@@ -4973,11 +5002,19 @@ nfs4_set_delegation(struct nfs4_client *clp, struct svc_fh *fh,
49735002
if (!fl)
49745003
goto out_clnt_odstate;
49755004

5005+
status = nfsd4_check_conflicting_opens(clp, fp);
5006+
if (status) {
5007+
locks_free_lock(fl);
5008+
goto out_clnt_odstate;
5009+
}
49765010
status = vfs_setlease(fp->fi_deleg_file->nf_file, fl->fl_type, &fl, NULL);
49775011
if (fl)
49785012
locks_free_lock(fl);
49795013
if (status)
49805014
goto out_clnt_odstate;
5015+
status = nfsd4_check_conflicting_opens(clp, fp);
5016+
if (status)
5017+
goto out_clnt_odstate;
49815018

49825019
spin_lock(&state_lock);
49835020
spin_lock(&fp->fi_lock);
@@ -5059,17 +5096,6 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open,
50595096
goto out_no_deleg;
50605097
if (!cb_up || !(oo->oo_flags & NFS4_OO_CONFIRMED))
50615098
goto out_no_deleg;
5062-
/*
5063-
* Also, if the file was opened for write or
5064-
* create, there's a good chance the client's
5065-
* about to write to it, resulting in an
5066-
* immediate recall (since we don't support
5067-
* write delegations):
5068-
*/
5069-
if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE)
5070-
goto out_no_deleg;
5071-
if (open->op_create == NFS4_OPEN_CREATE)
5072-
goto out_no_deleg;
50735099
break;
50745100
default:
50755101
goto out_no_deleg;

0 commit comments

Comments
 (0)