Skip to content
percpu.c 37.1 KiB
Newer Older
	spin_lock_irqsave(&pcpu_lock, flags);

	chunk = pcpu_chunk_addr_search(addr);
	off = addr - chunk->vm->addr;

	pcpu_free_area(chunk, off);

	/* if there are more than one fully free chunks, wake up grim reaper */
	if (chunk->free_size == pcpu_unit_size) {
		struct pcpu_chunk *pos;

		list_for_each_entry(pos, &pcpu_slot[pcpu_nr_slots - 1], list)
			if (pos != chunk) {
				schedule_work(&pcpu_reclaim_work);
	spin_unlock_irqrestore(&pcpu_lock, flags);
}
EXPORT_SYMBOL_GPL(free_percpu);

/**
 * pcpu_setup_first_chunk - initialize the first percpu chunk
 * @get_page_fn: callback to fetch page pointer
 * @static_size: the size of static percpu area in bytes
 * @reserved_size: the size of reserved percpu area in bytes
 * @dyn_size: free size for dynamic allocation in bytes, -1 for auto
 * @unit_size: unit size in bytes, must be multiple of PAGE_SIZE, -1 for auto
 * @base_addr: mapped address, NULL for auto
 * @populate_pte_fn: callback to allocate pagetable, NULL if unnecessary
 *
 * Initialize the first percpu chunk which contains the kernel static
 * perpcu area.  This function is to be called from arch percpu area
 * setup path.  The first two parameters are mandatory.  The rest are
 * optional.
 *
 * @get_page_fn() should return pointer to percpu page given cpu
 * number and page number.  It should at least return enough pages to
 * cover the static area.  The returned pages for static area should
 * have been initialized with valid data.  If @unit_size is specified,
 * it can also return pages after the static area.  NULL return
 * indicates end of pages for the cpu.  Note that @get_page_fn() must
 * return the same number of pages for all cpus.
 *
 * @reserved_size, if non-zero, specifies the amount of bytes to
 * reserve after the static area in the first chunk.  This reserves
 * the first chunk such that it's available only through reserved
 * percpu allocation.  This is primarily used to serve module percpu
 * static areas on architectures where the addressing model has
 * limited offset range for symbol relocations to guarantee module
 * percpu symbols fall inside the relocatable range.
 *
 * @dyn_size, if non-negative, determines the number of bytes
 * available for dynamic allocation in the first chunk.  Specifying
 * non-negative value makes percpu leave alone the area beyond
 * @static_size + @reserved_size + @dyn_size.
 *
 * @unit_size, if non-negative, specifies unit size and must be
 * aligned to PAGE_SIZE and equal to or larger than @static_size +
 * @reserved_size + if non-negative, @dyn_size.
 *
 * Non-null @base_addr means that the caller already allocated virtual
 * region for the first chunk and mapped it.  percpu must not mess
 * with the chunk.  Note that @base_addr with 0 @unit_size or non-NULL
 * @populate_pte_fn doesn't make any sense.
 *
 * @populate_pte_fn is used to populate the pagetable.  NULL means the
 * caller already populated the pagetable.
 * If the first chunk ends up with both reserved and dynamic areas, it
 * is served by two chunks - one to serve the core static and reserved
 * areas and the other for the dynamic area.  They share the same vm
 * and page map but uses different area allocation map to stay away
 * from each other.  The latter chunk is circulated in the chunk slots
 * and available for dynamic allocation like any other chunks.
 *
 * RETURNS:
 * The determined pcpu_unit_size which can be used to initialize
 * percpu access.
 */
size_t __init pcpu_setup_first_chunk(pcpu_get_page_fn_t get_page_fn,
				     size_t static_size, size_t reserved_size,
				     ssize_t dyn_size, ssize_t unit_size,
				     pcpu_populate_pte_fn_t populate_pte_fn)
	static struct vm_struct first_vm;
	size_t size_sum = static_size + reserved_size +
			  (dyn_size >= 0 ? dyn_size : 0);
	struct pcpu_chunk *schunk, *dchunk = NULL;
	unsigned int cpu;
	BUILD_BUG_ON(ARRAY_SIZE(smap) >= PCPU_DFL_MAP_ALLOC ||
		     ARRAY_SIZE(dmap) >= PCPU_DFL_MAP_ALLOC);
		BUG_ON(unit_size < size_sum);
		BUG_ON(unit_size & ~PAGE_MASK);
		BUG_ON(unit_size < PCPU_MIN_UNIT_SIZE);
	} else
	BUG_ON(base_addr && populate_pte_fn);
		pcpu_unit_pages = unit_size >> PAGE_SHIFT;
	else
		pcpu_unit_pages = max_t(int, PCPU_MIN_UNIT_SIZE >> PAGE_SHIFT,
	pcpu_unit_size = pcpu_unit_pages << PAGE_SHIFT;
	pcpu_chunk_size = num_possible_cpus() * pcpu_unit_size;
	pcpu_chunk_struct_size = sizeof(struct pcpu_chunk)
		+ num_possible_cpus() * pcpu_unit_pages * sizeof(struct page *);
		dyn_size = pcpu_unit_size - static_size - reserved_size;
	/*
	 * Allocate chunk slots.  The additional last slot is for
	 * empty chunks.
	 */
	pcpu_nr_slots = __pcpu_size_to_slot(pcpu_unit_size) + 2;
	pcpu_slot = alloc_bootmem(pcpu_nr_slots * sizeof(pcpu_slot[0]));
	for (i = 0; i < pcpu_nr_slots; i++)
		INIT_LIST_HEAD(&pcpu_slot[i]);

	/*
	 * Initialize static chunk.  If reserved_size is zero, the
	 * static chunk covers static area + dynamic allocation area
	 * in the first chunk.  If reserved_size is not zero, it
	 * covers static area + reserved area (mostly used for module
	 * static percpu allocation).
	 */
	schunk = alloc_bootmem(pcpu_chunk_struct_size);
	INIT_LIST_HEAD(&schunk->list);
	schunk->vm = &first_vm;
	schunk->map = smap;
	schunk->map_alloc = ARRAY_SIZE(smap);
	schunk->page = schunk->page_ar;

	if (reserved_size) {
		schunk->free_size = reserved_size;
		pcpu_reserved_chunk = schunk;	/* not for dynamic alloc */
	} else {
		schunk->free_size = dyn_size;
		dyn_size = 0;			/* dynamic area covered */
	}
	schunk->contig_hint = schunk->free_size;
	schunk->map[schunk->map_used++] = -static_size;
	if (schunk->free_size)
		schunk->map[schunk->map_used++] = schunk->free_size;

	pcpu_reserved_chunk_limit = static_size + schunk->free_size;

	/* init dynamic chunk if necessary */
	if (dyn_size) {
		dchunk = alloc_bootmem(sizeof(struct pcpu_chunk));
		INIT_LIST_HEAD(&dchunk->list);
		dchunk->vm = &first_vm;
		dchunk->map = dmap;
		dchunk->map_alloc = ARRAY_SIZE(dmap);
		dchunk->page = schunk->page_ar;	/* share page map with schunk */

		dchunk->contig_hint = dchunk->free_size = dyn_size;
		dchunk->map[dchunk->map_used++] = -pcpu_reserved_chunk_limit;
		dchunk->map[dchunk->map_used++] = dchunk->free_size;
	}

	first_vm.flags = VM_ALLOC;
	first_vm.size = pcpu_chunk_size;
		vm_area_register_early(&first_vm, PAGE_SIZE);
	else {
		/*
		 * Pages already mapped.  No need to remap into
		 * vmalloc area.  In this case the first chunks can't
		 * be mapped or unmapped by percpu and are marked
		first_vm.addr = base_addr;
		schunk->immutable = true;
	for_each_possible_cpu(cpu) {
		for (i = 0; i < pcpu_unit_pages; i++) {
			struct page *page = get_page_fn(cpu, i);

			if (!page)
				break;
			*pcpu_chunk_pagep(schunk, cpu, i) = page;
		BUG_ON(i < PFN_UP(static_size));

		if (nr_pages < 0)
			nr_pages = i;
		else
			BUG_ON(nr_pages != i);
	/* map them */
	if (populate_pte_fn) {
		for_each_possible_cpu(cpu)
			for (i = 0; i < nr_pages; i++)
				populate_pte_fn(pcpu_chunk_addr(schunk,
		err = pcpu_map(schunk, 0, nr_pages);
		if (err)
			panic("failed to setup static percpu area, err=%d\n",
			      err);
	}
	/* link the first chunk in */
	if (!dchunk) {
		pcpu_chunk_relocate(schunk, -1);
		pcpu_chunk_addr_insert(schunk);
	} else {
		pcpu_chunk_relocate(dchunk, -1);
		pcpu_chunk_addr_insert(dchunk);
	}
	pcpu_base_addr = (void *)pcpu_chunk_addr(schunk, 0, 0);
	return pcpu_unit_size;
}

/*
 * Embedding first chunk setup helper.
 */
static void *pcpue_ptr __initdata;
static size_t pcpue_size __initdata;
static size_t pcpue_unit_size __initdata;

static struct page * __init pcpue_get_page(unsigned int cpu, int pageno)
{
	size_t off = (size_t)pageno << PAGE_SHIFT;

	if (off >= pcpue_size)
		return NULL;

	return virt_to_page(pcpue_ptr + cpu * pcpue_unit_size + off);
}

/**
 * pcpu_embed_first_chunk - embed the first percpu chunk into bootmem
 * @static_size: the size of static percpu area in bytes
 * @reserved_size: the size of reserved percpu area in bytes
 * @dyn_size: free size for dynamic allocation in bytes, -1 for auto
 * @unit_size: unit size in bytes, must be multiple of PAGE_SIZE, -1 for auto
 *
 * This is a helper to ease setting up embedded first percpu chunk and
 * can be called where pcpu_setup_first_chunk() is expected.
 *
 * If this function is used to setup the first chunk, it is allocated
 * as a contiguous area using bootmem allocator and used as-is without
 * being mapped into vmalloc area.  This enables the first chunk to
 * piggy back on the linear physical mapping which often uses larger
 * page size.
 *
 * When @dyn_size is positive, dynamic area might be larger than
 * specified to fill page alignment.  Also, when @dyn_size is auto,
 * @dyn_size does not fill the whole first chunk but only what's
 * necessary for page alignment after static and reserved areas.
 *
 * If the needed size is smaller than the minimum or specified unit
 * size, the leftover is returned to the bootmem allocator.
 *
 * RETURNS:
 * The determined pcpu_unit_size which can be used to initialize
 * percpu access on success, -errno on failure.
 */
ssize_t __init pcpu_embed_first_chunk(size_t static_size, size_t reserved_size,
				      ssize_t dyn_size, ssize_t unit_size)
{
	unsigned int cpu;

	/* determine parameters and allocate */
	pcpue_size = PFN_ALIGN(static_size + reserved_size +
			       (dyn_size >= 0 ? dyn_size : 0));
	if (dyn_size != 0)
		dyn_size = pcpue_size - static_size - reserved_size;

	if (unit_size >= 0) {
		BUG_ON(unit_size < pcpue_size);
		pcpue_unit_size = unit_size;
	} else
		pcpue_unit_size = max_t(size_t, pcpue_size, PCPU_MIN_UNIT_SIZE);

	pcpue_ptr = __alloc_bootmem_nopanic(
					num_possible_cpus() * pcpue_unit_size,
					PAGE_SIZE, __pa(MAX_DMA_ADDRESS));
	if (!pcpue_ptr)
		return -ENOMEM;

	/* return the leftover and copy */
	for_each_possible_cpu(cpu) {
		void *ptr = pcpue_ptr + cpu * pcpue_unit_size;

		free_bootmem(__pa(ptr + pcpue_size),
			     pcpue_unit_size - pcpue_size);
		memcpy(ptr, __per_cpu_load, static_size);
	}

	/* we're ready, commit */
	pr_info("PERCPU: Embedded %zu pages at %p, static data %zu bytes\n",
		pcpue_size >> PAGE_SHIFT, pcpue_ptr, static_size);

	return pcpu_setup_first_chunk(pcpue_get_page, static_size,
				      reserved_size, dyn_size,
				      pcpue_unit_size, pcpue_ptr, NULL);
}