QEMU FlatView:用 RCU 分离映射发布与回收
通过 RCU 发布新映射,并在引用归零、已有读者退出后回收旧视图。
1问题背景#
设备热插拔或内存映射变更会更新地址空间,而 vCPU 与 IOThread 还在读取映射。逐次保护 current_map 的互斥锁会让这些读者竞争;直接替换并释放又会留下悬空指针。
2原有方案#
历史实现用 flat_view_mutex 保护 current_map。Paolo Bonzini 的修改说明直接指出,多 IOThread 和大系统会在这里产生 futex 争用。
3设计方案#
将映射发布与内存回收分开:新视图准备完成后通过 RCU 指针发布;旧视图引用归零后,由 call_rcu 将销毁推迟到已有读者退出之后。
4改进分析#
读者在 RCU 临界区取得视图,无须为 current_map 竞争同一把互斥锁。引用计数负责显式持有的视图引用,RCU 保护临界区内尚未结束的读取,两者共同确定安全回收的时机。
5源码分析#
5.1RCU 读侧临界区#
在 RCU 临界区取 current_map,再沿着该视图执行读取。它不会在一次访问中主动切换成刚发布的另一份视图。
2942MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr,2943 MemTxAttrs attrs, void *buf, hwaddr len)2944{2945 MemTxResult result = MEMTX_OK;2946 FlatView *fv;29472948 if (len > 0) {2949 RCU_READ_LOCK_GUARD();2950 fv = address_space_to_flatview(as);2951 result = flatview_read(fv, addr, attrs, buf, len);2952 }29532954 return result;2955}读侧先进入 RCU 临界区,再取得当前 FlatView,并在同一临界区内完成读取。保护的是映射对象生命周期;设备回调和 RAM 内容仍有各自同步规则。
5.2新视图发布与区域回调#
在 BQL 保护下执行区域差异回调,再发布新指针;旧视图仍可能被读者使用,事务级 commit 在发布后执行。
1099 if (old_view) {1100 flatview_ref(old_view);1101 }11021103 flatview_ref(new_view);11041105 if (!QTAILQ_EMPTY(&as->listeners)) {1106 FlatView tmpview = { .nr = 0 }, *old_view2 = old_view;11071108 if (!old_view2) {1109 old_view2 = &tmpview;1110 }1111 address_space_update_topology_pass(as, old_view2, new_view, false);1112 address_space_update_topology_pass(as, old_view2, new_view, true);1113 }11141115 /* Writes are protected by the BQL. */1116 qatomic_rcu_set(&as->current_map, new_view);1117 if (old_view) {1118 flatview_unref(old_view);1119 }更新者持有 BQL,先持有新旧视图引用并调用 MemoryListener 的区域差异回调,再用 qatomic_rcu_set 发布新视图。事务级 commit 回调在所有 AddressSpace 更新后执行。旧视图的临时引用在完整函数末尾另行归还,不能把片段中的一次 unref 理解为全部生命周期处理。
5.3引用归零后的延迟回收#
引用计数归零只触发延迟回收,已有 RCU 读者退出后才运行销毁回调。
310void flatview_unref(FlatView *view)311{312 if (qatomic_fetch_dec(&view->ref) == 1) {313 trace_flatview_destroy_rcu(view, view->root);314 assert(view->root);315 call_rcu(view, flatview_destroy, rcu);316 }317}最后一个引用归还后,call_rcu 登记延迟销毁回调;回调在登记时已经存在的 RCU 读侧临界区全部结束后执行。普通引用计数和 RCU 各自保护不同的使用方式。
6适用范围与限制#
回收延迟会让新旧视图短期并存;写者仍由 BQL 串行化。映射安全不等于设备状态、RAM 内容或整个虚拟机操作都自动成为无锁快照。
固定于 QEMU v9.2.0 的 FlatView 生命周期。2015 年提交用于说明互斥锁对照方案与设计动机;未对该版本的性能进行实测。
7参考资料#
memory: protect current_map by RCU
互斥锁对照与动机来自原作者提交;性能收益未在本站实测。