使用PSCI機制的SMP啟動分析


 

 

其他core的入口

 

文件:arch/arm64/kernel/head.S

 

 

 

secondary_entry

在從bl31切到EL1上的Linux Kernel后:

595行,在el2_setup中設置EL1EL0為小端模式,然后將w0設置為BOOT_CPU_MODE_EL1,並返回

596行,記錄cpuX的啟動模式到__boot_cpu_mode,目前是BOOT_CPU_MODE_EL1

 

secondary_startup

604行,__cpu_setup:

1、無效本地tlb

2、使能FP/ASIMD

3、設置mdscr_el1,在EL0訪問Debug Communication Channel寄存器時,陷入EL1

4、操作daif,使能debug中斷

5、設置pmuserenr_el0,當EL0訪問PMU寄存器時會陷入EL1

6、填充mair_el1,設置后面要用到的內存屬性索引,目前用到了6中內存屬性:

 

7、讀取sctlr_el1,修改后存入x0,后面配置mmu時會用到x0的值:x0= (sctrl_el1 & ~0xfcffffff)|0x34d5d91d,即將控制大小端的bit保留(因為之前在el2_setup里設置過了),其他位清零,然后設置新值,新值的含義如下:

 

從低位到高位依次說明:

0

M

1表示開啟EL1EL0stage1地址轉換機制,目前因為用不到虛擬化,所有只有stage1

1

A

0:關閉地址對齊檢查,但是load/store exclusiveload-acquire/store-release除外

2

C

1EL0/1data cache的控制不再由sctrl_el1控制,如果HCR_EL2.DC1,那么EL0/1data cache開啟。(所以,不開MMU,數據cache也可以開?)

3

SA

1  EL1上棧指針對齊檢查,需要16字節對齊

4

SA0

1EL0上棧指針對齊檢查,需要16字節對齊

5

CP15BEN

0EL0運行在AArch32時,不能使用CP15DMBCP15DSB以及CP15ISB

7

ITD

0EL0運行在AArch32模式時,仍舊可以使用IT指令(IF-THENhttp://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0489c/Cjabicci.html

8

SED

1EL0運行在AArch32時,不能使用SEDEND指令。這個指令是在ARMv6引入的,用於切換當前cpu的大小端,請參考:http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0489c/Cjacabbf.html

9

UMA

0:在EL0運行在AArch64上通過MRSMSR指令訪問DAIF時會陷入EL1

12

I

1EL0/1的指令cache不再由sctrl_el1控制。如果HCR_EL2.DC1,那么EL0/1的指令 cache開啟

14

DZE

1EL0運行AArch64時,如果執行DC ZVA指令(清除指令虛擬地址的data cache),會陷入EL1.

15

UCT

1EL0運行AArch64時,訪問CTR_EL0時不會陷入EL1. 這個寄存器用於獲取當前系統的cache的架構信息

16

nTWI

1: EL0上執行WFI時不會陷入EL1

18

nTWE

1: EL0上執行WFE時不會陷入EL1

19

WXN

0: EL1&0的內存訪問權限沒有影響。如果是1的話,在EL1&0下,如果某塊內存具備可寫權限,那么這塊內存就是XNExecute Never

24

E0E

控制EL0數據訪問的大小端控制,0小端,1大端

25

EE

控制EL1以及EL1/0table walk時的大小端,0小端,1大端

26

UCI

1EL0運行在AArch64時,如果執行DC CVAU/CIVAC/CVACIC IVAU時不會陷入EL1

 

8、設置TCR_EL1,用於建立1:1映射,目前配置的VA時48bit的:

 

[5:0] T0SZ 64-48=16
[21:16] T1SZ 64-48=16
[9:8] IRGN0, 使用TTBR0_EL1進行內存訪問時的Inner cacheability 1:Normal memory, Inner Write-Back Write-Allocate Cacheable
[25:24] IRGN1, 使用TTBR1_EL1進行內存訪問時的Inner cacheability 1:Normal memory, Inner Write-Back Write-Allocate Cacheable
[11:10] ORGN0, 使用TTBR0_EL1進行內存訪問時的Outer cacheability 1:Normal memory, Outer Write-Back Write-Allocate Cacheable
[27:26] ORGN1, 使用TTBR1_EL1進行內存訪問時的Outer cacheability 1:Normal memory, Outer Write-Back Write-Allocate Cacheable
[13:12] SH0, 使用TTBR0_EL1進行內存訪問時的Shareability attribute 3: Inner Shareable
[29:28] SH1, 使用TTBR1_EL1進行內存訪問時的Shareability attribute 3: Inner Shareable
[15:14] TG0, 使用TTBR0_EL1時的頁大小 0:4KB
[31:30] TG1, 使用TTBR1_EL1時的頁大小 0:4KB
[36] AS, 表示ASID的大小 1:16bit
[37] TBI0, 在使用TTBR0_EL1做地址翻譯時,是否忽略虛擬地址的高字節,如果不忽略的話,會被用來當tagged地址 1:忽略高字節
[22] A1, 選擇通過TTBR0_EL1或者TTBR1_EL1來定義ASID 1:使用TTBR1_EL1.ASID來定義ASID
[34:32] IPS,物理地址大小 通過讀取ID_AA64MMFR0_EL1的獲取當前系統的Physical Address range,然后將值寫到這里,目前是48
 

第605行,__enable_mmu:

1、首先判斷當前系統硬件是否支持4KB的粒度,是的話繼續執行,否則stuck住

2、更新boot cpu的狀態信息,即將__early_cpu_boot_status的值設置為0

3、將idmap_pg_dir賦值給ttbr0_el1

4、將swapper_pg_dir賦值給ttbr1_el1

5、將之前填充好的x0的值賦值給sctlr_el1

6、將icache無效

此時,mmu已經開啟了,上面用到的兩個頁目錄的內容在cpu0啟動時已經填寫好了,其他的core直接用。

 

__secondary_switched:

 

第611行,設置CPUx的異常向量表基地址為vectors

第615~619,設置CPUx的棧指針的值,這里用到的secondary_data的內容是在CPU0啟動CPUx時賦值的。sp指向了idle task的棧頂地址,sp_el0指向idle task的task_struct首地址

第622,跳轉到secondary_start_kernel

 

secondary_start_kernel:

 

第218,獲取init_mm,后面會賦值給current->active_mm
第222,獲取當前current任務所處的cpu編號,如果是cpu1的話,這里就返回1
第223,將cpuX在數組__per_cpu_offset中的對應項的地址&__per_cpu_offset[X]賦值給tpidr_el1
第229,增加init_mm的引用計數mm_count,因為所有的內核線程共享同一個mm_struct
第236,將ttbr0_el1賦值為empty_zero_page
第238,關閉當前CPU正在執行的task的搶占,這樣當前cpu就只能被中斷打斷,不會切到其他進程
第254,獲取當前cpu的各種硬件信息,比如arch_timer的頻率、cache類型等等,將信息存放到一個per cpu的全局變量cpu_data中,此時會看到
              啟動log里輸出"Detected %s I-cache on CPU%d" 接着會將這些信息跟cpu0的進行比較,如果發現不合理的地方,
               會輸出“Unsupported CPU feature variation detected.”
第259,通知之前注冊的回調函數,會使能gic和timer。此時cpuX的中斷還沒有使能,並且cpu0還在__cpu_up()中等待,沒有返回
            首先獲取當前cpu的cpuhp_state,然后調用從(CPUHP_BRINGUP_CPU+1)到CPUHP_AP_ONLINE之間注冊的bringup回調函數, 這個結合后面cpu0喚醒cpuX的邏輯分析。
 
CPUHP_AP_IDLE_DEAD    
CPUHP_AP_OFFLINE    
CPUHP_AP_SCHED_STARTING sched_cpu_starting  
CPUHP_AP_RCUTREE_DYING    
CPUHP_AP_IRQ_GIC_STARTING gic_starting_cpu 動態注冊的
CPUHP_AP_ARM_VFP_STARTING    
CPUHP_AP_ARM64_DEBUG_MONITORS_STARTING    
CPUHP_AP_PERF_ARM_HW_BREAKPOINT_STARTING    
CPUHP_AP_PERF_ARM_STARTING    
CPUHP_AP_ARM_ARCH_TIMER_STARTING arch_timer_starting_cpu 動態注冊
CPUHP_AP_ARM_GLOBAL_TIMER_STARTING    
CPUHP_AP_DUMMY_TIMER_STARTING    
CPUHP_AP_ARM_CORESIGHT_STARTING    
CPUHP_AP_ARM64_ISNDEP_STARTING    
CPUHP_AP_SMPCFD_DYING    
CPUHP_AP_ONLINE    

 

第261,讀取mpdir_el1,如果是多核的話,獲取當前cpu的thread id、core id以及cluster id。如果使能了DEBUG,那么會輸出“CPU%u: cluster %d core %d thread %d mpidr %#016llx”

第270,設置secondary_data.status為CPU_BOOT_SUCCESS,后面cpu0醒來后,會根據這個標志判斷cpuX是否啟動正常

第271,設置__cpu_online_mask中當前cpu對應的bit,后面cpu0醒來后,會根據這個標志判斷cpuX是否啟動正常

第272,通知cpu0,可以繼續往下運行了,此時cpu0在arch\arm64\kernel\smp.c中調用完boot_secondary后,緊接着wait_for_completion_timeout(&cpu_running, msecs_to_jiffies(1000))

第274,開啟本地中斷

第275,使能SERROR中斷

第280,執行cpu_startup_entry:

 

第371,arm沒有實現
第372,將當前cpu的cpuhp_state的state設置為CPUHP_AP_ONLINE_IDLE,然后對done_up完成變量執行complete操作。這個是跟cpu0通信,cpu0在執行到bringup_cpu最后,完成喚醒操作后, 會執行bringup_wait_for_ap(cpu)操作,它里面會調用wait_for_ap_thread(st, true)-->wait_for_completion(done)

第374,執行do_idle

 

 

CPU0啟動以及PSCI初始化

設備樹

psci:

 

cpu:
 

start_kernel:

1、smp_setup_processor_id(); 讀取mpdir,獲取當前cpu id,填寫到cpu_logical_map(0),輸出"Booting Linux on physical CPU 0x%lx\n"
2、boot_cpu_init():讀取mpdir,填寫set_cpu_online(0,true)/set_cpu_active(0, true)/set_cpu_present(0, true)/set_cpu_possible(0, true),記錄cpu號到__boot_cpu_id
3、setup_arch -> psci_dt_init: 調用psci_0_2_init,其中會跟bl31通信,並給psci_ops賦值
4、setup_arch -> cpu_read_bootcpu_ops(): 設置cpu_ops[0]為dt_supported_cpu_ops[1],即cpu_psci_ops,其中[0]用於spin-table啟動方式,目前我們用的是psci方式
其中cpu_psci_ops的定義如下:
5、setup_arch -> smp_init_cpus(),這個函數會接着調用of_parse_and_init_cpus,解析設備樹的cpu節點,將解析出來的reg屬性的值分
    別跟cpu_logical_map(0)/cpu_logical_map(1)/cpu_logical_map(2)/cpu_logical_map(3)映射起來,在解析到cpu0時,還會將bootcpu_valid設置為true。
                    如果設備樹中還指定了“numa-node-id”屬性,那么還會屬性 的值跟cpu_to_node_map[cpu]映射起來,最后cpu_count記錄實際解析到的cpu的個數。接着,smp_init_cpu對 非boot cpu,依次調用smp_cpu_setup,在其中會設置cpu_ops[cpu]為 cpu_psci_ops,然后 調用cpu_ops[cpu]->cpu_init(cpu),也就是上面的cpu_psci_cpu_init,在arm64平台上這 是個空函數,最后設置 set_cpu_possible (cpu, true)。 此時其他core都還沒有上電
6、 setup_arch -> smp_build_mpidr_hash() 填充mpidr_hash結構,如果開了debug,那么會輸出"MPIDR hash: aff0[%u] aff1[%u] aff2[%u] aff3[%u] mask[%#llx] bits[%u]\n"
7、smp_prepare_boot_cpu():首先設置tpidr_el1為__per_cpu_offset[0],接着調用jump_label_init,這個暫不分析。接着調用cpuinfo_store_boot_cpu,獲取cpu0的
    各種硬件信息,存放到per_cpu變量cpu_data中(對於cpu0,還會將boot_cpu_data指向cpu0的cpu_data ),這個在其他core起 來后也會執行,
     但是boot cpu(即cpu0)的不同之處是,除了獲取硬件信息之外,還會將這 些信息設置到arm64_ftr_regs結構中,將來其他core起來后,會進行對比。最后,如果
    使能了VHE,那么還會根據kernel是否處於EL2,來設置boot_cpu_hyp_mode為true或者false
8、boot_cpu_hotplug_init:設置cpu0的cpuhp_state.booted_once為true,同時將cpu0的cpuhp_state.state設置為CPUHP_ONLINE
9、init_IRQ():設置中斷棧,調用of_irq_init初始化中斷控制器,其中會注冊CPUHP_AP_IRQ_GIC_STARTING的回調函數gic_starting_cpu
10、time_init():會調用到drivers\clocksource\arm_arch_timer.c中的arch_timer_of_init,注冊CPUHP_AP_ARM_ARCH_TIMER_STARTING的回調函數arch_timer_starting_cpu

 

下面進入rest_init:

在這個函數里先后創建個內核線程kernel_init和kthreadd,然后在kernel_init中會wait_for_completion(&kthreadd_done),當主流程准備就緒后,設置system_state為SYSTEM_SCHEDULING,

然后主流程會complete(&kthreadd_done),在主流程的最后,會執行cpu_startup_entry(CPUHP_ONLINE),在前面非boot cpu的啟動分析中,最后也會執行cpu_startup_entry,不過它傳遞的cpu狀態是CPUHP_AP_ONLINE_IDLE

下面重點分析kernel_init:

在執行這個內核線程是,使用的仍然是cpu0,因為當前系統中也只有cpu0.

 

第1058,smp_prepare_cpus,首先調用init_cpu_topology,解析當前/cpus節點以及下面的cpu-map子節點,填充cpu_topology[cpu],得到系統cpu的拓撲關系,然后

            調用set_sched_topology設置sched_domain_topology為arm64_topology。如果沒有從設備樹里cpu-map獲得拓撲關系,smp_prepare_cpus會調用store_cpu_topology來

            設置cpu_topology[0]的內容。最后,這個函數會遍歷當前所有cpu,設置per_cpu變量cpu_number為cpu編號,然后cpu_ops[cpu]->cpu_prepare(cpu),如果成功的話,

             設置set_cpu_present(cpu, true)

第1064,do_pre_smp_initcalls:執行__initcall_start到__initcall0_start之間的回調函數

第1067,smp_init:這個函數就是cpu0喚醒其他core的地方,重點分析

 

smp_init:

 

第568,idle_threads_init,為每個非boot cpu都各fork一個idle task,將獲得的task_struct記錄到per_cpu變量idle_threads中
第569,cpuhp_threads_init,為每個core都創建一個"cpuhp/%u"內核線程,結果記錄在per_cpu變量cpuhp_state.thread中,然后啟動當前cpu的"cpuhp/%u"線程:"cpuhp/0"

第574~579,遍歷cpu,如果cpu沒有online(cpu_online_mask),那么調用cpu_on(cpu)

 

下面重點分析cpu_on:

 

上面第二個參數表示target status,表示需要將第一個參數表示的cpu,喚醒,並達到指定的狀態,目前cpu0就是處於CPUHP_ONLINE,這是cpu正常工作時的狀態
這個函數會繼續調用_cpu_up(cpu, 0, target):
1、檢查cpu_present(cpu),正常情況下,為1
2、如果待喚醒的cpu的cpuhp_state.state大於target表示的狀態,那么直接返回。數值越大,表示cpu越接近CPUHP_ONLINE,既然是bring up,那么當然是將state變大為目標
下面是cpu狀態切換的狀態圖:
 
上圖左邊是cpu up時的狀態切換:OFFLINE -> BRINGUP_CPU -> AP_OFFLINE -> AP_ONLINE  -> AP_ACTIVE
3、如果待處理cpu的當前狀態是CPUHP_OFFLINE,那么要先獲取該cpu的idle task結構,由於該cpu目前還沒有上電,自然屬於這種
4、調用cpuhp_set_state(st, target),設置待喚醒cpu的目標狀態為CPUHP_ONLINE,如果當前狀態小於target,將bringup設置為1,表示要執行up操作,返回的是當前狀態
 

4、如果待處理cpu的當前狀態大於CPUHP_BRINGUP_CPU,那么會執行cpuhp_kick_ap_work(cpu)。

5、下面的喚醒步驟分兩個階段,首先從OFFLINE到CPUHP_BRINGUP_CPU,然后剩下從CPUHP_BRINGUP_CPU到CPUHP_ONLINE則通過AP hotplug thread進行,這個線程就是上面創建的"cpuhp/0",

        它的task_struct存放在per_cpu變量cpuhp_state.thread中

 

下面是從OFFLINE->CPUHP_BRINGUP_CPU:

下面分析cpuhp_up_callbacks:

在從 OFFLINE-> CPUHP_BRINGUP_CPU的切換過程中,每一步切換,都會調用cpuhp_invoke_callback完成:
在該函數中,首先通過調用cpuhp_get_step(state)獲取state對應的cpuhp_step結構,內核在kernel\cpu.c里提供了兩個cpuhp_state數組:cpuhp_ap_states和cpuhp_bp_states,內核在這兩個數組中內置了一些初始的回調函數,也為動態注冊留出了位置,可以使用include\linux\cpuhotplug.h中提供的API進行注冊。 如果當前狀態小於CPUHP_BRINGUP_CPU並且不等於CPUHP_TEARDOWN_CPU時,使用的是cpuhp_bp_states,否則使用cpuhp_ap_states。 所以,對 OFFLINE-> CPUHP_BRINGUP_CPU,使用的是cpuhp_bp_states。

在獲得cpuhp_step結構后,先判斷該step是否支持multi_instance,如果不支持的話,對於bringup操作,會回調step->startup.single(cpu),否則回調step->teardown.single(cpu).對於支持multi_instance的step,會遍歷step->list,對於bringup,回調step->startup.multi(cpu,node),其中node就是從step->list遍歷得到的,否則回調step->teardown.multi(cpu,node)。

 

下面是cpuhp_bp_states中涉及到的回調:

 

CPUHP_CREATE_THREADS smpboot_create_threads 遍歷hotplug_threads,對沒有創建task的
CPUHP_PERF_PREPARE perf_event_init_cpu  
CPUHP_WORKQUEUE_PREP workqueue_prepare_cpu  
CPUHP_HRTIMERS_PREPARE hrtimers_prepare_cpu  
CPUHP_SMPCFD_PREPARE smpcfd_prepare_cpu  
CPUHP_RELAY_PREPARE relay_prepare_cpu  
CPUHP_SLAB_PREPARE slab_prepare_cpu  
CPUHP_RCUTREE_PREP rcutree_prepare_cpu  
CPUHP_TIMERS_PREPARE timers_prepare_cpu  
CPUHP_BRINGUP_CPU bringup_cpu  

 

bringup_cpu:

 

第518,獲取指定cpu的idle task進程描述符
第529,調用__cpu_up(cpu, idle),這里的cpu表示要喚醒的cpu,idle用於為剛喚醒的cpu提供棧空間。具體過程如下:
    1、將idle賦值給secondary_data.task
    2、將task_stack_page(idle) + THREAD_SIZE賦值給secondary_data.stack,cpuX喚醒后會使用這個棧,參考之前的分析
    3、將CPU_MMU_OFF賦值給secondary_data.status,在cpuX喚醒后,會修改這個值,然后cpu0會根據這個值判斷cpuX是否正常
    4、調用cpu_ops[cpu]->cpu_boot(cpu),這里的cpu_ops是cpu_psci_cpu_init,cpu_boot是cpu_psci_cpu_boot,這個函數會執行
psci_ops.cpu_on(cpu_logical_map(cpu), __pa_symbol(secondary_entry))

 

           cpu_on是在drivers\firmware\psci.c賦值的:

        上面會通過smc調用陷入bl31,然后cpuX在從EL3回到EL1時,就會從__pa_symbol(secondary_entry)開始運行,不過此時cpuX的MMU還沒有開啟,用的是物理地址

            5、如果上面返回成功,那么cpu0調用wait_for_completion_timeout(&cpu_running,msecs_to_jiffies(1000));  根據前面的分析,當cpuX醒來之后,會complete這里的cpu_running

            6、調用cpu_online(cpu)檢查cpuX是否成功online

            7、讀取READ_ONCE(secondary_data.status),然后判斷cpuX是否正常

第533,bringup_wait_for_ap(cpu):

            1、調用wait_for_ap_thread(st, true),因為是bringup,所以會wait_for_completion(&st->done_up)。在cpuX喚醒后,執行idle任務之前會complete該變量

            2、stop_machine_unpark(cpu);

            3、kthread_unpark(st->thread),這里的st->thread是"cpuhp/0",是在cpuhp_threads_init中初始化的

            4、如果st->target小於等於CPUHP_AP_ONLINE_IDLE,那么這個函數的任務就完成了,但是對於這里,target的值是CPUHP_ONLINE

            5、調用cpuhp_kick_ap(st, st->target),這個函數進一步調用__cpuhp_kick_ap(st):

 

            6、設置st->shoule_run為true
            7、在 __cpuhp_kick_ap中會喚醒st->thread,此時smpboot_thread_fn開始在cpuX上開始運行,並檢查 st->shoule_run為true,這樣 cpuhp_thread_fun被
                      smpboot_thread_fn 回調,這個線程負責cpuX第二個階段的狀態切換,在cpuX喚醒后,會進入CPUHP_AP_ONLINE_IDLE,所以完成從CPUHP_AP_ONLINE_IDLE->CPUHP_ONLINE
            8、wait_for_completion(&st->done_up),在 st->thread中會complete該變量

 

cpuhp_thread_fun:

 

1、比較cpuX的當前狀態和目標狀態,如果小於目標狀態,那么設置st->should為true,這樣這個函數返回后,還會被smpboot_thread_fn繼續回調

2、調用st->result = cpuhp_invoke_callback(cpu, state, bringup, st->node, &st->last);

3、如果上個函數返回失敗,設置st->should_run = false

4、如果st->should_run為false,那么調用complete_ap_thread(st, bringup),如果是bringup,這個函數會complete(&st->done_up)。即:在正常情況下,

        如果當前狀態達不到target狀態,那么st->should_run一直為true,只有達到target,才會complete(&st->done_up)。別忘了,此時cpu0還在wait呢

 

第二階段從CPUHP_AP_ONLINE_IDLE->CPUHP_ONLINE,下面的函數會被調用,這些函數位於cpuhp_ap_states中:

CPUHP_AP_SMPBOOT_THREADS smpboot_unpark_threads  
CPUHP_AP_IRQ_AFFINITY_ONLINE irq_affinity_online_cpu  
CPUHP_AP_PERF_ONLINE perf_event_init_cpu  
CPUHP_AP_WORKQUEUE_ONLINE workqueue_online_cpu  
CPUHP_AP_RCUTREE_ONLINE rcutree_online_cpu  
CPUHP_AP_ACTIVE sched_cpu_activate  
CPUHP_ONLINE NULL  
 
 

SMP啟動log

 

下面是多核啟動時的log,有助於我們理解上面的分析過程:(log中<cpu_id 進程name:進程id>)

 

[    0.117936] <0  swapper/0:1> smp: Bringing up secondary CPUs ...
[    0.118893] <0  swapper/0:1> smpboot_create_threads, cpu: 1
[    0.140551] <0  swapper/0:1> perf_event_init_cpu, cpu: 1
[    0.140958] <0  swapper/0:1> workqueue_prepare_cpu, cpu: 1
[    0.149409] <0  swapper/0:1> hrtimers_prepare_cpu, cpu: 1
[    0.149555] <0  swapper/0:1> smpcfd_prepare_cpu, cpu: 1
[    0.149780] <0  swapper/0:1> rcutree_prepare_cpu, cpu: 1
[    0.150696] <0  swapper/0:1> timers_prepare_cpu, cpu: 1
[    0.150999] <0  swapper/0:1> bringup_cpu, cpu: 1
[    0.152518] <1  swapper/1:0> Detected PIPT I-cache on CPU1
[    0.153478] <1  swapper/1:0> sched_cpu_starting, cpu: 1
[    0.153575] <1  swapper/1:0> GICv3: CPU1: found redistributor 1 region 0:0x00000000080c0000
[    0.153849] <1  swapper/1:0> CPU1: cluster 0 core 1 thread -1 mpidr 0x00000080000001
[    0.153955] <1  swapper/1:0> CPU1: Booted secondary processor [410fd083]
[    0.157031] <1    cpuhp/1:13> smpboot_unpark_threads, cpu: 1
[    0.158684] <1    cpuhp/1:13> irq_affinity_online_cpu, cpu: 1
[    0.158999] <1    cpuhp/1:13> perf_event_init_cpu, cpu: 1
[    0.159236] <1    cpuhp/1:13> workqueue_online_cpu, cpu: 1
[    0.160831] <1    cpuhp/1:13> rcutree_online_cpu, cpu: 1
[    0.161322] <1    cpuhp/1:13> sched_cpu_activate, cpu: 1
[    0.162781] <0  swapper/0:1> smpboot_create_threads, cpu: 2
[    0.185357] <0  swapper/0:1> perf_event_init_cpu, cpu: 2
[    0.185528] <0  swapper/0:1> workqueue_prepare_cpu, cpu: 2
[    0.193720] <0  swapper/0:1> hrtimers_prepare_cpu, cpu: 2
[    0.193829] <0  swapper/0:1> smpcfd_prepare_cpu, cpu: 2
[    0.193951] <0  swapper/0:1> rcutree_prepare_cpu, cpu: 2
[    0.194451] <0  swapper/0:1> timers_prepare_cpu, cpu: 2
[    0.194583] <0  swapper/0:1> bringup_cpu, cpu: 2
[    0.194853] <2  swapper/2:0> Detected PIPT I-cache on CPU2
[    0.194970] <2  swapper/2:0> sched_cpu_starting, cpu: 2
[    0.195027] <2  swapper/2:0> GICv3: CPU2: found redistributor 2 region 0:0x00000000080e0000
[    0.195198] <2  swapper/2:0> CPU2: cluster 0 core 2 thread -1 mpidr 0x00000080000002
[    0.195269] <2  swapper/2:0> CPU2: Booted secondary processor [410fd083]
[    0.195899] <2    cpuhp/2:18> smpboot_unpark_threads, cpu: 2
[    0.196294] <2    cpuhp/2:18> irq_affinity_online_cpu, cpu: 2
[    0.196426] <2    cpuhp/2:18> perf_event_init_cpu, cpu: 2
[    0.196594] <2    cpuhp/2:18> workqueue_online_cpu, cpu: 2
[    0.197099] <2    cpuhp/2:18> rcutree_online_cpu, cpu: 2
[    0.197325] <2    cpuhp/2:18> sched_cpu_activate, cpu: 2
[    0.197811] <0  swapper/0:1> smpboot_create_threads, cpu: 3
[    0.220870] <0  swapper/0:1> perf_event_init_cpu, cpu: 3
[    0.221065] <0  swapper/0:1> workqueue_prepare_cpu, cpu: 3
[    0.229439] <0  swapper/0:1> hrtimers_prepare_cpu, cpu: 3
[    0.229550] <0  swapper/0:1> smpcfd_prepare_cpu, cpu: 3
[    0.229675] <0  swapper/0:1> rcutree_prepare_cpu, cpu: 3
[    0.230062] <0  swapper/0:1> timers_prepare_cpu, cpu: 3
[    0.230195] <0  swapper/0:1> bringup_cpu, cpu: 3
[    0.230439] <3  swapper/3:0> Detected PIPT I-cache on CPU3
[    0.230549] <3  swapper/3:0> sched_cpu_starting, cpu: 3
[    0.230604] <3  swapper/3:0> GICv3: CPU3: found redistributor 3 region 0:0x0000000008100000
[    0.230767] <3  swapper/3:0> CPU3: cluster 0 core 3 thread -1 mpidr 0x00000080000003
[    0.230907] <3  swapper/3:0> CPU3: Booted secondary processor [410fd083]
[    0.231723] <3    cpuhp/3:23> smpboot_unpark_threads, cpu: 3
[    0.232132] <3    cpuhp/3:23> irq_affinity_online_cpu, cpu: 3
[    0.232266] <3    cpuhp/3:23> perf_event_init_cpu, cpu: 3
[    0.232439] <3    cpuhp/3:23> workqueue_online_cpu, cpu: 3
[    0.232941] <3    cpuhp/3:23> rcutree_online_cpu, cpu: 3
[    0.233179] <3    cpuhp/3:23> sched_cpu_activate, cpu: 3
[    0.233572] <0  swapper/0:1> smp: Brought up 1 node, 4 CPUs
[    0.233680] <0  swapper/0:1> SMP: Total of 4 processors activated.

 

 
完。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM