From: "Daniel P. Smith" <dpsmith@apertussolutions.com>
To: xen-devel@lists.xenproject.org
Cc: "Daniel P. Smith" <dpsmith@apertussolutions.com>,
jason.andryuk@amd.com, stefano.stabellini@amd.com,
agarciav@amd.com, "Andrew Cooper" <andrew.cooper3@citrix.com>,
"Anthony PERARD" <anthony.perard@vates.tech>,
"Michal Orzel" <michal.orzel@amd.com>,
"Jan Beulich" <jbeulich@suse.com>,
"Julien Grall" <julien@xen.org>,
"Roger Pau Monné" <roger.pau@citrix.com>,
"Stefano Stabellini" <sstabellini@kernel.org>
Subject: [RFCv2 38/38] tools: introduce hyperlaunch domain late init
Date: Thu, 15 May 2025 09:19:50 -0400 [thread overview]
Message-ID: <20250515131951.5594-9-dpsmith@apertussolutions.com> (raw)
In-Reply-To: <20250515131951.5594-1-dpsmith@apertussolutions.com>
The late domain init helper is a helper tool for late setup of Xenstore for a
domain that was created by the hypervisor using hyperlaunch.
Signed-off-by: Daniel P. Smith <dpsmith@apertussolutions.com>
---
.gitignore | 1 +
tools/helpers/Makefile | 12 +
tools/helpers/late-init-domains.c | 364 ++++++++++++++++++++++++++++++
tools/helpers/late-init-domains.h | 18 ++
tools/helpers/xs-helpers.c | 117 ++++++++++
tools/helpers/xs-helpers.h | 26 +++
6 files changed, 538 insertions(+)
create mode 100644 tools/helpers/late-init-domains.c
create mode 100644 tools/helpers/late-init-domains.h
create mode 100644 tools/helpers/xs-helpers.c
create mode 100644 tools/helpers/xs-helpers.h
diff --git a/.gitignore b/.gitignore
index 53f5df000383..7b0c390dbe0d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -122,6 +122,7 @@ tools/flask/utils/flask-label-pci
tools/helpers/init-dom0less
tools/helpers/init-xenstore-domain
tools/helpers/xen-init-dom0
+tools/helpers/late-init-domains
tools/hotplug/common/hotplugpath.sh
tools/hotplug/FreeBSD/rc.d/xencommons
tools/hotplug/FreeBSD/rc.d/xendriverdomain
diff --git a/tools/helpers/Makefile b/tools/helpers/Makefile
index 09590eb5b6f0..26fa079e8b1f 100644
--- a/tools/helpers/Makefile
+++ b/tools/helpers/Makefile
@@ -14,6 +14,7 @@ ifeq ($(CONFIG_ARM),y)
TARGETS += init-dom0less
endif
endif
+TARGETS += late-init-domains
XEN_INIT_DOM0_OBJS = xen-init-dom0.o init-dom-json.o
$(XEN_INIT_DOM0_OBJS): CFLAGS += $(CFLAGS_libxentoollog)
@@ -39,6 +40,14 @@ $(INIT_DOM0LESS_OBJS): CFLAGS += $(CFLAGS_libxenctrl)
$(INIT_DOM0LESS_OBJS): CFLAGS += $(CFLAGS_libxenevtchn)
init-dom0less: LDLIBS += $(call xenlibs-ldlibs,ctrl evtchn toollog store light guest foreignmemory)
+LATE_INIT_DOMAINS_OBJS = late-init-domains.o xs-helpers.o init-dom-json.o
+$(LATE_INIT_DOMAINS_OBJS): CFLAGS += $(CFLAGS_libxentoollog)
+$(LATE_INIT_DOMAINS_OBJS): CFLAGS += $(CFLAGS_libxenguest)
+$(LATE_INIT_DOMAINS_OBJS): CFLAGS += $(CFLAGS_libxenlight)
+$(LATE_INIT_DOMAINS_OBJS): CFLAGS += $(CFLAGS_libxenctrl)
+$(LATE_INIT_DOMAINS_OBJS): CFLAGS += $(CFLAGS_libxenstore)
+late-init-domains: LDLIBS += $(call xenlibs-ldlibs,ctrl toollog store light guest)
+
.PHONY: all
all: $(TARGETS)
@@ -51,6 +60,9 @@ init-xenstore-domain: $(INIT_XENSTORE_DOMAIN_OBJS)
init-dom0less: $(INIT_DOM0LESS_OBJS)
$(CC) $(LDFLAGS) -o $@ $(INIT_DOM0LESS_OBJS) $(LDLIBS) $(APPEND_LDFLAGS)
+late-init-domains: $(LATE_INIT_DOMAINS_OBJS)
+ $(CC) $(LDFLAGS) -o $@ $(LATE_INIT_DOMAINS_OBJS) $(LDLIBS) $(APPEND_LDFLAGS)
+
.PHONY: install
install: all
$(INSTALL_DIR) $(DESTDIR)$(LIBEXEC_BIN)
diff --git a/tools/helpers/late-init-domains.c b/tools/helpers/late-init-domains.c
new file mode 100644
index 000000000000..06911d2e93d1
--- /dev/null
+++ b/tools/helpers/late-init-domains.c
@@ -0,0 +1,364 @@
+
+#include <errno.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <libxl.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <xenctrl.h>
+#include <xenguest.h>
+#include <xenstore.h>
+#include <xentoollog.h>
+#include <xen/io/xenbus.h>
+
+#include "init-dom-json.h"
+#include "late-init-domains.h"
+#include "xs-helpers.h"
+
+static struct option options[] = {
+ { "console", 0, NULL, 'c' },
+ { "xenstore", 1, NULL, 'x' },
+ { "force", 0, NULL, 'f' },
+ { "verbose", 0, NULL, 'v' },
+ { "help", 0, NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+};
+
+static void usage(void)
+{
+ fprintf(stderr,
+"Usage:\n"
+"\n"
+"late-init-domains <options>\n"
+"\n"
+"where options may include:\n"
+"\n"
+" --console <con domid> configure the console\n"
+" --xenstore <xs domid> domain id of the xenstore domain\n"
+" --force force domain introduction even if xenstore entries exist\n"
+" -v[v[v]] verbosity constructing xenstore tree\n"
+" --help help message\n");
+}
+
+#define XS_DOM_PERM(x, d, k, v) \
+ ret = do_xs_write_dom_with_perm(x, d, k, v, perms, num_perms); \
+ if ( ret != 0 ) return ret \
+
+#define XS_DIR_PERM(x, p, k, v) \
+ ret = do_xs_write_dir_node_with_perm(x, p, k, v, perms, num_perms); \
+ if ( ret != 0 ) return ret \
+
+static int pages_from_hvm_params(
+ struct xc_interface_core *xch, libxl_dominfo *info,
+ struct system_pages *pgs)
+{
+ int ret;
+ domid_t domid = info->domid;
+
+ ret = xc_hvm_param_get(xch, domid, HVM_PARAM_STORE_EVTCHN,
+ &pgs->store.evtchn);
+ if (ret != 0) {
+ fprintf(stderr, "err: failed to get dom%d store evtchn\n", domid);
+ return ret;
+ }
+
+ ret = xc_hvm_param_get(xch, domid, HVM_PARAM_STORE_PFN,
+ &pgs->store.pfn);
+ if (ret < 0) {
+ fprintf(stderr, "err: failed to get dom%d store pfn\n", domid);
+ return ret;
+ }
+
+ if ( pgs->console.enabled )
+ {
+ ret = xc_hvm_param_get(xch, domid, HVM_PARAM_CONSOLE_EVTCHN,
+ &pgs->console.evtchn);
+ if (ret != 0) {
+ fprintf(stderr, "warn: console for dom%d not configured\n", domid);
+ pgs->console.evtchn = pgs->console.pfn = 0;
+ return 0;
+ }
+
+ ret = xc_hvm_param_get(xch, domid, HVM_PARAM_CONSOLE_PFN,
+ &pgs->console.pfn);
+ if (ret < 0) {
+ fprintf(stderr, "warn: console for dom%d not configured\n", domid);
+ pgs->console.evtchn = pgs->console.pfn = 0;
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+static int create_xs_entries(
+ struct xs_handle *xsh, struct system_pages *pgs, libxl_dominfo *di)
+{
+ char path[128], value[16];
+ struct xs_permissions perms[2] = {
+ {.id = pgs->store.be_domid, .perms = XS_PERM_NONE},
+ {.id = di->domid, .perms = XS_PERM_READ},
+ };
+ uint32_t num_perms = (sizeof(perms) / sizeof((perms)[0]));
+ int ret = 0;
+
+ while ( do_xs_start_transaction(xsh) == 0 )
+ {
+ XS_DOM_PERM(xsh, di->domid, "", "");
+
+ snprintf(value, 16, "%d", di->domid);
+ XS_DOM_PERM(xsh, di->domid, "domid", value);
+
+ XS_DOM_PERM(xsh, di->domid, "memory", "");
+ snprintf(value, 16, "%" PRIu64, di->current_memkb);
+ XS_DOM_PERM(xsh, di->domid, "memory/target", value);
+
+ snprintf(value, 16, "%" PRIu64, di->max_memkb);
+ XS_DOM_PERM(xsh, di->domid, "memory/static-max", value);
+
+ XS_DOM_PERM(xsh, di->domid, "store", "");
+ snprintf(value, 16, "%" PRIu64, pgs->store.evtchn);
+ XS_DOM_PERM(xsh, di->domid, "store/port", value);
+
+ snprintf(value, 16, "%" PRIu64, pgs->store.pfn);
+ XS_DOM_PERM(xsh, di->domid, "store/ring-ref", value);
+
+ if ( pgs->console.enabled && pgs->console.evtchn )
+ {
+ char be_path[64], fe_path[64];
+
+ snprintf(fe_path, 64, "/local/domain/%d/console", di->domid);
+ snprintf(be_path, 64, "/local/domain/%d/backend/console/%d/0",
+ pgs->console.be_domid, di->domid);
+
+ /* Backend entries */
+ XS_DIR_PERM(xsh, be_path, "", "");
+ snprintf(value, 16, "%d", di->domid);
+ XS_DIR_PERM(xsh, be_path, "frontend-id", value);
+ XS_DIR_PERM(xsh, be_path, "frontend", fe_path);
+ XS_DIR_PERM(xsh, be_path, "online", "1");
+ XS_DIR_PERM(xsh, be_path, "protocol", "vt100");
+
+ snprintf(value, 16, "%d", XenbusStateInitialising);
+ XS_DIR_PERM(xsh, be_path, "state", value);
+
+ /* Frontend entries */
+ XS_DOM_PERM(xsh, di->domid, "console", "");
+ snprintf(value, 16, "%d", pgs->console.be_domid);
+ XS_DIR_PERM(xsh, fe_path, "backend", be_path);
+ XS_DIR_PERM(xsh, fe_path, "backend-id", value);
+ XS_DIR_PERM(xsh, fe_path, "limit", "1048576");
+ XS_DIR_PERM(xsh, fe_path, "type", "xenconsoled");
+ XS_DIR_PERM(xsh, fe_path, "output", "pty");
+ XS_DIR_PERM(xsh, fe_path, "tty", "");
+
+ snprintf(value, 16, "%" PRIu64, pgs->console.evtchn);
+ XS_DIR_PERM(xsh, fe_path, "port", value);
+
+ snprintf(value, 16, "%" PRIu64, pgs->console.pfn);
+ XS_DIR_PERM(xsh, fe_path, "ring-ref", value);
+
+ }
+
+ snprintf(path, 128, "/libxl/%u", di->domid);
+ switch ( di->domain_type )
+ {
+ case LIBXL_DOMAIN_TYPE_PV:
+ XS_DIR_PERM(xsh, path, "type", "pv");
+ break;
+ case LIBXL_DOMAIN_TYPE_PVH:
+ XS_DIR_PERM(xsh, path, "type", "pvh");
+ break;
+ case LIBXL_DOMAIN_TYPE_HVM:
+ XS_DIR_PERM(xsh, path, "type", "hvm");
+ break;
+ default:
+ break;
+ }
+
+ ret = do_xs_end_transaction(xsh);
+ switch ( ret )
+ {
+ case 0:
+ break; /* proceed to loop break */
+ case -EAGAIN:
+ continue; /* try again */
+ default:
+ return ret; /* failed */
+ }
+
+ break;
+ }
+
+ return ret;
+}
+
+static bool init_domain(
+ struct xc_interface_core *xch, struct xs_handle *xsh,
+ struct system_pages *pgs, libxl_dominfo *di)
+{
+ xen_pfn_t con_pfn = 0L;
+ /*xc_dom_gnttab_seed will do nothing if front == back */
+ uint32_t con_domid = di->domid;
+ bool is_hvm = (di->domain_type == LIBXL_DOMAIN_TYPE_HVM ||
+ di->domain_type == LIBXL_DOMAIN_TYPE_PVH);
+ int ret;
+
+ if ( (ret = pages_from_hvm_params(xch, di, pgs)) != 0 )
+ {
+ fprintf(stderr, "error(%d): unable to fetch dom%d system pages\n", ret,
+ di->domid);
+ return false;
+ }
+
+ if ( pgs->console.enabled && pgs->console.evtchn )
+ {
+ con_domid = pgs->console.be_domid;
+ con_pfn = pgs->console.pfn;
+ }
+
+ ret = xc_dom_gnttab_seed(xch, di->domid, is_hvm, con_pfn,
+ pgs->store.pfn, con_domid, pgs->store.be_domid);
+ if ( ret != 0 )
+ {
+ fprintf(stderr, "error (%d) setting up grant tables for dom%d\n",
+ ret, di->domid);
+ return false;
+ }
+
+ libxl_uuid_generate(&di->uuid);
+ xc_domain_sethandle(xch, di->domid,
+ libxl_uuid_bytearray(&di->uuid));
+
+ if ( (ret = gen_stub_json_config(di->domid, &di->uuid)) != 0 )
+ fprintf(stderr, "warn(%d): unable generate dom%d json stub\n", ret,
+ di->domid);
+
+ if ( (ret = create_xs_entries(xsh, pgs, di)) != 0 )
+ {
+ fprintf(stderr, "error(%d): unable create dom%d xenstore entries\n",
+ ret, di->domid);
+ return false;
+ }
+
+ if ( !xs_introduce_domain(xsh, di->domid, pgs->store.pfn,
+ pgs->store.evtchn) )
+ {
+ fprintf(stderr, "error introducing dom%d\n", di->domid);
+ return false;
+ }
+
+ return true;
+}
+
+int main(int argc, char** argv)
+{
+ int opt, ret, i, nb_vm = 0, count = 0;
+ bool force = false;
+ struct xs_handle *xsh = NULL;
+ struct xc_interface_core *xch = NULL;
+ xentoollog_level minmsglevel = XTL_PROGRESS;
+ xentoollog_logger *logger = NULL;
+ libxl_dominfo *info = NULL;
+ libxl_ctx *ctx;
+ struct system_pages pages = { {0} };
+
+ while ( (opt = getopt_long(argc, argv, "c:x:fv", options, NULL)) != -1 )
+ {
+ switch ( opt )
+ {
+ case 'c':
+ pages.console.be_domid = strtol(optarg, NULL, 10);
+ pages.console.enabled = true;
+ break;
+ case 'x':
+ pages.store.be_domid = strtol(optarg, NULL, 10);
+ break;
+ case 'f':
+ force = true;
+ break;
+ case 'v':
+ if ( minmsglevel > 1 )
+ minmsglevel--;
+ break;
+ case 'h':
+ usage();
+ return 0;
+ default:
+ usage();
+ return 2;
+ }
+ }
+
+ if ( optind != argc )
+ {
+ usage();
+ return 1;
+ }
+
+ logger = (xentoollog_logger *)xtl_createlogger_stdiostream(stderr,
+ minmsglevel, 0);
+
+ xsh = xs_open(0);
+ xch = xc_interface_open(0, 0, 0);
+ if ( xsh == NULL || xch == NULL )
+ {
+ fprintf(stderr, "error: unable to connect to xs and/or xc interface\n");
+ ret = 1;
+ goto out;
+ }
+
+ ret = libxl_ctx_alloc(&ctx, LIBXL_VERSION, 0, NULL);
+ if (ret) {
+ fprintf(stderr, "cannot init xl context\n");
+ goto out;
+ }
+
+ info = libxl_list_domain(ctx, &nb_vm);
+ if (!info) {
+ fprintf(stderr, "libxl_list_vm failed.\n");
+ ret = 1;
+ goto out;
+ }
+
+ for (i = 0; i < nb_vm; i++) {
+ domid_t domid = info[i].domid;
+
+ /* Don't need to check for Dom0 */
+ if (!domid)
+ continue;
+
+ if ( xs_is_domain_introduced(xsh, domid) )
+ {
+ if ( !force )
+ continue;
+
+ fprintf(stderr, "warning: re-introducting domain %d\n", domid);
+ }
+
+ if ( init_domain(xch, xsh, &pages, &info[i]) )
+ count++;
+ }
+
+ printf("initialized %d out of %d domains\n", count, nb_vm);
+
+ ret = 0;
+
+out:
+ if ( info )
+ libxl_dominfo_list_free(info, nb_vm);
+
+ if ( xsh )
+ xs_close(xsh);
+
+ if ( xch )
+ xc_interface_close(xch);
+
+ if ( logger )
+ xtl_logger_destroy(logger);
+
+ return ret;
+}
diff --git a/tools/helpers/late-init-domains.h b/tools/helpers/late-init-domains.h
new file mode 100644
index 000000000000..8d071ef82ea0
--- /dev/null
+++ b/tools/helpers/late-init-domains.h
@@ -0,0 +1,18 @@
+#ifndef __LATE_INIT_PV_H
+#define __LATE_INIT_PV_H
+
+struct system_pages {
+ struct {
+ uint16_t be_domid;
+ uint64_t evtchn;
+ uint64_t pfn;
+ } store;
+ struct {
+ bool enabled;
+ uint16_t be_domid;
+ uint64_t evtchn;
+ uint64_t pfn;
+ } console;
+};
+
+#endif
diff --git a/tools/helpers/xs-helpers.c b/tools/helpers/xs-helpers.c
new file mode 100644
index 000000000000..a4d2bebbbd54
--- /dev/null
+++ b/tools/helpers/xs-helpers.c
@@ -0,0 +1,117 @@
+
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+#include <xenstore.h>
+
+#define MAX_XS_PAATH 100
+
+static xs_transaction_t t_id = XBT_NULL;
+
+int do_xs_start_transaction(struct xs_handle *xsh)
+{
+ t_id = xs_transaction_start(xsh);
+ if (t_id == XBT_NULL)
+ return -errno;
+
+ return 0;
+}
+
+int do_xs_end_transaction(struct xs_handle *xsh)
+{
+ if ( t_id == XBT_NULL )
+ return -EINVAL;
+
+ if (!xs_transaction_end(xsh, t_id, false))
+ return -errno;
+
+ return 0;
+}
+
+int do_xs_write(struct xs_handle *xsh, char *path, char *val)
+{
+ if ( !xs_write(xsh, t_id, path, val, strlen(val)) )
+ {
+ fprintf(stderr, "failed write: %s\n", path);
+ return -errno;
+ }
+
+ return 0;
+}
+
+int do_xs_perms(
+ struct xs_handle *xsh, char *path, struct xs_permissions *perms,
+ uint32_t num_perms)
+{
+ if ( !xs_set_permissions(xsh, t_id, path, perms, num_perms) )
+ {
+ fprintf(stderr, "failed set perm: %s\n", path);
+ return -errno;
+ }
+
+ return 0;
+}
+
+int do_xs_write_dir_node_with_perm(
+ struct xs_handle *xsh, char *dir, char *node, char *val,
+ struct xs_permissions *perms, uint32_t num_perms)
+{
+ char full_path[MAX_XS_PAATH];
+ int ret = 0;
+
+ /*
+ * mainly for creating a value holding node, but
+ * also support creating directory nodes.
+ */
+ if ( strlen(node) != 0 )
+ snprintf(full_path, MAX_XS_PAATH, "%s/%s", dir, node);
+ else
+ snprintf(full_path, MAX_XS_PAATH, "%s", dir);
+
+ ret = do_xs_write(xsh, full_path, val);
+ if ( ret < 0 )
+ return ret;
+
+ if ( perms != NULL && num_perms > 0 )
+ ret = do_xs_perms(xsh, full_path, perms, num_perms);
+
+ return ret;
+}
+
+int do_xs_write_dir_node(
+ struct xs_handle *xsh, char *dir, char *node, char *val)
+{
+ return do_xs_write_dir_node_with_perm(xsh, dir, node, val, NULL, 0);
+}
+
+int do_xs_write_dom_with_perm(
+ struct xs_handle *xsh, uint32_t domid, char *path, char *val,
+ struct xs_permissions *perms, uint32_t num_perms)
+{
+ char full_path[MAX_XS_PAATH];
+ int ret = 0;
+
+ /*
+ * mainly for creating a value holding node, but
+ * also support creating directory nodes.
+ */
+ if ( strlen(path) != 0 )
+ snprintf(full_path, MAX_XS_PAATH, "/local/domain/%d/%s", domid, path);
+ else
+ snprintf(full_path, MAX_XS_PAATH, "/local/domain/%d", domid);
+
+ ret = do_xs_write(xsh, full_path, val);
+ if ( ret < 0 )
+ return ret;
+
+ if ( perms != NULL && num_perms > 0 )
+ ret = do_xs_perms(xsh, full_path, perms, num_perms);
+
+ return ret;
+}
+
+int do_xs_write_dom(
+ struct xs_handle *xsh, uint32_t domid, char *path, char *val)
+{
+ return do_xs_write_dom_with_perm(xsh, domid, path, val, NULL, 0);
+}
diff --git a/tools/helpers/xs-helpers.h b/tools/helpers/xs-helpers.h
new file mode 100644
index 000000000000..89585637d4bb
--- /dev/null
+++ b/tools/helpers/xs-helpers.h
@@ -0,0 +1,26 @@
+#ifndef __XS_HELPERS_H
+#define __XS_HELPERS_H
+
+#include <xenstore.h>
+
+int do_xs_start_transaction(struct xs_handle *xsh);
+int do_xs_end_transaction(struct xs_handle *xsh);
+
+int do_xs_write(struct xs_handle *xsh, char *path, char *val);
+int do_xs_perms(
+ struct xs_handle *xsh, char *path, struct xs_permissions *perms,
+ uint32_t num_perms);
+
+int do_xs_write_dir_node_with_perm(
+ struct xs_handle *xsh, char *dir, char *node, char *val,
+ struct xs_permissions *perms, uint32_t num_perms);
+int do_xs_write_dir_node(
+ struct xs_handle *xsh, char *dir, char *node, char *val);
+
+int do_xs_write_dom_with_perm(
+ struct xs_handle *xsh, uint32_t domid, char *path, char *val,
+ struct xs_permissions *perms, uint32_t num_perms);
+int do_xs_write_dom(
+ struct xs_handle *xsh, uint32_t domid, char *path, char *val);
+
+#endif
--
2.30.2
prev parent reply other threads:[~2025-05-15 13:30 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-05-15 13:19 [RFCv2 30/38] x86/hyperlaunch: introduce concept of core domains Daniel P. Smith
2025-05-15 13:19 ` [RFCv2 31/38] common/gzip: add function to read isize field Daniel P. Smith
2025-05-15 13:19 ` [RFCv2 32/38] x86/hyperlaunch: move headroom under domain builder Daniel P. Smith
2025-05-15 13:19 ` [RFCv2 33/38] x86/hyperlaunch: move kernel extraction " Daniel P. Smith
2025-05-15 13:19 ` [RFCv2 34/38] x86/hyperlaunch: introduce multidomain kconfig option Daniel P. Smith
2025-05-15 13:19 ` [RFCv2 35/38] x86/hyperlaunch: add multidomain construction logic Daniel P. Smith
2025-05-15 13:19 ` [RFCv2 36/38] x86/hyperlaunch: enable unpausing mulitple domains Daniel P. Smith
2025-05-15 13:19 ` [RFCv2 37/38] x86/hyperlaunch: generalize domid assignment Daniel P. Smith
2025-05-15 13:19 ` Daniel P. Smith [this message]
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=20250515131951.5594-9-dpsmith@apertussolutions.com \
--to=dpsmith@apertussolutions.com \
--cc=agarciav@amd.com \
--cc=andrew.cooper3@citrix.com \
--cc=anthony.perard@vates.tech \
--cc=jason.andryuk@amd.com \
--cc=jbeulich@suse.com \
--cc=julien@xen.org \
--cc=michal.orzel@amd.com \
--cc=roger.pau@citrix.com \
--cc=sstabellini@kernel.org \
--cc=stefano.stabellini@amd.com \
--cc=xen-devel@lists.xenproject.org \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.