[PATCH v6 07/12] mm/cma: Introduce cma_alloc_at() API
Marek Szyprowski
m.szyprowski at samsung.com
Tue Sep 8 01:21:00 PDT 2026
On 04.09.2026 12:44, Thierry Reding wrote:
> From: Thierry Reding <treding at nvidia.com>
>
> This API can be used to allocate a number of CMA pages starting at a
> fixed offset. This is useful, for example, if the CMA area is used as
> backing storage for a nested allocator that has stricter requirements
> than CMA itself.
>
> In order to reuse most of the existing code, the cma_range_alloc()
> function is enhanced to take as inputs a range of pages to look for a
> free region. If this range encompasses the entire CMA range, the
> behavior is as before, but the range can also be specified to match
> the offset and size passed in from cma_alloc_at(), in which case that
> is the range that will be allocated (or allocation will fail if the
> range is not available).
>
> Suggested-by: Marek Szyprowski <m.szyprowski at samsung.com>
> Signed-off-by: Thierry Reding <treding at nvidia.com>
Acked-by: Marek Szyprowski <m.szyprowski at samsung.com>
> ---
> Changes in v6:
> - refactor cma_range_alloc() so it can be reused for _at() code paths
> ---
> include/linux/cma.h | 4 +++
> include/trace/events/cma.h | 77 ++++++++++++++++++++++++++++++++++++++--
> mm/cma.c | 88 +++++++++++++++++++++++++++++++++++-----------
> 3 files changed, 146 insertions(+), 23 deletions(-)
>
> diff --git a/include/linux/cma.h b/include/linux/cma.h
> index 8555d38a97b1..844404459a42 100644
> --- a/include/linux/cma.h
> +++ b/include/linux/cma.h
> @@ -49,11 +49,15 @@ extern int cma_init_reserved_mem(phys_addr_t base, phys_addr_t size,
> struct cma **res_cma);
> extern struct page *cma_alloc(struct cma *cma, unsigned long count, unsigned int align,
> bool no_warn);
> +extern struct page *cma_alloc_at(struct cma *cma, unsigned long offset,
> + unsigned long count, bool no_warn);
> extern bool cma_release(struct cma *cma, const struct page *pages, unsigned long count);
>
> struct page *cma_alloc_frozen(struct cma *cma, unsigned long count,
> unsigned int align, bool no_warn);
> struct page *cma_alloc_frozen_compound(struct cma *cma, unsigned int order);
> +struct page *cma_alloc_at_frozen(struct cma *cma, unsigned long offset,
> + unsigned long count, bool no_warn);
> bool cma_release_frozen(struct cma *cma, const struct page *pages,
> unsigned long count);
>
> diff --git a/include/trace/events/cma.h b/include/trace/events/cma.h
> index 37195edf2498..4e0ab63e303f 100644
> --- a/include/trace/events/cma.h
> +++ b/include/trace/events/cma.h
> @@ -38,13 +38,17 @@ TRACE_EVENT(cma_release,
>
> TRACE_EVENT(cma_alloc_start,
>
> - TP_PROTO(const char *name, unsigned long request_count, unsigned long available_count,
> + TP_PROTO(const char *name, unsigned long start, unsigned long end,
> + unsigned long request_count, unsigned long available_count,
> unsigned long total_count, unsigned int align),
>
> - TP_ARGS(name, request_count, available_count, total_count, align),
> + TP_ARGS(name, start, end, request_count, available_count, total_count,
> + align),
>
> TP_STRUCT__entry(
> __string(name, name)
> + __field(unsigned long, start)
> + __field(unsigned long, end)
> __field(unsigned long, request_count)
> __field(unsigned long, available_count)
> __field(unsigned long, total_count)
> @@ -53,14 +57,18 @@ TRACE_EVENT(cma_alloc_start,
>
> TP_fast_assign(
> __assign_str(name);
> + __entry->start = start;
> + __entry->end = end;
> __entry->request_count = request_count;
> __entry->available_count = available_count;
> __entry->total_count = total_count;
> __entry->align = align;
> ),
>
> - TP_printk("name=%s request_count=%lu available_count=%lu total_count=%lu align=%u",
> + TP_printk("name=%s start=%lu end=%lu request_count=%lu available_count=%lu total_count=%lu align=%u",
> __get_str(name),
> + __entry->start,
> + __entry->end,
> __entry->request_count,
> __entry->available_count,
> __entry->total_count,
> @@ -132,6 +140,69 @@ TRACE_EVENT(cma_alloc_busy_retry,
> __entry->align)
> );
>
> +TRACE_EVENT(cma_alloc_at_start,
> +
> + TP_PROTO(const char *name, unsigned long pfn,
> + unsigned long request_count, unsigned long available_count,
> + unsigned long total_count),
> +
> + TP_ARGS(name, pfn, request_count, available_count, total_count),
> +
> + TP_STRUCT__entry(
> + __string(name, name)
> + __field(unsigned long, pfn)
> + __field(unsigned long, request_count)
> + __field(unsigned long, available_count)
> + __field(unsigned long, total_count)
> + ),
> +
> + TP_fast_assign(
> + __assign_str(name);
> + __entry->pfn = pfn;
> + __entry->request_count = request_count;
> + __entry->available_count = available_count;
> + __entry->total_count = total_count;
> + ),
> +
> + TP_printk("name=%s pfn=%lx, request_count=%lu available_count=%lu total_count=%lu",
> + __get_str(name),
> + __entry->pfn,
> + __entry->request_count,
> + __entry->available_count,
> + __entry->total_count)
> +);
> +
> +TRACE_EVENT(cma_alloc_at_finish,
> +
> + TP_PROTO(const char *name, unsigned long pfn, const struct page *page,
> + unsigned long count, int errorno),
> +
> + TP_ARGS(name, pfn, page, count, errorno),
> +
> + TP_STRUCT__entry(
> + __string(name, name)
> + __field(unsigned long, pfn)
> + __field(const struct page *, page)
> + __field(unsigned long, count)
> + __field(int, errorno)
> + ),
> +
> + TP_fast_assign(
> + __assign_str(name);
> + __entry->pfn = pfn;
> + __entry->page = page;
> + __entry->count = count;
> + __entry->errorno = errorno;
> + ),
> +
> + TP_printk("name=%s pfn=0x%lx page=%p count=%lu errorno=%d",
> + __get_str(name),
> + __entry->pfn,
> + __entry->page,
> + __entry->count,
> + __entry->errorno)
> +);
> +
> #endif /* _TRACE_CMA_H */
>
> /* This part must be outside protection */
> diff --git a/mm/cma.c b/mm/cma.c
> index a10ea37a261d..f92d3a188209 100644
> --- a/mm/cma.c
> +++ b/mm/cma.c
> @@ -783,25 +783,32 @@ static void cma_debug_show_areas(struct cma *cma)
> spin_unlock_irq(&cma->lock);
> }
>
> +/*
> + * Searches the CMA memrange, from @start to @end, for a free region of
> + * @count bits. If @end is less or equal to @start, will search the entire
> + * memrange.
> + */
> static int cma_range_alloc(struct cma *cma, struct cma_memrange *cmr,
> - unsigned long count, unsigned int align,
> - struct page **pagep, gfp_t gfp)
> + unsigned long start, unsigned long end,
> + unsigned long count, unsigned int align,
> + struct page **pagep, gfp_t gfp)
> {
> unsigned long bitmap_maxno, bitmap_no, bitmap_count;
> - unsigned long start, pfn, mask, offset;
> - int ret = -EBUSY;
> + unsigned long pfn, mask, offset;
> struct page *page = NULL;
> + int ret = -EBUSY;
>
> mask = cma_bitmap_aligned_mask(cma, align);
> offset = cma_bitmap_aligned_offset(cma, cmr, align);
> - bitmap_maxno = cma_bitmap_maxno(cma, cmr);
> + bitmap_maxno = (end > start) ? end : cma_bitmap_maxno(cma, cmr);
> bitmap_count = cma_bitmap_pages_to_bits(cma, count);
>
> if (bitmap_count > bitmap_maxno)
> goto out;
>
> - for (start = 0; ; start = bitmap_no + mask + 1) {
> + while (true) {
> spin_lock_irq(&cma->lock);
> +
> /*
> * If the request is larger than the available number
> * of pages, stop right away.
> @@ -810,6 +817,7 @@ static int cma_range_alloc(struct cma *cma, struct cma_memrange *cmr,
> spin_unlock_irq(&cma->lock);
> break;
> }
> +
> bitmap_no = bitmap_find_next_zero_area_off(cmr->bitmap,
> bitmap_maxno, start, bitmap_count, mask,
> offset);
> @@ -835,10 +843,11 @@ static int cma_range_alloc(struct cma *cma, struct cma_memrange *cmr,
>
> bitmap_set(cmr->bitmap, bitmap_no, bitmap_count);
> cma->available_count -= count;
> +
> /*
> - * It's safe to drop the lock here. We've marked this region for
> - * our exclusive use. If the migration fails we will take the
> - * lock again and unmark it.
> + * It's safe to drop the lock here. We've marked this region
> + * for our exclusive use. If the migration fails we will take
> + * the lock again and unmark it.
> */
> spin_unlock_irq(&cma->lock);
>
> @@ -856,37 +865,45 @@ static int cma_range_alloc(struct cma *cma, struct cma_memrange *cmr,
> __func__, pfn, page);
>
> trace_cma_alloc_busy_retry(cma->name, pfn, page, count, align);
> + start = bitmap_no + mask + 1;
> }
> +
> out:
> if (!ret)
> *pagep = page;
> +
> return ret;
> }
>
> -static struct page *__cma_alloc_frozen(struct cma *cma,
> - unsigned long count, unsigned int align, gfp_t gfp)
> +static struct page *__cma_alloc_frozen(struct cma *cma, unsigned long start,
> + unsigned long end, unsigned long count,
> + unsigned int align, gfp_t gfp)
> {
> struct page *page = NULL;
> int ret = -ENOMEM, r;
> unsigned long i;
> - const char *name = cma ? cma->name : NULL;
> +
> + /* cma_alloc_at() and friends will only work with single-range CMA */
> + if (WARN_ON_ONCE(start > 0 && cma->nranges != 1))
> + return page;
>
> if (!cma || !cma->count)
> return page;
>
> - pr_debug("%s(cma %p, name: %s, count %lu, align %d)\n", __func__,
> - (void *)cma, cma->name, count, align);
> + pr_debug("%s(cma %p, name: %s, start %lu, end %lu, count %lu, align %d)\n",
> + __func__, (void *)cma, cma->name, start, end, count, align);
>
> if (!count)
> return page;
>
> - trace_cma_alloc_start(name, count, cma->available_count, cma->count, align);
> + trace_cma_alloc_start(cma->name, start, end, count,
> + cma->available_count, cma->count, align);
>
> for (r = 0; r < cma->nranges; r++) {
> page = NULL;
>
> - ret = cma_range_alloc(cma, &cma->ranges[r], count, align,
> - &page, gfp);
> + ret = cma_range_alloc(cma, &cma->ranges[r], start, end,
> + count, align, &page, gfp);
> if (ret != -EBUSY || page)
> break;
> }
> @@ -908,7 +925,7 @@ static struct page *__cma_alloc_frozen(struct cma *cma,
> }
>
> pr_debug("%s(): returned %p\n", __func__, page);
> - trace_cma_alloc_finish(name, page ? page_to_pfn(page) : 0,
> + trace_cma_alloc_finish(cma->name, page ? page_to_pfn(page) : 0,
> page, count, align, ret);
> if (page) {
> count_vm_event(CMA_ALLOC_SUCCESS);
> @@ -926,14 +943,22 @@ struct page *cma_alloc_frozen(struct cma *cma, unsigned long count,
> {
> gfp_t gfp = GFP_KERNEL | (no_warn ? __GFP_NOWARN : 0);
>
> - return __cma_alloc_frozen(cma, count, align, gfp);
> + return __cma_alloc_frozen(cma, 0, 0, count, align, gfp);
> }
>
> struct page *cma_alloc_frozen_compound(struct cma *cma, unsigned int order)
> {
> gfp_t gfp = GFP_KERNEL | __GFP_COMP | __GFP_NOWARN;
>
> - return __cma_alloc_frozen(cma, 1 << order, order, gfp);
> + return __cma_alloc_frozen(cma, 0, 0, 1 << order, order, gfp);
> +}
> +
> +struct page *cma_alloc_at_frozen(struct cma *cma, unsigned long offset,
> + unsigned long count, bool no_warn)
> +{
> + gfp_t gfp = GFP_KERNEL | (no_warn ? __GFP_NOWARN : 0);
> +
> + return __cma_alloc_frozen(cma, offset, offset + count, count, 0, gfp);
> }
>
> /**
> @@ -959,6 +984,29 @@ struct page *cma_alloc(struct cma *cma, unsigned long count,
> }
> EXPORT_SYMBOL_GPL(cma_alloc);
>
> +/**
> + * cma_alloc_at() - allocate pages from contiguous area at fixed offset
> + * @cma: Contiguous memory region for which the allocation is performed.
> + * @offset: Index of the first page to allocate.
> + * @count: Requested number of pages (in PAGE_SIZE order).
> + * @no_warn: Avoid printing message about failed allocation.
> + *
> + * This function allocates a part of the contiguous memory on a specific
> + * contiguous memory area.
> + */
> +struct page *cma_alloc_at(struct cma *cma, unsigned long offset,
> + unsigned long count, bool no_warn)
> +{
> + struct page *page;
> +
> + page = cma_alloc_at_frozen(cma, offset, count, no_warn);
> + if (page)
> + set_pages_refcounted(page, count);
> +
> + return page;
> +}
> +EXPORT_SYMBOL_GPL(cma_alloc_at);
> +
> static struct cma_memrange *find_cma_memrange(struct cma *cma,
> const struct page *pages, unsigned long count)
> {
>
Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland
More information about the linux-arm-kernel
mailing list