1 | /* $NetBSD: xenbus_client.c,v 1.13 2014/09/21 12:46:15 bouyer Exp $ */ |
2 | /****************************************************************************** |
3 | * Client-facing interface for the Xenbus driver. In other words, the |
4 | * interface between the Xenbus and the device-specific code, be it the |
5 | * frontend or the backend of that driver. |
6 | * |
7 | * Copyright (C) 2005 XenSource Ltd |
8 | * |
9 | * This file may be distributed separately from the Linux kernel, or |
10 | * incorporated into other software packages, subject to the following license: |
11 | * |
12 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
13 | * of this source file (the "Software"), to deal in the Software without |
14 | * restriction, including without limitation the rights to use, copy, modify, |
15 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, |
16 | * and to permit persons to whom the Software is furnished to do so, subject to |
17 | * the following conditions: |
18 | * |
19 | * The above copyright notice and this permission notice shall be included in |
20 | * all copies or substantial portions of the Software. |
21 | * |
22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
25 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
27 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
28 | * IN THE SOFTWARE. |
29 | */ |
30 | |
31 | #include <sys/cdefs.h> |
32 | __KERNEL_RCSID(0, "$NetBSD: xenbus_client.c,v 1.13 2014/09/21 12:46:15 bouyer Exp $" ); |
33 | |
34 | #if 0 |
35 | #define DPRINTK(fmt, args...) \ |
36 | printk("xenbus_client (%s:%d) " fmt ".\n", __func__, __LINE__, ##args) |
37 | #else |
38 | #define DPRINTK(fmt, args...) ((void)0) |
39 | #endif |
40 | |
41 | #include <sys/types.h> |
42 | #include <sys/null.h> |
43 | #include <sys/errno.h> |
44 | #include <sys/malloc.h> |
45 | #include <sys/systm.h> |
46 | |
47 | #include <xen/xen.h> |
48 | #include <xen/hypervisor.h> |
49 | #include <xen/evtchn.h> |
50 | #include <xen/xenbus.h> |
51 | #include <xen/granttables.h> |
52 | |
53 | |
54 | int |
55 | xenbus_watch_path(struct xenbus_device *dev, char *path, |
56 | struct xenbus_watch *watch, |
57 | void (*callback)(struct xenbus_watch *, |
58 | const char **, unsigned int)) |
59 | { |
60 | int err; |
61 | |
62 | watch->node = path; |
63 | watch->xbw_callback = callback; |
64 | |
65 | err = register_xenbus_watch(watch); |
66 | |
67 | if (err) { |
68 | watch->node = NULL; |
69 | watch->xbw_callback = NULL; |
70 | xenbus_dev_fatal(dev, err, "adding watch on %s" , path); |
71 | } |
72 | err = 0; |
73 | |
74 | return err; |
75 | } |
76 | |
77 | int |
78 | xenbus_watch_path2(struct xenbus_device *dev, const char *path, |
79 | const char *path2, struct xenbus_watch *watch, |
80 | void (*callback)(struct xenbus_watch *, |
81 | const char **, unsigned int)) |
82 | { |
83 | int err; |
84 | char *state; |
85 | |
86 | DPRINTK("xenbus_watch_path2 path %s path2 %s\n" , path, path2); |
87 | state = |
88 | malloc(strlen(path) + 1 + strlen(path2) + 1, M_DEVBUF, |
89 | M_NOWAIT); |
90 | if (!state) { |
91 | xenbus_dev_fatal(dev, ENOMEM, "allocating path for watch" ); |
92 | return ENOMEM; |
93 | } |
94 | strcpy(state, path); |
95 | strcat(state, "/" ); |
96 | strcat(state, path2); |
97 | |
98 | err = xenbus_watch_path(dev, state, watch, callback); |
99 | |
100 | if (err) { |
101 | free(state, M_DEVBUF); |
102 | } |
103 | return err; |
104 | } |
105 | |
106 | |
107 | int |
108 | xenbus_switch_state(struct xenbus_device *dev, |
109 | struct xenbus_transaction *xbt, |
110 | XenbusState state) |
111 | { |
112 | /* We check whether the state is currently set to the given value, and |
113 | if not, then the state is set. We don't want to unconditionally |
114 | write the given state, because we don't want to fire watches |
115 | unnecessarily. Furthermore, if the node has gone, we don't write |
116 | to it, as the device will be tearing down, and we don't want to |
117 | resurrect that directory. |
118 | */ |
119 | |
120 | u_long current_state; |
121 | |
122 | int err = xenbus_read_ul(xbt, dev->xbusd_path, "state" , |
123 | ¤t_state, 10); |
124 | if (err) |
125 | return 0; |
126 | |
127 | if ((XenbusState)current_state == state) |
128 | return 0; |
129 | |
130 | err = xenbus_printf(xbt, dev->xbusd_path, "state" , "%d" , state); |
131 | if (err) { |
132 | xenbus_dev_fatal(dev, err, "writing new state" ); |
133 | return err; |
134 | } |
135 | return 0; |
136 | } |
137 | |
138 | /** |
139 | * Return the path to the error node for the given device, or NULL on failure. |
140 | * If the value returned is non-NULL, then it is the caller's to kfree. |
141 | */ |
142 | static char * |
143 | error_path(struct xenbus_device *dev) |
144 | { |
145 | char *path_buffer = malloc(strlen("error/" ) + strlen(dev->xbusd_path) + |
146 | 1, M_DEVBUF, M_NOWAIT); |
147 | if (path_buffer == NULL) { |
148 | return NULL; |
149 | } |
150 | |
151 | strcpy(path_buffer, "error/" ); |
152 | strcpy(path_buffer + strlen("error/" ), dev->xbusd_path); |
153 | |
154 | return path_buffer; |
155 | } |
156 | |
157 | |
158 | static void |
159 | _dev_error(struct xenbus_device *dev, int err, const char *fmt, |
160 | va_list ap) |
161 | { |
162 | int ret __diagused; |
163 | unsigned int len; |
164 | char *printf_buffer = NULL, *path_buffer = NULL; |
165 | |
166 | #define PRINTF_BUFFER_SIZE 4096 |
167 | printf_buffer = malloc(PRINTF_BUFFER_SIZE, M_DEVBUF, M_NOWAIT); |
168 | if (printf_buffer == NULL) |
169 | goto fail; |
170 | |
171 | len = snprintf(printf_buffer, PRINTF_BUFFER_SIZE, "%i " , -err); |
172 | KASSERT(len < PRINTF_BUFFER_SIZE); |
173 | ret = vsnprintf(printf_buffer+len, PRINTF_BUFFER_SIZE-len, fmt, ap); |
174 | KASSERT(len + ret < PRINTF_BUFFER_SIZE); |
175 | dev->xbusd_has_error = 1; |
176 | |
177 | path_buffer = error_path(dev); |
178 | |
179 | if (path_buffer == NULL) { |
180 | printk("xenbus: failed to write error node for %s (%s)\n" , |
181 | dev->xbusd_path, printf_buffer); |
182 | goto fail; |
183 | } |
184 | |
185 | if (xenbus_write(NULL, path_buffer, "error" , printf_buffer) != 0) { |
186 | printk("xenbus: failed to write error node for %s (%s)\n" , |
187 | dev->xbusd_path, printf_buffer); |
188 | goto fail; |
189 | } |
190 | |
191 | fail: |
192 | if (printf_buffer) |
193 | free(printf_buffer, M_DEVBUF); |
194 | if (path_buffer) |
195 | free(path_buffer, M_DEVBUF); |
196 | } |
197 | |
198 | |
199 | void |
200 | xenbus_dev_error(struct xenbus_device *dev, int err, const char *fmt, |
201 | ...) |
202 | { |
203 | va_list ap; |
204 | |
205 | va_start(ap, fmt); |
206 | _dev_error(dev, err, fmt, ap); |
207 | va_end(ap); |
208 | } |
209 | |
210 | |
211 | void |
212 | xenbus_dev_fatal(struct xenbus_device *dev, int err, const char *fmt, |
213 | ...) |
214 | { |
215 | va_list ap; |
216 | |
217 | va_start(ap, fmt); |
218 | _dev_error(dev, err, fmt, ap); |
219 | va_end(ap); |
220 | |
221 | xenbus_switch_state(dev, NULL, XenbusStateClosing); |
222 | } |
223 | |
224 | |
225 | int |
226 | xenbus_grant_ring(struct xenbus_device *dev, paddr_t ring_pa, |
227 | grant_ref_t *entryp) |
228 | { |
229 | int err = xengnt_grant_access(dev->xbusd_otherend_id, ring_pa, |
230 | 0, entryp); |
231 | if (err != 0) |
232 | xenbus_dev_fatal(dev, err, "granting access to ring page" ); |
233 | return err; |
234 | } |
235 | |
236 | |
237 | int |
238 | xenbus_alloc_evtchn(struct xenbus_device *dev, int *port) |
239 | { |
240 | evtchn_op_t op = { |
241 | .cmd = EVTCHNOP_alloc_unbound, |
242 | .u.alloc_unbound = { |
243 | .dom = DOMID_SELF, |
244 | .remote_dom = dev->xbusd_otherend_id, |
245 | .port = 0 |
246 | } |
247 | }; |
248 | |
249 | int err = HYPERVISOR_event_channel_op(&op); |
250 | if (err) |
251 | xenbus_dev_fatal(dev, err, "allocating event channel" ); |
252 | else |
253 | *port = op.u.alloc_unbound.port; |
254 | return err; |
255 | } |
256 | |
257 | |
258 | XenbusState |
259 | xenbus_read_driver_state(const char *path) |
260 | { |
261 | u_long result; |
262 | |
263 | int err = xenbus_read_ul(NULL, path, "state" , &result, 10); |
264 | if (err) |
265 | result = XenbusStateClosed; |
266 | |
267 | return result; |
268 | } |
269 | |
270 | |
271 | /* |
272 | * Local variables: |
273 | * c-file-style: "linux" |
274 | * indent-tabs-mode: t |
275 | * c-indent-level: 8 |
276 | * c-basic-offset: 8 |
277 | * tab-width: 8 |
278 | * End: |
279 | */ |
280 | |