diff mbox series

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

Message ID 20221129131235.38880-1-joao.m.martins@oracle.com (mailing list archive)
State New, archived
Headers show
Series [v2] vfio/iova_bitmap: refactor iova_bitmap_set() to better handle page boundaries | expand

Commit Message

Joao Martins Nov. 29, 2022, 1:12 p.m. UTC
Commit f38044e5ef58 ("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: f38044e5ef58 ("vfio/iova_bitmap: Fix PAGE_SIZE unaligned bitmaps")
Co-developed-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Joao Martins <joao.m.martins@oracle.com>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
---
Changes since v1:
 * Add Reviewed-by by Jason Gunthorpe
 * Add Fixes tag (Alex Williamson)

It passes my tests but to be extra sure: Avihai could you take this
patch a spin in your rig/tests as well? Thanks!
---
 drivers/vfio/iova_bitmap.c | 30 +++++++++++++-----------------
 1 file changed, 13 insertions(+), 17 deletions(-)

Comments

Avihai Horon Dec. 1, 2022, 10:49 a.m. UTC | #1
Hi,

On 29/11/2022 15:12, Joao Martins wrote:
> External email: Use caution opening links or attachments
>
>
> Commit f38044e5ef58 ("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: f38044e5ef58 ("vfio/iova_bitmap: Fix PAGE_SIZE unaligned bitmaps")
> Co-developed-by: Jason Gunthorpe <jgg@nvidia.com>
> Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
> Signed-off-by: Joao Martins <joao.m.martins@oracle.com>
> Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
> ---
> Changes since v1:
>   * Add Reviewed-by by Jason Gunthorpe
>   * Add Fixes tag (Alex Williamson)
>
> It passes my tests but to be extra sure: Avihai could you take this
> patch a spin in your rig/tests as well? Thanks!

Looks good on my side:

Tested-by: Avihai Horon <avihaih@nvidia.com>

> ---
>   drivers/vfio/iova_bitmap.c | 30 +++++++++++++-----------------
>   1 file changed, 13 insertions(+), 17 deletions(-)
>
> diff --git a/drivers/vfio/iova_bitmap.c b/drivers/vfio/iova_bitmap.c
> index de6d6ea5c496..0848f920efb7 100644
> --- a/drivers/vfio/iova_bitmap.c
> +++ b/drivers/vfio/iova_bitmap.c
> @@ -298,9 +298,7 @@ static unsigned long iova_bitmap_mapped_remaining(struct iova_bitmap *bitmap)
>   {
>          unsigned long remaining, bytes;
>
> -       /* Cap to one page in the first iteration, if PAGE_SIZE unaligned. */
> -       bytes = !bitmap->mapped.pgoff ? bitmap->mapped.npages << PAGE_SHIFT :
> -                                       PAGE_SIZE - bitmap->mapped.pgoff;
> +       bytes = (bitmap->mapped.npages << PAGE_SHIFT) - bitmap->mapped.pgoff;
>
>          remaining = bitmap->mapped_total_index - bitmap->mapped_base_index;
>          remaining = min_t(unsigned long, remaining,
> @@ -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
>    * the user bitmap.
>    *
> - * Return: The number of bits set.
>    */
>   void iova_bitmap_set(struct iova_bitmap *bitmap,
>                       unsigned long iova, size_t length)
>   {
>          struct iova_bitmap_map *mapped = &bitmap->mapped;
> -       unsigned long offset = (iova - mapped->iova) >> mapped->pgshift;
> -       unsigned long nbits = max_t(unsigned long, 1, length >> mapped->pgshift);
> -       unsigned long page_idx = offset / BITS_PER_PAGE;
> -       unsigned long page_offset = mapped->pgoff;
> -       void *kaddr;
> -
> -       offset = offset % BITS_PER_PAGE;
> +       unsigned long cur_bit = ((iova - mapped->iova) >>
> +                       mapped->pgshift) + mapped->pgoff * BITS_PER_BYTE;
> +       unsigned long last_bit = (((iova + length - 1) - mapped->iova) >>
> +                       mapped->pgshift) + mapped->pgoff * BITS_PER_BYTE;
>
>          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]);
> -               bitmap_set(kaddr + page_offset, offset, size);
> +               bitmap_set(kaddr, offset, nbits);
>                  kunmap_local(kaddr);
> -               page_offset = offset = 0;
> -               nbits -= size;
> -               page_idx++;
> -       } while (nbits > 0);
> +               cur_bit += nbits;
> +       } while (cur_bit <= last_bit);
>   }
>   EXPORT_SYMBOL_GPL(iova_bitmap_set);
> --
> 2.17.2
>
Alex Williamson Dec. 2, 2022, 11:31 p.m. UTC | #2
On Tue, 29 Nov 2022 13:12:35 +0000
Joao Martins <joao.m.martins@oracle.com> wrote:

> Commit f38044e5ef58 ("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: f38044e5ef58 ("vfio/iova_bitmap: Fix PAGE_SIZE unaligned bitmaps")
> Co-developed-by: Jason Gunthorpe <jgg@nvidia.com>
> Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
> Signed-off-by: Joao Martins <joao.m.martins@oracle.com>
> Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
> ---
> Changes since v1:
>  * Add Reviewed-by by Jason Gunthorpe
>  * Add Fixes tag (Alex Williamson)
> 
> It passes my tests but to be extra sure: Avihai could you take this
> patch a spin in your rig/tests as well? Thanks!
> ---
>  drivers/vfio/iova_bitmap.c | 30 +++++++++++++-----------------
>  1 file changed, 13 insertions(+), 17 deletions(-)

Applied to vfio next branch for v6.2 with Avihai's tested-by.  Thanks,

Alex
diff mbox series

Patch

diff --git a/drivers/vfio/iova_bitmap.c b/drivers/vfio/iova_bitmap.c
index de6d6ea5c496..0848f920efb7 100644
--- a/drivers/vfio/iova_bitmap.c
+++ b/drivers/vfio/iova_bitmap.c
@@ -298,9 +298,7 @@  static unsigned long iova_bitmap_mapped_remaining(struct iova_bitmap *bitmap)
 {
 	unsigned long remaining, bytes;
 
-	/* Cap to one page in the first iteration, if PAGE_SIZE unaligned. */
-	bytes = !bitmap->mapped.pgoff ? bitmap->mapped.npages << PAGE_SHIFT :
-					PAGE_SIZE - bitmap->mapped.pgoff;
+	bytes = (bitmap->mapped.npages << PAGE_SHIFT) - bitmap->mapped.pgoff;
 
 	remaining = bitmap->mapped_total_index - bitmap->mapped_base_index;
 	remaining = min_t(unsigned long, remaining,
@@ -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
  * the user bitmap.
  *
- * Return: The number of bits set.
  */
 void iova_bitmap_set(struct iova_bitmap *bitmap,
 		     unsigned long iova, size_t length)
 {
 	struct iova_bitmap_map *mapped = &bitmap->mapped;
-	unsigned long offset = (iova - mapped->iova) >> mapped->pgshift;
-	unsigned long nbits = max_t(unsigned long, 1, length >> mapped->pgshift);
-	unsigned long page_idx = offset / BITS_PER_PAGE;
-	unsigned long page_offset = mapped->pgoff;
-	void *kaddr;
-
-	offset = offset % BITS_PER_PAGE;
+	unsigned long cur_bit = ((iova - mapped->iova) >>
+			mapped->pgshift) + mapped->pgoff * BITS_PER_BYTE;
+	unsigned long last_bit = (((iova + length - 1) - mapped->iova) >>
+			mapped->pgshift) + mapped->pgoff * BITS_PER_BYTE;
 
 	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]);
-		bitmap_set(kaddr + page_offset, offset, size);
+		bitmap_set(kaddr, offset, nbits);
 		kunmap_local(kaddr);
-		page_offset = offset = 0;
-		nbits -= size;
-		page_idx++;
-	} while (nbits > 0);
+		cur_bit += nbits;
+	} while (cur_bit <= last_bit);
 }
 EXPORT_SYMBOL_GPL(iova_bitmap_set);