All of lore.kernel.org
 help / color / mirror / Atom feed
From: Xueming Li <xuemingl@nvidia.com>
To: Viacheslav Ovsiienko <viacheslavo@nvidia.com>
Cc: dev@dpdk.org, Matan Azrad <matan@nvidia.com>,
	Shahaf Shuler <shahafs@nvidia.com>,
	Thomas Monjalon <thomas@monjalon.net>,
	xuemingl@nvidia.com, Asaf Penso <asafp@nvidia.com>
Subject: [dpdk-dev] [PATCH v3 9/9] net/mlx5: improve bonding xstats
Date: Mon, 18 Jan 2021 11:29:13 +0000	[thread overview]
Message-ID: <1610969353-31938-10-git-send-email-xuemingl@nvidia.com> (raw)
In-Reply-To: <1610969353-31938-1-git-send-email-xuemingl@nvidia.com>
In-Reply-To: <1608304614-13908-2-git-send-email-xuemingl@nvidia.com>

In case of kernel bonding device, counter was read from first bonding PF
member.

This patch reads all member PFs and sums to get bond xstats.

Signed-off-by: Xueming Li <xuemingl@nvidia.com>
Acked-by: Viacheslav Ovsiienko <viacheslavo@nvidia.com>
---
 drivers/net/mlx5/linux/mlx5_ethdev_os.c | 127 +++++++++++++++++++-----
 1 file changed, 102 insertions(+), 25 deletions(-)

diff --git a/drivers/net/mlx5/linux/mlx5_ethdev_os.c b/drivers/net/mlx5/linux/mlx5_ethdev_os.c
index 84610a7bc0..27afb74aff 100644
--- a/drivers/net/mlx5/linux/mlx5_ethdev_os.c
+++ b/drivers/net/mlx5/linux/mlx5_ethdev_os.c
@@ -169,10 +169,10 @@ mlx5_get_ifname(const struct rte_eth_dev *dev, char (*ifname)[MLX5_NAMESIZE])
 }
 
 /**
- * Perform ifreq ioctl() on associated Ethernet device.
+ * Perform ifreq ioctl() on associated netdev ifname.
  *
- * @param[in] dev
- *   Pointer to Ethernet device.
+ * @param[in] ifname
+ *   Pointer to netdev name.
  * @param req
  *   Request number to pass to ioctl().
  * @param[out] ifr
@@ -182,7 +182,7 @@ mlx5_get_ifname(const struct rte_eth_dev *dev, char (*ifname)[MLX5_NAMESIZE])
  *   0 on success, a negative errno value otherwise and rte_errno is set.
  */
 static int
-mlx5_ifreq(const struct rte_eth_dev *dev, int req, struct ifreq *ifr)
+mlx5_ifreq_by_ifname(const char *ifname, int req, struct ifreq *ifr)
 {
 	int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
 	int ret = 0;
@@ -191,9 +191,7 @@ mlx5_ifreq(const struct rte_eth_dev *dev, int req, struct ifreq *ifr)
 		rte_errno = errno;
 		return -rte_errno;
 	}
-	ret = mlx5_get_ifname(dev, &ifr->ifr_name);
-	if (ret)
-		goto error;
+	rte_strscpy(ifr->ifr_name, ifname, sizeof(ifr->ifr_name));
 	ret = ioctl(sock, req, ifr);
 	if (ret == -1) {
 		rte_errno = errno;
@@ -206,6 +204,31 @@ mlx5_ifreq(const struct rte_eth_dev *dev, int req, struct ifreq *ifr)
 	return -rte_errno;
 }
 
+/**
+ * Perform ifreq ioctl() on associated Ethernet device.
+ *
+ * @param[in] dev
+ *   Pointer to Ethernet device.
+ * @param req
+ *   Request number to pass to ioctl().
+ * @param[out] ifr
+ *   Interface request structure output buffer.
+ *
+ * @return
+ *   0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+static int
+mlx5_ifreq(const struct rte_eth_dev *dev, int req, struct ifreq *ifr)
+{
+	char ifname[sizeof(ifr->ifr_name)];
+	int ret;
+
+	ret = mlx5_get_ifname(dev, &ifname);
+	if (ret)
+		return -rte_errno;
+	return mlx5_ifreq_by_ifname(ifname, req, ifr);
+}
+
 /**
  * Get device MTU.
  *
@@ -1243,6 +1266,8 @@ int mlx5_get_module_eeprom(struct rte_eth_dev *dev,
  *
  * @param dev
  *   Pointer to Ethernet device.
+ * @param[in] pf
+ *   PF index in case of bonding device, -1 otherwise
  * @param[out] stats
  *   Counters table output buffer.
  *
@@ -1250,8 +1275,8 @@ int mlx5_get_module_eeprom(struct rte_eth_dev *dev,
  *   0 on success and stats is filled, negative errno value otherwise and
  *   rte_errno is set.
  */
-int
-mlx5_os_read_dev_counters(struct rte_eth_dev *dev, uint64_t *stats)
+static int
+_mlx5_os_read_dev_counters(struct rte_eth_dev *dev, int pf, uint64_t *stats)
 {
 	struct mlx5_priv *priv = dev->data->dev_private;
 	struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
@@ -1265,7 +1290,11 @@ mlx5_os_read_dev_counters(struct rte_eth_dev *dev, uint64_t *stats)
 	et_stats->cmd = ETHTOOL_GSTATS;
 	et_stats->n_stats = xstats_ctrl->stats_n;
 	ifr.ifr_data = (caddr_t)et_stats;
-	ret = mlx5_ifreq(dev, SIOCETHTOOL, &ifr);
+	if (pf >= 0)
+		ret = mlx5_ifreq_by_ifname(priv->sh->bond.ports[pf].ifname,
+					   SIOCETHTOOL, &ifr);
+	else
+		ret = mlx5_ifreq(dev, SIOCETHTOOL, &ifr);
 	if (ret) {
 		DRV_LOG(WARNING,
 			"port %u unable to read statistic values from device",
@@ -1273,23 +1302,60 @@ mlx5_os_read_dev_counters(struct rte_eth_dev *dev, uint64_t *stats)
 		return ret;
 	}
 	for (i = 0; i != xstats_ctrl->mlx5_stats_n; ++i) {
-		if (xstats_ctrl->info[i].dev) {
-			ret = mlx5_os_read_dev_stat(priv,
-					    xstats_ctrl->info[i].ctr_name,
-					    &stats[i]);
-			/* return last xstats counter if fail to read. */
-			if (ret == 0)
-				xstats_ctrl->xstats[i] = stats[i];
-			else
-				stats[i] = xstats_ctrl->xstats[i];
-		} else {
-			stats[i] = (uint64_t)
-				et_stats->data[xstats_ctrl->dev_table_idx[i]];
-		}
+		if (xstats_ctrl->info[i].dev)
+			continue;
+		stats[i] += (uint64_t)
+			    et_stats->data[xstats_ctrl->dev_table_idx[i]];
 	}
 	return 0;
 }
 
+/**
+ * Read device counters.
+ *
+ * @param dev
+ *   Pointer to Ethernet device.
+ * @param[out] stats
+ *   Counters table output buffer.
+ *
+ * @return
+ *   0 on success and stats is filled, negative errno value otherwise and
+ *   rte_errno is set.
+ */
+int
+mlx5_os_read_dev_counters(struct rte_eth_dev *dev, uint64_t *stats)
+{
+	struct mlx5_priv *priv = dev->data->dev_private;
+	struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
+	int ret = 0, i;
+
+	memset(stats, 0, sizeof(*stats) * xstats_ctrl->mlx5_stats_n);
+	/* Read ifreq counters. */
+	if (priv->master && priv->pf_bond >= 0) {
+		/* Sum xstats from bonding device member ports. */
+		for (i = 0; i < priv->sh->bond.n_port; i++) {
+			ret = _mlx5_os_read_dev_counters(dev, i, stats);
+			if (ret)
+				return ret;
+		}
+	} else {
+		ret = _mlx5_os_read_dev_counters(dev, -1, stats);
+	}
+	/* Read IB counters. */
+	for (i = 0; i != xstats_ctrl->mlx5_stats_n; ++i) {
+		if (!xstats_ctrl->info[i].dev)
+			continue;
+		ret = mlx5_os_read_dev_stat(priv, xstats_ctrl->info[i].ctr_name,
+					    &stats[i]);
+		/* return last xstats counter if fail to read. */
+		if (ret != 0)
+			xstats_ctrl->xstats[i] = stats[i];
+		else
+			stats[i] = xstats_ctrl->xstats[i];
+	}
+	return ret;
+}
+
 /**
  * Query the number of statistics provided by ETHTOOL.
  *
@@ -1303,13 +1369,19 @@ mlx5_os_read_dev_counters(struct rte_eth_dev *dev, uint64_t *stats)
 int
 mlx5_os_get_stats_n(struct rte_eth_dev *dev)
 {
+	struct mlx5_priv *priv = dev->data->dev_private;
 	struct ethtool_drvinfo drvinfo;
 	struct ifreq ifr;
 	int ret;
 
 	drvinfo.cmd = ETHTOOL_GDRVINFO;
 	ifr.ifr_data = (caddr_t)&drvinfo;
-	ret = mlx5_ifreq(dev, SIOCETHTOOL, &ifr);
+	if (priv->master && priv->pf_bond >= 0)
+		/* Bonding PF. */
+		ret = mlx5_ifreq_by_ifname(priv->sh->bond.ports[0].ifname,
+					   SIOCETHTOOL, &ifr);
+	else
+		ret = mlx5_ifreq(dev, SIOCETHTOOL, &ifr);
 	if (ret) {
 		DRV_LOG(WARNING, "port %u unable to query number of statistics",
 			dev->data->port_id);
@@ -1480,7 +1552,12 @@ mlx5_os_stats_init(struct rte_eth_dev *dev)
 	strings->string_set = ETH_SS_STATS;
 	strings->len = dev_stats_n;
 	ifr.ifr_data = (caddr_t)strings;
-	ret = mlx5_ifreq(dev, SIOCETHTOOL, &ifr);
+	if (priv->master && priv->pf_bond >= 0)
+		/* Bonding master. */
+		ret = mlx5_ifreq_by_ifname(priv->sh->bond.ports[0].ifname,
+					   SIOCETHTOOL, &ifr);
+	else
+		ret = mlx5_ifreq(dev, SIOCETHTOOL, &ifr);
 	if (ret) {
 		DRV_LOG(WARNING, "port %u unable to get statistic names",
 			dev->data->port_id);
-- 
2.25.1


  parent reply	other threads:[~2021-01-18 11:29 UTC|newest]

Thread overview: 126+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-12-18 15:16 [dpdk-dev] [RFC 0/9] support global syntax Xueming Li
2020-12-18 15:16 ` [dpdk-dev] [RFC 1/9] devargs: fix data buffer storage type Xueming Li
2021-01-08 14:54   ` [dpdk-dev] [PATCH v1 0/7] eal: support global syntax Xueming Li
2021-01-08 14:54   ` [dpdk-dev] [PATCH v1 1/7] devargs: fix data buffer storage type Xueming Li
2021-01-08 14:54   ` [dpdk-dev] [PATCH v1 2/7] devargs: fix memory leak on parsing error Xueming Li
2021-01-08 14:54   ` [dpdk-dev] [PATCH v1 3/7] devargs: fix memory leak in legacy parser Xueming Li
2021-01-08 14:54   ` [dpdk-dev] [PATCH v1 4/7] devargs: fix buffer data memory leak Xueming Li
2021-01-08 14:54   ` [dpdk-dev] [PATCH v1 5/7] kvargs: add get by key function Xueming Li
2021-01-08 14:54   ` [dpdk-dev] [PATCH v1 6/7] devargs: support new global device syntax Xueming Li
2021-01-08 14:54   ` [dpdk-dev] [PATCH v1 7/7] bus/pci: add new global device syntax support Xueming Li
2021-01-08 15:14   ` [dpdk-dev] [PATCH v1 0/2] mlx5: support global syntax Xueming Li
2021-01-08 15:14   ` [dpdk-dev] [PATCH v1 1/2] common/mlx5: support device " Xueming Li
2021-01-08 15:15   ` [dpdk-dev] [PATCH v1 2/2] net/mlx5: support new " Xueming Li
2021-01-18 11:29   ` [dpdk-dev] [PATCH v3 0/9] net/mlx5: support SubFunction representor Xueming Li
2021-01-18 11:29   ` [dpdk-dev] [PATCH v3 1/9] common/mlx5: update representor name parsing Xueming Li
2021-01-18 11:29   ` [dpdk-dev] [PATCH v3 2/9] net/mlx5: support representor of sub function Xueming Li
2021-01-18 11:29   ` [dpdk-dev] [PATCH v3 3/9] net/mlx5: revert setting bonding representor to first PF Xueming Li
2021-01-18 11:29   ` [dpdk-dev] [PATCH v3 4/9] net/mlx5: refactor bonding representor probe Xueming Li
2021-01-18 11:29   ` [dpdk-dev] [PATCH v3 5/9] net/mlx5: support representor from multiple PFs Xueming Li
2021-01-18 11:29   ` [dpdk-dev] [PATCH v3 6/9] net/mlx5: save bonding member ports information Xueming Li
2021-01-18 11:29   ` [dpdk-dev] [PATCH v3 7/9] " Xueming Li
2021-01-18 16:17     ` Slava Ovsiienko
2021-01-18 23:05       ` Xueming(Steven) Li
2021-01-18 11:29   ` [dpdk-dev] [PATCH v3 8/9] net/mlx5: fix setting VF default MAC through representor Xueming Li
2021-01-18 11:29   ` Xueming Li [this message]
2021-01-18 15:16   ` [dpdk-dev] [PATCH v2 0/5] eal: enable global device syntax Xueming Li
2021-01-18 15:16   ` [dpdk-dev] [PATCH v2 1/5] devargs: fix memory leak on parsing error Xueming Li
2021-03-18  9:12     ` Thomas Monjalon
2021-01-18 15:16   ` [dpdk-dev] [PATCH v2 2/5] devargs: refactor scratch buffer storage Xueming Li
2021-03-18  9:14     ` Thomas Monjalon
2021-01-18 15:16   ` [dpdk-dev] [PATCH v2 3/5] kvargs: add get by key function Xueming Li
2021-01-18 15:16   ` [dpdk-dev] [PATCH v2 4/5] devargs: parse name from global device syntax Xueming Li
2021-01-18 15:16   ` [dpdk-dev] [PATCH v2 5/5] devargs: enable global device syntax devargs Xueming Li
2021-01-18 15:26   ` [dpdk-dev] [PATCH v2 0/2] mlx5: support global device syntax Xueming Li
2021-09-20  8:07     ` Thomas Monjalon
2021-09-23  6:47       ` Xueming(Steven) Li
2021-09-23  6:45     ` [dpdk-dev] [PATCH v3] net/mlx5: support new " Xueming Li
2021-09-29 19:37       ` Thomas Monjalon
2021-01-18 15:26   ` [dpdk-dev] [PATCH v2 1/2] common/mlx5: support device global syntax Xueming Li
2021-04-05 10:54     ` Slava Ovsiienko
2021-04-08 12:24       ` Raslan Darawsheh
2021-04-08 14:04         ` Raslan Darawsheh
2021-04-08 14:08           ` Xueming(Steven) Li
2021-04-08 14:13             ` Raslan Darawsheh
2021-04-19  9:29               ` Raslan Darawsheh
2021-04-19 10:36                 ` Raslan Darawsheh
2021-01-18 15:26   ` [dpdk-dev] [PATCH v2 2/2] net/mlx5: support new global device syntax Xueming Li
2021-04-05 10:56     ` Slava Ovsiienko
2021-01-19  7:28   ` [dpdk-dev] [PATCH v4 0/8] net/mlx5: support SubFunction representor Xueming Li
2021-01-19  7:28   ` [dpdk-dev] [PATCH v4 1/8] common/mlx5: update representor name parsing Xueming Li
2021-01-19  7:28   ` [dpdk-dev] [PATCH v4 2/8] net/mlx5: support representor of sub function Xueming Li
2021-01-19  7:28   ` [dpdk-dev] [PATCH v4 3/8] net/mlx5: revert setting bonding representor to first PF Xueming Li
2021-01-19  7:28   ` [dpdk-dev] [PATCH v4 4/8] net/mlx5: refactor bonding representor probe Xueming Li
2021-01-19  7:28   ` [dpdk-dev] [PATCH v4 5/8] net/mlx5: support representor from multiple PFs Xueming Li
2021-01-19  7:28   ` [dpdk-dev] [PATCH v4 6/8] net/mlx5: save bonding member ports information Xueming Li
2021-01-19  7:28   ` [dpdk-dev] [PATCH v4 7/8] net/mlx5: fix setting VF default MAC through representor Xueming Li
2021-01-19  7:28   ` [dpdk-dev] [PATCH v4 8/8] net/mlx5: improve bonding xstats Xueming Li
2021-03-28 13:48   ` [dpdk-dev] [PATCH v5 0/9] net/mlx5: support SubFunction representor Xueming Li
2021-03-31  7:20     ` Raslan Darawsheh
2021-03-31  7:27       ` Xueming(Steven) Li
2021-03-28 13:48   ` [dpdk-dev] [PATCH v5 1/9] common/mlx5: sub-function representor port name parsing Xueming Li
2021-03-28 13:48   ` [dpdk-dev] [PATCH v5 2/9] net/mlx5: support representor of sub function Xueming Li
2021-03-28 13:48   ` [dpdk-dev] [PATCH v5 3/9] net/mlx5: revert setting bonding representor to first PF Xueming Li
2021-03-28 13:48   ` [dpdk-dev] [PATCH v5 4/9] net/mlx5: refactor bonding representor probe Xueming Li
2021-03-28 13:48   ` [dpdk-dev] [PATCH v5 5/9] net/mlx5: support list value of representor PF Xueming Li
2021-03-28 13:48   ` [dpdk-dev] [PATCH v5 6/9] net/mlx5: save bonding member ports information Xueming Li
2021-03-28 13:48   ` [dpdk-dev] [PATCH v5 7/9] net/mlx5: fix setting VF default MAC through representor Xueming Li
2021-03-31  7:46     ` Raslan Darawsheh
2021-03-28 13:48   ` [dpdk-dev] [PATCH v5 8/9] net/mlx5: improve xstats of bonding port Xueming Li
2021-03-28 13:48   ` [dpdk-dev] [PATCH v5 9/9] net/mlx5: probe host PF representor with SubFunction Xueming Li
2021-03-30  7:37     ` Slava Ovsiienko
2021-03-30 12:15   ` [dpdk-dev] [PATCH v3 0/5] eal: enable global device syntax by default Xueming Li
2021-03-31  8:23     ` Gaëtan Rivet
2021-03-30 12:15   ` [dpdk-dev] [PATCH v3 1/5] devargs: unify scratch buffer storage Xueming Li
2021-04-01  9:04     ` Kinsella, Ray
2021-03-30 12:15   ` [dpdk-dev] [PATCH v3 2/5] devargs: fix memory leak on parsing error Xueming Li
2021-03-30 12:15   ` [dpdk-dev] [PATCH v3 3/5] kvargs: add get by key function Xueming Li
2021-04-01  9:06     ` Kinsella, Ray
2021-04-01  9:10       ` Xueming(Steven) Li
2021-03-30 12:15   ` [dpdk-dev] [PATCH v3 4/5] bus: add device arguments name parsing API Xueming Li
2021-03-31 10:19     ` Thomas Monjalon
2021-04-01 15:13       ` Xueming(Steven) Li
2021-04-08 23:49         ` Thomas Monjalon
2021-03-30 12:15   ` [dpdk-dev] [PATCH v3 5/5] devargs: parse global device syntax Xueming Li
2021-04-10 14:23   ` [dpdk-dev] [PATCH v4 0/5] eal: enable global device syntax by default Xueming Li
2021-04-10 14:23   ` [dpdk-dev] [PATCH v4 1/5] devargs: unify scratch buffer storage Xueming Li
2021-04-10 19:59     ` Tal Shnaiderman
2021-04-12 12:07       ` Xueming(Steven) Li
2021-04-10 14:23   ` [dpdk-dev] [PATCH v4 2/5] devargs: fix memory leak on parsing error Xueming Li
2021-04-10 14:23   ` [dpdk-dev] [PATCH v4 3/5] kvargs: add get by key function Xueming Li
2021-04-12  6:52     ` Olivier Matz
2021-04-12 12:07       ` Xueming(Steven) Li
2021-04-12 21:18         ` Thomas Monjalon
2021-04-10 14:23   ` [dpdk-dev] [PATCH v4 4/5] bus: add device arguments name parsing API Xueming Li
2021-04-12 21:16     ` Thomas Monjalon
2021-04-12 23:37       ` Xueming(Steven) Li
2021-04-10 14:23   ` [dpdk-dev] [PATCH v4 5/5] devargs: parse global device syntax Xueming Li
2021-04-12 21:24     ` Thomas Monjalon
2021-04-12 23:47       ` Xueming(Steven) Li
2021-04-13  3:14   ` [dpdk-dev] [PATCH v5 0/5] eal: enable global device syntax by default Xueming Li
2021-04-14 19:49     ` Thomas Monjalon
2021-04-23 11:06       ` Kinsella, Ray
2021-04-23 11:39         ` Gaëtan Rivet
2021-04-23 12:35           ` Kinsella, Ray
2021-09-02 14:46       ` Thomas Monjalon
2021-04-13  3:14   ` [dpdk-dev] [PATCH v5 1/5] devargs: unify scratch buffer storage Xueming Li
2021-04-16  7:00     ` David Marchand
2021-04-16 12:32       ` Aaron Conole
2021-04-16 12:43         ` [dpdk-dev] [dpdklab] " Lincoln Lavoie
2021-04-16 12:58           ` Thomas Monjalon
2021-04-16 13:14             ` Lincoln Lavoie
2021-04-13  3:14   ` [dpdk-dev] [PATCH v5 2/5] devargs: fix memory leak on parsing error Xueming Li
2021-04-13  3:14   ` [dpdk-dev] [PATCH v5 3/5] kvargs: add get by key function Xueming Li
2021-04-13  3:14   ` [dpdk-dev] [PATCH v5 4/5] bus: add device arguments name parsing API Xueming Li
2021-04-13  3:14   ` [dpdk-dev] [PATCH v5 5/5] devargs: parse global device syntax Xueming Li
2021-09-28  8:29     ` David Marchand
2021-09-28  9:04       ` Thomas Monjalon
2021-10-03 10:51         ` Xueming(Steven) Li
2020-12-18 15:16 ` [dpdk-dev] [RFC 2/9] devargs: fix memory leak on parsing error Xueming Li
2020-12-18 15:16 ` [dpdk-dev] [RFC 3/9] devargs: fix memory leak in legacy parser Xueming Li
2020-12-18 15:16 ` [dpdk-dev] [RFC 4/9] devargs: fix buffer data memory leak Xueming Li
2020-12-18 15:16 ` [dpdk-dev] [RFC 5/9] kvargs: add get by key function Xueming Li
2020-12-18 15:16 ` [dpdk-dev] [RFC 6/9] devargs: support new global device syntax Xueming Li
2020-12-18 15:16 ` [dpdk-dev] [RFC 7/9] bus/pci: add new global device syntax support Xueming Li
2020-12-18 15:16 ` [dpdk-dev] [RFC 8/9] common/mlx5: support device global syntax Xueming Li
2020-12-18 15:16 ` [dpdk-dev] [RFC 9/9] net/mlx5: support new " Xueming Li

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=1610969353-31938-10-git-send-email-xuemingl@nvidia.com \
    --to=xuemingl@nvidia.com \
    --cc=asafp@nvidia.com \
    --cc=dev@dpdk.org \
    --cc=matan@nvidia.com \
    --cc=shahafs@nvidia.com \
    --cc=thomas@monjalon.net \
    --cc=viacheslavo@nvidia.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.