diff options
Diffstat (limited to 'drivers/gpu/drm/i915/i915_timeline.c')
| -rw-r--r-- | drivers/gpu/drm/i915/i915_timeline.c | 257 | 
1 files changed, 244 insertions, 13 deletions
| diff --git a/drivers/gpu/drm/i915/i915_timeline.c b/drivers/gpu/drm/i915/i915_timeline.c index 4667cc08c416..b2202d2e58a2 100644 --- a/drivers/gpu/drm/i915/i915_timeline.c +++ b/drivers/gpu/drm/i915/i915_timeline.c @@ -9,34 +9,199 @@  #include "i915_timeline.h"  #include "i915_syncmap.h" -void i915_timeline_init(struct drm_i915_private *i915, -			struct i915_timeline *timeline, -			const char *name) +struct i915_timeline_hwsp { +	struct i915_vma *vma; +	struct list_head free_link; +	u64 free_bitmap; +}; + +static inline struct i915_timeline_hwsp * +i915_timeline_hwsp(const struct i915_timeline *tl) +{ +	return tl->hwsp_ggtt->private; +} + +static struct i915_vma *__hwsp_alloc(struct drm_i915_private *i915) +{ +	struct drm_i915_gem_object *obj; +	struct i915_vma *vma; + +	obj = i915_gem_object_create_internal(i915, PAGE_SIZE); +	if (IS_ERR(obj)) +		return ERR_CAST(obj); + +	i915_gem_object_set_cache_coherency(obj, I915_CACHE_LLC); + +	vma = i915_vma_instance(obj, &i915->ggtt.vm, NULL); +	if (IS_ERR(vma)) +		i915_gem_object_put(obj); + +	return vma; +} + +static struct i915_vma * +hwsp_alloc(struct i915_timeline *timeline, unsigned int *cacheline)  { -	lockdep_assert_held(&i915->drm.struct_mutex); +	struct drm_i915_private *i915 = timeline->i915; +	struct i915_gt_timelines *gt = &i915->gt.timelines; +	struct i915_timeline_hwsp *hwsp; + +	BUILD_BUG_ON(BITS_PER_TYPE(u64) * CACHELINE_BYTES > PAGE_SIZE); + +	spin_lock(>->hwsp_lock); + +	/* hwsp_free_list only contains HWSP that have available cachelines */ +	hwsp = list_first_entry_or_null(>->hwsp_free_list, +					typeof(*hwsp), free_link); +	if (!hwsp) { +		struct i915_vma *vma; + +		spin_unlock(>->hwsp_lock); + +		hwsp = kmalloc(sizeof(*hwsp), GFP_KERNEL); +		if (!hwsp) +			return ERR_PTR(-ENOMEM); + +		vma = __hwsp_alloc(i915); +		if (IS_ERR(vma)) { +			kfree(hwsp); +			return vma; +		} + +		vma->private = hwsp; +		hwsp->vma = vma; +		hwsp->free_bitmap = ~0ull; + +		spin_lock(>->hwsp_lock); +		list_add(&hwsp->free_link, >->hwsp_free_list); +	} + +	GEM_BUG_ON(!hwsp->free_bitmap); +	*cacheline = __ffs64(hwsp->free_bitmap); +	hwsp->free_bitmap &= ~BIT_ULL(*cacheline); +	if (!hwsp->free_bitmap) +		list_del(&hwsp->free_link); + +	spin_unlock(>->hwsp_lock); + +	GEM_BUG_ON(hwsp->vma->private != hwsp); +	return hwsp->vma; +} + +static void hwsp_free(struct i915_timeline *timeline) +{ +	struct i915_gt_timelines *gt = &timeline->i915->gt.timelines; +	struct i915_timeline_hwsp *hwsp; + +	hwsp = i915_timeline_hwsp(timeline); +	if (!hwsp) /* leave global HWSP alone! */ +		return; + +	spin_lock(>->hwsp_lock); + +	/* As a cacheline becomes available, publish the HWSP on the freelist */ +	if (!hwsp->free_bitmap) +		list_add_tail(&hwsp->free_link, >->hwsp_free_list); + +	hwsp->free_bitmap |= BIT_ULL(timeline->hwsp_offset / CACHELINE_BYTES); + +	/* And if no one is left using it, give the page back to the system */ +	if (hwsp->free_bitmap == ~0ull) { +		i915_vma_put(hwsp->vma); +		list_del(&hwsp->free_link); +		kfree(hwsp); +	} + +	spin_unlock(>->hwsp_lock); +} + +int i915_timeline_init(struct drm_i915_private *i915, +		       struct i915_timeline *timeline, +		       const char *name, +		       struct i915_vma *hwsp) +{ +	void *vaddr;  	/*  	 * Ideally we want a set of engines on a single leaf as we expect  	 * to mostly be tracking synchronisation between engines. It is not  	 * a huge issue if this is not the case, but we may want to mitigate  	 * any page crossing penalties if they become an issue. +	 * +	 * Called during early_init before we know how many engines there are.  	 */  	BUILD_BUG_ON(KSYNCMAP < I915_NUM_ENGINES); +	timeline->i915 = i915;  	timeline->name = name; +	timeline->pin_count = 0; +	timeline->has_initial_breadcrumb = !hwsp; -	list_add(&timeline->link, &i915->gt.timelines); +	timeline->hwsp_offset = I915_GEM_HWS_SEQNO_ADDR; +	if (!hwsp) { +		unsigned int cacheline; + +		hwsp = hwsp_alloc(timeline, &cacheline); +		if (IS_ERR(hwsp)) +			return PTR_ERR(hwsp); + +		timeline->hwsp_offset = cacheline * CACHELINE_BYTES; +	} +	timeline->hwsp_ggtt = i915_vma_get(hwsp); + +	vaddr = i915_gem_object_pin_map(hwsp->obj, I915_MAP_WB); +	if (IS_ERR(vaddr)) { +		hwsp_free(timeline); +		i915_vma_put(hwsp); +		return PTR_ERR(vaddr); +	} -	/* Called during early_init before we know how many engines there are */ +	timeline->hwsp_seqno = +		memset(vaddr + timeline->hwsp_offset, 0, CACHELINE_BYTES);  	timeline->fence_context = dma_fence_context_alloc(1);  	spin_lock_init(&timeline->lock); -	init_request_active(&timeline->last_request, NULL); +	INIT_ACTIVE_REQUEST(&timeline->barrier); +	INIT_ACTIVE_REQUEST(&timeline->last_request);  	INIT_LIST_HEAD(&timeline->requests);  	i915_syncmap_init(&timeline->sync); + +	return 0; +} + +void i915_timelines_init(struct drm_i915_private *i915) +{ +	struct i915_gt_timelines *gt = &i915->gt.timelines; + +	mutex_init(>->mutex); +	INIT_LIST_HEAD(>->active_list); + +	spin_lock_init(>->hwsp_lock); +	INIT_LIST_HEAD(>->hwsp_free_list); + +	/* via i915_gem_wait_for_idle() */ +	i915_gem_shrinker_taints_mutex(i915, >->mutex); +} + +static void timeline_add_to_active(struct i915_timeline *tl) +{ +	struct i915_gt_timelines *gt = &tl->i915->gt.timelines; + +	mutex_lock(>->mutex); +	list_add(&tl->link, >->active_list); +	mutex_unlock(>->mutex); +} + +static void timeline_remove_from_active(struct i915_timeline *tl) +{ +	struct i915_gt_timelines *gt = &tl->i915->gt.timelines; + +	mutex_lock(>->mutex); +	list_del(&tl->link); +	mutex_unlock(>->mutex);  }  /** @@ -51,11 +216,11 @@ void i915_timeline_init(struct drm_i915_private *i915,   */  void i915_timelines_park(struct drm_i915_private *i915)  { +	struct i915_gt_timelines *gt = &i915->gt.timelines;  	struct i915_timeline *timeline; -	lockdep_assert_held(&i915->drm.struct_mutex); - -	list_for_each_entry(timeline, &i915->gt.timelines, link) { +	mutex_lock(>->mutex); +	list_for_each_entry(timeline, >->active_list, link) {  		/*  		 * All known fences are completed so we can scrap  		 * the current sync point tracking and start afresh, @@ -64,32 +229,88 @@ void i915_timelines_park(struct drm_i915_private *i915)  		 */  		i915_syncmap_free(&timeline->sync);  	} +	mutex_unlock(>->mutex);  }  void i915_timeline_fini(struct i915_timeline *timeline)  { +	GEM_BUG_ON(timeline->pin_count);  	GEM_BUG_ON(!list_empty(&timeline->requests)); +	GEM_BUG_ON(i915_active_request_isset(&timeline->barrier));  	i915_syncmap_free(&timeline->sync); +	hwsp_free(timeline); -	list_del(&timeline->link); +	i915_gem_object_unpin_map(timeline->hwsp_ggtt->obj); +	i915_vma_put(timeline->hwsp_ggtt);  }  struct i915_timeline * -i915_timeline_create(struct drm_i915_private *i915, const char *name) +i915_timeline_create(struct drm_i915_private *i915, +		     const char *name, +		     struct i915_vma *global_hwsp)  {  	struct i915_timeline *timeline; +	int err;  	timeline = kzalloc(sizeof(*timeline), GFP_KERNEL);  	if (!timeline)  		return ERR_PTR(-ENOMEM); -	i915_timeline_init(i915, timeline, name); +	err = i915_timeline_init(i915, timeline, name, global_hwsp); +	if (err) { +		kfree(timeline); +		return ERR_PTR(err); +	} +  	kref_init(&timeline->kref);  	return timeline;  } +int i915_timeline_pin(struct i915_timeline *tl) +{ +	int err; + +	if (tl->pin_count++) +		return 0; +	GEM_BUG_ON(!tl->pin_count); + +	err = i915_vma_pin(tl->hwsp_ggtt, 0, 0, PIN_GLOBAL | PIN_HIGH); +	if (err) +		goto unpin; + +	tl->hwsp_offset = +		i915_ggtt_offset(tl->hwsp_ggtt) + +		offset_in_page(tl->hwsp_offset); + +	timeline_add_to_active(tl); + +	return 0; + +unpin: +	tl->pin_count = 0; +	return err; +} + +void i915_timeline_unpin(struct i915_timeline *tl) +{ +	GEM_BUG_ON(!tl->pin_count); +	if (--tl->pin_count) +		return; + +	timeline_remove_from_active(tl); + +	/* +	 * Since this timeline is idle, all bariers upon which we were waiting +	 * must also be complete and so we can discard the last used barriers +	 * without loss of information. +	 */ +	i915_syncmap_free(&tl->sync); + +	__i915_vma_unpin(tl->hwsp_ggtt); +} +  void __i915_timeline_free(struct kref *kref)  {  	struct i915_timeline *timeline = @@ -99,6 +320,16 @@ void __i915_timeline_free(struct kref *kref)  	kfree(timeline);  } +void i915_timelines_fini(struct drm_i915_private *i915) +{ +	struct i915_gt_timelines *gt = &i915->gt.timelines; + +	GEM_BUG_ON(!list_empty(>->active_list)); +	GEM_BUG_ON(!list_empty(>->hwsp_free_list)); + +	mutex_destroy(>->mutex); +} +  #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)  #include "selftests/mock_timeline.c"  #include "selftests/i915_timeline.c" | 
