Drivers: hv: vmbus: Suspend/resume the vmbus itself for hibernation
Before Linux enters hibernation, it sends the CHANNELMSG_UNLOAD message to the host so all the offers are gone. After hibernation, Linux needs to re-negotiate with the host using the same vmbus protocol version (which was in use before hibernation), and ask the host to re-offer the vmbus devices. Signed-off-by: Dexuan Cui <decui@microsoft.com> Reviewed-by: Michael Kelley <mikelley@microsoft.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Родитель
e3ede02add
Коммит
f53335e328
|
@ -59,8 +59,7 @@ static __u32 vmbus_get_next_version(__u32 current_version)
|
|||
}
|
||||
}
|
||||
|
||||
static int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo,
|
||||
__u32 version)
|
||||
int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned int cur_cpu;
|
||||
|
|
|
@ -274,6 +274,8 @@ struct vmbus_msginfo {
|
|||
|
||||
extern struct vmbus_connection vmbus_connection;
|
||||
|
||||
int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version);
|
||||
|
||||
static inline void vmbus_send_interrupt(u32 relid)
|
||||
{
|
||||
sync_set_bit(relid, vmbus_connection.send_int_page);
|
||||
|
|
|
@ -2089,6 +2089,51 @@ acpi_walk_err:
|
|||
return ret_val;
|
||||
}
|
||||
|
||||
static int vmbus_bus_suspend(struct device *dev)
|
||||
{
|
||||
vmbus_initiate_unload(false);
|
||||
|
||||
vmbus_connection.conn_state = DISCONNECTED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vmbus_bus_resume(struct device *dev)
|
||||
{
|
||||
struct vmbus_channel_msginfo *msginfo;
|
||||
size_t msgsize;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* We only use the 'vmbus_proto_version', which was in use before
|
||||
* hibernation, to re-negotiate with the host.
|
||||
*/
|
||||
if (vmbus_proto_version == VERSION_INVAL ||
|
||||
vmbus_proto_version == 0) {
|
||||
pr_err("Invalid proto version = 0x%x\n", vmbus_proto_version);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
msgsize = sizeof(*msginfo) +
|
||||
sizeof(struct vmbus_channel_initiate_contact);
|
||||
|
||||
msginfo = kzalloc(msgsize, GFP_KERNEL);
|
||||
|
||||
if (msginfo == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = vmbus_negotiate_version(msginfo, vmbus_proto_version);
|
||||
|
||||
kfree(msginfo);
|
||||
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
vmbus_request_offers();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id vmbus_acpi_device_ids[] = {
|
||||
{"VMBUS", 0},
|
||||
{"VMBus", 0},
|
||||
|
@ -2096,6 +2141,19 @@ static const struct acpi_device_id vmbus_acpi_device_ids[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(acpi, vmbus_acpi_device_ids);
|
||||
|
||||
/*
|
||||
* Note: we must use SET_NOIRQ_SYSTEM_SLEEP_PM_OPS rather than
|
||||
* SET_SYSTEM_SLEEP_PM_OPS, otherwise NIC SR-IOV can not work, because the
|
||||
* "pci_dev_pm_ops" uses the "noirq" callbacks: in the resume path, the
|
||||
* pci "noirq" restore callback runs before "non-noirq" callbacks (see
|
||||
* resume_target_kernel() -> dpm_resume_start(), and hibernation_restore() ->
|
||||
* dpm_resume_end()). This means vmbus_bus_resume() and the pci-hyperv's
|
||||
* resume callback must also run via the "noirq" callbacks.
|
||||
*/
|
||||
static const struct dev_pm_ops vmbus_bus_pm = {
|
||||
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(vmbus_bus_suspend, vmbus_bus_resume)
|
||||
};
|
||||
|
||||
static struct acpi_driver vmbus_acpi_driver = {
|
||||
.name = "vmbus",
|
||||
.ids = vmbus_acpi_device_ids,
|
||||
|
@ -2103,6 +2161,7 @@ static struct acpi_driver vmbus_acpi_driver = {
|
|||
.add = vmbus_acpi_add,
|
||||
.remove = vmbus_acpi_remove,
|
||||
},
|
||||
.drv.pm = &vmbus_bus_pm,
|
||||
};
|
||||
|
||||
static void hv_kexec_handler(void)
|
||||
|
|
Загрузка…
Ссылка в новой задаче