All of lore.kernel.org
 help / color / mirror / Atom feed
From: Shreyansh Jain <shreyansh.jain@nxp.com>
To: "dev@dpdk.org" <dev@dpdk.org>
Cc: "ferruh.yigit@intel.com" <ferruh.yigit@intel.com>,
	Nipun Gupta <nipun.gupta@nxp.com>
Subject: [PATCH v2 15/20] net/dpaa2: add dpdmux initialization and configuration
Date: Fri, 11 Jan 2019 11:58:40 +0000	[thread overview]
Message-ID: <20190111115712.6482-16-shreyansh.jain@nxp.com> (raw)
In-Reply-To: <20190111115712.6482-1-shreyansh.jain@nxp.com>

From: Nipun Gupta <nipun.gupta@nxp.com>

This patch introduces an rte pmd API to configure dpdmux from
the application.
dpdmux can work in association with dpni as an additional
distribution capability on the NIC.

Signed-off-by: Nipun Gupta <nipun.gupta@nxp.com>
---
 drivers/net/dpaa2/Makefile                  |   1 +
 drivers/net/dpaa2/dpaa2_ethdev.h            |   2 +
 drivers/net/dpaa2/dpaa2_mux.c               | 222 ++++++++++++++++++++
 drivers/net/dpaa2/meson.build               |   1 +
 drivers/net/dpaa2/rte_pmd_dpaa2.h           |  23 ++
 drivers/net/dpaa2/rte_pmd_dpaa2_version.map |   1 +
 6 files changed, 250 insertions(+)
 create mode 100644 drivers/net/dpaa2/dpaa2_mux.c

diff --git a/drivers/net/dpaa2/Makefile b/drivers/net/dpaa2/Makefile
index c58a39725..562551175 100644
--- a/drivers/net/dpaa2/Makefile
+++ b/drivers/net/dpaa2/Makefile
@@ -33,6 +33,7 @@ CFLAGS += -DALLOW_EXPERIMENTAL_API
 SRCS-$(CONFIG_RTE_LIBRTE_DPAA2_PMD) += base/dpaa2_hw_dpni.c
 SRCS-$(CONFIG_RTE_LIBRTE_DPAA2_PMD) += dpaa2_rxtx.c
 SRCS-$(CONFIG_RTE_LIBRTE_DPAA2_PMD) += dpaa2_ethdev.c
+SRCS-$(CONFIG_RTE_LIBRTE_DPAA2_PMD) += dpaa2_mux.c
 SRCS-$(CONFIG_RTE_LIBRTE_DPAA2_PMD) += mc/dpni.c
 SRCS-$(CONFIG_RTE_LIBRTE_DPAA2_PMD) += mc/dpkg.c
 SRCS-$(CONFIG_RTE_LIBRTE_DPAA2_PMD) += mc/dpdmux.c
diff --git a/drivers/net/dpaa2/dpaa2_ethdev.h b/drivers/net/dpaa2/dpaa2_ethdev.h
index 7cf6e4191..420ad6446 100644
--- a/drivers/net/dpaa2/dpaa2_ethdev.h
+++ b/drivers/net/dpaa2/dpaa2_ethdev.h
@@ -11,6 +11,8 @@
 #include <rte_event_eth_rx_adapter.h>
 #include <rte_pmd_dpaa2.h>
 
+#include <dpaa2_hw_pvt.h>
+
 #include <mc/fsl_dpni.h>
 #include <mc/fsl_mc_sys.h>
 
diff --git a/drivers/net/dpaa2/dpaa2_mux.c b/drivers/net/dpaa2/dpaa2_mux.c
new file mode 100644
index 000000000..1d043dcdc
--- /dev/null
+++ b/drivers/net/dpaa2/dpaa2_mux.c
@@ -0,0 +1,222 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2018 NXP
+ */
+
+#include <sys/queue.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+
+#include <rte_ethdev.h>
+#include <rte_log.h>
+#include <rte_eth_ctrl.h>
+#include <rte_malloc.h>
+#include <rte_flow_driver.h>
+#include <rte_tailq.h>
+
+#include <rte_fslmc.h>
+#include <fsl_dpdmux.h>
+#include <fsl_dpkg.h>
+
+#include <dpaa2_ethdev.h>
+#include <dpaa2_pmd_logs.h>
+
+struct dpaa2_dpdmux_dev {
+	TAILQ_ENTRY(dpaa2_dpdmux_dev) next;
+		/**< Pointer to Next device instance */
+	struct fsl_mc_io dpdmux;  /** handle to DPDMUX portal object */
+	uint16_t token;
+	uint32_t dpdmux_id; /*HW ID for DPDMUX object */
+	uint8_t num_ifs;   /* Number of interfaces in DPDMUX */
+};
+
+struct rte_flow {
+	struct dpdmux_rule_cfg rule;
+};
+
+TAILQ_HEAD(dpdmux_dev_list, dpaa2_dpdmux_dev);
+static struct dpdmux_dev_list dpdmux_dev_list =
+	TAILQ_HEAD_INITIALIZER(dpdmux_dev_list); /*!< DPDMUX device list */
+
+static struct dpaa2_dpdmux_dev *get_dpdmux_from_id(uint32_t dpdmux_id)
+{
+	struct dpaa2_dpdmux_dev *dpdmux_dev = NULL;
+
+	/* Get DPBP dev handle from list using index */
+	TAILQ_FOREACH(dpdmux_dev, &dpdmux_dev_list, next) {
+		if (dpdmux_dev->dpdmux_id == dpdmux_id)
+			break;
+	}
+
+	return dpdmux_dev;
+}
+
+struct rte_flow *
+rte_pmd_dpaa2_mux_flow_create(uint32_t dpdmux_id,
+			      struct rte_flow_item *pattern[],
+			      struct rte_flow_action *actions[])
+{
+	struct dpaa2_dpdmux_dev *dpdmux_dev;
+	struct dpkg_profile_cfg kg_cfg;
+	const struct rte_flow_item_ipv4 *spec;
+	const struct rte_flow_action_vf *vf_conf;
+	struct dpdmux_cls_action dpdmux_action;
+	struct rte_flow *flow = NULL;
+	void *key_iova, *mask_iova, *key_cfg_iova = NULL;
+	int ret;
+
+	if (pattern[0]->type != RTE_FLOW_ITEM_TYPE_IPV4) {
+		DPAA2_PMD_ERR("Not supported pattern type: %d",
+			      pattern[0]->type);
+		return NULL;
+	}
+
+	/* Find the DPDMUX from dpdmux_id in our list */
+	dpdmux_dev = get_dpdmux_from_id(dpdmux_id);
+	if (!dpdmux_dev) {
+		DPAA2_PMD_ERR("Invalid dpdmux_id: %d", dpdmux_id);
+		return NULL;
+	}
+
+	key_cfg_iova = rte_zmalloc(NULL, DIST_PARAM_IOVA_SIZE,
+				   RTE_CACHE_LINE_SIZE);
+	if (!key_cfg_iova) {
+		DPAA2_PMD_ERR("Unable to allocate flow-dist parameters");
+		return NULL;
+	}
+
+	/* Currently taking only IP protocol as an extract type.
+	 * This can be exended to other fields using pattern->type.
+	 */
+	memset(&kg_cfg, 0, sizeof(struct dpkg_profile_cfg));
+	kg_cfg.extracts[0].extract.from_hdr.prot = NET_PROT_IP;
+	kg_cfg.extracts[0].extract.from_hdr.field = NH_FLD_IP_PROTO;
+	kg_cfg.extracts[0].type = DPKG_EXTRACT_FROM_HDR;
+	kg_cfg.extracts[0].extract.from_hdr.type = DPKG_FULL_FIELD;
+	kg_cfg.num_extracts = 1;
+
+	ret = dpkg_prepare_key_cfg(&kg_cfg, key_cfg_iova);
+	if (ret) {
+		DPAA2_PMD_ERR("dpkg_prepare_key_cfg failed: err(%d)", ret);
+		goto creation_error;
+	}
+
+	ret = dpdmux_set_custom_key(&dpdmux_dev->dpdmux, CMD_PRI_LOW,
+				    dpdmux_dev->token,
+			(uint64_t)(DPAA2_VADDR_TO_IOVA(key_cfg_iova)));
+	if (ret) {
+		DPAA2_PMD_ERR("dpdmux_set_custom_key failed: err(%d)", ret);
+		goto creation_error;
+	}
+
+	/* As now our key extract parameters are set, let us configure
+	 * the rule.
+	 */
+	flow = rte_zmalloc(NULL, sizeof(struct rte_flow) +
+			   (2 * DIST_PARAM_IOVA_SIZE), RTE_CACHE_LINE_SIZE);
+	if (!flow) {
+		DPAA2_PMD_ERR(
+			"Memory allocation failure for rule configration\n");
+		goto creation_error;
+	}
+	key_iova = (void *)((size_t)flow + sizeof(struct rte_flow));
+	mask_iova = (void *)((size_t)key_iova + DIST_PARAM_IOVA_SIZE);
+
+	spec = (const struct rte_flow_item_ipv4 *)pattern[0]->spec;
+	memcpy(key_iova, (const void *)&spec->hdr.next_proto_id,
+	       sizeof(uint8_t));
+	memcpy(mask_iova, pattern[0]->mask, sizeof(uint8_t));
+
+	flow->rule.key_iova = (uint64_t)(DPAA2_VADDR_TO_IOVA(key_iova));
+	flow->rule.mask_iova = (uint64_t)(DPAA2_VADDR_TO_IOVA(mask_iova));
+	flow->rule.key_size = sizeof(uint8_t);
+
+	vf_conf = (const struct rte_flow_action_vf *)(actions[0]->conf);
+	if (vf_conf->id == 0 || vf_conf->id > dpdmux_dev->num_ifs) {
+		DPAA2_PMD_ERR("Invalid destination id\n");
+		goto creation_error;
+	}
+	dpdmux_action.dest_if = vf_conf->id;
+
+	ret = dpdmux_add_custom_cls_entry(&dpdmux_dev->dpdmux, CMD_PRI_LOW,
+					  dpdmux_dev->token, &flow->rule,
+					  &dpdmux_action);
+	if (ret) {
+		DPAA2_PMD_ERR("dpdmux_add_custom_cls_entry failed: err(%d)",
+			      ret);
+		goto creation_error;
+	}
+
+	return flow;
+
+creation_error:
+	rte_free((void *)key_cfg_iova);
+	rte_free((void *)flow);
+	return NULL;
+}
+
+static int
+dpaa2_create_dpdmux_device(int vdev_fd __rte_unused,
+			   struct vfio_device_info *obj_info __rte_unused,
+			   int dpdmux_id)
+{
+	struct dpaa2_dpdmux_dev *dpdmux_dev;
+	struct dpdmux_attr attr;
+	int ret;
+
+	PMD_INIT_FUNC_TRACE();
+
+	/* Allocate DPAA2 dpdmux handle */
+	dpdmux_dev = rte_malloc(NULL, sizeof(struct dpaa2_dpdmux_dev), 0);
+	if (!dpdmux_dev) {
+		DPAA2_PMD_ERR("Memory allocation failed for DPDMUX Device");
+		return -1;
+	}
+
+	/* Open the dpdmux object */
+	dpdmux_dev->dpdmux.regs = rte_mcp_ptr_list[MC_PORTAL_INDEX];
+	ret = dpdmux_open(&dpdmux_dev->dpdmux, CMD_PRI_LOW, dpdmux_id,
+			  &dpdmux_dev->token);
+	if (ret) {
+		DPAA2_PMD_ERR("Unable to open dpdmux object: err(%d)", ret);
+		goto init_err;
+	}
+
+	ret = dpdmux_get_attributes(&dpdmux_dev->dpdmux, CMD_PRI_LOW,
+				    dpdmux_dev->token, &attr);
+	if (ret) {
+		DPAA2_PMD_ERR("Unable to get dpdmux attr: err(%d)", ret);
+		goto init_err;
+	}
+
+	ret = dpdmux_if_set_default(&dpdmux_dev->dpdmux, CMD_PRI_LOW,
+				    dpdmux_dev->token, 1);
+	if (ret) {
+		DPAA2_PMD_ERR("setting default interface failed in %s",
+			      __func__);
+		goto init_err;
+	}
+
+	dpdmux_dev->dpdmux_id = dpdmux_id;
+	dpdmux_dev->num_ifs = attr.num_ifs;
+
+	TAILQ_INSERT_TAIL(&dpdmux_dev_list, dpdmux_dev, next);
+
+	return 0;
+
+init_err:
+	if (dpdmux_dev)
+		rte_free(dpdmux_dev);
+
+	return -1;
+}
+
+static struct rte_dpaa2_object rte_dpaa2_dpdmux_obj = {
+	.dev_type = DPAA2_MUX,
+	.create = dpaa2_create_dpdmux_device,
+};
+
+RTE_PMD_REGISTER_DPAA2_OBJECT(dpdmux, rte_dpaa2_dpdmux_obj);
diff --git a/drivers/net/dpaa2/meson.build b/drivers/net/dpaa2/meson.build
index c7c2df417..801cbf5d7 100644
--- a/drivers/net/dpaa2/meson.build
+++ b/drivers/net/dpaa2/meson.build
@@ -9,6 +9,7 @@ endif
 
 deps += ['mempool_dpaa2']
 sources = files('base/dpaa2_hw_dpni.c',
+		'dpaa2_mux.c',
 		'dpaa2_ethdev.c',
 		'dpaa2_rxtx.c',
 		'mc/dpkg.c',
diff --git a/drivers/net/dpaa2/rte_pmd_dpaa2.h b/drivers/net/dpaa2/rte_pmd_dpaa2.h
index f9303acad..57de27f21 100644
--- a/drivers/net/dpaa2/rte_pmd_dpaa2.h
+++ b/drivers/net/dpaa2/rte_pmd_dpaa2.h
@@ -36,4 +36,27 @@ enum pmd_dpaa2_ts {
 __rte_experimental
 void rte_pmd_dpaa2_set_timestamp(enum pmd_dpaa2_ts);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
+ * Create a flow rule to demultiplex ethernet traffic to separate network
+ * interfaces.
+ *
+ * @param dpdmux_id
+ *    ID of the DPDMUX MC object.
+ * @param[in] pattern
+ *    Pattern specification.
+ * @param[in] actions
+ *    Associated actions.
+ *
+ * @return
+ *    A valid handle in case of success, NULL otherwise.
+ */
+__rte_experimental
+struct rte_flow *
+rte_pmd_dpaa2_mux_flow_create(uint32_t dpdmux_id,
+			      struct rte_flow_item *pattern[],
+			      struct rte_flow_action *actions[]);
+
 #endif /* _RTE_PMD_DPAA2_H */
diff --git a/drivers/net/dpaa2/rte_pmd_dpaa2_version.map b/drivers/net/dpaa2/rte_pmd_dpaa2_version.map
index de95a03cd..1661c5fb5 100644
--- a/drivers/net/dpaa2/rte_pmd_dpaa2_version.map
+++ b/drivers/net/dpaa2/rte_pmd_dpaa2_version.map
@@ -14,5 +14,6 @@ DPDK_17.11 {
 EXPERIMENTAL {
 	global:
 
+	rte_pmd_dpaa2_mux_flow_create;
 	rte_pmd_dpaa2_set_timestamp;
 } DPDK_17.11;
-- 
2.17.1

  parent reply	other threads:[~2019-01-11 11:58 UTC|newest]

Thread overview: 70+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-12-27  6:22 [PATCH 00/20] NXP DPAA2 fixes and enhancements Hemant Agrawal
2018-12-27  6:22 ` [PATCH 01/20] bus/fslmc: fix to reset portal memory before use Hemant Agrawal
2018-12-27  6:22 ` [PATCH 02/20] bus/fslmc: fix the ring mode to use correct cache settings Hemant Agrawal
2018-12-27  6:22 ` [PATCH 03/20] bus/fslmc: fix to use correct physical core for logical core Hemant Agrawal
2018-12-27  6:22 ` [PATCH 04/20] net/dpaa2: fix bad check for not-null Hemant Agrawal
2018-12-27  6:22 ` [PATCH 05/20] bus/fslmc: fix to convert error msg to warning Hemant Agrawal
2018-12-27  6:23 ` [PATCH 06/20] bus/fslmc: fix parse method for bus devices Hemant Agrawal
2018-12-27  6:23 ` [PATCH 07/20] net/dpaa2: fix device init for secondary process Hemant Agrawal
2018-12-27  6:23 ` [PATCH 08/20] net/dpaa2: enable optional timestamp in mbuf Hemant Agrawal
2018-12-27  6:23 ` [PATCH 09/20] bus/fslmc: upgrade to latest qbman library Hemant Agrawal
2018-12-27  6:23 ` [PATCH 10/20] bus/fslmc: add dynamic config for memback portal mode Hemant Agrawal
2018-12-27  6:23 ` [PATCH 11/20] bus/fslmc: rename portal pi index to consumer index Hemant Agrawal
2018-12-27  6:23 ` [PATCH 12/20] bus/fslmc: make portal func static Hemant Agrawal
2018-12-27  6:23 ` [PATCH 13/20] net/dpaa2: add dpdmux mc flib Hemant Agrawal
2018-12-27  6:23 ` [PATCH 14/20] bus/fslmc: add support for scanning DPDMUX object Hemant Agrawal
2018-12-27  6:23 ` [PATCH 15/20] net/dpaa2: add dpdmux initialization and configuration Hemant Agrawal
2018-12-27  6:23 ` [PATCH 16/20] net/dpaa2: add API to support custom hash key Hemant Agrawal
2018-12-27  6:23 ` [PATCH 17/20] mempool/dpaa2: support saving context of buffer pool Hemant Agrawal
2018-12-27  6:23 ` [PATCH 18/20] net/dpaa2: change ref of device to private device Hemant Agrawal
2018-12-27  6:23 ` [PATCH 19/20] bus/fslmc: add support for secondary processes Hemant Agrawal
2018-12-27  6:23 ` [PATCH 20/20] bus/fslmc: add function to map any addr via VFIO Hemant Agrawal
2019-01-08 14:10   ` Ferruh Yigit
2019-01-10  9:58     ` Shreyansh Jain
2019-01-11 11:58       ` Ferruh Yigit
2019-01-11 12:16         ` Shreyansh Jain
2019-01-11 11:57 ` [PATCH v2 00/20] NXP DPAA2 fixes and enhancements Shreyansh Jain
2019-01-11 11:57   ` [PATCH v2 01/20] bus/fslmc: fix to reset portal memory before use Shreyansh Jain
2019-01-11 11:57   ` [PATCH v2 02/20] bus/fslmc: fix the ring mode to use correct cache settings Shreyansh Jain
2019-01-11 11:57   ` [PATCH v2 03/20] bus/fslmc: fix to use correct physical core for logical core Shreyansh Jain
2019-01-11 11:58   ` [PATCH v2 04/20] net/dpaa2: fix bad check for not-null Shreyansh Jain
2019-01-11 11:58   ` [PATCH v2 05/20] bus/fslmc: fix to convert error msg to warning Shreyansh Jain
2019-01-11 11:58   ` [PATCH v2 06/20] bus/fslmc: fix parse method for bus devices Shreyansh Jain
2019-01-11 11:58   ` [PATCH v2 07/20] net/dpaa2: fix device init for secondary process Shreyansh Jain
2019-01-11 11:58   ` [PATCH v2 08/20] net/dpaa2: enable optional timestamp in mbuf Shreyansh Jain
2019-01-11 11:58   ` [PATCH v2 09/20] bus/fslmc: upgrade to latest qbman library Shreyansh Jain
2019-01-11 11:58   ` [PATCH v2 10/20] bus/fslmc: add dynamic config for memback portal mode Shreyansh Jain
2019-01-11 11:58   ` [PATCH v2 11/20] bus/fslmc: rename portal pi index to consumer index Shreyansh Jain
2019-01-11 11:58   ` [PATCH v2 12/20] bus/fslmc: make portal func static Shreyansh Jain
2019-01-11 11:58   ` [PATCH v2 13/20] net/dpaa2: add dpdmux mc flib Shreyansh Jain
2019-01-11 11:58   ` [PATCH v2 14/20] bus/fslmc: add support for scanning DPDMUX object Shreyansh Jain
2019-01-11 11:58   ` Shreyansh Jain [this message]
2019-01-11 11:58   ` [PATCH v2 16/20] net/dpaa2: add API to support custom hash key Shreyansh Jain
2019-01-11 11:58   ` [PATCH v2 17/20] mempool/dpaa2: support saving context of buffer pool Shreyansh Jain
2019-01-11 11:58   ` [PATCH v2 18/20] net/dpaa2: change reference to private device Shreyansh Jain
2019-01-11 11:58   ` [PATCH v2 19/20] bus/fslmc: add support for secondary processes Shreyansh Jain
2019-01-11 11:58   ` [PATCH v2 20/20] bus/fslmc: add function to map any addr via VFIO Shreyansh Jain
2019-01-11 12:24   ` [PATCH v3 00/20] NXP DPAA2 fixes and enhancements Shreyansh Jain
2019-01-11 12:24     ` [PATCH v3 01/19] bus/fslmc: fix to reset portal memory before use Shreyansh Jain
2019-01-11 12:24     ` [PATCH v3 02/19] bus/fslmc: fix the ring mode to use correct cache settings Shreyansh Jain
2019-01-11 12:24     ` [PATCH v3 03/19] bus/fslmc: fix to use correct physical core for logical core Shreyansh Jain
2019-01-11 12:24     ` [PATCH v3 04/19] net/dpaa2: fix bad check for not-null Shreyansh Jain
2019-01-11 12:24     ` [PATCH v3 05/19] bus/fslmc: fix to convert error msg to warning Shreyansh Jain
2019-01-11 12:24     ` [PATCH v3 06/19] bus/fslmc: fix parse method for bus devices Shreyansh Jain
2019-01-11 12:24     ` [PATCH v3 07/19] net/dpaa2: fix device init for secondary process Shreyansh Jain
2019-01-11 12:24     ` [PATCH v3 08/19] net/dpaa2: enable optional timestamp in mbuf Shreyansh Jain
2019-01-11 12:24     ` [PATCH v3 09/19] bus/fslmc: upgrade to latest qbman library Shreyansh Jain
2019-01-11 12:24     ` [PATCH v3 10/19] bus/fslmc: add dynamic config for memback portal mode Shreyansh Jain
2019-01-11 12:24     ` [PATCH v3 11/19] bus/fslmc: rename portal pi index to consumer index Shreyansh Jain
2019-01-11 12:24     ` [PATCH v3 12/19] bus/fslmc: make portal func static Shreyansh Jain
2019-01-11 12:24     ` [PATCH v3 13/19] net/dpaa2: add dpdmux mc flib Shreyansh Jain
2019-01-11 12:24     ` [PATCH v3 14/19] bus/fslmc: add support for scanning DPDMUX object Shreyansh Jain
2019-01-11 12:24     ` [PATCH v3 15/19] net/dpaa2: add dpdmux initialization and configuration Shreyansh Jain
2019-01-11 12:24     ` [PATCH v3 16/19] net/dpaa2: add API to support custom hash key Shreyansh Jain
2019-01-11 12:25     ` [PATCH v3 17/19] mempool/dpaa2: support saving context of buffer pool Shreyansh Jain
2019-01-11 12:25     ` [PATCH v3 18/19] net/dpaa2: change reference to private device Shreyansh Jain
2019-01-11 12:25     ` [PATCH v3 19/19] bus/fslmc: add support for secondary processes Shreyansh Jain
2019-01-11 15:51     ` [PATCH v3 00/20] NXP DPAA2 fixes and enhancements Ferruh Yigit
2019-01-11 16:12       ` Ferruh Yigit
2019-01-14  5:20         ` Shreyansh Jain
2019-01-14  5:19     ` Shreyansh Jain

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=20190111115712.6482-16-shreyansh.jain@nxp.com \
    --to=shreyansh.jain@nxp.com \
    --cc=dev@dpdk.org \
    --cc=ferruh.yigit@intel.com \
    --cc=nipun.gupta@nxp.com \
    /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.