Message ID | 20220930140150.37463-6-imbrenda@linux.ibm.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | KVM: s390: pv: implement lazy destroy for reboot | expand |
On 9/30/22 16:01, Claudio Imbrenda wrote: > Add support for the Destroy Secure Configuration Fast Ultravisor call, > and take advantage of it for asynchronous destroy. > > When supported, the protected guest is destroyed immediately using the > new UVC, leaving only the memory to be cleaned up asynchronously. > > Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com> > Reviewed-by: Nico Boehr <nrb@linux.ibm.com> > Reviewed-by: Janosch Frank <frankja@linux.ibm.com> LGTM Reviewed-by: Steffen Eiden <seiden@linux.ibm.com> > --- > arch/s390/include/asm/uv.h | 10 +++++++ > arch/s390/kvm/pv.c | 57 ++++++++++++++++++++++++++++++++------ > 2 files changed, 59 insertions(+), 8 deletions(-) > > diff --git a/arch/s390/include/asm/uv.h b/arch/s390/include/asm/uv.h > index be3ef9dd6972..28a9ad57b6f1 100644 > --- a/arch/s390/include/asm/uv.h > +++ b/arch/s390/include/asm/uv.h > @@ -34,6 +34,7 @@ > #define UVC_CMD_INIT_UV 0x000f > #define UVC_CMD_CREATE_SEC_CONF 0x0100 > #define UVC_CMD_DESTROY_SEC_CONF 0x0101 > +#define UVC_CMD_DESTROY_SEC_CONF_FAST 0x0102 > #define UVC_CMD_CREATE_SEC_CPU 0x0120 > #define UVC_CMD_DESTROY_SEC_CPU 0x0121 > #define UVC_CMD_CONV_TO_SEC_STOR 0x0200 > @@ -81,6 +82,7 @@ enum uv_cmds_inst { > BIT_UVC_CMD_UNSHARE_ALL = 20, > BIT_UVC_CMD_PIN_PAGE_SHARED = 21, > BIT_UVC_CMD_UNPIN_PAGE_SHARED = 22, > + BIT_UVC_CMD_DESTROY_SEC_CONF_FAST = 23, > BIT_UVC_CMD_DUMP_INIT = 24, > BIT_UVC_CMD_DUMP_CONFIG_STOR_STATE = 25, > BIT_UVC_CMD_DUMP_CPU = 26, > @@ -230,6 +232,14 @@ struct uv_cb_nodata { > u64 reserved20[4]; > } __packed __aligned(8); > > +/* Destroy Configuration Fast */ > +struct uv_cb_destroy_fast { > + struct uv_cb_header header; > + u64 reserved08[2]; > + u64 handle; > + u64 reserved20[5]; > +} __packed __aligned(8); > + > /* Set Shared Access */ > struct uv_cb_share { > struct uv_cb_header header; > diff --git a/arch/s390/kvm/pv.c b/arch/s390/kvm/pv.c > index 335eeec6e3da..1b51e5c3f7df 100644 > --- a/arch/s390/kvm/pv.c > +++ b/arch/s390/kvm/pv.c > @@ -201,6 +201,9 @@ static int kvm_s390_pv_dispose_one_leftover(struct kvm *kvm, > { > int cc; > > + /* It used the destroy-fast UVC, nothing left to do here */ > + if (!leftover->handle) > + goto done_fast; > cc = uv_cmd_nodata(leftover->handle, UVC_CMD_DESTROY_SEC_CONF, rc, rrc); > KVM_UV_EVENT(kvm, 3, "PROTVIRT DESTROY LEFTOVER VM: rc %x rrc %x", *rc, *rrc); > WARN_ONCE(cc, "protvirt destroy leftover vm failed rc %x rrc %x", *rc, *rrc); > @@ -215,6 +218,7 @@ static int kvm_s390_pv_dispose_one_leftover(struct kvm *kvm, > free_pages(leftover->stor_base, get_order(uv_info.guest_base_stor_len)); > free_pages(leftover->old_gmap_table, CRST_ALLOC_ORDER); > vfree(leftover->stor_var); > +done_fast: > atomic_dec(&kvm->mm->context.protected_count); > return 0; > } > @@ -248,6 +252,32 @@ static void kvm_s390_destroy_lower_2g(struct kvm *kvm) > srcu_read_unlock(&kvm->srcu, srcu_idx); > } > > +static int kvm_s390_pv_deinit_vm_fast(struct kvm *kvm, u16 *rc, u16 *rrc) > +{ > + struct uv_cb_destroy_fast uvcb = { > + .header.cmd = UVC_CMD_DESTROY_SEC_CONF_FAST, > + .header.len = sizeof(uvcb), > + .handle = kvm_s390_pv_get_handle(kvm), > + }; > + int cc; > + > + cc = uv_call_sched(0, (u64)&uvcb); > + *rc = uvcb.header.rc; > + *rrc = uvcb.header.rrc; > + WRITE_ONCE(kvm->arch.gmap->guest_handle, 0); > + KVM_UV_EVENT(kvm, 3, "PROTVIRT DESTROY VM FAST: rc %x rrc %x", *rc, *rrc); > + WARN_ONCE(cc, "protvirt destroy vm fast failed rc %x rrc %x", *rc, *rrc); > + /* Inteded memory leak on "impossible" error */ > + if (!cc) > + kvm_s390_pv_dealloc_vm(kvm); > + return cc ? -EIO : 0; > +} > + > +static inline bool is_destroy_fast_available(void) > +{ > + return test_bit_inv(BIT_UVC_CMD_DESTROY_SEC_CONF_FAST, uv_info.inst_calls_list); > +} > + > /** > * kvm_s390_pv_set_aside - Set aside a protected VM for later teardown. > * @kvm: the VM > @@ -269,6 +299,7 @@ static void kvm_s390_destroy_lower_2g(struct kvm *kvm) > int kvm_s390_pv_set_aside(struct kvm *kvm, u16 *rc, u16 *rrc) > { > struct pv_vm_to_be_destroyed *priv; > + int res = 0; > > /* > * If another protected VM was already prepared for teardown, refuse. > @@ -280,14 +311,21 @@ int kvm_s390_pv_set_aside(struct kvm *kvm, u16 *rc, u16 *rrc) > if (!priv) > return -ENOMEM; > > - priv->stor_var = kvm->arch.pv.stor_var; > - priv->stor_base = kvm->arch.pv.stor_base; > - priv->handle = kvm_s390_pv_get_handle(kvm); > - priv->old_gmap_table = (unsigned long)kvm->arch.gmap->table; > - WRITE_ONCE(kvm->arch.gmap->guest_handle, 0); > - if (s390_replace_asce(kvm->arch.gmap)) { > + if (is_destroy_fast_available()) { > + res = kvm_s390_pv_deinit_vm_fast(kvm, rc, rrc); > + } else { > + priv->stor_var = kvm->arch.pv.stor_var; > + priv->stor_base = kvm->arch.pv.stor_base; > + priv->handle = kvm_s390_pv_get_handle(kvm); > + priv->old_gmap_table = (unsigned long)kvm->arch.gmap->table; > + WRITE_ONCE(kvm->arch.gmap->guest_handle, 0); > + if (s390_replace_asce(kvm->arch.gmap)) > + res = -ENOMEM; > + } > + > + if (res) { > kfree(priv); > - return -ENOMEM; > + return res; > } > > kvm_s390_destroy_lower_2g(kvm); > @@ -464,6 +502,7 @@ static void kvm_s390_pv_mmu_notifier_release(struct mmu_notifier *subscription, > { > struct kvm *kvm = container_of(subscription, struct kvm, arch.pv.mmu_notifier); > u16 dummy; > + int r; > > /* > * No locking is needed since this is the last thread of the last user of this > @@ -472,7 +511,9 @@ static void kvm_s390_pv_mmu_notifier_release(struct mmu_notifier *subscription, > * unregistered. This means that if this notifier runs, then the > * struct kvm is still valid. > */ > - kvm_s390_cpus_from_pv(kvm, &dummy, &dummy); > + r = kvm_s390_cpus_from_pv(kvm, &dummy, &dummy); > + if (!r && is_destroy_fast_available()) > + kvm_s390_pv_deinit_vm_fast(kvm, &dummy, &dummy); > } > > static const struct mmu_notifier_ops kvm_s390_pv_mmu_notifier_ops = {
diff --git a/arch/s390/include/asm/uv.h b/arch/s390/include/asm/uv.h index be3ef9dd6972..28a9ad57b6f1 100644 --- a/arch/s390/include/asm/uv.h +++ b/arch/s390/include/asm/uv.h @@ -34,6 +34,7 @@ #define UVC_CMD_INIT_UV 0x000f #define UVC_CMD_CREATE_SEC_CONF 0x0100 #define UVC_CMD_DESTROY_SEC_CONF 0x0101 +#define UVC_CMD_DESTROY_SEC_CONF_FAST 0x0102 #define UVC_CMD_CREATE_SEC_CPU 0x0120 #define UVC_CMD_DESTROY_SEC_CPU 0x0121 #define UVC_CMD_CONV_TO_SEC_STOR 0x0200 @@ -81,6 +82,7 @@ enum uv_cmds_inst { BIT_UVC_CMD_UNSHARE_ALL = 20, BIT_UVC_CMD_PIN_PAGE_SHARED = 21, BIT_UVC_CMD_UNPIN_PAGE_SHARED = 22, + BIT_UVC_CMD_DESTROY_SEC_CONF_FAST = 23, BIT_UVC_CMD_DUMP_INIT = 24, BIT_UVC_CMD_DUMP_CONFIG_STOR_STATE = 25, BIT_UVC_CMD_DUMP_CPU = 26, @@ -230,6 +232,14 @@ struct uv_cb_nodata { u64 reserved20[4]; } __packed __aligned(8); +/* Destroy Configuration Fast */ +struct uv_cb_destroy_fast { + struct uv_cb_header header; + u64 reserved08[2]; + u64 handle; + u64 reserved20[5]; +} __packed __aligned(8); + /* Set Shared Access */ struct uv_cb_share { struct uv_cb_header header; diff --git a/arch/s390/kvm/pv.c b/arch/s390/kvm/pv.c index 335eeec6e3da..1b51e5c3f7df 100644 --- a/arch/s390/kvm/pv.c +++ b/arch/s390/kvm/pv.c @@ -201,6 +201,9 @@ static int kvm_s390_pv_dispose_one_leftover(struct kvm *kvm, { int cc; + /* It used the destroy-fast UVC, nothing left to do here */ + if (!leftover->handle) + goto done_fast; cc = uv_cmd_nodata(leftover->handle, UVC_CMD_DESTROY_SEC_CONF, rc, rrc); KVM_UV_EVENT(kvm, 3, "PROTVIRT DESTROY LEFTOVER VM: rc %x rrc %x", *rc, *rrc); WARN_ONCE(cc, "protvirt destroy leftover vm failed rc %x rrc %x", *rc, *rrc); @@ -215,6 +218,7 @@ static int kvm_s390_pv_dispose_one_leftover(struct kvm *kvm, free_pages(leftover->stor_base, get_order(uv_info.guest_base_stor_len)); free_pages(leftover->old_gmap_table, CRST_ALLOC_ORDER); vfree(leftover->stor_var); +done_fast: atomic_dec(&kvm->mm->context.protected_count); return 0; } @@ -248,6 +252,32 @@ static void kvm_s390_destroy_lower_2g(struct kvm *kvm) srcu_read_unlock(&kvm->srcu, srcu_idx); } +static int kvm_s390_pv_deinit_vm_fast(struct kvm *kvm, u16 *rc, u16 *rrc) +{ + struct uv_cb_destroy_fast uvcb = { + .header.cmd = UVC_CMD_DESTROY_SEC_CONF_FAST, + .header.len = sizeof(uvcb), + .handle = kvm_s390_pv_get_handle(kvm), + }; + int cc; + + cc = uv_call_sched(0, (u64)&uvcb); + *rc = uvcb.header.rc; + *rrc = uvcb.header.rrc; + WRITE_ONCE(kvm->arch.gmap->guest_handle, 0); + KVM_UV_EVENT(kvm, 3, "PROTVIRT DESTROY VM FAST: rc %x rrc %x", *rc, *rrc); + WARN_ONCE(cc, "protvirt destroy vm fast failed rc %x rrc %x", *rc, *rrc); + /* Inteded memory leak on "impossible" error */ + if (!cc) + kvm_s390_pv_dealloc_vm(kvm); + return cc ? -EIO : 0; +} + +static inline bool is_destroy_fast_available(void) +{ + return test_bit_inv(BIT_UVC_CMD_DESTROY_SEC_CONF_FAST, uv_info.inst_calls_list); +} + /** * kvm_s390_pv_set_aside - Set aside a protected VM for later teardown. * @kvm: the VM @@ -269,6 +299,7 @@ static void kvm_s390_destroy_lower_2g(struct kvm *kvm) int kvm_s390_pv_set_aside(struct kvm *kvm, u16 *rc, u16 *rrc) { struct pv_vm_to_be_destroyed *priv; + int res = 0; /* * If another protected VM was already prepared for teardown, refuse. @@ -280,14 +311,21 @@ int kvm_s390_pv_set_aside(struct kvm *kvm, u16 *rc, u16 *rrc) if (!priv) return -ENOMEM; - priv->stor_var = kvm->arch.pv.stor_var; - priv->stor_base = kvm->arch.pv.stor_base; - priv->handle = kvm_s390_pv_get_handle(kvm); - priv->old_gmap_table = (unsigned long)kvm->arch.gmap->table; - WRITE_ONCE(kvm->arch.gmap->guest_handle, 0); - if (s390_replace_asce(kvm->arch.gmap)) { + if (is_destroy_fast_available()) { + res = kvm_s390_pv_deinit_vm_fast(kvm, rc, rrc); + } else { + priv->stor_var = kvm->arch.pv.stor_var; + priv->stor_base = kvm->arch.pv.stor_base; + priv->handle = kvm_s390_pv_get_handle(kvm); + priv->old_gmap_table = (unsigned long)kvm->arch.gmap->table; + WRITE_ONCE(kvm->arch.gmap->guest_handle, 0); + if (s390_replace_asce(kvm->arch.gmap)) + res = -ENOMEM; + } + + if (res) { kfree(priv); - return -ENOMEM; + return res; } kvm_s390_destroy_lower_2g(kvm); @@ -464,6 +502,7 @@ static void kvm_s390_pv_mmu_notifier_release(struct mmu_notifier *subscription, { struct kvm *kvm = container_of(subscription, struct kvm, arch.pv.mmu_notifier); u16 dummy; + int r; /* * No locking is needed since this is the last thread of the last user of this @@ -472,7 +511,9 @@ static void kvm_s390_pv_mmu_notifier_release(struct mmu_notifier *subscription, * unregistered. This means that if this notifier runs, then the * struct kvm is still valid. */ - kvm_s390_cpus_from_pv(kvm, &dummy, &dummy); + r = kvm_s390_cpus_from_pv(kvm, &dummy, &dummy); + if (!r && is_destroy_fast_available()) + kvm_s390_pv_deinit_vm_fast(kvm, &dummy, &dummy); } static const struct mmu_notifier_ops kvm_s390_pv_mmu_notifier_ops = {