Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
channel_mgmt.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2009, Microsoft Corporation.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms and conditions of the GNU General Public License,
6  * version 2, as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11  * more details.
12  *
13  * You should have received a copy of the GNU General Public License along with
14  * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
15  * Place - Suite 330, Boston, MA 02111-1307 USA.
16  *
17  * Authors:
18  * Haiyang Zhang <[email protected]>
19  * Hank Janssen <[email protected]>
20  */
21 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
22 
23 #include <linux/kernel.h>
24 #include <linux/sched.h>
25 #include <linux/wait.h>
26 #include <linux/mm.h>
27 #include <linux/slab.h>
28 #include <linux/list.h>
29 #include <linux/module.h>
30 #include <linux/completion.h>
31 #include <linux/hyperv.h>
32 
33 #include "hyperv_vmbus.h"
34 
36  enum vmbus_channel_message_type message_type;
37  void (*message_handler)(struct vmbus_channel_message_header *msg);
38 };
39 
40 
59 void vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp,
60  struct icmsg_negotiate *negop, u8 *buf,
61  int max_fw_version, int max_srv_version)
62 {
63  int icframe_vercnt;
64  int icmsg_vercnt;
65  int i;
66 
67  icmsghdrp->icmsgsize = 0x10;
68 
69  negop = (struct icmsg_negotiate *)&buf[
70  sizeof(struct vmbuspipe_hdr) +
71  sizeof(struct icmsg_hdr)];
72 
73  icframe_vercnt = negop->icframe_vercnt;
74  icmsg_vercnt = negop->icmsg_vercnt;
75 
76  /*
77  * Select the framework version number we will
78  * support.
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  }
85 
86  for (i = negop->icframe_vercnt;
87  (i < negop->icframe_vercnt + negop->icmsg_vercnt); i++) {
88  if (negop->icversion_data[i].major <= max_srv_version)
89  icmsg_vercnt = negop->icversion_data[i].major;
90  }
91 
92  /*
93  * Respond with the maximum framework and service
94  * version numbers we can support.
95  */
96  negop->icframe_vercnt = 1;
97  negop->icmsg_vercnt = 1;
98  negop->icversion_data[0].major = icframe_vercnt;
99  negop->icversion_data[0].minor = 0;
100  negop->icversion_data[1].major = icmsg_vercnt;
101  negop->icversion_data[1].minor = 0;
102 }
103 
105 
106 /*
107  * alloc_channel - Allocate and initialize a vmbus channel object
108  */
109 static struct vmbus_channel *alloc_channel(void)
110 {
111  struct vmbus_channel *channel;
112 
113  channel = kzalloc(sizeof(*channel), GFP_ATOMIC);
114  if (!channel)
115  return NULL;
116 
117  spin_lock_init(&channel->inbound_lock);
118 
119  channel->controlwq = create_workqueue("hv_vmbus_ctl");
120  if (!channel->controlwq) {
121  kfree(channel);
122  return NULL;
123  }
124 
125  return channel;
126 }
127 
128 /*
129  * release_hannel - Release the vmbus channel object itself
130  */
131 static void release_channel(struct work_struct *work)
132 {
133  struct vmbus_channel *channel = container_of(work,
134  struct vmbus_channel,
135  work);
136 
137  destroy_workqueue(channel->controlwq);
138 
139  kfree(channel);
140 }
141 
142 /*
143  * free_channel - Release the resources used by the vmbus channel object
144  */
145 static void free_channel(struct vmbus_channel *channel)
146 {
147 
148  /*
149  * We have to release the channel's workqueue/thread in the vmbus's
150  * workqueue/thread context
151  * ie we can't destroy ourselves.
152  */
153  INIT_WORK(&channel->work, release_channel);
154  queue_work(vmbus_connection.work_queue, &channel->work);
155 }
156 
157 
158 
159 /*
160  * vmbus_process_rescind_offer -
161  * Rescind the offer by initiating a device removal
162  */
163 static void vmbus_process_rescind_offer(struct work_struct *work)
164 {
165  struct vmbus_channel *channel = container_of(work,
166  struct vmbus_channel,
167  work);
168 
169  vmbus_device_unregister(channel->device_obj);
170 }
171 
173 {
174  struct vmbus_channel *channel;
175 
176  list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) {
177  vmbus_device_unregister(channel->device_obj);
178  kfree(channel->device_obj);
179  free_channel(channel);
180  }
181 }
182 
183 /*
184  * vmbus_process_offer - Process the offer by creating a channel/device
185  * associated with this offer
186  */
187 static void vmbus_process_offer(struct work_struct *work)
188 {
189  struct vmbus_channel *newchannel = container_of(work,
190  struct vmbus_channel,
191  work);
192  struct vmbus_channel *channel;
193  bool fnew = true;
194  int ret;
195  unsigned long flags;
196 
197  /* The next possible work is rescind handling */
198  INIT_WORK(&newchannel->work, vmbus_process_rescind_offer);
199 
200  /* Make sure this is a new offer */
202 
203  list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) {
204  if (!uuid_le_cmp(channel->offermsg.offer.if_type,
205  newchannel->offermsg.offer.if_type) &&
206  !uuid_le_cmp(channel->offermsg.offer.if_instance,
207  newchannel->offermsg.offer.if_instance)) {
208  fnew = false;
209  break;
210  }
211  }
212 
213  if (fnew)
214  list_add_tail(&newchannel->listentry,
216 
217  spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags);
218 
219  if (!fnew) {
220  free_channel(newchannel);
221  return;
222  }
223 
224  /*
225  * Start the process of binding this offer to the driver
226  * We need to set the DeviceObject field before calling
227  * vmbus_child_dev_add()
228  */
229  newchannel->device_obj = vmbus_device_create(
230  &newchannel->offermsg.offer.if_type,
231  &newchannel->offermsg.offer.if_instance,
232  newchannel);
233 
234  /*
235  * Add the new device to the bus. This will kick off device-driver
236  * binding which eventually invokes the device driver's AddDevice()
237  * method.
238  */
239  ret = vmbus_device_register(newchannel->device_obj);
240  if (ret != 0) {
241  pr_err("unable to add child device object (relid %d)\n",
242  newchannel->offermsg.child_relid);
243 
245  list_del(&newchannel->listentry);
246  spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags);
247  kfree(newchannel->device_obj);
248 
249  free_channel(newchannel);
250  } else {
251  /*
252  * This state is used to indicate a successful open
253  * so that when we do close the channel normally, we
254  * can cleanup properly
255  */
256  newchannel->state = CHANNEL_OPEN_STATE;
257  }
258 }
259 
260 /*
261  * vmbus_onoffer - Handler for channel offers from vmbus in parent partition.
262  *
263  */
264 static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)
265 {
266  struct vmbus_channel_offer_channel *offer;
267  struct vmbus_channel *newchannel;
268  uuid_le *guidtype;
269  uuid_le *guidinstance;
270 
271  offer = (struct vmbus_channel_offer_channel *)hdr;
272 
273  guidtype = &offer->offer.if_type;
274  guidinstance = &offer->offer.if_instance;
275 
276  /* Allocate the channel object and save this offer. */
277  newchannel = alloc_channel();
278  if (!newchannel) {
279  pr_err("Unable to allocate channel object\n");
280  return;
281  }
282 
283  memcpy(&newchannel->offermsg, offer,
284  sizeof(struct vmbus_channel_offer_channel));
285  newchannel->monitor_grp = (u8)offer->monitorid / 32;
286  newchannel->monitor_bit = (u8)offer->monitorid % 32;
287 
288  INIT_WORK(&newchannel->work, vmbus_process_offer);
289  queue_work(newchannel->controlwq, &newchannel->work);
290 }
291 
292 /*
293  * vmbus_onoffer_rescind - Rescind offer handler.
294  *
295  * We queue a work item to process this offer synchronously
296  */
297 static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
298 {
299  struct vmbus_channel_rescind_offer *rescind;
300  struct vmbus_channel *channel;
301 
302  rescind = (struct vmbus_channel_rescind_offer *)hdr;
303  channel = relid2channel(rescind->child_relid);
304 
305  if (channel == NULL)
306  /* Just return here, no channel found */
307  return;
308 
309  /* work is initialized for vmbus_process_rescind_offer() from
310  * vmbus_process_offer() where the channel got created */
311  queue_work(channel->controlwq, &channel->work);
312 }
313 
314 /*
315  * vmbus_onoffers_delivered -
316  * This is invoked when all offers have been delivered.
317  *
318  * Nothing to do here.
319  */
320 static void vmbus_onoffers_delivered(
321  struct vmbus_channel_message_header *hdr)
322 {
323 }
324 
325 /*
326  * vmbus_onopen_result - Open result handler.
327  *
328  * This is invoked when we received a response to our channel open request.
329  * Find the matching request, copy the response and signal the requesting
330  * thread.
331  */
332 static void vmbus_onopen_result(struct vmbus_channel_message_header *hdr)
333 {
334  struct vmbus_channel_open_result *result;
335  struct vmbus_channel_msginfo *msginfo;
336  struct vmbus_channel_message_header *requestheader;
337  struct vmbus_channel_open_channel *openmsg;
338  unsigned long flags;
339 
340  result = (struct vmbus_channel_open_result *)hdr;
341 
342  /*
343  * Find the open msg, copy the result and signal/unblock the wait event
344  */
346 
348  msglistentry) {
349  requestheader =
350  (struct vmbus_channel_message_header *)msginfo->msg;
351 
352  if (requestheader->msgtype == CHANNELMSG_OPENCHANNEL) {
353  openmsg =
354  (struct vmbus_channel_open_channel *)msginfo->msg;
355  if (openmsg->child_relid == result->child_relid &&
356  openmsg->openid == result->openid) {
357  memcpy(&msginfo->response.open_result,
358  result,
359  sizeof(
360  struct vmbus_channel_open_result));
361  complete(&msginfo->waitevent);
362  break;
363  }
364  }
365  }
366  spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
367 }
368 
369 /*
370  * vmbus_ongpadl_created - GPADL created handler.
371  *
372  * This is invoked when we received a response to our gpadl create request.
373  * Find the matching request, copy the response and signal the requesting
374  * thread.
375  */
376 static void vmbus_ongpadl_created(struct vmbus_channel_message_header *hdr)
377 {
378  struct vmbus_channel_gpadl_created *gpadlcreated;
379  struct vmbus_channel_msginfo *msginfo;
380  struct vmbus_channel_message_header *requestheader;
381  struct vmbus_channel_gpadl_header *gpadlheader;
382  unsigned long flags;
383 
384  gpadlcreated = (struct vmbus_channel_gpadl_created *)hdr;
385 
386  /*
387  * Find the establish msg, copy the result and signal/unblock the wait
388  * event
389  */
391 
393  msglistentry) {
394  requestheader =
395  (struct vmbus_channel_message_header *)msginfo->msg;
396 
397  if (requestheader->msgtype == CHANNELMSG_GPADL_HEADER) {
398  gpadlheader =
399  (struct vmbus_channel_gpadl_header *)requestheader;
400 
401  if ((gpadlcreated->child_relid ==
402  gpadlheader->child_relid) &&
403  (gpadlcreated->gpadl == gpadlheader->gpadl)) {
404  memcpy(&msginfo->response.gpadl_created,
405  gpadlcreated,
406  sizeof(
407  struct vmbus_channel_gpadl_created));
408  complete(&msginfo->waitevent);
409  break;
410  }
411  }
412  }
413  spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
414 }
415 
416 /*
417  * vmbus_ongpadl_torndown - GPADL torndown handler.
418  *
419  * This is invoked when we received a response to our gpadl teardown request.
420  * Find the matching request, copy the response and signal the requesting
421  * thread.
422  */
423 static void vmbus_ongpadl_torndown(
424  struct vmbus_channel_message_header *hdr)
425 {
426  struct vmbus_channel_gpadl_torndown *gpadl_torndown;
427  struct vmbus_channel_msginfo *msginfo;
428  struct vmbus_channel_message_header *requestheader;
429  struct vmbus_channel_gpadl_teardown *gpadl_teardown;
430  unsigned long flags;
431 
432  gpadl_torndown = (struct vmbus_channel_gpadl_torndown *)hdr;
433 
434  /*
435  * Find the open msg, copy the result and signal/unblock the wait event
436  */
438 
440  msglistentry) {
441  requestheader =
442  (struct vmbus_channel_message_header *)msginfo->msg;
443 
444  if (requestheader->msgtype == CHANNELMSG_GPADL_TEARDOWN) {
445  gpadl_teardown =
446  (struct vmbus_channel_gpadl_teardown *)requestheader;
447 
448  if (gpadl_torndown->gpadl == gpadl_teardown->gpadl) {
449  memcpy(&msginfo->response.gpadl_torndown,
450  gpadl_torndown,
451  sizeof(
452  struct vmbus_channel_gpadl_torndown));
453  complete(&msginfo->waitevent);
454  break;
455  }
456  }
457  }
458  spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
459 }
460 
461 /*
462  * vmbus_onversion_response - Version response handler
463  *
464  * This is invoked when we received a response to our initiate contact request.
465  * Find the matching request, copy the response and signal the requesting
466  * thread.
467  */
468 static void vmbus_onversion_response(
469  struct vmbus_channel_message_header *hdr)
470 {
471  struct vmbus_channel_msginfo *msginfo;
472  struct vmbus_channel_message_header *requestheader;
473  struct vmbus_channel_initiate_contact *initiate;
474  struct vmbus_channel_version_response *version_response;
475  unsigned long flags;
476 
477  version_response = (struct vmbus_channel_version_response *)hdr;
479 
481  msglistentry) {
482  requestheader =
483  (struct vmbus_channel_message_header *)msginfo->msg;
484 
485  if (requestheader->msgtype ==
486  CHANNELMSG_INITIATE_CONTACT) {
487  initiate =
488  (struct vmbus_channel_initiate_contact *)requestheader;
489  memcpy(&msginfo->response.version_response,
490  version_response,
491  sizeof(struct vmbus_channel_version_response));
492  complete(&msginfo->waitevent);
493  }
494  }
495  spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
496 }
497 
498 /* Channel message dispatch table */
500  channel_message_table[CHANNELMSG_COUNT] = {
501  {CHANNELMSG_INVALID, NULL},
502  {CHANNELMSG_OFFERCHANNEL, vmbus_onoffer},
503  {CHANNELMSG_RESCIND_CHANNELOFFER, vmbus_onoffer_rescind},
504  {CHANNELMSG_REQUESTOFFERS, NULL},
505  {CHANNELMSG_ALLOFFERS_DELIVERED, vmbus_onoffers_delivered},
506  {CHANNELMSG_OPENCHANNEL, NULL},
507  {CHANNELMSG_OPENCHANNEL_RESULT, vmbus_onopen_result},
508  {CHANNELMSG_CLOSECHANNEL, NULL},
509  {CHANNELMSG_GPADL_HEADER, NULL},
510  {CHANNELMSG_GPADL_BODY, NULL},
511  {CHANNELMSG_GPADL_CREATED, vmbus_ongpadl_created},
512  {CHANNELMSG_GPADL_TEARDOWN, NULL},
513  {CHANNELMSG_GPADL_TORNDOWN, vmbus_ongpadl_torndown},
514  {CHANNELMSG_RELID_RELEASED, NULL},
515  {CHANNELMSG_INITIATE_CONTACT, NULL},
516  {CHANNELMSG_VERSION_RESPONSE, vmbus_onversion_response},
517  {CHANNELMSG_UNLOAD, NULL},
518 };
519 
520 /*
521  * vmbus_onmessage - Handler for channel protocol messages.
522  *
523  * This is invoked in the vmbus worker thread context.
524  */
526 {
527  struct hv_message *msg = context;
528  struct vmbus_channel_message_header *hdr;
529  int size;
530 
531  hdr = (struct vmbus_channel_message_header *)msg->u.payload;
532  size = msg->header.payload_size;
533 
534  if (hdr->msgtype >= CHANNELMSG_COUNT) {
535  pr_err("Received invalid channel message type %d size %d\n",
536  hdr->msgtype, size);
537  print_hex_dump_bytes("", DUMP_PREFIX_NONE,
538  (unsigned char *)msg->u.payload, size);
539  return;
540  }
541 
542  if (channel_message_table[hdr->msgtype].message_handler)
543  channel_message_table[hdr->msgtype].message_handler(hdr);
544  else
545  pr_err("Unhandled channel message type %d\n", hdr->msgtype);
546 }
547 
548 /*
549  * vmbus_request_offers - Send a request to get all our pending offers.
550  */
552 {
553  struct vmbus_channel_message_header *msg;
554  struct vmbus_channel_msginfo *msginfo;
555  int ret, t;
556 
557  msginfo = kmalloc(sizeof(*msginfo) +
558  sizeof(struct vmbus_channel_message_header),
559  GFP_KERNEL);
560  if (!msginfo)
561  return -ENOMEM;
562 
563  init_completion(&msginfo->waitevent);
564 
565  msg = (struct vmbus_channel_message_header *)msginfo->msg;
566 
567  msg->msgtype = CHANNELMSG_REQUESTOFFERS;
568 
569 
570  ret = vmbus_post_msg(msg,
571  sizeof(struct vmbus_channel_message_header));
572  if (ret != 0) {
573  pr_err("Unable to request offers - %d\n", ret);
574 
575  goto cleanup;
576  }
577 
578  t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ);
579  if (t == 0) {
580  ret = -ETIMEDOUT;
581  goto cleanup;
582  }
583 
584 
585 
586 cleanup:
587  kfree(msginfo);
588 
589  return ret;
590 }
591 
592 /* eof */