vmbus_xact.c revision 307164
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: stable/10/sys/dev/hyperv/vmbus/vmbus_xact.c 307164 2016-10-13 02:28:40Z sephe $");
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
40struct 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
52struct vmbus_xact_ctx {
53	uint32_t			xc_flags;
54	size_t				xc_req_size;
55	size_t				xc_resp_size;
56	size_t				xc_priv_size;
57
58	struct vmbus_xact		*xc_free;
59	struct mtx			xc_free_lock;
60
61	struct vmbus_xact		*xc_active;
62	struct mtx			xc_active_lock;
63};
64
65#define VMBUS_XACT_CTXF_DESTROY		0x0001
66
67static struct vmbus_xact	*vmbus_xact_alloc(struct vmbus_xact_ctx *,
68				    bus_dma_tag_t);
69static void			vmbus_xact_free(struct vmbus_xact *);
70static struct vmbus_xact	*vmbus_xact_get1(struct vmbus_xact_ctx *,
71				    uint32_t);
72
73static struct vmbus_xact *
74vmbus_xact_alloc(struct vmbus_xact_ctx *ctx, bus_dma_tag_t parent_dtag)
75{
76	struct vmbus_xact *xact;
77
78	xact = malloc(sizeof(*xact), M_DEVBUF, M_WAITOK | M_ZERO);
79	xact->x_ctx = ctx;
80
81	/* XXX assume that page aligned is enough */
82	xact->x_req = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0,
83	    ctx->xc_req_size, &xact->x_req_dma, BUS_DMA_WAITOK);
84	if (xact->x_req == NULL) {
85		free(xact, M_DEVBUF);
86		return (NULL);
87	}
88	if (ctx->xc_priv_size != 0)
89		xact->x_priv = malloc(ctx->xc_priv_size, M_DEVBUF, M_WAITOK);
90	xact->x_resp0 = malloc(ctx->xc_resp_size, M_DEVBUF, M_WAITOK);
91
92	return (xact);
93}
94
95static void
96vmbus_xact_free(struct vmbus_xact *xact)
97{
98
99	hyperv_dmamem_free(&xact->x_req_dma, xact->x_req);
100	free(xact->x_resp0, M_DEVBUF);
101	if (xact->x_priv != NULL)
102		free(xact->x_priv, M_DEVBUF);
103	free(xact, M_DEVBUF);
104}
105
106static struct vmbus_xact *
107vmbus_xact_get1(struct vmbus_xact_ctx *ctx, uint32_t dtor_flag)
108{
109	struct vmbus_xact *xact;
110
111	mtx_lock(&ctx->xc_free_lock);
112
113	while ((ctx->xc_flags & dtor_flag) == 0 && ctx->xc_free == NULL)
114		mtx_sleep(&ctx->xc_free, &ctx->xc_free_lock, 0, "gxact", 0);
115	if (ctx->xc_flags & dtor_flag) {
116		/* Being destroyed */
117		xact = NULL;
118	} else {
119		xact = ctx->xc_free;
120		KASSERT(xact != NULL, ("no free xact"));
121		KASSERT(xact->x_resp == NULL, ("xact has pending response"));
122		ctx->xc_free = NULL;
123	}
124
125	mtx_unlock(&ctx->xc_free_lock);
126
127	return (xact);
128}
129
130struct vmbus_xact_ctx *
131vmbus_xact_ctx_create(bus_dma_tag_t dtag, size_t req_size, size_t resp_size,
132    size_t priv_size)
133{
134	struct vmbus_xact_ctx *ctx;
135
136	ctx = malloc(sizeof(*ctx), M_DEVBUF, M_WAITOK | M_ZERO);
137	ctx->xc_req_size = req_size;
138	ctx->xc_resp_size = resp_size;
139	ctx->xc_priv_size = priv_size;
140
141	ctx->xc_free = vmbus_xact_alloc(ctx, dtag);
142	if (ctx->xc_free == NULL) {
143		free(ctx, M_DEVBUF);
144		return (NULL);
145	}
146
147	mtx_init(&ctx->xc_free_lock, "vmbus xact free", NULL, MTX_DEF);
148	mtx_init(&ctx->xc_active_lock, "vmbus xact active", NULL, MTX_DEF);
149
150	return (ctx);
151}
152
153void
154vmbus_xact_ctx_destroy(struct vmbus_xact_ctx *ctx)
155{
156	struct vmbus_xact *xact;
157
158	mtx_lock(&ctx->xc_free_lock);
159	ctx->xc_flags |= VMBUS_XACT_CTXF_DESTROY;
160	mtx_unlock(&ctx->xc_free_lock);
161	wakeup(&ctx->xc_free);
162
163	xact = vmbus_xact_get1(ctx, 0);
164	if (xact == NULL)
165		panic("can't get xact");
166
167	vmbus_xact_free(xact);
168	mtx_destroy(&ctx->xc_free_lock);
169	mtx_destroy(&ctx->xc_active_lock);
170	free(ctx, M_DEVBUF);
171}
172
173struct vmbus_xact *
174vmbus_xact_get(struct vmbus_xact_ctx *ctx, size_t req_len)
175{
176	struct vmbus_xact *xact;
177
178	if (req_len > ctx->xc_req_size)
179		panic("invalid request size %zu", req_len);
180
181	xact = vmbus_xact_get1(ctx, VMBUS_XACT_CTXF_DESTROY);
182	if (xact == NULL)
183		return (NULL);
184
185	memset(xact->x_req, 0, req_len);
186	return (xact);
187}
188
189void
190vmbus_xact_put(struct vmbus_xact *xact)
191{
192	struct vmbus_xact_ctx *ctx = xact->x_ctx;
193
194	KASSERT(ctx->xc_active == NULL, ("pending active xact"));
195	xact->x_resp = NULL;
196
197	mtx_lock(&ctx->xc_free_lock);
198	KASSERT(ctx->xc_free == NULL, ("has free xact"));
199	ctx->xc_free = xact;
200	mtx_unlock(&ctx->xc_free_lock);
201	wakeup(&ctx->xc_free);
202}
203
204void *
205vmbus_xact_req_data(const struct vmbus_xact *xact)
206{
207
208	return (xact->x_req);
209}
210
211bus_addr_t
212vmbus_xact_req_paddr(const struct vmbus_xact *xact)
213{
214
215	return (xact->x_req_dma.hv_paddr);
216}
217
218void *
219vmbus_xact_priv(const struct vmbus_xact *xact, size_t priv_len)
220{
221
222	if (priv_len > xact->x_ctx->xc_priv_size)
223		panic("invalid priv size %zu", priv_len);
224	return (xact->x_priv);
225}
226
227void
228vmbus_xact_activate(struct vmbus_xact *xact)
229{
230	struct vmbus_xact_ctx *ctx = xact->x_ctx;
231
232	KASSERT(xact->x_resp == NULL, ("xact has pending response"));
233
234	mtx_lock(&ctx->xc_active_lock);
235	KASSERT(ctx->xc_active == NULL, ("pending active xact"));
236	ctx->xc_active = xact;
237	mtx_unlock(&ctx->xc_active_lock);
238}
239
240void
241vmbus_xact_deactivate(struct vmbus_xact *xact)
242{
243	struct vmbus_xact_ctx *ctx = xact->x_ctx;
244
245	mtx_lock(&ctx->xc_active_lock);
246	KASSERT(ctx->xc_active == xact, ("xact mismatch"));
247	ctx->xc_active = NULL;
248	mtx_unlock(&ctx->xc_active_lock);
249}
250
251const void *
252vmbus_xact_wait(struct vmbus_xact *xact, size_t *resp_len)
253{
254	struct vmbus_xact_ctx *ctx = xact->x_ctx;
255	const void *resp;
256
257	mtx_lock(&ctx->xc_active_lock);
258
259	KASSERT(ctx->xc_active == xact, ("xact mismatch"));
260	while (xact->x_resp == NULL) {
261		mtx_sleep(&ctx->xc_active, &ctx->xc_active_lock, 0,
262		    "wxact", 0);
263	}
264	ctx->xc_active = NULL;
265
266	resp = xact->x_resp;
267	*resp_len = xact->x_resp_len;
268
269	mtx_unlock(&ctx->xc_active_lock);
270
271	return (resp);
272}
273
274static void
275vmbus_xact_save_resp(struct vmbus_xact *xact, const void *data, size_t dlen)
276{
277	struct vmbus_xact_ctx *ctx = xact->x_ctx;
278	size_t cplen = dlen;
279
280	mtx_assert(&ctx->xc_active_lock, MA_OWNED);
281
282	if (cplen > ctx->xc_resp_size) {
283		printf("vmbus: xact response truncated %zu -> %zu\n",
284		    cplen, ctx->xc_resp_size);
285		cplen = ctx->xc_resp_size;
286	}
287
288	KASSERT(ctx->xc_active == xact, ("xact mismatch"));
289	memcpy(xact->x_resp0, data, cplen);
290	xact->x_resp_len = cplen;
291	xact->x_resp = xact->x_resp0;
292}
293
294void
295vmbus_xact_wakeup(struct vmbus_xact *xact, const void *data, size_t dlen)
296{
297	struct vmbus_xact_ctx *ctx = xact->x_ctx;
298
299	mtx_lock(&ctx->xc_active_lock);
300	vmbus_xact_save_resp(xact, data, dlen);
301	mtx_unlock(&ctx->xc_active_lock);
302	wakeup(&ctx->xc_active);
303}
304
305void
306vmbus_xact_ctx_wakeup(struct vmbus_xact_ctx *ctx, const void *data, size_t dlen)
307{
308	mtx_lock(&ctx->xc_active_lock);
309	KASSERT(ctx->xc_active != NULL, ("no pending xact"));
310	vmbus_xact_save_resp(ctx->xc_active, data, dlen);
311	mtx_unlock(&ctx->xc_active_lock);
312	wakeup(&ctx->xc_active);
313}
314