1 | /****************************************************************************** |
2 | * Talks to Xen Store to figure out what devices we have (backend half). |
3 | * |
4 | * Copyright (C) 2005 Rusty Russell, IBM Corporation |
5 | * Copyright (C) 2005 Mike Wray, Hewlett-Packard |
6 | * Copyright (C) 2005, 2006 XenSource Ltd |
7 | * Copyright (C) 2007 Solarflare Communications, Inc. |
8 | * |
9 | * This program is free software; you can redistribute it and/or |
10 | * modify it under the terms of the GNU General Public License version 2 |
11 | * as published by the Free Software Foundation; or, when distributed |
12 | * separately from the Linux kernel or incorporated into other |
13 | * software packages, subject to the following license: |
14 | * |
15 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
16 | * of this source file (the "Software"), to deal in the Software without |
17 | * restriction, including without limitation the rights to use, copy, modify, |
18 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, |
19 | * and to permit persons to whom the Software is furnished to do so, subject to |
20 | * the following conditions: |
21 | * |
22 | * The above copyright notice and this permission notice shall be included in |
23 | * all copies or substantial portions of the Software. |
24 | * |
25 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
26 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
27 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
28 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
29 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
30 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
31 | * IN THE SOFTWARE. |
32 | */ |
33 | |
34 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
35 | |
36 | #define DPRINTK(fmt, ...) \ |
37 | pr_debug("(%s:%d) " fmt "\n", \ |
38 | __func__, __LINE__, ##__VA_ARGS__) |
39 | |
40 | #include <linux/kernel.h> |
41 | #include <linux/err.h> |
42 | #include <linux/string.h> |
43 | #include <linux/ctype.h> |
44 | #include <linux/fcntl.h> |
45 | #include <linux/mm.h> |
46 | #include <linux/notifier.h> |
47 | #include <linux/export.h> |
48 | #include <linux/semaphore.h> |
49 | |
50 | #include <asm/page.h> |
51 | #include <asm/xen/hypervisor.h> |
52 | #include <asm/hypervisor.h> |
53 | #include <xen/xenbus.h> |
54 | #include <xen/features.h> |
55 | |
56 | #include "xenbus.h" |
57 | |
58 | /* backend/<type>/<fe-uuid>/<id> => <type>-<fe-domid>-<id> */ |
59 | static int backend_bus_id(char bus_id[XEN_BUS_ID_SIZE], const char *nodename) |
60 | { |
61 | int domid, err; |
62 | const char *devid, *type, *frontend; |
63 | unsigned int typelen; |
64 | |
65 | type = strchr(nodename, '/'); |
66 | if (!type) |
67 | return -EINVAL; |
68 | type++; |
69 | typelen = strcspn(type, "/" ); |
70 | if (!typelen || type[typelen] != '/') |
71 | return -EINVAL; |
72 | |
73 | devid = strrchr(nodename, '/') + 1; |
74 | |
75 | err = xenbus_gather(XBT_NIL, dir: nodename, "frontend-id" , "%i" , &domid, |
76 | "frontend" , NULL, &frontend, |
77 | NULL); |
78 | if (err) |
79 | return err; |
80 | if (strlen(frontend) == 0) |
81 | err = -ERANGE; |
82 | if (!err && !xenbus_exists(XBT_NIL, frontend, "" )) |
83 | err = -ENOENT; |
84 | kfree(frontend); |
85 | |
86 | if (err) |
87 | return err; |
88 | |
89 | if (snprintf(bus_id, XEN_BUS_ID_SIZE, "%.*s-%i-%s" , |
90 | typelen, type, domid, devid) >= XEN_BUS_ID_SIZE) |
91 | return -ENOSPC; |
92 | return 0; |
93 | } |
94 | |
95 | static int xenbus_uevent_backend(const struct device *dev, |
96 | struct kobj_uevent_env *env) |
97 | { |
98 | const struct xenbus_device *xdev; |
99 | const struct xenbus_driver *drv; |
100 | const struct xen_bus_type *bus; |
101 | |
102 | DPRINTK("" ); |
103 | |
104 | if (dev == NULL) |
105 | return -ENODEV; |
106 | |
107 | xdev = to_xenbus_device(dev); |
108 | bus = container_of(xdev->dev.bus, struct xen_bus_type, bus); |
109 | |
110 | if (add_uevent_var(env, format: "MODALIAS=xen-backend:%s" , xdev->devicetype)) |
111 | return -ENOMEM; |
112 | |
113 | /* stuff we want to pass to /sbin/hotplug */ |
114 | if (add_uevent_var(env, format: "XENBUS_TYPE=%s" , xdev->devicetype)) |
115 | return -ENOMEM; |
116 | |
117 | if (add_uevent_var(env, format: "XENBUS_PATH=%s" , xdev->nodename)) |
118 | return -ENOMEM; |
119 | |
120 | if (add_uevent_var(env, format: "XENBUS_BASE_PATH=%s" , bus->root)) |
121 | return -ENOMEM; |
122 | |
123 | if (dev->driver) { |
124 | drv = to_xenbus_driver(drv: dev->driver); |
125 | if (drv && drv->uevent) |
126 | return drv->uevent(xdev, env); |
127 | } |
128 | |
129 | return 0; |
130 | } |
131 | |
132 | /* backend/<typename>/<frontend-uuid>/<name> */ |
133 | static int xenbus_probe_backend_unit(struct xen_bus_type *bus, |
134 | const char *dir, |
135 | const char *type, |
136 | const char *name) |
137 | { |
138 | char *nodename; |
139 | int err; |
140 | |
141 | nodename = kasprintf(GFP_KERNEL, fmt: "%s/%s" , dir, name); |
142 | if (!nodename) |
143 | return -ENOMEM; |
144 | |
145 | DPRINTK("%s\n" , nodename); |
146 | |
147 | err = xenbus_probe_node(bus, type, nodename); |
148 | kfree(objp: nodename); |
149 | return err; |
150 | } |
151 | |
152 | /* backend/<typename>/<frontend-domid> */ |
153 | static int xenbus_probe_backend(struct xen_bus_type *bus, const char *type, |
154 | const char *domid) |
155 | { |
156 | char *nodename; |
157 | int err = 0; |
158 | char **dir; |
159 | unsigned int i, dir_n = 0; |
160 | |
161 | DPRINTK("" ); |
162 | |
163 | nodename = kasprintf(GFP_KERNEL, fmt: "%s/%s/%s" , bus->root, type, domid); |
164 | if (!nodename) |
165 | return -ENOMEM; |
166 | |
167 | dir = xenbus_directory(XBT_NIL, dir: nodename, node: "" , num: &dir_n); |
168 | if (IS_ERR(dir)) { |
169 | kfree(nodename); |
170 | return PTR_ERR(dir); |
171 | } |
172 | |
173 | for (i = 0; i < dir_n; i++) { |
174 | err = xenbus_probe_backend_unit(bus, nodename, type, dir[i]); |
175 | if (err) |
176 | break; |
177 | } |
178 | kfree(dir); |
179 | kfree(nodename); |
180 | return err; |
181 | } |
182 | |
183 | static bool frontend_will_handle(struct xenbus_watch *watch, |
184 | const char *path, const char *token) |
185 | { |
186 | return watch->nr_pending == 0; |
187 | } |
188 | |
189 | static void frontend_changed(struct xenbus_watch *watch, |
190 | const char *path, const char *token) |
191 | { |
192 | xenbus_otherend_changed(watch, path, token, ignore_on_shutdown: 0); |
193 | } |
194 | |
195 | static struct xen_bus_type xenbus_backend = { |
196 | .root = "backend" , |
197 | .levels = 3, /* backend/type/<frontend>/<id> */ |
198 | .get_bus_id = backend_bus_id, |
199 | .probe = xenbus_probe_backend, |
200 | .otherend_will_handle = frontend_will_handle, |
201 | .otherend_changed = frontend_changed, |
202 | .bus = { |
203 | .name = "xen-backend" , |
204 | .match = xenbus_match, |
205 | .uevent = xenbus_uevent_backend, |
206 | .probe = xenbus_dev_probe, |
207 | .remove = xenbus_dev_remove, |
208 | .dev_groups = xenbus_dev_groups, |
209 | }, |
210 | }; |
211 | |
212 | static void backend_changed(struct xenbus_watch *watch, |
213 | const char *path, const char *token) |
214 | { |
215 | DPRINTK("" ); |
216 | |
217 | xenbus_dev_changed(node: path, bus: &xenbus_backend); |
218 | } |
219 | |
220 | static struct xenbus_watch be_watch = { |
221 | .node = "backend" , |
222 | .callback = backend_changed, |
223 | }; |
224 | |
225 | static int read_frontend_details(struct xenbus_device *xendev) |
226 | { |
227 | return xenbus_read_otherend_details(xendev, id_node: "frontend-id" , path_node: "frontend" ); |
228 | } |
229 | |
230 | int xenbus_dev_is_online(struct xenbus_device *dev) |
231 | { |
232 | return !!xenbus_read_unsigned(dir: dev->nodename, node: "online" , default_val: 0); |
233 | } |
234 | EXPORT_SYMBOL_GPL(xenbus_dev_is_online); |
235 | |
236 | int __xenbus_register_backend(struct xenbus_driver *drv, struct module *owner, |
237 | const char *mod_name) |
238 | { |
239 | drv->read_otherend_details = read_frontend_details; |
240 | |
241 | return xenbus_register_driver_common(drv, bus: &xenbus_backend, |
242 | owner, mod_name); |
243 | } |
244 | EXPORT_SYMBOL_GPL(__xenbus_register_backend); |
245 | |
246 | static int backend_probe_and_watch(struct notifier_block *notifier, |
247 | unsigned long event, |
248 | void *data) |
249 | { |
250 | /* Enumerate devices in xenstore and watch for changes. */ |
251 | xenbus_probe_devices(bus: &xenbus_backend); |
252 | register_xenbus_watch(watch: &be_watch); |
253 | |
254 | return NOTIFY_DONE; |
255 | } |
256 | |
257 | static int backend_reclaim_memory(struct device *dev, void *data) |
258 | { |
259 | const struct xenbus_driver *drv; |
260 | struct xenbus_device *xdev; |
261 | |
262 | if (!dev->driver) |
263 | return 0; |
264 | drv = to_xenbus_driver(drv: dev->driver); |
265 | if (drv && drv->reclaim_memory) { |
266 | xdev = to_xenbus_device(dev); |
267 | if (down_trylock(sem: &xdev->reclaim_sem)) |
268 | return 0; |
269 | drv->reclaim_memory(xdev); |
270 | up(sem: &xdev->reclaim_sem); |
271 | } |
272 | return 0; |
273 | } |
274 | |
275 | /* |
276 | * Returns 0 always because we are using shrinker to only detect memory |
277 | * pressure. |
278 | */ |
279 | static unsigned long backend_shrink_memory_count(struct shrinker *shrinker, |
280 | struct shrink_control *sc) |
281 | { |
282 | bus_for_each_dev(bus: &xenbus_backend.bus, NULL, NULL, |
283 | fn: backend_reclaim_memory); |
284 | return 0; |
285 | } |
286 | |
287 | static int __init xenbus_probe_backend_init(void) |
288 | { |
289 | struct shrinker *backend_memory_shrinker; |
290 | static struct notifier_block xenstore_notifier = { |
291 | .notifier_call = backend_probe_and_watch |
292 | }; |
293 | int err; |
294 | |
295 | DPRINTK("" ); |
296 | |
297 | /* Register ourselves with the kernel bus subsystem */ |
298 | err = bus_register(bus: &xenbus_backend.bus); |
299 | if (err) |
300 | return err; |
301 | |
302 | register_xenstore_notifier(nb: &xenstore_notifier); |
303 | |
304 | backend_memory_shrinker = shrinker_alloc(flags: 0, fmt: "xen-backend" ); |
305 | if (!backend_memory_shrinker) { |
306 | pr_warn("shrinker allocation failed\n" ); |
307 | return 0; |
308 | } |
309 | |
310 | backend_memory_shrinker->count_objects = backend_shrink_memory_count; |
311 | |
312 | shrinker_register(shrinker: backend_memory_shrinker); |
313 | |
314 | return 0; |
315 | } |
316 | subsys_initcall(xenbus_probe_backend_init); |
317 | |