1 /* $NetBSD: nouveau_nvkm_core_mm.c,v 1.3 2021/12/18 23:45:34 riastradh Exp $ */ 2 3 /* 4 * Copyright 2012 Red Hat Inc. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 * OTHER DEALINGS IN THE SOFTWARE. 23 * 24 * Authors: Ben Skeggs 25 */ 26 #include <sys/cdefs.h> 27 __KERNEL_RCSID(0, "$NetBSD: nouveau_nvkm_core_mm.c,v 1.3 2021/12/18 23:45:34 riastradh Exp $"); 28 29 #include <core/mm.h> 30 31 #define node(root, dir) ((root)->nl_entry.dir == &mm->nodes) ? NULL : \ 32 list_entry((root)->nl_entry.dir, struct nvkm_mm_node, nl_entry) 33 34 void 35 nvkm_mm_dump(struct nvkm_mm *mm, const char *header) 36 { 37 struct nvkm_mm_node *node; 38 39 pr_err("nvkm: %s\n", header); 40 pr_err("nvkm: node list:\n"); 41 list_for_each_entry(node, &mm->nodes, nl_entry) { 42 pr_err("nvkm: \t%08x %08x %d\n", 43 node->offset, node->length, node->type); 44 } 45 pr_err("nvkm: free list:\n"); 46 list_for_each_entry(node, &mm->free, fl_entry) { 47 pr_err("nvkm: \t%08x %08x %d\n", 48 node->offset, node->length, node->type); 49 } 50 } 51 52 void 53 nvkm_mm_free(struct nvkm_mm *mm, struct nvkm_mm_node **pthis) 54 { 55 struct nvkm_mm_node *this = *pthis; 56 57 if (this) { 58 struct nvkm_mm_node *prev = node(this, prev); 59 struct nvkm_mm_node *next = node(this, next); 60 61 if (prev && prev->type == NVKM_MM_TYPE_NONE) { 62 prev->length += this->length; 63 list_del(&this->nl_entry); 64 kfree(this); this = prev; 65 } 66 67 if (next && next->type == NVKM_MM_TYPE_NONE) { 68 next->offset = this->offset; 69 next->length += this->length; 70 if (this->type == NVKM_MM_TYPE_NONE) 71 list_del(&this->fl_entry); 72 list_del(&this->nl_entry); 73 kfree(this); this = NULL; 74 } 75 76 if (this && this->type != NVKM_MM_TYPE_NONE) { 77 list_for_each_entry(prev, &mm->free, fl_entry) { 78 if (this->offset < prev->offset) 79 break; 80 } 81 82 list_add_tail(&this->fl_entry, &prev->fl_entry); 83 this->type = NVKM_MM_TYPE_NONE; 84 } 85 } 86 87 *pthis = NULL; 88 } 89 90 static struct nvkm_mm_node * 91 region_head(struct nvkm_mm *mm, struct nvkm_mm_node *a, u32 size) 92 { 93 struct nvkm_mm_node *b; 94 95 if (a->length == size) 96 return a; 97 98 b = kmalloc(sizeof(*b), GFP_KERNEL); 99 if (unlikely(b == NULL)) 100 return NULL; 101 102 b->offset = a->offset; 103 b->length = size; 104 b->heap = a->heap; 105 b->type = a->type; 106 a->offset += size; 107 a->length -= size; 108 list_add_tail(&b->nl_entry, &a->nl_entry); 109 if (b->type == NVKM_MM_TYPE_NONE) 110 list_add_tail(&b->fl_entry, &a->fl_entry); 111 112 return b; 113 } 114 115 int 116 nvkm_mm_head(struct nvkm_mm *mm, u8 heap, u8 type, u32 size_max, u32 size_min, 117 u32 align, struct nvkm_mm_node **pnode) 118 { 119 struct nvkm_mm_node *prev, *this, *next; 120 u32 mask = align - 1; 121 u32 splitoff; 122 u32 s, e; 123 124 BUG_ON(type == NVKM_MM_TYPE_NONE || type == NVKM_MM_TYPE_HOLE); 125 126 list_for_each_entry(this, &mm->free, fl_entry) { 127 if (unlikely(heap != NVKM_MM_HEAP_ANY)) { 128 if (this->heap != heap) 129 continue; 130 } 131 e = this->offset + this->length; 132 s = this->offset; 133 134 prev = node(this, prev); 135 if (prev && prev->type != type) 136 s = roundup(s, mm->block_size); 137 138 next = node(this, next); 139 if (next && next->type != type) 140 e = rounddown(e, mm->block_size); 141 142 s = (s + mask) & ~mask; 143 e &= ~mask; 144 if (s > e || e - s < size_min) 145 continue; 146 147 splitoff = s - this->offset; 148 if (splitoff && !region_head(mm, this, splitoff)) 149 return -ENOMEM; 150 151 this = region_head(mm, this, min(size_max, e - s)); 152 if (!this) 153 return -ENOMEM; 154 155 this->next = NULL; 156 this->type = type; 157 list_del(&this->fl_entry); 158 *pnode = this; 159 return 0; 160 } 161 162 return -ENOSPC; 163 } 164 165 static struct nvkm_mm_node * 166 region_tail(struct nvkm_mm *mm, struct nvkm_mm_node *a, u32 size) 167 { 168 struct nvkm_mm_node *b; 169 170 if (a->length == size) 171 return a; 172 173 b = kmalloc(sizeof(*b), GFP_KERNEL); 174 if (unlikely(b == NULL)) 175 return NULL; 176 177 a->length -= size; 178 b->offset = a->offset + a->length; 179 b->length = size; 180 b->heap = a->heap; 181 b->type = a->type; 182 183 list_add(&b->nl_entry, &a->nl_entry); 184 if (b->type == NVKM_MM_TYPE_NONE) 185 list_add(&b->fl_entry, &a->fl_entry); 186 187 return b; 188 } 189 190 int 191 nvkm_mm_tail(struct nvkm_mm *mm, u8 heap, u8 type, u32 size_max, u32 size_min, 192 u32 align, struct nvkm_mm_node **pnode) 193 { 194 struct nvkm_mm_node *prev, *this, *next; 195 u32 mask = align - 1; 196 197 BUG_ON(type == NVKM_MM_TYPE_NONE || type == NVKM_MM_TYPE_HOLE); 198 199 list_for_each_entry_reverse(this, &mm->free, fl_entry) { 200 u32 e = this->offset + this->length; 201 u32 s = this->offset; 202 u32 c = 0, a; 203 if (unlikely(heap != NVKM_MM_HEAP_ANY)) { 204 if (this->heap != heap) 205 continue; 206 } 207 208 prev = node(this, prev); 209 if (prev && prev->type != type) 210 s = roundup(s, mm->block_size); 211 212 next = node(this, next); 213 if (next && next->type != type) { 214 e = rounddown(e, mm->block_size); 215 c = next->offset - e; 216 } 217 218 s = (s + mask) & ~mask; 219 a = e - s; 220 if (s > e || a < size_min) 221 continue; 222 223 a = min(a, size_max); 224 s = (e - a) & ~mask; 225 c += (e - s) - a; 226 227 if (c && !region_tail(mm, this, c)) 228 return -ENOMEM; 229 230 this = region_tail(mm, this, a); 231 if (!this) 232 return -ENOMEM; 233 234 this->next = NULL; 235 this->type = type; 236 list_del(&this->fl_entry); 237 *pnode = this; 238 return 0; 239 } 240 241 return -ENOSPC; 242 } 243 244 int 245 nvkm_mm_init(struct nvkm_mm *mm, u8 heap, u32 offset, u32 length, u32 block) 246 { 247 struct nvkm_mm_node *node, *prev; 248 u32 next; 249 250 if (nvkm_mm_initialised(mm)) { 251 prev = list_last_entry(&mm->nodes, typeof(*node), nl_entry); 252 next = prev->offset + prev->length; 253 if (next != offset) { 254 BUG_ON(next > offset); 255 if (!(node = kzalloc(sizeof(*node), GFP_KERNEL))) 256 return -ENOMEM; 257 node->type = NVKM_MM_TYPE_HOLE; 258 node->offset = next; 259 node->length = offset - next; 260 list_add_tail(&node->nl_entry, &mm->nodes); 261 } 262 BUG_ON(block != mm->block_size); 263 } else { 264 INIT_LIST_HEAD(&mm->nodes); 265 INIT_LIST_HEAD(&mm->free); 266 mm->block_size = block; 267 mm->heap_nodes = 0; 268 } 269 270 node = kzalloc(sizeof(*node), GFP_KERNEL); 271 if (!node) 272 return -ENOMEM; 273 274 if (length) { 275 node->offset = roundup(offset, mm->block_size); 276 node->length = rounddown(offset + length, mm->block_size); 277 node->length -= node->offset; 278 } 279 280 list_add_tail(&node->nl_entry, &mm->nodes); 281 list_add_tail(&node->fl_entry, &mm->free); 282 node->heap = heap; 283 mm->heap_nodes++; 284 return 0; 285 } 286 287 int 288 nvkm_mm_fini(struct nvkm_mm *mm) 289 { 290 struct nvkm_mm_node *node, *temp; 291 int nodes = 0; 292 293 if (!nvkm_mm_initialised(mm)) 294 return 0; 295 296 list_for_each_entry(node, &mm->nodes, nl_entry) { 297 if (node->type != NVKM_MM_TYPE_HOLE) { 298 if (++nodes > mm->heap_nodes) { 299 nvkm_mm_dump(mm, "mm not clean!"); 300 return -EBUSY; 301 } 302 } 303 } 304 305 list_for_each_entry_safe(node, temp, &mm->nodes, nl_entry) { 306 list_del(&node->nl_entry); 307 kfree(node); 308 } 309 310 mm->heap_nodes = 0; 311 return 0; 312 } 313