[PATCH 1/1] Drivers: hv: vmbus: Limit channel interrupt scan to relid high water mark

From: Michael Kelley

Date: Fri Feb 20 2026 - 11:41:15 EST


From: Michael Kelley <mhklinux@xxxxxxxxxxx>

When checking for VMBus channel interrutps, current code always scans the
full SynIC receive interrupt bit array to get the relid of the
interrupting channels. The array has HV_EVENT_FLAGS_COUNT (2048) bits.
But VMs rarely have more than 100 channels, and the relid is typically
a small integer that is densely assigned by the Hyper-V host. It's
wasteful to scan 2048 bits when it is highly unlikely that anything will
be found past bit 100. The waste is double with Confidential VMBus because
there are two receive interrupt arrays that must be scanned: one for the
hypervisor SynIC and one for the paravisor SynIC.

Improve the scanning by tracking the largest relid that has been offered
by the Hyper-V host. Then when checking for VMBus channel interrupts, only
scan up to this high water mark.

When channels are rescinded, it's not worth the complexity to recalculate
the high water mark. Hyper-V tends to reuse the rescinded relids for any
new channels that are subsequently added, and the performance benefit of
exactly tracking the high water mark would be minimal.

Signed-off-by: Michael Kelley <mhklinux@xxxxxxxxxxx>
---
drivers/hv/channel_mgmt.c | 16 ++++++++++++----
drivers/hv/hyperv_vmbus.h | 3 ++-
drivers/hv/vmbus_drv.c | 7 +------
3 files changed, 15 insertions(+), 11 deletions(-)

diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c
index 74fed2c073d4..61f7dffd0f50 100644
--- a/drivers/hv/channel_mgmt.c
+++ b/drivers/hv/channel_mgmt.c
@@ -384,8 +384,18 @@ static void free_channel(struct vmbus_channel *channel)

void vmbus_channel_map_relid(struct vmbus_channel *channel)
{
- if (WARN_ON(channel->offermsg.child_relid >= MAX_CHANNEL_RELIDS))
+ u32 new_relid = channel->offermsg.child_relid;
+
+ if (WARN_ON(new_relid >= MAX_CHANNEL_RELIDS))
return;
+
+ /*
+ * This function is always called in the tasklet for the connect CPU.
+ * So updating the relid hiwater mark does not need to be atomic.
+ */
+ if (new_relid > READ_ONCE(vmbus_connection.relid_hiwater))
+ WRITE_ONCE(vmbus_connection.relid_hiwater, new_relid);
+
/*
* The mapping of the channel's relid is visible from the CPUs that
* execute vmbus_chan_sched() by the time that vmbus_chan_sched() will
@@ -411,9 +421,7 @@ void vmbus_channel_map_relid(struct vmbus_channel *channel)
* of the VMBus driver and vmbus_chan_sched() can not run before
* vmbus_bus_resume() has completed execution (cf. resume_noirq).
*/
- virt_store_mb(
- vmbus_connection.channels[channel->offermsg.child_relid],
- channel);
+ virt_store_mb(vmbus_connection.channels[new_relid], channel);
}

void vmbus_channel_unmap_relid(struct vmbus_channel *channel)
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
index 7bd8f8486e85..2c90c81a3b0f 100644
--- a/drivers/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
@@ -276,8 +276,9 @@ struct vmbus_connection {
struct list_head chn_list;
struct mutex channel_mutex;

- /* Array of channels */
+ /* Array of channel pointers, indexed by relid */
struct vmbus_channel **channels;
+ u32 relid_hiwater;

/*
* An offer message is handled first on the work_queue, and then
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
index 3e7a52918ce0..a96da105b593 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
@@ -1258,17 +1258,12 @@ static void vmbus_chan_sched(void *event_page_addr)
return;
event = (union hv_synic_event_flags *)event_page_addr + VMBUS_MESSAGE_SINT;

- maxbits = HV_EVENT_FLAGS_COUNT;
+ maxbits = READ_ONCE(vmbus_connection.relid_hiwater) + 1;
recv_int_page = event->flags;

if (unlikely(!recv_int_page))
return;

- /*
- * Suggested-by: Michael Kelley <mhklinux@xxxxxxxxxxx>
- * One possible optimization would be to keep track of the largest relID that's in use,
- * and only scan up to that relID.
- */
for_each_set_bit(relid, recv_int_page, maxbits) {
void (*callback_fn)(void *context);
struct vmbus_channel *channel;
--
2.25.1