kexec: enable kexec_crash_size to support two crash kernel regions
[linux-2.6-microblaze.git] / kernel / kexec_core.c
index f7ff7de..e2f2574 100644 (file)
@@ -1091,6 +1091,11 @@ __bpf_kfunc void crash_kexec(struct pt_regs *regs)
        }
 }
 
+static inline resource_size_t crash_resource_size(const struct resource *res)
+{
+       return !res->end ? 0 : resource_size(res);
+}
+
 ssize_t crash_get_memory_size(void)
 {
        ssize_t size = 0;
@@ -1098,8 +1103,8 @@ ssize_t crash_get_memory_size(void)
        if (!kexec_trylock())
                return -EBUSY;
 
-       if (crashk_res.end != crashk_res.start)
-               size = resource_size(&crashk_res);
+       size += crash_resource_size(&crashk_res);
+       size += crash_resource_size(&crashk_low_res);
 
        kexec_unlock();
        return size;
@@ -1136,7 +1141,7 @@ static int __crash_shrink_memory(struct resource *old_res,
 int crash_shrink_memory(unsigned long new_size)
 {
        int ret = 0;
-       unsigned long old_size;
+       unsigned long old_size, low_size;
 
        if (!kexec_trylock())
                return -EBUSY;
@@ -1145,14 +1150,42 @@ int crash_shrink_memory(unsigned long new_size)
                ret = -ENOENT;
                goto unlock;
        }
-       old_size = !crashk_res.end ? 0 : resource_size(&crashk_res);
+
+       low_size = crash_resource_size(&crashk_low_res);
+       old_size = crash_resource_size(&crashk_res) + low_size;
        new_size = roundup(new_size, KEXEC_CRASH_MEM_ALIGN);
        if (new_size >= old_size) {
                ret = (new_size == old_size) ? 0 : -EINVAL;
                goto unlock;
        }
 
-       ret = __crash_shrink_memory(&crashk_res, new_size);
+       /*
+        * (low_size > new_size) implies that low_size is greater than zero.
+        * This also means that if low_size is zero, the else branch is taken.
+        *
+        * If low_size is greater than 0, (low_size > new_size) indicates that
+        * crashk_low_res also needs to be shrunken. Otherwise, only crashk_res
+        * needs to be shrunken.
+        */
+       if (low_size > new_size) {
+               ret = __crash_shrink_memory(&crashk_res, 0);
+               if (ret)
+                       goto unlock;
+
+               ret = __crash_shrink_memory(&crashk_low_res, new_size);
+       } else {
+               ret = __crash_shrink_memory(&crashk_res, new_size - low_size);
+       }
+
+       /* Swap crashk_res and crashk_low_res if needed */
+       if (!crashk_res.end && crashk_low_res.end) {
+               crashk_res.start = crashk_low_res.start;
+               crashk_res.end   = crashk_low_res.end;
+               release_resource(&crashk_low_res);
+               crashk_low_res.start = 0;
+               crashk_low_res.end   = 0;
+               insert_resource(&iomem_resource, &crashk_res);
+       }
 
 unlock:
        kexec_unlock();