Drivers: hv: vmbus: Fix a Host signaling bug
authorK. Y. Srinivasan <kys@microsoft.com>
Tue, 15 Dec 2015 00:01:54 +0000 (16:01 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 15 Dec 2015 03:15:05 +0000 (19:15 -0800)
Currently we have two policies for deciding when to signal the host:
One based on the ring buffer state and the other based on what the
VMBUS client driver wants to do. Consider the case when the client
wants to explicitly control when to signal the host. In this case,
if the client were to defer signaling, we will not be able to signal
the host subsequently when the client does want to signal since the
ring buffer state will prevent the signaling. Implement logic to
have only one signaling policy in force for a given channel.

Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
Tested-by: Haiyang Zhang <haiyangz@microsoft.com>
Cc: <stable@vger.kernel.org> # v4.2+
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/hv/channel.c
include/linux/hyperv.h

index 77d2579d612415b97bdae6c84004455e48fe3254..2889d97c03b1d8da44dfb179715e78d59439f272 100644 (file)
@@ -653,10 +653,19 @@ int vmbus_sendpacket_ctl(struct vmbus_channel *channel, void *buffer,
         *    on the ring. We will not signal if more data is
         *    to be placed.
         *
+        * Based on the channel signal state, we will decide
+        * which signaling policy will be applied.
+        *
         * If we cannot write to the ring-buffer; signal the host
         * even if we may not have written anything. This is a rare
         * enough condition that it should not matter.
         */
+
+       if (channel->signal_policy)
+               signal = true;
+       else
+               kick_q = true;
+
        if (((ret == 0) && kick_q && signal) || (ret))
                vmbus_setevent(channel);
 
@@ -756,10 +765,19 @@ int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel,
         *    on the ring. We will not signal if more data is
         *    to be placed.
         *
+        * Based on the channel signal state, we will decide
+        * which signaling policy will be applied.
+        *
         * If we cannot write to the ring-buffer; signal the host
         * even if we may not have written anything. This is a rare
         * enough condition that it should not matter.
         */
+
+       if (channel->signal_policy)
+               signal = true;
+       else
+               kick_q = true;
+
        if (((ret == 0) && kick_q && signal) || (ret))
                vmbus_setevent(channel);
 
index f773a6871f4c68c47173b7137f354d4cb4c3a6dd..acd995b81c6bbc45c742d21ff800ae04e6de8b4b 100644 (file)
@@ -630,6 +630,11 @@ struct hv_input_signal_event_buffer {
        struct hv_input_signal_event event;
 };
 
+enum hv_signal_policy {
+       HV_SIGNAL_POLICY_DEFAULT = 0,
+       HV_SIGNAL_POLICY_EXPLICIT,
+};
+
 struct vmbus_channel {
        /* Unique channel id */
        int id;
@@ -757,8 +762,21 @@ struct vmbus_channel {
         * link up channels based on their CPU affinity.
         */
        struct list_head percpu_list;
+       /*
+        * Host signaling policy: The default policy will be
+        * based on the ring buffer state. We will also support
+        * a policy where the client driver can have explicit
+        * signaling control.
+        */
+       enum hv_signal_policy  signal_policy;
 };
 
+static inline void set_channel_signal_state(struct vmbus_channel *c,
+                                           enum hv_signal_policy policy)
+{
+       c->signal_policy = policy;
+}
+
 static inline void set_channel_read_state(struct vmbus_channel *c, bool state)
 {
        c->batched_reading = state;