From: Wei Liu <wei.liu@kernel.org> Date: 2021-02-03 15:06:57
Hi all
Here we propose this patch series to make Linux run as the root partition [0]
on Microsoft Hypervisor [1]. There will be a subsequent patch series to provide a
device node (/dev/mshv) such that userspace programs can create and run virtual
machines. We've also ported Cloud Hypervisor [3] over and have been able to
boot a Linux guest with Virtio devices since late July 2020.
This series implements only the absolutely necessary components to get
things running. A large portion of this series consists of patches that
augment hyperv-tlfs.h. They should be rather uncontroversial and can be
applied right away.
A few key things other than the changes to hyperv-tlfs.h:
1. Linux needs to setup existing Hyper-V facilities differently.
2. Linux needs to make a few hypercalls to bring up APs.
3. Interrupts are remapped by IOMMU, which is controlled by the hypervisor.
Linux needs to make hypercalls to map and unmap interrupts. This is
done by introducing a new MSI irqdomain and extending the remapping
domain in hyperv-iommu.
This series is now based on 5.11-rc2.
Comments and suggestions are welcome.
Thanks,
Wei.
[0] Just think of it like Xen's Dom0.
[1] Hyper-V is more well-known, but it really refers to the whole stack
including the hypervisor and other components that run in Windows kernel
and userspace.
[3] https://github.com/cloud-hypervisor/
Cc: sameo@linux.intel.com
Cc: robert.bradford@intel.com
Cc: sebastien.boeuf@intel.com
Changes since v5:
1. Address Michael's comments.
2. Further improve and simplify code.
3. Drop a redundant patch and add one new patch for ACPI / NUMA code.
Changes since v4:
1. Rework IO-APIC handling.
Changes since v3:
1. Fix compilation errors.
2. Adapt to upstream changes.
Changes since v2:
1. Address more comments from Vitaly.
2. Fix and test 32bit build.
Changes since v1:
1. Simplify MSI IRQ domain implementation.
2. Address Vitaly's comments.
Wei Liu (16):
asm-generic/hyperv: change HV_CPU_POWER_MANAGEMENT to
HV_CPU_MANAGEMENT
x86/hyperv: detect if Linux is the root partition
Drivers: hv: vmbus: skip VMBus initialization if Linux is root
clocksource/hyperv: use MSR-based access if running as root
x86/hyperv: allocate output arg pages if required
x86/hyperv: extract partition ID from Microsoft Hypervisor if
necessary
x86/hyperv: handling hypercall page setup for root
ACPI / NUMA: add a stub function for node_to_pxm()
x86/hyperv: provide a bunch of helper functions
x86/hyperv: implement and use hv_smp_prepare_cpus
asm-generic/hyperv: update hv_msi_entry
asm-generic/hyperv: update hv_interrupt_entry
asm-generic/hyperv: introduce hv_device_id and auxiliary structures
asm-generic/hyperv: import data structures for mapping device
interrupts
x86/hyperv: implement an MSI domain for root partition
iommu/hyperv: setup an IO-APIC IRQ remapping domain for root partition
arch/x86/hyperv/Makefile | 4 +-
arch/x86/hyperv/hv_init.c | 107 +++++++-
arch/x86/hyperv/hv_proc.c | 219 ++++++++++++++++
arch/x86/hyperv/irqdomain.c | 387 ++++++++++++++++++++++++++++
arch/x86/include/asm/hyperv-tlfs.h | 23 ++
arch/x86/include/asm/mshyperv.h | 19 +-
arch/x86/kernel/cpu/mshyperv.c | 49 ++++
drivers/clocksource/hyperv_timer.c | 3 +
drivers/hv/vmbus_drv.c | 3 +
drivers/iommu/hyperv-iommu.c | 177 ++++++++++++-
drivers/pci/controller/pci-hyperv.c | 2 +-
include/acpi/acpi_numa.h | 4 +
include/asm-generic/hyperv-tlfs.h | 254 +++++++++++++++++-
13 files changed, 1230 insertions(+), 21 deletions(-)
create mode 100644 arch/x86/hyperv/hv_proc.c
create mode 100644 arch/x86/hyperv/irqdomain.c
base-commit: e71ba9452f0b5b2e8dc8aa5445198cd9214a6a62
--
2.20.1
@@ -331,6 +334,24 @@ static struct syscore_ops hv_syscore_ops = {.resume=hv_resume,};+staticvoid__inithv_get_partition_id(void)+{+structhv_get_partition_id*output_page;+u64status;+unsignedlongflags;++local_irq_save(flags);+output_page=*this_cpu_ptr(hyperv_pcpu_output_arg);+status=hv_do_hypercall(HVCALL_GET_PARTITION_ID,NULL,output_page);+if((status&HV_HYPERCALL_RESULT_MASK)!=HV_STATUS_SUCCESS){+/* No point in proceeding if this failed */+pr_err("Failed to get partition ID: %lld\n",status);+BUG();+}+hv_current_partition_id=output_page->partition_id;+local_irq_restore(flags);+}+/**Thisfunctionistobeinvokedearlyinthebootsequenceafterthe*hypervisorhasbeendetected.
@@ -331,6 +334,24 @@ static struct syscore_ops hv_syscore_ops = {.resume=hv_resume,};+staticvoid__inithv_get_partition_id(void)+{+structhv_get_partition_id*output_page;+u64status;+unsignedlongflags;++local_irq_save(flags);+output_page=*this_cpu_ptr(hyperv_pcpu_output_arg);+status=hv_do_hypercall(HVCALL_GET_PARTITION_ID,NULL,output_page);+if((status&HV_HYPERCALL_RESULT_MASK)!=HV_STATUS_SUCCESS){+/* No point in proceeding if this failed */+pr_err("Failed to get partition ID: %lld\n",status);+BUG();+}+hv_current_partition_id=output_page->partition_id;+local_irq_restore(flags);+}+/**Thisfunctionistobeinvokedearlyinthebootsequenceafterthe*hypervisorhasbeendetected.
From: Wei Liu <wei.liu@kernel.org> Date: 2021-02-03 15:08:34
When Linux is running as the root partition, the hypercall page will
have already been setup by Hyper-V. Copy the content over to the
allocated page.
Add checks to hv_suspend & co to bail early because they are not
supported in this setup yet.
Signed-off-by: Lillian Grassin-Drake <redacted>
Signed-off-by: Sunil Muthuswamy <redacted>
Signed-off-by: Nuno Das Neves <redacted>
Co-Developed-by: Lillian Grassin-Drake <redacted>
Co-Developed-by: Sunil Muthuswamy <redacted>
Co-Developed-by: Nuno Das Neves <redacted>
Signed-off-by: Wei Liu <wei.liu@kernel.org>
Reviewed-by: Michael Kelley <redacted>
---
v3:
1. Use HV_HYP_PAGE_SIZE.
2. Add checks to hv_suspend & co.
---
arch/x86/hyperv/hv_init.c | 37 ++++++++++++++++++++++++++++++++++---
1 file changed, 34 insertions(+), 3 deletions(-)
From: Wei Liu <wei.liu@kernel.org> Date: 2021-02-03 15:09:20
Just like MSI/MSI-X, IO-APIC interrupts are remapped by Microsoft
Hypervisor when Linux runs as the root partition. Implement an IRQ
domain to handle mapping and unmapping of IO-APIC interrupts.
Signed-off-by: Wei Liu <wei.liu@kernel.org>
---
v6:
1. Simplify code due to changes in a previous patch.
---
arch/x86/hyperv/irqdomain.c | 25 +++++
arch/x86/include/asm/mshyperv.h | 4 +
drivers/iommu/hyperv-iommu.c | 177 +++++++++++++++++++++++++++++++-
3 files changed, 203 insertions(+), 3 deletions(-)
@@ -115,30 +116,43 @@ static const struct irq_domain_ops hyperv_ir_domain_ops = {.free=hyperv_irq_remapping_free,};+staticconststructirq_domain_opshyperv_root_ir_domain_ops;staticint__inithyperv_prepare_irq_remapping(void){structfwnode_handle*fn;inti;+constchar*name;+conststructirq_domain_ops*ops;if(!hypervisor_is_type(X86_HYPER_MS_HYPERV)||x86_init.hyper.msi_ext_dest_id()||!x2apic_supported())return-ENODEV;-fn=irq_domain_alloc_named_id_fwnode("HYPERV-IR",0);+if(hv_root_partition){+name="HYPERV-ROOT-IR";+ops=&hyperv_root_ir_domain_ops;+}else{+name="HYPERV-IR";+ops=&hyperv_ir_domain_ops;+}++fn=irq_domain_alloc_named_id_fwnode(name,0);if(!fn)return-ENOMEM;ioapic_ir_domain=irq_domain_create_hierarchy(arch_get_ir_parent_domain(),-0,IOAPIC_REMAPPING_ENTRY,fn,-&hyperv_ir_domain_ops,NULL);+0,IOAPIC_REMAPPING_ENTRY,fn,ops,NULL);if(!ioapic_ir_domain){irq_domain_free_fwnode(fn);return-ENOMEM;}+if(hv_root_partition)+return0;/* The rest is only relevant to guests */+/**Hyper-Vdoesn'tprovideirqremappingfunctionfor*IO-APICandsoIO-APIConlyaccepts8-bitAPICID.
@@ -166,4 +180,161 @@ struct irq_remap_ops hyperv_irq_remap_ops = {.enable=hyperv_enable_irq_remapping,};+/* IRQ remapping domain when Linux runs as the root partition */+structhyperv_root_ir_data{+u8ioapic_id;+boolis_level;+structhv_interrupt_entryentry;+};++staticvoid+hyperv_root_ir_compose_msi_msg(structirq_data*irq_data,structmsi_msg*msg)+{+u64status;+u32vector;+structirq_cfg*cfg;+intioapic_id;+structcpumask*affinity;+intcpu;+structhv_interrupt_entryentry;+structhyperv_root_ir_data*data=irq_data->chip_data;+structIO_APIC_route_entrye;++cfg=irqd_cfg(irq_data);+affinity=irq_data_get_effective_affinity_mask(irq_data);+cpu=cpumask_first_and(affinity,cpu_online_mask);++vector=cfg->vector;+ioapic_id=data->ioapic_id;++if(data->entry.source==HV_DEVICE_TYPE_IOAPIC+&&data->entry.ioapic_rte.as_uint64){+entry=data->entry;++status=hv_unmap_ioapic_interrupt(ioapic_id,&entry);++if(status!=HV_STATUS_SUCCESS)+pr_debug("%s: unexpected unmap status %lld\n",__func__,status);++data->entry.ioapic_rte.as_uint64=0;+data->entry.source=0;/* Invalid source */+}+++status=hv_map_ioapic_interrupt(ioapic_id,data->is_level,cpu,+vector,&entry);++if(status!=HV_STATUS_SUCCESS){+pr_err("%s: map hypercall failed, status %lld\n",__func__,status);+return;+}++data->entry=entry;++/* Turn it into an IO_APIC_route_entry, and generate MSI MSG. */+e.w1=entry.ioapic_rte.low_uint32;+e.w2=entry.ioapic_rte.high_uint32;++memset(msg,0,sizeof(*msg));+msg->arch_data.vector=e.vector;+msg->arch_data.delivery_mode=e.delivery_mode;+msg->arch_addr_lo.dest_mode_logical=e.dest_mode_logical;+msg->arch_addr_lo.dmar_format=e.ir_format;+msg->arch_addr_lo.dmar_index_0_14=e.ir_index_0_14;+}++staticinthyperv_root_ir_set_affinity(structirq_data*data,+conststructcpumask*mask,boolforce)+{+structirq_data*parent=data->parent_data;+structirq_cfg*cfg=irqd_cfg(data);+intret;++ret=parent->chip->irq_set_affinity(parent,mask,force);+if(ret<0||ret==IRQ_SET_MASK_OK_DONE)+returnret;++send_cleanup_vector(cfg);++return0;+}++staticstructirq_chiphyperv_root_ir_chip={+.name="HYPERV-ROOT-IR",+.irq_ack=apic_ack_irq,+.irq_set_affinity=hyperv_root_ir_set_affinity,+.irq_compose_msi_msg=hyperv_root_ir_compose_msi_msg,+};++staticinthyperv_root_irq_remapping_alloc(structirq_domain*domain,+unsignedintvirq,unsignedintnr_irqs,+void*arg)+{+structirq_alloc_info*info=arg;+structirq_data*irq_data;+structhyperv_root_ir_data*data;+intret=0;++if(!info||info->type!=X86_IRQ_ALLOC_TYPE_IOAPIC||nr_irqs>1)+return-EINVAL;++ret=irq_domain_alloc_irqs_parent(domain,virq,nr_irqs,arg);+if(ret<0)+returnret;++data=kzalloc(sizeof(*data),GFP_KERNEL);+if(!data){+irq_domain_free_irqs_common(domain,virq,nr_irqs);+return-ENOMEM;+}++irq_data=irq_domain_get_irq_data(domain,virq);+if(!irq_data){+kfree(data);+irq_domain_free_irqs_common(domain,virq,nr_irqs);+return-EINVAL;+}++data->ioapic_id=info->devid;+data->is_level=info->ioapic.is_level;++irq_data->chip=&hyperv_root_ir_chip;+irq_data->chip_data=data;++return0;+}++staticvoidhyperv_root_irq_remapping_free(structirq_domain*domain,+unsignedintvirq,unsignedintnr_irqs)+{+structirq_data*irq_data;+structhyperv_root_ir_data*data;+structhv_interrupt_entry*e;+inti;++for(i=0;i<nr_irqs;i++){+irq_data=irq_domain_get_irq_data(domain,virq+i);++if(irq_data&&irq_data->chip_data){+data=irq_data->chip_data;+e=&data->entry;++if(e->source==HV_DEVICE_TYPE_IOAPIC+&&e->ioapic_rte.as_uint64)+hv_unmap_ioapic_interrupt(data->ioapic_id,+&data->entry);++kfree(data);+}+}++irq_domain_free_irqs_common(domain,virq,nr_irqs);+}++staticconststructirq_domain_opshyperv_root_ir_domain_ops={+.select=hyperv_irq_remapping_select,+.alloc=hyperv_root_irq_remapping_alloc,+.free=hyperv_root_irq_remapping_free,+};+#endif
On Wed, Feb 03, 2021 at 03:04:35PM +0000, Wei Liu wrote:
Just like MSI/MSI-X, IO-APIC interrupts are remapped by Microsoft
Hypervisor when Linux runs as the root partition. Implement an IRQ
domain to handle mapping and unmapping of IO-APIC interrupts.
Signed-off-by: Wei Liu <wei.liu@kernel.org>
Acked-by: Joerg Roedel <joro@8bytes.org>
quoted hunk
---
v6:
1. Simplify code due to changes in a previous patch.
---
arch/x86/hyperv/irqdomain.c | 25 +++++
arch/x86/include/asm/mshyperv.h | 4 +
drivers/iommu/hyperv-iommu.c | 177 +++++++++++++++++++++++++++++++-
3 files changed, 203 insertions(+), 3 deletions(-)
@@ -115,30 +116,43 @@ static const struct irq_domain_ops hyperv_ir_domain_ops = {.free=hyperv_irq_remapping_free,};+staticconststructirq_domain_opshyperv_root_ir_domain_ops;staticint__inithyperv_prepare_irq_remapping(void){structfwnode_handle*fn;inti;+constchar*name;+conststructirq_domain_ops*ops;if(!hypervisor_is_type(X86_HYPER_MS_HYPERV)||x86_init.hyper.msi_ext_dest_id()||!x2apic_supported())return-ENODEV;-fn=irq_domain_alloc_named_id_fwnode("HYPERV-IR",0);+if(hv_root_partition){+name="HYPERV-ROOT-IR";+ops=&hyperv_root_ir_domain_ops;+}else{+name="HYPERV-IR";+ops=&hyperv_ir_domain_ops;+}++fn=irq_domain_alloc_named_id_fwnode(name,0);if(!fn)return-ENOMEM;ioapic_ir_domain=irq_domain_create_hierarchy(arch_get_ir_parent_domain(),-0,IOAPIC_REMAPPING_ENTRY,fn,-&hyperv_ir_domain_ops,NULL);+0,IOAPIC_REMAPPING_ENTRY,fn,ops,NULL);if(!ioapic_ir_domain){irq_domain_free_fwnode(fn);return-ENOMEM;}+if(hv_root_partition)+return0;/* The rest is only relevant to guests */+/**Hyper-Vdoesn'tprovideirqremappingfunctionfor*IO-APICandsoIO-APIConlyaccepts8-bitAPICID.
@@ -166,4 +180,161 @@ struct irq_remap_ops hyperv_irq_remap_ops = {.enable=hyperv_enable_irq_remapping,};+/* IRQ remapping domain when Linux runs as the root partition */+structhyperv_root_ir_data{+u8ioapic_id;+boolis_level;+structhv_interrupt_entryentry;+};++staticvoid+hyperv_root_ir_compose_msi_msg(structirq_data*irq_data,structmsi_msg*msg)+{+u64status;+u32vector;+structirq_cfg*cfg;+intioapic_id;+structcpumask*affinity;+intcpu;+structhv_interrupt_entryentry;+structhyperv_root_ir_data*data=irq_data->chip_data;+structIO_APIC_route_entrye;++cfg=irqd_cfg(irq_data);+affinity=irq_data_get_effective_affinity_mask(irq_data);+cpu=cpumask_first_and(affinity,cpu_online_mask);++vector=cfg->vector;+ioapic_id=data->ioapic_id;++if(data->entry.source==HV_DEVICE_TYPE_IOAPIC+&&data->entry.ioapic_rte.as_uint64){+entry=data->entry;++status=hv_unmap_ioapic_interrupt(ioapic_id,&entry);++if(status!=HV_STATUS_SUCCESS)+pr_debug("%s: unexpected unmap status %lld\n",__func__,status);++data->entry.ioapic_rte.as_uint64=0;+data->entry.source=0;/* Invalid source */+}+++status=hv_map_ioapic_interrupt(ioapic_id,data->is_level,cpu,+vector,&entry);++if(status!=HV_STATUS_SUCCESS){+pr_err("%s: map hypercall failed, status %lld\n",__func__,status);+return;+}++data->entry=entry;++/* Turn it into an IO_APIC_route_entry, and generate MSI MSG. */+e.w1=entry.ioapic_rte.low_uint32;+e.w2=entry.ioapic_rte.high_uint32;++memset(msg,0,sizeof(*msg));+msg->arch_data.vector=e.vector;+msg->arch_data.delivery_mode=e.delivery_mode;+msg->arch_addr_lo.dest_mode_logical=e.dest_mode_logical;+msg->arch_addr_lo.dmar_format=e.ir_format;+msg->arch_addr_lo.dmar_index_0_14=e.ir_index_0_14;+}++staticinthyperv_root_ir_set_affinity(structirq_data*data,+conststructcpumask*mask,boolforce)+{+structirq_data*parent=data->parent_data;+structirq_cfg*cfg=irqd_cfg(data);+intret;++ret=parent->chip->irq_set_affinity(parent,mask,force);+if(ret<0||ret==IRQ_SET_MASK_OK_DONE)+returnret;++send_cleanup_vector(cfg);++return0;+}++staticstructirq_chiphyperv_root_ir_chip={+.name="HYPERV-ROOT-IR",+.irq_ack=apic_ack_irq,+.irq_set_affinity=hyperv_root_ir_set_affinity,+.irq_compose_msi_msg=hyperv_root_ir_compose_msi_msg,+};++staticinthyperv_root_irq_remapping_alloc(structirq_domain*domain,+unsignedintvirq,unsignedintnr_irqs,+void*arg)+{+structirq_alloc_info*info=arg;+structirq_data*irq_data;+structhyperv_root_ir_data*data;+intret=0;++if(!info||info->type!=X86_IRQ_ALLOC_TYPE_IOAPIC||nr_irqs>1)+return-EINVAL;++ret=irq_domain_alloc_irqs_parent(domain,virq,nr_irqs,arg);+if(ret<0)+returnret;++data=kzalloc(sizeof(*data),GFP_KERNEL);+if(!data){+irq_domain_free_irqs_common(domain,virq,nr_irqs);+return-ENOMEM;+}++irq_data=irq_domain_get_irq_data(domain,virq);+if(!irq_data){+kfree(data);+irq_domain_free_irqs_common(domain,virq,nr_irqs);+return-EINVAL;+}++data->ioapic_id=info->devid;+data->is_level=info->ioapic.is_level;++irq_data->chip=&hyperv_root_ir_chip;+irq_data->chip_data=data;++return0;+}++staticvoidhyperv_root_irq_remapping_free(structirq_domain*domain,+unsignedintvirq,unsignedintnr_irqs)+{+structirq_data*irq_data;+structhyperv_root_ir_data*data;+structhv_interrupt_entry*e;+inti;++for(i=0;i<nr_irqs;i++){+irq_data=irq_domain_get_irq_data(domain,virq+i);++if(irq_data&&irq_data->chip_data){+data=irq_data->chip_data;+e=&data->entry;++if(e->source==HV_DEVICE_TYPE_IOAPIC+&&e->ioapic_rte.as_uint64)+hv_unmap_ioapic_interrupt(data->ioapic_id,+&data->entry);++kfree(data);+}+}++irq_domain_free_irqs_common(domain,virq,nr_irqs);+}++staticconststructirq_domain_opshyperv_root_ir_domain_ops={+.select=hyperv_irq_remapping_select,+.alloc=hyperv_root_irq_remapping_alloc,+.free=hyperv_root_irq_remapping_free,+};+#endif
From: Michael Kelley <hidden> Date: 2021-02-04 17:55:30
From: Wei Liu <wei.liu@kernel.org> Sent: Wednesday, February 3, 2021 7:05 AM
Just like MSI/MSI-X, IO-APIC interrupts are remapped by Microsoft
Hypervisor when Linux runs as the root partition. Implement an IRQ
domain to handle mapping and unmapping of IO-APIC interrupts.
Signed-off-by: Wei Liu <wei.liu@kernel.org>
---
v6:
1. Simplify code due to changes in a previous patch.
---
arch/x86/hyperv/irqdomain.c | 25 +++++
arch/x86/include/asm/mshyperv.h | 4 +
drivers/iommu/hyperv-iommu.c | 177 +++++++++++++++++++++++++++++++-
3 files changed, 203 insertions(+), 3 deletions(-)
From: Wei Liu <wei.liu@kernel.org> Date: 2021-02-03 15:09:26
When Linux runs as the root partition on Microsoft Hypervisor, its
interrupts are remapped. Linux will need to explicitly map and unmap
interrupts for hardware.
Implement an MSI domain to issue the correct hypercalls. And initialize
this irqdomain as the default MSI irq domain.
Signed-off-by: Sunil Muthuswamy <redacted>
Co-Developed-by: Sunil Muthuswamy <redacted>
Signed-off-by: Wei Liu <wei.liu@kernel.org>
---
v6:
1. Use u64 status.
2. Use vpset instead of bitmap.
3. Factor out hv_map_interrupt
4. Address other misc comments.
v4: Fix compilation issue when CONFIG_PCI_MSI is not set.
v3: build irqdomain.o for 32bit as well.
v2: This patch is simplified due to upstream changes.
---
arch/x86/hyperv/Makefile | 2 +-
arch/x86/hyperv/hv_init.c | 9 +
arch/x86/hyperv/irqdomain.c | 362 ++++++++++++++++++++++++++++++++
arch/x86/include/asm/mshyperv.h | 2 +
4 files changed, 374 insertions(+), 1 deletion(-)
create mode 100644 arch/x86/hyperv/irqdomain.c
@@ -0,0 +1,362 @@+// SPDX-License-Identifier: GPL-2.0++/*+*forLinuxtorunastherootpartitiononMicrosoftHypervisor.+*+*Authors:+*SunilMuthuswamy<sunilmut@microsoft.com>+*WeiLiu<wei.liu@kernel.org>+*/++#include<linux/pci.h>+#include<linux/irq.h>+#include<asm/mshyperv.h>++staticinthv_map_interrupt(unionhv_device_iddevice_id,boollevel,+intcpu,intvector,structhv_interrupt_entry*entry)+{+structhv_input_map_device_interrupt*input;+structhv_output_map_device_interrupt*output;+structhv_device_interrupt_descriptor*intr_desc;+unsignedlongflags;+u64status;+cpumask_tmask=CPU_MASK_NONE;+intnr_bank,var_size;++local_irq_save(flags);++input=*this_cpu_ptr(hyperv_pcpu_input_arg);+output=*this_cpu_ptr(hyperv_pcpu_output_arg);++intr_desc=&input->interrupt_descriptor;+memset(input,0,sizeof(*input));+input->partition_id=hv_current_partition_id;+input->device_id=device_id.as_uint64;+intr_desc->interrupt_type=HV_X64_INTERRUPT_TYPE_FIXED;+intr_desc->vector_count=1;+intr_desc->target.vector=vector;++if(level)+intr_desc->trigger_mode=HV_INTERRUPT_TRIGGER_MODE_LEVEL;+else+intr_desc->trigger_mode=HV_INTERRUPT_TRIGGER_MODE_EDGE;++cpumask_set_cpu(cpu,&mask);+intr_desc->target.vp_set.valid_bank_mask=0;+intr_desc->target.vp_set.format=HV_GENERIC_SET_SPARSE_4K;+nr_bank=cpumask_to_vpset(&(intr_desc->target.vp_set),&mask);+if(nr_bank<0){+local_irq_restore(flags);+pr_err("%s: unable to generate VP set\n",__func__);+returnEINVAL;+}+intr_desc->target.flags=HV_DEVICE_INTERRUPT_TARGET_PROCESSOR_SET;++/*+*var-sizedhypercall,var-sizestartsaftervp_mask(thus+*vp_set.formatdoesnotcount,butvp_set.valid_bank_mask+*does).+*/+var_size=nr_bank+1;++status=hv_do_rep_hypercall(HVCALL_MAP_DEVICE_INTERRUPT,0,var_size,+input,output);+*entry=output->interrupt_entry;++local_irq_restore(flags);++if((status&HV_HYPERCALL_RESULT_MASK)!=HV_STATUS_SUCCESS)+pr_err("%s: hypercall failed, status %lld\n",__func__,status);++returnstatus&HV_HYPERCALL_RESULT_MASK;+}++staticinthv_unmap_interrupt(u64id,structhv_interrupt_entry*old_entry)+{+unsignedlongflags;+structhv_input_unmap_device_interrupt*input;+structhv_interrupt_entry*intr_entry;+u64status;++local_irq_save(flags);+input=*this_cpu_ptr(hyperv_pcpu_input_arg);++memset(input,0,sizeof(*input));+intr_entry=&input->interrupt_entry;+input->partition_id=hv_current_partition_id;+input->device_id=id;+*intr_entry=*old_entry;++status=hv_do_hypercall(HVCALL_UNMAP_DEVICE_INTERRUPT,input,NULL);+local_irq_restore(flags);++returnstatus&HV_HYPERCALL_RESULT_MASK;+}++#ifdef CONFIG_PCI_MSI+structrid_data{+structpci_dev*bridge;+u32rid;+};++staticintget_rid_cb(structpci_dev*pdev,u16alias,void*data)+{+structrid_data*rd=data;+u8bus=PCI_BUS_NUM(rd->rid);++if(pdev->bus->number!=bus||PCI_BUS_NUM(alias)!=bus){+rd->bridge=pdev;+rd->rid=alias;+}++return0;+}++staticunionhv_device_idhv_build_pci_dev_id(structpci_dev*dev)+{+unionhv_device_iddev_id;+structrid_datadata={+.bridge=NULL,+.rid=PCI_DEVID(dev->bus->number,dev->devfn)+};++pci_for_each_dma_alias(dev,get_rid_cb,&data);++dev_id.as_uint64=0;+dev_id.device_type=HV_DEVICE_TYPE_PCI;+dev_id.pci.segment=pci_domain_nr(dev->bus);++dev_id.pci.bdf.bus=PCI_BUS_NUM(data.rid);+dev_id.pci.bdf.device=PCI_SLOT(data.rid);+dev_id.pci.bdf.function=PCI_FUNC(data.rid);+dev_id.pci.source_shadow=HV_SOURCE_SHADOW_NONE;++if(data.bridge){+intpos;++/*+*MicrosoftHypervisorrequiresabusrangewhenthebridgeis+*runninginPCI-Xmode.+*+*TodistinguishconventionalvsPCI-Xbridge,wecancheck+*thebridge'sPCI-XSecondaryStatusRegister,SecondaryBus+*ModeandFrequencybits.SeePCIExpresstoPCI/PCI-XBridge+*SpecificationRevision1.05.2.2.1.3.+*+*Valuezeromeansitisinconventionalmode,otherwiseitis+*inPCI-Xmode.+*/++pos=pci_find_capability(data.bridge,PCI_CAP_ID_PCIX);+if(pos){+u16status;++pci_read_config_word(data.bridge,pos++PCI_X_BRIDGE_SSTATUS,&status);++if(status&PCI_X_SSTATUS_FREQ){+/* Non-zero, PCI-X mode */+u8sec_bus,sub_bus;++dev_id.pci.source_shadow=HV_SOURCE_SHADOW_BRIDGE_BUS_RANGE;++pci_read_config_byte(data.bridge,PCI_SECONDARY_BUS,&sec_bus);+dev_id.pci.shadow_bus_range.secondary_bus=sec_bus;+pci_read_config_byte(data.bridge,PCI_SUBORDINATE_BUS,&sub_bus);+dev_id.pci.shadow_bus_range.subordinate_bus=sub_bus;+}+}+}++returndev_id;+}++staticinthv_map_msi_interrupt(structpci_dev*dev,intcpu,intvector,+structhv_interrupt_entry*entry)+{+unionhv_device_iddevice_id=hv_build_pci_dev_id(dev);++returnhv_map_interrupt(device_id,false,cpu,vector,entry);+}++staticinlinevoidentry_to_msi_msg(structhv_interrupt_entry*entry,structmsi_msg*msg)+{+/* High address is always 0 */+msg->address_hi=0;+msg->address_lo=entry->msi_entry.address.as_uint32;+msg->data=entry->msi_entry.data.as_uint32;+}++staticinthv_unmap_msi_interrupt(structpci_dev*dev,structhv_interrupt_entry*old_entry);+staticvoidhv_irq_compose_msi_msg(structirq_data*data,structmsi_msg*msg)+{+structmsi_desc*msidesc;+structpci_dev*dev;+structhv_interrupt_entryout_entry,*stored_entry;+structirq_cfg*cfg=irqd_cfg(data);+cpumask_t*affinity;+intcpu;+u64status;++msidesc=irq_data_get_msi_desc(data);+dev=msi_desc_to_pci_dev(msidesc);++if(!cfg){+pr_debug("%s: cfg is NULL",__func__);+return;+}++affinity=irq_data_get_effective_affinity_mask(data);+cpu=cpumask_first_and(affinity,cpu_online_mask);++if(data->chip_data){+/*+*Thisinterruptisalreadymapped.Let'sunmapfirst.+*+*Wedon'tuseretargetinterrupthypercallsherebecause+*MicrosoftHypervisordoens'tallowroottochangethevector+*orspecifyVPsoutsideofthesetthatisinitiallyused+*duringmapping.+*/+stored_entry=data->chip_data;+data->chip_data=NULL;++status=hv_unmap_msi_interrupt(dev,stored_entry);++kfree(stored_entry);++if(status!=HV_STATUS_SUCCESS){+pr_debug("%s: failed to unmap, status %lld",__func__,status);+return;+}+}++stored_entry=kzalloc(sizeof(*stored_entry),GFP_ATOMIC);+if(!stored_entry){+pr_debug("%s: failed to allocate chip data\n",__func__);+return;+}++status=hv_map_msi_interrupt(dev,cpu,cfg->vector,&out_entry);+if(status!=HV_STATUS_SUCCESS){+kfree(stored_entry);+return;+}++*stored_entry=out_entry;+data->chip_data=stored_entry;+entry_to_msi_msg(&out_entry,msg);++return;+}++staticinthv_unmap_msi_interrupt(structpci_dev*dev,structhv_interrupt_entry*old_entry)+{+returnhv_unmap_interrupt(hv_build_pci_dev_id(dev).as_uint64,old_entry);+}++staticvoidhv_teardown_msi_irq_common(structpci_dev*dev,structmsi_desc*msidesc,intirq)+{+u64status;+structhv_interrupt_entryold_entry;+structirq_desc*desc;+structirq_data*data;+structmsi_msgmsg;++desc=irq_to_desc(irq);+if(!desc){+pr_debug("%s: no irq desc\n",__func__);+return;+}++data=&desc->irq_data;+if(!data){+pr_debug("%s: no irq data\n",__func__);+return;+}++if(!data->chip_data){+pr_debug("%s: no chip data\n!",__func__);+return;+}++old_entry=*(structhv_interrupt_entry*)data->chip_data;+entry_to_msi_msg(&old_entry,&msg);++kfree(data->chip_data);+data->chip_data=NULL;++status=hv_unmap_msi_interrupt(dev,&old_entry);++if(status!=HV_STATUS_SUCCESS){+pr_err("%s: hypercall failed, status %lld\n",__func__,status);+return;+}+}++staticvoidhv_msi_domain_free_irqs(structirq_domain*domain,structdevice*dev)+{+inti;+structmsi_desc*entry;+structpci_dev*pdev;++if(WARN_ON_ONCE(!dev_is_pci(dev)))+return;++pdev=to_pci_dev(dev);++for_each_pci_msi_entry(entry,pdev){+if(entry->irq){+for(i=0;i<entry->nvec_used;i++){+hv_teardown_msi_irq_common(pdev,entry,entry->irq+i);+irq_domain_free_irqs(entry->irq+i,1);+}+}+}+}++/*+*IRQChipforMSIPCI/PCI-X/PCI-ExpressDevices,+*whichimplementtheMSIorMSI-XCapabilityStructure.+*/+staticstructirq_chiphv_pci_msi_controller={+.name="HV-PCI-MSI",+.irq_unmask=pci_msi_unmask_irq,+.irq_mask=pci_msi_mask_irq,+.irq_ack=irq_chip_ack_parent,+.irq_retrigger=irq_chip_retrigger_hierarchy,+.irq_compose_msi_msg=hv_irq_compose_msi_msg,+.irq_set_affinity=msi_domain_set_affinity,+.flags=IRQCHIP_SKIP_SET_WAKE,+};++staticstructmsi_domain_opspci_msi_domain_ops={+.domain_free_irqs=hv_msi_domain_free_irqs,+.msi_prepare=pci_msi_prepare,+};++staticstructmsi_domain_infohv_pci_msi_domain_info={+.flags=MSI_FLAG_USE_DEF_DOM_OPS|MSI_FLAG_USE_DEF_CHIP_OPS|+MSI_FLAG_PCI_MSIX,+.ops=&pci_msi_domain_ops,+.chip=&hv_pci_msi_controller,+.handler=handle_edge_irq,+.handler_name="edge",+};++structirq_domain*__inithv_create_pci_msi_domain(void)+{+structirq_domain*d=NULL;+structfwnode_handle*fn;++fn=irq_domain_alloc_named_fwnode("HV-PCI-MSI");+if(fn)+d=pci_msi_create_irq_domain(fn,&hv_pci_msi_domain_info,x86_vector_domain);++/* No point in going further if we can't get an irq domain */+BUG_ON(!d);++returnd;+}++#endif /* CONFIG_PCI_MSI */
From: Michael Kelley <hidden> Date: 2021-02-04 17:44:47
From: Wei Liu <wei.liu@kernel.org> Sent: Wednesday, February 3, 2021 7:05 AM
quoted hunk
When Linux runs as the root partition on Microsoft Hypervisor, its
interrupts are remapped. Linux will need to explicitly map and unmap
interrupts for hardware.
Implement an MSI domain to issue the correct hypercalls. And initialize
this irqdomain as the default MSI irq domain.
Signed-off-by: Sunil Muthuswamy <redacted>
Co-Developed-by: Sunil Muthuswamy <redacted>
Signed-off-by: Wei Liu <wei.liu@kernel.org>
---
v6:
1. Use u64 status.
2. Use vpset instead of bitmap.
3. Factor out hv_map_interrupt
4. Address other misc comments.
v4: Fix compilation issue when CONFIG_PCI_MSI is not set.
v3: build irqdomain.o for 32bit as well.
v2: This patch is simplified due to upstream changes.
---
arch/x86/hyperv/Makefile | 2 +-
arch/x86/hyperv/hv_init.c | 9 +
arch/x86/hyperv/irqdomain.c | 362 ++++++++++++++++++++++++++++++++
arch/x86/include/asm/mshyperv.h | 2 +
4 files changed, 374 insertions(+), 1 deletion(-)
create mode 100644 arch/x86/hyperv/irqdomain.c
There's a function get_cpu_mask() that returns a pointer to a cpumask with only
the specified cpu set in the mask. It returns a const pointer to the correct entry
in a pre-allocated array of all such cpumasks, so it's a lot more efficient than
allocating and initializing a local cpumask instance on the stack.
quoted hunk
+ if (nr_bank < 0) {
+ local_irq_restore(flags);
+ pr_err("%s: unable to generate VP set\n", __func__);
+ return EINVAL;
+ }
+ intr_desc->target.flags = HV_DEVICE_INTERRUPT_TARGET_PROCESSOR_SET;
+
+ /*
+ * var-sized hypercall, var-size starts after vp_mask (thus
+ * vp_set.format does not count, but vp_set.valid_bank_mask
+ * does).
+ */
+ var_size = nr_bank + 1;
+
+ status = hv_do_rep_hypercall(HVCALL_MAP_DEVICE_INTERRUPT, 0, var_size,
+ input, output);
+ *entry = output->interrupt_entry;
+
+ local_irq_restore(flags);
+
+ if ((status & HV_HYPERCALL_RESULT_MASK) != HV_STATUS_SUCCESS)
+ pr_err("%s: hypercall failed, status %lld\n", __func__, status);
+
+ return status & HV_HYPERCALL_RESULT_MASK;
+}
+
+static int hv_unmap_interrupt(u64 id, struct hv_interrupt_entry *old_entry)
+{
+ unsigned long flags;
+ struct hv_input_unmap_device_interrupt *input;
+ struct hv_interrupt_entry *intr_entry;
+ u64 status;
+
+ local_irq_save(flags);
+ input = *this_cpu_ptr(hyperv_pcpu_input_arg);
+
+ memset(input, 0, sizeof(*input));
+ intr_entry = &input->interrupt_entry;
+ input->partition_id = hv_current_partition_id;
+ input->device_id = id;
+ *intr_entry = *old_entry;
+
+ status = hv_do_hypercall(HVCALL_UNMAP_DEVICE_INTERRUPT, input, NULL);
+ local_irq_restore(flags);
+
+ return status & HV_HYPERCALL_RESULT_MASK;
+}
+
+#ifdef CONFIG_PCI_MSI
+struct rid_data {
+ struct pci_dev *bridge;
+ u32 rid;
+};
+
+static int get_rid_cb(struct pci_dev *pdev, u16 alias, void *data)
+{
+ struct rid_data *rd = data;
+ u8 bus = PCI_BUS_NUM(rd->rid);
+
+ if (pdev->bus->number != bus || PCI_BUS_NUM(alias) != bus) {
+ rd->bridge = pdev;
+ rd->rid = alias;
+ }
+
+ return 0;
+}
+
+static union hv_device_id hv_build_pci_dev_id(struct pci_dev *dev)
+{
+ union hv_device_id dev_id;
+ struct rid_data data = {
+ .bridge = NULL,
+ .rid = PCI_DEVID(dev->bus->number, dev->devfn)
+ };
+
+ pci_for_each_dma_alias(dev, get_rid_cb, &data);
+
+ dev_id.as_uint64 = 0;
+ dev_id.device_type = HV_DEVICE_TYPE_PCI;
+ dev_id.pci.segment = pci_domain_nr(dev->bus);
+
+ dev_id.pci.bdf.bus = PCI_BUS_NUM(data.rid);
+ dev_id.pci.bdf.device = PCI_SLOT(data.rid);
+ dev_id.pci.bdf.function = PCI_FUNC(data.rid);
+ dev_id.pci.source_shadow = HV_SOURCE_SHADOW_NONE;
+
+ if (data.bridge) {
+ int pos;
+
+ /*
+ * Microsoft Hypervisor requires a bus range when the bridge is
+ * running in PCI-X mode.
+ *
+ * To distinguish conventional vs PCI-X bridge, we can check
+ * the bridge's PCI-X Secondary Status Register, Secondary Bus
+ * Mode and Frequency bits. See PCI Express to PCI/PCI-X Bridge
+ * Specification Revision 1.0 5.2.2.1.3.
+ *
+ * Value zero means it is in conventional mode, otherwise it is
+ * in PCI-X mode.
+ */
+
+ pos = pci_find_capability(data.bridge, PCI_CAP_ID_PCIX);
+ if (pos) {
+ u16 status;
+
+ pci_read_config_word(data.bridge, pos +
+ PCI_X_BRIDGE_SSTATUS, &status);
+
+ if (status & PCI_X_SSTATUS_FREQ) {
+ /* Non-zero, PCI-X mode */
+ u8 sec_bus, sub_bus;
+
+ dev_id.pci.source_shadow = HV_SOURCE_SHADOW_BRIDGE_BUS_RANGE;
+
+ pci_read_config_byte(data.bridge, PCI_SECONDARY_BUS, &sec_bus);
+ dev_id.pci.shadow_bus_range.secondary_bus = sec_bus;
+ pci_read_config_byte(data.bridge, PCI_SUBORDINATE_BUS, &sub_bus);
+ dev_id.pci.shadow_bus_range.subordinate_bus = sub_bus;
+ }
+ }
+ }
+
+ return dev_id;
+}
+
+static int hv_map_msi_interrupt(struct pci_dev *dev, int cpu, int vector,
+ struct hv_interrupt_entry *entry)
+{
+ union hv_device_id device_id = hv_build_pci_dev_id(dev);
+
+ return hv_map_interrupt(device_id, false, cpu, vector, entry);
+}
+
+static inline void entry_to_msi_msg(struct hv_interrupt_entry *entry, struct msi_msg *msg)
+{
+ /* High address is always 0 */
+ msg->address_hi = 0;
+ msg->address_lo = entry->msi_entry.address.as_uint32;
+ msg->data = entry->msi_entry.data.as_uint32;
+}
+
+static int hv_unmap_msi_interrupt(struct pci_dev *dev, struct hv_interrupt_entry *old_entry);
+static void hv_irq_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+ struct msi_desc *msidesc;
+ struct pci_dev *dev;
+ struct hv_interrupt_entry out_entry, *stored_entry;
+ struct irq_cfg *cfg = irqd_cfg(data);
+ cpumask_t *affinity;
+ int cpu;
+ u64 status;
+
+ msidesc = irq_data_get_msi_desc(data);
+ dev = msi_desc_to_pci_dev(msidesc);
+
+ if (!cfg) {
+ pr_debug("%s: cfg is NULL", __func__);
+ return;
+ }
+
+ affinity = irq_data_get_effective_affinity_mask(data);
+ cpu = cpumask_first_and(affinity, cpu_online_mask);
+
+ if (data->chip_data) {
+ /*
+ * This interrupt is already mapped. Let's unmap first.
+ *
+ * We don't use retarget interrupt hypercalls here because
+ * Microsoft Hypervisor doens't allow root to change the vector
+ * or specify VPs outside of the set that is initially used
+ * during mapping.
+ */
+ stored_entry = data->chip_data;
+ data->chip_data = NULL;
+
+ status = hv_unmap_msi_interrupt(dev, stored_entry);
+
+ kfree(stored_entry);
+
+ if (status != HV_STATUS_SUCCESS) {
+ pr_debug("%s: failed to unmap, status %lld", __func__, status);
+ return;
+ }
+ }
+
+ stored_entry = kzalloc(sizeof(*stored_entry), GFP_ATOMIC);
+ if (!stored_entry) {
+ pr_debug("%s: failed to allocate chip data\n", __func__);
+ return;
+ }
+
+ status = hv_map_msi_interrupt(dev, cpu, cfg->vector, &out_entry);
+ if (status != HV_STATUS_SUCCESS) {
+ kfree(stored_entry);
+ return;
+ }
+
+ *stored_entry = out_entry;
+ data->chip_data = stored_entry;
+ entry_to_msi_msg(&out_entry, msg);
+
+ return;
+}
+
+static int hv_unmap_msi_interrupt(struct pci_dev *dev, struct hv_interrupt_entry *old_entry)
+{
+ return hv_unmap_interrupt(hv_build_pci_dev_id(dev).as_uint64, old_entry);
+}
+
+static void hv_teardown_msi_irq_common(struct pci_dev *dev, struct msi_desc *msidesc, int irq)
+{
+ u64 status;
+ struct hv_interrupt_entry old_entry;
+ struct irq_desc *desc;
+ struct irq_data *data;
+ struct msi_msg msg;
+
+ desc = irq_to_desc(irq);
+ if (!desc) {
+ pr_debug("%s: no irq desc\n", __func__);
+ return;
+ }
+
+ data = &desc->irq_data;
+ if (!data) {
+ pr_debug("%s: no irq data\n", __func__);
+ return;
+ }
+
+ if (!data->chip_data) {
+ pr_debug("%s: no chip data\n!", __func__);
+ return;
+ }
+
+ old_entry = *(struct hv_interrupt_entry *)data->chip_data;
+ entry_to_msi_msg(&old_entry, &msg);
+
+ kfree(data->chip_data);
+ data->chip_data = NULL;
+
+ status = hv_unmap_msi_interrupt(dev, &old_entry);
+
+ if (status != HV_STATUS_SUCCESS) {
+ pr_err("%s: hypercall failed, status %lld\n", __func__, status);
+ return;
+ }
+}
+
+static void hv_msi_domain_free_irqs(struct irq_domain *domain, struct device *dev)
+{
+ int i;
+ struct msi_desc *entry;
+ struct pci_dev *pdev;
+
+ if (WARN_ON_ONCE(!dev_is_pci(dev)))
+ return;
+
+ pdev = to_pci_dev(dev);
+
+ for_each_pci_msi_entry(entry, pdev) {
+ if (entry->irq) {
+ for (i = 0; i < entry->nvec_used; i++) {
+ hv_teardown_msi_irq_common(pdev, entry, entry->irq + i);
+ irq_domain_free_irqs(entry->irq + i, 1);
+ }
+ }
+ }
+}
+
+/*
+ * IRQ Chip for MSI PCI/PCI-X/PCI-Express Devices,
+ * which implement the MSI or MSI-X Capability Structure.
+ */
+static struct irq_chip hv_pci_msi_controller = {
+ .name = "HV-PCI-MSI",
+ .irq_unmask = pci_msi_unmask_irq,
+ .irq_mask = pci_msi_mask_irq,
+ .irq_ack = irq_chip_ack_parent,
+ .irq_retrigger = irq_chip_retrigger_hierarchy,
+ .irq_compose_msi_msg = hv_irq_compose_msi_msg,
+ .irq_set_affinity = msi_domain_set_affinity,
+ .flags = IRQCHIP_SKIP_SET_WAKE,
+};
+
+static struct msi_domain_ops pci_msi_domain_ops = {
+ .domain_free_irqs = hv_msi_domain_free_irqs,
+ .msi_prepare = pci_msi_prepare,
+};
+
+static struct msi_domain_info hv_pci_msi_domain_info = {
+ .flags = MSI_FLAG_USE_DEF_DOM_OPS |
MSI_FLAG_USE_DEF_CHIP_OPS |
+ MSI_FLAG_PCI_MSIX,
+ .ops = &pci_msi_domain_ops,
+ .chip = &hv_pci_msi_controller,
+ .handler = handle_edge_irq,
+ .handler_name = "edge",
+};
+
+struct irq_domain * __init hv_create_pci_msi_domain(void)
+{
+ struct irq_domain *d = NULL;
+ struct fwnode_handle *fn;
+
+ fn = irq_domain_alloc_named_fwnode("HV-PCI-MSI");
+ if (fn)
+ d = pci_msi_create_irq_domain(fn, &hv_pci_msi_domain_info, x86_vector_domain);
+
+ /* No point in going further if we can't get an irq domain */
+ BUG_ON(!d);
+
+ return d;
+}
+
+#endif /* CONFIG_PCI_MSI */
There's a function get_cpu_mask() that returns a pointer to a cpumask with only
the specified cpu set in the mask. It returns a const pointer to the correct entry
in a pre-allocated array of all such cpumasks, so it's a lot more efficient than
allocating and initializing a local cpumask instance on the stack.
That's nice.
I've got the following diff to fix both issues. If you're happy with the
changes, can you give your Reviewed-by? That saves a round of posting.
There's a function get_cpu_mask() that returns a pointer to a cpumask with only
the specified cpu set in the mask. It returns a const pointer to the correct entry
in a pre-allocated array of all such cpumasks, so it's a lot more efficient than
allocating and initializing a local cpumask instance on the stack.
That's nice.
I've got the following diff to fix both issues. If you're happy with the
changes, can you give your Reviewed-by? That saves a round of posting.
Can you just do the following and get rid of the 'mask' local entirely?
nr_bank = cpumask_to_vpset(&(intr_desc->target.vp_set), cpumask_of(cpu));
Either way,
Reviewed-by: Michael Kelley <redacted>
if (nr_bank < 0) {
local_irq_restore(flags);
pr_err("%s: unable to generate VP set\n", __func__);
From: Wei Liu <wei.liu@kernel.org> Date: 2021-02-03 15:12:03
We will soon need to access fields inside the MSI address and MSI data
fields. Introduce hv_msi_address_register and hv_msi_data_register.
Fix up one user of hv_msi_entry in mshyperv.h.
No functional change expected.
Signed-off-by: Wei Liu <wei.liu@kernel.org>
Reviewed-by: Michael Kelley <redacted>
---
arch/x86/include/asm/mshyperv.h | 4 ++--
include/asm-generic/hyperv-tlfs.h | 28 ++++++++++++++++++++++++++--
2 files changed, 28 insertions(+), 4 deletions(-)
From: Wei Liu <wei.liu@kernel.org> Date: 2021-02-03 15:12:32
We will need to identify the device we want Microsoft Hypervisor to
manipulate. Introduce the data structures for that purpose.
They will be used in a later patch.
Signed-off-by: Sunil Muthuswamy <redacted>
Co-Developed-by: Sunil Muthuswamy <redacted>
Signed-off-by: Wei Liu <wei.liu@kernel.org>
---
v6:
1. Add reserved0 as field name.
---
include/asm-generic/hyperv-tlfs.h | 79 +++++++++++++++++++++++++++++++
1 file changed, 79 insertions(+)
From: Michael Kelley <hidden> Date: 2021-02-04 17:18:02
From: Wei Liu <wei.liu@kernel.org> Sent: Wednesday, February 3, 2021 7:05 AM
quoted hunk
We will need to identify the device we want Microsoft Hypervisor to
manipulate. Introduce the data structures for that purpose.
They will be used in a later patch.
Signed-off-by: Sunil Muthuswamy <redacted>
Co-Developed-by: Sunil Muthuswamy <redacted>
Signed-off-by: Wei Liu <wei.liu@kernel.org>
---
v6:
1. Add reserved0 as field name.
---
include/asm-generic/hyperv-tlfs.h | 79 +++++++++++++++++++++++++++++++
1 file changed, 79 insertions(+)
From: Wei Liu <wei.liu@kernel.org> Date: 2021-02-03 15:12:32
They are used to deposit pages into Microsoft Hypervisor and bring up
logical and virtual processors.
Signed-off-by: Lillian Grassin-Drake <redacted>
Signed-off-by: Sunil Muthuswamy <redacted>
Signed-off-by: Nuno Das Neves <redacted>
Co-Developed-by: Lillian Grassin-Drake <redacted>
Co-Developed-by: Sunil Muthuswamy <redacted>
Co-Developed-by: Nuno Das Neves <redacted>
Signed-off-by: Wei Liu <wei.liu@kernel.org>
---
v6:
1. Address Michael's comments.
v4: Fix compilation issue when CONFIG_ACPI_NUMA is not set.
v3:
1. Add __packed to structures.
2. Drop unnecessary exports.
v2:
1. Adapt to hypervisor side changes
2. Address Vitaly's comments
use u64 status
pages
major comments
minor comments
rely on acpi code
---
arch/x86/hyperv/Makefile | 2 +-
arch/x86/hyperv/hv_proc.c | 219 ++++++++++++++++++++++++++++++
arch/x86/include/asm/mshyperv.h | 4 +
include/asm-generic/hyperv-tlfs.h | 67 +++++++++
4 files changed, 291 insertions(+), 1 deletion(-)
create mode 100644 arch/x86/hyperv/hv_proc.c
@@ -0,0 +1,219 @@+// SPDX-License-Identifier: GPL-2.0+#include<linux/types.h>+#include<linux/version.h>+#include<linux/vmalloc.h>+#include<linux/mm.h>+#include<linux/clockchips.h>+#include<linux/acpi.h>+#include<linux/hyperv.h>+#include<linux/slab.h>+#include<linux/cpuhotplug.h>+#include<linux/minmax.h>+#include<asm/hypervisor.h>+#include<asm/mshyperv.h>+#include<asm/apic.h>++#include<asm/trace/hyperv.h>++/*+*Seestructhv_deposit_memory.Thefirstu64ispartitionID,therest+*areGPAs.+*/+#define HV_DEPOSIT_MAX (HV_HYP_PAGE_SIZE / sizeof(u64) - 1)++/* Deposits exact number of pages. Must be called with interrupts enabled. */+inthv_call_deposit_pages(intnode,u64partition_id,u32num_pages)+{+structpage**pages,*page;+int*counts;+intnum_allocations;+inti,j,page_count;+intorder;+u64status;+intret;+u64base_pfn;+structhv_deposit_memory*input_page;+unsignedlongflags;++if(num_pages>HV_DEPOSIT_MAX)+return-E2BIG;+if(!num_pages)+return0;++/* One buffer for page pointers and counts */+page=alloc_page(GFP_KERNEL);+if(!page)+return-ENOMEM;+pages=page_address(page);++counts=kcalloc(HV_DEPOSIT_MAX,sizeof(int),GFP_KERNEL);+if(!counts){+free_page((unsignedlong)pages);+return-ENOMEM;+}++/* Allocate all the pages before disabling interrupts */+i=0;++while(num_pages){+/* Find highest order we can actually allocate */+order=31-__builtin_clz(num_pages);++while(1){+pages[i]=alloc_pages_node(node,GFP_KERNEL,order);+if(pages[i])+break;+if(!order){+ret=-ENOMEM;+num_allocations=i;+gotoerr_free_allocations;+}+--order;+}++split_page(pages[i],order);+counts[i]=1<<order;+num_pages-=counts[i];+i++;+}+num_allocations=i;++local_irq_save(flags);++input_page=*this_cpu_ptr(hyperv_pcpu_input_arg);++input_page->partition_id=partition_id;++/* Populate gpa_page_list - these will fit on the input page */+for(i=0,page_count=0;i<num_allocations;++i){+base_pfn=page_to_pfn(pages[i]);+for(j=0;j<counts[i];++j,++page_count)+input_page->gpa_page_list[page_count]=base_pfn+j;+}+status=hv_do_rep_hypercall(HVCALL_DEPOSIT_MEMORY,+page_count,0,input_page,NULL);+local_irq_restore(flags);++if((status&HV_HYPERCALL_RESULT_MASK)!=HV_STATUS_SUCCESS){+pr_err("Failed to deposit pages: %lld\n",status);+ret=status;+gotoerr_free_allocations;+}++ret=0;+gotofree_buf;++err_free_allocations:+for(i=0;i<num_allocations;++i){+base_pfn=page_to_pfn(pages[i]);+for(j=0;j<counts[i];++j)+__free_page(pfn_to_page(base_pfn+j));+}++free_buf:+free_page((unsignedlong)pages);+kfree(counts);+returnret;+}++inthv_call_add_logical_proc(intnode,u32lp_index,u32apic_id)+{+structhv_add_logical_processor_in*input;+structhv_add_logical_processor_out*output;+u64status;+unsignedlongflags;+intret=0;+intpxm=node_to_pxm(node);++/*+*Whenaddingalogicalprocessor,thehypervisormayreturn+*HV_STATUS_INSUFFICIENT_MEMORY.Whenthathappens,wedepositmore+*pagesandretry.+*/+do{+local_irq_save(flags);++input=*this_cpu_ptr(hyperv_pcpu_input_arg);+/* We don't do anything with the output right now */+output=*this_cpu_ptr(hyperv_pcpu_output_arg);++input->lp_index=lp_index;+input->apic_id=apic_id;+input->flags=0;+input->proximity_domain_info.domain_id=pxm;+input->proximity_domain_info.flags.reserved=0;+input->proximity_domain_info.flags.proximity_info_valid=1;+input->proximity_domain_info.flags.proximity_preferred=1;+status=hv_do_hypercall(HVCALL_ADD_LOGICAL_PROCESSOR,+input,output);+local_irq_restore(flags);++status&=HV_HYPERCALL_RESULT_MASK;++if(status!=HV_STATUS_INSUFFICIENT_MEMORY){+if(status!=HV_STATUS_SUCCESS){+pr_err("%s: cpu %u apic ID %u, %lld\n",__func__,+lp_index,apic_id,status);+ret=status;+}+break;+}+ret=hv_call_deposit_pages(node,hv_current_partition_id,1);+}while(!ret);++returnret;+}++inthv_call_create_vp(intnode,u64partition_id,u32vp_index,u32flags)+{+structhv_create_vp*input;+u64status;+unsignedlongirq_flags;+intret=0;+intpxm=node_to_pxm(node);++/* Root VPs don't seem to need pages deposited */+if(partition_id!=hv_current_partition_id){+/* The value 90 is empirically determined. It may change. */+ret=hv_call_deposit_pages(node,partition_id,90);+if(ret)+returnret;+}++do{+local_irq_save(irq_flags);++input=*this_cpu_ptr(hyperv_pcpu_input_arg);++input->partition_id=partition_id;+input->vp_index=vp_index;+input->flags=flags;+input->subnode_type=HvSubnodeAny;+if(node!=NUMA_NO_NODE){+input->proximity_domain_info.domain_id=pxm;+input->proximity_domain_info.flags.reserved=0;+input->proximity_domain_info.flags.proximity_info_valid=1;+input->proximity_domain_info.flags.proximity_preferred=1;+}else{+input->proximity_domain_info.as_uint64=0;+}+status=hv_do_hypercall(HVCALL_CREATE_VP,input,NULL);+local_irq_restore(irq_flags);++status&=HV_HYPERCALL_RESULT_MASK;++if(status!=HV_STATUS_INSUFFICIENT_MEMORY){+if(status!=HV_STATUS_SUCCESS){+pr_err("%s: vcpu %u, lp %u, %lld\n",__func__,+vp_index,flags,status);+ret=status;+}+break;+}+ret=hv_call_deposit_pages(node,partition_id,1);++}while(!ret);++returnret;+}+
@@ -413,6 +416,70 @@ struct hv_get_partition_id {u64partition_id;}__packed;+/* HvDepositMemory hypercall */+structhv_deposit_memory{+u64partition_id;+u64gpa_page_list[];+}__packed;++structhv_proximity_domain_flags{+u32proximity_preferred:1;+u32reserved:30;+u32proximity_info_valid:1;+}__packed;++/* Not a union in windows but useful for zeroing */+unionhv_proximity_domain_info{+struct{+u32domain_id;+structhv_proximity_domain_flagsflags;+};+u64as_uint64;+}__packed;++structhv_lp_startup_status{+u64hv_status;+u64substatus1;+u64substatus2;+u64substatus3;+u64substatus4;+u64substatus5;+u64substatus6;+}__packed;++/* HvAddLogicalProcessor hypercall */+structhv_add_logical_processor_in{+u32lp_index;+u32apic_id;+unionhv_proximity_domain_infoproximity_domain_info;+u64flags;+}__packed;++structhv_add_logical_processor_out{+structhv_lp_startup_statusstartup_status;+}__packed;++enumHV_SUBNODE_TYPE+{+HvSubnodeAny=0,+HvSubnodeSocket=1,+HvSubnodeAmdNode=2,+HvSubnodeL3=3,+HvSubnodeCount=4,+HvSubnodeInvalid=-1+};++/* HvCreateVp hypercall */+structhv_create_vp{+u64partition_id;+u32vp_index;+u8padding[3];+u8subnode_type;+u64subnode_id;+unionhv_proximity_domain_infoproximity_domain_info;+u64flags;+}__packed;+/* HvRetargetDeviceInterrupt hypercall */unionhv_msi_entry{u64as_uint64;
From: Michael Kelley <hidden> Date: 2021-02-04 17:17:31
From: Wei Liu <wei.liu@kernel.org> Sent: Wednesday, February 3, 2021 7:04 AM
They are used to deposit pages into Microsoft Hypervisor and bring up
logical and virtual processors.
Signed-off-by: Lillian Grassin-Drake <redacted>
Signed-off-by: Sunil Muthuswamy <redacted>
Signed-off-by: Nuno Das Neves <redacted>
Co-Developed-by: Lillian Grassin-Drake <redacted>
Co-Developed-by: Sunil Muthuswamy <redacted>
Co-Developed-by: Nuno Das Neves <redacted>
Signed-off-by: Wei Liu <wei.liu@kernel.org>
---
v6:
1. Address Michael's comments.
v4: Fix compilation issue when CONFIG_ACPI_NUMA is not set.
v3:
1. Add __packed to structures.
2. Drop unnecessary exports.
v2:
1. Adapt to hypervisor side changes
2. Address Vitaly's comments
use u64 status
pages
major comments
minor comments
rely on acpi code
---
arch/x86/hyperv/Makefile | 2 +-
arch/x86/hyperv/hv_proc.c | 219 ++++++++++++++++++++++++++++++
arch/x86/include/asm/mshyperv.h | 4 +
include/asm-generic/hyperv-tlfs.h | 67 +++++++++
4 files changed, 291 insertions(+), 1 deletion(-)
create mode 100644 arch/x86/hyperv/hv_proc.c
From: Wei Liu <wei.liu@kernel.org> Date: 2021-02-03 15:12:33
We will soon use the same structure to handle IO-APIC interrupts as
well. Introduce an enum to identify the source and a data structure for
IO-APIC RTE.
While at it, update pci-hyperv.c to use the enum.
No functional change.
Signed-off-by: Wei Liu <wei.liu@kernel.org>
Acked-by: Rob Herring <robh@kernel.org>
Reviewed-by: Michael Kelley <redacted>
---
drivers/pci/controller/pci-hyperv.c | 2 +-
include/asm-generic/hyperv-tlfs.h | 36 +++++++++++++++++++++++++++--
2 files changed, 35 insertions(+), 3 deletions(-)
From: Wei Liu <wei.liu@kernel.org> Date: 2021-02-03 15:15:13
Microsoft Hypervisor requires the root partition to make a few
hypercalls to setup application processors before they can be used.
Signed-off-by: Lillian Grassin-Drake <redacted>
Signed-off-by: Sunil Muthuswamy <redacted>
Co-Developed-by: Lillian Grassin-Drake <redacted>
Co-Developed-by: Sunil Muthuswamy <redacted>
Signed-off-by: Wei Liu <wei.liu@kernel.org>
Reviewed-by: Michael Kelley <redacted>
---
CPU hotplug and unplug is not yet supported in this setup, so those
paths remain untouched.
v3: Always call native SMP preparation function.
---
arch/x86/kernel/cpu/mshyperv.c | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
@@ -31,6 +31,7 @@#include<asm/reboot.h>#include<asm/nmi.h>#include<clocksource/hyperv_timer.h>+#include<asm/numa.h>/* Is Linux running as the root partition? */boolhv_root_partition;
From: Wei Liu <wei.liu@kernel.org> Date: 2021-02-03 15:15:52
There is already a stub function for pxm_to_node but conversion to the
other direction is missing.
It will be used by Microsoft Hypervisor code later.
Signed-off-by: Wei Liu <wei.liu@kernel.org>
---
v6: new
---
include/acpi/acpi_numa.h | 4 ++++
1 file changed, 4 insertions(+)
From: Wei Liu <wei.liu@kernel.org> Date: 2021-02-04 18:44:55
On Wed, Feb 03, 2021 at 03:04:27PM +0000, Wei Liu wrote:
There is already a stub function for pxm_to_node but conversion to the
other direction is missing.
It will be used by Microsoft Hypervisor code later.
Signed-off-by: Wei Liu <wei.liu@kernel.org>
Hi ACPI maintainers, if you're happy with this patch I can take it via
the hyperv-next tree, given the issue is discovered when pxm_to_node is
called in our code.
From: "Rafael J. Wysocki" <rafael@kernel.org> Date: 2021-02-04 18:48:55
On Thu, Feb 4, 2021 at 7:41 PM Wei Liu [off-list ref] wrote:
On Wed, Feb 03, 2021 at 03:04:27PM +0000, Wei Liu wrote:
quoted
There is already a stub function for pxm_to_node but conversion to the
other direction is missing.
It will be used by Microsoft Hypervisor code later.
Signed-off-by: Wei Liu <wei.liu@kernel.org>
Hi ACPI maintainers, if you're happy with this patch I can take it via
the hyperv-next tree, given the issue is discovered when pxm_to_node is
called in our code.
From: Wei Liu <wei.liu@kernel.org> Date: 2021-02-04 18:51:44
On Thu, Feb 04, 2021 at 07:45:25PM +0100, Rafael J. Wysocki wrote:
On Thu, Feb 4, 2021 at 7:41 PM Wei Liu [off-list ref] wrote:
quoted
On Wed, Feb 03, 2021 at 03:04:27PM +0000, Wei Liu wrote:
quoted
There is already a stub function for pxm_to_node but conversion to the
other direction is missing.
It will be used by Microsoft Hypervisor code later.
Signed-off-by: Wei Liu <wei.liu@kernel.org>
Hi ACPI maintainers, if you're happy with this patch I can take it via
the hyperv-next tree, given the issue is discovered when pxm_to_node is
called in our code.
Yes, you can.
Thanks Rafael. I will add your ack to the patch as well.
Wei.
From: Wei Liu <wei.liu@kernel.org> Date: 2021-02-03 15:15:52
When Linux runs as the root partition, it will need to make hypercalls
which return data from the hypervisor.
Allocate pages for storing results when Linux runs as the root
partition.
Signed-off-by: Lillian Grassin-Drake <redacted>
Co-Developed-by: Lillian Grassin-Drake <redacted>
Signed-off-by: Wei Liu <wei.liu@kernel.org>
---
v3: Fix hv_cpu_die to use free_pages.
v2: Address Vitaly's comments
---
arch/x86/hyperv/hv_init.c | 35 ++++++++++++++++++++++++++++-----
arch/x86/include/asm/mshyperv.h | 1 +
2 files changed, 31 insertions(+), 5 deletions(-)
@@ -73,12 +76,19 @@ static int hv_cpu_init(unsigned int cpu)void**input_arg;structpage*pg;-input_arg=(void**)this_cpu_ptr(hyperv_pcpu_input_arg);/* hv_cpu_init() can be called with IRQs disabled from hv_resume() */-pg=alloc_page(irqs_disabled()?GFP_ATOMIC:GFP_KERNEL);+pg=alloc_pages(irqs_disabled()?GFP_ATOMIC:GFP_KERNEL,hv_root_partition?1:0);if(unlikely(!pg))return-ENOMEM;++input_arg=(void**)this_cpu_ptr(hyperv_pcpu_input_arg);*input_arg=page_address(pg);+if(hv_root_partition){+void**output_arg;++output_arg=(void**)this_cpu_ptr(hyperv_pcpu_output_arg);+*output_arg=page_address(pg+1);+}hv_get_vp_index(msr_vp_index);
@@ -205,14 +215,23 @@ static int hv_cpu_die(unsigned int cpu)unsignedintnew_cpu;unsignedlongflags;void**input_arg;-void*input_pg=NULL;+void*pg;local_irq_save(flags);input_arg=(void**)this_cpu_ptr(hyperv_pcpu_input_arg);-input_pg=*input_arg;+pg=*input_arg;*input_arg=NULL;++if(hv_root_partition){+void**output_arg;++output_arg=(void**)this_cpu_ptr(hyperv_pcpu_output_arg);+*output_arg=NULL;+}+local_irq_restore(flags);-free_page((unsignedlong)input_pg);++free_pages((unsignedlong)pg,hv_root_partition?1:0);if(hv_vp_assist_page&&hv_vp_assist_page[cpu])wrmsrl(HV_X64_MSR_VP_ASSIST_PAGE,0);
@@ -346,6 +365,12 @@ void __init hyperv_init(void)BUG_ON(hyperv_pcpu_input_arg==NULL);+/* Allocate the per-CPU state for output arg for root */+if(hv_root_partition){+hyperv_pcpu_output_arg=alloc_percpu(void*);+BUG_ON(hyperv_pcpu_output_arg==NULL);+}+/* Allocate percpu VP index */hv_vp_index=kmalloc_array(num_possible_cpus(),sizeof(*hv_vp_index),GFP_KERNEL);
From: Michael Kelley <hidden> Date: 2021-02-04 16:57:21
From: Wei Liu <wei.liu@kernel.org> Sent: Wednesday, February 3, 2021 7:04 AM
quoted hunk
When Linux runs as the root partition, it will need to make hypercalls
which return data from the hypervisor.
Allocate pages for storing results when Linux runs as the root
partition.
Signed-off-by: Lillian Grassin-Drake <redacted>
Co-Developed-by: Lillian Grassin-Drake <redacted>
Signed-off-by: Wei Liu <wei.liu@kernel.org>
---
v3: Fix hv_cpu_die to use free_pages.
v2: Address Vitaly's comments
---
arch/x86/hyperv/hv_init.c | 35 ++++++++++++++++++++++++++++-----
arch/x86/include/asm/mshyperv.h | 1 +
2 files changed, 31 insertions(+), 5 deletions(-)
@@ -73,12 +76,19 @@ static int hv_cpu_init(unsigned int cpu)void**input_arg;structpage*pg;-input_arg=(void**)this_cpu_ptr(hyperv_pcpu_input_arg);/* hv_cpu_init() can be called with IRQs disabled from hv_resume() */-pg=alloc_page(irqs_disabled()?GFP_ATOMIC:GFP_KERNEL);+pg=alloc_pages(irqs_disabled()?GFP_ATOMIC:GFP_KERNEL,hv_root_partition?1:0);if(unlikely(!pg))return-ENOMEM;++input_arg=(void**)this_cpu_ptr(hyperv_pcpu_input_arg);*input_arg=page_address(pg);+if(hv_root_partition){+void**output_arg;++output_arg=(void**)this_cpu_ptr(hyperv_pcpu_output_arg);+*output_arg=page_address(pg+1);+}hv_get_vp_index(msr_vp_index);
@@ -205,14 +215,23 @@ static int hv_cpu_die(unsigned int cpu)unsignedintnew_cpu;unsignedlongflags;void**input_arg;-void*input_pg=NULL;+void*pg;local_irq_save(flags);input_arg=(void**)this_cpu_ptr(hyperv_pcpu_input_arg);-input_pg=*input_arg;+pg=*input_arg;*input_arg=NULL;++if(hv_root_partition){+void**output_arg;++output_arg=(void**)this_cpu_ptr(hyperv_pcpu_output_arg);+*output_arg=NULL;+}+local_irq_restore(flags);-free_page((unsignedlong)input_pg);++free_pages((unsignedlong)pg,hv_root_partition?1:0);if(hv_vp_assist_page&&hv_vp_assist_page[cpu])wrmsrl(HV_X64_MSR_VP_ASSIST_PAGE,0);
@@ -346,6 +365,12 @@ void __init hyperv_init(void)BUG_ON(hyperv_pcpu_input_arg==NULL);+/* Allocate the per-CPU state for output arg for root */+if(hv_root_partition){+hyperv_pcpu_output_arg=alloc_percpu(void*);+BUG_ON(hyperv_pcpu_output_arg==NULL);+}+/* Allocate percpu VP index */hv_vp_index=kmalloc_array(num_possible_cpus(),sizeof(*hv_vp_index),GFP_KERNEL);
From: Wei Liu <wei.liu@kernel.org> Date: 2021-02-03 15:16:12
There is no VMBus and the other infrastructures initialized in
hv_acpi_init when Linux is running as the root partition.
Signed-off-by: Wei Liu <wei.liu@kernel.org>
Reviewed-by: Pavel Tatashin <pasha.tatashin@soleen.com>
Reviewed-by: Michael Kelley <redacted>
---
v3: Return 0 instead of -ENODEV.
---
drivers/hv/vmbus_drv.c | 3 +++
1 file changed, 3 insertions(+)
From: Wei Liu <wei.liu@kernel.org> Date: 2021-02-03 15:16:25
When Linux runs as the root partition, the setup required for TSC page
is different. Luckily Linux also has access to the MSR based
clocksource. We can just disable the TSC page clocksource if Linux is
the root partition.
Signed-off-by: Wei Liu <wei.liu@kernel.org>
Acked-by: Daniel Lezcano <redacted>
Reviewed-by: Pavel Tatashin <pasha.tatashin@soleen.com>
Reviewed-by: Michael Kelley <redacted>
---
drivers/clocksource/hyperv_timer.c | 3 +++
1 file changed, 3 insertions(+)
From: Wei Liu <wei.liu@kernel.org> Date: 2021-02-03 15:16:54
For now we can use the privilege flag to check. Stash the value to be
used later.
Put in a bunch of defines for future use when we want to have more
fine-grained detection.
Signed-off-by: Wei Liu <wei.liu@kernel.org>
Reviewed-by: Pavel Tatashin <pasha.tatashin@soleen.com>
---
v3: move hv_root_partition to mshyperv.c
---
arch/x86/include/asm/hyperv-tlfs.h | 10 ++++++++++
arch/x86/include/asm/mshyperv.h | 2 ++
arch/x86/kernel/cpu/mshyperv.c | 20 ++++++++++++++++++++
3 files changed, 32 insertions(+)
@@ -32,6 +32,10 @@#include<asm/nmi.h>#include<clocksource/hyperv_timer.h>+/* Is Linux running as the root partition? */+boolhv_root_partition;+EXPORT_SYMBOL_GPL(hv_root_partition);+structms_hyperv_infoms_hyperv;EXPORT_SYMBOL_GPL(ms_hyperv);
@@ -237,6 +241,22 @@ static void __init ms_hyperv_init_platform(void)pr_debug("Hyper-V: max %u virtual processors, %u logical processors\n",ms_hyperv.max_vp_index,ms_hyperv.max_lp_index);+/*+*CheckCPUmanagementprivilege.+*+*TomirrorwhatWindowsdoesweshouldextractCPUmanagement+*featuresandusetheReservedIdentityBittodetectifLinuxisthe+*rootpartition.ButthatrequiresnegotiatingCPUmanagement+*interface(aprocesstobefinalized).+*+*Fornow,usetheprivilegeflagastheindicatorforrunningas+*root.+*/+if(cpuid_ebx(HYPERV_CPUID_FEATURES)&HV_CPU_MANAGEMENT){+hv_root_partition=true;+pr_info("Hyper-V: running as root partition\n");+}+/**Extracthostinformation.*/
From: Michael Kelley <hidden> Date: 2021-02-04 16:52:19
From: Wei Liu <wei.liu@kernel.org> Sent: Wednesday, February 3, 2021 7:04 AM
quoted hunk
For now we can use the privilege flag to check. Stash the value to be
used later.
Put in a bunch of defines for future use when we want to have more
fine-grained detection.
Signed-off-by: Wei Liu <wei.liu@kernel.org>
Reviewed-by: Pavel Tatashin <pasha.tatashin@soleen.com>
---
v3: move hv_root_partition to mshyperv.c
---
arch/x86/include/asm/hyperv-tlfs.h | 10 ++++++++++
arch/x86/include/asm/mshyperv.h | 2 ++
arch/x86/kernel/cpu/mshyperv.c | 20 ++++++++++++++++++++
3 files changed, 32 insertions(+)
@@ -32,6 +32,10 @@#include<asm/nmi.h>#include<clocksource/hyperv_timer.h>+/* Is Linux running as the root partition? */+boolhv_root_partition;+EXPORT_SYMBOL_GPL(hv_root_partition);+structms_hyperv_infoms_hyperv;EXPORT_SYMBOL_GPL(ms_hyperv);
@@ -237,6 +241,22 @@ static void __init ms_hyperv_init_platform(void)pr_debug("Hyper-V: max %u virtual processors, %u logical processors\n",ms_hyperv.max_vp_index,ms_hyperv.max_lp_index);+/*+*CheckCPUmanagementprivilege.+*+*TomirrorwhatWindowsdoesweshouldextractCPUmanagement+*featuresandusetheReservedIdentityBittodetectifLinuxisthe+*rootpartition.ButthatrequiresnegotiatingCPUmanagement+*interface(aprocesstobefinalized).+*+*Fornow,usetheprivilegeflagastheindicatorforrunningas+*root.+*/+if(cpuid_ebx(HYPERV_CPUID_FEATURES)&HV_CPU_MANAGEMENT){+hv_root_partition=true;+pr_info("Hyper-V: running as root partition\n");+}+/**Extracthostinformation.*/--
From: Wei Liu <wei.liu@kernel.org> Date: 2021-02-03 15:17:29
This makes the name match Hyper-V TLFS.
Signed-off-by: Wei Liu <wei.liu@kernel.org>
Reviewed-by: Vitaly Kuznetsov <vkuznets@redhat.com>
Reviewed-by: Pavel Tatashin <pasha.tatashin@soleen.com>
Reviewed-by: Michael Kelley <redacted>
---
include/asm-generic/hyperv-tlfs.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
From: Wei Liu <wei.liu@kernel.org> Date: 2021-02-04 19:50:37
On Wed, Feb 03, 2021 at 03:04:19PM +0000, Wei Liu wrote:
Wei Liu (16):
asm-generic/hyperv: change HV_CPU_POWER_MANAGEMENT to
HV_CPU_MANAGEMENT
x86/hyperv: detect if Linux is the root partition
Drivers: hv: vmbus: skip VMBus initialization if Linux is root
clocksource/hyperv: use MSR-based access if running as root
x86/hyperv: allocate output arg pages if required
x86/hyperv: extract partition ID from Microsoft Hypervisor if
necessary
x86/hyperv: handling hypercall page setup for root
ACPI / NUMA: add a stub function for node_to_pxm()
x86/hyperv: provide a bunch of helper functions
x86/hyperv: implement and use hv_smp_prepare_cpus
asm-generic/hyperv: update hv_msi_entry
asm-generic/hyperv: update hv_interrupt_entry
asm-generic/hyperv: introduce hv_device_id and auxiliary structures
asm-generic/hyperv: import data structures for mapping device
interrupts
x86/hyperv: implement an MSI domain for root partition
iommu/hyperv: setup an IO-APIC IRQ remapping domain for root partition
This series is now rebased and pushed to hyperv-next.
Many thanks to all the people that provided reviews and comments.
Wei.