aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorK. Y. Srinivasan <kys@microsoft.com>2013-07-02 13:31:30 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-07-26 19:40:41 -0400
commit6741335bc7294548ac95cb1f4671991ff30da193 (patch)
treeb5930cd4e558ff76838966e8a23941352acf9a0e
parentd12e14692448e9f256028f53926a9bd3c3091e11 (diff)
Drivers: hv: util: Fix a bug in version negotiation code for util services
The current code picked the highest version advertised by the host. WS2012 R2 has implemented a protocol version for KVP that is not compatible with prior protocol versions of KVP. Fix the bug in the current code by explicitly specifying the protocol version that the guest can support. Signed-off-by: K. Y. Srinivasan <kys@microsoft.com> Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/hv/channel_mgmt.c75
-rw-r--r--drivers/hv/hv_kvp.c24
-rw-r--r--drivers/hv/hv_snapshot.c18
-rw-r--r--drivers/hv/hv_util.c21
-rw-r--r--include/linux/hyperv.h10
5 files changed, 109 insertions, 39 deletions
diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c
index 0df75908200e..12ec8c801b25 100644
--- a/drivers/hv/channel_mgmt.c
+++ b/drivers/hv/channel_mgmt.c
@@ -48,30 +48,39 @@ struct vmbus_channel_message_table_entry {
48 * @negop is of type &struct icmsg_negotiate. 48 * @negop is of type &struct icmsg_negotiate.
49 * Set up and fill in default negotiate response message. 49 * Set up and fill in default negotiate response message.
50 * 50 *
51 * The max_fw_version specifies the maximum framework version that 51 * The fw_version specifies the framework version that
52 * we can support and max _srv_version specifies the maximum service 52 * we can support and srv_version specifies the service
53 * version we can support. A special value MAX_SRV_VER can be 53 * version we can support.
54 * specified to indicate that we can handle the maximum version
55 * exposed by the host.
56 * 54 *
57 * Mainly used by Hyper-V drivers. 55 * Mainly used by Hyper-V drivers.
58 */ 56 */
59void vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, 57bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp,
60 struct icmsg_negotiate *negop, u8 *buf, 58 struct icmsg_negotiate *negop, u8 *buf,
61 int max_fw_version, int max_srv_version) 59 int fw_version, int srv_version)
62{ 60{
63 int icframe_vercnt; 61 int icframe_major, icframe_minor;
64 int icmsg_vercnt; 62 int icmsg_major, icmsg_minor;
63 int fw_major, fw_minor;
64 int srv_major, srv_minor;
65 int i; 65 int i;
66 bool found_match = false;
66 67
67 icmsghdrp->icmsgsize = 0x10; 68 icmsghdrp->icmsgsize = 0x10;
69 fw_major = (fw_version >> 16);
70 fw_minor = (fw_version & 0xFFFF);
71
72 srv_major = (srv_version >> 16);
73 srv_minor = (srv_version & 0xFFFF);
68 74
69 negop = (struct icmsg_negotiate *)&buf[ 75 negop = (struct icmsg_negotiate *)&buf[
70 sizeof(struct vmbuspipe_hdr) + 76 sizeof(struct vmbuspipe_hdr) +
71 sizeof(struct icmsg_hdr)]; 77 sizeof(struct icmsg_hdr)];
72 78
73 icframe_vercnt = negop->icframe_vercnt; 79 icframe_major = negop->icframe_vercnt;
74 icmsg_vercnt = negop->icmsg_vercnt; 80 icframe_minor = 0;
81
82 icmsg_major = negop->icmsg_vercnt;
83 icmsg_minor = 0;
75 84
76 /* 85 /*
77 * Select the framework version number we will 86 * Select the framework version number we will
@@ -79,26 +88,48 @@ void vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp,
79 */ 88 */
80 89
81 for (i = 0; i < negop->icframe_vercnt; i++) { 90 for (i = 0; i < negop->icframe_vercnt; i++) {
82 if (negop->icversion_data[i].major <= max_fw_version) 91 if ((negop->icversion_data[i].major == fw_major) &&
83 icframe_vercnt = negop->icversion_data[i].major; 92 (negop->icversion_data[i].minor == fw_minor)) {
93 icframe_major = negop->icversion_data[i].major;
94 icframe_minor = negop->icversion_data[i].minor;
95 found_match = true;
96 }
84 } 97 }
85 98
99 if (!found_match)
100 goto fw_error;
101
102 found_match = false;
103
86 for (i = negop->icframe_vercnt; 104 for (i = negop->icframe_vercnt;
87 (i < negop->icframe_vercnt + negop->icmsg_vercnt); i++) { 105 (i < negop->icframe_vercnt + negop->icmsg_vercnt); i++) {
88 if (negop->icversion_data[i].major <= max_srv_version) 106 if ((negop->icversion_data[i].major == srv_major) &&
89 icmsg_vercnt = negop->icversion_data[i].major; 107 (negop->icversion_data[i].minor == srv_minor)) {
108 icmsg_major = negop->icversion_data[i].major;
109 icmsg_minor = negop->icversion_data[i].minor;
110 found_match = true;
111 }
90 } 112 }
91 113
92 /* 114 /*
93 * Respond with the maximum framework and service 115 * Respond with the framework and service
94 * version numbers we can support. 116 * version numbers we can support.
95 */ 117 */
96 negop->icframe_vercnt = 1; 118
97 negop->icmsg_vercnt = 1; 119fw_error:
98 negop->icversion_data[0].major = icframe_vercnt; 120 if (!found_match) {
99 negop->icversion_data[0].minor = 0; 121 negop->icframe_vercnt = 0;
100 negop->icversion_data[1].major = icmsg_vercnt; 122 negop->icmsg_vercnt = 0;
101 negop->icversion_data[1].minor = 0; 123 } else {
124 negop->icframe_vercnt = 1;
125 negop->icmsg_vercnt = 1;
126 }
127
128 negop->icversion_data[0].major = icframe_major;
129 negop->icversion_data[0].minor = icframe_minor;
130 negop->icversion_data[1].major = icmsg_major;
131 negop->icversion_data[1].minor = icmsg_minor;
132 return found_match;
102} 133}
103 134
104EXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp); 135EXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp);
diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c
index ed50e9e83c61..53127209a404 100644
--- a/drivers/hv/hv_kvp.c
+++ b/drivers/hv/hv_kvp.c
@@ -29,6 +29,16 @@
29#include <linux/hyperv.h> 29#include <linux/hyperv.h>
30 30
31 31
32/*
33 * Pre win8 version numbers used in ws2008 and ws 2008 r2 (win7)
34 */
35#define WIN7_SRV_MAJOR 3
36#define WIN7_SRV_MINOR 0
37#define WIN7_SRV_MAJOR_MINOR (WIN7_SRV_MAJOR << 16 | WIN7_SRV_MINOR)
38
39#define WIN8_SRV_MAJOR 4
40#define WIN8_SRV_MINOR 0
41#define WIN8_SRV_MAJOR_MINOR (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR)
32 42
33/* 43/*
34 * Global state maintained for transaction that is being processed. 44 * Global state maintained for transaction that is being processed.
@@ -593,8 +603,19 @@ void hv_kvp_onchannelcallback(void *context)
593 sizeof(struct vmbuspipe_hdr)]; 603 sizeof(struct vmbuspipe_hdr)];
594 604
595 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { 605 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
606 /*
607 * We start with win8 version and if the host cannot
608 * support that we use the previous version.
609 */
610 if (vmbus_prep_negotiate_resp(icmsghdrp, negop,
611 recv_buffer, UTIL_FW_MAJOR_MINOR,
612 WIN8_SRV_MAJOR_MINOR))
613 goto done;
614
596 vmbus_prep_negotiate_resp(icmsghdrp, negop, 615 vmbus_prep_negotiate_resp(icmsghdrp, negop,
597 recv_buffer, MAX_SRV_VER, MAX_SRV_VER); 616 recv_buffer, UTIL_FW_MAJOR_MINOR,
617 WIN7_SRV_MAJOR_MINOR);
618
598 } else { 619 } else {
599 kvp_msg = (struct hv_kvp_msg *)&recv_buffer[ 620 kvp_msg = (struct hv_kvp_msg *)&recv_buffer[
600 sizeof(struct vmbuspipe_hdr) + 621 sizeof(struct vmbuspipe_hdr) +
@@ -626,6 +647,7 @@ void hv_kvp_onchannelcallback(void *context)
626 return; 647 return;
627 648
628 } 649 }
650done:
629 651
630 icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION 652 icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
631 | ICMSGHDRFLAG_RESPONSE; 653 | ICMSGHDRFLAG_RESPONSE;
diff --git a/drivers/hv/hv_snapshot.c b/drivers/hv/hv_snapshot.c
index 8ad5653ce447..e4572f3f2834 100644
--- a/drivers/hv/hv_snapshot.c
+++ b/drivers/hv/hv_snapshot.c
@@ -24,6 +24,10 @@
24#include <linux/workqueue.h> 24#include <linux/workqueue.h>
25#include <linux/hyperv.h> 25#include <linux/hyperv.h>
26 26
27#define VSS_MAJOR 5
28#define VSS_MINOR 0
29#define VSS_MAJOR_MINOR (VSS_MAJOR << 16 | VSS_MINOR)
30
27 31
28 32
29/* 33/*
@@ -186,18 +190,8 @@ void hv_vss_onchannelcallback(void *context)
186 190
187 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { 191 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
188 vmbus_prep_negotiate_resp(icmsghdrp, negop, 192 vmbus_prep_negotiate_resp(icmsghdrp, negop,
189 recv_buffer, MAX_SRV_VER, MAX_SRV_VER); 193 recv_buffer, UTIL_FW_MAJOR_MINOR,
190 /* 194 VSS_MAJOR_MINOR);
191 * We currently negotiate the highest number the
192 * host has presented. If this version is not
193 * atleast 5.0, reject.
194 */
195 negop = (struct icmsg_negotiate *)&recv_buffer[
196 sizeof(struct vmbuspipe_hdr) +
197 sizeof(struct icmsg_hdr)];
198
199 if (negop->icversion_data[1].major < 5)
200 negop->icframe_vercnt = 0;
201 } else { 195 } else {
202 vss_msg = (struct hv_vss_msg *)&recv_buffer[ 196 vss_msg = (struct hv_vss_msg *)&recv_buffer[
203 sizeof(struct vmbuspipe_hdr) + 197 sizeof(struct vmbuspipe_hdr) +
diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c
index 2f561c5dfe24..c16164d4a28b 100644
--- a/drivers/hv/hv_util.c
+++ b/drivers/hv/hv_util.c
@@ -28,6 +28,18 @@
28#include <linux/reboot.h> 28#include <linux/reboot.h>
29#include <linux/hyperv.h> 29#include <linux/hyperv.h>
30 30
31#define SHUTDOWN_MAJOR 3
32#define SHUTDOWN_MINOR 0
33#define SHUTDOWN_MAJOR_MINOR (SHUTDOWN_MAJOR << 16 | SHUTDOWN_MINOR)
34
35#define TIMESYNCH_MAJOR 3
36#define TIMESYNCH_MINOR 0
37#define TIMESYNCH_MAJOR_MINOR (TIMESYNCH_MAJOR << 16 | TIMESYNCH_MINOR)
38
39#define HEARTBEAT_MAJOR 3
40#define HEARTBEAT_MINOR 0
41#define HEARTBEAT_MAJOR_MINOR (HEARTBEAT_MAJOR << 16 | HEARTBEAT_MINOR)
42
31static void shutdown_onchannelcallback(void *context); 43static void shutdown_onchannelcallback(void *context);
32static struct hv_util_service util_shutdown = { 44static struct hv_util_service util_shutdown = {
33 .util_cb = shutdown_onchannelcallback, 45 .util_cb = shutdown_onchannelcallback,
@@ -87,7 +99,8 @@ static void shutdown_onchannelcallback(void *context)
87 99
88 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { 100 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
89 vmbus_prep_negotiate_resp(icmsghdrp, negop, 101 vmbus_prep_negotiate_resp(icmsghdrp, negop,
90 shut_txf_buf, MAX_SRV_VER, MAX_SRV_VER); 102 shut_txf_buf, UTIL_FW_MAJOR_MINOR,
103 SHUTDOWN_MAJOR_MINOR);
91 } else { 104 } else {
92 shutdown_msg = 105 shutdown_msg =
93 (struct shutdown_msg_data *)&shut_txf_buf[ 106 (struct shutdown_msg_data *)&shut_txf_buf[
@@ -213,7 +226,8 @@ static void timesync_onchannelcallback(void *context)
213 226
214 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { 227 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
215 vmbus_prep_negotiate_resp(icmsghdrp, NULL, time_txf_buf, 228 vmbus_prep_negotiate_resp(icmsghdrp, NULL, time_txf_buf,
216 MAX_SRV_VER, MAX_SRV_VER); 229 UTIL_FW_MAJOR_MINOR,
230 TIMESYNCH_MAJOR_MINOR);
217 } else { 231 } else {
218 timedatap = (struct ictimesync_data *)&time_txf_buf[ 232 timedatap = (struct ictimesync_data *)&time_txf_buf[
219 sizeof(struct vmbuspipe_hdr) + 233 sizeof(struct vmbuspipe_hdr) +
@@ -253,7 +267,8 @@ static void heartbeat_onchannelcallback(void *context)
253 267
254 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { 268 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
255 vmbus_prep_negotiate_resp(icmsghdrp, NULL, 269 vmbus_prep_negotiate_resp(icmsghdrp, NULL,
256 hbeat_txf_buf, MAX_SRV_VER, MAX_SRV_VER); 270 hbeat_txf_buf, UTIL_FW_MAJOR_MINOR,
271 HEARTBEAT_MAJOR_MINOR);
257 } else { 272 } else {
258 heartbeat_msg = 273 heartbeat_msg =
259 (struct heartbeat_msg_data *)&hbeat_txf_buf[ 274 (struct heartbeat_msg_data *)&hbeat_txf_buf[
diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
index fae8bac907ef..49949079cf47 100644
--- a/include/linux/hyperv.h
+++ b/include/linux/hyperv.h
@@ -27,6 +27,14 @@
27 27
28#include <linux/types.h> 28#include <linux/types.h>
29 29
30/*
31 * Framework version for util services.
32 */
33
34#define UTIL_FW_MAJOR 3
35#define UTIL_FW_MINOR 0
36#define UTIL_FW_MAJOR_MINOR (UTIL_FW_MAJOR << 16 | UTIL_FW_MINOR)
37
30 38
31/* 39/*
32 * Implementation of host controlled snapshot of the guest. 40 * Implementation of host controlled snapshot of the guest.
@@ -1494,7 +1502,7 @@ struct hyperv_service_callback {
1494}; 1502};
1495 1503
1496#define MAX_SRV_VER 0x7ffffff 1504#define MAX_SRV_VER 0x7ffffff
1497extern void vmbus_prep_negotiate_resp(struct icmsg_hdr *, 1505extern bool vmbus_prep_negotiate_resp(struct icmsg_hdr *,
1498 struct icmsg_negotiate *, u8 *, int, 1506 struct icmsg_negotiate *, u8 *, int,
1499 int); 1507 int);
1500 1508