]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - kernel/resource.c
generic: add phys_addr_t for holding physical addresses
[linux-2.6-omap-h63xx.git] / kernel / resource.c
index f5b518eabefec5cfe880c230fe07ce7b8615f732..fc59dcc4795b772c9aae54f266d50d9a79703db2 100644 (file)
@@ -362,35 +362,21 @@ int allocate_resource(struct resource *root, struct resource *new,
 
 EXPORT_SYMBOL(allocate_resource);
 
-/**
- * insert_resource - Inserts a resource in the resource tree
- * @parent: parent of the new resource
- * @new: new resource to insert
- *
- * Returns 0 on success, -EBUSY if the resource can't be inserted.
- *
- * This function is equivalent to request_resource when no conflict
- * happens. If a conflict happens, and the conflicting resources
- * entirely fit within the range of the new resource, then the new
- * resource is inserted and the conflicting resources become children of
- * the new resource.
+/*
+ * Insert a resource into the resource tree. If successful, return NULL,
+ * otherwise return the conflicting resource (compare to __request_resource())
  */
-int insert_resource(struct resource *parent, struct resource *new)
+static struct resource * __insert_resource(struct resource *parent, struct resource *new)
 {
-       int result;
        struct resource *first, *next;
 
-       write_lock(&resource_lock);
-
        for (;; parent = first) {
-               result = 0;
                first = __request_resource(parent, new);
                if (!first)
-                       goto out;
+                       return first;
 
-               result = -EBUSY;
                if (first == parent)
-                       goto out;
+                       return first;
 
                if ((first->start > new->start) || (first->end < new->end))
                        break;
@@ -401,15 +387,13 @@ int insert_resource(struct resource *parent, struct resource *new)
        for (next = first; ; next = next->sibling) {
                /* Partial overlap? Bad, and unfixable */
                if (next->start < new->start || next->end > new->end)
-                       goto out;
+                       return next;
                if (!next->sibling)
                        break;
                if (next->sibling->start > new->end)
                        break;
        }
 
-       result = 0;
-
        new->parent = parent;
        new->sibling = next->sibling;
        new->child = first;
@@ -426,10 +410,64 @@ int insert_resource(struct resource *parent, struct resource *new)
                        next = next->sibling;
                next->sibling = new;
        }
+       return NULL;
+}
 
- out:
+/**
+ * insert_resource - Inserts a resource in the resource tree
+ * @parent: parent of the new resource
+ * @new: new resource to insert
+ *
+ * Returns 0 on success, -EBUSY if the resource can't be inserted.
+ *
+ * This function is equivalent to request_resource when no conflict
+ * happens. If a conflict happens, and the conflicting resources
+ * entirely fit within the range of the new resource, then the new
+ * resource is inserted and the conflicting resources become children of
+ * the new resource.
+ */
+int insert_resource(struct resource *parent, struct resource *new)
+{
+       struct resource *conflict;
+
+       write_lock(&resource_lock);
+       conflict = __insert_resource(parent, new);
+       write_unlock(&resource_lock);
+       return conflict ? -EBUSY : 0;
+}
+
+/**
+ * insert_resource_expand_to_fit - Insert a resource into the resource tree
+ * @root: root resource descriptor
+ * @new: new resource to insert
+ *
+ * Insert a resource into the resource tree, possibly expanding it in order
+ * to make it encompass any conflicting resources.
+ */
+void insert_resource_expand_to_fit(struct resource *root, struct resource *new)
+{
+       if (new->parent)
+               return;
+
+       write_lock(&resource_lock);
+       for (;;) {
+               struct resource *conflict;
+
+               conflict = __insert_resource(root, new);
+               if (!conflict)
+                       break;
+               if (conflict == root)
+                       break;
+
+               /* Ok, expand resource to cover the conflict, then try again .. */
+               if (conflict->start < new->start)
+                       new->start = conflict->start;
+               if (conflict->end > new->end)
+                       new->end = conflict->end;
+
+               printk("Expanded resource %s due to conflict with %s\n", new->name, conflict->name);
+       }
        write_unlock(&resource_lock);
-       return result;
 }
 
 /**
@@ -478,6 +516,70 @@ int adjust_resource(struct resource *res, resource_size_t start, resource_size_t
        return result;
 }
 
+static void __init __reserve_region_with_split(struct resource *root,
+               resource_size_t start, resource_size_t end,
+               const char *name)
+{
+       struct resource *parent = root;
+       struct resource *conflict;
+       struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL);
+
+       if (!res)
+               return;
+
+       res->name = name;
+       res->start = start;
+       res->end = end;
+       res->flags = IORESOURCE_BUSY;
+
+       for (;;) {
+               conflict = __request_resource(parent, res);
+               if (!conflict)
+                       break;
+               if (conflict != parent) {
+                       parent = conflict;
+                       if (!(conflict->flags & IORESOURCE_BUSY))
+                               continue;
+               }
+
+               /* Uhhuh, that didn't work out.. */
+               kfree(res);
+               res = NULL;
+               break;
+       }
+
+       if (!res) {
+               /* failed, split and try again */
+
+               /* conflict covered whole area */
+               if (conflict->start <= start && conflict->end >= end)
+                       return;
+
+               if (conflict->start > start)
+                       __reserve_region_with_split(root, start, conflict->start-1, name);
+               if (!(conflict->flags & IORESOURCE_BUSY)) {
+                       resource_size_t common_start, common_end;
+
+                       common_start = max(conflict->start, start);
+                       common_end = min(conflict->end, end);
+                       if (common_start < common_end)
+                               __reserve_region_with_split(root, common_start, common_end, name);
+               }
+               if (conflict->end < end)
+                       __reserve_region_with_split(root, conflict->end+1, end, name);
+       }
+
+}
+
+void reserve_region_with_split(struct resource *root,
+               resource_size_t start, resource_size_t end,
+               const char *name)
+{
+       write_lock(&resource_lock);
+       __reserve_region_with_split(root, start, end, name);
+       write_unlock(&resource_lock);
+}
+
 EXPORT_SYMBOL(adjust_resource);
 
 /**