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