Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm: Add support for two-ended allocation, v3

Clients like i915 need to segregate cache domains within the GTT which
can lead to small amounts of fragmentation. By allocating the uncached
buffers from the bottom and the cacheable buffers from the top, we can
reduce the amount of wasted space and also optimize allocation of the
mappable portion of the GTT to only those buffers that require CPU
access through the GTT.

For other drivers, allocating small bos from one end and large ones
from the other helps improve the quality of fragmentation.

Based on drm_mm work by Chris Wilson.

v3: Changed to use a TTM placement flag
v2: Updated kerneldoc

Cc: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Ben Widawsky <ben@bwidawsk.net>
Cc: Christian König <deathsimple@vodafone.de>
Signed-off-by: Lauri Kasanen <cand@gmx.com>
Signed-off-by: David Airlie <airlied@redhat.com>

authored by

Lauri Kasanen and committed by
Dave Airlie
62347f9e 2614dc66

+92 -26
+49 -17
drivers/gpu/drm/drm_mm.c
··· 82 82 * this to implement guard pages between incompatible caching domains in the 83 83 * graphics TT. 84 84 * 85 + * Two behaviors are supported for searching and allocating: bottom-up and top-down. 86 + * The default is bottom-up. Top-down allocation can be used if the memory area 87 + * has different restrictions, or just to reduce fragmentation. 88 + * 85 89 * Finally iteration helpers to walk all nodes and all holes are provided as are 86 90 * some basic allocator dumpers for debugging. 87 91 */ ··· 106 102 static void drm_mm_insert_helper(struct drm_mm_node *hole_node, 107 103 struct drm_mm_node *node, 108 104 unsigned long size, unsigned alignment, 109 - unsigned long color) 105 + unsigned long color, 106 + enum drm_mm_allocator_flags flags) 110 107 { 111 108 struct drm_mm *mm = hole_node->mm; 112 109 unsigned long hole_start = drm_mm_hole_node_start(hole_node); ··· 120 115 if (mm->color_adjust) 121 116 mm->color_adjust(hole_node, color, &adj_start, &adj_end); 122 117 118 + if (flags & DRM_MM_CREATE_TOP) 119 + adj_start = adj_end - size; 120 + 123 121 if (alignment) { 124 122 unsigned tmp = adj_start % alignment; 125 - if (tmp) 126 - adj_start += alignment - tmp; 123 + if (tmp) { 124 + if (flags & DRM_MM_CREATE_TOP) 125 + adj_start -= tmp; 126 + else 127 + adj_start += alignment - tmp; 128 + } 127 129 } 130 + 131 + BUG_ON(adj_start < hole_start); 132 + BUG_ON(adj_end > hole_end); 128 133 129 134 if (adj_start == hole_start) { 130 135 hole_node->hole_follows = 0; ··· 220 205 * @size: size of the allocation 221 206 * @alignment: alignment of the allocation 222 207 * @color: opaque tag value to use for this node 223 - * @flags: flags to fine-tune the allocation 208 + * @sflags: flags to fine-tune the allocation search 209 + * @aflags: flags to fine-tune the allocation behavior 224 210 * 225 211 * The preallocated node must be cleared to 0. 226 212 * ··· 231 215 int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node, 232 216 unsigned long size, unsigned alignment, 233 217 unsigned long color, 234 - enum drm_mm_search_flags flags) 218 + enum drm_mm_search_flags sflags, 219 + enum drm_mm_allocator_flags aflags) 235 220 { 236 221 struct drm_mm_node *hole_node; 237 222 238 223 hole_node = drm_mm_search_free_generic(mm, size, alignment, 239 - color, flags); 224 + color, sflags); 240 225 if (!hole_node) 241 226 return -ENOSPC; 242 227 243 - drm_mm_insert_helper(hole_node, node, size, alignment, color); 228 + drm_mm_insert_helper(hole_node, node, size, alignment, color, aflags); 244 229 return 0; 245 230 } 246 231 EXPORT_SYMBOL(drm_mm_insert_node_generic); ··· 250 233 struct drm_mm_node *node, 251 234 unsigned long size, unsigned alignment, 252 235 unsigned long color, 253 - unsigned long start, unsigned long end) 236 + unsigned long start, unsigned long end, 237 + enum drm_mm_allocator_flags flags) 254 238 { 255 239 struct drm_mm *mm = hole_node->mm; 256 240 unsigned long hole_start = drm_mm_hole_node_start(hole_node); ··· 266 248 if (adj_end > end) 267 249 adj_end = end; 268 250 251 + if (flags & DRM_MM_CREATE_TOP) 252 + adj_start = adj_end - size; 253 + 269 254 if (mm->color_adjust) 270 255 mm->color_adjust(hole_node, color, &adj_start, &adj_end); 271 256 272 257 if (alignment) { 273 258 unsigned tmp = adj_start % alignment; 274 - if (tmp) 275 - adj_start += alignment - tmp; 259 + if (tmp) { 260 + if (flags & DRM_MM_CREATE_TOP) 261 + adj_start -= tmp; 262 + else 263 + adj_start += alignment - tmp; 264 + } 276 265 } 277 266 278 267 if (adj_start == hole_start) { ··· 296 271 INIT_LIST_HEAD(&node->hole_stack); 297 272 list_add(&node->node_list, &hole_node->node_list); 298 273 274 + BUG_ON(node->start < start); 275 + BUG_ON(node->start < adj_start); 299 276 BUG_ON(node->start + node->size > adj_end); 300 277 BUG_ON(node->start + node->size > end); 301 278 ··· 317 290 * @color: opaque tag value to use for this node 318 291 * @start: start of the allowed range for this node 319 292 * @end: end of the allowed range for this node 320 - * @flags: flags to fine-tune the allocation 293 + * @sflags: flags to fine-tune the allocation search 294 + * @aflags: flags to fine-tune the allocation behavior 321 295 * 322 296 * The preallocated node must be cleared to 0. 323 297 * ··· 326 298 * 0 on success, -ENOSPC if there's no suitable hole. 327 299 */ 328 300 int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *node, 329 - unsigned long size, unsigned alignment, unsigned long color, 301 + unsigned long size, unsigned alignment, 302 + unsigned long color, 330 303 unsigned long start, unsigned long end, 331 - enum drm_mm_search_flags flags) 304 + enum drm_mm_search_flags sflags, 305 + enum drm_mm_allocator_flags aflags) 332 306 { 333 307 struct drm_mm_node *hole_node; 334 308 335 309 hole_node = drm_mm_search_free_in_range_generic(mm, 336 310 size, alignment, color, 337 - start, end, flags); 311 + start, end, sflags); 338 312 if (!hole_node) 339 313 return -ENOSPC; 340 314 341 315 drm_mm_insert_helper_range(hole_node, node, 342 316 size, alignment, color, 343 - start, end); 317 + start, end, aflags); 344 318 return 0; 345 319 } 346 320 EXPORT_SYMBOL(drm_mm_insert_node_in_range_generic); ··· 421 391 best = NULL; 422 392 best_size = ~0UL; 423 393 424 - drm_mm_for_each_hole(entry, mm, adj_start, adj_end) { 394 + __drm_mm_for_each_hole(entry, mm, adj_start, adj_end, 395 + flags & DRM_MM_SEARCH_BELOW) { 425 396 if (mm->color_adjust) { 426 397 mm->color_adjust(entry, color, &adj_start, &adj_end); 427 398 if (adj_end <= adj_start) ··· 463 432 best = NULL; 464 433 best_size = ~0UL; 465 434 466 - drm_mm_for_each_hole(entry, mm, adj_start, adj_end) { 435 + __drm_mm_for_each_hole(entry, mm, adj_start, adj_end, 436 + flags & DRM_MM_SEARCH_BELOW) { 467 437 if (adj_start < start) 468 438 adj_start = start; 469 439 if (adj_end > end)
+2 -1
drivers/gpu/drm/i915/i915_gem.c
··· 3264 3264 ret = drm_mm_insert_node_in_range_generic(&vm->mm, &vma->node, 3265 3265 size, alignment, 3266 3266 obj->cache_level, 0, gtt_max, 3267 - DRM_MM_SEARCH_DEFAULT); 3267 + DRM_MM_SEARCH_DEFAULT, 3268 + DRM_MM_CREATE_DEFAULT); 3268 3269 if (ret) { 3269 3270 ret = i915_gem_evict_something(dev, vm, size, alignment, 3270 3271 obj->cache_level, flags);
+2 -1
drivers/gpu/drm/i915/i915_gem_gtt.c
··· 1074 1074 &ppgtt->node, GEN6_PD_SIZE, 1075 1075 GEN6_PD_ALIGN, 0, 1076 1076 0, dev_priv->gtt.base.total, 1077 - DRM_MM_SEARCH_DEFAULT); 1077 + DRM_MM_SEARCH_DEFAULT, 1078 + DRM_MM_CREATE_DEFAULT); 1078 1079 if (ret == -ENOSPC && !retried) { 1079 1080 ret = i915_gem_evict_something(dev, &dev_priv->gtt.base, 1080 1081 GEN6_PD_SIZE, GEN6_PD_ALIGN,
+8 -3
drivers/gpu/drm/ttm/ttm_bo_manager.c
··· 55 55 struct ttm_range_manager *rman = (struct ttm_range_manager *) man->priv; 56 56 struct drm_mm *mm = &rman->mm; 57 57 struct drm_mm_node *node = NULL; 58 + enum drm_mm_allocator_flags aflags = DRM_MM_CREATE_DEFAULT; 58 59 unsigned long lpfn; 59 60 int ret; 60 61 ··· 67 66 if (!node) 68 67 return -ENOMEM; 69 68 69 + if (bo->mem.placement & TTM_PL_FLAG_TOPDOWN) 70 + aflags = DRM_MM_CREATE_TOP; 71 + 70 72 spin_lock(&rman->lock); 71 - ret = drm_mm_insert_node_in_range(mm, node, mem->num_pages, 72 - mem->page_alignment, 73 + ret = drm_mm_insert_node_in_range_generic(mm, node, mem->num_pages, 74 + mem->page_alignment, 0, 73 75 placement->fpfn, lpfn, 74 - DRM_MM_SEARCH_BEST); 76 + DRM_MM_SEARCH_BEST, 77 + aflags); 75 78 spin_unlock(&rman->lock); 76 79 77 80 if (unlikely(ret)) {
+28 -4
include/drm/drm_mm.h
··· 47 47 enum drm_mm_search_flags { 48 48 DRM_MM_SEARCH_DEFAULT = 0, 49 49 DRM_MM_SEARCH_BEST = 1 << 0, 50 + DRM_MM_SEARCH_BELOW = 1 << 1, 50 51 }; 52 + 53 + enum drm_mm_allocator_flags { 54 + DRM_MM_CREATE_DEFAULT = 0, 55 + DRM_MM_CREATE_TOP = 1 << 0, 56 + }; 57 + 58 + #define DRM_MM_BOTTOMUP DRM_MM_SEARCH_DEFAULT, DRM_MM_CREATE_DEFAULT 59 + #define DRM_MM_TOPDOWN DRM_MM_SEARCH_BELOW, DRM_MM_CREATE_TOP 51 60 52 61 struct drm_mm_node { 53 62 struct list_head node_list; ··· 195 186 * Implementation Note: 196 187 * We need to inline list_for_each_entry in order to be able to set hole_start 197 188 * and hole_end on each iteration while keeping the macro sane. 189 + * 190 + * The __drm_mm_for_each_hole version is similar, but with added support for 191 + * going backwards. 198 192 */ 199 193 #define drm_mm_for_each_hole(entry, mm, hole_start, hole_end) \ 200 194 for (entry = list_entry((mm)->hole_stack.next, struct drm_mm_node, hole_stack); \ ··· 206 194 hole_end = drm_mm_hole_node_end(entry), \ 207 195 1 : 0; \ 208 196 entry = list_entry(entry->hole_stack.next, struct drm_mm_node, hole_stack)) 197 + 198 + #define __drm_mm_for_each_hole(entry, mm, hole_start, hole_end, backwards) \ 199 + for (entry = list_entry((backwards) ? (mm)->hole_stack.prev : (mm)->hole_stack.next, struct drm_mm_node, hole_stack); \ 200 + &entry->hole_stack != &(mm)->hole_stack ? \ 201 + hole_start = drm_mm_hole_node_start(entry), \ 202 + hole_end = drm_mm_hole_node_end(entry), \ 203 + 1 : 0; \ 204 + entry = list_entry((backwards) ? entry->hole_stack.prev : entry->hole_stack.next, struct drm_mm_node, hole_stack)) 209 205 210 206 /* 211 207 * Basic range manager support (drm_mm.c) ··· 225 205 unsigned long size, 226 206 unsigned alignment, 227 207 unsigned long color, 228 - enum drm_mm_search_flags flags); 208 + enum drm_mm_search_flags sflags, 209 + enum drm_mm_allocator_flags aflags); 229 210 /** 230 211 * drm_mm_insert_node - search for space and insert @node 231 212 * @mm: drm_mm to allocate from ··· 249 228 unsigned alignment, 250 229 enum drm_mm_search_flags flags) 251 230 { 252 - return drm_mm_insert_node_generic(mm, node, size, alignment, 0, flags); 231 + return drm_mm_insert_node_generic(mm, node, size, alignment, 0, flags, 232 + DRM_MM_CREATE_DEFAULT); 253 233 } 254 234 255 235 int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, ··· 260 238 unsigned long color, 261 239 unsigned long start, 262 240 unsigned long end, 263 - enum drm_mm_search_flags flags); 241 + enum drm_mm_search_flags sflags, 242 + enum drm_mm_allocator_flags aflags); 264 243 /** 265 244 * drm_mm_insert_node_in_range - ranged search for space and insert @node 266 245 * @mm: drm_mm to allocate from ··· 289 266 enum drm_mm_search_flags flags) 290 267 { 291 268 return drm_mm_insert_node_in_range_generic(mm, node, size, alignment, 292 - 0, start, end, flags); 269 + 0, start, end, flags, 270 + DRM_MM_CREATE_DEFAULT); 293 271 } 294 272 295 273 void drm_mm_remove_node(struct drm_mm_node *node);
+3
include/drm/ttm/ttm_placement.h
··· 65 65 * reference the buffer. 66 66 * TTM_PL_FLAG_NO_EVICT means that the buffer may never 67 67 * be evicted to make room for other buffers. 68 + * TTM_PL_FLAG_TOPDOWN requests to be placed from the 69 + * top of the memory area, instead of the bottom. 68 70 */ 69 71 70 72 #define TTM_PL_FLAG_CACHED (1 << 16) ··· 74 72 #define TTM_PL_FLAG_WC (1 << 18) 75 73 #define TTM_PL_FLAG_SHARED (1 << 20) 76 74 #define TTM_PL_FLAG_NO_EVICT (1 << 21) 75 + #define TTM_PL_FLAG_TOPDOWN (1 << 22) 77 76 78 77 #define TTM_PL_MASK_CACHING (TTM_PL_FLAG_CACHED | \ 79 78 TTM_PL_FLAG_UNCACHED | \