Commit 0dd0c8f7 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'hyperv-next-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux

Pull Hyper-V updates from Sasha Levin:

 - support for new VMBus protocols (Andrea Parri)

 - hibernation support (Dexuan Cui)

 - latency testing framework (Branden Bonaby)

 - decoupling Hyper-V page size from guest page size (Himadri Pandya)

* tag 'hyperv-next-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux: (22 commits)
  Drivers: hv: vmbus: Fix crash handler reset of Hyper-V synic
  drivers/hv: Replace binary semaphore with mutex
  drivers: iommu: hyperv: Make HYPERV_IOMMU only available on x86
  HID: hyperv: Add the support of hibernation
  hv_balloon: Add the support of hibernation
  x86/hyperv: Implement hv_is_hibernation_supported()
  Drivers: hv: balloon: Remove dependencies on guest page size
  Drivers: hv: vmbus: Remove dependencies on guest page size
  x86: hv: Add function to allocate zeroed page for Hyper-V
  Drivers: hv: util: Specify ring buffer size using Hyper-V page size
  Drivers: hv: Specify receive buffer size using Hyper-V page size
  tools: hv: add vmbus testing tool
  drivers: hv: vmbus: Introduce latency testing
  video: hyperv: hyperv_fb: Support deferred IO for Hyper-V frame buffer driver
  video: hyperv: hyperv_fb: Obtain screen resolution from Hyper-V host
  hv_netvsc: Add the support of hibernation
  hv_sock: Add the support of hibernation
  video: hyperv_fb: Add the support of hibernation
  scsi: storvsc: Add the support of hibernation
  Drivers: hv: vmbus: Add module parameter to cap the VMBus version
  ...
parents 8fa91bfa 7a1323b5
What: /sys/kernel/debug/hyperv/<UUID>/fuzz_test_state
Date: October 2019
KernelVersion: 5.5
Contact: Branden Bonaby <brandonbonaby94@gmail.com>
Description: Fuzz testing status of a vmbus device, whether its in an ON
state or a OFF state
Users: Debugging tools
What: /sys/kernel/debug/hyperv/<UUID>/delay/fuzz_test_buffer_interrupt_delay
Date: October 2019
KernelVersion: 5.5
Contact: Branden Bonaby <brandonbonaby94@gmail.com>
Description: Fuzz testing buffer interrupt delay value between 0 - 1000
microseconds (inclusive).
Users: Debugging tools
What: /sys/kernel/debug/hyperv/<UUID>/delay/fuzz_test_message_delay
Date: October 2019
KernelVersion: 5.5
Contact: Branden Bonaby <brandonbonaby94@gmail.com>
Description: Fuzz testing message delay value between 0 - 1000 microseconds
(inclusive).
Users: Debugging tools
...@@ -7654,6 +7654,7 @@ F: include/uapi/linux/hyperv.h ...@@ -7654,6 +7654,7 @@ F: include/uapi/linux/hyperv.h
F: include/asm-generic/mshyperv.h F: include/asm-generic/mshyperv.h
F: tools/hv/ F: tools/hv/
F: Documentation/ABI/stable/sysfs-bus-vmbus F: Documentation/ABI/stable/sysfs-bus-vmbus
F: Documentation/ABI/testing/debugfs-hyperv
HYPERBUS SUPPORT HYPERBUS SUPPORT
M: Vignesh Raghavendra <vigneshr@ti.com> M: Vignesh Raghavendra <vigneshr@ti.com>
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
* Author : K. Y. Srinivasan <kys@microsoft.com> * Author : K. Y. Srinivasan <kys@microsoft.com>
*/ */
#include <linux/acpi.h>
#include <linux/efi.h> #include <linux/efi.h>
#include <linux/types.h> #include <linux/types.h>
#include <asm/apic.h> #include <asm/apic.h>
...@@ -45,6 +46,14 @@ void *hv_alloc_hyperv_page(void) ...@@ -45,6 +46,14 @@ void *hv_alloc_hyperv_page(void)
} }
EXPORT_SYMBOL_GPL(hv_alloc_hyperv_page); EXPORT_SYMBOL_GPL(hv_alloc_hyperv_page);
void *hv_alloc_hyperv_zeroed_page(void)
{
BUILD_BUG_ON(PAGE_SIZE != HV_HYP_PAGE_SIZE);
return (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
}
EXPORT_SYMBOL_GPL(hv_alloc_hyperv_zeroed_page);
void hv_free_hyperv_page(unsigned long addr) void hv_free_hyperv_page(unsigned long addr)
{ {
free_page(addr); free_page(addr);
...@@ -437,3 +446,9 @@ bool hv_is_hyperv_initialized(void) ...@@ -437,3 +446,9 @@ bool hv_is_hyperv_initialized(void)
return hypercall_msr.enable; return hypercall_msr.enable;
} }
EXPORT_SYMBOL_GPL(hv_is_hyperv_initialized); EXPORT_SYMBOL_GPL(hv_is_hyperv_initialized);
bool hv_is_hibernation_supported(void)
{
return acpi_sleep_state_supported(ACPI_STATE_S4);
}
EXPORT_SYMBOL_GPL(hv_is_hibernation_supported);
...@@ -219,6 +219,7 @@ static inline struct hv_vp_assist_page *hv_get_vp_assist_page(unsigned int cpu) ...@@ -219,6 +219,7 @@ static inline struct hv_vp_assist_page *hv_get_vp_assist_page(unsigned int cpu)
void __init hyperv_init(void); void __init hyperv_init(void);
void hyperv_setup_mmu_ops(void); void hyperv_setup_mmu_ops(void);
void *hv_alloc_hyperv_page(void); void *hv_alloc_hyperv_page(void);
void *hv_alloc_hyperv_zeroed_page(void);
void hv_free_hyperv_page(unsigned long addr); void hv_free_hyperv_page(unsigned long addr);
void hyperv_reenlightenment_intr(struct pt_regs *regs); void hyperv_reenlightenment_intr(struct pt_regs *regs);
void set_hv_tscchange_cb(void (*cb)(void)); void set_hv_tscchange_cb(void (*cb)(void));
......
...@@ -192,6 +192,9 @@ static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device, ...@@ -192,6 +192,9 @@ static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device,
if (desc->bLength == 0) if (desc->bLength == 0)
goto cleanup; goto cleanup;
/* The pointer is not NULL when we resume from hibernation */
if (input_device->hid_desc != NULL)
kfree(input_device->hid_desc);
input_device->hid_desc = kmemdup(desc, desc->bLength, GFP_ATOMIC); input_device->hid_desc = kmemdup(desc, desc->bLength, GFP_ATOMIC);
if (!input_device->hid_desc) if (!input_device->hid_desc)
...@@ -203,6 +206,9 @@ static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device, ...@@ -203,6 +206,9 @@ static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device,
goto cleanup; goto cleanup;
} }
/* The pointer is not NULL when we resume from hibernation */
if (input_device->report_desc != NULL)
kfree(input_device->report_desc);
input_device->report_desc = kzalloc(input_device->report_desc_size, input_device->report_desc = kzalloc(input_device->report_desc_size,
GFP_ATOMIC); GFP_ATOMIC);
...@@ -342,6 +348,8 @@ static int mousevsc_connect_to_vsp(struct hv_device *device) ...@@ -342,6 +348,8 @@ static int mousevsc_connect_to_vsp(struct hv_device *device)
struct mousevsc_prt_msg *request; struct mousevsc_prt_msg *request;
struct mousevsc_prt_msg *response; struct mousevsc_prt_msg *response;
reinit_completion(&input_dev->wait_event);
request = &input_dev->protocol_req; request = &input_dev->protocol_req;
memset(request, 0, sizeof(struct mousevsc_prt_msg)); memset(request, 0, sizeof(struct mousevsc_prt_msg));
...@@ -541,6 +549,30 @@ static int mousevsc_remove(struct hv_device *dev) ...@@ -541,6 +549,30 @@ static int mousevsc_remove(struct hv_device *dev)
return 0; return 0;
} }
static int mousevsc_suspend(struct hv_device *dev)
{
vmbus_close(dev->channel);
return 0;
}
static int mousevsc_resume(struct hv_device *dev)
{
int ret;
ret = vmbus_open(dev->channel,
INPUTVSC_SEND_RING_BUFFER_SIZE,
INPUTVSC_RECV_RING_BUFFER_SIZE,
NULL, 0,
mousevsc_on_channel_callback,
dev);
if (ret)
return ret;
ret = mousevsc_connect_to_vsp(dev);
return ret;
}
static const struct hv_vmbus_device_id id_table[] = { static const struct hv_vmbus_device_id id_table[] = {
/* Mouse guid */ /* Mouse guid */
{ HV_MOUSE_GUID, }, { HV_MOUSE_GUID, },
...@@ -554,6 +586,8 @@ static struct hv_driver mousevsc_drv = { ...@@ -554,6 +586,8 @@ static struct hv_driver mousevsc_drv = {
.id_table = id_table, .id_table = id_table,
.probe = mousevsc_probe, .probe = mousevsc_probe,
.remove = mousevsc_remove, .remove = mousevsc_remove,
.suspend = mousevsc_suspend,
.resume = mousevsc_resume,
.driver = { .driver = {
.probe_type = PROBE_PREFER_ASYNCHRONOUS, .probe_type = PROBE_PREFER_ASYNCHRONOUS,
}, },
......
...@@ -9,4 +9,5 @@ CFLAGS_hv_balloon.o = -I$(src) ...@@ -9,4 +9,5 @@ CFLAGS_hv_balloon.o = -I$(src)
hv_vmbus-y := vmbus_drv.o \ hv_vmbus-y := vmbus_drv.o \
hv.o connection.o channel.o \ hv.o connection.o channel.o \
channel_mgmt.o ring_buffer.o hv_trace.o channel_mgmt.o ring_buffer.o hv_trace.o
hv_vmbus-$(CONFIG_HYPERV_TESTING) += hv_debugfs.o
hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o hv_fcopy.o hv_utils_transport.o hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o hv_fcopy.o hv_utils_transport.o
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/hyperv.h> #include <linux/hyperv.h>
...@@ -40,29 +41,30 @@ EXPORT_SYMBOL_GPL(vmbus_connection); ...@@ -40,29 +41,30 @@ EXPORT_SYMBOL_GPL(vmbus_connection);
__u32 vmbus_proto_version; __u32 vmbus_proto_version;
EXPORT_SYMBOL_GPL(vmbus_proto_version); EXPORT_SYMBOL_GPL(vmbus_proto_version);
static __u32 vmbus_get_next_version(__u32 current_version) /*
{ * Table of VMBus versions listed from newest to oldest.
switch (current_version) { */
case (VERSION_WIN7): static __u32 vmbus_versions[] = {
return VERSION_WS2008; VERSION_WIN10_V5_2,
VERSION_WIN10_V5_1,
case (VERSION_WIN8): VERSION_WIN10_V5,
return VERSION_WIN7; VERSION_WIN10_V4_1,
VERSION_WIN10,
case (VERSION_WIN8_1): VERSION_WIN8_1,
return VERSION_WIN8; VERSION_WIN8,
VERSION_WIN7,
VERSION_WS2008
};
case (VERSION_WIN10): /*
return VERSION_WIN8_1; * Maximal VMBus protocol version guests can negotiate. Useful to cap the
* VMBus version for testing and debugging purpose.
*/
static uint max_version = VERSION_WIN10_V5_2;
case (VERSION_WIN10_V5): module_param(max_version, uint, S_IRUGO);
return VERSION_WIN10; MODULE_PARM_DESC(max_version,
"Maximal VMBus protocol version which can be negotiated");
case (VERSION_WS2008):
default:
return VERSION_INVAL;
}
}
int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version) int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
{ {
...@@ -80,12 +82,12 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version) ...@@ -80,12 +82,12 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
msg->vmbus_version_requested = version; msg->vmbus_version_requested = version;
/* /*
* VMBus protocol 5.0 (VERSION_WIN10_V5) requires that we must use * VMBus protocol 5.0 (VERSION_WIN10_V5) and higher require that we must
* VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate Contact Message, * use VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate Contact Message,
* and for subsequent messages, we must use the Message Connection ID * and for subsequent messages, we must use the Message Connection ID
* field in the host-returned Version Response Message. And, with * field in the host-returned Version Response Message. And, with
* VERSION_WIN10_V5, we don't use msg->interrupt_page, but we tell * VERSION_WIN10_V5 and higher, we don't use msg->interrupt_page, but we
* the host explicitly that we still use VMBUS_MESSAGE_SINT(2) for * tell the host explicitly that we still use VMBUS_MESSAGE_SINT(2) for
* compatibility. * compatibility.
* *
* On old hosts, we should always use VMBUS_MESSAGE_CONNECTION_ID (1). * On old hosts, we should always use VMBUS_MESSAGE_CONNECTION_ID (1).
...@@ -169,8 +171,8 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version) ...@@ -169,8 +171,8 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
*/ */
int vmbus_connect(void) int vmbus_connect(void)
{ {
int ret = 0;
struct vmbus_channel_msginfo *msginfo = NULL; struct vmbus_channel_msginfo *msginfo = NULL;
int i, ret = 0;
__u32 version; __u32 version;
/* Initialize the vmbus connection */ /* Initialize the vmbus connection */
...@@ -206,7 +208,7 @@ int vmbus_connect(void) ...@@ -206,7 +208,7 @@ int vmbus_connect(void)
* abstraction stuff * abstraction stuff
*/ */
vmbus_connection.int_page = vmbus_connection.int_page =
(void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, 0); (void *)hv_alloc_hyperv_zeroed_page();
if (vmbus_connection.int_page == NULL) { if (vmbus_connection.int_page == NULL) {
ret = -ENOMEM; ret = -ENOMEM;
goto cleanup; goto cleanup;
...@@ -215,14 +217,14 @@ int vmbus_connect(void) ...@@ -215,14 +217,14 @@ int vmbus_connect(void)
vmbus_connection.recv_int_page = vmbus_connection.int_page; vmbus_connection.recv_int_page = vmbus_connection.int_page;
vmbus_connection.send_int_page = vmbus_connection.send_int_page =
(void *)((unsigned long)vmbus_connection.int_page + (void *)((unsigned long)vmbus_connection.int_page +
(PAGE_SIZE >> 1)); (HV_HYP_PAGE_SIZE >> 1));
/* /*
* Setup the monitor notification facility. The 1st page for * Setup the monitor notification facility. The 1st page for
* parent->child and the 2nd page for child->parent * parent->child and the 2nd page for child->parent
*/ */
vmbus_connection.monitor_pages[0] = (void *)__get_free_pages((GFP_KERNEL|__GFP_ZERO), 0); vmbus_connection.monitor_pages[0] = (void *)hv_alloc_hyperv_zeroed_page();
vmbus_connection.monitor_pages[1] = (void *)__get_free_pages((GFP_KERNEL|__GFP_ZERO), 0); vmbus_connection.monitor_pages[1] = (void *)hv_alloc_hyperv_zeroed_page();
if ((vmbus_connection.monitor_pages[0] == NULL) || if ((vmbus_connection.monitor_pages[0] == NULL) ||
(vmbus_connection.monitor_pages[1] == NULL)) { (vmbus_connection.monitor_pages[1] == NULL)) {
ret = -ENOMEM; ret = -ENOMEM;
...@@ -244,21 +246,21 @@ int vmbus_connect(void) ...@@ -244,21 +246,21 @@ int vmbus_connect(void)
* version. * version.
*/ */
version = VERSION_CURRENT; for (i = 0; ; i++) {
if (i == ARRAY_SIZE(vmbus_versions))
goto cleanup;
version = vmbus_versions[i];
if (version > max_version)
continue;
do {
ret = vmbus_negotiate_version(msginfo, version); ret = vmbus_negotiate_version(msginfo, version);
if (ret == -ETIMEDOUT) if (ret == -ETIMEDOUT)
goto cleanup; goto cleanup;
if (vmbus_connection.conn_state == CONNECTED) if (vmbus_connection.conn_state == CONNECTED)
break; break;
}
version = vmbus_get_next_version(version);
} while (version != VERSION_INVAL);
if (version == VERSION_INVAL)
goto cleanup;
vmbus_proto_version = version; vmbus_proto_version = version;
pr_info("Vmbus version:%d.%d\n", pr_info("Vmbus version:%d.%d\n",
...@@ -295,12 +297,12 @@ void vmbus_disconnect(void) ...@@ -295,12 +297,12 @@ void vmbus_disconnect(void)
destroy_workqueue(vmbus_connection.work_queue); destroy_workqueue(vmbus_connection.work_queue);
if (vmbus_connection.int_page) { if (vmbus_connection.int_page) {
free_pages((unsigned long)vmbus_connection.int_page, 0); hv_free_hyperv_page((unsigned long)vmbus_connection.int_page);
vmbus_connection.int_page = NULL; vmbus_connection.int_page = NULL;
} }
free_pages((unsigned long)vmbus_connection.monitor_pages[0], 0); hv_free_hyperv_page((unsigned long)vmbus_connection.monitor_pages[0]);
free_pages((unsigned long)vmbus_connection.monitor_pages[1], 0); hv_free_hyperv_page((unsigned long)vmbus_connection.monitor_pages[1]);
vmbus_connection.monitor_pages[0] = NULL; vmbus_connection.monitor_pages[0] = NULL;
vmbus_connection.monitor_pages[1] = NULL; vmbus_connection.monitor_pages[1] = NULL;
} }
...@@ -361,6 +363,7 @@ void vmbus_on_event(unsigned long data) ...@@ -361,6 +363,7 @@ void vmbus_on_event(unsigned long data)
trace_vmbus_on_event(channel); trace_vmbus_on_event(channel);
hv_debug_delay_test(channel, INTERRUPT_DELAY);
do { do {
void (*callback_fn)(void *); void (*callback_fn)(void *);
...@@ -413,7 +416,7 @@ int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep) ...@@ -413,7 +416,7 @@ int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep)
case HV_STATUS_INVALID_CONNECTION_ID: case HV_STATUS_INVALID_CONNECTION_ID:
/* /*
* See vmbus_negotiate_version(): VMBus protocol 5.0 * See vmbus_negotiate_version(): VMBus protocol 5.0
* requires that we must use * and higher require that we must use
* VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate * VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate
* Contact message, but on old hosts that only * Contact message, but on old hosts that only
* support VMBus protocol 4.0 or lower, here we get * support VMBus protocol 4.0 or lower, here we get
......
...@@ -23,6 +23,9 @@ ...@@ -23,6 +23,9 @@
#include <linux/percpu_counter.h> #include <linux/percpu_counter.h>
#include <linux/hyperv.h> #include <linux/hyperv.h>
#include <asm/hyperv-tlfs.h>
#include <asm/mshyperv.h>
#define CREATE_TRACE_POINTS #define CREATE_TRACE_POINTS
#include "hv_trace_balloon.h" #include "hv_trace_balloon.h"
...@@ -341,8 +344,6 @@ struct dm_unballoon_response { ...@@ -341,8 +344,6 @@ struct dm_unballoon_response {
* *
* mem_range: Memory range to hot add. * mem_range: Memory range to hot add.
* *
* On Linux we currently don't support this since we cannot hot add
* arbitrary granularity of memory.
*/ */
struct dm_hot_add { struct dm_hot_add {
...@@ -457,6 +458,7 @@ struct hot_add_wrk { ...@@ -457,6 +458,7 @@ struct hot_add_wrk {
struct work_struct wrk; struct work_struct wrk;
}; };
static bool allow_hibernation;
static bool hot_add = true; static bool hot_add = true;
static bool do_hot_add; static bool do_hot_add;
/* /*
...@@ -477,7 +479,7 @@ module_param(pressure_report_delay, uint, (S_IRUGO | S_IWUSR)); ...@@ -477,7 +479,7 @@ module_param(pressure_report_delay, uint, (S_IRUGO | S_IWUSR));
MODULE_PARM_DESC(pressure_report_delay, "Delay in secs in reporting pressure"); MODULE_PARM_DESC(pressure_report_delay, "Delay in secs in reporting pressure");
static atomic_t trans_id = ATOMIC_INIT(0); static atomic_t trans_id = ATOMIC_INIT(0);
static int dm_ring_size = (5 * PAGE_SIZE); static int dm_ring_size = 20 * 1024;
/* /*
* Driver specific state. * Driver specific state.
...@@ -493,10 +495,10 @@ enum hv_dm_state { ...@@ -493,10 +495,10 @@ enum hv_dm_state {
}; };
static __u8 recv_buffer[PAGE_SIZE]; static __u8 recv_buffer[HV_HYP_PAGE_SIZE];
static __u8 balloon_up_send_buffer[PAGE_SIZE]; static __u8 balloon_up_send_buffer[HV_HYP_PAGE_SIZE];
#define PAGES_IN_2M 512 #define PAGES_IN_2M (2 * 1024 * 1024 / PAGE_SIZE)
#define HA_CHUNK (32 * 1024) #define HA_CHUNK (128 * 1024 * 1024 / PAGE_SIZE)
struct hv_dynmem_device { struct hv_dynmem_device {
struct hv_device *dev; struct hv_device *dev;
...@@ -1053,8 +1055,12 @@ static void hot_add_req(struct work_struct *dummy) ...@@ -1053,8 +1055,12 @@ static void hot_add_req(struct work_struct *dummy)
else else
resp.result = 0; resp.result = 0;
if (!do_hot_add || (resp.page_count == 0)) if (!do_hot_add || resp.page_count == 0) {
pr_err("Memory hot add failed\n"); if (!allow_hibernation)
pr_err("Memory hot add failed\n");
else
pr_info("Ignore hot-add request!\n");
}
dm->state = DM_INITIALIZED; dm->state = DM_INITIALIZED;
resp.hdr.trans_id = atomic_inc_return(&trans_id); resp.hdr.trans_id = atomic_inc_return(&trans_id);
...@@ -1076,7 +1082,7 @@ static void process_info(struct hv_dynmem_device *dm, struct dm_info_msg *msg) ...@@ -1076,7 +1082,7 @@ static void process_info(struct hv_dynmem_device *dm, struct dm_info_msg *msg)
__u64 *max_page_count = (__u64 *)&info_hdr[1]; __u64 *max_page_count = (__u64 *)&info_hdr[1];
pr_info("Max. dynamic memory size: %llu MB\n", pr_info("Max. dynamic memory size: %llu MB\n",
(*max_page_count) >> (20 - PAGE_SHIFT)); (*max_page_count) >> (20 - HV_HYP_PAGE_SHIFT));
} }
break; break;
...@@ -1218,7 +1224,7 @@ static unsigned int alloc_balloon_pages(struct hv_dynmem_device *dm, ...@@ -1218,7 +1224,7 @@ static unsigned int alloc_balloon_pages(struct hv_dynmem_device *dm,
for (i = 0; (i * alloc_unit) < num_pages; i++) { for (i = 0; (i * alloc_unit) < num_pages; i++) {
if (bl_resp->hdr.size + sizeof(union dm_mem_page_range) > if (bl_resp->hdr.size + sizeof(union dm_mem_page_range) >
PAGE_SIZE) HV_HYP_PAGE_SIZE)
return i * alloc_unit; return i * alloc_unit;
/* /*
...@@ -1274,9 +1280,9 @@ static void balloon_up(struct work_struct *dummy) ...@@ -1274,9 +1280,9 @@ static void balloon_up(struct work_struct *dummy)
/* /*
* We will attempt 2M allocations. However, if we fail to * We will attempt 2M allocations. However, if we fail to
* allocate 2M chunks, we will go back to 4k allocations. * allocate 2M chunks, we will go back to PAGE_SIZE allocations.
*/ */
alloc_unit = 512; alloc_unit = PAGES_IN_2M;
avail_pages = si_mem_available(); avail_pages = si_mem_available();
floor = compute_balloon_floor(); floor = compute_balloon_floor();
...@@ -1292,7 +1298,7 @@ static void balloon_up(struct work_struct *dummy) ...@@ -1292,7 +1298,7 @@ static void balloon_up(struct work_struct *dummy)
} }
while (!done) { while (!done) {
memset(balloon_up_send_buffer, 0, PAGE_SIZE); memset(balloon_up_send_buffer, 0, HV_HYP_PAGE_SIZE);
bl_resp = (struct dm_balloon_response *)balloon_up_send_buffer; bl_resp = (struct dm_balloon_response *)balloon_up_send_buffer;
bl_resp->hdr.type = DM_BALLOON_RESPONSE; bl_resp->hdr.type = DM_BALLOON_RESPONSE;
bl_resp->hdr.size = sizeof(struct dm_balloon_response); bl_resp->hdr.size = sizeof(struct dm_balloon_response);
...@@ -1491,7 +1497,7 @@ static void balloon_onchannelcallback(void *context) ...@@ -1491,7 +1497,7 @@ static void balloon_onchannelcallback(void *context)
memset(recv_buffer, 0, sizeof(recv_buffer)); memset(recv_buffer, 0, sizeof(recv_buffer));
vmbus_recvpacket(dev->channel, recv_buffer, vmbus_recvpacket(dev->channel, recv_buffer,
PAGE_SIZE, &recvlen, &requestid); HV_HYP_PAGE_SIZE, &recvlen, &requestid);
if (recvlen > 0) { if (recvlen > 0) {
dm_msg = (struct dm_message *)recv_buffer; dm_msg = (struct dm_message *)recv_buffer;
...@@ -1509,6 +1515,11 @@ static void balloon_onchannelcallback(void *context) ...@@ -1509,6 +1515,11 @@ static void balloon_onchannelcallback(void *context)
break; break;
case DM_BALLOON_REQUEST: case DM_BALLOON_REQUEST:
if (allow_hibernation) {
pr_info("Ignore balloon-up request!\n");
break;
}
if (dm->state == DM_BALLOON_UP) if (dm->state == DM_BALLOON_UP)
pr_warn("Currently ballooning\n"); pr_warn("Currently ballooning\n");
bal_msg = (struct dm_balloon *)recv_buffer; bal_msg = (struct dm_balloon *)recv_buffer;
...@@ -1518,6 +1529,11 @@ static void balloon_onchannelcallback(void *context) ...@@ -1518,6 +1529,11 @@ static void balloon_onchannelcallback(void *context)
break; break;
case DM_UNBALLOON_REQUEST: case DM_UNBALLOON_REQUEST:
if (allow_hibernation) {
pr_info("Ignore balloon-down request!\n");
break;
}
dm->state = DM_BALLOON_DOWN; dm->state = DM_BALLOON_DOWN;
balloon_down(dm, balloon_down(dm,
(struct dm_unballoon_request *)recv_buffer); (struct dm_unballoon_request *)recv_buffer);
...@@ -1623,6 +1639,11 @@ static int balloon_connect_vsp(struct hv_device *dev) ...@@ -1623,6 +1639,11 @@ static int balloon_connect_vsp(struct hv_device *dev)
cap_msg.hdr.size = sizeof(struct dm_capabilities); cap_msg.hdr.size = sizeof(struct dm_capabilities);
cap_msg.hdr.trans_id = atomic_inc_return(&trans_id); cap_msg.hdr.trans_id = atomic_inc_return(&trans_id);
/*
* When hibernation (i.e. virtual ACPI S4 state) is enabled, the host
* currently still requires the bits to be set, so we have to add code
* to fail the host's hot-add and balloon up/down requests, if any.
*/
cap_msg.caps.cap_bits.balloon = 1; cap_msg.caps.cap_bits.balloon = 1;
cap_msg.caps.cap_bits.hot_add = 1; cap_msg.caps.cap_bits.hot_add = 1;
...@@ -1672,6 +1693,10 @@ static int balloon_probe(struct hv_device *dev, ...@@ -1672,6 +1693,10 @@ static int balloon_probe(struct hv_device *dev,
{ {
int ret; int ret;
allow_hibernation = hv_is_hibernation_supported();
if (allow_hibernation)
hot_add = false;
#ifdef CONFIG_MEMORY_HOTPLUG #ifdef CONFIG_MEMORY_HOTPLUG
do_hot_add = hot_add; do_hot_add = hot_add;
#else #else
...@@ -1711,6 +1736,8 @@ static int balloon_probe(struct hv_device *dev, ...@@ -1711,6 +1736,8 @@ static int balloon_probe(struct hv_device *dev,
return 0; return 0;
probe_error: probe_error:
dm_device.state = DM_INIT_ERROR;
dm_device.thread = NULL;
vmbus_close(dev->channel); vmbus_close(dev->channel);
#ifdef CONFIG_MEMORY_HOTPLUG #ifdef CONFIG_MEMORY_HOTPLUG
unregister_memory_notifier(&hv_memory_nb); unregister_memory_notifier(&hv_memory_nb);
...@@ -1752,6 +1779,59 @@ static int balloon_remove(struct hv_device *dev) ...@@ -1752,6 +1779,59 @@ static int balloon_remove(struct hv_device *dev)
return 0; return 0;
} }
static int balloon_suspend(struct hv_device *hv_dev)
{
struct hv_dynmem_device *dm = hv_get_drvdata(hv_dev);
tasklet_disable(&hv_dev->channel->callback_event);
cancel_work_sync(&dm->balloon_wrk.wrk);
cancel_work_sync(&dm->ha_wrk.wrk);
if (dm->thread) {
kthread_stop(dm->thread);
dm->thread = NULL;
vmbus_close(hv_dev->channel);
}
tasklet_enable(&hv_dev->channel->callback_event);
return 0;
}
static int balloon_resume(struct hv_device *dev)
{
int ret;
dm_device.state = DM_INITIALIZING;
ret = balloon_connect_vsp(dev);
if (ret != 0)
goto out;
dm_device.thread =
kthread_run(dm_thread_func, &dm_device, "hv_balloon");
if (IS_ERR(dm_device.thread)) {
ret = PTR_ERR(dm_device.thread);
dm_device.thread = NULL;
goto close_channel;
}
dm_device.state = DM_INITIALIZED;
return 0;
close_channel:
vmbus_close(dev->channel);
out:
dm_device.state = DM_INIT_ERROR;
#ifdef CONFIG_MEMORY_HOTPLUG
unregister_memory_notifier(&hv_memory_nb);
restore_online_page_callback(&hv_online_page);
#endif
return ret;
}
static const struct hv_vmbus_device_id id_table[] = { static const struct hv_vmbus_device_id id_table[] = {
/* Dynamic Memory Class ID */ /* Dynamic Memory Class ID */
/* 525074DC-8985-46e2-8057-A307DC18A502 */ /* 525074DC-8985-46e2-8057-A307DC18A502 */
...@@ -1766,6 +1846,8 @@ static struct hv_driver balloon_drv = { ...@@ -1766,6 +1846,8 @@ static struct hv_driver balloon_drv = {
.id_table = id_table, .id_table = id_table,
.probe = balloon_probe, .probe = balloon_probe,
.remove = balloon_remove, .remove = balloon_remove,
.suspend = balloon_suspend,
.resume = balloon_resume,
.driver = { .driver = {
.probe_type = PROBE_PREFER_ASYNCHRONOUS, .probe_type = PROBE_PREFER_ASYNCHRONOUS,
}, },
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Authors:
* Branden Bonaby <brandonbonaby94@gmail.com>
*/
#include <linux/hyperv.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/err.h>
#include "hyperv_vmbus.h"
struct dentry *hv_debug_root;
static int hv_debugfs_delay_get(void *data, u64 *val)
{
*val = *(u32 *)data;
return 0;
}
static int hv_debugfs_delay_set(void *data, u64 val)
{
if (val > 1000)
return -EINVAL;
*(u32 *)data = val;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(hv_debugfs_delay_fops, hv_debugfs_delay_get,
hv_debugfs_delay_set, "%llu\n");
static int hv_debugfs_state_get(void *data, u64 *val)
{
*val = *(bool *)data;
return 0;
}
static int hv_debugfs_state_set(void *data, u64 val)
{
if (val == 1)
*(bool *)data = true;
else if (val == 0)
*(bool *)data = false;
else
return -EINVAL;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(hv_debugfs_state_fops, hv_debugfs_state_get,
hv_debugfs_state_set, "%llu\n");
/* Setup delay files to store test values */
static int hv_debug_delay_files(struct hv_device *dev, struct dentry *root)
{
struct vmbus_channel *channel = dev->channel;
char *buffer = "fuzz_test_buffer_interrupt_delay";
char *message = "fuzz_test_message_delay";
int *buffer_val = &channel->fuzz_testing_interrupt_delay;
int *message_val = &channel->fuzz_testing_message_delay;
struct dentry *buffer_file, *message_file;
buffer_file = debugfs_create_file(buffer, 0644, root,
buffer_val,
&hv_debugfs_delay_fops);
if (IS_ERR(buffer_file)) {
pr_debug("debugfs_hyperv: file %s not created\n", buffer);
return PTR_ERR(buffer_file);
}
message_file = debugfs_create_file(message, 0644, root,
message_val,
&hv_debugfs_delay_fops);
if (IS_ERR(message_file)) {
pr_debug("debugfs_hyperv: file %s not created\n", message);
return PTR_ERR(message_file);
}
return 0;
}
/* Setup test state value for vmbus device */
static int hv_debug_set_test_state(struct hv_device *dev, struct dentry *root)
{
struct vmbus_channel *channel = dev->channel;
bool *state = &channel->fuzz_testing_state;
char *status = "fuzz_test_state";
struct dentry *test_state;
test_state = debugfs_create_file(status, 0644, root,
state,
&hv_debugfs_state_fops);
if (IS_ERR(test_state)) {
pr_debug("debugfs_hyperv: file %s not created\n", status);
return PTR_ERR(test_state);
}
return 0;
}
/* Bind hv device to a dentry for debugfs */
static void hv_debug_set_dir_dentry(struct hv_device *dev, struct dentry *root)
{
if (hv_debug_root)
dev->debug_dir = root;
}
/* Create all test dentry's and names for fuzz testing */
int hv_debug_add_dev_dir(struct hv_device *dev)
{
const char *device = dev_name(&dev->device);
char *delay_name = "delay";
struct dentry *delay, *dev_root;
int ret;
if (!IS_ERR(hv_debug_root)) {
dev_root = debugfs_create_dir(device, hv_debug_root);
if (IS_ERR(dev_root)) {
pr_debug("debugfs_hyperv: hyperv/%s/ not created\n",
device);
return PTR_ERR(dev_root);
}
hv_debug_set_test_state(dev, dev_root);
hv_debug_set_dir_dentry(dev, dev_root);
delay = debugfs_create_dir(delay_name, dev_root);
if (IS_ERR(delay)) {
pr_debug("debugfs_hyperv: hyperv/%s/%s/ not created\n",
device, delay_name);
return PTR_ERR(delay);
}
ret = hv_debug_delay_files(dev, delay);
return ret;
}
pr_debug("debugfs_hyperv: hyperv/ not in root debugfs path\n");
return PTR_ERR(hv_debug_root);
}
/* Remove dentry associated with released hv device */
void hv_debug_rm_dev_dir(struct hv_device *dev)
{
if (!IS_ERR(hv_debug_root))
debugfs_remove_recursive(dev->debug_dir);
}
/* Remove all dentrys associated with vmbus testing */
void hv_debug_rm_all_dir(void)
{
debugfs_remove_recursive(hv_debug_root);
}
/* Delay buffer/message reads on a vmbus channel */
void hv_debug_delay_test(struct vmbus_channel *channel, enum delay delay_type)
{
struct vmbus_channel *test_channel = channel->primary_channel ?
channel->primary_channel :
channel;
bool state = test_channel->fuzz_testing_state;
if (state) {
if (delay_type == 0)
udelay(test_channel->fuzz_testing_interrupt_delay);
else
udelay(test_channel->fuzz_testing_message_delay);
}
}
/* Initialize top dentry for vmbus testing */
int hv_debug_init(void)
{
hv_debug_root = debugfs_create_dir("hyperv", NULL);
if (IS_ERR(hv_debug_root)) {
pr_debug("debugfs_hyperv: hyperv/ not created\n");
return PTR_ERR(hv_debug_root);
}
return 0;
}
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/hyperv.h> #include <linux/hyperv.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <asm/hyperv-tlfs.h>
#include "hyperv_vmbus.h" #include "hyperv_vmbus.h"
#include "hv_utils_transport.h" #include "hv_utils_transport.h"
...@@ -234,7 +235,7 @@ void hv_fcopy_onchannelcallback(void *context) ...@@ -234,7 +235,7 @@ void hv_fcopy_onchannelcallback(void *context)
if (fcopy_transaction.state > HVUTIL_READY) if (fcopy_transaction.state > HVUTIL_READY)
return; return;
vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 2, &recvlen, vmbus_recvpacket(channel, recv_buffer, HV_HYP_PAGE_SIZE * 2, &recvlen,
&requestid); &requestid);
if (recvlen <= 0) if (recvlen <= 0)
return; return;
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <linux/connector.h> #include <linux/connector.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/hyperv.h> #include <linux/hyperv.h>
#include <asm/hyperv-tlfs.h>
#include "hyperv_vmbus.h" #include "hyperv_vmbus.h"
#include "hv_utils_transport.h" #include "hv_utils_transport.h"
...@@ -661,7 +662,7 @@ void hv_kvp_onchannelcallback(void *context) ...@@ -661,7 +662,7 @@ void hv_kvp_onchannelcallback(void *context)
if (kvp_transaction.state > HVUTIL_READY) if (kvp_transaction.state > HVUTIL_READY)
return; return;
vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 4, &recvlen, vmbus_recvpacket(channel, recv_buffer, HV_HYP_PAGE_SIZE * 4, &recvlen,
&requestid); &requestid);
if (recvlen > 0) { if (recvlen > 0) {
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/connector.h> #include <linux/connector.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/hyperv.h> #include <linux/hyperv.h>
#include <asm/hyperv-tlfs.h>
#include "hyperv_vmbus.h" #include "hyperv_vmbus.h"
#include "hv_utils_transport.h" #include "hv_utils_transport.h"
...@@ -297,7 +298,7 @@ void hv_vss_onchannelcallback(void *context) ...@@ -297,7 +298,7 @@ void hv_vss_onchannelcallback(void *context)
if (vss_transaction.state > HVUTIL_READY) if (vss_transaction.state > HVUTIL_READY)
return; return;
vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 2, &recvlen, vmbus_recvpacket(channel, recv_buffer, HV_HYP_PAGE_SIZE * 2, &recvlen,
&requestid); &requestid);
if (recvlen > 0) { if (recvlen > 0) {
......
...@@ -136,7 +136,7 @@ static void shutdown_onchannelcallback(void *context) ...@@ -136,7 +136,7 @@ static void shutdown_onchannelcallback(void *context)
struct icmsg_hdr *icmsghdrp; struct icmsg_hdr *icmsghdrp;
vmbus_recvpacket(channel, shut_txf_buf, vmbus_recvpacket(channel, shut_txf_buf,
PAGE_SIZE, &recvlen, &requestid); HV_HYP_PAGE_SIZE, &recvlen, &requestid);
if (recvlen > 0) { if (recvlen > 0) {
icmsghdrp = (struct icmsg_hdr *)&shut_txf_buf[ icmsghdrp = (struct icmsg_hdr *)&shut_txf_buf[
...@@ -284,7 +284,7 @@ static void timesync_onchannelcallback(void *context) ...@@ -284,7 +284,7 @@ static void timesync_onchannelcallback(void *context)
u8 *time_txf_buf = util_timesynch.recv_buffer; u8 *time_txf_buf = util_timesynch.recv_buffer;
vmbus_recvpacket(channel, time_txf_buf, vmbus_recvpacket(channel, time_txf_buf,
PAGE_SIZE, &recvlen, &requestid); HV_HYP_PAGE_SIZE, &recvlen, &requestid);
if (recvlen > 0) { if (recvlen > 0) {
icmsghdrp = (struct icmsg_hdr *)&time_txf_buf[ icmsghdrp = (struct icmsg_hdr *)&time_txf_buf[
...@@ -346,7 +346,7 @@ static void heartbeat_onchannelcallback(void *context) ...@@ -346,7 +346,7 @@ static void heartbeat_onchannelcallback(void *context)
while (1) { while (1) {
vmbus_recvpacket(channel, hbeat_txf_buf, vmbus_recvpacket(channel, hbeat_txf_buf,
PAGE_SIZE, &recvlen, &requestid); HV_HYP_PAGE_SIZE, &recvlen, &requestid);
if (!recvlen) if (!recvlen)
break; break;
...@@ -390,7 +390,7 @@ static int util_probe(struct hv_device *dev, ...@@ -390,7 +390,7 @@ static int util_probe(struct hv_device *dev,
(struct hv_util_service *)dev_id->driver_data; (struct hv_util_service *)dev_id->driver_data;
int ret; int ret;
srv->recv_buffer = kmalloc(PAGE_SIZE * 4, GFP_KERNEL); srv->recv_buffer = kmalloc(HV_HYP_PAGE_SIZE * 4, GFP_KERNEL);
if (!srv->recv_buffer) if (!srv->recv_buffer)
return -ENOMEM; return -ENOMEM;
srv->channel = dev->channel; srv->channel = dev->channel;
...@@ -413,8 +413,9 @@ static int util_probe(struct hv_device *dev, ...@@ -413,8 +413,9 @@ static int util_probe(struct hv_device *dev,
hv_set_drvdata(dev, srv); hv_set_drvdata(dev, srv);
ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE, NULL, 0, ret = vmbus_open(dev->channel, 4 * HV_HYP_PAGE_SIZE,
srv->util_cb, dev->channel); 4 * HV_HYP_PAGE_SIZE, NULL, 0, srv->util_cb,
dev->channel);
if (ret) if (ret)
goto error; goto error;
......
...@@ -385,4 +385,35 @@ enum hvutil_device_state { ...@@ -385,4 +385,35 @@ enum hvutil_device_state {
HVUTIL_DEVICE_DYING, /* driver unload is in progress */ HVUTIL_DEVICE_DYING, /* driver unload is in progress */
}; };
enum delay {
INTERRUPT_DELAY = 0,
MESSAGE_DELAY = 1,
};
#ifdef CONFIG_HYPERV_TESTING
int hv_debug_add_dev_dir(struct hv_device *dev);
void hv_debug_rm_dev_dir(struct hv_device *dev);
void hv_debug_rm_all_dir(void);
int hv_debug_init(void);
void hv_debug_delay_test(struct vmbus_channel *channel, enum delay delay_type);
#else /* CONFIG_HYPERV_TESTING */
static inline void hv_debug_rm_dev_dir(struct hv_device *dev) {};
static inline void hv_debug_rm_all_dir(void) {};
static inline void hv_debug_delay_test(struct vmbus_channel *channel,
enum delay delay_type) {};
static inline int hv_debug_init(void)
{
return -1;
}
static inline int hv_debug_add_dev_dir(struct hv_device *dev)
{
return -1;
}
#endif /* CONFIG_HYPERV_TESTING */
#endif /* _HYPERV_VMBUS_H */ #endif /* _HYPERV_VMBUS_H */
...@@ -396,6 +396,7 @@ struct vmpacket_descriptor *hv_pkt_iter_first(struct vmbus_channel *channel) ...@@ -396,6 +396,7 @@ struct vmpacket_descriptor *hv_pkt_iter_first(struct vmbus_channel *channel)
struct hv_ring_buffer_info *rbi = &channel->inbound; struct hv_ring_buffer_info *rbi = &channel->inbound;
struct vmpacket_descriptor *desc; struct vmpacket_descriptor *desc;
hv_debug_delay_test(channel, MESSAGE_DELAY);
if (hv_pkt_iter_avail(rbi) < sizeof(struct vmpacket_descriptor)) if (hv_pkt_iter_avail(rbi) < sizeof(struct vmpacket_descriptor))
return NULL; return NULL;
...@@ -421,6 +422,7 @@ __hv_pkt_iter_next(struct vmbus_channel *channel, ...@@ -421,6 +422,7 @@ __hv_pkt_iter_next(struct vmbus_channel *channel,
u32 packetlen = desc->len8 << 3; u32 packetlen = desc->len8 << 3;
u32 dsize = rbi->ring_datasize; u32 dsize = rbi->ring_datasize;
hv_debug_delay_test(channel, MESSAGE_DELAY);
/* bump offset to next potential packet */ /* bump offset to next potential packet */
rbi->priv_read_index += packetlen + VMBUS_PKT_TRAILER; rbi->priv_read_index += packetlen + VMBUS_PKT_TRAILER;
if (rbi->priv_read_index >= dsize) if (rbi->priv_read_index >= dsize)
......
...@@ -79,7 +79,7 @@ static struct notifier_block hyperv_panic_block = { ...@@ -79,7 +79,7 @@ static struct notifier_block hyperv_panic_block = {
static const char *fb_mmio_name = "fb_range"; static const char *fb_mmio_name = "fb_range";
static struct resource *fb_mmio; static struct resource *fb_mmio;
static struct resource *hyperv_mmio; static struct resource *hyperv_mmio;
static DEFINE_SEMAPHORE(hyperv_mmio_lock); static DEFINE_MUTEX(hyperv_mmio_lock);
static int vmbus_exists(void) static int vmbus_exists(void)
{ {
...@@ -960,6 +960,8 @@ static void vmbus_device_release(struct device *device) ...@@ -960,6 +960,8 @@ static void vmbus_device_release(struct device *device)
struct hv_device *hv_dev = device_to_hv_device(device); struct hv_device *hv_dev = device_to_hv_device(device);
struct vmbus_channel *channel = hv_dev->channel; struct vmbus_channel *channel = hv_dev->channel;
hv_debug_rm_dev_dir(hv_dev);
mutex_lock(&vmbus_connection.channel_mutex); mutex_lock(&vmbus_connection.channel_mutex);
hv_process_channel_removal(channel); hv_process_channel_removal(channel);
mutex_unlock(&vmbus_connection.channel_mutex); mutex_unlock(&vmbus_connection.channel_mutex);
...@@ -1273,7 +1275,7 @@ static void hv_kmsg_dump(struct kmsg_dumper *dumper, ...@@ -1273,7 +1275,7 @@ static void hv_kmsg_dump(struct kmsg_dumper *dumper,
* Write dump contents to the page. No need to synchronize; panic should * Write dump contents to the page. No need to synchronize; panic should
* be single-threaded. * be single-threaded.
*/ */
kmsg_dump_get_buffer(dumper, true, hv_panic_page, PAGE_SIZE, kmsg_dump_get_buffer(dumper, true, hv_panic_page, HV_HYP_PAGE_SIZE,
&bytes_written); &bytes_written);
if (bytes_written) if (bytes_written)
hyperv_report_panic_msg(panic_pa, bytes_written); hyperv_report_panic_msg(panic_pa, bytes_written);
...@@ -1373,7 +1375,7 @@ static int vmbus_bus_init(void) ...@@ -1373,7 +1375,7 @@ static int vmbus_bus_init(void)
*/ */
hv_get_crash_ctl(hyperv_crash_ctl); hv_get_crash_ctl(hyperv_crash_ctl);
if (hyperv_crash_ctl & HV_CRASH_CTL_CRASH_NOTIFY_MSG) { if (hyperv_crash_ctl & HV_CRASH_CTL_CRASH_NOTIFY_MSG) {
hv_panic_page = (void *)get_zeroed_page(GFP_KERNEL); hv_panic_page = (void *)hv_alloc_hyperv_zeroed_page();
if (hv_panic_page) { if (hv_panic_page) {
ret = kmsg_dump_register(&hv_kmsg_dumper); ret = kmsg_dump_register(&hv_kmsg_dumper);
if (ret) if (ret)
...@@ -1401,7 +1403,7 @@ static int vmbus_bus_init(void) ...@@ -1401,7 +1403,7 @@ static int vmbus_bus_init(void)
hv_remove_vmbus_irq(); hv_remove_vmbus_irq();
bus_unregister(&hv_bus); bus_unregister(&hv_bus);
free_page((unsigned long)hv_panic_page); hv_free_hyperv_page((unsigned long)hv_panic_page);
unregister_sysctl_table(hv_ctl_table_hdr); unregister_sysctl_table(hv_ctl_table_hdr);
hv_ctl_table_hdr = NULL; hv_ctl_table_hdr = NULL;
return ret; return ret;
...@@ -1809,6 +1811,7 @@ int vmbus_device_register(struct hv_device *child_device_obj) ...@@ -1809,6 +1811,7 @@ int vmbus_device_register(struct hv_device *child_device_obj)
pr_err("Unable to register primary channeln"); pr_err("Unable to register primary channeln");
goto err_kset_unregister; goto err_kset_unregister;
} }
hv_debug_add_dev_dir(child_device_obj);
return 0; return 0;
...@@ -2010,7 +2013,7 @@ int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj, ...@@ -2010,7 +2013,7 @@ int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj,
int retval; int retval;
retval = -ENXIO; retval = -ENXIO;
down(&hyperv_mmio_lock); mutex_lock(&hyperv_mmio_lock);
/* /*
* If overlaps with frame buffers are allowed, then first attempt to * If overlaps with frame buffers are allowed, then first attempt to
...@@ -2057,7 +2060,7 @@ int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj, ...@@ -2057,7 +2060,7 @@ int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj,
} }
exit: exit:
up(&hyperv_mmio_lock); mutex_unlock(&hyperv_mmio_lock);
return retval; return retval;
} }
EXPORT_SYMBOL_GPL(vmbus_allocate_mmio); EXPORT_SYMBOL_GPL(vmbus_allocate_mmio);
...@@ -2074,7 +2077,7 @@ void vmbus_free_mmio(resource_size_t start, resource_size_t size) ...@@ -2074,7 +2077,7 @@ void vmbus_free_mmio(resource_size_t start, resource_size_t size)
{ {
struct resource *iter; struct resource *iter;
down(&hyperv_mmio_lock); mutex_lock(&hyperv_mmio_lock);
for (iter = hyperv_mmio; iter; iter = iter->sibling) { for (iter = hyperv_mmio; iter; iter = iter->sibling) {
if ((iter->start >= start + size) || (iter->end <= start)) if ((iter->start >= start + size) || (iter->end <= start))
continue; continue;
...@@ -2082,7 +2085,7 @@ void vmbus_free_mmio(resource_size_t start, resource_size_t size) ...@@ -2082,7 +2085,7 @@ void vmbus_free_mmio(resource_size_t start, resource_size_t size)
__release_region(iter, start, size); __release_region(iter, start, size);
} }
release_mem_region(start, size); release_mem_region(start, size);
up(&hyperv_mmio_lock); mutex_unlock(&hyperv_mmio_lock);
} }
EXPORT_SYMBOL_GPL(vmbus_free_mmio); EXPORT_SYMBOL_GPL(vmbus_free_mmio);
...@@ -2215,8 +2218,7 @@ static int vmbus_bus_resume(struct device *dev) ...@@ -2215,8 +2218,7 @@ static int vmbus_bus_resume(struct device *dev)
* We only use the 'vmbus_proto_version', which was in use before * We only use the 'vmbus_proto_version', which was in use before
* hibernation, to re-negotiate with the host. * hibernation, to re-negotiate with the host.
*/ */
if (vmbus_proto_version == VERSION_INVAL || if (!vmbus_proto_version) {
vmbus_proto_version == 0) {
pr_err("Invalid proto version = 0x%x\n", vmbus_proto_version); pr_err("Invalid proto version = 0x%x\n", vmbus_proto_version);
return -EINVAL; return -EINVAL;
} }
...@@ -2303,7 +2305,7 @@ static void hv_crash_handler(struct pt_regs *regs) ...@@ -2303,7 +2305,7 @@ static void hv_crash_handler(struct pt_regs *regs)
vmbus_connection.conn_state = DISCONNECTED; vmbus_connection.conn_state = DISCONNECTED;
cpu = smp_processor_id(); cpu = smp_processor_id();
hv_stimer_cleanup(cpu); hv_stimer_cleanup(cpu);
hv_synic_cleanup(cpu); hv_synic_disable_regs(cpu);
hyperv_cleanup(); hyperv_cleanup();
}; };
...@@ -2373,6 +2375,7 @@ static int __init hv_acpi_init(void) ...@@ -2373,6 +2375,7 @@ static int __init hv_acpi_init(void)
ret = -ETIMEDOUT; ret = -ETIMEDOUT;
goto cleanup; goto cleanup;
} }
hv_debug_init();
ret = vmbus_bus_init(); ret = vmbus_bus_init();
if (ret) if (ret)
...@@ -2409,6 +2412,8 @@ static void __exit vmbus_exit(void) ...@@ -2409,6 +2412,8 @@ static void __exit vmbus_exit(void)
tasklet_kill(&hv_cpu->msg_dpc); tasklet_kill(&hv_cpu->msg_dpc);
} }
hv_debug_rm_all_dir();
vmbus_free_channels(); vmbus_free_channels();
if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE) { if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE) {
......
...@@ -467,7 +467,7 @@ config QCOM_IOMMU ...@@ -467,7 +467,7 @@ config QCOM_IOMMU
config HYPERV_IOMMU config HYPERV_IOMMU
bool "Hyper-V x2APIC IRQ Handling" bool "Hyper-V x2APIC IRQ Handling"
depends on HYPERV depends on HYPERV && X86
select IOMMU_API select IOMMU_API
default HYPERV default HYPERV
help help
......
...@@ -955,6 +955,9 @@ struct net_device_context { ...@@ -955,6 +955,9 @@ struct net_device_context {
u32 vf_alloc; u32 vf_alloc;
/* Serial number of the VF to team with */ /* Serial number of the VF to team with */
u32 vf_serial; u32 vf_serial;
/* Used to temporarily save the config info across hibernation */
struct netvsc_device_info *saved_netvsc_dev_info;
}; };
/* Per channel data */ /* Per channel data */
......
...@@ -2424,6 +2424,61 @@ static int netvsc_remove(struct hv_device *dev) ...@@ -2424,6 +2424,61 @@ static int netvsc_remove(struct hv_device *dev)
return 0; return 0;
} }
static int netvsc_suspend(struct hv_device *dev)
{
struct net_device_context *ndev_ctx;
struct net_device *vf_netdev, *net;
struct netvsc_device *nvdev;
int ret;
net = hv_get_drvdata(dev);
ndev_ctx = netdev_priv(net);
cancel_delayed_work_sync(&ndev_ctx->dwork);
rtnl_lock();
nvdev = rtnl_dereference(ndev_ctx->nvdev);
if (nvdev == NULL) {
ret = -ENODEV;
goto out;
}
vf_netdev = rtnl_dereference(ndev_ctx->vf_netdev);
if (vf_netdev)
netvsc_unregister_vf(vf_netdev);
/* Save the current config info */
ndev_ctx->saved_netvsc_dev_info = netvsc_devinfo_get(nvdev);
ret = netvsc_detach(net, nvdev);
out:
rtnl_unlock();
return ret;
}
static int netvsc_resume(struct hv_device *dev)
{
struct net_device *net = hv_get_drvdata(dev);
struct net_device_context *net_device_ctx;
struct netvsc_device_info *device_info;
int ret;
rtnl_lock();
net_device_ctx = netdev_priv(net);
device_info = net_device_ctx->saved_netvsc_dev_info;
ret = netvsc_attach(net, device_info);
rtnl_unlock();
kfree(device_info);
net_device_ctx->saved_netvsc_dev_info = NULL;
return ret;
}
static const struct hv_vmbus_device_id id_table[] = { static const struct hv_vmbus_device_id id_table[] = {
/* Network guid */ /* Network guid */
{ HV_NIC_GUID, }, { HV_NIC_GUID, },
...@@ -2438,6 +2493,8 @@ static struct hv_driver netvsc_drv = { ...@@ -2438,6 +2493,8 @@ static struct hv_driver netvsc_drv = {
.id_table = id_table, .id_table = id_table,
.probe = netvsc_probe, .probe = netvsc_probe,
.remove = netvsc_remove, .remove = netvsc_remove,
.suspend = netvsc_suspend,
.resume = netvsc_resume,
.driver = { .driver = {
.probe_type = PROBE_FORCE_SYNCHRONOUS, .probe_type = PROBE_FORCE_SYNCHRONOUS,
}, },
......
...@@ -1727,6 +1727,13 @@ static const struct hv_vmbus_device_id id_table[] = { ...@@ -1727,6 +1727,13 @@ static const struct hv_vmbus_device_id id_table[] = {
MODULE_DEVICE_TABLE(vmbus, id_table); MODULE_DEVICE_TABLE(vmbus, id_table);
static const struct { guid_t guid; } fc_guid = { HV_SYNTHFC_GUID };
static bool hv_dev_is_fc(struct hv_device *hv_dev)
{
return guid_equal(&fc_guid.guid, &hv_dev->dev_type);
}
static int storvsc_probe(struct hv_device *device, static int storvsc_probe(struct hv_device *device,
const struct hv_vmbus_device_id *dev_id) const struct hv_vmbus_device_id *dev_id)
{ {
...@@ -1934,11 +1941,45 @@ static int storvsc_remove(struct hv_device *dev) ...@@ -1934,11 +1941,45 @@ static int storvsc_remove(struct hv_device *dev)
return 0; return 0;
} }
static int storvsc_suspend(struct hv_device *hv_dev)
{
struct storvsc_device *stor_device = hv_get_drvdata(hv_dev);
struct Scsi_Host *host = stor_device->host;
struct hv_host_device *host_dev = shost_priv(host);
storvsc_wait_to_drain(stor_device);
drain_workqueue(host_dev->handle_error_wq);
vmbus_close(hv_dev->channel);
memset(stor_device->stor_chns, 0,
num_possible_cpus() * sizeof(void *));
kfree(stor_device->stor_chns);
stor_device->stor_chns = NULL;
cpumask_clear(&stor_device->alloced_cpus);
return 0;
}
static int storvsc_resume(struct hv_device *hv_dev)
{
int ret;
ret = storvsc_connect_to_vsp(hv_dev, storvsc_ringbuffer_size,
hv_dev_is_fc(hv_dev));
return ret;
}
static struct hv_driver storvsc_drv = { static struct hv_driver storvsc_drv = {
.name = KBUILD_MODNAME, .name = KBUILD_MODNAME,
.id_table = id_table, .id_table = id_table,
.probe = storvsc_probe, .probe = storvsc_probe,
.remove = storvsc_remove, .remove = storvsc_remove,
.suspend = storvsc_suspend,
.resume = storvsc_resume,
.driver = { .driver = {
.probe_type = PROBE_PREFER_ASYNCHRONOUS, .probe_type = PROBE_PREFER_ASYNCHRONOUS,
}, },
......
...@@ -2214,6 +2214,7 @@ config FB_HYPERV ...@@ -2214,6 +2214,7 @@ config FB_HYPERV
select FB_CFB_FILLRECT select FB_CFB_FILLRECT
select FB_CFB_COPYAREA select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT select FB_CFB_IMAGEBLIT
select FB_DEFERRED_IO
help help
This framebuffer driver supports Microsoft Hyper-V Synthetic Video. This framebuffer driver supports Microsoft Hyper-V Synthetic Video.
......
This diff is collapsed.
...@@ -166,10 +166,12 @@ static inline int cpumask_to_vpset(struct hv_vpset *vpset, ...@@ -166,10 +166,12 @@ static inline int cpumask_to_vpset(struct hv_vpset *vpset,
void hyperv_report_panic(struct pt_regs *regs, long err); void hyperv_report_panic(struct pt_regs *regs, long err);
void hyperv_report_panic_msg(phys_addr_t pa, size_t size); void hyperv_report_panic_msg(phys_addr_t pa, size_t size);
bool hv_is_hyperv_initialized(void); bool hv_is_hyperv_initialized(void);
bool hv_is_hibernation_supported(void);
void hyperv_cleanup(void); void hyperv_cleanup(void);
void hv_setup_sched_clock(void *sched_clock); void hv_setup_sched_clock(void *sched_clock);
#else /* CONFIG_HYPERV */ #else /* CONFIG_HYPERV */
static inline bool hv_is_hyperv_initialized(void) { return false; } static inline bool hv_is_hyperv_initialized(void) { return false; }
static inline bool hv_is_hibernation_supported(void) { return false; }
static inline void hyperv_cleanup(void) {} static inline void hyperv_cleanup(void) {}
#endif /* CONFIG_HYPERV */ #endif /* CONFIG_HYPERV */
......
...@@ -182,19 +182,21 @@ static inline u32 hv_get_avail_to_write_percent( ...@@ -182,19 +182,21 @@ static inline u32 hv_get_avail_to_write_percent(
* 2 . 4 (Windows 8) * 2 . 4 (Windows 8)
* 3 . 0 (Windows 8 R2) * 3 . 0 (Windows 8 R2)
* 4 . 0 (Windows 10) * 4 . 0 (Windows 10)
* 4 . 1 (Windows 10 RS3)
* 5 . 0 (Newer Windows 10) * 5 . 0 (Newer Windows 10)
* 5 . 1 (Windows 10 RS4)
* 5 . 2 (Windows Server 2019, RS5)
*/ */
#define VERSION_WS2008 ((0 << 16) | (13)) #define VERSION_WS2008 ((0 << 16) | (13))
#define VERSION_WIN7 ((1 << 16) | (1)) #define VERSION_WIN7 ((1 << 16) | (1))
#define VERSION_WIN8 ((2 << 16) | (4)) #define VERSION_WIN8 ((2 << 16) | (4))
#define VERSION_WIN8_1 ((3 << 16) | (0)) #define VERSION_WIN8_1 ((3 << 16) | (0))
#define VERSION_WIN10 ((4 << 16) | (0)) #define VERSION_WIN10 ((4 << 16) | (0))
#define VERSION_WIN10_V4_1 ((4 << 16) | (1))
#define VERSION_WIN10_V5 ((5 << 16) | (0)) #define VERSION_WIN10_V5 ((5 << 16) | (0))
#define VERSION_WIN10_V5_1 ((5 << 16) | (1))
#define VERSION_INVAL -1 #define VERSION_WIN10_V5_2 ((5 << 16) | (2))
#define VERSION_CURRENT VERSION_WIN10_V5
/* Make maximum size of pipe payload of 16K */ /* Make maximum size of pipe payload of 16K */
#define MAX_PIPE_DATA_PAYLOAD (sizeof(u8) * 16384) #define MAX_PIPE_DATA_PAYLOAD (sizeof(u8) * 16384)
...@@ -932,6 +934,21 @@ struct vmbus_channel { ...@@ -932,6 +934,21 @@ struct vmbus_channel {
* full outbound ring buffer. * full outbound ring buffer.
*/ */
u64 out_full_first; u64 out_full_first;
/* enabling/disabling fuzz testing on the channel (default is false)*/
bool fuzz_testing_state;
/*
* Interrupt delay will delay the guest from emptying the ring buffer
* for a specific amount of time. The delay is in microseconds and will
* be between 1 to a maximum of 1000, its default is 0 (no delay).
* The Message delay will delay guest reading on a per message basis
* in microseconds between 1 to 1000 with the default being 0
* (no delay).
*/
u32 fuzz_testing_interrupt_delay;
u32 fuzz_testing_message_delay;
}; };
static inline bool is_hvsock_channel(const struct vmbus_channel *c) static inline bool is_hvsock_channel(const struct vmbus_channel *c)
...@@ -1180,6 +1197,10 @@ struct hv_device { ...@@ -1180,6 +1197,10 @@ struct hv_device {
struct vmbus_channel *channel; struct vmbus_channel *channel;
struct kset *channels_kset; struct kset *channels_kset;
/* place holder to keep track of the dir for hv device in debugfs */
struct dentry *debug_dir;
}; };
......
...@@ -2167,4 +2167,11 @@ config IO_STRICT_DEVMEM ...@@ -2167,4 +2167,11 @@ config IO_STRICT_DEVMEM
source "arch/$(SRCARCH)/Kconfig.debug" source "arch/$(SRCARCH)/Kconfig.debug"
config HYPERV_TESTING
bool "Microsoft Hyper-V driver testing"
default n
depends on HYPERV && DEBUG_FS
help
Select this option to enable Hyper-V vmbus testing.
endmenu # Kernel hacking endmenu # Kernel hacking
...@@ -922,6 +922,24 @@ static int hvs_remove(struct hv_device *hdev) ...@@ -922,6 +922,24 @@ static int hvs_remove(struct hv_device *hdev)
return 0; return 0;
} }
/* hv_sock connections can not persist across hibernation, and all the hv_sock
* channels are forced to be rescinded before hibernation: see
* vmbus_bus_suspend(). Here the dummy hvs_suspend() and hvs_resume()
* are only needed because hibernation requires that every vmbus device's
* driver should have a .suspend and .resume callback: see vmbus_suspend().
*/
static int hvs_suspend(struct hv_device *hv_dev)
{
/* Dummy */
return 0;
}
static int hvs_resume(struct hv_device *dev)
{
/* Dummy */
return 0;
}
/* This isn't really used. See vmbus_match() and vmbus_probe() */ /* This isn't really used. See vmbus_match() and vmbus_probe() */
static const struct hv_vmbus_device_id id_table[] = { static const struct hv_vmbus_device_id id_table[] = {
{}, {},
...@@ -933,6 +951,8 @@ static struct hv_driver hvs_drv = { ...@@ -933,6 +951,8 @@ static struct hv_driver hvs_drv = {
.id_table = id_table, .id_table = id_table,
.probe = hvs_probe, .probe = hvs_probe,
.remove = hvs_remove, .remove = hvs_remove,
.suspend = hvs_suspend,
.resume = hvs_resume,
}; };
static int __init hvs_init(void) static int __init hvs_init(void)
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment