mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
mm, cma: support multiple contiguous ranges, if requested
Currently, CMA manages one range of physically contiguous memory. Creation of larger CMA areas with hugetlb_cma may run in to gaps in physical memory, so that they are not able to allocate that contiguous physical range from memblock when creating the CMA area. This can happen, for example, on an AMD system with > 1TB of memory, where there will be a gap just below the 1TB (40bit DMA) line. If you have set aside most of memory for potential hugetlb CMA allocation, cma_declare_contiguous_nid will fail. hugetlb_cma doesn't need the entire area to be one physically contiguous range. It just cares about being able to get physically contiguous chunks of a certain size (e.g. 1G), and it is fine to have the CMA area backed by multiple physical ranges, as long as it gets 1G contiguous allocations. Multi-range support is implemented by introducing an array of ranges, instead of just one big one. Each range has its own bitmap. Effectively, the allocate and release operations work as before, just per-range. So, instead of going through one large bitmap, they now go through a number of smaller ones. The maximum number of supported ranges is 8, as defined in CMA_MAX_RANGES. Since some current users of CMA expect a CMA area to just use one physically contiguous range, only allow for multiple ranges if a new interface, cma_declare_contiguous_nid_multi, is used. The other interfaces will work like before, creating only CMA areas with 1 range. cma_declare_contiguous_nid_multi works as follows, mimicking the default "bottom-up, above 4G" reservation approach: 0) Try cma_declare_contiguous_nid, which will use only one region. If this succeeds, return. This makes sure that for all the cases that currently work, the behavior remains unchanged even if the caller switches from cma_declare_contiguous_nid to cma_declare_contiguous_nid_multi. 1) Select the largest free memblock ranges above 4G, with a maximum number of CMA_MAX_RANGES. 2) If we did not find at most CMA_MAX_RANGES that add up to the total size requested, return -ENOMEM. 3) Sort the selected ranges by base address. 4) Reserve them bottom-up until we get what we wanted. Link: https://lkml.kernel.org/r/20250228182928.2645936-3-fvdl@google.com Signed-off-by: Frank van der Linden <fvdl@google.com> Cc: Arnd Bergmann <arnd@arndb.de> Cc: Alexander Gordeev <agordeev@linux.ibm.com> Cc: Andy Lutomirski <luto@kernel.org> Cc: Dan Carpenter <dan.carpenter@linaro.org> Cc: Dave Hansen <dave.hansen@linux.intel.com> Cc: David Hildenbrand <david@redhat.com> Cc: Heiko Carstens <hca@linux.ibm.com> Cc: Joao Martins <joao.m.martins@oracle.com> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: Madhavan Srinivasan <maddy@linux.ibm.com> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Muchun Song <muchun.song@linux.dev> Cc: Oscar Salvador <osalvador@suse.de> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Roman Gushchin (Cruise) <roman.gushchin@linux.dev> Cc: Usama Arif <usamaarif642@gmail.com> Cc: Vasily Gorbik <gor@linux.ibm.com> Cc: Yu Zhao <yuzhao@google.com> Cc: Zi Yan <ziy@nvidia.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
committed by
Andrew Morton
parent
7365ff2c8e
commit
c009da4258
@@ -12,10 +12,16 @@ its CMA name like below:
|
|||||||
|
|
||||||
The structure of the files created under that directory is as follows:
|
The structure of the files created under that directory is as follows:
|
||||||
|
|
||||||
- [RO] base_pfn: The base PFN (Page Frame Number) of the zone.
|
- [RO] base_pfn: The base PFN (Page Frame Number) of the CMA area.
|
||||||
|
This is the same as ranges/0/base_pfn.
|
||||||
- [RO] count: Amount of memory in the CMA area.
|
- [RO] count: Amount of memory in the CMA area.
|
||||||
- [RO] order_per_bit: Order of pages represented by one bit.
|
- [RO] order_per_bit: Order of pages represented by one bit.
|
||||||
- [RO] bitmap: The bitmap of page states in the zone.
|
- [RO] bitmap: The bitmap of allocated pages in the area.
|
||||||
|
This is the same as ranges/0/base_pfn.
|
||||||
|
- [RO] ranges/N/base_pfn: The base PFN of contiguous range N
|
||||||
|
in the CMA area.
|
||||||
|
- [RO] ranges/N/bitmap: The bit map of allocated pages in
|
||||||
|
range N in the CMA area.
|
||||||
- [WO] alloc: Allocate N pages from that CMA area. For example::
|
- [WO] alloc: Allocate N pages from that CMA area. For example::
|
||||||
|
|
||||||
echo 5 > <debugfs>/cma/<cma_name>/alloc
|
echo 5 > <debugfs>/cma/<cma_name>/alloc
|
||||||
|
|||||||
@@ -40,6 +40,9 @@ static inline int __init cma_declare_contiguous(phys_addr_t base,
|
|||||||
return cma_declare_contiguous_nid(base, size, limit, alignment,
|
return cma_declare_contiguous_nid(base, size, limit, alignment,
|
||||||
order_per_bit, fixed, name, res_cma, NUMA_NO_NODE);
|
order_per_bit, fixed, name, res_cma, NUMA_NO_NODE);
|
||||||
}
|
}
|
||||||
|
extern int __init cma_declare_contiguous_multi(phys_addr_t size,
|
||||||
|
phys_addr_t align, unsigned int order_per_bit,
|
||||||
|
const char *name, struct cma **res_cma, int nid);
|
||||||
extern int cma_init_reserved_mem(phys_addr_t base, phys_addr_t size,
|
extern int cma_init_reserved_mem(phys_addr_t base, phys_addr_t size,
|
||||||
unsigned int order_per_bit,
|
unsigned int order_per_bit,
|
||||||
const char *name,
|
const char *name,
|
||||||
|
|||||||
@@ -10,19 +10,35 @@ struct cma_kobject {
|
|||||||
struct cma *cma;
|
struct cma *cma;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Multi-range support. This can be useful if the size of the allocation
|
||||||
|
* is not expected to be larger than the alignment (like with hugetlb_cma),
|
||||||
|
* and the total amount of memory requested, while smaller than the total
|
||||||
|
* amount of memory available, is large enough that it doesn't fit in a
|
||||||
|
* single physical memory range because of memory holes.
|
||||||
|
*/
|
||||||
|
struct cma_memrange {
|
||||||
|
unsigned long base_pfn;
|
||||||
|
unsigned long count;
|
||||||
|
unsigned long *bitmap;
|
||||||
|
#ifdef CONFIG_CMA_DEBUGFS
|
||||||
|
struct debugfs_u32_array dfs_bitmap;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
#define CMA_MAX_RANGES 8
|
||||||
|
|
||||||
struct cma {
|
struct cma {
|
||||||
unsigned long base_pfn;
|
|
||||||
unsigned long count;
|
unsigned long count;
|
||||||
unsigned long available_count;
|
unsigned long available_count;
|
||||||
unsigned long *bitmap;
|
|
||||||
unsigned int order_per_bit; /* Order of pages represented by one bit */
|
unsigned int order_per_bit; /* Order of pages represented by one bit */
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
#ifdef CONFIG_CMA_DEBUGFS
|
#ifdef CONFIG_CMA_DEBUGFS
|
||||||
struct hlist_head mem_head;
|
struct hlist_head mem_head;
|
||||||
spinlock_t mem_head_lock;
|
spinlock_t mem_head_lock;
|
||||||
struct debugfs_u32_array dfs_bitmap;
|
|
||||||
#endif
|
#endif
|
||||||
char name[CMA_MAX_NAME];
|
char name[CMA_MAX_NAME];
|
||||||
|
int nranges;
|
||||||
|
struct cma_memrange ranges[CMA_MAX_RANGES];
|
||||||
#ifdef CONFIG_CMA_SYSFS
|
#ifdef CONFIG_CMA_SYSFS
|
||||||
/* the number of CMA page successful allocations */
|
/* the number of CMA page successful allocations */
|
||||||
atomic64_t nr_pages_succeeded;
|
atomic64_t nr_pages_succeeded;
|
||||||
@@ -39,9 +55,10 @@ struct cma {
|
|||||||
extern struct cma cma_areas[MAX_CMA_AREAS];
|
extern struct cma cma_areas[MAX_CMA_AREAS];
|
||||||
extern unsigned int cma_area_count;
|
extern unsigned int cma_area_count;
|
||||||
|
|
||||||
static inline unsigned long cma_bitmap_maxno(struct cma *cma)
|
static inline unsigned long cma_bitmap_maxno(struct cma *cma,
|
||||||
|
struct cma_memrange *cmr)
|
||||||
{
|
{
|
||||||
return cma->count >> cma->order_per_bit;
|
return cmr->count >> cma->order_per_bit;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_CMA_SYSFS
|
#ifdef CONFIG_CMA_SYSFS
|
||||||
|
|||||||
+41
-15
@@ -46,17 +46,26 @@ DEFINE_DEBUGFS_ATTRIBUTE(cma_used_fops, cma_used_get, NULL, "%llu\n");
|
|||||||
static int cma_maxchunk_get(void *data, u64 *val)
|
static int cma_maxchunk_get(void *data, u64 *val)
|
||||||
{
|
{
|
||||||
struct cma *cma = data;
|
struct cma *cma = data;
|
||||||
|
struct cma_memrange *cmr;
|
||||||
unsigned long maxchunk = 0;
|
unsigned long maxchunk = 0;
|
||||||
unsigned long start, end = 0;
|
unsigned long start, end;
|
||||||
unsigned long bitmap_maxno = cma_bitmap_maxno(cma);
|
unsigned long bitmap_maxno;
|
||||||
|
int r;
|
||||||
|
|
||||||
spin_lock_irq(&cma->lock);
|
spin_lock_irq(&cma->lock);
|
||||||
for (;;) {
|
for (r = 0; r < cma->nranges; r++) {
|
||||||
start = find_next_zero_bit(cma->bitmap, bitmap_maxno, end);
|
cmr = &cma->ranges[r];
|
||||||
if (start >= bitmap_maxno)
|
bitmap_maxno = cma_bitmap_maxno(cma, cmr);
|
||||||
break;
|
end = 0;
|
||||||
end = find_next_bit(cma->bitmap, bitmap_maxno, start);
|
for (;;) {
|
||||||
maxchunk = max(end - start, maxchunk);
|
start = find_next_zero_bit(cmr->bitmap,
|
||||||
|
bitmap_maxno, end);
|
||||||
|
if (start >= bitmap_maxno)
|
||||||
|
break;
|
||||||
|
end = find_next_bit(cmr->bitmap, bitmap_maxno,
|
||||||
|
start);
|
||||||
|
maxchunk = max(end - start, maxchunk);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
spin_unlock_irq(&cma->lock);
|
spin_unlock_irq(&cma->lock);
|
||||||
*val = (u64)maxchunk << cma->order_per_bit;
|
*val = (u64)maxchunk << cma->order_per_bit;
|
||||||
@@ -159,24 +168,41 @@ DEFINE_DEBUGFS_ATTRIBUTE(cma_alloc_fops, NULL, cma_alloc_write, "%llu\n");
|
|||||||
|
|
||||||
static void cma_debugfs_add_one(struct cma *cma, struct dentry *root_dentry)
|
static void cma_debugfs_add_one(struct cma *cma, struct dentry *root_dentry)
|
||||||
{
|
{
|
||||||
struct dentry *tmp;
|
struct dentry *tmp, *dir, *rangedir;
|
||||||
|
int r;
|
||||||
|
char rdirname[12];
|
||||||
|
struct cma_memrange *cmr;
|
||||||
|
|
||||||
tmp = debugfs_create_dir(cma->name, root_dentry);
|
tmp = debugfs_create_dir(cma->name, root_dentry);
|
||||||
|
|
||||||
debugfs_create_file("alloc", 0200, tmp, cma, &cma_alloc_fops);
|
debugfs_create_file("alloc", 0200, tmp, cma, &cma_alloc_fops);
|
||||||
debugfs_create_file("free", 0200, tmp, cma, &cma_free_fops);
|
debugfs_create_file("free", 0200, tmp, cma, &cma_free_fops);
|
||||||
debugfs_create_file("base_pfn", 0444, tmp,
|
|
||||||
&cma->base_pfn, &cma_debugfs_fops);
|
|
||||||
debugfs_create_file("count", 0444, tmp, &cma->count, &cma_debugfs_fops);
|
debugfs_create_file("count", 0444, tmp, &cma->count, &cma_debugfs_fops);
|
||||||
debugfs_create_file("order_per_bit", 0444, tmp,
|
debugfs_create_file("order_per_bit", 0444, tmp,
|
||||||
&cma->order_per_bit, &cma_debugfs_fops);
|
&cma->order_per_bit, &cma_debugfs_fops);
|
||||||
debugfs_create_file("used", 0444, tmp, cma, &cma_used_fops);
|
debugfs_create_file("used", 0444, tmp, cma, &cma_used_fops);
|
||||||
debugfs_create_file("maxchunk", 0444, tmp, cma, &cma_maxchunk_fops);
|
debugfs_create_file("maxchunk", 0444, tmp, cma, &cma_maxchunk_fops);
|
||||||
|
|
||||||
cma->dfs_bitmap.array = (u32 *)cma->bitmap;
|
rangedir = debugfs_create_dir("ranges", tmp);
|
||||||
cma->dfs_bitmap.n_elements = DIV_ROUND_UP(cma_bitmap_maxno(cma),
|
for (r = 0; r < cma->nranges; r++) {
|
||||||
BITS_PER_BYTE * sizeof(u32));
|
cmr = &cma->ranges[r];
|
||||||
debugfs_create_u32_array("bitmap", 0444, tmp, &cma->dfs_bitmap);
|
snprintf(rdirname, sizeof(rdirname), "%d", r);
|
||||||
|
dir = debugfs_create_dir(rdirname, rangedir);
|
||||||
|
debugfs_create_file("base_pfn", 0444, dir,
|
||||||
|
&cmr->base_pfn, &cma_debugfs_fops);
|
||||||
|
cmr->dfs_bitmap.array = (u32 *)cmr->bitmap;
|
||||||
|
cmr->dfs_bitmap.n_elements =
|
||||||
|
DIV_ROUND_UP(cma_bitmap_maxno(cma, cmr),
|
||||||
|
BITS_PER_BYTE * sizeof(u32));
|
||||||
|
debugfs_create_u32_array("bitmap", 0444, dir,
|
||||||
|
&cmr->dfs_bitmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Backward compatible symlinks to range 0 for base_pfn and bitmap.
|
||||||
|
*/
|
||||||
|
debugfs_create_symlink("base_pfn", tmp, "ranges/0/base_pfn");
|
||||||
|
debugfs_create_symlink("bitmap", tmp, "ranges/0/bitmap");
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init cma_debugfs_init(void)
|
static int __init cma_debugfs_init(void)
|
||||||
|
|||||||
Reference in New Issue
Block a user