162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR MIT
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <drm/drm_exec.h>
462306a36Sopenharmony_ci#include <drm/drm_gem.h>
562306a36Sopenharmony_ci#include <linux/dma-resv.h>
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci/**
862306a36Sopenharmony_ci * DOC: Overview
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * This component mainly abstracts the retry loop necessary for locking
1162306a36Sopenharmony_ci * multiple GEM objects while preparing hardware operations (e.g. command
1262306a36Sopenharmony_ci * submissions, page table updates etc..).
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * If a contention is detected while locking a GEM object the cleanup procedure
1562306a36Sopenharmony_ci * unlocks all previously locked GEM objects and locks the contended one first
1662306a36Sopenharmony_ci * before locking any further objects.
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * After an object is locked fences slots can optionally be reserved on the
1962306a36Sopenharmony_ci * dma_resv object inside the GEM object.
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci * A typical usage pattern should look like this::
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci *	struct drm_gem_object *obj;
2462306a36Sopenharmony_ci *	struct drm_exec exec;
2562306a36Sopenharmony_ci *	unsigned long index;
2662306a36Sopenharmony_ci *	int ret;
2762306a36Sopenharmony_ci *
2862306a36Sopenharmony_ci *	drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT);
2962306a36Sopenharmony_ci *	drm_exec_until_all_locked(&exec) {
3062306a36Sopenharmony_ci *		ret = drm_exec_prepare_obj(&exec, boA, 1);
3162306a36Sopenharmony_ci *		drm_exec_retry_on_contention(&exec);
3262306a36Sopenharmony_ci *		if (ret)
3362306a36Sopenharmony_ci *			goto error;
3462306a36Sopenharmony_ci *
3562306a36Sopenharmony_ci *		ret = drm_exec_prepare_obj(&exec, boB, 1);
3662306a36Sopenharmony_ci *		drm_exec_retry_on_contention(&exec);
3762306a36Sopenharmony_ci *		if (ret)
3862306a36Sopenharmony_ci *			goto error;
3962306a36Sopenharmony_ci *	}
4062306a36Sopenharmony_ci *
4162306a36Sopenharmony_ci *	drm_exec_for_each_locked_object(&exec, index, obj) {
4262306a36Sopenharmony_ci *		dma_resv_add_fence(obj->resv, fence, DMA_RESV_USAGE_READ);
4362306a36Sopenharmony_ci *		...
4462306a36Sopenharmony_ci *	}
4562306a36Sopenharmony_ci *	drm_exec_fini(&exec);
4662306a36Sopenharmony_ci *
4762306a36Sopenharmony_ci * See struct dma_exec for more details.
4862306a36Sopenharmony_ci */
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/* Dummy value used to initially enter the retry loop */
5162306a36Sopenharmony_ci#define DRM_EXEC_DUMMY ((void *)~0)
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/* Unlock all objects and drop references */
5462306a36Sopenharmony_cistatic void drm_exec_unlock_all(struct drm_exec *exec)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	struct drm_gem_object *obj;
5762306a36Sopenharmony_ci	unsigned long index;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	drm_exec_for_each_locked_object_reverse(exec, index, obj) {
6062306a36Sopenharmony_ci		dma_resv_unlock(obj->resv);
6162306a36Sopenharmony_ci		drm_gem_object_put(obj);
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	drm_gem_object_put(exec->prelocked);
6562306a36Sopenharmony_ci	exec->prelocked = NULL;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/**
6962306a36Sopenharmony_ci * drm_exec_init - initialize a drm_exec object
7062306a36Sopenharmony_ci * @exec: the drm_exec object to initialize
7162306a36Sopenharmony_ci * @flags: controls locking behavior, see DRM_EXEC_* defines
7262306a36Sopenharmony_ci *
7362306a36Sopenharmony_ci * Initialize the object and make sure that we can track locked objects.
7462306a36Sopenharmony_ci */
7562306a36Sopenharmony_civoid drm_exec_init(struct drm_exec *exec, uint32_t flags)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	exec->flags = flags;
7862306a36Sopenharmony_ci	exec->objects = kmalloc(PAGE_SIZE, GFP_KERNEL);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	/* If allocation here fails, just delay that till the first use */
8162306a36Sopenharmony_ci	exec->max_objects = exec->objects ? PAGE_SIZE / sizeof(void *) : 0;
8262306a36Sopenharmony_ci	exec->num_objects = 0;
8362306a36Sopenharmony_ci	exec->contended = DRM_EXEC_DUMMY;
8462306a36Sopenharmony_ci	exec->prelocked = NULL;
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ciEXPORT_SYMBOL(drm_exec_init);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci/**
8962306a36Sopenharmony_ci * drm_exec_fini - finalize a drm_exec object
9062306a36Sopenharmony_ci * @exec: the drm_exec object to finalize
9162306a36Sopenharmony_ci *
9262306a36Sopenharmony_ci * Unlock all locked objects, drop the references to objects and free all memory
9362306a36Sopenharmony_ci * used for tracking the state.
9462306a36Sopenharmony_ci */
9562306a36Sopenharmony_civoid drm_exec_fini(struct drm_exec *exec)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	drm_exec_unlock_all(exec);
9862306a36Sopenharmony_ci	kvfree(exec->objects);
9962306a36Sopenharmony_ci	if (exec->contended != DRM_EXEC_DUMMY) {
10062306a36Sopenharmony_ci		drm_gem_object_put(exec->contended);
10162306a36Sopenharmony_ci		ww_acquire_fini(&exec->ticket);
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ciEXPORT_SYMBOL(drm_exec_fini);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci/**
10762306a36Sopenharmony_ci * drm_exec_cleanup - cleanup when contention is detected
10862306a36Sopenharmony_ci * @exec: the drm_exec object to cleanup
10962306a36Sopenharmony_ci *
11062306a36Sopenharmony_ci * Cleanup the current state and return true if we should stay inside the retry
11162306a36Sopenharmony_ci * loop, false if there wasn't any contention detected and we can keep the
11262306a36Sopenharmony_ci * objects locked.
11362306a36Sopenharmony_ci */
11462306a36Sopenharmony_cibool drm_exec_cleanup(struct drm_exec *exec)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	if (likely(!exec->contended)) {
11762306a36Sopenharmony_ci		ww_acquire_done(&exec->ticket);
11862306a36Sopenharmony_ci		return false;
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	if (likely(exec->contended == DRM_EXEC_DUMMY)) {
12262306a36Sopenharmony_ci		exec->contended = NULL;
12362306a36Sopenharmony_ci		ww_acquire_init(&exec->ticket, &reservation_ww_class);
12462306a36Sopenharmony_ci		return true;
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	drm_exec_unlock_all(exec);
12862306a36Sopenharmony_ci	exec->num_objects = 0;
12962306a36Sopenharmony_ci	return true;
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_exec_cleanup);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci/* Track the locked object in the array */
13462306a36Sopenharmony_cistatic int drm_exec_obj_locked(struct drm_exec *exec,
13562306a36Sopenharmony_ci			       struct drm_gem_object *obj)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	if (unlikely(exec->num_objects == exec->max_objects)) {
13862306a36Sopenharmony_ci		size_t size = exec->max_objects * sizeof(void *);
13962306a36Sopenharmony_ci		void *tmp;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci		tmp = kvrealloc(exec->objects, size, size + PAGE_SIZE,
14262306a36Sopenharmony_ci				GFP_KERNEL);
14362306a36Sopenharmony_ci		if (!tmp)
14462306a36Sopenharmony_ci			return -ENOMEM;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci		exec->objects = tmp;
14762306a36Sopenharmony_ci		exec->max_objects += PAGE_SIZE / sizeof(void *);
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci	drm_gem_object_get(obj);
15062306a36Sopenharmony_ci	exec->objects[exec->num_objects++] = obj;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	return 0;
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci/* Make sure the contended object is locked first */
15662306a36Sopenharmony_cistatic int drm_exec_lock_contended(struct drm_exec *exec)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	struct drm_gem_object *obj = exec->contended;
15962306a36Sopenharmony_ci	int ret;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	if (likely(!obj))
16262306a36Sopenharmony_ci		return 0;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	/* Always cleanup the contention so that error handling can kick in */
16562306a36Sopenharmony_ci	exec->contended = NULL;
16662306a36Sopenharmony_ci	if (exec->flags & DRM_EXEC_INTERRUPTIBLE_WAIT) {
16762306a36Sopenharmony_ci		ret = dma_resv_lock_slow_interruptible(obj->resv,
16862306a36Sopenharmony_ci						       &exec->ticket);
16962306a36Sopenharmony_ci		if (unlikely(ret))
17062306a36Sopenharmony_ci			goto error_dropref;
17162306a36Sopenharmony_ci	} else {
17262306a36Sopenharmony_ci		dma_resv_lock_slow(obj->resv, &exec->ticket);
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	ret = drm_exec_obj_locked(exec, obj);
17662306a36Sopenharmony_ci	if (unlikely(ret))
17762306a36Sopenharmony_ci		goto error_unlock;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	exec->prelocked = obj;
18062306a36Sopenharmony_ci	return 0;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cierror_unlock:
18362306a36Sopenharmony_ci	dma_resv_unlock(obj->resv);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cierror_dropref:
18662306a36Sopenharmony_ci	drm_gem_object_put(obj);
18762306a36Sopenharmony_ci	return ret;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci/**
19162306a36Sopenharmony_ci * drm_exec_lock_obj - lock a GEM object for use
19262306a36Sopenharmony_ci * @exec: the drm_exec object with the state
19362306a36Sopenharmony_ci * @obj: the GEM object to lock
19462306a36Sopenharmony_ci *
19562306a36Sopenharmony_ci * Lock a GEM object for use and grab a reference to it.
19662306a36Sopenharmony_ci *
19762306a36Sopenharmony_ci * Returns: -EDEADLK if a contention is detected, -EALREADY when object is
19862306a36Sopenharmony_ci * already locked (can be suppressed by setting the DRM_EXEC_IGNORE_DUPLICATES
19962306a36Sopenharmony_ci * flag), -ENOMEM when memory allocation failed and zero for success.
20062306a36Sopenharmony_ci */
20162306a36Sopenharmony_ciint drm_exec_lock_obj(struct drm_exec *exec, struct drm_gem_object *obj)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	int ret;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	ret = drm_exec_lock_contended(exec);
20662306a36Sopenharmony_ci	if (unlikely(ret))
20762306a36Sopenharmony_ci		return ret;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	if (exec->prelocked == obj) {
21062306a36Sopenharmony_ci		drm_gem_object_put(exec->prelocked);
21162306a36Sopenharmony_ci		exec->prelocked = NULL;
21262306a36Sopenharmony_ci		return 0;
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (exec->flags & DRM_EXEC_INTERRUPTIBLE_WAIT)
21662306a36Sopenharmony_ci		ret = dma_resv_lock_interruptible(obj->resv, &exec->ticket);
21762306a36Sopenharmony_ci	else
21862306a36Sopenharmony_ci		ret = dma_resv_lock(obj->resv, &exec->ticket);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	if (unlikely(ret == -EDEADLK)) {
22162306a36Sopenharmony_ci		drm_gem_object_get(obj);
22262306a36Sopenharmony_ci		exec->contended = obj;
22362306a36Sopenharmony_ci		return -EDEADLK;
22462306a36Sopenharmony_ci	}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	if (unlikely(ret == -EALREADY) &&
22762306a36Sopenharmony_ci	    exec->flags & DRM_EXEC_IGNORE_DUPLICATES)
22862306a36Sopenharmony_ci		return 0;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	if (unlikely(ret))
23162306a36Sopenharmony_ci		return ret;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	ret = drm_exec_obj_locked(exec, obj);
23462306a36Sopenharmony_ci	if (ret)
23562306a36Sopenharmony_ci		goto error_unlock;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	return 0;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cierror_unlock:
24062306a36Sopenharmony_ci	dma_resv_unlock(obj->resv);
24162306a36Sopenharmony_ci	return ret;
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ciEXPORT_SYMBOL(drm_exec_lock_obj);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci/**
24662306a36Sopenharmony_ci * drm_exec_unlock_obj - unlock a GEM object in this exec context
24762306a36Sopenharmony_ci * @exec: the drm_exec object with the state
24862306a36Sopenharmony_ci * @obj: the GEM object to unlock
24962306a36Sopenharmony_ci *
25062306a36Sopenharmony_ci * Unlock the GEM object and remove it from the collection of locked objects.
25162306a36Sopenharmony_ci * Should only be used to unlock the most recently locked objects. It's not time
25262306a36Sopenharmony_ci * efficient to unlock objects locked long ago.
25362306a36Sopenharmony_ci */
25462306a36Sopenharmony_civoid drm_exec_unlock_obj(struct drm_exec *exec, struct drm_gem_object *obj)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	unsigned int i;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	for (i = exec->num_objects; i--;) {
25962306a36Sopenharmony_ci		if (exec->objects[i] == obj) {
26062306a36Sopenharmony_ci			dma_resv_unlock(obj->resv);
26162306a36Sopenharmony_ci			for (++i; i < exec->num_objects; ++i)
26262306a36Sopenharmony_ci				exec->objects[i - 1] = exec->objects[i];
26362306a36Sopenharmony_ci			--exec->num_objects;
26462306a36Sopenharmony_ci			drm_gem_object_put(obj);
26562306a36Sopenharmony_ci			return;
26662306a36Sopenharmony_ci		}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ciEXPORT_SYMBOL(drm_exec_unlock_obj);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci/**
27362306a36Sopenharmony_ci * drm_exec_prepare_obj - prepare a GEM object for use
27462306a36Sopenharmony_ci * @exec: the drm_exec object with the state
27562306a36Sopenharmony_ci * @obj: the GEM object to prepare
27662306a36Sopenharmony_ci * @num_fences: how many fences to reserve
27762306a36Sopenharmony_ci *
27862306a36Sopenharmony_ci * Prepare a GEM object for use by locking it and reserving fence slots.
27962306a36Sopenharmony_ci *
28062306a36Sopenharmony_ci * Returns: -EDEADLK if a contention is detected, -EALREADY when object is
28162306a36Sopenharmony_ci * already locked, -ENOMEM when memory allocation failed and zero for success.
28262306a36Sopenharmony_ci */
28362306a36Sopenharmony_ciint drm_exec_prepare_obj(struct drm_exec *exec, struct drm_gem_object *obj,
28462306a36Sopenharmony_ci			 unsigned int num_fences)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	int ret;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	ret = drm_exec_lock_obj(exec, obj);
28962306a36Sopenharmony_ci	if (ret)
29062306a36Sopenharmony_ci		return ret;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	ret = dma_resv_reserve_fences(obj->resv, num_fences);
29362306a36Sopenharmony_ci	if (ret) {
29462306a36Sopenharmony_ci		drm_exec_unlock_obj(exec, obj);
29562306a36Sopenharmony_ci		return ret;
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	return 0;
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ciEXPORT_SYMBOL(drm_exec_prepare_obj);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci/**
30362306a36Sopenharmony_ci * drm_exec_prepare_array - helper to prepare an array of objects
30462306a36Sopenharmony_ci * @exec: the drm_exec object with the state
30562306a36Sopenharmony_ci * @objects: array of GEM object to prepare
30662306a36Sopenharmony_ci * @num_objects: number of GEM objects in the array
30762306a36Sopenharmony_ci * @num_fences: number of fences to reserve on each GEM object
30862306a36Sopenharmony_ci *
30962306a36Sopenharmony_ci * Prepares all GEM objects in an array, aborts on first error.
31062306a36Sopenharmony_ci * Reserves @num_fences on each GEM object after locking it.
31162306a36Sopenharmony_ci *
31262306a36Sopenharmony_ci * Returns: -EDEADLOCK on contention, -EALREADY when object is already locked,
31362306a36Sopenharmony_ci * -ENOMEM when memory allocation failed and zero for success.
31462306a36Sopenharmony_ci */
31562306a36Sopenharmony_ciint drm_exec_prepare_array(struct drm_exec *exec,
31662306a36Sopenharmony_ci			   struct drm_gem_object **objects,
31762306a36Sopenharmony_ci			   unsigned int num_objects,
31862306a36Sopenharmony_ci			   unsigned int num_fences)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	int ret;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	for (unsigned int i = 0; i < num_objects; ++i) {
32362306a36Sopenharmony_ci		ret = drm_exec_prepare_obj(exec, objects[i], num_fences);
32462306a36Sopenharmony_ci		if (unlikely(ret))
32562306a36Sopenharmony_ci			return ret;
32662306a36Sopenharmony_ci	}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	return 0;
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ciEXPORT_SYMBOL(drm_exec_prepare_array);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ciMODULE_DESCRIPTION("DRM execution context");
33362306a36Sopenharmony_ciMODULE_LICENSE("Dual MIT/GPL");
334