connection.c revision 37f7278b81a9ab9b76cf4d716ba420444ca4a616
1/* 2 * 3 * Copyright (c) 2009, Microsoft Corporation. 4 * 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms and conditions of the GNU General Public License, 7 * version 2, as published by the Free Software Foundation. 8 * 9 * This program is distributed in the hope it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 * more details. 13 * 14 * You should have received a copy of the GNU General Public License along with 15 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 16 * Place - Suite 330, Boston, MA 02111-1307 USA. 17 * 18 * Authors: 19 * Haiyang Zhang <haiyangz@microsoft.com> 20 * Hank Janssen <hjanssen@microsoft.com> 21 * 22 */ 23#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 24 25#include <linux/kernel.h> 26#include <linux/sched.h> 27#include <linux/wait.h> 28#include <linux/delay.h> 29#include <linux/mm.h> 30#include <linux/slab.h> 31#include <linux/vmalloc.h> 32#include <linux/hyperv.h> 33#include <linux/export.h> 34#include <asm/hyperv.h> 35#include "hyperv_vmbus.h" 36 37 38struct vmbus_connection vmbus_connection = { 39 .conn_state = DISCONNECTED, 40 .next_gpadl_handle = ATOMIC_INIT(0xE1E10), 41}; 42 43/* 44 * VMBUS version is 32 bit entity broken up into 45 * two 16 bit quantities: major_number. minor_number. 46 * 47 * 0 . 13 (Windows Server 2008) 48 * 1 . 1 (Windows 7) 49 * 2 . 4 (Windows 8) 50 */ 51 52#define VERSION_WS2008 ((0 << 16) | (13)) 53#define VERSION_WIN7 ((1 << 16) | (1)) 54#define VERSION_WIN8 ((2 << 16) | (4)) 55 56#define VERSION_INVAL -1 57 58/* 59 * Negotiated protocol version with the host. 60 */ 61__u32 vmbus_proto_version; 62EXPORT_SYMBOL_GPL(vmbus_proto_version); 63 64static __u32 vmbus_get_next_version(__u32 current_version) 65{ 66 switch (current_version) { 67 case (VERSION_WIN7): 68 return VERSION_WS2008; 69 70 case (VERSION_WIN8): 71 return VERSION_WIN7; 72 73 case (VERSION_WS2008): 74 default: 75 return VERSION_INVAL; 76 } 77} 78 79static int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, 80 __u32 version) 81{ 82 int ret = 0; 83 struct vmbus_channel_initiate_contact *msg; 84 unsigned long flags; 85 int t; 86 87 init_completion(&msginfo->waitevent); 88 89 msg = (struct vmbus_channel_initiate_contact *)msginfo->msg; 90 91 msg->header.msgtype = CHANNELMSG_INITIATE_CONTACT; 92 msg->vmbus_version_requested = version; 93 msg->interrupt_page = virt_to_phys(vmbus_connection.int_page); 94 msg->monitor_page1 = virt_to_phys(vmbus_connection.monitor_pages); 95 msg->monitor_page2 = virt_to_phys( 96 (void *)((unsigned long)vmbus_connection.monitor_pages + 97 PAGE_SIZE)); 98 99 /* 100 * Add to list before we send the request since we may 101 * receive the response before returning from this routine 102 */ 103 spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); 104 list_add_tail(&msginfo->msglistentry, 105 &vmbus_connection.chn_msg_list); 106 107 spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); 108 109 ret = vmbus_post_msg(msg, 110 sizeof(struct vmbus_channel_initiate_contact)); 111 if (ret != 0) { 112 spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); 113 list_del(&msginfo->msglistentry); 114 spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, 115 flags); 116 return ret; 117 } 118 119 /* Wait for the connection response */ 120 t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ); 121 if (t == 0) { 122 spin_lock_irqsave(&vmbus_connection.channelmsg_lock, 123 flags); 124 list_del(&msginfo->msglistentry); 125 spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, 126 flags); 127 return -ETIMEDOUT; 128 } 129 130 spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); 131 list_del(&msginfo->msglistentry); 132 spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); 133 134 /* Check if successful */ 135 if (msginfo->response.version_response.version_supported) { 136 vmbus_connection.conn_state = CONNECTED; 137 } else { 138 pr_err("Unable to connect, " 139 "Version %d not supported by Hyper-V\n", 140 version); 141 return -ECONNREFUSED; 142 } 143 144 return ret; 145} 146 147/* 148 * vmbus_connect - Sends a connect request on the partition service connection 149 */ 150int vmbus_connect(void) 151{ 152 int ret = 0; 153 struct vmbus_channel_msginfo *msginfo = NULL; 154 __u32 version; 155 156 /* Initialize the vmbus connection */ 157 vmbus_connection.conn_state = CONNECTING; 158 vmbus_connection.work_queue = create_workqueue("hv_vmbus_con"); 159 if (!vmbus_connection.work_queue) { 160 ret = -ENOMEM; 161 goto cleanup; 162 } 163 164 INIT_LIST_HEAD(&vmbus_connection.chn_msg_list); 165 spin_lock_init(&vmbus_connection.channelmsg_lock); 166 167 INIT_LIST_HEAD(&vmbus_connection.chn_list); 168 spin_lock_init(&vmbus_connection.channel_lock); 169 170 /* 171 * Setup the vmbus event connection for channel interrupt 172 * abstraction stuff 173 */ 174 vmbus_connection.int_page = 175 (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, 0); 176 if (vmbus_connection.int_page == NULL) { 177 ret = -ENOMEM; 178 goto cleanup; 179 } 180 181 vmbus_connection.recv_int_page = vmbus_connection.int_page; 182 vmbus_connection.send_int_page = 183 (void *)((unsigned long)vmbus_connection.int_page + 184 (PAGE_SIZE >> 1)); 185 186 /* 187 * Setup the monitor notification facility. The 1st page for 188 * parent->child and the 2nd page for child->parent 189 */ 190 vmbus_connection.monitor_pages = 191 (void *)__get_free_pages((GFP_KERNEL|__GFP_ZERO), 1); 192 if (vmbus_connection.monitor_pages == NULL) { 193 ret = -ENOMEM; 194 goto cleanup; 195 } 196 197 msginfo = kzalloc(sizeof(*msginfo) + 198 sizeof(struct vmbus_channel_initiate_contact), 199 GFP_KERNEL); 200 if (msginfo == NULL) { 201 ret = -ENOMEM; 202 goto cleanup; 203 } 204 205 /* 206 * Negotiate a compatible VMBUS version number with the 207 * host. We start with the highest number we can support 208 * and work our way down until we negotiate a compatible 209 * version. 210 */ 211 212 version = VERSION_WS2008; 213 214 do { 215 ret = vmbus_negotiate_version(msginfo, version); 216 if (ret == 0) 217 break; 218 219 version = vmbus_get_next_version(version); 220 } while (version != VERSION_INVAL); 221 222 if (version == VERSION_INVAL) 223 goto cleanup; 224 225 vmbus_proto_version = version; 226 pr_info("Negotiated host information %d\n", version); 227 kfree(msginfo); 228 return 0; 229 230cleanup: 231 vmbus_connection.conn_state = DISCONNECTED; 232 233 if (vmbus_connection.work_queue) 234 destroy_workqueue(vmbus_connection.work_queue); 235 236 if (vmbus_connection.int_page) { 237 free_pages((unsigned long)vmbus_connection.int_page, 0); 238 vmbus_connection.int_page = NULL; 239 } 240 241 if (vmbus_connection.monitor_pages) { 242 free_pages((unsigned long)vmbus_connection.monitor_pages, 1); 243 vmbus_connection.monitor_pages = NULL; 244 } 245 246 kfree(msginfo); 247 248 return ret; 249} 250 251 252/* 253 * relid2channel - Get the channel object given its 254 * child relative id (ie channel id) 255 */ 256struct vmbus_channel *relid2channel(u32 relid) 257{ 258 struct vmbus_channel *channel; 259 struct vmbus_channel *found_channel = NULL; 260 unsigned long flags; 261 262 spin_lock_irqsave(&vmbus_connection.channel_lock, flags); 263 list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { 264 if (channel->offermsg.child_relid == relid) { 265 found_channel = channel; 266 break; 267 } 268 } 269 spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); 270 271 return found_channel; 272} 273 274/* 275 * process_chn_event - Process a channel event notification 276 */ 277static void process_chn_event(u32 relid) 278{ 279 struct vmbus_channel *channel; 280 unsigned long flags; 281 void *arg; 282 bool read_state; 283 u32 bytes_to_read; 284 285 /* 286 * Find the channel based on this relid and invokes the 287 * channel callback to process the event 288 */ 289 channel = relid2channel(relid); 290 291 if (!channel) { 292 pr_err("channel not found for relid - %u\n", relid); 293 return; 294 } 295 296 /* 297 * A channel once created is persistent even when there 298 * is no driver handling the device. An unloading driver 299 * sets the onchannel_callback to NULL under the 300 * protection of the channel inbound_lock. Thus, checking 301 * and invoking the driver specific callback takes care of 302 * orderly unloading of the driver. 303 */ 304 305 spin_lock_irqsave(&channel->inbound_lock, flags); 306 if (channel->onchannel_callback != NULL) { 307 arg = channel->channel_callback_context; 308 read_state = channel->batched_reading; 309 /* 310 * This callback reads the messages sent by the host. 311 * We can optimize host to guest signaling by ensuring: 312 * 1. While reading the channel, we disable interrupts from 313 * host. 314 * 2. Ensure that we process all posted messages from the host 315 * before returning from this callback. 316 * 3. Once we return, enable signaling from the host. Once this 317 * state is set we check to see if additional packets are 318 * available to read. In this case we repeat the process. 319 */ 320 321 do { 322 hv_begin_read(&channel->inbound); 323 channel->onchannel_callback(arg); 324 bytes_to_read = hv_end_read(&channel->inbound); 325 } while (read_state && (bytes_to_read != 0)); 326 } else { 327 pr_err("no channel callback for relid - %u\n", relid); 328 } 329 330 spin_unlock_irqrestore(&channel->inbound_lock, flags); 331} 332 333/* 334 * vmbus_on_event - Handler for events 335 */ 336void vmbus_on_event(unsigned long data) 337{ 338 u32 dword; 339 u32 maxdword = MAX_NUM_CHANNELS_SUPPORTED >> 5; 340 int bit; 341 u32 relid; 342 u32 *recv_int_page = vmbus_connection.recv_int_page; 343 344 /* Check events */ 345 if (!recv_int_page) 346 return; 347 for (dword = 0; dword < maxdword; dword++) { 348 if (!recv_int_page[dword]) 349 continue; 350 for (bit = 0; bit < 32; bit++) { 351 if (sync_test_and_clear_bit(bit, 352 (unsigned long *)&recv_int_page[dword])) { 353 relid = (dword << 5) + bit; 354 355 if (relid == 0) 356 /* 357 * Special case - vmbus 358 * channel protocol msg 359 */ 360 continue; 361 362 process_chn_event(relid); 363 } 364 } 365 } 366} 367 368/* 369 * vmbus_post_msg - Send a msg on the vmbus's message connection 370 */ 371int vmbus_post_msg(void *buffer, size_t buflen) 372{ 373 union hv_connection_id conn_id; 374 int ret = 0; 375 int retries = 0; 376 377 conn_id.asu32 = 0; 378 conn_id.u.id = VMBUS_MESSAGE_CONNECTION_ID; 379 380 /* 381 * hv_post_message() can have transient failures because of 382 * insufficient resources. Retry the operation a couple of 383 * times before giving up. 384 */ 385 while (retries < 3) { 386 ret = hv_post_message(conn_id, 1, buffer, buflen); 387 if (ret != HV_STATUS_INSUFFICIENT_BUFFERS) 388 return ret; 389 retries++; 390 msleep(100); 391 } 392 return ret; 393} 394 395/* 396 * vmbus_set_event - Send an event notification to the parent 397 */ 398int vmbus_set_event(u32 child_relid) 399{ 400 /* Each u32 represents 32 channels */ 401 sync_set_bit(child_relid & 31, 402 (unsigned long *)vmbus_connection.send_int_page + 403 (child_relid >> 5)); 404 405 return hv_signal_event(); 406} 407