Kernel KVM virtualization development
 help / color / mirror / Atom feed
* [PATCH v5 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation
@ 2026-08-26 15:05 Christoph Schlameuss
  2026-08-26 15:05 ` [PATCH v5 01/21] KVM: s390: vsie: Add SCAO read and write helpers Christoph Schlameuss
                   ` (20 more replies)
  0 siblings, 21 replies; 43+ messages in thread
From: Christoph Schlameuss @ 2026-08-26 15:05 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, Sven Schnelle, Vasily Gorbik, Paolo Bonzini,
	Shuah Khan, Sean Christopherson, 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

**Performance Expectations**
SIGP sense running instructions perform about −97% faster without the
intercept. External call is harder to measure but about the same
(faster) on the sending side.
Entry/exit are within +-4% variation against the prior state.
Only tested on shared systems so far, so the numbers are to be taken
with caution.

**Limitations**
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 v5:
- Rebased to current master already containing dependency
- Dropped "Guard against invalid CPU address" patch, replaced with
  masking icpua usages - upper bits of icpua do not need to be checked
- Switched patch order 05 <-> 06 as un/pin_scb() depends on
  un/pin_guest_pages()
- Fix data race on flags fields (reported by sashiko)
- Remove addr_to_page usage on ssca path - vsie_pages are held in
  vsie_sca
- Change pin_guest_pages() to rc and out-parameter for clarity
- Inject intercept into current cpu on bad secondary cpu flags
- Harmonized method names (reset_vsie_page, init_vsie_page)
- Extracted _shadow_sca() to make _shadow_sca_cpu() to make easier to
  read
- Fixed non ssca path loosing scao on reentry
- Link to v4: https://patch.msgid.link/20260812-vsie-sigpi-v4-0-f19e49666315@linux.ibm.com

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

To: kvm@vger.kernel.org
To: linux-s390@vger.kernel.org
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: Sven Schnelle <svens@linux.ibm.com>
Cc: Vasily Gorbik <gor@linux.ibm.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Sean Christopherson <seanjc@google.com>

---
Christoph Schlameuss (21):
      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 guest page
      KVM: s390: vsie: Move pin/unpin_scb methods
      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: Refactor kvm_s390_vsie_destroy and extract reusable methods
      KVM: s390: vsie: Add helper reset_vsie_page() and unshadow_intercept()
      KVM: s390: vsie: Add helper unshadow_intercept()
      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: 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_s390.h          |   28 +-
 arch/s390/include/asm/kvm_host_s390_types.h    |   28 +-
 arch/s390/include/asm/sclp.h                   |    1 +
 arch/s390/kvm/s390/s390.c                      |   15 +-
 arch/s390/kvm/s390/vsie.c                      | 1311 +++++++++++++++++++-----
 drivers/s390/char/sclp_early.c                 |    1 +
 tools/testing/selftests/kvm/include/s390/sie.h |    2 +-
 7 files changed, 1111 insertions(+), 275 deletions(-)
---
base-commit: 45c13f3f9e3bb15fd89ff2864c6f627a3b4b4229
change-id: 20251113-vsie-sigpi-3de01243efd6

Best regards,
--  
Christoph Schlameuss <schlameuss@linux.ibm.com>


^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH v5 01/21] KVM: s390: vsie: Add SCAO read and write helpers
  2026-08-26 15:05 [PATCH v5 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
@ 2026-08-26 15:05 ` Christoph Schlameuss
  2026-08-26 15:12   ` sashiko-bot
  2026-08-26 15:05 ` [PATCH v5 02/21] KVM: s390: vsie: Move SCAO validation into a function Christoph Schlameuss
                   ` (19 subsequent siblings)
  20 siblings, 1 reply; 43+ messages in thread
From: Christoph Schlameuss @ 2026-08-26 15:05 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, Sven Schnelle, Vasily Gorbik, Paolo Bonzini,
	Shuah Khan, Sean Christopherson, 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/s390/vsie.c | 26 +++++++++++++++++++-------
 1 file changed, 19 insertions(+), 7 deletions(-)

diff --git a/arch/s390/kvm/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
index 5926689fa226..61096ee991c8 100644
--- a/arch/s390/kvm/s390/vsie.c
+++ b/arch/s390/kvm/s390/vsie.c
@@ -71,6 +71,22 @@ static_assert(sizeof(struct vsie_page) == PAGE_SIZE);
 static_assert(offsetof(struct vsie_page, mcck_info) == offsetof(struct sie_page, mcck_info));
 static_assert(IS_ALIGNED(offsetof(struct vsie_page, crycb), 8));
 
+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] 43+ messages in thread

* [PATCH v5 02/21] KVM: s390: vsie: Move SCAO validation into a function
  2026-08-26 15:05 [PATCH v5 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
  2026-08-26 15:05 ` [PATCH v5 01/21] KVM: s390: vsie: Add SCAO read and write helpers Christoph Schlameuss
@ 2026-08-26 15:05 ` Christoph Schlameuss
  2026-08-26 15:15   ` sashiko-bot
  2026-08-26 15:05 ` [PATCH v5 03/21] KVM: s390: vsie: Add vsie_interp_extf detection Christoph Schlameuss
                   ` (18 subsequent siblings)
  20 siblings, 1 reply; 43+ messages in thread
From: Christoph Schlameuss @ 2026-08-26 15:05 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, Sven Schnelle, Vasily Gorbik, Paolo Bonzini,
	Shuah Khan, Sean Christopherson, 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/s390/vsie.c | 42 ++++++++++++++++++++++++++++++------------
 1 file changed, 30 insertions(+), 12 deletions(-)

diff --git a/arch/s390/kvm/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
index 61096ee991c8..04cb38aad33c 100644
--- a/arch/s390/kvm/s390/vsie.c
+++ b/arch/s390/kvm/s390/vsie.c
@@ -71,6 +71,11 @@ static_assert(sizeof(struct vsie_page) == PAGE_SIZE);
 static_assert(offsetof(struct vsie_page, mcck_info) == offsetof(struct sie_page, mcck_info));
 static_assert(IS_ALIGNED(offsetof(struct vsie_page, crycb), 8));
 
+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;
@@ -97,6 +102,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] 43+ messages in thread

* [PATCH v5 03/21] KVM: s390: vsie: Add vsie_interp_extf detection
  2026-08-26 15:05 [PATCH v5 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
  2026-08-26 15:05 ` [PATCH v5 01/21] KVM: s390: vsie: Add SCAO read and write helpers Christoph Schlameuss
  2026-08-26 15:05 ` [PATCH v5 02/21] KVM: s390: vsie: Move SCAO validation into a function Christoph Schlameuss
@ 2026-08-26 15:05 ` Christoph Schlameuss
  2026-08-26 15:12   ` sashiko-bot
  2026-08-26 15:05 ` [PATCH v5 04/21] KVM: s390: vsie: Add ssca_block and ssca_entry structs Christoph Schlameuss
                   ` (17 subsequent siblings)
  20 siblings, 1 reply; 43+ messages in thread
From: Christoph Schlameuss @ 2026-08-26 15:05 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, Sven Schnelle, Vasily Gorbik, Paolo Bonzini,
	Shuah Khan, Sean Christopherson, 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_s390.h | 1 +
 arch/s390/include/asm/sclp.h          | 1 +
 arch/s390/kvm/s390/s390.c             | 1 +
 drivers/s390/char/sclp_early.c        | 1 +
 4 files changed, 4 insertions(+)

diff --git a/arch/s390/include/asm/kvm_host_s390.h b/arch/s390/include/asm/kvm_host_s390.h
index cd692f8fb764..6fdac144ca3a 100644
--- a/arch/s390/include/asm/kvm_host_s390.h
+++ b/arch/s390/include/asm/kvm_host_s390.h
@@ -623,6 +623,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 44066550b9b1..9e960c09ce0b 100644
--- a/arch/s390/include/asm/sclp.h
+++ b/arch/s390/include/asm/sclp.h
@@ -105,6 +105,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/s390/s390.c b/arch/s390/kvm/s390/s390.c
index b0839e887221..ae0177d74b11 100644
--- a/arch/s390/kvm/s390/s390.c
+++ b/arch/s390/kvm/s390/s390.c
@@ -3359,6 +3359,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] 43+ messages in thread

* [PATCH v5 04/21] KVM: s390: vsie: Add ssca_block and ssca_entry structs
  2026-08-26 15:05 [PATCH v5 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (2 preceding siblings ...)
  2026-08-26 15:05 ` [PATCH v5 03/21] KVM: s390: vsie: Add vsie_interp_extf detection Christoph Schlameuss
@ 2026-08-26 15:05 ` Christoph Schlameuss
  2026-08-26 15:09   ` sashiko-bot
  2026-08-26 15:05 ` [PATCH v5 05/21] KVM: s390: vsie: Move pin/unpin guest page Christoph Schlameuss
                   ` (16 subsequent siblings)
  20 siblings, 1 reply; 43+ messages in thread
From: Christoph Schlameuss @ 2026-08-26 15:05 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, Sven Schnelle, Vasily Gorbik, Paolo Bonzini,
	Shuah Khan, Sean Christopherson, 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_s390_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_s390_types.h b/arch/s390/include/asm/kvm_host_s390_types.h
index 9e348a530421..0b69e71bc8ee 100644
--- a/arch/s390/include/asm/kvm_host_s390_types.h
+++ b/arch/s390/include/asm/kvm_host_s390_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
 
@@ -47,6 +49,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 {
@@ -88,6 +97,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.
@@ -317,7 +340,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] 43+ messages in thread

* [PATCH v5 05/21] KVM: s390: vsie: Move pin/unpin guest page
  2026-08-26 15:05 [PATCH v5 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (3 preceding siblings ...)
  2026-08-26 15:05 ` [PATCH v5 04/21] KVM: s390: vsie: Add ssca_block and ssca_entry structs Christoph Schlameuss
@ 2026-08-26 15:05 ` Christoph Schlameuss
  2026-08-26 15:17   ` sashiko-bot
  2026-08-26 15:05 ` [PATCH v5 06/21] KVM: s390: vsie: Move pin/unpin_scb methods Christoph Schlameuss
                   ` (15 subsequent siblings)
  20 siblings, 1 reply; 43+ messages in thread
From: Christoph Schlameuss @ 2026-08-26 15:05 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, Sven Schnelle, Vasily Gorbik, Paolo Bonzini,
	Shuah Khan, Sean Christopherson, 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/s390/vsie.c | 52 +++++++++++++++++++++++------------------------
 1 file changed, 26 insertions(+), 26 deletions(-)

diff --git a/arch/s390/kvm/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
index 04cb38aad33c..e867d96917dd 100644
--- a/arch/s390/kvm/s390/vsie.c
+++ b/arch/s390/kvm/s390/vsie.c
@@ -149,6 +149,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)
 {
@@ -720,32 +746,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(phys_to_pfn(hpa)));
-	/* 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] 43+ messages in thread

* [PATCH v5 06/21] KVM: s390: vsie: Move pin/unpin_scb methods
  2026-08-26 15:05 [PATCH v5 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (4 preceding siblings ...)
  2026-08-26 15:05 ` [PATCH v5 05/21] KVM: s390: vsie: Move pin/unpin guest page Christoph Schlameuss
@ 2026-08-26 15:05 ` Christoph Schlameuss
  2026-08-26 15:10   ` sashiko-bot
  2026-08-26 15:05 ` [PATCH v5 07/21] KVM: s390: vsie: Move release/acquire gmap shadow Christoph Schlameuss
                   ` (14 subsequent siblings)
  20 siblings, 1 reply; 43+ messages in thread
From: Christoph Schlameuss @ 2026-08-26 15:05 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, Sven Schnelle, Vasily Gorbik, Paolo Bonzini,
	Shuah Khan, Sean Christopherson, 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/s390/vsie.c | 66 +++++++++++++++++++++++------------------------
 1 file changed, 33 insertions(+), 33 deletions(-)

diff --git a/arch/s390/kvm/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
index e867d96917dd..55e9399b9b30 100644
--- a/arch/s390/kvm/s390/vsie.c
+++ b/arch/s390/kvm/s390/vsie.c
@@ -686,6 +686,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] 43+ messages in thread

* [PATCH v5 07/21] KVM: s390: vsie: Move release/acquire gmap shadow
  2026-08-26 15:05 [PATCH v5 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (5 preceding siblings ...)
  2026-08-26 15:05 ` [PATCH v5 06/21] KVM: s390: vsie: Move pin/unpin_scb methods Christoph Schlameuss
@ 2026-08-26 15:05 ` Christoph Schlameuss
  2026-08-26 15:14   ` sashiko-bot
  2026-08-26 15:05 ` [PATCH v5 08/21] KVM: s390: vsie: Create helpers to alloc and free vsie_pages Christoph Schlameuss
                   ` (13 subsequent siblings)
  20 siblings, 1 reply; 43+ messages in thread
From: Christoph Schlameuss @ 2026-08-26 15:05 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, Sven Schnelle, Vasily Gorbik, Paolo Bonzini,
	Shuah Khan, Sean Christopherson, 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/s390/vsie.c | 130 +++++++++++++++++++++++-----------------------
 1 file changed, 65 insertions(+), 65 deletions(-)

diff --git a/arch/s390/kvm/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
index 55e9399b9b30..afc2e56fcabf 100644
--- a/arch/s390/kvm/s390/vsie.c
+++ b/arch/s390/kvm/s390/vsie.c
@@ -149,6 +149,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] 43+ messages in thread

* [PATCH v5 08/21] KVM: s390: vsie: Create helpers to alloc and free vsie_pages
  2026-08-26 15:05 [PATCH v5 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (6 preceding siblings ...)
  2026-08-26 15:05 ` [PATCH v5 07/21] KVM: s390: vsie: Move release/acquire gmap shadow Christoph Schlameuss
@ 2026-08-26 15:05 ` Christoph Schlameuss
  2026-08-26 15:10   ` sashiko-bot
  2026-08-26 15:05 ` [PATCH v5 09/21] KVM: s390: vsie: Replace radix_tree with xarray addr_to_page Christoph Schlameuss
                   ` (12 subsequent siblings)
  20 siblings, 1 reply; 43+ messages in thread
From: Christoph Schlameuss @ 2026-08-26 15:05 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, Sven Schnelle, Vasily Gorbik, Paolo Bonzini,
	Shuah Khan, Sean Christopherson, 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/s390/vsie.c | 26 ++++++++++++++++++++++----
 1 file changed, 22 insertions(+), 4 deletions(-)

diff --git a/arch/s390/kvm/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
index afc2e56fcabf..e7005bade757 100644
--- a/arch/s390/kvm/s390/vsie.c
+++ b/arch/s390/kvm/s390/vsie.c
@@ -1502,6 +1502,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.
  *
@@ -1537,7 +1555,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);
@@ -1557,9 +1575,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 >> SCB_ALIGNMENT_SHIFT);
+		/* 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 >> SCB_ALIGNMENT_SHIFT,
@@ -1662,7 +1680,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 >> SCB_ALIGNMENT_SHIFT);
-		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] 43+ messages in thread

* [PATCH v5 09/21] KVM: s390: vsie: Replace radix_tree with xarray addr_to_page
  2026-08-26 15:05 [PATCH v5 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (7 preceding siblings ...)
  2026-08-26 15:05 ` [PATCH v5 08/21] KVM: s390: vsie: Create helpers to alloc and free vsie_pages Christoph Schlameuss
@ 2026-08-26 15:05 ` Christoph Schlameuss
  2026-08-26 15:14   ` sashiko-bot
  2026-08-26 15:05 ` [PATCH v5 10/21] KVM: s390: vsie: Refactor kvm_s390_vsie_destroy and extract reusable methods Christoph Schlameuss
                   ` (11 subsequent siblings)
  20 siblings, 1 reply; 43+ messages in thread
From: Christoph Schlameuss @ 2026-08-26 15:05 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, Sven Schnelle, Vasily Gorbik, Paolo Bonzini,
	Shuah Khan, Sean Christopherson, 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_s390.h |  2 +-
 arch/s390/kvm/s390/vsie.c             | 40 ++++++++++++++---------------------
 2 files changed, 17 insertions(+), 25 deletions(-)

diff --git a/arch/s390/include/asm/kvm_host_s390.h b/arch/s390/include/asm/kvm_host_s390.h
index 6fdac144ca3a..82bfcc2bec74 100644
--- a/arch/s390/include/asm/kvm_host_s390.h
+++ b/arch/s390/include/asm/kvm_host_s390.h
@@ -575,7 +575,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/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
index e7005bade757..75676f493ab2 100644
--- a/arch/s390/kvm/s390/vsie.c
+++ b/arch/s390/kvm/s390/vsie.c
@@ -56,8 +56,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 */
@@ -1532,19 +1531,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 >> SCB_ALIGNMENT_SHIFT);
-	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 >> SCB_ALIGNMENT_SHIFT);
+	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);
 	}
 
 	/*
@@ -1573,15 +1568,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 >> SCB_ALIGNMENT_SHIFT);
+			xa_erase(&kvm->arch.vsie.addr_to_page,
+				 vsie_page->scb_gpa >> SCB_ALIGNMENT_SHIFT);
 		/* 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 >> SCB_ALIGNMENT_SHIFT,
-			      vsie_page)) {
+	if (xa_insert(&kvm->arch.vsie.addr_to_page, addr >> SCB_ALIGNMENT_SHIFT, vsie_page,
+		      GFP_KERNEL_ACCOUNT)) {
 		put_vsie_page(vsie_page);
 		mutex_unlock(&kvm->arch.vsie.mutex);
 		return NULL;
@@ -1660,7 +1655,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. */
@@ -1676,12 +1671,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 >> SCB_ALIGNMENT_SHIFT);
 		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] 43+ messages in thread

* [PATCH v5 10/21] KVM: s390: vsie: Refactor kvm_s390_vsie_destroy and extract reusable methods
  2026-08-26 15:05 [PATCH v5 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (8 preceding siblings ...)
  2026-08-26 15:05 ` [PATCH v5 09/21] KVM: s390: vsie: Replace radix_tree with xarray addr_to_page Christoph Schlameuss
@ 2026-08-26 15:05 ` Christoph Schlameuss
  2026-08-26 15:19   ` sashiko-bot
  2026-08-26 15:05 ` [PATCH v5 11/21] KVM: s390: vsie: Add helper reset_vsie_page() and unshadow_intercept() Christoph Schlameuss
                   ` (10 subsequent siblings)
  20 siblings, 1 reply; 43+ messages in thread
From: Christoph Schlameuss @ 2026-08-26 15:05 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, Sven Schnelle, Vasily Gorbik, Paolo Bonzini,
	Shuah Khan, Sean Christopherson, Christoph Schlameuss

Create a helper method release_gmap_shadow_safe() to simplify releasing
the gmap shadow of a vsie_page and a method kvm_s390_vsie_destroy_page()
reduce nesting.
Both methods will be called from multiple places in upcoming patches.

Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
---
 arch/s390/kvm/s390/vsie.c | 36 ++++++++++++++++++++++++------------
 1 file changed, 24 insertions(+), 12 deletions(-)

diff --git a/arch/s390/kvm/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
index 75676f493ab2..5e41dd151f8e 100644
--- a/arch/s390/kvm/s390/vsie.c
+++ b/arch/s390/kvm/s390/vsie.c
@@ -164,6 +164,17 @@ 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)
+		return;
+
+	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;
@@ -1585,11 +1596,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;
@@ -1658,24 +1665,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] 43+ messages in thread

* [PATCH v5 11/21] KVM: s390: vsie: Add helper reset_vsie_page() and unshadow_intercept()
  2026-08-26 15:05 [PATCH v5 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (9 preceding siblings ...)
  2026-08-26 15:05 ` [PATCH v5 10/21] KVM: s390: vsie: Refactor kvm_s390_vsie_destroy and extract reusable methods Christoph Schlameuss
@ 2026-08-26 15:05 ` Christoph Schlameuss
  2026-08-26 15:18   ` sashiko-bot
  2026-08-26 15:05 ` [PATCH v5 12/21] KVM: s390: vsie: Add helper unshadow_intercept() Christoph Schlameuss
                   ` (9 subsequent siblings)
  20 siblings, 1 reply; 43+ messages in thread
From: Christoph Schlameuss @ 2026-08-26 15:05 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, Sven Schnelle, Vasily Gorbik, Paolo Bonzini,
	Shuah Khan, Sean Christopherson, Christoph Schlameuss

Extract helper method from existing code to reuse in new code.

reset_vsie_page() clears and releases gmap related to vsie_page.

Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
---
 arch/s390/kvm/s390/vsie.c | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/arch/s390/kvm/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
index 5e41dd151f8e..5c4f13777178 100644
--- a/arch/s390/kvm/s390/vsie.c
+++ b/arch/s390/kvm/s390/vsie.c
@@ -1530,6 +1530,16 @@ static struct vsie_page *alloc_vsie_page(struct kvm *kvm)
 	return vsie_page;
 }
 
+/* Reset shadow state after a vsie_page has been (re)initialised for a new SCB. */
+static void reset_vsie_page(struct kvm *kvm, struct vsie_page *vsie_page)
+{
+	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;
+}
+
 /*
  * Get or create a vsie page for a scb address.
  *
@@ -1595,11 +1605,8 @@ static struct vsie_page *get_vsie_page(struct kvm *kvm, unsigned long addr)
 	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;
+	reset_vsie_page(kvm, vsie_page);
+
 	return vsie_page;
 }
 

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH v5 12/21] KVM: s390: vsie: Add helper unshadow_intercept()
  2026-08-26 15:05 [PATCH v5 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (10 preceding siblings ...)
  2026-08-26 15:05 ` [PATCH v5 11/21] KVM: s390: vsie: Add helper reset_vsie_page() and unshadow_intercept() Christoph Schlameuss
@ 2026-08-26 15:05 ` Christoph Schlameuss
  2026-08-26 15:14   ` sashiko-bot
  2026-08-26 15:05 ` [PATCH v5 13/21] KVM: s390: vsie: Lazily keep original scb pinned after vsie exit Christoph Schlameuss
                   ` (8 subsequent siblings)
  20 siblings, 1 reply; 43+ messages in thread
From: Christoph Schlameuss @ 2026-08-26 15:05 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, Sven Schnelle, Vasily Gorbik, Paolo Bonzini,
	Shuah Khan, Sean Christopherson, Christoph Schlameuss

Extract helper method from existing code to reuse in new code.

unshadow_intercept() acts like unshadow_scb() but only copies intercept
information. Used where the rest of the scb is not yet shadowed.

Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
---
 arch/s390/kvm/s390/vsie.c | 28 ++++++++++++++++++++--------
 1 file changed, 20 insertions(+), 8 deletions(-)

diff --git a/arch/s390/kvm/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
index 5c4f13777178..988370f491bf 100644
--- a/arch/s390/kvm/s390/vsie.c
+++ b/arch/s390/kvm/s390/vsie.c
@@ -565,18 +565,27 @@ static void prepare_ibc(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
 	}
 }
 
-/* unshadow the scb, copying parameters back to the real scb */
-static void unshadow_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
+/*
+ * Only write the interception fields back; avoid clobbering the timer and
+ * guest-state fields in scb_o.
+ */
+static inline void unshadow_intercept(struct kvm_s390_sie_block *scb_o,
+				      struct kvm_s390_sie_block *scb_s)
 {
-	struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s;
-	struct kvm_s390_sie_block *scb_o = vsie_page->scb_o;
-
-	/* interception */
 	scb_o->icptcode = scb_s->icptcode;
 	scb_o->icptstatus = scb_s->icptstatus;
 	scb_o->ipa = scb_s->ipa;
 	scb_o->ipb = scb_s->ipb;
 	scb_o->gbea = scb_s->gbea;
+}
+
+/* unshadow the scb, copying parameters back to the real scb */
+static void unshadow_scb(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;
+
+	unshadow_intercept(scb_o, scb_s);
 
 	/* timer */
 	scb_o->cputm = scb_s->cputm;
@@ -641,7 +650,7 @@ static int shadow_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
 
 	rc = prepare_cpuflags(vcpu, vsie_page);
 	if (rc)
-		goto out;
+		goto out_validity;
 
 	/* timer */
 	scb_s->cputm = scb_o->cputm;
@@ -755,10 +764,13 @@ static int shadow_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
 
 	prepare_ibc(vcpu, vsie_page);
 	rc = shadow_crycb(vcpu, vsie_page);
-out:
 	if (rc)
 		unshadow_scb(vcpu, vsie_page);
 	return rc;
+
+out_validity:
+	unshadow_intercept(vsie_page->scb_o, &vsie_page->scb_s);
+	return rc;
 }
 
 /* unpin the scb provided by guest 2, marking it as dirty */

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH v5 13/21] KVM: s390: vsie: Lazily keep original scb pinned after vsie exit
  2026-08-26 15:05 [PATCH v5 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (11 preceding siblings ...)
  2026-08-26 15:05 ` [PATCH v5 12/21] KVM: s390: vsie: Add helper unshadow_intercept() Christoph Schlameuss
@ 2026-08-26 15:05 ` Christoph Schlameuss
  2026-08-26 15:25   ` sashiko-bot
  2026-08-26 15:05 ` [PATCH v5 14/21] KVM: s390: vsie: Add helper to pin and unpin multiple guest pages Christoph Schlameuss
                   ` (7 subsequent siblings)
  20 siblings, 1 reply; 43+ messages in thread
From: Christoph Schlameuss @ 2026-08-26 15:05 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, Sven Schnelle, Vasily Gorbik, Paolo Bonzini,
	Shuah Khan, Sean Christopherson, 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/s390/vsie.c | 129 +++++++++++++++++++++++++++++++---------------
 1 file changed, 88 insertions(+), 41 deletions(-)

diff --git a/arch/s390/kvm/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
index 988370f491bf..24af48af89cd 100644
--- a/arch/s390/kvm/s390/vsie.c
+++ b/arch/s390/kvm/s390/vsie.c
@@ -29,6 +29,7 @@
 
 enum vsie_page_flags {
 	VSIE_PAGE_IN_USE = 0,
+	VSIE_PAGE_SCB_PINNED = 1,
 };
 
 struct vsie_page {
@@ -774,14 +775,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);
 }
 
 /*
@@ -790,19 +795,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;
 }
 
@@ -1542,6 +1550,22 @@ static struct vsie_page *alloc_vsie_page(struct kvm *kvm)
 	return vsie_page;
 }
 
+static int init_vsie_page(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page, unsigned long scb_gpa)
+{
+	int rc;
+
+	vsie_page->scb_gpa = scb_gpa;
+	rc = pin_scb(vcpu, vsie_page);
+	if (rc) {
+		vsie_page->scb_gpa = ULONG_MAX;
+		return rc;
+	}
+
+	vsie_page->sca_gpa = read_scao(vcpu->kvm, vsie_page->scb_o);
+
+	return 0;
+}
+
 /* Reset shadow state after a vsie_page has been (re)initialised for a new SCB. */
 static void reset_vsie_page(struct kvm *kvm, struct vsie_page *vsie_page)
 {
@@ -1555,14 +1579,21 @@ static void reset_vsie_page(struct kvm *kvm, struct vsie_page *vsie_page)
 /*
  * 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 >> SCB_ALIGNMENT_SHIFT);
 	if (vsie_page && try_get_vsie_page(vsie_page)) {
@@ -1575,47 +1606,65 @@ 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 >> SCB_ALIGNMENT_SHIFT);
-		/* 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 >> SCB_ALIGNMENT_SHIFT, vsie_page,
-		      GFP_KERNEL_ACCOUNT)) {
+	rc = init_vsie_page(vcpu, vsie_page, addr);
+	if (!rc)
+		xa_store(&kvm->arch.vsie.addr_to_page, addr >> SCB_ALIGNMENT_SHIFT, vsie_page,
+			 GFP_KERNEL_ACCOUNT);
+
+	mutex_unlock(&kvm->arch.vsie.mutex);
+	if (vsie_page_new)
+		free_vsie_page(vsie_page_new);
+	if (rc) {
+		vsie_page->scb_gpa = ULONG_MAX;
 		put_vsie_page(vsie_page);
-		mutex_unlock(&kvm->arch.vsie.mutex);
-		return NULL;
+		return rc < 0 ? ERR_PTR(rc) : NULL;
 	}
-	vsie_page->scb_gpa = addr;
-	mutex_unlock(&kvm->arch.vsie.mutex);
 
 	reset_vsie_page(kvm, vsie_page);
 
@@ -1645,7 +1694,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) {
@@ -1654,12 +1703,12 @@ 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);
 	if (rc)
-		goto out_unpin_scb;
+		goto out_put;
 	rc = pin_blocks(vcpu, vsie_page);
 	if (rc)
 		goto out_unshadow;
@@ -1669,8 +1718,6 @@ int kvm_s390_handle_vsie(struct kvm_vcpu *vcpu)
 	unpin_blocks(vcpu, vsie_page);
 out_unshadow:
 	unshadow_scb(vcpu, vsie_page);
-out_unpin_scb:
-	unpin_scb(vcpu, vsie_page, scb_addr);
 out_put:
 	put_vsie_page(vsie_page);
 

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH v5 14/21] KVM: s390: vsie: Add helper to pin and unpin multiple guest pages
  2026-08-26 15:05 [PATCH v5 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (12 preceding siblings ...)
  2026-08-26 15:05 ` [PATCH v5 13/21] KVM: s390: vsie: Lazily keep original scb pinned after vsie exit Christoph Schlameuss
@ 2026-08-26 15:05 ` Christoph Schlameuss
  2026-08-26 15:15   ` sashiko-bot
  2026-08-26 15:05 ` [PATCH v5 15/21] KVM: s390: vsie: Add struct vsie_sca with pin and unpin methods Christoph Schlameuss
                   ` (6 subsequent siblings)
  20 siblings, 1 reply; 43+ messages in thread
From: Christoph Schlameuss @ 2026-08-26 15:05 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, Sven Schnelle, Vasily Gorbik, Paolo Bonzini,
	Shuah Khan, Sean Christopherson, 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/s390/vsie.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)

diff --git a/arch/s390/kvm/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
index 24af48af89cd..f7a7170e3112 100644
--- a/arch/s390/kvm/s390/vsie.c
+++ b/arch/s390/kvm/s390/vsie.c
@@ -71,6 +71,11 @@ static_assert(sizeof(struct vsie_page) == PAGE_SIZE);
 static_assert(offsetof(struct vsie_page, mcck_info) == offsetof(struct sie_page, mcck_info));
 static_assert(IS_ALIGNED(offsetof(struct vsie_page, crycb), 8));
 
+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);
@@ -251,6 +256,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
+ */
+static int pin_guest_pages(struct kvm *kvm, gpa_t gpa, unsigned int nr_pages,
+			   struct kvm_address_pair *addr, unsigned int *nr_pages_pinned)
+{
+	int rc = 0, i;
+	hpa_t hpa;
+
+	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;
+	}
+	*nr_pages_pinned = i;
+	return rc;
+
+err:
+	unpin_guest_pages(kvm, addr, i);
+	*nr_pages_pinned = 0;
+	return rc;
+}
+
 /* 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] 43+ messages in thread

* [PATCH v5 15/21] KVM: s390: vsie: Add struct vsie_sca with pin and unpin methods
  2026-08-26 15:05 [PATCH v5 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (13 preceding siblings ...)
  2026-08-26 15:05 ` [PATCH v5 14/21] KVM: s390: vsie: Add helper to pin and unpin multiple guest pages Christoph Schlameuss
@ 2026-08-26 15:05 ` Christoph Schlameuss
  2026-08-26 15:26   ` sashiko-bot
  2026-08-26 15:05 ` [PATCH v5 16/21] KVM: s390: vsie: Shadow VSIE SCA in guest-1 Christoph Schlameuss
                   ` (5 subsequent siblings)
  20 siblings, 1 reply; 43+ messages in thread
From: Christoph Schlameuss @ 2026-08-26 15:05 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, Sven Schnelle, Vasily Gorbik, Paolo Bonzini,
	Shuah Khan, Sean Christopherson, 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_s390_types.h |   1 +
 arch/s390/kvm/s390/vsie.c                   | 101 +++++++++++++++++++++++++++-
 2 files changed, 101 insertions(+), 1 deletion(-)

diff --git a/arch/s390/include/asm/kvm_host_s390_types.h b/arch/s390/include/asm/kvm_host_s390_types.h
index 0b69e71bc8ee..1ac31a54d508 100644
--- a/arch/s390/include/asm/kvm_host_s390_types.h
+++ b/arch/s390/include/asm/kvm_host_s390_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/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
index f7a7170e3112..3c94faa35b44 100644
--- a/arch/s390/kvm/s390/vsie.c
+++ b/arch/s390/kvm/s390/vsie.c
@@ -62,7 +62,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 */
 };
@@ -76,6 +77,36 @@ struct kvm_address_pair {
 	hpa_t hpa;
 };
 
+enum vsie_sca_flags {
+	VSIE_SCA_ESCA = 0,
+	VSIE_SCA_SCA_PINNED = 1,
+};
+
+struct vsie_sca {
+	struct_group(head,
+		struct ssca_block	ssca;
+	);
+	struct_group(keep,
+		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_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];
+	);
+};
+
+/*
+ * 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);
@@ -859,6 +890,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 in write, except in the destroy path.
+ */
+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 in write.
+ */
+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 rc, 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;
+	}
+
+	rc = pin_guest_pages(kvm, vsie_sca->sca_gpa, nr_pages, vsie_sca->sca_o_pages,
+			     &vsie_sca->sca_o_nr_pages);
+	if (rc)
+		return rc;
+	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] 43+ messages in thread

* [PATCH v5 16/21] KVM: s390: vsie: Shadow VSIE SCA in guest-1
  2026-08-26 15:05 [PATCH v5 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (14 preceding siblings ...)
  2026-08-26 15:05 ` [PATCH v5 15/21] KVM: s390: vsie: Add struct vsie_sca with pin and unpin methods Christoph Schlameuss
@ 2026-08-26 15:05 ` Christoph Schlameuss
  2026-08-26 15:25   ` sashiko-bot
  2026-08-26 15:05 ` [PATCH v5 17/21] KVM: s390: vsie: Allow guest-3 cpu add and remove with ssca Christoph Schlameuss
                   ` (4 subsequent siblings)
  20 siblings, 1 reply; 43+ messages in thread
From: Christoph Schlameuss @ 2026-08-26 15:05 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, Sven Schnelle, Vasily Gorbik, Paolo Bonzini,
	Shuah Khan, Sean Christopherson, 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_s390.h       |  21 +-
 arch/s390/include/asm/kvm_host_s390_types.h |   2 +
 arch/s390/kvm/s390/vsie.c                   | 475 ++++++++++++++++++++++++++--
 3 files changed, 473 insertions(+), 25 deletions(-)

diff --git a/arch/s390/include/asm/kvm_host_s390.h b/arch/s390/include/asm/kvm_host_s390.h
index 82bfcc2bec74..9768c9dca27c 100644
--- a/arch/s390/include/asm/kvm_host_s390.h
+++ b/arch/s390/include/asm/kvm_host_s390.h
@@ -572,13 +572,32 @@ struct sie_page2 {
 };
 
 struct vsie_page;
+struct vsie_sca;
 
+/*
+ * vsie_pages, scas and accompanied management vars
+ */
 struct kvm_s390_vsie {
+	/*
+	 * protects pages[], page_count, next, addr_to_page
+	 */
 	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
+	 * - sca_count and sca_next
+	 * - new vsie_sca creation and initialization
+	 */
+	struct rw_semaphore vsie_sca_lock;
+	struct xarray osca_to_sca;
+	int sca_count;
+	int sca_next;
+	struct vsie_sca *scas[KVM_S390_MAX_VSIE_VCPUS];
 };
 
 struct kvm_s390_gisa_iam {
diff --git a/arch/s390/include/asm/kvm_host_s390_types.h b/arch/s390/include/asm/kvm_host_s390_types.h
index 1ac31a54d508..6eef71072b4e 100644
--- a/arch/s390/include/asm/kvm_host_s390_types.h
+++ b/arch/s390/include/asm/kvm_host_s390_types.h
@@ -6,6 +6,7 @@
 #include <linux/atomic.h>
 #include <linux/types.h>
 
+#define KVM_S390_CPU_MASK 0xff
 #define KVM_S390_MAX_VSIE_VCPUS 256
 #define KVM_S390_MAX_SCA_PAGES 5
 
@@ -13,6 +14,7 @@
 #define KVM_S390_ESCA_CPU_SLOTS 248
 
 #define SCB_ALIGNMENT_SHIFT 9
+#define SCA_ALIGNMENT_SHIFT 6
 
 #define SIGP_CTRL_C		0x80
 #define SIGP_CTRL_SCN_MASK	0x3f
diff --git a/arch/s390/kvm/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
index 3c94faa35b44..4bd11eaa7d81 100644
--- a/arch/s390/kvm/s390/vsie.c
+++ b/arch/s390/kvm/s390/vsie.c
@@ -107,6 +107,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);
@@ -128,6 +133,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)
@@ -939,6 +955,83 @@ 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 a matching vsie_sca with the correct sca format.
+ * @sca_o_gpa: original system control area address; guest-2 physical
+ * @uses_esca: whether the guest SCB has ECB2_ESCA set
+ *
+ * Called with lock on vsie_sca_lock.
+ */
+static struct vsie_sca *get_vsie_sca_existing(struct kvm *kvm, gpa_t sca_o_gpa, bool uses_esca)
+{
+	struct vsie_sca *vsie_sca = xa_load(&kvm->arch.vsie.osca_to_sca,
+					    sca_o_gpa >> SCA_ALIGNMENT_SHIFT);
+
+	if (!vsie_sca)
+		return NULL;
+	if (uses_esca != test_bit(VSIE_SCA_ESCA, &vsie_sca->flags))
+		return NULL;
+	WARN_ON_ONCE(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_vsie_sca_unused(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));
@@ -958,6 +1051,128 @@ 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);
+	bool is_esca = sie_uses_esca(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_vsie_sca_existing(kvm, sca_gpa, is_esca);
+	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 xarray and arch.vise.scas, etc.
+	 *
+	 * 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 = xa_load(&kvm->arch.vsie.osca_to_sca, sca_gpa >> SCA_ALIGNMENT_SHIFT);
+	if (vsie_sca) {
+		WARN_ON_ONCE(atomic_inc_return(&vsie_sca->ref_count) < 1);
+		if (is_esca == test_bit(VSIE_SCA_ESCA, &vsie_sca->flags))
+			goto out;
+		/* found vsie_sca with matching sca_gpa but wrong format */
+		put_vsie_sca(vsie_sca);
+		xa_erase(&kvm->arch.vsie.osca_to_sca, sca_gpa >> SCA_ALIGNMENT_SHIFT);
+	}
+
+	/* 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_vsie_sca_unused(kvm);
+		/* with nr_vcpus scas one must be reusable */
+		if (IS_ERR(vsie_sca))
+			goto out;
+
+		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) {
+		vsie_sca->sca_gpa = ULONG_MAX;
+		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_ACCOUNT));
+
+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;
@@ -1024,11 +1239,12 @@ 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);
+			write_scao(scb_s, 0);
+		}
 	}
 
 	hpa = scb_s->itdba;
@@ -1067,9 +1283,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
@@ -1082,8 +1295,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;
@@ -1092,7 +1305,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);
 	}
 
@@ -1633,7 +1845,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)
@@ -1815,11 +2027,165 @@ 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 = init_vsie_page(vcpu, vsie_page, scb_gpa);
+		}
+		if (rc) {
+			put_vsie_page(vsie_page);
+			return ERR_PTR(rc);
+		}
+
+		reset_vsie_page(vcpu->kvm, vsie_page);
+	}
+
+	return vsie_page;
+}
+
+static void update_vsie_sca(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);
+}
+
+static int _shadow_sca_cpu(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,
+			   struct vsie_sca *vsie_sca, hpa_t sca_o_entry_hpa,
+			   unsigned int cpu_nr, bool is_esca)
+{
+	hva_t sca_o_entry_hva = (hva_t)phys_to_virt(sca_o_entry_hpa);
+	struct vsie_page *vsie_page_n;
+	gpa_t scb_o_gpa;
+	int rc;
+
+	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)
+		return set_validity_icpt(vsie_page->scb_o, 0x0001U);
+
+	vsie_page_n = get_vsie_page_cpu_nr(vcpu, vsie_sca, scb_o_gpa, cpu_nr);
+	if (!vsie_page_n)
+		return -EAGAIN;
+	if (IS_ERR(vsie_page_n))
+		return PTR_ERR(vsie_page_n);
+
+	rc = shadow_scb(vcpu, vsie_page_n);
+	update_vsie_sca(vsie_sca, cpu_nr, vsie_page_n, sca_o_entry_hpa);
+	if (rc) {
+		/* copy intercept to primary scb_o, no unshadow_scb() on exit */
+		unshadow_intercept(vsie_page->scb_o, &vsie_page_n->scb_s);
+		rc = 1;
+	}
+	put_vsie_page(vsie_page_n);
+
+	return rc;
+}
+
+/* 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;
+	hpa_t sca_o_entry_hpa;
+	unsigned long *mcn;
+	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)
+			break;
+
+		if ((vsie_page->scb_o->icpua & KVM_S390_CPU_MASK) == cpu_nr) {
+			update_vsie_sca(vsie_sca, cpu_nr, vsie_page, sca_o_entry_hpa);
+			continue;
+		}
+
+		rc = _shadow_sca_cpu(vcpu, vsie_page, vsie_sca, sca_o_entry_hpa, cpu_nr, is_esca);
+		if (rc)
+			break;
+	}
+
+	if (rc) {
+		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;
+		}
+	} else {
+		vsie_sca->ssca.osca = sca_o_hpa(vsie_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);
+
+	if (!rc)
+		write_scao(&vsie_page->scb_s, virt_to_phys(&vsie_sca->ssca));
+
+	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))
@@ -1838,33 +2204,71 @@ 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 & KVM_S390_CPU_MASK);
+		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_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_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;
 }
 
@@ -1873,6 +2277,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)
@@ -1886,7 +2292,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);
 
@@ -1897,7 +2304,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] 43+ messages in thread

* [PATCH v5 17/21] KVM: s390: vsie: Allow guest-3 cpu add and remove with ssca
  2026-08-26 15:05 [PATCH v5 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (15 preceding siblings ...)
  2026-08-26 15:05 ` [PATCH v5 16/21] KVM: s390: vsie: Shadow VSIE SCA in guest-1 Christoph Schlameuss
@ 2026-08-26 15:05 ` Christoph Schlameuss
  2026-08-26 15:23   ` sashiko-bot
  2026-08-26 15:05 ` [PATCH v5 18/21] KVM: s390: vsie: Add VSIE max shadow configuration Christoph Schlameuss
                   ` (3 subsequent siblings)
  20 siblings, 1 reply; 43+ messages in thread
From: Christoph Schlameuss @ 2026-08-26 15:05 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, Sven Schnelle, Vasily Gorbik, Paolo Bonzini,
	Shuah Khan, Sean Christopherson, 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/s390/vsie.c | 66 +++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 55 insertions(+), 11 deletions(-)

diff --git a/arch/s390/kvm/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
index 4bd11eaa7d81..6e42b00206bd 100644
--- a/arch/s390/kvm/s390/vsie.c
+++ b/arch/s390/kvm/s390/vsie.c
@@ -2075,6 +2075,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 update_vsie_sca(struct vsie_sca *vsie_sca, unsigned int cpu_nr,
 			    struct vsie_page *vsie_page_n, hpa_t sca_o_entry_hpa)
 {
@@ -2126,17 +2163,17 @@ static int _shadow_sca(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,
 	bool is_esca = sie_uses_esca(vsie_page->scb_o);
 	unsigned int cpu_nr, cpu_slots;
 	hpa_t sca_o_entry_hpa;
-	unsigned long *mcn;
 	int rc;
 
 	if (is_esca)
-		mcn = phys_to_virt(sca_o_hpa(vsie_sca)) + offsetof(struct esca_block, mcn);
+		set_bit(VSIE_SCA_ESCA, &vsie_sca->flags);
 	else
-		mcn = phys_to_virt(sca_o_hpa(vsie_sca)) + offsetof(struct bsca_block, mcn);
+		clear_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)
 			break;
@@ -2163,19 +2200,26 @@ static int _shadow_sca(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,
 	return rc;
 }
 
+static bool shadow_sca_needed(struct vsie_sca *vsie_sca)
+{
+	if (!vsie_sca->ssca.osca)
+		return true;
+	return !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 (!shadow_sca_needed(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 (!rc)
-		write_scao(&vsie_page->scb_s, virt_to_phys(&vsie_sca->ssca));
+	if (!shadow_sca_needed(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] 43+ messages in thread

* [PATCH v5 18/21] KVM: s390: vsie: Add VSIE max shadow configuration
  2026-08-26 15:05 [PATCH v5 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (16 preceding siblings ...)
  2026-08-26 15:05 ` [PATCH v5 17/21] KVM: s390: vsie: Allow guest-3 cpu add and remove with ssca Christoph Schlameuss
@ 2026-08-26 15:05 ` Christoph Schlameuss
  2026-08-26 15:24   ` sashiko-bot
  2026-08-26 15:05 ` [PATCH v5 19/21] KVM: s390: vsie: Add VSIE shadow stat counters Christoph Schlameuss
                   ` (2 subsequent siblings)
  20 siblings, 1 reply; 43+ messages in thread
From: Christoph Schlameuss @ 2026-08-26 15:05 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, Sven Schnelle, Vasily Gorbik, Paolo Bonzini,
	Shuah Khan, Sean Christopherson, 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/s390/vsie.c | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/arch/s390/kvm/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
index 6e42b00206bd..fe94587511b0 100644
--- a/arch/s390/kvm/s390/vsie.c
+++ b/arch/s390/kvm/s390/vsie.c
@@ -107,6 +107,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);
@@ -1085,7 +1095,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();
@@ -1962,7 +1973,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] 43+ messages in thread

* [PATCH v5 19/21] KVM: s390: vsie: Add VSIE shadow stat counters
  2026-08-26 15:05 [PATCH v5 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (17 preceding siblings ...)
  2026-08-26 15:05 ` [PATCH v5 18/21] KVM: s390: vsie: Add VSIE max shadow configuration Christoph Schlameuss
@ 2026-08-26 15:05 ` Christoph Schlameuss
  2026-08-26 15:19   ` sashiko-bot
  2026-08-26 15:05 ` [PATCH v5 20/21] KVM: s390: vsie: Create minimal scb shadows for not running g3 blocks Christoph Schlameuss
  2026-08-26 15:05 ` [PATCH v5 21/21] KVM: s390: vsie: Enable use of VSIE SSCA Christoph Schlameuss
  20 siblings, 1 reply; 43+ messages in thread
From: Christoph Schlameuss @ 2026-08-26 15:05 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, Sven Schnelle, Vasily Gorbik, Paolo Bonzini,
	Shuah Khan, Sean Christopherson, 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_s390.h |  4 ++++
 arch/s390/kvm/s390/s390.c             |  4 ++++
 arch/s390/kvm/s390/vsie.c             | 10 ++++++++++
 3 files changed, 18 insertions(+)

diff --git a/arch/s390/include/asm/kvm_host_s390.h b/arch/s390/include/asm/kvm_host_s390.h
index 9768c9dca27c..a19d3a5f550c 100644
--- a/arch/s390/include/asm/kvm_host_s390.h
+++ b/arch/s390/include/asm/kvm_host_s390.h
@@ -412,6 +412,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/s390/s390.c b/arch/s390/kvm/s390/s390.c
index ae0177d74b11..a1ce75be3955 100644
--- a/arch/s390/kvm/s390/s390.c
+++ b/arch/s390/kvm/s390/s390.c
@@ -87,6 +87,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/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
index fe94587511b0..335ed1a927aa 100644
--- a/arch/s390/kvm/s390/vsie.c
+++ b/arch/s390/kvm/s390/vsie.c
@@ -1133,6 +1133,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_vsie_sca_unused(kvm);
@@ -1154,6 +1155,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))
@@ -2006,6 +2008,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) {
@@ -2018,6 +2021,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 = init_vsie_page(vcpu, vsie_page, addr);
@@ -2043,6 +2047,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];
@@ -2063,6 +2068,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);
 
@@ -2076,6 +2082,10 @@ static struct vsie_page *get_vsie_page_cpu_nr(struct kvm_vcpu *vcpu, struct vsie
 			unpin_scb(vcpu->kvm, vsie_page);
 			rc = init_vsie_page(vcpu, vsie_page, scb_gpa);
 		}
+		if (vsie_page_is_new)
+			vcpu->kvm->stat.vsie_shadow_scb++;
+		else
+			vcpu->kvm->stat.vsie_shadow_scb_reuse++;
 		if (rc) {
 			put_vsie_page(vsie_page);
 			return ERR_PTR(rc);

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH v5 20/21] KVM: s390: vsie: Create minimal scb shadows for not running g3 blocks
  2026-08-26 15:05 [PATCH v5 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (18 preceding siblings ...)
  2026-08-26 15:05 ` [PATCH v5 19/21] KVM: s390: vsie: Add VSIE shadow stat counters Christoph Schlameuss
@ 2026-08-26 15:05 ` Christoph Schlameuss
  2026-08-26 15:35   ` sashiko-bot
  2026-08-26 15:05 ` [PATCH v5 21/21] KVM: s390: vsie: Enable use of VSIE SSCA Christoph Schlameuss
  20 siblings, 1 reply; 43+ messages in thread
From: Christoph Schlameuss @ 2026-08-26 15:05 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, Sven Schnelle, Vasily Gorbik, Paolo Bonzini,
	Shuah Khan, Sean Christopherson, 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/s390/vsie.c | 54 ++++++++++++++++++++++++++++++++---------------
 1 file changed, 37 insertions(+), 17 deletions(-)

diff --git a/arch/s390/kvm/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
index 335ed1a927aa..86ccd92233b5 100644
--- a/arch/s390/kvm/s390/vsie.c
+++ b/arch/s390/kvm/s390/vsie.c
@@ -368,8 +368,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)
@@ -722,6 +721,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.
@@ -741,17 +773,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_validity;
 
@@ -781,8 +803,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
@@ -2166,7 +2186,7 @@ static int _shadow_sca_cpu(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,
 	if (IS_ERR(vsie_page_n))
 		return PTR_ERR(vsie_page_n);
 
-	rc = shadow_scb(vcpu, vsie_page_n);
+	rc = shadow_scb_minimal(vcpu, vsie_page_n);
 	update_vsie_sca(vsie_sca, cpu_nr, vsie_page_n, sca_o_entry_hpa);
 	if (rc) {
 		/* copy intercept to primary scb_o, no unshadow_scb() on exit */
@@ -2193,7 +2213,7 @@ static int _shadow_sca(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,
 		clear_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);

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH v5 21/21] KVM: s390: vsie: Enable use of VSIE SSCA
  2026-08-26 15:05 [PATCH v5 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (19 preceding siblings ...)
  2026-08-26 15:05 ` [PATCH v5 20/21] KVM: s390: vsie: Create minimal scb shadows for not running g3 blocks Christoph Schlameuss
@ 2026-08-26 15:05 ` Christoph Schlameuss
  2026-08-26 15:33   ` sashiko-bot
  20 siblings, 1 reply; 43+ messages in thread
From: Christoph Schlameuss @ 2026-08-26 15:05 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, Sven Schnelle, Vasily Gorbik, Paolo Bonzini,
	Shuah Khan, Sean Christopherson, 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/s390/s390.c | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/arch/s390/kvm/s390/s390.c b/arch/s390/kvm/s390/s390.c
index a1ce75be3955..ffa5bbb8713b 100644
--- a/arch/s390/kvm/s390/s390.c
+++ b/arch/s390/kvm/s390/s390.c
@@ -248,6 +248,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.
 	 */
 }
 
@@ -3363,7 +3367,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] 43+ messages in thread

* Re: [PATCH v5 04/21] KVM: s390: vsie: Add ssca_block and ssca_entry structs
  2026-08-26 15:05 ` [PATCH v5 04/21] KVM: s390: vsie: Add ssca_block and ssca_entry structs Christoph Schlameuss
@ 2026-08-26 15:09   ` sashiko-bot
  0 siblings, 0 replies; 43+ messages in thread
From: sashiko-bot @ 2026-08-26 15:09 UTC (permalink / raw)
  To: Christoph Schlameuss
  Cc: Heiko Carstens, Christian Borntraeger, linux-s390,
	Alexander Gordeev, kvm, 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/20260826-vsie-sigpi-v5-0-cf471edb46b2@linux.ibm.com?part=4


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v5 06/21] KVM: s390: vsie: Move pin/unpin_scb methods
  2026-08-26 15:05 ` [PATCH v5 06/21] KVM: s390: vsie: Move pin/unpin_scb methods Christoph Schlameuss
@ 2026-08-26 15:10   ` sashiko-bot
  0 siblings, 0 replies; 43+ messages in thread
From: sashiko-bot @ 2026-08-26 15:10 UTC (permalink / raw)
  To: Christoph Schlameuss
  Cc: Christian Borntraeger, kvm, Alexander Gordeev, Vasily Gorbik,
	linux-s390, Heiko Carstens

> Move {,un}pin_scb() up in preparation for the next patches.
> 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/20260826-vsie-sigpi-v5-0-cf471edb46b2@linux.ibm.com?part=6


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v5 08/21] KVM: s390: vsie: Create helpers to alloc and free vsie_pages
  2026-08-26 15:05 ` [PATCH v5 08/21] KVM: s390: vsie: Create helpers to alloc and free vsie_pages Christoph Schlameuss
@ 2026-08-26 15:10   ` sashiko-bot
  0 siblings, 0 replies; 43+ messages in thread
From: sashiko-bot @ 2026-08-26 15:10 UTC (permalink / raw)
  To: Christoph Schlameuss
  Cc: Heiko Carstens, Vasily Gorbik, linux-s390, kvm, Alexander Gordeev,
	Christian Borntraeger

> 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>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260826-vsie-sigpi-v5-0-cf471edb46b2@linux.ibm.com?part=8


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v5 01/21] KVM: s390: vsie: Add SCAO read and write helpers
  2026-08-26 15:05 ` [PATCH v5 01/21] KVM: s390: vsie: Add SCAO read and write helpers Christoph Schlameuss
@ 2026-08-26 15:12   ` sashiko-bot
  0 siblings, 0 replies; 43+ messages in thread
From: sashiko-bot @ 2026-08-26 15:12 UTC (permalink / raw)
  To: Christoph Schlameuss
  Cc: Heiko Carstens, Vasily Gorbik, Christian Borntraeger, kvm,
	linux-s390, Alexander Gordeev

> 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/20260826-vsie-sigpi-v5-0-cf471edb46b2@linux.ibm.com?part=1


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v5 03/21] KVM: s390: vsie: Add vsie_interp_extf detection
  2026-08-26 15:05 ` [PATCH v5 03/21] KVM: s390: vsie: Add vsie_interp_extf detection Christoph Schlameuss
@ 2026-08-26 15:12   ` sashiko-bot
  0 siblings, 0 replies; 43+ messages in thread
From: sashiko-bot @ 2026-08-26 15:12 UTC (permalink / raw)
  To: Christoph Schlameuss
  Cc: Alexander Gordeev, Christian Borntraeger, Heiko Carstens, kvm,
	linux-s390, Vasily Gorbik

> 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/20260826-vsie-sigpi-v5-0-cf471edb46b2@linux.ibm.com?part=3


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v5 09/21] KVM: s390: vsie: Replace radix_tree with xarray addr_to_page
  2026-08-26 15:05 ` [PATCH v5 09/21] KVM: s390: vsie: Replace radix_tree with xarray addr_to_page Christoph Schlameuss
@ 2026-08-26 15:14   ` sashiko-bot
  0 siblings, 0 replies; 43+ messages in thread
From: sashiko-bot @ 2026-08-26 15:14 UTC (permalink / raw)
  To: Christoph Schlameuss
  Cc: Christian Borntraeger, linux-s390, Vasily Gorbik, kvm,
	Alexander Gordeev, Heiko Carstens

> 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/20260826-vsie-sigpi-v5-0-cf471edb46b2@linux.ibm.com?part=9


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v5 07/21] KVM: s390: vsie: Move release/acquire gmap shadow
  2026-08-26 15:05 ` [PATCH v5 07/21] KVM: s390: vsie: Move release/acquire gmap shadow Christoph Schlameuss
@ 2026-08-26 15:14   ` sashiko-bot
  0 siblings, 0 replies; 43+ messages in thread
From: sashiko-bot @ 2026-08-26 15:14 UTC (permalink / raw)
  To: Christoph Schlameuss
  Cc: Christian Borntraeger, kvm, linux-s390, Heiko Carstens,
	Alexander Gordeev, Vasily Gorbik

> 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/20260826-vsie-sigpi-v5-0-cf471edb46b2@linux.ibm.com?part=7


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v5 12/21] KVM: s390: vsie: Add helper unshadow_intercept()
  2026-08-26 15:05 ` [PATCH v5 12/21] KVM: s390: vsie: Add helper unshadow_intercept() Christoph Schlameuss
@ 2026-08-26 15:14   ` sashiko-bot
  0 siblings, 0 replies; 43+ messages in thread
From: sashiko-bot @ 2026-08-26 15:14 UTC (permalink / raw)
  To: Christoph Schlameuss
  Cc: Alexander Gordeev, Vasily Gorbik, Christian Borntraeger,
	linux-s390, Heiko Carstens, kvm

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] unshadow_intercept() writes uninitialized or stale shadow SCB fields (gbea and icptstatus) back to the guest's original SCB on early validity intercepts, causing guest state corruption.
--

commit 065588b108c97fdd6441ebcc560b6bd343203830
Author: Christoph Schlameuss <schlameuss@linux.ibm.com>

KVM: s390: vsie: Add helper unshadow_intercept()

This commit extracts the unshadow_intercept() helper from unshadow_scb().
The new helper only copies intercept information and avoids clobbering the
timer and guest-state fields, to be used when the rest of the scb is not yet
shadowed.

> diff --git a/arch/s390/kvm/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
> index 5c4f13777178b..988370f491bf5 100644
> --- a/arch/s390/kvm/s390/vsie.c
> +++ b/arch/s390/kvm/s390/vsie.c
> @@ -565,18 +565,27 @@ static void prepare_ibc(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
>  	}
>  }
>  
> -/* unshadow the scb, copying parameters back to the real scb */
> -static void unshadow_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
> +/*
> + * Only write the interception fields back; avoid clobbering the timer and
> + * guest-state fields in scb_o.
> + */
> +static inline void unshadow_intercept(struct kvm_s390_sie_block *scb_o,
> +				      struct kvm_s390_sie_block *scb_s)
>  {
> -	struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s;
> -	struct kvm_s390_sie_block *scb_o = vsie_page->scb_o;
> -
> -	/* interception */
>  	scb_o->icptcode = scb_s->icptcode;
>  	scb_o->icptstatus = scb_s->icptstatus;
>  	scb_o->ipa = scb_s->ipa;
>  	scb_o->ipb = scb_s->ipb;
>  	scb_o->gbea = scb_s->gbea;
> +}

[ ... ]

> @@ -755,10 +764,13 @@ static int shadow_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
>  
>  	prepare_ibc(vcpu, vsie_page);
>  	rc = shadow_crycb(vcpu, vsie_page);
> -out:
>  	if (rc)
>  		unshadow_scb(vcpu, vsie_page);
>  	return rc;
> +
> +out_validity:
> +	unshadow_intercept(vsie_page->scb_o, &vsie_page->scb_s);

[Severity: High]
This is a pre-existing issue, but does calling unshadow_intercept() here
corrupt the L1 guest state by writing uninitialized or stale shadow SCB fields
back to the guest's original SCB?

When shadow_scb() fails early in prepare_cpuflags() (e.g., due to an L1 guest
setting an invalid CPU flag), it jumps to out_validity and calls
unshadow_intercept().

However, because gbea and icptstatus are not copied from scb_o to scb_s until
much later in shadow_scb(), unshadow_intercept() will write the stale values
from scb_s back into the guest's scb_o, explicitly clobbering the L1 guest's
changes.

An L1 guest could potentially trigger this trivially by forcing an early validity
intercept.

> +	return rc;
>  }
>  
>  /* unpin the scb provided by guest 2, marking it as dirty */

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260826-vsie-sigpi-v5-0-cf471edb46b2@linux.ibm.com?part=12

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v5 02/21] KVM: s390: vsie: Move SCAO validation into a function
  2026-08-26 15:05 ` [PATCH v5 02/21] KVM: s390: vsie: Move SCAO validation into a function Christoph Schlameuss
@ 2026-08-26 15:15   ` sashiko-bot
  0 siblings, 0 replies; 43+ messages in thread
From: sashiko-bot @ 2026-08-26 15:15 UTC (permalink / raw)
  To: Christoph Schlameuss
  Cc: Alexander Gordeev, Heiko Carstens, Vasily Gorbik,
	Christian Borntraeger, linux-s390, kvm

> 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/20260826-vsie-sigpi-v5-0-cf471edb46b2@linux.ibm.com?part=2


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v5 14/21] KVM: s390: vsie: Add helper to pin and unpin multiple guest pages
  2026-08-26 15:05 ` [PATCH v5 14/21] KVM: s390: vsie: Add helper to pin and unpin multiple guest pages Christoph Schlameuss
@ 2026-08-26 15:15   ` sashiko-bot
  0 siblings, 0 replies; 43+ messages in thread
From: sashiko-bot @ 2026-08-26 15:15 UTC (permalink / raw)
  To: Christoph Schlameuss
  Cc: linux-s390, Vasily Gorbik, kvm, Heiko Carstens, Alexander Gordeev,
	Christian Borntraeger

> 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/20260826-vsie-sigpi-v5-0-cf471edb46b2@linux.ibm.com?part=14


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v5 05/21] KVM: s390: vsie: Move pin/unpin guest page
  2026-08-26 15:05 ` [PATCH v5 05/21] KVM: s390: vsie: Move pin/unpin guest page Christoph Schlameuss
@ 2026-08-26 15:17   ` sashiko-bot
  0 siblings, 0 replies; 43+ messages in thread
From: sashiko-bot @ 2026-08-26 15:17 UTC (permalink / raw)
  To: Christoph Schlameuss
  Cc: linux-s390, Heiko Carstens, Vasily Gorbik, kvm,
	Christian Borntraeger, Alexander Gordeev

> Move {,un}pin_guest_page() 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/20260826-vsie-sigpi-v5-0-cf471edb46b2@linux.ibm.com?part=5


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v5 11/21] KVM: s390: vsie: Add helper reset_vsie_page() and unshadow_intercept()
  2026-08-26 15:05 ` [PATCH v5 11/21] KVM: s390: vsie: Add helper reset_vsie_page() and unshadow_intercept() Christoph Schlameuss
@ 2026-08-26 15:18   ` sashiko-bot
  0 siblings, 0 replies; 43+ messages in thread
From: sashiko-bot @ 2026-08-26 15:18 UTC (permalink / raw)
  To: Christoph Schlameuss
  Cc: Heiko Carstens, Alexander Gordeev, kvm, linux-s390,
	Christian Borntraeger, Vasily Gorbik

> Extract helper method from existing code to reuse in new code.
> 
> reset_vsie_page() clears and releases gmap related to vsie_page.
> 
> 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/20260826-vsie-sigpi-v5-0-cf471edb46b2@linux.ibm.com?part=11


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v5 10/21] KVM: s390: vsie: Refactor kvm_s390_vsie_destroy and extract reusable methods
  2026-08-26 15:05 ` [PATCH v5 10/21] KVM: s390: vsie: Refactor kvm_s390_vsie_destroy and extract reusable methods Christoph Schlameuss
@ 2026-08-26 15:19   ` sashiko-bot
  0 siblings, 0 replies; 43+ messages in thread
From: sashiko-bot @ 2026-08-26 15:19 UTC (permalink / raw)
  To: Christoph Schlameuss
  Cc: Heiko Carstens, Alexander Gordeev, Christian Borntraeger,
	Vasily Gorbik, linux-s390, kvm

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The newly introduced helper `kvm_s390_vsie_destroy_page()` calls `unpin_scb()` with incorrect arguments and types, causing a direct build break and a logical panic.
--

commit 76eef6d237151922051461101295712b30214ae6
Author: Christoph Schlameuss <schlameuss@linux.ibm.com>

KVM: s390: vsie: Refactor kvm_s390_vsie_destroy and extract reusable methods

This commit refactors kvm_s390_vsie_destroy() by extracting the 
gmap shadow release logic into a safe helper and creating 
kvm_s390_vsie_destroy_page() to reduce nesting. These helper 
methods are intended for reuse in upcoming patches.

> diff --git a/arch/s390/kvm/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
> index 75676f493ab24..5e41dd151f8e4 100644
> --- a/arch/s390/kvm/s390/vsie.c
> +++ b/arch/s390/kvm/s390/vsie.c

[ ... ]

> @@ -1658,24 +1665,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);

[Severity: High]
Does this code call unpin_scb() with the correct arguments?

At this point in the series, unpin_scb() is defined as:

static void unpin_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,
                      gpa_t gpa)

This call passes only 2 arguments instead of 3, and provides a struct kvm *
instead of the expected struct kvm_vcpu *, which breaks the build.

Additionally, could this cause a panic when executing kvm_s390_vsie_destroy()
during VM teardown (a path reachable by untrusted userspace)?

At this stage in the series, vsie_page->scb_o is already unpinned and
set to NULL for cached pages. Calling unpin_scb() here will unconditionally
call virt_to_phys(vsie_page->scb_o), which panics when operating on NULL.

Note that this appears to be a patch splitting error where teardown code
intended for a later patch was introduced too early. The build and logic
errors are resolved later in the series by commit
5c64511a5db61f1b968b748560aa62a1a39b08a9 ('KVM: s390: vsie: Lazily keep
original scb pinned after vsie exit').

> +	release_gmap_shadow_safe(kvm, vsie_page);
> +	free_vsie_page(vsie_page);
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260826-vsie-sigpi-v5-0-cf471edb46b2@linux.ibm.com?part=10

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v5 19/21] KVM: s390: vsie: Add VSIE shadow stat counters
  2026-08-26 15:05 ` [PATCH v5 19/21] KVM: s390: vsie: Add VSIE shadow stat counters Christoph Schlameuss
@ 2026-08-26 15:19   ` sashiko-bot
  0 siblings, 0 replies; 43+ messages in thread
From: sashiko-bot @ 2026-08-26 15:19 UTC (permalink / raw)
  To: Christoph Schlameuss
  Cc: linux-s390, Christian Borntraeger, kvm, Heiko Carstens,
	Vasily Gorbik, Alexander Gordeev

> 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>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260826-vsie-sigpi-v5-0-cf471edb46b2@linux.ibm.com?part=19


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v5 17/21] KVM: s390: vsie: Allow guest-3 cpu add and remove with ssca
  2026-08-26 15:05 ` [PATCH v5 17/21] KVM: s390: vsie: Allow guest-3 cpu add and remove with ssca Christoph Schlameuss
@ 2026-08-26 15:23   ` sashiko-bot
  0 siblings, 0 replies; 43+ messages in thread
From: sashiko-bot @ 2026-08-26 15:23 UTC (permalink / raw)
  To: Christoph Schlameuss
  Cc: Heiko Carstens, Christian Borntraeger, linux-s390, Vasily Gorbik,
	Alexander Gordeev, kvm

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Shadow SCA does not clear entries for CPUs that are removed from the configuration, leading to incorrect SIGP delivery.
--

commit c4d994f0fee41044da7d25bf24f02291a68cd80a
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.

> diff --git a/arch/s390/kvm/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
> index 4bd11eaa7d81d..6e42b00206bd9 100644
> --- a/arch/s390/kvm/s390/vsie.c
> +++ b/arch/s390/kvm/s390/vsie.c

[ ... ]

> @@ -2126,17 +2163,17 @@ static int _shadow_sca(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,
>  	bool is_esca = sie_uses_esca(vsie_page->scb_o);
>  	unsigned int cpu_nr, cpu_slots;
>  	hpa_t sca_o_entry_hpa;
> -	unsigned long *mcn;
>  	int rc;
>  
>  	if (is_esca)
> -		mcn = phys_to_virt(sca_o_hpa(vsie_sca)) + offsetof(struct esca_block, mcn);
> +		set_bit(VSIE_SCA_ESCA, &vsie_sca->flags);
>  	else
> -		mcn = phys_to_virt(sca_o_hpa(vsie_sca)) + offsetof(struct bsca_block, mcn);
> +		clear_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) {

[Severity: High]
Does this code skip clearing entries for CPUs that are removed from the
configuration?

When guest-2 removes a CPU, its bit is cleared in the original SCA's mcn.
Then KVM detects the mcn change and calls _shadow_sca().

Since this loop copies the new mcn and iterates only over the set bits using
for_each_set_bit_inv(), won't the ssca.cpu entries for the removed CPUs be
skipped and never zeroed out?

Because the hardware SSCA structure lacks an mcn mask and relies on ssda == 0
to identify non-existent CPUs, could the hardware continue to use the stale,
non-zero shadow pointers and process SIGPs for removed CPUs instead of
returning CC 3 (Not Operational)?

>  		rc = get_sca_entry_addr(vcpu->kvm, vsie_sca, cpu_nr, NULL, &sca_o_entry_hpa);
>  		if (rc)
>  			break;

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260826-vsie-sigpi-v5-0-cf471edb46b2@linux.ibm.com?part=17

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v5 18/21] KVM: s390: vsie: Add VSIE max shadow configuration
  2026-08-26 15:05 ` [PATCH v5 18/21] KVM: s390: vsie: Add VSIE max shadow configuration Christoph Schlameuss
@ 2026-08-26 15:24   ` sashiko-bot
  0 siblings, 0 replies; 43+ messages in thread
From: sashiko-bot @ 2026-08-26 15:24 UTC (permalink / raw)
  To: Christoph Schlameuss
  Cc: Vasily Gorbik, Alexander Gordeev, linux-s390, kvm,
	Christian Borntraeger, Heiko Carstens

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Unbounded memory leak of XArray nodes in `addr_to_page` due to missing `xa_erase` when reusing a `vsie_page`.
--

commit aeb8317597460836fd64fcf59420cb809c866f05
Author: Christoph Schlameuss <schlameuss@linux.ibm.com>

KVM: s390: vsie: Add VSIE max shadow configuration

Introduce two new module parameters allowing to keep more shadow
structures

[ ... ]

> diff --git a/arch/s390/kvm/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
> index 6e42b00206bd9..fe94587511b03 100644
> --- a/arch/s390/kvm/s390/vsie.c
> +++ b/arch/s390/kvm/s390/vsie.c

[ ... ]

> @@ -1962,7 +1973,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) {

[Severity: High]
This is a pre-existing issue, but does reusing a vsie_page here lead to an
unbounded memory leak of XArray nodes in addr_to_page?

When the vsie_page pool reaches max_vsie_page, get_vsie_page() recycles an
existing vsie_page:

arch/s390/kvm/s390/vsie.c:get_vsie_page() {
    ...
	} else {
		/* reuse an existing entry that belongs to nobody */
		while (true) {
			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;
		}

		unpin_scb(kvm, vsie_page);
		kvm->stat.vsie_shadow_scb_reuse++;
	}

	rc = init_vsie_page(vcpu, vsie_page, addr);
	if (!rc)
		xa_store(&kvm->arch.vsie.addr_to_page, addr >> SCB_ALIGNMENT_SHIFT, vsie_page,
			 GFP_KERNEL_ACCOUNT);
    ...
}

Should xa_erase() be called for the old index (vsie_page->scb_gpa >>
SCB_ALIGNMENT_SHIFT) before the vsie_page is repurposed?

If a nested guest repeatedly executes the SIE instruction with continuously
varying scb_addr values, the new address is stored in the XArray via
xa_store(), but the old index appears to never be removed.

Since XArray internally allocates memory for nodes as new indices are
inserted, can a malicious guest intentionally trigger unbounded allocation
of XArray nodes, leading to host or cgroup OOM?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260826-vsie-sigpi-v5-0-cf471edb46b2@linux.ibm.com?part=18

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v5 13/21] KVM: s390: vsie: Lazily keep original scb pinned after vsie exit
  2026-08-26 15:05 ` [PATCH v5 13/21] KVM: s390: vsie: Lazily keep original scb pinned after vsie exit Christoph Schlameuss
@ 2026-08-26 15:25   ` sashiko-bot
  0 siblings, 0 replies; 43+ messages in thread
From: sashiko-bot @ 2026-08-26 15:25 UTC (permalink / raw)
  To: Christoph Schlameuss
  Cc: linux-s390, Vasily Gorbik, kvm, Heiko Carstens,
	Christian Borntraeger, Alexander Gordeev

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Critical] Removing the old SCB address from the XArray when recycling a vsie_page causes an unbounded host memory leak.
- [High] Unconditional `xa_store()` silently overwrites concurrent entries, bypassing KVM's protection against concurrent nested SCB execution.
- [High] Misrouting of addressing exceptions to the 'double use' path causes improper guest PSW rewinding.
--

commit 5c64511a5db61f1b968b748560aa62a1a39b08a9
Author: Christoph Schlameuss <schlameuss@linux.ibm.com>

KVM: s390: vsie: Lazily keep original scb pinned after vsie exit

Keep the original SIE control block (SCB) pinned and only lazily unpin
it on reuse of the vsie_page for a different SCB. Track pinned pages and
ensure stale pins are released when needed.

> diff --git a/arch/s390/kvm/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
> index 988370f491bf5..24af48af89cd9 100644
> --- a/arch/s390/kvm/s390/vsie.c
> +++ b/arch/s390/kvm/s390/vsie.c
[ ... ]
> @@ -1575,47 +1606,65 @@ static struct vsie_page *get_vsie_page(struct kvm *kvm, unsigned long addr)
[ ... ]
>  	} 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 >> SCB_ALIGNMENT_SHIFT);
> -		/* Mark it as invalid until it resides in the tree. */
> -		vsie_page->scb_gpa = ULONG_MAX;
> +
> +		unpin_scb(kvm, vsie_page);
>  	}

[Severity: Critical]
Why is xa_erase() removed here? Leaving the old guest SCB address mapping in
the XArray when recycling a vsie_page allows a malicious guest to repeatedly
execute vSIE with unique SCB addresses, continuously consuming host kernel
memory by allocating new XArray entries. Could this lead to an unbounded
host memory leak?

>  
> -	/* Double use of the same address or allocation failure. */
> -	if (xa_insert(&kvm->arch.vsie.addr_to_page, addr >> SCB_ALIGNMENT_SHIFT, vsie_page,
> -		      GFP_KERNEL_ACCOUNT)) {
> +	rc = init_vsie_page(vcpu, vsie_page, addr);
> +	if (!rc)
> +		xa_store(&kvm->arch.vsie.addr_to_page, addr >> SCB_ALIGNMENT_SHIFT, vsie_page,
> +			 GFP_KERNEL_ACCOUNT);

[Severity: High]
Does xa_store() here bypass the concurrent double-use protection?

The baseline code used xa_insert() which gracefully returns an error if an
entry already exists, preventing two vCPUs from executing the same nested SCB
concurrently.

By replacing this with an unconditional xa_store(), the second vCPU will
silently overwrite the XArray entry. Both vCPUs will enter nested SIE using
different shadow structures for the same physical guest SCB, and when they exit,
both will concurrently write back to scb_o in unshadow_scb().

Could this cause severe nested guest state corruption?

> +
> +	mutex_unlock(&kvm->arch.vsie.mutex);
> +	if (vsie_page_new)
> +		free_vsie_page(vsie_page_new);
> +	if (rc) {
> +		vsie_page->scb_gpa = ULONG_MAX;
>  		put_vsie_page(vsie_page);
> -		mutex_unlock(&kvm->arch.vsie.mutex);
> -		return NULL;
> +		return rc < 0 ? ERR_PTR(rc) : NULL;
>  	}

[Severity: High]
If a guest provides an invalid SCB address, pin_scb() injects a PGM_ADDRESSING
program exception and returns 1. Here, rc < 0 ? ERR_PTR(rc) : NULL will convert
this 1 into a NULL pointer return.

The caller kvm_s390_handle_vsie() interprets a NULL return strictly as a 'double
use' scenario and executes kvm_s390_rewind_psw(vcpu, 4). Doesn't this
incorrectly rewind the guest PSW after a program exception has already been
injected?

Furthermore, if follow-up patches in the series return ERR_PTR(rc) directly
(evaluating to ERR_PTR(1)), an IS_ERR check in the caller will fail to catch
it because 1 is a valid pointer address, leading to a host kernel panic when
dereferenced.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260826-vsie-sigpi-v5-0-cf471edb46b2@linux.ibm.com?part=13

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v5 16/21] KVM: s390: vsie: Shadow VSIE SCA in guest-1
  2026-08-26 15:05 ` [PATCH v5 16/21] KVM: s390: vsie: Shadow VSIE SCA in guest-1 Christoph Schlameuss
@ 2026-08-26 15:25   ` sashiko-bot
  0 siblings, 0 replies; 43+ messages in thread
From: sashiko-bot @ 2026-08-26 15:25 UTC (permalink / raw)
  To: Christoph Schlameuss
  Cc: linux-s390, Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
	Christian Borntraeger, kvm

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Critical] `get_vsie_page_cpu_nr()` incorrectly wraps positive return codes (like 1 for validity intercepts) in `ERR_PTR()`, bypassing `IS_ERR()` checks and causing a host kernel panic.
- [High] Stale `sca_gpa` in reused `vsie_sca` causes incorrect `xa_erase()` of valid shadow SCAs.
- [High] `get_vsie_sca()` incorrectly translates guest validity intercepts or unbacked SCA errors into KVM_RUN `-EINVAL` exits, abruptly crashing the VM.
- [Medium] Guest-triggerable `WARN_ON_ONCE` if `xa_store()` fails due to memory pressure.
--

commit 763d66e9883805b058573509abca88b10cdfb124
Author: Christoph Schlameuss <schlameuss@linux.ibm.com>

KVM: s390: vsie: Shadow VSIE SCA in guest-1
    
This commit restructures kvm_s390_handle_vsie() to create a guest-1 shadow
of the SCA if guest-2 attempts to enter SIE with an SCA. The original SCA
is pinned in guest-2 memory, and references to existing shadow SCAs are
stored in a map to efficiently reuse them.

> diff --git a/arch/s390/kvm/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
> index 3c94faa35b44e..4bd11eaa7d81d 100644
> --- a/arch/s390/kvm/s390/vsie.c
> +++ b/arch/s390/kvm/s390/vsie.c
[ ... ]
> @@ -958,6 +1051,128 @@ 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)
[ ... ]
> +/* 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)
> +{
[ ... ]
> +	/* 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);

[Severity: High]
Does this code incorrectly translate a guest validity intercept into a fatal
host KVM error?

When validate_scao() fails, it returns a positive validity intercept value
(like 1). Returning ERR_PTR(-EINVAL) here overrides that intercept and
returns -EINVAL instead.

> +
> +	down_read(&kvm->arch.vsie.vsie_sca_lock);
> +	vsie_sca = get_vsie_sca_existing(kvm, sca_gpa, is_esca);
> +	up_read(&kvm->arch.vsie.vsie_sca_lock);
> +	if (vsie_sca)
> +		return vsie_sca;
[ ... ]
> +	/* 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) {
[ ... ]
> +	} else {
> +		/* reuse previously created vsie_sca allocation for different osca */
> +		vsie_sca = get_vsie_sca_unused(kvm);
> +		/* with nr_vcpus scas one must be reusable */
> +		if (IS_ERR(vsie_sca))
> +			goto out;
> +
> +		xa_erase(&kvm->arch.vsie.osca_to_sca, vsie_sca->sca_gpa >> SCA_ALIGNMENT_SHIFT);

[Severity: High]
If we are reusing an orphaned vsie_sca, does its sca_gpa still hold the old,
stale address? If so, does this xa_erase() inadvertently remove a valid
active shadow SCA from the osca_to_sca xarray?

[ ... ]
> +	rc = pin_sca(kvm, vsie_sca);
> +	if (rc) {
> +		vsie_sca->sca_gpa = ULONG_MAX;
> +		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_ACCOUNT));

[Severity: Medium]
If xa_store() fails due to memory pressure, it returns an error pointer.
Since this is a non-null value, will this WARN_ON_ONCE trigger?

Guest-reachable warnings can be exploited for log spam or DoS.

> +
> +out:
> +	up_write(&kvm->arch.vsie.vsie_sca_lock);
> +	if (vsie_sca_new)
> +		free_vsie_sca(kvm, vsie_sca_new);
> +	return vsie_sca;
> +}
[ ... ]
> @@ -1815,11 +2027,165 @@ static struct vsie_page *get_vsie_page(struct kvm_vcpu *vcpu, unsigned long addr
[ ... ]
> +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)
> +{
[ ... ]
> +	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 = init_vsie_page(vcpu, vsie_page, scb_gpa);
> +		}
> +		if (rc) {
> +			put_vsie_page(vsie_page);
> +			return ERR_PTR(rc);
> +		}

[Severity: Critical]
If init_vsie_page() returns a positive validity intercept (such as 1), does
wrapping it in ERR_PTR(rc) return (void *)1?

Since 1 is not >= -MAX_ERRNO, IS_ERR() downstream will evaluate to false,
treating this as a valid pointer.

> +
> +		reset_vsie_page(vcpu->kvm, vsie_page);
> +	}
> +
> +	return vsie_page;
> +}
[ ... ]
> +static int _shadow_sca_cpu(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,
> +			   struct vsie_sca *vsie_sca, hpa_t sca_o_entry_hpa,
> +			   unsigned int cpu_nr, bool is_esca)
> +{
[ ... ]
> +	vsie_page_n = get_vsie_page_cpu_nr(vcpu, vsie_sca, scb_o_gpa, cpu_nr);
> +	if (!vsie_page_n)
> +		return -EAGAIN;
> +	if (IS_ERR(vsie_page_n))
> +		return PTR_ERR(vsie_page_n);
> +
> +	rc = shadow_scb(vcpu, vsie_page_n);

[Severity: Critical]
Because IS_ERR(vsie_page_n) evaluates to false for (void *)1, does this allow
the invalid pointer 0x1 to be dereferenced in shadow_scb(), leading to a host
kernel panic?

[ ... ]
> @@ -1838,33 +2204,71 @@ int kvm_s390_handle_vsie(struct kvm_vcpu *vcpu)
[ ... ]
> +	} 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;
> +		}

[Severity: High]
If get_vsie_sca() returned ERR_PTR(-EINVAL) due to validate_scao() failing
above, rc becomes -EINVAL here.

Does returning -EINVAL abort the KVM_RUN ioctl entirely, abruptly crashing
the VM instead of gracefully reflecting an error to the guest?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260826-vsie-sigpi-v5-0-cf471edb46b2@linux.ibm.com?part=16

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v5 15/21] KVM: s390: vsie: Add struct vsie_sca with pin and unpin methods
  2026-08-26 15:05 ` [PATCH v5 15/21] KVM: s390: vsie: Add struct vsie_sca with pin and unpin methods Christoph Schlameuss
@ 2026-08-26 15:26   ` sashiko-bot
  0 siblings, 0 replies; 43+ messages in thread
From: sashiko-bot @ 2026-08-26 15:26 UTC (permalink / raw)
  To: Christoph Schlameuss
  Cc: Vasily Gorbik, kvm, linux-s390, Heiko Carstens, Alexander Gordeev,
	Christian Borntraeger

> 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>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260826-vsie-sigpi-v5-0-cf471edb46b2@linux.ibm.com?part=15


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v5 21/21] KVM: s390: vsie: Enable use of VSIE SSCA
  2026-08-26 15:05 ` [PATCH v5 21/21] KVM: s390: vsie: Enable use of VSIE SSCA Christoph Schlameuss
@ 2026-08-26 15:33   ` sashiko-bot
  0 siblings, 0 replies; 43+ messages in thread
From: sashiko-bot @ 2026-08-26 15:33 UTC (permalink / raw)
  To: Christoph Schlameuss
  Cc: Heiko Carstens, Christian Borntraeger, Vasily Gorbik, linux-s390,
	kvm, Alexander Gordeev

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Critical] get_vsie_page_cpu_nr() erroneously wraps a positive return code (1) in ERR_PTR(), bypassing IS_ERR() checks and causing a host kernel panic.
- [High] VSIE SIGPI interpretation feature is globally enabled, bypassing KVM CPU feature negotiation and leaking the feature to nested guests.
- [High] get_vsie_sca() propagates validation and pinning errors as ERR_PTR(-EINVAL), crashing the VM instead of injecting an intercept.
--

commit accdb605c2c295d895867d6cf5f606b51df2bb5a
Author: Christoph Schlameuss <schlameuss@linux.ibm.com>

KVM: s390: vsie: Enable use of VSIE SSCA

This commit enables the use of VSIE SSCA code paths for handling SIGPI
and SRSI instructions without exiting VSIE. The feature defaults to true
on supported hardware.

> diff --git a/arch/s390/kvm/s390/s390.c b/arch/s390/kvm/s390/s390.c
> index a1ce75be3955b..ffa5bbb8713bc 100644
> --- a/arch/s390/kvm/s390/s390.c
> +++ b/arch/s390/kvm/s390/s390.c
[ ... ]
> @@ -3363,7 +3367,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: High]
Does this bypass KVM CPU feature negotiation and leak the SSCA feature to
nested guests?

When ssca is enabled here, use_ssca() in arch/s390/kvm/s390/vsie.c relies
purely on kvm->arch.use_ssca:

arch/s390/kvm/s390/vsie.c:use_ssca() {
	if (!kvm->arch.use_ssca)
		return false;
...
}

The hardware intercept bits are then unconditionally copied to the shadow SCB
without validating the guest's CPU feature model:

arch/s390/kvm/s390/vsie.c:prepare_scb() {
...
	if (vsie_page->vsie_sca) {
		scb_s->eca |= scb_o->eca & ECA_SIGPI;
		scb_s->ecb |= scb_o->ecb & ECB_SRSI;
...
}

Could this break live migration by exposing hardware features to the guest
without QEMU explicitly opting in via the KVM_S390_VM_CPU_FEAT_SIGPIF CPU
feature?

[Severity: Critical]
Does enabling this feature expose a host kernel panic when handling invalid
SCB addresses?

Enabling use_ssca routes execution to get_vsie_page_cpu_nr() in
kvm_s390_handle_vsie(). If an L1 guest provides an invalid SCB address,
init_vsie_page() injects a program interrupt and returns 1. However,
get_vsie_page_cpu_nr() wraps this positive return code in ERR_PTR:

arch/s390/kvm/s390/vsie.c:get_vsie_page_cpu_nr() {
...
	rc = init_vsie_page(vcpu, vsie_page_new, scb_gpa);
	if (rc) {
		put_vsie_page(vsie_page);
		return ERR_PTR(rc);
	}
...
}

The caller then checks IS_ERR(vsie_page), which evaluates to false for a
positive value like (void*)1:

arch/s390/kvm/s390/vsie.c:kvm_s390_handle_vsie() {
...
		vsie_page = get_vsie_page_cpu_nr(vcpu, vsie_sca, scb_addr,
						 scb_o->icpua & KVM_S390_CPU_MASK);
		if (IS_ERR(vsie_page)) {
			rc = PTR_ERR(vsie_page);
...
}

Will this allow execution to proceed and dereference the invalid pointer,
leading to a full host denial of service?

[Severity: High]
Can an invalid SCA origin provided by an L1 guest cause a complete VM abort?

Similar to the regression above, the newly reachable get_vsie_sca()
converts the return code 1 from validate_scao() into a hard -EINVAL error:

arch/s390/kvm/s390/vsie.c:get_vsie_sca() {
...
	rc = validate_scao(vcpu, scb_o, sca_gpa);
	if (rc)
		return ERR_PTR(-EINVAL);
...
}

It also propagates pinning errors directly as hard errors rather than
injecting intercepts:

arch/s390/kvm/s390/vsie.c:get_vsie_sca() {
...
	rc = pin_sca(kvm, vsie_sca);
	if (rc) {
		vsie_sca->sca_gpa = ULONG_MAX;
		put_vsie_sca(vsie_sca);
		vsie_sca = ERR_PTR(rc);
		goto out;
	}
...
}

These errors are propagated all the way up to kvm_arch_vcpu_ioctl_run(),
which returns -EINVAL to QEMU and aborts the entire VM. Is it
intended to crash the VM instead of gracefully resuming the guest with an
intercept when a misaligned or unmapped SCAO is encountered?

>  	spin_lock_init(&kvm->arch.start_stop_lock);
>  	kvm_s390_vsie_init(kvm);
>  	if (use_gisa)

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260826-vsie-sigpi-v5-0-cf471edb46b2@linux.ibm.com?part=21

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v5 20/21] KVM: s390: vsie: Create minimal scb shadows for not running g3 blocks
  2026-08-26 15:05 ` [PATCH v5 20/21] KVM: s390: vsie: Create minimal scb shadows for not running g3 blocks Christoph Schlameuss
@ 2026-08-26 15:35   ` sashiko-bot
  0 siblings, 0 replies; 43+ messages in thread
From: sashiko-bot @ 2026-08-26 15:35 UTC (permalink / raw)
  To: Christoph Schlameuss
  Cc: Heiko Carstens, linux-s390, kvm, Alexander Gordeev, Vasily Gorbik,
	Christian Borntraeger

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] Hardware SIGPI execution can read a partially zeroed `scb_s` structure while KVM is executing `memset` on it, leading to host memory corruption via wild physical address writes.
--

commit 6a6b22a554f022a2f999c91a98c908cfacf9ce2d
Author: Christoph Schlameuss <schlameuss@linux.ibm.com>

KVM: s390: vsie: Create minimal scb shadows for not running g3 blocks

This commit introduces a minimal shadow of the vSIE control block for
guest-3 blocks that are not currently running. The minimal shadow only
contains the information needed for SIGPI and SRSI and is recreated fully
when a vSIE is entered with that SCB later.

[ ... ]

> diff --git a/arch/s390/kvm/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
> index 335ed1a927aa6..86ccd92233b5c 100644
> --- a/arch/s390/kvm/s390/vsie.c
> +++ b/arch/s390/kvm/s390/vsie.c

[ ... ]

> @@ -2166,7 +2186,7 @@ static int _shadow_sca_cpu(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,
>  	if (IS_ERR(vsie_page_n))
>  		return PTR_ERR(vsie_page_n);
>  
> -	rc = shadow_scb(vcpu, vsie_page_n);
> +	rc = shadow_scb_minimal(vcpu, vsie_page_n);

[Severity: Critical]
Can this lead to host memory corruption if a malicious guest-2 changes the
SDA (scb_gpa) for a nested vCPU in its SCA while another guest-2 vCPU is
actively running sie for the same VM?

When guest-2 enters sie for the modified vCPU, KVM's get_vsie_page_cpu_nr()
call just above this block unpins the old SCB and clears the shadow SCB:

arch/s390/kvm/s390/vsie.c:reset_vsie_page() {
    ...
    memset(&vsie_page->scb_s, 0, sizeof(struct kvm_s390_sie_block));
    ...
}

Does this memset execute while the other hardware thread is actively allowed
to follow the ssda pointer to it?

If osda is zeroed while hardware SIGPI execution reads this partially
zeroed scb_s structure, it appears hardware could write to absolute
physical address 0 (or a freed host page), causing host memory corruption.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260826-vsie-sigpi-v5-0-cf471edb46b2@linux.ibm.com?part=20

^ permalink raw reply	[flat|nested] 43+ messages in thread

end of thread, other threads:[~2026-08-26 15:35 UTC | newest]

Thread overview: 43+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-26 15:05 [PATCH v5 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
2026-08-26 15:05 ` [PATCH v5 01/21] KVM: s390: vsie: Add SCAO read and write helpers Christoph Schlameuss
2026-08-26 15:12   ` sashiko-bot
2026-08-26 15:05 ` [PATCH v5 02/21] KVM: s390: vsie: Move SCAO validation into a function Christoph Schlameuss
2026-08-26 15:15   ` sashiko-bot
2026-08-26 15:05 ` [PATCH v5 03/21] KVM: s390: vsie: Add vsie_interp_extf detection Christoph Schlameuss
2026-08-26 15:12   ` sashiko-bot
2026-08-26 15:05 ` [PATCH v5 04/21] KVM: s390: vsie: Add ssca_block and ssca_entry structs Christoph Schlameuss
2026-08-26 15:09   ` sashiko-bot
2026-08-26 15:05 ` [PATCH v5 05/21] KVM: s390: vsie: Move pin/unpin guest page Christoph Schlameuss
2026-08-26 15:17   ` sashiko-bot
2026-08-26 15:05 ` [PATCH v5 06/21] KVM: s390: vsie: Move pin/unpin_scb methods Christoph Schlameuss
2026-08-26 15:10   ` sashiko-bot
2026-08-26 15:05 ` [PATCH v5 07/21] KVM: s390: vsie: Move release/acquire gmap shadow Christoph Schlameuss
2026-08-26 15:14   ` sashiko-bot
2026-08-26 15:05 ` [PATCH v5 08/21] KVM: s390: vsie: Create helpers to alloc and free vsie_pages Christoph Schlameuss
2026-08-26 15:10   ` sashiko-bot
2026-08-26 15:05 ` [PATCH v5 09/21] KVM: s390: vsie: Replace radix_tree with xarray addr_to_page Christoph Schlameuss
2026-08-26 15:14   ` sashiko-bot
2026-08-26 15:05 ` [PATCH v5 10/21] KVM: s390: vsie: Refactor kvm_s390_vsie_destroy and extract reusable methods Christoph Schlameuss
2026-08-26 15:19   ` sashiko-bot
2026-08-26 15:05 ` [PATCH v5 11/21] KVM: s390: vsie: Add helper reset_vsie_page() and unshadow_intercept() Christoph Schlameuss
2026-08-26 15:18   ` sashiko-bot
2026-08-26 15:05 ` [PATCH v5 12/21] KVM: s390: vsie: Add helper unshadow_intercept() Christoph Schlameuss
2026-08-26 15:14   ` sashiko-bot
2026-08-26 15:05 ` [PATCH v5 13/21] KVM: s390: vsie: Lazily keep original scb pinned after vsie exit Christoph Schlameuss
2026-08-26 15:25   ` sashiko-bot
2026-08-26 15:05 ` [PATCH v5 14/21] KVM: s390: vsie: Add helper to pin and unpin multiple guest pages Christoph Schlameuss
2026-08-26 15:15   ` sashiko-bot
2026-08-26 15:05 ` [PATCH v5 15/21] KVM: s390: vsie: Add struct vsie_sca with pin and unpin methods Christoph Schlameuss
2026-08-26 15:26   ` sashiko-bot
2026-08-26 15:05 ` [PATCH v5 16/21] KVM: s390: vsie: Shadow VSIE SCA in guest-1 Christoph Schlameuss
2026-08-26 15:25   ` sashiko-bot
2026-08-26 15:05 ` [PATCH v5 17/21] KVM: s390: vsie: Allow guest-3 cpu add and remove with ssca Christoph Schlameuss
2026-08-26 15:23   ` sashiko-bot
2026-08-26 15:05 ` [PATCH v5 18/21] KVM: s390: vsie: Add VSIE max shadow configuration Christoph Schlameuss
2026-08-26 15:24   ` sashiko-bot
2026-08-26 15:05 ` [PATCH v5 19/21] KVM: s390: vsie: Add VSIE shadow stat counters Christoph Schlameuss
2026-08-26 15:19   ` sashiko-bot
2026-08-26 15:05 ` [PATCH v5 20/21] KVM: s390: vsie: Create minimal scb shadows for not running g3 blocks Christoph Schlameuss
2026-08-26 15:35   ` sashiko-bot
2026-08-26 15:05 ` [PATCH v5 21/21] KVM: s390: vsie: Enable use of VSIE SSCA Christoph Schlameuss
2026-08-26 15:33   ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox