diff options
| author | Dan Williams <dan.j.williams@intel.com> | 2019-07-18 14:58:22 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-07-18 16:08:07 -0800 |
| commit | 7ea6216049ff9cf250a6722cd766d99c8d1424e5 (patch) | |
| tree | 404d13bb0359762c161df64effc26e10b78e3790 /mm | |
| parent | 46d945aeab4d7dd837bd0724662de2caf712f047 (diff) | |
| download | linux-stable-7ea6216049ff9cf250a6722cd766d99c8d1424e5.tar.gz linux-stable-7ea6216049ff9cf250a6722cd766d99c8d1424e5.tar.bz2 linux-stable-7ea6216049ff9cf250a6722cd766d99c8d1424e5.tar.xz linux-stable-7ea6216049ff9cf250a6722cd766d99c8d1424e5.zip | |
mm/sparsemem: prepare for sub-section ranges
Prepare the memory hot-{add,remove} paths for handling sub-section
ranges by plumbing the starting page frame and number of pages being
handled through arch_{add,remove}_memory() to
sparse_{add,remove}_one_section().
This is simply plumbing, small cleanups, and some identifier renames.
No intended functional changes.
Link: http://lkml.kernel.org/r/156092353780.979959.9713046515562743194.stgit@dwillia2-desk3.amr.corp.intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Pavel Tatashin <pasha.tatashin@soleen.com>
Tested-by: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com> [ppc64]
Reviewed-by: Oscar Salvador <osalvador@suse.de>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Logan Gunthorpe <logang@deltatee.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: Jane Chu <jane.chu@oracle.com>
Cc: Jeff Moyer <jmoyer@redhat.com>
Cc: Jérôme Glisse <jglisse@redhat.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Mike Rapoport <rppt@linux.ibm.com>
Cc: Toshi Kani <toshi.kani@hpe.com>
Cc: Wei Yang <richardw.yang@linux.intel.com>
Cc: Jason Gunthorpe <jgg@mellanox.com>
Cc: Christoph Hellwig <hch@lst.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm')
| -rw-r--r-- | mm/memory_hotplug.c | 114 | ||||
| -rw-r--r-- | mm/sparse.c | 16 |
2 files changed, 78 insertions, 52 deletions
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 11220044b01a..3fbb2cfab126 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c | |||
| @@ -252,51 +252,84 @@ void __init register_page_bootmem_info_node(struct pglist_data *pgdat) | |||
| 252 | } | 252 | } |
| 253 | #endif /* CONFIG_HAVE_BOOTMEM_INFO_NODE */ | 253 | #endif /* CONFIG_HAVE_BOOTMEM_INFO_NODE */ |
| 254 | 254 | ||
| 255 | static int __meminit __add_section(int nid, unsigned long phys_start_pfn, | 255 | static int __meminit __add_section(int nid, unsigned long pfn, |
| 256 | struct vmem_altmap *altmap) | 256 | unsigned long nr_pages, struct vmem_altmap *altmap) |
| 257 | { | 257 | { |
| 258 | int ret; | 258 | int ret; |
| 259 | 259 | ||
| 260 | if (pfn_valid(phys_start_pfn)) | 260 | if (pfn_valid(pfn)) |
| 261 | return -EEXIST; | 261 | return -EEXIST; |
| 262 | 262 | ||
| 263 | ret = sparse_add_one_section(nid, phys_start_pfn, altmap); | 263 | ret = sparse_add_section(nid, pfn, nr_pages, altmap); |
| 264 | return ret < 0 ? ret : 0; | 264 | return ret < 0 ? ret : 0; |
| 265 | } | 265 | } |
| 266 | 266 | ||
| 267 | static int check_pfn_span(unsigned long pfn, unsigned long nr_pages, | ||
| 268 | const char *reason) | ||
| 269 | { | ||
| 270 | /* | ||
| 271 | * Disallow all operations smaller than a sub-section and only | ||
| 272 | * allow operations smaller than a section for | ||
| 273 | * SPARSEMEM_VMEMMAP. Note that check_hotplug_memory_range() | ||
| 274 | * enforces a larger memory_block_size_bytes() granularity for | ||
| 275 | * memory that will be marked online, so this check should only | ||
| 276 | * fire for direct arch_{add,remove}_memory() users outside of | ||
| 277 | * add_memory_resource(). | ||
| 278 | */ | ||
| 279 | unsigned long min_align; | ||
| 280 | |||
| 281 | if (IS_ENABLED(CONFIG_SPARSEMEM_VMEMMAP)) | ||
| 282 | min_align = PAGES_PER_SUBSECTION; | ||
| 283 | else | ||
| 284 | min_align = PAGES_PER_SECTION; | ||
| 285 | if (!IS_ALIGNED(pfn, min_align) | ||
| 286 | || !IS_ALIGNED(nr_pages, min_align)) { | ||
| 287 | WARN(1, "Misaligned __%s_pages start: %#lx end: #%lx\n", | ||
| 288 | reason, pfn, pfn + nr_pages - 1); | ||
| 289 | return -EINVAL; | ||
| 290 | } | ||
| 291 | return 0; | ||
| 292 | } | ||
| 293 | |||
| 267 | /* | 294 | /* |
| 268 | * Reasonably generic function for adding memory. It is | 295 | * Reasonably generic function for adding memory. It is |
| 269 | * expected that archs that support memory hotplug will | 296 | * expected that archs that support memory hotplug will |
| 270 | * call this function after deciding the zone to which to | 297 | * call this function after deciding the zone to which to |
| 271 | * add the new pages. | 298 | * add the new pages. |
| 272 | */ | 299 | */ |
| 273 | int __ref __add_pages(int nid, unsigned long phys_start_pfn, | 300 | int __ref __add_pages(int nid, unsigned long pfn, unsigned long nr_pages, |
| 274 | unsigned long nr_pages, struct mhp_restrictions *restrictions) | 301 | struct mhp_restrictions *restrictions) |
| 275 | { | 302 | { |
| 276 | unsigned long i; | 303 | unsigned long i; |
| 277 | int err = 0; | 304 | int start_sec, end_sec, err; |
| 278 | int start_sec, end_sec; | ||
| 279 | struct vmem_altmap *altmap = restrictions->altmap; | 305 | struct vmem_altmap *altmap = restrictions->altmap; |
| 280 | 306 | ||
| 281 | /* during initialize mem_map, align hot-added range to section */ | ||
| 282 | start_sec = pfn_to_section_nr(phys_start_pfn); | ||
| 283 | end_sec = pfn_to_section_nr(phys_start_pfn + nr_pages - 1); | ||
| 284 | |||
| 285 | if (altmap) { | 307 | if (altmap) { |
| 286 | /* | 308 | /* |
| 287 | * Validate altmap is within bounds of the total request | 309 | * Validate altmap is within bounds of the total request |
| 288 | */ | 310 | */ |
| 289 | if (altmap->base_pfn != phys_start_pfn | 311 | if (altmap->base_pfn != pfn |
| 290 | || vmem_altmap_offset(altmap) > nr_pages) { | 312 | || vmem_altmap_offset(altmap) > nr_pages) { |
| 291 | pr_warn_once("memory add fail, invalid altmap\n"); | 313 | pr_warn_once("memory add fail, invalid altmap\n"); |
| 292 | err = -EINVAL; | 314 | return -EINVAL; |
| 293 | goto out; | ||
| 294 | } | 315 | } |
| 295 | altmap->alloc = 0; | 316 | altmap->alloc = 0; |
| 296 | } | 317 | } |
| 297 | 318 | ||
| 319 | err = check_pfn_span(pfn, nr_pages, "add"); | ||
| 320 | if (err) | ||
| 321 | return err; | ||
| 322 | |||
| 323 | start_sec = pfn_to_section_nr(pfn); | ||
| 324 | end_sec = pfn_to_section_nr(pfn + nr_pages - 1); | ||
| 298 | for (i = start_sec; i <= end_sec; i++) { | 325 | for (i = start_sec; i <= end_sec; i++) { |
| 299 | err = __add_section(nid, section_nr_to_pfn(i), altmap); | 326 | unsigned long pfns; |
| 327 | |||
| 328 | pfns = min(nr_pages, PAGES_PER_SECTION | ||
| 329 | - (pfn & ~PAGE_SECTION_MASK)); | ||
| 330 | err = __add_section(nid, pfn, pfns, altmap); | ||
| 331 | pfn += pfns; | ||
| 332 | nr_pages -= pfns; | ||
| 300 | 333 | ||
| 301 | /* | 334 | /* |
| 302 | * EEXIST is finally dealt with by ioresource collision | 335 | * EEXIST is finally dealt with by ioresource collision |
| @@ -309,7 +342,6 @@ int __ref __add_pages(int nid, unsigned long phys_start_pfn, | |||
| 309 | cond_resched(); | 342 | cond_resched(); |
| 310 | } | 343 | } |
| 311 | vmemmap_populate_print_last(); | 344 | vmemmap_populate_print_last(); |
| 312 | out: | ||
| 313 | return err; | 345 | return err; |
| 314 | } | 346 | } |
| 315 | 347 | ||
| @@ -487,10 +519,10 @@ static void shrink_pgdat_span(struct pglist_data *pgdat, | |||
| 487 | pgdat->node_spanned_pages = 0; | 519 | pgdat->node_spanned_pages = 0; |
| 488 | } | 520 | } |
| 489 | 521 | ||
| 490 | static void __remove_zone(struct zone *zone, unsigned long start_pfn) | 522 | static void __remove_zone(struct zone *zone, unsigned long start_pfn, |
| 523 | unsigned long nr_pages) | ||
| 491 | { | 524 | { |
| 492 | struct pglist_data *pgdat = zone->zone_pgdat; | 525 | struct pglist_data *pgdat = zone->zone_pgdat; |
| 493 | int nr_pages = PAGES_PER_SECTION; | ||
| 494 | unsigned long flags; | 526 | unsigned long flags; |
| 495 | 527 | ||
| 496 | pgdat_resize_lock(zone->zone_pgdat, &flags); | 528 | pgdat_resize_lock(zone->zone_pgdat, &flags); |
| @@ -499,27 +531,23 @@ static void __remove_zone(struct zone *zone, unsigned long start_pfn) | |||
| 499 | pgdat_resize_unlock(zone->zone_pgdat, &flags); | 531 | pgdat_resize_unlock(zone->zone_pgdat, &flags); |
| 500 | } | 532 | } |
| 501 | 533 | ||
| 502 | static void __remove_section(struct zone *zone, struct mem_section *ms, | 534 | static void __remove_section(struct zone *zone, unsigned long pfn, |
| 503 | unsigned long map_offset, | 535 | unsigned long nr_pages, unsigned long map_offset, |
| 504 | struct vmem_altmap *altmap) | 536 | struct vmem_altmap *altmap) |
| 505 | { | 537 | { |
| 506 | unsigned long start_pfn; | 538 | struct mem_section *ms = __nr_to_section(pfn_to_section_nr(pfn)); |
| 507 | int scn_nr; | ||
| 508 | 539 | ||
| 509 | if (WARN_ON_ONCE(!valid_section(ms))) | 540 | if (WARN_ON_ONCE(!valid_section(ms))) |
| 510 | return; | 541 | return; |
| 511 | 542 | ||
| 512 | scn_nr = __section_nr(ms); | 543 | __remove_zone(zone, pfn, nr_pages); |
| 513 | start_pfn = section_nr_to_pfn((unsigned long)scn_nr); | 544 | sparse_remove_one_section(ms, pfn, nr_pages, map_offset, altmap); |
| 514 | __remove_zone(zone, start_pfn); | ||
| 515 | |||
| 516 | sparse_remove_one_section(ms, map_offset, altmap); | ||
| 517 | } | 545 | } |
| 518 | 546 | ||
| 519 | /** | 547 | /** |
| 520 | * __remove_pages() - remove sections of pages from a zone | 548 | * __remove_pages() - remove sections of pages from a zone |
| 521 | * @zone: zone from which pages need to be removed | 549 | * @zone: zone from which pages need to be removed |
| 522 | * @phys_start_pfn: starting pageframe (must be aligned to start of a section) | 550 | * @pfn: starting pageframe (must be aligned to start of a section) |
| 523 | * @nr_pages: number of pages to remove (must be multiple of section size) | 551 | * @nr_pages: number of pages to remove (must be multiple of section size) |
| 524 | * @altmap: alternative device page map or %NULL if default memmap is used | 552 | * @altmap: alternative device page map or %NULL if default memmap is used |
| 525 | * | 553 | * |
| @@ -528,30 +556,30 @@ static void __remove_section(struct zone *zone, struct mem_section *ms, | |||
| 528 | * sure that pages are marked reserved and zones are adjust properly by | 556 | * sure that pages are marked reserved and zones are adjust properly by |
| 529 | * calling offline_pages(). | 557 | * calling offline_pages(). |
| 530 | */ | 558 | */ |
| 531 | void __remove_pages(struct zone *zone, unsigned long phys_start_pfn, | 559 | void __remove_pages(struct zone *zone, unsigned long pfn, |
| 532 | unsigned long nr_pages, struct vmem_altmap *altmap) | 560 | unsigned long nr_pages, struct vmem_altmap *altmap) |
| 533 | { | 561 | { |
| 534 | unsigned long i; | ||
| 535 | unsigned long map_offset = 0; | 562 | unsigned long map_offset = 0; |
| 536 | int sections_to_remove; | 563 | int i, start_sec, end_sec; |
| 537 | 564 | ||
| 538 | map_offset = vmem_altmap_offset(altmap); | 565 | map_offset = vmem_altmap_offset(altmap); |
| 539 | 566 | ||
| 540 | clear_zone_contiguous(zone); | 567 | clear_zone_contiguous(zone); |
| 541 | 568 | ||
| 542 | /* | 569 | if (check_pfn_span(pfn, nr_pages, "remove")) |
| 543 | * We can only remove entire sections | 570 | return; |
| 544 | */ | ||
| 545 | BUG_ON(phys_start_pfn & ~PAGE_SECTION_MASK); | ||
| 546 | BUG_ON(nr_pages % PAGES_PER_SECTION); | ||
| 547 | 571 | ||
| 548 | sections_to_remove = nr_pages / PAGES_PER_SECTION; | 572 | start_sec = pfn_to_section_nr(pfn); |
| 549 | for (i = 0; i < sections_to_remove; i++) { | 573 | end_sec = pfn_to_section_nr(pfn + nr_pages - 1); |
| 550 | unsigned long pfn = phys_start_pfn + i*PAGES_PER_SECTION; | 574 | for (i = start_sec; i <= end_sec; i++) { |
| 575 | unsigned long pfns; | ||
| 551 | 576 | ||
| 552 | cond_resched(); | 577 | cond_resched(); |
| 553 | __remove_section(zone, __pfn_to_section(pfn), map_offset, | 578 | pfns = min(nr_pages, PAGES_PER_SECTION |
| 554 | altmap); | 579 | - (pfn & ~PAGE_SECTION_MASK)); |
| 580 | __remove_section(zone, pfn, pfns, map_offset, altmap); | ||
| 581 | pfn += pfns; | ||
| 582 | nr_pages -= pfns; | ||
| 555 | map_offset = 0; | 583 | map_offset = 0; |
| 556 | } | 584 | } |
| 557 | 585 | ||
diff --git a/mm/sparse.c b/mm/sparse.c index 6b01022e23a9..41579b66fff1 100644 --- a/mm/sparse.c +++ b/mm/sparse.c | |||
| @@ -728,8 +728,8 @@ static void free_map_bootmem(struct page *memmap) | |||
| 728 | * * -EEXIST - Section has been present. | 728 | * * -EEXIST - Section has been present. |
| 729 | * * -ENOMEM - Out of memory. | 729 | * * -ENOMEM - Out of memory. |
| 730 | */ | 730 | */ |
| 731 | int __meminit sparse_add_one_section(int nid, unsigned long start_pfn, | 731 | int __meminit sparse_add_section(int nid, unsigned long start_pfn, |
| 732 | struct vmem_altmap *altmap) | 732 | unsigned long nr_pages, struct vmem_altmap *altmap) |
| 733 | { | 733 | { |
| 734 | unsigned long section_nr = pfn_to_section_nr(start_pfn); | 734 | unsigned long section_nr = pfn_to_section_nr(start_pfn); |
| 735 | struct mem_section_usage *usage; | 735 | struct mem_section_usage *usage; |
| @@ -835,8 +835,9 @@ static void free_section_usage(struct mem_section *ms, struct page *memmap, | |||
| 835 | free_map_bootmem(memmap); | 835 | free_map_bootmem(memmap); |
| 836 | } | 836 | } |
| 837 | 837 | ||
| 838 | void sparse_remove_one_section(struct mem_section *ms, unsigned long map_offset, | 838 | void sparse_remove_one_section(struct mem_section *ms, unsigned long pfn, |
| 839 | struct vmem_altmap *altmap) | 839 | unsigned long nr_pages, unsigned long map_offset, |
| 840 | struct vmem_altmap *altmap) | ||
| 840 | { | 841 | { |
| 841 | struct page *memmap = NULL; | 842 | struct page *memmap = NULL; |
| 842 | struct mem_section_usage *usage = NULL; | 843 | struct mem_section_usage *usage = NULL; |
| @@ -849,10 +850,7 @@ void sparse_remove_one_section(struct mem_section *ms, unsigned long map_offset, | |||
| 849 | ms->usage = NULL; | 850 | ms->usage = NULL; |
| 850 | } | 851 | } |
| 851 | 852 | ||
| 852 | clear_hwpoisoned_pages(memmap + map_offset, | 853 | clear_hwpoisoned_pages(memmap + map_offset, nr_pages - map_offset); |
| 853 | PAGES_PER_SECTION - map_offset); | 854 | free_section_usage(ms, memmap, usage, pfn, nr_pages, altmap); |
| 854 | free_section_usage(ms, memmap, usage, | ||
| 855 | section_nr_to_pfn(__section_nr(ms)), | ||
| 856 | PAGES_PER_SECTION, altmap); | ||
| 857 | } | 855 | } |
| 858 | #endif /* CONFIG_MEMORY_HOTPLUG */ | 856 | #endif /* CONFIG_MEMORY_HOTPLUG */ |
