RCU-5——RCU案例汇总
基于 Linux-5.10
一、经典(可抢占)RCU
1. 例子-RCU链表
假设链表节点和头结点如下:
typedef struct { struct list_head link; struct rcu_head rcu; //used for call_rcu() int key; int val; } test_entry; struct list_head test_head;
读者访问链表方法如下:
int test_read(int key, int *val_ptr) { test_entry *entry; int found = 0; rcu_read_lock(); list_for_each_entry_rcu(entry, &test_head, link) { if (entry->key == key) { *val_ptr = entry->val; found = 1; break; } } rcu_read_unlock(); return found; }
1.1. 如果只有一个写者,那么写者是不需要使用锁进行保护的,添加、更新、删除的操作实现方法如下:
(1) 写者添加一个节点到链表尾部
void test_add_node(test_entry *entry) { list_add_tail_rcu(&entry->link, &test_head); }
(2) 写者更新一个节点
更新的过程是:首先把旧的节点复制更新,然后使用新节点替换旧节点,最后使用函数 call_rcu() 注册回调函数,延后释放旧节点。
void test_update_node(int key, int new_val) { test_entry *entry, *new_entry; int ret = -ENOENT; list_for_each_entry(entry, &test_head, link) { if (entry->key == key) { new_entry = kmalloc(sizeof(test_entry), GFP_ATOMIC); if (new_entry == NULL) { ret = -ENOMEM; break; } *new_entry = *entry; new_entry->val = new_val; list_replace_rcu(&entry->link, &new_entry->link); call_rcu(&entry->rcu, test_free_node); ret = 0; break; } } return ret; } void test_free_node(struct rcu_head *head) { test_entry *entry = container_of(head, test_entry, rcu); kfree(entry); }
(3) 写者删除一个节点
第一种方法:首先将节点从链表中删除,然后使用函数 call_rcu() 注册回调函数延后释放节点。
int test_del_node(int key) { test_entry *entry; int found = 0; list_for_each_entry(entry, &test_head, link) { if (entry->key == key) { list_del_rcu(&entry->link); call_rcu(&entry->rcu, test_free_node); //不会休眠 found = 1; break; } } return found; }
第二种方法:首先把节点从链表中删除,然后使用 synchronize_rcu() 等待宽限期结束,最后释放节点。
int test_del_node(int key) { test_entry *entry; int found = 0; list_for_each_entry(entry, &test_head, link) { if (entry->key == key) { list_del_rcu(&entry->link); synchronize_rcu(); //会休眠 kfree(entry); found = 1; break; } } return found; }
1.2. 如果有多个写者,那么写者之间必须使用锁互斥,添加、更新、删除的操作实现方法如下:
(1) 写者添加一个节点到链表尾部,假设使用 spin_lock 保护链表,视情况也可以使用其它互斥锁。
void test_add_node(test_entry *entry) { spin_lock(&test_lock); //struct spinlock test_lock; list_add_tail_rcu(&entry->link, &test_head); spin_unlock(&test_lock); }
(2) 写者更新一个节点
void test_update_node(int key, int new_val) { test_entry *entry, *new_entry; int ret = -ENOENT; spin_lock(&test_lock); list_for_each_entry(entry, &test_head, link) { if (entry->key == key) { new_entry = kmalloc(sizeof(test_entry), GFP_ATOMIC); if (new_entry == NULL) { ret = -ENOMEM; break; } *new_entry = *entry; new_entry->val = new_val; list_replace_rcu(&entry->link, &new_entry->link); call_rcu(&entry->rcu, test_free_node); ret = 0; break; } } spin_unlock(&test_lock); return ret; }
(3) 写者删除一个节点
int test_del_node(int key) { test_entry *entry; int found = 0; spin_lock(&test_lock); list_for_each_entry(entry, &test_head, link) { if (entry->key == key) { list_del_rcu(&entry->link); call_rcu(&entry->rcu, test_free_node); //不会休眠 found = 1; break; } } spin_unlock(&test_lock); return found; }
使用 spin_lock 进行保护,释放就不能使用会导致睡眠的同步等待宽限期结束的 synchronize_rcu() 函数了。
二、加速RCU
TODO
三、可抢占RCU
TODO
四、可休眠RCU
TODO
posted on 2023-04-27 21:10 Hello-World3 阅读(153) 评论(0) 编辑 收藏 举报