DPDK patches and discussions
 help / color / mirror / Atom feed
From: Bernard Iremonger <bernard.iremonger@intel.com>
To: thomas.monjalon@6wind.com, dev@dpdk.org
Cc: Bernard Iremonger <bernard.iremonger@intel.com>
Subject: [dpdk-dev] [PATCH v4 1/7] net/ixgbe: move set VF functions from the ethdev
Date: Tue, 13 Dec 2016 11:40:35 +0000	[thread overview]
Message-ID: <1481629241-19500-2-git-send-email-bernard.iremonger@intel.com> (raw)
In-Reply-To: <1481550626-14539-1-git-send-email-bernard.iremonger@intel.com>

Move the following functions from eth_dev_ops to the ixgbe PMD and rename:

ixgbe_set_pool_rx_mode
ixgbe_set_pool_rx
ixgbe_set_pool_tx
ixgbe_set_pool_vlan_filter
ixgbe_set_vf_rate_limit

Rename the functions to the following:

rte_pmd_ixgbe_set_vf_rxmode
rte_pmd_ixgbe_set_vf_rx
rte_pmd_ixgbe_set_vf_tx
rte_pmd_ixgbe_set_vf_vlan_filter
rte_pmd_ixgbe_set_vf_rate_limit

Use public function internally

Signed-off-by: Bernard Iremonger <bernard.iremonger@intel.com>
---
 drivers/net/ixgbe/ixgbe_ethdev.c            | 266 +++++++++++++++++++++++++++-
 drivers/net/ixgbe/rte_pmd_ixgbe.h           | 104 +++++++++++
 drivers/net/ixgbe/rte_pmd_ixgbe_version.map |  10 ++
 3 files changed, 379 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ixgbe/ixgbe_ethdev.c b/drivers/net/ixgbe/ixgbe_ethdev.c
index edc9b22..37b82a4 100644
--- a/drivers/net/ixgbe/ixgbe_ethdev.c
+++ b/drivers/net/ixgbe/ixgbe_ethdev.c
@@ -2294,7 +2294,8 @@ ixgbe_dev_start(struct rte_eth_dev *dev)
 		for (vf = 0; vf < dev->pci_dev->max_vfs; vf++)
 			for (idx = 0; idx < IXGBE_MAX_QUEUE_NUM_PER_VF; idx++)
 				if (vfinfo[vf].tx_rate[idx] != 0)
-					ixgbe_set_vf_rate_limit(dev, vf,
+					rte_pmd_ixgbe_set_vf_rate_limit(
+						dev->data->port_id, vf,
 						vfinfo[vf].tx_rate[idx],
 						1 << idx);
 	}
@@ -4883,6 +4884,269 @@ rte_pmd_ixgbe_set_vf_vlan_stripq(uint8_t port, uint16_t vf, uint8_t on)
 	return 0;
 }
 
+int
+rte_pmd_ixgbe_set_vf_rxmode(uint8_t port, uint16_t vf, uint16_t rx_mask, uint8_t on)
+{
+	int val = 0;
+	struct rte_eth_dev *dev;
+	struct rte_eth_dev_info dev_info;
+	struct ixgbe_hw *hw;
+	uint32_t vmolr;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV);
+
+	dev = &rte_eth_devices[port];
+	rte_eth_dev_info_get(port, &dev_info);
+
+	if (strstr(dev_info.driver_name, "ixgbe_vf"))
+		return -ENOTSUP;
+
+	if (vf >= dev_info.max_vfs)
+		return -EINVAL;
+
+	if (on > 1)
+		return -EINVAL;
+
+	hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	vmolr = IXGBE_READ_REG(hw, IXGBE_VMOLR(vf));
+
+	if (hw->mac.type == ixgbe_mac_82598EB) {
+		PMD_INIT_LOG(ERR, "setting VF receive mode set should be done"
+			     " on 82599 hardware and newer");
+		return -ENOTSUP;
+	}
+	if (ixgbe_vmdq_mode_check(hw) < 0)
+		return -ENOTSUP;
+
+	val = ixgbe_convert_vm_rx_mask_to_val(rx_mask, val);
+
+	if (on)
+		vmolr |= val;
+	else
+		vmolr &= ~val;
+
+	IXGBE_WRITE_REG(hw, IXGBE_VMOLR(vf), vmolr);
+
+	return 0;
+}
+
+int
+rte_pmd_ixgbe_set_vf_rx(uint8_t port, uint16_t vf, uint8_t on)
+{
+	struct rte_eth_dev *dev;
+	struct rte_eth_dev_info dev_info;
+	uint32_t reg, addr;
+	uint32_t val;
+	const uint8_t bit1 = 0x1;
+	struct ixgbe_hw *hw;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV);
+
+	dev = &rte_eth_devices[port];
+	rte_eth_dev_info_get(port, &dev_info);
+
+	if (strstr(dev_info.driver_name, "ixgbe_vf"))
+		return -ENOTSUP;
+
+	if (vf >= dev_info.max_vfs)
+		return -EINVAL;
+
+	if (on > 1)
+		return -EINVAL;
+
+	hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+
+	if (ixgbe_vmdq_mode_check(hw) < 0)
+		return -ENOTSUP;
+
+	/* for vf >= 32, set bit in PFVFRE[1], otherwise PFVFRE[0] */
+	if (vf >= 32) {
+		addr = IXGBE_VFRE(1);
+		val = bit1 << (vf - 32);
+	} else {
+		addr = IXGBE_VFRE(0);
+		val = bit1 << vf;
+	}
+
+	reg = IXGBE_READ_REG(hw, addr);
+
+	if (on)
+		reg |= val;
+	else
+		reg &= ~val;
+
+	IXGBE_WRITE_REG(hw, addr, reg);
+
+	return 0;
+}
+
+int
+rte_pmd_ixgbe_set_vf_tx(uint8_t port, uint16_t vf, uint8_t on)
+{
+	struct rte_eth_dev *dev;
+	struct rte_eth_dev_info dev_info;
+	uint32_t reg, addr;
+	uint32_t val;
+	const uint8_t bit1 = 0x1;
+
+	struct ixgbe_hw *hw;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV);
+
+	dev = &rte_eth_devices[port];
+	rte_eth_dev_info_get(port, &dev_info);
+
+	if (strstr(dev_info.driver_name, "ixgbe_vf"))
+		return -ENOTSUP;
+
+	if (vf >= dev_info.max_vfs)
+		return -EINVAL;
+
+	if (on > 1)
+		return -EINVAL;
+
+	hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	if (ixgbe_vmdq_mode_check(hw) < 0)
+		return -ENOTSUP;
+
+	/* for vf >= 32, set bit in PFVFTE[1], otherwise PFVFTE[0] */
+	if (vf >= 32) {
+		addr = IXGBE_VFTE(1);
+		val = bit1 << (vf - 32);
+	} else {
+		addr = IXGBE_VFTE(0);
+		val = bit1 << vf;
+	}
+
+	reg = IXGBE_READ_REG(hw, addr);
+
+	if (on)
+		reg |= val;
+	else
+		reg &= ~val;
+
+	IXGBE_WRITE_REG(hw, addr, reg);
+
+	return 0;
+}
+
+int
+rte_pmd_ixgbe_set_vf_vlan_filter(uint8_t port, uint16_t vlan,
+			uint64_t vf_mask, uint8_t vlan_on)
+{
+	struct rte_eth_dev *dev;
+	struct rte_eth_dev_info dev_info;
+	int ret = 0;
+	uint16_t vf_idx;
+	struct ixgbe_hw *hw;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV);
+
+	dev = &rte_eth_devices[port];
+	rte_eth_dev_info_get(port, &dev_info);
+
+	if (strstr(dev_info.driver_name, "ixgbe_vf"))
+		return -ENOTSUP;
+
+	if ((vlan > ETHER_MAX_VLAN_ID) || (vf_mask == 0))
+		return -EINVAL;
+
+	hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	if (ixgbe_vmdq_mode_check(hw) < 0)
+		return -ENOTSUP;
+
+	for (vf_idx = 0; vf_idx < 64; vf_idx++) {
+		if (vf_mask & ((uint64_t)(1ULL << vf_idx))) {
+			ret = hw->mac.ops.set_vfta(hw, vlan, vf_idx,
+						   vlan_on, false);
+			if (ret < 0)
+				return ret;
+		}
+	}
+
+	return ret;
+}
+
+int rte_pmd_ixgbe_set_vf_rate_limit(uint8_t port, uint16_t vf,
+	uint16_t tx_rate, uint64_t q_msk)
+{
+	struct rte_eth_dev *dev;
+	struct rte_eth_dev_info dev_info;
+	struct ixgbe_hw *hw;
+	struct ixgbe_vf_info *vfinfo;
+	struct rte_eth_link link;
+	uint8_t  nb_q_per_pool;
+	uint32_t queue_stride;
+	uint32_t queue_idx, idx = 0, vf_idx;
+	uint32_t queue_end;
+	uint16_t total_rate = 0;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV);
+
+	dev = &rte_eth_devices[port];
+	rte_eth_dev_info_get(port, &dev_info);
+	rte_eth_link_get_nowait(port, &link);
+
+	if (strstr(dev_info.driver_name, "ixgbe_vf"))
+		return -ENOTSUP;
+
+	if (vf >= dev_info.max_vfs)
+		return -EINVAL;
+
+	if (tx_rate > link.link_speed)
+		return -EINVAL;
+
+	if (q_msk == 0)
+		return 0;
+
+	hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	vfinfo = *(IXGBE_DEV_PRIVATE_TO_P_VFDATA(dev->data->dev_private));
+	nb_q_per_pool = RTE_ETH_DEV_SRIOV(dev).nb_q_per_pool;
+	queue_stride = IXGBE_MAX_RX_QUEUE_NUM / RTE_ETH_DEV_SRIOV(dev).active;
+	queue_idx = vf * queue_stride;
+	queue_end = queue_idx + nb_q_per_pool - 1;
+	if (queue_end >= hw->mac.max_tx_queues)
+		return -EINVAL;
+
+	if (vfinfo) {
+		for (vf_idx = 0; vf_idx < dev->pci_dev->max_vfs; vf_idx++) {
+			if (vf_idx == vf)
+				continue;
+			for (idx = 0; idx < RTE_DIM(vfinfo[vf_idx].tx_rate);
+				idx++)
+				total_rate += vfinfo[vf_idx].tx_rate[idx];
+		}
+	} else {
+		return -EINVAL;
+	}
+
+	/* Store tx_rate for this vf. */
+	for (idx = 0; idx < nb_q_per_pool; idx++) {
+		if (((uint64_t)0x1 << idx) & q_msk) {
+			if (vfinfo[vf].tx_rate[idx] != tx_rate)
+				vfinfo[vf].tx_rate[idx] = tx_rate;
+			total_rate += tx_rate;
+		}
+	}
+
+	if (total_rate > dev->data->dev_link.link_speed) {
+		/* Reset stored TX rate of the VF if it causes exceed
+		 * link speed.
+		 */
+		memset(vfinfo[vf].tx_rate, 0, sizeof(vfinfo[vf].tx_rate));
+		return -EINVAL;
+	}
+
+	/* Set RTTBCNRC of each queue/pool for vf X  */
+	for (; queue_idx <= queue_end; queue_idx++) {
+		if (0x1 & q_msk)
+			ixgbe_set_queue_rate_limit(dev, queue_idx, tx_rate);
+		q_msk = q_msk >> 1;
+	}
+
+	return 0;
+}
+
 #define IXGBE_MRCTL_VPME  0x01 /* Virtual Pool Mirroring. */
 #define IXGBE_MRCTL_UPME  0x02 /* Uplink Port Mirroring. */
 #define IXGBE_MRCTL_DPME  0x04 /* Downlink Port Mirroring. */
diff --git a/drivers/net/ixgbe/rte_pmd_ixgbe.h b/drivers/net/ixgbe/rte_pmd_ixgbe.h
index c2fb826..4eb0c9a 100644
--- a/drivers/net/ixgbe/rte_pmd_ixgbe.h
+++ b/drivers/net/ixgbe/rte_pmd_ixgbe.h
@@ -183,6 +183,110 @@ int
 rte_pmd_ixgbe_set_vf_vlan_stripq(uint8_t port, uint16_t vf, uint8_t on);
 
 /**
+* Set RX L2 Filtering mode of a VF of an Ethernet device.
+*
+* @param port
+*   The port identifier of the Ethernet device.
+* @param vf
+*   VF id.
+* @param rx_mode
+*    The RX mode mask, which is one or more of accepting Untagged Packets,
+*    packets that match the PFUTA table, Broadcast and Multicast Promiscuous.
+*    ETH_VMDQ_ACCEPT_UNTAG,ETH_VMDQ_ACCEPT_HASH_UC,
+*    ETH_VMDQ_ACCEPT_BROADCAST and ETH_VMDQ_ACCEPT_MULTICAST will be used
+*    in rx_mode.
+* @param on
+*    1 - Enable a VF RX mode.
+*    0 - Disable a VF RX mode.
+* @return
+*   - (0) if successful.
+*   - (-ENOTSUP) if hardware doesn't support.
+*   - (-ENODEV) if *port_id* invalid.
+*   - (-EINVAL) if bad parameter.
+*/
+int
+rte_pmd_ixgbe_set_vf_rxmode(uint8_t port, uint16_t vf, uint16_t rx_mask, uint8_t on);
+
+/**
+* Enable or disable a VF traffic receive of an Ethernet device.
+*
+* @param port
+*   The port identifier of the Ethernet device.
+* @param vf
+*   VF id.
+* @param on
+*    1 - Enable a VF traffic receive.
+*    0 - Disable a VF traffic receive.
+* @return
+*   - (0) if successful.
+*   - (-ENOTSUP) if hardware doesn't support.
+*   - (-ENODEV) if *port_id* invalid.
+*   - (-EINVAL) if bad parameter.
+*/
+int
+rte_pmd_ixgbe_set_vf_rx(uint8_t port, uint16_t vf, uint8_t on);
+
+/**
+* Enable or disable a VF traffic transmit of the Ethernet device.
+*
+* @param port
+*   The port identifier of the Ethernet device.
+* @param vf
+*   VF id.
+* @param on
+*    1 - Enable a VF traffic transmit.
+*    0 - Disable a VF traffic transmit.
+* @return
+*   - (0) if successful.
+*   - (-ENODEV) if *port_id* invalid.
+*   - (-ENOTSUP) if hardware doesn't support.
+*   - (-EINVAL) if bad parameter.
+*/
+int
+rte_pmd_ixgbe_set_vf_tx(uint8_t port, uint16_t vf, uint8_t on);
+
+/**
+* Enable/Disable hardware VF VLAN filtering by an Ethernet device of
+* received VLAN packets tagged with a given VLAN Tag Identifier.
+*
+* @param port id
+*   The port identifier of the Ethernet device.
+* @param vlan_id
+*   The VLAN Tag Identifier whose filtering must be enabled or disabled.
+* @param vf_mask
+*    Bitmap listing which VFs participate in the VLAN filtering.
+* @param vlan_on
+*    1 - Enable VFs VLAN filtering.
+*    0 - Disable VFs VLAN filtering.
+* @return
+*   - (0) if successful.
+*   - (-ENOTSUP) if hardware doesn't support.
+*   - (-ENODEV) if *port_id* invalid.
+*   - (-EINVAL) if bad parameter.
+*/
+int
+rte_pmd_ixgbe_set_vf_vlan_filter(uint8_t port, uint16_t vlan, uint64_t vf_mask, uint8_t vlan_on);
+
+/**
+ * Set the rate limitation for a vf on an Ethernet device.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param vf
+ *   VF id.
+ * @param tx_rate
+ *   The tx rate allocated from the total link speed for this VF id.
+ * @param q_msk
+ *   The queue mask which need to set the rate.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support this feature.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - (-EINVAL) if bad parameter.
+ */
+int rte_pmd_ixgbe_set_vf_rate_limit(uint8_t port, uint16_t vf, uint16_t tx_rate, uint64_t q_msk);
+
+/**
  * Response sent back to ixgbe driver from user app after callback
  */
 enum rte_pmd_ixgbe_mb_event_rsp {
diff --git a/drivers/net/ixgbe/rte_pmd_ixgbe_version.map b/drivers/net/ixgbe/rte_pmd_ixgbe_version.map
index 92434f3..5252bc2 100644
--- a/drivers/net/ixgbe/rte_pmd_ixgbe_version.map
+++ b/drivers/net/ixgbe/rte_pmd_ixgbe_version.map
@@ -15,3 +15,13 @@ DPDK_16.11 {
 	rte_pmd_ixgbe_set_vf_vlan_insert;
 	rte_pmd_ixgbe_set_vf_vlan_stripq;
 } DPDK_2.0;
+
+DPDK_17.02 {
+	global:
+
+	rte_pmd_ixgbe_set_vf_rate_limit;
+	rte_pmd_ixgbe_set_vf_rx;
+	rte_pmd_ixgbe_set_vf_rxmode;
+	rte_pmd_ixgbe_set_vf_tx;
+	rte_pmd_ixgbe_set_vf_vlan_filter;
+} DPDK_16.11;
\ No newline at end of file
-- 
2.10.1

  parent reply	other threads:[~2016-12-13 11:40 UTC|newest]

Thread overview: 50+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-12-09 11:27 [dpdk-dev] [PATCH v1 0/5] net/ixgbe: move set VF functions Bernard Iremonger
2016-12-09 11:27 ` [dpdk-dev] [PATCH v1 1/5] net/ixgbe: move set VF functions from the ethdev Bernard Iremonger
2016-12-09 11:27 ` [dpdk-dev] [PATCH v1 2/5] app/testpmd: use ixgbe public functions Bernard Iremonger
2016-12-09 11:56   ` Ferruh Yigit
2016-12-09 12:08     ` Iremonger, Bernard
2016-12-09 11:27 ` [dpdk-dev] [PATCH v1 3/5] app/testpmd: add command for set VF VLAN filter Bernard Iremonger
2016-12-09 12:02   ` Ferruh Yigit
2016-12-09 12:10     ` Iremonger, Bernard
2016-12-09 11:27 ` [dpdk-dev] [PATCH v1 4/5] app/testpmd: add command for set VF receive Bernard Iremonger
2016-12-09 11:27 ` [dpdk-dev] [PATCH v1 5/5] app/testpmd: add command for set VF transmit Bernard Iremonger
2016-12-09 11:54 ` [dpdk-dev] [PATCH v1 0/5] net/ixgbe: move set VF functions Ferruh Yigit
2016-12-09 12:00   ` Iremonger, Bernard
2016-12-09 13:04     ` Thomas Monjalon
2016-12-09 13:23       ` Iremonger, Bernard
2016-12-09 17:25 ` [dpdk-dev] [PATCH v2 0/9] " Bernard Iremonger
2016-12-12 13:50   ` [dpdk-dev] [PATCH v3 " Bernard Iremonger
2016-12-13 11:40     ` [dpdk-dev] [PATCH v4 0/7] " Bernard Iremonger
2016-12-13 13:36       ` Ferruh Yigit
2016-12-13 13:46         ` Iremonger, Bernard
2016-12-13 11:40     ` Bernard Iremonger [this message]
2016-12-13 11:40     ` [dpdk-dev] [PATCH v4 2/7] app/testpmd: use ixgbe public functions Bernard Iremonger
2016-12-13 11:40     ` [dpdk-dev] [PATCH v4 3/7] app/testpmd: cleanup parameter checking Bernard Iremonger
2016-12-13 11:40     ` [dpdk-dev] [PATCH v4 4/7] examples/ethtool: use ixgbe public function Bernard Iremonger
2016-12-13 11:40     ` [dpdk-dev] [PATCH v4 5/7] net/ixgbe: remove static set VF functions Bernard Iremonger
2016-12-13 11:40     ` [dpdk-dev] [PATCH v4 6/7] librte_ether: remove the set VF API's Bernard Iremonger
2016-12-13 11:40     ` [dpdk-dev] [PATCH v4 7/7] doc: update release notes Bernard Iremonger
2016-12-12 13:50   ` [dpdk-dev] [PATCH v3 1/9] net/ixgbe: move set VF functions from the ethdev Bernard Iremonger
2016-12-12 13:50   ` [dpdk-dev] [PATCH v3 2/9] app/testpmd: use ixgbe public functions Bernard Iremonger
2016-12-12 13:50   ` [dpdk-dev] [PATCH v3 3/9] app/testpmd: add command for set VF VLAN filter Bernard Iremonger
2016-12-12 13:50   ` [dpdk-dev] [PATCH v3 4/9] app/testpmd: add command for set VF receive Bernard Iremonger
2016-12-12 13:50   ` [dpdk-dev] [PATCH v3 5/9] app/testpmd: add command for set VF transmit Bernard Iremonger
2016-12-12 13:50   ` [dpdk-dev] [PATCH v3 6/9] examples/ethtool: use ixgbe public function Bernard Iremonger
2016-12-12 16:25     ` Ferruh Yigit
2016-12-13  9:04       ` Iremonger, Bernard
2016-12-12 13:50   ` [dpdk-dev] [PATCH v3 7/9] net/ixgbe: remove static set VF functions Bernard Iremonger
2016-12-12 13:50   ` [dpdk-dev] [PATCH v3 8/9] librte_ether: remove the set VF API's Bernard Iremonger
2016-12-12 13:50   ` [dpdk-dev] [PATCH v3 9/9] doc: update release notes Bernard Iremonger
2016-12-12 15:51     ` Ferruh Yigit
2016-12-12 15:55       ` Iremonger, Bernard
2016-12-12 16:07         ` Ferruh Yigit
2016-12-09 17:25 ` [dpdk-dev] [PATCH v2 1/9] net/ixgbe: move set VF functions from the ethdev Bernard Iremonger
2016-12-09 17:25 ` [dpdk-dev] [PATCH v2 2/9] app/testpmd: use ixgbe public functions Bernard Iremonger
2016-12-09 17:25 ` [dpdk-dev] [PATCH v2 3/9] app/testpmd: add command for set VF VLAN filter Bernard Iremonger
2016-12-09 17:25 ` [dpdk-dev] [PATCH v2 4/9] app/testpmd: add command for set VF receive Bernard Iremonger
2016-12-09 17:25 ` [dpdk-dev] [PATCH v2 5/9] app/testpmd: add command for set VF transmit Bernard Iremonger
2016-12-09 17:25 ` [dpdk-dev] [PATCH v2 6/9] examples/ethtool: use ixgbe public function Bernard Iremonger
2016-12-09 17:25 ` [dpdk-dev] [PATCH v2 7/9] net/ixgbe: remove static set VF functions Bernard Iremonger
2016-12-09 17:26 ` [dpdk-dev] [PATCH v2 8/9] librte_ether: remove the set VF API's Bernard Iremonger
2016-12-09 18:00   ` Ferruh Yigit
2016-12-09 17:26 ` [dpdk-dev] [PATCH v2 9/9] doc: remove deprecation notice Bernard Iremonger

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=1481629241-19500-2-git-send-email-bernard.iremonger@intel.com \
    --to=bernard.iremonger@intel.com \
    --cc=dev@dpdk.org \
    --cc=thomas.monjalon@6wind.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 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).