diff options
Diffstat (limited to 'kernel/resource.c')
| -rw-r--r-- | kernel/resource.c | 137 |
1 files changed, 119 insertions, 18 deletions
diff --git a/kernel/resource.c b/kernel/resource.c index 798e2fae2a06..3b3cedc52592 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); |
| @@ -473,6 +553,27 @@ int allocate_resource(struct resource *root, struct resource *new, | |||
| 473 | 553 | ||
| 474 | EXPORT_SYMBOL(allocate_resource); | 554 | EXPORT_SYMBOL(allocate_resource); |
| 475 | 555 | ||
| 556 | /** | ||
| 557 | * lookup_resource - find an existing resource by a resource start address | ||
| 558 | * @root: root resource descriptor | ||
| 559 | * @start: resource start address | ||
| 560 | * | ||
| 561 | * Returns a pointer to the resource if found, NULL otherwise | ||
| 562 | */ | ||
| 563 | struct resource *lookup_resource(struct resource *root, resource_size_t start) | ||
| 564 | { | ||
| 565 | struct resource *res; | ||
| 566 | |||
| 567 | read_lock(&resource_lock); | ||
| 568 | for (res = root->child; res; res = res->sibling) { | ||
| 569 | if (res->start == start) | ||
| 570 | break; | ||
| 571 | } | ||
| 572 | read_unlock(&resource_lock); | ||
| 573 | |||
| 574 | return res; | ||
| 575 | } | ||
| 576 | |||
| 476 | /* | 577 | /* |
| 477 | * Insert a resource into the resource tree. If successful, return NULL, | 578 | * Insert a resource into the resource tree. If successful, return NULL, |
| 478 | * otherwise return the conflicting resource (compare to __request_resource()) | 579 | * otherwise return the conflicting resource (compare to __request_resource()) |
