From: Ivan Malov <ivan.malov@oktetlabs.ru>
To: dev@dpdk.org
Cc: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>,
Andy Moreton <amoreton@xilinx.com>, Ray Kinsella <mdr@ashroe.eu>
Subject: [PATCH 3/4] common/sfc_efx/base: support MAC address edit actions in MAE
Date: Wed, 17 Nov 2021 14:44:37 +0300 [thread overview]
Message-ID: <20211117114438.8347-4-ivan.malov@oktetlabs.ru> (raw)
In-Reply-To: <20211117114438.8347-1-ivan.malov@oktetlabs.ru>
In a tunnel packet, these actions affect the inner header if
action DECAP is set; otherwise, they affect the outer header.
Adding these actions is done in two steps: add the action to
the action mask and indicate the MAC address entry ID to use.
This allows the user to check the order of actions first and
allocate resources when time comes to enable the action rule.
Signed-off-by: Ivan Malov <ivan.malov@oktetlabs.ru>
Reviewed-by: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>
Reviewed-by: Andy Moreton <amoreton@xilinx.com>
---
drivers/common/sfc_efx/base/efx.h | 60 +++++
drivers/common/sfc_efx/base/efx_impl.h | 4 +
drivers/common/sfc_efx/base/efx_mae.c | 303 ++++++++++++++++++++++++-
drivers/common/sfc_efx/version.map | 6 +
4 files changed, 363 insertions(+), 10 deletions(-)
diff --git a/drivers/common/sfc_efx/base/efx.h b/drivers/common/sfc_efx/base/efx.h
index f08a004536..62eead638a 100644
--- a/drivers/common/sfc_efx/base/efx.h
+++ b/drivers/common/sfc_efx/base/efx.h
@@ -4437,6 +4437,34 @@ extern __checkReturn efx_rc_t
efx_mae_action_set_populate_vlan_pop(
__in efx_mae_actions_t *spec);
+/*
+ * This always amends the outermost header. This way, for a tunnel
+ * packet, if action DECAP is not requested, this will affect the
+ * outer header; otherwise, the inner header will be updated.
+ *
+ * Use efx_mae_action_set_fill_in_dst_mac_id() to set ID of
+ * the allocated MAC address entry in the specification
+ * prior to action set allocation.
+ */
+LIBEFX_API
+extern __checkReturn efx_rc_t
+efx_mae_action_set_populate_set_dst_mac(
+ __in efx_mae_actions_t *spec);
+
+/*
+ * This always amends the outermost header. This way, for a tunnel
+ * packet, if action DECAP is not requested, this will affect the
+ * outer header; otherwise, the inner header will be updated.
+ *
+ * Use efx_mae_action_set_fill_in_src_mac_id() to set ID of
+ * the allocated MAC address entry in the specification
+ * prior to action set allocation.
+ */
+LIBEFX_API
+extern __checkReturn efx_rc_t
+efx_mae_action_set_populate_set_src_mac(
+ __in efx_mae_actions_t *spec);
+
/*
* This always amends the outermost header. This way, for a tunnel
* packet, if action DECAP is not requested, this will affect the
@@ -4570,6 +4598,38 @@ efx_mae_match_spec_outer_rule_id_set(
__in efx_mae_match_spec_t *spec,
__in const efx_mae_rule_id_t *or_idp);
+/* MAC address entry ID */
+typedef struct efx_mae_mac_id_s {
+ uint32_t id;
+} efx_mae_mac_id_t;
+
+LIBEFX_API
+extern __checkReturn efx_rc_t
+efx_mae_mac_addr_alloc(
+ __in efx_nic_t *enp,
+ __in uint8_t addr_bytes[EFX_MAC_ADDR_LEN],
+ __out efx_mae_mac_id_t *mac_idp);
+
+LIBEFX_API
+extern __checkReturn efx_rc_t
+efx_mae_mac_addr_free(
+ __in efx_nic_t *enp,
+ __in const efx_mae_mac_id_t *mac_idp);
+
+/* See description before efx_mae_action_set_populate_set_dst_mac(). */
+LIBEFX_API
+extern __checkReturn efx_rc_t
+efx_mae_action_set_fill_in_dst_mac_id(
+ __in efx_mae_actions_t *spec,
+ __in const efx_mae_mac_id_t *mac_idp);
+
+/* See description before efx_mae_action_set_populate_set_src_mac(). */
+LIBEFX_API
+extern __checkReturn efx_rc_t
+efx_mae_action_set_fill_in_src_mac_id(
+ __in efx_mae_actions_t *spec,
+ __in const efx_mae_mac_id_t *mac_idp);
+
/* Encap. header ID */
typedef struct efx_mae_eh_id_s {
uint32_t id;
diff --git a/drivers/common/sfc_efx/base/efx_impl.h b/drivers/common/sfc_efx/base/efx_impl.h
index eda41b4be0..d181040186 100644
--- a/drivers/common/sfc_efx/base/efx_impl.h
+++ b/drivers/common/sfc_efx/base/efx_impl.h
@@ -1740,6 +1740,8 @@ typedef enum efx_mae_action_e {
/* These actions are strictly ordered. */
EFX_MAE_ACTION_DECAP,
EFX_MAE_ACTION_VLAN_POP,
+ EFX_MAE_ACTION_SET_DST_MAC,
+ EFX_MAE_ACTION_SET_SRC_MAC,
EFX_MAE_ACTION_DECR_IP_TTL,
EFX_MAE_ACTION_VLAN_PUSH,
EFX_MAE_ACTION_COUNT,
@@ -1772,6 +1774,8 @@ typedef struct efx_mae_action_vlan_push_s {
} efx_mae_action_vlan_push_t;
typedef struct efx_mae_actions_rsrc_s {
+ efx_mae_mac_id_t emar_dst_mac_id;
+ efx_mae_mac_id_t emar_src_mac_id;
efx_mae_eh_id_t emar_eh_id;
efx_counter_t emar_counter_id;
} efx_mae_actions_rsrc_t;
diff --git a/drivers/common/sfc_efx/base/efx_mae.c b/drivers/common/sfc_efx/base/efx_mae.c
index 756c35788e..7b24e3fee4 100644
--- a/drivers/common/sfc_efx/base/efx_mae.c
+++ b/drivers/common/sfc_efx/base/efx_mae.c
@@ -1386,6 +1386,8 @@ efx_mae_action_set_spec_init(
goto fail1;
}
+ spec->ema_rsrc.emar_dst_mac_id.id = EFX_MAE_RSRC_ID_INVALID;
+ spec->ema_rsrc.emar_src_mac_id.id = EFX_MAE_RSRC_ID_INVALID;
spec->ema_rsrc.emar_eh_id.id = EFX_MAE_RSRC_ID_INVALID;
spec->ema_rsrc.emar_counter_id.id = EFX_MAE_RSRC_ID_INVALID;
@@ -1633,6 +1635,12 @@ static const efx_mae_action_desc_t efx_mae_actions[EFX_MAE_NACTIONS] = {
[EFX_MAE_ACTION_VLAN_POP] = {
.emad_add = efx_mae_action_set_add_vlan_pop
},
+ [EFX_MAE_ACTION_SET_DST_MAC] = {
+ .emad_add = efx_mae_action_set_no_op
+ },
+ [EFX_MAE_ACTION_SET_SRC_MAC] = {
+ .emad_add = efx_mae_action_set_no_op
+ },
[EFX_MAE_ACTION_DECR_IP_TTL] = {
.emad_add = efx_mae_action_set_no_op
},
@@ -1659,6 +1667,8 @@ static const efx_mae_action_desc_t efx_mae_actions[EFX_MAE_NACTIONS] = {
static const uint32_t efx_mae_action_ordered_map =
(1U << EFX_MAE_ACTION_DECAP) |
(1U << EFX_MAE_ACTION_VLAN_POP) |
+ (1U << EFX_MAE_ACTION_SET_DST_MAC) |
+ (1U << EFX_MAE_ACTION_SET_SRC_MAC) |
(1U << EFX_MAE_ACTION_DECR_IP_TTL) |
(1U << EFX_MAE_ACTION_VLAN_PUSH) |
/*
@@ -1778,6 +1788,44 @@ efx_mae_action_set_populate_vlan_pop(
EFX_MAE_ACTION_VLAN_POP, 0, NULL));
}
+ __checkReturn efx_rc_t
+efx_mae_action_set_populate_set_dst_mac(
+ __in efx_mae_actions_t *spec)
+{
+ efx_rc_t rc;
+
+ if (spec->ema_v2_is_supported == B_FALSE) {
+ rc = ENOTSUP;
+ goto fail1;
+ }
+
+ return (efx_mae_action_set_spec_populate(spec,
+ EFX_MAE_ACTION_SET_DST_MAC, 0, NULL));
+
+fail1:
+ EFSYS_PROBE1(fail1, efx_rc_t, rc);
+ return (rc);
+}
+
+ __checkReturn efx_rc_t
+efx_mae_action_set_populate_set_src_mac(
+ __in efx_mae_actions_t *spec)
+{
+ efx_rc_t rc;
+
+ if (spec->ema_v2_is_supported == B_FALSE) {
+ rc = ENOTSUP;
+ goto fail1;
+ }
+
+ return (efx_mae_action_set_spec_populate(spec,
+ EFX_MAE_ACTION_SET_SRC_MAC, 0, NULL));
+
+fail1:
+ EFSYS_PROBE1(fail1, efx_rc_t, rc);
+ return (rc);
+}
+
__checkReturn efx_rc_t
efx_mae_action_set_populate_decr_ip_ttl(
__in efx_mae_actions_t *spec)
@@ -2317,6 +2365,224 @@ efx_mae_match_spec_outer_rule_id_set(
return (0);
+fail3:
+ EFSYS_PROBE(fail3);
+fail2:
+ EFSYS_PROBE(fail2);
+fail1:
+ EFSYS_PROBE1(fail1, efx_rc_t, rc);
+ return (rc);
+}
+
+ __checkReturn efx_rc_t
+efx_mae_mac_addr_alloc(
+ __in efx_nic_t *enp,
+ __in uint8_t addr_bytes[EFX_MAC_ADDR_LEN],
+ __out efx_mae_mac_id_t *mac_idp)
+{
+ const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
+ efx_mcdi_req_t req;
+ EFX_MCDI_DECLARE_BUF(payload,
+ MC_CMD_MAE_MAC_ADDR_ALLOC_IN_LEN,
+ MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_LEN);
+ efx_mae_mac_id_t mac_id;
+ efx_rc_t rc;
+
+ EFX_STATIC_ASSERT(sizeof (mac_idp->id) ==
+ MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_LEN);
+
+ EFX_STATIC_ASSERT(EFX_MAE_RSRC_ID_INVALID ==
+ MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL);
+
+ if (encp->enc_mae_supported == B_FALSE) {
+ rc = ENOTSUP;
+ goto fail1;
+ }
+
+ if (encp->enc_mae_aset_v2_supported == B_FALSE) {
+ rc = ENOTSUP;
+ goto fail2;
+ }
+
+ req.emr_cmd = MC_CMD_MAE_MAC_ADDR_ALLOC;
+ req.emr_in_buf = payload;
+ req.emr_in_length = MC_CMD_MAE_MAC_ADDR_ALLOC_IN_LEN;
+ req.emr_out_buf = payload;
+ req.emr_out_length = MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_LEN;
+
+ memcpy(payload + MC_CMD_MAE_MAC_ADDR_ALLOC_IN_MAC_ADDR_OFST,
+ addr_bytes, EFX_MAC_ADDR_LEN);
+
+ efx_mcdi_execute(enp, &req);
+
+ if (req.emr_rc != 0) {
+ rc = req.emr_rc;
+ goto fail3;
+ }
+
+ if (req.emr_out_length_used < MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_LEN) {
+ rc = EMSGSIZE;
+ goto fail4;
+ }
+
+ mac_id.id = MCDI_OUT_DWORD(req, MAE_MAC_ADDR_ALLOC_OUT_MAC_ID);
+ if (mac_id.id == EFX_MAE_RSRC_ID_INVALID) {
+ rc = ENOENT;
+ goto fail5;
+ }
+
+ mac_idp->id = mac_id.id;
+
+ return (0);
+
+fail5:
+ EFSYS_PROBE(fail5);
+fail4:
+ EFSYS_PROBE(fail4);
+fail3:
+ EFSYS_PROBE(fail3);
+fail2:
+ EFSYS_PROBE(fail2);
+fail1:
+ EFSYS_PROBE1(fail1, efx_rc_t, rc);
+ return (rc);
+}
+
+ __checkReturn efx_rc_t
+efx_mae_mac_addr_free(
+ __in efx_nic_t *enp,
+ __in const efx_mae_mac_id_t *mac_idp)
+{
+ const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
+ efx_mcdi_req_t req;
+ EFX_MCDI_DECLARE_BUF(payload,
+ MC_CMD_MAE_MAC_ADDR_FREE_IN_LEN(1),
+ MC_CMD_MAE_MAC_ADDR_FREE_OUT_LEN(1));
+ efx_rc_t rc;
+
+ if (encp->enc_mae_supported == B_FALSE) {
+ rc = ENOTSUP;
+ goto fail1;
+ }
+
+ if (encp->enc_mae_aset_v2_supported == B_FALSE) {
+ rc = ENOTSUP;
+ goto fail2;
+ }
+
+ req.emr_cmd = MC_CMD_MAE_MAC_ADDR_FREE;
+ req.emr_in_buf = payload;
+ req.emr_in_length = MC_CMD_MAE_MAC_ADDR_FREE_IN_LEN(1);
+ req.emr_out_buf = payload;
+ req.emr_out_length = MC_CMD_MAE_MAC_ADDR_FREE_OUT_LEN(1);
+
+ MCDI_IN_SET_DWORD(req, MAE_MAC_ADDR_FREE_IN_MAC_ID, mac_idp->id);
+
+ efx_mcdi_execute(enp, &req);
+
+ if (req.emr_rc != 0) {
+ rc = req.emr_rc;
+ goto fail3;
+ }
+
+ if (req.emr_out_length_used < MC_CMD_MAE_MAC_ADDR_FREE_OUT_LEN(1)) {
+ rc = EMSGSIZE;
+ goto fail4;
+ }
+
+ if (MCDI_OUT_DWORD(req, MAE_MAC_ADDR_FREE_OUT_FREED_MAC_ID) !=
+ mac_idp->id) {
+ /* Firmware failed to remove the MAC address entry. */
+ rc = EAGAIN;
+ goto fail5;
+ }
+
+ return (0);
+
+fail5:
+ EFSYS_PROBE(fail5);
+fail4:
+ EFSYS_PROBE(fail4);
+fail3:
+ EFSYS_PROBE(fail3);
+fail2:
+ EFSYS_PROBE(fail2);
+fail1:
+ EFSYS_PROBE1(fail1, efx_rc_t, rc);
+ return (rc);
+}
+
+ __checkReturn efx_rc_t
+efx_mae_action_set_fill_in_dst_mac_id(
+ __in efx_mae_actions_t *spec,
+ __in const efx_mae_mac_id_t *mac_idp)
+{
+ efx_rc_t rc;
+
+ if ((spec->ema_actions & (1U << EFX_MAE_ACTION_SET_DST_MAC)) == 0) {
+ /*
+ * The caller has not intended to have this action originally,
+ * hence, they cannot indicate the MAC address entry ID.
+ */
+ rc = EINVAL;
+ goto fail1;
+ }
+
+ if (spec->ema_rsrc.emar_dst_mac_id.id != EFX_MAE_RSRC_ID_INVALID) {
+ /* An attempt to indicate the MAC address entry ID twice. */
+ rc = EINVAL;
+ goto fail2;
+ }
+
+ if (mac_idp->id == EFX_MAE_RSRC_ID_INVALID) {
+ rc = EINVAL;
+ goto fail3;
+ }
+
+ spec->ema_rsrc.emar_dst_mac_id.id = mac_idp->id;
+
+ return (0);
+
+fail3:
+ EFSYS_PROBE(fail3);
+fail2:
+ EFSYS_PROBE(fail2);
+fail1:
+ EFSYS_PROBE1(fail1, efx_rc_t, rc);
+ return (rc);
+}
+
+ __checkReturn efx_rc_t
+efx_mae_action_set_fill_in_src_mac_id(
+ __in efx_mae_actions_t *spec,
+ __in const efx_mae_mac_id_t *mac_idp)
+{
+ efx_rc_t rc;
+
+ if ((spec->ema_actions & (1U << EFX_MAE_ACTION_SET_SRC_MAC)) == 0) {
+ /*
+ * The caller has not intended to have this action originally,
+ * hence, they cannot indicate the MAC address entry ID.
+ */
+ rc = EINVAL;
+ goto fail1;
+ }
+
+ if (spec->ema_rsrc.emar_src_mac_id.id != EFX_MAE_RSRC_ID_INVALID) {
+ /* An attempt to indicate the MAC address entry ID twice. */
+ rc = EINVAL;
+ goto fail2;
+ }
+
+ if (mac_idp->id == EFX_MAE_RSRC_ID_INVALID) {
+ rc = EINVAL;
+ goto fail3;
+ }
+
+ spec->ema_rsrc.emar_src_mac_id.id = mac_idp->id;
+
+ return (0);
+
fail3:
EFSYS_PROBE(fail3);
fail2:
@@ -2538,16 +2804,28 @@ efx_mae_action_set_alloc(
goto fail1;
}
+ if ((spec->ema_actions & (1U << EFX_MAE_ACTION_SET_DST_MAC)) != 0 &&
+ spec->ema_rsrc.emar_dst_mac_id.id == EFX_MAE_RSRC_ID_INVALID) {
+ rc = EINVAL;
+ goto fail2;
+ }
+
+ if ((spec->ema_actions & (1U << EFX_MAE_ACTION_SET_SRC_MAC)) != 0 &&
+ spec->ema_rsrc.emar_src_mac_id.id == EFX_MAE_RSRC_ID_INVALID) {
+ rc = EINVAL;
+ goto fail3;
+ }
+
if ((spec->ema_actions & (1U << EFX_MAE_ACTION_ENCAP)) != 0 &&
spec->ema_rsrc.emar_eh_id.id == EFX_MAE_RSRC_ID_INVALID) {
rc = EINVAL;
- goto fail2;
+ goto fail4;
}
if (spec->ema_n_count_actions == 1 &&
spec->ema_rsrc.emar_counter_id.id == EFX_MAE_RSRC_ID_INVALID) {
rc = EINVAL;
- goto fail3;
+ goto fail5;
}
req.emr_cmd = MC_CMD_MAE_ACTION_SET_ALLOC;
@@ -2572,6 +2850,12 @@ efx_mae_action_set_alloc(
MCDI_IN_SET_DWORD_FIELD(req, MAE_ACTION_SET_ALLOC_IN_FLAGS,
MAE_ACTION_SET_ALLOC_IN_VLAN_POP, spec->ema_n_vlan_tags_to_pop);
+ MCDI_IN_SET_DWORD(req, MAE_ACTION_SET_ALLOC_IN_DST_MAC_ID,
+ spec->ema_rsrc.emar_dst_mac_id.id);
+
+ MCDI_IN_SET_DWORD(req, MAE_ACTION_SET_ALLOC_IN_SRC_MAC_ID,
+ spec->ema_rsrc.emar_src_mac_id.id);
+
if ((spec->ema_actions & (1U << EFX_MAE_ACTION_DECR_IP_TTL)) != 0) {
MCDI_IN_SET_DWORD_FIELD(req, MAE_ACTION_SET_ALLOC_IN_FLAGS,
MAE_ACTION_SET_ALLOC_IN_DO_DECR_IP_TTL, 1);
@@ -2623,33 +2907,32 @@ efx_mae_action_set_alloc(
MCDI_IN_SET_DWORD(req,
MAE_ACTION_SET_ALLOC_IN_DELIVER, spec->ema_deliver_mport.sel);
- MCDI_IN_SET_DWORD(req, MAE_ACTION_SET_ALLOC_IN_SRC_MAC_ID,
- MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL);
- MCDI_IN_SET_DWORD(req, MAE_ACTION_SET_ALLOC_IN_DST_MAC_ID,
- MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL);
-
efx_mcdi_execute(enp, &req);
if (req.emr_rc != 0) {
rc = req.emr_rc;
- goto fail4;
+ goto fail6;
}
if (req.emr_out_length_used < MC_CMD_MAE_ACTION_SET_ALLOC_OUT_LEN) {
rc = EMSGSIZE;
- goto fail5;
+ goto fail7;
}
aset_id.id = MCDI_OUT_DWORD(req, MAE_ACTION_SET_ALLOC_OUT_AS_ID);
if (aset_id.id == EFX_MAE_RSRC_ID_INVALID) {
rc = ENOENT;
- goto fail6;
+ goto fail8;
}
aset_idp->id = aset_id.id;
return (0);
+fail8:
+ EFSYS_PROBE(fail8);
+fail7:
+ EFSYS_PROBE(fail7);
fail6:
EFSYS_PROBE(fail6);
fail5:
diff --git a/drivers/common/sfc_efx/version.map b/drivers/common/sfc_efx/version.map
index 765ca39332..1471aad2c4 100644
--- a/drivers/common/sfc_efx/version.map
+++ b/drivers/common/sfc_efx/version.map
@@ -90,7 +90,9 @@ INTERNAL {
efx_mae_action_rule_remove;
efx_mae_action_set_alloc;
efx_mae_action_set_fill_in_counter_id;
+ efx_mae_action_set_fill_in_dst_mac_id;
efx_mae_action_set_fill_in_eh_id;
+ efx_mae_action_set_fill_in_src_mac_id;
efx_mae_action_set_free;
efx_mae_action_set_get_nb_count;
efx_mae_action_set_populate_count;
@@ -101,6 +103,8 @@ INTERNAL {
efx_mae_action_set_populate_encap;
efx_mae_action_set_populate_flag;
efx_mae_action_set_populate_mark;
+ efx_mae_action_set_populate_set_dst_mac;
+ efx_mae_action_set_populate_set_src_mac;
efx_mae_action_set_populate_vlan_pop;
efx_mae_action_set_populate_vlan_push;
efx_mae_action_set_spec_fini;
@@ -116,6 +120,8 @@ INTERNAL {
efx_mae_fini;
efx_mae_get_limits;
efx_mae_init;
+ efx_mae_mac_addr_alloc;
+ efx_mae_mac_addr_free;
efx_mae_match_spec_bit_set;
efx_mae_match_spec_field_set;
efx_mae_match_spec_fini;
--
2.30.2
next prev parent reply other threads:[~2021-11-17 11:44 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-11-17 11:44 [PATCH 0/4] net/sfc: support MAC address edits in transfer flows Ivan Malov
2021-11-17 11:44 ` [PATCH 1/4] net/sfc: refine the order of checks on MAE action set attach Ivan Malov
2021-11-17 11:44 ` [PATCH 2/4] net/sfc: organise MAE flow action parsing function arguments Ivan Malov
2021-11-17 11:44 ` Ivan Malov [this message]
2021-11-17 11:44 ` [PATCH 4/4] net/sfc: support MAC address edits in transfer flows Ivan Malov
2021-11-17 13:17 ` [PATCH 0/4] " Ferruh Yigit
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=20211117114438.8347-4-ivan.malov@oktetlabs.ru \
--to=ivan.malov@oktetlabs.ru \
--cc=amoreton@xilinx.com \
--cc=andrew.rybchenko@oktetlabs.ru \
--cc=dev@dpdk.org \
--cc=mdr@ashroe.eu \
/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).