.. | .. |
---|
22 | 22 | * |
---|
23 | 23 | */ |
---|
24 | 24 | |
---|
25 | | -#include "i915_vma.h" |
---|
| 25 | +#include <linux/sched/mm.h> |
---|
| 26 | +#include <drm/drm_gem.h> |
---|
| 27 | + |
---|
| 28 | +#include "display/intel_frontbuffer.h" |
---|
| 29 | + |
---|
| 30 | +#include "gt/intel_engine.h" |
---|
| 31 | +#include "gt/intel_engine_heartbeat.h" |
---|
| 32 | +#include "gt/intel_gt.h" |
---|
| 33 | +#include "gt/intel_gt_requests.h" |
---|
26 | 34 | |
---|
27 | 35 | #include "i915_drv.h" |
---|
28 | | -#include "intel_ringbuffer.h" |
---|
29 | | -#include "intel_frontbuffer.h" |
---|
| 36 | +#include "i915_globals.h" |
---|
| 37 | +#include "i915_sw_fence_work.h" |
---|
| 38 | +#include "i915_trace.h" |
---|
| 39 | +#include "i915_vma.h" |
---|
30 | 40 | |
---|
31 | | -#include <drm/drm_gem.h> |
---|
| 41 | +static struct i915_global_vma { |
---|
| 42 | + struct i915_global base; |
---|
| 43 | + struct kmem_cache *slab_vmas; |
---|
| 44 | +} global; |
---|
| 45 | + |
---|
| 46 | +struct i915_vma *i915_vma_alloc(void) |
---|
| 47 | +{ |
---|
| 48 | + return kmem_cache_zalloc(global.slab_vmas, GFP_KERNEL); |
---|
| 49 | +} |
---|
| 50 | + |
---|
| 51 | +void i915_vma_free(struct i915_vma *vma) |
---|
| 52 | +{ |
---|
| 53 | + return kmem_cache_free(global.slab_vmas, vma); |
---|
| 54 | +} |
---|
32 | 55 | |
---|
33 | 56 | #if IS_ENABLED(CONFIG_DRM_I915_ERRLOG_GEM) && IS_ENABLED(CONFIG_DRM_DEBUG_MM) |
---|
34 | 57 | |
---|
.. | .. |
---|
36 | 59 | |
---|
37 | 60 | static void vma_print_allocator(struct i915_vma *vma, const char *reason) |
---|
38 | 61 | { |
---|
39 | | - unsigned long entries[12]; |
---|
40 | | - struct stack_trace trace = { |
---|
41 | | - .entries = entries, |
---|
42 | | - .max_entries = ARRAY_SIZE(entries), |
---|
43 | | - }; |
---|
| 62 | + unsigned long *entries; |
---|
| 63 | + unsigned int nr_entries; |
---|
44 | 64 | char buf[512]; |
---|
45 | 65 | |
---|
46 | 66 | if (!vma->node.stack) { |
---|
.. | .. |
---|
49 | 69 | return; |
---|
50 | 70 | } |
---|
51 | 71 | |
---|
52 | | - depot_fetch_stack(vma->node.stack, &trace); |
---|
53 | | - snprint_stack_trace(buf, sizeof(buf), &trace, 0); |
---|
| 72 | + nr_entries = stack_depot_fetch(vma->node.stack, &entries); |
---|
| 73 | + stack_trace_snprint(buf, sizeof(buf), entries, nr_entries, 0); |
---|
54 | 74 | DRM_DEBUG_DRIVER("vma.node [%08llx + %08llx] %s: inserted at %s\n", |
---|
55 | 75 | vma->node.start, vma->node.size, reason, buf); |
---|
56 | 76 | } |
---|
.. | .. |
---|
63 | 83 | |
---|
64 | 84 | #endif |
---|
65 | 85 | |
---|
66 | | -struct i915_vma_active { |
---|
67 | | - struct i915_gem_active base; |
---|
68 | | - struct i915_vma *vma; |
---|
69 | | - struct rb_node node; |
---|
70 | | - u64 timeline; |
---|
71 | | -}; |
---|
72 | | - |
---|
73 | | -static void |
---|
74 | | -__i915_vma_retire(struct i915_vma *vma, struct i915_request *rq) |
---|
| 86 | +static inline struct i915_vma *active_to_vma(struct i915_active *ref) |
---|
75 | 87 | { |
---|
76 | | - struct drm_i915_gem_object *obj = vma->obj; |
---|
77 | | - |
---|
78 | | - GEM_BUG_ON(!i915_vma_is_active(vma)); |
---|
79 | | - if (--vma->active_count) |
---|
80 | | - return; |
---|
81 | | - |
---|
82 | | - GEM_BUG_ON(!drm_mm_node_allocated(&vma->node)); |
---|
83 | | - list_move_tail(&vma->vm_link, &vma->vm->inactive_list); |
---|
84 | | - |
---|
85 | | - GEM_BUG_ON(!i915_gem_object_is_active(obj)); |
---|
86 | | - if (--obj->active_count) |
---|
87 | | - return; |
---|
88 | | - |
---|
89 | | - /* Prune the shared fence arrays iff completely idle (inc. external) */ |
---|
90 | | - if (reservation_object_trylock(obj->resv)) { |
---|
91 | | - if (reservation_object_test_signaled_rcu(obj->resv, true)) |
---|
92 | | - reservation_object_add_excl_fence(obj->resv, NULL); |
---|
93 | | - reservation_object_unlock(obj->resv); |
---|
94 | | - } |
---|
95 | | - |
---|
96 | | - /* Bump our place on the bound list to keep it roughly in LRU order |
---|
97 | | - * so that we don't steal from recently used but inactive objects |
---|
98 | | - * (unless we are forced to ofc!) |
---|
99 | | - */ |
---|
100 | | - spin_lock(&rq->i915->mm.obj_lock); |
---|
101 | | - if (obj->bind_count) |
---|
102 | | - list_move_tail(&obj->mm.link, &rq->i915->mm.bound_list); |
---|
103 | | - spin_unlock(&rq->i915->mm.obj_lock); |
---|
104 | | - |
---|
105 | | - obj->mm.dirty = true; /* be paranoid */ |
---|
106 | | - |
---|
107 | | - if (i915_gem_object_has_active_reference(obj)) { |
---|
108 | | - i915_gem_object_clear_active_reference(obj); |
---|
109 | | - i915_gem_object_put(obj); |
---|
110 | | - } |
---|
| 88 | + return container_of(ref, typeof(struct i915_vma), active); |
---|
111 | 89 | } |
---|
112 | 90 | |
---|
113 | | -static void |
---|
114 | | -i915_vma_retire(struct i915_gem_active *base, struct i915_request *rq) |
---|
| 91 | +static int __i915_vma_active(struct i915_active *ref) |
---|
115 | 92 | { |
---|
116 | | - struct i915_vma_active *active = |
---|
117 | | - container_of(base, typeof(*active), base); |
---|
118 | | - |
---|
119 | | - __i915_vma_retire(active->vma, rq); |
---|
| 93 | + return i915_vma_tryget(active_to_vma(ref)) ? 0 : -ENOENT; |
---|
120 | 94 | } |
---|
121 | 95 | |
---|
122 | | -static void |
---|
123 | | -i915_vma_last_retire(struct i915_gem_active *base, struct i915_request *rq) |
---|
| 96 | +__i915_active_call |
---|
| 97 | +static void __i915_vma_retire(struct i915_active *ref) |
---|
124 | 98 | { |
---|
125 | | - __i915_vma_retire(container_of(base, struct i915_vma, last_active), rq); |
---|
| 99 | + i915_vma_put(active_to_vma(ref)); |
---|
126 | 100 | } |
---|
127 | 101 | |
---|
128 | 102 | static struct i915_vma * |
---|
.. | .. |
---|
130 | 104 | struct i915_address_space *vm, |
---|
131 | 105 | const struct i915_ggtt_view *view) |
---|
132 | 106 | { |
---|
| 107 | + struct i915_vma *pos = ERR_PTR(-E2BIG); |
---|
133 | 108 | struct i915_vma *vma; |
---|
134 | 109 | struct rb_node *rb, **p; |
---|
135 | 110 | |
---|
136 | 111 | /* The aliasing_ppgtt should never be used directly! */ |
---|
137 | | - GEM_BUG_ON(vm == &vm->i915->mm.aliasing_ppgtt->vm); |
---|
| 112 | + GEM_BUG_ON(vm == &vm->gt->ggtt->alias->vm); |
---|
138 | 113 | |
---|
139 | | - vma = kmem_cache_zalloc(vm->i915->vmas, GFP_KERNEL); |
---|
| 114 | + vma = i915_vma_alloc(); |
---|
140 | 115 | if (vma == NULL) |
---|
141 | 116 | return ERR_PTR(-ENOMEM); |
---|
142 | 117 | |
---|
143 | | - vma->active = RB_ROOT; |
---|
144 | | - |
---|
145 | | - init_request_active(&vma->last_active, i915_vma_last_retire); |
---|
146 | | - init_request_active(&vma->last_fence, NULL); |
---|
147 | | - vma->vm = vm; |
---|
| 118 | + kref_init(&vma->ref); |
---|
| 119 | + mutex_init(&vma->pages_mutex); |
---|
| 120 | + vma->vm = i915_vm_get(vm); |
---|
148 | 121 | vma->ops = &vm->vma_ops; |
---|
149 | 122 | vma->obj = obj; |
---|
150 | | - vma->resv = obj->resv; |
---|
| 123 | + vma->resv = obj->base.resv; |
---|
151 | 124 | vma->size = obj->base.size; |
---|
152 | 125 | vma->display_alignment = I915_GTT_MIN_ALIGNMENT; |
---|
| 126 | + |
---|
| 127 | + i915_active_init(&vma->active, __i915_vma_active, __i915_vma_retire); |
---|
| 128 | + |
---|
| 129 | + /* Declare ourselves safe for use inside shrinkers */ |
---|
| 130 | + if (IS_ENABLED(CONFIG_LOCKDEP)) { |
---|
| 131 | + fs_reclaim_acquire(GFP_KERNEL); |
---|
| 132 | + might_lock(&vma->active.mutex); |
---|
| 133 | + fs_reclaim_release(GFP_KERNEL); |
---|
| 134 | + } |
---|
| 135 | + |
---|
| 136 | + INIT_LIST_HEAD(&vma->closed_link); |
---|
153 | 137 | |
---|
154 | 138 | if (view && view->type != I915_GGTT_VIEW_NORMAL) { |
---|
155 | 139 | vma->ggtt_view = *view; |
---|
.. | .. |
---|
164 | 148 | } else if (view->type == I915_GGTT_VIEW_ROTATED) { |
---|
165 | 149 | vma->size = intel_rotation_info_size(&view->rotated); |
---|
166 | 150 | vma->size <<= PAGE_SHIFT; |
---|
| 151 | + } else if (view->type == I915_GGTT_VIEW_REMAPPED) { |
---|
| 152 | + vma->size = intel_remapped_info_size(&view->remapped); |
---|
| 153 | + vma->size <<= PAGE_SHIFT; |
---|
167 | 154 | } |
---|
168 | 155 | } |
---|
169 | 156 | |
---|
.. | .. |
---|
172 | 159 | |
---|
173 | 160 | GEM_BUG_ON(!IS_ALIGNED(vma->size, I915_GTT_PAGE_SIZE)); |
---|
174 | 161 | |
---|
| 162 | + spin_lock(&obj->vma.lock); |
---|
| 163 | + |
---|
175 | 164 | if (i915_is_ggtt(vm)) { |
---|
176 | 165 | if (unlikely(overflows_type(vma->size, u32))) |
---|
177 | | - goto err_vma; |
---|
| 166 | + goto err_unlock; |
---|
178 | 167 | |
---|
179 | 168 | vma->fence_size = i915_gem_fence_size(vm->i915, vma->size, |
---|
180 | 169 | i915_gem_object_get_tiling(obj), |
---|
181 | 170 | i915_gem_object_get_stride(obj)); |
---|
182 | 171 | if (unlikely(vma->fence_size < vma->size || /* overflow */ |
---|
183 | 172 | vma->fence_size > vm->total)) |
---|
184 | | - goto err_vma; |
---|
| 173 | + goto err_unlock; |
---|
185 | 174 | |
---|
186 | 175 | GEM_BUG_ON(!IS_ALIGNED(vma->fence_size, I915_GTT_MIN_ALIGNMENT)); |
---|
187 | 176 | |
---|
.. | .. |
---|
190 | 179 | i915_gem_object_get_stride(obj)); |
---|
191 | 180 | GEM_BUG_ON(!is_power_of_2(vma->fence_alignment)); |
---|
192 | 181 | |
---|
| 182 | + __set_bit(I915_VMA_GGTT_BIT, __i915_vma_flags(vma)); |
---|
| 183 | + } |
---|
| 184 | + |
---|
| 185 | + rb = NULL; |
---|
| 186 | + p = &obj->vma.tree.rb_node; |
---|
| 187 | + while (*p) { |
---|
| 188 | + long cmp; |
---|
| 189 | + |
---|
| 190 | + rb = *p; |
---|
| 191 | + pos = rb_entry(rb, struct i915_vma, obj_node); |
---|
| 192 | + |
---|
| 193 | + /* |
---|
| 194 | + * If the view already exists in the tree, another thread |
---|
| 195 | + * already created a matching vma, so return the older instance |
---|
| 196 | + * and dispose of ours. |
---|
| 197 | + */ |
---|
| 198 | + cmp = i915_vma_compare(pos, vm, view); |
---|
| 199 | + if (cmp < 0) |
---|
| 200 | + p = &rb->rb_right; |
---|
| 201 | + else if (cmp > 0) |
---|
| 202 | + p = &rb->rb_left; |
---|
| 203 | + else |
---|
| 204 | + goto err_unlock; |
---|
| 205 | + } |
---|
| 206 | + rb_link_node(&vma->obj_node, rb, p); |
---|
| 207 | + rb_insert_color(&vma->obj_node, &obj->vma.tree); |
---|
| 208 | + |
---|
| 209 | + if (i915_vma_is_ggtt(vma)) |
---|
193 | 210 | /* |
---|
194 | 211 | * We put the GGTT vma at the start of the vma-list, followed |
---|
195 | 212 | * by the ppGGTT vma. This allows us to break early when |
---|
196 | 213 | * iterating over only the GGTT vma for an object, see |
---|
197 | 214 | * for_each_ggtt_vma() |
---|
198 | 215 | */ |
---|
199 | | - vma->flags |= I915_VMA_GGTT; |
---|
200 | | - list_add(&vma->obj_link, &obj->vma_list); |
---|
201 | | - } else { |
---|
202 | | - list_add_tail(&vma->obj_link, &obj->vma_list); |
---|
203 | | - } |
---|
| 216 | + list_add(&vma->obj_link, &obj->vma.list); |
---|
| 217 | + else |
---|
| 218 | + list_add_tail(&vma->obj_link, &obj->vma.list); |
---|
204 | 219 | |
---|
205 | | - rb = NULL; |
---|
206 | | - p = &obj->vma_tree.rb_node; |
---|
207 | | - while (*p) { |
---|
208 | | - struct i915_vma *pos; |
---|
209 | | - |
---|
210 | | - rb = *p; |
---|
211 | | - pos = rb_entry(rb, struct i915_vma, obj_node); |
---|
212 | | - if (i915_vma_compare(pos, vm, view) < 0) |
---|
213 | | - p = &rb->rb_right; |
---|
214 | | - else |
---|
215 | | - p = &rb->rb_left; |
---|
216 | | - } |
---|
217 | | - rb_link_node(&vma->obj_node, rb, p); |
---|
218 | | - rb_insert_color(&vma->obj_node, &obj->vma_tree); |
---|
219 | | - list_add(&vma->vm_link, &vm->unbound_list); |
---|
| 220 | + spin_unlock(&obj->vma.lock); |
---|
220 | 221 | |
---|
221 | 222 | return vma; |
---|
222 | 223 | |
---|
| 224 | +err_unlock: |
---|
| 225 | + spin_unlock(&obj->vma.lock); |
---|
223 | 226 | err_vma: |
---|
224 | | - kmem_cache_free(vm->i915->vmas, vma); |
---|
225 | | - return ERR_PTR(-E2BIG); |
---|
| 227 | + i915_vm_put(vm); |
---|
| 228 | + i915_vma_free(vma); |
---|
| 229 | + return pos; |
---|
226 | 230 | } |
---|
227 | 231 | |
---|
228 | 232 | static struct i915_vma * |
---|
.. | .. |
---|
232 | 236 | { |
---|
233 | 237 | struct rb_node *rb; |
---|
234 | 238 | |
---|
235 | | - rb = obj->vma_tree.rb_node; |
---|
| 239 | + rb = obj->vma.tree.rb_node; |
---|
236 | 240 | while (rb) { |
---|
237 | 241 | struct i915_vma *vma = rb_entry(rb, struct i915_vma, obj_node); |
---|
238 | 242 | long cmp; |
---|
.. | .. |
---|
261 | 265 | * Once created, the VMA is kept until either the object is freed, or the |
---|
262 | 266 | * address space is closed. |
---|
263 | 267 | * |
---|
264 | | - * Must be called with struct_mutex held. |
---|
265 | | - * |
---|
266 | 268 | * Returns the vma, or an error pointer. |
---|
267 | 269 | */ |
---|
268 | 270 | struct i915_vma * |
---|
.. | .. |
---|
272 | 274 | { |
---|
273 | 275 | struct i915_vma *vma; |
---|
274 | 276 | |
---|
275 | | - lockdep_assert_held(&obj->base.dev->struct_mutex); |
---|
276 | 277 | GEM_BUG_ON(view && !i915_is_ggtt(vm)); |
---|
277 | | - GEM_BUG_ON(vm->closed); |
---|
| 278 | + GEM_BUG_ON(!atomic_read(&vm->open)); |
---|
278 | 279 | |
---|
| 280 | + spin_lock(&obj->vma.lock); |
---|
279 | 281 | vma = vma_lookup(obj, vm, view); |
---|
280 | | - if (!vma) |
---|
| 282 | + spin_unlock(&obj->vma.lock); |
---|
| 283 | + |
---|
| 284 | + /* vma_create() will resolve the race if another creates the vma */ |
---|
| 285 | + if (unlikely(!vma)) |
---|
281 | 286 | vma = vma_create(obj, vm, view); |
---|
282 | 287 | |
---|
283 | 288 | GEM_BUG_ON(!IS_ERR(vma) && i915_vma_compare(vma, vm, view)); |
---|
284 | | - GEM_BUG_ON(!IS_ERR(vma) && vma_lookup(obj, vm, view) != vma); |
---|
285 | 289 | return vma; |
---|
| 290 | +} |
---|
| 291 | + |
---|
| 292 | +struct i915_vma_work { |
---|
| 293 | + struct dma_fence_work base; |
---|
| 294 | + struct i915_address_space *vm; |
---|
| 295 | + struct i915_vm_pt_stash stash; |
---|
| 296 | + struct i915_vma *vma; |
---|
| 297 | + struct drm_i915_gem_object *pinned; |
---|
| 298 | + struct i915_sw_dma_fence_cb cb; |
---|
| 299 | + enum i915_cache_level cache_level; |
---|
| 300 | + unsigned int flags; |
---|
| 301 | +}; |
---|
| 302 | + |
---|
| 303 | +static int __vma_bind(struct dma_fence_work *work) |
---|
| 304 | +{ |
---|
| 305 | + struct i915_vma_work *vw = container_of(work, typeof(*vw), base); |
---|
| 306 | + struct i915_vma *vma = vw->vma; |
---|
| 307 | + |
---|
| 308 | + vma->ops->bind_vma(vw->vm, &vw->stash, |
---|
| 309 | + vma, vw->cache_level, vw->flags); |
---|
| 310 | + return 0; |
---|
| 311 | +} |
---|
| 312 | + |
---|
| 313 | +static void __vma_release(struct dma_fence_work *work) |
---|
| 314 | +{ |
---|
| 315 | + struct i915_vma_work *vw = container_of(work, typeof(*vw), base); |
---|
| 316 | + |
---|
| 317 | + if (vw->pinned) { |
---|
| 318 | + __i915_gem_object_unpin_pages(vw->pinned); |
---|
| 319 | + i915_gem_object_put(vw->pinned); |
---|
| 320 | + } |
---|
| 321 | + |
---|
| 322 | + i915_vm_free_pt_stash(vw->vm, &vw->stash); |
---|
| 323 | + i915_vm_put(vw->vm); |
---|
| 324 | +} |
---|
| 325 | + |
---|
| 326 | +static const struct dma_fence_work_ops bind_ops = { |
---|
| 327 | + .name = "bind", |
---|
| 328 | + .work = __vma_bind, |
---|
| 329 | + .release = __vma_release, |
---|
| 330 | +}; |
---|
| 331 | + |
---|
| 332 | +struct i915_vma_work *i915_vma_work(void) |
---|
| 333 | +{ |
---|
| 334 | + struct i915_vma_work *vw; |
---|
| 335 | + |
---|
| 336 | + vw = kzalloc(sizeof(*vw), GFP_KERNEL); |
---|
| 337 | + if (!vw) |
---|
| 338 | + return NULL; |
---|
| 339 | + |
---|
| 340 | + dma_fence_work_init(&vw->base, &bind_ops); |
---|
| 341 | + vw->base.dma.error = -EAGAIN; /* disable the worker by default */ |
---|
| 342 | + |
---|
| 343 | + return vw; |
---|
| 344 | +} |
---|
| 345 | + |
---|
| 346 | +int i915_vma_wait_for_bind(struct i915_vma *vma) |
---|
| 347 | +{ |
---|
| 348 | + int err = 0; |
---|
| 349 | + |
---|
| 350 | + if (rcu_access_pointer(vma->active.excl.fence)) { |
---|
| 351 | + struct dma_fence *fence; |
---|
| 352 | + |
---|
| 353 | + rcu_read_lock(); |
---|
| 354 | + fence = dma_fence_get_rcu_safe(&vma->active.excl.fence); |
---|
| 355 | + rcu_read_unlock(); |
---|
| 356 | + if (fence) { |
---|
| 357 | + err = dma_fence_wait(fence, MAX_SCHEDULE_TIMEOUT); |
---|
| 358 | + dma_fence_put(fence); |
---|
| 359 | + } |
---|
| 360 | + } |
---|
| 361 | + |
---|
| 362 | + return err; |
---|
286 | 363 | } |
---|
287 | 364 | |
---|
288 | 365 | /** |
---|
.. | .. |
---|
290 | 367 | * @vma: VMA to map |
---|
291 | 368 | * @cache_level: mapping cache level |
---|
292 | 369 | * @flags: flags like global or local mapping |
---|
| 370 | + * @work: preallocated worker for allocating and binding the PTE |
---|
293 | 371 | * |
---|
294 | 372 | * DMA addresses are taken from the scatter-gather table of this object (or of |
---|
295 | 373 | * this VMA in case of non-default GGTT views) and PTE entries set up. |
---|
296 | 374 | * Note that DMA addresses are also the only part of the SG table we care about. |
---|
297 | 375 | */ |
---|
298 | | -int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level, |
---|
299 | | - u32 flags) |
---|
| 376 | +int i915_vma_bind(struct i915_vma *vma, |
---|
| 377 | + enum i915_cache_level cache_level, |
---|
| 378 | + u32 flags, |
---|
| 379 | + struct i915_vma_work *work) |
---|
300 | 380 | { |
---|
301 | 381 | u32 bind_flags; |
---|
302 | 382 | u32 vma_flags; |
---|
303 | | - int ret; |
---|
304 | 383 | |
---|
305 | 384 | GEM_BUG_ON(!drm_mm_node_allocated(&vma->node)); |
---|
306 | 385 | GEM_BUG_ON(vma->size > vma->node.size); |
---|
307 | 386 | |
---|
308 | | - if (GEM_WARN_ON(range_overflows(vma->node.start, |
---|
309 | | - vma->node.size, |
---|
310 | | - vma->vm->total))) |
---|
| 387 | + if (GEM_DEBUG_WARN_ON(range_overflows(vma->node.start, |
---|
| 388 | + vma->node.size, |
---|
| 389 | + vma->vm->total))) |
---|
311 | 390 | return -ENODEV; |
---|
312 | 391 | |
---|
313 | | - if (GEM_WARN_ON(!flags)) |
---|
| 392 | + if (GEM_DEBUG_WARN_ON(!flags)) |
---|
314 | 393 | return -EINVAL; |
---|
315 | 394 | |
---|
316 | | - bind_flags = 0; |
---|
317 | | - if (flags & PIN_GLOBAL) |
---|
318 | | - bind_flags |= I915_VMA_GLOBAL_BIND; |
---|
319 | | - if (flags & PIN_USER) |
---|
320 | | - bind_flags |= I915_VMA_LOCAL_BIND; |
---|
| 395 | + bind_flags = flags; |
---|
| 396 | + bind_flags &= I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND; |
---|
321 | 397 | |
---|
322 | | - vma_flags = vma->flags & (I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND); |
---|
323 | | - if (flags & PIN_UPDATE) |
---|
324 | | - bind_flags |= vma_flags; |
---|
325 | | - else |
---|
326 | | - bind_flags &= ~vma_flags; |
---|
| 398 | + vma_flags = atomic_read(&vma->flags); |
---|
| 399 | + vma_flags &= I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND; |
---|
| 400 | + |
---|
| 401 | + bind_flags &= ~vma_flags; |
---|
327 | 402 | if (bind_flags == 0) |
---|
328 | 403 | return 0; |
---|
329 | 404 | |
---|
330 | 405 | GEM_BUG_ON(!vma->pages); |
---|
331 | 406 | |
---|
332 | 407 | trace_i915_vma_bind(vma, bind_flags); |
---|
333 | | - ret = vma->ops->bind_vma(vma, cache_level, bind_flags); |
---|
334 | | - if (ret) |
---|
335 | | - return ret; |
---|
| 408 | + if (work && bind_flags & vma->vm->bind_async_flags) { |
---|
| 409 | + struct dma_fence *prev; |
---|
336 | 410 | |
---|
337 | | - vma->flags |= bind_flags; |
---|
| 411 | + work->vma = vma; |
---|
| 412 | + work->cache_level = cache_level; |
---|
| 413 | + work->flags = bind_flags; |
---|
| 414 | + |
---|
| 415 | + /* |
---|
| 416 | + * Note we only want to chain up to the migration fence on |
---|
| 417 | + * the pages (not the object itself). As we don't track that, |
---|
| 418 | + * yet, we have to use the exclusive fence instead. |
---|
| 419 | + * |
---|
| 420 | + * Also note that we do not want to track the async vma as |
---|
| 421 | + * part of the obj->resv->excl_fence as it only affects |
---|
| 422 | + * execution and not content or object's backing store lifetime. |
---|
| 423 | + */ |
---|
| 424 | + prev = i915_active_set_exclusive(&vma->active, &work->base.dma); |
---|
| 425 | + if (prev) { |
---|
| 426 | + __i915_sw_fence_await_dma_fence(&work->base.chain, |
---|
| 427 | + prev, |
---|
| 428 | + &work->cb); |
---|
| 429 | + dma_fence_put(prev); |
---|
| 430 | + } |
---|
| 431 | + |
---|
| 432 | + work->base.dma.error = 0; /* enable the queue_work() */ |
---|
| 433 | + |
---|
| 434 | + if (vma->obj) { |
---|
| 435 | + __i915_gem_object_pin_pages(vma->obj); |
---|
| 436 | + work->pinned = i915_gem_object_get(vma->obj); |
---|
| 437 | + } |
---|
| 438 | + } else { |
---|
| 439 | + vma->ops->bind_vma(vma->vm, NULL, vma, cache_level, bind_flags); |
---|
| 440 | + } |
---|
338 | 441 | |
---|
339 | 442 | if (vma->obj) |
---|
340 | 443 | set_bit(I915_BO_WAS_BOUND_BIT, &vma->obj->flags); |
---|
341 | 444 | |
---|
| 445 | + atomic_or(bind_flags, &vma->flags); |
---|
342 | 446 | return 0; |
---|
343 | 447 | } |
---|
344 | 448 | |
---|
.. | .. |
---|
347 | 451 | void __iomem *ptr; |
---|
348 | 452 | int err; |
---|
349 | 453 | |
---|
350 | | - /* Access through the GTT requires the device to be awake. */ |
---|
351 | | - assert_rpm_wakelock_held(vma->vm->i915); |
---|
352 | | - |
---|
353 | | - lockdep_assert_held(&vma->vm->i915->drm.struct_mutex); |
---|
354 | | - if (WARN_ON(!i915_vma_is_map_and_fenceable(vma))) { |
---|
| 454 | + if (GEM_WARN_ON(!i915_vma_is_map_and_fenceable(vma))) { |
---|
355 | 455 | err = -ENODEV; |
---|
356 | 456 | goto err; |
---|
357 | 457 | } |
---|
358 | 458 | |
---|
359 | 459 | GEM_BUG_ON(!i915_vma_is_ggtt(vma)); |
---|
360 | | - GEM_BUG_ON((vma->flags & I915_VMA_GLOBAL_BIND) == 0); |
---|
| 460 | + GEM_BUG_ON(!i915_vma_is_bound(vma, I915_VMA_GLOBAL_BIND)); |
---|
361 | 461 | |
---|
362 | | - ptr = vma->iomap; |
---|
| 462 | + ptr = READ_ONCE(vma->iomap); |
---|
363 | 463 | if (ptr == NULL) { |
---|
364 | 464 | ptr = io_mapping_map_wc(&i915_vm_to_ggtt(vma->vm)->iomap, |
---|
365 | 465 | vma->node.start, |
---|
.. | .. |
---|
369 | 469 | goto err; |
---|
370 | 470 | } |
---|
371 | 471 | |
---|
372 | | - vma->iomap = ptr; |
---|
| 472 | + if (unlikely(cmpxchg(&vma->iomap, NULL, ptr))) { |
---|
| 473 | + io_mapping_unmap(ptr); |
---|
| 474 | + ptr = vma->iomap; |
---|
| 475 | + } |
---|
373 | 476 | } |
---|
374 | 477 | |
---|
375 | 478 | __i915_vma_pin(vma); |
---|
.. | .. |
---|
379 | 482 | goto err_unpin; |
---|
380 | 483 | |
---|
381 | 484 | i915_vma_set_ggtt_write(vma); |
---|
| 485 | + |
---|
| 486 | + /* NB Access through the GTT requires the device to be awake. */ |
---|
382 | 487 | return ptr; |
---|
383 | 488 | |
---|
384 | 489 | err_unpin: |
---|
.. | .. |
---|
389 | 494 | |
---|
390 | 495 | void i915_vma_flush_writes(struct i915_vma *vma) |
---|
391 | 496 | { |
---|
392 | | - if (!i915_vma_has_ggtt_write(vma)) |
---|
393 | | - return; |
---|
394 | | - |
---|
395 | | - i915_gem_flush_ggtt_writes(vma->vm->i915); |
---|
396 | | - |
---|
397 | | - i915_vma_unset_ggtt_write(vma); |
---|
| 497 | + if (i915_vma_unset_ggtt_write(vma)) |
---|
| 498 | + intel_gt_flush_ggtt_writes(vma->vm->gt); |
---|
398 | 499 | } |
---|
399 | 500 | |
---|
400 | 501 | void i915_vma_unpin_iomap(struct i915_vma *vma) |
---|
401 | 502 | { |
---|
402 | | - lockdep_assert_held(&vma->vm->i915->drm.struct_mutex); |
---|
403 | | - |
---|
404 | 503 | GEM_BUG_ON(vma->iomap == NULL); |
---|
405 | 504 | |
---|
406 | 505 | i915_vma_flush_writes(vma); |
---|
.. | .. |
---|
409 | 508 | i915_vma_unpin(vma); |
---|
410 | 509 | } |
---|
411 | 510 | |
---|
412 | | -void i915_vma_unpin_and_release(struct i915_vma **p_vma) |
---|
| 511 | +void i915_vma_unpin_and_release(struct i915_vma **p_vma, unsigned int flags) |
---|
413 | 512 | { |
---|
414 | 513 | struct i915_vma *vma; |
---|
415 | 514 | struct drm_i915_gem_object *obj; |
---|
.. | .. |
---|
422 | 521 | GEM_BUG_ON(!obj); |
---|
423 | 522 | |
---|
424 | 523 | i915_vma_unpin(vma); |
---|
425 | | - i915_vma_close(vma); |
---|
426 | 524 | |
---|
427 | | - __i915_gem_object_release_unless_active(obj); |
---|
| 525 | + if (flags & I915_VMA_RELEASE_MAP) |
---|
| 526 | + i915_gem_object_unpin_map(obj); |
---|
| 527 | + |
---|
| 528 | + i915_gem_object_put(obj); |
---|
428 | 529 | } |
---|
429 | 530 | |
---|
430 | 531 | bool i915_vma_misplaced(const struct i915_vma *vma, |
---|
.. | .. |
---|
432 | 533 | { |
---|
433 | 534 | if (!drm_mm_node_allocated(&vma->node)) |
---|
434 | 535 | return false; |
---|
| 536 | + |
---|
| 537 | + if (test_bit(I915_VMA_ERROR_BIT, __i915_vma_flags(vma))) |
---|
| 538 | + return true; |
---|
435 | 539 | |
---|
436 | 540 | if (vma->node.size < size) |
---|
437 | 541 | return true; |
---|
.. | .. |
---|
461 | 565 | GEM_BUG_ON(!i915_vma_is_ggtt(vma)); |
---|
462 | 566 | GEM_BUG_ON(!vma->fence_size); |
---|
463 | 567 | |
---|
464 | | - /* |
---|
465 | | - * Explicitly disable for rotated VMA since the display does not |
---|
466 | | - * need the fence and the VMA is not accessible to other users. |
---|
467 | | - */ |
---|
468 | | - if (vma->ggtt_view.type == I915_GGTT_VIEW_ROTATED) |
---|
469 | | - return; |
---|
470 | | - |
---|
471 | 568 | fenceable = (vma->node.size >= vma->fence_size && |
---|
472 | 569 | IS_ALIGNED(vma->node.start, vma->fence_alignment)); |
---|
473 | 570 | |
---|
474 | 571 | mappable = vma->node.start + vma->fence_size <= i915_vm_to_ggtt(vma->vm)->mappable_end; |
---|
475 | 572 | |
---|
476 | 573 | if (mappable && fenceable) |
---|
477 | | - vma->flags |= I915_VMA_CAN_FENCE; |
---|
| 574 | + set_bit(I915_VMA_CAN_FENCE_BIT, __i915_vma_flags(vma)); |
---|
478 | 575 | else |
---|
479 | | - vma->flags &= ~I915_VMA_CAN_FENCE; |
---|
| 576 | + clear_bit(I915_VMA_CAN_FENCE_BIT, __i915_vma_flags(vma)); |
---|
480 | 577 | } |
---|
481 | 578 | |
---|
482 | | -static bool color_differs(struct drm_mm_node *node, unsigned long color) |
---|
483 | | -{ |
---|
484 | | - return node->allocated && node->color != color; |
---|
485 | | -} |
---|
486 | | - |
---|
487 | | -bool i915_gem_valid_gtt_space(struct i915_vma *vma, unsigned long cache_level) |
---|
| 579 | +bool i915_gem_valid_gtt_space(struct i915_vma *vma, unsigned long color) |
---|
488 | 580 | { |
---|
489 | 581 | struct drm_mm_node *node = &vma->node; |
---|
490 | 582 | struct drm_mm_node *other; |
---|
.. | .. |
---|
496 | 588 | * these constraints apply and set the drm_mm.color_adjust |
---|
497 | 589 | * appropriately. |
---|
498 | 590 | */ |
---|
499 | | - if (vma->vm->mm.color_adjust == NULL) |
---|
| 591 | + if (!i915_vm_has_cache_coloring(vma->vm)) |
---|
500 | 592 | return true; |
---|
501 | 593 | |
---|
502 | 594 | /* Only valid to be called on an already inserted vma */ |
---|
.. | .. |
---|
504 | 596 | GEM_BUG_ON(list_empty(&node->node_list)); |
---|
505 | 597 | |
---|
506 | 598 | other = list_prev_entry(node, node_list); |
---|
507 | | - if (color_differs(other, cache_level) && !drm_mm_hole_follows(other)) |
---|
| 599 | + if (i915_node_color_differs(other, color) && |
---|
| 600 | + !drm_mm_hole_follows(other)) |
---|
508 | 601 | return false; |
---|
509 | 602 | |
---|
510 | 603 | other = list_next_entry(node, node_list); |
---|
511 | | - if (color_differs(other, cache_level) && !drm_mm_hole_follows(node)) |
---|
| 604 | + if (i915_node_color_differs(other, color) && |
---|
| 605 | + !drm_mm_hole_follows(node)) |
---|
512 | 606 | return false; |
---|
513 | 607 | |
---|
514 | 608 | return true; |
---|
515 | | -} |
---|
516 | | - |
---|
517 | | -static void assert_bind_count(const struct drm_i915_gem_object *obj) |
---|
518 | | -{ |
---|
519 | | - /* |
---|
520 | | - * Combine the assertion that the object is bound and that we have |
---|
521 | | - * pinned its pages. But we should never have bound the object |
---|
522 | | - * more than we have pinned its pages. (For complete accuracy, we |
---|
523 | | - * assume that no else is pinning the pages, but as a rough assertion |
---|
524 | | - * that we will not run into problems later, this will do!) |
---|
525 | | - */ |
---|
526 | | - GEM_BUG_ON(atomic_read(&obj->mm.pages_pin_count) < obj->bind_count); |
---|
527 | 609 | } |
---|
528 | 610 | |
---|
529 | 611 | /** |
---|
.. | .. |
---|
543 | 625 | static int |
---|
544 | 626 | i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags) |
---|
545 | 627 | { |
---|
546 | | - struct drm_i915_private *dev_priv = vma->vm->i915; |
---|
547 | | - unsigned int cache_level; |
---|
| 628 | + unsigned long color; |
---|
548 | 629 | u64 start, end; |
---|
549 | 630 | int ret; |
---|
550 | 631 | |
---|
551 | | - GEM_BUG_ON(i915_vma_is_closed(vma)); |
---|
552 | | - GEM_BUG_ON(vma->flags & (I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND)); |
---|
| 632 | + GEM_BUG_ON(i915_vma_is_bound(vma, I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND)); |
---|
553 | 633 | GEM_BUG_ON(drm_mm_node_allocated(&vma->node)); |
---|
554 | 634 | |
---|
555 | 635 | size = max(size, vma->size); |
---|
.. | .. |
---|
569 | 649 | |
---|
570 | 650 | end = vma->vm->total; |
---|
571 | 651 | if (flags & PIN_MAPPABLE) |
---|
572 | | - end = min_t(u64, end, dev_priv->ggtt.mappable_end); |
---|
| 652 | + end = min_t(u64, end, i915_vm_to_ggtt(vma->vm)->mappable_end); |
---|
573 | 653 | if (flags & PIN_ZONE_4G) |
---|
574 | 654 | end = min_t(u64, end, (1ULL << 32) - I915_GTT_PAGE_SIZE); |
---|
575 | 655 | GEM_BUG_ON(!IS_ALIGNED(end, I915_GTT_PAGE_SIZE)); |
---|
.. | .. |
---|
585 | 665 | return -ENOSPC; |
---|
586 | 666 | } |
---|
587 | 667 | |
---|
588 | | - if (vma->obj) { |
---|
589 | | - ret = i915_gem_object_pin_pages(vma->obj); |
---|
590 | | - if (ret) |
---|
591 | | - return ret; |
---|
592 | | - |
---|
593 | | - cache_level = vma->obj->cache_level; |
---|
594 | | - } else { |
---|
595 | | - cache_level = 0; |
---|
596 | | - } |
---|
597 | | - |
---|
598 | | - GEM_BUG_ON(vma->pages); |
---|
599 | | - |
---|
600 | | - ret = vma->ops->set_pages(vma); |
---|
601 | | - if (ret) |
---|
602 | | - goto err_unpin; |
---|
| 668 | + color = 0; |
---|
| 669 | + if (vma->obj && i915_vm_has_cache_coloring(vma->vm)) |
---|
| 670 | + color = vma->obj->cache_level; |
---|
603 | 671 | |
---|
604 | 672 | if (flags & PIN_OFFSET_FIXED) { |
---|
605 | 673 | u64 offset = flags & PIN_OFFSET_MASK; |
---|
606 | 674 | if (!IS_ALIGNED(offset, alignment) || |
---|
607 | | - range_overflows(offset, size, end)) { |
---|
608 | | - ret = -EINVAL; |
---|
609 | | - goto err_clear; |
---|
610 | | - } |
---|
| 675 | + range_overflows(offset, size, end)) |
---|
| 676 | + return -EINVAL; |
---|
611 | 677 | |
---|
612 | 678 | ret = i915_gem_gtt_reserve(vma->vm, &vma->node, |
---|
613 | | - size, offset, cache_level, |
---|
| 679 | + size, offset, color, |
---|
614 | 680 | flags); |
---|
615 | 681 | if (ret) |
---|
616 | | - goto err_clear; |
---|
| 682 | + return ret; |
---|
617 | 683 | } else { |
---|
618 | 684 | /* |
---|
619 | 685 | * We only support huge gtt pages through the 48b PPGTT, |
---|
.. | .. |
---|
649 | 715 | } |
---|
650 | 716 | |
---|
651 | 717 | ret = i915_gem_gtt_insert(vma->vm, &vma->node, |
---|
652 | | - size, alignment, cache_level, |
---|
| 718 | + size, alignment, color, |
---|
653 | 719 | start, end, flags); |
---|
654 | 720 | if (ret) |
---|
655 | | - goto err_clear; |
---|
| 721 | + return ret; |
---|
656 | 722 | |
---|
657 | 723 | GEM_BUG_ON(vma->node.start < start); |
---|
658 | 724 | GEM_BUG_ON(vma->node.start + vma->node.size > end); |
---|
659 | 725 | } |
---|
660 | 726 | GEM_BUG_ON(!drm_mm_node_allocated(&vma->node)); |
---|
661 | | - GEM_BUG_ON(!i915_gem_valid_gtt_space(vma, cache_level)); |
---|
| 727 | + GEM_BUG_ON(!i915_gem_valid_gtt_space(vma, color)); |
---|
662 | 728 | |
---|
663 | | - list_move_tail(&vma->vm_link, &vma->vm->inactive_list); |
---|
664 | | - |
---|
665 | | - if (vma->obj) { |
---|
666 | | - struct drm_i915_gem_object *obj = vma->obj; |
---|
667 | | - |
---|
668 | | - spin_lock(&dev_priv->mm.obj_lock); |
---|
669 | | - list_move_tail(&obj->mm.link, &dev_priv->mm.bound_list); |
---|
670 | | - obj->bind_count++; |
---|
671 | | - spin_unlock(&dev_priv->mm.obj_lock); |
---|
672 | | - |
---|
673 | | - assert_bind_count(obj); |
---|
674 | | - } |
---|
| 729 | + list_add_tail(&vma->vm_link, &vma->vm->bound_list); |
---|
675 | 730 | |
---|
676 | 731 | return 0; |
---|
677 | | - |
---|
678 | | -err_clear: |
---|
679 | | - vma->ops->clear_pages(vma); |
---|
680 | | -err_unpin: |
---|
681 | | - if (vma->obj) |
---|
682 | | - i915_gem_object_unpin_pages(vma->obj); |
---|
683 | | - return ret; |
---|
684 | 732 | } |
---|
685 | 733 | |
---|
686 | 734 | static void |
---|
687 | | -i915_vma_remove(struct i915_vma *vma) |
---|
| 735 | +i915_vma_detach(struct i915_vma *vma) |
---|
688 | 736 | { |
---|
689 | | - struct drm_i915_private *i915 = vma->vm->i915; |
---|
690 | | - |
---|
691 | 737 | GEM_BUG_ON(!drm_mm_node_allocated(&vma->node)); |
---|
692 | | - GEM_BUG_ON(vma->flags & (I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND)); |
---|
693 | | - |
---|
694 | | - vma->ops->clear_pages(vma); |
---|
695 | | - |
---|
696 | | - drm_mm_remove_node(&vma->node); |
---|
697 | | - list_move_tail(&vma->vm_link, &vma->vm->unbound_list); |
---|
| 738 | + GEM_BUG_ON(i915_vma_is_bound(vma, I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND)); |
---|
698 | 739 | |
---|
699 | 740 | /* |
---|
700 | | - * Since the unbound list is global, only move to that list if |
---|
701 | | - * no more VMAs exist. |
---|
| 741 | + * And finally now the object is completely decoupled from this |
---|
| 742 | + * vma, we can drop its hold on the backing storage and allow |
---|
| 743 | + * it to be reaped by the shrinker. |
---|
702 | 744 | */ |
---|
703 | | - if (vma->obj) { |
---|
704 | | - struct drm_i915_gem_object *obj = vma->obj; |
---|
705 | | - |
---|
706 | | - spin_lock(&i915->mm.obj_lock); |
---|
707 | | - if (--obj->bind_count == 0) |
---|
708 | | - list_move_tail(&obj->mm.link, &i915->mm.unbound_list); |
---|
709 | | - spin_unlock(&i915->mm.obj_lock); |
---|
710 | | - |
---|
711 | | - /* |
---|
712 | | - * And finally now the object is completely decoupled from this |
---|
713 | | - * vma, we can drop its hold on the backing storage and allow |
---|
714 | | - * it to be reaped by the shrinker. |
---|
715 | | - */ |
---|
716 | | - i915_gem_object_unpin_pages(obj); |
---|
717 | | - assert_bind_count(obj); |
---|
718 | | - } |
---|
| 745 | + list_del(&vma->vm_link); |
---|
719 | 746 | } |
---|
720 | 747 | |
---|
721 | | -int __i915_vma_do_pin(struct i915_vma *vma, |
---|
722 | | - u64 size, u64 alignment, u64 flags) |
---|
| 748 | +static bool try_qad_pin(struct i915_vma *vma, unsigned int flags) |
---|
723 | 749 | { |
---|
724 | | - const unsigned int bound = vma->flags; |
---|
725 | | - int ret; |
---|
| 750 | + unsigned int bound; |
---|
| 751 | + bool pinned = true; |
---|
726 | 752 | |
---|
727 | | - lockdep_assert_held(&vma->vm->i915->drm.struct_mutex); |
---|
728 | | - GEM_BUG_ON((flags & (PIN_GLOBAL | PIN_USER)) == 0); |
---|
729 | | - GEM_BUG_ON((flags & PIN_GLOBAL) && !i915_vma_is_ggtt(vma)); |
---|
| 753 | + bound = atomic_read(&vma->flags); |
---|
| 754 | + do { |
---|
| 755 | + if (unlikely(flags & ~bound)) |
---|
| 756 | + return false; |
---|
730 | 757 | |
---|
731 | | - if (WARN_ON(bound & I915_VMA_PIN_OVERFLOW)) { |
---|
732 | | - ret = -EBUSY; |
---|
733 | | - goto err_unpin; |
---|
| 758 | + if (unlikely(bound & (I915_VMA_OVERFLOW | I915_VMA_ERROR))) |
---|
| 759 | + return false; |
---|
| 760 | + |
---|
| 761 | + if (!(bound & I915_VMA_PIN_MASK)) |
---|
| 762 | + goto unpinned; |
---|
| 763 | + |
---|
| 764 | + GEM_BUG_ON(((bound + 1) & I915_VMA_PIN_MASK) == 0); |
---|
| 765 | + } while (!atomic_try_cmpxchg(&vma->flags, &bound, bound + 1)); |
---|
| 766 | + |
---|
| 767 | + return true; |
---|
| 768 | + |
---|
| 769 | +unpinned: |
---|
| 770 | + /* |
---|
| 771 | + * If pin_count==0, but we are bound, check under the lock to avoid |
---|
| 772 | + * racing with a concurrent i915_vma_unbind(). |
---|
| 773 | + */ |
---|
| 774 | + mutex_lock(&vma->vm->mutex); |
---|
| 775 | + do { |
---|
| 776 | + if (unlikely(bound & (I915_VMA_OVERFLOW | I915_VMA_ERROR))) { |
---|
| 777 | + pinned = false; |
---|
| 778 | + break; |
---|
| 779 | + } |
---|
| 780 | + |
---|
| 781 | + if (unlikely(flags & ~bound)) { |
---|
| 782 | + pinned = false; |
---|
| 783 | + break; |
---|
| 784 | + } |
---|
| 785 | + } while (!atomic_try_cmpxchg(&vma->flags, &bound, bound + 1)); |
---|
| 786 | + mutex_unlock(&vma->vm->mutex); |
---|
| 787 | + |
---|
| 788 | + return pinned; |
---|
| 789 | +} |
---|
| 790 | + |
---|
| 791 | +static int vma_get_pages(struct i915_vma *vma) |
---|
| 792 | +{ |
---|
| 793 | + int err = 0; |
---|
| 794 | + |
---|
| 795 | + if (atomic_add_unless(&vma->pages_count, 1, 0)) |
---|
| 796 | + return 0; |
---|
| 797 | + |
---|
| 798 | + /* Allocations ahoy! */ |
---|
| 799 | + if (mutex_lock_interruptible(&vma->pages_mutex)) |
---|
| 800 | + return -EINTR; |
---|
| 801 | + |
---|
| 802 | + if (!atomic_read(&vma->pages_count)) { |
---|
| 803 | + if (vma->obj) { |
---|
| 804 | + err = i915_gem_object_pin_pages(vma->obj); |
---|
| 805 | + if (err) |
---|
| 806 | + goto unlock; |
---|
| 807 | + } |
---|
| 808 | + |
---|
| 809 | + err = vma->ops->set_pages(vma); |
---|
| 810 | + if (err) { |
---|
| 811 | + if (vma->obj) |
---|
| 812 | + i915_gem_object_unpin_pages(vma->obj); |
---|
| 813 | + goto unlock; |
---|
| 814 | + } |
---|
| 815 | + } |
---|
| 816 | + atomic_inc(&vma->pages_count); |
---|
| 817 | + |
---|
| 818 | +unlock: |
---|
| 819 | + mutex_unlock(&vma->pages_mutex); |
---|
| 820 | + |
---|
| 821 | + return err; |
---|
| 822 | +} |
---|
| 823 | + |
---|
| 824 | +static void __vma_put_pages(struct i915_vma *vma, unsigned int count) |
---|
| 825 | +{ |
---|
| 826 | + /* We allocate under vma_get_pages, so beware the shrinker */ |
---|
| 827 | + mutex_lock_nested(&vma->pages_mutex, SINGLE_DEPTH_NESTING); |
---|
| 828 | + GEM_BUG_ON(atomic_read(&vma->pages_count) < count); |
---|
| 829 | + if (atomic_sub_return(count, &vma->pages_count) == 0) { |
---|
| 830 | + vma->ops->clear_pages(vma); |
---|
| 831 | + GEM_BUG_ON(vma->pages); |
---|
| 832 | + if (vma->obj) |
---|
| 833 | + i915_gem_object_unpin_pages(vma->obj); |
---|
| 834 | + } |
---|
| 835 | + mutex_unlock(&vma->pages_mutex); |
---|
| 836 | +} |
---|
| 837 | + |
---|
| 838 | +static void vma_put_pages(struct i915_vma *vma) |
---|
| 839 | +{ |
---|
| 840 | + if (atomic_add_unless(&vma->pages_count, -1, 1)) |
---|
| 841 | + return; |
---|
| 842 | + |
---|
| 843 | + __vma_put_pages(vma, 1); |
---|
| 844 | +} |
---|
| 845 | + |
---|
| 846 | +static void vma_unbind_pages(struct i915_vma *vma) |
---|
| 847 | +{ |
---|
| 848 | + unsigned int count; |
---|
| 849 | + |
---|
| 850 | + lockdep_assert_held(&vma->vm->mutex); |
---|
| 851 | + |
---|
| 852 | + /* The upper portion of pages_count is the number of bindings */ |
---|
| 853 | + count = atomic_read(&vma->pages_count); |
---|
| 854 | + count >>= I915_VMA_PAGES_BIAS; |
---|
| 855 | + GEM_BUG_ON(!count); |
---|
| 856 | + |
---|
| 857 | + __vma_put_pages(vma, count | count << I915_VMA_PAGES_BIAS); |
---|
| 858 | +} |
---|
| 859 | + |
---|
| 860 | +int i915_vma_pin_ww(struct i915_vma *vma, struct i915_gem_ww_ctx *ww, |
---|
| 861 | + u64 size, u64 alignment, u64 flags) |
---|
| 862 | +{ |
---|
| 863 | + struct i915_vma_work *work = NULL; |
---|
| 864 | + intel_wakeref_t wakeref = 0; |
---|
| 865 | + unsigned int bound; |
---|
| 866 | + int err; |
---|
| 867 | + |
---|
| 868 | +#ifdef CONFIG_PROVE_LOCKING |
---|
| 869 | + if (debug_locks && lockdep_is_held(&vma->vm->i915->drm.struct_mutex)) |
---|
| 870 | + WARN_ON(!ww); |
---|
| 871 | +#endif |
---|
| 872 | + |
---|
| 873 | + BUILD_BUG_ON(PIN_GLOBAL != I915_VMA_GLOBAL_BIND); |
---|
| 874 | + BUILD_BUG_ON(PIN_USER != I915_VMA_LOCAL_BIND); |
---|
| 875 | + |
---|
| 876 | + GEM_BUG_ON(!(flags & (PIN_USER | PIN_GLOBAL))); |
---|
| 877 | + |
---|
| 878 | + /* First try and grab the pin without rebinding the vma */ |
---|
| 879 | + if (try_qad_pin(vma, flags & I915_VMA_BIND_MASK)) |
---|
| 880 | + return 0; |
---|
| 881 | + |
---|
| 882 | + err = vma_get_pages(vma); |
---|
| 883 | + if (err) |
---|
| 884 | + return err; |
---|
| 885 | + |
---|
| 886 | + if (flags & PIN_GLOBAL) |
---|
| 887 | + wakeref = intel_runtime_pm_get(&vma->vm->i915->runtime_pm); |
---|
| 888 | + |
---|
| 889 | + if (flags & vma->vm->bind_async_flags) { |
---|
| 890 | + work = i915_vma_work(); |
---|
| 891 | + if (!work) { |
---|
| 892 | + err = -ENOMEM; |
---|
| 893 | + goto err_rpm; |
---|
| 894 | + } |
---|
| 895 | + |
---|
| 896 | + work->vm = i915_vm_get(vma->vm); |
---|
| 897 | + |
---|
| 898 | + /* Allocate enough page directories to used PTE */ |
---|
| 899 | + if (vma->vm->allocate_va_range) { |
---|
| 900 | + err = i915_vm_alloc_pt_stash(vma->vm, |
---|
| 901 | + &work->stash, |
---|
| 902 | + vma->size); |
---|
| 903 | + if (err) |
---|
| 904 | + goto err_fence; |
---|
| 905 | + |
---|
| 906 | + err = i915_vm_pin_pt_stash(vma->vm, |
---|
| 907 | + &work->stash); |
---|
| 908 | + if (err) |
---|
| 909 | + goto err_fence; |
---|
| 910 | + } |
---|
734 | 911 | } |
---|
735 | 912 | |
---|
736 | | - if ((bound & I915_VMA_BIND_MASK) == 0) { |
---|
737 | | - ret = i915_vma_insert(vma, size, alignment, flags); |
---|
738 | | - if (ret) |
---|
739 | | - goto err_unpin; |
---|
740 | | - } |
---|
741 | | - GEM_BUG_ON(!drm_mm_node_allocated(&vma->node)); |
---|
| 913 | + /* |
---|
| 914 | + * Differentiate between user/kernel vma inside the aliasing-ppgtt. |
---|
| 915 | + * |
---|
| 916 | + * We conflate the Global GTT with the user's vma when using the |
---|
| 917 | + * aliasing-ppgtt, but it is still vitally important to try and |
---|
| 918 | + * keep the use cases distinct. For example, userptr objects are |
---|
| 919 | + * not allowed inside the Global GTT as that will cause lock |
---|
| 920 | + * inversions when we have to evict them the mmu_notifier callbacks - |
---|
| 921 | + * but they are allowed to be part of the user ppGTT which can never |
---|
| 922 | + * be mapped. As such we try to give the distinct users of the same |
---|
| 923 | + * mutex, distinct lockclasses [equivalent to how we keep i915_ggtt |
---|
| 924 | + * and i915_ppgtt separate]. |
---|
| 925 | + * |
---|
| 926 | + * NB this may cause us to mask real lock inversions -- while the |
---|
| 927 | + * code is safe today, lockdep may not be able to spot future |
---|
| 928 | + * transgressions. |
---|
| 929 | + */ |
---|
| 930 | + err = mutex_lock_interruptible_nested(&vma->vm->mutex, |
---|
| 931 | + !(flags & PIN_GLOBAL)); |
---|
| 932 | + if (err) |
---|
| 933 | + goto err_fence; |
---|
742 | 934 | |
---|
743 | | - ret = i915_vma_bind(vma, vma->obj ? vma->obj->cache_level : 0, flags); |
---|
744 | | - if (ret) |
---|
| 935 | + /* No more allocations allowed now we hold vm->mutex */ |
---|
| 936 | + |
---|
| 937 | + if (unlikely(i915_vma_is_closed(vma))) { |
---|
| 938 | + err = -ENOENT; |
---|
| 939 | + goto err_unlock; |
---|
| 940 | + } |
---|
| 941 | + |
---|
| 942 | + bound = atomic_read(&vma->flags); |
---|
| 943 | + if (unlikely(bound & I915_VMA_ERROR)) { |
---|
| 944 | + err = -ENOMEM; |
---|
| 945 | + goto err_unlock; |
---|
| 946 | + } |
---|
| 947 | + |
---|
| 948 | + if (unlikely(!((bound + 1) & I915_VMA_PIN_MASK))) { |
---|
| 949 | + err = -EAGAIN; /* pins are meant to be fairly temporary */ |
---|
| 950 | + goto err_unlock; |
---|
| 951 | + } |
---|
| 952 | + |
---|
| 953 | + if (unlikely(!(flags & ~bound & I915_VMA_BIND_MASK))) { |
---|
| 954 | + __i915_vma_pin(vma); |
---|
| 955 | + goto err_unlock; |
---|
| 956 | + } |
---|
| 957 | + |
---|
| 958 | + err = i915_active_acquire(&vma->active); |
---|
| 959 | + if (err) |
---|
| 960 | + goto err_unlock; |
---|
| 961 | + |
---|
| 962 | + if (!(bound & I915_VMA_BIND_MASK)) { |
---|
| 963 | + err = i915_vma_insert(vma, size, alignment, flags); |
---|
| 964 | + if (err) |
---|
| 965 | + goto err_active; |
---|
| 966 | + |
---|
| 967 | + if (i915_is_ggtt(vma->vm)) |
---|
| 968 | + __i915_vma_set_map_and_fenceable(vma); |
---|
| 969 | + } |
---|
| 970 | + |
---|
| 971 | + GEM_BUG_ON(!vma->pages); |
---|
| 972 | + err = i915_vma_bind(vma, |
---|
| 973 | + vma->obj ? vma->obj->cache_level : 0, |
---|
| 974 | + flags, work); |
---|
| 975 | + if (err) |
---|
745 | 976 | goto err_remove; |
---|
746 | 977 | |
---|
747 | | - GEM_BUG_ON((vma->flags & I915_VMA_BIND_MASK) == 0); |
---|
| 978 | + /* There should only be at most 2 active bindings (user, global) */ |
---|
| 979 | + GEM_BUG_ON(bound + I915_VMA_PAGES_ACTIVE < bound); |
---|
| 980 | + atomic_add(I915_VMA_PAGES_ACTIVE, &vma->pages_count); |
---|
| 981 | + list_move_tail(&vma->vm_link, &vma->vm->bound_list); |
---|
748 | 982 | |
---|
749 | | - if ((bound ^ vma->flags) & I915_VMA_GLOBAL_BIND) |
---|
750 | | - __i915_vma_set_map_and_fenceable(vma); |
---|
751 | | - |
---|
| 983 | + __i915_vma_pin(vma); |
---|
| 984 | + GEM_BUG_ON(!i915_vma_is_pinned(vma)); |
---|
| 985 | + GEM_BUG_ON(!i915_vma_is_bound(vma, flags)); |
---|
752 | 986 | GEM_BUG_ON(i915_vma_misplaced(vma, size, alignment, flags)); |
---|
753 | | - return 0; |
---|
754 | 987 | |
---|
755 | 988 | err_remove: |
---|
756 | | - if ((bound & I915_VMA_BIND_MASK) == 0) { |
---|
757 | | - i915_vma_remove(vma); |
---|
758 | | - GEM_BUG_ON(vma->pages); |
---|
759 | | - GEM_BUG_ON(vma->flags & I915_VMA_BIND_MASK); |
---|
| 989 | + if (!i915_vma_is_bound(vma, I915_VMA_BIND_MASK)) { |
---|
| 990 | + i915_vma_detach(vma); |
---|
| 991 | + drm_mm_remove_node(&vma->node); |
---|
760 | 992 | } |
---|
761 | | -err_unpin: |
---|
762 | | - __i915_vma_unpin(vma); |
---|
763 | | - return ret; |
---|
| 993 | +err_active: |
---|
| 994 | + i915_active_release(&vma->active); |
---|
| 995 | +err_unlock: |
---|
| 996 | + mutex_unlock(&vma->vm->mutex); |
---|
| 997 | +err_fence: |
---|
| 998 | + if (work) |
---|
| 999 | + dma_fence_work_commit_imm(&work->base); |
---|
| 1000 | +err_rpm: |
---|
| 1001 | + if (wakeref) |
---|
| 1002 | + intel_runtime_pm_put(&vma->vm->i915->runtime_pm, wakeref); |
---|
| 1003 | + vma_put_pages(vma); |
---|
| 1004 | + return err; |
---|
764 | 1005 | } |
---|
765 | 1006 | |
---|
766 | | -void i915_vma_close(struct i915_vma *vma) |
---|
| 1007 | +static void flush_idle_contexts(struct intel_gt *gt) |
---|
767 | 1008 | { |
---|
768 | | - lockdep_assert_held(&vma->vm->i915->drm.struct_mutex); |
---|
| 1009 | + struct intel_engine_cs *engine; |
---|
| 1010 | + enum intel_engine_id id; |
---|
769 | 1011 | |
---|
770 | | - GEM_BUG_ON(i915_vma_is_closed(vma)); |
---|
771 | | - vma->flags |= I915_VMA_CLOSED; |
---|
| 1012 | + for_each_engine(engine, gt, id) |
---|
| 1013 | + intel_engine_flush_barriers(engine); |
---|
772 | 1014 | |
---|
| 1015 | + intel_gt_wait_for_idle(gt, MAX_SCHEDULE_TIMEOUT); |
---|
| 1016 | +} |
---|
| 1017 | + |
---|
| 1018 | +int i915_ggtt_pin(struct i915_vma *vma, struct i915_gem_ww_ctx *ww, |
---|
| 1019 | + u32 align, unsigned int flags) |
---|
| 1020 | +{ |
---|
| 1021 | + struct i915_address_space *vm = vma->vm; |
---|
| 1022 | + int err; |
---|
| 1023 | + |
---|
| 1024 | + GEM_BUG_ON(!i915_vma_is_ggtt(vma)); |
---|
| 1025 | + |
---|
| 1026 | + do { |
---|
| 1027 | + err = i915_vma_pin_ww(vma, ww, 0, align, flags | PIN_GLOBAL); |
---|
| 1028 | + if (err != -ENOSPC) { |
---|
| 1029 | + if (!err) { |
---|
| 1030 | + err = i915_vma_wait_for_bind(vma); |
---|
| 1031 | + if (err) |
---|
| 1032 | + i915_vma_unpin(vma); |
---|
| 1033 | + } |
---|
| 1034 | + return err; |
---|
| 1035 | + } |
---|
| 1036 | + |
---|
| 1037 | + /* Unlike i915_vma_pin, we don't take no for an answer! */ |
---|
| 1038 | + flush_idle_contexts(vm->gt); |
---|
| 1039 | + if (mutex_lock_interruptible(&vm->mutex) == 0) { |
---|
| 1040 | + i915_gem_evict_vm(vm); |
---|
| 1041 | + mutex_unlock(&vm->mutex); |
---|
| 1042 | + } |
---|
| 1043 | + } while (1); |
---|
| 1044 | +} |
---|
| 1045 | + |
---|
| 1046 | +static void __vma_close(struct i915_vma *vma, struct intel_gt *gt) |
---|
| 1047 | +{ |
---|
773 | 1048 | /* |
---|
774 | 1049 | * We defer actually closing, unbinding and destroying the VMA until |
---|
775 | 1050 | * the next idle point, or if the object is freed in the meantime. By |
---|
.. | .. |
---|
782 | 1057 | * causing us to rebind the VMA once more. This ends up being a lot |
---|
783 | 1058 | * of wasted work for the steady state. |
---|
784 | 1059 | */ |
---|
785 | | - list_add_tail(&vma->closed_link, &vma->vm->i915->gt.closed_vma); |
---|
| 1060 | + GEM_BUG_ON(i915_vma_is_closed(vma)); |
---|
| 1061 | + list_add(&vma->closed_link, >->closed_vma); |
---|
| 1062 | +} |
---|
| 1063 | + |
---|
| 1064 | +void i915_vma_close(struct i915_vma *vma) |
---|
| 1065 | +{ |
---|
| 1066 | + struct intel_gt *gt = vma->vm->gt; |
---|
| 1067 | + unsigned long flags; |
---|
| 1068 | + |
---|
| 1069 | + if (i915_vma_is_ggtt(vma)) |
---|
| 1070 | + return; |
---|
| 1071 | + |
---|
| 1072 | + GEM_BUG_ON(!atomic_read(&vma->open_count)); |
---|
| 1073 | + if (atomic_dec_and_lock_irqsave(&vma->open_count, |
---|
| 1074 | + >->closed_lock, |
---|
| 1075 | + flags)) { |
---|
| 1076 | + __vma_close(vma, gt); |
---|
| 1077 | + spin_unlock_irqrestore(>->closed_lock, flags); |
---|
| 1078 | + } |
---|
| 1079 | +} |
---|
| 1080 | + |
---|
| 1081 | +static void __i915_vma_remove_closed(struct i915_vma *vma) |
---|
| 1082 | +{ |
---|
| 1083 | + struct intel_gt *gt = vma->vm->gt; |
---|
| 1084 | + |
---|
| 1085 | + spin_lock_irq(>->closed_lock); |
---|
| 1086 | + list_del_init(&vma->closed_link); |
---|
| 1087 | + spin_unlock_irq(>->closed_lock); |
---|
786 | 1088 | } |
---|
787 | 1089 | |
---|
788 | 1090 | void i915_vma_reopen(struct i915_vma *vma) |
---|
789 | 1091 | { |
---|
790 | | - lockdep_assert_held(&vma->vm->i915->drm.struct_mutex); |
---|
791 | | - |
---|
792 | | - if (vma->flags & I915_VMA_CLOSED) { |
---|
793 | | - vma->flags &= ~I915_VMA_CLOSED; |
---|
794 | | - list_del(&vma->closed_link); |
---|
795 | | - } |
---|
796 | | -} |
---|
797 | | - |
---|
798 | | -static void __i915_vma_destroy(struct i915_vma *vma) |
---|
799 | | -{ |
---|
800 | | - struct drm_i915_private *i915 = vma->vm->i915; |
---|
801 | | - struct i915_vma_active *iter, *n; |
---|
802 | | - |
---|
803 | | - GEM_BUG_ON(vma->node.allocated); |
---|
804 | | - GEM_BUG_ON(vma->fence); |
---|
805 | | - |
---|
806 | | - GEM_BUG_ON(i915_gem_active_isset(&vma->last_fence)); |
---|
807 | | - |
---|
808 | | - list_del(&vma->obj_link); |
---|
809 | | - list_del(&vma->vm_link); |
---|
810 | | - if (vma->obj) |
---|
811 | | - rb_erase(&vma->obj_node, &vma->obj->vma_tree); |
---|
812 | | - |
---|
813 | | - rbtree_postorder_for_each_entry_safe(iter, n, &vma->active, node) { |
---|
814 | | - GEM_BUG_ON(i915_gem_active_isset(&iter->base)); |
---|
815 | | - kfree(iter); |
---|
816 | | - } |
---|
817 | | - |
---|
818 | | - kmem_cache_free(i915->vmas, vma); |
---|
819 | | -} |
---|
820 | | - |
---|
821 | | -void i915_vma_destroy(struct i915_vma *vma) |
---|
822 | | -{ |
---|
823 | | - lockdep_assert_held(&vma->vm->i915->drm.struct_mutex); |
---|
824 | | - |
---|
825 | | - GEM_BUG_ON(i915_vma_is_active(vma)); |
---|
826 | | - GEM_BUG_ON(i915_vma_is_pinned(vma)); |
---|
827 | | - |
---|
828 | 1092 | if (i915_vma_is_closed(vma)) |
---|
829 | | - list_del(&vma->closed_link); |
---|
830 | | - |
---|
831 | | - WARN_ON(i915_vma_unbind(vma)); |
---|
832 | | - __i915_vma_destroy(vma); |
---|
| 1093 | + __i915_vma_remove_closed(vma); |
---|
833 | 1094 | } |
---|
834 | 1095 | |
---|
835 | | -void i915_vma_parked(struct drm_i915_private *i915) |
---|
| 1096 | +void i915_vma_release(struct kref *ref) |
---|
| 1097 | +{ |
---|
| 1098 | + struct i915_vma *vma = container_of(ref, typeof(*vma), ref); |
---|
| 1099 | + |
---|
| 1100 | + if (drm_mm_node_allocated(&vma->node)) { |
---|
| 1101 | + mutex_lock(&vma->vm->mutex); |
---|
| 1102 | + atomic_and(~I915_VMA_PIN_MASK, &vma->flags); |
---|
| 1103 | + WARN_ON(__i915_vma_unbind(vma)); |
---|
| 1104 | + mutex_unlock(&vma->vm->mutex); |
---|
| 1105 | + GEM_BUG_ON(drm_mm_node_allocated(&vma->node)); |
---|
| 1106 | + } |
---|
| 1107 | + GEM_BUG_ON(i915_vma_is_active(vma)); |
---|
| 1108 | + |
---|
| 1109 | + if (vma->obj) { |
---|
| 1110 | + struct drm_i915_gem_object *obj = vma->obj; |
---|
| 1111 | + |
---|
| 1112 | + spin_lock(&obj->vma.lock); |
---|
| 1113 | + list_del(&vma->obj_link); |
---|
| 1114 | + if (!RB_EMPTY_NODE(&vma->obj_node)) |
---|
| 1115 | + rb_erase(&vma->obj_node, &obj->vma.tree); |
---|
| 1116 | + spin_unlock(&obj->vma.lock); |
---|
| 1117 | + } |
---|
| 1118 | + |
---|
| 1119 | + __i915_vma_remove_closed(vma); |
---|
| 1120 | + i915_vm_put(vma->vm); |
---|
| 1121 | + |
---|
| 1122 | + i915_active_fini(&vma->active); |
---|
| 1123 | + i915_vma_free(vma); |
---|
| 1124 | +} |
---|
| 1125 | + |
---|
| 1126 | +void i915_vma_parked(struct intel_gt *gt) |
---|
836 | 1127 | { |
---|
837 | 1128 | struct i915_vma *vma, *next; |
---|
| 1129 | + LIST_HEAD(closed); |
---|
838 | 1130 | |
---|
839 | | - list_for_each_entry_safe(vma, next, &i915->gt.closed_vma, closed_link) { |
---|
840 | | - GEM_BUG_ON(!i915_vma_is_closed(vma)); |
---|
841 | | - i915_vma_destroy(vma); |
---|
| 1131 | + spin_lock_irq(>->closed_lock); |
---|
| 1132 | + list_for_each_entry_safe(vma, next, >->closed_vma, closed_link) { |
---|
| 1133 | + struct drm_i915_gem_object *obj = vma->obj; |
---|
| 1134 | + struct i915_address_space *vm = vma->vm; |
---|
| 1135 | + |
---|
| 1136 | + /* XXX All to avoid keeping a reference on i915_vma itself */ |
---|
| 1137 | + |
---|
| 1138 | + if (!kref_get_unless_zero(&obj->base.refcount)) |
---|
| 1139 | + continue; |
---|
| 1140 | + |
---|
| 1141 | + if (!i915_vm_tryopen(vm)) { |
---|
| 1142 | + i915_gem_object_put(obj); |
---|
| 1143 | + continue; |
---|
| 1144 | + } |
---|
| 1145 | + |
---|
| 1146 | + list_move(&vma->closed_link, &closed); |
---|
842 | 1147 | } |
---|
| 1148 | + spin_unlock_irq(>->closed_lock); |
---|
843 | 1149 | |
---|
844 | | - GEM_BUG_ON(!list_empty(&i915->gt.closed_vma)); |
---|
| 1150 | + /* As the GT is held idle, no vma can be reopened as we destroy them */ |
---|
| 1151 | + list_for_each_entry_safe(vma, next, &closed, closed_link) { |
---|
| 1152 | + struct drm_i915_gem_object *obj = vma->obj; |
---|
| 1153 | + struct i915_address_space *vm = vma->vm; |
---|
| 1154 | + |
---|
| 1155 | + INIT_LIST_HEAD(&vma->closed_link); |
---|
| 1156 | + __i915_vma_put(vma); |
---|
| 1157 | + |
---|
| 1158 | + i915_gem_object_put(obj); |
---|
| 1159 | + i915_vm_close(vm); |
---|
| 1160 | + } |
---|
845 | 1161 | } |
---|
846 | 1162 | |
---|
847 | 1163 | static void __i915_vma_iounmap(struct i915_vma *vma) |
---|
.. | .. |
---|
857 | 1173 | |
---|
858 | 1174 | void i915_vma_revoke_mmap(struct i915_vma *vma) |
---|
859 | 1175 | { |
---|
860 | | - struct drm_vma_offset_node *node = &vma->obj->base.vma_node; |
---|
| 1176 | + struct drm_vma_offset_node *node; |
---|
861 | 1177 | u64 vma_offset; |
---|
862 | | - |
---|
863 | | - lockdep_assert_held(&vma->vm->i915->drm.struct_mutex); |
---|
864 | 1178 | |
---|
865 | 1179 | if (!i915_vma_has_userfault(vma)) |
---|
866 | 1180 | return; |
---|
.. | .. |
---|
868 | 1182 | GEM_BUG_ON(!i915_vma_is_map_and_fenceable(vma)); |
---|
869 | 1183 | GEM_BUG_ON(!vma->obj->userfault_count); |
---|
870 | 1184 | |
---|
| 1185 | + node = &vma->mmo->vma_node; |
---|
871 | 1186 | vma_offset = vma->ggtt_view.partial.offset << PAGE_SHIFT; |
---|
872 | 1187 | unmap_mapping_range(vma->vm->i915->drm.anon_inode->i_mapping, |
---|
873 | 1188 | drm_vma_node_offset_addr(node) + vma_offset, |
---|
.. | .. |
---|
879 | 1194 | list_del(&vma->obj->userfault_link); |
---|
880 | 1195 | } |
---|
881 | 1196 | |
---|
882 | | -static void export_fence(struct i915_vma *vma, |
---|
883 | | - struct i915_request *rq, |
---|
884 | | - unsigned int flags) |
---|
| 1197 | +static int |
---|
| 1198 | +__i915_request_await_bind(struct i915_request *rq, struct i915_vma *vma) |
---|
885 | 1199 | { |
---|
886 | | - struct reservation_object *resv = vma->resv; |
---|
887 | | - |
---|
888 | | - /* |
---|
889 | | - * Ignore errors from failing to allocate the new fence, we can't |
---|
890 | | - * handle an error right now. Worst case should be missed |
---|
891 | | - * synchronisation leading to rendering corruption. |
---|
892 | | - */ |
---|
893 | | - reservation_object_lock(resv, NULL); |
---|
894 | | - if (flags & EXEC_OBJECT_WRITE) |
---|
895 | | - reservation_object_add_excl_fence(resv, &rq->fence); |
---|
896 | | - else if (reservation_object_reserve_shared(resv) == 0) |
---|
897 | | - reservation_object_add_shared_fence(resv, &rq->fence); |
---|
898 | | - reservation_object_unlock(resv); |
---|
| 1200 | + return __i915_request_await_exclusive(rq, &vma->active); |
---|
899 | 1201 | } |
---|
900 | 1202 | |
---|
901 | | -static struct i915_gem_active *active_instance(struct i915_vma *vma, u64 idx) |
---|
| 1203 | +int __i915_vma_move_to_active(struct i915_vma *vma, struct i915_request *rq) |
---|
902 | 1204 | { |
---|
903 | | - struct i915_vma_active *active; |
---|
904 | | - struct rb_node **p, *parent; |
---|
905 | | - struct i915_request *old; |
---|
| 1205 | + int err; |
---|
906 | 1206 | |
---|
907 | | - /* |
---|
908 | | - * We track the most recently used timeline to skip a rbtree search |
---|
909 | | - * for the common case, under typical loads we never need the rbtree |
---|
910 | | - * at all. We can reuse the last_active slot if it is empty, that is |
---|
911 | | - * after the previous activity has been retired, or if the active |
---|
912 | | - * matches the current timeline. |
---|
913 | | - * |
---|
914 | | - * Note that we allow the timeline to be active simultaneously in |
---|
915 | | - * the rbtree and the last_active cache. We do this to avoid having |
---|
916 | | - * to search and replace the rbtree element for a new timeline, with |
---|
917 | | - * the cost being that we must be aware that the vma may be retired |
---|
918 | | - * twice for the same timeline (as the older rbtree element will be |
---|
919 | | - * retired before the new request added to last_active). |
---|
920 | | - */ |
---|
921 | | - old = i915_gem_active_raw(&vma->last_active, |
---|
922 | | - &vma->vm->i915->drm.struct_mutex); |
---|
923 | | - if (!old || old->fence.context == idx) |
---|
924 | | - goto out; |
---|
| 1207 | + GEM_BUG_ON(!i915_vma_is_pinned(vma)); |
---|
925 | 1208 | |
---|
926 | | - /* Move the currently active fence into the rbtree */ |
---|
927 | | - idx = old->fence.context; |
---|
| 1209 | + /* Wait for the vma to be bound before we start! */ |
---|
| 1210 | + err = __i915_request_await_bind(rq, vma); |
---|
| 1211 | + if (err) |
---|
| 1212 | + return err; |
---|
928 | 1213 | |
---|
929 | | - parent = NULL; |
---|
930 | | - p = &vma->active.rb_node; |
---|
931 | | - while (*p) { |
---|
932 | | - parent = *p; |
---|
933 | | - |
---|
934 | | - active = rb_entry(parent, struct i915_vma_active, node); |
---|
935 | | - if (active->timeline == idx) |
---|
936 | | - goto replace; |
---|
937 | | - |
---|
938 | | - if (active->timeline < idx) |
---|
939 | | - p = &parent->rb_right; |
---|
940 | | - else |
---|
941 | | - p = &parent->rb_left; |
---|
942 | | - } |
---|
943 | | - |
---|
944 | | - active = kmalloc(sizeof(*active), GFP_KERNEL); |
---|
945 | | - |
---|
946 | | - /* kmalloc may retire the vma->last_active request (thanks shrinker)! */ |
---|
947 | | - if (unlikely(!i915_gem_active_raw(&vma->last_active, |
---|
948 | | - &vma->vm->i915->drm.struct_mutex))) { |
---|
949 | | - kfree(active); |
---|
950 | | - goto out; |
---|
951 | | - } |
---|
952 | | - |
---|
953 | | - if (unlikely(!active)) |
---|
954 | | - return ERR_PTR(-ENOMEM); |
---|
955 | | - |
---|
956 | | - init_request_active(&active->base, i915_vma_retire); |
---|
957 | | - active->vma = vma; |
---|
958 | | - active->timeline = idx; |
---|
959 | | - |
---|
960 | | - rb_link_node(&active->node, parent, p); |
---|
961 | | - rb_insert_color(&active->node, &vma->active); |
---|
962 | | - |
---|
963 | | -replace: |
---|
964 | | - /* |
---|
965 | | - * Overwrite the previous active slot in the rbtree with last_active, |
---|
966 | | - * leaving last_active zeroed. If the previous slot is still active, |
---|
967 | | - * we must be careful as we now only expect to receive one retire |
---|
968 | | - * callback not two, and so much undo the active counting for the |
---|
969 | | - * overwritten slot. |
---|
970 | | - */ |
---|
971 | | - if (i915_gem_active_isset(&active->base)) { |
---|
972 | | - /* Retire ourselves from the old rq->active_list */ |
---|
973 | | - __list_del_entry(&active->base.link); |
---|
974 | | - vma->active_count--; |
---|
975 | | - GEM_BUG_ON(!vma->active_count); |
---|
976 | | - } |
---|
977 | | - GEM_BUG_ON(list_empty(&vma->last_active.link)); |
---|
978 | | - list_replace_init(&vma->last_active.link, &active->base.link); |
---|
979 | | - active->base.request = fetch_and_zero(&vma->last_active.request); |
---|
980 | | - |
---|
981 | | -out: |
---|
982 | | - return &vma->last_active; |
---|
| 1214 | + return i915_active_add_request(&vma->active, rq); |
---|
983 | 1215 | } |
---|
984 | 1216 | |
---|
985 | 1217 | int i915_vma_move_to_active(struct i915_vma *vma, |
---|
.. | .. |
---|
987 | 1219 | unsigned int flags) |
---|
988 | 1220 | { |
---|
989 | 1221 | struct drm_i915_gem_object *obj = vma->obj; |
---|
990 | | - struct i915_gem_active *active; |
---|
| 1222 | + int err; |
---|
991 | 1223 | |
---|
992 | | - lockdep_assert_held(&rq->i915->drm.struct_mutex); |
---|
993 | | - GEM_BUG_ON(!drm_mm_node_allocated(&vma->node)); |
---|
| 1224 | + assert_object_held(obj); |
---|
994 | 1225 | |
---|
995 | | - active = active_instance(vma, rq->fence.context); |
---|
996 | | - if (IS_ERR(active)) |
---|
997 | | - return PTR_ERR(active); |
---|
| 1226 | + err = __i915_vma_move_to_active(vma, rq); |
---|
| 1227 | + if (unlikely(err)) |
---|
| 1228 | + return err; |
---|
998 | 1229 | |
---|
999 | | - /* |
---|
1000 | | - * Add a reference if we're newly entering the active list. |
---|
1001 | | - * The order in which we add operations to the retirement queue is |
---|
1002 | | - * vital here: mark_active adds to the start of the callback list, |
---|
1003 | | - * such that subsequent callbacks are called first. Therefore we |
---|
1004 | | - * add the active reference first and queue for it to be dropped |
---|
1005 | | - * *last*. |
---|
1006 | | - */ |
---|
1007 | | - if (!i915_gem_active_isset(active) && !vma->active_count++) { |
---|
1008 | | - list_move_tail(&vma->vm_link, &vma->vm->active_list); |
---|
1009 | | - obj->active_count++; |
---|
1010 | | - } |
---|
1011 | | - i915_gem_active_set(active, rq); |
---|
1012 | | - GEM_BUG_ON(!i915_vma_is_active(vma)); |
---|
1013 | | - GEM_BUG_ON(!obj->active_count); |
---|
1014 | | - |
---|
1015 | | - obj->write_domain = 0; |
---|
1016 | 1230 | if (flags & EXEC_OBJECT_WRITE) { |
---|
| 1231 | + struct intel_frontbuffer *front; |
---|
| 1232 | + |
---|
| 1233 | + front = __intel_frontbuffer_get(obj); |
---|
| 1234 | + if (unlikely(front)) { |
---|
| 1235 | + if (intel_frontbuffer_invalidate(front, ORIGIN_CS)) |
---|
| 1236 | + i915_active_add_request(&front->write, rq); |
---|
| 1237 | + intel_frontbuffer_put(front); |
---|
| 1238 | + } |
---|
| 1239 | + |
---|
| 1240 | + dma_resv_add_excl_fence(vma->resv, &rq->fence); |
---|
1017 | 1241 | obj->write_domain = I915_GEM_DOMAIN_RENDER; |
---|
1018 | | - |
---|
1019 | | - if (intel_fb_obj_invalidate(obj, ORIGIN_CS)) |
---|
1020 | | - i915_gem_active_set(&obj->frontbuffer_write, rq); |
---|
1021 | | - |
---|
1022 | 1242 | obj->read_domains = 0; |
---|
| 1243 | + } else { |
---|
| 1244 | + err = dma_resv_reserve_shared(vma->resv, 1); |
---|
| 1245 | + if (unlikely(err)) |
---|
| 1246 | + return err; |
---|
| 1247 | + |
---|
| 1248 | + dma_resv_add_shared_fence(vma->resv, &rq->fence); |
---|
| 1249 | + obj->write_domain = 0; |
---|
1023 | 1250 | } |
---|
| 1251 | + |
---|
| 1252 | + if (flags & EXEC_OBJECT_NEEDS_FENCE && vma->fence) |
---|
| 1253 | + i915_active_add_request(&vma->fence->active, rq); |
---|
| 1254 | + |
---|
1024 | 1255 | obj->read_domains |= I915_GEM_GPU_DOMAINS; |
---|
| 1256 | + obj->mm.dirty = true; |
---|
1025 | 1257 | |
---|
1026 | | - if (flags & EXEC_OBJECT_NEEDS_FENCE) |
---|
1027 | | - i915_gem_active_set(&vma->last_fence, rq); |
---|
1028 | | - |
---|
1029 | | - export_fence(vma, rq, flags); |
---|
| 1258 | + GEM_BUG_ON(!i915_vma_is_active(vma)); |
---|
1030 | 1259 | return 0; |
---|
1031 | 1260 | } |
---|
1032 | 1261 | |
---|
1033 | | -int i915_vma_unbind(struct i915_vma *vma) |
---|
| 1262 | +void __i915_vma_evict(struct i915_vma *vma) |
---|
1034 | 1263 | { |
---|
1035 | | - int ret; |
---|
1036 | | - |
---|
1037 | | - lockdep_assert_held(&vma->vm->i915->drm.struct_mutex); |
---|
1038 | | - |
---|
1039 | | - /* |
---|
1040 | | - * First wait upon any activity as retiring the request may |
---|
1041 | | - * have side-effects such as unpinning or even unbinding this vma. |
---|
1042 | | - */ |
---|
1043 | | - might_sleep(); |
---|
1044 | | - if (i915_vma_is_active(vma)) { |
---|
1045 | | - struct i915_vma_active *active, *n; |
---|
1046 | | - |
---|
1047 | | - /* |
---|
1048 | | - * When a closed VMA is retired, it is unbound - eek. |
---|
1049 | | - * In order to prevent it from being recursively closed, |
---|
1050 | | - * take a pin on the vma so that the second unbind is |
---|
1051 | | - * aborted. |
---|
1052 | | - * |
---|
1053 | | - * Even more scary is that the retire callback may free |
---|
1054 | | - * the object (last active vma). To prevent the explosion |
---|
1055 | | - * we defer the actual object free to a worker that can |
---|
1056 | | - * only proceed once it acquires the struct_mutex (which |
---|
1057 | | - * we currently hold, therefore it cannot free this object |
---|
1058 | | - * before we are finished). |
---|
1059 | | - */ |
---|
1060 | | - __i915_vma_pin(vma); |
---|
1061 | | - |
---|
1062 | | - ret = i915_gem_active_retire(&vma->last_active, |
---|
1063 | | - &vma->vm->i915->drm.struct_mutex); |
---|
1064 | | - if (ret) |
---|
1065 | | - goto unpin; |
---|
1066 | | - |
---|
1067 | | - rbtree_postorder_for_each_entry_safe(active, n, |
---|
1068 | | - &vma->active, node) { |
---|
1069 | | - ret = i915_gem_active_retire(&active->base, |
---|
1070 | | - &vma->vm->i915->drm.struct_mutex); |
---|
1071 | | - if (ret) |
---|
1072 | | - goto unpin; |
---|
1073 | | - } |
---|
1074 | | - |
---|
1075 | | - ret = i915_gem_active_retire(&vma->last_fence, |
---|
1076 | | - &vma->vm->i915->drm.struct_mutex); |
---|
1077 | | -unpin: |
---|
1078 | | - __i915_vma_unpin(vma); |
---|
1079 | | - if (ret) |
---|
1080 | | - return ret; |
---|
1081 | | - } |
---|
1082 | | - GEM_BUG_ON(i915_vma_is_active(vma)); |
---|
1083 | | - |
---|
1084 | | - if (i915_vma_is_pinned(vma)) { |
---|
1085 | | - vma_print_allocator(vma, "is pinned"); |
---|
1086 | | - return -EBUSY; |
---|
1087 | | - } |
---|
1088 | | - |
---|
1089 | | - if (!drm_mm_node_allocated(&vma->node)) |
---|
1090 | | - return 0; |
---|
| 1264 | + GEM_BUG_ON(i915_vma_is_pinned(vma)); |
---|
1091 | 1265 | |
---|
1092 | 1266 | if (i915_vma_is_map_and_fenceable(vma)) { |
---|
| 1267 | + /* Force a pagefault for domain tracking on next user access */ |
---|
| 1268 | + i915_vma_revoke_mmap(vma); |
---|
| 1269 | + |
---|
1093 | 1270 | /* |
---|
1094 | 1271 | * Check that we have flushed all writes through the GGTT |
---|
1095 | 1272 | * before the unbind, other due to non-strict nature of those |
---|
1096 | 1273 | * indirect writes they may end up referencing the GGTT PTE |
---|
1097 | 1274 | * after the unbind. |
---|
| 1275 | + * |
---|
| 1276 | + * Note that we may be concurrently poking at the GGTT_WRITE |
---|
| 1277 | + * bit from set-domain, as we mark all GGTT vma associated |
---|
| 1278 | + * with an object. We know this is for another vma, as we |
---|
| 1279 | + * are currently unbinding this one -- so if this vma will be |
---|
| 1280 | + * reused, it will be refaulted and have its dirty bit set |
---|
| 1281 | + * before the next write. |
---|
1098 | 1282 | */ |
---|
1099 | 1283 | i915_vma_flush_writes(vma); |
---|
1100 | | - GEM_BUG_ON(i915_vma_has_ggtt_write(vma)); |
---|
1101 | 1284 | |
---|
1102 | 1285 | /* release the fence reg _after_ flushing */ |
---|
1103 | | - ret = i915_vma_put_fence(vma); |
---|
1104 | | - if (ret) |
---|
1105 | | - return ret; |
---|
1106 | | - |
---|
1107 | | - /* Force a pagefault for domain tracking on next user access */ |
---|
1108 | | - i915_vma_revoke_mmap(vma); |
---|
| 1286 | + i915_vma_revoke_fence(vma); |
---|
1109 | 1287 | |
---|
1110 | 1288 | __i915_vma_iounmap(vma); |
---|
1111 | | - vma->flags &= ~I915_VMA_CAN_FENCE; |
---|
| 1289 | + clear_bit(I915_VMA_CAN_FENCE_BIT, __i915_vma_flags(vma)); |
---|
1112 | 1290 | } |
---|
1113 | 1291 | GEM_BUG_ON(vma->fence); |
---|
1114 | 1292 | GEM_BUG_ON(i915_vma_has_userfault(vma)); |
---|
1115 | 1293 | |
---|
1116 | | - if (likely(!vma->vm->closed)) { |
---|
| 1294 | + if (likely(atomic_read(&vma->vm->open))) { |
---|
1117 | 1295 | trace_i915_vma_unbind(vma); |
---|
1118 | | - vma->ops->unbind_vma(vma); |
---|
| 1296 | + vma->ops->unbind_vma(vma->vm, vma); |
---|
1119 | 1297 | } |
---|
1120 | | - vma->flags &= ~(I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND); |
---|
| 1298 | + atomic_and(~(I915_VMA_BIND_MASK | I915_VMA_ERROR | I915_VMA_GGTT_WRITE), |
---|
| 1299 | + &vma->flags); |
---|
1121 | 1300 | |
---|
1122 | | - i915_vma_remove(vma); |
---|
| 1301 | + i915_vma_detach(vma); |
---|
| 1302 | + vma_unbind_pages(vma); |
---|
| 1303 | +} |
---|
1123 | 1304 | |
---|
| 1305 | +int __i915_vma_unbind(struct i915_vma *vma) |
---|
| 1306 | +{ |
---|
| 1307 | + int ret; |
---|
| 1308 | + |
---|
| 1309 | + lockdep_assert_held(&vma->vm->mutex); |
---|
| 1310 | + |
---|
| 1311 | + if (!drm_mm_node_allocated(&vma->node)) |
---|
| 1312 | + return 0; |
---|
| 1313 | + |
---|
| 1314 | + if (i915_vma_is_pinned(vma)) { |
---|
| 1315 | + vma_print_allocator(vma, "is pinned"); |
---|
| 1316 | + return -EAGAIN; |
---|
| 1317 | + } |
---|
| 1318 | + |
---|
| 1319 | + /* |
---|
| 1320 | + * After confirming that no one else is pinning this vma, wait for |
---|
| 1321 | + * any laggards who may have crept in during the wait (through |
---|
| 1322 | + * a residual pin skipping the vm->mutex) to complete. |
---|
| 1323 | + */ |
---|
| 1324 | + ret = i915_vma_sync(vma); |
---|
| 1325 | + if (ret) |
---|
| 1326 | + return ret; |
---|
| 1327 | + |
---|
| 1328 | + GEM_BUG_ON(i915_vma_is_active(vma)); |
---|
| 1329 | + __i915_vma_evict(vma); |
---|
| 1330 | + |
---|
| 1331 | + drm_mm_remove_node(&vma->node); /* pairs with i915_vma_release() */ |
---|
1124 | 1332 | return 0; |
---|
| 1333 | +} |
---|
| 1334 | + |
---|
| 1335 | +int i915_vma_unbind(struct i915_vma *vma) |
---|
| 1336 | +{ |
---|
| 1337 | + struct i915_address_space *vm = vma->vm; |
---|
| 1338 | + intel_wakeref_t wakeref = 0; |
---|
| 1339 | + int err; |
---|
| 1340 | + |
---|
| 1341 | + /* Optimistic wait before taking the mutex */ |
---|
| 1342 | + err = i915_vma_sync(vma); |
---|
| 1343 | + if (err) |
---|
| 1344 | + return err; |
---|
| 1345 | + |
---|
| 1346 | + if (!drm_mm_node_allocated(&vma->node)) |
---|
| 1347 | + return 0; |
---|
| 1348 | + |
---|
| 1349 | + if (i915_vma_is_pinned(vma)) { |
---|
| 1350 | + vma_print_allocator(vma, "is pinned"); |
---|
| 1351 | + return -EAGAIN; |
---|
| 1352 | + } |
---|
| 1353 | + |
---|
| 1354 | + if (i915_vma_is_bound(vma, I915_VMA_GLOBAL_BIND)) |
---|
| 1355 | + /* XXX not always required: nop_clear_range */ |
---|
| 1356 | + wakeref = intel_runtime_pm_get(&vm->i915->runtime_pm); |
---|
| 1357 | + |
---|
| 1358 | + err = mutex_lock_interruptible_nested(&vma->vm->mutex, !wakeref); |
---|
| 1359 | + if (err) |
---|
| 1360 | + goto out_rpm; |
---|
| 1361 | + |
---|
| 1362 | + err = __i915_vma_unbind(vma); |
---|
| 1363 | + mutex_unlock(&vm->mutex); |
---|
| 1364 | + |
---|
| 1365 | +out_rpm: |
---|
| 1366 | + if (wakeref) |
---|
| 1367 | + intel_runtime_pm_put(&vm->i915->runtime_pm, wakeref); |
---|
| 1368 | + return err; |
---|
| 1369 | +} |
---|
| 1370 | + |
---|
| 1371 | +struct i915_vma *i915_vma_make_unshrinkable(struct i915_vma *vma) |
---|
| 1372 | +{ |
---|
| 1373 | + i915_gem_object_make_unshrinkable(vma->obj); |
---|
| 1374 | + return vma; |
---|
| 1375 | +} |
---|
| 1376 | + |
---|
| 1377 | +void i915_vma_make_shrinkable(struct i915_vma *vma) |
---|
| 1378 | +{ |
---|
| 1379 | + i915_gem_object_make_shrinkable(vma->obj); |
---|
| 1380 | +} |
---|
| 1381 | + |
---|
| 1382 | +void i915_vma_make_purgeable(struct i915_vma *vma) |
---|
| 1383 | +{ |
---|
| 1384 | + i915_gem_object_make_purgeable(vma->obj); |
---|
1125 | 1385 | } |
---|
1126 | 1386 | |
---|
1127 | 1387 | #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST) |
---|
1128 | 1388 | #include "selftests/i915_vma.c" |
---|
1129 | 1389 | #endif |
---|
| 1390 | + |
---|
| 1391 | +static void i915_global_vma_shrink(void) |
---|
| 1392 | +{ |
---|
| 1393 | + kmem_cache_shrink(global.slab_vmas); |
---|
| 1394 | +} |
---|
| 1395 | + |
---|
| 1396 | +static void i915_global_vma_exit(void) |
---|
| 1397 | +{ |
---|
| 1398 | + kmem_cache_destroy(global.slab_vmas); |
---|
| 1399 | +} |
---|
| 1400 | + |
---|
| 1401 | +static struct i915_global_vma global = { { |
---|
| 1402 | + .shrink = i915_global_vma_shrink, |
---|
| 1403 | + .exit = i915_global_vma_exit, |
---|
| 1404 | +} }; |
---|
| 1405 | + |
---|
| 1406 | +int __init i915_global_vma_init(void) |
---|
| 1407 | +{ |
---|
| 1408 | + global.slab_vmas = KMEM_CACHE(i915_vma, SLAB_HWCACHE_ALIGN); |
---|
| 1409 | + if (!global.slab_vmas) |
---|
| 1410 | + return -ENOMEM; |
---|
| 1411 | + |
---|
| 1412 | + i915_global_register(&global.base); |
---|
| 1413 | + return 0; |
---|
| 1414 | +} |
---|