Kernel KVM virtualization development
 help / color / mirror / Atom feed
* [PATCH v6 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation
@ 2026-08-27 15:52 Christoph Schlameuss
  2026-08-27 15:52 ` [PATCH v6 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-27 15:52 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, 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.

---
Do not expect any answers in the next two weeks.

---
Changes in v6:
- Fix valid sashiko findings
- Change some get_vsie_* functions to have a normal rc and out parameter
  instead of returning ERR_PTR
- Re-insert xa_erase hunk lost on rebasing
- Fix compile problems in intermediate patches
- Link to v5: https://patch.msgid.link/20260826-vsie-sigpi-v5-0-cf471edb46b2@linux.ibm.com

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                      | 1320 +++++++++++++++++++-----
 drivers/s390/char/sclp_early.c                 |    1 +
 tools/testing/selftests/kvm/include/s390/sie.h |    2 +-
 7 files changed, 1118 insertions(+), 277 deletions(-)
---
base-commit: 73e3f0710014fe6d4ed98cfc02292f6121db7558
change-id: 20251113-vsie-sigpi-3de01243efd6

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


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

* [PATCH v6 01/21] KVM: s390: vsie: Add SCAO read and write helpers
  2026-08-27 15:52 [PATCH v6 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
@ 2026-08-27 15:52 ` Christoph Schlameuss
  2026-08-27 16:00   ` sashiko-bot
  2026-08-27 15:52 ` [PATCH v6 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-27 15:52 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, 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 v6 02/21] KVM: s390: vsie: Move SCAO validation into a function
  2026-08-27 15:52 [PATCH v6 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
  2026-08-27 15:52 ` [PATCH v6 01/21] KVM: s390: vsie: Add SCAO read and write helpers Christoph Schlameuss
@ 2026-08-27 15:52 ` Christoph Schlameuss
  2026-08-27 16:11   ` sashiko-bot
  2026-08-27 15:52 ` [PATCH v6 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-27 15:52 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, 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 v6 03/21] KVM: s390: vsie: Add vsie_interp_extf detection
  2026-08-27 15:52 [PATCH v6 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
  2026-08-27 15:52 ` [PATCH v6 01/21] KVM: s390: vsie: Add SCAO read and write helpers Christoph Schlameuss
  2026-08-27 15:52 ` [PATCH v6 02/21] KVM: s390: vsie: Move SCAO validation into a function Christoph Schlameuss
@ 2026-08-27 15:52 ` Christoph Schlameuss
  2026-08-27 15:59   ` sashiko-bot
  2026-08-27 15:52 ` [PATCH v6 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-27 15:52 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, 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 v6 04/21] KVM: s390: vsie: Add ssca_block and ssca_entry structs
  2026-08-27 15:52 [PATCH v6 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (2 preceding siblings ...)
  2026-08-27 15:52 ` [PATCH v6 03/21] KVM: s390: vsie: Add vsie_interp_extf detection Christoph Schlameuss
@ 2026-08-27 15:52 ` Christoph Schlameuss
  2026-08-27 15:58   ` sashiko-bot
  2026-08-27 15:52 ` [PATCH v6 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-27 15:52 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, 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 v6 05/21] KVM: s390: vsie: Move pin/unpin guest page
  2026-08-27 15:52 [PATCH v6 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (3 preceding siblings ...)
  2026-08-27 15:52 ` [PATCH v6 04/21] KVM: s390: vsie: Add ssca_block and ssca_entry structs Christoph Schlameuss
@ 2026-08-27 15:52 ` Christoph Schlameuss
  2026-08-27 16:02   ` sashiko-bot
  2026-08-27 15:52 ` [PATCH v6 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-27 15:52 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, 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 v6 06/21] KVM: s390: vsie: Move pin/unpin_scb methods
  2026-08-27 15:52 [PATCH v6 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (4 preceding siblings ...)
  2026-08-27 15:52 ` [PATCH v6 05/21] KVM: s390: vsie: Move pin/unpin guest page Christoph Schlameuss
@ 2026-08-27 15:52 ` Christoph Schlameuss
  2026-08-27 15:57   ` sashiko-bot
  2026-08-27 15:52 ` [PATCH v6 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-27 15:52 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, 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 v6 07/21] KVM: s390: vsie: Move release/acquire gmap shadow
  2026-08-27 15:52 [PATCH v6 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (5 preceding siblings ...)
  2026-08-27 15:52 ` [PATCH v6 06/21] KVM: s390: vsie: Move pin/unpin_scb methods Christoph Schlameuss
@ 2026-08-27 15:52 ` Christoph Schlameuss
  2026-08-27 16:00   ` sashiko-bot
  2026-08-27 15:52 ` [PATCH v6 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-27 15:52 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, 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 v6 08/21] KVM: s390: vsie: Create helpers to alloc and free vsie_pages
  2026-08-27 15:52 [PATCH v6 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (6 preceding siblings ...)
  2026-08-27 15:52 ` [PATCH v6 07/21] KVM: s390: vsie: Move release/acquire gmap shadow Christoph Schlameuss
@ 2026-08-27 15:52 ` Christoph Schlameuss
  2026-08-27 15:59   ` sashiko-bot
  2026-08-27 15:52 ` [PATCH v6 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-27 15:52 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, 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 v6 09/21] KVM: s390: vsie: Replace radix_tree with xarray addr_to_page
  2026-08-27 15:52 [PATCH v6 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (7 preceding siblings ...)
  2026-08-27 15:52 ` [PATCH v6 08/21] KVM: s390: vsie: Create helpers to alloc and free vsie_pages Christoph Schlameuss
@ 2026-08-27 15:52 ` Christoph Schlameuss
  2026-08-27 16:01   ` sashiko-bot
  2026-08-27 15:52 ` [PATCH v6 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-27 15:52 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, 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 v6 10/21] KVM: s390: vsie: Refactor kvm_s390_vsie_destroy and extract reusable methods
  2026-08-27 15:52 [PATCH v6 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (8 preceding siblings ...)
  2026-08-27 15:52 ` [PATCH v6 09/21] KVM: s390: vsie: Replace radix_tree with xarray addr_to_page Christoph Schlameuss
@ 2026-08-27 15:52 ` Christoph Schlameuss
  2026-08-27 16:09   ` sashiko-bot
  2026-08-27 15:52 ` [PATCH v6 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-27 15:52 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, 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 | 42 +++++++++++++++++++++++++++---------------
 1 file changed, 27 insertions(+), 15 deletions(-)

diff --git a/arch/s390/kvm/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
index 75676f493ab2..78d59fc535f7 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;
@@ -751,13 +762,13 @@ 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,
+static void unpin_scb(struct kvm *kvm, 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);
+		unpin_guest_page(kvm, gpa, hpa);
 	vsie_page->scb_o = NULL;
 }
 
@@ -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;
@@ -1644,7 +1651,7 @@ int kvm_s390_handle_vsie(struct kvm_vcpu *vcpu)
 out_unshadow:
 	unshadow_scb(vcpu, vsie_page);
 out_unpin_scb:
-	unpin_scb(vcpu, vsie_page, scb_addr);
+	unpin_scb(vcpu->kvm, vsie_page, scb_addr);
 out_put:
 	put_vsie_page(vsie_page);
 
@@ -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, vsie_page->scb_gpa);
+	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 v6 11/21] KVM: s390: vsie: Add helper reset_vsie_page() and unshadow_intercept()
  2026-08-27 15:52 [PATCH v6 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (9 preceding siblings ...)
  2026-08-27 15:52 ` [PATCH v6 10/21] KVM: s390: vsie: Refactor kvm_s390_vsie_destroy and extract reusable methods Christoph Schlameuss
@ 2026-08-27 15:52 ` Christoph Schlameuss
  2026-08-27 16:02   ` sashiko-bot
  2026-08-27 15:52 ` [PATCH v6 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-27 15:52 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, 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 78d59fc535f7..95baac9a1ccb 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 v6 12/21] KVM: s390: vsie: Add helper unshadow_intercept()
  2026-08-27 15:52 [PATCH v6 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (10 preceding siblings ...)
  2026-08-27 15:52 ` [PATCH v6 11/21] KVM: s390: vsie: Add helper reset_vsie_page() and unshadow_intercept() Christoph Schlameuss
@ 2026-08-27 15:52 ` Christoph Schlameuss
  2026-08-27 16:01   ` sashiko-bot
  2026-08-27 15:52 ` [PATCH v6 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-27 15:52 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, 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 95baac9a1ccb..c42e2df4c0ab 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 v6 13/21] KVM: s390: vsie: Lazily keep original scb pinned after vsie exit
  2026-08-27 15:52 [PATCH v6 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (11 preceding siblings ...)
  2026-08-27 15:52 ` [PATCH v6 12/21] KVM: s390: vsie: Add helper unshadow_intercept() Christoph Schlameuss
@ 2026-08-27 15:52 ` Christoph Schlameuss
  2026-08-27 16:06   ` sashiko-bot
  2026-08-27 15:52 ` [PATCH v6 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-27 15:52 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, 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 | 159 ++++++++++++++++++++++++++++++++--------------
 1 file changed, 110 insertions(+), 49 deletions(-)

diff --git a/arch/s390/kvm/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
index c42e2df4c0ab..cdce4b3b2525 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 *kvm, 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(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 *kvm, 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,19 +1579,28 @@ static void reset_vsie_page(struct kvm *kvm, struct vsie_page *vsie_page)
 /*
  * Get or create a vsie page for a scb address.
  *
- * 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
+ * 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: - -EBUSY if the same scb address is already used by another VCPU
+ *          - -ENOMEM if out of memory
  */
-static struct vsie_page *get_vsie_page(struct kvm *kvm, unsigned long addr)
+static int get_vsie_page(struct kvm_vcpu *vcpu, unsigned long addr,
+			 struct vsie_page **vsie_page_out)
 {
-	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)) {
-		if (vsie_page->scb_gpa == addr)
-			return vsie_page;
+		if (vsie_page->scb_gpa == addr) {
+			*vsie_page_out = vsie_page;
+			return 0;
+		}
 		/*
 		 * We raced with someone reusing + putting this vsie
 		 * page before we grabbed it.
@@ -1575,51 +1608,79 @@ 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 -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);
+			*vsie_page_out = vsie_page;
+			return 0;
 		}
-		__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)) {
-		put_vsie_page(vsie_page);
-		mutex_unlock(&kvm->arch.vsie.mutex);
-		return NULL;
+	rc = init_vsie_page(vcpu, vsie_page, addr);
+	if (!rc) {
+		rc = xa_insert(&kvm->arch.vsie.addr_to_page, addr >> SCB_ALIGNMENT_SHIFT, vsie_page,
+			       GFP_KERNEL_ACCOUNT);
+		if (rc == -EBUSY)
+			rc = -EAGAIN;
 	}
-	vsie_page->scb_gpa = addr;
+
 	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);
+		return rc;
+	}
 
 	reset_vsie_page(kvm, vsie_page);
 
-	return vsie_page;
+	*vsie_page_out = vsie_page;
+	return 0;
 }
 
 int kvm_s390_handle_vsie(struct kvm_vcpu *vcpu)
@@ -1645,21 +1706,23 @@ int kvm_s390_handle_vsie(struct kvm_vcpu *vcpu)
 		return 0;
 	}
 
-	vsie_page = get_vsie_page(vcpu->kvm, scb_addr);
-	if (IS_ERR(vsie_page)) {
-		return PTR_ERR(vsie_page);
-	} else if (!vsie_page) {
-		/* double use of sie control block - simply do nothing */
-		kvm_s390_rewind_psw(vcpu, 4);
-		return 0;
+	rc = get_vsie_page(vcpu, scb_addr, &vsie_page);
+	if (rc) {
+		if (rc == -EBUSY) {
+			/* double use of sie control block - simply do nothing */
+			kvm_s390_rewind_psw(vcpu, 4);
+			return 0;
+		} else {
+			return PTR_ERR(vsie_page);
+		}
 	}
 
-	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 +1732,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->kvm, vsie_page, scb_addr);
 out_put:
 	put_vsie_page(vsie_page);
 
@@ -1686,7 +1747,7 @@ void kvm_s390_vsie_init(struct kvm *kvm)
 
 static void kvm_s390_vsie_destroy_page(struct kvm *kvm, struct vsie_page *vsie_page)
 {
-	unpin_scb(kvm, vsie_page, vsie_page->scb_gpa);
+	unpin_scb(kvm, vsie_page);
 	release_gmap_shadow_safe(kvm, vsie_page);
 	free_vsie_page(vsie_page);
 }

-- 
2.55.0


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

* [PATCH v6 14/21] KVM: s390: vsie: Add helper to pin and unpin multiple guest pages
  2026-08-27 15:52 [PATCH v6 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (12 preceding siblings ...)
  2026-08-27 15:52 ` [PATCH v6 13/21] KVM: s390: vsie: Lazily keep original scb pinned after vsie exit Christoph Schlameuss
@ 2026-08-27 15:52 ` Christoph Schlameuss
  2026-08-27 16:05   ` sashiko-bot
  2026-08-27 15:52 ` [PATCH v6 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-27 15:52 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, 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 cdce4b3b2525..d52c3a42aa27 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 v6 15/21] KVM: s390: vsie: Add struct vsie_sca with pin and unpin methods
  2026-08-27 15:52 [PATCH v6 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (13 preceding siblings ...)
  2026-08-27 15:52 ` [PATCH v6 14/21] KVM: s390: vsie: Add helper to pin and unpin multiple guest pages Christoph Schlameuss
@ 2026-08-27 15:52 ` Christoph Schlameuss
  2026-08-27 16:12   ` sashiko-bot
  2026-08-27 15:52 ` [PATCH v6 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-27 15:52 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, 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 d52c3a42aa27..69334d4a3231 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 v6 16/21] KVM: s390: vsie: Shadow VSIE SCA in guest-1
  2026-08-27 15:52 [PATCH v6 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (14 preceding siblings ...)
  2026-08-27 15:52 ` [PATCH v6 15/21] KVM: s390: vsie: Add struct vsie_sca with pin and unpin methods Christoph Schlameuss
@ 2026-08-27 15:52 ` Christoph Schlameuss
  2026-08-27 16:15   ` sashiko-bot
  2026-08-27 15:52 ` [PATCH v6 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-27 15:52 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, 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                   | 474 ++++++++++++++++++++++++++--
 3 files changed, 468 insertions(+), 29 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 69334d4a3231..16273cf5cbff 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,81 @@ 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;
+
+	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 +1049,135 @@ 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 int get_vsie_sca(struct kvm_vcpu *vcpu, struct kvm_s390_sie_block *scb_o,
+		struct vsie_sca **vsie_sca_out)
+{
+	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 rc;
+
+	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) {
+		*vsie_sca_out = vsie_sca;
+		return 0;
+	}
+
+	/*
+	 * 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 -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;
+
+		/* unused vsie_sca exclusive under vsie_sca_lock write lock */
+		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);
+		goto out;
+	}
+
+	rc = xa_insert(&kvm->arch.vsie.osca_to_sca, vsie_sca->sca_gpa >> SCA_ALIGNMENT_SHIFT,
+		       vsie_sca, GFP_KERNEL_ACCOUNT);
+	if (rc == -EBUSY)
+		rc = 1;
+
+out:
+	up_write(&kvm->arch.vsie.vsie_sca_lock);
+	if (vsie_sca_new)
+		free_vsie_sca(kvm, vsie_sca_new);
+	if (vsie_sca)
+		*vsie_sca_out = vsie_sca;
+	return rc;
+}
+
 void kvm_s390_vsie_gmap_notifier(struct gmap *gmap, gpa_t start, gpa_t end)
 {
 	struct vsie_page *cur, *next;
@@ -1024,11 +1244,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 +1288,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 +1300,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 +1310,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 +1850,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)
@@ -1827,12 +2044,165 @@ static int get_vsie_page(struct kvm_vcpu *vcpu, unsigned long addr,
 	return 0;
 }
 
-int kvm_s390_handle_vsie(struct kvm_vcpu *vcpu)
+static int 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_out)
 {
-	struct vsie_page *vsie_page;
-	unsigned long scb_addr;
+	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 -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 -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 rc;
+		}
+
+		reset_vsie_page(vcpu->kvm, vsie_page);
+	}
+
+	*vsie_page_out = vsie_page;
+	return 0;
+}
+
+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);
+
+	rc = get_vsie_page_cpu_nr(vcpu, vsie_sca, scb_o_gpa, cpu_nr, &vsie_page_n);
+	if (rc)
+		return rc;
+
+	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 vsie_page *vsie_page = NULL;
+	struct vsie_sca *vsie_sca = NULL;
+	struct kvm_s390_sie_block *scb_o;
+	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))
 		return -EOPNOTSUPP;
@@ -1850,35 +2220,60 @@ int kvm_s390_handle_vsie(struct kvm_vcpu *vcpu)
 		return 0;
 	}
 
-	rc = get_vsie_page(vcpu, scb_addr, &vsie_page);
-	if (rc) {
-		if (rc == -EBUSY) {
-			/* double use of sie control block - simply do nothing */
-			kvm_s390_rewind_psw(vcpu, 4);
-			return 0;
-		} else {
-			return PTR_ERR(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 */
+		rc = get_vsie_page(vcpu, scb_addr, &vsie_page);
+		if (rc)
+			goto out_unpin;
+		vsie_page->vsie_sca = NULL;
+	} else {
+		/* get the vsie_sca with pinned original sca */
+		rc = get_vsie_sca(vcpu, scb_o, &vsie_sca);
+		if (rc)
+			goto out_unpin;
+		rc = get_vsie_page_cpu_nr(vcpu, vsie_sca, scb_addr,
+					  scb_o->icpua & KVM_S390_CPU_MASK, &vsie_page);
+		if (rc)
+			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;
 }
 
@@ -1887,6 +2282,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)
@@ -1900,7 +2297,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);
 
@@ -1911,7 +2309,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 v6 17/21] KVM: s390: vsie: Allow guest-3 cpu add and remove with ssca
  2026-08-27 15:52 [PATCH v6 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (15 preceding siblings ...)
  2026-08-27 15:52 ` [PATCH v6 16/21] KVM: s390: vsie: Shadow VSIE SCA in guest-1 Christoph Schlameuss
@ 2026-08-27 15:52 ` Christoph Schlameuss
  2026-08-27 16:13   ` sashiko-bot
  2026-08-27 15:52 ` [PATCH v6 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-27 15:52 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, 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 16273cf5cbff..bfd5df1f8b9c 100644
--- a/arch/s390/kvm/s390/vsie.c
+++ b/arch/s390/kvm/s390/vsie.c
@@ -2093,6 +2093,43 @@ static int get_vsie_page_cpu_nr(struct kvm_vcpu *vcpu, struct vsie_sca *vsie_sca
 	return 0;
 }
 
+/*
+ * 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)
 {
@@ -2142,17 +2179,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;
@@ -2179,19 +2216,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 v6 18/21] KVM: s390: vsie: Add VSIE max shadow configuration
  2026-08-27 15:52 [PATCH v6 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (16 preceding siblings ...)
  2026-08-27 15:52 ` [PATCH v6 17/21] KVM: s390: vsie: Allow guest-3 cpu add and remove with ssca Christoph Schlameuss
@ 2026-08-27 15:52 ` Christoph Schlameuss
  2026-08-27 16:13   ` sashiko-bot
  2026-08-27 15:52 ` [PATCH v6 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-27 15:52 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, 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 bfd5df1f8b9c..3a839154fdfe 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);
@@ -1086,7 +1096,8 @@ static int get_vsie_sca(struct kvm_vcpu *vcpu, struct kvm_s390_sie_block *scb_o,
 	 * 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();
@@ -1969,7 +1980,8 @@ static int 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 v6 19/21] KVM: s390: vsie: Add VSIE shadow stat counters
  2026-08-27 15:52 [PATCH v6 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (17 preceding siblings ...)
  2026-08-27 15:52 ` [PATCH v6 18/21] KVM: s390: vsie: Add VSIE max shadow configuration Christoph Schlameuss
@ 2026-08-27 15:52 ` Christoph Schlameuss
  2026-08-27 16:11   ` sashiko-bot
  2026-08-27 15:53 ` [PATCH v6 20/21] KVM: s390: vsie: Create minimal scb shadows for not running g3 blocks Christoph Schlameuss
  2026-08-27 15:53 ` [PATCH v6 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-27 15:52 UTC (permalink / raw)
  To: kvm, linux-s390
  Cc: Alexander Gordeev, Christian Borntraeger, Claudio Imbrenda,
	David Hildenbrand, Eric Farman, Heiko Carstens, Janosch Frank,
	Nico Boehr, 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 3a839154fdfe..f8dff6846fb3 100644
--- a/arch/s390/kvm/s390/vsie.c
+++ b/arch/s390/kvm/s390/vsie.c
@@ -1134,6 +1134,7 @@ static int get_vsie_sca(struct kvm_vcpu *vcpu, struct kvm_s390_sie_block *scb_o,
 		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);
@@ -1156,6 +1157,7 @@ static int get_vsie_sca(struct kvm_vcpu *vcpu, struct kvm_s390_sie_block *scb_o,
 		}
 		unpin_sca(kvm, vsie_sca);
 		clear_vsie_sca(vsie_sca);
+		kvm->stat.vsie_shadow_sca_reuse++;
 	}
 
 	if (sie_uses_esca(scb_o))
@@ -2014,6 +2016,7 @@ static int 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) {
@@ -2031,6 +2034,7 @@ static int get_vsie_page(struct kvm_vcpu *vcpu, unsigned long addr,
 		vsie_page->scb_gpa = ULONG_MAX;
 
 		unpin_scb(kvm, vsie_page);
+		kvm->stat.vsie_shadow_scb_reuse++;
 	}
 
 	rc = init_vsie_page(vcpu, vsie_page, addr);
@@ -2060,6 +2064,7 @@ static int get_vsie_page_cpu_nr(struct kvm_vcpu *vcpu, struct vsie_sca *vsie_sca
 				u16 cpu_nr, struct vsie_page **vsie_page_out)
 {
 	struct vsie_page *vsie_page, *vsie_page_new = NULL;
+	bool vsie_page_is_new = true;
 	int rc;
 
 	vsie_page = vsie_sca->pages[cpu_nr];
@@ -2080,6 +2085,7 @@ static int get_vsie_page_cpu_nr(struct kvm_vcpu *vcpu, struct vsie_sca *vsie_sca
 		}
 	}
 	if (vsie_page != vsie_page_new) {
+		vsie_page_is_new = false;
 		if (vsie_page_new)
 			free_vsie_page(vsie_page_new);
 
@@ -2093,6 +2099,10 @@ static int get_vsie_page_cpu_nr(struct kvm_vcpu *vcpu, struct vsie_sca *vsie_sca
 			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 rc;

-- 
2.55.0


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

* [PATCH v6 20/21] KVM: s390: vsie: Create minimal scb shadows for not running g3 blocks
  2026-08-27 15:52 [PATCH v6 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (18 preceding siblings ...)
  2026-08-27 15:52 ` [PATCH v6 19/21] KVM: s390: vsie: Add VSIE shadow stat counters Christoph Schlameuss
@ 2026-08-27 15:53 ` Christoph Schlameuss
  2026-08-27 16:08   ` sashiko-bot
  2026-08-27 15:53 ` [PATCH v6 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-27 15:53 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 f8dff6846fb3..67b68f4302b7 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
@@ -2182,7 +2202,7 @@ static int _shadow_sca_cpu(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,
 	if (rc)
 		return rc;
 
-	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 */
@@ -2209,7 +2229,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 v6 21/21] KVM: s390: vsie: Enable use of VSIE SSCA
  2026-08-27 15:52 [PATCH v6 00/21] KVM: s390: vsie: Add VSIE SIGP ECALL and SRSI Interpretation Christoph Schlameuss
                   ` (19 preceding siblings ...)
  2026-08-27 15:53 ` [PATCH v6 20/21] KVM: s390: vsie: Create minimal scb shadows for not running g3 blocks Christoph Schlameuss
@ 2026-08-27 15:53 ` Christoph Schlameuss
  2026-08-27 16:17   ` sashiko-bot
  20 siblings, 1 reply; 43+ messages in thread
From: Christoph Schlameuss @ 2026-08-27 15:53 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 v6 06/21] KVM: s390: vsie: Move pin/unpin_scb methods
  2026-08-27 15:52 ` [PATCH v6 06/21] KVM: s390: vsie: Move pin/unpin_scb methods Christoph Schlameuss
@ 2026-08-27 15:57   ` sashiko-bot
  0 siblings, 0 replies; 43+ messages in thread
From: sashiko-bot @ 2026-08-27 15:57 UTC (permalink / raw)
  To: Christoph Schlameuss
  Cc: Vasily Gorbik, Alexander Gordeev, linux-s390, kvm, Heiko Carstens,
	Christian Borntraeger

> 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/20260827-vsie-sigpi-v6-0-8020bb53be52@linux.ibm.com?part=6


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

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


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

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

> 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/20260827-vsie-sigpi-v6-0-8020bb53be52@linux.ibm.com?part=3


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

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

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Unused parameter `kvm` in `alloc_vsie_page`
--

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

KVM: s390: vsie: Create helpers to alloc and free vsie_pages

This commit encapsulates the allocation and freeing of struct vsie_page
into dedicated helper functions to allow reuse from multiple code paths.

> diff --git a/arch/s390/kvm/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
> index afc2e56fcabfa..e7005bade7574 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)

[Severity: Low]
The parameter kvm is passed to alloc_vsie_page(), but it doesn't appear
to be used anywhere within the function body. The memory allocation only
uses constant GFP flags, and the structure initialization is hardcoded.

Is this kvm parameter necessary, or could it be removed to simplify the
function signature?

> +{
> +	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;
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260827-vsie-sigpi-v6-0-8020bb53be52@linux.ibm.com?part=8

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

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

> 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/20260827-vsie-sigpi-v6-0-8020bb53be52@linux.ibm.com?part=1


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

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

> 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/20260827-vsie-sigpi-v6-0-8020bb53be52@linux.ibm.com?part=7


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

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

> The underlying implementation of radix_tree is xarray since v4.20.
> Changing to the xarray API directly relaxes some of the locking
> obligations and provides more symmetry with the next vsie changes in
> the series.
> 
> Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>

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

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260827-vsie-sigpi-v6-0-8020bb53be52@linux.ibm.com?part=9


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

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

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

Pre-existing issues:
- [High] `unshadow_intercept()` clobbers the original SCB's `gbea` and `icptstatus` fields with uninitialized or stale data on early validity intercepts.
--

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

KVM: s390: vsie: Add helper unshadow_intercept()

This commit extracts an unshadow_intercept() helper from the existing SCB
unshadowing logic. It uses this helper in early error paths to copy only
intercept information back, avoiding clobbering the timer and guest-state
fields of the original SCB when the rest of it hasn't been shadowed yet.

> diff --git a/arch/s390/kvm/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
> --- a/arch/s390/kvm/s390/vsie.c
> +++ b/arch/s390/kvm/s390/vsie.c
[ ... ]
> +static inline void unshadow_intercept(struct kvm_s390_sie_block *scb_o,
> +				      struct kvm_s390_sie_block *scb_s)
>  {
>  	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;
> +}
[ ... ]
> @@ -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;
[ ... ]
> @@ -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 isn't a bug introduced by this patch, but won't this call clobber the
original SCB's gbea and icptstatus fields with uninitialized or stale data?

If shadow_scb() fails early, for example when prepare_cpuflags() detects
an invalid CPU flag set by L1, it triggers a validity intercept and jumps
to out_validity to call unshadow_intercept().

However, at this early point in shadow_scb(), scb_s->gbea and
scb_s->icptstatus have not yet been populated from scb_o. Consequently,
this writeback overwrites the nested guest's original SCB with stale data
from a previous execution (or zeros), destroying any updates L1 made to
these fields prior to triggering the validity intercept.

While this patch successfully stops the clobbering of timer and guest state
fields on early exits, does grouping gbea and icptstatus into this shared
helper inadvertently preserve the data corruption for these specific fields?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260827-vsie-sigpi-v6-0-8020bb53be52@linux.ibm.com?part=12

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

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

> 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/20260827-vsie-sigpi-v6-0-8020bb53be52@linux.ibm.com?part=5


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

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

> 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/20260827-vsie-sigpi-v6-0-8020bb53be52@linux.ibm.com?part=11


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

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

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The commit message claims to move the existing pin_guest_page() and unpin_guest_page() functions up in the file, but the patch diff does not include this move.
--

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

KVM: s390: vsie: Add helper to pin and unpin multiple guest pages

This commit introduces helpers to pin and unpin multiple consecutive guest-2
pages in guest-1 to handle vSIE configurations with an ssca. It iterates over
the pages and stores 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.

[Severity: Low]
Is this portion of the commit message accurate for this specific patch?

It appears the relocation of the existing {,un}pin_guest_page() functions 
was already performed in an earlier patch in the series. The diff for 
this patch only introduces the new plural helpers without moving any 
existing code.

Could the commit message be updated to avoid confusion and reflect that 
the code movement was already handled?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260827-vsie-sigpi-v6-0-8020bb53be52@linux.ibm.com?part=14

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

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

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The guest physical address (GPA) of the SCB is clobbered before unpinning, causing KVM to lose dirty page tracking and corrupt nested guest state during live migration.
--

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

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

This patch changes the original SIE control block (SCB) to be lazily unpinned
only on reuse for a different SCB. This bounds the number of pinned SCBs by
the number of vCPUs and reduces memory pressure while extending pin and unpin
methods to track status.

> diff --git a/arch/s390/kvm/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
> index c42e2df4c0abd..cdce4b3b25251 100644
> --- a/arch/s390/kvm/s390/vsie.c
> +++ b/arch/s390/kvm/s390/vsie.c
[ ... ]
> @@ -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 *kvm, 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(kvm, gpa, hpa);
> +		unpin_guest_page(kvm, vsie_page->scb_gpa, hpa);

[Severity: High]
Does this code rely on scb_gpa to correctly mark the page dirty?

When reusing a vsie_page for a different SCB in get_vsie_page(), scb_gpa is
set to ULONG_MAX before unpin_scb() is called. This causes unpin_guest_page()
to fail to find a valid memslot and silently drops the dirty page log. Can
this result in stale nested guest state being migrated during live migration?

>  	vsie_page->scb_o = NULL;
> +	clear_bit(VSIE_PAGE_SCB_PINNED, &vsie_page->flags);
>  }
[ ... ]
> @@ -1575,51 +1608,79 @@ 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: High]
Since unpin_scb() now relies on vsie_page->scb_gpa to mark the page dirty,
but we just set scb_gpa to ULONG_MAX immediately above, will the page actually
be marked dirty correctly?

>  	}
>  
> -	/* 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)) {
> -		put_vsie_page(vsie_page);
> -		mutex_unlock(&kvm->arch.vsie.mutex);
> -		return NULL;
> +	rc = init_vsie_page(vcpu, vsie_page, addr);
> +	if (!rc) {
> +		rc = xa_insert(&kvm->arch.vsie.addr_to_page, addr >> SCB_ALIGNMENT_SHIFT, vsie_page,
> +			       GFP_KERNEL_ACCOUNT);
> +		if (rc == -EBUSY)
> +			rc = -EAGAIN;
>  	}
> -	vsie_page->scb_gpa = addr;
> +
>  	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);
> +		return rc;

[Severity: High]
In this error path, the page is left pinned but its GPA is wiped. Doesn't
this mean the next time the page is reused or destroyed, it will suffer from
the same missing dirty page tracking failure?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260827-vsie-sigpi-v6-0-8020bb53be52@linux.ibm.com?part=13

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

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

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

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

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260827-vsie-sigpi-v6-0-8020bb53be52@linux.ibm.com?part=20


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

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

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

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

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260827-vsie-sigpi-v6-0-8020bb53be52@linux.ibm.com?part=10


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

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

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Permanent leak of a vsie_sca slot and pinned guest pages if xa_insert() fails.
--

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

KVM: s390: vsie: Add VSIE shadow stat counters

This commit adds new stat counters to VSIE shadowing to verify and monitor
functionality. It tracks the number of allocated and reused SIE control
block (SCB) and system control area (SCA) shadows.

> diff --git a/arch/s390/kvm/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
> index 3a839154fdfed..f8dff6846fb3c 100644
> --- a/arch/s390/kvm/s390/vsie.c
> +++ b/arch/s390/kvm/s390/vsie.c
[ ... ]
> @@ -1134,6 +1134,7 @@ static int get_vsie_sca(struct kvm_vcpu *vcpu, struct kvm_s390_sie_block *scb_o,
>  		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);

[Severity: High]
Does this code leak a vsie_sca slot and its pinned guest pages if
xa_insert() fails?

Looking at get_vsie_sca() later in this function, if xa_insert() fails
(e.g., due to -ENOMEM):

arch/s390/kvm/s390/vsie.c:get_vsie_sca() {
    ...
    rc = xa_insert(&kvm->arch.vsie.osca_to_sca,
                   vsie_sca->sca_gpa >> SCA_ALIGNMENT_SHIFT,
                   vsie_sca, GFP_KERNEL_ACCOUNT);
    if (rc == -EBUSY)
        rc = 1;

out:
    up_write(&kvm->arch.vsie.vsie_sca_lock);
    if (vsie_sca_new)
        free_vsie_sca(kvm, vsie_sca_new);
    if (vsie_sca)
        *vsie_sca_out = vsie_sca;
    return rc;
}

The error path falls through to the out label without calling unpin_sca()
or put_vsie_sca(). The vsie_sca struct is left with a ref_count of 1 but
is not added to the osca_to_sca xarray. Because its reference count is > 0,
get_vsie_sca_unused() will never reuse it.

Since xa_insert() allocates with GFP_KERNEL_ACCOUNT, could an unprivileged
guest hitting memory limits systematically exhaust the capped pool of
vsie_sca structures, permanently breaking nested virtualization for that VM?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260827-vsie-sigpi-v6-0-8020bb53be52@linux.ibm.com?part=19

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

* Re: [PATCH v6 02/21] KVM: s390: vsie: Move SCAO validation into a function
  2026-08-27 15:52 ` [PATCH v6 02/21] KVM: s390: vsie: Move SCAO validation into a function Christoph Schlameuss
@ 2026-08-27 16:11   ` sashiko-bot
  0 siblings, 0 replies; 43+ messages in thread
From: sashiko-bot @ 2026-08-27 16:11 UTC (permalink / raw)
  To: Christoph Schlameuss
  Cc: Vasily Gorbik, Heiko Carstens, Alexander Gordeev, linux-s390,
	Christian Borntraeger, 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/20260827-vsie-sigpi-v6-0-8020bb53be52@linux.ibm.com?part=2


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

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

> 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/20260827-vsie-sigpi-v6-0-8020bb53be52@linux.ibm.com?part=15


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

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

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] TOCTOU race dynamically flipping the `VSIE_SCA_ESCA` flag allows a malicious guest to trigger a host WARN_ON_ONCE DoS.
- [High] `_shadow_sca` leaves stale shadow SCA entries for removed CPUs because it fails to fully clear the `ssca.cpu` array when the Maximum CPU Number (`mcn`) changes.
--

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

KVM: s390: vsie: Allow guest-3 cpu add and remove with ssca

This commit allows dynamically adding and removing processors for a running
guest-3 configuration. It accomplishes this by copying the Maximum CPU Number
(mcn) and fully re-initializing the shadowed System Control Area (ssca)
whenever a change in the mcn is detected.

> diff --git a/arch/s390/kvm/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
> index 16273cf5cbff5..bfd5df1f8b9ca 100644
> --- a/arch/s390/kvm/s390/vsie.c
> +++ b/arch/s390/kvm/s390/vsie.c
[ ... ]
> @@ -2142,17 +2179,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);

[Severity: High]
If a malicious guest concurrently flips the ECB2_ESCA bit in its SCB after
vsie_sca is pinned, _shadow_sca() will read this bit and dynamically toggle
the VSIE_SCA_ESCA flag on the shared vsie_sca structure.

Could dynamically flipping this flag based on untrusted guest memory introduce
a time-of-check to time-of-use (TOCTOU) race?

This can cause subsequent ESCA-based offset calculations to exceed the
initially pinned BSCA pages, leading to a guest-triggerable out-of-bounds
WARN_ON assertion when the corrupted flag is later used:

get_sca_entry_addr() {
    ...
	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;
    ...
}

> +		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]
If the guest removes a CPU, its bit in the Maximum CPU Number (mcn) mask
transitions from 1 to 0.

By using for_each_set_bit_inv(), we skip the removed CPU and fail to clear
its corresponding ssda and ossea entries in the ssca.cpu array.

Does iterating only over the currently set bits in mcn leave stale shadow
SCA entries for removed CPUs?

Could this leave hardware-visible pointers intact, potentially causing the
physical hardware to erroneously route intercepted hardware-assisted SIGP
operations to a stale shadow control block?

>  		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/20260827-vsie-sigpi-v6-0-8020bb53be52@linux.ibm.com?part=17

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

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

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

Pre-existing issues:
- [Critical] Missing return value assignment in get_vsie_sca() leads to dereferencing ERR_PTR and a host kernel panic.
- [High] Reference leak of vsie_sca upon xa_insert failure allows a guest to permanently exhaust the SCA pool.
--

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

KVM: s390: vsie: Add VSIE max shadow configuration

This commit introduces two new module parameters, vsie_shadow_scb_max
and vsie_shadow_sca_max, to configure the maximum number of VSIE shadow
control blocks and system control areas kept by guest-1. It modifies
get_vsie_sca() and get_vsie_page() to respect these new limits.

> diff --git a/arch/s390/kvm/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
> index bfd5df1f8b9ca..3a839154fdfed 100644
> --- a/arch/s390/kvm/s390/vsie.c
> +++ b/arch/s390/kvm/s390/vsie.c
[ ... ]
> @@ -1086,7 +1096,8 @@ static int get_vsie_sca(struct kvm_vcpu *vcpu, struct kvm_s390_sie_block *scb_o,
>  	 * 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();
[ ... ]
> 	} 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;

[Severity: Critical]
This is a pre-existing issue, but missing a return value assignment here
can lead to a host kernel panic.

If get_vsie_sca_unused() exhausts the pool and returns ERR_PTR(-EAGAIN),
the IS_ERR check triggers a goto to the out label without setting rc.
As a result, rc remains 0 (success) while the error pointer is assigned
to *vsie_sca_out:

> out:
> 	up_write(&kvm->arch.vsie.vsie_sca_lock);
> 	if (vsie_sca_new)
> 		free_vsie_sca(kvm, vsie_sca_new);
> 	if (vsie_sca)
> 		*vsie_sca_out = vsie_sca;
> 	return rc;

Because the caller kvm_s390_handle_vsie() receives a success code, it
proceeds to call get_vsie_page_cpu_nr(), which dereferences the error
pointer.

Can a malicious guest deterministically trigger this crash by forcing memory
allocation failures on the xa_insert path to exhaust the pool?

[ ... ]
> 	rc = xa_insert(&kvm->arch.vsie.osca_to_sca, vsie_sca->sca_gpa >> SCA_ALIGNMENT_SHIFT,
> 		       vsie_sca, GFP_KERNEL_ACCOUNT);
> 	if (rc == -EBUSY)
> 		rc = 1;
> 
> out:

[Severity: High]
This isn't a bug introduced by this patch, but does this error path
permanently leak a vsie_sca reference?

If xa_insert() fails (e.g., returning -ENOMEM), it sets rc and jumps to out.
However, it skips calling put_vsie_sca(vsie_sca) to clean up the reference
count initialized to 1.

Because the caller receives an error code and also skips cleanup, the
ref_count remains stuck at 1 and the vsie_sca can never be reclaimed by
get_vsie_sca_unused().

Could a guest apply memory pressure to repeatedly trigger this, exhaust the
SCA pool, and pave the way for the ERR_PTR dereference bug above?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260827-vsie-sigpi-v6-0-8020bb53be52@linux.ibm.com?part=18

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

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

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Critical] Unhandled ERR_PTR from get_vsie_sca_unused() leads to host kernel panic.
- [Critical] XArray collision due to SCA alignment shift causes shadow SCA state mixups.
- [High] KVM permanently caches the SCA address, ignoring L1 guest updates when SSCA is not used.
- [Medium] Error path in `get_vsie_sca` leaks memory pins and the `vsie_sca` refcount.
--

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

KVM: s390: vsie: Shadow VSIE SCA in guest-1

Restructure kvm_s390_handle_vsie() to create a guest-1 shadow of the SCA
if guest-2 attempts to enter SIE with an SCA. If the SCA is used the
vsie_pages are stored in a new vsie_sca struct instead of the arch vsie
struct.

> diff --git a/arch/s390/kvm/s390/vsie.c b/arch/s390/kvm/s390/vsie.c
> index 69334d4a32311..16273cf5cbff5 100644
> --- a/arch/s390/kvm/s390/vsie.c
> +++ b/arch/s390/kvm/s390/vsie.c

[ ... ]

> @@ -1031,6 +1031,16 @@ 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;
>  }

[Severity: Critical]
Does this code allow an XArray collision if multiple SCAs share the same
64-byte block?

If a guest allocates two different SCAs (which is valid since BSCA only
requires 16-byte alignment), they will result in the same XArray index due
to SCA_ALIGNMENT_SHIFT. The returned shadow SCA is not verified to ensure
vsie_sca->sca_gpa == sca_o_gpa, which could cause L2 VCPUs to operate on the
wrong SCA context.

[ ... ]

> @@ -1124,6 +1124,10 @@ static int get_vsie_sca(struct kvm_vcpu *vcpu, struct kvm_s390_sie_block *scb_o,
>  	} 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;

[Severity: Critical]
If get_vsie_sca_unused() returns ERR_PTR(-EAGAIN) when the system exhausts
the maximum number of concurrent active shadow SCAs, doesn't this jump to the
out label without setting an error code in rc?

This would leak the ERR_PTR to the caller because rc remains 0.

[ ... ]

> @@ -1164,13 +1164,13 @@ static int get_vsie_sca(struct kvm_vcpu *vcpu, struct kvm_s390_sie_block *scb_o,
>  	rc = xa_insert(&kvm->arch.vsie.osca_to_sca, vsie_sca->sca_gpa >> SCA_ALIGNMENT_SHIFT,
>  		       vsie_sca, GFP_KERNEL_ACCOUNT);
>  	if (rc == -EBUSY)
>  		rc = 1;
>  
>  out:
>  	up_write(&kvm->arch.vsie.vsie_sca_lock);
>  	if (vsie_sca_new)
>  		free_vsie_sca(kvm, vsie_sca_new);
>  	if (vsie_sca)
>  		*vsie_sca_out = vsie_sca;
>  	return rc;
>  }

[Severity: Medium]
If xa_insert() fails (e.g., returning -ENOMEM), this error path returns the
error code but still populates *vsie_sca_out with the valid vsie_sca pointer.

Because the caller kvm_s390_handle_vsie() jumps to out_unpin on error and
skips the out_put_sca label, won't this leak the memory pinned by pin_sca()
and the vsie_sca refcount?

[ ... ]

> @@ -1245,13 +1245,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);
> +		}
>  	}

[ ... ]

> @@ -1300,8 +1300,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;
> @@ -1310,7 +1310,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);
>  	}

[Severity: High]
Does removing vsie_page->sca_gpa = 0; break the guest's ability to dynamically
update the SCA?

Because pin_blocks() now uses the one-time cached vsie_page->sca_gpa instead
of evaluating the active L1 guest SCB via read_scao(), KVM might silently
overwrite the scaol field with the old cached physical address when the L1
guest updates the SCB.

[ ... ]

> @@ -2043,15 +2043,18 @@ static int 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_out)
>  {
>  	struct vsie_page *vsie_page, *vsie_page_new = NULL;
>  	int rc;
>  
>  	vsie_page = vsie_sca->pages[cpu_nr];

[Severity: Critical]
If get_vsie_sca() successfully returned an ERR_PTR (due to the missing rc
update discussed earlier), won't this dereference the unhandled ERR_PTR
and cause a host kernel panic?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260827-vsie-sigpi-v6-0-8020bb53be52@linux.ibm.com?part=16

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

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

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The VSIE SSCA hardware interpretation (SIGPIF) feature is implicitly enabled based on host capabilities, bypassing guest capability negotiation and breaking live migration.
- [High] A TOCTOU race in `get_vsie_sca_unused` combined with an unhandled error path in `get_vsie_sca` causes KVM to return and dereference an ERR_PTR, leading to a host kernel panic.
--

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

KVM: s390: vsie: Enable use of VSIE SSCA

This commit enables code paths actually configuring and using VSIE SIGPI and
SRSI. This feature improves guest-3 performance by allowing hardware to handle
these instructions without exiting VSIE.

> 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]
By enabling the SSCA feature globally based on the host capability here,
does this bypass guest capability negotiation?

In arch/s390/kvm/s390/vsie.c, use_ssca() checks the global kvm->arch.use_ssca
flag but appears to miss checking if the L1 guest actually negotiated the
KVM_S390_VM_CPU_FEAT_SIGPIF feature via test_kvm_cpu_feat().

Additionally, in shadow_scb_minimal():

    scb_s->eca |= scb_o->eca & ECA_SIGPI;
    scb_s->ecb |= scb_o->ecb & ECB_SRSI;

The requested features are copied into the shadow SCB without masking them
against the guest's negotiated capabilities. Could this allow a nested L1
guest to utilize hardware SSCA interpretation even if the VMM explicitly
disabled it, leading to silent live migration failures on destination hosts
without SSCA support?


[Severity: High]
Does enabling this feature expose a potential race condition and error pointer
dereference in the VSIE SCA handling?

In arch/s390/kvm/s390/vsie.c, if get_vsie_sca_unused() runs concurrently with
put_vsie_sca() (which decrements the refcount without holding vsie_sca_lock)
and all SCAs are temporarily busy, it can return ERR_PTR(-EAGAIN).

When get_vsie_sca() receives this error:

    if (IS_ERR(vsie_sca))
        goto out;

It jumps to the out label without updating the local rc variable. Since rc
remains 0 from a previous successful call to validate_scao(), get_vsie_sca()
incorrectly returns success while passing out the ERR_PTR.

This leads to kvm_s390_handle_vsie() seeing a success return:

    rc = get_vsie_sca(vcpu, scb_o, &vsie_sca);
    if (rc)
        goto out_unpin;
    rc = get_vsie_page_cpu_nr(vcpu, vsie_sca, ...);

And passing the invalid pointer to get_vsie_page_cpu_nr(), which immediately
dereferences it:

    vsie_page = vsie_sca->pages[cpu_nr];

Could this unhandled error path allow a nested guest workload to trigger a
host kernel panic?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260827-vsie-sigpi-v6-0-8020bb53be52@linux.ibm.com?part=21

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

end of thread, other threads:[~2026-08-27 16:18 UTC | newest]

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

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