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

本文详细解析了RCU(Read-Copy Update)机制中的关键步骤,包括call_rcu的实现,宽限期的开始、检测和结束处理。重点讨论了如何将回调函数加入列表,启动和检测宽限期,以及在宽限期结束后处理回调函数的过程。通过这些内容,有助于深入理解RCU的工作原理。
摘要由CSDN通过智能技术生成

         RCU的实现集中在以下几个步骤:

         1, 调用call_rcu,将回调函数增加到列表。

         2,   开始一个宽限期。

         3,   每个CPU报告自己的状态,直到最后一个CPU,结束一个宽限期。

         4, 宽限期结束,每个CPU处理自己的回调函数。

call_rcu的实现  
static void
__call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu),
           struct rcu_state *rsp, bool lazy)
{
        unsigned long flags;
        struct rcu_data *rdp;

        WARN_ON_ONCE((unsigned long)head & 0x3); /* 检测head在内存中是否对齐! */
        debug_rcu_head_queue(head);
        head->func = func;
        head->next = NULL;

        smp_mb(); /* Ensure RCU update seen before callback registry. */

        /*
         * 这是一个检测宽限期开始或者结束的机会。
         * 当我们看到一个结束的时候,可能还会看到一个开始。
         * 反过来,看到一个开始的时候,不一定能看到一个结束,
         * 因为宽限期结束需要一定时间。
         */
        local_irq_save(flags);
        rdp = this_cpu_ptr(rsp->rda);

        /* 将要增加callback到nxtlist. */
        ACCESS_ONCE(rdp->qlen)++;
        if (lazy)
                rdp->qlen_lazy++;
        else
                rcu_idle_count_callbacks_posted();
        smp_mb();  /* Count before adding callback for rcu_barrier(). */
        *rdp->nxttail[RCU_NEXT_TAIL] = head;
        rdp->nxttail[RCU_NEXT_TAIL] = &head->next;

        if (__is_kfree_rcu_offset((unsigned long)func))
                trace_rcu_kfree_callback(rsp->name, head, (unsigned long)func,
                                         rdp->qlen_lazy, rdp->qlen);
        else
                trace_rcu_callback(rsp->name, head, rdp->qlen_lazy, rdp->qlen);

        /* 去处理rcu_core。 */
        __call_rcu_core(rsp, rdp, head, flags);
        local_irq_restore(flags);
}

         call_rcu中最主要的工作,就是将回调函数加入到CPU的nxtlist列表。这里用到了指针处理的小技巧,我们来看看。首先看看nxttail的初始化:

 

static void init_callback_list(struct rcu_data *rdp)
{
        int i;

        rdp->nxtlist = NULL;
        for (i = 0; i < RCU_NEXT_SIZE; i++)
                rdp->nxttail[i] = &rdp->nxtlist;
}

       我们看到nxttail的全部成员都指向了nxtlist的地址。当nxtlist为空的时候,也是这个情形。

       *rdp->nxttail[RCU_NEXT_TAIL] = head;       

       当nxtlist为空的时候, *rdp->nxttail[RCU_NEXT_TAIL] 得到的其实就是nxtlist,将head的值赋予它。

       rdp->nxttail[RCU_NEXT_TAIL] = &head->next;

       之后 RCU_NEXT_TAIL指向 head的next指针。这样当再有一个节点加入的时候,*rdp->nxttail[RCU_NEXT_TAIL]得到的其实就是前一次加入的head的next指针,它将指向新加入的值。如此,nxtlist就成为了一个链表。或者这样理解,rdp->nxttail[RCU_NEXT_TAIL] 指向的就是nxtlist中最后一个节点的 next指针。

       除了将回调函数插入,该函数其它代码多为检查代码。而最后要调用__call_rcu_core,该函数的功用主要是在回调函数太多或者等待时间过长的状态下,强制执行RCU状态更新。我们暂时不关注。

开始一个宽限期

       在一个宽限期结束,或者当一个CPU检测到自身有需要一个宽限期的时候会开始一个新的宽限期,开始宽限期的代码如下:

 

static void
rcu_start_gp(struct rcu_state *rsp, unsigned long flags)
        __releases(rcu_get_root(rsp)->lock)
{
        struct rcu_data *rdp = this_cpu_ptr(rsp->rda);
        struct rcu_node *rnp = rcu_get_root(rsp);

        if (!rcu_scheduler_fully_active ||
            !cpu_needs_another_gp(rsp, rdp)) {
                /*
                 * 如果scheduler 还没有启动non-idle任务
                 * 或者不需要启动一个新的宽限期则退出。
                 * 需要再次判断cpu_needs_another_gp,
                 * 是因为可能有多个CPU执行这个过程。
                 */
               raw_spin_unlock_irqrestore(&rnp->lock, flags);
               return;
        }

        if (rsp->fqs_active) {
                /*
                 * 这个CPU需要一个宽限期,而force_quiescent_state()
                 * 正在运行,告诉它开始一个。
                 */
                rsp->fqs_need_gp = 1;
                raw_spin_unlock_irqrestore(&rnp->lock, flags);
                return;
   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值