Commit b6c126a3 authored by Changbin Du's avatar Changbin Du Committed by Zhenyu Wang

drm/i915/gvt: Manage shadow pages with radix tree

We don't know how many page tables will be shadowed. It varies
considerably corresponding to guest load. Radix tree is a better
choice for us. Since Page Frame Number is used as key so most of
the bits are common.

Here is some performance data (duration in us) of looking up a
element:
Before: (aka. ppgtt_find_shadow_page)
 0.308 0.292 0.246 0.432 0.143 ... 0.311 0.225 0.382 0.199 0.325
After: (aka. intel_vgpu_find_spt_by_mfn)
 0.106 0.106 0.107 0.106 0.105 0.107 ... 0.107 0.109 0.105 0.108

This time I didn't get the early data of hash table. The data is
measured when desktop is shown.

As last change, the overall benchmark almost is not changed, but
we get better scalability.
Signed-off-by: default avatarChangbin Du <changbin.du@intel.com>
Signed-off-by: default avatarZhenyu Wang <zhenyuw@linux.intel.com>
parent e502a2af
...@@ -640,8 +640,8 @@ static void ppgtt_free_spt(struct intel_vgpu_ppgtt_spt *spt) ...@@ -640,8 +640,8 @@ static void ppgtt_free_spt(struct intel_vgpu_ppgtt_spt *spt)
dma_unmap_page(kdev, spt->shadow_page.mfn << I915_GTT_PAGE_SHIFT, 4096, dma_unmap_page(kdev, spt->shadow_page.mfn << I915_GTT_PAGE_SHIFT, 4096,
PCI_DMA_BIDIRECTIONAL); PCI_DMA_BIDIRECTIONAL);
if (!hlist_unhashed(&spt->node))
hash_del(&spt->node); radix_tree_delete(&spt->vgpu->gtt.spt_tree, spt->shadow_page.mfn);
if (spt->guest_page.oos_page) if (spt->guest_page.oos_page)
detach_oos_page(spt->vgpu, spt->guest_page.oos_page); detach_oos_page(spt->vgpu, spt->guest_page.oos_page);
...@@ -654,12 +654,14 @@ static void ppgtt_free_spt(struct intel_vgpu_ppgtt_spt *spt) ...@@ -654,12 +654,14 @@ static void ppgtt_free_spt(struct intel_vgpu_ppgtt_spt *spt)
static void ppgtt_free_all_spt(struct intel_vgpu *vgpu) static void ppgtt_free_all_spt(struct intel_vgpu *vgpu)
{ {
struct hlist_node *n;
struct intel_vgpu_ppgtt_spt *spt; struct intel_vgpu_ppgtt_spt *spt;
int i; struct radix_tree_iter iter;
void **slot;
hash_for_each_safe(vgpu->gtt.spt_hash_table, i, n, spt, node) radix_tree_for_each_slot(slot, &vgpu->gtt.spt_tree, &iter, 0) {
spt = radix_tree_deref_slot(slot);
ppgtt_free_spt(spt); ppgtt_free_spt(spt);
}
} }
static int ppgtt_handle_guest_write_page_table_bytes( static int ppgtt_handle_guest_write_page_table_bytes(
...@@ -697,16 +699,10 @@ static struct intel_vgpu_ppgtt_spt *intel_vgpu_find_spt_by_gfn( ...@@ -697,16 +699,10 @@ static struct intel_vgpu_ppgtt_spt *intel_vgpu_find_spt_by_gfn(
} }
/* Find the spt by shadow page mfn. */ /* Find the spt by shadow page mfn. */
static struct intel_vgpu_ppgtt_spt *intel_vgpu_find_spt_by_mfn( static inline struct intel_vgpu_ppgtt_spt *intel_vgpu_find_spt_by_mfn(
struct intel_vgpu *vgpu, unsigned long mfn) struct intel_vgpu *vgpu, unsigned long mfn)
{ {
struct intel_vgpu_ppgtt_spt *spt; return radix_tree_lookup(&vgpu->gtt.spt_tree, mfn);
hash_for_each_possible(vgpu->gtt.spt_hash_table, spt, node, mfn) {
if (spt->shadow_page.mfn == mfn)
return spt;
}
return NULL;
} }
static int reclaim_one_ppgtt_mm(struct intel_gvt *gvt); static int reclaim_one_ppgtt_mm(struct intel_gvt *gvt);
...@@ -741,8 +737,8 @@ static struct intel_vgpu_ppgtt_spt *ppgtt_alloc_spt( ...@@ -741,8 +737,8 @@ static struct intel_vgpu_ppgtt_spt *ppgtt_alloc_spt(
0, 4096, PCI_DMA_BIDIRECTIONAL); 0, 4096, PCI_DMA_BIDIRECTIONAL);
if (dma_mapping_error(kdev, daddr)) { if (dma_mapping_error(kdev, daddr)) {
gvt_vgpu_err("fail to map dma addr\n"); gvt_vgpu_err("fail to map dma addr\n");
free_spt(spt); ret = -EINVAL;
return ERR_PTR(-EINVAL); goto err_free_spt;
} }
spt->shadow_page.vaddr = page_address(spt->shadow_page.page); spt->shadow_page.vaddr = page_address(spt->shadow_page.page);
spt->shadow_page.mfn = daddr >> I915_GTT_PAGE_SHIFT; spt->shadow_page.mfn = daddr >> I915_GTT_PAGE_SHIFT;
...@@ -755,17 +751,23 @@ static struct intel_vgpu_ppgtt_spt *ppgtt_alloc_spt( ...@@ -755,17 +751,23 @@ static struct intel_vgpu_ppgtt_spt *ppgtt_alloc_spt(
ret = intel_vgpu_register_page_track(vgpu, spt->guest_page.gfn, ret = intel_vgpu_register_page_track(vgpu, spt->guest_page.gfn,
ppgtt_write_protection_handler, spt); ppgtt_write_protection_handler, spt);
if (ret) { if (ret)
free_spt(spt); goto err_unmap_dma;
dma_unmap_page(kdev, daddr, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
return ERR_PTR(ret);
}
INIT_HLIST_NODE(&spt->node); ret = radix_tree_insert(&vgpu->gtt.spt_tree, spt->shadow_page.mfn, spt);
hash_add(vgpu->gtt.spt_hash_table, &spt->node, spt->shadow_page.mfn); if (ret)
goto err_unreg_page_track;
trace_spt_alloc(vgpu->id, spt, type, spt->shadow_page.mfn, gfn); trace_spt_alloc(vgpu->id, spt, type, spt->shadow_page.mfn, gfn);
return spt; return spt;
err_unreg_page_track:
intel_vgpu_unregister_page_track(vgpu, spt->guest_page.gfn);
err_unmap_dma:
dma_unmap_page(kdev, daddr, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
err_free_spt:
free_spt(spt);
return ERR_PTR(ret);
} }
#define pt_entry_size_shift(spt) \ #define pt_entry_size_shift(spt) \
...@@ -1994,7 +1996,7 @@ int intel_vgpu_init_gtt(struct intel_vgpu *vgpu) ...@@ -1994,7 +1996,7 @@ int intel_vgpu_init_gtt(struct intel_vgpu *vgpu)
{ {
struct intel_vgpu_gtt *gtt = &vgpu->gtt; struct intel_vgpu_gtt *gtt = &vgpu->gtt;
hash_init(gtt->spt_hash_table); INIT_RADIX_TREE(&gtt->spt_tree, GFP_KERNEL);
INIT_LIST_HEAD(&gtt->ppgtt_mm_list_head); INIT_LIST_HEAD(&gtt->ppgtt_mm_list_head);
INIT_LIST_HEAD(&gtt->oos_page_list_head); INIT_LIST_HEAD(&gtt->oos_page_list_head);
...@@ -2024,7 +2026,7 @@ static void intel_vgpu_destroy_all_ppgtt_mm(struct intel_vgpu *vgpu) ...@@ -2024,7 +2026,7 @@ static void intel_vgpu_destroy_all_ppgtt_mm(struct intel_vgpu *vgpu)
if (GEM_WARN_ON(!list_empty(&vgpu->gtt.ppgtt_mm_list_head))) if (GEM_WARN_ON(!list_empty(&vgpu->gtt.ppgtt_mm_list_head)))
gvt_err("vgpu ppgtt mm is not fully destoried\n"); gvt_err("vgpu ppgtt mm is not fully destoried\n");
if (GEM_WARN_ON(!hlist_empty(vgpu->gtt.spt_hash_table))) { if (GEM_WARN_ON(!radix_tree_empty(&vgpu->gtt.spt_tree))) {
gvt_err("Why we still has spt not freed?\n"); gvt_err("Why we still has spt not freed?\n");
ppgtt_free_all_spt(vgpu); ppgtt_free_all_spt(vgpu);
} }
......
...@@ -39,7 +39,6 @@ ...@@ -39,7 +39,6 @@
struct intel_vgpu_mm; struct intel_vgpu_mm;
#define INTEL_GVT_GTT_HASH_BITS 8
#define INTEL_GVT_INVALID_ADDR (~0UL) #define INTEL_GVT_INVALID_ADDR (~0UL)
struct intel_gvt_gtt_entry { struct intel_gvt_gtt_entry {
...@@ -186,7 +185,7 @@ struct intel_vgpu_gtt { ...@@ -186,7 +185,7 @@ struct intel_vgpu_gtt {
struct intel_vgpu_mm *ggtt_mm; struct intel_vgpu_mm *ggtt_mm;
unsigned long active_ppgtt_mm_bitmap; unsigned long active_ppgtt_mm_bitmap;
struct list_head ppgtt_mm_list_head; struct list_head ppgtt_mm_list_head;
DECLARE_HASHTABLE(spt_hash_table, INTEL_GVT_GTT_HASH_BITS); struct radix_tree_root spt_tree;
struct list_head oos_page_list_head; struct list_head oos_page_list_head;
struct list_head post_shadow_list_head; struct list_head post_shadow_list_head;
struct intel_vgpu_scratch_pt scratch_pt[GTT_TYPE_MAX]; struct intel_vgpu_scratch_pt scratch_pt[GTT_TYPE_MAX];
...@@ -217,7 +216,6 @@ struct intel_vgpu_oos_page { ...@@ -217,7 +216,6 @@ struct intel_vgpu_oos_page {
struct intel_vgpu_ppgtt_spt { struct intel_vgpu_ppgtt_spt {
atomic_t refcount; atomic_t refcount;
struct intel_vgpu *vgpu; struct intel_vgpu *vgpu;
struct hlist_node node;
struct { struct {
intel_gvt_gtt_type_t type; intel_gvt_gtt_type_t type;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment