resize_region allows enlarging or reducing a region end while checking for conflicts. This is less effort than removing a region and reallocating it.
Signed-off-by: Ahmad Fatoum <[email protected]> --- common/resource.c | 45 ++++++++++++++++++++++++++++++++++++++++++ include/linux/ioport.h | 2 ++ 2 files changed, 47 insertions(+) diff --git a/common/resource.c b/common/resource.c index e391d268e0bb..39867da63b69 100644 --- a/common/resource.c +++ b/common/resource.c @@ -92,6 +92,51 @@ struct resource *__request_region(struct resource *parent, return new; } +int resize_region(struct resource *res, resource_size_t size) +{ + struct resource *parent; + struct resource *next; + resource_size_t newend; + + if (!res) + return 0; + if (!size) + return release_region(res); + + if (size == resource_size(res)) + return 0; + + if (size < resource_size(res)) { + res->end = res->start + size - 1; + return 0; + } + + parent = res->parent; + if (!parent) + return -EINVAL; + + if (check_add_overflow(res->start, size - 1, &newend)) + return -EINVAL; + + if (newend > parent->end) + return -EINVAL; + + /* + * parent->children is the list_head that anchors the ordered list of + * children. If res is not the last entry, the immediate next entry is + * the only sibling we must check. + */ + if (res->sibling.next != &parent->children) { + next = list_next_entry(res, sibling); + + if (newend >= next->start) + return -EBUSY; + } + + res->end = newend; + return 0; +} + /* * release a region previously requested with request_*_region */ diff --git a/include/linux/ioport.h b/include/linux/ioport.h index 5baae0dae10a..8f9191d1c6be 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -272,6 +272,8 @@ static inline void resource_set_range(struct resource *res, resource_set_size(res, size); } +int resize_region(struct resource *res, resource_size_t size); + #define region_is_gap(region) ((region)->flags & IORESOURCE_UNSET) struct resource *resource_iter_first(struct resource *current, struct resource *gap); -- 2.47.3
