linux-scsi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Dan Williams <dan.j.williams@intel.com>
To: linux-scsi@vger.kernel.org
Cc: Jeff Skirvin <jeffrey.d.skirvin@intel.com>
Subject: [isci-rnc PATCH v1 10/37] isci: Add suspension cases for RNC INVALIDATING, POSTING states.
Date: Thu, 22 Mar 2012 17:28:45 -0700	[thread overview]
Message-ID: <20120323002845.18065.77645.stgit@dwillia2-linux.jf.intel.com> (raw)
In-Reply-To: <20120323002504.18065.45709.stgit@dwillia2-linux.jf.intel.com>

From: Jeff Skirvin <jeffrey.d.skirvin@intel.com>

The RNC can be any of the states in the loop from suspended to
ready when the API "suspend" or "resume" are called.  This change
adds destination states parameters that control the suspension /
resumption action of the RNC statemachine for those transition states.

Signed-off-by: Jeff Skirvin <jeffrey.d.skirvin@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
 drivers/scsi/isci/port.c                |   11 --
 drivers/scsi/isci/remote_device.c       |    2 
 drivers/scsi/isci/remote_node_context.c |  145 +++++++++++++++++++++----------
 drivers/scsi/isci/remote_node_context.h |   10 +-
 4 files changed, 105 insertions(+), 63 deletions(-)

diff --git a/drivers/scsi/isci/port.c b/drivers/scsi/isci/port.c
index 00666f7..4858ddd 100644
--- a/drivers/scsi/isci/port.c
+++ b/drivers/scsi/isci/port.c
@@ -1686,17 +1686,6 @@ int isci_port_perform_hard_reset(struct isci_host *ihost, struct isci_port *ipor
 			__func__, iport, status);
 
 	}
-
-	/* If the hard reset for the port has failed, consider this
-	 * the same as link failures on all phys in the port.
-	 */
-	if (ret != TMF_RESP_FUNC_COMPLETE) {
-
-		dev_err(&ihost->pdev->dev,
-			"%s: iport = %p; hard reset failed "
-			"(0x%x) - driving explicit link fail for all phys\n",
-			__func__, iport, iport->hard_reset_status);
-	}
 	return ret;
 }
 
diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c
index c47304c..cf5d554 100644
--- a/drivers/scsi/isci/remote_device.c
+++ b/drivers/scsi/isci/remote_device.c
@@ -192,7 +192,7 @@ static void isci_remote_device_not_ready(struct isci_host *ihost,
 					 u32 reason)
 {
 	dev_dbg(&ihost->pdev->dev,
-		"%s: isci_device = %p\n", __func__, idev);
+		"%s: isci_device = %p; reason = %d\n", __func__, idev, reason);
 
 	switch (reason) {
 	case SCIC_REMOTE_DEVICE_NOT_READY_STOP_REQUESTED:
diff --git a/drivers/scsi/isci/remote_node_context.c b/drivers/scsi/isci/remote_node_context.c
index feeca17..75cf043 100644
--- a/drivers/scsi/isci/remote_node_context.c
+++ b/drivers/scsi/isci/remote_node_context.c
@@ -165,21 +165,24 @@ static void sci_remote_node_context_construct_buffer(struct sci_remote_node_cont
 static void sci_remote_node_context_setup_to_resume(
 	struct sci_remote_node_context *sci_rnc,
 	scics_sds_remote_node_context_callback callback,
-	void *callback_parameter)
+	void *callback_parameter,
+	enum sci_remote_node_context_destination_state dest_param)
 {
-	if (sci_rnc->destination_state != SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_FINAL) {
-		sci_rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY;
-		sci_rnc->user_callback     = callback;
-		sci_rnc->user_cookie       = callback_parameter;
+	if (sci_rnc->destination_state != RNC_DEST_FINAL) {
+		sci_rnc->destination_state = dest_param;
+		if (callback != NULL) {
+			sci_rnc->user_callback = callback;
+			sci_rnc->user_cookie   = callback_parameter;
+		}
 	}
 }
 
-static void sci_remote_node_context_setup_to_destory(
+static void sci_remote_node_context_setup_to_destroy(
 	struct sci_remote_node_context *sci_rnc,
 	scics_sds_remote_node_context_callback callback,
 	void *callback_parameter)
 {
-	sci_rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_FINAL;
+	sci_rnc->destination_state = RNC_DEST_FINAL;
 	sci_rnc->user_callback     = callback;
 	sci_rnc->user_cookie       = callback_parameter;
 }
@@ -203,9 +206,13 @@ static void sci_remote_node_context_notify_user(
 
 static void sci_remote_node_context_continue_state_transitions(struct sci_remote_node_context *rnc)
 {
-	if (rnc->destination_state == SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY)
+	if ((rnc->destination_state == RNC_DEST_READY) ||
+	    (rnc->destination_state == RNC_DEST_SUSPENDED_RESUME)) {
+		rnc->destination_state = RNC_DEST_READY;
 		sci_remote_node_context_resume(rnc, rnc->user_callback,
 						    rnc->user_cookie);
+	} else
+		rnc->destination_state = RNC_DEST_UNSPECIFIED;
 }
 
 static void sci_remote_node_context_validate_context_buffer(struct sci_remote_node_context *sci_rnc)
@@ -252,7 +259,7 @@ static void sci_remote_node_context_initial_state_enter(struct sci_base_state_ma
 	 * someone requested to destroy the remote node context object.
 	 */
 	if (sm->previous_state_id == SCI_RNC_INVALIDATING) {
-		rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED;
+		rnc->destination_state = RNC_DEST_UNSPECIFIED;
 		sci_remote_node_context_notify_user(rnc);
 	}
 }
@@ -297,10 +304,26 @@ static void sci_remote_node_context_resuming_state_enter(struct sci_base_state_m
 static void sci_remote_node_context_ready_state_enter(struct sci_base_state_machine *sm)
 {
 	struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm);
-
-	rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED;
-
-	if (rnc->user_callback)
+	enum sci_remote_node_context_destination_state dest_select;
+	scics_sds_remote_node_context_callback usr_cb = rnc->user_callback;
+	void *usr_param = rnc->user_cookie;
+	int tell_user = 1;
+
+	dest_select = rnc->destination_state;
+	rnc->destination_state = RNC_DEST_UNSPECIFIED;
+
+	if ((dest_select == RNC_DEST_SUSPENDED) ||
+	    (dest_select == RNC_DEST_SUSPENDED_RESUME)) {
+		sci_remote_node_context_suspend(
+			rnc, SCI_SOFTWARE_SUSPENSION,
+			SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT, NULL, NULL);
+
+		if (dest_select == RNC_DEST_SUSPENDED_RESUME) {
+			sci_remote_node_context_resume(rnc, usr_cb, usr_param);
+			tell_user = 0;  /* Wait until ready again. */
+		}
+	}
+	if (tell_user && rnc->user_callback)
 		sci_remote_node_context_notify_user(rnc);
 }
 
@@ -366,7 +389,7 @@ void sci_remote_node_context_construct(struct sci_remote_node_context *rnc,
 	memset(rnc, 0, sizeof(struct sci_remote_node_context));
 
 	rnc->remote_node_index = remote_node_index;
-	rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED;
+	rnc->destination_state = RNC_DEST_UNSPECIFIED;
 
 	sci_init_sm(&rnc->sm, sci_remote_node_context_state_table, SCI_RNC_INITIAL);
 }
@@ -390,11 +413,11 @@ enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_con
 		break;
 	case SCI_RNC_INVALIDATING:
 		if (scu_get_event_code(event_code) == SCU_EVENT_POST_RNC_INVALIDATE_COMPLETE) {
-			if (sci_rnc->destination_state == SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_FINAL)
-				state = SCI_RNC_INITIAL;
+			if (sci_rnc->destination_state == RNC_DEST_FINAL)
+				next_state = SCI_RNC_INITIAL;
 			else
-				state = SCI_RNC_POSTING;
-			sci_change_state(&sci_rnc->sm, state);
+				next_state = SCI_RNC_POSTING;
+			sci_change_state(&sci_rnc->sm, next_state);
 		} else {
 			switch (scu_get_event_type(event_code)) {
 			case SCU_EVENT_TYPE_RNC_SUSPEND_TX:
@@ -483,7 +506,7 @@ enum sci_status sci_remote_node_context_destruct(struct sci_remote_node_context
 	state = sci_rnc->sm.current_state_id;
 	switch (state) {
 	case SCI_RNC_INVALIDATING:
-		sci_remote_node_context_setup_to_destory(sci_rnc, cb_fn, cb_p);
+		sci_remote_node_context_setup_to_destroy(sci_rnc, cb_fn, cb_p);
 		return SCI_SUCCESS;
 	case SCI_RNC_POSTING:
 	case SCI_RNC_RESUMING:
@@ -491,7 +514,7 @@ enum sci_status sci_remote_node_context_destruct(struct sci_remote_node_context
 	case SCI_RNC_TX_SUSPENDED:
 	case SCI_RNC_TX_RX_SUSPENDED:
 	case SCI_RNC_AWAIT_SUSPENSION:
-		sci_remote_node_context_setup_to_destory(sci_rnc, cb_fn, cb_p);
+		sci_remote_node_context_setup_to_destroy(sci_rnc, cb_fn, cb_p);
 		sci_change_state(&sci_rnc->sm, SCI_RNC_INVALIDATING);
 		return SCI_SUCCESS;
 	case SCI_RNC_INITIAL:
@@ -522,6 +545,8 @@ enum sci_status sci_remote_node_context_suspend(
 		= sci_rnc->sm.current_state_id;
 	struct isci_remote_device *idev = rnc_to_dev(sci_rnc);
 	enum sci_status status = SCI_FAILURE_INVALID_STATE;
+	enum sci_remote_node_context_destination_state dest_param =
+		RNC_DEST_UNSPECIFIED;
 
 	dev_dbg(scirdev_to_dev(idev),
 		"%s: current state %d, current suspend_type %x dest state %d,"
@@ -531,12 +556,31 @@ enum sci_status sci_remote_node_context_suspend(
 		suspend_type);
 
 	/* Disable automatic state continuations if explicitly suspending. */
-	if (suspend_reason == SCI_SOFTWARE_SUSPENSION)
-		sci_rnc->destination_state
-			= SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED;
+	if ((suspend_reason != SCI_SOFTWARE_SUSPENSION) ||
+	    (sci_rnc->destination_state == RNC_DEST_FINAL))
+		dest_param = sci_rnc->destination_state;
+
 	switch (state) {
+	case SCI_RNC_RESUMING:
+		break;  /* The RNC has been posted, so start the suspend. */
 	case SCI_RNC_READY:
 		break;
+	case SCI_RNC_INVALIDATING:
+		if (sci_rnc->destination_state == RNC_DEST_FINAL) {
+			dev_warn(scirdev_to_dev(idev),
+				 "%s: already destroying %p\n",
+				 __func__, sci_rnc);
+			return SCI_FAILURE_INVALID_STATE;
+		}
+		/* Fall through and handle like SCI_RNC_POSTING */
+	case SCI_RNC_POSTING:
+		/* Set the destination state to AWAIT - this signals the
+		 * entry into the SCI_RNC_READY state that a suspension
+		 * needs to be done immediately.
+		 */
+		sci_rnc->destination_state = RNC_DEST_SUSPENDED;
+		return SCI_SUCCESS;
+
 	case SCI_RNC_TX_SUSPENDED:
 		if (suspend_type == SCU_EVENT_TL_RNC_SUSPEND_TX)
 			status = SCI_SUCCESS;
@@ -556,6 +600,7 @@ enum sci_status sci_remote_node_context_suspend(
 			 rnc_state_name(state));
 		return SCI_FAILURE_INVALID_STATE;
 	}
+	sci_rnc->destination_state = dest_param;
 	sci_rnc->user_callback = cb_fn;
 	sci_rnc->user_cookie   = cb_p;
 	sci_rnc->suspend_type  = suspend_type;
@@ -590,18 +635,31 @@ enum sci_status sci_remote_node_context_resume(struct sci_remote_node_context *s
 		if (sci_rnc->remote_node_index == SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX)
 			return SCI_FAILURE_INVALID_STATE;
 
-		sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p);
+		sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p,
+							RNC_DEST_READY);
 		sci_remote_node_context_construct_buffer(sci_rnc);
 		sci_change_state(&sci_rnc->sm, SCI_RNC_POSTING);
 		return SCI_SUCCESS;
 	case SCI_RNC_POSTING:
 	case SCI_RNC_INVALIDATING:
 	case SCI_RNC_RESUMING:
-		if (sci_rnc->destination_state != SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY)
-			return SCI_FAILURE_INVALID_STATE;
-
-		sci_rnc->user_callback = cb_fn;
-		sci_rnc->user_cookie   = cb_p;
+		/* We are still waiting to post when a resume was requested. */
+		switch (sci_rnc->destination_state) {
+		case RNC_DEST_SUSPENDED:
+		case RNC_DEST_SUSPENDED_RESUME:
+			/* Previously waiting to suspend after posting.  Now
+			 * continue onto resumption.
+			 */
+			sci_remote_node_context_setup_to_resume(
+				sci_rnc, cb_fn, cb_p,
+				RNC_DEST_SUSPENDED_RESUME);
+			break;
+		default:
+			sci_remote_node_context_setup_to_resume(
+				sci_rnc, cb_fn, cb_p,
+				RNC_DEST_READY);
+			break;
+		}
 		return SCI_SUCCESS;
 	case SCI_RNC_TX_SUSPENDED:
 	case SCI_RNC_TX_RX_SUSPENDED: {
@@ -613,7 +671,8 @@ enum sci_status sci_remote_node_context_resume(struct sci_remote_node_context *s
 		 * to clear the TCi to NCQ tag mapping table for the RNi.
 		 * All other device types we can just resume.
 		 */
-		sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p);
+		sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p,
+							RNC_DEST_READY);
 
 		if (dev_is_sata(dev) && dev->parent)
 			sci_change_state(&sci_rnc->sm, SCI_RNC_INVALIDATING);
@@ -622,7 +681,9 @@ enum sci_status sci_remote_node_context_resume(struct sci_remote_node_context *s
 		return SCI_SUCCESS;
 	}
 	case SCI_RNC_AWAIT_SUSPENSION:
-		sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p);
+		sci_remote_node_context_setup_to_resume(
+			sci_rnc, cb_fn, cb_p,
+			RNC_DEST_SUSPENDED_RESUME);
 		return SCI_SUCCESS;
 	default:
 		dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
@@ -663,24 +724,12 @@ enum sci_status sci_remote_node_context_start_task(
 	scics_sds_remote_node_context_callback cb_fn,
 	void *cb_p)
 {
-	enum scis_sds_remote_node_context_states state;
-
-	state = sci_rnc->sm.current_state_id;
-	switch (state) {
-	case SCI_RNC_RESUMING:
-	case SCI_RNC_READY:
-	case SCI_RNC_AWAIT_SUSPENSION:
-		return SCI_SUCCESS;
-	case SCI_RNC_TX_SUSPENDED:
-	case SCI_RNC_TX_RX_SUSPENDED:
-		sci_remote_node_context_resume(sci_rnc, cb_fn, cb_p);
-		return SCI_SUCCESS;
-	default:
+	enum sci_status status = sci_remote_node_context_resume(sci_rnc,
+								cb_fn, cb_p);
+	if (status != SCI_SUCCESS)
 		dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
-			"%s: invalid state %s\n", __func__,
-			rnc_state_name(state));
-		return SCI_FAILURE_INVALID_STATE;
-	}
+			"%s: resume failed: %d\n", __func__, status);
+	return status;
 }
 
 int sci_remote_node_context_is_safe_to_abort(
diff --git a/drivers/scsi/isci/remote_node_context.h b/drivers/scsi/isci/remote_node_context.h
index 2870af1..4831906 100644
--- a/drivers/scsi/isci/remote_node_context.h
+++ b/drivers/scsi/isci/remote_node_context.h
@@ -141,9 +141,13 @@ const char *rnc_state_name(enum scis_sds_remote_node_context_states state);
  * node context.
  */
 enum sci_remote_node_context_destination_state {
-	SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED,
-	SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY,
-	SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_FINAL
+	RNC_DEST_UNSPECIFIED,
+	RNC_DEST_READY,
+	RNC_DEST_FINAL,
+	RNC_DEST_SUSPENDED,       /* Set when suspend during post/invalidate */
+	RNC_DEST_SUSPENDED_RESUME /* Set when a resume was done during posting
+				   * or invalidating and already suspending.
+				   */
 };
 
 /**


  parent reply	other threads:[~2012-03-23  0:13 UTC|newest]

Thread overview: 38+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-03-23  0:27 [isci-rnc PATCH v1 00/37] remote node context rework Dan Williams
2012-03-23  0:27 ` [isci-rnc PATCH v1 01/37] isci: Manage the link layer hang detect timer for RNC suspensions Dan Williams
2012-03-23  0:27 ` [isci-rnc PATCH v1 02/37] isci: Fixed bug in resumption from RNC Tx/Rx suspend state Dan Williams
2012-03-23  0:28 ` [isci-rnc PATCH v1 03/37] isci: Handle all suspending TC completions Dan Williams
2012-03-23  0:28 ` [isci-rnc PATCH v1 04/37] isci: Terminate outstanding TCs on TX/RX RNC suspensions Dan Williams
2012-03-23  0:28 ` [isci-rnc PATCH v1 05/37] isci: Manage device suspensions during TC terminations Dan Williams
2012-03-23  0:28 ` [isci-rnc PATCH v1 06/37] isci: Remote device must be suspended for NCQ cleanup Dan Williams
2012-03-23  0:28 ` [isci-rnc PATCH v1 07/37] isci: Remote device stop also suspends the RNC and terminates I/O Dan Williams
2012-03-23  0:28 ` [isci-rnc PATCH v1 08/37] isci: Escalate to I_T_Nexus_Reset when the device is gone Dan Williams
2012-03-23  0:28 ` [isci-rnc PATCH v1 09/37] isci: Redesign device suspension, abort, cleanup Dan Williams
2012-03-23  0:28 ` Dan Williams [this message]
2012-03-23  0:28 ` [isci-rnc PATCH v1 11/37] isci: Device access in the error path does not depend on IDEV_GONE Dan Williams
2012-03-23  0:28 ` [isci-rnc PATCH v1 12/37] isci: All pending TCs are terminated when the RNC is invalidated Dan Williams
2012-03-23  0:29 ` [isci-rnc PATCH v1 13/37] isci: Only set IDEV_GONE in the device stop path Dan Williams
2012-03-23  0:29 ` [isci-rnc PATCH v1 14/37] isci: Remove isci_device reqs_in_process and dev_node from isci_device Dan Williams
2012-03-23  0:29 ` [isci-rnc PATCH v1 15/37] isci: Distinguish between remote device suspension cases Dan Williams
2012-03-23  0:29 ` [isci-rnc PATCH v1 16/37] isci: Fix the terminated I/O to not call sas_task_abort() Dan Williams
2012-03-23  0:29 ` [isci-rnc PATCH v1 17/37] isci: Save the suspension hint for upcoming suspensions Dan Williams
2012-03-23  0:29 ` [isci-rnc PATCH v1 18/37] isci: Manage the LLHANG timer enable/disable per-device Dan Williams
2012-03-23  0:29 ` [isci-rnc PATCH v1 19/37] isci: Make sure all TCs are terminated and cleaned in LUN reset Dan Williams
2012-03-23  0:29 ` [isci-rnc PATCH v1 20/37] isci: Implement waiting for suspend in the abort path Dan Williams
2012-03-23  0:29 ` [isci-rnc PATCH v1 21/37] isci: When in the abort path, defeat other resume calls until done Dan Williams
2012-03-23  0:29 ` [isci-rnc PATCH v1 22/37] isci: Callbacks to libsas occur under scic_lock and are synchronized Dan Williams
2012-03-23  0:29 ` [isci-rnc PATCH v1 23/37] isci: Manage tag releases differently when aborting tasks Dan Williams
2012-03-23  0:29 ` [isci-rnc PATCH v1 24/37] isci: Fix RNC suspend call for SCI_RESUMING state Dan Williams
2012-03-23  0:30 ` [isci-rnc PATCH v1 25/37] isci: Wait for RNC resumption before leaving the abort path Dan Williams
2012-03-23  0:30 ` [isci-rnc PATCH v1 26/37] isci: Directly control IREQ_ABORT_PATH_ACTIVE when completing TMFs Dan Williams
2012-03-23  0:30 ` [isci-rnc PATCH v1 27/37] isci: Add protocol indicator for TMF requests Dan Williams
2012-03-23  0:30 ` [isci-rnc PATCH v1 28/37] isci: Added timeouts to RNC suspensions in the abort path Dan Williams
2012-03-23  0:30 ` [isci-rnc PATCH v1 29/37] isci: Change the phy control and link reset interface for HW reasons Dan Williams
2012-03-23  0:30 ` [isci-rnc PATCH v1 30/37] isci: Don't wait for an RNC suspend if it's being destroyed Dan Williams
2012-03-23  0:30 ` [isci-rnc PATCH v1 31/37] isci: Restore the ATAPI device RNC management code Dan Williams
2012-03-23  0:30 ` [isci-rnc PATCH v1 32/37] isci: Check IDEV_GONE before performing abort path operations Dan Williams
2012-03-23  0:30 ` [isci-rnc PATCH v1 33/37] isci: Remove obviated host callback list Dan Williams
2012-03-23  0:30 ` [isci-rnc PATCH v1 34/37] isci: Manage the IREQ_NO_AUTO_FREE_TAG under scic_lock Dan Williams
2012-03-23  0:31 ` [isci-rnc PATCH v1 35/37] isci: Fix RNC AWAIT_SUSPENSION->INVALIDATING transition Dan Williams
2012-03-23  0:31 ` [isci-rnc PATCH v1 36/37] isci: Fixed RNC bug that lost the suspension or resumption during destroy Dan Williams
2012-03-23  0:31 ` [isci-rnc PATCH v1 37/37] isci: End the RNC resumption wait when the RNC is destroyed Dan Williams

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=20120323002845.18065.77645.stgit@dwillia2-linux.jf.intel.com \
    --to=dan.j.williams@intel.com \
    --cc=jeffrey.d.skirvin@intel.com \
    --cc=linux-scsi@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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).