Skip to content

Commit bca3fea

Browse files
Anshuman Khandualtorvalds
authored andcommitted
mm/memory_hotplug: prevalidate the address range being added with platform
Patch series "mm/memory_hotplug: Pre-validate the address range with platform", v5. This series adds a mechanism allowing platforms to weigh in and prevalidate incoming address range before proceeding further with the memory hotplug. This helps prevent potential platform errors for the given address range, down the hotplug call chain, which inevitably fails the hotplug itself. This mechanism was suggested by David Hildenbrand during another discussion with respect to a memory hotplug fix on arm64 platform. https://lore.kernel.org/linux-arm-kernel/[email protected]/ This mechanism focuses on the addressibility aspect and not [sub] section alignment aspect. Hence check_hotplug_memory_range() and check_pfn_span() have been left unchanged. This patch (of 4): This introduces mhp_range_allowed() which can be called in various memory hotplug paths to prevalidate the address range which is being added, with the platform. Then mhp_range_allowed() calls mhp_get_pluggable_range() which provides applicable address range depending on whether linear mapping is required or not. For ranges that require linear mapping, it calls a new arch callback arch_get_mappable_range() which the platform can override. So the new callback, in turn provides the platform an opportunity to configure acceptable memory hotplug address ranges in case there are constraints. This mechanism will help prevent platform specific errors deep down during hotplug calls. This drops now redundant check_hotplug_memory_addressable() check in __add_pages() but instead adds a VM_BUG_ON() check which would ensure that the range has been validated with mhp_range_allowed() earlier in the call chain. Besides mhp_get_pluggable_range() also can be used by potential memory hotplug callers to avail the allowed physical range which would go through on a given platform. This does not really add any new range check in generic memory hotplug but instead compensates for lost checks in arch_add_memory() where applicable and check_hotplug_memory_addressable(), with unified mhp_range_allowed(). [[email protected]: make pagemap_range() return -EINVAL when mhp_range_allowed() fails] Link: https://lkml.kernel.org/r/[email protected] Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Anshuman Khandual <[email protected]> Suggested-by: David Hildenbrand <[email protected]> Reviewed-by: David Hildenbrand <[email protected]> Reviewed-by: Oscar Salvador <[email protected]> Cc: Heiko Carstens <[email protected]> Cc: Catalin Marinas <[email protected]> Cc: Vasily Gorbik <[email protected]> # s390 Cc: Will Deacon <[email protected]> Cc: Ard Biesheuvel <[email protected]> Cc: Mark Rutland <[email protected]> Cc: Jason Wang <[email protected]> Cc: Jonathan Cameron <[email protected]> Cc: "Michael S. Tsirkin" <[email protected]> Cc: Michal Hocko <[email protected]> Cc: Pankaj Gupta <[email protected]> Cc: Pankaj Gupta <[email protected]> Cc: teawater <[email protected]> Cc: Wei Yang <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent a89107c commit bca3fea

File tree

3 files changed

+76
-20
lines changed

3 files changed

+76
-20
lines changed

include/linux/memory_hotplug.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ struct mhp_params {
6666
pgprot_t pgprot;
6767
};
6868

69+
bool mhp_range_allowed(u64 start, u64 size, bool need_mapping);
70+
struct range mhp_get_pluggable_range(bool need_mapping);
71+
6972
/*
7073
* Zone resizing functions
7174
*
@@ -266,6 +269,13 @@ static inline bool movable_node_is_enabled(void)
266269
}
267270
#endif /* ! CONFIG_MEMORY_HOTPLUG */
268271

272+
/*
273+
* Keep this declaration outside CONFIG_MEMORY_HOTPLUG as some
274+
* platforms might override and use arch_get_mappable_range()
275+
* for internal non memory hotplug purposes.
276+
*/
277+
struct range arch_get_mappable_range(void);
278+
269279
#if defined(CONFIG_MEMORY_HOTPLUG) || defined(CONFIG_DEFERRED_STRUCT_PAGE_INIT)
270280
/*
271281
* pgdat resizing functions

mm/memory_hotplug.c

Lines changed: 59 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ static struct resource *register_memory_resource(u64 start, u64 size,
107107
if (strcmp(resource_name, "System RAM"))
108108
flags |= IORESOURCE_SYSRAM_DRIVER_MANAGED;
109109

110+
if (!mhp_range_allowed(start, size, true))
111+
return ERR_PTR(-E2BIG);
112+
110113
/*
111114
* Make sure value parsed from 'mem=' only restricts memory adding
112115
* while booting, so that memory hotplug won't be impacted. Please
@@ -284,22 +287,6 @@ static int check_pfn_span(unsigned long pfn, unsigned long nr_pages,
284287
return 0;
285288
}
286289

287-
static int check_hotplug_memory_addressable(unsigned long pfn,
288-
unsigned long nr_pages)
289-
{
290-
const u64 max_addr = PFN_PHYS(pfn + nr_pages) - 1;
291-
292-
if (max_addr >> MAX_PHYSMEM_BITS) {
293-
const u64 max_allowed = (1ull << (MAX_PHYSMEM_BITS + 1)) - 1;
294-
WARN(1,
295-
"Hotplugged memory exceeds maximum addressable address, range=%#llx-%#llx, maximum=%#llx\n",
296-
(u64)PFN_PHYS(pfn), max_addr, max_allowed);
297-
return -E2BIG;
298-
}
299-
300-
return 0;
301-
}
302-
303290
/*
304291
* Return page for the valid pfn only if the page is online. All pfn
305292
* walkers which rely on the fully initialized page->flags and others
@@ -365,9 +352,7 @@ int __ref __add_pages(int nid, unsigned long pfn, unsigned long nr_pages,
365352
if (WARN_ON_ONCE(!params->pgprot.pgprot))
366353
return -EINVAL;
367354

368-
err = check_hotplug_memory_addressable(pfn, nr_pages);
369-
if (err)
370-
return err;
355+
VM_BUG_ON(!mhp_range_allowed(PFN_PHYS(pfn), nr_pages * PAGE_SIZE, false));
371356

372357
if (altmap) {
373358
/*
@@ -1248,6 +1233,61 @@ int add_memory_driver_managed(int nid, u64 start, u64 size,
12481233
}
12491234
EXPORT_SYMBOL_GPL(add_memory_driver_managed);
12501235

1236+
/*
1237+
* Platforms should define arch_get_mappable_range() that provides
1238+
* maximum possible addressable physical memory range for which the
1239+
* linear mapping could be created. The platform returned address
1240+
* range must adhere to these following semantics.
1241+
*
1242+
* - range.start <= range.end
1243+
* - Range includes both end points [range.start..range.end]
1244+
*
1245+
* There is also a fallback definition provided here, allowing the
1246+
* entire possible physical address range in case any platform does
1247+
* not define arch_get_mappable_range().
1248+
*/
1249+
struct range __weak arch_get_mappable_range(void)
1250+
{
1251+
struct range mhp_range = {
1252+
.start = 0UL,
1253+
.end = -1ULL,
1254+
};
1255+
return mhp_range;
1256+
}
1257+
1258+
struct range mhp_get_pluggable_range(bool need_mapping)
1259+
{
1260+
const u64 max_phys = (1ULL << MAX_PHYSMEM_BITS) - 1;
1261+
struct range mhp_range;
1262+
1263+
if (need_mapping) {
1264+
mhp_range = arch_get_mappable_range();
1265+
if (mhp_range.start > max_phys) {
1266+
mhp_range.start = 0;
1267+
mhp_range.end = 0;
1268+
}
1269+
mhp_range.end = min_t(u64, mhp_range.end, max_phys);
1270+
} else {
1271+
mhp_range.start = 0;
1272+
mhp_range.end = max_phys;
1273+
}
1274+
return mhp_range;
1275+
}
1276+
EXPORT_SYMBOL_GPL(mhp_get_pluggable_range);
1277+
1278+
bool mhp_range_allowed(u64 start, u64 size, bool need_mapping)
1279+
{
1280+
struct range mhp_range = mhp_get_pluggable_range(need_mapping);
1281+
u64 end = start + size;
1282+
1283+
if (start < end && start >= mhp_range.start && (end - 1) <= mhp_range.end)
1284+
return true;
1285+
1286+
pr_warn("Hotplug memory [%#llx-%#llx] exceeds maximum addressable range [%#llx-%#llx]\n",
1287+
start, end, mhp_range.start, mhp_range.end);
1288+
return false;
1289+
}
1290+
12511291
#ifdef CONFIG_MEMORY_HOTREMOVE
12521292
/*
12531293
* Confirm all pages in a range [start, end) belong to the same zone (skipping

mm/memremap.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ static void dev_pagemap_percpu_release(struct percpu_ref *ref)
200200
static int pagemap_range(struct dev_pagemap *pgmap, struct mhp_params *params,
201201
int range_id, int nid)
202202
{
203+
const bool is_private = pgmap->type == MEMORY_DEVICE_PRIVATE;
203204
struct range *range = &pgmap->ranges[range_id];
204205
struct dev_pagemap *conflict_pgmap;
205206
int error, is_ram;
@@ -245,6 +246,11 @@ static int pagemap_range(struct dev_pagemap *pgmap, struct mhp_params *params,
245246
if (error)
246247
goto err_pfn_remap;
247248

249+
if (!mhp_range_allowed(range->start, range_len(range), !is_private)) {
250+
error = -EINVAL;
251+
goto err_pfn_remap;
252+
}
253+
248254
mem_hotplug_begin();
249255

250256
/*
@@ -258,7 +264,7 @@ static int pagemap_range(struct dev_pagemap *pgmap, struct mhp_params *params,
258264
* the CPU, we do want the linear mapping and thus use
259265
* arch_add_memory().
260266
*/
261-
if (pgmap->type == MEMORY_DEVICE_PRIVATE) {
267+
if (is_private) {
262268
error = add_pages(nid, PHYS_PFN(range->start),
263269
PHYS_PFN(range_len(range)), params);
264270
} else {

0 commit comments

Comments
 (0)