Thread (31 messages) 31 messages, 4 authors, 2021-09-30

RE: [PATCH V5 12/12] net: netvsc: Add Isolation VM support for netvsc driver

From: Haiyang Zhang <haiyangz@microsoft.com>
Date: 2021-09-15 16:47:08
Also in: linux-arch, linux-hyperv, linux-iommu, linux-scsi, lkml, xen-devel

-----Original Message-----
From: Michael Kelley <redacted>
Sent: Wednesday, September 15, 2021 12:22 PM
To: Tianyu Lan <redacted>; KY Srinivasan <kys@microsoft.com>;
quoted
+				memset(vmap_pages, 0,
+				       sizeof(*vmap_pages) * vmap_page_index);
+				vmap_page_index = 0;
+
+				for (j = 0; j < i; j++)
+					__free_pages(pages[j], alloc_unit);
+
+				kfree(pages);
+				alloc_unit = 1;
This is the case where a large enough contiguous physical memory chunk
could not be found.  But rather than dropping all the way down to single
pages, would it make sense to try something smaller, but not 1?  For
example, cut the alloc_unit in half and try again.  But I'm not sure of
all the implications.
I had the same question. But probably gradually decrementing uses too much
time?
quoted
+				goto retry;
+			}
+		}
+
+		pages[i] = page;
+		for (j = 0; j < alloc_unit; j++)
+			vmap_pages[vmap_page_index++] = page++;
+	}
+
+	vaddr = vmap(vmap_pages, vmap_page_index, VM_MAP, PAGE_KERNEL);
+	kfree(vmap_pages);
+
+	*pages_array = pages;
+	return vaddr;
+
+cleanup:
+	for (j = 0; j < i; j++)
+		__free_pages(pages[i], alloc_unit);
+
+	kfree(pages);
+	kfree(vmap_pages);
+	return NULL;
+}
+
+static void *netvsc_map_pages(struct page **pages, int count, int
+alloc_unit) {
+	int pg_count = count * alloc_unit;
+	struct page *page;
+	unsigned long *pfns;
+	int pfn_index = 0;
+	void *vaddr;
+	int i, j;
+
+	if (!pages)
+		return NULL;
+
+	pfns = kcalloc(pg_count, sizeof(*pfns), GFP_KERNEL);
+	if (!pfns)
+		return NULL;
+
+	for (i = 0; i < count; i++) {
+		page = pages[i];
+		if (!page) {
+			pr_warn("page is not available %d.\n", i);
+			return NULL;
+		}
+
+		for (j = 0; j < alloc_unit; j++) {
+			pfns[pfn_index++] = page_to_pfn(page++) +
+				(ms_hyperv.shared_gpa_boundary >> PAGE_SHIFT);
+		}
+	}
+
+	vaddr = vmap_pfn(pfns, pg_count, PAGE_KERNEL_IO);
+	kfree(pfns);
+	return vaddr;
+}
+
I think you are proposing this approach to allocating memory for the
send and receive buffers so that you can avoid having two virtual
mappings for the memory, per comments from Christop Hellwig.  But
overall, the approach seems a bit complex and I wonder if it is worth it.
If allocating large contiguous chunks of physical memory is successful,
then there is some memory savings in that the data structures needed to
keep track of the physical pages is smaller than the equivalent page
tables might be.  But if you have to revert to allocating individual
pages, then the memory savings is reduced.

Ultimately, the list of actual PFNs has to be kept somewhere.  Another
approach would be to do the reverse of what hv_map_memory() from the v4
patch series does.  I.e., you could do virt_to_phys() on each virtual
address that maps above VTOM, and subtract out the shared_gpa_boundary
to get the
list of actual PFNs that need to be freed.   This way you don't have two
copies
of the list of PFNs -- one with and one without the shared_gpa_boundary
added.
But it comes at the cost of additional code so that may not be a great
idea.

I think what you have here works, and I don't have a clearly better
solution at the moment except perhaps to revert to the v4 solution and
just have two virtual mappings.  I'll keep thinking about it.  Maybe
Christop has other thoughts.
quoted
 static int netvsc_init_buf(struct hv_device *device,
 			   struct netvsc_device *net_device,
 			   const struct netvsc_device_info *device_info) @@ -
337,7 +462,7
quoted
@@ static int netvsc_init_buf(struct hv_device *device,
 	struct nvsp_1_message_send_receive_buffer_complete *resp;
 	struct net_device *ndev = hv_get_drvdata(device);
 	struct nvsp_message *init_packet;
-	unsigned int buf_size;
+	unsigned int buf_size, alloc_unit;
 	size_t map_words;
 	int i, ret = 0;
@@ -350,7 +475,14 @@ static int netvsc_init_buf(struct hv_device
*device,
quoted
 		buf_size = min_t(unsigned int, buf_size,
 				 NETVSC_RECEIVE_BUFFER_SIZE_LEGACY);

-	net_device->recv_buf = vzalloc(buf_size);
+	if (hv_isolation_type_snp())
+		net_device->recv_buf =
+			netvsc_alloc_pages(&net_device->recv_pages,
+					   &net_device->recv_page_count,
+					   buf_size);
+	else
+		net_device->recv_buf = vzalloc(buf_size);
+
I wonder if it is necessary to have two different code paths here.  The
allocating and freeing of the send and receive buffers is not perf
sensitive, and it seems like netvsc_alloc_pages() could be used
regardless of whether SNP Isolation is in effect.  To my thinking, one
code path is better than two code paths unless there's a compelling
reason to have two.
I still prefer keeping the simple vzalloc for the non isolated VMs, because
simple code path usually means more robust. 
I don't know how much time difference between the two, but in some cases 
we really care about boot time? 
Also in the multi vPort case for MANA, we potentially support hundreds of 
vPorts, and there will be the same number of synthetic NICs associated with 
them. So even small time difference in the initialization time may add up.

Thanks,
- Haiyang
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help