]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/hyperv/vmbus/vmbus_xact.c
arm64: Hyper-V: making INTR MP Safe
[FreeBSD/FreeBSD.git] / sys / dev / hyperv / vmbus / vmbus_xact.c
1 /*-
2  * Copyright (c) 2016 Microsoft Corp.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice unmodified, this list of conditions, and the following
10  *    disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/lock.h>
32 #include <sys/malloc.h>
33 #include <sys/mutex.h>
34 #include <sys/proc.h>
35 #include <sys/systm.h>
36
37 #include <dev/hyperv/include/hyperv_busdma.h>
38 #include <dev/hyperv/include/vmbus_xact.h>
39
40 struct vmbus_xact {
41         struct vmbus_xact_ctx           *x_ctx;
42         void                            *x_priv;
43
44         void                            *x_req;
45         struct hyperv_dma               x_req_dma;
46
47         const void                      *x_resp;
48         size_t                          x_resp_len;
49         void                            *x_resp0;
50 };
51
52 struct vmbus_xact_ctx {
53         size_t                          xc_req_size;
54         size_t                          xc_resp_size;
55         size_t                          xc_priv_size;
56
57         struct mtx                      xc_lock;
58         /*
59          * Protected by xc_lock.
60          */
61         uint32_t                        xc_flags;       /* VMBUS_XACT_CTXF_ */
62         struct vmbus_xact               *xc_free;
63         struct vmbus_xact               *xc_active;
64         struct vmbus_xact               *xc_orphan;
65 };
66
67 #define VMBUS_XACT_CTXF_DESTROY         0x0001
68
69 static struct vmbus_xact        *vmbus_xact_alloc(struct vmbus_xact_ctx *,
70                                     bus_dma_tag_t);
71 static void                     vmbus_xact_free(struct vmbus_xact *);
72 static struct vmbus_xact        *vmbus_xact_get1(struct vmbus_xact_ctx *,
73                                     uint32_t);
74 static const void               *vmbus_xact_wait1(struct vmbus_xact *, size_t *,
75                                     bool);
76 static const void               *vmbus_xact_return(struct vmbus_xact *,
77                                     size_t *);
78 static void                     vmbus_xact_save_resp(struct vmbus_xact *,
79                                     const void *, size_t);
80 static void                     vmbus_xact_ctx_free(struct vmbus_xact_ctx *);
81
82 static struct vmbus_xact *
83 vmbus_xact_alloc(struct vmbus_xact_ctx *ctx, bus_dma_tag_t parent_dtag)
84 {
85         struct vmbus_xact *xact;
86
87         xact = malloc(sizeof(*xact), M_DEVBUF, M_WAITOK | M_ZERO);
88         xact->x_ctx = ctx;
89
90         /* XXX assume that page aligned is enough */
91         xact->x_req = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0,
92             ctx->xc_req_size, &xact->x_req_dma, BUS_DMA_WAITOK);
93         if (xact->x_req == NULL) {
94                 free(xact, M_DEVBUF);
95                 return (NULL);
96         }
97         if (ctx->xc_priv_size != 0)
98                 xact->x_priv = malloc(ctx->xc_priv_size, M_DEVBUF, M_WAITOK);
99         xact->x_resp0 = malloc(ctx->xc_resp_size, M_DEVBUF, M_WAITOK);
100
101         return (xact);
102 }
103
104 static void
105 vmbus_xact_free(struct vmbus_xact *xact)
106 {
107
108         hyperv_dmamem_free(&xact->x_req_dma, xact->x_req);
109         free(xact->x_resp0, M_DEVBUF);
110         if (xact->x_priv != NULL)
111                 free(xact->x_priv, M_DEVBUF);
112         free(xact, M_DEVBUF);
113 }
114
115 static struct vmbus_xact *
116 vmbus_xact_get1(struct vmbus_xact_ctx *ctx, uint32_t dtor_flag)
117 {
118         struct vmbus_xact *xact;
119
120         mtx_lock(&ctx->xc_lock);
121
122         while ((ctx->xc_flags & dtor_flag) == 0 && ctx->xc_free == NULL)
123                 mtx_sleep(&ctx->xc_free, &ctx->xc_lock, 0, "gxact", 0);
124         if (ctx->xc_flags & dtor_flag) {
125                 /* Being destroyed */
126                 xact = NULL;
127         } else {
128                 xact = ctx->xc_free;
129                 KASSERT(xact != NULL, ("no free xact"));
130                 KASSERT(xact->x_resp == NULL, ("xact has pending response"));
131                 ctx->xc_free = NULL;
132         }
133
134         mtx_unlock(&ctx->xc_lock);
135
136         return (xact);
137 }
138
139 struct vmbus_xact_ctx *
140 vmbus_xact_ctx_create(bus_dma_tag_t dtag, size_t req_size, size_t resp_size,
141     size_t priv_size)
142 {
143         struct vmbus_xact_ctx *ctx;
144
145         KASSERT(req_size > 0, ("request size is 0"));
146         KASSERT(resp_size > 0, ("response size is 0"));
147
148         ctx = malloc(sizeof(*ctx), M_DEVBUF, M_WAITOK | M_ZERO);
149         ctx->xc_req_size = req_size;
150         ctx->xc_resp_size = resp_size;
151         ctx->xc_priv_size = priv_size;
152
153         ctx->xc_free = vmbus_xact_alloc(ctx, dtag);
154         if (ctx->xc_free == NULL) {
155                 free(ctx, M_DEVBUF);
156                 return (NULL);
157         }
158
159         mtx_init(&ctx->xc_lock, "vmbus xact", NULL, MTX_DEF);
160
161         return (ctx);
162 }
163
164 bool
165 vmbus_xact_ctx_orphan(struct vmbus_xact_ctx *ctx)
166 {
167         mtx_lock(&ctx->xc_lock);
168         if (ctx->xc_flags & VMBUS_XACT_CTXF_DESTROY) {
169                 mtx_unlock(&ctx->xc_lock);
170                 return (false);
171         }
172         ctx->xc_flags |= VMBUS_XACT_CTXF_DESTROY;
173         mtx_unlock(&ctx->xc_lock);
174
175         wakeup(&ctx->xc_free);
176         wakeup(&ctx->xc_active);
177
178         ctx->xc_orphan = vmbus_xact_get1(ctx, 0);
179         if (ctx->xc_orphan == NULL)
180                 panic("can't get xact");
181         return (true);
182 }
183
184 static void
185 vmbus_xact_ctx_free(struct vmbus_xact_ctx *ctx)
186 {
187         KASSERT(ctx->xc_flags & VMBUS_XACT_CTXF_DESTROY,
188             ("xact ctx was not orphaned"));
189         KASSERT(ctx->xc_orphan != NULL, ("no orphaned xact"));
190
191         vmbus_xact_free(ctx->xc_orphan);
192         mtx_destroy(&ctx->xc_lock);
193         free(ctx, M_DEVBUF);
194 }
195
196 void
197 vmbus_xact_ctx_destroy(struct vmbus_xact_ctx *ctx)
198 {
199
200         vmbus_xact_ctx_orphan(ctx);
201         vmbus_xact_ctx_free(ctx);
202 }
203
204 struct vmbus_xact *
205 vmbus_xact_get(struct vmbus_xact_ctx *ctx, size_t req_len)
206 {
207         struct vmbus_xact *xact;
208
209         if (req_len > ctx->xc_req_size)
210                 panic("invalid request size %zu", req_len);
211
212         xact = vmbus_xact_get1(ctx, VMBUS_XACT_CTXF_DESTROY);
213         if (xact == NULL)
214                 return (NULL);
215
216         memset(xact->x_req, 0, req_len);
217         return (xact);
218 }
219
220 void
221 vmbus_xact_put(struct vmbus_xact *xact)
222 {
223         struct vmbus_xact_ctx *ctx = xact->x_ctx;
224
225         KASSERT(ctx->xc_active == NULL, ("pending active xact"));
226         xact->x_resp = NULL;
227
228         mtx_lock(&ctx->xc_lock);
229         KASSERT(ctx->xc_free == NULL, ("has free xact"));
230         ctx->xc_free = xact;
231         mtx_unlock(&ctx->xc_lock);
232         wakeup(&ctx->xc_free);
233 }
234
235 void *
236 vmbus_xact_req_data(const struct vmbus_xact *xact)
237 {
238
239         return (xact->x_req);
240 }
241
242 bus_addr_t
243 vmbus_xact_req_paddr(const struct vmbus_xact *xact)
244 {
245
246         return (xact->x_req_dma.hv_paddr);
247 }
248
249 void *
250 vmbus_xact_priv(const struct vmbus_xact *xact, size_t priv_len)
251 {
252
253         if (priv_len > xact->x_ctx->xc_priv_size)
254                 panic("invalid priv size %zu", priv_len);
255         return (xact->x_priv);
256 }
257
258 void
259 vmbus_xact_activate(struct vmbus_xact *xact)
260 {
261         struct vmbus_xact_ctx *ctx = xact->x_ctx;
262
263         KASSERT(xact->x_resp == NULL, ("xact has pending response"));
264
265         mtx_lock(&ctx->xc_lock);
266         KASSERT(ctx->xc_active == NULL, ("pending active xact"));
267         ctx->xc_active = xact;
268         mtx_unlock(&ctx->xc_lock);
269 }
270
271 void
272 vmbus_xact_deactivate(struct vmbus_xact *xact)
273 {
274         struct vmbus_xact_ctx *ctx = xact->x_ctx;
275
276         mtx_lock(&ctx->xc_lock);
277         KASSERT(ctx->xc_active == xact, ("xact mismatch"));
278         ctx->xc_active = NULL;
279         mtx_unlock(&ctx->xc_lock);
280 }
281
282 static const void *
283 vmbus_xact_return(struct vmbus_xact *xact, size_t *resp_len)
284 {
285         struct vmbus_xact_ctx *ctx = xact->x_ctx;
286         const void *resp;
287
288         mtx_assert(&ctx->xc_lock, MA_OWNED);
289         KASSERT(ctx->xc_active == xact, ("xact trashed"));
290
291         if ((ctx->xc_flags & VMBUS_XACT_CTXF_DESTROY) && xact->x_resp == NULL) {
292                 uint8_t b = 0;
293
294                 /*
295                  * Orphaned and no response was received yet; fake up
296                  * an one byte response.
297                  */
298                 printf("vmbus: xact ctx was orphaned w/ pending xact\n");
299                 vmbus_xact_save_resp(ctx->xc_active, &b, sizeof(b));
300         }
301         KASSERT(xact->x_resp != NULL, ("no response"));
302
303         ctx->xc_active = NULL;
304
305         resp = xact->x_resp;
306         *resp_len = xact->x_resp_len;
307
308         return (resp);
309 }
310
311 static const void *
312 vmbus_xact_wait1(struct vmbus_xact *xact, size_t *resp_len,
313     bool can_sleep)
314 {
315         struct vmbus_xact_ctx *ctx = xact->x_ctx;
316         const void *resp;
317
318         mtx_lock(&ctx->xc_lock);
319
320         KASSERT(ctx->xc_active == xact, ("xact mismatch"));
321         while (xact->x_resp == NULL &&
322             (ctx->xc_flags & VMBUS_XACT_CTXF_DESTROY) == 0) {
323                 if (can_sleep) {
324                         mtx_sleep(&ctx->xc_active, &ctx->xc_lock, 0,
325                             "wxact", 0);
326                 } else {
327                         mtx_unlock(&ctx->xc_lock);
328                         DELAY(1000);
329                         mtx_lock(&ctx->xc_lock);
330                 }
331         }
332         resp = vmbus_xact_return(xact, resp_len);
333
334         mtx_unlock(&ctx->xc_lock);
335
336         return (resp);
337 }
338
339 const void *
340 vmbus_xact_wait(struct vmbus_xact *xact, size_t *resp_len)
341 {
342
343         return (vmbus_xact_wait1(xact, resp_len, true /* can sleep */));
344 }
345
346 const void *
347 vmbus_xact_busywait(struct vmbus_xact *xact, size_t *resp_len)
348 {
349
350         return (vmbus_xact_wait1(xact, resp_len, false /* can't sleep */));
351 }
352
353 const void *
354 vmbus_xact_poll(struct vmbus_xact *xact, size_t *resp_len)
355 {
356         struct vmbus_xact_ctx *ctx = xact->x_ctx;
357         const void *resp;
358
359         mtx_lock(&ctx->xc_lock);
360
361         KASSERT(ctx->xc_active == xact, ("xact mismatch"));
362         if (xact->x_resp == NULL &&
363             (ctx->xc_flags & VMBUS_XACT_CTXF_DESTROY) == 0) {
364                 mtx_unlock(&ctx->xc_lock);
365                 *resp_len = 0;
366                 return (NULL);
367         }
368         resp = vmbus_xact_return(xact, resp_len);
369
370         mtx_unlock(&ctx->xc_lock);
371
372         return (resp);
373 }
374
375 static void
376 vmbus_xact_save_resp(struct vmbus_xact *xact, const void *data, size_t dlen)
377 {
378         struct vmbus_xact_ctx *ctx = xact->x_ctx;
379         size_t cplen = dlen;
380
381         mtx_assert(&ctx->xc_lock, MA_OWNED);
382
383         if (cplen > ctx->xc_resp_size) {
384                 printf("vmbus: xact response truncated %zu -> %zu\n",
385                     cplen, ctx->xc_resp_size);
386                 cplen = ctx->xc_resp_size;
387         }
388
389         KASSERT(ctx->xc_active == xact, ("xact mismatch"));
390         memcpy(xact->x_resp0, data, cplen);
391         xact->x_resp_len = cplen;
392         xact->x_resp = xact->x_resp0;
393 }
394
395 void
396 vmbus_xact_wakeup(struct vmbus_xact *xact, const void *data, size_t dlen)
397 {
398         struct vmbus_xact_ctx *ctx = xact->x_ctx;
399         int do_wakeup = 0;
400
401         mtx_lock(&ctx->xc_lock);
402         /*
403          * NOTE:
404          * xc_active could be NULL, if the ctx has been orphaned.
405          */
406         if (ctx->xc_active != NULL) {
407                 vmbus_xact_save_resp(xact, data, dlen);
408                 do_wakeup = 1;
409         } else {
410                 KASSERT(ctx->xc_flags & VMBUS_XACT_CTXF_DESTROY,
411                     ("no active xact pending"));
412                 printf("vmbus: drop xact response\n");
413         }
414         mtx_unlock(&ctx->xc_lock);
415
416         if (do_wakeup)
417                 wakeup(&ctx->xc_active);
418 }
419
420 void
421 vmbus_xact_ctx_wakeup(struct vmbus_xact_ctx *ctx, const void *data, size_t dlen)
422 {
423         int do_wakeup = 0;
424
425         mtx_lock(&ctx->xc_lock);
426         /*
427          * NOTE:
428          * xc_active could be NULL, if the ctx has been orphaned.
429          */
430         if (ctx->xc_active != NULL) {
431                 vmbus_xact_save_resp(ctx->xc_active, data, dlen);
432                 do_wakeup = 1;
433         } else {
434                 KASSERT(ctx->xc_flags & VMBUS_XACT_CTXF_DESTROY,
435                     ("no active xact pending"));
436                 printf("vmbus: drop xact response\n");
437         }
438         mtx_unlock(&ctx->xc_lock);
439
440         if (do_wakeup)
441                 wakeup(&ctx->xc_active);
442 }