Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

kernel/resource: make release_mem_region_adjustable() never fail

Patch series "selective merging of system ram resources", v4.

Some add_memory*() users add memory in small, contiguous memory blocks.
Examples include virtio-mem, hyper-v balloon, and the XEN balloon.

This can quickly result in a lot of memory resources, whereby the actual
resource boundaries are not of interest (e.g., it might be relevant for
DIMMs, exposed via /proc/iomem to user space). We really want to merge
added resources in this scenario where possible.

Resources are effectively stored in a list-based tree. Having a lot of
resources not only wastes memory, it also makes traversing that tree more
expensive, and makes /proc/iomem explode in size (e.g., requiring
kexec-tools to manually merge resources when creating a kdump header. The
current kexec-tools resource count limit does not allow for more than
~100GB of memory with a memory block size of 128MB on x86-64).

Let's allow to selectively merge system ram resources by specifying a new
flag for add_memory*(). Patch #5 contains a /proc/iomem example. Only
tested with virtio-mem.

This patch (of 8):

Let's make sure splitting a resource on memory hotunplug will never fail.
This will become more relevant once we merge selected System RAM resources
- then, we'll trigger that case more often on memory hotunplug.

In general, this function is already unlikely to fail. When we remove
memory, we free up quite a lot of metadata (memmap, page tables, memory
block device, etc.). The only reason it could really fail would be when
injecting allocation errors.

All other error cases inside release_mem_region_adjustable() seem to be
sanity checks if the function would be abused in different context - let's
add WARN_ON_ONCE() in these cases so we can catch them.

[natechancellor@gmail.com: fix use of ternary condition in release_mem_region_adjustable]
Link: https://lkml.kernel.org/r/20200922060748.2452056-1-natechancellor@gmail.com
Link: https://github.com/ClangBuiltLinux/linux/issues/1159

Signed-off-by: David Hildenbrand <david@redhat.com>
Signed-off-by: Nathan Chancellor <natechancellor@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Kees Cook <keescook@chromium.org>
Cc: Ard Biesheuvel <ardb@kernel.org>
Cc: Pankaj Gupta <pankaj.gupta.linux@gmail.com>
Cc: Baoquan He <bhe@redhat.com>
Cc: Wei Yang <richardw.yang@linux.intel.com>
Cc: Anton Blanchard <anton@ozlabs.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Boris Ostrovsky <boris.ostrovsky@oracle.com>
Cc: Christian Borntraeger <borntraeger@de.ibm.com>
Cc: Dave Jiang <dave.jiang@intel.com>
Cc: Eric Biederman <ebiederm@xmission.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Haiyang Zhang <haiyangz@microsoft.com>
Cc: Heiko Carstens <hca@linux.ibm.com>
Cc: Jason Wang <jasowang@redhat.com>
Cc: Juergen Gross <jgross@suse.com>
Cc: Julien Grall <julien@xen.org>
Cc: "K. Y. Srinivasan" <kys@microsoft.com>
Cc: Len Brown <lenb@kernel.org>
Cc: Leonardo Bras <leobras.c@gmail.com>
Cc: Libor Pechacek <lpechacek@suse.cz>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Cc: Nathan Lynch <nathanl@linux.ibm.com>
Cc: "Oliver O'Halloran" <oohall@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Pingfan Liu <kernelfans@gmail.com>
Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
Cc: Roger Pau Monn <roger.pau@citrix.com>
Cc: Stefano Stabellini <sstabellini@kernel.org>
Cc: Stephen Hemminger <sthemmin@microsoft.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Vasily Gorbik <gor@linux.ibm.com>
Cc: Vishal Verma <vishal.l.verma@intel.com>
Cc: Wei Liu <wei.liu@kernel.org>
Link: https://lkml.kernel.org/r/20200911103459.10306-2-david@redhat.com
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

David Hildenbrand and committed by
Linus Torvalds
ec62d04e b30c5927

+31 -44
+2 -2
include/linux/ioport.h
··· 248 248 extern void __release_region(struct resource *, resource_size_t, 249 249 resource_size_t); 250 250 #ifdef CONFIG_MEMORY_HOTREMOVE 251 - extern int release_mem_region_adjustable(struct resource *, resource_size_t, 252 - resource_size_t); 251 + extern void release_mem_region_adjustable(struct resource *, resource_size_t, 252 + resource_size_t); 253 253 #endif 254 254 255 255 /* Wrappers for managed devices */
+28 -21
kernel/resource.c
··· 1258 1258 * assumes that all children remain in the lower address entry for 1259 1259 * simplicity. Enhance this logic when necessary. 1260 1260 */ 1261 - int release_mem_region_adjustable(struct resource *parent, 1262 - resource_size_t start, resource_size_t size) 1261 + void release_mem_region_adjustable(struct resource *parent, 1262 + resource_size_t start, resource_size_t size) 1263 1263 { 1264 + struct resource *new_res = NULL; 1265 + bool alloc_nofail = false; 1264 1266 struct resource **p; 1265 1267 struct resource *res; 1266 - struct resource *new_res; 1267 1268 resource_size_t end; 1268 - int ret = -EINVAL; 1269 1269 1270 1270 end = start + size - 1; 1271 - if ((start < parent->start) || (end > parent->end)) 1272 - return ret; 1271 + if (WARN_ON_ONCE((start < parent->start) || (end > parent->end))) 1272 + return; 1273 1273 1274 - /* The alloc_resource() result gets checked later */ 1275 - new_res = alloc_resource(GFP_KERNEL); 1274 + /* 1275 + * We free up quite a lot of memory on memory hotunplug (esp., memap), 1276 + * just before releasing the region. This is highly unlikely to 1277 + * fail - let's play save and make it never fail as the caller cannot 1278 + * perform any error handling (e.g., trying to re-add memory will fail 1279 + * similarly). 1280 + */ 1281 + retry: 1282 + new_res = alloc_resource(GFP_KERNEL | (alloc_nofail ? __GFP_NOFAIL : 0)); 1276 1283 1277 1284 p = &parent->child; 1278 1285 write_lock(&resource_lock); ··· 1305 1298 * so if we are dealing with them, let us just back off here. 1306 1299 */ 1307 1300 if (!(res->flags & IORESOURCE_SYSRAM)) { 1308 - ret = 0; 1309 1301 break; 1310 1302 } 1311 1303 ··· 1321 1315 /* free the whole entry */ 1322 1316 *p = res->sibling; 1323 1317 free_resource(res); 1324 - ret = 0; 1325 1318 } else if (res->start == start && res->end != end) { 1326 1319 /* adjust the start */ 1327 - ret = __adjust_resource(res, end + 1, 1328 - res->end - end); 1320 + WARN_ON_ONCE(__adjust_resource(res, end + 1, 1321 + res->end - end)); 1329 1322 } else if (res->start != start && res->end == end) { 1330 1323 /* adjust the end */ 1331 - ret = __adjust_resource(res, res->start, 1332 - start - res->start); 1324 + WARN_ON_ONCE(__adjust_resource(res, res->start, 1325 + start - res->start)); 1333 1326 } else { 1334 - /* split into two entries */ 1327 + /* split into two entries - we need a new resource */ 1335 1328 if (!new_res) { 1336 - ret = -ENOMEM; 1337 - break; 1329 + new_res = alloc_resource(GFP_ATOMIC); 1330 + if (!new_res) { 1331 + alloc_nofail = true; 1332 + write_unlock(&resource_lock); 1333 + goto retry; 1334 + } 1338 1335 } 1339 1336 new_res->name = res->name; 1340 1337 new_res->start = end + 1; ··· 1348 1339 new_res->sibling = res->sibling; 1349 1340 new_res->child = NULL; 1350 1341 1351 - ret = __adjust_resource(res, res->start, 1352 - start - res->start); 1353 - if (ret) 1342 + if (WARN_ON_ONCE(__adjust_resource(res, res->start, 1343 + start - res->start))) 1354 1344 break; 1355 1345 res->sibling = new_res; 1356 1346 new_res = NULL; ··· 1360 1352 1361 1353 write_unlock(&resource_lock); 1362 1354 free_resource(new_res); 1363 - return ret; 1364 1355 } 1365 1356 #endif /* CONFIG_MEMORY_HOTREMOVE */ 1366 1357
+1 -21
mm/memory_hotplug.c
··· 1727 1727 } 1728 1728 EXPORT_SYMBOL(try_offline_node); 1729 1729 1730 - static void __release_memory_resource(resource_size_t start, 1731 - resource_size_t size) 1732 - { 1733 - int ret; 1734 - 1735 - /* 1736 - * When removing memory in the same granularity as it was added, 1737 - * this function never fails. It might only fail if resources 1738 - * have to be adjusted or split. We'll ignore the error, as 1739 - * removing of memory cannot fail. 1740 - */ 1741 - ret = release_mem_region_adjustable(&iomem_resource, start, size); 1742 - if (ret) { 1743 - resource_size_t endres = start + size - 1; 1744 - 1745 - pr_warn("Unable to release resource <%pa-%pa> (%d)\n", 1746 - &start, &endres, ret); 1747 - } 1748 - } 1749 - 1750 1730 static int __ref try_remove_memory(int nid, u64 start, u64 size) 1751 1731 { 1752 1732 int rc = 0; ··· 1760 1780 memblock_remove(start, size); 1761 1781 } 1762 1782 1763 - __release_memory_resource(start, size); 1783 + release_mem_region_adjustable(&iomem_resource, start, size); 1764 1784 1765 1785 try_offline_node(nid); 1766 1786