File: | dev/pv/xenstore.c |
Warning: | line 1042, column 34 Access to field 'iov_base' results in a dereference of a null pointer (loaded from variable 'iovp') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: xenstore.c,v 1.49 2023/04/11 00:45:08 jsg Exp $ */ | |||
2 | ||||
3 | /* | |||
4 | * Copyright (c) 2015 Mike Belopuhov | |||
5 | * | |||
6 | * Permission to use, copy, modify, and distribute this software for any | |||
7 | * purpose with or without fee is hereby granted, provided that the above | |||
8 | * copyright notice and this permission notice appear in all copies. | |||
9 | * | |||
10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
17 | */ | |||
18 | ||||
19 | #include <sys/param.h> | |||
20 | #include <sys/systm.h> | |||
21 | #include <sys/atomic.h> | |||
22 | #include <sys/kernel.h> | |||
23 | #include <sys/malloc.h> | |||
24 | #include <sys/device.h> | |||
25 | #include <sys/mutex.h> | |||
26 | #include <sys/rwlock.h> | |||
27 | #include <sys/ioctl.h> | |||
28 | #include <sys/task.h> | |||
29 | ||||
30 | #include <machine/bus.h> | |||
31 | ||||
32 | #include <uvm/uvm_extern.h> | |||
33 | ||||
34 | #include <dev/pv/pvvar.h> | |||
35 | #include <dev/pv/xenreg.h> | |||
36 | #include <dev/pv/xenvar.h> | |||
37 | ||||
38 | /* #define XS_DEBUG */ | |||
39 | ||||
40 | #ifdef XS_DEBUG | |||
41 | #define DPRINTF(x...) printf(x) | |||
42 | #else | |||
43 | #define DPRINTF(x...) | |||
44 | #endif | |||
45 | ||||
46 | /* | |||
47 | * The XenStore interface is a simple storage system that is a means of | |||
48 | * communicating state and configuration data between the Xen Domain 0 | |||
49 | * and the various guest domains. All configuration data other than | |||
50 | * a small amount of essential information required during the early | |||
51 | * boot process of launching a Xen aware guest, is managed using the | |||
52 | * XenStore. | |||
53 | * | |||
54 | * The XenStore is ASCII string based, and has a structure and semantics | |||
55 | * similar to a filesystem. There are files and directories that are | |||
56 | * able to contain files or other directories. The depth of the hierarchy | |||
57 | * is only limited by the XenStore's maximum path length. | |||
58 | * | |||
59 | * The communication channel between the XenStore service and other | |||
60 | * domains is via two, guest specific, ring buffers in a shared memory | |||
61 | * area. One ring buffer is used for communicating in each direction. | |||
62 | * The grant table references for this shared memory are given to the | |||
63 | * guest via HVM hypercalls. | |||
64 | * | |||
65 | * The XenStore communication relies on an event channel and thus | |||
66 | * interrupts. Several Xen services depend on the XenStore, most | |||
67 | * notably the XenBus used to discover and manage Xen devices. | |||
68 | */ | |||
69 | ||||
70 | const struct { | |||
71 | const char *xse_errstr; | |||
72 | int xse_errnum; | |||
73 | } xs_errors[] = { | |||
74 | { "EINVAL", EINVAL22 }, | |||
75 | { "EACCES", EACCES13 }, | |||
76 | { "EEXIST", EEXIST17 }, | |||
77 | { "EISDIR", EISDIR21 }, | |||
78 | { "ENOENT", ENOENT2 }, | |||
79 | { "ENOMEM", ENOMEM12 }, | |||
80 | { "ENOSPC", ENOSPC28 }, | |||
81 | { "EIO", EIO5 }, | |||
82 | { "ENOTEMPTY", ENOTEMPTY66 }, | |||
83 | { "ENOSYS", ENOSYS78 }, | |||
84 | { "EROFS", EROFS30 }, | |||
85 | { "EBUSY", EBUSY16 }, | |||
86 | { "EAGAIN", EAGAIN35 }, | |||
87 | { "EISCONN", EISCONN56 }, | |||
88 | { NULL((void *)0), -1 }, | |||
89 | }; | |||
90 | ||||
91 | struct xs_msghdr { | |||
92 | /* Message type */ | |||
93 | uint32_t xmh_type; | |||
94 | /* Request identifier, echoed in daemon's response. */ | |||
95 | uint32_t xmh_rid; | |||
96 | /* Transaction id (0 if not related to a transaction). */ | |||
97 | uint32_t xmh_tid; | |||
98 | /* Length of data following this. */ | |||
99 | uint32_t xmh_len; | |||
100 | /* Generally followed by nul-terminated string(s). */ | |||
101 | } __packed__attribute__((__packed__)); | |||
102 | ||||
103 | /* | |||
104 | * A minimum output buffer size needed to store an error string. | |||
105 | */ | |||
106 | #define XS_ERR_PAYLOAD16 16 | |||
107 | ||||
108 | /* | |||
109 | * Although the Xen source code implies that the limit is 4k, | |||
110 | * in practice it turns out that we can only send 2k bytes of | |||
111 | * payload before receiving a ENOSPC. We set it to an even | |||
112 | * smaller value however, because there's no real need to use | |||
113 | * large buffers for anything. | |||
114 | */ | |||
115 | #define XS_MAX_PAYLOAD1024 1024 | |||
116 | ||||
117 | struct xs_msg { | |||
118 | struct xs_msghdr xsm_hdr; | |||
119 | uint32_t xsm_read; | |||
120 | uint32_t xsm_dlen; | |||
121 | int xsm_error; | |||
122 | uint8_t *xsm_data; | |||
123 | TAILQ_ENTRY(xs_msg)struct { struct xs_msg *tqe_next; struct xs_msg **tqe_prev; } xsm_link; | |||
124 | }; | |||
125 | TAILQ_HEAD(xs_msgq, xs_msg)struct xs_msgq { struct xs_msg *tqh_first; struct xs_msg **tqh_last ; }; | |||
126 | ||||
127 | #define XS_RING_SIZE1024 1024 | |||
128 | ||||
129 | struct xs_ring { | |||
130 | uint8_t xsr_req[XS_RING_SIZE1024]; | |||
131 | uint8_t xsr_rsp[XS_RING_SIZE1024]; | |||
132 | uint32_t xsr_req_cons; | |||
133 | uint32_t xsr_req_prod; | |||
134 | uint32_t xsr_rsp_cons; | |||
135 | uint32_t xsr_rsp_prod; | |||
136 | } __packed__attribute__((__packed__)); | |||
137 | ||||
138 | #define XST_DELAY1 1 /* in seconds */ | |||
139 | ||||
140 | #define XSW_TOKLEN(sizeof(void *) * 2 + 1) (sizeof(void *) * 2 + 1) | |||
141 | ||||
142 | struct xs_watch { | |||
143 | TAILQ_ENTRY(xs_watch)struct { struct xs_watch *tqe_next; struct xs_watch **tqe_prev ; } xsw_entry; | |||
144 | uint8_t xsw_token[XSW_TOKLEN(sizeof(void *) * 2 + 1)]; | |||
145 | struct task *xsw_task; | |||
146 | }; | |||
147 | ||||
148 | /* | |||
149 | * Container for all XenStore related state. | |||
150 | */ | |||
151 | struct xs_softc { | |||
152 | struct xen_softc *xs_sc; | |||
153 | ||||
154 | evtchn_port_t xs_port; | |||
155 | xen_intr_handle_t xs_ih; | |||
156 | ||||
157 | struct xs_ring *xs_ring; | |||
158 | ||||
159 | struct xs_msg xs_msgs[10]; | |||
160 | struct xs_msg *xs_rmsg; | |||
161 | ||||
162 | struct xs_msgq xs_free; | |||
163 | struct xs_msgq xs_reqs; | |||
164 | struct xs_msgq xs_rsps; | |||
165 | ||||
166 | volatile uint xs_rid; | |||
167 | ||||
168 | const char *xs_wchan; | |||
169 | const char *xs_rchan; | |||
170 | ||||
171 | struct mutex xs_reqlck; /* request queue mutex */ | |||
172 | struct mutex xs_rsplck; /* response queue mutex */ | |||
173 | struct mutex xs_frqlck; /* free queue mutex */ | |||
174 | ||||
175 | TAILQ_HEAD(, xs_watch)struct { struct xs_watch *tqh_first; struct xs_watch **tqh_last ; } xs_watches; | |||
176 | struct mutex xs_watchlck; | |||
177 | struct xs_msg xs_emsg; | |||
178 | struct taskq *xs_watchtq; | |||
179 | ||||
180 | struct rwlock xs_rnglck; | |||
181 | }; | |||
182 | ||||
183 | struct xs_msg * | |||
184 | xs_get_msg(struct xs_softc *, int); | |||
185 | void xs_put_msg(struct xs_softc *, struct xs_msg *); | |||
186 | int xs_ring_get(struct xs_softc *, void *, size_t); | |||
187 | int xs_ring_put(struct xs_softc *, void *, size_t); | |||
188 | void xs_intr(void *); | |||
189 | void xs_poll(struct xs_softc *, int); | |||
190 | int xs_output(struct xs_transaction *, uint8_t *, int); | |||
191 | int xs_start(struct xs_transaction *, struct xs_msg *, struct iovec *, int); | |||
192 | struct xs_msg * | |||
193 | xs_reply(struct xs_transaction *, uint); | |||
194 | int xs_parse(struct xs_transaction *, struct xs_msg *, struct iovec **, | |||
195 | int *); | |||
196 | int xs_event(struct xs_softc *, struct xs_msg *); | |||
197 | ||||
198 | int | |||
199 | xs_attach(struct xen_softc *sc) | |||
200 | { | |||
201 | struct xen_hvm_param xhv; | |||
202 | struct xs_softc *xs; | |||
203 | paddr_t pa; | |||
204 | int i; | |||
205 | ||||
206 | if ((xs = malloc(sizeof(*xs), M_DEVBUF2, M_NOWAIT0x0002 | M_ZERO0x0008)) == NULL((void *)0)) { | |||
207 | printf(": failed to allocate xenstore softc\n"); | |||
208 | return (-1); | |||
209 | } | |||
210 | sc->sc_xs = xs; | |||
211 | xs->xs_sc = sc; | |||
212 | ||||
213 | /* Fetch event channel port */ | |||
214 | memset(&xhv, 0, sizeof(xhv))__builtin_memset((&xhv), (0), (sizeof(xhv))); | |||
215 | xhv.domid = DOMID_SELF(0x7FF0U); | |||
216 | xhv.index = HVM_PARAM_STORE_EVTCHN2; | |||
217 | if (xen_hypercall(sc, XC_HVM34, 2, HVMOP_get_param1, &xhv)) { | |||
218 | printf(": failed to obtain a xenstore event channel\n"); | |||
219 | goto fail_1; | |||
220 | } | |||
221 | xs->xs_port = xhv.value; | |||
222 | ||||
223 | printf(", event channel %u\n", xs->xs_port); | |||
224 | ||||
225 | /* Fetch a frame number (PA) of a shared xenstore page */ | |||
226 | memset(&xhv, 0, sizeof(xhv))__builtin_memset((&xhv), (0), (sizeof(xhv))); | |||
227 | xhv.domid = DOMID_SELF(0x7FF0U); | |||
228 | xhv.index = HVM_PARAM_STORE_PFN1; | |||
229 | if (xen_hypercall(sc, XC_HVM34, 2, HVMOP_get_param1, &xhv)) | |||
230 | goto fail_1; | |||
231 | pa = ptoa(xhv.value)((paddr_t)(xhv.value) << 12); | |||
232 | /* Allocate a page of virtual memory */ | |||
233 | xs->xs_ring = km_alloc(PAGE_SIZE(1 << 12), &kv_any, &kp_none, &kd_nowait); | |||
234 | if (xs->xs_ring == NULL((void *)0)) | |||
235 | goto fail_1; | |||
236 | /* Map in the xenstore page into our KVA */ | |||
237 | pa |= PMAP_NOCACHE0x1; | |||
238 | pmap_kenter_pa((vaddr_t)xs->xs_ring, pa, PROT_READ0x01 | PROT_WRITE0x02); | |||
239 | pmap_update(pmap_kernel()); | |||
240 | ||||
241 | if (xen_intr_establish(xs->xs_port, &xs->xs_ih, 0, xs_intr, xs, | |||
242 | sc->sc_dev.dv_xname)) | |||
243 | goto fail_2; | |||
244 | ||||
245 | xs->xs_wchan = "xswrite"; | |||
246 | xs->xs_rchan = "xsread"; | |||
247 | ||||
248 | TAILQ_INIT(&xs->xs_free)do { (&xs->xs_free)->tqh_first = ((void *)0); (& xs->xs_free)->tqh_last = &(&xs->xs_free)-> tqh_first; } while (0); | |||
249 | TAILQ_INIT(&xs->xs_reqs)do { (&xs->xs_reqs)->tqh_first = ((void *)0); (& xs->xs_reqs)->tqh_last = &(&xs->xs_reqs)-> tqh_first; } while (0); | |||
250 | TAILQ_INIT(&xs->xs_rsps)do { (&xs->xs_rsps)->tqh_first = ((void *)0); (& xs->xs_rsps)->tqh_last = &(&xs->xs_rsps)-> tqh_first; } while (0); | |||
251 | for (i = 0; i < nitems(xs->xs_msgs)(sizeof((xs->xs_msgs)) / sizeof((xs->xs_msgs)[0])); i++) | |||
252 | TAILQ_INSERT_TAIL(&xs->xs_free, &xs->xs_msgs[i], xsm_link)do { (&xs->xs_msgs[i])->xsm_link.tqe_next = ((void * )0); (&xs->xs_msgs[i])->xsm_link.tqe_prev = (&xs ->xs_free)->tqh_last; *(&xs->xs_free)->tqh_last = (&xs->xs_msgs[i]); (&xs->xs_free)->tqh_last = &(&xs->xs_msgs[i])->xsm_link.tqe_next; } while (0); | |||
253 | ||||
254 | mtx_init(&xs->xs_reqlck, IPL_NET)do { (void)(((void *)0)); (void)(0); __mtx_init((&xs-> xs_reqlck), ((((0x4)) > 0x0 && ((0x4)) < 0x9) ? 0x9 : ((0x4)))); } while (0); | |||
255 | mtx_init(&xs->xs_rsplck, IPL_NET)do { (void)(((void *)0)); (void)(0); __mtx_init((&xs-> xs_rsplck), ((((0x4)) > 0x0 && ((0x4)) < 0x9) ? 0x9 : ((0x4)))); } while (0); | |||
256 | mtx_init(&xs->xs_frqlck, IPL_NET)do { (void)(((void *)0)); (void)(0); __mtx_init((&xs-> xs_frqlck), ((((0x4)) > 0x0 && ((0x4)) < 0x9) ? 0x9 : ((0x4)))); } while (0); | |||
257 | ||||
258 | rw_init(&xs->xs_rnglck, "xsrnglck")_rw_init_flags(&xs->xs_rnglck, "xsrnglck", 0, ((void * )0)); | |||
259 | ||||
260 | xs->xs_watchtq = taskq_create("xenwatch", 1, IPL_NET0x4, 0); | |||
261 | ||||
262 | mtx_init(&xs->xs_watchlck, IPL_NET)do { (void)(((void *)0)); (void)(0); __mtx_init((&xs-> xs_watchlck), ((((0x4)) > 0x0 && ((0x4)) < 0x9) ? 0x9 : ((0x4)))); } while (0); | |||
263 | TAILQ_INIT(&xs->xs_watches)do { (&xs->xs_watches)->tqh_first = ((void *)0); (& xs->xs_watches)->tqh_last = &(&xs->xs_watches )->tqh_first; } while (0); | |||
264 | ||||
265 | xs->xs_emsg.xsm_data = malloc(XS_MAX_PAYLOAD1024, M_DEVBUF2, | |||
266 | M_ZERO0x0008 | M_NOWAIT0x0002); | |||
267 | if (xs->xs_emsg.xsm_data == NULL((void *)0)) | |||
268 | goto fail_2; | |||
269 | xs->xs_emsg.xsm_dlen = XS_MAX_PAYLOAD1024; | |||
270 | ||||
271 | return (0); | |||
272 | ||||
273 | fail_2: | |||
274 | pmap_kremove((vaddr_t)xs->xs_ring, PAGE_SIZE(1 << 12)); | |||
275 | pmap_update(pmap_kernel()); | |||
276 | km_free(xs->xs_ring, PAGE_SIZE(1 << 12), &kv_any, &kp_none); | |||
277 | xs->xs_ring = NULL((void *)0); | |||
278 | fail_1: | |||
279 | free(xs, sizeof(*xs), M_DEVBUF2); | |||
280 | sc->sc_xs = NULL((void *)0); | |||
281 | return (-1); | |||
282 | } | |||
283 | ||||
284 | struct xs_msg * | |||
285 | xs_get_msg(struct xs_softc *xs, int waitok) | |||
286 | { | |||
287 | static const char *chan = "xsalloc"; | |||
288 | struct xs_msg *xsm; | |||
289 | ||||
290 | mtx_enter(&xs->xs_frqlck); | |||
291 | for (;;) { | |||
292 | xsm = TAILQ_FIRST(&xs->xs_free)((&xs->xs_free)->tqh_first); | |||
293 | if (xsm != NULL((void *)0)) { | |||
294 | TAILQ_REMOVE(&xs->xs_free, xsm, xsm_link)do { if (((xsm)->xsm_link.tqe_next) != ((void *)0)) (xsm)-> xsm_link.tqe_next->xsm_link.tqe_prev = (xsm)->xsm_link. tqe_prev; else (&xs->xs_free)->tqh_last = (xsm)-> xsm_link.tqe_prev; *(xsm)->xsm_link.tqe_prev = (xsm)->xsm_link .tqe_next; ((xsm)->xsm_link.tqe_prev) = ((void *)-1); ((xsm )->xsm_link.tqe_next) = ((void *)-1); } while (0); | |||
295 | break; | |||
296 | } | |||
297 | if (!waitok) { | |||
298 | mtx_leave(&xs->xs_frqlck); | |||
299 | delay(XST_DELAY * 1000 >> 2)(*delay_func)(1 * 1000 >> 2); | |||
300 | mtx_enter(&xs->xs_frqlck); | |||
301 | } else | |||
302 | msleep_nsec(chan, &xs->xs_frqlck, PRIBIO16, chan, | |||
303 | SEC_TO_NSEC(XST_DELAY1) >> 2); | |||
304 | } | |||
305 | mtx_leave(&xs->xs_frqlck); | |||
306 | return (xsm); | |||
307 | } | |||
308 | ||||
309 | void | |||
310 | xs_put_msg(struct xs_softc *xs, struct xs_msg *xsm) | |||
311 | { | |||
312 | memset(xsm, 0, sizeof(*xsm))__builtin_memset((xsm), (0), (sizeof(*xsm))); | |||
313 | mtx_enter(&xs->xs_frqlck); | |||
314 | TAILQ_INSERT_TAIL(&xs->xs_free, xsm, xsm_link)do { (xsm)->xsm_link.tqe_next = ((void *)0); (xsm)->xsm_link .tqe_prev = (&xs->xs_free)->tqh_last; *(&xs-> xs_free)->tqh_last = (xsm); (&xs->xs_free)->tqh_last = &(xsm)->xsm_link.tqe_next; } while (0); | |||
315 | mtx_leave(&xs->xs_frqlck); | |||
316 | } | |||
317 | ||||
318 | int | |||
319 | xs_geterror(struct xs_msg *xsm) | |||
320 | { | |||
321 | int i; | |||
322 | ||||
323 | for (i = 0; i < nitems(xs_errors)(sizeof((xs_errors)) / sizeof((xs_errors)[0])); i++) | |||
324 | if (strcmp(xs_errors[i].xse_errstr, xsm->xsm_data) == 0) | |||
325 | return (xs_errors[i].xse_errnum); | |||
326 | return (EOPNOTSUPP45); | |||
327 | } | |||
328 | ||||
329 | static inline uint32_t | |||
330 | xs_ring_avail(struct xs_ring *xsr, int req) | |||
331 | { | |||
332 | uint32_t cons = req ? xsr->xsr_req_cons : xsr->xsr_rsp_cons; | |||
333 | uint32_t prod = req ? xsr->xsr_req_prod : xsr->xsr_rsp_prod; | |||
334 | ||||
335 | KASSERT(prod - cons <= XS_RING_SIZE)((prod - cons <= 1024) ? (void)0 : __assert("diagnostic ", "/usr/src/sys/dev/pv/xenstore.c", 335, "prod - cons <= XS_RING_SIZE" )); | |||
336 | return (req ? XS_RING_SIZE1024 - (prod - cons) : prod - cons); | |||
337 | } | |||
338 | ||||
339 | void | |||
340 | xs_poll(struct xs_softc *xs, int nosleep) | |||
341 | { | |||
342 | int s; | |||
343 | ||||
344 | if (nosleep) { | |||
345 | delay(XST_DELAY * 1000 >> 2)(*delay_func)(1 * 1000 >> 2); | |||
346 | s = splnet()splraise(0x4); | |||
347 | xs_intr(xs); | |||
348 | splx(s)spllower(s); | |||
349 | } else { | |||
350 | tsleep_nsec(xs->xs_wchan, PRIBIO16, xs->xs_wchan, | |||
351 | SEC_TO_NSEC(XST_DELAY1) >> 2); | |||
352 | } | |||
353 | } | |||
354 | ||||
355 | int | |||
356 | xs_output(struct xs_transaction *xst, uint8_t *bp, int len) | |||
357 | { | |||
358 | struct xs_softc *xs = xst->xst_cookie; | |||
359 | int chunk; | |||
360 | ||||
361 | while (len > 0) { | |||
362 | chunk = xs_ring_put(xs, bp, MIN(len, XS_RING_SIZE)(((len)<(1024))?(len):(1024))); | |||
363 | if (chunk < 0) | |||
364 | return (-1); | |||
365 | if (chunk > 0) { | |||
366 | len -= chunk; | |||
367 | bp += chunk; | |||
368 | if (xs_ring_avail(xs->xs_ring, 1) > 0) | |||
369 | continue; | |||
370 | } | |||
371 | /* Squeaky wheel gets the kick */ | |||
372 | xen_intr_signal(xs->xs_ih); | |||
373 | /* | |||
374 | * chunk == 0: we need to wait for hv to consume | |||
375 | * what has already been written; | |||
376 | * | |||
377 | * Alternatively we have managed to fill the ring | |||
378 | * and must wait for HV to collect the data. | |||
379 | */ | |||
380 | while (xs->xs_ring->xsr_req_prod != xs->xs_ring->xsr_req_cons) | |||
381 | xs_poll(xs, 1); | |||
382 | } | |||
383 | return (0); | |||
384 | } | |||
385 | ||||
386 | int | |||
387 | xs_start(struct xs_transaction *xst, struct xs_msg *xsm, struct iovec *iov, | |||
388 | int iov_cnt) | |||
389 | { | |||
390 | struct xs_softc *xs = xst->xst_cookie; | |||
391 | int i; | |||
392 | ||||
393 | rw_enter_write(&xs->xs_rnglck); | |||
394 | ||||
395 | /* Header */ | |||
396 | if (xs_output(xst, (uint8_t *)&xsm->xsm_hdr, | |||
397 | sizeof(xsm->xsm_hdr)) == -1) { | |||
398 | printf("%s: failed to write the header\n", __func__); | |||
399 | rw_exit_write(&xs->xs_rnglck); | |||
400 | return (-1); | |||
401 | } | |||
402 | ||||
403 | /* Data loop */ | |||
404 | for (i = 0; i < iov_cnt; i++) { | |||
405 | if (xs_output(xst, iov[i].iov_base, iov[i].iov_len) == -1) { | |||
406 | printf("%s: failed on iovec #%d len %lu\n", __func__, | |||
407 | i, iov[i].iov_len); | |||
408 | rw_exit_write(&xs->xs_rnglck); | |||
409 | return (-1); | |||
410 | } | |||
411 | } | |||
412 | ||||
413 | mtx_enter(&xs->xs_reqlck); | |||
414 | TAILQ_INSERT_TAIL(&xs->xs_reqs, xsm, xsm_link)do { (xsm)->xsm_link.tqe_next = ((void *)0); (xsm)->xsm_link .tqe_prev = (&xs->xs_reqs)->tqh_last; *(&xs-> xs_reqs)->tqh_last = (xsm); (&xs->xs_reqs)->tqh_last = &(xsm)->xsm_link.tqe_next; } while (0); | |||
415 | mtx_leave(&xs->xs_reqlck); | |||
416 | ||||
417 | xen_intr_signal(xs->xs_ih); | |||
418 | ||||
419 | rw_exit_write(&xs->xs_rnglck); | |||
420 | ||||
421 | return (0); | |||
422 | } | |||
423 | ||||
424 | struct xs_msg * | |||
425 | xs_reply(struct xs_transaction *xst, uint rid) | |||
426 | { | |||
427 | struct xs_softc *xs = xst->xst_cookie; | |||
428 | struct xs_msg *xsm; | |||
429 | int s; | |||
430 | ||||
431 | mtx_enter(&xs->xs_rsplck); | |||
432 | for (;;) { | |||
433 | TAILQ_FOREACH(xsm, &xs->xs_rsps, xsm_link)for((xsm) = ((&xs->xs_rsps)->tqh_first); (xsm) != ( (void *)0); (xsm) = ((xsm)->xsm_link.tqe_next)) { | |||
434 | if (xsm->xsm_hdr.xmh_tid == xst->xst_id && | |||
435 | xsm->xsm_hdr.xmh_rid == rid) | |||
436 | break; | |||
437 | } | |||
438 | if (xsm != NULL((void *)0)) { | |||
439 | TAILQ_REMOVE(&xs->xs_rsps, xsm, xsm_link)do { if (((xsm)->xsm_link.tqe_next) != ((void *)0)) (xsm)-> xsm_link.tqe_next->xsm_link.tqe_prev = (xsm)->xsm_link. tqe_prev; else (&xs->xs_rsps)->tqh_last = (xsm)-> xsm_link.tqe_prev; *(xsm)->xsm_link.tqe_prev = (xsm)->xsm_link .tqe_next; ((xsm)->xsm_link.tqe_prev) = ((void *)-1); ((xsm )->xsm_link.tqe_next) = ((void *)-1); } while (0); | |||
440 | break; | |||
441 | } | |||
442 | if (cold) { | |||
443 | mtx_leave(&xs->xs_rsplck); | |||
444 | delay(XST_DELAY * 1000 >> 2)(*delay_func)(1 * 1000 >> 2); | |||
445 | s = splnet()splraise(0x4); | |||
446 | xs_intr(xs); | |||
447 | splx(s)spllower(s); | |||
448 | mtx_enter(&xs->xs_rsplck); | |||
449 | } else | |||
450 | msleep_nsec(xs->xs_rchan, &xs->xs_rsplck, PRIBIO16, | |||
451 | xs->xs_rchan, SEC_TO_NSEC(XST_DELAY1) >> 2); | |||
452 | } | |||
453 | mtx_leave(&xs->xs_rsplck); | |||
454 | return (xsm); | |||
455 | } | |||
456 | ||||
457 | int | |||
458 | xs_ring_put(struct xs_softc *xs, void *src, size_t size) | |||
459 | { | |||
460 | struct xs_ring *xsr = xs->xs_ring; | |||
461 | uint32_t prod = xsr->xsr_req_prod & (XS_RING_SIZE1024 - 1); | |||
462 | uint32_t avail = xs_ring_avail(xsr, 1); | |||
463 | size_t left; | |||
464 | ||||
465 | if (size > XS_RING_SIZE1024) | |||
466 | return (-1); | |||
467 | if (avail == 0) | |||
468 | return (0); | |||
469 | ||||
470 | /* Bound the size by the number of available slots */ | |||
471 | size = MIN(size, avail)(((size)<(avail))?(size):(avail)); | |||
472 | /* How many contiguous bytes can we memcpy... */ | |||
473 | left = XS_RING_SIZE1024 - prod; | |||
474 | /* ...bounded by how much we need to write? */ | |||
475 | left = MIN(left, size)(((left)<(size))?(left):(size)); | |||
476 | ||||
477 | memcpy(&xsr->xsr_req[prod], src, left)__builtin_memcpy((&xsr->xsr_req[prod]), (src), (left)); | |||
478 | memcpy(&xsr->xsr_req[0], (caddr_t)src + left, size - left)__builtin_memcpy((&xsr->xsr_req[0]), ((caddr_t)src + left ), (size - left)); | |||
479 | virtio_membar_sync()do { __asm volatile("mfence" ::: "memory"); } while (0); | |||
480 | xsr->xsr_req_prod += size; | |||
481 | return (size); | |||
482 | } | |||
483 | ||||
484 | int | |||
485 | xs_ring_get(struct xs_softc *xs, void *dst, size_t size) | |||
486 | { | |||
487 | struct xs_ring *xsr = xs->xs_ring; | |||
488 | uint32_t cons = xsr->xsr_rsp_cons & (XS_RING_SIZE1024 - 1); | |||
489 | uint32_t avail = xs_ring_avail(xsr, 0); | |||
490 | size_t left; | |||
491 | ||||
492 | if (size > XS_RING_SIZE1024) | |||
493 | return (-1); | |||
494 | if (avail == 0) | |||
495 | return (0); | |||
496 | ||||
497 | /* Bound the size by the number of available slots */ | |||
498 | size = MIN(size, avail)(((size)<(avail))?(size):(avail)); | |||
499 | /* How many contiguous bytes can we memcpy... */ | |||
500 | left = XS_RING_SIZE1024 - cons; | |||
501 | /* ...bounded by how much we need to read? */ | |||
502 | left = MIN(left, size)(((left)<(size))?(left):(size)); | |||
503 | ||||
504 | memcpy(dst, &xsr->xsr_rsp[cons], left)__builtin_memcpy((dst), (&xsr->xsr_rsp[cons]), (left)); | |||
505 | memcpy((caddr_t)dst + left, &xsr->xsr_rsp[0], size - left)__builtin_memcpy(((caddr_t)dst + left), (&xsr->xsr_rsp [0]), (size - left)); | |||
506 | virtio_membar_sync()do { __asm volatile("mfence" ::: "memory"); } while (0); | |||
507 | xsr->xsr_rsp_cons += size; | |||
508 | return (size); | |||
509 | } | |||
510 | ||||
511 | void | |||
512 | xs_intr(void *arg) | |||
513 | { | |||
514 | struct xs_softc *xs = arg; | |||
515 | struct xs_ring *xsr = xs->xs_ring; | |||
516 | struct xen_softc *sc = xs->xs_sc; | |||
517 | struct xs_msg *xsm = xs->xs_rmsg; | |||
518 | struct xs_msghdr xmh; | |||
519 | uint32_t avail; | |||
520 | int len; | |||
521 | ||||
522 | virtio_membar_sync()do { __asm volatile("mfence" ::: "memory"); } while (0); | |||
523 | ||||
524 | if (xsr->xsr_rsp_cons == xsr->xsr_rsp_prod) | |||
525 | return; | |||
526 | ||||
527 | avail = xs_ring_avail(xsr, 0); | |||
528 | ||||
529 | /* Response processing */ | |||
530 | ||||
531 | again: | |||
532 | if (xs->xs_rmsg == NULL((void *)0)) { | |||
533 | if (avail < sizeof(xmh)) { | |||
534 | DPRINTF("%s: incomplete header: %u\n", | |||
535 | sc->sc_dev.dv_xname, avail); | |||
536 | goto out; | |||
537 | } | |||
538 | avail -= sizeof(xmh); | |||
539 | ||||
540 | if ((len = xs_ring_get(xs, &xmh, sizeof(xmh))) != sizeof(xmh)) { | |||
541 | printf("%s: message too short: %d\n", | |||
542 | sc->sc_dev.dv_xname, len); | |||
543 | goto out; | |||
544 | } | |||
545 | ||||
546 | if (xmh.xmh_type == XS_EVENT0x0f) { | |||
547 | xsm = &xs->xs_emsg; | |||
548 | xsm->xsm_read = 0; | |||
549 | } else { | |||
550 | mtx_enter(&xs->xs_reqlck); | |||
551 | TAILQ_FOREACH(xsm, &xs->xs_reqs, xsm_link)for((xsm) = ((&xs->xs_reqs)->tqh_first); (xsm) != ( (void *)0); (xsm) = ((xsm)->xsm_link.tqe_next)) { | |||
552 | if (xsm->xsm_hdr.xmh_rid == xmh.xmh_rid) { | |||
553 | TAILQ_REMOVE(&xs->xs_reqs, xsm,do { if (((xsm)->xsm_link.tqe_next) != ((void *)0)) (xsm)-> xsm_link.tqe_next->xsm_link.tqe_prev = (xsm)->xsm_link. tqe_prev; else (&xs->xs_reqs)->tqh_last = (xsm)-> xsm_link.tqe_prev; *(xsm)->xsm_link.tqe_prev = (xsm)->xsm_link .tqe_next; ((xsm)->xsm_link.tqe_prev) = ((void *)-1); ((xsm )->xsm_link.tqe_next) = ((void *)-1); } while (0) | |||
554 | xsm_link)do { if (((xsm)->xsm_link.tqe_next) != ((void *)0)) (xsm)-> xsm_link.tqe_next->xsm_link.tqe_prev = (xsm)->xsm_link. tqe_prev; else (&xs->xs_reqs)->tqh_last = (xsm)-> xsm_link.tqe_prev; *(xsm)->xsm_link.tqe_prev = (xsm)->xsm_link .tqe_next; ((xsm)->xsm_link.tqe_prev) = ((void *)-1); ((xsm )->xsm_link.tqe_next) = ((void *)-1); } while (0); | |||
555 | break; | |||
556 | } | |||
557 | } | |||
558 | mtx_leave(&xs->xs_reqlck); | |||
559 | if (xsm == NULL((void *)0)) { | |||
560 | printf("%s: unexpected message id %u\n", | |||
561 | sc->sc_dev.dv_xname, xmh.xmh_rid); | |||
562 | goto out; | |||
563 | } | |||
564 | } | |||
565 | memcpy(&xsm->xsm_hdr, &xmh, sizeof(xmh))__builtin_memcpy((&xsm->xsm_hdr), (&xmh), (sizeof( xmh))); | |||
566 | xs->xs_rmsg = xsm; | |||
567 | } | |||
568 | ||||
569 | if (xsm->xsm_hdr.xmh_len > xsm->xsm_dlen) | |||
570 | xsm->xsm_error = EMSGSIZE40; | |||
571 | ||||
572 | len = MIN(xsm->xsm_hdr.xmh_len - xsm->xsm_read, avail)(((xsm->xsm_hdr.xmh_len - xsm->xsm_read)<(avail))?(xsm ->xsm_hdr.xmh_len - xsm->xsm_read):(avail)); | |||
573 | if (len) { | |||
574 | /* Get data if reply is not empty */ | |||
575 | if ((len = xs_ring_get(xs, | |||
576 | &xsm->xsm_data[xsm->xsm_read], len)) <= 0) { | |||
577 | printf("%s: read failure %d\n", sc->sc_dev.dv_xname, | |||
578 | len); | |||
579 | goto out; | |||
580 | } | |||
581 | xsm->xsm_read += len; | |||
582 | } | |||
583 | ||||
584 | /* Notify reader that we've managed to read the whole message */ | |||
585 | if (xsm->xsm_read == xsm->xsm_hdr.xmh_len) { | |||
586 | xs->xs_rmsg = NULL((void *)0); | |||
587 | if (xsm->xsm_hdr.xmh_type == XS_EVENT0x0f) { | |||
588 | xs_event(xs, xsm); | |||
589 | } else { | |||
590 | mtx_enter(&xs->xs_rsplck); | |||
591 | TAILQ_INSERT_TAIL(&xs->xs_rsps, xsm, xsm_link)do { (xsm)->xsm_link.tqe_next = ((void *)0); (xsm)->xsm_link .tqe_prev = (&xs->xs_rsps)->tqh_last; *(&xs-> xs_rsps)->tqh_last = (xsm); (&xs->xs_rsps)->tqh_last = &(xsm)->xsm_link.tqe_next; } while (0); | |||
592 | mtx_leave(&xs->xs_rsplck); | |||
593 | wakeup(xs->xs_rchan); | |||
594 | } | |||
595 | } | |||
596 | ||||
597 | if ((avail = xs_ring_avail(xsr, 0)) > 0) | |||
598 | goto again; | |||
599 | ||||
600 | out: | |||
601 | /* Wakeup sleeping writes (if any) */ | |||
602 | wakeup(xs->xs_wchan); | |||
603 | xen_intr_signal(xs->xs_ih); | |||
604 | } | |||
605 | ||||
606 | static inline int | |||
607 | xs_get_buf(struct xs_transaction *xst, struct xs_msg *xsm, int len) | |||
608 | { | |||
609 | unsigned char *buf; | |||
610 | ||||
611 | buf = malloc(len, M_DEVBUF2, M_ZERO0x0008 | (cold ? M_NOWAIT0x0002 : M_WAITOK0x0001)); | |||
612 | if (buf == NULL((void *)0)) | |||
613 | return (-1); | |||
614 | xsm->xsm_dlen = len; | |||
615 | xsm->xsm_data = buf; | |||
616 | return (0); | |||
617 | } | |||
618 | ||||
619 | static inline void | |||
620 | xs_put_buf(struct xs_transaction *xst, struct xs_msg *xsm) | |||
621 | { | |||
622 | free(xsm->xsm_data, M_DEVBUF2, xsm->xsm_dlen); | |||
623 | xsm->xsm_data = NULL((void *)0); | |||
624 | } | |||
625 | ||||
626 | void | |||
627 | xs_resfree(struct xs_transaction *xst, struct iovec *iov, int iov_cnt) | |||
628 | { | |||
629 | int i; | |||
630 | ||||
631 | for (i = 0; i < iov_cnt; i++) | |||
632 | free(iov[i].iov_base, M_DEVBUF2, iov[i].iov_len); | |||
633 | free(iov, M_DEVBUF2, sizeof(struct iovec) * iov_cnt); | |||
634 | } | |||
635 | ||||
636 | int | |||
637 | xs_parse(struct xs_transaction *xst, struct xs_msg *xsm, struct iovec **iov, | |||
638 | int *iov_cnt) | |||
639 | { | |||
640 | char *bp, *cp; | |||
641 | uint32_t dlen; | |||
642 | int i, flags; | |||
643 | ||||
644 | /* If the response size is zero, we return an empty string */ | |||
645 | dlen = MAX(xsm->xsm_hdr.xmh_len, 1)(((xsm->xsm_hdr.xmh_len)>(1))?(xsm->xsm_hdr.xmh_len) :(1)); | |||
646 | flags = M_ZERO0x0008 | (cold ? M_NOWAIT0x0002 : M_WAITOK0x0001); | |||
647 | ||||
648 | *iov_cnt = 0; | |||
649 | /* Make sure that the data is NUL terminated */ | |||
650 | if (xsm->xsm_data[dlen - 1] != '\0') { | |||
651 | /* | |||
652 | * The XS_READ operation always returns length without | |||
653 | * the trailing NUL so we have to adjust the length. | |||
654 | */ | |||
655 | dlen = MIN(dlen + 1, xsm->xsm_dlen)(((dlen + 1)<(xsm->xsm_dlen))?(dlen + 1):(xsm->xsm_dlen )); | |||
656 | xsm->xsm_data[dlen - 1] = '\0'; | |||
657 | } | |||
658 | for (i = 0; i < dlen; i++) | |||
659 | if (xsm->xsm_data[i] == '\0') | |||
660 | (*iov_cnt)++; | |||
661 | *iov = mallocarray(*iov_cnt, sizeof(struct iovec), M_DEVBUF2, flags); | |||
662 | if (*iov == NULL((void *)0)) | |||
663 | goto cleanup; | |||
664 | bp = xsm->xsm_data; | |||
665 | for (i = 0; i < *iov_cnt; i++) { | |||
666 | cp = bp; | |||
667 | while (cp - (caddr_t)xsm->xsm_data < dlen && *cp != '\0') | |||
668 | cp++; | |||
669 | (*iov)[i].iov_len = cp - bp + 1; | |||
670 | (*iov)[i].iov_base = malloc((*iov)[i].iov_len, M_DEVBUF2, flags); | |||
671 | if (!(*iov)[i].iov_base) { | |||
672 | xs_resfree(xst, *iov, *iov_cnt); | |||
673 | goto cleanup; | |||
674 | } | |||
675 | memcpy((*iov)[i].iov_base, bp, (*iov)[i].iov_len)__builtin_memcpy(((*iov)[i].iov_base), (bp), ((*iov)[i].iov_len )); | |||
676 | bp = ++cp; | |||
677 | } | |||
678 | return (0); | |||
679 | ||||
680 | cleanup: | |||
681 | *iov = NULL((void *)0); | |||
682 | *iov_cnt = 0; | |||
683 | return (ENOMEM12); | |||
684 | } | |||
685 | ||||
686 | int | |||
687 | xs_event(struct xs_softc *xs, struct xs_msg *xsm) | |||
688 | { | |||
689 | struct xs_watch *xsw; | |||
690 | char *token = NULL((void *)0); | |||
691 | int i; | |||
692 | ||||
693 | for (i = 0; i < xsm->xsm_read; i++) { | |||
694 | if (xsm->xsm_data[i] == '\0') { | |||
695 | token = &xsm->xsm_data[i+1]; | |||
696 | break; | |||
697 | } | |||
698 | } | |||
699 | if (token == NULL((void *)0)) { | |||
700 | printf("%s: event on \"%s\" without token\n", | |||
701 | xs->xs_sc->sc_dev.dv_xname, xsm->xsm_data); | |||
702 | return (-1); | |||
703 | } | |||
704 | ||||
705 | mtx_enter(&xs->xs_watchlck); | |||
706 | TAILQ_FOREACH(xsw, &xs->xs_watches, xsw_entry)for((xsw) = ((&xs->xs_watches)->tqh_first); (xsw) != ((void *)0); (xsw) = ((xsw)->xsw_entry.tqe_next)) { | |||
707 | if (strcmp(xsw->xsw_token, token)) | |||
708 | continue; | |||
709 | mtx_leave(&xs->xs_watchlck); | |||
710 | task_add(xs->xs_watchtq, xsw->xsw_task); | |||
711 | return (0); | |||
712 | } | |||
713 | mtx_leave(&xs->xs_watchlck); | |||
714 | ||||
715 | printf("%s: no watchers for node \"%s\"\n", | |||
716 | xs->xs_sc->sc_dev.dv_xname, xsm->xsm_data); | |||
717 | return (-1); | |||
718 | } | |||
719 | ||||
720 | int | |||
721 | xs_cmd(struct xs_transaction *xst, int cmd, const char *path, | |||
722 | struct iovec **iov, int *iov_cnt) | |||
723 | { | |||
724 | struct xs_softc *xs = xst->xst_cookie; | |||
725 | struct xs_msg *xsm; | |||
726 | struct iovec ov[10]; /* output vector */ | |||
727 | int datalen = XS_ERR_PAYLOAD16; | |||
728 | int ov_cnt = 0; | |||
729 | enum { READ, WRITE } mode = READ; | |||
730 | int i, error = 0; | |||
731 | ||||
732 | if (cmd
| |||
733 | return (EINVAL22); | |||
734 | ||||
735 | switch (cmd) { | |||
736 | case XS_TOPEN0x06: | |||
737 | ov[0].iov_base = ""; | |||
738 | ov[0].iov_len = 1; | |||
739 | ov_cnt++; | |||
740 | break; | |||
741 | case XS_TCLOSE0x07: | |||
742 | case XS_RM0x0d: | |||
743 | case XS_WATCH0x04: | |||
744 | case XS_WRITE0x0b: | |||
745 | mode = WRITE; | |||
746 | /* FALLTHROUGH */ | |||
747 | default: | |||
748 | if (mode
| |||
749 | datalen = XS_MAX_PAYLOAD1024; | |||
750 | break; | |||
751 | } | |||
752 | ||||
753 | if (path
| |||
754 | ov[ov_cnt].iov_base = (void *)path; | |||
755 | ov[ov_cnt++].iov_len = strlen(path) + 1; /* +NUL */ | |||
756 | } | |||
757 | ||||
758 | if (mode
| |||
759 | for (i = 0; i < *iov_cnt && ov_cnt < nitems(ov)(sizeof((ov)) / sizeof((ov)[0])); | |||
760 | i++, ov_cnt++) { | |||
761 | ov[ov_cnt].iov_base = (*iov)[i].iov_base; | |||
762 | ov[ov_cnt].iov_len = (*iov)[i].iov_len; | |||
763 | } | |||
764 | } | |||
765 | ||||
766 | xsm = xs_get_msg(xs, !cold); | |||
767 | ||||
768 | if (xs_get_buf(xst, xsm, datalen)) { | |||
769 | xs_put_msg(xs, xsm); | |||
770 | return (ENOMEM12); | |||
771 | } | |||
772 | ||||
773 | xsm->xsm_hdr.xmh_tid = xst->xst_id; | |||
774 | xsm->xsm_hdr.xmh_type = cmd; | |||
775 | xsm->xsm_hdr.xmh_rid = atomic_inc_int_nv(&xs->xs_rid)_atomic_add_int_nv((&xs->xs_rid), 1); | |||
776 | ||||
777 | for (i = 0; i < ov_cnt; i++) | |||
778 | xsm->xsm_hdr.xmh_len += ov[i].iov_len; | |||
779 | ||||
780 | if (xsm->xsm_hdr.xmh_len > XS_MAX_PAYLOAD1024) { | |||
781 | printf("%s: message type %d with payload above the limit\n", | |||
782 | xs->xs_sc->sc_dev.dv_xname, cmd); | |||
783 | xs_put_buf(xst, xsm); | |||
784 | xs_put_msg(xs, xsm); | |||
785 | return (EIO5); | |||
786 | } | |||
787 | ||||
788 | if (xs_start(xst, xsm, ov, ov_cnt)) { | |||
789 | printf("%s: message type %d transmission failed\n", | |||
790 | xs->xs_sc->sc_dev.dv_xname, cmd); | |||
791 | xs_put_buf(xst, xsm); | |||
792 | xs_put_msg(xs, xsm); | |||
793 | return (EIO5); | |||
794 | } | |||
795 | ||||
796 | xsm = xs_reply(xst, xsm->xsm_hdr.xmh_rid); | |||
797 | ||||
798 | if (xsm->xsm_hdr.xmh_type == XS_ERROR0x10) { | |||
799 | error = xs_geterror(xsm); | |||
800 | DPRINTF("%s: xenstore request %d \"%s\" error %s\n", | |||
801 | xs->xs_sc->sc_dev.dv_xname, cmd, path, xsm->xsm_data); | |||
802 | } else if (xsm->xsm_error != 0) | |||
803 | error = xsm->xsm_error; | |||
804 | else if (mode == READ) { | |||
805 | KASSERT(iov && iov_cnt)((iov && iov_cnt) ? (void)0 : __assert("diagnostic ", "/usr/src/sys/dev/pv/xenstore.c", 805, "iov && iov_cnt" )); | |||
806 | error = xs_parse(xst, xsm, iov, iov_cnt); | |||
807 | } | |||
808 | #ifdef XS_DEBUG | |||
809 | else | |||
810 | if (strcmp(xsm->xsm_data, "OK")) | |||
811 | printf("%s: xenstore request %d failed: %s\n", | |||
812 | xs->xs_sc->sc_dev.dv_xname, cmd, xsm->xsm_data); | |||
813 | #endif | |||
814 | ||||
815 | xs_put_buf(xst, xsm); | |||
816 | xs_put_msg(xs, xsm); | |||
817 | ||||
818 | return (error); | |||
819 | } | |||
820 | ||||
821 | int | |||
822 | xs_watch(void *xsc, const char *path, const char *property, struct task *task, | |||
823 | void (*cb)(void *), void *arg) | |||
824 | { | |||
825 | struct xen_softc *sc = xsc; | |||
826 | struct xs_softc *xs = sc->sc_xs; | |||
827 | struct xs_transaction xst; | |||
828 | struct xs_watch *xsw; | |||
829 | struct iovec iov, *iovp = &iov; | |||
830 | char key[256]; | |||
831 | int error, iov_cnt, ret; | |||
832 | ||||
833 | memset(&xst, 0, sizeof(xst))__builtin_memset((&xst), (0), (sizeof(xst))); | |||
834 | xst.xst_id = 0; | |||
835 | xst.xst_cookie = sc->sc_xs; | |||
836 | ||||
837 | xsw = malloc(sizeof(*xsw), M_DEVBUF2, M_NOWAIT0x0002 | M_ZERO0x0008); | |||
838 | if (xsw == NULL((void *)0)) | |||
839 | return (-1); | |||
840 | ||||
841 | task_set(task, cb, arg); | |||
842 | xsw->xsw_task = task; | |||
843 | ||||
844 | snprintf(xsw->xsw_token, sizeof(xsw->xsw_token), "%0lx", | |||
845 | (unsigned long)xsw); | |||
846 | ||||
847 | if (path) | |||
848 | ret = snprintf(key, sizeof(key), "%s/%s", path, property); | |||
849 | else | |||
850 | ret = snprintf(key, sizeof(key), "%s", property); | |||
851 | if (ret == -1 || ret >= sizeof(key)) { | |||
852 | free(xsw, M_DEVBUF2, sizeof(*xsw)); | |||
853 | return (EINVAL22); | |||
854 | } | |||
855 | ||||
856 | iov.iov_base = xsw->xsw_token; | |||
857 | iov.iov_len = sizeof(xsw->xsw_token); | |||
858 | iov_cnt = 1; | |||
859 | ||||
860 | /* | |||
861 | * xs_watches must be prepared pre-emptively because a xenstore | |||
862 | * event is raised immediately after a watch is established. | |||
863 | */ | |||
864 | mtx_enter(&xs->xs_watchlck); | |||
865 | TAILQ_INSERT_TAIL(&xs->xs_watches, xsw, xsw_entry)do { (xsw)->xsw_entry.tqe_next = ((void *)0); (xsw)->xsw_entry .tqe_prev = (&xs->xs_watches)->tqh_last; *(&xs-> xs_watches)->tqh_last = (xsw); (&xs->xs_watches)-> tqh_last = &(xsw)->xsw_entry.tqe_next; } while (0); | |||
866 | mtx_leave(&xs->xs_watchlck); | |||
867 | ||||
868 | if ((error = xs_cmd(&xst, XS_WATCH0x04, key, &iovp, &iov_cnt)) != 0) { | |||
869 | mtx_enter(&xs->xs_watchlck); | |||
870 | TAILQ_REMOVE(&xs->xs_watches, xsw, xsw_entry)do { if (((xsw)->xsw_entry.tqe_next) != ((void *)0)) (xsw) ->xsw_entry.tqe_next->xsw_entry.tqe_prev = (xsw)->xsw_entry .tqe_prev; else (&xs->xs_watches)->tqh_last = (xsw) ->xsw_entry.tqe_prev; *(xsw)->xsw_entry.tqe_prev = (xsw )->xsw_entry.tqe_next; ((xsw)->xsw_entry.tqe_prev) = (( void *)-1); ((xsw)->xsw_entry.tqe_next) = ((void *)-1); } while (0); | |||
871 | mtx_leave(&xs->xs_watchlck); | |||
872 | free(xsw, M_DEVBUF2, sizeof(*xsw)); | |||
873 | return (error); | |||
874 | } | |||
875 | ||||
876 | return (0); | |||
877 | } | |||
878 | ||||
879 | static unsigned long long | |||
880 | atoull(const char *cp, int *error) | |||
881 | { | |||
882 | unsigned long long res, cutoff; | |||
883 | int ch; | |||
884 | int cutlim; | |||
885 | ||||
886 | res = 0; | |||
887 | cutoff = ULLONG_MAX0xffffffffffffffffULL / (unsigned long long)10; | |||
888 | cutlim = ULLONG_MAX0xffffffffffffffffULL % (unsigned long long)10; | |||
889 | ||||
890 | do { | |||
891 | if (*cp < '0' || *cp > '9') { | |||
892 | *error = EINVAL22; | |||
893 | return (res); | |||
894 | } | |||
895 | ch = *cp - '0'; | |||
896 | if (res > cutoff || (res == cutoff && ch > cutlim)) { | |||
897 | *error = ERANGE34; | |||
898 | return (res); | |||
899 | } | |||
900 | res *= 10; | |||
901 | res += ch; | |||
902 | } while (*(++cp) != '\0'); | |||
903 | ||||
904 | *error = 0; | |||
905 | return (res); | |||
906 | } | |||
907 | ||||
908 | int | |||
909 | xs_getnum(void *xsc, const char *path, const char *property, | |||
910 | unsigned long long *val) | |||
911 | { | |||
912 | char *buf; | |||
913 | int error = 0; | |||
914 | ||||
915 | buf = malloc(XS_MAX_PAYLOAD1024, M_DEVBUF2, M_ZERO0x0008 | | |||
916 | (cold ? M_NOWAIT0x0002 : M_WAITOK0x0001)); | |||
917 | if (buf == NULL((void *)0)) | |||
918 | return (ENOMEM12); | |||
919 | ||||
920 | error = xs_getprop(xsc, path, property, buf, XS_MAX_PAYLOAD1024); | |||
921 | if (error) | |||
922 | goto out; | |||
923 | ||||
924 | *val = atoull(buf, &error); | |||
925 | if (error) | |||
926 | goto out; | |||
927 | ||||
928 | out: | |||
929 | free(buf, M_DEVBUF2, XS_MAX_PAYLOAD1024); | |||
930 | return (error); | |||
931 | } | |||
932 | ||||
933 | int | |||
934 | xs_setnum(void *xsc, const char *path, const char *property, | |||
935 | unsigned long long val) | |||
936 | { | |||
937 | char buf[32]; | |||
938 | int ret; | |||
939 | ||||
940 | ret = snprintf(buf, sizeof(buf), "%llu", val); | |||
941 | if (ret == -1 || ret >= sizeof(buf)) | |||
942 | return (ERANGE34); | |||
943 | ||||
944 | return (xs_setprop(xsc, path, property, buf, strlen(buf))); | |||
945 | } | |||
946 | ||||
947 | int | |||
948 | xs_getprop(void *xsc, const char *path, const char *property, char *value, | |||
949 | int size) | |||
950 | { | |||
951 | struct xen_softc *sc = xsc; | |||
952 | struct xs_transaction xst; | |||
953 | struct iovec *iovp = NULL((void *)0); | |||
954 | char key[256]; | |||
955 | int error, ret, iov_cnt = 0; | |||
956 | ||||
957 | if (!property) | |||
958 | return (EINVAL22); | |||
959 | ||||
960 | memset(&xst, 0, sizeof(xst))__builtin_memset((&xst), (0), (sizeof(xst))); | |||
961 | xst.xst_id = 0; | |||
962 | xst.xst_cookie = sc->sc_xs; | |||
963 | ||||
964 | if (path) | |||
965 | ret = snprintf(key, sizeof(key), "%s/%s", path, property); | |||
966 | else | |||
967 | ret = snprintf(key, sizeof(key), "%s", property); | |||
968 | if (ret == -1 || ret >= sizeof(key)) | |||
969 | return (EINVAL22); | |||
970 | ||||
971 | if ((error = xs_cmd(&xst, XS_READ0x02, key, &iovp, &iov_cnt)) != 0) | |||
972 | return (error); | |||
973 | ||||
974 | if (iov_cnt > 0) | |||
975 | strlcpy(value, (char *)iovp->iov_base, size); | |||
976 | ||||
977 | xs_resfree(&xst, iovp, iov_cnt); | |||
978 | ||||
979 | return (0); | |||
980 | } | |||
981 | ||||
982 | int | |||
983 | xs_setprop(void *xsc, const char *path, const char *property, char *value, | |||
984 | int size) | |||
985 | { | |||
986 | struct xen_softc *sc = xsc; | |||
987 | struct xs_transaction xst; | |||
988 | struct iovec iov, *iovp = &iov; | |||
989 | char key[256]; | |||
990 | int error, ret, iov_cnt = 0; | |||
991 | ||||
992 | if (!property) | |||
993 | return (EINVAL22); | |||
994 | ||||
995 | memset(&xst, 0, sizeof(xst))__builtin_memset((&xst), (0), (sizeof(xst))); | |||
996 | xst.xst_id = 0; | |||
997 | xst.xst_cookie = sc->sc_xs; | |||
998 | ||||
999 | if (path) | |||
1000 | ret = snprintf(key, sizeof(key), "%s/%s", path, property); | |||
1001 | else | |||
1002 | ret = snprintf(key, sizeof(key), "%s", property); | |||
1003 | if (ret == -1 || ret >= sizeof(key)) | |||
1004 | return (EINVAL22); | |||
1005 | ||||
1006 | iov.iov_base = value; | |||
1007 | iov.iov_len = size; | |||
1008 | iov_cnt = 1; | |||
1009 | ||||
1010 | error = xs_cmd(&xst, XS_WRITE0x0b, key, &iovp, &iov_cnt); | |||
1011 | ||||
1012 | return (error); | |||
1013 | } | |||
1014 | ||||
1015 | int | |||
1016 | xs_cmpprop(void *xsc, const char *path, const char *property, const char *value, | |||
1017 | int *result) | |||
1018 | { | |||
1019 | struct xen_softc *sc = xsc; | |||
1020 | struct xs_transaction xst; | |||
1021 | struct iovec *iovp = NULL((void *)0); | |||
1022 | char key[256]; | |||
1023 | int error, ret, iov_cnt = 0; | |||
1024 | ||||
1025 | if (!property) | |||
1026 | return (EINVAL22); | |||
1027 | ||||
1028 | memset(&xst, 0, sizeof(xst))__builtin_memset((&xst), (0), (sizeof(xst))); | |||
1029 | xst.xst_id = 0; | |||
1030 | xst.xst_cookie = sc->sc_xs; | |||
1031 | ||||
1032 | if (path) | |||
1033 | ret = snprintf(key, sizeof(key), "%s/%s", path, property); | |||
1034 | else | |||
1035 | ret = snprintf(key, sizeof(key), "%s", property); | |||
1036 | if (ret == -1 || ret >= sizeof(key)) | |||
1037 | return (EINVAL22); | |||
1038 | ||||
1039 | if ((error = xs_cmd(&xst, XS_READ0x02, key, &iovp, &iov_cnt)) != 0) | |||
1040 | return (error); | |||
1041 | ||||
1042 | *result = strcmp(value, (char *)iovp->iov_base); | |||
| ||||
1043 | ||||
1044 | xs_resfree(&xst, iovp, iov_cnt); | |||
1045 | ||||
1046 | return (0); | |||
1047 | } | |||
1048 | ||||
1049 | int | |||
1050 | xs_await_transition(void *xsc, const char *path, const char *property, | |||
1051 | const char *value, int timo) | |||
1052 | { | |||
1053 | struct xen_softc *sc = xsc; | |||
1054 | int error, res; | |||
1055 | ||||
1056 | do { | |||
1057 | error = xs_cmpprop(xsc, path, property, value, &res); | |||
| ||||
1058 | if (error) | |||
1059 | return (error); | |||
1060 | if (timo && --timo == 0) | |||
1061 | return (ETIMEDOUT60); | |||
1062 | xs_poll(sc->sc_xs, cold); | |||
1063 | } while (res != 0); | |||
1064 | ||||
1065 | return (0); | |||
1066 | } | |||
1067 | ||||
1068 | int | |||
1069 | xs_kvop(void *xsc, int op, char *key, char *value, size_t valuelen) | |||
1070 | { | |||
1071 | struct xen_softc *sc = xsc; | |||
1072 | struct xs_transaction xst; | |||
1073 | struct iovec iov, *iovp = &iov; | |||
1074 | int error = 0, iov_cnt = 0, cmd, i; | |||
1075 | ||||
1076 | switch (op) { | |||
1077 | case PVBUS_KVWRITE: | |||
1078 | cmd = XS_WRITE0x0b; | |||
1079 | iov.iov_base = value; | |||
1080 | iov.iov_len = strlen(value); | |||
1081 | iov_cnt = 1; | |||
1082 | break; | |||
1083 | case PVBUS_KVREAD: | |||
1084 | cmd = XS_READ0x02; | |||
1085 | break; | |||
1086 | case PVBUS_KVLS: | |||
1087 | cmd = XS_LIST0x01; | |||
1088 | break; | |||
1089 | default: | |||
1090 | return (EOPNOTSUPP45); | |||
1091 | } | |||
1092 | ||||
1093 | memset(&xst, 0, sizeof(xst))__builtin_memset((&xst), (0), (sizeof(xst))); | |||
1094 | xst.xst_id = 0; | |||
1095 | xst.xst_cookie = sc->sc_xs; | |||
1096 | ||||
1097 | if ((error = xs_cmd(&xst, cmd, key, &iovp, &iov_cnt)) != 0) | |||
1098 | return (error); | |||
1099 | ||||
1100 | memset(value, 0, valuelen)__builtin_memset((value), (0), (valuelen)); | |||
1101 | ||||
1102 | switch (cmd) { | |||
1103 | case XS_READ0x02: | |||
1104 | if (iov_cnt == 1 && iovp[0].iov_len == 1) { | |||
1105 | xs_resfree(&xst, iovp, iov_cnt); | |||
1106 | ||||
1107 | /* | |||
1108 | * We cannot distinguish if the returned value is | |||
1109 | * a directory or a file in the xenstore. The only | |||
1110 | * indication is that the read value of a directory | |||
1111 | * returns an empty string (single nul byte), | |||
1112 | * so try to get the directory list in this case. | |||
1113 | */ | |||
1114 | return (xs_kvop(xsc, PVBUS_KVLS, key, value, valuelen)); | |||
1115 | } | |||
1116 | /* FALLTHROUGH */ | |||
1117 | case XS_LIST0x01: | |||
1118 | for (i = 0; i < iov_cnt; i++) { | |||
1119 | if (i > 0 && strlcat(value, "\n", valuelen) >= | |||
1120 | valuelen) { | |||
1121 | error = ERANGE34; | |||
1122 | break; | |||
1123 | } | |||
1124 | if (strlcat(value, iovp[i].iov_base, | |||
1125 | valuelen) >= valuelen) { | |||
1126 | error = ERANGE34; | |||
1127 | break; | |||
1128 | } | |||
1129 | } | |||
1130 | xs_resfree(&xst, iovp, iov_cnt); | |||
1131 | break; | |||
1132 | default: | |||
1133 | break; | |||
1134 | } | |||
1135 | ||||
1136 | return (error); | |||
1137 | } |