Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1333492 > unrolled thread
| Started by | Xiao Guangrong <guangrong.xiao@linux.intel.com> |
|---|---|
| First post | 2016-02-14 12:50 +0100 |
| Last post | 2016-02-22 11:20 +0100 |
| Articles | 8 — 2 participants |
Back to article view | Back to linux.kernel
[PATCH v3 00/11] KVM: x86: track guest page access Xiao Guangrong <guangrong.xiao@linux.intel.com> - 2016-02-14 12:50 +0100
[PATCH v3 07/11] KVM: page track: add notifier support Xiao Guangrong <guangrong.xiao@linux.intel.com> - 2016-02-14 12:50 +0100
Re: [PATCH v3 07/11] KVM: page track: add notifier support Paolo Bonzini <pbonzini@redhat.com> - 2016-02-19 13:00 +0100
[PATCH v3 04/11] KVM: page track: add the framework of guest page tracking Xiao Guangrong <guangrong.xiao@linux.intel.com> - 2016-02-14 12:50 +0100
Re: [PATCH v3 04/11] KVM: page track: add the framework of guest page tracking Paolo Bonzini <pbonzini@redhat.com> - 2016-02-19 12:30 +0100
[PATCH v3 03/11] KVM: MMU: introduce kvm_mmu_slot_gfn_write_protect Xiao Guangrong <guangrong.xiao@linux.intel.com> - 2016-02-14 12:50 +0100
Re: [PATCH v3 00/11] KVM: x86: track guest page access Paolo Bonzini <pbonzini@redhat.com> - 2016-02-19 13:10 +0100
Re: [PATCH v3 00/11] KVM: x86: track guest page access Xiao Guangrong <guangrong.xiao@linux.intel.com> - 2016-02-22 11:20 +0100
| From | Xiao Guangrong <guangrong.xiao@linux.intel.com> |
|---|---|
| Date | 2016-02-14 12:50 +0100 |
| Subject | [PATCH v3 00/11] KVM: x86: track guest page access |
| Message-ID | <r23lD-25X-3@gated-at.bofh.it> |
Changelong in v3:
- refine the code of mmu_need_write_protect() based on Huang Kai's suggestion
- rebase the patchset against current code
Changelog in v2:
- fix a issue that the track memory of memslot is freed if we only move
the memslot or change the flags of memslot
- do not track the gfn which is not mapped in memslots
- introduce the nolock APIs at the begin of the patchset
- use 'unsigned short' as the track counter to reduce the memory and which
should be enough for shadow page table and KVMGT
This patchset introduces the feature which allows us to track page
access in guest. Currently, only write access tracking is implemented
in this version.
Four APIs are introduces:
- kvm_page_track_add_page(kvm, gfn, mode), single guest page @gfn is
added into the track pool of the guest instance represented by @kvm,
@mode specifies which kind of access on the @gfn is tracked
- kvm_page_track_remove_page(kvm, gfn, mode), is the opposed operation
of kvm_page_track_add_page() which removes @gfn from the tracking pool.
gfn is no tracked after its last user is gone
- kvm_page_track_register_notifier(kvm, n), register a notifier so that
the event triggered by page tracking will be received, at that time,
the callback of n->track_write() will be called
- kvm_page_track_unregister_notifier(kvm, n), does the opposed operation
of kvm_page_track_register_notifier(), which unlinks the notifier and
stops receiving the tracked event
The first user of page track is non-leaf shadow page tables as they are
always write protected. It also gains performance improvement because
page track speeds up page fault handler for the tracked pages. The
performance result of kernel building is as followings:
before after
real 461.63 real 455.48
user 4529.55 user 4557.88
sys 1995.39 sys 1922.57
Furthermore, it is the infrastructure of other kind of shadow page table,
such as GPU shadow page table introduced in KVMGT (1) and native nested
IOMMU.
This patch can be divided into two parts:
- patch 1 ~ patch 7, implement page tracking
- others patches apply page tracking to non-leaf shadow page table
(1): http://lkml.iu.edu/hypermail/linux/kernel/1510.3/01562.html
Xiao Guangrong (11):
KVM: MMU: rename has_wrprotected_page to mmu_gfn_lpage_is_disallowed
KVM: MMU: introduce kvm_mmu_gfn_{allow,disallow}_lpage
KVM: MMU: introduce kvm_mmu_slot_gfn_write_protect
KVM: page track: add the framework of guest page tracking
KVM: page track: introduce kvm_page_track_{add,remove}_page
KVM: MMU: let page fault handler be aware tracked page
KVM: page track: add notifier support
KVM: MMU: use page track for non-leaf shadow pages
KVM: MMU: simplify mmu_need_write_protect
KVM: MMU: clear write-flooding on the fast path of tracked page
KVM: MMU: apply page track notifier
Documentation/virtual/kvm/mmu.txt | 6 +-
arch/x86/include/asm/kvm_host.h | 12 +-
arch/x86/include/asm/kvm_page_track.h | 67 +++++++++
arch/x86/kvm/Makefile | 3 +-
arch/x86/kvm/mmu.c | 209 ++++++++++++++++++---------
arch/x86/kvm/mmu.h | 5 +
arch/x86/kvm/page_track.c | 257 ++++++++++++++++++++++++++++++++++
arch/x86/kvm/paging_tmpl.h | 5 +
arch/x86/kvm/x86.c | 27 ++--
9 files changed, 512 insertions(+), 79 deletions(-)
create mode 100644 arch/x86/include/asm/kvm_page_track.h
create mode 100644 arch/x86/kvm/page_track.c
--
1.8.3.1
[toc] | [next] | [standalone]
| From | Xiao Guangrong <guangrong.xiao@linux.intel.com> |
|---|---|
| Date | 2016-02-14 12:50 +0100 |
| Subject | [PATCH v3 07/11] KVM: page track: add notifier support |
| Message-ID | <r23lE-25X-27@gated-at.bofh.it> |
| In reply to | #1333492 |
Notifier list is introduced so that any node wants to receive the track
event can register to the list
Two APIs are introduced here:
- kvm_page_track_register_notifier(): register the notifier to receive
track event
- kvm_page_track_unregister_notifier(): stop receiving track event by
unregister the notifier
The callback, node->track_write() is called when a write access on the
write tracked page happens
Signed-off-by: Xiao Guangrong <guangrong.xiao@linux.intel.com>
---
arch/x86/include/asm/kvm_host.h | 1 +
arch/x86/include/asm/kvm_page_track.h | 39 ++++++++++++++++++++
arch/x86/kvm/page_track.c | 67 +++++++++++++++++++++++++++++++++++
arch/x86/kvm/x86.c | 4 +++
4 files changed, 111 insertions(+)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index d8931d0..282bc2f 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -696,6 +696,7 @@ struct kvm_arch {
*/
struct list_head active_mmu_pages;
struct list_head zapped_obsolete_pages;
+ struct kvm_page_track_notifier_head track_notifier_head;
struct list_head assigned_dev_head;
struct iommu_domain *iommu_domain;
diff --git a/arch/x86/include/asm/kvm_page_track.h b/arch/x86/include/asm/kvm_page_track.h
index 97ac9c3..1aae4ef 100644
--- a/arch/x86/include/asm/kvm_page_track.h
+++ b/arch/x86/include/asm/kvm_page_track.h
@@ -6,6 +6,36 @@ enum kvm_page_track_mode {
KVM_PAGE_TRACK_MAX,
};
+/*
+ * The notifier represented by @kvm_page_track_notifier_node is linked into
+ * the head which will be notified when guest is triggering the track event.
+ *
+ * Write access on the head is protected by kvm->mmu_lock, read access
+ * is protected by track_srcu.
+ */
+struct kvm_page_track_notifier_head {
+ struct srcu_struct track_srcu;
+ struct hlist_head track_notifier_list;
+};
+
+struct kvm_page_track_notifier_node {
+ struct hlist_node node;
+
+ /*
+ * It is called when guest is writing the write-tracked page
+ * and write emulation is finished at that time.
+ *
+ * @vcpu: the vcpu where the write access happened.
+ * @gpa: the physical address written by guest.
+ * @new: the data was written to the address.
+ * @bytes: the written length.
+ */
+ void (*track_write)(struct kvm_vcpu *vcpu, gpa_t gpa, const u8 *new,
+ int bytes);
+};
+
+void kvm_page_track_init(struct kvm *kvm);
+
void kvm_page_track_free_memslot(struct kvm_memory_slot *free,
struct kvm_memory_slot *dont);
int kvm_page_track_create_memslot(struct kvm_memory_slot *slot,
@@ -25,4 +55,13 @@ void kvm_page_track_remove_page(struct kvm *kvm, gfn_t gfn,
enum kvm_page_track_mode mode);
bool kvm_page_track_check_mode(struct kvm_vcpu *vcpu, gfn_t gfn,
enum kvm_page_track_mode mode);
+
+void
+kvm_page_track_register_notifier(struct kvm *kvm,
+ struct kvm_page_track_notifier_node *n);
+void
+kvm_page_track_unregister_notifier(struct kvm *kvm,
+ struct kvm_page_track_notifier_node *n);
+void kvm_page_track_write(struct kvm_vcpu *vcpu, gpa_t gpa, const u8 *new,
+ int bytes);
#endif
diff --git a/arch/x86/kvm/page_track.c b/arch/x86/kvm/page_track.c
index de9b32f..0692cc6 100644
--- a/arch/x86/kvm/page_track.c
+++ b/arch/x86/kvm/page_track.c
@@ -188,3 +188,70 @@ bool kvm_page_track_check_mode(struct kvm_vcpu *vcpu, gfn_t gfn,
return !!ACCESS_ONCE(slot->arch.gfn_track[mode][index]);
}
+
+void kvm_page_track_init(struct kvm *kvm)
+{
+ struct kvm_page_track_notifier_head *head;
+
+ head = &kvm->arch.track_notifier_head;
+ init_srcu_struct(&head->track_srcu);
+ INIT_HLIST_HEAD(&head->track_notifier_list);
+}
+
+/*
+ * register the notifier so that event interception for the tracked guest
+ * pages can be received.
+ */
+void
+kvm_page_track_register_notifier(struct kvm *kvm,
+ struct kvm_page_track_notifier_node *n)
+{
+ struct kvm_page_track_notifier_head *head;
+
+ head = &kvm->arch.track_notifier_head;
+
+ spin_lock(&kvm->mmu_lock);
+ hlist_add_head_rcu(&n->node, &head->track_notifier_list);
+ spin_unlock(&kvm->mmu_lock);
+}
+
+/*
+ * stop receiving the event interception. It is the opposed operation of
+ * kvm_page_track_register_notifier().
+ */
+void
+kvm_page_track_unregister_notifier(struct kvm *kvm,
+ struct kvm_page_track_notifier_node *n)
+{
+ struct kvm_page_track_notifier_head *head;
+
+ head = &kvm->arch.track_notifier_head;
+
+ spin_lock(&kvm->mmu_lock);
+ hlist_del_rcu(&n->node);
+ spin_unlock(&kvm->mmu_lock);
+ synchronize_srcu(&head->track_srcu);
+}
+
+/*
+ * Notify the node that write access is intercepted and write emulation is
+ * finished at this time.
+ *
+ * The node should figure out if the written page is the one that node is
+ * interested in by itself.
+ */
+void kvm_page_track_write(struct kvm_vcpu *vcpu, gpa_t gpa, const u8 *new,
+ int bytes)
+{
+ struct kvm_page_track_notifier_head *head;
+ struct kvm_page_track_notifier_node *n;
+ int idx;
+
+ head = &vcpu->kvm->arch.track_notifier_head;
+
+ idx = srcu_read_lock(&head->track_srcu);
+ hlist_for_each_entry_rcu(n, &head->track_notifier_list, node)
+ if (n->track_write)
+ n->track_write(vcpu, gpa, new, bytes);
+ srcu_read_unlock(&head->track_srcu, idx);
+}
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index e25ebb7..98019b6 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -4370,6 +4370,7 @@ int emulator_write_phys(struct kvm_vcpu *vcpu, gpa_t gpa,
if (ret < 0)
return 0;
kvm_mmu_pte_write(vcpu, gpa, val, bytes);
+ kvm_page_track_write(vcpu, gpa, val, bytes);
return 1;
}
@@ -4628,6 +4629,7 @@ static int emulator_cmpxchg_emulated(struct x86_emulate_ctxt *ctxt,
kvm_vcpu_mark_page_dirty(vcpu, gpa >> PAGE_SHIFT);
kvm_mmu_pte_write(vcpu, gpa, new, bytes);
+ kvm_page_track_write(vcpu, gpa, new, bytes);
return X86EMUL_CONTINUE;
@@ -7748,6 +7750,8 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
INIT_DELAYED_WORK(&kvm->arch.kvmclock_update_work, kvmclock_update_fn);
INIT_DELAYED_WORK(&kvm->arch.kvmclock_sync_work, kvmclock_sync_fn);
+ kvm_page_track_init(kvm);
+
return 0;
}
--
1.8.3.1
[toc] | [prev] | [next] | [standalone]
| From | Paolo Bonzini <pbonzini@redhat.com> |
|---|---|
| Date | 2016-02-19 13:00 +0100 |
| Subject | Re: [PATCH v3 07/11] KVM: page track: add notifier support |
| Message-ID | <r3RT4-4A8-21@gated-at.bofh.it> |
| In reply to | #1333493 |
On 14/02/2016 12:31, Xiao Guangrong wrote:
> Notifier list is introduced so that any node wants to receive the track
> event can register to the list
>
> Two APIs are introduced here:
> - kvm_page_track_register_notifier(): register the notifier to receive
> track event
>
> - kvm_page_track_unregister_notifier(): stop receiving track event by
> unregister the notifier
>
> The callback, node->track_write() is called when a write access on the
> write tracked page happens
>
> Signed-off-by: Xiao Guangrong <guangrong.xiao@linux.intel.com>
> ---
> arch/x86/include/asm/kvm_host.h | 1 +
> arch/x86/include/asm/kvm_page_track.h | 39 ++++++++++++++++++++
> arch/x86/kvm/page_track.c | 67 +++++++++++++++++++++++++++++++++++
> arch/x86/kvm/x86.c | 4 +++
> 4 files changed, 111 insertions(+)
>
> diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
> index d8931d0..282bc2f 100644
> --- a/arch/x86/include/asm/kvm_host.h
> +++ b/arch/x86/include/asm/kvm_host.h
> @@ -696,6 +696,7 @@ struct kvm_arch {
> */
> struct list_head active_mmu_pages;
> struct list_head zapped_obsolete_pages;
> + struct kvm_page_track_notifier_head track_notifier_head;
>
> struct list_head assigned_dev_head;
> struct iommu_domain *iommu_domain;
> diff --git a/arch/x86/include/asm/kvm_page_track.h b/arch/x86/include/asm/kvm_page_track.h
> index 97ac9c3..1aae4ef 100644
> --- a/arch/x86/include/asm/kvm_page_track.h
> +++ b/arch/x86/include/asm/kvm_page_track.h
> @@ -6,6 +6,36 @@ enum kvm_page_track_mode {
> KVM_PAGE_TRACK_MAX,
> };
>
> +/*
> + * The notifier represented by @kvm_page_track_notifier_node is linked into
> + * the head which will be notified when guest is triggering the track event.
> + *
> + * Write access on the head is protected by kvm->mmu_lock, read access
> + * is protected by track_srcu.
> + */
> +struct kvm_page_track_notifier_head {
> + struct srcu_struct track_srcu;
> + struct hlist_head track_notifier_list;
> +};
> +
> +struct kvm_page_track_notifier_node {
> + struct hlist_node node;
> +
> + /*
> + * It is called when guest is writing the write-tracked page
> + * and write emulation is finished at that time.
> + *
> + * @vcpu: the vcpu where the write access happened.
> + * @gpa: the physical address written by guest.
> + * @new: the data was written to the address.
> + * @bytes: the written length.
> + */
> + void (*track_write)(struct kvm_vcpu *vcpu, gpa_t gpa, const u8 *new,
> + int bytes);
> +};
> +
> +void kvm_page_track_init(struct kvm *kvm);
> +
> void kvm_page_track_free_memslot(struct kvm_memory_slot *free,
> struct kvm_memory_slot *dont);
> int kvm_page_track_create_memslot(struct kvm_memory_slot *slot,
> @@ -25,4 +55,13 @@ void kvm_page_track_remove_page(struct kvm *kvm, gfn_t gfn,
> enum kvm_page_track_mode mode);
> bool kvm_page_track_check_mode(struct kvm_vcpu *vcpu, gfn_t gfn,
> enum kvm_page_track_mode mode);
> +
> +void
> +kvm_page_track_register_notifier(struct kvm *kvm,
> + struct kvm_page_track_notifier_node *n);
> +void
> +kvm_page_track_unregister_notifier(struct kvm *kvm,
> + struct kvm_page_track_notifier_node *n);
> +void kvm_page_track_write(struct kvm_vcpu *vcpu, gpa_t gpa, const u8 *new,
> + int bytes);
> #endif
> diff --git a/arch/x86/kvm/page_track.c b/arch/x86/kvm/page_track.c
> index de9b32f..0692cc6 100644
> --- a/arch/x86/kvm/page_track.c
> +++ b/arch/x86/kvm/page_track.c
> @@ -188,3 +188,70 @@ bool kvm_page_track_check_mode(struct kvm_vcpu *vcpu, gfn_t gfn,
>
> return !!ACCESS_ONCE(slot->arch.gfn_track[mode][index]);
> }
> +
> +void kvm_page_track_init(struct kvm *kvm)
> +{
> + struct kvm_page_track_notifier_head *head;
> +
> + head = &kvm->arch.track_notifier_head;
> + init_srcu_struct(&head->track_srcu);
> + INIT_HLIST_HEAD(&head->track_notifier_list);
> +}
> +
> +/*
> + * register the notifier so that event interception for the tracked guest
> + * pages can be received.
> + */
> +void
> +kvm_page_track_register_notifier(struct kvm *kvm,
> + struct kvm_page_track_notifier_node *n)
> +{
> + struct kvm_page_track_notifier_head *head;
> +
> + head = &kvm->arch.track_notifier_head;
> +
> + spin_lock(&kvm->mmu_lock);
> + hlist_add_head_rcu(&n->node, &head->track_notifier_list);
> + spin_unlock(&kvm->mmu_lock);
> +}
> +
> +/*
> + * stop receiving the event interception. It is the opposed operation of
> + * kvm_page_track_register_notifier().
> + */
> +void
> +kvm_page_track_unregister_notifier(struct kvm *kvm,
> + struct kvm_page_track_notifier_node *n)
> +{
> + struct kvm_page_track_notifier_head *head;
> +
> + head = &kvm->arch.track_notifier_head;
> +
> + spin_lock(&kvm->mmu_lock);
> + hlist_del_rcu(&n->node);
> + spin_unlock(&kvm->mmu_lock);
> + synchronize_srcu(&head->track_srcu);
> +}
> +
> +/*
> + * Notify the node that write access is intercepted and write emulation is
> + * finished at this time.
> + *
> + * The node should figure out if the written page is the one that node is
> + * interested in by itself.
> + */
> +void kvm_page_track_write(struct kvm_vcpu *vcpu, gpa_t gpa, const u8 *new,
> + int bytes)
> +{
> + struct kvm_page_track_notifier_head *head;
> + struct kvm_page_track_notifier_node *n;
> + int idx;
> +
> + head = &vcpu->kvm->arch.track_notifier_head;
Please check outside SRCU if the notifier list is empty. If so, there
is no need to do the (relatively) expensive srcu_read_lock/unlock.
Paolo
> + idx = srcu_read_lock(&head->track_srcu);
> + hlist_for_each_entry_rcu(n, &head->track_notifier_list, node)
> + if (n->track_write)
> + n->track_write(vcpu, gpa, new, bytes);
> + srcu_read_unlock(&head->track_srcu, idx);
> +}
> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index e25ebb7..98019b6 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
> @@ -4370,6 +4370,7 @@ int emulator_write_phys(struct kvm_vcpu *vcpu, gpa_t gpa,
> if (ret < 0)
> return 0;
A kvm_vcpu_mark_page_dirty is missing here, isn't it? I can take care
of it, but it would be great if you double-checked this. If so, that
should be fixed in stable kernels too.
Can you add a kvm_vcpu_note_page_write(vcpu, gpa, val, bytes) function
that takes care of calling kvm_vcpu_mark_page_dirty, kvm_mmu_pte_write
and kvm_page_track-write?
Thanks,
Paolo
> kvm_mmu_pte_write(vcpu, gpa, val, bytes);
> + kvm_page_track_write(vcpu, gpa, val, bytes);
> return 1;
> }
>
> @@ -4628,6 +4629,7 @@ static int emulator_cmpxchg_emulated(struct x86_emulate_ctxt *ctxt,
>
> kvm_vcpu_mark_page_dirty(vcpu, gpa >> PAGE_SHIFT);
> kvm_mmu_pte_write(vcpu, gpa, new, bytes);
> + kvm_page_track_write(vcpu, gpa, new, bytes);
>
> return X86EMUL_CONTINUE;
>
> @@ -7748,6 +7750,8 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
> INIT_DELAYED_WORK(&kvm->arch.kvmclock_update_work, kvmclock_update_fn);
> INIT_DELAYED_WORK(&kvm->arch.kvmclock_sync_work, kvmclock_sync_fn);
>
> + kvm_page_track_init(kvm);
> +
> return 0;
> }
>
>
[toc] | [prev] | [next] | [standalone]
| From | Xiao Guangrong <guangrong.xiao@linux.intel.com> |
|---|---|
| Date | 2016-02-14 12:50 +0100 |
| Subject | [PATCH v3 04/11] KVM: page track: add the framework of guest page tracking |
| Message-ID | <r23lE-25X-23@gated-at.bofh.it> |
| In reply to | #1333492 |
The array, gfn_track[mode][gfn], is introduced in memory slot for every
guest page, this is the tracking count for the gust page on different
modes. If the page is tracked then the count is increased, the page is
not tracked after the count reaches zero
We use 'unsigned short' as the tracking count which should be enough as
shadow page table only can use 2^14 (2^3 for level, 2^1 for cr4_pae, 2^2
for quadrant, 2^3 for access, 2^1 for nxe, 2^1 for cr0_wp, 2^1 for
smep_andnot_wp, 2^1 for smap_andnot_wp, and 2^1 for smm) at most, there
is enough room for other trackers
Two callbacks, kvm_page_track_create_memslot() and
kvm_page_track_free_memslot() are implemented in this patch, they are
internally used to initialize and reclaim the memory of the array
Currently, only write track mode is supported
Signed-off-by: Xiao Guangrong <guangrong.xiao@linux.intel.com>
---
arch/x86/include/asm/kvm_host.h | 2 ++
arch/x86/include/asm/kvm_page_track.h | 13 +++++++++
arch/x86/kvm/Makefile | 3 +-
arch/x86/kvm/page_track.c | 52 +++++++++++++++++++++++++++++++++++
arch/x86/kvm/x86.c | 5 ++++
5 files changed, 74 insertions(+), 1 deletion(-)
create mode 100644 arch/x86/include/asm/kvm_page_track.h
create mode 100644 arch/x86/kvm/page_track.c
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index e1c1f57..d8931d0 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -32,6 +32,7 @@
#include <asm/mtrr.h>
#include <asm/msr-index.h>
#include <asm/asm.h>
+#include <asm/kvm_page_track.h>
#define KVM_MAX_VCPUS 255
#define KVM_SOFT_MAX_VCPUS 160
@@ -650,6 +651,7 @@ struct kvm_lpage_info {
struct kvm_arch_memory_slot {
struct kvm_rmap_head *rmap[KVM_NR_PAGE_SIZES];
struct kvm_lpage_info *lpage_info[KVM_NR_PAGE_SIZES - 1];
+ unsigned short *gfn_track[KVM_PAGE_TRACK_MAX];
};
/*
diff --git a/arch/x86/include/asm/kvm_page_track.h b/arch/x86/include/asm/kvm_page_track.h
new file mode 100644
index 0000000..55200406
--- /dev/null
+++ b/arch/x86/include/asm/kvm_page_track.h
@@ -0,0 +1,13 @@
+#ifndef _ASM_X86_KVM_PAGE_TRACK_H
+#define _ASM_X86_KVM_PAGE_TRACK_H
+
+enum kvm_page_track_mode {
+ KVM_PAGE_TRACK_WRITE,
+ KVM_PAGE_TRACK_MAX,
+};
+
+void kvm_page_track_free_memslot(struct kvm_memory_slot *free,
+ struct kvm_memory_slot *dont);
+int kvm_page_track_create_memslot(struct kvm_memory_slot *slot,
+ unsigned long npages);
+#endif
diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile
index a1ff508..464fa47 100644
--- a/arch/x86/kvm/Makefile
+++ b/arch/x86/kvm/Makefile
@@ -13,9 +13,10 @@ kvm-$(CONFIG_KVM_ASYNC_PF) += $(KVM)/async_pf.o
kvm-y += x86.o mmu.o emulate.o i8259.o irq.o lapic.o \
i8254.o ioapic.o irq_comm.o cpuid.o pmu.o mtrr.o \
- hyperv.o
+ hyperv.o page_track.o
kvm-$(CONFIG_KVM_DEVICE_ASSIGNMENT) += assigned-dev.o iommu.o
+
kvm-intel-y += vmx.o pmu_intel.o
kvm-amd-y += svm.o pmu_amd.o
diff --git a/arch/x86/kvm/page_track.c b/arch/x86/kvm/page_track.c
new file mode 100644
index 0000000..8c396d0
--- /dev/null
+++ b/arch/x86/kvm/page_track.c
@@ -0,0 +1,52 @@
+/*
+ * Support KVM gust page tracking
+ *
+ * This feature allows us to track page access in guest. Currently, only
+ * write access is tracked.
+ *
+ * Copyright(C) 2015 Intel Corporation.
+ *
+ * Author:
+ * Xiao Guangrong <guangrong.xiao@linux.intel.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include <linux/kvm_host.h>
+#include <asm/kvm_host.h>
+#include <asm/kvm_page_track.h>
+
+#include "mmu.h"
+
+void kvm_page_track_free_memslot(struct kvm_memory_slot *free,
+ struct kvm_memory_slot *dont)
+{
+ int i;
+
+ for (i = 0; i < KVM_PAGE_TRACK_MAX; i++)
+ if (!dont || free->arch.gfn_track[i] !=
+ dont->arch.gfn_track[i]) {
+ kvfree(free->arch.gfn_track[i]);
+ free->arch.gfn_track[i] = NULL;
+ }
+}
+
+int kvm_page_track_create_memslot(struct kvm_memory_slot *slot,
+ unsigned long npages)
+{
+ int i;
+
+ for (i = 0; i < KVM_PAGE_TRACK_MAX; i++) {
+ slot->arch.gfn_track[i] = kvm_kvzalloc(npages *
+ sizeof(*slot->arch.gfn_track[i]));
+ if (!slot->arch.gfn_track[i])
+ goto track_free;
+ }
+
+ return 0;
+
+track_free:
+ kvm_page_track_free_memslot(slot, NULL);
+ return -ENOMEM;
+}
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index f448e64..e25ebb7 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -7895,6 +7895,8 @@ void kvm_arch_free_memslot(struct kvm *kvm, struct kvm_memory_slot *free,
free->arch.lpage_info[i - 1] = NULL;
}
}
+
+ kvm_page_track_free_memslot(free, dont);
}
int kvm_arch_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot,
@@ -7943,6 +7945,9 @@ int kvm_arch_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot,
}
}
+ if (kvm_page_track_create_memslot(slot, npages))
+ goto out_free;
+
return 0;
out_free:
--
1.8.3.1
[toc] | [prev] | [next] | [standalone]
| From | Paolo Bonzini <pbonzini@redhat.com> |
|---|---|
| Date | 2016-02-19 12:30 +0100 |
| Subject | Re: [PATCH v3 04/11] KVM: page track: add the framework of guest page tracking |
| Message-ID | <r3Rq2-4mn-19@gated-at.bofh.it> |
| In reply to | #1333494 |
On 14/02/2016 12:31, Xiao Guangrong wrote:
> The array, gfn_track[mode][gfn], is introduced in memory slot for every
> guest page, this is the tracking count for the gust page on different
> modes. If the page is tracked then the count is increased, the page is
> not tracked after the count reaches zero
>
> We use 'unsigned short' as the tracking count which should be enough as
> shadow page table only can use 2^14 (2^3 for level, 2^1 for cr4_pae, 2^2
> for quadrant, 2^3 for access, 2^1 for nxe, 2^1 for cr0_wp, 2^1 for
> smep_andnot_wp, 2^1 for smap_andnot_wp, and 2^1 for smm) at most, there
> is enough room for other trackers
>
> Two callbacks, kvm_page_track_create_memslot() and
> kvm_page_track_free_memslot() are implemented in this patch, they are
> internally used to initialize and reclaim the memory of the array
>
> Currently, only write track mode is supported
>
> Signed-off-by: Xiao Guangrong <guangrong.xiao@linux.intel.com>
> ---
> arch/x86/include/asm/kvm_host.h | 2 ++
> arch/x86/include/asm/kvm_page_track.h | 13 +++++++++
> arch/x86/kvm/Makefile | 3 +-
> arch/x86/kvm/page_track.c | 52 +++++++++++++++++++++++++++++++++++
> arch/x86/kvm/x86.c | 5 ++++
> 5 files changed, 74 insertions(+), 1 deletion(-)
> create mode 100644 arch/x86/include/asm/kvm_page_track.h
> create mode 100644 arch/x86/kvm/page_track.c
>
> diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
> index e1c1f57..d8931d0 100644
> --- a/arch/x86/include/asm/kvm_host.h
> +++ b/arch/x86/include/asm/kvm_host.h
> @@ -32,6 +32,7 @@
> #include <asm/mtrr.h>
> #include <asm/msr-index.h>
> #include <asm/asm.h>
> +#include <asm/kvm_page_track.h>
>
> #define KVM_MAX_VCPUS 255
> #define KVM_SOFT_MAX_VCPUS 160
> @@ -650,6 +651,7 @@ struct kvm_lpage_info {
> struct kvm_arch_memory_slot {
> struct kvm_rmap_head *rmap[KVM_NR_PAGE_SIZES];
> struct kvm_lpage_info *lpage_info[KVM_NR_PAGE_SIZES - 1];
> + unsigned short *gfn_track[KVM_PAGE_TRACK_MAX];
Please add a comment at struct kvm_mmu_page_role mentioning that the
number of role bits for shadow pages (i.e. not counting direct and
invalid) must not exceed 15 (16 thoretically risks overflow already!),
and counting the 14 bits that are in use.
Paolo
> };
>
> /*
> diff --git a/arch/x86/include/asm/kvm_page_track.h b/arch/x86/include/asm/kvm_page_track.h
> new file mode 100644
> index 0000000..55200406
> --- /dev/null
> +++ b/arch/x86/include/asm/kvm_page_track.h
> @@ -0,0 +1,13 @@
> +#ifndef _ASM_X86_KVM_PAGE_TRACK_H
> +#define _ASM_X86_KVM_PAGE_TRACK_H
> +
> +enum kvm_page_track_mode {
> + KVM_PAGE_TRACK_WRITE,
> + KVM_PAGE_TRACK_MAX,
> +};
> +
> +void kvm_page_track_free_memslot(struct kvm_memory_slot *free,
> + struct kvm_memory_slot *dont);
> +int kvm_page_track_create_memslot(struct kvm_memory_slot *slot,
> + unsigned long npages);
> +#endif
> diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile
> index a1ff508..464fa47 100644
> --- a/arch/x86/kvm/Makefile
> +++ b/arch/x86/kvm/Makefile
> @@ -13,9 +13,10 @@ kvm-$(CONFIG_KVM_ASYNC_PF) += $(KVM)/async_pf.o
>
> kvm-y += x86.o mmu.o emulate.o i8259.o irq.o lapic.o \
> i8254.o ioapic.o irq_comm.o cpuid.o pmu.o mtrr.o \
> - hyperv.o
> + hyperv.o page_track.o
>
> kvm-$(CONFIG_KVM_DEVICE_ASSIGNMENT) += assigned-dev.o iommu.o
> +
> kvm-intel-y += vmx.o pmu_intel.o
> kvm-amd-y += svm.o pmu_amd.o
>
> diff --git a/arch/x86/kvm/page_track.c b/arch/x86/kvm/page_track.c
> new file mode 100644
> index 0000000..8c396d0
> --- /dev/null
> +++ b/arch/x86/kvm/page_track.c
> @@ -0,0 +1,52 @@
> +/*
> + * Support KVM gust page tracking
> + *
> + * This feature allows us to track page access in guest. Currently, only
> + * write access is tracked.
> + *
> + * Copyright(C) 2015 Intel Corporation.
> + *
> + * Author:
> + * Xiao Guangrong <guangrong.xiao@linux.intel.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2. See
> + * the COPYING file in the top-level directory.
> + */
> +
> +#include <linux/kvm_host.h>
> +#include <asm/kvm_host.h>
> +#include <asm/kvm_page_track.h>
> +
> +#include "mmu.h"
> +
> +void kvm_page_track_free_memslot(struct kvm_memory_slot *free,
> + struct kvm_memory_slot *dont)
> +{
> + int i;
> +
> + for (i = 0; i < KVM_PAGE_TRACK_MAX; i++)
> + if (!dont || free->arch.gfn_track[i] !=
> + dont->arch.gfn_track[i]) {
> + kvfree(free->arch.gfn_track[i]);
> + free->arch.gfn_track[i] = NULL;
> + }
> +}
> +
> +int kvm_page_track_create_memslot(struct kvm_memory_slot *slot,
> + unsigned long npages)
> +{
> + int i;
> +
> + for (i = 0; i < KVM_PAGE_TRACK_MAX; i++) {
> + slot->arch.gfn_track[i] = kvm_kvzalloc(npages *
> + sizeof(*slot->arch.gfn_track[i]));
> + if (!slot->arch.gfn_track[i])
> + goto track_free;
> + }
> +
> + return 0;
> +
> +track_free:
> + kvm_page_track_free_memslot(slot, NULL);
> + return -ENOMEM;
> +}
> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index f448e64..e25ebb7 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
> @@ -7895,6 +7895,8 @@ void kvm_arch_free_memslot(struct kvm *kvm, struct kvm_memory_slot *free,
> free->arch.lpage_info[i - 1] = NULL;
> }
> }
> +
> + kvm_page_track_free_memslot(free, dont);
> }
>
> int kvm_arch_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot,
> @@ -7943,6 +7945,9 @@ int kvm_arch_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot,
> }
> }
>
> + if (kvm_page_track_create_memslot(slot, npages))
> + goto out_free;
> +
> return 0;
>
> out_free:
>
[toc] | [prev] | [next] | [standalone]
| From | Xiao Guangrong <guangrong.xiao@linux.intel.com> |
|---|---|
| Date | 2016-02-14 12:50 +0100 |
| Subject | [PATCH v3 03/11] KVM: MMU: introduce kvm_mmu_slot_gfn_write_protect |
| Message-ID | <r23lE-25X-29@gated-at.bofh.it> |
| In reply to | #1333492 |
Split rmap_write_protect() and introduce the function to abstract the write
protection based on the slot
This function will be used in the later patch
Signed-off-by: Xiao Guangrong <guangrong.xiao@linux.intel.com>
---
arch/x86/kvm/mmu.c | 16 +++++++++++-----
arch/x86/kvm/mmu.h | 2 ++
2 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c
index e1bb66c..edad3c7 100644
--- a/arch/x86/kvm/mmu.c
+++ b/arch/x86/kvm/mmu.c
@@ -1336,23 +1336,29 @@ void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm,
kvm_mmu_write_protect_pt_masked(kvm, slot, gfn_offset, mask);
}
-static bool rmap_write_protect(struct kvm_vcpu *vcpu, u64 gfn)
+bool kvm_mmu_slot_gfn_write_protect(struct kvm *kvm,
+ struct kvm_memory_slot *slot, u64 gfn)
{
- struct kvm_memory_slot *slot;
struct kvm_rmap_head *rmap_head;
int i;
bool write_protected = false;
- slot = kvm_vcpu_gfn_to_memslot(vcpu, gfn);
-
for (i = PT_PAGE_TABLE_LEVEL; i <= PT_MAX_HUGEPAGE_LEVEL; ++i) {
rmap_head = __gfn_to_rmap(gfn, i, slot);
- write_protected |= __rmap_write_protect(vcpu->kvm, rmap_head, true);
+ write_protected |= __rmap_write_protect(kvm, rmap_head, true);
}
return write_protected;
}
+static bool rmap_write_protect(struct kvm_vcpu *vcpu, u64 gfn)
+{
+ struct kvm_memory_slot *slot;
+
+ slot = kvm_vcpu_gfn_to_memslot(vcpu, gfn);
+ return kvm_mmu_slot_gfn_write_protect(vcpu->kvm, slot, gfn);
+}
+
static bool kvm_zap_rmapp(struct kvm *kvm, struct kvm_rmap_head *rmap_head)
{
u64 *sptep;
diff --git a/arch/x86/kvm/mmu.h b/arch/x86/kvm/mmu.h
index de92bed..58fe98a 100644
--- a/arch/x86/kvm/mmu.h
+++ b/arch/x86/kvm/mmu.h
@@ -177,4 +177,6 @@ void kvm_zap_gfn_range(struct kvm *kvm, gfn_t gfn_start, gfn_t gfn_end);
void kvm_mmu_gfn_disallow_lpage(struct kvm_memory_slot *slot, gfn_t gfn);
void kvm_mmu_gfn_allow_lpage(struct kvm_memory_slot *slot, gfn_t gfn);
+bool kvm_mmu_slot_gfn_write_protect(struct kvm *kvm,
+ struct kvm_memory_slot *slot, u64 gfn);
#endif
--
1.8.3.1
[toc] | [prev] | [next] | [standalone]
| From | Paolo Bonzini <pbonzini@redhat.com> |
|---|---|
| Date | 2016-02-19 13:10 +0100 |
| Message-ID | <r3S2K-4Vi-23@gated-at.bofh.it> |
| In reply to | #1333492 |
On 14/02/2016 12:31, Xiao Guangrong wrote: > Changelong in v3: > - refine the code of mmu_need_write_protect() based on Huang Kai's suggestion > - rebase the patchset against current code > > Changelog in v2: > - fix a issue that the track memory of memslot is freed if we only move > the memslot or change the flags of memslot > - do not track the gfn which is not mapped in memslots > - introduce the nolock APIs at the begin of the patchset > - use 'unsigned short' as the track counter to reduce the memory and which > should be enough for shadow page table and KVMGT > > This patchset introduces the feature which allows us to track page > access in guest. Currently, only write access tracking is implemented > in this version. > > Four APIs are introduces: > - kvm_page_track_add_page(kvm, gfn, mode), single guest page @gfn is > added into the track pool of the guest instance represented by @kvm, > @mode specifies which kind of access on the @gfn is tracked > > - kvm_page_track_remove_page(kvm, gfn, mode), is the opposed operation > of kvm_page_track_add_page() which removes @gfn from the tracking pool. > gfn is no tracked after its last user is gone > > - kvm_page_track_register_notifier(kvm, n), register a notifier so that > the event triggered by page tracking will be received, at that time, > the callback of n->track_write() will be called > > - kvm_page_track_unregister_notifier(kvm, n), does the opposed operation > of kvm_page_track_register_notifier(), which unlinks the notifier and > stops receiving the tracked event > > The first user of page track is non-leaf shadow page tables as they are > always write protected. It also gains performance improvement because > page track speeds up page fault handler for the tracked pages. The > performance result of kernel building is as followings: > > before after > real 461.63 real 455.48 > user 4529.55 user 4557.88 > sys 1995.39 sys 1922.57 > > Furthermore, it is the infrastructure of other kind of shadow page table, > such as GPU shadow page table introduced in KVMGT (1) and native nested > IOMMU. > > This patch can be divided into two parts: > - patch 1 ~ patch 7, implement page tracking > - others patches apply page tracking to non-leaf shadow page table Xiao, the patches are very readable and very good. My comments are only minor. I still have a doubt: how are you going to handle invalidation of GPU shadow page tables if a device (emulated in QEMU or even vhost) does DMA to the PPGTT? Generally, this was the reason to keep stuff out of KVM and instead hook into the kernel mm subsystem (as with userfaultfd). Paolo
[toc] | [prev] | [next] | [standalone]
| From | Xiao Guangrong <guangrong.xiao@linux.intel.com> |
|---|---|
| Date | 2016-02-22 11:20 +0100 |
| Message-ID | <r4VKW-47A-19@gated-at.bofh.it> |
| In reply to | #1338130 |
On 02/19/2016 08:00 PM, Paolo Bonzini wrote:
>
>
> On 14/02/2016 12:31, Xiao Guangrong wrote:
>> Changelong in v3:
>> - refine the code of mmu_need_write_protect() based on Huang Kai's suggestion
>> - rebase the patchset against current code
>>
>> Changelog in v2:
>> - fix a issue that the track memory of memslot is freed if we only move
>> the memslot or change the flags of memslot
>> - do not track the gfn which is not mapped in memslots
>> - introduce the nolock APIs at the begin of the patchset
>> - use 'unsigned short' as the track counter to reduce the memory and which
>> should be enough for shadow page table and KVMGT
>>
>> This patchset introduces the feature which allows us to track page
>> access in guest. Currently, only write access tracking is implemented
>> in this version.
>>
>> Four APIs are introduces:
>> - kvm_page_track_add_page(kvm, gfn, mode), single guest page @gfn is
>> added into the track pool of the guest instance represented by @kvm,
>> @mode specifies which kind of access on the @gfn is tracked
>>
>> - kvm_page_track_remove_page(kvm, gfn, mode), is the opposed operation
>> of kvm_page_track_add_page() which removes @gfn from the tracking pool.
>> gfn is no tracked after its last user is gone
>>
>> - kvm_page_track_register_notifier(kvm, n), register a notifier so that
>> the event triggered by page tracking will be received, at that time,
>> the callback of n->track_write() will be called
>>
>> - kvm_page_track_unregister_notifier(kvm, n), does the opposed operation
>> of kvm_page_track_register_notifier(), which unlinks the notifier and
>> stops receiving the tracked event
>>
>> The first user of page track is non-leaf shadow page tables as they are
>> always write protected. It also gains performance improvement because
>> page track speeds up page fault handler for the tracked pages. The
>> performance result of kernel building is as followings:
>>
>> before after
>> real 461.63 real 455.48
>> user 4529.55 user 4557.88
>> sys 1995.39 sys 1922.57
>>
>> Furthermore, it is the infrastructure of other kind of shadow page table,
>> such as GPU shadow page table introduced in KVMGT (1) and native nested
>> IOMMU.
>>
>> This patch can be divided into two parts:
>> - patch 1 ~ patch 7, implement page tracking
>> - others patches apply page tracking to non-leaf shadow page table
>
> Xiao,
>
> the patches are very readable and very good. My comments are only minor.
Thank you, Paolo!
>
> I still have a doubt: how are you going to handle invalidation of GPU
> shadow page tables if a device (emulated in QEMU or even vhost) does DMA
> to the PPGTT?
I think Jike is the better one to answer this question, Jike, could you
please clarify it? :)
> Generally, this was the reason to keep stuff out of KVM
> and instead hook into the kernel mm subsystem (as with userfaultfd).
We considered it carefully but this way can not satisfy KVMGT's requirements.
The reasons i explained in the old thread (https://lkml.org/lkml/2015/12/1/516)
are:
"For the performance, shadow GPU is performance critical and requires
frequently being switched, it is not good to handle it in userspace. And
windows guest has many GPU tables and updates it frequently, that means,
we need to write protect huge number of pages which are single page based,
I am afraid userfaultfd can not handle this case efficiently.
For the functionality, userfaultfd can not fill the need of shadow page
because:
- the page is keeping readonly, userfaultfd can not fix the fault and let
the vcpu progress (write access causes writeable gup).
- the access need to be emulated, however, userfaultfd/kernel does not have
the ability to emulate the access as the access is trigged by guest, the
instruction info is stored in VMCS so that only KVM can emulate it.
- shadow page needs to be notified after the emulation is finished as it
should know the new data written to the page to update its page hierarchy.
(some hardwares lack the 'retry' ability so the shadow page table need to
reflect the table in guest at any time). "
Any idea?
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web