内存热插拔¶
内存热插拔事件通知器¶
热插拔事件被发送到一个通知队列。
在 include/linux/memory.h
中定义了六种类型的通知
- MEM_GOING_ONLINE
在新内存可用之前生成,以便能够准备子系统来处理内存。页面分配器仍然无法从新内存中分配。
- MEM_CANCEL_ONLINE
如果 MEM_GOING_ONLINE 失败则生成。
- MEM_ONLINE
当内存成功上线时生成。回调可以从新内存中分配页面。
- MEM_GOING_OFFLINE
生成以开始使内存下线的进程。不再可能从内存中进行分配,但一些要下线的内存仍在被使用。回调可用于释放子系统从指示的内存块中已知的内存。
- MEM_CANCEL_OFFLINE
如果 MEM_GOING_OFFLINE 失败则生成。我们尝试下线的内存块中的内存再次可用。
- MEM_OFFLINE
在下线内存完成后生成。
可以通过调用注册回调例程
hotplug_memory_notifier(callback_func, priority)
具有较高优先级值的回调函数在具有较低值的回调函数之前调用。
回调函数必须具有以下原型
int callback_func(
struct notifier_block *self, unsigned long action, void *arg);
回调函数的第一个参数 (self) 是指向通知链块的指针,该块指向回调函数本身。第二个参数 (action) 是上面描述的事件类型之一。第三个参数 (arg) 传递一个指向 struct memory_notify 的指针
struct memory_notify {
unsigned long start_pfn;
unsigned long nr_pages;
int status_change_nid_normal;
int status_change_nid;
}
start_pfn 是上线/下线内存的起始 pfn。
nr_pages 是上线/下线内存的页数。
status_change_nid_normal 是当 nodemask 的 N_NORMAL_MEMORY 被(将要)设置/清除时的节点 ID,如果这是 -1,则 nodemask 状态不会更改。
status_change_nid 是当 nodemask 的 N_MEMORY 被(将要)设置/清除时的节点 ID。这意味着一个新(无内存)节点通过上线获得新内存,并且一个节点丢失了所有内存。如果这是 -1,则 nodemask 状态不会更改。
如果 status_changed_nid* >= 0,则回调应在必要时为节点创建/丢弃结构。
回调例程应返回 include/linux/notifier.h
中定义的 NOTIFY_DONE、NOTIFY_OK、NOTIFY_BAD、NOTIFY_STOP 值之一
NOTIFY_DONE 和 NOTIFY_OK 对进一步的处理没有影响。
NOTIFY_BAD 用作对 MEM_GOING_ONLINE、MEM_GOING_OFFLINE、MEM_ONLINE 或 MEM_OFFLINE 操作的响应,以取消热插拔。它会停止对通知队列的进一步处理。
NOTIFY_STOP 会停止对通知队列的进一步处理。
锁定内部机制¶
当添加/移除使用内存块设备(即普通 RAM)的内存时,应持有 device_hotplug_lock 以
与上线/下线请求同步(例如,通过 sysfs)。这样,一旦内存完全添加,用户空间只能访问内存块设备(.online/.state 属性)。当移除内存时,我们知道没有人处于临界区。
与 CPU 热插拔和类似操作同步(例如,与 ACPI 和 PPC 相关)
特别是,当添加内存并且用户空间尝试比预期更快地上线内存时,可能会发生使用 device_hotplug_lock 避免的潜在锁反转
device_online() 将首先获取 device_lock(),然后获取 mem_hotplug_lock
add_memory_resource() 将首先获取 mem_hotplug_lock,然后获取 device_lock()(在创建设备期间,在 bus_add_device() 期间)。
由于设备在获取 device_lock() 之前对用户空间可见,因此这可能会导致锁反转。
内存的上线/下线应通过 device_online()/device_offline() 完成 - 以确保它与通过 sysfs 的操作正确同步。建议持有 device_hotplug_lock(例如,为了保护 online_type)
当添加/移除/上线/下线内存或添加/移除异构/设备内存时,我们应始终以写入模式持有 mem_hotplug_lock,以序列化内存热插拔(例如,访问全局/区域变量)。
此外,mem_hotplug_lock(与 device_hotplug_lock 相反)以读取模式允许实现相当高效的 get_online_mems/put_online_mems,因此访问内存的代码可以防止内存消失。