From: "Philippe Mathieu-Daudé" <philmd@linaro.org>
To: Akihiko Odaki <akihiko.odaki@daynix.com>
Cc: "Sriram Yagnaraman" <sriram.yagnaraman@est.tech>,
"Jason Wang" <jasowang@redhat.com>,
"Dmitry Fleytman" <dmitry.fleytman@gmail.com>,
"Michael S . Tsirkin" <mst@redhat.com>,
"Alex Bennée" <alex.bennee@linaro.org>,
"Thomas Huth" <thuth@redhat.com>,
"Wainer dos Santos Moschetta" <wainersm@redhat.com>,
"Beraldo Leal" <bleal@redhat.com>,
"Cleber Rosa" <crosa@redhat.com>,
"Laurent Vivier" <lvivier@redhat.com>,
"Paolo Bonzini" <pbonzini@redhat.com>,
qemu-devel@nongnu.org,
"Tomasz Dzieciol" <t.dzieciol@partner.samsung.com>
Subject: Re: [PATCH v2 02/41] hw/net/net_tx_pkt: Decouple interface from PCI
Date: Thu, 20 Apr 2023 11:54:27 +0200 [thread overview]
Message-ID: <a38922a7-ee0b-dcac-a232-be48cc89fc60@linaro.org> (raw)
In-Reply-To: <20230420054657.50367-3-akihiko.odaki@daynix.com>
On 20/4/23 07:46, Akihiko Odaki wrote:
> This allows to use the network packet abstractions even if PCI is not
> used.
>
> Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com>
> ---
> hw/net/net_tx_pkt.h | 31 ++++++++++++++++++++-----------
> hw/net/e1000e_core.c | 13 ++++++++-----
> hw/net/igb_core.c | 13 ++++++-------
> hw/net/net_tx_pkt.c | 36 +++++++++++++-----------------------
> hw/net/vmxnet3.c | 14 +++++++-------
> 5 files changed, 54 insertions(+), 53 deletions(-)
>
> diff --git a/hw/net/net_tx_pkt.h b/hw/net/net_tx_pkt.h
> index 5eb123ef90..f5cd44da6f 100644
> --- a/hw/net/net_tx_pkt.h
> +++ b/hw/net/net_tx_pkt.h
> @@ -26,17 +26,16 @@
>
> struct NetTxPkt;
>
> -typedef void (* NetTxPktCallback)(void *, const struct iovec *, int, const struct iovec *, int);
> +typedef void (*NetTxPktFreeFrag)(void *, void *, size_t);
If you don't want to restrict to PCIDevice, you can still use DeviceState:
typedef void (*NetTxPktFreeFrag)(DeviceState *, void *, size_t);
> +typedef void (*NetTxPktSend)(void *, const struct iovec *, int, const struct iovec *, int);
>
> /**
> * Init function for tx packet functionality
> *
> * @pkt: packet pointer
> - * @pci_dev: PCI device processing this packet
> * @max_frags: max tx ip fragments
> */
> -void net_tx_pkt_init(struct NetTxPkt **pkt, PCIDevice *pci_dev,
> - uint32_t max_frags);
> +void net_tx_pkt_init(struct NetTxPkt **pkt, uint32_t max_frags);
>
> /**
> * Clean all tx packet resources.
> @@ -95,12 +94,11 @@ net_tx_pkt_setup_vlan_header(struct NetTxPkt *pkt, uint16_t vlan)
> * populate data fragment into pkt context.
> *
> * @pkt: packet
> - * @pa: physical address of fragment
> + * @pa: pointer to fragment
You renamed it @base.
> * @len: length of fragment
> *
> */
> -bool net_tx_pkt_add_raw_fragment(struct NetTxPkt *pkt, hwaddr pa,
> - size_t len);
> +bool net_tx_pkt_add_raw_fragment(struct NetTxPkt *pkt, void *base, size_t len);
>
> /**
> * Fix ip header fields and calculate IP header and pseudo header checksums.
> @@ -148,10 +146,11 @@ void net_tx_pkt_dump(struct NetTxPkt *pkt);
> * reset tx packet private context (needed to be called between packets)
> *
> * @pkt: packet
> - * @dev: PCI device processing the next packet
> - *
> + * @callback: function to free the fragments
> + * @context: pointer to be passed to the callback
> */
> -void net_tx_pkt_reset(struct NetTxPkt *pkt, PCIDevice *dev);
> +void net_tx_pkt_reset(struct NetTxPkt *pkt,
> + NetTxPktFreeFrag callback, void *context);
>
> /**
> * Unmap a fragment mapped from a PCI device.
> @@ -162,6 +161,16 @@ void net_tx_pkt_reset(struct NetTxPkt *pkt, PCIDevice *dev);
> */
> void net_tx_pkt_unmap_frag_pci(void *context, void *base, size_t len);
>
> +/**
> + * map data fragment from PCI device and populate it into pkt context.
> + *
> + * @pci_dev: PCI device owning fragment
> + * @pa: physical address of fragment
> + * @len: length of fragment
> + */
> +bool net_tx_pkt_add_raw_fragment_pci(struct NetTxPkt *pkt, PCIDevice *pci_dev,
> + dma_addr_t pa, size_t len);
(following previous patch comment, here too you use dma_addr_t).
> +
> /**
> * Send packet to qemu. handles sw offloads if vhdr is not supported.
> *
> @@ -182,7 +191,7 @@ bool net_tx_pkt_send(struct NetTxPkt *pkt, NetClientState *nc);
> * @ret: operation result
> */
> bool net_tx_pkt_send_custom(struct NetTxPkt *pkt, bool offload,
> - NetTxPktCallback callback, void *context);
> + NetTxPktSend callback, void *context);
>
> /**
> * parse raw packet data and analyze offload requirements.
> diff --git a/hw/net/e1000e_core.c b/hw/net/e1000e_core.c
> index cfa3f55e96..15821a75e0 100644
> --- a/hw/net/e1000e_core.c
> +++ b/hw/net/e1000e_core.c
> @@ -746,7 +746,8 @@ e1000e_process_tx_desc(E1000ECore *core,
> addr = le64_to_cpu(dp->buffer_addr);
>
> if (!tx->skip_cp) {
> - if (!net_tx_pkt_add_raw_fragment(tx->tx_pkt, addr, split_size)) {
> + if (!net_tx_pkt_add_raw_fragment_pci(tx->tx_pkt, core->owner,
> + addr, split_size)) {
> tx->skip_cp = true;
> }
> }
> @@ -764,7 +765,7 @@ e1000e_process_tx_desc(E1000ECore *core,
> }
>
> tx->skip_cp = false;
> - net_tx_pkt_reset(tx->tx_pkt, core->owner);
> + net_tx_pkt_reset(tx->tx_pkt, net_tx_pkt_unmap_frag_pci, core->owner);
>
> tx->sum_needed = 0;
> tx->cptse = 0;
> @@ -3421,7 +3422,7 @@ e1000e_core_pci_realize(E1000ECore *core,
> qemu_add_vm_change_state_handler(e1000e_vm_state_change, core);
>
> for (i = 0; i < E1000E_NUM_QUEUES; i++) {
> - net_tx_pkt_init(&core->tx[i].tx_pkt, core->owner, E1000E_MAX_TX_FRAGS);
> + net_tx_pkt_init(&core->tx[i].tx_pkt, E1000E_MAX_TX_FRAGS);
> }
>
> net_rx_pkt_init(&core->rx_pkt);
> @@ -3446,7 +3447,8 @@ e1000e_core_pci_uninit(E1000ECore *core)
> qemu_del_vm_change_state_handler(core->vmstate);
>
> for (i = 0; i < E1000E_NUM_QUEUES; i++) {
> - net_tx_pkt_reset(core->tx[i].tx_pkt, core->owner);
> + net_tx_pkt_reset(core->tx[i].tx_pkt,
> + net_tx_pkt_unmap_frag_pci, core->owner);
> net_tx_pkt_uninit(core->tx[i].tx_pkt);
> }
>
> @@ -3571,7 +3573,8 @@ static void e1000e_reset(E1000ECore *core, bool sw)
> e1000x_reset_mac_addr(core->owner_nic, core->mac, core->permanent_mac);
>
> for (i = 0; i < ARRAY_SIZE(core->tx); i++) {
> - net_tx_pkt_reset(core->tx[i].tx_pkt, core->owner);
> + net_tx_pkt_reset(core->tx[i].tx_pkt,
> + net_tx_pkt_unmap_frag_pci, core->owner);
> memset(&core->tx[i].props, 0, sizeof(core->tx[i].props));
> core->tx[i].skip_cp = false;
> }
> diff --git a/hw/net/igb_core.c b/hw/net/igb_core.c
> index 826e7a6cf1..abfdce9aaf 100644
> --- a/hw/net/igb_core.c
> +++ b/hw/net/igb_core.c
> @@ -597,7 +597,8 @@ igb_process_tx_desc(IGBCore *core,
> length = cmd_type_len & 0xFFFF;
>
> if (!tx->skip_cp) {
> - if (!net_tx_pkt_add_raw_fragment(tx->tx_pkt, buffer_addr, length)) {
> + if (!net_tx_pkt_add_raw_fragment_pci(tx->tx_pkt, dev,
> + buffer_addr, length)) {
> tx->skip_cp = true;
> }
> }
> @@ -616,7 +617,7 @@ igb_process_tx_desc(IGBCore *core,
>
> tx->first = true;
> tx->skip_cp = false;
> - net_tx_pkt_reset(tx->tx_pkt, dev);
> + net_tx_pkt_reset(tx->tx_pkt, net_tx_pkt_unmap_frag_pci, dev);
> }
> }
>
> @@ -842,8 +843,6 @@ igb_start_xmit(IGBCore *core, const IGB_TxRing *txr)
> d = core->owner;
> }
>
> - net_tx_pkt_reset(txr->tx->tx_pkt, d);
> -
> while (!igb_ring_empty(core, txi)) {
> base = igb_ring_head_descr(core, txi);
>
> @@ -861,6 +860,8 @@ igb_start_xmit(IGBCore *core, const IGB_TxRing *txr)
> core->mac[EICR] |= eic;
> igb_set_interrupt_cause(core, E1000_ICR_TXDW);
> }
> +
> + net_tx_pkt_reset(txr->tx->tx_pkt, net_tx_pkt_unmap_frag_pci, d);
> }
>
> static uint32_t
> @@ -3954,7 +3955,7 @@ igb_core_pci_realize(IGBCore *core,
> core->vmstate = qemu_add_vm_change_state_handler(igb_vm_state_change, core);
>
> for (i = 0; i < IGB_NUM_QUEUES; i++) {
> - net_tx_pkt_init(&core->tx[i].tx_pkt, NULL, E1000E_MAX_TX_FRAGS);
> + net_tx_pkt_init(&core->tx[i].tx_pkt, E1000E_MAX_TX_FRAGS);
> }
>
> net_rx_pkt_init(&core->rx_pkt);
> @@ -3979,7 +3980,6 @@ igb_core_pci_uninit(IGBCore *core)
> qemu_del_vm_change_state_handler(core->vmstate);
>
> for (i = 0; i < IGB_NUM_QUEUES; i++) {
> - net_tx_pkt_reset(core->tx[i].tx_pkt, NULL);
> net_tx_pkt_uninit(core->tx[i].tx_pkt);
> }
>
> @@ -4158,7 +4158,6 @@ static void igb_reset(IGBCore *core, bool sw)
>
> for (i = 0; i < ARRAY_SIZE(core->tx); i++) {
> tx = &core->tx[i];
> - net_tx_pkt_reset(tx->tx_pkt, NULL);
> memset(tx->ctx, 0, sizeof(tx->ctx));
> tx->first = true;
> tx->skip_cp = false;
> diff --git a/hw/net/net_tx_pkt.c b/hw/net/net_tx_pkt.c
> index aca12ff035..cc36750c9b 100644
> --- a/hw/net/net_tx_pkt.c
> +++ b/hw/net/net_tx_pkt.c
> @@ -16,12 +16,12 @@
> */
>
> #include "qemu/osdep.h"
> -#include "net_tx_pkt.h"
> #include "net/eth.h"
> #include "net/checksum.h"
> #include "net/tap.h"
> #include "net/net.h"
> #include "hw/pci/pci_device.h"
> +#include "net_tx_pkt.h"
>
> enum {
> NET_TX_PKT_VHDR_FRAG = 0,
> @@ -32,8 +32,6 @@ enum {
>
> /* TX packet private context */
> struct NetTxPkt {
> - PCIDevice *pci_dev;
> -
> struct virtio_net_hdr virt_hdr;
>
> struct iovec *raw;
> @@ -59,13 +57,10 @@ struct NetTxPkt {
> uint8_t l4proto;
> };
>
> -void net_tx_pkt_init(struct NetTxPkt **pkt, PCIDevice *pci_dev,
> - uint32_t max_frags)
> +void net_tx_pkt_init(struct NetTxPkt **pkt, uint32_t max_frags)
> {
> struct NetTxPkt *p = g_malloc0(sizeof *p);
>
> - p->pci_dev = pci_dev;
> -
> p->vec = g_new(struct iovec, max_frags + NET_TX_PKT_PL_START_FRAG);
>
> p->raw = g_new(struct iovec, max_frags);
> @@ -384,8 +379,7 @@ void net_tx_pkt_setup_vlan_header_ex(struct NetTxPkt *pkt,
> }
> }
>
> -static bool net_tx_pkt_add_raw_fragment_common(struct NetTxPkt *pkt,
> - void *base, size_t len)
> +bool net_tx_pkt_add_raw_fragment(struct NetTxPkt *pkt, void *base, size_t len)
> {
> struct iovec *ventry;
> assert(pkt);
> @@ -433,7 +427,8 @@ void net_tx_pkt_dump(struct NetTxPkt *pkt)
> #endif
> }
>
> -void net_tx_pkt_reset(struct NetTxPkt *pkt, PCIDevice *pci_dev)
> +void net_tx_pkt_reset(struct NetTxPkt *pkt,
> + NetTxPktFreeFrag callback, void *context)
'void *context' -> 'DeviceState *dev'.
> {
> int i;
>
> @@ -453,12 +448,9 @@ void net_tx_pkt_reset(struct NetTxPkt *pkt, PCIDevice *pci_dev)
> assert(pkt->raw);
> for (i = 0; i < pkt->raw_frags; i++) {
> assert(pkt->raw[i].iov_base);
> - net_tx_pkt_unmap_frag_pci(pkt->pci_dev,
> - pkt->raw[i].iov_base,
> - pkt->raw[i].iov_len);
> + callback(context, pkt->raw[i].iov_base, pkt->raw[i].iov_len);
> }
> }
> - pkt->pci_dev = pci_dev;
> pkt->raw_frags = 0;
>
> pkt->hdr_len = 0;
> @@ -470,19 +462,17 @@ void net_tx_pkt_unmap_frag_pci(void *context, void *base, size_t len)
> pci_dma_unmap(context, base, len, DMA_DIRECTION_TO_DEVICE, 0);
> }
>
> -bool net_tx_pkt_add_raw_fragment(struct NetTxPkt *pkt, hwaddr pa,
> - size_t len)
> +bool net_tx_pkt_add_raw_fragment_pci(struct NetTxPkt *pkt, PCIDevice *pci_dev,
> + dma_addr_t pa, size_t len)
Ah, finally you use dma_addr_t. So this clearly belong to the previous
patch.
> {
> dma_addr_t mapped_len = len;
> - void *base = pci_dma_map(pkt->pci_dev, pa, &mapped_len,
> - DMA_DIRECTION_TO_DEVICE);
> + void *base = pci_dma_map(pci_dev, pa, &mapped_len, DMA_DIRECTION_TO_DEVICE);
> if (!base) {
> return false;
> }
>
> - if (mapped_len != len ||
> - !net_tx_pkt_add_raw_fragment_common(pkt, base, len)) {
> - net_tx_pkt_unmap_frag_pci(pkt->pci_dev, base, mapped_len);
> + if (mapped_len != len || !net_tx_pkt_add_raw_fragment(pkt, base, len)) {
> + net_tx_pkt_unmap_frag_pci(pci_dev, base, mapped_len);
> return false;
> }
>
> @@ -710,7 +700,7 @@ static void net_tx_pkt_udp_fragment_fix(struct NetTxPkt *pkt,
> }
>
> static bool net_tx_pkt_do_sw_fragmentation(struct NetTxPkt *pkt,
> - NetTxPktCallback callback,
> + NetTxPktSend callback,
> void *context)
> {
> uint8_t gso_type = pkt->virt_hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN;
> @@ -807,7 +797,7 @@ bool net_tx_pkt_send(struct NetTxPkt *pkt, NetClientState *nc)
> }
>
> bool net_tx_pkt_send_custom(struct NetTxPkt *pkt, bool offload,
> - NetTxPktCallback callback, void *context)
> + NetTxPktSend callback, void *context)
> {
> assert(pkt);
next prev parent reply other threads:[~2023-04-20 9:55 UTC|newest]
Thread overview: 62+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-04-20 5:46 [PATCH v2 00/41] igb: Fix for DPDK Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 01/41] hw/net/net_tx_pkt: Decouple implementation from PCI Akihiko Odaki
2023-04-20 9:46 ` Philippe Mathieu-Daudé
2023-04-20 17:25 ` Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 02/41] hw/net/net_tx_pkt: Decouple interface " Akihiko Odaki
2023-04-20 9:54 ` Philippe Mathieu-Daudé [this message]
2023-04-20 17:33 ` Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 03/41] e1000x: Fix BPRC and MPRC Akihiko Odaki
2023-04-20 16:22 ` Sriram Yagnaraman
2023-04-20 5:46 ` [PATCH v2 04/41] igb: Fix Rx packet type encoding Akihiko Odaki
2023-04-20 16:22 ` Sriram Yagnaraman
2023-04-20 5:46 ` [PATCH v2 05/41] igb: Do not require CTRL.VME for tx VLAN tagging Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 06/41] net/net_rx_pkt: Use iovec for net_rx_pkt_set_protocols() Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 07/41] e1000e: Always copy ethernet header Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 08/41] igb: " Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 09/41] Fix references to igb Avocado test Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 10/41] tests/avocado: Remove unused imports Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 11/41] tests/avocado: Remove test_igb_nomsi_kvm Akihiko Odaki
2023-04-20 7:23 ` Thomas Huth
2023-04-20 17:42 ` Alex Bennée
2023-04-20 5:46 ` [PATCH v2 12/41] hw/net/net_tx_pkt: Remove net_rx_pkt_get_l4_info Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 13/41] net/eth: Rename eth_setup_vlan_headers_ex Akihiko Odaki
2023-04-20 9:55 ` Philippe Mathieu-Daudé
2023-04-20 5:46 ` [PATCH v2 14/41] e1000x: Share more Rx filtering logic Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 15/41] e1000x: Take CRC into consideration for size check Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 16/41] e1000x: Rename TcpIpv6 into TcpIpv6Ex Akihiko Odaki
2023-04-20 16:22 ` Sriram Yagnaraman
2023-04-20 17:36 ` Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 17/41] e1000e: Always log status after building rx metadata Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 18/41] igb: " Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 19/41] igb: Remove goto Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 20/41] igb: Read DCMD.VLE of the first Tx descriptor Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 21/41] e1000e: Reset packet state after emptying Tx queue Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 22/41] vmxnet3: " Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 23/41] igb: Add more definitions for Tx descriptor Akihiko Odaki
2023-04-20 16:22 ` Sriram Yagnaraman
2023-04-20 5:46 ` [PATCH v2 24/41] igb: Share common VF constants Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 25/41] igb: Fix igb_mac_reg_init coding style alignment Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 26/41] net/eth: Use void pointers Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 27/41] net/eth: Always add VLAN tag Akihiko Odaki
2023-04-20 16:22 ` Sriram Yagnaraman
2023-04-20 17:38 ` Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 28/41] hw/net/net_rx_pkt: Enforce alignment for eth_header Akihiko Odaki
2023-04-20 16:22 ` Sriram Yagnaraman
2023-04-20 5:46 ` [PATCH v2 29/41] tests/qtest/libqos/igb: Set GPIE.Multiple_MSIX Akihiko Odaki
2023-04-20 16:22 ` Sriram Yagnaraman
2023-04-20 5:46 ` [PATCH v2 30/41] igb: Implement MSI-X single vector mode Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 31/41] igb: Use UDP for RSS hash Akihiko Odaki
2023-04-20 16:22 ` Sriram Yagnaraman
2023-04-20 5:46 ` [PATCH v2 32/41] igb: Implement Rx SCTP CSO Akihiko Odaki
2023-04-20 16:22 ` Sriram Yagnaraman
2023-04-20 17:56 ` Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 33/41] igb: Implement Tx " Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 34/41] igb: Strip the second VLAN tag for extended VLAN Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 35/41] igb: Filter with " Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 36/41] igb: Implement igb-specific oversize check Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 37/41] igb: Implement Rx PTP2 timestamp Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 38/41] igb: Implement Tx timestamp Akihiko Odaki
2023-04-20 16:22 ` Sriram Yagnaraman
2023-04-20 5:46 ` [PATCH v2 39/41] vmxnet3: Do not depend on PC Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 40/41] MAINTAINERS: Add a reviewer for network packet abstractions Akihiko Odaki
2023-04-20 5:46 ` [PATCH v2 41/41] docs/system/devices/igb: Note igb is tested for DPDK Akihiko Odaki
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=a38922a7-ee0b-dcac-a232-be48cc89fc60@linaro.org \
--to=philmd@linaro.org \
--cc=akihiko.odaki@daynix.com \
--cc=alex.bennee@linaro.org \
--cc=bleal@redhat.com \
--cc=crosa@redhat.com \
--cc=dmitry.fleytman@gmail.com \
--cc=jasowang@redhat.com \
--cc=lvivier@redhat.com \
--cc=mst@redhat.com \
--cc=pbonzini@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=sriram.yagnaraman@est.tech \
--cc=t.dzieciol@partner.samsung.com \
--cc=thuth@redhat.com \
--cc=wainersm@redhat.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).