diff mbox series

[v3,3/4] x86/kvm: add max number of vcpus for hyperv emulation

Message ID 20211116141054.17800-4-jgross@suse.com (mailing list archive)
State New, archived
Headers show
Series x86/kvm: add boot parameters for max vcpu configs | expand

Commit Message

Jürgen Groß Nov. 16, 2021, 2:10 p.m. UTC
When emulating Hyperv the theoretical maximum of vcpus supported is
4096, as this is the architectural limit for sending IPIs via the PV
interface.

For restricting the actual supported number of vcpus for that case
introduce another define KVM_MAX_HYPERV_VCPUS and set it to 1024, like
today's KVM_MAX_VCPUS. Make both values unsigned ones as this will be
needed later.

The actual number of supported vcpus for Hyperv emulation will be the
lower value of both defines.

This is a preparation for a future boot parameter support of the max
number of vcpus for a KVM guest.

Signed-off-by: Juergen Gross <jgross@suse.com>
---
V3:
- new patch
---
 arch/x86/include/asm/kvm_host.h |  3 ++-
 arch/x86/kvm/hyperv.c           | 15 ++++++++-------
 2 files changed, 10 insertions(+), 8 deletions(-)

Comments

Sean Christopherson Nov. 17, 2021, 8:50 p.m. UTC | #1
On Tue, Nov 16, 2021, Juergen Gross wrote:
> When emulating Hyperv the theoretical maximum of vcpus supported is
> 4096, as this is the architectural limit for sending IPIs via the PV
> interface.
> 
> For restricting the actual supported number of vcpus for that case
> introduce another define KVM_MAX_HYPERV_VCPUS and set it to 1024, like
> today's KVM_MAX_VCPUS. Make both values unsigned ones as this will be
> needed later.
> 
> The actual number of supported vcpus for Hyperv emulation will be the
> lower value of both defines.
> 
> This is a preparation for a future boot parameter support of the max
> number of vcpus for a KVM guest.
> 
> Signed-off-by: Juergen Gross <jgross@suse.com>
> ---
> V3:
> - new patch
> ---
>  arch/x86/include/asm/kvm_host.h |  3 ++-
>  arch/x86/kvm/hyperv.c           | 15 ++++++++-------
>  2 files changed, 10 insertions(+), 8 deletions(-)
> 
> diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
> index 886930ec8264..8ea03ff01c45 100644
> --- a/arch/x86/include/asm/kvm_host.h
> +++ b/arch/x86/include/asm/kvm_host.h
> @@ -38,7 +38,8 @@
>  
>  #define __KVM_HAVE_ARCH_VCPU_DEBUGFS
>  
> -#define KVM_MAX_VCPUS 1024
> +#define KVM_MAX_VCPUS 1024U
> +#define KVM_MAX_HYPERV_VCPUS 1024U

I don't see any reason to put this in kvm_host.h, it should never be used outside
of hyperv.c.

>  #define KVM_MAX_VCPU_IDS kvm_max_vcpu_ids()
>  /* memory slots that are not exposed to userspace */
>  #define KVM_PRIVATE_MEM_SLOTS 3
> diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c
> index 4a555f32885a..c0fa837121f1 100644
> --- a/arch/x86/kvm/hyperv.c
> +++ b/arch/x86/kvm/hyperv.c
> @@ -41,7 +41,7 @@
>  /* "Hv#1" signature */
>  #define HYPERV_CPUID_SIGNATURE_EAX 0x31237648
>  
> -#define KVM_HV_MAX_SPARSE_VCPU_SET_BITS DIV_ROUND_UP(KVM_MAX_VCPUS, 64)
> +#define KVM_HV_MAX_SPARSE_VCPU_SET_BITS DIV_ROUND_UP(KVM_MAX_HYPERV_VCPUS, 64)
>  
>  static void stimer_mark_pending(struct kvm_vcpu_hv_stimer *stimer,
>  				bool vcpu_kick);
> @@ -166,7 +166,7 @@ static struct kvm_vcpu *get_vcpu_by_vpidx(struct kvm *kvm, u32 vpidx)
>  	struct kvm_vcpu *vcpu = NULL;
>  	int i;
>  
> -	if (vpidx >= KVM_MAX_VCPUS)
> +	if (vpidx >= min(KVM_MAX_VCPUS, KVM_MAX_HYPERV_VCPUS))

IMO, this is conceptually wrong.  KVM should refuse to allow Hyper-V to be enabled
if the max number of vCPUs exceeds what can be supported, or should refuse to create
the vCPUs.  I agree it makes sense to add a Hyper-V specific limit, since there are
Hyper-V structures that have a hard limit, but detection of violations should be a
BUILD_BUG_ON, not a silent failure at runtime.
Jürgen Groß Nov. 18, 2021, 7:43 a.m. UTC | #2
On 17.11.21 21:50, Sean Christopherson wrote:
> On Tue, Nov 16, 2021, Juergen Gross wrote:
>> When emulating Hyperv the theoretical maximum of vcpus supported is
>> 4096, as this is the architectural limit for sending IPIs via the PV
>> interface.
>>
>> For restricting the actual supported number of vcpus for that case
>> introduce another define KVM_MAX_HYPERV_VCPUS and set it to 1024, like
>> today's KVM_MAX_VCPUS. Make both values unsigned ones as this will be
>> needed later.
>>
>> The actual number of supported vcpus for Hyperv emulation will be the
>> lower value of both defines.
>>
>> This is a preparation for a future boot parameter support of the max
>> number of vcpus for a KVM guest.
>>
>> Signed-off-by: Juergen Gross <jgross@suse.com>
>> ---
>> V3:
>> - new patch
>> ---
>>   arch/x86/include/asm/kvm_host.h |  3 ++-
>>   arch/x86/kvm/hyperv.c           | 15 ++++++++-------
>>   2 files changed, 10 insertions(+), 8 deletions(-)
>>
>> diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
>> index 886930ec8264..8ea03ff01c45 100644
>> --- a/arch/x86/include/asm/kvm_host.h
>> +++ b/arch/x86/include/asm/kvm_host.h
>> @@ -38,7 +38,8 @@
>>   
>>   #define __KVM_HAVE_ARCH_VCPU_DEBUGFS
>>   
>> -#define KVM_MAX_VCPUS 1024
>> +#define KVM_MAX_VCPUS 1024U
>> +#define KVM_MAX_HYPERV_VCPUS 1024U
> 
> I don't see any reason to put this in kvm_host.h, it should never be used outside
> of hyperv.c.

Okay, fine with me.

> 
>>   #define KVM_MAX_VCPU_IDS kvm_max_vcpu_ids()
>>   /* memory slots that are not exposed to userspace */
>>   #define KVM_PRIVATE_MEM_SLOTS 3
>> diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c
>> index 4a555f32885a..c0fa837121f1 100644
>> --- a/arch/x86/kvm/hyperv.c
>> +++ b/arch/x86/kvm/hyperv.c
>> @@ -41,7 +41,7 @@
>>   /* "Hv#1" signature */
>>   #define HYPERV_CPUID_SIGNATURE_EAX 0x31237648
>>   
>> -#define KVM_HV_MAX_SPARSE_VCPU_SET_BITS DIV_ROUND_UP(KVM_MAX_VCPUS, 64)
>> +#define KVM_HV_MAX_SPARSE_VCPU_SET_BITS DIV_ROUND_UP(KVM_MAX_HYPERV_VCPUS, 64)
>>   
>>   static void stimer_mark_pending(struct kvm_vcpu_hv_stimer *stimer,
>>   				bool vcpu_kick);
>> @@ -166,7 +166,7 @@ static struct kvm_vcpu *get_vcpu_by_vpidx(struct kvm *kvm, u32 vpidx)
>>   	struct kvm_vcpu *vcpu = NULL;
>>   	int i;
>>   
>> -	if (vpidx >= KVM_MAX_VCPUS)
>> +	if (vpidx >= min(KVM_MAX_VCPUS, KVM_MAX_HYPERV_VCPUS))
> 
> IMO, this is conceptually wrong.  KVM should refuse to allow Hyper-V to be enabled
> if the max number of vCPUs exceeds what can be supported, or should refuse to create

TBH, I wasn't sure where to put this test. Is there a guaranteed
sequence of ioctl()s regarding vcpu creation (or setting the max
number of vcpus) and the Hyper-V enabling?

> the vCPUs.  I agree it makes sense to add a Hyper-V specific limit, since there are
> Hyper-V structures that have a hard limit, but detection of violations should be a
> BUILD_BUG_ON, not a silent failure at runtime.
> 

A BUILD_BUG_ON won't be possible with KVM_MAX_VCPUS being selecteble via
boot parameter.


Juergen
Sean Christopherson Nov. 18, 2021, 2:49 p.m. UTC | #3
On Thu, Nov 18, 2021, Juergen Gross wrote:
> On 17.11.21 21:50, Sean Christopherson wrote:
> > > @@ -166,7 +166,7 @@ static struct kvm_vcpu *get_vcpu_by_vpidx(struct kvm *kvm, u32 vpidx)
> > >   	struct kvm_vcpu *vcpu = NULL;
> > >   	int i;
> > > -	if (vpidx >= KVM_MAX_VCPUS)
> > > +	if (vpidx >= min(KVM_MAX_VCPUS, KVM_MAX_HYPERV_VCPUS))
> > 
> > IMO, this is conceptually wrong.  KVM should refuse to allow Hyper-V to be enabled
> > if the max number of vCPUs exceeds what can be supported, or should refuse to create
> 
> TBH, I wasn't sure where to put this test. Is there a guaranteed
> sequence of ioctl()s regarding vcpu creation (or setting the max
> number of vcpus) and the Hyper-V enabling?

For better or worse (mostly worse), like all other things CPUID, Hyper-V is a per-vCPU
knob.  If KVM can't detect the impossible condition at compile time, kvm_check_cpuid()
is probably the right place to prevent enabling Hyper-V on an unreachable vCPU.

> > the vCPUs.  I agree it makes sense to add a Hyper-V specific limit, since there are
> > Hyper-V structures that have a hard limit, but detection of violations should be a
> > BUILD_BUG_ON, not a silent failure at runtime.
> > 
> 
> A BUILD_BUG_ON won't be possible with KVM_MAX_VCPUS being selecteble via
> boot parameter.

I was thinking that there would still be a KVM-defined max that would cap whatever
comes in from userspace.
Jürgen Groß Nov. 18, 2021, 3:24 p.m. UTC | #4
On 18.11.21 15:49, Sean Christopherson wrote:
> On Thu, Nov 18, 2021, Juergen Gross wrote:
>> On 17.11.21 21:50, Sean Christopherson wrote:
>>>> @@ -166,7 +166,7 @@ static struct kvm_vcpu *get_vcpu_by_vpidx(struct kvm *kvm, u32 vpidx)
>>>>    	struct kvm_vcpu *vcpu = NULL;
>>>>    	int i;
>>>> -	if (vpidx >= KVM_MAX_VCPUS)
>>>> +	if (vpidx >= min(KVM_MAX_VCPUS, KVM_MAX_HYPERV_VCPUS))
>>>
>>> IMO, this is conceptually wrong.  KVM should refuse to allow Hyper-V to be enabled
>>> if the max number of vCPUs exceeds what can be supported, or should refuse to create
>>
>> TBH, I wasn't sure where to put this test. Is there a guaranteed
>> sequence of ioctl()s regarding vcpu creation (or setting the max
>> number of vcpus) and the Hyper-V enabling?
> 
> For better or worse (mostly worse), like all other things CPUID, Hyper-V is a per-vCPU
> knob.  If KVM can't detect the impossible condition at compile time, kvm_check_cpuid()
> is probably the right place to prevent enabling Hyper-V on an unreachable vCPU.

With HYPERV_CPUID_IMPLEMENT_LIMITS already returning the
supported number of vcpus for the Hyper-V case I'm not sure
there is really more needed.

The problem I'm seeing is that the only thing I can do is to
let kvm_get_hv_cpuid() not adding the Hyper-V cpuid leaves for
vcpus > 64. I can't return a failure, because that would
probably let vcpu creation fail. And this is something we don't
want, as kvm_get_hv_cpuid() is called even in the case the guest
doesn't plan to use Hyper-V extensions.

> 
>>> the vCPUs.  I agree it makes sense to add a Hyper-V specific limit, since there are
>>> Hyper-V structures that have a hard limit, but detection of violations should be a
>>> BUILD_BUG_ON, not a silent failure at runtime.
>>>
>>
>> A BUILD_BUG_ON won't be possible with KVM_MAX_VCPUS being selecteble via
>> boot parameter.
> 
> I was thinking that there would still be a KVM-defined max that would cap whatever
> comes in from userspace.
> 

See my answers to you your other responses.


Juergen
Sean Christopherson Nov. 18, 2021, 4:12 p.m. UTC | #5
On Thu, Nov 18, 2021, Juergen Gross wrote:
> On 18.11.21 15:49, Sean Christopherson wrote:
> > On Thu, Nov 18, 2021, Juergen Gross wrote:
> > > On 17.11.21 21:50, Sean Christopherson wrote:
> > > > > @@ -166,7 +166,7 @@ static struct kvm_vcpu *get_vcpu_by_vpidx(struct kvm *kvm, u32 vpidx)
> > > > >    	struct kvm_vcpu *vcpu = NULL;
> > > > >    	int i;
> > > > > -	if (vpidx >= KVM_MAX_VCPUS)
> > > > > +	if (vpidx >= min(KVM_MAX_VCPUS, KVM_MAX_HYPERV_VCPUS))
> > > > 
> > > > IMO, this is conceptually wrong.  KVM should refuse to allow Hyper-V to be enabled
> > > > if the max number of vCPUs exceeds what can be supported, or should refuse to create
> > > 
> > > TBH, I wasn't sure where to put this test. Is there a guaranteed
> > > sequence of ioctl()s regarding vcpu creation (or setting the max
> > > number of vcpus) and the Hyper-V enabling?
> > 
> > For better or worse (mostly worse), like all other things CPUID, Hyper-V is a per-vCPU
> > knob.  If KVM can't detect the impossible condition at compile time, kvm_check_cpuid()
> > is probably the right place to prevent enabling Hyper-V on an unreachable vCPU.
> 
> With HYPERV_CPUID_IMPLEMENT_LIMITS already returning the
> supported number of vcpus for the Hyper-V case I'm not sure
> there is really more needed.

Yep, that'll do nicely.

> The problem I'm seeing is that the only thing I can do is to
> let kvm_get_hv_cpuid() not adding the Hyper-V cpuid leaves for
> vcpus > 64. I can't return a failure, because that would
> probably let vcpu creation fail. And this is something we don't
> want, as kvm_get_hv_cpuid() is called even in the case the guest
> doesn't plan to use Hyper-V extensions.

Argh, that thing is annoying.

My vote is still to reject KVM_SET_CPUID{2} if userspace attempts to enable Hyper-V
for a vCPU when the max number of vCPUs exceeds HYPERV_CPUID_IMPLEMENT_LIMITS.  If
userspace parrots back KVM_GET_SUPPORTED_CPUID, it will specify KVM as the hypervisor,
i.e. enabling Hyper-V requires deliberate action from userspace.

The non-vCPU version of KVM_GET_SUPPORTED_HV_CPUID is not an issue, e.g. the generic
KVM_GET_SUPPORTED_CPUID also reports features that become unsupported if dependent
CPUID features are not enabled by userspace.

The discrepancy with the per-vCPU variant of kvm_get_hv_cpuid() would be unfortunate,
but IMO that ship sailed when the per-vCPU variant was added by commit 2bc39970e932
("x86/kvm/hyper-v: Introduce KVM_GET_SUPPORTED_HV_CPUID").  We can't retroactively
yank that code out, but I don't think we should be overly concerned with keeping it
100% accurate.  IMO it's perfectly fine for KVM to define the output of
KVM_GET_SUPPORTED_HV_CPUID as being garbage if the vCPU cannot possibly support
Hyper-V enlightments.  That situation isn't possible today, so there's no backwards
compatibility to worry about.
diff mbox series

Patch

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 886930ec8264..8ea03ff01c45 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -38,7 +38,8 @@ 
 
 #define __KVM_HAVE_ARCH_VCPU_DEBUGFS
 
-#define KVM_MAX_VCPUS 1024
+#define KVM_MAX_VCPUS 1024U
+#define KVM_MAX_HYPERV_VCPUS 1024U
 #define KVM_MAX_VCPU_IDS kvm_max_vcpu_ids()
 /* memory slots that are not exposed to userspace */
 #define KVM_PRIVATE_MEM_SLOTS 3
diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c
index 4a555f32885a..c0fa837121f1 100644
--- a/arch/x86/kvm/hyperv.c
+++ b/arch/x86/kvm/hyperv.c
@@ -41,7 +41,7 @@ 
 /* "Hv#1" signature */
 #define HYPERV_CPUID_SIGNATURE_EAX 0x31237648
 
-#define KVM_HV_MAX_SPARSE_VCPU_SET_BITS DIV_ROUND_UP(KVM_MAX_VCPUS, 64)
+#define KVM_HV_MAX_SPARSE_VCPU_SET_BITS DIV_ROUND_UP(KVM_MAX_HYPERV_VCPUS, 64)
 
 static void stimer_mark_pending(struct kvm_vcpu_hv_stimer *stimer,
 				bool vcpu_kick);
@@ -166,7 +166,7 @@  static struct kvm_vcpu *get_vcpu_by_vpidx(struct kvm *kvm, u32 vpidx)
 	struct kvm_vcpu *vcpu = NULL;
 	int i;
 
-	if (vpidx >= KVM_MAX_VCPUS)
+	if (vpidx >= min(KVM_MAX_VCPUS, KVM_MAX_HYPERV_VCPUS))
 		return NULL;
 
 	vcpu = kvm_get_vcpu(kvm, vpidx);
@@ -1446,7 +1446,8 @@  static int kvm_hv_set_msr(struct kvm_vcpu *vcpu, u32 msr, u64 data, bool host)
 		struct kvm_hv *hv = to_kvm_hv(vcpu->kvm);
 		u32 new_vp_index = (u32)data;
 
-		if (!host || new_vp_index >= KVM_MAX_VCPUS)
+		if (!host ||
+		    new_vp_index >= min(KVM_MAX_VCPUS, KVM_MAX_HYPERV_VCPUS))
 			return 1;
 
 		if (new_vp_index == hv_vcpu->vp_index)
@@ -1729,7 +1730,7 @@  static __always_inline unsigned long *sparse_set_to_vcpu_mask(
 		return (unsigned long *)vp_bitmap;
 	}
 
-	bitmap_zero(vcpu_bitmap, KVM_MAX_VCPUS);
+	bitmap_zero(vcpu_bitmap, min(KVM_MAX_VCPUS, KVM_MAX_HYPERV_VCPUS));
 	kvm_for_each_vcpu(i, vcpu, kvm) {
 		if (test_bit(kvm_hv_get_vpindex(vcpu), (unsigned long *)vp_bitmap))
 			__set_bit(i, vcpu_bitmap);
@@ -1757,7 +1758,7 @@  static u64 kvm_hv_flush_tlb(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc, bool
 	struct hv_tlb_flush_ex flush_ex;
 	struct hv_tlb_flush flush;
 	u64 vp_bitmap[KVM_HV_MAX_SPARSE_VCPU_SET_BITS];
-	DECLARE_BITMAP(vcpu_bitmap, KVM_MAX_VCPUS);
+	DECLARE_BITMAP(vcpu_bitmap, KVM_MAX_HYPERV_VCPUS);
 	unsigned long *vcpu_mask;
 	u64 valid_bank_mask;
 	u64 sparse_banks[64];
@@ -1880,7 +1881,7 @@  static u64 kvm_hv_send_ipi(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc, bool
 	struct hv_send_ipi_ex send_ipi_ex;
 	struct hv_send_ipi send_ipi;
 	u64 vp_bitmap[KVM_HV_MAX_SPARSE_VCPU_SET_BITS];
-	DECLARE_BITMAP(vcpu_bitmap, KVM_MAX_VCPUS);
+	DECLARE_BITMAP(vcpu_bitmap, KVM_MAX_HYPERV_VCPUS);
 	unsigned long *vcpu_mask;
 	unsigned long valid_bank_mask;
 	u64 sparse_banks[64];
@@ -2505,7 +2506,7 @@  int kvm_get_hv_cpuid(struct kvm_vcpu *vcpu, struct kvm_cpuid2 *cpuid,
 
 		case HYPERV_CPUID_IMPLEMENT_LIMITS:
 			/* Maximum number of virtual processors */
-			ent->eax = KVM_MAX_VCPUS;
+			ent->eax = min(KVM_MAX_VCPUS, KVM_MAX_HYPERV_VCPUS);
 			/*
 			 * Maximum number of logical processors, matches
 			 * HyperV 2016.