From: Alistair Popple <apopple@nvidia.com>
To: dri-devel@lists.freedesktop.org, christian.koenig@amd.com,
sumit.semwal@linaro.org, jgg@nvidia.com
Cc: "Alistair Popple" <apopple@nvidia.com>,
"Thomas Hellström" <thomas.hellstrom@linux.intel.com>,
"Simona Vetter" <simona.vetter@ffwll.ch>,
"Matthew Brost" <matthew.brost@intel.com>,
"Vivek Kasireddy" <vivek.kasireddy@intel.com>,
"Dongwon Kim" <dongwon.kim@intel.com>,
"Leon Romanovsky" <leonro@nvidia.com>,
"Kevin Tian" <kevin.tian@intel.com>,
"Xu Yilun" <yilun.xu@linux.intel.com>,
"Karl Mehltretter" <kmehltretter@gmail.com>,
"Baolu Lu" <baolu.lu@linux.intel.com>,
"Felix Kuehling" <felix.kuehling@amd.com>,
"Alex Deucher" <alexander.deucher@amd.com>,
intel-xe@lists.freedesktop.org, amd-gfx@lists.freedesktop.org,
linux-media@vger.kernel.org, linaro-mm-sig@lists.linaro.org,
linux-rdma@vger.kernel.org, iommu@lists.linux.dev,
"Alexey Kardashevskiy" <aik@amd.com>,
zhipingz@meta.com, treding@nvidia.com, jhubbard@nvidia.com
Subject: [PATCH 26/26] dma-buf: Add kunit tests for mapping type selection
Date: Mon, 26 Oct 2026 16:36:17 +1100 [thread overview]
Message-ID: <20261026053623.202642-27-apopple@nvidia.com> (raw)
In-Reply-To: <20261026053623.202642-1-apopple@nvidia.com>
Add an offer list and a select_mapping() callback to the mock exporter
along with map/unmap for the two mock types.
Adds tests for the exporter/importer contract.
- test_match_set_is_final: a later importer attaching, even one the
exporter answers differently, does not change an existing
attachment's match set or default. Later attachments influence what
the exporter selects at pin and map time, not previous attachments.
- test_select_at_map: the exporter chooses a non-default entry
at the first map and the result is tagged with it. A pin taken
under the live mapping inherits the type and the choice survives
unmap/remap even though the exporter would answer differently. Only
dma_buf_invalidate_mappings() allows the exporter to make a different
selection. An -EBUSY from selection leaves nothing established.
- test_select_at_pin: pin selects, and the choice survives map, unmap
and unpin so a pinned importer that is never invalidated keeps its
first type for the life of the import.
Assisted-by: LLM
Signed-off-by: Alistair Popple <apopple@nvidia.com>
---
drivers/dma-buf/st-dma-mapping.c | 277 +++++++++++++++++++++++++++++++
1 file changed, 277 insertions(+)
diff --git a/drivers/dma-buf/st-dma-mapping.c b/drivers/dma-buf/st-dma-mapping.c
index 9d0f1e389e15..22c1d73ea087 100644
--- a/drivers/dma-buf/st-dma-mapping.c
+++ b/drivers/dma-buf/st-dma-mapping.c
@@ -7,6 +7,7 @@
#include <kunit/test.h>
#include <linux/dma-buf.h>
#include <linux/dma-buf-mapping.h>
+#include <linux/dma-resv.h>
#include <linux/errno.h>
/* Mock tracking state -- reset before each test */
@@ -20,6 +21,14 @@ static const struct dma_buf_match_args *mock_finish_args_arg;
static const struct dma_buf_mapping_match *mock_finish_exp_arg;
static const struct dma_buf_mapping_match *mock_finish_imp_arg;
+/* Mapping types the configurable mock exporter currently offers */
+static const struct dma_buf_mapping_match *mock_exp_offer;
+static size_t mock_exp_offer_len;
+
+/* Which match set entry the selecting mock exporter picks, or an error */
+static int mock_select_idx;
+static int mock_select_err;
+
static int reset_mock_state(struct kunit *test)
{
mock_match_called = false;
@@ -30,6 +39,10 @@ static int reset_mock_state(struct kunit *test)
mock_finish_args_arg = NULL;
mock_finish_exp_arg = NULL;
mock_finish_imp_arg = NULL;
+ mock_exp_offer = NULL;
+ mock_exp_offer_len = 0;
+ mock_select_idx = 0;
+ mock_select_err = 0;
return 0;
}
@@ -357,6 +370,267 @@ static void test_mock_attach(struct kunit *test)
dma_buf_put(dmabuf);
}
+static int mock_dmabuf_offer_match_mapping(struct dma_buf_match_args *args)
+{
+ return dma_buf_match_mapping(args, mock_exp_offer, mock_exp_offer_len);
+}
+
+static const struct dma_buf_ops mock_dmabuf_offer_ops = {
+ .release = mock_dmabuf_release,
+ .match_mapping = mock_dmabuf_offer_match_mapping,
+};
+
+/*
+ * The match set is final once attached. A later importer attaching, even one
+ * the exporter answers differently, must not change an existing attachment.
+ *
+ * Later attachments influence what the exporter selects at pin or map time,
+ * not what was previously negotiated.
+ */
+static void test_match_set_is_final(struct kunit *test)
+{
+ static const struct dma_buf_mapping_match offer_ab[] = {
+ { .type = &mock_type_a },
+ { .type = &mock_type_b },
+ };
+ static const struct dma_buf_mapping_match offer_b[] = {
+ { .type = &mock_type_b },
+ };
+ struct dma_buf_mapping_match imp1[] = {
+ { .type = &mock_type_a },
+ { .type = &mock_type_b },
+ };
+ struct dma_buf_mapping_match imp2[] = { { .type = &mock_type_b } };
+ struct dma_buf_attachment *attach1, *attach2;
+ struct dma_buf *dmabuf;
+
+ dmabuf = mock_dmabuf_export(&mock_dmabuf_offer_ops);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dmabuf);
+
+ mock_exp_offer = offer_ab;
+ mock_exp_offer_len = ARRAY_SIZE(offer_ab);
+ attach1 = dma_buf_mapping_attach(dmabuf, imp1, ARRAY_SIZE(imp1),
+ &mock_importer_ops, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, attach1);
+ KUNIT_ASSERT_EQ(test, attach1->nr_matches, 2);
+ KUNIT_EXPECT_PTR_EQ(test, attach1->map_type.type, &mock_type_a);
+
+ /* Exporter's answer for new importers changes; attach1 does not */
+ mock_exp_offer = offer_b;
+ mock_exp_offer_len = ARRAY_SIZE(offer_b);
+ attach2 = dma_buf_mapping_attach(dmabuf, imp2, ARRAY_SIZE(imp2),
+ &mock_importer_ops, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, attach2);
+ KUNIT_ASSERT_EQ(test, attach2->nr_matches, 1);
+ KUNIT_EXPECT_PTR_EQ(test, attach2->map_type.type, &mock_type_b);
+ KUNIT_EXPECT_EQ(test, attach1->nr_matches, 2);
+ KUNIT_EXPECT_PTR_EQ(test, attach1->map_type.type, &mock_type_a);
+
+ dma_buf_detach(dmabuf, attach2);
+ KUNIT_EXPECT_EQ(test, attach1->nr_matches, 2);
+ KUNIT_EXPECT_PTR_EQ(test, attach1->map_type.type, &mock_type_a);
+
+ dma_buf_detach(dmabuf, attach1);
+ dma_buf_put(dmabuf);
+}
+
+/* Mock mapping result for the generic map path */
+struct mock_mapping {
+ struct dma_buf_mapping base;
+ const struct dma_buf_mapping_type *made_by;
+};
+
+static struct dma_buf_mapping *mock_type_map(struct dma_buf_attachment *attach,
+ enum dma_data_direction dir)
+{
+ struct mock_mapping *m = kzalloc_obj(*m);
+
+ if (!m)
+ return ERR_PTR(-ENOMEM);
+ m->base.type = attach->map_type.type;
+ m->made_by = attach->map_type.type;
+ return &m->base;
+}
+
+static void mock_type_unmap(struct dma_buf_attachment *attach,
+ struct dma_buf_mapping *mapping,
+ enum dma_data_direction dir)
+{
+ kfree(container_of(mapping, struct mock_mapping, base));
+}
+
+/* Exporter choosing the match set entry the test asks for */
+static const struct dma_buf_mapping_match *
+mock_dmabuf_select_mapping(struct dma_buf_attachment *attach)
+{
+ if (mock_select_err)
+ return ERR_PTR(mock_select_err);
+ return &attach->matches[mock_select_idx];
+}
+
+static const struct dma_buf_ops mock_dmabuf_select_ops = {
+ .release = mock_dmabuf_release,
+ .match_mapping = mock_dmabuf_offer_match_mapping,
+ .select_mapping = mock_dmabuf_select_mapping,
+};
+
+/*
+ * Exporter decides type at first map from the match set. The result is tagged
+ * with it and the core holds the choice across unmap/remap until the attachment
+ * has been explicitly invalidated.
+ */
+static void test_select_at_map(struct kunit *test)
+{
+ static const struct dma_buf_mapping_match offer_ab[] = {
+ { .type = &mock_type_a },
+ { .type = &mock_type_b },
+ };
+ struct dma_buf_mapping_match imp[] = {
+ { .type = &mock_type_a },
+ { .type = &mock_type_b },
+ };
+ struct dma_buf_attachment *attach;
+ struct dma_buf_mapping *mapping;
+ struct dma_buf *dmabuf;
+
+ mock_type_a.map = mock_type_map;
+ mock_type_a.unmap = mock_type_unmap;
+ mock_type_b.map = mock_type_map;
+ mock_type_b.unmap = mock_type_unmap;
+
+ dmabuf = mock_dmabuf_export(&mock_dmabuf_select_ops);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dmabuf);
+
+ mock_exp_offer = offer_ab;
+ mock_exp_offer_len = ARRAY_SIZE(offer_ab);
+ attach = dma_buf_mapping_attach(dmabuf, imp, ARRAY_SIZE(imp),
+ &mock_importer_ops, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, attach);
+ KUNIT_EXPECT_FALSE(test, attach->selected);
+ KUNIT_EXPECT_PTR_EQ(test, attach->map_type.type, &mock_type_a);
+
+ dma_resv_lock(dmabuf->resv, NULL);
+
+ /* Exporter picks the second entry (mock_type_b) */
+ mock_select_idx = 1;
+ mapping = dma_buf_mapping_map(attach, DMA_BIDIRECTIONAL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mapping);
+ KUNIT_EXPECT_PTR_EQ(test, mapping->type, &mock_type_b);
+ KUNIT_EXPECT_PTR_EQ(test, attach->map_type.type, &mock_type_b);
+ KUNIT_EXPECT_TRUE(test, attach->selected);
+
+ /* A pin taken whilst mapping is live gets the same type */
+ mock_select_idx = 0;
+ KUNIT_EXPECT_EQ(test, dma_buf_pin(attach), 0);
+ KUNIT_EXPECT_PTR_EQ(test, attach->map_type.type, &mock_type_b);
+ dma_buf_unpin(attach);
+
+ dma_buf_mapping_unmap(attach, mapping, DMA_BIDIRECTIONAL);
+
+ /*
+ * Unmapped the exporter would now pick A, but no invalidate has
+ * happened so remap must get B again to avoid ping-ponging.
+ */
+ KUNIT_EXPECT_TRUE(test, attach->selected);
+ mapping = dma_buf_mapping_map(attach, DMA_BIDIRECTIONAL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mapping);
+ KUNIT_EXPECT_PTR_EQ(test, mapping->type, &mock_type_b);
+ dma_buf_mapping_unmap(attach, mapping, DMA_BIDIRECTIONAL);
+
+ /* Only an explicit invalidation allows the type to change */
+ dma_buf_invalidate_mappings(dmabuf);
+ KUNIT_EXPECT_FALSE(test, attach->selected);
+ /* map_type is kept for a pending unmap until the next selection */
+ KUNIT_EXPECT_PTR_EQ(test, attach->map_type.type, &mock_type_b);
+
+ mapping = dma_buf_mapping_map(attach, DMA_BIDIRECTIONAL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mapping);
+ KUNIT_EXPECT_PTR_EQ(test, mapping->type, &mock_type_a);
+ dma_buf_mapping_unmap(attach, mapping, DMA_BIDIRECTIONAL);
+
+ /* Exporter cannot serve this importer right now */
+ dma_buf_invalidate_mappings(dmabuf);
+ mock_select_err = -EBUSY;
+ mapping = dma_buf_mapping_map(attach, DMA_BIDIRECTIONAL);
+ KUNIT_EXPECT_EQ(test, PTR_ERR(mapping), -EBUSY);
+ KUNIT_EXPECT_FALSE(test, attach->selected);
+ mock_select_err = 0;
+
+ dma_resv_unlock(dmabuf->resv);
+
+ dma_buf_detach(dmabuf, attach);
+ dma_buf_put(dmabuf);
+
+ mock_type_a.map = NULL;
+ mock_type_a.unmap = NULL;
+ mock_type_b.map = NULL;
+ mock_type_b.unmap = NULL;
+}
+
+/*
+ * Pin selects too and the choice survives map, unmap and unpin. A pinned
+ * importer that is never invalidated keeps its first type for the life of the
+ * attachment.
+ */
+static void test_select_at_pin(struct kunit *test)
+{
+ static const struct dma_buf_mapping_match offer_ab[] = {
+ { .type = &mock_type_a },
+ { .type = &mock_type_b },
+ };
+ struct dma_buf_mapping_match imp[] = {
+ { .type = &mock_type_a },
+ { .type = &mock_type_b },
+ };
+ struct dma_buf_attachment *attach;
+ struct dma_buf_mapping *mapping;
+ struct dma_buf *dmabuf;
+
+ mock_type_a.map = mock_type_map;
+ mock_type_a.unmap = mock_type_unmap;
+ mock_type_b.map = mock_type_map;
+ mock_type_b.unmap = mock_type_unmap;
+
+ dmabuf = mock_dmabuf_export(&mock_dmabuf_select_ops);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dmabuf);
+
+ mock_exp_offer = offer_ab;
+ mock_exp_offer_len = ARRAY_SIZE(offer_ab);
+ attach = dma_buf_mapping_attach(dmabuf, imp, ARRAY_SIZE(imp),
+ &mock_importer_ops, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, attach);
+
+ dma_resv_lock(dmabuf->resv, NULL);
+
+ mock_select_idx = 1;
+ KUNIT_EXPECT_EQ(test, dma_buf_pin(attach), 0);
+ KUNIT_EXPECT_TRUE(test, attach->selected);
+ KUNIT_EXPECT_PTR_EQ(test, attach->map_type.type, &mock_type_b);
+
+ /* Map after pin uses the pinned type regardless of the exporter now */
+ mock_select_idx = 0;
+ mapping = dma_buf_mapping_map(attach, DMA_BIDIRECTIONAL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mapping);
+ KUNIT_EXPECT_PTR_EQ(test, mapping->type, &mock_type_b);
+ dma_buf_mapping_unmap(attach, mapping, DMA_BIDIRECTIONAL);
+
+ KUNIT_EXPECT_PTR_EQ(test, attach->map_type.type, &mock_type_b);
+
+ dma_buf_unpin(attach);
+ KUNIT_EXPECT_TRUE(test, attach->selected);
+ KUNIT_EXPECT_PTR_EQ(test, attach->map_type.type, &mock_type_b);
+
+ dma_resv_unlock(dmabuf->resv);
+
+ dma_buf_detach(dmabuf, attach);
+ dma_buf_put(dmabuf);
+
+ mock_type_a.map = NULL;
+ mock_type_a.unmap = NULL;
+ mock_type_b.map = NULL;
+ mock_type_b.unmap = NULL;
+}
+
static const struct dma_exporter dma_exporter_params[] = {
{ &mock_dmabuf_simple_sgt_ops, "simple_sgt" },
{ &mock_dmabuf_two_exp_ops, "two_exp" },
@@ -371,6 +645,9 @@ static struct kunit_case dma_mapping_cases[] = {
KUNIT_CASE(test_match_importer_priority),
KUNIT_CASE_PARAM(test_sgt_attach, dma_exporter_gen_params),
KUNIT_CASE(test_mock_attach),
+ KUNIT_CASE(test_match_set_is_final),
+ KUNIT_CASE(test_select_at_map),
+ KUNIT_CASE(test_select_at_pin),
{}
};
--
2.54.0
next prev parent reply other threads:[~2026-10-26 5:38 UTC|newest]
Thread overview: 71+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-26 5:35 [PATCH 00/26] DMA-buf mapping types Alistair Popple
2026-10-26 5:35 ` [PATCH 01/26] dma-buf: Introduce " Alistair Popple
2026-10-26 7:00 ` sashiko-bot
2026-10-26 5:35 ` [PATCH 02/26] dma-buf: Add the SGT DMA mapping type Alistair Popple
2026-10-26 7:04 ` sashiko-bot
2026-10-26 5:35 ` [PATCH 03/26] dma-buf: Add dma_buf_mapping_attach() Alistair Popple
2026-10-26 7:20 ` sashiko-bot
2026-10-26 5:35 ` [PATCH 04/26] dma-buf: Route SGT related actions through attach->map_type Alistair Popple
2026-10-26 7:29 ` sashiko-bot
2026-10-26 5:35 ` [PATCH 05/26] dma-buf: Let the exporter select mapping type at pin/map Alistair Popple
2026-10-26 7:39 ` sashiko-bot
2026-10-26 8:03 ` Karl Mehltretter
2026-10-27 5:46 ` Alistair Popple
2026-10-26 5:35 ` [PATCH 06/26] dma-buf: Add match set queries for exporter placement decisions Alistair Popple
2026-10-26 7:42 ` sashiko-bot
2026-10-26 5:35 ` [PATCH 07/26] dma-buf: Add dma_buf_mapping_map() returning a type-tagged mapping Alistair Popple
2026-10-26 7:56 ` sashiko-bot
2026-10-26 5:35 ` [PATCH 08/26] dma-buf: Allow single exporter drivers to avoid the match_mapping function Alistair Popple
2026-10-26 7:58 ` sashiko-bot
2026-10-26 5:36 ` [PATCH 09/26] drm: Check the SGT ops for drm_gem_map_dma_buf() Alistair Popple
2026-10-26 8:02 ` sashiko-bot
2026-10-26 5:36 ` [PATCH 10/26] dma-buf: Convert all the simple exporters to use SGT mapping type Alistair Popple
2026-10-26 8:12 ` sashiko-bot
2026-10-26 5:36 ` [PATCH 11/26] drm/vmwgfx: Use match_mapping instead of dummy calls Alistair Popple
2026-10-26 8:13 ` sashiko-bot
2026-10-26 5:36 ` [PATCH 12/26] accel/habanalabs: Use the SGT_P2P mapping type Alistair Popple
2026-10-26 8:18 ` sashiko-bot
2026-10-26 5:36 ` [PATCH 13/26] drm/xe/dma-buf: Use the SGT " Alistair Popple
2026-10-26 8:34 ` sashiko-bot
2026-10-26 5:36 ` [PATCH 14/26] drm/amdgpu: " Alistair Popple
2026-10-26 8:46 ` sashiko-bot
2026-10-26 5:36 ` [PATCH 15/26] vfio/pci: Change the DMA-buf exporter to use mapping_type Alistair Popple
2026-10-26 9:00 ` sashiko-bot
2026-10-26 5:36 ` [PATCH 16/26] RDMA/uverbs: Use the SGT_P2P mapping type Alistair Popple
2026-10-26 9:16 ` sashiko-bot
2026-10-26 5:36 ` [PATCH 17/26] dma-buf: Update dma_buf_phys_vec_to_sgt() to use the SGT " Alistair Popple
2026-10-26 9:26 ` sashiko-bot
2026-10-26 5:36 ` [PATCH 18/26] iio: buffer: convert " Alistair Popple
2026-10-26 9:28 ` sashiko-bot
2026-10-26 5:36 ` [PATCH 19/26] functionfs: " Alistair Popple
2026-10-26 9:35 ` sashiko-bot
2026-10-26 5:36 ` [PATCH 20/26] dma-buf: Remove unused SGT stuff from the common structures Alistair Popple
2026-10-26 9:39 ` sashiko-bot
2026-10-26 5:36 ` [PATCH 21/26] treewide: Rename dma_buf_map_attachment(_unlocked) to dma_buf_sgt_ Alistair Popple
2026-10-26 9:49 ` sashiko-bot
2026-10-27 12:05 ` Christian König
2026-10-27 12:35 ` Jason Gunthorpe
2026-10-26 5:36 ` [PATCH 22/26] treewide: Rename dma_buf_unmap_attachment(_unlocked) to dma_buf_sgt_* Alistair Popple
2026-10-26 9:56 ` sashiko-bot
2026-10-26 5:36 ` [PATCH 23/26] treewide: Rename dma_buf_attach() to dma_buf_sgt_attach() Alistair Popple
2026-10-26 10:01 ` sashiko-bot
2026-10-27 2:15 ` Karl Mehltretter
2026-10-27 3:42 ` Mikko Perttunen
2026-10-27 12:33 ` Jason Gunthorpe
2026-10-27 13:34 ` Mikko Perttunen
2026-10-27 13:43 ` Christian König
2026-10-27 12:11 ` Christian König
2026-10-27 12:31 ` Jason Gunthorpe
2026-10-27 12:07 ` Christian König
2026-10-27 12:38 ` Jason Gunthorpe
2026-10-27 13:17 ` Christian König
2026-10-27 14:08 ` Jason Gunthorpe
2026-10-27 14:28 ` Christian König
2026-10-27 14:44 ` Jason Gunthorpe
2026-10-27 14:54 ` Christian König
2026-10-26 5:36 ` [PATCH 24/26] treewide: Rename dma_buf_dynamic_attach() to dma_buf_sgt_dynamic_attach() Alistair Popple
2026-10-26 10:13 ` sashiko-bot
2026-10-26 5:36 ` [PATCH 25/26] dma-buf: Add kunit tests for mapping type Alistair Popple
2026-10-26 10:19 ` sashiko-bot
2026-10-26 5:36 ` Alistair Popple [this message]
2026-10-26 10:22 ` [PATCH 26/26] dma-buf: Add kunit tests for mapping type selection sashiko-bot
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=20261026053623.202642-27-apopple@nvidia.com \
--to=apopple@nvidia.com \
--cc=aik@amd.com \
--cc=alexander.deucher@amd.com \
--cc=amd-gfx@lists.freedesktop.org \
--cc=baolu.lu@linux.intel.com \
--cc=christian.koenig@amd.com \
--cc=dongwon.kim@intel.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=felix.kuehling@amd.com \
--cc=intel-xe@lists.freedesktop.org \
--cc=iommu@lists.linux.dev \
--cc=jgg@nvidia.com \
--cc=jhubbard@nvidia.com \
--cc=kevin.tian@intel.com \
--cc=kmehltretter@gmail.com \
--cc=leonro@nvidia.com \
--cc=linaro-mm-sig@lists.linaro.org \
--cc=linux-media@vger.kernel.org \
--cc=linux-rdma@vger.kernel.org \
--cc=matthew.brost@intel.com \
--cc=simona.vetter@ffwll.ch \
--cc=sumit.semwal@linaro.org \
--cc=thomas.hellstrom@linux.intel.com \
--cc=treding@nvidia.com \
--cc=vivek.kasireddy@intel.com \
--cc=yilun.xu@linux.intel.com \
--cc=zhipingz@meta.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