mm: cma: Ensure that reservations never cross the low/high mem boundary
[deliverable/linux.git] / mm / cma.c
index 62a5dccc3fb827fe711e544d02d5d0f9b9e75b43..c30a6edee65c30a2907e841f9ab76da7feb62954 100644 (file)
--- a/mm/cma.c
+++ b/mm/cma.c
@@ -253,23 +253,24 @@ int __init cma_declare_contiguous(phys_addr_t base,
                return -EINVAL;
 
        /*
-        * adjust limit to avoid crossing low/high memory boundary for
-        * automatically allocated regions
+        * If allocating at a fixed base the request region must not cross the
+        * low/high memory boundary.
         */
-       if (((limit == 0 || limit > memblock_end) &&
-            (memblock_end - size < highmem_start &&
-             memblock_end > highmem_start)) ||
-           (!fixed && limit > highmem_start && limit - size < highmem_start)) {
-               limit = highmem_start;
-       }
-
-       if (fixed && base < highmem_start && base+size > highmem_start) {
+       if (fixed && base < highmem_start && base + size > highmem_start) {
                ret = -EINVAL;
                pr_err("Region at %08lx defined on low/high memory boundary (%08lx)\n",
                        (unsigned long)base, (unsigned long)highmem_start);
                goto err;
        }
 
+       /*
+        * If the limit is unspecified or above the memblock end, its effective
+        * value will be the memblock end. Set it explicitly to simplify further
+        * checks.
+        */
+       if (limit == 0 || limit > memblock_end)
+               limit = memblock_end;
+
        /* Reserve memory */
        if (fixed) {
                if (memblock_is_region_reserved(base, size) ||
@@ -278,14 +279,30 @@ int __init cma_declare_contiguous(phys_addr_t base,
                        goto err;
                }
        } else {
-               phys_addr_t addr = memblock_alloc_range(size, alignment, base,
-                                                       limit);
+               phys_addr_t addr = 0;
+
+               /*
+                * All pages in the reserved area must come from the same zone.
+                * If the requested region crosses the low/high memory boundary,
+                * try allocating from high memory first and fall back to low
+                * memory in case of failure.
+                */
+               if (base < highmem_start && limit > highmem_start) {
+                       addr = memblock_alloc_range(size, alignment,
+                                                   highmem_start, limit);
+                       limit = highmem_start;
+               }
+
                if (!addr) {
-                       ret = -ENOMEM;
-                       goto err;
-               } else {
-                       base = addr;
+                       addr = memblock_alloc_range(size, alignment, base,
+                                                   limit);
+                       if (!addr) {
+                               ret = -ENOMEM;
+                               goto err;
+                       }
                }
+
+               base = addr;
        }
 
        ret = cma_init_reserved_mem(base, size, order_per_bit, res_cma);
This page took 0.032099 seconds and 5 git commands to generate.