Merge branch 'SLE11-SP2' into SLE11-SP3
[opensuse:kernel-source.git] / patches.suse / suse-hv-Drivers-hv-util-Fix-a-bug-in-version-negotiation-cod.patch
1 From: "K. Y. Srinivasan" <kys@microsoft.com>
2 Date: Tue, 2 Jul 2013 10:31:30 -0700
3 Subject: Drivers: hv: util: Fix a bug in version negotiation code for util services
4 Patch-mainline: submitted (lkml 2013-07-02)
5 References: bnc#828714
6
7 The current code picked the highest version advertised by the host. WS2012 R2
8 has implemented a protocol version for KVP that is not compatible with prior
9 protocol versions of KVP. Fix the bug in the current code by explicitly specifying
10 the protocol version that the guest can support.
11
12 Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
13 Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
14 Acked-by: <ohering@suse.de>
15 ---
16  drivers/hv/channel_mgmt.c | 75 +++++++++++++++++++++++++++++++++--------------
17  drivers/hv/hv_kvp.c       | 24 ++++++++++++++-
18  drivers/hv/hv_snapshot.c  | 18 ++++--------
19  drivers/hv/hv_util.c      | 21 +++++++++++--
20  include/linux/hyperv.h    | 10 ++++++-
21  5 files changed, 109 insertions(+), 39 deletions(-)
22
23 diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c
24 index 21ef689..a8fb7de 100644
25 --- a/drivers/hv/channel_mgmt.c
26 +++ b/drivers/hv/channel_mgmt.c
27 @@ -48,30 +48,39 @@ struct vmbus_channel_message_table_entry {
28   * @negop is of type &struct icmsg_negotiate.
29   * Set up and fill in default negotiate response message.
30   *
31 - * The max_fw_version specifies the maximum framework version that
32 - * we can support and max _srv_version specifies the maximum service
33 - * version we can support. A special value MAX_SRV_VER can be
34 - * specified to indicate that we can handle the maximum version
35 - * exposed by the host.
36 + * The fw_version specifies the  framework version that
37 + * we can support and srv_version specifies the service
38 + * version we can support.
39   *
40   * Mainly used by Hyper-V drivers.
41   */
42 -void vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp,
43 +bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp,
44                                 struct icmsg_negotiate *negop, u8 *buf,
45 -                               int max_fw_version, int max_srv_version)
46 +                               int fw_version, int srv_version)
47  {
48 -       int icframe_vercnt;
49 -       int icmsg_vercnt;
50 +       int icframe_major, icframe_minor;
51 +       int icmsg_major, icmsg_minor;
52 +       int fw_major, fw_minor;
53 +       int srv_major, srv_minor;
54         int i;
55 +       bool found_match = false;
56  
57         icmsghdrp->icmsgsize = 0x10;
58 +       fw_major = (fw_version >> 16);
59 +       fw_minor = (fw_version & 0xFFFF);
60 +
61 +       srv_major = (srv_version >> 16);
62 +       srv_minor = (srv_version & 0xFFFF);
63  
64         negop = (struct icmsg_negotiate *)&buf[
65                 sizeof(struct vmbuspipe_hdr) +
66                 sizeof(struct icmsg_hdr)];
67  
68 -       icframe_vercnt = negop->icframe_vercnt;
69 -       icmsg_vercnt = negop->icmsg_vercnt;
70 +       icframe_major = negop->icframe_vercnt;
71 +       icframe_minor = 0;
72 +
73 +       icmsg_major = negop->icmsg_vercnt;
74 +       icmsg_minor = 0;
75  
76         /*
77          * Select the framework version number we will
78 @@ -79,26 +88,48 @@ void vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp,
79          */
80  
81         for (i = 0; i < negop->icframe_vercnt; i++) {
82 -               if (negop->icversion_data[i].major <= max_fw_version)
83 -                       icframe_vercnt = negop->icversion_data[i].major;
84 +               if ((negop->icversion_data[i].major == fw_major) &&
85 +                  (negop->icversion_data[i].minor == fw_minor)) {
86 +                       icframe_major = negop->icversion_data[i].major;
87 +                       icframe_minor = negop->icversion_data[i].minor;
88 +                       found_match = true;
89 +               }
90         }
91  
92 +       if (!found_match)
93 +               goto fw_error;
94 +
95 +       found_match = false;
96 +
97         for (i = negop->icframe_vercnt;
98                  (i < negop->icframe_vercnt + negop->icmsg_vercnt); i++) {
99 -               if (negop->icversion_data[i].major <= max_srv_version)
100 -                       icmsg_vercnt = negop->icversion_data[i].major;
101 +               if ((negop->icversion_data[i].major == srv_major) &&
102 +                  (negop->icversion_data[i].minor == srv_minor)) {
103 +                       icmsg_major = negop->icversion_data[i].major;
104 +                       icmsg_minor = negop->icversion_data[i].minor;
105 +                       found_match = true;
106 +               }
107         }
108  
109         /*
110 -        * Respond with the maximum framework and service
111 +        * Respond with the framework and service
112          * version numbers we can support.
113          */
114 -       negop->icframe_vercnt = 1;
115 -       negop->icmsg_vercnt = 1;
116 -       negop->icversion_data[0].major = icframe_vercnt;
117 -       negop->icversion_data[0].minor = 0;
118 -       negop->icversion_data[1].major = icmsg_vercnt;
119 -       negop->icversion_data[1].minor = 0;
120 +
121 +fw_error:
122 +       if (!found_match) {
123 +               negop->icframe_vercnt = 0;
124 +               negop->icmsg_vercnt = 0;
125 +       } else {
126 +               negop->icframe_vercnt = 1;
127 +               negop->icmsg_vercnt = 1;
128 +       }
129 +
130 +       negop->icversion_data[0].major = icframe_major;
131 +       negop->icversion_data[0].minor = icframe_minor;
132 +       negop->icversion_data[1].major = icmsg_major;
133 +       negop->icversion_data[1].minor = icmsg_minor;
134 +       return found_match;
135  }
136  
137  EXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp);
138 diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c
139 index ed50e9e..5312720 100644
140 --- a/drivers/hv/hv_kvp.c
141 +++ b/drivers/hv/hv_kvp.c
142 @@ -29,6 +29,16 @@
143  #include <linux/hyperv.h>
144  
145  
146 +/*
147 + * Pre win8 version numbers used in ws2008 and ws 2008 r2 (win7)
148 + */
149 +#define WIN7_SRV_MAJOR   3
150 +#define WIN7_SRV_MINOR   0
151 +#define WIN7_SRV_MAJOR_MINOR     (WIN7_SRV_MAJOR << 16 | WIN7_SRV_MINOR)
152 +
153 +#define WIN8_SRV_MAJOR   4
154 +#define WIN8_SRV_MINOR   0
155 +#define WIN8_SRV_MAJOR_MINOR     (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR)
156  
157  /*
158   * Global state maintained for transaction that is being processed.
159 @@ -593,8 +603,19 @@ void hv_kvp_onchannelcallback(void *context)
160                         sizeof(struct vmbuspipe_hdr)];
161  
162                 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
163 +                       /*
164 +                        * We start with win8 version and if the host cannot
165 +                        * support that we use the previous version.
166 +                        */
167 +                       if (vmbus_prep_negotiate_resp(icmsghdrp, negop,
168 +                                recv_buffer, UTIL_FW_MAJOR_MINOR,
169 +                                WIN8_SRV_MAJOR_MINOR))
170 +                               goto done;
171 +
172                         vmbus_prep_negotiate_resp(icmsghdrp, negop,
173 -                                recv_buffer, MAX_SRV_VER, MAX_SRV_VER);
174 +                                recv_buffer, UTIL_FW_MAJOR_MINOR,
175 +                                WIN7_SRV_MAJOR_MINOR);
176 +
177                 } else {
178                         kvp_msg = (struct hv_kvp_msg *)&recv_buffer[
179                                 sizeof(struct vmbuspipe_hdr) +
180 @@ -626,6 +647,7 @@ void hv_kvp_onchannelcallback(void *context)
181                         return;
182  
183                 }
184 +done:
185  
186                 icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
187                         | ICMSGHDRFLAG_RESPONSE;
188 diff --git a/drivers/hv/hv_snapshot.c b/drivers/hv/hv_snapshot.c
189 index 8ad5653..e4572f3 100644
190 --- a/drivers/hv/hv_snapshot.c
191 +++ b/drivers/hv/hv_snapshot.c
192 @@ -24,6 +24,10 @@
193  #include <linux/workqueue.h>
194  #include <linux/hyperv.h>
195  
196 +#define VSS_MAJOR  5
197 +#define VSS_MINOR  0
198 +#define VSS_MAJOR_MINOR    (VSS_MAJOR << 16 | VSS_MINOR)
199 +
200  
201  
202  /*
203 @@ -186,18 +190,8 @@ void hv_vss_onchannelcallback(void *context)
204  
205                 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
206                         vmbus_prep_negotiate_resp(icmsghdrp, negop,
207 -                                recv_buffer, MAX_SRV_VER, MAX_SRV_VER);
208 -                       /*
209 -                        * We currently negotiate the highest number the
210 -                        * host has presented. If this version is not
211 -                        * atleast 5.0, reject.
212 -                        */
213 -                       negop = (struct icmsg_negotiate *)&recv_buffer[
214 -                               sizeof(struct vmbuspipe_hdr) +
215 -                               sizeof(struct icmsg_hdr)];
216 -
217 -                       if (negop->icversion_data[1].major < 5)
218 -                               negop->icframe_vercnt = 0;
219 +                                recv_buffer, UTIL_FW_MAJOR_MINOR,
220 +                                VSS_MAJOR_MINOR);
221                 } else {
222                         vss_msg = (struct hv_vss_msg *)&recv_buffer[
223                                 sizeof(struct vmbuspipe_hdr) +
224 diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c
225 index 2f561c5..c16164d 100644
226 --- a/drivers/hv/hv_util.c
227 +++ b/drivers/hv/hv_util.c
228 @@ -28,6 +28,18 @@
229  #include <linux/reboot.h>
230  #include <linux/hyperv.h>
231  
232 +#define SHUTDOWN_MAJOR 3
233 +#define SHUTDOWN_MINOR  0
234 +#define SHUTDOWN_MAJOR_MINOR   (SHUTDOWN_MAJOR << 16 | SHUTDOWN_MINOR)
235 +
236 +#define TIMESYNCH_MAJOR        3
237 +#define TIMESYNCH_MINOR 0
238 +#define TIMESYNCH_MAJOR_MINOR  (TIMESYNCH_MAJOR << 16 | TIMESYNCH_MINOR)
239 +
240 +#define HEARTBEAT_MAJOR        3
241 +#define HEARTBEAT_MINOR 0
242 +#define HEARTBEAT_MAJOR_MINOR  (HEARTBEAT_MAJOR << 16 | HEARTBEAT_MINOR)
243 +
244  static void shutdown_onchannelcallback(void *context);
245  static struct hv_util_service util_shutdown = {
246         .util_cb = shutdown_onchannelcallback,
247 @@ -87,7 +99,8 @@ static void shutdown_onchannelcallback(void *context)
248  
249                 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
250                         vmbus_prep_negotiate_resp(icmsghdrp, negop,
251 -                                       shut_txf_buf, MAX_SRV_VER, MAX_SRV_VER);
252 +                                       shut_txf_buf, UTIL_FW_MAJOR_MINOR,
253 +                                       SHUTDOWN_MAJOR_MINOR);
254                 } else {
255                         shutdown_msg =
256                                 (struct shutdown_msg_data *)&shut_txf_buf[
257 @@ -213,7 +226,8 @@ static void timesync_onchannelcallback(void *context)
258  
259                 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
260                         vmbus_prep_negotiate_resp(icmsghdrp, NULL, time_txf_buf,
261 -                                               MAX_SRV_VER, MAX_SRV_VER);
262 +                                               UTIL_FW_MAJOR_MINOR,
263 +                                               TIMESYNCH_MAJOR_MINOR);
264                 } else {
265                         timedatap = (struct ictimesync_data *)&time_txf_buf[
266                                 sizeof(struct vmbuspipe_hdr) +
267 @@ -253,7 +267,8 @@ static void heartbeat_onchannelcallback(void *context)
268  
269                 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
270                         vmbus_prep_negotiate_resp(icmsghdrp, NULL,
271 -                               hbeat_txf_buf, MAX_SRV_VER, MAX_SRV_VER);
272 +                               hbeat_txf_buf, UTIL_FW_MAJOR_MINOR,
273 +                               HEARTBEAT_MAJOR_MINOR);
274                 } else {
275                         heartbeat_msg =
276                                 (struct heartbeat_msg_data *)&hbeat_txf_buf[
277 diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
278 index c255984..2254f4b 100644
279 --- a/include/linux/hyperv.h
280 +++ b/include/linux/hyperv.h
281 @@ -27,6 +27,14 @@
282  
283  #include <linux/types.h>
284  
285 +/*
286 + * Framework version for util services.
287 + */
288 +
289 +#define UTIL_FW_MAJOR  3
290 +#define UTIL_FW_MINOR  0
291 +#define UTIL_FW_MAJOR_MINOR     (UTIL_FW_MAJOR << 16 | UTIL_FW_MINOR)
292 +
293  
294  /*
295   * Implementation of host controlled snapshot of the guest.
296 @@ -1424,7 +1432,7 @@ struct hyperv_service_callback {
297  };
298  
299  #define MAX_SRV_VER    0x7ffffff
300 -extern void vmbus_prep_negotiate_resp(struct icmsg_hdr *,
301 +extern bool vmbus_prep_negotiate_resp(struct icmsg_hdr *,
302                                         struct icmsg_negotiate *, u8 *, int,
303                                         int);
304