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 	const u64 max_segment = i915_sg_segment_size();
26 	struct intel_memory_region *mem = obj->mm.region;
27 	struct list_head *blocks = &obj->mm.blocks;
28 	resource_size_t size = obj->base.size;
29 	resource_size_t prev_end;
30 	struct i915_buddy_block *block;
31 	unsigned int flags;
32 	struct sg_table *st;
33 	struct scatterlist *sg;
34 	unsigned int sg_page_sizes;
35 	int ret;
36 
37 	st = kmalloc(sizeof(*st), GFP_KERNEL);
38 	if (!st)
39 		return -ENOMEM;
40 
41 	if (sg_alloc_table(st, size >> PAGE_SHIFT, GFP_KERNEL)) {
42 		kfree(st);
43 		return -ENOMEM;
44 	}
45 
46 	flags = I915_ALLOC_MIN_PAGE_SIZE;
47 	if (obj->flags & I915_BO_ALLOC_CONTIGUOUS)
48 		flags |= I915_ALLOC_CONTIGUOUS;
49 
50 	ret = __intel_memory_region_get_pages_buddy(mem, size, flags, blocks);
51 	if (ret)
52 		goto err_free_sg;
53 
54 	GEM_BUG_ON(list_empty(blocks));
55 
56 	sg = st->sgl;
57 	st->nents = 0;
58 	sg_page_sizes = 0;
59 	prev_end = (resource_size_t)-1;
60 
61 	list_for_each_entry(block, blocks, link) {
62 		u64 block_size, offset;
63 
64 		block_size = min_t(u64, size,
65 				   i915_buddy_block_size(&mem->mm, block));
66 		offset = i915_buddy_block_offset(block);
67 
68 		while (block_size) {
69 			u64 len;
70 
71 			if (offset != prev_end || sg->length >= max_segment) {
72 				if (st->nents) {
73 					sg_page_sizes |= sg->length;
74 					sg = __sg_next(sg);
75 				}
76 
77 				sg_dma_address(sg) = mem->region.start + offset;
78 				sg_dma_len(sg) = 0;
79 				sg->length = 0;
80 				st->nents++;
81 			}
82 
83 			len = min(block_size, max_segment - sg->length);
84 			sg->length += len;
85 			sg_dma_len(sg) += len;
86 
87 			offset += len;
88 			block_size -= len;
89 
90 			prev_end = offset;
91 		}
92 	}
93 
94 	sg_page_sizes |= sg->length;
95 	sg_mark_end(sg);
96 	i915_sg_trim(st);
97 
98 	__i915_gem_object_set_pages(obj, st, sg_page_sizes);
99 
100 	return 0;
101 
102 err_free_sg:
103 	sg_free_table(st);
104 	kfree(st);
105 	return ret;
106 }
107 
108 void i915_gem_object_init_memory_region(struct drm_i915_gem_object *obj,
109 					struct intel_memory_region *mem)
110 {
111 	INIT_LIST_HEAD(&obj->mm.blocks);
112 	obj->mm.region = intel_memory_region_get(mem);
113 
114 	if (obj->base.size <= mem->min_page_size)
115 		obj->flags |= I915_BO_ALLOC_CONTIGUOUS;
116 
117 	mutex_lock(&mem->objects.lock);
118 
119 	if (obj->flags & I915_BO_ALLOC_VOLATILE)
120 		list_add(&obj->mm.region_link, &mem->objects.purgeable);
121 	else
122 		list_add(&obj->mm.region_link, &mem->objects.list);
123 
124 	mutex_unlock(&mem->objects.lock);
125 }
126 
127 void i915_gem_object_release_memory_region(struct drm_i915_gem_object *obj)
128 {
129 	struct intel_memory_region *mem = obj->mm.region;
130 
131 	mutex_lock(&mem->objects.lock);
132 	list_del(&obj->mm.region_link);
133 	mutex_unlock(&mem->objects.lock);
134 
135 	intel_memory_region_put(mem);
136 }
137 
138 struct drm_i915_gem_object *
139 i915_gem_object_create_region(struct intel_memory_region *mem,
140 			      resource_size_t size,
141 			      unsigned int flags)
142 {
143 	struct drm_i915_gem_object *obj;
144 	int err;
145 
146 	/*
147 	 * NB: Our use of resource_size_t for the size stems from using struct
148 	 * resource for the mem->region. We might need to revisit this in the
149 	 * future.
150 	 */
151 
152 	GEM_BUG_ON(flags & ~I915_BO_ALLOC_FLAGS);
153 
154 	if (!mem)
155 		return ERR_PTR(-ENODEV);
156 
157 	size = round_up(size, mem->min_page_size);
158 
159 	GEM_BUG_ON(!size);
160 	GEM_BUG_ON(!IS_ALIGNED(size, I915_GTT_MIN_ALIGNMENT));
161 
162 	if (i915_gem_object_size_2big(size))
163 		return ERR_PTR(-E2BIG);
164 
165 	obj = i915_gem_object_alloc();
166 	if (!obj)
167 		return ERR_PTR(-ENOMEM);
168 
169 	err = mem->ops->init_object(mem, obj, size, flags);
170 	if (err)
171 		goto err_object_free;
172 
173 	trace_i915_gem_object_create(obj);
174 	return obj;
175 
176 err_object_free:
177 	i915_gem_object_free(obj);
178 	return ERR_PTR(err);
179 }
180