Home | History | Annotate | Line # | Download | only in i915
      1 /*	$NetBSD: intel_memory_region.c,v 1.4 2021/12/19 11:47:48 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: intel_memory_region.c,v 1.4 2021/12/19 11:47:48 riastradh Exp $");
     10 
     11 #include "intel_memory_region.h"
     12 #include "i915_drv.h"
     13 
     14 #include <linux/nbsd-namespace.h>
     15 
     16 /* XXX: Hysterical raisins. BIT(inst) needs to just be (inst) at some point. */
     17 #define REGION_MAP(type, inst) \
     18 	BIT((type) + INTEL_MEMORY_TYPE_SHIFT) | BIT(inst)
     19 
     20 const u32 intel_region_map[] = {
     21 	[INTEL_REGION_SMEM] = REGION_MAP(INTEL_MEMORY_SYSTEM, 0),
     22 	[INTEL_REGION_LMEM] = REGION_MAP(INTEL_MEMORY_LOCAL, 0),
     23 	[INTEL_REGION_STOLEN] = REGION_MAP(INTEL_MEMORY_STOLEN, 0),
     24 };
     25 
     26 struct intel_memory_region *
     27 intel_memory_region_by_type(struct drm_i915_private *i915,
     28 			    enum intel_memory_type mem_type)
     29 {
     30 	struct intel_memory_region *mr;
     31 	int id;
     32 
     33 	for_each_memory_region(mr, i915, id)
     34 		if (mr->type == mem_type)
     35 			return mr;
     36 
     37 	return NULL;
     38 }
     39 
     40 static u64
     41 intel_memory_region_free_pages(struct intel_memory_region *mem,
     42 			       struct list_head *blocks)
     43 {
     44 	struct i915_buddy_block *block, *on;
     45 	u64 size = 0;
     46 
     47 	list_for_each_entry_safe(block, on, blocks, link) {
     48 		size += i915_buddy_block_size(&mem->mm, block);
     49 		i915_buddy_free(&mem->mm, block);
     50 	}
     51 	INIT_LIST_HEAD(blocks);
     52 
     53 	return size;
     54 }
     55 
     56 void
     57 __intel_memory_region_put_pages_buddy(struct intel_memory_region *mem,
     58 				      struct list_head *blocks)
     59 {
     60 	mutex_lock(&mem->mm_lock);
     61 	mem->avail += intel_memory_region_free_pages(mem, blocks);
     62 	mutex_unlock(&mem->mm_lock);
     63 }
     64 
     65 void
     66 __intel_memory_region_put_block_buddy(struct i915_buddy_block *block)
     67 {
     68 	struct list_head blocks;
     69 
     70 	INIT_LIST_HEAD(&blocks);
     71 	list_add(&block->link, &blocks);
     72 	__intel_memory_region_put_pages_buddy(block->private, &blocks);
     73 }
     74 
     75 int
     76 __intel_memory_region_get_pages_buddy(struct intel_memory_region *mem,
     77 				      resource_size_t size,
     78 				      unsigned int flags,
     79 				      struct list_head *blocks)
     80 {
     81 	unsigned int min_order = 0;
     82 	unsigned long n_pages;
     83 
     84 	GEM_BUG_ON(!IS_ALIGNED(size, mem->mm.chunk_size));
     85 	GEM_BUG_ON(!list_empty(blocks));
     86 
     87 	if (flags & I915_ALLOC_MIN_PAGE_SIZE) {
     88 		min_order = ilog2(mem->min_page_size) -
     89 			    ilog2(mem->mm.chunk_size);
     90 	}
     91 
     92 	if (flags & I915_ALLOC_CONTIGUOUS) {
     93 		size = roundup_pow_of_two(size);
     94 		min_order = ilog2(size) - ilog2(mem->mm.chunk_size);
     95 	}
     96 
     97 	if (size > BIT(mem->mm.max_order) * mem->mm.chunk_size)
     98 		return -E2BIG;
     99 
    100 	n_pages = size >> ilog2(mem->mm.chunk_size);
    101 
    102 	mutex_lock(&mem->mm_lock);
    103 
    104 	do {
    105 		struct i915_buddy_block *block;
    106 		unsigned int order;
    107 
    108 		order = fls(n_pages) - 1;
    109 		GEM_BUG_ON(order > mem->mm.max_order);
    110 		GEM_BUG_ON(order < min_order);
    111 
    112 		do {
    113 			block = i915_buddy_alloc(&mem->mm, order);
    114 			if (!IS_ERR(block))
    115 				break;
    116 
    117 			if (order-- == min_order)
    118 				goto err_free_blocks;
    119 		} while (1);
    120 
    121 		n_pages -= BIT(order);
    122 
    123 		block->private = mem;
    124 		list_add(&block->link, blocks);
    125 
    126 		if (!n_pages)
    127 			break;
    128 	} while (1);
    129 
    130 	mem->avail -= size;
    131 	mutex_unlock(&mem->mm_lock);
    132 	return 0;
    133 
    134 err_free_blocks:
    135 	intel_memory_region_free_pages(mem, blocks);
    136 	mutex_unlock(&mem->mm_lock);
    137 	return -ENXIO;
    138 }
    139 
    140 struct i915_buddy_block *
    141 __intel_memory_region_get_block_buddy(struct intel_memory_region *mem,
    142 				      resource_size_t size,
    143 				      unsigned int flags)
    144 {
    145 	struct i915_buddy_block *block;
    146 	LIST_HEAD(blocks);
    147 	int ret;
    148 
    149 	ret = __intel_memory_region_get_pages_buddy(mem, size, flags, &blocks);
    150 	if (ret)
    151 		return ERR_PTR(ret);
    152 
    153 	block = list_first_entry(&blocks, typeof(*block), link);
    154 	list_del_init(&block->link);
    155 	return block;
    156 }
    157 
    158 int intel_memory_region_init_buddy(struct intel_memory_region *mem)
    159 {
    160 	return i915_buddy_init(&mem->mm, resource_size(&mem->region),
    161 			       PAGE_SIZE);
    162 }
    163 
    164 void intel_memory_region_release_buddy(struct intel_memory_region *mem)
    165 {
    166 	i915_buddy_fini(&mem->mm);
    167 }
    168 
    169 struct intel_memory_region *
    170 intel_memory_region_create(struct drm_i915_private *i915,
    171 			   resource_size_t start,
    172 			   resource_size_t size,
    173 			   resource_size_t min_page_size,
    174 			   resource_size_t io_start,
    175 			   const struct intel_memory_region_ops *ops)
    176 {
    177 	struct intel_memory_region *mem;
    178 	int err;
    179 
    180 	mem = kzalloc(sizeof(*mem), GFP_KERNEL);
    181 	if (!mem)
    182 		return ERR_PTR(-ENOMEM);
    183 
    184 	mem->i915 = i915;
    185 	mem->region = (struct resource)DEFINE_RES_MEM(start, size);
    186 	mem->io_start = io_start;
    187 	mem->min_page_size = min_page_size;
    188 	mem->ops = ops;
    189 	mem->total = size;
    190 	mem->avail = mem->total;
    191 
    192 	mutex_init(&mem->objects.lock);
    193 	INIT_LIST_HEAD(&mem->objects.list);
    194 	INIT_LIST_HEAD(&mem->objects.purgeable);
    195 
    196 	mutex_init(&mem->mm_lock);
    197 
    198 	if (ops->init) {
    199 		err = ops->init(mem);
    200 		if (err)
    201 			goto err_free;
    202 	}
    203 
    204 	kref_init(&mem->kref);
    205 	return mem;
    206 
    207 err_free:
    208 	mutex_destroy(&mem->mm_lock);
    209 	mutex_destroy(&mem->objects.lock);
    210 	kfree(mem);
    211 	return ERR_PTR(err);
    212 }
    213 
    214 void intel_memory_region_set_name(struct intel_memory_region *mem,
    215 				  const char *fmt, ...)
    216 {
    217 	va_list ap;
    218 
    219 	va_start(ap, fmt);
    220 	vsnprintf(mem->name, sizeof(mem->name), fmt, ap);
    221 	va_end(ap);
    222 }
    223 
    224 static void __intel_memory_region_destroy(struct kref *kref)
    225 {
    226 	struct intel_memory_region *mem =
    227 		container_of(kref, typeof(*mem), kref);
    228 
    229 	if (mem->ops->release)
    230 		mem->ops->release(mem);
    231 
    232 	mutex_destroy(&mem->mm_lock);
    233 	mutex_destroy(&mem->objects.lock);
    234 	kfree(mem);
    235 }
    236 
    237 struct intel_memory_region *
    238 intel_memory_region_get(struct intel_memory_region *mem)
    239 {
    240 	kref_get(&mem->kref);
    241 	return mem;
    242 }
    243 
    244 void intel_memory_region_put(struct intel_memory_region *mem)
    245 {
    246 	kref_put(&mem->kref, __intel_memory_region_destroy);
    247 }
    248 
    249 /* Global memory region registration -- only slight layer inversions! */
    250 
    251 int intel_memory_regions_hw_probe(struct drm_i915_private *i915)
    252 {
    253 	int err, i;
    254 
    255 	for (i = 0; i < ARRAY_SIZE(i915->mm.regions); i++) {
    256 		struct intel_memory_region *mem = ERR_PTR(-ENODEV);
    257 		u32 type;
    258 
    259 		if (!HAS_REGION(i915, BIT(i)))
    260 			continue;
    261 
    262 		type = MEMORY_TYPE_FROM_REGION(intel_region_map[i]);
    263 		switch (type) {
    264 		case INTEL_MEMORY_SYSTEM:
    265 			mem = i915_gem_shmem_setup(i915);
    266 			break;
    267 		case INTEL_MEMORY_STOLEN:
    268 			mem = i915_gem_stolen_setup(i915);
    269 			break;
    270 		case INTEL_MEMORY_LOCAL:
    271 			mem = intel_setup_fake_lmem(i915);
    272 			break;
    273 		}
    274 
    275 		if (IS_ERR(mem)) {
    276 			err = PTR_ERR(mem);
    277 			DRM_ERROR("Failed to setup region(%d) type=%d\n", err, type);
    278 			goto out_cleanup;
    279 		}
    280 
    281 		mem->id = intel_region_map[i];
    282 		mem->type = type;
    283 		mem->instance = MEMORY_INSTANCE_FROM_REGION(intel_region_map[i]);
    284 
    285 		i915->mm.regions[i] = mem;
    286 	}
    287 
    288 	return 0;
    289 
    290 out_cleanup:
    291 	intel_memory_regions_driver_release(i915);
    292 	return err;
    293 }
    294 
    295 void intel_memory_regions_driver_release(struct drm_i915_private *i915)
    296 {
    297 	int i;
    298 
    299 	for (i = 0; i < ARRAY_SIZE(i915->mm.regions); i++) {
    300 		struct intel_memory_region *region =
    301 			fetch_and_zero(&i915->mm.regions[i]);
    302 
    303 		if (region)
    304 			intel_memory_region_put(region);
    305 	}
    306 }
    307 
    308 #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
    309 #include "selftests/intel_memory_region.c"
    310 #include "selftests/mock_region.c"
    311 #endif
    312