@@ -585,16 +585,16 @@ static int __init_memblock memblock_add_range(struct memblock_type *type,
phys_addr_t base, phys_addr_t size,
int nid, enum memblock_flags flags)
{
- bool insert = false;
phys_addr_t obase = base;
phys_addr_t end = base + memblock_cap_size(base, &size);
- int idx, nr_new, start_rgn = -1, end_rgn;
+ phys_addr_t rbase, rend;
+ int idx, nr_new, start_rgn, end_rgn;
struct memblock_region *rgn;
if (!size)
return 0;
- /* special case for empty array */
+ /* Special case for empty array */
if (type->regions[0].size == 0) {
WARN_ON(type->cnt != 0 || type->total_size);
type->regions[0].base = base;
@@ -606,80 +606,114 @@ static int __init_memblock memblock_add_range(struct memblock_type *type,
return 0;
}
+ /* Delayed assignment, which is not necessary when the array is empty. */
+ start_rgn = -1;
/*
- * The worst case is when new range overlaps all existing regions,
- * then we'll need type->cnt + 1 empty regions in @type. So if
- * type->cnt * 2 + 1 is less than or equal to type->max, we know
- * that there is enough empty regions in @type, and we can insert
- * regions directly.
+ * Originally, `end_rgn` didn't need to be assigned a value,
+ * but due to the use of nested conditional expressions,
+ * the compiler reports a warning that `end_rgn` is uninitialized.
+ * Therefore, it has been given an initial value here
+ * to eliminate the warning.
*/
- if (type->cnt * 2 + 1 <= type->max)
- insert = true;
+ end_rgn = -1;
repeat:
/*
- * The following is executed twice. Once with %false @insert and
- * then with %true. The first counts the number of regions needed
- * to accommodate the new area. The second actually inserts them.
+ * It is assumed that insertion is always possible under normal circumstances.
+ * If memory is insufficient during insertion, the operation will record the need,
+ * allocate memory, and then re-execute the insertion for the remaining portion.
*/
base = obase;
nr_new = 0;
for_each_memblock_type(idx, type, rgn) {
- phys_addr_t rbase = rgn->base;
- phys_addr_t rend = rbase + rgn->size;
+ rbase = rgn->base;
+ rend = rbase + rgn->size;
if (rbase >= end)
break;
if (rend <= base)
continue;
+
/*
- * @rgn overlaps. If it separates the lower part of new
- * area, insert that portion.
+ * @rgn overlaps. If it separates the lower part of new area, insert that portion.
*/
if (rbase > base) {
#ifdef CONFIG_NUMA
WARN_ON(nid != memblock_get_region_node(rgn));
#endif
WARN_ON(flags != rgn->flags);
- nr_new++;
- if (insert) {
+ /*
+ * If memory is insufficient, the space required will be recorded.
+ * If memory is sufficient, the insertion will proceed.
+ */
+ if (type->cnt >= type->max) {
+ /*
+ * Record obase as the address where the
+ * overlapping part has not been resolved,
+ * so that when repeat restarts,
+ * redundant operations of resolving the
+ * overlapping addresses are avoided.
+ */
+ if (nr_new == 0)
+ obase = base;
+ nr_new++;
+ } else {
if (start_rgn == -1)
start_rgn = idx;
end_rgn = idx + 1;
- memblock_insert_region(type, idx++, base,
- rbase - base, nid,
- flags);
+ memblock_insert_region(type, idx++, base, rbase - base, nid, flags);
}
}
- /* area below @rend is dealt with, forget about it */
+ /* Area below @rend is dealt with, forget about it */
base = min(rend, end);
}
- /* insert the remaining portion */
+ /* Insert the remaining portion */
if (base < end) {
- nr_new++;
- if (insert) {
+ /*
+ * Similarly, after handling the overlapping part,
+ * it is still possible that memory is
+ * insufficient. In that case, the space will be recorded once again.
+ */
+ if (type->cnt >= type->max) {
+ /*
+ * The address of obase needs to be recorded here as well. The purpose is to
+ * handle the situation where,
+ * after resolving the overlap, there is still a remaining space to
+ * insert but memory is insufficient (i.e.,
+ * no memory shortage occurred while resolving the overlap).
+ * This means that space for
+ * N (overlapping parts) + 1 (non-overlapping part) is required.
+ * If obase is not recorded, after memory expansion,
+ * base might revert to the original address to be
+ * inserted (which could be overlapping).
+ * This could lead to for_each_memblock_type attempting
+ * to resolve the overlap again, causing multiple unnecessary iterations,
+ * even if it's just a simple check.
+ */
+ if (nr_new == 0)
+ obase = base;
+ nr_new++;
+ } else {
if (start_rgn == -1)
start_rgn = idx;
end_rgn = idx + 1;
- memblock_insert_region(type, idx, base, end - base,
- nid, flags);
+ memblock_insert_region(type, idx, base, end - base, nid, flags);
}
}
- if (!nr_new)
- return 0;
-
/*
- * If this was the first round, resize array and repeat for actual
- * insertions; otherwise, merge and return.
+ * Finally, check if memory insufficiency occurred during insertion.
+ * If so, the memory will be expanded to an appropriate size,
+ * and the remaining portion will be inserted again.
+ * If not, it means memory is sufficient, and the regions will be merged directly.
*/
- if (!insert) {
- while (type->cnt + nr_new > type->max)
+ if (nr_new > 0) {
+ while (type->cnt + nr_new > type->max) {
if (memblock_double_array(type, obase, size) < 0)
return -ENOMEM;
- insert = true;
+ }
goto repeat;
} else {
memblock_merge_regions(type, start_rgn, end_rgn);