Add __GFP_MOVABLE for callers to flag allocations from high memory that may be migrated
authorMel Gorman <mel@csn.ul.ie>
Tue, 17 Jul 2007 11:03:05 +0000 (04:03 -0700)
committerLinus Torvalds <torvalds@woody.linux-foundation.org>
Tue, 17 Jul 2007 17:22:59 +0000 (10:22 -0700)
It is often known at allocation time whether a page may be migrated or not.
This patch adds a flag called __GFP_MOVABLE and a new mask called
GFP_HIGH_MOVABLE.  Allocations using the __GFP_MOVABLE can be either migrated
using the page migration mechanism or reclaimed by syncing with backing
storage and discarding.

An API function very similar to alloc_zeroed_user_highpage() is added for
__GFP_MOVABLE allocations called alloc_zeroed_user_highpage_movable().  The
flags used by alloc_zeroed_user_highpage() are not changed because it would
change the semantics of an existing API.  After this patch is applied there
are no in-kernel users of alloc_zeroed_user_highpage() so it probably should
be marked deprecated if this patch is merged.

Note that this patch includes a minor cleanup to the use of __GFP_ZERO in
shmem.c to keep all flag modifications to inode->mapping in the
shmem_dir_alloc() helper function.  This clean-up suggestion is courtesy of
Hugh Dickens.

Additional credit goes to Christoph Lameter and Linus Torvalds for shaping the
concept.  Credit to Hugh Dickens for catching issues with shmem swap vector
and ramfs allocations.

[akpm@linux-foundation.org: build fix]
[hugh@veritas.com: __GFP_ZERO cleanup]
Signed-off-by: Mel Gorman <mel@csn.ul.ie>
Cc: Andy Whitcroft <apw@shadowen.org>
Cc: Christoph Lameter <clameter@sgi.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
19 files changed:
fs/buffer.c
fs/inode.c
fs/ramfs/inode.c
include/asm-alpha/page.h
include/asm-cris/page.h
include/asm-h8300/page.h
include/asm-i386/page.h
include/asm-ia64/page.h
include/asm-m32r/page.h
include/asm-m68knommu/page.h
include/asm-s390/page.h
include/asm-x86_64/page.h
include/linux/gfp.h
include/linux/highmem.h
mm/memory.c
mm/mempolicy.c
mm/migrate.c
mm/shmem.c
mm/swap_state.c

index 424165b569f80e796db474661b31170b1627a301..94344b2e0b46ccd3f4e19070e020be4ed6535bfd 100644 (file)
@@ -982,7 +982,7 @@ grow_dev_page(struct block_device *bdev, sector_t block,
        struct buffer_head *bh;
 
        page = find_or_create_page(inode->i_mapping, index,
-               mapping_gfp_mask(inode->i_mapping) & ~__GFP_FS);
+               (mapping_gfp_mask(inode->i_mapping) & ~__GFP_FS)|__GFP_MOVABLE);
        if (!page)
                return NULL;
 
index 9a012cc5b6cd6e060977a6b13565c8f11d98dfca..47b87b071de37504a918fd0f21e03700276666a9 100644 (file)
@@ -145,7 +145,7 @@ static struct inode *alloc_inode(struct super_block *sb)
                mapping->a_ops = &empty_aops;
                mapping->host = inode;
                mapping->flags = 0;
-               mapping_set_gfp_mask(mapping, GFP_HIGHUSER);
+               mapping_set_gfp_mask(mapping, GFP_HIGHUSER_PAGECACHE);
                mapping->assoc_mapping = NULL;
                mapping->backing_dev_info = &default_backing_dev_info;
 
@@ -519,7 +519,13 @@ repeat:
  *     new_inode       - obtain an inode
  *     @sb: superblock
  *
- *     Allocates a new inode for given superblock.
+ *     Allocates a new inode for given superblock. The default gfp_mask
+ *     for allocations related to inode->i_mapping is GFP_HIGHUSER_PAGECACHE.
+ *     If HIGHMEM pages are unsuitable or it is known that pages allocated
+ *     for the page cache are not reclaimable or migratable,
+ *     mapping_set_gfp_mask() must be called with suitable flags on the
+ *     newly created inode's mapping
+ *
  */
 struct inode *new_inode(struct super_block *sb)
 {
index d40d22b347b767843b7048d7d488f536d774a65d..ef2b46d099ffb195e45c4c462a68a55090147580 100644 (file)
@@ -60,6 +60,7 @@ struct inode *ramfs_get_inode(struct super_block *sb, int mode, dev_t dev)
                inode->i_blocks = 0;
                inode->i_mapping->a_ops = &ramfs_aops;
                inode->i_mapping->backing_dev_info = &ramfs_backing_dev_info;
+               mapping_set_gfp_mask(inode->i_mapping, GFP_HIGHUSER);
                inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
                switch (mode & S_IFMT) {
                default:
index d2bed3cb33ff106309d297afe747cf73513276fd..bae7f05716d4b9ac9bd5f99f1da92b9eed0589dd 100644 (file)
@@ -17,7 +17,8 @@
 extern void clear_page(void *page);
 #define clear_user_page(page, vaddr, pg)       clear_page(page)
 
-#define alloc_zeroed_user_highpage(vma, vaddr) alloc_page_vma(GFP_HIGHUSER | __GFP_ZERO, vma, vmaddr)
+#define __alloc_zeroed_user_highpage(movableflags, vma, vaddr) \
+       alloc_page_vma(GFP_HIGHUSER | __GFP_ZERO | movableflags, vma, vmaddr)
 #define __HAVE_ARCH_ALLOC_ZEROED_USER_HIGHPAGE
 
 extern void copy_page(void * _to, void * _from);
index 9f13c32552bfb1eab23969ab0141fe85e22ac50d..0648e3153f81f2d1f49d8633d5b72d59aa3ba117 100644 (file)
@@ -20,7 +20,8 @@
 #define clear_user_page(page, vaddr, pg)    clear_page(page)
 #define copy_user_page(to, from, vaddr, pg) copy_page(to, from)
 
-#define alloc_zeroed_user_highpage(vma, vaddr) alloc_page_vma(GFP_HIGHUSER | __GFP_ZERO, vma, vaddr)
+#define __alloc_zeroed_user_highpage(movableflags, vma, vaddr) \
+       alloc_page_vma(GFP_HIGHUSER | __GFP_ZERO | movableflags, vma, vaddr)
 #define __HAVE_ARCH_ALLOC_ZEROED_USER_HIGHPAGE
 
 /*
index 3b4f2903f91d92fba260bf7ae84f552c009f9d91..c8cc81a3aca52619e0670634332775b9739e6d5c 100644 (file)
@@ -22,7 +22,8 @@
 #define clear_user_page(page, vaddr, pg)       clear_page(page)
 #define copy_user_page(to, from, vaddr, pg)    copy_page(to, from)
 
-#define alloc_zeroed_user_highpage(vma, vaddr) alloc_page_vma(GFP_HIGHUSER | __GFP_ZERO, vma, vaddr)
+#define __alloc_zeroed_user_highpage(movableflags, vma, vaddr) \
+       alloc_page_vma(GFP_HIGHUSER | __GFP_ZERO | movableflags, vma, vaddr)
 #define __HAVE_ARCH_ALLOC_ZEROED_USER_HIGHPAGE
 
 /*
index 818ac8bf01e2f2e4237d7d61a4c6447dfdf1b520..99cf5d3692a920699c5e50a99d32609363b81218 100644 (file)
@@ -34,7 +34,8 @@
 #define clear_user_page(page, vaddr, pg)       clear_page(page)
 #define copy_user_page(to, from, vaddr, pg)    copy_page(to, from)
 
-#define alloc_zeroed_user_highpage(vma, vaddr) alloc_page_vma(GFP_HIGHUSER | __GFP_ZERO, vma, vaddr)
+#define __alloc_zeroed_user_highpage(movableflags, vma, vaddr) \
+       alloc_page_vma(GFP_HIGHUSER | __GFP_ZERO | movableflags, vma, vaddr)
 #define __HAVE_ARCH_ALLOC_ZEROED_USER_HIGHPAGE
 
 /*
index 485759ba9e36e5ce538721f6f5e63b7bb1af51fd..d6345464a2b3bc9b26ec755561fe0358acdf72ae 100644 (file)
@@ -87,12 +87,13 @@ do {                                                \
 } while (0)
 
 
-#define alloc_zeroed_user_highpage(vma, vaddr) \
-({                                             \
-       struct page *page = alloc_page_vma(GFP_HIGHUSER | __GFP_ZERO, vma, vaddr); \
-       if (page)                               \
-               flush_dcache_page(page);        \
-       page;                                   \
+#define __alloc_zeroed_user_highpage(movableflags, vma, vaddr)         \
+({                                                                     \
+       struct page *page = alloc_page_vma(                             \
+               GFP_HIGHUSER | __GFP_ZERO | movableflags, vma, vaddr);  \
+       if (page)                                                       \
+               flush_dcache_page(page);                                \
+       page;                                                           \
 })
 
 #define __HAVE_ARCH_ALLOC_ZEROED_USER_HIGHPAGE
index 6f6ecf7d14a3550bafd331892704b75a0c51fe8c..04fd183a2c581d5dd43ab96505b72c283e053d51 100644 (file)
@@ -15,7 +15,8 @@ extern void copy_page(void *to, void *from);
 #define clear_user_page(page, vaddr, pg)       clear_page(page)
 #define copy_user_page(to, from, vaddr, pg)    copy_page(to, from)
 
-#define alloc_zeroed_user_highpage(vma, vaddr) alloc_page_vma(GFP_HIGHUSER | __GFP_ZERO, vma, vaddr)
+#define __alloc_zeroed_user_highpage(movableflags, vma, vaddr) \
+       alloc_page_vma(GFP_HIGHUSER | __GFP_ZERO | movableflags, vma, vaddr)
 #define __HAVE_ARCH_ALLOC_ZEROED_USER_HIGHPAGE
 
 /*
index 2a1b8bdcb29c497020c72d7a68e02bf9f993041d..9efa0a9851b1ffb26aad17ce1b2a41658731e4ee 100644 (file)
@@ -22,7 +22,8 @@
 #define clear_user_page(page, vaddr, pg)       clear_page(page)
 #define copy_user_page(to, from, vaddr, pg)    copy_page(to, from)
 
-#define alloc_zeroed_user_highpage(vma, vaddr) alloc_page_vma(GFP_HIGHUSER | __GFP_ZERO, vma, vaddr)
+#define __alloc_zeroed_user_highpage(movableflags, vma, vaddr) \
+       alloc_page_vma(GFP_HIGHUSER | __GFP_ZERO | movableflags, vma, vaddr)
 #define __HAVE_ARCH_ALLOC_ZEROED_USER_HIGHPAGE
 
 /*
index 05ea6f172786524b31a189f5dc29303ddb3edf70..f326451ed6ecbcf04a5ddada0622ddd3801d5a08 100644 (file)
@@ -64,7 +64,8 @@ static inline void copy_page(void *to, void *from)
 #define clear_user_page(page, vaddr, pg)       clear_page(page)
 #define copy_user_page(to, from, vaddr, pg)    copy_page(to, from)
 
-#define alloc_zeroed_user_highpage(vma, vaddr) alloc_page_vma(GFP_HIGHUSER | __GFP_ZERO, vma, vaddr)
+#define __alloc_zeroed_user_highpage(movableflags, vma, vaddr) \
+       alloc_page_vma(GFP_HIGHUSER | __GFP_ZERO | movableflags, vma, vaddr)
 #define __HAVE_ARCH_ALLOC_ZEROED_USER_HIGHPAGE
 
 /*
index e327c830da0c241e26308f09c01bb8ac1ad89ce5..88adf1afb0a28ce66caac316f8d997c99c569a03 100644 (file)
@@ -48,7 +48,8 @@ void copy_page(void *, void *);
 #define clear_user_page(page, vaddr, pg)       clear_page(page)
 #define copy_user_page(to, from, vaddr, pg)    copy_page(to, from)
 
-#define alloc_zeroed_user_highpage(vma, vaddr) alloc_page_vma(GFP_HIGHUSER | __GFP_ZERO, vma, vaddr)
+#define __alloc_zeroed_user_highpage(movableflags, vma, vaddr) \
+       alloc_page_vma(GFP_HIGHUSER | __GFP_ZERO | movableflags, vma, vaddr)
 #define __HAVE_ARCH_ALLOC_ZEROED_USER_HIGHPAGE
 /*
  * These are used to make use of C type-checking..
index 0d2ef0b082a626853b7651914094cabf88672434..e5882fe49f8345e541368f19a9264e84f1ea139d 100644 (file)
@@ -30,6 +30,9 @@ struct vm_area_struct;
  * cannot handle allocation failures.
  *
  * __GFP_NORETRY: The VM implementation must not retry indefinitely.
+ *
+ * __GFP_MOVABLE: Flag that this page will be movable by the page migration
+ * mechanism or reclaimed
  */
 #define __GFP_WAIT     ((__force gfp_t)0x10u)  /* Can wait and reschedule? */
 #define __GFP_HIGH     ((__force gfp_t)0x20u)  /* Should access emergency pools? */
@@ -45,6 +48,7 @@ struct vm_area_struct;
 #define __GFP_NOMEMALLOC ((__force gfp_t)0x10000u) /* Don't use emergency reserves */
 #define __GFP_HARDWALL   ((__force gfp_t)0x20000u) /* Enforce hardwall cpuset memory allocs */
 #define __GFP_THISNODE ((__force gfp_t)0x40000u)/* No fallback, no policies */
+#define __GFP_MOVABLE  ((__force gfp_t)0x80000u) /* Page is movable */
 
 #define __GFP_BITS_SHIFT 20    /* Room for 20 __GFP_FOO bits */
 #define __GFP_BITS_MASK ((__force gfp_t)((1 << __GFP_BITS_SHIFT) - 1))
@@ -53,7 +57,8 @@ struct vm_area_struct;
 #define GFP_LEVEL_MASK (__GFP_WAIT|__GFP_HIGH|__GFP_IO|__GFP_FS| \
                        __GFP_COLD|__GFP_NOWARN|__GFP_REPEAT| \
                        __GFP_NOFAIL|__GFP_NORETRY|__GFP_COMP| \
-                       __GFP_NOMEMALLOC|__GFP_HARDWALL|__GFP_THISNODE)
+                       __GFP_NOMEMALLOC|__GFP_HARDWALL|__GFP_THISNODE| \
+                       __GFP_MOVABLE)
 
 /* This equals 0, but use constants in case they ever change */
 #define GFP_NOWAIT     (GFP_ATOMIC & ~__GFP_HIGH)
@@ -65,6 +70,15 @@ struct vm_area_struct;
 #define GFP_USER       (__GFP_WAIT | __GFP_IO | __GFP_FS | __GFP_HARDWALL)
 #define GFP_HIGHUSER   (__GFP_WAIT | __GFP_IO | __GFP_FS | __GFP_HARDWALL | \
                         __GFP_HIGHMEM)
+#define GFP_HIGHUSER_MOVABLE   (__GFP_WAIT | __GFP_IO | __GFP_FS | \
+                                __GFP_HARDWALL | __GFP_HIGHMEM | \
+                                __GFP_MOVABLE)
+#define GFP_NOFS_PAGECACHE     (__GFP_WAIT | __GFP_IO | __GFP_MOVABLE)
+#define GFP_USER_PAGECACHE     (__GFP_WAIT | __GFP_IO | __GFP_FS | \
+                                __GFP_HARDWALL | __GFP_MOVABLE)
+#define GFP_HIGHUSER_PAGECACHE (__GFP_WAIT | __GFP_IO | __GFP_FS | \
+                                __GFP_HARDWALL | __GFP_HIGHMEM | \
+                                __GFP_MOVABLE)
 
 #ifdef CONFIG_NUMA
 #define GFP_THISNODE   (__GFP_THISNODE | __GFP_NOWARN | __GFP_NORETRY)
index 98e2cce996a4c1867543ffa03d83c66c3c8846de..12c5e4e3135a945ea8a10f0c06d6a4fcd9015fc6 100644 (file)
@@ -73,10 +73,27 @@ static inline void clear_user_highpage(struct page *page, unsigned long vaddr)
 }
 
 #ifndef __HAVE_ARCH_ALLOC_ZEROED_USER_HIGHPAGE
+/**
+ * __alloc_zeroed_user_highpage - Allocate a zeroed HIGHMEM page for a VMA with caller-specified movable GFP flags
+ * @movableflags: The GFP flags related to the pages future ability to move like __GFP_MOVABLE
+ * @vma: The VMA the page is to be allocated for
+ * @vaddr: The virtual address the page will be inserted into
+ *
+ * This function will allocate a page for a VMA but the caller is expected
+ * to specify via movableflags whether the page will be movable in the
+ * future or not
+ *
+ * An architecture may override this function by defining
+ * __HAVE_ARCH_ALLOC_ZEROED_USER_HIGHPAGE and providing their own
+ * implementation.
+ */
 static inline struct page *
-alloc_zeroed_user_highpage(struct vm_area_struct *vma, unsigned long vaddr)
+__alloc_zeroed_user_highpage(gfp_t movableflags,
+                       struct vm_area_struct *vma,
+                       unsigned long vaddr)
 {
-       struct page *page = alloc_page_vma(GFP_HIGHUSER, vma, vaddr);
+       struct page *page = alloc_page_vma(GFP_HIGHUSER | movableflags,
+                       vma, vaddr);
 
        if (page)
                clear_user_highpage(page, vaddr);
@@ -85,6 +102,36 @@ alloc_zeroed_user_highpage(struct vm_area_struct *vma, unsigned long vaddr)
 }
 #endif
 
+/**
+ * alloc_zeroed_user_highpage - Allocate a zeroed HIGHMEM page for a VMA
+ * @vma: The VMA the page is to be allocated for
+ * @vaddr: The virtual address the page will be inserted into
+ *
+ * This function will allocate a page for a VMA that the caller knows will
+ * not be able to move in the future using move_pages() or reclaim. If it
+ * is known that the page can move, use alloc_zeroed_user_highpage_movable
+ */
+static inline struct page *
+alloc_zeroed_user_highpage(struct vm_area_struct *vma, unsigned long vaddr)
+{
+       return __alloc_zeroed_user_highpage(0, vma, vaddr);
+}
+
+/**
+ * alloc_zeroed_user_highpage_movable - Allocate a zeroed HIGHMEM page for a VMA that the caller knows can move
+ * @vma: The VMA the page is to be allocated for
+ * @vaddr: The virtual address the page will be inserted into
+ *
+ * This function will allocate a page for a VMA that the caller knows will
+ * be able to migrate in the future using move_pages() or reclaimed
+ */
+static inline struct page *
+alloc_zeroed_user_highpage_movable(struct vm_area_struct *vma,
+                                       unsigned long vaddr)
+{
+       return __alloc_zeroed_user_highpage(__GFP_MOVABLE, vma, vaddr);
+}
+
 static inline void clear_highpage(struct page *page)
 {
        void *kaddr = kmap_atomic(page, KM_USER0);
index b3d73bb1f6803037384db42a44965b4c60f79c8a..9c6ff7fffdc8cf653d1e04e717a6b3a8bb8fe1c9 100644 (file)
@@ -1715,11 +1715,11 @@ gotten:
        if (unlikely(anon_vma_prepare(vma)))
                goto oom;
        if (old_page == ZERO_PAGE(address)) {
-               new_page = alloc_zeroed_user_highpage(vma, address);
+               new_page = alloc_zeroed_user_highpage_movable(vma, address);
                if (!new_page)
                        goto oom;
        } else {
-               new_page = alloc_page_vma(GFP_HIGHUSER, vma, address);
+               new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, address);
                if (!new_page)
                        goto oom;
                cow_user_page(new_page, old_page, address, vma);
@@ -2237,7 +2237,7 @@ static int do_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma,
 
                if (unlikely(anon_vma_prepare(vma)))
                        goto oom;
-               page = alloc_zeroed_user_highpage(vma, address);
+               page = alloc_zeroed_user_highpage_movable(vma, address);
                if (!page)
                        goto oom;
 
@@ -2340,7 +2340,8 @@ retry:
 
                        if (unlikely(anon_vma_prepare(vma)))
                                goto oom;
-                       page = alloc_page_vma(GFP_HIGHUSER, vma, address);
+                       page = alloc_page_vma(GFP_HIGHUSER_MOVABLE,
+                                               vma, address);
                        if (!page)
                                goto oom;
                        copy_user_highpage(page, new_page, address, vma);
index 188f8d9c4aedf4eddb6137c44d55f336936129e4..4c0f99996811702055927f4447f347a2a837621d 100644 (file)
@@ -594,7 +594,7 @@ static void migrate_page_add(struct page *page, struct list_head *pagelist,
 
 static struct page *new_node_page(struct page *page, unsigned long node, int **x)
 {
-       return alloc_pages_node(node, GFP_HIGHUSER, 0);
+       return alloc_pages_node(node, GFP_HIGHUSER_MOVABLE, 0);
 }
 
 /*
@@ -710,7 +710,8 @@ static struct page *new_vma_page(struct page *page, unsigned long private, int *
 {
        struct vm_area_struct *vma = (struct vm_area_struct *)private;
 
-       return alloc_page_vma(GFP_HIGHUSER, vma, page_address_in_vma(page, vma));
+       return alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma,
+                                       page_address_in_vma(page, vma));
 }
 #else
 
index a91ca00abebea3c7b0a8f0317cabfbc02f403d74..34d8ada053e4562a3a7d216a544b4d9195741c16 100644 (file)
@@ -761,7 +761,8 @@ static struct page *new_page_node(struct page *p, unsigned long private,
 
        *result = &pm->status;
 
-       return alloc_pages_node(pm->node, GFP_HIGHUSER | GFP_THISNODE, 0);
+       return alloc_pages_node(pm->node,
+                               GFP_HIGHUSER_MOVABLE | GFP_THISNODE, 0);
 }
 
 /*
index 0493e4d0bcaab2d5b0281446171b4de57598b8ee..e49181d9d893734dade8f924ef599c0c7123e9cf 100644 (file)
@@ -93,8 +93,11 @@ static inline struct page *shmem_dir_alloc(gfp_t gfp_mask)
         * The above definition of ENTRIES_PER_PAGE, and the use of
         * BLOCKS_PER_PAGE on indirect pages, assume PAGE_CACHE_SIZE:
         * might be reconsidered if it ever diverges from PAGE_SIZE.
+        *
+        * __GFP_MOVABLE is masked out as swap vectors cannot move
         */
-       return alloc_pages(gfp_mask, PAGE_CACHE_SHIFT-PAGE_SHIFT);
+       return alloc_pages((gfp_mask & ~__GFP_MOVABLE) | __GFP_ZERO,
+                               PAGE_CACHE_SHIFT-PAGE_SHIFT);
 }
 
 static inline void shmem_dir_free(struct page *page)
@@ -372,7 +375,7 @@ static swp_entry_t *shmem_swp_alloc(struct shmem_inode_info *info, unsigned long
                }
 
                spin_unlock(&info->lock);
-               page = shmem_dir_alloc(mapping_gfp_mask(inode->i_mapping) | __GFP_ZERO);
+               page = shmem_dir_alloc(mapping_gfp_mask(inode->i_mapping));
                if (page)
                        set_page_private(page, 0);
                spin_lock(&info->lock);
index 925d5c50f18d0fd401037c339ff0d72660b79eac..67daecb6031ad62022fbe7aa7fe9ea7b027dd3cf 100644 (file)
@@ -334,7 +334,8 @@ struct page *read_swap_cache_async(swp_entry_t entry,
                 * Get a new page to read into from swap.
                 */
                if (!new_page) {
-                       new_page = alloc_page_vma(GFP_HIGHUSER, vma, addr);
+                       new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE,
+                                                               vma, addr);
                        if (!new_page)
                                break;          /* Out of memory */
                }