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