lkml.org 
[lkml]   [2009]   [Feb]   [23]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 09/10] x86: add embedding percpu first chunk allocator
    Date
    Impact: add better first percpu allocation for !NUMA

    On !NUMA, we can simply allocate contiguous memory and use it for the
    first chunk without mapping it into vmalloc area. As the memory area
    is covered by the large page physical memory mapping, it allows the
    dynamic perpcu allocator to not add any TLB overhead for the static
    percpu area and whatever falls into the first chunk and the
    implementation is very simple too.

    Signed-off-by: Tejun Heo <tj@kernel.org>
    ---
    arch/x86/kernel/setup_percpu.c | 86 +++++++++++++++++++++++++++++++++++++++-
    1 files changed, 85 insertions(+), 1 deletions(-)

    diff --git a/arch/x86/kernel/setup_percpu.c b/arch/x86/kernel/setup_percpu.c
    index 4a17c96..fd4c399 100644
    --- a/arch/x86/kernel/setup_percpu.c
    +++ b/arch/x86/kernel/setup_percpu.c
    @@ -43,6 +43,35 @@ unsigned long __per_cpu_offset[NR_CPUS] __read_mostly = {
    EXPORT_SYMBOL(__per_cpu_offset);

    /**
    + * pcpu_need_numa - determine percpu allocation needs to consider NUMA
    + *
    + * If NUMA is not configured or there is only one NUMA node available,
    + * there is no reason to consider NUMA. This function determines
    + * whether percpu allocation should consider NUMA or not.
    + *
    + * RETURNS:
    + * true if NUMA should be considered; otherwise, false.
    + */
    +static bool __init pcpu_need_numa(void)
    +{
    +#ifdef CONFIG_NEED_MULTIPLE_NODES
    + pg_data_t *last = NULL;
    + unsigned int cpu;
    +
    + for_each_possible_cpu(cpu) {
    + int node = early_cpu_to_node(cpu);
    +
    + if (node_online(node) && NODE_DATA(node) &&
    + last && last != NODE_DATA(node))
    + return true;
    +
    + last = NODE_DATA(node);
    + }
    +#endif
    + return false;
    +}
    +
    +/**
    * pcpu_alloc_bootmem - NUMA friendly alloc_bootmem wrapper for percpu
    * @cpu: cpu to allocate for
    * @size: size allocation in bytes
    @@ -82,6 +111,59 @@ static void * __init pcpu_alloc_bootmem(unsigned int cpu, unsigned long size,
    }

    /*
    + * Embedding allocator
    + *
    + * The first chunk is sized to just contain the static area plus
    + * PERCPU_DYNAMIC_RESERVE and 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 PMD mapping and doesn't add any additional pressure to
    + * TLB.
    + */
    +static void *pcpue_ptr __initdata;
    +static size_t pcpue_unit_size __initdata;
    +
    +static struct page * __init pcpue_get_page(unsigned int cpu, int pageno)
    +{
    + return virt_to_page(pcpue_ptr + cpu * pcpue_unit_size
    + + ((size_t)pageno << PAGE_SHIFT));
    +}
    +
    +static ssize_t __init setup_pcpu_embed(size_t static_size)
    +{
    + unsigned int cpu;
    +
    + /*
    + * If large page isn't supported, there's no benefit in doing
    + * this. Also, embedding allocation doesn't play well with
    + * NUMA.
    + */
    + if (!cpu_has_pse || pcpu_need_numa())
    + return -EINVAL;
    +
    + /* allocate and copy */
    + pcpue_unit_size = PFN_ALIGN(static_size + PERCPU_DYNAMIC_RESERVE);
    + pcpue_unit_size = max(pcpue_unit_size, PCPU_MIN_UNIT_SIZE);
    + pcpue_ptr = pcpu_alloc_bootmem(0, num_possible_cpus() * pcpue_unit_size,
    + PAGE_SIZE);
    + if (!pcpue_ptr)
    + return -ENOMEM;
    +
    + for_each_possible_cpu(cpu)
    + memcpy(pcpue_ptr + cpu * pcpue_unit_size, __per_cpu_load,
    + static_size);
    +
    + /* we're ready, commit */
    + pr_info("PERCPU: Embedded %zu pages at %p, static data %zu bytes\n",
    + pcpue_unit_size >> PAGE_SHIFT, pcpue_ptr, static_size);
    +
    + return pcpu_setup_first_chunk(pcpue_get_page, static_size,
    + pcpue_unit_size,
    + pcpue_unit_size - static_size, pcpue_ptr,
    + NULL);
    +}
    +
    +/*
    * 4k page allocator
    *
    * This is the basic allocator. Static percpu area is allocated
    @@ -178,7 +260,9 @@ void __init setup_per_cpu_areas(void)
    NR_CPUS, nr_cpumask_bits, nr_cpu_ids, nr_node_ids);

    /* allocate percpu area */
    - ret = setup_pcpu_4k(static_size);
    + ret = setup_pcpu_embed(static_size);
    + if (ret < 0)
    + ret = setup_pcpu_4k(static_size);
    if (ret < 0)
    panic("cannot allocate static percpu area (%zu bytes, err=%zd)",
    static_size, ret);
    --
    1.6.0.2


    \
     
     \ /
      Last update: 2009-02-24 04:19    [W:5.055 / U:0.448 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site