@@ -4988,6 +4988,65 @@ static struct file_lock *nfs4_alloc_init_lease(struct nfs4_delegation *dp,
4988
4988
return fl ;
4989
4989
}
4990
4990
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
+
4991
5050
static struct nfs4_delegation *
4992
5051
nfs4_set_delegation (struct nfs4_client * clp , struct svc_fh * fh ,
4993
5052
struct nfs4_file * fp , struct nfs4_clnt_odstate * odstate )
@@ -5007,9 +5066,12 @@ nfs4_set_delegation(struct nfs4_client *clp, struct svc_fh *fh,
5007
5066
5008
5067
nf = find_readable_file (fp );
5009
5068
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 );
5013
5075
}
5014
5076
spin_lock (& state_lock );
5015
5077
spin_lock (& fp -> fi_lock );
@@ -5044,6 +5106,9 @@ nfs4_set_delegation(struct nfs4_client *clp, struct svc_fh *fh,
5044
5106
locks_free_lock (fl );
5045
5107
if (status )
5046
5108
goto out_clnt_odstate ;
5109
+ status = nfsd4_check_conflicting_opens (clp , fp );
5110
+ if (status )
5111
+ goto out_unlock ;
5047
5112
5048
5113
spin_lock (& state_lock );
5049
5114
spin_lock (& fp -> fi_lock );
@@ -5125,17 +5190,6 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open,
5125
5190
goto out_no_deleg ;
5126
5191
if (!cb_up || !(oo -> oo_flags & NFS4_OO_CONFIRMED ))
5127
5192
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 ;
5139
5193
break ;
5140
5194
default :
5141
5195
goto out_no_deleg ;
0 commit comments