mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge branch 'hyperv-fixes' of https://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux.git
This commit is contained in:
+28
-19
@@ -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.
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user