From: Emerson Busson <emersonbusson@gmail.com>
To: linux-hyperv@vger.kernel.org
Cc: kys@microsoft.com, haiyangz@microsoft.com, wei.liu@kernel.org,
decui@microsoft.com, linux-kernel@vger.kernel.org,
emersonbusson@gmail.com
Subject: [PATCH 2/2] hv: vmbus: add virtual memory fallback for ring buffer allocations under memory pressure
Date: Thu, 17 Sep 2026 22:40:17 -0300 [thread overview]
Message-ID: <20260918014017.2536753-3-emersonbusson@gmail.com> (raw)
In-Reply-To: <20260918014017.2536753-1-emersonbusson@gmail.com>
When VMBus sub-channels (such as synthetic SCSI, network, or vsock
channels) are dynamically opened during periods of sustained memory
load or memory tier eviction, `vmbus_alloc_ring()` attempts to
allocate physically contiguous memory using
`alloc_pages(GFP_KERNEL | __GFP_ZERO, order)`.
For standard rings (order-7, 512 KiB contiguous memory), high buddy
allocator fragmentation under memory pressure frequently causes
`alloc_pages()` to fail with -ENOMEM even when ample total virtual
memory is available. This manifests in userspace as connection
timeouts (e.g. `accept4 failed 110: Connection timed out` on WSL2 vsock
control planes).
This patch introduces a resilient fallback mechanism:
1. When `alloc_pages()` fails due to external fragmentation,
`vmbus_alloc_ring()` falls back to `vzalloc_node()` (or `vzalloc()`)
to satisfy the buffer allocation from virtually contiguous pages.
2. In `hv_ringbuffer_init()`, detects `is_vmalloc_addr(virt_addr)` and
populates the `pages_wraparound` mapping array using
`vmalloc_to_page()`.
3. In `vmbus_free_ring()`, tracks `ringbuffer_is_vmalloc` and calls
`vfree()` safely, preserving Confidential VM (CoCo) memory
re-encryption isolation checks prior to release.
Signed-off-by: Emerson Busson <emersonbusson@gmail.com>
---
drivers/hv/channel.c | 46 ++++++++++++++++++++++++++++++++++-----
drivers/hv/hyperv_vmbus.h | 2 +-
drivers/hv/ring_buffer.c | 19 +++++++++++-----
include/linux/hyperv.h | 2 ++
4 files changed, 57 insertions(+), 12 deletions(-)
diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c
index 162d6aeec..f0fb3dd8f 100644
--- a/drivers/hv/channel.c
+++ b/drivers/hv/channel.c
@@ -12,6 +12,7 @@
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/mm.h>
+#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/hyperv.h>
@@ -153,13 +154,20 @@ void vmbus_free_ring(struct vmbus_channel *channel)
hv_ringbuffer_cleanup(&channel->outbound);
hv_ringbuffer_cleanup(&channel->inbound);
- if (channel->ringbuffer_page) {
+ if (channel->ringbuffer_is_vmalloc && channel->ringbuffer_page_virt) {
+ /* In a CoCo VM leak the memory if it didn't get re-encrypted */
+ if (!channel->ringbuffer_gpadlhandle.decrypted)
+ vfree(channel->ringbuffer_page_virt);
+ channel->ringbuffer_page_virt = NULL;
+ channel->ringbuffer_is_vmalloc = false;
+ } else 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;
+ channel->ringbuffer_page_virt = NULL;
}
}
EXPORT_SYMBOL_GPL(vmbus_free_ring);
@@ -182,10 +190,26 @@ int vmbus_alloc_ring(struct vmbus_channel *newchannel,
if (!page)
page = alloc_pages(GFP_KERNEL|__GFP_ZERO, order);
- if (!page)
- return -ENOMEM;
+ if (!page) {
+ /* Fallback to virtual memory allocation under buddy fragmentation */
+ void *virt_addr = vzalloc_node(send_size + recv_size,
+ cpu_to_node(newchannel->target_cpu));
+
+ if (!virt_addr)
+ virt_addr = vzalloc(send_size + recv_size);
+
+ if (!virt_addr)
+ return -ENOMEM;
+
+ newchannel->ringbuffer_page = NULL;
+ newchannel->ringbuffer_page_virt = virt_addr;
+ newchannel->ringbuffer_is_vmalloc = true;
+ } else {
+ newchannel->ringbuffer_page = page;
+ newchannel->ringbuffer_page_virt = page_address(page);
+ newchannel->ringbuffer_is_vmalloc = false;
+ }
- newchannel->ringbuffer_page = page;
newchannel->ringbuffer_pagecount = (send_size + recv_size) >> PAGE_SHIFT;
newchannel->ringbuffer_send_offset = send_size >> PAGE_SHIFT;
@@ -639,6 +663,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;
+ void *inbound_virt = NULL;
u32 send_pages, recv_pages;
unsigned long flags;
int err;
@@ -669,6 +694,8 @@ static int __vmbus_open(struct vmbus_channel *newchannel,
newchannel->ringbuffer_gpadlhandle.gpadl_handle = 0;
err = __vmbus_establish_gpadl(newchannel, HV_GPADL_RING,
+ newchannel->ringbuffer_page_virt ?
+ newchannel->ringbuffer_page_virt :
page_address(newchannel->ringbuffer_page),
(send_pages + recv_pages) << PAGE_SHIFT,
newchannel->ringbuffer_send_offset << PAGE_SHIFT,
@@ -677,11 +704,18 @@ static int __vmbus_open(struct vmbus_channel *newchannel,
goto error_clean_ring;
err = hv_ringbuffer_init(&newchannel->outbound,
- page, send_pages, 0);
+ page, newchannel->ringbuffer_page_virt,
+ send_pages, 0);
if (err)
goto error_free_gpadl;
- err = hv_ringbuffer_init(&newchannel->inbound, &page[send_pages],
+ if (newchannel->ringbuffer_page_virt)
+ inbound_virt = newchannel->ringbuffer_page_virt +
+ (send_pages << PAGE_SHIFT);
+
+ err = hv_ringbuffer_init(&newchannel->inbound,
+ page ? &page[send_pages] : NULL,
+ inbound_virt,
recv_pages, newchannel->max_pkt_size);
if (err)
goto error_free_gpadl;
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
index 34943de7d..ec06c30d2 100644
--- a/drivers/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
@@ -182,7 +182,7 @@ 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);
+ struct page *pages, void *virt_addr, u32 pagecnt, u32 max_pkt_size);
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 23ce1fb70..e6d4cf185 100644
--- a/drivers/hv/ring_buffer.c
+++ b/drivers/hv/ring_buffer.c
@@ -184,7 +184,7 @@ 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)
+ struct page *pages, void *virt_addr, u32 page_cnt, u32 max_pkt_size)
{
struct page **pages_wraparound;
int i;
@@ -201,10 +201,19 @@ int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info,
if (!pages_wraparound)
return -ENOMEM;
- pages_wraparound[0] = pages;
- for (i = 0; i < 2 * (page_cnt - 1); i++)
- pages_wraparound[i + 1] =
- &pages[i % (page_cnt - 1) + 1];
+ if (virt_addr && is_vmalloc_addr(virt_addr)) {
+ pages_wraparound[0] = vmalloc_to_page(virt_addr);
+ for (i = 0; i < 2 * (page_cnt - 1); i++) {
+ void *curr_virt = virt_addr + ((i % (page_cnt - 1) + 1) << PAGE_SHIFT);
+
+ pages_wraparound[i + 1] = vmalloc_to_page(curr_virt);
+ }
+ } else {
+ pages_wraparound[0] = pages;
+ for (i = 0; i < 2 * (page_cnt - 1); i++)
+ pages_wraparound[i + 1] =
+ &pages[i % (page_cnt - 1) + 1];
+ }
ring_info->ring_buffer = (struct hv_ring_buffer *)
vmap(pages_wraparound, page_cnt * 2 - 1, VM_MAP,
diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
index a76f556f5..63203df4d 100644
--- a/include/linux/hyperv.h
+++ b/include/linux/hyperv.h
@@ -807,6 +807,8 @@ struct vmbus_channel {
/* Allocated memory for ring buffer */
struct page *ringbuffer_page;
+ void *ringbuffer_page_virt;
+ bool ringbuffer_is_vmalloc;
u32 ringbuffer_pagecount;
u32 ringbuffer_send_offset;
struct hv_ring_buffer_info outbound; /* send to parent */
--
2.43.0
prev parent reply other threads:[~2026-09-18 1:40 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-18 1:40 [PATCH 0/2] hv: vmbus: prevent control-plane starvation and order-7 ring deadlocks " Emerson Busson
2026-09-18 1:40 ` [PATCH 1/2] hv: vmbus: prevent control-plane starvation and balloon thrash " Emerson Busson
2026-09-18 1:40 ` Emerson Busson [this message]
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=20260918014017.2536753-3-emersonbusson@gmail.com \
--to=emersonbusson@gmail.com \
--cc=decui@microsoft.com \
--cc=haiyangz@microsoft.com \
--cc=kys@microsoft.com \
--cc=linux-hyperv@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--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®