SLUB allocator 4 for Linux memory management [slub page size calculation method]

Posted by adamdyer on Wed, 29 Dec 2021 16:56:23 +0100

SLUB allocator 4 for Linux memory management [slub page size calculation method]

Today, I tracked the calculation methods of object and page order in SLUB in detail and sorted them out to avoid forgetting later, mainly including:

  1. The calculation method of page order is to apply for kmem_ How many pages should each slab apply for when caching?
  2. order_ Calculation and API description of objects

1. Calculation description of order

Order is the parameter passed in when applying for page. 2^order is the number of pages;

During calculation, the number of objects that can be accommodated in this slab and the remaining (wasted) space after removing objects need to be considered. First, look at the overall calculation logic diagram:

  1. Judgment on the minimum number of objects that can be accommodated:

    • Confirm according to the number of CPUs of the machine: min_objects = 4 * (fls(nr_cpu_ids) + 1)

      fls is the highest position of the parameter, that is, fls(100B) = 3 for 4 cores, and min is obtained_ Objects is 16

    • According to size and configured Max_ Order confirmation: ((page_size < < max_order) - reserved) / size

      PAGE_ According to the machine configuration, the size is 4K, max_ The order quantity is configured as 3

      That is, if the object size is 5K, the Min obtained here_ Objects is 6

    size(Bytes)max_orderpage_sizemax_objectsmin_objects(fls)min_result
    128 Bytes34K2561616
    256 Bytes34K1281616
    512 Bytes34K641616
    1024 Bytes34K321616
    2048 Bytes34K161616
    4096 Bytes34K8168
    8192 Bytes34K4164
    4*1024*2^3/size4*(fls(n)+1)min(max_obj, min_obj)

    Relevant parts:

    	min_objects = slub_min_objects; //According to the print value, it is 0 here, that is, at least 0 object s
    	if (!min_objects)
    		min_objects = 4 * (fls(nr_cpu_ids) + 1);//fls is the highest position, that is, fls(100B)=3 for 4 cores, and min is obtained_ Objects is 16;
    	//This is used to calculate the maximum number of object s of size that can be allocated if we apply for the maximum number of order s?
        max_objects = order_objects(slub_max_order, size, reserved);//slab_max_order = 3
    	min_objects = min(min_objects, max_objects);//Take the smaller one
        //Obviously, 4K * 2 ^ 3 = 32K at most, that is, object s less than 2K basically use the value of 16;
    
  2. The amount of wasted space, i.e. according to min_objects * size how many page s will be available after getting the order application:

    Calculation method: (page_size < < order - reserved)% size

    size(Bytes)max_ordermin_resultmin_orderrem
    128 Bytes31600
    256 Bytes31600
    512 Bytes31610
    1024 Bytes31620
    2048 Bytes31630
    4096 Bytes3830
    8192 Bytes3430
    min(max_obj, min_obj)size*min_result -1

    Since the above size examples are all integer multiples of 2, the calculated rmem is 0, and the order uses min_ Just order

    Relevant parts:

    while (min_objects > 1) {//
        fraction = 16;//Influence factor
        while (fraction >= 4) {
            //Combining size and minobject to calculate the appropriate order is actually the ability to apply for min_ There are so many objects, and then the waste can be within 1/fraction of the whole
            order = slab_order(size, min_objects, slub_max_order, fraction, reserved);
            if (order <= slub_max_order)
                return order;
            fraction /= 2;//If you are not satisfied, halve the fraction, that is, the waste of free mem can reach twice the original
        }
        min_objects--;
    }
    
    static inline int slab_order(int size, int min_objects, int max_order, int fract_leftover, int reserved)
    {
    	int order;
    	int rem;
    	int min_order = slub_min_order;//0
    	...
    	for (order = max(min_order, get_order(min_objects * size + reserved)); order <= max_order; order++) {
    		unsigned long slab_size = PAGE_SIZE << order;//Suppose you apply for an order page at a time
    		rem = (slab_size - reserved) % size;//How much mem is wasted
    		if (rem <= slab_size / fract_leftover)//If the waste does not exceed 1 / 16 of the application, it is considered OK;
    			break;
    	}
    	return order;
    }
    
  3. Obtain the appropriate order value according to the results of the first two steps

    1. Meet the minimum number of objects;
    2. Meet the requirement that the wasted mem is less than 1/fraction of the overall mem;
    3. If the above two requirements cannot be met, proceed as follows:
      1. Increase the number of order s
      2. Decrease the value of fraction
      3. Decrease min_ Value of object

Relevant code s and notes in this part are as follows:

static inline int calculate_order(int size, int reserved)
{
	int order;
	int min_objects;
	int fraction;
	int max_objects;

	min_objects = slub_min_objects; //According to the print value, it is 0 here, that is, at least 0 object s
	if (!min_objects)
		min_objects = 4 * (fls(nr_cpu_ids) + 1);//fls is the highest position, that is, fls(100B)=3 for 4 cores, and min is obtained_ Objects is 16;
	//This is used to calculate the maximum number of object s of size that can be allocated if we apply for the maximum number of order s?
    max_objects = order_objects(slub_max_order, size, reserved);//slab_max_order = 3
	min_objects = min(min_objects, max_objects);//Take the smaller one
    //Obviously, 4K * 2 ^ 3 = 32K at most, that is, object s less than 2K basically use the value of 16;
	while (min_objects > 1) {//
		fraction = 16;//Influence factor
		while (fraction >= 4) {
            //Combining size and minobject to calculate the appropriate order is actually the ability to apply for min_ There are so many objects, and then the waste can be within 1/fraction of the whole
			order = slab_order(size, min_objects, slub_max_order, fraction, reserved);
			if (order <= slub_max_order)
				return order;
			fraction /= 2;//If you are not satisfied, halve the fraction, that is, the waste of free mem can reach twice the original
		}
		min_objects--;
	}

	order = slab_order(size, 1, slub_max_order, 1, reserved);//Apply for one
	if (order <= slub_max_order)
		return order;

	order = slab_order(size, 1, MAX_ORDER, 1, reserved);//Expand the number of order s
	if (order < MAX_ORDER)
		return order;
	return -ENOSYS;
}

static inline int order_objects(int order, unsigned long size, int reserved)
{
	return ((PAGE_SIZE << order) - reserved) / size;//A simple division calculation
}

static inline int slab_order(int size, int min_objects,
				int max_order, int fract_leftover, int reserved)
{
	int order;
	int rem;
	int min_order = slub_min_order;//0

	if (order_objects(min_order, size, reserved) > MAX_OBJS_PER_PAGE)//Determine whether to apply for content < = 1bit
		return get_order(size * MAX_OBJS_PER_PAGE) - 1;//Assuming 1 bit, the maximum order is actually returned here, that is, 3

	for (order = max(min_order, get_order(min_objects * size + reserved));
			order <= max_order; order++) {
		unsigned long slab_size = PAGE_SIZE << order;//Suppose you apply for an order page at a time
		rem = (slab_size - reserved) % size;//How much mem is wasted
		if (rem <= slab_size / fract_leftover)//If the waste does not exceed 1 / 16 of the application, it is considered OK;
			break;
	}

	return order;
}

static inline __attribute_const__
int __get_order(unsigned long size)
{
	int order;

	size--;
	size >>= PAGE_SHIFT;//12, shift right 12bit
	order = fls64(size);
	return order;
}

2. order_ API for object calculation and construction

order_ The structure of object is kmem_ The structure used to save order and objects count in cache is as follows:

Interfaceexplain
kmem_cache_order_objectsdefinition
oo_makeConstructor
oo_orderGet order, i.e. high 16bits
oo_objectsGet objects, i.e. low 16bits
order_objectsCalculate objects
  1. definition

    struct kmem_cache_order_objects {
    	unsigned long x;
    };
    
  2. Constructor

    static inline struct kmem_cache_order_objects oo_make(int order,
    		unsigned long size, int reserved)
    {
    	struct kmem_cache_order_objects x = {
    		(order << OO_SHIFT) + order_objects(order, size, reserved)
    	};
    
    	return x;
    }
    #define OO_SHIFT	16
    #define OO_MASK		((1 << OO_SHIFT) - 1)
    

    Save the order in high 16bits and the number of objects in low 16bits

  3. Get order, i.e. high 16bits

    static inline int oo_order(struct kmem_cache_order_objects x)
    {
    	return x.x >> OO_SHIFT;
    }
    
  4. Get objects, i.e. low 16bits

    static inline int oo_objects(struct kmem_cache_order_objects x)
    {
    	return x.x & OO_MASK;
    }
    
  5. Calculate objects

    static inline int order_objects(int order, unsigned long size, int reserved)
    {
    	return ((PAGE_SIZE << order) - reserved) / size;
    }
    

Topics: Linux memory management