Commit b058ea3a authored by Joao Martins's avatar Joao Martins Committed by Alex Williamson

vfio/iova_bitmap: refactor iova_bitmap_set() to better handle page boundaries

Commit f38044e5 ("vfio/iova_bitmap: Fix PAGE_SIZE unaligned bitmaps")
had fixed the unaligned bitmaps by capping the remaining iterable set at
the start of the bitmap. Although, that mistakenly worked around
iova_bitmap_set() incorrectly setting bits across page boundary.

Fix this by reworking the loop inside iova_bitmap_set() to iterate over a
range of bits to set (cur_bit .. last_bit) which may span different pinned
pages, thus updating @page_idx and @offset as it sets the bits. The
previous cap to the first page is now adjusted to be always accounted
rather than when there's only a non-zero pgoff.

While at it, make @page_idx , @offset and @nbits to be unsigned int given
that it won't be more than 512 and 4096 respectively (even a bigger
PAGE_SIZE or a smaller struct page size won't make this bigger than the
above 32-bit max). Also, delete the stale kdoc on Return type.

Cc: Avihai Horon <avihaih@nvidia.com>
Fixes: f38044e5 ("vfio/iova_bitmap: Fix PAGE_SIZE unaligned bitmaps")
Co-developed-by: default avatarJason Gunthorpe <jgg@nvidia.com>
Signed-off-by: default avatarJason Gunthorpe <jgg@nvidia.com>
Signed-off-by: default avatarJoao Martins <joao.m.martins@oracle.com>
Reviewed-by: default avatarJason Gunthorpe <jgg@nvidia.com>
Tested-by: default avatarAvihai Horon <avihaih@nvidia.com>
Link: https://lore.kernel.org/r/20221129131235.38880-1-joao.m.martins@oracle.comSigned-off-by: default avatarAlex Williamson <alex.williamson@redhat.com>
parent 2f5d8cef
...@@ -298,9 +298,7 @@ static unsigned long iova_bitmap_mapped_remaining(struct iova_bitmap *bitmap) ...@@ -298,9 +298,7 @@ static unsigned long iova_bitmap_mapped_remaining(struct iova_bitmap *bitmap)
{ {
unsigned long remaining, bytes; unsigned long remaining, bytes;
/* Cap to one page in the first iteration, if PAGE_SIZE unaligned. */ bytes = (bitmap->mapped.npages << PAGE_SHIFT) - bitmap->mapped.pgoff;
bytes = !bitmap->mapped.pgoff ? bitmap->mapped.npages << PAGE_SHIFT :
PAGE_SIZE - bitmap->mapped.pgoff;
remaining = bitmap->mapped_total_index - bitmap->mapped_base_index; remaining = bitmap->mapped_total_index - bitmap->mapped_base_index;
remaining = min_t(unsigned long, remaining, remaining = min_t(unsigned long, remaining,
...@@ -399,29 +397,27 @@ int iova_bitmap_for_each(struct iova_bitmap *bitmap, void *opaque, ...@@ -399,29 +397,27 @@ int iova_bitmap_for_each(struct iova_bitmap *bitmap, void *opaque,
* Set the bits corresponding to the range [iova .. iova+length-1] in * Set the bits corresponding to the range [iova .. iova+length-1] in
* the user bitmap. * the user bitmap.
* *
* Return: The number of bits set.
*/ */
void iova_bitmap_set(struct iova_bitmap *bitmap, void iova_bitmap_set(struct iova_bitmap *bitmap,
unsigned long iova, size_t length) unsigned long iova, size_t length)
{ {
struct iova_bitmap_map *mapped = &bitmap->mapped; struct iova_bitmap_map *mapped = &bitmap->mapped;
unsigned long offset = (iova - mapped->iova) >> mapped->pgshift; unsigned long cur_bit = ((iova - mapped->iova) >>
unsigned long nbits = max_t(unsigned long, 1, length >> mapped->pgshift); mapped->pgshift) + mapped->pgoff * BITS_PER_BYTE;
unsigned long page_idx = offset / BITS_PER_PAGE; unsigned long last_bit = (((iova + length - 1) - mapped->iova) >>
unsigned long page_offset = mapped->pgoff; mapped->pgshift) + mapped->pgoff * BITS_PER_BYTE;
void *kaddr;
offset = offset % BITS_PER_PAGE;
do { do {
unsigned long size = min(BITS_PER_PAGE - offset, nbits); unsigned int page_idx = cur_bit / BITS_PER_PAGE;
unsigned int offset = cur_bit % BITS_PER_PAGE;
unsigned int nbits = min(BITS_PER_PAGE - offset,
last_bit - cur_bit + 1);
void *kaddr;
kaddr = kmap_local_page(mapped->pages[page_idx]); kaddr = kmap_local_page(mapped->pages[page_idx]);
bitmap_set(kaddr + page_offset, offset, size); bitmap_set(kaddr, offset, nbits);
kunmap_local(kaddr); kunmap_local(kaddr);
page_offset = offset = 0; cur_bit += nbits;
nbits -= size; } while (cur_bit <= last_bit);
page_idx++;
} while (nbits > 0);
} }
EXPORT_SYMBOL_GPL(iova_bitmap_set); EXPORT_SYMBOL_GPL(iova_bitmap_set);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment