From 78c65f0f3c0ca5198b454f59069a1c2e352424dd Mon Sep 17 00:00:00 2001 From: Saurabh Sengar Date: Mon, 25 Jul 2022 02:37:28 -0700 Subject: [PATCH 01/10] Drivers: hv: vmbus: Optimize vmbus_on_event In the vmbus_on_event loop, 2 jiffies timer will not serve the purpose if callback_fn takes longer. For effective use move this check inside of callback functions where needed. Out of all the VMbus drivers using vmbus_on_event, only storvsc has a high packet volume, thus add this limit only in storvsc callback for now. There is no apparent benefit of loop itself because this tasklet will be scheduled anyway again if there are packets left in ring buffer. This patch removes this unnecessary loop as well. Signed-off-by: Saurabh Sengar Reviewed-by: Michael Kelley Link: https://lore.kernel.org/r/1658741848-4210-1-git-send-email-ssengar@linux.microsoft.com Signed-off-by: Wei Liu --- drivers/hv/connection.c | 33 ++++++++++++++------------------- drivers/scsi/storvsc_drv.c | 9 +++++++++ 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index eca7afd366d6..9dc27e5d367a 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -431,34 +431,29 @@ struct vmbus_channel *relid2channel(u32 relid) void vmbus_on_event(unsigned long data) { struct vmbus_channel *channel = (void *) data; - unsigned long time_limit = jiffies + 2; + void (*callback_fn)(void *context); trace_vmbus_on_event(channel); hv_debug_delay_test(channel, INTERRUPT_DELAY); - do { - void (*callback_fn)(void *); - /* A channel once created is persistent even when - * there is no driver handling the device. An - * unloading driver sets the onchannel_callback to NULL. - */ - callback_fn = READ_ONCE(channel->onchannel_callback); - if (unlikely(callback_fn == NULL)) - return; + /* A channel once created is persistent even when + * there is no driver handling the device. An + * unloading driver sets the onchannel_callback to NULL. + */ + callback_fn = READ_ONCE(channel->onchannel_callback); + if (unlikely(!callback_fn)) + return; - (*callback_fn)(channel->channel_callback_context); + (*callback_fn)(channel->channel_callback_context); - if (channel->callback_mode != HV_CALL_BATCHED) - return; + if (channel->callback_mode != HV_CALL_BATCHED) + return; - if (likely(hv_end_read(&channel->inbound) == 0)) - return; + if (likely(hv_end_read(&channel->inbound) == 0)) + return; - hv_begin_read(&channel->inbound); - } while (likely(time_before(jiffies, time_limit))); - - /* The time limit (2 jiffies) has been reached */ + hv_begin_read(&channel->inbound); tasklet_schedule(&channel->callback_event); } diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index 8ced292c4b96..398777d3ec15 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -60,6 +60,9 @@ #define VMSTOR_PROTO_VERSION_WIN8_1 VMSTOR_PROTO_VERSION(6, 0) #define VMSTOR_PROTO_VERSION_WIN10 VMSTOR_PROTO_VERSION(6, 2) +/* channel callback timeout in ms */ +#define CALLBACK_TIMEOUT 2 + /* Packet structure describing virtual storage requests. */ enum vstor_packet_operation { VSTOR_OPERATION_COMPLETE_IO = 1, @@ -1204,6 +1207,7 @@ static void storvsc_on_channel_callback(void *context) struct hv_device *device; struct storvsc_device *stor_device; struct Scsi_Host *shost; + unsigned long time_limit = jiffies + msecs_to_jiffies(CALLBACK_TIMEOUT); if (channel->primary_channel != NULL) device = channel->primary_channel->device_obj; @@ -1224,6 +1228,11 @@ static void storvsc_on_channel_callback(void *context) u32 minlen = rqst_id ? sizeof(struct vstor_packet) : sizeof(enum vstor_packet_operation); + if (unlikely(time_after(jiffies, time_limit))) { + hv_pkt_iter_close(channel); + return; + } + if (pktlen < minlen) { dev_err(&device->device, "Invalid pkt: id=%llu, len=%u, minlen=%u\n", From 19b5e6659eaf537ebeac90ae30c7df0296fe5ab9 Mon Sep 17 00:00:00 2001 From: Saurabh Sengar Date: Fri, 23 Sep 2022 10:09:50 +0000 Subject: [PATCH 02/10] drm/hyperv: Don't overwrite dirt_needed value set by host Existing code is causing a race condition where dirt_needed value is already set by the host and gets overwritten with default value. Remove this default setting of dirt_needed, to avoid overwriting the value received in the channel callback set by vmbus_open. Removing this setting also means the default value for dirt_needed is changed to false as it's allocated by kzalloc which is similar to legacy hyperv_fb driver. Signed-off-by: Saurabh Sengar Reviewed-by: Dexuan Cui Reviewed-by: Michael Kelley Link: https://lore.kernel.org/r/1662996766-19304-1-git-send-email-ssengar@linux.microsoft.com Signed-off-by: Wei Liu --- drivers/gpu/drm/hyperv/hyperv_drm_drv.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c index f84d39762a72..ca127ff797f7 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c +++ b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c @@ -142,8 +142,6 @@ static int hyperv_vmbus_probe(struct hv_device *hdev, if (ret) drm_warn(dev, "Failed to update vram location.\n"); - hv->dirt_needed = true; - ret = hyperv_mode_config_init(hv); if (ret) goto err_free_mmio; From e1a863cddbed9cab1d768c720f598322e9a96edb Mon Sep 17 00:00:00 2001 From: Jiapeng Chong Date: Mon, 19 Sep 2022 14:38:15 +0800 Subject: [PATCH 03/10] Drivers: hv: vmbus: Fix kernel-doc drivers/hv/vmbus_drv.c:1587: warning: expecting prototype for __vmbus_child_driver_register(). Prototype was for __vmbus_driver_register() instead. Link: https://bugzilla.openanolis.cn/show_bug.cgi?id=2210 Reported-by: Abaci Robot Signed-off-by: Jiapeng Chong Reviewed-by: Michael Kelley Link: https://lore.kernel.org/r/20220919063815.1881-1-jiapeng.chong@linux.alibaba.com Signed-off-by: Wei Liu --- drivers/hv/vmbus_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 3c833ea60db6..7b628802b1cc 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -1573,7 +1573,7 @@ err_setup: } /** - * __vmbus_child_driver_register() - Register a vmbus's driver + * __vmbus_driver_register() - Register a vmbus's driver * @hv_driver: Pointer to driver structure you want to register * @owner: owner module of the drv * @mod_name: module name string From a99aaf2e3b334a0242ed3c07d39efdf6d4f530f1 Mon Sep 17 00:00:00 2001 From: Easwar Hariharan Date: Mon, 19 Sep 2022 15:04:44 -0700 Subject: [PATCH 04/10] Drivers: hv: vmbus: Use PCI_VENDOR_ID_MICROSOFT for better discoverability pci_ids.h already defines PCI_VENDOR_ID_MICROSOFT, and is included via linux/pci.h. Use the define instead of the magic number. Signed-off-by: Easwar Hariharan Reviewed-by: Michael Kelley Link: https://lore.kernel.org/r/1663625084-2518-2-git-send-email-eahariha@linux.microsoft.com Signed-off-by: Wei Liu --- drivers/hv/vmbus_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 7b628802b1cc..8622db6c7935 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -2052,7 +2052,7 @@ struct hv_device *vmbus_device_create(const guid_t *type, child_device_obj->channel = channel; guid_copy(&child_device_obj->dev_type, type); guid_copy(&child_device_obj->dev_instance, instance); - child_device_obj->vendor_id = 0x1414; /* MSFT vendor ID */ + child_device_obj->vendor_id = PCI_VENDOR_ID_MICROSOFT; return child_device_obj; } From f7ac541e18e2a7b70ae215803e27c78e0f221d00 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskiy Date: Wed, 21 Sep 2022 18:39:05 +0000 Subject: [PATCH 05/10] Drivers: hv: vmbus: Don't wait for the ACPI device upon initialization Waiting to 5 seconds in case of missing VMBus ACPI device is redundant as the device is either present already or won't be available at all. This patch enforces synchronous probing to make sure the bus traversal, happening upon driver registering will either find the device (if present) or not spend any additional time if device is absent. Signed-off-by: Stanislav Kinsburskiy CC: "K. Y. Srinivasan" CC: Haiyang Zhang CC: Stephen Hemminger CC: Wei Liu CC: Dexuan Cui CC: linux-hyperv@vger.kernel.org CC: linux-kernel@vger.kernel.org Reviewed-by: Michael Kelley Reviewed-by: Dexuan Cui Link: https://lore.kernel.org/r/166378554568.581670.1124852716698789244.stgit@skinsburskii-cloud-desktop.internal.cloudapp.net Signed-off-by: Wei Liu --- drivers/hv/vmbus_drv.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 8622db6c7935..cfc231beb52a 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -46,8 +46,6 @@ struct vmbus_dynid { static struct acpi_device *hv_acpi_dev; -static struct completion probe_event; - static int hyperv_cpuhp_online; static void *hv_panic_page; @@ -2467,7 +2465,6 @@ static int vmbus_acpi_add(struct acpi_device *device) ret_val = 0; acpi_walk_err: - complete(&probe_event); if (ret_val) vmbus_acpi_remove(device); return ret_val; @@ -2646,6 +2643,7 @@ static struct acpi_driver vmbus_acpi_driver = { .remove = vmbus_acpi_remove, }, .drv.pm = &vmbus_bus_pm, + .drv.probe_type = PROBE_FORCE_SYNCHRONOUS, }; static void hv_kexec_handler(void) @@ -2718,7 +2716,7 @@ static struct syscore_ops hv_synic_syscore_ops = { static int __init hv_acpi_init(void) { - int ret, t; + int ret; if (!hv_is_hyperv_initialized()) return -ENODEV; @@ -2726,8 +2724,6 @@ static int __init hv_acpi_init(void) if (hv_root_partition) return 0; - init_completion(&probe_event); - /* * Get ACPI resources first. */ @@ -2736,9 +2732,8 @@ static int __init hv_acpi_init(void) if (ret) return ret; - t = wait_for_completion_timeout(&probe_event, 5*HZ); - if (t == 0) { - ret = -ETIMEDOUT; + if (!hv_acpi_dev) { + ret = -ENODEV; goto cleanup; } From 635b241d93010cbbbea3855e4f274c2621df7a19 Mon Sep 17 00:00:00 2001 From: wangjianli Date: Thu, 8 Sep 2022 21:07:54 +0800 Subject: [PATCH 06/10] scsi: storvsc: remove an extraneous "to" in a comment Signed-off-by: wangjianli Link: https://lore.kernel.org/r/20220908130754.34999-1-wangjianli@cdjrlc.com Signed-off-by: Wei Liu --- drivers/scsi/storvsc_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index 398777d3ec15..a60808bb4064 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -2068,7 +2068,7 @@ err_out3: err_out2: /* * Once we have connected with the host, we would need to - * to invoke storvsc_dev_remove() to rollback this state and + * invoke storvsc_dev_remove() to rollback this state and * this call also frees up the stor_device; hence the jump around * err_out1 label. */ From fb2d14add4f813c73bd9d28b750315ccb3f5f0ea Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 27 Sep 2022 14:17:36 -0700 Subject: [PATCH 07/10] Drivers: hv: vmbus: Split memcpy of flex-array To work around a misbehavior of the compiler's ability to see into composite flexible array structs (as detailed in the coming memcpy() hardening series[1]), split the memcpy() of the header and the payload so no false positive run-time overflow warning will be generated. This results in the already inlined memcpy getting unrolled a little more, which very slightly increases text size: $ size drivers/hv/vmbus_drv.o.before drivers/hv/vmbus_drv.o text data bss dec hex filename 22968 5239 232 28439 6f17 drivers/hv/vmbus_drv.o.before 23032 5239 232 28503 6f57 drivers/hv/vmbus_drv.o Avoids the run-time false-positive warning: memcpy: detected field-spanning write (size 212) of single field "&ctx->msg" at drivers/hv/vmbus_drv.c:1133 (size 16) [1] https://lore.kernel.org/linux-hardening/20220901065914.1417829-2-keescook@chromium.org/ Cc: "K. Y. Srinivasan" Cc: Haiyang Zhang Cc: Stephen Hemminger Cc: Wei Liu Cc: Dexuan Cui Cc: linux-hyperv@vger.kernel.org Reported-by: Nathan Chancellor Reported-by: "Gustavo A. R. Silva" Tested-by: Nathan Chancellor Signed-off-by: Kees Cook Reviewed-by: Gustavo A. R. Silva Reviewed-by: Michael Kelley Link: https://lore.kernel.org/r/20220927211736.3241175-1-keescook@chromium.org Signed-off-by: Wei Liu --- drivers/hv/vmbus_drv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index cfc231beb52a..bbbc92994e94 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -1130,7 +1130,8 @@ void vmbus_on_msg_dpc(unsigned long data) return; INIT_WORK(&ctx->work, vmbus_onmessage_work); - memcpy(&ctx->msg, &msg_copy, sizeof(msg->header) + payload_size); + ctx->msg.header = msg_copy.header; + memcpy(&ctx->msg.payload, msg_copy.u.payload, payload_size); /* * The host can generate a rescind message while we From d5ebde1e2b46154d7e03efb1ae3039a304e5386d Mon Sep 17 00:00:00 2001 From: Li kunyu Date: Wed, 28 Sep 2022 14:40:46 +0800 Subject: [PATCH 08/10] hyperv: simplify and rename generate_guest_id The generate_guest_id function is more suitable for use after the following modifications. 1. The return value of the function is modified to u64. 2. Remove the d_info1 and d_info2 parameters from the function, keep the u64 type kernel_version parameter. 3. Rename the function to make it clearly a Hyper-V related function, and modify it to hv_generate_guest_id. Signed-off-by: Li kunyu Reviewed-by: Michael Kelley Link: https://lore.kernel.org/r/20220928064046.3545-1-kunyu@nfschina.com Signed-off-by: Wei Liu --- arch/arm64/hyperv/mshyperv.c | 2 +- arch/x86/hyperv/hv_init.c | 2 +- include/asm-generic/mshyperv.h | 9 +++------ 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/arch/arm64/hyperv/mshyperv.c b/arch/arm64/hyperv/mshyperv.c index bbbe351e9045..a406454578f0 100644 --- a/arch/arm64/hyperv/mshyperv.c +++ b/arch/arm64/hyperv/mshyperv.c @@ -38,7 +38,7 @@ static int __init hyperv_init(void) return 0; /* Setup the guest ID */ - guest_id = generate_guest_id(0, LINUX_VERSION_CODE, 0); + guest_id = hv_generate_guest_id(LINUX_VERSION_CODE); hv_set_vpreg(HV_REGISTER_GUEST_OSID, guest_id); /* Get the features and hints from Hyper-V */ diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c index 3de6d8b53367..032d85ac33fa 100644 --- a/arch/x86/hyperv/hv_init.c +++ b/arch/x86/hyperv/hv_init.c @@ -426,7 +426,7 @@ void __init hyperv_init(void) * 1. Register the guest ID * 2. Enable the hypercall and register the hypercall page */ - guest_id = generate_guest_id(0, LINUX_VERSION_CODE, 0); + guest_id = hv_generate_guest_id(LINUX_VERSION_CODE); wrmsrl(HV_X64_MSR_GUEST_OS_ID, guest_id); /* Hyper-V requires to write guest os id via ghcb in SNP IVM. */ diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h index c05d2ce9b6cd..bfb9eb9d7215 100644 --- a/include/asm-generic/mshyperv.h +++ b/include/asm-generic/mshyperv.h @@ -105,15 +105,12 @@ static inline u64 hv_do_rep_hypercall(u16 code, u16 rep_count, u16 varhead_size, } /* Generate the guest OS identifier as described in the Hyper-V TLFS */ -static inline __u64 generate_guest_id(__u64 d_info1, __u64 kernel_version, - __u64 d_info2) +static inline u64 hv_generate_guest_id(u64 kernel_version) { - __u64 guest_id = 0; + u64 guest_id; - guest_id = (((__u64)HV_LINUX_VENDOR_ID) << 48); - guest_id |= (d_info1 << 48); + guest_id = (((u64)HV_LINUX_VENDOR_ID) << 48); guest_id |= (kernel_version << 16); - guest_id |= d_info2; return guest_id; } From 4c3386f64a432b3697fede579d06f9c1058043ad Mon Sep 17 00:00:00 2001 From: Saurabh Sengar Date: Fri, 9 Sep 2022 08:09:53 -0700 Subject: [PATCH 09/10] drm/hyperv: Add ratelimit on error message Due to a full ring buffer, the driver may be unable to send updates to the Hyper-V host. But outputing the error message can make the problem worse because console output is also typically written to the frame buffer. Rate limiting the error message, also output the error code for additional diagnosability. Signed-off-by: Saurabh Sengar Reviewed-by: Michael Kelley Link: https://lore.kernel.org/r/1662736193-31379-1-git-send-email-ssengar@linux.microsoft.com Signed-off-by: Wei Liu --- drivers/gpu/drm/hyperv/hyperv_drm_proto.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c index 76a182a9a765..013a7829182d 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c +++ b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c @@ -208,7 +208,7 @@ static inline int hyperv_sendpacket(struct hv_device *hdev, struct synthvid_msg VM_PKT_DATA_INBAND, 0); if (ret) - drm_err(&hv->dev, "Unable to send packet via vmbus\n"); + drm_err_ratelimited(&hv->dev, "Unable to send packet via vmbus; error %d\n", ret); return ret; } From 154fb14df7a3c81dea82eca7c0c46590f5ffc3d2 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 28 Sep 2022 17:56:40 +0800 Subject: [PATCH 10/10] x86/hyperv: Replace kmap() with kmap_local_page() kmap() is being deprecated in favor of kmap_local_page()[1]. There are two main problems with kmap(): (1) It comes with an overhead as mapping space is restricted and protected by a global lock for synchronization and (2) it also requires global TLB invalidation when the kmap's pool wraps and it might block when the mapping space is fully utilized until a slot becomes available. With kmap_local_page() the mappings are per thread, CPU local, can take page faults, and can be called from any context (including interrupts). It is faster than kmap() in kernels with HIGHMEM enabled. Furthermore, the tasks can be preempted and, when they are scheduled to run again, the kernel virtual addresses are restored and are still valid. In the fuction hyperv_init() of hyperv/hv_init.c, the mapping is used in a single thread and is short live. So, in this case, it's safe to simply use kmap_local_page() to create mapping, and this avoids the wasted cost of kmap() for global synchronization. In addtion, the fuction hyperv_init() checks if kmap() fails by BUG_ON(). From the original discussion[2], the BUG_ON() here is just used to explicitly panic NULL pointer. So still keep the BUG_ON() in place to check if kmap_local_page() fails. Based on this consideration, memcpy_to_page() is not selected here but only kmap_local_page() is used. Therefore, replace kmap() with kmap_local_page() in hyperv/hv_init.c. [1]: https://lore.kernel.org/all/20220813220034.806698-1-ira.weiny@intel.com [2]: https://lore.kernel.org/lkml/20200915103710.cqmdvzh5lys4wsqo@liuwe-devbox-debian-v2/ Suggested-by: Dave Hansen Suggested-by: Ira Weiny Suggested-by: Fabio M. De Francesco Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20220928095640.626350-1-zhao1.liu@linux.intel.com Signed-off-by: Wei Liu --- arch/x86/hyperv/hv_init.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c index 032d85ac33fa..29774126e931 100644 --- a/arch/x86/hyperv/hv_init.c +++ b/arch/x86/hyperv/hv_init.c @@ -459,13 +459,13 @@ void __init hyperv_init(void) wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); pg = vmalloc_to_page(hv_hypercall_pg); - dst = kmap(pg); + dst = kmap_local_page(pg); src = memremap(hypercall_msr.guest_physical_address << PAGE_SHIFT, PAGE_SIZE, MEMREMAP_WB); BUG_ON(!(src && dst)); memcpy(dst, src, HV_HYP_PAGE_SIZE); memunmap(src); - kunmap(pg); + kunmap_local(dst); } else { hypercall_msr.guest_physical_address = vmalloc_to_pfn(hv_hypercall_pg); wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);