diff options
author | Dexuan Cui <decui@microsoft.com> | 2020-01-26 06:49:44 +0100 |
---|---|---|
committer | Sasha Levin <sashal@kernel.org> | 2020-01-27 04:10:17 +0100 |
commit | 54e19d34011fea26d39aa74781131de0ce642a01 (patch) | |
tree | 3d0913466b3f1cc1f90338f44bc635d73d8809f0 /drivers/hv/hv_fcopy.c | |
parent | hv_utils: Support host-initiated hibernation request (diff) | |
download | linux-54e19d34011fea26d39aa74781131de0ce642a01.tar.xz linux-54e19d34011fea26d39aa74781131de0ce642a01.zip |
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 <decui@microsoft.com>
Reviewed-by: Michael Kelley <mikelley@microsoft.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
Diffstat (limited to 'drivers/hv/hv_fcopy.c')
-rw-r--r-- | drivers/hv/hv_fcopy.c | 54 |
1 files changed, 53 insertions, 1 deletions
diff --git a/drivers/hv/hv_fcopy.c b/drivers/hv/hv_fcopy.c index 08fa4a5de644..bb9ba3f7c794 100644 --- a/drivers/hv/hv_fcopy.c +++ b/drivers/hv/hv_fcopy.c @@ -346,9 +346,61 @@ int hv_fcopy_init(struct hv_util_service *srv) return 0; } +static void hv_fcopy_cancel_work(void) +{ + cancel_delayed_work_sync(&fcopy_timeout_work); + cancel_work_sync(&fcopy_send_work); +} + +int hv_fcopy_pre_suspend(void) +{ + struct vmbus_channel *channel = fcopy_transaction.recv_channel; + struct hv_fcopy_hdr *fcopy_msg; + + /* + * Fake a CANCEL_FCOPY message for the user space daemon in case the + * daemon is in the middle of copying some file. It doesn't matter if + * there is already a message pending to be delivered to the user + * space since we force fcopy_transaction.state to be HVUTIL_READY, so + * the user space daemon's write() will fail with EINVAL (see + * fcopy_on_msg()), and the daemon will reset the device by closing + * and re-opening it. + */ + fcopy_msg = kzalloc(sizeof(*fcopy_msg), GFP_KERNEL); + if (!fcopy_msg) + return -ENOMEM; + + tasklet_disable(&channel->callback_event); + + fcopy_msg->operation = CANCEL_FCOPY; + + hv_fcopy_cancel_work(); + + /* We don't care about the return value. */ + hvutil_transport_send(hvt, fcopy_msg, sizeof(*fcopy_msg), NULL); + + kfree(fcopy_msg); + + fcopy_transaction.state = HVUTIL_READY; + + /* tasklet_enable() will be called in hv_fcopy_pre_resume(). */ + return 0; +} + +int hv_fcopy_pre_resume(void) +{ + struct vmbus_channel *channel = fcopy_transaction.recv_channel; + + tasklet_enable(&channel->callback_event); + + return 0; +} + 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); } |