mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Terry Bowman <terry.bowman@amd.com>
To: <dave@stgolabs.net>, <jonathan.cameron@huawei.com>,
	<dave.jiang@intel.com>, <alison.schofield@intel.com>,
	<dan.j.williams@intel.com>, <bhelgaas@google.com>,
	<shiju.jose@huawei.com>, <ming.li@zohomail.com>,
	<Smita.KoralahalliChannabasappa@amd.com>, <rrichter@amd.com>,
	<dan.carpenter@linaro.org>, <PradeepVineshReddy.Kodamati@amd.com>,
	<lukas@wunner.de>, <Benjamin.Cheatham@amd.com>,
	<sathyanarayanan.kuppuswamy@linux.intel.com>,
	<linux-cxl@vger.kernel.org>, <alucerop@amd.com>,
	<ira.weiny@intel.com>
Cc: <linux-kernel@vger.kernel.org>, <linux-pci@vger.kernel.org>
Subject: [PATCH v11 17/23] CXL/AER: Introduce cxl_aer.c into AER driver for forwarding CXL errors
Date: Tue, 26 Aug 2025 20:35:32 -0500	[thread overview]
Message-ID: <20250827013539.903682-18-terry.bowman@amd.com> (raw)
In-Reply-To: <20250827013539.903682-1-terry.bowman@amd.com>

CXL virtual hierarchy (VH) RAS handling for CXL Port devices will be added
soon. This requires a notification mechanism for the AER driver to share
the AER interrupt with the CXL driver. The notification will be used as an
indication for the CXL drivers to handle and log the CXL RAS errors.

Note, 'CXL protocol error' terminology will refer to CXL VH and not
CXL RCH errors unless specifically noted going forward.

Introduce a new file in the AER driver to handle the CXL protocol errors
named pci/pcie/cxl_aer.c.

Add a kfifo work queue to be used by the AER and CXL drivers. The AER
driver will be the sole kfifo producer adding work and the cxl_core will be
the sole kfifo consumer removing work. Add the boilerplate kfifo support.
Encapsulate the kfifo, RW semaphore, and work pointer in a single structure.

Add CXL work queue handler registration functions in the AER driver. Export
the functions allowing CXL driver to access. Implement registration
functions for the CXL driver to assign or clear the work handler function.
Synchronize accesses using the RW semaphore.

Introduce 'struct cxl_proto_err_work_data' to serve as the kfifo work data.
This will contain a reference to the erring PCI device and the error
severity. This will be used when the work is dequeued by the cxl_core driver.

Signed-off-by: Terry Bowman <terry.bowman@amd.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>

---
Changes in v10->v11:
- Move RCH implementation to cxl_march.c and RCH declarations to pci/pci.h. (Terry)
- Introduce 'struct cxl_proto_err_kfifo' containing semaphore, fifo,
and work struct. (Dan)
- Remove embedded struct from cxl_proto_err_work (Dan)
- Make 'struct work_struct *cxl_proto_err_work' definition static (Jonathan)
- Add check for NULL cxl_proto_err_kfifo to determine if CXL driver is
not registered for workqueue. (Dan)
---
 drivers/pci/pci.h          |   4 ++
 drivers/pci/pcie/Makefile  |   1 +
 drivers/pci/pcie/aer.c     |  50 ++--------------
 drivers/pci/pcie/cxl_aer.c | 120 +++++++++++++++++++++++++++++++++++++
 include/linux/aer.h        |  21 +++++++
 5 files changed, 150 insertions(+), 46 deletions(-)
 create mode 100644 drivers/pci/pcie/cxl_aer.c

diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 417a088d815f..cfa75903dd3f 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -1179,10 +1179,14 @@ static inline void cxl_rch_enable_rcec(struct pci_dev *rcec) { }
 void pci_aer_unmask_internal_errors(struct pci_dev *dev);
 bool cxl_error_is_native(struct pci_dev *dev);
 bool is_internal_error(struct aer_err_info *info);
+bool is_cxl_error(struct pci_dev *pdev, struct aer_err_info *info);
+void cxl_forward_error(struct pci_dev *pdev, struct aer_err_info *info);
 #else
 static inline void pci_aer_unmask_internal_errors(struct pci_dev *dev) { }
 static inline bool cxl_error_is_native(struct pci_dev *dev) { return false; }
 static inline bool is_internal_error(struct aer_err_info *info) { return false; }
+static inline bool is_cxl_error(struct pci_dev *pdev, struct aer_err_info *info) { return false; }
+static inline void cxl_forward_error(struct pci_dev *pdev, struct aer_err_info *info) { }
 #endif
 
 #endif /* DRIVERS_PCI_H */
diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile
index 07c299dbcdd7..bfe5bb5a3c89 100644
--- a/drivers/pci/pcie/Makefile
+++ b/drivers/pci/pcie/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_PCIEPORTBUS)	+= pcieportdrv.o bwctrl.o
 obj-y				+= aspm.o
 obj-$(CONFIG_PCIEAER)		+= aer.o err.o tlp.o
 obj-$(CONFIG_CXL_RCH_RAS)	+= rch_aer.o
+obj-$(CONFIG_CXL_RAS)		+= cxl_aer.o
 obj-$(CONFIG_PCIEAER_INJECT)	+= aer_inject.o
 obj-$(CONFIG_PCIE_PME)		+= pme.o
 obj-$(CONFIG_PCIE_DPC)		+= dpc.o
diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c
index ed1de9256898..627d89ccea9c 100644
--- a/drivers/pci/pcie/aer.c
+++ b/drivers/pci/pcie/aer.c
@@ -1092,51 +1092,6 @@ static bool find_source_device(struct pci_dev *parent,
 	return true;
 }
 
-#ifdef CONFIG_CXL_RAS
-
-/**
- * pci_aer_unmask_internal_errors - unmask internal errors
- * @dev: pointer to the pci_dev data structure
- *
- * Unmask internal errors in the Uncorrectable and Correctable Error
- * Mask registers.
- *
- * Note: AER must be enabled and supported by the device which must be
- * checked in advance, e.g. with pcie_aer_is_native().
- */
-void pci_aer_unmask_internal_errors(struct pci_dev *dev)
-{
-	int aer = dev->aer_cap;
-	u32 mask;
-
-	pci_read_config_dword(dev, aer + PCI_ERR_UNCOR_MASK, &mask);
-	mask &= ~PCI_ERR_UNC_INTN;
-	pci_write_config_dword(dev, aer + PCI_ERR_UNCOR_MASK, mask);
-
-	pci_read_config_dword(dev, aer + PCI_ERR_COR_MASK, &mask);
-	mask &= ~PCI_ERR_COR_INTERNAL;
-	pci_write_config_dword(dev, aer + PCI_ERR_COR_MASK, mask);
-}
-EXPORT_SYMBOL_NS_GPL(pci_aer_unmask_internal_errors, "CXL");
-
-bool cxl_error_is_native(struct pci_dev *dev)
-{
-	struct pci_host_bridge *host = pci_find_host_bridge(dev->bus);
-
-	return (pcie_ports_native || host->native_aer);
-}
-EXPORT_SYMBOL_NS_GPL(cxl_error_is_native, "CXL");
-
-bool is_internal_error(struct aer_err_info *info)
-{
-	if (info->severity == AER_CORRECTABLE)
-		return info->status & PCI_ERR_COR_INTERNAL;
-
-	return info->status & PCI_ERR_UNC_INTN;
-}
-EXPORT_SYMBOL_NS_GPL(is_internal_error, "CXL");
-#endif /* CONFIG_CXL_RAS */
-
 /**
  * pci_aer_handle_error - handle logging error into an event log
  * @dev: pointer to pci_dev data structure of error source device
@@ -1173,7 +1128,10 @@ static void pci_aer_handle_error(struct pci_dev *dev, struct aer_err_info *info)
 static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info)
 {
 	cxl_rch_handle_error(dev, info);
-	pci_aer_handle_error(dev, info);
+	if (is_cxl_error(dev, info))
+		cxl_forward_error(dev, info);
+	else
+		pci_aer_handle_error(dev, info);
 	pci_dev_put(dev);
 }
 
diff --git a/drivers/pci/pcie/cxl_aer.c b/drivers/pci/pcie/cxl_aer.c
new file mode 100644
index 000000000000..74e6d2d04ab6
--- /dev/null
+++ b/drivers/pci/pcie/cxl_aer.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright(c) 2025 AMD Corporation. All rights reserved. */
+
+#include <linux/pci.h>
+#include <linux/aer.h>
+#include <linux/pci.h>
+#include <linux/bitfield.h>
+#include <linux/kfifo.h>
+#include "../pci.h"
+
+#define CXL_ERROR_SOURCES_MAX          128
+
+struct cxl_proto_err_kfifo {
+	struct work_struct *work;
+	struct rw_semaphore rw_sema;
+	DECLARE_KFIFO(fifo, struct cxl_proto_err_work_data,
+		      CXL_ERROR_SOURCES_MAX);
+};
+
+static struct cxl_proto_err_kfifo cxl_proto_err_kfifo = {
+	.rw_sema = __RWSEM_INITIALIZER(cxl_proto_err_kfifo.rw_sema)
+};
+
+/**
+ * pci_aer_unmask_internal_errors - unmask internal errors
+ * @dev: pointer to the pci_dev data structure
+ *
+ * Unmask internal errors in the Uncorrectable and Correctable Error
+ * Mask registers.
+ *
+ * Note: AER must be enabled and supported by the device which must be
+ * checked in advance, e.g. with pcie_aer_is_native().
+ */
+void pci_aer_unmask_internal_errors(struct pci_dev *dev)
+{
+	int aer = dev->aer_cap;
+	u32 mask;
+
+	pci_read_config_dword(dev, aer + PCI_ERR_UNCOR_MASK, &mask);
+	mask &= ~PCI_ERR_UNC_INTN;
+	pci_write_config_dword(dev, aer + PCI_ERR_UNCOR_MASK, mask);
+
+	pci_read_config_dword(dev, aer + PCI_ERR_COR_MASK, &mask);
+	mask &= ~PCI_ERR_COR_INTERNAL;
+	pci_write_config_dword(dev, aer + PCI_ERR_COR_MASK, mask);
+}
+EXPORT_SYMBOL_NS_GPL(pci_aer_unmask_internal_errors, "CXL");
+
+bool cxl_error_is_native(struct pci_dev *dev)
+{
+	struct pci_host_bridge *host = pci_find_host_bridge(dev->bus);
+
+	return (pcie_ports_native || host->native_aer);
+}
+EXPORT_SYMBOL_NS_GPL(cxl_error_is_native, "CXL");
+
+bool is_internal_error(struct aer_err_info *info)
+{
+	if (info->severity == AER_CORRECTABLE)
+		return info->status & PCI_ERR_COR_INTERNAL;
+
+	return info->status & PCI_ERR_UNC_INTN;
+}
+EXPORT_SYMBOL_NS_GPL(is_internal_error, "CXL");
+
+bool is_cxl_error(struct pci_dev *pdev, struct aer_err_info *info)
+{
+	if (!info || !info->is_cxl)
+		return false;
+
+	if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ENDPOINT)
+		return false;
+
+	return is_internal_error(info);
+}
+EXPORT_SYMBOL_NS_GPL(is_cxl_error, "CXL");
+
+void cxl_forward_error(struct pci_dev *pdev, struct aer_err_info *info)
+{
+	struct cxl_proto_err_work_data wd = (struct cxl_proto_err_work_data) {
+		.severity = info->severity,
+		.pdev = pdev
+	};
+
+	guard(rwsem_write)(&cxl_proto_err_kfifo.rw_sema);
+
+	if (!cxl_proto_err_kfifo.work) {
+		dev_warn_once(&pdev->dev, "CXL driver is not registered for kfifo");
+		return;
+	}
+
+	if (!kfifo_put(&cxl_proto_err_kfifo.fifo, wd)) {
+		dev_err_ratelimited(&pdev->dev, "CXL kfifo overflow\n");
+		return;
+	}
+
+	schedule_work(cxl_proto_err_kfifo.work);
+}
+EXPORT_SYMBOL_NS_GPL(cxl_forward_error, "CXL");
+
+void cxl_register_proto_err_work(struct work_struct *work)
+{
+	guard(rwsem_write)(&cxl_proto_err_kfifo.rw_sema);
+	cxl_proto_err_kfifo.work = work;
+}
+EXPORT_SYMBOL_NS_GPL(cxl_register_proto_err_work, "CXL");
+
+void cxl_unregister_proto_err_work(void)
+{
+	guard(rwsem_write)(&cxl_proto_err_kfifo.rw_sema);
+	cxl_proto_err_kfifo.work = NULL;
+}
+EXPORT_SYMBOL_NS_GPL(cxl_unregister_proto_err_work, "CXL");
+
+int cxl_proto_err_kfifo_get(struct cxl_proto_err_work_data *wd)
+{
+	guard(rwsem_read)(&cxl_proto_err_kfifo.rw_sema);
+	return kfifo_get(&cxl_proto_err_kfifo.fifo, wd);
+}
+EXPORT_SYMBOL_NS_GPL(cxl_proto_err_kfifo_get, "CXL");
diff --git a/include/linux/aer.h b/include/linux/aer.h
index 02940be66324..f8eb32805957 100644
--- a/include/linux/aer.h
+++ b/include/linux/aer.h
@@ -10,6 +10,7 @@
 
 #include <linux/errno.h>
 #include <linux/types.h>
+#include <linux/workqueue_types.h>
 
 #define AER_NONFATAL			0
 #define AER_FATAL			1
@@ -53,6 +54,16 @@ struct aer_capability_regs {
 	u16 uncor_err_source;
 };
 
+/**
+ * struct cxl_proto_err_work_data - Error information used in CXL error handling
+ * @severity: AER severity
+ * @pdev: PCI device detecting the error
+ */
+struct cxl_proto_err_work_data {
+	int severity;
+	struct pci_dev *pdev;
+};
+
 #if defined(CONFIG_PCIEAER)
 int pci_aer_clear_nonfatal_status(struct pci_dev *dev);
 int pcie_aer_is_native(struct pci_dev *dev);
@@ -64,6 +75,16 @@ static inline int pci_aer_clear_nonfatal_status(struct pci_dev *dev)
 static inline int pcie_aer_is_native(struct pci_dev *dev) { return 0; }
 #endif
 
+#ifdef CONFIG_CXL_RAS
+int cxl_proto_err_kfifo_get(struct cxl_proto_err_work_data *wd);
+void cxl_register_proto_err_work(struct work_struct *work);
+void cxl_unregister_proto_err_work(void);
+#else
+static inline int cxl_proto_err_kfifo_get(struct cxl_proto_err_work_data *wd) { return 0; }
+static inline void cxl_register_proto_err_work(struct work_struct *work) { }
+static inline void cxl_unregister_proto_err_work(void) { }
+#endif
+
 void pci_print_aer(struct pci_dev *dev, int aer_severity,
 		    struct aer_capability_regs *aer);
 int cper_severity_to_aer(int cper_severity);
-- 
2.51.0.rc2.21.ge5ab6b3e5a


  parent reply	other threads:[~2025-08-27  1:39 UTC|newest]

Thread overview: 89+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-08-27  1:35 [PATCH v11 00/23] Enable CXL PCIe Port Protocol Error handling and logging Terry Bowman
2025-08-27  1:35 ` [PATCH v11 01/23] cxl: Remove ifdef blocks of CONFIG_PCIEAER_CXL from core/pci.c Terry Bowman
2025-08-27  1:35 ` [PATCH v11 02/23] CXL/AER: Remove CONFIG_PCIEAER_CXL and replace with CONFIG_CXL_RAS Terry Bowman
2025-08-29 15:24   ` Jonathan Cameron
2025-08-29 18:16   ` Sathyanarayanan Kuppuswamy
2025-08-27  1:35 ` [PATCH v11 03/23] cxl/pci: Remove unnecessary CXL Endpoint handling helper functions Terry Bowman
2025-08-28 15:28   ` Dave Jiang
2025-08-27  1:35 ` [PATCH v11 04/23] cxl/pci: Remove unnecessary CXL RCH " Terry Bowman
2025-08-28  8:35   ` Alejandro Lucero Palau
2025-08-28 17:32   ` Dave Jiang
2025-08-27  1:35 ` [PATCH v11 05/23] cxl: Move CXL driver RCH error handling into CONFIG_CXL_RCH_RAS conditional block Terry Bowman
2025-08-28  8:57   ` Alejandro Lucero Palau
2025-09-10 16:43     ` Bowman, Terry
2025-08-29 15:33   ` Jonathan Cameron
2025-09-11 17:48     ` Bowman, Terry
2025-09-11 19:41       ` Dave Jiang
2025-09-15 13:32         ` Bowman, Terry
2025-08-27  1:35 ` [PATCH v11 06/23] CXL/AER: Introduce rch_aer.c into AER driver for handling CXL RCH errors Terry Bowman
2025-08-28 20:53   ` Dave Jiang
2025-08-29  8:39     ` Lukas Wunner
2025-09-10 17:01       ` Bowman, Terry
2025-09-10 17:26         ` Dave Jiang
2025-09-12 13:59       ` Bowman, Terry
2025-09-12 19:09         ` Lukas Wunner
2025-09-10 16:56     ` Bowman, Terry
2025-09-10 12:43   ` Jonathan Cameron
2025-08-27  1:35 ` [PATCH v11 07/23] CXL/PCI: Move CXL DVSEC definitions into uapi/linux/pci_regs.h Terry Bowman
2025-08-27 14:51   ` Lukas Wunner
2025-08-29 15:42     ` Jonathan Cameron
2025-08-29 15:47     ` Jonathan Cameron
2025-08-28 21:07   ` Dave Jiang
2025-09-10 18:11     ` Bowman, Terry
2025-09-10 20:06       ` Dave Jiang
2025-08-27  1:35 ` [PATCH v11 08/23] PCI/CXL: Introduce pcie_is_cxl() Terry Bowman
2025-08-28  8:18   ` Alejandro Lucero Palau
2025-09-10 16:24     ` Bowman, Terry
2025-09-11  3:48       ` Lukas Wunner
2025-09-10 13:10   ` Jonathan Cameron
2025-08-27  1:35 ` [PATCH v11 09/23] PCI/AER: Report CXL or PCIe bus error type in trace logging Terry Bowman
2025-08-27  7:37   ` Lukas Wunner
2025-09-10 15:26     ` Bowman, Terry
2025-09-10 15:33       ` Lukas Wunner
2025-09-11  5:07   ` Lukas Wunner
2025-08-27  1:35 ` [PATCH v11 10/23] CXL/AER: Update PCI class code check to use FIELD_GET() Terry Bowman
2025-08-29 16:03   ` Jonathan Cameron
2025-09-11 18:18     ` Bowman, Terry
2025-08-27  1:35 ` [PATCH v11 11/23] cxl/pci: Update RAS handler interfaces to also support CXL Ports Terry Bowman
2025-08-27  1:35 ` [PATCH v11 12/23] cxl/pci: Log message if RAS registers are unmapped Terry Bowman
2025-08-27  1:35 ` [PATCH v11 13/23] cxl/pci: Unify CXL trace logging for CXL Endpoints and CXL Ports Terry Bowman
2025-08-27 11:55   ` Shiju Jose
2025-08-29 16:06     ` Jonathan Cameron
2025-08-27  1:35 ` [PATCH v11 14/23] cxl/pci: Update cxl_handle_cor_ras() to return early if no RAS errors Terry Bowman
2025-08-27  1:35 ` [PATCH v11 15/23] cxl/pci: Map CXL Endpoint Port and CXL Switch Port RAS registers Terry Bowman
2025-08-28 23:05   ` Dave Jiang
2025-09-10 18:40     ` Bowman, Terry
2025-08-27  1:35 ` [PATCH v11 16/23] cxl/pci: Introduce CXL Endpoint protocol error handlers Terry Bowman
2025-08-27  7:48   ` Lukas Wunner
2025-09-10 13:23   ` Jonathan Cameron
2025-08-27  1:35 ` Terry Bowman [this message]
2025-08-27  7:56   ` [PATCH v11 17/23] CXL/AER: Introduce cxl_aer.c into AER driver for forwarding CXL errors Lukas Wunner
2025-08-27  1:35 ` [PATCH v11 18/23] PCI/AER: Dequeue forwarded CXL error Terry Bowman
2025-08-29  0:43   ` Dave Jiang
2025-08-29  7:10     ` Lukas Wunner
2025-09-16 15:18       ` Bowman, Terry
2025-09-11 14:33     ` Bowman, Terry
2025-09-11 15:41       ` Dave Jiang
2025-09-11 16:47         ` Bowman, Terry
2025-09-11 19:45           ` Dave Jiang
2025-09-10 13:29   ` Jonathan Cameron
2025-08-27  1:35 ` [PATCH v11 19/23] CXL/PCI: Introduce CXL Port protocol error handlers Terry Bowman
2025-08-30  0:17   ` Dave Jiang
2025-08-27  1:35 ` [PATCH v11 20/23] CXL/PCI: Export and rename merge_result() to pci_ers_merge_result() Terry Bowman
2025-08-27  8:04   ` Lukas Wunner
2025-09-10 15:57     ` Bowman, Terry
2025-09-11  3:44       ` Lukas Wunner
2025-08-27 12:19   ` kernel test robot
2025-08-27  1:35 ` [PATCH v11 21/23] CXL/PCI: Introduce CXL uncorrectable protocol error recovery Terry Bowman
2025-09-03 22:30   ` Dave Jiang
2025-09-11 19:19     ` Bowman, Terry
2025-09-11 19:48       ` Dave Jiang
2025-09-10 13:33   ` Jonathan Cameron
2025-08-27  1:35 ` [PATCH v11 22/23] CXL/PCI: Enable CXL protocol errors during CXL Port probe Terry Bowman
2025-09-03 23:23   ` Dave Jiang
2025-08-27  1:35 ` [PATCH v11 23/23] CXL/PCI: Disable CXL protocol error interrupts during CXL Port cleanup Terry Bowman
2025-09-10 15:07   ` Gregory Price
2025-09-11 20:19     ` Bowman, Terry
2025-08-29  0:07 ` [PATCH v11 00/23] Enable CXL PCIe Port Protocol Error handling and logging Dave Jiang
2025-09-23  3:29 ` Gregory Price
2025-09-23  9:21   ` Srinivasulu Thanneeru

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=20250827013539.903682-18-terry.bowman@amd.com \
    --to=terry.bowman@amd.com \
    --cc=Benjamin.Cheatham@amd.com \
    --cc=PradeepVineshReddy.Kodamati@amd.com \
    --cc=Smita.KoralahalliChannabasappa@amd.com \
    --cc=alison.schofield@intel.com \
    --cc=alucerop@amd.com \
    --cc=bhelgaas@google.com \
    --cc=dan.carpenter@linaro.org \
    --cc=dan.j.williams@intel.com \
    --cc=dave.jiang@intel.com \
    --cc=dave@stgolabs.net \
    --cc=ira.weiny@intel.com \
    --cc=jonathan.cameron@huawei.com \
    --cc=linux-cxl@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=lukas@wunner.de \
    --cc=ming.li@zohomail.com \
    --cc=rrichter@amd.com \
    --cc=sathyanarayanan.kuppuswamy@linux.intel.com \
    --cc=shiju.jose@huawei.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

all inboxes | Powered by JetHome®