Skip to contentSkip to conversation

CMA: clear buffer-head lru before page migration

RFC PATCH In review Gioh Kim 4 participants 9 messages Jul 4, 2014 – Jul 15, 2014

Subsystems (5)
lore
Share & tools
lore

Gioh Kim

author
#0 lore raw

RFC PATCH 1/1 CMA: clear buffer-head lru before page migration

Hi,

For page migration of CMA, buffer-heads of lru should be dropped. Please refer to https://lkml.org/lkml/2014/6/23/932 for the detail.

I'm attaching a patch to drop all buffer-head on lru with invalidate_bh_lrus. Please look into this. Thanks

------------------------------ 8< ---------------------------------

Quoted 1 line

From d90cd3d13b73f7278c60d8fe625840664adcbb6a Mon Sep 17 00:00:00 2001

From: Gioh Kim <[email protected]>
Date: Fri, 4 Jul 2014 16:53:22 +0900
Subject: [PATCH] [RFC] CMA: clear buffer-head lru before page migration

When CMA try to migrate page, some buffer-heads can exist on lru. The bh on lru has non-zero count value so that it cannot be dropped even-if it is not used. We can drop only buffers related to the migrated page, but it can take long time more than dropping all because of searching list. There all buffers in lru are dropped.

---
 fs/buffer.c |   13 +++++++++++++
 1 file changed, 13 insertions(+)
fs/buffer.c +13 -0
index eba6e4f..4f11b7a 100644
@@ -3233,6 +3233,19 @@ int try_to_free_buffers(struct page *page)
        if (PageWriteback(page))
                return 0;
 
+#ifdef CONFIG_CMA
+       /*
+        * When CMA try to migrate page, some buffer-heads can exist on lru.
+        * The bh on lru has non-zero count value so that it cannot
+        * be dropped even-if it is not used.
+        * We can drop only buffers related to the migrated page,
+        * but it can take long time more than dropping all
+        * because of searching list.
+        * There all buffers in lru are dropped first.
+        */
+       invalidate_bh_lrus();
+#endif
+
        if (mapping == NULL) {          /* can this still happen? */
                ret = drop_buffers(page, &buffers_to_free);
                goto out;

--
1.7.9.5

maintainer
#1 lore raw
Quoted 26 lines · On Fri, 04 Jul 2014 17:25:09 +0900 Gioh Kim <[email protected]> wrote:

From: Gioh Kim <[email protected]>
Date: Fri, 4 Jul 2014 16:53:22 +0900
Subject: [PATCH] [RFC] CMA: clear buffer-head lru before page migration

When CMA try to migrate page, some buffer-heads can exist on lru. The bh on lru has non-zero count value so that it cannot be dropped even-if it is not used. We can drop only buffers related to the migrated page, but it can take long time more than dropping all because of searching list. There all buffers in lru are dropped.

---
 fs/buffer.c |   13 +++++++++++++
 1 file changed, 13 insertions(+)
fs/buffer.c +2 -0
index eba6e4f..4f11b7a 100644
@@ -3233,6 +3233,19 @@ int try_to_free_buffers(struct page *page)
        if (PageWriteback(page))
                return 0;
 
+#ifdef CONFIG_CMA
+       /*

fs/buffer.c int try_to_free_buffers(struct page *page) · line 3247

Quoted 10 lines
fs/buffer.c +10 -0
+        * When CMA try to migrate page, some buffer-heads can exist on lru.
+        * The bh on lru has non-zero count value so that it cannot
+        * be dropped even-if it is not used.
+        * We can drop only buffers related to the migrated page,
+        * but it can take long time more than dropping all
+        * because of searching list.
+        * There all buffers in lru are dropped first.
+        */
+       invalidate_bh_lrus();
+#endif

No, this will be tremendously expensive.

What I proposed is that CMA call invalidate_bh_lrus() right at the outset. Something along the lines of

mm/page_alloc.c renamed from mm/page_alloc.c~a +8 -0
@@ -6329,6 +6329,14 @@ int alloc_contig_range(unsigned long sta
 	};
 	INIT_LIST_HEAD(&cc.migratepages);
 
+#ifdef CONFIG_CMA
+	/*
+	 * Comment goes here
+	 */
+	if (migratetype == MIGRATE_CMA)
+		invalidate_bh_lrus();
+#endif
+
 	/*
 	 * What we do here is we mark all pageblocks in range as
 	 * MIGRATE_ISOLATE.  Because pageblock and max order pages may

- I'd have thought that it would make sense to do this for huge pages as well (MIGRATE_MOVABLE) but nobody really seems to know.

- There's a patch floating around ("Allow increasing the buffer-head per-CPU LRU size") which will double the size of the bh lrus, so this all becomes more important.

- alloc_contig_range() does lru_add_drain_all() and drain_all_pages() *after* performing the allocation. I can't work out why this is the case and of course it is undocumented. If this is indeed not a bug then probably the invalidate_bh_lrus() should happen in the same place.

author
#2 lore raw

It's my fault.
I'm going to send another patch ASAP.

2014-07-08 오전 7:52, Andrew Morton 쓴 글:

Quoted 76 lines · On Fri, 04 Jul 2014 17:25:09 +0900 Gioh Kim <[email protected]> wrote:
Quoted 27 lines

From: Gioh Kim <[email protected]>
Date: Fri, 4 Jul 2014 16:53:22 +0900
Subject: [PATCH] [RFC] CMA: clear buffer-head lru before page migration

When CMA try to migrate page, some buffer-heads can exist on lru. The bh on lru has non-zero count value so that it cannot be dropped even-if it is not used. We can drop only buffers related to the migrated page, but it can take long time more than dropping all because of searching list. There all buffers in lru are dropped.

---
  fs/buffer.c |   13 +++++++++++++
  1 file changed, 13 insertions(+)
fs/buffer.c +2 -0
index eba6e4f..4f11b7a 100644
@@ -3233,6 +3233,19 @@ int try_to_free_buffers(struct page *page)
         if (PageWriteback(page))
                 return 0;
 
+#ifdef CONFIG_CMA
+       /*

fs/buffer.c int try_to_free_buffers(struct page *page) · line 3247

Quoted 10 lines
fs/buffer.c +10 -0
+        * When CMA try to migrate page, some buffer-heads can exist on lru.
+        * The bh on lru has non-zero count value so that it cannot
+        * be dropped even-if it is not used.
+        * We can drop only buffers related to the migrated page,
+        * but it can take long time more than dropping all
+        * because of searching list.
+        * There all buffers in lru are dropped first.
+        */
+       invalidate_bh_lrus();
+#endif

No, this will be tremendously expensive.

What I proposed is that CMA call invalidate_bh_lrus() right at the outset. Something along the lines of

mm/page_alloc.c renamed from mm/page_alloc.c~a +8 -3
@@ -6329,6 +6329,14 @@ int alloc_contig_range(unsigned long sta
  	};
  	INIT_LIST_HEAD(&cc.migratepages);
 
+#ifdef CONFIG_CMA
+	/*
+	 * Comment goes here
+	 */
+	if (migratetype == MIGRATE_CMA)
+		invalidate_bh_lrus();
+#endif
+
  	/*
  	 * What we do here is we mark all pageblocks in range as
  	 * MIGRATE_ISOLATE.  Because pageblock and max order pages may
 
 
- I'd have thought that it would make sense to do this for huge pages
   as well (MIGRATE_MOVABLE) but nobody really seems to know.
 
- There's a patch floating around ("Allow increasing the buffer-head
   per-CPU LRU size") which will double the size of the bh lrus, so this
   all becomes more important.
 
- alloc_contig_range() does lru_add_drain_all() and drain_all_pages()
   *after* performing the allocation.  I can't work out why this is the
   case and of course it is undocumented.  If this is indeed not a bug
   then probably the invalidate_bh_lrus() should happen in the same
   place.
maintainer
#3 lore raw
Quoted 44 lines · On Tue, 08 Jul 2014 13:44:04 +0900 Gioh Kim <[email protected]> wrote:

2014-07-08 ______ 7:52, Andrew Morton ___ ___:

Quoted 43 lines · On Fri, 04 Jul 2014 17:25:09 +0900 Gioh Kim <[email protected]> wrote:
Quoted 27 lines

From: Gioh Kim <[email protected]>
Date: Fri, 4 Jul 2014 16:53:22 +0900
Subject: [PATCH] [RFC] CMA: clear buffer-head lru before page migration

When CMA try to migrate page, some buffer-heads can exist on lru. The bh on lru has non-zero count value so that it cannot be dropped even-if it is not used. We can drop only buffers related to the migrated page, but it can take long time more than dropping all because of searching list. There all buffers in lru are dropped.

---
  fs/buffer.c |   13 +++++++++++++
  1 file changed, 13 insertions(+)
fs/buffer.c +2 -0
index eba6e4f..4f11b7a 100644
@@ -3233,6 +3233,19 @@ int try_to_free_buffers(struct page *page)
         if (PageWriteback(page))
                 return 0;
 
+#ifdef CONFIG_CMA
+       /*

fs/buffer.c int try_to_free_buffers(struct page *page) · line 3247

Quoted 10 lines
fs/buffer.c +10 -0
+        * When CMA try to migrate page, some buffer-heads can exist on lru.
+        * The bh on lru has non-zero count value so that it cannot
+        * be dropped even-if it is not used.
+        * We can drop only buffers related to the migrated page,
+        * but it can take long time more than dropping all
+        * because of searching list.
+        * There all buffers in lru are dropped first.
+        */
+       invalidate_bh_lrus();
+#endif

No, this will be tremendously expensive.

What I proposed is that CMA call invalidate_bh_lrus() right at the outset. Something along the lines of

Please do not top-post your email replies - it makes it very hard to conduct a coherent discussion.

Quoted 2 lines

It's my fault.
I'm going to send another patch ASAP.

No, not "ASAP". Such a patch will require careful testing on numerous system configurations and workloads. Take however much time it needs to get it right.

#4 lore raw
In reply to Andrew Morton
Quoted 7 lines · Andrew Morton

What I proposed is that CMA call invalidate_bh_lrus() right at the outset. Something along the lines of

mm/page_alloc.c renamed from mm/page_alloc.c~a +0 -0
@@ -6329,6 +6329,14 @@ int alloc_contig_range(unsigned long sta
 	};

mm/page_alloc.c int alloc_contig_range(unsigned long sta · line 6339

Quoted 10 lines
mm/page_alloc.c renamed from mm/page_alloc.c~a +8 -0
 	INIT_LIST_HEAD(&cc.migratepages);
 
+#ifdef CONFIG_CMA
+	/*
+	 * Comment goes here
+	 */
+	if (migratetype == MIGRATE_CMA)
+		invalidate_bh_lrus();
+#endif
+

This seems reasonable, except I think it should go after start_isolate_page_range call because otherwise there's no guarantee that someone won't grab those pages back.

Also to avoid the #ifdef perhaps we want this as well:

include/linux/mmzone.h +3 -2
index 6cbd1b6..2640a55 100644
@@ -64,10 +64,11 @@ enum {
 };
 
 #ifdef CONFIG_CMA
-#  define is_migrate_cma(migratetype) unlikely((migratetype) == MIGRATE_CMA)
+#  define __is_migrate_cma(migratetype) ((migratetype) == MIGRATE_CMA)
 #else
-#  define is_migrate_cma(migratetype) false
+#  define __is_migrate_cma(migratetype) false
 #endif
+#define is_migrate_cma(migratetype) unlikely(__is_migrate_cma(migratetype))
 
 #define for_each_migratetype_order(order, type) \
 	for (order = 0; order < MAX_ORDER; order++) \

and then use “if (__is_migrate_cma(migratetype))”.

Quoted 7 lines
mm/page_alloc.c renamed from mm/page_alloc.c~a +0 -1
 	/*
 	 * What we do here is we mark all pageblocks in range as
 	 * MIGRATE_ISOLATE.  Because pageblock and max order pages may
 
 
- I'd have thought that it would make sense to do this for huge pages
  as well (MIGRATE_MOVABLE) but nobody really seems to know.

mm/page_alloc.c int alloc_contig_range(unsigned long sta

Quoted 10 lines
mm/page_alloc.c renamed from mm/page_alloc.c~a +0 -2
 
- There's a patch floating around ("Allow increasing the buffer-head
  per-CPU LRU size") which will double the size of the bh lrus, so this
  all becomes more important.
 
- alloc_contig_range() does lru_add_drain_all() and drain_all_pages()
  *after* performing the allocation.  I can't work out why this is the
  case and of course it is undocumented.  If this is indeed not a bug
  then probably the invalidate_bh_lrus() should happen in the same
  place.

The purpose is to get free non-buddy pages (so pages on PCP lists for instance) back onto the buddy list. It's safe to move those calls above the call to __alloc_contig_migrate_range, but I don't think it will change anything (except of course the fact that if migration fails, we'll do the draining for nothing).

Signature · 8 lines
Best regards,                                         _     _
.o. | Liege of Serenely Enlightened Majesty of      o' \,=./ `o
..o | Computer Science,  Michał “mina86” Nazarewicz    (o o)
ooo +--<[email protected]>--<xmpp:[email protected]>--ooO--(_)--Ooo--
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#5 lore raw
Quoted 23 lines · Michal Nazarewicz
Quoted 7 lines · Andrew Morton

What I proposed is that CMA call invalidate_bh_lrus() right at the outset. Something along the lines of

mm/page_alloc.c renamed from mm/page_alloc.c~a +0 -0
@@ -6329,6 +6329,14 @@ int alloc_contig_range(unsigned long sta
 	};

mm/page_alloc.c int alloc_contig_range(unsigned long sta · line 6339

Quoted 10 lines
mm/page_alloc.c renamed from mm/page_alloc.c~a +8 -0
 	INIT_LIST_HEAD(&cc.migratepages);
 
+#ifdef CONFIG_CMA
+	/*
+	 * Comment goes here
+	 */
+	if (migratetype == MIGRATE_CMA)
+		invalidate_bh_lrus();
+#endif
+

This seems reasonable, except I think it should go after start_isolate_page_range call because otherwise there's no guarantee that someone won't grab those pages back.

Quoted 1 line

Also to avoid the #ifdef perhaps we want this as well:

I think that we just want to remove ifdef CONFIG_CMA on above code snippet, because invalidate_bh_lrus() would also help user of alloc_contig_range() with MIGRATE_MOVABLE.

Quoted 40 lines
include/linux/mmzone.h +3 -2
index 6cbd1b6..2640a55 100644
@@ -64,10 +64,11 @@ enum {
 };
 
 #ifdef CONFIG_CMA
-#  define is_migrate_cma(migratetype) unlikely((migratetype) == MIGRATE_CMA)
+#  define __is_migrate_cma(migratetype) ((migratetype) == MIGRATE_CMA)
 #else
-#  define is_migrate_cma(migratetype) false
+#  define __is_migrate_cma(migratetype) false
 #endif
+#define is_migrate_cma(migratetype) unlikely(__is_migrate_cma(migratetype))
 
 #define for_each_migratetype_order(order, type) \
 	for (order = 0; order < MAX_ORDER; order++) \

and then use “if (__is_migrate_cma(migratetype))”.

Quoted 7 lines
mm/page_alloc.c renamed from mm/page_alloc.c~a +0 -1
 	/*
 	 * What we do here is we mark all pageblocks in range as
 	 * MIGRATE_ISOLATE.  Because pageblock and max order pages may
 
 
- I'd have thought that it would make sense to do this for huge pages
  as well (MIGRATE_MOVABLE) but nobody really seems to know.

mm/page_alloc.c int alloc_contig_range(unsigned long sta

Quoted 10 lines
mm/page_alloc.c renamed from mm/page_alloc.c~a +0 -2
 
- There's a patch floating around ("Allow increasing the buffer-head
  per-CPU LRU size") which will double the size of the bh lrus, so this
  all becomes more important.
 
- alloc_contig_range() does lru_add_drain_all() and drain_all_pages()
  *after* performing the allocation.  I can't work out why this is the
  case and of course it is undocumented.  If this is indeed not a bug
  then probably the invalidate_bh_lrus() should happen in the same
  place.
Quoted 5 lines

The purpose is to get free non-buddy pages (so pages on PCP lists for instance) back onto the buddy list. It's safe to move those calls above the call to __alloc_contig_migrate_range, but I don't think it will change anything (except of course the fact that if migration fails, we'll do the draining for nothing).

At a glance, we don't need that drain_all_pages(), because drain_all_pages() is also called by set_migratetype_isolate() after changing migratetype.

And, it is better to move up lru_add_drain_all() to ahead of __alloc_contig_migrate_range(), because some pages could be skipped to migrate due to this lru page caching mechanism.

Thanks.
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#6 lore raw
Quoted 10 lines · Joonsoo Kim
Quoted 5 lines · Michal Nazarewicz

This seems reasonable, except I think [invalidate_bh_lrus()] should go after start_isolate_page_range call because otherwise there's no guarantee that someone won't grab those pages back.

Also to avoid the #ifdef perhaps we want this as well:

I think that we just want to remove ifdef CONFIG_CMA on above code snippet, because invalidate_bh_lrus() would also help user of alloc_contig_range() with MIGRATE_MOVABLE.

Sounds good to me.

Quoted 9 lines
Quoted 5 lines

The purpose is to get free non-buddy pages (so pages on PCP lists for instance) back onto the buddy list. It's safe to move those calls above the call to __alloc_contig_migrate_range, but I don't think it will change anything (except of course the fact that if migration fails, we'll do the draining for nothing).

At a glance, we don't need that drain_all_pages(), because drain_all_pages() is also called by set_migratetype_isolate() after changing migratetype.

You are likely correct.

Quoted 3 lines

And, it is better to move up lru_add_drain_all() to ahead of __alloc_contig_migrate_range(), because some pages could be skipped to migrate due to this lru page caching mechanism.

Again, sounds good to me.

Signature · 4 lines
Best regards,                                         _     _
.o. | Liege of Serenely Enlightened Majesty of      o' \,=./ `o
..o | Computer Science,  Michał “mina86” Nazarewicz    (o o)
ooo +--<[email protected]>--<xmpp:[email protected]>--ooO--(_)--Ooo--
maintainer
#7 lore raw
In reply to Joonsoo Kim
Quoted 24 lines · On Mon, 14 Jul 2014 16:02:25 +0900 Joonsoo Kim <[email protected]> wrote:
Quoted 23 lines · Michal Nazarewicz
Quoted 7 lines · Andrew Morton

What I proposed is that CMA call invalidate_bh_lrus() right at the outset. Something along the lines of

mm/page_alloc.c renamed from mm/page_alloc.c~a +0 -0
@@ -6329,6 +6329,14 @@ int alloc_contig_range(unsigned long sta
 	};

mm/page_alloc.c int alloc_contig_range(unsigned long sta · line 6339

Quoted 10 lines
mm/page_alloc.c renamed from mm/page_alloc.c~a +8 -0
 	INIT_LIST_HEAD(&cc.migratepages);
 
+#ifdef CONFIG_CMA
+	/*
+	 * Comment goes here
+	 */
+	if (migratetype == MIGRATE_CMA)
+		invalidate_bh_lrus();
+#endif
+

This seems reasonable, except I think it should go after start_isolate_page_range call because otherwise there's no guarantee that someone won't grab those pages back.

Quoted 5 lines
Quoted 1 line

Also to avoid the #ifdef perhaps we want this as well:

I think that we just want to remove ifdef CONFIG_CMA on above code snippet, because invalidate_bh_lrus() would also help user of alloc_contig_range() with MIGRATE_MOVABLE.

That's what I believe also. I pinged Mel and Johannes off-list and Mel said "I hit it, the invalidation cost wasn't worth it for a THP alloc".

So hm. I do think it's worth additional investigation but some careful testing would be needed to demonstrate that it's worthwhile. If the invalidation cost is hurting then perhaps additional logic will be needed to perform the invalidation only as a last-resort thing.

author
#8 lore raw

2014-07-15 오전 5:37, Andrew Morton 쓴 글:

Quoted 32 lines · On Mon, 14 Jul 2014 16:02:25 +0900 Joonsoo Kim <[email protected]> wrote:
Quoted 25 lines · Michal Nazarewicz
Quoted 23 lines · Andrew Morton
Quoted 7 lines

What I proposed is that CMA call invalidate_bh_lrus() right at the outset. Something along the lines of

mm/page_alloc.c renamed from mm/page_alloc.c~a +0 -0
@@ -6329,6 +6329,14 @@ int alloc_contig_range(unsigned long sta
  	};

mm/page_alloc.c int alloc_contig_range(unsigned long sta · line 6339

Quoted 10 lines
mm/page_alloc.c renamed from mm/page_alloc.c~a +8 -0
  	INIT_LIST_HEAD(&cc.migratepages);
 
+#ifdef CONFIG_CMA
+	/*
+	 * Comment goes here
+	 */
+	if (migratetype == MIGRATE_CMA)
+		invalidate_bh_lrus();
+#endif
+

This seems reasonable, except I think it should go after start_isolate_page_range call because otherwise there's no guarantee that someone won't grab those pages back.

Quoted 5 lines
Quoted 1 line

Also to avoid the #ifdef perhaps we want this as well:

I think that we just want to remove ifdef CONFIG_CMA on above code snippet, because invalidate_bh_lrus() would also help user of alloc_contig_range() with MIGRATE_MOVABLE.

Quoted 10 lines

That's what I believe also. I pinged Mel and Johannes off-list and Mel said "I hit it, the invalidation cost wasn't worth it for a THP alloc".

So hm. I do think it's worth additional investigation but some careful testing would be needed to demonstrate that it's worthwhile. If the invalidation cost is hurting then perhaps additional logic will be needed to perform the invalidation only as a last-resort thing.

Adding invalidate_bh_lrus() between start_isolate_page_range and __alloc_contig_migrate_range is working well on my platform.

But I'd like to test performance before sending patch. Would anybody recommend me a benchmark tool?

--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html