This set of patches de-couples the idea that there is a single
directory in sysfs for each memory section. The intent of the
patches is to reduce the number of sysfs directories created to
resolve a boot-time performance issue. On very large systems
boot time are getting very long (as seen on powerpc hardware)
due to the enormous number of sysfs directories being created.
On a system with 1 TB of memory we create ~63,000 directories.
For even larger systems boot times are being measured in hours.
This set of patches allows for each directory created in sysfs
to cover more than one memory section. The default behavior for
sysfs directory creation is the same, in that each directory
represents a single memory section. A new file 'end_phys_index'
in each directory contains the physical_id of the last memory
section covered by the directory so that users can easily
determine the memory section range of a directory.
-Nathan Fontenot
Update the 'phys_index' properties of a memory block to include a
'start_phys_index' which is the same as the current 'phys_index' property.
This also adds an 'end_phys_index' property to indicate the id of the
last section in th memory block.
Signed-off-by: Nathan Fontenot <redacted>
---
drivers/base/memory.c | 32 ++++++++++++++++++++++----------
include/linux/memory.h | 3 ++-
2 files changed, 24 insertions(+), 11 deletions(-)
Index: linux-2.6/drivers/base/memory.c
===================================================================
@@ -476,15 +485,17 @@ static int add_memory_block(int nid, strif(!mem)return-ENOMEM;-mem->phys_index=__section_nr(section);+mem->start_phys_index=__section_nr(section);mem->state=state;mutex_init(&mem->state_mutex);-start_pfn=section_nr_to_pfn(mem->phys_index);+start_pfn=section_nr_to_pfn(mem->start_phys_index);mem->phys_device=arch_get_memory_phys_device(start_pfn);ret=register_memory(mem,section);if(!ret)-ret=mem_create_simple_file(mem,phys_index);+ret=mem_create_simple_file(mem,start_phys_index);+if(!ret)+ret=mem_create_simple_file(mem,end_phys_index);if(!ret)ret=mem_create_simple_file(mem,state);if(!ret)
@@ -506,7 +517,8 @@ int remove_memory_block(unsigned long nomem=find_memory_block(section);unregister_mem_sect_under_nodes(mem);-mem_remove_simple_file(mem,phys_index);+mem_remove_simple_file(mem,start_phys_index);+mem_remove_simple_file(mem,end_phys_index);mem_remove_simple_file(mem,state);mem_remove_simple_file(mem,phys_device);mem_remove_simple_file(mem,removable);
Update the 'phys_index' properties of a memory block to include a
'start_phys_index' which is the same as the current 'phys_index' property.
This also adds an 'end_phys_index' property to indicate the id of the
last section in th memory block.
Signed-off-by: Nathan Fontenot <redacted>
No, please remain "phys_index" as it is. please don't rename it.
IMHO, just adding end_phys_index is better.
please avoid interface change AFAP.
Do you have a problem if phys_index means start_phys_index ?
Thanks,
-Kame
Update the 'phys_index' properties of a memory block to include a
'start_phys_index' which is the same as the current 'phys_index' property.
This also adds an 'end_phys_index' property to indicate the id of the
last section in th memory block.
Patch updated to keep the name of the phys_index property instead of
renaming it to start_phys_index.
Signed-off-by: Nathan Fontenot <redacted>
---
drivers/base/memory.c | 28 ++++++++++++++++++++--------
include/linux/memory.h | 3 ++-
2 files changed, 22 insertions(+), 9 deletions(-)
Index: linux-2.6/drivers/base/memory.c
===================================================================
@@ -476,16 +485,18 @@ static int add_memory_block(int nid, strif(!mem)return-ENOMEM;-mem->phys_index=__section_nr(section);+mem->start_phys_index=__section_nr(section);mem->state=state;mutex_init(&mem->state_mutex);-start_pfn=section_nr_to_pfn(mem->phys_index);+start_pfn=section_nr_to_pfn(mem->start_phys_index);mem->phys_device=arch_get_memory_phys_device(start_pfn);ret=register_memory(mem,section);if(!ret)ret=mem_create_simple_file(mem,phys_index);if(!ret)+ret=mem_create_simple_file(mem,end_phys_index);+if(!ret)ret=mem_create_simple_file(mem,state);if(!ret)ret=mem_create_simple_file(mem,phys_device);
@@ -507,6 +518,7 @@ int remove_memory_block(unsigned long nomem=find_memory_block(section);unregister_mem_sect_under_nodes(mem);mem_remove_simple_file(mem,phys_index);+mem_remove_simple_file(mem,end_phys_index);mem_remove_simple_file(mem,state);mem_remove_simple_file(mem,phys_device);mem_remove_simple_file(mem,removable);
From: Dave Hansen <hidden> Date: 2010-07-20 19:10:47
On Tue, 2010-07-20 at 08:24 -0500, Nathan Fontenot wrote:
Update the 'phys_index' properties of a memory block to include a
'start_phys_index' which is the same as the current 'phys_index' property.
This also adds an 'end_phys_index' property to indicate the id of the
last section in th memory block.
Patch updated to keep the name of the phys_index property instead of
renaming it to start_phys_index.
KAME is right on this. We should keep the old one if at all possible.
The only other thing we might want to do is move 'phys_index' to
'start_phys_index', and make a new 'phys_index' that does a WARN_ONCE(),
gives a deprecated warning, then calls the new 'start_phys_index' code.
So, basically make the new, more clear name, but keep the old one for a
while and deprecate it. Maybe we could get away with removing it in ten
years. :)
-- Dave
Add a section count property to the memory_block struct to track the number
of memory sections that have been added/removed from a emory block.
Signed-off-by: Nathan Fontenot <redacted>
---
drivers/base/memory.c | 19 ++++++++++++-------
include/linux/memory.h | 2 ++
2 files changed, 14 insertions(+), 7 deletions(-)
Index: linux-2.6/drivers/base/memory.c
===================================================================
@@ -487,6 +487,7 @@ static int add_memory_block(int nid, strmem->start_phys_index=__section_nr(section);mem->state=state;+atomic_inc(&mem->section_count);mutex_init(&mem->state_mutex);start_pfn=section_nr_to_pfn(mem->start_phys_index);mem->phys_device=arch_get_memory_phys_device(start_pfn);
@@ -516,13 +517,17 @@ int remove_memory_block(unsigned long nostructmemory_block*mem;mem=find_memory_block(section);-unregister_mem_sect_under_nodes(mem);-mem_remove_simple_file(mem,start_phys_index);-mem_remove_simple_file(mem,end_phys_index);-mem_remove_simple_file(mem,state);-mem_remove_simple_file(mem,phys_device);-mem_remove_simple_file(mem,removable);-unregister_memory(mem,section);+atomic_dec(&mem->section_count);++if(atomic_read(&mem->section_count)==0){+unregister_mem_sect_under_nodes(mem);+mem_remove_simple_file(mem,start_phys_index);+mem_remove_simple_file(mem,end_phys_index);+mem_remove_simple_file(mem,state);+mem_remove_simple_file(mem,phys_device);+mem_remove_simple_file(mem,removable);+unregister_memory(mem,section);+}return0;}
Add a section count property to the memory_block struct to track the number
of memory sections that have been added/removed from a emory block.
Signed-off-by: Nathan Fontenot <redacted>
---
drivers/base/memory.c | 19 ++++++++++++-------
include/linux/memory.h | 2 ++
2 files changed, 14 insertions(+), 7 deletions(-)
Index: linux-2.6/drivers/base/memory.c
===================================================================
@@ -487,6 +487,7 @@ static int add_memory_block(int nid, strmem->start_phys_index=__section_nr(section);mem->state=state;+atomic_inc(&mem->section_count);mutex_init(&mem->state_mutex);start_pfn=section_nr_to_pfn(mem->start_phys_index);mem->phys_device=arch_get_memory_phys_device(start_pfn);
@@ -516,13 +517,17 @@ int remove_memory_block(unsigned long nostructmemory_block*mem;mem=find_memory_block(section);-unregister_mem_sect_under_nodes(mem);-mem_remove_simple_file(mem,start_phys_index);-mem_remove_simple_file(mem,end_phys_index);-mem_remove_simple_file(mem,state);-mem_remove_simple_file(mem,phys_device);-mem_remove_simple_file(mem,removable);-unregister_memory(mem,section);+atomic_dec(&mem->section_count);++if(atomic_read(&mem->section_count)==0){
We use atomic_dec_and_test() in usual.
Otherwise, I don't see other problems in other part. Please fix this nitpick.
Regards,
-Kame
Add a section count property to the memory_block struct to track the number
of memory sections that have been added/removed from a memory block. Updated
to use atomic_dec_and_test().
Signed-off-by: Nathan Fontenot <redacted>
---
drivers/base/memory.c | 18 +++++++++++-------
include/linux/memory.h | 2 ++
2 files changed, 13 insertions(+), 7 deletions(-)
Index: linux-2.6/drivers/base/memory.c
===================================================================
@@ -487,6 +487,7 @@ static int add_memory_block(int nid, strmem->start_phys_index=__section_nr(section);mem->state=state;+atomic_inc(&mem->section_count);mutex_init(&mem->state_mutex);start_pfn=section_nr_to_pfn(mem->start_phys_index);mem->phys_device=arch_get_memory_phys_device(start_pfn);
@@ -516,13 +517,16 @@ int remove_memory_block(unsigned long nostructmemory_block*mem;mem=find_memory_block(section);-unregister_mem_sect_under_nodes(mem);-mem_remove_simple_file(mem,phys_index);-mem_remove_simple_file(mem,end_phys_index);-mem_remove_simple_file(mem,state);-mem_remove_simple_file(mem,phys_device);-mem_remove_simple_file(mem,removable);-unregister_memory(mem,section);++if(atomic_dec_and_test(&mem->section_count)){+unregister_mem_sect_under_nodes(mem);+mem_remove_simple_file(mem,phys_index);+mem_remove_simple_file(mem,end_phys_index);+mem_remove_simple_file(mem,state);+mem_remove_simple_file(mem,phys_device);+mem_remove_simple_file(mem,removable);+unregister_memory(mem,section);+}return0;}
Update the memory sysfs code that each sysfs memory directory is now
considered a memory block that can contain multiple memory sections per
memory block. The default size of each memory block is SECTION_SIZE_BITS
to maintain the current behavior of having a single memory section per
memory block (i.e. one sysfs directory per memory section).
For architectures that want to have memory blocks span multiple
memory sections they need only define their own memory_block_size_bytes()
routine.
Signed-off-by: Nathan Fontenot <redacted>
---
drivers/base/memory.c | 141 ++++++++++++++++++++++++++++++++++----------------
1 file changed, 98 insertions(+), 43 deletions(-)
Index: linux-2.6/drivers/base/memory.c
===================================================================
@@ -82,22 +90,21 @@ EXPORT_SYMBOL(unregister_memory_isolate_*register_memory-Setupasysfsdeviceforamemoryblock*/static-intregister_memory(structmemory_block*memory,structmem_section*section)+intregister_memory(structmemory_block*memory){interror;memory->sysdev.cls=&memory_sysdev_class;-memory->sysdev.id=__section_nr(section);+memory->sysdev.id=memory->start_phys_index;error=sysdev_register(&memory->sysdev);returnerror;}staticvoid-unregister_memory(structmemory_block*memory,structmem_section*section)+unregister_memory(structmemory_block*memory){BUG_ON(memory->sysdev.cls!=&memory_sysdev_class);-BUG_ON(memory->sysdev.id!=__section_nr(section));/* drop the ref. we got in remove_memory_block() */kobject_put(&memory->sysdev.kobj);
@@ -190,17 +200,14 @@ int memory_isolate_notify(unsigned long*OKtohavedirectreferencestosparsememvariablesinhere.*/staticint-memory_block_action(structmemory_block*mem,unsignedlongaction)+memory_section_action(unsignedlongphys_index,unsignedlongaction){inti;-unsignedlongpsection;unsignedlongstart_pfn,start_paddr;structpage*first_page;intret;-intold_state=mem->state;-psection=mem->start_phys_index;-first_page=pfn_to_page(psection<<PFN_SECTION_SHIFT);+first_page=pfn_to_page(phys_index<<PFN_SECTION_SHIFT);/**Theproberoutinesleavethepagesreserved,just
@@ -213,8 +220,8 @@ memory_block_action(struct memory_blockcontinue;printk(KERN_WARNING"section number %ld page number %d "-"not reserved, was it already online? \n",-psection,i);+"not reserved, was it already online?\n",+phys_index,i);return-EBUSY;}}
@@ -503,8 +516,27 @@ static int add_memory_block(int nid, strret=mem_create_simple_file(mem,phys_device);if(!ret)ret=mem_create_simple_file(mem,removable);++*memory=mem;+returnret;+}++staticintadd_memory_section(intnid,structmem_section*section,+unsignedlongstate,enummem_add_contextcontext)+{+structmemory_block*mem;+intret=0;++mem=find_memory_block(section);+if(mem){+atomic_inc(&mem->section_count);+kobject_put(&mem->sysdev.kobj);+}else+ret=init_memory_block(&mem,section,state);+if(!ret){-if(context==HOTPLUG)+if(context==HOTPLUG&&+atomic_read(&mem->section_count)==sections_per_block)ret=register_mem_sect_under_node(mem,nid);}
@@ -526,8 +558,9 @@ int remove_memory_block(unsigned long nomem_remove_simple_file(mem,state);mem_remove_simple_file(mem,phys_device);mem_remove_simple_file(mem,removable);-unregister_memory(mem,section);-}+unregister_memory(mem);+}else+kobject_put(&mem->sysdev.kobj);return0;}
@@ -538,7 +571,7 @@ int remove_memory_block(unsigned long no*/intregister_new_memory(intnid,structmem_section*section){-returnadd_memory_block(nid,section,MEM_OFFLINE,HOTPLUG);+returnadd_memory_section(nid,section,MEM_OFFLINE,HOTPLUG);}intunregister_memory_section(structmem_section*section)
@@ -549,6 +582,24 @@ int unregister_memory_section(struct memreturnremove_memory_block(0,section,0);}+u32__weakmemory_block_size_bytes(void)+{+returnMIN_MEMORY_BLOCK_SIZE;+}++staticu32get_memory_block_size(void)+{+u32block_sz;++block_sz=memory_block_size_bytes();++/* Validate blk_sz is a power of 2 and not less than section size */+if((block_sz&(block_sz-1))||(block_sz<MIN_MEMORY_BLOCK_SIZE))+block_sz=MIN_MEMORY_BLOCK_SIZE;++returnblock_sz;+}+/**Initializethesysfssupportformemorydevices...*/
@@ -557,12 +608,16 @@ int __init memory_dev_init(void)unsignedinti;intret;interr;+intblock_sz;memory_sysdev_class.kset.uevent_ops=&memory_uevent_ops;ret=sysdev_class_register(&memory_sysdev_class);if(ret)gotoout;+block_sz=get_memory_block_size();+sections_per_block=block_sz/MIN_MEMORY_BLOCK_SIZE;+/**Createentriesformemorysectionsthatwerefound*duringbootandhavebeeninitialized
@@ -570,8 +625,8 @@ int __init memory_dev_init(void)for(i=0;i<NR_MEM_SECTIONS;i++){if(!present_section_nr(i))continue;-err=add_memory_block(0,__nr_to_section(i),MEM_ONLINE,-BOOT);+err=add_memory_section(0,__nr_to_section(i),MEM_ONLINE,+BOOT);if(!ret)ret=err;}
Update the memory sysfs code that each sysfs memory directory is now
considered a memory block that can contain multiple memory sections per
memory block. The default size of each memory block is SECTION_SIZE_BITS
to maintain the current behavior of having a single memory section per
memory block (i.e. one sysfs directory per memory section).
For architectures that want to have memory blocks span multiple
memory sections they need only define their own memory_block_size_bytes()
routine.
Signed-off-by: Nathan Fontenot <redacted>
---
drivers/base/memory.c | 141 ++++++++++++++++++++++++++++++++++----------------
1 file changed, 98 insertions(+), 43 deletions(-)
Index: linux-2.6/drivers/base/memory.c
===================================================================
Update the memory sysfs code that each sysfs memory directory is now
considered a memory block that can contain multiple memory sections per
memory block. The default size of each memory block is SECTION_SIZE_BITS
to maintain the current behavior of having a single memory section per
memory block (i.e. one sysfs directory per memory section).
For architectures that want to have memory blocks span multiple
memory sections they need only define their own memory_block_size_bytes()
routine.
Patch refreshed to apply cleanly with previous two patch updates.
Signed-off-by: Nathan Fontenot <redacted>
---
drivers/base/memory.c | 141 ++++++++++++++++++++++++++++++++++----------------
1 file changed, 98 insertions(+), 43 deletions(-)
Index: linux-2.6/drivers/base/memory.c
===================================================================
@@ -82,22 +90,21 @@ EXPORT_SYMBOL(unregister_memory_isolate_*register_memory-Setupasysfsdeviceforamemoryblock*/static-intregister_memory(structmemory_block*memory,structmem_section*section)+intregister_memory(structmemory_block*memory){interror;memory->sysdev.cls=&memory_sysdev_class;-memory->sysdev.id=__section_nr(section);+memory->sysdev.id=memory->start_phys_index;error=sysdev_register(&memory->sysdev);returnerror;}staticvoid-unregister_memory(structmemory_block*memory,structmem_section*section)+unregister_memory(structmemory_block*memory){BUG_ON(memory->sysdev.cls!=&memory_sysdev_class);-BUG_ON(memory->sysdev.id!=__section_nr(section));/* drop the ref. we got in remove_memory_block() */kobject_put(&memory->sysdev.kobj);
@@ -190,17 +200,14 @@ int memory_isolate_notify(unsigned long*OKtohavedirectreferencestosparsememvariablesinhere.*/staticint-memory_block_action(structmemory_block*mem,unsignedlongaction)+memory_section_action(unsignedlongphys_index,unsignedlongaction){inti;-unsignedlongpsection;unsignedlongstart_pfn,start_paddr;structpage*first_page;intret;-intold_state=mem->state;-psection=mem->start_phys_index;-first_page=pfn_to_page(psection<<PFN_SECTION_SHIFT);+first_page=pfn_to_page(phys_index<<PFN_SECTION_SHIFT);/**Theproberoutinesleavethepagesreserved,just
@@ -213,8 +220,8 @@ memory_block_action(struct memory_blockcontinue;printk(KERN_WARNING"section number %ld page number %d "-"not reserved, was it already online? \n",-psection,i);+"not reserved, was it already online?\n",+phys_index,i);return-EBUSY;}}
@@ -503,8 +516,27 @@ static int add_memory_block(int nid, strret=mem_create_simple_file(mem,phys_device);if(!ret)ret=mem_create_simple_file(mem,removable);++*memory=mem;+returnret;+}++staticintadd_memory_section(intnid,structmem_section*section,+unsignedlongstate,enummem_add_contextcontext)+{+structmemory_block*mem;+intret=0;++mem=find_memory_block(section);+if(mem){+atomic_inc(&mem->section_count);+kobject_put(&mem->sysdev.kobj);+}else+ret=init_memory_block(&mem,section,state);+if(!ret){-if(context==HOTPLUG)+if(context==HOTPLUG&&+atomic_read(&mem->section_count)==sections_per_block)ret=register_mem_sect_under_node(mem,nid);}
@@ -525,8 +557,9 @@ int remove_memory_block(unsigned long nomem_remove_simple_file(mem,state);mem_remove_simple_file(mem,phys_device);mem_remove_simple_file(mem,removable);-unregister_memory(mem,section);-}+unregister_memory(mem);+}else+kobject_put(&mem->sysdev.kobj);return0;}
@@ -537,7 +570,7 @@ int remove_memory_block(unsigned long no*/intregister_new_memory(intnid,structmem_section*section){-returnadd_memory_block(nid,section,MEM_OFFLINE,HOTPLUG);+returnadd_memory_section(nid,section,MEM_OFFLINE,HOTPLUG);}intunregister_memory_section(structmem_section*section)
@@ -548,6 +581,24 @@ int unregister_memory_section(struct memreturnremove_memory_block(0,section,0);}+u32__weakmemory_block_size_bytes(void)+{+returnMIN_MEMORY_BLOCK_SIZE;+}++staticu32get_memory_block_size(void)+{+u32block_sz;++block_sz=memory_block_size_bytes();++/* Validate blk_sz is a power of 2 and not less than section size */+if((block_sz&(block_sz-1))||(block_sz<MIN_MEMORY_BLOCK_SIZE))+block_sz=MIN_MEMORY_BLOCK_SIZE;++returnblock_sz;+}+/**Initializethesysfssupportformemorydevices...*/
@@ -556,12 +607,16 @@ int __init memory_dev_init(void)unsignedinti;intret;interr;+intblock_sz;memory_sysdev_class.kset.uevent_ops=&memory_uevent_ops;ret=sysdev_class_register(&memory_sysdev_class);if(ret)gotoout;+block_sz=get_memory_block_size();+sections_per_block=block_sz/MIN_MEMORY_BLOCK_SIZE;+/**Createentriesformemorysectionsthatwerefound*duringbootandhavebeeninitialized
@@ -569,8 +624,8 @@ int __init memory_dev_init(void)for(i=0;i<NR_MEM_SECTIONS;i++){if(!present_section_nr(i))continue;-err=add_memory_block(0,__nr_to_section(i),MEM_ONLINE,-BOOT);+err=add_memory_section(0,__nr_to_section(i),MEM_ONLINE,+BOOT);if(!ret)ret=err;}
From: Dave Hansen <hidden> Date: 2010-07-20 19:18:15
On Mon, 2010-07-19 at 22:55 -0500, Nathan Fontenot wrote:
+static int add_memory_section(int nid, struct mem_section *section,
+ unsigned long state, enum mem_add_context context)
+{
+ struct memory_block *mem;
+ int ret = 0;
+
+ mem = find_memory_block(section);
+ if (mem) {
+ atomic_inc(&mem->section_count);
+ kobject_put(&mem->sysdev.kobj);
+ } else
+ ret = init_memory_block(&mem, section, state);
+
if (!ret) {
- if (context == HOTPLUG)
+ if (context == HOTPLUG &&
+ atomic_read(&mem->section_count) == sections_per_block)
ret = register_mem_sect_under_node(mem, nid);
}
I think the atomic_inc() can race with the atomic_dec_and_test() in
remove_memory_block().
Thread 1 does:
mem = find_memory_block(section);
Thread 2 does
atomic_dec_and_test(&mem->section_count);
and destroys the memory block, Thread 1 runs again:
if (mem) {
atomic_inc(&mem->section_count);
kobject_put(&mem->sysdev.kobj);
} else
but now mem got destroyed by Thread 2. You probably need to change
find_memory_block() to itself take a reference, and to use
atomic_inc_unless().
-- Dave
On Mon, 2010-07-19 at 22:55 -0500, Nathan Fontenot wrote:
quoted
+static int add_memory_section(int nid, struct mem_section *section,
+ unsigned long state, enum mem_add_context context)
+{
+ struct memory_block *mem;
+ int ret = 0;
+
+ mem = find_memory_block(section);
+ if (mem) {
+ atomic_inc(&mem->section_count);
+ kobject_put(&mem->sysdev.kobj);
+ } else
+ ret = init_memory_block(&mem, section, state);
+
if (!ret) {
- if (context == HOTPLUG)
+ if (context == HOTPLUG &&
+ atomic_read(&mem->section_count) == sections_per_block)
ret = register_mem_sect_under_node(mem, nid);
}
I think the atomic_inc() can race with the atomic_dec_and_test() in
remove_memory_block().
Thread 1 does:
mem = find_memory_block(section);
Thread 2 does
atomic_dec_and_test(&mem->section_count);
and destroys the memory block, Thread 1 runs again:
if (mem) {
atomic_inc(&mem->section_count);
kobject_put(&mem->sysdev.kobj);
} else
but now mem got destroyed by Thread 2. You probably need to change
find_memory_block() to itself take a reference, and to use
atomic_inc_unless().
You're right but I think the fix you suggested will narrow the window for the
race condition, not eliminate it. We could still take a time splice in
find_memory_block prior to the container_of() calls to get the memory
block pointer and end up de-referencing a invalid kobject o sysdev pointer.
I think if we want to eliminate this we may need to have lock that protects
access to any of the memory_block structures. This would need to be taken
any time find_memory_block is called and released when use of the memory_block
returned is finished. If we're going to fix this we should eliminate the
window completely instead of just closing it further.
If we add a lock should I submit it as part of this patchset? or submit it
as a follow-on?
-Nathan
From: Dave Hansen <hidden> Date: 2010-07-27 02:37:00
On Fri, 2010-07-23 at 22:09 -0500, Nathan Fontenot wrote:
If we add a lock should I submit it as part of this patchset? or
submit it
as a follow-on?
It should probably be at the beginning of the patch set. We don't want
to have a case where your set introduces races that we _need_ a later
patch to fix.
-- Dave
On Mon, 2010-07-19 at 22:55 -0500, Nathan Fontenot wrote:
quoted
+static int add_memory_section(int nid, struct mem_section *section,
+ unsigned long state, enum mem_add_context context)
+{
+ struct memory_block *mem;
+ int ret = 0;
+
+ mem = find_memory_block(section);
+ if (mem) {
+ atomic_inc(&mem->section_count);
+ kobject_put(&mem->sysdev.kobj);
+ } else
+ ret = init_memory_block(&mem, section, state);
+
if (!ret) {
- if (context == HOTPLUG)
+ if (context == HOTPLUG &&
+ atomic_read(&mem->section_count) == sections_per_block)
ret = register_mem_sect_under_node(mem, nid);
}
I think the atomic_inc() can race with the atomic_dec_and_test() in
remove_memory_block().
Thread 1 does:
mem = find_memory_block(section);
Thread 2 does
atomic_dec_and_test(&mem->section_count);
and destroys the memory block, Thread 1 runs again:
if (mem) {
atomic_inc(&mem->section_count);
kobject_put(&mem->sysdev.kobj);
} else
but now mem got destroyed by Thread 2. You probably need to change
find_memory_block() to itself take a reference, and to use
atomic_inc_unless().
I'm not sure I like that for a couple of reasons. I think there may still be a
path through the find_memory_block() code that this race condition can occur.
We could take a time sslice after the kobject_get and before getting the
memory_block pointer.
The second reason is that the node sysfs code calls find_memory_block() and it
may be a bit kludgy to have callers of find_memory_block have to reduce the
section_count after using it.
With the way the memory_block structs are kept, retrieved via a kobject_get()
call instead maintained on a local list, there may not be a solution that is
foolproof without changing this.
-Nathan
From: Dave Hansen <hidden> Date: 2010-07-20 19:21:33
On Mon, 2010-07-19 at 22:55 -0500, Nathan Fontenot wrote:
+static u32 get_memory_block_size(void)
+{
+ u32 block_sz;
+
+ block_sz = memory_block_size_bytes();
+
+ /* Validate blk_sz is a power of 2 and not less than section size */
+ if ((block_sz & (block_sz - 1)) || (block_sz < MIN_MEMORY_BLOCK_SIZE))
+ block_sz = MIN_MEMORY_BLOCK_SIZE;
Is this worth a WARN_ON()? Seems pretty bogus if someone is returning
funky block sizes.
-- Dave
Update the find_memory_block declaration to to take a struct mem_section *
so that it matches the definition.
Signed-off-by: Nathan Fontenot <redacted>
---
include/linux/memory.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
Index: linux-2.6/include/linux/memory.h
===================================================================
Update the find_memory_block declaration to to take a struct mem_section *
so that it matches the definition.
Signed-off-by: Nathan Fontenot <redacted>
Update the node sysfs code to be aware of the new capability for a memory
block to contain multiple memory sections. This requires an additional
parameter to unregister_mem_sect_under_nodes so that we know which memory
section of the memory block to unregister.
Signed-off-by: Nathan Fontenot <redacted>
---
drivers/base/memory.c | 2 +-
drivers/base/node.c | 12 ++++++++----
include/linux/node.h | 3 ++-
3 files changed, 11 insertions(+), 6 deletions(-)
Index: linux-2.6/drivers/base/node.c
===================================================================
@@ -346,8 +346,10 @@ int register_mem_sect_under_node(structreturn-EFAULT;if(!node_online(nid))return0;-sect_start_pfn=section_nr_to_pfn(mem_blk->phys_index);-sect_end_pfn=sect_start_pfn+PAGES_PER_SECTION-1;++sect_start_pfn=section_nr_to_pfn(mem_blk->start_phys_index);+sect_end_pfn=section_nr_to_pfn(mem_blk->end_phys_index);+sect_end_pfn+=PAGES_PER_SECTION-1;for(pfn=sect_start_pfn;pfn<=sect_end_pfn;pfn++){intpage_nid;
@@ -371,7 +373,8 @@ int register_mem_sect_under_node(struct}/* unregister memory section under all nodes that it spans */-intunregister_mem_sect_under_nodes(structmemory_block*mem_blk)+intunregister_mem_sect_under_nodes(structmemory_block*mem_blk,+unsignedlongphys_index){NODEMASK_ALLOC(nodemask_t,unlinked_nodes,GFP_KERNEL);unsignedlongpfn,sect_start_pfn,sect_end_pfn;
@@ -383,7 +386,8 @@ int unregister_mem_sect_under_nodes(struif(!unlinked_nodes)return-ENOMEM;nodes_clear(*unlinked_nodes);-sect_start_pfn=section_nr_to_pfn(mem_blk->phys_index);++sect_start_pfn=section_nr_to_pfn(phys_index);sect_end_pfn=sect_start_pfn+PAGES_PER_SECTION-1;for(pfn=sect_start_pfn;pfn<=sect_end_pfn;pfn++){intnid;
@@ -550,9 +550,9 @@ int remove_memory_block(unsigned long nomem=find_memory_block(section);atomic_dec(&mem->section_count);+unregister_mem_sect_under_nodes(mem,__section_nr(section));if(atomic_read(&mem->section_count)==0){-unregister_mem_sect_under_nodes(mem);mem_remove_simple_file(mem,start_phys_index);mem_remove_simple_file(mem,end_phys_index);mem_remove_simple_file(mem,state);
Update the node sysfs code to be aware of the new capability for a memory
block to contain multiple memory sections. This requires an additional
parameter to unregister_mem_sect_under_nodes so that we know which memory
section of the memory block to unregister.
Signed-off-by: Nathan Fontenot <redacted>
@@ -44,7 +44,8 @@ extern int register_cpu_under_node(unsigexternintunregister_cpu_under_node(unsignedintcpu,unsignedintnid);externintregister_mem_sect_under_node(structmemory_block*mem_blk,intnid);-externintunregister_mem_sect_under_nodes(structmemory_block*mem_blk);+externintunregister_mem_sect_under_nodes(structmemory_block*mem_blk,+unsignedlongphys_index);
You also need to update the inline definition of unregister_mem_sect_under_nodes
for the !CONFIG_NUMA case.
-Brian
--
Brian King
Linux on Power Virtualization
IBM Linux Technology Center
Define a version of memory_block_size_bytes() for powerpc/pseries such that
a memory block spans an entire lmb.
Signed-off-by: Nathan Fontenot <redacted>
---
arch/powerpc/platforms/pseries/hotplug-memory.c | 66 +++++++++++++++++++-----
1 file changed, 53 insertions(+), 13 deletions(-)
Index: linux-2.6/arch/powerpc/platforms/pseries/hotplug-memory.c
===================================================================
@@ -17,6 +17,54 @@#include<asm/pSeries_reconfig.h>#include<asm/sparsemem.h>+staticu32get_memblock_size(void)+{+structdevice_node*np;+unsignedintmemblock_size=0;++np=of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");+if(np){+constunsignedint*size;++size=of_get_property(np,"ibm,lmb-size",NULL);+memblock_size=size?*size:0;++of_node_put(np);+}else{+unsignedintmemzero_size=0;+constunsignedint*regs;++np=of_find_node_by_path("/memory@0");+if(np){+regs=of_get_property(np,"reg",NULL);+memzero_size=regs?regs[3]:0;+of_node_put(np);+}++if(memzero_size){+/* We now know the size of memory@0, use this to find+*thefirstmemoryblockandgetitssize.+*/+charbuf[64];++sprintf(buf,"/memory@%x",memzero_size);+np=of_find_node_by_path(buf);+if(np){+regs=of_get_property(np,"reg",NULL);+memblock_size=regs?regs[3]:0;+of_node_put(np);+}+}+}++returnmemblock_size;+}++u32memory_block_size_bytes(void)+{+returnget_memblock_size();+}+staticintpseries_remove_memblock(unsignedlongbase,unsignedintmemblock_size){unsignedlongstart,start_pfn;
@@ -127,30 +175,22 @@ static int pseries_add_memory(struct devstaticintpseries_drconf_memory(unsignedlong*base,unsignedintaction){-structdevice_node*np;-constunsignedlong*memblock_size;+unsignedlongmemblock_size;intrc;-np=of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");-if(!np)+memblock_size=get_memblock_size();+if(!memblock_size)return-EINVAL;-memblock_size=of_get_property(np,"ibm,memblock-size",NULL);-if(!memblock_size){-of_node_put(np);-return-EINVAL;-}-if(action==PSERIES_DRCONF_MEM_ADD){-rc=memblock_add(*base,*memblock_size);+rc=memblock_add(*base,memblock_size);rc=(rc<0)?-EINVAL:0;}elseif(action==PSERIES_DRCONF_MEM_REMOVE){-rc=pseries_remove_memblock(*base,*memblock_size);+rc=pseries_remove_memblock(*base,memblock_size);}else{rc=-EINVAL;}-of_node_put(np);returnrc;}
@@ -126,36 +126,44 @@ config options. -------------------------------- 4 sysfs files for memory hotplug ---------------------------------All sections have their device information under /sys/devices/system/memory as+All sections have their device information in sysfs. Each section is part of+a memory block under /sys/devices/system/memory as /sys/devices/system/memory/memoryXXX-(XXX is section id.)+(XXX is the section id.)-Now, XXX is defined as start_address_of_section / section_size.+Now, XXX is defined as (start_address_of_section / section_size) of the first+section conatined in the memory block. For example, assume 1GiB section size. A device for a memory starting at 0x100000000 is /sys/device/system/memory/memory4 (0x100000000 / 1Gib = 4) This device covers address range [0x100000000 ... 0x140000000)-Under each section, you can see 4 files.+Under each section, you can see 5 files.-/sys/devices/system/memory/memoryXXX/phys_index+/sys/devices/system/memory/memoryXXX/start_phys_index+/sys/devices/system/memory/memoryXXX/end_phys_index /sys/devices/system/memory/memoryXXX/phys_device /sys/devices/system/memory/memoryXXX/state /sys/devices/system/memory/memoryXXX/removable-'phys_index' : read-only and contains section id, same as XXX.-'state' : read-write- at read: contains online/offline state of memory.- at write: user can specify "online", "offline" command-'phys_device': read-only: designed to show the name of physical memory device.- This is not well implemented now.-'removable' : read-only: contains an integer value indicating- whether the memory section is removable or not- removable. A value of 1 indicates that the memory- section is removable and a value of 0 indicates that- it is not removable.+'start_phys_index' : read-only and contains section id of the first section+ in the memory block, same as XXX.+'end_phys_index' : read-only and contains section id of the last section+ in the memory block.+'state' : read-write+ at read: contains online/offline state of memory.+ at write: user can specify "online", "offline" command+ which will be performed on al sections in the block.+'phys_device' : read-only: designed to show the name of physical memory+ device. This is not well implemented now.+'removable' : read-only: contains an integer value indicating+ whether the memory block is removable or not+ removable. A value of 1 indicates that the memory+ block is removable and a value of 0 indicates that+ it is not removable. A memory block is removable only if+ every section in the block is removable. NOTE: These directories/files appear after physical memory hotplug phase.
From: Dave Hansen <hidden> Date: 2010-07-20 19:23:25
On Mon, 2010-07-19 at 22:59 -0500, Nathan Fontenot wrote:
-Now, XXX is defined as start_address_of_section / section_size.
+Now, XXX is defined as (start_address_of_section / section_size) of
the first
+section conatined in the memory block.
For example, assume 1GiB section size. A device for a memory starting
at
0x100000000 is /sys/device/system/memory/memory4
(0x100000000 / 1Gib = 4)
This device covers address range [0x100000000 ... 0x140000000)
-Under each section, you can see 4 files.
+Under each section, you can see 5 files.
-/sys/devices/system/memory/memoryXXX/phys_index
+/sys/devices/system/memory/memoryXXX/start_phys_index
+/sys/devices/system/memory/memoryXXX/end_phys_index
Just wanted to make sure you didn't forget to update this after KAME's
comments on the first couple of patches.
-- Dave
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2010-07-31 05:38:50
On Mon, 2010-07-19 at 22:45 -0500, Nathan Fontenot wrote:
This set of patches de-couples the idea that there is a single
directory in sysfs for each memory section. The intent of the
patches is to reduce the number of sysfs directories created to
resolve a boot-time performance issue. On very large systems
boot time are getting very long (as seen on powerpc hardware)
due to the enormous number of sysfs directories being created.
On a system with 1 TB of memory we create ~63,000 directories.
For even larger systems boot times are being measured in hours.
Greg, Kame, how do we proceed with these ? I'm happy to put them in
powerpc.git with appropriate acks or will you take them ?
Cheers,
Ben.
On Sat, Jul 31, 2010 at 03:36:24PM +1000, Benjamin Herrenschmidt wrote:
On Mon, 2010-07-19 at 22:45 -0500, Nathan Fontenot wrote:
quoted
This set of patches de-couples the idea that there is a single
directory in sysfs for each memory section. The intent of the
patches is to reduce the number of sysfs directories created to
resolve a boot-time performance issue. On very large systems
boot time are getting very long (as seen on powerpc hardware)
due to the enormous number of sysfs directories being created.
On a system with 1 TB of memory we create ~63,000 directories.
For even larger systems boot times are being measured in hours.
Greg, Kame, how do we proceed with these ? I'm happy to put them in
powerpc.git with appropriate acks or will you take them ?
I thought there would be at least one more round of these patches based
on the review comments, right?
I'll be glad to take them when everyone agrees with them.
thanks,
greg k-h
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2010-08-01 00:30:20
On Sat, 2010-07-31 at 12:55 -0700, Greg KH wrote:
On Sat, Jul 31, 2010 at 03:36:24PM +1000, Benjamin Herrenschmidt wrote:
quoted
On Mon, 2010-07-19 at 22:45 -0500, Nathan Fontenot wrote:
quoted
This set of patches de-couples the idea that there is a single
directory in sysfs for each memory section. The intent of the
patches is to reduce the number of sysfs directories created to
resolve a boot-time performance issue. On very large systems
boot time are getting very long (as seen on powerpc hardware)
due to the enormous number of sysfs directories being created.
On a system with 1 TB of memory we create ~63,000 directories.
For even larger systems boot times are being measured in hours.
Greg, Kame, how do we proceed with these ? I'm happy to put them in
powerpc.git with appropriate acks or will you take them ?
I thought there would be at least one more round of these patches based
on the review comments, right?
Yes, but I was nontheless inquiring whether I should pick them up after
said repost :-)
I'll be glad to take them when everyone agrees with them.
Ok, good, one less thing to worry about in powerpc patchwork :-)
Cheers,
Ben.