mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
hyperv-next for v7.4-rc1
-----BEGIN PGP SIGNATURE----- iQFHBAABCgAxFiEEIbPD0id6easf0xsudhRwX5BBoF4FAmri3mgTHHdlaS5saXVA a2VybmVsLm9yZwAKCRB2FHBfkEGgXpRSB/49w0SPLd5RYa+fdq3qdvwr6+UJbkxp +F0n4keD03UCEcKljMsUBKdhD3qJnj1IB0/NR34Pr7YR4Yhv0YxodQK40nWBCFLd nsc99jw+d7TbepxsVwGqiwIzaWtidJwLw7DoBbagGUYFAFVkxlj6W+7S2e6hKcJH pDx92x5myMpUyEKuA8DNTWyMLNk9iysPxvQq/WSjY36roxkdvA5pwDhzi3hLoZw4 kfLuT+yKtKGwL+1dhn4gz0aYsuIq1wu36GlPMRfDU+cl96KGHEnUW861JrnqHpaU ccTRX7O2yKUSjPaYZZaqPa1ZW8R5L9DEpQxJA1Ilaw4Z9rXaQUmqC3OR =A5Bb -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
9c7e3d5910
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
struct page *page;
|
||||
int num_allocd, count = 0;
|
||||
|
||||
if (num_pages > HV_DEPOSIT_MAX)
|
||||
return -E2BIG;
|
||||
if (!num_pages)
|
||||
return 0;
|
||||
/* Published ABI, enforce its immutability. */
|
||||
BUILD_BUG_ON(HV_DEPOSIT_INP_MAX != 511);
|
||||
|
||||
/* One buffer for page pointers and counts */
|
||||
page = alloc_page(GFP_KERNEL);
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
pages = page_address(page);
|
||||
|
||||
counts = kzalloc_objs(int, HV_DEPOSIT_MAX);
|
||||
if (!counts) {
|
||||
free_page((unsigned long)pages);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
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;
|
||||
if (num_allocd-- && count == HV_DEPOSIT_INP_MAX) {
|
||||
*lastpfnp = page_to_pfn(page);
|
||||
count++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
goto free_buf;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
free_buf:
|
||||
free_page((unsigned long)pages);
|
||||
kfree(counts);
|
||||
return ret;
|
||||
return count ? count : -ENOMEM;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hv_call_deposit_pages);
|
||||
|
||||
int hv_deposit_memory_node(int node, u64 partition_id,
|
||||
u64 hv_status)
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
u32 num_pages = 1;
|
||||
struct hv_deposit_memory *hc_input;
|
||||
int i, rc, num_pages;
|
||||
u64 status, *pfna, lastpfn = 0;
|
||||
bool trunc_extra = 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;
|
||||
BUILD_BUG_ON(HV_MAX_CONTIGUOUS_ALLOCATION_PAGES > HV_DEPOSIT_MAX);
|
||||
|
||||
case HV_STATUS_INSUFFICIENT_CONTIGUOUS_ROOT_MEMORY:
|
||||
num_pages = HV_MAX_CONTIGUOUS_ALLOCATION_PAGES;
|
||||
fallthrough;
|
||||
case HV_STATUS_INSUFFICIENT_ROOT_MEMORY:
|
||||
if (!hv_root_partition()) {
|
||||
hv_status_err(hv_status, "Unexpected root memory deposit\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
partition_id = HV_PARTITION_ID_SELF;
|
||||
break;
|
||||
|
||||
default:
|
||||
hv_status_err(hv_status, "Unexpected!\n");
|
||||
return -ENOMEM;
|
||||
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;
|
||||
}
|
||||
return hv_call_deposit_pages(node, partition_id, num_pages);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
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_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;
|
||||
}
|
||||
|
||||
int hv_deposit_memory_node(int node, u64 pt_id, u64 hv_status)
|
||||
{
|
||||
int result = hv_result(hv_status);
|
||||
bool contiguous = false;
|
||||
|
||||
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 -EINVAL;
|
||||
}
|
||||
|
||||
pt_id = HV_PARTITION_ID_SELF;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
/*
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user