diff options
Diffstat (limited to 'kernel/resource.c')
-rw-r--r-- | kernel/resource.c | 116 |
1 files changed, 98 insertions, 18 deletions
diff --git a/kernel/resource.c b/kernel/resource.c index 798e2fae2a06..3ff40178dce7 100644 --- a/kernel/resource.c +++ b/kernel/resource.c | |||
@@ -38,6 +38,14 @@ struct resource iomem_resource = { | |||
38 | }; | 38 | }; |
39 | EXPORT_SYMBOL(iomem_resource); | 39 | EXPORT_SYMBOL(iomem_resource); |
40 | 40 | ||
41 | /* constraints to be met while allocating resources */ | ||
42 | struct 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 | |||
41 | static DEFINE_RWLOCK(resource_lock); | 49 | static DEFINE_RWLOCK(resource_lock); |
42 | 50 | ||
43 | static void *r_next(struct seq_file *m, void *v, loff_t *pos) | 51 | static void *r_next(struct seq_file *m, void *v, loff_t *pos) |
@@ -384,16 +392,13 @@ static bool resource_contains(struct resource *res1, struct resource *res2) | |||
384 | } | 392 | } |
385 | 393 | ||
386 | /* | 394 | /* |
387 | * 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 | ||
388 | */ | 397 | */ |
389 | static int find_resource(struct resource *root, struct resource *new, | 398 | static int __find_resource(struct resource *root, struct resource *old, |
390 | resource_size_t size, resource_size_t min, | 399 | struct resource *new, |
391 | resource_size_t max, resource_size_t align, | 400 | resource_size_t size, |
392 | resource_size_t (*alignf)(void *, | 401 | struct resource_constraint *constraint) |
393 | const struct resource *, | ||
394 | resource_size_t, | ||
395 | resource_size_t), | ||
396 | void *alignf_data) | ||
397 | { | 402 | { |
398 | struct resource *this = root->child; | 403 | struct resource *this = root->child; |
399 | struct resource tmp = *new, avail, alloc; | 404 | struct resource tmp = *new, avail, alloc; |
@@ -404,25 +409,26 @@ static int find_resource(struct resource *root, struct resource *new, | |||
404 | * 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 |
405 | * 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. |
406 | */ | 411 | */ |
407 | if (this && this->start == 0) { | 412 | if (this && this->start == root->start) { |
408 | tmp.start = this->end + 1; | 413 | tmp.start = (this == old) ? old->start : this->end + 1; |
409 | this = this->sibling; | 414 | this = this->sibling; |
410 | } | 415 | } |
411 | for(;;) { | 416 | for(;;) { |
412 | if (this) | 417 | if (this) |
413 | tmp.end = this->start - 1; | 418 | tmp.end = (this == old) ? this->end : this->start - 1; |
414 | else | 419 | else |
415 | tmp.end = root->end; | 420 | tmp.end = root->end; |
416 | 421 | ||
417 | resource_clip(&tmp, min, max); | 422 | resource_clip(&tmp, constraint->min, constraint->max); |
418 | arch_remove_reservations(&tmp); | 423 | arch_remove_reservations(&tmp); |
419 | 424 | ||
420 | /* Check for overflow after ALIGN() */ | 425 | /* Check for overflow after ALIGN() */ |
421 | avail = *new; | 426 | avail = *new; |
422 | avail.start = ALIGN(tmp.start, align); | 427 | avail.start = ALIGN(tmp.start, constraint->align); |
423 | avail.end = tmp.end; | 428 | avail.end = tmp.end; |
424 | if (avail.start >= tmp.start) { | 429 | if (avail.start >= tmp.start) { |
425 | alloc.start = alignf(alignf_data, &avail, size, align); | 430 | alloc.start = constraint->alignf(constraint->alignf_data, &avail, |
431 | size, constraint->align); | ||
426 | alloc.end = alloc.start + size - 1; | 432 | alloc.end = alloc.start + size - 1; |
427 | if (resource_contains(&avail, &alloc)) { | 433 | if (resource_contains(&avail, &alloc)) { |
428 | new->start = alloc.start; | 434 | new->start = alloc.start; |
@@ -432,14 +438,75 @@ static int find_resource(struct resource *root, struct resource *new, | |||
432 | } | 438 | } |
433 | if (!this) | 439 | if (!this) |
434 | break; | 440 | break; |
435 | tmp.start = this->end + 1; | 441 | if (this != old) |
442 | tmp.start = this->end + 1; | ||
436 | this = this->sibling; | 443 | this = this->sibling; |
437 | } | 444 | } |
438 | return -EBUSY; | 445 | return -EBUSY; |
439 | } | 446 | } |
440 | 447 | ||
448 | /* | ||
449 | * Find empty slot in the resource tree given range and alignment. | ||
450 | */ | ||
451 | static 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 | |||
441 | /** | 458 | /** |
442 | * allocate_resource - allocate empty slot in the resource tree given range & alignment | 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 | */ | ||
468 | int 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 | } | ||
501 | out: | ||
502 | write_unlock(&resource_lock); | ||
503 | return err; | ||
504 | } | ||
505 | |||
506 | |||
507 | /** | ||
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 | ||
443 | * @root: root resource descriptor | 510 | * @root: root resource descriptor |
444 | * @new: resource descriptor desired by caller | 511 | * @new: resource descriptor desired by caller |
445 | * @size: requested resource region size | 512 | * @size: requested resource region size |
@@ -459,12 +526,25 @@ int allocate_resource(struct resource *root, struct resource *new, | |||
459 | void *alignf_data) | 526 | void *alignf_data) |
460 | { | 527 | { |
461 | int err; | 528 | int err; |
529 | struct resource_constraint constraint; | ||
462 | 530 | ||
463 | if (!alignf) | 531 | if (!alignf) |
464 | alignf = simple_align_resource; | 532 | alignf = simple_align_resource; |
465 | 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 | } | ||
545 | |||
466 | write_lock(&resource_lock); | 546 | write_lock(&resource_lock); |
467 | err = find_resource(root, new, size, min, max, align, alignf, alignf_data); | 547 | err = find_resource(root, new, size, &constraint); |
468 | if (err >= 0 && __request_resource(root, new)) | 548 | if (err >= 0 && __request_resource(root, new)) |
469 | err = -EBUSY; | 549 | err = -EBUSY; |
470 | write_unlock(&resource_lock); | 550 | write_unlock(&resource_lock); |