記憶體熱插拔

記憶體熱插拔事件通知器

熱插拔事件被髮送到通知佇列。

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 是上線/下線記憶體的 start_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,因此訪問記憶體的程式碼可以防止記憶體消失。