Confidential-computing shared allocations need CMA backing allocations
to be aligned to the architecture private/shared state-change granule
size. Passing a DMA attribute into CMA would make a generic physical
allocator interpret DMA and confidential-computing policy that has
already been resolved by its caller.

Add an explicit minimum alignment order to dma_alloc_contiguous().
Preserve the existing size-derived alignment for ordinary callers by
passing zero, and reject a request that exceeds CONFIG_CMA_ALIGNMENT.
For supported requests, use the larger of the size-derived and requested
orders, capped by the configured CMA alignment as before.

Also distinguish preferred and required alignment for
dma_alloc_from_contiguous(). Existing callers pass zero as the required
alignment and retain the current clamping behavior. Callers that require
a minimum alignment can request it explicitly and receive NULL when CMA
cannot satisfy it.

Signed-off-by: Aneesh Kumar K.V (Arm) <[email protected]>
---
 arch/arm/mm/dma-mapping.c   |  5 +++--
 drivers/iommu/dma-iommu.c   |  2 +-
 include/linux/dma-map-ops.h | 10 ++++++----
 kernel/dma/contiguous.c     | 33 +++++++++++++++++++++++----------
 kernel/dma/direct.c         |  2 +-
 kernel/dma/ops_helpers.c    |  2 +-
 kernel/dma/pool.c           |  2 +-
 kernel/kexec_file.c         |  3 ++-
 8 files changed, 38 insertions(+), 21 deletions(-)

diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index 7761099dde9e..9714fcd51941 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -398,7 +398,8 @@ static void *__alloc_from_contiguous(struct device *dev, 
size_t size,
        struct page *page;
        void *ptr = NULL;
 
-       page = dma_alloc_from_contiguous(dev, count, order, gfp & __GFP_NOWARN);
+       page = dma_alloc_from_contiguous(dev, count, order, 0,
+                                        gfp & __GFP_NOWARN);
        if (!page)
                return NULL;
 
@@ -866,7 +867,7 @@ static struct page **__iommu_alloc_buffer(struct device 
*dev, size_t size,
                unsigned long order = get_order(size);
                struct page *page;
 
-               page = dma_alloc_from_contiguous(dev, count, order,
+               page = dma_alloc_from_contiguous(dev, count, order, 0,
                                                 gfp & __GFP_NOWARN);
                if (!page)
                        goto error;
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 58c624513cd4..59baf2687612 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -1627,7 +1627,7 @@ static void *iommu_dma_alloc_pages(struct device *dev, 
size_t size,
        struct page *page = NULL;
        void *cpu_addr;
 
-       page = dma_alloc_contiguous(dev, alloc_size, gfp);
+       page = dma_alloc_contiguous(dev, alloc_size, gfp, 0);
        if (!page)
                page = alloc_pages_node(node, gfp, get_order(alloc_size));
        if (!page)
diff --git a/include/linux/dma-map-ops.h b/include/linux/dma-map-ops.h
index 8fae2b7deb20..5fccda7e5c69 100644
--- a/include/linux/dma-map-ops.h
+++ b/include/linux/dma-map-ops.h
@@ -99,10 +99,11 @@ int __init dma_contiguous_reserve_area(phys_addr_t size, 
phys_addr_t base,
                phys_addr_t limit, struct cma **res_cma, bool fixed);
 
 struct page *dma_alloc_from_contiguous(struct device *dev, size_t count,
-                                      unsigned int order, bool no_warn);
+               unsigned int order, unsigned int required_order, bool no_warn);
 bool dma_release_from_contiguous(struct device *dev, struct page *pages,
                                 int count);
-struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp);
+struct page *dma_alloc_contiguous(struct device *dev, size_t size,
+               gfp_t gfp, unsigned int align_order);
 void dma_free_contiguous(struct device *dev, struct page *page, size_t size);
 
 void dma_contiguous_early_fixup(phys_addr_t base, unsigned long size);
@@ -125,7 +126,8 @@ static inline int dma_contiguous_reserve_area(phys_addr_t 
size,
        return -ENOSYS;
 }
 static inline struct page *dma_alloc_from_contiguous(struct device *dev,
-               size_t count, unsigned int order, bool no_warn)
+               size_t count, unsigned int order, unsigned int required_order,
+               bool no_warn)
 {
        return NULL;
 }
@@ -136,7 +138,7 @@ static inline bool dma_release_from_contiguous(struct 
device *dev,
 }
 /* Use fallback alloc() and free() when CONFIG_DMA_CMA=n */
 static inline struct page *dma_alloc_contiguous(struct device *dev, size_t 
size,
-               gfp_t gfp)
+               gfp_t gfp, unsigned int align_order)
 {
        return NULL;
 }
diff --git a/kernel/dma/contiguous.c b/kernel/dma/contiguous.c
index 66093460584e..a3eb3299b817 100644
--- a/kernel/dma/contiguous.c
+++ b/kernel/dma/contiguous.c
@@ -357,19 +357,25 @@ int __init dma_contiguous_reserve_area(phys_addr_t size, 
phys_addr_t base,
  * dma_alloc_from_contiguous() - allocate pages from contiguous area
  * @dev:   Pointer to device for which the allocation is performed.
  * @count: Requested number of pages.
- * @align: Requested alignment of pages (in PAGE_SIZE order).
+ * @align: Preferred alignment of pages (in PAGE_SIZE order).
+ * @required_align: Minimum required alignment (in PAGE_SIZE order).
  * @no_warn: Avoid printing message about failed allocation.
  *
  * This function allocates memory buffer for specified device. It uses
  * device specific contiguous memory area if available or the default
  * global one. Requires architecture specific dev_get_cma_area() helper
  * function.
+ *
+ * The preferred alignment is capped at CONFIG_CMA_ALIGNMENT. Return NULL
+ * if the required alignment exceeds this limit. A required alignment of
+ * zero preserves the preferred-alignment clamping behavior.
  */
 struct page *dma_alloc_from_contiguous(struct device *dev, size_t count,
-                                      unsigned int align, bool no_warn)
+               unsigned int align, unsigned int required_align, bool no_warn)
 {
-       if (align > CONFIG_CMA_ALIGNMENT)
-               align = CONFIG_CMA_ALIGNMENT;
+       if (required_align > CONFIG_CMA_ALIGNMENT)
+               return NULL;
+       align = min(max(align, required_align), CONFIG_CMA_ALIGNMENT);
 
        return cma_alloc(dev_get_cma_area(dev), count, align, no_warn);
 }
@@ -390,9 +396,14 @@ bool dma_release_from_contiguous(struct device *dev, 
struct page *pages,
        return cma_release(dev_get_cma_area(dev), pages, count);
 }
 
-static struct page *cma_alloc_aligned(struct cma *cma, size_t size, gfp_t gfp)
+static struct page *cma_alloc_aligned(struct cma *cma, size_t size,
+          gfp_t gfp, unsigned int align_order)
 {
-       unsigned int align = min(get_order(size), CONFIG_CMA_ALIGNMENT);
+       unsigned int align;
+
+       if (align_order > CONFIG_CMA_ALIGNMENT)
+               return NULL;
+       align = min(max(get_order(size), align_order), CONFIG_CMA_ALIGNMENT);
 
        return cma_alloc(cma, size >> PAGE_SHIFT, align, gfp & __GFP_NOWARN);
 }
@@ -402,6 +413,7 @@ static struct page *cma_alloc_aligned(struct cma *cma, 
size_t size, gfp_t gfp)
  * @dev:   Pointer to device for which the allocation is performed.
  * @size:  Requested allocation size.
  * @gfp:   Allocation flags.
+ * @align_order: Minimum alignment as a power-of-two page order.
  *
  * tries to use device specific contiguous memory area if available, or it
  * tries to use per-numa cma, if the allocation fails, it will fallback to
@@ -412,7 +424,8 @@ static struct page *cma_alloc_aligned(struct cma *cma, 
size_t size, gfp_t gfp)
  * there is no need to waste CMA pages for that kind; it also helps reduce
  * fragmentations.
  */
-struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp)
+struct page *dma_alloc_contiguous(struct device *dev, size_t size,
+          gfp_t gfp, unsigned int align_order)
 {
 #ifdef CONFIG_DMA_NUMA_CMA
        int nid = dev_to_node(dev);
@@ -422,7 +435,7 @@ struct page *dma_alloc_contiguous(struct device *dev, 
size_t size, gfp_t gfp)
        if (!gfpflags_allow_blocking(gfp))
                return NULL;
        if (dev->cma_area)
-               return cma_alloc_aligned(dev->cma_area, size, gfp);
+               return cma_alloc_aligned(dev->cma_area, size, gfp, align_order);
        if (size <= PAGE_SIZE)
                return NULL;
 
@@ -431,7 +444,7 @@ struct page *dma_alloc_contiguous(struct device *dev, 
size_t size, gfp_t gfp)
                struct cma *cma = dma_contiguous_numa_area[nid];
                struct page *page;
                if (cma) {
-                       page = cma_alloc_aligned(cma, size, gfp);
+                       page = cma_alloc_aligned(cma, size, gfp, align_order);
                        if (page)
                                return page;
                }
@@ -440,7 +453,7 @@ struct page *dma_alloc_contiguous(struct device *dev, 
size_t size, gfp_t gfp)
        if (!dma_contiguous_default_area)
                return NULL;
 
-       return cma_alloc_aligned(dma_contiguous_default_area, size, gfp);
+       return cma_alloc_aligned(dma_contiguous_default_area, size, gfp, 
align_order);
 }
 
 /**
diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
index da665ca22d5c..d968a0c81e73 100644
--- a/kernel/dma/direct.c
+++ b/kernel/dma/direct.c
@@ -124,7 +124,7 @@ static struct page *__dma_direct_alloc_pages(struct device 
*dev, size_t size,
        WARN_ON_ONCE(!PAGE_ALIGNED(size));
 
        gfp |= dma_direct_optimal_gfp_mask(dev, &phys_limit);
-       page = dma_alloc_contiguous(dev, size, gfp);
+       page = dma_alloc_contiguous(dev, size, gfp, 0);
        if (page) {
                if (dma_coherent_ok(dev, page_to_phys(page), size) &&
                    (allow_highmem || !PageHighMem(page)))
diff --git a/kernel/dma/ops_helpers.c b/kernel/dma/ops_helpers.c
index 6b5f9208d31c..8320cc0fada5 100644
--- a/kernel/dma/ops_helpers.c
+++ b/kernel/dma/ops_helpers.c
@@ -66,7 +66,7 @@ struct page *dma_common_alloc_pages(struct device *dev, 
size_t size,
        struct page *page;
        phys_addr_t phys;
 
-       page = dma_alloc_contiguous(dev, size, gfp);
+       page = dma_alloc_contiguous(dev, size, gfp, 0);
        if (!page)
                page = alloc_pages_node(dev_to_node(dev), gfp, get_order(size));
        if (!page)
diff --git a/kernel/dma/pool.c b/kernel/dma/pool.c
index 00f422a1e896..70b7f64b17ab 100644
--- a/kernel/dma/pool.c
+++ b/kernel/dma/pool.c
@@ -99,7 +99,7 @@ static int atomic_pool_expand(struct dma_gen_pool *dma_pool, 
size_t pool_size,
                pool_size = 1 << (PAGE_SHIFT + order);
                if (cma_in_zone(gfp))
                        page = dma_alloc_from_contiguous(NULL, 1 << order,
-                                                        order, false);
+                                                        order, 0, false);
                if (!page)
                        page = alloc_pages(gfp | __GFP_NOWARN, order);
        } while (!page && order-- > 0);
diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
index 59fb9d71e9d8..2a337ce7264e 100644
--- a/kernel/kexec_file.c
+++ b/kernel/kexec_file.c
@@ -682,7 +682,8 @@ static int kexec_alloc_contig(struct kexec_buf *kbuf)
        if (kbuf->image->type == KEXEC_TYPE_CRASH)
                return -EPERM;
 
-       p = dma_alloc_from_contiguous(NULL, nr_pages, 
get_order(kbuf->buf_align), true);
+       p = dma_alloc_from_contiguous(NULL, nr_pages,
+                                     get_order(kbuf->buf_align), 0, true);
        if (!p)
                return -ENOMEM;
 
-- 
2.43.0

Reply via email to