Skip to content

Commit f0a1a06

Browse files
Claudio Imbrendaborntraeger
authored andcommitted
KVM: s390: pv: avoid stalls when making pages secure
Improve make_secure_pte to avoid stalls when the system is heavily overcommitted. This was especially problematic in kvm_s390_pv_unpack, because of the loop over all pages that needed unpacking. Due to the locks being held, it was not possible to simply replace uv_call with uv_call_sched. A more complex approach was needed, in which uv_call is replaced with __uv_call, which does not loop. When the UVC needs to be executed again, -EAGAIN is returned, and the caller (or its caller) will try again. When -EAGAIN is returned, the path is the same as when the page is in writeback (and the writeback check is also performed, which is harmless). Fixes: 214d9bb ("s390/mm: provide memory management functions for protected KVM guests") Signed-off-by: Claudio Imbrenda <[email protected]> Reviewed-by: Janosch Frank <[email protected]> Reviewed-by: Christian Borntraeger <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Christian Borntraeger <[email protected]>
1 parent 1e2aa46 commit f0a1a06

File tree

2 files changed

+28
-6
lines changed

2 files changed

+28
-6
lines changed

arch/s390/kernel/uv.c

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ static int make_secure_pte(pte_t *ptep, unsigned long addr,
165165
{
166166
pte_t entry = READ_ONCE(*ptep);
167167
struct page *page;
168-
int expected, rc = 0;
168+
int expected, cc = 0;
169169

170170
if (!pte_present(entry))
171171
return -ENXIO;
@@ -181,12 +181,25 @@ static int make_secure_pte(pte_t *ptep, unsigned long addr,
181181
if (!page_ref_freeze(page, expected))
182182
return -EBUSY;
183183
set_bit(PG_arch_1, &page->flags);
184-
rc = uv_call(0, (u64)uvcb);
184+
/*
185+
* If the UVC does not succeed or fail immediately, we don't want to
186+
* loop for long, or we might get stall notifications.
187+
* On the other hand, this is a complex scenario and we are holding a lot of
188+
* locks, so we can't easily sleep and reschedule. We try only once,
189+
* and if the UVC returned busy or partial completion, we return
190+
* -EAGAIN and we let the callers deal with it.
191+
*/
192+
cc = __uv_call(0, (u64)uvcb);
185193
page_ref_unfreeze(page, expected);
186-
/* Return -ENXIO if the page was not mapped, -EINVAL otherwise */
187-
if (rc)
188-
rc = uvcb->rc == 0x10a ? -ENXIO : -EINVAL;
189-
return rc;
194+
/*
195+
* Return -ENXIO if the page was not mapped, -EINVAL for other errors.
196+
* If busy or partially completed, return -EAGAIN.
197+
*/
198+
if (cc == UVC_CC_OK)
199+
return 0;
200+
else if (cc == UVC_CC_BUSY || cc == UVC_CC_PARTIAL)
201+
return -EAGAIN;
202+
return uvcb->rc == 0x10a ? -ENXIO : -EINVAL;
190203
}
191204

192205
/*
@@ -239,6 +252,10 @@ int gmap_make_secure(struct gmap *gmap, unsigned long gaddr, void *uvcb)
239252
mmap_read_unlock(gmap->mm);
240253

241254
if (rc == -EAGAIN) {
255+
/*
256+
* If we are here because the UVC returned busy or partial
257+
* completion, this is just a useless check, but it is safe.
258+
*/
242259
wait_on_page_writeback(page);
243260
} else if (rc == -EBUSY) {
244261
/*

arch/s390/kvm/intercept.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,11 @@ static int handle_pv_uvc(struct kvm_vcpu *vcpu)
518518
*/
519519
if (rc == -EINVAL)
520520
return 0;
521+
/*
522+
* If we got -EAGAIN here, we simply return it. It will eventually
523+
* get propagated all the way to userspace, which should then try
524+
* again.
525+
*/
521526
return rc;
522527
}
523528

0 commit comments

Comments
 (0)