* use it's pages as requested migratetype in the future.
  */
 static void steal_suitable_fallback(struct zone *zone, struct page *page,
-                                                         int start_type)
+                                       int start_type, bool whole_block)
 {
        unsigned int current_order = page_order(page);
+       struct free_area *area;
        int pages;
 
+       /*
+        * This can happen due to races and we want to prevent broken
+        * highatomic accounting.
+        */
+       if (is_migrate_highatomic_page(page))
+               goto single_page;
+
        /* Take ownership for orders >= pageblock_order */
        if (current_order >= pageblock_order) {
                change_pageblock_range(page, current_order, start_type);
-               return;
+               goto single_page;
        }
 
+       /* We are not allowed to try stealing from the whole block */
+       if (!whole_block)
+               goto single_page;
+
        pages = move_freepages_block(zone, page, start_type);
+       /* moving whole block can fail due to zone boundary conditions */
+       if (!pages)
+               goto single_page;
 
        /* Claim the whole block if over half of it is free */
        if (pages >= (1 << (pageblock_order-1)) ||
                        page_group_by_mobility_disabled)
                set_pageblock_migratetype(page, start_type);
+
+       return;
+
+single_page:
+       area = &zone->free_area[current_order];
+       list_move(&page->lru, &area->free_list[start_type]);
 }
 
 /*
        return false;
 }
 
-/* Remove an element from the buddy allocator from the fallback list */
-static inline struct page *
+/*
+ * Try finding a free buddy page on the fallback list and put it on the free
+ * list of requested migratetype, possibly along with other pages from the same
+ * block, depending on fragmentation avoidance heuristics. Returns true if
+ * fallback was found so that __rmqueue_smallest() can grab it.
+ */
+static inline bool
 __rmqueue_fallback(struct zone *zone, unsigned int order, int start_migratetype)
 {
        struct free_area *area;
 
                page = list_first_entry(&area->free_list[fallback_mt],
                                                struct page, lru);
-               if (can_steal && !is_migrate_highatomic_page(page))
-                       steal_suitable_fallback(zone, page, start_migratetype);
 
-               /* Remove the page from the freelists */
-               area->nr_free--;
-               list_del(&page->lru);
-               rmv_page_order(page);
-
-               expand(zone, page, order, current_order, area,
-                                       start_migratetype);
-               /*
-                * The pcppage_migratetype may differ from pageblock's
-                * migratetype depending on the decisions in
-                * find_suitable_fallback(). This is OK as long as it does not
-                * differ for MIGRATE_CMA pageblocks. Those can be used as
-                * fallback only via special __rmqueue_cma_fallback() function
-                */
-               set_pcppage_migratetype(page, start_migratetype);
+               steal_suitable_fallback(zone, page, start_migratetype,
+                                                               can_steal);
 
                trace_mm_page_alloc_extfrag(page, order, current_order,
                        start_migratetype, fallback_mt);
 
-               return page;
+               return true;
        }
 
-       return NULL;
+       return false;
 }
 
 /*
 {
        struct page *page;
 
+retry:
        page = __rmqueue_smallest(zone, order, migratetype);
        if (unlikely(!page)) {
                if (migratetype == MIGRATE_MOVABLE)
                        page = __rmqueue_cma_fallback(zone, order);
 
-               if (!page)
-                       page = __rmqueue_fallback(zone, order, migratetype);
+               if (!page && __rmqueue_fallback(zone, order, migratetype))
+                       goto retry;
        }
 
        trace_mm_page_alloc_zone_locked(page, order, migratetype);