[2/2] KVM: arm64: Reuse struct cpu_fp_state to track the guest FP state
Commit Message
At present we store the various bits of floating point state individually
in struct kvm_vpcu_arch and construct a struct cpu_fp_state to share with
the host each time we exit the guest. Let's simplify this a little by
having a struct cpu_fp_state in the struct kvm_vcpu_arch and initialising
this while initialising the guest.
As part of this remove the separate variables used for the SVE register
storage and vector length information, just using the variables in the
struct cpu_fp_state directly.
Since cpu_fp_state stores pointers to variables to be updated as part of
saving we do still need some variables stored directly in the struct for
the FPSIMD registers, SVCR and the type of FP state saved. Due to the
embedding of the FPSIMD registers into ucontext which is stored directly in
the host's data and the future need to support KVM's system register view
of SVCR and FPMR unpicking these indirections would be more involved.
No functional changes.
Signed-off-by: Mark Brown <broonie@kernel.org>
---
arch/arm64/include/asm/kvm_host.h | 11 +++++------
arch/arm64/kvm/arm.c | 12 ++++++++++++
arch/arm64/kvm/fpsimd.c | 19 +------------------
arch/arm64/kvm/guest.c | 21 ++++++++++++++-------
arch/arm64/kvm/hyp/nvhe/hyp-main.c | 5 +++--
arch/arm64/kvm/reset.c | 14 ++++++++------
6 files changed, 43 insertions(+), 39 deletions(-)
Comments
Hey broonie,
On Mon, Feb 26, 2024 at 08:44:11PM +0000, Mark Brown wrote:
> At present we store the various bits of floating point state individually
> in struct kvm_vpcu_arch and construct a struct cpu_fp_state to share with
typo: kvm_vcpu_arch
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index a2cba18effb2..84cc0dbd9b14 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -379,6 +379,18 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
> */
> vcpu->arch.fp_owner = FP_STATE_FREE;
>
> + /*
> + * Initial setup for FP state for sharing with host, if SVE is
> + * enabled additional configuration will be done.
> + *
> + * Currently we do not support SME guests so SVCR is always 0
> + * and we just need a variable to point to.
> + */
> + vcpu->arch.fp_state.st = &vcpu->arch.ctxt.fp_regs;
> + vcpu->arch.fp_state.fp_type = &vcpu->arch.fp_type;
> + vcpu->arch.fp_state.svcr = &vcpu->arch.svcr;
> + vcpu->arch.fp_state.to_save = FP_STATE_FPSIMD;
> +
I'm not too big of a fan of scattering the initialization in various
places... Why can't we have a unified helper for priming cpu_fp_state once
we know what we're dealing with?
That can be called from either kvm_setup_vcpu() or kvm_vcpu_finalize_sve()
depending on whether userspace signed up for SVE or not.
> /* Set up the timer */
> kvm_timer_vcpu_init(vcpu);
>
> diff --git a/arch/arm64/kvm/fpsimd.c b/arch/arm64/kvm/fpsimd.c
> index 8dbd62d1e677..45fe4a942992 100644
> --- a/arch/arm64/kvm/fpsimd.c
> +++ b/arch/arm64/kvm/fpsimd.c
> @@ -143,24 +143,7 @@ void kvm_arch_vcpu_ctxsync_fp(struct kvm_vcpu *vcpu)
> WARN_ON_ONCE(!irqs_disabled());
>
> if (vcpu->arch.fp_owner == FP_STATE_GUEST_OWNED) {
> -
> - /*
> - * Currently we do not support SME guests so SVCR is
> - * always 0 and we just need a variable to point to.
> - */
> - fp_state.st = &vcpu->arch.ctxt.fp_regs;
> - fp_state.sve_state = vcpu->arch.sve_state;
> - fp_state.sve_vl = vcpu->arch.sve_max_vl;
> - fp_state.sme_state = NULL;
> - fp_state.svcr = &vcpu->arch.svcr;
> - fp_state.fp_type = &vcpu->arch.fp_type;
> -
> - if (vcpu_has_sve(vcpu))
> - fp_state.to_save = FP_STATE_SVE;
> - else
> - fp_state.to_save = FP_STATE_FPSIMD;
> -
> - fpsimd_bind_state_to_cpu(&fp_state);
> + fpsimd_bind_state_to_cpu(&vcpu->arch.fp_state);
Shouldn't we get rid of the fp_state local at this point? I'm pretty
sure a compiler would emit a warning here...
On Mon, Feb 26, 2024 at 11:07:55PM -0800, Oliver Upton wrote:
> On Mon, Feb 26, 2024 at 08:44:11PM +0000, Mark Brown wrote:
> > vcpu->arch.fp_owner = FP_STATE_FREE;
..
> > + vcpu->arch.fp_state.st = &vcpu->arch.ctxt.fp_regs;
> > + vcpu->arch.fp_state.fp_type = &vcpu->arch.fp_type;
> > + vcpu->arch.fp_state.svcr = &vcpu->arch.svcr;
> > + vcpu->arch.fp_state.to_save = FP_STATE_FPSIMD;
> I'm not too big of a fan of scattering the initialization in various
> places... Why can't we have a unified helper for priming cpu_fp_state once
> we know what we're dealing with?
> That can be called from either kvm_setup_vcpu() or kvm_vcpu_finalize_sve()
> depending on whether userspace signed up for SVE or not.
It's just reflecting the existing structure of the code, we already
split the initialisation between _create() and the SVE setup so this is
just following along from that.
I'm also happier with doing whatever initialisation we can prior to
userspace getting involved, it wouldn't be an issue currently but this
state is visible to userspace and it feels error prone to have it
partially initialised when we aren't compelled to like we are with the
configurability of the vector lengths. Someone could too easily not
notice that there's a window where the state is not yet initialised but
userspace can try to access it when adding to the initisation function
during future work.
> > - fpsimd_bind_state_to_cpu(&fp_state);
> > + fpsimd_bind_state_to_cpu(&vcpu->arch.fp_state);
> Shouldn't we get rid of the fp_state local at this point? I'm pretty
> sure a compiler would emit a warning here...
Indeed, that was actually fixed locally - looks like that didn't make it
into what got sent out somehow.
@@ -539,9 +539,8 @@ struct kvm_vcpu_arch {
* floating point code saves the register state of a task it
* records which view it saved in fp_type.
*/
- void *sve_state;
+ struct cpu_fp_state fp_state;
enum fp_type fp_type;
- unsigned int sve_max_vl;
u64 svcr;
/* Ownership of the FP regs */
@@ -799,16 +798,16 @@ struct kvm_vcpu_arch {
/* Pointer to the vcpu's SVE FFR for sve_{save,load}_state() */
-#define vcpu_sve_pffr(vcpu) (kern_hyp_va((vcpu)->arch.sve_state) + \
- sve_ffr_offset((vcpu)->arch.sve_max_vl))
+#define vcpu_sve_pffr(vcpu) (kern_hyp_va((vcpu)->arch.fp_state.sve_state) + \
+ sve_ffr_offset((vcpu)->arch.fp_state.sve_vl))
-#define vcpu_sve_max_vq(vcpu) sve_vq_from_vl((vcpu)->arch.sve_max_vl)
+#define vcpu_sve_max_vq(vcpu) sve_vq_from_vl((vcpu)->arch.fp_state.sve_vl)
#define vcpu_sve_state_size(vcpu) ({ \
size_t __size_ret; \
unsigned int __vcpu_vq; \
\
- if (WARN_ON(!sve_vl_valid((vcpu)->arch.sve_max_vl))) { \
+ if (WARN_ON(!sve_vl_valid((vcpu)->arch.fp_state.sve_vl))) { \
__size_ret = 0; \
} else { \
__vcpu_vq = vcpu_sve_max_vq(vcpu); \
@@ -379,6 +379,18 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
*/
vcpu->arch.fp_owner = FP_STATE_FREE;
+ /*
+ * Initial setup for FP state for sharing with host, if SVE is
+ * enabled additional configuration will be done.
+ *
+ * Currently we do not support SME guests so SVCR is always 0
+ * and we just need a variable to point to.
+ */
+ vcpu->arch.fp_state.st = &vcpu->arch.ctxt.fp_regs;
+ vcpu->arch.fp_state.fp_type = &vcpu->arch.fp_type;
+ vcpu->arch.fp_state.svcr = &vcpu->arch.svcr;
+ vcpu->arch.fp_state.to_save = FP_STATE_FPSIMD;
+
/* Set up the timer */
kvm_timer_vcpu_init(vcpu);
@@ -143,24 +143,7 @@ void kvm_arch_vcpu_ctxsync_fp(struct kvm_vcpu *vcpu)
WARN_ON_ONCE(!irqs_disabled());
if (vcpu->arch.fp_owner == FP_STATE_GUEST_OWNED) {
-
- /*
- * Currently we do not support SME guests so SVCR is
- * always 0 and we just need a variable to point to.
- */
- fp_state.st = &vcpu->arch.ctxt.fp_regs;
- fp_state.sve_state = vcpu->arch.sve_state;
- fp_state.sve_vl = vcpu->arch.sve_max_vl;
- fp_state.sme_state = NULL;
- fp_state.svcr = &vcpu->arch.svcr;
- fp_state.fp_type = &vcpu->arch.fp_type;
-
- if (vcpu_has_sve(vcpu))
- fp_state.to_save = FP_STATE_SVE;
- else
- fp_state.to_save = FP_STATE_FPSIMD;
-
- fpsimd_bind_state_to_cpu(&fp_state);
+ fpsimd_bind_state_to_cpu(&vcpu->arch.fp_state);
clear_thread_flag(TIF_FOREIGN_FPSTATE);
}
@@ -317,7 +317,7 @@ static int get_sve_vls(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
if (!vcpu_has_sve(vcpu))
return -ENOENT;
- if (WARN_ON(!sve_vl_valid(vcpu->arch.sve_max_vl)))
+ if (WARN_ON(!sve_vl_valid(vcpu->arch.fp_state.sve_vl)))
return -EINVAL;
memset(vqs, 0, sizeof(vqs));
@@ -344,7 +344,7 @@ static int set_sve_vls(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
if (kvm_arm_vcpu_sve_finalized(vcpu))
return -EPERM; /* too late! */
- if (WARN_ON(vcpu->arch.sve_state))
+ if (WARN_ON(vcpu->arch.fp_state.sve_state))
return -EINVAL;
if (copy_from_user(vqs, (const void __user *)reg->addr, sizeof(vqs)))
@@ -373,8 +373,11 @@ static int set_sve_vls(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
if (max_vq < SVE_VQ_MIN)
return -EINVAL;
- /* vcpu->arch.sve_state will be alloc'd by kvm_vcpu_finalize_sve() */
- vcpu->arch.sve_max_vl = sve_vl_from_vq(max_vq);
+ /*
+ * vcpu->arch.fp_state.sve_state will be alloc'd by
+ * kvm_vcpu_finalize_sve().
+ */
+ vcpu->arch.fp_state.sve_vl = sve_vl_from_vq(max_vq);
return 0;
}
@@ -403,7 +406,10 @@ static int set_sve_vls(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
*/
#define vcpu_sve_slices(vcpu) 1
-/* Bounds of a single SVE register slice within vcpu->arch.sve_state */
+/*
+ * Bounds of a single SVE register slice within
+ * vcpu->arch.fp_state.sve_state
+ */
struct sve_state_reg_region {
unsigned int koffset; /* offset into sve_state in kernel memory */
unsigned int klen; /* length in kernel memory */
@@ -499,7 +505,7 @@ static int get_sve_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
if (!kvm_arm_vcpu_sve_finalized(vcpu))
return -EPERM;
- if (copy_to_user(uptr, vcpu->arch.sve_state + region.koffset,
+ if (copy_to_user(uptr, vcpu->arch.fp_state.sve_state + region.koffset,
region.klen) ||
clear_user(uptr + region.klen, region.upad))
return -EFAULT;
@@ -525,7 +531,8 @@ static int set_sve_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
if (!kvm_arm_vcpu_sve_finalized(vcpu))
return -EPERM;
- if (copy_from_user(vcpu->arch.sve_state + region.koffset, uptr,
+ if (copy_from_user(vcpu->arch.fp_state.sve_state + region.koffset,
+ uptr,
region.klen))
return -EFAULT;
@@ -29,8 +29,9 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
hyp_vcpu->vcpu.arch.ctxt = host_vcpu->arch.ctxt;
- hyp_vcpu->vcpu.arch.sve_state = kern_hyp_va(host_vcpu->arch.sve_state);
- hyp_vcpu->vcpu.arch.sve_max_vl = host_vcpu->arch.sve_max_vl;
+ hyp_vcpu->vcpu.arch.fp_state.sve_state
+ = kern_hyp_va(host_vcpu->arch.fp_state.sve_state);
+ hyp_vcpu->vcpu.arch.fp_state.sve_vl = host_vcpu->arch.fp_state.sve_vl;
hyp_vcpu->vcpu.arch.hw_mmu = host_vcpu->arch.hw_mmu;
@@ -75,7 +75,7 @@ int __init kvm_arm_init_sve(void)
static void kvm_vcpu_enable_sve(struct kvm_vcpu *vcpu)
{
- vcpu->arch.sve_max_vl = kvm_sve_max_vl;
+ vcpu->arch.fp_state.sve_vl = kvm_sve_max_vl;
/*
* Userspace can still customize the vector lengths by writing
@@ -87,7 +87,7 @@ static void kvm_vcpu_enable_sve(struct kvm_vcpu *vcpu)
/*
* Finalize vcpu's maximum SVE vector length, allocating
- * vcpu->arch.sve_state as necessary.
+ * vcpu->arch.fp_state.sve_state as necessary.
*/
static int kvm_vcpu_finalize_sve(struct kvm_vcpu *vcpu)
{
@@ -96,7 +96,7 @@ static int kvm_vcpu_finalize_sve(struct kvm_vcpu *vcpu)
size_t reg_sz;
int ret;
- vl = vcpu->arch.sve_max_vl;
+ vl = vcpu->arch.fp_state.sve_vl;
/*
* Responsibility for these properties is shared between
@@ -118,7 +118,8 @@ static int kvm_vcpu_finalize_sve(struct kvm_vcpu *vcpu)
return ret;
}
- vcpu->arch.sve_state = buf;
+ vcpu->arch.fp_state.sve_state = buf;
+ vcpu->arch.fp_state.to_save = FP_STATE_SVE;
vcpu_set_flag(vcpu, VCPU_SVE_FINALIZED);
return 0;
}
@@ -149,7 +150,7 @@ bool kvm_arm_vcpu_is_finalized(struct kvm_vcpu *vcpu)
void kvm_arm_vcpu_destroy(struct kvm_vcpu *vcpu)
{
- void *sve_state = vcpu->arch.sve_state;
+ void *sve_state = vcpu->arch.fp_state.sve_state;
kvm_vcpu_unshare_task_fp(vcpu);
kvm_unshare_hyp(vcpu, vcpu + 1);
@@ -162,7 +163,8 @@ void kvm_arm_vcpu_destroy(struct kvm_vcpu *vcpu)
static void kvm_vcpu_reset_sve(struct kvm_vcpu *vcpu)
{
if (vcpu_has_sve(vcpu))
- memset(vcpu->arch.sve_state, 0, vcpu_sve_state_size(vcpu));
+ memset(vcpu->arch.fp_state.sve_state, 0,
+ vcpu_sve_state_size(vcpu));
}
static void kvm_vcpu_enable_ptrauth(struct kvm_vcpu *vcpu)