Home | History | Annotate | Line # | Download | only in gem
      1 /*	$NetBSD: i915_gem_client_blt.c,v 1.4 2021/12/19 11:27:12 riastradh Exp $	*/
      2 
      3 // SPDX-License-Identifier: MIT
      4 /*
      5  * Copyright  2019 Intel Corporation
      6  */
      7 
      8 #include <sys/cdefs.h>
      9 __KERNEL_RCSID(0, "$NetBSD: i915_gem_client_blt.c,v 1.4 2021/12/19 11:27:12 riastradh Exp $");
     10 
     11 #include "i915_drv.h"
     12 #include "gt/intel_context.h"
     13 #include "gt/intel_engine_pm.h"
     14 #include "gt/intel_engine_pool.h"
     15 #include "i915_gem_client_blt.h"
     16 #include "i915_gem_object_blt.h"
     17 
     18 struct i915_sleeve {
     19 	struct i915_vma *vma;
     20 	struct drm_i915_gem_object *obj;
     21 	struct sg_table *pages;
     22 	struct i915_page_sizes page_sizes;
     23 };
     24 
     25 static int vma_set_pages(struct i915_vma *vma)
     26 {
     27 	struct i915_sleeve *sleeve = vma->private;
     28 
     29 	vma->pages = sleeve->pages;
     30 	vma->page_sizes = sleeve->page_sizes;
     31 
     32 	return 0;
     33 }
     34 
     35 static void vma_clear_pages(struct i915_vma *vma)
     36 {
     37 	GEM_BUG_ON(!vma->pages);
     38 	vma->pages = NULL;
     39 }
     40 
     41 static int vma_bind(struct i915_vma *vma,
     42 		    enum i915_cache_level cache_level,
     43 		    u32 flags)
     44 {
     45 	return vma->vm->vma_ops.bind_vma(vma, cache_level, flags);
     46 }
     47 
     48 static void vma_unbind(struct i915_vma *vma)
     49 {
     50 	vma->vm->vma_ops.unbind_vma(vma);
     51 }
     52 
     53 static const struct i915_vma_ops proxy_vma_ops = {
     54 	.set_pages = vma_set_pages,
     55 	.clear_pages = vma_clear_pages,
     56 	.bind_vma = vma_bind,
     57 	.unbind_vma = vma_unbind,
     58 };
     59 
     60 static struct i915_sleeve *create_sleeve(struct i915_address_space *vm,
     61 					 struct drm_i915_gem_object *obj,
     62 					 struct sg_table *pages,
     63 					 struct i915_page_sizes *page_sizes)
     64 {
     65 	struct i915_sleeve *sleeve;
     66 	struct i915_vma *vma;
     67 	int err;
     68 
     69 	sleeve = kzalloc(sizeof(*sleeve), GFP_KERNEL);
     70 	if (!sleeve)
     71 		return ERR_PTR(-ENOMEM);
     72 
     73 	vma = i915_vma_instance(obj, vm, NULL);
     74 	if (IS_ERR(vma)) {
     75 		err = PTR_ERR(vma);
     76 		goto err_free;
     77 	}
     78 
     79 	vma->private = sleeve;
     80 	vma->ops = &proxy_vma_ops;
     81 
     82 	sleeve->vma = vma;
     83 	sleeve->pages = pages;
     84 	sleeve->page_sizes = *page_sizes;
     85 
     86 	return sleeve;
     87 
     88 err_free:
     89 	kfree(sleeve);
     90 	return ERR_PTR(err);
     91 }
     92 
     93 static void destroy_sleeve(struct i915_sleeve *sleeve)
     94 {
     95 	kfree(sleeve);
     96 }
     97 
     98 struct clear_pages_work {
     99 	struct dma_fence dma;
    100 	struct dma_fence_cb cb;
    101 	struct i915_sw_fence wait;
    102 	struct work_struct work;
    103 	struct irq_work irq_work;
    104 	struct i915_sleeve *sleeve;
    105 	struct intel_context *ce;
    106 	u32 value;
    107 };
    108 
    109 static const char *clear_pages_work_driver_name(struct dma_fence *fence)
    110 {
    111 	return DRIVER_NAME;
    112 }
    113 
    114 static const char *clear_pages_work_timeline_name(struct dma_fence *fence)
    115 {
    116 	return "clear";
    117 }
    118 
    119 static void clear_pages_work_release(struct dma_fence *fence)
    120 {
    121 	struct clear_pages_work *w = container_of(fence, typeof(*w), dma);
    122 
    123 	destroy_sleeve(w->sleeve);
    124 
    125 	i915_sw_fence_fini(&w->wait);
    126 
    127 	BUILD_BUG_ON(offsetof(typeof(*w), dma));
    128 	dma_fence_free(&w->dma);
    129 }
    130 
    131 static const struct dma_fence_ops clear_pages_work_ops = {
    132 	.get_driver_name = clear_pages_work_driver_name,
    133 	.get_timeline_name = clear_pages_work_timeline_name,
    134 	.release = clear_pages_work_release,
    135 };
    136 
    137 static void clear_pages_signal_irq_worker(struct irq_work *work)
    138 {
    139 	struct clear_pages_work *w = container_of(work, typeof(*w), irq_work);
    140 
    141 	dma_fence_signal(&w->dma);
    142 	dma_fence_put(&w->dma);
    143 }
    144 
    145 static void clear_pages_dma_fence_cb(struct dma_fence *fence,
    146 				     struct dma_fence_cb *cb)
    147 {
    148 	struct clear_pages_work *w = container_of(cb, typeof(*w), cb);
    149 
    150 	if (fence->error)
    151 		dma_fence_set_error(&w->dma, fence->error);
    152 
    153 	/*
    154 	 * Push the signalling of the fence into yet another worker to avoid
    155 	 * the nightmare locking around the fence spinlock.
    156 	 */
    157 	irq_work_queue(&w->irq_work);
    158 }
    159 
    160 static void clear_pages_worker(struct work_struct *work)
    161 {
    162 	struct clear_pages_work *w = container_of(work, typeof(*w), work);
    163 	struct drm_i915_gem_object *obj = w->sleeve->vma->obj;
    164 	struct i915_vma *vma = w->sleeve->vma;
    165 	struct i915_request *rq;
    166 	struct i915_vma *batch;
    167 	int err = w->dma.error;
    168 
    169 	if (unlikely(err))
    170 		goto out_signal;
    171 
    172 	if (obj->cache_dirty) {
    173 		if (i915_gem_object_has_struct_page(obj))
    174 			drm_clflush_sg(w->sleeve->pages);
    175 		obj->cache_dirty = false;
    176 	}
    177 	obj->read_domains = I915_GEM_GPU_DOMAINS;
    178 	obj->write_domain = 0;
    179 
    180 	err = i915_vma_pin(vma, 0, 0, PIN_USER);
    181 	if (unlikely(err))
    182 		goto out_signal;
    183 
    184 	batch = intel_emit_vma_fill_blt(w->ce, vma, w->value);
    185 	if (IS_ERR(batch)) {
    186 		err = PTR_ERR(batch);
    187 		goto out_unpin;
    188 	}
    189 
    190 	rq = intel_context_create_request(w->ce);
    191 	if (IS_ERR(rq)) {
    192 		err = PTR_ERR(rq);
    193 		goto out_batch;
    194 	}
    195 
    196 	/* There's no way the fence has signalled */
    197 	if (dma_fence_add_callback(&rq->fence, &w->cb,
    198 				   clear_pages_dma_fence_cb))
    199 		GEM_BUG_ON(1);
    200 
    201 	err = intel_emit_vma_mark_active(batch, rq);
    202 	if (unlikely(err))
    203 		goto out_request;
    204 
    205 	if (w->ce->engine->emit_init_breadcrumb) {
    206 		err = w->ce->engine->emit_init_breadcrumb(rq);
    207 		if (unlikely(err))
    208 			goto out_request;
    209 	}
    210 
    211 	/*
    212 	 * w->dma is already exported via (vma|obj)->resv we need only
    213 	 * keep track of the GPU activity within this vma/request, and
    214 	 * propagate the signal from the request to w->dma.
    215 	 */
    216 	err = __i915_vma_move_to_active(vma, rq);
    217 	if (err)
    218 		goto out_request;
    219 
    220 	err = w->ce->engine->emit_bb_start(rq,
    221 					   batch->node.start, batch->node.size,
    222 					   0);
    223 out_request:
    224 	if (unlikely(err)) {
    225 		i915_request_skip(rq, err);
    226 		err = 0;
    227 	}
    228 
    229 	i915_request_add(rq);
    230 out_batch:
    231 	intel_emit_vma_release(w->ce, batch);
    232 out_unpin:
    233 	i915_vma_unpin(vma);
    234 out_signal:
    235 	if (unlikely(err)) {
    236 		dma_fence_set_error(&w->dma, err);
    237 		dma_fence_signal(&w->dma);
    238 		dma_fence_put(&w->dma);
    239 	}
    240 }
    241 
    242 static int __i915_sw_fence_call
    243 clear_pages_work_notify(struct i915_sw_fence *fence,
    244 			enum i915_sw_fence_notify state)
    245 {
    246 	struct clear_pages_work *w = container_of(fence, typeof(*w), wait);
    247 
    248 	switch (state) {
    249 	case FENCE_COMPLETE:
    250 		schedule_work(&w->work);
    251 		break;
    252 
    253 	case FENCE_FREE:
    254 		dma_fence_put(&w->dma);
    255 		break;
    256 	}
    257 
    258 	return NOTIFY_DONE;
    259 }
    260 
    261 static DEFINE_SPINLOCK(fence_lock);
    262 
    263 /* XXX: better name please */
    264 int i915_gem_schedule_fill_pages_blt(struct drm_i915_gem_object *obj,
    265 				     struct intel_context *ce,
    266 				     struct sg_table *pages,
    267 				     struct i915_page_sizes *page_sizes,
    268 				     u32 value)
    269 {
    270 	struct clear_pages_work *work;
    271 	struct i915_sleeve *sleeve;
    272 	int err;
    273 
    274 	sleeve = create_sleeve(ce->vm, obj, pages, page_sizes);
    275 	if (IS_ERR(sleeve))
    276 		return PTR_ERR(sleeve);
    277 
    278 	work = kmalloc(sizeof(*work), GFP_KERNEL);
    279 	if (!work) {
    280 		destroy_sleeve(sleeve);
    281 		return -ENOMEM;
    282 	}
    283 
    284 	work->value = value;
    285 	work->sleeve = sleeve;
    286 	work->ce = ce;
    287 
    288 	INIT_WORK(&work->work, clear_pages_worker);
    289 
    290 	init_irq_work(&work->irq_work, clear_pages_signal_irq_worker);
    291 
    292 	dma_fence_init(&work->dma, &clear_pages_work_ops, &fence_lock, 0, 0);
    293 	i915_sw_fence_init(&work->wait, clear_pages_work_notify);
    294 
    295 	i915_gem_object_lock(obj);
    296 	err = i915_sw_fence_await_reservation(&work->wait,
    297 					      obj->base.resv, NULL,
    298 					      true, I915_FENCE_TIMEOUT,
    299 					      I915_FENCE_GFP);
    300 	if (err < 0) {
    301 		dma_fence_set_error(&work->dma, err);
    302 	} else {
    303 		dma_resv_add_excl_fence(obj->base.resv, &work->dma);
    304 		err = 0;
    305 	}
    306 	i915_gem_object_unlock(obj);
    307 
    308 	dma_fence_get(&work->dma);
    309 	i915_sw_fence_commit(&work->wait);
    310 
    311 	return err;
    312 }
    313 
    314 #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
    315 #include "selftests/i915_gem_client_blt.c"
    316 #endif
    317