Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
11void
12i915_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
22int
23i915_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
102err_free_sg:
103 sg_free_table(st);
104 kfree(st);
105 return ret;
106}
107
108void 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
127void 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
138struct drm_i915_gem_object *
139i915_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
176err_object_free:
177 i915_gem_object_free(obj);
178 return ERR_PTR(err);
179}