fate#314663 hyper-v memory ballooning support
[opensuse:kernel-source.git] / patches.suse / suse-hv-fate314665-0007-Drivers-hv-Support-handling-multiple-VMBUS-versions.patch
1 Patch-mainline: submitted
2 From: <ohering@suse.de>
3 Date: Sat, 1 Dec 2012 06:46:38 -0800
4 Subject: [PATCH 07/28] Drivers: hv: Support handling multiple VMBUS versions
5
6 The current code hard coded the vmbus version independent of the host
7 it was running on. Add code to dynamically negotiate the most appropriate
8 version.
9
10 Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
11 Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
12 ---
13  drivers/hv/connection.c | 165 ++++++++++++++++++++++++++++++++----------------
14  include/linux/hyperv.h  |   6 --
15  2 files changed, 111 insertions(+), 60 deletions(-)
16
17 diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c
18 index d1019a7..2b56a3f 100644
19 --- a/drivers/hv/connection.c
20 +++ b/drivers/hv/connection.c
21 @@ -40,15 +40,111 @@ struct vmbus_connection vmbus_connection = {
22  };
23  
24  /*
25 + * VMBUS version is 32 bit entity broken up into
26 + * two 16 bit quantities: major_number. minor_number.
27 + *
28 + * 0 . 13 (Windows Server 2008)
29 + * 1 . 1  (Windows 7)
30 + * 2 . 4  (Windows 8)
31 + */
32 +
33 +#define VERSION_WS2008 ((0 << 16) | (13))
34 +#define VERSION_WIN7   ((1 << 16) | (1))
35 +#define VERSION_WIN8   ((2 << 16) | (4))
36 +
37 +#define VERSION_INVAL -1
38 +
39 +static __u32 vmbus_get_next_version(__u32 current_version)
40 +{
41 +       switch (current_version) {
42 +       case (VERSION_WIN7):
43 +               return VERSION_WS2008;
44 +
45 +       case (VERSION_WIN8):
46 +               return VERSION_WIN7;
47 +
48 +       case (VERSION_WS2008):
49 +       default:
50 +               return VERSION_INVAL;
51 +       }
52 +}
53 +
54 +static int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo,
55 +                                       __u32 version)
56 +{
57 +       int ret = 0;
58 +       struct vmbus_channel_initiate_contact *msg;
59 +       unsigned long flags;
60 +       int t;
61 +
62 +       init_completion(&msginfo->waitevent);
63 +
64 +       msg = (struct vmbus_channel_initiate_contact *)msginfo->msg;
65 +
66 +       msg->header.msgtype = CHANNELMSG_INITIATE_CONTACT;
67 +       msg->vmbus_version_requested = version;
68 +       msg->interrupt_page = virt_to_phys(vmbus_connection.int_page);
69 +       msg->monitor_page1 = virt_to_phys(vmbus_connection.monitor_pages);
70 +       msg->monitor_page2 = virt_to_phys(
71 +                       (void *)((unsigned long)vmbus_connection.monitor_pages +
72 +                                PAGE_SIZE));
73 +
74 +       /*
75 +        * Add to list before we send the request since we may
76 +        * receive the response before returning from this routine
77 +        */
78 +       spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
79 +       list_add_tail(&msginfo->msglistentry,
80 +                     &vmbus_connection.chn_msg_list);
81 +
82 +       spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
83 +
84 +       ret = vmbus_post_msg(msg,
85 +                              sizeof(struct vmbus_channel_initiate_contact));
86 +       if (ret != 0) {
87 +               spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
88 +               list_del(&msginfo->msglistentry);
89 +               spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock,
90 +                                       flags);
91 +               return ret;
92 +       }
93 +
94 +       /* Wait for the connection response */
95 +       t =  wait_for_completion_timeout(&msginfo->waitevent, 5*HZ);
96 +       if (t == 0) {
97 +               spin_lock_irqsave(&vmbus_connection.channelmsg_lock,
98 +                               flags);
99 +               list_del(&msginfo->msglistentry);
100 +               spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock,
101 +                                       flags);
102 +               return -ETIMEDOUT;
103 +       }
104 +
105 +       spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
106 +       list_del(&msginfo->msglistentry);
107 +       spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
108 +
109 +       /* Check if successful */
110 +       if (msginfo->response.version_response.version_supported) {
111 +               vmbus_connection.conn_state = CONNECTED;
112 +       } else {
113 +               pr_err("Unable to connect, "
114 +                       "Version %d not supported by Hyper-V\n",
115 +                       version);
116 +               return -ECONNREFUSED;
117 +       }
118 +
119 +       return ret;
120 +}
121 +
122 +/*
123   * vmbus_connect - Sends a connect request on the partition service connection
124   */
125  int vmbus_connect(void)
126  {
127         int ret = 0;
128 -       int t;
129         struct vmbus_channel_msginfo *msginfo = NULL;
130 -       struct vmbus_channel_initiate_contact *msg;
131 -       unsigned long flags;
132 +       __u32 version;
133  
134         /* Initialize the vmbus connection */
135         vmbus_connection.conn_state = CONNECTING;
136 @@ -99,64 +195,25 @@ int vmbus_connect(void)
137                 goto cleanup;
138         }
139  
140 -       init_completion(&msginfo->waitevent);
141 -
142 -       msg = (struct vmbus_channel_initiate_contact *)msginfo->msg;
143 -
144 -       msg->header.msgtype = CHANNELMSG_INITIATE_CONTACT;
145 -       msg->vmbus_version_requested = VMBUS_REVISION_NUMBER;
146 -       msg->interrupt_page = virt_to_phys(vmbus_connection.int_page);
147 -       msg->monitor_page1 = virt_to_phys(vmbus_connection.monitor_pages);
148 -       msg->monitor_page2 = virt_to_phys(
149 -                       (void *)((unsigned long)vmbus_connection.monitor_pages +
150 -                                PAGE_SIZE));
151 -
152         /*
153 -        * Add to list before we send the request since we may
154 -        * receive the response before returning from this routine
155 +        * Negotiate a compatible VMBUS version number with the
156 +        * host. We start with the highest number we can support
157 +        * and work our way down until we negotiate a compatible
158 +        * version.
159          */
160 -       spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
161 -       list_add_tail(&msginfo->msglistentry,
162 -                     &vmbus_connection.chn_msg_list);
163 -
164 -       spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
165  
166 -       ret = vmbus_post_msg(msg,
167 -                              sizeof(struct vmbus_channel_initiate_contact));
168 -       if (ret != 0) {
169 -               spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
170 -               list_del(&msginfo->msglistentry);
171 -               spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock,
172 -                                       flags);
173 -               goto cleanup;
174 -       }
175 +       version = VERSION_WS2008;
176  
177 -       /* Wait for the connection response */
178 -       t =  wait_for_completion_timeout(&msginfo->waitevent, 5*HZ);
179 -       if (t == 0) {
180 -               spin_lock_irqsave(&vmbus_connection.channelmsg_lock,
181 -                               flags);
182 -               list_del(&msginfo->msglistentry);
183 -               spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock,
184 -                                       flags);
185 -               ret = -ETIMEDOUT;
186 -               goto cleanup;
187 -       }
188 +       do {
189 +               ret = vmbus_negotiate_version(msginfo, version);
190 +               if (ret == 0)
191 +                       break;
192  
193 -       spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
194 -       list_del(&msginfo->msglistentry);
195 -       spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
196 +               version = vmbus_get_next_version(version);
197 +       } while (version != VERSION_INVAL);
198  
199 -       /* Check if successful */
200 -       if (msginfo->response.version_response.version_supported) {
201 -               vmbus_connection.conn_state = CONNECTED;
202 -       } else {
203 -               pr_err("Unable to connect, "
204 -                       "Version %d not supported by Hyper-V\n",
205 -                       VMBUS_REVISION_NUMBER);
206 -               ret = -ECONNREFUSED;
207 +       if (version == VERSION_INVAL)
208                 goto cleanup;
209 -       }
210  
211         kfree(msginfo);
212         return 0;
213 diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
214 index 1ffe84d..b097bf9 100644
215 --- a/include/linux/hyperv.h
216 +++ b/include/linux/hyperv.h
217 @@ -406,12 +406,6 @@ hv_get_ringbuffer_availbytes(struct hv_ring_buffer_info *rbi,
218  #define HV_DRV_VERSION           "3.1"
219  
220  
221 -/*
222 - * A revision number of vmbus that is used for ensuring both ends on a
223 - * partition are using compatible versions.
224 - */
225 -#define VMBUS_REVISION_NUMBER          13
226 -
227  /* Make maximum size of pipe payload of 16K */
228  #define MAX_PIPE_DATA_PAYLOAD          (sizeof(u8) * 16384)
229  
230 -- 
231 1.8.0.1
232