Article directory
1. Prototype analysis of memblock_add_range function
In the memblock_add
function , calling the memblock_add_range
function inserts a block of memory;
memblock_add_range
The function prototype is as follows:
int __init_memblock memblock_add_range(struct memblock_type *type,
phys_addr_t base, phys_addr_t size,
int nid, unsigned long flags)
struct memblock_type *type
The parameter representation points to the type of memory block to be added to the new region;
phys_addr_t base
The parameter indicates the starting ;
phys_addr_t size
The parameter indicates the size ;
int nid
The parameter represents the nid of the new area , pointing to the NUMA structure;
unsigned long flags
The parameter indicates the flag bit of the newly added memory block;
2. Source code analysis of memblock_add_range function
Memory block comparison: In this function, the following code is called, its function is to traverse all "memory blocks", and each time it is traversed, the "new memory block" is compared with "this memory block" ;
for_each_memblock_type(type, rgn) {
phys_addr_t rbase = rgn->base;
phys_addr_t 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.
*/
if (rbase > base) {
#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
WARN_ON(nid != memblock_get_region_node(rgn));
#endif
WARN_ON(flags != rgn->flags);
nr_new++;
if (insert)
memblock_insert_region(type, idx++, base,
rbase - base, nid,
flags);
}
/* area below @rend is dealt with, forget about it */
base = min(rend, end);
}
Source code path: linux-4.12\mm\memblock.c #543
Insert memory: If the start address is less than the end address, you need to insert memory, call the memblock_insert_region
function , and complete the memory insertion operation;
/* insert the remaining portion */
if (base < end) {
nr_new++;
if (insert)
memblock_insert_region(type, idx, base, end - base,
nid, flags);
}
Source code path: linux-4.12\mm\memblock.c #571/font>
Memory block list flag: nr_new
The parameter indicates whether there is a new "memory block" to be added to the "memory block list" ;
if (!nr_new)
return 0;
Three, memblock_insert_region function prototype analysis
memblock_insert_region
The function prototype is as follows:
struct memblock_type *type
The parameter pointer points to the memory area
int idx
The parameter indicates the index of the memory area linked list
phys_addr_t base
The parameter indicates the base address of the memory area
phys_addr_t size
The parameter indicates the size of the memory area
int nid
The parameter indicates the memory node id
unsigned long flags
The parameter indicates the memory block flag bit
/**
* memblock_insert_region - insert new memblock region
* @type: memblock type to insert into
* @idx: index for the insertion point
* @base: base address of the new region
* @size: size of the new region
* @nid: node id of the new region
* @flags: flags of the new region
*
* Insert new memblock region [@base,@base+@size) into @type at @idx.
* @type must already have extra room to accommodate the new region.
*/
static void __init_memblock memblock_insert_region(struct memblock_type *type,
int idx, phys_addr_t base,
phys_addr_t size,
int nid, unsigned long flags)
Source code path: linux-4.12\mm\memblock.c #478
Four, memblock_add_range function source code
memblock_add_range
The function is defined in the linux-4.12\mm\memblock.c #511 location of the Linux kernel source code ;
/**
* memblock_add_range - add new memblock region
* @type: memblock type to add new region into
* @base: base address of the new region
* @size: size of the new region
* @nid: nid of the new region
* @flags: flags of the new region
*
* Add new memblock region [@base,@base+@size) into @type. The new region
* is allowed to overlap with existing ones - overlaps don't affect already
* existing regions. @type is guaranteed to be minimal (all neighbouring
* compatible regions are merged) after the addition.
*
* RETURNS:
* 0 on success, -errno on failure.
*/
int __init_memblock memblock_add_range(struct memblock_type *type,
phys_addr_t base, phys_addr_t size,
int nid, unsigned long flags)
{
bool insert = false;
phys_addr_t obase = base;
phys_addr_t end = base + memblock_cap_size(base, &size);
int idx, nr_new;
struct memblock_region *rgn;
if (!size)
return 0;
/* special case for empty array */
if (type->regions[0].size == 0) {
WARN_ON(type->cnt != 1 || type->total_size);
type->regions[0].base = base;
type->regions[0].size = size;
type->regions[0].flags = flags;
memblock_set_region_node(&type->regions[0], nid);
type->total_size = size;
return 0;
}
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.
*/
base = obase;
nr_new = 0;
for_each_memblock_type(type, rgn) {
phys_addr_t rbase = rgn->base;
phys_addr_t 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.
*/
if (rbase > base) {
#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
WARN_ON(nid != memblock_get_region_node(rgn));
#endif
WARN_ON(flags != rgn->flags);
nr_new++;
if (insert)
memblock_insert_region(type, idx++, base,
rbase - base, nid,
flags);
}
/* area below @rend is dealt with, forget about it */
base = min(rend, end);
}
/* insert the remaining portion */
if (base < end) {
nr_new++;
if (insert)
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.
*/
if (!insert) {
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);
return 0;
}
}
Source code path: linux-4.12\mm\memblock.c #511