* [PATCH] hv_balloon: Add the support of hibernation
From: Dexuan Cui @ 2019-09-11 23:36 UTC (permalink / raw)
To: KY Srinivasan, Haiyang Zhang, Stephen Hemminger,
sashal@kernel.org, linux-hyperv@vger.kernel.org,
linux-kernel@vger.kernel.org, Michael Kelley
Cc: Dexuan Cui
When hibernation is enabled, we must ignore the balloon up/down and
hot-add requests from the host, if any.
Fow now, if people want to test hibernation, please blacklist hv_balloon
or do not enable Dynamic Memory and Memory Resizing. See the comment in
balloon_probe() for more info.
Signed-off-by: Dexuan Cui <decui@microsoft.com>
---
This patch is basically a pure Hyper-V specific change and it has a
build dependency on the commit 271b2224d42f ("Drivers: hv: vmbus: Implement
suspend/resume for VSC drivers for hibernation"), which is on Sasha Levin's
Hyper-V tree's hyperv-next branch:
https://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux.git/log/?h=hyperv-next
I request this patch should go through Sasha's tree rather than the
other tree(s).
drivers/hv/hv_balloon.c | 101 +++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 99 insertions(+), 2 deletions(-)
diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c
index 34bd735..7df0f67 100644
--- a/drivers/hv/hv_balloon.c
+++ b/drivers/hv/hv_balloon.c
@@ -24,6 +24,8 @@
#include <linux/hyperv.h>
+#include <asm/mshyperv.h>
+
#define CREATE_TRACE_POINTS
#include "hv_trace_balloon.h"
@@ -457,6 +459,7 @@ struct hot_add_wrk {
struct work_struct wrk;
};
+static bool allow_hibernation;
static bool hot_add = true;
static bool do_hot_add;
/*
@@ -1053,8 +1056,12 @@ static void hot_add_req(struct work_struct *dummy)
else
resp.result = 0;
- if (!do_hot_add || (resp.page_count == 0))
- pr_err("Memory hot add failed\n");
+ if (!do_hot_add || resp.page_count == 0) {
+ if (!allow_hibernation)
+ pr_err("Memory hot add failed\n");
+ else
+ pr_info("Ignore hot-add request!\n");
+ }
dm->state = DM_INITIALIZED;
resp.hdr.trans_id = atomic_inc_return(&trans_id);
@@ -1509,6 +1516,11 @@ static void balloon_onchannelcallback(void *context)
break;
case DM_BALLOON_REQUEST:
+ if (allow_hibernation) {
+ pr_info("Ignore balloon-up request!\n");
+ break;
+ }
+
if (dm->state == DM_BALLOON_UP)
pr_warn("Currently ballooning\n");
bal_msg = (struct dm_balloon *)recv_buffer;
@@ -1518,6 +1530,11 @@ static void balloon_onchannelcallback(void *context)
break;
case DM_UNBALLOON_REQUEST:
+ if (allow_hibernation) {
+ pr_info("Ignore balloon-down request!\n");
+ break;
+ }
+
dm->state = DM_BALLOON_DOWN;
balloon_down(dm,
(struct dm_unballoon_request *)recv_buffer);
@@ -1623,6 +1640,11 @@ static int balloon_connect_vsp(struct hv_device *dev)
cap_msg.hdr.size = sizeof(struct dm_capabilities);
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.hot_add = 1;
@@ -1672,6 +1694,24 @@ static int balloon_probe(struct hv_device *dev,
{
int ret;
+#if 0
+ /*
+ * The patch to implement hv_is_hibernation_supported() is going
+ * through the tip tree. For now, let's hardcode allow_hibernation
+ * to false to keep the current behavior of hv_balloon. If people
+ * want to test hibernation, please blacklist hv_balloon fow now
+ * or do not enable Dynamid Memory and Memory Resizing.
+ *
+ * We'll remove the conditional compilation as soon as
+ * hv_is_hibernation_supported() is available in the mainline tree.
+ */
+ allow_hibernation = hv_is_hibernation_supported();
+#else
+ allow_hibernation = false;
+#endif
+ if (allow_hibernation)
+ hot_add = false;
+
#ifdef CONFIG_MEMORY_HOTPLUG
do_hot_add = hot_add;
#else
@@ -1711,6 +1751,8 @@ static int balloon_probe(struct hv_device *dev,
return 0;
probe_error:
+ dm_device.state = DM_INIT_ERROR;
+ dm_device.thread = NULL;
vmbus_close(dev->channel);
#ifdef CONFIG_MEMORY_HOTPLUG
unregister_memory_notifier(&hv_memory_nb);
@@ -1752,6 +1794,59 @@ static int balloon_remove(struct hv_device *dev)
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[] = {
/* Dynamic Memory Class ID */
/* 525074DC-8985-46e2-8057-A307DC18A502 */
@@ -1766,6 +1861,8 @@ static int balloon_remove(struct hv_device *dev)
.id_table = id_table,
.probe = balloon_probe,
.remove = balloon_remove,
+ .suspend = balloon_suspend,
+ .resume = balloon_resume,
.driver = {
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
--
1.8.3.1
^ permalink raw reply related
* [PATCH][PATCH net-next] hv_sock: Add the support of hibernation
From: Dexuan Cui @ 2019-09-11 23:37 UTC (permalink / raw)
To: KY Srinivasan, Haiyang Zhang, Stephen Hemminger,
sashal@kernel.org, davem@davemloft.net,
linux-hyperv@vger.kernel.org, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org, Michael Kelley
Cc: Dexuan Cui
Add the necessary dummy callbacks for hibernation.
Signed-off-by: Dexuan Cui <decui@microsoft.com>
---
This patch is basically a pure Hyper-V specific change and it has a
build dependency on the commit 271b2224d42f ("Drivers: hv: vmbus: Implement
suspend/resume for VSC drivers for hibernation"), which is on Sasha Levin's
Hyper-V tree's hyperv-next branch:
https://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux.git/log/?h=hyperv-next
I request this patch should go through Sasha's tree rather than the
net-next tree.
net/vmw_vsock/hyperv_transport.c | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/net/vmw_vsock/hyperv_transport.c b/net/vmw_vsock/hyperv_transport.c
index f2084e3..e91a884 100644
--- a/net/vmw_vsock/hyperv_transport.c
+++ b/net/vmw_vsock/hyperv_transport.c
@@ -930,6 +930,24 @@ static int hvs_remove(struct hv_device *hdev)
return 0;
}
+/* hv_sock connections can not persist across hibernation, and all the hv_sock
+ * channels are forceed 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 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() */
static const struct hv_vmbus_device_id id_table[] = {
{},
@@ -941,6 +959,8 @@ static int hvs_remove(struct hv_device *hdev)
.id_table = id_table,
.probe = hvs_probe,
.remove = hvs_remove,
+ .suspend = hvs_suspend,
+ .resume = hvs_resume,
};
static int __init hvs_init(void)
--
1.8.3.1
^ permalink raw reply related
* [PATCH][PATCH net-next] hv_netvsc: Add the support of hibernation
From: Dexuan Cui @ 2019-09-11 23:37 UTC (permalink / raw)
To: KY Srinivasan, Haiyang Zhang, Stephen Hemminger,
sashal@kernel.org, davem@davemloft.net,
linux-hyperv@vger.kernel.org, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org, Michael Kelley
Cc: Dexuan Cui
The existing netvsc_detach() and netvsc_attach() APIs make it easy to
implement the suspend/resume callbacks.
Signed-off-by: Dexuan Cui <decui@microsoft.com>
---
This patch is basically a pure Hyper-V specific change and it has a
build dependency on the commit 271b2224d42f ("Drivers: hv: vmbus: Implement
suspend/resume for VSC drivers for hibernation"), which is on Sasha Levin's
Hyper-V tree's hyperv-next branch:
https://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux.git/log/?h=hyperv-next
I request this patch should go through Sasha's tree rather than the
net-next tree.
drivers/net/hyperv/hyperv_net.h | 3 +++
drivers/net/hyperv/netvsc_drv.c | 59 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 62 insertions(+)
diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h
index ecc9af0..b8763ee 100644
--- a/drivers/net/hyperv/hyperv_net.h
+++ b/drivers/net/hyperv/hyperv_net.h
@@ -952,6 +952,9 @@ struct net_device_context {
u32 vf_alloc;
/* Serial number of the VF to team with */
u32 vf_serial;
+
+ /* Used to temporarily save the config info across hibernation */
+ struct netvsc_device_info *saved_netvsc_dev_info;
};
/* Per channel data */
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
index afdcc56..f920959 100644
--- a/drivers/net/hyperv/netvsc_drv.c
+++ b/drivers/net/hyperv/netvsc_drv.c
@@ -2392,6 +2392,63 @@ static int netvsc_remove(struct hv_device *dev)
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;
+ }
+
+ cancel_work_sync(&nvdev->subchan_work);
+
+ 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[] = {
/* Network guid */
{ HV_NIC_GUID, },
@@ -2406,6 +2463,8 @@ static int netvsc_remove(struct hv_device *dev)
.id_table = id_table,
.probe = netvsc_probe,
.remove = netvsc_remove,
+ .suspend = netvsc_suspend,
+ .resume = netvsc_resume,
.driver = {
.probe_type = PROBE_FORCE_SYNCHRONOUS,
},
--
1.8.3.1
^ permalink raw reply related
* [PATCH 0/4] Enhance pci-hyperv to support hibernation
From: Dexuan Cui @ 2019-09-11 23:38 UTC (permalink / raw)
To: KY Srinivasan, Haiyang Zhang, Stephen Hemminger,
sashal@kernel.org, lorenzo.pieralisi@arm.com, bhelgaas@google.com,
linux-hyperv@vger.kernel.org, linux-pci@vger.kernel.org,
linux-kernel@vger.kernel.org, Michael Kelley
Cc: Dexuan Cui
This patchset is basically a pure Hyper-V specific change and it has a
build dependency on the commit 271b2224d42f ("Drivers: hv: vmbus: Implement
suspend/resume for VSC drivers for hibernation"), which is on Sasha Levin's
Hyper-V tree's hyperv-next branch:
https://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux.git/log/?h=hyperv-next
I request this patch should go through Sasha's tree rather than the
pci tree.
Dexuan Cui (4):
PCI: hv: Reorganize the code in preparation of hibernation
PCI: hv: Add the support of hibernation
PCI: hv: Do not queue new work items on hibernation
PCI: hv: Change pci_protocol_version to per-hbus
drivers/pci/controller/pci-hyperv.c | 166 ++++++++++++++++++++++++++++++------
1 file changed, 140 insertions(+), 26 deletions(-)
--
1.8.3.1
^ permalink raw reply
* [PATCH 4/4] PCI: hv: Change pci_protocol_version to per-hbus
From: Dexuan Cui @ 2019-09-11 23:38 UTC (permalink / raw)
To: KY Srinivasan, Haiyang Zhang, Stephen Hemminger,
sashal@kernel.org, lorenzo.pieralisi@arm.com, bhelgaas@google.com,
linux-hyperv@vger.kernel.org, linux-pci@vger.kernel.org,
linux-kernel@vger.kernel.org, Michael Kelley
Cc: Dexuan Cui
In-Reply-To: <1568245086-70601-1-git-send-email-decui@microsoft.com>
A VM can have multiple hbus. It looks incorrect for the second hbus's
hv_pci_protocol_negotiation() to set the global variable
'pci_protocol_version' (which was set by the first hbus), even if the
same value is written.
Signed-off-by: Dexuan Cui <decui@microsoft.com>
---
drivers/pci/controller/pci-hyperv.c | 22 ++++++++++------------
1 file changed, 10 insertions(+), 12 deletions(-)
diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c
index 2655df2..55730c5 100644
--- a/drivers/pci/controller/pci-hyperv.c
+++ b/drivers/pci/controller/pci-hyperv.c
@@ -76,11 +76,6 @@ enum pci_protocol_version_t {
PCI_PROTOCOL_VERSION_1_1,
};
-/*
- * Protocol version negotiated by hv_pci_protocol_negotiation().
- */
-static enum pci_protocol_version_t pci_protocol_version;
-
#define PCI_CONFIG_MMIO_LENGTH 0x2000
#define CFG_PAGE_OFFSET 0x1000
#define CFG_PAGE_SIZE (PCI_CONFIG_MMIO_LENGTH - CFG_PAGE_OFFSET)
@@ -429,6 +424,8 @@ enum hv_pcibus_state {
struct hv_pcibus_device {
struct pci_sysdata sysdata;
+ /* Protocol version negotiated with the host */
+ enum pci_protocol_version_t protocol_version;
enum hv_pcibus_state state;
refcount_t remove_lock;
struct hv_device *hdev;
@@ -942,7 +939,7 @@ static void hv_irq_unmask(struct irq_data *data)
* negative effect (yet?).
*/
- if (pci_protocol_version >= PCI_PROTOCOL_VERSION_1_2) {
+ if (hbus->protocol_version >= PCI_PROTOCOL_VERSION_1_2) {
/*
* PCI_PROTOCOL_VERSION_1_2 supports the VP_SET version of the
* HVCALL_RETARGET_INTERRUPT hypercall, which also coincides
@@ -1112,7 +1109,7 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
ctxt.pci_pkt.completion_func = hv_pci_compose_compl;
ctxt.pci_pkt.compl_ctxt = ∁
- switch (pci_protocol_version) {
+ switch (hbus->protocol_version) {
case PCI_PROTOCOL_VERSION_1_1:
size = hv_compose_msi_req_v1(&ctxt.int_pkts.v1,
dest,
@@ -2116,6 +2113,7 @@ static int hv_pci_protocol_negotiation(struct hv_device *hdev,
enum pci_protocol_version_t version[],
int num_version)
{
+ struct hv_pcibus_device *hbus = hv_get_drvdata(hdev);
struct pci_version_request *version_req;
struct hv_pci_compl comp_pkt;
struct pci_packet *pkt;
@@ -2155,10 +2153,10 @@ static int hv_pci_protocol_negotiation(struct hv_device *hdev,
}
if (comp_pkt.completion_status >= 0) {
- pci_protocol_version = version[i];
+ hbus->protocol_version = version[i];
dev_info(&hdev->device,
"PCI VMBus probing: Using version %#x\n",
- pci_protocol_version);
+ hbus->protocol_version);
goto exit;
}
@@ -2442,7 +2440,7 @@ static int hv_send_resources_allocated(struct hv_device *hdev)
u32 wslot;
int ret;
- size_res = (pci_protocol_version < PCI_PROTOCOL_VERSION_1_2)
+ size_res = (hbus->protocol_version < PCI_PROTOCOL_VERSION_1_2)
? sizeof(*res_assigned) : sizeof(*res_assigned2);
pkt = kmalloc(sizeof(*pkt) + size_res, GFP_KERNEL);
@@ -2461,7 +2459,7 @@ static int hv_send_resources_allocated(struct hv_device *hdev)
pkt->completion_func = hv_pci_generic_compl;
pkt->compl_ctxt = &comp_pkt;
- if (pci_protocol_version < PCI_PROTOCOL_VERSION_1_2) {
+ if (hbus->protocol_version < PCI_PROTOCOL_VERSION_1_2) {
res_assigned =
(struct pci_resources_assigned *)&pkt->message;
res_assigned->message_type.type =
@@ -2812,7 +2810,7 @@ static int hv_pci_resume(struct hv_device *hdev)
return ret;
/* Only use the version that was in use before hibernation. */
- version[0] = pci_protocol_version;
+ version[0] = hbus->protocol_version;
ret = hv_pci_protocol_negotiation(hdev, version, 1);
if (ret)
goto out;
--
1.8.3.1
^ permalink raw reply related
* [PATCH 3/4] PCI: hv: Do not queue new work items on hibernation
From: Dexuan Cui @ 2019-09-11 23:38 UTC (permalink / raw)
To: KY Srinivasan, Haiyang Zhang, Stephen Hemminger,
sashal@kernel.org, lorenzo.pieralisi@arm.com, bhelgaas@google.com,
linux-hyperv@vger.kernel.org, linux-pci@vger.kernel.org,
linux-kernel@vger.kernel.org, Michael Kelley
Cc: Dexuan Cui
In-Reply-To: <1568245086-70601-1-git-send-email-decui@microsoft.com>
We must make sure there is no pending work items before we call
vmbus_close().
Signed-off-by: Dexuan Cui <decui@microsoft.com>
---
drivers/pci/controller/pci-hyperv.c | 33 ++++++++++++++++++++++++++++++---
1 file changed, 30 insertions(+), 3 deletions(-)
diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c
index 3b77a3a..2655df2 100644
--- a/drivers/pci/controller/pci-hyperv.c
+++ b/drivers/pci/controller/pci-hyperv.c
@@ -422,6 +422,7 @@ enum hv_pcibus_state {
hv_pcibus_init = 0,
hv_pcibus_probed,
hv_pcibus_installed,
+ hv_pcibus_removing,
hv_pcibus_removed,
hv_pcibus_maximum
};
@@ -1841,6 +1842,12 @@ static void hv_pci_devices_present(struct hv_pcibus_device *hbus,
unsigned long flags;
bool pending_dr;
+ if (hbus->state == hv_pcibus_removing) {
+ dev_info(&hbus->hdev->device,
+ "PCI VMBus BUS_RELATIONS: ignored\n");
+ return;
+ }
+
dr_wrk = kzalloc(sizeof(*dr_wrk), GFP_NOWAIT);
if (!dr_wrk)
return;
@@ -1957,11 +1964,19 @@ static void hv_eject_device_work(struct work_struct *work)
*/
static void hv_pci_eject_device(struct hv_pci_dev *hpdev)
{
+ struct hv_pcibus_device *hbus = hpdev->hbus;
+ struct hv_device *hdev = hbus->hdev;
+
+ if (hbus->state == hv_pcibus_removing) {
+ dev_info(&hdev->device, "PCI VMBus EJECT: ignored\n");
+ return;
+ }
+
hpdev->state = hv_pcichild_ejecting;
get_pcichild(hpdev);
INIT_WORK(&hpdev->wrk, hv_eject_device_work);
- get_hvpcibus(hpdev->hbus);
- queue_work(hpdev->hbus->wq, &hpdev->wrk);
+ get_hvpcibus(hbus);
+ queue_work(hbus->wq, &hpdev->wrk);
}
/**
@@ -2757,9 +2772,21 @@ static int hv_pci_remove(struct hv_device *hdev)
static int hv_pci_suspend(struct hv_device *hdev)
{
struct hv_pcibus_device *hbus = hv_get_drvdata(hdev);
+ enum hv_pcibus_state old_state;
int ret;
- /* XXX: Need to prevent any new work from being queued. */
+ tasklet_disable(&hdev->channel->callback_event);
+
+ /* Change the hbus state to prevent new work items. */
+ old_state = hbus->state;
+ if (hbus->state == hv_pcibus_installed)
+ hbus->state = hv_pcibus_removing;
+
+ tasklet_enable(&hdev->channel->callback_event);
+
+ if (old_state != hv_pcibus_installed)
+ return -EINVAL;
+
flush_workqueue(hbus->wq);
ret = hv_pci_bus_exit(hdev, true);
--
1.8.3.1
^ permalink raw reply related
* [PATCH 2/4] PCI: hv: Add the support of hibernation
From: Dexuan Cui @ 2019-09-11 23:38 UTC (permalink / raw)
To: KY Srinivasan, Haiyang Zhang, Stephen Hemminger,
sashal@kernel.org, lorenzo.pieralisi@arm.com, bhelgaas@google.com,
linux-hyperv@vger.kernel.org, linux-pci@vger.kernel.org,
linux-kernel@vger.kernel.org, Michael Kelley
Cc: Dexuan Cui
In-Reply-To: <1568245086-70601-1-git-send-email-decui@microsoft.com>
Implement the suspend/resume callbacks for hibernation.
hv_pci_suspend() needs to prevent any new work from being queued: a later
patch will address this issue.
Signed-off-by: Dexuan Cui <decui@microsoft.com>
---
drivers/pci/controller/pci-hyperv.c | 76 +++++++++++++++++++++++++++++++++++++
1 file changed, 76 insertions(+)
diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c
index 03fa039..3b77a3a 100644
--- a/drivers/pci/controller/pci-hyperv.c
+++ b/drivers/pci/controller/pci-hyperv.c
@@ -1398,6 +1398,23 @@ static void prepopulate_bars(struct hv_pcibus_device *hbus)
spin_lock_irqsave(&hbus->device_list_lock, flags);
+ /*
+ * Clear the memory enable bit, in case it's already set. This occurs
+ * in the suspend path of hibernation, where the device is suspended,
+ * resumed and suspended again: see hibernation_snapshot() and
+ * hibernation_platform_enter().
+ *
+ * If the memory enable bit is already set, Hyper-V sliently ignores
+ * the below BAR updates, and the related PCI device driver can not
+ * work, because reading from the device register(s) always returns
+ * 0xFFFFFFFF.
+ */
+ list_for_each_entry(hpdev, &hbus->children, list_entry) {
+ _hv_pcifront_read_config(hpdev, PCI_COMMAND, 2, &command);
+ command &= ~PCI_COMMAND_MEMORY;
+ _hv_pcifront_write_config(hpdev, PCI_COMMAND, 2, command);
+ }
+
/* Pick addresses for the BARs. */
do {
list_for_each_entry(hpdev, &hbus->children, list_entry) {
@@ -2737,6 +2754,63 @@ static int hv_pci_remove(struct hv_device *hdev)
return ret;
}
+static int hv_pci_suspend(struct hv_device *hdev)
+{
+ struct hv_pcibus_device *hbus = hv_get_drvdata(hdev);
+ int ret;
+
+ /* XXX: Need to prevent any new work from being queued. */
+ flush_workqueue(hbus->wq);
+
+ ret = hv_pci_bus_exit(hdev, true);
+ if (ret)
+ return ret;
+
+ vmbus_close(hdev->channel);
+
+ return 0;
+}
+
+static int hv_pci_resume(struct hv_device *hdev)
+{
+ struct hv_pcibus_device *hbus = hv_get_drvdata(hdev);
+ enum pci_protocol_version_t version[1];
+ int ret;
+
+ hbus->state = hv_pcibus_init;
+
+ ret = vmbus_open(hdev->channel, pci_ring_size, pci_ring_size, NULL, 0,
+ hv_pci_onchannelcallback, hbus);
+ if (ret)
+ return ret;
+
+ /* Only use the version that was in use before hibernation. */
+ version[0] = pci_protocol_version;
+ ret = hv_pci_protocol_negotiation(hdev, version, 1);
+ if (ret)
+ goto out;
+
+ ret = hv_pci_query_relations(hdev);
+ if (ret)
+ goto out;
+
+ ret = hv_pci_enter_d0(hdev);
+ if (ret)
+ goto out;
+
+ ret = hv_send_resources_allocated(hdev);
+ if (ret)
+ goto out;
+
+ prepopulate_bars(hbus);
+
+ hbus->state = hv_pcibus_installed;
+ return 0;
+out:
+ vmbus_close(hdev->channel);
+ return ret;
+}
+
static const struct hv_vmbus_device_id hv_pci_id_table[] = {
/* PCI Pass-through Class ID */
/* 44C4F61D-4444-4400-9D52-802E27EDE19F */
@@ -2751,6 +2825,8 @@ static int hv_pci_remove(struct hv_device *hdev)
.id_table = hv_pci_id_table,
.probe = hv_pci_probe,
.remove = hv_pci_remove,
+ .suspend = hv_pci_suspend,
+ .resume = hv_pci_resume,
};
static void __exit exit_hv_pci_drv(void)
--
1.8.3.1
^ permalink raw reply related
* [PATCH 1/4] PCI: hv: Reorganize the code in preparation of hibernation
From: Dexuan Cui @ 2019-09-11 23:38 UTC (permalink / raw)
To: KY Srinivasan, Haiyang Zhang, Stephen Hemminger,
sashal@kernel.org, lorenzo.pieralisi@arm.com, bhelgaas@google.com,
linux-hyperv@vger.kernel.org, linux-pci@vger.kernel.org,
linux-kernel@vger.kernel.org, Michael Kelley
Cc: Dexuan Cui
In-Reply-To: <1568245086-70601-1-git-send-email-decui@microsoft.com>
There is no functional change. This is just preparatory to a later
patch which adds the hibernation support for the pci-hyperv driver.
Signed-off-by: Dexuan Cui <decui@microsoft.com>
---
drivers/pci/controller/pci-hyperv.c | 43 ++++++++++++++++++++++++-------------
1 file changed, 28 insertions(+), 15 deletions(-)
diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c
index 40b6254..03fa039 100644
--- a/drivers/pci/controller/pci-hyperv.c
+++ b/drivers/pci/controller/pci-hyperv.c
@@ -2080,7 +2080,9 @@ static void hv_pci_onchannelcallback(void *context)
* failing if the host doesn't support the necessary protocol
* level.
*/
-static int hv_pci_protocol_negotiation(struct hv_device *hdev)
+static int hv_pci_protocol_negotiation(struct hv_device *hdev,
+ enum pci_protocol_version_t version[],
+ int num_version)
{
struct pci_version_request *version_req;
struct hv_pci_compl comp_pkt;
@@ -2104,8 +2106,8 @@ static int hv_pci_protocol_negotiation(struct hv_device *hdev)
version_req = (struct pci_version_request *)&pkt->message;
version_req->message_type.type = PCI_QUERY_PROTOCOL_VERSION;
- for (i = 0; i < ARRAY_SIZE(pci_protocol_versions); i++) {
- version_req->protocol_version = pci_protocol_versions[i];
+ for (i = 0; i < num_version; i++) {
+ version_req->protocol_version = version[i];
ret = vmbus_sendpacket(hdev->channel, version_req,
sizeof(struct pci_version_request),
(unsigned long)pkt, VM_PKT_DATA_INBAND,
@@ -2121,7 +2123,7 @@ static int hv_pci_protocol_negotiation(struct hv_device *hdev)
}
if (comp_pkt.completion_status >= 0) {
- pci_protocol_version = pci_protocol_versions[i];
+ pci_protocol_version = version[i];
dev_info(&hdev->device,
"PCI VMBus probing: Using version %#x\n",
pci_protocol_version);
@@ -2572,7 +2574,8 @@ static int hv_pci_probe(struct hv_device *hdev,
hv_set_drvdata(hdev, hbus);
- ret = hv_pci_protocol_negotiation(hdev);
+ ret = hv_pci_protocol_negotiation(hdev, pci_protocol_versions,
+ ARRAY_SIZE(pci_protocol_versions));
if (ret)
goto close;
@@ -2644,7 +2647,7 @@ static int hv_pci_probe(struct hv_device *hdev,
return ret;
}
-static void hv_pci_bus_exit(struct hv_device *hdev)
+static int hv_pci_bus_exit(struct hv_device *hdev, bool hibernating)
{
struct hv_pcibus_device *hbus = hv_get_drvdata(hdev);
struct {
@@ -2660,16 +2663,20 @@ static void hv_pci_bus_exit(struct hv_device *hdev)
* access the per-channel ringbuffer any longer.
*/
if (hdev->channel->rescind)
- return;
+ return 0;
- /* Delete any children which might still exist. */
- memset(&relations, 0, sizeof(relations));
- hv_pci_devices_present(hbus, &relations);
+ if (!hibernating) {
+ /* Delete any children which might still exist. */
+ memset(&relations, 0, sizeof(relations));
+ hv_pci_devices_present(hbus, &relations);
+ }
ret = hv_send_resources_released(hdev);
- if (ret)
+ if (ret) {
dev_err(&hdev->device,
"Couldn't send resources released packet(s)\n");
+ return ret;
+ }
memset(&pkt.teardown_packet, 0, sizeof(pkt.teardown_packet));
init_completion(&comp_pkt.host_event);
@@ -2682,8 +2689,13 @@ static void hv_pci_bus_exit(struct hv_device *hdev)
(unsigned long)&pkt.teardown_packet,
VM_PKT_DATA_INBAND,
VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
- if (!ret)
- wait_for_completion_timeout(&comp_pkt.host_event, 10 * HZ);
+ if (ret)
+ return ret;
+
+ if (wait_for_completion_timeout(&comp_pkt.host_event, 10 * HZ) == 0)
+ return -ETIMEDOUT;
+
+ return 0;
}
/**
@@ -2695,6 +2707,7 @@ static void hv_pci_bus_exit(struct hv_device *hdev)
static int hv_pci_remove(struct hv_device *hdev)
{
struct hv_pcibus_device *hbus;
+ int ret;
hbus = hv_get_drvdata(hdev);
if (hbus->state == hv_pcibus_installed) {
@@ -2707,7 +2720,7 @@ static int hv_pci_remove(struct hv_device *hdev)
hbus->state = hv_pcibus_removed;
}
- hv_pci_bus_exit(hdev);
+ ret = hv_pci_bus_exit(hdev, false);
vmbus_close(hdev->channel);
@@ -2721,7 +2734,7 @@ static int hv_pci_remove(struct hv_device *hdev)
wait_for_completion(&hbus->remove_event);
destroy_workqueue(hbus->wq);
free_page((unsigned long)hbus);
- return 0;
+ return ret;
}
static const struct hv_vmbus_device_id hv_pci_id_table[] = {
--
1.8.3.1
^ permalink raw reply related
* [PATCH 0/3] Enhance hv_utils to support hibernation
From: Dexuan Cui @ 2019-09-11 23:38 UTC (permalink / raw)
To: KY Srinivasan, Haiyang Zhang, Stephen Hemminger,
sashal@kernel.org, linux-hyperv@vger.kernel.org,
linux-kernel@vger.kernel.org, Michael Kelley
Cc: Dexuan Cui
This patch is basically a pure Hyper-V specific change and it has a
build dependency on the commit 271b2224d42f ("Drivers: hv: vmbus: Implement
suspend/resume for VSC drivers for hibernation"), which is on Sasha Levin's
Hyper-V tree's hyperv-next branch:
https://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux.git/log/?h=hyperv-next
I request this patch should go through Sasha's tree rather than the
char-misc tree.
Dexuan Cui (3):
hv_utils: Add the support of hibernation
hv_utils: Support host-initiated hibernation request
hv_utils: Support host-initiated restart request
drivers/hv/hv_fcopy.c | 9 +++-
drivers/hv/hv_kvp.c | 11 +++-
drivers/hv/hv_snapshot.c | 11 +++-
drivers/hv/hv_util.c | 124 +++++++++++++++++++++++++++++++++++++++++++++-
drivers/hv/hyperv_vmbus.h | 3 ++
include/linux/hyperv.h | 1 +
6 files changed, 152 insertions(+), 7 deletions(-)
--
1.8.3.1
^ permalink raw reply
* [PATCH 3/3] hv_utils: Support host-initiated restart request
From: Dexuan Cui @ 2019-09-11 23:39 UTC (permalink / raw)
To: KY Srinivasan, Haiyang Zhang, Stephen Hemminger,
sashal@kernel.org, linux-hyperv@vger.kernel.org,
linux-kernel@vger.kernel.org, Michael Kelley
Cc: Dexuan Cui
In-Reply-To: <1568245130-70712-1-git-send-email-decui@microsoft.com>
To test the code, we should run this command on the host:
Restart-VM $vm -Type Reboot
Signed-off-by: Dexuan Cui <decui@microsoft.com>
---
drivers/hv/hv_util.c | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c
index 9e98c5d..6d642f5 100644
--- a/drivers/hv/hv_util.c
+++ b/drivers/hv/hv_util.c
@@ -24,7 +24,9 @@
#define SD_MAJOR 3
#define SD_MINOR 0
+#define SD_MINOR_1 1
#define SD_MINOR_2 2
+#define SD_VERSION_3_1 (SD_MAJOR << 16 | SD_MINOR_1)
#define SD_VERSION_3_2 (SD_MAJOR << 16 | SD_MINOR_2)
#define SD_VERSION (SD_MAJOR << 16 | SD_MINOR)
@@ -52,9 +54,10 @@
static int ts_srv_version;
static int hb_srv_version;
-#define SD_VER_COUNT 3
+#define SD_VER_COUNT 4
static const int sd_versions[] = {
SD_VERSION_3_2,
+ SD_VERSION_3_1,
SD_VERSION,
SD_VERSION_1
};
@@ -147,6 +150,11 @@ static void perform_shutdown(struct work_struct *dummy)
orderly_poweroff(true);
}
+static void perform_restart(struct work_struct *dummy)
+{
+ orderly_reboot();
+}
+
static void perform_hibernation(struct work_struct *dummy)
{
/*
@@ -175,6 +183,11 @@ static void perform_hibernation(struct work_struct *dummy)
static DECLARE_WORK(shutdown_work, perform_shutdown);
/*
+ * Perform the restart operation in a thread context.
+ */
+static DECLARE_WORK(restart_work, perform_restart);
+
+/*
* Perform the hibernation operation in a thread context.
*/
static DECLARE_WORK(hibernate_work, perform_hibernation);
@@ -222,6 +235,14 @@ static void shutdown_onchannelcallback(void *context)
pr_info("Shutdown request received -"
" graceful shutdown initiated\n");
break;
+ case 2:
+ case 3:
+ pr_info("Restart request received -"
+ " graceful restart initiated\n");
+ icmsghdrp->status = HV_S_OK;
+
+ schedule_work(&restart_work);
+ break;
case 4:
case 5:
pr_info("Hibernation request received -"
--
1.8.3.1
^ permalink raw reply related
* [PATCH 2/3] hv_utils: Support host-initiated hibernation request
From: Dexuan Cui @ 2019-09-11 23:38 UTC (permalink / raw)
To: KY Srinivasan, Haiyang Zhang, Stephen Hemminger,
sashal@kernel.org, linux-hyperv@vger.kernel.org,
linux-kernel@vger.kernel.org, Michael Kelley
Cc: Dexuan Cui
In-Reply-To: <1568245130-70712-1-git-send-email-decui@microsoft.com>
Update the Shutdown IC version to 3.2, which is required for the host to
send the hibernation request.
The user is expected to create the program "/sbin/hyperv-hibernate", which
is called on the host-initiated hibernation request.
The program can be a script like
test@localhost:~$ cat /sbin/hyperv-hibernate
#!/bin/bash
echo disk > /sys/power/state
Signed-off-by: Dexuan Cui <decui@microsoft.com>
---
drivers/hv/hv_util.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 65 insertions(+), 1 deletion(-)
diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c
index 039c752..9e98c5d 100644
--- a/drivers/hv/hv_util.c
+++ b/drivers/hv/hv_util.c
@@ -24,6 +24,8 @@
#define SD_MAJOR 3
#define SD_MINOR 0
+#define SD_MINOR_2 2
+#define SD_VERSION_3_2 (SD_MAJOR << 16 | SD_MINOR_2)
#define SD_VERSION (SD_MAJOR << 16 | SD_MINOR)
#define SD_MAJOR_1 1
@@ -50,8 +52,9 @@
static int ts_srv_version;
static int hb_srv_version;
-#define SD_VER_COUNT 2
+#define SD_VER_COUNT 3
static const int sd_versions[] = {
+ SD_VERSION_3_2,
SD_VERSION,
SD_VERSION_1
};
@@ -75,9 +78,30 @@
UTIL_WS2K8_FW_VERSION
};
+static bool execute_hibernate;
+static int hv_shutdown_init(struct hv_util_service *srv)
+{
+#if 0
+ /*
+ * The patch to implement hv_is_hibernation_supported() is going
+ * through the tip tree. For now, let's hardcode execute_hibernate
+ * to true -- this doesn't break anything since hibernation for
+ * Linux VM on Hyper-V never worked before. We'll remove the
+ * conditional compilation as soon as hv_is_hibernation_supported()
+ * is available in the mainline tree.
+ */
+ execute_hibernate = hv_is_hibernation_supported();
+#else
+ execute_hibernate = true;
+#endif
+
+ return 0;
+}
+
static void shutdown_onchannelcallback(void *context);
static struct hv_util_service util_shutdown = {
.util_cb = shutdown_onchannelcallback,
+ .util_init = hv_shutdown_init,
};
static int hv_timesync_init(struct hv_util_service *srv);
@@ -123,11 +147,38 @@ static void perform_shutdown(struct work_struct *dummy)
orderly_poweroff(true);
}
+static void perform_hibernation(struct work_struct *dummy)
+{
+ /*
+ * The user is expected to create the program, which can be a simple
+ * script containing two lines:
+ * #!/bin/bash
+ * echo disk > /sys/power/state
+ */
+ static char hibernate_cmd[PATH_MAX] = "/sbin/hyperv-hibernate";
+
+ static char *envp[] = {
+ NULL,
+ };
+
+ static char *argv[] = {
+ hibernate_cmd,
+ NULL,
+ };
+
+ call_usermodehelper(hibernate_cmd, argv, envp, UMH_NO_WAIT);
+}
+
/*
* Perform the shutdown operation in a thread context.
*/
static DECLARE_WORK(shutdown_work, perform_shutdown);
+/*
+ * Perform the hibernation operation in a thread context.
+ */
+static DECLARE_WORK(hibernate_work, perform_hibernation);
+
static void shutdown_onchannelcallback(void *context)
{
struct vmbus_channel *channel = context;
@@ -171,6 +222,19 @@ static void shutdown_onchannelcallback(void *context)
pr_info("Shutdown request received -"
" graceful shutdown initiated\n");
break;
+ case 4:
+ case 5:
+ pr_info("Hibernation request received -"
+ " hibernation %sinitiated\n",
+ execute_hibernate ? "" : "not ");
+
+ if (execute_hibernate) {
+ icmsghdrp->status = HV_S_OK;
+ schedule_work(&hibernate_work);
+ } else {
+ icmsghdrp->status = HV_E_FAIL;
+ }
+ break;
default:
icmsghdrp->status = HV_E_FAIL;
execute_shutdown = false;
--
1.8.3.1
^ permalink raw reply related
* [PATCH 1/3] hv_utils: Add the support of hibernation
From: Dexuan Cui @ 2019-09-11 23:38 UTC (permalink / raw)
To: KY Srinivasan, Haiyang Zhang, Stephen Hemminger,
sashal@kernel.org, linux-hyperv@vger.kernel.org,
linux-kernel@vger.kernel.org, Michael Kelley
Cc: Dexuan Cui
In-Reply-To: <1568245130-70712-1-git-send-email-decui@microsoft.com>
On hibernation, Linux can not guarantee the host side utils operations
still succeed without any issue, so let's simply cancel the work items.
The host is supposed to retry the operations, if necessary.
Signed-off-by: Dexuan Cui <decui@microsoft.com>
---
drivers/hv/hv_fcopy.c | 9 ++++++++-
drivers/hv/hv_kvp.c | 11 +++++++++--
drivers/hv/hv_snapshot.c | 11 +++++++++--
drivers/hv/hv_util.c | 37 ++++++++++++++++++++++++++++++++++++-
drivers/hv/hyperv_vmbus.h | 3 +++
include/linux/hyperv.h | 1 +
6 files changed, 66 insertions(+), 6 deletions(-)
diff --git a/drivers/hv/hv_fcopy.c b/drivers/hv/hv_fcopy.c
index 7e30ae0..f44df3d 100644
--- a/drivers/hv/hv_fcopy.c
+++ b/drivers/hv/hv_fcopy.c
@@ -345,9 +345,16 @@ int hv_fcopy_init(struct hv_util_service *srv)
return 0;
}
+void hv_fcopy_cancel_work(void)
+{
+ cancel_delayed_work_sync(&fcopy_timeout_work);
+}
+
void hv_fcopy_deinit(void)
{
fcopy_transaction.state = HVUTIL_DEVICE_DYING;
- cancel_delayed_work_sync(&fcopy_timeout_work);
+
+ hv_fcopy_cancel_work();
+
hvutil_transport_destroy(hvt);
}
diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c
index 5054d11..064c384 100644
--- a/drivers/hv/hv_kvp.c
+++ b/drivers/hv/hv_kvp.c
@@ -757,11 +757,18 @@ static void kvp_on_reset(void)
return 0;
}
-void hv_kvp_deinit(void)
+void hv_kvp_cancel_work(void)
{
- kvp_transaction.state = HVUTIL_DEVICE_DYING;
cancel_delayed_work_sync(&kvp_host_handshake_work);
cancel_delayed_work_sync(&kvp_timeout_work);
cancel_work_sync(&kvp_sendkey_work);
+}
+
+void hv_kvp_deinit(void)
+{
+ kvp_transaction.state = HVUTIL_DEVICE_DYING;
+
+ hv_kvp_cancel_work();
+
hvutil_transport_destroy(hvt);
}
diff --git a/drivers/hv/hv_snapshot.c b/drivers/hv/hv_snapshot.c
index 20ba95b..0eb718a 100644
--- a/drivers/hv/hv_snapshot.c
+++ b/drivers/hv/hv_snapshot.c
@@ -378,10 +378,17 @@ static void vss_on_reset(void)
return 0;
}
-void hv_vss_deinit(void)
+void hv_vss_cancel_work(void)
{
- vss_transaction.state = HVUTIL_DEVICE_DYING;
cancel_delayed_work_sync(&vss_timeout_work);
cancel_work_sync(&vss_handle_request_work);
+}
+
+void hv_vss_deinit(void)
+{
+ vss_transaction.state = HVUTIL_DEVICE_DYING;
+
+ hv_vss_cancel_work();
+
hvutil_transport_destroy(hvt);
}
diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c
index e32681e..039c752 100644
--- a/drivers/hv/hv_util.c
+++ b/drivers/hv/hv_util.c
@@ -81,12 +81,14 @@
};
static int hv_timesync_init(struct hv_util_service *srv);
+static void hv_timesync_cancel_work(void);
static void hv_timesync_deinit(void);
static void timesync_onchannelcallback(void *context);
static struct hv_util_service util_timesynch = {
.util_cb = timesync_onchannelcallback,
.util_init = hv_timesync_init,
+ .util_cancel_work = hv_timesync_cancel_work,
.util_deinit = hv_timesync_deinit,
};
@@ -98,18 +100,21 @@
static struct hv_util_service util_kvp = {
.util_cb = hv_kvp_onchannelcallback,
.util_init = hv_kvp_init,
+ .util_cancel_work = hv_kvp_cancel_work,
.util_deinit = hv_kvp_deinit,
};
static struct hv_util_service util_vss = {
.util_cb = hv_vss_onchannelcallback,
.util_init = hv_vss_init,
+ .util_cancel_work = hv_vss_cancel_work,
.util_deinit = hv_vss_deinit,
};
static struct hv_util_service util_fcopy = {
.util_cb = hv_fcopy_onchannelcallback,
.util_init = hv_fcopy_init,
+ .util_cancel_work = hv_fcopy_cancel_work,
.util_deinit = hv_fcopy_deinit,
};
@@ -440,6 +445,28 @@ static int util_remove(struct hv_device *dev)
return 0;
}
+static int util_suspend(struct hv_device *dev)
+{
+ struct hv_util_service *srv = hv_get_drvdata(dev);
+
+ if (srv->util_cancel_work)
+ srv->util_cancel_work();
+
+ vmbus_close(dev->channel);
+
+ return 0;
+}
+
+static int util_resume(struct hv_device *dev)
+{
+ struct hv_util_service *srv = hv_get_drvdata(dev);
+ int ret;
+
+ ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE,
+ NULL, 0, srv->util_cb, dev->channel);
+ return ret;
+}
+
static const struct hv_vmbus_device_id id_table[] = {
/* Shutdown guid */
{ HV_SHUTDOWN_GUID,
@@ -476,6 +503,8 @@ static int util_remove(struct hv_device *dev)
.id_table = id_table,
.probe = util_probe,
.remove = util_remove,
+ .suspend = util_suspend,
+ .resume = util_resume,
.driver = {
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
@@ -545,11 +574,17 @@ static int hv_timesync_init(struct hv_util_service *srv)
return 0;
}
+static void hv_timesync_cancel_work(void)
+{
+ cancel_work_sync(&adj_time_work);
+}
+
static void hv_timesync_deinit(void)
{
if (hv_ptp_clock)
ptp_clock_unregister(hv_ptp_clock);
- cancel_work_sync(&adj_time_work);
+
+ hv_timesync_cancel_work();
}
static int __init init_hyperv_utils(void)
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
index f7a5f56..dc280fa 100644
--- a/drivers/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
@@ -353,14 +353,17 @@ int vmbus_add_channel_kobj(struct hv_device *device_obj,
void vmbus_on_msg_dpc(unsigned long data);
int hv_kvp_init(struct hv_util_service *srv);
+void hv_kvp_cancel_work(void);
void hv_kvp_deinit(void);
void hv_kvp_onchannelcallback(void *context);
int hv_vss_init(struct hv_util_service *srv);
+void hv_vss_cancel_work(void);
void hv_vss_deinit(void);
void hv_vss_onchannelcallback(void *context);
int hv_fcopy_init(struct hv_util_service *srv);
+void hv_fcopy_cancel_work(void);
void hv_fcopy_deinit(void);
void hv_fcopy_onchannelcallback(void *context);
void vmbus_initiate_unload(bool crash);
diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
index a3aa9e9..b4e2768 100644
--- a/include/linux/hyperv.h
+++ b/include/linux/hyperv.h
@@ -1412,6 +1412,7 @@ struct hv_util_service {
void (*util_cb)(void *);
int (*util_init)(struct hv_util_service *);
void (*util_deinit)(void);
+ void (*util_cancel_work)(void);
};
struct vmbuspipe_hdr {
--
1.8.3.1
^ permalink raw reply related
* Re: [PATCH] hv_balloon: Add the support of hibernation
From: David Hildenbrand @ 2019-09-12 10:08 UTC (permalink / raw)
To: Dexuan Cui, KY Srinivasan, Haiyang Zhang, Stephen Hemminger,
sashal@kernel.org, linux-hyperv@vger.kernel.org,
linux-kernel@vger.kernel.org, Michael Kelley
In-Reply-To: <1568245010-66879-1-git-send-email-decui@microsoft.com>
On 12.09.19 01:36, Dexuan Cui wrote:
> When hibernation is enabled, we must ignore the balloon up/down and
> hot-add requests from the host, if any.
>
> Fow now, if people want to test hibernation, please blacklist hv_balloon
> or do not enable Dynamic Memory and Memory Resizing. See the comment in
> balloon_probe() for more info.
>
Why do you even care about supporting hibernation? Can't you just pause
the VM in the hypervisor and continue to live a happy life? :)
> Signed-off-by: Dexuan Cui <decui@microsoft.com>
> ---
>
> This patch is basically a pure Hyper-V specific change and it has a
> build dependency on the commit 271b2224d42f ("Drivers: hv: vmbus: Implement
> suspend/resume for VSC drivers for hibernation"), which is on Sasha Levin's
> Hyper-V tree's hyperv-next branch:
> https://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux.git/log/?h=hyperv-next
>
> I request this patch should go through Sasha's tree rather than the
> other tree(s).
>
> drivers/hv/hv_balloon.c | 101 +++++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 99 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c
> index 34bd735..7df0f67 100644
> --- a/drivers/hv/hv_balloon.c
> +++ b/drivers/hv/hv_balloon.c
> @@ -24,6 +24,8 @@
>
> #include <linux/hyperv.h>
>
> +#include <asm/mshyperv.h>
> +
> #define CREATE_TRACE_POINTS
> #include "hv_trace_balloon.h"
>
> @@ -457,6 +459,7 @@ struct hot_add_wrk {
> struct work_struct wrk;
> };
>
> +static bool allow_hibernation;
> static bool hot_add = true;
> static bool do_hot_add;
> /*
> @@ -1053,8 +1056,12 @@ static void hot_add_req(struct work_struct *dummy)
> else
> resp.result = 0;
>
> - if (!do_hot_add || (resp.page_count == 0))
> - pr_err("Memory hot add failed\n");
> + if (!do_hot_add || resp.page_count == 0) {
> + if (!allow_hibernation)
> + pr_err("Memory hot add failed\n");
> + else
> + pr_info("Ignore hot-add request!\n");
> + }
>
> dm->state = DM_INITIALIZED;
> resp.hdr.trans_id = atomic_inc_return(&trans_id);
> @@ -1509,6 +1516,11 @@ static void balloon_onchannelcallback(void *context)
> break;
>
> case DM_BALLOON_REQUEST:
> + if (allow_hibernation) {
> + pr_info("Ignore balloon-up request!\n");
> + break;
> + }
> +
> if (dm->state == DM_BALLOON_UP)
> pr_warn("Currently ballooning\n");
> bal_msg = (struct dm_balloon *)recv_buffer;
> @@ -1518,6 +1530,11 @@ static void balloon_onchannelcallback(void *context)
> break;
>
> case DM_UNBALLOON_REQUEST:
> + if (allow_hibernation) {
> + pr_info("Ignore balloon-down request!\n");
> + break;
> + }
> +
> dm->state = DM_BALLOON_DOWN;
> balloon_down(dm,
> (struct dm_unballoon_request *)recv_buffer);
> @@ -1623,6 +1640,11 @@ static int balloon_connect_vsp(struct hv_device *dev)
> cap_msg.hdr.size = sizeof(struct dm_capabilities);
> 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.hot_add = 1;
>
> @@ -1672,6 +1694,24 @@ static int balloon_probe(struct hv_device *dev,
> {
> int ret;
>
> +#if 0
I am not sure if that's a good idea. Can't you base this series on
hv_is_hibernation_supported() ?
> + /*
> + * The patch to implement hv_is_hibernation_supported() is going
> + * through the tip tree. For now, let's hardcode allow_hibernation
> + * to false to keep the current behavior of hv_balloon. If people
> + * want to test hibernation, please blacklist hv_balloon fow now
> + * or do not enable Dynamid Memory and Memory Resizing.
> + *
> + * We'll remove the conditional compilation as soon as
> + * hv_is_hibernation_supported() is available in the mainline tree.
> + */
> + allow_hibernation = hv_is_hibernation_supported();
> +#else
> + allow_hibernation = false;
> +#endif
> + if (allow_hibernation)
> + hot_add = false;
> +
> #ifdef CONFIG_MEMORY_HOTPLUG
> do_hot_add = hot_add;
> #else
> @@ -1711,6 +1751,8 @@ static int balloon_probe(struct hv_device *dev,
> return 0;
>
> probe_error:
> + dm_device.state = DM_INIT_ERROR;
> + dm_device.thread = NULL;
> vmbus_close(dev->channel);
> #ifdef CONFIG_MEMORY_HOTPLUG
> unregister_memory_notifier(&hv_memory_nb);
> @@ -1752,6 +1794,59 @@ static int balloon_remove(struct hv_device *dev)
> 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[] = {
> /* Dynamic Memory Class ID */
> /* 525074DC-8985-46e2-8057-A307DC18A502 */
> @@ -1766,6 +1861,8 @@ static int balloon_remove(struct hv_device *dev)
> .id_table = id_table,
> .probe = balloon_probe,
> .remove = balloon_remove,
> + .suspend = balloon_suspend,
> + .resume = balloon_resume,
> .driver = {
> .probe_type = PROBE_PREFER_ASYNCHRONOUS,
> },
>
--
Thanks,
David / dhildenb
^ permalink raw reply
* Re: [PATCH] hv_balloon: Add the support of hibernation
From: David Hildenbrand @ 2019-09-12 10:11 UTC (permalink / raw)
To: Dexuan Cui, KY Srinivasan, Haiyang Zhang, Stephen Hemminger,
sashal@kernel.org, linux-hyperv@vger.kernel.org,
linux-kernel@vger.kernel.org, Michael Kelley
In-Reply-To: <42de5835-8faa-2047-0f77-db51dd57b036@redhat.com>
On 12.09.19 12:08, David Hildenbrand wrote:
> On 12.09.19 01:36, Dexuan Cui wrote:
>> When hibernation is enabled, we must ignore the balloon up/down and
>> hot-add requests from the host, if any.
>>
>> Fow now, if people want to test hibernation, please blacklist hv_balloon
>> or do not enable Dynamic Memory and Memory Resizing. See the comment in
>> balloon_probe() for more info.
>>
>
> Why do you even care about supporting hibernation? Can't you just pause
> the VM in the hypervisor and continue to live a happy life? :)
(to be more precise, most QEMU/KVM distributions I am aware of don't
support suspend/hibernation of guests for said reason, so I wonder why
Hyper-V needs it)
--
Thanks,
David / dhildenb
^ permalink raw reply
* RE: [PATCH][PATCH net-next] hv_netvsc: Add the support of hibernation
From: Haiyang Zhang @ 2019-09-12 13:50 UTC (permalink / raw)
To: Dexuan Cui, KY Srinivasan, Stephen Hemminger, sashal@kernel.org,
davem@davemloft.net, linux-hyperv@vger.kernel.org,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
Michael Kelley
In-Reply-To: <1568245063-69693-1-git-send-email-decui@microsoft.com>
> -----Original Message-----
> From: Dexuan Cui <decui@microsoft.com>
> Sent: Wednesday, September 11, 2019 7:38 PM
> To: KY Srinivasan <kys@microsoft.com>; Haiyang Zhang
> <haiyangz@microsoft.com>; Stephen Hemminger
> <sthemmin@microsoft.com>; sashal@kernel.org; davem@davemloft.net;
> linux-hyperv@vger.kernel.org; netdev@vger.kernel.org; linux-
> kernel@vger.kernel.org; Michael Kelley <mikelley@microsoft.com>
> Cc: Dexuan Cui <decui@microsoft.com>
> Subject: [PATCH][PATCH net-next] hv_netvsc: Add the support of hibernation
>
> The existing netvsc_detach() and netvsc_attach() APIs make it easy to
> implement the suspend/resume callbacks.
>
> Signed-off-by: Dexuan Cui <decui@microsoft.com>
> ---
>
> This patch is basically a pure Hyper-V specific change and it has a
> build dependency on the commit 271b2224d42f ("Drivers: hv: vmbus:
> Implement
> suspend/resume for VSC drivers for hibernation"), which is on Sasha Levin's
> Hyper-V tree's hyperv-next branch:
> https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgit.k
> ernel.org%2Fpub%2Fscm%2Flinux%2Fkernel%2Fgit%2Fhyperv%2Flinux.git%2
> Flog%2F%3Fh%3Dhyperv-
> next&data=02%7C01%7Chaiyangz%40microsoft.com%7C0b84e40c446
> 648cc35fb08d737110e28%7C72f988bf86f141af91ab2d7cd011db47%7C1%7
> C0%7C637038418703112019&sdata=qd7DGFCJZ%2BDTix0VGcCe1JucV
> O97E0gILpVpcxlA6EE%3D&reserved=0
>
> I request this patch should go through Sasha's tree rather than the
> net-next tree.
>
> drivers/net/hyperv/hyperv_net.h | 3 +++
> drivers/net/hyperv/netvsc_drv.c | 59
> +++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 62 insertions(+)
>
> diff --git a/drivers/net/hyperv/hyperv_net.h
> b/drivers/net/hyperv/hyperv_net.h
> index ecc9af0..b8763ee 100644
> --- a/drivers/net/hyperv/hyperv_net.h
> +++ b/drivers/net/hyperv/hyperv_net.h
> @@ -952,6 +952,9 @@ struct net_device_context {
> u32 vf_alloc;
> /* Serial number of the VF to team with */
> u32 vf_serial;
> +
> + /* Used to temporarily save the config info across hibernation */
> + struct netvsc_device_info *saved_netvsc_dev_info;
> };
>
> /* Per channel data */
> diff --git a/drivers/net/hyperv/netvsc_drv.c
> b/drivers/net/hyperv/netvsc_drv.c
> index afdcc56..f920959 100644
> --- a/drivers/net/hyperv/netvsc_drv.c
> +++ b/drivers/net/hyperv/netvsc_drv.c
> @@ -2392,6 +2392,63 @@ static int netvsc_remove(struct hv_device *dev)
> 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;
> + }
> +
> + cancel_work_sync(&nvdev->subchan_work);
This looks redundant because netvsc_detach() cancels subchan_work.
Thanks,
- Haiyang
^ permalink raw reply
* [PATCH v4] video: hyperv: hyperv_fb: Support deferred IO for Hyper-V frame buffer driver
From: Wei Hu @ 2019-09-12 14:43 UTC (permalink / raw)
To: Michael Kelley, rdunlap@infradead.org, shc_work@mail.ru,
gregkh@linuxfoundation.org, lee.jones@linaro.org,
alexandre.belloni@bootlin.com, baijiaju1990@gmail.com,
fthain@telegraphics.com.au, info@metux.net,
linux-hyperv@vger.kernel.org, dri-devel@lists.freedesktop.org,
linux-fbdev@vger.kernel.org, linux-kernel@vger.kernel.org,
sashal@kernel.org, Stephen Hemminger, Haiyang Zhang,
KY Srinivasan, Dexuan Cui
Cc: Wei Hu
Without deferred IO support, hyperv_fb driver informs the host to refresh
the entire guest frame buffer at fixed rate, e.g. at 20Hz, no matter there
is screen update or not. This patch supports deferred IO for screens in
graphics mode and also enables the frame buffer on-demand refresh. The
highest refresh rate is still set at 20Hz.
Currently Hyper-V only takes a physical address from guest as the starting
address of frame buffer. This implies the guest must allocate contiguous
physical memory for frame buffer. In addition, Hyper-V Gen 2 VMs only
accept address from MMIO region as frame buffer address. Due to these
limitations on Hyper-V host, we keep a shadow copy of frame buffer
in the guest. This means one more copy of the dirty rectangle inside
guest when doing the on-demand refresh. This can be optimized in the
future with help from host. For now the host performance gain from deferred
IO outweighs the shadow copy impact in the guest.
Signed-off-by: Wei Hu <weh@microsoft.com>
---
v2: Incorporated review comments from Michael Kelley
- Increased dirty rectangle by one row in deferred IO case when sending
to Hyper-V.
- Corrected the dirty rectangle size in the text mode.
- Added more comments.
- Other minor code cleanups.
v3: Incorporated more review comments
- Removed a few unnecessary variable tests
v4: Incorporated test and review feedback from Dexuan Cui
- Not disable interrupt while acquiring docopy_lock in
hvfb_update_work(). This avoids significant bootup delay in
large vCPU count VMs.
drivers/video/fbdev/Kconfig | 1 +
drivers/video/fbdev/hyperv_fb.c | 216 +++++++++++++++++++++++++++++---
2 files changed, 197 insertions(+), 20 deletions(-)
diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
index 1b2f5f31fb6f..e781f89a1824 100644
--- a/drivers/video/fbdev/Kconfig
+++ b/drivers/video/fbdev/Kconfig
@@ -2241,6 +2241,7 @@ config FB_HYPERV
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
+ select FB_DEFERRED_IO
help
This framebuffer driver supports Microsoft Hyper-V Synthetic Video.
diff --git a/drivers/video/fbdev/hyperv_fb.c b/drivers/video/fbdev/hyperv_fb.c
index fe319fc39bec..711c46a5d5d2 100644
--- a/drivers/video/fbdev/hyperv_fb.c
+++ b/drivers/video/fbdev/hyperv_fb.c
@@ -237,6 +237,7 @@ struct synthvid_msg {
#define RING_BUFSIZE (256 * 1024)
#define VSP_TIMEOUT (10 * HZ)
#define HVFB_UPDATE_DELAY (HZ / 20)
+#define HVFB_ONDEMAND_THROTTLE (HZ / 20)
struct hvfb_par {
struct fb_info *info;
@@ -256,6 +257,17 @@ struct hvfb_par {
bool synchronous_fb;
struct notifier_block hvfb_panic_nb;
+
+ /* Memory for deferred IO and frame buffer itself */
+ unsigned char *dio_vp;
+ unsigned char *mmio_vp;
+ unsigned long mmio_pp;
+ spinlock_t docopy_lock; /* Lock to protect memory copy */
+
+ /* Dirty rectangle, protected by delayed_refresh_lock */
+ int x1, y1, x2, y2;
+ bool delayed_refresh;
+ spinlock_t delayed_refresh_lock;
};
static uint screen_width = HVFB_WIDTH;
@@ -264,6 +276,7 @@ static uint screen_width_max = HVFB_WIDTH;
static uint screen_height_max = HVFB_HEIGHT;
static uint screen_depth;
static uint screen_fb_size;
+static uint dio_fb_size; /* FB size for deferred IO */
/* Send message to Hyper-V host */
static inline int synthvid_send(struct hv_device *hdev,
@@ -350,28 +363,92 @@ static int synthvid_send_ptr(struct hv_device *hdev)
}
/* Send updated screen area (dirty rectangle) location to host */
-static int synthvid_update(struct fb_info *info)
+static int
+synthvid_update(struct fb_info *info, int x1, int y1, int x2, int y2)
{
struct hv_device *hdev = device_to_hv_device(info->device);
struct synthvid_msg msg;
memset(&msg, 0, sizeof(struct synthvid_msg));
+ if (x2 == INT_MAX)
+ x2 = info->var.xres;
+ if (y2 == INT_MAX)
+ y2 = info->var.yres;
msg.vid_hdr.type = SYNTHVID_DIRT;
msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
sizeof(struct synthvid_dirt);
msg.dirt.video_output = 0;
msg.dirt.dirt_count = 1;
- msg.dirt.rect[0].x1 = 0;
- msg.dirt.rect[0].y1 = 0;
- msg.dirt.rect[0].x2 = info->var.xres;
- msg.dirt.rect[0].y2 = info->var.yres;
+ msg.dirt.rect[0].x1 = (x1 > x2) ? 0 : x1;
+ msg.dirt.rect[0].y1 = (y1 > y2) ? 0 : y1;
+ msg.dirt.rect[0].x2 =
+ (x2 < x1 || x2 > info->var.xres) ? info->var.xres : x2;
+ msg.dirt.rect[0].y2 =
+ (y2 < y1 || y2 > info->var.yres) ? info->var.yres : y2;
synthvid_send(hdev, &msg);
return 0;
}
+static void hvfb_docopy(struct hvfb_par *par,
+ unsigned long offset,
+ unsigned long size)
+{
+ if (!par || !par->mmio_vp || !par->dio_vp || !par->fb_ready ||
+ size == 0 || offset >= dio_fb_size)
+ return;
+
+ if (offset + size > dio_fb_size)
+ size = dio_fb_size - offset;
+
+ memcpy(par->mmio_vp + offset, par->dio_vp + offset, size);
+}
+
+/* Deferred IO callback */
+static void synthvid_deferred_io(struct fb_info *p,
+ struct list_head *pagelist)
+{
+ struct hvfb_par *par = p->par;
+ struct page *page;
+ unsigned long start, end;
+ int y1, y2, miny, maxy;
+ unsigned long flags;
+
+ miny = INT_MAX;
+ maxy = 0;
+
+ /*
+ * Merge dirty pages. It is possible that last page cross
+ * over the end of frame buffer row yres. This is taken care of
+ * in synthvid_update function by clamping the y2
+ * value to yres.
+ */
+ list_for_each_entry(page, pagelist, lru) {
+ start = page->index << PAGE_SHIFT;
+ end = start + PAGE_SIZE - 1;
+ y1 = start / p->fix.line_length;
+ y2 = end / p->fix.line_length;
+ miny = min_t(int, miny, y1);
+ maxy = max_t(int, maxy, y2);
+
+ /* Copy from dio space to mmio address */
+ if (par->fb_ready) {
+ spin_lock_irqsave(&par->docopy_lock, flags);
+ hvfb_docopy(par, start, PAGE_SIZE);
+ spin_unlock_irqrestore(&par->docopy_lock, flags);
+ }
+ }
+
+ if (par->fb_ready)
+ synthvid_update(p, 0, miny, p->var.xres, maxy + 1);
+}
+
+static struct fb_deferred_io synthvid_defio = {
+ .delay = HZ / 20,
+ .deferred_io = synthvid_deferred_io,
+};
/*
* Actions on received messages from host:
@@ -618,7 +695,7 @@ static int synthvid_send_config(struct hv_device *hdev)
msg->vid_hdr.type = SYNTHVID_VRAM_LOCATION;
msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
sizeof(struct synthvid_vram_location);
- msg->vram.user_ctx = msg->vram.vram_gpa = info->fix.smem_start;
+ msg->vram.user_ctx = msg->vram.vram_gpa = par->mmio_pp;
msg->vram.is_vram_gpa_specified = 1;
synthvid_send(hdev, msg);
@@ -628,7 +705,7 @@ static int synthvid_send_config(struct hv_device *hdev)
ret = -ETIMEDOUT;
goto out;
}
- if (msg->vram_ack.user_ctx != info->fix.smem_start) {
+ if (msg->vram_ack.user_ctx != par->mmio_pp) {
pr_err("Unable to set VRAM location\n");
ret = -ENODEV;
goto out;
@@ -645,19 +722,79 @@ static int synthvid_send_config(struct hv_device *hdev)
/*
* Delayed work callback:
- * It is called at HVFB_UPDATE_DELAY or longer time interval to process
- * screen updates. It is re-scheduled if further update is necessary.
+ * It is scheduled to call whenever update request is received and it has
+ * not been called in last HVFB_ONDEMAND_THROTTLE time interval.
*/
static void hvfb_update_work(struct work_struct *w)
{
struct hvfb_par *par = container_of(w, struct hvfb_par, dwork.work);
struct fb_info *info = par->info;
+ unsigned long flags;
+ int x1, x2, y1, y2;
+ int j;
+
+ spin_lock_irqsave(&par->delayed_refresh_lock, flags);
+ /* Reset the request flag */
+ par->delayed_refresh = false;
+
+ /* Store the dirty rectangle to local variables */
+ x1 = par->x1;
+ x2 = par->x2;
+ y1 = par->y1;
+ y2 = par->y2;
+
+ /* Clear dirty rectangle */
+ par->x1 = par->y1 = INT_MAX;
+ par->x2 = par->y2 = 0;
+
+ spin_unlock_irqrestore(&par->delayed_refresh_lock, flags);
+ if (x1 > info->var.xres || x2 > info->var.xres ||
+ y1 > info->var.yres || y2 > info->var.yres || x2 <= x1)
+ return;
+
+ /* Copy the dirty rectangle to frame buffer memory */
+ spin_lock(&par->docopy_lock);
+ for (j = y1; j < y2; j++) {
+ hvfb_docopy(par,
+ j * info->fix.line_length +
+ (x1 * screen_depth / 8),
+ (x2 - x1) * screen_depth / 8);
+ }
+ spin_unlock(&par->docopy_lock);
+
+ /* Refresh */
if (par->fb_ready)
- synthvid_update(info);
+ synthvid_update(info, x1, y1, x2, y2);
+}
- if (par->update)
- schedule_delayed_work(&par->dwork, HVFB_UPDATE_DELAY);
+/*
+ * Control the on-demand refresh frequency. It schedules a delayed
+ * screen update if it has not yet.
+ */
+static void hvfb_ondemand_refresh_throttle(struct hvfb_par *par,
+ int x1, int y1, int w, int h)
+{
+ unsigned long flags;
+ int x2 = x1 + w;
+ int y2 = y1 + h;
+
+ spin_lock_irqsave(&par->delayed_refresh_lock, flags);
+
+ /* Merge dirty rectangle */
+ par->x1 = min_t(int, par->x1, x1);
+ par->y1 = min_t(int, par->y1, y1);
+ par->x2 = max_t(int, par->x2, x2);
+ par->y2 = max_t(int, par->y2, y2);
+
+ /* Schedule a delayed screen update if not yet */
+ if (par->delayed_refresh == false) {
+ schedule_delayed_work(&par->dwork,
+ HVFB_ONDEMAND_THROTTLE);
+ par->delayed_refresh = true;
+ }
+
+ spin_unlock_irqrestore(&par->delayed_refresh_lock, flags);
}
static int hvfb_on_panic(struct notifier_block *nb,
@@ -669,7 +806,8 @@ static int hvfb_on_panic(struct notifier_block *nb,
par = container_of(nb, struct hvfb_par, hvfb_panic_nb);
par->synchronous_fb = true;
info = par->info;
- synthvid_update(info);
+ hvfb_docopy(par, 0, dio_fb_size);
+ synthvid_update(info, 0, 0, INT_MAX, INT_MAX);
return NOTIFY_DONE;
}
@@ -730,7 +868,10 @@ static void hvfb_cfb_fillrect(struct fb_info *p,
cfb_fillrect(p, rect);
if (par->synchronous_fb)
- synthvid_update(p);
+ synthvid_update(p, 0, 0, INT_MAX, INT_MAX);
+ else
+ hvfb_ondemand_refresh_throttle(par, rect->dx, rect->dy,
+ rect->width, rect->height);
}
static void hvfb_cfb_copyarea(struct fb_info *p,
@@ -740,7 +881,10 @@ static void hvfb_cfb_copyarea(struct fb_info *p,
cfb_copyarea(p, area);
if (par->synchronous_fb)
- synthvid_update(p);
+ synthvid_update(p, 0, 0, INT_MAX, INT_MAX);
+ else
+ hvfb_ondemand_refresh_throttle(par, area->dx, area->dy,
+ area->width, area->height);
}
static void hvfb_cfb_imageblit(struct fb_info *p,
@@ -750,7 +894,10 @@ static void hvfb_cfb_imageblit(struct fb_info *p,
cfb_imageblit(p, image);
if (par->synchronous_fb)
- synthvid_update(p);
+ synthvid_update(p, 0, 0, INT_MAX, INT_MAX);
+ else
+ hvfb_ondemand_refresh_throttle(par, image->dx, image->dy,
+ image->width, image->height);
}
static struct fb_ops hvfb_ops = {
@@ -809,6 +956,9 @@ static int hvfb_getmem(struct hv_device *hdev, struct fb_info *info)
resource_size_t pot_start, pot_end;
int ret;
+ dio_fb_size =
+ screen_width * screen_height * screen_depth / 8;
+
if (gen2vm) {
pot_start = 0;
pot_end = -1;
@@ -843,9 +993,14 @@ static int hvfb_getmem(struct hv_device *hdev, struct fb_info *info)
if (!fb_virt)
goto err2;
+ /* Allocate memory for deferred IO */
+ par->dio_vp = vzalloc(round_up(dio_fb_size, PAGE_SIZE));
+ if (par->dio_vp == NULL)
+ goto err3;
+
info->apertures = alloc_apertures(1);
if (!info->apertures)
- goto err3;
+ goto err4;
if (gen2vm) {
info->apertures->ranges[0].base = screen_info.lfb_base;
@@ -857,16 +1012,23 @@ static int hvfb_getmem(struct hv_device *hdev, struct fb_info *info)
info->apertures->ranges[0].size = pci_resource_len(pdev, 0);
}
+ /* Physical address of FB device */
+ par->mmio_pp = par->mem->start;
+ /* Virtual address of FB device */
+ par->mmio_vp = (unsigned char *) fb_virt;
+
info->fix.smem_start = par->mem->start;
- info->fix.smem_len = screen_fb_size;
- info->screen_base = fb_virt;
- info->screen_size = screen_fb_size;
+ info->fix.smem_len = dio_fb_size;
+ info->screen_base = par->dio_vp;
+ info->screen_size = dio_fb_size;
if (!gen2vm)
pci_dev_put(pdev);
return 0;
+err4:
+ vfree(par->dio_vp);
err3:
iounmap(fb_virt);
err2:
@@ -884,6 +1046,7 @@ static void hvfb_putmem(struct fb_info *info)
{
struct hvfb_par *par = info->par;
+ vfree(par->dio_vp);
iounmap(info->screen_base);
vmbus_free_mmio(par->mem->start, screen_fb_size);
par->mem = NULL;
@@ -909,6 +1072,12 @@ static int hvfb_probe(struct hv_device *hdev,
init_completion(&par->wait);
INIT_DELAYED_WORK(&par->dwork, hvfb_update_work);
+ par->delayed_refresh = false;
+ spin_lock_init(&par->delayed_refresh_lock);
+ spin_lock_init(&par->docopy_lock);
+ par->x1 = par->y1 = INT_MAX;
+ par->x2 = par->y2 = 0;
+
/* Connect to VSP */
hv_set_drvdata(hdev, info);
ret = synthvid_connect_vsp(hdev);
@@ -960,6 +1129,10 @@ static int hvfb_probe(struct hv_device *hdev,
info->fbops = &hvfb_ops;
info->pseudo_palette = par->pseudo_palette;
+ /* Initialize deferred IO */
+ info->fbdefio = &synthvid_defio;
+ fb_deferred_io_init(info);
+
/* Send config to host */
ret = synthvid_send_config(hdev);
if (ret)
@@ -981,6 +1154,7 @@ static int hvfb_probe(struct hv_device *hdev,
return 0;
error:
+ fb_deferred_io_cleanup(info);
hvfb_putmem(info);
error2:
vmbus_close(hdev->channel);
@@ -1003,6 +1177,8 @@ static int hvfb_remove(struct hv_device *hdev)
par->update = false;
par->fb_ready = false;
+ fb_deferred_io_cleanup(info);
+
unregister_framebuffer(info);
cancel_delayed_work_sync(&par->dwork);
--
2.20.1
^ permalink raw reply related
* Re: [PATCH 2/3] hv_utils: Support host-initiated hibernation request
From: Vitaly Kuznetsov @ 2019-09-12 16:26 UTC (permalink / raw)
To: Dexuan Cui
Cc: KY Srinivasan, Haiyang Zhang, Stephen Hemminger,
sashal@kernel.org, linux-hyperv@vger.kernel.org,
linux-kernel@vger.kernel.org, Michael Kelley
In-Reply-To: <1568245130-70712-3-git-send-email-decui@microsoft.com>
Dexuan Cui <decui@microsoft.com> writes:
> +static void perform_hibernation(struct work_struct *dummy)
> +{
> + /*
> + * The user is expected to create the program, which can be a simple
> + * script containing two lines:
> + * #!/bin/bash
> + * echo disk > /sys/power/state
'systemctl hibernate' is what people do nowadays :-)
> + */
> + static char hibernate_cmd[PATH_MAX] = "/sbin/hyperv-hibernate";
> +
Let's not do that (I remember when we were triggering network restart
from netvsc and it was a lot of pain).
Receiving hybernation request from the host is similar to pushing power
button on your desktop: an ACPI event is going to be generated and your
userspace will somehow react to it. I see two options:
1) We try to hook up some existing userspace (udev?)
2) We write a new hyperv-daemon handling the request (with a config file
instead of hardcoding please).
--
Vitaly
^ permalink raw reply
* Re: [PATCH 1/3] hv_utils: Add the support of hibernation
From: Vitaly Kuznetsov @ 2019-09-12 16:36 UTC (permalink / raw)
To: Dexuan Cui
Cc: KY Srinivasan, Haiyang Zhang, Stephen Hemminger,
sashal@kernel.org, linux-hyperv@vger.kernel.org,
linux-kernel@vger.kernel.org, Michael Kelley
In-Reply-To: <1568245130-70712-2-git-send-email-decui@microsoft.com>
Dexuan Cui <decui@microsoft.com> writes:
> On hibernation, Linux can not guarantee the host side utils operations
> still succeed without any issue, so let's simply cancel the work items.
> The host is supposed to retry the operations, if necessary.
>
> Signed-off-by: Dexuan Cui <decui@microsoft.com>
> ---
> drivers/hv/hv_fcopy.c | 9 ++++++++-
> drivers/hv/hv_kvp.c | 11 +++++++++--
> drivers/hv/hv_snapshot.c | 11 +++++++++--
> drivers/hv/hv_util.c | 37 ++++++++++++++++++++++++++++++++++++-
> drivers/hv/hyperv_vmbus.h | 3 +++
> include/linux/hyperv.h | 1 +
> 6 files changed, 66 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/hv/hv_fcopy.c b/drivers/hv/hv_fcopy.c
> index 7e30ae0..f44df3d 100644
> --- a/drivers/hv/hv_fcopy.c
> +++ b/drivers/hv/hv_fcopy.c
> @@ -345,9 +345,16 @@ int hv_fcopy_init(struct hv_util_service *srv)
> return 0;
> }
>
> +void hv_fcopy_cancel_work(void)
> +{
> + cancel_delayed_work_sync(&fcopy_timeout_work);
> +}
> +
> void hv_fcopy_deinit(void)
> {
> fcopy_transaction.state = HVUTIL_DEVICE_DYING;
> - cancel_delayed_work_sync(&fcopy_timeout_work);
> +
> + hv_fcopy_cancel_work();
> +
> hvutil_transport_destroy(hvt);
> }
> diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c
> index 5054d11..064c384 100644
> --- a/drivers/hv/hv_kvp.c
> +++ b/drivers/hv/hv_kvp.c
> @@ -757,11 +757,18 @@ static void kvp_on_reset(void)
> return 0;
> }
>
> -void hv_kvp_deinit(void)
> +void hv_kvp_cancel_work(void)
> {
> - kvp_transaction.state = HVUTIL_DEVICE_DYING;
> cancel_delayed_work_sync(&kvp_host_handshake_work);
> cancel_delayed_work_sync(&kvp_timeout_work);
> cancel_work_sync(&kvp_sendkey_work);
> +}
> +
> +void hv_kvp_deinit(void)
> +{
> + kvp_transaction.state = HVUTIL_DEVICE_DYING;
> +
> + hv_kvp_cancel_work();
> +
> hvutil_transport_destroy(hvt);
> }
> diff --git a/drivers/hv/hv_snapshot.c b/drivers/hv/hv_snapshot.c
> index 20ba95b..0eb718a 100644
> --- a/drivers/hv/hv_snapshot.c
> +++ b/drivers/hv/hv_snapshot.c
> @@ -378,10 +378,17 @@ static void vss_on_reset(void)
> return 0;
> }
>
> -void hv_vss_deinit(void)
> +void hv_vss_cancel_work(void)
> {
> - vss_transaction.state = HVUTIL_DEVICE_DYING;
> cancel_delayed_work_sync(&vss_timeout_work);
> cancel_work_sync(&vss_handle_request_work);
> +}
> +
> +void hv_vss_deinit(void)
> +{
> + vss_transaction.state = HVUTIL_DEVICE_DYING;
> +
> + hv_vss_cancel_work();
> +
> hvutil_transport_destroy(hvt);
> }
> diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c
> index e32681e..039c752 100644
> --- a/drivers/hv/hv_util.c
> +++ b/drivers/hv/hv_util.c
> @@ -81,12 +81,14 @@
> };
>
> static int hv_timesync_init(struct hv_util_service *srv);
> +static void hv_timesync_cancel_work(void);
> static void hv_timesync_deinit(void);
>
> static void timesync_onchannelcallback(void *context);
> static struct hv_util_service util_timesynch = {
> .util_cb = timesync_onchannelcallback,
> .util_init = hv_timesync_init,
> + .util_cancel_work = hv_timesync_cancel_work,
> .util_deinit = hv_timesync_deinit,
> };
>
> @@ -98,18 +100,21 @@
> static struct hv_util_service util_kvp = {
> .util_cb = hv_kvp_onchannelcallback,
> .util_init = hv_kvp_init,
> + .util_cancel_work = hv_kvp_cancel_work,
> .util_deinit = hv_kvp_deinit,
> };
>
> static struct hv_util_service util_vss = {
> .util_cb = hv_vss_onchannelcallback,
> .util_init = hv_vss_init,
> + .util_cancel_work = hv_vss_cancel_work,
> .util_deinit = hv_vss_deinit,
> };
>
> static struct hv_util_service util_fcopy = {
> .util_cb = hv_fcopy_onchannelcallback,
> .util_init = hv_fcopy_init,
> + .util_cancel_work = hv_fcopy_cancel_work,
> .util_deinit = hv_fcopy_deinit,
> };
>
> @@ -440,6 +445,28 @@ static int util_remove(struct hv_device *dev)
> return 0;
> }
>
> +static int util_suspend(struct hv_device *dev)
> +{
> + struct hv_util_service *srv = hv_get_drvdata(dev);
> +
> + if (srv->util_cancel_work)
> + srv->util_cancel_work();
> +
> + vmbus_close(dev->channel);
And what happens if you receive a real reply from userspace after you
close the channel? You've only cancelled timeout work so the driver
will not try to reply by itself but this doesn't mean we won't try to
write to the channel on legitimate replies from daemons.
I think you need to block all userspace requests (hang in kernel until
util_resume()).
While I'm not sure we can't get away without it but I'd suggest we add a
new HVUTIL_SUSPENDED state to the hvutil state machine.
> +
> + return 0;
> +}
> +
> +static int util_resume(struct hv_device *dev)
> +{
> + struct hv_util_service *srv = hv_get_drvdata(dev);
> + int ret;
> +
> + ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE,
> + NULL, 0, srv->util_cb, dev->channel);
> + return ret;
> +}
> +
> static const struct hv_vmbus_device_id id_table[] = {
> /* Shutdown guid */
> { HV_SHUTDOWN_GUID,
> @@ -476,6 +503,8 @@ static int util_remove(struct hv_device *dev)
> .id_table = id_table,
> .probe = util_probe,
> .remove = util_remove,
> + .suspend = util_suspend,
> + .resume = util_resume,
> .driver = {
> .probe_type = PROBE_PREFER_ASYNCHRONOUS,
> },
> @@ -545,11 +574,17 @@ static int hv_timesync_init(struct hv_util_service *srv)
> return 0;
> }
>
> +static void hv_timesync_cancel_work(void)
> +{
> + cancel_work_sync(&adj_time_work);
> +}
> +
> static void hv_timesync_deinit(void)
> {
> if (hv_ptp_clock)
> ptp_clock_unregister(hv_ptp_clock);
> - cancel_work_sync(&adj_time_work);
> +
> + hv_timesync_cancel_work();
> }
>
> static int __init init_hyperv_utils(void)
> diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
> index f7a5f56..dc280fa 100644
> --- a/drivers/hv/hyperv_vmbus.h
> +++ b/drivers/hv/hyperv_vmbus.h
> @@ -353,14 +353,17 @@ int vmbus_add_channel_kobj(struct hv_device *device_obj,
> void vmbus_on_msg_dpc(unsigned long data);
>
> int hv_kvp_init(struct hv_util_service *srv);
> +void hv_kvp_cancel_work(void);
> void hv_kvp_deinit(void);
> void hv_kvp_onchannelcallback(void *context);
>
> int hv_vss_init(struct hv_util_service *srv);
> +void hv_vss_cancel_work(void);
> void hv_vss_deinit(void);
> void hv_vss_onchannelcallback(void *context);
>
> int hv_fcopy_init(struct hv_util_service *srv);
> +void hv_fcopy_cancel_work(void);
> void hv_fcopy_deinit(void);
> void hv_fcopy_onchannelcallback(void *context);
> void vmbus_initiate_unload(bool crash);
> diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
> index a3aa9e9..b4e2768 100644
> --- a/include/linux/hyperv.h
> +++ b/include/linux/hyperv.h
> @@ -1412,6 +1412,7 @@ struct hv_util_service {
> void (*util_cb)(void *);
> int (*util_init)(struct hv_util_service *);
> void (*util_deinit)(void);
> + void (*util_cancel_work)(void);
> };
>
> struct vmbuspipe_hdr {
--
Vitaly
^ permalink raw reply
* RE: [PATCH] hv_balloon: Add the support of hibernation
From: Dexuan Cui @ 2019-09-12 19:18 UTC (permalink / raw)
To: David Hildenbrand, KY Srinivasan, Haiyang Zhang,
Stephen Hemminger, sashal@kernel.org,
linux-hyperv@vger.kernel.org, linux-kernel@vger.kernel.org,
Michael Kelley
In-Reply-To: <42de5835-8faa-2047-0f77-db51dd57b036@redhat.com>
> From: David Hildenbrand <david@redhat.com>
> Sent: Thursday, September 12, 2019 3:09 AM
> On 12.09.19 01:36, Dexuan Cui wrote:
> > When hibernation is enabled, we must ignore the balloon up/down and
> > hot-add requests from the host, if any.
>
> Why do you even care about supporting hibernation? Can't you just pause
> the VM in the hypervisor and continue to live a happy life? :)
>
> (to be more precise, most QEMU/KVM distributions I am aware of don't
> support suspend/hibernation of guests for said reason, so I wonder why
> Hyper-V needs it)
In some scenarios, hibernation can be better than pause/unpause,
save/restore and live migration:
1. Compared to pause/unpause, the VM can power off completely with
hibernation, and all the states are saved inside the VM image, then the
image can be copied to another host to start the VM again, as long as
the new host uses exactly the same configuration for the VM.
2. Compared to pause/unpause, hibernation may be more reliable, since it's
performed by the VM kernel rather than the host, so the VM kernel may
better tackle some clock-source/event-sensitive issues.
3. Hibernation can be especially useful when we pass through a PCIe device,
e.g. a NIC, a NVMe controller or a GPU, to the VM, as usually save/restore
and live migration can not work with this kind of configuration, because
usually the host doesn't know how to save/restore the state of the PCIe
device.
> > This patch is basically a pure Hyper-V specific change and it has a
> > build dependency on the commit 271b2224d42f ("Drivers: hv: vmbus:
> Implement
> > suspend/resume for VSC drivers for hibernation"), which is on Sasha Levin's
> > Hyper-V tree's hyperv-next branch
> > @@ -1672,6 +1694,24 @@ static int balloon_probe(struct hv_device *dev,
> > {
> > int ret;
> >
> > +#if 0
>
> I am not sure if that's a good idea. Can't you base this series on
> hv_is_hibernation_supported() ?
Unluckily, I can not. :-(
My hv_is_hibernation_supported() patch is still in review, and has not been
in any tree yet (it's supposed to go through the tip.git tree's timers/core
branch since otherwise the branch contains some patches that would
cause conflicts):
https://lkml.org/lkml/2019/9/5/1158
https://lkml.org/lkml/2019/9/5/1160
> > + /*
> > + * The patch to implement hv_is_hibernation_supported() is going
> > + * through the tip tree. For now, let's hardcode allow_hibernation
> > + * to false to keep the current behavior of hv_balloon. If people
> > + * want to test hibernation, please blacklist hv_balloon for now
> > + * or do not enable Dynamid Memory and Memory Resizing.
> > + *
> > + * We'll remove the conditional compilation as soon as
> > + * hv_is_hibernation_supported() is available in the mainline tree.
> > + */
> > + allow_hibernation = hv_is_hibernation_supported();
> > +#else
> > + allow_hibernation = false;
> > +#endif
Thanks,
-- Dexuan
^ permalink raw reply
* RE: [PATCH][PATCH net-next] hv_netvsc: Add the support of hibernation
From: Dexuan Cui @ 2019-09-12 19:44 UTC (permalink / raw)
To: Haiyang Zhang, KY Srinivasan, Stephen Hemminger,
sashal@kernel.org, davem@davemloft.net,
linux-hyperv@vger.kernel.org, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org, Michael Kelley
In-Reply-To: <DM6PR21MB1337EEC46A276CAD9FAA5EB4CAB00@DM6PR21MB1337.namprd21.prod.outlook.com>
> From: Haiyang Zhang <haiyangz@microsoft.com>
> Sent: Thursday, September 12, 2019 6:50 AM
> > -----Original Message-----
> > From: Dexuan Cui <decui@microsoft.com>
> > Sent: Wednesday, September 11, 2019 7:38 PM
> > +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;
> > + }
> > +
> > + cancel_work_sync(&nvdev->subchan_work);
>
> This looks redundant because netvsc_detach() cancels subchan_work.
> - Haiyang
You are right. I'll remove the superflous line
cancel_work_sync(&nvdev->subchan_work);
in netvsc_suspend() in v2.
I'll wait for a few days before posting v2, in case people may have
other comments.
Thanks,
-- Dexuan
^ permalink raw reply
* Re: [PATCH] scsi: storvsc: Add the support of hibernation
From: kbuild test robot @ 2019-09-12 20:54 UTC (permalink / raw)
To: Dexuan Cui
Cc: kbuild-all, KY Srinivasan, Haiyang Zhang, Stephen Hemminger,
sashal@kernel.org, jejb@linux.ibm.com, martin.petersen@oracle.com,
linux-hyperv@vger.kernel.org, linux-scsi@vger.kernel.org,
linux-kernel@vger.kernel.org, Michael Kelley, Dexuan Cui
In-Reply-To: <1568244905-66625-1-git-send-email-decui@microsoft.com>
[-- Attachment #1: Type: text/plain, Size: 2137 bytes --]
Hi Dexuan,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on linus/master]
[cannot apply to v5.3-rc8 next-20190904]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Dexuan-Cui/scsi-storvsc-Add-the-support-of-hibernation/20190912-234223
config: i386-randconfig-b004-201936 (attached as .config)
compiler: gcc-7 (Debian 7.4.0-11) 7.4.0
reproduce:
# save the attached .config to linux build tree
make ARCH=i386
If you fix the issue, kindly add following tag
Reported-by: kbuild test robot <lkp@intel.com>
All error/warnings (new ones prefixed by >>):
>> drivers//scsi/storvsc_drv.c:1982:3: error: 'struct hv_driver' has no member named 'suspend'
.suspend = storvsc_suspend,
^~~~~~~
>> drivers//scsi/storvsc_drv.c:1982:13: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
.suspend = storvsc_suspend,
^~~~~~~~~~~~~~~
drivers//scsi/storvsc_drv.c:1982:13: note: (near initialization for 'storvsc_drv.shutdown')
>> drivers//scsi/storvsc_drv.c:1983:3: error: 'struct hv_driver' has no member named 'resume'; did you mean 'remove'?
.resume = storvsc_resume,
^~~~~~
remove
>> drivers//scsi/storvsc_drv.c:1983:12: warning: excess elements in struct initializer
.resume = storvsc_resume,
^~~~~~~~~~~~~~
drivers//scsi/storvsc_drv.c:1983:12: note: (near initialization for 'storvsc_drv')
cc1: some warnings being treated as errors
vim +1982 drivers//scsi/storvsc_drv.c
1976
1977 static struct hv_driver storvsc_drv = {
1978 .name = KBUILD_MODNAME,
1979 .id_table = id_table,
1980 .probe = storvsc_probe,
1981 .remove = storvsc_remove,
> 1982 .suspend = storvsc_suspend,
> 1983 .resume = storvsc_resume,
1984 .driver = {
1985 .probe_type = PROBE_PREFER_ASYNCHRONOUS,
1986 },
1987 };
1988
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 30335 bytes --]
^ permalink raw reply
* RE: [PATCH] scsi: storvsc: Add the support of hibernation
From: Dexuan Cui @ 2019-09-12 23:08 UTC (permalink / raw)
To: kbuild test robot
Cc: kbuild-all@01.org, KY Srinivasan, Haiyang Zhang,
Stephen Hemminger, sashal@kernel.org, jejb@linux.ibm.com,
martin.petersen@oracle.com, linux-hyperv@vger.kernel.org,
linux-scsi@vger.kernel.org, linux-kernel@vger.kernel.org,
Michael Kelley
In-Reply-To: <201909130413.j9CHsgZl%lkp@intel.com>
> From: linux-scsi-owner@vger.kernel.org <linux-scsi-owner@vger.kernel.org>
> On Behalf Of kbuild test robot
> Sent: Thursday, September 12, 2019 1:54 PM
> To: Dexuan Cui <decui@microsoft.com>
> Cc: kbuild-all@01.org; KY Srinivasan <kys@microsoft.com>; Haiyang Zhang
> <haiyangz@microsoft.com>; Stephen Hemminger
> <sthemmin@microsoft.com>; sashal@kernel.org; jejb@linux.ibm.com;
> martin.petersen@oracle.com; linux-hyperv@vger.kernel.org;
> linux-scsi@vger.kernel.org; linux-kernel@vger.kernel.org; Michael Kelley
> <mikelley@microsoft.com>; Dexuan Cui <decui@microsoft.com>
> Subject: Re: [PATCH] scsi: storvsc: Add the support of hibernation
>
> Hi Dexuan,
>
> Thank you for the patch! Yet something to improve:
>
> [auto build test ERROR on linus/master]
> [cannot apply to v5.3-rc8 next-20190904]
> [if your patch is applied to the wrong git tree, please drop us a note to help
> improve the system]
>
> >> drivers//scsi/storvsc_drv.c:1982:3: error: 'struct hv_driver' has no member
> named 'suspend'
> .suspend = storvsc_suspend,
> ^~~~~~~
This build failure is expected: In the patch mail, I mentioned this patch
has a build dependency on the commit 271b2224d42f ("Drivers: hv: vmbus: Implement
suspend/resume for VSC drivers for hibernation"), which is on Sasha Levin's
Hyper-V tree's hyperv-next branch:
https://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux.git/log/?h=hyperv-next
Thanks,
-- Dexuan
^ permalink raw reply
* [PATCH v5 0/2] hv: vmbus: add fuzz testing to hv device
From: Branden Bonaby @ 2019-09-13 2:31 UTC (permalink / raw)
To: kys, haiyangz, sthemmin, sashal
Cc: Branden Bonaby, linux-hyperv, linux-kernel
This patchset introduces a testing framework for Hyper-V drivers.
This framework allows us to introduce delays in the packet receive
path on a per-device basis. While the current code only supports
introducing arbitrary delays in the host/guest communication path,
we intend to expand this to support error injection in the future.
changes in v5:
patch 1:
As per Stephen's suggestion, Moved CONFIG_HYPERV_TESTING
to lib/Kconfig.debug.
Fixed build issue reported by Kbuild, with Michael's
suggestion to make hv_debugfs part of the hv_vmbus
module.
changes in v4:
patch 1:
Combined previous v3 patches 1 and 2, into a single patch
which is now patch 1. This was done so that calls to
the new debugfs functions are in the same patch as
the definitions for these functions.
Moved debugfs code from "vmbus_drv.c" that was in
previous v3 patch 2, into a new file "debugfs.c" in
drivers/hv.
Updated the Makefile to compile "debugfs.c" if
CONFIG_HYPERV_TESTING is enabled
As per Michael's comments, added empty implementations
of the new functions, so the compiler will not generate
code when CONFIG_HYPERV_TESTING is not enabled.
patch 2 (was previously v3 patch 3):
Based on Harrys comments, made the tool more
user friendly and added more error checking.
changes in v3:
patch 2: change call to IS_ERR_OR_NULL, to IS_ERR.
patch 3: Align python tool to match Linux coding style.
Changes in v2:
Patch 1: As per Vitaly's suggestion, wrapped the test code under an
#ifdef and updated the Kconfig file, so that the test code
will only be used when the config option is set to true.
(default is false).
Updated hyperv_vmbus header to contain new #ifdef with new
new functions for the test code.
Patch 2: Moved code from under sysfs to debugfs and wrapped it under
the new ifdef.
Updated MAINTAINERS file with new debugfs-hyperv file under
the section for hyperv.
Patch 3: Updated testing tool with new debugfs location.
Branden Bonaby (2):
drivers: hv: vmbus: Introduce latency testing
tools: hv: add vmbus testing tool
Documentation/ABI/testing/debugfs-hyperv | 23 ++
MAINTAINERS | 1 +
drivers/hv/Makefile | 1 +
drivers/hv/connection.c | 1 +
drivers/hv/hv_debugfs.c | 185 +++++++++++
drivers/hv/hyperv_vmbus.h | 31 ++
drivers/hv/ring_buffer.c | 2 +
drivers/hv/vmbus_drv.c | 6 +
include/linux/hyperv.h | 19 ++
lib/Kconfig.debug | 7 +
tools/hv/vmbus_testing | 376 +++++++++++++++++++++++
11 files changed, 652 insertions(+)
create mode 100644 Documentation/ABI/testing/debugfs-hyperv
create mode 100644 drivers/hv/hv_debugfs.c
create mode 100644 tools/hv/vmbus_testing
--
2.17.1
^ permalink raw reply
* [PATCH v5 1/2] drivers: hv: vmbus: Introduce latency testing
From: Branden Bonaby @ 2019-09-13 2:32 UTC (permalink / raw)
To: kys, haiyangz, sthemmin, sashal
Cc: Branden Bonaby, linux-hyperv, linux-kernel
In-Reply-To: <cover.1568320416.git.brandonbonaby94@gmail.com>
Introduce user specified latency in the packet reception path
By exposing the test parameters as part of the debugfs channel
attributes. We will control the testing state via these attributes.
Signed-off-by: Branden Bonaby <brandonbonaby94@gmail.com>
---
changes in v5:
- As per Stephen's suggestion, Moved CONFIG_HYPERV_TESTING
to lib/Kconfig.debug.
- Fixed build issue reported by Kbuild, with Michael's
suggestion to make hv_debugfs part of the hv_vmbus
module.
- updated debugfs-hyperv to show kernel version 5.4
changes in v4:
- Combined v3 patch 2 into this patch, and changed the
commit description to reflect this.
- Moved debugfs code from "vmbus_drv.c" that was in
previous v3 patch 2, into a new file "debugfs.c" in
drivers/hv.
- Updated the Makefile to compile "debugfs.c" if
CONFIG_HYPERV_TESTING is enabled
- As per Michael's comments, added empty implementations
of the new functions, so the compiler will not generate
code when CONFIG_HYPERV_TESTING is not enabled.
- Added microseconds into description for files in
Documentation/ABI/testing/debugfs-hyperv.
Changes in v2:
- Add #ifdef in Kconfig file so test code will not interfere
with non-test code.
- Move test code functions for delay to hyperv_vmbus header
file.
- Wrap test code under #ifdef statement.
Documentation/ABI/testing/debugfs-hyperv | 23 +++
MAINTAINERS | 1 +
drivers/hv/Makefile | 1 +
drivers/hv/connection.c | 1 +
drivers/hv/hv_debugfs.c | 185 +++++++++++++++++++++++
drivers/hv/hyperv_vmbus.h | 31 ++++
drivers/hv/ring_buffer.c | 2 +
drivers/hv/vmbus_drv.c | 6 +
include/linux/hyperv.h | 19 +++
lib/Kconfig.debug | 7 +
10 files changed, 276 insertions(+)
create mode 100644 Documentation/ABI/testing/debugfs-hyperv
create mode 100644 drivers/hv/hv_debugfs.c
diff --git a/Documentation/ABI/testing/debugfs-hyperv b/Documentation/ABI/testing/debugfs-hyperv
new file mode 100644
index 000000000000..4427503ec762
--- /dev/null
+++ b/Documentation/ABI/testing/debugfs-hyperv
@@ -0,0 +1,23 @@
+What: /sys/kernel/debug/hyperv/<UUID>/fuzz_test_state
+Date: August 2019
+KernelVersion: 5.4
+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: August 2019
+KernelVersion: 5.4
+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: August 2019
+KernelVersion: 5.4
+Contact: Branden Bonaby <brandonbonaby94@gmail.com>
+Description: Fuzz testing message delay value between 0 - 1000 microseconds
+ (inclusive).
+Users: Debugging tools
diff --git a/MAINTAINERS b/MAINTAINERS
index e7a47b5210fd..00831931eb22 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7468,6 +7468,7 @@ F: include/uapi/linux/hyperv.h
F: include/asm-generic/mshyperv.h
F: tools/hv/
F: Documentation/ABI/stable/sysfs-bus-vmbus
+F: Documentation/ABI/testing/debugfs-hyperv
HYPERBUS SUPPORT
M: Vignesh Raghavendra <vigneshr@ti.com>
diff --git a/drivers/hv/Makefile b/drivers/hv/Makefile
index a1eec7177c2d..94daf8240c95 100644
--- a/drivers/hv/Makefile
+++ b/drivers/hv/Makefile
@@ -9,4 +9,5 @@ CFLAGS_hv_balloon.o = -I$(src)
hv_vmbus-y := vmbus_drv.o \
hv.o connection.o channel.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
diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c
index 09829e15d4a0..4d4d40832846 100644
--- a/drivers/hv/connection.c
+++ b/drivers/hv/connection.c
@@ -357,6 +357,7 @@ void vmbus_on_event(unsigned long data)
trace_vmbus_on_event(channel);
+ hv_debug_delay_test(channel, INTERRUPT_DELAY);
do {
void (*callback_fn)(void *);
diff --git a/drivers/hv/hv_debugfs.c b/drivers/hv/hv_debugfs.c
new file mode 100644
index 000000000000..933080b51410
--- /dev/null
+++ b/drivers/hv/hv_debugfs.c
@@ -0,0 +1,185 @@
+// 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)
+{
+ int ret = 0;
+
+ if (val >= 0 && val <= 1000)
+ *(u32 *)data = val;
+ else
+ ret = -EINVAL;
+
+ return ret;
+}
+
+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)
+{
+ int ret = 0;
+
+ if (val == 1)
+ *(bool *)data = true;
+ else if (val == 0)
+ *(bool *)data = false;
+ else
+ ret = -EINVAL;
+
+ return ret;
+}
+
+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;
+}
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
index fb16a622e8ab..642f609b0e26 100644
--- a/drivers/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
@@ -355,4 +355,35 @@ enum hvutil_device_state {
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 */
diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c
index 9a03b163cbbd..356e22159e83 100644
--- a/drivers/hv/ring_buffer.c
+++ b/drivers/hv/ring_buffer.c
@@ -396,6 +396,7 @@ struct vmpacket_descriptor *hv_pkt_iter_first(struct vmbus_channel *channel)
struct hv_ring_buffer_info *rbi = &channel->inbound;
struct vmpacket_descriptor *desc;
+ hv_debug_delay_test(channel, MESSAGE_DELAY);
if (hv_pkt_iter_avail(rbi) < sizeof(struct vmpacket_descriptor))
return NULL;
@@ -421,6 +422,7 @@ __hv_pkt_iter_next(struct vmbus_channel *channel,
u32 packetlen = desc->len8 << 3;
u32 dsize = rbi->ring_datasize;
+ hv_debug_delay_test(channel, MESSAGE_DELAY);
/* bump offset to next potential packet */
rbi->priv_read_index += packetlen + VMBUS_PKT_TRAILER;
if (rbi->priv_read_index >= dsize)
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
index ebd35fc35290..8b2e2352fc6c 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
@@ -919,6 +919,8 @@ static void vmbus_device_release(struct device *device)
struct hv_device *hv_dev = device_to_hv_device(device);
struct vmbus_channel *channel = hv_dev->channel;
+ hv_debug_rm_dev_dir(hv_dev);
+
mutex_lock(&vmbus_connection.channel_mutex);
hv_process_channel_removal(channel);
mutex_unlock(&vmbus_connection.channel_mutex);
@@ -1727,6 +1729,7 @@ int vmbus_device_register(struct hv_device *child_device_obj)
pr_err("Unable to register primary channeln");
goto err_kset_unregister;
}
+ hv_debug_add_dev_dir(child_device_obj);
return 0;
@@ -2108,6 +2111,7 @@ static int __init hv_acpi_init(void)
ret = -ETIMEDOUT;
goto cleanup;
}
+ hv_debug_init();
ret = vmbus_bus_init();
if (ret)
@@ -2140,6 +2144,8 @@ static void __exit vmbus_exit(void)
tasklet_kill(&hv_cpu->msg_dpc);
}
+ hv_debug_rm_all_dir();
+
vmbus_free_channels();
if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE) {
diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
index 6256cc34c4a6..6d815f14d97f 100644
--- a/include/linux/hyperv.h
+++ b/include/linux/hyperv.h
@@ -926,6 +926,21 @@ struct vmbus_channel {
* full outbound ring buffer.
*/
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)
@@ -1166,6 +1181,10 @@ struct hv_device {
struct vmbus_channel *channel;
struct kset *channels_kset;
+
+ /* place holder to keep track of the dir for hv device in debugfs */
+ struct dentry *debug_dir;
+
};
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 5960e2980a8a..2965ac8257f7 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -2144,4 +2144,11 @@ config IO_STRICT_DEVMEM
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
--
2.17.1
^ permalink raw reply related
* [PATCH v5 2/2] tools: hv: add vmbus testing tool
From: Branden Bonaby @ 2019-09-13 2:32 UTC (permalink / raw)
To: kys, haiyangz, sthemmin, sashal
Cc: Branden Bonaby, linux-hyperv, linux-kernel
In-Reply-To: <cover.1568320416.git.brandonbonaby94@gmail.com>
This is a userspace tool to drive the testing. Currently it supports
introducing user specified delay in the host to guest communication
path on a per-channel basis.
Signed-off-by: Branden Bonaby <brandonbonaby94@gmail.com>
---
Changes in v4:
- Based on Harrys comments, made the tool more
user friendly and added more error checking.
Changes in v3:
- Align python tool to match Linux coding style.
Changes in v2:
- Move testing location to new location in debugfs.
tools/hv/vmbus_testing | 376 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 376 insertions(+)
create mode 100644 tools/hv/vmbus_testing
diff --git a/tools/hv/vmbus_testing b/tools/hv/vmbus_testing
new file mode 100644
index 000000000000..e7212903dd1d
--- /dev/null
+++ b/tools/hv/vmbus_testing
@@ -0,0 +1,376 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Program to allow users to fuzz test Hyper-V drivers
+# by interfacing with Hyper-V debugfs attributes.
+# Current test methods available:
+# 1. delay testing
+#
+# Current file/directory structure of hyper-V debugfs:
+# /sys/kernel/debug/hyperv/UUID
+# /sys/kernel/debug/hyperv/UUID/<test-state filename>
+# /sys/kernel/debug/hyperv/UUID/<test-method sub-directory>
+#
+# author: Branden Bonaby <brandonbonaby94@gmail.com>
+
+import os
+import cmd
+import argparse
+import glob
+from argparse import RawDescriptionHelpFormatter
+from argparse import RawTextHelpFormatter
+from enum import Enum
+
+# Do not change unless, you change the debugfs attributes
+# in /drivers/hv/debugfs.c. All fuzz testing
+# attributes will start with "fuzz_test".
+
+# debugfs path for hyperv must exist before proceeding
+debugfs_hyperv_path = "/sys/kernel/debug/hyperv"
+if not os.path.isdir(debugfs_hyperv_path):
+ print("{} doesn't exist/check permissions".format(debugfs_hyperv_path))
+ exit(-1)
+
+class dev_state(Enum):
+ off = 0
+ on = 1
+
+# File names, that correspond to the files created in
+# /drivers/hv/debugfs.c
+class f_names(Enum):
+ state_f = "fuzz_test_state"
+ buff_f = "fuzz_test_buffer_interrupt_delay"
+ mess_f = "fuzz_test_message_delay"
+
+# Both single_actions and all_actions are used
+# for error checking and to allow for some subparser
+# names to be abbreviated. Do not abbreviate the
+# test method names, as it will become less intuitive
+# as to what the user can do. If you do decide to
+# abbreviate the test method name, make sure the main
+# function reflects this change.
+
+all_actions = [
+ "disable_all",
+ "D",
+ "enable_all",
+ "view_all",
+ "V"
+]
+
+single_actions = [
+ "disable_single",
+ "d",
+ "enable_single",
+ "view_single",
+ "v"
+]
+
+def main():
+
+ file_map = recursive_file_lookup(debugfs_hyperv_path, dict())
+ args = parse_args()
+ if (not args.action):
+ print ("Error, no options selected...exiting")
+ exit(-1)
+ arg_set = { k for (k,v) in vars(args).items() if v and k != "action" }
+ arg_set.add(args.action)
+ path = args.path if "path" in arg_set else None
+ if (path and path[-1] == "/"):
+ path = path[:-1]
+ validate_args_path(path, arg_set, file_map)
+ if (path and "enable_single" in arg_set):
+ state_path = locate_state(path, file_map)
+ set_test_state(state_path, dev_state.on.value, args.quiet)
+
+ # Use subparsers as the key for different actions
+ if ("delay" in arg_set):
+ validate_delay_values(args.delay_time)
+ if (args.enable_all):
+ set_delay_all_devices(file_map, args.delay_time,
+ args.quiet)
+ else:
+ set_delay_values(path, file_map, args.delay_time,
+ args.quiet)
+ elif ("disable_all" in arg_set or "D" in arg_set):
+ disable_all_testing(file_map)
+ elif ("disable_single" in arg_set or "d" in arg_set):
+ disable_testing_single_device(path, file_map)
+ elif ("view_all" in arg_set or "V" in arg_set):
+ get_all_devices_test_status(file_map)
+ elif ("view_single" in arg_set or "v" in arg_set):
+ get_device_test_values(path, file_map)
+
+# Get the state location
+def locate_state(device, file_map):
+ return file_map[device][f_names.state_f.value]
+
+# Validate delay values to make sure they are acceptable to
+# enable delays on a device
+def validate_delay_values(delay):
+
+ if (delay[0] == -1 and delay[1] == -1):
+ print("\nError, At least 1 value must be greater than 0")
+ exit(-1)
+ for i in delay:
+ if (i < -1 or i == 0 or i > 1000):
+ print("\nError, Values must be equal to -1 "
+ "or be > 0 and <= 1000")
+ exit(-1)
+
+# Validate argument path
+def validate_args_path(path, arg_set, file_map):
+
+ if (not path and any(element in arg_set for element in single_actions)):
+ print("Error, path (-p) REQUIRED for the specified option. "
+ "Use (-h) to check usage.")
+ exit(-1)
+ elif (path and any(item in arg_set for item in all_actions)):
+ print("Error, path (-p) NOT REQUIRED for the specified option. "
+ "Use (-h) to check usage." )
+ exit(-1)
+ elif (path not in file_map and any(item in arg_set
+ for item in single_actions)):
+ print("Error, path '{}' not a valid vmbus device".format(path))
+ exit(-1)
+
+# display Testing status of single device
+def get_device_test_values(path, file_map):
+
+ for name in file_map[path]:
+ file_location = file_map[path][name]
+ print( name + " = " + str(read_test_files(file_location)))
+
+# Create a map of the vmbus devices and their associated files
+# [key=device, value = [key = filename, value = file path]]
+def recursive_file_lookup(path, file_map):
+
+ for f_path in glob.iglob(path + '**/*'):
+ if (os.path.isfile(f_path)):
+ if (f_path.rsplit("/",2)[0] == debugfs_hyperv_path):
+ directory = f_path.rsplit("/",1)[0]
+ else:
+ directory = f_path.rsplit("/",2)[0]
+ f_name = f_path.split("/")[-1]
+ if (file_map.get(directory)):
+ file_map[directory].update({f_name:f_path})
+ else:
+ file_map[directory] = {f_name:f_path}
+ elif (os.path.isdir(f_path)):
+ recursive_file_lookup(f_path,file_map)
+ return file_map
+
+# display Testing state of devices
+def get_all_devices_test_status(file_map):
+
+ for device in file_map:
+ if (get_test_state(locate_state(device, file_map)) is 1):
+ print("Testing = ON for: {}"
+ .format(device.split("/")[5]))
+ else:
+ print("Testing = OFF for: {}"
+ .format(device.split("/")[5]))
+
+# read the vmbus device files, path must be absolute path before calling
+def read_test_files(path):
+ try:
+ with open(path,"r") as f:
+ file_value = f.readline().strip()
+ return int(file_value)
+
+ except IOError as e:
+ errno, strerror = e.args
+ print("I/O error({0}): {1} on file {2}"
+ .format(errno, strerror, path))
+ exit(-1)
+ except ValueError:
+ print ("Element to int conversion error in: \n{}".format(path))
+ exit(-1)
+
+# writing to vmbus device files, path must be absolute path before calling
+def write_test_files(path, value):
+
+ try:
+ with open(path,"w") as f:
+ f.write("{}".format(value))
+ except IOError as e:
+ errno, strerror = e.args
+ print("I/O error({0}): {1} on file {2}"
+ .format(errno, strerror, path))
+ exit(-1)
+
+# set testing state of device
+def set_test_state(state_path, state_value, quiet):
+
+ write_test_files(state_path, state_value)
+ if (get_test_state(state_path) is 1):
+ if (not quiet):
+ print("Testing = ON for device: {}"
+ .format(state_path.split("/")[5]))
+ else:
+ if (not quiet):
+ print("Testing = OFF for device: {}"
+ .format(state_path.split("/")[5]))
+
+# get testing state of device
+def get_test_state(state_path):
+ #state == 1 - test = ON
+ #state == 0 - test = OFF
+ return read_test_files(state_path)
+
+# write 1 - 1000 microseconds, into a single device using the
+# fuzz_test_buffer_interrupt_delay and fuzz_test_message_delay
+# debugfs attributes
+def set_delay_values(device, file_map, delay_length, quiet):
+
+ try:
+ interrupt = file_map[device][f_names.buff_f.value]
+ message = file_map[device][f_names.mess_f.value]
+
+ # delay[0]- buffer interrupt delay, delay[1]- message delay
+ if (delay_length[0] >= 0 and delay_length[0] <= 1000):
+ write_test_files(interrupt, delay_length[0])
+ if (delay_length[1] >= 0 and delay_length[1] <= 1000):
+ write_test_files(message, delay_length[1])
+ if (not quiet):
+ print("Buffer delay testing = {} for: {}"
+ .format(read_test_files(interrupt),
+ interrupt.split("/")[5]))
+ print("Message delay testing = {} for: {}"
+ .format(read_test_files(message),
+ message.split("/")[5]))
+ except IOError as e:
+ errno, strerror = e.args
+ print("I/O error({0}): {1} on files {2}{3}"
+ .format(errno, strerror, interrupt, message))
+ exit(-1)
+
+# enabling delay testing on all devices
+def set_delay_all_devices(file_map, delay, quiet):
+
+ for device in (file_map):
+ set_test_state(locate_state(device, file_map),
+ dev_state.on.value,
+ quiet)
+ set_delay_values(device, file_map, delay, quiet)
+
+# disable all testing on a SINGLE device.
+def disable_testing_single_device(device, file_map):
+
+ for name in file_map[device]:
+ file_location = file_map[device][name]
+ write_test_files(file_location, dev_state.off.value)
+ print("ALL testing now OFF for {}".format(device.split("/")[-1]))
+
+# disable all testing on ALL devices
+def disable_all_testing(file_map):
+
+ for device in file_map:
+ disable_testing_single_device(device, file_map)
+
+def parse_args():
+ parser = argparse.ArgumentParser(prog = "vmbus_testing",usage ="\n"
+ "%(prog)s [delay] [-h] [-e|-E] -t [-p]\n"
+ "%(prog)s [view_all | V] [-h]\n"
+ "%(prog)s [disable_all | D] [-h]\n"
+ "%(prog)s [disable_single | d] [-h|-p]\n"
+ "%(prog)s [view_single | v] [-h|-p]\n"
+ "%(prog)s --version\n",
+ description = "\nUse lsvmbus to get vmbus device type "
+ "information.\n" "\nThe debugfs root path is "
+ "/sys/kernel/debug/hyperv",
+ formatter_class = RawDescriptionHelpFormatter)
+ subparsers = parser.add_subparsers(dest = "action")
+ parser.add_argument("--version", action = "version",
+ version = '%(prog)s 0.1.0')
+ parser.add_argument("-q","--quiet", action = "store_true",
+ help = "silence none important test messages."
+ " This will only work when enabling testing"
+ " on a device.")
+ # Use the path parser to hold the --path attribute so it can
+ # be shared between subparsers. Also do the same for the state
+ # parser, as all testing methods will use --enable_all and
+ # enable_single.
+ path_parser = argparse.ArgumentParser(add_help=False)
+ path_parser.add_argument("-p","--path", metavar = "",
+ help = "Debugfs path to a vmbus device. The path "
+ "must be the absolute path to the device.")
+ state_parser = argparse.ArgumentParser(add_help=False)
+ state_group = state_parser.add_mutually_exclusive_group(required = True)
+ state_group.add_argument("-E", "--enable_all", action = "store_const",
+ const = "enable_all",
+ help = "Enable the specified test type "
+ "on ALL vmbus devices.")
+ state_group.add_argument("-e", "--enable_single",
+ action = "store_const",
+ const = "enable_single",
+ help = "Enable the specified test type on a "
+ "SINGLE vmbus device.")
+ parser_delay = subparsers.add_parser("delay",
+ parents = [state_parser, path_parser],
+ help = "Delay the ring buffer interrupt or the "
+ "ring buffer message reads in microseconds.",
+ prog = "vmbus_testing",
+ usage = "%(prog)s [-h]\n"
+ "%(prog)s -E -t [value] [value]\n"
+ "%(prog)s -e -t [value] [value] -p",
+ description = "Delay the ring buffer interrupt for "
+ "vmbus devices, or delay the ring buffer message "
+ "reads for vmbus devices (both in microseconds). This "
+ "is only on the host to guest channel.")
+ parser_delay.add_argument("-t", "--delay_time", metavar = "", nargs = 2,
+ type = check_range, default =[0,0], required = (True),
+ help = "Set [buffer] & [message] delay time. "
+ "Value constraints: -1 == value "
+ "or 0 < value <= 1000.\n"
+ "Use -1 to keep the previous value for that delay "
+ "type, or a value > 0 <= 1000 to change the delay "
+ "time.")
+ parser_dis_all = subparsers.add_parser("disable_all",
+ aliases = ['D'], prog = "vmbus_testing",
+ usage = "%(prog)s [disable_all | D] -h\n"
+ "%(prog)s [disable_all | D]\n",
+ help = "Disable ALL testing on ALL vmbus devices.",
+ description = "Disable ALL testing on ALL vmbus "
+ "devices.")
+ parser_dis_single = subparsers.add_parser("disable_single",
+ aliases = ['d'],
+ parents = [path_parser], prog = "vmbus_testing",
+ usage = "%(prog)s [disable_single | d] -h\n"
+ "%(prog)s [disable_single | d] -p\n",
+ help = "Disable ALL testing on a SINGLE vmbus device.",
+ description = "Disable ALL testing on a SINGLE vmbus "
+ "device.")
+ parser_view_all = subparsers.add_parser("view_all", aliases = ['V'],
+ help = "View the test state for ALL vmbus devices.",
+ prog = "vmbus_testing",
+ usage = "%(prog)s [view_all | V] -h\n"
+ "%(prog)s [view_all | V]\n",
+ description = "This shows the test state for ALL the "
+ "vmbus devices.")
+ parser_view_single = subparsers.add_parser("view_single",
+ aliases = ['v'],parents = [path_parser],
+ help = "View the test values for a SINGLE vmbus "
+ "device.",
+ description = "This shows the test values for a SINGLE "
+ "vmbus device.", prog = "vmbus_testing",
+ usage = "%(prog)s [view_single | v] -h\n"
+ "%(prog)s [view_single | v] -p")
+
+ return parser.parse_args()
+
+# value checking for range checking input in parser
+def check_range(arg1):
+
+ try:
+ val = int(arg1)
+ except ValueError as err:
+ raise argparse.ArgumentTypeError(str(err))
+ if val < -1 or val > 1000:
+ message = ("\n\nvalue must be -1 or 0 < value <= 1000. "
+ "Value program received: {}\n").format(val)
+ raise argparse.ArgumentTypeError(message)
+ return val
+
+if __name__ == "__main__":
+ main()
--
2.17.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox