Contributors: 5
Author Tokens Token Proportion Commits Commit Proportion
Matthew Brost 1027 91.13% 2 28.57%
Thomas Hellstrom 76 6.74% 2 28.57%
Bommu Krishnaiah 14 1.24% 1 14.29%
José Roberto de Souza 9 0.80% 1 14.29%
Francois Dugast 1 0.09% 1 14.29%
Total 1127 7


// SPDX-License-Identifier: MIT
/*
 * Copyright © 2021 Intel Corporation
 */

#include "xe_hw_fence.h"

#include <linux/device.h>
#include <linux/slab.h>

#include "xe_bo.h"
#include "xe_device.h"
#include "xe_gt.h"
#include "xe_hw_engine.h"
#include "xe_macros.h"
#include "xe_map.h"
#include "xe_trace.h"

static struct kmem_cache *xe_hw_fence_slab;

int __init xe_hw_fence_module_init(void)
{
	xe_hw_fence_slab = kmem_cache_create("xe_hw_fence",
					     sizeof(struct xe_hw_fence), 0,
					     SLAB_HWCACHE_ALIGN, NULL);
	if (!xe_hw_fence_slab)
		return -ENOMEM;

	return 0;
}

void xe_hw_fence_module_exit(void)
{
	rcu_barrier();
	kmem_cache_destroy(xe_hw_fence_slab);
}

static struct xe_hw_fence *fence_alloc(void)
{
	return kmem_cache_zalloc(xe_hw_fence_slab, GFP_KERNEL);
}

static void fence_free(struct rcu_head *rcu)
{
	struct xe_hw_fence *fence =
		container_of(rcu, struct xe_hw_fence, dma.rcu);

	if (!WARN_ON_ONCE(!fence))
		kmem_cache_free(xe_hw_fence_slab, fence);
}

static void hw_fence_irq_run_cb(struct irq_work *work)
{
	struct xe_hw_fence_irq *irq = container_of(work, typeof(*irq), work);
	struct xe_hw_fence *fence, *next;
	bool tmp;

	tmp = dma_fence_begin_signalling();
	spin_lock(&irq->lock);
	if (irq->enabled) {
		list_for_each_entry_safe(fence, next, &irq->pending, irq_link) {
			struct dma_fence *dma_fence = &fence->dma;

			trace_xe_hw_fence_try_signal(fence);
			if (dma_fence_is_signaled_locked(dma_fence)) {
				trace_xe_hw_fence_signal(fence);
				list_del_init(&fence->irq_link);
				dma_fence_put(dma_fence);
			}
		}
	}
	spin_unlock(&irq->lock);
	dma_fence_end_signalling(tmp);
}

void xe_hw_fence_irq_init(struct xe_hw_fence_irq *irq)
{
	spin_lock_init(&irq->lock);
	init_irq_work(&irq->work, hw_fence_irq_run_cb);
	INIT_LIST_HEAD(&irq->pending);
	irq->enabled = true;
}

void xe_hw_fence_irq_finish(struct xe_hw_fence_irq *irq)
{
	struct xe_hw_fence *fence, *next;
	unsigned long flags;
	int err;
	bool tmp;

	if (XE_WARN_ON(!list_empty(&irq->pending))) {
		tmp = dma_fence_begin_signalling();
		spin_lock_irqsave(&irq->lock, flags);
		list_for_each_entry_safe(fence, next, &irq->pending, irq_link) {
			list_del_init(&fence->irq_link);
			err = dma_fence_signal_locked(&fence->dma);
			dma_fence_put(&fence->dma);
			XE_WARN_ON(err);
		}
		spin_unlock_irqrestore(&irq->lock, flags);
		dma_fence_end_signalling(tmp);
	}
}

void xe_hw_fence_irq_run(struct xe_hw_fence_irq *irq)
{
	irq_work_queue(&irq->work);
}

void xe_hw_fence_irq_stop(struct xe_hw_fence_irq *irq)
{
	spin_lock_irq(&irq->lock);
	irq->enabled = false;
	spin_unlock_irq(&irq->lock);
}

void xe_hw_fence_irq_start(struct xe_hw_fence_irq *irq)
{
	spin_lock_irq(&irq->lock);
	irq->enabled = true;
	spin_unlock_irq(&irq->lock);

	irq_work_queue(&irq->work);
}

void xe_hw_fence_ctx_init(struct xe_hw_fence_ctx *ctx, struct xe_gt *gt,
			  struct xe_hw_fence_irq *irq, const char *name)
{
	ctx->gt = gt;
	ctx->irq = irq;
	ctx->dma_fence_ctx = dma_fence_context_alloc(1);
	ctx->next_seqno = XE_FENCE_INITIAL_SEQNO;
	snprintf(ctx->name, sizeof(ctx->name), "%s", name);
}

void xe_hw_fence_ctx_finish(struct xe_hw_fence_ctx *ctx)
{
}

static struct xe_hw_fence *to_xe_hw_fence(struct dma_fence *fence);

static struct xe_hw_fence_irq *xe_hw_fence_irq(struct xe_hw_fence *fence)
{
	return container_of(fence->dma.lock, struct xe_hw_fence_irq, lock);
}

static const char *xe_hw_fence_get_driver_name(struct dma_fence *dma_fence)
{
	struct xe_hw_fence *fence = to_xe_hw_fence(dma_fence);

	return dev_name(fence->xe->drm.dev);
}

static const char *xe_hw_fence_get_timeline_name(struct dma_fence *dma_fence)
{
	struct xe_hw_fence *fence = to_xe_hw_fence(dma_fence);

	return fence->name;
}

static bool xe_hw_fence_signaled(struct dma_fence *dma_fence)
{
	struct xe_hw_fence *fence = to_xe_hw_fence(dma_fence);
	struct xe_device *xe = fence->xe;
	u32 seqno = xe_map_rd(xe, &fence->seqno_map, 0, u32);

	return dma_fence->error ||
		!__dma_fence_is_later(dma_fence->seqno, seqno, dma_fence->ops);
}

static bool xe_hw_fence_enable_signaling(struct dma_fence *dma_fence)
{
	struct xe_hw_fence *fence = to_xe_hw_fence(dma_fence);
	struct xe_hw_fence_irq *irq = xe_hw_fence_irq(fence);

	dma_fence_get(dma_fence);
	list_add_tail(&fence->irq_link, &irq->pending);

	/* SW completed (no HW IRQ) so kick handler to signal fence */
	if (xe_hw_fence_signaled(dma_fence))
		xe_hw_fence_irq_run(irq);

	return true;
}

static void xe_hw_fence_release(struct dma_fence *dma_fence)
{
	struct xe_hw_fence *fence = to_xe_hw_fence(dma_fence);

	XE_WARN_ON(!list_empty(&fence->irq_link));
	call_rcu(&dma_fence->rcu, fence_free);
}

static const struct dma_fence_ops xe_hw_fence_ops = {
	.get_driver_name = xe_hw_fence_get_driver_name,
	.get_timeline_name = xe_hw_fence_get_timeline_name,
	.enable_signaling = xe_hw_fence_enable_signaling,
	.signaled = xe_hw_fence_signaled,
	.release = xe_hw_fence_release,
};

static struct xe_hw_fence *to_xe_hw_fence(struct dma_fence *fence)
{
	if (XE_WARN_ON(fence->ops != &xe_hw_fence_ops))
		return NULL;

	return container_of(fence, struct xe_hw_fence, dma);
}

/**
 * xe_hw_fence_alloc() -  Allocate an hw fence.
 *
 * Allocate but don't initialize an hw fence.
 *
 * Return: Pointer to the allocated fence or
 * negative error pointer on error.
 */
struct dma_fence *xe_hw_fence_alloc(void)
{
	struct xe_hw_fence *hw_fence = fence_alloc();

	if (!hw_fence)
		return ERR_PTR(-ENOMEM);

	return &hw_fence->dma;
}

/**
 * xe_hw_fence_free() - Free an hw fence.
 * @fence: Pointer to the fence to free.
 *
 * Frees an hw fence that hasn't yet been
 * initialized.
 */
void xe_hw_fence_free(struct dma_fence *fence)
{
	fence_free(&fence->rcu);
}

/**
 * xe_hw_fence_init() - Initialize an hw fence.
 * @fence: Pointer to the fence to initialize.
 * @ctx: Pointer to the struct xe_hw_fence_ctx fence context.
 * @seqno_map: Pointer to the map into where the seqno is blitted.
 *
 * Initializes a pre-allocated hw fence.
 * After initialization, the fence is subject to normal
 * dma-fence refcounting.
 */
void xe_hw_fence_init(struct dma_fence *fence, struct xe_hw_fence_ctx *ctx,
		      struct iosys_map seqno_map)
{
	struct  xe_hw_fence *hw_fence =
		container_of(fence, typeof(*hw_fence), dma);

	hw_fence->xe = gt_to_xe(ctx->gt);
	snprintf(hw_fence->name, sizeof(hw_fence->name), "%s", ctx->name);
	hw_fence->seqno_map = seqno_map;
	INIT_LIST_HEAD(&hw_fence->irq_link);

	dma_fence_init(fence, &xe_hw_fence_ops, &ctx->irq->lock,
		       ctx->dma_fence_ctx, ctx->next_seqno++);

	trace_xe_hw_fence_create(hw_fence);
}