]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - sys/dev/hyperv/vmbus/hv_channel_mgmt.c
MFC 302733,302737,302801-302806
[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 struct vmbus_chanmsg_choffer *);
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_offers_delivered(struct vmbus_softc *,
54                     const struct vmbus_message *);
55 static void     vmbus_chan_msgproc_chrescind(struct vmbus_softc *,
56                     const struct vmbus_message *);
57
58 /*
59  * Vmbus channel message processing.
60  */
61
62 #define VMBUS_CHANMSG_PROC(name, func)  \
63         [VMBUS_CHANMSG_TYPE_##name] = func
64 #define VMBUS_CHANMSG_PROC_WAKEUP(name) \
65         VMBUS_CHANMSG_PROC(name, vmbus_msghc_wakeup)
66
67 static const vmbus_chanmsg_proc_t
68 vmbus_chanmsg_process[VMBUS_CHANMSG_TYPE_MAX] = {
69         VMBUS_CHANMSG_PROC(CHOFFER,     vmbus_channel_on_offer),
70         VMBUS_CHANMSG_PROC(CHRESCIND,   vmbus_chan_msgproc_chrescind),
71         VMBUS_CHANMSG_PROC(CHOFFER_DONE,vmbus_channel_on_offers_delivered),
72
73         VMBUS_CHANMSG_PROC_WAKEUP(CHOPEN_RESP),
74         VMBUS_CHANMSG_PROC_WAKEUP(GPADL_CONNRESP),
75         VMBUS_CHANMSG_PROC_WAKEUP(GPADL_DISCONNRESP),
76         VMBUS_CHANMSG_PROC_WAKEUP(CONNECT_RESP)
77 };
78
79 #undef VMBUS_CHANMSG_PROC_WAKEUP
80 #undef VMBUS_CHANMSG_PROC
81
82 /**
83  * @brief Allocate and initialize a vmbus channel object
84  */
85 static struct hv_vmbus_channel *
86 hv_vmbus_allocate_channel(struct vmbus_softc *sc)
87 {
88         struct hv_vmbus_channel *channel;
89
90         channel = malloc(sizeof(*channel), M_DEVBUF, M_WAITOK | M_ZERO);
91         channel->vmbus_sc = sc;
92
93         mtx_init(&channel->sc_lock, "vmbus multi channel", NULL, MTX_DEF);
94         TAILQ_INIT(&channel->sc_list_anchor);
95         TASK_INIT(&channel->ch_detach_task, 0, vmbus_chan_detach_task, channel);
96
97         return (channel);
98 }
99
100 /**
101  * @brief Release the resources used by the vmbus channel object
102  */
103 void
104 hv_vmbus_free_vmbus_channel(hv_vmbus_channel* channel)
105 {
106         mtx_destroy(&channel->sc_lock);
107         free(channel, M_DEVBUF);
108 }
109
110 /**
111  * @brief Process the offer by creating a channel/device
112  * associated with this offer
113  */
114 static void
115 vmbus_channel_process_offer(hv_vmbus_channel *new_channel)
116 {
117         struct vmbus_softc *sc = new_channel->vmbus_sc;
118         hv_vmbus_channel*       channel;
119
120         /*
121          * Make sure this is a new offer
122          */
123         mtx_lock(&sc->vmbus_chlist_lock);
124         if (new_channel->ch_id == 0) {
125                 /*
126                  * XXX channel0 will not be processed; skip it.
127                  */
128                 printf("VMBUS: got channel0 offer\n");
129         } else {
130                 sc->vmbus_chmap[new_channel->ch_id] = new_channel;
131         }
132
133         TAILQ_FOREACH(channel, &sc->vmbus_chlist, ch_link) {
134                 if (memcmp(&channel->ch_guid_type, &new_channel->ch_guid_type,
135                     sizeof(struct hyperv_guid)) == 0 &&
136                     memcmp(&channel->ch_guid_inst, &new_channel->ch_guid_inst,
137                     sizeof(struct hyperv_guid)) == 0)
138                         break;
139         }
140
141         if (channel == NULL) {
142                 /* Install the new primary channel */
143                 TAILQ_INSERT_TAIL(&sc->vmbus_chlist, new_channel, ch_link);
144         }
145         mtx_unlock(&sc->vmbus_chlist_lock);
146
147         if (bootverbose) {
148                 char logstr[64];
149
150                 logstr[0] = '\0';
151                 if (channel != NULL) {
152                         snprintf(logstr, sizeof(logstr), ", primary chan%u",
153                             channel->ch_id);
154                 }
155                 device_printf(sc->vmbus_dev, "chan%u subchanid%u offer%s\n",
156                     new_channel->ch_id,
157                     new_channel->ch_subidx, logstr);
158         }
159
160         if (channel != NULL) {
161                 /*
162                  * Check if this is a sub channel.
163                  */
164                 if (new_channel->ch_subidx != 0) {
165                         /*
166                          * It is a sub channel offer, process it.
167                          */
168                         new_channel->primary_channel = channel;
169                         new_channel->ch_dev = channel->ch_dev;
170                         mtx_lock(&channel->sc_lock);
171                         TAILQ_INSERT_TAIL(&channel->sc_list_anchor,
172                             new_channel, sc_list_entry);
173                         mtx_unlock(&channel->sc_lock);
174
175                         /*
176                          * Insert the new channel to the end of the global
177                          * channel list.
178                          *
179                          * NOTE:
180                          * The new sub-channel MUST be inserted AFTER it's
181                          * primary channel, so that the primary channel will
182                          * be found in the above loop for its baby siblings.
183                          */
184                         mtx_lock(&sc->vmbus_chlist_lock);
185                         TAILQ_INSERT_TAIL(&sc->vmbus_chlist, new_channel,
186                             ch_link);
187                         mtx_unlock(&sc->vmbus_chlist_lock);
188
189                         new_channel->state = HV_CHANNEL_OPEN_STATE;
190
191                         /*
192                          * Bump up sub-channel count and notify anyone that is
193                          * interested in this sub-channel, after this sub-channel
194                          * is setup.
195                          */
196                         mtx_lock(&channel->sc_lock);
197                         channel->subchan_cnt++;
198                         mtx_unlock(&channel->sc_lock);
199                         wakeup(channel);
200
201                         return;
202                 }
203
204                 printf("VMBUS: duplicated primary channel%u\n",
205                     new_channel->ch_id);
206                 hv_vmbus_free_vmbus_channel(new_channel);
207                 return;
208         }
209
210         new_channel->state = HV_CHANNEL_OPEN_STATE;
211
212         /*
213          * Add the new device to the bus. This will kick off device-driver
214          * binding which eventually invokes the device driver's AddDevice()
215          * method.
216          *
217          * NOTE:
218          * Error is ignored here; don't have much to do if error really
219          * happens.
220          */
221         hv_vmbus_child_device_register(new_channel);
222 }
223
224 void
225 vmbus_channel_cpu_set(struct hv_vmbus_channel *chan, int cpu)
226 {
227         KASSERT(cpu >= 0 && cpu < mp_ncpus, ("invalid cpu %d", cpu));
228
229         if (chan->vmbus_sc->vmbus_version == VMBUS_VERSION_WS2008 ||
230             chan->vmbus_sc->vmbus_version == VMBUS_VERSION_WIN7) {
231                 /* Only cpu0 is supported */
232                 cpu = 0;
233         }
234
235         chan->target_cpu = cpu;
236         chan->target_vcpu = VMBUS_PCPU_GET(chan->vmbus_sc, vcpuid, cpu);
237
238         if (bootverbose) {
239                 printf("vmbus_chan%u: assigned to cpu%u [vcpu%u]\n",
240                     chan->ch_id,
241                     chan->target_cpu, chan->target_vcpu);
242         }
243 }
244
245 void
246 vmbus_channel_cpu_rr(struct hv_vmbus_channel *chan)
247 {
248         static uint32_t vmbus_chan_nextcpu;
249         int cpu;
250
251         cpu = atomic_fetchadd_int(&vmbus_chan_nextcpu, 1) % mp_ncpus;
252         vmbus_channel_cpu_set(chan, cpu);
253 }
254
255 static void
256 vmbus_channel_select_defcpu(struct hv_vmbus_channel *chan)
257 {
258         /*
259          * By default, pin the channel to cpu0.  Devices having
260          * special channel-cpu mapping requirement should call
261          * vmbus_channel_cpu_{set,rr}().
262          */
263         vmbus_channel_cpu_set(chan, 0);
264 }
265
266 /**
267  * @brief Handler for channel offers from Hyper-V/Azure
268  *
269  * Handler for channel offers from vmbus in parent partition.
270  */
271 static void
272 vmbus_channel_on_offer(struct vmbus_softc *sc, const struct vmbus_message *msg)
273 {
274         /* New channel is offered by vmbus */
275         vmbus_scan_newchan(sc);
276
277         vmbus_channel_on_offer_internal(sc,
278             (const struct vmbus_chanmsg_choffer *)msg->msg_data);
279 }
280
281 static void
282 vmbus_channel_on_offer_internal(struct vmbus_softc *sc,
283     const struct vmbus_chanmsg_choffer *offer)
284 {
285         hv_vmbus_channel* new_channel;
286
287         /*
288          * Allocate the channel object and save this offer
289          */
290         new_channel = hv_vmbus_allocate_channel(sc);
291         new_channel->ch_id = offer->chm_chanid;
292         new_channel->ch_subidx = offer->chm_subidx;
293         new_channel->ch_guid_type = offer->chm_chtype;
294         new_channel->ch_guid_inst = offer->chm_chinst;
295
296         /* Batch reading is on by default */
297         new_channel->ch_flags |= VMBUS_CHAN_FLAG_BATCHREAD;
298         if (offer->chm_flags1 & VMBUS_CHOFFER_FLAG1_HASMNF)
299                 new_channel->ch_flags |= VMBUS_CHAN_FLAG_HASMNF;
300
301         new_channel->ch_monprm = hyperv_dmamem_alloc(
302             bus_get_dma_tag(sc->vmbus_dev),
303             HYPERCALL_PARAM_ALIGN, 0, sizeof(struct hyperv_mon_param),
304             &new_channel->ch_monprm_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO);
305         if (new_channel->ch_monprm == NULL) {
306                 device_printf(sc->vmbus_dev, "monprm alloc failed\n");
307                 /* XXX */
308                 mtx_destroy(&new_channel->sc_lock);
309                 free(new_channel, M_DEVBUF);
310                 return;
311         }
312         new_channel->ch_monprm->mp_connid = VMBUS_CONNID_EVENT;
313         if (sc->vmbus_version != VMBUS_VERSION_WS2008)
314                 new_channel->ch_monprm->mp_connid = offer->chm_connid;
315
316         if (new_channel->ch_flags & VMBUS_CHAN_FLAG_HASMNF) {
317                 new_channel->ch_montrig_idx =
318                     offer->chm_montrig / VMBUS_MONTRIG_LEN;
319                 if (new_channel->ch_montrig_idx >= VMBUS_MONTRIGS_MAX)
320                         panic("invalid monitor trigger %u", offer->chm_montrig);
321                 new_channel->ch_montrig_mask =
322                     1 << (offer->chm_montrig % VMBUS_MONTRIG_LEN);
323         }
324
325         /* Select default cpu for this channel. */
326         vmbus_channel_select_defcpu(new_channel);
327
328         vmbus_channel_process_offer(new_channel);
329 }
330
331 /*
332  * XXX pretty broken; need rework.
333  */
334 static void
335 vmbus_chan_msgproc_chrescind(struct vmbus_softc *sc,
336     const struct vmbus_message *msg)
337 {
338         const struct vmbus_chanmsg_chrescind *note;
339         struct hv_vmbus_channel *chan;
340
341         note = (const struct vmbus_chanmsg_chrescind *)msg->msg_data;
342         if (note->chm_chanid > VMBUS_CHAN_MAX) {
343                 device_printf(sc->vmbus_dev, "invalid rescinded chan%u\n",
344                     note->chm_chanid);
345                 return;
346         }
347
348         if (bootverbose) {
349                 device_printf(sc->vmbus_dev, "chan%u rescinded\n",
350                     note->chm_chanid);
351         }
352
353         chan = sc->vmbus_chmap[note->chm_chanid];
354         if (chan == NULL)
355                 return;
356         sc->vmbus_chmap[note->chm_chanid] = NULL;
357
358         taskqueue_enqueue(taskqueue_thread, &chan->ch_detach_task);
359 }
360
361 static void
362 vmbus_chan_detach_task(void *xchan, int pending __unused)
363 {
364         struct hv_vmbus_channel *chan = xchan;
365
366         if (HV_VMBUS_CHAN_ISPRIMARY(chan)) {
367                 /* Only primary channel owns the device */
368                 hv_vmbus_child_device_unregister(chan);
369                 /* NOTE: DO NOT free primary channel for now */
370         } else {
371                 struct vmbus_softc *sc = chan->vmbus_sc;
372                 struct hv_vmbus_channel *pri_chan = chan->primary_channel;
373                 struct vmbus_chanmsg_chfree *req;
374                 struct vmbus_msghc *mh;
375                 int error;
376
377                 mh = vmbus_msghc_get(sc, sizeof(*req));
378                 if (mh == NULL) {
379                         device_printf(sc->vmbus_dev,
380                             "can not get msg hypercall for chfree(chan%u)\n",
381                             chan->ch_id);
382                         goto remove;
383                 }
384
385                 req = vmbus_msghc_dataptr(mh);
386                 req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHFREE;
387                 req->chm_chanid = chan->ch_id;
388
389                 error = vmbus_msghc_exec_noresult(mh);
390                 vmbus_msghc_put(sc, mh);
391
392                 if (error) {
393                         device_printf(sc->vmbus_dev,
394                             "chfree(chan%u) failed: %d",
395                             chan->ch_id, error);
396                         /* NOTE: Move on! */
397                 } else {
398                         if (bootverbose) {
399                                 device_printf(sc->vmbus_dev, "chan%u freed\n",
400                                     chan->ch_id);
401                         }
402                 }
403 remove:
404                 mtx_lock(&sc->vmbus_chlist_lock);
405                 TAILQ_REMOVE(&sc->vmbus_chlist, chan, ch_link);
406                 mtx_unlock(&sc->vmbus_chlist_lock);
407
408                 mtx_lock(&pri_chan->sc_lock);
409                 TAILQ_REMOVE(&pri_chan->sc_list_anchor, chan, sc_list_entry);
410                 KASSERT(pri_chan->subchan_cnt > 0,
411                     ("invalid subchan_cnt %d", pri_chan->subchan_cnt));
412                 pri_chan->subchan_cnt--;
413                 mtx_unlock(&pri_chan->sc_lock);
414                 wakeup(pri_chan);
415
416                 hv_vmbus_free_vmbus_channel(chan);
417         }
418 }
419
420 /**
421  *
422  * @brief Invoked when all offers have been delivered.
423  */
424 static void
425 vmbus_channel_on_offers_delivered(struct vmbus_softc *sc,
426     const struct vmbus_message *msg __unused)
427 {
428
429         /* No more new channels for the channel request. */
430         vmbus_scan_done(sc);
431 }
432
433 /**
434  * @brief Release channels that are unattached/unconnected (i.e., no drivers associated)
435  */
436 void
437 hv_vmbus_release_unattached_channels(struct vmbus_softc *sc)
438 {
439         hv_vmbus_channel *channel;
440
441         mtx_lock(&sc->vmbus_chlist_lock);
442
443         while (!TAILQ_EMPTY(&sc->vmbus_chlist)) {
444             channel = TAILQ_FIRST(&sc->vmbus_chlist);
445             TAILQ_REMOVE(&sc->vmbus_chlist, channel, ch_link);
446
447             if (HV_VMBUS_CHAN_ISPRIMARY(channel)) {
448                 /* Only primary channel owns the device */
449                 hv_vmbus_child_device_unregister(channel);
450             }
451             hv_vmbus_free_vmbus_channel(channel);
452         }
453         bzero(sc->vmbus_chmap,
454             sizeof(struct hv_vmbus_channel *) * VMBUS_CHAN_MAX);
455
456         mtx_unlock(&sc->vmbus_chlist_lock);
457 }
458
459 /**
460  * @brief Select the best outgoing channel
461  * 
462  * The channel whose vcpu binding is closest to the currect vcpu will
463  * be selected.
464  * If no multi-channel, always select primary channel
465  * 
466  * @param primary - primary channel
467  */
468 struct hv_vmbus_channel *
469 vmbus_select_outgoing_channel(struct hv_vmbus_channel *primary)
470 {
471         hv_vmbus_channel *new_channel = NULL;
472         hv_vmbus_channel *outgoing_channel = primary;
473         int old_cpu_distance = 0;
474         int new_cpu_distance = 0;
475         int cur_vcpu = 0;
476         int smp_pro_id = PCPU_GET(cpuid);
477
478         if (TAILQ_EMPTY(&primary->sc_list_anchor)) {
479                 return outgoing_channel;
480         }
481
482         if (smp_pro_id >= MAXCPU) {
483                 return outgoing_channel;
484         }
485
486         cur_vcpu = VMBUS_PCPU_GET(primary->vmbus_sc, vcpuid, smp_pro_id);
487         
488         TAILQ_FOREACH(new_channel, &primary->sc_list_anchor, sc_list_entry) {
489                 if (new_channel->state != HV_CHANNEL_OPENED_STATE){
490                         continue;
491                 }
492
493                 if (new_channel->target_vcpu == cur_vcpu){
494                         return new_channel;
495                 }
496
497                 old_cpu_distance = ((outgoing_channel->target_vcpu > cur_vcpu) ?
498                     (outgoing_channel->target_vcpu - cur_vcpu) :
499                     (cur_vcpu - outgoing_channel->target_vcpu));
500
501                 new_cpu_distance = ((new_channel->target_vcpu > cur_vcpu) ?
502                     (new_channel->target_vcpu - cur_vcpu) :
503                     (cur_vcpu - new_channel->target_vcpu));
504
505                 if (old_cpu_distance < new_cpu_distance) {
506                         continue;
507                 }
508
509                 outgoing_channel = new_channel;
510         }
511
512         return(outgoing_channel);
513 }
514
515 struct hv_vmbus_channel **
516 vmbus_get_subchan(struct hv_vmbus_channel *pri_chan, int subchan_cnt)
517 {
518         struct hv_vmbus_channel **ret, *chan;
519         int i;
520
521         ret = malloc(subchan_cnt * sizeof(struct hv_vmbus_channel *), M_TEMP,
522             M_WAITOK);
523
524         mtx_lock(&pri_chan->sc_lock);
525
526         while (pri_chan->subchan_cnt < subchan_cnt)
527                 mtx_sleep(pri_chan, &pri_chan->sc_lock, 0, "subch", 0);
528
529         i = 0;
530         TAILQ_FOREACH(chan, &pri_chan->sc_list_anchor, sc_list_entry) {
531                 /* TODO: refcnt chan */
532                 ret[i] = chan;
533
534                 ++i;
535                 if (i == subchan_cnt)
536                         break;
537         }
538         KASSERT(i == subchan_cnt, ("invalid subchan count %d, should be %d",
539             pri_chan->subchan_cnt, subchan_cnt));
540
541         mtx_unlock(&pri_chan->sc_lock);
542
543         return ret;
544 }
545
546 void
547 vmbus_rel_subchan(struct hv_vmbus_channel **subchan, int subchan_cnt __unused)
548 {
549
550         free(subchan, M_TEMP);
551 }
552
553 void
554 vmbus_drain_subchan(struct hv_vmbus_channel *pri_chan)
555 {
556         mtx_lock(&pri_chan->sc_lock);
557         while (pri_chan->subchan_cnt > 0)
558                 mtx_sleep(pri_chan, &pri_chan->sc_lock, 0, "dsubch", 0);
559         mtx_unlock(&pri_chan->sc_lock);
560 }
561
562 void
563 vmbus_chan_msgproc(struct vmbus_softc *sc, const struct vmbus_message *msg)
564 {
565         vmbus_chanmsg_proc_t msg_proc;
566         uint32_t msg_type;
567
568         msg_type = ((const struct vmbus_chanmsg_hdr *)msg->msg_data)->chm_type;
569         if (msg_type >= VMBUS_CHANMSG_TYPE_MAX) {
570                 device_printf(sc->vmbus_dev, "unknown message type 0x%x\n",
571                     msg_type);
572                 return;
573         }
574
575         msg_proc = vmbus_chanmsg_process[msg_type];
576         if (msg_proc != NULL)
577                 msg_proc(sc, msg);
578 }