diff options
Diffstat (limited to 'kernel/resource.c')
-rw-r--r-- | kernel/resource.c | 171 |
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 | }; |
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) |
@@ -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 | ||
368 | void __weak arch_remove_reservations(struct resource *avail) | ||
369 | { | ||
370 | } | ||
371 | |||
372 | static 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 | |||
380 | static 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 | |||
389 | static 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 | */ |
363 | static int find_resource(struct resource *root, struct resource *new, | 398 | static 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 | */ | ||
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 | |||
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 | */ | ||
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 | |||
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; |