]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - sys/dev/hyperv/vmbus/hv_channel_mgmt.c
MFC 302707-302709
[FreeBSD/stable/10.git] / sys / dev / hyperv / vmbus / hv_channel_mgmt.c
1 /*-
2  * Copyright (c) 2009-2012,2016 Microsoft Corp.
3  * Copyright (c) 2012 NetApp Inc.
4  * Copyright (c) 2012 Citrix Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice unmodified, this list of conditions, and the following
12  *    disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/kernel.h>
34 #include <sys/lock.h>
35 #include <sys/mbuf.h>
36 #include <sys/mutex.h>
37
38 #include <dev/hyperv/include/hyperv_busdma.h>
39 #include <dev/hyperv/vmbus/hv_vmbus_priv.h>
40 #include <dev/hyperv/vmbus/vmbus_reg.h>
41 #include <dev/hyperv/vmbus/vmbus_var.h>
42
43 typedef void    (*vmbus_chanmsg_proc_t)
44                 (struct vmbus_softc *, const struct vmbus_message *);
45
46 static struct hv_vmbus_channel *hv_vmbus_allocate_channel(struct vmbus_softc *);
47 static void     vmbus_channel_on_offer_internal(struct vmbus_softc *,
48                     const hv_vmbus_channel_offer_channel *offer);
49 static void     vmbus_chan_detach_task(void *, int);
50
51 static void     vmbus_channel_on_offer(struct vmbus_softc *,
52                     const struct vmbus_message *);
53 static void     vmbus_channel_on_offer_rescind(struct vmbus_softc *,
54                     const struct vmbus_message *);
55 static void     vmbus_channel_on_offers_delivered(struct vmbus_softc *,
56                     const struct vmbus_message *);
57
58 /**
59  * Channel message dispatch table
60  */
61 static const vmbus_chanmsg_proc_t
62 vmbus_chanmsg_process[HV_CHANNEL_MESSAGE_COUNT] = {
63         [HV_CHANNEL_MESSAGE_OFFER_CHANNEL] =
64                 vmbus_channel_on_offer,
65         [HV_CHANNEL_MESSAGE_RESCIND_CHANNEL_OFFER] =
66                 vmbus_channel_on_offer_rescind,
67         [HV_CHANNEL_MESSAGE_ALL_OFFERS_DELIVERED] =
68                 vmbus_channel_on_offers_delivered,
69         [HV_CHANNEL_MESSAGE_OPEN_CHANNEL_RESULT] =
70                 vmbus_msghc_wakeup,
71         [HV_CHANNEL_MESSAGE_GPADL_CREATED] =
72                 vmbus_msghc_wakeup,
73         [HV_CHANNEL_MESSAGE_GPADL_TORNDOWN] =
74                 vmbus_msghc_wakeup,
75         [HV_CHANNEL_MESSAGE_VERSION_RESPONSE] =
76                 vmbus_msghc_wakeup
77 };
78
79 /**
80  * @brief Allocate and initialize a vmbus channel object
81  */
82 static struct hv_vmbus_channel *
83 hv_vmbus_allocate_channel(struct vmbus_softc *sc)
84 {
85         struct hv_vmbus_channel *channel;
86
87         channel = malloc(sizeof(*channel), M_DEVBUF, M_WAITOK | M_ZERO);
88         channel->vmbus_sc = sc;
89
90         mtx_init(&channel->sc_lock, "vmbus multi channel", NULL, MTX_DEF);
91         TAILQ_INIT(&channel->sc_list_anchor);
92         TASK_INIT(&channel->ch_detach_task, 0, vmbus_chan_detach_task, channel);
93
94         return (channel);
95 }
96
97 /**
98  * @brief Release the resources used by the vmbus channel object
99  */
100 void
101 hv_vmbus_free_vmbus_channel(hv_vmbus_channel* channel)
102 {
103         mtx_destroy(&channel->sc_lock);
104         free(channel, M_DEVBUF);
105 }
106
107 /**
108  * @brief Process the offer by creating a channel/device
109  * associated with this offer
110  */
111 static void
112 vmbus_channel_process_offer(hv_vmbus_channel *new_channel)
113 {
114         struct vmbus_softc *sc = new_channel->vmbus_sc;
115         hv_vmbus_channel*       channel;
116         uint32_t                relid;
117
118         relid = new_channel->ch_id;
119         /*
120          * Make sure this is a new offer
121          */
122         mtx_lock(&sc->vmbus_chlist_lock);
123         if (relid == 0) {
124                 /*
125                  * XXX channel0 will not be processed; skip it.
126                  */
127                 printf("VMBUS: got channel0 offer\n");
128         } else {
129                 sc->vmbus_chmap[relid] = new_channel;
130         }
131
132         TAILQ_FOREACH(channel, &sc->vmbus_chlist, ch_link) {
133                 if (memcmp(&channel->ch_guid_type, &new_channel->ch_guid_type,
134                     sizeof(hv_guid)) == 0 &&
135                     memcmp(&channel->ch_guid_inst, &new_channel->ch_guid_inst,
136                     sizeof(hv_guid)) == 0)
137                         break;
138         }
139
140         if (channel == NULL) {
141                 /* Install the new primary channel */
142                 TAILQ_INSERT_TAIL(&sc->vmbus_chlist, new_channel, ch_link);
143         }
144         mtx_unlock(&sc->vmbus_chlist_lock);
145
146         if (bootverbose) {
147                 char logstr[64];
148
149                 logstr[0] = '\0';
150                 if (channel != NULL) {
151                         snprintf(logstr, sizeof(logstr), ", primary chan%u",
152                             channel->ch_id);
153                 }
154                 device_printf(sc->vmbus_dev, "chan%u subchanid%u offer%s\n",
155                     new_channel->ch_id,
156                     new_channel->ch_subidx, logstr);
157         }
158
159         if (channel != NULL) {
160                 /*
161                  * Check if this is a sub channel.
162                  */
163                 if (new_channel->ch_subidx != 0) {
164                         /*
165                          * It is a sub channel offer, process it.
166                          */
167                         new_channel->primary_channel = channel;
168                         new_channel->ch_dev = channel->ch_dev;
169                         mtx_lock(&channel->sc_lock);
170                         TAILQ_INSERT_TAIL(&channel->sc_list_anchor,
171                             new_channel, sc_list_entry);
172                         mtx_unlock(&channel->sc_lock);
173
174                         /*
175                          * Insert the new channel to the end of the global
176                          * channel list.
177                          *
178                          * NOTE:
179                          * The new sub-channel MUST be inserted AFTER it's
180                          * primary channel, so that the primary channel will
181                          * be found in the above loop for its baby siblings.
182                          */
183                         mtx_lock(&sc->vmbus_chlist_lock);
184                         TAILQ_INSERT_TAIL(&sc->vmbus_chlist, new_channel,
185                             ch_link);
186                         mtx_unlock(&sc->vmbus_chlist_lock);
187
188                         new_channel->state = HV_CHANNEL_OPEN_STATE;
189
190                         /*
191                          * Bump up sub-channel count and notify anyone that is
192                          * interested in this sub-channel, after this sub-channel
193                          * is setup.
194                          */
195                         mtx_lock(&channel->sc_lock);
196                         channel->subchan_cnt++;
197                         mtx_unlock(&channel->sc_lock);
198                         wakeup(channel);
199
200                         return;
201                 }
202
203                 printf("VMBUS: duplicated primary channel%u\n",
204                     new_channel->ch_id);
205                 hv_vmbus_free_vmbus_channel(new_channel);
206                 return;
207         }
208
209         new_channel->state = HV_CHANNEL_OPEN_STATE;
210
211         /*
212          * Add the new device to the bus. This will kick off device-driver
213          * binding which eventually invokes the device driver's AddDevice()
214          * method.
215          *
216          * NOTE:
217          * Error is ignored here; don't have much to do if error really
218          * happens.
219          */
220         hv_vmbus_child_device_register(new_channel);
221 }
222
223 void
224 vmbus_channel_cpu_set(struct hv_vmbus_channel *chan, int cpu)
225 {
226         KASSERT(cpu >= 0 && cpu < mp_ncpus, ("invalid cpu %d", cpu));
227
228         if (chan->vmbus_sc->vmbus_version == VMBUS_VERSION_WS2008 ||
229             chan->vmbus_sc->vmbus_version == VMBUS_VERSION_WIN7) {
230                 /* Only cpu0 is supported */
231                 cpu = 0;
232         }
233
234         chan->target_cpu = cpu;
235         chan->target_vcpu = VMBUS_PCPU_GET(chan->vmbus_sc, vcpuid, cpu);
236
237         if (bootverbose) {
238                 printf("vmbus_chan%u: assigned to cpu%u [vcpu%u]\n",
239                     chan->ch_id,
240                     chan->target_cpu, chan->target_vcpu);
241         }
242 }
243
244 void
245 vmbus_channel_cpu_rr(struct hv_vmbus_channel *chan)
246 {
247         static uint32_t vmbus_chan_nextcpu;
248         int cpu;
249
250         cpu = atomic_fetchadd_int(&vmbus_chan_nextcpu, 1) % mp_ncpus;
251         vmbus_channel_cpu_set(chan, cpu);
252 }
253
254 static void
255 vmbus_channel_select_defcpu(struct hv_vmbus_channel *chan)
256 {
257         /*
258          * By default, pin the channel to cpu0.  Devices having
259          * special channel-cpu mapping requirement should call
260          * vmbus_channel_cpu_{set,rr}().
261          */
262         vmbus_channel_cpu_set(chan, 0);
263 }
264
265 /**
266  * @brief Handler for channel offers from Hyper-V/Azure
267  *
268  * Handler for channel offers from vmbus in parent partition.
269  */
270 static void
271 vmbus_channel_on_offer(struct vmbus_softc *sc, const struct vmbus_message *msg)
272 {
273         const hv_vmbus_channel_offer_channel *offer;
274
275         /* New channel is offered by vmbus */
276         vmbus_scan_newchan(sc);
277
278         offer = (const hv_vmbus_channel_offer_channel *)msg->msg_data;
279         vmbus_channel_on_offer_internal(sc, offer);
280 }
281
282 static void
283 vmbus_channel_on_offer_internal(struct vmbus_softc *sc,
284     const hv_vmbus_channel_offer_channel *offer)
285 {
286         hv_vmbus_channel* new_channel;
287
288         /*
289          * Allocate the channel object and save this offer
290          */
291         new_channel = hv_vmbus_allocate_channel(sc);
292         new_channel->ch_id = offer->child_rel_id;
293         new_channel->ch_subidx = offer->offer.sub_channel_index;
294         new_channel->ch_guid_type = offer->offer.interface_type;
295         new_channel->ch_guid_inst = offer->offer.interface_instance;
296
297         /* Batch reading is on by default */
298         new_channel->ch_flags |= VMBUS_CHAN_FLAG_BATCHREAD;
299         if (offer->monitor_allocated)
300                 new_channel->ch_flags |= VMBUS_CHAN_FLAG_HASMNF;
301
302         new_channel->ch_sigevt = hyperv_dmamem_alloc(
303             bus_get_dma_tag(sc->vmbus_dev),
304             HYPERCALL_SIGEVTIN_ALIGN, 0, sizeof(struct hypercall_sigevt_in),
305             &new_channel->ch_sigevt_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO);
306         if (new_channel->ch_sigevt == NULL) {
307                 device_printf(sc->vmbus_dev, "sigevt alloc failed\n");
308                 /* XXX */
309                 mtx_destroy(&new_channel->sc_lock);
310                 free(new_channel, M_DEVBUF);
311                 return;
312         }
313         new_channel->ch_sigevt->hc_connid = VMBUS_CONNID_EVENT;
314         if (sc->vmbus_version != VMBUS_VERSION_WS2008)
315                 new_channel->ch_sigevt->hc_connid = offer->connection_id;
316
317         new_channel->monitor_group = (uint8_t) offer->monitor_id / 32;
318         new_channel->monitor_bit = (uint8_t) offer->monitor_id % 32;
319
320         /* Select default cpu for this channel. */
321         vmbus_channel_select_defcpu(new_channel);
322
323         vmbus_channel_process_offer(new_channel);
324 }
325
326 /**
327  * @brief Rescind offer handler.
328  *
329  * We queue a work item to process this offer
330  * synchronously.
331  *
332  * XXX pretty broken; need rework.
333  */
334 static void
335 vmbus_channel_on_offer_rescind(struct vmbus_softc *sc,
336     const struct vmbus_message *msg)
337 {
338         const hv_vmbus_channel_rescind_offer *rescind;
339         hv_vmbus_channel*               channel;
340
341         rescind = (const hv_vmbus_channel_rescind_offer *)msg->msg_data;
342         if (bootverbose) {
343                 device_printf(sc->vmbus_dev, "chan%u rescind\n",
344                     rescind->child_rel_id);
345         }
346
347         channel = sc->vmbus_chmap[rescind->child_rel_id];
348         if (channel == NULL)
349             return;
350         sc->vmbus_chmap[rescind->child_rel_id] = NULL;
351
352         taskqueue_enqueue(taskqueue_thread, &channel->ch_detach_task);
353 }
354
355 static void
356 vmbus_chan_detach_task(void *xchan, int pending __unused)
357 {
358         struct hv_vmbus_channel *chan = xchan;
359
360         if (HV_VMBUS_CHAN_ISPRIMARY(chan)) {
361                 /* Only primary channel owns the device */
362                 hv_vmbus_child_device_unregister(chan);
363                 /* NOTE: DO NOT free primary channel for now */
364         } else {
365                 struct vmbus_softc *sc = chan->vmbus_sc;
366                 struct hv_vmbus_channel *pri_chan = chan->primary_channel;
367                 struct vmbus_chanmsg_chfree *req;
368                 struct vmbus_msghc *mh;
369                 int error;
370
371                 mh = vmbus_msghc_get(sc, sizeof(*req));
372                 if (mh == NULL) {
373                         device_printf(sc->vmbus_dev,
374                             "can not get msg hypercall for chfree(chan%u)\n",
375                             chan->ch_id);
376                         goto remove;
377                 }
378
379                 req = vmbus_msghc_dataptr(mh);
380                 req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHFREE;
381                 req->chm_chanid = chan->ch_id;
382
383                 error = vmbus_msghc_exec_noresult(mh);
384                 vmbus_msghc_put(sc, mh);
385
386                 if (error) {
387                         device_printf(sc->vmbus_dev,
388                             "chfree(chan%u) failed: %d",
389                             chan->ch_id, error);
390                         /* NOTE: Move on! */
391                 } else {
392                         if (bootverbose) {
393                                 device_printf(sc->vmbus_dev, "chan%u freed\n",
394                                     chan->ch_id);
395                         }
396                 }
397 remove:
398                 mtx_lock(&sc->vmbus_chlist_lock);
399                 TAILQ_REMOVE(&sc->vmbus_chlist, chan, ch_link);
400                 mtx_unlock(&sc->vmbus_chlist_lock);
401
402                 mtx_lock(&pri_chan->sc_lock);
403                 TAILQ_REMOVE(&pri_chan->sc_list_anchor, chan, sc_list_entry);
404                 KASSERT(pri_chan->subchan_cnt > 0,
405                     ("invalid subchan_cnt %d", pri_chan->subchan_cnt));
406                 pri_chan->subchan_cnt--;
407                 mtx_unlock(&pri_chan->sc_lock);
408                 wakeup(pri_chan);
409
410                 hv_vmbus_free_vmbus_channel(chan);
411         }
412 }
413
414 /**
415  *
416  * @brief Invoked when all offers have been delivered.
417  */
418 static void
419 vmbus_channel_on_offers_delivered(struct vmbus_softc *sc,
420     const struct vmbus_message *msg __unused)
421 {
422
423         /* No more new channels for the channel request. */
424         vmbus_scan_done(sc);
425 }
426
427 /**
428  * @brief Release channels that are unattached/unconnected (i.e., no drivers associated)
429  */
430 void
431 hv_vmbus_release_unattached_channels(struct vmbus_softc *sc)
432 {
433         hv_vmbus_channel *channel;
434
435         mtx_lock(&sc->vmbus_chlist_lock);
436
437         while (!TAILQ_EMPTY(&sc->vmbus_chlist)) {
438             channel = TAILQ_FIRST(&sc->vmbus_chlist);
439             TAILQ_REMOVE(&sc->vmbus_chlist, channel, ch_link);
440
441             if (HV_VMBUS_CHAN_ISPRIMARY(channel)) {
442                 /* Only primary channel owns the device */
443                 hv_vmbus_child_device_unregister(channel);
444             }
445             hv_vmbus_free_vmbus_channel(channel);
446         }
447         bzero(sc->vmbus_chmap,
448             sizeof(struct hv_vmbus_channel *) * VMBUS_CHAN_MAX);
449
450         mtx_unlock(&sc->vmbus_chlist_lock);
451 }
452
453 /**
454  * @brief Select the best outgoing channel
455  * 
456  * The channel whose vcpu binding is closest to the currect vcpu will
457  * be selected.
458  * If no multi-channel, always select primary channel
459  * 
460  * @param primary - primary channel
461  */
462 struct hv_vmbus_channel *
463 vmbus_select_outgoing_channel(struct hv_vmbus_channel *primary)
464 {
465         hv_vmbus_channel *new_channel = NULL;
466         hv_vmbus_channel *outgoing_channel = primary;
467         int old_cpu_distance = 0;
468         int new_cpu_distance = 0;
469         int cur_vcpu = 0;
470         int smp_pro_id = PCPU_GET(cpuid);
471
472         if (TAILQ_EMPTY(&primary->sc_list_anchor)) {
473                 return outgoing_channel;
474         }
475
476         if (smp_pro_id >= MAXCPU) {
477                 return outgoing_channel;
478         }
479
480         cur_vcpu = VMBUS_PCPU_GET(primary->vmbus_sc, vcpuid, smp_pro_id);
481         
482         TAILQ_FOREACH(new_channel, &primary->sc_list_anchor, sc_list_entry) {
483                 if (new_channel->state != HV_CHANNEL_OPENED_STATE){
484                         continue;
485                 }
486
487                 if (new_channel->target_vcpu == cur_vcpu){
488                         return new_channel;
489                 }
490
491                 old_cpu_distance = ((outgoing_channel->target_vcpu > cur_vcpu) ?
492                     (outgoing_channel->target_vcpu - cur_vcpu) :
493                     (cur_vcpu - outgoing_channel->target_vcpu));
494
495                 new_cpu_distance = ((new_channel->target_vcpu > cur_vcpu) ?
496                     (new_channel->target_vcpu - cur_vcpu) :
497                     (cur_vcpu - new_channel->target_vcpu));
498
499                 if (old_cpu_distance < new_cpu_distance) {
500                         continue;
501                 }
502
503                 outgoing_channel = new_channel;
504         }
505
506         return(outgoing_channel);
507 }
508
509 struct hv_vmbus_channel **
510 vmbus_get_subchan(struct hv_vmbus_channel *pri_chan, int subchan_cnt)
511 {
512         struct hv_vmbus_channel **ret, *chan;
513         int i;
514
515         ret = malloc(subchan_cnt * sizeof(struct hv_vmbus_channel *), M_TEMP,
516             M_WAITOK);
517
518         mtx_lock(&pri_chan->sc_lock);
519
520         while (pri_chan->subchan_cnt < subchan_cnt)
521                 mtx_sleep(pri_chan, &pri_chan->sc_lock, 0, "subch", 0);
522
523         i = 0;
524         TAILQ_FOREACH(chan, &pri_chan->sc_list_anchor, sc_list_entry) {
525                 /* TODO: refcnt chan */
526                 ret[i] = chan;
527
528                 ++i;
529                 if (i == subchan_cnt)
530                         break;
531         }
532         KASSERT(i == subchan_cnt, ("invalid subchan count %d, should be %d",
533             pri_chan->subchan_cnt, subchan_cnt));
534
535         mtx_unlock(&pri_chan->sc_lock);
536
537         return ret;
538 }
539
540 void
541 vmbus_rel_subchan(struct hv_vmbus_channel **subchan, int subchan_cnt __unused)
542 {
543
544         free(subchan, M_TEMP);
545 }
546
547 void
548 vmbus_drain_subchan(struct hv_vmbus_channel *pri_chan)
549 {
550         mtx_lock(&pri_chan->sc_lock);
551         while (pri_chan->subchan_cnt > 0)
552                 mtx_sleep(pri_chan, &pri_chan->sc_lock, 0, "dsubch", 0);
553         mtx_unlock(&pri_chan->sc_lock);
554 }
555
556 void
557 vmbus_chan_msgproc(struct vmbus_softc *sc, const struct vmbus_message *msg)
558 {
559         vmbus_chanmsg_proc_t msg_proc;
560         uint32_t msg_type;
561
562         msg_type = ((const struct vmbus_chanmsg_hdr *)msg->msg_data)->chm_type;
563         if (msg_type >= HV_CHANNEL_MESSAGE_COUNT) {
564                 device_printf(sc->vmbus_dev, "unknown message type 0x%x\n",
565                     msg_type);
566                 return;
567         }
568
569         msg_proc = vmbus_chanmsg_process[msg_type];
570         if (msg_proc != NULL)
571                 msg_proc(sc, msg);
572 }