mirror of
https://github.com/torvalds/linux.git
synced 2026-08-01 12:11:59 +02:00
Merge aa1b67903a ("Revert "include/linux/mm_inline.h: fold __update_lru_size() into its sole caller"") into android-mainline
Steps on the way to 6.1-rc1 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Change-Id: Iaa91521d917512387a06206a1872bfa66f8be6ae
This commit is contained in:
commit
6a8e52b0ec
|
|
@ -13,7 +13,7 @@ a) waiting for a CPU (while being runnable)
|
|||
b) completion of synchronous block I/O initiated by the task
|
||||
c) swapping in pages
|
||||
d) memory reclaim
|
||||
e) thrashing page cache
|
||||
e) thrashing
|
||||
f) direct compact
|
||||
g) write-protect copy
|
||||
|
||||
|
|
|
|||
|
|
@ -1416,6 +1416,14 @@ config DYNAMIC_SIGFRAME
|
|||
config HAVE_ARCH_NODE_DEV_GROUP
|
||||
bool
|
||||
|
||||
config ARCH_HAS_NONLEAF_PMD_YOUNG
|
||||
bool
|
||||
help
|
||||
Architectures that select this option are capable of setting the
|
||||
accessed bit in non-leaf PMD entries when using them as part of linear
|
||||
address translations. Page table walkers that clear the accessed bit
|
||||
may use this capability to reduce their search space.
|
||||
|
||||
source "kernel/gcov/Kconfig"
|
||||
|
||||
source "scripts/gcc-plugins/Kconfig"
|
||||
|
|
|
|||
|
|
@ -1082,24 +1082,13 @@ static inline void update_mmu_cache(struct vm_area_struct *vma,
|
|||
* page after fork() + CoW for pfn mappings. We don't always have a
|
||||
* hardware-managed access flag on arm64.
|
||||
*/
|
||||
static inline bool arch_faults_on_old_pte(void)
|
||||
{
|
||||
/* The register read below requires a stable CPU to make any sense */
|
||||
cant_migrate();
|
||||
|
||||
return !cpu_has_hw_af();
|
||||
}
|
||||
#define arch_faults_on_old_pte arch_faults_on_old_pte
|
||||
#define arch_has_hw_pte_young cpu_has_hw_af
|
||||
|
||||
/*
|
||||
* Experimentally, it's cheap to set the access flag in hardware and we
|
||||
* benefit from prefaulting mappings as 'old' to start with.
|
||||
*/
|
||||
static inline bool arch_wants_old_prefaulted_pte(void)
|
||||
{
|
||||
return !arch_faults_on_old_pte();
|
||||
}
|
||||
#define arch_wants_old_prefaulted_pte arch_wants_old_prefaulted_pte
|
||||
#define arch_wants_old_prefaulted_pte cpu_has_hw_af
|
||||
|
||||
static inline bool pud_sect_supported(void)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ config X86
|
|||
select ARCH_HAS_PMEM_API if X86_64
|
||||
select ARCH_HAS_PTE_DEVMAP if X86_64
|
||||
select ARCH_HAS_PTE_SPECIAL
|
||||
select ARCH_HAS_NONLEAF_PMD_YOUNG if PGTABLE_LEVELS > 2
|
||||
select ARCH_HAS_UACCESS_FLUSHCACHE if X86_64
|
||||
select ARCH_HAS_COPY_MC if X86_64
|
||||
select ARCH_HAS_SET_MEMORY
|
||||
|
|
|
|||
|
|
@ -815,7 +815,8 @@ static inline unsigned long pmd_page_vaddr(pmd_t pmd)
|
|||
|
||||
static inline int pmd_bad(pmd_t pmd)
|
||||
{
|
||||
return (pmd_flags(pmd) & ~_PAGE_USER) != _KERNPG_TABLE;
|
||||
return (pmd_flags(pmd) & ~(_PAGE_USER | _PAGE_ACCESSED)) !=
|
||||
(_KERNPG_TABLE & ~_PAGE_ACCESSED);
|
||||
}
|
||||
|
||||
static inline unsigned long pages_to_mb(unsigned long npg)
|
||||
|
|
@ -1431,10 +1432,10 @@ static inline bool arch_has_pfn_modify_check(void)
|
|||
return boot_cpu_has_bug(X86_BUG_L1TF);
|
||||
}
|
||||
|
||||
#define arch_faults_on_old_pte arch_faults_on_old_pte
|
||||
static inline bool arch_faults_on_old_pte(void)
|
||||
#define arch_has_hw_pte_young arch_has_hw_pte_young
|
||||
static inline bool arch_has_hw_pte_young(void)
|
||||
{
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PAGE_TABLE_CHECK
|
||||
|
|
|
|||
|
|
@ -550,7 +550,7 @@ int ptep_test_and_clear_young(struct vm_area_struct *vma,
|
|||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
||||
#if defined(CONFIG_TRANSPARENT_HUGEPAGE) || defined(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG)
|
||||
int pmdp_test_and_clear_young(struct vm_area_struct *vma,
|
||||
unsigned long addr, pmd_t *pmdp)
|
||||
{
|
||||
|
|
@ -562,6 +562,9 @@ int pmdp_test_and_clear_young(struct vm_area_struct *vma,
|
|||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
||||
int pudp_test_and_clear_young(struct vm_area_struct *vma,
|
||||
unsigned long addr, pud_t *pudp)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -73,8 +73,8 @@ extern int delayacct_add_tsk(struct taskstats *, struct task_struct *);
|
|||
extern __u64 __delayacct_blkio_ticks(struct task_struct *);
|
||||
extern void __delayacct_freepages_start(void);
|
||||
extern void __delayacct_freepages_end(void);
|
||||
extern void __delayacct_thrashing_start(void);
|
||||
extern void __delayacct_thrashing_end(void);
|
||||
extern void __delayacct_thrashing_start(bool *in_thrashing);
|
||||
extern void __delayacct_thrashing_end(bool *in_thrashing);
|
||||
extern void __delayacct_swapin_start(void);
|
||||
extern void __delayacct_swapin_end(void);
|
||||
extern void __delayacct_compact_start(void);
|
||||
|
|
@ -143,22 +143,22 @@ static inline void delayacct_freepages_end(void)
|
|||
__delayacct_freepages_end();
|
||||
}
|
||||
|
||||
static inline void delayacct_thrashing_start(void)
|
||||
static inline void delayacct_thrashing_start(bool *in_thrashing)
|
||||
{
|
||||
if (!static_branch_unlikely(&delayacct_key))
|
||||
return;
|
||||
|
||||
if (current->delays)
|
||||
__delayacct_thrashing_start();
|
||||
__delayacct_thrashing_start(in_thrashing);
|
||||
}
|
||||
|
||||
static inline void delayacct_thrashing_end(void)
|
||||
static inline void delayacct_thrashing_end(bool *in_thrashing)
|
||||
{
|
||||
if (!static_branch_unlikely(&delayacct_key))
|
||||
return;
|
||||
|
||||
if (current->delays)
|
||||
__delayacct_thrashing_end();
|
||||
__delayacct_thrashing_end(in_thrashing);
|
||||
}
|
||||
|
||||
static inline void delayacct_swapin_start(void)
|
||||
|
|
@ -237,9 +237,9 @@ static inline void delayacct_freepages_start(void)
|
|||
{}
|
||||
static inline void delayacct_freepages_end(void)
|
||||
{}
|
||||
static inline void delayacct_thrashing_start(void)
|
||||
static inline void delayacct_thrashing_start(bool *in_thrashing)
|
||||
{}
|
||||
static inline void delayacct_thrashing_end(void)
|
||||
static inline void delayacct_thrashing_end(bool *in_thrashing)
|
||||
{}
|
||||
static inline void delayacct_swapin_start(void)
|
||||
{}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ static inline int page_is_file_lru(struct page *page)
|
|||
return folio_is_file_lru(page_folio(page));
|
||||
}
|
||||
|
||||
static __always_inline void update_lru_size(struct lruvec *lruvec,
|
||||
static __always_inline void __update_lru_size(struct lruvec *lruvec,
|
||||
enum lru_list lru, enum zone_type zid,
|
||||
long nr_pages)
|
||||
{
|
||||
|
|
@ -43,6 +43,13 @@ static __always_inline void update_lru_size(struct lruvec *lruvec,
|
|||
__mod_lruvec_state(lruvec, NR_LRU_BASE + lru, nr_pages);
|
||||
__mod_zone_page_state(&pgdat->node_zones[zid],
|
||||
NR_ZONE_LRU_BASE + lru, nr_pages);
|
||||
}
|
||||
|
||||
static __always_inline void update_lru_size(struct lruvec *lruvec,
|
||||
enum lru_list lru, enum zone_type zid,
|
||||
long nr_pages)
|
||||
{
|
||||
__update_lru_size(lruvec, lru, zid, nr_pages);
|
||||
#ifdef CONFIG_MEMCG
|
||||
mem_cgroup_update_lru_size(lruvec, lru, zid, nr_pages);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -213,7 +213,7 @@ static inline int ptep_test_and_clear_young(struct vm_area_struct *vma,
|
|||
#endif
|
||||
|
||||
#ifndef __HAVE_ARCH_PMDP_TEST_AND_CLEAR_YOUNG
|
||||
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
||||
#if defined(CONFIG_TRANSPARENT_HUGEPAGE) || defined(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG)
|
||||
static inline int pmdp_test_and_clear_young(struct vm_area_struct *vma,
|
||||
unsigned long address,
|
||||
pmd_t *pmdp)
|
||||
|
|
@ -234,7 +234,7 @@ static inline int pmdp_test_and_clear_young(struct vm_area_struct *vma,
|
|||
BUILD_BUG();
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
|
||||
#endif /* CONFIG_TRANSPARENT_HUGEPAGE || CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG */
|
||||
#endif
|
||||
|
||||
#ifndef __HAVE_ARCH_PTEP_CLEAR_YOUNG_FLUSH
|
||||
|
|
@ -260,6 +260,19 @@ static inline int pmdp_clear_flush_young(struct vm_area_struct *vma,
|
|||
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
|
||||
#endif
|
||||
|
||||
#ifndef arch_has_hw_pte_young
|
||||
/*
|
||||
* Return whether the accessed bit is supported on the local CPU.
|
||||
*
|
||||
* This stub assumes accessing through an old PTE triggers a page fault.
|
||||
* Architectures that automatically set the access bit should overwrite it.
|
||||
*/
|
||||
static inline bool arch_has_hw_pte_young(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef __HAVE_ARCH_PTEP_GET_AND_CLEAR
|
||||
static inline pte_t ptep_get_and_clear(struct mm_struct *mm,
|
||||
unsigned long address,
|
||||
|
|
|
|||
|
|
@ -956,6 +956,10 @@ struct task_struct {
|
|||
#ifdef CONFIG_CPU_SUP_INTEL
|
||||
unsigned reported_split_lock:1;
|
||||
#endif
|
||||
#ifdef CONFIG_TASK_DELAY_ACCT
|
||||
/* delay due to memory thrashing */
|
||||
unsigned in_thrashing:1;
|
||||
#endif
|
||||
|
||||
unsigned long atomic_flags; /* Flags requiring atomic access. */
|
||||
|
||||
|
|
|
|||
|
|
@ -214,13 +214,22 @@ void __delayacct_freepages_end(void)
|
|||
¤t->delays->freepages_count);
|
||||
}
|
||||
|
||||
void __delayacct_thrashing_start(void)
|
||||
void __delayacct_thrashing_start(bool *in_thrashing)
|
||||
{
|
||||
*in_thrashing = !!current->in_thrashing;
|
||||
if (*in_thrashing)
|
||||
return;
|
||||
|
||||
current->in_thrashing = 1;
|
||||
current->delays->thrashing_start = local_clock();
|
||||
}
|
||||
|
||||
void __delayacct_thrashing_end(void)
|
||||
void __delayacct_thrashing_end(bool *in_thrashing)
|
||||
{
|
||||
if (*in_thrashing)
|
||||
return;
|
||||
|
||||
current->in_thrashing = 0;
|
||||
delayacct_end(¤t->delays->lock,
|
||||
¤t->delays->thrashing_start,
|
||||
¤t->delays->thrashing_delay,
|
||||
|
|
|
|||
20
mm/filemap.c
20
mm/filemap.c
|
|
@ -1221,15 +1221,12 @@ static inline int folio_wait_bit_common(struct folio *folio, int bit_nr,
|
|||
struct wait_page_queue wait_page;
|
||||
wait_queue_entry_t *wait = &wait_page.wait;
|
||||
bool thrashing = false;
|
||||
bool delayacct = false;
|
||||
unsigned long pflags;
|
||||
bool in_thrashing;
|
||||
|
||||
if (bit_nr == PG_locked &&
|
||||
!folio_test_uptodate(folio) && folio_test_workingset(folio)) {
|
||||
if (!folio_test_swapbacked(folio)) {
|
||||
delayacct_thrashing_start();
|
||||
delayacct = true;
|
||||
}
|
||||
delayacct_thrashing_start(&in_thrashing);
|
||||
psi_memstall_enter(&pflags);
|
||||
thrashing = true;
|
||||
}
|
||||
|
|
@ -1329,8 +1326,7 @@ static inline int folio_wait_bit_common(struct folio *folio, int bit_nr,
|
|||
finish_wait(q, wait);
|
||||
|
||||
if (thrashing) {
|
||||
if (delayacct)
|
||||
delayacct_thrashing_end();
|
||||
delayacct_thrashing_end(&in_thrashing);
|
||||
psi_memstall_leave(&pflags);
|
||||
}
|
||||
|
||||
|
|
@ -1378,17 +1374,14 @@ void migration_entry_wait_on_locked(swp_entry_t entry, pte_t *ptep,
|
|||
struct wait_page_queue wait_page;
|
||||
wait_queue_entry_t *wait = &wait_page.wait;
|
||||
bool thrashing = false;
|
||||
bool delayacct = false;
|
||||
unsigned long pflags;
|
||||
bool in_thrashing;
|
||||
wait_queue_head_t *q;
|
||||
struct folio *folio = page_folio(pfn_swap_entry_to_page(entry));
|
||||
|
||||
q = folio_waitqueue(folio);
|
||||
if (!folio_test_uptodate(folio) && folio_test_workingset(folio)) {
|
||||
if (!folio_test_swapbacked(folio)) {
|
||||
delayacct_thrashing_start();
|
||||
delayacct = true;
|
||||
}
|
||||
delayacct_thrashing_start(&in_thrashing);
|
||||
psi_memstall_enter(&pflags);
|
||||
thrashing = true;
|
||||
}
|
||||
|
|
@ -1435,8 +1428,7 @@ void migration_entry_wait_on_locked(swp_entry_t entry, pte_t *ptep,
|
|||
finish_wait(q, wait);
|
||||
|
||||
if (thrashing) {
|
||||
if (delayacct)
|
||||
delayacct_thrashing_end();
|
||||
delayacct_thrashing_end(&in_thrashing);
|
||||
psi_memstall_leave(&pflags);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
14
mm/memory.c
14
mm/memory.c
|
|
@ -126,18 +126,6 @@ int randomize_va_space __read_mostly =
|
|||
2;
|
||||
#endif
|
||||
|
||||
#ifndef arch_faults_on_old_pte
|
||||
static inline bool arch_faults_on_old_pte(void)
|
||||
{
|
||||
/*
|
||||
* Those arches which don't have hw access flag feature need to
|
||||
* implement their own helper. By default, "true" means pagefault
|
||||
* will be hit on old pte.
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef arch_wants_old_prefaulted_pte
|
||||
static inline bool arch_wants_old_prefaulted_pte(void)
|
||||
{
|
||||
|
|
@ -2872,7 +2860,7 @@ static inline bool __wp_page_copy_user(struct page *dst, struct page *src,
|
|||
* On architectures with software "accessed" bits, we would
|
||||
* take a double page fault, so mark it accessed here.
|
||||
*/
|
||||
if (arch_faults_on_old_pte() && !pte_young(vmf->orig_pte)) {
|
||||
if (!arch_has_hw_pte_young() && !pte_young(vmf->orig_pte)) {
|
||||
pte_t entry;
|
||||
|
||||
vmf->pte = pte_offset_map_lock(mm, vmf->pmd, addr, &vmf->ptl);
|
||||
|
|
|
|||
52
mm/migrate.c
52
mm/migrate.c
|
|
@ -1260,10 +1260,8 @@ static int unmap_and_move_huge_page(new_page_t get_new_page,
|
|||
* tables or check whether the hugepage is pmd-based or not before
|
||||
* kicking migration.
|
||||
*/
|
||||
if (!hugepage_migration_supported(page_hstate(hpage))) {
|
||||
list_move_tail(&hpage->lru, ret);
|
||||
if (!hugepage_migration_supported(page_hstate(hpage)))
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
if (page_count(hpage) == 1) {
|
||||
/* page was freed from under us. So we are done. */
|
||||
|
|
@ -1369,16 +1367,15 @@ static int unmap_and_move_huge_page(new_page_t get_new_page,
|
|||
return rc;
|
||||
}
|
||||
|
||||
static inline int try_split_thp(struct page *page, struct page **page2,
|
||||
struct list_head *from)
|
||||
static inline int try_split_thp(struct page *page, struct list_head *split_pages)
|
||||
{
|
||||
int rc = 0;
|
||||
int rc;
|
||||
|
||||
lock_page(page);
|
||||
rc = split_huge_page_to_list(page, from);
|
||||
rc = split_huge_page_to_list(page, split_pages);
|
||||
unlock_page(page);
|
||||
if (!rc)
|
||||
list_safe_reset_next(page, *page2, lru);
|
||||
list_move_tail(&page->lru, split_pages);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
|
@ -1416,6 +1413,7 @@ int migrate_pages(struct list_head *from, new_page_t get_new_page,
|
|||
int thp_retry = 1;
|
||||
int nr_failed = 0;
|
||||
int nr_failed_pages = 0;
|
||||
int nr_retry_pages = 0;
|
||||
int nr_succeeded = 0;
|
||||
int nr_thp_succeeded = 0;
|
||||
int nr_thp_failed = 0;
|
||||
|
|
@ -1436,9 +1434,9 @@ int migrate_pages(struct list_head *from, new_page_t get_new_page,
|
|||
for (pass = 0; pass < 10 && (retry || thp_retry); pass++) {
|
||||
retry = 0;
|
||||
thp_retry = 0;
|
||||
nr_retry_pages = 0;
|
||||
|
||||
list_for_each_entry_safe(page, page2, from, lru) {
|
||||
retry:
|
||||
/*
|
||||
* THP statistics is based on the source huge page.
|
||||
* Capture required information that might get lost
|
||||
|
|
@ -1463,6 +1461,7 @@ int migrate_pages(struct list_head *from, new_page_t get_new_page,
|
|||
* page will be put back
|
||||
* -EAGAIN: stay on the from list
|
||||
* -ENOMEM: stay on the from list
|
||||
* -ENOSYS: stay on the from list
|
||||
* Other errno: put on ret_pages list then splice to
|
||||
* from list
|
||||
*/
|
||||
|
|
@ -1473,18 +1472,17 @@ int migrate_pages(struct list_head *from, new_page_t get_new_page,
|
|||
* retry on the same page with the THP split
|
||||
* to base pages.
|
||||
*
|
||||
* Head page is retried immediately and tail
|
||||
* pages are added to the tail of the list so
|
||||
* we encounter them after the rest of the list
|
||||
* is processed.
|
||||
* Sub-pages are put in thp_split_pages, and
|
||||
* we will migrate them after the rest of the
|
||||
* list is processed.
|
||||
*/
|
||||
case -ENOSYS:
|
||||
/* THP migration is unsupported */
|
||||
if (is_thp) {
|
||||
nr_thp_failed++;
|
||||
if (!try_split_thp(page, &page2, &thp_split_pages)) {
|
||||
if (!try_split_thp(page, &thp_split_pages)) {
|
||||
nr_thp_split++;
|
||||
goto retry;
|
||||
break;
|
||||
}
|
||||
/* Hugetlb migration is unsupported */
|
||||
} else if (!no_subpage_counting) {
|
||||
|
|
@ -1492,24 +1490,25 @@ int migrate_pages(struct list_head *from, new_page_t get_new_page,
|
|||
}
|
||||
|
||||
nr_failed_pages += nr_subpages;
|
||||
list_move_tail(&page->lru, &ret_pages);
|
||||
break;
|
||||
case -ENOMEM:
|
||||
/*
|
||||
* When memory is low, don't bother to try to migrate
|
||||
* other pages, just exit.
|
||||
* THP NUMA faulting doesn't split THP to retry.
|
||||
*/
|
||||
if (is_thp && !nosplit) {
|
||||
if (is_thp) {
|
||||
nr_thp_failed++;
|
||||
if (!try_split_thp(page, &page2, &thp_split_pages)) {
|
||||
/* THP NUMA faulting doesn't split THP to retry. */
|
||||
if (!nosplit && !try_split_thp(page, &thp_split_pages)) {
|
||||
nr_thp_split++;
|
||||
goto retry;
|
||||
break;
|
||||
}
|
||||
} else if (!no_subpage_counting) {
|
||||
nr_failed++;
|
||||
}
|
||||
|
||||
nr_failed_pages += nr_subpages;
|
||||
nr_failed_pages += nr_subpages + nr_retry_pages;
|
||||
/*
|
||||
* There might be some subpages of fail-to-migrate THPs
|
||||
* left in thp_split_pages list. Move them back to migration
|
||||
|
|
@ -1517,13 +1516,15 @@ int migrate_pages(struct list_head *from, new_page_t get_new_page,
|
|||
* the caller otherwise the page refcnt will be leaked.
|
||||
*/
|
||||
list_splice_init(&thp_split_pages, from);
|
||||
/* nr_failed isn't updated for not used */
|
||||
nr_thp_failed += thp_retry;
|
||||
goto out;
|
||||
case -EAGAIN:
|
||||
if (is_thp)
|
||||
thp_retry++;
|
||||
else
|
||||
else if (!no_subpage_counting)
|
||||
retry++;
|
||||
nr_retry_pages += nr_subpages;
|
||||
break;
|
||||
case MIGRATEPAGE_SUCCESS:
|
||||
nr_succeeded += nr_subpages;
|
||||
|
|
@ -1549,6 +1550,7 @@ int migrate_pages(struct list_head *from, new_page_t get_new_page,
|
|||
}
|
||||
nr_failed += retry;
|
||||
nr_thp_failed += thp_retry;
|
||||
nr_failed_pages += nr_retry_pages;
|
||||
/*
|
||||
* Try to migrate subpages of fail-to-migrate THPs, no nr_failed
|
||||
* counting in this round, since all subpages of a THP is counted
|
||||
|
|
@ -1751,7 +1753,7 @@ static int move_pages_and_store_status(struct mm_struct *mm, int node,
|
|||
* well.
|
||||
*/
|
||||
if (err > 0)
|
||||
err += nr_pages - i - 1;
|
||||
err += nr_pages - i;
|
||||
return err;
|
||||
}
|
||||
return store_status(status, start, node, i - start);
|
||||
|
|
@ -1837,8 +1839,12 @@ static int do_pages_move(struct mm_struct *mm, nodemask_t task_nodes,
|
|||
|
||||
err = move_pages_and_store_status(mm, current_node, &pagelist,
|
||||
status, start, i, nr_pages);
|
||||
if (err)
|
||||
if (err) {
|
||||
/* We have accounted for page i */
|
||||
if (err > 0)
|
||||
err--;
|
||||
goto out;
|
||||
}
|
||||
current_node = NUMA_NO_NODE;
|
||||
}
|
||||
out_flush:
|
||||
|
|
|
|||
15
mm/page_io.c
15
mm/page_io.c
|
|
@ -452,18 +452,21 @@ int swap_readpage(struct page *page, bool synchronous,
|
|||
struct swap_info_struct *sis = page_swap_info(page);
|
||||
bool workingset = PageWorkingset(page);
|
||||
unsigned long pflags;
|
||||
bool in_thrashing;
|
||||
|
||||
VM_BUG_ON_PAGE(!PageSwapCache(page) && !synchronous, page);
|
||||
VM_BUG_ON_PAGE(!PageLocked(page), page);
|
||||
VM_BUG_ON_PAGE(PageUptodate(page), page);
|
||||
|
||||
/*
|
||||
* Count submission time as memory stall. When the device is congested,
|
||||
* or the submitting cgroup IO-throttled, submission can be a
|
||||
* significant part of overall IO time.
|
||||
* Count submission time as memory stall and delay. When the device
|
||||
* is congested, or the submitting cgroup IO-throttled, submission
|
||||
* can be a significant part of overall IO time.
|
||||
*/
|
||||
if (workingset)
|
||||
if (workingset) {
|
||||
delayacct_thrashing_start(&in_thrashing);
|
||||
psi_memstall_enter(&pflags);
|
||||
}
|
||||
delayacct_swapin_start();
|
||||
|
||||
if (frontswap_load(page) == 0) {
|
||||
|
|
@ -512,8 +515,10 @@ int swap_readpage(struct page *page, bool synchronous,
|
|||
bio_put(bio);
|
||||
|
||||
out:
|
||||
if (workingset)
|
||||
if (workingset) {
|
||||
delayacct_thrashing_end(&in_thrashing);
|
||||
psi_memstall_leave(&pflags);
|
||||
}
|
||||
delayacct_swapin_end();
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
198
mm/vmscan.c
198
mm/vmscan.c
|
|
@ -2731,6 +2731,109 @@ enum scan_balance {
|
|||
SCAN_FILE,
|
||||
};
|
||||
|
||||
static void prepare_scan_count(pg_data_t *pgdat, struct scan_control *sc)
|
||||
{
|
||||
unsigned long file;
|
||||
struct lruvec *target_lruvec;
|
||||
|
||||
target_lruvec = mem_cgroup_lruvec(sc->target_mem_cgroup, pgdat);
|
||||
|
||||
/*
|
||||
* Flush the memory cgroup stats, so that we read accurate per-memcg
|
||||
* lruvec stats for heuristics.
|
||||
*/
|
||||
mem_cgroup_flush_stats();
|
||||
|
||||
/*
|
||||
* Determine the scan balance between anon and file LRUs.
|
||||
*/
|
||||
spin_lock_irq(&target_lruvec->lru_lock);
|
||||
sc->anon_cost = target_lruvec->anon_cost;
|
||||
sc->file_cost = target_lruvec->file_cost;
|
||||
spin_unlock_irq(&target_lruvec->lru_lock);
|
||||
|
||||
/*
|
||||
* Target desirable inactive:active list ratios for the anon
|
||||
* and file LRU lists.
|
||||
*/
|
||||
if (!sc->force_deactivate) {
|
||||
unsigned long refaults;
|
||||
|
||||
/*
|
||||
* When refaults are being observed, it means a new
|
||||
* workingset is being established. Deactivate to get
|
||||
* rid of any stale active pages quickly.
|
||||
*/
|
||||
refaults = lruvec_page_state(target_lruvec,
|
||||
WORKINGSET_ACTIVATE_ANON);
|
||||
if (refaults != target_lruvec->refaults[WORKINGSET_ANON] ||
|
||||
inactive_is_low(target_lruvec, LRU_INACTIVE_ANON))
|
||||
sc->may_deactivate |= DEACTIVATE_ANON;
|
||||
else
|
||||
sc->may_deactivate &= ~DEACTIVATE_ANON;
|
||||
|
||||
refaults = lruvec_page_state(target_lruvec,
|
||||
WORKINGSET_ACTIVATE_FILE);
|
||||
if (refaults != target_lruvec->refaults[WORKINGSET_FILE] ||
|
||||
inactive_is_low(target_lruvec, LRU_INACTIVE_FILE))
|
||||
sc->may_deactivate |= DEACTIVATE_FILE;
|
||||
else
|
||||
sc->may_deactivate &= ~DEACTIVATE_FILE;
|
||||
} else
|
||||
sc->may_deactivate = DEACTIVATE_ANON | DEACTIVATE_FILE;
|
||||
|
||||
/*
|
||||
* If we have plenty of inactive file pages that aren't
|
||||
* thrashing, try to reclaim those first before touching
|
||||
* anonymous pages.
|
||||
*/
|
||||
file = lruvec_page_state(target_lruvec, NR_INACTIVE_FILE);
|
||||
if (file >> sc->priority && !(sc->may_deactivate & DEACTIVATE_FILE))
|
||||
sc->cache_trim_mode = 1;
|
||||
else
|
||||
sc->cache_trim_mode = 0;
|
||||
|
||||
/*
|
||||
* Prevent the reclaimer from falling into the cache trap: as
|
||||
* cache pages start out inactive, every cache fault will tip
|
||||
* the scan balance towards the file LRU. And as the file LRU
|
||||
* shrinks, so does the window for rotation from references.
|
||||
* This means we have a runaway feedback loop where a tiny
|
||||
* thrashing file LRU becomes infinitely more attractive than
|
||||
* anon pages. Try to detect this based on file LRU size.
|
||||
*/
|
||||
if (!cgroup_reclaim(sc)) {
|
||||
unsigned long total_high_wmark = 0;
|
||||
unsigned long free, anon;
|
||||
int z;
|
||||
|
||||
free = sum_zone_node_page_state(pgdat->node_id, NR_FREE_PAGES);
|
||||
file = node_page_state(pgdat, NR_ACTIVE_FILE) +
|
||||
node_page_state(pgdat, NR_INACTIVE_FILE);
|
||||
|
||||
for (z = 0; z < MAX_NR_ZONES; z++) {
|
||||
struct zone *zone = &pgdat->node_zones[z];
|
||||
|
||||
if (!managed_zone(zone))
|
||||
continue;
|
||||
|
||||
total_high_wmark += high_wmark_pages(zone);
|
||||
}
|
||||
|
||||
/*
|
||||
* Consider anon: if that's low too, this isn't a
|
||||
* runaway file reclaim problem, but rather just
|
||||
* extreme pressure. Reclaim as per usual then.
|
||||
*/
|
||||
anon = node_page_state(pgdat, NR_INACTIVE_ANON);
|
||||
|
||||
sc->file_is_tiny =
|
||||
file + free <= total_high_wmark &&
|
||||
!(sc->may_deactivate & DEACTIVATE_ANON) &&
|
||||
anon >> sc->priority;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine how aggressively the anon and file LRU lists should be
|
||||
* scanned.
|
||||
|
|
@ -3205,109 +3308,16 @@ static void shrink_node(pg_data_t *pgdat, struct scan_control *sc)
|
|||
unsigned long nr_reclaimed, nr_scanned;
|
||||
struct lruvec *target_lruvec;
|
||||
bool reclaimable = false;
|
||||
unsigned long file;
|
||||
|
||||
target_lruvec = mem_cgroup_lruvec(sc->target_mem_cgroup, pgdat);
|
||||
|
||||
again:
|
||||
/*
|
||||
* Flush the memory cgroup stats, so that we read accurate per-memcg
|
||||
* lruvec stats for heuristics.
|
||||
*/
|
||||
mem_cgroup_flush_stats();
|
||||
|
||||
memset(&sc->nr, 0, sizeof(sc->nr));
|
||||
|
||||
nr_reclaimed = sc->nr_reclaimed;
|
||||
nr_scanned = sc->nr_scanned;
|
||||
|
||||
/*
|
||||
* Determine the scan balance between anon and file LRUs.
|
||||
*/
|
||||
spin_lock_irq(&target_lruvec->lru_lock);
|
||||
sc->anon_cost = target_lruvec->anon_cost;
|
||||
sc->file_cost = target_lruvec->file_cost;
|
||||
spin_unlock_irq(&target_lruvec->lru_lock);
|
||||
|
||||
/*
|
||||
* Target desirable inactive:active list ratios for the anon
|
||||
* and file LRU lists.
|
||||
*/
|
||||
if (!sc->force_deactivate) {
|
||||
unsigned long refaults;
|
||||
|
||||
/*
|
||||
* When refaults are being observed, it means a new
|
||||
* workingset is being established. Deactivate to get
|
||||
* rid of any stale active pages quickly.
|
||||
*/
|
||||
refaults = lruvec_page_state(target_lruvec,
|
||||
WORKINGSET_ACTIVATE_ANON);
|
||||
if (refaults != target_lruvec->refaults[WORKINGSET_ANON] ||
|
||||
inactive_is_low(target_lruvec, LRU_INACTIVE_ANON))
|
||||
sc->may_deactivate |= DEACTIVATE_ANON;
|
||||
else
|
||||
sc->may_deactivate &= ~DEACTIVATE_ANON;
|
||||
|
||||
refaults = lruvec_page_state(target_lruvec,
|
||||
WORKINGSET_ACTIVATE_FILE);
|
||||
if (refaults != target_lruvec->refaults[WORKINGSET_FILE] ||
|
||||
inactive_is_low(target_lruvec, LRU_INACTIVE_FILE))
|
||||
sc->may_deactivate |= DEACTIVATE_FILE;
|
||||
else
|
||||
sc->may_deactivate &= ~DEACTIVATE_FILE;
|
||||
} else
|
||||
sc->may_deactivate = DEACTIVATE_ANON | DEACTIVATE_FILE;
|
||||
|
||||
/*
|
||||
* If we have plenty of inactive file pages that aren't
|
||||
* thrashing, try to reclaim those first before touching
|
||||
* anonymous pages.
|
||||
*/
|
||||
file = lruvec_page_state(target_lruvec, NR_INACTIVE_FILE);
|
||||
if (file >> sc->priority && !(sc->may_deactivate & DEACTIVATE_FILE))
|
||||
sc->cache_trim_mode = 1;
|
||||
else
|
||||
sc->cache_trim_mode = 0;
|
||||
|
||||
/*
|
||||
* Prevent the reclaimer from falling into the cache trap: as
|
||||
* cache pages start out inactive, every cache fault will tip
|
||||
* the scan balance towards the file LRU. And as the file LRU
|
||||
* shrinks, so does the window for rotation from references.
|
||||
* This means we have a runaway feedback loop where a tiny
|
||||
* thrashing file LRU becomes infinitely more attractive than
|
||||
* anon pages. Try to detect this based on file LRU size.
|
||||
*/
|
||||
if (!cgroup_reclaim(sc)) {
|
||||
unsigned long total_high_wmark = 0;
|
||||
unsigned long free, anon;
|
||||
int z;
|
||||
|
||||
free = sum_zone_node_page_state(pgdat->node_id, NR_FREE_PAGES);
|
||||
file = node_page_state(pgdat, NR_ACTIVE_FILE) +
|
||||
node_page_state(pgdat, NR_INACTIVE_FILE);
|
||||
|
||||
for (z = 0; z < MAX_NR_ZONES; z++) {
|
||||
struct zone *zone = &pgdat->node_zones[z];
|
||||
if (!managed_zone(zone))
|
||||
continue;
|
||||
|
||||
total_high_wmark += high_wmark_pages(zone);
|
||||
}
|
||||
|
||||
/*
|
||||
* Consider anon: if that's low too, this isn't a
|
||||
* runaway file reclaim problem, but rather just
|
||||
* extreme pressure. Reclaim as per usual then.
|
||||
*/
|
||||
anon = node_page_state(pgdat, NR_INACTIVE_ANON);
|
||||
|
||||
sc->file_is_tiny =
|
||||
file + free <= total_high_wmark &&
|
||||
!(sc->may_deactivate & DEACTIVATE_ANON) &&
|
||||
anon >> sc->priority;
|
||||
}
|
||||
prepare_scan_count(pgdat, sc);
|
||||
|
||||
shrink_node_memcgs(pgdat, sc);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user