Skip to content

Commit

Permalink
drm/i915/gt: Batch TLB invalidations
Browse files Browse the repository at this point in the history
commit 59eda6c upstream.

Invalidate TLB in batches, in order to reduce performance regressions.

Currently, every caller performs a full barrier around a TLB
invalidation, ignoring all other invalidations that may have already
removed their PTEs from the cache. As this is a synchronous operation
and can be quite slow, we cause multiple threads to contend on the TLB
invalidate mutex blocking userspace.

We only need to invalidate the TLB once after replacing our PTE to
ensure that there is no possible continued access to the physical
address before releasing our pages. By tracking a seqno for each full
TLB invalidate we can quickly determine if one has been performed since
rewriting the PTE, and only if necessary trigger one for ourselves.

That helps to reduce the performance regression introduced by TLB
invalidate logic.

[mchehab: rebased to not require moving the code to a separate file]

Cc: stable@vger.kernel.org
Fixes: 7938d61 ("drm/i915: Flush TLBs before releasing backing store")
Suggested-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Signed-off-by: Chris Wilson <chris.p.wilson@intel.com>
Cc: Fei Yang <fei.yang@intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
Acked-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Reviewed-by: Andi Shyti <andi.shyti@linux.intel.com>
Signed-off-by: Andi Shyti <andi.shyti@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/4e97ef5deb6739cadaaf40aa45620547e9c4ec06.1658924372.git.mchehab@kernel.org
(cherry picked from commit 5d36acb)
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
ickle-intel authored and gregkh committed Aug 25, 2022
1 parent 99a4dbc commit 26d7e3f
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 35 deletions.
3 changes: 2 additions & 1 deletion drivers/gpu/drm/i915/gem/i915_gem_object_types.h
Expand Up @@ -335,7 +335,6 @@ struct drm_i915_gem_object {
#define I915_BO_READONLY BIT(7)
#define I915_TILING_QUIRK_BIT 8 /* unknown swizzling; do not release! */
#define I915_BO_PROTECTED BIT(9)
#define I915_BO_WAS_BOUND_BIT 10
/**
* @mem_flags - Mutable placement-related flags
*
Expand Down Expand Up @@ -598,6 +597,8 @@ struct drm_i915_gem_object {
* pages were last acquired.
*/
bool dirty:1;

u32 tlb;
} mm;

struct {
Expand Down
21 changes: 13 additions & 8 deletions drivers/gpu/drm/i915/gem/i915_gem_pages.c
Expand Up @@ -191,6 +191,18 @@ static void unmap_object(struct drm_i915_gem_object *obj, void *ptr)
vunmap(ptr);
}

static void flush_tlb_invalidate(struct drm_i915_gem_object *obj)
{
struct drm_i915_private *i915 = to_i915(obj->base.dev);
struct intel_gt *gt = to_gt(i915);

if (!obj->mm.tlb)
return;

intel_gt_invalidate_tlb(gt, obj->mm.tlb);
obj->mm.tlb = 0;
}

struct sg_table *
__i915_gem_object_unset_pages(struct drm_i915_gem_object *obj)
{
Expand All @@ -216,14 +228,7 @@ __i915_gem_object_unset_pages(struct drm_i915_gem_object *obj)
__i915_gem_object_reset_page_iter(obj);
obj->mm.page_sizes.phys = obj->mm.page_sizes.sg = 0;

if (test_and_clear_bit(I915_BO_WAS_BOUND_BIT, &obj->flags)) {
struct drm_i915_private *i915 = to_i915(obj->base.dev);
struct intel_gt *gt = to_gt(i915);
intel_wakeref_t wakeref;

with_intel_gt_pm_if_awake(gt, wakeref)
intel_gt_invalidate_tlbs(gt);
}
flush_tlb_invalidate(obj);

return pages;
}
Expand Down
53 changes: 40 additions & 13 deletions drivers/gpu/drm/i915/gt/intel_gt.c
Expand Up @@ -36,8 +36,6 @@ static void __intel_gt_init_early(struct intel_gt *gt)
{
spin_lock_init(&gt->irq_lock);

mutex_init(&gt->tlb_invalidate_lock);

INIT_LIST_HEAD(&gt->closed_vma);
spin_lock_init(&gt->closed_lock);

Expand All @@ -48,6 +46,8 @@ static void __intel_gt_init_early(struct intel_gt *gt)
intel_gt_init_reset(gt);
intel_gt_init_requests(gt);
intel_gt_init_timelines(gt);
mutex_init(&gt->tlb.invalidate_lock);
seqcount_mutex_init(&gt->tlb.seqno, &gt->tlb.invalidate_lock);
intel_gt_pm_init_early(gt);

intel_uc_init_early(&gt->uc);
Expand Down Expand Up @@ -833,6 +833,7 @@ void intel_gt_driver_late_release_all(struct drm_i915_private *i915)
intel_gt_fini_requests(gt);
intel_gt_fini_reset(gt);
intel_gt_fini_timelines(gt);
mutex_destroy(&gt->tlb.invalidate_lock);
intel_engines_free(gt);
}
}
Expand Down Expand Up @@ -1165,7 +1166,7 @@ get_reg_and_bit(const struct intel_engine_cs *engine, const bool gen8,
return rb;
}

void intel_gt_invalidate_tlbs(struct intel_gt *gt)
static void mmio_invalidate_full(struct intel_gt *gt)
{
static const i915_reg_t gen8_regs[] = {
[RENDER_CLASS] = GEN8_RTCR,
Expand All @@ -1188,12 +1189,6 @@ void intel_gt_invalidate_tlbs(struct intel_gt *gt)
const i915_reg_t *regs;
unsigned int num = 0;

if (I915_SELFTEST_ONLY(gt->awake == -ENODEV))
return;

if (intel_gt_is_wedged(gt))
return;

if (GRAPHICS_VER(i915) == 12) {
regs = gen12_regs;
num = ARRAY_SIZE(gen12_regs);
Expand All @@ -1208,9 +1203,6 @@ void intel_gt_invalidate_tlbs(struct intel_gt *gt)
"Platform does not implement TLB invalidation!"))
return;

GEM_TRACE("\n");

mutex_lock(&gt->tlb_invalidate_lock);
intel_uncore_forcewake_get(uncore, FORCEWAKE_ALL);

spin_lock_irq(&uncore->lock); /* serialise invalidate with GT reset */
Expand All @@ -1230,6 +1222,8 @@ void intel_gt_invalidate_tlbs(struct intel_gt *gt)
awake |= engine->mask;
}

GT_TRACE(gt, "invalidated engines %08x\n", awake);

/* Wa_2207587034:tgl,dg1,rkl,adl-s,adl-p */
if (awake &&
(IS_TIGERLAKE(i915) ||
Expand Down Expand Up @@ -1269,5 +1263,38 @@ void intel_gt_invalidate_tlbs(struct intel_gt *gt)
* transitions.
*/
intel_uncore_forcewake_put_delayed(uncore, FORCEWAKE_ALL);
mutex_unlock(&gt->tlb_invalidate_lock);
}

static bool tlb_seqno_passed(const struct intel_gt *gt, u32 seqno)
{
u32 cur = intel_gt_tlb_seqno(gt);

/* Only skip if a *full* TLB invalidate barrier has passed */
return (s32)(cur - ALIGN(seqno, 2)) > 0;
}

void intel_gt_invalidate_tlb(struct intel_gt *gt, u32 seqno)
{
intel_wakeref_t wakeref;

if (I915_SELFTEST_ONLY(gt->awake == -ENODEV))
return;

if (intel_gt_is_wedged(gt))
return;

if (tlb_seqno_passed(gt, seqno))
return;

with_intel_gt_pm_if_awake(gt, wakeref) {
mutex_lock(&gt->tlb.invalidate_lock);
if (tlb_seqno_passed(gt, seqno))
goto unlock;

mmio_invalidate_full(gt);

write_seqcount_invalidate(&gt->tlb.seqno);
unlock:
mutex_unlock(&gt->tlb.invalidate_lock);
}
}
12 changes: 11 additions & 1 deletion drivers/gpu/drm/i915/gt/intel_gt.h
Expand Up @@ -123,7 +123,17 @@ void intel_gt_info_print(const struct intel_gt_info *info,

void intel_gt_watchdog_work(struct work_struct *work);

void intel_gt_invalidate_tlbs(struct intel_gt *gt);
static inline u32 intel_gt_tlb_seqno(const struct intel_gt *gt)
{
return seqprop_sequence(&gt->tlb.seqno);
}

static inline u32 intel_gt_next_invalidate_tlb_full(const struct intel_gt *gt)
{
return intel_gt_tlb_seqno(gt) | 1;
}

void intel_gt_invalidate_tlb(struct intel_gt *gt, u32 seqno);

struct resource intel_pci_resource(struct pci_dev *pdev, int bar);

Expand Down
18 changes: 17 additions & 1 deletion drivers/gpu/drm/i915/gt/intel_gt_types.h
Expand Up @@ -11,6 +11,7 @@
#include <linux/llist.h>
#include <linux/mutex.h>
#include <linux/notifier.h>
#include <linux/seqlock.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/workqueue.h>
Expand Down Expand Up @@ -76,7 +77,22 @@ struct intel_gt {
struct intel_uc uc;
struct intel_gsc gsc;

struct mutex tlb_invalidate_lock;
struct {
/* Serialize global tlb invalidations */
struct mutex invalidate_lock;

/*
* Batch TLB invalidations
*
* After unbinding the PTE, we need to ensure the TLB
* are invalidated prior to releasing the physical pages.
* But we only need one such invalidation for all unbinds,
* so we track how many TLB invalidations have been
* performed since unbind the PTE and only emit an extra
* invalidate if no full barrier has been passed.
*/
seqcount_mutex_t seqno;
} tlb;

struct i915_wa_list wa_list;

Expand Down
8 changes: 6 additions & 2 deletions drivers/gpu/drm/i915/gt/intel_ppgtt.c
Expand Up @@ -206,8 +206,12 @@ void ppgtt_bind_vma(struct i915_address_space *vm,
void ppgtt_unbind_vma(struct i915_address_space *vm,
struct i915_vma_resource *vma_res)
{
if (vma_res->allocated)
vm->clear_range(vm, vma_res->start, vma_res->vma_size);
if (!vma_res->allocated)
return;

vm->clear_range(vm, vma_res->start, vma_res->vma_size);
if (vma_res->tlb)
vma_invalidate_tlb(vm, *vma_res->tlb);
}

static unsigned long pd_count(u64 size, int shift)
Expand Down
33 changes: 26 additions & 7 deletions drivers/gpu/drm/i915/i915_vma.c
Expand Up @@ -537,8 +537,6 @@ int i915_vma_bind(struct i915_vma *vma,
bind_flags);
}

set_bit(I915_BO_WAS_BOUND_BIT, &vma->obj->flags);

atomic_or(bind_flags, &vma->flags);
return 0;
}
Expand Down Expand Up @@ -1301,6 +1299,19 @@ I915_SELFTEST_EXPORT int i915_vma_get_pages(struct i915_vma *vma)
return err;
}

void vma_invalidate_tlb(struct i915_address_space *vm, u32 tlb)
{
/*
* Before we release the pages that were bound by this vma, we
* must invalidate all the TLBs that may still have a reference
* back to our physical address. It only needs to be done once,
* so after updating the PTE to point away from the pages, record
* the most recent TLB invalidation seqno, and if we have not yet
* flushed the TLBs upon release, perform a full invalidation.
*/
WRITE_ONCE(tlb, intel_gt_next_invalidate_tlb_full(vm->gt));
}

static void __vma_put_pages(struct i915_vma *vma, unsigned int count)
{
/* We allocate under vma_get_pages, so beware the shrinker */
Expand Down Expand Up @@ -1927,18 +1938,26 @@ struct dma_fence *__i915_vma_evict(struct i915_vma *vma, bool async)
vma->vm->skip_pte_rewrite;
trace_i915_vma_unbind(vma);

unbind_fence = i915_vma_resource_unbind(vma_res);
if (async)
unbind_fence = i915_vma_resource_unbind(vma_res,
&vma->obj->mm.tlb);
else
unbind_fence = i915_vma_resource_unbind(vma_res, NULL);

vma->resource = NULL;

atomic_and(~(I915_VMA_BIND_MASK | I915_VMA_ERROR | I915_VMA_GGTT_WRITE),
&vma->flags);

i915_vma_detach(vma);

if (!async && unbind_fence) {
dma_fence_wait(unbind_fence, false);
dma_fence_put(unbind_fence);
unbind_fence = NULL;
if (!async) {
if (unbind_fence) {
dma_fence_wait(unbind_fence, false);
dma_fence_put(unbind_fence);
unbind_fence = NULL;
}
vma_invalidate_tlb(vma->vm, vma->obj->mm.tlb);
}

/*
Expand Down
1 change: 1 addition & 0 deletions drivers/gpu/drm/i915/i915_vma.h
Expand Up @@ -213,6 +213,7 @@ bool i915_vma_misplaced(const struct i915_vma *vma,
u64 size, u64 alignment, u64 flags);
void __i915_vma_set_map_and_fenceable(struct i915_vma *vma);
void i915_vma_revoke_mmap(struct i915_vma *vma);
void vma_invalidate_tlb(struct i915_address_space *vm, u32 tlb);
struct dma_fence *__i915_vma_evict(struct i915_vma *vma, bool async);
int __i915_vma_unbind(struct i915_vma *vma);
int __must_check i915_vma_unbind(struct i915_vma *vma);
Expand Down
5 changes: 4 additions & 1 deletion drivers/gpu/drm/i915/i915_vma_resource.c
Expand Up @@ -223,10 +223,13 @@ i915_vma_resource_fence_notify(struct i915_sw_fence *fence,
* Return: A refcounted pointer to a dma-fence that signals when unbinding is
* complete.
*/
struct dma_fence *i915_vma_resource_unbind(struct i915_vma_resource *vma_res)
struct dma_fence *i915_vma_resource_unbind(struct i915_vma_resource *vma_res,
u32 *tlb)
{
struct i915_address_space *vm = vma_res->vm;

vma_res->tlb = tlb;

/* Reference for the sw fence */
i915_vma_resource_get(vma_res);

Expand Down
6 changes: 5 additions & 1 deletion drivers/gpu/drm/i915/i915_vma_resource.h
Expand Up @@ -67,6 +67,7 @@ struct i915_page_sizes {
* taken when the unbind is scheduled.
* @skip_pte_rewrite: During ggtt suspend and vm takedown pte rewriting
* needs to be skipped for unbind.
* @tlb: pointer for obj->mm.tlb, if async unbind. Otherwise, NULL
*
* The lifetime of a struct i915_vma_resource is from a binding request to
* the actual possible asynchronous unbind has completed.
Expand Down Expand Up @@ -119,6 +120,8 @@ struct i915_vma_resource {
bool immediate_unbind:1;
bool needs_wakeref:1;
bool skip_pte_rewrite:1;

u32 *tlb;
};

bool i915_vma_resource_hold(struct i915_vma_resource *vma_res,
Expand All @@ -131,7 +134,8 @@ struct i915_vma_resource *i915_vma_resource_alloc(void);

void i915_vma_resource_free(struct i915_vma_resource *vma_res);

struct dma_fence *i915_vma_resource_unbind(struct i915_vma_resource *vma_res);
struct dma_fence *i915_vma_resource_unbind(struct i915_vma_resource *vma_res,
u32 *tlb);

void __i915_vma_resource_init(struct i915_vma_resource *vma_res);

Expand Down

0 comments on commit 26d7e3f

Please sign in to comment.