提交 f8502f80 编写于 作者: P Pavel Tatashin 提交者: Shile Zhang

mm/hotplug: make remove_memory() interface usable

commit eca499ab3749a4537dee77ffead47a1a2c0dee19 upstream

Presently the remove_memory() interface is inherently broken.  It tries
to remove memory but panics if some memory is not offline.  The problem
is that it is impossible to ensure that all memory blocks are offline as
this function also takes lock_device_hotplug that is required to change
memory state via sysfs.

So, between calling this function and offlining all memory blocks there
is always a window when lock_device_hotplug is released, and therefore,
there is always a chance for a panic during this window.

Make this interface to return an error if memory removal fails.  This
way it is safe to call this function without panicking machine, and also
makes it symmetric to add_memory() which already returns an error.

Link: http://lkml.kernel.org/r/20190517215438.6487-3-pasha.tatashin@soleen.comSigned-off-by: NPavel Tatashin <pasha.tatashin@soleen.com>
Reviewed-by: NDavid Hildenbrand <david@redhat.com>
Acked-by: NMichal Hocko <mhocko@suse.com>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Borislav Petkov <bp@suse.de>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Dave Hansen <dave.hansen@intel.com>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Dave Jiang <dave.jiang@intel.com>
Cc: Fengguang Wu <fengguang.wu@intel.com>
Cc: Huang Ying <ying.huang@intel.com>
Cc: James Morris <jmorris@namei.org>
Cc: Jérôme Glisse <jglisse@redhat.com>
Cc: Keith Busch <keith.busch@intel.com>
Cc: Ross Zwisler <zwisler@kernel.org>
Cc: Sasha Levin <sashal@kernel.org>
Cc: Takashi Iwai <tiwai@suse.de>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Vishal Verma <vishal.l.verma@intel.com>
Cc: Yaowei Bai <baiyaowei@cmss.chinamobile.com>
Signed-off-by: NAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: NLinus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Nyinhe <yinhe@linux.alibaba.com>
Reviewed-by: NYang Shi <yang.shi@linux.alibaba.com>
上级 d2097173
...@@ -299,10 +299,11 @@ static inline void pgdat_resize_init(struct pglist_data *pgdat) {} ...@@ -299,10 +299,11 @@ static inline void pgdat_resize_init(struct pglist_data *pgdat) {}
#ifdef CONFIG_MEMORY_HOTREMOVE #ifdef CONFIG_MEMORY_HOTREMOVE
#define CONFIG_MEMORY_HOTREMOVE_INTERFACE
extern bool is_mem_section_removable(unsigned long pfn, unsigned long nr_pages); extern bool is_mem_section_removable(unsigned long pfn, unsigned long nr_pages);
extern void try_offline_node(int nid); extern void try_offline_node(int nid);
extern int offline_pages(unsigned long start_pfn, unsigned long nr_pages); extern int offline_pages(unsigned long start_pfn, unsigned long nr_pages);
extern void remove_memory(int nid, u64 start, u64 size); extern int remove_memory(int nid, u64 start, u64 size);
extern void __remove_memory(int nid, u64 start, u64 size); extern void __remove_memory(int nid, u64 start, u64 size);
#else #else
...@@ -319,7 +320,11 @@ static inline int offline_pages(unsigned long start_pfn, unsigned long nr_pages) ...@@ -319,7 +320,11 @@ static inline int offline_pages(unsigned long start_pfn, unsigned long nr_pages)
return -EINVAL; return -EINVAL;
} }
static inline void remove_memory(int nid, u64 start, u64 size) {} static inline int remove_memory(int nid, u64 start, u64 size)
{
return -EBUSY;
}
static inline void __remove_memory(int nid, u64 start, u64 size) {} static inline void __remove_memory(int nid, u64 start, u64 size) {}
#endif /* CONFIG_MEMORY_HOTREMOVE */ #endif /* CONFIG_MEMORY_HOTREMOVE */
......
...@@ -1793,9 +1793,10 @@ static int check_memblock_offlined_cb(struct memory_block *mem, void *arg) ...@@ -1793,9 +1793,10 @@ static int check_memblock_offlined_cb(struct memory_block *mem, void *arg)
endpa = PFN_PHYS(section_nr_to_pfn(mem->end_section_nr + 1))-1; endpa = PFN_PHYS(section_nr_to_pfn(mem->end_section_nr + 1))-1;
pr_warn("removing memory fails, because memory [%pa-%pa] is onlined\n", pr_warn("removing memory fails, because memory [%pa-%pa] is onlined\n",
&beginpa, &endpa); &beginpa, &endpa);
}
return ret; return -EBUSY;
}
return 0;
} }
static int check_cpu_on_node(pg_data_t *pgdat) static int check_cpu_on_node(pg_data_t *pgdat)
...@@ -1893,19 +1894,9 @@ void try_offline_node(int nid) ...@@ -1893,19 +1894,9 @@ void try_offline_node(int nid)
} }
EXPORT_SYMBOL(try_offline_node); EXPORT_SYMBOL(try_offline_node);
/** static int __ref try_remove_memory(int nid, u64 start, u64 size)
* remove_memory
* @nid: the node ID
* @start: physical address of the region to remove
* @size: size of the region to remove
*
* NOTE: The caller must call lock_device_hotplug() to serialize hotplug
* and online/offline operations before this call, as required by
* try_offline_node().
*/
void __ref __remove_memory(int nid, u64 start, u64 size)
{ {
int ret; int rc = 0;
BUG_ON(check_hotplug_memory_range(start, size)); BUG_ON(check_hotplug_memory_range(start, size));
...@@ -1913,13 +1904,13 @@ void __ref __remove_memory(int nid, u64 start, u64 size) ...@@ -1913,13 +1904,13 @@ void __ref __remove_memory(int nid, u64 start, u64 size)
/* /*
* All memory blocks must be offlined before removing memory. Check * All memory blocks must be offlined before removing memory. Check
* whether all memory blocks in question are offline and trigger a BUG() * whether all memory blocks in question are offline and return error
* if this is not the case. * if this is not the case.
*/ */
ret = walk_memory_range(PFN_DOWN(start), PFN_UP(start + size - 1), NULL, rc = walk_memory_range(PFN_DOWN(start), PFN_UP(start + size - 1), NULL,
check_memblock_offlined_cb); check_memblock_offlined_cb);
if (ret) if (rc)
BUG(); goto done;
/* remove memmap entry */ /* remove memmap entry */
firmware_map_remove(start, start + size, "System RAM"); firmware_map_remove(start, start + size, "System RAM");
...@@ -1930,14 +1921,45 @@ void __ref __remove_memory(int nid, u64 start, u64 size) ...@@ -1930,14 +1921,45 @@ void __ref __remove_memory(int nid, u64 start, u64 size)
try_offline_node(nid); try_offline_node(nid);
done:
mem_hotplug_done(); mem_hotplug_done();
return rc;
} }
void remove_memory(int nid, u64 start, u64 size) /**
* remove_memory
* @nid: the node ID
* @start: physical address of the region to remove
* @size: size of the region to remove
*
* NOTE: The caller must call lock_device_hotplug() to serialize hotplug
* and online/offline operations before this call, as required by
* try_offline_node().
*/
void __remove_memory(int nid, u64 start, u64 size)
{
/*
* trigger BUG() is some memory is not offlined prior to calling this
* function
*/
if (try_remove_memory(nid, start, size))
BUG();
}
/*
* Remove memory if every memory block is offline, otherwise return -EBUSY is
* some memory is not offline
*/
int remove_memory(int nid, u64 start, u64 size)
{ {
int rc;
lock_device_hotplug(); lock_device_hotplug();
__remove_memory(nid, start, size); rc = try_remove_memory(nid, start, size);
unlock_device_hotplug(); unlock_device_hotplug();
return rc;
} }
EXPORT_SYMBOL_GPL(remove_memory); EXPORT_SYMBOL_GPL(remove_memory);
#endif /* CONFIG_MEMORY_HOTREMOVE */ #endif /* CONFIG_MEMORY_HOTREMOVE */
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册