码迷,mamicode.com
首页 > 其他好文 > 详细

start_kernel——boot_cpu_init及PER_CPU

时间:2015-07-06 16:12:41      阅读:323      评论:0      收藏:0      [点我收藏+]

标签:

init/main.c

/*
 *  Activate the first processor.
 */

static void __init boot_cpu_init(void)
{
    int cpu = smp_processor_id();
    /* Mark the boot cpu "present", "online" etc for SMP and UP case */
    set_cpu_online(cpu, true);
    set_cpu_active(cpu, true);
    set_cpu_present(cpu, true);
    set_cpu_possible(cpu, true);
}

active第一个CPU,默认第一个CPU为boot CPU.
include/linux/smp.h

/*
 * smp_processor_id(): get the current CPU ID.
 *
 * if DEBUG_PREEMPT is enabled then we check whether it is
 * used in a preemption-safe way. (smp_processor_id() is safe
 * if it‘s used in a preemption-off critical section, or in
 * a thread that is bound to the current CPU.)
 *
 * NOTE: raw_smp_processor_id() is for internal use only
 * (smp_processor_id() is the preferred variant), but in rare
 * instances it might also be used to turn off false positives
 * (i.e. smp_processor_id() use that the debugging code reports but
 * which use for some reason is legal). Don‘t use this to hack around
 * the warning message, as your code might not work under PREEMPT.
 */
#ifdef CONFIG_DEBUG_PREEMPT
  extern unsigned int debug_smp_processor_id(void);
# define smp_processor_id() debug_smp_processor_id()
#else
# define smp_processor_id() raw_smp_processor_id()
#endif

得到当前CPU的ID
在我的代码中定义了CONFIG_DEBUG_PREEMPT宏,所以调用
lib/smp_processor.c

vnotrace unsigned int debug_smp_processor_id(void)
{
    int this_cpu = raw_smp_processor_id();

    if (likely(preempt_count()))
        goto out;

    if (irqs_disabled())
        goto out;

    /*
     * Kernel threads bound to a single CPU can safely use
     * smp_processor_id():
     */
    if (cpumask_equal(tsk_cpus_allowed(current), cpumask_of(this_cpu)))
        goto out;

    /*
     * It is valid to assume CPU-locality during early bootup:
     */
    if (system_state != SYSTEM_RUNNING)
        goto out;

    /*
     * Avoid recursion:
     */
    preempt_disable_notrace();

    if (!printk_ratelimit())
        goto out_enable;

    printk(KERN_ERR "BUG: using smp_processor_id() in preemptible [%08x] "
            "code: %s/%d\n",
            preempt_count() - 1, current->comm, current->pid);
    print_symbol("caller is %s\n", (long)__builtin_return_address(0));
    dump_stack();

out_enable:
    preempt_enable_no_resched_notrace();
out:
    return this_cpu;
}

EXPORT_SYMBOL(debug_smp_processor_id);

我们只看raw_smp_processor_id函数:
acrh/x86/include/asm/smp.h

#ifdef CONFIG_X86_32_SMP
/*
 * This function is needed by all SMP systems. It must _always_ be valid
 * from the initial startup. We map APIC_BASE very early in page_setup(),
 * so this is correct in the x86 case.
 */
#define raw_smp_processor_id() (this_cpu_read(cpu_number))
extern int safe_smp_processor_id(void);

#elif defined(CONFIG_X86_64_SMP)
#define raw_smp_processor_id() (this_cpu_read(cpu_number))

32位情况下调用this_cpu_read(cpu_number)
先看一下cpu_number的定义:

DEFINE_PER_CPU_READ_MOSTLY(int, cpu_number);
EXPORT_PER_CPU_SYMBOL(cpu_number);

PER_CPU变量


定义一个type为int,name为cpu_number的PER_CPU变量
include/linux/percpu-defs.h

#define DEFINE_PER_CPU_READ_MOSTLY(type, name)              \
    DEFINE_PER_CPU_SECTION(type, name, "..readmostly")

include/linux/percpu-defs.h

/*
 * s390 and alpha modules require percpu variables to be defined as
 * weak to force the compiler to generate GOT based external
 * references for them.  This is necessary because percpu sections
 * will be located outside of the usually addressable area.
 *
 * This definition puts the following two extra restrictions when
 * defining percpu variables.
 *
 * 1. The symbol must be globally unique, even the static ones.
 * 2. Static percpu variables cannot be defined inside a function.
 *
 * Archs which need weak percpu definitions should define
 * ARCH_NEEDS_WEAK_PER_CPU in asm/percpu.h when necessary.
 *
 * To ensure that the generic code observes the above two
 * restrictions, if CONFIG_DEBUG_FORCE_WEAK_PER_CPU is set weak
 * definition is used for all cases.
 */
#if defined(ARCH_NEEDS_WEAK_PER_CPU) || defined(CONFIG_DEBUG_FORCE_WEAK_PER_CPU)
/*
 * __pcpu_scope_* dummy variable is used to enforce scope.  It
 * receives the static modifier when it‘s used in front of
 * DEFINE_PER_CPU() and will trigger build failure if
 * DECLARE_PER_CPU() is used for the same variable.
 *
 * __pcpu_unique_* dummy variable is used to enforce symbol uniqueness
 * such that hidden weak symbol collision, which will cause unrelated
 * variables to share the same address, can be detected during build.
 */
#define DECLARE_PER_CPU_SECTION(type, name, sec)            \
    extern __PCPU_DUMMY_ATTRS char __pcpu_scope_##name;     \
    extern __PCPU_ATTRS(sec) __typeof__(type) name

#define DEFINE_PER_CPU_SECTION(type, name, sec)             \
    __PCPU_DUMMY_ATTRS char __pcpu_scope_##name;            \
    extern __PCPU_DUMMY_ATTRS char __pcpu_unique_##name;        \
    __PCPU_DUMMY_ATTRS char __pcpu_unique_##name;           \
    extern __PCPU_ATTRS(sec) __typeof__(type) name;             __PCPU_ATTRS(sec) PER_CPU_DEF_ATTRIBUTES __weak             __typeof__(type) name
#else
/*
 * Normal declaration and definition macros.
 */
#define DECLARE_PER_CPU_SECTION(type, name, sec)            \
    extern __PCPU_ATTRS(sec) __typeof__(type) name

#define DEFINE_PER_CPU_SECTION(type, name, sec)             \
    __PCPU_ATTRS(sec) PER_CPU_DEF_ATTRIBUTES                __typeof__(type) name
#endif

ARCH_NEEDS_WEAK_PER_CPU和CONFIG_DEBUG_FORCE_WEAK_PER_CPU都没有被定义,所以调用

__PCPU_ATTRS(sec) PER_CPU_DEF_ATTRIBUTES                __typeof__(type) name

__PCPU_ATTRS("..readmostly") PER_CPU_DEF_ATTRIBUTES             __typeof__(int) cpu_number

__PCPU_ATTRS定义在include/linux/percpu-defs.h

/*
 * Base implementations of per-CPU variable declarations and definitions, where
 * the section in which the variable is to be placed is provided by the
 * ‘sec‘ argument.  This may be used to affect the parameters governing the
 * variable‘s storage.
 *
 * NOTE!  The sections for the DECLARE and for the DEFINE must match, lest
 * linkage errors occur due the compiler generating the wrong code to access
 * that section.
 */
#define __PCPU_ATTRS(sec)                       \
    __percpu __attribute__((section(PER_CPU_BASE_SECTION sec)))     PER_CPU_ATTRIBUTES

__percpu在include/linux/compiler.h被定义为空
PER_CPU_ATTRIBUTES在include/asm-generic/percpu.h被定义为空
PER_CPU_DEF_ATTRIBUTES在include/asm-generic/percpu.h被定义为空
PER_CPU_BASE_SECTION定义在include/asm-generic/percpu.h

#ifndef PER_CPU_BASE_SECTION
#ifdef CONFIG_SMP
#define PER_CPU_BASE_SECTION ".data..percpu"
#else
#define PER_CPU_BASE_SECTION ".data"
#endif
#endif

我的代码中PER_CPU_BASE_SECTION被定义为”.data..percpu”
所以最终展开得到

__attribute__((section(".data..percpu" "..readmostly")))
    __typeof__(int) cpu_number

“gcc支持一种叫做类型识别的技术,通过typeof(x)关键字,获得x的数据类型。而如果是在一个要被一些c文件包含的头文件中获得变量的数据类型,就需要用_typeof_而不是typeof关键字了,比如说我们这里。最后,这里就是声明一个int类型的cpu_number变量,编译的时候把他指向.data..percpu段的开始位置”
(引号内这段摘抄自http://blog.chinaunix.net/uid-27717694-id-4033413.html


我们回到raw_smp_processor_id()宏,看一下this_cpu_read(cpu_number)
include/linux/percpu.h

# define this_cpu_read(pcp) __pcpu_size_call_return(this_cpu_read_, (pcp))

include/linux/percpu.h

#define __pcpu_size_call_return(stem, variable)             \
({  typeof(variable) pscr_ret__;                        __verify_pcpu_ptr(&(variable));                     switch(sizeof(variable)) {                      case 1: pscr_ret__ = stem##1(variable);break;           \
    case 2: pscr_ret__ = stem##2(variable);break;           \
    case 4: pscr_ret__ = stem##4(variable);break;           \
    case 8: pscr_ret__ = stem##8(variable);break;           \
    default:                                    __bad_size_call_parameter();break;              }                                   pscr_ret__;                         })

再往下就涉及汇编代码了,我们不分析具体架构。

回到boot_cpu_init函数接下来执行:

    /* Mark the boot cpu "present", "online" etc for SMP and UP case */
    set_cpu_online(cpu, true);
    set_cpu_active(cpu, true);
    set_cpu_present(cpu, true);
    set_cpu_possible(cpu, true);

对boot cpu进行一系列的设置:
先看set_cpu_online,这里采用位图对CPU变量进行管理,
linux/kernel/cpu.c

void set_cpu_online(unsigned int cpu, bool online)
{
    if (online)
        cpumask_set_cpu(cpu, to_cpumask(cpu_online_bits));
    else
        cpumask_clear_cpu(cpu, to_cpumask(cpu_online_bits));
}

cpu_online_bits的定义:
linux/kernel/cpu.c

static DECLARE_BITMAP(cpu_online_bits, CONFIG_NR_CPUS) __read_mostly;

include/linux/types.h

#define DECLARE_BITMAP(name,bits) \
    unsigned long name[BITS_TO_LONGS(bits)]

展开后得到

unsigned long cpu_online_bits[BITS_TO_LONGS(CONFIG_NR_CPUS)] __read_mostly;

CONFIG_NR_CPUS is maximum supported processors.
与具体的架构相关。
BITS_TO_LONGS定义在include/linux/bitops.h

#define BITS_PER_BYTE       8
#define BITS_TO_LONGS(nr)   DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))

include/linux/bitops.h

#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))

DIV_ROUND_UP表示向上圆整,是一种算法,保证数组的维度不小于nr,并且多出的部分不会太大。
所以这里实际上是定义了一个名为cpu_online_bits,类型为unsigned long的数组。

假设有一个4核CPU,那么CONFIG_NR_CPUS的值为4,而long32位,sizeof(long)为4,那么DIV_ROUND_UP(4, 8*4)的值为1.unsigned long bitmap[1],一共32位,足够4个CPU用了。

to_cpumask对cpu_online_bits数组进行转化:

/**
 * to_cpumask - convert an NR_CPUS bitmap to a struct cpumask *
 * @bitmap: the bitmap
 *
 * There are a few places where cpumask_var_t isn‘t appropriate and
 * static cpumasks must be used (eg. very early boot), yet we don‘t
 * expose the definition of ‘struct cpumask‘.
 *
 * This does the conversion, and can be used as a constant initializer.
 */
#define to_cpumask(bitmap)                      \
    ((struct cpumask *)(1 ? (bitmap)                                : (void *)sizeof(__check_is_bitmap(bitmap))))

其实就是将bitmap转化为struct cpumask *类型。
include/linux/cpumask.h

typedef struct cpumask { DECLARE_BITMAP(bits, NR_CPUS); } cpumask_t;

include/linux/threads.h

/* Places which use this should consider cpumask_var_t. */
#define NR_CPUS     CONFIG_NR_CPUS

它的成员也是一个unsigned long 类型的数组。
include/linux/cpumask.h

/**
 * cpumask_set_cpu - set a cpu in a cpumask
 * @cpu: cpu number (< nr_cpu_ids)
 * @dstp: the cpumask pointer
 */
static inline void cpumask_set_cpu(unsigned int cpu, struct cpumask *dstp)
{
    set_bit(cpumask_check(cpu), cpumask_bits(dstp));
}

接下来就是设置bit位,不同架构对应不同的汇编,我们不准备分析。
以上是对set_cpu_online的分析,其他几个函数也差不多。
这里可以看到,很多cpu相关的变量是用位图形式进行管理的,通过操作位图,可改变cpu的状态。

版权声明:本文为博主原创文章,未经博主允许不得转载。

start_kernel——boot_cpu_init及PER_CPU

标签:

原文地址:http://blog.csdn.net/yin262/article/details/46774041

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!