From: Alex Mastro <amastro@fb.com>
To: David Matlack <dmatlack@google.com>
Cc: Alex Williamson <alex@shazbot.org>, Shuah Khan <shuah@kernel.org>,
<kvm@vger.kernel.org>, <linux-kselftest@vger.kernel.org>,
<linux-kernel@vger.kernel.org>, Jason Gunthorpe <jgg@ziepe.ca>
Subject: Re: [PATCH 3/4] vfio: selftests: add iova allocator
Date: Mon, 10 Nov 2025 15:14:21 -0800 [thread overview]
Message-ID: <aRJxzUte5AUxebIC@devgpu015.cco6.facebook.com> (raw)
In-Reply-To: <aRJtDHJZ6yAW2xIj@google.com>
On Mon, Nov 10, 2025 at 10:54:04PM +0000, David Matlack wrote:
> On 2025-11-10 01:10 PM, Alex Mastro wrote:
> > Add struct iova_allocator, which gives tests a convenient way to generate
> > legally-accessible IOVAs to map.
> >
> > This is based on Alex Williamson's patch series for adding an IOVA
> > allocator [1].
> >
> > [1] https://lore.kernel.org/all/20251108212954.26477-1-alex@shazbot.org/
> >
> > Signed-off-by: Alex Mastro <amastro@fb.com>
> > ---
> > .../testing/selftests/vfio/lib/include/vfio_util.h | 14 +++++
> > tools/testing/selftests/vfio/lib/vfio_pci_device.c | 65 +++++++++++++++++++++-
> > 2 files changed, 78 insertions(+), 1 deletion(-)
> >
> > diff --git a/tools/testing/selftests/vfio/lib/include/vfio_util.h b/tools/testing/selftests/vfio/lib/include/vfio_util.h
> > index fb5efec52316..bb1e7d39dfb9 100644
> > --- a/tools/testing/selftests/vfio/lib/include/vfio_util.h
> > +++ b/tools/testing/selftests/vfio/lib/include/vfio_util.h
> > @@ -13,6 +13,8 @@
> >
> > #include "../../../kselftest.h"
> >
> > +#define ALIGN(x, a) (((x) + (a - 1)) & (~((a) - 1)))
>
> Please name this ALIGN_UP() so that it is clear it aligns x up and not
> down.
Ack.
> > +
> > #define VFIO_LOG_AND_EXIT(...) do { \
> > fprintf(stderr, " " __VA_ARGS__); \
> > fprintf(stderr, "\n"); \
> > @@ -188,6 +190,13 @@ struct vfio_pci_device {
> > struct vfio_pci_driver driver;
> > };
> >
> > +struct iova_allocator {
> > + struct iommu_iova_range *ranges;
> > + size_t nranges;
> > + size_t range_idx;
> > + iova_t iova_next;
> > +};
> > +
> > /*
> > * Return the BDF string of the device that the test should use.
> > *
> > @@ -212,6 +221,11 @@ void vfio_pci_device_reset(struct vfio_pci_device *device);
> > struct iommu_iova_range *vfio_pci_iova_ranges(struct vfio_pci_device *device,
> > size_t *nranges);
> >
> > +int iova_allocator_init(struct vfio_pci_device *device,
> > + struct iova_allocator *allocator);
> > +void iova_allocator_deinit(struct iova_allocator *allocator);
> > +iova_t iova_allocator_alloc(struct iova_allocator *allocator, size_t size);
> > +
> > int __vfio_pci_dma_map(struct vfio_pci_device *device,
> > struct vfio_dma_region *region);
> > int __vfio_pci_dma_unmap(struct vfio_pci_device *device,
> > diff --git a/tools/testing/selftests/vfio/lib/vfio_pci_device.c b/tools/testing/selftests/vfio/lib/vfio_pci_device.c
> > index 6bedbe65f0a1..a634feb1d378 100644
> > --- a/tools/testing/selftests/vfio/lib/vfio_pci_device.c
> > +++ b/tools/testing/selftests/vfio/lib/vfio_pci_device.c
> > @@ -12,11 +12,12 @@
> > #include <sys/mman.h>
> >
> > #include <uapi/linux/types.h>
> > +#include <linux/iommufd.h>
> > #include <linux/limits.h>
> > #include <linux/mman.h>
> > +#include <linux/overflow.h>
> > #include <linux/types.h>
> > #include <linux/vfio.h>
> > -#include <linux/iommufd.h>
> >
> > #include "../../../kselftest.h"
> > #include <vfio_util.h>
> > @@ -190,6 +191,68 @@ struct iommu_iova_range *vfio_pci_iova_ranges(struct vfio_pci_device *device,
> > return ranges;
> > }
> >
> > +int iova_allocator_init(struct vfio_pci_device *device,
> > + struct iova_allocator *allocator)
> > +{
> > + struct iommu_iova_range *ranges;
> > + size_t nranges;
> > +
> > + memset(allocator, 0, sizeof(*allocator));
> > +
> > + ranges = vfio_pci_iova_ranges(device, &nranges);
> > + if (!ranges)
> > + return -ENOENT;
> > +
> > + *allocator = (struct iova_allocator){
> > + .ranges = ranges,
> > + .nranges = nranges,
> > + .range_idx = 0,
> > + .iova_next = 0,
> > + };
> > +
> > + return 0;
> > +}
> > +
> > +void iova_allocator_deinit(struct iova_allocator *allocator)
> > +{
> > + free(allocator->ranges);
> > +}
>
> I think it would be good to be consistent about how the library hands
> out and initializes objects. e.g. For devices we have:
>
> device = vfio_pci_device_init(...);
> vfio_pci_device_cleanup(device);
>
> So for allocator it would be:
>
> allocator = iova_allocator_init();
> iova_allocator_cleanup(allocator);
>
> It's a small thing, but this way users of the library can always work
> with pointers allocated by the library, there is a consistent meaning of
> *_init() functions, and one doesn't have to distinguish between
> *_deinit() and *_cleanup().
>
> Forcing dynamic memory allocation is less efficient, but I think
> simplicity and consistency matters more when it comes to tests.
SGTM -- will change it to be like how you suggested for consistency.
>
> > +
> > +iova_t iova_allocator_alloc(struct iova_allocator *allocator, size_t size)
> > +{
> > + int idx = allocator->range_idx;
> > + struct iommu_iova_range *range = &allocator->ranges[idx];
> > +
> > + VFIO_ASSERT_LT(idx, allocator->nranges, "IOVA allocator out of space\n");
> > + VFIO_ASSERT_GT(size, 0, "Invalid size arg, zero\n");
> > + VFIO_ASSERT_EQ(size & (size - 1), 0, "Invalid size arg, non-power-of-2\n");
>
> ALIGN() is what requires size to be a power of 2, so the assert should
> probably go inside that macro.
SGTM
>
> > +
> > + for (;;) {
> > + iova_t iova, last;
> > +
> > + iova = ALIGN(allocator->iova_next, size);
> > +
> > + if (iova < allocator->iova_next || iova > range->last ||
> > + check_add_overflow(iova, size - 1, &last) ||
> > + last > range->last) {
> > + allocator->range_idx = ++idx;
> > + VFIO_ASSERT_LT(idx, allocator->nranges,
> > + "Out of ranges for allocation\n");
> > + allocator->iova_next = (++range)->start;
> > + continue;
> > + }
> > +
> > + if (check_add_overflow(last, (iova_t)1, &allocator->iova_next) ||
> > + allocator->iova_next > range->last) {
> > + allocator->range_idx = ++idx;
> > + if (idx < allocator->nranges)
> > + allocator->iova_next = (++range)->start;
> > + }
> > +
> > + return iova;
> > + }
>
> I found this loop a bit hard to read. The if statements have 3-4
> statements, and idx and range are managed deep in the loop. What about
> something like this? It also avoids the need to check for overflow
> (unless I missed something :).
I'll take a closer look at your suggestions. Agree that it's terse. I
shamelessly lifted this verbatim from AlexW's patch, and it seemed ok from my
first pass squinting.
>
> diff --git a/tools/testing/selftests/vfio/lib/include/vfio_util.h b/tools/testing/selftests/vfio/lib/include/vfio_util.h
> index bb1e7d39dfb9..63fce0ffe287 100644
> --- a/tools/testing/selftests/vfio/lib/include/vfio_util.h
> +++ b/tools/testing/selftests/vfio/lib/include/vfio_util.h
> @@ -193,8 +193,10 @@ struct vfio_pci_device {
> struct iova_allocator {
> struct iommu_iova_range *ranges;
> size_t nranges;
> +
> + /* The next range, and offset within it, from which to allocate. */
> size_t range_idx;
> - iova_t iova_next;
> + iova_t range_offset;
> };
>
> /*
> diff --git a/tools/testing/selftests/vfio/lib/vfio_pci_device.c b/tools/testing/selftests/vfio/lib/vfio_pci_device.c
> index a634feb1d378..5b85005c4544 100644
> --- a/tools/testing/selftests/vfio/lib/vfio_pci_device.c
> +++ b/tools/testing/selftests/vfio/lib/vfio_pci_device.c
> @@ -207,7 +207,7 @@ int iova_allocator_init(struct vfio_pci_device *device,
> .ranges = ranges,
> .nranges = nranges,
> .range_idx = 0,
> - .iova_next = 0,
> + .range_offset = 0,
> };
>
> return 0;
> @@ -220,37 +220,41 @@ void iova_allocator_deinit(struct iova_allocator *allocator)
>
> iova_t iova_allocator_alloc(struct iova_allocator *allocator, size_t size)
> {
> - int idx = allocator->range_idx;
> - struct iommu_iova_range *range = &allocator->ranges[idx];
> + int idx;
>
> - VFIO_ASSERT_LT(idx, allocator->nranges, "IOVA allocator out of space\n");
> VFIO_ASSERT_GT(size, 0, "Invalid size arg, zero\n");
> VFIO_ASSERT_EQ(size & (size - 1), 0, "Invalid size arg, non-power-of-2\n");
>
> - for (;;) {
> + for (idx = allocator->range_idx; idx < allocator->nranges; idx++) {
> + struct iommu_iova_range *range = &allocator->ranges[idx];
> iova_t iova, last;
>
> - iova = ALIGN(allocator->iova_next, size);
> + if (idx == allocator->range_idx)
> + iova = ALIGN(range->start + allocator->range_offset, size);
> + else
> + iova = ALIGN(range->start, size);
>
> - if (iova < allocator->iova_next || iova > range->last ||
> - check_add_overflow(iova, size - 1, &last) ||
> - last > range->last) {
> - allocator->range_idx = ++idx;
> - VFIO_ASSERT_LT(idx, allocator->nranges,
> - "Out of ranges for allocation\n");
> - allocator->iova_next = (++range)->start;
> + if (range->last - iova + 1 < size)
> continue;
> - }
>
> - if (check_add_overflow(last, (iova_t)1, &allocator->iova_next) ||
> - allocator->iova_next > range->last) {
> - allocator->range_idx = ++idx;
> - if (idx < allocator->nranges)
> - allocator->iova_next = (++range)->start;
> + /*
> + * Found a range to hold the allocation. Update the allocator
> + * for the next allocation.
> + */
> + last = iova + (size - 1);
> +
> + if (last < range->last) {
> + allocator->range_idx = idx;
> + allocator->range_offset = last - range->start + 1;
> + } else {
> + allocator->range_idx = idx + 1;
> + allocator->range_offset = 0;
> }
>
> return iova;
> }
> +
> + VFIO_FAIL("Failed to iova range of size 0x%lx\n", size);
> }
>
> iova_t __to_iova(struct vfio_pci_device *device, void *vaddr)
>
> > +}
> > +
> > iova_t __to_iova(struct vfio_pci_device *device, void *vaddr)
> > {
> > struct vfio_dma_region *region;
> >
> > --
> > 2.47.3
> >
next prev parent reply other threads:[~2025-11-10 23:14 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-11-10 21:10 [PATCH 0/4] vfio: selftests: update DMA mapping tests to use queried IOVA ranges Alex Mastro
2025-11-10 21:10 ` [PATCH 1/4] vfio: selftests: add iova range query helpers Alex Mastro
2025-11-10 21:31 ` Alex Williamson
2025-11-10 22:35 ` Alex Mastro
2025-11-10 22:03 ` David Matlack
2025-11-10 22:32 ` Alex Mastro
2025-11-10 23:02 ` David Matlack
2025-11-10 23:08 ` Alex Mastro
2025-11-10 21:10 ` [PATCH 2/4] vfio: selftests: fix map limit tests to use last available iova Alex Mastro
2025-11-10 21:31 ` Alex Williamson
2025-11-10 22:38 ` Alex Mastro
2025-11-11 0:09 ` David Matlack
2025-11-10 21:10 ` [PATCH 3/4] vfio: selftests: add iova allocator Alex Mastro
2025-11-10 21:31 ` Alex Williamson
2025-11-10 22:37 ` Alex Mastro
2025-11-10 22:54 ` David Matlack
2025-11-10 23:14 ` Alex Mastro [this message]
2025-11-10 21:10 ` [PATCH 4/4] vfio: selftests: update vfio_dma_mapping_test to allocate iovas Alex Mastro
2025-11-10 21:31 ` Alex Williamson
2025-11-10 22:36 ` Alex Mastro
2025-11-10 23:06 ` [PATCH 0/4] vfio: selftests: update DMA mapping tests to use queried IOVA ranges David Matlack
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=aRJxzUte5AUxebIC@devgpu015.cco6.facebook.com \
--to=amastro@fb.com \
--cc=alex@shazbot.org \
--cc=dmatlack@google.com \
--cc=jgg@ziepe.ca \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=shuah@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.