[RFC PATCH v5 02/19] memory-hotplug: implement offline_memory()

2012-07-27 Thread Wen Congyang
The function offline_memory() will be called when hot removing a
memory device. The memory device may contain more than one memory
block. If the memory block has been offlined, __offline_pages()
will fail. So we should try to offline one memory block at a
time.

If the memory block is offlined in offline_memory(), we also
update it's state, and notify the userspace that its state is
changed.

The function offline_memory() also check each memory block's
state. So there is no need to check the memory block's state
before calling offline_memory().

CC: David Rientjes 
CC: Jiang Liu 
CC: Len Brown 
CC: Benjamin Herrenschmidt 
CC: Paul Mackerras 
CC: Christoph Lameter 
Cc: Minchan Kim 
CC: Andrew Morton 
CC: KOSAKI Motohiro 
CC: Yasuaki Ishimatsu 
CC: Vasilis Liaskovitis 
Signed-off-by: Wen Congyang 
---
 drivers/base/memory.c  |   31 +++
 include/linux/memory_hotplug.h |2 ++
 mm/memory_hotplug.c|   37 -
 3 files changed, 65 insertions(+), 5 deletions(-)

diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index 44e7de6..86c8821 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -275,13 +275,11 @@ memory_block_action(unsigned long phys_index, unsigned 
long action)
return ret;
 }
 
-static int memory_block_change_state(struct memory_block *mem,
+static int __memory_block_change_state(struct memory_block *mem,
unsigned long to_state, unsigned long from_state_req)
 {
int ret = 0;
 
-   mutex_lock(>state_mutex);
-
if (mem->state != from_state_req) {
ret = -EINVAL;
goto out;
@@ -309,10 +307,20 @@ static int memory_block_change_state(struct memory_block 
*mem,
break;
}
 out:
-   mutex_unlock(>state_mutex);
return ret;
 }
 
+static int memory_block_change_state(struct memory_block *mem,
+   unsigned long to_state, unsigned long from_state_req)
+{
+   int ret;
+
+   mutex_lock(>state_mutex);
+   ret = __memory_block_change_state(mem, to_state, from_state_req);
+   mutex_unlock(>state_mutex);
+
+   return ret;
+}
 static ssize_t
 store_mem_state(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
@@ -653,6 +661,21 @@ int unregister_memory_section(struct mem_section *section)
 }
 
 /*
+ * offline one memory block. If the memory block has been offlined, do nothing.
+ */
+int offline_memory_block(struct memory_block *mem)
+{
+   int ret = 0;
+
+   mutex_lock(>state_mutex);
+   if (mem->state != MEM_OFFLINE)
+   ret = __memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE);
+   mutex_unlock(>state_mutex);
+
+   return ret;
+}
+
+/*
  * Initialize the sysfs support for memory devices...
  */
 int __init memory_dev_init(void)
diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h
index c183f39..0b040bb 100644
--- a/include/linux/memory_hotplug.h
+++ b/include/linux/memory_hotplug.h
@@ -10,6 +10,7 @@ struct page;
 struct zone;
 struct pglist_data;
 struct mem_section;
+struct memory_block;
 
 #ifdef CONFIG_MEMORY_HOTPLUG
 
@@ -234,6 +235,7 @@ extern int mem_online_node(int nid);
 extern int add_memory(int nid, u64 start, u64 size);
 extern int arch_add_memory(int nid, u64 start, u64 size);
 extern int offline_pages(unsigned long start_pfn, unsigned long nr_pages);
+extern int offline_memory_block(struct memory_block *mem);
 extern int offline_memory(u64 start, u64 size);
 extern int sparse_add_one_section(struct zone *zone, unsigned long start_pfn,
int nr_pages);
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index 7a6659f..992454a 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -997,7 +997,42 @@ int offline_pages(unsigned long start_pfn, unsigned long 
nr_pages)
 
 int offline_memory(u64 start, u64 size)
 {
-   return -EINVAL;
+   struct memory_block *mem = NULL;
+   struct mem_section *section;
+   unsigned long start_pfn, end_pfn;
+   unsigned long pfn, section_nr;
+   int ret;
+
+   start_pfn = PFN_DOWN(start);
+   end_pfn = start_pfn + PFN_DOWN(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;
+
+   ret = offline_memory_block(mem);
+   if (ret) {
+ 

[RFC PATCH v5 02/19] memory-hotplug: implement offline_memory()

2012-07-27 Thread Wen Congyang
The function offline_memory() will be called when hot removing a
memory device. The memory device may contain more than one memory
block. If the memory block has been offlined, __offline_pages()
will fail. So we should try to offline one memory block at a
time.

If the memory block is offlined in offline_memory(), we also
update it's state, and notify the userspace that its state is
changed.

The function offline_memory() also check each memory block's
state. So there is no need to check the memory block's state
before calling offline_memory().

CC: David Rientjes rient...@google.com
CC: Jiang Liu liu...@gmail.com
CC: Len Brown len.br...@intel.com
CC: Benjamin Herrenschmidt b...@kernel.crashing.org
CC: Paul Mackerras pau...@samba.org
CC: Christoph Lameter c...@linux.com
Cc: Minchan Kim minchan@gmail.com
CC: Andrew Morton a...@linux-foundation.org
CC: KOSAKI Motohiro kosaki.motoh...@jp.fujitsu.com
CC: Yasuaki Ishimatsu isimatu.yasu...@jp.fujitsu.com
CC: Vasilis Liaskovitis vasilis.liaskovi...@profitbricks.com
Signed-off-by: Wen Congyang we...@cn.fujitsu.com
---
 drivers/base/memory.c  |   31 +++
 include/linux/memory_hotplug.h |2 ++
 mm/memory_hotplug.c|   37 -
 3 files changed, 65 insertions(+), 5 deletions(-)

diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index 44e7de6..86c8821 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -275,13 +275,11 @@ memory_block_action(unsigned long phys_index, unsigned 
long action)
return ret;
 }
 
-static int memory_block_change_state(struct memory_block *mem,
+static int __memory_block_change_state(struct memory_block *mem,
unsigned long to_state, unsigned long from_state_req)
 {
int ret = 0;
 
-   mutex_lock(mem-state_mutex);
-
if (mem-state != from_state_req) {
ret = -EINVAL;
goto out;
@@ -309,10 +307,20 @@ static int memory_block_change_state(struct memory_block 
*mem,
break;
}
 out:
-   mutex_unlock(mem-state_mutex);
return ret;
 }
 
+static int memory_block_change_state(struct memory_block *mem,
+   unsigned long to_state, unsigned long from_state_req)
+{
+   int ret;
+
+   mutex_lock(mem-state_mutex);
+   ret = __memory_block_change_state(mem, to_state, from_state_req);
+   mutex_unlock(mem-state_mutex);
+
+   return ret;
+}
 static ssize_t
 store_mem_state(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
@@ -653,6 +661,21 @@ int unregister_memory_section(struct mem_section *section)
 }
 
 /*
+ * offline one memory block. If the memory block has been offlined, do nothing.
+ */
+int offline_memory_block(struct memory_block *mem)
+{
+   int ret = 0;
+
+   mutex_lock(mem-state_mutex);
+   if (mem-state != MEM_OFFLINE)
+   ret = __memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE);
+   mutex_unlock(mem-state_mutex);
+
+   return ret;
+}
+
+/*
  * Initialize the sysfs support for memory devices...
  */
 int __init memory_dev_init(void)
diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h
index c183f39..0b040bb 100644
--- a/include/linux/memory_hotplug.h
+++ b/include/linux/memory_hotplug.h
@@ -10,6 +10,7 @@ struct page;
 struct zone;
 struct pglist_data;
 struct mem_section;
+struct memory_block;
 
 #ifdef CONFIG_MEMORY_HOTPLUG
 
@@ -234,6 +235,7 @@ extern int mem_online_node(int nid);
 extern int add_memory(int nid, u64 start, u64 size);
 extern int arch_add_memory(int nid, u64 start, u64 size);
 extern int offline_pages(unsigned long start_pfn, unsigned long nr_pages);
+extern int offline_memory_block(struct memory_block *mem);
 extern int offline_memory(u64 start, u64 size);
 extern int sparse_add_one_section(struct zone *zone, unsigned long start_pfn,
int nr_pages);
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index 7a6659f..992454a 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -997,7 +997,42 @@ int offline_pages(unsigned long start_pfn, unsigned long 
nr_pages)
 
 int offline_memory(u64 start, u64 size)
 {
-   return -EINVAL;
+   struct memory_block *mem = NULL;
+   struct mem_section *section;
+   unsigned long start_pfn, end_pfn;
+   unsigned long pfn, section_nr;
+   int ret;
+
+   start_pfn = PFN_DOWN(start);
+   end_pfn = start_pfn + PFN_DOWN(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) 
+