[Linux kernel memory management] memblock allocator programming interface ② (memblock_add_range function analysis | memblock_insert_region function analysis)





1. Prototype analysis of memblock_add_range function



In the memblock_addfunction , calling the memblock_add_rangefunction inserts a block of memory;

memblock_add_rangeThe 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 *typeThe parameter representation points to the type of memory block to be added to the new region;

phys_addr_t baseThe parameter indicates the starting ;

phys_addr_t sizeThe parameter indicates the size ;

int nidThe parameter represents the nid of the new area , pointing to the NUMA structure;

unsigned long flagsThe 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_regionfunction , 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_regionThe function prototype is as follows:

struct memblock_type *typeThe parameter pointer points to the memory area

int idxThe parameter indicates the index of the memory area linked list

phys_addr_t baseThe parameter indicates the base address of the memory area

phys_addr_t sizeThe parameter indicates the size of the memory area

int nidThe parameter indicates the memory node id

unsigned long flagsThe 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_rangeThe function is defined in the linux-4.12\mm\memblock.c #511 location of the Linux kernel source code ;

insert image description here

/**
 * 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

Guess you like

Origin blog.csdn.net/han1202012/article/details/124292398