This commit is contained in:
Mark Brown
2026-07-31 13:59:25 +01:00
9 changed files with 125 additions and 61 deletions
+28 -19
View File
@@ -72,7 +72,8 @@ module_param(max_version, uint, S_IRUGO);
MODULE_PARM_DESC(max_version,
"Maximal VMBus protocol version which can be negotiated");
int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
static int vmbus_try_connection_id(struct vmbus_channel_msginfo *msginfo,
u32 version, u32 connection_id)
{
int ret = 0;
struct vmbus_channel_initiate_contact *msg;
@@ -87,20 +88,20 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
msg->vmbus_version_requested = version;
/*
* VMBus protocol 5.0 (VERSION_WIN10_V5) and higher require that we must
* use VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate Contact Message,
* and for subsequent messages, we must use the Message Connection ID
* field in the host-returned Version Response Message. And, with
* VERSION_WIN10_V5 and higher, we don't use msg->interrupt_page, but we
* tell the host explicitly that we still use VMBUS_MESSAGE_SINT(2) for
* compatibility.
* For VMBus protocol 5.0 (VERSION_WIN10_V5) and higher, use the
* caller-supplied connection_id for the Initiate Contact message so
* the caller can implement the required retry scheme. For subsequent
* messages, use the Message Connection ID field in the host-returned
* Version Response message. With VERSION_WIN10_V5 and higher, we don't
* use msg->interrupt_page, but tell the host explicitly that we still
* use VMBUS_MESSAGE_SINT(2) for compatibility.
*
* On old hosts, we should always use VMBUS_MESSAGE_CONNECTION_ID (1).
*/
if (version >= VERSION_WIN10_V5) {
msg->msg_sint = VMBUS_MESSAGE_SINT;
msg->msg_vtl = ms_hyperv.vtl;
vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID_4;
vmbus_connection.msg_conn_id = connection_id;
} else {
msg->interrupt_page = virt_to_phys(vmbus_connection.int_page);
vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID;
@@ -165,6 +166,22 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
return ret;
}
int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
{
int ret;
/* Try the redirect ID first for VTL2 with VMBus protocol 5.0+. */
if (version >= VERSION_WIN10_V5 && ms_hyperv.vtl == 2) {
ret = vmbus_try_connection_id(msginfo, version,
VMBUS_MESSAGE_CONNECTION_ID_REDIRECT);
if (ret != -ENXIO)
return ret;
}
return vmbus_try_connection_id(msginfo, version,
VMBUS_MESSAGE_CONNECTION_ID_4);
}
/*
* vmbus_connect - Sends a connect request on the partition service connection
*/
@@ -457,18 +474,10 @@ int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep)
switch (ret) {
case HV_STATUS_INVALID_CONNECTION_ID:
/*
* See vmbus_negotiate_version(): VMBus protocol 5.0
* and higher require that we must use
* VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate
* Contact message, but on old hosts that only
* support VMBus protocol 4.0 or lower, here we get
* HV_STATUS_INVALID_CONNECTION_ID and we should
* return an error immediately without retrying.
*/
/* Allow INITIATE_CONTACT to try another connection ID. */
hdr = buffer;
if (hdr->msgtype == CHANNELMSG_INITIATE_CONTACT)
return -EINVAL;
return -ENXIO;
/*
* We could get this if we send messages too
* frequently.
+2
View File
@@ -110,6 +110,8 @@ struct hv_input_post_message {
enum {
VMBUS_MESSAGE_CONNECTION_ID = 1,
VMBUS_MESSAGE_CONNECTION_ID_4 = 4,
/* VTL2 redirect connection ID for INITIATE_CONTACT. */
VMBUS_MESSAGE_CONNECTION_ID_REDIRECT = 0x800074,
VMBUS_MESSAGE_PORT_ID = 1,
VMBUS_EVENT_CONNECTION_ID = 2,
VMBUS_EVENT_PORT_ID = 2,
+26 -15
View File
@@ -169,7 +169,14 @@ static int mshv_try_assert_irq_fast(struct mshv_irqfd *irqfd)
return -EOPNOTSUPP;
#endif
vp = partition->pt_vp_array[irq->lapic_apic_id];
/*
* Pairs with smp_store_release() in mshv_partition_ioctl_create_vp().
* MSHV_IRQFD does not require the target lapic_apic_id to refer to an
* existing VP, so this read can race a concurrent VP creation; the
* acquire ensures that a non-NULL pointer implies the VP's
* initialising stores are visible.
*/
vp = smp_load_acquire(&partition->pt_vp_array[irq->lapic_apic_id]);
if (!vp->vp_register_page)
return -EOPNOTSUPP;
@@ -284,7 +291,7 @@ static void mshv_irqfd_deactivate(struct mshv_irqfd *irqfd)
if (!mshv_irqfd_is_active(irqfd))
return;
hlist_del(&irqfd->irqfd_hnode);
hlist_del_init(&irqfd->irqfd_hnode);
queue_work(irqfd_cleanup_wq, &irqfd->irqfd_shutdown);
}
@@ -473,18 +480,6 @@ static int mshv_irqfd_assign(struct mshv_partition *pt,
init_poll_funcptr(&irqfd->irqfd_polltbl, mshv_irqfd_queue_proc);
spin_lock_irq(&pt->pt_irqfds_lock);
#if IS_ENABLED(CONFIG_X86)
if (args->flags & BIT(MSHV_IRQFD_BIT_RESAMPLE) &&
!irqfd->irqfd_lapic_irq.lapic_control.level_triggered) {
/*
* Resample Fd must be for level triggered interrupt
* Otherwise return with failure
*/
spin_unlock_irq(&pt->pt_irqfds_lock);
ret = -EINVAL;
goto fail;
}
#endif
ret = 0;
hlist_for_each_entry(tmp, &pt->pt_irqfds_list, irqfd_hnode) {
if (irqfd->irqfd_eventfd_ctx != tmp->irqfd_eventfd_ctx)
@@ -497,6 +492,21 @@ static int mshv_irqfd_assign(struct mshv_partition *pt,
idx = srcu_read_lock(&pt->pt_irq_srcu);
mshv_irqfd_update(pt, irqfd);
#if IS_ENABLED(CONFIG_X86)
if (args->flags & BIT(MSHV_IRQFD_BIT_RESAMPLE) &&
!irqfd->irqfd_lapic_irq.lapic_control.level_triggered) {
/*
* Resample Fd must be for level triggered interrupt
* Otherwise return with failure
*/
spin_unlock_irq(&pt->pt_irqfds_lock);
srcu_read_unlock(&pt->pt_irq_srcu, idx);
ret = -EINVAL;
goto fail;
}
#endif
hlist_add_head(&irqfd->irqfd_hnode, &pt->pt_irqfds_list);
spin_unlock_irq(&pt->pt_irqfds_lock);
@@ -541,13 +551,14 @@ static int mshv_irqfd_deassign(struct mshv_partition *pt,
if (IS_ERR(eventfd))
return PTR_ERR(eventfd);
spin_lock_irq(&pt->pt_irqfds_lock);
hlist_for_each_entry_safe(irqfd, n, &pt->pt_irqfds_list,
irqfd_hnode) {
if (irqfd->irqfd_eventfd_ctx == eventfd &&
irqfd->irqfd_irqnum == args->gsi)
mshv_irqfd_deactivate(irqfd);
}
spin_unlock_irq(&pt->pt_irqfds_lock);
eventfd_ctx_put(eventfd);
+1 -1
View File
@@ -51,7 +51,7 @@ int mshv_update_routing_table(struct mshv_partition *partition,
/*
* Allow only one to one mapping between GSI and MSI routing.
*/
if (girq->guest_irq_num != 0) {
if (girq->girq_entry_valid) {
r = -EINVAL;
goto out;
}
+5 -4
View File
@@ -40,12 +40,14 @@ mshv_port_table_fini(void)
int
mshv_portid_alloc(struct port_table_info *info)
{
int ret = 0;
int ret;
idr_preload(GFP_KERNEL);
idr_lock(&port_table_idr);
ret = idr_alloc(&port_table_idr, info, PORTID_MIN,
PORTID_MAX, GFP_KERNEL);
PORTID_MAX, GFP_NOWAIT);
idr_unlock(&port_table_idr);
idr_preload_end();
return ret;
}
@@ -60,8 +62,7 @@ mshv_portid_free(int port_id)
WARN_ON(!info);
idr_unlock(&port_table_idr);
synchronize_rcu();
kfree(info);
kfree_rcu(info, portbl_rcu);
}
int
+32 -9
View File
@@ -1072,6 +1072,8 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
struct mshv_vp *vp;
struct page *intercept_msg_page, *register_page, *ghcb_page;
struct hv_stats_page *stats_pages[2];
struct file *file;
int fd;
long ret;
if (copy_from_user(&args, arg, sizeof(args)))
@@ -1117,8 +1119,10 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
goto unmap_ghcb_page;
vp = kzalloc_obj(*vp);
if (!vp)
if (!vp) {
ret = -ENOMEM;
goto unmap_stats_pages;
}
vp->vp_partition = mshv_partition_get(partition);
if (!vp->vp_partition) {
@@ -1144,21 +1148,40 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
if (ret)
goto put_partition;
/*
* Keep anon_inode_getfd last: it installs fd in the file struct and
* thus makes the state accessible in user space.
*/
ret = anon_inode_getfd("mshv_vp", &mshv_vp_fops, vp,
O_RDWR | O_CLOEXEC);
if (ret < 0)
fd = get_unused_fd_flags(O_RDWR | O_CLOEXEC);
if (fd < 0) {
ret = fd;
goto remove_debugfs_vp;
}
file = anon_inode_getfile("mshv_vp", &mshv_vp_fops, vp,
O_RDWR | O_CLOEXEC);
if (IS_ERR(file)) {
ret = PTR_ERR(file);
goto put_unused_vp_fd;
}
/* already exclusive with the partition mutex for all ioctls */
partition->pt_vp_count++;
partition->pt_vp_array[args.vp_index] = vp;
/*
* Pairs with smp_load_acquire() in mshv_try_assert_irq_fast(), which
* can run concurrently from an irqfd waker without holding pt_mutex.
* The release ensures the VP's initialising stores are visible to any
* reader that observes a non-NULL pointer in pt_vp_array.
*/
smp_store_release(&partition->pt_vp_array[args.vp_index], vp);
/*
* fd_install() is the userspace-visibility commit point. Must be the
* last operation that can fail or be observed.
*/
fd_install(fd, file);
ret = fd;
goto out;
put_unused_vp_fd:
put_unused_fd(fd);
remove_debugfs_vp:
mshv_debugfs_vp_remove(vp);
put_partition:
+12 -1
View File
@@ -129,6 +129,7 @@ mshv_ioctl_create_vtl(void __user *user_arg, struct device *module_dev)
file = anon_inode_getfile("mshv_vtl", &mshv_vtl_fops,
vtl, O_RDWR);
if (IS_ERR(file)) {
put_unused_fd(fd);
kfree(vtl);
return PTR_ERR(file);
}
@@ -801,7 +802,7 @@ static vm_fault_t mshv_vtl_fault(struct vm_fault *vmf)
int cpu = vmf->pgoff & MSHV_PG_OFF_CPU_MASK;
int real_off = vmf->pgoff >> MSHV_REAL_OFF_SHIFT;
if (!cpu_online(cpu))
if (cpu >= nr_cpu_ids || !cpu_online(cpu))
return VM_FAULT_SIGBUS;
/*
* CPU Hotplug is not supported in VTL2 in OpenHCL, where this kernel driver exists.
@@ -1148,12 +1149,22 @@ static int mshv_vtl_hvcall_call(struct mshv_vtl_hvcall_fd *fd,
*/
in = (void *)__get_free_page(GFP_KERNEL);
out = (void *)__get_free_page(GFP_KERNEL);
if (!in || !out) {
ret = -ENOMEM;
goto free_pages;
}
if (copy_from_user(in, (void __user *)hvcall.input_ptr, hvcall.input_size)) {
ret = -EFAULT;
goto free_pages;
}
/*
* The caller supplies output_size, so clear the range copied back to
* userspace in case the hypercall writes fewer bytes than requested.
*/
memset(out, 0, hvcall.output_size);
hvcall.status = hv_do_hypercall(hvcall.control, in, out);
if (copy_to_user((void __user *)hvcall.output_ptr, out, hvcall.output_size)) {
+17 -11
View File
@@ -1369,8 +1369,6 @@ static void run_vmbus_irqd(unsigned int cpu)
__vmbus_isr();
}
static bool vmbus_irq_initialized;
static struct smp_hotplug_thread vmbus_irq_threads = {
.store = &vmbus_irqd,
.setup = vmbus_irqd_setup,
@@ -1384,8 +1382,19 @@ void vmbus_isr(void)
if (IS_ENABLED(CONFIG_PREEMPT_RT)) {
vmbus_irqd_wake();
} else {
lockdep_hardirq_threaded();
static DEFINE_WAIT_OVERRIDE_MAP(vmbus_map, LD_WAIT_CONFIG);
/*
* vmbus_isr is never force-threaded and always invoked at hard
* IRQ level. __vmbus_isr() below can acquire a spinlock_t
* which becomes a sleeping lock and must not be acquired in
* this context. Therefore on PREEMPT_RT this will be threaded
* via vmbus_irqd_wake(). On non-PREEMPT the annotation lets
* lockdep know that acquiring a spinlock_t is not an issue.
*/
lock_map_acquire_try(&vmbus_map);
__vmbus_isr();
lock_map_release(&vmbus_map);
}
}
EXPORT_SYMBOL_FOR_MODULES(vmbus_isr, "mshv_vtl");
@@ -1486,11 +1495,10 @@ static int vmbus_bus_init(void)
* the VMbus interrupt handler.
*/
if (IS_ENABLED(CONFIG_PREEMPT_RT) && !vmbus_irq_initialized) {
if (IS_ENABLED(CONFIG_PREEMPT_RT)) {
ret = smpboot_register_percpu_thread(&vmbus_irq_threads);
if (ret)
goto err_kthread;
vmbus_irq_initialized = true;
}
if (vmbus_irq == -1) {
@@ -1534,10 +1542,8 @@ err_connect:
else
free_percpu_irq(vmbus_irq, &vmbus_evt);
err_setup:
if (IS_ENABLED(CONFIG_PREEMPT_RT) && vmbus_irq_initialized) {
if (IS_ENABLED(CONFIG_PREEMPT_RT))
smpboot_unregister_percpu_thread(&vmbus_irq_threads);
vmbus_irq_initialized = false;
}
err_kthread:
bus_unregister(&hv_bus);
return ret;
@@ -2169,6 +2175,7 @@ int vmbus_device_register(struct hv_device *child_device_obj)
child_device_obj->device.dma_parms = &child_device_obj->dma_parms;
child_device_obj->device.dma_mask = &child_device_obj->dma_mask;
dma_set_mask(&child_device_obj->device, DMA_BIT_MASK(64));
dma_set_coherent_mask(&child_device_obj->device, DMA_BIT_MASK(64));
/*
* Register with the LDM. This will kick off the driver/device
@@ -3034,10 +3041,9 @@ static void __exit vmbus_exit(void)
hv_remove_vmbus_handler();
else
free_percpu_irq(vmbus_irq, &vmbus_evt);
if (IS_ENABLED(CONFIG_PREEMPT_RT) && vmbus_irq_initialized) {
if (IS_ENABLED(CONFIG_PREEMPT_RT))
smpboot_unregister_percpu_thread(&vmbus_irq_threads);
vmbus_irq_initialized = false;
}
for_each_online_cpu(cpu) {
struct hv_per_cpu_context *hv_cpu
= per_cpu_ptr(hv_context.cpu_context, cpu);
+2 -1
View File
@@ -184,8 +184,9 @@ enum hv_dynamic_processor_feature_property {
struct hv_input_get_system_property {
u32 property_id; /* enum hv_system_property */
u32 reserved;
union {
u32 as_uint32;
u64 as_uint64;
#if IS_ENABLED(CONFIG_X86)
/* enum hv_dynamic_processor_feature_property */
u32 hv_processor_feature;