aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/resource.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/resource.c')
-rw-r--r--kernel/resource.c171
1 files changed, 144 insertions, 27 deletions
diff --git a/kernel/resource.c b/kernel/resource.c
index 7b36976e5dea..3ff40178dce7 100644
--- a/kernel/resource.c
+++ b/kernel/resource.c
@@ -38,6 +38,14 @@ struct resource iomem_resource = {
38}; 38};
39EXPORT_SYMBOL(iomem_resource); 39EXPORT_SYMBOL(iomem_resource);
40 40
41/* constraints to be met while allocating resources */
42struct resource_constraint {
43 resource_size_t min, max, align;
44 resource_size_t (*alignf)(void *, const struct resource *,
45 resource_size_t, resource_size_t);
46 void *alignf_data;
47};
48
41static DEFINE_RWLOCK(resource_lock); 49static DEFINE_RWLOCK(resource_lock);
42 50
43static void *r_next(struct seq_file *m, void *v, loff_t *pos) 51static void *r_next(struct seq_file *m, void *v, loff_t *pos)
@@ -357,57 +365,148 @@ int __weak page_is_ram(unsigned long pfn)
357 return walk_system_ram_range(pfn, 1, NULL, __is_ram) == 1; 365 return walk_system_ram_range(pfn, 1, NULL, __is_ram) == 1;
358} 366}
359 367
368void __weak arch_remove_reservations(struct resource *avail)
369{
370}
371
372static resource_size_t simple_align_resource(void *data,
373 const struct resource *avail,
374 resource_size_t size,
375 resource_size_t align)
376{
377 return avail->start;
378}
379
380static void resource_clip(struct resource *res, resource_size_t min,
381 resource_size_t max)
382{
383 if (res->start < min)
384 res->start = min;
385 if (res->end > max)
386 res->end = max;
387}
388
389static bool resource_contains(struct resource *res1, struct resource *res2)
390{
391 return res1->start <= res2->start && res1->end >= res2->end;
392}
393
360/* 394/*
361 * Find empty slot in the resource tree given range and alignment. 395 * Find empty slot in the resource tree with the given range and
396 * alignment constraints
362 */ 397 */
363static int find_resource(struct resource *root, struct resource *new, 398static int __find_resource(struct resource *root, struct resource *old,
364 resource_size_t size, resource_size_t min, 399 struct resource *new,
365 resource_size_t max, resource_size_t align, 400 resource_size_t size,
366 resource_size_t (*alignf)(void *, 401 struct resource_constraint *constraint)
367 const struct resource *,
368 resource_size_t,
369 resource_size_t),
370 void *alignf_data)
371{ 402{
372 struct resource *this = root->child; 403 struct resource *this = root->child;
373 struct resource tmp = *new; 404 struct resource tmp = *new, avail, alloc;
374 405
406 tmp.flags = new->flags;
375 tmp.start = root->start; 407 tmp.start = root->start;
376 /* 408 /*
377 * Skip past an allocated resource that starts at 0, since the assignment 409 * Skip past an allocated resource that starts at 0, since the assignment
378 * of this->start - 1 to tmp->end below would cause an underflow. 410 * of this->start - 1 to tmp->end below would cause an underflow.
379 */ 411 */
380 if (this && this->start == 0) { 412 if (this && this->start == root->start) {
381 tmp.start = this->end + 1; 413 tmp.start = (this == old) ? old->start : this->end + 1;
382 this = this->sibling; 414 this = this->sibling;
383 } 415 }
384 for(;;) { 416 for(;;) {
385 if (this) 417 if (this)
386 tmp.end = this->start - 1; 418 tmp.end = (this == old) ? this->end : this->start - 1;
387 else 419 else
388 tmp.end = root->end; 420 tmp.end = root->end;
389 if (tmp.start < min) 421
390 tmp.start = min; 422 resource_clip(&tmp, constraint->min, constraint->max);
391 if (tmp.end > max) 423 arch_remove_reservations(&tmp);
392 tmp.end = max; 424
393 tmp.start = ALIGN(tmp.start, align); 425 /* Check for overflow after ALIGN() */
394 if (alignf) 426 avail = *new;
395 tmp.start = alignf(alignf_data, &tmp, size, align); 427 avail.start = ALIGN(tmp.start, constraint->align);
396 if (tmp.start < tmp.end && tmp.end - tmp.start >= size - 1) { 428 avail.end = tmp.end;
397 new->start = tmp.start; 429 if (avail.start >= tmp.start) {
398 new->end = tmp.start + size - 1; 430 alloc.start = constraint->alignf(constraint->alignf_data, &avail,
399 return 0; 431 size, constraint->align);
432 alloc.end = alloc.start + size - 1;
433 if (resource_contains(&avail, &alloc)) {
434 new->start = alloc.start;
435 new->end = alloc.end;
436 return 0;
437 }
400 } 438 }
401 if (!this) 439 if (!this)
402 break; 440 break;
403 tmp.start = this->end + 1; 441 if (this != old)
442 tmp.start = this->end + 1;
404 this = this->sibling; 443 this = this->sibling;
405 } 444 }
406 return -EBUSY; 445 return -EBUSY;
407} 446}
408 447
448/*
449 * Find empty slot in the resource tree given range and alignment.
450 */
451static int find_resource(struct resource *root, struct resource *new,
452 resource_size_t size,
453 struct resource_constraint *constraint)
454{
455 return __find_resource(root, NULL, new, size, constraint);
456}
457
458/**
459 * reallocate_resource - allocate a slot in the resource tree given range & alignment.
460 * The resource will be relocated if the new size cannot be reallocated in the
461 * current location.
462 *
463 * @root: root resource descriptor
464 * @old: resource descriptor desired by caller
465 * @newsize: new size of the resource descriptor
466 * @constraint: the size and alignment constraints to be met.
467 */
468int reallocate_resource(struct resource *root, struct resource *old,
469 resource_size_t newsize,
470 struct resource_constraint *constraint)
471{
472 int err=0;
473 struct resource new = *old;
474 struct resource *conflict;
475
476 write_lock(&resource_lock);
477
478 if ((err = __find_resource(root, old, &new, newsize, constraint)))
479 goto out;
480
481 if (resource_contains(&new, old)) {
482 old->start = new.start;
483 old->end = new.end;
484 goto out;
485 }
486
487 if (old->child) {
488 err = -EBUSY;
489 goto out;
490 }
491
492 if (resource_contains(old, &new)) {
493 old->start = new.start;
494 old->end = new.end;
495 } else {
496 __release_resource(old);
497 *old = new;
498 conflict = __request_resource(root, old);
499 BUG_ON(conflict);
500 }
501out:
502 write_unlock(&resource_lock);
503 return err;
504}
505
506
409/** 507/**
410 * allocate_resource - allocate empty slot in the resource tree given range & alignment 508 * allocate_resource - allocate empty slot in the resource tree given range & alignment.
509 * The resource will be reallocated with a new size if it was already allocated
411 * @root: root resource descriptor 510 * @root: root resource descriptor
412 * @new: resource descriptor desired by caller 511 * @new: resource descriptor desired by caller
413 * @size: requested resource region size 512 * @size: requested resource region size
@@ -427,9 +526,25 @@ int allocate_resource(struct resource *root, struct resource *new,
427 void *alignf_data) 526 void *alignf_data)
428{ 527{
429 int err; 528 int err;
529 struct resource_constraint constraint;
530
531 if (!alignf)
532 alignf = simple_align_resource;
533
534 constraint.min = min;
535 constraint.max = max;
536 constraint.align = align;
537 constraint.alignf = alignf;
538 constraint.alignf_data = alignf_data;
539
540 if ( new->parent ) {
541 /* resource is already allocated, try reallocating with
542 the new constraints */
543 return reallocate_resource(root, new, size, &constraint);
544 }
430 545
431 write_lock(&resource_lock); 546 write_lock(&resource_lock);
432 err = find_resource(root, new, size, min, max, align, alignf, alignf_data); 547 err = find_resource(root, new, size, &constraint);
433 if (err >= 0 && __request_resource(root, new)) 548 if (err >= 0 && __request_resource(root, new))
434 err = -EBUSY; 549 err = -EBUSY;
435 write_unlock(&resource_lock); 550 write_unlock(&resource_lock);
@@ -453,6 +568,8 @@ static struct resource * __insert_resource(struct resource *parent, struct resou
453 568
454 if (first == parent) 569 if (first == parent)
455 return first; 570 return first;
571 if (WARN_ON(first == new)) /* duplicated insertion */
572 return first;
456 573
457 if ((first->start > new->start) || (first->end < new->end)) 574 if ((first->start > new->start) || (first->end < new->end))
458 break; 575 break;