@@ -21,6 +21,8 @@
*
*/
+#include <linux/dma-fence.h>
+#include <linux/dma-fence-array.h>
#include <linux/kthread.h>
#include <linux/slab.h>
#include <linux/completion.h>
@@ -92,7 +94,7 @@ int drm_sched_entity_init(struct drm_sched_entity *entity,
spsc_queue_init(&entity->job_queue);
atomic_set(&entity->fence_seq, 0);
- entity->fence_context = dma_fence_context_alloc(2);
+ entity->fence_context = dma_fence_context_alloc(4);
return 0;
}
@@ -183,39 +185,78 @@ static void drm_sched_entity_kill_jobs_cb(struct dma_fence *f,
{
struct drm_sched_job *job = container_of(cb, struct drm_sched_job,
finish_cb);
- unsigned long index;
dma_fence_put(f);
- /* Wait for all dependencies to avoid data corruptions */
- xa_for_each(&job->dependencies, index, f) {
- struct drm_sched_fence *s_fence = to_drm_sched_fence(f);
-
- if (s_fence && f == &s_fence->scheduled) {
- /* The dependencies array had a reference on the scheduled
- * fence, and the finished fence refcount might have
- * dropped to zero. Use dma_fence_get_rcu() so we get
- * a NULL fence in that case.
+ /* Wait for all dependencies to avoid data corruption */
+ if (!dma_fence_add_callback(job->deps_finished, &job->finish_cb,
+ drm_sched_entity_kill_jobs_cb))
+ return;
+
+ INIT_WORK(&job->work, drm_sched_entity_kill_jobs_work);
+ schedule_work(&job->work);
+}
+
+static struct dma_fence *job_deps_finished(struct drm_sched_job *job)
+{
+ struct dma_fence_array *array;
+ unsigned int i, j, num_fences;
+ struct dma_fence **fences;
+
+ if (job->deps) {
+ array = to_dma_fence_array(job->deps);
+
+ if (array)
+ num_fences = array->num_fences;
+ else
+ num_fences = 1;
+ } else {
+ num_fences = 0;
+ }
+
+ if (!num_fences)
+ return dma_fence_get_stub();
+
+ fences = kmalloc_array(num_fences, sizeof(*fences), GFP_KERNEL);
+ if (!fences)
+ return NULL;
+
+ if (num_fences == 1)
+ fences[0] = job->deps;
+ else
+ memcpy(fences, array->fences, num_fences * sizeof(*fences));
+
+ for (i = 0, j = 0; i < num_fences; i++) {
+ struct dma_fence *fence = dma_fence_get(fences[i]);
+ struct drm_sched_fence *s_fence;
+
+ s_fence = to_drm_sched_fence(fence);
+ if (s_fence && fence == &s_fence->scheduled) {
+ /*
+ * The dependencies array had a reference on the
+ * scheduled fence, and the finished fence refcount
+ * might have dropped to zero. Use dma_fence_get_rcu()
+ * so we get a NULL fence in that case.
*/
- f = dma_fence_get_rcu(&s_fence->finished);
+ fence = dma_fence_get_rcu(&s_fence->finished);
- /* Now that we have a reference on the finished fence,
+ /*
+ * Now that we have a reference on the finished fence,
* we can release the reference the dependencies array
* had on the scheduled fence.
*/
dma_fence_put(&s_fence->scheduled);
}
- xa_erase(&job->dependencies, index);
- if (f && !dma_fence_add_callback(f, &job->finish_cb,
- drm_sched_entity_kill_jobs_cb))
- return;
-
- dma_fence_put(f);
+ if (fence)
+ fences[j++] = fence;
}
- INIT_WORK(&job->work, drm_sched_entity_kill_jobs_work);
- schedule_work(&job->work);
+ array = dma_fence_array_create(j, fences,
+ job->entity->fence_context + 3, 1,
+ false);
+
+ return array ? &array->base : NULL;
}
/* Remove the entity from the scheduler and kill all pending jobs */
@@ -242,6 +283,11 @@ static void drm_sched_entity_kill(struct drm_sched_entity *entity)
struct drm_sched_fence *s_fence = job->s_fence;
dma_fence_get(&s_fence->finished);
+
+ job->deps_finished = job_deps_finished(job);
+ if (WARN_ON_ONCE(!job->deps_finished))
+ continue;
+
if (!prev || dma_fence_add_callback(prev, &job->finish_cb,
drm_sched_entity_kill_jobs_cb))
drm_sched_entity_kill_jobs_cb(NULL, &job->finish_cb);
@@ -435,17 +481,8 @@ static struct dma_fence *
drm_sched_job_dependency(struct drm_sched_job *job,
struct drm_sched_entity *entity)
{
- struct dma_fence *f;
-
- /* We keep the fence around, so we can iterate over all dependencies
- * in drm_sched_entity_kill_jobs_cb() to ensure all deps are signaled
- * before killing the job.
- */
- f = xa_load(&job->dependencies, job->last_dependency);
- if (f) {
- job->last_dependency++;
- return dma_fence_get(f);
- }
+ if (job->deps && !dma_fence_is_signaled(job->deps))
+ return dma_fence_get(job->deps);
if (job->sched->ops->prepare_job)
return job->sched->ops->prepare_job(job, entity);
@@ -69,6 +69,8 @@
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/completion.h>
+#include <linux/dma-fence-array.h>
+#include <linux/dma-fence-unwrap.h>
#include <linux/dma-resv.h>
#include <uapi/linux/sched/types.h>
@@ -564,8 +566,6 @@ int drm_sched_job_init(struct drm_sched_job *job,
INIT_LIST_HEAD(&job->list);
- xa_init_flags(&job->dependencies, XA_FLAGS_ALLOC);
-
return 0;
}
EXPORT_SYMBOL(drm_sched_job_init);
@@ -614,46 +614,34 @@ int drm_sched_job_add_dependency(struct drm_sched_job *job,
struct dma_fence *fence)
{
struct drm_sched_entity *entity = job->entity;
- struct dma_fence *entry;
- unsigned long index;
- u32 id = 0;
- int ret;
+ struct dma_fence *deps;
+ int ret = 0;
if (!fence)
return 0;
- if (fence->context == entity->fence_context ||
- fence->context == entity->fence_context + 1) {
+ if (fence->context >= entity->fence_context &&
+ fence->context <= entity->fence_context + 3) {
/*
* Fence is a scheduled/finished fence from a job
* which belongs to the same entity, we can ignore
* fences from ourself
*/
- dma_fence_put(fence);
- return 0;
+ goto out_put;
}
- /* Deduplicate if we already depend on a fence from the same context.
- * This lets the size of the array of deps scale with the number of
- * engines involved, rather than the number of BOs.
- */
- xa_for_each(&job->dependencies, index, entry) {
- if (entry->context != fence->context)
- continue;
-
- if (dma_fence_is_later(fence, entry)) {
- dma_fence_put(entry);
- xa_store(&job->dependencies, index, fence, GFP_KERNEL);
- } else {
- dma_fence_put(fence);
- }
- return 0;
+ deps = dma_fence_unwrap_merge_context(entity->fence_context + 2,
+ job->deps, fence);
+ if (!deps) {
+ ret = -ENOMEM;
+ goto out_put;
}
- ret = xa_alloc(&job->dependencies, &id, fence, xa_limit_32b, GFP_KERNEL);
- if (ret != 0)
- dma_fence_put(fence);
+ dma_fence_put(job->deps);
+ job->deps = deps;
+out_put:
+ dma_fence_put(fence);
return ret;
}
EXPORT_SYMBOL(drm_sched_job_add_dependency);
@@ -745,26 +733,49 @@ int drm_sched_job_add_implicit_dependencies(struct drm_sched_job *job,
}
EXPORT_SYMBOL(drm_sched_job_add_implicit_dependencies);
+static bool
+can_replace_fence(struct drm_gpu_scheduler *sched,
+ struct drm_sched_fence *s_fence,
+ struct dma_fence *fence)
+{
+ if (fence->error || !s_fence || s_fence->sched != sched ||
+ test_bit(DRM_SCHED_FENCE_DONT_PIPELINE, &fence->flags))
+ return false;
+
+ return true;
+}
+
void drm_sched_job_prepare_dependecies(struct drm_sched_job *job)
{
struct drm_gpu_scheduler *sched = job->sched;
+ struct drm_sched_fence *s_fence;
+ struct dma_fence_array *array;
struct dma_fence *fence;
- unsigned long index;
- xa_for_each(&job->dependencies, index, fence) {
- struct drm_sched_fence *s_fence = to_drm_sched_fence(fence);
+ if (!job->deps)
+ return;
- if (fence->error || !s_fence || s_fence->sched != sched ||
- test_bit(DRM_SCHED_FENCE_DONT_PIPELINE, &fence->flags))
- continue;
+ array = to_dma_fence_array(job->deps);
+ if (!array) {
+ fence = job->deps;
+ s_fence = to_drm_sched_fence(fence);
+ if (can_replace_fence(sched, s_fence, fence)) {
+ job->deps = dma_fence_get(&s_fence->scheduled);
+ dma_fence_put(fence);
+ }
+ } else {
+ unsigned i;
- /*
- * Fence is from the same scheduler, only need to wait for
- * it to be scheduled.
- */
- xa_store(&job->dependencies, index,
- dma_fence_get(&s_fence->scheduled), GFP_KERNEL);
- dma_fence_put(fence);
+ for (i = 0; i < array->num_fences; i++) {
+ fence = array->fences[i];
+ s_fence = to_drm_sched_fence(fence);
+ if (can_replace_fence(sched, s_fence, fence)) {
+ array->fences[i] =
+ dma_fence_get(&s_fence->scheduled);
+ dma_fence_put(fence);
+ }
+ }
+ array->base.seqno = job->s_fence->scheduled.seqno;
}
}
@@ -783,24 +794,17 @@ void drm_sched_job_prepare_dependecies(struct drm_sched_job *job)
*/
void drm_sched_job_cleanup(struct drm_sched_job *job)
{
- struct dma_fence *fence;
- unsigned long index;
-
if (kref_read(&job->s_fence->finished.refcount)) {
/* drm_sched_job_arm() has been called */
dma_fence_put(&job->s_fence->finished);
+ dma_fence_put(job->deps);
+ dma_fence_put(job->deps_finished);
} else {
/* aborted job before committing to run it */
drm_sched_fence_free(job->s_fence);
}
job->s_fence = NULL;
-
- xa_for_each(&job->dependencies, index, fence) {
- dma_fence_put(fence);
- }
- xa_destroy(&job->dependencies);
-
}
EXPORT_SYMBOL(drm_sched_job_cleanup);
@@ -27,7 +27,6 @@
#include <drm/spsc_queue.h>
#include <linux/dma-fence.h>
#include <linux/completion.h>
-#include <linux/xarray.h>
#include <linux/workqueue.h>
#define MAX_WAIT_SCHED_ENTITY_Q_EMPTY msecs_to_jiffies(1000)
@@ -360,17 +359,10 @@ struct drm_sched_job {
enum drm_sched_priority s_priority;
struct drm_sched_entity *entity;
struct dma_fence_cb cb;
- /**
- * @dependencies:
- *
- * Contains the dependencies as struct dma_fence for this job, see
- * drm_sched_job_add_dependency() and
- * drm_sched_job_add_implicit_dependencies().
- */
- struct xarray dependencies;
- /** @last_dependency: tracks @dependencies as they signal */
- unsigned long last_dependency;
+ struct dma_fence stub_fence;
+ struct dma_fence *deps;
+ struct dma_fence *deps_finished;
/**
* @submit_ts: