Files
android_kernel_xiaomi_sm8450/include/linux/page_ext.h
Minchan Kim ddc4a48797 ANDROID: mm: page_pinner: introduce failure_tracking feature
CMA allocation can fail by temporal page refcount increasement
by get_page API as well as get_user_pages friends.
However, since get_page is one of the most hot function, it is
hard to hook get_page to get callstack everytime due to
performance concern. Furthermore, get_page could be nested
multiple times so we couldn't track all of the pin sites on
limited space of page_pinner.

Thus, here approach is keep tracking of put_page callsite rather
than get_page once VM found the page migration failed.
It's based on assumption:

1. Since it's temporal page refcount, it could be released soon
   before overflowing dmesg log buffer
2. developer can find the pair of get_page by reviewing put_page.

By default, it's eanbled. If you want to disable it:

  echo 0 > $debugfs/page_pinner/failure_tracking

You can capture the tracking using:

  cat $debugfs/page_pinner/alloc_contig_failed

note: the example below is artificial:

Page pinned ts 386067292 us count 0
PFN 10162530 Block 9924 type Isolate Flags 0x800000000008000c(uptodate|dirty|swapbacked)
 __page_pinner_migration_failed+0x30/0x104
 putback_lru_page+0x90/0xac
 putback_movable_pages+0xc4/0x204
 __alloc_contig_migrate_range+0x290/0x31c
 alloc_contig_range+0x114/0x2bc
 cma_alloc+0x2d8/0x698
 cma_alloc_write+0x58/0xb8
 simple_attr_write+0xd4/0x124
 debugfs_attr_write+0x50/0xd8
 full_proxy_write+0x70/0xf8
 vfs_write+0x168/0x3a8
 ksys_write+0x7c/0xec
 __arm64_sys_write+0x20/0x30
 el0_svc_common+0xa4/0x180
 do_el0_svc+0x28/0x88
 el0_svc+0x14/0x24

Page pinned ts 385867394 us count 0
PFN 10162530 Block 9924 type Isolate Flags 0x800000000008000c(uptodate|dirty|swapbacked)
 __page_pinner_migration_failed+0x30/0x104
 __alloc_contig_migrate_range+0x200/0x31c
 alloc_contig_range+0x114/0x2bc
 cma_alloc+0x2d8/0x698
 cma_alloc_write+0x58/0xb8
 simple_attr_write+0xd4/0x124
 debugfs_attr_write+0x50/0xd8
 full_proxy_write+0x70/0xf8
 vfs_write+0x168/0x3a8
 ksys_write+0x7c/0xec
 __arm64_sys_write+0x20/0x30
 el0_svc_common+0xa4/0x180
 do_el0_svc+0x28/0x88
 el0_svc+0x14/0x24
 el0_sync_handler+0x88/0xec
 el0_sync+0x198/0x1c0

Bug: 183414571
Signed-off-by: Minchan Kim <minchan@kernel.org>
Signed-off-by: Minchan Kim <minchan@google.com>
Change-Id: Ie79902c18390eb9f320d823839bb9d9a7fdcdb31
2021-04-30 09:13:34 -07:00

90 lines
1.9 KiB
C

/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __LINUX_PAGE_EXT_H
#define __LINUX_PAGE_EXT_H
#include <linux/types.h>
#include <linux/stacktrace.h>
#include <linux/stackdepot.h>
struct pglist_data;
struct page_ext_operations {
size_t offset;
size_t size;
bool (*need)(void);
void (*init)(void);
};
#ifdef CONFIG_PAGE_EXTENSION
enum page_ext_flags {
PAGE_EXT_OWNER,
PAGE_EXT_OWNER_ALLOCATED,
#if defined(CONFIG_PAGE_PINNER)
/* page refcount was increased by GUP or follow_page(FOLL_GET) */
PAGE_EXT_GET,
/* page migration failed */
PAGE_EXT_PINNER_MIGRATION_FAILED,
#endif
#if defined(CONFIG_IDLE_PAGE_TRACKING) && !defined(CONFIG_64BIT)
PAGE_EXT_YOUNG,
PAGE_EXT_IDLE,
#endif
};
/*
* Page Extension can be considered as an extended mem_map.
* A page_ext page is associated with every page descriptor. The
* page_ext helps us add more information about the page.
* All page_ext are allocated at boot or memory hotplug event,
* then the page_ext for pfn always exists.
*/
struct page_ext {
unsigned long flags;
};
extern unsigned long page_ext_size;
extern void pgdat_page_ext_init(struct pglist_data *pgdat);
#ifdef CONFIG_SPARSEMEM
static inline void page_ext_init_flatmem(void)
{
}
extern void page_ext_init(void);
#else
extern void page_ext_init_flatmem(void);
static inline void page_ext_init(void)
{
}
#endif
struct page_ext *lookup_page_ext(const struct page *page);
static inline struct page_ext *page_ext_next(struct page_ext *curr)
{
void *next = curr;
next += page_ext_size;
return next;
}
#else /* !CONFIG_PAGE_EXTENSION */
struct page_ext;
static inline void pgdat_page_ext_init(struct pglist_data *pgdat)
{
}
static inline struct page_ext *lookup_page_ext(const struct page *page)
{
return NULL;
}
static inline void page_ext_init(void)
{
}
static inline void page_ext_init_flatmem(void)
{
}
#endif /* CONFIG_PAGE_EXTENSION */
#endif /* __LINUX_PAGE_EXT_H */