[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20220815151109.180403-5-yishaih@nvidia.com>
Date: Mon, 15 Aug 2022 18:11:03 +0300
From: Yishai Hadas <yishaih@...dia.com>
To: <alex.williamson@...hat.com>, <jgg@...dia.com>
CC: <saeedm@...dia.com>, <kvm@...r.kernel.org>,
<netdev@...r.kernel.org>, <kuba@...nel.org>,
<kevin.tian@...el.com>, <joao.m.martins@...cle.com>,
<leonro@...dia.com>, <yishaih@...dia.com>, <maorg@...dia.com>,
<cohuck@...hat.com>
Subject: [PATCH V4 vfio 04/10] vfio: Add an IOVA bitmap support
From: Joao Martins <joao.m.martins@...cle.com>
The new facility adds a bunch of wrappers that abstract how an IOVA
range is represented in a bitmap that is granulated by a given
page_size. So it translates all the lifting of dealing with user
pointers into its corresponding kernel addresses backing said user
memory into doing finally the (non-atomic) bitmap ops to change
various bits.
The formula for the bitmap is:
data[(iova / page_size) / 64] & (1ULL << (iova % 64))
Where 64 is the number of bits in a unsigned long (depending on arch)
It introduces an IOVA iterator that uses a windowing scheme to minimize
the pinning overhead, as opposed to be pinning it on demand 4K at a
time. So on a 512G and with base page size it would iterate in ranges of
64G at a time, while pinning 512 pages at a time leading to fewer
atomics (specially if the underlying user memory are hugepages).
An example usage of these helpers for a given @base_iova, @page_size, @length
and __user @data:
ret = iova_bitmap_iter_init(&iter, base_iova, page_size, length, data);
if (ret)
return -ENOMEM;
for (; !iova_bitmap_iter_done(&iter) && !ret;
ret = iova_bitmap_iter_advance(&iter)) {
dirty_reporter_ops(&iter.dirty, iova_bitmap_iova(&iter),
iova_bitmap_length(&iter));
}
iova_bitmap_iter_free(&iter);
An implementation of the lower end -- referred to above as dirty_reporter_ops
to exemplify -- that is tracking dirty bits would mark an IOVA as dirty
as following:
iova_bitmap_set(&iter.dirty, iova, page_size);
or a contiguous range (example two pages):
iova_bitmap_set(&iter.dirty, iova, 2 * page_size);
The facility is intended to be used for user bitmaps representing
dirtied IOVAs by IOMMU (via IOMMUFD) and PCI Devices (via vfio-pci).
Signed-off-by: Joao Martins <joao.m.martins@...cle.com>
Signed-off-by: Yishai Hadas <yishaih@...dia.com>
---
drivers/vfio/Makefile | 6 +-
drivers/vfio/iova_bitmap.c | 224 ++++++++++++++++++++++++++++++++++++
include/linux/iova_bitmap.h | 189 ++++++++++++++++++++++++++++++
3 files changed, 417 insertions(+), 2 deletions(-)
create mode 100644 drivers/vfio/iova_bitmap.c
create mode 100644 include/linux/iova_bitmap.h
diff --git a/drivers/vfio/Makefile b/drivers/vfio/Makefile
index 1a32357592e3..1d6cad32d366 100644
--- a/drivers/vfio/Makefile
+++ b/drivers/vfio/Makefile
@@ -1,9 +1,11 @@
# SPDX-License-Identifier: GPL-2.0
vfio_virqfd-y := virqfd.o
-vfio-y += vfio_main.o
-
obj-$(CONFIG_VFIO) += vfio.o
+
+vfio-y := vfio_main.o \
+ iova_bitmap.o \
+
obj-$(CONFIG_VFIO_VIRQFD) += vfio_virqfd.o
obj-$(CONFIG_VFIO_IOMMU_TYPE1) += vfio_iommu_type1.o
obj-$(CONFIG_VFIO_IOMMU_SPAPR_TCE) += vfio_iommu_spapr_tce.o
diff --git a/drivers/vfio/iova_bitmap.c b/drivers/vfio/iova_bitmap.c
new file mode 100644
index 000000000000..6b6008ef146c
--- /dev/null
+++ b/drivers/vfio/iova_bitmap.c
@@ -0,0 +1,224 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022, Oracle and/or its affiliates.
+ * Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved
+ */
+#include <linux/iova_bitmap.h>
+#include <linux/highmem.h>
+
+#define BITS_PER_PAGE (PAGE_SIZE * BITS_PER_BYTE)
+
+static void iova_bitmap_iter_put(struct iova_bitmap_iter *iter);
+
+/*
+ * Converts a relative IOVA to a bitmap index.
+ * The bitmap is viewed an array of u64, and each u64 represents
+ * a range of IOVA, and the whole pinned pages to the range window.
+ * Relative IOVA means relative to the iter::dirty base IOVA (stored
+ * in dirty::iova). All computations in this file are done using
+ * relative IOVAs and thus avoid an extra subtraction against
+ * dirty::iova. The user API iova_bitmap_set() always uses a regular
+ * absolute IOVAs.
+ */
+static unsigned long iova_bitmap_iova_to_index(struct iova_bitmap_iter *iter,
+ unsigned long iova)
+{
+ unsigned long pgsize = 1 << iter->dirty.pgshift;
+
+ return iova / (BITS_PER_TYPE(*iter->data) * pgsize);
+}
+
+/*
+ * Converts a bitmap index to a *relative* IOVA.
+ */
+static unsigned long iova_bitmap_index_to_iova(struct iova_bitmap_iter *iter,
+ unsigned long index)
+{
+ unsigned long pgshift = iter->dirty.pgshift;
+
+ return (index * BITS_PER_TYPE(*iter->data)) << pgshift;
+}
+
+/*
+ * Pins the bitmap user pages for the current range window.
+ * This is internal to IOVA bitmap and called when advancing the
+ * iterator.
+ */
+static int iova_bitmap_iter_get(struct iova_bitmap_iter *iter)
+{
+ struct iova_bitmap *dirty = &iter->dirty;
+ unsigned long npages;
+ u64 __user *addr;
+ long ret;
+
+ /*
+ * @offset is the cursor of the currently mapped u64 words
+ * that we have access. And it indexes u64 bitmap word that is
+ * mapped. Anything before @offset is not mapped. The range
+ * @offset .. @count is mapped but capped at a maximum number
+ * of pages.
+ */
+ npages = DIV_ROUND_UP((iter->count - iter->offset) *
+ sizeof(*iter->data), PAGE_SIZE);
+
+ /*
+ * We always cap at max number of 'struct page' a base page can fit.
+ * This is, for example, on x86 means 2M of bitmap data max.
+ */
+ npages = min(npages, PAGE_SIZE / sizeof(struct page *));
+ addr = iter->data + iter->offset;
+ ret = pin_user_pages_fast((unsigned long)addr, npages,
+ FOLL_WRITE, dirty->pages);
+ if (ret <= 0)
+ return -EFAULT;
+
+ dirty->npages = (unsigned long)ret;
+ /* Base IOVA where @pages point to i.e. bit 0 of the first page */
+ dirty->iova = iova_bitmap_iova(iter);
+
+ /*
+ * offset of the page where pinned pages bit 0 is located.
+ * This handles the case where the bitmap is not PAGE_SIZE
+ * aligned.
+ */
+ dirty->start_offset = offset_in_page(addr);
+ return 0;
+}
+
+/*
+ * Unpins the bitmap user pages and clears @npages
+ * (un)pinning is abstracted from API user and it's done
+ * when advancing or freeing the iterator.
+ */
+static void iova_bitmap_iter_put(struct iova_bitmap_iter *iter)
+{
+ struct iova_bitmap *dirty = &iter->dirty;
+
+ if (dirty->npages) {
+ unpin_user_pages(dirty->pages, dirty->npages);
+ dirty->npages = 0;
+ }
+}
+
+int iova_bitmap_iter_init(struct iova_bitmap_iter *iter,
+ unsigned long iova, unsigned long length,
+ unsigned long page_size, u64 __user *data)
+{
+ struct iova_bitmap *dirty = &iter->dirty;
+
+ memset(iter, 0, sizeof(*iter));
+ dirty->pgshift = __ffs(page_size);
+ iter->data = data;
+ iter->count = iova_bitmap_iova_to_index(iter, length - 1) + 1;
+ iter->iova = iova;
+ iter->length = length;
+
+ dirty->iova = iova;
+ dirty->pages = (struct page **)__get_free_page(GFP_KERNEL);
+ if (!dirty->pages)
+ return -ENOMEM;
+
+ return iova_bitmap_iter_get(iter);
+}
+
+void iova_bitmap_iter_free(struct iova_bitmap_iter *iter)
+{
+ struct iova_bitmap *dirty = &iter->dirty;
+
+ iova_bitmap_iter_put(iter);
+
+ if (dirty->pages) {
+ free_page((unsigned long)dirty->pages);
+ dirty->pages = NULL;
+ }
+
+ memset(iter, 0, sizeof(*iter));
+}
+
+unsigned long iova_bitmap_iova(struct iova_bitmap_iter *iter)
+{
+ unsigned long skip = iter->offset;
+
+ return iter->iova + iova_bitmap_index_to_iova(iter, skip);
+}
+
+/*
+ * Returns the remaining bitmap indexes count to process for the currently pinned
+ * bitmap pages.
+ */
+static unsigned long iova_bitmap_iter_remaining(struct iova_bitmap_iter *iter)
+{
+ unsigned long remaining = iter->count - iter->offset;
+
+ remaining = min_t(unsigned long, remaining,
+ (iter->dirty.npages << PAGE_SHIFT) / sizeof(*iter->data));
+
+ return remaining;
+}
+
+unsigned long iova_bitmap_length(struct iova_bitmap_iter *iter)
+{
+ unsigned long max_iova = iter->iova + iter->length - 1;
+ unsigned long iova = iova_bitmap_iova(iter);
+ unsigned long remaining;
+
+ /*
+ * iova_bitmap_iter_remaining() returns a number of indexes which
+ * when converted to IOVA gives us a max length that the bitmap
+ * pinned data can cover. Afterwards, that is capped to
+ * only cover the IOVA range in @iter::iova .. iter::length.
+ */
+ remaining = iova_bitmap_index_to_iova(iter,
+ iova_bitmap_iter_remaining(iter));
+
+ if (iova + remaining - 1 > max_iova)
+ remaining -= ((iova + remaining - 1) - max_iova);
+
+ return remaining;
+}
+
+bool iova_bitmap_iter_done(struct iova_bitmap_iter *iter)
+{
+ return iter->offset >= iter->count;
+}
+
+int iova_bitmap_iter_advance(struct iova_bitmap_iter *iter)
+{
+ unsigned long iova = iova_bitmap_length(iter) - 1;
+ unsigned long count = iova_bitmap_iova_to_index(iter, iova) + 1;
+
+ iter->offset += count;
+
+ iova_bitmap_iter_put(iter);
+ if (iova_bitmap_iter_done(iter))
+ return 0;
+
+ /* When we advance the iterator we pin the next set of bitmap pages */
+ return iova_bitmap_iter_get(iter);
+}
+
+unsigned long iova_bitmap_set(struct iova_bitmap *dirty,
+ unsigned long iova, unsigned long length)
+{
+ unsigned long nbits = max(1UL, length >> dirty->pgshift), set = nbits;
+ unsigned long offset = (iova - dirty->iova) >> dirty->pgshift;
+ unsigned long page_idx = offset / BITS_PER_PAGE;
+ unsigned long page_offset = dirty->start_offset;
+ void *kaddr;
+
+ offset = offset % BITS_PER_PAGE;
+
+ do {
+ unsigned long size = min(BITS_PER_PAGE - offset, nbits);
+
+ kaddr = kmap_local_page(dirty->pages[page_idx]);
+ bitmap_set(kaddr + page_offset, offset, size);
+ kunmap_local(kaddr);
+ page_offset = offset = 0;
+ nbits -= size;
+ page_idx++;
+ } while (nbits > 0);
+
+ return set;
+}
+EXPORT_SYMBOL_GPL(iova_bitmap_set);
diff --git a/include/linux/iova_bitmap.h b/include/linux/iova_bitmap.h
new file mode 100644
index 000000000000..e258d03386d3
--- /dev/null
+++ b/include/linux/iova_bitmap.h
@@ -0,0 +1,189 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2022, Oracle and/or its affiliates.
+ * Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved
+ */
+#ifndef _IOVA_BITMAP_H_
+#define _IOVA_BITMAP_H_
+
+#include <linux/mm.h>
+
+/**
+ * struct iova_bitmap - A bitmap representing a portion IOVA space
+ *
+ * Main data structure for tracking dirty IOVAs.
+ *
+ * For example something recording dirty IOVAs, will be provided of a
+ * struct iova_bitmap structure. This structure only represents a
+ * subset of the total IOVA space pinned by its parent counterpart
+ * iterator object.
+ *
+ * The user does not need to exact location of the bits in the bitmap.
+ * From user perspective the bitmap the only API available to the dirty
+ * tracker is iova_bitmap_set() which records the dirty IOVA *range*
+ * in the bitmap data.
+ *
+ * The bitmap is an array of u64 whereas each bit represents an IOVA
+ * of range of (1 << pgshift). Thus formula for the bitmap data to be
+ * set is:
+ *
+ * data[(iova / page_size) / 64] & (1ULL << (iova % 64))
+ */
+struct iova_bitmap {
+ /* base IOVA representing bit 0 of the first page */
+ unsigned long iova;
+
+ /* page size order that each bit granules to */
+ unsigned long pgshift;
+
+ /* offset of the first user page pinned */
+ unsigned long start_offset;
+
+ /* number of pages pinned */
+ unsigned long npages;
+
+ /* pinned pages representing the bitmap data */
+ struct page **pages;
+};
+
+/**
+ * struct iova_bitmap_iter - Iterator object of the IOVA bitmap
+ *
+ * Main data structure for walking the bitmap data.
+ *
+ * Abstracts the pinning work to iterate an IOVA ranges.
+ * It uses a windowing scheme and pins the bitmap in relatively
+ * big ranges e.g.
+ *
+ * The iterator uses one base page to store all the pinned pages
+ * pointers related to the bitmap. For sizeof(struct page) == 64 it
+ * stores 512 struct pages which, if base page size is 4096 it means 2M
+ * of bitmap data is pinned at a time. If the iova_bitmap page size is
+ * also base page size then the range window to iterate is 64G.
+ *
+ * For example iterating on a total IOVA range of 4G..128G, it will
+ * walk through this set of ranges:
+ *
+ * - 4G - 68G-1 (64G)
+ * - 68G - 128G-1 (64G)
+ *
+ * An example of the APIs on how to iterate the IOVA bitmap:
+ *
+ * ret = iova_bitmap_iter_init(&iter, iova, PAGE_SIZE, length, data);
+ * if (ret)
+ * return -ENOMEM;
+ *
+ * for (; !iova_bitmap_iter_done(&iter) && !ret;
+ * ret = iova_bitmap_iter_advance(&iter)) {
+ *
+ * dirty_reporter_ops(&iter.dirty, iova_bitmap_iova(&iter),
+ * iova_bitmap_length(&iter));
+ * }
+ *
+ * An implementation of the lower end (referred to above as
+ * dirty_reporter_ops) that is tracking dirty bits would:
+ *
+ * if (iova_dirty)
+ * iova_bitmap_set(&iter.dirty, iova, PAGE_SIZE);
+ *
+ * The internals of the object use a cursor @offset that indexes
+ * which part u64 word of the bitmap is mapped, up to @count.
+ * Those keep being incremented until @count reaches while mapping
+ * up to PAGE_SIZE / sizeof(struct page*) maximum of pages.
+ *
+ * The iterator is usually located on what tracks DMA mapped ranges
+ * or some form of IOVA range tracking that co-relates to the user
+ * passed bitmap.
+ */
+struct iova_bitmap_iter {
+ /* IOVA range representing the currently pinned bitmap data */
+ struct iova_bitmap dirty;
+
+ /* userspace address of the bitmap */
+ u64 __user *data;
+
+ /* u64 index that @dirty points to */
+ size_t offset;
+
+ /* how many u64 can we walk in total */
+ size_t count;
+
+ /* base IOVA of the whole bitmap */
+ unsigned long iova;
+
+ /* length of the IOVA range for the whole bitmap */
+ unsigned long length;
+};
+
+/**
+ * iova_bitmap_iter_init() - Initializes an IOVA bitmap iterator object.
+ * @iter: IOVA bitmap iterator to initialize
+ * @iova: Start address of the IOVA range
+ * @length: Length of the IOVA range
+ * @page_size: Page size of the IOVA bitmap. It defines what each bit
+ * granularity represents
+ * @data: Userspace address of the bitmap
+ *
+ * Initializes all the fields in the IOVA iterator including the first
+ * user pages of @data. Returns 0 on success or otherwise errno on error.
+ */
+int iova_bitmap_iter_init(struct iova_bitmap_iter *iter, unsigned long iova,
+ unsigned long length, unsigned long page_size,
+ u64 __user *data);
+
+/**
+ * iova_bitmap_iter_free() - Frees an IOVA bitmap iterator object
+ * @iter: IOVA bitmap iterator to free
+ *
+ * It unpins and releases pages array memory and clears any leftover
+ * state.
+ */
+void iova_bitmap_iter_free(struct iova_bitmap_iter *iter);
+
+/**
+ * iova_bitmap_iter_done: Checks if the IOVA bitmap has data to iterate
+ * @iter: IOVA bitmap iterator to free
+ *
+ * Returns true if there's more data to iterate.
+ */
+bool iova_bitmap_iter_done(struct iova_bitmap_iter *iter);
+
+/**
+ * iova_bitmap_iter_advance: Advances the IOVA bitmap iterator
+ * @iter: IOVA bitmap iterator to advance
+ *
+ * Advances to the next range, releases the current pinned
+ * pages and pins the next set of bitmap pages.
+ * Returns 0 on success or otherwise errno.
+ */
+int iova_bitmap_iter_advance(struct iova_bitmap_iter *iter);
+
+/**
+ * iova_bitmap_iova: Base IOVA of the current range
+ * @iter: IOVA bitmap iterator
+ *
+ * Returns the base IOVA of the current range.
+ */
+unsigned long iova_bitmap_iova(struct iova_bitmap_iter *iter);
+
+/**
+ * iova_bitmap_length: IOVA length of the current range
+ * @iter: IOVA bitmap iterator
+ *
+ * Returns the length of the current IOVA range.
+ */
+unsigned long iova_bitmap_length(struct iova_bitmap_iter *iter);
+
+/**
+ * iova_bitmap_set: Marks an IOVA range as dirty
+ * @dirty: IOVA bitmap
+ * @iova: IOVA to mark as dirty
+ * @length: IOVA range length
+ *
+ * Marks the range [iova .. iova+length-1] as dirty in the bitmap.
+ * Returns the number of bits set.
+ */
+unsigned long iova_bitmap_set(struct iova_bitmap *dirty,
+ unsigned long iova, unsigned long length);
+
+#endif
--
2.18.1
Powered by blists - more mailing lists