* [PATCH 1/1] Drivers: hv: vmbus: Limit channel interrupt scan to relid high water mark
@ 2026-02-20 16:40 Michael Kelley
2026-02-21 1:47 ` vdso
2026-03-12 4:44 ` Wei Liu
0 siblings, 2 replies; 4+ messages in thread
From: Michael Kelley @ 2026-02-20 16:40 UTC (permalink / raw)
To: kys, haiyangz, wei.liu, decui, longli, linux-hyperv; +Cc: linux-kernel
From: Michael Kelley <mhklinux@outlook.com>
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@outlook.com>
---
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@outlook.com>
- * 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
^ permalink raw reply related [flat|nested] 4+ messages in thread* Re: [PATCH 1/1] Drivers: hv: vmbus: Limit channel interrupt scan to relid high water mark
2026-02-20 16:40 [PATCH 1/1] Drivers: hv: vmbus: Limit channel interrupt scan to relid high water mark Michael Kelley
@ 2026-02-21 1:47 ` vdso
2026-02-21 2:55 ` mhklkml
2026-03-12 4:44 ` Wei Liu
1 sibling, 1 reply; 4+ messages in thread
From: vdso @ 2026-02-21 1:47 UTC (permalink / raw)
To: mhklinux, Michael Kelley
Cc: linux-kernel, kys, wei.liu, haiyangz, longli, decui, linux-hyperv
Hi Michael,
Boots for me on an x86_64 machine. Got a typo fix and a question for you.
Tagging as reviewed and tested regardless :)
> On 02/20/2026 8:40 AM Michael Kelley <mhklkml@zohomail.com> wrote:
>
>
> From: Michael Kelley <mhklinux@outlook.com>
>
> When checking for VMBus channel interrutps, current code always scans the
/s/interrutps/interrupts
> 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@outlook.com>
Tested-by: Roman Kisel <vdso@mailbox.org>
Reviewed-by: Roman Kisel <vdso@mailbox.org>
> ---
> 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;
Worth checking that "maxbits <= HV_EVENT_FLAGS_COUNT" to protect from corruptions,
etc. or would be too paranoidal?
> recv_int_page = event->flags;
>
> if (unlikely(!recv_int_page))
> return;
>
> - /*
> - * Suggested-by: Michael Kelley <mhklinux@outlook.com>
> - * 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
^ permalink raw reply [flat|nested] 4+ messages in thread* RE: [PATCH 1/1] Drivers: hv: vmbus: Limit channel interrupt scan to relid high water mark
2026-02-21 1:47 ` vdso
@ 2026-02-21 2:55 ` mhklkml
0 siblings, 0 replies; 4+ messages in thread
From: mhklkml @ 2026-02-21 2:55 UTC (permalink / raw)
To: vdso, mhklinux
Cc: linux-kernel, kys, wei.liu, haiyangz, longli, decui, linux-hyperv
From: vdso@mailbox.org <vdso@mailbox.org> Sent: Friday, February 20, 2026 5:48 PM
>
> Hi Michael,
>
> Boots for me on an x86_64 machine. Got a typo fix and a question for you.
> Tagging as reviewed and tested regardless :)
>
> > On 02/20/2026 8:40 AM Michael Kelley <mhklkml@zohomail.com> wrote:
> >
> >
> > From: Michael Kelley <mhklinux@outlook.com>
> >
> > When checking for VMBus channel interrutps, current code always scans the
>
> /s/interrutps/interrupts
>
> > 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@outlook.com>
>
> Tested-by: Roman Kisel <vdso@mailbox.org>
> Reviewed-by: Roman Kisel <vdso@mailbox.org>
Thanks!
>
> > ---
> > 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;
>
> Worth checking that "maxbits <= HV_EVENT_FLAGS_COUNT" to protect from
> corruptions, etc. or would be too paranoidal?
We definitely want to validate what Hyper-V returns to the guest as a relid,
and drop any values that are "too big", so we don't go indexing off into
bogus memory. But that validation is done in vmbus_channel_map_relid()
with a WARN_ON() before setting relid_hiwater. So there's no way for
relid_hiwater to be bogus, and additional validation here in
vmbus_chan_sched() really isn't necessary.
Michael
>
> > recv_int_page = event->flags;
> >
> > if (unlikely(!recv_int_page))
> > return;
> >
> > - /*
> > - * Suggested-by: Michael Kelley <mhklinux@outlook.com>
> > - * 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
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH 1/1] Drivers: hv: vmbus: Limit channel interrupt scan to relid high water mark
2026-02-20 16:40 [PATCH 1/1] Drivers: hv: vmbus: Limit channel interrupt scan to relid high water mark Michael Kelley
2026-02-21 1:47 ` vdso
@ 2026-03-12 4:44 ` Wei Liu
1 sibling, 0 replies; 4+ messages in thread
From: Wei Liu @ 2026-03-12 4:44 UTC (permalink / raw)
To: mhklinux
Cc: kys, haiyangz, wei.liu, decui, longli, linux-hyperv, linux-kernel
On Fri, Feb 20, 2026 at 08:40:45AM -0800, Michael Kelley wrote:
> From: Michael Kelley <mhklinux@outlook.com>
>
> 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@outlook.com>
Applied to hyperv-next. Thanks.
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-03-12 4:44 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-20 16:40 [PATCH 1/1] Drivers: hv: vmbus: Limit channel interrupt scan to relid high water mark Michael Kelley
2026-02-21 1:47 ` vdso
2026-02-21 2:55 ` mhklkml
2026-03-12 4:44 ` Wei Liu
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox