The patch-set was divided from following thread's patch-set.
https://lkml.org/lkml/2012/9/5/201
If you want to know the reason, please read following thread.
https://lkml.org/lkml/2012/10/2/83
The patch-set has only the function of kernel core side for physical
memory hot remove. So if you use the patch, please apply following
patches.
- bug fix for memory hot remove
https://lkml.org/lkml/2012/9/27/39https://lkml.org/lkml/2012/10/2/83http://www.spinics.net/lists/linux-mm/msg42982.html
- acpi framework
https://lkml.org/lkml/2012/10/3/126https://lkml.org/lkml/2012/10/3/641
The patches can free/remove the following things:
- /sys/firmware/memmap/X/{end, start, type} : [PATCH 2/10]
- mem_section and related sysfs files : [PATCH 3-4/10]
- memmap of sparse-vmemmap : [PATCH 5-7/10]
- page table of removed memory : [RFC PATCH 8/10]
- node and related sysfs files : [RFC PATCH 9-10/10]
* [PATCH 1/10] checks whether the memory can be removed or not.
If you find lack of function for physical memory hot-remove, please let me
know.
How to test this patchset?
1. apply this patchset and build the kernel. MEMORY_HOTPLUG, MEMORY_HOTREMOVE,
ACPI_HOTPLUG_MEMORY must be selected.
2. load the module acpi_memhotplug
3. hotplug the memory device(it depends on your hardware)
You will see the memory device under the directory /sys/bus/acpi/devices/.
Its name is PNP0C80:XX.
4. online/offline pages provided by this memory device
You can write online/offline to /sys/devices/system/memory/memoryX/state to
online/offline pages provided by this memory device
5. hotremove the memory device
You can hotremove the memory device by the hardware, or writing 1 to
/sys/bus/acpi/devices/PNP0C80:XX/eject.
Note: if the memory provided by the memory device is used by the kernel, it
can't be offlined. It is not a bug.
Known problems:
1. memory can't be offlined when CONFIG_MEMCG is selected.
For example: there is a memory device on node 1. The address range
is [1G, 1.5G). You will find 4 new directories memory8, memory9, memory10,
and memory11 under the directory /sys/devices/system/memory/.
If CONFIG_MEMCG is selected, we will allocate memory to store page cgroup
when we online pages. When we online memory8, the memory stored page cgroup
is not provided by this memory device. But when we online memory9, the memory
stored page cgroup may be provided by memory8. So we can't offline memory8
now. We should offline the memory in the reversed order.
When the memory device is hotremoved, we will auto offline memory provided
by this memory device. But we don't know which memory is onlined first, so
offlining memory may fail. In such case, you should offline the memory by
hand before hotremoving the memory device.
2. hotremoving memory device may cause kernel panicked
This bug will be fixed by Liu Jiang's patch:
https://lkml.org/lkml/2012/7/3/1
When calling remove_memory(), the memory should be offline. If the function
is used to online memory, kernel panic may occur.
So the patch checks whether memory is offline or not.
CC: David Rientjes <rientjes@google.com>
CC: Jiang Liu <redacted>
CC: Len Brown <redacted>
CC: Christoph Lameter <redacted>
Cc: Minchan Kim <redacted>
CC: Andrew Morton <akpm@linux-foundation.org>
CC: KOSAKI Motohiro <redacted>
Signed-off-by: Wen Congyang <redacted>
Signed-off-by: Yasuaki Ishimatsu <redacted>
---
drivers/base/memory.c | 39 +++++++++++++++++++++++++++++++++++++++
include/linux/memory.h | 5 +++++
mm/memory_hotplug.c | 17 +++++++++++++++--
3 files changed, 59 insertions(+), 2 deletions(-)
Index: linux-3.6/drivers/base/memory.c
===================================================================
@@ -1045,8 +1045,21 @@ int offline_memory(u64 start, u64 size)intremove_memory(intnid,u64start,u64size){-/* It is not implemented yet*/-return0;+intret=0;+lock_memory_hotplug();+/*+*Thememorymightbecomeonlinebyothertask,evenifyouoffineit.+*Sowecheckwhetherthememoryhasbeenonlinedornot.+*/+if(!is_memblk_offline(start,size)){+pr_warn("memory removing [mem %#010llx-%#010llx] failed, "+"because the memmory range is online\n",+start,start+size);+ret=-EAGAIN;+}++unlock_memory_hotplug();+returnret;}EXPORT_SYMBOL_GPL(remove_memory);#else
When (hot)adding memory into system, /sys/firmware/memmap/X/{end, start, type}
sysfs files are created. But there is no code to remove these files. The patch
implements the function to remove them.
Note : The code does not free firmware_map_entry since there is no way to free
memory which is allocated by bootmem.
CC: David Rientjes <rientjes@google.com>
CC: Jiang Liu <redacted>
CC: Len Brown <redacted>
CC: Christoph Lameter <redacted>
Cc: Minchan Kim <redacted>
CC: Andrew Morton <akpm@linux-foundation.org>
CC: KOSAKI Motohiro <redacted>
Signed-off-by: Wen Congyang <redacted>
Signed-off-by: Yasuaki Ishimatsu <redacted>
---
drivers/firmware/memmap.c | 98 ++++++++++++++++++++++++++++++++++++++++++-
include/linux/firmware-map.h | 6 ++
mm/memory_hotplug.c | 7 ++-
3 files changed, 108 insertions(+), 3 deletions(-)
Index: linux-3.6/drivers/firmware/memmap.c
===================================================================
@@ -41,6 +42,7 @@ struct firmware_map_entry {constchar*type;/* type of the memory range */structlist_headlist;/* entry for the linked list */structkobjectkobj;/* kobject for each entry */+unsignedintbootmem:1;/* allocated from bootmem */};/*
@@ -79,7 +81,26 @@ static const struct sysfs_ops memmap_att.show=memmap_attr_show,};++staticinlinestructfirmware_map_entry*+to_memmap_entry(structkobject*kobj)+{+returncontainer_of(kobj,structfirmware_map_entry,kobj);+}++staticvoidrelease_firmware_map_entry(structkobject*kobj)+{+structfirmware_map_entry*entry=to_memmap_entry(kobj);++if(entry->bootmem)+/* There is no way to free memory allocated from bootmem */+return;++kfree(entry);+}+staticstructkobj_typememmap_ktype={+.release=release_firmware_map_entry,.sysfs_ops=&memmap_attr_ops,.default_attrs=def_attrs,};
@@ -1043,7 +1043,7 @@ int offline_memory(u64 start, u64 size)return0;}-intremove_memory(intnid,u64start,u64size)+int__refremove_memory(intnid,u64start,u64size){intret=0;lock_memory_hotplug();
@@ -1056,8 +1056,13 @@ int remove_memory(int nid, u64 start, u6"because the memmory range is online\n",start,start+size);ret=-EAGAIN;+gotoout;}+/* remove memmap entry */+firmware_map_remove(start,start+size,"System RAM");++out:unlock_memory_hotplug();returnret;}
From: Wen Congyang <redacted>
For removing memory, we need to remove page table. But it depends
on architecture. So the patch introduce arch_remove_memory() for
removing page table. Now it only calls __remove_pages().
Note: __remove_pages() for some archtecuture is not implemented
(I don't know how to implement it for s390).
CC: David Rientjes <rientjes@google.com>
CC: Jiang Liu <redacted>
CC: Len Brown <redacted>
CC: Benjamin Herrenschmidt <benh@kernel.crashing.org>
CC: Paul Mackerras <redacted>
CC: Christoph Lameter <redacted>
Cc: Minchan Kim <redacted>
CC: Andrew Morton <akpm@linux-foundation.org>
CC: KOSAKI Motohiro <redacted>
CC: Yasuaki Ishimatsu <redacted>
Signed-off-by: Wen Congyang <redacted>
---
arch/ia64/mm/init.c | 18 ++++++++++++++++++
arch/powerpc/mm/mem.c | 12 ++++++++++++
arch/s390/mm/init.c | 12 ++++++++++++
arch/sh/mm/init.c | 17 +++++++++++++++++
arch/tile/mm/init.c | 8 ++++++++
arch/x86/mm/init_32.c | 12 ++++++++++++
arch/x86/mm/init_64.c | 15 +++++++++++++++
include/linux/memory_hotplug.h | 1 +
mm/memory_hotplug.c | 1 +
9 files changed, 96 insertions(+)
Index: linux-3.6/arch/ia64/mm/init.c
===================================================================
@@ -85,6 +85,7 @@ extern void __online_page_free(struct pa#ifdef CONFIG_MEMORY_HOTREMOVEexternboolis_pageblock_removable_nolock(structpage*page);+externintarch_remove_memory(u64start,u64size);#endif /* CONFIG_MEMORY_HOTREMOVE *//* reasonably generic interface to expand the physical pages in a zone */
Currently __remove_section for SPARSEMEM_VMEMMAP does nothing. But even if
we use SPARSEMEM_VMEMMAP, we can unregister the memory_section.
So the patch add unregister_memory_section() into __remove_section().
CC: David Rientjes <rientjes@google.com>
CC: Jiang Liu <redacted>
CC: Len Brown <redacted>
CC: Christoph Lameter <redacted>
Cc: Minchan Kim <redacted>
CC: Andrew Morton <akpm@linux-foundation.org>
CC: KOSAKI Motohiro <redacted>
CC: Wen Congyang <redacted>
Signed-off-by: Yasuaki Ishimatsu <redacted>
---
mm/memory_hotplug.c | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
Index: linux-3.6/mm/memory_hotplug.c
===================================================================
The function get_page_bootmem() may be called more than one time to the same
page. There is no need to set page's type, private if the function is not
the first time called to the page.
Note: the patch is just optimization and does not fix any problem.
CC: David Rientjes <rientjes@google.com>
CC: Jiang Liu <redacted>
CC: Len Brown <redacted>
CC: Christoph Lameter <redacted>
Cc: Minchan Kim <redacted>
CC: Andrew Morton <akpm@linux-foundation.org>
CC: KOSAKI Motohiro <redacted>
CC: Wen Congyang <redacted>
Signed-off-by: Yasuaki Ishimatsu <redacted>
---
mm/memory_hotplug.c | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
Index: linux-3.6/mm/memory_hotplug.c
===================================================================
For removing memmap region of sparse-vmemmap which is allocated bootmem,
memmap region of sparse-vmemmap needs to be registered by get_page_bootmem().
So the patch searches pages of virtual mapping and registers the pages by
get_page_bootmem().
Note: register_page_bootmem_memmap() is not implemented for ia64, ppc, s390,
and sparc.
CC: David Rientjes <rientjes@google.com>
CC: Jiang Liu <redacted>
CC: Len Brown <redacted>
CC: Christoph Lameter <redacted>
Cc: Minchan Kim <redacted>
CC: Andrew Morton <akpm@linux-foundation.org>
CC: KOSAKI Motohiro <redacted>
Signed-off-by: Wen Congyang <redacted>
Signed-off-by: Yasuaki Ishimatsu <redacted>
---
arch/ia64/mm/discontig.c | 6 ++++
arch/powerpc/mm/init_64.c | 6 ++++
arch/s390/mm/vmem.c | 6 ++++
arch/sparc/mm/init_64.c | 6 ++++
arch/x86/mm/init_64.c | 52 +++++++++++++++++++++++++++++++++++++++++
include/linux/memory_hotplug.h | 11 +-------
include/linux/mm.h | 3 +-
mm/memory_hotplug.c | 37 ++++++++++++++++++++++++++---
8 files changed, 113 insertions(+), 14 deletions(-)
Index: linux-3.6/include/linux/memory_hotplug.h
===================================================================
@@ -822,4 +822,10 @@ int __meminit vmemmap_populate(struct pa{returnvmemmap_populate_basepages(start_page,size,node);}++voidregister_page_bootmem_memmap(unsignedlongsection_nr,+structpage*start_page,unsignedlongsize)+{+/* TODO */+}#endif
All pages of virtual mapping in removed memory cannot be freed, since some pages
used as PGD/PUD includes not only removed memory but also other memory. So the
patch checks whether page can be freed or not.
How to check whether page can be freed or not?
1. When removing memory, the page structs of the revmoved memory are filled
with 0FD.
2. All page structs are filled with 0xFD on PT/PMD, PT/PMD can be cleared.
In this case, the page used as PT/PMD can be freed.
Applying patch, __remove_section() of CONFIG_SPARSEMEM_VMEMMAP is integrated
into one. So __remove_section() of CONFIG_SPARSEMEM_VMEMMAP is deleted.
Note: vmemmap_kfree() and vmemmap_free_bootmem() are not implemented for ia64,
ppc, s390, and sparc.
CC: David Rientjes <rientjes@google.com>
CC: Jiang Liu <redacted>
CC: Len Brown <redacted>
CC: Christoph Lameter <redacted>
Cc: Minchan Kim <redacted>
CC: Andrew Morton <akpm@linux-foundation.org>
CC: KOSAKI Motohiro <redacted>
CC: Wen Congyang <redacted>
Signed-off-by: Yasuaki Ishimatsu <redacted>
---
arch/ia64/mm/discontig.c | 8 +++
arch/powerpc/mm/init_64.c | 8 +++
arch/s390/mm/vmem.c | 8 +++
arch/sparc/mm/init_64.c | 8 +++
arch/x86/mm/init_64.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++
include/linux/mm.h | 2
mm/memory_hotplug.c | 17 ------
mm/sparse.c | 5 +
8 files changed, 158 insertions(+), 17 deletions(-)
Index: linux-3.6/arch/ia64/mm/discontig.c
===================================================================
@@ -312,19 +312,6 @@ static int __meminit __add_section(int nreturnregister_new_memory(nid,__pfn_to_section(phys_start_pfn));}-#ifdef CONFIG_SPARSEMEM_VMEMMAP-staticint__remove_section(structzone*zone,structmem_section*ms)-{-intret=-EINVAL;--if(!valid_section(ms))-returnret;--ret=unregister_memory_section(ms);--returnret;-}-#elsestaticint__remove_section(structzone*zone,structmem_section*ms){unsignedlongflags;
@@ -341,9 +328,9 @@ static int __remove_section(struct zone pgdat_resize_lock(pgdat,&flags);sparse_remove_one_section(zone,ms);pgdat_resize_unlock(pgdat,&flags);-return0;++returnret;}-#endif/**Reasonablygenericfunctionforaddingmemory.Itis
@@ -613,12 +613,13 @@ static inline struct page *kmalloc_secti/* This will make the necessary allocations eventually. */returnsparse_mem_map_populate(pnum,nid);}-staticvoid__kfree_section_memmap(structpage*memmap,unsignedlongnr_pages)+staticvoid__kfree_section_memmap(structpage*page,unsignedlongnr_pages){-return;/* XXX: Not implemented yet */+vmemmap_kfree(page,nr_pages);}staticvoidfree_map_bootmem(structpage*page,unsignedlongnr_pages){+vmemmap_free_bootmem(page,nr_pages);}#elsestaticstructpage*__kmalloc_section_memmap(unsignedlongnr_pages)
From: Wen Congyang <redacted>
For hot removing memory, we sholud remove page table about the memory.
So the patch searches a page table about the removed memory, and clear
page table.
CC: David Rientjes <rientjes@google.com>
CC: Jiang Liu <redacted>
CC: Len Brown <redacted>
CC: Christoph Lameter <redacted>
Cc: Minchan Kim <redacted>
CC: Andrew Morton <akpm@linux-foundation.org>
CC: KOSAKI Motohiro <redacted>
CC: Yasuaki Ishimatsu <redacted>
Signed-off-by: Wen Congyang <redacted>
---
arch/x86/include/asm/pgtable_types.h | 1
arch/x86/mm/init_64.c | 147 +++++++++++++++++++++++++++++++++++
arch/x86/mm/pageattr.c | 47 +++++------
3 files changed, 173 insertions(+), 22 deletions(-)
Index: linux-3.6/arch/x86/mm/init_64.c
===================================================================
When a memory is added, we update zone's and pgdat's start_pfn and
spanned_pages in the function __add_zone(). So we should revert them
when the memory is removed.
The patch adds a new function __remove_zone() to do this.
CC: David Rientjes <rientjes@google.com>
CC: Jiang Liu <redacted>
CC: Len Brown <redacted>
CC: Christoph Lameter <redacted>
Cc: Minchan Kim <redacted>
CC: Andrew Morton <akpm@linux-foundation.org>
CC: KOSAKI Motohiro <redacted>
Signed-off-by: Yasuaki Ishimatsu <redacted>
Signed-off-by: Wen Congyang <redacted>
---
mm/memory_hotplug.c | 207 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 207 insertions(+)
Index: linux-3.6/mm/memory_hotplug.c
===================================================================
@@ -312,10 +312,213 @@ static int __meminit __add_section(int nreturnregister_new_memory(nid,__pfn_to_section(phys_start_pfn));}+/* find the smallest valid pfn in the range [start_pfn, end_pfn) */+staticintfind_smallest_section_pfn(intnid,structzone*zone,+unsignedlongstart_pfn,+unsignedlongend_pfn)+{+structmem_section*ms;++for(;start_pfn<end_pfn;start_pfn+=PAGES_PER_SECTION){+ms=__pfn_to_section(start_pfn);++if(unlikely(!valid_section(ms)))+continue;++if(unlikely(pfn_to_nid(start_pfn))!=nid)+continue;++if(zone&&zone!=page_zone(pfn_to_page(start_pfn)))+continue;++returnstart_pfn;+}++return0;+}++/* find the biggest valid pfn in the range [start_pfn, end_pfn). */+staticintfind_biggest_section_pfn(intnid,structzone*zone,+unsignedlongstart_pfn,+unsignedlongend_pfn)+{+structmem_section*ms;+unsignedlongpfn;++/* pfn is the end pfn of a memory section. */+pfn=end_pfn-1;+for(;pfn>=start_pfn;pfn-=PAGES_PER_SECTION){+ms=__pfn_to_section(pfn);++if(unlikely(!valid_section(ms)))+continue;++if(unlikely(pfn_to_nid(pfn))!=nid)+continue;++if(zone&&zone!=page_zone(pfn_to_page(pfn)))+continue;++returnpfn;+}++return0;+}++staticvoidshrink_zone_span(structzone*zone,unsignedlongstart_pfn,+unsignedlongend_pfn)+{+unsignedlongzone_start_pfn=zone->zone_start_pfn;+unsignedlongzone_end_pfn=zone->zone_start_pfn+zone->spanned_pages;+unsignedlongpfn;+structmem_section*ms;+intnid=zone_to_nid(zone);++zone_span_writelock(zone);+if(zone_start_pfn==start_pfn){+/*+*Ifthesectionissmallestsectioninthezone,itneed+*shrinkzone->zone_start_pfnandzone->zone_spanned_pages.+*Inthiscase,wefindsecondsmallestvalidmem_section+*forshrinkingzone.+*/+pfn=find_smallest_section_pfn(nid,zone,end_pfn,+zone_end_pfn);+if(pfn){+zone->zone_start_pfn=pfn;+zone->spanned_pages=zone_end_pfn-pfn;+}+}elseif(zone_end_pfn==end_pfn){+/*+*Ifthesectionisbiggestsectioninthezone,itneed+*shrinkzone->spanned_pages.+*Inthiscase,wefindsecondbiggestvalidmem_sectionfor+*shrinkingzone.+*/+pfn=find_biggest_section_pfn(nid,zone,zone_start_pfn,+start_pfn);+if(pfn)+zone->spanned_pages=pfn-zone_start_pfn+1;+}++/*+*Thesectionisnotbiggestorsmallestmem_sectioninthezone,it+*onlycreatesaholeinthezone.Sointhiscase,weneednot+*changethezone.Butperhaps,thezonehasonlyholedata.Thus+*itcheckthezonehasonlyholeornot.+*/+pfn=zone_start_pfn;+for(;pfn<zone_end_pfn;pfn+=PAGES_PER_SECTION){+ms=__pfn_to_section(pfn);++if(unlikely(!valid_section(ms)))+continue;++if(page_zone(pfn_to_page(pfn))!=zone)+continue;++/* If the section is current section, it continues the loop */+if(start_pfn==pfn)+continue;++/* If we find valid section, we have nothing to do */+zone_span_writeunlock(zone);+return;+}++/* The zone has no valid section */+zone->zone_start_pfn=0;+zone->spanned_pages=0;+zone_span_writeunlock(zone);+}++staticvoidshrink_pgdat_span(structpglist_data*pgdat,+unsignedlongstart_pfn,unsignedlongend_pfn)+{+unsignedlongpgdat_start_pfn=pgdat->node_start_pfn;+unsignedlongpgdat_end_pfn=+pgdat->node_start_pfn+pgdat->node_spanned_pages;+unsignedlongpfn;+structmem_section*ms;+intnid=pgdat->node_id;++if(pgdat_start_pfn==start_pfn){+/*+*Ifthesectionissmallestsectioninthepgdat,itneed+*shrinkpgdat->node_start_pfnandpgdat->node_spanned_pages.+*Inthiscase,wefindsecondsmallestvalidmem_section+*forshrinkingzone.+*/+pfn=find_smallest_section_pfn(nid,NULL,end_pfn,+pgdat_end_pfn);+if(pfn){+pgdat->node_start_pfn=pfn;+pgdat->node_spanned_pages=pgdat_end_pfn-pfn;+}+}elseif(pgdat_end_pfn==end_pfn){+/*+*Ifthesectionisbiggestsectioninthepgdat,itneed+*shrinkpgdat->node_spanned_pages.+*Inthiscase,wefindsecondbiggestvalidmem_sectionfor+*shrinkingzone.+*/+pfn=find_biggest_section_pfn(nid,NULL,pgdat_start_pfn,+start_pfn);+if(pfn)+pgdat->node_spanned_pages=pfn-pgdat_start_pfn+1;+}++/*+*Ifthesectionisnotbiggestorsmallestmem_sectioninthepgdat,+*itonlycreatesaholeinthepgdat.Sointhiscase,weneednot+*changethepgdat.+*Butperhaps,thepgdathasonlyholedata.Thusitcheckthepgdat+*hasonlyholeornot.+*/+pfn=pgdat_start_pfn;+for(;pfn<pgdat_end_pfn;pfn+=PAGES_PER_SECTION){+ms=__pfn_to_section(pfn);++if(unlikely(!valid_section(ms)))+continue;++if(pfn_to_nid(pfn)!=nid)+continue;++/* If the section is current section, it continues the loop */+if(start_pfn==pfn)+continue;++/* If we find valid section, we have nothing to do */+return;+}++/* The pgdat has no valid section */+pgdat->node_start_pfn=0;+pgdat->node_spanned_pages=0;+}++staticvoid__remove_zone(structzone*zone,unsignedlongstart_pfn)+{+structpglist_data*pgdat=zone->zone_pgdat;+intnr_pages=PAGES_PER_SECTION;+intzone_type;+unsignedlongflags;++zone_type=zone-pgdat->node_zones;++pgdat_resize_lock(zone->zone_pgdat,&flags);+shrink_zone_span(zone,start_pfn,start_pfn+nr_pages);+shrink_pgdat_span(pgdat,start_pfn,start_pfn+nr_pages);+pgdat_resize_unlock(zone->zone_pgdat,&flags);+}+staticint__remove_section(structzone*zone,structmem_section*ms){unsignedlongflags;structpglist_data*pgdat=zone->zone_pgdat;+unsignedlongstart_pfn;+intscn_nr;intret=-EINVAL;if(!valid_section(ms))
@@ -325,6 +528,10 @@ static int __remove_section(struct zone if(ret)returnret;+scn_nr=__section_nr(ms);+start_pfn=section_nr_to_pfn(scn_nr);+__remove_zone(zone,start_pfn);+pgdat_resize_lock(pgdat,&flags);sparse_remove_one_section(zone,ms);pgdat_resize_unlock(pgdat,&flags);
From: Wen Congyang <redacted>
This patch introduces a new function try_offline_node() to
remove sysfs file of node when all memory sections of this
node are removed. If some memory sections of this node are
not removed, this function does nothing.
CC: David Rientjes <rientjes@google.com>
CC: Jiang Liu <redacted>
CC: Len Brown <redacted>
CC: Christoph Lameter <redacted>
Cc: Minchan Kim <redacted>
CC: Andrew Morton <akpm@linux-foundation.org>
CC: KOSAKI Motohiro <redacted>
CC: Yasuaki Ishimatsu <redacted>
Signed-off-by: Wen Congyang <redacted>
---
mm/memory_hotplug.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 54 insertions(+)
Index: linux-3.6/mm/memory_hotplug.c
===================================================================
@@ -1276,6 +1277,57 @@ int offline_memory(u64 start, u64 size)return0;}+staticintcheck_cpu_on_node(void*data)+{+structpglist_data*pgdat=data;+intcpu;++for_each_online_cpu(cpu){+if(cpu_to_node(cpu)==pgdat->node_id)+/*+*thecpuonthisnodeisonlined,andwecan't+*offlinethisnode.+*/+return-EBUSY;+}++return0;+}++/* offline the node if all memory sections of this node are removed */+staticvoidtry_offline_node(intnid)+{+unsignedlongstart_pfn=NODE_DATA(nid)->node_start_pfn;+unsignedlongend_pfn=start_pfn+NODE_DATA(nid)->node_spanned_pages;+unsignedlongpfn;++for(pfn=start_pfn;pfn<end_pfn;pfn+=PAGES_PER_SECTION){+unsignedlongsection_nr=pfn_to_section_nr(pfn);++if(!present_section_nr(section_nr))+continue;++if(pfn_to_nid(pfn)!=nid)+continue;++/*+*somememorysectionsofthisnodearenotremoved,andwe+*can'tofflinenodenow.+*/+return;+}++if(stop_machine(check_cpu_on_node,NODE_DATA(nid),NULL))+return;++/*+*allmemorysectionsofthisnodeareremoved,wecanofflinethis+*nodenow.+*/+node_set_offline(nid);+unregister_one_node(nid);+}+int__refremove_memory(intnid,u64start,u64size){intret=0;
Known problems:
1. memory can't be offlined when CONFIG_MEMCG is selected.
For example: there is a memory device on node 1. The address range
is [1G, 1.5G). You will find 4 new directories memory8, memory9, memory10,
and memory11 under the directory /sys/devices/system/memory/.
If CONFIG_MEMCG is selected, we will allocate memory to store page cgroup
when we online pages. When we online memory8, the memory stored page cgroup
is not provided by this memory device. But when we online memory9, the memory
stored page cgroup may be provided by memory8. So we can't offline memory8
now. We should offline the memory in the reversed order.
When the memory device is hotremoved, we will auto offline memory provided
by this memory device. But we don't know which memory is onlined first, so
offlining memory may fail. In such case, you should offline the memory by
hand before hotremoving the memory device.
Just iterate twice. 1st iterate: offline every non primary memory
block. 2nd iterate:
offline primary (i.e. first added) memory block. It may work.
On Thu, Oct 4, 2012 at 10:25 PM, Yasuaki Ishimatsu
[off-list ref] wrote:
When calling remove_memory(), the memory should be offline. If the function
is used to online memory, kernel panic may occur.
So the patch checks whether memory is offline or not.
You don't explain WHY we need the check.
quoted hunk
CC: David Rientjes <rientjes@google.com>
CC: Jiang Liu <redacted>
CC: Len Brown <redacted>
CC: Christoph Lameter <redacted>
Cc: Minchan Kim <redacted>
CC: Andrew Morton <akpm@linux-foundation.org>
CC: KOSAKI Motohiro <redacted>
Signed-off-by: Wen Congyang <redacted>
Signed-off-by: Yasuaki Ishimatsu <redacted>
---
drivers/base/memory.c | 39 +++++++++++++++++++++++++++++++++++++++
include/linux/memory.h | 5 +++++
mm/memory_hotplug.c | 17 +++++++++++++++--
3 files changed, 59 insertions(+), 2 deletions(-)
Index: linux-3.6/drivers/base/memory.c
===================================================================
Don't use memblk. Usually memblk mean struct numa_meminfo for x86/numa.
Maybe memory_range_offlined() is better.
And, this function don't take struct memory_block, then this file may be no good
place.
And you need to write down function comment.
quoted hunk
+{
+ struct memory_block *mem = NULL;
+ struct mem_section *section;
+ unsigned long start_pfn, end_pfn;
+ unsigned long pfn, section_nr;
+
+ start_pfn = PFN_DOWN(start);
+ end_pfn = PFN_UP(start + size);
+
+ for (pfn = start_pfn; pfn < end_pfn; pfn += PAGES_PER_SECTION) {
+ section_nr = pfn_to_section_nr(pfn);
+ if (!present_section_nr(section_nr))
+ continue;
+
+ section = __nr_to_section(section_nr);
+ /* same memblock? */
+ if (mem)
+ if ((section_nr >= mem->start_section_nr) &&
+ (section_nr <= mem->end_section_nr))
+ continue;
+
+ mem = find_memory_block_hinted(section, mem);
+ if (!mem)
+ continue;
+ if (mem->state == MEM_OFFLINE)
+ continue;
+
+ kobject_put(&mem->dev.kobj);
+ return false;
+ }
+
+ if (mem)
+ kobject_put(&mem->dev.kobj);
+
+ return true;
+}
+EXPORT_SYMBOL(is_memblk_offline);
+
/*
* register_memory - Setup a sysfs device for a memory block
*/
Index: linux-3.6/include/linux/memory.h
===================================================================
@@ -1045,8 +1045,21 @@ int offline_memory(u64 start, u64 size)intremove_memory(intnid,u64start,u64size){
Your remove_memory() don't remove anything. that's strange.
- /* It is not implemented yet*/
- return 0;
+ int ret = 0;
+ lock_memory_hotplug();
+ /*
+ * The memory might become online by other task, even if you offine it.
+ * So we check whether the memory has been onlined or not.
+ */
+ if (!is_memblk_offline(start, size)) {
+ pr_warn("memory removing [mem %#010llx-%#010llx] failed, "
+ "because the memmory range is online\n",
+ start, start + size);
No good warning. You should output which memory block can't be
offlined, I think.
+ ret = -EAGAIN;
+ }
+
+ unlock_memory_hotplug();
+ return ret;
}
EXPORT_SYMBOL_GPL(remove_memory);
#else
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
On Thu, Oct 4, 2012 at 10:26 PM, Yasuaki Ishimatsu
[off-list ref] wrote:
When (hot)adding memory into system, /sys/firmware/memmap/X/{end, start, type}
sysfs files are created. But there is no code to remove these files. The patch
implements the function to remove them.
Note : The code does not free firmware_map_entry since there is no way to free
memory which is allocated by bootmem.
You have to explain why this is ok. I guess the unfreed
firmware_map_entry is reused
at next online memory and don't make memory leak, right?
quoted hunk
CC: David Rientjes <rientjes@google.com>
CC: Jiang Liu <redacted>
CC: Len Brown <redacted>
CC: Christoph Lameter <redacted>
Cc: Minchan Kim <redacted>
CC: Andrew Morton <akpm@linux-foundation.org>
CC: KOSAKI Motohiro <redacted>
Signed-off-by: Wen Congyang <redacted>
Signed-off-by: Yasuaki Ishimatsu <redacted>
---
drivers/firmware/memmap.c | 98 ++++++++++++++++++++++++++++++++++++++++++-
include/linux/firmware-map.h | 6 ++
mm/memory_hotplug.c | 7 ++-
3 files changed, 108 insertions(+), 3 deletions(-)
Index: linux-3.6/drivers/firmware/memmap.c
===================================================================
@@ -41,6 +42,7 @@ struct firmware_map_entry {constchar*type;/* type of the memory range */structlist_headlist;/* entry for the linked list */structkobjectkobj;/* kobject for each entry */+unsignedintbootmem:1;/* allocated from bootmem */
Use bool.
quoted hunk
};
/*
@@ -79,7 +81,26 @@ static const struct sysfs_ops memmap_att .show = memmap_attr_show, };++static inline struct firmware_map_entry *+to_memmap_entry(struct kobject *kobj)+{+ return container_of(kobj, struct firmware_map_entry, kobj);+}++static void release_firmware_map_entry(struct kobject *kobj)+{+ struct firmware_map_entry *entry = to_memmap_entry(kobj);++ if (entry->bootmem)+ /* There is no way to free memory allocated from bootmem */+ return;++ kfree(entry);+}+ static struct kobj_type memmap_ktype = {+ .release = release_firmware_map_entry, .sysfs_ops = &memmap_attr_ops, .default_attrs = def_attrs, };
@@ -94,6 +115,7 @@ static struct kobj_type memmap_ktype = { * in firmware initialisation code in one single thread of execution. */ static LIST_HEAD(map_entries);+static DEFINE_SPINLOCK(map_entries_lock); /** * firmware_map_add_entry() - Does the real work to add a firmware memmap entry.
@@ -118,11 +140,25 @@ static int firmware_map_add_entry(u64 st INIT_LIST_HEAD(&entry->list); kobject_init(&entry->kobj, &memmap_ktype);+ spin_lock(&map_entries_lock); list_add_tail(&entry->list, &map_entries);+ spin_unlock(&map_entries_lock); return 0; }+/**+ * firmware_map_remove_entry() - Does the real work to remove a firmware+ * memmap entry.+ * @entry: removed entry.+ **/+static inline void firmware_map_remove_entry(struct firmware_map_entry *entry)
Don't use inline in *.c file. gcc is wise than you.
@@ -193,9 +258,36 @@ int __init firmware_map_add_early(u64 st if (WARN_ON(!entry)) return -ENOMEM;+ entry->bootmem = 1; return firmware_map_add_entry(start, end, type, entry); }+/**+ * firmware_map_remove() - remove a firmware mapping entry+ * @start: Start of the memory range.+ * @end: End of the memory range.+ * @type: Type of the memory range.+ *+ * removes a firmware mapping entry.+ *+ * Returns 0 on success, or -EINVAL if no entry.+ **/+int __meminit firmware_map_remove(u64 start, u64 end, const char *type)
Remove type argument if this is always passed "System RAM".
@@ -1043,7 +1043,7 @@ int offline_memory(u64 start, u64 size)return0;}-intremove_memory(intnid,u64start,u64size)+int__refremove_memory(intnid,u64start,u64size){intret=0;lock_memory_hotplug();
@@ -1056,8 +1056,13 @@ int remove_memory(int nid, u64 start, u6"because the memmory range is online\n",start,start+size);ret=-EAGAIN;+gotoout;}+/* remove memmap entry */+firmware_map_remove(start,start+size,"System RAM");++out:unlock_memory_hotplug();returnret;}
Known problems:
1. memory can't be offlined when CONFIG_MEMCG is selected.
For example: there is a memory device on node 1. The address range
is [1G, 1.5G). You will find 4 new directories memory8, memory9, memory10,
and memory11 under the directory /sys/devices/system/memory/.
If CONFIG_MEMCG is selected, we will allocate memory to store page cgroup
when we online pages. When we online memory8, the memory stored page cgroup
is not provided by this memory device. But when we online memory9, the memory
stored page cgroup may be provided by memory8. So we can't offline memory8
now. We should offline the memory in the reversed order.
When the memory device is hotremoved, we will auto offline memory provided
by this memory device. But we don't know which memory is onlined first, so
offlining memory may fail. In such case, you should offline the memory by
hand before hotremoving the memory device.
Just iterate twice. 1st iterate: offline every non primary memory
block. 2nd iterate:
offline primary (i.e. first added) memory block. It may work.
Hi Congyang,
I think we should also free pages which are used by page tables after removing
page tables of the memory.
From: Jianguo Wu <redacted>
Signed-off-by: Jianguo Wu <redacted>
Signed-off-by: Jiang Liu <redacted>
---
arch/x86/mm/init_64.c | 110 +++++++++++++++++++++++++++++++++++++++---------
1 files changed, 89 insertions(+), 21 deletions(-)
@@ -675,6 +675,74 @@ int arch_add_memory(int nid, u64 start, u64 size)}EXPORT_SYMBOL_GPL(arch_add_memory);+staticinlinevoidfree_pagetable(structpage*page)+{+structzone*zone;++__ClearPageReserved(page);+__free_page(page);++zone=page_zone(page);+zone_span_writelock(zone);+zone->present_pages++;+zone_span_writeunlock(zone);+totalram_pages++;+}++staticvoidfree_pte_table(pte_t*pte_start,pmd_t*pmd)+{+pte_t*pte;+inti;++for(i=0;i<PTRS_PER_PTE;i++){+pte=pte_start+i;+if(pte_val(*pte))+break;+}++/* free a pte talbe */+if(i==PTRS_PER_PTE){+free_pagetable(pmd_page(*pmd));+pmd_clear(pmd);+}+}++staticvoidfree_pmd_table(pmd_t*pmd_start,pud_t*pud)+{+pmd_t*pmd;+inti;++for(i=0;i<PTRS_PER_PMD;i++){+pmd=pmd_start+i;+if(pmd_val(*pmd))+break;+}++/* free a pmd talbe */+if(i==PTRS_PER_PMD){+free_pagetable(pud_page(*pud));+pud_clear(pud);+}+}++staticvoidfree_pud_table(pud_t*pud_start,pgd_t*pgd)+{+pud_t*pud;+inti;++for(i=0;i<PTRS_PER_PUD;i++){+pud=pud_start+i;+if(pud_val(*pud))+break;+}++/* free a pud table */+if(i==PTRS_PER_PUD){+free_pagetable(pgd_page(*pgd));+pgd_clear(pgd);+}+}+staticvoid__meminitphys_pte_remove(pte_t*pte_page,unsignedlongaddr,unsignedlongend){
@@ -704,21 +772,19 @@ phys_pmd_remove(pmd_t *pmd_page, unsigned long addr, unsigned long end)unsignedlongpages=0,next;inti=pmd_index(addr);-for(;i<PTRS_PER_PMD;i++,addr=next){+for(;i<PTRS_PER_PMD&&addr<end;i++,addr=next){unsignedlongpte_phys;pmd_t*pmd=pmd_page+pmd_index(addr);pte_t*pte;-if(addr>=end)-break;--next=(addr&PMD_MASK)+PMD_SIZE;+next=pmd_addr_end(addr,end);if(!pmd_present(*pmd))continue;if(pmd_large(*pmd)){-if((addr&~PMD_MASK)==0&&next<=end){+if(IS_ALIGNED(addr,PMD_SIZE)&&+IS_ALIGNED(next,PMD_SIZE)){set_pmd(pmd,__pmd(0));pages++;continue;
@@ -729,7 +795,8 @@ phys_pmd_remove(pmd_t *pmd_page, unsigned long addr, unsigned long end)*sosplit2Mpageto4Kpage.*/pte=alloc_low_page(&pte_phys);-__split_large_page((pte_t*)pmd,addr,pte);+__split_large_page((pte_t*)pmd,+(unsignedlong)__va(addr),pte);spin_lock(&init_mm.page_table_lock);pmd_populate_kernel(&init_mm,pmd,__va(pte_phys));
@@ -738,7 +805,8 @@ phys_pmd_remove(pmd_t *pmd_page, unsigned long addr, unsigned long end)spin_lock(&init_mm.page_table_lock);pte=map_low_page((pte_t*)pmd_page_vaddr(*pmd));-phys_pte_remove(pte,addr,end);+phys_pte_remove(pte,addr,next);+free_pte_table(pte,pmd);unmap_low_page(pte);spin_unlock(&init_mm.page_table_lock);}
@@ -751,21 +819,19 @@ phys_pud_remove(pud_t *pud_page, unsigned long addr, unsigned long end)unsignedlongpages=0,next;inti=pud_index(addr);-for(;i<PTRS_PER_PUD;i++,addr=next){+for(;i<PTRS_PER_PUD&&addr<end;i++,addr=next){unsignedlongpmd_phys;pud_t*pud=pud_page+pud_index(addr);pmd_t*pmd;-if(addr>=end)-break;--next=(addr&PUD_MASK)+PUD_SIZE;+next=pud_addr_end(addr,end);if(!pud_present(*pud))continue;if(pud_large(*pud)){-if((addr&~PUD_MASK)==0&&next<=end){+if(IS_ALIGNED(addr,PUD_SIZE)&&+IS_ALIGNED(next,PUD_SIZE)){set_pud(pud,__pud(0));pages++;continue;
@@ -776,15 +842,18 @@ phys_pud_remove(pud_t *pud_page, unsigned long addr, unsigned long end)*sosplit1Gpageto2Mpage.*/pmd=alloc_low_page(&pmd_phys);-__split_large_page((pte_t*)pud,addr,(pte_t*)pmd);+__split_large_page((pte_t*)pud,+(unsignedlong)__va(addr),+(pte_t*)pmd);spin_lock(&init_mm.page_table_lock);pud_populate(&init_mm,pud,__va(pmd_phys));spin_unlock(&init_mm.page_table_lock);}-pmd=map_low_page(pmd_offset(pud,0));-phys_pmd_remove(pmd,addr,end);+pmd=map_low_page((pmd_t*)pud_page_vaddr(*pud));+phys_pmd_remove(pmd,addr,next);+free_pmd_table(pmd,pud);unmap_low_page(pmd);__flush_tlb_all();}
@@ -805,15 +874,14 @@ kernel_physical_mapping_remove(unsigned long start, unsigned long end)pgd_t*pgd=pgd_offset_k(start);pud_t*pud;-next=(start+PGDIR_SIZE)&PGDIR_MASK;-if(next>end)-next=end;+next=pgd_addr_end(start,end);if(!pgd_present(*pgd))continue;pud=map_low_page((pud_t*)pgd_page_vaddr(*pgd));-phys_pud_remove(pud,__pa(start),__pa(end));+phys_pud_remove(pud,__pa(start),__pa(next));+free_pud_table(pud,pgd);unmap_low_page(pud);}--1.7.6.1.
On 2012-10-5 10:36, Yasuaki Ishimatsu wrote:
quoted hunk
From: Wen Congyang <redacted>
For hot removing memory, we sholud remove page table about the memory.
So the patch searches a page table about the removed memory, and clear
page table.
CC: David Rientjes <rientjes@google.com>
CC: Jiang Liu <redacted>
CC: Len Brown <redacted>
CC: Christoph Lameter <redacted>
Cc: Minchan Kim <redacted>
CC: Andrew Morton <akpm@linux-foundation.org>
CC: KOSAKI Motohiro <redacted>
CC: Yasuaki Ishimatsu <redacted>
Signed-off-by: Wen Congyang <redacted>
---
arch/x86/include/asm/pgtable_types.h | 1
arch/x86/mm/init_64.c | 147 +++++++++++++++++++++++++++++++++++
arch/x86/mm/pageattr.c | 47 +++++------
3 files changed, 173 insertions(+), 22 deletions(-)
Index: linux-3.6/arch/x86/mm/init_64.c
===================================================================
@@ -523,10 +515,11 @@ static int split_large_page(pte_t *kpte,*upforusalready:*/tmp=lookup_address(address,&level);-if(tmp!=kpte)-gotoout_unlock;+if(tmp!=kpte){+spin_unlock(&pgd_lock);+return1;+}-pbase=(pte_t*)page_address(base);paravirt_alloc_pte(&init_mm,page_to_pfn(base));ref_prot=pte_pgprot(pte_clrhuge(*kpte));/*
@@ -579,17 +572,27 @@ static int split_large_page(pte_t *kpte,*goingon.*/__flush_tlb_all();+spin_unlock(&pgd_lock);-base=NULL;+return0;+}-out_unlock:-/*-*Ifwedroppedoutviathelookup_addresscheckunder-*pgd_lockthenstickthepagebackintothepool:-*/-if(base)+staticintsplit_large_page(pte_t*kpte,unsignedlongaddress)+{+pte_t*pbase;+structpage*base;++if(!debug_pagealloc)+spin_unlock(&cpa_lock);+base=alloc_pages(GFP_KERNEL|__GFP_NOTRACK,0);+if(!debug_pagealloc)+spin_lock(&cpa_lock);+if(!base)+return-ENOMEM;++pbase=(pte_t*)page_address(base);+if(__split_large_page(kpte,address,pbase))__free_page(base);-spin_unlock(&pgd_lock);return0;}--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
On Thu, Oct 4, 2012 at 10:26 PM, Yasuaki Ishimatsu
[off-list ref] wrote:
quoted
When (hot)adding memory into system, /sys/firmware/memmap/X/{end, start, type}
sysfs files are created. But there is no code to remove these files. The patch
implements the function to remove them.
Note : The code does not free firmware_map_entry since there is no way to free
memory which is allocated by bootmem.
You have to explain why this is ok. I guess the unfreed
firmware_map_entry is reused
at next online memory and don't make memory leak, right?
Unfortunately, it is no. It makes memory leak about firmware_map_entry size.
If we hot add memory, slab allocater prepares a other memory for
firmware_map_entry.
In my understanding, if the memory is allocated by bootmem allocator,
the memory is not managed by slab allocator. So we can not use kfree()
against the memory.
On the other hand, the page of the memory may have various data allocalted
by bootmem allocater with the exception of the firmware_map_entry. Thus we
cannot free the page.
So the patch makes memory leak. But I think the memory leak size is
very samll. And it does not affect the system.
quoted
CC: David Rientjes <rientjes@google.com>
CC: Jiang Liu <redacted>
CC: Len Brown <redacted>
CC: Christoph Lameter <redacted>
Cc: Minchan Kim <redacted>
CC: Andrew Morton <akpm@linux-foundation.org>
CC: KOSAKI Motohiro <redacted>
Signed-off-by: Wen Congyang <redacted>
Signed-off-by: Yasuaki Ishimatsu <redacted>
---
drivers/firmware/memmap.c | 98 ++++++++++++++++++++++++++++++++++++++++++-
include/linux/firmware-map.h | 6 ++
mm/memory_hotplug.c | 7 ++-
3 files changed, 108 insertions(+), 3 deletions(-)
Index: linux-3.6/drivers/firmware/memmap.c
===================================================================
@@ -41,6 +42,7 @@ struct firmware_map_entry {constchar*type;/* type of the memory range */structlist_headlist;/* entry for the linked list */structkobjectkobj;/* kobject for each entry */+unsignedintbootmem:1;/* allocated from bootmem */
Use bool.
We'll update it.
quoted
};
/*
@@ -79,7 +81,26 @@ static const struct sysfs_ops memmap_att .show = memmap_attr_show, };++static inline struct firmware_map_entry *+to_memmap_entry(struct kobject *kobj)+{+ return container_of(kobj, struct firmware_map_entry, kobj);+}++static void release_firmware_map_entry(struct kobject *kobj)+{+ struct firmware_map_entry *entry = to_memmap_entry(kobj);++ if (entry->bootmem)+ /* There is no way to free memory allocated from bootmem */+ return;++ kfree(entry);+}+ static struct kobj_type memmap_ktype = {+ .release = release_firmware_map_entry, .sysfs_ops = &memmap_attr_ops, .default_attrs = def_attrs, };
@@ -94,6 +115,7 @@ static struct kobj_type memmap_ktype = { * in firmware initialisation code in one single thread of execution. */ static LIST_HEAD(map_entries);+static DEFINE_SPINLOCK(map_entries_lock); /** * firmware_map_add_entry() - Does the real work to add a firmware memmap entry.
@@ -118,11 +140,25 @@ static int firmware_map_add_entry(u64 st INIT_LIST_HEAD(&entry->list); kobject_init(&entry->kobj, &memmap_ktype);+ spin_lock(&map_entries_lock); list_add_tail(&entry->list, &map_entries);+ spin_unlock(&map_entries_lock); return 0; }+/**+ * firmware_map_remove_entry() - Does the real work to remove a firmware+ * memmap entry.+ * @entry: removed entry.+ **/+static inline void firmware_map_remove_entry(struct firmware_map_entry *entry)
Don't use inline in *.c file. gcc is wise than you.
@@ -193,9 +258,36 @@ int __init firmware_map_add_early(u64 st if (WARN_ON(!entry)) return -ENOMEM;+ entry->bootmem = 1; return firmware_map_add_entry(start, end, type, entry); }+/**+ * firmware_map_remove() - remove a firmware mapping entry+ * @start: Start of the memory range.+ * @end: End of the memory range.+ * @type: Type of the memory range.+ *+ * removes a firmware mapping entry.+ *+ * Returns 0 on success, or -EINVAL if no entry.+ **/+int __meminit firmware_map_remove(u64 start, u64 end, const char *type)
Remove type argument if this is always passed "System RAM".
Probably, the type is always "System RAM". But we need to check whether
that the range of start and end variables are "System RAM" or not.
So I want to keep it.
Thanks,
Yasuaki Ishimatsu
@@ -1043,7 +1043,7 @@ int offline_memory(u64 start, u64 size)return0;}-intremove_memory(intnid,u64start,u64size)+int__refremove_memory(intnid,u64start,u64size){intret=0;lock_memory_hotplug();
@@ -1056,8 +1056,13 @@ int remove_memory(int nid, u64 start, u6"because the memmory range is online\n",start,start+size);ret=-EAGAIN;+gotoout;}+/* remove memmap entry */+firmware_map_remove(start,start+size,"System RAM");++out:unlock_memory_hotplug();returnret;}
On Thu, Oct 4, 2012 at 10:32 PM, Yasuaki Ishimatsu
[off-list ref] wrote:
quoted hunk
The function get_page_bootmem() may be called more than one time to the same
page. There is no need to set page's type, private if the function is not
the first time called to the page.
Note: the patch is just optimization and does not fix any problem.
CC: David Rientjes <rientjes@google.com>
CC: Jiang Liu <redacted>
CC: Len Brown <redacted>
CC: Christoph Lameter <redacted>
Cc: Minchan Kim <redacted>
CC: Andrew Morton <akpm@linux-foundation.org>
CC: KOSAKI Motohiro <redacted>
CC: Wen Congyang <redacted>
Signed-off-by: Yasuaki Ishimatsu <redacted>
---
mm/memory_hotplug.c | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
Index: linux-3.6/mm/memory_hotplug.c
===================================================================
If I understand correctly, page->lru.next might be uninitialized yet.
Moreover, I have no seen any good effect in this patch. I don't understand
why we need to increase code complexity.
+ if (page_type < MEMORY_HOTPLUG_MIN_BOOTMEM_TYPE ||
+ page_type > MEMORY_HOTPLUG_MAX_BOOTMEM_TYPE){
+ page->lru.next = (struct list_head *)type;
+ SetPagePrivate(page);
+ set_page_private(page, info);
+ atomic_inc(&page->_count);
+ } else
+ atomic_inc(&page->_count);
}
/* reference to __meminit __free_pages_bootmem is valid
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Hi Kosaki,
Sorry for late reply.
2012/10/13 4:28, KOSAKI Motohiro wrote:
On Thu, Oct 4, 2012 at 10:32 PM, Yasuaki Ishimatsu
[off-list ref] wrote:
quoted
The function get_page_bootmem() may be called more than one time to the same
page. There is no need to set page's type, private if the function is not
the first time called to the page.
Note: the patch is just optimization and does not fix any problem.
CC: David Rientjes <rientjes@google.com>
CC: Jiang Liu <redacted>
CC: Len Brown <redacted>
CC: Christoph Lameter <redacted>
Cc: Minchan Kim <redacted>
CC: Andrew Morton <akpm@linux-foundation.org>
CC: KOSAKI Motohiro <redacted>
CC: Wen Congyang <redacted>
Signed-off-by: Yasuaki Ishimatsu <redacted>
---
mm/memory_hotplug.c | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
Index: linux-3.6/mm/memory_hotplug.c
===================================================================
If I understand correctly, page->lru.next might be uninitialized yet.
Ah yes. I was misunderstanding...
Hi Wen,
When you update the physical hot remove patch-set, please drop the patch.
Thanks,
Yasuaki Ishimatsu
Moreover, I have no seen any good effect in this patch. I don't understand
why we need to increase code complexity.
quoted
+ if (page_type < MEMORY_HOTPLUG_MIN_BOOTMEM_TYPE ||
+ page_type > MEMORY_HOTPLUG_MAX_BOOTMEM_TYPE){
+ page->lru.next = (struct list_head *)type;
+ SetPagePrivate(page);
+ set_page_private(page, info);
+ atomic_inc(&page->_count);
+ } else
+ atomic_inc(&page->_count);
}
/* reference to __meminit __free_pages_bootmem is valid
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Hi Kosaki,
Sorry for late reply.
2012/10/13 4:28, KOSAKI Motohiro wrote:
quoted
On Thu, Oct 4, 2012 at 10:32 PM, Yasuaki Ishimatsu
[off-list ref] wrote:
quoted
The function get_page_bootmem() may be called more than one time to
the same
page. There is no need to set page's type, private if the function is
not
the first time called to the page.
Note: the patch is just optimization and does not fix any problem.
CC: David Rientjes <rientjes@google.com>
CC: Jiang Liu <redacted>
CC: Len Brown <redacted>
CC: Christoph Lameter <redacted>
Cc: Minchan Kim <redacted>
CC: Andrew Morton <akpm@linux-foundation.org>
CC: KOSAKI Motohiro <redacted>
CC: Wen Congyang <redacted>
Signed-off-by: Yasuaki Ishimatsu <redacted>
---
mm/memory_hotplug.c | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
Index: linux-3.6/mm/memory_hotplug.c
===================================================================
If I understand correctly, page->lru.next might be uninitialized yet.
Ah yes. I was misunderstanding...
Hi Wen,
When you update the physical hot remove patch-set, please drop the patch.
OK
Thanks
Wen Congyang
Thanks,
Yasuaki Ishimatsu
quoted
Moreover, I have no seen any good effect in this patch. I don't
understand
why we need to increase code complexity.
quoted
+ if (page_type < MEMORY_HOTPLUG_MIN_BOOTMEM_TYPE ||
+ page_type > MEMORY_HOTPLUG_MAX_BOOTMEM_TYPE){
+ page->lru.next = (struct list_head *)type;
+ SetPagePrivate(page);
+ set_page_private(page, info);
+ atomic_inc(&page->_count);
+ } else
+ atomic_inc(&page->_count);
}
/* reference to __meminit __free_pages_bootmem is valid
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
On Thu, Oct 4, 2012 at 10:25 PM, Yasuaki Ishimatsu
[off-list ref] wrote:
quoted
When calling remove_memory(), the memory should be offline. If the function
is used to online memory, kernel panic may occur.
So the patch checks whether memory is offline or not.
You don't explain WHY we need the check.
This patch is no necessary now, because the newest kernel has checked
it.
Thanks
Wen Congyang
quoted
CC: David Rientjes <rientjes@google.com>
CC: Jiang Liu <redacted>
CC: Len Brown <redacted>
CC: Christoph Lameter <redacted>
Cc: Minchan Kim <redacted>
CC: Andrew Morton <akpm@linux-foundation.org>
CC: KOSAKI Motohiro <redacted>
Signed-off-by: Wen Congyang <redacted>
Signed-off-by: Yasuaki Ishimatsu <redacted>
---
drivers/base/memory.c | 39 +++++++++++++++++++++++++++++++++++++++
include/linux/memory.h | 5 +++++
mm/memory_hotplug.c | 17 +++++++++++++++--
3 files changed, 59 insertions(+), 2 deletions(-)
Index: linux-3.6/drivers/base/memory.c
===================================================================
Don't use memblk. Usually memblk mean struct numa_meminfo for x86/numa.
Maybe memory_range_offlined() is better.
And, this function don't take struct memory_block, then this file may be no good
place.
And you need to write down function comment.
quoted
+{
+ struct memory_block *mem = NULL;
+ struct mem_section *section;
+ unsigned long start_pfn, end_pfn;
+ unsigned long pfn, section_nr;
+
+ start_pfn = PFN_DOWN(start);
+ end_pfn = PFN_UP(start + size);
+
+ for (pfn = start_pfn; pfn < end_pfn; pfn += PAGES_PER_SECTION) {
+ section_nr = pfn_to_section_nr(pfn);
+ if (!present_section_nr(section_nr))
+ continue;
+
+ section = __nr_to_section(section_nr);
+ /* same memblock? */
+ if (mem)
+ if ((section_nr >= mem->start_section_nr) &&
+ (section_nr <= mem->end_section_nr))
+ continue;
+
+ mem = find_memory_block_hinted(section, mem);
+ if (!mem)
+ continue;
+ if (mem->state == MEM_OFFLINE)
+ continue;
+
+ kobject_put(&mem->dev.kobj);
+ return false;
+ }
+
+ if (mem)
+ kobject_put(&mem->dev.kobj);
+
+ return true;
+}
+EXPORT_SYMBOL(is_memblk_offline);
+
/*
* register_memory - Setup a sysfs device for a memory block
*/
Index: linux-3.6/include/linux/memory.h
===================================================================
@@ -1045,8 +1045,21 @@ int offline_memory(u64 start, u64 size)intremove_memory(intnid,u64start,u64size){
Your remove_memory() don't remove anything. that's strange.
quoted
- /* It is not implemented yet*/
- return 0;
+ int ret = 0;
+ lock_memory_hotplug();
+ /*
+ * The memory might become online by other task, even if you offine it.
+ * So we check whether the memory has been onlined or not.
+ */
+ if (!is_memblk_offline(start, size)) {
+ pr_warn("memory removing [mem %#010llx-%#010llx] failed, "
+ "because the memmory range is online\n",
+ start, start + size);
No good warning. You should output which memory block can't be
offlined, I think.
quoted
+ ret = -EAGAIN;
+ }
+
+ unlock_memory_hotplug();
+ return ret;
}
EXPORT_SYMBOL_GPL(remove_memory);
#else
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
On Thu, Oct 4, 2012 at 10:25 PM, Yasuaki Ishimatsu
[off-list ref] wrote:
quoted
When calling remove_memory(), the memory should be offline. If the function
is used to online memory, kernel panic may occur.
So the patch checks whether memory is offline or not.
You don't explain WHY we need the check.
This patch is no necessary now, because the newest kernel has checked
it.
I think it again, and found that this check is necessary. Because we only
lock memory hotplug when offlining pages. Here is the steps to offline and
remove memory:
1. lock memory hotplug
2. offline a memory section
3. unlock memory hotplug
4. repeat 1-3 to offline all memory sections
5. lock memory hotplug
6. remove memory
7. unlock memory hotplug
All memory sections must be offlined before removing memory. But we
don't hold
the lock in the whole operation. So we should check whether all memory
sections
are offlined before step6.
Don't use memblk. Usually memblk mean struct numa_meminfo for x86/numa.
Maybe memory_range_offlined() is better.
And, this function don't take struct memory_block, then this file may be no good
place.
And you need to write down function comment.
quoted
+{
+ struct memory_block *mem = NULL;
+ struct mem_section *section;
+ unsigned long start_pfn, end_pfn;
+ unsigned long pfn, section_nr;
+
+ start_pfn = PFN_DOWN(start);
+ end_pfn = PFN_UP(start + size);
+
+ for (pfn = start_pfn; pfn< end_pfn; pfn += PAGES_PER_SECTION) {
+ section_nr = pfn_to_section_nr(pfn);
+ if (!present_section_nr(section_nr))
+ continue;
+
+ section = __nr_to_section(section_nr);
+ /* same memblock? */
+ if (mem)
+ if ((section_nr>= mem->start_section_nr)&&
+ (section_nr<= mem->end_section_nr))
+ continue;
+
+ mem = find_memory_block_hinted(section, mem);
+ if (!mem)
+ continue;
+ if (mem->state == MEM_OFFLINE)
+ continue;
+
+ kobject_put(&mem->dev.kobj);
+ return false;
+ }
+
+ if (mem)
+ kobject_put(&mem->dev.kobj);
+
+ return true;
+}
+EXPORT_SYMBOL(is_memblk_offline);
+
/*
* register_memory - Setup a sysfs device for a memory block
*/
Index: linux-3.6/include/linux/memory.h
===================================================================
@@ -1045,8 +1045,21 @@ int offline_memory(u64 start, u64 size)intremove_memory(intnid,u64start,u64size){
Your remove_memory() don't remove anything. that's strange.
IIUC, this batch is based on another patchset.
quoted
quoted
- /* It is not implemented yet*/
- return 0;
+ int ret = 0;
+ lock_memory_hotplug();
+ /*
+ * The memory might become online by other task, even if you offine it.
+ * So we check whether the memory has been onlined or not.
+ */
+ if (!is_memblk_offline(start, size)) {
+ pr_warn("memory removing [mem %#010llx-%#010llx] failed, "
+ "because the memmory range is online\n",
+ start, start + size);
No good warning. You should output which memory block can't be
offlined, I think.
OK. I'll update it.
Thanks
Wen Congyang
quoted
quoted
+ ret = -EAGAIN;
+ }
+
+ unlock_memory_hotplug();
+ return ret;
}
EXPORT_SYMBOL_GPL(remove_memory);
#else
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email:<a href=mailto:"dont@kvack.org"> email@kvack.org</a>
--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
I think it again, and found that this check is necessary. Because we only
lock memory hotplug when offlining pages. Here is the steps to offline and
remove memory:
1. lock memory hotplug
2. offline a memory section
3. unlock memory hotplug
4. repeat 1-3 to offline all memory sections
5. lock memory hotplug
6. remove memory
7. unlock memory hotplug
All memory sections must be offlined before removing memory. But we don't
hold
the lock in the whole operation. So we should check whether all memory
sections
are offlined before step6.
You should describe the race scenario in the patch description. OK?
I think it again, and found that this check is necessary. Because we only
lock memory hotplug when offlining pages. Here is the steps to offline and
remove memory:
1. lock memory hotplug
2. offline a memory section
3. unlock memory hotplug
4. repeat 1-3 to offline all memory sections
5. lock memory hotplug
6. remove memory
7. unlock memory hotplug
All memory sections must be offlined before removing memory. But we don't
hold
the lock in the whole operation. So we should check whether all memory
sections
are offlined before step6.
You should describe the race scenario in the patch description. OK?
@@ -805,15 +874,14 @@ kernel_physical_mapping_remove(unsigned long start, unsigned long end) pgd_t *pgd = pgd_offset_k(start); pud_t *pud;- next = (start + PGDIR_SIZE) & PGDIR_MASK;- if (next > end)- next = end;+ next = pgd_addr_end(start, end); if (!pgd_present(*pgd)) continue; pud = map_low_page((pud_t *)pgd_page_vaddr(*pgd));- phys_pud_remove(pud, __pa(start), __pa(end));+ phys_pud_remove(pud, __pa(start), __pa(next));+ free_pud_table(pud, pgd); unmap_low_page(pud); }-- 1.7.6.1 .
On 2012-10-5 10:36, Yasuaki Ishimatsu wrote:
quoted
From: Wen Congyang <redacted>
For hot removing memory, we sholud remove page table about the memory.
So the patch searches a page table about the removed memory, and clear
page table.
CC: David Rientjes <rientjes@google.com>
CC: Jiang Liu <redacted>
CC: Len Brown <redacted>
CC: Christoph Lameter <redacted>
Cc: Minchan Kim <redacted>
CC: Andrew Morton <akpm@linux-foundation.org>
CC: KOSAKI Motohiro <redacted>
CC: Yasuaki Ishimatsu <redacted>
Signed-off-by: Wen Congyang <redacted>
---
arch/x86/include/asm/pgtable_types.h | 1
arch/x86/mm/init_64.c | 147 +++++++++++++++++++++++++++++++++++
arch/x86/mm/pageattr.c | 47 +++++------
3 files changed, 173 insertions(+), 22 deletions(-)
Index: linux-3.6/arch/x86/mm/init_64.c
===================================================================
@@ -523,10 +515,11 @@ static int split_large_page(pte_t *kpte,*upforusalready:*/tmp=lookup_address(address,&level);-if(tmp!=kpte)-gotoout_unlock;+if(tmp!=kpte){+spin_unlock(&pgd_lock);+return1;+}-pbase=(pte_t*)page_address(base);paravirt_alloc_pte(&init_mm,page_to_pfn(base));ref_prot=pte_pgprot(pte_clrhuge(*kpte));/*
@@ -579,17 +572,27 @@ static int split_large_page(pte_t *kpte,*goingon.*/__flush_tlb_all();+spin_unlock(&pgd_lock);-base=NULL;+return0;+}-out_unlock:-/*-*Ifwedroppedoutviathelookup_addresscheckunder-*pgd_lockthenstickthepagebackintothepool:-*/-if(base)+staticintsplit_large_page(pte_t*kpte,unsignedlongaddress)+{+pte_t*pbase;+structpage*base;++if(!debug_pagealloc)+spin_unlock(&cpa_lock);+base=alloc_pages(GFP_KERNEL|__GFP_NOTRACK,0);+if(!debug_pagealloc)+spin_lock(&cpa_lock);+if(!base)+return-ENOMEM;++pbase=(pte_t*)page_address(base);+if(__split_large_page(kpte,address,pbase))__free_page(base);-spin_unlock(&pgd_lock);return0;}--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Sorry, I made a mistake here. Only if the page was allocated at booting, we should update
zone and totalram_pages(zone->present_pages and totalram_pages mean pages which are
managed by buddy system).
How about:
static inline void free_pagetable(struct page *page)
{
struct zone *zone;
bool bootmem = false;
/* bootmem page has reserved flag */
if (PageReserved(page)) {
__ClearPageReserved(page);
bootmem = true;
}
__free_page(page);
if (bootmem) {
zone = page_zone(page);
zone_span_writelock(zone);
zone->present_pages++;
zone_span_writeunlock(zone);
totalram_pages++;
}
}
quoted
+}
+
+static void free_pte_table(pte_t *pte_start, pmd_t *pmd)
+{
+ pte_t *pte;
+ int i;
+
+ for (i = 0; i < PTRS_PER_PTE; i++) {
+ pte = pte_start + i;
+ if (pte_val(*pte))
+ break;
+ }
+
+ /* free a pte talbe */
+ if (i == PTRS_PER_PTE) {
+ free_pagetable(pmd_page(*pmd));
The memory may be allocated at booting. So it is very dangerous to
free it without any check.
The page is only used by page table, so is safe to free it when all the page table
entries have been cleared, right?
quoted
+ pmd_clear(pmd);
+ }
+}
+
+static void free_pmd_table(pmd_t *pmd_start, pud_t *pud)
+{
+ pmd_t *pmd;
+ int i;
+
+ for (i = 0; i < PTRS_PER_PMD; i++) {
+ pmd = pmd_start + i;
+ if (pmd_val(*pmd))
+ break;
+ }
+
+ /* free a pmd talbe */
+ if (i == PTRS_PER_PMD) {
+ free_pagetable(pud_page(*pud));
+ pud_clear(pud);
+ }
+}
+
+static void free_pud_table(pud_t *pud_start, pgd_t *pgd)
+{
+ pud_t *pud;
+ int i;
+
+ for (i = 0; i < PTRS_PER_PUD; i++) {
+ pud = pud_start + i;
+ if (pud_val(*pud))
+ break;
+ }
+
+ /* free a pud table */
+ if (i == PTRS_PER_PUD) {
+ free_pagetable(pgd_page(*pgd));
+ pgd_clear(pgd);
+ }
+}
+
static void __meminit
phys_pte_remove(pte_t *pte_page, unsigned long addr, unsigned long end)
{
@@ -704,21 +772,19 @@ phys_pmd_remove(pmd_t *pmd_page, unsigned long addr, unsigned long end) unsigned long pages = 0, next; int i = pmd_index(addr);- for (; i < PTRS_PER_PMD; i++, addr = next) {+ for (; i < PTRS_PER_PMD && addr < end; i++, addr = next) { unsigned long pte_phys; pmd_t *pmd = pmd_page + pmd_index(addr); pte_t *pte;- if (addr >= end)- break;-- next = (addr & PMD_MASK) + PMD_SIZE;+ next = pmd_addr_end(addr, end); if (!pmd_present(*pmd)) continue; if (pmd_large(*pmd)) {- if ((addr & ~PMD_MASK) == 0 && next <= end) {+ if (IS_ALIGNED(addr, PMD_SIZE) &&+ IS_ALIGNED(next, PMD_SIZE)) { set_pmd(pmd, __pmd(0)); pages++; continue;
@@ -729,7 +795,8 @@ phys_pmd_remove(pmd_t *pmd_page, unsigned long addr, unsigned long end) * so split 2M page to 4K page. */ pte = alloc_low_page(&pte_phys);- __split_large_page((pte_t *)pmd, addr, pte);+ __split_large_page((pte_t *)pmd,+ (unsigned long)__va(addr), pte); spin_lock(&init_mm.page_table_lock); pmd_populate_kernel(&init_mm, pmd, __va(pte_phys));
@@ -805,15 +874,14 @@ kernel_physical_mapping_remove(unsigned long start, unsigned long end) pgd_t *pgd = pgd_offset_k(start); pud_t *pud;- next = (start + PGDIR_SIZE) & PGDIR_MASK;- if (next > end)- next = end;+ next = pgd_addr_end(start, end); if (!pgd_present(*pgd)) continue; pud = map_low_page((pud_t *)pgd_page_vaddr(*pgd));- phys_pud_remove(pud, __pa(start), __pa(end));+ phys_pud_remove(pud, __pa(start), __pa(next));+ free_pud_table(pud, pgd); unmap_low_page(pud); }-- 1.7.6.1 .
On 2012-10-5 10:36, Yasuaki Ishimatsu wrote:
quoted
From: Wen Congyang <redacted>
For hot removing memory, we sholud remove page table about the memory.
So the patch searches a page table about the removed memory, and clear
page table.
CC: David Rientjes <rientjes@google.com>
CC: Jiang Liu <redacted>
CC: Len Brown <redacted>
CC: Christoph Lameter <redacted>
Cc: Minchan Kim <redacted>
CC: Andrew Morton <akpm@linux-foundation.org>
CC: KOSAKI Motohiro <redacted>
CC: Yasuaki Ishimatsu <redacted>
Signed-off-by: Wen Congyang <redacted>
---
arch/x86/include/asm/pgtable_types.h | 1
arch/x86/mm/init_64.c | 147 +++++++++++++++++++++++++++++++++++
arch/x86/mm/pageattr.c | 47 +++++------
3 files changed, 173 insertions(+), 22 deletions(-)
Index: linux-3.6/arch/x86/mm/init_64.c
===================================================================
@@ -523,10 +515,11 @@ static int split_large_page(pte_t *kpte,*upforusalready:*/tmp=lookup_address(address,&level);-if(tmp!=kpte)-gotoout_unlock;+if(tmp!=kpte){+spin_unlock(&pgd_lock);+return1;+}-pbase=(pte_t*)page_address(base);paravirt_alloc_pte(&init_mm,page_to_pfn(base));ref_prot=pte_pgprot(pte_clrhuge(*kpte));/*
@@ -579,17 +572,27 @@ static int split_large_page(pte_t *kpte,*goingon.*/__flush_tlb_all();+spin_unlock(&pgd_lock);-base=NULL;+return0;+}-out_unlock:-/*-*Ifwedroppedoutviathelookup_addresscheckunder-*pgd_lockthenstickthepagebackintothepool:-*/-if(base)+staticintsplit_large_page(pte_t*kpte,unsignedlongaddress)+{+pte_t*pbase;+structpage*base;++if(!debug_pagealloc)+spin_unlock(&cpa_lock);+base=alloc_pages(GFP_KERNEL|__GFP_NOTRACK,0);+if(!debug_pagealloc)+spin_lock(&cpa_lock);+if(!base)+return-ENOMEM;++pbase=(pte_t*)page_address(base);+if(__split_large_page(kpte,address,pbase))__free_page(base);-spin_unlock(&pgd_lock);return0;}--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Sorry, I made a mistake here. Only if the page was allocated at booting, we should update
zone and totalram_pages(zone->present_pages and totalram_pages mean pages which are
managed by buddy system).
How about:
static inline void free_pagetable(struct page *page)
{
struct zone *zone;
bool bootmem = false;
/* bootmem page has reserved flag */
if (PageReserved(page)) {
__ClearPageReserved(page);
bootmem = true;
}
__free_page(page);
if (bootmem) {
zone = page_zone(page);
zone_span_writelock(zone);
zone->present_pages++;
zone_span_writeunlock(zone);
totalram_pages++;
}
}
This verson looks fine to me.
quoted
quoted
+}
+
+static void free_pte_table(pte_t *pte_start, pmd_t *pmd)
+{
+ pte_t *pte;
+ int i;
+
+ for (i = 0; i < PTRS_PER_PTE; i++) {
+ pte = pte_start + i;
+ if (pte_val(*pte))
+ break;
+ }
+
+ /* free a pte talbe */
+ if (i == PTRS_PER_PTE) {
+ free_pagetable(pmd_page(*pmd));
The memory may be allocated at booting. So it is very dangerous to
free it without any check.
The page is only used by page table, so is safe to free it when all the page table
entries have been cleared, right?
Yes, but I guess we need to do more. For example, all boot memory are in memblock.reserved,
and you don't update memblock here.
Thanks
Wen Congyang
quoted
quoted
+ pmd_clear(pmd);
+ }
+}
+
+static void free_pmd_table(pmd_t *pmd_start, pud_t *pud)
+{
+ pmd_t *pmd;
+ int i;
+
+ for (i = 0; i < PTRS_PER_PMD; i++) {
+ pmd = pmd_start + i;
+ if (pmd_val(*pmd))
+ break;
+ }
+
+ /* free a pmd talbe */
+ if (i == PTRS_PER_PMD) {
+ free_pagetable(pud_page(*pud));
+ pud_clear(pud);
+ }
+}
+
+static void free_pud_table(pud_t *pud_start, pgd_t *pgd)
+{
+ pud_t *pud;
+ int i;
+
+ for (i = 0; i < PTRS_PER_PUD; i++) {
+ pud = pud_start + i;
+ if (pud_val(*pud))
+ break;
+ }
+
+ /* free a pud table */
+ if (i == PTRS_PER_PUD) {
+ free_pagetable(pgd_page(*pgd));
+ pgd_clear(pgd);
+ }
+}
+
static void __meminit
phys_pte_remove(pte_t *pte_page, unsigned long addr, unsigned long end)
{
@@ -704,21 +772,19 @@ phys_pmd_remove(pmd_t *pmd_page, unsigned long addr, unsigned long end) unsigned long pages = 0, next; int i = pmd_index(addr);- for (; i < PTRS_PER_PMD; i++, addr = next) {+ for (; i < PTRS_PER_PMD && addr < end; i++, addr = next) { unsigned long pte_phys; pmd_t *pmd = pmd_page + pmd_index(addr); pte_t *pte;- if (addr >= end)- break;-- next = (addr & PMD_MASK) + PMD_SIZE;+ next = pmd_addr_end(addr, end); if (!pmd_present(*pmd)) continue; if (pmd_large(*pmd)) {- if ((addr & ~PMD_MASK) == 0 && next <= end) {+ if (IS_ALIGNED(addr, PMD_SIZE) &&+ IS_ALIGNED(next, PMD_SIZE)) { set_pmd(pmd, __pmd(0)); pages++; continue;
@@ -729,7 +795,8 @@ phys_pmd_remove(pmd_t *pmd_page, unsigned long addr, unsigned long end) * so split 2M page to 4K page. */ pte = alloc_low_page(&pte_phys);- __split_large_page((pte_t *)pmd, addr, pte);+ __split_large_page((pte_t *)pmd,+ (unsigned long)__va(addr), pte); spin_lock(&init_mm.page_table_lock); pmd_populate_kernel(&init_mm, pmd, __va(pte_phys));
@@ -805,15 +874,14 @@ kernel_physical_mapping_remove(unsigned long start, unsigned long end) pgd_t *pgd = pgd_offset_k(start); pud_t *pud;- next = (start + PGDIR_SIZE) & PGDIR_MASK;- if (next > end)- next = end;+ next = pgd_addr_end(start, end); if (!pgd_present(*pgd)) continue; pud = map_low_page((pud_t *)pgd_page_vaddr(*pgd));- phys_pud_remove(pud, __pa(start), __pa(end));+ phys_pud_remove(pud, __pa(start), __pa(next));+ free_pud_table(pud, pgd); unmap_low_page(pud); }-- 1.7.6.1 .
On 2012-10-5 10:36, Yasuaki Ishimatsu wrote:
quoted
From: Wen Congyang <redacted>
For hot removing memory, we sholud remove page table about the memory.
So the patch searches a page table about the removed memory, and clear
page table.
CC: David Rientjes <rientjes@google.com>
CC: Jiang Liu <redacted>
CC: Len Brown <redacted>
CC: Christoph Lameter <redacted>
Cc: Minchan Kim <redacted>
CC: Andrew Morton <akpm@linux-foundation.org>
CC: KOSAKI Motohiro <redacted>
CC: Yasuaki Ishimatsu <redacted>
Signed-off-by: Wen Congyang <redacted>
---
arch/x86/include/asm/pgtable_types.h | 1
arch/x86/mm/init_64.c | 147 +++++++++++++++++++++++++++++++++++
arch/x86/mm/pageattr.c | 47 +++++------
3 files changed, 173 insertions(+), 22 deletions(-)
Index: linux-3.6/arch/x86/mm/init_64.c
===================================================================
@@ -523,10 +515,11 @@ static int split_large_page(pte_t *kpte,*upforusalready:*/tmp=lookup_address(address,&level);-if(tmp!=kpte)-gotoout_unlock;+if(tmp!=kpte){+spin_unlock(&pgd_lock);+return1;+}-pbase=(pte_t*)page_address(base);paravirt_alloc_pte(&init_mm,page_to_pfn(base));ref_prot=pte_pgprot(pte_clrhuge(*kpte));/*
@@ -579,17 +572,27 @@ static int split_large_page(pte_t *kpte,*goingon.*/__flush_tlb_all();+spin_unlock(&pgd_lock);-base=NULL;+return0;+}-out_unlock:-/*-*Ifwedroppedoutviathelookup_addresscheckunder-*pgd_lockthenstickthepagebackintothepool:-*/-if(base)+staticintsplit_large_page(pte_t*kpte,unsignedlongaddress)+{+pte_t*pbase;+structpage*base;++if(!debug_pagealloc)+spin_unlock(&cpa_lock);+base=alloc_pages(GFP_KERNEL|__GFP_NOTRACK,0);+if(!debug_pagealloc)+spin_lock(&cpa_lock);+if(!base)+return-ENOMEM;++pbase=(pte_t*)page_address(base);+if(__split_large_page(kpte,address,pbase))__free_page(base);-spin_unlock(&pgd_lock);return0;}--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>