DPDK patches and discussions
 help / color / mirror / Atom feed
From: <skori@marvell.com>
To: Ferruh Yigit <ferruh.yigit@amd.com>,
	Thomas Monjalon <thomas@monjalon.net>,
	Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>,
	"Ray Kinsella" <mdr@ashroe.eu>
Cc: <dev@dpdk.org>, Jerin Jacob <jerinj@marvell.com>,
	Sunil Kumar Kori <skori@marvell.com>
Subject: [PATCH v3 1/1] ethdev: support congestion management
Date: Thu, 29 Sep 2022 15:05:03 +0530	[thread overview]
Message-ID: <20220929093503.2172344-1-skori@marvell.com> (raw)
In-Reply-To: <20220919121534.1058884-1-skori@marvell.com>

From: Jerin Jacob <jerinj@marvell.com>

NIC HW controllers often come with congestion management support on
various HW objects such as Rx queue depth or mempool queue depth.

Also, it can support various modes of operation such as RED
(Random early discard), WRED etc on those HW objects.

This patch adds a framework to express such modes(enum rte_cman_mode)
and introduce (enum rte_eth_cman_obj) to enumerate the different
objects where the modes can operate on.

This patch adds RTE_CMAN_RED mode of operation and
RTE_ETH_CMAN_OBJ_RX_QUEUE, RTE_ETH_CMAN_OBJ_RX_QUEUE_MEMPOOL object.

Introduced reserved fields in configuration structure
backed by rte_eth_cman_config_init() to add new configuration
parameters without ABI breakage.

Added rte_eth_cman_info_get() API to get the information such as
supported modes and objects.

Added rte_eth_cman_config_init(), rte_eth_cman_config_set() APIs
to configure congestion management on those object with associated mode.

Finally, Added rte_eth_cman_config_get() API to retrieve the
applied configuration.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Sunil Kumar Kori <skori@marvell.com>
---
v2..v3:
 - Rename rte_cman.c to rte_ethdev_cman.c
 - Move lib/eal/include/rte_cman.h to lib/ethdev/rte_cman.h
 - Fix review comments (Andrew Rybchenko)
 - Add release notes

v1..v2:
 - Fix review comments (Akhil Goyal)

rfc..v1:
 - Added RED specification (http://www.aciri.org/floyd/papers/red/red.html) link
 - Fixed doxygen comment issue (Min Hu)

 doc/guides/nics/features.rst           |  12 ++
 doc/guides/nics/features/default.ini   |   1 +
 doc/guides/rel_notes/release_22_11.rst |   6 +
 lib/ethdev/ethdev_driver.h             |  25 ++++
 lib/ethdev/ethdev_private.c            |  12 ++
 lib/ethdev/ethdev_private.h            |   2 +
 lib/ethdev/meson.build                 |   2 +
 lib/ethdev/rte_cman.h                  |  55 +++++++++
 lib/ethdev/rte_ethdev.h                | 164 +++++++++++++++++++++++++
 lib/ethdev/rte_ethdev_cman.c           | 101 +++++++++++++++
 lib/ethdev/version.map                 |   6 +
 11 files changed, 386 insertions(+)
 create mode 100644 lib/ethdev/rte_cman.h
 create mode 100644 lib/ethdev/rte_ethdev_cman.c

diff --git a/doc/guides/nics/features.rst b/doc/guides/nics/features.rst
index b4a8e9881c..70ca46e651 100644
--- a/doc/guides/nics/features.rst
+++ b/doc/guides/nics/features.rst
@@ -727,6 +727,18 @@ Supports configuring per-queue stat counter mapping.
   ``rte_eth_dev_set_tx_queue_stats_mapping()``.
 
 
+.. _nic_features_congestion_management:
+
+Congestion management
+---------------------
+
+Supports congestion management.
+
+* **[implements] eth_dev_ops**: ``cman_info_get``, ``cman_config_set``, ``cman_config_get``.
+* **[related]    API**: ``rte_eth_cman_info_get()``, ``rte_eth_cman_config_init()``,
+  ``rte_eth_cman_config_set()``, ``rte_eth_cman_config_get()``.
+
+
 .. _nic_features_fw_version:
 
 FW version
diff --git a/doc/guides/nics/features/default.ini b/doc/guides/nics/features/default.ini
index f7192cb0da..a9c0008ebd 100644
--- a/doc/guides/nics/features/default.ini
+++ b/doc/guides/nics/features/default.ini
@@ -60,6 +60,7 @@ Tx descriptor status =
 Basic stats          =
 Extended stats       =
 Stats per queue      =
+Congestion management =
 FW version           =
 EEPROM dump          =
 Module EEPROM dump   =
diff --git a/doc/guides/rel_notes/release_22_11.rst b/doc/guides/rel_notes/release_22_11.rst
index 0231959874..ea9908e578 100644
--- a/doc/guides/rel_notes/release_22_11.rst
+++ b/doc/guides/rel_notes/release_22_11.rst
@@ -81,6 +81,12 @@ New Features
   * Added AES-CCM support in lookaside protocol (IPsec) for CN9K & CN10K.
   * Added AES & DES DOCSIS algorithm support in lookaside crypto for CN9K.
 
+* **Added support for congestion management for ethdev.**
+
+  Added new APIs ``rte_eth_cman_config_init()``, ``rte_eth_cman_config_get()``,
+  ``rte_eth_cman_config_set()``, ``rte_eth_cman_info_get()``
+  to support congestion management.
+
 * **Added eventdev adapter instance get API.**
 
   * Added ``rte_event_eth_rx_adapter_instance_get`` to get Rx adapter
diff --git a/lib/ethdev/ethdev_driver.h b/lib/ethdev/ethdev_driver.h
index 8cd8eb8685..e1e2d10a35 100644
--- a/lib/ethdev/ethdev_driver.h
+++ b/lib/ethdev/ethdev_driver.h
@@ -1094,6 +1094,22 @@ typedef int (*eth_rx_queue_avail_thresh_query_t)(struct rte_eth_dev *dev,
 					uint16_t *rx_queue_id,
 					uint8_t *avail_thresh);
 
+/** @internal Get congestion management information. */
+typedef int (*eth_cman_info_get_t)(struct rte_eth_dev *dev,
+				struct rte_eth_cman_info *info);
+
+/** @internal Init congestion management structure with default values. */
+typedef int (*eth_cman_config_init_t)(struct rte_eth_dev *dev,
+				struct rte_eth_cman_config *config);
+
+/** @internal Configure congestion management on a port. */
+typedef int (*eth_cman_config_set_t)(struct rte_eth_dev *dev,
+				const struct rte_eth_cman_config *config);
+
+/** @internal Retrieve congestion management configuration of a port. */
+typedef int (*eth_cman_config_get_t)(struct rte_eth_dev *dev,
+				struct rte_eth_cman_config *config);
+
 /**
  * @internal A structure containing the functions exported by an Ethernet driver.
  */
@@ -1309,6 +1325,15 @@ struct eth_dev_ops {
 	eth_rx_queue_avail_thresh_set_t rx_queue_avail_thresh_set;
 	/** Query Rx queue available descriptors threshold event */
 	eth_rx_queue_avail_thresh_query_t rx_queue_avail_thresh_query;
+
+	/** Get congestion management information */
+	eth_cman_info_get_t cman_info_get;
+	/** Initialize congestion management structure with default values */
+	eth_cman_config_init_t cman_config_init;
+	/** Configure congestion management */
+	eth_cman_config_set_t cman_config_set;
+	/** Retrieve congestion management configuration */
+	eth_cman_config_get_t cman_config_get;
 };
 
 /**
diff --git a/lib/ethdev/ethdev_private.c b/lib/ethdev/ethdev_private.c
index 48090c879a..8787c3985e 100644
--- a/lib/ethdev/ethdev_private.c
+++ b/lib/ethdev/ethdev_private.c
@@ -27,6 +27,18 @@ eth_dev_to_id(const struct rte_eth_dev *dev)
 	return dev - rte_eth_devices;
 }
 
+int32_t
+eth_check_err(struct rte_eth_dev *dev, int ret)
+{
+	if (ret == 0)
+		return 0;
+
+	if (rte_eth_dev_is_removed(eth_dev_to_id(dev)))
+		return -EIO;
+
+	return ret;
+}
+
 struct rte_eth_dev *
 eth_find_device(const struct rte_eth_dev *start, rte_eth_cmp_t cmp,
 		const void *data)
diff --git a/lib/ethdev/ethdev_private.h b/lib/ethdev/ethdev_private.h
index cc9879907c..c7ac074029 100644
--- a/lib/ethdev/ethdev_private.h
+++ b/lib/ethdev/ethdev_private.h
@@ -69,4 +69,6 @@ void eth_dev_txq_release(struct rte_eth_dev *dev, uint16_t qid);
 int eth_dev_rx_queue_config(struct rte_eth_dev *dev, uint16_t nb_queues);
 int eth_dev_tx_queue_config(struct rte_eth_dev *dev, uint16_t nb_queues);
 
+int32_t eth_check_err(struct rte_eth_dev *dev, int ret);
+
 #endif /* _ETH_PRIVATE_H_ */
diff --git a/lib/ethdev/meson.build b/lib/ethdev/meson.build
index 47bb2625b0..39250b5da1 100644
--- a/lib/ethdev/meson.build
+++ b/lib/ethdev/meson.build
@@ -8,6 +8,7 @@ sources = files(
         'ethdev_trace_points.c',
         'rte_class_eth.c',
         'rte_ethdev.c',
+        'rte_ethdev_cman.c',
         'rte_flow.c',
         'rte_mtr.c',
         'rte_tm.c',
@@ -19,6 +20,7 @@ sources = files(
 )
 
 headers = files(
+        'rte_cman.h',
         'rte_ethdev.h',
         'rte_ethdev_trace.h',
         'rte_ethdev_trace_fp.h',
diff --git a/lib/ethdev/rte_cman.h b/lib/ethdev/rte_cman.h
new file mode 100644
index 0000000000..297db8e095
--- /dev/null
+++ b/lib/ethdev/rte_cman.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2022 Marvell International Ltd.
+ */
+
+#ifndef RTE_CMAN_H
+#define RTE_CMAN_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_bitops.h>
+
+/**
+ * @file
+ * Congestion management related parameters for DPDK.
+ */
+
+/** Congestion management modes */
+enum rte_cman_mode {
+	/**
+	 * Congestion based on Random Early Detection.
+	 *
+	 * https://en.wikipedia.org/wiki/Random_early_detection
+	 * http://www.aciri.org/floyd/papers/red/red.html
+	 * @see struct rte_cman_red_params
+	 */
+	RTE_CMAN_RED = RTE_BIT32(0),
+};
+
+/**
+ * RED based congestion management configuration parameters.
+ */
+struct rte_cman_red_params {
+	/**
+	 * Minimum threshold (min_th) value
+	 *
+	 * Value expressed as percentage. Value must be in 0 to 100(inclusive).
+	 */
+	uint8_t min_th;
+	/**
+	 * Maximum threshold (max_th) value
+	 *
+	 * Value expressed as percentage. Value must be in 0 to 100(inclusive).
+	 */
+	uint8_t max_th;
+	/** Inverse of packet marking probability maximum value (maxp = 1 / maxp_inv) */
+	uint16_t maxp_inv;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* RTE_CMAN_H */
diff --git a/lib/ethdev/rte_ethdev.h b/lib/ethdev/rte_ethdev.h
index 12535c703e..719b5c2707 100644
--- a/lib/ethdev/rte_ethdev.h
+++ b/lib/ethdev/rte_ethdev.h
@@ -160,6 +160,7 @@ extern "C" {
 #define RTE_ETHDEV_DEBUG_TX
 #endif
 
+#include <rte_cman.h>
 #include <rte_compat.h>
 #include <rte_log.h>
 #include <rte_interrupts.h>
@@ -5314,6 +5315,169 @@ typedef struct {
 __rte_experimental
 int rte_eth_dev_priv_dump(uint16_t port_id, FILE *file);
 
+/* Congestion management */
+
+/** Enumerate list of ethdev congestion management objects */
+enum rte_eth_cman_obj {
+	/** Congestion management based on Rx queue depth */
+	RTE_ETH_CMAN_OBJ_RX_QUEUE = RTE_BIT32(0),
+	/**
+	 * Congestion management based on mempool depth associated with Rx queue
+	 * @see rte_eth_rx_queue_setup()
+	 */
+	RTE_ETH_CMAN_OBJ_RX_QUEUE_MEMPOOL = RTE_BIT32(1),
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this structure may change, or be removed, without prior notice
+ *
+ * A structure used to retrieve information of ethdev congestion management.
+ */
+struct rte_eth_cman_info {
+	/**
+	 * Set of supported congestion management modes
+	 * @see enum rte_cman_mode
+	 */
+	uint64_t modes_supported;
+	/**
+	 * Set of supported congestion management objects
+	 * @see enum rte_eth_cman_obj
+	 */
+	uint64_t objs_supported;
+	/**
+	 * Reserved for future fields. Always returned as 0 when
+	 * rte_eth_cman_info_get() is invoked
+	 */
+	uint8_t rsvd[8];
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this structure may change, or be removed, without prior notice
+ *
+ * A structure used to configure the ethdev congestion management.
+ */
+struct rte_eth_cman_config {
+	/** Congestion management object */
+	enum rte_eth_cman_obj obj;
+	/** Congestion management mode */
+	enum rte_cman_mode mode;
+	union {
+		/**
+		 * Rx queue to configure congestion management.
+		 *
+		 * Valid when object is RTE_ETH_CMAN_OBJ_RX_QUEUE or
+		 * RTE_ETH_CMAN_OBJ_RX_QUEUE_MEMPOOL.
+		 */
+		uint16_t rx_queue;
+		/**
+		 * Reserved for future fields.
+		 * It must be set to 0 when rte_eth_cman_config_set() is invoked
+		 * and will be returned as 0 when rte_eth_cman_config_get() is
+		 * invoked.
+		 */
+		uint8_t rsvd_obj_params[4];
+	} obj_param;
+	union {
+		/**
+		 * RED configuration parameters.
+		 *
+		 * Valid when mode is RTE_CMAN_RED.
+		 */
+		struct rte_cman_red_params red;
+		/**
+		 * Reserved for future fields.
+		 * It must be set to 0 when rte_eth_cman_config_set() is invoked
+		 * and will be returned as 0 when rte_eth_cman_config_get() is
+		 * invoked.
+		 */
+		uint8_t rsvd_mode_params[4];
+	} mode_param;
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
+ * Retrieve the information for ethdev congestion management
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param info
+ *   A pointer to a structure of type *rte_eth_cman_info* to be filled with
+ *   the information about congestion management.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if support for cman_info_get does not exist.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - (-EINVAL) if bad parameter.
+ */
+__rte_experimental
+int rte_eth_cman_info_get(uint16_t port_id, struct rte_eth_cman_info *info);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
+ * Initialize the ethdev congestion management configuration structure with default values.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param config
+ *   A pointer to a structure of type *rte_eth_cman_config* to be initialized
+ *   with default value.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if support for cman_config_init does not exist.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - (-EINVAL) if bad parameter.
+ */
+__rte_experimental
+int rte_eth_cman_config_init(uint16_t port_id, struct rte_eth_cman_config *config);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
+ * Configure ethdev congestion management
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param config
+ *   A pointer to a structure of type *rte_eth_cman_config* to be configured.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if support for cman_config_set does not exist.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - (-EINVAL) if bad parameter.
+ */
+__rte_experimental
+int rte_eth_cman_config_set(uint16_t port_id, const struct rte_eth_cman_config *config);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
+ * Retrieve the applied ethdev congestion management parameters for the given port.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param config
+ *   A pointer to a structure of type *rte_eth_cman_config* to retrieve
+ *   congestion management parameters for the given object.
+ *   Application must fill all parameters except mode_param parameter in
+ *   struct rte_eth_cman_config.
+ *
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if support for cman_config_get does not exist.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - (-EINVAL) if bad parameter.
+ */
+__rte_experimental
+int rte_eth_cman_config_get(uint16_t port_id, struct rte_eth_cman_config *config);
+
 #include <rte_ethdev_core.h>
 
 /**
diff --git a/lib/ethdev/rte_ethdev_cman.c b/lib/ethdev/rte_ethdev_cman.c
new file mode 100644
index 0000000000..2ad39d7ed4
--- /dev/null
+++ b/lib/ethdev/rte_ethdev_cman.c
@@ -0,0 +1,101 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2022 Marvell International Ltd.
+ */
+
+#include <stdint.h>
+
+#include <rte_errno.h>
+#include "rte_ethdev.h"
+#include "ethdev_driver.h"
+#include "ethdev_private.h"
+
+/* Get congestion management information for a port */
+int
+rte_eth_cman_info_get(uint16_t port_id, struct rte_eth_cman_info *info)
+{
+	struct rte_eth_dev *dev;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	if (info == NULL) {
+		RTE_ETHDEV_LOG(ERR, "congestion management info is NULL\n");
+		return -EINVAL;
+	}
+
+	if (dev->dev_ops->cman_info_get == NULL) {
+		RTE_ETHDEV_LOG(ERR, "Function not implemented\n");
+		return -ENOTSUP;
+	}
+
+	memset(info, 0, sizeof(struct rte_eth_cman_info));
+	return eth_check_err(dev, (*dev->dev_ops->cman_info_get)(dev, info));
+}
+
+/* Initialize congestion management structure with default values */
+int
+rte_eth_cman_config_init(uint16_t port_id, struct rte_eth_cman_config *config)
+{
+	struct rte_eth_dev *dev;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	if (config == NULL) {
+		RTE_ETHDEV_LOG(ERR, "congestion management config is NULL\n");
+		return -EINVAL;
+	}
+
+	if (dev->dev_ops->cman_config_init == NULL) {
+		RTE_ETHDEV_LOG(ERR, "Function not implemented\n");
+		return -ENOTSUP;
+	}
+
+	memset(config, 0, sizeof(struct rte_eth_cman_config));
+	return eth_check_err(dev, (*dev->dev_ops->cman_config_init)(dev, config));
+}
+
+/* Configure congestion management on a port */
+int
+rte_eth_cman_config_set(uint16_t port_id, const struct rte_eth_cman_config *config)
+{
+	struct rte_eth_dev *dev;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	if (config == NULL) {
+		RTE_ETHDEV_LOG(ERR, "congestion management config is NULL\n");
+		return -EINVAL;
+	}
+
+	if (dev->dev_ops->cman_config_set == NULL) {
+		RTE_ETHDEV_LOG(ERR, "Function not implemented\n");
+		return -ENOTSUP;
+	}
+
+	return eth_check_err(dev, (*dev->dev_ops->cman_config_set)(dev, config));
+}
+
+/* Retrieve congestion management configuration of a port */
+int
+rte_eth_cman_config_get(uint16_t port_id, struct rte_eth_cman_config *config)
+{
+	struct rte_eth_dev *dev;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	if (config == NULL) {
+		RTE_ETHDEV_LOG(ERR, "congestion management config is NULL\n");
+		return -EINVAL;
+	}
+
+	if (dev->dev_ops->cman_config_get == NULL) {
+		RTE_ETHDEV_LOG(ERR, "Function not implemented\n");
+		return -ENOTSUP;
+	}
+
+	memset(config, 0, sizeof(struct rte_eth_cman_config));
+	return eth_check_err(dev, (*dev->dev_ops->cman_config_get)(dev, config));
+}
diff --git a/lib/ethdev/version.map b/lib/ethdev/version.map
index 25e54f9d3e..a974ce7140 100644
--- a/lib/ethdev/version.map
+++ b/lib/ethdev/version.map
@@ -285,6 +285,12 @@ EXPERIMENTAL {
 	rte_mtr_color_in_protocol_priority_get;
 	rte_mtr_color_in_protocol_set;
 	rte_mtr_meter_vlan_table_update;
+
+	# added in 22.11
+	rte_eth_cman_config_get;
+	rte_eth_cman_config_init;
+	rte_eth_cman_config_set;
+	rte_eth_cman_info_get;
 };
 
 INTERNAL {
-- 
2.25.1


  parent reply	other threads:[~2022-09-29  9:37 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-05-30 13:15 [dpdk-dev] [RFC PATCH] " jerinj
2022-05-30 19:43 ` Ajit Khaparde
2022-07-13 12:11   ` Jerin Jacob
2022-05-31  1:09 ` Min Hu (Connor)
2022-07-13 12:16   ` Jerin Jacob
2022-05-31 16:09 ` Stephen Hemminger
2022-07-13 12:25   ` Jerin Jacob
2022-07-13 13:03 ` [dpdk-dev] [PATCH v1] " jerinj
2022-09-19 12:15   ` [PATCH v2 1/1] " skori
2022-09-27 14:36     ` Thomas Monjalon
2022-09-27 15:09       ` Bruce Richardson
2022-09-28 11:14         ` Jerin Jacob
2022-09-28 12:08           ` Thomas Monjalon
2022-09-28 12:23             ` Jerin Jacob
2022-09-28 13:07               ` Olivier Matz
2022-09-28  8:19     ` Andrew Rybchenko
2022-09-28 12:20       ` Jerin Jacob
2022-09-29  9:35     ` skori [this message]
2022-10-04  9:02       ` [PATCH v3 " Andrew Rybchenko
2022-10-04  9:04         ` Andrew Rybchenko

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=20220929093503.2172344-1-skori@marvell.com \
    --to=skori@marvell.com \
    --cc=andrew.rybchenko@oktetlabs.ru \
    --cc=dev@dpdk.org \
    --cc=ferruh.yigit@amd.com \
    --cc=jerinj@marvell.com \
    --cc=mdr@ashroe.eu \
    --cc=thomas@monjalon.net \
    /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).