#include <linux/mempolicy.h>
 #include <linux/migrate_mode.h>
 
-typedef struct page *new_page_t(struct page *, unsigned long private, int **);
+typedef struct page *new_page_t(struct page *page, unsigned long private,
+                               int **reason);
+typedef void free_page_t(struct page *page, unsigned long private);
 
 /*
  * Return values from addresss_space_operations.migratepage():
 extern void putback_movable_pages(struct list_head *l);
 extern int migrate_page(struct address_space *,
                        struct page *, struct page *, enum migrate_mode);
-extern int migrate_pages(struct list_head *l, new_page_t x,
+extern int migrate_pages(struct list_head *l, new_page_t new, free_page_t free,
                unsigned long private, enum migrate_mode mode, int reason);
 
 extern int migrate_prep(void);
 #else
 
 static inline void putback_movable_pages(struct list_head *l) {}
-static inline int migrate_pages(struct list_head *l, new_page_t x,
-               unsigned long private, enum migrate_mode mode, int reason)
+static inline int migrate_pages(struct list_head *l, new_page_t new,
+               free_page_t free, unsigned long private, enum migrate_mode mode,
+               int reason)
        { return -ENOSYS; }
 
 static inline int migrate_prep(void) { return -ENOSYS; }
 
 
        /* Keep page count to indicate a given hugepage is isolated. */
        list_move(&hpage->lru, &pagelist);
-       ret = migrate_pages(&pagelist, new_page, MPOL_MF_MOVE_ALL,
+       ret = migrate_pages(&pagelist, new_page, NULL, MPOL_MF_MOVE_ALL,
                                MIGRATE_SYNC, MR_MEMORY_FAILURE);
        if (ret) {
                pr_info("soft offline: %#lx: migration failed %d, type %lx\n",
                inc_zone_page_state(page, NR_ISOLATED_ANON +
                                        page_is_file_cache(page));
                list_add(&page->lru, &pagelist);
-               ret = migrate_pages(&pagelist, new_page, MPOL_MF_MOVE_ALL,
+               ret = migrate_pages(&pagelist, new_page, NULL, MPOL_MF_MOVE_ALL,
                                        MIGRATE_SYNC, MR_MEMORY_FAILURE);
                if (ret) {
                        if (!list_empty(&pagelist)) {
 
                        flags | MPOL_MF_DISCONTIG_OK, &pagelist);
 
        if (!list_empty(&pagelist)) {
-               err = migrate_pages(&pagelist, new_node_page, dest,
+               err = migrate_pages(&pagelist, new_node_page, NULL, dest,
                                        MIGRATE_SYNC, MR_SYSCALL);
                if (err)
                        putback_movable_pages(&pagelist);
                if (!list_empty(&pagelist)) {
                        WARN_ON_ONCE(flags & MPOL_MF_LAZY);
                        nr_failed = migrate_pages(&pagelist, new_vma_page,
-                                       (unsigned long)vma,
+                                       NULL, (unsigned long)vma,
                                        MIGRATE_SYNC, MR_MEMPOLICY_MBIND);
                        if (nr_failed)
                                putback_movable_pages(&pagelist);
 
  * Obtain the lock on page, remove all ptes and migrate the page
  * to the newly allocated page in newpage.
  */
-static int unmap_and_move(new_page_t get_new_page, unsigned long private,
-                       struct page *page, int force, enum migrate_mode mode)
+static int unmap_and_move(new_page_t get_new_page, free_page_t put_new_page,
+                       unsigned long private, struct page *page, int force,
+                       enum migrate_mode mode)
 {
        int rc = 0;
        int *result = NULL;
                                page_is_file_cache(page));
                putback_lru_page(page);
        }
+
        /*
-        * Move the new page to the LRU. If migration was not successful
-        * then this will free the page.
+        * If migration was not successful and there's a freeing callback, use
+        * it.  Otherwise, putback_lru_page() will drop the reference grabbed
+        * during isolation.
         */
-       putback_lru_page(newpage);
+       if (rc != MIGRATEPAGE_SUCCESS && put_new_page)
+               put_new_page(newpage, private);
+       else
+               putback_lru_page(newpage);
+
        if (result) {
                if (rc)
                        *result = rc;
  * will wait in the page fault for migration to complete.
  */
 static int unmap_and_move_huge_page(new_page_t get_new_page,
-                               unsigned long private, struct page *hpage,
-                               int force, enum migrate_mode mode)
+                               free_page_t put_new_page, unsigned long private,
+                               struct page *hpage, int force,
+                               enum migrate_mode mode)
 {
        int rc = 0;
        int *result = NULL;
        if (!page_mapped(hpage))
                rc = move_to_new_page(new_hpage, hpage, 1, mode);
 
-       if (rc)
+       if (rc != MIGRATEPAGE_SUCCESS)
                remove_migration_ptes(hpage, hpage);
 
        if (anon_vma)
                put_anon_vma(anon_vma);
 
-       if (!rc)
+       if (rc == MIGRATEPAGE_SUCCESS)
                hugetlb_cgroup_migrate(hpage, new_hpage);
 
        unlock_page(hpage);
 out:
        if (rc != -EAGAIN)
                putback_active_hugepage(hpage);
-       put_page(new_hpage);
+
+       /*
+        * If migration was not successful and there's a freeing callback, use
+        * it.  Otherwise, put_page() will drop the reference grabbed during
+        * isolation.
+        */
+       if (rc != MIGRATEPAGE_SUCCESS && put_new_page)
+               put_new_page(new_hpage, private);
+       else
+               put_page(new_hpage);
+
        if (result) {
                if (rc)
                        *result = rc;
  * @from:              The list of pages to be migrated.
  * @get_new_page:      The function used to allocate free pages to be used
  *                     as the target of the page migration.
+ * @put_new_page:      The function used to free target pages if migration
+ *                     fails, or NULL if no special handling is necessary.
  * @private:           Private data to be passed on to get_new_page()
  * @mode:              The migration mode that specifies the constraints for
  *                     page migration, if any.
  * Returns the number of pages that were not migrated, or an error code.
  */
 int migrate_pages(struct list_head *from, new_page_t get_new_page,
-               unsigned long private, enum migrate_mode mode, int reason)
+               free_page_t put_new_page, unsigned long private,
+               enum migrate_mode mode, int reason)
 {
        int retry = 1;
        int nr_failed = 0;
 
                        if (PageHuge(page))
                                rc = unmap_and_move_huge_page(get_new_page,
-                                               private, page, pass > 2, mode);
+                                               put_new_page, private, page,
+                                               pass > 2, mode);
                        else
-                               rc = unmap_and_move(get_new_page, private,
-                                               page, pass > 2, mode);
+                               rc = unmap_and_move(get_new_page, put_new_page,
+                                               private, page, pass > 2, mode);
 
                        switch(rc) {
                        case -ENOMEM:
 
        err = 0;
        if (!list_empty(&pagelist)) {
-               err = migrate_pages(&pagelist, new_page_node,
+               err = migrate_pages(&pagelist, new_page_node, NULL,
                                (unsigned long)pm, MIGRATE_SYNC, MR_SYSCALL);
                if (err)
                        putback_movable_pages(&pagelist);
 
        list_add(&page->lru, &migratepages);
        nr_remaining = migrate_pages(&migratepages, alloc_misplaced_dst_page,
-                                    node, MIGRATE_ASYNC, MR_NUMA_MISPLACED);
+                                    NULL, node, MIGRATE_ASYNC,
+                                    MR_NUMA_MISPLACED);
        if (nr_remaining) {
                if (!list_empty(&migratepages)) {
                        list_del(&page->lru);