TREE RCU实现之三 —— 定期调用

原创 2012年12月07日 11:17:52

 

       上一节,介绍过了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);
}


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

 

 

 

 

相关文章推荐

RCU's CPU Stall Detector

有时候可以会打印INFO: rcu_bh_state detected stalls on CPUs/tasks: { } (detected by 4, 2502 jiffies) 类似的log。这...

INFO: rcu_preempt_state detected stalls on CPUs/tasks:

Using RCU's CPU Stall Detector The rcu_cpu_stall_suppress module parameter enables RCU's CPU stall ...
  • myxmu
  • myxmu
  • 2012年10月01日 15:52
  • 8681

<深入浅出> linux内核 RCU (二)分级RCU

在前一篇文章里,分析过经典RCU的来历和实现: http://blog.csdn.net/chenyu105/article/details/7910269 经典RCU看完后,作为读者的你,有没有发...

linux rcu 理解

linux内核 RCU机制详解 参考资料: http://blog.csdn.net/xabc3000/article/details/15335131

指针和引用的定义和性质区别

指针是一个变量,只不过这个变量存储的是一个地址,指向内存的一个存储单元;而引用跟原来的变量实质上是同一个东西,只不过是原变量的一个别名而已。...

windows环境下unistd.h缺失解决方法

自建一个unistd.h 文件内容为#ifndef _UNISTD_H#define _UNISTD_H #include #include #endif /* _UNISTD_H */ 当然,...

TREE RCU实现之一 —— 数据结构

代码分布         在分析代码之前, 先看看代码的分布情况。RCU实现的代码包含在下列一些文件中,此处用到的是linux 3.6.4的代码。                  RCU实现的...
  • junguo
  • junguo
  • 2012年12月05日 02:36
  • 8097

TREE RCU实现之二 —— 主干函数

RCU的实现集中在以下几个步骤:          1, 调用call_rcu,将回调函数增加到列表。          2,   开始一个宽限期。          3,   每个CPU报告自...
  • junguo
  • junguo
  • 2012年12月06日 18:36
  • 5701
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:TREE RCU实现之三 —— 定期调用
举报原因:
原因补充:

(最多只允许输入30个字)