DPDK patches and discussions
 help / color / mirror / Atom feed
From: Slawomir Mrozowicz <slawomirx.mrozowicz@intel.com>
To: dev@dpdk.org
Cc: Slawomir Mrozowicz <slawomirx.mrozowicz@intel.com>,
	Declan Doherty <declan.doherty@intel.com>,
	Marcin Kerlin <marcinx.kerlin@intel.com>
Subject: [dpdk-dev] [PATCH v4 1/4] cryptodev: add functions to retrieve device info
Date: Wed, 25 Jan 2017 17:27:32 +0100	[thread overview]
Message-ID: <1485361655-21519-2-git-send-email-slawomirx.mrozowicz@intel.com> (raw)
In-Reply-To: <1485361655-21519-1-git-send-email-slawomirx.mrozowicz@intel.com>

This patch adds helper functions for new performance application which
provide identifiers and number of crypto device and
provide and check capabilities available for defined device and algorithm.
The performance application can be used to measure throughput and latency
of cryptography operation performed by crypto device.

Signed-off-by: Declan Doherty <declan.doherty@intel.com>
Signed-off-by: Slawomir Mrozowicz <slawomirx.mrozowicz@intel.com>
Signed-off-by: Marcin Kerlin <marcinx.kerlin@intel.com>
---
To be applied on top of:
  [dpdk-dev,v4] crypto/aesni_gcm: migration from MB library to ISA-L

v2 changes:
- code style fix
- add information in version.map about changes

v3 changes:
- title of the cryptodev patch
- supply information in version.map about changes

v4 changes:
- change name of functions
---
 lib/librte_cryptodev/rte_crypto_sym.h          |  16 +++
 lib/librte_cryptodev/rte_cryptodev.c           | 183 ++++++++++++++++++++++++-
 lib/librte_cryptodev/rte_cryptodev.h           | 147 +++++++++++++-------
 lib/librte_cryptodev/rte_cryptodev_version.map |   8 ++
 4 files changed, 306 insertions(+), 48 deletions(-)

diff --git a/lib/librte_cryptodev/rte_crypto_sym.h b/lib/librte_cryptodev/rte_crypto_sym.h
index 0e20b30..c782588 100644
--- a/lib/librte_cryptodev/rte_crypto_sym.h
+++ b/lib/librte_cryptodev/rte_crypto_sym.h
@@ -112,6 +112,10 @@ enum rte_crypto_cipher_algorithm {
 
 };
 
+/** Cipher algorithm name strings */
+extern const char *
+rte_crypto_cipher_algorithm_strings[];
+
 /** Symmetric Cipher Direction */
 enum rte_crypto_cipher_operation {
 	RTE_CRYPTO_CIPHER_OP_ENCRYPT,
@@ -120,6 +124,10 @@ enum rte_crypto_cipher_operation {
 	/**< Decrypt cipher operation */
 };
 
+/** Cipher operation name strings */
+extern const char *
+rte_crypto_cipher_operation_strings[];
+
 /**
  * Symmetric Cipher Setup Data.
  *
@@ -245,12 +253,20 @@ enum rte_crypto_auth_algorithm {
 	RTE_CRYPTO_AUTH_LIST_END
 };
 
+/** Authentication algorithm name strings */
+extern const char *
+rte_crypto_auth_algorithm_strings[];
+
 /** Symmetric Authentication / Hash Operations */
 enum rte_crypto_auth_operation {
 	RTE_CRYPTO_AUTH_OP_VERIFY,	/**< Verify authentication digest */
 	RTE_CRYPTO_AUTH_OP_GENERATE	/**< Generate authentication digest */
 };
 
+/** Authentication operation name strings */
+extern const char *
+rte_crypto_auth_operation_strings[];
+
 /**
  * Authentication / Hash transform data.
  *
diff --git a/lib/librte_cryptodev/rte_cryptodev.c b/lib/librte_cryptodev/rte_cryptodev.c
index d4f170b..e557e77 100644
--- a/lib/librte_cryptodev/rte_cryptodev.c
+++ b/lib/librte_cryptodev/rte_cryptodev.c
@@ -1,7 +1,7 @@
 /*-
  *   BSD LICENSE
  *
- *   Copyright(c) 2015-2016 Intel Corporation. All rights reserved.
+ *   Copyright(c) 2015-2017 Intel Corporation. All rights reserved.
  *
  *   Redistribution and use in source and binary forms, with or without
  *   modification, are permitted provided that the following conditions
@@ -113,6 +113,86 @@ static const char *cryptodev_vdev_valid_params[] = {
 	RTE_CRYPTODEV_VDEV_SOCKET_ID
 };
 
+/**
+ * The crypto cipher algorithm strings identifiers.
+ * It could be used in application command line.
+ */
+const char *
+rte_crypto_cipher_algorithm_strings[] = {
+	[RTE_CRYPTO_CIPHER_3DES_CBC]	= "3des-cbc",
+	[RTE_CRYPTO_CIPHER_3DES_ECB]	= "3des-ecb",
+	[RTE_CRYPTO_CIPHER_3DES_CTR]	= "3des-ctr",
+
+	[RTE_CRYPTO_CIPHER_AES_CBC]	= "aes-cbc",
+	[RTE_CRYPTO_CIPHER_AES_CCM]	= "aes-ccm",
+	[RTE_CRYPTO_CIPHER_AES_CTR]	= "aes-ctr",
+	[RTE_CRYPTO_CIPHER_AES_ECB]	= "aes-ecb",
+	[RTE_CRYPTO_CIPHER_AES_GCM]	= "aes-gcm",
+	[RTE_CRYPTO_CIPHER_AES_F8]	= "aes-f8",
+	[RTE_CRYPTO_CIPHER_AES_XTS]	= "aes-xts",
+
+	[RTE_CRYPTO_CIPHER_ARC4]	= "arc4",
+
+	[RTE_CRYPTO_CIPHER_NULL]	= "null",
+
+	[RTE_CRYPTO_CIPHER_KASUMI_F8]	= "kasumi-f8",
+	[RTE_CRYPTO_CIPHER_SNOW3G_UEA2]	= "snow3g-uea2",
+	[RTE_CRYPTO_CIPHER_ZUC_EEA3]	= "zuc-eea3"
+};
+
+/**
+ * The crypto cipher operation strings identifiers.
+ * It could be used in application command line.
+ */
+const char *
+rte_crypto_cipher_operation_strings[] = {
+		[RTE_CRYPTO_CIPHER_OP_ENCRYPT]	= "encrypt",
+		[RTE_CRYPTO_CIPHER_OP_DECRYPT]	= "decrypt"
+};
+
+/**
+ * The crypto auth algorithm strings identifiers.
+ * It could be used in application command line.
+ */
+const char *
+rte_crypto_auth_algorithm_strings[] = {
+	[RTE_CRYPTO_AUTH_AES_CBC_MAC]	= "aes-cbc-mac",
+	[RTE_CRYPTO_AUTH_AES_CCM]	= "aes-ccm",
+	[RTE_CRYPTO_AUTH_AES_CMAC]	= "aes-cmac",
+	[RTE_CRYPTO_AUTH_AES_GCM]	= "aes-gcm",
+	[RTE_CRYPTO_AUTH_AES_GMAC]	= "aes-gmac",
+	[RTE_CRYPTO_AUTH_AES_XCBC_MAC]	= "aes-xcbc-mac",
+
+	[RTE_CRYPTO_AUTH_MD5]		= "md5",
+	[RTE_CRYPTO_AUTH_MD5_HMAC]	= "md5-hmac",
+
+	[RTE_CRYPTO_AUTH_SHA1]		= "sha1",
+	[RTE_CRYPTO_AUTH_SHA1_HMAC]	= "sha1-hmac",
+
+	[RTE_CRYPTO_AUTH_SHA224]	= "sha2-224",
+	[RTE_CRYPTO_AUTH_SHA224_HMAC]	= "sha2-224-hmac",
+	[RTE_CRYPTO_AUTH_SHA256]	= "sha2-256",
+	[RTE_CRYPTO_AUTH_SHA256_HMAC]	= "sha2-256-hmac",
+	[RTE_CRYPTO_AUTH_SHA384]	= "sha2-384",
+	[RTE_CRYPTO_AUTH_SHA384_HMAC]	= "sha2-384-hmac",
+	[RTE_CRYPTO_AUTH_SHA512]	= "sha2-512",
+	[RTE_CRYPTO_AUTH_SHA512_HMAC]	= "sha2-512-hmac",
+
+	[RTE_CRYPTO_AUTH_KASUMI_F9]	= "kasumi-f9",
+	[RTE_CRYPTO_AUTH_SNOW3G_UIA2]	= "snow3g-uia2",
+	[RTE_CRYPTO_AUTH_ZUC_EIA3]	= "zuc-eia3"
+};
+
+/**
+ * The crypto auth operation strings identifiers.
+ * It could be used in application command line.
+ */
+const char *
+rte_crypto_auth_operation_strings[] = {
+		[RTE_CRYPTO_AUTH_OP_VERIFY]	= "verify",
+		[RTE_CRYPTO_AUTH_OP_GENERATE]	= "generate"
+};
+
 static uint8_t
 number_of_sockets(void)
 {
@@ -218,6 +298,73 @@ rte_cryptodev_parse_vdev_init_params(struct rte_crypto_vdev_init_params *params,
 	return ret;
 }
 
+const struct rte_cryptodev_symmetric_capability *
+rte_cryptodev_sym_capability_get(uint8_t dev_id,
+		const struct rte_cryptodev_sym_capability_idx *idx)
+{
+	const struct rte_cryptodev_capabilities *capability;
+	struct rte_cryptodev_info dev_info;
+	int i = 0;
+
+	rte_cryptodev_info_get(dev_id, &dev_info);
+
+	while ((capability = &dev_info.capabilities[i++])->op !=
+			RTE_CRYPTO_OP_TYPE_UNDEFINED) {
+		if (capability->op != RTE_CRYPTO_OP_TYPE_SYMMETRIC)
+			continue;
+
+		if (capability->sym.xform_type != idx->type)
+			continue;
+
+		if (idx->type == RTE_CRYPTO_SYM_XFORM_AUTH &&
+			capability->sym.auth.algo == idx->algo.auth)
+			return &capability->sym;
+
+		if (idx->type == RTE_CRYPTO_SYM_XFORM_CIPHER &&
+			capability->sym.cipher.algo == idx->algo.cipher)
+			return &capability->sym;
+	}
+
+	return NULL;
+
+}
+
+#define param_range_check(x, y) \
+	(((x < y.min) || (x > y.max)) || \
+	(y.increment != 0 && (x % y.increment) != 0))
+
+int
+rte_cryptodev_sym_capability_check_cipher(
+		const struct rte_cryptodev_symmetric_capability *capability,
+		uint16_t key_size, uint16_t iv_size)
+{
+	if (param_range_check(key_size, capability->cipher.key_size))
+		return -1;
+
+	if (param_range_check(iv_size, capability->cipher.iv_size))
+		return -1;
+
+	return 0;
+}
+
+int
+rte_cryptodev_sym_capability_check_auth(
+		const struct rte_cryptodev_symmetric_capability *capability,
+		uint16_t key_size, uint16_t digest_size, uint16_t aad_size)
+{
+	if (param_range_check(key_size, capability->auth.key_size))
+		return -1;
+
+	if (param_range_check(digest_size, capability->auth.digest_size))
+		return -1;
+
+	if (param_range_check(aad_size, capability->auth.aad_size))
+		return -1;
+
+	return 0;
+}
+
+
 const char *
 rte_cryptodev_get_feature_name(uint64_t flag)
 {
@@ -336,6 +483,40 @@ rte_cryptodev_count_devtype(enum rte_cryptodev_type type)
 }
 
 int
+rte_cryptodev_devices_get(const char *dev_name, uint8_t *devices,
+	uint8_t nb_devices)
+{
+	uint8_t i, cmp, count = 0;
+	struct rte_cryptodev **devs = &rte_cryptodev_globals->devs;
+	struct rte_device *dev;
+
+	for (i = 0; i < rte_cryptodev_globals->max_devs && count < nb_devices;
+			i++) {
+
+		if ((*devs + i)
+				&& (*devs + i)->attached ==
+						RTE_CRYPTODEV_ATTACHED) {
+
+			dev = (*devs + i)->device;
+
+			if (dev)
+				cmp = strncmp(dev->driver->name,
+						dev_name,
+						strlen(dev_name));
+			else
+				cmp = strncmp((*devs + i)->data->name,
+						dev_name,
+						strlen(dev_name));
+
+			if (cmp == 0)
+				devices[count++] = (*devs + i)->data->dev_id;
+		}
+	}
+
+	return count;
+}
+
+int
 rte_cryptodev_socket_id(uint8_t dev_id)
 {
 	struct rte_cryptodev *dev;
diff --git a/lib/librte_cryptodev/rte_cryptodev.h b/lib/librte_cryptodev/rte_cryptodev.h
index 6930442..67d0f84 100644
--- a/lib/librte_cryptodev/rte_cryptodev.h
+++ b/lib/librte_cryptodev/rte_cryptodev.h
@@ -1,6 +1,6 @@
 /*-
  *
- *   Copyright(c) 2015-2016 Intel Corporation. All rights reserved.
+ *   Copyright(c) 2015-2017 Intel Corporation. All rights reserved.
  *
  *   Redistribution and use in source and binary forms, with or without
  *   modification, are permitted provided that the following conditions
@@ -113,6 +113,20 @@ extern const char **rte_cyptodev_names;
 #endif
 
 /**
+ * Crypto parameters range description
+ */
+struct rte_crypto_param_range {
+	uint16_t min;	/**< minimum size */
+	uint16_t max;	/**< maximum size */
+	uint16_t increment;
+	/**< if a range of sizes are supported,
+	 * this parameter is used to indicate
+	 * increments in byte size that are supported
+	 * between the minimum and maximum
+	 */
+};
+
+/**
  * Symmetric Crypto Capability
  */
 struct rte_cryptodev_symmetric_capability {
@@ -125,35 +139,11 @@ struct rte_cryptodev_symmetric_capability {
 			/**< authentication algorithm */
 			uint16_t block_size;
 			/**< algorithm block size */
-			struct {
-				uint16_t min;	/**< minimum key size */
-				uint16_t max;	/**< maximum key size */
-				uint16_t increment;
-				/**< if a range of sizes are supported,
-				 * this parameter is used to indicate
-				 * increments in byte size that are supported
-				 * between the minimum and maximum */
-			} key_size;
+			struct rte_crypto_param_range key_size;
 			/**< auth key size range */
-			struct {
-				uint16_t min;	/**< minimum digest size */
-				uint16_t max;	/**< maximum digest size */
-				uint16_t increment;
-				/**< if a range of sizes are supported,
-				 * this parameter is used to indicate
-				 * increments in byte size that are supported
-				 * between the minimum and maximum */
-			} digest_size;
+			struct rte_crypto_param_range digest_size;
 			/**< digest size range */
-			struct {
-				uint16_t min;	/**< minimum aad size */
-				uint16_t max;	/**< maximum aad size */
-				uint16_t increment;
-				/**< if a range of sizes are supported,
-				 * this parameter is used to indicate
-				 * increments in byte size that are supported
-				 * between the minimum and maximum */
-			} aad_size;
+			struct rte_crypto_param_range aad_size;
 			/**< Additional authentication data size range */
 		} auth;
 		/**< Symmetric Authentication transform capabilities */
@@ -162,25 +152,9 @@ struct rte_cryptodev_symmetric_capability {
 			/**< cipher algorithm */
 			uint16_t block_size;
 			/**< algorithm block size */
-			struct {
-				uint16_t min;	/**< minimum key size */
-				uint16_t max;	/**< maximum key size */
-				uint16_t increment;
-				/**< if a range of sizes are supported,
-				 * this parameter is used to indicate
-				 * increments in byte size that are supported
-				 * between the minimum and maximum */
-			} key_size;
+			struct rte_crypto_param_range key_size;
 			/**< cipher key size range */
-			struct {
-				uint16_t min;	/**< minimum iv size */
-				uint16_t max;	/**< maximum iv size */
-				uint16_t increment;
-				/**< if a range of sizes are supported,
-				 * this parameter is used to indicate
-				 * increments in byte size that are supported
-				 * between the minimum and maximum */
-			} iv_size;
+			struct rte_crypto_param_range iv_size;
 			/**< Initialisation vector data size range */
 		} cipher;
 		/**< Symmetric Cipher transform capabilities */
@@ -199,6 +173,64 @@ struct rte_cryptodev_capabilities {
 	};
 };
 
+/** Structure used to describe crypto algorithms */
+struct rte_cryptodev_sym_capability_idx {
+	enum rte_crypto_sym_xform_type type;
+	union {
+		enum rte_crypto_cipher_algorithm cipher;
+		enum rte_crypto_auth_algorithm auth;
+	} algo;
+};
+
+/**
+ *  Provide capabilities available for defined device and algorithm
+ *
+ * @param	dev_id		The identifier of the device.
+ * @param	idx		Describtion of crypto algorithms.
+ *
+ * @return
+ *   - Return description of the symmetric crypto capability if exist.
+ *   - Return NULL if the capability not exist.
+ */
+const struct rte_cryptodev_symmetric_capability *
+rte_cryptodev_sym_capability_get(uint8_t dev_id,
+		const struct rte_cryptodev_sym_capability_idx *idx);
+
+/**
+ * Check if key size and initial vector are supported
+ * in crypto cipher capability
+ *
+ * @param	capability	Description of the symmetric crypto capability.
+ * @param	key_size	Cipher key size.
+ * @param	iv_size		Cipher initial vector size.
+ *
+ * @return
+ *   - Return 0 if the parameters are in range of the capability.
+ *   - Return -1 if the parameters are out of range of the capability.
+ */
+int
+rte_cryptodev_sym_capability_check_cipher(
+		const struct rte_cryptodev_symmetric_capability *capability,
+		uint16_t key_size, uint16_t iv_size);
+
+/**
+ * Check if key size and initial vector are supported
+ * in crypto auth capability
+ *
+ * @param	capability	Description of the symmetric crypto capability.
+ * @param	key_size	Auth key size.
+ * @param	digest_size	Auth digest size.
+ * @param	aad_size	Auth aad size.
+ *
+ * @return
+ *   - Return 0 if the parameters are in range of the capability.
+ *   - Return -1 if the parameters are out of range of the capability.
+ */
+int
+rte_cryptodev_sym_capability_check_auth(
+		const struct rte_cryptodev_symmetric_capability *capability,
+		uint16_t key_size, uint16_t digest_size, uint16_t aad_size);
+
 /** Macro used at end of crypto PMD list */
 #define RTE_CRYPTODEV_END_OF_CAPABILITIES_LIST() \
 	{ RTE_CRYPTO_OP_TYPE_UNDEFINED }
@@ -238,7 +270,6 @@ struct rte_cryptodev_capabilities {
 /**< Utilises ARM CPU Cryptographic Extensions */
 
 
-
 /**
  * Get the name of a crypto device feature flag
  *
@@ -380,8 +411,30 @@ rte_cryptodev_get_dev_id(const char *name);
 extern uint8_t
 rte_cryptodev_count(void);
 
+/**
+ * Get number of crypto device defined type.
+ *
+ * @param	type	type of device.
+ *
+ * @return
+ *   Returns number of crypto device.
+ */
 extern uint8_t
 rte_cryptodev_count_devtype(enum rte_cryptodev_type type);
+
+/**
+ * Get number and identifiers of attached crypto device.
+ *
+ * @param	dev_name	device name.
+ * @param	devices		output devices identifiers.
+ * @param	nb_devices	maximal number of devices.
+ *
+ * @return
+ *   Returns number of attached crypto device.
+ */
+int
+rte_cryptodev_devices_get(const char *dev_name, uint8_t *devices,
+		uint8_t nb_devices);
 /*
  * Return the NUMA socket to which a device is connected
  *
diff --git a/lib/librte_cryptodev/rte_cryptodev_version.map b/lib/librte_cryptodev/rte_cryptodev_version.map
index a92df62..238bf13 100644
--- a/lib/librte_cryptodev/rte_cryptodev_version.map
+++ b/lib/librte_cryptodev/rte_cryptodev_version.map
@@ -50,9 +50,17 @@ DPDK_16.11 {
 DPDK_17.02 {
 	global:
 
+	rte_cryptodev_devices_get;
 	rte_cryptodev_pmd_create_dev_name;
 	rte_cryptodev_pmd_get_dev;
 	rte_cryptodev_pmd_get_named_dev;
 	rte_cryptodev_pmd_is_valid_dev;
+	rte_cryptodev_sym_capability_check_auth;
+	rte_cryptodev_sym_capability_check_cipher;
+	rte_cryptodev_sym_capability_get;
+	rte_crypto_auth_algorithm_strings;
+	rte_crypto_auth_operation_strings;
+	rte_crypto_cipher_algorithm_strings;
+	rte_crypto_cipher_operation_strings;
 
 } DPDK_16.11;
-- 
2.5.0

  parent reply	other threads:[~2017-01-25 14:28 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-12-02 15:15 [dpdk-dev] [PATCH 0/2] Introduce new performance test application Michal Jastrzebski
2016-12-02 15:15 ` [dpdk-dev] [PATCH 1/2] lib/librte_cryptodev: functions for " Michal Jastrzebski
2016-12-02 15:15 ` [dpdk-dev] [PATCH 2/2] app/crypto-perf: Introduce " Michal Jastrzebski
2017-01-05 16:49 ` [dpdk-dev] [PATCH v2 0/4] " Slawomir Mrozowicz
2017-01-05 16:49   ` [dpdk-dev] [PATCH v2 1/3] eal: add quiet mode to suppress log output to stdout Slawomir Mrozowicz
2017-01-05 15:40     ` Thomas Monjalon
2017-01-10 10:41       ` Declan Doherty
2017-01-05 16:50   ` [dpdk-dev] [PATCH v2 2/3] lib/librte_cryptodev: functions for new performance test application Slawomir Mrozowicz
2017-01-06 12:21     ` De Lara Guarch, Pablo
2017-01-09  9:11       ` Mrozowicz, SlawomirX
2017-01-05 16:50   ` [dpdk-dev] [PATCH v2 3/3] app/crypto-perf: introduce " Slawomir Mrozowicz
2017-01-09 14:51     ` De Lara Guarch, Pablo
2017-01-10  7:28       ` Mrozowicz, SlawomirX
2017-01-09 15:24     ` Thomas Monjalon
2017-01-10  8:52       ` Mrozowicz, SlawomirX
2017-01-11 16:07   ` [dpdk-dev] [PATCH v3 0/2] Introduce " Slawomir Mrozowicz
2017-01-11 16:07     ` [dpdk-dev] [PATCH v3 1/2] cryptodev: add functions to retrieve device info Slawomir Mrozowicz
2017-01-11 15:21       ` Trahe, Fiona
2017-01-11 16:07     ` [dpdk-dev] [PATCH v3 2/2] app/crypto-perf: introduce new performance test application Slawomir Mrozowicz
2017-01-25 16:27     ` [dpdk-dev] [PATCH v4 0/4] Introduce " Slawomir Mrozowicz
2017-01-25 15:59       ` De Lara Guarch, Pablo
2017-01-26  9:43         ` De Lara Guarch, Pablo
2017-01-25 16:27       ` Slawomir Mrozowicz [this message]
2017-01-25 16:27       ` [dpdk-dev] [PATCH v4 2/4] app/crypto-perf: introduce " Slawomir Mrozowicz
2017-01-25 16:27       ` [dpdk-dev] [PATCH v4 3/4] app/crypto-perf: add test vectors files Slawomir Mrozowicz
2017-01-25 16:27       ` [dpdk-dev] [PATCH v4 4/4] doc: describe new performance test application Slawomir Mrozowicz
2017-01-26 15:21         ` Ferruh Yigit
2017-01-26 15:23           ` Richardson, Bruce
2017-01-26 17:53           ` Thomas Monjalon

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=1485361655-21519-2-git-send-email-slawomirx.mrozowicz@intel.com \
    --to=slawomirx.mrozowicz@intel.com \
    --cc=declan.doherty@intel.com \
    --cc=dev@dpdk.org \
    --cc=marcinx.kerlin@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 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).