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:
Linus Torvalds 2026-10-29 12:39:45 -10:00
commit 9c7e3d5910
16 changed files with 363 additions and 301 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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))
{
}

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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();

View File

@ -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;

View File

@ -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(

View File

@ -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

View File

@ -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;
/*