第12章: MMU notifier内核演进与未来方向

12.1 历史演进

12.1.1 mmu_notifier诞生(2008年)

原始mmu_notifier设计(2008年引入):

  • Copyright © 2008 Qumranet, Inc.
  • Copyright © 2008 SGI
  • Author: Christoph Lameter cl@linux.com

早期设计目标 :

  1. 为KVM虚拟化提供shadow page table同步机制
  2. 支持RDMA内存注册失效通知
  3. 为未来异构内存管理奠定基础

初始实现特点:

  • 全局通知模型(通知整个mm)
  • 简单的链表结构
  • 回调驱动的事件模型

/* 早期mmu_notifier_ops结构(简化版) */
struct mmu_notifier_ops_v1 {
	/* 页表清除时通知 */
	void (*invalidate_page)(struct mmu_notifier *mn,
				struct mm_struct *mm,
				unsigned long address);
	/* 范围失效开始 */
	void (*invalidate_range_start)(struct mmu_notifier *mn,
				       struct mm_struct *mm,
				       unsigned long start,
				       unsigned long end);
	/* 范围失效结束 */
	void (*invalidate_range_end)(struct mmu_notifier *mn,
				     struct mm_struct *mm,
				     unsigned long start,
				     unsigned long end);
	/* 进程退出时通知 */
	void (*release)(struct mmu_notifier *mn,
			struct mm_struct *mm);
};
/*
 * 早期限制:
 * - 无法精确跟踪特定地址范围
 * - 回调粒度粗(整个mm级别)
 * - 性能开销大(需要扫描全部映射)
 * - 并发控制简单
 */

12.1.2 改进阶段(2010-2018年)

关键改进点:
2010年: 引入invalidate_range()
– 在页表锁外通知
– 提高并发性

2012年: 改进阻塞语义
– 引入non-blocking标志
– 支持trylock模式

2014年: 添加clear_flush_young()
– 优化页面老化
– 减少TLB刷新开销

2015年: 引入mmu_notifier_range
– 统一事件描述
– 添加事件类型标志

2016年: 优化锁机制
– 引入SRCU (Sleepable RCU)
– 减少锁竞争

/* 改进后的mmu_notifier_range */
struct mmu_notifier_range {
	struct mm_struct *mm;
	unsigned long start;
	unsigned long end;
	unsigned flags;
	enum mmu_notifier_event event;  // 新增事件类型
	void *owner;                     // 新增owner字段
};
/* 事件类型枚举 */
enum mmu_notifier_event {
	MMU_NOTIFY_UNMAP = 0,
	MMU_NOTIFY_CLEAR,
	MMU_NOTIFY_PROTECTION_VMA,
	MMU_NOTIFY_PROTECTION_PAGE,
	MMU_NOTIFY_SOFT_DIRTY,
	MMU_NOTIFY_RELEASE,
	MMU_NOTIFY_MIGRATE,
	MMU_NOTIFY_DEVICE_EXCLUSIVE,
	MMU_NOTIFY_EXCLUSIVE,
};
/*
 * 性能优化:
 * - 减少不必要的回调
 * - 更细粒度的事件分类
 * - 支持设备私有页
 */

12.1.3 mmu_interval_notifier革命(2019年)

2019年重大突破: 引入mmu_interval_notifier
动机:

  1. GPU驱动需要跟踪大量小区间
  2. 全局扫描开销太大
  3. 需要更精确的失效通知
  4. HMM需要高效的range跟踪

/* mmu_interval_notifier核心设计 */
struct mmu_interval_notifier {
	struct interval_tree_node interval_tree;  // interval tree节点
	const struct mmu_interval_notifier_ops *ops;
	struct mm_struct *mm;
	struct hlist_node deferred_item;
	unsigned long invalidate_seq;              // 序列号同步
};
/* interval tree优势 */
/*
 * 数据结构: 红黑树 + 区间查询
 * 复杂度: O(log N + K)
 *   - N: 总区间数
 *   - K: 重叠区间数
 * 
 * 对比传统方案:
 *   传统: O(N) - 扫描所有notifier
 *   新方案: O(log N + K) - 只查询相关区间
 * 
 * 性能提升:
 *   100个区间: 100x -> 7x (14倍提升)
 *   1000个区间: 1000x -> 10x (100倍提升)
 *   10000个区间: 10000x -> 14x (700倍提升)
 */
/* 序列号同步机制 */
/*
 * 创新点: 无锁冲突检测
 * 
 * 序列号协议:
 *   偶数 = 空闲状态
 *   奇数 = 正在进行invalidate
 * 
 * 使用模式:
 *   seq = read_begin()      // 记录起始序列号
 *   ... 访问页表 ...
 *   if (read_retry(seq))    // 检测冲突
 *       retry;              // 重试
 * 
 * 优势:
 *   - 无需持锁检查
 *   - 高并发友好
 *   - 适合长时间操作
 */

12.1.4 双状态优化(2020年)

2020年优化: 双状态invalidate机制
问题: 即使没有interval notifier,也要操作interval tree
解决: 区分两种invalidate状态

// mm/mmu_notifier.c
struct mmu_notifier_subscriptions {
	struct hlist_head list;
	bool has_itree;                           // 是否有interval notifier
	spinlock_t lock;
	unsigned long invalidate_seq;             // 序列号
	unsigned long active_invalidate_ranges;   // 活跃invalidate计数
	struct rb_root_cached itree;              // interval tree
	wait_queue_head_t wq;
	struct hlist_head deferred_list;
};
/*
 * 状态机设计:
 * 
 * 完全排他状态 (Full Exclusion):
 *   - invalidate_seq & 1 == 1 (奇数)
 *   - 不允许修改interval tree
 *   - 用于遍历回调期间
 * 
 * 部分排他状态 (Partial Exclusion):
 *   - invalidate_seq & 1 == 0 (偶数)
 *   - 允许修改interval tree
 *   - 用于无interval notifier场景
 * 
 * 优化效果:
 *   无interval notifier时避免昂贵的tree加锁
 *   减少invalidate_end开销
 */
/* 状态检查函数 */
static bool
mn_itree_is_invalidating(struct mmu_notifier_subscriptions *subscriptions)
{
	lockdep_assert_held(&subscriptions->lock);
	return subscriptions->invalidate_seq & 1;  // 奇数=正在invalidate
}
/*
 * 性能提升:
 * - KVM场景: 10-20% invalidate_end开销降低
 * - GPU场景: 15-30% 整体性能提升
 */

12.2 当前内核版本改进

12.2.1 6.x内核特性

Linux 6.0+ 主要改进:

  1. 统一的mmu_notifier_range

    • 所有回调使用相同参数
    • 简化驱动实现
  2. 改进的死锁检测

    • lockdep注解增强
    • 运行时验证
  3. 设备私有页支持

    • DEVICE_PRIVATE页类型
    • 设备独占模式
/* 当前mmu_notifier_ops完整版 */
struct mmu_notifier_ops {
	/* 释放通知 */
	void (*release)(struct mmu_notifier *subscription,
			struct mm_struct *mm);
	/* 范围失效 */
	int (*invalidate_range_start)(struct mmu_notifier *subscription,
				      const struct mmu_notifier_range *range);
	void (*invalidate_range_end)(struct mmu_notifier *subscription,
				     const struct mmu_notifier_range *range);
	/* 页面老化 */
	int (*clear_flush_young)(struct mmu_notifier *subscription,
				 struct mm_struct *mm,
				 unsigned long start,
				 unsigned long end);
	int (*clear_young)(struct mmu_notifier *subscription,
			   struct mm_struct *mm,
			   unsigned long start,
			   unsigned long end);
	int (*test_young)(struct mmu_notifier *subscription,
			  struct mm_struct *mm,
			  unsigned long address);
	/* 页变脏通知 */
	void (*change_pte)(struct mmu_notifier *subscription,
			   struct mm_struct *mm,
			   unsigned long address,
			   pte_t pte);
	/* 辅助TLB失效 */
	void (*arch_invalidate_secondary_tlbs)(
				struct mmu_notifier *subscription,
				struct mm_struct *mm,
				unsigned long start,
				unsigned long end);
};
/* interval notifier完整版 */
struct mmu_interval_notifier_ops {
	bool (*invalidate)(struct mmu_interval_notifier *interval_sub,
			   const struct mmu_notifier_range *range,
			   unsigned long cur_seq);
};

12.2.2 SRCU优化

// mm/mmu_notifier.c
/*
 * SRCU (Sleepable Read-Copy Update) 使用
 * 
 * 优势:
 * - 允许回调中阻塞
 * - 更好的可扩展性
 * - 减少锁争用
 */
/* 全局SRCU域 */
DEFINE_STATIC_SRCU(srcu);
/* SRCU保护的遍历 */
static int mn_hlist_invalidate_range_start(
		struct mmu_notifier_subscriptions *subscriptions,
		struct mmu_notifier_range *range)
{
	struct mmu_notifier *subscription;
	int ret = 0;
	int id;
	/* SRCU读侧临界区 */
	id = srcu_read_lock(&srcu);
	hlist_for_each_entry_rcu(subscription, &subscriptions->list,
				 hlist, srcu_read_lock_held(&srcu)) {
		if (subscription->ops->invalidate_range_start) {
			ret = subscription->ops->invalidate_range_start(
					subscription, range);
			if (ret)
				break;
		}
	}
	srcu_read_unlock(&srcu, id);
	return ret;
}
/*
 * SRCU vs RCU对比:
 * 
 * RCU:
 *   - 读侧不能阻塞
 *   - 更快的读性能
 *   - 适合短临界区
 * 
 * SRCU:
 *   - 读侧可以阻塞
 *   - 稍慢的读性能
 *   - 适合长临界区
 *   - 更好的CPU隔离
 * 
 * MMU notifier选择SRCU原因:
 *   - 回调可能需要等待硬件
 *   - 可能需要获取锁
 *   - 操作时间不确定
 */

12.2.3 Lockdep集成

// mm/mmu_notifier.c
#ifdef CONFIG_LOCKDEP
/* lockdep注解 */
struct lockdep_map __mmu_notifier_invalidate_range_start_map = {
	.name = "mmu_notifier_invalidate_range_start"
};
#endif
/*
 * Lockdep检测场景:
 * 
 * 1. 锁顺序违反
 *    invalidate回调中不当的锁操作
 * 
 * 2. 递归invalidate
 *    回调中触发新的page fault
 * 
 * 3. 死锁路径
 *    与GUP或其他mm锁的冲突
 */
/* 使用示例 */
int __mmu_notifier_invalidate_range_start(struct mmu_notifier_range *range)
{
	struct mm_struct *mm = range->mm;
	int ret;
	/* Lockdep检查点 */
	lock_map_acquire(&__mmu_notifier_invalidate_range_start_map);
	/* 执行invalidate */
	ret = mn_itree_invalidate(mm->notifier_subscriptions, range);
	ret = mn_hlist_invalidate_range_start(mm->notifier_subscriptions, range);
	lock_map_release(&__mmu_notifier_invalidate_range_start_map);
	return ret;
}
/*
 * 调试技巧:
 * 
 * 启用lockdep:
 *   CONFIG_LOCKDEP=y
 *   CONFIG_PROVE_LOCKING=y
 *   CONFIG_LOCK_STAT=y
 * 
 * 查看锁统计:
 *   cat /proc/lock_stat
 * 
 * 追踪锁依赖:
 *   echo 1 > /proc/sys/kernel/lock_stat
 */

12.3 未来发展方向

12.3.1 硬件加速趋势

新硬件特性:

  1. ATS (Address Translation Services)

    • PCIe设备直接查询CPU页表
    • 减少软件同步开销
    • 硬件自动失效
  2. PASID (Process Address Space ID)

    • 多进程地址空间隔离
    • IOMMU原生支持
    • 更精确的TLB管理
  3. CCIX/CXL (Cache Coherent Interconnect)

    • 硬件缓存一致性
    • 减少软件通知需求
    • 更低延迟
/* 未来API设计考虑 */
struct mmu_notifier_ops_future {
	/* 支持硬件加速路径 */
	bool (*supports_hardware_invalidate)(struct mmu_notifier *mn);
	/* PASID支持 */
	int (*bind_pasid)(struct mmu_notifier *mn, u32 pasid);
	void (*unbind_pasid)(struct mmu_notifier *mn, u32 pasid);
	/* 批量操作 */
	int (*invalidate_ranges)(struct mmu_notifier *mn,
				 struct mmu_notifier_range *ranges,
				 unsigned int count);
};
/*
 * 优化方向:
 * - 硬件感知的通知路径
 * - 自适应批量处理
 * - 零拷贝数据传输
 */

12.3.2 异构内存管理增强

HMM未来演进:

  1. 统一内存架构

    • CPU/GPU/加速器共享地址空间
    • 透明页面迁移
    • 自动负载平衡
  2. 多层内存支持

    • DRAM + NVM + Device Memory
    • 自动分层策略
    • 性能优先放置
  3. 细粒度保护

    • 子页粒度保护
    • 灵活的访问权限
    • 硬件加速检查
/* 概念性未来接口 */
struct hmm_range_v2 {
	struct mmu_interval_notifier *notifier;
	unsigned long notifier_seq;
	/* 多段支持 */
	struct {
		unsigned long start;
		unsigned long end;
		unsigned long *hmm_pfns;
	} segments[HMM_MAX_SEGMENTS];
	unsigned int nr_segments;
	/* 扩展标志 */
	unsigned long flags;
#define HMM_RANGE_MULTI_DEVICE  BIT(0)  // 多设备协同
#define HMM_RANGE_PREFAULT      BIT(1)  // 预先fault
#define HMM_RANGE_ASYNC         BIT(2)  // 异步处理
	/* 设备列表 */
	void **devices;
	unsigned int nr_devices;
};
/*
 * 研究方向:
 * - 机器学习驱动的页面放置
 * - 预测性预取
 * - 自适应迁移策略
 */

12.3.3 性能优化方向

持续优化目标:

  1. 减少延迟

    • 更快的interval tree查询
    • 无锁快速路径
    • 批量处理优化
  2. 提高并发

    • Per-CPU数据结构
    • 细粒度锁
    • Lock-free算法
  3. 降低内存开销

    • 压缩interval tree
    • 共享公共数据
    • 按需分配
/* 可能的优化实现 */
/* Per-CPU interval tree */
struct per_cpu_itree {
	struct rb_root_cached tree;
	spinlock_t lock;
} ____cacheline_aligned;
/* 分区设计 */
struct partitioned_notifier_subscriptions {
	/* 全局notifier(保持向后兼容) */
	struct hlist_head global_list;
	/* Per-CPU interval tree */
	struct per_cpu_itree __percpu *per_cpu_trees;
	/* 统计信息 */
	atomic64_t total_intervals;
	atomic64_t invalidate_count;
};
/*
 * 预期效果:
 * - 50%+ 并发性能提升
 * - 30%+ 延迟降低
 * - 20%+ 内存节省
 */

12.3.4 标准化和协同

跨子系统协作:

  1. 与io_uring集成

    • 异步invalidate通知
    • 批量I/O优化
    • 零拷贝路径
  2. 与eBPF集成

    • 可编程策略
    • 运行时调优
    • 监控和追踪
  3. 与cgroup集成

    • 资源限制
    • QoS保证
    • 隔离性增强
/* 概念性eBPF程序 */
/*
SEC("mmu_notifier/invalidate")
int mmu_notifier_invalidate_prog(struct mmu_notifier_range *range)
{
	// 用户定义的策略
	if (should_skip_invalidate(range))
		return 0;
	// 自定义预处理
	preprocess_range(range);
	// 继续标准处理
	return 1;
}
*/
/*
 * 标准化工作:
 * - 跨架构API统一
 * - 设备驱动最佳实践
 * - 性能基准测试
 * - 互操作性指南
 */

12.4 社区讨论热点

活跃提案:

  1. 异步invalidate通知

    • 允许延迟处理
    • 减少阻塞时间
    • 提高响应性
  2. 细粒度事件过滤

    • 只接收相关事件
    • 减少噪音通知
    • 优化性能
  3. 统计和监控API

    • 标准化性能计数器
    • 运行时诊断接口
    • 问题定位工具

12.4.1 当前提案

/* 提案: 异步invalidate */
struct mmu_notifier_ops_async {
	/* 同步路径(快速) */
	int (*invalidate_range_start_fast)(
			struct mmu_notifier *subscription,
			const struct mmu_notifier_range *range);
	/* 异步路径(慢速) */
	void (*invalidate_range_start_async)(
			struct mmu_notifier *subscription,
			const struct mmu_notifier_range *range,
			struct work_struct *work);
};
/* 提案: 事件过滤 */
struct mmu_notifier_filter {
	unsigned long event_mask;     // 感兴趣的事件
	unsigned long addr_min;       // 地址范围最小值
	unsigned long addr_max;       // 地址范围最大值
	bool (*should_notify)(const struct mmu_notifier_range *range);
};

12.4.2 争议点

社区争议:

  1. 性能 vs 正确性

    • 延迟通知的安全性
    • 弱序内存模型影响
    • Race condition风险
  2. 复杂度 vs 灵活性

    • API简单性
    • 驱动实现难度
    • 维护成本
  3. 通用性 vs 特化

    • 为特定硬件优化
    • 保持架构中立
    • 平衡取舍

12.5 小结

MMU notifier演进轨迹:

  1. 2008年: 基础框架,全局通知
  2. 2010-2018年: 渐进改进,优化性能
  3. 2019年: interval notifier革命性突破
  4. 2020年+: 双状态优化,持续演进

未来方向:

  • 硬件加速集成
  • 异构内存统一管理
  • 性能持续优化
  • 跨子系统协作

关键趋势:

  • 从全局到局部(interval tree)
  • 从同步到异步(可能的方向)
  • 从软件到硬件(ATS/PASID)
  • 从单一到协同(HMM/CXL)

本专栏到此已完结。如果内核有upstream,会及时更新,请持续关注。


🔗 导航

  • 上一章: 第11章: 性能分析与调优
  • 附录A:API快速参考
  • 返回目录: Linux MMU Notifier 机制与应用系列目录
© 版权声明

相关文章