首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

Linux 2.6内核中新的锁机制--RCU(4)RCU 典型应用2

Linux 2.6内核中新的锁机制--RCU(4)RCU 典型应用2

下面部分将就 RCU 的几种典型应用情况详细讲解。
1.只有增加和删除的链表操作
在这种应用情况下,绝大部分是对链表的遍历,即读操作,而很少出现的写操作只有增加或删除链表项,并没有对链表项的修改操作,这种情况使用RCU非常容易,从rwlock转换成RCU非常自然。路由表的维护就是这种情况的典型应用,对路由表的操作,绝大部分是路由表查询,而对路由表的写操作也仅仅是增加或删除,因此使用RCU替换原来的rwlock顺理成章。系统调用审计也是这样的情况。
这是一段使用rwlock的系统调用审计部分的读端代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static enum audit_state audit_filter_task(struct task_struct *tsk)
{
         struct audit_entry *e;
         enum audit_state   state;
         read_lock(&auditsc_lock);
         /* Note: audit_netlink_sem held by caller. */
         list_for_each_entry(e, &audit_tsklist, list) {
                 if (audit_filter_rules(tsk, &e->rule, NULL, &state)) {
                         read_unlock(&auditsc_lock);
                         return state;
                 }
         }
         read_unlock(&auditsc_lock);
         return AUDIT_BUILD_CONTEXT;
}




使用RCU后将变成:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static enum audit_state audit_filter_task(struct task_struct *tsk)
{
         struct audit_entry *e;
         enum audit_state   state;
         rcu_read_lock();
         /* Note: audit_netlink_sem held by caller. */
         list_for_each_entry_rcu(e, &audit_tsklist, list) {
                 if (audit_filter_rules(tsk, &e->rule, NULL, &state)) {
                         rcu_read_unlock();
                         return state;
                 }
         }
         rcu_read_unlock();
         return AUDIT_BUILD_CONTEXT;
}




这种转换非常直接,使用rcu_read_lock和rcu_read_unlock分别替换read_lock和read_unlock,链表遍历函数使用_rcu版本替换就可以了。
使用rwlock的写端代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
static inline int audit_del_rule(struct audit_rule *rule,
                                  struct list_head *list)
{
         struct audit_entry  *e;
         write_lock(&auditsc_lock);
         list_for_each_entry(e, list, list) {
                 if (!audit_compare_rule(rule, &e->rule)) {
                         list_del(&e->list);
                         write_unlock(&auditsc_lock);
                         return 0;
                 }
         }
         write_unlock(&auditsc_lock);
         return -EFAULT;         /* No matching rule */
}
static inline int audit_add_rule(struct audit_entry *entry,
                                  struct list_head *list)
{
         write_lock(&auditsc_lock);
         if (entry->rule.flags & AUDIT_PREPEND) {
                 entry->rule.flags &= ~AUDIT_PREPEND;
                 list_add(&entry->list, list);
         } else {
                 list_add_tail(&entry->list, list);
         }
         write_unlock(&auditsc_lock);
         return 0;
}




使用RCU后写端代码变成为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
static inline int audit_del_rule(struct audit_rule *rule,
                                  struct list_head *list)
{
         struct audit_entry  *e;
         /* Do not use the _rcu iterator here, since this is the only
          * deletion routine. */
         list_for_each_entry(e, list, list) {
                 if (!audit_compare_rule(rule, &e->rule)) {
                         list_del_rcu(&e->list);
                         call_rcu(&e->rcu, audit_free_rule, e);
                         return 0;
                 }
         }
         return -EFAULT;         /* No matching rule */
}
static inline int audit_add_rule(struct audit_entry *entry,
                                  struct list_head *list)
{
         if (entry->rule.flags & AUDIT_PREPEND) {
                 entry->rule.flags &= ~AUDIT_PREPEND;
                 list_add_rcu(&entry->list, list);
         } else {
                 list_add_tail_rcu(&entry->list, list);
         }
         return 0;
}




对于链表删除操作,list_del替换为list_del_rcu和call_rcu,这是因为被删除的链表项可能还在被别的读者引用,所以不能立即删除,必须等到所有读者经历一个quiescent state才可以删除。另外,list_for_each_entry并没有被替换为list_for_each_entry_rcu,这是因为,只有一个写者在做链表删除操作,因此没有必要使用_rcu版本。
通常情况下,write_lock和write_unlock应当分别替换成spin_lock和spin_unlock,但是对于只是对链表进行增加和删除操作而且只有一个写者的写端,在使用了_rcu版本的链表操作API后,rwlock可以完全消除,不需要spinlock来同步读者的访问。对于上面的例子,由于已经有audit_netlink_sem被调用者保持,所以spinlock就没有必要了。
这种情况允许修改结果延后一定时间才可见,而且写者对链表仅仅做增加和删除操作,所以转换成使用RCU非常容易。
2.写端需要对链表条目进行修改操作
如果写者需要对链表条目进行修改,那么就需要首先拷贝要修改的条目,然后修改条目的拷贝,等修改完毕后,再使用条目拷贝取代要修改的条目,要修改条目将被在经历一个grace period后安全删除。
对于系统调用审计代码,并没有这种情况。这里假设有修改的情况,那么使用rwlock的修改代码应当如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static inline int audit_upd_rule(struct audit_rule *rule,
                                  struct list_head *list,
                                  __u32 newaction,
                                  __u32 newfield_count)
{
         struct audit_entry  *e;
         struct audit_newentry *ne;
         write_lock(&auditsc_lock);
         /* Note: audit_netlink_sem held by caller. */
         list_for_each_entry(e, list, list) {
                 if (!audit_compare_rule(rule, &e->rule)) {
                         e->rule.action = newaction;
                         e->rule.file_count = newfield_count;
                         write_unlock(&auditsc_lock);
                         return 0;
                 }
         }
         write_unlock(&auditsc_lock);
         return -EFAULT;         /* No matching rule */
}




如果使用RCU,修改代码应当为;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static inline int audit_upd_rule(struct audit_rule *rule,
                                   struct list_head *list,
                                   __u32 newaction,
                                   __u32 newfield_count)
  {
          struct audit_entry  *e;
          struct audit_newentry *ne;
          list_for_each_entry(e, list, list) {
                  if (!audit_compare_rule(rule, &e->rule)) {
                          ne = kmalloc(sizeof(*entry), GFP_ATOMIC);
                          if (ne == NULL)
                                  return -ENOMEM;
                          audit_copy_rule(&ne->rule, &e->rule);
                          ne->rule.action = newaction;
                          ne->rule.file_count = newfield_count;
                          list_replace_rcu(e, ne);
                          call_rcu(&e->rcu, audit_free_rule, e);
                          return 0;
                  }
          }
          return -EFAULT;         /* No matching rule */
  }

返回列表