1 // SPDX-License-Identifier: MIT 2 /* 3 * Copyright © 2019 Intel Corporation 4 */ 5 6 #include "intel_memory_region.h" 7 #include "i915_gem_region.h" 8 #include "i915_drv.h" 9 #include "i915_trace.h" 10 11 void 12 i915_gem_object_put_pages_buddy(struct drm_i915_gem_object *obj, 13 struct sg_table *pages) 14 { 15 __intel_memory_region_put_pages_buddy(obj->mm.region, &obj->mm.blocks); 16 17 obj->mm.dirty = false; 18 sg_free_table(pages); 19 kfree(pages); 20 } 21 22 int 23 i915_gem_object_get_pages_buddy(struct drm_i915_gem_object *obj) 24 { 25 struct intel_memory_region *mem = obj->mm.region; 26 struct list_head *blocks = &obj->mm.blocks; 27 resource_size_t size = obj->base.size; 28 resource_size_t prev_end; 29 struct i915_buddy_block *block; 30 unsigned int flags; 31 struct sg_table *st; 32 struct scatterlist *sg; 33 unsigned int sg_page_sizes; 34 int ret; 35 36 st = kmalloc(sizeof(*st), GFP_KERNEL); 37 if (!st) 38 return -ENOMEM; 39 40 if (sg_alloc_table(st, size >> ilog2(mem->mm.chunk_size), GFP_KERNEL)) { 41 kfree(st); 42 return -ENOMEM; 43 } 44 45 flags = I915_ALLOC_MIN_PAGE_SIZE; 46 if (obj->flags & I915_BO_ALLOC_CONTIGUOUS) 47 flags |= I915_ALLOC_CONTIGUOUS; 48 49 ret = __intel_memory_region_get_pages_buddy(mem, size, flags, blocks); 50 if (ret) 51 goto err_free_sg; 52 53 GEM_BUG_ON(list_empty(blocks)); 54 55 sg = st->sgl; 56 st->nents = 0; 57 sg_page_sizes = 0; 58 prev_end = (resource_size_t)-1; 59 60 list_for_each_entry(block, blocks, link) { 61 u64 block_size, offset; 62 63 block_size = min_t(u64, size, 64 i915_buddy_block_size(&mem->mm, block)); 65 offset = i915_buddy_block_offset(block); 66 67 GEM_BUG_ON(overflows_type(block_size, sg->length)); 68 69 if (offset != prev_end || 70 add_overflows_t(typeof(sg->length), sg->length, block_size)) { 71 if (st->nents) { 72 sg_page_sizes |= sg->length; 73 sg = __sg_next(sg); 74 } 75 76 sg_dma_address(sg) = mem->region.start + offset; 77 sg_dma_len(sg) = block_size; 78 79 sg->length = block_size; 80 81 st->nents++; 82 } else { 83 sg->length += block_size; 84 sg_dma_len(sg) += block_size; 85 } 86 87 prev_end = offset + block_size; 88 }; 89 90 sg_page_sizes |= sg->length; 91 sg_mark_end(sg); 92 i915_sg_trim(st); 93 94 __i915_gem_object_set_pages(obj, st, sg_page_sizes); 95 96 return 0; 97 98 err_free_sg: 99 sg_free_table(st); 100 kfree(st); 101 return ret; 102 } 103 104 void i915_gem_object_init_memory_region(struct drm_i915_gem_object *obj, 105 struct intel_memory_region *mem, 106 unsigned long flags) 107 { 108 INIT_LIST_HEAD(&obj->mm.blocks); 109 obj->mm.region = intel_memory_region_get(mem); 110 obj->flags |= flags; 111 112 mutex_lock(&mem->objects.lock); 113 114 if (obj->flags & I915_BO_ALLOC_VOLATILE) 115 list_add(&obj->mm.region_link, &mem->objects.purgeable); 116 else 117 list_add(&obj->mm.region_link, &mem->objects.list); 118 119 mutex_unlock(&mem->objects.lock); 120 } 121 122 void i915_gem_object_release_memory_region(struct drm_i915_gem_object *obj) 123 { 124 struct intel_memory_region *mem = obj->mm.region; 125 126 mutex_lock(&mem->objects.lock); 127 list_del(&obj->mm.region_link); 128 mutex_unlock(&mem->objects.lock); 129 130 intel_memory_region_put(mem); 131 } 132 133 struct drm_i915_gem_object * 134 i915_gem_object_create_region(struct intel_memory_region *mem, 135 resource_size_t size, 136 unsigned int flags) 137 { 138 struct drm_i915_gem_object *obj; 139 140 /* 141 * NB: Our use of resource_size_t for the size stems from using struct 142 * resource for the mem->region. We might need to revisit this in the 143 * future. 144 */ 145 146 GEM_BUG_ON(flags & ~I915_BO_ALLOC_FLAGS); 147 148 if (!mem) 149 return ERR_PTR(-ENODEV); 150 151 size = round_up(size, mem->min_page_size); 152 153 GEM_BUG_ON(!size); 154 GEM_BUG_ON(!IS_ALIGNED(size, I915_GTT_MIN_ALIGNMENT)); 155 156 /* 157 * XXX: There is a prevalence of the assumption that we fit the 158 * object's page count inside a 32bit _signed_ variable. Let's document 159 * this and catch if we ever need to fix it. In the meantime, if you do 160 * spot such a local variable, please consider fixing! 161 */ 162 163 if (size >> PAGE_SHIFT > INT_MAX) 164 return ERR_PTR(-E2BIG); 165 166 if (overflows_type(size, obj->base.size)) 167 return ERR_PTR(-E2BIG); 168 169 obj = mem->ops->create_object(mem, size, flags); 170 if (!IS_ERR(obj)) 171 trace_i915_gem_object_create(obj); 172 173 return obj; 174 } 175