2014-11-11 01:28:06

by Tang Chen

[permalink] [raw]
Subject: [PATCH v2 0/2] Fix node meminfo and zoneinfo corruption.

There are two problems in memory hot-add progress:

1. When hot-adding a node without onlining any page, node meminfo corrupted:

# hot-add node2 (memory not onlined)
# cat /sys/device/system/node/node2/meminfo
Node 2 MemTotal: 33554432 kB /* corrupted */
Node 2 MemFree: 0 kB
Node 2 MemUsed: 33554432 kB
Node 2 Active: 0 kB
......


2. When onlining memory on node2, node2 zoneinfo and node3 meminfo corrupted:

# for ((i = 2048; i < 2064; i++)); do echo online_movable > /sys/devices/system/node/node2/memory$i/state; done
# cat /sys/devices/system/node/node2/meminfo
Node 2 MemTotal: 33554432 kB
Node 2 MemFree: 33549092 kB
Node 2 MemUsed: 5340 kB
......
# cat /sys/devices/system/node/node3/meminfo
Node 3 MemTotal: 0 kB
Node 3 MemFree: 248 kB /* corrupted, should be 0 */
Node 3 MemUsed: 0 kB
......

# cat /proc/zoneinfo
......
Node 2, zone Movable
......
spanned 8388608
present 16777216 /* corrupted, should be 8388608 */
managed 8388608



Change log v1 -> v2:
1. Replace patch 2/2 with a new one. It provides the simplest way to
fix problem 2.

Tang Chen (2):
mem-hotplug: Reset node managed pages when hot-adding a new pgdat.
mem-hotplug: Reset node present pages when hot-adding a new pgdat.

include/linux/bootmem.h | 1 +
mm/bootmem.c | 9 +++++----
mm/memory_hotplug.c | 24 ++++++++++++++++++++++++
mm/nobootmem.c | 8 +++++---
4 files changed, 35 insertions(+), 7 deletions(-)

--
1.8.3.1


2014-11-11 01:28:17

by Tang Chen

[permalink] [raw]
Subject: [PATCH v2 2/2] mem-hotplug: Reset node present pages when hot-adding a new pgdat.

When onlining memory on node2, node2 zoneinfo and node3 meminfo corrupted:

# for ((i = 2048; i < 2064; i++)); do echo online_movable > /sys/devices/system/node/node2/memory$i/state; done
# cat /sys/devices/system/node/node2/meminfo
Node 2 MemTotal: 33554432 kB
Node 2 MemFree: 33549092 kB
Node 2 MemUsed: 5340 kB
......
# cat /sys/devices/system/node/node3/meminfo
Node 3 MemTotal: 0 kB
Node 3 MemFree: 248 kB /* corrupted, should be 0 */
Node 3 MemUsed: 0 kB
......

# cat /proc/zoneinfo
......
Node 2, zone Movable
......
spanned 8388608
present 16777216 /* corrupted, should be 8388608 */
managed 8388608


When memory is hot-added, all the memory is in offline state. So
clear all zone->present_pages because they will be updated in
online_pages() and offline_pages().

Signed-off-by: Tang Chen <[email protected]>
Reviewed-by: Yasuaki Ishimatsu <[email protected]>
---
mm/memory_hotplug.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)

diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index 8aba12b..26eac61 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -1067,6 +1067,14 @@ out:
}
#endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */

+static void reset_node_present_pages(pg_data_t *pgdat)
+{
+ struct zone *z;
+
+ for (z = pgdat->node_zones; z < pgdat->node_zones + MAX_NR_ZONES; z++)
+ z->present_pages = 0;
+}
+
/* we are OK calling __meminit stuff here - we have CONFIG_MEMORY_HOTPLUG */
static pg_data_t __ref *hotadd_new_pgdat(int nid, u64 start)
{
@@ -1105,6 +1113,13 @@ static pg_data_t __ref *hotadd_new_pgdat(int nid, u64 start)
*/
reset_node_managed_pages(pgdat);

+ /*
+ * When memory is hot-added, all the memory is in offline state. So
+ * clear all zones' present_pages because they will be updated in
+ * online_pages() and offline_pages().
+ */
+ reset_node_present_pages(pgdat);
+
return pgdat;
}

--
1.8.3.1

2014-11-11 01:28:10

by Tang Chen

[permalink] [raw]
Subject: [PATCH v2 1/2] mem-hotplug: Reset node managed pages when hot-adding a new pgdat.

In free_area_init_core(), zone->managed_pages is set to an approximate
value for lowmem, and will be adjusted when the bootmem allocator frees
pages into the buddy system. But free_area_init_core() is also called
by hotadd_new_pgdat() when hot-adding memory. As a result, zone->managed_pages
of the newly added node's pgdat is set to an approximate value in the
very beginning. Even if the memory on that node has node been onlined,
/sys/device/system/node/nodeXXX/meminfo has wrong value.

hot-add node2 (memory not onlined)
cat /sys/device/system/node/node2/meminfo
Node 2 MemTotal: 33554432 kB
Node 2 MemFree: 0 kB
Node 2 MemUsed: 33554432 kB
Node 2 Active: 0 kB

This patch fixes this problem by reset node managed pages to 0 after hot-adding
a new node.

1. Move reset_managed_pages_done from reset_node_managed_pages() to reset_all_zones_managed_pages()
2. Make reset_node_managed_pages() non-static
3. Call reset_node_managed_pages() in hotadd_new_pgdat() after pgdat is initialized

Signed-off-by: Tang Chen <[email protected]>
Signed-off-by: Yasuaki Ishimatsu <[email protected]>
---
include/linux/bootmem.h | 1 +
mm/bootmem.c | 9 +++++----
mm/memory_hotplug.c | 9 +++++++++
mm/nobootmem.c | 8 +++++---
4 files changed, 20 insertions(+), 7 deletions(-)

diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h
index 4e2bd4c..0995c2d 100644
--- a/include/linux/bootmem.h
+++ b/include/linux/bootmem.h
@@ -46,6 +46,7 @@ extern unsigned long init_bootmem_node(pg_data_t *pgdat,
extern unsigned long init_bootmem(unsigned long addr, unsigned long memend);

extern unsigned long free_all_bootmem(void);
+extern void reset_node_managed_pages(pg_data_t *pgdat);
extern void reset_all_zones_managed_pages(void);

extern void free_bootmem_node(pg_data_t *pgdat,
diff --git a/mm/bootmem.c b/mm/bootmem.c
index 8a000ce..477be69 100644
--- a/mm/bootmem.c
+++ b/mm/bootmem.c
@@ -243,13 +243,10 @@ static unsigned long __init free_all_bootmem_core(bootmem_data_t *bdata)

static int reset_managed_pages_done __initdata;

-static inline void __init reset_node_managed_pages(pg_data_t *pgdat)
+void reset_node_managed_pages(pg_data_t *pgdat)
{
struct zone *z;

- if (reset_managed_pages_done)
- return;
-
for (z = pgdat->node_zones; z < pgdat->node_zones + MAX_NR_ZONES; z++)
z->managed_pages = 0;
}
@@ -258,8 +255,12 @@ void __init reset_all_zones_managed_pages(void)
{
struct pglist_data *pgdat;

+ if (reset_managed_pages_done)
+ return;
+
for_each_online_pgdat(pgdat)
reset_node_managed_pages(pgdat);
+
reset_managed_pages_done = 1;
}

diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index 29d8693..8aba12b 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -31,6 +31,7 @@
#include <linux/stop_machine.h>
#include <linux/hugetlb.h>
#include <linux/memblock.h>
+#include <linux/bootmem.h>

#include <asm/tlbflush.h>

@@ -1096,6 +1097,14 @@ static pg_data_t __ref *hotadd_new_pgdat(int nid, u64 start)
build_all_zonelists(pgdat, NULL);
mutex_unlock(&zonelists_mutex);

+ /*
+ * zone->managed_pages is set to an approximate value in
+ * free_area_init_core(), which will cause
+ * /sys/device/system/node/nodeX/meminfo has wrong data.
+ * So reset it to 0 before any memory is onlined.
+ */
+ reset_node_managed_pages(pgdat);
+
return pgdat;
}

diff --git a/mm/nobootmem.c b/mm/nobootmem.c
index 7c7ab32..90b5046 100644
--- a/mm/nobootmem.c
+++ b/mm/nobootmem.c
@@ -145,12 +145,10 @@ static unsigned long __init free_low_memory_core_early(void)

static int reset_managed_pages_done __initdata;

-static inline void __init reset_node_managed_pages(pg_data_t *pgdat)
+void reset_node_managed_pages(pg_data_t *pgdat)
{
struct zone *z;

- if (reset_managed_pages_done)
- return;
for (z = pgdat->node_zones; z < pgdat->node_zones + MAX_NR_ZONES; z++)
z->managed_pages = 0;
}
@@ -159,8 +157,12 @@ void __init reset_all_zones_managed_pages(void)
{
struct pglist_data *pgdat;

+ if (reset_managed_pages_done)
+ return;
+
for_each_online_pgdat(pgdat)
reset_node_managed_pages(pgdat);
+
reset_managed_pages_done = 1;
}

--
1.8.3.1

2014-11-11 04:55:29

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH v2 1/2] mem-hotplug: Reset node managed pages when hot-adding a new pgdat.

On Tue, Nov 11, 2014 at 09:27:06AM +0800, Tang Chen wrote:
> In free_area_init_core(), zone->managed_pages is set to an approximate
> value for lowmem, and will be adjusted when the bootmem allocator frees
> pages into the buddy system. But free_area_init_core() is also called
> by hotadd_new_pgdat() when hot-adding memory. As a result, zone->managed_pages
> of the newly added node's pgdat is set to an approximate value in the
> very beginning. Even if the memory on that node has node been onlined,
> /sys/device/system/node/nodeXXX/meminfo has wrong value.
>
> hot-add node2 (memory not onlined)
> cat /sys/device/system/node/node2/meminfo
> Node 2 MemTotal: 33554432 kB
> Node 2 MemFree: 0 kB
> Node 2 MemUsed: 33554432 kB
> Node 2 Active: 0 kB
>
> This patch fixes this problem by reset node managed pages to 0 after hot-adding
> a new node.
>
> 1. Move reset_managed_pages_done from reset_node_managed_pages() to reset_all_zones_managed_pages()
> 2. Make reset_node_managed_pages() non-static
> 3. Call reset_node_managed_pages() in hotadd_new_pgdat() after pgdat is initialized
>
> Signed-off-by: Tang Chen <[email protected]>
> Signed-off-by: Yasuaki Ishimatsu <[email protected]>
> ---
> include/linux/bootmem.h | 1 +
> mm/bootmem.c | 9 +++++----
> mm/memory_hotplug.c | 9 +++++++++
> mm/nobootmem.c | 8 +++++---
> 4 files changed, 20 insertions(+), 7 deletions(-)

<formletter>

This is not the correct way to submit patches for inclusion in the
stable kernel tree. Please read Documentation/stable_kernel_rules.txt
for how to do this properly.

</formletter>

2014-11-12 04:29:51

by Yasuaki Ishimatsu

[permalink] [raw]
Subject: Re: [PATCH v2 2/2] mem-hotplug: Reset node present pages when hot-adding a new pgdat.

(2014/11/11 10:27), Tang Chen wrote:
> When onlining memory on node2, node2 zoneinfo and node3 meminfo corrupted:
>
> # for ((i = 2048; i < 2064; i++)); do echo online_movable > /sys/devices/system/node/node2/memory$i/state; done
> # cat /sys/devices/system/node/node2/meminfo
> Node 2 MemTotal: 33554432 kB
> Node 2 MemFree: 33549092 kB
> Node 2 MemUsed: 5340 kB
> ......
> # cat /sys/devices/system/node/node3/meminfo
> Node 3 MemTotal: 0 kB
> Node 3 MemFree: 248 kB /* corrupted, should be 0 */
> Node 3 MemUsed: 0 kB
> ......
>
> # cat /proc/zoneinfo
> ......
> Node 2, zone Movable
> ......
> spanned 8388608
> present 16777216 /* corrupted, should be 8388608 */
> managed 8388608
>
>
> When memory is hot-added, all the memory is in offline state. So
> clear all zone->present_pages because they will be updated in
> online_pages() and offline_pages().
>
> Signed-off-by: Tang Chen <[email protected]>

> Reviewed-by: Yasuaki Ishimatsu <[email protected]>

I has reviewed your patch. But I have one comment.
Please see below.

> ---
> mm/memory_hotplug.c | 15 +++++++++++++++
> 1 file changed, 15 insertions(+)
>
> diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
> index 8aba12b..26eac61 100644
> --- a/mm/memory_hotplug.c
> +++ b/mm/memory_hotplug.c
> @@ -1067,6 +1067,14 @@ out:
> }
> #endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */
>

> +static void reset_node_present_pages(pg_data_t *pgdat)
> +{
> + struct zone *z;
> +
> + for (z = pgdat->node_zones; z < pgdat->node_zones + MAX_NR_ZONES; z++)
> + z->present_pages = 0;
> +}

You should reset pgdat->node_present_pages.
pgdat->node_present_pages is set to realtotalpages by calculate_node_totalpages().
And it is also incremented/decremented by memroy online/offline.
So pgdat->node_present_pages is broken internally too.

Thanks,
Yasuaki Ishimatsu.

> +
> /* we are OK calling __meminit stuff here - we have CONFIG_MEMORY_HOTPLUG */
> static pg_data_t __ref *hotadd_new_pgdat(int nid, u64 start)
> {
> @@ -1105,6 +1113,13 @@ static pg_data_t __ref *hotadd_new_pgdat(int nid, u64 start)
> */
> reset_node_managed_pages(pgdat);
>
> + /*
> + * When memory is hot-added, all the memory is in offline state. So
> + * clear all zones' present_pages because they will be updated in
> + * online_pages() and offline_pages().
> + */
> + reset_node_present_pages(pgdat);
> +
> return pgdat;
> }
>
>