ANDROID: mm: add cma allocation statistics

alloc_contig_range is the core worker function for CMA allocation
so it has every information to be able to understand allocation
latency. For example, how many pages are migrated, how many time
unmap was needed to migrate pages, how many times it encountered
errors by some reasons.

This patch adds such statistics in the alloc_contig_range and
return it to user so user can use those information to analyize
latency. The cma_alloc is first user for the statistics, which
export the statistics as new trace event(i.e., cma_alloc_info).

It was really usefuli to optimize cma allocation work.

Bug: 192475091
Signed-off-by: Minchan Kim <minchan@google.com>
Change-Id: I7be43cc89d11078e2a324d2d06aada6d8e9e1cc9
Signed-off-by: Richard Chang <richardycc@google.com>
(cherry picked from commit 675e504598)
This commit is contained in:
Minchan Kim
2021-06-29 11:28:13 -07:00
committed by Richard Chang
parent 1b2de5aa2d
commit a7f55c5c73
6 changed files with 105 additions and 8 deletions

View File

@@ -1144,6 +1144,7 @@ static int virtio_mem_fake_offline(unsigned long pfn, unsigned long nr_pages)
const bool is_movable = page_zonenum(pfn_to_page(pfn)) ==
ZONE_MOVABLE;
int rc, retry_count;
struct acr_info dummy;
/*
* TODO: We want an alloc_contig_range() mode that tries to allocate
@@ -1154,7 +1155,7 @@ static int virtio_mem_fake_offline(unsigned long pfn, unsigned long nr_pages)
*/
for (retry_count = 0; retry_count < 5; retry_count++) {
rc = alloc_contig_range(pfn, pfn + nr_pages, MIGRATE_MOVABLE,
GFP_KERNEL);
GFP_KERNEL, &dummy);
if (rc == -ENOMEM)
/* whoops, out of memory */
return rc;

View File

@@ -22,6 +22,15 @@
struct cma;
struct cma_alloc_info {
unsigned long nr_migrated;
unsigned long nr_reclaimed;
unsigned long nr_mapped;
unsigned int nr_isolate_fail;
unsigned int nr_migrate_fail;
unsigned int nr_test_fail;
};
extern unsigned long totalcma_pages;
extern phys_addr_t cma_get_base(const struct cma *cma);
extern unsigned long cma_get_size(const struct cma *cma);

View File

@@ -693,9 +693,21 @@ static inline bool pm_suspended_storage(void)
#endif /* CONFIG_PM_SLEEP */
#ifdef CONFIG_CONTIG_ALLOC
#define ACR_ERR_ISOLATE (1 << 0)
#define ACR_ERR_MIGRATE (1 << 1)
#define ACR_ERR_TEST (1 << 2)
struct acr_info {
unsigned long nr_mapped;
unsigned long nr_migrated;
unsigned long nr_reclaimed;
unsigned int err;
};
/* The below functions must be run on a range from a single zone. */
extern int alloc_contig_range(unsigned long start, unsigned long end,
unsigned migratetype, gfp_t gfp_mask);
unsigned migratetype, gfp_t gfp_mask,
struct acr_info *info);
extern struct page *alloc_contig_pages(unsigned long nr_pages, gfp_t gfp_mask,
int nid, nodemask_t *nodemask);
#endif

View File

@@ -91,6 +91,51 @@ TRACE_EVENT(cma_alloc_start,
__entry->align)
);
TRACE_EVENT(cma_alloc_info,
TP_PROTO(const char *name, const struct page *page, unsigned int count, unsigned int align, struct cma_alloc_info *info),
TP_ARGS(name, page, count, align, info),
TP_STRUCT__entry(
__string(name, name)
__field(unsigned long, pfn)
__field(unsigned int, count)
__field(unsigned int, align)
__field(unsigned long, nr_migrated)
__field(unsigned long, nr_reclaimed)
__field(unsigned long, nr_mapped)
__field(unsigned int, err_iso)
__field(unsigned int, err_mig)
__field(unsigned int, err_test)
),
TP_fast_assign(
__assign_str(name, name);
__entry->pfn = page ? page_to_pfn(page) : -1;
__entry->count = count;
__entry->align = align;
__entry->nr_migrated = info->nr_migrated;
__entry->nr_reclaimed = info->nr_reclaimed;
__entry->nr_mapped = info->nr_mapped;
__entry->err_iso = info->nr_isolate_fail;
__entry->err_mig = info->nr_migrate_fail;
__entry->err_test = info->nr_test_fail;
),
TP_printk("name=%s pfn=0x%lx count=%u align=%u nr_migrated=%lu nr_reclaimed=%lu nr_mapped=%lu err_iso=%u err_mig=%u err_test=%u",
__get_str(name),
__entry->pfn,
__entry->count,
__entry->align,
__entry->nr_migrated,
__entry->nr_reclaimed,
__entry->nr_mapped,
__entry->err_iso,
__entry->err_mig,
__entry->err_test)
);
DEFINE_EVENT(cma_alloc_class, cma_alloc_finish,
TP_PROTO(const char *name, unsigned long pfn, const struct page *page,

View File

@@ -444,6 +444,7 @@ struct page *cma_alloc(struct cma *cma, unsigned long count,
int num_attempts = 0;
int max_retries = 5;
s64 ts;
struct cma_alloc_info cma_info = {0};
trace_android_vh_cma_alloc_start(&ts);
@@ -467,6 +468,8 @@ struct page *cma_alloc(struct cma *cma, unsigned long count,
goto out;
for (;;) {
struct acr_info info = {0};
spin_lock_irq(&cma->lock);
bitmap_no = bitmap_find_next_zero_area_off(cma->bitmap,
bitmap_maxno, start, bitmap_count, mask,
@@ -505,7 +508,18 @@ struct page *cma_alloc(struct cma *cma, unsigned long count,
pfn = cma->base_pfn + (bitmap_no << cma->order_per_bit);
mutex_lock(&cma_mutex);
ret = alloc_contig_range(pfn, pfn + count, MIGRATE_CMA, gfp_mask);
ret = alloc_contig_range(pfn, pfn + count, MIGRATE_CMA, gfp_mask, &info);
cma_info.nr_migrated += info.nr_migrated;
cma_info.nr_reclaimed += info.nr_reclaimed;
cma_info.nr_mapped += info.nr_mapped;
if (info.err) {
if (info.err & ACR_ERR_ISOLATE)
cma_info.nr_isolate_fail++;
if (info.err & ACR_ERR_MIGRATE)
cma_info.nr_migrate_fail++;
if (info.err & ACR_ERR_TEST)
cma_info.nr_test_fail++;
}
mutex_unlock(&cma_mutex);
if (ret == 0) {
page = pfn_to_page(pfn);
@@ -526,6 +540,7 @@ struct page *cma_alloc(struct cma *cma, unsigned long count,
}
trace_cma_alloc_finish(cma->name, pfn, page, count, align);
trace_cma_alloc_info(cma->name, page, count, align, &cma_info);
/*
* CMA can allocate multiple page blocks, which results in different

View File

@@ -9140,7 +9140,8 @@ static inline void alloc_contig_dump_pages(struct list_head *page_list)
/* [start, end) must belong to a single zone. */
static int __alloc_contig_migrate_range(struct compact_control *cc,
unsigned long start, unsigned long end)
unsigned long start, unsigned long end,
struct acr_info *info)
{
/* This function is based on compact_zone() from compaction.c. */
unsigned int nr_reclaimed;
@@ -9148,6 +9149,7 @@ static int __alloc_contig_migrate_range(struct compact_control *cc,
unsigned int tries = 0;
unsigned int max_tries = 5;
int ret = 0;
struct page *page;
struct migration_target_control mtc = {
.nid = zone_to_nid(cc->zone),
.gfp_mask = GFP_USER | __GFP_MOVABLE | __GFP_RETRY_MAYFAIL,
@@ -9178,11 +9180,18 @@ static int __alloc_contig_migrate_range(struct compact_control *cc,
nr_reclaimed = reclaim_clean_pages_from_list(cc->zone,
&cc->migratepages);
info->nr_reclaimed += nr_reclaimed;
cc->nr_migratepages -= nr_reclaimed;
list_for_each_entry(page, &cc->migratepages, lru)
info->nr_mapped += page_mapcount(page);
ret = migrate_pages(&cc->migratepages, alloc_migration_target,
NULL, (unsigned long)&mtc, cc->mode, MR_CONTIG_RANGE, NULL);
if (!ret)
info->nr_migrated += cc->nr_migratepages;
/*
* On -ENOMEM, migrate_pages() bails out right away. It is pointless
* to retry again over this error, so do the same here.
@@ -9205,6 +9214,7 @@ static int __alloc_contig_migrate_range(struct compact_control *cc,
}
}
putback_movable_pages(&cc->migratepages);
info->err |= ACR_ERR_MIGRATE;
return ret;
}
return 0;
@@ -9232,7 +9242,8 @@ static int __alloc_contig_migrate_range(struct compact_control *cc,
* need to be freed with free_contig_range().
*/
int alloc_contig_range(unsigned long start, unsigned long end,
unsigned migratetype, gfp_t gfp_mask)
unsigned migratetype, gfp_t gfp_mask,
struct acr_info *info)
{
unsigned long outer_start, outer_end;
unsigned int order;
@@ -9277,8 +9288,10 @@ int alloc_contig_range(unsigned long start, unsigned long end,
ret = start_isolate_page_range(pfn_max_align_down(start),
pfn_max_align_up(end), migratetype, 0);
if (ret)
if (ret) {
info->err |= ACR_ERR_ISOLATE;
return ret;
}
trace_android_vh_cma_drain_all_pages_bypass(migratetype,
&skip_drain_all_pages);
@@ -9295,7 +9308,7 @@ int alloc_contig_range(unsigned long start, unsigned long end,
* allocated. So, if we fall through be sure to clear ret so that
* -EBUSY is not accidentally used or returned to caller.
*/
ret = __alloc_contig_migrate_range(&cc, start, end);
ret = __alloc_contig_migrate_range(&cc, start, end, info);
if (ret && ret != -EBUSY)
goto done;
ret = 0;
@@ -9343,6 +9356,7 @@ int alloc_contig_range(unsigned long start, unsigned long end,
/* Make sure the range is really isolated. */
if (test_pages_isolated(outer_start, end, 0)) {
ret = -EBUSY;
info->err |= ACR_ERR_TEST;
goto done;
}
@@ -9369,10 +9383,11 @@ EXPORT_SYMBOL(alloc_contig_range);
static int __alloc_contig_pages(unsigned long start_pfn,
unsigned long nr_pages, gfp_t gfp_mask)
{
struct acr_info dummy;
unsigned long end_pfn = start_pfn + nr_pages;
return alloc_contig_range(start_pfn, end_pfn, MIGRATE_MOVABLE,
gfp_mask);
gfp_mask, &dummy);
}
static bool pfn_range_valid_contig(struct zone *z, unsigned long start_pfn,