All of lore.kernel.org
 help / color / mirror / Atom feed
From: Chuck Lever <chuck.lever@oracle.com>
To: Trond.Myklebust@netapp.com
Cc: linux-nfs@vger.kernel.org
Subject: [PATCH 18/20] NFS: Detect NFSv4 server trunking when mounting
Date: Mon, 23 Apr 2012 16:55:53 -0400	[thread overview]
Message-ID: <20120423205552.11446.58714.stgit@degas.1015granger.net> (raw)
In-Reply-To: <20120423205312.11446.67081.stgit@degas.1015granger.net>

Currently the Linux NFS client waits to perform a SETCLIENTID until
just before an application wants to open a file.  Quite a bit of
activity can occur before any state is needed.

If the client cares about server trunking, however, no NFSv4
operations can proceed until the client determines who it is talking
to.  Thus server IP trunking detection must be done when the client
first encounters an unfamiliar server IP address.

The nfs_get_client() function walks the nfs_client_list and matches on
server IP address.  The outcome of that walk tells us immediately if
we have an unfamiliar server IP address.  It invokes an init_client()
method in this case.

Thus, nfs4_init_client() can establish a fresh client ID, and perform
trunking detection with it.  The exact process for detecting trunking
is different for NFSv4.0 and NFSv4.1, so a minorversion-specific
init_client callout is introduced.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 fs/nfs/client.c    |  223 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/nfs/internal.h  |    6 +
 fs/nfs/nfs4_fs.h   |    7 ++
 fs/nfs/nfs4proc.c  |    2 
 fs/nfs/nfs4state.c |  131 ++++++++++++++++++++++++++++++-
 5 files changed, 367 insertions(+), 2 deletions(-)

diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index 920abbc..7330673 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -566,7 +566,8 @@ nfs_get_client(const struct nfs_client_initdata *cl_init,
 			return nfs_found_client(cl_init, clp);
 		}
 		if (new) {
-			list_add(&new->cl_share_link, &nn->nfs_client_list);
+			list_add_tail(&new->cl_share_link,
+					&nn->nfs_client_list);
 			spin_unlock(&nn->nfs_client_lock);
 			new->cl_flags = cl_init->init_flags;
 			return cl_init->rpc_ops->init_client(new,
@@ -584,6 +585,210 @@ nfs_get_client(const struct nfs_client_initdata *cl_init,
 	return new;
 }
 
+#ifdef CONFIG_NFS_V4
+/*
+ * Returns true if the client IDs match
+ */
+static bool
+nfs4_match_clientids(struct nfs_client *a, struct nfs_client *b)
+{
+	if (a->cl_clientid != b->cl_clientid) {
+		dprintk("NFS: --> %s client ID %llx does not match %llx\n",
+			__func__, a->cl_clientid, b->cl_clientid);
+		return false;
+	}
+	dprintk("NFS: --> %s client ID %llx matches %llx\n",
+		__func__, a->cl_clientid, b->cl_clientid);
+	return true;
+}
+
+/**
+ * nfs40_walk_client_list - Find server that recognizes a client ID
+ *
+ * @new: nfs_client with client ID to test
+ * @result: OUT: found nfs_client, or new
+ * @cred: credential to use for trunking test
+ *
+ * Returns NFS4_OK, a negative errno, or a negative NFS4ERR status.
+ * If NFS4_OK is returned, an nfs_client pointer is planted in "result."
+ *
+ * NB: nfs40_walk_client_list() relies on the new nfs_client being
+ *     the last nfs_client on the list.
+ */
+int nfs40_walk_client_list(struct nfs_client *new,
+			   struct nfs_client **result,
+			   struct rpc_cred *cred)
+{
+	struct nfs_net *nn = net_generic(new->cl_net, nfs_net_id);
+	struct nfs_client *pos, *prev = NULL;
+	struct nfs4_setclientid_res clid = {
+		.clientid	= new->cl_clientid,
+		.confirm	= new->cl_confirm,
+	};
+	int status;
+
+	dprintk("NFS: --> %s nfs_client = %p\n", __func__, new);
+
+	spin_lock(&nn->nfs_client_lock);
+
+	list_for_each_entry(pos, &nn->nfs_client_list, cl_share_link) {
+		if (pos->cl_cons_state < 0)
+			continue;
+
+		if (pos->rpc_ops != new->rpc_ops)
+			continue;
+
+		if (pos->cl_proto != new->cl_proto)
+			continue;
+
+		if (pos->cl_minorversion != new->cl_minorversion)
+			continue;
+
+		dprintk("NFS: --> %s comparing %llx and %llx\n", __func__,
+			new->cl_clientid, pos->cl_clientid);
+		if (pos->cl_clientid != new->cl_clientid)
+			continue;
+
+		atomic_inc(&pos->cl_count);
+		dprintk("%s nfs_client = %p ({%d})\n",
+			__func__, pos, atomic_read(&pos->cl_count));
+		spin_unlock(&nn->nfs_client_lock);
+
+		dprintk("NFS: --> %s confirming %llx\n",
+			__func__, new->cl_clientid);
+
+		if (prev)
+			nfs_put_client(prev);
+
+		status = nfs4_proc_setclientid_confirm(pos, &clid, cred);
+		if (status == NFS4_OK) {
+			/* The new nfs_client doesn't need the extra
+			 * cl_count bump. */
+			nfs_put_client(pos);
+			*result = pos;
+			dprintk("NFS: <-- %s using nfs_client = %p ({%d})\n",
+				__func__, pos, atomic_read(&pos->cl_count));
+			return NFS4_OK;
+		}
+		if (status != NFS4ERR_STALE_CLIENTID) {
+			nfs_put_client(pos);
+			dprintk("NFS: <-- %s status = %d, no result\n",
+				__func__, status);
+			return status;
+		}
+
+		spin_lock(&nn->nfs_client_lock);
+		prev = pos;
+	}
+
+	/*
+	 * No matching nfs_client found.  This should be impossible,
+	 * because the new nfs_client has already been added to
+	 * nfs_client_list by nfs_get_client().
+	 *
+	 * Don't BUG(), since the caller is holding a mutex.
+	 */
+	spin_unlock(&nn->nfs_client_lock);
+	printk(KERN_ERR "NFS: %s Error: no matching nfs_client found\n",
+		__func__);
+	return NFS4ERR_STALE_CLIENTID;
+}
+
+#ifdef CONFIG_NFS_V4_1
+/*
+ * Returns true if the server owners match
+ */
+static bool
+nfs4_match_serverowners(struct nfs_client *a, struct nfs_client *b)
+{
+	struct nfs41_server_owner *o1 = a->cl_serverowner;
+	struct nfs41_server_owner *o2 = b->cl_serverowner;
+
+	if (o1->minor_id != o2->minor_id) {
+		dprintk("NFS: --> %s server owner minor IDs do not match\n",
+			__func__);
+		return false;
+	}
+
+	if (o1->major_id_sz != o2->major_id_sz)
+		goto out_major_mismatch;
+	if (memcmp(o1->major_id, o2->major_id, o1->major_id_sz) != 0)
+		goto out_major_mismatch;
+
+	dprintk("NFS: --> %s server owners match\n", __func__);
+	return true;
+
+out_major_mismatch:
+	dprintk("NFS: --> %s server owner major IDs do not match\n",
+		__func__);
+	return false;
+}
+
+/**
+ * nfs41_walk_client_list - Find nfs_client that matches a client/server owner
+ *
+ * @new: nfs_client with client ID to test
+ * @result: OUT: found nfs_client, or new
+ * @cred: credential to use for trunking test
+ *
+ * Returns NFS4_OK, a negative errno, or a negative NFS4ERR status.
+ * If NFS4_OK is returned, an nfs_client pointer is planted in "result."
+ *
+ * NB: nfs41_walk_client_list() relies on the new nfs_client being
+ *     the last nfs_client on the list.
+ */
+int nfs41_walk_client_list(struct nfs_client *new,
+			   struct nfs_client **result,
+			   struct rpc_cred *cred)
+{
+	struct nfs_net *nn = net_generic(new->cl_net, nfs_net_id);
+	struct nfs_client *pos;
+
+	dprintk("NFS: --> %s nfs_client = %p\n", __func__, new);
+
+	spin_lock(&nn->nfs_client_lock);
+
+	list_for_each_entry(pos, &nn->nfs_client_list, cl_share_link) {
+		if (pos->cl_cons_state < 0)
+			continue;
+
+		if (pos->rpc_ops != new->rpc_ops)
+			continue;
+
+		if (pos->cl_proto != new->cl_proto)
+			continue;
+
+		if (pos->cl_minorversion != new->cl_minorversion)
+			continue;
+
+		if (!nfs4_match_clientids(pos, new))
+			continue;
+
+		if (!nfs4_match_serverowners(pos, new))
+			continue;
+
+		atomic_inc(&pos->cl_count);
+		*result = pos;
+		dprintk("NFS: <-- %s using nfs_client = %p ({%d})\n",
+			__func__, pos, atomic_read(&pos->cl_count));
+		return NFS4_OK;
+	}
+
+	/*
+	 * No matching nfs_client found.  This should be impossible,
+	 * because the new nfs_client has already been added to
+	 * nfs_client_list by nfs_get_client().
+	 *
+	 * Don't BUG(), since the caller is holding a mutex.
+	 */
+	spin_unlock(&nn->nfs_client_lock);
+	printk(KERN_ERR "NFS: %s Error: no matching nfs_client found\n",
+		__func__);
+	return NFS4ERR_STALE_CLIENTID;
+}
+#endif	/* CONFIG_NFS_V4_1 */
+#endif	/* CONFIG_NFS_V4 */
+
 /*
  * Mark a server as ready or failed
  */
@@ -1350,6 +1555,7 @@ struct nfs_client *nfs4_init_client(struct nfs_client *clp,
 				    rpc_authflavor_t authflavour)
 {
 	char buf[INET6_ADDRSTRLEN + 1];
+	struct nfs_client *old;
 	int error;
 
 	if (clp->cl_cons_state == NFS_CS_READY) {
@@ -1395,6 +1601,21 @@ struct nfs_client *nfs4_init_client(struct nfs_client *clp,
 
 	if (!nfs4_has_session(clp))
 		nfs_mark_client_ready(clp, NFS_CS_READY);
+
+	error = nfs4_detect_trunking(clp, &old);
+	if (error < 0)
+		goto error;
+	if (clp != old) {
+		nfs_mark_client_ready(clp, NFS_CS_READY);
+		nfs_put_client(clp);
+		dprintk("<-- %s() returning %p instead of %p\n",
+			__func__, old, clp);
+		clp = old;
+		atomic_inc(&clp->cl_count);
+		dprintk("NFS: <-- %s using nfs_client = %p ({%d})\n",
+			__func__, clp, atomic_read(&clp->cl_count));
+	}
+
 	return clp;
 
 error:
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 315dc86..85888f6 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -162,6 +162,12 @@ extern struct nfs_client *nfs4_init_client(struct nfs_client *clp,
 				const struct rpc_timeout *timeparms,
 				const char *ip_addr,
 				rpc_authflavor_t authflavour);
+extern int nfs40_walk_client_list(struct nfs_client *clp,
+				struct nfs_client **result,
+				struct rpc_cred *cred);
+extern int nfs41_walk_client_list(struct nfs_client *clp,
+				struct nfs_client **result,
+				struct rpc_cred *cred);
 extern struct nfs_server *nfs_create_server(
 					const struct nfs_parsed_mount_data *,
 					struct nfs_fh *);
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index 2953f2c..ba13986 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -190,6 +190,8 @@ struct nfs4_state_recovery_ops {
 	int (*establish_clid)(struct nfs_client *, struct rpc_cred *);
 	struct rpc_cred * (*get_clid_cred)(struct nfs_client *);
 	int (*reclaim_complete)(struct nfs_client *);
+	int (*detect_trunking)(struct nfs_client *, struct nfs_client **,
+		struct rpc_cred *);
 };
 
 struct nfs4_state_maintenance_ops {
@@ -297,9 +299,14 @@ extern void nfs4_renew_state(struct work_struct *);
 /* nfs4state.c */
 struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp);
 struct rpc_cred *nfs4_get_renew_cred_locked(struct nfs_client *clp);
+int nfs4_detect_trunking(struct nfs_client *clp, struct nfs_client **);
+int nfs40_detect_trunking(struct nfs_client *clp, struct nfs_client **,
+			struct rpc_cred *);
 #if defined(CONFIG_NFS_V4_1)
 struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp);
 struct rpc_cred *nfs4_get_exchange_id_cred(struct nfs_client *clp);
+int nfs41_detect_trunking(struct nfs_client *clp, struct nfs_client **,
+			struct rpc_cred *);
 extern void nfs4_schedule_session_recovery(struct nfs4_session *);
 #else
 static inline void nfs4_schedule_session_recovery(struct nfs4_session *session)
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 3fd9944..00b5d02 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -6453,6 +6453,7 @@ static const struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = {
 	.recover_lock	= nfs4_lock_reclaim,
 	.establish_clid = nfs4_init_clientid,
 	.get_clid_cred	= nfs4_get_setclientid_cred,
+	.detect_trunking = nfs40_detect_trunking,
 };
 
 #if defined(CONFIG_NFS_V4_1)
@@ -6464,6 +6465,7 @@ static const struct nfs4_state_recovery_ops nfs41_reboot_recovery_ops = {
 	.establish_clid = nfs41_init_clientid,
 	.get_clid_cred	= nfs4_get_exchange_id_cred,
 	.reclaim_complete = nfs41_proc_reclaim_complete,
+	.detect_trunking = nfs41_detect_trunking,
 };
 #endif /* CONFIG_NFS_V4_1 */
 
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
index 6a1a305..df59951 100644
--- a/fs/nfs/nfs4state.c
+++ b/fs/nfs/nfs4state.c
@@ -57,10 +57,12 @@
 #include "internal.h"
 #include "pnfs.h"
 
+#define NFSDBG_FACILITY		NFSDBG_CLIENT
+
 #define OPENOWNER_POOL_SIZE	8
 
 const nfs4_stateid zero_stateid;
-
+static DEFINE_MUTEX(nfs_clid_init_mutex);
 static LIST_HEAD(nfs4_clientid_list);
 
 int nfs4_init_clientid(struct nfs_client *clp, struct rpc_cred *cred)
@@ -94,6 +96,47 @@ out:
 	return status;
 }
 
+/**
+ * nfs40_detect_trunking - Detect server IP address trunking (mv0)
+ *
+ * @clp: nfs_client under test
+ * @result: OUT: found nfs_client, or clp
+ * @cred: credential to use for trunking test
+ *
+ * Returns NFS4_OK, a negative errno, or a negative NFS4ERR status.
+ * If NFS4_OK is returned, an nfs_client pointer is planted in
+ * "result".
+ */
+int nfs40_detect_trunking(struct nfs_client *clp, struct nfs_client **result,
+			  struct rpc_cred *cred)
+{
+	struct nfs4_setclientid_res clid = {
+		.clientid = clp->cl_clientid,
+		.confirm = clp->cl_confirm,
+	};
+	unsigned short port;
+	int status;
+
+	port = nfs_callback_tcpport;
+	if (clp->cl_addr.ss_family == AF_INET6)
+		port = nfs_callback_tcpport6;
+
+	status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, port, cred, &clid);
+	if (status != NFS4_OK)
+		goto out;
+	clp->cl_clientid = clid.clientid;
+	clp->cl_confirm = clid.confirm;
+
+	status = nfs40_walk_client_list(clp, result, cred);
+	if (status != NFS4_OK) {
+		set_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
+		nfs4_schedule_state_renewal(*result);
+	}
+
+out:
+	return status;
+}
+
 struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp)
 {
 	struct rpc_cred *cred = NULL;
@@ -264,6 +307,44 @@ out:
 	return status;
 }
 
+/**
+ * nfs41_detect_trunking - Detect server IP address trunking (mv1)
+ *
+ * @clp: nfs_client under test
+ * @result: OUT: found nfs_client, or clp
+ * @cred: credential to use for trunking test
+ *
+ * Returns NFS4_OK, a negative errno, or a negative NFS4ERR status.
+ * If NFS4_OK is returned, an nfs_client pointer is planted in
+ * "result".
+ */
+int nfs41_detect_trunking(struct nfs_client *clp, struct nfs_client **result,
+			  struct rpc_cred *cred)
+{
+	struct nfs_client *trunked;
+	int status;
+
+	nfs4_begin_drain_session(clp);
+	status = nfs4_proc_exchange_id(clp, cred);
+	if (status != NFS4_OK)
+		goto out;
+
+	status = nfs41_walk_client_list(clp, &trunked, cred);
+	if (status != NFS4_OK)
+		goto out;
+
+	set_bit(NFS4CLNT_LEASE_CONFIRM, &trunked->cl_state);
+	status = nfs4_proc_create_session(trunked);
+	if (status != NFS4_OK)
+		goto out;
+	clear_bit(NFS4CLNT_LEASE_CONFIRM, &trunked->cl_state);
+	nfs41_setup_state_renewal(trunked);
+	nfs_mark_client_ready(trunked, NFS_CS_READY);
+	*result = trunked;
+out:
+	return status;
+}
+
 struct rpc_cred *nfs4_get_exchange_id_cred(struct nfs_client *clp)
 {
 	struct rpc_cred *cred;
@@ -1579,6 +1660,8 @@ static int nfs4_reclaim_lease(struct nfs_client *clp)
 	rpc_authflavor_t flavors[NFS_MAX_SECFLAVORS];
 	int i, len, status;
 
+	mutex_lock(&nfs_clid_init_mutex);
+
 	i = 0;
 	len = gss_mech_list_pseudoflavors(flavors);
 
@@ -1613,6 +1696,52 @@ again:
 			break;
 		}
 	}
+
+	mutex_unlock(&nfs_clid_init_mutex);
+	return status;
+}
+
+/**
+ * nfs4_detect_trunking - Detect server IP address trunking
+ *
+ * @clp: nfs_client under test
+ * @result: OUT: found nfs_client, or clp
+ *
+ * Returns NFS4_OK, a negative errno, or a negative NFS4ERR status.
+ * If NFS4_OK is returned, an nfs_client pointer is planted in
+ * "result".
+ */
+int nfs4_detect_trunking(struct nfs_client *clp,
+			 struct nfs_client **result)
+{
+	const struct nfs4_state_recovery_ops *ops =
+				clp->cl_mvops->reboot_recovery_ops;
+	struct rpc_cred *cred;
+	int status;
+
+	dprintk("NFS: <-- %s nfs_client = %p\n", __func__, clp);
+	mutex_lock(&nfs_clid_init_mutex);
+
+	status = -ENOENT;
+	cred = ops->get_clid_cred(clp);
+	if (cred != NULL) {
+		status = ops->detect_trunking(clp, result, cred);
+		put_rpccred(cred);
+		/* Handle case where the user hasn't set up machine creds */
+		if (status == -EACCES && cred == clp->cl_machine_cred) {
+			nfs4_clear_machine_cred(clp);
+			status = -EAGAIN;
+		}
+		if (status == -NFS4ERR_MINOR_VERS_MISMATCH)
+			status = -EPROTONOSUPPORT;
+	}
+
+	mutex_unlock(&nfs_clid_init_mutex);
+	if (status == NFS4_OK) {
+		clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
+		dprintk("NFS: <-- %s result = %p\n", __func__, *result);
+	} else
+		dprintk("NFS: <-- %s status = %d\n", __func__, status);
 	return status;
 }
 


  parent reply	other threads:[~2012-04-23 20:55 UTC|newest]

Thread overview: 37+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-04-23 20:53 [PATCH 01/20] NFS: Fix comment misspelling in struct nfs_client definition Chuck Lever
2012-04-23 20:53 ` [PATCH 02/20] NFS: Use proper naming conventions for NFSv4.1 server scope fields Chuck Lever
2012-04-23 20:53 ` [PATCH 03/20] NFS: Use proper naming conventions for nfs_client.impl_id field Chuck Lever
2012-04-23 20:53 ` [PATCH 04/20] NFS: Use proper naming conventions for the nfs_client.net field Chuck Lever
2012-04-23 20:53 ` [PATCH 05/20] NFS: Clean up return code checking in nfs4_proc_exchange_id() Chuck Lever
2012-04-23 21:07   ` Myklebust, Trond
2012-04-23 20:54 ` [PATCH 06/20] NFS: Remove nfs_unique_id Chuck Lever
2012-04-23 20:54 ` [PATCH 07/20] NFS: Don't swap bytes in nfs4_construct_boot_verifier() Chuck Lever
2012-04-23 20:54 ` [PATCH 08/20] NFS: Fix NFSv4 BAD_SEQID recovery Chuck Lever
2012-04-23 20:54 ` [PATCH 09/20] NFS: Force server to drop NFSv4 state Chuck Lever
2012-04-23 21:13   ` Myklebust, Trond
2012-04-23 21:18     ` Chuck Lever
2012-04-23 20:54 ` [PATCH 10/20] NFS: Always use the same SETCLIENTID boot verifier Chuck Lever
2012-04-23 20:54 ` [PATCH 11/20] NFS: Refactor nfs_get_client(): add nfs_found_client() Chuck Lever
2012-04-23 20:54 ` [PATCH 12/20] NFS: Refactor nfs_get_client(): initialize nfs_client Chuck Lever
2012-04-23 20:55 ` [PATCH 13/20] NFS: Fix recovery from NFS4ERR_CLID_INUSE Chuck Lever
2012-04-26 16:24   ` Chuck Lever
2012-04-26 16:55     ` Myklebust, Trond
2012-04-26 18:43       ` Chuck Lever
2012-04-26 18:53         ` Myklebust, Trond
2012-04-26 18:57           ` Myklebust, Trond
2012-04-26 19:04           ` Chuck Lever
2012-04-26 19:14             ` Myklebust, Trond
2012-04-26 19:46               ` Chuck Lever
2012-04-26 19:57                 ` Myklebust, Trond
2012-04-23 20:55 ` [PATCH 14/20] NFS: Add nfs_client behavior flags Chuck Lever
2012-04-23 20:55 ` [PATCH 15/20] NFS: Introduce "migration" mount option Chuck Lever
2012-04-23 20:55 ` [PATCH 16/20] NFS: Use the same nfs_client_id4 for every server Chuck Lever
2012-04-23 20:55 ` [PATCH 17/20] NFS: EXCHANGE_ID should save the server major and minor ID Chuck Lever
2012-04-23 20:55 ` Chuck Lever [this message]
2012-04-23 21:27   ` [PATCH 18/20] NFS: Detect NFSv4 server trunking when mounting Myklebust, Trond
2012-04-23 21:43     ` Chuck Lever
2012-04-23 21:47     ` Chuck Lever
2012-04-23 21:56       ` Myklebust, Trond
2012-04-23 20:56 ` [PATCH 19/20] NFS: Add nfs4_unique_id boot parameter Chuck Lever
2012-04-23 20:56 ` [PATCH 20/20] NFS: Clean up debugging messages in fs/nfs/client.c Chuck Lever
2012-04-23 21:23   ` Malahal Naineni

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20120423205552.11446.58714.stgit@degas.1015granger.net \
    --to=chuck.lever@oracle.com \
    --cc=Trond.Myklebust@netapp.com \
    --cc=linux-nfs@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.