drivers/gpu/drm/xe/xe_tlb_inval.c

Source file repositories/reference/linux-study-clean/drivers/gpu/drm/xe/xe_tlb_inval.c

File Facts

System
Linux kernel
Corpus path
drivers/gpu/drm/xe/xe_tlb_inval.c
Extension
.c
Size
16804 bytes
Lines
573
Domain
Driver Families
Bucket
drivers/gpu
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

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

#include <drm/drm_managed.h>

#include "xe_device_types.h"
#include "xe_force_wake.h"
#include "xe_gt_stats.h"
#include "xe_gt_types.h"
#include "xe_guc_ct.h"
#include "xe_guc_tlb_inval.h"
#include "xe_mmio.h"
#include "xe_pm.h"
#include "xe_tlb_inval.h"
#include "xe_trace.h"

/**
 * DOC: Xe TLB invalidation
 *
 * Xe TLB invalidation is implemented in two layers. The first is the frontend
 * API, which provides an interface for TLB invalidations to the driver code.
 * The frontend handles seqno assignment, synchronization (fences), and the
 * timeout mechanism. The frontend is implemented via an embedded structure
 * xe_tlb_inval that includes a set of ops hooking into the backend. The backend
 * interacts with the hardware (or firmware) to perform the actual invalidation.
 */

#define FENCE_STACK_BIT		DMA_FENCE_FLAG_USER_BITS

static void xe_tlb_inval_fence_fini(struct xe_tlb_inval_fence *fence)
{
	if (WARN_ON_ONCE(!fence->tlb_inval))
		return;

	xe_pm_runtime_put(fence->tlb_inval->xe);
	fence->tlb_inval = NULL; /* fini() should be called once */
}

static void
xe_tlb_inval_fence_signal(struct xe_tlb_inval_fence *fence)
{
	struct xe_tlb_inval *tlb_inval = fence->tlb_inval;
	bool stack = test_bit(FENCE_STACK_BIT, &fence->base.flags);

	lockdep_assert_held(&fence->tlb_inval->pending_lock);

	list_del(&fence->link);
	if (list_empty(&tlb_inval->pending_fences))
		cancel_delayed_work(&tlb_inval->fence_tdr);
	trace_xe_tlb_inval_fence_signal(fence->tlb_inval->xe, fence);
	xe_tlb_inval_fence_fini(fence);
	dma_fence_signal(&fence->base);
	if (!stack)
		dma_fence_put(&fence->base);
}

static void
xe_tlb_inval_fence_signal_unlocked(struct xe_tlb_inval_fence *fence)
{
	struct xe_tlb_inval *tlb_inval = fence->tlb_inval;

	spin_lock_irq(&tlb_inval->pending_lock);
	xe_tlb_inval_fence_signal(fence);
	spin_unlock_irq(&tlb_inval->pending_lock);
}

static void xe_tlb_inval_fence_timeout(struct work_struct *work)
{
	struct xe_tlb_inval *tlb_inval = container_of(work, struct xe_tlb_inval,
						      fence_tdr.work);
	struct xe_device *xe = tlb_inval->xe;
	struct xe_tlb_inval_fence *fence, *next;
	long timeout_delay = tlb_inval->ops->timeout_delay(tlb_inval);

	tlb_inval->ops->flush(tlb_inval);

	spin_lock_irq(&tlb_inval->pending_lock);
	list_for_each_entry_safe(fence, next,
				 &tlb_inval->pending_fences, link) {
		s64 since_inval_ms = ktime_ms_delta(ktime_get(),
						    fence->inval_time);

		if (msecs_to_jiffies(since_inval_ms) < timeout_delay)
			break;

		trace_xe_tlb_inval_fence_timeout(xe, fence);
		drm_err(&xe->drm,
			"TLB invalidation fence timeout, seqno=%d recv=%d",

Annotation

Implementation Notes