xref: /freebsd/sys/dev/hyperv/vmbus/vmbus_xact.c (revision 0957b409)
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 }
443