From: Niranjana Vishwanathapura <niranjana.vishwanathapura@intel.com>
To: <priyanka.dandamudi@intel.com>
Cc: <igt-dev@lists.freedesktop.org>
Subject: Re: [PATCH i-g-t 5/7] tests/intel/xe_exec_reset: Add multi queue subtests
Date: Wed, 31 Dec 2025 20:17:03 -0800 [thread overview]
Message-ID: <aVX1Pyb02_3hU6cp@nvishwa1-desk> (raw)
In-Reply-To: <20251219120154.695287-6-priyanka.dandamudi@intel.com>
On Fri, Dec 19, 2025 at 05:31:52PM +0530, priyanka.dandamudi@intel.com wrote:
>From: Apoorva Singh <apoorva.singh@intel.com>
>
>Add multi queue subtests with gt reset, engine reset,
>close fd, close-execqueues, cat-error.
>
>Signed-off-by: Apoorva Singh <apoorva.singh@intel.com>
>Signed-off-by: Fei Yang <fei.yang@intel.com>
>Signed-off-by: Katarzyna Piecielska <katarzyna.piecielska@intel.com>
>Signed-off-by: Priyanka Dandamudi <priyanka.dandamudi@intel.com>
>Signed-off-by: Daniel Charles <daniel.charles@intel.com>
>Signed-off-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>
>---
> lib/xe/xe_legacy.c | 38 +++++--
> tests/intel/xe_exec_reset.c | 197 ++++++++++++++++++++++++++++++++++--
> 2 files changed, 222 insertions(+), 13 deletions(-)
>
>diff --git a/lib/xe/xe_legacy.c b/lib/xe/xe_legacy.c
>index 084445305..6572617fd 100644
>--- a/lib/xe/xe_legacy.c
>+++ b/lib/xe/xe_legacy.c
>@@ -26,6 +26,10 @@
> #define GT_RESET (0x1 << 0)
> #define MAX_N_EXECQUEUES 16
>
>+#define MULTI_QUEUE (0x1 << 20)
>+#define ENGINE_RESET (0x1 << 21)
>+#define FAULT_ON_SECONDARY_QUEUE (0x1 << 22)
>+
> /**
> * xe_legacy_test_mode:
> * @fd: file descriptor
>@@ -69,11 +73,18 @@ xe_legacy_test_mode(int fd, struct drm_xe_engine_class_instance *eci,
> .ctx_ticks = flags & LONG_SPIN ?
> xe_spin_nsec_to_ticks(fd, 0, THREE_SEC) : 0,
> };
>- int i, b;
>+ int i, b, hang_exec_queue = n_exec_queues / 2;
>+ int fault_position = 0;
> int extra_execs = (flags & LONG_SPIN_REUSE_QUEUE) ? n_exec_queues : 0;
>
> igt_assert_lte(n_exec_queues, MAX_N_EXECQUEUES);
>
>+ igt_assert_f(!(flags & FAULT_ON_SECONDARY_QUEUE) || (flags & MULTI_QUEUE),
>+ "FAULT_ON_SECONDARY_QUEUE requires MULTI_QUEUE to be set");
Isn't the ENGINE_RESET test also only valid if MULTI_QUEUE is valid?
If so, we should assert for that also. If not, then we need to add a subtest
for that only (without multi-queue).
>+
>+ if ((flags & FAULT_ON_SECONDARY_QUEUE) && (flags & CAT_ERROR))
>+ fault_position = 1; /* index that complies with index % n_exec_queues != 0 */
>+
> if (flags & COMPRESSION)
> igt_require(intel_gen(intel_get_drm_devid(fd)) >= 20);
>
>@@ -101,7 +112,20 @@ xe_legacy_test_mode(int fd, struct drm_xe_engine_class_instance *eci,
> data = xe_bo_map(fd, bo, bo_size);
>
> for (i = 0; i < n_exec_queues; i++) {
>- exec_queues[i] = xe_exec_queue_create(fd, vm, eci, 0);
>+ if (flags & MULTI_QUEUE) {
>+ struct drm_xe_ext_set_property multi_queue = {
>+ .base.next_extension = 0,
>+ .base.name = DRM_XE_EXEC_QUEUE_EXTENSION_SET_PROPERTY,
>+ .property = DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_GROUP,
>+ };
>+
>+ uint64_t ext = to_user_pointer(&multi_queue);
>+
>+ multi_queue.value = i ? exec_queues[0] : DRM_XE_MULTI_GROUP_CREATE;
>+ exec_queues[i] = xe_exec_queue_create(fd, vm, eci, ext);
>+ } else {
>+ exec_queues[i] = xe_exec_queue_create(fd, vm, eci, 0);
>+ }
> syncobjs[i] = syncobj_create(fd, 0);
> }
>
>@@ -123,7 +147,7 @@ xe_legacy_test_mode(int fd, struct drm_xe_engine_class_instance *eci,
> }
>
> for (i = 0; i < n_execs; i++) {
>- u64 base_addr = (!use_capture_mode && (flags & CAT_ERROR) && !i)
>+ u64 base_addr = (!use_capture_mode && (flags & CAT_ERROR) && i == fault_position)
> ? (addr + bo_size * 128) : addr;
> u64 batch_offset = (char *)&data[i].batch - (char *)data;
> u64 batch_addr = base_addr + batch_offset;
>@@ -133,7 +157,8 @@ xe_legacy_test_mode(int fd, struct drm_xe_engine_class_instance *eci,
> u64 exec_addr;
> int e = i % n_exec_queues;
>
>- if (!i || flags & CANCEL ||
>+ if ((flags & ENGINE_RESET && e == hang_exec_queue) ||
>+ !i || flags & CANCEL ||
> (flags & LONG_SPIN && i < n_exec_queues)) {
> spin_opts.addr = base_addr + spin_offset;
> xe_spin_init(&data[i].spin, &spin_opts);
>@@ -157,7 +182,7 @@ xe_legacy_test_mode(int fd, struct drm_xe_engine_class_instance *eci,
> exec.exec_queue_id = exec_queues[e];
> exec.address = exec_addr;
>
>- if (e != i)
>+ if (e != i && !(flags & ENGINE_RESET))
> syncobj_reset(fd, &syncobjs[e], 1);
>
Why is this required? Even if we reset syncobj of the hanging job,
the new syncobj will still fail as its job will be stuck behing the
hanging job, right? Asking because looks like we are skipping this
step irrespective of whether that syncobj is going to hang or not.
> xe_exec(fd, &exec);
>@@ -224,7 +249,8 @@ xe_legacy_test_mode(int fd, struct drm_xe_engine_class_instance *eci,
> xe_vm_unbind_async(fd, vm, 0, 0, addr, bo_size, sync, 1);
> igt_assert(syncobj_wait(fd, &sync[0].handle, 1, INT64_MAX, 0, NULL));
>
>- if (!use_capture_mode && !(flags & (GT_RESET | CANCEL | COMPRESSION))) {
>+ if (!use_capture_mode &&
>+ !(flags & (GT_RESET | ENGINE_RESET | CAT_ERROR | CANCEL | COMPRESSION))) {
> for (i = flags & LONG_SPIN ? n_exec_queues : 1;
> i < n_execs + extra_execs; i++)
> igt_assert_eq(data[i].data, 0xc0ffee);
So, we are skipping data validation for CAT_ERROR also irrespective of whether
it is multi-queue case or not. Is that intended? If so, that should be a separate
patch (independent of multi-queue).
>diff --git a/tests/intel/xe_exec_reset.c b/tests/intel/xe_exec_reset.c
>index 7aaee31dd..fe496a538 100644
>--- a/tests/intel/xe_exec_reset.c
>+++ b/tests/intel/xe_exec_reset.c
>@@ -125,6 +125,10 @@ static void test_spin(int fd, struct drm_xe_engine_class_instance *eci,
> #define SYSTEM (0x1 << 12)
> #define COMPRESSION (0x1 << 13)
>
>+#define MULTI_QUEUE (0x1 << 20)
>+#define ENGINE_RESET (0x1 << 21)
>+#define FAULT_ON_SECONDARY_QUEUE (0x1 << 22)
>+
> /**
> * SUBTEST: %s-cat-error
> * Description: Test %arg[1] cat error
>@@ -355,6 +359,68 @@ test_balancer(int fd, int gt, int class, int n_exec_queues, int n_execs,
> * Description: Test compute mode close exec_queues close fd
> */
>
>+/**
>+ * SUBTEST: multi-queue-cat-error
>+ * Mega feature: MultiQ
>+ * Sub-category: MultiQ tests
>+ * Description: Test cat error with multi_queue
>+ *
>+ * SUBTEST: multi-queue-cat-error-on-secondary
>+ * Mega feature: MultiQ
>+ * Sub-category: MultiQ tests
>+ * Description: Test cat error on secondary queue with multi_queue
>+ *
>+ * SUBTEST: multi-queue-gt-reset
>+ * Mega feature: MultiQ
>+ * Sub-category: MultiQ tests
>+ * Description: Test GT reset with multi_queue
>+ *
>+ * SUBTEST: multi-queue-engine-reset
>+ * Mega feature: MultiQ
>+ * Sub-category: MultiQ tests
>+ * Description: Test engine reset with multi_queue
>+ *
>+ * SUBTEST: multi-queue-close-fd
>+ * Mega feature: MultiQ
>+ * Sub-category: MultiQ tests
>+ * Description: Test close fd with multi_queue
>+ *
>+ * SUBTEST: multi-queue-close-execqueues
>+ * Mega feature: MultiQ
>+ * Sub-category: MultiQ tests
>+ * Description: Test close execqueues with multi_queue
>+ *
>+ * SUBTEST: cm-multi-queue-cat-error
>+ * Mega feature: MultiQ
>+ * Sub-category: MultiQ tests
>+ * Description: Test compute mode cat error with multi_queue
>+ *
>+ * SUBTEST: cm-multi-queue-cat-error-on-secondary
>+ * Mega feature: MultiQ
>+ * Sub-category: MultiQ tests
>+ * Description: Test compute mode cat error with multi_queue
>+ *
>+ * SUBTEST: cm-multi-queue-gt-reset
>+ * Mega feature: MultiQ
>+ * Sub-category: MultiQ tests
>+ * Description: Test compute mode GT reset with multi_queue
>+ *
>+ * SUBTEST: cm-multi-queue-engine-reset
>+ * Mega feature: MultiQ
>+ * Sub-category: MultiQ tests
>+ * Description: Test compute mode engine reset with multi_queue
>+ *
>+ * SUBTEST: cm-multi-queue-close-fd
>+ * Mega feature: MultiQ
>+ * Sub-category: MultiQ tests
>+ * Description: Test compute mode close fd with multi_queue
>+ *
>+ * SUBTEST: cm-multi-queue-close-execqueues
>+ * Mega feature: MultiQ
>+ * Sub-category: MultiQ tests
>+ * Description: Test compute mode close execqueues with multi_queue
>+ */
>+
> static void
> test_compute_mode(int fd, struct drm_xe_engine_class_instance *eci,
> int n_exec_queues, int n_execs, unsigned int flags)
>@@ -383,10 +449,17 @@ test_compute_mode(int fd, struct drm_xe_engine_class_instance *eci,
> uint32_t data;
> } *data;
> struct xe_spin_opts spin_opts = { .preempt = flags & PREEMPT };
>- int i, b;
>+ int i, b, hang_exec_queue = n_exec_queues / 2;
>+ int fault_position = 0;
>
> igt_assert_lte(n_exec_queues, MAX_N_EXECQUEUES);
>
>+ igt_assert_f(!(flags & FAULT_ON_SECONDARY_QUEUE) || (flags & MULTI_QUEUE),
>+ "FAULT_ON_SECONDARY_QUEUE requires MULTI_QUEUE to be set");
>+
>+ if ((flags & FAULT_ON_SECONDARY_QUEUE) && (flags & CAT_ERROR))
>+ fault_position = 1; /* index that complies with index % n_exec_queues != 0 */
>+
> if (flags & CLOSE_FD)
> fd = drm_open_driver(DRIVER_XE);
>
>@@ -401,7 +474,20 @@ test_compute_mode(int fd, struct drm_xe_engine_class_instance *eci,
> memset(data, 0, bo_size);
>
> for (i = 0; i < n_exec_queues; i++) {
>- exec_queues[i] = xe_exec_queue_create(fd, vm, eci, 0);
>+ if (flags & MULTI_QUEUE) {
>+ struct drm_xe_ext_set_property multi_queue = {
>+ .base.next_extension = 0,
>+ .base.name = DRM_XE_EXEC_QUEUE_EXTENSION_SET_PROPERTY,
>+ .property = DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_GROUP,
>+ };
>+
>+ uint64_t ext = to_user_pointer(&multi_queue);
>+
>+ multi_queue.value = i ? exec_queues[0] : DRM_XE_MULTI_GROUP_CREATE;
>+ exec_queues[i] = xe_exec_queue_create(fd, vm, eci, ext);
>+ } else {
>+ exec_queues[i] = xe_exec_queue_create(fd, vm, eci, 0);
>+ }
> };
>
> sync[0].addr = to_user_pointer(&data[0].vm_sync);
>@@ -411,7 +497,7 @@ test_compute_mode(int fd, struct drm_xe_engine_class_instance *eci,
> data[0].vm_sync = 0;
>
> for (i = 0; i < n_execs; i++) {
>- uint64_t base_addr = flags & CAT_ERROR && !i ?
>+ uint64_t base_addr = flags & CAT_ERROR && i == fault_position ?
> addr + bo_size * 128 : addr;
> uint64_t batch_offset = (char *)&data[i].batch - (char *)data;
> uint64_t batch_addr = base_addr + batch_offset;
>@@ -421,7 +507,7 @@ test_compute_mode(int fd, struct drm_xe_engine_class_instance *eci,
> uint64_t exec_addr;
> int e = i % n_exec_queues;
>
>- if (!i || flags & CANCEL) {
>+ if ((flags & ENGINE_RESET && e == hang_exec_queue) || !i || flags & CANCEL) {
> spin_opts.addr = base_addr + spin_offset;
> xe_spin_init(&data[i].spin, &spin_opts);
> exec_addr = spin_opts.addr;
>@@ -466,8 +552,14 @@ test_compute_mode(int fd, struct drm_xe_engine_class_instance *eci,
> int err;
>
> err = __xe_wait_ufence(fd, &data[i].exec_sync, USER_FENCE_VALUE,
>- exec_queues[i % n_exec_queues], &timeout);
>- if (flags & GT_RESET || flags & CAT_ERROR)
>+ exec_queues[i % n_exec_queues], &timeout);
Looks like unwanted indentation change.
>+ if (flags & FAULT_ON_SECONDARY_QUEUE) {
>+ /* exec may result on -ETIME for fault on secondary queue */
>+ igt_assert(err == -ETIME || err == -EIO || !err);
>+ if (err == -ETIME)
>+ /* no need to continue checking once -ETIME happened*/
>+ break;
>+ } else if (flags & GT_RESET || flags & CAT_ERROR)
> /* exec races with reset: may return -EIO or complete */
> igt_assert(err == -EIO || !err);
> else
>@@ -478,7 +570,7 @@ test_compute_mode(int fd, struct drm_xe_engine_class_instance *eci,
> xe_vm_unbind_async(fd, vm, 0, 0, addr, bo_size, sync, 1);
> xe_wait_ufence(fd, &data[0].vm_sync, USER_FENCE_VALUE, 0, 3 * NSEC_PER_SEC);
>
>- if (!(flags & (GT_RESET | CANCEL))) {
>+ if (!(flags & (GT_RESET | CANCEL | ENGINE_RESET | CAT_ERROR))) {
> for (i = 1; i < n_execs; i++)
> igt_assert_eq(data[i].data, 0xc0ffee);
> }
Some legacy mode comments applies here for compute mode also.
Niranjana
>@@ -979,6 +1071,97 @@ int igt_main()
> xe_for_each_gt(fd, gt)
> gt_mocs_reset(fd, gt);
>
>+ igt_subtest("multi-queue-cat-error") {
>+ igt_require(intel_graphics_ver(intel_get_drm_devid(fd)) >= IP_VER(35, 0));
>+ xe_for_each_multi_queue_engine(fd, hwe)
>+ xe_legacy_test_mode(fd, hwe, 16, 256, CAT_ERROR |
>+ MULTI_QUEUE, LEGACY_MODE_ADDR,
>+ false);
>+ }
>+
>+ igt_subtest("multi-queue-cat-error-on-secondary") {
>+ igt_require(intel_graphics_ver(intel_get_drm_devid(fd)) >= IP_VER(35, 0));
>+ xe_for_each_multi_queue_engine(fd, hwe)
>+ xe_legacy_test_mode(fd, hwe, 16, 256, CAT_ERROR |
>+ MULTI_QUEUE | FAULT_ON_SECONDARY_QUEUE, LEGACY_MODE_ADDR,
>+ false);
>+ }
>+
>+ igt_subtest("multi-queue-gt-reset") {
>+ igt_require(intel_graphics_ver(intel_get_drm_devid(fd)) >= IP_VER(35, 0));
>+ xe_for_each_multi_queue_engine(fd, hwe)
>+ xe_legacy_test_mode(fd, hwe, 16, 256, GT_RESET |
>+ MULTI_QUEUE, LEGACY_MODE_ADDR,
>+ false);
>+ }
>+
>+ igt_subtest("multi-queue-engine-reset") {
>+ igt_require(intel_graphics_ver(intel_get_drm_devid(fd)) >= IP_VER(35, 0));
>+ xe_for_each_multi_queue_engine(fd, hwe)
>+ xe_legacy_test_mode(fd, hwe, 16, 256, ENGINE_RESET |
>+ MULTI_QUEUE, LEGACY_MODE_ADDR,
>+ false);
>+ }
>+
>+ igt_subtest("multi-queue-close-fd") {
>+ igt_require(intel_graphics_ver(intel_get_drm_devid(fd)) >= IP_VER(35, 0));
>+ xe_for_each_multi_queue_engine(fd, hwe)
>+ xe_legacy_test_mode(-1, hwe, 16, 256, CLOSE_FD |
>+ MULTI_QUEUE, LEGACY_MODE_ADDR,
>+ false);
>+ }
>+
>+ igt_subtest("multi-queue-close-execqueues") {
>+ igt_require(intel_graphics_ver(intel_get_drm_devid(fd)) >= IP_VER(35, 0));
>+ xe_for_each_multi_queue_engine(fd, hwe)
>+ xe_legacy_test_mode(-1, hwe, 16, 256, CLOSE_EXEC_QUEUES | CLOSE_FD |
>+ MULTI_QUEUE, LEGACY_MODE_ADDR,
>+ false);
>+ }
>+
>+
>+ igt_subtest("cm-multi-queue-cat-error") {
>+ igt_require(intel_graphics_ver(intel_get_drm_devid(fd)) >= IP_VER(35, 0));
>+ xe_for_each_multi_queue_engine(fd, hwe)
>+ test_compute_mode(fd, hwe, 16, 256, CAT_ERROR |
>+ MULTI_QUEUE);
>+ }
>+
>+ igt_subtest("cm-multi-queue-cat-error-on-secondary") {
>+ igt_require(intel_graphics_ver(intel_get_drm_devid(fd)) >= IP_VER(35, 0));
>+ xe_for_each_multi_queue_engine(fd, hwe)
>+ test_compute_mode(fd, hwe, 16, 256, CAT_ERROR |
>+ MULTI_QUEUE | FAULT_ON_SECONDARY_QUEUE);
>+ }
>+
>+ igt_subtest("cm-multi-queue-gt-reset") {
>+ igt_require(intel_graphics_ver(intel_get_drm_devid(fd)) >= IP_VER(35, 0));
>+ xe_for_each_multi_queue_engine(fd, hwe)
>+ test_compute_mode(fd, hwe, 16, 256, GT_RESET |
>+ MULTI_QUEUE);
>+ }
>+
>+ igt_subtest("cm-multi-queue-engine-reset") {
>+ igt_require(intel_graphics_ver(intel_get_drm_devid(fd)) >= IP_VER(35, 0));
>+ xe_for_each_multi_queue_engine(fd, hwe)
>+ test_compute_mode(fd, hwe, 16, 256, ENGINE_RESET |
>+ MULTI_QUEUE);
>+ }
>+
>+ igt_subtest("cm-multi-queue-close-fd") {
>+ igt_require(intel_graphics_ver(intel_get_drm_devid(fd)) >= IP_VER(35, 0));
>+ xe_for_each_multi_queue_engine(fd, hwe)
>+ test_compute_mode(-1, hwe, 16, 256, CLOSE_FD |
>+ MULTI_QUEUE);
>+ }
>+
>+ igt_subtest("cm-multi-queue-close-execqueues") {
>+ igt_require(intel_graphics_ver(intel_get_drm_devid(fd)) >= IP_VER(35, 0));
>+ xe_for_each_multi_queue_engine(fd, hwe)
>+ test_compute_mode(-1, hwe, 16, 256, CLOSE_EXEC_QUEUES | CLOSE_FD |
>+ MULTI_QUEUE);
>+ }
>+
> igt_fixture()
> drm_close_driver(fd);
> }
>--
>2.43.0
>
next prev parent reply other threads:[~2026-01-01 4:17 UTC|newest]
Thread overview: 29+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-12-19 12:01 [PATCH i-g-t 0/7] Extend multi queue feature validation support priyanka.dandamudi
2025-12-19 12:01 ` [PATCH i-g-t 1/7] tests/intel/xe_exec_fault_mode: Add multi queue test support priyanka.dandamudi
2025-12-31 19:55 ` Niranjana Vishwanathapura
2026-01-07 6:24 ` Dandamudi, Priyanka
2025-12-19 12:01 ` [PATCH i-g-t 2/7] tests/intel/xe_evict: Add basic multi queue test priyanka.dandamudi
2026-01-01 0:21 ` Niranjana Vishwanathapura
2026-01-07 6:25 ` Dandamudi, Priyanka
2026-01-01 0:34 ` Niranjana Vishwanathapura
2025-12-19 12:01 ` [PATCH i-g-t 3/7] tests/intel/xe_evict: Add priority for multi queue priyanka.dandamudi
2026-01-01 0:23 ` Niranjana Vishwanathapura
2026-01-14 6:29 ` Dandamudi, Priyanka
2025-12-19 12:01 ` [PATCH i-g-t 4/7] tests/intel/xe_evict: Enhance testing of multi queue functionality priyanka.dandamudi
2026-01-01 0:27 ` Niranjana Vishwanathapura
2026-01-14 6:32 ` Dandamudi, Priyanka
2025-12-19 12:01 ` [PATCH i-g-t 5/7] tests/intel/xe_exec_reset: Add multi queue subtests priyanka.dandamudi
2026-01-01 4:17 ` Niranjana Vishwanathapura [this message]
2026-01-14 6:39 ` Dandamudi, Priyanka
2025-12-19 12:01 ` [PATCH i-g-t 6/7] tests/intel/xe_exec_threads: Add multi queue basic test priyanka.dandamudi
2025-12-31 21:15 ` Niranjana Vishwanathapura
2026-01-01 4:19 ` Niranjana Vishwanathapura
2026-01-14 6:33 ` Dandamudi, Priyanka
2026-01-14 6:28 ` Dandamudi, Priyanka
2025-12-19 12:01 ` [PATCH i-g-t 7/7] tests/intel/xe_exec_threads: Extend multi queue testing priyanka.dandamudi
2025-12-31 21:34 ` Niranjana Vishwanathapura
2026-01-14 6:36 ` Dandamudi, Priyanka
2025-12-22 21:58 ` ✓ Xe.CI.BAT: success for Extend multi queue feature validation support (rev2) Patchwork
2025-12-22 22:11 ` ✓ i915.CI.BAT: " Patchwork
2025-12-23 4:36 ` ✗ Xe.CI.Full: failure " Patchwork
2025-12-24 6:06 ` ✗ i915.CI.Full: " Patchwork
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=aVX1Pyb02_3hU6cp@nvishwa1-desk \
--to=niranjana.vishwanathapura@intel.com \
--cc=igt-dev@lists.freedesktop.org \
--cc=priyanka.dandamudi@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