There's typical usage of si_meminfo as below: si_meminfo(&si); threshold = si->totalram - si.totalhigh; It may cause underflow if memory hotplug races with si_meminfo() because there's no mechanism to protect si_meminfo() from memory hotplug operations. And some callers expects that si_meminfo() is a lightweight operations. So introduce a lightweight mechanism to protect si_meminfo() from memory hotplug operations. Signed-off-by: Jiang Liu <jiang.liu@xxxxxxxxxx> --- mm/page_alloc.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 6884dc5..5cf03d4 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2831,18 +2831,34 @@ static inline void show_node(struct zone *zone) void si_meminfo(struct sysinfo *val) { int nid; - unsigned long present_pages = 0; + unsigned long present_pages; + val->sharedram = 0; + val->mem_unit = PAGE_SIZE; + +restart: + present_pages = 0; for_each_node_state(nid, N_MEMORY) present_pages += node_present_pages(nid); val->totalram = present_pages; - val->sharedram = 0; val->freeram = global_page_state(NR_FREE_PAGES); val->bufferram = nr_blockdev_pages(); val->totalhigh = totalhigh_pages; val->freehigh = nr_free_highpages(); - val->mem_unit = PAGE_SIZE; + + /* + * si_meminfo() may generate invalid results because it isn't protected + * from memory hotplug operaitons. And some callers expect that it's an + * lightweigh operation. So provide minimal protections without heavy + * overhead. + */ + if (val->totalram < val->freeram || + val->totalram < val->bufferram || + val->totalram < val->totalhigh || + val->totalhigh < val->freehigh || + val->freeram < val->freehigh) + goto restart; } EXPORT_SYMBOL(si_meminfo); @@ -2854,6 +2870,7 @@ void si_meminfo_node(struct sysinfo *val, int nid) unsigned long managed_pages = 0; pg_data_t *pgdat = NODE_DATA(nid); + lock_memory_hotplug(); for (zone_type = 0; zone_type < MAX_NR_ZONES; zone_type++) managed_pages += pgdat->node_zones[zone_type].managed_pages; @@ -2874,6 +2891,7 @@ void si_meminfo_node(struct sysinfo *val, int nid) val->freehigh = 0; #endif val->mem_unit = PAGE_SIZE; + unlock_memory_hotplug(); } #endif -- 1.7.9.5 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to majordomo@xxxxxxxxx. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"dont@xxxxxxxxx"> email@xxxxxxxxx </a>