[PATCH 2/2] mm: protect si_meminfo() and si_meminfo_node() from memory hotplug operations

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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>


[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]