| @@ -252,51 +252,84 @@ void __init register_page_bootmem_info_node(struct pglist_data *pgdat) · 251 unmodified lines |
| 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, · 6 unmodified lines |
| 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, · 171 unmodified lines |
| 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) · 2 unmodified lines |
| 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, · 2 unmodified lines |
| 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 | |