xref: /freebsd/sys/dev/hyperv/vmbus/vmbus_xact.c (revision d3de06238379fc0e692927ebf74fcc41860c726f)
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 
73 static struct vmbus_xact *
74 vmbus_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 
95 static void
96 vmbus_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 
106 static struct vmbus_xact *
107 vmbus_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 
130 struct vmbus_xact_ctx *
131 vmbus_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 
153 void
154 vmbus_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 
173 struct vmbus_xact *
174 vmbus_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 
189 void
190 vmbus_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 
204 void *
205 vmbus_xact_req_data(const struct vmbus_xact *xact)
206 {
207 
208 	return (xact->x_req);
209 }
210 
211 bus_addr_t
212 vmbus_xact_req_paddr(const struct vmbus_xact *xact)
213 {
214 
215 	return (xact->x_req_dma.hv_paddr);
216 }
217 
218 void *
219 vmbus_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 
227 void
228 vmbus_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 
240 void
241 vmbus_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 
251 const void *
252 vmbus_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 
274 static void
275 vmbus_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 
294 void
295 vmbus_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 
305 void
306 vmbus_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