Re: [PATCH v3 8/15] iommufd: Algorithms for PFN storage

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Oct 25, 2022 at 03:12:17PM -0300, Jason Gunthorpe wrote:
> +/**
> + * iopt_pages_unfill_xarray() - Update the xarry after removing an access
> + * @pages: The pages to act on
> + * @start_index: Starting PFN index
> + * @last_index: Last PFN index
> + *
> + * Called when an iopt_pages_access is removed, removes pages from the itree.
> + * The access should already be removed from the access_itree.
> + */
> +void iopt_pages_unfill_xarray(struct iopt_pages *pages,
> +			      unsigned long start_index,
> +			      unsigned long last_index)
> +{
> +	struct interval_tree_double_span_iter span;
> +	u64 backup[BATCH_BACKUP_SIZE];
> +	struct pfn_batch batch;
> +	bool batch_inited = false;
> +
> +	lockdep_assert_held(&pages->mutex);
> +
> +	interval_tree_for_each_double_span(&span, &pages->access_itree,
> +					   &pages->domains_itree, start_index,
> +					   last_index) {
> +		if (!span.is_used) {
> +			if (!batch_inited) {
> +				batch_init_backup(&batch,
> +						  last_index - start_index + 1,
> +						  backup, sizeof(backup));
> +				batch_inited = true;
> +			}
> +			batch_from_xarray_clear(&batch, &pages->pinned_pfns,
> +						span.start_hole,
> +						span.last_hole);
> +			batch_unpin(&batch, pages, 0, batch.total_pfns);
> +			batch_clear(&batch);

Syzkaller, and then the nth test suite found this - the required loop
around the 'batch_from' is missing. The normal test suite should have
found this, but it turns out it is using huge pages, and not enough of
them to overfill even the limited batch.

Testing also showed that batch overflow accidently zerod an xarray
entry that was not actually stored. Thus this:

diff --git a/drivers/iommu/iommufd/pages.c b/drivers/iommu/iommufd/pages.c
index da95b28b41772c..3640f83f57069b 100644
--- a/drivers/iommu/iommufd/pages.c
+++ b/drivers/iommu/iommufd/pages.c
@@ -479,10 +479,11 @@ static void batch_from_xarray_clear(struct pfn_batch *batch, struct xarray *xa,
 		entry = xas_next(&xas);
 		if (xas_retry(&xas, entry))
 			continue;
-		xas_store(&xas, NULL);
 		WARN_ON(!xa_is_value(entry));
-		if (!batch_add_pfn(batch, xa_to_value(entry)) ||
-		    start_index == last_index)
+		if (!batch_add_pfn(batch, xa_to_value(entry)))
+			break;
+		xas_store(&xas, NULL);
+		if (start_index == last_index)
 			break;
 		start_index++;
 	}
@@ -1407,6 +1408,20 @@ void iopt_area_unfill_domains(struct iopt_area *area, struct iopt_pages *pages)
 	mutex_unlock(&pages->mutex);
 }
 
+static void iopt_pages_unpin_xarray(struct pfn_batch *batch,
+				    struct iopt_pages *pages,
+				    unsigned long start_index,
+				    unsigned long end_index)
+{
+	while (start_index <= end_index) {
+		batch_from_xarray_clear(batch, &pages->pinned_pfns, start_index,
+					end_index);
+		batch_unpin(batch, pages, 0, batch->total_pfns);
+		start_index += batch->total_pfns;
+		batch_clear(batch);
+	}
+}
+
 /**
  * iopt_pages_unfill_xarray() - Update the xarry after removing an access
  * @pages: The pages to act on
@@ -1437,11 +1452,8 @@ void iopt_pages_unfill_xarray(struct iopt_pages *pages,
 						  backup, sizeof(backup));
 				batch_inited = true;
 			}
-			batch_from_xarray_clear(&batch, &pages->pinned_pfns,
-						span.start_hole,
+			iopt_pages_unpin_xarray(&batch, pages, span.start_hole,
 						span.last_hole);
-			batch_unpin(&batch, pages, 0, batch.total_pfns);
-			batch_clear(&batch);
 		} else if (span.is_used == 2) {
 			/* Covered by a domain */
 			clear_xarray(&pages->pinned_pfns, span.start_used,



[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux FS]     [Yosemite Forum]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux