From: Wei Hu <weh@linux.microsoft.com>
To: linux-hyperv@vger.kernel.org
Cc: linux-kernel@vger.kernel.org,
"K. Y. Srinivasan" <kys@microsoft.com>,
Haiyang Zhang <haiyangz@microsoft.com>,
Wei Liu <wei.liu@kernel.org>, Dexuan Cui <decui@microsoft.com>,
Long Li <longli@microsoft.com>
Subject: [PATCH v1 04/13] mshv: wire SEV-SNP partition ioctls
Date: Fri, 7 Aug 2026 13:51:10 +0000 [thread overview]
Message-ID: <20260807135134.303943-5-weh@linux.microsoft.com> (raw)
In-Reply-To: <20260807135134.303943-1-weh@linux.microsoft.com>
From: Wei Liu <wei.liu@kernel.org>
Wire the SEV-SNP partition ioctls for GPA host-access changes,
isolated-page import, PSP guest requests, AP creation, and SNP partition
teardown.
The GPA host-access ioctl converts the guest addresses to GFNs before
issuing the hypercall. The PSP guest-request path restores host access
to the request/response pages if the hypercall fails, so a failed request
does not leave guest pages inaccessible to the host.
Signed-off-by: Wei Liu <wei.liu@kernel.org>
---
drivers/hv/mshv_root.h | 5 +
drivers/hv/mshv_root_hv_call.c | 35 +++
drivers/hv/mshv_root_main.c | 391 +++++++++++++++++++++++++++++++++
include/hyperv/hvgdk_mini.h | 12 +
4 files changed, 443 insertions(+)
diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h
index 69226179cfbf..0e6d4ce25112 100644
--- a/drivers/hv/mshv_root.h
+++ b/drivers/hv/mshv_root.h
@@ -378,6 +378,11 @@ bool mshv_region_handle_gfn_fault(struct mshv_mem_region *region, u64 gfn);
void mshv_region_movable_fini(struct mshv_mem_region *region);
bool mshv_region_movable_init(struct mshv_mem_region *region);
+int hv_call_set_partition_property(u64 partition_id, u64 property_code,
+ u64 property_value,
+ void (*completion_handler)(void *, u64 *),
+ void *completion_data);
+
#ifdef HV_SUPPORTS_SEV_SNP_GUESTS
int hv_call_import_isolated_pages(u64 partition_id, u64 *pages,
u64 num_pages,
diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c
index 9ddc8dd73da7..bf5636a9ef6a 100644
--- a/drivers/hv/mshv_root_hv_call.c
+++ b/drivers/hv/mshv_root_hv_call.c
@@ -1014,6 +1014,41 @@ int hv_unmap_stats_page(enum hv_stats_object_type type,
}
#ifdef HV_SUPPORTS_SEV_SNP_GUESTS
+int hv_call_set_partition_property(u64 partition_id, u64 property_code,
+ u64 property_value,
+ void (*completion_handler)(void *, u64 *),
+ void *completion_data)
+{
+ u64 status;
+ unsigned long flags;
+ struct hv_input_set_partition_property *input;
+
+ if (!completion_handler) {
+ pr_err("%s: Missing completion handler\n", __func__);
+ return -EINVAL;
+ }
+
+ local_irq_save(flags);
+ input = *this_cpu_ptr(hyperv_pcpu_input_arg);
+
+ memset(input, 0, sizeof(*input));
+ input->partition_id = partition_id;
+ input->property_code = property_code;
+ input->property_value = property_value;
+ status = hv_do_hypercall(HVCALL_SET_PARTITION_PROPERTY, input, NULL);
+ local_irq_restore(flags);
+
+ if (unlikely(status == HV_STATUS_CALL_PENDING))
+ completion_handler(completion_data, &status);
+
+ if (!hv_result_success(status)) {
+ pr_err("%s: %s\n", __func__, hv_result_to_string(status));
+ return hv_result_to_errno(status);
+ }
+
+ return 0;
+}
+
int hv_call_import_isolated_pages(u64 partition_id, u64 *pages,
u64 num_pages,
enum hv_isolated_page_type page_type,
diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index 146726cc4e9b..3590a8ea24b5 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -115,6 +115,9 @@ static u16 mshv_passthru_hvcalls[] = {
HVCALL_GET_PARTITION_PROPERTY,
HVCALL_GET_PARTITION_PROPERTY_EX,
HVCALL_SET_PARTITION_PROPERTY,
+ HVCALL_ISSUE_SNP_PSP_GUEST_REQUEST,
+ HVCALL_COMPLETE_ISOLATED_IMPORT,
+ HVCALL_IMPORT_ISOLATED_PAGES,
HVCALL_INSTALL_INTERCEPT,
HVCALL_GET_VP_REGISTERS,
HVCALL_SET_VP_REGISTERS,
@@ -142,6 +145,8 @@ static bool mshv_hvcall_is_async(u16 code)
{
switch (code) {
case HVCALL_SET_PARTITION_PROPERTY:
+ case HVCALL_IMPORT_ISOLATED_PAGES:
+ case HVCALL_ISSUE_SNP_PSP_GUEST_REQUEST:
return true;
default:
break;
@@ -641,6 +646,42 @@ mshv_partition_region_by_gfn_get(struct mshv_partition *p, u64 gfn)
return region;
}
+static int mshv_gpfns_to_pages(struct mshv_partition *partition,
+ const u64 *gpfns, u64 page_count,
+ struct page **pages)
+{
+ struct mshv_mem_region *region;
+ u64 i;
+ int ret = 0;
+
+ for (i = 0; i < page_count; i++) {
+ u64 gfn = gpfns[i];
+ u64 offset;
+
+ region = mshv_partition_region_by_gfn_get(partition, gfn);
+ if (!region) {
+ pt_err(partition, "Failed to find region for GFN %#llx\n",
+ gfn);
+ return -ERANGE;
+ }
+
+ offset = gfn - region->start_gfn;
+ mutex_lock(®ion->mreg_mutex);
+ if (offset >= region->nr_pages || !region->mreg_pages[offset]) {
+ ret = -EFAULT;
+ mutex_unlock(®ion->mreg_mutex);
+ mshv_region_put(region);
+ return ret;
+ }
+
+ pages[i] = region->mreg_pages[offset];
+ mutex_unlock(®ion->mreg_mutex);
+ mshv_region_put(region);
+ }
+
+ return 0;
+}
+
/**
* mshv_handle_gpa_intercept - Handle GPA (Guest Physical Address) intercepts.
* @vp: Pointer to the virtual processor structure.
@@ -1625,6 +1666,260 @@ mshv_partition_ioctl_initialize(struct mshv_partition *partition)
return ret;
}
+#ifdef HV_SUPPORTS_SEV_SNP_GUESTS
+static int set_sev_control_register(struct mshv_vp *vp,
+ u64 enable_encrypted_state,
+ u64 vmsa_gpa_page_number)
+{
+ struct hv_register_assoc sev_control = {
+ .name = HV_X64_REGISTER_SEV_CONTROL,
+ };
+
+ sev_control.value.sev_control.enable_encrypted_state =
+ enable_encrypted_state;
+ sev_control.value.sev_control.vmsa_gpa_page_number =
+ vmsa_gpa_page_number;
+
+ return mshv_set_vp_registers(vp->vp_index, vp->vp_partition->pt_id,
+ 1, &sev_control);
+}
+
+static long
+mshv_partition_ioctl_sev_snp_ap_create(struct mshv_partition *partition,
+ void __user *user_args)
+{
+ struct hv_register_assoc internal_activity = {
+ .name = HV_REGISTER_INTERNAL_ACTIVITY_STATE,
+ .value.internal_activity.as_uint64 = 0,
+ };
+ struct mshv_sev_snp_ap_create req;
+ struct mshv_vp *vp;
+ long ret;
+
+ if (copy_from_user(&req, user_args, sizeof(req)))
+ return -EFAULT;
+
+ if (req.vp_id >= MSHV_MAX_VPS)
+ return -EINVAL;
+
+ vp = partition->pt_vp_array[req.vp_id];
+ if (!vp)
+ return -EINVAL;
+
+ ret = set_sev_control_register(vp, 1, HVPFN_DOWN(req.vmsa_gpa));
+ if (ret) {
+ vp_err(vp, "Failed to set SEV control register\n");
+ return ret;
+ }
+
+ ret = mshv_set_vp_registers(vp->vp_index, vp->vp_partition->pt_id, 1,
+ &internal_activity);
+ if (ret)
+ vp_err(vp, "Failed to set internal activity\n");
+
+ return ret;
+}
+
+static long
+mshv_partition_ioctl_modify_gpa_host_access(struct mshv_partition *partition,
+ void __user *user_args)
+{
+ struct mshv_modify_gpa_host_access args;
+ struct page **pages;
+ u64 *gpfns;
+ u64 i;
+ u32 host_access = 0;
+ u32 flags = 0;
+ bool acquire;
+ long ret;
+
+ if (copy_from_user(&args, user_args, sizeof(args)))
+ return -EFAULT;
+
+ if ((args.flags & ~MSHV_GPA_HOST_ACCESS_FLAGS_MASK) ||
+ mshv_field_nonzero(args, rsvd) || !args.page_count)
+ return -EINVAL;
+
+ gpfns = vmemdup_user((char __user *)user_args +
+ offsetof(struct mshv_modify_gpa_host_access,
+ guest_pfns),
+ size_mul(sizeof(*gpfns), args.page_count));
+ if (IS_ERR(gpfns))
+ return PTR_ERR(gpfns);
+
+ pages = kcalloc(args.page_count, sizeof(*pages), GFP_KERNEL);
+ if (!pages) {
+ ret = -ENOMEM;
+ goto free_gpfns;
+ }
+
+ for (i = 0; i < args.page_count; i++)
+ gpfns[i] = HVPFN_DOWN(gpfns[i]);
+
+ ret = mshv_gpfns_to_pages(partition, gpfns, args.page_count, pages);
+ if (ret)
+ goto free_pages;
+
+ if (args.flags & BIT(MSHV_GPA_HOST_ACCESS_BIT_READABLE))
+ host_access |= HV_MAP_GPA_READABLE;
+ if (args.flags & BIT(MSHV_GPA_HOST_ACCESS_BIT_WRITABLE))
+ host_access |= HV_MAP_GPA_WRITABLE;
+ if (args.flags & BIT(MSHV_GPA_HOST_ACCESS_BIT_LARGE_PAGE))
+ flags |= HV_MODIFY_SPA_PAGE_HOST_ACCESS_LARGE_PAGE;
+ acquire = args.flags & BIT(MSHV_GPA_HOST_ACCESS_BIT_ACQUIRE);
+
+ ret = hv_call_modify_spa_host_access(partition->pt_id, pages,
+ args.page_count, host_access,
+ flags, acquire);
+
+free_pages:
+ kfree(pages);
+free_gpfns:
+ kvfree(gpfns);
+ return ret;
+}
+
+static long
+mshv_partition_ioctl_import_isolated_pages(struct mshv_partition *partition,
+ void __user *user_args)
+{
+ struct mshv_import_isolated_pages args;
+ u64 *pages;
+ long ret;
+
+ if (copy_from_user(&args, user_args, sizeof(args)))
+ return -EFAULT;
+
+ if (args.page_type >= MSHV_ISOLATED_PAGE_COUNT ||
+ mshv_field_nonzero(args, rsvd) || !args.page_count)
+ return -EINVAL;
+
+ pages = vmemdup_user((char __user *)user_args +
+ offsetof(struct mshv_import_isolated_pages,
+ guest_pfns),
+ size_mul(sizeof(*pages), args.page_count));
+ if (IS_ERR(pages))
+ return PTR_ERR(pages);
+
+ ret = mshv_init_async_handler(partition);
+ if (ret)
+ goto out;
+
+ ret = hv_call_import_isolated_pages(partition->pt_id, pages,
+ args.page_count, args.page_type,
+ HV_ISOLATED_PAGE_SIZE_4KB,
+ mshv_async_hvcall_handler,
+ partition);
+
+out:
+ kvfree(pages);
+ return ret;
+}
+
+static long
+mshv_partition_ioctl_complete_isolated_import(struct mshv_partition *partition,
+ void __user *user_args)
+{
+ struct mshv_complete_isolated_import *args;
+ long ret;
+
+ args = memdup_user(user_args, sizeof(*args));
+ if (IS_ERR(args))
+ return PTR_ERR(args);
+
+ ret = mshv_init_async_handler(partition);
+ if (ret)
+ goto out;
+
+ ret = hv_call_complete_isolated_import(partition->pt_id,
+ &args->import_data,
+ mshv_async_hvcall_handler,
+ partition);
+ if (!ret)
+ partition->import_completed = true;
+
+out:
+ kfree(args);
+ return ret;
+}
+
+static long
+mshv_partition_ioctl_issue_psp_guest_request(struct mshv_partition *partition,
+ void __user *user_args)
+{
+ struct mshv_issue_psp_guest_request req;
+ u32 host_access = HV_MAP_GPA_READABLE | HV_MAP_GPA_WRITABLE;
+ struct page *pages[2];
+ u64 gpfns[2];
+ long ret;
+
+ if (copy_from_user(&req, user_args, sizeof(req)))
+ return -EFAULT;
+
+ gpfns[0] = HVPFN_DOWN(req.req_gpa);
+ gpfns[1] = HVPFN_DOWN(req.rsp_gpa);
+
+ ret = mshv_gpfns_to_pages(partition, gpfns, ARRAY_SIZE(gpfns), pages);
+ if (ret)
+ return ret;
+
+ ret = hv_call_modify_spa_host_access(partition->pt_id, pages,
+ ARRAY_SIZE(pages), 0, 0, false);
+ if (ret)
+ return ret;
+
+ ret = mshv_init_async_handler(partition);
+ if (ret)
+ goto restore_host_access;
+
+ ret = hv_call_issue_psp_guest_request(partition->pt_id,
+ HVPFN_DOWN(req.req_gpa),
+ HVPFN_DOWN(req.rsp_gpa),
+ mshv_async_hvcall_handler,
+ partition);
+ if (!ret)
+ return 0;
+
+restore_host_access:
+ hv_call_modify_spa_host_access(partition->pt_id, pages,
+ ARRAY_SIZE(pages), host_access, 0, true);
+ return ret;
+}
+
+static long mshv_partition_snp_ioctl(unsigned int ioctl,
+ struct mshv_partition *partition,
+ unsigned long arg)
+{
+ void __user *uarg = (void __user *)arg;
+
+ if (!mshv_partition_encrypted(partition)) {
+ pt_err(partition,
+ "Ioctl(%u) not supported for non SEV-SNP partition\n",
+ ioctl);
+ return -EOPNOTSUPP;
+ }
+
+ switch (ioctl) {
+ case MSHV_MODIFY_GPA_HOST_ACCESS:
+ return mshv_partition_ioctl_modify_gpa_host_access(partition,
+ uarg);
+ case MSHV_IMPORT_ISOLATED_PAGES:
+ return mshv_partition_ioctl_import_isolated_pages(partition,
+ uarg);
+ case MSHV_COMPLETE_ISOLATED_IMPORT:
+ return mshv_partition_ioctl_complete_isolated_import(partition,
+ uarg);
+ case MSHV_ISSUE_PSP_GUEST_REQUEST:
+ return mshv_partition_ioctl_issue_psp_guest_request(partition,
+ uarg);
+ case MSHV_SEV_SNP_AP_CREATE:
+ return mshv_partition_ioctl_sev_snp_ap_create(partition, uarg);
+ default:
+ return -ENOTTY;
+ }
+}
+#endif
+
static long
mshv_partition_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
{
@@ -1661,6 +1956,15 @@ mshv_partition_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
case MSHV_ROOT_HVCALL:
ret = mshv_ioctl_passthru_hvcall(partition, true, uarg);
break;
+#ifdef HV_SUPPORTS_SEV_SNP_GUESTS
+ case MSHV_MODIFY_GPA_HOST_ACCESS:
+ case MSHV_IMPORT_ISOLATED_PAGES:
+ case MSHV_COMPLETE_ISOLATED_IMPORT:
+ case MSHV_ISSUE_PSP_GUEST_REQUEST:
+ case MSHV_SEV_SNP_AP_CREATE:
+ ret = mshv_partition_snp_ioctl(ioctl, partition, arg);
+ break;
+#endif
default:
ret = -ENOTTY;
}
@@ -1772,6 +2076,69 @@ remove_partition(struct mshv_partition *partition)
synchronize_rcu();
}
+#ifdef HV_SUPPORTS_SEV_SNP_GUESTS
+static int destroy_snp_partition_state(struct mshv_partition *partition)
+{
+ struct hv_register_assoc explicit_suspend = {
+ .name = HV_REGISTER_EXPLICIT_SUSPEND,
+ .value.explicit_suspend.suspended = 1,
+ };
+ struct mshv_vp *vp;
+ int i, ret;
+
+ for (i = 0; i < MSHV_MAX_VPS; i++) {
+ vp = partition->pt_vp_array[i];
+ if (!vp)
+ continue;
+
+ ret = mshv_set_vp_registers(vp->vp_index,
+ vp->vp_partition->pt_id, 1,
+ &explicit_suspend);
+ if (ret) {
+ vp_err(vp, "Failed to set explicit suspend\n");
+ return ret;
+ }
+
+ ret = set_sev_control_register(vp, 0, 0);
+ if (ret) {
+ vp_err(vp, "Failed to clear SEV control register\n");
+ return ret;
+ }
+ }
+
+ if (partition->import_completed) {
+ union hv_partition_isolation_control isolation_control = {};
+
+ ret = mshv_init_async_handler(partition);
+ if (ret)
+ return ret;
+
+ ret = hv_call_set_partition_property(partition->pt_id,
+ HV_PARTITION_PROPERTY_ISOLATION_CONTROL,
+ isolation_control.as_uint64,
+ mshv_async_hvcall_handler, partition);
+ if (ret) {
+ pt_err(partition, "Failed to clear runnable bit\n");
+ return ret;
+ }
+ }
+
+ ret = mshv_init_async_handler(partition);
+ if (ret)
+ return ret;
+
+ ret = hv_call_set_partition_property(partition->pt_id,
+ HV_PARTITION_PROPERTY_ISOLATION_STATE,
+ HV_PARTITION_ISOLATION_INSECURE_DIRTY,
+ mshv_async_hvcall_handler, partition);
+ if (ret)
+ pt_err(partition,
+ "Failed to set isolation state to INSECURE_DIRTY\n");
+
+ return ret;
+}
+#endif
+
/*
* Tear down a partition and remove it from the list.
* Partition's refcount must be 0
@@ -1782,6 +2149,9 @@ static void destroy_partition(struct mshv_partition *partition)
struct mshv_mem_region *region;
struct hlist_node *n;
int i;
+#ifdef HV_SUPPORTS_SEV_SNP_GUESTS
+ int ret;
+#endif
if (refcount_read(&partition->pt_ref_count)) {
pt_err(partition,
@@ -1792,6 +2162,24 @@ static void destroy_partition(struct mshv_partition *partition)
trace_mshv_destroy_partition(partition->pt_id);
if (partition->pt_initialized) {
+#ifdef HV_SUPPORTS_SEV_SNP_GUESTS
+ if (mshv_partition_encrypted(partition)) {
+ hlist_for_each_entry_safe(region, n,
+ &partition->pt_mem_regions,
+ hnode) {
+ hlist_del(®ion->hnode);
+ mshv_region_put(region);
+ }
+
+ ret = destroy_snp_partition_state(partition);
+ if (ret) {
+ pt_err(partition,
+ "Failed to destroy SNP state: %d\n",
+ ret);
+ return;
+ }
+ }
+#endif
/*
* We only need to drain signals for root scheduler. This should be
* done before removing the partition from the partition list.
@@ -2016,6 +2404,9 @@ static long mshv_ioctl_process_pt_flags(void __user *user_arg, u64 *pt_flags,
case MSHV_PT_ISOLATION_NONE:
isol_props->isolation_type = HV_PARTITION_ISOLATION_TYPE_NONE;
break;
+ case MSHV_PT_ISOLATION_SNP:
+ isol_props->isolation_type = HV_PARTITION_ISOLATION_TYPE_SNP;
+ break;
}
return 0;
diff --git a/include/hyperv/hvgdk_mini.h b/include/hyperv/hvgdk_mini.h
index a53c0f59994a..5553d5c918a7 100644
--- a/include/hyperv/hvgdk_mini.h
+++ b/include/hyperv/hvgdk_mini.h
@@ -975,6 +975,7 @@ enum hv_register_name {
HV_REGISTER_EXPLICIT_SUSPEND = 0x00000000,
HV_REGISTER_INTERCEPT_SUSPEND = 0x00000001,
HV_REGISTER_DISPATCH_SUSPEND = 0x00000003,
+ HV_REGISTER_INTERNAL_ACTIVITY_STATE = 0x00000004,
/* Version - 128-bit result same as CPUID 0x40000002 */
HV_REGISTER_HYPERVISOR_VERSION = 0x00000100,
@@ -1250,6 +1251,16 @@ union hv_arm64_pending_synthetic_exception_event {
} __packed;
};
+union hv_internal_activity_register {
+ u64 as_uint64;
+ struct {
+ u64 startup_suspend : 1;
+ u64 halt_suspend : 1;
+ u64 idle_suspend : 1;
+ u64 rsvd_z : 61;
+ } __packed;
+};
+
union hv_x64_interrupt_state_register {
u64 as_uint64;
struct {
@@ -1297,6 +1308,7 @@ union hv_register_value {
union hv_explicit_suspend_register explicit_suspend;
union hv_intercept_suspend_register intercept_suspend;
union hv_dispatch_suspend_register dispatch_suspend;
+ union hv_internal_activity_register internal_activity;
#ifdef CONFIG_ARM64
union hv_arm64_interrupt_state_register interrupt_state;
union hv_arm64_pending_interruption_register pending_interruption;
--
2.43.0
next prev parent reply other threads:[~2026-08-07 13:52 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-07 13:51 [PATCH v1 00/13] mshv: add SEV-SNP support for MSHV root partitions Wei Hu
2026-08-07 13:51 ` [PATCH v1 01/13] mshv: add SEV-SNP UAPI definitions Wei Hu
2026-08-07 13:51 ` [PATCH v1 02/13] mshv: add SEV-SNP PSP request hypercall Wei Hu
2026-08-07 13:51 ` [PATCH v1 03/13] mshv: add SEV-SNP isolated page hypercalls Wei Hu
2026-08-07 13:51 ` Wei Hu [this message]
2026-08-07 13:51 ` [PATCH v1 05/13] hyperv: fix hv_input_get_system_property layout for SNP status Wei Hu
2026-08-07 13:51 ` [PATCH v1 06/13] mshv: detect and report SEV-SNP support at init Wei Hu
2026-08-07 13:51 ` [PATCH v1 07/13] mshv: default to safe partition CPU features Wei Hu
2026-08-07 13:51 ` [PATCH v1 08/13] mshv: accept partial CPU feature banks Wei Hu
2026-08-07 13:51 ` [PATCH v1 09/13] mshv: define full processor and xsave feature masks Wei Hu
2026-08-07 13:51 ` [PATCH v1 10/13] mshv: unmap SNP memory before state teardown Wei Hu
2026-08-07 13:51 ` [PATCH v1 11/13] mshv: unlock SNP pages on panic for crashdump collection Wei Hu
2026-08-07 13:51 ` [PATCH v1 12/13] hyperv: add MSHV Dom0 root-partition boot enablement (EFI HvLoader) Wei Hu
2026-08-07 13:51 ` [PATCH v1 13/13] mshv: set up own SynIC registers on a nested root partition Wei Hu
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=20260807135134.303943-5-weh@linux.microsoft.com \
--to=weh@linux.microsoft.com \
--cc=decui@microsoft.com \
--cc=haiyangz@microsoft.com \
--cc=kys@microsoft.com \
--cc=linux-hyperv@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=longli@microsoft.com \
--cc=wei.liu@kernel.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox