Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

of: reserved_mem: Restructure how the reserved memory regions are processed

Reserved memory regions defined in the devicetree can be broken up into
two groups:
i) Statically-placed reserved memory regions
i.e. regions defined with a static start address and size using the
"reg" property.
ii) Dynamically-placed reserved memory regions.
i.e. regions defined by specifying an address range where they can be
placed in memory using the "alloc_ranges" and "size" properties.

These regions are processed and set aside at boot time.
This is done in two stages as seen below:

Stage 1:
At this stage, fdt_scan_reserved_mem() scans through the child nodes of
the reserved_memory node using the flattened devicetree and does the
following:

1) If the node represents a statically-placed reserved memory region,
i.e. if it is defined using the "reg" property:
- Call memblock_reserve() or memblock_mark_nomap() as needed.
- Add the information for that region into the reserved_mem array
using fdt_reserved_mem_save_node().
i.e. fdt_reserved_mem_save_node(node, name, base, size).

2) If the node represents a dynamically-placed reserved memory region,
i.e. if it is defined using "alloc-ranges" and "size" properties:
- Add the information for that region to the reserved_mem array with
the starting address and size set to 0.
i.e. fdt_reserved_mem_save_node(node, name, 0, 0).
Note: This region is saved to the array with a starting address of 0
because a starting address is not yet allocated for it.

Stage 2:
After iterating through all the reserved memory nodes and storing their
relevant information in the reserved_mem array,fdt_init_reserved_mem() is
called and does the following:

1) For statically-placed reserved memory regions:
- Call the region specific init function using
__reserved_mem_init_node().
2) For dynamically-placed reserved memory regions:
- Call __reserved_mem_alloc_size() which is used to allocate memory
for each of these regions, and mark them as nomap if they have the
nomap property specified in the DT.
- Call the region specific init function.

The current size of the resvered_mem array is 64 as is defined by
MAX_RESERVED_REGIONS. This means that there is a limitation of 64 for
how many reserved memory regions can be specified on a system.
As systems continue to grow more and more complex, the number of
reserved memory regions needed are also growing and are starting to hit
this 64 count limit, hence the need to make the reserved_mem array
dynamically sized (i.e. dynamically allocating memory for the
reserved_mem array using membock_alloc_*).

On architectures such as arm64, memory allocated using memblock is
writable only after the page tables have been setup. This means that if
the reserved_mem array is going to be dynamically allocated, it needs to
happen after the page tables have been setup, not before.

Since the reserved memory regions are currently being processed and
added to the array before the page tables are setup, there is a need to
change the order in which some of the processing is done to allow for
the reserved_mem array to be dynamically sized.

It is possible to process the statically-placed reserved memory regions
without needing to store them in the reserved_mem array until after the
page tables have been setup because all the information stored in the
array is readily available in the devicetree and can be referenced at
any time.
Dynamically-placed reserved memory regions on the other hand get
assigned a start address only at runtime, and hence need a place to be
stored once they are allocated since there is no other referrence to the
start address for these regions.

Hence this patch changes the processing order of the reserved memory
regions in the following ways:

Step 1:
fdt_scan_reserved_mem() scans through the child nodes of
the reserved_memory node using the flattened devicetree and does the
following:

1) If the node represents a statically-placed reserved memory region,
i.e. if it is defined using the "reg" property:
- Call memblock_reserve() or memblock_mark_nomap() as needed.

2) If the node represents a dynamically-placed reserved memory region,
i.e. if it is defined using "alloc-ranges" and "size" properties:
- Call __reserved_mem_alloc_size() which will:
i) Allocate memory for the reserved region and call
memblock_mark_nomap() as needed.
ii) Call the region specific initialization function using
fdt_init_reserved_mem_node().
iii) Save the region information in the reserved_mem array using
fdt_reserved_mem_save_node().

Step 2:
1) This stage of the reserved memory processing is now only used to add
the statically-placed reserved memory regions into the reserved_mem
array using fdt_scan_reserved_mem_reg_nodes(), as well as call their
region specific initialization functions.

2) This step has also been moved to be after the page tables are
setup. Moving this will allow us to replace the reserved_mem
array with a dynamically sized array before storing the rest of
these regions.

Signed-off-by: Oreoluwa Babatunde <quic_obabatun@quicinc.com>
Link: https://lore.kernel.org/r/20241008220624.551309-2-quic_obabatun@quicinc.com
Signed-off-by: Rob Herring (Arm) <robh@kernel.org>

authored by

Oreoluwa Babatunde and committed by
Rob Herring (Arm)
8a6e02d0 d79616b0

+121 -53
+3 -2
drivers/of/fdt.c
··· 511 511 break; 512 512 memblock_reserve(base, size); 513 513 } 514 - 515 - fdt_init_reserved_mem(); 516 514 } 517 515 518 516 /** ··· 1209 1211 void __init unflatten_device_tree(void) 1210 1212 { 1211 1213 void *fdt = initial_boot_params; 1214 + 1215 + /* Save the statically-placed regions in the reserved_mem array */ 1216 + fdt_scan_reserved_mem_reg_nodes(); 1212 1217 1213 1218 /* Don't use the bootloader provided DTB if ACPI is enabled */ 1214 1219 if (!acpi_disabled)
+2 -1
drivers/of/of_private.h
··· 9 9 */ 10 10 11 11 #define FDT_ALIGN_SIZE 8 12 + #define MAX_RESERVED_REGIONS 64 12 13 13 14 /** 14 15 * struct alias_prop - Alias property in 'aliases' node ··· 181 180 #endif 182 181 183 182 int fdt_scan_reserved_mem(void); 184 - void fdt_init_reserved_mem(void); 183 + void __init fdt_scan_reserved_mem_reg_nodes(void); 185 184 186 185 bool of_fdt_device_is_available(const void *blob, unsigned long node); 187 186
+116 -50
drivers/of/of_reserved_mem.c
··· 27 27 28 28 #include "of_private.h" 29 29 30 - #define MAX_RESERVED_REGIONS 64 31 30 static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS]; 32 31 static int reserved_mem_count; 33 32 ··· 55 56 return err; 56 57 } 57 58 59 + static void __init fdt_init_reserved_mem_node(struct reserved_mem *rmem); 58 60 /* 59 61 * fdt_reserved_mem_save_node() - save fdt node for second pass initialization 60 62 */ ··· 73 73 rmem->name = uname; 74 74 rmem->base = base; 75 75 rmem->size = size; 76 + 77 + /* Call the region specific initialization function */ 78 + fdt_init_reserved_mem_node(rmem); 76 79 77 80 reserved_mem_count++; 78 81 return; ··· 109 106 phys_addr_t base, size; 110 107 int len; 111 108 const __be32 *prop; 112 - int first = 1; 113 109 bool nomap; 114 110 115 111 prop = of_get_flat_dt_prop(node, "reg", &len); ··· 136 134 uname, &base, (unsigned long)(size / SZ_1M)); 137 135 138 136 len -= t_len; 139 - if (first) { 140 - fdt_reserved_mem_save_node(node, uname, base, size); 141 - first = 0; 142 - } 143 137 } 144 138 return 0; 145 139 } ··· 163 165 return 0; 164 166 } 165 167 168 + static void __init __rmem_check_for_overlap(void); 169 + 170 + /** 171 + * fdt_scan_reserved_mem_reg_nodes() - Store info for the "reg" defined 172 + * reserved memory regions. 173 + * 174 + * This function is used to scan through the DT and store the 175 + * information for the reserved memory regions that are defined using 176 + * the "reg" property. The region node number, name, base address, and 177 + * size are all stored in the reserved_mem array by calling the 178 + * fdt_reserved_mem_save_node() function. 179 + */ 180 + void __init fdt_scan_reserved_mem_reg_nodes(void) 181 + { 182 + int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32); 183 + const void *fdt = initial_boot_params; 184 + phys_addr_t base, size; 185 + const __be32 *prop; 186 + int node, child; 187 + int len; 188 + 189 + if (!fdt) 190 + return; 191 + 192 + node = fdt_path_offset(fdt, "/reserved-memory"); 193 + if (node < 0) { 194 + pr_info("Reserved memory: No reserved-memory node in the DT\n"); 195 + return; 196 + } 197 + 198 + if (__reserved_mem_check_root(node)) { 199 + pr_err("Reserved memory: unsupported node format, ignoring\n"); 200 + return; 201 + } 202 + 203 + fdt_for_each_subnode(child, fdt, node) { 204 + const char *uname; 205 + 206 + prop = of_get_flat_dt_prop(child, "reg", &len); 207 + if (!prop) 208 + continue; 209 + if (!of_fdt_device_is_available(fdt, child)) 210 + continue; 211 + 212 + uname = fdt_get_name(fdt, child, NULL); 213 + if (len && len % t_len != 0) { 214 + pr_err("Reserved memory: invalid reg property in '%s', skipping node.\n", 215 + uname); 216 + continue; 217 + } 218 + base = dt_mem_next_cell(dt_root_addr_cells, &prop); 219 + size = dt_mem_next_cell(dt_root_size_cells, &prop); 220 + 221 + if (size) 222 + fdt_reserved_mem_save_node(child, uname, base, size); 223 + } 224 + 225 + /* check for overlapping reserved regions */ 226 + __rmem_check_for_overlap(); 227 + } 228 + 229 + static int __init __reserved_mem_alloc_size(unsigned long node, const char *uname); 230 + 166 231 /* 167 232 * fdt_scan_reserved_mem() - scan a single FDT node for reserved memory 168 233 */ 169 234 int __init fdt_scan_reserved_mem(void) 170 235 { 171 236 int node, child; 237 + int dynamic_nodes_cnt = 0; 238 + int dynamic_nodes[MAX_RESERVED_REGIONS]; 172 239 const void *fdt = initial_boot_params; 173 240 174 241 node = fdt_path_offset(fdt, "/reserved-memory"); ··· 255 192 uname = fdt_get_name(fdt, child, NULL); 256 193 257 194 err = __reserved_mem_reserve_reg(child, uname); 258 - if (err == -ENOENT && of_get_flat_dt_prop(child, "size", NULL)) 259 - fdt_reserved_mem_save_node(child, uname, 0, 0); 195 + /* 196 + * Save the nodes for the dynamically-placed regions 197 + * into an array which will be used for allocation right 198 + * after all the statically-placed regions are reserved 199 + * or marked as no-map. This is done to avoid dynamically 200 + * allocating from one of the statically-placed regions. 201 + */ 202 + if (err == -ENOENT && of_get_flat_dt_prop(child, "size", NULL)) { 203 + dynamic_nodes[dynamic_nodes_cnt] = child; 204 + dynamic_nodes_cnt++; 205 + } 206 + } 207 + for (int i = 0; i < dynamic_nodes_cnt; i++) { 208 + const char *uname; 209 + 210 + child = dynamic_nodes[i]; 211 + uname = fdt_get_name(fdt, child, NULL); 212 + __reserved_mem_alloc_size(child, uname); 260 213 } 261 214 return 0; 262 215 } ··· 332 253 * __reserved_mem_alloc_size() - allocate reserved memory described by 333 254 * 'size', 'alignment' and 'alloc-ranges' properties. 334 255 */ 335 - static int __init __reserved_mem_alloc_size(unsigned long node, 336 - const char *uname, phys_addr_t *res_base, phys_addr_t *res_size) 256 + static int __init __reserved_mem_alloc_size(unsigned long node, const char *uname) 337 257 { 338 258 int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32); 339 259 phys_addr_t start = 0, end = 0; ··· 412 334 return -ENOMEM; 413 335 } 414 336 415 - *res_base = base; 416 - *res_size = size; 417 - 337 + /* Save region in the reserved_mem array */ 338 + fdt_reserved_mem_save_node(node, uname, base, size); 418 339 return 0; 419 340 } 420 341 ··· 502 425 } 503 426 504 427 /** 505 - * fdt_init_reserved_mem() - allocate and init all saved reserved memory regions 428 + * fdt_init_reserved_mem_node() - Initialize a reserved memory region 429 + * @rmem: reserved_mem struct of the memory region to be initialized. 430 + * 431 + * This function is used to call the region specific initialization 432 + * function for a reserved memory region. 506 433 */ 507 - void __init fdt_init_reserved_mem(void) 434 + static void __init fdt_init_reserved_mem_node(struct reserved_mem *rmem) 508 435 { 509 - int i; 436 + unsigned long node = rmem->fdt_node; 437 + int err = 0; 438 + bool nomap; 510 439 511 - /* check for overlapping reserved regions */ 512 - __rmem_check_for_overlap(); 440 + nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; 513 441 514 - for (i = 0; i < reserved_mem_count; i++) { 515 - struct reserved_mem *rmem = &reserved_mem[i]; 516 - unsigned long node = rmem->fdt_node; 517 - int err = 0; 518 - bool nomap; 442 + err = __reserved_mem_init_node(rmem); 443 + if (err != 0 && err != -ENOENT) { 444 + pr_info("node %s compatible matching fail\n", rmem->name); 445 + if (nomap) 446 + memblock_clear_nomap(rmem->base, rmem->size); 447 + else 448 + memblock_phys_free(rmem->base, rmem->size); 449 + } else { 450 + phys_addr_t end = rmem->base + rmem->size - 1; 451 + bool reusable = 452 + (of_get_flat_dt_prop(node, "reusable", NULL)) != NULL; 519 453 520 - nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; 521 - 522 - if (rmem->size == 0) 523 - err = __reserved_mem_alloc_size(node, rmem->name, 524 - &rmem->base, &rmem->size); 525 - if (err == 0) { 526 - err = __reserved_mem_init_node(rmem); 527 - if (err != 0 && err != -ENOENT) { 528 - pr_info("node %s compatible matching fail\n", 529 - rmem->name); 530 - if (nomap) 531 - memblock_clear_nomap(rmem->base, rmem->size); 532 - else 533 - memblock_phys_free(rmem->base, 534 - rmem->size); 535 - } else { 536 - phys_addr_t end = rmem->base + rmem->size - 1; 537 - bool reusable = 538 - (of_get_flat_dt_prop(node, "reusable", NULL)) != NULL; 539 - 540 - pr_info("%pa..%pa (%lu KiB) %s %s %s\n", 541 - &rmem->base, &end, (unsigned long)(rmem->size / SZ_1K), 542 - nomap ? "nomap" : "map", 543 - reusable ? "reusable" : "non-reusable", 544 - rmem->name ? rmem->name : "unknown"); 545 - } 546 - } 454 + pr_info("%pa..%pa (%lu KiB) %s %s %s\n", 455 + &rmem->base, &end, (unsigned long)(rmem->size / SZ_1K), 456 + nomap ? "nomap" : "map", 457 + reusable ? "reusable" : "non-reusable", 458 + rmem->name ? rmem->name : "unknown"); 547 459 } 548 460 } 549 461