mm: compaction: add /proc trigger for memory compaction
Mel Gorman [Mon, 24 May 2010 21:32:28 +0000 (14:32 -0700)]
Add a proc file /proc/sys/vm/compact_memory.  When an arbitrary value is
written to the file, all zones are compacted.  The expected user of such a
trigger is a job scheduler that prepares the system before the target
application runs.

Signed-off-by: Mel Gorman <mel@csn.ul.ie>
Acked-by: Rik van Riel <riel@redhat.com>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Minchan Kim <minchan.kim@gmail.com>
Reviewed-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Reviewed-by: Christoph Lameter <cl@linux-foundation.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Documentation/sysctl/vm.txt
include/linux/compaction.h
kernel/sysctl.c
mm/compaction.c

index 6c7d18c..56dd29b 100644 (file)
@@ -19,6 +19,7 @@ files can be found in mm/swap.c.
 Currently, these files are in /proc/sys/vm:
 
 - block_dump
+- compact_memory
 - dirty_background_bytes
 - dirty_background_ratio
 - dirty_bytes
@@ -64,6 +65,15 @@ information on block I/O debugging is in Documentation/laptops/laptop-mode.txt.
 
 ==============================================================
 
+compact_memory
+
+Available only when CONFIG_COMPACTION is set. When 1 is written to the file,
+all zones are compacted such that free memory is available in contiguous
+blocks where possible. This can be important for example in the allocation of
+huge pages although processes will also directly compact memory as required.
+
+==============================================================
+
 dirty_background_bytes
 
 Contains the amount of dirty memory at which the pdflush background writeback
index 465ca88..5723888 100644 (file)
@@ -6,4 +6,10 @@
 #define COMPACT_PARTIAL                1
 #define COMPACT_COMPLETE       2
 
+#ifdef CONFIG_COMPACTION
+extern int sysctl_compact_memory;
+extern int sysctl_compaction_handler(struct ctl_table *table, int write,
+                       void __user *buffer, size_t *length, loff_t *ppos);
+#endif /* CONFIG_COMPACTION */
+
 #endif /* _LINUX_COMPACTION_H */
index 4c93486..284f330 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/highuid.h>
 #include <linux/writeback.h>
 #include <linux/ratelimit.h>
+#include <linux/compaction.h>
 #include <linux/hugetlb.h>
 #include <linux/initrd.h>
 #include <linux/key.h>
@@ -1121,6 +1122,15 @@ static struct ctl_table vm_table[] = {
                .mode           = 0644,
                .proc_handler   = drop_caches_sysctl_handler,
        },
+#ifdef CONFIG_COMPACTION
+       {
+               .procname       = "compact_memory",
+               .data           = &sysctl_compact_memory,
+               .maxlen         = sizeof(int),
+               .mode           = 0200,
+               .proc_handler   = sysctl_compaction_handler,
+       },
+#endif /* CONFIG_COMPACTION */
        {
                .procname       = "min_free_kbytes",
                .data           = &min_free_kbytes,
index be1ff3f..77854fb 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/compaction.h>
 #include <linux/mm_inline.h>
 #include <linux/backing-dev.h>
+#include <linux/sysctl.h>
 #include "internal.h"
 
 /*
@@ -391,3 +392,64 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
 
        return ret;
 }
+
+/* Compact all zones within a node */
+static int compact_node(int nid)
+{
+       int zoneid;
+       pg_data_t *pgdat;
+       struct zone *zone;
+
+       if (nid < 0 || nid >= nr_node_ids || !node_online(nid))
+               return -EINVAL;
+       pgdat = NODE_DATA(nid);
+
+       /* Flush pending updates to the LRU lists */
+       lru_add_drain_all();
+
+       for (zoneid = 0; zoneid < MAX_NR_ZONES; zoneid++) {
+               struct compact_control cc = {
+                       .nr_freepages = 0,
+                       .nr_migratepages = 0,
+               };
+
+               zone = &pgdat->node_zones[zoneid];
+               if (!populated_zone(zone))
+                       continue;
+
+               cc.zone = zone;
+               INIT_LIST_HEAD(&cc.freepages);
+               INIT_LIST_HEAD(&cc.migratepages);
+
+               compact_zone(zone, &cc);
+
+               VM_BUG_ON(!list_empty(&cc.freepages));
+               VM_BUG_ON(!list_empty(&cc.migratepages));
+       }
+
+       return 0;
+}
+
+/* Compact all nodes in the system */
+static int compact_nodes(void)
+{
+       int nid;
+
+       for_each_online_node(nid)
+               compact_node(nid);
+
+       return COMPACT_COMPLETE;
+}
+
+/* The written value is actually unused, all memory is compacted */
+int sysctl_compact_memory;
+
+/* This is the entry point for compacting all nodes via /proc/sys/vm */
+int sysctl_compaction_handler(struct ctl_table *table, int write,
+                       void __user *buffer, size_t *length, loff_t *ppos)
+{
+       if (write)
+               return compact_nodes();
+
+       return 0;
+}