Skip to content

Commit e641a31

Browse files
committed
KVM: PPC: Book3S HV: Unify dirty page map between HPT and radix
Currently, the HPT code in HV KVM maintains a dirty bit per guest page in the rmap array, whether or not dirty page tracking has been enabled for the memory slot. In contrast, the radix code maintains a dirty bit per guest page in memslot->dirty_bitmap, and only does so when dirty page tracking has been enabled. This changes the HPT code to maintain the dirty bits in the memslot dirty_bitmap like radix does. This results in slightly less code overall, and will mean that we do not lose the dirty bits when transitioning between HPT and radix mode in future. There is one minor change to behaviour as a result. With HPT, when dirty tracking was enabled for a memslot, we would previously clear all the dirty bits at that point (both in the HPT entries and in the rmap arrays), meaning that a KVM_GET_DIRTY_LOG ioctl immediately following would show no pages as dirty (assuming no vcpus have run in the meantime). With this change, the dirty bits on HPT entries are not cleared at the point where dirty tracking is enabled, so KVM_GET_DIRTY_LOG would show as dirty any guest pages that are resident in the HPT and dirty. This is consistent with what happens on radix. This also fixes a bug in the mark_pages_dirty() function for radix (in the sense that the function no longer exists). In the case where a large page of 64 normal pages or more is marked dirty, the addressing of the dirty bitmap was incorrect and could write past the end of the bitmap. Fortunately this case was never hit in practice because a 2MB large page is only 32 x 64kB pages, and we don't support backing the guest with 1GB huge pages at this point. Signed-off-by: Paul Mackerras <[email protected]>
1 parent 1b151ce commit e641a31

File tree

7 files changed

+96
-113
lines changed

7 files changed

+96
-113
lines changed

arch/powerpc/include/asm/kvm_book3s.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,8 @@ extern kvm_pfn_t kvmppc_gpa_to_pfn(struct kvm_vcpu *vcpu, gpa_t gpa,
216216
bool writing, bool *writable);
217217
extern void kvmppc_add_revmap_chain(struct kvm *kvm, struct revmap_entry *rev,
218218
unsigned long *rmap, long pte_index, int realmode);
219-
extern void kvmppc_update_rmap_change(unsigned long *rmap, unsigned long psize);
219+
extern void kvmppc_update_dirty_map(struct kvm_memory_slot *memslot,
220+
unsigned long gfn, unsigned long psize);
220221
extern void kvmppc_invalidate_hpte(struct kvm *kvm, __be64 *hptep,
221222
unsigned long pte_index);
222223
void kvmppc_clear_ref_hpte(struct kvm *kvm, __be64 *hptep,

arch/powerpc/include/asm/kvm_book3s_64.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
#ifndef __ASM_KVM_BOOK3S_64_H__
2121
#define __ASM_KVM_BOOK3S_64_H__
2222

23+
#include <linux/string.h>
24+
#include <asm/bitops.h>
2325
#include <asm/book3s/64/mmu-hash.h>
2426

2527
/* Power architecture requires HPT is at least 256kiB, at most 64TiB */
@@ -444,6 +446,28 @@ static inline unsigned long kvmppc_hpt_mask(struct kvm_hpt_info *hpt)
444446
return (1UL << (hpt->order - 7)) - 1;
445447
}
446448

449+
/* Set bits in a dirty bitmap, which is in LE format */
450+
static inline void set_dirty_bits(unsigned long *map, unsigned long i,
451+
unsigned long npages)
452+
{
453+
454+
if (npages >= 8)
455+
memset((char *)map + i / 8, 0xff, npages / 8);
456+
else
457+
for (; npages; ++i, --npages)
458+
__set_bit_le(i, map);
459+
}
460+
461+
static inline void set_dirty_bits_atomic(unsigned long *map, unsigned long i,
462+
unsigned long npages)
463+
{
464+
if (npages >= 8)
465+
memset((char *)map + i / 8, 0xff, npages / 8);
466+
else
467+
for (; npages; ++i, --npages)
468+
set_bit_le(i, map);
469+
}
470+
447471
#endif /* CONFIG_KVM_BOOK3S_HV_POSSIBLE */
448472

449473
#endif /* __ASM_KVM_BOOK3S_64_H__ */

arch/powerpc/include/asm/kvm_host.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -235,10 +235,7 @@ struct revmap_entry {
235235
*/
236236
#define KVMPPC_RMAP_LOCK_BIT 63
237237
#define KVMPPC_RMAP_RC_SHIFT 32
238-
#define KVMPPC_RMAP_CHG_SHIFT 48
239238
#define KVMPPC_RMAP_REFERENCED (HPTE_R_R << KVMPPC_RMAP_RC_SHIFT)
240-
#define KVMPPC_RMAP_CHANGED (HPTE_R_C << KVMPPC_RMAP_RC_SHIFT)
241-
#define KVMPPC_RMAP_CHG_ORDER (0x3ful << KVMPPC_RMAP_CHG_SHIFT)
242239
#define KVMPPC_RMAP_PRESENT 0x100000000ul
243240
#define KVMPPC_RMAP_INDEX 0xfffffffful
244241

arch/powerpc/kvm/book3s_64_mmu_hv.c

Lines changed: 11 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,7 @@ static int kvm_handle_hva(struct kvm *kvm, unsigned long hva,
776776

777777
/* Must be called with both HPTE and rmap locked */
778778
static void kvmppc_unmap_hpte(struct kvm *kvm, unsigned long i,
779+
struct kvm_memory_slot *memslot,
779780
unsigned long *rmapp, unsigned long gfn)
780781
{
781782
__be64 *hptep = (__be64 *) (kvm->arch.hpt.virt + (i << 4));
@@ -807,8 +808,8 @@ static void kvmppc_unmap_hpte(struct kvm *kvm, unsigned long i,
807808
/* Harvest R and C */
808809
rcbits = be64_to_cpu(hptep[1]) & (HPTE_R_R | HPTE_R_C);
809810
*rmapp |= rcbits << KVMPPC_RMAP_RC_SHIFT;
810-
if (rcbits & HPTE_R_C)
811-
kvmppc_update_rmap_change(rmapp, psize);
811+
if ((rcbits & HPTE_R_C) && memslot->dirty_bitmap)
812+
kvmppc_update_dirty_map(memslot, gfn, psize);
812813
if (rcbits & ~rev[i].guest_rpte) {
813814
rev[i].guest_rpte = ptel | rcbits;
814815
note_hpte_modification(kvm, &rev[i]);
@@ -846,7 +847,7 @@ static int kvm_unmap_rmapp(struct kvm *kvm, struct kvm_memory_slot *memslot,
846847
continue;
847848
}
848849

849-
kvmppc_unmap_hpte(kvm, i, rmapp, gfn);
850+
kvmppc_unmap_hpte(kvm, i, memslot, rmapp, gfn);
850851
unlock_rmap(rmapp);
851852
__unlock_hpte(hptep, be64_to_cpu(hptep[0]));
852853
}
@@ -1029,14 +1030,6 @@ static int kvm_test_clear_dirty_npages(struct kvm *kvm, unsigned long *rmapp)
10291030

10301031
retry:
10311032
lock_rmap(rmapp);
1032-
if (*rmapp & KVMPPC_RMAP_CHANGED) {
1033-
long change_order = (*rmapp & KVMPPC_RMAP_CHG_ORDER)
1034-
>> KVMPPC_RMAP_CHG_SHIFT;
1035-
*rmapp &= ~(KVMPPC_RMAP_CHANGED | KVMPPC_RMAP_CHG_ORDER);
1036-
npages_dirty = 1;
1037-
if (change_order > PAGE_SHIFT)
1038-
npages_dirty = 1ul << (change_order - PAGE_SHIFT);
1039-
}
10401033
if (!(*rmapp & KVMPPC_RMAP_PRESENT)) {
10411034
unlock_rmap(rmapp);
10421035
return npages_dirty;
@@ -1128,7 +1121,7 @@ void kvmppc_harvest_vpa_dirty(struct kvmppc_vpa *vpa,
11281121
long kvmppc_hv_get_dirty_log_hpt(struct kvm *kvm,
11291122
struct kvm_memory_slot *memslot, unsigned long *map)
11301123
{
1131-
unsigned long i, j;
1124+
unsigned long i;
11321125
unsigned long *rmapp;
11331126

11341127
preempt_disable();
@@ -1140,9 +1133,8 @@ long kvmppc_hv_get_dirty_log_hpt(struct kvm *kvm,
11401133
* since we always put huge-page HPTEs in the rmap chain
11411134
* corresponding to their page base address.
11421135
*/
1143-
if (npages && map)
1144-
for (j = i; npages; ++j, --npages)
1145-
__set_bit_le(j, map);
1136+
if (npages)
1137+
set_dirty_bits(map, i, npages);
11461138
++rmapp;
11471139
}
11481140
preempt_enable();
@@ -1186,28 +1178,19 @@ void kvmppc_unpin_guest_page(struct kvm *kvm, void *va, unsigned long gpa,
11861178
struct page *page = virt_to_page(va);
11871179
struct kvm_memory_slot *memslot;
11881180
unsigned long gfn;
1189-
unsigned long *rmap;
11901181
int srcu_idx;
11911182

11921183
put_page(page);
11931184

11941185
if (!dirty)
11951186
return;
11961187

1197-
/* We need to mark this page dirty in the rmap chain */
1188+
/* We need to mark this page dirty in the memslot dirty_bitmap, if any */
11981189
gfn = gpa >> PAGE_SHIFT;
11991190
srcu_idx = srcu_read_lock(&kvm->srcu);
12001191
memslot = gfn_to_memslot(kvm, gfn);
1201-
if (memslot) {
1202-
if (!kvm_is_radix(kvm)) {
1203-
rmap = &memslot->arch.rmap[gfn - memslot->base_gfn];
1204-
lock_rmap(rmap);
1205-
*rmap |= KVMPPC_RMAP_CHANGED;
1206-
unlock_rmap(rmap);
1207-
} else if (memslot->dirty_bitmap) {
1208-
mark_page_dirty(kvm, gfn);
1209-
}
1210-
}
1192+
if (memslot && memslot->dirty_bitmap)
1193+
set_bit_le(gfn - memslot->base_gfn, memslot->dirty_bitmap);
12111194
srcu_read_unlock(&kvm->srcu, srcu_idx);
12121195
}
12131196

@@ -1282,7 +1265,7 @@ static unsigned long resize_hpt_rehash_hpte(struct kvm_resize_hpt *resize,
12821265
rmapp = &memslot->arch.rmap[gfn - memslot->base_gfn];
12831266

12841267
lock_rmap(rmapp);
1285-
kvmppc_unmap_hpte(kvm, idx, rmapp, gfn);
1268+
kvmppc_unmap_hpte(kvm, idx, memslot, rmapp, gfn);
12861269
unlock_rmap(rmapp);
12871270
}
12881271

arch/powerpc/kvm/book3s_64_mmu_radix.c

Lines changed: 9 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -474,26 +474,6 @@ int kvmppc_book3s_radix_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu,
474474
return ret;
475475
}
476476

477-
static void mark_pages_dirty(struct kvm *kvm, struct kvm_memory_slot *memslot,
478-
unsigned long gfn, unsigned int order)
479-
{
480-
unsigned long i, limit;
481-
unsigned long *dp;
482-
483-
if (!memslot->dirty_bitmap)
484-
return;
485-
limit = 1ul << order;
486-
if (limit < BITS_PER_LONG) {
487-
for (i = 0; i < limit; ++i)
488-
mark_page_dirty(kvm, gfn + i);
489-
return;
490-
}
491-
dp = memslot->dirty_bitmap + (gfn - memslot->base_gfn);
492-
limit /= BITS_PER_LONG;
493-
for (i = 0; i < limit; ++i)
494-
*dp++ = ~0ul;
495-
}
496-
497477
/* Called with kvm->lock held */
498478
int kvm_unmap_radix(struct kvm *kvm, struct kvm_memory_slot *memslot,
499479
unsigned long gfn)
@@ -508,12 +488,11 @@ int kvm_unmap_radix(struct kvm *kvm, struct kvm_memory_slot *memslot,
508488
old = kvmppc_radix_update_pte(kvm, ptep, _PAGE_PRESENT, 0,
509489
gpa, shift);
510490
kvmppc_radix_tlbie_page(kvm, gpa, shift);
511-
if (old & _PAGE_DIRTY) {
512-
if (!shift)
513-
mark_page_dirty(kvm, gfn);
514-
else
515-
mark_pages_dirty(kvm, memslot,
516-
gfn, shift - PAGE_SHIFT);
491+
if ((old & _PAGE_DIRTY) && memslot->dirty_bitmap) {
492+
unsigned long npages = 1;
493+
if (shift)
494+
npages = 1ul << (shift - PAGE_SHIFT);
495+
kvmppc_update_dirty_map(memslot, gfn, npages);
517496
}
518497
}
519498
return 0;
@@ -579,20 +558,8 @@ long kvmppc_hv_get_dirty_log_radix(struct kvm *kvm,
579558
struct kvm_memory_slot *memslot, unsigned long *map)
580559
{
581560
unsigned long i, j;
582-
unsigned long n, *p;
583561
int npages;
584562

585-
/*
586-
* Radix accumulates dirty bits in the first half of the
587-
* memslot's dirty_bitmap area, for when pages are paged
588-
* out or modified by the host directly. Pick up these
589-
* bits and add them to the map.
590-
*/
591-
n = kvm_dirty_bitmap_bytes(memslot) / sizeof(long);
592-
p = memslot->dirty_bitmap;
593-
for (i = 0; i < n; ++i)
594-
map[i] |= xchg(&p[i], 0);
595-
596563
for (i = 0; i < memslot->npages; i = j) {
597564
npages = kvm_radix_test_clear_dirty(kvm, memslot, i);
598565

@@ -604,9 +571,10 @@ long kvmppc_hv_get_dirty_log_radix(struct kvm *kvm,
604571
* real address, if npages > 1 we can skip to i + npages.
605572
*/
606573
j = i + 1;
607-
if (npages)
608-
for (j = i; npages; ++j, --npages)
609-
__set_bit_le(j, map);
574+
if (npages) {
575+
set_dirty_bits(map, i, npages);
576+
i = j + npages;
577+
}
610578
}
611579
return 0;
612580
}

arch/powerpc/kvm/book3s_hv.c

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3376,7 +3376,7 @@ static int kvm_vm_ioctl_get_dirty_log_hv(struct kvm *kvm,
33763376
struct kvm_memory_slot *memslot;
33773377
int i, r;
33783378
unsigned long n;
3379-
unsigned long *buf;
3379+
unsigned long *buf, *p;
33803380
struct kvm_vcpu *vcpu;
33813381

33823382
mutex_lock(&kvm->slots_lock);
@@ -3392,8 +3392,8 @@ static int kvm_vm_ioctl_get_dirty_log_hv(struct kvm *kvm,
33923392
goto out;
33933393

33943394
/*
3395-
* Use second half of bitmap area because radix accumulates
3396-
* bits in the first half.
3395+
* Use second half of bitmap area because both HPT and radix
3396+
* accumulate bits in the first half.
33973397
*/
33983398
n = kvm_dirty_bitmap_bytes(memslot);
33993399
buf = memslot->dirty_bitmap + n / sizeof(long);
@@ -3406,6 +3406,16 @@ static int kvm_vm_ioctl_get_dirty_log_hv(struct kvm *kvm,
34063406
if (r)
34073407
goto out;
34083408

3409+
/*
3410+
* We accumulate dirty bits in the first half of the
3411+
* memslot's dirty_bitmap area, for when pages are paged
3412+
* out or modified by the host directly. Pick up these
3413+
* bits and add them to the map.
3414+
*/
3415+
p = memslot->dirty_bitmap;
3416+
for (i = 0; i < n / sizeof(long); ++i)
3417+
buf[i] |= xchg(&p[i], 0);
3418+
34093419
/* Harvest dirty bits from VPA and DTL updates */
34103420
/* Note: we never modify the SLB shadow buffer areas */
34113421
kvm_for_each_vcpu(i, vcpu, kvm) {
@@ -3466,8 +3476,6 @@ static void kvmppc_core_commit_memory_region_hv(struct kvm *kvm,
34663476
const struct kvm_memory_slot *new)
34673477
{
34683478
unsigned long npages = mem->memory_size >> PAGE_SHIFT;
3469-
struct kvm_memslots *slots;
3470-
struct kvm_memory_slot *memslot;
34713479

34723480
/*
34733481
* If we are making a new memslot, it might make
@@ -3477,18 +3485,6 @@ static void kvmppc_core_commit_memory_region_hv(struct kvm *kvm,
34773485
*/
34783486
if (npages)
34793487
atomic64_inc(&kvm->arch.mmio_update);
3480-
3481-
if (npages && old->npages && !kvm_is_radix(kvm)) {
3482-
/*
3483-
* If modifying a memslot, reset all the rmap dirty bits.
3484-
* If this is a new memslot, we don't need to do anything
3485-
* since the rmap array starts out as all zeroes,
3486-
* i.e. no pages are dirty.
3487-
*/
3488-
slots = kvm_memslots(kvm);
3489-
memslot = id_to_memslot(slots, mem->slot);
3490-
kvmppc_hv_get_dirty_log_hpt(kvm, memslot, NULL);
3491-
}
34923488
}
34933489

34943490
/*

0 commit comments

Comments
 (0)