aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYinghai Lu <yinghai@kernel.org>2012-09-17 22:24:28 -0700
committerYinghai Lu <yinghai@kernel.org>2012-09-17 22:24:28 -0700
commit19d4b99688d8e99e37b8f51b04da596074c85977 (patch)
treec9c85dea49cb1e35118cef0882a88d8bdf34c132
parent30978d17fe9b4c8b89423326e50e5dfe45180ec5 (diff)
downloadlinux-yinghai-19d4b99688d8e99e37b8f51b04da596074c85977.tar.gz
resources: Add resource_shrink_parents_top()
We use it to shrink the resource top of parents that could be expanded before. Signed-off-by: Yinghai Lu <yinghai@kernel.org> Cc: Andrew Morton <akpm@linux-foundation.org>
-rw-r--r--include/linux/ioport.h2
-rw-r--r--kernel/resource.c41
2 files changed, 43 insertions, 0 deletions
diff --git a/include/linux/ioport.h b/include/linux/ioport.h
index 589e0e75efae2d..43d5e451cf9e28 100644
--- a/include/linux/ioport.h
+++ b/include/linux/ioport.h
@@ -156,6 +156,8 @@ extern int allocate_resource(struct resource *root, struct resource *new,
resource_size_t,
resource_size_t),
void *alignf_data);
+int resource_shrink_parents_top(struct resource *b_res,
+ long size, struct resource *parent_res);
struct resource *lookup_resource(struct resource *root, resource_size_t start);
int adjust_resource(struct resource *res, resource_size_t start,
resource_size_t size);
diff --git a/kernel/resource.c b/kernel/resource.c
index efbbe9f5d2bbd9..50c36fb4dbcd35 100644
--- a/kernel/resource.c
+++ b/kernel/resource.c
@@ -1020,6 +1020,47 @@ void __release_region(struct resource *parent, resource_size_t start,
}
EXPORT_SYMBOL(__release_region);
+static int __resource_shrink_parents_top(struct resource *b_res,
+ long size, struct resource *parent_res)
+{
+ struct resource *res = b_res;
+
+ if (size <= 0)
+ return 0;
+
+ while (res && res != parent_res) {
+ if (__adjust_resource(res, res->start,
+ resource_size(res) - size)) {
+ struct resource *tmp = b_res;
+
+ /* roll back */
+ while (tmp != res) {
+ __adjust_resource(tmp, tmp->start,
+ resource_size(tmp) + size);
+ tmp = tmp->parent;
+ }
+
+ return -EBUSY;
+
+ }
+ res = res->parent;
+ }
+
+ return 0;
+}
+
+int resource_shrink_parents_top(struct resource *b_res,
+ long size, struct resource *parent_res)
+{
+ int ret;
+
+ write_lock(&resource_lock);
+ ret = __resource_shrink_parents_top(b_res, size, parent_res);
+ write_unlock(&resource_lock);
+
+ return ret;
+}
+
/*
* Managed region resource
*/