mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 20:07:46 +09:00
mm: add cma debug interafce [1/1]
PD#GH-17 Problem: sometimes cma allocation failed but it's hard to get failed log and need recompile code. Solution: Add /proc/cma_debug to open cma debug message dynamic. By default, no debug message is printed. If you write a value large than 0 to this node, debug message will be printed. Verify: p212 Change-Id: Ibcfd1d48be5f33f674f09df713dc2e493748c405 Signed-off-by: tao zeng <tao.zeng@amlogic.com>
This commit is contained in:
@@ -30,11 +30,14 @@
|
||||
#include <linux/spinlock_types.h>
|
||||
#include <linux/amlogic/aml_cma.h>
|
||||
#include <linux/hugetlb.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <trace/events/page_isolation.h>
|
||||
#ifdef CONFIG_AMLOGIC_PAGE_TRACE
|
||||
#include <linux/amlogic/page_trace.h>
|
||||
#endif /* CONFIG_AMLOGIC_PAGE_TRACE */
|
||||
|
||||
#define MAX_DEBUG_LEVEL 5
|
||||
|
||||
struct work_cma {
|
||||
struct list_head list;
|
||||
unsigned long pfn;
|
||||
@@ -52,6 +55,8 @@ struct cma_pcp {
|
||||
|
||||
static bool can_boost;
|
||||
static DEFINE_PER_CPU(struct cma_pcp, cma_pcp_thread);
|
||||
static struct proc_dir_entry *dentry;
|
||||
int cma_debug_level;
|
||||
|
||||
DEFINE_SPINLOCK(cma_iso_lock);
|
||||
static atomic_t cma_allocate;
|
||||
@@ -261,6 +266,7 @@ static int aml_alloc_contig_migrate_range(struct compact_control *cc,
|
||||
pfn = isolate_migratepages_range(cc, pfn, end);
|
||||
if (!pfn) {
|
||||
ret = -EINTR;
|
||||
cma_debug(1, NULL, " iso migrate page fail\n");
|
||||
break;
|
||||
}
|
||||
tries = 0;
|
||||
@@ -336,7 +342,7 @@ static int cma_boost_work_func(void *cma_data)
|
||||
drain_local_pages(NULL);
|
||||
}
|
||||
if (ret)
|
||||
pr_debug("%s, failed, ret:%d\n", __func__, ret);
|
||||
cma_debug(1, NULL, "failed, ret:%d\n", ret);
|
||||
next:
|
||||
complete(&c_work->end);
|
||||
if (kthread_should_stop()) {
|
||||
@@ -441,34 +447,6 @@ int cma_alloc_contig_boost(unsigned long start_pfn, unsigned long count)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some of these functions are implemented from page_isolate.c
|
||||
*/
|
||||
static bool can_free_list_page(struct page *page, struct list_head *list)
|
||||
{
|
||||
#if 0
|
||||
unsigned long flags;
|
||||
bool ret = false;
|
||||
|
||||
if (!spin_trylock_irqsave(&cma_iso_lock, flags))
|
||||
return ret;
|
||||
|
||||
if (!(page->flags & PAGE_FLAGS_CHECK_AT_FREE) &&
|
||||
!PageSwapBacked(page) &&
|
||||
(page->lru.next != LIST_POISON1)) {
|
||||
if (list_empty(&page->lru))
|
||||
list_add(&page->lru, list);
|
||||
else
|
||||
list_move(&page->lru, list);
|
||||
ret = true;
|
||||
}
|
||||
spin_unlock_irqrestore(&cma_iso_lock, flags);
|
||||
return ret;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int __aml_check_pageblock_isolate(unsigned long pfn,
|
||||
unsigned long end_pfn,
|
||||
bool skip_hwpoisoned_pages,
|
||||
@@ -496,13 +474,7 @@ static int __aml_check_pageblock_isolate(unsigned long pfn,
|
||||
*/
|
||||
pfn++;
|
||||
} else {
|
||||
/* This page can be freed ? */
|
||||
if (!page_count(page)) {
|
||||
if (can_free_list_page(page, list)) {
|
||||
pfn++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
cma_debug(1, page, " isolate failed\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -581,11 +553,14 @@ int aml_cma_alloc_range(unsigned long start, unsigned long end)
|
||||
};
|
||||
INIT_LIST_HEAD(&cc.migratepages);
|
||||
|
||||
cma_debug(0, NULL, " range [%lx-%lx]\n", start, end);
|
||||
ret = start_isolate_page_range(get_align_pfn_low(start),
|
||||
get_align_pfn_high(end), MIGRATE_CMA,
|
||||
false);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
cma_debug(1, NULL, "ret:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
try_again:
|
||||
/*
|
||||
@@ -600,8 +575,10 @@ try_again:
|
||||
} else
|
||||
ret = aml_alloc_contig_migrate_range(&cc, start, end, 0);
|
||||
|
||||
if (ret && ret != -EBUSY)
|
||||
if (ret && ret != -EBUSY) {
|
||||
cma_debug(1, NULL, "ret:%d\n", ret);
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
if (!boost_ok) {
|
||||
@@ -633,8 +610,8 @@ try_again:
|
||||
|
||||
/* Make sure the range is really isolated. */
|
||||
if (aml_check_pages_isolated(outer_start, end, false)) {
|
||||
pr_debug("%s check_pages_isolated(%lx, %lx) failed\n",
|
||||
__func__, outer_start, end);
|
||||
cma_debug(1, NULL, "check page isolate(%lx, %lx) failed\n",
|
||||
outer_start, end);
|
||||
try_times++;
|
||||
if (try_times < 10)
|
||||
goto try_again;
|
||||
@@ -650,6 +627,8 @@ try_again:
|
||||
pr_info("cma_alloc [%lx-%lx] aborted\n", start, end);
|
||||
} else
|
||||
ret = -EBUSY;
|
||||
cma_debug(1, NULL, "iso free range(%lx, %lx) failed\n",
|
||||
outer_start, end);
|
||||
goto done;
|
||||
}
|
||||
|
||||
@@ -733,11 +712,67 @@ void aml_cma_free(unsigned long pfn, unsigned int nr_pages)
|
||||
}
|
||||
EXPORT_SYMBOL(aml_cma_free);
|
||||
|
||||
void show_page(struct page *page)
|
||||
{
|
||||
unsigned long trace = 0;
|
||||
|
||||
if (!page)
|
||||
return;
|
||||
#ifdef CONFIG_AMLOGIC_PAGE_TRACE
|
||||
trace = get_page_trace(page);
|
||||
#endif
|
||||
pr_info("page:%lx, map:%p, f:%lx, m:%d, c:%d, f:%pf\n",
|
||||
page_to_pfn(page), page->mapping,
|
||||
page->flags & 0xffffffff,
|
||||
page_mapcount(page), page_count(page),
|
||||
(void *)trace);
|
||||
}
|
||||
|
||||
static int cma_debug_show(struct seq_file *m, void *arg)
|
||||
{
|
||||
seq_printf(m, "level=%d\n", cma_debug_level);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t cma_debug_write(struct file *file, const char __user *buffer,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
int arg = 0;
|
||||
|
||||
if (kstrtoint_from_user(buffer, count, 10, &arg))
|
||||
return -EINVAL;
|
||||
|
||||
if (arg > MAX_DEBUG_LEVEL)
|
||||
return -EINVAL;
|
||||
|
||||
cma_debug_level = arg;
|
||||
return count;
|
||||
}
|
||||
|
||||
static int cma_debug_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, cma_debug_show, NULL);
|
||||
}
|
||||
|
||||
static const struct file_operations cma_dbg_file_ops = {
|
||||
.open = cma_debug_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.write = cma_debug_write,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int __init aml_cma_init(void)
|
||||
{
|
||||
atomic_set(&cma_allocate, 0);
|
||||
atomic_long_set(&nr_cma_allocated, 0);
|
||||
|
||||
dentry = proc_create("cma_debug", 0644, NULL, &cma_dbg_file_ops);
|
||||
if (IS_ERR_OR_NULL(dentry)) {
|
||||
pr_err("%s, create sysfs failed\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
arch_initcall(aml_cma_init);
|
||||
|
||||
@@ -74,6 +74,7 @@ extern bool cma_page(struct page *page);
|
||||
extern unsigned long get_cma_allocated(void);
|
||||
extern unsigned long get_total_cmapages(void);
|
||||
extern spinlock_t cma_iso_lock;
|
||||
extern int cma_debug_level;
|
||||
extern int aml_cma_alloc_range(unsigned long start, unsigned long end);
|
||||
|
||||
extern void aml_cma_free(unsigned long pfn, unsigned int nr_pages);
|
||||
@@ -81,6 +82,7 @@ extern void aml_cma_free(unsigned long pfn, unsigned int nr_pages);
|
||||
extern unsigned long reclaim_clean_pages_from_list(struct zone *zone,
|
||||
struct list_head *page_list);
|
||||
|
||||
extern void show_page(struct page *page);
|
||||
unsigned long
|
||||
isolate_freepages_range(struct compact_control *cc,
|
||||
unsigned long start_pfn, unsigned long end_pfn);
|
||||
@@ -91,4 +93,14 @@ isolate_migratepages_range(struct compact_control *cc,
|
||||
struct page *compaction_cma_alloc(struct page *migratepage,
|
||||
unsigned long data,
|
||||
int **result);
|
||||
|
||||
#define cma_debug(l, p, format, args...) \
|
||||
{ \
|
||||
if (l < cma_debug_level) { \
|
||||
show_page(p); \
|
||||
pr_info("%s, %d "format, __func__, __LINE__, ##args); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
#endif /* __AMLOGIC_CMA_H__ */
|
||||
|
||||
5
mm/cma.c
5
mm/cma.c
@@ -520,6 +520,10 @@ struct page *cma_alloc(struct cma *cma, size_t count, unsigned int align)
|
||||
pr_debug("%s(cma %p, count %zu, align %d)\n", __func__, (void *)cma,
|
||||
count, align);
|
||||
|
||||
#ifdef CONFIG_AMLOGIC_CMA
|
||||
cma_debug(0, NULL, "(cma %p, count %zu, align %d)\n",
|
||||
(void *)cma, count, align);
|
||||
#endif
|
||||
if (!count)
|
||||
return NULL;
|
||||
|
||||
@@ -581,6 +585,7 @@ struct page *cma_alloc(struct cma *cma, size_t count, unsigned int align)
|
||||
|
||||
#ifdef CONFIG_AMLOGIC_CMA
|
||||
aml_cma_alloc_post_hook(&dummy, count, page);
|
||||
cma_debug(0, NULL, "return %p\n", page);
|
||||
#endif /* CONFIG_AMLOGIC_CMA */
|
||||
pr_debug("%s(): returned %p\n", __func__, page);
|
||||
return page;
|
||||
|
||||
@@ -434,14 +434,34 @@ static unsigned long isolate_freepages_block(struct compact_control *cc,
|
||||
* contention, to give chance to IRQs. Abort if fatal signal
|
||||
* pending or async compaction detects need_resched()
|
||||
*/
|
||||
#ifdef CONFIG_AMLOGIC_CMA
|
||||
if (!(blockpfn % SWAP_CLUSTER_MAX)
|
||||
&& compact_unlock_should_abort(&cc->zone->lock, flags,
|
||||
&locked, cc)) {
|
||||
if (strict)
|
||||
cma_debug(1, page, "abort, blk:%lx, swap:%ld\n",
|
||||
blockpfn, SWAP_CLUSTER_MAX);
|
||||
break;
|
||||
}
|
||||
#else
|
||||
if (!(blockpfn % SWAP_CLUSTER_MAX)
|
||||
&& compact_unlock_should_abort(&cc->zone->lock, flags,
|
||||
&locked, cc))
|
||||
break;
|
||||
#endif
|
||||
|
||||
nr_scanned++;
|
||||
#ifdef CONFIG_AMLOGIC_CMA
|
||||
if (!pfn_valid_within(blockpfn)) {
|
||||
if (strict)
|
||||
cma_debug(1, page, "invalid pfn:%lx\n",
|
||||
blockpfn);
|
||||
goto isolate_fail;
|
||||
}
|
||||
#else
|
||||
if (!pfn_valid_within(blockpfn))
|
||||
goto isolate_fail;
|
||||
#endif
|
||||
|
||||
if (!valid_page)
|
||||
valid_page = page;
|
||||
@@ -459,12 +479,25 @@ static unsigned long isolate_freepages_block(struct compact_control *cc,
|
||||
blockpfn += (1UL << comp_order) - 1;
|
||||
cursor += (1UL << comp_order) - 1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_AMLOGIC_CMA
|
||||
if (strict)
|
||||
cma_debug(1, page, "compound page:%lx\n",
|
||||
page_to_pfn(page));
|
||||
#endif
|
||||
goto isolate_fail;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_AMLOGIC_CMA
|
||||
if (!PageBuddy(page)) {
|
||||
if (strict)
|
||||
cma_debug(1, page, " NO buddy page1:%lx\n",
|
||||
page_to_pfn(page));
|
||||
goto isolate_fail;
|
||||
}
|
||||
#else
|
||||
if (!PageBuddy(page))
|
||||
goto isolate_fail;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If we already hold the lock, we can skip some rechecking.
|
||||
@@ -484,19 +517,44 @@ static unsigned long isolate_freepages_block(struct compact_control *cc,
|
||||
*/
|
||||
locked = compact_trylock_irqsave(&cc->zone->lock,
|
||||
&flags, cc);
|
||||
#ifdef CONFIG_AMLOGIC_CMA
|
||||
if (!locked) {
|
||||
if (strict)
|
||||
cma_debug(1, page, " lock failed:%lx\n",
|
||||
page_to_pfn(page));
|
||||
break;
|
||||
}
|
||||
/* Recheck this is a buddy page under lock */
|
||||
if (!PageBuddy(page)) {
|
||||
if (strict)
|
||||
cma_debug(1, page, " No buddy2:%lx\n",
|
||||
page_to_pfn(page));
|
||||
goto isolate_fail;
|
||||
}
|
||||
#else
|
||||
if (!locked)
|
||||
break;
|
||||
|
||||
/* Recheck this is a buddy page under lock */
|
||||
if (!PageBuddy(page))
|
||||
goto isolate_fail;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Found a free page, will break it into order-0 pages */
|
||||
order = page_order(page);
|
||||
isolated = __isolate_free_page(page, order);
|
||||
#ifdef CONFIG_AMLOGIC_CMA
|
||||
if (!isolated) {
|
||||
if (strict)
|
||||
cma_debug(1, page, "iso free fail:%lx, o:%d\n",
|
||||
page_to_pfn(page), order);
|
||||
break;
|
||||
}
|
||||
#else
|
||||
if (!isolated)
|
||||
break;
|
||||
#endif
|
||||
set_page_private(page, order);
|
||||
|
||||
total_isolated += isolated;
|
||||
@@ -599,9 +657,17 @@ isolate_freepages_range(struct compact_control *cc,
|
||||
block_end_pfn = min(block_end_pfn, end_pfn);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_AMLOGIC_CMA
|
||||
if (!pageblock_pfn_to_page(block_start_pfn,
|
||||
block_end_pfn, cc->zone)) {
|
||||
cma_debug(1, NULL, " no page block\n");
|
||||
break;
|
||||
}
|
||||
#else
|
||||
if (!pageblock_pfn_to_page(block_start_pfn,
|
||||
block_end_pfn, cc->zone))
|
||||
break;
|
||||
#endif
|
||||
|
||||
isolated = isolate_freepages_block(cc, &isolate_start_pfn,
|
||||
block_end_pfn, &freelist, true);
|
||||
@@ -611,8 +677,15 @@ isolate_freepages_range(struct compact_control *cc,
|
||||
* there are any holes in the block (ie. invalid PFNs or
|
||||
* non-free pages).
|
||||
*/
|
||||
#ifdef CONFIG_AMLOGIC_CMA
|
||||
if (!isolated) {
|
||||
cma_debug(1, NULL, " isolate free page failed\n");
|
||||
break;
|
||||
}
|
||||
#else
|
||||
if (!isolated)
|
||||
break;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If we managed to isolate pages, it is always (1 << n) *
|
||||
@@ -626,6 +699,10 @@ isolate_freepages_range(struct compact_control *cc,
|
||||
|
||||
if (pfn < end_pfn) {
|
||||
/* Loop terminated early, cleanup. */
|
||||
#ifdef CONFIG_AMLOGIC_CMA
|
||||
cma_debug(1, NULL, "pfn:%lx, end:%lx, start:%lx\n",
|
||||
pfn, end_pfn, start_pfn);
|
||||
#endif
|
||||
release_freepages(&freelist);
|
||||
return 0;
|
||||
}
|
||||
|
||||
25
mm/migrate.c
25
mm/migrate.c
@@ -415,6 +415,11 @@ int migrate_page_move_mapping(struct address_space *mapping,
|
||||
|
||||
if (!mapping) {
|
||||
/* Anonymous page without mapping */
|
||||
#ifdef CONFIG_AMLOGIC_CMA
|
||||
if (page_count(page) != expected_count)
|
||||
cma_debug(2, page, " anon page cnt miss match, e:%d\n",
|
||||
expected_count);
|
||||
#endif
|
||||
if (page_count(page) != expected_count)
|
||||
return -EAGAIN;
|
||||
|
||||
@@ -439,6 +444,10 @@ int migrate_page_move_mapping(struct address_space *mapping,
|
||||
if (page_count(page) != expected_count ||
|
||||
radix_tree_deref_slot_protected(pslot, &mapping->tree_lock) != page) {
|
||||
spin_unlock_irq(&mapping->tree_lock);
|
||||
#ifdef CONFIG_AMLOGIC_CMA
|
||||
cma_debug(2, page, " anon page cnt miss match, e:%d, p:%d\n",
|
||||
expected_count, page_has_private(page));
|
||||
#endif
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
@@ -1017,11 +1026,21 @@ static int __unmap_and_move(struct page *page, struct page *newpage,
|
||||
goto out_unlock_both;
|
||||
}
|
||||
} else if (page_mapped(page)) {
|
||||
#ifdef CONFIG_AMLOGIC_CMA
|
||||
int ret;
|
||||
|
||||
ret = try_to_unmap(page,
|
||||
TTU_MIGRATION | TTU_IGNORE_MLOCK |
|
||||
TTU_IGNORE_ACCESS);
|
||||
if (ret != SWAP_SUCCESS)
|
||||
cma_debug(2, page, " unmap failed:%d\n", ret);
|
||||
#else
|
||||
/* Establish migration ptes */
|
||||
VM_BUG_ON_PAGE(PageAnon(page) && !PageKsm(page) && !anon_vma,
|
||||
page);
|
||||
try_to_unmap(page,
|
||||
TTU_MIGRATION|TTU_IGNORE_MLOCK|TTU_IGNORE_ACCESS);
|
||||
#endif
|
||||
page_was_mapped = 1;
|
||||
}
|
||||
|
||||
@@ -1341,6 +1360,9 @@ int migrate_pages(struct list_head *from, new_page_t get_new_page,
|
||||
switch(rc) {
|
||||
case -ENOMEM:
|
||||
nr_failed++;
|
||||
#ifdef CONFIG_AMLOGIC_CMA
|
||||
cma_debug(2, page, " NO MEM\n");
|
||||
#endif
|
||||
goto out;
|
||||
case -EAGAIN:
|
||||
retry++;
|
||||
@@ -1356,6 +1378,9 @@ int migrate_pages(struct list_head *from, new_page_t get_new_page,
|
||||
* retried in the next outer loop.
|
||||
*/
|
||||
nr_failed++;
|
||||
#ifdef CONFIG_AMLOGIC_CMA
|
||||
cma_debug(2, page, " failed:%d\n", rc);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user