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