From 0dad457899d7c4f28855dfadf6c71a1ff8d751ff Mon Sep 17 00:00:00 2001 From: Suren Baghdasaryan Date: Fri, 28 Feb 2025 11:16:51 -0800 Subject: [PATCH] ANDROID: integrate GCMA with CMA using dt-bindings This patch introduces a new "guarantee" property for shared-dma-pool. With the property, admin can create specific memory pool as GCMA-based-CMA if they care about allocation success rate and latency without reserving a dedicated memory area. Bug: 236887352 Bug: 270089503 Bug: 391879760 Change-Id: I3a019f889f7ffa4d2255609c19b15078b9c5b6a4 Signed-off-by: Minchan Kim Signed-off-by: Suren Baghdasaryan --- arch/powerpc/kernel/fadump.c | 2 +- include/linux/cma.h | 3 ++- kernel/dma/contiguous.c | 11 ++++++++++- mm/cma.c | 33 +++++++++++++++++++++++++-------- mm/cma.h | 1 + mm/cma_sysfs.c | 10 ++++++++++ 6 files changed, 49 insertions(+), 11 deletions(-) diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c index 4641de75f7fc..58e0b2667209 100644 --- a/arch/powerpc/kernel/fadump.c +++ b/arch/powerpc/kernel/fadump.c @@ -95,7 +95,7 @@ void __init fadump_cma_init(void) base = fw_dump.reserve_dump_area_start; size = fw_dump.boot_memory_size; - rc = cma_init_reserved_mem(base, size, 0, "fadump_cma", &fadump_cma); + rc = cma_init_reserved_mem(base, size, 0, "fadump_cma", &fadump_cma, false); if (rc) { pr_err("Failed to init cma area for firmware-assisted dump,%d\n", rc); /* diff --git a/include/linux/cma.h b/include/linux/cma.h index f93f5c1488ee..c8d6d1de4cce 100644 --- a/include/linux/cma.h +++ b/include/linux/cma.h @@ -43,7 +43,8 @@ static inline int __init cma_declare_contiguous(phys_addr_t base, extern int cma_init_reserved_mem(phys_addr_t base, phys_addr_t size, unsigned int order_per_bit, const char *name, - struct cma **res_cma); + struct cma **res_cma, + bool gcma); extern struct page *__cma_alloc(struct cma *cma, unsigned long count, unsigned int align, gfp_t gfp_mask); extern struct page *cma_alloc(struct cma *cma, unsigned long count, unsigned int align, diff --git a/kernel/dma/contiguous.c b/kernel/dma/contiguous.c index 0de38657260e..6badfaeda89a 100644 --- a/kernel/dma/contiguous.c +++ b/kernel/dma/contiguous.c @@ -465,6 +465,7 @@ static int __init rmem_cma_setup(struct reserved_mem *rmem) unsigned long node = rmem->fdt_node; bool default_cma = of_get_flat_dt_prop(node, "linux,cma-default", NULL); struct cma *cma; + bool gcma; int err; if (size_cmdline != -1 && default_cma) { @@ -482,7 +483,15 @@ static int __init rmem_cma_setup(struct reserved_mem *rmem) return -EINVAL; } - err = cma_init_reserved_mem(rmem->base, rmem->size, 0, rmem->name, &cma); + gcma = !!of_get_flat_dt_prop(node, "guarantee", NULL); +#ifndef CONFIG_GCMA + if (gcma) { + pr_err("Reserved memory: unable to setup GCMA region, GCMA is not enabled\n"); + return -EINVAL; + } +#endif + err = cma_init_reserved_mem(rmem->base, rmem->size, 0, rmem->name, + &cma, gcma); if (err) { pr_err("Reserved memory: unable to setup CMA region\n"); return err; diff --git a/mm/cma.c b/mm/cma.c index cd5148dc996d..911eed21cb0f 100644 --- a/mm/cma.c +++ b/mm/cma.c @@ -29,6 +29,7 @@ #include #include #include +#include #define CREATE_TRACE_POINTS #include #undef CREATE_TRACE_POINTS @@ -126,9 +127,14 @@ static void __init cma_activate_area(struct cma *cma) goto not_in_zone; } - for (pfn = base_pfn; pfn < base_pfn + cma->count; - pfn += pageblock_nr_pages) - init_cma_reserved_pageblock(pfn_to_page(pfn)); + if (cma->gcma) { + register_gcma_area(cma->name, PFN_PHYS(base_pfn), + cma->count << PAGE_SHIFT); + } else { + for (pfn = base_pfn; pfn < base_pfn + cma->count; + pfn += pageblock_nr_pages) + init_cma_reserved_pageblock(pfn_to_page(pfn)); + } spin_lock_init(&cma->lock); @@ -186,7 +192,7 @@ void __init cma_reserve_pages_on_error(struct cma *cma) int __init cma_init_reserved_mem(phys_addr_t base, phys_addr_t size, unsigned int order_per_bit, const char *name, - struct cma **res_cma) + struct cma **res_cma, bool gcma) { struct cma *cma; @@ -212,11 +218,13 @@ int __init cma_init_reserved_mem(phys_addr_t base, phys_addr_t size, if (name) snprintf(cma->name, CMA_MAX_NAME, name); else - snprintf(cma->name, CMA_MAX_NAME, "cma%d\n", cma_area_count); + snprintf(cma->name, CMA_MAX_NAME, + gcma ? "gcma%d\n" : "cma%d\n", cma_area_count); cma->base_pfn = PFN_DOWN(base); cma->count = size >> PAGE_SHIFT; cma->order_per_bit = order_per_bit; + cma->gcma = gcma; *res_cma = cma; cma_area_count++; totalcma_pages += cma->count; @@ -379,7 +387,8 @@ int __init cma_declare_contiguous_nid(phys_addr_t base, base = addr; } - ret = cma_init_reserved_mem(base, size, order_per_bit, name, res_cma); + ret = cma_init_reserved_mem(base, size, order_per_bit, name, res_cma, + false); if (ret) goto free_mem; @@ -509,7 +518,12 @@ 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); + if (cma->gcma) { + gcma_alloc_range(pfn, pfn + count - 1); + ret = 0; + } else { + ret = alloc_contig_range(pfn, pfn + count, MIGRATE_CMA, gfp); + } mutex_unlock(&cma_mutex); if (ret == 0) { page = pfn_to_page(pfn); @@ -632,7 +646,10 @@ bool cma_release(struct cma *cma, const struct page *pages, VM_BUG_ON(pfn + count > cma->base_pfn + cma->count); - free_contig_range(pfn, count); + if (cma->gcma) + gcma_free_range(pfn, pfn + count - 1); + else + free_contig_range(pfn, count); cma_clear_bitmap(cma, pfn, count); cma_sysfs_account_release_pages(cma, count); trace_cma_release(cma->name, pfn, pages, count); diff --git a/mm/cma.h b/mm/cma.h index c5c1f94c4529..19abcb9af71f 100644 --- a/mm/cma.h +++ b/mm/cma.h @@ -23,6 +23,7 @@ struct cma { struct debugfs_u32_array dfs_bitmap; #endif char name[CMA_MAX_NAME]; + bool gcma; #ifdef CONFIG_CMA_SYSFS /* the number of CMA page successful allocations */ atomic64_t nr_pages_succeeded; diff --git a/mm/cma_sysfs.c b/mm/cma_sysfs.c index f50db3973171..8586c57465ab 100644 --- a/mm/cma_sysfs.c +++ b/mm/cma_sysfs.c @@ -62,6 +62,15 @@ static ssize_t release_pages_success_show(struct kobject *kobj, } CMA_ATTR_RO(release_pages_success); +static ssize_t gcma_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct cma *cma = cma_from_kobj(kobj); + + return sysfs_emit(buf, "%d\n", cma->gcma); +} +CMA_ATTR_RO(gcma); + static void cma_kobj_release(struct kobject *kobj) { struct cma *cma = cma_from_kobj(kobj); @@ -75,6 +84,7 @@ static struct attribute *cma_attrs[] = { &alloc_pages_success_attr.attr, &alloc_pages_fail_attr.attr, &release_pages_success_attr.attr, + &gcma_attr.attr, NULL, }; ATTRIBUTE_GROUPS(cma);