* [PATCH v4 00/20] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation
@ 2026-08-12 13:52 Christoph Schlameuss
2026-08-12 13:52 ` [PATCH v4 01/20] KVM: s390: vsie: Add SCAO read and write helpers Christoph Schlameuss
` (19 more replies)
0 siblings, 20 replies; 41+ messages in thread
From: Christoph Schlameuss @ 2026-08-12 13:52 UTC (permalink / raw)
To: kvm, linux-s390
Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
Nico Boehr, Paolo Bonzini, Shuah Khan, Sven Schnelle,
Vasily Gorbik, Christoph Schlameuss
In the IBM Z machine generation z17 the s390x architecture does have a
VSIE Interpretation Extension Facility to improve guest-3
guest performance.
To exploit the machine support the guest-1 KVM needs to create and
maintain shadow structures pointing to the original state descriptions
and system control areas of currently running guest-3 configurations.
These pointers are followed by the machines firmware and modifications
of the original SCA for guest-3 (located in guest-2) are monitored and
handled by firmware. This results in fewer VSIE exits.
The SIGP orders EXTERNAL CALL (2) and SENSE RUNNING STATUS (21) can be
handled if requested by the setting the ECA_SIGPI respective ECB_SRSI
state description bits.
The series includes some changes to the non ssca path as well.
- original SCA pinning is lazily kept after exiting vsie
- addr_to_page radix_tree usage is replaced by xarray
Due to no ESCA support in old g2 kernel and qemu the patches do
introduce a limitation for setups with a unaware g2 when g1 KVM has
ssca=1 the g2 kernel will report KVM_CAP_MAX_VCPUS=64.
---
Changes in v4:
- Sorry for the spam!
- Same code as v2 / v3, rebased to v7.2-rc7 so that sashiko can
definitely apply it and provide feedback
- Removed dependency on change-id: 20260716-vsie-cleanup-202607-8690ba7ac3ea:v1
- Link to v3: https://patch.msgid.link/20260811-vsie-sigpi-v3-0-45aa1ab07c7c@linux.ibm.com
Changes in v3:
- Same code as v2, just rebased to kvms390/next so that sashiko can
apply it and provide feedback
- Link to v2: https://patch.msgid.link/20260810-vsie-sigpi-v2-0-e8d59a2f2f70@linux.ibm.com
Changes in v2:
Changes requested by Janosch (Thanks!)
- First 3 commits split out to its own vsie-cleanup series
- Re-split main code into commits a bit differently and hopefully easier
- Renamed sclp bit to has_vsie_interp_extf
- Renamed module parameter to ssca
- Renamed arch struct variable to ssca
- Add head and tail struct groups in struct vsie_sca
- Make writes to ssca atomic
- Add patch introducing SCA_ALIGNMENT_SHIFT
- Force vsie_sca->ssca offset to 0 instead of complex assertion
- Do not error out on scb_addr 0 (architecture compliant)
- move VSIE_PAGE_PINNED const
- decrease sca_o_nr_pages variable size
- Fixed Sashiko findings
- allow 4 page esca if properly aligned
- put sca on error in get_vsie_sca()
- force reshadow on _shadow_sca() fail
- unpin when destroy vsie_page
- fix locking in sca change cases
- fix writeback of SCAO in unpin_blocks() if g3 does not use 64bscao
- fix err goto from shadow_sca()
- set validity on shadow sca from pin_blocks()
- remove vsie_page mention from comment
- fix get_sca_entry_addr() for sca pages 2-5
- fixed commit message, fix double read conditions for configuration values
- use xarray insert instead of store and change alloc flags to *_ACCOUNT
- fix double read conditions for max_sc* configuration values
- empty xarrays on destroy
- correct stat counter increments
- Link to v1: https://lore.kernel.org/r/20260709-vsie-sigpi-v1-0-ea9f12066408@linux.ibm.com
Changes from RFC:
- fix locking and vsie_page reuse to run stable
- pull some methods out of the main patch to simplify the review
- rework kvm_s390_handle_vsie() to allocate vsie_sca first to store
vsie_page references directly
- move sie_uses_esca() to patch 1 as it is used there as well - thanks
Eric
- change pointers in {read,write}_scao() to generic 'unsigned long' to
not imply gpa or hpa as it can in fact be either depending on the
situation - thanks Claudio
- simplify code in sca_mcn_equals() using memcmp() - thanks Janosch
- copy mcn into vsie_sca using memcpy() instead of loop
- only shadow sca if the g3 wants and can to use sigpif - thanks Janosch
- change vsie_sca->ssca from pointer to embedded struct to as vsie_sca
is only needed when we actually also want the ssca
- fix kvm_s390_handle_vsie() cleanup order
- rename sca variables to vsie_sca
- cleanup error paths
- adjust some comments
Cc: Alexander Gordeev <agordeev@linux.ibm.com>
Cc: Christian Borntraeger <borntraeger@linux.ibm.com>
Cc: Claudio Imbrenda <imbrenda@linux.ibm.com>
Cc: David Hildenbrand <david@kernel.org>
Cc: Eric Farman <farman@linux.ibm.com>
Cc: Heiko Carstens <hca@linux.ibm.com>
Cc: Janosch Frank <frankja@linux.ibm.com>
Cc: Nico Boehr <nrb@linux.ibm.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Sven Schnelle <svens@linux.ibm.com>
Cc: Vasily Gorbik <gor@linux.ibm.com>
To: kvm@vger.kernel.org
To: linux-s390@vger.kernel.org
---
Christoph Schlameuss (20):
KVM: s390: vsie: Add SCAO read and write helpers
KVM: s390: vsie: Move SCAO validation into a function
KVM: s390: vsie: Add vsie_interp_extf detection
KVM: s390: vsie: Add ssca_block and ssca_entry structs
KVM: s390: vsie: Move pin/unpin_scb methods
KVM: s390: vsie: Move pin/unpin guest page
KVM: s390: vsie: Move release/acquire gmap shadow
KVM: s390: vsie: Create helpers to alloc and free vsie_pages
KVM: s390: vsie: Replace radix_tree with xarray addr_to_page
KVM: s390: vsie: Add helper to release gmap shadow
KVM: s390: vsie: Lazily keep original scb pinned after vsie exit
KVM: s390: vsie: Add helper to pin and unpin multiple guest pages
KVM: s390: vsie: Add struct vsie_sca with pin and unpin methods
KVM: s390: vsie: Shadow VSIE SCA in guest-1
KVM: s390: vsie: Guard against invalid CPU address
KVM: s390: vsie: Allow guest-3 cpu add and remove with ssca
KVM: s390: vsie: Add VSIE max shadow configuration
KVM: s390: vsie: Add VSIE shadow stat counters
KVM: s390: vsie: Create minimal scb shadows for not running g3 blocks
KVM: s390: vsie: Enable use of VSIE SSCA
arch/s390/include/asm/kvm_host.h | 28 +-
arch/s390/include/asm/kvm_host_types.h | 28 +-
arch/s390/include/asm/sclp.h | 1 +
arch/s390/kvm/kvm-s390.c | 15 +-
arch/s390/kvm/vsie.c | 1231 +++++++++++++++++++-----
drivers/s390/char/sclp_early.c | 1 +
tools/testing/selftests/kvm/include/s390/sie.h | 2 +-
7 files changed, 1055 insertions(+), 251 deletions(-)
---
base-commit: f5bbbfec59b4e2fb7520a91de3df8a6174325d6a
change-id: 20251113-vsie-sigpi-3de01243efd6
Best regards,
--
Christoph Schlameuss <schlameuss@linux.ibm.com>
^ permalink raw reply [flat|nested] 41+ messages in thread
* [PATCH v4 01/20] KVM: s390: vsie: Add SCAO read and write helpers
2026-08-12 13:52 [PATCH v4 00/20] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
@ 2026-08-12 13:52 ` Christoph Schlameuss
2026-08-12 13:58 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 02/20] KVM: s390: vsie: Move SCAO validation into a function Christoph Schlameuss
` (18 subsequent siblings)
19 siblings, 1 reply; 41+ messages in thread
From: Christoph Schlameuss @ 2026-08-12 13:52 UTC (permalink / raw)
To: kvm, linux-s390
Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
Nico Boehr, Paolo Bonzini, Shuah Khan, Sven Schnelle,
Vasily Gorbik, Christoph Schlameuss
Introduce some small helper functions to get and set the system control
area origin address from the SIE control block.
Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
---
arch/s390/kvm/vsie.c | 26 +++++++++++++++++++-------
1 file changed, 19 insertions(+), 7 deletions(-)
diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c
index eea24562e7db..7bf3e921ca9a 100644
--- a/arch/s390/kvm/vsie.c
+++ b/arch/s390/kvm/vsie.c
@@ -72,6 +72,22 @@ struct vsie_page {
static_assert(sizeof(struct vsie_page) == PAGE_SIZE);
+static unsigned long read_scao(struct kvm *kvm, struct kvm_s390_sie_block *scb)
+{
+ unsigned long vsie_sca = READ_ONCE(scb->scaol) & ~0xfUL;
+
+ if (test_kvm_cpu_feat(kvm, KVM_S390_VM_CPU_FEAT_64BSCAO))
+ vsie_sca |= (u64)READ_ONCE(scb->scaoh) << 32;
+
+ return vsie_sca;
+}
+
+static void write_scao(struct kvm_s390_sie_block *scb, unsigned long hpa)
+{
+ scb->scaoh = (u32)((u64)hpa >> 32);
+ scb->scaol = (u32)(u64)hpa;
+}
+
/* trigger a validity icpt for the given scb */
static int set_validity_icpt(struct kvm_s390_sie_block *scb,
__u16 reason_code)
@@ -716,8 +732,7 @@ static void unpin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
if (hpa) {
unpin_guest_page(vcpu->kvm, vsie_page->sca_gpa, hpa);
vsie_page->sca_gpa = 0;
- scb_s->scaol = 0;
- scb_s->scaoh = 0;
+ write_scao(scb_s, 0);
}
hpa = scb_s->itdba;
@@ -771,9 +786,7 @@ static int pin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
gpa_t gpa;
int rc = 0;
- gpa = READ_ONCE(scb_o->scaol) & ~0xfUL;
- if (test_kvm_cpu_feat(vcpu->kvm, KVM_S390_VM_CPU_FEAT_64BSCAO))
- gpa |= (u64) READ_ONCE(scb_o->scaoh) << 32;
+ gpa = read_scao(vcpu->kvm, scb_o);
if (gpa) {
if (gpa < 2 * PAGE_SIZE)
rc = set_validity_icpt(scb_s, 0x0038U);
@@ -790,8 +803,7 @@ static int pin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
if (rc)
goto unpin;
vsie_page->sca_gpa = gpa;
- scb_s->scaoh = (u32)((u64)hpa >> 32);
- scb_s->scaol = (u32)(u64)hpa;
+ write_scao(scb_s, hpa);
}
gpa = READ_ONCE(scb_o->itdba) & ~0xffUL;
--
2.55.0
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v4 02/20] KVM: s390: vsie: Move SCAO validation into a function
2026-08-12 13:52 [PATCH v4 00/20] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
2026-08-12 13:52 ` [PATCH v4 01/20] KVM: s390: vsie: Add SCAO read and write helpers Christoph Schlameuss
@ 2026-08-12 13:52 ` Christoph Schlameuss
2026-08-12 15:13 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 03/20] KVM: s390: vsie: Add vsie_interp_extf detection Christoph Schlameuss
` (17 subsequent siblings)
19 siblings, 1 reply; 41+ messages in thread
From: Christoph Schlameuss @ 2026-08-12 13:52 UTC (permalink / raw)
To: kvm, linux-s390
Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
Nico Boehr, Paolo Bonzini, Shuah Khan, Sven Schnelle,
Vasily Gorbik, Christoph Schlameuss
Improve readability as well as allow re-use in coming patches.
In particular add the logic to be able to check the validity of BSCA and
ESCA origin addresses.
Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
---
arch/s390/kvm/vsie.c | 42 ++++++++++++++++++++++++++++++------------
1 file changed, 30 insertions(+), 12 deletions(-)
diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c
index 7bf3e921ca9a..eea670530acb 100644
--- a/arch/s390/kvm/vsie.c
+++ b/arch/s390/kvm/vsie.c
@@ -72,6 +72,11 @@ struct vsie_page {
static_assert(sizeof(struct vsie_page) == PAGE_SIZE);
+static inline bool sie_uses_esca(struct kvm_s390_sie_block *scb)
+{
+ return (scb->ecb2 & ECB2_ESCA);
+}
+
static unsigned long read_scao(struct kvm *kvm, struct kvm_s390_sie_block *scb)
{
unsigned long vsie_sca = READ_ONCE(scb->scaol) & ~0xfUL;
@@ -98,6 +103,25 @@ static int set_validity_icpt(struct kvm_s390_sie_block *scb,
return 1;
}
+/* The sca header must not cross pages etc. */
+static int validate_scao(struct kvm_vcpu *vcpu, struct kvm_s390_sie_block *scb, gpa_t gpa)
+{
+ int offset;
+
+ if (gpa < 2 * PAGE_SIZE)
+ return set_validity_icpt(scb, 0x0038U);
+ if ((gpa & ~0x1fffUL) == kvm_s390_get_prefix(vcpu))
+ return set_validity_icpt(scb, 0x0011U);
+
+ if (sie_uses_esca(scb))
+ offset = offsetof(struct esca_block, cpu[0]) - 1;
+ else
+ offset = offsetof(struct bsca_block, cpu[0]) - 1;
+ if ((gpa & PAGE_MASK) != ((gpa + offset) & PAGE_MASK))
+ return set_validity_icpt(scb, 0x003bU);
+ return 0;
+}
+
/* mark the prefix as unmapped, this will block the VSIE */
static void prefix_unmapped(struct vsie_page *vsie_page)
{
@@ -788,20 +812,14 @@ static int pin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
gpa = read_scao(vcpu->kvm, scb_o);
if (gpa) {
- if (gpa < 2 * PAGE_SIZE)
- rc = set_validity_icpt(scb_s, 0x0038U);
- else if ((gpa & ~0x1fffUL) == kvm_s390_get_prefix(vcpu))
- rc = set_validity_icpt(scb_s, 0x0011U);
- else if ((gpa & PAGE_MASK) !=
- ((gpa + offsetof(struct bsca_block, cpu[0]) - 1) & PAGE_MASK))
- rc = set_validity_icpt(scb_s, 0x003bU);
- if (!rc) {
- rc = pin_guest_page(vcpu->kvm, gpa, &hpa);
- if (rc)
- rc = set_validity_icpt(scb_s, 0x0034U);
- }
+ rc = validate_scao(vcpu, scb_s, gpa);
if (rc)
goto unpin;
+ rc = pin_guest_page(vcpu->kvm, gpa, &hpa);
+ if (rc) {
+ rc = set_validity_icpt(scb_s, 0x0034U);
+ goto unpin;
+ }
vsie_page->sca_gpa = gpa;
write_scao(scb_s, hpa);
}
--
2.55.0
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v4 03/20] KVM: s390: vsie: Add vsie_interp_extf detection
2026-08-12 13:52 [PATCH v4 00/20] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
2026-08-12 13:52 ` [PATCH v4 01/20] KVM: s390: vsie: Add SCAO read and write helpers Christoph Schlameuss
2026-08-12 13:52 ` [PATCH v4 02/20] KVM: s390: vsie: Move SCAO validation into a function Christoph Schlameuss
@ 2026-08-12 13:52 ` Christoph Schlameuss
2026-08-12 13:58 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 04/20] KVM: s390: vsie: Add ssca_block and ssca_entry structs Christoph Schlameuss
` (16 subsequent siblings)
19 siblings, 1 reply; 41+ messages in thread
From: Christoph Schlameuss @ 2026-08-12 13:52 UTC (permalink / raw)
To: kvm, linux-s390
Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
Nico Boehr, Paolo Bonzini, Shuah Khan, Sven Schnelle,
Vasily Gorbik, Christoph Schlameuss
Add sensing of the VSIE Interpretation Extension Facility as
vsie_interp_extf from SCLP. This facility is introduced with IBM Z
gen17.
Along with the new facility add a KVM struct member use_ssca that we
enable when the vsie_interp_extf is available.
Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
---
arch/s390/include/asm/kvm_host.h | 1 +
arch/s390/include/asm/sclp.h | 1 +
arch/s390/kvm/kvm-s390.c | 1 +
drivers/s390/char/sclp_early.c | 1 +
4 files changed, 4 insertions(+)
diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h
index b4182ca4435f..d0b0dd9e4f10 100644
--- a/arch/s390/include/asm/kvm_host.h
+++ b/arch/s390/include/asm/kvm_host.h
@@ -678,6 +678,7 @@ struct kvm_arch {
int use_pfmfi;
int use_skf;
int use_zpci_interp;
+ int use_ssca;
int user_cpu_state_ctrl;
int user_sigp;
int user_stsi;
diff --git a/arch/s390/include/asm/sclp.h b/arch/s390/include/asm/sclp.h
index f2d490558054..d790a5995103 100644
--- a/arch/s390/include/asm/sclp.h
+++ b/arch/s390/include/asm/sclp.h
@@ -102,6 +102,7 @@ struct sclp_info {
unsigned char has_dirq : 1;
unsigned char has_iplcc : 1;
unsigned char has_zpci_lsi : 1;
+ unsigned char has_vsie_interp_extf : 1;
unsigned char has_aisii : 1;
unsigned char has_aeni : 1;
unsigned char has_aisi : 1;
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index 518a69c55e85..8bbd6e6290c0 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -3385,6 +3385,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
kvm->arch.use_pfmfi = sclp.has_pfmfi;
kvm->arch.use_skf = sclp.has_skey;
+ kvm->arch.use_ssca = sclp.has_vsie_interp_extf;
spin_lock_init(&kvm->arch.start_stop_lock);
kvm_s390_vsie_init(kvm);
if (use_gisa)
diff --git a/drivers/s390/char/sclp_early.c b/drivers/s390/char/sclp_early.c
index 22dd797e6229..40e6e346ee08 100644
--- a/drivers/s390/char/sclp_early.c
+++ b/drivers/s390/char/sclp_early.c
@@ -56,6 +56,7 @@ static void __init sclp_early_facilities_detect(void)
sclp.has_diag318 = !!(sccb->byte_134 & 0x80);
sclp.has_diag320 = !!(sccb->byte_134 & 0x04);
sclp.has_iplcc = !!(sccb->byte_134 & 0x02);
+ sclp.has_vsie_interp_extf = !!(sccb->byte_134 & 0x01);
}
if (sccb->cpuoff > 137) {
sclp.has_sipl = !!(sccb->cbl & 0x4000);
--
2.55.0
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v4 04/20] KVM: s390: vsie: Add ssca_block and ssca_entry structs
2026-08-12 13:52 [PATCH v4 00/20] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
` (2 preceding siblings ...)
2026-08-12 13:52 ` [PATCH v4 03/20] KVM: s390: vsie: Add vsie_interp_extf detection Christoph Schlameuss
@ 2026-08-12 13:52 ` Christoph Schlameuss
2026-08-12 13:57 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 05/20] KVM: s390: vsie: Move pin/unpin_scb methods Christoph Schlameuss
` (15 subsequent siblings)
19 siblings, 1 reply; 41+ messages in thread
From: Christoph Schlameuss @ 2026-08-12 13:52 UTC (permalink / raw)
To: kvm, linux-s390
Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
Nico Boehr, Paolo Bonzini, Shuah Khan, Sven Schnelle,
Vasily Gorbik, Christoph Schlameuss
Add the required guest-1 structures for the ssca to the SIE
control block for use in later patches.
The shadow SCA features the address of the original SCA as well as an
entry for each original SIGP entry. The entries contain the addresses of
the shadow state description and original SIGP entry.
Reviewed-by: Janosch Frank <frankja@linux.ibm.com>
Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
---
arch/s390/include/asm/kvm_host_types.h | 25 ++++++++++++++++++++++++-
tools/testing/selftests/kvm/include/s390/sie.h | 2 +-
2 files changed, 25 insertions(+), 2 deletions(-)
diff --git a/arch/s390/include/asm/kvm_host_types.h b/arch/s390/include/asm/kvm_host_types.h
index 3f50942bdfe6..6540146b4b00 100644
--- a/arch/s390/include/asm/kvm_host_types.h
+++ b/arch/s390/include/asm/kvm_host_types.h
@@ -6,6 +6,8 @@
#include <linux/atomic.h>
#include <linux/types.h>
+#define KVM_S390_MAX_VSIE_VCPUS 256
+
#define KVM_S390_BSCA_CPU_SLOTS 64
#define KVM_S390_ESCA_CPU_SLOTS 248
@@ -45,6 +47,13 @@ struct bsca_entry {
__u64 reserved2[2];
};
+struct ssca_entry {
+ __u64 reserved1;
+ __u64 ssda;
+ __u64 ossea;
+ __u64 reserved2;
+};
+
union ipte_control {
unsigned long val;
struct {
@@ -86,6 +95,20 @@ struct esca_block {
struct esca_entry cpu[KVM_S390_ESCA_CPU_SLOTS];
};
+/*
+ * The shadow sca / ssca needs to cover both bsca and esca depending on what the
+ * guest uses so we allocate space for 256 entries that are defined in the
+ * architecture.
+ * The header part of the struct must not cross page boundaries.
+ */
+struct ssca_block {
+ __u64 osca;
+ __u64 reserved08[7];
+ struct ssca_entry cpu[KVM_S390_MAX_VSIE_VCPUS];
+};
+
+static_assert(offsetof(struct ssca_block, cpu) == 64);
+
/*
* This struct is used to store some machine check info from lowcore
* for machine checks that happen while the guest is running.
@@ -315,7 +338,7 @@ struct kvm_s390_sie_block {
__u32 fac; /* 0x01a0 */
__u8 reserved1a4[20]; /* 0x01a4 */
__u64 cbrlo; /* 0x01b8 */
- __u8 reserved1c0[8]; /* 0x01c0 */
+ __u64 osda; /* 0x01c0 */
#define ECD_HOSTREGMGMT 0x20000000
#define ECD_MEF 0x08000000
#define ECD_ETOKENF 0x02000000
diff --git a/tools/testing/selftests/kvm/include/s390/sie.h b/tools/testing/selftests/kvm/include/s390/sie.h
index 160acd4a1db9..4ff1c1a354af 100644
--- a/tools/testing/selftests/kvm/include/s390/sie.h
+++ b/tools/testing/selftests/kvm/include/s390/sie.h
@@ -223,7 +223,7 @@ struct kvm_s390_sie_block {
__u32 fac; /* 0x01a0 */
__u8 reserved1a4[20]; /* 0x01a4 */
__u64 cbrlo; /* 0x01b8 */
- __u8 reserved1c0[8]; /* 0x01c0 */
+ __u64 osda; /* 0x01c0 */
#define ECD_HOSTREGMGMT 0x20000000
#define ECD_MEF 0x08000000
#define ECD_ETOKENF 0x02000000
--
2.55.0
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v4 05/20] KVM: s390: vsie: Move pin/unpin_scb methods
2026-08-12 13:52 [PATCH v4 00/20] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
` (3 preceding siblings ...)
2026-08-12 13:52 ` [PATCH v4 04/20] KVM: s390: vsie: Add ssca_block and ssca_entry structs Christoph Schlameuss
@ 2026-08-12 13:52 ` Christoph Schlameuss
2026-08-12 14:03 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 06/20] KVM: s390: vsie: Move pin/unpin guest page Christoph Schlameuss
` (14 subsequent siblings)
19 siblings, 1 reply; 41+ messages in thread
From: Christoph Schlameuss @ 2026-08-12 13:52 UTC (permalink / raw)
To: kvm, linux-s390
Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
Nico Boehr, Paolo Bonzini, Shuah Khan, Sven Schnelle,
Vasily Gorbik, Christoph Schlameuss
Move {,un}pin_scb() up in preparation for the next patches.
No change intended.
Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
---
arch/s390/kvm/vsie.c | 66 ++++++++++++++++++++++++++--------------------------
1 file changed, 33 insertions(+), 33 deletions(-)
diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c
index eea670530acb..4b67bde0535f 100644
--- a/arch/s390/kvm/vsie.c
+++ b/arch/s390/kvm/vsie.c
@@ -660,6 +660,39 @@ static int shadow_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
return rc;
}
+/* unpin the scb provided by guest 2, marking it as dirty */
+static void unpin_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,
+ gpa_t gpa)
+{
+ hpa_t hpa = virt_to_phys(vsie_page->scb_o);
+
+ if (hpa)
+ unpin_guest_page(vcpu->kvm, gpa, hpa);
+ vsie_page->scb_o = NULL;
+}
+
+/*
+ * Pin the scb at gpa provided by guest 2 at vsie_page->scb_o.
+ *
+ * Returns: - 0 if the scb was pinned.
+ * - > 0 if control has to be given to guest 2
+ */
+static int pin_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,
+ gpa_t gpa)
+{
+ hpa_t hpa;
+ int rc;
+
+ rc = pin_guest_page(vcpu->kvm, gpa, &hpa);
+ if (rc) {
+ rc = kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+ WARN_ON_ONCE(rc);
+ return 1;
+ }
+ vsie_page->scb_o = phys_to_virt(hpa);
+ return 0;
+}
+
void kvm_s390_vsie_gmap_notifier(struct gmap *gmap, gpa_t start, gpa_t end)
{
struct vsie_page *cur, *next;
@@ -910,39 +943,6 @@ static int pin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
return rc;
}
-/* unpin the scb provided by guest 2, marking it as dirty */
-static void unpin_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,
- gpa_t gpa)
-{
- hpa_t hpa = virt_to_phys(vsie_page->scb_o);
-
- if (hpa)
- unpin_guest_page(vcpu->kvm, gpa, hpa);
- vsie_page->scb_o = NULL;
-}
-
-/*
- * Pin the scb at gpa provided by guest 2 at vsie_page->scb_o.
- *
- * Returns: - 0 if the scb was pinned.
- * - > 0 if control has to be given to guest 2
- */
-static int pin_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,
- gpa_t gpa)
-{
- hpa_t hpa;
- int rc;
-
- rc = pin_guest_page(vcpu->kvm, gpa, &hpa);
- if (rc) {
- rc = kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
- WARN_ON_ONCE(rc);
- return 1;
- }
- vsie_page->scb_o = phys_to_virt(hpa);
- return 0;
-}
-
/*
* Inject a fault into guest 2.
*
--
2.55.0
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v4 06/20] KVM: s390: vsie: Move pin/unpin guest page
2026-08-12 13:52 [PATCH v4 00/20] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
` (4 preceding siblings ...)
2026-08-12 13:52 ` [PATCH v4 05/20] KVM: s390: vsie: Move pin/unpin_scb methods Christoph Schlameuss
@ 2026-08-12 13:52 ` Christoph Schlameuss
2026-08-12 14:08 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 07/20] KVM: s390: vsie: Move release/acquire gmap shadow Christoph Schlameuss
` (13 subsequent siblings)
19 siblings, 1 reply; 41+ messages in thread
From: Christoph Schlameuss @ 2026-08-12 13:52 UTC (permalink / raw)
To: kvm, linux-s390
Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
Nico Boehr, Paolo Bonzini, Shuah Khan, Sven Schnelle,
Vasily Gorbik, Christoph Schlameuss
Move {,un}pin_guest_page() up in preparation for the next patch.
No change intended.
Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
---
arch/s390/kvm/vsie.c | 52 ++++++++++++++++++++++++++--------------------------
1 file changed, 26 insertions(+), 26 deletions(-)
diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c
index 4b67bde0535f..1b4f6b1691a2 100644
--- a/arch/s390/kvm/vsie.c
+++ b/arch/s390/kvm/vsie.c
@@ -150,6 +150,32 @@ static int prefix_is_mapped(struct vsie_page *vsie_page)
return !(atomic_read(&vsie_page->scb_s.prog20) & PROG_REQUEST);
}
+/*
+ * Pin the guest page given by gpa and set hpa to the pinned host address.
+ * Will always be pinned writable.
+ *
+ * Returns: - 0 on success
+ * - -EINVAL if the gpa is not valid guest storage
+ */
+static int pin_guest_page(struct kvm *kvm, gpa_t gpa, hpa_t *hpa)
+{
+ struct page *page;
+
+ page = gfn_to_page(kvm, gpa_to_gfn(gpa));
+ if (!page)
+ return -EINVAL;
+ *hpa = (hpa_t)page_to_phys(page) + (gpa & ~PAGE_MASK);
+ return 0;
+}
+
+/* Unpins a page previously pinned via pin_guest_page, marking it as dirty. */
+static void unpin_guest_page(struct kvm *kvm, gpa_t gpa, hpa_t hpa)
+{
+ kvm_release_page_dirty(pfn_to_page(phys_to_pfn(hpa)));
+ /* mark the page always as dirty for migration */
+ mark_page_dirty(kvm, gpa_to_gfn(gpa));
+}
+
/* copy the updated intervention request bits into the shadow scb */
static void update_intervention_requests(struct vsie_page *vsie_page)
{
@@ -753,32 +779,6 @@ static int map_prefix(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page, struct
return rc;
}
-/*
- * Pin the guest page given by gpa and set hpa to the pinned host address.
- * Will always be pinned writable.
- *
- * Returns: - 0 on success
- * - -EINVAL if the gpa is not valid guest storage
- */
-static int pin_guest_page(struct kvm *kvm, gpa_t gpa, hpa_t *hpa)
-{
- struct page *page;
-
- page = gfn_to_page(kvm, gpa_to_gfn(gpa));
- if (!page)
- return -EINVAL;
- *hpa = (hpa_t)page_to_phys(page) + (gpa & ~PAGE_MASK);
- return 0;
-}
-
-/* Unpins a page previously pinned via pin_guest_page, marking it as dirty. */
-static void unpin_guest_page(struct kvm *kvm, gpa_t gpa, hpa_t hpa)
-{
- kvm_release_page_dirty(pfn_to_page(hpa >> PAGE_SHIFT));
- /* mark the page always as dirty for migration */
- mark_page_dirty(kvm, gpa_to_gfn(gpa));
-}
-
/* unpin all blocks previously pinned by pin_blocks(), marking them dirty */
static void unpin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
{
--
2.55.0
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v4 07/20] KVM: s390: vsie: Move release/acquire gmap shadow
2026-08-12 13:52 [PATCH v4 00/20] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
` (5 preceding siblings ...)
2026-08-12 13:52 ` [PATCH v4 06/20] KVM: s390: vsie: Move pin/unpin guest page Christoph Schlameuss
@ 2026-08-12 13:52 ` Christoph Schlameuss
2026-08-12 14:02 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 08/20] KVM: s390: vsie: Create helpers to alloc and free vsie_pages Christoph Schlameuss
` (12 subsequent siblings)
19 siblings, 1 reply; 41+ messages in thread
From: Christoph Schlameuss @ 2026-08-12 13:52 UTC (permalink / raw)
To: kvm, linux-s390
Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
Nico Boehr, Paolo Bonzini, Shuah Khan, Sven Schnelle,
Vasily Gorbik, Christoph Schlameuss
Move {release,acquire}_gmap_shadow() up in preparation for the next patch.
No change intended.
Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
---
arch/s390/kvm/vsie.c | 130 +++++++++++++++++++++++++--------------------------
1 file changed, 65 insertions(+), 65 deletions(-)
diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c
index 1b4f6b1691a2..c935a375f57c 100644
--- a/arch/s390/kvm/vsie.c
+++ b/arch/s390/kvm/vsie.c
@@ -150,6 +150,71 @@ static int prefix_is_mapped(struct vsie_page *vsie_page)
return !(atomic_read(&vsie_page->scb_s.prog20) & PROG_REQUEST);
}
+static void release_gmap_shadow(struct vsie_page *vsie_page)
+{
+ struct gmap *gmap = vsie_page->gmap_cache.gmap;
+
+ lockdep_assert_held(&gmap->kvm->arch.gmap->children_lock);
+
+ list_del(&vsie_page->gmap_cache.list);
+ vsie_page->gmap_cache.gmap = NULL;
+ prefix_unmapped(vsie_page);
+
+ if (list_empty(&gmap->scb_users)) {
+ gmap_remove_child(gmap);
+ gmap_put(gmap);
+ }
+}
+
+static struct gmap *acquire_gmap_shadow(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
+{
+ union ctlreg0 cr0;
+ struct gmap *gmap;
+ union asce asce;
+ int edat;
+
+ asce.val = vcpu->arch.sie_block->gcr[1];
+ cr0.val = vcpu->arch.sie_block->gcr[0];
+ edat = cr0.edat && test_kvm_facility(vcpu->kvm, 8);
+ edat += edat && test_kvm_facility(vcpu->kvm, 78);
+
+ scoped_guard(spinlock, &vcpu->kvm->arch.gmap->children_lock) {
+ gmap = vsie_page->gmap_cache.gmap;
+ if (gmap) {
+ /*
+ * ASCE or EDAT could have changed since last icpt, or the gmap
+ * we're holding has been unshadowed. If the gmap is still valid,
+ * we can safely reuse it.
+ */
+ if (gmap_is_shadow_valid(gmap, asce, edat)) {
+ vcpu->kvm->stat.gmap_shadow_reuse++;
+ gmap_get(gmap);
+ return gmap;
+ }
+ /* release the old shadow and mark the prefix as unmapped */
+ release_gmap_shadow(vsie_page);
+ }
+ }
+again:
+ gmap = gmap_create_shadow(vcpu->arch.mc, vcpu->kvm->arch.gmap, asce, edat);
+ if (IS_ERR(gmap))
+ return gmap;
+ scoped_guard(spinlock, &vcpu->kvm->arch.gmap->children_lock) {
+ /* unlikely race condition, remove the previous shadow */
+ if (vsie_page->gmap_cache.gmap)
+ release_gmap_shadow(vsie_page);
+ if (!gmap->parent) {
+ gmap_put(gmap);
+ goto again;
+ }
+ vcpu->kvm->stat.gmap_shadow_create++;
+ list_add(&vsie_page->gmap_cache.list, &gmap->scb_users);
+ vsie_page->gmap_cache.gmap = gmap;
+ prefix_unmapped(vsie_page);
+ }
+ return gmap;
+}
+
/*
* Pin the guest page given by gpa and set hpa to the pinned host address.
* Will always be pinned writable.
@@ -1311,71 +1376,6 @@ static int do_vsie_run(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page, struc
return rc;
}
-static void release_gmap_shadow(struct vsie_page *vsie_page)
-{
- struct gmap *gmap = vsie_page->gmap_cache.gmap;
-
- lockdep_assert_held(&gmap->kvm->arch.gmap->children_lock);
-
- list_del(&vsie_page->gmap_cache.list);
- vsie_page->gmap_cache.gmap = NULL;
- prefix_unmapped(vsie_page);
-
- if (list_empty(&gmap->scb_users)) {
- gmap_remove_child(gmap);
- gmap_put(gmap);
- }
-}
-
-static struct gmap *acquire_gmap_shadow(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
-{
- union ctlreg0 cr0;
- struct gmap *gmap;
- union asce asce;
- int edat;
-
- asce.val = vcpu->arch.sie_block->gcr[1];
- cr0.val = vcpu->arch.sie_block->gcr[0];
- edat = cr0.edat && test_kvm_facility(vcpu->kvm, 8);
- edat += edat && test_kvm_facility(vcpu->kvm, 78);
-
- scoped_guard(spinlock, &vcpu->kvm->arch.gmap->children_lock) {
- gmap = vsie_page->gmap_cache.gmap;
- if (gmap) {
- /*
- * ASCE or EDAT could have changed since last icpt, or the gmap
- * we're holding has been unshadowed. If the gmap is still valid,
- * we can safely reuse it.
- */
- if (gmap_is_shadow_valid(gmap, asce, edat)) {
- vcpu->kvm->stat.gmap_shadow_reuse++;
- gmap_get(gmap);
- return gmap;
- }
- /* release the old shadow and mark the prefix as unmapped */
- release_gmap_shadow(vsie_page);
- }
- }
-again:
- gmap = gmap_create_shadow(vcpu->arch.mc, vcpu->kvm->arch.gmap, asce, edat);
- if (IS_ERR(gmap))
- return gmap;
- scoped_guard(spinlock, &vcpu->kvm->arch.gmap->children_lock) {
- /* unlikely race condition, remove the previous shadow */
- if (vsie_page->gmap_cache.gmap)
- release_gmap_shadow(vsie_page);
- if (!gmap->parent) {
- gmap_put(gmap);
- goto again;
- }
- vcpu->kvm->stat.gmap_shadow_create++;
- list_add(&vsie_page->gmap_cache.list, &gmap->scb_users);
- vsie_page->gmap_cache.gmap = gmap;
- prefix_unmapped(vsie_page);
- }
- return gmap;
-}
-
/*
* Register the shadow scb at the VCPU, e.g. for kicking out of vsie.
*/
--
2.55.0
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v4 08/20] KVM: s390: vsie: Create helpers to alloc and free vsie_pages
2026-08-12 13:52 [PATCH v4 00/20] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
` (6 preceding siblings ...)
2026-08-12 13:52 ` [PATCH v4 07/20] KVM: s390: vsie: Move release/acquire gmap shadow Christoph Schlameuss
@ 2026-08-12 13:52 ` Christoph Schlameuss
2026-08-12 14:04 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 09/20] KVM: s390: vsie: Replace radix_tree with xarray addr_to_page Christoph Schlameuss
` (11 subsequent siblings)
19 siblings, 1 reply; 41+ messages in thread
From: Christoph Schlameuss @ 2026-08-12 13:52 UTC (permalink / raw)
To: kvm, linux-s390
Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
Nico Boehr, Paolo Bonzini, Shuah Khan, Sven Schnelle,
Vasily Gorbik, Christoph Schlameuss
Encapsulate allocation of struct vsie_page and free into its own methods
to allow this from multiple code paths.
Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
---
arch/s390/kvm/vsie.c | 26 ++++++++++++++++++++++----
1 file changed, 22 insertions(+), 4 deletions(-)
diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c
index c935a375f57c..805d28f0e705 100644
--- a/arch/s390/kvm/vsie.c
+++ b/arch/s390/kvm/vsie.c
@@ -1503,6 +1503,24 @@ static void put_vsie_page(struct vsie_page *vsie_page)
clear_bit(VSIE_PAGE_IN_USE, &vsie_page->flags);
}
+static void free_vsie_page(struct vsie_page *vsie_page)
+{
+ free_page((unsigned long)vsie_page);
+}
+
+static struct vsie_page *alloc_vsie_page(struct kvm *kvm)
+{
+ struct vsie_page *vsie_page;
+
+ vsie_page = (struct vsie_page *)__get_free_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO | GFP_DMA);
+ if (!vsie_page)
+ return vsie_page;
+
+ /* Mark it as invalid until it resides in the tree. */
+ vsie_page->scb_gpa = ULONG_MAX;
+ return vsie_page;
+}
+
/*
* Get or create a vsie page for a scb address.
*
@@ -1538,7 +1556,7 @@ static struct vsie_page *get_vsie_page(struct kvm *kvm, unsigned long addr)
mutex_lock(&kvm->arch.vsie.mutex);
if (kvm->arch.vsie.page_count < nr_vcpus) {
- vsie_page = (void *)__get_free_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO | GFP_DMA);
+ vsie_page = alloc_vsie_page(kvm);
if (!vsie_page) {
mutex_unlock(&kvm->arch.vsie.mutex);
return ERR_PTR(-ENOMEM);
@@ -1558,9 +1576,9 @@ static struct vsie_page *get_vsie_page(struct kvm *kvm, unsigned long addr)
if (vsie_page->scb_gpa != ULONG_MAX)
radix_tree_delete(&kvm->arch.vsie.addr_to_page,
vsie_page->scb_gpa >> 9);
+ /* Mark it as invalid until it resides in the tree. */
+ vsie_page->scb_gpa = ULONG_MAX;
}
- /* Mark it as invalid until it resides in the tree. */
- vsie_page->scb_gpa = ULONG_MAX;
/* Double use of the same address or allocation failure. */
if (radix_tree_insert(&kvm->arch.vsie.addr_to_page, addr >> 9, vsie_page)) {
@@ -1663,7 +1681,7 @@ void kvm_s390_vsie_destroy(struct kvm *kvm)
if (vsie_page->scb_gpa != ULONG_MAX)
radix_tree_delete(&kvm->arch.vsie.addr_to_page,
vsie_page->scb_gpa >> 9);
- free_page((unsigned long)vsie_page);
+ free_vsie_page(vsie_page);
}
kvm->arch.vsie.page_count = 0;
mutex_unlock(&kvm->arch.vsie.mutex);
--
2.55.0
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v4 09/20] KVM: s390: vsie: Replace radix_tree with xarray addr_to_page
2026-08-12 13:52 [PATCH v4 00/20] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
` (7 preceding siblings ...)
2026-08-12 13:52 ` [PATCH v4 08/20] KVM: s390: vsie: Create helpers to alloc and free vsie_pages Christoph Schlameuss
@ 2026-08-12 13:52 ` Christoph Schlameuss
2026-08-12 14:03 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 10/20] KVM: s390: vsie: Add helper to release gmap shadow Christoph Schlameuss
` (10 subsequent siblings)
19 siblings, 1 reply; 41+ messages in thread
From: Christoph Schlameuss @ 2026-08-12 13:52 UTC (permalink / raw)
To: kvm, linux-s390
Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
Nico Boehr, Paolo Bonzini, Shuah Khan, Sven Schnelle,
Vasily Gorbik, Christoph Schlameuss
The underlying implementation of radix_tree is xarray since v4.20.
Changing to the xarray API directly relaxes some of the locking
obligations and provides more symmetry with the next vsie changes in
the series.
Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
---
arch/s390/include/asm/kvm_host.h | 2 +-
arch/s390/kvm/vsie.c | 39 ++++++++++++++++-----------------------
2 files changed, 17 insertions(+), 24 deletions(-)
diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h
index d0b0dd9e4f10..2ae8fa51bf51 100644
--- a/arch/s390/include/asm/kvm_host.h
+++ b/arch/s390/include/asm/kvm_host.h
@@ -630,7 +630,7 @@ struct vsie_page;
struct kvm_s390_vsie {
struct mutex mutex;
- struct radix_tree_root addr_to_page;
+ struct xarray addr_to_page;
int page_count;
int next;
struct vsie_page *pages[KVM_MAX_VCPUS];
diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c
index 805d28f0e705..569054552b83 100644
--- a/arch/s390/kvm/vsie.c
+++ b/arch/s390/kvm/vsie.c
@@ -59,8 +59,7 @@ struct vsie_page {
gpa_t sdnx_gpa; /* 0x0250 */
/*
* guest address of the original SCB. Remains set for free vsie
- * pages, so we can properly look them up in our addr_to_page
- * radix tree.
+ * pages, so we can properly look them up in our addr_to_page map.
*/
gpa_t scb_gpa; /* 0x0258 */
/* the shadow gmap in use by the vsie_page */
@@ -1533,19 +1532,15 @@ static struct vsie_page *get_vsie_page(struct kvm *kvm, unsigned long addr)
struct vsie_page *vsie_page;
int nr_vcpus;
- rcu_read_lock();
- vsie_page = radix_tree_lookup(&kvm->arch.vsie.addr_to_page, addr >> 9);
- rcu_read_unlock();
- if (vsie_page) {
- if (try_get_vsie_page(vsie_page)) {
- if (vsie_page->scb_gpa == addr)
- return vsie_page;
- /*
- * We raced with someone reusing + putting this vsie
- * page before we grabbed it.
- */
- put_vsie_page(vsie_page);
- }
+ vsie_page = xa_load(&kvm->arch.vsie.addr_to_page, addr >> 9);
+ if (vsie_page && try_get_vsie_page(vsie_page)) {
+ if (vsie_page->scb_gpa == addr)
+ return vsie_page;
+ /*
+ * We raced with someone reusing + putting this vsie
+ * page before we grabbed it.
+ */
+ put_vsie_page(vsie_page);
}
/*
@@ -1574,14 +1569,15 @@ static struct vsie_page *get_vsie_page(struct kvm *kvm, unsigned long addr)
kvm->arch.vsie.next %= nr_vcpus;
}
if (vsie_page->scb_gpa != ULONG_MAX)
- radix_tree_delete(&kvm->arch.vsie.addr_to_page,
- vsie_page->scb_gpa >> 9);
+ xa_erase(&kvm->arch.vsie.addr_to_page,
+ vsie_page->scb_gpa >> 9);
/* Mark it as invalid until it resides in the tree. */
vsie_page->scb_gpa = ULONG_MAX;
}
/* Double use of the same address or allocation failure. */
- if (radix_tree_insert(&kvm->arch.vsie.addr_to_page, addr >> 9, vsie_page)) {
+ if (xa_insert(&kvm->arch.vsie.addr_to_page, addr >> 9, vsie_page,
+ GFP_KERNEL_ACCOUNT)) {
put_vsie_page(vsie_page);
mutex_unlock(&kvm->arch.vsie.mutex);
return NULL;
@@ -1661,7 +1657,7 @@ int kvm_s390_handle_vsie(struct kvm_vcpu *vcpu)
void kvm_s390_vsie_init(struct kvm *kvm)
{
mutex_init(&kvm->arch.vsie.mutex);
- INIT_RADIX_TREE(&kvm->arch.vsie.addr_to_page, GFP_KERNEL_ACCOUNT);
+ xa_init_flags(&kvm->arch.vsie.addr_to_page, XA_FLAGS_ACCOUNT);
}
/* Destroy the vsie data structures. To be called when a vm is destroyed. */
@@ -1677,12 +1673,9 @@ void kvm_s390_vsie_destroy(struct kvm *kvm)
if (vsie_page->gmap_cache.gmap)
release_gmap_shadow(vsie_page);
kvm->arch.vsie.pages[i] = NULL;
- /* free the radix tree entry */
- if (vsie_page->scb_gpa != ULONG_MAX)
- radix_tree_delete(&kvm->arch.vsie.addr_to_page,
- vsie_page->scb_gpa >> 9);
free_vsie_page(vsie_page);
}
+ xa_destroy(&kvm->arch.vsie.addr_to_page);
kvm->arch.vsie.page_count = 0;
mutex_unlock(&kvm->arch.vsie.mutex);
}
--
2.55.0
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v4 10/20] KVM: s390: vsie: Add helper to release gmap shadow
2026-08-12 13:52 [PATCH v4 00/20] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
` (8 preceding siblings ...)
2026-08-12 13:52 ` [PATCH v4 09/20] KVM: s390: vsie: Replace radix_tree with xarray addr_to_page Christoph Schlameuss
@ 2026-08-12 13:52 ` Christoph Schlameuss
2026-08-12 14:04 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 11/20] KVM: s390: vsie: Lazily keep original scb pinned after vsie exit Christoph Schlameuss
` (9 subsequent siblings)
19 siblings, 1 reply; 41+ messages in thread
From: Christoph Schlameuss @ 2026-08-12 13:52 UTC (permalink / raw)
To: kvm, linux-s390
Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
Nico Boehr, Paolo Bonzini, Shuah Khan, Sven Schnelle,
Vasily Gorbik, Christoph Schlameuss
Create a helper method release_gmap_shadow_safe() to simplify releasing
the gmap shadow of a vsie_page.
Will be called from multiple places in upcoming patches.
Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
---
arch/s390/kvm/vsie.c | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c
index 569054552b83..d2582519b3ec 100644
--- a/arch/s390/kvm/vsie.c
+++ b/arch/s390/kvm/vsie.c
@@ -165,6 +165,15 @@ static void release_gmap_shadow(struct vsie_page *vsie_page)
}
}
+static void release_gmap_shadow_safe(struct kvm *kvm, struct vsie_page *vsie_page)
+{
+ if (vsie_page->gmap_cache.gmap) {
+ guard(spinlock)(&kvm->arch.gmap->children_lock);
+ if (vsie_page->gmap_cache.gmap)
+ release_gmap_shadow(vsie_page);
+ }
+}
+
static struct gmap *acquire_gmap_shadow(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
{
union ctlreg0 cr0;
@@ -1586,11 +1595,7 @@ static struct vsie_page *get_vsie_page(struct kvm *kvm, unsigned long addr)
mutex_unlock(&kvm->arch.vsie.mutex);
memset(&vsie_page->scb_s, 0, sizeof(struct kvm_s390_sie_block));
- if (vsie_page->gmap_cache.gmap) {
- scoped_guard(spinlock, &kvm->arch.gmap->children_lock)
- if (vsie_page->gmap_cache.gmap)
- release_gmap_shadow(vsie_page);
- }
+ release_gmap_shadow_safe(kvm, vsie_page);
prefix_unmapped(vsie_page);
vsie_page->fault_addr = 0;
vsie_page->scb_s.ihcpu = 0xffffU;
--
2.55.0
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v4 11/20] KVM: s390: vsie: Lazily keep original scb pinned after vsie exit
2026-08-12 13:52 [PATCH v4 00/20] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
` (9 preceding siblings ...)
2026-08-12 13:52 ` [PATCH v4 10/20] KVM: s390: vsie: Add helper to release gmap shadow Christoph Schlameuss
@ 2026-08-12 13:52 ` Christoph Schlameuss
2026-08-12 15:28 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 12/20] KVM: s390: vsie: Add helper to pin and unpin multiple guest pages Christoph Schlameuss
` (8 subsequent siblings)
19 siblings, 1 reply; 41+ messages in thread
From: Christoph Schlameuss @ 2026-08-12 13:52 UTC (permalink / raw)
To: kvm, linux-s390
Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
Nico Boehr, Paolo Bonzini, Shuah Khan, Sven Schnelle,
Vasily Gorbik, Christoph Schlameuss
Keep the original SIE control block (SCB) pinned and only lazily unpin
it on reuse of the vsie_page for a different SCB.
- Pinned pages are tracked and will be unpinned on VM destruction
- Memory pressure is not significantly impacted as the number of
pinned SCBs is bounded by the number of vCPUs
- Reuse detection ensures stale pins are released when needed
{,un}pin_scb() methods are extended to track the pin status in the
vsie_page->flags.
A new vsie_page_init() method is created to allow reuse for common tasks
in following patches.
Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
---
arch/s390/kvm/vsie.c | 149 +++++++++++++++++++++++++++++++++++----------------
1 file changed, 103 insertions(+), 46 deletions(-)
diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c
index d2582519b3ec..490c29b369cd 100644
--- a/arch/s390/kvm/vsie.c
+++ b/arch/s390/kvm/vsie.c
@@ -29,6 +29,7 @@
enum vsie_page_flags {
VSIE_PAGE_IN_USE = 0,
+ VSIE_PAGE_SCB_PINNED = 1,
};
struct vsie_page {
@@ -760,14 +761,18 @@ static int shadow_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
}
/* unpin the scb provided by guest 2, marking it as dirty */
-static void unpin_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,
- gpa_t gpa)
+static void unpin_scb(struct kvm *kvm, struct vsie_page *vsie_page)
{
- hpa_t hpa = virt_to_phys(vsie_page->scb_o);
+ hpa_t hpa;
+
+ if (!test_bit(VSIE_PAGE_SCB_PINNED, &vsie_page->flags))
+ return;
+ hpa = virt_to_phys(vsie_page->scb_o);
if (hpa)
- unpin_guest_page(vcpu->kvm, gpa, hpa);
+ unpin_guest_page(kvm, vsie_page->scb_gpa, hpa);
vsie_page->scb_o = NULL;
+ __clear_bit(VSIE_PAGE_SCB_PINNED, &vsie_page->flags);
}
/*
@@ -776,19 +781,22 @@ static void unpin_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,
* Returns: - 0 if the scb was pinned.
* - > 0 if control has to be given to guest 2
*/
-static int pin_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,
- gpa_t gpa)
+static int pin_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
{
hpa_t hpa;
int rc;
- rc = pin_guest_page(vcpu->kvm, gpa, &hpa);
+ if (test_bit(VSIE_PAGE_SCB_PINNED, &vsie_page->flags))
+ return 0;
+
+ rc = pin_guest_page(vcpu->kvm, vsie_page->scb_gpa, &hpa);
if (rc) {
rc = kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
WARN_ON_ONCE(rc);
return 1;
}
vsie_page->scb_o = phys_to_virt(hpa);
+ __set_bit(VSIE_PAGE_SCB_PINNED, &vsie_page->flags);
return 0;
}
@@ -1529,17 +1537,45 @@ static struct vsie_page *alloc_vsie_page(struct kvm *kvm)
return vsie_page;
}
+static int vsie_page_init(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page, unsigned long scb_gpa)
+{
+ struct kvm *kvm = vcpu->kvm;
+ int rc;
+
+ if (vsie_page->scb_gpa != ULONG_MAX)
+ xa_erase(&kvm->arch.vsie.addr_to_page, vsie_page->scb_gpa >> SCB_ALIGNMENT_SHIFT);
+ vsie_page->scb_gpa = scb_gpa;
+ rc = pin_scb(vcpu, vsie_page);
+ if (rc) {
+ vsie_page->scb_gpa = ULONG_MAX;
+ return -ENOMEM;
+ }
+
+ vsie_page->sca_gpa = read_scao(kvm, vsie_page->scb_o);
+ WARN_ON_ONCE(xa_insert(&kvm->arch.vsie.addr_to_page, scb_gpa >> SCB_ALIGNMENT_SHIFT,
+ vsie_page, GFP_KERNEL_ACCOUNT));
+
+ return 0;
+}
+
/*
* Get or create a vsie page for a scb address.
*
+ * Original control blocks are pinned when the vsie_page pointing to them is
+ * returned.
+ * Newly created vsie_pages only have vsie_page->scb_gpa and vsie_page->sca_gpa
+ * set.
+ *
* Returns: - address of a vsie page (cached or new one)
* - NULL if the same scb address is already used by another VCPU
* - ERR_PTR(-ENOMEM) if out of memory
*/
-static struct vsie_page *get_vsie_page(struct kvm *kvm, unsigned long addr)
+static struct vsie_page *get_vsie_page(struct kvm_vcpu *vcpu, unsigned long addr)
{
- struct vsie_page *vsie_page;
- int nr_vcpus;
+ struct vsie_page *vsie_page, *vsie_page_new = NULL;
+ struct kvm *kvm = vcpu->kvm;
+ unsigned int max_vsie_page;
+ int rc, pages_idx;
vsie_page = xa_load(&kvm->arch.vsie.addr_to_page, addr >> 9);
if (vsie_page && try_get_vsie_page(vsie_page)) {
@@ -1552,53 +1588,69 @@ static struct vsie_page *get_vsie_page(struct kvm *kvm, unsigned long addr)
put_vsie_page(vsie_page);
}
- /*
- * We want at least #online_vcpus shadows, so every VCPU can execute
- * the VSIE in parallel.
- */
- nr_vcpus = atomic_read(&kvm->online_vcpus);
+ max_vsie_page = atomic_read(&kvm->online_vcpus);
+
+ /* allocate new vsie_page - we will likely need it */
+ if (kvm->arch.vsie.page_count < max_vsie_page) {
+ vsie_page_new = alloc_vsie_page(kvm);
+ if (!vsie_page_new)
+ return ERR_PTR(-ENOMEM);
+ __set_bit(VSIE_PAGE_IN_USE, &vsie_page_new->flags);
+ }
mutex_lock(&kvm->arch.vsie.mutex);
- if (kvm->arch.vsie.page_count < nr_vcpus) {
- vsie_page = alloc_vsie_page(kvm);
- if (!vsie_page) {
+ vsie_page = xa_load(&kvm->arch.vsie.addr_to_page, addr >> SCB_ALIGNMENT_SHIFT);
+ if (vsie_page && try_get_vsie_page(vsie_page)) {
+ if (vsie_page->scb_gpa == addr) {
mutex_unlock(&kvm->arch.vsie.mutex);
- return ERR_PTR(-ENOMEM);
+ if (vsie_page_new)
+ free_vsie_page(vsie_page_new);
+ return vsie_page;
}
- __set_bit(VSIE_PAGE_IN_USE, &vsie_page->flags);
- kvm->arch.vsie.pages[kvm->arch.vsie.page_count] = vsie_page;
+ /*
+ * We raced with someone reusing + putting this vsie
+ * page before we grabbed it.
+ */
+ put_vsie_page(vsie_page);
+ }
+
+ if (kvm->arch.vsie.page_count < max_vsie_page) {
+ pages_idx = kvm->arch.vsie.page_count;
+ vsie_page = vsie_page_new;
+ vsie_page_new = NULL;
+ WRITE_ONCE(kvm->arch.vsie.pages[kvm->arch.vsie.page_count], vsie_page);
kvm->arch.vsie.page_count++;
} else {
/* reuse an existing entry that belongs to nobody */
while (true) {
- vsie_page = kvm->arch.vsie.pages[kvm->arch.vsie.next];
+ pages_idx = kvm->arch.vsie.next;
+ kvm->arch.vsie.next++;
+ kvm->arch.vsie.next %= kvm->arch.vsie.page_count;
+ vsie_page = kvm->arch.vsie.pages[pages_idx];
if (try_get_vsie_page(vsie_page))
break;
- kvm->arch.vsie.next++;
- kvm->arch.vsie.next %= nr_vcpus;
}
- if (vsie_page->scb_gpa != ULONG_MAX)
- xa_erase(&kvm->arch.vsie.addr_to_page,
- vsie_page->scb_gpa >> 9);
- /* Mark it as invalid until it resides in the tree. */
- vsie_page->scb_gpa = ULONG_MAX;
+
+ unpin_scb(kvm, vsie_page);
}
- /* Double use of the same address or allocation failure. */
- if (xa_insert(&kvm->arch.vsie.addr_to_page, addr >> 9, vsie_page,
- GFP_KERNEL_ACCOUNT)) {
+ rc = vsie_page_init(vcpu, vsie_page, addr);
+ mutex_unlock(&kvm->arch.vsie.mutex);
+ if (vsie_page_new)
+ free_vsie_page(vsie_page_new);
+ if (WARN_ON_ONCE(rc)) {
+ unpin_scb(kvm, vsie_page);
+ vsie_page->scb_gpa = ULONG_MAX;
put_vsie_page(vsie_page);
- mutex_unlock(&kvm->arch.vsie.mutex);
- return NULL;
+ return ERR_PTR(rc);
}
- vsie_page->scb_gpa = addr;
- mutex_unlock(&kvm->arch.vsie.mutex);
memset(&vsie_page->scb_s, 0, sizeof(struct kvm_s390_sie_block));
release_gmap_shadow_safe(kvm, vsie_page);
prefix_unmapped(vsie_page);
vsie_page->fault_addr = 0;
vsie_page->scb_s.ihcpu = 0xffffU;
+
return vsie_page;
}
@@ -1626,7 +1678,7 @@ int kvm_s390_handle_vsie(struct kvm_vcpu *vcpu)
return 0;
}
- vsie_page = get_vsie_page(vcpu->kvm, scb_addr);
+ vsie_page = get_vsie_page(vcpu, scb_addr);
if (IS_ERR(vsie_page)) {
return PTR_ERR(vsie_page);
} else if (!vsie_page) {
@@ -1635,7 +1687,7 @@ int kvm_s390_handle_vsie(struct kvm_vcpu *vcpu)
return 0;
}
- rc = pin_scb(vcpu, vsie_page, scb_addr);
+ rc = pin_scb(vcpu, vsie_page);
if (rc)
goto out_put;
rc = shadow_scb(vcpu, vsie_page);
@@ -1651,7 +1703,7 @@ int kvm_s390_handle_vsie(struct kvm_vcpu *vcpu)
out_unshadow:
unshadow_scb(vcpu, vsie_page);
out_unpin_scb:
- unpin_scb(vcpu, vsie_page, scb_addr);
+ unpin_scb(vcpu->kvm, vsie_page);
out_put:
put_vsie_page(vsie_page);
@@ -1665,24 +1717,29 @@ void kvm_s390_vsie_init(struct kvm *kvm)
xa_init_flags(&kvm->arch.vsie.addr_to_page, XA_FLAGS_ACCOUNT);
}
+static void kvm_s390_vsie_destroy_page(struct kvm *kvm, struct vsie_page *vsie_page)
+{
+ unpin_scb(kvm, vsie_page);
+ release_gmap_shadow_safe(kvm, vsie_page);
+ free_vsie_page(vsie_page);
+}
+
/* Destroy the vsie data structures. To be called when a vm is destroyed. */
void kvm_s390_vsie_destroy(struct kvm *kvm)
{
struct vsie_page *vsie_page;
int i;
- mutex_lock(&kvm->arch.vsie.mutex);
+ guard(mutex)(&kvm->arch.vsie.mutex);
+
for (i = 0; i < kvm->arch.vsie.page_count; i++) {
vsie_page = kvm->arch.vsie.pages[i];
- scoped_guard(spinlock, &kvm->arch.gmap->children_lock)
- if (vsie_page->gmap_cache.gmap)
- release_gmap_shadow(vsie_page);
kvm->arch.vsie.pages[i] = NULL;
- free_vsie_page(vsie_page);
+ kvm_s390_vsie_destroy_page(kvm, vsie_page);
}
- xa_destroy(&kvm->arch.vsie.addr_to_page);
+
kvm->arch.vsie.page_count = 0;
- mutex_unlock(&kvm->arch.vsie.mutex);
+ xa_destroy(&kvm->arch.vsie.addr_to_page);
}
void kvm_s390_vsie_kick(struct kvm_vcpu *vcpu)
--
2.55.0
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v4 12/20] KVM: s390: vsie: Add helper to pin and unpin multiple guest pages
2026-08-12 13:52 [PATCH v4 00/20] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
` (10 preceding siblings ...)
2026-08-12 13:52 ` [PATCH v4 11/20] KVM: s390: vsie: Lazily keep original scb pinned after vsie exit Christoph Schlameuss
@ 2026-08-12 13:52 ` Christoph Schlameuss
2026-08-12 14:11 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 13/20] KVM: s390: vsie: Add struct vsie_sca with pin and unpin methods Christoph Schlameuss
` (7 subsequent siblings)
19 siblings, 1 reply; 41+ messages in thread
From: Christoph Schlameuss @ 2026-08-12 13:52 UTC (permalink / raw)
To: kvm, linux-s390
Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
Nico Boehr, Paolo Bonzini, Shuah Khan, Sven Schnelle,
Vasily Gorbik, Christoph Schlameuss
To handle vSIE configurations with an ssca we need to pin and unpin
multiple consecutive guest-2 pages in guest-1. As these might not be
consecutive in guest-1 it is necessary to iterate over all pages and
store guest and host addresses for later use.
Since the new methods use the existing {,un}pin_guest_page() helpers,
they are moved up unchanged in the file to avoid having to resort to
forward declarations later on.
Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
---
arch/s390/kvm/vsie.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 45 insertions(+)
diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c
index 490c29b369cd..442e7997ee51 100644
--- a/arch/s390/kvm/vsie.c
+++ b/arch/s390/kvm/vsie.c
@@ -72,6 +72,11 @@ struct vsie_page {
static_assert(sizeof(struct vsie_page) == PAGE_SIZE);
+struct kvm_address_pair {
+ gpa_t gpa;
+ hpa_t hpa;
+};
+
static inline bool sie_uses_esca(struct kvm_s390_sie_block *scb)
{
return (scb->ecb2 & ECB2_ESCA);
@@ -250,6 +255,46 @@ static void unpin_guest_page(struct kvm *kvm, gpa_t gpa, hpa_t hpa)
mark_page_dirty(kvm, gpa_to_gfn(gpa));
}
+/* unpin multiple guest pages pinned with pin_guest_pages() */
+static void unpin_guest_pages(struct kvm *kvm, struct kvm_address_pair *addr, unsigned int nr_pages)
+{
+ int i;
+
+ for (i = 0; i < nr_pages; i++) {
+ unpin_guest_page(kvm, addr[i].gpa, addr[i].hpa);
+ addr[i].gpa = 0;
+ addr[i].hpa = 0;
+ }
+}
+
+/*
+ * pin nr_pages consecutive guest pages
+ *
+ * Returns number of pages pinned or -EFAULT.
+ */
+static int pin_guest_pages(struct kvm *kvm, gpa_t gpa, unsigned int nr_pages,
+ struct kvm_address_pair *addr)
+{
+ hpa_t hpa;
+ int i, rc;
+
+ gpa = gpa & PAGE_MASK;
+
+ /* the guest pages may not be mapped continuously, so pin each page */
+ for (i = 0; i < nr_pages; i++) {
+ rc = pin_guest_page(kvm, gpa + PAGE_SIZE * i, &hpa);
+ if (rc)
+ goto err;
+ addr[i].gpa = gpa + PAGE_SIZE * i;
+ addr[i].hpa = hpa;
+ }
+ return i;
+
+err:
+ unpin_guest_pages(kvm, addr, i);
+ return -EFAULT;
+}
+
/* copy the updated intervention request bits into the shadow scb */
static void update_intervention_requests(struct vsie_page *vsie_page)
{
--
2.55.0
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v4 13/20] KVM: s390: vsie: Add struct vsie_sca with pin and unpin methods
2026-08-12 13:52 [PATCH v4 00/20] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
` (11 preceding siblings ...)
2026-08-12 13:52 ` [PATCH v4 12/20] KVM: s390: vsie: Add helper to pin and unpin multiple guest pages Christoph Schlameuss
@ 2026-08-12 13:52 ` Christoph Schlameuss
2026-08-12 15:43 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 14/20] KVM: s390: vsie: Shadow VSIE SCA in guest-1 Christoph Schlameuss
` (6 subsequent siblings)
19 siblings, 1 reply; 41+ messages in thread
From: Christoph Schlameuss @ 2026-08-12 13:52 UTC (permalink / raw)
To: kvm, linux-s390
Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
Nico Boehr, Paolo Bonzini, Shuah Khan, Sven Schnelle,
Vasily Gorbik, Christoph Schlameuss
Introduce the struct vsie_sca to hold the SSCA and required management
data along with alloc and free methods to use with the struct.
On this basis we can also add methods {,un}pin_sca() to simplify the
pinning SCAs in g1.
Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
---
arch/s390/include/asm/kvm_host_types.h | 1 +
arch/s390/kvm/vsie.c | 97 +++++++++++++++++++++++++++++++++-
2 files changed, 97 insertions(+), 1 deletion(-)
diff --git a/arch/s390/include/asm/kvm_host_types.h b/arch/s390/include/asm/kvm_host_types.h
index 6540146b4b00..d6025b34a66d 100644
--- a/arch/s390/include/asm/kvm_host_types.h
+++ b/arch/s390/include/asm/kvm_host_types.h
@@ -7,6 +7,7 @@
#include <linux/types.h>
#define KVM_S390_MAX_VSIE_VCPUS 256
+#define KVM_S390_MAX_SCA_PAGES 5
#define KVM_S390_BSCA_CPU_SLOTS 64
#define KVM_S390_ESCA_CPU_SLOTS 248
diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c
index 442e7997ee51..e63231122200 100644
--- a/arch/s390/kvm/vsie.c
+++ b/arch/s390/kvm/vsie.c
@@ -65,7 +65,8 @@ struct vsie_page {
gpa_t scb_gpa; /* 0x0258 */
/* the shadow gmap in use by the vsie_page */
struct gmap_cache gmap_cache; /* 0x0260 */
- __u8 reserved[0x06f8 - 0x0278]; /* 0x0278 */
+ struct vsie_sca *vsie_sca; /* 0x0278 */
+ __u8 reserved[0x06f8 - 0x0280]; /* 0x0280 */
struct kvm_s390_crypto_cb crycb; /* 0x06f8 */
__u8 fac[8 + S390_ARCH_FAC_LIST_SIZE_BYTE];/* 0x07f8 */
};
@@ -77,6 +78,32 @@ struct kvm_address_pair {
hpa_t hpa;
};
+enum vsie_sca_flags {
+ VSIE_SCA_ESCA = 0,
+ VSIE_SCA_SCA_PINNED = 1,
+};
+
+struct vsie_sca {
+ struct ssca_block ssca;
+ struct {} start_no_clear_fields;
+ struct vsie_page *pages[KVM_S390_MAX_VSIE_VCPUS];
+ /* The mutex is used to synchronize access to the pages[] */
+ struct mutex mutex;
+ atomic_t ref_count;
+ struct {} end_no_clear_fields;
+ gpa_t sca_gpa;
+ unsigned long flags;
+ u64 mcn[4];
+ unsigned int sca_o_nr_pages;
+ struct kvm_address_pair sca_o_pages[KVM_S390_MAX_SCA_PAGES];
+};
+
+/*
+ * SSCA needs to be 32-byte aligned and members before the cpu field need to be on the same page.
+ * Forcing it to offset 0 will always fulfill the requirements.
+ */
+static_assert(!(offsetof(struct vsie_sca, ssca)));
+
static inline bool sie_uses_esca(struct kvm_s390_sie_block *scb)
{
return (scb->ecb2 & ECB2_ESCA);
@@ -845,6 +872,74 @@ static int pin_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
return 0;
}
+/*
+ * Unpin g2 original sca in g1 memory.
+ *
+ * Called with vsie_sca_lock held.
+ */
+static void unpin_sca(struct kvm *kvm, struct vsie_sca *vsie_sca)
+{
+ if (!test_bit(VSIE_SCA_SCA_PINNED, &vsie_sca->flags))
+ return;
+
+ unpin_guest_pages(kvm, vsie_sca->sca_o_pages, vsie_sca->sca_o_nr_pages);
+ vsie_sca->sca_o_nr_pages = 0;
+
+ __clear_bit(VSIE_SCA_SCA_PINNED, &vsie_sca->flags);
+}
+
+/*
+ * Pin g2 original sca in g1 memory.
+ *
+ * Called with vsie_sca_lock held.
+ */
+static int pin_sca(struct kvm *kvm, struct vsie_sca *vsie_sca)
+{
+ bool is_esca = test_bit(VSIE_SCA_ESCA, &vsie_sca->flags);
+ gpa_t offset = vsie_sca->sca_gpa & ~PAGE_MASK;
+ int nr_pages;
+
+ if (test_bit(VSIE_SCA_SCA_PINNED, &vsie_sca->flags))
+ return 0;
+
+ if (is_esca) {
+ nr_pages = 4;
+ if (offset + sizeof(struct esca_block) > 4 * PAGE_SIZE)
+ nr_pages = 5;
+ } else {
+ nr_pages = 1;
+ if (offset + sizeof(struct bsca_block) > PAGE_SIZE)
+ nr_pages = 2;
+ }
+
+ vsie_sca->sca_o_nr_pages = pin_guest_pages(kvm, vsie_sca->sca_gpa, nr_pages,
+ vsie_sca->sca_o_pages);
+ if (WARN_ON_ONCE(vsie_sca->sca_o_nr_pages != nr_pages))
+ return -EIO;
+ __set_bit(VSIE_SCA_SCA_PINNED, &vsie_sca->flags);
+
+ return 0;
+}
+
+static void free_vsie_sca(struct kvm *kvm, struct vsie_sca *vsie_sca)
+{
+ free_pages_exact(vsie_sca, sizeof(*vsie_sca));
+}
+
+static struct vsie_sca *alloc_vsie_sca(void)
+{
+ struct vsie_sca *vsie_sca;
+
+ vsie_sca = alloc_pages_exact(sizeof(*vsie_sca), GFP_KERNEL_ACCOUNT | __GFP_ZERO);
+ if (!vsie_sca)
+ return NULL;
+
+ mutex_init(&vsie_sca->mutex);
+ atomic_set(&vsie_sca->ref_count, 0);
+
+ return vsie_sca;
+}
+
void kvm_s390_vsie_gmap_notifier(struct gmap *gmap, gpa_t start, gpa_t end)
{
struct vsie_page *cur, *next;
--
2.55.0
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v4 14/20] KVM: s390: vsie: Shadow VSIE SCA in guest-1
2026-08-12 13:52 [PATCH v4 00/20] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
` (12 preceding siblings ...)
2026-08-12 13:52 ` [PATCH v4 13/20] KVM: s390: vsie: Add struct vsie_sca with pin and unpin methods Christoph Schlameuss
@ 2026-08-12 13:52 ` Christoph Schlameuss
2026-08-12 16:10 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 15/20] KVM: s390: vsie: Guard against invalid CPU address Christoph Schlameuss
` (5 subsequent siblings)
19 siblings, 1 reply; 41+ messages in thread
From: Christoph Schlameuss @ 2026-08-12 13:52 UTC (permalink / raw)
To: kvm, linux-s390
Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
Nico Boehr, Paolo Bonzini, Shuah Khan, Sven Schnelle,
Vasily Gorbik, Christoph Schlameuss
Restructure kvm_s390_handle_vsie() to create a guest-1 shadow of the SCA
if guest-2 attempts to enter SIE with an SCA. If the SCA is used the
vsie_pages are stored in a new vsie_sca struct instead of the arch vsie
struct.
When the VSIE-Interpretation-Extension Facility is active the shadow SCA
(ssca_block) will be created and shadows of all CPUs defined in the
configuration are created. SCAOL/H in the VSIE control block are
overwritten with references to the shadow SCA.
The shadow SCA contains the addresses of the original guest-3 SCA as
well as the original VSIE control blocks. With these addresses the
machine can directly monitor the intervention bits within the original
SCA entries, enabling it to handle SENSE_RUNNING and EXTERNAL_CALL SIGP
instructions without exiting VSIE. The benefit of this is that the SIGP
calls are handled faster. Additionally the number of required VM exits
and therefore reentries are reduced, reducing the un-/shadowing effort.
The original SCA will be pinned in guest-2 memory and only be unpinned
before reuse. This means some pages might still be pinned even after the
guest 3 VM no longer exists.
References to the existing vsie_scas including the ssca_blocks are also
kept within a map to reuse already existing ssca_blocks efficiently.
The map and array with references to the vsie_scas are held in the
arch vsie struct. The use of vsie_scas is tracked using a ref_count.
Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
---
arch/s390/include/asm/kvm_host.h | 21 +-
arch/s390/include/asm/kvm_host_types.h | 2 +
arch/s390/kvm/vsie.c | 482 ++++++++++++++++++++++++++++++---
3 files changed, 465 insertions(+), 40 deletions(-)
diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h
index 2ae8fa51bf51..3d75731e1e19 100644
--- a/arch/s390/include/asm/kvm_host.h
+++ b/arch/s390/include/asm/kvm_host.h
@@ -627,13 +627,32 @@ struct sie_page2 {
};
struct vsie_page;
+struct vsie_sca;
+/*
+ * vsie_pages, scas and accompanied management vars
+ */
struct kvm_s390_vsie {
struct mutex mutex;
struct xarray addr_to_page;
int page_count;
int next;
- struct vsie_page *pages[KVM_MAX_VCPUS];
+ struct vsie_page *pages[KVM_S390_MAX_VSIE_VCPUS];
+ /*
+ * The vsie_sca_lock is used to synchronize access to
+ * - the kvm_s390_vsie.scas[]
+ * - the kvm_s390_vsie.osca_to_sca map
+ * - new vsie_sca creation and initialization
+ */
+ struct rw_semaphore vsie_sca_lock;
+ struct xarray osca_to_sca;
+ int sca_count;
+ int sca_next;
+ /*
+ * In addition to the use of the array when entering and exiting vsie the scas[] is
+ * accessed from the gmap_notifier without any lock held.
+ */
+ struct vsie_sca *scas[KVM_S390_MAX_VSIE_VCPUS];
};
struct kvm_s390_gisa_iam {
diff --git a/arch/s390/include/asm/kvm_host_types.h b/arch/s390/include/asm/kvm_host_types.h
index d6025b34a66d..c4ef3a72999a 100644
--- a/arch/s390/include/asm/kvm_host_types.h
+++ b/arch/s390/include/asm/kvm_host_types.h
@@ -12,6 +12,8 @@
#define KVM_S390_BSCA_CPU_SLOTS 64
#define KVM_S390_ESCA_CPU_SLOTS 248
+#define SCA_ALIGNMENT_SHIFT 6
+
#define SIGP_CTRL_C 0x80
#define SIGP_CTRL_SCN_MASK 0x3f
diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c
index e63231122200..2fd349e52bd6 100644
--- a/arch/s390/kvm/vsie.c
+++ b/arch/s390/kvm/vsie.c
@@ -84,18 +84,20 @@ enum vsie_sca_flags {
};
struct vsie_sca {
- struct ssca_block ssca;
- struct {} start_no_clear_fields;
+ struct_group(head,
+ struct ssca_block ssca;
+ );
struct vsie_page *pages[KVM_S390_MAX_VSIE_VCPUS];
/* The mutex is used to synchronize access to the pages[] */
struct mutex mutex;
atomic_t ref_count;
- struct {} end_no_clear_fields;
- gpa_t sca_gpa;
- unsigned long flags;
- u64 mcn[4];
- unsigned int sca_o_nr_pages;
- struct kvm_address_pair sca_o_pages[KVM_S390_MAX_SCA_PAGES];
+ struct_group(tail,
+ gpa_t sca_gpa;
+ unsigned long flags;
+ u64 mcn[4];
+ unsigned int sca_o_nr_pages;
+ struct kvm_address_pair sca_o_pages[KVM_S390_MAX_SCA_PAGES];
+ );
};
/*
@@ -104,6 +106,11 @@ struct vsie_sca {
*/
static_assert(!(offsetof(struct vsie_sca, ssca)));
+static inline hpa_t sca_o_hpa(struct vsie_sca *vsie_sca)
+{
+ return vsie_sca->sca_o_pages[0].hpa | (vsie_sca->sca_gpa & ~PAGE_MASK);
+}
+
static inline bool sie_uses_esca(struct kvm_s390_sie_block *scb)
{
return (scb->ecb2 & ECB2_ESCA);
@@ -125,6 +132,17 @@ static void write_scao(struct kvm_s390_sie_block *scb, unsigned long hpa)
scb->scaol = (u32)(u64)hpa;
}
+static inline bool use_ssca(struct kvm *kvm, struct kvm_s390_sie_block *scb)
+{
+ if (!kvm->arch.use_ssca)
+ return false;
+ if (!(scb->eca & ECA_SIGPI) && !(scb->ecb & ECB_SRSI))
+ return false;
+ if (!read_scao(kvm, scb))
+ return false;
+ return true;
+}
+
/* trigger a validity icpt for the given scb */
static int set_validity_icpt(struct kvm_s390_sie_block *scb,
__u16 reason_code)
@@ -921,6 +939,78 @@ static int pin_sca(struct kvm *kvm, struct vsie_sca *vsie_sca)
return 0;
}
+static int get_sca_entry_addr(struct kvm *kvm, struct vsie_sca *vsie_sca, u16 cpu_nr, gpa_t *gpa,
+ hpa_t *hpa)
+{
+ hpa_t cpu_offset, offset;
+ int pn;
+
+ /*
+ * We cannot simply access the hva since the esca_block has typically
+ * 4 pages (arch max 5 pages) that might not be continuous in g1 memory.
+ * The bsca_block may also be stretched over two pages. Only the header
+ * is guaranteed to be on the same page.
+ */
+ if (test_bit(VSIE_SCA_ESCA, &vsie_sca->flags))
+ cpu_offset = offsetof(struct esca_block, cpu[cpu_nr]);
+ else
+ cpu_offset = offsetof(struct bsca_block, cpu[cpu_nr]);
+ pn = ((vsie_sca->sca_gpa & ~PAGE_MASK) + cpu_offset) >> PAGE_SHIFT;
+ offset = (vsie_sca->sca_gpa + cpu_offset) & ~PAGE_MASK;
+ if (WARN_ON_ONCE(pn >= vsie_sca->sca_o_nr_pages))
+ return -EINVAL;
+
+ if (gpa)
+ *gpa = vsie_sca->sca_o_pages[pn].gpa | offset;
+ if (hpa)
+ *hpa = vsie_sca->sca_o_pages[pn].hpa | offset;
+ return 0;
+}
+
+static void put_vsie_sca(struct vsie_sca *vsie_sca)
+{
+ if (!vsie_sca)
+ return;
+
+ WARN_ON_ONCE(atomic_dec_return(&vsie_sca->ref_count) < 0);
+}
+
+/*
+ * Try to find the address of an existing shadow system control area.
+ * @sca_o_gpa: original system control area address; guest-2 physical
+ *
+ * Called with lock on vsie_sca_lock.
+ */
+static struct vsie_sca *get_existing_vsie_sca(struct kvm *kvm, gpa_t sca_o_gpa)
+{
+ struct vsie_sca *vsie_sca = xa_load(&kvm->arch.vsie.osca_to_sca,
+ sca_o_gpa >> SCA_ALIGNMENT_SHIFT);
+
+ WARN_ON_ONCE(vsie_sca && atomic_inc_return(&vsie_sca->ref_count) < 1);
+ return vsie_sca;
+}
+
+/* Try to find and get a currently unused vsie_sca from the vsie struct. */
+static struct vsie_sca *get_reuseable_vsie_sca(struct kvm *kvm)
+{
+ struct vsie_sca *vsie_sca;
+ int i, ref_count;
+
+ lockdep_assert_held_write(&kvm->arch.vsie.vsie_sca_lock);
+
+ for (i = 0; i < kvm->arch.vsie.sca_count; i++) {
+ vsie_sca = READ_ONCE(kvm->arch.vsie.scas[kvm->arch.vsie.sca_next]);
+ kvm->arch.vsie.sca_next++;
+ kvm->arch.vsie.sca_next %= kvm->arch.vsie.sca_count;
+ ref_count = atomic_inc_return(&vsie_sca->ref_count);
+ WARN_ON_ONCE(ref_count < 1);
+ if (ref_count == 1)
+ return vsie_sca;
+ put_vsie_sca(vsie_sca);
+ }
+ return ERR_PTR(-EAGAIN);
+}
+
static void free_vsie_sca(struct kvm *kvm, struct vsie_sca *vsie_sca)
{
free_pages_exact(vsie_sca, sizeof(*vsie_sca));
@@ -940,6 +1030,121 @@ static struct vsie_sca *alloc_vsie_sca(void)
return vsie_sca;
}
+/* Clear the vsie_sca struct but keep the vsie_page references, mutex and ref_count */
+static void clear_vsie_sca(struct vsie_sca *vsie_sca)
+{
+ memset(&vsie_sca->head, 0, sizeof(vsie_sca->head));
+ memset(&vsie_sca->tail, 0, sizeof(vsie_sca->tail));
+}
+
+/* Pin and get an existing or new guest-3 system control area.*/
+static struct vsie_sca *get_vsie_sca(struct kvm_vcpu *vcpu, struct kvm_s390_sie_block *scb_o)
+{
+ struct vsie_sca *vsie_sca, *vsie_sca_new = NULL;
+ gpa_t sca_gpa = read_scao(vcpu->kvm, scb_o);
+ struct vsie_page *vsie_page_n;
+ struct kvm *kvm = vcpu->kvm;
+ unsigned int max_vsie_sca;
+ int rc, cpu_nr;
+
+ /* validate scb_o as we do not unshadow on error here */
+ rc = validate_scao(vcpu, scb_o, sca_gpa);
+ if (rc)
+ return ERR_PTR(-EINVAL);
+
+ down_read(&kvm->arch.vsie.vsie_sca_lock);
+ vsie_sca = get_existing_vsie_sca(kvm, sca_gpa);
+ up_read(&kvm->arch.vsie.vsie_sca_lock);
+ if (vsie_sca)
+ return vsie_sca;
+
+ /*
+ * Allocate new vsie_sca, it will likely be needed below.
+ * We want at least #online_vcpus shadows, so every VCPU can execute the
+ * VSIE in parallel. (Worst case all single core VMs.)
+ */
+ max_vsie_sca = MIN(atomic_read(&kvm->online_vcpus), KVM_S390_MAX_VSIE_VCPUS);
+
+ if (kvm->arch.vsie.sca_count < max_vsie_sca) {
+ vsie_sca_new = alloc_vsie_sca();
+ if (!vsie_sca_new)
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /*
+ * Now we're taking the vsie_sca_lock in write mode so that we can manipulate
+ * the radix tree and recheck for existing SCAs with exclusive access.
+ *
+ * In the next lines we try three things to get an SCA:
+ * - Retry getting an existing vsie_sca
+ * - Using our newly allocated vsie_sca if we're under the limit
+ * - Reusing an vsie_sca including ssca to shadow a different osca
+ */
+ down_write(&kvm->arch.vsie.vsie_sca_lock);
+ vsie_sca = get_existing_vsie_sca(kvm, sca_gpa);
+ if (vsie_sca)
+ goto out;
+
+ /* check again under write lock if we are still under our vsie_sca limit */
+ if (vsie_sca_new && kvm->arch.vsie.sca_count < max_vsie_sca) {
+ /* make use of vsie_sca just created */
+ vsie_sca = vsie_sca_new;
+ vsie_sca_new = NULL;
+
+ kvm->arch.vsie.scas[kvm->arch.vsie.sca_count] = vsie_sca;
+ kvm->arch.vsie.sca_count++;
+ atomic_set(&vsie_sca->ref_count, 1);
+ } else {
+ /* reuse previously created vsie_sca allocation for different osca */
+ vsie_sca = get_reuseable_vsie_sca(kvm);
+ /* with nr_vcpus scas one must be reusable */
+ if (IS_ERR(vsie_sca))
+ goto out;
+ WARN_ON_ONCE(atomic_read(&vsie_sca->ref_count) != 1);
+
+ xa_erase(&kvm->arch.vsie.osca_to_sca, vsie_sca->sca_gpa >> SCA_ALIGNMENT_SHIFT);
+ for (cpu_nr = 0; cpu_nr < KVM_S390_MAX_VSIE_VCPUS; cpu_nr++) {
+ vsie_page_n = vsie_sca->pages[cpu_nr];
+ if (!vsie_page_n)
+ continue;
+
+ /* unpin but keep the vsie_page for reuse */
+ unpin_scb(kvm, vsie_page_n);
+ release_gmap_shadow_safe(kvm, vsie_page_n);
+ memset(vsie_page_n, 0, sizeof(struct vsie_page));
+ vsie_page_n->scb_gpa = ULONG_MAX;
+ }
+ unpin_sca(kvm, vsie_sca);
+ clear_vsie_sca(vsie_sca);
+ }
+
+ if (sie_uses_esca(scb_o))
+ __set_bit(VSIE_SCA_ESCA, &vsie_sca->flags);
+ vsie_sca->sca_gpa = sca_gpa;
+
+ /*
+ * The pinned original sca will only be unpinned lazily to limit the
+ * required amount of pins/unpins on each vsie entry/exit.
+ * The unpin is done in the reuse vsie_sca allocation path above and
+ * kvm_s390_vsie_destroy().
+ */
+ rc = pin_sca(kvm, vsie_sca);
+ if (rc) {
+ put_vsie_sca(vsie_sca);
+ vsie_sca = ERR_PTR(rc);
+ goto out;
+ }
+
+ WARN_ON_ONCE(xa_store(&kvm->arch.vsie.osca_to_sca,
+ vsie_sca->sca_gpa >> SCA_ALIGNMENT_SHIFT, vsie_sca, GFP_KERNEL));
+
+out:
+ up_write(&kvm->arch.vsie.vsie_sca_lock);
+ if (vsie_sca_new)
+ free_vsie_sca(kvm, vsie_sca_new);
+ return vsie_sca;
+}
+
void kvm_s390_vsie_gmap_notifier(struct gmap *gmap, gpa_t start, gpa_t end)
{
struct vsie_page *cur, *next;
@@ -1006,11 +1211,13 @@ static void unpin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s;
hpa_t hpa;
- hpa = (u64) scb_s->scaoh << 32 | scb_s->scaol;
- if (hpa) {
- unpin_guest_page(vcpu->kvm, vsie_page->sca_gpa, hpa);
- vsie_page->sca_gpa = 0;
- write_scao(scb_s, 0);
+ if (!vsie_page->vsie_sca) {
+ hpa = (u64) scb_s->scaoh << 32 | scb_s->scaol;
+ if (hpa) {
+ unpin_guest_page(vcpu->kvm, vsie_page->sca_gpa, hpa);
+ vsie_page->sca_gpa = 0;
+ write_scao(scb_s, 0);
+ }
}
hpa = scb_s->itdba;
@@ -1049,9 +1256,6 @@ static void unpin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
* This works as long as the data lies in one page. If blocks ever exceed one
* page, we have to fall back to shadowing.
*
- * As we reuse the sca, the vcpu pointers contained in it are invalid. We must
- * therefore not enable any facilities that access these pointers (e.g. SIGPIF).
- *
* Returns: - 0 if all blocks were pinned.
* - > 0 if control has to be given to guest 2
* - -ENOMEM if out of memory
@@ -1064,8 +1268,8 @@ static int pin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
gpa_t gpa;
int rc = 0;
- gpa = read_scao(vcpu->kvm, scb_o);
- if (gpa) {
+ gpa = vsie_page->sca_gpa;
+ if (gpa && !vsie_page->vsie_sca) {
rc = validate_scao(vcpu, scb_s, gpa);
if (rc)
goto unpin;
@@ -1074,7 +1278,6 @@ static int pin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
rc = set_validity_icpt(scb_s, 0x0034U);
goto unpin;
}
- vsie_page->sca_gpa = gpa;
write_scao(scb_s, hpa);
}
@@ -1615,7 +1818,7 @@ static int vsie_run(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
*/
if (kvm_s390_vcpu_has_irq(vcpu, 0) ||
kvm_s390_vcpu_sie_inhibited(vcpu)) {
- kvm_s390_rewind_psw(vcpu, 4);
+ rc = -EAGAIN;
break;
}
if (sg)
@@ -1679,11 +1882,10 @@ static struct vsie_page *alloc_vsie_page(struct kvm *kvm)
static int vsie_page_init(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page, unsigned long scb_gpa)
{
+ struct vsie_page *vsie_page_old;
struct kvm *kvm = vcpu->kvm;
int rc;
- if (vsie_page->scb_gpa != ULONG_MAX)
- xa_erase(&kvm->arch.vsie.addr_to_page, vsie_page->scb_gpa >> SCB_ALIGNMENT_SHIFT);
vsie_page->scb_gpa = scb_gpa;
rc = pin_scb(vcpu, vsie_page);
if (rc) {
@@ -1692,8 +1894,18 @@ static int vsie_page_init(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page, un
}
vsie_page->sca_gpa = read_scao(kvm, vsie_page->scb_o);
- WARN_ON_ONCE(xa_insert(&kvm->arch.vsie.addr_to_page, scb_gpa >> SCB_ALIGNMENT_SHIFT,
- vsie_page, GFP_KERNEL_ACCOUNT));
+
+ /*
+ * store the vsie_page in addr_to_page
+ * mind that g2 may have reused the sca - make sure we do not remove the sca from
+ * the new config when reusing the vsie_page_old
+ */
+ vsie_page_old = xa_store(&kvm->arch.vsie.addr_to_page, scb_gpa >> SCB_ALIGNMENT_SHIFT,
+ vsie_page, GFP_KERNEL_ACCOUNT);
+ if (WARN_ON_ONCE(xa_err(vsie_page_old)))
+ return 0;
+ if (vsie_page_old && vsie_page_old != vsie_page)
+ WRITE_ONCE(vsie_page_old->scb_gpa, ULONG_MAX);
return 0;
}
@@ -1794,11 +2006,145 @@ static struct vsie_page *get_vsie_page(struct kvm_vcpu *vcpu, unsigned long addr
return vsie_page;
}
+static struct vsie_page *get_vsie_page_cpu_nr(struct kvm_vcpu *vcpu, struct vsie_sca *vsie_sca,
+ gpa_t scb_gpa, u16 cpu_nr)
+{
+ struct vsie_page *vsie_page, *vsie_page_new = NULL;
+ int rc;
+
+ vsie_page = vsie_sca->pages[cpu_nr];
+ if (!vsie_page) {
+ vsie_page_new = alloc_vsie_page(vcpu->kvm);
+ if (!vsie_page_new)
+ return ERR_PTR(-ENOMEM);
+ vsie_page_new->vsie_sca = vsie_sca;
+ __set_bit(VSIE_PAGE_IN_USE, &vsie_page_new->flags);
+
+ /* be careful to not loose a page here if we raced */
+ scoped_guard(mutex, &vsie_sca->mutex) {
+ vsie_page = vsie_sca->pages[cpu_nr];
+ if (!vsie_page) {
+ WRITE_ONCE(vsie_sca->pages[cpu_nr], vsie_page_new);
+ vsie_page = vsie_page_new;
+ }
+ }
+ }
+ if (vsie_page != vsie_page_new) {
+ if (vsie_page_new)
+ free_vsie_page(vsie_page_new);
+
+ /* not a new vsie_page so get it */
+ if (!try_get_vsie_page(vsie_page))
+ return ERR_PTR(-EAGAIN);
+ vsie_page->vsie_sca = vsie_sca;
+ }
+ if (vsie_page->scb_gpa != scb_gpa || vsie_page->sca_gpa != vsie_sca->sca_gpa) {
+ scoped_guard(mutex, &vcpu->kvm->arch.vsie.mutex) {
+ unpin_scb(vcpu->kvm, vsie_page);
+ rc = vsie_page_init(vcpu, vsie_page, scb_gpa);
+ }
+ if (WARN_ON_ONCE(rc)) {
+ put_vsie_page(vsie_page);
+ return ERR_PTR(rc);
+ }
+ }
+
+ return vsie_page;
+}
+
+static void vsie_sca_update(struct vsie_sca *vsie_sca, unsigned int cpu_nr,
+ struct vsie_page *vsie_page_n, hpa_t sca_o_entry_hpa)
+{
+ guard(mutex)(&vsie_sca->mutex);
+
+ WRITE_ONCE(vsie_sca->ssca.cpu[cpu_nr].ssda, virt_to_phys(&vsie_page_n->scb_s));
+ WRITE_ONCE(vsie_sca->ssca.cpu[cpu_nr].ossea, sca_o_entry_hpa);
+ WRITE_ONCE(vsie_sca->pages[cpu_nr], vsie_page_n);
+}
+
+/* Fill the shadow system control area used for VSIE SIGPI. */
+static int _shadow_sca(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,
+ struct vsie_sca *vsie_sca)
+{
+ bool is_esca = sie_uses_esca(vsie_page->scb_o);
+ unsigned int cpu_nr, cpu_slots;
+ struct vsie_page *vsie_page_n;
+ hpa_t sca_o_entry_hpa;
+ hva_t sca_o_entry_hva;
+ unsigned long *mcn;
+ gpa_t scb_o_gpa;
+ int rc;
+
+ if (is_esca)
+ mcn = phys_to_virt(sca_o_hpa(vsie_sca)) + offsetof(struct esca_block, mcn);
+ else
+ mcn = phys_to_virt(sca_o_hpa(vsie_sca)) + offsetof(struct bsca_block, mcn);
+
+ /* pin and make shadow for ALL scb in the sca */
+ cpu_slots = is_esca ? KVM_S390_MAX_VSIE_VCPUS : KVM_S390_BSCA_CPU_SLOTS;
+ for_each_set_bit_inv(cpu_nr, mcn, cpu_slots) {
+ rc = get_sca_entry_addr(vcpu->kvm, vsie_sca, cpu_nr, NULL, &sca_o_entry_hpa);
+ if (rc)
+ goto err;
+
+ if (vsie_page->scb_o->icpua == cpu_nr) {
+ vsie_sca_update(vsie_sca, cpu_nr, vsie_page, sca_o_entry_hpa);
+ } else {
+ sca_o_entry_hva = (hva_t)phys_to_virt(sca_o_entry_hpa);
+ if (is_esca)
+ scb_o_gpa = ((struct esca_entry *)sca_o_entry_hva)->sda;
+ else
+ scb_o_gpa = ((struct bsca_entry *)sca_o_entry_hva)->sda;
+ if (scb_o_gpa & 0x1ffUL) {
+ rc = -EINVAL;
+ goto err;
+ }
+ vsie_page_n = get_vsie_page_cpu_nr(vcpu, vsie_sca, scb_o_gpa, cpu_nr);
+ if (!vsie_page_n)
+ rc = -EAGAIN;
+ if (IS_ERR(vsie_page_n))
+ rc = PTR_ERR(vsie_page_n);
+ if (rc)
+ goto err;
+ rc = shadow_scb(vcpu, vsie_page_n);
+ vsie_sca_update(vsie_sca, cpu_nr, vsie_page_n, sca_o_entry_hpa);
+ put_vsie_page(vsie_page_n);
+ if (rc)
+ goto err;
+ }
+ }
+ vsie_sca->ssca.osca = sca_o_hpa(vsie_sca);
+
+ return 0;
+
+err:
+ for_each_set_bit_inv(cpu_nr, mcn, cpu_slots) {
+ vsie_sca->ssca.cpu[cpu_nr].ssda = 0;
+ vsie_sca->ssca.cpu[cpu_nr].ossea = 0;
+ }
+ return rc;
+}
+
+/* Shadow or reshadow the SCA on VSIE enter. */
+static int shadow_sca(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page, struct vsie_sca *vsie_sca)
+{
+ int rc = 0;
+
+ guard(rwsem_write)(&vcpu->kvm->arch.vsie.vsie_sca_lock);
+ if (!vsie_sca->ssca.osca)
+ rc = _shadow_sca(vcpu, vsie_page, vsie_sca);
+
+ return rc;
+}
+
int kvm_s390_handle_vsie(struct kvm_vcpu *vcpu)
{
+ struct kvm_s390_sie_block *scb_o;
+ struct vsie_sca *vsie_sca = NULL;
struct vsie_page *vsie_page;
- unsigned long scb_addr;
- int rc;
+ gpa_t scb_addr;
+ hpa_t scb_hpa;
+ int rc = 0;
vcpu->stat.instruction_sie++;
if (!test_kvm_cpu_feat(vcpu->kvm, KVM_S390_VM_CPU_FEAT_SIEF2))
@@ -1818,35 +2164,70 @@ int kvm_s390_handle_vsie(struct kvm_vcpu *vcpu)
return 0;
}
- vsie_page = get_vsie_page(vcpu, scb_addr);
- if (IS_ERR(vsie_page)) {
- return PTR_ERR(vsie_page);
- } else if (!vsie_page) {
+ rc = pin_guest_page(vcpu->kvm, scb_addr, &scb_hpa);
+ if (rc)
+ return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+ scb_o = (struct kvm_s390_sie_block *)phys_to_virt(scb_hpa);
+
+ if (!use_ssca(vcpu->kvm, scb_o)) {
+ /* get the vsie_page with pinned scb_o */
+ vsie_page = get_vsie_page(vcpu, scb_addr);
+ if (IS_ERR(vsie_page)) {
+ rc = PTR_ERR(vsie_page);
+ goto out_unpin;
+ }
+ vsie_page->vsie_sca = NULL;
+ } else {
+ /* get the vsie_sca with pinned original sca */
+ vsie_sca = get_vsie_sca(vcpu, scb_o);
+ if (IS_ERR(vsie_sca)) {
+ rc = PTR_ERR(vsie_sca);
+ goto out_unpin;
+ }
+ vsie_page = get_vsie_page_cpu_nr(vcpu, vsie_sca, scb_addr, scb_o->icpua);
+ if (IS_ERR(vsie_page)) {
+ rc = PTR_ERR(vsie_page);
+ goto out_put_sca;
+ }
+ }
+ if (!vsie_page) {
/* double use of sie control block - simply do nothing */
- kvm_s390_rewind_psw(vcpu, 4);
- return 0;
+ rc = -EAGAIN;
+ goto out_put_sca;
}
- rc = pin_scb(vcpu, vsie_page);
- if (rc)
- goto out_put;
rc = shadow_scb(vcpu, vsie_page);
if (rc)
- goto out_unpin_scb;
+ goto out_put;
+ if (vsie_sca) {
+ /* pin and shadow the sca including all scb_o in the g3 conf */
+ rc = shadow_sca(vcpu, vsie_page, vsie_sca);
+ if (rc)
+ goto out_put;
+ }
+
rc = pin_blocks(vcpu, vsie_page);
if (rc)
goto out_unshadow;
register_shadow_scb(vcpu, vsie_page);
+
rc = vsie_run(vcpu, vsie_page);
+
unregister_shadow_scb(vcpu);
unpin_blocks(vcpu, vsie_page);
out_unshadow:
unshadow_scb(vcpu, vsie_page);
-out_unpin_scb:
- unpin_scb(vcpu->kvm, vsie_page);
out_put:
put_vsie_page(vsie_page);
+out_put_sca:
+ put_vsie_sca(vsie_sca);
+out_unpin:
+ unpin_guest_page(vcpu->kvm, scb_addr, scb_hpa);
+ if (rc == -EAGAIN) {
+ kvm_s390_rewind_psw(vcpu, 4);
+ rc = 0;
+ }
return rc < 0 ? rc : 0;
}
@@ -1855,6 +2236,8 @@ void kvm_s390_vsie_init(struct kvm *kvm)
{
mutex_init(&kvm->arch.vsie.mutex);
xa_init_flags(&kvm->arch.vsie.addr_to_page, XA_FLAGS_ACCOUNT);
+ init_rwsem(&kvm->arch.vsie.vsie_sca_lock);
+ xa_init_flags(&kvm->arch.vsie.osca_to_sca, XA_FLAGS_ACCOUNT);
}
static void kvm_s390_vsie_destroy_page(struct kvm *kvm, struct vsie_page *vsie_page)
@@ -1868,7 +2251,8 @@ static void kvm_s390_vsie_destroy_page(struct kvm *kvm, struct vsie_page *vsie_p
void kvm_s390_vsie_destroy(struct kvm *kvm)
{
struct vsie_page *vsie_page;
- int i;
+ struct vsie_sca *vsie_sca;
+ int i, cpu_nr;
guard(mutex)(&kvm->arch.vsie.mutex);
@@ -1879,7 +2263,27 @@ void kvm_s390_vsie_destroy(struct kvm *kvm)
}
kvm->arch.vsie.page_count = 0;
+ for (i = 0; i < kvm->arch.vsie.sca_count; i++) {
+ vsie_sca = kvm->arch.vsie.scas[i];
+ kvm->arch.vsie.scas[i] = NULL;
+ if (!vsie_sca)
+ continue;
+
+ for (cpu_nr = 0; cpu_nr < KVM_S390_MAX_VSIE_VCPUS; cpu_nr++) {
+ vsie_page = vsie_sca->pages[cpu_nr];
+ vsie_sca->pages[cpu_nr] = NULL;
+ if (!vsie_page)
+ continue;
+ unpin_scb(kvm, vsie_page);
+ kvm_s390_vsie_destroy_page(kvm, vsie_page);
+ }
+
+ unpin_sca(kvm, vsie_sca);
+ free_vsie_sca(kvm, vsie_sca);
+ }
+ kvm->arch.vsie.sca_count = 0;
xa_destroy(&kvm->arch.vsie.addr_to_page);
+ xa_destroy(&kvm->arch.vsie.osca_to_sca);
}
void kvm_s390_vsie_kick(struct kvm_vcpu *vcpu)
--
2.55.0
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v4 15/20] KVM: s390: vsie: Guard against invalid CPU address
2026-08-12 13:52 [PATCH v4 00/20] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
` (13 preceding siblings ...)
2026-08-12 13:52 ` [PATCH v4 14/20] KVM: s390: vsie: Shadow VSIE SCA in guest-1 Christoph Schlameuss
@ 2026-08-12 13:52 ` Christoph Schlameuss
2026-08-12 16:23 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 16/20] KVM: s390: vsie: Allow guest-3 cpu add and remove with ssca Christoph Schlameuss
` (4 subsequent siblings)
19 siblings, 1 reply; 41+ messages in thread
From: Christoph Schlameuss @ 2026-08-12 13:52 UTC (permalink / raw)
To: kvm, linux-s390
Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
Nico Boehr, Paolo Bonzini, Shuah Khan, Sven Schnelle,
Vasily Gorbik, Christoph Schlameuss
Inject a specification interrupt if the CPU address is above 255.
It would be possible to implement a finer check here to guard against
CPU addresses over 63 when a BSCA is used. But this minimal check is
sufficient to guard against out-of-bounds accesses.
The architecture allows CPU addresses up to 255 for ESCA configurations,
so this check ensures compliance with architectural limits.
Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
---
arch/s390/kvm/vsie.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c
index 2fd349e52bd6..154772278d0a 100644
--- a/arch/s390/kvm/vsie.c
+++ b/arch/s390/kvm/vsie.c
@@ -2169,6 +2169,11 @@ int kvm_s390_handle_vsie(struct kvm_vcpu *vcpu)
return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
scb_o = (struct kvm_s390_sie_block *)phys_to_virt(scb_hpa);
+ if (scb_o->icpua >= KVM_S390_MAX_VSIE_VCPUS) {
+ rc = kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+ goto out_unpin;
+ }
+
if (!use_ssca(vcpu->kvm, scb_o)) {
/* get the vsie_page with pinned scb_o */
vsie_page = get_vsie_page(vcpu, scb_addr);
--
2.55.0
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v4 16/20] KVM: s390: vsie: Allow guest-3 cpu add and remove with ssca
2026-08-12 13:52 [PATCH v4 00/20] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
` (14 preceding siblings ...)
2026-08-12 13:52 ` [PATCH v4 15/20] KVM: s390: vsie: Guard against invalid CPU address Christoph Schlameuss
@ 2026-08-12 13:52 ` Christoph Schlameuss
2026-08-12 16:40 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 17/20] KVM: s390: vsie: Add VSIE max shadow configuration Christoph Schlameuss
` (3 subsequent siblings)
19 siblings, 1 reply; 41+ messages in thread
From: Christoph Schlameuss @ 2026-08-12 13:52 UTC (permalink / raw)
To: kvm, linux-s390
Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
Nico Boehr, Paolo Bonzini, Shuah Khan, Sven Schnelle,
Vasily Gorbik, Christoph Schlameuss
As we are shadowing the SCA we need to add and remove the pointers to
the shadowed control blocks and sca entries whenever the mcn /
processors in the configuration changes.
It is not expected that the mcn changes frequently for an already
running guest-3 configuration. So we can simply fully re-init the ssca
whenever the mcn changes.
To detect the mcn change we store the expected mcn in the struct
vsie_sca when running _shadow_sca().
Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
---
arch/s390/kvm/vsie.c | 68 ++++++++++++++++++++++++++++++++++++++++++++--------
1 file changed, 58 insertions(+), 10 deletions(-)
diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c
index 154772278d0a..6088a9c31564 100644
--- a/arch/s390/kvm/vsie.c
+++ b/arch/s390/kvm/vsie.c
@@ -2052,6 +2052,43 @@ static struct vsie_page *get_vsie_page_cpu_nr(struct kvm_vcpu *vcpu, struct vsie
return vsie_page;
}
+/*
+ * Copy the mcn from the osca to the vsie_sca to be able to detect mcn changes later on.
+ *
+ * @vsie_sca: vsie_sca to copy mcn to.
+ * @sca: Pointer to a struct bsca_block or struct esca_block to read from.
+ */
+static void sca_mcn_copy(struct vsie_sca *vsie_sca, void *sca)
+{
+ int offset = offsetof(struct bsca_block, mcn);
+ int size = sizeof(unsigned long);
+
+ if (test_bit(VSIE_SCA_ESCA, &vsie_sca->flags)) {
+ offset = offsetof(struct esca_block, mcn);
+ size = size * 4;
+ }
+ memcpy(&vsie_sca->mcn, sca + offset, size);
+}
+
+/*
+ * Compare the mcn from the given sca to the vsie_sca to be able to detect mcn changes.
+ *
+ * @vsie_sca: vsie_sca to compare mcn to.
+ * @sca: Pointer to a struct bsca_block or struct esca_block to compare to.
+ */
+static bool sca_mcn_equals(struct vsie_sca *vsie_sca, void *sca)
+{
+ int offset = offsetof(struct bsca_block, mcn);
+ int size = sizeof(unsigned long);
+
+ if (test_bit(VSIE_SCA_ESCA, &vsie_sca->flags)) {
+ size = size * 4;
+ offset = offsetof(struct esca_block, mcn);
+ }
+
+ return !memcmp(&vsie_sca->mcn, sca + offset, size);
+}
+
static void vsie_sca_update(struct vsie_sca *vsie_sca, unsigned int cpu_nr,
struct vsie_page *vsie_page_n, hpa_t sca_o_entry_hpa)
{
@@ -2071,18 +2108,16 @@ static int _shadow_sca(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,
struct vsie_page *vsie_page_n;
hpa_t sca_o_entry_hpa;
hva_t sca_o_entry_hva;
- unsigned long *mcn;
gpa_t scb_o_gpa;
int rc;
if (is_esca)
- mcn = phys_to_virt(sca_o_hpa(vsie_sca)) + offsetof(struct esca_block, mcn);
- else
- mcn = phys_to_virt(sca_o_hpa(vsie_sca)) + offsetof(struct bsca_block, mcn);
+ __set_bit(VSIE_SCA_ESCA, &vsie_sca->flags);
+ sca_mcn_copy(vsie_sca, phys_to_virt(sca_o_hpa(vsie_sca)));
/* pin and make shadow for ALL scb in the sca */
cpu_slots = is_esca ? KVM_S390_MAX_VSIE_VCPUS : KVM_S390_BSCA_CPU_SLOTS;
- for_each_set_bit_inv(cpu_nr, mcn, cpu_slots) {
+ for_each_set_bit_inv(cpu_nr, (unsigned long *)&vsie_sca->mcn, cpu_slots) {
rc = get_sca_entry_addr(vcpu->kvm, vsie_sca, cpu_nr, NULL, &sca_o_entry_hpa);
if (rc)
goto err;
@@ -2118,23 +2153,36 @@ static int _shadow_sca(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,
return 0;
err:
- for_each_set_bit_inv(cpu_nr, mcn, cpu_slots) {
+ vsie_sca->ssca.osca = 0;
+ for_each_set_bit_inv(cpu_nr, (unsigned long *)&vsie_sca->mcn, cpu_slots) {
vsie_sca->ssca.cpu[cpu_nr].ssda = 0;
vsie_sca->ssca.cpu[cpu_nr].ossea = 0;
}
return rc;
}
+static bool config_changed(struct vsie_page *vsie_page, struct vsie_sca *vsie_sca)
+{
+ bool changed = !vsie_sca->ssca.osca;
+
+ changed = changed || sie_uses_esca(vsie_page->scb_o) !=
+ test_bit(VSIE_SCA_ESCA, &vsie_sca->flags);
+ return changed || !sca_mcn_equals(vsie_sca, phys_to_virt(sca_o_hpa(vsie_sca)));
+}
+
/* Shadow or reshadow the SCA on VSIE enter. */
static int shadow_sca(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page, struct vsie_sca *vsie_sca)
{
- int rc = 0;
+ scoped_guard(rwsem_read, &vcpu->kvm->arch.vsie.vsie_sca_lock) {
+ if (!config_changed(vsie_page, vsie_sca))
+ return 0;
+ }
guard(rwsem_write)(&vcpu->kvm->arch.vsie.vsie_sca_lock);
- if (!vsie_sca->ssca.osca)
- rc = _shadow_sca(vcpu, vsie_page, vsie_sca);
+ if (!config_changed(vsie_page, vsie_sca))
+ return 0;
- return rc;
+ return _shadow_sca(vcpu, vsie_page, vsie_sca);
}
int kvm_s390_handle_vsie(struct kvm_vcpu *vcpu)
--
2.55.0
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v4 17/20] KVM: s390: vsie: Add VSIE max shadow configuration
2026-08-12 13:52 [PATCH v4 00/20] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
` (15 preceding siblings ...)
2026-08-12 13:52 ` [PATCH v4 16/20] KVM: s390: vsie: Allow guest-3 cpu add and remove with ssca Christoph Schlameuss
@ 2026-08-12 13:52 ` Christoph Schlameuss
2026-08-12 16:53 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 18/20] KVM: s390: vsie: Add VSIE shadow stat counters Christoph Schlameuss
` (2 subsequent siblings)
19 siblings, 1 reply; 41+ messages in thread
From: Christoph Schlameuss @ 2026-08-12 13:52 UTC (permalink / raw)
To: kvm, linux-s390
Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
Nico Boehr, Paolo Bonzini, Shuah Khan, Sven Schnelle,
Vasily Gorbik, Christoph Schlameuss
Introduce two new module parameters allowing to keep more shadow
structures
* vsie_shadow_scb_max
Override the maximum number of VSIE control blocks / vsie_pages to
shadow in guest-1 that are not using the SSCA. KVM will either use
this value or the number of current VCPUs. Either way the number will
be capped to 256. This is the number of guest-3 control blocks / CPUs
to keep shadowed to minimize the repeated shadowing effort.
* vsie_shadow_sca_max
Override the maximum number of VSIE system control areas / SSCAs to
shadow in guest-1. KVM will use a minimum of the current number of
vCPUs and a maximum of 256 or this value if it is lower.
This is the number of guest-3 system control areas / VMs to keep
shadowed to minimize repeated shadowing effort.
Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
---
arch/s390/kvm/vsie.c | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c
index 6088a9c31564..5f6dd4b9e9fd 100644
--- a/arch/s390/kvm/vsie.c
+++ b/arch/s390/kvm/vsie.c
@@ -106,6 +106,16 @@ struct vsie_sca {
*/
static_assert(!(offsetof(struct vsie_sca, ssca)));
+/* maximum vsie shadow scb */
+static unsigned int vsie_shadow_scb_max = 1;
+module_param(vsie_shadow_scb_max, uint, 0644);
+MODULE_PARM_DESC(vsie_shadow_scb_max, "Maximum number of VSIE shadow control blocks to keep. Values smaller number VCPUs uses number of VCPUs; maximum 256");
+
+/* maximum vsie shadow sca */
+static unsigned int vsie_shadow_sca_max = 1;
+module_param(vsie_shadow_sca_max, uint, 0644);
+MODULE_PARM_DESC(vsie_shadow_sca_max, "Maximum number of VSIE shadow system control areas to keep. Values smaller number of VCPUs uses number of VCPUs; maximum 256");
+
static inline hpa_t sca_o_hpa(struct vsie_sca *vsie_sca)
{
return vsie_sca->sca_o_pages[0].hpa | (vsie_sca->sca_gpa & ~PAGE_MASK);
@@ -1063,7 +1073,8 @@ static struct vsie_sca *get_vsie_sca(struct kvm_vcpu *vcpu, struct kvm_s390_sie_
* We want at least #online_vcpus shadows, so every VCPU can execute the
* VSIE in parallel. (Worst case all single core VMs.)
*/
- max_vsie_sca = MIN(atomic_read(&kvm->online_vcpus), KVM_S390_MAX_VSIE_VCPUS);
+ max_vsie_sca = min_t(unsigned int, max_t(unsigned int, atomic_read(&kvm->online_vcpus),
+ vsie_shadow_sca_max), KVM_S390_MAX_VSIE_VCPUS);
if (kvm->arch.vsie.sca_count < max_vsie_sca) {
vsie_sca_new = alloc_vsie_sca();
@@ -1940,7 +1951,8 @@ static struct vsie_page *get_vsie_page(struct kvm_vcpu *vcpu, unsigned long addr
put_vsie_page(vsie_page);
}
- max_vsie_page = atomic_read(&kvm->online_vcpus);
+ max_vsie_page = min_t(unsigned int, max_t(unsigned int, atomic_read(&kvm->online_vcpus),
+ vsie_shadow_scb_max), KVM_S390_MAX_VSIE_VCPUS);
/* allocate new vsie_page - we will likely need it */
if (kvm->arch.vsie.page_count < max_vsie_page) {
--
2.55.0
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v4 18/20] KVM: s390: vsie: Add VSIE shadow stat counters
2026-08-12 13:52 [PATCH v4 00/20] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
` (16 preceding siblings ...)
2026-08-12 13:52 ` [PATCH v4 17/20] KVM: s390: vsie: Add VSIE max shadow configuration Christoph Schlameuss
@ 2026-08-12 13:52 ` Christoph Schlameuss
2026-08-12 17:02 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 19/20] KVM: s390: vsie: Create minimal scb shadows for not running g3 blocks Christoph Schlameuss
2026-08-12 13:52 ` [PATCH v4 20/20] KVM: s390: vsie: Enable use of VSIE SSCA Christoph Schlameuss
19 siblings, 1 reply; 41+ messages in thread
From: Christoph Schlameuss @ 2026-08-12 13:52 UTC (permalink / raw)
To: kvm, linux-s390
Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
Nico Boehr, Paolo Bonzini, Shuah Khan, Sven Schnelle,
Vasily Gorbik, Christoph Schlameuss
Add new stat counters to VSIE shadowing to be able to verify and monitor
the functionality.
* vsie_shadow_scb shows the number of allocated SIE control block
shadows. Should count upwards between 0 and the max number of active
CPUs or configured value.
* vsie_shadow_sca shows the number of allocated system control area
shadows. Should count upwards between 0 and the max number of active
CPUs or configured value.
* vsie_shadow_scb_reuse shows the number of reused SIE control block
shadows.
* vsie_shadow_sca_reuse shows the number of reused system control area
shadows.
Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
---
arch/s390/include/asm/kvm_host.h | 4 ++++
arch/s390/kvm/kvm-s390.c | 4 ++++
arch/s390/kvm/vsie.c | 11 +++++++++++
3 files changed, 19 insertions(+)
diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h
index 3d75731e1e19..6bdfdf95676c 100644
--- a/arch/s390/include/asm/kvm_host.h
+++ b/arch/s390/include/asm/kvm_host.h
@@ -467,6 +467,10 @@ struct kvm_vm_stat {
u64 gmap_shadow_r3_entry;
u64 gmap_shadow_sg_entry;
u64 gmap_shadow_pg_entry;
+ u64 vsie_shadow_scb;
+ u64 vsie_shadow_scb_reuse;
+ u64 vsie_shadow_sca;
+ u64 vsie_shadow_sca_reuse;
};
struct kvm_arch_memory_slot {
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index 8bbd6e6290c0..c21603f2745b 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -86,6 +86,10 @@ const struct kvm_stats_desc kvm_vm_stats_desc[] = {
STATS_DESC_COUNTER(VM, gmap_shadow_r3_entry),
STATS_DESC_COUNTER(VM, gmap_shadow_sg_entry),
STATS_DESC_COUNTER(VM, gmap_shadow_pg_entry),
+ STATS_DESC_COUNTER(VM, vsie_shadow_scb),
+ STATS_DESC_COUNTER(VM, vsie_shadow_scb_reuse),
+ STATS_DESC_COUNTER(VM, vsie_shadow_sca),
+ STATS_DESC_COUNTER(VM, vsie_shadow_sca_reuse),
};
const struct kvm_stats_header kvm_vm_stats_header = {
diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c
index 5f6dd4b9e9fd..a1b679b4aa36 100644
--- a/arch/s390/kvm/vsie.c
+++ b/arch/s390/kvm/vsie.c
@@ -1105,6 +1105,7 @@ static struct vsie_sca *get_vsie_sca(struct kvm_vcpu *vcpu, struct kvm_s390_sie_
kvm->arch.vsie.scas[kvm->arch.vsie.sca_count] = vsie_sca;
kvm->arch.vsie.sca_count++;
atomic_set(&vsie_sca->ref_count, 1);
+ kvm->stat.vsie_shadow_sca++;
} else {
/* reuse previously created vsie_sca allocation for different osca */
vsie_sca = get_reuseable_vsie_sca(kvm);
@@ -1127,6 +1128,7 @@ static struct vsie_sca *get_vsie_sca(struct kvm_vcpu *vcpu, struct kvm_s390_sie_
}
unpin_sca(kvm, vsie_sca);
clear_vsie_sca(vsie_sca);
+ kvm->stat.vsie_shadow_sca_reuse++;
}
if (sie_uses_esca(scb_o))
@@ -1984,6 +1986,7 @@ static struct vsie_page *get_vsie_page(struct kvm_vcpu *vcpu, unsigned long addr
vsie_page_new = NULL;
WRITE_ONCE(kvm->arch.vsie.pages[kvm->arch.vsie.page_count], vsie_page);
kvm->arch.vsie.page_count++;
+ kvm->stat.vsie_shadow_scb++;
} else {
/* reuse an existing entry that belongs to nobody */
while (true) {
@@ -1996,6 +1999,7 @@ static struct vsie_page *get_vsie_page(struct kvm_vcpu *vcpu, unsigned long addr
}
unpin_scb(kvm, vsie_page);
+ kvm->stat.vsie_shadow_scb_reuse++;
}
rc = vsie_page_init(vcpu, vsie_page, addr);
@@ -2022,6 +2026,7 @@ static struct vsie_page *get_vsie_page_cpu_nr(struct kvm_vcpu *vcpu, struct vsie
gpa_t scb_gpa, u16 cpu_nr)
{
struct vsie_page *vsie_page, *vsie_page_new = NULL;
+ bool vsie_page_is_new = true;
int rc;
vsie_page = vsie_sca->pages[cpu_nr];
@@ -2042,6 +2047,7 @@ static struct vsie_page *get_vsie_page_cpu_nr(struct kvm_vcpu *vcpu, struct vsie
}
}
if (vsie_page != vsie_page_new) {
+ vsie_page_is_new = false;
if (vsie_page_new)
free_vsie_page(vsie_page_new);
@@ -2059,6 +2065,11 @@ static struct vsie_page *get_vsie_page_cpu_nr(struct kvm_vcpu *vcpu, struct vsie
put_vsie_page(vsie_page);
return ERR_PTR(rc);
}
+
+ if (vsie_page_is_new)
+ vcpu->kvm->stat.vsie_shadow_scb++;
+ else
+ vcpu->kvm->stat.vsie_shadow_scb_reuse++;
}
return vsie_page;
--
2.55.0
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v4 19/20] KVM: s390: vsie: Create minimal scb shadows for not running g3 blocks
2026-08-12 13:52 [PATCH v4 00/20] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
` (17 preceding siblings ...)
2026-08-12 13:52 ` [PATCH v4 18/20] KVM: s390: vsie: Add VSIE shadow stat counters Christoph Schlameuss
@ 2026-08-12 13:52 ` Christoph Schlameuss
2026-08-12 17:26 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 20/20] KVM: s390: vsie: Enable use of VSIE SSCA Christoph Schlameuss
19 siblings, 1 reply; 41+ messages in thread
From: Christoph Schlameuss @ 2026-08-12 13:52 UTC (permalink / raw)
To: kvm, linux-s390
Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
Nico Boehr, Paolo Bonzini, Shuah Khan, Sven Schnelle,
Vasily Gorbik, Christoph Schlameuss
Instead of creating a full shadow for vSIE control blocks only create a
minimal shadow for SIGP purposes for currently not running guest-3
control blocks. This reduces the overhead to shadow the whole guest-3
configuration.
The minimal shadow only contains the information needed for SIGPI and
SRSI. The shadow will still be recreated fully when a vSIE is entered
with that SCB later.
Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
---
arch/s390/kvm/vsie.c | 54 +++++++++++++++++++++++++++++++++++-----------------
1 file changed, 37 insertions(+), 17 deletions(-)
diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c
index a1b679b4aa36..3a7c58dddf22 100644
--- a/arch/s390/kvm/vsie.c
+++ b/arch/s390/kvm/vsie.c
@@ -365,8 +365,7 @@ static void update_intervention_requests(struct vsie_page *vsie_page)
static int prepare_cpuflags(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
{
struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s;
- struct kvm_s390_sie_block *scb_o = vsie_page->scb_o;
- int newflags, cpuflags = atomic_read(&scb_o->cpuflags);
+ int newflags, cpuflags = atomic_read(&vsie_page->scb_o->cpuflags);
/* we don't allow ESA/390 guests unless explicitly enabled */
if (!(cpuflags & CPUSTAT_ZARCH) && !vcpu->kvm->arch.allow_vsie_esamode)
@@ -709,6 +708,39 @@ static void unshadow_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
scb_o->ihcpu = scb_s->ihcpu;
}
+static int shadow_scb_minimal(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
+{
+ struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s;
+ struct kvm_s390_sie_block *scb_o = vsie_page->scb_o;
+ int rc;
+
+ /* make sure we don't have any leftovers when reusing the scb */
+ scb_s->icptcode = 0;
+ scb_s->eca = 0;
+ scb_s->ecb = 0;
+ scb_s->ecb2 = 0;
+ scb_s->ecb3 = 0;
+ scb_s->ecd = 0;
+ scb_s->fac = 0;
+ scb_s->fpf = 0;
+
+ rc = prepare_cpuflags(vcpu, vsie_page);
+ if (rc)
+ return rc;
+
+ scb_s->icpua = scb_o->icpua;
+ scb_s->ecb2 |= scb_o->ecb2 & ECB2_ESCA;
+
+ if (vsie_page->vsie_sca) {
+ scb_s->eca |= scb_o->eca & ECA_SIGPI;
+ scb_s->ecb |= scb_o->ecb & ECB_SRSI;
+ write_scao(scb_s, virt_to_phys(&vsie_page->vsie_sca->ssca));
+ scb_s->osda = virt_to_phys(scb_o);
+ }
+
+ return 0;
+}
+
/*
* Setup the shadow scb by copying and checking the relevant parts of the g2
* provided scb.
@@ -728,17 +760,7 @@ static int shadow_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
unsigned long new_mso = 0;
int rc;
- /* make sure we don't have any leftovers when reusing the scb */
- scb_s->icptcode = 0;
- scb_s->eca = 0;
- scb_s->ecb = 0;
- scb_s->ecb2 = 0;
- scb_s->ecb3 = 0;
- scb_s->ecd = 0;
- scb_s->fac = 0;
- scb_s->fpf = 0;
-
- rc = prepare_cpuflags(vcpu, vsie_page);
+ rc = shadow_scb_minimal(vcpu, vsie_page);
if (rc)
goto out;
@@ -768,8 +790,6 @@ static int shadow_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
if (!(atomic_read(&scb_s->cpuflags) & CPUSTAT_KSS))
scb_s->ictl |= ICTL_ISKE | ICTL_SSKE | ICTL_RRBE;
- scb_s->icpua = scb_o->icpua;
-
if (!(atomic_read(&scb_s->cpuflags) & CPUSTAT_ZARCH))
new_prefix &= GUEST_PREFIX_MASK_ESA;
else
@@ -2138,7 +2158,7 @@ static int _shadow_sca(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,
__set_bit(VSIE_SCA_ESCA, &vsie_sca->flags);
sca_mcn_copy(vsie_sca, phys_to_virt(sca_o_hpa(vsie_sca)));
- /* pin and make shadow for ALL scb in the sca */
+ /* pin and make minimal shadow for ALL scb in the sca */
cpu_slots = is_esca ? KVM_S390_MAX_VSIE_VCPUS : KVM_S390_BSCA_CPU_SLOTS;
for_each_set_bit_inv(cpu_nr, (unsigned long *)&vsie_sca->mcn, cpu_slots) {
rc = get_sca_entry_addr(vcpu->kvm, vsie_sca, cpu_nr, NULL, &sca_o_entry_hpa);
@@ -2164,7 +2184,7 @@ static int _shadow_sca(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,
rc = PTR_ERR(vsie_page_n);
if (rc)
goto err;
- rc = shadow_scb(vcpu, vsie_page_n);
+ rc = shadow_scb_minimal(vcpu, vsie_page_n);
vsie_sca_update(vsie_sca, cpu_nr, vsie_page_n, sca_o_entry_hpa);
put_vsie_page(vsie_page_n);
if (rc)
--
2.55.0
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v4 20/20] KVM: s390: vsie: Enable use of VSIE SSCA
2026-08-12 13:52 [PATCH v4 00/20] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
` (18 preceding siblings ...)
2026-08-12 13:52 ` [PATCH v4 19/20] KVM: s390: vsie: Create minimal scb shadows for not running g3 blocks Christoph Schlameuss
@ 2026-08-12 13:52 ` Christoph Schlameuss
2026-08-12 17:45 ` sashiko-bot
19 siblings, 1 reply; 41+ messages in thread
From: Christoph Schlameuss @ 2026-08-12 13:52 UTC (permalink / raw)
To: kvm, linux-s390
Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
Nico Boehr, Paolo Bonzini, Shuah Khan, Sven Schnelle,
Vasily Gorbik, Christoph Schlameuss
Finally enable code paths actually configuring and using VSIE SIGPI and
SRSI. SIGP can be globally disabled in the KVM module configuration but
will be enabled by default if the machine (IBM z17 onwards) supports it.
This feature improves guest-3 performance by allowing the hardware to
handle SIGP EXTERNAL_CALL and SENSE_RUNNING_STATUS instructions without
exiting VSIE.
Module parameter: ssca (default: true on supported hardware)
Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
---
arch/s390/kvm/kvm-s390.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index c21603f2745b..59561d459a4f 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -247,6 +247,11 @@ static int async_destroy = 1;
module_param(async_destroy, int, 0444);
MODULE_PARM_DESC(async_destroy, "Asynchronous destroy for protected guests");
+/* allow vsie sigp and srs interpretation if available */
+static bool ssca = true;
+module_param(ssca, bool, 0444);
+MODULE_PARM_DESC(ssca, "VSIE SIGPI and SRSI using the SSCA");
+
#define HMFAI_DWORDS 16
/*
* Base feature mask that defines default mask for facilities. Consists of the
@@ -477,6 +482,8 @@ static void __init kvm_s390_cpu_feat_init(void)
allow_cpu_feat(KVM_S390_VM_CPU_FEAT_KSS);
if (sclp.has_astfleie2)
allow_cpu_feat(KVM_S390_VM_CPU_FEAT_ASTFLEIE2);
+ if (sclp.has_vsie_interp_extf && ssca)
+ allow_cpu_feat(KVM_S390_VM_CPU_FEAT_SIGPIF);
/*
* KVM_S390_VM_CPU_FEAT_SKEY: Wrong shadow of PTE.I bits will make
* all skey handling functions read/set the skey from the PGSTE
@@ -491,9 +498,6 @@ static void __init kvm_s390_cpu_feat_init(void)
* For KVM_S390_VM_CPU_FEAT_SKEY, KVM_S390_VM_CPU_FEAT_CMMA and
* KVM_S390_VM_CPU_FEAT_PFMFI, all PTE.I and PGSTE bits have to be
* correctly shadowed. We can do that for the PGSTE but not for PTE.I.
- *
- * KVM_S390_VM_CPU_FEAT_SIGPIF: Wrong SCB addresses in the SCA. We
- * cannot easily shadow the SCA because of the ipte lock.
*/
}
@@ -3389,7 +3393,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
kvm->arch.use_pfmfi = sclp.has_pfmfi;
kvm->arch.use_skf = sclp.has_skey;
- kvm->arch.use_ssca = sclp.has_vsie_interp_extf;
+ kvm->arch.use_ssca = sclp.has_vsie_interp_extf && ssca;
spin_lock_init(&kvm->arch.start_stop_lock);
kvm_s390_vsie_init(kvm);
if (use_gisa)
--
2.55.0
^ permalink raw reply related [flat|nested] 41+ messages in thread
* Re: [PATCH v4 04/20] KVM: s390: vsie: Add ssca_block and ssca_entry structs
2026-08-12 13:52 ` [PATCH v4 04/20] KVM: s390: vsie: Add ssca_block and ssca_entry structs Christoph Schlameuss
@ 2026-08-12 13:57 ` sashiko-bot
0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-08-12 13:57 UTC (permalink / raw)
To: Christoph Schlameuss
Cc: Alexander Gordeev, Christian Borntraeger, kvm, Heiko Carstens,
linux-s390, Vasily Gorbik
> Add the required guest-1 structures for the ssca to the SIE
> control block for use in later patches.
>
> The shadow SCA features the address of the original SCA as well as an
> entry for each original SIGP entry. The entries contain the addresses of
> the shadow state description and original SIGP entry.
>
> Reviewed-by: Janosch Frank <frankja@linux.ibm.com>
> Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260812-vsie-sigpi-v4-0-f19e49666315@linux.ibm.com?part=4
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v4 01/20] KVM: s390: vsie: Add SCAO read and write helpers
2026-08-12 13:52 ` [PATCH v4 01/20] KVM: s390: vsie: Add SCAO read and write helpers Christoph Schlameuss
@ 2026-08-12 13:58 ` sashiko-bot
0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-08-12 13:58 UTC (permalink / raw)
To: Christoph Schlameuss
Cc: Heiko Carstens, linux-s390, Vasily Gorbik, Christian Borntraeger,
Alexander Gordeev, kvm
> Introduce some small helper functions to get and set the system control
> area origin address from the SIE control block.
>
> Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260812-vsie-sigpi-v4-0-f19e49666315@linux.ibm.com?part=1
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v4 03/20] KVM: s390: vsie: Add vsie_interp_extf detection
2026-08-12 13:52 ` [PATCH v4 03/20] KVM: s390: vsie: Add vsie_interp_extf detection Christoph Schlameuss
@ 2026-08-12 13:58 ` sashiko-bot
0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-08-12 13:58 UTC (permalink / raw)
To: Christoph Schlameuss
Cc: linux-s390, Heiko Carstens, Alexander Gordeev, Vasily Gorbik,
Christian Borntraeger, kvm
> Add sensing of the VSIE Interpretation Extension Facility as
> vsie_interp_extf from SCLP. This facility is introduced with IBM Z
> gen17.
> Along with the new facility add a KVM struct member use_ssca that we
> enable when the vsie_interp_extf is available.
>
> Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260812-vsie-sigpi-v4-0-f19e49666315@linux.ibm.com?part=3
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v4 07/20] KVM: s390: vsie: Move release/acquire gmap shadow
2026-08-12 13:52 ` [PATCH v4 07/20] KVM: s390: vsie: Move release/acquire gmap shadow Christoph Schlameuss
@ 2026-08-12 14:02 ` sashiko-bot
0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-08-12 14:02 UTC (permalink / raw)
To: Christoph Schlameuss
Cc: Alexander Gordeev, Christian Borntraeger, kvm, Vasily Gorbik,
linux-s390, Heiko Carstens
> Move {release,acquire}_gmap_shadow() up in preparation for the next patch.
> No change intended.
>
> Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260812-vsie-sigpi-v4-0-f19e49666315@linux.ibm.com?part=7
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v4 09/20] KVM: s390: vsie: Replace radix_tree with xarray addr_to_page
2026-08-12 13:52 ` [PATCH v4 09/20] KVM: s390: vsie: Replace radix_tree with xarray addr_to_page Christoph Schlameuss
@ 2026-08-12 14:03 ` sashiko-bot
0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-08-12 14:03 UTC (permalink / raw)
To: Christoph Schlameuss
Cc: Vasily Gorbik, Alexander Gordeev, kvm, Christian Borntraeger,
Heiko Carstens, linux-s390
> The underlying implementation of radix_tree is xarray since v4.20.
> Changing to the xarray API directly relaxes some of the locking
> obligations and provides more symmetry with the next vsie changes in
> the series.
>
> Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260812-vsie-sigpi-v4-0-f19e49666315@linux.ibm.com?part=9
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v4 05/20] KVM: s390: vsie: Move pin/unpin_scb methods
2026-08-12 13:52 ` [PATCH v4 05/20] KVM: s390: vsie: Move pin/unpin_scb methods Christoph Schlameuss
@ 2026-08-12 14:03 ` sashiko-bot
0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-08-12 14:03 UTC (permalink / raw)
To: Christoph Schlameuss
Cc: linux-s390, Heiko Carstens, Vasily Gorbik, Alexander Gordeev, kvm,
Christian Borntraeger
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Moving `pin_scb()` and `unpin_scb()` above `pin_guest_page()` and `unpin_guest_page()` breaks the build due to implicit function declarations.
--
--- Patch [5]: [PATCH v4 05/20] KVM: s390: vsie: Move pin/unpin_scb methods ---
commit 9ab8e79b084bfe3f666a241f273dcfef094a8a8b
Author: Christoph Schlameuss <schlameuss@linux.ibm.com>
KVM: s390: vsie: Move pin/unpin_scb methods
This commit moves the pin_scb() and unpin_scb() functions higher up in
arch/s390/kvm/vsie.c in preparation for subsequent patches.
> diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c
> index eea670530acbc..4b67bde0535fc 100644
> --- a/arch/s390/kvm/vsie.c
> +++ b/arch/s390/kvm/vsie.c
> @@ -660,6 +660,39 @@ static int shadow_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
> return rc;
> }
>
> +/* unpin the scb provided by guest 2, marking it as dirty */
> +static void unpin_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,
> + gpa_t gpa)
> +{
> + hpa_t hpa = virt_to_phys(vsie_page->scb_o);
> +
> + if (hpa)
> + unpin_guest_page(vcpu->kvm, gpa, hpa);
[Severity: Low]
Does moving this function up here cause a build failure?
It looks like unpin_guest_page() is defined later in the file. Calling it
here before it is declared or defined might result in an implicit function
declaration error during compilation.
> + vsie_page->scb_o = NULL;
> +}
> +
> +/*
> + * Pin the scb at gpa provided by guest 2 at vsie_page->scb_o.
> + *
> + * Returns: - 0 if the scb was pinned.
> + * - > 0 if control has to be given to guest 2
> + */
> +static int pin_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,
> + gpa_t gpa)
> +{
> + hpa_t hpa;
> + int rc;
> +
> + rc = pin_guest_page(vcpu->kvm, gpa, &hpa);
[Severity: Low]
Similarly, does this call to pin_guest_page() also trigger an implicit
function declaration error, since it appears to be defined further down in
the file without a forward declaration?
> + if (rc) {
> + rc = kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
> + WARN_ON_ONCE(rc);
> + return 1;
> + }
> + vsie_page->scb_o = phys_to_virt(hpa);
> + return 0;
> +}
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260812-vsie-sigpi-v4-0-f19e49666315@linux.ibm.com?part=5
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v4 08/20] KVM: s390: vsie: Create helpers to alloc and free vsie_pages
2026-08-12 13:52 ` [PATCH v4 08/20] KVM: s390: vsie: Create helpers to alloc and free vsie_pages Christoph Schlameuss
@ 2026-08-12 14:04 ` sashiko-bot
0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-08-12 14:04 UTC (permalink / raw)
To: Christoph Schlameuss
Cc: Vasily Gorbik, Alexander Gordeev, Heiko Carstens, linux-s390,
Christian Borntraeger, kvm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] When KVM recycles a `vsie_page` for a nested guest, it only zeros the `scb_s` field, leaving the `crycb` struct retaining data from previous executions. If an L1 guest runs an L2 guest with a format 2 CRYCB, `setup_apcb11()` populates the full `apcb1` array. If the page is subsequently recycled for an L2 guest with a format 0 or 1 CRYCB, `setup_apcb10()` only overwrites the first elements (`apm[0]`, `aqm[0]`, `adm[0]`). Since KVM unconditionally sets `CRYCB_FORMAT2` for the shadow SCB, the hardware reads the full `apcb1` array, unintentionally granting the second nested guest AP (crypto) permissions from the first guest.
--
--- Patch [8]: [PATCH v4 08/20] KVM: s390: vsie: Create helpers to alloc and free vsie_pages ---
commit 14607902bebe42835f13bc9f13b042b6aeeaca0f
Author: Christoph Schlameuss <schlameuss@linux.ibm.com>
KVM: s390: vsie: Create helpers to alloc and free vsie_pages
This commit encapsulates the allocation and freeing of vsie_page
structures into their own helper methods. This refactoring enables these
operations to be called from multiple code paths.
> diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c
> index c935a375f57ca..805d28f0e7058 100644
> --- a/arch/s390/kvm/vsie.c
> +++ b/arch/s390/kvm/vsie.c
[ ... ]
> @@ -1558,9 +1576,9 @@ static struct vsie_page *get_vsie_page(struct kvm *kvm, unsigned long addr)
> if (vsie_page->scb_gpa != ULONG_MAX)
> radix_tree_delete(&kvm->arch.vsie.addr_to_page,
> vsie_page->scb_gpa >> 9);
> + /* Mark it as invalid until it resides in the tree. */
> + vsie_page->scb_gpa = ULONG_MAX;
> }
[Severity: High]
This is a pre-existing issue, but when a vsie_page is recycled here in
get_vsie_page(), only the scb_s field is zeroed later in the function:
arch/s390/kvm/vsie.c:get_vsie_page() {
...
memset(&vsie_page->scb_s, 0, sizeof(struct kvm_s390_sie_block));
...
}
The crycb struct at offset 0x06f8 is left uninitialized and retains data
from previous guests.
If an L1 guest runs an L2 guest with CRYCB_FORMAT2, setup_apcb11()
populates the full apcb1 array. If the page is subsequently recycled by
get_vsie_page() for an L2 guest with CRYCB_FORMAT1, setup_apcb10() only
overwrites the first elements:
arch/s390/kvm/vsie.c:setup_apcb10() {
...
apcb_s->apm[0] = apcb_h->apm[0] & tmp.apm[0];
apcb_s->aqm[0] = apcb_h->aqm[0] & tmp.aqm[0] & 0xffff000000000000UL;
apcb_s->adm[0] = apcb_h->adm[0] & tmp.adm[0] & 0xffff000000000000UL;
...
}
Because shadow_crycb() sets CRYCB_FORMAT2 unconditionally in the shadow SCB:
arch/s390/kvm/vsie.c:shadow_crycb() {
...
scb_s->crycbd = (u32)virt_to_phys(&vsie_page->crycb) | CRYCB_FORMAT2;
...
}
Will the hardware read the full apcb1 array, unintentionally granting the
second nested guest AP (crypto) permissions from the first guest?
> - /* Mark it as invalid until it resides in the tree. */
> - vsie_page->scb_gpa = ULONG_MAX;
>
> /* Double use of the same address or allocation failure. */
> if (radix_tree_insert(&kvm->arch.vsie.addr_to_page, addr >> 9, vsie_page)) {
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260812-vsie-sigpi-v4-0-f19e49666315@linux.ibm.com?part=8
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v4 10/20] KVM: s390: vsie: Add helper to release gmap shadow
2026-08-12 13:52 ` [PATCH v4 10/20] KVM: s390: vsie: Add helper to release gmap shadow Christoph Schlameuss
@ 2026-08-12 14:04 ` sashiko-bot
0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-08-12 14:04 UTC (permalink / raw)
To: Christoph Schlameuss
Cc: kvm, linux-s390, Vasily Gorbik, Heiko Carstens, Alexander Gordeev,
Christian Borntraeger
> Create a helper method release_gmap_shadow_safe() to simplify releasing
> the gmap shadow of a vsie_page.
> Will be called from multiple places in upcoming patches.
>
> Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260812-vsie-sigpi-v4-0-f19e49666315@linux.ibm.com?part=10
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v4 06/20] KVM: s390: vsie: Move pin/unpin guest page
2026-08-12 13:52 ` [PATCH v4 06/20] KVM: s390: vsie: Move pin/unpin guest page Christoph Schlameuss
@ 2026-08-12 14:08 ` sashiko-bot
0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-08-12 14:08 UTC (permalink / raw)
To: Christoph Schlameuss
Cc: Heiko Carstens, Alexander Gordeev, Christian Borntraeger, kvm,
Vasily Gorbik, linux-s390
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [Critical] pin_guest_page() acquires memory using FOLL_GET instead of FOLL_PIN, which is unsafe for hardware-direct memory accesses and exposes the host to the GUP-DMA data corruption bug.
--
--- Patch [6]: [PATCH v4 06/20] KVM: s390: vsie: Move pin/unpin guest page ---
commit 987f41bc833e63c2dc670459a5500894b2f97ace
Author: Christoph Schlameuss <schlameuss@linux.ibm.com>
KVM: s390: vsie: Move pin/unpin guest page
Move {,un}pin_guest_page() up in preparation for the next patch. No change
intended.
> diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c
> index 4b67bde0535fc..1b4f6b1691a27 100644
> --- a/arch/s390/kvm/vsie.c
> +++ b/arch/s390/kvm/vsie.c
[ ... ]
> @@ -150,6 +150,32 @@ static int prefix_is_mapped(struct vsie_page *vsie_page)
> return !(atomic_read(&vsie_page->scb_s.prog20) & PROG_REQUEST);
> }
>
> +/*
> + * Pin the guest page given by gpa and set hpa to the pinned host address.
> + * Will always be pinned writable.
> + *
> + * Returns: - 0 on success
> + * - -EINVAL if the gpa is not valid guest storage
> + */
> +static int pin_guest_page(struct kvm *kvm, gpa_t gpa, hpa_t *hpa)
> +{
> + struct page *page;
> +
> + page = gfn_to_page(kvm, gpa_to_gfn(gpa));
[Severity: Critical]
This isn't a bug introduced by this patch, but is it safe for
pin_guest_page() to acquire memory using gfn_to_page() instead of FOLL_PIN?
Because gfn_to_page() only elevates the page refcount (FOLL_GET),
page_maybe_dma_pinned() evaluates to false. If a guest uses nested
virtualization and its hardware control blocks are backed by a file on the
host, could this expose the host to the GUP-DMA data corruption bug?
When the host filesystem performs writeback, it expects the page to remain
immutable and does not bounce the page. If the SIE hardware acts as an
independent DMA engine bypassing the host MMU, it might continue writing to
the physical memory while the data is in-flight to disk.
Could this cause checksum failures or data corruption in host filesystems
like ext4 or btrfs? Should this be updated to use a FOLL_PIN-enabled helper
(for example, configuring kvm_follow_pfn with .pin = true) to properly lock
the page for hardware access?
> + if (!page)
> + return -EINVAL;
> + *hpa = (hpa_t)page_to_phys(page) + (gpa & ~PAGE_MASK);
> + return 0;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260812-vsie-sigpi-v4-0-f19e49666315@linux.ibm.com?part=6
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v4 12/20] KVM: s390: vsie: Add helper to pin and unpin multiple guest pages
2026-08-12 13:52 ` [PATCH v4 12/20] KVM: s390: vsie: Add helper to pin and unpin multiple guest pages Christoph Schlameuss
@ 2026-08-12 14:11 ` sashiko-bot
0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-08-12 14:11 UTC (permalink / raw)
To: Christoph Schlameuss
Cc: Heiko Carstens, kvm, Christian Borntraeger, linux-s390,
Alexander Gordeev, Vasily Gorbik
> To handle vSIE configurations with an ssca we need to pin and unpin
> multiple consecutive guest-2 pages in guest-1. As these might not be
> consecutive in guest-1 it is necessary to iterate over all pages and
> store guest and host addresses for later use.
>
> Since the new methods use the existing {,un}pin_guest_page() helpers,
> they are moved up unchanged in the file to avoid having to resort to
> forward declarations later on.
>
> Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260812-vsie-sigpi-v4-0-f19e49666315@linux.ibm.com?part=12
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v4 02/20] KVM: s390: vsie: Move SCAO validation into a function
2026-08-12 13:52 ` [PATCH v4 02/20] KVM: s390: vsie: Move SCAO validation into a function Christoph Schlameuss
@ 2026-08-12 15:13 ` sashiko-bot
0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-08-12 15:13 UTC (permalink / raw)
To: Christoph Schlameuss
Cc: Vasily Gorbik, Alexander Gordeev, Christian Borntraeger, kvm,
linux-s390, Heiko Carstens
> Improve readability as well as allow re-use in coming patches.
>
> In particular add the logic to be able to check the validity of BSCA and
> ESCA origin addresses.
>
> Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260812-vsie-sigpi-v4-0-f19e49666315@linux.ibm.com?part=2
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v4 11/20] KVM: s390: vsie: Lazily keep original scb pinned after vsie exit
2026-08-12 13:52 ` [PATCH v4 11/20] KVM: s390: vsie: Lazily keep original scb pinned after vsie exit Christoph Schlameuss
@ 2026-08-12 15:28 ` sashiko-bot
0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-08-12 15:28 UTC (permalink / raw)
To: Christoph Schlameuss
Cc: kvm, Heiko Carstens, Alexander Gordeev, Christian Borntraeger,
linux-s390, Vasily Gorbik
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [High] Ignored `xa_insert` errors in `vsie_page_init` break architectural double-use protection, cause XArray corruption, and expose a guest-triggerable host `WARN_ON_ONCE`.
- [High] Guest-triggerable `PGM_ADDRESSING` exceptions are incorrectly translated into a host `-ENOMEM` error, aborting the VM.
- [Medium] The intended lazy pinning optimization is completely defeated because the SCB is unconditionally unpinned at the end of every VSIE execution.
- [Medium] Non-atomic bitwise operations on `vsie_page->flags` violate documented atomic requirements and create data races.
- [Low] `SCB_ALIGNMENT_SHIFT` is not defined anywhere in the codebase, causing a build failure.
--
--- Patch [11]: [PATCH v4 11/20] KVM: s390: vsie: Lazily keep original scb pinned after vsie exit ---
commit 987277adb095f19cd7aeacd230e5c42d67de0027
Author: Christoph Schlameuss <schlameuss@linux.ibm.com>
Subject: KVM: s390: vsie: Lazily keep original scb pinned after vsie exit
This patch updates the nested virtualization code to cache the pinned
original SCB for a vsie_page. It introduces pinning state flags and delays
unpinning until the vsie_page is reused or the VM is destroyed.
> diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c
> index d2582519b3ecc..490c29b369cd0 100644
> --- a/arch/s390/kvm/vsie.c
> +++ b/arch/s390/kvm/vsie.c
[ ... ]
> @@ -760,14 +761,18 @@ static int shadow_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
> }
>
> /* unpin the scb provided by guest 2, marking it as dirty */
> -static void unpin_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,
> - gpa_t gpa)
> +static void unpin_scb(struct kvm *kvm, struct vsie_page *vsie_page)
> {
> - hpa_t hpa = virt_to_phys(vsie_page->scb_o);
> + hpa_t hpa;
> +
> + if (!test_bit(VSIE_PAGE_SCB_PINNED, &vsie_page->flags))
> + return;
>
> + hpa = virt_to_phys(vsie_page->scb_o);
> if (hpa)
> - unpin_guest_page(vcpu->kvm, gpa, hpa);
> vsie_page->scb_o = NULL;
> + __clear_bit(VSIE_PAGE_SCB_PINNED, &vsie_page->flags);
> }
[Severity: Medium]
Does this introduce a data race on the flags field?
The vsie_page struct documentation states that flags must be modified
atomically. Since other threads might concurrently update the
VSIE_PAGE_IN_USE bit using atomic operations like test_and_set_bit() in
try_get_vsie_page(), using the non-atomic __clear_bit() here could
overwrite those concurrent changes.
> /*
> @@ -776,19 +781,22 @@ static void unpin_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,
> * Returns: - 0 if the scb was pinned.
> * - > 0 if control has to be given to guest 2
> */
> -static int pin_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,
> - gpa_t gpa)
> +static int pin_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
> {
> hpa_t hpa;
> int rc;
>
> - rc = pin_guest_page(vcpu->kvm, gpa, &hpa);
> + if (test_bit(VSIE_PAGE_SCB_PINNED, &vsie_page->flags))
> + return 0;
> +
> + rc = pin_guest_page(vcpu->kvm, vsie_page->scb_gpa, &hpa);
> if (rc) {
> rc = kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
> WARN_ON_ONCE(rc);
> return 1;
> }
> vsie_page->scb_o = phys_to_virt(hpa);
> + __set_bit(VSIE_PAGE_SCB_PINNED, &vsie_page->flags);
> return 0;
> }
[Severity: Medium]
Similar to the unpin_scb() function above, should this be an atomic
set_bit() to avoid racing with concurrent access to vsie_page->flags?
[ ... ]
> @@ -1529,17 +1537,45 @@ static struct vsie_page *alloc_vsie_page(struct kvm *kvm)
> return vsie_page;
> }
>
> +static int vsie_page_init(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page, unsigned long scb_gpa)
> +{
> + struct kvm *kvm = vcpu->kvm;
> + int rc;
> +
> + if (vsie_page->scb_gpa != ULONG_MAX)
> + xa_erase(&kvm->arch.vsie.addr_to_page, vsie_page->scb_gpa >> SCB_ALIGNMENT_SHIFT);
[Severity: Low]
Is SCB_ALIGNMENT_SHIFT defined in this tree?
It doesn't appear to be defined anywhere in the codebase, which would
cause a build failure here and in the xa_insert() and xa_load() calls
below.
> + vsie_page->scb_gpa = scb_gpa;
> + rc = pin_scb(vcpu, vsie_page);
> + if (rc) {
> + vsie_page->scb_gpa = ULONG_MAX;
> + return -ENOMEM;
> + }
[Severity: High]
When pin_scb() fails due to an invalid guest physical address, it
successfully queues a PGM_ADDRESSING exception for the guest and returns
1.
By returning -ENOMEM here, are we incorrectly escalating a guest-level
addressing fault into a host-level memory allocation failure? This will
propagate up to the run loop and abort the VM instead of just delivering
the exception to the guest.
> +
> + vsie_page->sca_gpa = read_scao(kvm, vsie_page->scb_o);
> + WARN_ON_ONCE(xa_insert(&kvm->arch.vsie.addr_to_page, scb_gpa >> SCB_ALIGNMENT_SHIFT,
> + vsie_page, GFP_KERNEL_ACCOUNT));
[Severity: High]
Could ignoring the error from xa_insert() here bypass the architectural
double-use protection?
If two nested vCPUs concurrently execute the SIE instruction with the same
guest SCB address, the second one will fail to insert. By wrapping this in
WARN_ON_ONCE and ignoring the failure, the guest can intentionally trigger
a host warning.
Furthermore, the second vCPU proceeds with the duplicate vsie_page, and
when it is later reused, the xa_erase() at the top of vsie_page_init()
will erase the valid XArray entry belonging to the first vCPU.
> +
> + return 0;
> +}
[ ... ]
> @@ -1552,53 +1588,69 @@ static struct vsie_page *get_vsie_page(struct kvm *kvm, unsigned long addr)
[ ... ]
> + rc = vsie_page_init(vcpu, vsie_page, addr);
> + mutex_unlock(&kvm->arch.vsie.mutex);
> + if (vsie_page_new)
> + free_vsie_page(vsie_page_new);
> + if (WARN_ON_ONCE(rc)) {
> + unpin_scb(kvm, vsie_page);
> + vsie_page->scb_gpa = ULONG_MAX;
> put_vsie_page(vsie_page);
> - mutex_unlock(&kvm->arch.vsie.mutex);
> - return NULL;
> + return ERR_PTR(rc);
> }
[Severity: High]
Following up on the vsie_page_init() return value, this WARN_ON_ONCE will
be triggered if the guest provided an invalid SCB address. Does this
result in a guest triggerable host warning before returning the -ENOMEM
error?
[ ... ]
> @@ -1626,7 +1678,7 @@ int kvm_s390_handle_vsie(struct kvm_vcpu *vcpu)
[ ... ]
> out_unshadow:
> unshadow_scb(vcpu, vsie_page);
> out_unpin_scb:
> - unpin_scb(vcpu, vsie_page, scb_addr);
> + unpin_scb(vcpu->kvm, vsie_page);
> out_put:
> put_vsie_page(vsie_page);
[Severity: Medium]
Will this unconditional call to unpin_scb() at the end of every nested SIE
execution defeat the lazy pinning optimization?
Since every normal exit flows through out_unpin_scb, the
VSIE_PAGE_SCB_PINNED flag is cleared and the SCB is unpinned immediately,
bypassing the intended cache behavior.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260812-vsie-sigpi-v4-0-f19e49666315@linux.ibm.com?part=11
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v4 13/20] KVM: s390: vsie: Add struct vsie_sca with pin and unpin methods
2026-08-12 13:52 ` [PATCH v4 13/20] KVM: s390: vsie: Add struct vsie_sca with pin and unpin methods Christoph Schlameuss
@ 2026-08-12 15:43 ` sashiko-bot
0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-08-12 15:43 UTC (permalink / raw)
To: Christoph Schlameuss
Cc: kvm, Heiko Carstens, Christian Borntraeger, Vasily Gorbik,
linux-s390, Alexander Gordeev
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Guest-triggerable WARN_ON_ONCE and implicit cast of negative error code to unsigned int in pin_sca().
--
--- Patch [13]: [PATCH v4 13/20] KVM: s390: vsie: Add struct vsie_sca with pin and unpin methods ---
commit ba3e6e1a8cfd384294d05c97179c56428f5bc9cf
Author: Christoph Schlameuss <schlameuss@linux.ibm.com>
KVM: s390: vsie: Add struct vsie_sca with pin and unpin methods
Introduce the struct vsie_sca to hold the SSCA and required management
data along with alloc and free methods to use with the struct. On this
basis we can also add methods {,un}pin_sca() to simplify the pinning
SCAs in g1.
> diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c
> index 442e7997ee51b..e632311222002 100644
> --- a/arch/s390/kvm/vsie.c
> +++ b/arch/s390/kvm/vsie.c
[ ... ]
> @@ -845,6 +872,74 @@ static int pin_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
> return 0;
> }
>
> +/*
> + * Unpin g2 original sca in g1 memory.
> + *
> + * Called with vsie_sca_lock held.
> + */
> +static void unpin_sca(struct kvm *kvm, struct vsie_sca *vsie_sca)
[ ... ]
> +/*
> + * Pin g2 original sca in g1 memory.
> + *
> + * Called with vsie_sca_lock held.
> + */
> +static int pin_sca(struct kvm *kvm, struct vsie_sca *vsie_sca)
> +{
> + bool is_esca = test_bit(VSIE_SCA_ESCA, &vsie_sca->flags);
> + gpa_t offset = vsie_sca->sca_gpa & ~PAGE_MASK;
> + int nr_pages;
> +
> + if (test_bit(VSIE_SCA_SCA_PINNED, &vsie_sca->flags))
> + return 0;
> +
> + if (is_esca) {
> + nr_pages = 4;
> + if (offset + sizeof(struct esca_block) > 4 * PAGE_SIZE)
> + nr_pages = 5;
> + } else {
> + nr_pages = 1;
> + if (offset + sizeof(struct bsca_block) > PAGE_SIZE)
> + nr_pages = 2;
> + }
> +
> + vsie_sca->sca_o_nr_pages = pin_guest_pages(kvm, vsie_sca->sca_gpa, nr_pages,
> + vsie_sca->sca_o_pages);
> + if (WARN_ON_ONCE(vsie_sca->sca_o_nr_pages != nr_pages))
> + return -EIO;
[Severity: High]
Can a malicious guest trigger this WARN_ON_ONCE?
If the guest provides an invalid or unmapped physical address for the SCA,
pin_guest_pages() can fail and return a negative error code (like -EINVAL).
Since vsie_sca->sca_o_nr_pages is declared as an unsigned int, the negative
error code is implicitly cast to a large unsigned value. This causes the
subsequent comparison vsie_sca->sca_o_nr_pages != nr_pages to evaluate to true,
triggering the WARN_ON_ONCE.
Because guest-reachable warnings can cause a host panic when panic_on_warn is
enabled, should this code gracefully handle the error code from
pin_guest_pages() and return it without warning?
> + __set_bit(VSIE_SCA_SCA_PINNED, &vsie_sca->flags);
> +
> + return 0;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260812-vsie-sigpi-v4-0-f19e49666315@linux.ibm.com?part=13
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v4 14/20] KVM: s390: vsie: Shadow VSIE SCA in guest-1
2026-08-12 13:52 ` [PATCH v4 14/20] KVM: s390: vsie: Shadow VSIE SCA in guest-1 Christoph Schlameuss
@ 2026-08-12 16:10 ` sashiko-bot
0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-08-12 16:10 UTC (permalink / raw)
To: Christoph Schlameuss
Cc: Heiko Carstens, Alexander Gordeev, Christian Borntraeger, kvm,
Vasily Gorbik, linux-s390
Thank you for your contribution! Sashiko AI review found 10 potential issue(s) to consider:
- [Critical] Reusing a `vsie_sca` unconditionally zeroes and unpins `vsie_page` structures that may be actively executing in hardware via a concurrent non-SCA VSIE run.
- [Critical] Reusing a `vsie_page` for a different SCB without resetting the shadow control block leads to cross-VM state leakage.
- [Critical] Unvalidated guest-controlled `icpua` field leads to out-of-bounds array indexing, enabling massive out-of-bounds host memory accesses.
- [High] VSIE error paths improperly convert guest-level validity intercepts and addressing exceptions into fatal host-level negative errors, allowing a nested guest to abort the host VM.
- [High] The shadow SCA is never linked to the shadow SIE control block, completely breaking the VSIE-Interpretation-Extension feature.
- [High] A Time-Of-Check to Time-Of-Use (TOCTOU) vulnerability exists when iterating over the guest-modifiable `mcn` mask, allowing malicious guests to evade error cleanup and leave dangling host pointers in the shadow SCA.
- [High] An unbounded memory leak of XArray nodes occurs because stale mappings are never erased from `addr_to_page`.
- [High] Failing to pin a reused `vsie_sca` leaves a dangling `sca_gpa` that causes erroneous XArray erasure upon the next reuse attempt, corrupting the SCA lookup cache.
- [Medium] Overwriting `vsie_page_old->scb_gpa` with `ULONG_MAX` causes KVM to pass a bogus GPA to `mark_page_dirty()`, silently losing dirty tracking for the guest SCB.
- [Medium] `shadow_sca()` fails to detect guest modifications to the SCA CPU mask (`mcn`) after the first shadow, breaking dynamic CPU hotplug in the nested guest.
--
--- Patch [14]: [PATCH v4 14/20] KVM: s390: vsie: Shadow VSIE SCA in guest-1 ---
commit 4635296fb400229acd33f3f98111bf5ac95e07d2
Author: Christoph Schlameuss <schlameuss@linux.ibm.com>
KVM: s390: vsie: Shadow VSIE SCA in guest-1
Restructure kvm_s390_handle_vsie() to create a guest-1 shadow of the SCA
if guest-2 attempts to enter SIE with an SCA. When active, shadow SCAs and
CPUs are created to allow direct machine monitoring of intervention bits,
reducing VM exits.
> diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c
> index e632311222002..2fd349e52bd6d 100644
> --- a/arch/s390/kvm/vsie.c
> +++ b/arch/s390/kvm/vsie.c
[ ... ]
> +static struct vsie_sca *get_vsie_sca(struct kvm_vcpu *vcpu, struct kvm_s390_sie_block *scb_o)
> +{
> + struct vsie_sca *vsie_sca, *vsie_sca_new = NULL;
[ ... ]
> + if (vsie_sca_new && kvm->arch.vsie.sca_count < max_vsie_sca) {
> + /* make use of vsie_sca just created */
> + vsie_sca = vsie_sca_new;
> + vsie_sca_new = NULL;
> +
> + kvm->arch.vsie.scas[kvm->arch.vsie.sca_count] = vsie_sca;
> + kvm->arch.vsie.sca_count++;
> + atomic_set(&vsie_sca->ref_count, 1);
> + } else {
> + /* reuse previously created vsie_sca allocation for different osca */
> + vsie_sca = get_reuseable_vsie_sca(kvm);
> + /* with nr_vcpus scas one must be reusable */
> + if (IS_ERR(vsie_sca))
> + goto out;
> + WARN_ON_ONCE(atomic_read(&vsie_sca->ref_count) != 1);
> +
> + xa_erase(&kvm->arch.vsie.osca_to_sca, vsie_sca->sca_gpa >> SCA_ALIGNMENT_SHIFT);
> + for (cpu_nr = 0; cpu_nr < KVM_S390_MAX_VSIE_VCPUS; cpu_nr++) {
> + vsie_page_n = vsie_sca->pages[cpu_nr];
> + if (!vsie_page_n)
> + continue;
> +
> + /* unpin but keep the vsie_page for reuse */
> + unpin_scb(kvm, vsie_page_n);
> + release_gmap_shadow_safe(kvm, vsie_page_n);
> + memset(vsie_page_n, 0, sizeof(struct vsie_page));
> + vsie_page_n->scb_gpa = ULONG_MAX;
[Severity: Critical]
Does this unconditionally zero and unpin a vsie_page that might be actively
executing in hardware via a concurrent non-SCA VSIE run?
If a guest uses the same SCB address in two different virtual CPUs, one vCPU
might enter VSIE without an SCA and claim the vsie_page without incrementing
vsie_sca->ref_count. Could this reuse path then destroy the memory while the
first vCPU is actively running it?
> + }
> + unpin_sca(kvm, vsie_sca);
> + clear_vsie_sca(vsie_sca);
> + }
> +
> + if (sie_uses_esca(scb_o))
> + __set_bit(VSIE_SCA_ESCA, &vsie_sca->flags);
> + vsie_sca->sca_gpa = sca_gpa;
> +
> + /*
> + * The pinned original sca will only be unpinned lazily to limit the
> + * required amount of pins/unpins on each vsie entry/exit.
> + * The unpin is done in the reuse vsie_sca allocation path above and
> + * kvm_s390_vsie_destroy().
> + */
> + rc = pin_sca(kvm, vsie_sca);
> + if (rc) {
> + put_vsie_sca(vsie_sca);
> + vsie_sca = ERR_PTR(rc);
> + goto out;
[Severity: High]
Does failing to pin a reused vsie_sca leave a dangling sca_gpa that causes an
erroneous XArray erasure on the next reuse?
The sca_gpa retains the failing address when it is released to the pool. When
get_reuseable_vsie_sca() picks it up again, it appears it will call xa_erase()
on the old sca_gpa, potentially deleting a valid entry stored by another
thread.
> + }
> +
> + WARN_ON_ONCE(xa_store(&kvm->arch.vsie.osca_to_sca,
> + vsie_sca->sca_gpa >> SCA_ALIGNMENT_SHIFT, vsie_sca, GFP_KERNEL));
> +
> +out:
> + up_write(&kvm->arch.vsie.vsie_sca_lock);
> + if (vsie_sca_new)
> + free_vsie_sca(kvm, vsie_sca_new);
> + return vsie_sca;
> +}
[ ... ]
> static int vsie_page_init(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page, unsigned long scb_gpa)
> {
> + struct vsie_page *vsie_page_old;
> struct kvm *kvm = vcpu->kvm;
> int rc;
>
> - if (vsie_page->scb_gpa != ULONG_MAX)
> - xa_erase(&kvm->arch.vsie.addr_to_page, vsie_page->scb_gpa >> SCB_ALIGNMENT_SHIFT);
[Severity: High]
Are old scb_gpa mappings permanently leaking in the XArray here?
By removing the xa_erase() call, a guest executing VSIE in a tight loop with
varying scb_addrs might force KVM to recycle vsie_pages while continuously
accumulating stale entries in addr_to_page, eventually exhausting host memory.
> -
> + vsie_page->scb_gpa = scb_gpa;
> + rc = pin_scb(vcpu, vsie_page);
> + if (rc) {
> + vsie_page->scb_gpa = ULONG_MAX;
> + return -ENOMEM;
> + }
[Severity: High]
Can this return a fatal host-level error for a guest-level addressing
exception?
If a nested guest enters VSIE with an invalid scb_o address, pin_scb() returns
1. By converting this and returning -ENOMEM here, does this allow a nested
guest to abort the host VM instead of correctly injecting a program interrupt
into the guest?
>
> vsie_page->sca_gpa = read_scao(kvm, vsie_page->scb_o);
> - WARN_ON_ONCE(xa_insert(&kvm->arch.vsie.addr_to_page, scb_gpa >> SCB_ALIGNMENT_SHIFT,
> - vsie_page, GFP_KERNEL_ACCOUNT));
> +
> + /*
> + * store the vsie_page in addr_to_page
> + * mind that g2 may have reused the sca - make sure we do not remove the sca from
> + * the new config when reusing the vsie_page_old
> + */
> + vsie_page_old = xa_store(&kvm->arch.vsie.addr_to_page, scb_gpa >> SCB_ALIGNMENT_SHIFT,
> + vsie_page, GFP_KERNEL_ACCOUNT);
> + if (WARN_ON_ONCE(xa_err(vsie_page_old)))
> + return 0;
> + if (vsie_page_old && vsie_page_old != vsie_page)
> + WRITE_ONCE(vsie_page_old->scb_gpa, ULONG_MAX);
[Severity: Medium]
Does overwriting the old vsie_page's scb_gpa with ULONG_MAX silently lose
dirty tracking?
When this evicted page is later unpinned via unpin_scb(), it will call
mark_page_dirty() using ULONG_MAX instead of the actual guest physical
address of the SCB, losing the migration state.
>
> return 0;
> }
[ ... ]
> +static struct vsie_page *get_vsie_page_cpu_nr(struct kvm_vcpu *vcpu, struct vsie_sca *vsie_sca,
> + gpa_t scb_gpa, u16 cpu_nr)
> +{
> + struct vsie_page *vsie_page, *vsie_page_new = NULL;
> + int rc;
> +
> + vsie_page = vsie_sca->pages[cpu_nr];
[Severity: Critical]
Does this use an unvalidated guest-controlled icpua field directly as an array
index?
Since cpu_nr comes from scb_o->icpua without prior bounds checking against
KVM_S390_MAX_VSIE_VCPUS in kvm_s390_handle_vsie(), a malicious guest could
provide an out-of-bounds value (up to 65535), leading to massive out-of-bounds
read/write on the kernel heap.
> + if (!vsie_page) {
> + vsie_page_new = alloc_vsie_page(vcpu->kvm);
[ ... ]
> + if (vsie_page->scb_gpa != scb_gpa || vsie_page->sca_gpa != vsie_sca->sca_gpa) {
> + scoped_guard(mutex, &vcpu->kvm->arch.vsie.mutex) {
> + unpin_scb(vcpu->kvm, vsie_page);
> + rc = vsie_page_init(vcpu, vsie_page, scb_gpa);
> + }
> + if (WARN_ON_ONCE(rc)) {
> + put_vsie_page(vsie_page);
> + return ERR_PTR(rc);
> + }
> + }
[Severity: Critical]
When reusing a vsie_page for a different SCB, does this lead to cross-VM state
leakage?
By updating the pinning via vsie_page_init() but omitting critical state reset
steps like memset(&vsie_page->scb_s, 0, ...), release_gmap_shadow_safe(), and
prefix_unmapped(), this might leave old shadow control block state intact and
stale GMAP translations active from a different nested VM.
> +
> + return vsie_page;
> +}
[ ... ]
> +static int _shadow_sca(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,
> + struct vsie_sca *vsie_sca)
> +{
> + bool is_esca = sie_uses_esca(vsie_page->scb_o);
> + unsigned int cpu_nr, cpu_slots;
> + struct vsie_page *vsie_page_n;
> + hpa_t sca_o_entry_hpa;
> + hva_t sca_o_entry_hva;
> + unsigned long *mcn;
> + gpa_t scb_o_gpa;
> + int rc;
> +
> + if (is_esca)
> + mcn = phys_to_virt(sca_o_hpa(vsie_sca)) + offsetof(struct esca_block, mcn);
> + else
> + mcn = phys_to_virt(sca_o_hpa(vsie_sca)) + offsetof(struct bsca_block, mcn);
> +
> + /* pin and make shadow for ALL scb in the sca */
> + cpu_slots = is_esca ? KVM_S390_MAX_VSIE_VCPUS : KVM_S390_BSCA_CPU_SLOTS;
> + for_each_set_bit_inv(cpu_nr, mcn, cpu_slots) {
> + rc = get_sca_entry_addr(vcpu->kvm, vsie_sca, cpu_nr, NULL, &sca_o_entry_hpa);
> + if (rc)
> + goto err;
[ ... ]
> + vsie_sca->ssca.osca = sca_o_hpa(vsie_sca);
> +
> + return 0;
> +
> +err:
> + for_each_set_bit_inv(cpu_nr, mcn, cpu_slots) {
> + vsie_sca->ssca.cpu[cpu_nr].ssda = 0;
> + vsie_sca->ssca.cpu[cpu_nr].ossea = 0;
> + }
[Severity: High]
Is there a TOCTOU race when iterating over the guest-modifiable mcn mask here?
Since mcn is read directly from guest memory twice, could a malicious guest
toggle bits between the initialization loop and this error cleanup loop? By
unsetting a bit before the error loop, the guest could force KVM to skip
clearing the ssda for that CPU, leaving a valid physical pointer to a shadow
SCB inside the active shadow SCA.
> + return rc;
> +}
> +
> +/* Shadow or reshadow the SCA on VSIE enter. */
> +static int shadow_sca(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page, struct vsie_sca *vsie_sca)
> +{
> + int rc = 0;
> +
> + guard(rwsem_write)(&vcpu->kvm->arch.vsie.vsie_sca_lock);
> + if (!vsie_sca->ssca.osca)
> + rc = _shadow_sca(vcpu, vsie_page, vsie_sca);
[Severity: Medium]
Does this fail to detect guest modifications to the SCA CPU mask after the
first shadow?
If the guest dynamically updates the CPU mask (e.g., setting a new bit in mcn
to bring up a new guest-3 vCPU) and re-enters VSIE, KVM will immediately
return without updating the shadow SCA since ssca.osca is already populated.
Could this break dynamic CPU hotplug for nested guests?
> +
> + return rc;
> +}
> +
> int kvm_s390_handle_vsie(struct kvm_vcpu *vcpu)
> {
[ ... ]
>
> - rc = pin_scb(vcpu, vsie_page);
> - if (rc)
> - goto out_put;
> rc = shadow_scb(vcpu, vsie_page);
> if (rc)
> - goto out_unpin_scb;
> + goto out_put;
[Severity: High]
Does the shadow SCA need to be linked to the shadow SIE control block during
shadow_scb() setup?
If write_scao() is not called for the active VCPU within shadow_scb() (and
pin_blocks() also skips setting scaol when vsie_page->vsie_sca is present), it
appears scb_s->scaol will remain 0, which would break the newly added
VSIE-Interpretation-Extension feature.
> + if (vsie_sca) {
> + /* pin and shadow the sca including all scb_o in the g3 conf */
> + rc = shadow_sca(vcpu, vsie_page, vsie_sca);
> + if (rc)
> + goto out_put;
> + }
> +
> rc = pin_blocks(vcpu, vsie_page);
> if (rc)
> goto out_unshadow;
> register_shadow_scb(vcpu, vsie_page);
> +
> rc = vsie_run(vcpu, vsie_page);
> +
> unregister_shadow_scb(vcpu);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260812-vsie-sigpi-v4-0-f19e49666315@linux.ibm.com?part=14
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v4 15/20] KVM: s390: vsie: Guard against invalid CPU address
2026-08-12 13:52 ` [PATCH v4 15/20] KVM: s390: vsie: Guard against invalid CPU address Christoph Schlameuss
@ 2026-08-12 16:23 ` sashiko-bot
0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-08-12 16:23 UTC (permalink / raw)
To: Christoph Schlameuss
Cc: linux-s390, Heiko Carstens, Alexander Gordeev,
Christian Borntraeger, Vasily Gorbik, kvm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] The newly added bounds check for `scb_o->icpua` is completely ineffective due to a signed-integer mismatch and a Time-of-Check to Time-of-Use (TOCTOU) race condition. A malicious guest can bypass this check, leading to a massive out-of-bounds array access and severe hypervisor memory corruption.
--
--- Patch [15]: [PATCH v4 15/20] KVM: s390: vsie: Guard against invalid CPU address ---
commit b018701df60566888acc3dc5acc8b060071a7256
Author: Christoph Schlameuss <schlameuss@linux.ibm.com>
KVM: s390: vsie: Guard against invalid CPU address
Inject a specification interrupt if the CPU address is above 255.
This adds a check to ensure compliance with architectural limits and
guard against out-of-bounds accesses.
> diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c
> --- a/arch/s390/kvm/vsie.c
> +++ b/arch/s390/kvm/vsie.c
> @@ -2169,6 +2169,11 @@ int kvm_s390_handle_vsie(struct kvm_vcpu *vcpu)
> return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
> scb_o = (struct kvm_s390_sie_block *)phys_to_virt(scb_hpa);
>
> + if (scb_o->icpua >= KVM_S390_MAX_VSIE_VCPUS) {
[Severity: Critical]
Is it possible for icpua to be a negative value? If icpua is a signed 16-bit
integer, a negative value would bypass this check.
Additionally, since scb_o resides in guest memory, is this check subject to a
Time-of-Check to Time-of-Use (TOCTOU) race? A guest could potentially mutate
icpua after this check passes but before it is fetched again.
For example, could the guest change the value in memory before the secondary
fetch occurs later in kvm_s390_handle_vsie():
vsie_page = get_vsie_page_cpu_nr(vcpu, vsie_sca, scb_addr, scb_o->icpua);
If a bypassed or mutated value is implicitly cast to a large unsigned index
in get_vsie_page_cpu_nr(), could this result in an out-of-bounds read and
write on vsie_sca->pages[cpu_nr]?
> + rc = kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
> + goto out_unpin;
> + }
> +
> if (!use_ssca(vcpu->kvm, scb_o)) {
> /* get the vsie_page with pinned scb_o */
> vsie_page = get_vsie_page(vcpu, scb_addr);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260812-vsie-sigpi-v4-0-f19e49666315@linux.ibm.com?part=15
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v4 16/20] KVM: s390: vsie: Allow guest-3 cpu add and remove with ssca
2026-08-12 13:52 ` [PATCH v4 16/20] KVM: s390: vsie: Allow guest-3 cpu add and remove with ssca Christoph Schlameuss
@ 2026-08-12 16:40 ` sashiko-bot
0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-08-12 16:40 UTC (permalink / raw)
To: Christoph Schlameuss
Cc: Heiko Carstens, kvm, linux-s390, Alexander Gordeev,
Christian Borntraeger, Vasily Gorbik
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Critical] The main CPU's `vsie_page` is freed twice during `kvm_s390_vsie_destroy()`, leading to a fatal host double-free vulnerability.
- [High] Removed CPUs are not cleared from the shadow system control area (SSCA) during configuration changes, leaving stale active entries in hardware.
- [High] Failure to clear the VSIE_SCA_ESCA flag when transitioning from ESCA to BSCA causes memory corruption and infinite re-shadow loops.
- [High] Dynamically changing the SCA format from BSCA to ESCA fails to repin memory, triggering a guest-controllable host WARN_ON_ONCE.
--
--- Patch [16]: [PATCH v4 16/20] KVM: s390: vsie: Allow guest-3 cpu add and remove with ssca ---
commit 73a8d58ca743bc97299afb560aeb506eff16cf44
Author: Christoph Schlameuss <schlameuss@linux.ibm.com>
KVM: s390: vsie: Allow guest-3 cpu add and remove with ssca
As we are shadowing the SCA we need to add and remove the pointers to
the shadowed control blocks and sca entries whenever the mcn /
processors in the configuration changes.
It is not expected that the mcn changes frequently for an already
running guest-3 configuration. So we can simply fully re-init the ssca
whenever the mcn changes.
> --- a/arch/s390/kvm/vsie.c
> +++ b/arch/s390/kvm/vsie.c
[ ... ]
> @@ -2071,18 +2108,16 @@ static int _shadow_sca(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,
> struct vsie_page *vsie_page_n;
> hpa_t sca_o_entry_hpa;
> hva_t sca_o_entry_hva;
> - unsigned long *mcn;
> gpa_t scb_o_gpa;
> int rc;
>
> if (is_esca)
> - mcn = phys_to_virt(sca_o_hpa(vsie_sca)) + offsetof(struct esca_block, mcn);
> - else
> - mcn = phys_to_virt(sca_o_hpa(vsie_sca)) + offsetof(struct bsca_block, mcn);
> + __set_bit(VSIE_SCA_ESCA, &vsie_sca->flags);
[Severity: High]
If the guest switches its SCA format from ESCA to BSCA, is_esca will
be false, but does anything clear the VSIE_SCA_ESCA flag?
If the flag remains set, it seems subsequent functions like
sca_mcn_copy() and get_sca_entry_addr() will incorrectly parse the BSCA
using ESCA offsets. Could this lead to memory corruption or infinite
re-shadowing loops since config_changed() would always return true?
[Severity: High]
When dynamically switching from BSCA to ESCA format, does the system
need to repin memory for the additional SCA pages?
If a guest starts with a BSCA (which pins 1-2 pages) and dynamically
switches to ESCA while keeping the same SCA address, the VSIE_SCA_ESCA
flag is set here but the additional pages (up to 5) are not pinned.
Could this cause get_sca_entry_addr() to calculate a page index that
exceeds sca_o_nr_pages:
get_sca_entry_addr() {
...
if (WARN_ON_ONCE(pn >= vsie_sca->sca_o_nr_pages))
return -EINVAL;
...
}
tripping the guest-controllable host WARN_ON_ONCE at the bounds check?
> + sca_mcn_copy(vsie_sca, phys_to_virt(sca_o_hpa(vsie_sca)));
>
> /* pin and make shadow for ALL scb in the sca */
> cpu_slots = is_esca ? KVM_S390_MAX_VSIE_VCPUS : KVM_S390_BSCA_CPU_SLOTS;
> - for_each_set_bit_inv(cpu_nr, mcn, cpu_slots) {
> + for_each_set_bit_inv(cpu_nr, (unsigned long *)&vsie_sca->mcn, cpu_slots) {
[Severity: High]
Does this loop leave stale active entries in the shadow system control
area (SSCA) when CPUs are removed?
If an unprivileged guest modifies the MCN mask to remove CPUs, this loop
only iterates over the currently set bits in the new MCN mask.
Since the SSCA entries for the removed CPUs are not cleared, won't
hardware continue delivering SIGPIs to these stale entries because it
uses a non-zero ssda to identify active CPUs?
[Severity: Critical]
Does storing the main CPU's vsie_page during this loop lead to a double-free
during VM destruction?
When vsie_page->scb_o->icpua == cpu_nr, vsie_sca_update() is called with
the vsie_page. This vsie_page is already tracked in kvm->arch.vsie.pages[],
and vsie_sca_update() also adds it to vsie_sca->pages[cpu_nr].
When the VM is destroyed, kvm_s390_vsie_destroy() frees all pages in
kvm->arch.vsie.pages[], and then iterates over vsie_sca->pages[], freeing
those pages too. Will this blindly free the same main CPU's page twice?
> rc = get_sca_entry_addr(vcpu->kvm, vsie_sca, cpu_nr, NULL, &sca_o_entry_hpa);
> if (rc)
> goto err;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260812-vsie-sigpi-v4-0-f19e49666315@linux.ibm.com?part=16
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v4 17/20] KVM: s390: vsie: Add VSIE max shadow configuration
2026-08-12 13:52 ` [PATCH v4 17/20] KVM: s390: vsie: Add VSIE max shadow configuration Christoph Schlameuss
@ 2026-08-12 16:53 ` sashiko-bot
0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-08-12 16:53 UTC (permalink / raw)
To: Christoph Schlameuss
Cc: Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
Christian Borntraeger, kvm, linux-s390
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] A `vsie_page` actively in use by a non-SCA VSIE execution can be corrupted (zeroed out) if its previously associated `vsie_sca` is concurrently selected for reuse, leading to host memory corruption.
--
--- Patch [17]: [PATCH v4 17/20] KVM: s390: vsie: Add VSIE max shadow configuration ---
commit a1cb743a25bd4d366ca8bd61632e7dad29b9214d
Author: Christoph Schlameuss <schlameuss@linux.ibm.com>
KVM: s390: vsie: Add VSIE max shadow configuration
This commit introduces two new module parameters, vsie_shadow_scb_max and
vsie_shadow_sca_max. These parameters allow overriding the maximum number of
shadow control blocks and system control areas kept by KVM to minimize
repeated shadowing efforts.
> diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c
> index 6088a9c31564f..5f6dd4b9e9fd7 100644
> --- a/arch/s390/kvm/vsie.c
> +++ b/arch/s390/kvm/vsie.c
[ ... ]
> @@ -1063,7 +1073,8 @@ static struct vsie_sca *get_vsie_sca(struct kvm_vcpu *vcpu, struct kvm_s390_sie_
> * We want at least #online_vcpus shadows, so every VCPU can execute the
> * VSIE in parallel. (Worst case all single core VMs.)
> */
> - max_vsie_sca = MIN(atomic_read(&kvm->online_vcpus), KVM_S390_MAX_VSIE_VCPUS);
> + max_vsie_sca = min_t(unsigned int, max_t(unsigned int, atomic_read(&kvm->online_vcpus),
> + vsie_shadow_sca_max), KVM_S390_MAX_VSIE_VCPUS);
>
> if (kvm->arch.vsie.sca_count < max_vsie_sca) {
> vsie_sca_new = alloc_vsie_sca();
[Severity: Critical]
Can a vsie_page actively in use by a non-SCA VSIE execution be corrupted
(zeroed out) if its previously associated vsie_sca is concurrently selected
for reuse?
When a guest VCPU executes a VSIE instruction with an SCA, KVM saves a pointer
to the allocated vsie_page in vsie_sca->pages[cpu_nr] during
get_vsie_page_cpu_nr().
If another VCPU later executes a non-SCA VSIE using the same SCB address,
it fetches this same vsie_page, sets vsie_page->vsie_sca = NULL in
kvm_s390_handle_vsie(), and enters guest execution. The page pointer remains in
vsie_sca->pages.
Concurrently, if the guest exhausts the max_vsie_sca limit (which can now be
influenced by vsie_shadow_sca_max), KVM will recycle the original vsie_sca.
In get_vsie_sca(), this reuse path iterates over vsie_sca->pages:
arch/s390/kvm/vsie.c:get_vsie_sca() {
...
vsie_sca = get_reuseable_vsie_sca(kvm);
...
for (cpu_nr = 0; cpu_nr < KVM_S390_MAX_VSIE_VCPUS; cpu_nr++) {
vsie_page_n = vsie_sca->pages[cpu_nr];
if (!vsie_page_n)
continue;
unpin_scb(kvm, vsie_page_n);
release_gmap_shadow_safe(kvm, vsie_page_n);
memset(vsie_page_n, 0, sizeof(struct vsie_page));
...
}
...
}
Does this unconditional memset() corrupt the vsie_page while it is actively
being used by the non-SCA VSIE execution, without checking if it is currently
in use (VSIE_PAGE_IN_USE)?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260812-vsie-sigpi-v4-0-f19e49666315@linux.ibm.com?part=17
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v4 18/20] KVM: s390: vsie: Add VSIE shadow stat counters
2026-08-12 13:52 ` [PATCH v4 18/20] KVM: s390: vsie: Add VSIE shadow stat counters Christoph Schlameuss
@ 2026-08-12 17:02 ` sashiko-bot
0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-08-12 17:02 UTC (permalink / raw)
To: Christoph Schlameuss
Cc: Vasily Gorbik, Alexander Gordeev, Christian Borntraeger, kvm,
Heiko Carstens, linux-s390
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The `vsie_shadow_scb` statistic undercounts allocated shadow SCB pages if their initialization fails, and incorrectly categorizes subsequent uses as reuses.
--
--- Patch [18]: [PATCH v4 18/20] KVM: s390: vsie: Add VSIE shadow stat counters ---
commit 30e90ae4cab73ef72cfd191918cda52ff46f4c0c
Author: Christoph Schlameuss <schlameuss@linux.ibm.com>
KVM: s390: vsie: Add VSIE shadow stat counters
This commit adds new statistics counters to track the allocation and reuse of
VSIE shadow SCBs and SCAs.
> diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c
> index 5f6dd4b9e9fd7..a1b679b4aa361 100644
> --- a/arch/s390/kvm/vsie.c
> +++ b/arch/s390/kvm/vsie.c
[ ... ]
> @@ -2042,6 +2047,7 @@ static struct vsie_page *get_vsie_page_cpu_nr(struct kvm_vcpu *vcpu, struct vsie
> }
> }
> if (vsie_page != vsie_page_new) {
> + vsie_page_is_new = false;
> if (vsie_page_new)
> free_vsie_page(vsie_page_new);
>
> @@ -2059,6 +2065,11 @@ static struct vsie_page *get_vsie_page_cpu_nr(struct kvm_vcpu *vcpu, struct vsie
> put_vsie_page(vsie_page);
> return ERR_PTR(rc);
> }
> +
> + if (vsie_page_is_new)
> + vcpu->kvm->stat.vsie_shadow_scb++;
> + else
> + vcpu->kvm->stat.vsie_shadow_scb_reuse++;
> }
>
> return vsie_page;
[Severity: Medium]
Will this lead to an undercount of vsie_shadow_scb allocations if
vsie_page_init() fails?
If vsie_page_init() fails in get_vsie_page_cpu_nr(), it returns ERR_PTR(rc)
and skips the stat increment here.
However, the newly allocated page is already saved in vsie_sca->pages[cpu_nr]
earlier in the function. The next time this page is fetched from the array,
vsie_page_is_new will evaluate to false, causing vsie_shadow_scb_reuse to be
incremented instead of counting the initial allocation.
Should the vsie_shadow_scb counter be incremented earlier upon successful
allocation, or should the page be removed from vsie_sca->pages[cpu_nr] on
error?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260812-vsie-sigpi-v4-0-f19e49666315@linux.ibm.com?part=18
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v4 19/20] KVM: s390: vsie: Create minimal scb shadows for not running g3 blocks
2026-08-12 13:52 ` [PATCH v4 19/20] KVM: s390: vsie: Create minimal scb shadows for not running g3 blocks Christoph Schlameuss
@ 2026-08-12 17:26 ` sashiko-bot
0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-08-12 17:26 UTC (permalink / raw)
To: Christoph Schlameuss
Cc: linux-s390, Vasily Gorbik, Christian Borntraeger, kvm,
Alexander Gordeev, Heiko Carstens
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
New issues:
- [High] Silent failure / Lost Validity Intercept on SCA Shadowing
- [High] Cross-VM Data Leak / Stale TLB due to missing ihcpu initialization
- [Medium] L2 Guest State Leak due to Uninitialized Shadow SCB
Pre-existing issues:
- [High] Guest State Corruption in unshadow_scb when prepare_cpuflags fails
--
--- Patch [19]: [PATCH v4 19/20] KVM: s390: vsie: Create minimal scb shadows for not running g3 blocks ---
commit 5e715443b75203daf2ad2801d214c07f27b51bf4
Author: Christoph Schlameuss <schlameuss@linux.ibm.com>
Subject: KVM: s390: vsie: Create minimal scb shadows for not running g3 blocks
Instead of creating a full shadow for vSIE control blocks, this commit creates
a minimal shadow for SIGP purposes for currently not running guest-3 control
blocks. This reduces the overhead of shadowing the entire guest-3 configuration.
The shadow will be fully recreated later when a vSIE is entered with that SCB.
> diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c
> index a1b679b4aa361..3a7c58dddf22d 100644
> --- a/arch/s390/kvm/vsie.c
> +++ b/arch/s390/kvm/vsie.c
> @@ -709,6 +708,39 @@ static void unshadow_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
> scb_o->ihcpu = scb_s->ihcpu;
> }
>
> +static int shadow_scb_minimal(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
> +{
> + struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s;
> + struct kvm_s390_sie_block *scb_o = vsie_page->scb_o;
> + int rc;
> +
> + /* make sure we don't have any leftovers when reusing the scb */
> + scb_s->icptcode = 0;
> + scb_s->eca = 0;
> + scb_s->ecb = 0;
> + scb_s->ecb2 = 0;
> + scb_s->ecb3 = 0;
> + scb_s->ecd = 0;
> + scb_s->fac = 0;
> + scb_s->fpf = 0;
[Severity: Medium]
Does this limited clearing leave stale L2 execution state in the shadow SCB?
When get_vsie_page_cpu_nr() recycles a vsie_page, it doesn't clear the shadow
SCB with memset().
If shadow_scb_minimal() only clears these specific fields, could uninitialized
data from a previous nested guest (such as ipa and ipb) leak back to the L1
hypervisor's memory when unshadow_scb() is called during an intercept?
[Severity: High]
Does this missing initialization also cause a stale TLB issue for the recycled
SCB?
Because get_vsie_page_cpu_nr() fails to set vsie_page->scb_s.ihcpu = 0xffffU,
and shadow_scb_minimal() doesn't clear it either, a recycled SCB could retain a
stale ihcpu value.
If the L1 guest provides an scb_o->ihcpu that matches the host CPU, could
hardware skip the TLB flush for the shadow SCB, leading to cross-VM memory
access?
[ ... ]
> @@ -728,17 +760,7 @@ static int shadow_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
> unsigned long new_mso = 0;
> int rc;
>
> - /* make sure we don't have any leftovers when reusing the scb */
> - scb_s->icptcode = 0;
> - scb_s->eca = 0;
> - scb_s->ecb = 0;
> - scb_s->ecb2 = 0;
> - scb_s->ecb3 = 0;
> - scb_s->ecd = 0;
> - scb_s->fac = 0;
> - scb_s->fpf = 0;
> -
> - rc = prepare_cpuflags(vcpu, vsie_page);
> + rc = shadow_scb_minimal(vcpu, vsie_page);
> if (rc)
> goto out;
[Severity: High]
This is a pre-existing issue, but does unshadow_scb() corrupt the L2 guest
state if prepare_cpuflags() fails?
In shadow_scb(), prepare_cpuflags() is evaluated (via shadow_scb_minimal)
before the L2 guest state is copied from scb_o to scb_s:
shadow_scb() {
rc = shadow_scb_minimal(vcpu, vsie_page);
if (rc)
goto out;
// guest state is copied to scb_s here
...
out:
if (rc)
unshadow_scb(vcpu, vsie_page);
}
If prepare_cpuflags() fails, shadow_scb() jumps to out and calls
unshadow_scb(), which unconditionally overwrites the L2 guest state in scb_o
with the data from scb_s.
Since scb_s hasn't been populated with the guest state yet, does this corrupt
the L2 guest's state variables (CPU timer, clock comparator, PSW, etc.) in the
L1 memory?
[ ... ]
> @@ -2164,7 +2184,7 @@ static int _shadow_sca(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,
> rc = PTR_ERR(vsie_page_n);
> if (rc)
> goto err;
> - rc = shadow_scb(vcpu, vsie_page_n);
> + rc = shadow_scb_minimal(vcpu, vsie_page_n);
> vsie_sca_update(vsie_sca, cpu_nr, vsie_page_n, sca_o_entry_hpa);
> put_vsie_page(vsie_page_n);
> if (rc)
[Severity: High]
Can this error path bypass the validity intercept delivery?
In _shadow_sca(), if shadow_scb_minimal() fails for a secondary vCPU, the
function propagates the error without unshadowing:
_shadow_sca() {
...
rc = shadow_scb_minimal(vcpu, vsie_page_n);
vsie_sca_update(vsie_sca, cpu_nr, vsie_page_n, sca_o_entry_hpa);
put_vsie_page(vsie_page_n);
if (rc)
goto err;
...
}
This error is returned to kvm_s390_handle_vsie(), which then jumps to out_put:
kvm_s390_handle_vsie() {
...
rc = shadow_sca(vcpu, vsie_page, vsie_sca);
if (rc)
goto out_put;
...
out_put:
put_vsie_page(vsie_page);
...
}
Because this bypasses out_unshadow for the primary vCPU, does this silently
drop the ICPT_VALIDITY intercept code, causing the L1 hypervisor's SIE
instruction to appear as if it completed successfully when it actually failed?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260812-vsie-sigpi-v4-0-f19e49666315@linux.ibm.com?part=19
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v4 20/20] KVM: s390: vsie: Enable use of VSIE SSCA
2026-08-12 13:52 ` [PATCH v4 20/20] KVM: s390: vsie: Enable use of VSIE SSCA Christoph Schlameuss
@ 2026-08-12 17:45 ` sashiko-bot
0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-08-12 17:45 UTC (permalink / raw)
To: Christoph Schlameuss
Cc: Alexander Gordeev, kvm, Heiko Carstens, linux-s390, Vasily Gorbik,
Christian Borntraeger
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Critical] Guest-2 can trigger an out-of-bounds array access (both read and write) in host kernel memory by supplying a malicious `icpua` value.
- [High] The newly introduced VSIE SCA shadowing code contains multiple WARN_ON_ONCE assertions that can be triggered by a malicious guest, leading to a host kernel panic.
- [High] VSIE unconditionally allows nested guests to use SSCA (SIGPI and SRSI interpretation) if the host supports it, bypassing the guest-2's negotiated CPU model capabilities.
- [High] Guest-triggerable QEMU/VM abort via invalid or unaligned SCA entry addresses.
--
--- Patch [20]: [PATCH v4 20/20] KVM: s390: vsie: Enable use of VSIE SSCA ---
commit 3bbd1f1a442d7f9a9ee468b6c61e02cb8392ba96
Author: Christoph Schlameuss <schlameuss@linux.ibm.com>
KVM: s390: vsie: Enable use of VSIE SSCA
This commit enables code paths configuring and using VSIE SIGPI and SRSI.
It adds a new module parameter to globally disable SIGP, but defaults to
enabled on supported hardware to improve guest-3 performance.
> diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
> index c21603f2745bf..59561d459a4f3 100644
> --- a/arch/s390/kvm/kvm-s390.c
> +++ b/arch/s390/kvm/kvm-s390.c
[ ... ]
> @@ -477,6 +482,8 @@ static void __init kvm_s390_cpu_feat_init(void)
> allow_cpu_feat(KVM_S390_VM_CPU_FEAT_KSS);
> if (sclp.has_astfleie2)
> allow_cpu_feat(KVM_S390_VM_CPU_FEAT_ASTFLEIE2);
> + if (sclp.has_vsie_interp_extf && ssca)
> + allow_cpu_feat(KVM_S390_VM_CPU_FEAT_SIGPIF);
[Severity: High]
Does this globally enable SSCA for the VM without verifying if the guest was
provisioned with the feature?
Looking at use_ssca() in arch/s390/kvm/vsie.c, it only checks the host-level
flag kvm->arch.use_ssca:
arch/s390/kvm/vsie.c:use_ssca() {
if (!kvm->arch.use_ssca)
return false;
...
}
Would this allow a nested guest to use SSCA features even if QEMU explicitly
disabled KVM_S390_VM_CPU_FEAT_SIGPIF for the CPU model, potentially breaking
capability negotiation and live migration?
[ ... ]
> @@ -3389,7 +3393,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
>
> kvm->arch.use_pfmfi = sclp.has_pfmfi;
> kvm->arch.use_skf = sclp.has_skey;
> - kvm->arch.use_ssca = sclp.has_vsie_interp_extf;
> + kvm->arch.use_ssca = sclp.has_vsie_interp_extf && ssca;
[Severity: Critical]
Is there a potential out-of-bounds array access when validating the guest's
icpua index now that these paths are being used?
In kvm_s390_handle_vsie(), the bounds check compares scb_o->icpua against
KVM_S390_MAX_VSIE_VCPUS:
arch/s390/kvm/vsie.c:kvm_s390_handle_vsie() {
if (scb_o->icpua >= KVM_S390_MAX_VSIE_VCPUS) {
rc = kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
goto out_unpin;
}
...
}
Since icpua is a signed 16-bit integer, could a negative value bypass this
check?
Additionally, scb_o resides in guest memory. Is there a TOCTOU race where the
guest could change icpua after the check, before it is read again for the
array index in get_vsie_page_cpu_nr()?
arch/s390/kvm/vsie.c:get_vsie_page_cpu_nr() {
...
vsie_page = vsie_sca->pages[cpu_nr];
...
WRITE_ONCE(vsie_sca->pages[cpu_nr], vsie_page_new);
}
Could this lead to host memory corruption if an untrusted guest supplies a
malicious index?
[Severity: High]
Could enabling these code paths allow a guest to trigger host kernel panics
via WARN_ON_ONCE assertions?
For example, if a guest configures a nested guest with an unmapped SCA address,
pin_guest_pages() or pin_scb() would return a negative error code (like
-EFAULT). This error is assigned to sca_o_nr_pages and triggers a warning:
arch/s390/kvm/vsie.c:pin_sca() {
...
if (WARN_ON_ONCE(vsie_sca->sca_o_nr_pages != nr_pages))
...
}
Similar warnings exist in get_vsie_page_cpu_nr() and get_vsie_page() when
vsie_page_init() fails. Since KVM guidelines advise against guest-reachable
WARN_ON assertions, should these be converted to avoid host Denial of Service?
[Severity: High]
Could this also allow a guest to crash the hypervisor by supplying invalid
nested SCB addresses?
In _shadow_sca(), if the guest provides an unaligned address, it returns
-EINVAL directly:
arch/s390/kvm/vsie.c:_shadow_sca() {
if (scb_o_gpa & 0x1ffUL) {
rc = -EINVAL;
goto err;
}
...
}
If the address is unmapped, get_vsie_page_cpu_nr() fails and returns -ENOMEM.
These error codes appear to propagate up to the main instruction intercept
handler. Will this cause KVM to exit to userspace with the error, aborting
the QEMU process instead of injecting an architectural program exception into
the guest?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260812-vsie-sigpi-v4-0-f19e49666315@linux.ibm.com?part=20
^ permalink raw reply [flat|nested] 41+ messages in thread
end of thread, other threads:[~2026-08-12 17:45 UTC | newest]
Thread overview: 41+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-12 13:52 [PATCH v4 00/20] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
2026-08-12 13:52 ` [PATCH v4 01/20] KVM: s390: vsie: Add SCAO read and write helpers Christoph Schlameuss
2026-08-12 13:58 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 02/20] KVM: s390: vsie: Move SCAO validation into a function Christoph Schlameuss
2026-08-12 15:13 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 03/20] KVM: s390: vsie: Add vsie_interp_extf detection Christoph Schlameuss
2026-08-12 13:58 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 04/20] KVM: s390: vsie: Add ssca_block and ssca_entry structs Christoph Schlameuss
2026-08-12 13:57 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 05/20] KVM: s390: vsie: Move pin/unpin_scb methods Christoph Schlameuss
2026-08-12 14:03 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 06/20] KVM: s390: vsie: Move pin/unpin guest page Christoph Schlameuss
2026-08-12 14:08 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 07/20] KVM: s390: vsie: Move release/acquire gmap shadow Christoph Schlameuss
2026-08-12 14:02 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 08/20] KVM: s390: vsie: Create helpers to alloc and free vsie_pages Christoph Schlameuss
2026-08-12 14:04 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 09/20] KVM: s390: vsie: Replace radix_tree with xarray addr_to_page Christoph Schlameuss
2026-08-12 14:03 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 10/20] KVM: s390: vsie: Add helper to release gmap shadow Christoph Schlameuss
2026-08-12 14:04 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 11/20] KVM: s390: vsie: Lazily keep original scb pinned after vsie exit Christoph Schlameuss
2026-08-12 15:28 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 12/20] KVM: s390: vsie: Add helper to pin and unpin multiple guest pages Christoph Schlameuss
2026-08-12 14:11 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 13/20] KVM: s390: vsie: Add struct vsie_sca with pin and unpin methods Christoph Schlameuss
2026-08-12 15:43 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 14/20] KVM: s390: vsie: Shadow VSIE SCA in guest-1 Christoph Schlameuss
2026-08-12 16:10 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 15/20] KVM: s390: vsie: Guard against invalid CPU address Christoph Schlameuss
2026-08-12 16:23 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 16/20] KVM: s390: vsie: Allow guest-3 cpu add and remove with ssca Christoph Schlameuss
2026-08-12 16:40 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 17/20] KVM: s390: vsie: Add VSIE max shadow configuration Christoph Schlameuss
2026-08-12 16:53 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 18/20] KVM: s390: vsie: Add VSIE shadow stat counters Christoph Schlameuss
2026-08-12 17:02 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 19/20] KVM: s390: vsie: Create minimal scb shadows for not running g3 blocks Christoph Schlameuss
2026-08-12 17:26 ` sashiko-bot
2026-08-12 13:52 ` [PATCH v4 20/20] KVM: s390: vsie: Enable use of VSIE SSCA Christoph Schlameuss
2026-08-12 17:45 ` sashiko-bot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.