From 7b89a44b2e8c7ba548e3ad5d5155a17279625335 Mon Sep 17 00:00:00 2001 From: Michael Kelley Date: Fri, 13 Jun 2025 16:00:59 -0700 Subject: [PATCH 01/15] Drivers: hv: Select CONFIG_SYSFB only if EFI is enabled Commit 96959283a58d ("Drivers: hv: Always select CONFIG_SYSFB for Hyper-V guests") selects CONFIG_SYSFB for Hyper-V guests so that screen_info is available to the VMBus driver to get the location of the framebuffer in Generation 2 VMs. However, if CONFIG_HYPERV is enabled but CONFIG_EFI is not, a kernel link error results in ARM64 builds because screen_info is provided by the EFI firmware interface. While configuring an ARM64 Hyper-V guest without EFI isn't useful since EFI is required to boot, the configuration is still possible and the link error should be prevented. Fix this by making the selection of CONFIG_SYSFB conditional on CONFIG_EFI being defined. For Generation 1 VMs on x86/x64, which don't use EFI, the additional condition is OK because such VMs get the framebuffer information via a mechanism that doesn't use screen_info. Fixes: 96959283a58d ("Drivers: hv: Always select CONFIG_SYSFB for Hyper-V guests") Reported-by: Arnd Bergmann Closes: https://lore.kernel.org/linux-hyperv/20250610091810.2638058-1-arnd@kernel.org/ Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202506080820.1wmkQufc-lkp@intel.com/ Signed-off-by: Michael Kelley Link: https://lore.kernel.org/stable/20250613230059.380483-1-mhklinux%40outlook.com Reviewed-by: Roman Kisel Link: https://lore.kernel.org/r/20250613230059.380483-1-mhklinux@outlook.com Signed-off-by: Wei Liu Message-ID: <20250613230059.380483-1-mhklinux@outlook.com> --- drivers/hv/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hv/Kconfig b/drivers/hv/Kconfig index 1cd188b73b74..57623ca7f350 100644 --- a/drivers/hv/Kconfig +++ b/drivers/hv/Kconfig @@ -9,7 +9,7 @@ config HYPERV select PARAVIRT select X86_HV_CALLBACK_VECTOR if X86 select OF_EARLY_FLATTREE if OF - select SYSFB if !HYPERV_VTL_MODE + select SYSFB if EFI && !HYPERV_VTL_MODE help Select this option to run Linux as a Hyper-V client operating system. From 0d86a8d65c1e69610bfe1a7a774f71ff111ed8c1 Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Sat, 28 Jun 2025 11:22:17 +0900 Subject: [PATCH 02/15] tools/hv: fcopy: Fix incorrect file path conversion The hv_fcopy_uio_daemon fails to correctly handle file copy requests from Windows hosts (e.g. via Copy-VMFile) due to wchar_t size differences between Windows and Linux. On Linux, wchar_t is 32 bit, whereas Windows uses 16 bit wide characters. Fix this by ensuring that file transfers from host to Linux guest succeed with correctly decoded file names and paths. - Treats file name and path as __u16 arrays, not wchar_t*. - Allocates fixed-size buffers (W_MAX_PATH) for converted strings instead of using malloc. - Adds a check for target path length to prevent snprintf() buffer overflow. Fixes: 82b0945ce2c2 ("tools: hv: Add new fcopy application based on uio driver") Signed-off-by: Yasumasa Suenaga Reviewed-by: Naman Jain Link: https://lore.kernel.org/r/20250628022217.1514-2-yasuenag@gmail.com Signed-off-by: Wei Liu Message-ID: <20250628022217.1514-2-yasuenag@gmail.com> --- tools/hv/hv_fcopy_uio_daemon.c | 37 +++++++++++++--------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/tools/hv/hv_fcopy_uio_daemon.c b/tools/hv/hv_fcopy_uio_daemon.c index 0198321d14a2..4b09ed6b637a 100644 --- a/tools/hv/hv_fcopy_uio_daemon.c +++ b/tools/hv/hv_fcopy_uio_daemon.c @@ -62,8 +62,11 @@ static int hv_fcopy_create_file(char *file_name, char *path_name, __u32 flags) filesize = 0; p = path_name; - snprintf(target_fname, sizeof(target_fname), "%s/%s", - path_name, file_name); + if (snprintf(target_fname, sizeof(target_fname), "%s/%s", + path_name, file_name) >= sizeof(target_fname)) { + syslog(LOG_ERR, "target file name is too long: %s/%s", path_name, file_name); + goto done; + } /* * Check to see if the path is already in place; if not, @@ -270,7 +273,7 @@ static void wcstoutf8(char *dest, const __u16 *src, size_t dest_size) { size_t len = 0; - while (len < dest_size) { + while (len < dest_size && *src) { if (src[len] < 0x80) dest[len++] = (char)(*src++); else @@ -282,27 +285,15 @@ static void wcstoutf8(char *dest, const __u16 *src, size_t dest_size) static int hv_fcopy_start(struct hv_start_fcopy *smsg_in) { + /* + * file_name and path_name should have same length with appropriate + * member of hv_start_fcopy. + */ + char file_name[W_MAX_PATH], path_name[W_MAX_PATH]; + setlocale(LC_ALL, "en_US.utf8"); - size_t file_size, path_size; - char *file_name, *path_name; - char *in_file_name = (char *)smsg_in->file_name; - char *in_path_name = (char *)smsg_in->path_name; - - file_size = wcstombs(NULL, (const wchar_t *restrict)in_file_name, 0) + 1; - path_size = wcstombs(NULL, (const wchar_t *restrict)in_path_name, 0) + 1; - - file_name = (char *)malloc(file_size * sizeof(char)); - path_name = (char *)malloc(path_size * sizeof(char)); - - if (!file_name || !path_name) { - free(file_name); - free(path_name); - syslog(LOG_ERR, "Can't allocate memory for file name and/or path name"); - return HV_E_FAIL; - } - - wcstoutf8(file_name, (__u16 *)in_file_name, file_size); - wcstoutf8(path_name, (__u16 *)in_path_name, path_size); + wcstoutf8(file_name, smsg_in->file_name, W_MAX_PATH - 1); + wcstoutf8(path_name, smsg_in->path_name, W_MAX_PATH - 1); return hv_fcopy_create_file(file_name, path_name, smsg_in->copy_flags); } From b0871aa0f8df55dbf0aad55d2ab2100c23071b4b Mon Sep 17 00:00:00 2001 From: Naman Jain Date: Mon, 7 Jul 2025 14:13:22 +0530 Subject: [PATCH 03/15] Drivers: hv: Fix the check for HYPERVISOR_CALLBACK_VECTOR __is_defined(HYPERVISOR_CALLBACK_VECTOR) would return 1, only if HYPERVISOR_CALLBACK_VECTOR macro is defined as 1. However its value is 0xf3 and this leads to __is_defined() returning 0. The expectation was to just check whether this MACRO is defined or not and get 1 if it's defined. Replace __is_defined with #ifdef blocks instead to fix it. Fixes: 1dc5df133b98 ("Drivers: hv: vmbus: Get the IRQ number from DeviceTree") Cc: stable@kernel.org Signed-off-by: Naman Jain Reviewed-by: Roman Kisel Link: https://lore.kernel.org/r/20250707084322.1763-1-namjain@linux.microsoft.com Signed-off-by: Wei Liu Message-ID: <20250707084322.1763-1-namjain@linux.microsoft.com> --- drivers/hv/vmbus_drv.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 33b524b4eb5e..2ed5a1e89d69 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -2509,7 +2509,7 @@ static int vmbus_acpi_add(struct platform_device *pdev) return 0; } #endif - +#ifndef HYPERVISOR_CALLBACK_VECTOR static int vmbus_set_irq(struct platform_device *pdev) { struct irq_data *data; @@ -2534,6 +2534,7 @@ static int vmbus_set_irq(struct platform_device *pdev) return 0; } +#endif static int vmbus_device_add(struct platform_device *pdev) { @@ -2549,11 +2550,11 @@ static int vmbus_device_add(struct platform_device *pdev) if (ret) return ret; - if (!__is_defined(HYPERVISOR_CALLBACK_VECTOR)) - ret = vmbus_set_irq(pdev); +#ifndef HYPERVISOR_CALLBACK_VECTOR + ret = vmbus_set_irq(pdev); if (ret) return ret; - +#endif for_each_of_range(&parser, &range) { struct resource *res; From 2b206d3468236047d71d4ab6110b1f5b70448e0e Mon Sep 17 00:00:00 2001 From: Naman Jain Date: Wed, 11 Jun 2025 15:34:54 +0530 Subject: [PATCH 04/15] Drivers: hv: Fix warnings for missing export.h header inclusion Fix below warning in Hyper-V drivers that comes when kernel is compiled with W=1 option. Include export.h in driver files to fix it. * warning: EXPORT_SYMBOL() is used, but #include is missing Signed-off-by: Naman Jain Reviewed-by: Nuno Das Neves Reviewed-by: Saurabh Sengar Link: https://lore.kernel.org/r/20250611100459.92900-2-namjain@linux.microsoft.com Signed-off-by: Wei Liu Message-ID: <20250611100459.92900-2-namjain@linux.microsoft.com> --- drivers/hv/channel.c | 1 + drivers/hv/channel_mgmt.c | 1 + drivers/hv/hv_proc.c | 1 + drivers/hv/mshv_common.c | 1 + drivers/hv/mshv_root_hv_call.c | 1 + drivers/hv/ring_buffer.c | 1 + 6 files changed, 6 insertions(+) diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 35f26fa1ffe7..7c7c66e0dc3f 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 6e084c207414..65dd299e2944 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include diff --git a/drivers/hv/hv_proc.c b/drivers/hv/hv_proc.c index 7d7ecb6f6137..fbb4eb3901bb 100644 --- a/drivers/hv/hv_proc.c +++ b/drivers/hv/hv_proc.c @@ -6,6 +6,7 @@ #include #include #include +#include #include /* diff --git a/drivers/hv/mshv_common.c b/drivers/hv/mshv_common.c index 2575e6d7a71f..6f227a8a5af7 100644 --- a/drivers/hv/mshv_common.c +++ b/drivers/hv/mshv_common.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "mshv.h" diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c index a222a16107f6..c9c274f29c3c 100644 --- a/drivers/hv/mshv_root_hv_call.c +++ b/drivers/hv/mshv_root_hv_call.c @@ -9,6 +9,7 @@ #include #include +#include #include #include "mshv_root.h" diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index 3c9b02471760..23ce1fb70de1 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "hyperv_vmbus.h" From 0271e72bc0f7cf180dce3f6d145c83f2d5709a25 Mon Sep 17 00:00:00 2001 From: Naman Jain Date: Wed, 11 Jun 2025 15:34:55 +0530 Subject: [PATCH 05/15] x86/hyperv: Fix warnings for missing export.h header inclusion Fix below warning in Hyper-V drivers that comes when kernel is compiled with W=1 option. Include export.h in driver files to fix it. * warning: EXPORT_SYMBOL() is used, but #include is missing Signed-off-by: Naman Jain Reviewed-by: Saurabh Sengar Link: https://lore.kernel.org/r/20250611100459.92900-3-namjain@linux.microsoft.com Signed-off-by: Wei Liu Message-ID: <20250611100459.92900-3-namjain@linux.microsoft.com> --- arch/x86/hyperv/hv_init.c | 1 + arch/x86/hyperv/irqdomain.c | 1 + arch/x86/hyperv/ivm.c | 1 + arch/x86/hyperv/nested.c | 1 + 4 files changed, 4 insertions(+) diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c index 3d1d3547095a..afdbda2dd7b7 100644 --- a/arch/x86/hyperv/hv_init.c +++ b/arch/x86/hyperv/hv_init.c @@ -34,6 +34,7 @@ #include #include #include +#include void *hv_hypercall_pg; EXPORT_SYMBOL_GPL(hv_hypercall_pg); diff --git a/arch/x86/hyperv/irqdomain.c b/arch/x86/hyperv/irqdomain.c index 31f0d29cbc5e..f7627bc8fe49 100644 --- a/arch/x86/hyperv/irqdomain.c +++ b/arch/x86/hyperv/irqdomain.c @@ -10,6 +10,7 @@ #include #include +#include #include static int hv_map_interrupt(union hv_device_id device_id, bool level, diff --git a/arch/x86/hyperv/ivm.c b/arch/x86/hyperv/ivm.c index e93a2f488ff7..ade6c665c97e 100644 --- a/arch/x86/hyperv/ivm.c +++ b/arch/x86/hyperv/ivm.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/x86/hyperv/nested.c b/arch/x86/hyperv/nested.c index 1083dc8646f9..8ccbb7c4fc27 100644 --- a/arch/x86/hyperv/nested.c +++ b/arch/x86/hyperv/nested.c @@ -11,6 +11,7 @@ #include +#include #include #include #include From 5b187e9a31547d63a1a4078b79f7fb3fdbca92cd Mon Sep 17 00:00:00 2001 From: Naman Jain Date: Wed, 11 Jun 2025 15:34:57 +0530 Subject: [PATCH 06/15] clocksource: hyper-v: Fix warnings for missing export.h header inclusion Fix below warning in Hyper-V clocksource driver that comes when kernel is compiled with W=1 option. Include export.h in driver files to fix it. * warning: EXPORT_SYMBOL() is used, but #include is missing Signed-off-by: Naman Jain Reviewed-by: Saurabh Sengar Link: https://lore.kernel.org/r/20250611100459.92900-5-namjain@linux.microsoft.com Signed-off-by: Wei Liu Message-ID: <20250611100459.92900-5-namjain@linux.microsoft.com> --- drivers/clocksource/hyperv_timer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clocksource/hyperv_timer.c b/drivers/clocksource/hyperv_timer.c index 09549451dd51..2edc13ca184e 100644 --- a/drivers/clocksource/hyperv_timer.c +++ b/drivers/clocksource/hyperv_timer.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include From 4a4f15170b63bd3b8a6534b39330e981704c7369 Mon Sep 17 00:00:00 2001 From: Naman Jain Date: Wed, 11 Jun 2025 15:34:58 +0530 Subject: [PATCH 07/15] PCI: hv: Fix warnings for missing export.h header inclusion Fix below warning in Hyper-V PCI driver that comes when kernel is compiled with W=1 option. Include export.h in driver files to fix it. * warning: EXPORT_SYMBOL() is used, but #include is missing Signed-off-by: Naman Jain Reviewed-by: Saurabh Sengar Link: https://lore.kernel.org/r/20250611100459.92900-6-namjain@linux.microsoft.com Signed-off-by: Wei Liu Message-ID: <20250611100459.92900-6-namjain@linux.microsoft.com> --- drivers/pci/controller/pci-hyperv-intf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pci/controller/pci-hyperv-intf.c b/drivers/pci/controller/pci-hyperv-intf.c index cc96be450360..28b3e93d31c0 100644 --- a/drivers/pci/controller/pci-hyperv-intf.c +++ b/drivers/pci/controller/pci-hyperv-intf.c @@ -14,6 +14,7 @@ #include #include #include +#include struct hyperv_pci_block_ops hvpci_block_ops; EXPORT_SYMBOL_GPL(hvpci_block_ops); From 9669ddda18fbe7f1e28cd0bfc1218e746fae6c50 Mon Sep 17 00:00:00 2001 From: Naman Jain Date: Wed, 11 Jun 2025 15:34:59 +0530 Subject: [PATCH 08/15] net: mana: Fix warnings for missing export.h header inclusion Fix below warning in Hyper-V's MANA drivers that comes when kernel is compiled with W=1 option. Include export.h in driver files to fix it. * warning: EXPORT_SYMBOL() is used, but #include is missing Signed-off-by: Naman Jain Reviewed-by: Saurabh Sengar Link: https://lore.kernel.org/r/20250611100459.92900-7-namjain@linux.microsoft.com Signed-off-by: Wei Liu Message-ID: <20250611100459.92900-7-namjain@linux.microsoft.com> --- drivers/net/ethernet/microsoft/mana/gdma_main.c | 1 + drivers/net/ethernet/microsoft/mana/mana_en.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c index 52cf7112762c..58f8ee710912 100644 --- a/drivers/net/ethernet/microsoft/mana/gdma_main.c +++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c @@ -6,6 +6,7 @@ #include #include #include +#include #include diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index ccd2885c939e..faad1cb880f8 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include From f84b21da3624d9c8514db409d254a22b84fac66a Mon Sep 17 00:00:00 2001 From: Mukesh Rathor Date: Thu, 3 Jul 2025 15:44:32 -0700 Subject: [PATCH 09/15] PCI: hv: Don't load the driver for baremetal root partition The root partition only uses VMBus when running nested. When running on baremetal the Hyper-V PCI driver is not needed, so do not initialize it. Signed-off-by: Mukesh Rathor Signed-off-by: Nuno Das Neves Reviewed-by: Roman Kisel Reviewed-by: Michael Kelley Acked-by: Bjorn Helgaas Link: https://lore.kernel.org/r/1751582677-30930-2-git-send-email-nunodasneves@linux.microsoft.com Signed-off-by: Wei Liu Message-ID: <1751582677-30930-2-git-send-email-nunodasneves@linux.microsoft.com> --- drivers/pci/controller/pci-hyperv.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c index ef5d655a0052..275b23af3de2 100644 --- a/drivers/pci/controller/pci-hyperv.c +++ b/drivers/pci/controller/pci-hyperv.c @@ -4144,6 +4144,9 @@ static int __init init_hv_pci_drv(void) if (!hv_is_hyperv_initialized()) return -ENODEV; + if (hv_root_partition() && !hv_nested) + return -ENODEV; + ret = hv_pci_irqchip_init(); if (ret) return ret; From bb169f80ed5a156ec3405e0e49c6b8e9ae264718 Mon Sep 17 00:00:00 2001 From: Nuno Das Neves Date: Thu, 3 Jul 2025 15:44:34 -0700 Subject: [PATCH 10/15] x86/hyperv: Fix usage of cpu_online_mask to get valid cpu Accessing cpu_online_mask here is problematic because the cpus read lock is not held in this context. However, cpu_online_mask isn't needed here since the effective affinity mask is guaranteed to be valid in this callback. So, just use cpumask_first() to get the cpu instead of ANDing it with cpus_online_mask unnecessarily. Fixes: e39397d1fd68 ("x86/hyperv: implement an MSI domain for root partition") Reported-by: Michael Kelley Closes: https://lore.kernel.org/linux-hyperv/SN6PR02MB4157639630F8AD2D8FD8F52FD475A@SN6PR02MB4157.namprd02.prod.outlook.com/ Suggested-by: Thomas Gleixner Signed-off-by: Nuno Das Neves Reviewed-by: Michael Kelley Link: https://lore.kernel.org/r/1751582677-30930-4-git-send-email-nunodasneves@linux.microsoft.com Signed-off-by: Wei Liu Message-ID: <1751582677-30930-4-git-send-email-nunodasneves@linux.microsoft.com> --- arch/x86/hyperv/irqdomain.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/x86/hyperv/irqdomain.c b/arch/x86/hyperv/irqdomain.c index f7627bc8fe49..de883ac76445 100644 --- a/arch/x86/hyperv/irqdomain.c +++ b/arch/x86/hyperv/irqdomain.c @@ -193,7 +193,6 @@ static void hv_irq_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) struct pci_dev *dev; struct hv_interrupt_entry out_entry, *stored_entry; struct irq_cfg *cfg = irqd_cfg(data); - const cpumask_t *affinity; int cpu; u64 status; @@ -205,8 +204,7 @@ static void hv_irq_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) return; } - affinity = irq_data_get_effective_affinity_mask(data); - cpu = cpumask_first_and(affinity, cpu_online_mask); + cpu = cpumask_first(irq_data_get_effective_affinity_mask(data)); if (data->chip_data) { /* From faab52b59b09721edeb8f92eabad3f4d320fb522 Mon Sep 17 00:00:00 2001 From: Nuno Das Neves Date: Thu, 3 Jul 2025 15:44:35 -0700 Subject: [PATCH 11/15] x86/hyperv: Clean up hv_map/unmap_interrupt() return values Fix the return values of these hypercall helpers so they return a negated errno either directly or via hv_result_to_errno(). Update the callers to check for errno instead of using hv_status_success(), and remove redundant error printing. While at it, rearrange some variable declarations to adhere to style guidelines i.e. "reverse fir tree order". Signed-off-by: Nuno Das Neves Reviewed-by: Michael Kelley Link: https://lore.kernel.org/r/1751582677-30930-5-git-send-email-nunodasneves@linux.microsoft.com Signed-off-by: Wei Liu Message-ID: <1751582677-30930-5-git-send-email-nunodasneves@linux.microsoft.com> --- arch/x86/hyperv/irqdomain.c | 32 ++++++++++++++------------------ drivers/iommu/hyperv-iommu.c | 33 ++++++++++++--------------------- 2 files changed, 26 insertions(+), 39 deletions(-) diff --git a/arch/x86/hyperv/irqdomain.c b/arch/x86/hyperv/irqdomain.c index de883ac76445..ad4dff48cf14 100644 --- a/arch/x86/hyperv/irqdomain.c +++ b/arch/x86/hyperv/irqdomain.c @@ -47,7 +47,7 @@ static int hv_map_interrupt(union hv_device_id device_id, bool level, if (nr_bank < 0) { local_irq_restore(flags); pr_err("%s: unable to generate VP set\n", __func__); - return EINVAL; + return -EINVAL; } intr_desc->target.flags = HV_DEVICE_INTERRUPT_TARGET_PROCESSOR_SET; @@ -67,7 +67,7 @@ static int hv_map_interrupt(union hv_device_id device_id, bool level, if (!hv_result_success(status)) hv_status_err(status, "\n"); - return hv_result(status); + return hv_result_to_errno(status); } static int hv_unmap_interrupt(u64 id, struct hv_interrupt_entry *old_entry) @@ -89,7 +89,10 @@ static int hv_unmap_interrupt(u64 id, struct hv_interrupt_entry *old_entry) status = hv_do_hypercall(HVCALL_UNMAP_DEVICE_INTERRUPT, input, NULL); local_irq_restore(flags); - return hv_result(status); + if (!hv_result_success(status)) + hv_status_err(status, "\n"); + + return hv_result_to_errno(status); } #ifdef CONFIG_PCI_MSI @@ -189,12 +192,11 @@ static inline void entry_to_msi_msg(struct hv_interrupt_entry *entry, struct msi static int hv_unmap_msi_interrupt(struct pci_dev *dev, struct hv_interrupt_entry *old_entry); static void hv_irq_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) { - struct msi_desc *msidesc; - struct pci_dev *dev; struct hv_interrupt_entry out_entry, *stored_entry; struct irq_cfg *cfg = irqd_cfg(data); - int cpu; - u64 status; + struct msi_desc *msidesc; + struct pci_dev *dev; + int cpu, ret; msidesc = irq_data_get_msi_desc(data); dev = msi_desc_to_pci_dev(msidesc); @@ -218,14 +220,12 @@ static void hv_irq_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) stored_entry = data->chip_data; data->chip_data = NULL; - status = hv_unmap_msi_interrupt(dev, stored_entry); + ret = hv_unmap_msi_interrupt(dev, stored_entry); kfree(stored_entry); - if (status != HV_STATUS_SUCCESS) { - hv_status_debug(status, "failed to unmap\n"); + if (ret) return; - } } stored_entry = kzalloc(sizeof(*stored_entry), GFP_ATOMIC); @@ -234,8 +234,8 @@ static void hv_irq_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) return; } - status = hv_map_msi_interrupt(dev, cpu, cfg->vector, &out_entry); - if (status != HV_STATUS_SUCCESS) { + ret = hv_map_msi_interrupt(dev, cpu, cfg->vector, &out_entry); + if (ret) { kfree(stored_entry); return; } @@ -256,7 +256,6 @@ static void hv_teardown_msi_irq(struct pci_dev *dev, struct irq_data *irqd) { struct hv_interrupt_entry old_entry; struct msi_msg msg; - u64 status; if (!irqd->chip_data) { pr_debug("%s: no chip data\n!", __func__); @@ -269,10 +268,7 @@ static void hv_teardown_msi_irq(struct pci_dev *dev, struct irq_data *irqd) kfree(irqd->chip_data); irqd->chip_data = NULL; - status = hv_unmap_msi_interrupt(dev, &old_entry); - - if (status != HV_STATUS_SUCCESS) - hv_status_err(status, "\n"); + (void)hv_unmap_msi_interrupt(dev, &old_entry); } static void hv_msi_free_irq(struct irq_domain *domain, diff --git a/drivers/iommu/hyperv-iommu.c b/drivers/iommu/hyperv-iommu.c index 761ab647f372..0961ac805944 100644 --- a/drivers/iommu/hyperv-iommu.c +++ b/drivers/iommu/hyperv-iommu.c @@ -193,15 +193,13 @@ struct hyperv_root_ir_data { static void hyperv_root_ir_compose_msi_msg(struct irq_data *irq_data, struct msi_msg *msg) { - u64 status; - u32 vector; - struct irq_cfg *cfg; - int ioapic_id; - const struct cpumask *affinity; - int cpu; - struct hv_interrupt_entry entry; struct hyperv_root_ir_data *data = irq_data->chip_data; + struct hv_interrupt_entry entry; + const struct cpumask *affinity; struct IO_APIC_route_entry e; + struct irq_cfg *cfg; + int cpu, ioapic_id; + u32 vector; cfg = irqd_cfg(irq_data); affinity = irq_data_get_effective_affinity_mask(irq_data); @@ -214,23 +212,16 @@ hyperv_root_ir_compose_msi_msg(struct irq_data *irq_data, struct msi_msg *msg) && data->entry.ioapic_rte.as_uint64) { entry = data->entry; - status = hv_unmap_ioapic_interrupt(ioapic_id, &entry); - - if (status != HV_STATUS_SUCCESS) - hv_status_debug(status, "failed to unmap\n"); + (void)hv_unmap_ioapic_interrupt(ioapic_id, &entry); data->entry.ioapic_rte.as_uint64 = 0; data->entry.source = 0; /* Invalid source */ } - status = hv_map_ioapic_interrupt(ioapic_id, data->is_level, cpu, - vector, &entry); - - if (status != HV_STATUS_SUCCESS) { - hv_status_err(status, "map failed\n"); + if (hv_map_ioapic_interrupt(ioapic_id, data->is_level, cpu, + vector, &entry)) return; - } data->entry = entry; @@ -322,10 +313,10 @@ static void hyperv_root_irq_remapping_free(struct irq_domain *domain, data = irq_data->chip_data; e = &data->entry; - if (e->source == HV_DEVICE_TYPE_IOAPIC - && e->ioapic_rte.as_uint64) - hv_unmap_ioapic_interrupt(data->ioapic_id, - &data->entry); + if (e->source == HV_DEVICE_TYPE_IOAPIC && + e->ioapic_rte.as_uint64) + (void)hv_unmap_ioapic_interrupt(data->ioapic_id, + &data->entry); kfree(data); } From 36c46e64c55061623daf214d26e634ae2cfe4a49 Mon Sep 17 00:00:00 2001 From: Nuno Das Neves Date: Fri, 11 Jul 2025 12:18:50 -0700 Subject: [PATCH 12/15] Drivers: hv: Use nested hypercall for post message and signal event When running nested, these hypercalls must be sent to the L0 hypervisor or VMBus will fail. Remove hv_do_nested_hypercall() and hv_do_fast_nested_hypercall8() altogether and open-code these cases, since there are only 2 and all they do is add the nested bit. Signed-off-by: Nuno Das Neves Reviewed-by: Roman Kisel Reviewed-by: Michael Kelley Link: https://lore.kernel.org/r/1752261532-7225-2-git-send-email-nunodasneves@linux.microsoft.com Signed-off-by: Wei Liu Message-ID: <1752261532-7225-2-git-send-email-nunodasneves@linux.microsoft.com> --- arch/x86/include/asm/mshyperv.h | 20 -------------------- drivers/hv/connection.c | 5 ++++- drivers/hv/hv.c | 6 ++++-- 3 files changed, 8 insertions(+), 23 deletions(-) diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h index e1752ba47e67..ab097a3a8b75 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -112,12 +112,6 @@ static inline u64 hv_do_hypercall(u64 control, void *input, void *output) return hv_status; } -/* Hypercall to the L0 hypervisor */ -static inline u64 hv_do_nested_hypercall(u64 control, void *input, void *output) -{ - return hv_do_hypercall(control | HV_HYPERCALL_NESTED, input, output); -} - /* Fast hypercall with 8 bytes of input and no output */ static inline u64 _hv_do_fast_hypercall8(u64 control, u64 input1) { @@ -165,13 +159,6 @@ static inline u64 hv_do_fast_hypercall8(u16 code, u64 input1) return _hv_do_fast_hypercall8(control, input1); } -static inline u64 hv_do_fast_nested_hypercall8(u16 code, u64 input1) -{ - u64 control = (u64)code | HV_HYPERCALL_FAST_BIT | HV_HYPERCALL_NESTED; - - return _hv_do_fast_hypercall8(control, input1); -} - /* Fast hypercall with 16 bytes of input */ static inline u64 _hv_do_fast_hypercall16(u64 control, u64 input1, u64 input2) { @@ -223,13 +210,6 @@ static inline u64 hv_do_fast_hypercall16(u16 code, u64 input1, u64 input2) return _hv_do_fast_hypercall16(control, input1, input2); } -static inline u64 hv_do_fast_nested_hypercall16(u16 code, u64 input1, u64 input2) -{ - u64 control = (u64)code | HV_HYPERCALL_FAST_BIT | HV_HYPERCALL_NESTED; - - return _hv_do_fast_hypercall16(control, input1, input2); -} - extern struct hv_vp_assist_page **hv_vp_assist_page; static inline struct hv_vp_assist_page *hv_get_vp_assist_page(unsigned int cpu) diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index be490c598785..1fe3573ae52a 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -519,7 +519,10 @@ void vmbus_set_event(struct vmbus_channel *channel) else WARN_ON_ONCE(1); } else { - hv_do_fast_hypercall8(HVCALL_SIGNAL_EVENT, channel->sig_event); + u64 control = HVCALL_SIGNAL_EVENT; + + control |= hv_nested ? HV_HYPERCALL_NESTED : 0; + hv_do_fast_hypercall8(control, channel->sig_event); } } EXPORT_SYMBOL_GPL(vmbus_set_event); diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 308c8f279df8..b14c5f9e0ef2 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -85,8 +85,10 @@ int hv_post_message(union hv_connection_id connection_id, else status = HV_STATUS_INVALID_PARAMETER; } else { - status = hv_do_hypercall(HVCALL_POST_MESSAGE, - aligned_msg, NULL); + u64 control = HVCALL_POST_MESSAGE; + + control |= hv_nested ? HV_HYPERCALL_NESTED : 0; + status = hv_do_hypercall(control, aligned_msg, NULL); } local_irq_restore(flags); From 52a45f870e271d6d55ef26c17e8c6c82a22312a7 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Fri, 11 Jul 2025 12:18:51 -0700 Subject: [PATCH 13/15] x86/hyperv: Expose hv_map_msi_interrupt() Move some of the logic of hv_irq_compose_irq_message() into hv_map_msi_interrupt(). Make hv_map_msi_interrupt() a globally-available helper function, which will be used to map PCI interrupts when running in the root partition. Signed-off-by: Stanislav Kinsburskii Signed-off-by: Nuno Das Neves Reviewed-by: Roman Kisel Reviewed-by: Michael Kelley Link: https://lore.kernel.org/r/1752261532-7225-3-git-send-email-nunodasneves@linux.microsoft.com Signed-off-by: Wei Liu Message-ID: <1752261532-7225-3-git-send-email-nunodasneves@linux.microsoft.com> --- arch/x86/hyperv/irqdomain.c | 40 ++++++++++++++++++++++++--------- arch/x86/include/asm/mshyperv.h | 2 ++ 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/arch/x86/hyperv/irqdomain.c b/arch/x86/hyperv/irqdomain.c index ad4dff48cf14..090f5ac9f492 100644 --- a/arch/x86/hyperv/irqdomain.c +++ b/arch/x86/hyperv/irqdomain.c @@ -173,13 +173,34 @@ static union hv_device_id hv_build_pci_dev_id(struct pci_dev *dev) return dev_id; } -static int hv_map_msi_interrupt(struct pci_dev *dev, int cpu, int vector, - struct hv_interrupt_entry *entry) +/** + * hv_map_msi_interrupt() - "Map" the MSI IRQ in the hypervisor. + * @data: Describes the IRQ + * @out_entry: Hypervisor (MSI) interrupt entry (can be NULL) + * + * Map the IRQ in the hypervisor by issuing a MAP_DEVICE_INTERRUPT hypercall. + * + * Return: 0 on success, -errno on failure + */ +int hv_map_msi_interrupt(struct irq_data *data, + struct hv_interrupt_entry *out_entry) { - union hv_device_id device_id = hv_build_pci_dev_id(dev); + struct irq_cfg *cfg = irqd_cfg(data); + struct hv_interrupt_entry dummy; + union hv_device_id device_id; + struct msi_desc *msidesc; + struct pci_dev *dev; + int cpu; - return hv_map_interrupt(device_id, false, cpu, vector, entry); + msidesc = irq_data_get_msi_desc(data); + dev = msi_desc_to_pci_dev(msidesc); + device_id = hv_build_pci_dev_id(dev); + cpu = cpumask_first(irq_data_get_effective_affinity_mask(data)); + + return hv_map_interrupt(device_id, false, cpu, cfg->vector, + out_entry ? out_entry : &dummy); } +EXPORT_SYMBOL_GPL(hv_map_msi_interrupt); static inline void entry_to_msi_msg(struct hv_interrupt_entry *entry, struct msi_msg *msg) { @@ -192,11 +213,11 @@ static inline void entry_to_msi_msg(struct hv_interrupt_entry *entry, struct msi static int hv_unmap_msi_interrupt(struct pci_dev *dev, struct hv_interrupt_entry *old_entry); static void hv_irq_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) { - struct hv_interrupt_entry out_entry, *stored_entry; + struct hv_interrupt_entry *stored_entry; struct irq_cfg *cfg = irqd_cfg(data); struct msi_desc *msidesc; struct pci_dev *dev; - int cpu, ret; + int ret; msidesc = irq_data_get_msi_desc(data); dev = msi_desc_to_pci_dev(msidesc); @@ -206,8 +227,6 @@ static void hv_irq_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) return; } - cpu = cpumask_first(irq_data_get_effective_affinity_mask(data)); - if (data->chip_data) { /* * This interrupt is already mapped. Let's unmap first. @@ -234,15 +253,14 @@ static void hv_irq_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) return; } - ret = hv_map_msi_interrupt(dev, cpu, cfg->vector, &out_entry); + ret = hv_map_msi_interrupt(data, stored_entry); if (ret) { kfree(stored_entry); return; } - *stored_entry = out_entry; data->chip_data = stored_entry; - entry_to_msi_msg(&out_entry, msg); + entry_to_msi_msg(data->chip_data, msg); return; } diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h index ab097a3a8b75..abc4659f5809 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -242,6 +242,8 @@ static inline void hv_apic_init(void) {} struct irq_domain *hv_create_pci_msi_domain(void); +int hv_map_msi_interrupt(struct irq_data *data, + struct hv_interrupt_entry *out_entry); int hv_map_ioapic_interrupt(int ioapic_id, bool level, int vcpu, int vector, struct hv_interrupt_entry *entry); int hv_unmap_ioapic_interrupt(int ioapic_id, struct hv_interrupt_entry *entry); From 6f490bb4a982f7455576d6cf409bef10aebd07b8 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Fri, 11 Jul 2025 12:18:52 -0700 Subject: [PATCH 14/15] PCI: hv: Use the correct hypercall for unmasking interrupts on nested Running as nested root on MSHV imposes a different requirement for the pci-hyperv controller. In this setup, the interrupt will first come to the L1 (nested) hypervisor, which will deliver it to the appropriate root CPU. Instead of issuing the RETARGET hypercall, issue the MAP_DEVICE_INTERRUPT hypercall to L1 to complete the setup. Rename hv_arch_irq_unmask() to hv_irq_retarget_interrupt(). Co-developed-by: Jinank Jain Signed-off-by: Jinank Jain Signed-off-by: Stanislav Kinsburskii Signed-off-by: Nuno Das Neves Reviewed-by: Roman Kisel Reviewed-by: Michael Kelley Acked-by: Bjorn Helgaas Link: https://lore.kernel.org/r/1752261532-7225-4-git-send-email-nunodasneves@linux.microsoft.com Signed-off-by: Wei Liu Message-ID: <1752261532-7225-4-git-send-email-nunodasneves@linux.microsoft.com> --- drivers/pci/controller/pci-hyperv.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c index 275b23af3de2..13680363ff19 100644 --- a/drivers/pci/controller/pci-hyperv.c +++ b/drivers/pci/controller/pci-hyperv.c @@ -600,7 +600,7 @@ static unsigned int hv_msi_get_int_vector(struct irq_data *data) #define hv_msi_prepare pci_msi_prepare /** - * hv_arch_irq_unmask() - "Unmask" the IRQ by setting its current + * hv_irq_retarget_interrupt() - "Unmask" the IRQ by setting its current * affinity. * @data: Describes the IRQ * @@ -609,7 +609,7 @@ static unsigned int hv_msi_get_int_vector(struct irq_data *data) * is built out of this PCI bus's instance GUID and the function * number of the device. */ -static void hv_arch_irq_unmask(struct irq_data *data) +static void hv_irq_retarget_interrupt(struct irq_data *data) { struct msi_desc *msi_desc = irq_data_get_msi_desc(data); struct hv_retarget_device_interrupt *params; @@ -714,6 +714,20 @@ out: dev_err(&hbus->hdev->device, "%s() failed: %#llx", __func__, res); } + +static void hv_arch_irq_unmask(struct irq_data *data) +{ + if (hv_root_partition()) + /* + * In case of the nested root partition, the nested hypervisor + * is taking care of interrupt remapping and thus the + * MAP_DEVICE_INTERRUPT hypercall is required instead of + * RETARGET_INTERRUPT. + */ + (void)hv_map_msi_interrupt(data, NULL); + else + hv_irq_retarget_interrupt(data); +} #elif defined(CONFIG_ARM64) /* * SPI vectors to use for vPCI; arch SPIs range is [32, 1019], but leaving a bit From a4131a50d072b369bfed0b41e741c41fd8048641 Mon Sep 17 00:00:00 2001 From: Naman Jain Date: Fri, 11 Jul 2025 11:38:46 +0530 Subject: [PATCH 15/15] tools/hv: fcopy: Fix irregularities with size of ring buffer Size of ring buffer, as defined in uio_hv_generic driver, is no longer fixed to 16 KB. This creates a problem in fcopy, since this size was hardcoded. With the change in place to make ring sysfs node actually reflect the size of underlying ring buffer, it is safe to get the size of ring sysfs file and use it for ring buffer size in fcopy daemon. Fix the issue of disparity in ring buffer size, by making it dynamic in fcopy uio daemon. Cc: stable@vger.kernel.org Fixes: 0315fef2aff9 ("uio_hv_generic: Align ring size to system page") Signed-off-by: Naman Jain Reviewed-by: Saurabh Sengar Reviewed-by: Long Li Link: https://lore.kernel.org/r/20250711060846.9168-1-namjain@linux.microsoft.com Signed-off-by: Wei Liu Message-ID: <20250711060846.9168-1-namjain@linux.microsoft.com> --- tools/hv/hv_fcopy_uio_daemon.c | 91 ++++++++++++++++++++++++++++++---- 1 file changed, 81 insertions(+), 10 deletions(-) diff --git a/tools/hv/hv_fcopy_uio_daemon.c b/tools/hv/hv_fcopy_uio_daemon.c index 4b09ed6b637a..92e8307b2a46 100644 --- a/tools/hv/hv_fcopy_uio_daemon.c +++ b/tools/hv/hv_fcopy_uio_daemon.c @@ -35,7 +35,10 @@ #define WIN8_SRV_MINOR 1 #define WIN8_SRV_VERSION (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR) -#define FCOPY_UIO "/sys/bus/vmbus/devices/eb765408-105f-49b6-b4aa-c123b64d17d4/uio" +#define FCOPY_DEVICE_PATH(subdir) \ + "/sys/bus/vmbus/devices/eb765408-105f-49b6-b4aa-c123b64d17d4/" #subdir +#define FCOPY_UIO_PATH FCOPY_DEVICE_PATH(uio) +#define FCOPY_CHANNELS_PATH FCOPY_DEVICE_PATH(channels) #define FCOPY_VER_COUNT 1 static const int fcopy_versions[] = { @@ -47,9 +50,62 @@ static const int fw_versions[] = { UTIL_FW_VERSION }; -#define HV_RING_SIZE 0x4000 /* 16KB ring buffer size */ +static uint32_t get_ring_buffer_size(void) +{ + char ring_path[PATH_MAX]; + DIR *dir; + struct dirent *entry; + struct stat st; + uint32_t ring_size = 0; + int retry_count = 0; -static unsigned char desc[HV_RING_SIZE]; + /* Find the channel directory */ + dir = opendir(FCOPY_CHANNELS_PATH); + if (!dir) { + usleep(100 * 1000); /* Avoid race with kernel, wait 100ms and retry once */ + dir = opendir(FCOPY_CHANNELS_PATH); + if (!dir) { + syslog(LOG_ERR, "Failed to open channels directory: %s", strerror(errno)); + return 0; + } + } + +retry_once: + while ((entry = readdir(dir)) != NULL) { + if (entry->d_type == DT_DIR && strcmp(entry->d_name, ".") != 0 && + strcmp(entry->d_name, "..") != 0) { + snprintf(ring_path, sizeof(ring_path), "%s/%s/ring", + FCOPY_CHANNELS_PATH, entry->d_name); + + if (stat(ring_path, &st) == 0) { + /* + * stat returns size of Tx, Rx rings combined, + * so take half of it for individual ring size. + */ + ring_size = (uint32_t)st.st_size / 2; + syslog(LOG_INFO, "Ring buffer size from %s: %u bytes", + ring_path, ring_size); + break; + } + } + } + + if (!ring_size && retry_count == 0) { + retry_count = 1; + rewinddir(dir); + usleep(100 * 1000); /* Wait 100ms and retry once */ + goto retry_once; + } + + closedir(dir); + + if (!ring_size) + syslog(LOG_ERR, "Could not determine ring size"); + + return ring_size; +} + +static unsigned char *desc; static int target_fd; static char target_fname[PATH_MAX]; @@ -397,7 +453,7 @@ int main(int argc, char *argv[]) int daemonize = 1, long_index = 0, opt, ret = -EINVAL; struct vmbus_br txbr, rxbr; void *ring; - uint32_t len = HV_RING_SIZE; + uint32_t ring_size, len; char uio_name[NAME_MAX] = {0}; char uio_dev_path[PATH_MAX] = {0}; @@ -428,7 +484,20 @@ int main(int argc, char *argv[]) openlog("HV_UIO_FCOPY", 0, LOG_USER); syslog(LOG_INFO, "starting; pid is:%d", getpid()); - fcopy_get_first_folder(FCOPY_UIO, uio_name); + ring_size = get_ring_buffer_size(); + if (!ring_size) { + ret = -ENODEV; + goto exit; + } + + desc = malloc(ring_size * sizeof(unsigned char)); + if (!desc) { + syslog(LOG_ERR, "malloc failed for desc buffer"); + ret = -ENOMEM; + goto exit; + } + + fcopy_get_first_folder(FCOPY_UIO_PATH, uio_name); snprintf(uio_dev_path, sizeof(uio_dev_path), "/dev/%s", uio_name); fcopy_fd = open(uio_dev_path, O_RDWR); @@ -436,17 +505,17 @@ int main(int argc, char *argv[]) syslog(LOG_ERR, "open %s failed; error: %d %s", uio_dev_path, errno, strerror(errno)); ret = fcopy_fd; - goto exit; + goto free_desc; } - ring = vmbus_uio_map(&fcopy_fd, HV_RING_SIZE); + ring = vmbus_uio_map(&fcopy_fd, ring_size); if (!ring) { ret = errno; syslog(LOG_ERR, "mmap ringbuffer failed; error: %d %s", ret, strerror(ret)); goto close; } - vmbus_br_setup(&txbr, ring, HV_RING_SIZE); - vmbus_br_setup(&rxbr, (char *)ring + HV_RING_SIZE, HV_RING_SIZE); + vmbus_br_setup(&txbr, ring, ring_size); + vmbus_br_setup(&rxbr, (char *)ring + ring_size, ring_size); rxbr.vbr->imask = 0; @@ -463,7 +532,7 @@ int main(int argc, char *argv[]) goto close; } - len = HV_RING_SIZE; + len = ring_size; ret = rte_vmbus_chan_recv_raw(&rxbr, desc, &len); if (unlikely(ret <= 0)) { /* This indicates a failure to communicate (or worse) */ @@ -483,6 +552,8 @@ int main(int argc, char *argv[]) } close: close(fcopy_fd); +free_desc: + free(desc); exit: return ret; }