]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
uio_hv_generic: Set event for all channels on the device
authorLong Li <longli@microsoft.com>
Mon, 10 Mar 2025 22:12:01 +0000 (15:12 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 24 Nov 2025 09:36:07 +0000 (10:36 +0100)
commit d062463edf1770427dc2d637df4088df4835aa47 upstream.

Hyper-V may offer a non latency sensitive device with subchannels without
monitor bit enabled. The decision is entirely on the Hyper-V host not
configurable within guest.

When a device has subchannels, also signal events for the subchannel
if its monitor bit is disabled.

This patch also removes the memory barrier when monitor bit is enabled
as it is not necessary. The memory barrier is only needed between
setting up interrupt mask and calling vmbus_set_event() when monitor
bit is disabled.

Signed-off-by: Long Li <longli@microsoft.com>
Reviewed-by: Michael Kelley <mhklinux@outlook.com>
Reviewed-by: Saurabh Sengar <ssengar@linux.microsoft.com>
Link: https://lore.kernel.org/r/1741644721-20389-1-git-send-email-longli@linuxonhyperv.com
Fixes: 37bd91f22794 ("uio_hv_generic: Let userspace take care of interrupt mask")
Cc: <stable@vger.kernel.org> # 6.12.x
Signed-off-by: Naman Jain <namjain@linux.microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/uio/uio_hv_generic.c

index 0b414d1168dd5349fbe25873a31ed3d9ecf6beb2..aa7593cea2e30e712ce0ae5c1143123948b7b5ae 100644 (file)
@@ -65,6 +65,16 @@ struct hv_uio_private_data {
        char    send_name[32];
 };
 
+static void set_event(struct vmbus_channel *channel, s32 irq_state)
+{
+       channel->inbound.ring_buffer->interrupt_mask = !irq_state;
+       if (!channel->offermsg.monitor_allocated && irq_state) {
+               /* MB is needed for host to see the interrupt mask first */
+               virt_mb();
+               vmbus_set_event(channel);
+       }
+}
+
 /*
  * This is the irqcontrol callback to be registered to uio_info.
  * It can be used to disable/enable interrupt from user space processes.
@@ -79,12 +89,15 @@ hv_uio_irqcontrol(struct uio_info *info, s32 irq_state)
 {
        struct hv_uio_private_data *pdata = info->priv;
        struct hv_device *dev = pdata->device;
+       struct vmbus_channel *primary, *sc;
 
-       dev->channel->inbound.ring_buffer->interrupt_mask = !irq_state;
-       virt_mb();
+       primary = dev->channel;
+       set_event(primary, irq_state);
 
-       if (!dev->channel->offermsg.monitor_allocated && irq_state)
-               vmbus_setevent(dev->channel);
+       mutex_lock(&vmbus_connection.channel_mutex);
+       list_for_each_entry(sc, &primary->sc_list, sc_list)
+               set_event(sc, irq_state);
+       mutex_unlock(&vmbus_connection.channel_mutex);
 
        return 0;
 }
@@ -95,11 +108,18 @@ hv_uio_irqcontrol(struct uio_info *info, s32 irq_state)
 static void hv_uio_channel_cb(void *context)
 {
        struct vmbus_channel *chan = context;
-       struct hv_device *hv_dev = chan->device_obj;
-       struct hv_uio_private_data *pdata = hv_get_drvdata(hv_dev);
+       struct hv_device *hv_dev;
+       struct hv_uio_private_data *pdata;
 
        virt_mb();
 
+       /*
+        * The callback may come from a subchannel, in which case look
+        * for the hv device in the primary channel
+        */
+       hv_dev = chan->primary_channel ?
+                chan->primary_channel->device_obj : chan->device_obj;
+       pdata = hv_get_drvdata(hv_dev);
        uio_event_notify(&pdata->info);
 }