@@ -2007,9 +2007,25 @@ static int i915_swizzle_info(struct seq_file *m, void *data)
return 0;
}
+static size_t gen6_ppgtt_count_pt_pages(struct i915_hw_ppgtt *ppgtt)
+{
+ struct i915_pagedir *pd = &ppgtt->pd;
+ struct i915_pagetab **pt = &pd->page_tables[0];
+ size_t cnt = 0;
+ int i;
+
+ for (i = 0; i < ppgtt->num_pd_entries; i++) {
+ if (pt[i] != ppgtt->scratch_pt)
+ cnt++;
+ }
+
+ return cnt;
+}
+
static void print_ppgtt(struct seq_file *m, struct i915_hw_ppgtt *ppgtt)
{
seq_printf(m, "pd gtt offset: 0x%08x\n", ppgtt->pd.pd_offset);
+ seq_printf(m, "\tpd pages: %zu\n", gen6_ppgtt_count_pt_pages(ppgtt));
}
static void gen8_ppgtt_info(struct seq_file *m, struct drm_device *dev, int verbose)
@@ -2081,6 +2097,8 @@ static void gen6_ppgtt_info(struct seq_file *m, struct drm_device *dev, bool ver
seq_printf(m, "PP_DIR_BASE_READ: 0x%08x\n", I915_READ(RING_PP_DIR_BASE_READ(ring)));
seq_printf(m, "PP_DIR_DCLV: 0x%08x\n", I915_READ(RING_PP_DIR_DCLV(ring)));
}
+ seq_printf(m, "ECOCHK: 0x%08x\n\n", I915_READ(GAM_ECOCHK));
+
if (dev_priv->mm.aliasing_ppgtt) {
struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt;
@@ -2098,7 +2116,6 @@ static void gen6_ppgtt_info(struct seq_file *m, struct drm_device *dev, bool ver
idr_for_each(&file_priv->context_idr, per_file_ctx,
(void *)(unsigned long)m);
}
- seq_printf(m, "ECOCHK: 0x%08x\n", I915_READ(GAM_ECOCHK));
}
static int i915_ppgtt_info(struct seq_file *m, void *data)
@@ -2588,6 +2588,13 @@ static inline bool i915_is_ggtt(struct i915_address_space *vm)
return vm == ggtt;
}
+static inline bool i915_is_aliasing_ppgtt(struct i915_address_space *vm)
+{
+ struct i915_address_space *appgtt =
+ &((struct drm_i915_private *)(vm)->dev->dev_private)->mm.aliasing_ppgtt->base;
+ return vm == appgtt;
+}
+
static inline struct i915_hw_ppgtt *
i915_vm_to_ppgtt(struct i915_address_space *vm)
{
@@ -968,10 +968,47 @@ static void gen6_ppgtt_insert_entries(struct i915_address_space *vm,
static int gen6_alloc_va_range(struct i915_address_space *vm,
uint64_t start, uint64_t length)
{
+ DECLARE_BITMAP(new_page_tables, I915_PDES_PER_PD);
+ struct drm_device *dev = vm->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct i915_hw_ppgtt *ppgtt =
container_of(vm, struct i915_hw_ppgtt, base);
struct i915_pagetab *pt;
+ const uint32_t start_save = start, length_save = length;
uint32_t pde, temp;
+ int ret;
+
+ BUG_ON(upper_32_bits(start));
+
+ bitmap_zero(new_page_tables, I915_PDES_PER_PD);
+
+ /* The allocation is done in two stages so that we can bail out with
+ * minimal amount of pain. The first stage finds new page tables that
+ * need allocation. The second stage marks use ptes within the page
+ * tables.
+ */
+ gen6_for_each_pde(pt, &ppgtt->pd, start, length, temp, pde) {
+ if (pt != ppgtt->scratch_pt) {
+ WARN_ON(bitmap_empty(pt->used_ptes, GEN6_PTES_PER_PT));
+ continue;
+ }
+
+ /* We've already allocated a page table */
+ WARN_ON(!bitmap_empty(pt->used_ptes, GEN6_PTES_PER_PT));
+
+ pt = alloc_pt_single(dev);
+ if (IS_ERR(pt)) {
+ ret = PTR_ERR(pt);
+ goto unwind_out;
+ }
+
+ ppgtt->pd.page_tables[pde] = pt;
+ set_bit(pde, new_page_tables);
+ trace_i915_pagetable_alloc(vm, pde, start, GEN6_PDE_SHIFT);
+ }
+
+ start = start_save;
+ length = length_save;
gen6_for_each_pde(pt, &ppgtt->pd, start, length, temp, pde) {
int j;
@@ -989,11 +1026,32 @@ static int gen6_alloc_va_range(struct i915_address_space *vm,
}
}
- bitmap_or(pt->used_ptes, pt->used_ptes, tmp_bitmap,
+ if (test_and_clear_bit(pde, new_page_tables))
+ gen6_map_single(&ppgtt->pd, pde, pt);
+
+ trace_i915_pagetable_map(vm, pde, pt,
+ gen6_pte_index(start),
+ gen6_pte_count(start, length),
+ GEN6_PTES_PER_PT);
+ bitmap_or(pt->used_ptes, tmp_bitmap, pt->used_ptes,
GEN6_PTES_PER_PT);
}
+ WARN_ON(!bitmap_empty(new_page_tables, I915_PDES_PER_PD));
+
+ /* Make sure write is complete before other code can use this page
+ * table. Also require for WC mapped PTEs */
+ readl(dev_priv->gtt.gsm);
+
return 0;
+
+unwind_out:
+ for_each_set_bit(pde, new_page_tables, I915_PDES_PER_PD) {
+ struct i915_pagetab *pt = ppgtt->pd.page_tables[pde];
+ ppgtt->pd.page_tables[pde] = NULL;
+ free_pt_single(pt, vm->dev);
+ }
+ return ret;
}
static void gen6_teardown_va_range(struct i915_address_space *vm,
@@ -1005,8 +1063,27 @@ static void gen6_teardown_va_range(struct i915_address_space *vm,
uint32_t pde, temp;
gen6_for_each_pde(pt, &ppgtt->pd, start, length, temp, pde) {
+
+ if (WARN(pt == ppgtt->scratch_pt,
+ "Tried to teardown scratch page vm %p. pde %u: %llx-%llx\n",
+ vm, pde, start, start + length))
+ continue;
+
+ trace_i915_pagetable_unmap(vm, pde, pt,
+ gen6_pte_index(start),
+ gen6_pte_count(start, length),
+ GEN6_PTES_PER_PT);
+
bitmap_clear(pt->used_ptes, gen6_pte_index(start),
gen6_pte_count(start, length));
+
+ if (bitmap_empty(pt->used_ptes, GEN6_PTES_PER_PT)) {
+ trace_i915_pagetable_destroy(vm, pde,
+ start & GENMASK_ULL(64, GEN6_PDE_SHIFT),
+ GEN6_PDE_SHIFT);
+ gen6_map_single(&ppgtt->pd, pde, ppgtt->scratch_pt);
+ ppgtt->pd.page_tables[pde] = ppgtt->scratch_pt;
+ }
}
}
@@ -1014,9 +1091,13 @@ static void gen6_ppgtt_free(struct i915_hw_ppgtt *ppgtt)
{
int i;
- for (i = 0; i < ppgtt->num_pd_entries; i++)
- free_pt_single(ppgtt->pd.page_tables[i], ppgtt->base.dev);
+ for (i = 0; i < ppgtt->num_pd_entries; i++) {
+ struct i915_pagetab *pt = ppgtt->pd.page_tables[i];
+ if (pt != ppgtt->scratch_pt)
+ free_pt_single(ppgtt->pd.page_tables[i], ppgtt->base.dev);
+ }
+ /* Consider putting this as part of pd free. */
free_pt_scratch(ppgtt->scratch_pt, ppgtt->base.dev);
free_pd_single(&ppgtt->pd, ppgtt->base.dev);
}
@@ -1080,7 +1161,7 @@ err_out:
return ret;
}
-static int gen6_ppgtt_alloc(struct i915_hw_ppgtt *ppgtt)
+static int gen6_ppgtt_alloc(struct i915_hw_ppgtt *ppgtt, bool preallocate_pt)
{
int ret;
@@ -1088,9 +1169,13 @@ static int gen6_ppgtt_alloc(struct i915_hw_ppgtt *ppgtt)
if (ret)
return ret;
+ if (!preallocate_pt)
+ return 0;
+
ret = alloc_pt_range(&ppgtt->pd, 0, ppgtt->num_pd_entries,
ppgtt->base.dev);
if (ret) {
+ free_pt_scratch(ppgtt->scratch_pt, ppgtt->base.dev);
drm_mm_remove_node(&ppgtt->node);
return ret;
}
@@ -1098,8 +1183,17 @@ static int gen6_ppgtt_alloc(struct i915_hw_ppgtt *ppgtt)
return 0;
}
+static void gen6_scratch_va_range(struct i915_hw_ppgtt *ppgtt,
+ uint64_t start, uint64_t length)
+{
+ struct i915_pagetab *unused;
+ uint32_t pde, temp;
+
+ gen6_for_each_pde(unused, &ppgtt->pd, start, length, temp, pde)
+ ppgtt->pd.page_tables[pde] = ppgtt->scratch_pt;
+}
-static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt)
+static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt, bool aliasing)
{
struct drm_device *dev = ppgtt->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1115,7 +1209,7 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt)
} else
BUG();
- ret = gen6_ppgtt_alloc(ppgtt);
+ ret = gen6_ppgtt_alloc(ppgtt, aliasing);
if (ret)
return ret;
@@ -1134,6 +1228,9 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt)
ppgtt->pd_addr = (gen6_gtt_pte_t __iomem*)dev_priv->gtt.gsm +
ppgtt->pd.pd_offset / sizeof(gen6_gtt_pte_t);
+ if (!aliasing)
+ gen6_scratch_va_range(ppgtt, 0, ppgtt->base.total);
+
gen6_map_page_range(dev_priv, &ppgtt->pd, 0, ppgtt->base.total);
DRM_DEBUG_DRIVER("Allocated pde space (%ldM) at GTT entry: %lx\n",
@@ -1146,7 +1243,8 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt)
return 0;
}
-static int __hw_ppgtt_init(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt)
+static int __hw_ppgtt_init(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt,
+ bool aliasing)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1154,7 +1252,7 @@ static int __hw_ppgtt_init(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt)
ppgtt->base.scratch = dev_priv->gtt.base.scratch;
if (INTEL_INFO(dev)->gen < 8)
- return gen6_ppgtt_init(ppgtt);
+ return gen6_ppgtt_init(ppgtt, aliasing);
else if (IS_GEN8(dev) || IS_GEN9(dev))
return gen8_ppgtt_init(ppgtt, dev_priv->gtt.base.total);
else
@@ -1165,7 +1263,7 @@ int i915_ppgtt_init(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt)
struct drm_i915_private *dev_priv = dev->dev_private;
int ret = 0;
- ret = __hw_ppgtt_init(dev, ppgtt);
+ ret = __hw_ppgtt_init(dev, ppgtt, false);
if (ret == 0) {
kref_init(&ppgtt->ref);
drm_mm_init(&ppgtt->base.mm, ppgtt->base.start,
@@ -1271,6 +1369,8 @@ ppgtt_bind_vma(struct i915_vma *vma,
flags |= PTE_READ_ONLY;
if (vma->vm->allocate_va_range) {
+ trace_i915_va_alloc(vma->vm, vma->node.start, vma->node.size,
+ VM_TO_TRACE_NAME(vma->vm));
ret = vma->vm->allocate_va_range(vma->vm,
vma->node.start,
vma->node.size);
@@ -1292,6 +1392,10 @@ static void ppgtt_unbind_vma(struct i915_vma *vma)
vma->obj->base.size,
true);
if (vma->vm->teardown_va_range) {
+ trace_i915_va_teardown(vma->vm,
+ vma->node.start, vma->node.size,
+ VM_TO_TRACE_NAME(vma->vm));
+
vma->vm->teardown_va_range(vma->vm,
vma->node.start, vma->node.size);
ppgtt_invalidate_tlbs(vma->vm);
@@ -1823,7 +1927,7 @@ int i915_gem_setup_global_gtt(struct drm_device *dev,
if (!ppgtt)
return -ENOMEM;
- ret = __hw_ppgtt_init(dev, ppgtt);
+ ret = __hw_ppgtt_init(dev, ppgtt, true);
if (ret != 0)
return ret;
@@ -156,6 +156,122 @@ TRACE_EVENT(i915_vma_unbind,
__entry->obj, __entry->offset, __entry->size, __entry->vm)
);
+#define VM_TO_TRACE_NAME(vm) \
+ (i915_is_ggtt(vm) ? "GGTT" : \
+ i915_is_aliasing_ppgtt(vm) ? "Aliasing PPGTT" : \
+ "Private VM")
+
+DECLARE_EVENT_CLASS(i915_va,
+ TP_PROTO(struct i915_address_space *vm, u64 start, u64 length, const char *name),
+ TP_ARGS(vm, start, length, name),
+
+ TP_STRUCT__entry(
+ __field(struct i915_address_space *, vm)
+ __field(u64, start)
+ __field(u64, end)
+ __string(name, name)
+ ),
+
+ TP_fast_assign(
+ __entry->vm = vm;
+ __entry->start = start;
+ __entry->end = start + length;
+ __assign_str(name, name);
+ ),
+
+ TP_printk("vm=%p (%s), 0x%llx-0x%llx",
+ __entry->vm, __get_str(name), __entry->start, __entry->end)
+);
+
+DEFINE_EVENT(i915_va, i915_va_alloc,
+ TP_PROTO(struct i915_address_space *vm, u64 start, u64 length, const char *name),
+ TP_ARGS(vm, start, length, name)
+);
+
+DEFINE_EVENT(i915_va, i915_va_teardown,
+ TP_PROTO(struct i915_address_space *vm, u64 start, u64 length, const char *name),
+ TP_ARGS(vm, start, length, name)
+);
+
+DECLARE_EVENT_CLASS(i915_pagetable,
+ TP_PROTO(struct i915_address_space *vm, u32 pde, u64 start, u64 pde_shift),
+ TP_ARGS(vm, pde, start, pde_shift),
+
+ TP_STRUCT__entry(
+ __field(struct i915_address_space *, vm)
+ __field(u32, pde)
+ __field(u64, start)
+ __field(u64, end)
+ ),
+
+ TP_fast_assign(
+ __entry->vm = vm;
+ __entry->pde = pde;
+ __entry->start = start;
+ __entry->end = (start + (1ULL << pde_shift)) & ~((1ULL << pde_shift)-1);
+ ),
+
+ TP_printk("vm=%p, pde=%d (0x%llx-0x%llx)",
+ __entry->vm, __entry->pde, __entry->start, __entry->end)
+);
+
+DEFINE_EVENT(i915_pagetable, i915_pagetable_alloc,
+ TP_PROTO(struct i915_address_space *vm, u32 pde, u64 start, u64 pde_shift),
+ TP_ARGS(vm, pde, start, pde_shift)
+);
+
+DEFINE_EVENT(i915_pagetable, i915_pagetable_destroy,
+ TP_PROTO(struct i915_address_space *vm, u32 pde, u64 start, u64 pde_shift),
+ TP_ARGS(vm, pde, start, pde_shift)
+);
+
+/* Avoid extra math because we only support two sizes. The format is defined by
+ * bitmap_scnprintf. Each 32 bits is 8 HEX digits followed by comma */
+#define TRACE_PT_SIZE(bits) \
+ ((((bits) == 1024) ? 288 : 144) + 1)
+
+DECLARE_EVENT_CLASS(i915_pagetable_update,
+ TP_PROTO(struct i915_address_space *vm, u32 pde,
+ struct i915_pagetab *pt, u32 first, u32 len, size_t bits),
+ TP_ARGS(vm, pde, pt, first, len, bits),
+
+ TP_STRUCT__entry(
+ __field(struct i915_address_space *, vm)
+ __field(u32, pde)
+ __field(u32, first)
+ __field(u32, last)
+ __dynamic_array(char, cur_ptes, TRACE_PT_SIZE(bits))
+ ),
+
+ TP_fast_assign(
+ __entry->vm = vm;
+ __entry->pde = pde;
+ __entry->first = first;
+ __entry->last = first + len;
+
+ bitmap_scnprintf(__get_str(cur_ptes),
+ TRACE_PT_SIZE(bits),
+ pt->used_ptes,
+ bits);
+ ),
+
+ TP_printk("vm=%p, pde=%d, updating %u:%u\t%s",
+ __entry->vm, __entry->pde, __entry->last, __entry->first,
+ __get_str(cur_ptes))
+);
+
+DEFINE_EVENT(i915_pagetable_update, i915_pagetable_map,
+ TP_PROTO(struct i915_address_space *vm, u32 pde,
+ struct i915_pagetab *pt, u32 first, u32 len, size_t bits),
+ TP_ARGS(vm, pde, pt, first, len, bits)
+);
+
+DEFINE_EVENT(i915_pagetable_update, i915_pagetable_unmap,
+ TP_PROTO(struct i915_address_space *vm, u32 pde,
+ struct i915_pagetab *pt, u32 first, u32 len, size_t bits),
+ TP_ARGS(vm, pde, pt, first, len, bits)
+);
+
TRACE_EVENT(i915_gem_object_change_domain,
TP_PROTO(struct drm_i915_gem_object *obj, u32 old_read, u32 old_write),
TP_ARGS(obj, old_read, old_write),