2022-07-10 18:30:25

by Shradha Gupta

[permalink] [raw]
Subject: [PATCH v2] Drivers: hv: vm_bus: Handle vmbus rescind calls after vmbus is suspended

Add a flag to indicate that the vmbus is suspended so we should ignore
any offer message. Add a new work_queue for rescind msg, so we could drain
it along with other offer work_queues upon suspension.
It was observed that in some hibernation related scenario testing, after
vmbus_bus_suspend() we get rescind offer message for the vmbus. This would
lead to processing of a rescind message for a channel that has already been
suspended.

Signed-off-by: Shradha Gupta <[email protected]>
---

Changes in v2:
* Rename the ignore_offer_rescind_msg flag to ignore_any_offer_msg, to
indicate that the flag can cause any offer message to be dropped.
* Remove redundent tasklet_enable(), tasklet_disable() calls around
ignore_any_offer_msg flag when value is changed from true to false.
* Add comment about tasklet_enable() providing memory barrier.
* In vmbus_bus_suspend() after we drain all workqueues, remove the code
to wait for any offer_in_progress

---
drivers/hv/connection.c | 11 +++++++++++
drivers/hv/hyperv_vmbus.h | 7 +++++++
drivers/hv/vmbus_drv.c | 29 +++++++++++++++++++++--------
3 files changed, 39 insertions(+), 8 deletions(-)

diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c
index 6218bbf6863a..eca7afd366d6 100644
--- a/drivers/hv/connection.c
+++ b/drivers/hv/connection.c
@@ -171,6 +171,14 @@ int vmbus_connect(void)
goto cleanup;
}

+ vmbus_connection.rescind_work_queue =
+ create_workqueue("hv_vmbus_rescind");
+ if (!vmbus_connection.rescind_work_queue) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+ vmbus_connection.ignore_any_offer_msg = false;
+
vmbus_connection.handle_primary_chan_wq =
create_workqueue("hv_pri_chan");
if (!vmbus_connection.handle_primary_chan_wq) {
@@ -357,6 +365,9 @@ void vmbus_disconnect(void)
if (vmbus_connection.handle_primary_chan_wq)
destroy_workqueue(vmbus_connection.handle_primary_chan_wq);

+ if (vmbus_connection.rescind_work_queue)
+ destroy_workqueue(vmbus_connection.rescind_work_queue);
+
if (vmbus_connection.work_queue)
destroy_workqueue(vmbus_connection.work_queue);

diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
index 4f5b824b16cf..dc673edf053c 100644
--- a/drivers/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
@@ -261,6 +261,13 @@ struct vmbus_connection {
struct workqueue_struct *work_queue;
struct workqueue_struct *handle_primary_chan_wq;
struct workqueue_struct *handle_sub_chan_wq;
+ struct workqueue_struct *rescind_work_queue;
+
+ /*
+ * On suspension of the vmbus, the accumulated offer messages
+ * must be dropped.
+ */
+ bool ignore_any_offer_msg;

/*
* The number of sub-channels and hv_sock channels that should be
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
index 547ae334e5cd..4ba0eb2441cf 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
@@ -1160,7 +1160,9 @@ void vmbus_on_msg_dpc(unsigned long data)
* work queue: the RESCIND handler can not start to
* run before the OFFER handler finishes.
*/
- schedule_work(&ctx->work);
+ if (vmbus_connection.ignore_any_offer_msg)
+ break;
+ queue_work(vmbus_connection.rescind_work_queue, &ctx->work);
break;

case CHANNELMSG_OFFERCHANNEL:
@@ -1186,6 +1188,8 @@ void vmbus_on_msg_dpc(unsigned long data)
* to the CPUs which will execute the offer & rescind
* works by the time these works will start execution.
*/
+ if (vmbus_connection.ignore_any_offer_msg)
+ break;
atomic_inc(&vmbus_connection.offer_in_progress);
fallthrough;

@@ -2446,15 +2450,20 @@ static int vmbus_acpi_add(struct acpi_device *device)
#ifdef CONFIG_PM_SLEEP
static int vmbus_bus_suspend(struct device *dev)
{
+ struct hv_per_cpu_context *hv_cpu = per_cpu_ptr(
+ hv_context.cpu_context, VMBUS_CONNECT_CPU);
struct vmbus_channel *channel, *sc;

- while (atomic_read(&vmbus_connection.offer_in_progress) != 0) {
- /*
- * We wait here until the completion of any channel
- * offers that are currently in progress.
- */
- usleep_range(1000, 2000);
- }
+ tasklet_disable(&hv_cpu->msg_dpc);
+ vmbus_connection.ignore_any_offer_msg = true;
+ /* The tasklet_enable() takes care of providing a memory barrier */
+ tasklet_enable(&hv_cpu->msg_dpc);
+
+ /* Drain all the workqueues as we are in suspend */
+ drain_workqueue(vmbus_connection.rescind_work_queue);
+ drain_workqueue(vmbus_connection.work_queue);
+ drain_workqueue(vmbus_connection.handle_primary_chan_wq);
+ drain_workqueue(vmbus_connection.handle_sub_chan_wq);

mutex_lock(&vmbus_connection.channel_mutex);
list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) {
@@ -2527,10 +2536,14 @@ static int vmbus_bus_suspend(struct device *dev)

static int vmbus_bus_resume(struct device *dev)
{
+ struct hv_per_cpu_context *hv_cpu = per_cpu_ptr(
+ hv_context.cpu_context, VMBUS_CONNECT_CPU);
struct vmbus_channel_msginfo *msginfo;
size_t msgsize;
int ret;

+ vmbus_connection.ignore_any_offer_msg = false;
+
/*
* We only use the 'vmbus_proto_version', which was in use before
* hibernation, to re-negotiate with the host.
--
2.17.1


2022-07-11 01:48:08

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v2] Drivers: hv: vm_bus: Handle vmbus rescind calls after vmbus is suspended

Hi Shradha,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on linus/master]
[also build test WARNING on v5.19-rc5 next-20220708]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url: https://github.com/intel-lab-lkp/linux/commits/Shradha-Gupta/Drivers-hv-vm_bus-Handle-vmbus-rescind-calls-after-vmbus-is-suspended/20220711-021702
base: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 5867f3b88bb54016c42cdde510c184255488a12b
config: i386-allyesconfig (https://download.01.org/0day-ci/archive/20220711/[email protected]/config)
compiler: gcc-11 (Debian 11.3.0-3) 11.3.0
reproduce (this is a W=1 build):
# https://github.com/intel-lab-lkp/linux/commit/7fbe63d321d3e0c099e90e8d8c36921c58c8868f
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Shradha-Gupta/Drivers-hv-vm_bus-Handle-vmbus-rescind-calls-after-vmbus-is-suspended/20220711-021702
git checkout 7fbe63d321d3e0c099e90e8d8c36921c58c8868f
# save the config file
mkdir build_dir && cp config build_dir/.config
make W=1 O=build_dir ARCH=i386 SHELL=/bin/bash drivers/hv/

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <[email protected]>

All warnings (new ones prefixed by >>):

drivers/hv/vmbus_drv.c: In function 'vmbus_bus_resume':
>> drivers/hv/vmbus_drv.c:2539:36: warning: unused variable 'hv_cpu' [-Wunused-variable]
2539 | struct hv_per_cpu_context *hv_cpu = per_cpu_ptr(
| ^~~~~~


vim +/hv_cpu +2539 drivers/hv/vmbus_drv.c

2536
2537 static int vmbus_bus_resume(struct device *dev)
2538 {
> 2539 struct hv_per_cpu_context *hv_cpu = per_cpu_ptr(
2540 hv_context.cpu_context, VMBUS_CONNECT_CPU);
2541 struct vmbus_channel_msginfo *msginfo;
2542 size_t msgsize;
2543 int ret;
2544
2545 vmbus_connection.ignore_any_offer_msg = false;
2546
2547 /*
2548 * We only use the 'vmbus_proto_version', which was in use before
2549 * hibernation, to re-negotiate with the host.
2550 */
2551 if (!vmbus_proto_version) {
2552 pr_err("Invalid proto version = 0x%x\n", vmbus_proto_version);
2553 return -EINVAL;
2554 }
2555
2556 msgsize = sizeof(*msginfo) +
2557 sizeof(struct vmbus_channel_initiate_contact);
2558
2559 msginfo = kzalloc(msgsize, GFP_KERNEL);
2560
2561 if (msginfo == NULL)
2562 return -ENOMEM;
2563
2564 ret = vmbus_negotiate_version(msginfo, vmbus_proto_version);
2565
2566 kfree(msginfo);
2567
2568 if (ret != 0)
2569 return ret;
2570
2571 WARN_ON(atomic_read(&vmbus_connection.nr_chan_fixup_on_resume) == 0);
2572
2573 vmbus_request_offers();
2574
2575 if (wait_for_completion_timeout(
2576 &vmbus_connection.ready_for_resume_event, 10 * HZ) == 0)
2577 pr_err("Some vmbus device is missing after suspending?\n");
2578
2579 /* Reset the event for the next suspend. */
2580 reinit_completion(&vmbus_connection.ready_for_suspend_event);
2581
2582 return 0;
2583 }
2584 #else
2585 #define vmbus_bus_suspend NULL
2586 #define vmbus_bus_resume NULL
2587 #endif /* CONFIG_PM_SLEEP */
2588

--
0-DAY CI Kernel Test Service
https://01.org/lkp