* [3.6 regression?] THP + migration/compaction livelock (I think)
@ 2012-11-13 22:13 Andy Lutomirski
2012-11-13 23:11 ` David Rientjes
0 siblings, 1 reply; 16+ messages in thread
From: Andy Lutomirski @ 2012-11-13 22:13 UTC (permalink / raw)
To: linux-kernel, linux-mm
I've seen an odd problem three times in the past two weeks. I suspect
a Linux 3.6 regression. I"m on 3.6.3-1.fc17.x86_64. I run a parallel
compilation, and no progress is made. All cpus are pegged at 100%
system time by the respective cc1plus processes. Reading
/proc/<pid>/stack shows either
[<ffffffff8108e01a>] __cond_resched+0x2a/0x40
[<ffffffff8114e432>] isolate_migratepages_range+0xb2/0x620
[<ffffffff8114eba4>] compact_zone+0x144/0x410
[<ffffffff8114f152>] compact_zone_order+0x82/0xc0
[<ffffffff8114f271>] try_to_compact_pages+0xe1/0x130
[<ffffffff816143db>] __alloc_pages_direct_compact+0xaa/0x190
[<ffffffff81133d26>] __alloc_pages_nodemask+0x526/0x990
[<ffffffff81171496>] alloc_pages_vma+0xb6/0x190
[<ffffffff81182683>] do_huge_pmd_anonymous_page+0x143/0x340
[<ffffffff811549fd>] handle_mm_fault+0x27d/0x320
[<ffffffff81620adc>] do_page_fault+0x15c/0x4b0
[<ffffffff8161d625>] page_fault+0x25/0x30
[<ffffffffffffffff>] 0xffffffffffffffff
or
[<ffffffffffffffff>] 0xffffffffffffffff
seemingly at random (i.e. if I read that file twice in a row, I might
see different results). If I had to guess, I'd say that
perf shows no 'faults'. The livelock resolved after several minutes
(and before I got far enough with perf to get more useful results).
Every time this happens, firefox hangs but everything else keeps
working.
If I trigger it again, I'll try to grab /proc/zoneinfo and /proc/meminfo.
--Andy
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [3.6 regression?] THP + migration/compaction livelock (I think)
2012-11-13 22:13 [3.6 regression?] THP + migration/compaction livelock (I think) Andy Lutomirski
@ 2012-11-13 23:11 ` David Rientjes
2012-11-13 23:25 ` Andy Lutomirski
0 siblings, 1 reply; 16+ messages in thread
From: David Rientjes @ 2012-11-13 23:11 UTC (permalink / raw)
To: Andy Lutomirski; +Cc: Marc Duponcheel, Mel Gorman, linux-kernel, linux-mm
On Tue, 13 Nov 2012, Andy Lutomirski wrote:
> I've seen an odd problem three times in the past two weeks. I suspect
> a Linux 3.6 regression. I"m on 3.6.3-1.fc17.x86_64. I run a parallel
> compilation, and no progress is made. All cpus are pegged at 100%
> system time by the respective cc1plus processes. Reading
> /proc/<pid>/stack shows either
>
> [<ffffffff8108e01a>] __cond_resched+0x2a/0x40
> [<ffffffff8114e432>] isolate_migratepages_range+0xb2/0x620
> [<ffffffff8114eba4>] compact_zone+0x144/0x410
> [<ffffffff8114f152>] compact_zone_order+0x82/0xc0
> [<ffffffff8114f271>] try_to_compact_pages+0xe1/0x130
> [<ffffffff816143db>] __alloc_pages_direct_compact+0xaa/0x190
> [<ffffffff81133d26>] __alloc_pages_nodemask+0x526/0x990
> [<ffffffff81171496>] alloc_pages_vma+0xb6/0x190
> [<ffffffff81182683>] do_huge_pmd_anonymous_page+0x143/0x340
> [<ffffffff811549fd>] handle_mm_fault+0x27d/0x320
> [<ffffffff81620adc>] do_page_fault+0x15c/0x4b0
> [<ffffffff8161d625>] page_fault+0x25/0x30
> [<ffffffffffffffff>] 0xffffffffffffffff
>
> or
>
> [<ffffffffffffffff>] 0xffffffffffffffff
>
This reminds me of the thread at http://marc.info/?t=135102111800004 which
caused Marc's system to reportedly go unresponsive like your report but in
his case it also caused a reboot. If your system is still running (or,
even better, if you're able to capture this happening in realtime), could
you try to capture
grep -E "compact_|thp_" /proc/vmstat
as well while it is in progress? (Even if it's not happening right now,
the data might still be useful if you have knowledge that it has occurred
since the last reboot.)
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [3.6 regression?] THP + migration/compaction livelock (I think)
2012-11-13 23:11 ` David Rientjes
@ 2012-11-13 23:25 ` Andy Lutomirski
2012-11-13 23:41 ` David Rientjes
0 siblings, 1 reply; 16+ messages in thread
From: Andy Lutomirski @ 2012-11-13 23:25 UTC (permalink / raw)
To: David Rientjes; +Cc: Marc Duponcheel, Mel Gorman, linux-kernel, linux-mm
On Tue, Nov 13, 2012 at 3:11 PM, David Rientjes <rientjes@google.com> wrote:
> On Tue, 13 Nov 2012, Andy Lutomirski wrote:
>
>> I've seen an odd problem three times in the past two weeks. I suspect
>> a Linux 3.6 regression. I"m on 3.6.3-1.fc17.x86_64. I run a parallel
>> compilation, and no progress is made. All cpus are pegged at 100%
>> system time by the respective cc1plus processes. Reading
>> /proc/<pid>/stack shows either
>>
>> [<ffffffff8108e01a>] __cond_resched+0x2a/0x40
>> [<ffffffff8114e432>] isolate_migratepages_range+0xb2/0x620
>> [<ffffffff8114eba4>] compact_zone+0x144/0x410
>> [<ffffffff8114f152>] compact_zone_order+0x82/0xc0
>> [<ffffffff8114f271>] try_to_compact_pages+0xe1/0x130
>> [<ffffffff816143db>] __alloc_pages_direct_compact+0xaa/0x190
>> [<ffffffff81133d26>] __alloc_pages_nodemask+0x526/0x990
>> [<ffffffff81171496>] alloc_pages_vma+0xb6/0x190
>> [<ffffffff81182683>] do_huge_pmd_anonymous_page+0x143/0x340
>> [<ffffffff811549fd>] handle_mm_fault+0x27d/0x320
>> [<ffffffff81620adc>] do_page_fault+0x15c/0x4b0
>> [<ffffffff8161d625>] page_fault+0x25/0x30
>> [<ffffffffffffffff>] 0xffffffffffffffff
>>
>> or
>>
>> [<ffffffffffffffff>] 0xffffffffffffffff
>>
>
> This reminds me of the thread at http://marc.info/?t=135102111800004 which
> caused Marc's system to reportedly go unresponsive like your report but in
> his case it also caused a reboot. If your system is still running (or,
> even better, if you're able to capture this happening in realtime), could
> you try to capture
>
> grep -E "compact_|thp_" /proc/vmstat
>
> as well while it is in progress? (Even if it's not happening right now,
> the data might still be useful if you have knowledge that it has occurred
> since the last reboot.)
It just happened again.
$ grep -E "compact_|thp_" /proc/vmstat
compact_blocks_moved 8332448774
compact_pages_moved 21831286
compact_pagemigrate_failed 211260
compact_stall 13484
compact_fail 6717
compact_success 6755
thp_fault_alloc 150665
thp_fault_fallback 4270
thp_collapse_alloc 19771
thp_collapse_alloc_failed 2188
thp_split 19600
/proc/meminfo:
MemTotal: 16388116 kB
MemFree: 6684372 kB
Buffers: 34960 kB
Cached: 6233588 kB
SwapCached: 29500 kB
Active: 4881396 kB
Inactive: 3824296 kB
Active(anon): 1687576 kB
Inactive(anon): 764852 kB
Active(file): 3193820 kB
Inactive(file): 3059444 kB
Unevictable: 0 kB
Mlocked: 0 kB
SwapTotal: 16777212 kB
SwapFree: 16643864 kB
Dirty: 184 kB
Writeback: 0 kB
AnonPages: 2408692 kB
Mapped: 126964 kB
Shmem: 15272 kB
Slab: 635496 kB
SReclaimable: 528924 kB
SUnreclaim: 106572 kB
KernelStack: 3600 kB
PageTables: 39460 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 24971268 kB
Committed_AS: 5688448 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 614952 kB
VmallocChunk: 34359109524 kB
HardwareCorrupted: 0 kB
AnonHugePages: 1050624 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
DirectMap4k: 3600384 kB
DirectMap2M: 11038720 kB
DirectMap1G: 1048576 kB
$ sudo ./perf stat -p 11764 -e
compaction:mm_compaction_isolate_migratepages,task-clock,vmscan:mm_vmscan_direct_reclaim_begin,vmscan:mm_vmscan_lru_isolate,vmscan:mm_vmscan_memcg_isolate
[sudo] password for luto:
^C
Performance counter stats for process id '11764':
1,638,009 compaction:mm_compaction_isolate_migratepages #
0.716 M/sec [100.00%]
2286.993046 task-clock # 0.872 CPUs utilized
[100.00%]
0 vmscan:mm_vmscan_direct_reclaim_begin # 0.000
M/sec [100.00%]
0 vmscan:mm_vmscan_lru_isolate # 0.000 M/sec
[100.00%]
0 vmscan:mm_vmscan_memcg_isolate # 0.000 M/sec
2.623626878 seconds time elapsed
/proc/zoneinfo:
Node 0, zone DMA
pages free 3972
min 16
low 20
high 24
scanned 0
spanned 4080
present 3911
nr_free_pages 3972
nr_inactive_anon 0
nr_active_anon 0
nr_inactive_file 0
nr_active_file 0
nr_unevictable 0
nr_mlock 0
nr_anon_pages 0
nr_mapped 0
nr_file_pages 0
nr_dirty 0
nr_writeback 0
nr_slab_reclaimable 0
nr_slab_unreclaimable 4
nr_page_table_pages 0
nr_kernel_stack 0
nr_unstable 0
nr_bounce 0
nr_vmscan_write 0
nr_vmscan_immediate_reclaim 0
nr_writeback_temp 0
nr_isolated_anon 0
nr_isolated_file 0
nr_shmem 0
nr_dirtied 0
nr_written 0
numa_hit 1
numa_miss 0
numa_foreign 0
numa_interleave 0
numa_local 1
numa_other 0
nr_anon_transparent_hugepages 0
protection: (0, 2434, 16042, 16042)
pagesets
cpu: 0
count: 0
high: 0
batch: 1
vm stats threshold: 8
cpu: 1
count: 0
high: 0
batch: 1
vm stats threshold: 8
cpu: 2
count: 0
high: 0
batch: 1
vm stats threshold: 8
cpu: 3
count: 0
high: 0
batch: 1
vm stats threshold: 8
cpu: 4
count: 0
high: 0
batch: 1
vm stats threshold: 8
cpu: 5
count: 0
high: 0
batch: 1
vm stats threshold: 8
cpu: 6
count: 0
high: 0
batch: 1
vm stats threshold: 8
cpu: 7
count: 0
high: 0
batch: 1
vm stats threshold: 8
cpu: 8
count: 0
high: 0
batch: 1
vm stats threshold: 8
cpu: 9
count: 0
high: 0
batch: 1
vm stats threshold: 8
cpu: 10
count: 0
high: 0
batch: 1
vm stats threshold: 8
cpu: 11
count: 0
high: 0
batch: 1
vm stats threshold: 8
all_unreclaimable: 1
start_pfn: 16
inactive_ratio: 1
Node 0, zone DMA32
pages free 321075
min 2561
low 3201
high 3841
scanned 0
spanned 1044480
present 623163
nr_free_pages 321075
nr_inactive_anon 43450
nr_active_anon 203472
nr_inactive_file 5416
nr_active_file 39568
nr_unevictable 0
nr_mlock 0
nr_anon_pages 86455
nr_mapped 156
nr_file_pages 45195
nr_dirty 0
nr_writeback 0
nr_slab_reclaimable 6679
nr_slab_unreclaimable 419
nr_page_table_pages 2
nr_kernel_stack 0
nr_unstable 0
nr_bounce 0
nr_vmscan_write 9994
nr_vmscan_immediate_reclaim 1
nr_writeback_temp 0
nr_isolated_anon 0
nr_isolated_file 0
nr_shmem 1
nr_dirtied 1765256
nr_written 1763392
numa_hit 53134489
numa_miss 0
numa_foreign 0
numa_interleave 0
numa_local 53134489
numa_other 0
nr_anon_transparent_hugepages 313
protection: (0, 0, 13608, 13608)
pagesets
cpu: 0
count: 0
high: 186
batch: 31
vm stats threshold: 48
cpu: 1
count: 4
high: 186
batch: 31
vm stats threshold: 48
cpu: 2
count: 4
high: 186
batch: 31
vm stats threshold: 48
cpu: 3
count: 0
high: 186
batch: 31
vm stats threshold: 48
cpu: 4
count: 4
high: 186
batch: 31
vm stats threshold: 48
cpu: 5
count: 0
high: 186
batch: 31
vm stats threshold: 48
cpu: 6
count: 0
high: 186
batch: 31
vm stats threshold: 48
cpu: 7
count: 11
high: 186
batch: 31
vm stats threshold: 48
cpu: 8
count: 0
high: 186
batch: 31
vm stats threshold: 48
cpu: 9
count: 4
high: 186
batch: 31
vm stats threshold: 48
cpu: 10
count: 13
high: 186
batch: 31
vm stats threshold: 48
cpu: 11
count: 4
high: 186
batch: 31
vm stats threshold: 48
all_unreclaimable: 0
start_pfn: 4096
inactive_ratio: 4
Node 0, zone Normal
pages free 1343098
min 14318
low 17897
high 21477
scanned 0
spanned 3538944
present 3483648
nr_free_pages 1343098
nr_inactive_anon 147925
nr_active_anon 221736
nr_inactive_file 759336
nr_active_file 758833
nr_unevictable 0
nr_mlock 0
nr_anon_pages 257074
nr_mapped 31632
nr_file_pages 1529150
nr_dirty 25
nr_writeback 0
nr_slab_reclaimable 125552
nr_slab_unreclaimable 26176
nr_page_table_pages 9844
nr_kernel_stack 456
nr_unstable 0
nr_bounce 0
nr_vmscan_write 36224
nr_vmscan_immediate_reclaim 117
nr_writeback_temp 0
nr_isolated_anon 0
nr_isolated_file 0
nr_shmem 3815
nr_dirtied 51415788
nr_written 48993658
numa_hit 1081691700
numa_miss 0
numa_foreign 0
numa_interleave 25195
numa_local 1081691700
numa_other 0
nr_anon_transparent_hugepages 199
protection: (0, 0, 0, 0)
pagesets
cpu: 0
count: 156
high: 186
batch: 31
vm stats threshold: 64
cpu: 1
count: 177
high: 186
batch: 31
vm stats threshold: 64
cpu: 2
count: 159
high: 186
batch: 31
vm stats threshold: 64
cpu: 3
count: 161
high: 186
batch: 31
vm stats threshold: 64
cpu: 4
count: 146
high: 186
batch: 31
vm stats threshold: 64
cpu: 5
count: 98
high: 186
batch: 31
vm stats threshold: 64
cpu: 6
count: 59
high: 186
batch: 31
vm stats threshold: 64
cpu: 7
count: 54
high: 186
batch: 31
vm stats threshold: 64
cpu: 8
count: 40
high: 186
batch: 31
vm stats threshold: 64
cpu: 9
count: 32
high: 186
batch: 31
vm stats threshold: 64
cpu: 10
count: 46
high: 186
batch: 31
vm stats threshold: 64
cpu: 11
count: 57
high: 186
batch: 31
vm stats threshold: 64
all_unreclaimable: 0
start_pfn: 1048576
inactive_ratio: 11
--Andy
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [3.6 regression?] THP + migration/compaction livelock (I think)
2012-11-13 23:25 ` Andy Lutomirski
@ 2012-11-13 23:41 ` David Rientjes
2012-11-13 23:45 ` Andy Lutomirski
2012-11-14 10:01 ` Mel Gorman
0 siblings, 2 replies; 16+ messages in thread
From: David Rientjes @ 2012-11-13 23:41 UTC (permalink / raw)
To: Andy Lutomirski; +Cc: Marc Duponcheel, Mel Gorman, linux-kernel, linux-mm
On Tue, 13 Nov 2012, Andy Lutomirski wrote:
> It just happened again.
>
> $ grep -E "compact_|thp_" /proc/vmstat
> compact_blocks_moved 8332448774
> compact_pages_moved 21831286
> compact_pagemigrate_failed 211260
> compact_stall 13484
> compact_fail 6717
> compact_success 6755
> thp_fault_alloc 150665
> thp_fault_fallback 4270
> thp_collapse_alloc 19771
> thp_collapse_alloc_failed 2188
> thp_split 19600
>
Two of the patches from the list provided at
http://marc.info/?l=linux-mm&m=135179005510688 are already in your 3.6.3
kernel:
mm: compaction: abort compaction loop if lock is contended or run too long
mm: compaction: acquire the zone->lock as late as possible
and all have not made it to the 3.6 stable kernel yet, so would it be
possible to try with 3.7-rc5 to see if it fixes the issue? If so, it will
indicate that the entire series is a candidate to backport to 3.6.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [3.6 regression?] THP + migration/compaction livelock (I think)
2012-11-13 23:41 ` David Rientjes
@ 2012-11-13 23:45 ` Andy Lutomirski
2012-11-13 23:54 ` David Rientjes
2012-11-14 10:01 ` Mel Gorman
1 sibling, 1 reply; 16+ messages in thread
From: Andy Lutomirski @ 2012-11-13 23:45 UTC (permalink / raw)
To: David Rientjes; +Cc: Marc Duponcheel, Mel Gorman, linux-kernel, linux-mm
On Tue, Nov 13, 2012 at 3:41 PM, David Rientjes <rientjes@google.com> wrote:
> On Tue, 13 Nov 2012, Andy Lutomirski wrote:
>
>> It just happened again.
>>
>> $ grep -E "compact_|thp_" /proc/vmstat
>> compact_blocks_moved 8332448774
>> compact_pages_moved 21831286
>> compact_pagemigrate_failed 211260
>> compact_stall 13484
>> compact_fail 6717
>> compact_success 6755
>> thp_fault_alloc 150665
>> thp_fault_fallback 4270
>> thp_collapse_alloc 19771
>> thp_collapse_alloc_failed 2188
>> thp_split 19600
>>
>
> Two of the patches from the list provided at
> http://marc.info/?l=linux-mm&m=135179005510688 are already in your 3.6.3
> kernel:
>
> mm: compaction: abort compaction loop if lock is contended or run too long
> mm: compaction: acquire the zone->lock as late as possible
>
> and all have not made it to the 3.6 stable kernel yet, so would it be
> possible to try with 3.7-rc5 to see if it fixes the issue? If so, it will
> indicate that the entire series is a candidate to backport to 3.6.
I'll try later on. The last time I tried to boot 3.7 on this box, it
failed impressively (presumably due to a localmodconfig bug, but I
haven't tracked it down yet).
I'm also not sure how reliably I can reproduce this.
--Andy
--
Andy Lutomirski
AMA Capital Management, LLC
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [3.6 regression?] THP + migration/compaction livelock (I think)
2012-11-13 23:45 ` Andy Lutomirski
@ 2012-11-13 23:54 ` David Rientjes
2012-11-14 1:22 ` Marc Duponcheel
0 siblings, 1 reply; 16+ messages in thread
From: David Rientjes @ 2012-11-13 23:54 UTC (permalink / raw)
To: Andy Lutomirski, Marc Duponcheel; +Cc: Mel Gorman, linux-kernel, linux-mm
On Tue, 13 Nov 2012, Andy Lutomirski wrote:
> >> $ grep -E "compact_|thp_" /proc/vmstat
> >> compact_blocks_moved 8332448774
> >> compact_pages_moved 21831286
> >> compact_pagemigrate_failed 211260
> >> compact_stall 13484
> >> compact_fail 6717
> >> compact_success 6755
> >> thp_fault_alloc 150665
> >> thp_fault_fallback 4270
> >> thp_collapse_alloc 19771
> >> thp_collapse_alloc_failed 2188
> >> thp_split 19600
> >>
> >
> > Two of the patches from the list provided at
> > http://marc.info/?l=linux-mm&m=135179005510688 are already in your 3.6.3
> > kernel:
> >
> > mm: compaction: abort compaction loop if lock is contended or run too long
> > mm: compaction: acquire the zone->lock as late as possible
> >
> > and all have not made it to the 3.6 stable kernel yet, so would it be
> > possible to try with 3.7-rc5 to see if it fixes the issue? If so, it will
> > indicate that the entire series is a candidate to backport to 3.6.
>
> I'll try later on. The last time I tried to boot 3.7 on this box, it
> failed impressively (presumably due to a localmodconfig bug, but I
> haven't tracked it down yet).
>
> I'm also not sure how reliably I can reproduce this.
>
The challenge goes out to Marc too since he reported this issue on 3.6.2
but we haven't heard back yet on the success of the backport (although
it's probably easier to try 3.7-rc5 since there are some conflicts to
resolve).
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [3.6 regression?] THP + migration/compaction livelock (I think)
2012-11-13 23:54 ` David Rientjes
@ 2012-11-14 1:22 ` Marc Duponcheel
2012-11-14 1:51 ` David Rientjes
0 siblings, 1 reply; 16+ messages in thread
From: Marc Duponcheel @ 2012-11-14 1:22 UTC (permalink / raw)
To: David Rientjes
Cc: Andy Lutomirski, Mel Gorman, linux-kernel, linux-mm, Marc Duponcheel
Hi all, please let me know if there is are patches you want me to try.
FWIW time did not stand still and I run 3.6.6 now.
On 2012 Nov 13, #David Rientjes wrote:
> On Tue, 13 Nov 2012, Andy Lutomirski wrote:
>
> > >> $ grep -E "compact_|thp_" /proc/vmstat
> > >> compact_blocks_moved 8332448774
> > >> compact_pages_moved 21831286
> > >> compact_pagemigrate_failed 211260
> > >> compact_stall 13484
> > >> compact_fail 6717
> > >> compact_success 6755
> > >> thp_fault_alloc 150665
> > >> thp_fault_fallback 4270
> > >> thp_collapse_alloc 19771
> > >> thp_collapse_alloc_failed 2188
> > >> thp_split 19600
> > >>
> > >
> > > Two of the patches from the list provided at
> > > http://marc.info/?l=linux-mm&m=135179005510688 are already in your 3.6.3
> > > kernel:
> > >
> > > mm: compaction: abort compaction loop if lock is contended or run too long
> > > mm: compaction: acquire the zone->lock as late as possible
> > >
> > > and all have not made it to the 3.6 stable kernel yet, so would it be
> > > possible to try with 3.7-rc5 to see if it fixes the issue? If so, it will
> > > indicate that the entire series is a candidate to backport to 3.6.
> >
> > I'll try later on. The last time I tried to boot 3.7 on this box, it
> > failed impressively (presumably due to a localmodconfig bug, but I
> > haven't tracked it down yet).
> >
> > I'm also not sure how reliably I can reproduce this.
> >
>
> The challenge goes out to Marc too since he reported this issue on 3.6.2
> but we haven't heard back yet on the success of the backport (although
> it's probably easier to try 3.7-rc5 since there are some conflicts to
> resolve).
--
Marc Duponcheel
Velodroomstraat 74 - 2600 Berchem - Belgium
+32 (0)478 68.10.91 - marc@offline.be
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [3.6 regression?] THP + migration/compaction livelock (I think)
2012-11-14 1:22 ` Marc Duponcheel
@ 2012-11-14 1:51 ` David Rientjes
2012-11-14 13:21 ` Marc Duponcheel
0 siblings, 1 reply; 16+ messages in thread
From: David Rientjes @ 2012-11-14 1:51 UTC (permalink / raw)
To: Marc Duponcheel; +Cc: Andy Lutomirski, Mel Gorman, linux-kernel, linux-mm
On Wed, 14 Nov 2012, Marc Duponcheel wrote:
> Hi all, please let me know if there is are patches you want me to try.
>
> FWIW time did not stand still and I run 3.6.6 now.
>
Hmm, interesting since there are no core VM changes between 3.6.2, the
kernel you ran into problems with, and 3.6.6.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [3.6 regression?] THP + migration/compaction livelock (I think)
2012-11-13 23:41 ` David Rientjes
2012-11-13 23:45 ` Andy Lutomirski
@ 2012-11-14 10:01 ` Mel Gorman
2012-11-14 13:29 ` Marc Duponcheel
1 sibling, 1 reply; 16+ messages in thread
From: Mel Gorman @ 2012-11-14 10:01 UTC (permalink / raw)
To: David Rientjes; +Cc: Andy Lutomirski, Marc Duponcheel, linux-kernel, linux-mm
On Tue, Nov 13, 2012 at 03:41:02PM -0800, David Rientjes wrote:
> On Tue, 13 Nov 2012, Andy Lutomirski wrote:
>
> > It just happened again.
> >
> > $ grep -E "compact_|thp_" /proc/vmstat
> > compact_blocks_moved 8332448774
> > compact_pages_moved 21831286
> > compact_pagemigrate_failed 211260
> > compact_stall 13484
> > compact_fail 6717
> > compact_success 6755
> > thp_fault_alloc 150665
> > thp_fault_fallback 4270
> > thp_collapse_alloc 19771
> > thp_collapse_alloc_failed 2188
> > thp_split 19600
> >
>
> Two of the patches from the list provided at
> http://marc.info/?l=linux-mm&m=135179005510688 are already in your 3.6.3
> kernel:
>
> mm: compaction: abort compaction loop if lock is contended or run too long
> mm: compaction: acquire the zone->lock as late as possible
>
> and all have not made it to the 3.6 stable kernel yet, so would it be
> possible to try with 3.7-rc5 to see if it fixes the issue? If so, it will
> indicate that the entire series is a candidate to backport to 3.6.
Thanks David once again.
The full list of compaction-related patches I believe are necessary for
this particular problem are
e64c5237cf6ff474cb2f3f832f48f2b441dd9979 mm: compaction: abort compaction loop if lock is contended or run too long
3cc668f4e30fbd97b3c0574d8cac7a83903c9bc7 mm: compaction: move fatal signal check out of compact_checklock_irqsave
661c4cb9b829110cb68c18ea05a56be39f75a4d2 mm: compaction: Update try_to_compact_pages()kerneldoc comment
2a1402aa044b55c2d30ab0ed9405693ef06fb07c mm: compaction: acquire the zone->lru_lock as late as possible
f40d1e42bb988d2a26e8e111ea4c4c7bac819b7e mm: compaction: acquire the zone->lock as late as possible
753341a4b85ff337487b9959c71c529f522004f4 revert "mm: have order > 0 compaction start off where it left"
bb13ffeb9f6bfeb301443994dfbf29f91117dfb3 mm: compaction: cache if a pageblock was scanned and no pages were isolated
c89511ab2f8fe2b47585e60da8af7fd213ec877e mm: compaction: Restart compaction from near where it left off
62997027ca5b3d4618198ed8b1aba40b61b1137b mm: compaction: clear PG_migrate_skip based on compaction and reclaim activity
0db63d7e25f96e2c6da925c002badf6f144ddf30 mm: compaction: correct the nr_strict va isolated check for CMA
If we can get confirmation that these fix the problem in 3.6 kernels then
I can backport them to -stable. This fixing a problem where "many processes
stall, all in an isolation-related function". This started happening after
lumpy reclaim was removed because we depended on that to aggressively
reclaim with less compaction. Now compaction is depended upon more.
The full 3.7-rc5 kernel has a different problem on top of this and it's
important the problems do not get conflacted. It has these fixes *but*
GFP_NO_KSWAPD has been removed and there is a patch that scales reclaim
with THP failures that is causing problem. With them, kswapd can get
stuck in a 100% loop where it is neither reclaiming nor reaching its exit
conditions. The correct fix would be to identify why this happens but I
have not got around to it yet. To test with 3.7-rc5 then apply either
1) https://lkml.org/lkml/2012/11/5/308
2) https://lkml.org/lkml/2012/11/12/113
or
1) https://lkml.org/lkml/2012/11/5/308
3) https://lkml.org/lkml/2012/11/12/151
on top of 3.7-rc5. So it's a lot of work but there are three tests I'm
interested in hearing about. The results of each determine what happens
in -stable or mainline
Test 1: 3.6 + the last of commits above (should fix processes stick in isolate)
Test 2: 3.7-rc5 + (1+2) above (should fix kswapd stuck at 100%)
Test 3: 3.7-rc5 + (1+3) above (should fix kswapd stuck at 100% but better)
Thanks.
--
Mel Gorman
SUSE Labs
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [3.6 regression?] THP + migration/compaction livelock (I think)
2012-11-14 1:51 ` David Rientjes
@ 2012-11-14 13:21 ` Marc Duponcheel
0 siblings, 0 replies; 16+ messages in thread
From: Marc Duponcheel @ 2012-11-14 13:21 UTC (permalink / raw)
To: David Rientjes
Cc: Andy Lutomirski, Mel Gorman, linux-kernel, linux-mm, Marc Duponcheel
On 2012 Nov 13, David Rientjes wrote:
> On Wed, 14 Nov 2012, Marc Duponcheel wrote:
>
> > Hi all, please let me know if there is are patches you want me to try.
> >
> > FWIW time did not stand still and I run 3.6.6 now.
>
> Hmm, interesting since there are no core VM changes between 3.6.2, the
> kernel you ran into problems with, and 3.6.6.
Hi David
I have not tried yet to repro #49361 on 3.6.6, but, as you say, if
there are no core VM changes, I am confident I can do so just by doing
# echo always > /sys/kernel/mm/transparent_hugepage/enabled
I am at your disposal to test further, and, if there are patches, to
try them out.
Note that I only once experienced a crash for which I could not find
relevant info in logs. But the hanging processes issue could always be
reproduced consistently.
have a nice day
--
Marc Duponcheel
Velodroomstraat 74 - 2600 Berchem - Belgium
+32 (0)478 68.10.91 - marc@offline.be
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [3.6 regression?] THP + migration/compaction livelock (I think)
2012-11-14 10:01 ` Mel Gorman
@ 2012-11-14 13:29 ` Marc Duponcheel
2012-11-14 21:50 ` David Rientjes
0 siblings, 1 reply; 16+ messages in thread
From: Marc Duponcheel @ 2012-11-14 13:29 UTC (permalink / raw)
To: Mel Gorman
Cc: David Rientjes, Andy Lutomirski, linux-kernel, linux-mm, Marc Duponcheel
Hi all
If someone can provide the patches (or learn me how to get them with
git (I apologise to not be git savy)) then, this weekend, I can apply
them to 3.6.6 and compare before/after to check if they fix #49361.
Thanks
On 2012 Nov 14, Mel Gorman wrote:
> On Tue, Nov 13, 2012 at 03:41:02PM -0800, David Rientjes wrote:
> > On Tue, 13 Nov 2012, Andy Lutomirski wrote:
> >
> > > It just happened again.
> > >
> > > $ grep -E "compact_|thp_" /proc/vmstat
> > > compact_blocks_moved 8332448774
> > > compact_pages_moved 21831286
> > > compact_pagemigrate_failed 211260
> > > compact_stall 13484
> > > compact_fail 6717
> > > compact_success 6755
> > > thp_fault_alloc 150665
> > > thp_fault_fallback 4270
> > > thp_collapse_alloc 19771
> > > thp_collapse_alloc_failed 2188
> > > thp_split 19600
> > >
> >
> > Two of the patches from the list provided at
> > http://marc.info/?l=linux-mm&m=135179005510688 are already in your 3.6.3
> > kernel:
> >
> > mm: compaction: abort compaction loop if lock is contended or run too long
> > mm: compaction: acquire the zone->lock as late as possible
> >
> > and all have not made it to the 3.6 stable kernel yet, so would it be
> > possible to try with 3.7-rc5 to see if it fixes the issue? If so, it will
> > indicate that the entire series is a candidate to backport to 3.6.
>
> Thanks David once again.
>
> The full list of compaction-related patches I believe are necessary for
> this particular problem are
>
> e64c5237cf6ff474cb2f3f832f48f2b441dd9979 mm: compaction: abort compaction loop if lock is contended or run too long
> 3cc668f4e30fbd97b3c0574d8cac7a83903c9bc7 mm: compaction: move fatal signal check out of compact_checklock_irqsave
> 661c4cb9b829110cb68c18ea05a56be39f75a4d2 mm: compaction: Update try_to_compact_pages()kerneldoc comment
> 2a1402aa044b55c2d30ab0ed9405693ef06fb07c mm: compaction: acquire the zone->lru_lock as late as possible
> f40d1e42bb988d2a26e8e111ea4c4c7bac819b7e mm: compaction: acquire the zone->lock as late as possible
> 753341a4b85ff337487b9959c71c529f522004f4 revert "mm: have order > 0 compaction start off where it left"
> bb13ffeb9f6bfeb301443994dfbf29f91117dfb3 mm: compaction: cache if a pageblock was scanned and no pages were isolated
> c89511ab2f8fe2b47585e60da8af7fd213ec877e mm: compaction: Restart compaction from near where it left off
> 62997027ca5b3d4618198ed8b1aba40b61b1137b mm: compaction: clear PG_migrate_skip based on compaction and reclaim activity
> 0db63d7e25f96e2c6da925c002badf6f144ddf30 mm: compaction: correct the nr_strict va isolated check for CMA
>
> If we can get confirmation that these fix the problem in 3.6 kernels then
> I can backport them to -stable. This fixing a problem where "many processes
> stall, all in an isolation-related function". This started happening after
> lumpy reclaim was removed because we depended on that to aggressively
> reclaim with less compaction. Now compaction is depended upon more.
>
> The full 3.7-rc5 kernel has a different problem on top of this and it's
> important the problems do not get conflacted. It has these fixes *but*
> GFP_NO_KSWAPD has been removed and there is a patch that scales reclaim
> with THP failures that is causing problem. With them, kswapd can get
> stuck in a 100% loop where it is neither reclaiming nor reaching its exit
> conditions. The correct fix would be to identify why this happens but I
> have not got around to it yet. To test with 3.7-rc5 then apply either
>
> 1) https://lkml.org/lkml/2012/11/5/308
> 2) https://lkml.org/lkml/2012/11/12/113
>
> or
>
> 1) https://lkml.org/lkml/2012/11/5/308
> 3) https://lkml.org/lkml/2012/11/12/151
>
> on top of 3.7-rc5. So it's a lot of work but there are three tests I'm
> interested in hearing about. The results of each determine what happens
> in -stable or mainline
>
> Test 1: 3.6 + the last of commits above (should fix processes stick in isolate)
> Test 2: 3.7-rc5 + (1+2) above (should fix kswapd stuck at 100%)
> Test 3: 3.7-rc5 + (1+3) above (should fix kswapd stuck at 100% but better)
>
> Thanks.
>
> --
> Mel Gorman
> SUSE Labs
>
--
--
Marc Duponcheel
Velodroomstraat 74 - 2600 Berchem - Belgium
+32 (0)478 68.10.91 - marc@offline.be
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [3.6 regression?] THP + migration/compaction livelock (I think)
2012-11-14 13:29 ` Marc Duponcheel
@ 2012-11-14 21:50 ` David Rientjes
2012-11-15 1:14 ` Marc Duponcheel
0 siblings, 1 reply; 16+ messages in thread
From: David Rientjes @ 2012-11-14 21:50 UTC (permalink / raw)
To: Marc Duponcheel; +Cc: Mel Gorman, Andy Lutomirski, linux-kernel, linux-mm
On Wed, 14 Nov 2012, Marc Duponcheel wrote:
> Hi all
>
> If someone can provide the patches (or learn me how to get them with
> git (I apologise to not be git savy)) then, this weekend, I can apply
> them to 3.6.6 and compare before/after to check if they fix #49361.
>
I've backported all the commits that Mel quoted to 3.6.6 and appended them
to this email as one big patch. It should apply cleanly to your kernel.
Now we are only missing these commits that weren't quoted:
- 1fb3f8ca0e92 ("mm: compaction: capture a suitable high-order page
immediately when it is made available"), and
- 83fde0f22872 ("mm: vmscan: scale number of pages reclaimed by
reclaim/compaction based on failures").
Since your regression is easily reproducible, would it be possible to try
to reproduce the issue FIRST with 3.6.6 and, if still present as it was in
3.6.2, then try reproducing it with the appended patch?
You earlier reported that khugepaged was taking the second-most cpu time
when this was happening, which initially pointed you to thp, so presumably
this isn't a kswapd issue running at 100%. If both 3.6.6 kernels fail
(the one with and without the following patch), would it be possible to
try Mel's suggestion of patching with
- https://lkml.org/lkml/2012/11/5/308 +
https://lkml.org/lkml/2012/11/12/113
to see if it helps and, if not, reverting the latter and trying
- https://lkml.org/lkml/2012/11/5/308 +
https://lkml.org/lkml/2012/11/12/151
as the final test? This will certainly help us to find out what needs to
be backported to 3.6 stable to prevent this issue for other users.
Thanks!
---
include/linux/compaction.h | 15 ++
include/linux/mmzone.h | 6 +-
include/linux/pageblock-flags.h | 19 +-
mm/compaction.c | 450 +++++++++++++++++++++++++--------------
mm/internal.h | 16 +-
mm/page_alloc.c | 42 ++--
mm/vmscan.c | 8 +
7 files changed, 366 insertions(+), 190 deletions(-)
diff --git a/include/linux/compaction.h b/include/linux/compaction.h
--- a/include/linux/compaction.h
+++ b/include/linux/compaction.h
@@ -24,6 +24,7 @@ extern unsigned long try_to_compact_pages(struct zonelist *zonelist,
int order, gfp_t gfp_mask, nodemask_t *mask,
bool sync, bool *contended);
extern int compact_pgdat(pg_data_t *pgdat, int order);
+extern void reset_isolation_suitable(pg_data_t *pgdat);
extern unsigned long compaction_suitable(struct zone *zone, int order);
/* Do not skip compaction more than 64 times */
@@ -61,6 +62,16 @@ static inline bool compaction_deferred(struct zone *zone, int order)
return zone->compact_considered < defer_limit;
}
+/* Returns true if restarting compaction after many failures */
+static inline bool compaction_restarting(struct zone *zone, int order)
+{
+ if (order < zone->compact_order_failed)
+ return false;
+
+ return zone->compact_defer_shift == COMPACT_MAX_DEFER_SHIFT &&
+ zone->compact_considered >= 1UL << zone->compact_defer_shift;
+}
+
#else
static inline unsigned long try_to_compact_pages(struct zonelist *zonelist,
int order, gfp_t gfp_mask, nodemask_t *nodemask,
@@ -74,6 +85,10 @@ static inline int compact_pgdat(pg_data_t *pgdat, int order)
return COMPACT_CONTINUE;
}
+static inline void reset_isolation_suitable(pg_data_t *pgdat)
+{
+}
+
static inline unsigned long compaction_suitable(struct zone *zone, int order)
{
return COMPACT_SKIPPED;
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -369,8 +369,12 @@ struct zone {
spinlock_t lock;
int all_unreclaimable; /* All pages pinned */
#if defined CONFIG_COMPACTION || defined CONFIG_CMA
- /* pfn where the last incremental compaction isolated free pages */
+ /* Set to true when the PG_migrate_skip bits should be cleared */
+ bool compact_blockskip_flush;
+
+ /* pfns where compaction scanners should start */
unsigned long compact_cached_free_pfn;
+ unsigned long compact_cached_migrate_pfn;
#endif
#ifdef CONFIG_MEMORY_HOTPLUG
/* see spanned/present_pages for more description */
diff --git a/include/linux/pageblock-flags.h b/include/linux/pageblock-flags.h
--- a/include/linux/pageblock-flags.h
+++ b/include/linux/pageblock-flags.h
@@ -30,6 +30,9 @@ enum pageblock_bits {
PB_migrate,
PB_migrate_end = PB_migrate + 3 - 1,
/* 3 bits required for migrate types */
+#ifdef CONFIG_COMPACTION
+ PB_migrate_skip,/* If set the block is skipped by compaction */
+#endif /* CONFIG_COMPACTION */
NR_PAGEBLOCK_BITS
};
@@ -65,10 +68,22 @@ unsigned long get_pageblock_flags_group(struct page *page,
void set_pageblock_flags_group(struct page *page, unsigned long flags,
int start_bitidx, int end_bitidx);
+#ifdef CONFIG_COMPACTION
+#define get_pageblock_skip(page) \
+ get_pageblock_flags_group(page, PB_migrate_skip, \
+ PB_migrate_skip + 1)
+#define clear_pageblock_skip(page) \
+ set_pageblock_flags_group(page, 0, PB_migrate_skip, \
+ PB_migrate_skip + 1)
+#define set_pageblock_skip(page) \
+ set_pageblock_flags_group(page, 1, PB_migrate_skip, \
+ PB_migrate_skip + 1)
+#endif /* CONFIG_COMPACTION */
+
#define get_pageblock_flags(page) \
- get_pageblock_flags_group(page, 0, NR_PAGEBLOCK_BITS-1)
+ get_pageblock_flags_group(page, 0, PB_migrate_end)
#define set_pageblock_flags(page, flags) \
set_pageblock_flags_group(page, flags, \
- 0, NR_PAGEBLOCK_BITS-1)
+ 0, PB_migrate_end)
#endif /* PAGEBLOCK_FLAGS_H */
diff --git a/mm/compaction.c b/mm/compaction.c
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -50,6 +50,111 @@ static inline bool migrate_async_suitable(int migratetype)
return is_migrate_cma(migratetype) || migratetype == MIGRATE_MOVABLE;
}
+#ifdef CONFIG_COMPACTION
+/* Returns true if the pageblock should be scanned for pages to isolate. */
+static inline bool isolation_suitable(struct compact_control *cc,
+ struct page *page)
+{
+ if (cc->ignore_skip_hint)
+ return true;
+
+ return !get_pageblock_skip(page);
+}
+
+/*
+ * This function is called to clear all cached information on pageblocks that
+ * should be skipped for page isolation when the migrate and free page scanner
+ * meet.
+ */
+static void __reset_isolation_suitable(struct zone *zone)
+{
+ unsigned long start_pfn = zone->zone_start_pfn;
+ unsigned long end_pfn = zone->zone_start_pfn + zone->spanned_pages;
+ unsigned long pfn;
+
+ zone->compact_cached_migrate_pfn = start_pfn;
+ zone->compact_cached_free_pfn = end_pfn;
+ zone->compact_blockskip_flush = false;
+
+ /* Walk the zone and mark every pageblock as suitable for isolation */
+ for (pfn = start_pfn; pfn < end_pfn; pfn += pageblock_nr_pages) {
+ struct page *page;
+
+ cond_resched();
+
+ if (!pfn_valid(pfn))
+ continue;
+
+ page = pfn_to_page(pfn);
+ if (zone != page_zone(page))
+ continue;
+
+ clear_pageblock_skip(page);
+ }
+}
+
+void reset_isolation_suitable(pg_data_t *pgdat)
+{
+ int zoneid;
+
+ for (zoneid = 0; zoneid < MAX_NR_ZONES; zoneid++) {
+ struct zone *zone = &pgdat->node_zones[zoneid];
+ if (!populated_zone(zone))
+ continue;
+
+ /* Only flush if a full compaction finished recently */
+ if (zone->compact_blockskip_flush)
+ __reset_isolation_suitable(zone);
+ }
+}
+
+/*
+ * If no pages were isolated then mark this pageblock to be skipped in the
+ * future. The information is later cleared by __reset_isolation_suitable().
+ */
+static void update_pageblock_skip(struct compact_control *cc,
+ struct page *page, unsigned long nr_isolated,
+ bool migrate_scanner)
+{
+ struct zone *zone = cc->zone;
+ if (!page)
+ return;
+
+ if (!nr_isolated) {
+ unsigned long pfn = page_to_pfn(page);
+ set_pageblock_skip(page);
+
+ /* Update where compaction should restart */
+ if (migrate_scanner) {
+ if (!cc->finished_update_migrate &&
+ pfn > zone->compact_cached_migrate_pfn)
+ zone->compact_cached_migrate_pfn = pfn;
+ } else {
+ if (!cc->finished_update_free &&
+ pfn < zone->compact_cached_free_pfn)
+ zone->compact_cached_free_pfn = pfn;
+ }
+ }
+}
+#else
+static inline bool isolation_suitable(struct compact_control *cc,
+ struct page *page)
+{
+ return true;
+}
+
+static void update_pageblock_skip(struct compact_control *cc,
+ struct page *page, unsigned long nr_isolated,
+ bool migrate_scanner)
+{
+}
+#endif /* CONFIG_COMPACTION */
+
+static inline bool should_release_lock(spinlock_t *lock)
+{
+ return need_resched() || spin_is_contended(lock);
+}
+
/*
* Compaction requires the taking of some coarse locks that are potentially
* very heavily contended. Check if the process needs to be scheduled or
@@ -62,7 +167,7 @@ static inline bool migrate_async_suitable(int migratetype)
static bool compact_checklock_irqsave(spinlock_t *lock, unsigned long *flags,
bool locked, struct compact_control *cc)
{
- if (need_resched() || spin_is_contended(lock)) {
+ if (should_release_lock(lock)) {
if (locked) {
spin_unlock_irqrestore(lock, *flags);
locked = false;
@@ -70,14 +175,11 @@ static bool compact_checklock_irqsave(spinlock_t *lock, unsigned long *flags,
/* async aborts if taking too long or contended */
if (!cc->sync) {
- if (cc->contended)
- *cc->contended = true;
+ cc->contended = true;
return false;
}
cond_resched();
- if (fatal_signal_pending(current))
- return false;
}
if (!locked)
@@ -91,44 +193,85 @@ static inline bool compact_trylock_irqsave(spinlock_t *lock,
return compact_checklock_irqsave(lock, flags, false, cc);
}
+/* Returns true if the page is within a block suitable for migration to */
+static bool suitable_migration_target(struct page *page)
+{
+ int migratetype = get_pageblock_migratetype(page);
+
+ /* Don't interfere with memory hot-remove or the min_free_kbytes blocks */
+ if (migratetype == MIGRATE_ISOLATE || migratetype == MIGRATE_RESERVE)
+ return false;
+
+ /* If the page is a large free page, then allow migration */
+ if (PageBuddy(page) && page_order(page) >= pageblock_order)
+ return true;
+
+ /* If the block is MIGRATE_MOVABLE or MIGRATE_CMA, allow migration */
+ if (migrate_async_suitable(migratetype))
+ return true;
+
+ /* Otherwise skip the block */
+ return false;
+}
+
/*
* Isolate free pages onto a private freelist. Caller must hold zone->lock.
* If @strict is true, will abort returning 0 on any invalid PFNs or non-free
* pages inside of the pageblock (even though it may still end up isolating
* some pages).
*/
-static unsigned long isolate_freepages_block(unsigned long blockpfn,
+static unsigned long isolate_freepages_block(struct compact_control *cc,
+ unsigned long blockpfn,
unsigned long end_pfn,
struct list_head *freelist,
bool strict)
{
int nr_scanned = 0, total_isolated = 0;
- struct page *cursor;
+ struct page *cursor, *valid_page = NULL;
+ unsigned long nr_strict_required = end_pfn - blockpfn;
+ unsigned long flags;
+ bool locked = false;
cursor = pfn_to_page(blockpfn);
- /* Isolate free pages. This assumes the block is valid */
+ /* Isolate free pages. */
for (; blockpfn < end_pfn; blockpfn++, cursor++) {
int isolated, i;
struct page *page = cursor;
- if (!pfn_valid_within(blockpfn)) {
- if (strict)
- return 0;
- continue;
- }
nr_scanned++;
+ if (!pfn_valid_within(blockpfn))
+ continue;
+ if (!valid_page)
+ valid_page = page;
+ if (!PageBuddy(page))
+ continue;
+
+ /*
+ * The zone lock must be held to isolate freepages.
+ * Unfortunately this is a very coarse lock and can be
+ * heavily contended if there are parallel allocations
+ * or parallel compactions. For async compaction do not
+ * spin on the lock and we acquire the lock as late as
+ * possible.
+ */
+ locked = compact_checklock_irqsave(&cc->zone->lock, &flags,
+ locked, cc);
+ if (!locked)
+ break;
+
+ /* Recheck this is a suitable migration target under lock */
+ if (!strict && !suitable_migration_target(page))
+ break;
- if (!PageBuddy(page)) {
- if (strict)
- return 0;
+ /* Recheck this is a buddy page under lock */
+ if (!PageBuddy(page))
continue;
- }
/* Found a free page, break it into order-0 pages */
isolated = split_free_page(page);
if (!isolated && strict)
- return 0;
+ break;
total_isolated += isolated;
for (i = 0; i < isolated; i++) {
list_add(&page->lru, freelist);
@@ -143,6 +286,22 @@ static unsigned long isolate_freepages_block(unsigned long blockpfn,
}
trace_mm_compaction_isolate_freepages(nr_scanned, total_isolated);
+
+ /*
+ * If strict isolation is requested by CMA then check that all the
+ * pages requested were isolated. If there were any failures, 0 is
+ * returned and CMA will fail.
+ */
+ if (strict && nr_strict_required > total_isolated)
+ total_isolated = 0;
+
+ if (locked)
+ spin_unlock_irqrestore(&cc->zone->lock, flags);
+
+ /* Update the pageblock-skip if the whole pageblock was scanned */
+ if (blockpfn == end_pfn)
+ update_pageblock_skip(cc, valid_page, total_isolated, false);
+
return total_isolated;
}
@@ -160,17 +319,14 @@ static unsigned long isolate_freepages_block(unsigned long blockpfn,
* a free page).
*/
unsigned long
-isolate_freepages_range(unsigned long start_pfn, unsigned long end_pfn)
+isolate_freepages_range(struct compact_control *cc,
+ unsigned long start_pfn, unsigned long end_pfn)
{
- unsigned long isolated, pfn, block_end_pfn, flags;
- struct zone *zone = NULL;
+ unsigned long isolated, pfn, block_end_pfn;
LIST_HEAD(freelist);
- if (pfn_valid(start_pfn))
- zone = page_zone(pfn_to_page(start_pfn));
-
for (pfn = start_pfn; pfn < end_pfn; pfn += isolated) {
- if (!pfn_valid(pfn) || zone != page_zone(pfn_to_page(pfn)))
+ if (!pfn_valid(pfn) || cc->zone != page_zone(pfn_to_page(pfn)))
break;
/*
@@ -180,10 +336,8 @@ isolate_freepages_range(unsigned long start_pfn, unsigned long end_pfn)
block_end_pfn = ALIGN(pfn + 1, pageblock_nr_pages);
block_end_pfn = min(block_end_pfn, end_pfn);
- spin_lock_irqsave(&zone->lock, flags);
- isolated = isolate_freepages_block(pfn, block_end_pfn,
+ isolated = isolate_freepages_block(cc, pfn, block_end_pfn,
&freelist, true);
- spin_unlock_irqrestore(&zone->lock, flags);
/*
* In strict mode, isolate_freepages_block() returns 0 if
@@ -276,7 +430,8 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
isolate_mode_t mode = 0;
struct lruvec *lruvec;
unsigned long flags;
- bool locked;
+ bool locked = false;
+ struct page *page = NULL, *valid_page = NULL;
/*
* Ensure that there are not too many pages isolated from the LRU
@@ -296,23 +451,15 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
/* Time to isolate some pages for migration */
cond_resched();
- spin_lock_irqsave(&zone->lru_lock, flags);
- locked = true;
for (; low_pfn < end_pfn; low_pfn++) {
- struct page *page;
-
/* give a chance to irqs before checking need_resched() */
- if (!((low_pfn+1) % SWAP_CLUSTER_MAX)) {
- spin_unlock_irqrestore(&zone->lru_lock, flags);
- locked = false;
+ if (locked && !((low_pfn+1) % SWAP_CLUSTER_MAX)) {
+ if (should_release_lock(&zone->lru_lock)) {
+ spin_unlock_irqrestore(&zone->lru_lock, flags);
+ locked = false;
+ }
}
- /* Check if it is ok to still hold the lock */
- locked = compact_checklock_irqsave(&zone->lru_lock, &flags,
- locked, cc);
- if (!locked)
- break;
-
/*
* migrate_pfn does not necessarily start aligned to a
* pageblock. Ensure that pfn_valid is called when moving
@@ -340,6 +487,14 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
if (page_zone(page) != zone)
continue;
+ if (!valid_page)
+ valid_page = page;
+
+ /* If isolation recently failed, do not retry */
+ pageblock_nr = low_pfn >> pageblock_order;
+ if (!isolation_suitable(cc, page))
+ goto next_pageblock;
+
/* Skip if free */
if (PageBuddy(page))
continue;
@@ -349,24 +504,43 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
* migration is optimistic to see if the minimum amount of work
* satisfies the allocation
*/
- pageblock_nr = low_pfn >> pageblock_order;
if (!cc->sync && last_pageblock_nr != pageblock_nr &&
!migrate_async_suitable(get_pageblock_migratetype(page))) {
- low_pfn += pageblock_nr_pages;
- low_pfn = ALIGN(low_pfn, pageblock_nr_pages) - 1;
- last_pageblock_nr = pageblock_nr;
- continue;
+ cc->finished_update_migrate = true;
+ goto next_pageblock;
}
+ /* Check may be lockless but that's ok as we recheck later */
if (!PageLRU(page))
continue;
/*
- * PageLRU is set, and lru_lock excludes isolation,
- * splitting and collapsing (collapsing has already
- * happened if PageLRU is set).
+ * PageLRU is set. lru_lock normally excludes isolation
+ * splitting and collapsing (collapsing has already happened
+ * if PageLRU is set) but the lock is not necessarily taken
+ * here and it is wasteful to take it just to check transhuge.
+ * Check TransHuge without lock and skip the whole pageblock if
+ * it's either a transhuge or hugetlbfs page, as calling
+ * compound_order() without preventing THP from splitting the
+ * page underneath us may return surprising results.
*/
if (PageTransHuge(page)) {
+ if (!locked)
+ goto next_pageblock;
+ low_pfn += (1 << compound_order(page)) - 1;
+ continue;
+ }
+
+ /* Check if it is ok to still hold the lock */
+ locked = compact_checklock_irqsave(&zone->lru_lock, &flags,
+ locked, cc);
+ if (!locked || fatal_signal_pending(current))
+ break;
+
+ /* Recheck PageLRU and PageTransHuge under lock */
+ if (!PageLRU(page))
+ continue;
+ if (PageTransHuge(page)) {
low_pfn += (1 << compound_order(page)) - 1;
continue;
}
@@ -383,6 +557,7 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
VM_BUG_ON(PageTransCompound(page));
/* Successfully isolated */
+ cc->finished_update_migrate = true;
del_page_from_lru_list(page, lruvec, page_lru(page));
list_add(&page->lru, migratelist);
cc->nr_migratepages++;
@@ -393,6 +568,13 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
++low_pfn;
break;
}
+
+ continue;
+
+next_pageblock:
+ low_pfn += pageblock_nr_pages;
+ low_pfn = ALIGN(low_pfn, pageblock_nr_pages) - 1;
+ last_pageblock_nr = pageblock_nr;
}
acct_isolated(zone, locked, cc);
@@ -400,6 +582,10 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
if (locked)
spin_unlock_irqrestore(&zone->lru_lock, flags);
+ /* Update the pageblock-skip if the whole pageblock was scanned */
+ if (low_pfn == end_pfn)
+ update_pageblock_skip(cc, valid_page, nr_isolated, true);
+
trace_mm_compaction_isolate_migratepages(nr_scanned, nr_isolated);
return low_pfn;
@@ -407,43 +593,6 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
#endif /* CONFIG_COMPACTION || CONFIG_CMA */
#ifdef CONFIG_COMPACTION
-
-/* Returns true if the page is within a block suitable for migration to */
-static bool suitable_migration_target(struct page *page)
-{
-
- int migratetype = get_pageblock_migratetype(page);
-
- /* Don't interfere with memory hot-remove or the min_free_kbytes blocks */
- if (migratetype == MIGRATE_ISOLATE || migratetype == MIGRATE_RESERVE)
- return false;
-
- /* If the page is a large free page, then allow migration */
- if (PageBuddy(page) && page_order(page) >= pageblock_order)
- return true;
-
- /* If the block is MIGRATE_MOVABLE or MIGRATE_CMA, allow migration */
- if (migrate_async_suitable(migratetype))
- return true;
-
- /* Otherwise skip the block */
- return false;
-}
-
-/*
- * Returns the start pfn of the last page block in a zone. This is the starting
- * point for full compaction of a zone. Compaction searches for free pages from
- * the end of each zone, while isolate_freepages_block scans forward inside each
- * page block.
- */
-static unsigned long start_free_pfn(struct zone *zone)
-{
- unsigned long free_pfn;
- free_pfn = zone->zone_start_pfn + zone->spanned_pages;
- free_pfn &= ~(pageblock_nr_pages-1);
- return free_pfn;
-}
-
/*
* Based on information in the current compact_control, find blocks
* suitable for isolating free pages from and then isolate them.
@@ -453,7 +602,6 @@ static void isolate_freepages(struct zone *zone,
{
struct page *page;
unsigned long high_pfn, low_pfn, pfn, zone_end_pfn, end_pfn;
- unsigned long flags;
int nr_freepages = cc->nr_freepages;
struct list_head *freelist = &cc->freepages;
@@ -501,30 +649,16 @@ static void isolate_freepages(struct zone *zone,
if (!suitable_migration_target(page))
continue;
- /*
- * Found a block suitable for isolating free pages from. Now
- * we disabled interrupts, double check things are ok and
- * isolate the pages. This is to minimise the time IRQs
- * are disabled
- */
- isolated = 0;
+ /* If isolation recently failed, do not retry */
+ if (!isolation_suitable(cc, page))
+ continue;
- /*
- * The zone lock must be held to isolate freepages. This
- * unfortunately this is a very coarse lock and can be
- * heavily contended if there are parallel allocations
- * or parallel compactions. For async compaction do not
- * spin on the lock
- */
- if (!compact_trylock_irqsave(&zone->lock, &flags, cc))
- break;
- if (suitable_migration_target(page)) {
- end_pfn = min(pfn + pageblock_nr_pages, zone_end_pfn);
- isolated = isolate_freepages_block(pfn, end_pfn,
- freelist, false);
- nr_freepages += isolated;
- }
- spin_unlock_irqrestore(&zone->lock, flags);
+ /* Found a block suitable for isolating free pages from */
+ isolated = 0;
+ end_pfn = min(pfn + pageblock_nr_pages, zone_end_pfn);
+ isolated = isolate_freepages_block(cc, pfn, end_pfn,
+ freelist, false);
+ nr_freepages += isolated;
/*
* Record the highest PFN we isolated pages from. When next
@@ -532,17 +666,8 @@ static void isolate_freepages(struct zone *zone,
* page migration may have returned some pages to the allocator
*/
if (isolated) {
+ cc->finished_update_free = true;
high_pfn = max(high_pfn, pfn);
-
- /*
- * If the free scanner has wrapped, update
- * compact_cached_free_pfn to point to the highest
- * pageblock with free pages. This reduces excessive
- * scanning of full pageblocks near the end of the
- * zone
- */
- if (cc->order > 0 && cc->wrapped)
- zone->compact_cached_free_pfn = high_pfn;
}
}
@@ -551,11 +676,6 @@ static void isolate_freepages(struct zone *zone,
cc->free_pfn = high_pfn;
cc->nr_freepages = nr_freepages;
-
- /* If compact_cached_free_pfn is reset then set it now */
- if (cc->order > 0 && !cc->wrapped &&
- zone->compact_cached_free_pfn == start_free_pfn(zone))
- zone->compact_cached_free_pfn = high_pfn;
}
/*
@@ -634,7 +754,7 @@ static isolate_migrate_t isolate_migratepages(struct zone *zone,
/* Perform the isolation */
low_pfn = isolate_migratepages_range(zone, cc, low_pfn, end_pfn);
- if (!low_pfn)
+ if (!low_pfn || cc->contended)
return ISOLATE_ABORT;
cc->migrate_pfn = low_pfn;
@@ -651,27 +771,19 @@ static int compact_finished(struct zone *zone,
if (fatal_signal_pending(current))
return COMPACT_PARTIAL;
- /*
- * A full (order == -1) compaction run starts at the beginning and
- * end of a zone; it completes when the migrate and free scanner meet.
- * A partial (order > 0) compaction can start with the free scanner
- * at a random point in the zone, and may have to restart.
- */
+ /* Compaction run completes if the migrate and free scanner meet */
if (cc->free_pfn <= cc->migrate_pfn) {
- if (cc->order > 0 && !cc->wrapped) {
- /* We started partway through; restart at the end. */
- unsigned long free_pfn = start_free_pfn(zone);
- zone->compact_cached_free_pfn = free_pfn;
- cc->free_pfn = free_pfn;
- cc->wrapped = 1;
- return COMPACT_CONTINUE;
- }
- return COMPACT_COMPLETE;
- }
+ /*
+ * Mark that the PG_migrate_skip information should be cleared
+ * by kswapd when it goes to sleep. kswapd does not set the
+ * flag itself as the decision to be clear should be directly
+ * based on an allocation request.
+ */
+ if (!current_is_kswapd())
+ zone->compact_blockskip_flush = true;
- /* We wrapped around and ended up where we started. */
- if (cc->wrapped && cc->free_pfn <= cc->start_free_pfn)
return COMPACT_COMPLETE;
+ }
/*
* order == -1 is expected when compacting via
@@ -754,6 +866,8 @@ unsigned long compaction_suitable(struct zone *zone, int order)
static int compact_zone(struct zone *zone, struct compact_control *cc)
{
int ret;
+ unsigned long start_pfn = zone->zone_start_pfn;
+ unsigned long end_pfn = zone->zone_start_pfn + zone->spanned_pages;
ret = compaction_suitable(zone, cc->order);
switch (ret) {
@@ -766,17 +880,29 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
;
}
- /* Setup to move all movable pages to the end of the zone */
- cc->migrate_pfn = zone->zone_start_pfn;
-
- if (cc->order > 0) {
- /* Incremental compaction. Start where the last one stopped. */
- cc->free_pfn = zone->compact_cached_free_pfn;
- cc->start_free_pfn = cc->free_pfn;
- } else {
- /* Order == -1 starts at the end of the zone. */
- cc->free_pfn = start_free_pfn(zone);
+ /*
+ * Setup to move all movable pages to the end of the zone. Used cached
+ * information on where the scanners should start but check that it
+ * is initialised by ensuring the values are within zone boundaries.
+ */
+ cc->migrate_pfn = zone->compact_cached_migrate_pfn;
+ cc->free_pfn = zone->compact_cached_free_pfn;
+ if (cc->free_pfn < start_pfn || cc->free_pfn > end_pfn) {
+ cc->free_pfn = end_pfn & ~(pageblock_nr_pages-1);
+ zone->compact_cached_free_pfn = cc->free_pfn;
}
+ if (cc->migrate_pfn < start_pfn || cc->migrate_pfn > end_pfn) {
+ cc->migrate_pfn = start_pfn;
+ zone->compact_cached_migrate_pfn = cc->migrate_pfn;
+ }
+
+ /*
+ * Clear pageblock skip if there were failures recently and compaction
+ * is about to be retried after being deferred. kswapd does not do
+ * this reset as it'll reset the cached information when going to sleep.
+ */
+ if (compaction_restarting(zone, cc->order) && !current_is_kswapd())
+ __reset_isolation_suitable(zone);
migrate_prep_local();
@@ -787,6 +913,8 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
switch (isolate_migratepages(zone, cc)) {
case ISOLATE_ABORT:
ret = COMPACT_PARTIAL;
+ putback_lru_pages(&cc->migratepages);
+ cc->nr_migratepages = 0;
goto out;
case ISOLATE_NONE:
continue;
@@ -831,6 +959,7 @@ static unsigned long compact_zone_order(struct zone *zone,
int order, gfp_t gfp_mask,
bool sync, bool *contended)
{
+ unsigned long ret;
struct compact_control cc = {
.nr_freepages = 0,
.nr_migratepages = 0,
@@ -838,12 +967,17 @@ static unsigned long compact_zone_order(struct zone *zone,
.migratetype = allocflags_to_migratetype(gfp_mask),
.zone = zone,
.sync = sync,
- .contended = contended,
};
INIT_LIST_HEAD(&cc.freepages);
INIT_LIST_HEAD(&cc.migratepages);
- return compact_zone(zone, &cc);
+ ret = compact_zone(zone, &cc);
+
+ VM_BUG_ON(!list_empty(&cc.freepages));
+ VM_BUG_ON(!list_empty(&cc.migratepages));
+
+ *contended = cc.contended;
+ return ret;
}
int sysctl_extfrag_threshold = 500;
@@ -855,6 +989,8 @@ int sysctl_extfrag_threshold = 500;
* @gfp_mask: The GFP mask of the current allocation
* @nodemask: The allowed nodes to allocate from
* @sync: Whether migration is synchronous or not
+ * @contended: Return value that is true if compaction was aborted due to lock contention
+ * @page: Optionally capture a free page of the requested order during compaction
*
* This is the main entry point for direct page compaction.
*/
diff --git a/mm/internal.h b/mm/internal.h
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -118,23 +118,23 @@ struct compact_control {
unsigned long nr_freepages; /* Number of isolated free pages */
unsigned long nr_migratepages; /* Number of pages to migrate */
unsigned long free_pfn; /* isolate_freepages search base */
- unsigned long start_free_pfn; /* where we started the search */
unsigned long migrate_pfn; /* isolate_migratepages search base */
bool sync; /* Synchronous migration */
- bool wrapped; /* Order > 0 compactions are
- incremental, once free_pfn
- and migrate_pfn meet, we restart
- from the top of the zone;
- remember we wrapped around. */
+ bool ignore_skip_hint; /* Scan blocks even if marked skip */
+ bool finished_update_free; /* True when the zone cached pfns are
+ * no longer being updated
+ */
+ bool finished_update_migrate;
int order; /* order a direct compactor needs */
int migratetype; /* MOVABLE, RECLAIMABLE etc */
struct zone *zone;
- bool *contended; /* True if a lock was contended */
+ bool contended; /* True if a lock was contended */
};
unsigned long
-isolate_freepages_range(unsigned long start_pfn, unsigned long end_pfn);
+isolate_freepages_range(struct compact_control *cc,
+ unsigned long start_pfn, unsigned long end_pfn);
unsigned long
isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
unsigned long low_pfn, unsigned long end_pfn);
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -2131,6 +2131,7 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order,
alloc_flags & ~ALLOC_NO_WATERMARKS,
preferred_zone, migratetype);
if (page) {
+ preferred_zone->compact_blockskip_flush = false;
preferred_zone->compact_considered = 0;
preferred_zone->compact_defer_shift = 0;
if (order >= preferred_zone->compact_order_failed)
@@ -4438,11 +4439,6 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat,
zone->spanned_pages = size;
zone->present_pages = realsize;
-#if defined CONFIG_COMPACTION || defined CONFIG_CMA
- zone->compact_cached_free_pfn = zone->zone_start_pfn +
- zone->spanned_pages;
- zone->compact_cached_free_pfn &= ~(pageblock_nr_pages-1);
-#endif
#ifdef CONFIG_NUMA
zone->node = nid;
zone->min_unmapped_pages = (realsize*sysctl_min_unmapped_ratio)
@@ -5632,7 +5628,8 @@ __alloc_contig_migrate_alloc(struct page *page, unsigned long private,
}
/* [start, end) must belong to a single zone. */
-static int __alloc_contig_migrate_range(unsigned long start, unsigned long end)
+static int __alloc_contig_migrate_range(struct compact_control *cc,
+ unsigned long start, unsigned long end)
{
/* This function is based on compact_zone() from compaction.c. */
@@ -5640,25 +5637,17 @@ static int __alloc_contig_migrate_range(unsigned long start, unsigned long end)
unsigned int tries = 0;
int ret = 0;
- struct compact_control cc = {
- .nr_migratepages = 0,
- .order = -1,
- .zone = page_zone(pfn_to_page(start)),
- .sync = true,
- };
- INIT_LIST_HEAD(&cc.migratepages);
-
migrate_prep_local();
- while (pfn < end || !list_empty(&cc.migratepages)) {
+ while (pfn < end || !list_empty(&cc->migratepages)) {
if (fatal_signal_pending(current)) {
ret = -EINTR;
break;
}
- if (list_empty(&cc.migratepages)) {
- cc.nr_migratepages = 0;
- pfn = isolate_migratepages_range(cc.zone, &cc,
+ if (list_empty(&cc->migratepages)) {
+ cc->nr_migratepages = 0;
+ pfn = isolate_migratepages_range(cc->zone, cc,
pfn, end);
if (!pfn) {
ret = -EINTR;
@@ -5670,12 +5659,12 @@ static int __alloc_contig_migrate_range(unsigned long start, unsigned long end)
break;
}
- ret = migrate_pages(&cc.migratepages,
+ ret = migrate_pages(&cc->migratepages,
__alloc_contig_migrate_alloc,
0, false, MIGRATE_SYNC);
}
- putback_lru_pages(&cc.migratepages);
+ putback_lru_pages(&cc->migratepages);
return ret > 0 ? 0 : ret;
}
@@ -5754,6 +5743,15 @@ int alloc_contig_range(unsigned long start, unsigned long end,
unsigned long outer_start, outer_end;
int ret = 0, order;
+ struct compact_control cc = {
+ .nr_migratepages = 0,
+ .order = -1,
+ .zone = page_zone(pfn_to_page(start)),
+ .sync = true,
+ .ignore_skip_hint = true,
+ };
+ INIT_LIST_HEAD(&cc.migratepages);
+
/*
* What we do here is we mark all pageblocks in range as
* MIGRATE_ISOLATE. Because pageblock and max order pages may
@@ -5783,7 +5781,7 @@ int alloc_contig_range(unsigned long start, unsigned long end,
if (ret)
goto done;
- ret = __alloc_contig_migrate_range(start, end);
+ ret = __alloc_contig_migrate_range(&cc, start, end);
if (ret)
goto done;
@@ -5832,7 +5830,7 @@ int alloc_contig_range(unsigned long start, unsigned long end,
__reclaim_pages(zone, GFP_HIGHUSER_MOVABLE, end-start);
/* Grab isolated pages from freelists. */
- outer_end = isolate_freepages_range(outer_start, end);
+ outer_end = isolate_freepages_range(&cc, outer_start, end);
if (!outer_end) {
ret = -EBUSY;
goto done;
diff --git a/mm/vmscan.c b/mm/vmscan.c
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -2839,6 +2839,14 @@ static void kswapd_try_to_sleep(pg_data_t *pgdat, int order, int classzone_idx)
*/
set_pgdat_percpu_threshold(pgdat, calculate_normal_threshold);
+ /*
+ * Compaction records what page blocks it recently failed to
+ * isolate pages from and skips them in the future scanning.
+ * When kswapd is going to sleep, it is reasonable to assume
+ * that pages and compaction may succeed so reset the cache.
+ */
+ reset_isolation_suitable(pgdat);
+
if (!kthread_should_stop())
schedule();
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [3.6 regression?] THP + migration/compaction livelock (I think)
2012-11-14 21:50 ` David Rientjes
@ 2012-11-15 1:14 ` Marc Duponcheel
2012-11-17 0:18 ` Marc Duponcheel
0 siblings, 1 reply; 16+ messages in thread
From: Marc Duponcheel @ 2012-11-15 1:14 UTC (permalink / raw)
To: David Rientjes
Cc: Mel Gorman, Andy Lutomirski, linux-kernel, linux-mm, Marc Duponcheel
Hi David
Thanks for the changeset
I will test 3.6.6 without&with this weekend.
Have a nice day
On 2012 Nov 14, #David Rientjes wrote:
> On Wed, 14 Nov 2012, Marc Duponcheel wrote:
>
> > Hi all
> >
> > If someone can provide the patches (or learn me how to get them with
> > git (I apologise to not be git savy)) then, this weekend, I can apply
> > them to 3.6.6 and compare before/after to check if they fix #49361.
> >
>
> I've backported all the commits that Mel quoted to 3.6.6 and appended them
> to this email as one big patch. It should apply cleanly to your kernel.
>
> Now we are only missing these commits that weren't quoted:
>
> - 1fb3f8ca0e92 ("mm: compaction: capture a suitable high-order page
> immediately when it is made available"), and
>
> - 83fde0f22872 ("mm: vmscan: scale number of pages reclaimed by
> reclaim/compaction based on failures").
>
> Since your regression is easily reproducible, would it be possible to try
> to reproduce the issue FIRST with 3.6.6 and, if still present as it was in
> 3.6.2, then try reproducing it with the appended patch?
>
> You earlier reported that khugepaged was taking the second-most cpu time
> when this was happening, which initially pointed you to thp, so presumably
> this isn't a kswapd issue running at 100%. If both 3.6.6 kernels fail
> (the one with and without the following patch), would it be possible to
> try Mel's suggestion of patching with
>
> - https://lkml.org/lkml/2012/11/5/308 +
> https://lkml.org/lkml/2012/11/12/113
>
> to see if it helps and, if not, reverting the latter and trying
>
> - https://lkml.org/lkml/2012/11/5/308 +
> https://lkml.org/lkml/2012/11/12/151
>
> as the final test? This will certainly help us to find out what needs to
> be backported to 3.6 stable to prevent this issue for other users.
>
> Thanks!
> ---
> include/linux/compaction.h | 15 ++
> include/linux/mmzone.h | 6 +-
> include/linux/pageblock-flags.h | 19 +-
> mm/compaction.c | 450 +++++++++++++++++++++++++--------------
> mm/internal.h | 16 +-
> mm/page_alloc.c | 42 ++--
> mm/vmscan.c | 8 +
> 7 files changed, 366 insertions(+), 190 deletions(-)
>
> diff --git a/include/linux/compaction.h b/include/linux/compaction.h
> --- a/include/linux/compaction.h
> +++ b/include/linux/compaction.h
> @@ -24,6 +24,7 @@ extern unsigned long try_to_compact_pages(struct zonelist *zonelist,
> int order, gfp_t gfp_mask, nodemask_t *mask,
> bool sync, bool *contended);
> extern int compact_pgdat(pg_data_t *pgdat, int order);
> +extern void reset_isolation_suitable(pg_data_t *pgdat);
> extern unsigned long compaction_suitable(struct zone *zone, int order);
>
> /* Do not skip compaction more than 64 times */
> @@ -61,6 +62,16 @@ static inline bool compaction_deferred(struct zone *zone, int order)
> return zone->compact_considered < defer_limit;
> }
>
> +/* Returns true if restarting compaction after many failures */
> +static inline bool compaction_restarting(struct zone *zone, int order)
> +{
> + if (order < zone->compact_order_failed)
> + return false;
> +
> + return zone->compact_defer_shift == COMPACT_MAX_DEFER_SHIFT &&
> + zone->compact_considered >= 1UL << zone->compact_defer_shift;
> +}
> +
> #else
> static inline unsigned long try_to_compact_pages(struct zonelist *zonelist,
> int order, gfp_t gfp_mask, nodemask_t *nodemask,
> @@ -74,6 +85,10 @@ static inline int compact_pgdat(pg_data_t *pgdat, int order)
> return COMPACT_CONTINUE;
> }
>
> +static inline void reset_isolation_suitable(pg_data_t *pgdat)
> +{
> +}
> +
> static inline unsigned long compaction_suitable(struct zone *zone, int order)
> {
> return COMPACT_SKIPPED;
> diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
> --- a/include/linux/mmzone.h
> +++ b/include/linux/mmzone.h
> @@ -369,8 +369,12 @@ struct zone {
> spinlock_t lock;
> int all_unreclaimable; /* All pages pinned */
> #if defined CONFIG_COMPACTION || defined CONFIG_CMA
> - /* pfn where the last incremental compaction isolated free pages */
> + /* Set to true when the PG_migrate_skip bits should be cleared */
> + bool compact_blockskip_flush;
> +
> + /* pfns where compaction scanners should start */
> unsigned long compact_cached_free_pfn;
> + unsigned long compact_cached_migrate_pfn;
> #endif
> #ifdef CONFIG_MEMORY_HOTPLUG
> /* see spanned/present_pages for more description */
> diff --git a/include/linux/pageblock-flags.h b/include/linux/pageblock-flags.h
> --- a/include/linux/pageblock-flags.h
> +++ b/include/linux/pageblock-flags.h
> @@ -30,6 +30,9 @@ enum pageblock_bits {
> PB_migrate,
> PB_migrate_end = PB_migrate + 3 - 1,
> /* 3 bits required for migrate types */
> +#ifdef CONFIG_COMPACTION
> + PB_migrate_skip,/* If set the block is skipped by compaction */
> +#endif /* CONFIG_COMPACTION */
> NR_PAGEBLOCK_BITS
> };
>
> @@ -65,10 +68,22 @@ unsigned long get_pageblock_flags_group(struct page *page,
> void set_pageblock_flags_group(struct page *page, unsigned long flags,
> int start_bitidx, int end_bitidx);
>
> +#ifdef CONFIG_COMPACTION
> +#define get_pageblock_skip(page) \
> + get_pageblock_flags_group(page, PB_migrate_skip, \
> + PB_migrate_skip + 1)
> +#define clear_pageblock_skip(page) \
> + set_pageblock_flags_group(page, 0, PB_migrate_skip, \
> + PB_migrate_skip + 1)
> +#define set_pageblock_skip(page) \
> + set_pageblock_flags_group(page, 1, PB_migrate_skip, \
> + PB_migrate_skip + 1)
> +#endif /* CONFIG_COMPACTION */
> +
> #define get_pageblock_flags(page) \
> - get_pageblock_flags_group(page, 0, NR_PAGEBLOCK_BITS-1)
> + get_pageblock_flags_group(page, 0, PB_migrate_end)
> #define set_pageblock_flags(page, flags) \
> set_pageblock_flags_group(page, flags, \
> - 0, NR_PAGEBLOCK_BITS-1)
> + 0, PB_migrate_end)
>
> #endif /* PAGEBLOCK_FLAGS_H */
> diff --git a/mm/compaction.c b/mm/compaction.c
> --- a/mm/compaction.c
> +++ b/mm/compaction.c
> @@ -50,6 +50,111 @@ static inline bool migrate_async_suitable(int migratetype)
> return is_migrate_cma(migratetype) || migratetype == MIGRATE_MOVABLE;
> }
>
> +#ifdef CONFIG_COMPACTION
> +/* Returns true if the pageblock should be scanned for pages to isolate. */
> +static inline bool isolation_suitable(struct compact_control *cc,
> + struct page *page)
> +{
> + if (cc->ignore_skip_hint)
> + return true;
> +
> + return !get_pageblock_skip(page);
> +}
> +
> +/*
> + * This function is called to clear all cached information on pageblocks that
> + * should be skipped for page isolation when the migrate and free page scanner
> + * meet.
> + */
> +static void __reset_isolation_suitable(struct zone *zone)
> +{
> + unsigned long start_pfn = zone->zone_start_pfn;
> + unsigned long end_pfn = zone->zone_start_pfn + zone->spanned_pages;
> + unsigned long pfn;
> +
> + zone->compact_cached_migrate_pfn = start_pfn;
> + zone->compact_cached_free_pfn = end_pfn;
> + zone->compact_blockskip_flush = false;
> +
> + /* Walk the zone and mark every pageblock as suitable for isolation */
> + for (pfn = start_pfn; pfn < end_pfn; pfn += pageblock_nr_pages) {
> + struct page *page;
> +
> + cond_resched();
> +
> + if (!pfn_valid(pfn))
> + continue;
> +
> + page = pfn_to_page(pfn);
> + if (zone != page_zone(page))
> + continue;
> +
> + clear_pageblock_skip(page);
> + }
> +}
> +
> +void reset_isolation_suitable(pg_data_t *pgdat)
> +{
> + int zoneid;
> +
> + for (zoneid = 0; zoneid < MAX_NR_ZONES; zoneid++) {
> + struct zone *zone = &pgdat->node_zones[zoneid];
> + if (!populated_zone(zone))
> + continue;
> +
> + /* Only flush if a full compaction finished recently */
> + if (zone->compact_blockskip_flush)
> + __reset_isolation_suitable(zone);
> + }
> +}
> +
> +/*
> + * If no pages were isolated then mark this pageblock to be skipped in the
> + * future. The information is later cleared by __reset_isolation_suitable().
> + */
> +static void update_pageblock_skip(struct compact_control *cc,
> + struct page *page, unsigned long nr_isolated,
> + bool migrate_scanner)
> +{
> + struct zone *zone = cc->zone;
> + if (!page)
> + return;
> +
> + if (!nr_isolated) {
> + unsigned long pfn = page_to_pfn(page);
> + set_pageblock_skip(page);
> +
> + /* Update where compaction should restart */
> + if (migrate_scanner) {
> + if (!cc->finished_update_migrate &&
> + pfn > zone->compact_cached_migrate_pfn)
> + zone->compact_cached_migrate_pfn = pfn;
> + } else {
> + if (!cc->finished_update_free &&
> + pfn < zone->compact_cached_free_pfn)
> + zone->compact_cached_free_pfn = pfn;
> + }
> + }
> +}
> +#else
> +static inline bool isolation_suitable(struct compact_control *cc,
> + struct page *page)
> +{
> + return true;
> +}
> +
> +static void update_pageblock_skip(struct compact_control *cc,
> + struct page *page, unsigned long nr_isolated,
> + bool migrate_scanner)
> +{
> +}
> +#endif /* CONFIG_COMPACTION */
> +
> +static inline bool should_release_lock(spinlock_t *lock)
> +{
> + return need_resched() || spin_is_contended(lock);
> +}
> +
> /*
> * Compaction requires the taking of some coarse locks that are potentially
> * very heavily contended. Check if the process needs to be scheduled or
> @@ -62,7 +167,7 @@ static inline bool migrate_async_suitable(int migratetype)
> static bool compact_checklock_irqsave(spinlock_t *lock, unsigned long *flags,
> bool locked, struct compact_control *cc)
> {
> - if (need_resched() || spin_is_contended(lock)) {
> + if (should_release_lock(lock)) {
> if (locked) {
> spin_unlock_irqrestore(lock, *flags);
> locked = false;
> @@ -70,14 +175,11 @@ static bool compact_checklock_irqsave(spinlock_t *lock, unsigned long *flags,
>
> /* async aborts if taking too long or contended */
> if (!cc->sync) {
> - if (cc->contended)
> - *cc->contended = true;
> + cc->contended = true;
> return false;
> }
>
> cond_resched();
> - if (fatal_signal_pending(current))
> - return false;
> }
>
> if (!locked)
> @@ -91,44 +193,85 @@ static inline bool compact_trylock_irqsave(spinlock_t *lock,
> return compact_checklock_irqsave(lock, flags, false, cc);
> }
>
> +/* Returns true if the page is within a block suitable for migration to */
> +static bool suitable_migration_target(struct page *page)
> +{
> + int migratetype = get_pageblock_migratetype(page);
> +
> + /* Don't interfere with memory hot-remove or the min_free_kbytes blocks */
> + if (migratetype == MIGRATE_ISOLATE || migratetype == MIGRATE_RESERVE)
> + return false;
> +
> + /* If the page is a large free page, then allow migration */
> + if (PageBuddy(page) && page_order(page) >= pageblock_order)
> + return true;
> +
> + /* If the block is MIGRATE_MOVABLE or MIGRATE_CMA, allow migration */
> + if (migrate_async_suitable(migratetype))
> + return true;
> +
> + /* Otherwise skip the block */
> + return false;
> +}
> +
> /*
> * Isolate free pages onto a private freelist. Caller must hold zone->lock.
> * If @strict is true, will abort returning 0 on any invalid PFNs or non-free
> * pages inside of the pageblock (even though it may still end up isolating
> * some pages).
> */
> -static unsigned long isolate_freepages_block(unsigned long blockpfn,
> +static unsigned long isolate_freepages_block(struct compact_control *cc,
> + unsigned long blockpfn,
> unsigned long end_pfn,
> struct list_head *freelist,
> bool strict)
> {
> int nr_scanned = 0, total_isolated = 0;
> - struct page *cursor;
> + struct page *cursor, *valid_page = NULL;
> + unsigned long nr_strict_required = end_pfn - blockpfn;
> + unsigned long flags;
> + bool locked = false;
>
> cursor = pfn_to_page(blockpfn);
>
> - /* Isolate free pages. This assumes the block is valid */
> + /* Isolate free pages. */
> for (; blockpfn < end_pfn; blockpfn++, cursor++) {
> int isolated, i;
> struct page *page = cursor;
>
> - if (!pfn_valid_within(blockpfn)) {
> - if (strict)
> - return 0;
> - continue;
> - }
> nr_scanned++;
> + if (!pfn_valid_within(blockpfn))
> + continue;
> + if (!valid_page)
> + valid_page = page;
> + if (!PageBuddy(page))
> + continue;
> +
> + /*
> + * The zone lock must be held to isolate freepages.
> + * Unfortunately this is a very coarse lock and can be
> + * heavily contended if there are parallel allocations
> + * or parallel compactions. For async compaction do not
> + * spin on the lock and we acquire the lock as late as
> + * possible.
> + */
> + locked = compact_checklock_irqsave(&cc->zone->lock, &flags,
> + locked, cc);
> + if (!locked)
> + break;
> +
> + /* Recheck this is a suitable migration target under lock */
> + if (!strict && !suitable_migration_target(page))
> + break;
>
> - if (!PageBuddy(page)) {
> - if (strict)
> - return 0;
> + /* Recheck this is a buddy page under lock */
> + if (!PageBuddy(page))
> continue;
> - }
>
> /* Found a free page, break it into order-0 pages */
> isolated = split_free_page(page);
> if (!isolated && strict)
> - return 0;
> + break;
> total_isolated += isolated;
> for (i = 0; i < isolated; i++) {
> list_add(&page->lru, freelist);
> @@ -143,6 +286,22 @@ static unsigned long isolate_freepages_block(unsigned long blockpfn,
> }
>
> trace_mm_compaction_isolate_freepages(nr_scanned, total_isolated);
> +
> + /*
> + * If strict isolation is requested by CMA then check that all the
> + * pages requested were isolated. If there were any failures, 0 is
> + * returned and CMA will fail.
> + */
> + if (strict && nr_strict_required > total_isolated)
> + total_isolated = 0;
> +
> + if (locked)
> + spin_unlock_irqrestore(&cc->zone->lock, flags);
> +
> + /* Update the pageblock-skip if the whole pageblock was scanned */
> + if (blockpfn == end_pfn)
> + update_pageblock_skip(cc, valid_page, total_isolated, false);
> +
> return total_isolated;
> }
>
> @@ -160,17 +319,14 @@ static unsigned long isolate_freepages_block(unsigned long blockpfn,
> * a free page).
> */
> unsigned long
> -isolate_freepages_range(unsigned long start_pfn, unsigned long end_pfn)
> +isolate_freepages_range(struct compact_control *cc,
> + unsigned long start_pfn, unsigned long end_pfn)
> {
> - unsigned long isolated, pfn, block_end_pfn, flags;
> - struct zone *zone = NULL;
> + unsigned long isolated, pfn, block_end_pfn;
> LIST_HEAD(freelist);
>
> - if (pfn_valid(start_pfn))
> - zone = page_zone(pfn_to_page(start_pfn));
> -
> for (pfn = start_pfn; pfn < end_pfn; pfn += isolated) {
> - if (!pfn_valid(pfn) || zone != page_zone(pfn_to_page(pfn)))
> + if (!pfn_valid(pfn) || cc->zone != page_zone(pfn_to_page(pfn)))
> break;
>
> /*
> @@ -180,10 +336,8 @@ isolate_freepages_range(unsigned long start_pfn, unsigned long end_pfn)
> block_end_pfn = ALIGN(pfn + 1, pageblock_nr_pages);
> block_end_pfn = min(block_end_pfn, end_pfn);
>
> - spin_lock_irqsave(&zone->lock, flags);
> - isolated = isolate_freepages_block(pfn, block_end_pfn,
> + isolated = isolate_freepages_block(cc, pfn, block_end_pfn,
> &freelist, true);
> - spin_unlock_irqrestore(&zone->lock, flags);
>
> /*
> * In strict mode, isolate_freepages_block() returns 0 if
> @@ -276,7 +430,8 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
> isolate_mode_t mode = 0;
> struct lruvec *lruvec;
> unsigned long flags;
> - bool locked;
> + bool locked = false;
> + struct page *page = NULL, *valid_page = NULL;
>
> /*
> * Ensure that there are not too many pages isolated from the LRU
> @@ -296,23 +451,15 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
>
> /* Time to isolate some pages for migration */
> cond_resched();
> - spin_lock_irqsave(&zone->lru_lock, flags);
> - locked = true;
> for (; low_pfn < end_pfn; low_pfn++) {
> - struct page *page;
> -
> /* give a chance to irqs before checking need_resched() */
> - if (!((low_pfn+1) % SWAP_CLUSTER_MAX)) {
> - spin_unlock_irqrestore(&zone->lru_lock, flags);
> - locked = false;
> + if (locked && !((low_pfn+1) % SWAP_CLUSTER_MAX)) {
> + if (should_release_lock(&zone->lru_lock)) {
> + spin_unlock_irqrestore(&zone->lru_lock, flags);
> + locked = false;
> + }
> }
>
> - /* Check if it is ok to still hold the lock */
> - locked = compact_checklock_irqsave(&zone->lru_lock, &flags,
> - locked, cc);
> - if (!locked)
> - break;
> -
> /*
> * migrate_pfn does not necessarily start aligned to a
> * pageblock. Ensure that pfn_valid is called when moving
> @@ -340,6 +487,14 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
> if (page_zone(page) != zone)
> continue;
>
> + if (!valid_page)
> + valid_page = page;
> +
> + /* If isolation recently failed, do not retry */
> + pageblock_nr = low_pfn >> pageblock_order;
> + if (!isolation_suitable(cc, page))
> + goto next_pageblock;
> +
> /* Skip if free */
> if (PageBuddy(page))
> continue;
> @@ -349,24 +504,43 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
> * migration is optimistic to see if the minimum amount of work
> * satisfies the allocation
> */
> - pageblock_nr = low_pfn >> pageblock_order;
> if (!cc->sync && last_pageblock_nr != pageblock_nr &&
> !migrate_async_suitable(get_pageblock_migratetype(page))) {
> - low_pfn += pageblock_nr_pages;
> - low_pfn = ALIGN(low_pfn, pageblock_nr_pages) - 1;
> - last_pageblock_nr = pageblock_nr;
> - continue;
> + cc->finished_update_migrate = true;
> + goto next_pageblock;
> }
>
> + /* Check may be lockless but that's ok as we recheck later */
> if (!PageLRU(page))
> continue;
>
> /*
> - * PageLRU is set, and lru_lock excludes isolation,
> - * splitting and collapsing (collapsing has already
> - * happened if PageLRU is set).
> + * PageLRU is set. lru_lock normally excludes isolation
> + * splitting and collapsing (collapsing has already happened
> + * if PageLRU is set) but the lock is not necessarily taken
> + * here and it is wasteful to take it just to check transhuge.
> + * Check TransHuge without lock and skip the whole pageblock if
> + * it's either a transhuge or hugetlbfs page, as calling
> + * compound_order() without preventing THP from splitting the
> + * page underneath us may return surprising results.
> */
> if (PageTransHuge(page)) {
> + if (!locked)
> + goto next_pageblock;
> + low_pfn += (1 << compound_order(page)) - 1;
> + continue;
> + }
> +
> + /* Check if it is ok to still hold the lock */
> + locked = compact_checklock_irqsave(&zone->lru_lock, &flags,
> + locked, cc);
> + if (!locked || fatal_signal_pending(current))
> + break;
> +
> + /* Recheck PageLRU and PageTransHuge under lock */
> + if (!PageLRU(page))
> + continue;
> + if (PageTransHuge(page)) {
> low_pfn += (1 << compound_order(page)) - 1;
> continue;
> }
> @@ -383,6 +557,7 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
> VM_BUG_ON(PageTransCompound(page));
>
> /* Successfully isolated */
> + cc->finished_update_migrate = true;
> del_page_from_lru_list(page, lruvec, page_lru(page));
> list_add(&page->lru, migratelist);
> cc->nr_migratepages++;
> @@ -393,6 +568,13 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
> ++low_pfn;
> break;
> }
> +
> + continue;
> +
> +next_pageblock:
> + low_pfn += pageblock_nr_pages;
> + low_pfn = ALIGN(low_pfn, pageblock_nr_pages) - 1;
> + last_pageblock_nr = pageblock_nr;
> }
>
> acct_isolated(zone, locked, cc);
> @@ -400,6 +582,10 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
> if (locked)
> spin_unlock_irqrestore(&zone->lru_lock, flags);
>
> + /* Update the pageblock-skip if the whole pageblock was scanned */
> + if (low_pfn == end_pfn)
> + update_pageblock_skip(cc, valid_page, nr_isolated, true);
> +
> trace_mm_compaction_isolate_migratepages(nr_scanned, nr_isolated);
>
> return low_pfn;
> @@ -407,43 +593,6 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
>
> #endif /* CONFIG_COMPACTION || CONFIG_CMA */
> #ifdef CONFIG_COMPACTION
> -
> -/* Returns true if the page is within a block suitable for migration to */
> -static bool suitable_migration_target(struct page *page)
> -{
> -
> - int migratetype = get_pageblock_migratetype(page);
> -
> - /* Don't interfere with memory hot-remove or the min_free_kbytes blocks */
> - if (migratetype == MIGRATE_ISOLATE || migratetype == MIGRATE_RESERVE)
> - return false;
> -
> - /* If the page is a large free page, then allow migration */
> - if (PageBuddy(page) && page_order(page) >= pageblock_order)
> - return true;
> -
> - /* If the block is MIGRATE_MOVABLE or MIGRATE_CMA, allow migration */
> - if (migrate_async_suitable(migratetype))
> - return true;
> -
> - /* Otherwise skip the block */
> - return false;
> -}
> -
> -/*
> - * Returns the start pfn of the last page block in a zone. This is the starting
> - * point for full compaction of a zone. Compaction searches for free pages from
> - * the end of each zone, while isolate_freepages_block scans forward inside each
> - * page block.
> - */
> -static unsigned long start_free_pfn(struct zone *zone)
> -{
> - unsigned long free_pfn;
> - free_pfn = zone->zone_start_pfn + zone->spanned_pages;
> - free_pfn &= ~(pageblock_nr_pages-1);
> - return free_pfn;
> -}
> -
> /*
> * Based on information in the current compact_control, find blocks
> * suitable for isolating free pages from and then isolate them.
> @@ -453,7 +602,6 @@ static void isolate_freepages(struct zone *zone,
> {
> struct page *page;
> unsigned long high_pfn, low_pfn, pfn, zone_end_pfn, end_pfn;
> - unsigned long flags;
> int nr_freepages = cc->nr_freepages;
> struct list_head *freelist = &cc->freepages;
>
> @@ -501,30 +649,16 @@ static void isolate_freepages(struct zone *zone,
> if (!suitable_migration_target(page))
> continue;
>
> - /*
> - * Found a block suitable for isolating free pages from. Now
> - * we disabled interrupts, double check things are ok and
> - * isolate the pages. This is to minimise the time IRQs
> - * are disabled
> - */
> - isolated = 0;
> + /* If isolation recently failed, do not retry */
> + if (!isolation_suitable(cc, page))
> + continue;
>
> - /*
> - * The zone lock must be held to isolate freepages. This
> - * unfortunately this is a very coarse lock and can be
> - * heavily contended if there are parallel allocations
> - * or parallel compactions. For async compaction do not
> - * spin on the lock
> - */
> - if (!compact_trylock_irqsave(&zone->lock, &flags, cc))
> - break;
> - if (suitable_migration_target(page)) {
> - end_pfn = min(pfn + pageblock_nr_pages, zone_end_pfn);
> - isolated = isolate_freepages_block(pfn, end_pfn,
> - freelist, false);
> - nr_freepages += isolated;
> - }
> - spin_unlock_irqrestore(&zone->lock, flags);
> + /* Found a block suitable for isolating free pages from */
> + isolated = 0;
> + end_pfn = min(pfn + pageblock_nr_pages, zone_end_pfn);
> + isolated = isolate_freepages_block(cc, pfn, end_pfn,
> + freelist, false);
> + nr_freepages += isolated;
>
> /*
> * Record the highest PFN we isolated pages from. When next
> @@ -532,17 +666,8 @@ static void isolate_freepages(struct zone *zone,
> * page migration may have returned some pages to the allocator
> */
> if (isolated) {
> + cc->finished_update_free = true;
> high_pfn = max(high_pfn, pfn);
> -
> - /*
> - * If the free scanner has wrapped, update
> - * compact_cached_free_pfn to point to the highest
> - * pageblock with free pages. This reduces excessive
> - * scanning of full pageblocks near the end of the
> - * zone
> - */
> - if (cc->order > 0 && cc->wrapped)
> - zone->compact_cached_free_pfn = high_pfn;
> }
> }
>
> @@ -551,11 +676,6 @@ static void isolate_freepages(struct zone *zone,
>
> cc->free_pfn = high_pfn;
> cc->nr_freepages = nr_freepages;
> -
> - /* If compact_cached_free_pfn is reset then set it now */
> - if (cc->order > 0 && !cc->wrapped &&
> - zone->compact_cached_free_pfn == start_free_pfn(zone))
> - zone->compact_cached_free_pfn = high_pfn;
> }
>
> /*
> @@ -634,7 +754,7 @@ static isolate_migrate_t isolate_migratepages(struct zone *zone,
>
> /* Perform the isolation */
> low_pfn = isolate_migratepages_range(zone, cc, low_pfn, end_pfn);
> - if (!low_pfn)
> + if (!low_pfn || cc->contended)
> return ISOLATE_ABORT;
>
> cc->migrate_pfn = low_pfn;
> @@ -651,27 +771,19 @@ static int compact_finished(struct zone *zone,
> if (fatal_signal_pending(current))
> return COMPACT_PARTIAL;
>
> - /*
> - * A full (order == -1) compaction run starts at the beginning and
> - * end of a zone; it completes when the migrate and free scanner meet.
> - * A partial (order > 0) compaction can start with the free scanner
> - * at a random point in the zone, and may have to restart.
> - */
> + /* Compaction run completes if the migrate and free scanner meet */
> if (cc->free_pfn <= cc->migrate_pfn) {
> - if (cc->order > 0 && !cc->wrapped) {
> - /* We started partway through; restart at the end. */
> - unsigned long free_pfn = start_free_pfn(zone);
> - zone->compact_cached_free_pfn = free_pfn;
> - cc->free_pfn = free_pfn;
> - cc->wrapped = 1;
> - return COMPACT_CONTINUE;
> - }
> - return COMPACT_COMPLETE;
> - }
> + /*
> + * Mark that the PG_migrate_skip information should be cleared
> + * by kswapd when it goes to sleep. kswapd does not set the
> + * flag itself as the decision to be clear should be directly
> + * based on an allocation request.
> + */
> + if (!current_is_kswapd())
> + zone->compact_blockskip_flush = true;
>
> - /* We wrapped around and ended up where we started. */
> - if (cc->wrapped && cc->free_pfn <= cc->start_free_pfn)
> return COMPACT_COMPLETE;
> + }
>
> /*
> * order == -1 is expected when compacting via
> @@ -754,6 +866,8 @@ unsigned long compaction_suitable(struct zone *zone, int order)
> static int compact_zone(struct zone *zone, struct compact_control *cc)
> {
> int ret;
> + unsigned long start_pfn = zone->zone_start_pfn;
> + unsigned long end_pfn = zone->zone_start_pfn + zone->spanned_pages;
>
> ret = compaction_suitable(zone, cc->order);
> switch (ret) {
> @@ -766,17 +880,29 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
> ;
> }
>
> - /* Setup to move all movable pages to the end of the zone */
> - cc->migrate_pfn = zone->zone_start_pfn;
> -
> - if (cc->order > 0) {
> - /* Incremental compaction. Start where the last one stopped. */
> - cc->free_pfn = zone->compact_cached_free_pfn;
> - cc->start_free_pfn = cc->free_pfn;
> - } else {
> - /* Order == -1 starts at the end of the zone. */
> - cc->free_pfn = start_free_pfn(zone);
> + /*
> + * Setup to move all movable pages to the end of the zone. Used cached
> + * information on where the scanners should start but check that it
> + * is initialised by ensuring the values are within zone boundaries.
> + */
> + cc->migrate_pfn = zone->compact_cached_migrate_pfn;
> + cc->free_pfn = zone->compact_cached_free_pfn;
> + if (cc->free_pfn < start_pfn || cc->free_pfn > end_pfn) {
> + cc->free_pfn = end_pfn & ~(pageblock_nr_pages-1);
> + zone->compact_cached_free_pfn = cc->free_pfn;
> }
> + if (cc->migrate_pfn < start_pfn || cc->migrate_pfn > end_pfn) {
> + cc->migrate_pfn = start_pfn;
> + zone->compact_cached_migrate_pfn = cc->migrate_pfn;
> + }
> +
> + /*
> + * Clear pageblock skip if there were failures recently and compaction
> + * is about to be retried after being deferred. kswapd does not do
> + * this reset as it'll reset the cached information when going to sleep.
> + */
> + if (compaction_restarting(zone, cc->order) && !current_is_kswapd())
> + __reset_isolation_suitable(zone);
>
> migrate_prep_local();
>
> @@ -787,6 +913,8 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
> switch (isolate_migratepages(zone, cc)) {
> case ISOLATE_ABORT:
> ret = COMPACT_PARTIAL;
> + putback_lru_pages(&cc->migratepages);
> + cc->nr_migratepages = 0;
> goto out;
> case ISOLATE_NONE:
> continue;
> @@ -831,6 +959,7 @@ static unsigned long compact_zone_order(struct zone *zone,
> int order, gfp_t gfp_mask,
> bool sync, bool *contended)
> {
> + unsigned long ret;
> struct compact_control cc = {
> .nr_freepages = 0,
> .nr_migratepages = 0,
> @@ -838,12 +967,17 @@ static unsigned long compact_zone_order(struct zone *zone,
> .migratetype = allocflags_to_migratetype(gfp_mask),
> .zone = zone,
> .sync = sync,
> - .contended = contended,
> };
> INIT_LIST_HEAD(&cc.freepages);
> INIT_LIST_HEAD(&cc.migratepages);
>
> - return compact_zone(zone, &cc);
> + ret = compact_zone(zone, &cc);
> +
> + VM_BUG_ON(!list_empty(&cc.freepages));
> + VM_BUG_ON(!list_empty(&cc.migratepages));
> +
> + *contended = cc.contended;
> + return ret;
> }
>
> int sysctl_extfrag_threshold = 500;
> @@ -855,6 +989,8 @@ int sysctl_extfrag_threshold = 500;
> * @gfp_mask: The GFP mask of the current allocation
> * @nodemask: The allowed nodes to allocate from
> * @sync: Whether migration is synchronous or not
> + * @contended: Return value that is true if compaction was aborted due to lock contention
> + * @page: Optionally capture a free page of the requested order during compaction
> *
> * This is the main entry point for direct page compaction.
> */
> diff --git a/mm/internal.h b/mm/internal.h
> --- a/mm/internal.h
> +++ b/mm/internal.h
> @@ -118,23 +118,23 @@ struct compact_control {
> unsigned long nr_freepages; /* Number of isolated free pages */
> unsigned long nr_migratepages; /* Number of pages to migrate */
> unsigned long free_pfn; /* isolate_freepages search base */
> - unsigned long start_free_pfn; /* where we started the search */
> unsigned long migrate_pfn; /* isolate_migratepages search base */
> bool sync; /* Synchronous migration */
> - bool wrapped; /* Order > 0 compactions are
> - incremental, once free_pfn
> - and migrate_pfn meet, we restart
> - from the top of the zone;
> - remember we wrapped around. */
> + bool ignore_skip_hint; /* Scan blocks even if marked skip */
> + bool finished_update_free; /* True when the zone cached pfns are
> + * no longer being updated
> + */
> + bool finished_update_migrate;
>
> int order; /* order a direct compactor needs */
> int migratetype; /* MOVABLE, RECLAIMABLE etc */
> struct zone *zone;
> - bool *contended; /* True if a lock was contended */
> + bool contended; /* True if a lock was contended */
> };
>
> unsigned long
> -isolate_freepages_range(unsigned long start_pfn, unsigned long end_pfn);
> +isolate_freepages_range(struct compact_control *cc,
> + unsigned long start_pfn, unsigned long end_pfn);
> unsigned long
> isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
> unsigned long low_pfn, unsigned long end_pfn);
> diff --git a/mm/page_alloc.c b/mm/page_alloc.c
> --- a/mm/page_alloc.c
> +++ b/mm/page_alloc.c
> @@ -2131,6 +2131,7 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order,
> alloc_flags & ~ALLOC_NO_WATERMARKS,
> preferred_zone, migratetype);
> if (page) {
> + preferred_zone->compact_blockskip_flush = false;
> preferred_zone->compact_considered = 0;
> preferred_zone->compact_defer_shift = 0;
> if (order >= preferred_zone->compact_order_failed)
> @@ -4438,11 +4439,6 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat,
>
> zone->spanned_pages = size;
> zone->present_pages = realsize;
> -#if defined CONFIG_COMPACTION || defined CONFIG_CMA
> - zone->compact_cached_free_pfn = zone->zone_start_pfn +
> - zone->spanned_pages;
> - zone->compact_cached_free_pfn &= ~(pageblock_nr_pages-1);
> -#endif
> #ifdef CONFIG_NUMA
> zone->node = nid;
> zone->min_unmapped_pages = (realsize*sysctl_min_unmapped_ratio)
> @@ -5632,7 +5628,8 @@ __alloc_contig_migrate_alloc(struct page *page, unsigned long private,
> }
>
> /* [start, end) must belong to a single zone. */
> -static int __alloc_contig_migrate_range(unsigned long start, unsigned long end)
> +static int __alloc_contig_migrate_range(struct compact_control *cc,
> + unsigned long start, unsigned long end)
> {
> /* This function is based on compact_zone() from compaction.c. */
>
> @@ -5640,25 +5637,17 @@ static int __alloc_contig_migrate_range(unsigned long start, unsigned long end)
> unsigned int tries = 0;
> int ret = 0;
>
> - struct compact_control cc = {
> - .nr_migratepages = 0,
> - .order = -1,
> - .zone = page_zone(pfn_to_page(start)),
> - .sync = true,
> - };
> - INIT_LIST_HEAD(&cc.migratepages);
> -
> migrate_prep_local();
>
> - while (pfn < end || !list_empty(&cc.migratepages)) {
> + while (pfn < end || !list_empty(&cc->migratepages)) {
> if (fatal_signal_pending(current)) {
> ret = -EINTR;
> break;
> }
>
> - if (list_empty(&cc.migratepages)) {
> - cc.nr_migratepages = 0;
> - pfn = isolate_migratepages_range(cc.zone, &cc,
> + if (list_empty(&cc->migratepages)) {
> + cc->nr_migratepages = 0;
> + pfn = isolate_migratepages_range(cc->zone, cc,
> pfn, end);
> if (!pfn) {
> ret = -EINTR;
> @@ -5670,12 +5659,12 @@ static int __alloc_contig_migrate_range(unsigned long start, unsigned long end)
> break;
> }
>
> - ret = migrate_pages(&cc.migratepages,
> + ret = migrate_pages(&cc->migratepages,
> __alloc_contig_migrate_alloc,
> 0, false, MIGRATE_SYNC);
> }
>
> - putback_lru_pages(&cc.migratepages);
> + putback_lru_pages(&cc->migratepages);
> return ret > 0 ? 0 : ret;
> }
>
> @@ -5754,6 +5743,15 @@ int alloc_contig_range(unsigned long start, unsigned long end,
> unsigned long outer_start, outer_end;
> int ret = 0, order;
>
> + struct compact_control cc = {
> + .nr_migratepages = 0,
> + .order = -1,
> + .zone = page_zone(pfn_to_page(start)),
> + .sync = true,
> + .ignore_skip_hint = true,
> + };
> + INIT_LIST_HEAD(&cc.migratepages);
> +
> /*
> * What we do here is we mark all pageblocks in range as
> * MIGRATE_ISOLATE. Because pageblock and max order pages may
> @@ -5783,7 +5781,7 @@ int alloc_contig_range(unsigned long start, unsigned long end,
> if (ret)
> goto done;
>
> - ret = __alloc_contig_migrate_range(start, end);
> + ret = __alloc_contig_migrate_range(&cc, start, end);
> if (ret)
> goto done;
>
> @@ -5832,7 +5830,7 @@ int alloc_contig_range(unsigned long start, unsigned long end,
> __reclaim_pages(zone, GFP_HIGHUSER_MOVABLE, end-start);
>
> /* Grab isolated pages from freelists. */
> - outer_end = isolate_freepages_range(outer_start, end);
> + outer_end = isolate_freepages_range(&cc, outer_start, end);
> if (!outer_end) {
> ret = -EBUSY;
> goto done;
> diff --git a/mm/vmscan.c b/mm/vmscan.c
> --- a/mm/vmscan.c
> +++ b/mm/vmscan.c
> @@ -2839,6 +2839,14 @@ static void kswapd_try_to_sleep(pg_data_t *pgdat, int order, int classzone_idx)
> */
> set_pgdat_percpu_threshold(pgdat, calculate_normal_threshold);
>
> + /*
> + * Compaction records what page blocks it recently failed to
> + * isolate pages from and skips them in the future scanning.
> + * When kswapd is going to sleep, it is reasonable to assume
> + * that pages and compaction may succeed so reset the cache.
> + */
> + reset_isolation_suitable(pgdat);
> +
> if (!kthread_should_stop())
> schedule();
>
>
--
--
Marc Duponcheel
Velodroomstraat 74 - 2600 Berchem - Belgium
+32 (0)478 68.10.91 - marc@offline.be
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [3.6 regression?] THP + migration/compaction livelock (I think)
2012-11-15 1:14 ` Marc Duponcheel
@ 2012-11-17 0:18 ` Marc Duponcheel
2012-11-18 22:55 ` David Rientjes
0 siblings, 1 reply; 16+ messages in thread
From: Marc Duponcheel @ 2012-11-17 0:18 UTC (permalink / raw)
To: David Rientjes
Cc: Mel Gorman, Andy Lutomirski, linux-kernel, linux-mm, Marc Duponcheel
[-- Attachment #1: Type: text/plain, Size: 1433 bytes --]
Hi David, others
Results seem OK
recap: I have 2 6core 64bit opterons and I make -j13
I do
# echo always >/sys/kernel/mm/transparent_hugepage/enabled
# while [ 1 ]
do
sleep 10
date
echo = vmstat
egrep "(thp|compact)" /proc/vmstat
echo = khugepaged stack
cat /proc/501/stack
done > /tmp/49361.xxxx
# emerge icedtea
(where 501 = pidof khugepaged)
for xxxx = base = 3.6.6
and xxxx = test = 3.6.6 + diff you provided
I attach
/tmp/49361.base.gz
and
/tmp/49361.test.gz
Note:
with xxx=base, I could see
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
8617 root 20 0 3620m 41m 10m S 988.3 0.5 6:19.06 javac
1 root 20 0 4208 588 556 S 0.0 0.0 0:03.25 init
already during configure and I needed to kill -9 javac
with xxx=test, I could see
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
9275 root 20 0 2067m 474m 10m S 304.2 5.9 0:32.81 javac
710 root 0 -20 0 0 0 S 0.3 0.0 0:01.07 kworker/0:1H
later when processing >700 java files
Also note that with xxx=test compact_blocks_moved stays 0
hope this helps
Thanks
have a nice day
On 2012 Nov 15, Marc Duponcheel wrote:
> Hi David
>
> Thanks for the changeset
>
> I will test 3.6.6 without&with this weekend.
>
> Have a nice day
--
Marc Duponcheel
Velodroomstraat 74 - 2600 Berchem - Belgium
+32 (0)478 68.10.91 - marc@offline.be
[-- Attachment #2: 49361.base.gz --]
[-- Type: application/octet-stream, Size: 861 bytes --]
[-- Attachment #3: 49361.test.gz --]
[-- Type: application/octet-stream, Size: 691 bytes --]
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [3.6 regression?] THP + migration/compaction livelock (I think)
2012-11-17 0:18 ` Marc Duponcheel
@ 2012-11-18 22:55 ` David Rientjes
2012-12-05 19:23 ` Andy Lutomirski
0 siblings, 1 reply; 16+ messages in thread
From: David Rientjes @ 2012-11-18 22:55 UTC (permalink / raw)
To: Marc Duponcheel; +Cc: Mel Gorman, Andy Lutomirski, linux-kernel, linux-mm
On Sat, 17 Nov 2012, Marc Duponcheel wrote:
> # echo always >/sys/kernel/mm/transparent_hugepage/enabled
> # while [ 1 ]
> do
> sleep 10
> date
> echo = vmstat
> egrep "(thp|compact)" /proc/vmstat
> echo = khugepaged stack
> cat /proc/501/stack
> done > /tmp/49361.xxxx
> # emerge icedtea
> (where 501 = pidof khugepaged)
>
> for xxxx = base = 3.6.6
> and xxxx = test = 3.6.6 + diff you provided
>
> I attach
> /tmp/49361.base.gz
> and
> /tmp/49361.test.gz
>
> Note:
>
> with xxx=base, I could see
> PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
> 8617 root 20 0 3620m 41m 10m S 988.3 0.5 6:19.06 javac
> 1 root 20 0 4208 588 556 S 0.0 0.0 0:03.25 init
> already during configure and I needed to kill -9 javac
>
> with xxx=test, I could see
> PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
> 9275 root 20 0 2067m 474m 10m S 304.2 5.9 0:32.81 javac
> 710 root 0 -20 0 0 0 S 0.3 0.0 0:01.07 kworker/0:1H
> later when processing >700 java files
>
> Also note that with xxx=test compact_blocks_moved stays 0
>
Sounds good! Andy, have you had the opportunity to try to reproduce your
issue with the backports that Mel listed? I think he'll be considering
asking for some of these to be backported for a future stable release so
any input you can provide would certainly be helpful.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [3.6 regression?] THP + migration/compaction livelock (I think)
2012-11-18 22:55 ` David Rientjes
@ 2012-12-05 19:23 ` Andy Lutomirski
0 siblings, 0 replies; 16+ messages in thread
From: Andy Lutomirski @ 2012-12-05 19:23 UTC (permalink / raw)
To: David Rientjes; +Cc: Marc Duponcheel, Mel Gorman, linux-kernel, linux-mm
On Sun, Nov 18, 2012 at 2:55 PM, David Rientjes <rientjes@google.com> wrote:
> On Sat, 17 Nov 2012, Marc Duponcheel wrote:
>
>> # echo always >/sys/kernel/mm/transparent_hugepage/enabled
>> # while [ 1 ]
>> do
>> sleep 10
>> date
>> echo = vmstat
>> egrep "(thp|compact)" /proc/vmstat
>> echo = khugepaged stack
>> cat /proc/501/stack
>> done > /tmp/49361.xxxx
>> # emerge icedtea
>> (where 501 = pidof khugepaged)
>>
>> for xxxx = base = 3.6.6
>> and xxxx = test = 3.6.6 + diff you provided
>>
>> I attach
>> /tmp/49361.base.gz
>> and
>> /tmp/49361.test.gz
>>
>> Note:
>>
>> with xxx=base, I could see
>> PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
>> 8617 root 20 0 3620m 41m 10m S 988.3 0.5 6:19.06 javac
>> 1 root 20 0 4208 588 556 S 0.0 0.0 0:03.25 init
>> already during configure and I needed to kill -9 javac
>>
>> with xxx=test, I could see
>> PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
>> 9275 root 20 0 2067m 474m 10m S 304.2 5.9 0:32.81 javac
>> 710 root 0 -20 0 0 0 S 0.3 0.0 0:01.07 kworker/0:1H
>> later when processing >700 java files
>>
>> Also note that with xxx=test compact_blocks_moved stays 0
>>
>
> Sounds good! Andy, have you had the opportunity to try to reproduce your
> issue with the backports that Mel listed? I think he'll be considering
> asking for some of these to be backported for a future stable release so
> any input you can provide would certainly be helpful.
I've had an impressive amount of trouble even reproducing it on 3.6.
Apparently I haven't hid the magic combination yet. I'll give it
another try soon.
--
Andy Lutomirski
AMA Capital Management, LLC
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2012-12-05 19:23 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-11-13 22:13 [3.6 regression?] THP + migration/compaction livelock (I think) Andy Lutomirski
2012-11-13 23:11 ` David Rientjes
2012-11-13 23:25 ` Andy Lutomirski
2012-11-13 23:41 ` David Rientjes
2012-11-13 23:45 ` Andy Lutomirski
2012-11-13 23:54 ` David Rientjes
2012-11-14 1:22 ` Marc Duponcheel
2012-11-14 1:51 ` David Rientjes
2012-11-14 13:21 ` Marc Duponcheel
2012-11-14 10:01 ` Mel Gorman
2012-11-14 13:29 ` Marc Duponcheel
2012-11-14 21:50 ` David Rientjes
2012-11-15 1:14 ` Marc Duponcheel
2012-11-17 0:18 ` Marc Duponcheel
2012-11-18 22:55 ` David Rientjes
2012-12-05 19:23 ` Andy Lutomirski
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).