OSDN Git Service

NFSD: add support for share reservation conflict to courteous server
authorDai Ngo <dai.ngo@oracle.com>
Mon, 2 May 2022 21:19:22 +0000 (14:19 -0700)
committerChuck Lever <chuck.lever@oracle.com>
Thu, 19 May 2022 16:25:39 +0000 (12:25 -0400)
This patch allows expired client with open state to be in COURTESY
state. Share/access conflict with COURTESY client is resolved by
setting COURTESY client to EXPIRABLE state, schedule laundromat
to run and returning nfserr_jukebox to the request client.

Reviewed-by: J. Bruce Fields <bfields@fieldses.org>
Signed-off-by: Dai Ngo <dai.ngo@oracle.com>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
fs/nfsd/nfs4state.c

index 24b51e8..fe79496 100644 (file)
@@ -694,6 +694,57 @@ static unsigned int file_hashval(struct svc_fh *fh)
 
 static struct hlist_head file_hashtbl[FILE_HASH_SIZE];
 
+/*
+ * Check if courtesy clients have conflicting access and resolve it if possible
+ *
+ * access:  is op_share_access if share_access is true.
+ *         Check if access mode, op_share_access, would conflict with
+ *         the current deny mode of the file 'fp'.
+ * access:  is op_share_deny if share_access is false.
+ *         Check if the deny mode, op_share_deny, would conflict with
+ *         current access of the file 'fp'.
+ * stp:     skip checking this entry.
+ * new_stp: normal open, not open upgrade.
+ *
+ * Function returns:
+ *     false - access/deny mode conflict with normal client.
+ *     true  - no conflict or conflict with courtesy client(s) is resolved.
+ */
+static bool
+nfs4_resolve_deny_conflicts_locked(struct nfs4_file *fp, bool new_stp,
+               struct nfs4_ol_stateid *stp, u32 access, bool share_access)
+{
+       struct nfs4_ol_stateid *st;
+       bool resolvable = true;
+       unsigned char bmap;
+       struct nfsd_net *nn;
+       struct nfs4_client *clp;
+
+       lockdep_assert_held(&fp->fi_lock);
+       list_for_each_entry(st, &fp->fi_stateids, st_perfile) {
+               /* ignore lock stateid */
+               if (st->st_openstp)
+                       continue;
+               if (st == stp && new_stp)
+                       continue;
+               /* check file access against deny mode or vice versa */
+               bmap = share_access ? st->st_deny_bmap : st->st_access_bmap;
+               if (!(access & bmap_to_share_mode(bmap)))
+                       continue;
+               clp = st->st_stid.sc_client;
+               if (try_to_expire_client(clp))
+                       continue;
+               resolvable = false;
+               break;
+       }
+       if (resolvable) {
+               clp = stp->st_stid.sc_client;
+               nn = net_generic(clp->net, nfsd_net_id);
+               mod_delayed_work(laundry_wq, &nn->laundromat_work, 0);
+       }
+       return resolvable;
+}
+
 static void
 __nfs4_file_get_access(struct nfs4_file *fp, u32 access)
 {
@@ -4968,7 +5019,7 @@ nfsd4_truncate(struct svc_rqst *rqstp, struct svc_fh *fh,
 
 static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp,
                struct svc_fh *cur_fh, struct nfs4_ol_stateid *stp,
-               struct nfsd4_open *open)
+               struct nfsd4_open *open, bool new_stp)
 {
        struct nfsd_file *nf = NULL;
        __be32 status;
@@ -4984,6 +5035,13 @@ static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp,
         */
        status = nfs4_file_check_deny(fp, open->op_share_deny);
        if (status != nfs_ok) {
+               if (status != nfserr_share_denied) {
+                       spin_unlock(&fp->fi_lock);
+                       goto out;
+               }
+               if (nfs4_resolve_deny_conflicts_locked(fp, new_stp,
+                               stp, open->op_share_deny, false))
+                       status = nfserr_jukebox;
                spin_unlock(&fp->fi_lock);
                goto out;
        }
@@ -4991,6 +5049,13 @@ static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp,
        /* set access to the file */
        status = nfs4_file_get_access(fp, open->op_share_access);
        if (status != nfs_ok) {
+               if (status != nfserr_share_denied) {
+                       spin_unlock(&fp->fi_lock);
+                       goto out;
+               }
+               if (nfs4_resolve_deny_conflicts_locked(fp, new_stp,
+                               stp, open->op_share_access, true))
+                       status = nfserr_jukebox;
                spin_unlock(&fp->fi_lock);
                goto out;
        }
@@ -5037,21 +5102,29 @@ out_put_access:
 }
 
 static __be32
-nfs4_upgrade_open(struct svc_rqst *rqstp, struct nfs4_file *fp, struct svc_fh *cur_fh, struct nfs4_ol_stateid *stp, struct nfsd4_open *open)
+nfs4_upgrade_open(struct svc_rqst *rqstp, struct nfs4_file *fp,
+               struct svc_fh *cur_fh, struct nfs4_ol_stateid *stp,
+               struct nfsd4_open *open)
 {
        __be32 status;
        unsigned char old_deny_bmap = stp->st_deny_bmap;
 
        if (!test_access(open->op_share_access, stp))
-               return nfs4_get_vfs_file(rqstp, fp, cur_fh, stp, open);
+               return nfs4_get_vfs_file(rqstp, fp, cur_fh, stp, open, false);
 
        /* test and set deny mode */
        spin_lock(&fp->fi_lock);
        status = nfs4_file_check_deny(fp, open->op_share_deny);
        if (status == nfs_ok) {
-               set_deny(open->op_share_deny, stp);
-               fp->fi_share_deny |=
+               if (status != nfserr_share_denied) {
+                       set_deny(open->op_share_deny, stp);
+                       fp->fi_share_deny |=
                                (open->op_share_deny & NFS4_SHARE_DENY_BOTH);
+               } else {
+                       if (nfs4_resolve_deny_conflicts_locked(fp, false,
+                                       stp, open->op_share_deny, false))
+                               status = nfserr_jukebox;
+               }
        }
        spin_unlock(&fp->fi_lock);
 
@@ -5392,7 +5465,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
                        goto out;
                }
        } else {
-               status = nfs4_get_vfs_file(rqstp, fp, current_fh, stp, open);
+               status = nfs4_get_vfs_file(rqstp, fp, current_fh, stp, open, true);
                if (status) {
                        stp->st_stid.sc_type = NFS4_CLOSED_STID;
                        release_open_stateid(stp);
@@ -5626,6 +5699,26 @@ static void nfsd4_ssc_expire_umount(struct nfsd_net *nn)
 }
 #endif
 
+static bool
+nfs4_has_any_locks(struct nfs4_client *clp)
+{
+       int i;
+       struct nfs4_stateowner *so;
+
+       spin_lock(&clp->cl_lock);
+       for (i = 0; i < OWNER_HASH_SIZE; i++) {
+               list_for_each_entry(so, &clp->cl_ownerstr_hashtbl[i],
+                               so_strhash) {
+                       if (so->so_is_open_owner)
+                               continue;
+                       spin_unlock(&clp->cl_lock);
+                       return true;
+               }
+       }
+       spin_unlock(&clp->cl_lock);
+       return false;
+}
+
 /*
  * place holder for now, no check for lock blockers yet
  */
@@ -5633,8 +5726,8 @@ static bool
 nfs4_anylock_blockers(struct nfs4_client *clp)
 {
        if (atomic_read(&clp->cl_delegs_in_recall) ||
-                       client_has_openowners(clp)  ||
-                       !list_empty(&clp->async_copies))
+                       !list_empty(&clp->async_copies) ||
+                       nfs4_has_any_locks(clp))
                return true;
        return false;
 }