diff options
author | Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com> | 2013-02-22 19:33:12 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-23 20:50:12 -0500 |
commit | 815121d2b5cd56f1757d4468dc3abadd06a0ed6b (patch) | |
tree | 185b7886cafc50096171e89e0222d0942b5a68d8 | |
parent | 5fc1d66a22384e7a09c74e4eb4bcb93165fb95f1 (diff) |
memory_hotplug: clear zone when removing the memory
When memory is added, we update zone's and pgdat's start_pfn and
spanned_pages in __add_zone(). So we should revert them when the memory
is removed.
The patch adds a new function __remove_zone() to do this.
Signed-off-by: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
Signed-off-by: Wen Congyang <wency@cn.fujitsu.com>
Signed-off-by: Tang Chen <tangchen@cn.fujitsu.com>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: Jiang Liu <jiang.liu@huawei.com>
Cc: Jianguo Wu <wujianguo@huawei.com>
Cc: Kamezawa Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Lai Jiangshan <laijs@cn.fujitsu.com>
Cc: Wu Jianguo <wujianguo@huawei.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | mm/memory_hotplug.c | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 46c58be2fdc4..3f792375f326 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c | |||
@@ -432,8 +432,211 @@ static int __meminit __add_section(int nid, struct zone *zone, | |||
432 | return register_new_memory(nid, __pfn_to_section(phys_start_pfn)); | 432 | return register_new_memory(nid, __pfn_to_section(phys_start_pfn)); |
433 | } | 433 | } |
434 | 434 | ||
435 | /* find the smallest valid pfn in the range [start_pfn, end_pfn) */ | ||
436 | static int find_smallest_section_pfn(int nid, struct zone *zone, | ||
437 | unsigned long start_pfn, | ||
438 | unsigned long end_pfn) | ||
439 | { | ||
440 | struct mem_section *ms; | ||
441 | |||
442 | for (; start_pfn < end_pfn; start_pfn += PAGES_PER_SECTION) { | ||
443 | ms = __pfn_to_section(start_pfn); | ||
444 | |||
445 | if (unlikely(!valid_section(ms))) | ||
446 | continue; | ||
447 | |||
448 | if (unlikely(pfn_to_nid(start_pfn) != nid)) | ||
449 | continue; | ||
450 | |||
451 | if (zone && zone != page_zone(pfn_to_page(start_pfn))) | ||
452 | continue; | ||
453 | |||
454 | return start_pfn; | ||
455 | } | ||
456 | |||
457 | return 0; | ||
458 | } | ||
459 | |||
460 | /* find the biggest valid pfn in the range [start_pfn, end_pfn). */ | ||
461 | static int find_biggest_section_pfn(int nid, struct zone *zone, | ||
462 | unsigned long start_pfn, | ||
463 | unsigned long end_pfn) | ||
464 | { | ||
465 | struct mem_section *ms; | ||
466 | unsigned long pfn; | ||
467 | |||
468 | /* pfn is the end pfn of a memory section. */ | ||
469 | pfn = end_pfn - 1; | ||
470 | for (; pfn >= start_pfn; pfn -= PAGES_PER_SECTION) { | ||
471 | ms = __pfn_to_section(pfn); | ||
472 | |||
473 | if (unlikely(!valid_section(ms))) | ||
474 | continue; | ||
475 | |||
476 | if (unlikely(pfn_to_nid(pfn) != nid)) | ||
477 | continue; | ||
478 | |||
479 | if (zone && zone != page_zone(pfn_to_page(pfn))) | ||
480 | continue; | ||
481 | |||
482 | return pfn; | ||
483 | } | ||
484 | |||
485 | return 0; | ||
486 | } | ||
487 | |||
488 | static void shrink_zone_span(struct zone *zone, unsigned long start_pfn, | ||
489 | unsigned long end_pfn) | ||
490 | { | ||
491 | unsigned long zone_start_pfn = zone->zone_start_pfn; | ||
492 | unsigned long zone_end_pfn = zone->zone_start_pfn + zone->spanned_pages; | ||
493 | unsigned long pfn; | ||
494 | struct mem_section *ms; | ||
495 | int nid = zone_to_nid(zone); | ||
496 | |||
497 | zone_span_writelock(zone); | ||
498 | if (zone_start_pfn == start_pfn) { | ||
499 | /* | ||
500 | * If the section is smallest section in the zone, it need | ||
501 | * shrink zone->zone_start_pfn and zone->zone_spanned_pages. | ||
502 | * In this case, we find second smallest valid mem_section | ||
503 | * for shrinking zone. | ||
504 | */ | ||
505 | pfn = find_smallest_section_pfn(nid, zone, end_pfn, | ||
506 | zone_end_pfn); | ||
507 | if (pfn) { | ||
508 | zone->zone_start_pfn = pfn; | ||
509 | zone->spanned_pages = zone_end_pfn - pfn; | ||
510 | } | ||
511 | } else if (zone_end_pfn == end_pfn) { | ||
512 | /* | ||
513 | * If the section is biggest section in the zone, it need | ||
514 | * shrink zone->spanned_pages. | ||
515 | * In this case, we find second biggest valid mem_section for | ||
516 | * shrinking zone. | ||
517 | */ | ||
518 | pfn = find_biggest_section_pfn(nid, zone, zone_start_pfn, | ||
519 | start_pfn); | ||
520 | if (pfn) | ||
521 | zone->spanned_pages = pfn - zone_start_pfn + 1; | ||
522 | } | ||
523 | |||
524 | /* | ||
525 | * The section is not biggest or smallest mem_section in the zone, it | ||
526 | * only creates a hole in the zone. So in this case, we need not | ||
527 | * change the zone. But perhaps, the zone has only hole data. Thus | ||
528 | * it check the zone has only hole or not. | ||
529 | */ | ||
530 | pfn = zone_start_pfn; | ||
531 | for (; pfn < zone_end_pfn; pfn += PAGES_PER_SECTION) { | ||
532 | ms = __pfn_to_section(pfn); | ||
533 | |||
534 | if (unlikely(!valid_section(ms))) | ||
535 | continue; | ||
536 | |||
537 | if (page_zone(pfn_to_page(pfn)) != zone) | ||
538 | continue; | ||
539 | |||
540 | /* If the section is current section, it continues the loop */ | ||
541 | if (start_pfn == pfn) | ||
542 | continue; | ||
543 | |||
544 | /* If we find valid section, we have nothing to do */ | ||
545 | zone_span_writeunlock(zone); | ||
546 | return; | ||
547 | } | ||
548 | |||
549 | /* The zone has no valid section */ | ||
550 | zone->zone_start_pfn = 0; | ||
551 | zone->spanned_pages = 0; | ||
552 | zone_span_writeunlock(zone); | ||
553 | } | ||
554 | |||
555 | static void shrink_pgdat_span(struct pglist_data *pgdat, | ||
556 | unsigned long start_pfn, unsigned long end_pfn) | ||
557 | { | ||
558 | unsigned long pgdat_start_pfn = pgdat->node_start_pfn; | ||
559 | unsigned long pgdat_end_pfn = | ||
560 | pgdat->node_start_pfn + pgdat->node_spanned_pages; | ||
561 | unsigned long pfn; | ||
562 | struct mem_section *ms; | ||
563 | int nid = pgdat->node_id; | ||
564 | |||
565 | if (pgdat_start_pfn == start_pfn) { | ||
566 | /* | ||
567 | * If the section is smallest section in the pgdat, it need | ||
568 | * shrink pgdat->node_start_pfn and pgdat->node_spanned_pages. | ||
569 | * In this case, we find second smallest valid mem_section | ||
570 | * for shrinking zone. | ||
571 | */ | ||
572 | pfn = find_smallest_section_pfn(nid, NULL, end_pfn, | ||
573 | pgdat_end_pfn); | ||
574 | if (pfn) { | ||
575 | pgdat->node_start_pfn = pfn; | ||
576 | pgdat->node_spanned_pages = pgdat_end_pfn - pfn; | ||
577 | } | ||
578 | } else if (pgdat_end_pfn == end_pfn) { | ||
579 | /* | ||
580 | * If the section is biggest section in the pgdat, it need | ||
581 | * shrink pgdat->node_spanned_pages. | ||
582 | * In this case, we find second biggest valid mem_section for | ||
583 | * shrinking zone. | ||
584 | */ | ||
585 | pfn = find_biggest_section_pfn(nid, NULL, pgdat_start_pfn, | ||
586 | start_pfn); | ||
587 | if (pfn) | ||
588 | pgdat->node_spanned_pages = pfn - pgdat_start_pfn + 1; | ||
589 | } | ||
590 | |||
591 | /* | ||
592 | * If the section is not biggest or smallest mem_section in the pgdat, | ||
593 | * it only creates a hole in the pgdat. So in this case, we need not | ||
594 | * change the pgdat. | ||
595 | * But perhaps, the pgdat has only hole data. Thus it check the pgdat | ||
596 | * has only hole or not. | ||
597 | */ | ||
598 | pfn = pgdat_start_pfn; | ||
599 | for (; pfn < pgdat_end_pfn; pfn += PAGES_PER_SECTION) { | ||
600 | ms = __pfn_to_section(pfn); | ||
601 | |||
602 | if (unlikely(!valid_section(ms))) | ||
603 | continue; | ||
604 | |||
605 | if (pfn_to_nid(pfn) != nid) | ||
606 | continue; | ||
607 | |||
608 | /* If the section is current section, it continues the loop */ | ||
609 | if (start_pfn == pfn) | ||
610 | continue; | ||
611 | |||
612 | /* If we find valid section, we have nothing to do */ | ||
613 | return; | ||
614 | } | ||
615 | |||
616 | /* The pgdat has no valid section */ | ||
617 | pgdat->node_start_pfn = 0; | ||
618 | pgdat->node_spanned_pages = 0; | ||
619 | } | ||
620 | |||
621 | static void __remove_zone(struct zone *zone, unsigned long start_pfn) | ||
622 | { | ||
623 | struct pglist_data *pgdat = zone->zone_pgdat; | ||
624 | int nr_pages = PAGES_PER_SECTION; | ||
625 | int zone_type; | ||
626 | unsigned long flags; | ||
627 | |||
628 | zone_type = zone - pgdat->node_zones; | ||
629 | |||
630 | pgdat_resize_lock(zone->zone_pgdat, &flags); | ||
631 | shrink_zone_span(zone, start_pfn, start_pfn + nr_pages); | ||
632 | shrink_pgdat_span(pgdat, start_pfn, start_pfn + nr_pages); | ||
633 | pgdat_resize_unlock(zone->zone_pgdat, &flags); | ||
634 | } | ||
635 | |||
435 | static int __remove_section(struct zone *zone, struct mem_section *ms) | 636 | static int __remove_section(struct zone *zone, struct mem_section *ms) |
436 | { | 637 | { |
638 | unsigned long start_pfn; | ||
639 | int scn_nr; | ||
437 | int ret = -EINVAL; | 640 | int ret = -EINVAL; |
438 | 641 | ||
439 | if (!valid_section(ms)) | 642 | if (!valid_section(ms)) |
@@ -443,6 +646,10 @@ static int __remove_section(struct zone *zone, struct mem_section *ms) | |||
443 | if (ret) | 646 | if (ret) |
444 | return ret; | 647 | return ret; |
445 | 648 | ||
649 | scn_nr = __section_nr(ms); | ||
650 | start_pfn = section_nr_to_pfn(scn_nr); | ||
651 | __remove_zone(zone, start_pfn); | ||
652 | |||
446 | sparse_remove_one_section(zone, ms); | 653 | sparse_remove_one_section(zone, ms); |
447 | return 0; | 654 | return 0; |
448 | } | 655 | } |