CPU 비트맵 (API)

 

for_each_cpu()

include/linux/cpumask.h

/**
 * for_each_cpu - iterate over every cpu in a mask
 * @cpu: the (optionally unsigned) integer iterator
 * @mask: the cpumask pointer
 *
 * After the loop, cpu is >= nr_cpu_ids.
 */
#define for_each_cpu(cpu, mask)                         \
        for ((cpu) = -1;                                \
                (cpu) = cpumask_next((cpu), (mask)),    \
                (cpu) < nr_cpu_ids;)
  • mask가 가리키는 비트맵에서 각 비트가 1로 기록된 노드만큼 루프를 돈다.
    • nr_cpu_ids는 초기에 NR_CPUS 값이 설정되었다가 나중에 possible cpu id + 1 값으로 설정된다.

 

for_each_possible_cpu()

include/linux/cpumask.h

#define for_each_possible_cpu(cpu) for_each_cpu((cpu), cpu_possible_mask)
  • cpu_possible_mask가 가리키는 비트맵에서 각 비트가 1로 기록된 노드만큼 루프를 돈다.

 

for_each_online_cpu()

include/linux/cpumask.h

#define for_each_online_cpu(cpu)   for_each_cpu((cpu), cpu_online_mask)
  • cpu_online_mask가 가리키는 비트맵에서 각 비트가 1로 기록된 노드만큼 루프를 돈다.

 

for_each_present_cpu()

include/linux/cpumask.h

#define for_each_present_cpu(cpu)  for_each_cpu((cpu), cpu_present_mask)
  • cpu_present_mask가 가리키는 비트맵에서 각 비트가 1로 기록된 노드만큼 루프를 돈다.

 

on_each_cpu_mask()

/**
 * on_each_cpu_mask(): Run a function on processors specified by
 * cpumask, which may include the local processor.
 * @mask: The set of cpus to run on (only runs on online subset).
 * @func: The function to run. This must be fast and non-blocking.
 * @info: An arbitrary pointer to pass to the function.
 * @wait: If true, wait (atomically) until function has completed
 *        on other CPUs.
 *
 * If @wait is true, then returns once @func has returned.
 *
 * You must not call this function with disabled interrupts or from a
 * hardware interrupt handler or from a bottom half handler.  The
 * exception is that it may be used during early boot while
 * early_boot_irqs_disabled is set.
 */                     
void on_each_cpu_mask(const struct cpumask *mask, smp_call_func_t func,
                        void *info, bool wait)
{
        int cpu = get_cpu();    

        smp_call_function_many(mask, func, info, wait);
        if (cpumask_test_cpu(cpu, mask)) {
                unsigned long flags;
                local_irq_save(flags);
                func(info);
                local_irq_restore(flags);
        }               
        put_cpu();
}        
EXPORT_SYMBOL(on_each_cpu_mask);

func 함수를 다른 cpu에서 모두 수행시키고 현재 cpu에서도 수행시킨다. 인수 wait이 true인 경우 모든 cpu의 수행이 완료될 때까지 기다린다.

 

cpumask_first()

include/linux/cpumask.h

/**
 * cpumask_first - get the first cpu in a cpumask
 * @srcp: the cpumask pointer
 *
 * Returns >= nr_cpu_ids if no cpus set.
 */
static inline unsigned int cpumask_first(const struct cpumask *srcp)
{
        return find_first_bit(cpumask_bits(srcp), nr_cpumask_bits);
}

srcp가 가리키는 cpu 상태 관련 비트맵의 nr_cpumask_bits 까지에서 1로 설정된 첫 cpu id(based 0)를 알아온다.

 

cpumask_next()

include/linux/cpumask.h

/**
 * cpumask_next - get the next cpu in a cpumask
 * @n: the cpu prior to the place to search (ie. return will be > @n)
 * @srcp: the cpumask pointer
 *
 * Returns >= nr_cpu_ids if no further cpus set.
 */
static inline unsigned int cpumask_next(int n, const struct cpumask *srcp)
{
        /* -1 is a legal arg here. */
        if (n != -1)
                cpumask_check(n);
        return find_next_bit(cpumask_bits(srcp), nr_cpumask_bits, n+1);
}

srcp가 가리키는 cpu 상태 관련 비트맵의 nr_cpumask_bits 까지에서 n+1 번째 비트부터 1로 설정된 cpu id(based 0)를 찾아 알아온다.

 

cpumask_next_zero()

include/linux/cpumask.h

/**
 * cpumask_next_zero - get the next unset cpu in a cpumask
 * @n: the cpu prior to the place to search (ie. return will be > @n)
 * @srcp: the cpumask pointer
 *
 * Returns >= nr_cpu_ids if no further cpus unset.
 */
static inline unsigned int cpumask_next_zero(int n, const struct cpumask *srcp)
{
        /* -1 is a legal arg here. */
        if (n != -1)
                cpumask_check(n);
        return find_next_zero_bit(cpumask_bits(srcp), nr_cpumask_bits, n+1);
}

srcp가 가리키는 cpu 상태 관련 비트맵의 nr_cpumask_bits 까지에서 n+1 번째 비트부터 0으로 설정된 cpu id(based 0)를 찾아 알아온다.

 

cpumask_next_and()

lib/cpumask.c

/**
 * cpumask_next_and - get the next cpu in *src1p & *src2p
 * @n: the cpu prior to the place to search (ie. return will be > @n)
 * @src1p: the first cpumask pointer
 * @src2p: the second cpumask pointer
 *
 * Returns >= nr_cpu_ids if no further cpus set in both.
 */ 
int cpumask_next_and(int n, const struct cpumask *src1p,
                     const struct cpumask *src2p)
{
        while ((n = cpumask_next(n, src1p)) < nr_cpu_ids)
                if (cpumask_test_cpu(n, src2p))
                        break;
        return n;
}
EXPORT_SYMBOL(cpumask_next_and);

src1p와 src2p가 가리키는 두 개의 cpu 상태 관련 비트맵의 n+1 번째 비트부터 두 비트맵이 동시에 1로 설정된 cpu id(based 0)를 찾아 알아온다.

 

cpumask_first_and()

include/linux/cpumask.h

/**
 * cpumask_first_and - return the first cpu from *srcp1 & *srcp2
 * @src1p: the first input
 * @src2p: the second input
 *       
 * Returns >= nr_cpu_ids if no cpus set in both.  See also cpumask_next_and().
 */
#define cpumask_first_and(src1p, src2p) cpumask_next_and(-1, (src1p), (src2p))

src1p와 src2p가 가리키는 두 개의 cpu 상태 관련 비트맵의 1 번째 비트부터 두 비트맵이 동시에 1로 설정된 cpu id(based 0)를 찾아 알아온다.

 

CPU 상태

cpumask_t 타입

include/linux/cpumask.h

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

 

cpu 비트맵 배열

다음은 unsigned long 배열 형태의 비트맵이다.

  • cpu_all_bits[]
  • cpu_possible_bits[]
  • cpu_online_bits[]
  • cpu_present_bits[]
  • cpu_active_bits[]

다음은 위 4개의 비트맵을 가리키는 const 포인터이다.

  • *cpu_possible_mask
  • *cpu_online_mask
  • *cpu_present_mask
  • *cpu_active_mask

kernel/cpu.c

const DECLARE_BITMAP(cpu_all_bits, NR_CPUS) = CPU_BITS_ALL;
EXPORT_SYMBOL(cpu_all_bits);

#ifdef CONFIG_INIT_ALL_POSSIBLE
static DECLARE_BITMAP(cpu_possible_bits, CONFIG_NR_CPUS) __read_mostly
        = CPU_BITS_ALL;
#else
static DECLARE_BITMAP(cpu_possible_bits, CONFIG_NR_CPUS) __read_mostly;
#endif
const struct cpumask *const cpu_possible_mask = to_cpumask(cpu_possible_bits);
EXPORT_SYMBOL(cpu_possible_mask);

static DECLARE_BITMAP(cpu_online_bits, CONFIG_NR_CPUS) __read_mostly;
const struct cpumask *const cpu_online_mask = to_cpumask(cpu_online_bits);
EXPORT_SYMBOL(cpu_online_mask);

static DECLARE_BITMAP(cpu_present_bits, CONFIG_NR_CPUS) __read_mostly;
const struct cpumask *const cpu_present_mask = to_cpumask(cpu_present_bits);
EXPORT_SYMBOL(cpu_present_mask);

static DECLARE_BITMAP(cpu_active_bits, CONFIG_NR_CPUS) __read_mostly;
const struct cpumask *const cpu_active_mask = to_cpumask(cpu_active_bits);
EXPORT_SYMBOL(cpu_active_mask);

 

전역 변수

nr_cpu_ids

kernel/smp.c

/* Setup number of possible processor ids */
int nr_cpu_ids __read_mostly = NR_CPUS;
EXPORT_SYMBOL(nr_cpu_ids);
  • 초기 값은 NR_CPUS로 설정됨

 

setup_max_cpus

kernel/smp.c

/* Setup configured maximum number of CPUs to activate */
unsigned int setup_max_cpus = NR_CPUS;
EXPORT_SYMBOL(setup_max_cpus);
  • 초기 값은 NR_CPUS로 설정됨

 

커널 파라메터

“nosmp”

kernel/smp.c

static int __init nosmp(char *str)
{
        setup_max_cpus = 0;
        arch_disable_smp_support();

        return 0;
}

early_param("nosmp", nosmp);

커널 파라메터 “nosmp”가 입력되면 setup_max_cpus에 0을 대입하고 arch_disable_smp_support() 함수를 통해 smp 기능이 동작하지 않도록 한다.

  • 현재 이 커널 파라메터는 x86 시스템에만 구현되어 있다.

 

arch_disable_smp_support()

kernel/smp.c

/*
 * Setup routine for controlling SMP activation
 *
 * Command-line option of "nosmp" or "maxcpus=0" will disable SMP
 * activation entirely (the MPS table probe still happens, though).
 *
 * Command-line option of "maxcpus=<NUM>", where <NUM> is an integer
 * greater than 0, limits the maximum number of CPUs activated in
 * SMP mode to <NUM>.
 */

void __weak arch_disable_smp_support(void) { }
  • 각 아키텍처에서 정의된 함수를 사용하며 없는 경우 week 함수로 정의된 빈 함수가 동작한다.

 

“nr_cpus”

kernel/smp.c

/* this is hard limit */
static int __init nrcpus(char *str)
{
        int nr_cpus;

        get_option(&str, &nr_cpus);
        if (nr_cpus > 0 && nr_cpus < nr_cpu_ids)
                nr_cpu_ids = nr_cpus;

        return 0;
}

early_param("nr_cpus", nrcpus);

커널 파라메터 “nr_cpus”로 입력 받은 cpu 수를 nr_cpu_ids에 대입한다.  대입 가능 범위는 1~nr_cpus 만큼이다.

  • 예) “nr_cpus=2”

 

get_option()

lib/cmdline.c

/**
 *      get_option - Parse integer from an option string 
 *      @str: option string
 *      @pint: (output) integer value parsed from @str
 *
 *      Read an int from an option string; if available accept a subsequent
 *      comma as well.
 *
 *      Return values:
 *      0 - no int in string
 *      1 - int found, no subsequent comma
 *      2 - int found including a subsequent comma
 *      3 - hyphen found to denote a range
 */

int get_option(char **str, int *pint)
{
        char *cur = *str;

        if (!cur || !(*cur))
                return 0;
        *pint = simple_strtol(cur, str, 0);
        if (cur == *str)
                return 0;
        if (**str == ',') {
                (*str)++; 
                return 2;
        }
        if (**str == '-')
                return 3;
        
        return 1;
}
EXPORT_SYMBOL(get_option);

str 문자열을 정수로 변환하여 pint에 저장하고 리턴 되는 값은 다음과 같다.

  • 정수 변환이 되지 않는 경우 0
  • 정수 변환 후, 옵션 문자열이 없는 경우 1
  • 정수 변환 후, 옵션 문자열이 ‘,’인 경우 2
  • 정수 변환 후, 옵션 문자열이 ‘-‘인 경우 3

 

  • if (!cur || !(*cur)) return 0;
    • cur가 null이거나 cur 문자열이 “”인 경우 0을 리턴한다.
  • *pint = simple_strtol(cur, str, 0);
    • cur를 파싱하고 숫자로 변환하여 pint에 저장한다. 변환된 문자열의 다음 문자를 가리키는 주소를 출력 인수 str에 저장한다.
  • if (cur == *str) return 0;
    • 문자열을 숫자로 변환되지 않는 경우 0을 리턴한다.
  • if (**str == ‘,’) { (*str)++; return 2; }
    • 변환 후 다음 문자가 ‘,’인 경우 str을 증가시키고 2를 리턴한다.
  • if (**str == ‘-‘) return 3;
    • 변환 후 다음 문자가 ‘-‘인 경우 str을 증가시키고 3을 리턴한다.
  • 변환 후 문자가 ‘,’ 또는 ‘-‘가 아닌 경우 1을 리턴한다.

 

“maxcpus”

kernel/smp.c

static int __init maxcpus(char *str)
{
        get_option(&str, &setup_max_cpus);
        if (setup_max_cpus == 0)
                arch_disable_smp_support();

        return 0;
}

early_param("maxcpus", maxcpus);

커널 파라메터 “maxcpus”로 입력 받은 값을 setup_max_cpus에 대입한다.

  • 만일 0이 입력되면 smp 기능이 동작하지 않게 한다.
    • 현재 0 값의 허용은 x86 시스템에만 구현되어 있다.
  • 예) “maxcpus=2”

 

 

참고

답글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.