call_function_init()

IPI(Inter Process Interrupts)를 수신 받아 처리하는 핸들러 함수는 handle_IPI()이다. 이 함수에서 사용하는 여러 IPI 항목 중 다음 2개의 항목은 다른 cpu의 요청에 의해 전달받은 call function data를 통해 이에 등록된 함수를 호출한다. 이러한 기능을 지원하기 위해 call_function_init() 함수를 통해 초기화한다.

  • IPI_CALL_FUNC
    • 요청한 함수들을 동작시킨다.
  • IPI_CALL_FUNC_SINGLE
    • 요청한 함수 하나를 동작시킨다.

 

call_function_init()

kernel/smp.c

void __init call_function_init(void)
{
        void *cpu = (void *)(long)smp_processor_id();
        int i;

        for_each_possible_cpu(i)
                init_llist_head(&per_cpu(call_single_queue, i));

        hotplug_cfd(&hotplug_cfd_notifier, CPU_UP_PREPARE, cpu);
        register_cpu_notifier(&hotplug_cfd_notifier);
}

call function에 사용되는 IPI 기능을 초기화한다.

  • 코드 라인 6~7에서 call function이 담기는 call_single_queue를 cpu 수 만큼초기화한다.
  • 코드 라인 9에서 부트업 중인 현재 cpu는 이미 up되어 있으므로 현재 cpu에 대한 hotplug_cfd() 함수를 호출하여 초기화한다.
  • 코드 라인 10에서 cpu 상태 변화에 따라 호출되도록 cpu notifier 블럭을 추가한다.

 

kernel/smp.c

static DEFINE_PER_CPU_SHARED_ALIGNED(struct llist_head, call_single_queue);

cpu별 call function data 구조체가 담기는 리스트이다.

 

kernel/smp.c

static struct notifier_block hotplug_cfd_notifier = {
        .notifier_call          = hotplug_cfd,
};

cpu 상태가 변경될 때 마다 hotplug_cfd() 함수가 호출될 수 있도록 notifier block을 준비한다.

 

hotplug_cfd()

kernel/smp.c

static int
hotplug_cfd(struct notifier_block *nfb, unsigned long action, void *hcpu)
{
        long cpu = (long)hcpu;
        struct call_function_data *cfd = &per_cpu(cfd_data, cpu);

        switch (action) {
        case CPU_UP_PREPARE:
        case CPU_UP_PREPARE_FROZEN:
                if (!zalloc_cpumask_var_node(&cfd->cpumask, GFP_KERNEL,
                                cpu_to_node(cpu)))
                        return notifier_from_errno(-ENOMEM);
                cfd->csd = alloc_percpu(struct call_single_data);
                if (!cfd->csd) {
                        free_cpumask_var(cfd->cpumask);
                        return notifier_from_errno(-ENOMEM);
                }
                break;

#ifdef CONFIG_HOTPLUG_CPU
        case CPU_UP_CANCELED:
        case CPU_UP_CANCELED_FROZEN:
                /* Fall-through to the CPU_DEAD[_FROZEN] case. */ 

        case CPU_DEAD:
        case CPU_DEAD_FROZEN:
                free_cpumask_var(cfd->cpumask);
                free_percpu(cfd->csd);
                break;

        case CPU_DYING:
        case CPU_DYING_FROZEN:
                /* 
                 * The IPIs for the smp-call-function callbacks queued by other
                 * CPUs might arrive late, either due to hardware latencies or
                 * because this CPU disabled interrupts (inside stop-machine)
                 * before the IPIs were sent. So flush out any pending callbacks
                 * explicitly (without waiting for the IPIs to arrive), to
                 * ensure that the outgoing CPU doesn't go offline with work
                 * still pending.
                 */
                flush_smp_call_function_queue(false);
                break;
#endif
        };

        return NOTIFY_OK;
}

cpu 상태가 변화할 때 마다 호출되는 함수로 cpu가 on/off 될 떄마다 해당 cpu에 대한 cfd_data를 초기화하거나 할당해제 처리한다.

  • 코드 라인 5에서 요청 cpu의 cfd_data를 알아와서 cfd에 대입한다.
  • 코드 라인 7~18에서 cfd의 cpumask를 할당하고 per-cpu로 call_single_data 구조체를 할당받아 cfd의 csd에 할당한다.
  • 코드 라인 20~29에서 시스템이 hotplug cpu를 지원하는 경우 cpu가 off된 경우 cfd에 할당해둔 cpumask와 csd를 할당 해제 한다.
  • 코드 라인 31~44에서 cpu가 off되려고하면 call_single_queue를 다 비우고 그 안에 담겨있었던 call function을 모두 호출하여 처리하고 현재의 모든 irq 작업들을 즉시 수행하게 한다.

 

kernel/smp.c

static DEFINE_PER_CPU_SHARED_ALIGNED(struct call_function_data, cfd_data);

전역 cpu별 call_function_data 구조체로 다른 cpu로 single call function을 요청할 때 요청 정보가 담긴다.

  • 이 정보는 전송 시 다른 cpu에서 요청한 함수의 처리가 완료될 때까지 기다리지 않을 때에만 전달 정보로 사용된다.

 

참고

답글 남기기

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