TREE RCU实现之三 —— 定期调用
上一节,介绍过了RCU实现中用到的主要函数。不过还需要定期的运行这些函数,整个机制才完整。
RCU的实现是通过在update_process_times() 中调用rcu_check_callbacks()来达到这个目的的。每个CPU都会定期的调用update_process_times()。rcu_check_callbacks()会去检查当前的RCU机制中是否有需要处理的内容,如当前CPU需要开启一个新的宽限期,当前CPU上的宽限期还没有处理完成。如果有需要处理的内容,将触发一个软件中断,真正的操作由软件中断触发的rcu_process_callbacks()来完成。
rcu_check_callbacks
void rcu_check_callbacks(int cpu, int user){ trace_rcu_utilization("Start scheduler-tick"); increment_cpu_stall_ticks(); if (user || rcu_is_cpu_rrupt_from_idle()) { /* * 如果是从用户模式或者是idle模式调用该函数, * 那么这个CPU是静止状态。 * * 此处不需要内存屏障。因为rcu_sched_qs()和 * and rcu_bh_qs()支处理CPU自身的局部变量, * 其它CPU不会访问和修改,至少当CPU在线的时候。 * */ rcu_sched_qs(cpu); rcu_bh_qs(cpu); } else if (!in_softirq()) { /* * 运行到这儿,如果不是软件中断。如果当前CPU上运行的 * 软中断的读过程,肯定已经完成,所以标记它。 * */ rcu_bh_qs(cpu); } rcu_preempt_check_callbacks(cpu); /*抢先式下的检测*/ if (rcu_pending(cpu)) invoke_rcu_core(); trace_rcu_utilization("End scheduler-tick");}
该函数的主要功能是通过 rcu_pending()判断是否当前有需要处理的rcu内容,如果有调用invoke_rcu_core()。
static int rcu_pending(int cpu){struct rcu_state *rsp;for_each_rcu_flavor(rsp)if (__rcu_pending(rsp, per_cpu_ptr(rsp->rda, cpu)))return 1;return 0;} rcu_pending会循环所有的rcu_state,在非抢占式模式下,有rcu_sched_state 和rcu_bh_state 两个实例。
static int __rcu_pending(struct rcu_state *rsp, struct rcu_data *rdp){struct rcu_node *rnp = rdp->mynode;rdp->n_rcu_pending++;/* Check for CPU stalls, if enabled. */check_cpu_stall(rsp, rdp);/* 是否宽限期在等待这个CPU去完成静止状态呢? */if (rcu_scheduler_fully_active && rdp->qs_pending && !rdp->passed_quiesce) {/* * 如果force_quiescent_state() 需要马上执行,而这个CPU * 需要一个静止状态,强制执行本地进程切换。 */rdp->n_rp_qs_pending++;if (!rdp->preemptible && ULONG_CMP_LT(ACCESS_ONCE(rsp->jiffies_force_qs) - 1, jiffies))set_need_resched();} else if (rdp->qs_pending && rdp->passed_quiesce) {rdp->n_rp_report_qs++;return 1;}/* 这个CPU是否有callbacks等着调用? */if (cpu_has_callbacks_ready_to_invoke(rdp)) {rdp->n_rp_cb_ready++;return 1;}/* 当前CPU有需要执行的宽限期,而没有其它的宽限期在执行? */if (cpu_needs_another_gp(rsp, rdp)) {rdp->n_rp_cpu_needs_gp++;return 1;}/* 另一个CPU上执行的宽限期结束? */if (ACCESS_ONCE(rnp->completed) != rdp->completed) { /* outside lock */rdp->n_rp_gp_completed++;return 1;}/* 有新的RCU开始? */if (ACCESS_ONCE(rnp->gpnum) != rdp->gpnum) { /* outside lock */rdp->n_rp_gp_started++;return 1;}/* 一个宽限期运行了太长时间,需要强制执行? */if (rcu_gp_in_progress(rsp) && ULONG_CMP_LT(ACCESS_ONCE(rsp->jiffies_force_qs), jiffies)) {rdp->n_rp_need_fqs++;return 1;}/* 无事可做 */rdp->n_rp_need_nothing++;return 0;}
__rcu_pending 判断了可能存在的各种情形,如果有需要处理的工作的话,就返回1,否则返回0。
static void invoke_rcu_core(void){raise_softirq(RCU_SOFTIRQ);}
invoke_rcu_core()的作用是开启软中断。在初始化的时候,系统已经注册了软中断。
open_softirq(RCU_SOFTIRQ, rcu_process_callbacks);
static void rcu_process_callbacks(struct softirq_action *unused){struct rcu_state *rsp;trace_rcu_utilization("Start RCU core");for_each_rcu_flavor(rsp)__rcu_process_callbacks(rsp);trace_rcu_utilization("End RCU core");}
static void__rcu_process_callbacks(struct rcu_state *rsp){unsigned long flags;struct rcu_data *rdp = __this_cpu_ptr(rsp->rda);WARN_ON_ONCE(rdp->beenonline == 0);/* * 如果一个宽限期运行了很长时间,那么强制静止状态。 * */if (ULONG_CMP_LT(ACCESS_ONCE(rsp->jiffies_force_qs), jiffies))force_quiescent_state(rsp, 1);/* * 处理宽限期结束相关内容。 */rcu_process_gp_end(rsp, rdp);/* 检测是否有新的宽限期开始或者静止状态需要向上报告。 */rcu_check_quiescent_state(rsp, rdp);/* 当前CPU需要新的宽限期吗? */if (cpu_needs_another_gp(rsp, rdp)) {raw_spin_lock_irqsave(&rcu_get_root(rsp)->lock, flags);rcu_start_gp(rsp, flags); /* releases above lock */}/* 如果有等着调用的回调函数,那么调用它。 */if (cpu_has_callbacks_ready_to_invoke(rdp))invoke_rcu_callbacks(rsp, rdp);}
软件中断其实就是调用之前提到过的函数来完成具体的任务。