Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f53335e3 authored by Dexuan Cui's avatar Dexuan Cui Committed by Sasha Levin
Browse files

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: default avatarDexuan Cui <decui@microsoft.com>
Reviewed-by: default avatarMichael Kelley <mikelley@microsoft.com>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent e3ede02a
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -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;
+2 −0
Original line number Diff line number Diff line
@@ -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);
+59 −0
Original line number Diff line number Diff line
@@ -2089,6 +2089,51 @@ static int vmbus_acpi_add(struct acpi_device *device)
	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)