In confidential computing environments like TDX, memory that was previously accepted by the guest could be explicitly "released" back to the hypervisor before it is unplugged, because hypervisor can do no-op for the unplug operation without guest awares, then replug will fail with re-accept error.
This callback infrastructure allows the TDX guest code to register a handler that will be invoked after kernel removes memory from its memory management subsystem but before it is unplugged, ensuring all memory pages are properly released via TDG.MEM.PAGE.RELEASE TDCALL. Then re-plug triggers TDG.MEM.PAGE.ACCEPT on pages in "unaccepted" state and succeed. Signed-off-by: Zhenzhong Duan <[email protected]> --- include/linux/memory_hotplug.h | 10 ++++++++++ mm/memory_hotplug.c | 20 ++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h index 39f0a35a5112..5bb77670b6cf 100644 --- a/include/linux/memory_hotplug.h +++ b/include/linux/memory_hotplug.h @@ -29,6 +29,7 @@ enum mmop { }; typedef int (*memory_post_plug_callback_t)(u64 addr, u64 size); +typedef int (*memory_pre_unplug_callback_t)(u64 addr, u64 size); #ifdef CONFIG_MEMORY_HOTPLUG struct page *pfn_to_online_page(unsigned long pfn); @@ -278,6 +279,9 @@ extern int remove_memory(u64 start, u64 size); extern void __remove_memory(u64 start, u64 size); extern int offline_and_remove_memory(u64 start, u64 size); +void set_memory_pre_unplug_callback(memory_pre_unplug_callback_t callback); +int memory_pre_unplug_call(u64 addr, u64 size); + #else static inline void try_offline_node(int nid) {} @@ -293,6 +297,12 @@ static inline int remove_memory(u64 start, u64 size) } static inline void __remove_memory(u64 start, u64 size) {} + +static inline void set_memory_pre_unplug_callback(memory_pre_unplug_callback_t callback) {} +static inline int memory_pre_unplug_call(u64 addr, u64 size) +{ + return 0; +} #endif /* CONFIG_MEMORY_HOTREMOVE */ #ifdef CONFIG_MEMORY_HOTPLUG diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 73054ed016fd..fcb6f85c40d0 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -2451,4 +2451,24 @@ int offline_and_remove_memory(u64 start, u64 size) return rc; } EXPORT_SYMBOL_GPL(offline_and_remove_memory); + +static memory_pre_unplug_callback_t memory_pre_unplug_callback __ro_after_init; + +void set_memory_pre_unplug_callback(memory_pre_unplug_callback_t callback) +{ + /* Fatal error to set callback twice in boot stage */ + if (memory_pre_unplug_callback) + panic("memory_pre_unplug_callback is already registered\n"); + + memory_pre_unplug_callback = callback; +} + +int memory_pre_unplug_call(u64 addr, u64 size) +{ + if (!memory_pre_unplug_callback) + return 0; + + return (*memory_pre_unplug_callback)(addr, size); +} +EXPORT_SYMBOL_GPL(memory_pre_unplug_call); #endif /* CONFIG_MEMORY_HOTREMOVE */ -- 2.52.0

