[PATCH 3/4] lmb: Add basic io_lmb functionality

Sughosh Ganu sughosh.ganu at linaro.org
Tue Nov 5 20:18:45 CET 2024


On Fri, 1 Nov 2024 at 15:47, Janne Grunau <j at jannau.net> wrote:
>
> These functions can be used with struct lmb pointers and will be used to
> manage IOVA space in the apple_dart iommu driver. This restores part of
> the pointer base struct lmb API from before commit ed17a33fed29 ("lmb:
> make LMB memory map persistent and global").
> io_lmb_add() and io_lmb_free() can trivially reuse exisiting lmb
> functions. io_lmb_setup() is separate for unique error log messages.
> io_lmb_alloc() is a simplified copy of _lmb_alloc_base() since the
> later has unused features and internal use of the global LMB memory map.
>
> Signed-off-by: Janne Grunau <j at jannau.net>
> ---
>  include/lmb.h | 47 +++++++++++++++++++++++++++++++++++++
>  lib/lmb.c     | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 121 insertions(+)
>
> diff --git a/include/lmb.h b/include/lmb.h
> index 2201d6f2b67bb605dbff015fa2a6a008b780c57a..9cb3ba8289e10ab92bea135d248580e44880e3e2 100644
> --- a/include/lmb.h
> +++ b/include/lmb.h
> @@ -156,6 +156,53 @@ static inline int lmb_read_check(phys_addr_t addr, phys_size_t len)
>         return lmb_alloc_addr(addr, len) == addr ? 0 : -1;
>  }
>
> +/**
> + * io_lmb_setup() - Initialize LMB struct
> + * @lmb: IO LMB to initialize
> + *
> + * Add the IOVA space [base, base + size] to be managed by io_lmb.
> + *
> + * Returns: 0 on success, negative error code on failure
> + */
> +int io_lmb_setup(struct lmb *io_lmb);
> +
> +/**
> + * io_lmb_add() - Add an IOVA range for allocations
> + * @io_lmb: LMB to add the space to
> + * @base: Base Address of region to add
> + * @size: Size of the region to add
> + *
> + * Add the IOVA space [base, base + size] to be managed by io_lmb.
> + *
> + * Returns: 0 if the region addition was successful, -1 on failure
> + */
> +long io_lmb_add(struct lmb *io_lmb, phys_addr_t base, phys_size_t size);
> +
> +/**
> + * io_lmb_alloc() - Allocate specified IO memory address with specified alignment
> + * @io_lmb: LMB to alloc from
> + * @size: Size of the region requested
> + * @align: Required address and size alignment
> + *
> + * Allocate a region of IO memory. The base parameter is used to specify the
> + * base address of the requested region.
> + *
> + * Return: base IO address on success, 0 on error
> + */
> +phys_addr_t io_lmb_alloc(struct lmb *io_lmb, phys_size_t size, ulong align);
> +
> +/**
> + * io_lmb_free() - Free up a region of IOVA space
> + * @io_lmb: LMB to return the IO address space to
> + * @base: Base Address of region to be freed
> + * @size: Size of the region to be freed
> + *
> + * Free up a region of IOVA space.
> + *
> + * Return: 0 if successful, -1 on failure
> + */
> +long io_lmb_free(struct lmb *io_lmb, phys_addr_t base, phys_size_t size);
> +
>  #endif /* __KERNEL__ */
>
>  #endif /* _LINUX_LMB_H */
> diff --git a/lib/lmb.c b/lib/lmb.c
> index 2ce91f7521cfe58577b092fdaea8004b17f0eeff..cbf1ee0df93a9cb0cfea571a4caebae09646595e 100644
> --- a/lib/lmb.c
> +++ b/lib/lmb.c
> @@ -351,6 +351,80 @@ static phys_addr_t lmb_align_down(phys_addr_t addr, phys_size_t size)
>         return addr & ~(size - 1);
>  }
>
> +/*
> + * IOVA LMB memory maps using lmb pointers instead of the global LMB memory map.
> + */
> +
> +int io_lmb_setup(struct lmb *io_lmb)
> +{
> +       int ret;
> +
> +       ret = alist_init(&io_lmb->free_mem, sizeof(struct lmb_region),
> +                        (uint)LMB_ALIST_INITIAL_SIZE);
> +       if (!ret) {
> +               log_debug("Unable to initialise the list for LMB free IOVA\n");
> +               return -ENOMEM;
> +       }
> +
> +       ret = alist_init(&io_lmb->used_mem, sizeof(struct lmb_region),
> +                        (uint)LMB_ALIST_INITIAL_SIZE);
> +       if (!ret) {
> +               log_debug("Unable to initialise the list for LMB used IOVA\n");
> +               return -ENOMEM;
> +       }
> +
> +       io_lmb->test = false;
> +
> +       return 0;
> +}

In the current use case, the above data structure is getting allocated
as part of the driver's probe function. The resource should be free'd
from the driver's remove function -- that should happen through a
corresponding alist_uninit() call.

-sughosh

> +
> +long io_lmb_add(struct lmb *io_lmb, phys_addr_t base, phys_size_t size)
> +{
> +       return lmb_add_region_flags(&io_lmb->free_mem, base, size, LMB_NONE);
> +}
> +
> +/* derived and simplified from _lmb_alloc_base() */
> +phys_addr_t io_lmb_alloc(struct lmb *io_lmb, phys_size_t size, ulong align)
> +{
> +       long i, rgn;
> +       phys_addr_t base = 0;
> +       phys_addr_t res_base;
> +       struct lmb_region *lmb_used = io_lmb->used_mem.data;
> +       struct lmb_region *lmb_memory = io_lmb->free_mem.data;
> +
> +       for (i = io_lmb->free_mem.count - 1; i >= 0; i--) {
> +               phys_addr_t lmbbase = lmb_memory[i].base;
> +               phys_size_t lmbsize = lmb_memory[i].size;
> +
> +               if (lmbsize < size)
> +                       continue;
> +               base = lmb_align_down(lmbbase + lmbsize - size, align);
> +
> +               while (base && lmbbase <= base) {
> +                       rgn = lmb_overlaps_region(&io_lmb->used_mem, base, size);
> +                       if (rgn < 0) {
> +                               /* This area isn't reserved, take it */
> +                               if (lmb_add_region_flags(&io_lmb->used_mem, base,
> +                                                        size, LMB_NONE) < 0)
> +                                       return 0;
> +
> +                               return base;
> +                       }
> +
> +                       res_base = lmb_used[rgn].base;
> +                       if (res_base < size)
> +                               break;
> +                       base = lmb_align_down(res_base - size, align);
> +               }
> +       }
> +       return 0;
> +}
> +
> +long io_lmb_free(struct lmb *io_lmb, phys_addr_t base, phys_size_t size)
> +{
> +       return _lmb_free(&io_lmb->used_mem, base, size);
> +}
> +
>  /*
>   * Low level LMB functions are used to manage IOVA memory maps for the Apple
>   * dart iommu. They must not access the global LMB memory map.
>
> --
> 2.47.0
>


More information about the U-Boot mailing list