UBUNTU: Ubuntu-2.6.38-12.51
[linux-flexiantxendom0-natty.git] / kernel / resource.c
index 9c358e2..798e2fa 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/spinlock.h>
 #include <linux/fs.h>
 #include <linux/proc_fs.h>
+#include <linux/sched.h>
 #include <linux/seq_file.h>
 #include <linux/device.h>
 #include <linux/pfn.h>
@@ -356,6 +357,32 @@ int __weak page_is_ram(unsigned long pfn)
        return walk_system_ram_range(pfn, 1, NULL, __is_ram) == 1;
 }
 
+void __weak arch_remove_reservations(struct resource *avail)
+{
+}
+
+static resource_size_t simple_align_resource(void *data,
+                                            const struct resource *avail,
+                                            resource_size_t size,
+                                            resource_size_t align)
+{
+       return avail->start;
+}
+
+static void resource_clip(struct resource *res, resource_size_t min,
+                         resource_size_t max)
+{
+       if (res->start < min)
+               res->start = min;
+       if (res->end > max)
+               res->end = max;
+}
+
+static bool resource_contains(struct resource *res1, struct resource *res2)
+{
+       return res1->start <= res2->start && res1->end >= res2->end;
+}
+
 /*
  * Find empty slot in the resource tree given range and alignment.
  */
@@ -369,8 +396,9 @@ static int find_resource(struct resource *root, struct resource *new,
                         void *alignf_data)
 {
        struct resource *this = root->child;
-       struct resource tmp = *new;
+       struct resource tmp = *new, avail, alloc;
 
+       tmp.flags = new->flags;
        tmp.start = root->start;
        /*
         * Skip past an allocated resource that starts at 0, since the assignment
@@ -385,17 +413,22 @@ static int find_resource(struct resource *root, struct resource *new,
                        tmp.end = this->start - 1;
                else
                        tmp.end = root->end;
-               if (tmp.start < min)
-                       tmp.start = min;
-               if (tmp.end > max)
-                       tmp.end = max;
-               tmp.start = ALIGN(tmp.start, align);
-               if (alignf)
-                       tmp.start = alignf(alignf_data, &tmp, size, align);
-               if (tmp.start < tmp.end && tmp.end - tmp.start >= size - 1) {
-                       new->start = tmp.start;
-                       new->end = tmp.start + size - 1;
-                       return 0;
+
+               resource_clip(&tmp, min, max);
+               arch_remove_reservations(&tmp);
+
+               /* Check for overflow after ALIGN() */
+               avail = *new;
+               avail.start = ALIGN(tmp.start, align);
+               avail.end = tmp.end;
+               if (avail.start >= tmp.start) {
+                       alloc.start = alignf(alignf_data, &avail, size, align);
+                       alloc.end = alloc.start + size - 1;
+                       if (resource_contains(&avail, &alloc)) {
+                               new->start = alloc.start;
+                               new->end = alloc.end;
+                               return 0;
+                       }
                }
                if (!this)
                        break;
@@ -427,6 +460,9 @@ int allocate_resource(struct resource *root, struct resource *new,
 {
        int err;
 
+       if (!alignf)
+               alignf = simple_align_resource;
+
        write_lock(&resource_lock);
        err = find_resource(root, new, size, min, max, align, alignf, alignf_data);
        if (err >= 0 && __request_resource(root, new))
@@ -452,6 +488,8 @@ static struct resource * __insert_resource(struct resource *parent, struct resou
 
                if (first == parent)
                        return first;
+               if (WARN_ON(first == new))      /* duplicated insertion */
+                       return first;
 
                if ((first->start > new->start) || (first->end < new->end))
                        break;
@@ -681,6 +719,8 @@ resource_size_t resource_alignment(struct resource *res)
  * release_region releases a matching busy region.
  */
 
+static DECLARE_WAIT_QUEUE_HEAD(muxed_resource_wait);
+
 /**
  * __request_region - create a new busy resource region
  * @parent: parent resource descriptor
@@ -693,6 +733,7 @@ struct resource * __request_region(struct resource *parent,
                                   resource_size_t start, resource_size_t n,
                                   const char *name, int flags)
 {
+       DECLARE_WAITQUEUE(wait, current);
        struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL);
 
        if (!res)
@@ -717,7 +758,15 @@ struct resource * __request_region(struct resource *parent,
                        if (!(conflict->flags & IORESOURCE_BUSY))
                                continue;
                }
-
+               if (conflict->flags & flags & IORESOURCE_MUXED) {
+                       add_wait_queue(&muxed_resource_wait, &wait);
+                       write_unlock(&resource_lock);
+                       set_current_state(TASK_UNINTERRUPTIBLE);
+                       schedule();
+                       remove_wait_queue(&muxed_resource_wait, &wait);
+                       write_lock(&resource_lock);
+                       continue;
+               }
                /* Uhhuh, that didn't work out.. */
                kfree(res);
                res = NULL;
@@ -791,6 +840,8 @@ void __release_region(struct resource *parent, resource_size_t start,
                                break;
                        *p = res->sibling;
                        write_unlock(&resource_lock);
+                       if (res->flags & IORESOURCE_MUXED)
+                               wake_up(&muxed_resource_wait);
                        kfree(res);
                        return;
                }