mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Emerson Busson <emersonbusson@gmail.com>
To: mhklinux@outlook.com
Cc: kys@microsoft.com, haiyangz@microsoft.com, wei.liu@kernel.org,
	decui@microsoft.com, andrew+netdev@lunn.ch, davem@davemloft.net,
	edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
	gregkh@linuxfoundation.org, linux-kernel@vger.kernel.org,
	linux-hyperv@vger.kernel.org, netdev@vger.kernel.org
Subject: [PATCH v2 01/14] hv: vmbus: convert ring backing through the chunk allocator
Date: Wed,  7 Oct 2026 16:07:39 -0300	[thread overview]
Message-ID: <20261007190752.336426-2-emersonbusson@gmail.com> (raw)
In-Reply-To: <20261007190752.336426-1-emersonbusson@gmail.com>

Use the existing virtually contiguous allocator for ring backing with
the ring confidentiality policy. An internal helper keeps that choice
distinct from the established external-buffer compatibility policy.
Preserve the exported allocator and caller-decrypted GPADL signatures
throughout the series; carry uncertain creation in the descriptor
instead of widening an exported function. The independent
guest-memory-encryption condition is present at the first ring consumer.

Signed-off-by: Emerson Busson <emersonbusson@gmail.com>
---
 drivers/hv/channel.c            | 176 +++++++++++++++++++++-----------
 drivers/hv/hyperv_vmbus.h       |   4 +-
 drivers/hv/ring_buffer.c        |   9 +-
 drivers/net/hyperv/hyperv_net.h |  10 +-
 drivers/net/hyperv/netvsc.c     |  58 ++++++-----
 drivers/uio/uio_hv_generic.c    |  37 ++++---
 include/linux/hyperv.h          |  18 +++-
 7 files changed, 196 insertions(+), 116 deletions(-)

diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c
index 7e4cc6f55237..f8feb2a0ec0a 100644
--- a/drivers/hv/channel.c
+++ b/drivers/hv/channel.c
@@ -12,6 +12,8 @@
 #include <linux/sched.h>
 #include <linux/wait.h>
 #include <linux/mm.h>
+#include <linux/cc_platform.h>
+#include <linux/overflow.h>
 #include <linux/slab.h>
 #include <linux/log2.h>
 #include <linux/module.h>
@@ -26,6 +28,12 @@
 
 #include "hyperv_vmbus.h"
 
+static void *__vmbus_alloc_buffer(struct vmbus_channel *channel,
+				  u32 size,
+				  bool confidential,
+				  struct page ***chunks_out,
+				  u32 *chunk_cnt_out);
+
 /*
  * hv_gpadl_size - Return the real size of a gpadl, the size that Hyper-V uses
  *
@@ -42,7 +50,6 @@ static inline u32 hv_gpadl_size(enum hv_gpadl_type type, u32 size)
 {
 	switch (type) {
 	case HV_GPADL_BUFFER:
-	case HV_GPADL_BUFFER_DECRYPTED:
 		return size;
 	case HV_GPADL_RING:
 		/* The size of a ringbuffer must be page-aligned */
@@ -103,7 +110,6 @@ static inline u64 hv_gpadl_hvpfn(enum hv_gpadl_type type, void *kbuffer,
 
 	switch (type) {
 	case HV_GPADL_BUFFER:
-	case HV_GPADL_BUFFER_DECRYPTED:
 		break;
 	case HV_GPADL_RING:
 		if (i == 0)
@@ -154,17 +160,15 @@ EXPORT_SYMBOL_GPL(vmbus_setevent);
 /* vmbus_free_ring - drop mapping of ring buffer */
 void vmbus_free_ring(struct vmbus_channel *channel)
 {
+	struct vmbus_buffer *buffer = &channel->ringbuffer;
+
 	hv_ringbuffer_cleanup(&channel->outbound);
 	hv_ringbuffer_cleanup(&channel->inbound);
 
-	if (channel->ringbuffer_page) {
-		/* In a CoCo VM leak the memory if it didn't get re-encrypted */
-		if (!channel->ringbuffer_gpadlhandle.decrypted)
-			__free_pages(channel->ringbuffer_page,
-			     get_order(channel->ringbuffer_pagecount
-				       << PAGE_SHIFT));
-		channel->ringbuffer_page = NULL;
-	}
+	if (!buffer->addr)
+		return;
+
+	vmbus_release_buffer(buffer);
 }
 EXPORT_SYMBOL_GPL(vmbus_free_ring);
 
@@ -172,26 +176,32 @@ EXPORT_SYMBOL_GPL(vmbus_free_ring);
 int vmbus_alloc_ring(struct vmbus_channel *newchannel,
 		     u32 send_size, u32 recv_size)
 {
-	struct page *page;
-	int order;
+	struct vmbus_buffer *buffer = &newchannel->ringbuffer;
+	u32 size;
+	u32 i;
 
-	if (send_size % PAGE_SIZE || recv_size % PAGE_SIZE)
+	if (!send_size || !recv_size ||
+	    send_size % PAGE_SIZE || recv_size % PAGE_SIZE ||
+	    check_add_overflow(send_size, recv_size, &size))
 		return -EINVAL;
 
-	/* Allocate the ring buffer */
-	order = get_order(send_size + recv_size);
-	page = alloc_pages_node(cpu_to_node(newchannel->target_cpu),
-				GFP_KERNEL|__GFP_ZERO, order);
-
-	if (!page)
-		page = alloc_pages(GFP_KERNEL|__GFP_ZERO, order);
-
-	if (!page)
+	buffer->addr = __vmbus_alloc_buffer(newchannel, size,
+					    newchannel->co_ring_buffer,
+					    &buffer->chunks, &buffer->chunk_cnt);
+	if (!buffer->addr)
 		return -ENOMEM;
 
-	newchannel->ringbuffer_page = page;
-	newchannel->ringbuffer_pagecount = (send_size + recv_size) >> PAGE_SHIFT;
+	newchannel->ringbuffer_pagecount = size >> PAGE_SHIFT;
 	newchannel->ringbuffer_send_offset = send_size >> PAGE_SHIFT;
+	buffer->pages = kvcalloc(newchannel->ringbuffer_pagecount,
+				 sizeof(*buffer->pages), GFP_KERNEL);
+	if (!buffer->pages) {
+		vmbus_release_buffer(buffer);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < newchannel->ringbuffer_pagecount; i++)
+		buffer->pages[i] = vmalloc_to_page(buffer->addr + (i << PAGE_SHIFT));
 
 	return 0;
 }
@@ -442,7 +452,8 @@ static void vmbus_free_channel_msginfo(struct vmbus_channel_msginfo *msginfo)
  */
 static int __vmbus_establish_gpadl(struct vmbus_channel *channel,
 				   enum hv_gpadl_type type, void *kbuffer,
-				   u32 size, u32 send_offset,
+				   u32 size, u32 send_offset, bool memory_prepared,
+				   bool *leak,
 				   struct vmbus_gpadl *gpadl)
 {
 	struct vmbus_channel_gpadl_header *gpadlmsg;
@@ -452,8 +463,13 @@ static int __vmbus_establish_gpadl(struct vmbus_channel *channel,
 	struct list_head *curr;
 	u32 next_gpadl_handle;
 	unsigned long flags;
+	bool posted = false;
 	int ret = 0;
 
+	if (leak)
+		*leak = false;
+	gpadl->leak = false;
+
 	next_gpadl_handle =
 		(atomic_inc_return(&vmbus_connection.next_gpadl_handle) - 1);
 
@@ -463,9 +479,9 @@ static int __vmbus_establish_gpadl(struct vmbus_channel *channel,
 		return ret;
 	}
 
-	gpadl->decrypted = !((channel->co_external_memory && type == HV_GPADL_BUFFER) ||
-		(channel->co_ring_buffer && type == HV_GPADL_RING) ||
-		(type == HV_GPADL_BUFFER_DECRYPTED));
+	gpadl->decrypted = !memory_prepared &&
+		!((channel->co_external_memory && type == HV_GPADL_BUFFER) ||
+		  (channel->co_ring_buffer && type == HV_GPADL_RING));
 	if (gpadl->decrypted) {
 		/*
 		 * The "decrypted" flag being true assumes that set_memory_decrypted() succeeds.
@@ -504,6 +520,8 @@ static int __vmbus_establish_gpadl(struct vmbus_channel *channel,
 		goto cleanup;
 	}
 
+	/* A failed post may still have reached the host. */
+	posted = true;
 	ret = vmbus_post_msg(gpadlmsg, msginfo->msgsize -
 			     sizeof(*msginfo), true);
 
@@ -534,6 +552,7 @@ static int __vmbus_establish_gpadl(struct vmbus_channel *channel,
 	wait_for_completion(&msginfo->waitevent);
 
 	if (msginfo->response.gpadl_created.creation_status != 0) {
+		posted = false;
 		pr_err("Failed to establish GPADL: err = 0x%x\n",
 		       msginfo->response.gpadl_created.creation_status);
 
@@ -542,6 +561,7 @@ static int __vmbus_establish_gpadl(struct vmbus_channel *channel,
 	}
 
 	if (channel->rescind) {
+		posted = false;
 		ret = -ENODEV;
 		goto cleanup;
 	}
@@ -550,6 +570,7 @@ static int __vmbus_establish_gpadl(struct vmbus_channel *channel,
 	gpadl->gpadl_handle = gpadlmsg->gpadl;
 	gpadl->buffer = kbuffer;
 	gpadl->size = size;
+	posted = false;
 
 
 cleanup:
@@ -559,7 +580,13 @@ static int __vmbus_establish_gpadl(struct vmbus_channel *channel,
 
 	vmbus_free_channel_msginfo(msginfo);
 
-	if (ret) {
+	if (ret && posted) {
+		gpadl->leak = true;
+		if (leak)
+			*leak = true;
+	}
+
+	if (ret && !posted) {
 		/*
 		 * If set_memory_encrypted() fails, the decrypted flag is
 		 * left as true so the memory is leaked instead of being
@@ -586,7 +613,7 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer,
 			  u32 size, struct vmbus_gpadl *gpadl)
 {
 	return __vmbus_establish_gpadl(channel, HV_GPADL_BUFFER, kbuffer, size,
-				       0U, gpadl);
+				       0U, false, &gpadl->leak, gpadl);
 }
 EXPORT_SYMBOL_GPL(vmbus_establish_gpadl);
 
@@ -597,6 +624,8 @@ EXPORT_SYMBOL_GPL(vmbus_establish_gpadl);
  * @channel: a channel
  * @kbuffer: from kmalloc or vmalloc; must already be decrypted by the caller
  * @size: page-size multiple
+ * @leak: set when a GPADL message may have reached the host but completion is
+ *        uncertain; the caller must retain the backing pages
  * @gpadl: output gpadl
  *
  * The caller is responsible for re-encrypting the buffer before freeing it.
@@ -605,8 +634,8 @@ int vmbus_establish_gpadl_caller_decrypted(struct vmbus_channel *channel,
 					   void *kbuffer, u32 size,
 					   struct vmbus_gpadl *gpadl)
 {
-	return __vmbus_establish_gpadl(channel, HV_GPADL_BUFFER_DECRYPTED,
-				       kbuffer, size, 0U, gpadl);
+	return __vmbus_establish_gpadl(channel, HV_GPADL_BUFFER,
+				       kbuffer, size, 0U, true, &gpadl->leak, gpadl);
 }
 EXPORT_SYMBOL_GPL(vmbus_establish_gpadl_caller_decrypted);
 
@@ -648,11 +677,26 @@ void vmbus_free_buffer(void *addr, struct page **chunks, u32 chunk_cnt)
 }
 EXPORT_SYMBOL_GPL(vmbus_free_buffer);
 
+void vmbus_release_buffer(struct vmbus_buffer *buffer)
+{
+	if (!buffer->addr)
+		return;
+
+	kvfree(buffer->pages);
+	if (!buffer->leak && !buffer->gpadl.leak &&
+	    !buffer->gpadl.gpadl_handle)
+		vmbus_free_buffer(buffer->addr, buffer->chunks,
+				  buffer->chunk_cnt);
+	memset(buffer, 0, sizeof(*buffer));
+}
+EXPORT_SYMBOL_GPL(vmbus_release_buffer);
+
 /**
- * vmbus_alloc_buffer - allocate a host-visible, virtually-contiguous buffer.
+ * __vmbus_alloc_buffer - allocate host-visible, virtually-contiguous backing.
  *
  * @channel: the channel the buffer will be attached to
  * @size: requested buffer size in bytes (will be rounded up to PAGE_SIZE)
+ * @confidential: keep the buffer private to the guest
  * @chunks_out: on success, set to the array of underlying chunks, or NULL when
  *              the buffer was allocated with vzalloc()
  * @chunk_cnt_out: on success, set to the number of chunks
@@ -667,10 +711,11 @@ EXPORT_SYMBOL_GPL(vmbus_free_buffer);
  *
  * Return: the buffer's virtual address, or NULL on failure.
  */
-void *vmbus_alloc_buffer(struct vmbus_channel *channel,
-			 u32 size,
-			 struct page ***chunks_out,
-			 u32 *chunk_cnt_out)
+static void *__vmbus_alloc_buffer(struct vmbus_channel *channel,
+				  u32 size,
+				  bool confidential,
+				  struct page ***chunks_out,
+				  u32 *chunk_cnt_out)
 {
 	unsigned long nr_pages = PFN_UP(size);
 	unsigned long remaining = nr_pages;
@@ -690,7 +735,8 @@ void *vmbus_alloc_buffer(struct vmbus_channel *channel,
 		return NULL;
 
 	/* If the buffer does not need to be decrypted, just use vzalloc() */
-	if (!hv_is_isolation_supported() || channel->co_external_memory)
+	if ((!hv_is_isolation_supported() &&
+	     !cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) || confidential)
 		return vzalloc(nr_pages << PAGE_SHIFT);
 
 	/* Worst case: every chunk is a single page. */
@@ -760,6 +806,14 @@ void *vmbus_alloc_buffer(struct vmbus_channel *channel,
 	vmbus_free_buffer(NULL, chunks, chunk_cnt);
 	return NULL;
 }
+
+void *vmbus_alloc_buffer(struct vmbus_channel *channel,
+			 u32 size, struct page ***chunks_out,
+			 u32 *chunk_cnt_out)
+{
+	return __vmbus_alloc_buffer(channel, size, channel->co_external_memory,
+				    chunks_out, chunk_cnt_out);
+}
 EXPORT_SYMBOL_GPL(vmbus_alloc_buffer);
 
 /**
@@ -832,7 +886,7 @@ static int __vmbus_open(struct vmbus_channel *newchannel,
 {
 	struct vmbus_channel_open_channel *open_msg;
 	struct vmbus_channel_msginfo *open_info = NULL;
-	struct page *page = newchannel->ringbuffer_page;
+	struct vmbus_buffer *buffer = &newchannel->ringbuffer;
 	u32 send_pages, recv_pages;
 	unsigned long flags;
 	int err;
@@ -860,22 +914,24 @@ static int __vmbus_open(struct vmbus_channel *newchannel,
 		newchannel->max_pkt_size = VMBUS_DEFAULT_MAX_PKT_SIZE;
 
 	/* Establish the gpadl for the ring buffer */
-	newchannel->ringbuffer_gpadlhandle.gpadl_handle = 0;
+	buffer->gpadl.gpadl_handle = 0;
 
 	err = __vmbus_establish_gpadl(newchannel, HV_GPADL_RING,
-				      page_address(newchannel->ringbuffer_page),
+				      buffer->addr,
 				      (send_pages + recv_pages) << PAGE_SHIFT,
 				      newchannel->ringbuffer_send_offset << PAGE_SHIFT,
-				      &newchannel->ringbuffer_gpadlhandle);
+				      true, &buffer->leak, &buffer->gpadl);
 	if (err)
 		goto error_clean_ring;
 
 	err = hv_ringbuffer_init(&newchannel->outbound,
-				 page, send_pages, 0, newchannel->co_ring_buffer);
+				 buffer->addr, send_pages, 0,
+				 newchannel->co_ring_buffer);
 	if (err)
 		goto error_free_gpadl;
 
-	err = hv_ringbuffer_init(&newchannel->inbound, &page[send_pages],
+	err = hv_ringbuffer_init(&newchannel->inbound,
+				 buffer->addr + (send_pages << PAGE_SHIFT),
 				 recv_pages, newchannel->max_pkt_size,
 				 newchannel->co_ring_buffer);
 	if (err)
@@ -897,8 +953,7 @@ static int __vmbus_open(struct vmbus_channel *newchannel,
 	open_msg->header.msgtype = CHANNELMSG_OPENCHANNEL;
 	open_msg->openid = newchannel->offermsg.child_relid;
 	open_msg->child_relid = newchannel->offermsg.child_relid;
-	open_msg->ringbuffer_gpadlhandle
-		= newchannel->ringbuffer_gpadlhandle.gpadl_handle;
+	open_msg->ringbuffer_gpadlhandle = buffer->gpadl.gpadl_handle;
 	/*
 	 * The unit of ->downstream_ringbuffer_pageoffset is HV_HYP_PAGE and
 	 * the unit of ->ringbuffer_send_offset (i.e. send_pages) is PAGE, so
@@ -956,7 +1011,8 @@ static int __vmbus_open(struct vmbus_channel *newchannel,
 error_free_info:
 	kfree(open_info);
 error_free_gpadl:
-	vmbus_teardown_gpadl(newchannel, &newchannel->ringbuffer_gpadlhandle);
+	if (vmbus_teardown_gpadl(newchannel, &buffer->gpadl))
+		buffer->leak = true;
 error_clean_ring:
 	hv_ringbuffer_cleanup(&newchannel->outbound);
 	hv_ringbuffer_cleanup(&newchannel->inbound);
@@ -1058,15 +1114,20 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, struct vmbus_gpadl *gpad
 
 	kfree(info);
 
-	if (gpadl->decrypted)
-		ret = set_memory_encrypted((unsigned long)gpadl->buffer,
-					PFN_UP(gpadl->size));
-	else
-		ret = 0;
-	if (ret)
-		pr_warn("Fail to set mem host visibility in GPADL teardown %d.\n", ret);
+	if (!ret && gpadl->decrypted) {
+		int encrypt_ret;
 
-	gpadl->decrypted = ret;
+		encrypt_ret = set_memory_encrypted((unsigned long)gpadl->buffer,
+						   PFN_UP(gpadl->size));
+		if (encrypt_ret) {
+			pr_warn("Failed to re-encrypt GPADL buffer: %d\n",
+				encrypt_ret);
+			ret = encrypt_ret;
+		}
+		gpadl->decrypted = !!encrypt_ret;
+	}
+	if (ret)
+		gpadl->leak = true;
 
 	return ret;
 }
@@ -1142,9 +1203,10 @@ static int vmbus_close_internal(struct vmbus_channel *channel)
 	}
 
 	/* Tear down the gpadl for the channel's ring buffer */
-	else if (channel->ringbuffer_gpadlhandle.gpadl_handle) {
-		ret = vmbus_teardown_gpadl(channel, &channel->ringbuffer_gpadlhandle);
+	else if (channel->ringbuffer.gpadl.gpadl_handle) {
+		ret = vmbus_teardown_gpadl(channel, &channel->ringbuffer.gpadl);
 		if (ret) {
+			channel->ringbuffer.leak = true;
 			pr_err("Close failed: teardown gpadl return %d\n", ret);
 			/*
 			 * If we failed to teardown gpadl,
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
index 33923621a5a3..20d023c9735e 100644
--- a/drivers/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
@@ -204,8 +204,8 @@ extern int hv_synic_cleanup(unsigned int cpu);
 void hv_ringbuffer_pre_init(struct vmbus_channel *channel);
 
 int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info,
-		       struct page *pages, u32 pagecnt, u32 max_pkt_size,
-			   bool confidential);
+		       void *addr, u32 pagecnt, u32 max_pkt_size,
+		       bool confidential);
 
 void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info);
 
diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c
index 592a9601faaa..16b1c2910789 100644
--- a/drivers/hv/ring_buffer.c
+++ b/drivers/hv/ring_buffer.c
@@ -184,8 +184,8 @@ void hv_ringbuffer_pre_init(struct vmbus_channel *channel)
 
 /* Initialize the ring buffer. */
 int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info,
-		       struct page *pages, u32 page_cnt, u32 max_pkt_size,
-			   bool confidential)
+		       void *addr, u32 page_cnt, u32 max_pkt_size,
+		       bool confidential)
 {
 	struct page **pages_wraparound;
 	int i;
@@ -200,10 +200,11 @@ int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info,
 	if (!pages_wraparound)
 		return -ENOMEM;
 
-	pages_wraparound[0] = pages;
+	pages_wraparound[0] = vmalloc_to_page(addr);
 	for (i = 0; i < 2 * (page_cnt - 1); i++)
 		pages_wraparound[i + 1] =
-			&pages[i % (page_cnt - 1) + 1];
+			vmalloc_to_page(addr +
+				((i % (page_cnt - 1) + 1) << PAGE_SHIFT));
 
 	ring_info->ring_buffer = (struct hv_ring_buffer *)
 		vmap(pages_wraparound, page_cnt * 2 - 1, VM_MAP,
diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h
index 4841367fdab2..a15cb2460344 100644
--- a/drivers/net/hyperv/hyperv_net.h
+++ b/drivers/net/hyperv/hyperv_net.h
@@ -1158,21 +1158,15 @@ struct netvsc_device {
 	bool tx_disable; /* if true, do not wake up queue again */
 
 	/* Receive buffer allocated by us but manages by NetVSP */
-	void *recv_buf;
+	struct vmbus_buffer recv_buffer;
 	u32 recv_buf_size; /* allocated bytes */
-	struct page **recv_buf_chunks;
-	u32 recv_buf_chunk_cnt;
-	struct vmbus_gpadl recv_buf_gpadl_handle;
 	u32 recv_section_cnt;
 	u32 recv_section_size;
 	u32 recv_completion_cnt;
 
 	/* Send buffer allocated by us */
-	void *send_buf;
+	struct vmbus_buffer send_buffer;
 	u32 send_buf_size;
-	struct page **send_buf_chunks;
-	u32 send_buf_chunk_cnt;
-	struct vmbus_gpadl send_buf_gpadl_handle;
 	u32 send_section_cnt;
 	u32 send_section_size;
 	unsigned long *send_section_map;
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
index 5cd084e5696c..ffba1443396a 100644
--- a/drivers/net/hyperv/netvsc.c
+++ b/drivers/net/hyperv/netvsc.c
@@ -134,10 +134,8 @@ static void __free_netvsc_device(struct netvsc_device *nvdev)
 
 	kfree(nvdev->extension);
 
-	vmbus_free_buffer(nvdev->recv_buf, nvdev->recv_buf_chunks,
-			  nvdev->recv_buf_chunk_cnt);
-	vmbus_free_buffer(nvdev->send_buf, nvdev->send_buf_chunks,
-			  nvdev->send_buf_chunk_cnt);
+	vmbus_release_buffer(&nvdev->recv_buffer);
+	vmbus_release_buffer(&nvdev->send_buffer);
 	bitmap_free(nvdev->send_section_map);
 
 	for (i = 0; i < VRSS_CHANNEL_MAX; i++) {
@@ -245,6 +243,7 @@ static void netvsc_revoke_recv_buf(struct hv_device *device,
 		if (ret != 0) {
 			netdev_err(ndev, "unable to send "
 				"revoke receive buffer to netvsp\n");
+			net_device->recv_buffer.leak = true;
 			return;
 		}
 		net_device->recv_section_cnt = 0;
@@ -296,6 +295,7 @@ static void netvsc_revoke_send_buf(struct hv_device *device,
 		if (ret != 0) {
 			netdev_err(ndev, "unable to send "
 				   "revoke send buffer to netvsp\n");
+			net_device->send_buffer.leak = true;
 			return;
 		}
 		net_device->send_section_cnt = 0;
@@ -308,14 +308,18 @@ static void netvsc_teardown_recv_gpadl(struct hv_device *device,
 {
 	int ret;
 
-	if (net_device->recv_buf_gpadl_handle.gpadl_handle) {
+	if (net_device->recv_buffer.leak)
+		return;
+
+	if (net_device->recv_buffer.gpadl.gpadl_handle) {
 		ret = vmbus_teardown_gpadl(device->channel,
-					   &net_device->recv_buf_gpadl_handle);
+					   &net_device->recv_buffer.gpadl);
 
 		/* If we failed here, we might as well return and have a leak
 		 * rather than continue and a bugchk
 		 */
 		if (ret != 0) {
+			net_device->recv_buffer.leak = true;
 			netdev_err(ndev,
 				   "unable to teardown receive buffer's gpadl\n");
 			return;
@@ -329,14 +333,18 @@ static void netvsc_teardown_send_gpadl(struct hv_device *device,
 {
 	int ret;
 
-	if (net_device->send_buf_gpadl_handle.gpadl_handle) {
+	if (net_device->send_buffer.leak)
+		return;
+
+	if (net_device->send_buffer.gpadl.gpadl_handle) {
 		ret = vmbus_teardown_gpadl(device->channel,
-					   &net_device->send_buf_gpadl_handle);
+					   &net_device->send_buffer.gpadl);
 
 		/* If we failed here, we might as well return and have a leak
 		 * rather than continue and a bugchk
 		 */
 		if (ret != 0) {
+			net_device->send_buffer.leak = true;
 			netdev_err(ndev,
 				   "unable to teardown send buffer's gpadl\n");
 			return;
@@ -377,11 +385,11 @@ static int netvsc_init_buf(struct hv_device *device,
 		buf_size = min_t(unsigned int, buf_size,
 				 NETVSC_RECEIVE_BUFFER_SIZE_LEGACY);
 
-	net_device->recv_buf =
+	net_device->recv_buffer.addr =
 		vmbus_alloc_buffer(device->channel, buf_size,
-				   &net_device->recv_buf_chunks,
-				   &net_device->recv_buf_chunk_cnt);
-	if (!net_device->recv_buf) {
+				   &net_device->recv_buffer.chunks,
+				   &net_device->recv_buffer.chunk_cnt);
+	if (!net_device->recv_buffer.addr) {
 		netdev_err(ndev,
 			   "unable to allocate receive buffer of size %u\n",
 			   buf_size);
@@ -397,9 +405,10 @@ static int netvsc_init_buf(struct hv_device *device,
 	 * than the channel to establish the gpadl handle.
 	 */
 	ret = vmbus_establish_gpadl_caller_decrypted(device->channel,
-						     net_device->recv_buf,
+						     net_device->recv_buffer.addr,
 						     buf_size,
-						     &net_device->recv_buf_gpadl_handle);
+						     &net_device->recv_buffer.gpadl);
+	net_device->recv_buffer.leak |= net_device->recv_buffer.gpadl.leak;
 	if (ret != 0) {
 		netdev_err(ndev,
 			"unable to establish receive buffer's gpadl\n");
@@ -411,7 +420,7 @@ static int netvsc_init_buf(struct hv_device *device,
 	memset(init_packet, 0, sizeof(struct nvsp_message));
 	init_packet->hdr.msg_type = NVSP_MSG1_TYPE_SEND_RECV_BUF;
 	init_packet->msg.v1_msg.send_recv_buf.
-		gpadl_handle = net_device->recv_buf_gpadl_handle.gpadl_handle;
+		gpadl_handle = net_device->recv_buffer.gpadl.gpadl_handle;
 	init_packet->msg.v1_msg.
 		send_recv_buf.id = NETVSC_RECEIVE_BUFFER_ID;
 
@@ -487,11 +496,11 @@ static int netvsc_init_buf(struct hv_device *device,
 	buf_size = device_info->send_sections * device_info->send_section_size;
 	buf_size = round_up(buf_size, PAGE_SIZE);
 
-	net_device->send_buf =
+	net_device->send_buffer.addr =
 		vmbus_alloc_buffer(device->channel, buf_size,
-				   &net_device->send_buf_chunks,
-				   &net_device->send_buf_chunk_cnt);
-	if (!net_device->send_buf) {
+				   &net_device->send_buffer.chunks,
+				   &net_device->send_buffer.chunk_cnt);
+	if (!net_device->send_buffer.addr) {
 		netdev_err(ndev, "unable to allocate send buffer of size %u\n",
 			   buf_size);
 		ret = -ENOMEM;
@@ -504,9 +513,10 @@ static int netvsc_init_buf(struct hv_device *device,
 	 * than the channel to establish the gpadl handle.
 	 */
 	ret = vmbus_establish_gpadl_caller_decrypted(device->channel,
-						     net_device->send_buf,
+						     net_device->send_buffer.addr,
 						     buf_size,
-						     &net_device->send_buf_gpadl_handle);
+						     &net_device->send_buffer.gpadl);
+	net_device->send_buffer.leak |= net_device->send_buffer.gpadl.leak;
 	if (ret != 0) {
 		netdev_err(ndev,
 			   "unable to establish send buffer's gpadl\n");
@@ -518,7 +528,7 @@ static int netvsc_init_buf(struct hv_device *device,
 	memset(init_packet, 0, sizeof(struct nvsp_message));
 	init_packet->hdr.msg_type = NVSP_MSG1_TYPE_SEND_SEND_BUF;
 	init_packet->msg.v1_msg.send_send_buf.gpadl_handle =
-		net_device->send_buf_gpadl_handle.gpadl_handle;
+		net_device->send_buffer.gpadl.gpadl_handle;
 	init_packet->msg.v1_msg.send_send_buf.id = NETVSC_SEND_BUFFER_ID;
 
 	trace_nvsp_send(ndev, init_packet);
@@ -968,7 +978,7 @@ static void netvsc_copy_to_send_buf(struct netvsc_device *net_device,
 				    struct hv_page_buffer *pb,
 				    bool xmit_more)
 {
-	char *start = net_device->send_buf;
+	char *start = net_device->send_buffer.addr;
 	char *dest = start + (section_index * net_device->send_section_size)
 		     + pend_size;
 	int i;
@@ -1475,7 +1485,7 @@ static int netvsc_receive(struct net_device *ndev,
 	const struct nvsp_message *nvsp = hv_pkt_data(desc);
 	u32 msglen = hv_pkt_datalen(desc);
 	u16 q_idx = channel->offermsg.offer.sub_channel_index;
-	char *recv_buf = net_device->recv_buf;
+	char *recv_buf = net_device->recv_buffer.addr;
 	u32 status = NVSP_STAT_SUCCESS;
 	int i;
 	int count = 0;
diff --git a/drivers/uio/uio_hv_generic.c b/drivers/uio/uio_hv_generic.c
index 7b4cc456c453..b3f41ffc74f8 100644
--- a/drivers/uio/uio_hv_generic.c
+++ b/drivers/uio/uio_hv_generic.c
@@ -150,19 +150,21 @@ static void hv_uio_rescind(struct vmbus_channel *channel)
 	vmbus_device_unregister(channel->device_obj);
 }
 
-/* Function used for mmap of ring buffer sysfs interface.
- * The ring buffer is allocated as contiguous memory by vmbus_open
- */
+/* Function used for mmap of the ring buffer sysfs interface. */
 static int
 hv_uio_ring_mmap_prepare(struct vmbus_channel *channel, struct vm_area_desc *desc)
 {
-	void *ring_buffer = page_address(channel->ringbuffer_page);
+	unsigned long pages = vma_desc_pages(desc);
+	pgoff_t offset = desc->pgoff;
 
 	if (channel->state != CHANNEL_OPENED_STATE)
 		return -ENODEV;
+	if (offset >= channel->ringbuffer_pagecount ||
+	    pages > channel->ringbuffer_pagecount - offset)
+		return -EINVAL;
 
-	mmap_action_simple_ioremap(desc, virt_to_phys(ring_buffer),
-			channel->ringbuffer_pagecount << PAGE_SHIFT);
+	mmap_action_map_kernel_pages(desc, desc->start,
+				     channel->ringbuffer.pages + offset, pages);
 	return 0;
 }
 
@@ -196,14 +198,16 @@ static void
 hv_uio_cleanup(struct hv_device *dev, struct hv_uio_private_data *pdata)
 {
 	if (pdata->send_gpadl.gpadl_handle) {
-		vmbus_teardown_gpadl(dev->channel, &pdata->send_gpadl);
-		if (!pdata->send_gpadl.decrypted)
+		if (vmbus_teardown_gpadl(dev->channel, &pdata->send_gpadl))
+			pdata->send_gpadl.leak = true;
+		if (!pdata->send_gpadl.leak && !pdata->send_gpadl.decrypted)
 			vfree(pdata->send_buf);
 	}
 
 	if (pdata->recv_gpadl.gpadl_handle) {
-		vmbus_teardown_gpadl(dev->channel, &pdata->recv_gpadl);
-		if (!pdata->recv_gpadl.decrypted)
+		if (vmbus_teardown_gpadl(dev->channel, &pdata->recv_gpadl))
+			pdata->recv_gpadl.leak = true;
+		if (!pdata->recv_gpadl.leak && !pdata->recv_gpadl.decrypted)
 			vfree(pdata->recv_buf);
 	}
 }
@@ -283,12 +287,11 @@ hv_uio_probe(struct hv_device *dev,
 
 	/* mem resources */
 	pdata->info.mem[TXRX_RING_MAP].name = "txrx_rings";
-	ring_buffer = page_address(channel->ringbuffer_page);
-	pdata->info.mem[TXRX_RING_MAP].addr
-		= (uintptr_t)virt_to_phys(ring_buffer);
+	ring_buffer = channel->ringbuffer.addr;
+	pdata->info.mem[TXRX_RING_MAP].addr = (uintptr_t)ring_buffer;
 	pdata->info.mem[TXRX_RING_MAP].size
 		= channel->ringbuffer_pagecount << PAGE_SHIFT;
-	pdata->info.mem[TXRX_RING_MAP].memtype = UIO_MEM_IOVA;
+	pdata->info.mem[TXRX_RING_MAP].memtype = UIO_MEM_VIRTUAL;
 
 	pdata->info.mem[INT_PAGE_MAP].name = "int_page";
 	pdata->info.mem[INT_PAGE_MAP].addr
@@ -312,7 +315,8 @@ hv_uio_probe(struct hv_device *dev,
 		ret = vmbus_establish_gpadl(channel, pdata->recv_buf,
 					    RECV_BUFFER_SIZE, &pdata->recv_gpadl);
 		if (ret) {
-			if (!pdata->recv_gpadl.decrypted)
+			if (!pdata->recv_gpadl.leak &&
+			    !pdata->recv_gpadl.decrypted)
 				vfree(pdata->recv_buf);
 			goto fail_close;
 		}
@@ -334,7 +338,8 @@ hv_uio_probe(struct hv_device *dev,
 		ret = vmbus_establish_gpadl(channel, pdata->send_buf,
 					    SEND_BUFFER_SIZE, &pdata->send_gpadl);
 		if (ret) {
-			if (!pdata->send_gpadl.decrypted)
+			if (!pdata->send_gpadl.leak &&
+			    !pdata->send_gpadl.decrypted)
 				vfree(pdata->send_buf);
 			goto fail_close;
 		}
diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
index 9e109d91aa14..2878aed14c45 100644
--- a/include/linux/hyperv.h
+++ b/include/linux/hyperv.h
@@ -70,8 +70,7 @@
  */
 enum hv_gpadl_type {
 	HV_GPADL_BUFFER,
-	HV_GPADL_RING,
-	HV_GPADL_BUFFER_DECRYPTED
+	HV_GPADL_RING
 };
 
 /* Single-page buffer */
@@ -782,6 +781,16 @@ struct vmbus_gpadl {
 	u32 size;
 	void *buffer;
 	bool decrypted;
+	bool leak;
+};
+
+struct vmbus_buffer {
+	void *addr;
+	struct page **chunks;
+	struct page **pages;
+	u32 chunk_cnt;
+	struct vmbus_gpadl gpadl;
+	bool leak;
 };
 
 struct vmbus_channel {
@@ -803,10 +812,8 @@ struct vmbus_channel {
 	bool rescind_ref; /* got rescind msg, got channel reference */
 	struct completion rescind_event;
 
-	struct vmbus_gpadl ringbuffer_gpadlhandle;
-
 	/* Allocated memory for ring buffer */
-	struct page *ringbuffer_page;
+	struct vmbus_buffer ringbuffer;
 	u32 ringbuffer_pagecount;
 	u32 ringbuffer_send_offset;
 	struct hv_ring_buffer_info outbound;	/* send to parent */
@@ -1219,6 +1226,7 @@ extern void *vmbus_alloc_buffer(struct vmbus_channel *channel,
 				u32 *chunk_cnt_out);
 
 extern void vmbus_free_buffer(void *addr, struct page **chunks, u32 chunk_cnt);
+void vmbus_release_buffer(struct vmbus_buffer *buffer);
 
 void vmbus_reset_channel_cb(struct vmbus_channel *channel);
 
-- 
2.43.0


  reply	other threads:[~2026-10-07 19:08 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-07 19:07 [PATCH v2 0/14] hv: vmbus: make rings and host-visible buffers survive buddy fragmentation Emerson Busson
2026-10-07 19:07 ` Emerson Busson [this message]
2026-10-07 19:07 ` [PATCH v2 02/14] hv: vmbus: validate chunk buffer allocation and cleanup Emerson Busson
2026-10-07 19:07 ` [PATCH v2 03/14] uio: hv_generic: describe buffers for owned allocation Emerson Busson
2026-10-07 19:07 ` [PATCH v2 04/14] hv: vmbus: add KUnit tests for GPADL post failure injection Emerson Busson
2026-10-07 19:07 ` [PATCH v2 05/14] hv: vmbus: add KUnit test for order-zero allocation fallback Emerson Busson
2026-10-07 19:07 ` [PATCH v2 06/14] hv: vmbus: cover all shared-page policy combinations Emerson Busson
2026-10-07 19:07 ` [PATCH v2 07/14] hv: vmbus: distinguish host rescind from local channel unload Emerson Busson
2026-10-07 19:07 ` [PATCH v2 08/14] hv: vmbus: retain backing until ownership and references clear Emerson Busson
2026-10-07 19:07 ` [PATCH v2 09/14] hv: use owned VMBus buffers in NetVSC and UIO Emerson Busson
2026-10-07 19:07 ` [PATCH v2 10/14] hv: vmbus: pin buffer pages across UIO mmap to close the reclaim race Emerson Busson
2026-10-07 19:07 ` [PATCH v2 11/14] hv: vmbus: vmalloc requestor metadata Emerson Busson
2026-10-07 19:07 ` [PATCH v2 12/14] hv: netvsc: allocate RNDIS request descriptors with kvzalloc_obj() Emerson Busson
2026-10-07 19:07 ` [PATCH v2 13/14] hv: netvsc: handle a NULL request address on empty completions Emerson Busson
2026-10-07 19:07 ` [PATCH v2 14/14] hv: netvsc: use kvzalloc for device state Emerson Busson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261007190752.336426-2-emersonbusson@gmail.com \
    --to=emersonbusson@gmail.com \
    --cc=andrew+netdev@lunn.ch \
    --cc=davem@davemloft.net \
    --cc=decui@microsoft.com \
    --cc=edumazet@google.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=haiyangz@microsoft.com \
    --cc=kuba@kernel.org \
    --cc=kys@microsoft.com \
    --cc=linux-hyperv@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mhklinux@outlook.com \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=wei.liu@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®