This repository contains some scripts, which I often use.
my-scripts's Introduction
my-scripts's People
my-scripts's Issues
tmp2
标题:向上级报告“静止状态”
小标题:rcu_report_qs_rdp
此函数用于向上级报告某个(指定的)cpu经过了当前宽限期的“静止状态”。
[插入rcu_report_qs_rdp代码]
<1> 首先需要检查是否需要上报:
(rdp->cpu_no_qs.b.norm && rdp->rcu_qs_ctr_snap == __this_cpu_read(rcu_qs_ctr)):表示此cpu还未经过任何“静止状态”
rdp->gpnum != rnp->gpnum:说明此cpu有“新的”宽限期(当前宽限期)没有注意到
rnp->completed == rnp->gpnum:当前宽限期已经结束了、相关工作都完成了
rdp->gpwrap为true:说明此cpu忽略了很多宽限期(可能由于它此前在“扩展静止状态”时间非常长)
上述任一情况下,不需要向上级报告。
<2> (rnp->qsmask & mask) == 0:说明此cpu不需要向其所属结点报告
<3> 走到这里,说明没有更新的宽限期且此cpu经历了当前宽限期(尚未结束)的“静止状态”,需要向上级报告。将rdp->core_needs_qs设置为false,表示此cpu目前不需要“静止状态”(因为还没有更新的宽限期)。然后,通过rcu_accelerate_cbs调整、“加快”callbacks链表(能加快的原因是:done-sublist中是宽限期已经完成的callbacks,而对于其他几个sublists中的callbacks,也仅仅需要等待一个宽限期完成,因此它们可以在下一个宽限期结束时被执行)。接着,通过rcu_report_qs_rnp函数向此cpu所属结点报告期经历了“静止状态”。最后,如果需要的话通过rcu_gp_kthread_wake唤醒宽限期线程。
小标题:rcu_report_qs_rnp
[插入rcu_report_qs_rnp代码]
<1> 回忆此前对Tree RCU“静止状态”上报流程的说明:某组中最后一个上报的cpu/group将代表该组向上级报告,所以此处我们首先看到的for循环就是其这个作用。该for循环中会做下列事情:
(a)!(rnp->qsmask & mask)说明相应的cpu/group已经报告过了,rnp->gpnum != gps说明gps号宽限期已经完成了。这两种情况下直接返回。
(b)否则说明有必要报告,于是从此组中将目标(正在上报的)cpu/group从qsmask中去除。这表示目标(正在上报的)cpu/group完成了报告。
(c)完成目标cpu/group的报告后,如果此组的qsmask不为0,说明刚才报告的目标cpu/group不是此组最后上报的,因此需要等待其他cpu/group继续向此组上报,所以直接返回。
(d)如果此组的parent为NULL,说明此组是root rcu_node了,于是break此循环。否则的话调整相关局部变量,在新一轮循环中此组将向上一级汇报。
<2> 走到这里,说明所有cpu都经过了“静止状态”,于是通过rcu_report_qs_rsp通知宽限期线程进行cleanup操作及开始下一个宽限期(如果需要的话)。
标题:宽限期处理
宽限期线程对应的执行函数是rcu_gp_kthread
小标题:rcu_gp_kthread
[插入rcu_gp_kthread代码]
<1> 此函数中包含一个大的for循环,永远不会结束。下面描述的操作均位于此大的for循环之中。
<2> 接着是一个内部for循环。此循环的作用是等待宽限期初始化请求(RCU_GP_FLAG_INIT),如果有请求到来,此线程会被唤醒,然后调用rcu_gp_init初始化新的宽限期,并跳出此循环。
<3> 在上一个内部for循环退出后,紧接着又是一个for循环。此循环的作用是等待宽限期完成或者设置了RCU_GP_FLAG_FQS或者(设置的强制宽限期检查间隔)超时。
(a)(!READ_ONCE(rnp->qsmask) && rcu_preempt_blocked_readers_cgp(rnp)表示是因为宽限期完成而被唤醒的,这种情况下跳出此循环,然后调用rcu_gp_cleanup进行宽限期结束的清理工作。
(b)(ULONG_CMP_GE(jiffies, rsp->jiffies_force_qs) || (gf & RCU_GP_FLAG_FQS))表示是因为超时或者设置了RCU_GP_FLAG_FQS而被唤醒的,则调用rcu_gp_fqs进行一轮强制静止状态处理。
(c)如果上述两种情况都不是,那么可能是此线程收到了什么错误的signal(不关心)
(d)重设强制宽限期检查间隔,然后回到此内部for循环开始出继续等待相关事件。
小标题:rcu_gp_init
[插入rcu_gp_init代码]
此函数的作用是初始化一个新的宽限期。如果没有宽限期请求的话会返回0。
<1> 如果rsp->gp_flags为0,表示不需要宽限期,于是直接返回0。
<2> 清空rsp->gp_flags标志
<3> 通过rcu_gp_in_progress判断当前是否已经有宽限期在进行中了,如果有的话就不要再启动另一个了,直接返回0。
<4> 要开始一个新的宽限期了,必然需要更新“当前宽限期”的编号,“当前宽限期”的编号是上一个宽限期的编号加1。
<5>
小标题:rcu_gp_fqs
[插入rcu_gp_fqs代码]
小标题:rcu_gp_cleanup
[插入rcu_gp_cleanup代码]
标题:其他重要函数
小标题:rcu_accelerate_cbs
小标题:rcu_advance_cbs
小标题:rcu_gp_kthread_wake
小标题:rcu_report_qs_rsp
小标题:rcu_gp_in_progress
rcu
cpu上报quiescent state
- rcu_sched_qs/rcu_bh_qs 两个函数表示注意到了一次quiescent state,其操作仅仅是将rcu_sched_data/rcu_bh_data的cpu_no_qs域置为false,表示该cpu已经有quiescent state了。
- rcu_all_qs为所有flavors的rcu注册一个quiescent state,其中主要的操作是将per-cpu变量rcu_qs_ctr递加。
- rcu_note_voluntary_context_switch,实际上就是rcu_all_qs函数
- rcu_note_context_switch函数
此函数表示注意到了一次context switch,它通过rcu_sched_qs来完成。
在__schedule和__kvm_guest_enter(rcu_virt_note_context_switch)中会调用rcu_note_context_switch。
在context switch中肯定会调用到__schedule,所以在后者中调用是毋庸置疑的。此外,当kvm进入guest mode时候,绝对不会持有被rcu保护的数据的reference,并且对于rcu来说,它所看到的 exit from guest mode 和 exit from userspace 效果是一样的,当 guest exit 的时候也可以认为是一次quiescent state。 - rcu_check_callbacks函数
此函数在hardirq context中执行,检查该cpu是否处在一个"非context-switch"quiescent state中(对于rcu/rcu_sched是user mode、idle;对于rcu_bh还加上在softirq执行之外),如果有必要的话(rcu_pending()返回true)则 invoke_rcu_core() 处理callbacks。
它通过rcu_sched_qs、rcu_bh_qs、invoke_rcu_core、rcu_note_voluntary_context_switch(rcu_all_qs)来完成。此函数会在timer中断处理中被调用(--> update_process_times --> rcu_check_callbacks),如果被中断的是user mode的话,那么会调用到rcu_note_voluntary_context_switch。(因为中断前已经在user mode中了,所以肯定出现一次quiescent state了) - cond_resched_rcu_qs, 向RCU报告 潜在的/可能的 quiescent state
如果当前不需要进行调度,那么调用rcu_note_voluntary_context_switch。
上面的分析中,我们需要注意一点,如何表示此cpu经过了一个quiescent state?那就是,要么将cpu_no_qs设置为false、要么递加其rcu_qs_ctr。
宽限期处理
对于每类flavors的RCU(rcu_sched、rcu_bh、rcu_preempt(暂不关注))会有各自专门的宽限期线程,这些线程的实际处理函数是rcu_gp_kthread。该线程首先会等待初始化一个宽限期的请求(RCU_GP_FLAG_INIT),收到之后便调用rcu_gp_init函数初始化一个新的宽限期,然后等待超时发生或者有强制quiescent-state扫描请求(RCU_GP_FLAG_FQS)或者当前宽限期已经完成。如果当前宽限期已经完成那么进入RCU_GP_CLEANUP状态,通过rcu_gp_cleanup函数来清理已完成的宽限期、同时会检查是否需要新的宽限期。如果是因为超时发生或者强制quiescent-state扫描请求,那么通过rcu_gp_fqs函数进行一个强制quiestate-state检查。
上述rcu_gp_kthread的等待过程都是将该线程设置为TASK_INTERRUPTIBLE状态,因此需要该线程执行的时候要显示唤醒它,唤醒方法是调用rcu_gp_kthread_wake函数。
处理callbacks
无论是nocb cpu还是非nocb cpu,其实都会执行到RCU_SOFTIRQ(对应处理函数是rcu_process_callbacks --> __rcu_process_callbacks),需要在里面对当前该cpu的相关rcu状态进行检查。当该cpu经历过一个quiescent-state时候会调用到rcu_report_qs_rdp函数。
rcu_report_qs_rdp函数用于向该cpu的rcu_data对象记录一个quiescent-state。此函数首先检查是否没有经历quiescent-state或者是否需要新的宽限期,如果是的话直接返回,否则继续下一步操作。检查该cpu所属的rcu_node(叶子结点),看该cpu是否需要上报一次quiescent-state,若是需要则立刻调用rcu_report_qs_rnp函数。
rcu_report_qs_rnp函数会从指定的rcu_node开始沿着rcu_node tree结构逐步向上报告一次quiescent-state,当然路径上某一个rcu_node在收到此次quiescent-state后其qsmask依然不为0,则说明此rcu_node中还有其他没有上报的cpu,所以到此直接返回。如果在上报此次之后,直到root node,其qsmask都为0了,表示系统中所有的cpu都已经上报过一次quiescent-state了,于是调用到rcu_report_qs_rsp函数。
rcu_report_qs_rsp函数是在所有cpu都经历了一次quiescent-state之后被调用,这种情况也说明当前宽限期已经完成了,于是通过rcu_gp_kthread_wake唤醒宽限期线程来完成收尾工作。
跑题了...
回到__rcu_process_callbacks函数中,会通过cpu_has_callbacks_ready_to_invoke函数(对于nocb cpu,永远返回false)检查当前是否有需要处理的callbacks,如果有的话则调用 invoke_rcu_callbacks--> rcu_do_batch 函数批量处理callbacks,依次对需要处理的callbacks执行 __rcu_reclaim函数,其中会调用相应的callback函数。
对于nocb cpu,__rcu_process_callbacks中会调用do_nocb_deferred_wakeup来唤醒相应的nocb线程(rcuoX/Y,执行体是rcu_nocb_kthread函数),依然是通过__rcu_reclaim函数处理每一个需要执行的callback。
extended quiescent state如何处理
首先,extended quiescent state指的是 idle loop 或 adaptive-tickless usermode execution,即在进入idle、进入usermode(包括kvm进入guest)可以看做是extended quiescent state。目前rcu核心中为了一个per-cpu变量rcu_dynticks,其中包括一个名为dynticks的域,如果它为偶数则说明该cpu在extened quiescent-state中,反之则不在。rcu核心中有一系列的 rcu__enter/rcu__exit接口用于这些场景。
上述对宽限期处理描述中并未覆盖到这种情况,那么问题来了:宽限期处理中如何感知某个cpu位于extened quiescent-state喃?
如果cpu处于extened quiescent-state状态,那么rcu_gp_kthread中会因为发生超时而调用到rcu_gp_fqs函数,在后者中又会调用到force_qs_rnp函数。在force_qs_rnp函数中,会通过传入的f参数(dyntick_save_progress_counter或rcu_implicit_dynticks_qs)检查各个叶子结点含有的cpu中是否有处于extened quiescent-state的(进入那两个函数后可以看到就是利用dynticks域的值来判断的),将这些cpu都记录在mask变量中,然后调用rcu_report_qs_rnp函数往上报!
相关配置
NOCB相关的配置有:
CONFIG_RCU_NOCB_CPU
(以下三者互斥)
CONFIG_RCU_NOCB_CPU_ALL:强制所有的cpu都offload callback
CONFIG_RCU_NOCB_CPU_NONE:不会强制任何一个cpu会offload callback,只有rcu_nocbs中设置的cpu会offload callback
CONFIG_RCU_NOCB_CPU_ZERO:强制 cpu 0 会offload callback,其他cpus若是rcu_nocbs中指明了则也会offload callback
rcu_nocb_mask中记录了哪些是nocb cpu。
rcu_is_nocb_cpu函数用来判断某个cpu是否是nocb cpu,根据不同配置其实现不同:
如果配置了CONFIG_RCU_NOCB_CPU_ALL,则所有的都返回true;
否则如果配置了CONFIG_RCU_NOCB_CPU,则通过rcu_nocb_mask来判断;
如果上面都没配置,则返回false。
CONFIG_NO_HZ_FULL:
如果某cpu上只有一个runnable task,则避免kernel发送scheduling-clock interrupts
在配置了CONFIG_RCU_NOCB_CPU前提下,如果又配置了CONFIG_NO_HZ_FULL,则no_hz_full的cpu也会被当做是nocb cpu(tick_nohz_full_mask、rcu_nocb_mask进行"或操作")
如果配置了CONFIG_NO_HZ_FULL,会定义rcu_user_enter/rcu_user_exit函数,这样当no_hz_full cpu进入userspace的时候可以认为该cpu进入了extended-quiescent-state中
CONFIG_RCU_FAST_NO_HZ:
此配置是的cpu即使在有callback插入的情况下也能够进入dynticks-idle模式。
这一点可以从rcu_needs_cpu函数中看出,此函数在tick_nohz_stop_sched_tick函数中被调用,后者会计算下一个timer到期时间,有下列片段:
if (rcu_needs_cpu(basemono, &next_rcu) ||
arch_needs_cpu() || irq_work_needs_cpu()) {
next_tick = basemono + TICK_NSEC;
} else {
......
next_tmr = get_next_timer_interrupt(basejiff, basemono);
ts->next_timer = next_tmr;
/* Take the next rcu event into account */
next_tick = next_rcu < next_tmr ? next_rcu : next_tmr;
}
可以看到如果rcu_needs_cpu返回true,那么下一个timer到期时间没有任何滞后(也就是没有进入dynticks);如果返回false的话则有可能(因为还要看arch_needs_cpu、irq_work_needs_cpu返回值)使得timer可以滞后(达到dynticks效果)
在没有配置CONFIG_RCU_FAST_NO_HZ情况下:如果配置了CONFIG_RCU_NOCB_CPU_ALL则返回false;否则当前cpu若是nocb的话返回false,如果不是nocb则如果没有callback则返回false、否则返回true
如果配置了CONFIG_RCU_FAST_NO_HZ:与上面最显著的区别就是对于 "非nocb cpu",即使有callbacks,但是只要这些callbacks不是ready的(即对应的宽限期已经完成)就可以进入dynticks
study
cpuid KVM-x86
TSC KVM-x86
VCPU模拟 KVM-x86
CR寄存器模拟 KVM-x86
指令模拟及解析 KVM-x86
MSR指令模拟 KVM-x86
hypercall模拟 KVM-x86
虚拟机上下文切换 KVM-x86
VMCS KVM-x86
VPID KVM-x86
EPT KVM-x86
PLE KVM-x86
VMX指令 KVM-x86
EPT缺页处理 KVM-x86
影子页表处理 KVM-x86
A/Dbit KVM-x86
异步缺页 KVM-x86
IOMMU KVM-x86
脏页跟踪 KVM-x86
影子页表 KVM-x86
虚拟机GPA及HVA管理 KVM-x86
透明大页 Kernel
静态大页 Kernel
swap Kernel
伙伴算法 Kernel
slab Kernel
PIC模拟 KVM-x86
IOAPIC模拟 KVM-x86
中断递交及路由 KVM-x86
NMI中断模拟 KVM-x86
MSI中断模拟 KVM-x86
APICV KVM-x86
X2apic模拟 KVM-x86
Lapic模拟 KVM-x86
ioeventfd KVM
irqfd KVM
FIFO Kernel
RR Kernel
OTHER Kernel
CFS框架 Kernel
RT Kernel
RCU Kernel
spinlock Kernel
读写锁 Kernel
原子锁 Kernel
序列锁 Kernel
Pit模拟 KVM-x86
kvmclock KVM-x86
Lapic timer 模拟 KVM-x86
hyperv clock KVM-x86
timekeeping Kernel
clocksource Kernel
clockevent Kernel
nohz Kernel
RTC Kernel
highres Kernel
IOMMU管理 KVM-x86
直通设备中断管理 KVM-x86
设备直通pci-assign KVM-x86
设备直通VFIO KVM-x86
mmio处理 KVM-x86
mmio聚合 KVM-x86
pio处理 KVM-x86
io_bus及io_device KVM-x86
fpu模拟 KVM-x86
pvchannel模拟 KVM-x86
pmu模拟 KVM-x86
hyperv支持 KVM-x86
嵌套虚拟化 KVM-x86
vcpu_enter_guest KVM-x86
虚拟机退出处理 KVM-x86
cpuset kernel
cpu kernel
nls kernel
blk kernel
memory kernel
tmp
标题:重要数据结构
(只列出了重要字段)
- struct rcu_node (kernel/rcu/tree.h)
unsigned long gpnum :当前宽限期的编号
unsigned long completed :最近的、已完成的宽限期的编号
unsigned long qsmask :需要track的CPUs或groups位图,对于leaf rcu_node来说每个bit对应一个rcu_data(cpu),其他的则每个bit对应一个group(rcu_node)
unsigned long qsmaskinit :在每个宽限期初始化阶段用来设置qsmask
unsigned long qsmaskinitnext :下一个宽限期中需要track的CPUs,在每个宽限期初始化阶段用来设置qsmaskinit
unsigned long grpmask :向parent表示此rcu_node的编号mask,其值为 1<<grpnum
int grplo :包含的groups(此node是非leaf)或CPUs(此node是leaf)中编号最小的
int grphi :包含的groups(此node是非leaf)或CPUs(此node是leaf)中编号最大的
u8 grpnum :此node的编号
struct rcu_node *parent :此node的父结点
wait_queue_head_t nocb_gp_wq[2] :nocb kthread leader在等待相关宽限期完成时sleep在此(见“nocb kthread组织形式”)
int need_future_gp[2] :???? - struct rcu_state (kernel/rcu/tree.h)
struct rcu_node node[NUM_RCU_NODES] :RCU tree结构的所有node
struct rcu_data __percpu *rda :指向此rcu_state的per-cpu类型的rcu_data数据
unsigned long gpnum :当前宽限期编号
unsigned long completed :最近的、已完成的宽限期号码
struct task_struct *gp_kthread :宽限期线程
wait_queue_head_t gp_wq :宽限期线程sleep在此
struct list_head flavors :所有flavor的rcu_state都维护在一个链表中 - struct rcu_data (kernel/rcu/tree.h)
unsigned long completed : 和p->completed的值一样,可以用来判断当前宽限期是否结束(结束的话completed==gpnum成立)
unsigned long gpnum :当前cpu所感知到的宽限期的最大编号
unsigned long rcu_qs_ctr_snap :是全局的、per-cpu变量rcu_qs_ctr的一个镜像
union rcu_noqs cpu_no_qs :为true表示此cpu没有经历quiescent-state,为false则表示经历了
struct rcu_head _nxtlist、struct rcu_head *_nxttail[RCU_NEXT_SIZE] :normal cpu的callbacks链表,有RCU_NEXT_SIZE个sublist组成
unsigned long nxtcompleted[RCU_NEXT_SIZE] :上述每个sublist中的callbacks在等待的宽限期
struct rcu_dynticks _dynticks :每个cpu的dynticks状态
struct rcu_head *nocb_head、struct rcu_head *_nocb_tail :nocb cpu的callbacks链表(等待相应宽限期完成)
wait_queue_head_t nocb_wq :nocb kthread(包括leader、follower)等待callbacks到来时阻塞在此
struct rcu_head _nocb_follower_head、struct rcu_head *_nocb_follower_tail :nocb cpu的callbacks链表(相应宽限期已经完成、等待被执行)
struct task_struct *nocb_kthread :此cpu对应的nocb线程
struct rcu_data *nocb_next_follower、struct rcu_data *nocb_leader :用于维护nocb kthread的leader-follower组织形式
标题:nocb cpu
-
什么是nocb cpu
早期RCU的设计中,所有的callback(通常是释放旧数据)都放在rcu softirq中执行,这会导致重大的OS jitter和调度延迟。(原因:无论什么进程、无论其优先级如何,在其执行期间softirq都有可能“插进来”处理,而根据系统中workload,有可能rcu softirq在所有softirq中占了比较大的比重)
为了解决这问题,Paul(mantainer,大师)引入了nocb特性:将某些cpu指定为nocb cpu(以前必须至少有一个不是nocb,现在取消了此限制,即所有cpu都可以是nocb的),对于nocb cpu,会为其创建对应的rcuo kthread,其rcu callback将offload到这些kthread中执行。
CONFIG_RCU_NOCB_CPU:rcu_nocbs中指定的cpu是nocb的。
CONFIG_RCU_NOCB_CPU_ALL:强制所有的cpu都是nocb的。
CONFIG_RCU_NOCB_CPU_NONE:不会强制任何一个cpu是nocb的,只有rcu_nocbs中设置的cpu是。
CONFIG_RCU_NOCB_CPU_ZERO:强制 cpu 0 是nocb的,其他cpus若是rcu_nocbs中指明了则也是。 -
如何判定是nocb cpu
a. rcu_is_nocb_cpu函数if defined(CONFIG_RCU_NOCB_CPU_ALL)
static inline bool rcu_is_nocb_cpu(int cpu) { return true; }
elif defined(CONFIG_RCU_NOCB_CPU)
bool rcu_is_nocb_cpu(int cpu);
else
static inline bool rcu_is_nocb_cpu(int cpu) { return false; }
endif
如果配置了CONFIG_RCU_NOCB_CPU_ALL,则所有cpu都是nocb的,因此直接返回true。
如果既没CONFIG_RCU_NOCB_CPU_ALL、又没CONFIG_RCU_NOCB_CPU,则所有cpu都不是nocb,因此直接返回false。
对于配置了CONFIG_RCU_NOCB_CPU,rcu_is_nocb_cpu函数实现如下:ifdef CONFIG_RCU_NOCB_CPU
......
ifndef CONFIG_RCU_NOCB_CPU_ALL
/* Is the specified CPU a no-CBs CPU? */
bool rcu_is_nocb_cpu(int cpu)
{
if (have_rcu_nocb_mask)
return cpumask_test_cpu(cpu, rcu_nocb_mask);
return false;
}endif /* #ifndef CONFIG_RCU_NOCB_CPU_ALL */
......
else /* #ifdef CONFIG_RCU_NOCB_CPU */
......
通过rcu_nocb_mask位图来判定指定cpu是否为nocb的。(rcu_nocb_setup、rcu_init_nohz中会对该位图进行初始化设置)
b. 通过rcu_data的nxtlist、nxttail进行判定
在RCU core中,每个cpu会对应一个rcu_data对象,其中nocb_head、nocb_tail用于维护nocb cpu的callbacks链表,而nxtlist、nxttail[]用于维护normal cpu的callbacks链表。在RCU core初始化过程中,nocb CPU的nxtlist、nxttail[RCU_NEXT_TAIL]都被设置为NULL,而normal cpu的nxttail[RCU_NEXT_TAIL]指向的是nxtlist(其nxttail[RCU_NEXT_TAIL]永远不会是NULL),所以能够通过nxttail[RCU_NEXT_TAIL]来判断是否为nocb的。
c. nocb kthread组织形式
当cpu较多、系统中跑着context-switch-heavy workload的情况下,需要很多nocb kthread的唤醒操作。仅仅是因为唤醒操作,RCU宽限期线程就花费了大量时间在上面。这显然导致可扩展性下降:当系统中cpu达到一定数量时,反而可能会增加宽限期延迟。为了解决此问题,Paul将nocb线程分成分成leader、follower,宽限期线程只需要唤醒leader,而leader负责依次唤醒其followers。默认情况下,每个组中有“cpu数量的平方根”个nocb线程,但可以通过rcu_nocb_leader_stride启动参数修改。
在rcu_init_nohz-->rcu_organize_nocb_kthreads中会组织nocb cpu的这种leader/follower关系
标题:上报"禁止状态"
- context switch
void rcu_note_context_switch(void)
{
......
rcu_sched_qs();
......
}
rcu_note_context_switch函数向RCU core通知经过了一次context-switch。
在__schedule中会调用它
在__kvm_guest_enter中,在context_tracking disable情况下会调用它。(在context_tracking enable情况下,会调用rcu_user_enter[__kvm_guest_enter-->guest_enter-->__context_tracking_enter-->rcu_user_enter]来告诉RCU core该cpu将进入kvm userspace(与user mode等效)) - idle loop
rcu_idle_enter通知RCU core当前cpu进入idle模式,此模式下RCU读者临界区不会出现。
有一种情况可能会被质疑:当idle时如果有interrupt发生,而相应irq handler中可能会进入读者临界区。但是该情况下irq_enter/irq_exit(会调用rcu_irq_enter/rcu_irq_exit)已经考虑到、处理了。
下面关注下rcu_idle_enter何时被调用:
cpu_startup_entry函数实质上是每个cpu的idle进程执行体
void cpu_startup_entry(enum cpuhp_state state)
{
......
cpu_idle_loop();
}
static void cpu_idle_loop(void)
{
while (1) {
......
while (!need_resched()) {
......
if (cpu_idle_force_poll || tick_check_broadcast_expired())
cpu_idle_poll();
else
cpuidle_idle_call();
......
}
......
}
}
static inline int cpu_idle_poll(void)
{
rcu_idle_enter();
......
rcu_idle_exit();
return 1;
}
static void cpuidle_idle_call(void)
{
......
rcu_idle_enter();
......
rcu_idle_exit();
}
- user mode / guest mode
对于kvm进入guest的情况,上面已经提及过了。
上面提及的函数是rcu_user_enter,在CONFIG_NO_HZ_FULL配置下才有意义。其他子系统的某些API(目前已知user_enter、guest_enter)中会调用到它。
遗留:还没看到在哪里调用它来通知进入user mode - offline
对于offline的cpu,RCU core会将其从所属leaf rcu_node的qsmaskinit中去除掉,这样在下一个宽限期中将不会track该cpu。 - 处理完一遍softirq
。。。。。。。
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.