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