xref: /freebsd/sys/dev/hyperv/vmbus/vmbus_xact.c (revision e6bfd18d21b225af6a0ed67ceeaf1293b7b9eba5)
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 <vm/vm.h>
38 #include <vm/vm_extern.h>
39 #include <vm/pmap.h>
40 
41 #include <dev/hyperv/include/vmbus_xact.h>
42 
43 struct vmbus_xact {
44 	struct vmbus_xact_ctx		*x_ctx;
45 	void				*x_priv;
46 
47 	void				*x_req;
48 
49 	const void			*x_resp;
50 	size_t				x_resp_len;
51 	void				*x_resp0;
52 };
53 
54 struct vmbus_xact_ctx {
55 	size_t				xc_req_size;
56 	size_t				xc_resp_size;
57 	size_t				xc_priv_size;
58 
59 	struct mtx			xc_lock;
60 	/*
61 	 * Protected by xc_lock.
62 	 */
63 	uint32_t			xc_flags;	/* VMBUS_XACT_CTXF_ */
64 	struct vmbus_xact		*xc_free;
65 	struct vmbus_xact		*xc_active;
66 	struct vmbus_xact		*xc_orphan;
67 };
68 
69 #define VMBUS_XACT_CTXF_DESTROY		0x0001
70 
71 static struct vmbus_xact	*vmbus_xact_alloc(struct vmbus_xact_ctx *,
72 				    bus_dma_tag_t);
73 static void			vmbus_xact_free(struct vmbus_xact *);
74 static struct vmbus_xact	*vmbus_xact_get1(struct vmbus_xact_ctx *,
75 				    uint32_t);
76 static const void		*vmbus_xact_wait1(struct vmbus_xact *, size_t *,
77 				    bool);
78 static const void		*vmbus_xact_return(struct vmbus_xact *,
79 				    size_t *);
80 static void			vmbus_xact_save_resp(struct vmbus_xact *,
81 				    const void *, size_t);
82 static void			vmbus_xact_ctx_free(struct vmbus_xact_ctx *);
83 
84 static struct vmbus_xact *
85 vmbus_xact_alloc(struct vmbus_xact_ctx *ctx, bus_dma_tag_t parent_dtag)
86 {
87 	struct vmbus_xact *xact;
88 
89 	xact = malloc(sizeof(*xact), M_DEVBUF, M_WAITOK | M_ZERO);
90 	xact->x_ctx = ctx;
91 
92 	/* XXX assume that page aligned is enough */
93 	xact->x_req = contigmalloc(ctx->xc_req_size, M_DEVBUF,
94 	    M_WAITOK | M_ZERO, 0ul, ~0ul, PAGE_SIZE, 0);
95 	if (xact->x_req == NULL) {
96 		free(xact, M_DEVBUF);
97 		return (NULL);
98 	}
99 	if (ctx->xc_priv_size != 0)
100 		xact->x_priv = malloc(ctx->xc_priv_size, M_DEVBUF, M_WAITOK);
101 	xact->x_resp0 = malloc(ctx->xc_resp_size, M_DEVBUF, M_WAITOK);
102 
103 	return (xact);
104 }
105 
106 static void
107 vmbus_xact_free(struct vmbus_xact *xact)
108 {
109 
110 	contigfree(xact->x_req, xact->x_ctx->xc_req_size, M_DEVBUF);
111 	free(xact->x_resp0, M_DEVBUF);
112 	if (xact->x_priv != NULL)
113 		free(xact->x_priv, M_DEVBUF);
114 	free(xact, M_DEVBUF);
115 }
116 
117 static struct vmbus_xact *
118 vmbus_xact_get1(struct vmbus_xact_ctx *ctx, uint32_t dtor_flag)
119 {
120 	struct vmbus_xact *xact;
121 
122 	mtx_lock(&ctx->xc_lock);
123 
124 	while ((ctx->xc_flags & dtor_flag) == 0 && ctx->xc_free == NULL)
125 		mtx_sleep(&ctx->xc_free, &ctx->xc_lock, 0, "gxact", 0);
126 	if (ctx->xc_flags & dtor_flag) {
127 		/* Being destroyed */
128 		xact = NULL;
129 	} else {
130 		xact = ctx->xc_free;
131 		KASSERT(xact != NULL, ("no free xact"));
132 		KASSERT(xact->x_resp == NULL, ("xact has pending response"));
133 		ctx->xc_free = NULL;
134 	}
135 
136 	mtx_unlock(&ctx->xc_lock);
137 
138 	return (xact);
139 }
140 
141 struct vmbus_xact_ctx *
142 vmbus_xact_ctx_create(bus_dma_tag_t dtag, size_t req_size, size_t resp_size,
143     size_t priv_size)
144 {
145 	struct vmbus_xact_ctx *ctx;
146 
147 	KASSERT(req_size > 0, ("request size is 0"));
148 	KASSERT(resp_size > 0, ("response size is 0"));
149 
150 	ctx = malloc(sizeof(*ctx), M_DEVBUF, M_WAITOK | M_ZERO);
151 	ctx->xc_req_size = req_size;
152 	ctx->xc_resp_size = resp_size;
153 	ctx->xc_priv_size = priv_size;
154 
155 	ctx->xc_free = vmbus_xact_alloc(ctx, dtag);
156 	if (ctx->xc_free == NULL) {
157 		free(ctx, M_DEVBUF);
158 		return (NULL);
159 	}
160 
161 	mtx_init(&ctx->xc_lock, "vmbus xact", NULL, MTX_DEF);
162 
163 	return (ctx);
164 }
165 
166 bool
167 vmbus_xact_ctx_orphan(struct vmbus_xact_ctx *ctx)
168 {
169 	mtx_lock(&ctx->xc_lock);
170 	if (ctx->xc_flags & VMBUS_XACT_CTXF_DESTROY) {
171 		mtx_unlock(&ctx->xc_lock);
172 		return (false);
173 	}
174 	ctx->xc_flags |= VMBUS_XACT_CTXF_DESTROY;
175 	mtx_unlock(&ctx->xc_lock);
176 
177 	wakeup(&ctx->xc_free);
178 	wakeup(&ctx->xc_active);
179 
180 	ctx->xc_orphan = vmbus_xact_get1(ctx, 0);
181 	if (ctx->xc_orphan == NULL)
182 		panic("can't get xact");
183 	return (true);
184 }
185 
186 static void
187 vmbus_xact_ctx_free(struct vmbus_xact_ctx *ctx)
188 {
189 	KASSERT(ctx->xc_flags & VMBUS_XACT_CTXF_DESTROY,
190 	    ("xact ctx was not orphaned"));
191 	KASSERT(ctx->xc_orphan != NULL, ("no orphaned xact"));
192 
193 	vmbus_xact_free(ctx->xc_orphan);
194 	mtx_destroy(&ctx->xc_lock);
195 	free(ctx, M_DEVBUF);
196 }
197 
198 void
199 vmbus_xact_ctx_destroy(struct vmbus_xact_ctx *ctx)
200 {
201 
202 	vmbus_xact_ctx_orphan(ctx);
203 	vmbus_xact_ctx_free(ctx);
204 }
205 
206 struct vmbus_xact *
207 vmbus_xact_get(struct vmbus_xact_ctx *ctx, size_t req_len)
208 {
209 	struct vmbus_xact *xact;
210 
211 	if (req_len > ctx->xc_req_size)
212 		panic("invalid request size %zu", req_len);
213 
214 	xact = vmbus_xact_get1(ctx, VMBUS_XACT_CTXF_DESTROY);
215 	if (xact == NULL)
216 		return (NULL);
217 
218 	memset(xact->x_req, 0, req_len);
219 	return (xact);
220 }
221 
222 void
223 vmbus_xact_put(struct vmbus_xact *xact)
224 {
225 	struct vmbus_xact_ctx *ctx = xact->x_ctx;
226 
227 	KASSERT(ctx->xc_active == NULL, ("pending active xact"));
228 	xact->x_resp = NULL;
229 
230 	mtx_lock(&ctx->xc_lock);
231 	KASSERT(ctx->xc_free == NULL, ("has free xact"));
232 	ctx->xc_free = xact;
233 	mtx_unlock(&ctx->xc_lock);
234 	wakeup(&ctx->xc_free);
235 }
236 
237 void *
238 vmbus_xact_req_data(const struct vmbus_xact *xact)
239 {
240 
241 	return (xact->x_req);
242 }
243 
244 bus_addr_t
245 vmbus_xact_req_paddr(const struct vmbus_xact *xact)
246 {
247 
248 	return (pmap_kextract((vm_offset_t)xact->x_req));
249 }
250 
251 void *
252 vmbus_xact_priv(const struct vmbus_xact *xact, size_t priv_len)
253 {
254 
255 	if (priv_len > xact->x_ctx->xc_priv_size)
256 		panic("invalid priv size %zu", priv_len);
257 	return (xact->x_priv);
258 }
259 
260 void
261 vmbus_xact_activate(struct vmbus_xact *xact)
262 {
263 	struct vmbus_xact_ctx *ctx = xact->x_ctx;
264 
265 	KASSERT(xact->x_resp == NULL, ("xact has pending response"));
266 
267 	mtx_lock(&ctx->xc_lock);
268 	KASSERT(ctx->xc_active == NULL, ("pending active xact"));
269 	ctx->xc_active = xact;
270 	mtx_unlock(&ctx->xc_lock);
271 }
272 
273 void
274 vmbus_xact_deactivate(struct vmbus_xact *xact)
275 {
276 	struct vmbus_xact_ctx *ctx = xact->x_ctx;
277 
278 	mtx_lock(&ctx->xc_lock);
279 	KASSERT(ctx->xc_active == xact, ("xact mismatch"));
280 	ctx->xc_active = NULL;
281 	mtx_unlock(&ctx->xc_lock);
282 }
283 
284 static const void *
285 vmbus_xact_return(struct vmbus_xact *xact, size_t *resp_len)
286 {
287 	struct vmbus_xact_ctx *ctx = xact->x_ctx;
288 	const void *resp;
289 
290 	mtx_assert(&ctx->xc_lock, MA_OWNED);
291 	KASSERT(ctx->xc_active == xact, ("xact trashed"));
292 
293 	if ((ctx->xc_flags & VMBUS_XACT_CTXF_DESTROY) && xact->x_resp == NULL) {
294 		uint8_t b = 0;
295 
296 		/*
297 		 * Orphaned and no response was received yet; fake up
298 		 * an one byte response.
299 		 */
300 		printf("vmbus: xact ctx was orphaned w/ pending xact\n");
301 		vmbus_xact_save_resp(ctx->xc_active, &b, sizeof(b));
302 	}
303 	KASSERT(xact->x_resp != NULL, ("no response"));
304 
305 	ctx->xc_active = NULL;
306 
307 	resp = xact->x_resp;
308 	*resp_len = xact->x_resp_len;
309 
310 	return (resp);
311 }
312 
313 static const void *
314 vmbus_xact_wait1(struct vmbus_xact *xact, size_t *resp_len,
315     bool can_sleep)
316 {
317 	struct vmbus_xact_ctx *ctx = xact->x_ctx;
318 	const void *resp;
319 
320 	mtx_lock(&ctx->xc_lock);
321 
322 	KASSERT(ctx->xc_active == xact, ("xact mismatch"));
323 	while (xact->x_resp == NULL &&
324 	    (ctx->xc_flags & VMBUS_XACT_CTXF_DESTROY) == 0) {
325 		if (can_sleep) {
326 			mtx_sleep(&ctx->xc_active, &ctx->xc_lock, 0,
327 			    "wxact", 0);
328 		} else {
329 			mtx_unlock(&ctx->xc_lock);
330 			DELAY(1000);
331 			mtx_lock(&ctx->xc_lock);
332 		}
333 	}
334 	resp = vmbus_xact_return(xact, resp_len);
335 
336 	mtx_unlock(&ctx->xc_lock);
337 
338 	return (resp);
339 }
340 
341 const void *
342 vmbus_xact_wait(struct vmbus_xact *xact, size_t *resp_len)
343 {
344 
345 	return (vmbus_xact_wait1(xact, resp_len, true /* can sleep */));
346 }
347 
348 const void *
349 vmbus_xact_busywait(struct vmbus_xact *xact, size_t *resp_len)
350 {
351 
352 	return (vmbus_xact_wait1(xact, resp_len, false /* can't sleep */));
353 }
354 
355 const void *
356 vmbus_xact_poll(struct vmbus_xact *xact, size_t *resp_len)
357 {
358 	struct vmbus_xact_ctx *ctx = xact->x_ctx;
359 	const void *resp;
360 
361 	mtx_lock(&ctx->xc_lock);
362 
363 	KASSERT(ctx->xc_active == xact, ("xact mismatch"));
364 	if (xact->x_resp == NULL &&
365 	    (ctx->xc_flags & VMBUS_XACT_CTXF_DESTROY) == 0) {
366 		mtx_unlock(&ctx->xc_lock);
367 		*resp_len = 0;
368 		return (NULL);
369 	}
370 	resp = vmbus_xact_return(xact, resp_len);
371 
372 	mtx_unlock(&ctx->xc_lock);
373 
374 	return (resp);
375 }
376 
377 static void
378 vmbus_xact_save_resp(struct vmbus_xact *xact, const void *data, size_t dlen)
379 {
380 	struct vmbus_xact_ctx *ctx = xact->x_ctx;
381 	size_t cplen = dlen;
382 
383 	mtx_assert(&ctx->xc_lock, MA_OWNED);
384 
385 	if (cplen > ctx->xc_resp_size) {
386 		printf("vmbus: xact response truncated %zu -> %zu\n",
387 		    cplen, ctx->xc_resp_size);
388 		cplen = ctx->xc_resp_size;
389 	}
390 
391 	KASSERT(ctx->xc_active == xact, ("xact mismatch"));
392 	memcpy(xact->x_resp0, data, cplen);
393 	xact->x_resp_len = cplen;
394 	xact->x_resp = xact->x_resp0;
395 }
396 
397 void
398 vmbus_xact_wakeup(struct vmbus_xact *xact, const void *data, size_t dlen)
399 {
400 	struct vmbus_xact_ctx *ctx = xact->x_ctx;
401 	int do_wakeup = 0;
402 
403 	mtx_lock(&ctx->xc_lock);
404 	/*
405 	 * NOTE:
406 	 * xc_active could be NULL, if the ctx has been orphaned.
407 	 */
408 	if (ctx->xc_active != NULL) {
409 		vmbus_xact_save_resp(xact, data, dlen);
410 		do_wakeup = 1;
411 	} else {
412 		KASSERT(ctx->xc_flags & VMBUS_XACT_CTXF_DESTROY,
413 		    ("no active xact pending"));
414 		printf("vmbus: drop xact response\n");
415 	}
416 	mtx_unlock(&ctx->xc_lock);
417 
418 	if (do_wakeup)
419 		wakeup(&ctx->xc_active);
420 }
421 
422 void
423 vmbus_xact_ctx_wakeup(struct vmbus_xact_ctx *ctx, const void *data, size_t dlen)
424 {
425 	int do_wakeup = 0;
426 
427 	mtx_lock(&ctx->xc_lock);
428 	/*
429 	 * NOTE:
430 	 * xc_active could be NULL, if the ctx has been orphaned.
431 	 */
432 	if (ctx->xc_active != NULL) {
433 		vmbus_xact_save_resp(ctx->xc_active, data, dlen);
434 		do_wakeup = 1;
435 	} else {
436 		KASSERT(ctx->xc_flags & VMBUS_XACT_CTXF_DESTROY,
437 		    ("no active xact pending"));
438 		printf("vmbus: drop xact response\n");
439 	}
440 	mtx_unlock(&ctx->xc_lock);
441 
442 	if (do_wakeup)
443 		wakeup(&ctx->xc_active);
444 }
445