Skip to content

Commit 54e19d3

Browse files
dcuiSasha Levin
authored andcommitted
hv_utils: Add the support of hibernation
Add util_pre_suspend() and util_pre_resume() for some hv_utils devices (e.g. kvp/vss/fcopy), because they need special handling before util_suspend() calls vmbus_close(). For kvp, all the possible pending work items should be cancelled. For vss and fcopy, some extra clean-up needs to be done, i.e. fake a THAW message for hv_vss_daemon and fake a CANCEL_FCOPY message for hv_fcopy_daemon, otherwise when the VM resums back, the daemons can end up in an inconsistent state (i.e. the file systems are frozen but will never be thawed; the file transmitted via fcopy may not be complete). Note: there is an extra patch for the daemons: "Tools: hv: Reopen the devices if read() or write() returns errors", because the hv_utils driver can not guarantee the whole transaction finishes completely once util_suspend() starts to run (at this time, all the userspace processes are frozen). util_probe() disables channel->callback_event to avoid the race with the channel callback. Signed-off-by: Dexuan Cui <[email protected]> Reviewed-by: Michael Kelley <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent ffd1d4a commit 54e19d3

File tree

6 files changed

+216
-6
lines changed

6 files changed

+216
-6
lines changed

drivers/hv/hv_fcopy.c

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,9 +346,61 @@ int hv_fcopy_init(struct hv_util_service *srv)
346346
return 0;
347347
}
348348

349+
static void hv_fcopy_cancel_work(void)
350+
{
351+
cancel_delayed_work_sync(&fcopy_timeout_work);
352+
cancel_work_sync(&fcopy_send_work);
353+
}
354+
355+
int hv_fcopy_pre_suspend(void)
356+
{
357+
struct vmbus_channel *channel = fcopy_transaction.recv_channel;
358+
struct hv_fcopy_hdr *fcopy_msg;
359+
360+
/*
361+
* Fake a CANCEL_FCOPY message for the user space daemon in case the
362+
* daemon is in the middle of copying some file. It doesn't matter if
363+
* there is already a message pending to be delivered to the user
364+
* space since we force fcopy_transaction.state to be HVUTIL_READY, so
365+
* the user space daemon's write() will fail with EINVAL (see
366+
* fcopy_on_msg()), and the daemon will reset the device by closing
367+
* and re-opening it.
368+
*/
369+
fcopy_msg = kzalloc(sizeof(*fcopy_msg), GFP_KERNEL);
370+
if (!fcopy_msg)
371+
return -ENOMEM;
372+
373+
tasklet_disable(&channel->callback_event);
374+
375+
fcopy_msg->operation = CANCEL_FCOPY;
376+
377+
hv_fcopy_cancel_work();
378+
379+
/* We don't care about the return value. */
380+
hvutil_transport_send(hvt, fcopy_msg, sizeof(*fcopy_msg), NULL);
381+
382+
kfree(fcopy_msg);
383+
384+
fcopy_transaction.state = HVUTIL_READY;
385+
386+
/* tasklet_enable() will be called in hv_fcopy_pre_resume(). */
387+
return 0;
388+
}
389+
390+
int hv_fcopy_pre_resume(void)
391+
{
392+
struct vmbus_channel *channel = fcopy_transaction.recv_channel;
393+
394+
tasklet_enable(&channel->callback_event);
395+
396+
return 0;
397+
}
398+
349399
void hv_fcopy_deinit(void)
350400
{
351401
fcopy_transaction.state = HVUTIL_DEVICE_DYING;
352-
cancel_delayed_work_sync(&fcopy_timeout_work);
402+
403+
hv_fcopy_cancel_work();
404+
353405
hvutil_transport_destroy(hvt);
354406
}

drivers/hv/hv_kvp.c

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -758,11 +758,50 @@ hv_kvp_init(struct hv_util_service *srv)
758758
return 0;
759759
}
760760

761-
void hv_kvp_deinit(void)
761+
static void hv_kvp_cancel_work(void)
762762
{
763-
kvp_transaction.state = HVUTIL_DEVICE_DYING;
764763
cancel_delayed_work_sync(&kvp_host_handshake_work);
765764
cancel_delayed_work_sync(&kvp_timeout_work);
766765
cancel_work_sync(&kvp_sendkey_work);
766+
}
767+
768+
int hv_kvp_pre_suspend(void)
769+
{
770+
struct vmbus_channel *channel = kvp_transaction.recv_channel;
771+
772+
tasklet_disable(&channel->callback_event);
773+
774+
/*
775+
* If there is a pending transtion, it's unnecessary to tell the host
776+
* that the transaction will fail, because that is implied when
777+
* util_suspend() calls vmbus_close() later.
778+
*/
779+
hv_kvp_cancel_work();
780+
781+
/*
782+
* Forece the state to READY to handle the ICMSGTYPE_NEGOTIATE message
783+
* later. The user space daemon may go out of order and its write()
784+
* may fail with EINVAL: this doesn't matter since the daemon will
785+
* reset the device by closing and re-opening it.
786+
*/
787+
kvp_transaction.state = HVUTIL_READY;
788+
return 0;
789+
}
790+
791+
int hv_kvp_pre_resume(void)
792+
{
793+
struct vmbus_channel *channel = kvp_transaction.recv_channel;
794+
795+
tasklet_enable(&channel->callback_event);
796+
797+
return 0;
798+
}
799+
800+
void hv_kvp_deinit(void)
801+
{
802+
kvp_transaction.state = HVUTIL_DEVICE_DYING;
803+
804+
hv_kvp_cancel_work();
805+
767806
hvutil_transport_destroy(hvt);
768807
}

drivers/hv/hv_snapshot.c

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -379,10 +379,61 @@ hv_vss_init(struct hv_util_service *srv)
379379
return 0;
380380
}
381381

382-
void hv_vss_deinit(void)
382+
static void hv_vss_cancel_work(void)
383383
{
384-
vss_transaction.state = HVUTIL_DEVICE_DYING;
385384
cancel_delayed_work_sync(&vss_timeout_work);
386385
cancel_work_sync(&vss_handle_request_work);
386+
}
387+
388+
int hv_vss_pre_suspend(void)
389+
{
390+
struct vmbus_channel *channel = vss_transaction.recv_channel;
391+
struct hv_vss_msg *vss_msg;
392+
393+
/*
394+
* Fake a THAW message for the user space daemon in case the daemon
395+
* has frozen the file systems. It doesn't matter if there is already
396+
* a message pending to be delivered to the user space since we force
397+
* vss_transaction.state to be HVUTIL_READY, so the user space daemon's
398+
* write() will fail with EINVAL (see vss_on_msg()), and the daemon
399+
* will reset the device by closing and re-opening it.
400+
*/
401+
vss_msg = kzalloc(sizeof(*vss_msg), GFP_KERNEL);
402+
if (!vss_msg)
403+
return -ENOMEM;
404+
405+
tasklet_disable(&channel->callback_event);
406+
407+
vss_msg->vss_hdr.operation = VSS_OP_THAW;
408+
409+
/* Cancel any possible pending work. */
410+
hv_vss_cancel_work();
411+
412+
/* We don't care about the return value. */
413+
hvutil_transport_send(hvt, vss_msg, sizeof(*vss_msg), NULL);
414+
415+
kfree(vss_msg);
416+
417+
vss_transaction.state = HVUTIL_READY;
418+
419+
/* tasklet_enable() will be called in hv_vss_pre_resume(). */
420+
return 0;
421+
}
422+
423+
int hv_vss_pre_resume(void)
424+
{
425+
struct vmbus_channel *channel = vss_transaction.recv_channel;
426+
427+
tasklet_enable(&channel->callback_event);
428+
429+
return 0;
430+
}
431+
432+
void hv_vss_deinit(void)
433+
{
434+
vss_transaction.state = HVUTIL_DEVICE_DYING;
435+
436+
hv_vss_cancel_work();
437+
387438
hvutil_transport_destroy(hvt);
388439
}

drivers/hv/hv_util.c

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,12 +123,14 @@ static struct hv_util_service util_shutdown = {
123123
};
124124

125125
static int hv_timesync_init(struct hv_util_service *srv);
126+
static int hv_timesync_pre_suspend(void);
126127
static void hv_timesync_deinit(void);
127128

128129
static void timesync_onchannelcallback(void *context);
129130
static struct hv_util_service util_timesynch = {
130131
.util_cb = timesync_onchannelcallback,
131132
.util_init = hv_timesync_init,
133+
.util_pre_suspend = hv_timesync_pre_suspend,
132134
.util_deinit = hv_timesync_deinit,
133135
};
134136

@@ -140,18 +142,24 @@ static struct hv_util_service util_heartbeat = {
140142
static struct hv_util_service util_kvp = {
141143
.util_cb = hv_kvp_onchannelcallback,
142144
.util_init = hv_kvp_init,
145+
.util_pre_suspend = hv_kvp_pre_suspend,
146+
.util_pre_resume = hv_kvp_pre_resume,
143147
.util_deinit = hv_kvp_deinit,
144148
};
145149

146150
static struct hv_util_service util_vss = {
147151
.util_cb = hv_vss_onchannelcallback,
148152
.util_init = hv_vss_init,
153+
.util_pre_suspend = hv_vss_pre_suspend,
154+
.util_pre_resume = hv_vss_pre_resume,
149155
.util_deinit = hv_vss_deinit,
150156
};
151157

152158
static struct hv_util_service util_fcopy = {
153159
.util_cb = hv_fcopy_onchannelcallback,
154160
.util_init = hv_fcopy_init,
161+
.util_pre_suspend = hv_fcopy_pre_suspend,
162+
.util_pre_resume = hv_fcopy_pre_resume,
155163
.util_deinit = hv_fcopy_deinit,
156164
};
157165

@@ -511,6 +519,44 @@ static int util_remove(struct hv_device *dev)
511519
return 0;
512520
}
513521

522+
/*
523+
* When we're in util_suspend(), all the userspace processes have been frozen
524+
* (refer to hibernate() -> freeze_processes()). The userspace is thawed only
525+
* after the whole resume procedure, including util_resume(), finishes.
526+
*/
527+
static int util_suspend(struct hv_device *dev)
528+
{
529+
struct hv_util_service *srv = hv_get_drvdata(dev);
530+
int ret = 0;
531+
532+
if (srv->util_pre_suspend) {
533+
ret = srv->util_pre_suspend();
534+
if (ret)
535+
return ret;
536+
}
537+
538+
vmbus_close(dev->channel);
539+
540+
return 0;
541+
}
542+
543+
static int util_resume(struct hv_device *dev)
544+
{
545+
struct hv_util_service *srv = hv_get_drvdata(dev);
546+
int ret = 0;
547+
548+
if (srv->util_pre_resume) {
549+
ret = srv->util_pre_resume();
550+
if (ret)
551+
return ret;
552+
}
553+
554+
ret = vmbus_open(dev->channel, 4 * HV_HYP_PAGE_SIZE,
555+
4 * HV_HYP_PAGE_SIZE, NULL, 0, srv->util_cb,
556+
dev->channel);
557+
return ret;
558+
}
559+
514560
static const struct hv_vmbus_device_id id_table[] = {
515561
/* Shutdown guid */
516562
{ HV_SHUTDOWN_GUID,
@@ -547,6 +593,8 @@ static struct hv_driver util_drv = {
547593
.id_table = id_table,
548594
.probe = util_probe,
549595
.remove = util_remove,
596+
.suspend = util_suspend,
597+
.resume = util_resume,
550598
.driver = {
551599
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
552600
},
@@ -616,11 +664,23 @@ static int hv_timesync_init(struct hv_util_service *srv)
616664
return 0;
617665
}
618666

667+
static void hv_timesync_cancel_work(void)
668+
{
669+
cancel_work_sync(&adj_time_work);
670+
}
671+
672+
static int hv_timesync_pre_suspend(void)
673+
{
674+
hv_timesync_cancel_work();
675+
return 0;
676+
}
677+
619678
static void hv_timesync_deinit(void)
620679
{
621680
if (hv_ptp_clock)
622681
ptp_clock_unregister(hv_ptp_clock);
623-
cancel_work_sync(&adj_time_work);
682+
683+
hv_timesync_cancel_work();
624684
}
625685

626686
static int __init init_hyperv_utils(void)

drivers/hv/hyperv_vmbus.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,14 +352,20 @@ void vmbus_on_msg_dpc(unsigned long data);
352352

353353
int hv_kvp_init(struct hv_util_service *srv);
354354
void hv_kvp_deinit(void);
355+
int hv_kvp_pre_suspend(void);
356+
int hv_kvp_pre_resume(void);
355357
void hv_kvp_onchannelcallback(void *context);
356358

357359
int hv_vss_init(struct hv_util_service *srv);
358360
void hv_vss_deinit(void);
361+
int hv_vss_pre_suspend(void);
362+
int hv_vss_pre_resume(void);
359363
void hv_vss_onchannelcallback(void *context);
360364

361365
int hv_fcopy_init(struct hv_util_service *srv);
362366
void hv_fcopy_deinit(void);
367+
int hv_fcopy_pre_suspend(void);
368+
int hv_fcopy_pre_resume(void);
363369
void hv_fcopy_onchannelcallback(void *context);
364370
void vmbus_initiate_unload(bool crash);
365371

include/linux/hyperv.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1435,6 +1435,8 @@ struct hv_util_service {
14351435
void (*util_cb)(void *);
14361436
int (*util_init)(struct hv_util_service *);
14371437
void (*util_deinit)(void);
1438+
int (*util_pre_suspend)(void);
1439+
int (*util_pre_resume)(void);
14381440
};
14391441

14401442
struct vmbuspipe_hdr {

0 commit comments

Comments
 (0)