All of lore.kernel.org
 help / color / mirror / Atom feed
From: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
To: dev@dpdk.org
Cc: yipeng1.wang@intel.com, sameh.gobriel@intel.com,
	bruce.richardson@intel.com, konstantin.ananyev@intel.com,
	stephen@networkplumber.org
Subject: [dpdk-dev] [PATCH v4 4/5] test/thash: add tests for a new Toeplitz hash function
Date: Thu, 21 Oct 2021 18:18:17 +0100	[thread overview]
Message-ID: <1634836698-10864-5-git-send-email-vladimir.medvedkin@intel.com> (raw)
In-Reply-To: <1634836698-10864-1-git-send-email-vladimir.medvedkin@intel.com>
In-Reply-To: <1634754016-367978-1-git-send-email-vladimir.medvedkin@intel.com>

This patch provides a set of tests for verifying the new
implementation of Toeplitz hash function using GFNI.

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
---
 app/test/test_thash.c | 237 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 237 insertions(+)

diff --git a/app/test/test_thash.c b/app/test/test_thash.c
index d8981fb..a625306 100644
--- a/app/test/test_thash.c
+++ b/app/test/test_thash.c
@@ -6,6 +6,7 @@
 #include <rte_eal.h>
 #include <rte_ip.h>
 #include <rte_random.h>
+#include <rte_malloc.h>
 
 #include "test.h"
 
@@ -78,6 +79,34 @@ uint8_t default_rss_key[] = {
 0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa,
 };
 
+static const uint8_t big_rss_key[] = {
+	0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
+	0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
+	0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
+	0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
+	0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa,
+	0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
+	0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
+	0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
+	0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
+	0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa,
+	0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
+	0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
+	0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
+	0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
+	0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa,
+	0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
+	0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
+	0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
+	0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
+	0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa,
+	0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
+	0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
+	0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
+	0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
+	0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa,
+};
+
 static int
 test_toeplitz_hash_calc(void)
 {
@@ -145,6 +174,210 @@ test_toeplitz_hash_calc(void)
 }
 
 static int
+test_toeplitz_hash_gfni(void)
+{
+	uint32_t i, j;
+	union rte_thash_tuple tuple;
+	uint32_t rss_l3, rss_l3l4;
+	uint64_t rss_key_matrixes[RTE_DIM(default_rss_key)];
+
+	if (!rte_thash_gfni_supported())
+		return TEST_SKIPPED;
+
+	/* Convert RSS key into matrixes */
+	rte_thash_complete_matrix(rss_key_matrixes, default_rss_key,
+		RTE_DIM(default_rss_key));
+
+	for (i = 0; i < RTE_DIM(v4_tbl); i++) {
+		tuple.v4.src_addr = rte_cpu_to_be_32(v4_tbl[i].src_ip);
+		tuple.v4.dst_addr = rte_cpu_to_be_32(v4_tbl[i].dst_ip);
+		tuple.v4.sport = rte_cpu_to_be_16(v4_tbl[i].dst_port);
+		tuple.v4.dport = rte_cpu_to_be_16(v4_tbl[i].src_port);
+
+		rss_l3 = rte_thash_gfni(rss_key_matrixes, (uint8_t *)&tuple,
+				RTE_THASH_V4_L3_LEN * 4);
+		rss_l3l4 = rte_thash_gfni(rss_key_matrixes, (uint8_t *)&tuple,
+				RTE_THASH_V4_L4_LEN * 4);
+		if ((rss_l3 != v4_tbl[i].hash_l3) ||
+				(rss_l3l4 != v4_tbl[i].hash_l3l4))
+			return -TEST_FAILED;
+	}
+
+	for (i = 0; i < RTE_DIM(v6_tbl); i++) {
+		for (j = 0; j < RTE_DIM(tuple.v6.src_addr); j++)
+			tuple.v6.src_addr[j] = v6_tbl[i].src_ip[j];
+		for (j = 0; j < RTE_DIM(tuple.v6.dst_addr); j++)
+			tuple.v6.dst_addr[j] = v6_tbl[i].dst_ip[j];
+		tuple.v6.sport = rte_cpu_to_be_16(v6_tbl[i].dst_port);
+		tuple.v6.dport = rte_cpu_to_be_16(v6_tbl[i].src_port);
+		rss_l3 = rte_thash_gfni(rss_key_matrixes, (uint8_t *)&tuple,
+				RTE_THASH_V6_L3_LEN * 4);
+		rss_l3l4 = rte_thash_gfni(rss_key_matrixes, (uint8_t *)&tuple,
+				RTE_THASH_V6_L4_LEN * 4);
+		if ((rss_l3 != v6_tbl[i].hash_l3) ||
+				(rss_l3l4 != v6_tbl[i].hash_l3l4))
+			return -TEST_FAILED;
+	}
+
+	return TEST_SUCCESS;
+}
+
+#define DATA_SZ		4
+#define ITER		1000
+
+enum {
+	SCALAR_DATA_BUF_1_HASH_IDX = 0,
+	SCALAR_DATA_BUF_2_HASH_IDX,
+	GFNI_DATA_BUF_1_HASH_IDX,
+	GFNI_DATA_BUF_2_HASH_IDX,
+	GFNI_BULK_DATA_BUF_1_HASH_IDX,
+	GFNI_BULK_DATA_BUF_2_HASH_IDX,
+	HASH_IDXES
+};
+
+static int
+test_toeplitz_hash_rand_data(void)
+{
+	uint32_t data[2][DATA_SZ];
+	uint32_t scalar_data[2][DATA_SZ];
+	uint32_t hash[HASH_IDXES] = { 0 };
+	uint64_t rss_key_matrixes[RTE_DIM(default_rss_key)];
+	int i, j;
+	uint8_t *bulk_data[2];
+
+	if (!rte_thash_gfni_supported())
+		return TEST_SKIPPED;
+
+	rte_thash_complete_matrix(rss_key_matrixes, default_rss_key,
+		RTE_DIM(default_rss_key));
+
+	for (i = 0; i < 2; i++)
+		bulk_data[i] = (uint8_t *)data[i];
+
+	for (i = 0; i < ITER; i++) {
+		for (j = 0; j < DATA_SZ; j++) {
+			data[0][j] = rte_rand();
+			data[1][j] = rte_rand();
+			scalar_data[0][j] = rte_cpu_to_be_32(data[0][j]);
+			scalar_data[1][j] = rte_cpu_to_be_32(data[1][j]);
+		}
+
+		hash[SCALAR_DATA_BUF_1_HASH_IDX] = rte_softrss(scalar_data[0],
+			DATA_SZ, default_rss_key);
+		hash[SCALAR_DATA_BUF_2_HASH_IDX] = rte_softrss(scalar_data[1],
+			DATA_SZ, default_rss_key);
+		hash[GFNI_DATA_BUF_1_HASH_IDX] = rte_thash_gfni(
+			rss_key_matrixes, (uint8_t *)data[0],
+			DATA_SZ * sizeof(uint32_t));
+		hash[GFNI_DATA_BUF_2_HASH_IDX] = rte_thash_gfni(
+			rss_key_matrixes, (uint8_t *)data[1],
+			DATA_SZ * sizeof(uint32_t));
+		rte_thash_gfni_bulk(rss_key_matrixes,
+			DATA_SZ * sizeof(uint32_t), bulk_data,
+			&hash[GFNI_BULK_DATA_BUF_1_HASH_IDX], 2);
+
+		if ((hash[SCALAR_DATA_BUF_1_HASH_IDX] !=
+				hash[GFNI_DATA_BUF_1_HASH_IDX]) ||
+				(hash[SCALAR_DATA_BUF_1_HASH_IDX] !=
+				hash[GFNI_BULK_DATA_BUF_1_HASH_IDX]) ||
+				(hash[SCALAR_DATA_BUF_2_HASH_IDX] !=
+				hash[GFNI_DATA_BUF_2_HASH_IDX]) ||
+				(hash[SCALAR_DATA_BUF_2_HASH_IDX] !=
+				hash[GFNI_BULK_DATA_BUF_2_HASH_IDX]))
+
+			return -TEST_FAILED;
+	}
+
+	return TEST_SUCCESS;
+}
+
+enum {
+	RSS_V4_IDX,
+	RSS_V6_IDX
+};
+
+static int
+test_toeplitz_hash_gfni_bulk(void)
+{
+	uint32_t i, j;
+	union rte_thash_tuple tuple[2];
+	uint8_t *tuples[2];
+	uint32_t rss[2] = { 0 };
+	uint64_t rss_key_matrixes[RTE_DIM(default_rss_key)];
+
+	if (!rte_thash_gfni_supported())
+		return TEST_SKIPPED;
+
+	/* Convert RSS key into matrixes */
+	rte_thash_complete_matrix(rss_key_matrixes, default_rss_key,
+		RTE_DIM(default_rss_key));
+
+	for (i = 0; i < RTE_DIM(tuples); i++) {
+		/* allocate memory enough for a biggest tuple */
+		tuples[i] = rte_zmalloc(NULL, RTE_THASH_V6_L4_LEN * 4, 0);
+		if (tuples[i] == NULL)
+			return -TEST_FAILED;
+	}
+
+	for (i = 0; i < RTE_MIN(RTE_DIM(v4_tbl), RTE_DIM(v6_tbl)); i++) {
+		/*Load IPv4 headers and copy it into the corresponding tuple*/
+		tuple[0].v4.src_addr = rte_cpu_to_be_32(v4_tbl[i].src_ip);
+		tuple[0].v4.dst_addr = rte_cpu_to_be_32(v4_tbl[i].dst_ip);
+		tuple[0].v4.sport = rte_cpu_to_be_16(v4_tbl[i].dst_port);
+		tuple[0].v4.dport = rte_cpu_to_be_16(v4_tbl[i].src_port);
+		rte_memcpy(tuples[0], &tuple[0], RTE_THASH_V4_L4_LEN * 4);
+
+		/*Load IPv6 headers and copy it into the corresponding tuple*/
+		for (j = 0; j < RTE_DIM(tuple[1].v6.src_addr); j++)
+			tuple[1].v6.src_addr[j] = v6_tbl[i].src_ip[j];
+		for (j = 0; j < RTE_DIM(tuple[1].v6.dst_addr); j++)
+			tuple[1].v6.dst_addr[j] = v6_tbl[i].dst_ip[j];
+		tuple[1].v6.sport = rte_cpu_to_be_16(v6_tbl[i].dst_port);
+		tuple[1].v6.dport = rte_cpu_to_be_16(v6_tbl[i].src_port);
+		rte_memcpy(tuples[1], &tuple[1], RTE_THASH_V6_L4_LEN * 4);
+
+		rte_thash_gfni_bulk(rss_key_matrixes, RTE_THASH_V6_L4_LEN * 4,
+			tuples, rss, 2);
+
+		if ((rss[RSS_V4_IDX] != v4_tbl[i].hash_l3l4) ||
+				(rss[RSS_V6_IDX] != v6_tbl[i].hash_l3l4))
+			return -TEST_FAILED;
+	}
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_big_tuple_gfni(void)
+{
+	uint32_t arr[16];
+	uint32_t arr_softrss[16];
+	uint32_t hash_1, hash_2;
+	uint64_t rss_key_matrixes[RTE_DIM(big_rss_key)];
+	unsigned int i, size = RTE_DIM(arr) * sizeof(uint32_t);
+
+	if (!rte_thash_gfni_supported())
+		return TEST_SKIPPED;
+
+	/* Convert RSS key into matrixes */
+	rte_thash_complete_matrix(rss_key_matrixes, big_rss_key,
+		RTE_DIM(big_rss_key));
+
+	for (i = 0; i < RTE_DIM(arr); i++) {
+		arr[i] = rte_rand();
+		arr_softrss[i] = rte_be_to_cpu_32(arr[i]);
+	}
+
+	hash_1 = rte_softrss(arr_softrss, RTE_DIM(arr), big_rss_key);
+	hash_2 = rte_thash_gfni(rss_key_matrixes, (uint8_t *)arr, size);
+
+	if (hash_1 != hash_2)
+		return -TEST_FAILED;
+
+	return TEST_SUCCESS;
+}
+
+static int
 test_create_invalid(void)
 {
 	struct rte_thash_ctx *ctx;
@@ -577,6 +810,10 @@ static struct unit_test_suite thash_tests = {
 	.teardown = NULL,
 	.unit_test_cases = {
 	TEST_CASE(test_toeplitz_hash_calc),
+	TEST_CASE(test_toeplitz_hash_gfni),
+	TEST_CASE(test_toeplitz_hash_rand_data),
+	TEST_CASE(test_toeplitz_hash_gfni_bulk),
+	TEST_CASE(test_big_tuple_gfni),
 	TEST_CASE(test_create_invalid),
 	TEST_CASE(test_multiple_create),
 	TEST_CASE(test_free_null),
-- 
2.7.4


  parent reply	other threads:[~2021-10-21 17:21 UTC|newest]

Thread overview: 72+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-09-06 16:03 [dpdk-dev] [PATCH 0/5] optimized Toeplitz hash implementation Vladimir Medvedkin
2021-09-06 16:03 ` [dpdk-dev] [PATCH 1/5] hash: add new toeplitz " Vladimir Medvedkin
2021-10-07 18:23   ` Ananyev, Konstantin
2021-10-08 11:19     ` Ananyev, Konstantin
2021-10-15  9:11     ` Medvedkin, Vladimir
2021-10-15 10:55       ` Ananyev, Konstantin
2021-10-15 13:09         ` Medvedkin, Vladimir
2021-09-06 16:03 ` [dpdk-dev] [PATCH 2/5] hash: enable gfni thash implementation Vladimir Medvedkin
2021-10-08 11:31   ` Ananyev, Konstantin
2021-10-15  9:13     ` Medvedkin, Vladimir
2021-09-06 16:03 ` [dpdk-dev] [PATCH 3/5] doc/hash: update documentation for the thash library Vladimir Medvedkin
2021-09-06 16:03 ` [dpdk-dev] [PATCH 4/5] test/thash: add tests for a new Toeplitz hash function Vladimir Medvedkin
2021-09-07  0:35   ` Stephen Hemminger
2021-09-08 13:59     ` Medvedkin, Vladimir
2021-09-06 16:03 ` [dpdk-dev] [PATCH 5/5] test/thash: add performance tests for the Toeplitz hash Vladimir Medvedkin
2021-10-15  9:30 ` [dpdk-dev] [PATCH v2 0/5] optimized Toeplitz hash implementation Vladimir Medvedkin
2021-10-20 18:20   ` [dpdk-dev] [PATCH v3 " Vladimir Medvedkin
2021-10-21 17:18     ` [dpdk-dev] [PATCH v4 " Vladimir Medvedkin
2021-10-21 18:54       ` [dpdk-dev] [PATCH v5 " Vladimir Medvedkin
2021-10-21 18:54       ` [dpdk-dev] [PATCH v5 1/5] hash: add new toeplitz " Vladimir Medvedkin
2021-10-25 17:05         ` Thomas Monjalon
2021-10-21 18:54       ` [dpdk-dev] [PATCH v5 2/5] hash: enable gfni thash implementation Vladimir Medvedkin
2021-10-21 18:54       ` [dpdk-dev] [PATCH v5 3/5] doc/hash: update documentation for the thash library Vladimir Medvedkin
2021-10-25 17:04         ` Thomas Monjalon
2021-10-26 20:30           ` Medvedkin, Vladimir
2021-10-21 18:54       ` [dpdk-dev] [PATCH v5 4/5] test/thash: add tests for a new Toeplitz hash function Vladimir Medvedkin
2021-10-21 18:54       ` [dpdk-dev] [PATCH v5 5/5] test/thash: add performance tests for the Toeplitz hash Vladimir Medvedkin
2021-10-25 17:02         ` Thomas Monjalon
2021-10-26 20:29           ` Medvedkin, Vladimir
2021-10-27  8:29             ` Thomas Monjalon
2021-10-27 15:48               ` Medvedkin, Vladimir
2021-10-25 17:27         ` Stephen Hemminger
2021-10-26 20:31           ` Medvedkin, Vladimir
2021-10-21 17:18     ` [dpdk-dev] [PATCH v4 1/5] hash: add new toeplitz hash implementation Vladimir Medvedkin
2021-10-21 17:18     ` [dpdk-dev] [PATCH v4 2/5] hash: enable gfni thash implementation Vladimir Medvedkin
2021-10-21 17:18     ` [dpdk-dev] [PATCH v4 3/5] doc/hash: update documentation for the thash library Vladimir Medvedkin
2021-10-21 17:18     ` Vladimir Medvedkin [this message]
2021-10-21 17:18     ` [dpdk-dev] [PATCH v4 5/5] test/thash: add performance tests for the Toeplitz hash Vladimir Medvedkin
2021-10-20 18:20   ` [dpdk-dev] [PATCH v3 1/5] hash: add new toeplitz hash implementation Vladimir Medvedkin
2021-10-21  9:42     ` Ananyev, Konstantin
2021-10-21 17:17       ` Medvedkin, Vladimir
2021-10-20 18:20   ` [dpdk-dev] [PATCH v3 2/5] hash: enable gfni thash implementation Vladimir Medvedkin
2021-10-21  9:46     ` Ananyev, Konstantin
2021-10-20 18:20   ` [dpdk-dev] [PATCH v3 3/5] doc/hash: update documentation for the thash library Vladimir Medvedkin
2021-10-20 18:20   ` [dpdk-dev] [PATCH v3 4/5] test/thash: add tests for a new Toeplitz hash function Vladimir Medvedkin
2021-10-20 18:20   ` [dpdk-dev] [PATCH v3 5/5] test/thash: add performance tests for the Toeplitz hash Vladimir Medvedkin
2021-10-15  9:30 ` [dpdk-dev] [PATCH v2 1/5] hash: add new toeplitz hash implementation Vladimir Medvedkin
2021-10-15 16:58   ` Stephen Hemminger
2021-10-18 10:40     ` Ananyev, Konstantin
2021-10-19  1:15       ` Stephen Hemminger
2021-10-19 15:42         ` Medvedkin, Vladimir
2021-10-18 11:08     ` Medvedkin, Vladimir
2021-10-15  9:30 ` [dpdk-dev] [PATCH v2 2/5] hash: enable gfni thash implementation Vladimir Medvedkin
2021-10-15  9:30 ` [dpdk-dev] [PATCH v2 3/5] doc/hash: update documentation for the thash library Vladimir Medvedkin
2021-10-15  9:30 ` [dpdk-dev] [PATCH v2 4/5] test/thash: add tests for a new Toeplitz hash function Vladimir Medvedkin
2021-10-15  9:30 ` [dpdk-dev] [PATCH v2 5/5] test/thash: add performance tests for the Toeplitz hash Vladimir Medvedkin
2021-10-26 20:32 ` [dpdk-dev] [PATCH v6 0/4] optimized Toeplitz hash implementation Vladimir Medvedkin
2021-10-26 20:32 ` [dpdk-dev] [PATCH v6 1/4] hash: add new toeplitz " Vladimir Medvedkin
2021-10-26 20:32 ` [dpdk-dev] [PATCH v6 2/4] hash: add bulk " Vladimir Medvedkin
2021-10-26 20:32 ` [dpdk-dev] [PATCH v6 3/4] hash: enable gfni thash implementation Vladimir Medvedkin
2021-10-26 20:32 ` [dpdk-dev] [PATCH v6 4/4] test/thash: add performance tests for the Toeplitz hash Vladimir Medvedkin
2021-10-27 16:16 ` [dpdk-dev] [PATCH v7 0/4] optimized Toeplitz hash implementation Vladimir Medvedkin
2021-10-27 16:16 ` [dpdk-dev] [PATCH v7 1/4] hash: add new toeplitz " Vladimir Medvedkin
2021-10-27 16:16 ` [dpdk-dev] [PATCH v7 2/4] hash: add bulk " Vladimir Medvedkin
2021-10-27 16:16 ` [dpdk-dev] [PATCH v7 3/4] hash: enable gfni thash implementation Vladimir Medvedkin
2021-10-27 16:16 ` [dpdk-dev] [PATCH v7 4/4] test/thash: add performance tests for the Toeplitz hash Vladimir Medvedkin
2021-11-02 18:38 ` [dpdk-dev] [PATCH v8 0/4] optimized Toeplitz hash implementation Vladimir Medvedkin
2021-11-04 10:20   ` Thomas Monjalon
2021-11-02 18:38 ` [dpdk-dev] [PATCH v8 1/4] hash: add new toeplitz " Vladimir Medvedkin
2021-11-02 18:38 ` [dpdk-dev] [PATCH v8 2/4] hash: add bulk " Vladimir Medvedkin
2021-11-02 18:38 ` [dpdk-dev] [PATCH v8 3/4] hash: enable gfni thash implementation Vladimir Medvedkin
2021-11-02 18:38 ` [dpdk-dev] [PATCH v8 4/4] test/thash: add performance tests for the Toeplitz hash Vladimir Medvedkin

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=1634836698-10864-5-git-send-email-vladimir.medvedkin@intel.com \
    --to=vladimir.medvedkin@intel.com \
    --cc=bruce.richardson@intel.com \
    --cc=dev@dpdk.org \
    --cc=konstantin.ananyev@intel.com \
    --cc=sameh.gobriel@intel.com \
    --cc=stephen@networkplumber.org \
    --cc=yipeng1.wang@intel.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.