Merge tag 'nfsd-5.14' of git://linux-nfs.org/~bfields/linux
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 7 Jul 2021 19:50:08 +0000 (12:50 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 7 Jul 2021 19:50:08 +0000 (12:50 -0700)
Pull nfsd updates from Bruce Fields:

 - add tracepoints for callbacks and for client creation and destruction

 - cache the mounts used for server-to-server copies

 - expose callback information in /proc/fs/nfsd/clients/*/info

 - don't hold locks unnecessarily while waiting for commits

 - update NLM to use xdr_stream, as we have for NFSv2/v3/v4

* tag 'nfsd-5.14' of git://linux-nfs.org/~bfields/linux: (69 commits)
  nfsd: fix NULL dereference in nfs3svc_encode_getaclres
  NFSD: Prevent a possible oops in the nfs_dirent() tracepoint
  nfsd: remove redundant assignment to pointer 'this'
  nfsd: Reduce contention for the nfsd_file nf_rwsem
  lockd: Update the NLMv4 SHARE results encoder to use struct xdr_stream
  lockd: Update the NLMv4 nlm_res results encoder to use struct xdr_stream
  lockd: Update the NLMv4 TEST results encoder to use struct xdr_stream
  lockd: Update the NLMv4 void results encoder to use struct xdr_stream
  lockd: Update the NLMv4 FREE_ALL arguments decoder to use struct xdr_stream
  lockd: Update the NLMv4 SHARE arguments decoder to use struct xdr_stream
  lockd: Update the NLMv4 SM_NOTIFY arguments decoder to use struct xdr_stream
  lockd: Update the NLMv4 nlm_res arguments decoder to use struct xdr_stream
  lockd: Update the NLMv4 UNLOCK arguments decoder to use struct xdr_stream
  lockd: Update the NLMv4 CANCEL arguments decoder to use struct xdr_stream
  lockd: Update the NLMv4 LOCK arguments decoder to use struct xdr_stream
  lockd: Update the NLMv4 TEST arguments decoder to use struct xdr_stream
  lockd: Update the NLMv4 void arguments decoder to use struct xdr_stream
  lockd: Update the NLMv1 SHARE results encoder to use struct xdr_stream
  lockd: Update the NLMv1 nlm_res results encoder to use struct xdr_stream
  lockd: Update the NLMv1 TEST results encoder to use struct xdr_stream
  ...

1  2 
fs/nfsd/nfs4state.c

diff --combined fs/nfsd/nfs4state.c
@@@ -44,6 -44,7 +44,7 @@@
  #include <linux/jhash.h>
  #include <linux/string_helpers.h>
  #include <linux/fsnotify.h>
+ #include <linux/nfs_ssc.h>
  #include "xdr4.h"
  #include "xdr4cb.h"
  #include "vfs.h"
@@@ -1745,6 -1746,8 +1746,8 @@@ static void nfsd4_conn_lost(struct svc_
        struct nfsd4_conn *c = container_of(u, struct nfsd4_conn, cn_xpt_user);
        struct nfs4_client *clp = c->cn_session->se_client;
  
+       trace_nfsd_cb_lost(clp);
        spin_lock(&clp->cl_lock);
        if (!list_empty(&c->cn_persession)) {
                list_del(&c->cn_persession);
@@@ -2351,10 -2354,25 +2354,25 @@@ static struct nfs4_client *get_nfsdfs_c
  static void seq_quote_mem(struct seq_file *m, char *data, int len)
  {
        seq_printf(m, "\"");
 -      seq_escape_mem_ascii(m, data, len);
 +      seq_escape_mem(m, data, len, ESCAPE_HEX | ESCAPE_NAP | ESCAPE_APPEND, "\"\\");
        seq_printf(m, "\"");
  }
  
+ static const char *cb_state2str(int state)
+ {
+       switch (state) {
+       case NFSD4_CB_UP:
+               return "UP";
+       case NFSD4_CB_UNKNOWN:
+               return "UNKNOWN";
+       case NFSD4_CB_DOWN:
+               return "DOWN";
+       case NFSD4_CB_FAULT:
+               return "FAULT";
+       }
+       return "UNDEFINED";
+ }
  static int client_info_show(struct seq_file *m, void *v)
  {
        struct inode *inode = m->private;
                seq_printf(m, "\nImplementation time: [%lld, %ld]\n",
                        clp->cl_nii_time.tv_sec, clp->cl_nii_time.tv_nsec);
        }
+       seq_printf(m, "callback state: %s\n", cb_state2str(clp->cl_cb_state));
+       seq_printf(m, "callback address: %pISpc\n", &clp->cl_cb_conn.cb_addr);
        drop_client(clp);
  
        return 0;
@@@ -2665,6 -2685,8 +2685,8 @@@ static void force_expire_client(struct 
        struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
        bool already_expired;
  
+       trace_nfsd_clid_admin_expired(&clp->cl_clientid);
        spin_lock(&clp->cl_lock);
        clp->cl_time = 0;
        spin_unlock(&clp->cl_lock);
@@@ -2816,14 -2838,11 +2838,11 @@@ move_to_confirmed(struct nfs4_client *c
  
        lockdep_assert_held(&nn->client_lock);
  
-       dprintk("NFSD: move_to_confirm nfs4_client %p\n", clp);
        list_move(&clp->cl_idhash, &nn->conf_id_hashtbl[idhashval]);
        rb_erase(&clp->cl_namenode, &nn->unconf_name_tree);
        add_clp_to_name_tree(clp, &nn->conf_name_tree);
-       if (!test_and_set_bit(NFSD4_CLIENT_CONFIRMED, &clp->cl_flags) &&
-           clp->cl_nfsd_dentry &&
-           clp->cl_nfsd_info_dentry)
-               fsnotify_dentry(clp->cl_nfsd_info_dentry, FS_MODIFY);
+       set_bit(NFSD4_CLIENT_CONFIRMED, &clp->cl_flags);
+       trace_nfsd_clid_confirmed(&clp->cl_clientid);
        renew_client_locked(clp);
  }
  
@@@ -3176,20 -3195,24 +3195,24 @@@ nfsd4_exchange_id(struct svc_rqst *rqst
                        }
                        /* case 6 */
                        exid->flags |= EXCHGID4_FLAG_CONFIRMED_R;
+                       trace_nfsd_clid_confirmed_r(conf);
                        goto out_copy;
                }
                if (!creds_match) { /* case 3 */
                        if (client_has_state(conf)) {
                                status = nfserr_clid_inuse;
+                               trace_nfsd_clid_cred_mismatch(conf, rqstp);
                                goto out;
                        }
                        goto out_new;
                }
                if (verfs_match) { /* case 2 */
                        conf->cl_exchange_flags |= EXCHGID4_FLAG_CONFIRMED_R;
+                       trace_nfsd_clid_confirmed_r(conf);
                        goto out_copy;
                }
                /* case 5, client reboot */
+               trace_nfsd_clid_verf_mismatch(conf, rqstp, &verf);
                conf = NULL;
                goto out_new;
        }
                goto out;
        }
  
-       unconf  = find_unconfirmed_client_by_name(&exid->clname, nn);
+       unconf = find_unconfirmed_client_by_name(&exid->clname, nn);
        if (unconf) /* case 4, possible retry or client restart */
                unhash_client_locked(unconf);
  
-       /* case 1 (normal case) */
+       /* case 1, new owner ID */
+       trace_nfsd_clid_fresh(new);
  out_new:
        if (conf) {
                status = mark_client_expired_locked(conf);
                if (status)
                        goto out;
+               trace_nfsd_clid_replaced(&conf->cl_clientid);
        }
        new->cl_minorversion = cstate->minorversion;
        new->cl_spo_must_allow.u.words[0] = exid->spo_must_allow[0];
  out_nolock:
        if (new)
                expire_client(new);
-       if (unconf)
+       if (unconf) {
+               trace_nfsd_clid_expire_unconf(&unconf->cl_clientid);
                expire_client(unconf);
+       }
        return status;
  }
  
@@@ -3425,9 -3453,10 +3453,10 @@@ nfsd4_create_session(struct svc_rqst *r
                        goto out_free_conn;
                }
        } else if (unconf) {
+               status = nfserr_clid_inuse;
                if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred) ||
                    !rpc_cmp_addr(sa, (struct sockaddr *) &unconf->cl_addr)) {
-                       status = nfserr_clid_inuse;
+                       trace_nfsd_clid_cred_mismatch(unconf, rqstp);
                        goto out_free_conn;
                }
                status = nfserr_wrong_cred;
                                old = NULL;
                                goto out_free_conn;
                        }
+                       trace_nfsd_clid_replaced(&old->cl_clientid);
                }
                move_to_confirmed(unconf);
                conf = unconf;
        /* cache solo and embedded create sessions under the client_lock */
        nfsd4_cache_create_session(cr_ses, cs_slot, status);
        spin_unlock(&nn->client_lock);
+       if (conf == unconf)
+               fsnotify_dentry(conf->cl_nfsd_info_dentry, FS_MODIFY);
        /* init connection and backchannel */
        nfsd4_init_conn(rqstp, conn, new);
        nfsd4_put_session(new);
@@@ -3904,6 -3936,7 +3936,7 @@@ nfsd4_destroy_clientid(struct svc_rqst 
                status = nfserr_wrong_cred;
                goto out;
        }
+       trace_nfsd_clid_destroyed(&clp->cl_clientid);
        unhash_client_locked(clp);
  out:
        spin_unlock(&nn->client_lock);
@@@ -3946,6 -3979,7 +3979,7 @@@ nfsd4_reclaim_complete(struct svc_rqst 
                goto out;
  
        status = nfs_ok;
+       trace_nfsd_clid_reclaim_complete(&clp->cl_clientid);
        nfsd4_client_record_create(clp);
        inc_reclaim_complete(clp);
  out:
@@@ -3967,27 -4001,29 +4001,29 @@@ nfsd4_setclientid(struct svc_rqst *rqst
        new = create_client(clname, rqstp, &clverifier);
        if (new == NULL)
                return nfserr_jukebox;
-       /* Cases below refer to rfc 3530 section 14.2.33: */
        spin_lock(&nn->client_lock);
        conf = find_confirmed_client_by_name(&clname, nn);
        if (conf && client_has_state(conf)) {
-               /* case 0: */
                status = nfserr_clid_inuse;
                if (clp_used_exchangeid(conf))
                        goto out;
                if (!same_creds(&conf->cl_cred, &rqstp->rq_cred)) {
-                       trace_nfsd_clid_inuse_err(conf);
+                       trace_nfsd_clid_cred_mismatch(conf, rqstp);
                        goto out;
                }
        }
        unconf = find_unconfirmed_client_by_name(&clname, nn);
        if (unconf)
                unhash_client_locked(unconf);
-       /* We need to handle only case 1: probable callback update */
-       if (conf && same_verf(&conf->cl_verifier, &clverifier)) {
-               copy_clid(new, conf);
-               gen_confirm(new, nn);
-       }
+       if (conf) {
+               if (same_verf(&conf->cl_verifier, &clverifier)) {
+                       copy_clid(new, conf);
+                       gen_confirm(new, nn);
+               } else
+                       trace_nfsd_clid_verf_mismatch(conf, rqstp,
+                                                     &clverifier);
+       } else
+               trace_nfsd_clid_fresh(new);
        new->cl_minorversion = 0;
        gen_callback(new, setclid, rqstp);
        add_to_unconfirmed(new);
        spin_unlock(&nn->client_lock);
        if (new)
                free_client(new);
-       if (unconf)
+       if (unconf) {
+               trace_nfsd_clid_expire_unconf(&unconf->cl_clientid);
                expire_client(unconf);
+       }
        return status;
  }
  
  __be32
  nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
                        struct nfsd4_compound_state *cstate,
         * Nevertheless, RFC 7530 recommends INUSE for this case:
         */
        status = nfserr_clid_inuse;
-       if (unconf && !same_creds(&unconf->cl_cred, &rqstp->rq_cred))
+       if (unconf && !same_creds(&unconf->cl_cred, &rqstp->rq_cred)) {
+               trace_nfsd_clid_cred_mismatch(unconf, rqstp);
                goto out;
-       if (conf && !same_creds(&conf->cl_cred, &rqstp->rq_cred))
+       }
+       if (conf && !same_creds(&conf->cl_cred, &rqstp->rq_cred)) {
+               trace_nfsd_clid_cred_mismatch(conf, rqstp);
                goto out;
-       /* cases below refer to rfc 3530 section 14.2.34: */
+       }
        if (!unconf || !same_verf(&confirm, &unconf->cl_confirm)) {
                if (conf && same_verf(&confirm, &conf->cl_confirm)) {
-                       /* case 2: probable retransmit */
                        status = nfs_ok;
-               } else /* case 4: client hasn't noticed we rebooted yet? */
+               } else
                        status = nfserr_stale_clientid;
                goto out;
        }
        status = nfs_ok;
-       if (conf) { /* case 1: callback update */
+       if (conf) {
                old = unconf;
                unhash_client_locked(old);
                nfsd4_change_callback(conf, &unconf->cl_cb_conn);
-       } else { /* case 3: normal case; new or rebooted client */
+       } else {
                old = find_confirmed_client_by_name(&unconf->cl_name, nn);
                if (old) {
                        status = nfserr_clid_inuse;
                                old = NULL;
                                goto out;
                        }
+                       trace_nfsd_clid_replaced(&old->cl_clientid);
                }
                move_to_confirmed(unconf);
                conf = unconf;
        }
        get_client_locked(conf);
        spin_unlock(&nn->client_lock);
+       if (conf == unconf)
+               fsnotify_dentry(conf->cl_nfsd_info_dentry, FS_MODIFY);
        nfsd4_probe_callback(conf);
        spin_lock(&nn->client_lock);
        put_client_renew_locked(conf);
@@@ -4618,7 -4660,7 +4660,7 @@@ nfsd_break_deleg_cb(struct file_lock *f
        struct nfs4_delegation *dp = (struct nfs4_delegation *)fl->fl_owner;
        struct nfs4_file *fp = dp->dl_stid.sc_file;
  
-       trace_nfsd_deleg_break(&dp->dl_stid.sc_stateid);
+       trace_nfsd_cb_recall(&dp->dl_stid);
  
        /*
         * We don't want the locks code to timeout the lease for us;
@@@ -5457,6 -5499,69 +5499,69 @@@ static bool state_expired(struct laundr
        return false;
  }
  
+ #ifdef CONFIG_NFSD_V4_2_INTER_SSC
+ void nfsd4_ssc_init_umount_work(struct nfsd_net *nn)
+ {
+       spin_lock_init(&nn->nfsd_ssc_lock);
+       INIT_LIST_HEAD(&nn->nfsd_ssc_mount_list);
+       init_waitqueue_head(&nn->nfsd_ssc_waitq);
+ }
+ EXPORT_SYMBOL_GPL(nfsd4_ssc_init_umount_work);
+ /*
+  * This is called when nfsd is being shutdown, after all inter_ssc
+  * cleanup were done, to destroy the ssc delayed unmount list.
+  */
+ static void nfsd4_ssc_shutdown_umount(struct nfsd_net *nn)
+ {
+       struct nfsd4_ssc_umount_item *ni = NULL;
+       struct nfsd4_ssc_umount_item *tmp;
+       spin_lock(&nn->nfsd_ssc_lock);
+       list_for_each_entry_safe(ni, tmp, &nn->nfsd_ssc_mount_list, nsui_list) {
+               list_del(&ni->nsui_list);
+               spin_unlock(&nn->nfsd_ssc_lock);
+               mntput(ni->nsui_vfsmount);
+               kfree(ni);
+               spin_lock(&nn->nfsd_ssc_lock);
+       }
+       spin_unlock(&nn->nfsd_ssc_lock);
+ }
+ static void nfsd4_ssc_expire_umount(struct nfsd_net *nn)
+ {
+       bool do_wakeup = false;
+       struct nfsd4_ssc_umount_item *ni = 0;
+       struct nfsd4_ssc_umount_item *tmp;
+       spin_lock(&nn->nfsd_ssc_lock);
+       list_for_each_entry_safe(ni, tmp, &nn->nfsd_ssc_mount_list, nsui_list) {
+               if (time_after(jiffies, ni->nsui_expire)) {
+                       if (refcount_read(&ni->nsui_refcnt) > 1)
+                               continue;
+                       /* mark being unmount */
+                       ni->nsui_busy = true;
+                       spin_unlock(&nn->nfsd_ssc_lock);
+                       mntput(ni->nsui_vfsmount);
+                       spin_lock(&nn->nfsd_ssc_lock);
+                       /* waiters need to start from begin of list */
+                       list_del(&ni->nsui_list);
+                       kfree(ni);
+                       /* wakeup ssc_connect waiters */
+                       do_wakeup = true;
+                       continue;
+               }
+               break;
+       }
+       if (do_wakeup)
+               wake_up_all(&nn->nfsd_ssc_waitq);
+       spin_unlock(&nn->nfsd_ssc_lock);
+ }
+ #endif
  static time64_t
  nfs4_laundromat(struct nfsd_net *nn)
  {
                clp = list_entry(pos, struct nfs4_client, cl_lru);
                if (!state_expired(&lt, clp->cl_time))
                        break;
-               if (mark_client_expired_locked(clp)) {
-                       trace_nfsd_clid_expired(&clp->cl_clientid);
+               if (mark_client_expired_locked(clp))
                        continue;
-               }
                list_add(&clp->cl_lru, &reaplist);
        }
        spin_unlock(&nn->client_lock);
                list_del_init(&nbl->nbl_lru);
                free_blocked_lock(nbl);
        }
+ #ifdef CONFIG_NFSD_V4_2_INTER_SSC
+       /* service the server-to-server copy delayed unmount list */
+       nfsd4_ssc_expire_umount(nn);
+ #endif
  out:
        return max_t(time64_t, lt.new_timeo, NFSD_LAUNDROMAT_MINTIMEOUT);
  }
@@@ -6430,8 -6537,10 +6537,10 @@@ nfsd4_lm_notify(struct file_lock *fl
        }
        spin_unlock(&nn->blocked_locks_lock);
  
-       if (queue)
+       if (queue) {
+               trace_nfsd_cb_notify_lock(lo, nbl);
                nfsd4_run_cb(&nbl->nbl_cb);
+       }
  }
  
  static const struct lock_manager_operations nfsd_posix_mng_ops  = {
@@@ -7229,7 -7338,6 +7338,6 @@@ nfs4_client_to_reclaim(struct xdr_netob
        unsigned int strhashval;
        struct nfs4_client_reclaim *crp;
  
-       trace_nfsd_clid_reclaim(nn, name.len, name.data);
        crp = alloc_reclaim();
        if (crp) {
                strhashval = clientstr_hashval(name);
@@@ -7279,8 -7387,6 +7387,6 @@@ nfsd4_find_reclaim_client(struct xdr_ne
        unsigned int strhashval;
        struct nfs4_client_reclaim *crp = NULL;
  
-       trace_nfsd_clid_find(nn, name.len, name.data);
        strhashval = clientstr_hashval(name);
        list_for_each_entry(crp, &nn->reclaim_str_hashtbl[strhashval], cr_strhash) {
                if (compare_blob(&crp->cr_name, &name) == 0) {
@@@ -7486,6 -7592,9 +7592,9 @@@ nfs4_state_shutdown_net(struct net *net
  
        nfsd4_client_tracking_exit(net);
        nfs4_state_destroy_net(net);
+ #ifdef CONFIG_NFSD_V4_2_INTER_SSC
+       nfsd4_ssc_shutdown_umount(nn);
+ #endif
  }
  
  void