内存管理-53-内存热插拔-1-理论与文档翻译


一、memory-hotplug.rst

注: 翻译自 msm-5.4/Documentation/core-api/memory-hotplug.rst

.. _memory_hotplug:

===============

内存热插拔

===============

1. 内存热插拔事件通知

=============================

热插拔事件会被发送到通知队列。

``include/linux/memory.h``中定义了六种类型的通知:

(1) MEM_GOING_ONLINE

在新内存可用之前生成,以便子系统能够准备好处理内存。此时页面分配器还无法从新内存中分配页面。

(2) MEM_CANCEL_ONLINE

如果 MEM_GOING_ONLINE 失败,则生成此通知。

(3) MEM_ONLINE

当内存成功上线时生成。回调函数可能会从新内存中分配页面。

(4) MEM_GOING_OFFLINE

用于启动内存离线过程。内存中已无法再进行分配,但部分待离线的内存仍在被使用。此回调函数可用于从指定的内存块中释放子系统已知的内存。

(5) MEM_CANCEL_OFFLINE

如果 MEM_GOING_OFFLINE 失败,则生成此事件。尝试离线的内存块中的内存已恢复可用。

(6) 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_high;
    int status_change_nid;
}

- start_pfn 是 online/offline 内存的起始页号。

- nr_pages 是 online/offline 内存的页数。

- status_change_nid_normal 是当节点掩码的 N_NORMAL_MEMORY 被设置/清除时设置的节点 ID。如果此值为 -1,则节点掩码状态不变。

- status_change_nid_high 是当节点掩码的 N_HIGH_MEMORY 被设置/清除时设置的节点 ID。如果此值为 -1,则节点掩码状态不变。

- status_change_nid 是当节点掩码的 N_MEMORY 被设置/清除时设置的节点 ID。这意味着一个新(无内存)节点通过联机获得新内存,而一个节点失去所有内存。如果此值为 -1,则节点掩码状态不变。

如果 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 会停止对通知队列的进一步处理。


2. 内部锁

=================

当添加/移除使用内存块设备(例如普通 RAM)的内存时,应保持 device_hotplug_lock 锁定状态,以便:

(1) 与 online/offline 请求(例如通过 sysfs)同步。这样,只有在内存完全添加后,用户空间才能访问内存块设备(.online/.state 属性)。移除内存时,我们也能确保没有程序处于临界区。

(2) 与 CPU 热插拔及类似机制同步(例如,与 ACPI 和 PPC 相关)


尤其需要注意的是,当添加内存且用户空间尝试以超出预期的速度联机该内存时,可能会出现锁反转的情况,而使用 `device_hotplug_lock` 可以避免这种情况:

(1) `device_online()` 会首先获取 `device_lock()`,然后获取 `mem_hotplug_lock`

(2) `add_memory_resource()` 会首先获取 `mem_hotplug_lock`,然后获取 `device_lock()`(在创建设备时,即在 `bus_add_device()` 期间)。

由于设备在获取 `device_lock()` 之前对用户空间可见,这可能导致锁反转。

内存的 onlining/offlining 操作应通过 `device_online()`/`device_offline()` 完成,以确保其与 sysfs 中的操作正确同步。建议持有 device_hotplug_lock(例如,用于保护 online_type)。

在 adding/removing/onlining/offlining 内存或 adding/removing 异构/设备内存时,我们应始终将 mem_hotplug_lock 保持在写模式,以串行化内存热插拔(例如,访问 global/zone 变量)。

此外,与 device_hotplug_lock 不同,mem_hotplug_lock 处于读模式时,可以实现非常高效的 get_online_mems()/put_online_mems() 函数,因此访问内存的代码可以防止内存丢失。

 

posted on 2025-12-01 20:30  Hello-World3  阅读(0)  评论(0)    收藏  举报

导航