mirror of
https://github.com/tbsdtv/linux_media.git
synced 2025-07-23 20:51:03 +02:00
Merge tag 'mm-hotfixes-stable-2023-03-14-16-51' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
Pull misc fixes from Andrew Morton: "Eleven hotfixes. Four of these are cc:stable and the remainder address post-6.2 issues or aren't considered suitable for backporting. Seven of these fixes are for MM" * tag 'mm-hotfixes-stable-2023-03-14-16-51' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm: mm/damon/paddr: fix folio_nr_pages() after folio_put() in damon_pa_mark_accessed_or_deactivate() mm/damon/paddr: fix folio_size() call after folio_put() in damon_pa_young() ocfs2: fix data corruption after failed write migrate_pages: try migrate in batch asynchronously firstly migrate_pages: move split folios processing out of migrate_pages_batch() migrate_pages: fix deadlock in batched migration .mailmap: add Alexandre Ghiti personal email address mailmap: correct Dikshita Agarwal's Qualcomm email address mailmap: updates for Jarkko Sakkinen mm/userfaultfd: propagate uffd-wp bit when PTE-mapping the huge zeropage mm: teach mincore_hugetlb about pte markers
This commit is contained in:
4
.mailmap
4
.mailmap
@@ -28,6 +28,7 @@ Alexander Lobakin <alobakin@pm.me> <bloodyreaper@yandex.ru>
|
|||||||
Alexander Mikhalitsyn <alexander@mihalicyn.com> <alexander.mikhalitsyn@virtuozzo.com>
|
Alexander Mikhalitsyn <alexander@mihalicyn.com> <alexander.mikhalitsyn@virtuozzo.com>
|
||||||
Alexander Mikhalitsyn <alexander@mihalicyn.com> <aleksandr.mikhalitsyn@canonical.com>
|
Alexander Mikhalitsyn <alexander@mihalicyn.com> <aleksandr.mikhalitsyn@canonical.com>
|
||||||
Alexandre Belloni <alexandre.belloni@bootlin.com> <alexandre.belloni@free-electrons.com>
|
Alexandre Belloni <alexandre.belloni@bootlin.com> <alexandre.belloni@free-electrons.com>
|
||||||
|
Alexandre Ghiti <alex@ghiti.fr> <alexandre.ghiti@canonical.com>
|
||||||
Alexei Starovoitov <ast@kernel.org> <alexei.starovoitov@gmail.com>
|
Alexei Starovoitov <ast@kernel.org> <alexei.starovoitov@gmail.com>
|
||||||
Alexei Starovoitov <ast@kernel.org> <ast@fb.com>
|
Alexei Starovoitov <ast@kernel.org> <ast@fb.com>
|
||||||
Alexei Starovoitov <ast@kernel.org> <ast@plumgrid.com>
|
Alexei Starovoitov <ast@kernel.org> <ast@plumgrid.com>
|
||||||
@@ -121,7 +122,7 @@ Dengcheng Zhu <dzhu@wavecomp.com> <dengcheng.zhu@gmail.com>
|
|||||||
Dengcheng Zhu <dzhu@wavecomp.com> <dengcheng.zhu@imgtec.com>
|
Dengcheng Zhu <dzhu@wavecomp.com> <dengcheng.zhu@imgtec.com>
|
||||||
Dengcheng Zhu <dzhu@wavecomp.com> <dengcheng.zhu@mips.com>
|
Dengcheng Zhu <dzhu@wavecomp.com> <dengcheng.zhu@mips.com>
|
||||||
<dev.kurt@vandijck-laurijssen.be> <kurt.van.dijck@eia.be>
|
<dev.kurt@vandijck-laurijssen.be> <kurt.van.dijck@eia.be>
|
||||||
Dikshita Agarwal <dikshita@qti.qualcomm.com> <dikshita@codeaurora.org>
|
Dikshita Agarwal <quic_dikshita@quicinc.com> <dikshita@codeaurora.org>
|
||||||
Dmitry Baryshkov <dbaryshkov@gmail.com>
|
Dmitry Baryshkov <dbaryshkov@gmail.com>
|
||||||
Dmitry Baryshkov <dbaryshkov@gmail.com> <[dbaryshkov@gmail.com]>
|
Dmitry Baryshkov <dbaryshkov@gmail.com> <[dbaryshkov@gmail.com]>
|
||||||
Dmitry Baryshkov <dbaryshkov@gmail.com> <dmitry_baryshkov@mentor.com>
|
Dmitry Baryshkov <dbaryshkov@gmail.com> <dmitry_baryshkov@mentor.com>
|
||||||
@@ -194,6 +195,7 @@ Jan Glauber <jan.glauber@gmail.com> <jang@linux.vnet.ibm.com>
|
|||||||
Jan Glauber <jan.glauber@gmail.com> <jglauber@cavium.com>
|
Jan Glauber <jan.glauber@gmail.com> <jglauber@cavium.com>
|
||||||
Jarkko Sakkinen <jarkko@kernel.org> <jarkko.sakkinen@linux.intel.com>
|
Jarkko Sakkinen <jarkko@kernel.org> <jarkko.sakkinen@linux.intel.com>
|
||||||
Jarkko Sakkinen <jarkko@kernel.org> <jarkko@profian.com>
|
Jarkko Sakkinen <jarkko@kernel.org> <jarkko@profian.com>
|
||||||
|
Jarkko Sakkinen <jarkko@kernel.org> <jarkko.sakkinen@tuni.fi>
|
||||||
Jason Gunthorpe <jgg@ziepe.ca> <jgg@mellanox.com>
|
Jason Gunthorpe <jgg@ziepe.ca> <jgg@mellanox.com>
|
||||||
Jason Gunthorpe <jgg@ziepe.ca> <jgg@nvidia.com>
|
Jason Gunthorpe <jgg@ziepe.ca> <jgg@nvidia.com>
|
||||||
Jason Gunthorpe <jgg@ziepe.ca> <jgunthorpe@obsidianresearch.com>
|
Jason Gunthorpe <jgg@ziepe.ca> <jgunthorpe@obsidianresearch.com>
|
||||||
|
@@ -1977,11 +1977,26 @@ int ocfs2_write_end_nolock(struct address_space *mapping,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (unlikely(copied < len) && wc->w_target_page) {
|
if (unlikely(copied < len) && wc->w_target_page) {
|
||||||
|
loff_t new_isize;
|
||||||
|
|
||||||
if (!PageUptodate(wc->w_target_page))
|
if (!PageUptodate(wc->w_target_page))
|
||||||
copied = 0;
|
copied = 0;
|
||||||
|
|
||||||
ocfs2_zero_new_buffers(wc->w_target_page, start+copied,
|
new_isize = max_t(loff_t, i_size_read(inode), pos + copied);
|
||||||
start+len);
|
if (new_isize > page_offset(wc->w_target_page))
|
||||||
|
ocfs2_zero_new_buffers(wc->w_target_page, start+copied,
|
||||||
|
start+len);
|
||||||
|
else {
|
||||||
|
/*
|
||||||
|
* When page is fully beyond new isize (data copy
|
||||||
|
* failed), do not bother zeroing the page. Invalidate
|
||||||
|
* it instead so that writeback does not get confused
|
||||||
|
* put page & buffer dirty bits into inconsistent
|
||||||
|
* state.
|
||||||
|
*/
|
||||||
|
block_invalidate_folio(page_folio(wc->w_target_page),
|
||||||
|
0, PAGE_SIZE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (wc->w_target_page)
|
if (wc->w_target_page)
|
||||||
flush_dcache_page(wc->w_target_page);
|
flush_dcache_page(wc->w_target_page);
|
||||||
|
@@ -130,7 +130,6 @@ static bool damon_pa_young(unsigned long paddr, unsigned long *folio_sz)
|
|||||||
accessed = false;
|
accessed = false;
|
||||||
else
|
else
|
||||||
accessed = true;
|
accessed = true;
|
||||||
folio_put(folio);
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,10 +143,10 @@ static bool damon_pa_young(unsigned long paddr, unsigned long *folio_sz)
|
|||||||
|
|
||||||
if (need_lock)
|
if (need_lock)
|
||||||
folio_unlock(folio);
|
folio_unlock(folio);
|
||||||
folio_put(folio);
|
|
||||||
|
|
||||||
out:
|
out:
|
||||||
*folio_sz = folio_size(folio);
|
*folio_sz = folio_size(folio);
|
||||||
|
folio_put(folio);
|
||||||
return accessed;
|
return accessed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,8 +280,8 @@ static inline unsigned long damon_pa_mark_accessed_or_deactivate(
|
|||||||
folio_mark_accessed(folio);
|
folio_mark_accessed(folio);
|
||||||
else
|
else
|
||||||
folio_deactivate(folio);
|
folio_deactivate(folio);
|
||||||
folio_put(folio);
|
|
||||||
applied += folio_nr_pages(folio);
|
applied += folio_nr_pages(folio);
|
||||||
|
folio_put(folio);
|
||||||
}
|
}
|
||||||
return applied * PAGE_SIZE;
|
return applied * PAGE_SIZE;
|
||||||
}
|
}
|
||||||
|
@@ -2037,7 +2037,7 @@ static void __split_huge_zero_page_pmd(struct vm_area_struct *vma,
|
|||||||
{
|
{
|
||||||
struct mm_struct *mm = vma->vm_mm;
|
struct mm_struct *mm = vma->vm_mm;
|
||||||
pgtable_t pgtable;
|
pgtable_t pgtable;
|
||||||
pmd_t _pmd;
|
pmd_t _pmd, old_pmd;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -2048,7 +2048,7 @@ static void __split_huge_zero_page_pmd(struct vm_area_struct *vma,
|
|||||||
*
|
*
|
||||||
* See Documentation/mm/mmu_notifier.rst
|
* See Documentation/mm/mmu_notifier.rst
|
||||||
*/
|
*/
|
||||||
pmdp_huge_clear_flush(vma, haddr, pmd);
|
old_pmd = pmdp_huge_clear_flush(vma, haddr, pmd);
|
||||||
|
|
||||||
pgtable = pgtable_trans_huge_withdraw(mm, pmd);
|
pgtable = pgtable_trans_huge_withdraw(mm, pmd);
|
||||||
pmd_populate(mm, &_pmd, pgtable);
|
pmd_populate(mm, &_pmd, pgtable);
|
||||||
@@ -2057,6 +2057,8 @@ static void __split_huge_zero_page_pmd(struct vm_area_struct *vma,
|
|||||||
pte_t *pte, entry;
|
pte_t *pte, entry;
|
||||||
entry = pfn_pte(my_zero_pfn(haddr), vma->vm_page_prot);
|
entry = pfn_pte(my_zero_pfn(haddr), vma->vm_page_prot);
|
||||||
entry = pte_mkspecial(entry);
|
entry = pte_mkspecial(entry);
|
||||||
|
if (pmd_uffd_wp(old_pmd))
|
||||||
|
entry = pte_mkuffd_wp(entry);
|
||||||
pte = pte_offset_map(&_pmd, haddr);
|
pte = pte_offset_map(&_pmd, haddr);
|
||||||
VM_BUG_ON(!pte_none(*pte));
|
VM_BUG_ON(!pte_none(*pte));
|
||||||
set_pte_at(mm, haddr, pte, entry);
|
set_pte_at(mm, haddr, pte, entry);
|
||||||
|
185
mm/migrate.c
185
mm/migrate.c
@@ -1112,9 +1112,8 @@ static void migrate_folio_done(struct folio *src,
|
|||||||
/* Obtain the lock on page, remove all ptes. */
|
/* Obtain the lock on page, remove all ptes. */
|
||||||
static int migrate_folio_unmap(new_page_t get_new_page, free_page_t put_new_page,
|
static int migrate_folio_unmap(new_page_t get_new_page, free_page_t put_new_page,
|
||||||
unsigned long private, struct folio *src,
|
unsigned long private, struct folio *src,
|
||||||
struct folio **dstp, int force, bool avoid_force_lock,
|
struct folio **dstp, enum migrate_mode mode,
|
||||||
enum migrate_mode mode, enum migrate_reason reason,
|
enum migrate_reason reason, struct list_head *ret)
|
||||||
struct list_head *ret)
|
|
||||||
{
|
{
|
||||||
struct folio *dst;
|
struct folio *dst;
|
||||||
int rc = -EAGAIN;
|
int rc = -EAGAIN;
|
||||||
@@ -1144,7 +1143,7 @@ static int migrate_folio_unmap(new_page_t get_new_page, free_page_t put_new_page
|
|||||||
dst->private = NULL;
|
dst->private = NULL;
|
||||||
|
|
||||||
if (!folio_trylock(src)) {
|
if (!folio_trylock(src)) {
|
||||||
if (!force || mode == MIGRATE_ASYNC)
|
if (mode == MIGRATE_ASYNC)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1163,17 +1162,6 @@ static int migrate_folio_unmap(new_page_t get_new_page, free_page_t put_new_page
|
|||||||
if (current->flags & PF_MEMALLOC)
|
if (current->flags & PF_MEMALLOC)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/*
|
|
||||||
* We have locked some folios and are going to wait to lock
|
|
||||||
* this folio. To avoid a potential deadlock, let's bail
|
|
||||||
* out and not do that. The locked folios will be moved and
|
|
||||||
* unlocked, then we can wait to lock this folio.
|
|
||||||
*/
|
|
||||||
if (avoid_force_lock) {
|
|
||||||
rc = -EDEADLOCK;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
folio_lock(src);
|
folio_lock(src);
|
||||||
}
|
}
|
||||||
locked = true;
|
locked = true;
|
||||||
@@ -1193,8 +1181,6 @@ static int migrate_folio_unmap(new_page_t get_new_page, free_page_t put_new_page
|
|||||||
rc = -EBUSY;
|
rc = -EBUSY;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if (!force)
|
|
||||||
goto out;
|
|
||||||
folio_wait_writeback(src);
|
folio_wait_writeback(src);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1253,7 +1239,7 @@ static int migrate_folio_unmap(new_page_t get_new_page, free_page_t put_new_page
|
|||||||
/* Establish migration ptes */
|
/* Establish migration ptes */
|
||||||
VM_BUG_ON_FOLIO(folio_test_anon(src) &&
|
VM_BUG_ON_FOLIO(folio_test_anon(src) &&
|
||||||
!folio_test_ksm(src) && !anon_vma, src);
|
!folio_test_ksm(src) && !anon_vma, src);
|
||||||
try_to_migrate(src, TTU_BATCH_FLUSH);
|
try_to_migrate(src, mode == MIGRATE_ASYNC ? TTU_BATCH_FLUSH : 0);
|
||||||
page_was_mapped = 1;
|
page_was_mapped = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1267,7 +1253,7 @@ out:
|
|||||||
* A folio that has not been unmapped will be restored to
|
* A folio that has not been unmapped will be restored to
|
||||||
* right list unless we want to retry.
|
* right list unless we want to retry.
|
||||||
*/
|
*/
|
||||||
if (rc == -EAGAIN || rc == -EDEADLOCK)
|
if (rc == -EAGAIN)
|
||||||
ret = NULL;
|
ret = NULL;
|
||||||
|
|
||||||
migrate_folio_undo_src(src, page_was_mapped, anon_vma, locked, ret);
|
migrate_folio_undo_src(src, page_was_mapped, anon_vma, locked, ret);
|
||||||
@@ -1508,6 +1494,9 @@ static inline int try_split_folio(struct folio *folio, struct list_head *split_f
|
|||||||
#define NR_MAX_BATCHED_MIGRATION 512
|
#define NR_MAX_BATCHED_MIGRATION 512
|
||||||
#endif
|
#endif
|
||||||
#define NR_MAX_MIGRATE_PAGES_RETRY 10
|
#define NR_MAX_MIGRATE_PAGES_RETRY 10
|
||||||
|
#define NR_MAX_MIGRATE_ASYNC_RETRY 3
|
||||||
|
#define NR_MAX_MIGRATE_SYNC_RETRY \
|
||||||
|
(NR_MAX_MIGRATE_PAGES_RETRY - NR_MAX_MIGRATE_ASYNC_RETRY)
|
||||||
|
|
||||||
struct migrate_pages_stats {
|
struct migrate_pages_stats {
|
||||||
int nr_succeeded; /* Normal and large folios migrated successfully, in
|
int nr_succeeded; /* Normal and large folios migrated successfully, in
|
||||||
@@ -1618,13 +1607,19 @@ static int migrate_hugetlbs(struct list_head *from, new_page_t get_new_page,
|
|||||||
/*
|
/*
|
||||||
* migrate_pages_batch() first unmaps folios in the from list as many as
|
* migrate_pages_batch() first unmaps folios in the from list as many as
|
||||||
* possible, then move the unmapped folios.
|
* possible, then move the unmapped folios.
|
||||||
|
*
|
||||||
|
* We only batch migration if mode == MIGRATE_ASYNC to avoid to wait a
|
||||||
|
* lock or bit when we have locked more than one folio. Which may cause
|
||||||
|
* deadlock (e.g., for loop device). So, if mode != MIGRATE_ASYNC, the
|
||||||
|
* length of the from list must be <= 1.
|
||||||
*/
|
*/
|
||||||
static int migrate_pages_batch(struct list_head *from, new_page_t get_new_page,
|
static int migrate_pages_batch(struct list_head *from, new_page_t get_new_page,
|
||||||
free_page_t put_new_page, unsigned long private,
|
free_page_t put_new_page, unsigned long private,
|
||||||
enum migrate_mode mode, int reason, struct list_head *ret_folios,
|
enum migrate_mode mode, int reason, struct list_head *ret_folios,
|
||||||
struct migrate_pages_stats *stats)
|
struct list_head *split_folios, struct migrate_pages_stats *stats,
|
||||||
|
int nr_pass)
|
||||||
{
|
{
|
||||||
int retry;
|
int retry = 1;
|
||||||
int large_retry = 1;
|
int large_retry = 1;
|
||||||
int thp_retry = 1;
|
int thp_retry = 1;
|
||||||
int nr_failed = 0;
|
int nr_failed = 0;
|
||||||
@@ -1634,21 +1629,15 @@ static int migrate_pages_batch(struct list_head *from, new_page_t get_new_page,
|
|||||||
bool is_large = false;
|
bool is_large = false;
|
||||||
bool is_thp = false;
|
bool is_thp = false;
|
||||||
struct folio *folio, *folio2, *dst = NULL, *dst2;
|
struct folio *folio, *folio2, *dst = NULL, *dst2;
|
||||||
int rc, rc_saved, nr_pages;
|
int rc, rc_saved = 0, nr_pages;
|
||||||
LIST_HEAD(split_folios);
|
|
||||||
LIST_HEAD(unmap_folios);
|
LIST_HEAD(unmap_folios);
|
||||||
LIST_HEAD(dst_folios);
|
LIST_HEAD(dst_folios);
|
||||||
bool nosplit = (reason == MR_NUMA_MISPLACED);
|
bool nosplit = (reason == MR_NUMA_MISPLACED);
|
||||||
bool no_split_folio_counting = false;
|
|
||||||
bool avoid_force_lock;
|
|
||||||
|
|
||||||
retry:
|
VM_WARN_ON_ONCE(mode != MIGRATE_ASYNC &&
|
||||||
rc_saved = 0;
|
!list_empty(from) && !list_is_singular(from));
|
||||||
avoid_force_lock = false;
|
|
||||||
retry = 1;
|
for (pass = 0; pass < nr_pass && (retry || large_retry); pass++) {
|
||||||
for (pass = 0;
|
|
||||||
pass < NR_MAX_MIGRATE_PAGES_RETRY && (retry || large_retry);
|
|
||||||
pass++) {
|
|
||||||
retry = 0;
|
retry = 0;
|
||||||
large_retry = 0;
|
large_retry = 0;
|
||||||
thp_retry = 0;
|
thp_retry = 0;
|
||||||
@@ -1679,7 +1668,7 @@ retry:
|
|||||||
if (!thp_migration_supported() && is_thp) {
|
if (!thp_migration_supported() && is_thp) {
|
||||||
nr_large_failed++;
|
nr_large_failed++;
|
||||||
stats->nr_thp_failed++;
|
stats->nr_thp_failed++;
|
||||||
if (!try_split_folio(folio, &split_folios)) {
|
if (!try_split_folio(folio, split_folios)) {
|
||||||
stats->nr_thp_split++;
|
stats->nr_thp_split++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -1689,15 +1678,13 @@ retry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
rc = migrate_folio_unmap(get_new_page, put_new_page, private,
|
rc = migrate_folio_unmap(get_new_page, put_new_page, private,
|
||||||
folio, &dst, pass > 2, avoid_force_lock,
|
folio, &dst, mode, reason, ret_folios);
|
||||||
mode, reason, ret_folios);
|
|
||||||
/*
|
/*
|
||||||
* The rules are:
|
* The rules are:
|
||||||
* Success: folio will be freed
|
* Success: folio will be freed
|
||||||
* Unmap: folio will be put on unmap_folios list,
|
* Unmap: folio will be put on unmap_folios list,
|
||||||
* dst folio put on dst_folios list
|
* dst folio put on dst_folios list
|
||||||
* -EAGAIN: stay on the from list
|
* -EAGAIN: stay on the from list
|
||||||
* -EDEADLOCK: stay on the from list
|
|
||||||
* -ENOMEM: stay on the from list
|
* -ENOMEM: stay on the from list
|
||||||
* Other errno: put on ret_folios list
|
* Other errno: put on ret_folios list
|
||||||
*/
|
*/
|
||||||
@@ -1712,7 +1699,7 @@ retry:
|
|||||||
stats->nr_thp_failed += is_thp;
|
stats->nr_thp_failed += is_thp;
|
||||||
/* Large folio NUMA faulting doesn't split to retry. */
|
/* Large folio NUMA faulting doesn't split to retry. */
|
||||||
if (!nosplit) {
|
if (!nosplit) {
|
||||||
int ret = try_split_folio(folio, &split_folios);
|
int ret = try_split_folio(folio, split_folios);
|
||||||
|
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
stats->nr_thp_split += is_thp;
|
stats->nr_thp_split += is_thp;
|
||||||
@@ -1729,18 +1716,11 @@ retry:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (!no_split_folio_counting) {
|
} else {
|
||||||
nr_failed++;
|
nr_failed++;
|
||||||
}
|
}
|
||||||
|
|
||||||
stats->nr_failed_pages += nr_pages + nr_retry_pages;
|
stats->nr_failed_pages += nr_pages + nr_retry_pages;
|
||||||
/*
|
|
||||||
* There might be some split folios of fail-to-migrate large
|
|
||||||
* folios left in split_folios list. Move them to ret_folios
|
|
||||||
* list so that they could be put back to the right list by
|
|
||||||
* the caller otherwise the folio refcnt will be leaked.
|
|
||||||
*/
|
|
||||||
list_splice_init(&split_folios, ret_folios);
|
|
||||||
/* nr_failed isn't updated for not used */
|
/* nr_failed isn't updated for not used */
|
||||||
nr_large_failed += large_retry;
|
nr_large_failed += large_retry;
|
||||||
stats->nr_thp_failed += thp_retry;
|
stats->nr_thp_failed += thp_retry;
|
||||||
@@ -1749,19 +1729,11 @@ retry:
|
|||||||
goto out;
|
goto out;
|
||||||
else
|
else
|
||||||
goto move;
|
goto move;
|
||||||
case -EDEADLOCK:
|
|
||||||
/*
|
|
||||||
* The folio cannot be locked for potential deadlock.
|
|
||||||
* Go move (and unlock) all locked folios. Then we can
|
|
||||||
* try again.
|
|
||||||
*/
|
|
||||||
rc_saved = rc;
|
|
||||||
goto move;
|
|
||||||
case -EAGAIN:
|
case -EAGAIN:
|
||||||
if (is_large) {
|
if (is_large) {
|
||||||
large_retry++;
|
large_retry++;
|
||||||
thp_retry += is_thp;
|
thp_retry += is_thp;
|
||||||
} else if (!no_split_folio_counting) {
|
} else {
|
||||||
retry++;
|
retry++;
|
||||||
}
|
}
|
||||||
nr_retry_pages += nr_pages;
|
nr_retry_pages += nr_pages;
|
||||||
@@ -1771,11 +1743,6 @@ retry:
|
|||||||
stats->nr_thp_succeeded += is_thp;
|
stats->nr_thp_succeeded += is_thp;
|
||||||
break;
|
break;
|
||||||
case MIGRATEPAGE_UNMAP:
|
case MIGRATEPAGE_UNMAP:
|
||||||
/*
|
|
||||||
* We have locked some folios, don't force lock
|
|
||||||
* to avoid deadlock.
|
|
||||||
*/
|
|
||||||
avoid_force_lock = true;
|
|
||||||
list_move_tail(&folio->lru, &unmap_folios);
|
list_move_tail(&folio->lru, &unmap_folios);
|
||||||
list_add_tail(&dst->lru, &dst_folios);
|
list_add_tail(&dst->lru, &dst_folios);
|
||||||
break;
|
break;
|
||||||
@@ -1789,7 +1756,7 @@ retry:
|
|||||||
if (is_large) {
|
if (is_large) {
|
||||||
nr_large_failed++;
|
nr_large_failed++;
|
||||||
stats->nr_thp_failed += is_thp;
|
stats->nr_thp_failed += is_thp;
|
||||||
} else if (!no_split_folio_counting) {
|
} else {
|
||||||
nr_failed++;
|
nr_failed++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1807,9 +1774,7 @@ move:
|
|||||||
try_to_unmap_flush();
|
try_to_unmap_flush();
|
||||||
|
|
||||||
retry = 1;
|
retry = 1;
|
||||||
for (pass = 0;
|
for (pass = 0; pass < nr_pass && (retry || large_retry); pass++) {
|
||||||
pass < NR_MAX_MIGRATE_PAGES_RETRY && (retry || large_retry);
|
|
||||||
pass++) {
|
|
||||||
retry = 0;
|
retry = 0;
|
||||||
large_retry = 0;
|
large_retry = 0;
|
||||||
thp_retry = 0;
|
thp_retry = 0;
|
||||||
@@ -1838,7 +1803,7 @@ move:
|
|||||||
if (is_large) {
|
if (is_large) {
|
||||||
large_retry++;
|
large_retry++;
|
||||||
thp_retry += is_thp;
|
thp_retry += is_thp;
|
||||||
} else if (!no_split_folio_counting) {
|
} else {
|
||||||
retry++;
|
retry++;
|
||||||
}
|
}
|
||||||
nr_retry_pages += nr_pages;
|
nr_retry_pages += nr_pages;
|
||||||
@@ -1851,7 +1816,7 @@ move:
|
|||||||
if (is_large) {
|
if (is_large) {
|
||||||
nr_large_failed++;
|
nr_large_failed++;
|
||||||
stats->nr_thp_failed += is_thp;
|
stats->nr_thp_failed += is_thp;
|
||||||
} else if (!no_split_folio_counting) {
|
} else {
|
||||||
nr_failed++;
|
nr_failed++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1888,30 +1853,52 @@ out:
|
|||||||
dst2 = list_next_entry(dst, lru);
|
dst2 = list_next_entry(dst, lru);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int migrate_pages_sync(struct list_head *from, new_page_t get_new_page,
|
||||||
|
free_page_t put_new_page, unsigned long private,
|
||||||
|
enum migrate_mode mode, int reason, struct list_head *ret_folios,
|
||||||
|
struct list_head *split_folios, struct migrate_pages_stats *stats)
|
||||||
|
{
|
||||||
|
int rc, nr_failed = 0;
|
||||||
|
LIST_HEAD(folios);
|
||||||
|
struct migrate_pages_stats astats;
|
||||||
|
|
||||||
|
memset(&astats, 0, sizeof(astats));
|
||||||
|
/* Try to migrate in batch with MIGRATE_ASYNC mode firstly */
|
||||||
|
rc = migrate_pages_batch(from, get_new_page, put_new_page, private, MIGRATE_ASYNC,
|
||||||
|
reason, &folios, split_folios, &astats,
|
||||||
|
NR_MAX_MIGRATE_ASYNC_RETRY);
|
||||||
|
stats->nr_succeeded += astats.nr_succeeded;
|
||||||
|
stats->nr_thp_succeeded += astats.nr_thp_succeeded;
|
||||||
|
stats->nr_thp_split += astats.nr_thp_split;
|
||||||
|
if (rc < 0) {
|
||||||
|
stats->nr_failed_pages += astats.nr_failed_pages;
|
||||||
|
stats->nr_thp_failed += astats.nr_thp_failed;
|
||||||
|
list_splice_tail(&folios, ret_folios);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
stats->nr_thp_failed += astats.nr_thp_split;
|
||||||
|
nr_failed += astats.nr_thp_split;
|
||||||
/*
|
/*
|
||||||
* Try to migrate split folios of fail-to-migrate large folios, no
|
* Fall back to migrate all failed folios one by one synchronously. All
|
||||||
* nr_failed counting in this round, since all split folios of a
|
* failed folios except split THPs will be retried, so their failure
|
||||||
* large folio is counted as 1 failure in the first round.
|
* isn't counted
|
||||||
*/
|
*/
|
||||||
if (rc >= 0 && !list_empty(&split_folios)) {
|
list_splice_tail_init(&folios, from);
|
||||||
/*
|
while (!list_empty(from)) {
|
||||||
* Move non-migrated folios (after NR_MAX_MIGRATE_PAGES_RETRY
|
list_move(from->next, &folios);
|
||||||
* retries) to ret_folios to avoid migrating them again.
|
rc = migrate_pages_batch(&folios, get_new_page, put_new_page,
|
||||||
*/
|
private, mode, reason, ret_folios,
|
||||||
list_splice_init(from, ret_folios);
|
split_folios, stats, NR_MAX_MIGRATE_SYNC_RETRY);
|
||||||
list_splice_init(&split_folios, from);
|
list_splice_tail_init(&folios, ret_folios);
|
||||||
no_split_folio_counting = true;
|
if (rc < 0)
|
||||||
goto retry;
|
return rc;
|
||||||
|
nr_failed += rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
return nr_failed;
|
||||||
* We have unlocked all locked folios, so we can force lock now, let's
|
|
||||||
* try again.
|
|
||||||
*/
|
|
||||||
if (rc == -EDEADLOCK)
|
|
||||||
goto retry;
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1949,6 +1936,7 @@ int migrate_pages(struct list_head *from, new_page_t get_new_page,
|
|||||||
struct folio *folio, *folio2;
|
struct folio *folio, *folio2;
|
||||||
LIST_HEAD(folios);
|
LIST_HEAD(folios);
|
||||||
LIST_HEAD(ret_folios);
|
LIST_HEAD(ret_folios);
|
||||||
|
LIST_HEAD(split_folios);
|
||||||
struct migrate_pages_stats stats;
|
struct migrate_pages_stats stats;
|
||||||
|
|
||||||
trace_mm_migrate_pages_start(mode, reason);
|
trace_mm_migrate_pages_start(mode, reason);
|
||||||
@@ -1959,6 +1947,7 @@ int migrate_pages(struct list_head *from, new_page_t get_new_page,
|
|||||||
mode, reason, &stats, &ret_folios);
|
mode, reason, &stats, &ret_folios);
|
||||||
if (rc_gather < 0)
|
if (rc_gather < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
again:
|
again:
|
||||||
nr_pages = 0;
|
nr_pages = 0;
|
||||||
list_for_each_entry_safe(folio, folio2, from, lru) {
|
list_for_each_entry_safe(folio, folio2, from, lru) {
|
||||||
@@ -1969,20 +1958,36 @@ again:
|
|||||||
}
|
}
|
||||||
|
|
||||||
nr_pages += folio_nr_pages(folio);
|
nr_pages += folio_nr_pages(folio);
|
||||||
if (nr_pages > NR_MAX_BATCHED_MIGRATION)
|
if (nr_pages >= NR_MAX_BATCHED_MIGRATION)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (nr_pages > NR_MAX_BATCHED_MIGRATION)
|
if (nr_pages >= NR_MAX_BATCHED_MIGRATION)
|
||||||
list_cut_before(&folios, from, &folio->lru);
|
list_cut_before(&folios, from, &folio2->lru);
|
||||||
else
|
else
|
||||||
list_splice_init(from, &folios);
|
list_splice_init(from, &folios);
|
||||||
rc = migrate_pages_batch(&folios, get_new_page, put_new_page, private,
|
if (mode == MIGRATE_ASYNC)
|
||||||
mode, reason, &ret_folios, &stats);
|
rc = migrate_pages_batch(&folios, get_new_page, put_new_page, private,
|
||||||
|
mode, reason, &ret_folios, &split_folios, &stats,
|
||||||
|
NR_MAX_MIGRATE_PAGES_RETRY);
|
||||||
|
else
|
||||||
|
rc = migrate_pages_sync(&folios, get_new_page, put_new_page, private,
|
||||||
|
mode, reason, &ret_folios, &split_folios, &stats);
|
||||||
list_splice_tail_init(&folios, &ret_folios);
|
list_splice_tail_init(&folios, &ret_folios);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
rc_gather = rc;
|
rc_gather = rc;
|
||||||
|
list_splice_tail(&split_folios, &ret_folios);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
if (!list_empty(&split_folios)) {
|
||||||
|
/*
|
||||||
|
* Failure isn't counted since all split folios of a large folio
|
||||||
|
* is counted as 1 failure already. And, we only try to migrate
|
||||||
|
* with minimal effort, force MIGRATE_ASYNC mode and retry once.
|
||||||
|
*/
|
||||||
|
migrate_pages_batch(&split_folios, get_new_page, put_new_page, private,
|
||||||
|
MIGRATE_ASYNC, reason, &ret_folios, NULL, &stats, 1);
|
||||||
|
list_splice_tail_init(&split_folios, &ret_folios);
|
||||||
|
}
|
||||||
rc_gather += rc;
|
rc_gather += rc;
|
||||||
if (!list_empty(from))
|
if (!list_empty(from))
|
||||||
goto again;
|
goto again;
|
||||||
|
@@ -33,7 +33,7 @@ static int mincore_hugetlb(pte_t *pte, unsigned long hmask, unsigned long addr,
|
|||||||
* Hugepages under user process are always in RAM and never
|
* Hugepages under user process are always in RAM and never
|
||||||
* swapped out, but theoretically it needs to be checked.
|
* swapped out, but theoretically it needs to be checked.
|
||||||
*/
|
*/
|
||||||
present = pte && !huge_pte_none(huge_ptep_get(pte));
|
present = pte && !huge_pte_none_mostly(huge_ptep_get(pte));
|
||||||
for (; addr != end; vec++, addr += PAGE_SIZE)
|
for (; addr != end; vec++, addr += PAGE_SIZE)
|
||||||
*vec = present;
|
*vec = present;
|
||||||
walk->private = vec;
|
walk->private = vec;
|
||||||
|
Reference in New Issue
Block a user