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
commitf436211e327e509a1da7867f0b707ddf627af12e (patch)
tree86730729bd748cb84f2ca76922231220d28e2b2d
parent19d4b99688d8e99e37b8f51b04da596074c85977 (diff)
downloadlinux-yinghai-f436211e327e509a1da7867f0b707ddf627af12e.tar.gz
resources: Add probe_resource()
It is changed from busn_res only version, because Bjorn found that version was not holding resource_lock. Even it may be ok for busn_res not holding resource_lock. It would be better to have it to be generic and use lock and we may use it for other resources. probe_resource() will try to find specified size or more in parent bus. If can not find current parent resource, and it will try to expand parents top. If still can not find that specified on top, it will try to reduce target size until find one. It will return 0, if it find any resource that could be used. Returned resource is registered in the tree. So caller may need to use replace_resource to put real resource in tree. -v3: remove two parameters that is for debug purpose. -v4: fix stop_flags checking. -v5: adjust stop_flags checking position to avoid not needed calling into allocate_resource(). -v6: use updated __allocate_resource. -v7: Linus said: "resource_extend_parents()" thing is too dangerous as it is. It can corrupt the resource list by making the resource end overlap with the next resource (for extension) or not cover all the child resources (for shrinking). Try to fold in resource_extend_parents_top, and have updated one resource_shrink_parent_top() with adjust_resource that will checking parent and children covering. -v8: Linus said: allocation/return is not right, and -1 step tricks make it not work as generic resource probe. So try to remove the needed_size tricks, and also use __adjust_resource for probing instead. -v9: remove two lines that is supposed to be removed after converting to use __adjust_resource -v10: split out two __adjust_resource and shrink_parent_res_top to another two patches. Also remove the local limit condition. Signed-off-by: Yinghai Lu <yinghai@kernel.org> Cc: Andrew Morton <akpm@linux-foundation.org>
-rw-r--r--include/linux/ioport.h4
-rw-r--r--kernel/resource.c104
2 files changed, 108 insertions, 0 deletions
diff --git a/include/linux/ioport.h b/include/linux/ioport.h
index 43d5e451cf9e2..fff4c56741640 100644
--- a/include/linux/ioport.h
+++ b/include/linux/ioport.h
@@ -158,6 +158,10 @@ extern int allocate_resource(struct resource *root, struct resource *new,
void *alignf_data);
int resource_shrink_parents_top(struct resource *b_res,
long size, struct resource *parent_res);
+int probe_resource(struct resource *b_res,
+ struct resource *busn_res,
+ resource_size_t needed_size, struct resource **p,
+ int skip_nr, int flags);
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 50c36fb4dbcd3..fe6ad96beca0b 100644
--- a/kernel/resource.c
+++ b/kernel/resource.c
@@ -1061,6 +1061,110 @@ int resource_shrink_parents_top(struct resource *b_res,
return ret;
}
+static resource_size_t __find_res_under_top_free_size(struct resource *res,
+ int skip_nr)
+{
+ resource_size_t n_size;
+
+ /*
+ * find out free number below res->end that we can use.
+ * res->start to res->start + skip_nr - 1 can not be used.
+ */
+ if (res->child) {
+ struct resource *child = res->child;
+
+ while (child->sibling)
+ child = child->sibling;
+
+ /* check if children cover skip_nr */
+ if (child->end - res->start + 1 >= skip_nr)
+ return res->end - child->end;
+ }
+
+ n_size = resource_size(res);
+ if (n_size <= skip_nr)
+ return 0;
+ return n_size - skip_nr;
+}
+
+/**
+ * probe_resource - Probe resource in parent resource.
+ * @b_res: parent resource descriptor
+ * @busn_res: return probed resource
+ * @needed_size: target size
+ * @p: pointer to farest parent that we extend the top
+ * @skip_nr: number in b_res start that we need to skip.
+ * @stop_flags: flags for stopping extend parent res
+ *
+ * will try to allocate resource in b_res, if can not find the range
+ * will try to extend parent resources' top.
+ */
+int probe_resource(struct resource *b_res,
+ struct resource *busn_res,
+ resource_size_t needed_size, struct resource **p,
+ int skip_nr, int stop_flags)
+{
+ int ret;
+ resource_size_t n_size;
+ struct resource *parent_res;
+
+ write_lock(&resource_lock);
+ /*
+ * We first try to allocate range in b_res that
+ * we can use in b_res directly.
+ * we also need to skip skip_nr from start of b_res.
+ */
+ memset(busn_res, 0, sizeof(struct resource));
+ ret = __allocate_resource(b_res, busn_res, needed_size,
+ b_res->start + skip_nr, b_res->end,
+ 1, NULL, NULL);
+ if (!ret) {
+ *p = NULL;
+ goto out;
+ }
+
+ /* Try to extend the top of parent resources to meet needed_size */
+
+ /* b_res could be root bus resource and can not be extended */
+ if (b_res->flags & stop_flags)
+ goto out;
+
+ /* find out free range under top at first */
+ n_size = __find_res_under_top_free_size(b_res, skip_nr);
+
+ /* Probe extended range above top */
+ memset(busn_res, 0, sizeof(struct resource));
+ parent_res = b_res;
+ while (parent_res && !(parent_res->flags & stop_flags)) {
+ ret = __adjust_resource(parent_res, parent_res->start,
+ resource_size(parent_res) + (needed_size - n_size));
+ if (!ret) {
+ struct resource *res = b_res;
+
+ /* save parent_res, we need it as stopper later */
+ *p = parent_res->parent;
+
+ /* extend parent resources top */
+ while (res && res != parent_res) {
+ res->end += needed_size - n_size;
+ res = res->parent;
+ }
+
+ ret = __allocate_resource(b_res, busn_res, needed_size,
+ b_res->start + skip_nr, b_res->end,
+ 1, NULL, NULL);
+ /* ret must be 0 here*/
+ goto out;
+ }
+ parent_res = parent_res->parent;
+ }
+
+out:
+ write_unlock(&resource_lock);
+
+ return ret;
+}
+
/*
* Managed region resource
*/