summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-10-29 12:39:45 -1000
committerLinus Torvalds <torvalds@linux-foundation.org>2026-10-29 12:39:45 -1000
commit9c7e3d59107e2daf204bf6bf87b6c448ca89defe (patch)
tree59e0a03cba2d5dc957b578b0afbb5e95a39fe0ee
parent1b8b67503725fa6deb3a4f00ea61f982bd00c152 (diff)
parent33b28f3b313353f11fc939e9a268605e66290d0b (diff)
Merge tag 'hyperv-next-signed-20261028' of git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux
Pull hyperv updates from Wei Liu: - Update to documents and code comments (Michael Kelley) - Harden Vmbus (Kameron Carr) - Rework MSHV driver page deposit logic (Mukesh Rathor) - Vmbus kexec bug fix (Shradha Gupta) - Miscellaneous bug fixes (Daehyeon Ko, Jiale Yao, Sasha Levin) * tag 'hyperv-next-signed-20261028' of git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux: Drivers: hv: Remove leftover support for old Hyper-V versions mshv: Remove unused *_old memory deposit functions mshv: Redesign hypervisor memory deposit logic mshv: Rename memory deposit memory functions to _old mshv_vtl: reject poll files with multiple waitqueues mshv_vtl: detach poll waiter on POLLFREE mshv_vtl: initialize poll file mutex before device registration Documentation: hyperv: Update information on guest CPU offlining Documentation: hyperv: Remove support for guests on Hyper-V 2012/2012R2 Drivers: hv: Run VMBus kexec cleanup from syscore shutdown kexec: Add helper to report an active kexec reboot tools: hv: remove unused rte_smp_mb() Documentation: hyperv: Update documentation for sending VMBus packets Drivers: hv: vmbus: Check ACPI companion before use Documentation: hyperv: Describe validation of the ring buffer indices Drivers: hv: vmbus: Keep the ring byte counts sane for a bad index Drivers: hv: vmbus: Compute ring byte counts from a caller-held snapshot Drivers: hv: vmbus: Use *_ONCE() to access shared ring buffer indices Drivers: hv: vmbus: Bounds check the shared ring buffer indices
-rw-r--r--Documentation/networking/device_drivers/ethernet/microsoft/netvsc.rst14
-rw-r--r--Documentation/virt/hyperv/clocks.rst8
-rw-r--r--Documentation/virt/hyperv/coco.rst5
-rw-r--r--Documentation/virt/hyperv/overview.rst28
-rw-r--r--Documentation/virt/hyperv/vmbus.rst17
-rw-r--r--arch/x86/kernel/cpu/mshyperv.c27
-rw-r--r--drivers/hv/hv_common.c10
-rw-r--r--drivers/hv/hv_proc.c246
-rw-r--r--drivers/hv/mshv_root_hv_call.c10
-rw-r--r--drivers/hv/mshv_vtl_main.c55
-rw-r--r--drivers/hv/ring_buffer.c126
-rw-r--r--drivers/hv/vmbus_drv.c30
-rw-r--r--include/asm-generic/mshyperv.h7
-rw-r--r--include/linux/hyperv.h58
-rw-r--r--include/linux/kexec.h2
-rw-r--r--kernel/kexec_core.c7
16 files changed, 356 insertions, 294 deletions
diff --git a/Documentation/networking/device_drivers/ethernet/microsoft/netvsc.rst b/Documentation/networking/device_drivers/ethernet/microsoft/netvsc.rst
index 616f808e885b..0d464706a59a 100644
--- a/Documentation/networking/device_drivers/ethernet/microsoft/netvsc.rst
+++ b/Documentation/networking/device_drivers/ethernet/microsoft/netvsc.rst
@@ -7,8 +7,9 @@ Hyper-V network driver
Compatibility
=============
-This driver is compatible with Windows Server 2012 R2, 2016 and
-Windows 10.
+This driver supports Linux guests running on Windows Server 2016 Hyper-V
+and later, and Windows 10 and later. Support for running as a guest on
+earlier versions of Hyper-V has been removed from Linux.
Features
========
@@ -16,9 +17,8 @@ Features
Checksum offload
----------------
The netvsc driver supports checksum offload as long as the
- Hyper-V host version does. Windows Server 2016 and Azure
- support checksum offload for TCP and UDP for both IPv4 and
- IPv6. Windows Server 2012 only supports checksum offload for TCP.
+ Hyper-V host version does. Windows Server 2016 Hyper-V and later, and
+ Azure, support checksum offload for TCP and UDP for both IPv4 and IPv6.
Receive Side Scaling
--------------------
@@ -58,8 +58,8 @@ Large Receive Offload (LRO), or Receive Side Coalescing (RSC)
-------------------------------------------------------------
The driver supports LRO/RSC in the vSwitch feature. It reduces the per packet
processing overhead by coalescing multiple TCP segments when possible. The
- feature is enabled by default on VMs running on Windows Server 2019 and
- later. It may be changed by ethtool command::
+ feature is enabled by default on VMs running on Windows Server 2019 Hyper-V
+ and later. It may be changed by ethtool command::
ethtool -K eth0 lro on
ethtool -K eth0 lro off
diff --git a/Documentation/virt/hyperv/clocks.rst b/Documentation/virt/hyperv/clocks.rst
index 176043265803..9527e6073654 100644
--- a/Documentation/virt/hyperv/clocks.rst
+++ b/Documentation/virt/hyperv/clocks.rst
@@ -64,12 +64,8 @@ applying the scale and offset to get the constant 10 MHz clock.
Linux clockevents are based on Hyper-V synthetic timer 0 (stimer0).
While Hyper-V offers 4 synthetic timers for each CPU, Linux only uses
-timer 0. In older versions of Hyper-V, an interrupt from stimer0
-results in a VMBus control message that is demultiplexed by
-vmbus_isr() as described in the Documentation/virt/hyperv/vmbus.rst
-documentation. In newer versions of Hyper-V, stimer0 interrupts can
-be mapped to an architectural interrupt, which is referred to as
-"Direct Mode". Linux prefers to use Direct Mode when available. Since
+timer 0. stimer0 is operated in what Hyper-V calls "Direct Mode"
+where timer interrupts are mapped to an architectural interrupt. Since
x86/x64 doesn't support per-CPU interrupts, Direct Mode statically
allocates an x86 interrupt vector (HYPERV_STIMER0_VECTOR) across all CPUs
and explicitly codes it to call the stimer0 interrupt handler. Hence
diff --git a/Documentation/virt/hyperv/coco.rst b/Documentation/virt/hyperv/coco.rst
index 3231e51444da..3e20249bd9b3 100644
--- a/Documentation/virt/hyperv/coco.rst
+++ b/Documentation/virt/hyperv/coco.rst
@@ -200,7 +200,10 @@ validation is performed. For messages passed from the host to the guest in a
VMBus ring buffer, the length of the message is validated, and the message is
copied into a temporary (encrypted) buffer for further validation and
processing. The copying adds a small amount of overhead, but is the only way
-to protect against a malicious host. See hv_pkt_iter_first().
+to protect against a malicious host. See hv_pkt_iter_first(). Similarly, the
+ring buffer header is shared with the host, so the guest must snapshot and
+validate the read and write indices before using them to index into the ring
+buffer or to compute available space.
Many drivers for VMBus devices have been "hardened" by adding code to fully
validate messages received over VMBus, instead of assuming that Hyper-V is
diff --git a/Documentation/virt/hyperv/overview.rst b/Documentation/virt/hyperv/overview.rst
index 77408a89d1a4..6c1b9d4b6ddd 100644
--- a/Documentation/virt/hyperv/overview.rst
+++ b/Documentation/virt/hyperv/overview.rst
@@ -114,11 +114,13 @@ a shared page or remove an overlay page after the new kernel is
using the page for a different purpose, corrupting the new kernel.
Hyper-V does not provide a single "set everything" operation to
guest VMs, so Linux code must individually revoke all sharing before
-doing kexec. See hv_kexec_handler() and hv_crash_handler(). But
-the crash/panic path still has holes in cleanup because some shared
-pages are set using per-CPU synthetic registers and there's no
-mechanism to revoke the shared pages for CPUs other than the CPU
-running the panic path.
+doing kexec. For a normal kexec reboot, the VMBus driver calls
+hv_kexec_handler() from its syscore shutdown callback, after device
+shutdown and before the architecture-specific machine shutdown. The
+crash/panic path uses hv_crash_handler(), but still has holes in
+cleanup because some shared pages are set using per-CPU synthetic
+registers and there is no mechanism to revoke the shared pages for
+CPUs other than the CPU running the panic path.
CPU Management
--------------
@@ -132,11 +134,11 @@ Starting in Windows Server 2022 Hyper-V, the ACPI tables reflect
only the CPUs actually present in the VM, so Linux does not report
any hot-add CPUs.
-A Linux guest CPU may be taken offline using the normal Linux
-mechanisms, provided no VMBus channel interrupts are assigned to
-the CPU. See the section on VMBus Interrupts for more details
-on how VMBus channel interrupts can be re-assigned to permit
-taking a CPU offline.
+A Linux guest CPU may be taken offline, and subsequently brought
+back online, using the normal Linux mechanisms. See the "VMBus
+Interrupts" section of Documentation/virt/hyperv/vmbus.rst
+documentation for how VMBus channel interrupts are automatically
+re-assigned if necessary when taking a CPU offline.
32-bit and 64-bit
-----------------
@@ -157,9 +159,9 @@ when accessing data shared with Hyper-V.
Versioning
----------
Current Linux kernels operate correctly with older versions of
-Hyper-V back to Windows Server 2012 Hyper-V. Support for running
-on the original Hyper-V release in Windows Server 2008/2008 R2
-has been removed.
+Hyper-V back to Windows Server 2016 Hyper-V and Windows 10. Support for
+running on Windows Server 2012/2012 R2, Windows Server 2008/2008 R2,
+and Windows 8.1 and earlier has been removed.
A Linux guest on Hyper-V outputs in dmesg the version of Hyper-V
it is running on. This version is in the form of a Windows build
diff --git a/Documentation/virt/hyperv/vmbus.rst b/Documentation/virt/hyperv/vmbus.rst
index 654bb4849972..c546e821ce6b 100644
--- a/Documentation/virt/hyperv/vmbus.rst
+++ b/Documentation/virt/hyperv/vmbus.rst
@@ -141,14 +141,15 @@ Three functions exist to send VMBus channel messages:
1. vmbus_sendpacket(): Control-only messages and messages with
embedded data -- no GPAs
-2. vmbus_sendpacket_pagebuffer(): Message with list of GPAs
- identifying data to transfer. An offset and length is
- associated with each GPA so that multiple discontinuous areas
- of guest memory can be targeted.
-3. vmbus_sendpacket_mpb_desc(): Message with list of GPAs
- identifying data to transfer. A single offset and length is
- associated with a list of GPAs. The GPAs must describe a
- single logical area of guest memory to be targeted.
+2. vmbus_sendpacket_getid(): Similar to vmbus_sendpacket() but returns
+ a transaction ID that is matched against a reply from the Hyper-V
+ host. Useful when the struct completion (or other rendezvous data)
+ must reside on the caller's stack. By invalidating the transaction
+ ID before exiting the function due to a timeout or other error, a
+ late arriving interrupt is assured to not trash the stack.
+3. vmbus_sendpacket_mpb_desc(): Message with one or more data ranges
+ to target. Each range consists of a single offset and length plus
+ a list of GPAs that describe a logical area of guest memory.
Historically, Linux guests have trusted Hyper-V to send well-formed
and valid messages, and Linux drivers for synthetic devices did not
diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c
index b4af7c0a70ac..fec1c37dafbf 100644
--- a/arch/x86/kernel/cpu/mshyperv.c
+++ b/arch/x86/kernel/cpu/mshyperv.c
@@ -148,7 +148,6 @@ EXPORT_SYMBOL_GPL(hv_set_msr);
static void (*mshv_handler)(void);
static void (*vmbus_handler)(void);
static void (*hv_stimer0_handler)(void);
-static void (*hv_kexec_handler)(void);
static void (*hv_crash_handler)(struct pt_regs *regs);
DEFINE_IDTENTRY_SYSVEC(sysvec_hyperv_callback)
@@ -215,16 +214,6 @@ void hv_remove_stimer0_handler(void)
hv_stimer0_handler = NULL;
}
-void hv_setup_kexec_handler(void (*handler)(void))
-{
- hv_kexec_handler = handler;
-}
-
-void hv_remove_kexec_handler(void)
-{
- hv_kexec_handler = NULL;
-}
-
void hv_setup_crash_handler(void (*handler)(struct pt_regs *regs))
{
hv_crash_handler = handler;
@@ -240,17 +229,13 @@ static void hv_machine_shutdown(void)
{
if (kexec_in_progress) {
hv_stimer_global_cleanup();
-
- if (hv_kexec_handler)
- hv_kexec_handler();
- }
-
- /*
- * Call hv_cpu_die() on all the CPUs, otherwise later the hypervisor
- * corrupts the old VP Assist Pages and can crash the kexec kernel.
- */
- if (kexec_in_progress)
+ /*
+ * Call hv_cpu_die() on all the CPUs, otherwise later the
+ * hypervisor corrupts the old VP Assist Pages and can crash
+ * the kexec kernel.
+ */
cpuhp_remove_state(CPUHP_AP_HYPERV_ONLINE);
+ }
/* The function calls stop_other_cpus(). */
native_machine_shutdown();
diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c
index 4f3bb9b4e87c..27b9b79d4f4c 100644
--- a/drivers/hv/hv_common.c
+++ b/drivers/hv/hv_common.c
@@ -711,16 +711,6 @@ void __weak hv_setup_mshv_handler(void (*handler)(void))
}
EXPORT_SYMBOL_GPL(hv_setup_mshv_handler);
-void __weak hv_setup_kexec_handler(void (*handler)(void))
-{
-}
-EXPORT_SYMBOL_GPL(hv_setup_kexec_handler);
-
-void __weak hv_remove_kexec_handler(void)
-{
-}
-EXPORT_SYMBOL_GPL(hv_remove_kexec_handler);
-
void __weak hv_setup_crash_handler(void (*handler)(struct pt_regs *regs))
{
}
diff --git a/drivers/hv/hv_proc.c b/drivers/hv/hv_proc.c
index 57b2c64197cb..f811bb5c42a1 100644
--- a/drivers/hv/hv_proc.c
+++ b/drivers/hv/hv_proc.c
@@ -9,135 +9,177 @@
#include <linux/export.h>
#include <asm/mshyperv.h>
+#define HV_DEPOSIT_MAX 512
+#define HV_DEPOSIT_INP_MAX ((HV_HYP_PAGE_SIZE - \
+ offsetof(struct hv_deposit_memory, gpa_page_list)) / sizeof(u64))
+
/*
- * See struct hv_deposit_memory. The first u64 is partition ID, the rest
- * are GPAs.
+ * Allocate free pages for deposit to hypervisor. pfna[] must be large enough
+ * to hold HV_DEPOSIT_INP_MAX (511) pages. If num_pages is 512, return last
+ * pfn in lastpfn. If @single, then it must be a single allocation (not split
+ * over multiple contiguous ranges).
+ *
+ * Returns: number of pages allocated or -ENOMEM
*/
-#define HV_DEPOSIT_MAX (HV_HYP_PAGE_SIZE / sizeof(u64) - 1)
-
-/* Deposits exact number of pages. Must be called with interrupts enabled. */
-int hv_call_deposit_pages(int node, u64 partition_id, u32 num_pages)
+static int hv_alloc_dep_pages(int node, u64 *pfna, u64 *lastpfnp, int num_pages,
+ bool single)
{
- struct page **pages, *page;
- int *counts;
- int num_allocations;
- int i, j, page_count;
- int order;
- u64 status;
- int ret;
- u64 base_pfn;
- struct hv_deposit_memory *input_page;
- unsigned long flags;
-
- if (num_pages > HV_DEPOSIT_MAX)
- return -E2BIG;
- if (!num_pages)
- return 0;
-
- /* One buffer for page pointers and counts */
- page = alloc_page(GFP_KERNEL);
- if (!page)
- return -ENOMEM;
- pages = page_address(page);
+ struct page *page;
+ int num_allocd, count = 0;
- counts = kzalloc_objs(int, HV_DEPOSIT_MAX);
- if (!counts) {
- free_page((unsigned long)pages);
- return -ENOMEM;
- }
+ /* Published ABI, enforce its immutability. */
+ BUILD_BUG_ON(HV_DEPOSIT_INP_MAX != 511);
- /* Allocate all the pages before disabling interrupts */
- i = 0;
+ if (num_pages > HV_DEPOSIT_MAX ||
+ (num_pages == HV_DEPOSIT_MAX && lastpfnp == NULL))
+ return -EINVAL;
while (num_pages) {
/* Find highest order we can actually allocate */
- order = 31 - __builtin_clz(num_pages);
+ int order = 31 - __builtin_clz(num_pages);
+ gfp_t gfp_flags = GFP_KERNEL;
+
+ if (!single)
+ gfp_flags |= __GFP_NOWARN;
while (1) {
- pages[i] = alloc_pages_node(node, GFP_KERNEL, order);
- if (pages[i])
+ page = alloc_pages_node(node, gfp_flags, order);
+ if (page || order == 0 || single)
break;
- if (!order) {
- ret = -ENOMEM;
- num_allocations = i;
- goto err_free_allocations;
- }
- --order;
+
+ order--;
}
- split_page(pages[i], order);
- counts[i] = 1 << order;
- num_pages -= counts[i];
- i++;
- }
- num_allocations = i;
+ if (page == NULL)
+ break;
- local_irq_save(flags);
+ split_page(page, order);
+ num_allocd = 1 << order;
+ num_pages -= num_allocd;
- input_page = *this_cpu_ptr(hyperv_pcpu_input_arg);
+ while (num_allocd && count < HV_DEPOSIT_INP_MAX) {
+ pfna[count++] = page_to_pfn(page++);
+ num_allocd--;
+ }
- input_page->partition_id = partition_id;
+ if (num_allocd-- && count == HV_DEPOSIT_INP_MAX) {
+ *lastpfnp = page_to_pfn(page);
+ count++;
+ break;
+ }
+ }
+
+ return count ? count : -ENOMEM;
+}
- /* Populate gpa_page_list - these will fit on the input page */
- for (i = 0, page_count = 0; i < num_allocations; ++i) {
- base_pfn = page_to_pfn(pages[i]);
- for (j = 0; j < counts[i]; ++j, ++page_count)
- input_page->gpa_page_list[page_count] = base_pfn + j;
+/*
+ * Deposit memory in the hypervisor. Even if @contiguous is false, a contiguous
+ * 2M worth of pfns is utmost desired for performance reasons. But short of
+ * that, we deposit whatever contiguous chunks we can get. If @contiguous is
+ * true, then the entire range has to be physically contiguous. Note, in that
+ * case, upon withdrawl, hypervisor could return any page in between the range,
+ * so we must split that also. Lastly, HV_MAX_CONTIGUOUS_ALLOCATION_PAGES is
+ * not guaranteed to always be power of 2.
+ */
+static int hv_call_deposit_pages(int node, u64 partition_id, bool contiguous)
+{
+ struct hv_deposit_memory *hc_input;
+ int i, rc, num_pages;
+ u64 status, *pfna, lastpfn = 0;
+ bool trunc_extra = false;
+
+ BUILD_BUG_ON(HV_MAX_CONTIGUOUS_ALLOCATION_PAGES > HV_DEPOSIT_MAX);
+
+ if (contiguous) {
+ num_pages = roundup_pow_of_two(
+ HV_MAX_CONTIGUOUS_ALLOCATION_PAGES);
+ trunc_extra = num_pages != HV_MAX_CONTIGUOUS_ALLOCATION_PAGES;
+ } else {
+ num_pages = HV_DEPOSIT_MAX;
}
- status = hv_do_rep_hypercall(HVCALL_DEPOSIT_MEMORY,
- page_count, 0, input_page, NULL);
- local_irq_restore(flags);
- if (!hv_result_success(status)) {
- hv_status_err(status, "\n");
- ret = hv_result_to_errno(status);
- goto err_free_allocations;
+
+ hc_input = (struct hv_deposit_memory *)get_zeroed_page(GFP_KERNEL);
+ if (hc_input == NULL)
+ return -ENOMEM;
+
+ hc_input->partition_id = partition_id;
+ pfna = hc_input->gpa_page_list;
+
+ rc = hv_alloc_dep_pages(node, pfna, &lastpfn, num_pages, contiguous);
+ if (rc < 0)
+ goto out_free;
+
+ num_pages = rc;
+ if (num_pages > HV_DEPOSIT_INP_MAX)
+ num_pages = HV_DEPOSIT_INP_MAX;
+
+ if (contiguous && trunc_extra) {
+ for (i = HV_MAX_CONTIGUOUS_ALLOCATION_PAGES; i < num_pages; i++)
+ __free_page(pfn_to_page(pfna[i]));
+
+ if (lastpfn) {
+ __free_page(pfn_to_page(lastpfn));
+ lastpfn = 0;
+ }
+
+ num_pages = HV_MAX_CONTIGUOUS_ALLOCATION_PAGES;
}
- ret = 0;
- goto free_buf;
+ /* Not using hyperv_pcpu_input_arg, so no need to disable interrupts */
+
+ status = hv_do_rep_hypercall(HVCALL_DEPOSIT_MEMORY, num_pages, 0,
+ hc_input, NULL);
+ if (!hv_result_success(status))
+ goto err_free_dep_pages;
-err_free_allocations:
- for (i = 0; i < num_allocations; ++i) {
- base_pfn = page_to_pfn(pages[i]);
- for (j = 0; j < counts[i]; ++j)
- __free_page(pfn_to_page(base_pfn + j));
+ if (lastpfn) {
+ hc_input->gpa_page_list[0] = lastpfn;
+ status = hv_do_rep_hypercall(HVCALL_DEPOSIT_MEMORY, 1, 0,
+ hc_input, NULL);
+
+ if (!hv_result_success(status) && hv_repcomp(status) == 0)
+ /* We deposited lot earlier, so give it a go */
+ __free_page(pfn_to_page(lastpfn));
}
-free_buf:
- free_page((unsigned long)pages);
- kfree(counts);
- return ret;
+ free_page((unsigned long)hc_input);
+ return 0;
+
+err_free_dep_pages:
+ hv_status_err(status, "\n");
+ rc = hv_result_to_errno(status);
+
+ for (i = hv_repcomp(status); i < num_pages; i++)
+ __free_page(pfn_to_page(pfna[i]));
+ if (lastpfn)
+ __free_page(pfn_to_page(lastpfn));
+
+out_free:
+ free_page((unsigned long)hc_input);
+ return rc;
}
-EXPORT_SYMBOL_GPL(hv_call_deposit_pages);
-int hv_deposit_memory_node(int node, u64 partition_id,
- u64 hv_status)
+int hv_deposit_memory_node(int node, u64 pt_id, u64 hv_status)
{
- u32 num_pages = 1;
+ int result = hv_result(hv_status);
+ bool contiguous = false;
- switch (hv_result(hv_status)) {
- case HV_STATUS_INSUFFICIENT_MEMORY:
- break;
- case HV_STATUS_INSUFFICIENT_CONTIGUOUS_MEMORY:
- num_pages = HV_MAX_CONTIGUOUS_ALLOCATION_PAGES;
- break;
-
- case HV_STATUS_INSUFFICIENT_CONTIGUOUS_ROOT_MEMORY:
- num_pages = HV_MAX_CONTIGUOUS_ALLOCATION_PAGES;
- fallthrough;
- case HV_STATUS_INSUFFICIENT_ROOT_MEMORY:
+ if (result == HV_STATUS_INSUFFICIENT_ROOT_MEMORY ||
+ result == HV_STATUS_INSUFFICIENT_CONTIGUOUS_ROOT_MEMORY) {
if (!hv_root_partition()) {
- hv_status_err(hv_status, "Unexpected root memory deposit\n");
- return -ENOMEM;
+ hv_status_err(hv_status,
+ "Unexpected root memory deposit\n");
+ return -EINVAL;
}
- partition_id = HV_PARTITION_ID_SELF;
- break;
- default:
- hv_status_err(hv_status, "Unexpected!\n");
- return -ENOMEM;
+ pt_id = HV_PARTITION_ID_SELF;
}
- return hv_call_deposit_pages(node, partition_id, num_pages);
+
+ if (result == HV_STATUS_INSUFFICIENT_CONTIGUOUS_MEMORY ||
+ result == HV_STATUS_INSUFFICIENT_CONTIGUOUS_ROOT_MEMORY)
+ contiguous = true;
+
+ return hv_call_deposit_pages(node, pt_id, contiguous);
}
EXPORT_SYMBOL_GPL(hv_deposit_memory_node);
@@ -203,14 +245,6 @@ int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags)
unsigned long irq_flags;
int ret = 0;
- /* Root VPs don't seem to need pages deposited */
- if (partition_id != hv_current_partition_id) {
- /* The value 90 is empirically determined. It may change. */
- ret = hv_call_deposit_pages(node, partition_id, 90);
- if (ret)
- return ret;
- }
-
do {
local_irq_save(irq_flags);
diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c
index cb55d4d4be2e..b8d199f95299 100644
--- a/drivers/hv/mshv_root_hv_call.c
+++ b/drivers/hv/mshv_root_hv_call.c
@@ -15,8 +15,6 @@
#include "mshv_root.h"
/* Determined empirically */
-#define HV_INIT_PARTITION_DEPOSIT_PAGES 208
-#define HV_MAP_GPA_DEPOSIT_PAGES 256
#define HV_UMAP_GPA_PAGES 512
#define HV_PAGE_COUNT_2M_ALIGNED(pg_count) (!((pg_count) & (0x200 - 1)))
@@ -140,11 +138,6 @@ int hv_call_initialize_partition(u64 partition_id)
input.partition_id = partition_id;
- ret = hv_call_deposit_pages(NUMA_NO_NODE, partition_id,
- HV_INIT_PARTITION_DEPOSIT_PAGES);
- if (ret)
- return ret;
-
do {
status = hv_do_fast_hypercall8(HVCALL_INITIALIZE_PARTITION,
*(u64 *)&input);
@@ -248,8 +241,7 @@ static int hv_do_map_gpa_hcall(u64 partition_id, u64 gfn, u64 page_struct_count,
completed = hv_repcomp(status);
if (hv_result_needs_memory(status)) {
- ret = hv_call_deposit_pages(NUMA_NO_NODE, partition_id,
- HV_MAP_GPA_DEPOSIT_PAGES);
+ ret = hv_deposit_memory(partition_id, status);
if (ret)
break;
diff --git a/drivers/hv/mshv_vtl_main.c b/drivers/hv/mshv_vtl_main.c
index d8225242c73f..2d4fe24cecbf 100644
--- a/drivers/hv/mshv_vtl_main.c
+++ b/drivers/hv/mshv_vtl_main.c
@@ -17,6 +17,7 @@
#include <linux/entry-virt.h>
#include <linux/eventfd.h>
#include <linux/poll.h>
+#include <linux/rcupdate.h>
#include <linux/file.h>
#include <linux/vmalloc.h>
#include <asm/debugreg.h>
@@ -72,6 +73,7 @@ struct mshv_vtl_poll_file {
wait_queue_entry_t wait;
wait_queue_head_t *wqh;
poll_table pt;
+ int error;
int cpu;
};
@@ -95,7 +97,7 @@ union hv_synic_overlay_page_msr {
} __packed;
};
-static struct mutex mshv_vtl_poll_file_lock;
+static DEFINE_MUTEX(mshv_vtl_poll_file_lock);
static union hv_register_vsm_page_offsets mshv_vsm_page_offsets;
static union hv_register_vsm_capabilities mshv_vsm_capabilities;
@@ -448,17 +450,51 @@ static void mshv_vtl_cancel(int cpu)
static int mshv_vtl_poll_file_wake(wait_queue_entry_t *wait, unsigned int mode, int sync, void *key)
{
struct mshv_vtl_poll_file *poll_file = container_of(wait, struct mshv_vtl_poll_file, wait);
+ __poll_t pollflags = key_to_poll(key);
+
+ if (pollflags & POLLFREE)
+ list_del_init(&wait->entry);
mshv_vtl_cancel(poll_file->cpu);
+ if (pollflags & POLLFREE) {
+ /* Publish teardown last; pair with acquire in the removal path. */
+ smp_store_release(&poll_file->wqh, NULL);
+ }
+
return 0;
}
+static void mshv_vtl_poll_file_remove_wait_queue(struct mshv_vtl_poll_file *poll_file)
+{
+ wait_queue_head_t *wqh;
+
+ /* POLLFREE providers RCU-delay the waitqueue free. */
+ rcu_read_lock();
+ /* Pair with the release store in the POLLFREE callback. */
+ wqh = smp_load_acquire(&poll_file->wqh);
+ if (wqh)
+ remove_wait_queue(wqh, &poll_file->wait);
+ rcu_read_unlock();
+ WRITE_ONCE(poll_file->wqh, NULL);
+}
+
static void mshv_vtl_ptable_queue_proc(struct file *file, wait_queue_head_t *wqh, poll_table *pt)
{
struct mshv_vtl_poll_file *poll_file = container_of(pt, struct mshv_vtl_poll_file, pt);
- WARN_ON(poll_file->wqh);
+ if (poll_file->error)
+ return;
+
+ if (poll_file->wqh) {
+ if (poll_file->wqh == wqh)
+ return;
+
+ mshv_vtl_poll_file_remove_wait_queue(poll_file);
+ poll_file->error = -EINVAL;
+ return;
+ }
+
poll_file->wqh = wqh;
add_wait_queue(wqh, &poll_file->wait);
}
@@ -468,6 +504,7 @@ static int mshv_vtl_ioctl_set_poll_file(struct mshv_vtl_set_poll_file __user *us
struct file *file, *old_file;
struct mshv_vtl_poll_file *poll_file;
struct mshv_vtl_set_poll_file input;
+ int ret;
if (copy_from_user(&input, user_input, sizeof(input)))
return -EFAULT;
@@ -490,12 +527,11 @@ static int mshv_vtl_ioctl_set_poll_file(struct mshv_vtl_set_poll_file __user *us
mutex_lock(&mshv_vtl_poll_file_lock);
- if (poll_file->wqh)
- remove_wait_queue(poll_file->wqh, &poll_file->wait);
- poll_file->wqh = NULL;
+ mshv_vtl_poll_file_remove_wait_queue(poll_file);
old_file = poll_file->file;
poll_file->file = file;
+ poll_file->error = 0;
poll_file->cpu = input.cpu;
if (file) {
@@ -503,13 +539,18 @@ static int mshv_vtl_ioctl_set_poll_file(struct mshv_vtl_set_poll_file __user *us
init_poll_funcptr(&poll_file->pt, mshv_vtl_ptable_queue_proc);
vfs_poll(file, &poll_file->pt);
}
+ ret = poll_file->error;
+ if (ret)
+ poll_file->file = NULL;
mutex_unlock(&mshv_vtl_poll_file_lock);
if (old_file)
fput(old_file);
+ if (ret)
+ fput(file);
- return 0;
+ return ret;
}
/* Static table mapping register names to their corresponding actions */
@@ -1370,8 +1411,6 @@ static int __init mshv_vtl_init(void)
goto free_low;
}
- mutex_init(&mshv_vtl_poll_file_lock);
-
device_initialize(mem_dev);
dev_set_name(mem_dev, "mshv vtl mem dev");
ret = device_add(mem_dev);
diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c
index 592a9601faaa..d8b085dfdb46 100644
--- a/drivers/hv/ring_buffer.c
+++ b/drivers/hv/ring_buffer.c
@@ -70,21 +70,12 @@ static void hv_signal_on_write(u32 old_write, struct vmbus_channel *channel)
}
}
-/* Get the next write location for the specified ring buffer. */
-static inline u32
-hv_get_next_write_location(struct hv_ring_buffer_info *ring_info)
-{
- u32 next = ring_info->ring_buffer->write_index;
-
- return next;
-}
-
/* Set the next write location for the specified ring buffer. */
static inline void
hv_set_next_write_location(struct hv_ring_buffer_info *ring_info,
u32 next_write_location)
{
- ring_info->ring_buffer->write_index = next_write_location;
+ WRITE_ONCE(ring_info->ring_buffer->write_index, next_write_location);
}
/* Get the size of the ring buffer. */
@@ -94,13 +85,6 @@ hv_get_ring_buffersize(const struct hv_ring_buffer_info *ring_info)
return ring_info->ring_datasize;
}
-/* Get the read and write indices as u64 of the specified ring buffer. */
-static inline u64
-hv_get_ring_bufferindices(struct hv_ring_buffer_info *ring_info)
-{
- return (u64)ring_info->ring_buffer->write_index << 32;
-}
-
/*
* Helper routine to copy from source to ring buffer.
* Assume there is enough room. Handles wrap-around in dest case only!!
@@ -123,35 +107,12 @@ static u32 hv_copyto_ringbuffer(
return start_write_offset;
}
-/*
- *
- * hv_get_ringbuffer_availbytes()
- *
- * Get number of bytes available to read and to write to
- * for the specified ring buffer
- */
-static void
-hv_get_ringbuffer_availbytes(const struct hv_ring_buffer_info *rbi,
- u32 *read, u32 *write)
-{
- u32 read_loc, write_loc, dsize;
-
- /* Capture the read/write indices before they changed */
- read_loc = READ_ONCE(rbi->ring_buffer->read_index);
- write_loc = READ_ONCE(rbi->ring_buffer->write_index);
- dsize = rbi->ring_datasize;
-
- *write = write_loc >= read_loc ? dsize - (write_loc - read_loc) :
- read_loc - write_loc;
- *read = dsize - *write;
-}
-
/* Get various debug metrics for the specified ring buffer. */
int hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info,
struct hv_ring_buffer_debug_info *debug_info)
{
- u32 bytes_avail_towrite;
- u32 bytes_avail_toread;
+ u32 read_index;
+ u32 write_index;
mutex_lock(&ring_info->ring_buffer_mutex);
@@ -160,13 +121,14 @@ int hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info,
return -EINVAL;
}
- hv_get_ringbuffer_availbytes(ring_info,
- &bytes_avail_toread,
- &bytes_avail_towrite);
- debug_info->bytes_avail_toread = bytes_avail_toread;
- debug_info->bytes_avail_towrite = bytes_avail_towrite;
- debug_info->current_read_index = ring_info->ring_buffer->read_index;
- debug_info->current_write_index = ring_info->ring_buffer->write_index;
+ read_index = READ_ONCE(ring_info->ring_buffer->read_index);
+ write_index = READ_ONCE(ring_info->ring_buffer->write_index);
+ debug_info->bytes_avail_toread =
+ hv_ringbuffer_avail_read(ring_info, read_index, write_index);
+ debug_info->bytes_avail_towrite =
+ hv_ringbuffer_avail_write(ring_info, read_index, write_index);
+ debug_info->current_read_index = read_index;
+ debug_info->current_write_index = write_index;
debug_info->current_interrupt_mask
= ring_info->ring_buffer->interrupt_mask;
mutex_unlock(&ring_info->ring_buffer_mutex);
@@ -281,6 +243,7 @@ int hv_ringbuffer_write(struct vmbus_channel *channel,
u32 totalbytes_towrite = sizeof(u64);
u32 next_write_location;
u32 old_write;
+ u32 read_index;
u64 prev_indices;
unsigned long flags;
struct hv_ring_buffer_info *outring_info = &channel->outbound;
@@ -295,7 +258,19 @@ int hv_ringbuffer_write(struct vmbus_channel *channel,
spin_lock_irqsave(&outring_info->ring_lock, flags);
- bytes_avail_towrite = hv_get_bytes_to_write(outring_info);
+ read_index = READ_ONCE(outring_info->ring_buffer->read_index);
+ old_write = READ_ONCE(outring_info->ring_buffer->write_index);
+ if (unlikely(hv_ringbuffer_indices_invalid(outring_info, read_index,
+ old_write))) {
+ spin_unlock_irqrestore(&outring_info->ring_lock, flags);
+ pr_err_ratelimited("outbound ring indices out of range: relid %u read %u write %u size %u\n",
+ channel->offermsg.child_relid, read_index,
+ old_write, outring_info->ring_datasize);
+ return -EIO;
+ }
+
+ bytes_avail_towrite = hv_ringbuffer_avail_write(outring_info, read_index,
+ old_write);
/*
* If there is only room for the packet, assume it is full.
@@ -317,9 +292,7 @@ int hv_ringbuffer_write(struct vmbus_channel *channel,
channel->out_full_flag = false;
/* Write to the ring buffer */
- next_write_location = hv_get_next_write_location(outring_info);
-
- old_write = next_write_location;
+ next_write_location = old_write;
for (i = 0; i < kv_count; i++) {
next_write_location = hv_copyto_ringbuffer(outring_info,
@@ -355,7 +328,7 @@ int hv_ringbuffer_write(struct vmbus_channel *channel,
*trans_id = __trans_id;
/* Set previous packet start */
- prev_indices = hv_get_ring_bufferindices(outring_info);
+ prev_indices = (u64)old_write << 32;
next_write_location = hv_copyto_ringbuffer(outring_info,
next_write_location,
@@ -435,8 +408,9 @@ int hv_ringbuffer_read(struct vmbus_channel *channel,
* This is similar to hv_get_bytes_to_read but with private
* read index instead.
*/
-static u32 hv_pkt_iter_avail(const struct hv_ring_buffer_info *rbi)
+static u32 hv_pkt_iter_avail(const struct vmbus_channel *channel)
{
+ const struct hv_ring_buffer_info *rbi = &channel->inbound;
u32 priv_read_loc = rbi->priv_read_index;
u32 write_loc;
@@ -448,11 +422,15 @@ static u32 hv_pkt_iter_avail(const struct hv_ring_buffer_info *rbi)
* stale data.
*/
write_loc = virt_load_acquire(&rbi->ring_buffer->write_index);
+ if (unlikely(hv_ringbuffer_indices_invalid(rbi, priv_read_loc,
+ write_loc))) {
+ pr_err_ratelimited("inbound write index out of range: relid %u write %u size %u\n",
+ channel->offermsg.child_relid, write_loc,
+ rbi->ring_datasize);
+ return 0;
+ }
- if (write_loc >= priv_read_loc)
- return write_loc - priv_read_loc;
- else
- return (rbi->ring_datasize - priv_read_loc) + write_loc;
+ return hv_ringbuffer_avail_read(rbi, priv_read_loc, write_loc);
}
/*
@@ -468,7 +446,7 @@ struct vmpacket_descriptor *hv_pkt_iter_first(struct vmbus_channel *channel)
hv_debug_delay_test(channel, MESSAGE_DELAY);
- bytes_avail = hv_pkt_iter_avail(rbi);
+ bytes_avail = hv_pkt_iter_avail(channel);
if (bytes_avail < sizeof(struct vmpacket_descriptor))
return NULL;
bytes_avail = min(rbi->pkt_buffer_size, bytes_avail);
@@ -572,6 +550,7 @@ void hv_pkt_iter_close(struct vmbus_channel *channel)
{
struct hv_ring_buffer_info *rbi = &channel->inbound;
u32 curr_write_sz, pending_sz, bytes_read, start_read_index;
+ u32 write_index;
/*
* Make sure all reads are done before we update the read index since
@@ -579,16 +558,8 @@ void hv_pkt_iter_close(struct vmbus_channel *channel)
* is updated.
*/
virt_rmb();
- start_read_index = rbi->ring_buffer->read_index;
- rbi->ring_buffer->read_index = rbi->priv_read_index;
-
- /*
- * Older versions of Hyper-V (before WS2102 and Win8) do not
- * implement pending_send_sz and simply poll if the host->guest
- * ring buffer is full. No signaling is needed or expected.
- */
- if (!rbi->ring_buffer->feature_bits.feat_pending_send_sz)
- return;
+ start_read_index = READ_ONCE(rbi->ring_buffer->read_index);
+ WRITE_ONCE(rbi->ring_buffer->read_index, rbi->priv_read_index);
/*
* Issue a full memory barrier before making the signaling decision.
@@ -611,11 +582,17 @@ void hv_pkt_iter_close(struct vmbus_channel *channel)
return;
/*
- * Ensure the read of write_index in hv_get_bytes_to_write()
- * happens after the read of pending_send_sz.
+ * Ensure the read of write_index happens after the read of
+ * pending_send_sz.
*/
virt_rmb();
- curr_write_sz = hv_get_bytes_to_write(rbi);
+ write_index = READ_ONCE(rbi->ring_buffer->write_index);
+ if (unlikely(hv_ringbuffer_indices_invalid(rbi, start_read_index,
+ write_index)))
+ return;
+
+ curr_write_sz = hv_ringbuffer_avail_write(rbi, rbi->priv_read_index,
+ write_index);
bytes_read = hv_pkt_iter_bytes_read(rbi, start_read_index);
/*
@@ -631,8 +608,7 @@ void hv_pkt_iter_close(struct vmbus_channel *channel)
* Exactly filling the ring buffer is treated as "not enough
* space". The ring buffer always must have at least one byte
* empty so the empty and full conditions are distinguishable.
- * hv_get_bytes_to_write() doesn't fully tell the truth in
- * this regard.
+ * curr_write_sz doesn't fully tell the truth in this regard.
*
* So first check if we were in the "enough free space" state
* before we began the iteration. If so, the host was not
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
index 4692687cdbf3..c8330fffb138 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
@@ -33,6 +33,7 @@
#include <linux/sysfb.h>
#include <linux/efi.h>
#include <linux/kernel.h>
+#include <linux/kexec.h>
#include <linux/syscore_ops.h>
#include <linux/dma-map-ops.h>
#include <linux/pci.h>
@@ -2571,10 +2572,14 @@ EXPORT_SYMBOL_GPL(vmbus_free_mmio);
#ifdef CONFIG_ACPI
static int vmbus_acpi_add(struct platform_device *pdev)
{
+ struct acpi_device *ancestor;
+ struct acpi_device *device;
acpi_status result;
int ret_val = -ENODEV;
- struct acpi_device *ancestor;
- struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
+
+ device = ACPI_COMPANION(&pdev->dev);
+ if (!device)
+ return -ENODEV;
vmbus_root_device = &device->dev;
@@ -2904,6 +2909,12 @@ static void hv_kexec_handler(void)
cpuhp_remove_state(hyperv_cpuhp_online);
};
+static void hv_vmbus_syscore_shutdown(void *data)
+{
+ if (kexec_is_in_progress())
+ hv_kexec_handler();
+}
+
static void hv_crash_handler(struct pt_regs *regs)
{
int cpu;
@@ -2954,14 +2965,15 @@ static void hv_synic_resume(void *data)
*/
}
-/* The callbacks run only on CPU0, with irqs_disabled. */
-static const struct syscore_ops hv_synic_syscore_ops = {
+/* The suspend and resume callbacks run only on CPU0, with irqs_disabled. */
+static const struct syscore_ops hv_vmbus_syscore_ops = {
.suspend = hv_synic_suspend,
.resume = hv_synic_resume,
+ .shutdown = hv_vmbus_syscore_shutdown,
};
-static struct syscore hv_synic_syscore = {
- .ops = &hv_synic_syscore_ops,
+static struct syscore hv_vmbus_syscore = {
+ .ops = &hv_vmbus_syscore_ops,
};
static int __init hv_acpi_init(void)
@@ -3010,10 +3022,9 @@ static int __init hv_acpi_init(void)
if (ret)
goto cleanup;
- hv_setup_kexec_handler(hv_kexec_handler);
hv_setup_crash_handler(hv_crash_handler);
- register_syscore(&hv_synic_syscore);
+ register_syscore(&hv_vmbus_syscore);
return 0;
@@ -3035,9 +3046,8 @@ static void __exit vmbus_exit(void)
*/
return;
- unregister_syscore(&hv_synic_syscore);
+ unregister_syscore(&hv_vmbus_syscore);
- hv_remove_kexec_handler();
hv_remove_crash_handler();
hv_stimer_global_cleanup();
vmbus_disconnect();
diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h
index 34d4c332449c..4c9f3353074a 100644
--- a/include/asm-generic/mshyperv.h
+++ b/include/asm-generic/mshyperv.h
@@ -187,8 +187,6 @@ void hv_remove_vmbus_handler(void);
void hv_setup_stimer0_handler(void (*handler)(void));
void hv_remove_stimer0_handler(void);
-void hv_setup_kexec_handler(void (*handler)(void));
-void hv_remove_kexec_handler(void);
void hv_setup_crash_handler(void (*handler)(struct pt_regs *regs));
void hv_remove_crash_handler(void);
void hv_setup_mshv_handler(void (*handler)(void));
@@ -349,7 +347,6 @@ static inline bool hv_parent_partition(void)
bool hv_result_needs_memory(u64 status);
int hv_deposit_memory_node(int node, u64 partition_id, u64 status);
-int hv_call_deposit_pages(int node, u64 partition_id, u32 num_pages);
int hv_call_add_logical_proc(int node, u32 lp_index, u32 acpi_id);
int hv_call_notify_all_processors_started(void);
bool hv_lp_exists(u32 lp_index);
@@ -364,10 +361,6 @@ static inline int hv_deposit_memory_node(int node, u64 partition_id, u64 status)
{
return -EOPNOTSUPP;
}
-static inline int hv_call_deposit_pages(int node, u64 partition_id, u32 num_pages)
-{
- return -EOPNOTSUPP;
-}
static inline int hv_call_add_logical_proc(int node, u32 lp_index, u32 acpi_id)
{
return -EOPNOTSUPP;
diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
index e61f9a4cb7c3..e85a1a043eac 100644
--- a/include/linux/hyperv.h
+++ b/include/linux/hyperv.h
@@ -209,31 +209,63 @@ struct hv_ring_buffer_info {
};
+/*
+ * The indices live in memory shared with the untrusted host, so check a
+ * snapshot of them before using it as an offset or to compute a byte count.
+ */
+static inline bool
+hv_ringbuffer_indices_invalid(const struct hv_ring_buffer_info *rbi,
+ u32 read_loc, u32 write_loc)
+{
+ return read_loc >= rbi->ring_datasize ||
+ write_loc >= rbi->ring_datasize;
+}
+
+/*
+ * Byte counts for a caller-supplied snapshot of the indices, so that the
+ * counts and the indices the caller goes on to use describe one state of the
+ * ring.
+ */
+static inline u32
+hv_ringbuffer_avail_write(const struct hv_ring_buffer_info *rbi,
+ u32 read_loc, u32 write_loc)
+{
+ u32 dsize = rbi->ring_datasize;
+
+ return write_loc >= read_loc ? dsize - (write_loc - read_loc) :
+ read_loc - write_loc;
+}
+
+static inline u32
+hv_ringbuffer_avail_read(const struct hv_ring_buffer_info *rbi,
+ u32 read_loc, u32 write_loc)
+{
+ return rbi->ring_datasize -
+ hv_ringbuffer_avail_write(rbi, read_loc, write_loc);
+}
+
static inline u32 hv_get_bytes_to_read(const struct hv_ring_buffer_info *rbi)
{
- u32 read_loc, write_loc, dsize, read;
+ u32 read_loc, write_loc;
- dsize = rbi->ring_datasize;
- read_loc = rbi->ring_buffer->read_index;
+ read_loc = READ_ONCE(rbi->ring_buffer->read_index);
write_loc = READ_ONCE(rbi->ring_buffer->write_index);
+ if (unlikely(hv_ringbuffer_indices_invalid(rbi, read_loc, write_loc)))
+ return 0;
- read = write_loc >= read_loc ? (write_loc - read_loc) :
- (dsize - read_loc) + write_loc;
-
- return read;
+ return hv_ringbuffer_avail_read(rbi, read_loc, write_loc);
}
static inline u32 hv_get_bytes_to_write(const struct hv_ring_buffer_info *rbi)
{
- u32 read_loc, write_loc, dsize, write;
+ u32 read_loc, write_loc;
- dsize = rbi->ring_datasize;
read_loc = READ_ONCE(rbi->ring_buffer->read_index);
- write_loc = rbi->ring_buffer->write_index;
+ write_loc = READ_ONCE(rbi->ring_buffer->write_index);
+ if (unlikely(hv_ringbuffer_indices_invalid(rbi, read_loc, write_loc)))
+ return 0;
- write = write_loc >= read_loc ? dsize - (write_loc - read_loc) :
- read_loc - write_loc;
- return write;
+ return hv_ringbuffer_avail_write(rbi, read_loc, write_loc);
}
static inline u32 hv_get_avail_to_write_percent(
diff --git a/include/linux/kexec.h b/include/linux/kexec.h
index f970ca2c8ce9..bac11e940e74 100644
--- a/include/linux/kexec.h
+++ b/include/linux/kexec.h
@@ -453,6 +453,7 @@ bool kexec_load_permitted(int kexec_image_type);
/* flag to track if kexec reboot is in progress */
extern bool kexec_in_progress;
+bool kexec_is_in_progress(void);
#ifndef page_to_boot_pfn
static inline unsigned long page_to_boot_pfn(struct page *page)
@@ -529,6 +530,7 @@ static inline void *kimage_map_segment(struct kimage *image, int idx)
{ return NULL; }
static inline void kimage_unmap_segment(void *buffer) { }
#define kexec_in_progress false
+static inline bool kexec_is_in_progress(void) { return false; }
#endif /* CONFIG_KEXEC_CORE */
#ifdef CONFIG_KEXEC_SIG
diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c
index 7ee8c9f078f6..f4a31565c447 100644
--- a/kernel/kexec_core.c
+++ b/kernel/kexec_core.c
@@ -23,6 +23,7 @@
#include <linux/hardirq.h>
#include <linux/elf.h>
#include <linux/elfcore.h>
+#include <linux/export.h>
#include <linux/utsname.h>
#include <linux/numa.h>
#include <linux/suspend.h>
@@ -54,6 +55,12 @@ atomic_t __kexec_lock = ATOMIC_INIT(0);
/* Flag to indicate we are going to kexec a new kernel */
bool kexec_in_progress = false;
+bool kexec_is_in_progress(void)
+{
+ return kexec_in_progress;
+}
+EXPORT_SYMBOL_GPL(kexec_is_in_progress);
+
bool kexec_file_dbg_print;
/*