Skip to content

Commit 721f5b0

Browse files
Paul Durrantsean-jc
authored andcommitted
KVM: pfncache: allow a cache to be activated with a fixed (userspace) HVA
Some pfncache pages may actually be overlays on guest memory that have a fixed HVA within the VMM. It's pointless to invalidate such cached mappings if the overlay is moved so allow a cache to be activated directly with the HVA to cater for such cases. A subsequent patch will make use of this facility. Signed-off-by: Paul Durrant <pdurrant@amazon.com> Reviewed-by: David Woodhouse <dwmw@amazon.co.uk> Link: https://lore.kernel.org/r/20240215152916.1158-10-paul@xen.org Signed-off-by: Sean Christopherson <seanjc@google.com>
1 parent 9e7325a commit 721f5b0

File tree

2 files changed

+101
-28
lines changed

2 files changed

+101
-28
lines changed

include/linux/kvm_host.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,11 @@ static inline bool kvm_is_error_hva(unsigned long addr)
148148

149149
#endif
150150

151+
static inline bool kvm_is_error_gpa(gpa_t gpa)
152+
{
153+
return gpa == INVALID_GPA;
154+
}
155+
151156
#define KVM_ERR_PTR_BAD_PAGE (ERR_PTR(-ENOENT))
152157

153158
static inline bool is_error_page(struct page *page)
@@ -1344,6 +1349,22 @@ void kvm_gpc_init(struct gfn_to_pfn_cache *gpc, struct kvm *kvm);
13441349
*/
13451350
int kvm_gpc_activate(struct gfn_to_pfn_cache *gpc, gpa_t gpa, unsigned long len);
13461351

1352+
/**
1353+
* kvm_gpc_activate_hva - prepare a cached kernel mapping and HPA for a given HVA.
1354+
*
1355+
* @gpc: struct gfn_to_pfn_cache object.
1356+
* @hva: userspace virtual address to map.
1357+
* @len: sanity check; the range being access must fit a single page.
1358+
*
1359+
* @return: 0 for success.
1360+
* -EINVAL for a mapping which would cross a page boundary.
1361+
* -EFAULT for an untranslatable guest physical address.
1362+
*
1363+
* The semantics of this function are the same as those of kvm_gpc_activate(). It
1364+
* merely bypasses a layer of address translation.
1365+
*/
1366+
int kvm_gpc_activate_hva(struct gfn_to_pfn_cache *gpc, unsigned long hva, unsigned long len);
1367+
13471368
/**
13481369
* kvm_gpc_check - check validity of a gfn_to_pfn_cache.
13491370
*
@@ -1390,6 +1411,16 @@ int kvm_gpc_refresh(struct gfn_to_pfn_cache *gpc, unsigned long len);
13901411
*/
13911412
void kvm_gpc_deactivate(struct gfn_to_pfn_cache *gpc);
13921413

1414+
static inline bool kvm_gpc_is_gpa_active(struct gfn_to_pfn_cache *gpc)
1415+
{
1416+
return gpc->active && !kvm_is_error_gpa(gpc->gpa);
1417+
}
1418+
1419+
static inline bool kvm_gpc_is_hva_active(struct gfn_to_pfn_cache *gpc)
1420+
{
1421+
return gpc->active && kvm_is_error_gpa(gpc->gpa);
1422+
}
1423+
13931424
void kvm_sigset_activate(struct kvm_vcpu *vcpu);
13941425
void kvm_sigset_deactivate(struct kvm_vcpu *vcpu);
13951426

virt/kvm/pfncache.c

Lines changed: 70 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,14 @@ bool kvm_gpc_check(struct gfn_to_pfn_cache *gpc, unsigned long len)
4848
if (!gpc->active)
4949
return false;
5050

51-
if (gpc->generation != slots->generation || kvm_is_error_hva(gpc->uhva))
51+
/*
52+
* If the page was cached from a memslot, make sure the memslots have
53+
* not been re-configured.
54+
*/
55+
if (!kvm_is_error_gpa(gpc->gpa) && gpc->generation != slots->generation)
56+
return false;
57+
58+
if (kvm_is_error_hva(gpc->uhva))
5259
return false;
5360

5461
if (offset_in_page(gpc->uhva) + len > PAGE_SIZE)
@@ -209,22 +216,27 @@ static kvm_pfn_t hva_to_pfn_retry(struct gfn_to_pfn_cache *gpc)
209216
return -EFAULT;
210217
}
211218

212-
static int __kvm_gpc_refresh(struct gfn_to_pfn_cache *gpc, gpa_t gpa,
219+
static int __kvm_gpc_refresh(struct gfn_to_pfn_cache *gpc, gpa_t gpa, unsigned long uhva,
213220
unsigned long len)
214221
{
215-
struct kvm_memslots *slots = kvm_memslots(gpc->kvm);
216-
unsigned long page_offset = offset_in_page(gpa);
222+
unsigned long page_offset;
217223
bool unmap_old = false;
218224
unsigned long old_uhva;
219225
kvm_pfn_t old_pfn;
220226
bool hva_change = false;
221227
void *old_khva;
222228
int ret;
223229

230+
/* Either gpa or uhva must be valid, but not both */
231+
if (WARN_ON_ONCE(kvm_is_error_gpa(gpa) == kvm_is_error_hva(uhva)))
232+
return -EINVAL;
233+
224234
/*
225-
* If must fit within a single page. The 'len' argument is
226-
* only to enforce that.
235+
* The cached acces must fit within a single page. The 'len' argument
236+
* exists only to enforce that.
227237
*/
238+
page_offset = kvm_is_error_gpa(gpa) ? offset_in_page(uhva) :
239+
offset_in_page(gpa);
228240
if (page_offset + len > PAGE_SIZE)
229241
return -EINVAL;
230242

@@ -246,29 +258,39 @@ static int __kvm_gpc_refresh(struct gfn_to_pfn_cache *gpc, gpa_t gpa,
246258
old_khva = (void *)PAGE_ALIGN_DOWN((uintptr_t)gpc->khva);
247259
old_uhva = PAGE_ALIGN_DOWN(gpc->uhva);
248260

249-
/* Refresh the userspace HVA if necessary */
250-
if (gpc->gpa != gpa || gpc->generation != slots->generation ||
251-
kvm_is_error_hva(gpc->uhva)) {
252-
gfn_t gfn = gpa_to_gfn(gpa);
253-
254-
gpc->gpa = gpa;
255-
gpc->generation = slots->generation;
256-
gpc->memslot = __gfn_to_memslot(slots, gfn);
257-
gpc->uhva = gfn_to_hva_memslot(gpc->memslot, gfn);
261+
if (kvm_is_error_gpa(gpa)) {
262+
gpc->gpa = INVALID_GPA;
263+
gpc->memslot = NULL;
264+
gpc->uhva = PAGE_ALIGN_DOWN(uhva);
258265

259-
if (kvm_is_error_hva(gpc->uhva)) {
260-
ret = -EFAULT;
261-
goto out;
262-
}
263-
264-
/*
265-
* Even if the GPA and/or the memslot generation changed, the
266-
* HVA may still be the same.
267-
*/
268266
if (gpc->uhva != old_uhva)
269267
hva_change = true;
270268
} else {
271-
gpc->uhva = old_uhva;
269+
struct kvm_memslots *slots = kvm_memslots(gpc->kvm);
270+
271+
if (gpc->gpa != gpa || gpc->generation != slots->generation ||
272+
kvm_is_error_hva(gpc->uhva)) {
273+
gfn_t gfn = gpa_to_gfn(gpa);
274+
275+
gpc->gpa = gpa;
276+
gpc->generation = slots->generation;
277+
gpc->memslot = __gfn_to_memslot(slots, gfn);
278+
gpc->uhva = gfn_to_hva_memslot(gpc->memslot, gfn);
279+
280+
if (kvm_is_error_hva(gpc->uhva)) {
281+
ret = -EFAULT;
282+
goto out;
283+
}
284+
285+
/*
286+
* Even if the GPA and/or the memslot generation changed, the
287+
* HVA may still be the same.
288+
*/
289+
if (gpc->uhva != old_uhva)
290+
hva_change = true;
291+
} else {
292+
gpc->uhva = old_uhva;
293+
}
272294
}
273295

274296
/* Note: the offset must be correct before calling hva_to_pfn_retry() */
@@ -319,7 +341,15 @@ static int __kvm_gpc_refresh(struct gfn_to_pfn_cache *gpc, gpa_t gpa,
319341

320342
int kvm_gpc_refresh(struct gfn_to_pfn_cache *gpc, unsigned long len)
321343
{
322-
return __kvm_gpc_refresh(gpc, gpc->gpa, len);
344+
/*
345+
* If the GPA is valid then ignore the HVA, as a cache can be GPA-based
346+
* or HVA-based, not both. For GPA-based caches, the HVA will be
347+
* recomputed during refresh if necessary.
348+
*/
349+
unsigned long uhva = kvm_is_error_gpa(gpc->gpa) ? gpc->uhva :
350+
KVM_HVA_ERR_BAD;
351+
352+
return __kvm_gpc_refresh(gpc, gpc->gpa, uhva, len);
323353
}
324354

325355
void kvm_gpc_init(struct gfn_to_pfn_cache *gpc, struct kvm *kvm)
@@ -329,10 +359,12 @@ void kvm_gpc_init(struct gfn_to_pfn_cache *gpc, struct kvm *kvm)
329359

330360
gpc->kvm = kvm;
331361
gpc->pfn = KVM_PFN_ERR_FAULT;
362+
gpc->gpa = INVALID_GPA;
332363
gpc->uhva = KVM_HVA_ERR_BAD;
333364
}
334365

335-
int kvm_gpc_activate(struct gfn_to_pfn_cache *gpc, gpa_t gpa, unsigned long len)
366+
static int __kvm_gpc_activate(struct gfn_to_pfn_cache *gpc, gpa_t gpa, unsigned long uhva,
367+
unsigned long len)
336368
{
337369
struct kvm *kvm = gpc->kvm;
338370

@@ -353,7 +385,17 @@ int kvm_gpc_activate(struct gfn_to_pfn_cache *gpc, gpa_t gpa, unsigned long len)
353385
gpc->active = true;
354386
write_unlock_irq(&gpc->lock);
355387
}
356-
return __kvm_gpc_refresh(gpc, gpa, len);
388+
return __kvm_gpc_refresh(gpc, gpa, uhva, len);
389+
}
390+
391+
int kvm_gpc_activate(struct gfn_to_pfn_cache *gpc, gpa_t gpa, unsigned long len)
392+
{
393+
return __kvm_gpc_activate(gpc, gpa, KVM_HVA_ERR_BAD, len);
394+
}
395+
396+
int kvm_gpc_activate_hva(struct gfn_to_pfn_cache *gpc, unsigned long uhva, unsigned long len)
397+
{
398+
return __kvm_gpc_activate(gpc, INVALID_GPA, uhva, len);
357399
}
358400

359401
void kvm_gpc_deactivate(struct gfn_to_pfn_cache *gpc)

0 commit comments

Comments
 (0)