博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
非抢占式RCU实现(二),解释:为什么 RCU_NEXT_SIZE 宏值是4?
阅读量:5978 次
发布时间:2019-06-20

本文共 4623 字,大约阅读时间需要 15 分钟。

参考:2.6.34

一个很奇怪的问题。

没有查找到为什么 RCU_NEXT_SIZE的值为4的原因(包括Documentation),主要是在rcu_state中定义了一个四级的list,感到很有意思。

我给出自己的解释。

还是看下代码吧,容易解释:

=========================================================

引入nxtlist与 RCU_NEXT_RCU_DONE_TAIL

static void rcu_do_batch(struct rcu_state *rsp, struct rcu_data *rdp)                                                  |---->if(!cpu_has_callbacks_ready_to_invoke(rdp))    |-----      return      |-----看下cpu_has_callbacks_ready_to_invoke的实现    |-----      |---- return &rdp->nxtlist !=     |-----      |-----       rdp->nxttail[RCU_DONE_TAIL];    |-----......    |----rdp->nxtlist = *rdp->nxttail[RCU_DONE_TAIL];    |-----......

  从rcu_do_batch的实现可以看出,首先rcu_do_batch在 RCU_SOFTIRQ的软中断是一定会被执行的,只是在入口处判定是否需要实质性地操作链表,如果 &rdp->nxtlist != rdp->nxttail[RCU_DONE_TAIL] 成立则不会继续运行,即该次软中断在rcu_do_batch中是不会执行实质性内容的(但是千万不要认为在该次软中断中,没有操作链表上的函数就没有执行关键任务,rcu_progress_gp_end, rcu_check_quiescent_state都在软中断被执行了)。

在rcu_data结构体中引入

nxtlistRCU_NEXT_RCU_DONE_TAIL的目的很清楚:
    nxtlist保存待处理的链表头,而*nxttail[RCU_NEXT_RCU_DONE_TAIL]中还保存着下次本核结束某个grace period时需要操作
的链表的头,如果两者相等,则进入rcu_do_batch后将立即退出。

=========================================================

=========================================================

引入RCU_NEXT_TAIL

static __call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu),                  struct rcu_state *rsp)   |----......   |----*rdp->nxttail[RCU_NEXT_TAIL] = head;   |----rdp->nxttail[RCU_NEXT_TAIL] = &head->next;   |----......

作用:缓存!(单链表,存储最后可插入节点的位置)

=========================================================

=========================================================

引入RCU_NEXT_READY_TAILRCU_NEXT_TAIL

这两个很容易混淆

static void __rcu_process_callbacks(struct rcu_state *rsp, struct rcu_data *rdp)    |----......    |----rcu_check_quiescent_state(rsp, rdp);         |----......         |         |通过了quiescent state,调用rcu_report_qs_rdp         |----rcu_report_qs_rdp(rdp->cpu, rsp, rdp, rdp->passed_quiesc_completed)              |----......              |----rdp->nxttail[RCU_NEXT_READY_TAIL] =              |----     rdp->nxttail[RCU_NEXT_TAIL]

通过了rcu_check_quiecent_state()中的检查,知道该核通过了quiescent state,因此设置rdp->nxttail[RCU_NEXT_READY_TAIL] = rdp->nxttail[RCU_NEXT_TAIL],而RCU_NEXT_TAIL是每次添加链表的缓存地址,因此可以判定RCU_NEXT_READY_TAIL是存储该核下次通过grace period时需被调用的链表头的地址(有点不妥,见下文)为什么是这样?虽然该核已经上报通过了quiescent state,但是该次grace period的运行结束是由最后一个通过quiescent state的所决定的,如果仍有其它核自开启该次grace period后没有通过quiescent state,那么只能返回,以后添加的的链表元数自然也就只能在下次grace period中被调用)。

不禁会问:RCU_NEXT_TAIL有什么用?

    先说明:本核上rcu_data中挂接的链表元素,只有两种情况可被处理
           (1)超时:

if(ULONG_CMP_LT(rsp->jiffies_force_qs), jiffies)                        force_quiescent_state(rsp, 1);

           (2)该核结束了由某个核开启的grace period:

rcu_node检测到该核是最后一个上报通过quiescent state的核.

    描述个场景:A核开启了grace period,B核在系统初始化后上挂了好几个链表节点,A、C、D核先后通过了quiescent state,B核最后通过quiescent state,则B核是最终结束该grace period的核,B核最终将在rcu_do_batch()中处理链表。

     问题:在于B核应该执行链表上的RCU_DONE_TAIL至RCU_NEXT_READY_TAIL的所有任务吗?

     回答:不能。

    原因:B核尽管在rcu_report_qs_rdp中执行了rdp->nxttail[RCU_NEXT_READY_TAIL] = rdp->nxttail[RCU_NEXT_TAIL],但是我们显然的看到由于该grace period并由非B核开启,因此我们无法判断在B核添加链表时其它核是否通过了quiescent state,如果其它核已经通过了quiescent state,对于此后在B核上挂接的链表,如果在该grace period被B核终止后即被调用处理显然是错的。

     如何解决:引入RCU_WAIT_TAIL

B核终止了一个非本身开启的grace period,而后:

rcu_report_qs_rnp(unsigned long mask, struct rcu_state *rsp,            struct rcu_node *rnp, unsigned long flags)    |----......    |----rcu_report_qs_rsp(rsp, flags);         |----......         |----rcu_start_gp(rsp, flags);         |    |----......         |    |----rcu_start_gp_per_cpu(rsp, rnp, rdp);         |    |    |----_rcu_process_gp_end(rsp, rnp, rdp);         |    |    |    |----......         |    |    |    |----rdp->nxttail[RCU_DONE_TAIL] =          |    |    |    |       rdp->nxttail[RCU_WAIT_TAIL];         |    |    |    |----rdp->nxttail[RCU_WAIT_TAIL] =          |    |    |    |       rdp->nxttail[RCU_NEXT_READY_TAIL];         |    |    |    |----rdp->nxttail[RCU_NEXT_READY_TAIL] =          |    |    |    |       rdp->nxttail[RCU_NEXT_TAIL];         |    |    |    |----.......         |    |    |----......         |    |----......         |----.......    |----......

  可以看出在rcu_process_per_cpu中置:rdp->nxttail[RCU_DONE_TAIL] = rdp->nxttail[RCU_WAIT_TAIL],而非rdp->nxttail[RCU_NEXT_READY_TAIL]。

  个人认为,这样做可以防止——某个核终止了并非由该核开启的grace period,而且该核在grace period时间段内在自己的链表中的添加了链表元素——在该次rcu_do_batch中,这些链表元素却被处理了。

=========================================================

关注TAIL指针的重点,在于

(1)主动开启grace period

(2)检测到有新的grace period,但是本核缺没有被动的开启grace period

 (3)检测grace period是否结束,及处理

(4)向rcu_node上报本核已发生quiescent state

在这4点处将推动TAIL指针的变化。

记住一点:某个核开启了grace period,但并不一定是该核来结束grace period,可能是自己,也可能是其它核。

转载地址:http://nwsox.baihongyu.com/

你可能感兴趣的文章
MariaDB 和 MySQL 性能测试比较
查看>>
Restful Web Service初识
查看>>
This用法和闭包
查看>>
JSP页面获取系统时间
查看>>
L-1-19 Linux之RAID&分区&文件系统命令
查看>>
stat查找权限以数字形式显示
查看>>
源码编译安装httpd2.4.9
查看>>
linux系统优化
查看>>
在使用 Windows Update 检查更新时,系统没有提供下载 Windows 7 SP1 的选项
查看>>
在Struts + Spring + Hibernate的组合框架模式中,三者各自的特点都是什么
查看>>
Windows 2012 R2 DataCenter服务器DNS无法打开AD, DNS错误代码4000 4007 4013
查看>>
java基础数据类型char
查看>>
打印 PE导入导出表
查看>>
miniWindbg 功能
查看>>
五、判断银行卡号的正则
查看>>
mysql基于mysqlslap的压力测试
查看>>
zencart中query_factory.php中连接mysql次数
查看>>
fail2ban 保护linux安全(转载)已用于生产环境
查看>>
表格元素的添加和删除,计算器,全选全不反选
查看>>
数据约束
查看>>