diff mbox series

[v1,4/6] mm: Introduce "aerated" pages

Message ID 20190619223323.1231.86906.stgit@localhost.localdomain (mailing list archive)
State New, archived
Headers show
Series mm / virtio: Provide support for paravirtual waste page treatment | expand

Commit Message

Alexander H Duyck June 19, 2019, 10:33 p.m. UTC
From: Alexander Duyck <alexander.h.duyck@linux.intel.com>

In order to pave the way for free page hinting in virtualized environments
we will need a way to get pages out of the free lists and identify those
pages after they have been returned. To accomplish this patch adds the
concept of an "aerated" flag, which is essentially meant to just be the
Offline page type used in conjustion with the Buddy page type bit.

For now we can just add the basic logic to set the flag and track the
number of aerated pages per free area.

Signed-off-by: Alexander Duyck <alexander.h.duyck@linux.intel.com>
---
 include/linux/memory_aeration.h |   61 +++++++++++++++++++++++++
 include/linux/mmzone.h          |   13 ++++-
 mm/Kconfig                      |    5 ++
 mm/page_alloc.c                 |   97 +++++++++++++++++++++++++++++++++++++--
 4 files changed, 168 insertions(+), 8 deletions(-)
 create mode 100644 include/linux/memory_aeration.h

Comments

Dave Hansen June 25, 2019, 7:45 p.m. UTC | #1
> +static inline void set_page_aerated(struct page *page,
> +				    struct zone *zone,
> +				    unsigned int order,
> +				    int migratetype)
> +{
> +#ifdef CONFIG_AERATION
> +	/* update areated page accounting */
> +	zone->free_area[order].nr_free_aerated++;
> +
> +	/* record migratetype and flag page as aerated */
> +	set_pcppage_migratetype(page, migratetype);
> +	__SetPageAerated(page);
> +#endif
> +}

Please don't refer to code before you introduce it, even if you #ifdef
it.  I went looking back in the series for the PageAerated() definition,
but didn't think to look forward.

Also, it doesn't make any sense to me that you would need to set the
migratetype here.  Isn't it set earlier in the allocator?  Also, when
can this function be called?  There's obviously some locking in place
because of the __Set, but what are they?

> +static inline void clear_page_aerated(struct page *page,
> +				      struct zone *zone,
> +				      struct free_area *area)
> +{
> +#ifdef CONFIG_AERATION
> +	if (likely(!PageAerated(page)))
> +		return;

Logically, why would you ever clear_page_aerated() on a page that's not
aerated?  Comments needed.

BTW, I already hate typing aerated. :)

> +	__ClearPageAerated(page);
> +	area->nr_free_aerated--;
> +#endif
> +}

More non-atomic flag clears.  Still no comments.


> @@ -787,10 +790,10 @@ static inline void add_to_free_area(struct page *page, struct zone *zone,
>  static inline void add_to_free_area_tail(struct page *page, struct zone *zone,
>  					 unsigned int order, int migratetype)
>  {
> -	struct free_area *area = &zone->free_area[order];
> +	struct list_head *tail = aerator_get_tail(zone, order, migratetype);

There is no logical change in this patch from this line.  That's
unfortunate because I can't see the change in logic that's presumably
coming.  You'll presumably change aerator_get_tail(), but then I'll have
to remember that this line is here and come back to it from a later patch.

If it *doesn't* change behavior, it has no business being calle
aerator_...().

This series seems rather suboptimal for reviewing.

> -	list_add_tail(&page->lru, &area->free_list[migratetype]);
> -	area->nr_free++;
> +	list_add_tail(&page->lru, tail);
> +	zone->free_area[order].nr_free++;
>  }
>  
>  /* Used for pages which are on another list */
> @@ -799,6 +802,8 @@ static inline void move_to_free_area(struct page *page, struct zone *zone,
>  {
>  	struct free_area *area = &zone->free_area[order];
>  
> +	clear_page_aerated(page, zone, area);
> +
>  	list_move(&page->lru, &area->free_list[migratetype]);
>  }

It's not immediately clear to me why moving a page should clear
aeration.  A comment would help make it clear.

> @@ -868,10 +869,11 @@ static inline struct capture_control *task_capc(struct zone *zone)
>  static inline void __free_one_page(struct page *page,
>  		unsigned long pfn,
>  		struct zone *zone, unsigned int order,
> -		int migratetype)
> +		int migratetype, bool aerated)
>  {
>  	struct capture_control *capc = task_capc(zone);
>  	unsigned long uninitialized_var(buddy_pfn);
> +	bool fully_aerated = aerated;
>  	unsigned long combined_pfn;
>  	unsigned int max_order;
>  	struct page *buddy;
> @@ -902,6 +904,11 @@ static inline void __free_one_page(struct page *page,
>  			goto done_merging;
>  		if (!page_is_buddy(page, buddy, order))
>  			goto done_merging;
> +
> +		/* assume buddy is not aerated */
> +		if (aerated)
> +			fully_aerated = false;

So, "full" vs. "partial" is with respect to high-order pages?  Why not
just check the page flag on the buddy?

>  		/*
>  		 * Our buddy is free or it is CONFIG_DEBUG_PAGEALLOC guard page,
>  		 * merge with it and move up one order.
> @@ -943,11 +950,17 @@ static inline void __free_one_page(struct page *page,
>  done_merging:
>  	set_page_order(page, order);
>  
> -	if (buddy_merge_likely(pfn, buddy_pfn, page, order) ||
> +	if (aerated ||
> +	    buddy_merge_likely(pfn, buddy_pfn, page, order) ||
>  	    is_shuffle_tail_page(order))
>  		add_to_free_area_tail(page, zone, order, migratetype);
>  	else
>  		add_to_free_area(page, zone, order, migratetype);

Aerated pages always go to the tail?  Ahh, so they don't get consumed
quickly and have to be undone?  Comments, please.

> +	if (fully_aerated)
> +		set_page_aerated(page, zone, order, migratetype);
> +	else
> +		aerator_notify_free(zone, order);
>  }

What is this notifying for?  It's not like this is some opaque
registration interface.  What does this *do*?

> @@ -2127,6 +2140,77 @@ struct page *__rmqueue_smallest(struct zone *zone, unsigned int order,
>  	return NULL;
>  }
>  
> +#ifdef CONFIG_AERATION
> +/**
> + * get_aeration_page - Provide a "raw" page for aeration by the aerator
> + * @zone: Zone to draw pages from
> + * @order: Order to draw pages from
> + * @migratetype: Migratetype to draw pages from

FWIW, kerneldoc is a waste of bytes here.  Please use it sparingly.

> + * This function will obtain a page from above the boundary. As a result
> + * we can guarantee the page has not been aerated.

This is the first mention of a boundary.  That's not good since I have
no idea at this point what the boundary is for or between.


> + * The page will have the migrate type and order stored in the page
> + * metadata.
> + *
> + * Return: page pointer if raw page found, otherwise NULL
> + */
> +struct page *get_aeration_page(struct zone *zone, unsigned int order,
> +			       int migratetype)
> +{
> +	struct free_area *area = &(zone->free_area[order]);
> +	struct list_head *list = &area->free_list[migratetype];
> +	struct page *page;
> +
> +	/* Find a page of the appropriate size in the preferred list */

I don't get the size comment.  Hasn't this already been given an order?

> +	page = list_last_entry(aerator_get_tail(zone, order, migratetype),
> +			       struct page, lru);
> +	list_for_each_entry_from_reverse(page, list, lru) {
> +		if (PageAerated(page)) {
> +			page = list_first_entry(list, struct page, lru);
> +			if (PageAerated(page))
> +				break;
> +		}

This confuses me.  It looks for a page, then goes to the next page and
checks again?  Why check twice?  Why is a function looking for an
aerated page that finds *two* pages returning NULL?

I'm stumped.

> +		del_page_from_free_area(page, zone, order);
> +
> +		/* record migratetype and order within page */
> +		set_pcppage_migratetype(page, migratetype);
> +		set_page_private(page, order);
> +		__mod_zone_freepage_state(zone, -(1 << order), migratetype);
> +
> +		return page;
> +	}
> +
> +	return NULL;
> +}

Oh, so this is trying to find a page _for_ aerating.
"get_aeration_page()" does not convey that.  Can that improved?
get_page_for_aeration()?

Rather than talk about boundaries, wouldn't a better description have been:

	Similar to allocation, this function removes a page from the
	free lists.  However, it only removes unaerated pages.

> +/**
> + * put_aeration_page - Return a now-aerated "raw" page back where we got it
> + * @zone: Zone to return pages to
> + * @page: Previously "raw" page that can now be returned after aeration
> + *
> + * This function will pull the migratetype and order information out
> + * of the page and attempt to return it where it found it.
> + */
> +void put_aeration_page(struct zone *zone, struct page *page)
> +{
> +	unsigned int order, mt;
> +	unsigned long pfn;
> +
> +	mt = get_pcppage_migratetype(page);
> +	pfn = page_to_pfn(page);
> +
> +	if (unlikely(has_isolate_pageblock(zone) || is_migrate_isolate(mt)))
> +		mt = get_pfnblock_migratetype(page, pfn);
> +
> +	order = page_private(page);
> +	set_page_private(page, 0);
> +
> +	__free_one_page(page, pfn, zone, order, mt, true);
> +}
> +#endif /* CONFIG_AERATION */

Yikes.  This seems to have glossed over some pretty big aspects here.
Pages which are being aerated are not free.  Pages which are freed are
diverted to be aerated before becoming free.  Right?  That sounds like
two really important things to add to a changelog.

>  /*
>   * This array describes the order lists are fallen back to when
>   * the free lists for the desirable migrate type are depleted
> @@ -5929,9 +6013,12 @@ void __ref memmap_init_zone_device(struct zone *zone,
>  static void __meminit zone_init_free_lists(struct zone *zone)
>  {
>  	unsigned int order, t;
> -	for_each_migratetype_order(order, t) {
> +	for_each_migratetype_order(order, t)
>  		INIT_LIST_HEAD(&zone->free_area[order].free_list[t]);
> +
> +	for (order = MAX_ORDER; order--; ) {
>  		zone->free_area[order].nr_free = 0;
> +		zone->free_area[order].nr_free_aerated = 0;
>  	}
>  }
>  
>
Alexander Duyck July 8, 2019, 5:32 p.m. UTC | #2
On Tue, 2019-06-25 at 12:45 -0700, Dave Hansen wrote:
> > +static inline void set_page_aerated(struct page *page,
> > +				    struct zone *zone,
> > +				    unsigned int order,
> > +				    int migratetype)
> > +{
> > +#ifdef CONFIG_AERATION
> > +	/* update areated page accounting */
> > +	zone->free_area[order].nr_free_aerated++;
> > +
> > +	/* record migratetype and flag page as aerated */
> > +	set_pcppage_migratetype(page, migratetype);
> > +	__SetPageAerated(page);
> > +#endif
> > +}
> 
> Please don't refer to code before you introduce it, even if you #ifdef
> it.  I went looking back in the series for the PageAerated() definition,
> but didn't think to look forward.

Yeah, I had split this code out from patch 5, but I realized after I
submitted it I had a number of issues. The kconfig option also ended up in
patch 5 instead of showing up in patch 4.

I'll have to work on cleaning up patches 4 and 5 so that the split between
them is cleaner.

> Also, it doesn't make any sense to me that you would need to set the
> migratetype here.  Isn't it set earlier in the allocator?  Also, when
> can this function be called?  There's obviously some locking in place
> because of the __Set, but what are they?

Generally this function is only called from inside __free_one_page so yes,
the zone lock is expected to be held.

> > +static inline void clear_page_aerated(struct page *page,
> > +				      struct zone *zone,
> > +				      struct free_area *area)
> > +{
> > +#ifdef CONFIG_AERATION
> > +	if (likely(!PageAerated(page)))
> > +		return;
> 
> Logically, why would you ever clear_page_aerated() on a page that's not
> aerated?  Comments needed.
> 
> BTW, I already hate typing aerated. :)

Well I am always open to other suggestions. I could just default to
offline which is what is used by the balloon drivers. Suggestions for a
better name are always welcome.

> > +	__ClearPageAerated(page);
> > +	area->nr_free_aerated--;
> > +#endif
> > +}
> 
> More non-atomic flag clears.  Still no comments.

Yes. it is the same kind of deal as the function above. Basically we only
call this just before we clear the buddy flag in the allocator so once
again the zone lock is expected to be held at this point.

> 
> > @@ -787,10 +790,10 @@ static inline void add_to_free_area(struct page *page, struct zone *zone,
> >  static inline void add_to_free_area_tail(struct page *page, struct zone *zone,
> >  					 unsigned int order, int migratetype)
> >  {
> > -	struct free_area *area = &zone->free_area[order];
> > +	struct list_head *tail = aerator_get_tail(zone, order, migratetype);
> 
> There is no logical change in this patch from this line.  That's
> unfortunate because I can't see the change in logic that's presumably
> coming.  You'll presumably change aerator_get_tail(), but then I'll have
> to remember that this line is here and come back to it from a later patch.
> 
> If it *doesn't* change behavior, it has no business being calle
> aerator_...().
> 
> This series seems rather suboptimal for reviewing.

I can move that into patch 5. That would make more sense anyway since that
is where I introduce the change that adds the boundaries.

> > -	list_add_tail(&page->lru, &area->free_list[migratetype]);
> > -	area->nr_free++;
> > +	list_add_tail(&page->lru, tail);
> > +	zone->free_area[order].nr_free++;
> >  }
> >  
> >  /* Used for pages which are on another list */
> > @@ -799,6 +802,8 @@ static inline void move_to_free_area(struct page *page, struct zone *zone,
> >  {
> >  	struct free_area *area = &zone->free_area[order];
> >  
> > +	clear_page_aerated(page, zone, area);
> > +
> >  	list_move(&page->lru, &area->free_list[migratetype]);
> >  }
> 
> It's not immediately clear to me why moving a page should clear
> aeration.  A comment would help make it clear.

I will do that. The main reason for having to do that is because when we
move the page there is no guarantee that the boundaries will still be in
place. As such we are pulling the page and placing it on the head of the
free_list we are moving it to. As such in order to avoid creating an
island of unprocessed pages we need to clear the flag and just reprocess
it later.

> > @@ -868,10 +869,11 @@ static inline struct capture_control *task_capc(struct zone *zone)
> >  static inline void __free_one_page(struct page *page,
> >  		unsigned long pfn,
> >  		struct zone *zone, unsigned int order,
> > -		int migratetype)
> > +		int migratetype, bool aerated)
> >  {
> >  	struct capture_control *capc = task_capc(zone);
> >  	unsigned long uninitialized_var(buddy_pfn);
> > +	bool fully_aerated = aerated;
> >  	unsigned long combined_pfn;
> >  	unsigned int max_order;
> >  	struct page *buddy;
> > @@ -902,6 +904,11 @@ static inline void __free_one_page(struct page *page,
> >  			goto done_merging;
> >  		if (!page_is_buddy(page, buddy, order))
> >  			goto done_merging;
> > +
> > +		/* assume buddy is not aerated */
> > +		if (aerated)
> > +			fully_aerated = false;
> 
> So, "full" vs. "partial" is with respect to high-order pages?  Why not
> just check the page flag on the buddy?

The buddy will never have the aerated flag set. If we are hinting on a
given page then the assumption is it was the highest order version of the
page available when we processed the pages in the previous pass. So if the
buddy is available when we are returning the processed page then the buddy
is non-aerated and will invalidate the aeration when merged with the
aerated page. What we will then do is hint on the higher-order page that
is created as a result of merging the two pages.

I'll make the comment more robust on that.

> >  		/*
> >  		 * Our buddy is free or it is CONFIG_DEBUG_PAGEALLOC guard page,
> >  		 * merge with it and move up one order.
> > @@ -943,11 +950,17 @@ static inline void __free_one_page(struct page *page,
> >  done_merging:
> >  	set_page_order(page, order);
> >  
> > -	if (buddy_merge_likely(pfn, buddy_pfn, page, order) ||
> > +	if (aerated ||
> > +	    buddy_merge_likely(pfn, buddy_pfn, page, order) ||
> >  	    is_shuffle_tail_page(order))
> >  		add_to_free_area_tail(page, zone, order, migratetype);
> >  	else
> >  		add_to_free_area(page, zone, order, migratetype);
> 
> Aerated pages always go to the tail?  Ahh, so they don't get consumed
> quickly and have to be undone?  Comments, please.

Will do.

> > +	if (fully_aerated)
> > +		set_page_aerated(page, zone, order, migratetype);
> > +	else
> > +		aerator_notify_free(zone, order);
> >  }
> 
> What is this notifying for?  It's not like this is some opaque
> registration interface.  What does this *do*?

This is updating the count of non-treated pages and comparing that to the
high water mark. If it crosses a certain threshold it will then set the
bits requesting the zone be processed and wake up the thread if it is not
active.

> > @@ -2127,6 +2140,77 @@ struct page *__rmqueue_smallest(struct zone *zone, unsigned int order,
> >  	return NULL;
> >  }
> >  
> > +#ifdef CONFIG_AERATION
> > +/**
> > + * get_aeration_page - Provide a "raw" page for aeration by the aerator
> > + * @zone: Zone to draw pages from
> > + * @order: Order to draw pages from
> > + * @migratetype: Migratetype to draw pages from
> 
> FWIW, kerneldoc is a waste of bytes here.  Please use it sparingly.
> 
> > + * This function will obtain a page from above the boundary. As a result
> > + * we can guarantee the page has not been aerated.
> 
> This is the first mention of a boundary.  That's not good since I have
> no idea at this point what the boundary is for or between.

Boundary doesn't get added until the next patch so this is another foul up
with the splitting of the patches.

> 
> > + * The page will have the migrate type and order stored in the page
> > + * metadata.
> > + *
> > + * Return: page pointer if raw page found, otherwise NULL
> > + */
> > +struct page *get_aeration_page(struct zone *zone, unsigned int order,
> > +			       int migratetype)
> > +{
> > +	struct free_area *area = &(zone->free_area[order]);
> > +	struct list_head *list = &area->free_list[migratetype];
> > +	struct page *page;
> > +
> > +	/* Find a page of the appropriate size in the preferred list */
> 
> I don't get the size comment.  Hasn't this already been given an order?

That is a hold over from a previous version of this patch. Originally this
code was getting anything order or greater. Now it only grabs pages of a
certain order as I had pulled some logic out of here and moved it into the
aeration logic.

> > +	page = list_last_entry(aerator_get_tail(zone, order, migratetype),
> > +			       struct page, lru);
> > +	list_for_each_entry_from_reverse(page, list, lru) {
> > +		if (PageAerated(page)) {
> > +			page = list_first_entry(list, struct page, lru);
> > +			if (PageAerated(page))
> > +				break;
> > +		}
> 
> This confuses me.  It looks for a page, then goes to the next page and
> checks again?  Why check twice?  Why is a function looking for an
> aerated page that finds *two* pages returning NULL?
> 
> I'm stumped.

So the logic here gets confusing because the boundary hasn't been defined
yet. Specifically boundary ends up being a secondary tail that applies
only to the non-processed pages. What we are doing is getting the last
non-processed page, and then using the "from" version of the list iterator
to make certain that the list isn't actually empty.

From there we do a check to see if the page we are currently looking at is
empty. If it is we return NULL. If the list is not empty we check and see
if the page was processed, if it was we try grapping the page from the
head of the list as we hit the bottom of the last batch we processed. If
that is also processed then we just exit.

I will try to do a better job of documenting all this. Basically I used a
bunch of list manipulation that is hiding things too well.

> > +		del_page_from_free_area(page, zone, order);
> > +
> > +		/* record migratetype and order within page */
> > +		set_pcppage_migratetype(page, migratetype);
> > +		set_page_private(page, order);
> > +		__mod_zone_freepage_state(zone, -(1 << order), migratetype);
> > +
> > +		return page;
> > +	}
> > +
> > +	return NULL;
> > +}
> 
> Oh, so this is trying to find a page _for_ aerating.
> "get_aeration_page()" does not convey that.  Can that improved?
> get_page_for_aeration()?
> 
> Rather than talk about boundaries, wouldn't a better description have been:
> 
> 	Similar to allocation, this function removes a page from the
> 	free lists.  However, it only removes unaerated pages.

I will update the kerneldoc and comments.

> > +/**
> > + * put_aeration_page - Return a now-aerated "raw" page back where we got it
> > + * @zone: Zone to return pages to
> > + * @page: Previously "raw" page that can now be returned after aeration
> > + *
> > + * This function will pull the migratetype and order information out
> > + * of the page and attempt to return it where it found it.
> > + */
> > +void put_aeration_page(struct zone *zone, struct page *page)
> > +{
> > +	unsigned int order, mt;
> > +	unsigned long pfn;
> > +
> > +	mt = get_pcppage_migratetype(page);
> > +	pfn = page_to_pfn(page);
> > +
> > +	if (unlikely(has_isolate_pageblock(zone) || is_migrate_isolate(mt)))
> > +		mt = get_pfnblock_migratetype(page, pfn);
> > +
> > +	order = page_private(page);
> > +	set_page_private(page, 0);
> > +
> > +	__free_one_page(page, pfn, zone, order, mt, true);
> > +}
> > +#endif /* CONFIG_AERATION */
> 
> Yikes.  This seems to have glossed over some pretty big aspects here.
> Pages which are being aerated are not free.  Pages which are freed are
> diverted to be aerated before becoming free.  Right?  That sounds like
> two really important things to add to a changelog.

Right. The aerated pages are not free. The go into the free_list and once
enough pages are in there the aerator will start pulling some out and
processing them. The idea is that any pages not being actively aerated
will stay in the free_list so it isn't as if we are shunting them off to a
side-queue. They are just sitting in the free_list either to be reused or
aerated.

I'll make some updates to the changelog to clarify that.

> >  /*
> >   * This array describes the order lists are fallen back to when
> >   * the free lists for the desirable migrate type are depleted
> > @@ -5929,9 +6013,12 @@ void __ref memmap_init_zone_device(struct zone *zone,
> >  static void __meminit zone_init_free_lists(struct zone *zone)
> >  {
> >  	unsigned int order, t;
> > -	for_each_migratetype_order(order, t) {
> > +	for_each_migratetype_order(order, t)
> >  		INIT_LIST_HEAD(&zone->free_area[order].free_list[t]);
> > +
> > +	for (order = MAX_ORDER; order--; ) {
> >  		zone->free_area[order].nr_free = 0;
> > +		zone->free_area[order].nr_free_aerated = 0;
> >  	}
> >  }
> >  
> >
diff mbox series

Patch

diff --git a/include/linux/memory_aeration.h b/include/linux/memory_aeration.h
new file mode 100644
index 000000000000..44cfbc259778
--- /dev/null
+++ b/include/linux/memory_aeration.h
@@ -0,0 +1,61 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_MEMORY_AERATION_H
+#define _LINUX_MEMORY_AERATION_H
+
+#include <linux/mmzone.h>
+#include <linux/pageblock-flags.h>
+
+struct page *get_aeration_page(struct zone *zone, unsigned int order,
+			       int migratetype);
+void put_aeration_page(struct zone *zone, struct page *page);
+
+static inline struct list_head *aerator_get_tail(struct zone *zone,
+						 unsigned int order,
+						 int migratetype)
+{
+	return &zone->free_area[order].free_list[migratetype];
+}
+
+static inline void set_page_aerated(struct page *page,
+				    struct zone *zone,
+				    unsigned int order,
+				    int migratetype)
+{
+#ifdef CONFIG_AERATION
+	/* update areated page accounting */
+	zone->free_area[order].nr_free_aerated++;
+
+	/* record migratetype and flag page as aerated */
+	set_pcppage_migratetype(page, migratetype);
+	__SetPageAerated(page);
+#endif
+}
+
+static inline void clear_page_aerated(struct page *page,
+				      struct zone *zone,
+				      struct free_area *area)
+{
+#ifdef CONFIG_AERATION
+	if (likely(!PageAerated(page)))
+		return;
+
+	__ClearPageAerated(page);
+	area->nr_free_aerated--;
+#endif
+}
+
+/**
+ * aerator_notify_free - Free page notification that will start page processing
+ * @zone: Pointer to current zone of last page processed
+ * @order: Order of last page added to zone
+ *
+ * This function is meant to act as a screener for __aerator_notify which
+ * will determine if a give zone has crossed over the high-water mark that
+ * will justify us beginning page treatment. If we have crossed that
+ * threshold then it will start the process of pulling some pages and
+ * placing them in the batch list for treatment.
+ */
+static inline void aerator_notify_free(struct zone *zone, int order)
+{
+}
+#endif /*_LINUX_MEMORY_AERATION_H */
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index c3597920a155..7d89722ae9eb 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -116,6 +116,7 @@  static inline void set_pcppage_migratetype(struct page *page, int migratetype)
 struct free_area {
 	struct list_head	free_list[MIGRATE_TYPES];
 	unsigned long		nr_free;
+	unsigned long		nr_free_aerated;
 };
 
 static inline struct page *get_page_from_free_area(struct free_area *area,
@@ -773,6 +774,8 @@  static inline bool pgdat_is_empty(pg_data_t *pgdat)
 	return !pgdat->node_start_pfn && !pgdat->node_spanned_pages;
 }
 
+#include <linux/memory_aeration.h>
+
 /* Used for pages not on another list */
 static inline void add_to_free_area(struct page *page, struct zone *zone,
 				    unsigned int order, int migratetype)
@@ -787,10 +790,10 @@  static inline void add_to_free_area(struct page *page, struct zone *zone,
 static inline void add_to_free_area_tail(struct page *page, struct zone *zone,
 					 unsigned int order, int migratetype)
 {
-	struct free_area *area = &zone->free_area[order];
+	struct list_head *tail = aerator_get_tail(zone, order, migratetype);
 
-	list_add_tail(&page->lru, &area->free_list[migratetype]);
-	area->nr_free++;
+	list_add_tail(&page->lru, tail);
+	zone->free_area[order].nr_free++;
 }
 
 /* Used for pages which are on another list */
@@ -799,6 +802,8 @@  static inline void move_to_free_area(struct page *page, struct zone *zone,
 {
 	struct free_area *area = &zone->free_area[order];
 
+	clear_page_aerated(page, zone, area);
+
 	list_move(&page->lru, &area->free_list[migratetype]);
 }
 
@@ -807,6 +812,8 @@  static inline void del_page_from_free_area(struct page *page, struct zone *zone,
 {
 	struct free_area *area = &zone->free_area[order];
 
+	clear_page_aerated(page, zone, area);
+
 	list_del(&page->lru);
 	__ClearPageBuddy(page);
 	set_page_private(page, 0);
diff --git a/mm/Kconfig b/mm/Kconfig
index 7c41d2300e07..209dc4bea481 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -236,6 +236,11 @@  config COMPACTION
           linux-mm@kvack.org.
 
 #
+# support for memory aeration
+config AERATION
+	bool
+
+#
 # support for page migration
 #
 config MIGRATION
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index aad2b2529ab7..eb7ba8385374 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -68,6 +68,7 @@ 
 #include <linux/lockdep.h>
 #include <linux/nmi.h>
 #include <linux/psi.h>
+#include <linux/memory_aeration.h>
 
 #include <asm/sections.h>
 #include <asm/tlbflush.h>
@@ -868,10 +869,11 @@  static inline struct capture_control *task_capc(struct zone *zone)
 static inline void __free_one_page(struct page *page,
 		unsigned long pfn,
 		struct zone *zone, unsigned int order,
-		int migratetype)
+		int migratetype, bool aerated)
 {
 	struct capture_control *capc = task_capc(zone);
 	unsigned long uninitialized_var(buddy_pfn);
+	bool fully_aerated = aerated;
 	unsigned long combined_pfn;
 	unsigned int max_order;
 	struct page *buddy;
@@ -902,6 +904,11 @@  static inline void __free_one_page(struct page *page,
 			goto done_merging;
 		if (!page_is_buddy(page, buddy, order))
 			goto done_merging;
+
+		/* assume buddy is not aerated */
+		if (aerated)
+			fully_aerated = false;
+
 		/*
 		 * Our buddy is free or it is CONFIG_DEBUG_PAGEALLOC guard page,
 		 * merge with it and move up one order.
@@ -943,11 +950,17 @@  static inline void __free_one_page(struct page *page,
 done_merging:
 	set_page_order(page, order);
 
-	if (buddy_merge_likely(pfn, buddy_pfn, page, order) ||
+	if (aerated ||
+	    buddy_merge_likely(pfn, buddy_pfn, page, order) ||
 	    is_shuffle_tail_page(order))
 		add_to_free_area_tail(page, zone, order, migratetype);
 	else
 		add_to_free_area(page, zone, order, migratetype);
+
+	if (fully_aerated)
+		set_page_aerated(page, zone, order, migratetype);
+	else
+		aerator_notify_free(zone, order);
 }
 
 /*
@@ -1247,7 +1260,7 @@  static void free_pcppages_bulk(struct zone *zone, int count,
 		if (unlikely(isolated_pageblocks))
 			mt = get_pageblock_migratetype(page);
 
-		__free_one_page(page, page_to_pfn(page), zone, 0, mt);
+		__free_one_page(page, page_to_pfn(page), zone, 0, mt, false);
 		trace_mm_page_pcpu_drain(page, 0, mt);
 	}
 	spin_unlock(&zone->lock);
@@ -1263,7 +1276,7 @@  static void free_one_page(struct zone *zone,
 		is_migrate_isolate(migratetype))) {
 		migratetype = get_pfnblock_migratetype(page, pfn);
 	}
-	__free_one_page(page, pfn, zone, order, migratetype);
+	__free_one_page(page, pfn, zone, order, migratetype, false);
 	spin_unlock(&zone->lock);
 }
 
@@ -2127,6 +2140,77 @@  struct page *__rmqueue_smallest(struct zone *zone, unsigned int order,
 	return NULL;
 }
 
+#ifdef CONFIG_AERATION
+/**
+ * get_aeration_page - Provide a "raw" page for aeration by the aerator
+ * @zone: Zone to draw pages from
+ * @order: Order to draw pages from
+ * @migratetype: Migratetype to draw pages from
+ *
+ * This function will obtain a page from above the boundary. As a result
+ * we can guarantee the page has not been aerated.
+ *
+ * The page will have the migrate type and order stored in the page
+ * metadata.
+ *
+ * Return: page pointer if raw page found, otherwise NULL
+ */
+struct page *get_aeration_page(struct zone *zone, unsigned int order,
+			       int migratetype)
+{
+	struct free_area *area = &(zone->free_area[order]);
+	struct list_head *list = &area->free_list[migratetype];
+	struct page *page;
+
+	/* Find a page of the appropriate size in the preferred list */
+	page = list_last_entry(aerator_get_tail(zone, order, migratetype),
+			       struct page, lru);
+	list_for_each_entry_from_reverse(page, list, lru) {
+		if (PageAerated(page)) {
+			page = list_first_entry(list, struct page, lru);
+			if (PageAerated(page))
+				break;
+		}
+
+		del_page_from_free_area(page, zone, order);
+
+		/* record migratetype and order within page */
+		set_pcppage_migratetype(page, migratetype);
+		set_page_private(page, order);
+		__mod_zone_freepage_state(zone, -(1 << order), migratetype);
+
+		return page;
+	}
+
+	return NULL;
+}
+
+/**
+ * put_aeration_page - Return a now-aerated "raw" page back where we got it
+ * @zone: Zone to return pages to
+ * @page: Previously "raw" page that can now be returned after aeration
+ *
+ * This function will pull the migratetype and order information out
+ * of the page and attempt to return it where it found it.
+ */
+void put_aeration_page(struct zone *zone, struct page *page)
+{
+	unsigned int order, mt;
+	unsigned long pfn;
+
+	mt = get_pcppage_migratetype(page);
+	pfn = page_to_pfn(page);
+
+	if (unlikely(has_isolate_pageblock(zone) || is_migrate_isolate(mt)))
+		mt = get_pfnblock_migratetype(page, pfn);
+
+	order = page_private(page);
+	set_page_private(page, 0);
+
+	__free_one_page(page, pfn, zone, order, mt, true);
+}
+#endif /* CONFIG_AERATION */
+
 /*
  * This array describes the order lists are fallen back to when
  * the free lists for the desirable migrate type are depleted
@@ -5929,9 +6013,12 @@  void __ref memmap_init_zone_device(struct zone *zone,
 static void __meminit zone_init_free_lists(struct zone *zone)
 {
 	unsigned int order, t;
-	for_each_migratetype_order(order, t) {
+	for_each_migratetype_order(order, t)
 		INIT_LIST_HEAD(&zone->free_area[order].free_list[t]);
+
+	for (order = MAX_ORDER; order--; ) {
 		zone->free_area[order].nr_free = 0;
+		zone->free_area[order].nr_free_aerated = 0;
 	}
 }