Skip to the content.

qemu rcu

继续看看文档再说吧

经典案例

RAMList::dirty_memory

typedef struct RAMList {
    // ...
    DirtyMemoryBlocks *dirty_memory[DIRTY_MEMORY_NUM];
    // ...
}

从 writer 的角度分析,做了两件事情

static void dirty_memory_extend(ram_addr_t old_ram_size,
                                ram_addr_t new_ram_size){
        new_blocks = g_malloc(sizeof(*new_blocks) +
                              sizeof(new_blocks->blocks[0]) * new_num_blocks);
        qatomic_rcu_set(&ram_list.dirty_memory[i], new_blocks);

        g_free_rcu(old_blocks, rcu);
}
void call_rcu1(struct rcu_head *node, void (*func)(struct rcu_head *node))
{
    node->func = func;
    enqueue(node);
    qatomic_inc(&rcu_call_count);
    qemu_event_set(&rcu_call_ready_event);
}

推迟的时间当然是等待所有的 reader 都结束才可以。

再看 reader 这一侧,使用 cpu_physical_memory_get_dirty 作为例子:

static inline bool cpu_physical_memory_get_dirty(ram_addr_t start,
                                                 ram_addr_t length,
                                                 unsigned client)
{
    WITH_RCU_READ_LOCK_GUARD() {
      // 访问
    }
    return dirty;
}

这里还使用了 QLIST_NEXT_RCU ,也是经典位置了

virtioqueue

void virtqueue_push(VirtQueue *vq, const VirtQueueElement *elem,
                    unsigned int len)
{
    RCU_READ_LOCK_GUARD();
    virtqueue_fill(vq, elem, len, 0);
    virtqueue_flush(vq, 1);
}

应该配合使用的地方为:

virtio_init_region_cache

    qatomic_rcu_set(&vq->vring.caches, new);
    if (old) {
        call_rcu(old, virtio_free_region_cache, rcu);
    }

问题

call_rcu_thread 中,需要持有 lock 才可以释放资源,这很奇怪。既然都是可以开始来执行 hook 函数了, 说明这些资源已经是没有人使用的, 那么为什么还需要使用 BQL 保护。 其原因在: https://lists.gnu.org/archive/html/qemu-devel/2015-02/msg03170.html

原理

WITH_RCU_READ_LOCK_GUARD 会展开为:

- rcu_read_auto_lock
  - rcu_read_lock
    - `rcu_reader->ctr = rcu_gp_ctr->ctr` : 在进入的时候同步 global 的 ctr 到本地,这样如果 global 的发生变动了,那么就可以检测出来

// 中间进行访问

- rcu_read_auto_unlock
  - rcu_read_unlock
    - 如果检测到 rcu_reader::waiting 的话,`qemu_event_set(&rcu_gp_event);`,通知 call_rcu thread 有 reader 结束了

先总结一下关联到几个主要结构体:

名称 作用
rcu_gp_ctr 全局变量,用于标记当前的 period
rcu_reader 每一个线程的局部变量,当 reader 进入 critical reagion 的时候,会和 rcu_gp_ctr 同步
rcu_call_ready_event 在 call_rcu1 中用于通知 call_rcu thread 有垃圾可以回收了
rcu_gp_event 在 rcu_read_unlock 中用于通知 call_rcu thread 有 reader 结束了

reader 和 writer 都是和 call_rcu thread 来交互的:

本站所有文章转发 CSDN 将按侵权追究法律责任,其它情况随意。