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

drm/mm: Reject over-sized allocation requests early

As we keep an rbtree of available holes sorted by their size, we can
very easily determine if there is any hole large enough that might
satisfy the allocation request. This helps when dealing with a highly
fragmented address space and a request for a search by address.

To cache the largest size, we convert into the cached rbtree variant
which tracks the leftmost node for us. However, currently we sorted into
ascending size order so the leftmost node is the smallest, and so to
make it the largest hole we need to invert our sorting.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
Reviewed-by: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20180521082131.13744-1-chris@chris-wilson.co.uk

+58 -26
+57 -25
drivers/gpu/drm/drm_mm.c
··· 239 239 #define HOLE_SIZE(NODE) ((NODE)->hole_size) 240 240 #define HOLE_ADDR(NODE) (__drm_mm_hole_node_start(NODE)) 241 241 242 + static u64 rb_to_hole_size(struct rb_node *rb) 243 + { 244 + return rb_entry(rb, struct drm_mm_node, rb_hole_size)->hole_size; 245 + } 246 + 247 + static void insert_hole_size(struct rb_root_cached *root, 248 + struct drm_mm_node *node) 249 + { 250 + struct rb_node **link = &root->rb_root.rb_node, *rb = NULL; 251 + u64 x = node->hole_size; 252 + bool first = true; 253 + 254 + while (*link) { 255 + rb = *link; 256 + if (x > rb_to_hole_size(rb)) { 257 + link = &rb->rb_left; 258 + } else { 259 + link = &rb->rb_right; 260 + first = false; 261 + } 262 + } 263 + 264 + rb_link_node(&node->rb_hole_size, rb, link); 265 + rb_insert_color_cached(&node->rb_hole_size, root, first); 266 + } 267 + 242 268 static void add_hole(struct drm_mm_node *node) 243 269 { 244 270 struct drm_mm *mm = node->mm; ··· 273 247 __drm_mm_hole_node_end(node) - __drm_mm_hole_node_start(node); 274 248 DRM_MM_BUG_ON(!drm_mm_hole_follows(node)); 275 249 276 - RB_INSERT(mm->holes_size, rb_hole_size, HOLE_SIZE); 250 + insert_hole_size(&mm->holes_size, node); 277 251 RB_INSERT(mm->holes_addr, rb_hole_addr, HOLE_ADDR); 278 252 279 253 list_add(&node->hole_stack, &mm->hole_stack); ··· 284 258 DRM_MM_BUG_ON(!drm_mm_hole_follows(node)); 285 259 286 260 list_del(&node->hole_stack); 287 - rb_erase(&node->rb_hole_size, &node->mm->holes_size); 261 + rb_erase_cached(&node->rb_hole_size, &node->mm->holes_size); 288 262 rb_erase(&node->rb_hole_addr, &node->mm->holes_addr); 289 263 node->hole_size = 0; 290 264 ··· 308 282 309 283 static struct drm_mm_node *best_hole(struct drm_mm *mm, u64 size) 310 284 { 311 - struct rb_node *best = NULL; 312 - struct rb_node **link = &mm->holes_size.rb_node; 285 + struct rb_node *rb = mm->holes_size.rb_root.rb_node; 286 + struct drm_mm_node *best = NULL; 313 287 314 - while (*link) { 315 - struct rb_node *rb = *link; 288 + do { 289 + struct drm_mm_node *node = 290 + rb_entry(rb, struct drm_mm_node, rb_hole_size); 316 291 317 - if (size <= rb_hole_size(rb)) { 318 - link = &rb->rb_left; 319 - best = rb; 292 + if (size <= node->hole_size) { 293 + best = node; 294 + rb = rb->rb_right; 320 295 } else { 321 - link = &rb->rb_right; 296 + rb = rb->rb_left; 322 297 } 323 - } 298 + } while (rb); 324 299 325 - return rb_hole_size_to_node(best); 300 + return best; 326 301 } 327 302 328 303 static struct drm_mm_node *find_hole(struct drm_mm *mm, u64 addr) 329 304 { 305 + struct rb_node *rb = mm->holes_addr.rb_node; 330 306 struct drm_mm_node *node = NULL; 331 - struct rb_node **link = &mm->holes_addr.rb_node; 332 307 333 - while (*link) { 308 + while (rb) { 334 309 u64 hole_start; 335 310 336 - node = rb_hole_addr_to_node(*link); 311 + node = rb_hole_addr_to_node(rb); 337 312 hole_start = __drm_mm_hole_node_start(node); 338 313 339 314 if (addr < hole_start) 340 - link = &node->rb_hole_addr.rb_left; 315 + rb = node->rb_hole_addr.rb_left; 341 316 else if (addr > hole_start + node->hole_size) 342 - link = &node->rb_hole_addr.rb_right; 317 + rb = node->rb_hole_addr.rb_right; 343 318 else 344 319 break; 345 320 } ··· 353 326 u64 start, u64 end, u64 size, 354 327 enum drm_mm_insert_mode mode) 355 328 { 356 - if (RB_EMPTY_ROOT(&mm->holes_size)) 357 - return NULL; 358 - 359 329 switch (mode) { 360 330 default: 361 331 case DRM_MM_INSERT_BEST: ··· 379 355 switch (mode) { 380 356 default: 381 357 case DRM_MM_INSERT_BEST: 382 - return rb_hole_size_to_node(rb_next(&node->rb_hole_size)); 358 + return rb_hole_size_to_node(rb_prev(&node->rb_hole_size)); 383 359 384 360 case DRM_MM_INSERT_LOW: 385 361 return rb_hole_addr_to_node(rb_next(&node->rb_hole_addr)); ··· 450 426 } 451 427 EXPORT_SYMBOL(drm_mm_reserve_node); 452 428 429 + static u64 rb_to_hole_size_or_zero(struct rb_node *rb) 430 + { 431 + return rb ? rb_to_hole_size(rb) : 0; 432 + } 433 + 453 434 /** 454 435 * drm_mm_insert_node_in_range - ranged search for space and insert @node 455 436 * @mm: drm_mm to allocate from ··· 484 455 DRM_MM_BUG_ON(range_start >= range_end); 485 456 486 457 if (unlikely(size == 0 || range_end - range_start < size)) 458 + return -ENOSPC; 459 + 460 + if (rb_to_hole_size_or_zero(rb_first_cached(&mm->holes_size)) < size) 487 461 return -ENOSPC; 488 462 489 463 if (alignment <= 1) ··· 619 587 620 588 if (drm_mm_hole_follows(old)) { 621 589 list_replace(&old->hole_stack, &new->hole_stack); 622 - rb_replace_node(&old->rb_hole_size, 623 - &new->rb_hole_size, 624 - &mm->holes_size); 590 + rb_replace_node_cached(&old->rb_hole_size, 591 + &new->rb_hole_size, 592 + &mm->holes_size); 625 593 rb_replace_node(&old->rb_hole_addr, 626 594 &new->rb_hole_addr, 627 595 &mm->holes_addr); ··· 917 885 918 886 INIT_LIST_HEAD(&mm->hole_stack); 919 887 mm->interval_tree = RB_ROOT_CACHED; 920 - mm->holes_size = RB_ROOT; 888 + mm->holes_size = RB_ROOT_CACHED; 921 889 mm->holes_addr = RB_ROOT; 922 890 923 891 /* Clever trick to avoid a special case in the free hole tracking. */
+1 -1
include/drm/drm_mm.h
··· 173 173 struct drm_mm_node head_node; 174 174 /* Keep an interval_tree for fast lookup of drm_mm_nodes by address. */ 175 175 struct rb_root_cached interval_tree; 176 - struct rb_root holes_size; 176 + struct rb_root_cached holes_size; 177 177 struct rb_root holes_addr; 178 178 179 179 unsigned long scan_active;