On 04.09.2026 12:44, Thierry Reding wrote:
> From: Thierry Reding <[email protected]>
>
> 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 <[email protected]>
> Signed-off-by: Thierry Reding <[email protected]>
Acked-by: Marek Szyprowski <[email protected]>
> ---
> 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


Reply via email to