All of lore.kernel.org
 help / color / mirror / Atom feed
From: Dean Nelson <dcn@sgi.com>
To: akpm@linux-foundation.org
Cc: linux-kernel@vger.kernel.org
Subject: [Patch 05/18] create a common xp_remote_memcpy() function
Date: Fri, 6 Jun 2008 11:47:13 -0500	[thread overview]
Message-ID: <20080606164713.GF13695@sgi.com> (raw)
In-Reply-To: <20080606164034.GA13695@sgi.com>

Create a common remote memcpy function that maps to what the hardware booted
supports.

Signed-off-by: Dean Nelson <dcn@sgi.com>

---

 drivers/misc/sgi-xp/xp.h            |   43 +------------------------
 drivers/misc/sgi-xp/xp_main.c       |    3 +
 drivers/misc/sgi-xp/xp_sn2.c        |   46 ++++++++++++++++++++++++++
 drivers/misc/sgi-xp/xp_uv.c         |   11 ++++++
 drivers/misc/sgi-xp/xpc.h           |    7 ----
 drivers/misc/sgi-xp/xpc_channel.c   |   20 ++++-------
 drivers/misc/sgi-xp/xpc_partition.c |   55 +++++++++++++-------------------
 drivers/misc/sgi-xp/xpnet.c         |   30 ++++++++---------
 8 files changed, 108 insertions(+), 107 deletions(-)

Index: linux-2.6/drivers/misc/sgi-xp/xp.h
===================================================================
--- linux-2.6.orig/drivers/misc/sgi-xp/xp.h	2008-05-30 13:55:49.537296334 -0500
+++ linux-2.6/drivers/misc/sgi-xp/xp.h	2008-05-30 13:56:05.871344137 -0500
@@ -17,7 +17,6 @@
 #include <linux/hardirq.h>
 #include <linux/mutex.h>
 #include <asm/sn/types.h>
-#include <asm/sn/bte.h>
 #ifdef CONFIG_IA64
 #include <asm/sn/arch.h>
 #endif
@@ -72,46 +71,6 @@
 #define XP_NASID_MASK_WORDS	((XP_MAX_PHYSNODE_ID + 63) / 64)
 
 /*
- * Wrapper for bte_copy() that should it return a failure status will retry
- * the bte_copy() once in the hope that the failure was due to a temporary
- * aberration (i.e., the link going down temporarily).
- *
- * 	src - physical address of the source of the transfer.
- *	vdst - virtual address of the destination of the transfer.
- *	len - number of bytes to transfer from source to destination.
- *	mode - see bte_copy() for definition.
- *	notification - see bte_copy() for definition.
- *
- * Note: xp_bte_copy() should never be called while holding a spinlock.
- */
-static inline bte_result_t
-xp_bte_copy(u64 src, u64 vdst, u64 len, u64 mode, void *notification)
-{
-	bte_result_t ret;
-	u64 pdst = ia64_tpa(vdst);
-
-	/*
-	 * Ensure that the physically mapped memory is contiguous.
-	 *
-	 * We do this by ensuring that the memory is from region 7 only.
-	 * If the need should arise to use memory from one of the other
-	 * regions, then modify the BUG_ON() statement to ensure that the
-	 * memory from that region is always physically contiguous.
-	 */
-	BUG_ON(REGION_NUMBER(vdst) != RGN_KERNEL);
-
-	ret = bte_copy(src, pdst, len, mode, notification);
-	if ((ret != BTE_SUCCESS) && BTE_ERROR_RETRY(ret)) {
-		if (!in_interrupt())
-			cond_resched();
-
-		ret = bte_copy(src, pdst, len, mode, notification);
-	}
-
-	return ret;
-}
-
-/*
  * XPC establishes channel connections between the local partition and any
  * other partition that is currently up. Over these channels, kernel-level
  * `users' can communicate with their counterparts on the other partitions.
@@ -408,6 +367,8 @@ xpc_partid_to_nasids(short partid, void 
 
 extern short xp_max_npartitions;
 
+extern enum xp_retval (*xp_remote_memcpy) (void *, const void *, size_t);
+
 extern u64 xp_nofault_PIOR_target;
 extern int xp_nofault_PIOR(void *);
 extern int xp_error_PIOR(void);
Index: linux-2.6/drivers/misc/sgi-xp/xp_main.c
===================================================================
--- linux-2.6.orig/drivers/misc/sgi-xp/xp_main.c	2008-05-30 13:55:49.665312383 -0500
+++ linux-2.6/drivers/misc/sgi-xp/xp_main.c	2008-05-30 13:56:05.887346143 -0500
@@ -36,6 +36,9 @@ struct device *xp = &xp_dbg_subname;
 short xp_max_npartitions;
 EXPORT_SYMBOL_GPL(xp_max_npartitions);
 
+enum xp_retval (*xp_remote_memcpy) (void *dst, const void *src, size_t len);
+EXPORT_SYMBOL_GPL(xp_remote_memcpy);
+
 /*
  * xpc_registrations[] keeps track of xpc_connect()'s done by the kernel-level
  * users of XPC.
Index: linux-2.6/drivers/misc/sgi-xp/xp_sn2.c
===================================================================
--- linux-2.6.orig/drivers/misc/sgi-xp/xp_sn2.c	2008-05-30 13:55:49.689315393 -0500
+++ linux-2.6/drivers/misc/sgi-xp/xp_sn2.c	2008-05-30 13:56:05.911349152 -0500
@@ -13,6 +13,7 @@
  */
 
 #include <linux/device.h>
+#include <asm/sn/bte.h>
 #include <asm/sn/sn_sal.h>
 #include "xp.h"
 
@@ -72,6 +73,49 @@ xp_unregister_nofault_code_sn2(void)
 				       err_func_addr, 1, 0);
 }
 
+/*
+ * Wrapper for bte_copy().
+ *
+ *	vdst - virtual address of the destination of the transfer.
+ *	psrc - physical address of the source of the transfer.
+ *	len - number of bytes to transfer from source to destination.
+ *
+ * Note: xp_remote_memcpy_sn2() should never be called while holding a spinlock.
+ */
+static enum xp_retval
+xp_remote_memcpy_sn2(void *vdst, const void *psrc, size_t len)
+{
+	bte_result_t ret;
+	u64 pdst = ia64_tpa(vdst);
+	/* >>> What are the rules governing the src and dst addresses passed in?
+	 * >>> Currently we're assuming that dst is a virtual address and src
+	 * >>> is a physical address, is this appropriate? Can we allow them to
+	 * >>> be whatever and we make the change here without damaging the
+	 * >>> addresses?
+	 */
+
+	/*
+	 * Ensure that the physically mapped memory is contiguous.
+	 *
+	 * We do this by ensuring that the memory is from region 7 only.
+	 * If the need should arise to use memory from one of the other
+	 * regions, then modify the BUG_ON() statement to ensure that the
+	 * memory from that region is always physically contiguous.
+	 */
+	BUG_ON(REGION_NUMBER(vdst) != RGN_KERNEL);
+
+	ret = bte_copy((u64)psrc, pdst, len, (BTE_NOTIFY | BTE_WACQUIRE), NULL);
+	if (ret == BTE_SUCCESS)
+		return xpSuccess;
+
+	if (is_shub2())
+		dev_err(xp, "bte_copy() on shub2 failed, error=0x%x\n", ret);
+	else
+		dev_err(xp, "bte_copy() failed, error=%d\n", ret);
+
+	return xpBteCopyError;
+}
+
 enum xp_retval
 xp_init_sn2(void)
 {
@@ -79,6 +123,8 @@ xp_init_sn2(void)
 
 	xp_max_npartitions = XP_MAX_NPARTITIONS_SN2;
 
+	xp_remote_memcpy = xp_remote_memcpy_sn2;
+
 	return xp_register_nofault_code_sn2();
 }
 
Index: linux-2.6/drivers/misc/sgi-xp/xp_uv.c
===================================================================
--- linux-2.6.orig/drivers/misc/sgi-xp/xp_uv.c	2008-05-30 13:55:49.713318402 -0500
+++ linux-2.6/drivers/misc/sgi-xp/xp_uv.c	2008-05-30 13:56:05.935352162 -0500
@@ -15,12 +15,23 @@
 
 #include "xp.h"
 
+static enum xp_retval
+xp_remote_memcpy_uv(void *vdst, const void *psrc, size_t len)
+{
+	/* >>> this function needs fleshing out */
+	return xpUnsupported;
+}
+
 enum xp_retval
 xp_init_uv(void)
 {
 	BUG_ON(!is_uv());
 
 	xp_max_npartitions = XP_MAX_NPARTITIONS_UV;
+
+	xp_remote_memcpy = xp_remote_memcpy_uv;
+
+	return xpSuccess;
 }
 
 void
Index: linux-2.6/drivers/misc/sgi-xp/xpc.h
===================================================================
--- linux-2.6.orig/drivers/misc/sgi-xp/xpc.h	2008-05-30 13:55:49.561299343 -0500
+++ linux-2.6/drivers/misc/sgi-xp/xpc.h	2008-05-30 13:56:05.955354669 -0500
@@ -20,7 +20,6 @@
 #include <linux/completion.h>
 #include <asm/pgtable.h>
 #include <asm/processor.h>
-#include <asm/sn/bte.h>
 #include <asm/sn/clksupport.h>
 #include <asm/sn/addrs.h>
 #include <asm/sn/mspec.h>
@@ -1125,12 +1124,6 @@ xpc_IPI_init(int index)
 	return amo;
 }
 
-static inline enum xp_retval
-xpc_map_bte_errors(bte_result_t error)
-{
-	return ((error == BTE_SUCCESS) ? xpSuccess : xpBteCopyError);
-}
-
 /*
  * Check to see if there is any channel activity to/from the specified
  * partition.
Index: linux-2.6/drivers/misc/sgi-xp/xpc_channel.c
===================================================================
--- linux-2.6.orig/drivers/misc/sgi-xp/xpc_channel.c	2008-05-30 13:55:49.609305362 -0500
+++ linux-2.6/drivers/misc/sgi-xp/xpc_channel.c	2008-05-30 13:56:05.975357177 -0500
@@ -21,7 +21,6 @@
 #include <linux/interrupt.h>
 #include <linux/mutex.h>
 #include <linux/completion.h>
-#include <asm/sn/bte.h>
 #include <asm/sn/sn_sal.h>
 #include "xpc.h"
 
@@ -252,13 +251,13 @@ xpc_setup_infrastructure(struct xpc_part
  *
  * src must be a cacheline aligned physical address on the remote partition.
  * dst must be a cacheline aligned virtual address on this partition.
- * cnt must be an cacheline sized
+ * cnt must be cacheline sized
  */
 static enum xp_retval
 xpc_pull_remote_cachelines(struct xpc_partition *part, void *dst,
 			   const void *src, size_t cnt)
 {
-	bte_result_t bte_ret;
+	enum xp_retval ret;
 
 	DBUG_ON((u64)src != L1_CACHE_ALIGN((u64)src));
 	DBUG_ON((u64)dst != L1_CACHE_ALIGN((u64)dst));
@@ -267,15 +266,12 @@ xpc_pull_remote_cachelines(struct xpc_pa
 	if (part->act_state == XPC_P_DEACTIVATING)
 		return part->reason;
 
-	bte_ret = xp_bte_copy((u64)src, (u64)dst, (u64)cnt,
-			      (BTE_NORMAL | BTE_WACQUIRE), NULL);
-	if (bte_ret == BTE_SUCCESS)
-		return xpSuccess;
-
-	dev_dbg(xpc_chan, "xp_bte_copy() from partition %d failed, ret=%d\n",
-		XPC_PARTID(part), bte_ret);
-
-	return xpc_map_bte_errors(bte_ret);
+	ret = xp_remote_memcpy(dst, src, cnt);
+	if (ret != xpSuccess) {
+		dev_dbg(xpc_chan, "xp_remote_memcpy() from partition %d failed,"
+			" ret=%d\n", XPC_PARTID(part), ret);
+	}
+	return ret;
 }
 
 /*
Index: linux-2.6/drivers/misc/sgi-xp/xpc_partition.c
===================================================================
--- linux-2.6.orig/drivers/misc/sgi-xp/xpc_partition.c	2008-05-30 13:55:49.589302854 -0500
+++ linux-2.6/drivers/misc/sgi-xp/xpc_partition.c	2008-05-30 13:56:05.999360186 -0500
@@ -21,7 +21,6 @@
 #include <linux/mmzone.h>
 #include <linux/nodemask.h>
 #include <asm/uncached.h>
-#include <asm/sn/bte.h>
 #include <asm/sn/intr.h>
 #include <asm/sn/sn_sal.h>
 #include <asm/sn/nodepda.h>
@@ -92,7 +91,7 @@ xpc_kmalloc_cacheline_aligned(size_t siz
 static u64
 xpc_get_rsvd_page_pa(int nasid)
 {
-	bte_result_t bte_res;
+	enum xp_retval ret;
 	s64 status;
 	u64 cookie = 0;
 	u64 rp_pa = nasid;	/* seed with nasid */
@@ -113,6 +112,7 @@ xpc_get_rsvd_page_pa(int nasid)
 		if (status != SALRET_MORE_PASSES)
 			break;
 
+		/* >>> L1_CACHE_ALIGN() is only a sn2-bte_copy requirement */
 		if (L1_CACHE_ALIGN(len) > buf_len) {
 			kfree(buf_base);
 			buf_len = L1_CACHE_ALIGN(len);
@@ -127,10 +127,9 @@ xpc_get_rsvd_page_pa(int nasid)
 			}
 		}
 
-		bte_res = xp_bte_copy(rp_pa, buf, buf_len,
-				      (BTE_NOTIFY | BTE_WACQUIRE), NULL);
-		if (bte_res != BTE_SUCCESS) {
-			dev_dbg(xpc_part, "xp_bte_copy failed %i\n", bte_res);
+		ret = xp_remote_memcpy((void *)buf, (void *)rp_pa, buf_len);
+		if (ret != xpSuccess) {
+			dev_dbg(xpc_part, "xp_remote_memcpy failed %d\n", ret);
 			status = SALRET_ERROR;
 			break;
 		}
@@ -398,7 +397,7 @@ xpc_check_remote_hb(void)
 	struct xpc_vars *remote_vars;
 	struct xpc_partition *part;
 	short partid;
-	bte_result_t bres;
+	enum xp_retval ret;
 
 	remote_vars = (struct xpc_vars *)xpc_remote_copy_buffer;
 
@@ -418,13 +417,11 @@ xpc_check_remote_hb(void)
 		}
 
 		/* pull the remote_hb cache line */
-		bres = xp_bte_copy(part->remote_vars_pa,
-				   (u64)remote_vars,
-				   XPC_RP_VARS_SIZE,
-				   (BTE_NOTIFY | BTE_WACQUIRE), NULL);
-		if (bres != BTE_SUCCESS) {
-			XPC_DEACTIVATE_PARTITION(part,
-						 xpc_map_bte_errors(bres));
+		ret = xp_remote_memcpy(remote_vars,
+				       (void *)part->remote_vars_pa,
+				       XPC_RP_VARS_SIZE);
+		if (ret != xpSuccess) {
+			XPC_DEACTIVATE_PARTITION(part, ret);
 			continue;
 		}
 
@@ -457,7 +454,8 @@ static enum xp_retval
 xpc_get_remote_rp(int nasid, u64 *discovered_nasids,
 		  struct xpc_rsvd_page *remote_rp, u64 *remote_rp_pa)
 {
-	int bres, i;
+	int i;
+	enum xp_retval ret;
 
 	/* get the reserved page's physical address */
 
@@ -466,11 +464,10 @@ xpc_get_remote_rp(int nasid, u64 *discov
 		return xpNoRsvdPageAddr;
 
 	/* pull over the reserved page header and part_nasids mask */
-	bres = xp_bte_copy(*remote_rp_pa, (u64)remote_rp,
-			   XPC_RP_HEADER_SIZE + xp_nasid_mask_bytes,
-			   (BTE_NOTIFY | BTE_WACQUIRE), NULL);
-	if (bres != BTE_SUCCESS)
-		return xpc_map_bte_errors(bres);
+	ret = xp_remote_memcpy(remote_rp, (void *)*remote_rp_pa,
+			       XPC_RP_HEADER_SIZE + xp_nasid_mask_bytes);
+	if (ret != xpSuccess)
+		return ret;
 
 	if (discovered_nasids != NULL) {
 		u64 *remote_part_nasids = XPC_RP_PART_NASIDS(remote_rp);
@@ -504,16 +501,16 @@ xpc_get_remote_rp(int nasid, u64 *discov
 static enum xp_retval
 xpc_get_remote_vars(u64 remote_vars_pa, struct xpc_vars *remote_vars)
 {
-	int bres;
+	enum xp_retval ret;
 
 	if (remote_vars_pa == 0)
 		return xpVarsNotSet;
 
 	/* pull over the cross partition variables */
-	bres = xp_bte_copy(remote_vars_pa, (u64)remote_vars, XPC_RP_VARS_SIZE,
-			   (BTE_NOTIFY | BTE_WACQUIRE), NULL);
-	if (bres != BTE_SUCCESS)
-		return xpc_map_bte_errors(bres);
+	ret = xp_remote_memcpy(remote_vars, (void *)remote_vars_pa,
+			       XPC_RP_VARS_SIZE);
+	if (ret != xpSuccess)
+		return ret;
 
 	if (XPC_VERSION_MAJOR(remote_vars->version) !=
 	    XPC_VERSION_MAJOR(XPC_V_VERSION)) {
@@ -1148,7 +1145,6 @@ xpc_initiate_partid_to_nasids(short part
 {
 	struct xpc_partition *part;
 	u64 part_nasid_pa;
-	int bte_res;
 
 	part = &xpc_partitions[partid];
 	if (part->remote_rp_pa == 0)
@@ -1158,9 +1154,6 @@ xpc_initiate_partid_to_nasids(short part
 
 	part_nasid_pa = (u64)XPC_RP_PART_NASIDS(part->remote_rp_pa);
 
-	bte_res = xp_bte_copy(part_nasid_pa, (u64)nasid_mask,
-			      xp_nasid_mask_bytes, (BTE_NOTIFY | BTE_WACQUIRE),
-			      NULL);
-
-	return xpc_map_bte_errors(bte_res);
+	return xp_remote_memcpy(nasid_mask, (void *)part_nasid_pa,
+				xp_nasid_mask_bytes);
 }
Index: linux-2.6/drivers/misc/sgi-xp/xpnet.c
===================================================================
--- linux-2.6.orig/drivers/misc/sgi-xp/xpnet.c	2008-05-30 13:55:49.641309374 -0500
+++ linux-2.6/drivers/misc/sgi-xp/xpnet.c	2008-05-30 13:56:06.039365201 -0500
@@ -32,7 +32,6 @@
 #include <linux/mii.h>
 #include <linux/smp.h>
 #include <linux/string.h>
-#include <asm/sn/bte.h>
 #include <asm/sn/io.h>
 #include <asm/sn/sn_sal.h>
 #include <asm/atomic.h>
@@ -169,7 +168,7 @@ static void
 xpnet_receive(short partid, int channel, struct xpnet_message *msg)
 {
 	struct sk_buff *skb;
-	bte_result_t bret;
+	enum xp_retval ret;
 	struct xpnet_dev_private *priv =
 	    (struct xpnet_dev_private *)xpnet_device->priv;
 
@@ -201,7 +200,7 @@ xpnet_receive(short partid, int channel,
 
 	/*
 	 * The allocated skb has some reserved space.
-	 * In order to use bte_copy, we need to get the
+	 * In order to use xp_remote_memcpy(), we need to get the
 	 * skb->data pointer moved forward.
 	 */
 	skb_reserve(skb, (L1_CACHE_BYTES - ((u64)skb->data &
@@ -227,25 +226,24 @@ xpnet_receive(short partid, int channel,
 					(size_t)msg->embedded_bytes);
 	} else {
 		dev_dbg(xpnet, "transferring buffer to the skb->data area;\n\t"
-			"bte_copy(0x%p, 0x%p, %hu)\n", (void *)msg->buf_pa,
-			(void *)__pa((u64)skb->data & ~(L1_CACHE_BYTES - 1)),
-			msg->size);
-
-		bret = bte_copy(msg->buf_pa,
-				__pa((u64)skb->data & ~(L1_CACHE_BYTES - 1)),
-				msg->size, (BTE_NOTIFY | BTE_WACQUIRE), NULL);
+			"xp_remote_memcpy(0x%p, 0x%p, %hu)\n", (void *)
+				       ((u64)skb->data & ~(L1_CACHE_BYTES - 1)),
+					  (void *)msg->buf_pa, msg->size);
+
+		ret = xp_remote_memcpy((void *)((u64)skb->data &
+				                ~(L1_CACHE_BYTES - 1)),
+				       (void *)msg->buf_pa, msg->size);
 
-		if (bret != BTE_SUCCESS) {
+		if (ret != xpSuccess) {
 			/*
 			 * >>> Need better way of cleaning skb.  Currently skb
 			 * >>> appears in_use and we can't just call
 			 * >>> dev_kfree_skb.
 			 */
-			dev_err(xpnet, "bte_copy(0x%p, 0x%p, 0x%hx) returned "
-				"error=0x%x\n", (void *)msg->buf_pa,
-				(void *)__pa((u64)skb->data &
-					     ~(L1_CACHE_BYTES - 1)),
-				msg->size, bret);
+			dev_err(xpnet, "xp_remote_memcpy(0x%p, 0x%p, 0x%hx) "
+				"returned error=0x%x\n", (void *)
+				((u64)skb->data & ~(L1_CACHE_BYTES - 1)),
+				(void *)msg->buf_pa, msg->size, ret);
 
 			xpc_received(partid, channel, (void *)msg);
 

  parent reply	other threads:[~2008-06-06 16:47 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-06-06 16:40 [Patch 00/18] continued prepartion of XPC/XPNET to support SGI UV Dean Nelson
2008-06-06 16:42 ` [Patch 01/18] define is_shub() and is_uv() macros Dean Nelson
2008-06-06 16:43 ` [Patch 02/18] define xpSalError reason code Dean Nelson
2008-06-06 16:44 ` [Patch 03/18] define BYTES_PER_WORD Dean Nelson
2008-06-09  0:12   ` Andrew Morton
2008-06-06 16:46 ` [Patch 04/18] support runtime selection of xp_max_npartitions Dean Nelson
2008-06-06 16:47 ` Dean Nelson [this message]
2008-06-06 16:48 ` [Patch 06/18] prepare xpc_rsvd_page to work on either sn2 or uv hardware Dean Nelson
2008-06-06 16:49 ` [Patch 07/18] isolate xpc_vars_part structure to sn2 only Dean Nelson
2008-06-06 16:51 ` [Patch 08/18] isolate xpc_vars " Dean Nelson
2008-06-06 16:52 ` [Patch 09/18] base xpc_rsvd_page's timestamp on jiffies Dean Nelson
2008-06-09  0:15   ` Andrew Morton
2008-06-06 16:53 ` [Patch 10/18] move xpc_allocate() into xpc_send()/xpc_send_notify() Dean Nelson
2008-06-06 16:54 ` [Patch 11/18] isolate activate IRQ's hardware specific components Dean Nelson
2008-06-06 16:55 ` [Patch 12/18] isolate additional sn2 specific code Dean Nelson
2008-06-06 16:56 ` [Patch 13/18] separate chctl_flags from XPC's notify IRQ Dean Nelson
2008-06-06 16:58 ` [Patch 14/18] replace AMO_t typedef by struct amo Dean Nelson
2008-06-06 16:59 ` [Patch 15/18] isolate allocation of XPC's msgqueues to sn2 only Dean Nelson
2008-06-06 17:00 ` [Patch 16/18] enable XPNET to handle more than 64 partitions Dean Nelson
2008-06-06 17:02 ` [Patch 17/18] isolate remote copy buffer to sn2 only Dean Nelson
2008-06-06 17:03 ` [Patch 18/18] add _sn2 suffix to a few variables Dean Nelson

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=20080606164713.GF13695@sgi.com \
    --to=dcn@sgi.com \
    --cc=akpm@linux-foundation.org \
    --cc=linux-kernel@vger.kernel.org \
    /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.