]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - kernel/resource.c
resource: fix locking in find_next_iomem_res()
[mirror_ubuntu-bionic-kernel.git] / kernel / resource.c
index 54ba6de3757c7ebdc83ad7f6c5b3d22b9d20f600..ea80a68a7fbc37abc2db6e9e332f67c1581323c3 100644 (file)
@@ -351,24 +351,27 @@ int release_resource(struct resource *old)
 
 EXPORT_SYMBOL(release_resource);
 
-/*
- * Finds the lowest iomem resource existing within [res->start.res->end).
- * The caller must specify res->start, res->end, res->flags, and optionally
- * desc.  If found, returns 0, res is overwritten, if not found, returns -1.
- * This function walks the whole tree and not just first level children until
- * and unless first_level_children_only is true.
+/**
+ * Finds the lowest iomem resource that covers part of [start..end].  The
+ * caller must specify start, end, flags, and desc (which may be
+ * IORES_DESC_NONE).
+ *
+ * If a resource is found, returns 0 and *res is overwritten with the part
+ * of the resource that's within [start..end]; if none is found, returns
+ * -ENODEV.  Returns -EINVAL for invalid parameters.
+ *
+ * This function walks the whole tree and not just first level children
+ * unless @first_level_children_only is true.
  */
-static int find_next_iomem_res(struct resource *res, unsigned long desc,
-                              bool first_level_children_only)
+static int find_next_iomem_res(resource_size_t start, resource_size_t end,
+                              unsigned long flags, unsigned long desc,
+                              bool first_level_children_only,
+                              struct resource *res)
 {
-       resource_size_t start, end;
        struct resource *p;
        bool sibling_only = false;
 
        BUG_ON(!res);
-
-       start = res->start;
-       end = res->end;
        BUG_ON(start >= end);
 
        if (first_level_children_only)
@@ -377,7 +380,7 @@ static int find_next_iomem_res(struct resource *res, unsigned long desc,
        read_lock(&resource_lock);
 
        for (p = iomem_resource.child; p; p = next_resource(p, sibling_only)) {
-               if ((p->flags & res->flags) != res->flags)
+               if ((p->flags & flags) != flags)
                        continue;
                if ((desc != IORES_DESC_NONE) && (desc != p->desc))
                        continue;
@@ -385,39 +388,38 @@ static int find_next_iomem_res(struct resource *res, unsigned long desc,
                        p = NULL;
                        break;
                }
-               if ((p->end >= start) && (p->start < end))
+               if ((p->end >= start) && (p->start <= end))
                        break;
        }
 
+       if (p) {
+               /* copy data */
+               res->start = max(start, p->start);
+               res->end = min(end, p->end);
+               res->flags = p->flags;
+               res->desc = p->desc;
+       }
+
        read_unlock(&resource_lock);
-       if (!p)
-               return -1;
-       /* copy data */
-       if (res->start < p->start)
-               res->start = p->start;
-       if (res->end > p->end)
-               res->end = p->end;
-       res->flags = p->flags;
-       res->desc = p->desc;
-       return 0;
+       return p ? 0 : -ENODEV;
 }
 
-static int __walk_iomem_res_desc(struct resource *res, unsigned long desc,
-                                bool first_level_children_only,
-                                void *arg,
+static int __walk_iomem_res_desc(resource_size_t start, resource_size_t end,
+                                unsigned long flags, unsigned long desc,
+                                bool first_level_children_only, void *arg,
                                 int (*func)(struct resource *, void *))
 {
-       u64 orig_end = res->end;
+       struct resource res;
        int ret = -1;
 
-       while ((res->start < res->end) &&
-              !find_next_iomem_res(res, desc, first_level_children_only)) {
-               ret = (*func)(res, arg);
+       while (start < end &&
+              !find_next_iomem_res(start, end, flags, desc,
+                                   first_level_children_only, &res)) {
+               ret = (*func)(&res, arg);
                if (ret)
                        break;
 
-               res->start = res->end + 1;
-               res->end = orig_end;
+               start = res.end + 1;
        }
 
        return ret;
@@ -440,13 +442,7 @@ static int __walk_iomem_res_desc(struct resource *res, unsigned long desc,
 int walk_iomem_res_desc(unsigned long desc, unsigned long flags, u64 start,
                u64 end, void *arg, int (*func)(struct resource *, void *))
 {
-       struct resource res;
-
-       res.start = start;
-       res.end = end;
-       res.flags = flags;
-
-       return __walk_iomem_res_desc(&res, desc, false, arg, func);
+       return __walk_iomem_res_desc(start, end, flags, desc, false, arg, func);
 }
 
 /*
@@ -459,13 +455,9 @@ int walk_iomem_res_desc(unsigned long desc, unsigned long flags, u64 start,
 int walk_system_ram_res(u64 start, u64 end, void *arg,
                                int (*func)(struct resource *, void *))
 {
-       struct resource res;
+       unsigned long flags = IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY;
 
-       res.start = start;
-       res.end = end;
-       res.flags = IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY;
-
-       return __walk_iomem_res_desc(&res, IORES_DESC_NONE, true,
+       return __walk_iomem_res_desc(start, end, flags, IORES_DESC_NONE, true,
                                     arg, func);
 }
 
@@ -476,13 +468,9 @@ int walk_system_ram_res(u64 start, u64 end, void *arg,
 int walk_mem_res(u64 start, u64 end, void *arg,
                 int (*func)(struct resource *, void *))
 {
-       struct resource res;
+       unsigned long flags = IORESOURCE_MEM | IORESOURCE_BUSY;
 
-       res.start = start;
-       res.end = end;
-       res.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
-
-       return __walk_iomem_res_desc(&res, IORES_DESC_NONE, true,
+       return __walk_iomem_res_desc(start, end, flags, IORES_DESC_NONE, true,
                                     arg, func);
 }
 
@@ -496,25 +484,25 @@ int walk_mem_res(u64 start, u64 end, void *arg,
 int walk_system_ram_range(unsigned long start_pfn, unsigned long nr_pages,
                void *arg, int (*func)(unsigned long, unsigned long, void *))
 {
+       resource_size_t start, end;
+       unsigned long flags;
        struct resource res;
        unsigned long pfn, end_pfn;
-       u64 orig_end;
        int ret = -1;
 
-       res.start = (u64) start_pfn << PAGE_SHIFT;
-       res.end = ((u64)(start_pfn + nr_pages) << PAGE_SHIFT) - 1;
-       res.flags = IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY;
-       orig_end = res.end;
-       while ((res.start < res.end) &&
-               (find_next_iomem_res(&res, IORES_DESC_NONE, true) >= 0)) {
+       start = (u64) start_pfn << PAGE_SHIFT;
+       end = ((u64)(start_pfn + nr_pages) << PAGE_SHIFT) - 1;
+       flags = IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY;
+       while (start < end &&
+              !find_next_iomem_res(start, end, flags, IORES_DESC_NONE,
+                                   true, &res)) {
                pfn = (res.start + PAGE_SIZE - 1) >> PAGE_SHIFT;
                end_pfn = (res.end + 1) >> PAGE_SHIFT;
                if (end_pfn > pfn)
                        ret = (*func)(pfn, end_pfn - pfn, arg);
                if (ret)
                        break;
-               res.start = res.end + 1;
-               res.end = orig_end;
+               start = res.end + 1;
        }
        return ret;
 }
@@ -651,7 +639,8 @@ static int __find_resource(struct resource *root, struct resource *old,
                        alloc.start = constraint->alignf(constraint->alignf_data, &avail,
                                        size, constraint->align);
                        alloc.end = alloc.start + size - 1;
-                       if (resource_contains(&avail, &alloc)) {
+                       if (alloc.start <= alloc.end &&
+                           resource_contains(&avail, &alloc)) {
                                new->start = alloc.start;
                                new->end = alloc.end;
                                return 0;