aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>2013-02-22 19:33:12 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2013-02-23 20:50:12 -0500
commit815121d2b5cd56f1757d4468dc3abadd06a0ed6b (patch)
tree185b7886cafc50096171e89e0222d0942b5a68d8
parent5fc1d66a22384e7a09c74e4eb4bcb93165fb95f1 (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.c207
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) */
436static 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). */
461static 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
488static 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
555static 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
621static 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
435static int __remove_section(struct zone *zone, struct mem_section *ms) 636static 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}