linux-media.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/3] dma-buf: add dma_resv_ctx for deadlock handling
@ 2019-09-18 17:55 Christian König
  2019-09-18 17:55 ` [PATCH 2/3] drm/ttm: switch ttm_execbuf_util to new drm_resv_ctx Christian König
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Christian König @ 2019-09-18 17:55 UTC (permalink / raw)
  To: dri-devel, linux-media, chris, daniel, sumit.semwal

The ww_mutex framework allows for detecting deadlocks when multiple
threads try to acquire the same set of locks in different order.

The problem is that handling those deadlocks was the burden of the user of
the ww_mutex implementation and at least some users didn't got that right
on the first try.

So introduce a new dma_resv_ctx object which can be used to
simplify the deadlock handling. This is done by tracking all locked
dma_resv objects in the context as well as the last contended object.

When a deadlock occurse we now unlock all previously locked objects and
acquire the contended lock in the slow path. After this is done -EDEADLK
is still returned to signal that all other locks now need to be
re-acquired again.

Signed-off-by: Christian König <christian.koenig@amd.com>
---
 drivers/dma-buf/Makefile       |   2 +-
 drivers/dma-buf/dma-resv-ctx.c | 108 +++++++++++++++++++++++++++++++++
 include/linux/dma-resv-ctx.h   |  68 +++++++++++++++++++++
 include/linux/dma-resv.h       |   1 +
 4 files changed, 178 insertions(+), 1 deletion(-)
 create mode 100644 drivers/dma-buf/dma-resv-ctx.c
 create mode 100644 include/linux/dma-resv-ctx.h

diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile
index 03479da06422..da7701c85de2 100644
--- a/drivers/dma-buf/Makefile
+++ b/drivers/dma-buf/Makefile
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0-only
 obj-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \
-	 dma-resv.o seqno-fence.o
+	 dma-resv.o dma-resv-ctx.o seqno-fence.o
 obj-$(CONFIG_SYNC_FILE)		+= sync_file.o
 obj-$(CONFIG_SW_SYNC)		+= sw_sync.o sync_debug.o
 obj-$(CONFIG_UDMABUF)		+= udmabuf.o
diff --git a/drivers/dma-buf/dma-resv-ctx.c b/drivers/dma-buf/dma-resv-ctx.c
new file mode 100644
index 000000000000..cad10fa6f80b
--- /dev/null
+++ b/drivers/dma-buf/dma-resv-ctx.c
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2019 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *	Christian König <christian.koenig@amd.com>
+ */
+
+#include <linux/dma-resv-ctx.h>
+
+/**
+ * dma_resv_ctx_init - initialize a reservation context
+ * @ctx: the context to initialize
+ *
+ * Start using this reservation context to lock reservation objects for update.
+ */
+void dma_resv_ctx_init(struct dma_resv_ctx *ctx)
+{
+	ww_acquire_init(&ctx->base, &reservation_ww_class);
+	init_llist_head(&ctx->locked);
+	ctx->contended = NULL;
+}
+EXPORT_SYMBOL(dma_resv_ctx_init);
+
+/**
+ * dma_resv_ctx_unlock_all - unlock all reservation objects
+ * @ctx: the context which holds the reservation objects
+ *
+ * Unlocks all reservation objects locked with this context.
+ */
+void dma_resv_ctx_unlock_all(struct dma_resv_ctx *ctx)
+{
+	struct dma_resv *obj, *next;
+
+	if (ctx->contended)
+		dma_resv_unlock(ctx->contended);
+	ctx->contended = NULL;
+
+	llist_for_each_entry_safe(obj, next, ctx->locked.first, locked)
+		ww_mutex_unlock(&obj->lock);
+	init_llist_head(&ctx->locked);
+}
+EXPORT_SYMBOL(dma_resv_ctx_unlock_all);
+
+/**
+ * dma_resv_ctx_lock - lock a reservation object with deadlock handling
+ * @ctx: the context which should be used to lock the object
+ * @obj: the object which needs to be locked
+ * @interruptible: if we should wait interruptible
+ *
+ * Use @ctx to lock the reservation object. If a deadlock is detected we backoff
+ * by releasing all locked objects and use the slow path to lock the reservation
+ * object. After successfully locking in the slow path -EDEADLK is returned to
+ * signal that all other locks must be re-taken as well.
+ */
+int dma_resv_ctx_lock(struct dma_resv_ctx *ctx, struct dma_resv *obj,
+		      bool interruptible)
+{
+	int ret = 0;
+
+	if (unlikely(ctx->contended == obj))
+		ctx->contended = NULL;
+	else if (interruptible)
+		ret = dma_resv_lock_interruptible(obj, &ctx->base);
+	else
+		ret = dma_resv_lock(obj, &ctx->base);
+
+	if (likely(!ret)) {
+		/* don't use llist_add here, we have separate locking */
+		obj->locked.next = ctx->locked.first;
+		ctx->locked.first = &obj->locked;
+		return 0;
+	}
+	if (unlikely(ret != -EDEADLK))
+		return ret;
+
+	dma_resv_ctx_unlock_all(ctx);
+
+	if (interruptible) {
+		ret = dma_resv_lock_slow_interruptible(obj, &ctx->base);
+		if (unlikely(ret))
+			return ret;
+	} else {
+		dma_resv_lock_slow(obj, &ctx->base);
+	}
+
+	ctx->contended = obj;
+	return -EDEADLK;
+}
+EXPORT_SYMBOL(dma_resv_ctx_lock);
diff --git a/include/linux/dma-resv-ctx.h b/include/linux/dma-resv-ctx.h
new file mode 100644
index 000000000000..86473de65167
--- /dev/null
+++ b/include/linux/dma-resv-ctx.h
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2019 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *	Christian König <christian.koenig@amd.com>
+ */
+
+#ifndef _LINUX_DMA_RESV_CTX_H
+#define _LINUX_DMA_RESV_CTX_H
+
+#include <linux/llist.h>
+#include <linux/dma-resv.h>
+
+/**
+ * struct dma_resv_ctx - context to lock reservation objects
+ * @base: ww_acquire_ctx used for deadlock detection
+ * @locked: list of dma_resv objects locked in this context
+ * @contended: contended dma_resv object
+ */
+struct dma_resv_ctx {
+	struct ww_acquire_ctx base;
+	struct llist_head locked;
+	struct dma_resv *contended;
+};
+
+/**
+ * dma_resv_ctx_done - wrapper for ww_acquire_done
+ * @ctx: the reservation context which is done with locking
+ */
+static inline void dma_resv_ctx_done(struct dma_resv_ctx *ctx)
+{
+	ww_acquire_done(&ctx->base);
+}
+
+/**
+ * dma_resv_ctx_fini - wrapper for ww_acquire_fini
+ * @ctx: the reservation context which is finished
+ */
+static inline void dma_resv_ctx_fini(struct dma_resv_ctx *ctx)
+{
+	ww_acquire_fini(&ctx->base);
+}
+
+void dma_resv_ctx_init(struct dma_resv_ctx *ctx);
+void dma_resv_ctx_unlock_all(struct dma_resv_ctx *ctx);
+int dma_resv_ctx_lock(struct dma_resv_ctx *ctx, struct dma_resv *obj,
+		      bool interruptible);
+
+#endif /* _LINUX_DMA_RESV_CTX_H */
diff --git a/include/linux/dma-resv.h b/include/linux/dma-resv.h
index ee50d10f052b..1267822c2669 100644
--- a/include/linux/dma-resv.h
+++ b/include/linux/dma-resv.h
@@ -71,6 +71,7 @@ struct dma_resv_list {
  */
 struct dma_resv {
 	struct ww_mutex lock;
+	struct llist_node locked;
 	seqcount_t seq;
 
 	struct dma_fence __rcu *fence_excl;
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 2/3] drm/ttm: switch ttm_execbuf_util to new drm_resv_ctx
  2019-09-18 17:55 [PATCH 1/3] dma-buf: add dma_resv_ctx for deadlock handling Christian König
@ 2019-09-18 17:55 ` Christian König
  2019-09-18 17:55 ` [PATCH 3/3] drm/gem: use new dma_resv_ctx Christian König
  2019-10-08 15:16 ` [PATCH 1/3] dma-buf: add dma_resv_ctx for deadlock handling Daniel Vetter
  2 siblings, 0 replies; 7+ messages in thread
From: Christian König @ 2019-09-18 17:55 UTC (permalink / raw)
  To: dri-devel, linux-media, chris, daniel, sumit.semwal

Change ttm_execbuf_util to use the new reservation context
object for deadlock handling.

Signed-off-by: Christian König <christian.koenig@amd.com>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu.h           |   2 +-
 .../gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c  |   4 +-
 drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c        |   4 +-
 drivers/gpu/drm/amd/amdgpu/amdgpu_csa.c       |   2 +-
 drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c       |   4 +-
 .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c |   2 +-
 drivers/gpu/drm/qxl/qxl_drv.h                 |   2 +-
 drivers/gpu/drm/qxl/qxl_release.c             |   2 +-
 drivers/gpu/drm/radeon/radeon.h               |   2 +-
 drivers/gpu/drm/radeon/radeon_gem.c           |   2 +-
 drivers/gpu/drm/radeon/radeon_object.c        |   2 +-
 drivers/gpu/drm/radeon/radeon_object.h        |   2 +-
 drivers/gpu/drm/ttm/ttm_execbuf_util.c        | 117 ++++++++----------
 drivers/gpu/drm/vmwgfx/vmwgfx_resource.c      |   8 +-
 drivers/gpu/drm/vmwgfx/vmwgfx_validation.h    |   2 +-
 include/drm/ttm/ttm_execbuf_util.h            |  13 +-
 16 files changed, 78 insertions(+), 92 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
index 8199d201b43a..0644829d990e 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
@@ -465,7 +465,7 @@ struct amdgpu_cs_parser {
 	struct drm_sched_entity	*entity;
 
 	/* buffer objects */
-	struct ww_acquire_ctx		ticket;
+	struct dma_resv_ctx		ticket;
 	struct amdgpu_bo_list		*bo_list;
 	struct amdgpu_mn		*mn;
 	struct amdgpu_bo_list_entry	vm_pd;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
index 76e3516484e7..b9bb35d1699e 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
@@ -540,7 +540,7 @@ struct bo_vm_reservation_context {
 	struct amdgpu_bo_list_entry kfd_bo; /* BO list entry for the KFD BO */
 	unsigned int n_vms;		    /* Number of VMs reserved	    */
 	struct amdgpu_bo_list_entry *vm_pd; /* Array of VM BO list entries  */
-	struct ww_acquire_ctx ticket;	    /* Reservation ticket	    */
+	struct dma_resv_ctx ticket;  /* Reservation ticket	    */
 	struct list_head list, duplicates;  /* BO lists			    */
 	struct amdgpu_sync *sync;	    /* Pointer to sync object	    */
 	bool reserved;			    /* Whether BOs are reserved	    */
@@ -1760,7 +1760,7 @@ static int validate_invalid_user_pages(struct amdkfd_process_info *process_info)
 {
 	struct amdgpu_bo_list_entry *pd_bo_list_entries;
 	struct list_head resv_list, duplicates;
-	struct ww_acquire_ctx ticket;
+	struct dma_resv_ctx ticket;
 	struct amdgpu_sync sync;
 
 	struct amdgpu_vm *peer_vm;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
index 22236d367e26..95ec965fcc2d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
@@ -1325,7 +1325,7 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p,
 	amdgpu_job_free_resources(job);
 
 	trace_amdgpu_cs_ioctl(job);
-	amdgpu_vm_bo_trace_cs(&fpriv->vm, &p->ticket);
+	amdgpu_vm_bo_trace_cs(&fpriv->vm, &p->ticket.base);
 	priority = job->base.s_priority;
 	drm_sched_entity_push_job(&job->base, entity);
 
@@ -1729,7 +1729,7 @@ int amdgpu_cs_find_mapping(struct amdgpu_cs_parser *parser,
 	*map = mapping;
 
 	/* Double check that the BO is reserved by this CS */
-	if (dma_resv_locking_ctx((*bo)->tbo.base.resv) != &parser->ticket)
+	if (dma_resv_locking_ctx((*bo)->tbo.base.resv) != &parser->ticket.base)
 		return -EINVAL;
 
 	if (!((*bo)->flags & AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS)) {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_csa.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_csa.c
index 35a8d3c96fc9..605f83046039 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_csa.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_csa.c
@@ -66,7 +66,7 @@ int amdgpu_map_static_csa(struct amdgpu_device *adev, struct amdgpu_vm *vm,
 			  struct amdgpu_bo *bo, struct amdgpu_bo_va **bo_va,
 			  uint64_t csa_addr, uint32_t size)
 {
-	struct ww_acquire_ctx ticket;
+	struct dma_resv_ctx ticket;
 	struct list_head list;
 	struct amdgpu_bo_list_entry pd;
 	struct ttm_validate_buffer csa_tv;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
index 40f673cfbbfe..b25a59c4bec6 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
@@ -162,7 +162,7 @@ void amdgpu_gem_object_close(struct drm_gem_object *obj,
 	struct amdgpu_bo_list_entry vm_pd;
 	struct list_head list, duplicates;
 	struct ttm_validate_buffer tv;
-	struct ww_acquire_ctx ticket;
+	struct dma_resv_ctx ticket;
 	struct amdgpu_bo_va *bo_va;
 	int r;
 
@@ -549,7 +549,7 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data,
 	struct amdgpu_bo_va *bo_va;
 	struct amdgpu_bo_list_entry vm_pd;
 	struct ttm_validate_buffer tv;
-	struct ww_acquire_ctx ticket;
+	struct dma_resv_ctx ticket;
 	struct list_head list, duplicates;
 	uint64_t va_flags;
 	int r = 0;
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 760af668f678..5df2ee1e10d8 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -4416,7 +4416,7 @@ static int dm_plane_helper_prepare_fb(struct drm_plane *plane,
 	struct dm_plane_state *dm_plane_state_new, *dm_plane_state_old;
 	struct list_head list;
 	struct ttm_validate_buffer tv;
-	struct ww_acquire_ctx ticket;
+	struct dma_resv_ctx ticket;
 	uint64_t tiling_flags;
 	uint32_t domain;
 	int r;
diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h
index d4051409ce64..378b2c81920a 100644
--- a/drivers/gpu/drm/qxl/qxl_drv.h
+++ b/drivers/gpu/drm/qxl/qxl_drv.h
@@ -154,7 +154,7 @@ struct qxl_release {
 	struct qxl_bo *release_bo;
 	uint32_t release_offset;
 	uint32_t surface_release_id;
-	struct ww_acquire_ctx ticket;
+	struct dma_resv_ctx ticket;
 	struct list_head bos;
 };
 
diff --git a/drivers/gpu/drm/qxl/qxl_release.c b/drivers/gpu/drm/qxl/qxl_release.c
index 312216caeea2..aa7a28795645 100644
--- a/drivers/gpu/drm/qxl/qxl_release.c
+++ b/drivers/gpu/drm/qxl/qxl_release.c
@@ -463,6 +463,6 @@ void qxl_release_fence_buffer_objects(struct qxl_release *release)
 		dma_resv_unlock(bo->base.resv);
 	}
 	spin_unlock(&glob->lru_lock);
-	ww_acquire_fini(&release->ticket);
+	dma_resv_ctx_fini(&release->ticket);
 }
 
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index de1d090df034..5aadb55731c3 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -1083,7 +1083,7 @@ struct radeon_cs_parser {
 	u32			cs_flags;
 	u32			ring;
 	s32			priority;
-	struct ww_acquire_ctx	ticket;
+	struct dma_resv_ctx	ticket;
 };
 
 static inline u32 radeon_get_ib_value(struct radeon_cs_parser *p, int idx)
diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c
index 4cf58dbbe439..c48c2fb35456 100644
--- a/drivers/gpu/drm/radeon/radeon_gem.c
+++ b/drivers/gpu/drm/radeon/radeon_gem.c
@@ -549,7 +549,7 @@ static void radeon_gem_va_update_vm(struct radeon_device *rdev,
 {
 	struct ttm_validate_buffer tv, *entry;
 	struct radeon_bo_list *vm_bos;
-	struct ww_acquire_ctx ticket;
+	struct dma_resv_ctx ticket;
 	struct list_head list;
 	unsigned domain;
 	int r;
diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c
index 2abe1eab471f..653fd7937b39 100644
--- a/drivers/gpu/drm/radeon/radeon_object.c
+++ b/drivers/gpu/drm/radeon/radeon_object.c
@@ -531,7 +531,7 @@ static u64 radeon_bo_get_threshold_for_moves(struct radeon_device *rdev)
 }
 
 int radeon_bo_list_validate(struct radeon_device *rdev,
-			    struct ww_acquire_ctx *ticket,
+			    struct dma_resv_ctx *ticket,
 			    struct list_head *head, int ring)
 {
 	struct ttm_operation_ctx ctx = { true, false };
diff --git a/drivers/gpu/drm/radeon/radeon_object.h b/drivers/gpu/drm/radeon/radeon_object.h
index d23f2ed4126e..e5638c919c98 100644
--- a/drivers/gpu/drm/radeon/radeon_object.h
+++ b/drivers/gpu/drm/radeon/radeon_object.h
@@ -141,7 +141,7 @@ extern void radeon_bo_force_delete(struct radeon_device *rdev);
 extern int radeon_bo_init(struct radeon_device *rdev);
 extern void radeon_bo_fini(struct radeon_device *rdev);
 extern int radeon_bo_list_validate(struct radeon_device *rdev,
-				   struct ww_acquire_ctx *ticket,
+				   struct dma_resv_ctx *ticket,
 				   struct list_head *head, int ring);
 extern int radeon_bo_set_tiling_flags(struct radeon_bo *bo,
 				u32 tiling_flags, u32 pitch);
diff --git a/drivers/gpu/drm/ttm/ttm_execbuf_util.c b/drivers/gpu/drm/ttm/ttm_execbuf_util.c
index 131dae8f4170..71148c83cc4f 100644
--- a/drivers/gpu/drm/ttm/ttm_execbuf_util.c
+++ b/drivers/gpu/drm/ttm/ttm_execbuf_util.c
@@ -33,16 +33,6 @@
 #include <linux/sched.h>
 #include <linux/module.h>
 
-static void ttm_eu_backoff_reservation_reverse(struct list_head *list,
-					      struct ttm_validate_buffer *entry)
-{
-	list_for_each_entry_continue_reverse(entry, list, head) {
-		struct ttm_buffer_object *bo = entry->bo;
-
-		dma_resv_unlock(bo->base.resv);
-	}
-}
-
 static void ttm_eu_del_from_lru_locked(struct list_head *list)
 {
 	struct ttm_validate_buffer *entry;
@@ -53,7 +43,7 @@ static void ttm_eu_del_from_lru_locked(struct list_head *list)
 	}
 }
 
-void ttm_eu_backoff_reservation(struct ww_acquire_ctx *ticket,
+void ttm_eu_backoff_reservation(struct dma_resv_ctx *ticket,
 				struct list_head *list)
 {
 	struct ttm_validate_buffer *entry;
@@ -71,12 +61,15 @@ void ttm_eu_backoff_reservation(struct ww_acquire_ctx *ticket,
 
 		if (list_empty(&bo->lru))
 			ttm_bo_add_to_lru(bo);
-		dma_resv_unlock(bo->base.resv);
+		if (!ticket)
+			dma_resv_unlock(bo->base.resv);
 	}
 	spin_unlock(&glob->lru_lock);
 
-	if (ticket)
-		ww_acquire_fini(ticket);
+	if (ticket) {
+		dma_resv_ctx_unlock_all(ticket);
+		dma_resv_ctx_fini(ticket);
+	}
 }
 EXPORT_SYMBOL(ttm_eu_backoff_reservation);
 
@@ -92,12 +85,12 @@ EXPORT_SYMBOL(ttm_eu_backoff_reservation);
  * buffers in different orders.
  */
 
-int ttm_eu_reserve_buffers(struct ww_acquire_ctx *ticket,
+int ttm_eu_reserve_buffers(struct dma_resv_ctx *ticket,
 			   struct list_head *list, bool intr,
 			   struct list_head *dups, bool del_lru)
 {
-	struct ttm_bo_global *glob;
 	struct ttm_validate_buffer *entry;
+	struct ttm_bo_global *glob;
 	int ret;
 
 	if (list_empty(list))
@@ -107,70 +100,46 @@ int ttm_eu_reserve_buffers(struct ww_acquire_ctx *ticket,
 	glob = entry->bo->bdev->glob;
 
 	if (ticket)
-		ww_acquire_init(ticket, &reservation_ww_class);
+		dma_resv_ctx_init(ticket);
 
+retry:
 	list_for_each_entry(entry, list, head) {
 		struct ttm_buffer_object *bo = entry->bo;
 
-		ret = __ttm_bo_reserve(bo, intr, (ticket == NULL), ticket);
-		if (!ret && unlikely(atomic_read(&bo->cpu_writers) > 0)) {
-			dma_resv_unlock(bo->base.resv);
+		if (likely(ticket)) {
+			ret = dma_resv_ctx_lock(ticket, bo->base.resv, intr);
+			if (ret == -EDEADLK)
+				goto retry;
+		} else {
+			ret = dma_resv_trylock(bo->base.resv) ? 0 : -EBUSY;
+		}
 
+		if (!ret && unlikely(atomic_read(&bo->cpu_writers) > 0)) {
+			if (!ticket)
+				dma_resv_unlock(bo->base.resv);
 			ret = -EBUSY;
 
 		} else if (ret == -EALREADY && dups) {
 			struct ttm_validate_buffer *safe = entry;
+
 			entry = list_prev_entry(entry, head);
 			list_del(&safe->head);
 			list_add(&safe->head, dups);
 			continue;
 		}
 
-		if (!ret) {
-			if (!entry->num_shared)
-				continue;
+		if (unlikely(ret))
+			goto error;
 
-			ret = dma_resv_reserve_shared(bo->base.resv,
-								entry->num_shared);
-			if (!ret)
-				continue;
-		}
-
-		/* uh oh, we lost out, drop every reservation and try
-		 * to only reserve this buffer, then start over if
-		 * this succeeds.
-		 */
-		ttm_eu_backoff_reservation_reverse(list, entry);
-
-		if (ret == -EDEADLK) {
-			if (intr) {
-				ret = dma_resv_lock_slow_interruptible(bo->base.resv,
-										 ticket);
-			} else {
-				dma_resv_lock_slow(bo->base.resv, ticket);
-				ret = 0;
-			}
-		}
+		if (!entry->num_shared)
+			continue;
 
-		if (!ret && entry->num_shared)
-			ret = dma_resv_reserve_shared(bo->base.resv,
-								entry->num_shared);
-
-		if (unlikely(ret != 0)) {
-			if (ret == -EINTR)
-				ret = -ERESTARTSYS;
-			if (ticket) {
-				ww_acquire_done(ticket);
-				ww_acquire_fini(ticket);
-			}
-			return ret;
+		ret = dma_resv_reserve_shared(bo->base.resv, entry->num_shared);
+		if (unlikely(ret)) {
+			if (!ticket)
+				dma_resv_unlock(bo->base.resv);
+			goto error;
 		}
-
-		/* move this item to the front of the list,
-		 * forces correct iteration of the loop without keeping track
-		 */
-		list_del(&entry->head);
-		list_add(&entry->head, list);
 	}
 
 	if (del_lru) {
@@ -179,10 +148,23 @@ int ttm_eu_reserve_buffers(struct ww_acquire_ctx *ticket,
 		spin_unlock(&glob->lru_lock);
 	}
 	return 0;
+
+error:
+	if (ret == -EINTR)
+		ret = -ERESTARTSYS;
+	if (ticket) {
+		dma_resv_ctx_unlock_all(ticket);
+		dma_resv_ctx_done(ticket);
+		dma_resv_ctx_fini(ticket);
+	} else {
+		list_for_each_entry_continue_reverse(entry, list, head)
+			dma_resv_unlock(entry->bo->base.resv);
+	}
+	return ret;
 }
 EXPORT_SYMBOL(ttm_eu_reserve_buffers);
 
-void ttm_eu_fence_buffer_objects(struct ww_acquire_ctx *ticket,
+void ttm_eu_fence_buffer_objects(struct dma_resv_ctx *ticket,
 				 struct list_head *list,
 				 struct dma_fence *fence)
 {
@@ -208,10 +190,13 @@ void ttm_eu_fence_buffer_objects(struct ww_acquire_ctx *ticket,
 			ttm_bo_add_to_lru(bo);
 		else
 			ttm_bo_move_to_lru_tail(bo, NULL);
-		dma_resv_unlock(bo->base.resv);
+		if (!ticket)
+			dma_resv_unlock(bo->base.resv);
 	}
 	spin_unlock(&glob->lru_lock);
-	if (ticket)
-		ww_acquire_fini(ticket);
+	if (ticket) {
+		dma_resv_ctx_unlock_all(ticket);
+		dma_resv_ctx_fini(ticket);
+	}
 }
 EXPORT_SYMBOL(ttm_eu_fence_buffer_objects);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
index 0b5472450633..2d7c5ad25359 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
@@ -443,7 +443,7 @@ void vmw_resource_unreserve(struct vmw_resource *res,
  *                  reserved and validated backup buffer.
  */
 static int
-vmw_resource_check_buffer(struct ww_acquire_ctx *ticket,
+vmw_resource_check_buffer(struct dma_resv_ctx *ticket,
 			  struct vmw_resource *res,
 			  bool interruptible,
 			  struct ttm_validate_buffer *val_buf)
@@ -535,7 +535,7 @@ int vmw_resource_reserve(struct vmw_resource *res, bool interruptible,
  * @val_buf:        Backup buffer information.
  */
 static void
-vmw_resource_backoff_reservation(struct ww_acquire_ctx *ticket,
+vmw_resource_backoff_reservation(struct dma_resv_ctx *ticket,
 				 struct ttm_validate_buffer *val_buf)
 {
 	struct list_head val_list;
@@ -558,7 +558,7 @@ vmw_resource_backoff_reservation(struct ww_acquire_ctx *ticket,
  * @res:            The resource to evict.
  * @interruptible:  Whether to wait interruptible.
  */
-static int vmw_resource_do_evict(struct ww_acquire_ctx *ticket,
+static int vmw_resource_do_evict(struct dma_resv_ctx *ticket,
 				 struct vmw_resource *res, bool interruptible)
 {
 	struct ttm_validate_buffer val_buf;
@@ -822,7 +822,7 @@ static void vmw_resource_evict_type(struct vmw_private *dev_priv,
 	struct vmw_resource *evict_res;
 	unsigned err_count = 0;
 	int ret;
-	struct ww_acquire_ctx ticket;
+	struct dma_resv_ctx ticket;
 
 	do {
 		spin_lock(&dev_priv->resource_lock);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_validation.h b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.h
index 1d2322ad6fd5..43f48df3844f 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_validation.h
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.h
@@ -77,7 +77,7 @@ struct vmw_validation_context {
 	struct list_head resource_ctx_list;
 	struct list_head bo_list;
 	struct list_head page_list;
-	struct ww_acquire_ctx ticket;
+	struct dma_resv_ctx ticket;
 	struct mutex *res_mutex;
 	unsigned int merge_dups;
 	unsigned int mem_size_left;
diff --git a/include/drm/ttm/ttm_execbuf_util.h b/include/drm/ttm/ttm_execbuf_util.h
index 7e46cc678e7e..4e86b6fd6c57 100644
--- a/include/drm/ttm/ttm_execbuf_util.h
+++ b/include/drm/ttm/ttm_execbuf_util.h
@@ -32,6 +32,7 @@
 #define _TTM_EXECBUF_UTIL_H_
 
 #include <linux/list.h>
+#include <linux/dma-resv-ctx.h>
 
 #include "ttm_bo_api.h"
 
@@ -52,20 +53,20 @@ struct ttm_validate_buffer {
 /**
  * function ttm_eu_backoff_reservation
  *
- * @ticket:   ww_acquire_ctx from reserve call
+ * @ticket:   reservation_context from reserve call
  * @list:     thread private list of ttm_validate_buffer structs.
  *
  * Undoes all buffer validation reservations for bos pointed to by
  * the list entries.
  */
 
-extern void ttm_eu_backoff_reservation(struct ww_acquire_ctx *ticket,
+extern void ttm_eu_backoff_reservation(struct dma_resv_ctx *ticket,
 				       struct list_head *list);
 
 /**
  * function ttm_eu_reserve_buffers
  *
- * @ticket:  [out] ww_acquire_ctx filled in by call, or NULL if only
+ * @ticket:  [out] reservation_context filled in by caller, or NULL if only
  *           non-blocking reserves should be tried.
  * @list:    thread private list of ttm_validate_buffer structs.
  * @intr:    should the wait be interruptible
@@ -97,14 +98,14 @@ extern void ttm_eu_backoff_reservation(struct ww_acquire_ctx *ticket,
  * has failed.
  */
 
-extern int ttm_eu_reserve_buffers(struct ww_acquire_ctx *ticket,
+extern int ttm_eu_reserve_buffers(struct dma_resv_ctx *ticket,
 				  struct list_head *list, bool intr,
 				  struct list_head *dups, bool del_lru);
 
 /**
  * function ttm_eu_fence_buffer_objects.
  *
- * @ticket:      ww_acquire_ctx from reserve call
+ * @ticket:      reservation_context from reserve call
  * @list:        thread private list of ttm_validate_buffer structs.
  * @fence:       The new exclusive fence for the buffers.
  *
@@ -114,7 +115,7 @@ extern int ttm_eu_reserve_buffers(struct ww_acquire_ctx *ticket,
  *
  */
 
-extern void ttm_eu_fence_buffer_objects(struct ww_acquire_ctx *ticket,
+extern void ttm_eu_fence_buffer_objects(struct dma_resv_ctx *ticket,
 					struct list_head *list,
 					struct dma_fence *fence);
 
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 3/3] drm/gem: use new dma_resv_ctx
  2019-09-18 17:55 [PATCH 1/3] dma-buf: add dma_resv_ctx for deadlock handling Christian König
  2019-09-18 17:55 ` [PATCH 2/3] drm/ttm: switch ttm_execbuf_util to new drm_resv_ctx Christian König
@ 2019-09-18 17:55 ` Christian König
  2019-10-08 15:16 ` [PATCH 1/3] dma-buf: add dma_resv_ctx for deadlock handling Daniel Vetter
  2 siblings, 0 replies; 7+ messages in thread
From: Christian König @ 2019-09-18 17:55 UTC (permalink / raw)
  To: dri-devel, linux-media, chris, daniel, sumit.semwal

Use the new dma_resv_ctx object instead of implementing
deadlock handling on our own.

Signed-off-by: Christian König <christian.koenig@amd.com>
---
 drivers/gpu/drm/drm_gem.c               | 62 +++++++------------------
 drivers/gpu/drm/panfrost/panfrost_job.c |  2 +-
 drivers/gpu/drm/v3d/v3d_gem.c           | 10 ++--
 drivers/gpu/drm/virtio/virtgpu_drv.h    |  2 +-
 include/drm/drm_gem.h                   |  5 +-
 5 files changed, 27 insertions(+), 54 deletions(-)

diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index 6854f5867d51..b3b684cf1e1b 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -1280,66 +1280,38 @@ void drm_gem_vunmap(struct drm_gem_object *obj, void *vaddr)
  */
 int
 drm_gem_lock_reservations(struct drm_gem_object **objs, int count,
-			  struct ww_acquire_ctx *acquire_ctx)
+			  struct dma_resv_ctx *acquire_ctx)
 {
-	int contended = -1;
 	int i, ret;
 
-	ww_acquire_init(acquire_ctx, &reservation_ww_class);
+	dma_resv_ctx_init(acquire_ctx);
 
 retry:
-	if (contended != -1) {
-		struct drm_gem_object *obj = objs[contended];
-
-		ret = dma_resv_lock_slow_interruptible(obj->resv,
-								 acquire_ctx);
-		if (ret) {
-			ww_acquire_done(acquire_ctx);
-			return ret;
-		}
-	}
-
 	for (i = 0; i < count; i++) {
-		if (i == contended)
-			continue;
-
-		ret = dma_resv_lock_interruptible(objs[i]->resv,
-							    acquire_ctx);
-		if (ret) {
-			int j;
-
-			for (j = 0; j < i; j++)
-				dma_resv_unlock(objs[j]->resv);
-
-			if (contended != -1 && contended >= i)
-				dma_resv_unlock(objs[contended]->resv);
-
-			if (ret == -EDEADLK) {
-				contended = i;
-				goto retry;
-			}
-
-			ww_acquire_done(acquire_ctx);
-			return ret;
-		}
+		ret = dma_resv_ctx_lock(acquire_ctx, objs[i]->resv, true);
+		if (ret)
+			goto error;
 	}
 
-	ww_acquire_done(acquire_ctx);
-
+	dma_resv_ctx_done(acquire_ctx);
 	return 0;
+
+error:
+	if (ret == -EDEADLK)
+		goto retry;
+
+	dma_resv_ctx_unlock_all(acquire_ctx);
+	dma_resv_ctx_done(acquire_ctx);
+	return ret;
 }
 EXPORT_SYMBOL(drm_gem_lock_reservations);
 
 void
 drm_gem_unlock_reservations(struct drm_gem_object **objs, int count,
-			    struct ww_acquire_ctx *acquire_ctx)
+			    struct dma_resv_ctx *acquire_ctx)
 {
-	int i;
-
-	for (i = 0; i < count; i++)
-		dma_resv_unlock(objs[i]->resv);
-
-	ww_acquire_fini(acquire_ctx);
+	dma_resv_ctx_unlock_all(acquire_ctx);
+	dma_resv_ctx_fini(acquire_ctx);
 }
 EXPORT_SYMBOL(drm_gem_unlock_reservations);
 
diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c
index 05c85f45a0de..b73079173c57 100644
--- a/drivers/gpu/drm/panfrost/panfrost_job.c
+++ b/drivers/gpu/drm/panfrost/panfrost_job.c
@@ -218,7 +218,7 @@ int panfrost_job_push(struct panfrost_job *job)
 	struct panfrost_device *pfdev = job->pfdev;
 	int slot = panfrost_job_get_slot(job);
 	struct drm_sched_entity *entity = &job->file_priv->sched_entity[slot];
-	struct ww_acquire_ctx acquire_ctx;
+	struct dma_resv_ctx acquire_ctx;
 	int ret = 0;
 
 	mutex_lock(&pfdev->sched_lock);
diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c
index 5d80507b539b..745570fccad9 100644
--- a/drivers/gpu/drm/v3d/v3d_gem.c
+++ b/drivers/gpu/drm/v3d/v3d_gem.c
@@ -248,7 +248,7 @@ v3d_invalidate_caches(struct v3d_dev *v3d)
  */
 static int
 v3d_lock_bo_reservations(struct v3d_job *job,
-			 struct ww_acquire_ctx *acquire_ctx)
+			 struct dma_resv_ctx *acquire_ctx)
 {
 	int i, ret;
 
@@ -486,7 +486,7 @@ v3d_push_job(struct v3d_file_priv *v3d_priv,
 static void
 v3d_attach_fences_and_unlock_reservation(struct drm_file *file_priv,
 					 struct v3d_job *job,
-					 struct ww_acquire_ctx *acquire_ctx,
+					 struct dma_resv_ctx *acquire_ctx,
 					 u32 out_sync,
 					 struct dma_fence *done_fence)
 {
@@ -530,7 +530,7 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data,
 	struct drm_v3d_submit_cl *args = data;
 	struct v3d_bin_job *bin = NULL;
 	struct v3d_render_job *render;
-	struct ww_acquire_ctx acquire_ctx;
+	struct dma_resv_ctx acquire_ctx;
 	int ret = 0;
 
 	trace_v3d_submit_cl_ioctl(&v3d->drm, args->rcl_start, args->rcl_end);
@@ -642,7 +642,7 @@ v3d_submit_tfu_ioctl(struct drm_device *dev, void *data,
 	struct v3d_file_priv *v3d_priv = file_priv->driver_priv;
 	struct drm_v3d_submit_tfu *args = data;
 	struct v3d_tfu_job *job;
-	struct ww_acquire_ctx acquire_ctx;
+	struct dma_resv_ctx acquire_ctx;
 	int ret = 0;
 
 	trace_v3d_submit_tfu_ioctl(&v3d->drm, args->iia);
@@ -738,7 +738,7 @@ v3d_submit_csd_ioctl(struct drm_device *dev, void *data,
 	struct drm_v3d_submit_csd *args = data;
 	struct v3d_csd_job *job;
 	struct v3d_job *clean_job;
-	struct ww_acquire_ctx acquire_ctx;
+	struct dma_resv_ctx acquire_ctx;
 	int ret;
 
 	trace_v3d_submit_csd_ioctl(&v3d->drm, args->cfg[5], args->cfg[6]);
diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h
index 314e02f94d9c..02b4ee1deeb4 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.h
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.h
@@ -77,7 +77,7 @@ struct virtio_gpu_object {
 	container_of((gobj), struct virtio_gpu_object, base.base)
 
 struct virtio_gpu_object_array {
-	struct ww_acquire_ctx ticket;
+	struct dma_resv_ctx ticket;
 	struct list_head next;
 	u32 nents, total;
 	struct drm_gem_object *objs[];
diff --git a/include/drm/drm_gem.h b/include/drm/drm_gem.h
index 6aaba14f5972..dff4d45a2c41 100644
--- a/include/drm/drm_gem.h
+++ b/include/drm/drm_gem.h
@@ -36,6 +36,7 @@
 
 #include <linux/kref.h>
 #include <linux/dma-resv.h>
+#include <linux/dma-resv-ctx.h>
 
 #include <drm/drm_vma_manager.h>
 
@@ -393,9 +394,9 @@ struct drm_gem_object *drm_gem_object_lookup(struct drm_file *filp, u32 handle);
 long drm_gem_dma_resv_wait(struct drm_file *filep, u32 handle,
 				    bool wait_all, unsigned long timeout);
 int drm_gem_lock_reservations(struct drm_gem_object **objs, int count,
-			      struct ww_acquire_ctx *acquire_ctx);
+			      struct dma_resv_ctx *acquire_ctx);
 void drm_gem_unlock_reservations(struct drm_gem_object **objs, int count,
-				 struct ww_acquire_ctx *acquire_ctx);
+				 struct dma_resv_ctx *acquire_ctx);
 int drm_gem_fence_array_add(struct xarray *fence_array,
 			    struct dma_fence *fence);
 int drm_gem_fence_array_add_implicit(struct xarray *fence_array,
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/3] dma-buf: add dma_resv_ctx for deadlock handling
  2019-09-18 17:55 [PATCH 1/3] dma-buf: add dma_resv_ctx for deadlock handling Christian König
  2019-09-18 17:55 ` [PATCH 2/3] drm/ttm: switch ttm_execbuf_util to new drm_resv_ctx Christian König
  2019-09-18 17:55 ` [PATCH 3/3] drm/gem: use new dma_resv_ctx Christian König
@ 2019-10-08 15:16 ` Daniel Vetter
  2019-10-09 13:21   ` Christian König
  2019-10-14  8:54   ` Gerd Hoffmann
  2 siblings, 2 replies; 7+ messages in thread
From: Daniel Vetter @ 2019-10-08 15:16 UTC (permalink / raw)
  To: Christian König, Gerd Hoffmann
  Cc: dri-devel, linux-media, chris, daniel, sumit.semwal

On Wed, Sep 18, 2019 at 07:55:23PM +0200, Christian König wrote:
> The ww_mutex framework allows for detecting deadlocks when multiple
> threads try to acquire the same set of locks in different order.
> 
> The problem is that handling those deadlocks was the burden of the user of
> the ww_mutex implementation and at least some users didn't got that right
> on the first try.
> 
> So introduce a new dma_resv_ctx object which can be used to
> simplify the deadlock handling. This is done by tracking all locked
> dma_resv objects in the context as well as the last contended object.
> 
> When a deadlock occurse we now unlock all previously locked objects and
> acquire the contended lock in the slow path. After this is done -EDEADLK
> is still returned to signal that all other locks now need to be
> re-acquired again.
> 
> Signed-off-by: Christian König <christian.koenig@amd.com>

I pondered this quite a bit, some thoughts:

- doing this over dma-buf means we can only use this for the ww_mutx
  dance, not for anything else. Which means we need another layer on top
  for shared execbuf utils (which Gerd has started looking into, but I
  felt like not quite the right approach yet in his first draft series).

- With the ttm/gem merger we could just put this into drm_gem_object, and
  ttm/gem helpers could still use it. Plus we could build some shared
  execbuf utils on top of this, by essentially merging ttm_operation_ctx
  into drm_gem_operation_ctx. I think this would also simplify the ttm
  parameters a bit, since currently the ttm_operation_ctx doesn't have an
  explicit pointer to the ww_acquire_ctx (which feels like an oversight to
  me).

- Aside, quick question: Any reason why struct amdgpu_cs_parser doesn't
  subclass ttm_operation_ctx? From my naive understanding this would make
  tons of sense ...

- Maybe we could even build some lru/eviction helpers on top, perhaps with
  two lists, one for the set of buffers in the execbuf, the other for
  additional buffers we've reserved as part of the eviction dance (to
  solve the TODO in ttm_mem_evict_wait_busy).

- Only downside would be that drivers outside of drivers/gpu won't be able
  to use these helpers. I don't see any immediate nor near-future need for
  that. All other drivers use so little memory they don't need to
  participate in the multi-lock dance, they just pin the few buffers they
  need and call it a day.

Ofc not everything above would need to be done right away, that's more
ideas for todo.rst entries to make sure we all agree on the rough
direction.

Thoughts?

Also adding Gerd Hoffmann, since he looked into this.

Cheers, Daniel
> ---
>  drivers/dma-buf/Makefile       |   2 +-
>  drivers/dma-buf/dma-resv-ctx.c | 108 +++++++++++++++++++++++++++++++++
>  include/linux/dma-resv-ctx.h   |  68 +++++++++++++++++++++
>  include/linux/dma-resv.h       |   1 +
>  4 files changed, 178 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/dma-buf/dma-resv-ctx.c
>  create mode 100644 include/linux/dma-resv-ctx.h
> 
> diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile
> index 03479da06422..da7701c85de2 100644
> --- a/drivers/dma-buf/Makefile
> +++ b/drivers/dma-buf/Makefile
> @@ -1,6 +1,6 @@
>  # SPDX-License-Identifier: GPL-2.0-only
>  obj-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \
> -	 dma-resv.o seqno-fence.o
> +	 dma-resv.o dma-resv-ctx.o seqno-fence.o
>  obj-$(CONFIG_SYNC_FILE)		+= sync_file.o
>  obj-$(CONFIG_SW_SYNC)		+= sw_sync.o sync_debug.o
>  obj-$(CONFIG_UDMABUF)		+= udmabuf.o
> diff --git a/drivers/dma-buf/dma-resv-ctx.c b/drivers/dma-buf/dma-resv-ctx.c
> new file mode 100644
> index 000000000000..cad10fa6f80b
> --- /dev/null
> +++ b/drivers/dma-buf/dma-resv-ctx.c
> @@ -0,0 +1,108 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright 2019 Advanced Micro Devices, Inc.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + *
> + * Authors:
> + *	Christian König <christian.koenig@amd.com>
> + */
> +
> +#include <linux/dma-resv-ctx.h>
> +
> +/**
> + * dma_resv_ctx_init - initialize a reservation context
> + * @ctx: the context to initialize
> + *
> + * Start using this reservation context to lock reservation objects for update.
> + */
> +void dma_resv_ctx_init(struct dma_resv_ctx *ctx)
> +{
> +	ww_acquire_init(&ctx->base, &reservation_ww_class);
> +	init_llist_head(&ctx->locked);
> +	ctx->contended = NULL;
> +}
> +EXPORT_SYMBOL(dma_resv_ctx_init);
> +
> +/**
> + * dma_resv_ctx_unlock_all - unlock all reservation objects
> + * @ctx: the context which holds the reservation objects
> + *
> + * Unlocks all reservation objects locked with this context.
> + */
> +void dma_resv_ctx_unlock_all(struct dma_resv_ctx *ctx)
> +{
> +	struct dma_resv *obj, *next;
> +
> +	if (ctx->contended)
> +		dma_resv_unlock(ctx->contended);
> +	ctx->contended = NULL;
> +
> +	llist_for_each_entry_safe(obj, next, ctx->locked.first, locked)
> +		ww_mutex_unlock(&obj->lock);
> +	init_llist_head(&ctx->locked);
> +}
> +EXPORT_SYMBOL(dma_resv_ctx_unlock_all);
> +
> +/**
> + * dma_resv_ctx_lock - lock a reservation object with deadlock handling
> + * @ctx: the context which should be used to lock the object
> + * @obj: the object which needs to be locked
> + * @interruptible: if we should wait interruptible
> + *
> + * Use @ctx to lock the reservation object. If a deadlock is detected we backoff
> + * by releasing all locked objects and use the slow path to lock the reservation
> + * object. After successfully locking in the slow path -EDEADLK is returned to
> + * signal that all other locks must be re-taken as well.
> + */
> +int dma_resv_ctx_lock(struct dma_resv_ctx *ctx, struct dma_resv *obj,
> +		      bool interruptible)
> +{
> +	int ret = 0;
> +
> +	if (unlikely(ctx->contended == obj))
> +		ctx->contended = NULL;
> +	else if (interruptible)
> +		ret = dma_resv_lock_interruptible(obj, &ctx->base);
> +	else
> +		ret = dma_resv_lock(obj, &ctx->base);
> +
> +	if (likely(!ret)) {
> +		/* don't use llist_add here, we have separate locking */
> +		obj->locked.next = ctx->locked.first;
> +		ctx->locked.first = &obj->locked;
> +		return 0;
> +	}
> +	if (unlikely(ret != -EDEADLK))
> +		return ret;
> +
> +	dma_resv_ctx_unlock_all(ctx);
> +
> +	if (interruptible) {
> +		ret = dma_resv_lock_slow_interruptible(obj, &ctx->base);
> +		if (unlikely(ret))
> +			return ret;
> +	} else {
> +		dma_resv_lock_slow(obj, &ctx->base);
> +	}
> +
> +	ctx->contended = obj;
> +	return -EDEADLK;
> +}
> +EXPORT_SYMBOL(dma_resv_ctx_lock);
> diff --git a/include/linux/dma-resv-ctx.h b/include/linux/dma-resv-ctx.h
> new file mode 100644
> index 000000000000..86473de65167
> --- /dev/null
> +++ b/include/linux/dma-resv-ctx.h
> @@ -0,0 +1,68 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright 2019 Advanced Micro Devices, Inc.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + *
> + * Authors:
> + *	Christian König <christian.koenig@amd.com>
> + */
> +
> +#ifndef _LINUX_DMA_RESV_CTX_H
> +#define _LINUX_DMA_RESV_CTX_H
> +
> +#include <linux/llist.h>
> +#include <linux/dma-resv.h>
> +
> +/**
> + * struct dma_resv_ctx - context to lock reservation objects
> + * @base: ww_acquire_ctx used for deadlock detection
> + * @locked: list of dma_resv objects locked in this context
> + * @contended: contended dma_resv object
> + */
> +struct dma_resv_ctx {
> +	struct ww_acquire_ctx base;
> +	struct llist_head locked;
> +	struct dma_resv *contended;
> +};
> +
> +/**
> + * dma_resv_ctx_done - wrapper for ww_acquire_done
> + * @ctx: the reservation context which is done with locking
> + */
> +static inline void dma_resv_ctx_done(struct dma_resv_ctx *ctx)
> +{
> +	ww_acquire_done(&ctx->base);
> +}
> +
> +/**
> + * dma_resv_ctx_fini - wrapper for ww_acquire_fini
> + * @ctx: the reservation context which is finished
> + */
> +static inline void dma_resv_ctx_fini(struct dma_resv_ctx *ctx)
> +{
> +	ww_acquire_fini(&ctx->base);
> +}
> +
> +void dma_resv_ctx_init(struct dma_resv_ctx *ctx);
> +void dma_resv_ctx_unlock_all(struct dma_resv_ctx *ctx);
> +int dma_resv_ctx_lock(struct dma_resv_ctx *ctx, struct dma_resv *obj,
> +		      bool interruptible);
> +
> +#endif /* _LINUX_DMA_RESV_CTX_H */
> diff --git a/include/linux/dma-resv.h b/include/linux/dma-resv.h
> index ee50d10f052b..1267822c2669 100644
> --- a/include/linux/dma-resv.h
> +++ b/include/linux/dma-resv.h
> @@ -71,6 +71,7 @@ struct dma_resv_list {
>   */
>  struct dma_resv {
>  	struct ww_mutex lock;
> +	struct llist_node locked;
>  	seqcount_t seq;
>  
>  	struct dma_fence __rcu *fence_excl;
> -- 
> 2.17.1
> 

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/3] dma-buf: add dma_resv_ctx for deadlock handling
  2019-10-08 15:16 ` [PATCH 1/3] dma-buf: add dma_resv_ctx for deadlock handling Daniel Vetter
@ 2019-10-09 13:21   ` Christian König
  2019-10-09 14:39     ` Daniel Vetter
  2019-10-14  8:54   ` Gerd Hoffmann
  1 sibling, 1 reply; 7+ messages in thread
From: Christian König @ 2019-10-09 13:21 UTC (permalink / raw)
  To: Daniel Vetter, Gerd Hoffmann; +Cc: dri-devel, linux-media, chris, sumit.semwal

Am 08.10.19 um 17:16 schrieb Daniel Vetter:
> On Wed, Sep 18, 2019 at 07:55:23PM +0200, Christian König wrote:
>> The ww_mutex framework allows for detecting deadlocks when multiple
>> threads try to acquire the same set of locks in different order.
>>
>> The problem is that handling those deadlocks was the burden of the user of
>> the ww_mutex implementation and at least some users didn't got that right
>> on the first try.
>>
>> So introduce a new dma_resv_ctx object which can be used to
>> simplify the deadlock handling. This is done by tracking all locked
>> dma_resv objects in the context as well as the last contended object.
>>
>> When a deadlock occurse we now unlock all previously locked objects and
>> acquire the contended lock in the slow path. After this is done -EDEADLK
>> is still returned to signal that all other locks now need to be
>> re-acquired again.
>>
>> Signed-off-by: Christian König <christian.koenig@amd.com>
> I pondered this quite a bit, some thoughts:
>
> - doing this over dma-buf means we can only use this for the ww_mutx
>    dance, not for anything else. Which means we need another layer on top
>    for shared execbuf utils (which Gerd has started looking into, but I
>    felt like not quite the right approach yet in his first draft series).

Yes, and I actually realized that this won't work on the dma_resv layer 
as well.

We need to base this on GEM to be able to do the correct ref/unref the 
locked objects.

> - With the ttm/gem merger we could just put this into drm_gem_object, and
>    ttm/gem helpers could still use it. Plus we could build some shared
>    execbuf utils on top of this, by essentially merging ttm_operation_ctx
>    into drm_gem_operation_ctx. I think this would also simplify the ttm
>    parameters a bit, since currently the ttm_operation_ctx doesn't have an
>    explicit pointer to the ww_acquire_ctx (which feels like an oversight to
>    me).

That ttm_operation_ctx doesn't contain a ww_acquire_ctx is intentional 
and mandatory for correct operation.

See for swapping things out from the MM callbacks you can't work with a 
ww_acquire_ctx and instead need to use trylock.

When you need a ww_acquire_ctx you can get that from the buffer object 
you try to allocate space for.

> - Aside, quick question: Any reason why struct amdgpu_cs_parser doesn't
>    subclass ttm_operation_ctx? From my naive understanding this would make
>    tons of sense ...

amdgpu_cs_parser is already overloaded with to much crap which actually 
should be temporary allocated on the stack.

> - Maybe we could even build some lru/eviction helpers on top, perhaps with
>    two lists, one for the set of buffers in the execbuf, the other for
>    additional buffers we've reserved as part of the eviction dance (to
>    solve the TODO in ttm_mem_evict_wait_busy).

That's what I'm currently working on, but some driver still need the 
struct_mutex for GEM ref/unref which is complicating things a bit.

So prototyping that in TTM BOs first before I move on to using the 
underlying GEM object.

> - Only downside would be that drivers outside of drivers/gpu won't be able
>    to use these helpers. I don't see any immediate nor near-future need for
>    that. All other drivers use so little memory they don't need to
>    participate in the multi-lock dance, they just pin the few buffers they
>    need and call it a day.

I can live with that.

Regards,
Christian.

>
> Ofc not everything above would need to be done right away, that's more
> ideas for todo.rst entries to make sure we all agree on the rough
> direction.
>
> Thoughts?
>
> Also adding Gerd Hoffmann, since he looked into this.
>
> Cheers, Daniel
>> ---
>>   drivers/dma-buf/Makefile       |   2 +-
>>   drivers/dma-buf/dma-resv-ctx.c | 108 +++++++++++++++++++++++++++++++++
>>   include/linux/dma-resv-ctx.h   |  68 +++++++++++++++++++++
>>   include/linux/dma-resv.h       |   1 +
>>   4 files changed, 178 insertions(+), 1 deletion(-)
>>   create mode 100644 drivers/dma-buf/dma-resv-ctx.c
>>   create mode 100644 include/linux/dma-resv-ctx.h
>>
>> diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile
>> index 03479da06422..da7701c85de2 100644
>> --- a/drivers/dma-buf/Makefile
>> +++ b/drivers/dma-buf/Makefile
>> @@ -1,6 +1,6 @@
>>   # SPDX-License-Identifier: GPL-2.0-only
>>   obj-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \
>> -	 dma-resv.o seqno-fence.o
>> +	 dma-resv.o dma-resv-ctx.o seqno-fence.o
>>   obj-$(CONFIG_SYNC_FILE)		+= sync_file.o
>>   obj-$(CONFIG_SW_SYNC)		+= sw_sync.o sync_debug.o
>>   obj-$(CONFIG_UDMABUF)		+= udmabuf.o
>> diff --git a/drivers/dma-buf/dma-resv-ctx.c b/drivers/dma-buf/dma-resv-ctx.c
>> new file mode 100644
>> index 000000000000..cad10fa6f80b
>> --- /dev/null
>> +++ b/drivers/dma-buf/dma-resv-ctx.c
>> @@ -0,0 +1,108 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright 2019 Advanced Micro Devices, Inc.
>> + *
>> + * Permission is hereby granted, free of charge, to any person obtaining a
>> + * copy of this software and associated documentation files (the "Software"),
>> + * to deal in the Software without restriction, including without limitation
>> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
>> + * and/or sell copies of the Software, and to permit persons to whom the
>> + * Software is furnished to do so, subject to the following conditions:
>> + *
>> + * The above copyright notice and this permission notice shall be included in
>> + * all copies or substantial portions of the Software.
>> + *
>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
>> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
>> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
>> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
>> + * OTHER DEALINGS IN THE SOFTWARE.
>> + *
>> + * Authors:
>> + *	Christian König <christian.koenig@amd.com>
>> + */
>> +
>> +#include <linux/dma-resv-ctx.h>
>> +
>> +/**
>> + * dma_resv_ctx_init - initialize a reservation context
>> + * @ctx: the context to initialize
>> + *
>> + * Start using this reservation context to lock reservation objects for update.
>> + */
>> +void dma_resv_ctx_init(struct dma_resv_ctx *ctx)
>> +{
>> +	ww_acquire_init(&ctx->base, &reservation_ww_class);
>> +	init_llist_head(&ctx->locked);
>> +	ctx->contended = NULL;
>> +}
>> +EXPORT_SYMBOL(dma_resv_ctx_init);
>> +
>> +/**
>> + * dma_resv_ctx_unlock_all - unlock all reservation objects
>> + * @ctx: the context which holds the reservation objects
>> + *
>> + * Unlocks all reservation objects locked with this context.
>> + */
>> +void dma_resv_ctx_unlock_all(struct dma_resv_ctx *ctx)
>> +{
>> +	struct dma_resv *obj, *next;
>> +
>> +	if (ctx->contended)
>> +		dma_resv_unlock(ctx->contended);
>> +	ctx->contended = NULL;
>> +
>> +	llist_for_each_entry_safe(obj, next, ctx->locked.first, locked)
>> +		ww_mutex_unlock(&obj->lock);
>> +	init_llist_head(&ctx->locked);
>> +}
>> +EXPORT_SYMBOL(dma_resv_ctx_unlock_all);
>> +
>> +/**
>> + * dma_resv_ctx_lock - lock a reservation object with deadlock handling
>> + * @ctx: the context which should be used to lock the object
>> + * @obj: the object which needs to be locked
>> + * @interruptible: if we should wait interruptible
>> + *
>> + * Use @ctx to lock the reservation object. If a deadlock is detected we backoff
>> + * by releasing all locked objects and use the slow path to lock the reservation
>> + * object. After successfully locking in the slow path -EDEADLK is returned to
>> + * signal that all other locks must be re-taken as well.
>> + */
>> +int dma_resv_ctx_lock(struct dma_resv_ctx *ctx, struct dma_resv *obj,
>> +		      bool interruptible)
>> +{
>> +	int ret = 0;
>> +
>> +	if (unlikely(ctx->contended == obj))
>> +		ctx->contended = NULL;
>> +	else if (interruptible)
>> +		ret = dma_resv_lock_interruptible(obj, &ctx->base);
>> +	else
>> +		ret = dma_resv_lock(obj, &ctx->base);
>> +
>> +	if (likely(!ret)) {
>> +		/* don't use llist_add here, we have separate locking */
>> +		obj->locked.next = ctx->locked.first;
>> +		ctx->locked.first = &obj->locked;
>> +		return 0;
>> +	}
>> +	if (unlikely(ret != -EDEADLK))
>> +		return ret;
>> +
>> +	dma_resv_ctx_unlock_all(ctx);
>> +
>> +	if (interruptible) {
>> +		ret = dma_resv_lock_slow_interruptible(obj, &ctx->base);
>> +		if (unlikely(ret))
>> +			return ret;
>> +	} else {
>> +		dma_resv_lock_slow(obj, &ctx->base);
>> +	}
>> +
>> +	ctx->contended = obj;
>> +	return -EDEADLK;
>> +}
>> +EXPORT_SYMBOL(dma_resv_ctx_lock);
>> diff --git a/include/linux/dma-resv-ctx.h b/include/linux/dma-resv-ctx.h
>> new file mode 100644
>> index 000000000000..86473de65167
>> --- /dev/null
>> +++ b/include/linux/dma-resv-ctx.h
>> @@ -0,0 +1,68 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright 2019 Advanced Micro Devices, Inc.
>> + *
>> + * Permission is hereby granted, free of charge, to any person obtaining a
>> + * copy of this software and associated documentation files (the "Software"),
>> + * to deal in the Software without restriction, including without limitation
>> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
>> + * and/or sell copies of the Software, and to permit persons to whom the
>> + * Software is furnished to do so, subject to the following conditions:
>> + *
>> + * The above copyright notice and this permission notice shall be included in
>> + * all copies or substantial portions of the Software.
>> + *
>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
>> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
>> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
>> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
>> + * OTHER DEALINGS IN THE SOFTWARE.
>> + *
>> + * Authors:
>> + *	Christian König <christian.koenig@amd.com>
>> + */
>> +
>> +#ifndef _LINUX_DMA_RESV_CTX_H
>> +#define _LINUX_DMA_RESV_CTX_H
>> +
>> +#include <linux/llist.h>
>> +#include <linux/dma-resv.h>
>> +
>> +/**
>> + * struct dma_resv_ctx - context to lock reservation objects
>> + * @base: ww_acquire_ctx used for deadlock detection
>> + * @locked: list of dma_resv objects locked in this context
>> + * @contended: contended dma_resv object
>> + */
>> +struct dma_resv_ctx {
>> +	struct ww_acquire_ctx base;
>> +	struct llist_head locked;
>> +	struct dma_resv *contended;
>> +};
>> +
>> +/**
>> + * dma_resv_ctx_done - wrapper for ww_acquire_done
>> + * @ctx: the reservation context which is done with locking
>> + */
>> +static inline void dma_resv_ctx_done(struct dma_resv_ctx *ctx)
>> +{
>> +	ww_acquire_done(&ctx->base);
>> +}
>> +
>> +/**
>> + * dma_resv_ctx_fini - wrapper for ww_acquire_fini
>> + * @ctx: the reservation context which is finished
>> + */
>> +static inline void dma_resv_ctx_fini(struct dma_resv_ctx *ctx)
>> +{
>> +	ww_acquire_fini(&ctx->base);
>> +}
>> +
>> +void dma_resv_ctx_init(struct dma_resv_ctx *ctx);
>> +void dma_resv_ctx_unlock_all(struct dma_resv_ctx *ctx);
>> +int dma_resv_ctx_lock(struct dma_resv_ctx *ctx, struct dma_resv *obj,
>> +		      bool interruptible);
>> +
>> +#endif /* _LINUX_DMA_RESV_CTX_H */
>> diff --git a/include/linux/dma-resv.h b/include/linux/dma-resv.h
>> index ee50d10f052b..1267822c2669 100644
>> --- a/include/linux/dma-resv.h
>> +++ b/include/linux/dma-resv.h
>> @@ -71,6 +71,7 @@ struct dma_resv_list {
>>    */
>>   struct dma_resv {
>>   	struct ww_mutex lock;
>> +	struct llist_node locked;
>>   	seqcount_t seq;
>>   
>>   	struct dma_fence __rcu *fence_excl;
>> -- 
>> 2.17.1
>>


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/3] dma-buf: add dma_resv_ctx for deadlock handling
  2019-10-09 13:21   ` Christian König
@ 2019-10-09 14:39     ` Daniel Vetter
  0 siblings, 0 replies; 7+ messages in thread
From: Daniel Vetter @ 2019-10-09 14:39 UTC (permalink / raw)
  To: christian.koenig
  Cc: Daniel Vetter, Gerd Hoffmann, dri-devel, linux-media, chris,
	sumit.semwal

On Wed, Oct 09, 2019 at 03:21:06PM +0200, Christian König wrote:
> Am 08.10.19 um 17:16 schrieb Daniel Vetter:
> > On Wed, Sep 18, 2019 at 07:55:23PM +0200, Christian König wrote:
> > > The ww_mutex framework allows for detecting deadlocks when multiple
> > > threads try to acquire the same set of locks in different order.
> > > 
> > > The problem is that handling those deadlocks was the burden of the user of
> > > the ww_mutex implementation and at least some users didn't got that right
> > > on the first try.
> > > 
> > > So introduce a new dma_resv_ctx object which can be used to
> > > simplify the deadlock handling. This is done by tracking all locked
> > > dma_resv objects in the context as well as the last contended object.
> > > 
> > > When a deadlock occurse we now unlock all previously locked objects and
> > > acquire the contended lock in the slow path. After this is done -EDEADLK
> > > is still returned to signal that all other locks now need to be
> > > re-acquired again.
> > > 
> > > Signed-off-by: Christian König <christian.koenig@amd.com>
> > I pondered this quite a bit, some thoughts:
> > 
> > - doing this over dma-buf means we can only use this for the ww_mutx
> >    dance, not for anything else. Which means we need another layer on top
> >    for shared execbuf utils (which Gerd has started looking into, but I
> >    felt like not quite the right approach yet in his first draft series).
> 
> Yes, and I actually realized that this won't work on the dma_resv layer as
> well.
> 
> We need to base this on GEM to be able to do the correct ref/unref the
> locked objects.
> 
> > - With the ttm/gem merger we could just put this into drm_gem_object, and
> >    ttm/gem helpers could still use it. Plus we could build some shared
> >    execbuf utils on top of this, by essentially merging ttm_operation_ctx
> >    into drm_gem_operation_ctx. I think this would also simplify the ttm
> >    parameters a bit, since currently the ttm_operation_ctx doesn't have an
> >    explicit pointer to the ww_acquire_ctx (which feels like an oversight to
> >    me).
> 
> That ttm_operation_ctx doesn't contain a ww_acquire_ctx is intentional and
> mandatory for correct operation.
> 
> See for swapping things out from the MM callbacks you can't work with a
> ww_acquire_ctx and instead need to use trylock.
> 
> When you need a ww_acquire_ctx you can get that from the buffer object you
> try to allocate space for.

Hm I've seen that trick, and I'm not sure I like it. Imo an explicit
pointer that tells you which acquire_ctx to use, with the clear meaning
that if the pointer is NULL then only trylock is ok, would be a lot
cleaner. At least for execbuf utils the acquire_ctx is really central and
really should be in there somehow. Or embed it and have a flag whether
it's ok to use it or not.

Other plan would be to make sure that acquire_ctx and non-acquire_ctx
(i.e. mmu/shrinker callbacks) are clearly separate paths. That might be the
even better option going forward, since mixing them up leads to a huge
mess ime.

> > - Aside, quick question: Any reason why struct amdgpu_cs_parser doesn't
> >    subclass ttm_operation_ctx? From my naive understanding this would make
> >    tons of sense ...
> 
> amdgpu_cs_parser is already overloaded with to much crap which actually
> should be temporary allocated on the stack.
> 
> > - Maybe we could even build some lru/eviction helpers on top, perhaps with
> >    two lists, one for the set of buffers in the execbuf, the other for
> >    additional buffers we've reserved as part of the eviction dance (to
> >    solve the TODO in ttm_mem_evict_wait_busy).
> 
> That's what I'm currently working on, but some driver still need the
> struct_mutex for GEM ref/unref which is complicating things a bit.
> 
> So prototyping that in TTM BOs first before I move on to using the
> underlying GEM object.
> 
> > - Only downside would be that drivers outside of drivers/gpu won't be able
> >    to use these helpers. I don't see any immediate nor near-future need for
> >    that. All other drivers use so little memory they don't need to
> >    participate in the multi-lock dance, they just pin the few buffers they
> >    need and call it a day.
> 
> I can live with that.

Ok, sounds like we're agreing on all this. And I think once your series
here has landed, Gerd could rebase his execbuf helpers on top and we can
see what color suits them to get them landed.
-Daniel

> 
> Regards,
> Christian.
> 
> > 
> > Ofc not everything above would need to be done right away, that's more
> > ideas for todo.rst entries to make sure we all agree on the rough
> > direction.
> > 
> > Thoughts?
> > 
> > Also adding Gerd Hoffmann, since he looked into this.
> > 
> > Cheers, Daniel
> > > ---
> > >   drivers/dma-buf/Makefile       |   2 +-
> > >   drivers/dma-buf/dma-resv-ctx.c | 108 +++++++++++++++++++++++++++++++++
> > >   include/linux/dma-resv-ctx.h   |  68 +++++++++++++++++++++
> > >   include/linux/dma-resv.h       |   1 +
> > >   4 files changed, 178 insertions(+), 1 deletion(-)
> > >   create mode 100644 drivers/dma-buf/dma-resv-ctx.c
> > >   create mode 100644 include/linux/dma-resv-ctx.h
> > > 
> > > diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile
> > > index 03479da06422..da7701c85de2 100644
> > > --- a/drivers/dma-buf/Makefile
> > > +++ b/drivers/dma-buf/Makefile
> > > @@ -1,6 +1,6 @@
> > >   # SPDX-License-Identifier: GPL-2.0-only
> > >   obj-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \
> > > -	 dma-resv.o seqno-fence.o
> > > +	 dma-resv.o dma-resv-ctx.o seqno-fence.o
> > >   obj-$(CONFIG_SYNC_FILE)		+= sync_file.o
> > >   obj-$(CONFIG_SW_SYNC)		+= sw_sync.o sync_debug.o
> > >   obj-$(CONFIG_UDMABUF)		+= udmabuf.o
> > > diff --git a/drivers/dma-buf/dma-resv-ctx.c b/drivers/dma-buf/dma-resv-ctx.c
> > > new file mode 100644
> > > index 000000000000..cad10fa6f80b
> > > --- /dev/null
> > > +++ b/drivers/dma-buf/dma-resv-ctx.c
> > > @@ -0,0 +1,108 @@
> > > +// SPDX-License-Identifier: GPL-2.0-only
> > > +/*
> > > + * Copyright 2019 Advanced Micro Devices, Inc.
> > > + *
> > > + * Permission is hereby granted, free of charge, to any person obtaining a
> > > + * copy of this software and associated documentation files (the "Software"),
> > > + * to deal in the Software without restriction, including without limitation
> > > + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> > > + * and/or sell copies of the Software, and to permit persons to whom the
> > > + * Software is furnished to do so, subject to the following conditions:
> > > + *
> > > + * The above copyright notice and this permission notice shall be included in
> > > + * all copies or substantial portions of the Software.
> > > + *
> > > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> > > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> > > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> > > + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
> > > + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> > > + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> > > + * OTHER DEALINGS IN THE SOFTWARE.
> > > + *
> > > + * Authors:
> > > + *	Christian König <christian.koenig@amd.com>
> > > + */
> > > +
> > > +#include <linux/dma-resv-ctx.h>
> > > +
> > > +/**
> > > + * dma_resv_ctx_init - initialize a reservation context
> > > + * @ctx: the context to initialize
> > > + *
> > > + * Start using this reservation context to lock reservation objects for update.
> > > + */
> > > +void dma_resv_ctx_init(struct dma_resv_ctx *ctx)
> > > +{
> > > +	ww_acquire_init(&ctx->base, &reservation_ww_class);
> > > +	init_llist_head(&ctx->locked);
> > > +	ctx->contended = NULL;
> > > +}
> > > +EXPORT_SYMBOL(dma_resv_ctx_init);
> > > +
> > > +/**
> > > + * dma_resv_ctx_unlock_all - unlock all reservation objects
> > > + * @ctx: the context which holds the reservation objects
> > > + *
> > > + * Unlocks all reservation objects locked with this context.
> > > + */
> > > +void dma_resv_ctx_unlock_all(struct dma_resv_ctx *ctx)
> > > +{
> > > +	struct dma_resv *obj, *next;
> > > +
> > > +	if (ctx->contended)
> > > +		dma_resv_unlock(ctx->contended);
> > > +	ctx->contended = NULL;
> > > +
> > > +	llist_for_each_entry_safe(obj, next, ctx->locked.first, locked)
> > > +		ww_mutex_unlock(&obj->lock);
> > > +	init_llist_head(&ctx->locked);
> > > +}
> > > +EXPORT_SYMBOL(dma_resv_ctx_unlock_all);
> > > +
> > > +/**
> > > + * dma_resv_ctx_lock - lock a reservation object with deadlock handling
> > > + * @ctx: the context which should be used to lock the object
> > > + * @obj: the object which needs to be locked
> > > + * @interruptible: if we should wait interruptible
> > > + *
> > > + * Use @ctx to lock the reservation object. If a deadlock is detected we backoff
> > > + * by releasing all locked objects and use the slow path to lock the reservation
> > > + * object. After successfully locking in the slow path -EDEADLK is returned to
> > > + * signal that all other locks must be re-taken as well.
> > > + */
> > > +int dma_resv_ctx_lock(struct dma_resv_ctx *ctx, struct dma_resv *obj,
> > > +		      bool interruptible)
> > > +{
> > > +	int ret = 0;
> > > +
> > > +	if (unlikely(ctx->contended == obj))
> > > +		ctx->contended = NULL;
> > > +	else if (interruptible)
> > > +		ret = dma_resv_lock_interruptible(obj, &ctx->base);
> > > +	else
> > > +		ret = dma_resv_lock(obj, &ctx->base);
> > > +
> > > +	if (likely(!ret)) {
> > > +		/* don't use llist_add here, we have separate locking */
> > > +		obj->locked.next = ctx->locked.first;
> > > +		ctx->locked.first = &obj->locked;
> > > +		return 0;
> > > +	}
> > > +	if (unlikely(ret != -EDEADLK))
> > > +		return ret;
> > > +
> > > +	dma_resv_ctx_unlock_all(ctx);
> > > +
> > > +	if (interruptible) {
> > > +		ret = dma_resv_lock_slow_interruptible(obj, &ctx->base);
> > > +		if (unlikely(ret))
> > > +			return ret;
> > > +	} else {
> > > +		dma_resv_lock_slow(obj, &ctx->base);
> > > +	}
> > > +
> > > +	ctx->contended = obj;
> > > +	return -EDEADLK;
> > > +}
> > > +EXPORT_SYMBOL(dma_resv_ctx_lock);
> > > diff --git a/include/linux/dma-resv-ctx.h b/include/linux/dma-resv-ctx.h
> > > new file mode 100644
> > > index 000000000000..86473de65167
> > > --- /dev/null
> > > +++ b/include/linux/dma-resv-ctx.h
> > > @@ -0,0 +1,68 @@
> > > +// SPDX-License-Identifier: GPL-2.0-only
> > > +/*
> > > + * Copyright 2019 Advanced Micro Devices, Inc.
> > > + *
> > > + * Permission is hereby granted, free of charge, to any person obtaining a
> > > + * copy of this software and associated documentation files (the "Software"),
> > > + * to deal in the Software without restriction, including without limitation
> > > + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> > > + * and/or sell copies of the Software, and to permit persons to whom the
> > > + * Software is furnished to do so, subject to the following conditions:
> > > + *
> > > + * The above copyright notice and this permission notice shall be included in
> > > + * all copies or substantial portions of the Software.
> > > + *
> > > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> > > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> > > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> > > + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
> > > + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> > > + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> > > + * OTHER DEALINGS IN THE SOFTWARE.
> > > + *
> > > + * Authors:
> > > + *	Christian König <christian.koenig@amd.com>
> > > + */
> > > +
> > > +#ifndef _LINUX_DMA_RESV_CTX_H
> > > +#define _LINUX_DMA_RESV_CTX_H
> > > +
> > > +#include <linux/llist.h>
> > > +#include <linux/dma-resv.h>
> > > +
> > > +/**
> > > + * struct dma_resv_ctx - context to lock reservation objects
> > > + * @base: ww_acquire_ctx used for deadlock detection
> > > + * @locked: list of dma_resv objects locked in this context
> > > + * @contended: contended dma_resv object
> > > + */
> > > +struct dma_resv_ctx {
> > > +	struct ww_acquire_ctx base;
> > > +	struct llist_head locked;
> > > +	struct dma_resv *contended;
> > > +};
> > > +
> > > +/**
> > > + * dma_resv_ctx_done - wrapper for ww_acquire_done
> > > + * @ctx: the reservation context which is done with locking
> > > + */
> > > +static inline void dma_resv_ctx_done(struct dma_resv_ctx *ctx)
> > > +{
> > > +	ww_acquire_done(&ctx->base);
> > > +}
> > > +
> > > +/**
> > > + * dma_resv_ctx_fini - wrapper for ww_acquire_fini
> > > + * @ctx: the reservation context which is finished
> > > + */
> > > +static inline void dma_resv_ctx_fini(struct dma_resv_ctx *ctx)
> > > +{
> > > +	ww_acquire_fini(&ctx->base);
> > > +}
> > > +
> > > +void dma_resv_ctx_init(struct dma_resv_ctx *ctx);
> > > +void dma_resv_ctx_unlock_all(struct dma_resv_ctx *ctx);
> > > +int dma_resv_ctx_lock(struct dma_resv_ctx *ctx, struct dma_resv *obj,
> > > +		      bool interruptible);
> > > +
> > > +#endif /* _LINUX_DMA_RESV_CTX_H */
> > > diff --git a/include/linux/dma-resv.h b/include/linux/dma-resv.h
> > > index ee50d10f052b..1267822c2669 100644
> > > --- a/include/linux/dma-resv.h
> > > +++ b/include/linux/dma-resv.h
> > > @@ -71,6 +71,7 @@ struct dma_resv_list {
> > >    */
> > >   struct dma_resv {
> > >   	struct ww_mutex lock;
> > > +	struct llist_node locked;
> > >   	seqcount_t seq;
> > >   	struct dma_fence __rcu *fence_excl;
> > > -- 
> > > 2.17.1
> > > 
> 

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/3] dma-buf: add dma_resv_ctx for deadlock handling
  2019-10-08 15:16 ` [PATCH 1/3] dma-buf: add dma_resv_ctx for deadlock handling Daniel Vetter
  2019-10-09 13:21   ` Christian König
@ 2019-10-14  8:54   ` Gerd Hoffmann
  1 sibling, 0 replies; 7+ messages in thread
From: Gerd Hoffmann @ 2019-10-14  8:54 UTC (permalink / raw)
  To: Daniel Vetter
  Cc: Christian König, dri-devel, linux-media, chris, sumit.semwal

  Hi,

> - doing this over dma-buf means we can only use this for the ww_mutx
>   dance, not for anything else. Which means we need another layer on top
>   for shared execbuf utils (which Gerd has started looking into, but I
>   felt like not quite the right approach yet in his first draft series).

FYI: this is in virtio-gpu for now, see virtio_gpu_array_*
in drivers/gpu/drm/virtio/virtgpu_gem.c

cheers,
  Gerd


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2019-10-14  8:54 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-09-18 17:55 [PATCH 1/3] dma-buf: add dma_resv_ctx for deadlock handling Christian König
2019-09-18 17:55 ` [PATCH 2/3] drm/ttm: switch ttm_execbuf_util to new drm_resv_ctx Christian König
2019-09-18 17:55 ` [PATCH 3/3] drm/gem: use new dma_resv_ctx Christian König
2019-10-08 15:16 ` [PATCH 1/3] dma-buf: add dma_resv_ctx for deadlock handling Daniel Vetter
2019-10-09 13:21   ` Christian König
2019-10-09 14:39     ` Daniel Vetter
2019-10-14  8:54   ` Gerd Hoffmann

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).