All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Claudio Imbrenda" <imbrenda@linux.ibm.com>, kvm@vger.kernel.org
Cc: linux-s390@vger.kernel.org, "Heiko Carstens" <hca@linux.ibm.com>,
	"Vasily Gorbik" <gor@linux.ibm.com>,
	"Alexander Gordeev" <agordeev@linux.ibm.com>,
	"Christian Borntraeger" <borntraeger@linux.ibm.com>
Subject: Re: [PATCH v1 08/10] KVM: s390: Improve unmapping of large ranges
Date: Thu, 22 Oct 2026 16:53:37 +0000	[thread overview]
Message-ID: <sashiko-outbox-181832@kernel.org> (raw)
In-Reply-To: <20261022155531.204593-9-imbrenda@linux.ibm.com>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] KVM: s390: hole PTE clobber and stale pgste.zero in _gmap_unmap_pmd_fast()
- [High] KVM: s390: missing compiler barriers around PTE loop in _gmap_unmap_pmd_fast()
--

commit 4a297a39db42bf18a8ae260ff7d2a55c3aa686ed
Author: Claudio Imbrenda <imbrenda@linux.ibm.com>

KVM: s390: Improve unmapping of large ranges

This commit adds a fast path for unmapping guest address ranges larger than
one segment by detaching the pmd via idte_crste(), clearing the pte entries
in the detached page table while saving storage keys, and reattaching the
pmd.

> diff --git a/arch/s390/kvm/gmap/gmap.c b/arch/s390/kvm/gmap/gmap.c
> index c10681f882c2d..6700a87a420a8 100644
> --- a/arch/s390/kvm/gmap/gmap.c
> +++ b/arch/s390/kvm/gmap/gmap.c

[ ... ]

> @@ -465,6 +456,103 @@ bool gmap_unmap_gfn_range(struct gmap *gmap, struct kvm_memory_slot *slot, gfn_t
>  	return false;
>  }
>  
> +static long _gmap_unmap_pud_fast(union crste *crstep, gfn_t gfn, gfn_t next, struct dat_walk *w)
> +{

[ ... ]

> +static long _gmap_unmap_pmd_fast(union crste *crstep, gfn_t gfn, gfn_t next, struct dat_walk *w)
> +{
> +	union crste old = *crstep;
> +	struct page_table *pt;
> +	gfn_t first, last;
> +	unsigned int i;
> +
> +	if (old.h.fc) {
> +		if (old.s.fc1.vsie_notif)
> +			gmap_handle_vsie_unshadow_event(w->priv, gfn);
> +
> +		idte_crste(crstep, gfn, w->datt.asce);
> +		WRITE_ONCE(*crstep, _CRSTE_EMPTY(old.h.tt));
> +
> +		return 0;
> +	}
> +
> +	if (old.h.i)
> +		return 0;
> +
> +	pt = dereference_pmd(old.pmd);
> +	first = max(gfn, w->start) & (PTRS_PER_PTE - 1);
> +	last = (min(next, w->end) - 1) & (PTRS_PER_PTE - 1);
> +
> +	for (i = first; i <= last; i++)
> +		if (pte_needs_unshadow(pt->ptes[i], _PTE_EMPTY, pt->pgstes[i]))
> +			gmap_handle_vsie_unshadow_event(w->priv, gfn + i);
> +
> +	/* Detach the page table */
> +	idte_crste(crstep, gfn, w->datt.asce);
> +
> +	for (i = first; i <= last; i++) {
> +		if (!pt->ptes[i].h.i && w->datt.uses_skeys)
> +			pt->pgstes[i] = dat_save_storage_key_into_pgste(pt->ptes[i], pt->pgstes[i]);
> +		pt->ptes[i] = _PTE_EMPTY;
> +		pt->pgstes[i].vsie_notif = 0;
> +		pt->pgstes[i].vsie_gmem = 0;
> +	}

[Severity: High]
Can this loop in _gmap_unmap_pmd_fast() clobber hole ptes and leave stale
pgste.zero bits set?

gmap_unmap_gfn_range_fast() passes DAT_WALK_IGN_HOLES to
_dat_walk_gfn_range(). In standard pte walks, dat_pte_walk_range() skips
entries where pte_hole() is true when DAT_WALK_IGN_HOLES is set without
DAT_WALK_ANY:

arch/s390/kvm/gmap/dat.c:dat_pte_walk_range() {
	for ( ; gfn < end; idx++, gfn++) {
		if (pte_hole(READ_ONCE(table->ptes[idx]))) {
			if (!(w->flags & DAT_WALK_IGN_HOLES))
				return -EFAULT;
			if (!(w->flags & DAT_WALK_ANY))
				continue;
		}
}

When a non-segment-aligned memslot is deleted or moved via
kvm_set_memslot() -> kvm_invalidate_memslot() ->
kvm_prepare_memory_region() -> s390_kvm_mmu_prepare_memory_region(),
dat_delete_slot() -> _dat_slot_pte() marks the ptes in partially covered
boundary segments as _PTE_TOK(_DAT_TOKEN_PIC, PGM_ADDRESSING) and drops
kvm->mmu_lock while invalid_slot is still present in the active hva tree:

arch/s390/kvm/gmap/dat.c:_dat_slot_pte() {
	new_pte = walk->priv ? _PTE_EMPTY : _PTE_TOK(_DAT_TOKEN_PIC, PGM_ADDRESSING);
}

If an mmu notifier invalidation larger than _PAGES_PER_SEGMENT runs on that
hva range before invalid_slot is removed, kvm_handle_hva_range() ->
kvm_unmap_gfn_range() -> gmap_unmap_gfn_range_fast() ->
_gmap_unmap_pmd_fast() iterates over [first, last] and unconditionally
writes _PTE_EMPTY, overwriting those PGM_ADDRESSING hole markers. Would
subsequent storage key or cmma operations (such as dat_get_storage_key(),
dat_set_storage_key(), dat_reset_reference_bit(), or dat_perform_essa()) on
those unmapped gfns then succeed instead of returning PGM_ADDRESSING?

Also, the slow path (_gmap_unmap_pte() -> gmap_ptep_xchg() ->
_gmap_ptep_xchg()) clears pgste.zero:

arch/s390/kvm/gmap/gmap.h:_gmap_ptep_xchg() {
	pgste.zero = 0;
	return __dat_ptep_xchg(ptep, pgste, newpte, gfn, gmap->datt);
}

Since _gmap_unmap_pmd_fast() only clears vsie_notif and vsie_gmem, could
stale pgste.zero bits remain set and be observed by ESSA_GET_STATE or
ESSA_SET_POT_VOLATILE?

> +
> +	/* Reattach the modified page table */
> +	WRITE_ONCE(*crstep, old);

[Severity: High]
What prevents the compiler from reordering the pt->ptes[i] and
pt->pgstes[i] accesses outside the detached pmd window in
_gmap_unmap_pmd_fast()?

In arch/s390/kvm/gmap/dat.h, idte_crste() only clobbers *crstep and "cc",
without a "memory" clobber:

arch/s390/kvm/gmap/dat.h:idte_crste() {
	if (!machine_has_tlb_guest()) {
		/* flush without guest asce */
		asm volatile("idte	%[table_origin],0,%[gaddr]"
			: "+m" (*crstep)
			: [table_origin] "a" (table_origin), [gaddr] "a" (gaddr)
			: "cc");
	} else {
		/* flush with guest asce */
		asm volatile("idte %[table_origin],%[asce],%[gaddr_opt]"
			: "+m" (*crstep)
			: [table_origin] "a" (table_origin),
			  [gaddr_opt] "a" (gaddr | IDTE_GUEST_ASCE),
			  [asce] "a" (asce.val)
			: "cc");
	}
}

Neither page_get_storage_key() nor WRITE_ONCE(*crstep, old) includes a
compiler memory barrier either (unlike pgste_set_unlock() in
arch/s390/kvm/gmap/dat.h, which places barrier() before WRITE_ONCE()).

Because pt->ptes[i] and pt->pgstes[i] are plain non-volatile accesses to
types distinct from *crstep (union crste), could the compiler reorder the
pte/pgste updates before idte_crste() or after WRITE_ONCE(*crstep, old),
allowing running vcpus to retain stale tlb entries to unmapped pages or
update hardware storage keys after dat_save_storage_key_into_pgste() has
read them?

> +
> +	return 0;
> +}
> +
> +static bool gmap_unmap_gfn_range_fast(struct gmap *gmap, gfn_t start, gfn_t end)
> +{
> +	const struct dat_walk_ops ops = {
> +		.pud_entry = _gmap_unmap_pud_fast,
> +		.pmd_entry = _gmap_unmap_pmd_fast,
> +	};
> +	bool failed;
> +
> +	lockdep_assert_held_write(&gmap->kvm->mmu_lock);
> +
> +	gmap_unmap_prefix(gmap, ALIGN_DOWN(start, _PAGE_ENTRIES), ALIGN(end, _PAGE_ENTRIES));
> +
> +	failed = _dat_walk_gfn_range(start, end, gmap->datt, &ops, DAT_WALK_IGN_HOLES, gmap);
> +	KVM_BUG_ON(failed, gmap->kvm);
> +	return false;
> +}

[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261022155531.204593-1-imbrenda@linux.ibm.com?part=8

  reply	other threads:[~2026-10-22 16:53 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-22 15:55 [PATCH v1 00/10] KVM: s390: Misc bugfixes and improvements for 7.4 Claudio Imbrenda
2026-10-22 15:55 ` [PATCH v1 01/10] KVM: s390: Move some skey functions to headers Claudio Imbrenda
2026-10-22 15:58   ` sashiko-bot
2026-10-22 15:55 ` [PATCH v1 02/10] KVM: s390: Refactor and improve DAT tables handling Claudio Imbrenda
2026-10-22 16:05   ` sashiko-bot
2026-10-22 15:55 ` [PATCH v1 03/10] KVM: s390: Move prefix marker from PGSTEs to PTEs Claudio Imbrenda
2026-10-22 16:06   ` sashiko-bot
2026-10-22 15:55 ` [PATCH v1 04/10] KVM: s390: Automatically enable storage keys for UCONTROL VMs Claudio Imbrenda
2026-10-22 16:13   ` sashiko-bot
2026-10-22 15:55 ` [PATCH v1 05/10] KVM: s390: Replace ASCE usage with a struct with more flags Claudio Imbrenda
2026-10-22 16:09   ` sashiko-bot
2026-10-22 15:55 ` [PATCH v1 06/10] KVM: s390: Rework some helper functions Claudio Imbrenda
2026-10-22 16:00   ` sashiko-bot
2026-10-22 15:55 ` [PATCH v1 07/10] KVM: s390: Fix UCONTROL inconsistencies Claudio Imbrenda
2026-10-22 17:00   ` sashiko-bot
2026-10-22 15:55 ` [PATCH v1 08/10] KVM: s390: Improve unmapping of large ranges Claudio Imbrenda
2026-10-22 16:53   ` sashiko-bot [this message]
2026-10-22 15:55 ` [PATCH v1 09/10] KVM: s390: vsie: Fix pinning using the wrong flag Claudio Imbrenda
2026-10-22 16:02   ` sashiko-bot
2026-10-22 15:55 ` [PATCH v1 10/10] KVM: s390: Enable 1M pages by default Claudio Imbrenda
2026-10-22 16:11   ` sashiko-bot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-181832@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=agordeev@linux.ibm.com \
    --cc=borntraeger@linux.ibm.com \
    --cc=gor@linux.ibm.com \
    --cc=hca@linux.ibm.com \
    --cc=imbrenda@linux.ibm.com \
    --cc=kvm@vger.kernel.org \
    --cc=linux-s390@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.