经过之前的分析基本清楚了整个RCU的结构体,下面就通过分代码来解读RCU的实现。
void synchronize_rcu(void)
{
if (!rcu_scheduler_active)
return;
wait_rcu_gp(call_rcu);
}
当rcu_scheduler_active变量将会在第一个任务创建之前由0转换为1.当这个变量等于0的时候,RCU可以假设当前系统中仅仅存在一个任务。这样进行RCU等待就没有必要,所以通过这个变量对初始化时RCU队列进行优化,当这个变量转换为1时,表明系统已经准备好探测真正的grace period。顺便提一下,这里之所以从synchronize_rcu函数开始,是因为这个函数最后会调用call_rcu,而这个函数的指针则是作为参数传递到wait_rcu_gp中。
void wait_rcu_gp(call_rcu_func_t crf)
{
struct rcu_synchronize rcu;
init_rcu_head_on_stack(&rcu.head);
init_completion(&rcu.completion);
crf(&rcu.head, wakeme_after_rcu);
wait_for_completion(&rcu.completion);
destroy_rcu_head_on_stack(&rcu.head);
}
Wait_rcu_gp函数的实现如上图所示,首先从栈中分配一个结构体,这个阶梯就是中包含rcu的实现——一个队列指针,一个函数指针,已经一个同步等待的结构体(如果是异步rcu则没有同步等待结构体)。在初始化之后就转入到调用异步的call_rcu函数中,然后进行等待操作以及对堆栈上的RCU对象进行清理。先看一下wait_for_completion。
void wait_for_completion(struct completion *x)
{
wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_UNINTERRUPTIBLE);
}
从wait_for_completion函数的实现可以看出,不能在x上进行唤醒操作。则当前函数将会无限等待下去(MAX_SCHEDULE_TIMEOUT)而不会被信号打断(TASK_UNINTERRUPTIBLE)。
static void wakeme_after_rcu(struct rcu_head *head)
{
struct rcu_synchronize *rcu;
rcu = container_of(head, struct rcu_synchronize, head);
complete(&rcu->completion);
}
再看看wakeme_after_rcu函数的实现,这个函数的实现仅仅唤醒当前正在等待的任务而不会释放内存,因为内存在堆栈中。
void call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu))
{
__call_rcu(head, func, &rcu_preempt_state);
}
static void
__call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu),
struct rcu_state *rsp)
{
unsigned long flags;
struct rcu_data *rdp;
head->func = func;
head->next = NULL;
smp_mb();
local_irq_save(flags);
rdp = this_cpu_ptr(rsp->rda);
*rdp->nxttail[RCU_NEXT_TAIL] = head;
rdp->nxttail[RCU_NEXT_TAIL] = &head->next;
rdp->qlen++;
if (irqs_disabled_flags(flags)) {
local_irq_restore(flags);
return;
}
if ((rdp->qlen > rdp->qlen_last_fqs_check + qhimark)) {
rcu_process_gp_end(rsp, rdp);
check_for_new_grace_period(rsp, rdp);
if (!rcu_gp_in_progress(rsp)) {
unsigned long nestflag;
struct rcu_node *rnp_root = rcu_get_root(rsp);
raw_spin_lock_irqsave(&rnp_root->lock, nestflag);
rcu_start_gp(rsp, nestflag); /* rlses rnp_root->lock */
} else {
rdp->blimit = LONG_MAX;
if (rsp->n_force_qs == rdp->n_force_qs_snap &&
*rdp->nxttail[RCU_DONE_TAIL] != head)
force_quiescent_state(rsp, 0);
rdp->n_force_qs_snap = rsp->n_force_qs;
rdp->qlen_last_fqs_check = rdp->qlen;
}
}
else if (ULONG_CMP_LT(rsp->jiffies_force_qs, jiffies))
force_quiescent_state(rsp, 1);
local_irq_restore(flags);
}
上面是call_rcu的具体实现,不过这个实现只包括一部分。因