root/sys/dev/drm/i915/i915_timeline.c
/*
 * SPDX-License-Identifier: MIT
 *
 * Copyright © 2016-2018 Intel Corporation
 */

#include "i915_drv.h"

#include "i915_timeline.h"
#include "i915_syncmap.h"

void i915_timeline_init(struct drm_i915_private *i915,
                        struct i915_timeline *timeline,
                        const char *name)
{
        lockdep_assert_held(&i915->drm.struct_mutex);

        /*
         * Ideally we want a set of engines on a single leaf as we expect
         * to mostly be tracking synchronisation between engines. It is not
         * a huge issue if this is not the case, but we may want to mitigate
         * any page crossing penalties if they become an issue.
         */
        BUILD_BUG_ON(KSYNCMAP < I915_NUM_ENGINES);

        timeline->name = name;

        list_add(&timeline->link, &i915->gt.timelines);

        /* Called during early_init before we know how many engines there are */

        timeline->fence_context = dma_fence_context_alloc(1);

        lockinit(&timeline->lock, "i915tll", 0, LK_CANRECURSE);

        init_request_active(&timeline->last_request, NULL);
        INIT_LIST_HEAD(&timeline->requests);

        i915_syncmap_init(&timeline->sync);
}

/**
 * i915_timelines_park - called when the driver idles
 * @i915: the drm_i915_private device
 *
 * When the driver is completely idle, we know that all of our sync points
 * have been signaled and our tracking is then entirely redundant. Any request
 * to wait upon an older sync point will be completed instantly as we know
 * the fence is signaled and therefore we will not even look them up in the
 * sync point map.
 */
void i915_timelines_park(struct drm_i915_private *i915)
{
        struct i915_timeline *timeline;

        lockdep_assert_held(&i915->drm.struct_mutex);

        list_for_each_entry(timeline, &i915->gt.timelines, link) {
                /*
                 * All known fences are completed so we can scrap
                 * the current sync point tracking and start afresh,
                 * any attempt to wait upon a previous sync point
                 * will be skipped as the fence was signaled.
                 */
                i915_syncmap_free(&timeline->sync);
        }
}

void i915_timeline_fini(struct i915_timeline *timeline)
{
        GEM_BUG_ON(!list_empty(&timeline->requests));

        i915_syncmap_free(&timeline->sync);

        list_del(&timeline->link);
}

struct i915_timeline *
i915_timeline_create(struct drm_i915_private *i915, const char *name)
{
        struct i915_timeline *timeline;

        timeline = kzalloc(sizeof(*timeline), GFP_KERNEL);
        if (!timeline)
                return ERR_PTR(-ENOMEM);

        i915_timeline_init(i915, timeline, name);
        kref_init(&timeline->kref);

        return timeline;
}

void __i915_timeline_free(struct kref *kref)
{
        struct i915_timeline *timeline =
                container_of(kref, typeof(*timeline), kref);

        i915_timeline_fini(timeline);
        kfree(timeline);
}

#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
#include "selftests/mock_timeline.c"
#include "selftests/i915_timeline.c"
#endif