Skip to content

Commit fe8e3f4

Browse files
author
Marc Zyngier
committed
Merge branch kvm-arm64/parallel-faults into kvmarm-master/next
* kvm-arm64/parallel-faults: : . : Parallel stage-2 fault handling, courtesy of Oliver Upton. : From the cover letter: : : "Presently KVM only takes a read lock for stage 2 faults if it believes : the fault can be fixed by relaxing permissions on a PTE (write unprotect : for dirty logging). Otherwise, stage 2 faults grab the write lock, which : predictably can pile up all the vCPUs in a sufficiently large VM. : : Like the TDP MMU for x86, this series loosens the locking around : manipulations of the stage 2 page tables to allow parallel faults. RCU : and atomics are exploited to safely build/destroy the stage 2 page : tables in light of multiple software observers." : . KVM: arm64: Reject shared table walks in the hyp code KVM: arm64: Don't acquire RCU read lock for exclusive table walks KVM: arm64: Take a pointer to walker data in kvm_dereference_pteref() KVM: arm64: Handle stage-2 faults in parallel KVM: arm64: Make table->block changes parallel-aware KVM: arm64: Make leaf->leaf PTE changes parallel-aware KVM: arm64: Make block->table PTE changes parallel-aware KVM: arm64: Split init and set for table PTE KVM: arm64: Atomically update stage 2 leaf attributes in parallel walks KVM: arm64: Protect stage-2 traversal with RCU KVM: arm64: Tear down unlinked stage-2 subtree after break-before-make KVM: arm64: Use an opaque type for pteps KVM: arm64: Add a helper to tear down unlinked stage-2 subtrees KVM: arm64: Don't pass kvm_pgtable through kvm_pgtable_walk_data KVM: arm64: Pass mm_ops through the visitor context KVM: arm64: Stash observed pte value in visitor context KVM: arm64: Combine visitor arguments into a context structure Signed-off-by: Marc Zyngier <[email protected]>
2 parents a937f37 + 5e806c5 commit fe8e3f4

File tree

5 files changed

+508
-372
lines changed

5 files changed

+508
-372
lines changed

arch/arm64/include/asm/kvm_pgtable.h

Lines changed: 125 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ static inline bool kvm_level_supports_block_mapping(u32 level)
8585
* allocation is physically contiguous.
8686
* @free_pages_exact: Free an exact number of memory pages previously
8787
* allocated by zalloc_pages_exact.
88+
* @free_removed_table: Free a removed paging structure by unlinking and
89+
* dropping references.
8890
* @get_page: Increment the refcount on a page.
8991
* @put_page: Decrement the refcount on a page. When the
9092
* refcount reaches 0 the page is automatically
@@ -103,6 +105,7 @@ struct kvm_pgtable_mm_ops {
103105
void* (*zalloc_page)(void *arg);
104106
void* (*zalloc_pages_exact)(size_t size);
105107
void (*free_pages_exact)(void *addr, size_t size);
108+
void (*free_removed_table)(void *addr, u32 level);
106109
void (*get_page)(void *addr);
107110
void (*put_page)(void *addr);
108111
int (*page_count)(void *addr);
@@ -161,29 +164,6 @@ enum kvm_pgtable_prot {
161164
typedef bool (*kvm_pgtable_force_pte_cb_t)(u64 addr, u64 end,
162165
enum kvm_pgtable_prot prot);
163166

164-
/**
165-
* struct kvm_pgtable - KVM page-table.
166-
* @ia_bits: Maximum input address size, in bits.
167-
* @start_level: Level at which the page-table walk starts.
168-
* @pgd: Pointer to the first top-level entry of the page-table.
169-
* @mm_ops: Memory management callbacks.
170-
* @mmu: Stage-2 KVM MMU struct. Unused for stage-1 page-tables.
171-
* @flags: Stage-2 page-table flags.
172-
* @force_pte_cb: Function that returns true if page level mappings must
173-
* be used instead of block mappings.
174-
*/
175-
struct kvm_pgtable {
176-
u32 ia_bits;
177-
u32 start_level;
178-
kvm_pte_t *pgd;
179-
struct kvm_pgtable_mm_ops *mm_ops;
180-
181-
/* Stage-2 only */
182-
struct kvm_s2_mmu *mmu;
183-
enum kvm_pgtable_stage2_flags flags;
184-
kvm_pgtable_force_pte_cb_t force_pte_cb;
185-
};
186-
187167
/**
188168
* enum kvm_pgtable_walk_flags - Flags to control a depth-first page-table walk.
189169
* @KVM_PGTABLE_WALK_LEAF: Visit leaf entries, including invalid
@@ -192,17 +172,34 @@ struct kvm_pgtable {
192172
* children.
193173
* @KVM_PGTABLE_WALK_TABLE_POST: Visit table entries after their
194174
* children.
175+
* @KVM_PGTABLE_WALK_SHARED: Indicates the page-tables may be shared
176+
* with other software walkers.
195177
*/
196178
enum kvm_pgtable_walk_flags {
197179
KVM_PGTABLE_WALK_LEAF = BIT(0),
198180
KVM_PGTABLE_WALK_TABLE_PRE = BIT(1),
199181
KVM_PGTABLE_WALK_TABLE_POST = BIT(2),
182+
KVM_PGTABLE_WALK_SHARED = BIT(3),
200183
};
201184

202-
typedef int (*kvm_pgtable_visitor_fn_t)(u64 addr, u64 end, u32 level,
203-
kvm_pte_t *ptep,
204-
enum kvm_pgtable_walk_flags flag,
205-
void * const arg);
185+
struct kvm_pgtable_visit_ctx {
186+
kvm_pte_t *ptep;
187+
kvm_pte_t old;
188+
void *arg;
189+
struct kvm_pgtable_mm_ops *mm_ops;
190+
u64 addr;
191+
u64 end;
192+
u32 level;
193+
enum kvm_pgtable_walk_flags flags;
194+
};
195+
196+
typedef int (*kvm_pgtable_visitor_fn_t)(const struct kvm_pgtable_visit_ctx *ctx,
197+
enum kvm_pgtable_walk_flags visit);
198+
199+
static inline bool kvm_pgtable_walk_shared(const struct kvm_pgtable_visit_ctx *ctx)
200+
{
201+
return ctx->flags & KVM_PGTABLE_WALK_SHARED;
202+
}
206203

207204
/**
208205
* struct kvm_pgtable_walker - Hook into a page-table walk.
@@ -217,6 +214,94 @@ struct kvm_pgtable_walker {
217214
const enum kvm_pgtable_walk_flags flags;
218215
};
219216

217+
/*
218+
* RCU cannot be used in a non-kernel context such as the hyp. As such, page
219+
* table walkers used in hyp do not call into RCU and instead use other
220+
* synchronization mechanisms (such as a spinlock).
221+
*/
222+
#if defined(__KVM_NVHE_HYPERVISOR__) || defined(__KVM_VHE_HYPERVISOR__)
223+
224+
typedef kvm_pte_t *kvm_pteref_t;
225+
226+
static inline kvm_pte_t *kvm_dereference_pteref(struct kvm_pgtable_walker *walker,
227+
kvm_pteref_t pteref)
228+
{
229+
return pteref;
230+
}
231+
232+
static inline int kvm_pgtable_walk_begin(struct kvm_pgtable_walker *walker)
233+
{
234+
/*
235+
* Due to the lack of RCU (or a similar protection scheme), only
236+
* non-shared table walkers are allowed in the hypervisor.
237+
*/
238+
if (walker->flags & KVM_PGTABLE_WALK_SHARED)
239+
return -EPERM;
240+
241+
return 0;
242+
}
243+
244+
static inline void kvm_pgtable_walk_end(struct kvm_pgtable_walker *walker) {}
245+
246+
static inline bool kvm_pgtable_walk_lock_held(void)
247+
{
248+
return true;
249+
}
250+
251+
#else
252+
253+
typedef kvm_pte_t __rcu *kvm_pteref_t;
254+
255+
static inline kvm_pte_t *kvm_dereference_pteref(struct kvm_pgtable_walker *walker,
256+
kvm_pteref_t pteref)
257+
{
258+
return rcu_dereference_check(pteref, !(walker->flags & KVM_PGTABLE_WALK_SHARED));
259+
}
260+
261+
static inline int kvm_pgtable_walk_begin(struct kvm_pgtable_walker *walker)
262+
{
263+
if (walker->flags & KVM_PGTABLE_WALK_SHARED)
264+
rcu_read_lock();
265+
266+
return 0;
267+
}
268+
269+
static inline void kvm_pgtable_walk_end(struct kvm_pgtable_walker *walker)
270+
{
271+
if (walker->flags & KVM_PGTABLE_WALK_SHARED)
272+
rcu_read_unlock();
273+
}
274+
275+
static inline bool kvm_pgtable_walk_lock_held(void)
276+
{
277+
return rcu_read_lock_held();
278+
}
279+
280+
#endif
281+
282+
/**
283+
* struct kvm_pgtable - KVM page-table.
284+
* @ia_bits: Maximum input address size, in bits.
285+
* @start_level: Level at which the page-table walk starts.
286+
* @pgd: Pointer to the first top-level entry of the page-table.
287+
* @mm_ops: Memory management callbacks.
288+
* @mmu: Stage-2 KVM MMU struct. Unused for stage-1 page-tables.
289+
* @flags: Stage-2 page-table flags.
290+
* @force_pte_cb: Function that returns true if page level mappings must
291+
* be used instead of block mappings.
292+
*/
293+
struct kvm_pgtable {
294+
u32 ia_bits;
295+
u32 start_level;
296+
kvm_pteref_t pgd;
297+
struct kvm_pgtable_mm_ops *mm_ops;
298+
299+
/* Stage-2 only */
300+
struct kvm_s2_mmu *mmu;
301+
enum kvm_pgtable_stage2_flags flags;
302+
kvm_pgtable_force_pte_cb_t force_pte_cb;
303+
};
304+
220305
/**
221306
* kvm_pgtable_hyp_init() - Initialise a hypervisor stage-1 page-table.
222307
* @pgt: Uninitialised page-table structure to initialise.
@@ -324,6 +409,17 @@ int __kvm_pgtable_stage2_init(struct kvm_pgtable *pgt, struct kvm_s2_mmu *mmu,
324409
*/
325410
void kvm_pgtable_stage2_destroy(struct kvm_pgtable *pgt);
326411

412+
/**
413+
* kvm_pgtable_stage2_free_removed() - Free a removed stage-2 paging structure.
414+
* @mm_ops: Memory management callbacks.
415+
* @pgtable: Unlinked stage-2 paging structure to be freed.
416+
* @level: Level of the stage-2 paging structure to be freed.
417+
*
418+
* The page-table is assumed to be unreachable by any hardware walkers prior to
419+
* freeing and therefore no TLB invalidation is performed.
420+
*/
421+
void kvm_pgtable_stage2_free_removed(struct kvm_pgtable_mm_ops *mm_ops, void *pgtable, u32 level);
422+
327423
/**
328424
* kvm_pgtable_stage2_map() - Install a mapping in a guest stage-2 page-table.
329425
* @pgt: Page-table structure initialised by kvm_pgtable_stage2_init*().
@@ -333,6 +429,7 @@ void kvm_pgtable_stage2_destroy(struct kvm_pgtable *pgt);
333429
* @prot: Permissions and attributes for the mapping.
334430
* @mc: Cache of pre-allocated and zeroed memory from which to allocate
335431
* page-table pages.
432+
* @flags: Flags to control the page-table walk (ex. a shared walk)
336433
*
337434
* The offset of @addr within a page is ignored, @size is rounded-up to
338435
* the next page boundary and @phys is rounded-down to the previous page
@@ -354,7 +451,7 @@ void kvm_pgtable_stage2_destroy(struct kvm_pgtable *pgt);
354451
*/
355452
int kvm_pgtable_stage2_map(struct kvm_pgtable *pgt, u64 addr, u64 size,
356453
u64 phys, enum kvm_pgtable_prot prot,
357-
void *mc);
454+
void *mc, enum kvm_pgtable_walk_flags flags);
358455

359456
/**
360457
* kvm_pgtable_stage2_set_owner() - Unmap and annotate pages in the IPA space to

arch/arm64/kvm/hyp/nvhe/mem_protect.c

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ static void host_s2_put_page(void *addr)
7979
hyp_put_page(&host_s2_pool, addr);
8080
}
8181

82+
static void host_s2_free_removed_table(void *addr, u32 level)
83+
{
84+
kvm_pgtable_stage2_free_removed(&host_kvm.mm_ops, addr, level);
85+
}
86+
8287
static int prepare_s2_pool(void *pgt_pool_base)
8388
{
8489
unsigned long nr_pages, pfn;
@@ -93,6 +98,7 @@ static int prepare_s2_pool(void *pgt_pool_base)
9398
host_kvm.mm_ops = (struct kvm_pgtable_mm_ops) {
9499
.zalloc_pages_exact = host_s2_zalloc_pages_exact,
95100
.zalloc_page = host_s2_zalloc_page,
101+
.free_removed_table = host_s2_free_removed_table,
96102
.phys_to_virt = hyp_phys_to_virt,
97103
.virt_to_phys = hyp_virt_to_phys,
98104
.page_count = hyp_page_count,
@@ -251,7 +257,7 @@ static inline int __host_stage2_idmap(u64 start, u64 end,
251257
enum kvm_pgtable_prot prot)
252258
{
253259
return kvm_pgtable_stage2_map(&host_kvm.pgt, start, end - start, start,
254-
prot, &host_s2_pool);
260+
prot, &host_s2_pool, 0);
255261
}
256262

257263
/*
@@ -417,18 +423,15 @@ struct check_walk_data {
417423
enum pkvm_page_state (*get_page_state)(kvm_pte_t pte);
418424
};
419425

420-
static int __check_page_state_visitor(u64 addr, u64 end, u32 level,
421-
kvm_pte_t *ptep,
422-
enum kvm_pgtable_walk_flags flag,
423-
void * const arg)
426+
static int __check_page_state_visitor(const struct kvm_pgtable_visit_ctx *ctx,
427+
enum kvm_pgtable_walk_flags visit)
424428
{
425-
struct check_walk_data *d = arg;
426-
kvm_pte_t pte = *ptep;
429+
struct check_walk_data *d = ctx->arg;
427430

428-
if (kvm_pte_valid(pte) && !addr_is_memory(kvm_pte_to_phys(pte)))
431+
if (kvm_pte_valid(ctx->old) && !addr_is_memory(kvm_pte_to_phys(ctx->old)))
429432
return -EINVAL;
430433

431-
return d->get_page_state(pte) == d->desired ? 0 : -EPERM;
434+
return d->get_page_state(ctx->old) == d->desired ? 0 : -EPERM;
432435
}
433436

434437
static int check_page_state_range(struct kvm_pgtable *pgt, u64 addr, u64 size,

arch/arm64/kvm/hyp/nvhe/setup.c

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -186,41 +186,38 @@ static void hpool_put_page(void *addr)
186186
hyp_put_page(&hpool, addr);
187187
}
188188

189-
static int finalize_host_mappings_walker(u64 addr, u64 end, u32 level,
190-
kvm_pte_t *ptep,
191-
enum kvm_pgtable_walk_flags flag,
192-
void * const arg)
189+
static int finalize_host_mappings_walker(const struct kvm_pgtable_visit_ctx *ctx,
190+
enum kvm_pgtable_walk_flags visit)
193191
{
194-
struct kvm_pgtable_mm_ops *mm_ops = arg;
192+
struct kvm_pgtable_mm_ops *mm_ops = ctx->mm_ops;
195193
enum kvm_pgtable_prot prot;
196194
enum pkvm_page_state state;
197-
kvm_pte_t pte = *ptep;
198195
phys_addr_t phys;
199196

200-
if (!kvm_pte_valid(pte))
197+
if (!kvm_pte_valid(ctx->old))
201198
return 0;
202199

203200
/*
204201
* Fix-up the refcount for the page-table pages as the early allocator
205202
* was unable to access the hyp_vmemmap and so the buddy allocator has
206203
* initialised the refcount to '1'.
207204
*/
208-
mm_ops->get_page(ptep);
209-
if (flag != KVM_PGTABLE_WALK_LEAF)
205+
mm_ops->get_page(ctx->ptep);
206+
if (visit != KVM_PGTABLE_WALK_LEAF)
210207
return 0;
211208

212-
if (level != (KVM_PGTABLE_MAX_LEVELS - 1))
209+
if (ctx->level != (KVM_PGTABLE_MAX_LEVELS - 1))
213210
return -EINVAL;
214211

215-
phys = kvm_pte_to_phys(pte);
212+
phys = kvm_pte_to_phys(ctx->old);
216213
if (!addr_is_memory(phys))
217214
return -EINVAL;
218215

219216
/*
220217
* Adjust the host stage-2 mappings to match the ownership attributes
221218
* configured in the hypervisor stage-1.
222219
*/
223-
state = pkvm_getstate(kvm_pgtable_hyp_pte_prot(pte));
220+
state = pkvm_getstate(kvm_pgtable_hyp_pte_prot(ctx->old));
224221
switch (state) {
225222
case PKVM_PAGE_OWNED:
226223
return host_stage2_set_owner_locked(phys, PAGE_SIZE, pkvm_hyp_id);
@@ -242,7 +239,6 @@ static int finalize_host_mappings(void)
242239
struct kvm_pgtable_walker walker = {
243240
.cb = finalize_host_mappings_walker,
244241
.flags = KVM_PGTABLE_WALK_LEAF | KVM_PGTABLE_WALK_TABLE_POST,
245-
.arg = pkvm_pgtable.mm_ops,
246242
};
247243
int i, ret;
248244

0 commit comments

Comments
 (0)