]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
Drivers: hv: kvp: fix IP Failover
authorVitaly Kuznetsov <vkuznets@redhat.com>
Sun, 1 May 2016 02:21:33 +0000 (19:21 -0700)
committerJack Vogel <jack.vogel@oracle.com>
Wed, 31 May 2017 02:49:58 +0000 (19:49 -0700)
Hyper-V VMs can be replicated to another hosts and there is a feature to
set different IP for replicas, it is called 'Failover TCP/IP'. When
such guest starts Hyper-V host sends it KVP_OP_SET_IP_INFO message as soon
as we finish negotiation procedure. The problem is that it can happen (and
it actually happens) before userspace daemon connects and we reply with
HV_E_FAIL to the message. As there are no repetitions we fail to set the
requested IP.

Solve the issue by postponing our reply to the negotiation message till
userspace daemon is connected. We can't wait too long as there is a
host-side timeout (cca. 75 seconds) and if we fail to reply in this time
frame the whole KVP service will become inactive. The solution is not
ideal - if it takes userspace daemon more than 60 seconds to connect
IP Failover will still fail but I don't see a solution with our current
separation between kernel and userspace parts.

Other two modules (VSS and FCOPY) don't require such delay, leave them
untouched.

Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Orabug: 25970637
(cherry picked from commit 4dbfc2e68004c60edab7e8fd26784383dd3ee9bc)
Signed-off-by: Jack Vogel <jack.vogel@oracle.com>
drivers/hv/hv_kvp.c
drivers/hv/hyperv_vmbus.h

index 3ed4fcde0b7e8c853cef60ae5e7dfb15f01296ed..4c0c6f27daac5dbc00fd10e7d35595b33dc00785 100644 (file)
@@ -78,9 +78,11 @@ static void kvp_send_key(struct work_struct *dummy);
 
 static void kvp_respond_to_host(struct hv_kvp_msg *msg, int error);
 static void kvp_timeout_func(struct work_struct *dummy);
+static void kvp_host_handshake_func(struct work_struct *dummy);
 static void kvp_register(int);
 
 static DECLARE_DELAYED_WORK(kvp_timeout_work, kvp_timeout_func);
+static DECLARE_DELAYED_WORK(kvp_host_handshake_work, kvp_host_handshake_func);
 static DECLARE_WORK(kvp_sendkey_work, kvp_send_key);
 
 static const char kvp_devname[] = "vmbus/hv_kvp";
@@ -130,6 +132,11 @@ static void kvp_timeout_func(struct work_struct *dummy)
        hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper);
 }
 
+static void kvp_host_handshake_func(struct work_struct *dummy)
+{
+       hv_poll_channel(kvp_transaction.recv_channel, hv_kvp_onchannelcallback);
+}
+
 static int kvp_handle_handshake(struct hv_kvp_msg *msg)
 {
        switch (msg->kvp_hdr.operation) {
@@ -153,7 +160,13 @@ static int kvp_handle_handshake(struct hv_kvp_msg *msg)
         */
        pr_info("KVP: user-mode registering done.\n");
        kvp_register(dm_reg_value);
-       kvp_transaction.state = HVUTIL_READY;
+
+       /*
+        * If we're still negotiating with the host cancel the timeout
+        * work to not poll the channel twice.
+        */
+       cancel_delayed_work_sync(&kvp_host_handshake_work);
+       hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper);
 
        return 0;
 }
@@ -593,7 +606,22 @@ void hv_kvp_onchannelcallback(void *context)
        struct icmsg_negotiate *negop = NULL;
        int util_fw_version;
        int kvp_srv_version;
+       static enum {NEGO_NOT_STARTED,
+                    NEGO_IN_PROGRESS,
+                    NEGO_FINISHED} host_negotiatied = NEGO_NOT_STARTED;
 
+       if (host_negotiatied == NEGO_NOT_STARTED &&
+           kvp_transaction.state < HVUTIL_READY) {
+               /*
+                * If userspace daemon is not connected and host is asking
+                * us to negotiate we need to delay to not lose messages.
+                * This is important for Failover IP setting.
+                */
+               host_negotiatied = NEGO_IN_PROGRESS;
+               schedule_delayed_work(&kvp_host_handshake_work,
+                                     HV_UTIL_NEGO_TIMEOUT * HZ);
+               return;
+       }
        if (kvp_transaction.state > HVUTIL_READY)
                return;
 
@@ -671,6 +699,8 @@ void hv_kvp_onchannelcallback(void *context)
                vmbus_sendpacket(channel, recv_buffer,
                                       recvlen, requestid,
                                       VM_PKT_DATA_INBAND, 0);
+
+               host_negotiatied = NEGO_FINISHED;
        }
 
 }
@@ -707,6 +737,7 @@ hv_kvp_init(struct hv_util_service *srv)
 void hv_kvp_deinit(void)
 {
        kvp_transaction.state = HVUTIL_DEVICE_DYING;
+       cancel_delayed_work_sync(&kvp_host_handshake_work);
        cancel_delayed_work_sync(&kvp_timeout_work);
        cancel_work_sync(&kvp_sendkey_work);
        hvutil_transport_destroy(hvt);
index 64950d81bde5cc02ff74f543afd4206c1cf282ed..2143b4bff446076e1fa14aaa9c54a57e7f2271ca 100644 (file)
  */
 #define HV_UTIL_TIMEOUT 30
 
+/*
+ * Timeout for guest-host handshake for services.
+ */
+#define HV_UTIL_NEGO_TIMEOUT 60
+
 /*
  * The below CPUID leaves are present if VersionAndFeatures.HypervisorPresent
  * is set by CPUID(HVCPUID_VERSION_FEATURES).