Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Drivers: hv: vmbus: Export hv_vmbus_exists() and use it in pci-hyperv

With commit f84b21da3624 ("PCI: hv: Don't load the driver for baremetal root partition"),
the bare metal Linux root partition won't use the pci-hyperv driver, but
when a Linux VM runs on the Linux root partition, pci-hyperv's module_init
function init_hv_pci_drv() can still run, e.g. in the case of
CONFIG_PCI_HYPERV=y, even if the VMBus driver is not used in such a VM
(i.e. the hv_vmbus driver's init function returns -ENODEV due to
vmbus_root_device being NULL).

In such a Linux VM, init_hv_pci_drv() runs with a side effect: the 3
hvpci_block_ops callbacks are set to functions that depend on hv_vmbus.

Later, when the MLX driver in such a VM invokes the callbacks, e.g. in
drivers/net/ethernet/mellanox/mlx5/core/lib/hv.c:
mlx5_hv_register_invalidate(), hvpci_block_ops.reg_blk_invalidate() is
hv_register_block_invalidate() rather than a NULL function pointer, and
hv_register_block_invalidate() assumes that it can find a struct
hv_pcibus_device from pdev->bus->sysdata, which is false in such a VM.

Consequently, hv_register_block_invalidate() -> get_pcichild_wslot() ->
spin_lock_irqsave() may hang since it can be accessing an invalid
spinlock pointer.

Fix the issue by exporting hv_vmbus_exists() and using it in pci-hyperv:

hv_root_partition() is true and hv_nested is false ==>
hv_vmbus_exists() is false.

hv_root_partition() is true and hv_nested is true ==>
hv_vmbus_exists() is true.

hv_root_partition() is false ==> hv_vmbus_exists() is true.

While at it, rename vmbus_exists() to hv_vmbus_exists() to follow the
convention that all public functions have the hv_ prefix; also change
the return value's type from int to bool to make the code more readable;
also move the two pr_info() calls.

Reported-by: Mukesh Rathor <mrathor@linux.microsoft.com>
Signed-off-by: Dexuan Cui <decui@microsoft.com>
Signed-off-by: Wei Liu <wei.liu@kernel.org>

authored by

Dexuan Cui and committed by
Wei Liu
0d5acba6 80acc80e

+11 -13
+8 -12
drivers/hv/vmbus_drv.c
··· 101 101 } 102 102 EXPORT_SYMBOL_GPL(hv_get_vmbus_root_device); 103 103 104 - static int vmbus_exists(void) 104 + bool hv_vmbus_exists(void) 105 105 { 106 - if (vmbus_root_device == NULL) 107 - return -ENODEV; 108 - 109 - return 0; 106 + return vmbus_root_device != NULL; 110 107 } 108 + EXPORT_SYMBOL_GPL(hv_vmbus_exists); 111 109 112 110 static u8 channel_monitor_group(const struct vmbus_channel *channel) 113 111 { ··· 1575 1577 { 1576 1578 int ret; 1577 1579 1578 - pr_info("registering driver %s\n", hv_driver->name); 1580 + if (!hv_vmbus_exists()) 1581 + return -ENODEV; 1579 1582 1580 - ret = vmbus_exists(); 1581 - if (ret < 0) 1582 - return ret; 1583 + pr_info("registering driver %s\n", hv_driver->name); 1583 1584 1584 1585 hv_driver->driver.name = hv_driver->name; 1585 1586 hv_driver->driver.owner = owner; ··· 1604 1607 */ 1605 1608 void vmbus_driver_unregister(struct hv_driver *hv_driver) 1606 1609 { 1607 - pr_info("unregistering driver %s\n", hv_driver->name); 1608 - 1609 - if (!vmbus_exists()) { 1610 + if (hv_vmbus_exists()) { 1611 + pr_info("unregistering driver %s\n", hv_driver->name); 1610 1612 driver_unregister(&hv_driver->driver); 1611 1613 vmbus_free_dynids(hv_driver); 1612 1614 }
+1 -1
drivers/pci/controller/pci-hyperv.c
··· 4172 4172 if (!hv_is_hyperv_initialized()) 4173 4173 return -ENODEV; 4174 4174 4175 - if (hv_root_partition() && !hv_nested) 4175 + if (!hv_vmbus_exists()) 4176 4176 return -ENODEV; 4177 4177 4178 4178 ret = hv_pci_irqchip_init();
+2
include/linux/hyperv.h
··· 1304 1304 1305 1305 struct device *hv_get_vmbus_root_device(void); 1306 1306 1307 + bool hv_vmbus_exists(void); 1308 + 1307 1309 struct hv_ring_buffer_debug_info { 1308 1310 u32 current_interrupt_mask; 1309 1311 u32 current_read_index;