Merge tag 'nfsd-5.14' of git://linux-nfs.org/~bfields/linux
[sfrench/cifs-2.6.git] / fs / nfsd / nfs4state.c
index b517a8794400ec5fee93ce48af27892c42e04acb..fa67ecd5fe63f7d1502e2c135afe63914054bd03 100644 (file)
@@ -44,6 +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 @@ static void nfsd4_conn_lost(struct svc_xpt_user *u)
        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 @@ static struct nfs4_client *get_nfsdfs_clp(struct inode *inode)
 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;
@@ -2383,6 +2401,8 @@ static int client_info_show(struct seq_file *m, void *v)
                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 @@ static void force_expire_client(struct nfs4_client *clp)
        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 @@ move_to_confirmed(struct nfs4_client *clp)
 
        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 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                        }
                        /* 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;
        }
@@ -3199,16 +3222,19 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                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];
@@ -3232,8 +3258,10 @@ out:
 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 @@ nfsd4_create_session(struct svc_rqst *rqstp,
                        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;
@@ -3447,6 +3476,7 @@ nfsd4_create_session(struct svc_rqst *rqstp,
                                old = NULL;
                                goto out_free_conn;
                        }
+                       trace_nfsd_clid_replaced(&old->cl_clientid);
                }
                move_to_confirmed(unconf);
                conf = unconf;
@@ -3471,6 +3501,8 @@ nfsd4_create_session(struct svc_rqst *rqstp,
        /* 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 @@ nfsd4_destroy_clientid(struct svc_rqst *rqstp,
                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 @@ nfsd4_reclaim_complete(struct svc_rqst *rqstp,
                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 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        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);
@@ -4000,12 +4036,13 @@ out:
        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,
@@ -4034,25 +4071,27 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
         * 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;
@@ -4065,12 +4104,15 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
                                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 @@ nfsd_break_deleg_cb(struct file_lock *fl)
        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 @@ static bool state_expired(struct laundry_time *lt, time64_t last_refresh)
        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)
 {
@@ -5495,10 +5600,8 @@ 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);
@@ -5568,6 +5671,10 @@ nfs4_laundromat(struct nfsd_net *nn)
                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 @@ 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 @@ nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash,
        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 @@ nfsd4_find_reclaim_client(struct xdr_netobj name, struct nfsd_net *nn)
        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 @@ 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