读书人

TREE RCU兑现之三 定期调用

发布时间: 2012-12-16 12:02:32 作者: rapoo

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);}


软件中断其实就是调用之前提到过的函数来完成具体的任务。

读书人网 >其他相关

热点推荐