Root/drivers/hv/channel_mgmt.c

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#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
35struct vmbus_channel_message_table_entry {
36    enum vmbus_channel_message_type message_type;
37    void (*message_handler)(struct vmbus_channel_message_header *msg);
38};
39
40
41/**
42 * vmbus_prep_negotiate_resp() - Create default response for Hyper-V Negotiate message
43 * @icmsghdrp: Pointer to msg header structure
44 * @icmsg_negotiate: Pointer to negotiate message structure
45 * @buf: Raw buffer channel data
46 *
47 * @icmsghdrp is of type &struct icmsg_hdr.
48 * @negop is of type &struct icmsg_negotiate.
49 * Set up and fill in default negotiate response message.
50 *
51 * The max_fw_version specifies the maximum framework version that
52 * we can support and max _srv_version specifies the maximum service
53 * version we can support. A special value MAX_SRV_VER can be
54 * specified to indicate that we can handle the maximum version
55 * exposed by the host.
56 *
57 * Mainly used by Hyper-V drivers.
58 */
59void 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
104EXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp);
105
106/*
107 * alloc_channel - Allocate and initialize a vmbus channel object
108 */
109static 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 */
131static 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 */
145static 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 */
163static 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
172void vmbus_free_channels(void)
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 */
187static 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 */
201    spin_lock_irqsave(&vmbus_connection.channel_lock, flags);
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,
215                  &vmbus_connection.chn_list);
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
244        spin_lock_irqsave(&vmbus_connection.channel_lock, flags);
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 */
264static 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 */
297static 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 */
320static 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 */
332static 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     */
345    spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
346
347    list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list,
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 */
376static 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     */
390    spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
391
392    list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list,
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 */
423static 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     */
437    spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
438
439    list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list,
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 */
468static 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;
478    spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
479
480    list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list,
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 */
499static struct vmbus_channel_message_table_entry
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 */
525void vmbus_onmessage(void *context)
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 */
551int vmbus_request_offers(void)
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
586cleanup:
587    kfree(msginfo);
588
589    return ret;
590}
591
592/* eof */
593

Archive Download this file



interactive