xref: /linux/drivers/gpu/drm/xe/xe_tlb_inval_job.c (revision ec2e0fb07d789976c601bec19ecced7a501c3705)
1*15366239SMatthew Brost // SPDX-License-Identifier: MIT
2*15366239SMatthew Brost /*
3*15366239SMatthew Brost  * Copyright © 2025 Intel Corporation
4*15366239SMatthew Brost  */
5*15366239SMatthew Brost 
6*15366239SMatthew Brost #include "xe_assert.h"
7*15366239SMatthew Brost #include "xe_dep_job_types.h"
8*15366239SMatthew Brost #include "xe_dep_scheduler.h"
9*15366239SMatthew Brost #include "xe_exec_queue.h"
10*15366239SMatthew Brost #include "xe_gt_types.h"
11*15366239SMatthew Brost #include "xe_tlb_inval.h"
12*15366239SMatthew Brost #include "xe_tlb_inval_job.h"
13*15366239SMatthew Brost #include "xe_migrate.h"
14*15366239SMatthew Brost #include "xe_pm.h"
15*15366239SMatthew Brost 
16*15366239SMatthew Brost /** struct xe_tlb_inval_job - TLB invalidation job */
17*15366239SMatthew Brost struct xe_tlb_inval_job {
18*15366239SMatthew Brost 	/** @dep: base generic dependency Xe job */
19*15366239SMatthew Brost 	struct xe_dep_job dep;
20*15366239SMatthew Brost 	/** @tlb_inval: TLB invalidation client */
21*15366239SMatthew Brost 	struct xe_tlb_inval *tlb_inval;
22*15366239SMatthew Brost 	/** @q: exec queue issuing the invalidate */
23*15366239SMatthew Brost 	struct xe_exec_queue *q;
24*15366239SMatthew Brost 	/** @refcount: ref count of this job */
25*15366239SMatthew Brost 	struct kref refcount;
26*15366239SMatthew Brost 	/**
27*15366239SMatthew Brost 	 * @fence: dma fence to indicate completion. 1 way relationship - job
28*15366239SMatthew Brost 	 * can safely reference fence, fence cannot safely reference job.
29*15366239SMatthew Brost 	 */
30*15366239SMatthew Brost 	struct dma_fence *fence;
31*15366239SMatthew Brost 	/** @start: Start address to invalidate */
32*15366239SMatthew Brost 	u64 start;
33*15366239SMatthew Brost 	/** @end: End address to invalidate */
34*15366239SMatthew Brost 	u64 end;
35*15366239SMatthew Brost 	/** @asid: Address space ID to invalidate */
36*15366239SMatthew Brost 	u32 asid;
37*15366239SMatthew Brost 	/** @fence_armed: Fence has been armed */
38*15366239SMatthew Brost 	bool fence_armed;
39*15366239SMatthew Brost };
40*15366239SMatthew Brost 
41*15366239SMatthew Brost static struct dma_fence *xe_tlb_inval_job_run(struct xe_dep_job *dep_job)
42*15366239SMatthew Brost {
43*15366239SMatthew Brost 	struct xe_tlb_inval_job *job =
44*15366239SMatthew Brost 		container_of(dep_job, typeof(*job), dep);
45*15366239SMatthew Brost 	struct xe_tlb_inval_fence *ifence =
46*15366239SMatthew Brost 		container_of(job->fence, typeof(*ifence), base);
47*15366239SMatthew Brost 
48*15366239SMatthew Brost 	xe_tlb_inval_range(job->tlb_inval, ifence, job->start,
49*15366239SMatthew Brost 			   job->end, job->asid);
50*15366239SMatthew Brost 
51*15366239SMatthew Brost 	return job->fence;
52*15366239SMatthew Brost }
53*15366239SMatthew Brost 
54*15366239SMatthew Brost static void xe_tlb_inval_job_free(struct xe_dep_job *dep_job)
55*15366239SMatthew Brost {
56*15366239SMatthew Brost 	struct xe_tlb_inval_job *job =
57*15366239SMatthew Brost 		container_of(dep_job, typeof(*job), dep);
58*15366239SMatthew Brost 
59*15366239SMatthew Brost 	/* Pairs with get in xe_tlb_inval_job_push */
60*15366239SMatthew Brost 	xe_tlb_inval_job_put(job);
61*15366239SMatthew Brost }
62*15366239SMatthew Brost 
63*15366239SMatthew Brost static const struct xe_dep_job_ops dep_job_ops = {
64*15366239SMatthew Brost 	.run_job = xe_tlb_inval_job_run,
65*15366239SMatthew Brost 	.free_job = xe_tlb_inval_job_free,
66*15366239SMatthew Brost };
67*15366239SMatthew Brost 
68*15366239SMatthew Brost /**
69*15366239SMatthew Brost  * xe_tlb_inval_job_create() - TLB invalidation job create
70*15366239SMatthew Brost  * @q: exec queue issuing the invalidate
71*15366239SMatthew Brost  * @tlb_inval: TLB invalidation client
72*15366239SMatthew Brost  * @dep_scheduler: Dependency scheduler for job
73*15366239SMatthew Brost  * @start: Start address to invalidate
74*15366239SMatthew Brost  * @end: End address to invalidate
75*15366239SMatthew Brost  * @asid: Address space ID to invalidate
76*15366239SMatthew Brost  *
77*15366239SMatthew Brost  * Create a TLB invalidation job and initialize internal fields. The caller is
78*15366239SMatthew Brost  * responsible for releasing the creation reference.
79*15366239SMatthew Brost  *
80*15366239SMatthew Brost  * Return: TLB invalidation job object on success, ERR_PTR failure
81*15366239SMatthew Brost  */
82*15366239SMatthew Brost struct xe_tlb_inval_job *
83*15366239SMatthew Brost xe_tlb_inval_job_create(struct xe_exec_queue *q, struct xe_tlb_inval *tlb_inval,
84*15366239SMatthew Brost 			struct xe_dep_scheduler *dep_scheduler, u64 start,
85*15366239SMatthew Brost 			u64 end, u32 asid)
86*15366239SMatthew Brost {
87*15366239SMatthew Brost 	struct xe_tlb_inval_job *job;
88*15366239SMatthew Brost 	struct drm_sched_entity *entity =
89*15366239SMatthew Brost 		xe_dep_scheduler_entity(dep_scheduler);
90*15366239SMatthew Brost 	struct xe_tlb_inval_fence *ifence;
91*15366239SMatthew Brost 	int err;
92*15366239SMatthew Brost 
93*15366239SMatthew Brost 	job = kmalloc(sizeof(*job), GFP_KERNEL);
94*15366239SMatthew Brost 	if (!job)
95*15366239SMatthew Brost 		return ERR_PTR(-ENOMEM);
96*15366239SMatthew Brost 
97*15366239SMatthew Brost 	job->q = q;
98*15366239SMatthew Brost 	job->tlb_inval = tlb_inval;
99*15366239SMatthew Brost 	job->start = start;
100*15366239SMatthew Brost 	job->end = end;
101*15366239SMatthew Brost 	job->asid = asid;
102*15366239SMatthew Brost 	job->fence_armed = false;
103*15366239SMatthew Brost 	job->dep.ops = &dep_job_ops;
104*15366239SMatthew Brost 	kref_init(&job->refcount);
105*15366239SMatthew Brost 	xe_exec_queue_get(q);	/* Pairs with put in xe_tlb_inval_job_destroy */
106*15366239SMatthew Brost 
107*15366239SMatthew Brost 	ifence = kmalloc(sizeof(*ifence), GFP_KERNEL);
108*15366239SMatthew Brost 	if (!ifence) {
109*15366239SMatthew Brost 		err = -ENOMEM;
110*15366239SMatthew Brost 		goto err_job;
111*15366239SMatthew Brost 	}
112*15366239SMatthew Brost 	job->fence = &ifence->base;
113*15366239SMatthew Brost 
114*15366239SMatthew Brost 	err = drm_sched_job_init(&job->dep.drm, entity, 1, NULL,
115*15366239SMatthew Brost 				 q->xef ? q->xef->drm->client_id : 0);
116*15366239SMatthew Brost 	if (err)
117*15366239SMatthew Brost 		goto err_fence;
118*15366239SMatthew Brost 
119*15366239SMatthew Brost 	/* Pairs with put in xe_tlb_inval_job_destroy */
120*15366239SMatthew Brost 	xe_pm_runtime_get_noresume(gt_to_xe(q->gt));
121*15366239SMatthew Brost 
122*15366239SMatthew Brost 	return job;
123*15366239SMatthew Brost 
124*15366239SMatthew Brost err_fence:
125*15366239SMatthew Brost 	kfree(ifence);
126*15366239SMatthew Brost err_job:
127*15366239SMatthew Brost 	xe_exec_queue_put(q);
128*15366239SMatthew Brost 	kfree(job);
129*15366239SMatthew Brost 
130*15366239SMatthew Brost 	return ERR_PTR(err);
131*15366239SMatthew Brost }
132*15366239SMatthew Brost 
133*15366239SMatthew Brost static void xe_tlb_inval_job_destroy(struct kref *ref)
134*15366239SMatthew Brost {
135*15366239SMatthew Brost 	struct xe_tlb_inval_job *job = container_of(ref, typeof(*job),
136*15366239SMatthew Brost 						    refcount);
137*15366239SMatthew Brost 	struct xe_tlb_inval_fence *ifence =
138*15366239SMatthew Brost 		container_of(job->fence, typeof(*ifence), base);
139*15366239SMatthew Brost 	struct xe_exec_queue *q = job->q;
140*15366239SMatthew Brost 	struct xe_device *xe = gt_to_xe(q->gt);
141*15366239SMatthew Brost 
142*15366239SMatthew Brost 	if (!job->fence_armed)
143*15366239SMatthew Brost 		kfree(ifence);
144*15366239SMatthew Brost 	else
145*15366239SMatthew Brost 		/* Ref from xe_tlb_inval_fence_init */
146*15366239SMatthew Brost 		dma_fence_put(job->fence);
147*15366239SMatthew Brost 
148*15366239SMatthew Brost 	drm_sched_job_cleanup(&job->dep.drm);
149*15366239SMatthew Brost 	kfree(job);
150*15366239SMatthew Brost 	xe_exec_queue_put(q);	/* Pairs with get from xe_tlb_inval_job_create */
151*15366239SMatthew Brost 	xe_pm_runtime_put(xe);	/* Pairs with get from xe_tlb_inval_job_create */
152*15366239SMatthew Brost }
153*15366239SMatthew Brost 
154*15366239SMatthew Brost /**
155*15366239SMatthew Brost  * xe_tlb_inval_alloc_dep() - TLB invalidation job alloc dependency
156*15366239SMatthew Brost  * @job: TLB invalidation job to alloc dependency for
157*15366239SMatthew Brost  *
158*15366239SMatthew Brost  * Allocate storage for a dependency in the TLB invalidation fence. This
159*15366239SMatthew Brost  * function should be called at most once per job and must be paired with
160*15366239SMatthew Brost  * xe_tlb_inval_job_push being called with a real fence.
161*15366239SMatthew Brost  *
162*15366239SMatthew Brost  * Return: 0 on success, -errno on failure
163*15366239SMatthew Brost  */
164*15366239SMatthew Brost int xe_tlb_inval_job_alloc_dep(struct xe_tlb_inval_job *job)
165*15366239SMatthew Brost {
166*15366239SMatthew Brost 	xe_assert(gt_to_xe(job->q->gt), !xa_load(&job->dep.drm.dependencies, 0));
167*15366239SMatthew Brost 	might_alloc(GFP_KERNEL);
168*15366239SMatthew Brost 
169*15366239SMatthew Brost 	return drm_sched_job_add_dependency(&job->dep.drm,
170*15366239SMatthew Brost 					    dma_fence_get_stub());
171*15366239SMatthew Brost }
172*15366239SMatthew Brost 
173*15366239SMatthew Brost /**
174*15366239SMatthew Brost  * xe_tlb_inval_job_push() - TLB invalidation job push
175*15366239SMatthew Brost  * @job: TLB invalidation job to push
176*15366239SMatthew Brost  * @m: The migration object being used
177*15366239SMatthew Brost  * @fence: Dependency for TLB invalidation job
178*15366239SMatthew Brost  *
179*15366239SMatthew Brost  * Pushes a TLB invalidation job for execution, using @fence as a dependency.
180*15366239SMatthew Brost  * Storage for @fence must be preallocated with xe_tlb_inval_job_alloc_dep
181*15366239SMatthew Brost  * prior to this call if @fence is not signaled. Takes a reference to the job’s
182*15366239SMatthew Brost  * finished fence, which the caller is responsible for releasing, and return it
183*15366239SMatthew Brost  * to the caller. This function is safe to be called in the path of reclaim.
184*15366239SMatthew Brost  *
185*15366239SMatthew Brost  * Return: Job's finished fence on success, cannot fail
186*15366239SMatthew Brost  */
187*15366239SMatthew Brost struct dma_fence *xe_tlb_inval_job_push(struct xe_tlb_inval_job *job,
188*15366239SMatthew Brost 					struct xe_migrate *m,
189*15366239SMatthew Brost 					struct dma_fence *fence)
190*15366239SMatthew Brost {
191*15366239SMatthew Brost 	struct xe_tlb_inval_fence *ifence =
192*15366239SMatthew Brost 		container_of(job->fence, typeof(*ifence), base);
193*15366239SMatthew Brost 
194*15366239SMatthew Brost 	if (!dma_fence_is_signaled(fence)) {
195*15366239SMatthew Brost 		void *ptr;
196*15366239SMatthew Brost 
197*15366239SMatthew Brost 		/*
198*15366239SMatthew Brost 		 * Can be in path of reclaim, hence the preallocation of fence
199*15366239SMatthew Brost 		 * storage in xe_tlb_inval_job_alloc_dep. Verify caller did
200*15366239SMatthew Brost 		 * this correctly.
201*15366239SMatthew Brost 		 */
202*15366239SMatthew Brost 		xe_assert(gt_to_xe(job->q->gt),
203*15366239SMatthew Brost 			  xa_load(&job->dep.drm.dependencies, 0) ==
204*15366239SMatthew Brost 			  dma_fence_get_stub());
205*15366239SMatthew Brost 
206*15366239SMatthew Brost 		dma_fence_get(fence);	/* ref released once dependency processed by scheduler */
207*15366239SMatthew Brost 		ptr = xa_store(&job->dep.drm.dependencies, 0, fence,
208*15366239SMatthew Brost 			       GFP_ATOMIC);
209*15366239SMatthew Brost 		xe_assert(gt_to_xe(job->q->gt), !xa_is_err(ptr));
210*15366239SMatthew Brost 	}
211*15366239SMatthew Brost 
212*15366239SMatthew Brost 	xe_tlb_inval_job_get(job);	/* Pairs with put in free_job */
213*15366239SMatthew Brost 	job->fence_armed = true;
214*15366239SMatthew Brost 
215*15366239SMatthew Brost 	/*
216*15366239SMatthew Brost 	 * We need the migration lock to protect the job's seqno and the spsc
217*15366239SMatthew Brost 	 * queue, only taken on migration queue, user queues protected dma-resv
218*15366239SMatthew Brost 	 * VM lock.
219*15366239SMatthew Brost 	 */
220*15366239SMatthew Brost 	xe_migrate_job_lock(m, job->q);
221*15366239SMatthew Brost 
222*15366239SMatthew Brost 	/* Creation ref pairs with put in xe_tlb_inval_job_destroy */
223*15366239SMatthew Brost 	xe_tlb_inval_fence_init(job->tlb_inval, ifence, false);
224*15366239SMatthew Brost 	dma_fence_get(job->fence);	/* Pairs with put in DRM scheduler */
225*15366239SMatthew Brost 
226*15366239SMatthew Brost 	drm_sched_job_arm(&job->dep.drm);
227*15366239SMatthew Brost 	/*
228*15366239SMatthew Brost 	 * caller ref, get must be done before job push as it could immediately
229*15366239SMatthew Brost 	 * signal and free.
230*15366239SMatthew Brost 	 */
231*15366239SMatthew Brost 	dma_fence_get(&job->dep.drm.s_fence->finished);
232*15366239SMatthew Brost 	drm_sched_entity_push_job(&job->dep.drm);
233*15366239SMatthew Brost 
234*15366239SMatthew Brost 	xe_migrate_job_unlock(m, job->q);
235*15366239SMatthew Brost 
236*15366239SMatthew Brost 	/*
237*15366239SMatthew Brost 	 * Not using job->fence, as it has its own dma-fence context, which does
238*15366239SMatthew Brost 	 * not allow TLB invalidation fences on the same queue, GT tuple to
239*15366239SMatthew Brost 	 * be squashed in dma-resv/DRM scheduler. Instead, we use the DRM scheduler
240*15366239SMatthew Brost 	 * context and job's finished fence, which enables squashing.
241*15366239SMatthew Brost 	 */
242*15366239SMatthew Brost 	return &job->dep.drm.s_fence->finished;
243*15366239SMatthew Brost }
244*15366239SMatthew Brost 
245*15366239SMatthew Brost /**
246*15366239SMatthew Brost  * xe_tlb_inval_job_get() - Get a reference to TLB invalidation job
247*15366239SMatthew Brost  * @job: TLB invalidation job object
248*15366239SMatthew Brost  *
249*15366239SMatthew Brost  * Increment the TLB invalidation job's reference count
250*15366239SMatthew Brost  */
251*15366239SMatthew Brost void xe_tlb_inval_job_get(struct xe_tlb_inval_job *job)
252*15366239SMatthew Brost {
253*15366239SMatthew Brost 	kref_get(&job->refcount);
254*15366239SMatthew Brost }
255*15366239SMatthew Brost 
256*15366239SMatthew Brost /**
257*15366239SMatthew Brost  * xe_tlb_inval_job_put() - Put a reference to TLB invalidation job
258*15366239SMatthew Brost  * @job: TLB invalidation job object
259*15366239SMatthew Brost  *
260*15366239SMatthew Brost  * Decrement the TLB invalidation job's reference count, call
261*15366239SMatthew Brost  * xe_tlb_inval_job_destroy when reference count == 0. Skips decrement if
262*15366239SMatthew Brost  * input @job is NULL or IS_ERR.
263*15366239SMatthew Brost  */
264*15366239SMatthew Brost void xe_tlb_inval_job_put(struct xe_tlb_inval_job *job)
265*15366239SMatthew Brost {
266*15366239SMatthew Brost 	if (!IS_ERR_OR_NULL(job))
267*15366239SMatthew Brost 		kref_put(&job->refcount, xe_tlb_inval_job_destroy);
268*15366239SMatthew Brost }
269