Skip to content

Commit aba2072

Browse files
J. Bruce Fieldschucklever
authored andcommitted
nfsd: grant read delegations to clients holding writes
It's OK to grant a read delegation to a client that holds a write, as long as it's the only client holding the write. We originally tried to do this in commit 94415b0 ("nfsd4: a client's own opens needn't prevent delegations"), which had to be reverted in commit 6ee65a7 ("Revert "nfsd4: a client's own opens needn't prevent delegations""). Signed-off-by: J. Bruce Fields <[email protected]> Signed-off-by: Chuck Lever <[email protected]>
1 parent ebd9d2c commit aba2072

File tree

2 files changed

+71
-14
lines changed

2 files changed

+71
-14
lines changed

fs/locks.c

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

18091809
if (flags & FL_LAYOUT)
18101810
return 0;
1811+
if (flags & FL_DELEG)
1812+
/* We leave these checks to the caller */
1813+
return 0;
18111814

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

fs/nfsd/nfs4state.c

Lines changed: 68 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4988,6 +4988,65 @@ static struct file_lock *nfs4_alloc_init_lease(struct nfs4_delegation *dp,
49884988
return fl;
49894989
}
49904990

4991+
static int nfsd4_check_conflicting_opens(struct nfs4_client *clp,
4992+
struct nfs4_file *fp)
4993+
{
4994+
struct nfs4_ol_stateid *st;
4995+
struct file *f = fp->fi_deleg_file->nf_file;
4996+
struct inode *ino = locks_inode(f);
4997+
int writes;
4998+
4999+
writes = atomic_read(&ino->i_writecount);
5000+
if (!writes)
5001+
return 0;
5002+
/*
5003+
* There could be multiple filehandles (hence multiple
5004+
* nfs4_files) referencing this file, but that's not too
5005+
* common; let's just give up in that case rather than
5006+
* trying to go look up all the clients using that other
5007+
* nfs4_file as well:
5008+
*/
5009+
if (fp->fi_aliased)
5010+
return -EAGAIN;
5011+
/*
5012+
* If there's a close in progress, make sure that we see it
5013+
* clear any fi_fds[] entries before we see it decrement
5014+
* i_writecount:
5015+
*/
5016+
smp_mb__after_atomic();
5017+
5018+
if (fp->fi_fds[O_WRONLY])
5019+
writes--;
5020+
if (fp->fi_fds[O_RDWR])
5021+
writes--;
5022+
if (writes > 0)
5023+
return -EAGAIN; /* There may be non-NFSv4 writers */
5024+
/*
5025+
* It's possible there are non-NFSv4 write opens in progress,
5026+
* but if they haven't incremented i_writecount yet then they
5027+
* also haven't called break lease yet; so, they'll break this
5028+
* lease soon enough. So, all that's left to check for is NFSv4
5029+
* opens:
5030+
*/
5031+
spin_lock(&fp->fi_lock);
5032+
list_for_each_entry(st, &fp->fi_stateids, st_perfile) {
5033+
if (st->st_openstp == NULL /* it's an open */ &&
5034+
access_permit_write(st) &&
5035+
st->st_stid.sc_client != clp) {
5036+
spin_unlock(&fp->fi_lock);
5037+
return -EAGAIN;
5038+
}
5039+
}
5040+
spin_unlock(&fp->fi_lock);
5041+
/*
5042+
* There's a small chance that we could be racing with another
5043+
* NFSv4 open. However, any open that hasn't added itself to
5044+
* the fi_stateids list also hasn't called break_lease yet; so,
5045+
* they'll break this lease soon enough.
5046+
*/
5047+
return 0;
5048+
}
5049+
49915050
static struct nfs4_delegation *
49925051
nfs4_set_delegation(struct nfs4_client *clp, struct svc_fh *fh,
49935052
struct nfs4_file *fp, struct nfs4_clnt_odstate *odstate)
@@ -5007,9 +5066,12 @@ nfs4_set_delegation(struct nfs4_client *clp, struct svc_fh *fh,
50075066

50085067
nf = find_readable_file(fp);
50095068
if (!nf) {
5010-
/* We should always have a readable file here */
5011-
WARN_ON_ONCE(1);
5012-
return ERR_PTR(-EBADF);
5069+
/*
5070+
* We probably could attempt another open and get a read
5071+
* delegation, but for now, don't bother until the
5072+
* client actually sends us one.
5073+
*/
5074+
return ERR_PTR(-EAGAIN);
50135075
}
50145076
spin_lock(&state_lock);
50155077
spin_lock(&fp->fi_lock);
@@ -5044,6 +5106,9 @@ nfs4_set_delegation(struct nfs4_client *clp, struct svc_fh *fh,
50445106
locks_free_lock(fl);
50455107
if (status)
50465108
goto out_clnt_odstate;
5109+
status = nfsd4_check_conflicting_opens(clp, fp);
5110+
if (status)
5111+
goto out_unlock;
50475112

50485113
spin_lock(&state_lock);
50495114
spin_lock(&fp->fi_lock);
@@ -5125,17 +5190,6 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open,
51255190
goto out_no_deleg;
51265191
if (!cb_up || !(oo->oo_flags & NFS4_OO_CONFIRMED))
51275192
goto out_no_deleg;
5128-
/*
5129-
* Also, if the file was opened for write or
5130-
* create, there's a good chance the client's
5131-
* about to write to it, resulting in an
5132-
* immediate recall (since we don't support
5133-
* write delegations):
5134-
*/
5135-
if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE)
5136-
goto out_no_deleg;
5137-
if (open->op_create == NFS4_OPEN_CREATE)
5138-
goto out_no_deleg;
51395193
break;
51405194
default:
51415195
goto out_no_deleg;

0 commit comments

Comments
 (0)