linux/drivers/staging/hv/channel_mgmt.c
<<
>>
Prefs
   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 <haiyangz@microsoft.com>
  19 *   Hank Janssen  <hjanssen@microsoft.com>
  20 */
  21#include <linux/kernel.h>
  22#include <linux/mm.h>
  23#include <linux/slab.h>
  24#include <linux/list.h>
  25#include <linux/module.h>
  26#include <linux/completion.h>
  27#include "osd.h"
  28#include "logging.h"
  29#include "vmbus_private.h"
  30#include "utils.h"
  31
  32struct vmbus_channel_message_table_entry {
  33        enum vmbus_channel_message_type messageType;
  34        void (*messageHandler)(struct vmbus_channel_message_header *msg);
  35};
  36
  37#define MAX_MSG_TYPES                    3
  38#define MAX_NUM_DEVICE_CLASSES_SUPPORTED 7
  39
  40static const struct hv_guid
  41        gSupportedDeviceClasses[MAX_NUM_DEVICE_CLASSES_SUPPORTED] = {
  42        /* {ba6163d9-04a1-4d29-b605-72e2ffb1dc7f} */
  43        /* Storage - SCSI */
  44        {
  45                .data  = {
  46                        0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d,
  47                        0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f
  48                }
  49        },
  50
  51        /* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */
  52        /* Network */
  53        {
  54                .data = {
  55                        0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46,
  56                        0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E
  57                }
  58        },
  59
  60        /* {CFA8B69E-5B4A-4cc0-B98B-8BA1A1F3F95A} */
  61        /* Input */
  62        {
  63                .data = {
  64                        0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c,
  65                        0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A
  66                }
  67        },
  68
  69        /* {32412632-86cb-44a2-9b5c-50d1417354f5} */
  70        /* IDE */
  71        {
  72                .data = {
  73                        0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44,
  74                        0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5
  75                }
  76        },
  77        /* 0E0B6031-5213-4934-818B-38D90CED39DB */
  78        /* Shutdown */
  79        {
  80                .data = {
  81                        0x31, 0x60, 0x0B, 0X0E, 0x13, 0x52, 0x34, 0x49,
  82                        0x81, 0x8B, 0x38, 0XD9, 0x0C, 0xED, 0x39, 0xDB
  83                }
  84        },
  85        /* {9527E630-D0AE-497b-ADCE-E80AB0175CAF} */
  86        /* TimeSync */
  87        {
  88                .data = {
  89                        0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49,
  90                        0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf
  91                }
  92        },
  93        /* {57164f39-9115-4e78-ab55-382f3bd5422d} */
  94        /* Heartbeat */
  95        {
  96                .data = {
  97                        0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e,
  98                        0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d
  99                }
 100        },
 101};
 102
 103
 104/**
 105 * prep_negotiate_resp() - Create default response for Hyper-V Negotiate message
 106 * @icmsghdrp: Pointer to msg header structure
 107 * @icmsg_negotiate: Pointer to negotiate message structure
 108 * @buf: Raw buffer channel data
 109 *
 110 * @icmsghdrp is of type &struct icmsg_hdr.
 111 * @negop is of type &struct icmsg_negotiate.
 112 * Set up and fill in default negotiate response message. This response can
 113 * come from both the vmbus driver and the hv_utils driver. The current api
 114 * will respond properly to both Windows 2008 and Windows 2008-R2 operating
 115 * systems.
 116 *
 117 * Mainly used by Hyper-V drivers.
 118 */
 119void prep_negotiate_resp(struct icmsg_hdr *icmsghdrp,
 120                             struct icmsg_negotiate *negop,
 121                             u8 *buf)
 122{
 123        if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
 124                icmsghdrp->icmsgsize = 0x10;
 125
 126                negop = (struct icmsg_negotiate *)&buf[
 127                        sizeof(struct vmbuspipe_hdr) +
 128                        sizeof(struct icmsg_hdr)];
 129
 130                if (negop->icframe_vercnt == 2 &&
 131                   negop->icversion_data[1].major == 3) {
 132                        negop->icversion_data[0].major = 3;
 133                        negop->icversion_data[0].minor = 0;
 134                        negop->icversion_data[1].major = 3;
 135                        negop->icversion_data[1].minor = 0;
 136                } else {
 137                        negop->icversion_data[0].major = 1;
 138                        negop->icversion_data[0].minor = 0;
 139                        negop->icversion_data[1].major = 1;
 140                        negop->icversion_data[1].minor = 0;
 141                }
 142
 143                negop->icframe_vercnt = 1;
 144                negop->icmsg_vercnt = 1;
 145        }
 146}
 147EXPORT_SYMBOL(prep_negotiate_resp);
 148
 149/**
 150 * chn_cb_negotiate() - Default handler for non IDE/SCSI/NETWORK
 151 * Hyper-V requests
 152 * @context: Pointer to argument structure.
 153 *
 154 * Set up the default handler for non device driver specific requests
 155 * from Hyper-V. This stub responds to the default negotiate messages
 156 * that come in for every non IDE/SCSI/Network request.
 157 * This behavior is normally overwritten in the hv_utils driver. That
 158 * driver handles requests like gracefull shutdown, heartbeats etc.
 159 *
 160 * Mainly used by Hyper-V drivers.
 161 */
 162void chn_cb_negotiate(void *context)
 163{
 164        struct vmbus_channel *channel = context;
 165        u8 *buf;
 166        u32 buflen, recvlen;
 167        u64 requestid;
 168
 169        struct icmsg_hdr *icmsghdrp;
 170        struct icmsg_negotiate *negop = NULL;
 171
 172        buflen = PAGE_SIZE;
 173        buf = kmalloc(buflen, GFP_ATOMIC);
 174
 175        vmbus_recvpacket(channel, buf, buflen, &recvlen, &requestid);
 176
 177        if (recvlen > 0) {
 178                icmsghdrp = (struct icmsg_hdr *)&buf[
 179                        sizeof(struct vmbuspipe_hdr)];
 180
 181                prep_negotiate_resp(icmsghdrp, negop, buf);
 182
 183                icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
 184                        | ICMSGHDRFLAG_RESPONSE;
 185
 186                vmbus_sendpacket(channel, buf,
 187                                       recvlen, requestid,
 188                                       VmbusPacketTypeDataInBand, 0);
 189        }
 190
 191        kfree(buf);
 192}
 193EXPORT_SYMBOL(chn_cb_negotiate);
 194
 195/*
 196 * Function table used for message responses for non IDE/SCSI/Network type
 197 * messages. (Such as KVP/Shutdown etc)
 198 */
 199struct hyperv_service_callback hv_cb_utils[MAX_MSG_TYPES] = {
 200        /* 0E0B6031-5213-4934-818B-38D90CED39DB */
 201        /* Shutdown */
 202        {
 203                .msg_type = HV_SHUTDOWN_MSG,
 204                .data = {
 205                        0x31, 0x60, 0x0B, 0X0E, 0x13, 0x52, 0x34, 0x49,
 206                        0x81, 0x8B, 0x38, 0XD9, 0x0C, 0xED, 0x39, 0xDB
 207                },
 208                .callback = chn_cb_negotiate,
 209                .log_msg = "Shutdown channel functionality initialized"
 210        },
 211
 212        /* {9527E630-D0AE-497b-ADCE-E80AB0175CAF} */
 213        /* TimeSync */
 214        {
 215                .msg_type = HV_TIMESYNC_MSG,
 216                .data = {
 217                        0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49,
 218                        0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf
 219                },
 220                .callback = chn_cb_negotiate,
 221                .log_msg = "Timesync channel functionality initialized"
 222        },
 223        /* {57164f39-9115-4e78-ab55-382f3bd5422d} */
 224        /* Heartbeat */
 225        {
 226                .msg_type = HV_HEARTBEAT_MSG,
 227                .data = {
 228                        0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e,
 229                        0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d
 230                },
 231                .callback = chn_cb_negotiate,
 232                .log_msg = "Heartbeat channel functionality initialized"
 233        },
 234};
 235EXPORT_SYMBOL(hv_cb_utils);
 236
 237/*
 238 * alloc_channel - Allocate and initialize a vmbus channel object
 239 */
 240static struct vmbus_channel *alloc_channel(void)
 241{
 242        struct vmbus_channel *channel;
 243
 244        channel = kzalloc(sizeof(*channel), GFP_ATOMIC);
 245        if (!channel)
 246                return NULL;
 247
 248        spin_lock_init(&channel->inbound_lock);
 249
 250        init_timer(&channel->poll_timer);
 251        channel->poll_timer.data = (unsigned long)channel;
 252        channel->poll_timer.function = vmbus_ontimer;
 253
 254        channel->controlwq = create_workqueue("hv_vmbus_ctl");
 255        if (!channel->controlwq) {
 256                kfree(channel);
 257                return NULL;
 258        }
 259
 260        return channel;
 261}
 262
 263/*
 264 * release_hannel - Release the vmbus channel object itself
 265 */
 266static void release_channel(struct work_struct *work)
 267{
 268        struct vmbus_channel *channel = container_of(work,
 269                                                     struct vmbus_channel,
 270                                                     work);
 271
 272        DPRINT_DBG(VMBUS, "releasing channel (%p)", channel);
 273        destroy_workqueue(channel->controlwq);
 274        DPRINT_DBG(VMBUS, "channel released (%p)", channel);
 275
 276        kfree(channel);
 277}
 278
 279/*
 280 * free_channel - Release the resources used by the vmbus channel object
 281 */
 282void free_channel(struct vmbus_channel *channel)
 283{
 284        del_timer_sync(&channel->poll_timer);
 285
 286        /*
 287         * We have to release the channel's workqueue/thread in the vmbus's
 288         * workqueue/thread context
 289         * ie we can't destroy ourselves.
 290         */
 291        INIT_WORK(&channel->work, release_channel);
 292        queue_work(gVmbusConnection.WorkQueue, &channel->work);
 293}
 294
 295
 296DECLARE_COMPLETION(hv_channel_ready);
 297
 298/*
 299 * Count initialized channels, and ensure all channels are ready when hv_vmbus
 300 * module loading completes.
 301 */
 302static void count_hv_channel(void)
 303{
 304        static int counter;
 305        unsigned long flags;
 306
 307        spin_lock_irqsave(&gVmbusConnection.channel_lock, flags);
 308        if (++counter == MAX_MSG_TYPES)
 309                complete(&hv_channel_ready);
 310        spin_unlock_irqrestore(&gVmbusConnection.channel_lock, flags);
 311}
 312
 313/*
 314 * vmbus_process_rescind_offer -
 315 * Rescind the offer by initiating a device removal
 316 */
 317static void vmbus_process_rescind_offer(struct work_struct *work)
 318{
 319        struct vmbus_channel *channel = container_of(work,
 320                                                     struct vmbus_channel,
 321                                                     work);
 322
 323        vmbus_child_device_unregister(channel->device_obj);
 324}
 325
 326/*
 327 * vmbus_process_offer - Process the offer by creating a channel/device
 328 * associated with this offer
 329 */
 330static void vmbus_process_offer(struct work_struct *work)
 331{
 332        struct vmbus_channel *newchannel = container_of(work,
 333                                                        struct vmbus_channel,
 334                                                        work);
 335        struct vmbus_channel *channel;
 336        bool fnew = true;
 337        int ret;
 338        int cnt;
 339        unsigned long flags;
 340
 341        /* The next possible work is rescind handling */
 342        INIT_WORK(&newchannel->work, vmbus_process_rescind_offer);
 343
 344        /* Make sure this is a new offer */
 345        spin_lock_irqsave(&gVmbusConnection.channel_lock, flags);
 346
 347        list_for_each_entry(channel, &gVmbusConnection.ChannelList, listentry) {
 348                if (!memcmp(&channel->offermsg.offer.InterfaceType,
 349                            &newchannel->offermsg.offer.InterfaceType,
 350                            sizeof(struct hv_guid)) &&
 351                    !memcmp(&channel->offermsg.offer.InterfaceInstance,
 352                            &newchannel->offermsg.offer.InterfaceInstance,
 353                            sizeof(struct hv_guid))) {
 354                        fnew = false;
 355                        break;
 356                }
 357        }
 358
 359        if (fnew)
 360                list_add_tail(&newchannel->listentry,
 361                              &gVmbusConnection.ChannelList);
 362
 363        spin_unlock_irqrestore(&gVmbusConnection.channel_lock, flags);
 364
 365        if (!fnew) {
 366                DPRINT_DBG(VMBUS, "Ignoring duplicate offer for relid (%d)",
 367                           newchannel->offermsg.child_relid);
 368                free_channel(newchannel);
 369                return;
 370        }
 371
 372        /*
 373         * Start the process of binding this offer to the driver
 374         * We need to set the DeviceObject field before calling
 375         * VmbusChildDeviceAdd()
 376         */
 377        newchannel->device_obj = vmbus_child_device_create(
 378                &newchannel->offermsg.offer.InterfaceType,
 379                &newchannel->offermsg.offer.InterfaceInstance,
 380                newchannel);
 381
 382        DPRINT_DBG(VMBUS, "child device object allocated - %p",
 383                   newchannel->device_obj);
 384
 385        /*
 386         * Add the new device to the bus. This will kick off device-driver
 387         * binding which eventually invokes the device driver's AddDevice()
 388         * method.
 389         */
 390        ret = VmbusChildDeviceAdd(newchannel->device_obj);
 391        if (ret != 0) {
 392                DPRINT_ERR(VMBUS,
 393                           "unable to add child device object (relid %d)",
 394                           newchannel->offermsg.child_relid);
 395
 396                spin_lock_irqsave(&gVmbusConnection.channel_lock, flags);
 397                list_del(&newchannel->listentry);
 398                spin_unlock_irqrestore(&gVmbusConnection.channel_lock, flags);
 399
 400                free_channel(newchannel);
 401        } else {
 402                /*
 403                 * This state is used to indicate a successful open
 404                 * so that when we do close the channel normally, we
 405                 * can cleanup properly
 406                 */
 407                newchannel->state = CHANNEL_OPEN_STATE;
 408
 409                /* Open IC channels */
 410                for (cnt = 0; cnt < MAX_MSG_TYPES; cnt++) {
 411                        if (memcmp(&newchannel->offermsg.offer.InterfaceType,
 412                                   &hv_cb_utils[cnt].data,
 413                                   sizeof(struct hv_guid)) == 0 &&
 414                                vmbus_open(newchannel, 2 * PAGE_SIZE,
 415                                                 2 * PAGE_SIZE, NULL, 0,
 416                                                 hv_cb_utils[cnt].callback,
 417                                                 newchannel) == 0) {
 418                                hv_cb_utils[cnt].channel = newchannel;
 419                                DPRINT_INFO(VMBUS, "%s",
 420                                                hv_cb_utils[cnt].log_msg);
 421                                count_hv_channel();
 422                        }
 423                }
 424        }
 425}
 426
 427/*
 428 * vmbus_onoffer - Handler for channel offers from vmbus in parent partition.
 429 *
 430 * We ignore all offers except network and storage offers. For each network and
 431 * storage offers, we create a channel object and queue a work item to the
 432 * channel object to process the offer synchronously
 433 */
 434static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)
 435{
 436        struct vmbus_channel_offer_channel *offer;
 437        struct vmbus_channel *newchannel;
 438        struct hv_guid *guidtype;
 439        struct hv_guid *guidinstance;
 440        int i;
 441        int fsupported = 0;
 442
 443        offer = (struct vmbus_channel_offer_channel *)hdr;
 444        for (i = 0; i < MAX_NUM_DEVICE_CLASSES_SUPPORTED; i++) {
 445                if (memcmp(&offer->offer.InterfaceType,
 446                    &gSupportedDeviceClasses[i], sizeof(struct hv_guid)) == 0) {
 447                        fsupported = 1;
 448                        break;
 449                }
 450        }
 451
 452        if (!fsupported) {
 453                DPRINT_DBG(VMBUS, "Ignoring channel offer notification for "
 454                           "child relid %d", offer->child_relid);
 455                return;
 456        }
 457
 458        guidtype = &offer->offer.InterfaceType;
 459        guidinstance = &offer->offer.InterfaceInstance;
 460
 461        DPRINT_INFO(VMBUS, "Channel offer notification - "
 462                    "child relid %d monitor id %d allocated %d, "
 463                    "type {%02x%02x%02x%02x-%02x%02x-%02x%02x-"
 464                    "%02x%02x%02x%02x%02x%02x%02x%02x} "
 465                    "instance {%02x%02x%02x%02x-%02x%02x-%02x%02x-"
 466                    "%02x%02x%02x%02x%02x%02x%02x%02x}",
 467                    offer->child_relid, offer->monitorid,
 468                    offer->monitor_allocated,
 469                    guidtype->data[3], guidtype->data[2],
 470                    guidtype->data[1], guidtype->data[0],
 471                    guidtype->data[5], guidtype->data[4],
 472                    guidtype->data[7], guidtype->data[6],
 473                    guidtype->data[8], guidtype->data[9],
 474                    guidtype->data[10], guidtype->data[11],
 475                    guidtype->data[12], guidtype->data[13],
 476                    guidtype->data[14], guidtype->data[15],
 477                    guidinstance->data[3], guidinstance->data[2],
 478                    guidinstance->data[1], guidinstance->data[0],
 479                    guidinstance->data[5], guidinstance->data[4],
 480                    guidinstance->data[7], guidinstance->data[6],
 481                    guidinstance->data[8], guidinstance->data[9],
 482                    guidinstance->data[10], guidinstance->data[11],
 483                    guidinstance->data[12], guidinstance->data[13],
 484                    guidinstance->data[14], guidinstance->data[15]);
 485
 486        /* Allocate the channel object and save this offer. */
 487        newchannel = alloc_channel();
 488        if (!newchannel) {
 489                DPRINT_ERR(VMBUS, "unable to allocate channel object");
 490                return;
 491        }
 492
 493        DPRINT_DBG(VMBUS, "channel object allocated - %p", newchannel);
 494
 495        memcpy(&newchannel->offermsg, offer,
 496               sizeof(struct vmbus_channel_offer_channel));
 497        newchannel->monitor_grp = (u8)offer->monitorid / 32;
 498        newchannel->monitor_bit = (u8)offer->monitorid % 32;
 499
 500        /* TODO: Make sure the offer comes from our parent partition */
 501        INIT_WORK(&newchannel->work, vmbus_process_offer);
 502        queue_work(newchannel->controlwq, &newchannel->work);
 503}
 504
 505/*
 506 * vmbus_onoffer_rescind - Rescind offer handler.
 507 *
 508 * We queue a work item to process this offer synchronously
 509 */
 510static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
 511{
 512        struct vmbus_channel_rescind_offer *rescind;
 513        struct vmbus_channel *channel;
 514
 515        rescind = (struct vmbus_channel_rescind_offer *)hdr;
 516        channel = GetChannelFromRelId(rescind->child_relid);
 517        if (channel == NULL) {
 518                DPRINT_DBG(VMBUS, "channel not found for relId %d",
 519                           rescind->child_relid);
 520                return;
 521        }
 522
 523        /* work is initialized for vmbus_process_rescind_offer() from
 524         * vmbus_process_offer() where the channel got created */
 525        queue_work(channel->controlwq, &channel->work);
 526}
 527
 528/*
 529 * vmbus_onoffers_delivered -
 530 * This is invoked when all offers have been delivered.
 531 *
 532 * Nothing to do here.
 533 */
 534static void vmbus_onoffers_delivered(
 535                        struct vmbus_channel_message_header *hdr)
 536{
 537}
 538
 539/*
 540 * vmbus_onopen_result - Open result handler.
 541 *
 542 * This is invoked when we received a response to our channel open request.
 543 * Find the matching request, copy the response and signal the requesting
 544 * thread.
 545 */
 546static void vmbus_onopen_result(struct vmbus_channel_message_header *hdr)
 547{
 548        struct vmbus_channel_open_result *result;
 549        struct list_head *curr;
 550        struct vmbus_channel_msginfo *msginfo;
 551        struct vmbus_channel_message_header *requestheader;
 552        struct vmbus_channel_open_channel *openmsg;
 553        unsigned long flags;
 554
 555        result = (struct vmbus_channel_open_result *)hdr;
 556        DPRINT_DBG(VMBUS, "vmbus open result - %d", result->status);
 557
 558        /*
 559         * Find the open msg, copy the result and signal/unblock the wait event
 560         */
 561        spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags);
 562
 563        list_for_each(curr, &gVmbusConnection.ChannelMsgList) {
 564/* FIXME: this should probably use list_entry() instead */
 565                msginfo = (struct vmbus_channel_msginfo *)curr;
 566                requestheader =
 567                        (struct vmbus_channel_message_header *)msginfo->msg;
 568
 569                if (requestheader->msgtype == CHANNELMSG_OPENCHANNEL) {
 570                        openmsg =
 571                        (struct vmbus_channel_open_channel *)msginfo->msg;
 572                        if (openmsg->child_relid == result->child_relid &&
 573                            openmsg->openid == result->openid) {
 574                                memcpy(&msginfo->response.open_result,
 575                                       result,
 576                                       sizeof(struct vmbus_channel_open_result));
 577                                osd_waitevent_set(msginfo->waitevent);
 578                                break;
 579                        }
 580                }
 581        }
 582        spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags);
 583}
 584
 585/*
 586 * vmbus_ongpadl_created - GPADL created handler.
 587 *
 588 * This is invoked when we received a response to our gpadl create request.
 589 * Find the matching request, copy the response and signal the requesting
 590 * thread.
 591 */
 592static void vmbus_ongpadl_created(struct vmbus_channel_message_header *hdr)
 593{
 594        struct vmbus_channel_gpadl_created *gpadlcreated;
 595        struct list_head *curr;
 596        struct vmbus_channel_msginfo *msginfo;
 597        struct vmbus_channel_message_header *requestheader;
 598        struct vmbus_channel_gpadl_header *gpadlheader;
 599        unsigned long flags;
 600
 601        gpadlcreated = (struct vmbus_channel_gpadl_created *)hdr;
 602        DPRINT_DBG(VMBUS, "vmbus gpadl created result - %d",
 603                   gpadlcreated->creation_status);
 604
 605        /*
 606         * Find the establish msg, copy the result and signal/unblock the wait
 607         * event
 608         */
 609        spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags);
 610
 611        list_for_each(curr, &gVmbusConnection.ChannelMsgList) {
 612/* FIXME: this should probably use list_entry() instead */
 613                msginfo = (struct vmbus_channel_msginfo *)curr;
 614                requestheader =
 615                        (struct vmbus_channel_message_header *)msginfo->msg;
 616
 617                if (requestheader->msgtype == CHANNELMSG_GPADL_HEADER) {
 618                        gpadlheader =
 619                        (struct vmbus_channel_gpadl_header *)requestheader;
 620
 621                        if ((gpadlcreated->child_relid ==
 622                             gpadlheader->child_relid) &&
 623                            (gpadlcreated->gpadl == gpadlheader->gpadl)) {
 624                                memcpy(&msginfo->response.gpadl_created,
 625                                       gpadlcreated,
 626                                       sizeof(struct vmbus_channel_gpadl_created));
 627                                osd_waitevent_set(msginfo->waitevent);
 628                                break;
 629                        }
 630                }
 631        }
 632        spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags);
 633}
 634
 635/*
 636 * vmbus_ongpadl_torndown - GPADL torndown handler.
 637 *
 638 * This is invoked when we received a response to our gpadl teardown request.
 639 * Find the matching request, copy the response and signal the requesting
 640 * thread.
 641 */
 642static void vmbus_ongpadl_torndown(
 643                        struct vmbus_channel_message_header *hdr)
 644{
 645        struct vmbus_channel_gpadl_torndown *gpadl_torndown;
 646        struct list_head *curr;
 647        struct vmbus_channel_msginfo *msginfo;
 648        struct vmbus_channel_message_header *requestheader;
 649        struct vmbus_channel_gpadl_teardown *gpadl_teardown;
 650        unsigned long flags;
 651
 652        gpadl_torndown = (struct vmbus_channel_gpadl_torndown *)hdr;
 653
 654        /*
 655         * Find the open msg, copy the result and signal/unblock the wait event
 656         */
 657        spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags);
 658
 659        list_for_each(curr, &gVmbusConnection.ChannelMsgList) {
 660/* FIXME: this should probably use list_entry() instead */
 661                msginfo = (struct vmbus_channel_msginfo *)curr;
 662                requestheader =
 663                        (struct vmbus_channel_message_header *)msginfo->msg;
 664
 665                if (requestheader->msgtype == CHANNELMSG_GPADL_TEARDOWN) {
 666                        gpadl_teardown =
 667                        (struct vmbus_channel_gpadl_teardown *)requestheader;
 668
 669                        if (gpadl_torndown->gpadl == gpadl_teardown->gpadl) {
 670                                memcpy(&msginfo->response.gpadl_torndown,
 671                                       gpadl_torndown,
 672                                       sizeof(struct vmbus_channel_gpadl_torndown));
 673                                osd_waitevent_set(msginfo->waitevent);
 674                                break;
 675                        }
 676                }
 677        }
 678        spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags);
 679}
 680
 681/*
 682 * vmbus_onversion_response - Version response handler
 683 *
 684 * This is invoked when we received a response to our initiate contact request.
 685 * Find the matching request, copy the response and signal the requesting
 686 * thread.
 687 */
 688static void vmbus_onversion_response(
 689                struct vmbus_channel_message_header *hdr)
 690{
 691        struct list_head *curr;
 692        struct vmbus_channel_msginfo *msginfo;
 693        struct vmbus_channel_message_header *requestheader;
 694        struct vmbus_channel_initiate_contact *initiate;
 695        struct vmbus_channel_version_response *version_response;
 696        unsigned long flags;
 697
 698        version_response = (struct vmbus_channel_version_response *)hdr;
 699        spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags);
 700
 701        list_for_each(curr, &gVmbusConnection.ChannelMsgList) {
 702/* FIXME: this should probably use list_entry() instead */
 703                msginfo = (struct vmbus_channel_msginfo *)curr;
 704                requestheader =
 705                        (struct vmbus_channel_message_header *)msginfo->msg;
 706
 707                if (requestheader->msgtype ==
 708                    CHANNELMSG_INITIATE_CONTACT) {
 709                        initiate =
 710                        (struct vmbus_channel_initiate_contact *)requestheader;
 711                        memcpy(&msginfo->response.version_response,
 712                              version_response,
 713                              sizeof(struct vmbus_channel_version_response));
 714                        osd_waitevent_set(msginfo->waitevent);
 715                }
 716        }
 717        spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags);
 718}
 719
 720/* Channel message dispatch table */
 721static struct vmbus_channel_message_table_entry
 722        gChannelMessageTable[CHANNELMSG_COUNT] = {
 723        {CHANNELMSG_INVALID,                    NULL},
 724        {CHANNELMSG_OFFERCHANNEL,               vmbus_onoffer},
 725        {CHANNELMSG_RESCIND_CHANNELOFFER,       vmbus_onoffer_rescind},
 726        {CHANNELMSG_REQUESTOFFERS,              NULL},
 727        {CHANNELMSG_ALLOFFERS_DELIVERED,        vmbus_onoffers_delivered},
 728        {CHANNELMSG_OPENCHANNEL,                NULL},
 729        {CHANNELMSG_OPENCHANNEL_RESULT, vmbus_onopen_result},
 730        {CHANNELMSG_CLOSECHANNEL,               NULL},
 731        {CHANNELMSG_GPADL_HEADER,               NULL},
 732        {CHANNELMSG_GPADL_BODY,         NULL},
 733        {CHANNELMSG_GPADL_CREATED,              vmbus_ongpadl_created},
 734        {CHANNELMSG_GPADL_TEARDOWN,             NULL},
 735        {CHANNELMSG_GPADL_TORNDOWN,             vmbus_ongpadl_torndown},
 736        {CHANNELMSG_RELID_RELEASED,             NULL},
 737        {CHANNELMSG_INITIATE_CONTACT,           NULL},
 738        {CHANNELMSG_VERSION_RESPONSE,           vmbus_onversion_response},
 739        {CHANNELMSG_UNLOAD,                     NULL},
 740};
 741
 742/*
 743 * vmbus_onmessage - Handler for channel protocol messages.
 744 *
 745 * This is invoked in the vmbus worker thread context.
 746 */
 747void vmbus_onmessage(void *context)
 748{
 749        struct hv_message *msg = context;
 750        struct vmbus_channel_message_header *hdr;
 751        int size;
 752
 753        hdr = (struct vmbus_channel_message_header *)msg->u.payload;
 754        size = msg->header.payload_size;
 755
 756        DPRINT_DBG(VMBUS, "message type %d size %d", hdr->msgtype, size);
 757
 758        if (hdr->msgtype >= CHANNELMSG_COUNT) {
 759                DPRINT_ERR(VMBUS,
 760                           "Received invalid channel message type %d size %d",
 761                           hdr->msgtype, size);
 762                print_hex_dump_bytes("", DUMP_PREFIX_NONE,
 763                                     (unsigned char *)msg->u.payload, size);
 764                return;
 765        }
 766
 767        if (gChannelMessageTable[hdr->msgtype].messageHandler)
 768                gChannelMessageTable[hdr->msgtype].messageHandler(hdr);
 769        else
 770                DPRINT_ERR(VMBUS, "Unhandled channel message type %d",
 771                           hdr->msgtype);
 772}
 773
 774/*
 775 * vmbus_request_offers - Send a request to get all our pending offers.
 776 */
 777int vmbus_request_offers(void)
 778{
 779        struct vmbus_channel_message_header *msg;
 780        struct vmbus_channel_msginfo *msginfo;
 781        int ret;
 782
 783        msginfo = kmalloc(sizeof(*msginfo) +
 784                          sizeof(struct vmbus_channel_message_header),
 785                          GFP_KERNEL);
 786        if (!msginfo)
 787                return -ENOMEM;
 788
 789        msginfo->waitevent = osd_waitevent_create();
 790        if (!msginfo->waitevent) {
 791                kfree(msginfo);
 792                return -ENOMEM;
 793        }
 794
 795        msg = (struct vmbus_channel_message_header *)msginfo->msg;
 796
 797        msg->msgtype = CHANNELMSG_REQUESTOFFERS;
 798
 799        /*SpinlockAcquire(gVmbusConnection.channelMsgLock);
 800        INSERT_TAIL_LIST(&gVmbusConnection.channelMsgList,
 801                         &msgInfo->msgListEntry);
 802        SpinlockRelease(gVmbusConnection.channelMsgLock);*/
 803
 804        ret = VmbusPostMessage(msg,
 805                               sizeof(struct vmbus_channel_message_header));
 806        if (ret != 0) {
 807                DPRINT_ERR(VMBUS, "Unable to request offers - %d", ret);
 808
 809                /*SpinlockAcquire(gVmbusConnection.channelMsgLock);
 810                REMOVE_ENTRY_LIST(&msgInfo->msgListEntry);
 811                SpinlockRelease(gVmbusConnection.channelMsgLock);*/
 812
 813                goto Cleanup;
 814        }
 815        /* osd_waitevent_wait(msgInfo->waitEvent); */
 816
 817        /*SpinlockAcquire(gVmbusConnection.channelMsgLock);
 818        REMOVE_ENTRY_LIST(&msgInfo->msgListEntry);
 819        SpinlockRelease(gVmbusConnection.channelMsgLock);*/
 820
 821
 822Cleanup:
 823        if (msginfo) {
 824                kfree(msginfo->waitevent);
 825                kfree(msginfo);
 826        }
 827
 828        return ret;
 829}
 830
 831/*
 832 * vmbus_release_unattached_channels - Release channels that are
 833 * unattached/unconnected ie (no drivers associated)
 834 */
 835void vmbus_release_unattached_channels(void)
 836{
 837        struct vmbus_channel *channel, *pos;
 838        struct vmbus_channel *start = NULL;
 839        unsigned long flags;
 840
 841        spin_lock_irqsave(&gVmbusConnection.channel_lock, flags);
 842
 843        list_for_each_entry_safe(channel, pos, &gVmbusConnection.ChannelList,
 844                                 listentry) {
 845                if (channel == start)
 846                        break;
 847
 848                if (!channel->device_obj->Driver) {
 849                        list_del(&channel->listentry);
 850                        DPRINT_INFO(VMBUS,
 851                                    "Releasing unattached device object %p",
 852                                    channel->device_obj);
 853
 854                        vmbus_child_device_unregister(channel->device_obj);
 855                        free_channel(channel);
 856                } else {
 857                        if (!start)
 858                                start = channel;
 859                }
 860        }
 861
 862        spin_unlock_irqrestore(&gVmbusConnection.channel_lock, flags);
 863}
 864
 865/* eof */
 866