[RFC,59/60] drm/i915: defer pd lmem block put to worker
diff mbox series

Message ID 20200710115757.290984-60-matthew.auld@intel.com
State New
Headers show
Series
  • DG1 LMEM enabling
Related show

Commit Message

Matthew Auld July 10, 2020, 11:57 a.m. UTC
From: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>

We take mem->mm_lock inside __i915_vma_unbind to release the memory
used for the page table itself, but __i915_vma_unbind is called while
holding vm->mutex; vm->mutex is tainted by the shrinker and therefore
locks related to allocations can't be taken while holding it
(kmem_cache_alloc is called under mem->mm_lock in i915_buddy_alloc,
so mem->mm_lock is a lock managing allocations).
As a temporary WA, move the memory release to a dedicated work called
outside the vm->mutex lock. A lockless list has been used to avoid any
locking dependency.

Cc: Matthew Auld <matthew.auld@intel.com>
Signed-off-by: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Signed-off-by: Venkata Sandeep Dhanalakota <venkata.s.dhanalakota@intel.com>
---
 drivers/gpu/drm/i915/i915_buddy.h             | 10 +++++++
 drivers/gpu/drm/i915/intel_memory_region.c    | 28 ++++++++++++++++---
 drivers/gpu/drm/i915/intel_memory_region.h    |  5 ++++
 .../gpu/drm/i915/selftests/mock_gem_device.c  |  3 +-
 4 files changed, 41 insertions(+), 5 deletions(-)

Patch
diff mbox series

diff --git a/drivers/gpu/drm/i915/i915_buddy.h b/drivers/gpu/drm/i915/i915_buddy.h
index ed41f3507cdc..fb08eb99d654 100644
--- a/drivers/gpu/drm/i915/i915_buddy.h
+++ b/drivers/gpu/drm/i915/i915_buddy.h
@@ -8,6 +8,7 @@ 
 
 #include <linux/bitops.h>
 #include <linux/list.h>
+#include <linux/llist.h>
 
 struct i915_buddy_block {
 #define I915_BUDDY_HEADER_OFFSET GENMASK_ULL(63, 12)
@@ -32,6 +33,15 @@  struct i915_buddy_block {
 	 */
 	struct list_head link;
 	struct list_head tmp_link;
+
+	/*
+	 * XXX: consider moving this somewhere specific to the pd stuff. In an
+	 * ideal world we would like to keep i915_buddy as non-i915 specific as
+	 * possible and in this case the delayed freeing is only required for
+	 * our pd handling, which is only one part of our overall i915_buddy
+	 * use.
+	 */
+	struct llist_node freed;
 };
 
 #define I915_BUDDY_MAX_ORDER  I915_BUDDY_HEADER_ORDER
diff --git a/drivers/gpu/drm/i915/intel_memory_region.c b/drivers/gpu/drm/i915/intel_memory_region.c
index 6e9d0861cf8c..80d827c4973d 100644
--- a/drivers/gpu/drm/i915/intel_memory_region.c
+++ b/drivers/gpu/drm/i915/intel_memory_region.c
@@ -84,14 +84,29 @@  __intel_memory_region_put_pages_buddy(struct intel_memory_region *mem,
 	mutex_unlock(&mem->mm_lock);
 }
 
-void
-__intel_memory_region_put_block_buddy(struct i915_buddy_block *block)
+static void __intel_memory_region_put_block_work(struct work_struct *work)
 {
+	struct intel_memory_region *mem =
+		container_of(work, struct intel_memory_region, pd_put.work);
+	struct llist_node *freed = llist_del_all(&mem->pd_put.blocks);
+	struct i915_buddy_block *block;
 	struct list_head blocks;
 
 	INIT_LIST_HEAD(&blocks);
-	list_add(&block->link, &blocks);
-	__intel_memory_region_put_pages_buddy(block->private, &blocks);
+
+	llist_for_each_entry(block, freed, freed)
+		list_add(&block->link, &blocks);
+
+	__intel_memory_region_put_pages_buddy(mem, &blocks);
+}
+
+void
+__intel_memory_region_put_block_buddy(struct i915_buddy_block *block)
+{
+	struct intel_memory_region *mem = block->private;
+
+	if (llist_add(&block->freed, &mem->pd_put.blocks))
+		queue_work(mem->i915->wq, &mem->pd_put.work);
 }
 
 int
@@ -224,6 +239,8 @@  intel_memory_region_create(struct drm_i915_private *i915,
 	mem->total = size;
 	mem->avail = mem->total;
 
+	INIT_WORK(&mem->pd_put.work, __intel_memory_region_put_block_work);
+
 	mutex_init(&mem->objects.lock);
 	INIT_LIST_HEAD(&mem->objects.list);
 	INIT_LIST_HEAD(&mem->objects.purgeable);
@@ -260,6 +277,9 @@  static void __intel_memory_region_destroy(struct kref *kref)
 	struct intel_memory_region *mem =
 		container_of(kref, typeof(*mem), kref);
 
+	/* Flush any pending work items to free blocks region */
+	flush_workqueue(mem->i915->wq);
+
 	if (mem->ops->release)
 		mem->ops->release(mem);
 
diff --git a/drivers/gpu/drm/i915/intel_memory_region.h b/drivers/gpu/drm/i915/intel_memory_region.h
index e082b895afdb..e11ee974301f 100644
--- a/drivers/gpu/drm/i915/intel_memory_region.h
+++ b/drivers/gpu/drm/i915/intel_memory_region.h
@@ -83,6 +83,11 @@  struct intel_memory_region {
 	struct i915_buddy_mm mm;
 	struct mutex mm_lock;
 
+	struct {
+		struct work_struct work;
+		struct llist_head blocks;
+	} pd_put;
+
 	struct kref kref;
 
 	resource_size_t io_start;
diff --git a/drivers/gpu/drm/i915/selftests/mock_gem_device.c b/drivers/gpu/drm/i915/selftests/mock_gem_device.c
index 9a46be05425a..07838a587413 100644
--- a/drivers/gpu/drm/i915/selftests/mock_gem_device.c
+++ b/drivers/gpu/drm/i915/selftests/mock_gem_device.c
@@ -69,11 +69,12 @@  static void mock_device_release(struct drm_device *dev)
 	i915_gem_drain_freed_objects(i915);
 
 	mock_fini_ggtt(&i915->ggtt);
-	destroy_workqueue(i915->wq);
 
 	intel_gt_driver_late_release(&i915->gt);
 	intel_memory_regions_driver_release(i915);
 
+	destroy_workqueue(i915->wq);
+
 	drm_mode_config_cleanup(&i915->drm);
 
 out: