Commit | Line | Data |
---|---|---|
4d05a28d KRW |
1 | /* Xenbus code for blkif backend |
2 | Copyright (C) 2005 Rusty Russell <rusty@rustcorp.com.au> | |
3 | Copyright (C) 2005 XenSource Ltd | |
4 | ||
5 | This program is free software; you can redistribute it and/or modify | |
6 | it under the terms of the GNU General Public License as published by | |
7 | the Free Software Foundation; either version 2 of the License, or | |
8 | (at your option) any later version. | |
9 | ||
10 | This program is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | GNU General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU General Public License | |
16 | along with this program; if not, write to the Free Software | |
17 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 | */ | |
19 | ||
20 | #include <stdarg.h> | |
21 | #include <linux/module.h> | |
22 | #include <linux/kthread.h> | |
23 | #include "common.h" | |
24 | ||
25 | #undef DPRINTK | |
26 | #define DPRINTK(fmt, args...) \ | |
27 | pr_debug("blkback/xenbus (%s:%d) " fmt ".\n", \ | |
28 | __FUNCTION__, __LINE__, ##args) | |
29 | ||
30 | struct backend_info | |
31 | { | |
32 | struct xenbus_device *dev; | |
33 | blkif_t *blkif; | |
34 | struct xenbus_watch backend_watch; | |
35 | unsigned major; | |
36 | unsigned minor; | |
37 | char *mode; | |
38 | }; | |
39 | ||
40 | static void connect(struct backend_info *); | |
41 | static int connect_ring(struct backend_info *); | |
42 | static void backend_changed(struct xenbus_watch *, const char **, | |
43 | unsigned int); | |
44 | ||
45 | static int blkback_name(blkif_t *blkif, char *buf) | |
46 | { | |
47 | char *devpath, *devname; | |
48 | struct xenbus_device *dev = blkif->be->dev; | |
49 | ||
50 | devpath = xenbus_read(XBT_NIL, dev->nodename, "dev", NULL); | |
51 | if (IS_ERR(devpath)) | |
52 | return PTR_ERR(devpath); | |
53 | ||
54 | if ((devname = strstr(devpath, "/dev/")) != NULL) | |
55 | devname += strlen("/dev/"); | |
56 | else | |
57 | devname = devpath; | |
58 | ||
59 | snprintf(buf, TASK_COMM_LEN, "blkback.%d.%s", blkif->domid, devname); | |
60 | kfree(devpath); | |
61 | ||
62 | return 0; | |
63 | } | |
64 | ||
65 | static void update_blkif_status(blkif_t *blkif) | |
66 | { | |
67 | int err; | |
68 | char name[TASK_COMM_LEN]; | |
69 | ||
70 | /* Not ready to connect? */ | |
71 | if (!blkif->irq || !blkif->vbd.bdev) | |
72 | return; | |
73 | ||
74 | /* Already connected? */ | |
75 | if (blkif->be->dev->state == XenbusStateConnected) | |
76 | return; | |
77 | ||
78 | /* Attempt to connect: exit if we fail to. */ | |
79 | connect(blkif->be); | |
80 | if (blkif->be->dev->state != XenbusStateConnected) | |
81 | return; | |
82 | ||
83 | err = blkback_name(blkif, name); | |
84 | if (err) { | |
85 | xenbus_dev_error(blkif->be->dev, err, "get blkback dev name"); | |
86 | return; | |
87 | } | |
88 | ||
89 | blkif->xenblkd = kthread_run(blkif_schedule, blkif, name); | |
90 | if (IS_ERR(blkif->xenblkd)) { | |
91 | err = PTR_ERR(blkif->xenblkd); | |
92 | blkif->xenblkd = NULL; | |
93 | xenbus_dev_error(blkif->be->dev, err, "start xenblkd"); | |
94 | } | |
95 | } | |
96 | ||
97 | ||
98 | /**************************************************************** | |
99 | * sysfs interface for VBD I/O requests | |
100 | */ | |
101 | ||
102 | #define VBD_SHOW(name, format, args...) \ | |
103 | static ssize_t show_##name(struct device *_dev, \ | |
104 | struct device_attribute *attr, \ | |
105 | char *buf) \ | |
106 | { \ | |
107 | struct xenbus_device *dev = to_xenbus_device(_dev); \ | |
108 | struct backend_info *be = dev->dev.driver_data; \ | |
109 | \ | |
110 | return sprintf(buf, format, ##args); \ | |
111 | } \ | |
112 | static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL) | |
113 | ||
114 | VBD_SHOW(oo_req, "%d\n", be->blkif->st_oo_req); | |
115 | VBD_SHOW(rd_req, "%d\n", be->blkif->st_rd_req); | |
116 | VBD_SHOW(wr_req, "%d\n", be->blkif->st_wr_req); | |
117 | VBD_SHOW(br_req, "%d\n", be->blkif->st_br_req); | |
118 | VBD_SHOW(rd_sect, "%d\n", be->blkif->st_rd_sect); | |
119 | VBD_SHOW(wr_sect, "%d\n", be->blkif->st_wr_sect); | |
120 | ||
121 | static struct attribute *vbdstat_attrs[] = { | |
122 | &dev_attr_oo_req.attr, | |
123 | &dev_attr_rd_req.attr, | |
124 | &dev_attr_wr_req.attr, | |
125 | &dev_attr_br_req.attr, | |
126 | &dev_attr_rd_sect.attr, | |
127 | &dev_attr_wr_sect.attr, | |
128 | NULL | |
129 | }; | |
130 | ||
131 | static struct attribute_group vbdstat_group = { | |
132 | .name = "statistics", | |
133 | .attrs = vbdstat_attrs, | |
134 | }; | |
135 | ||
136 | VBD_SHOW(physical_device, "%x:%x\n", be->major, be->minor); | |
137 | VBD_SHOW(mode, "%s\n", be->mode); | |
138 | ||
139 | int xenvbd_sysfs_addif(struct xenbus_device *dev) | |
140 | { | |
141 | int error; | |
142 | ||
143 | error = device_create_file(&dev->dev, &dev_attr_physical_device); | |
144 | if (error) | |
145 | goto fail1; | |
146 | ||
147 | error = device_create_file(&dev->dev, &dev_attr_mode); | |
148 | if (error) | |
149 | goto fail2; | |
150 | ||
151 | error = sysfs_create_group(&dev->dev.kobj, &vbdstat_group); | |
152 | if (error) | |
153 | goto fail3; | |
154 | ||
155 | return 0; | |
156 | ||
157 | fail3: sysfs_remove_group(&dev->dev.kobj, &vbdstat_group); | |
158 | fail2: device_remove_file(&dev->dev, &dev_attr_mode); | |
159 | fail1: device_remove_file(&dev->dev, &dev_attr_physical_device); | |
160 | return error; | |
161 | } | |
162 | ||
163 | void xenvbd_sysfs_delif(struct xenbus_device *dev) | |
164 | { | |
165 | sysfs_remove_group(&dev->dev.kobj, &vbdstat_group); | |
166 | device_remove_file(&dev->dev, &dev_attr_mode); | |
167 | device_remove_file(&dev->dev, &dev_attr_physical_device); | |
168 | } | |
169 | ||
170 | static int blkback_remove(struct xenbus_device *dev) | |
171 | { | |
172 | struct backend_info *be = dev->dev.driver_data; | |
173 | ||
174 | DPRINTK(""); | |
175 | ||
176 | if (be->major || be->minor) | |
177 | xenvbd_sysfs_delif(dev); | |
178 | ||
179 | if (be->backend_watch.node) { | |
180 | unregister_xenbus_watch(&be->backend_watch); | |
181 | kfree(be->backend_watch.node); | |
182 | be->backend_watch.node = NULL; | |
183 | } | |
184 | ||
185 | if (be->blkif) { | |
186 | blkif_disconnect(be->blkif); | |
187 | vbd_free(&be->blkif->vbd); | |
188 | blkif_free(be->blkif); | |
189 | be->blkif = NULL; | |
190 | } | |
191 | ||
192 | kfree(be); | |
193 | dev->dev.driver_data = NULL; | |
194 | return 0; | |
195 | } | |
196 | ||
197 | int blkback_barrier(struct xenbus_transaction xbt, | |
198 | struct backend_info *be, int state) | |
199 | { | |
200 | struct xenbus_device *dev = be->dev; | |
201 | int err; | |
202 | ||
203 | err = xenbus_printf(xbt, dev->nodename, "feature-barrier", | |
204 | "%d", state); | |
205 | if (err) | |
206 | xenbus_dev_fatal(dev, err, "writing feature-barrier"); | |
207 | ||
208 | return err; | |
209 | } | |
210 | ||
211 | /** | |
212 | * Entry point to this code when a new device is created. Allocate the basic | |
213 | * structures, and watch the store waiting for the hotplug scripts to tell us | |
214 | * the device's physical major and minor numbers. Switch to InitWait. | |
215 | */ | |
216 | static int blkback_probe(struct xenbus_device *dev, | |
217 | const struct xenbus_device_id *id) | |
218 | { | |
219 | int err; | |
220 | struct backend_info *be = kzalloc(sizeof(struct backend_info), | |
221 | GFP_KERNEL); | |
222 | if (!be) { | |
223 | xenbus_dev_fatal(dev, -ENOMEM, | |
224 | "allocating backend structure"); | |
225 | return -ENOMEM; | |
226 | } | |
227 | be->dev = dev; | |
228 | dev->dev.driver_data = be; | |
229 | ||
230 | be->blkif = blkif_alloc(dev->otherend_id); | |
231 | if (IS_ERR(be->blkif)) { | |
232 | err = PTR_ERR(be->blkif); | |
233 | be->blkif = NULL; | |
234 | xenbus_dev_fatal(dev, err, "creating block interface"); | |
235 | goto fail; | |
236 | } | |
237 | ||
238 | /* setup back pointer */ | |
239 | be->blkif->be = be; | |
240 | ||
241 | err = xenbus_watch_path2(dev, dev->nodename, "physical-device", | |
242 | &be->backend_watch, backend_changed); | |
243 | if (err) | |
244 | goto fail; | |
245 | ||
246 | err = xenbus_switch_state(dev, XenbusStateInitWait); | |
247 | if (err) | |
248 | goto fail; | |
249 | ||
250 | return 0; | |
251 | ||
252 | fail: | |
253 | DPRINTK("failed"); | |
254 | blkback_remove(dev); | |
255 | return err; | |
256 | } | |
257 | ||
258 | ||
259 | /** | |
260 | * Callback received when the hotplug scripts have placed the physical-device | |
261 | * node. Read it and the mode node, and create a vbd. If the frontend is | |
262 | * ready, connect. | |
263 | */ | |
264 | static void backend_changed(struct xenbus_watch *watch, | |
265 | const char **vec, unsigned int len) | |
266 | { | |
267 | int err; | |
268 | unsigned major; | |
269 | unsigned minor; | |
270 | struct backend_info *be | |
271 | = container_of(watch, struct backend_info, backend_watch); | |
272 | struct xenbus_device *dev = be->dev; | |
273 | int cdrom = 0; | |
274 | char *device_type; | |
275 | ||
276 | DPRINTK(""); | |
277 | ||
278 | err = xenbus_scanf(XBT_NIL, dev->nodename, "physical-device", "%x:%x", | |
279 | &major, &minor); | |
280 | if (XENBUS_EXIST_ERR(err)) { | |
281 | /* Since this watch will fire once immediately after it is | |
282 | registered, we expect this. Ignore it, and wait for the | |
283 | hotplug scripts. */ | |
284 | return; | |
285 | } | |
286 | if (err != 2) { | |
287 | xenbus_dev_fatal(dev, err, "reading physical-device"); | |
288 | return; | |
289 | } | |
290 | ||
291 | if ((be->major || be->minor) && | |
292 | ((be->major != major) || (be->minor != minor))) { | |
293 | printk(KERN_WARNING | |
294 | "blkback: changing physical device (from %x:%x to " | |
295 | "%x:%x) not supported.\n", be->major, be->minor, | |
296 | major, minor); | |
297 | return; | |
298 | } | |
299 | ||
300 | be->mode = xenbus_read(XBT_NIL, dev->nodename, "mode", NULL); | |
301 | if (IS_ERR(be->mode)) { | |
302 | err = PTR_ERR(be->mode); | |
303 | be->mode = NULL; | |
304 | xenbus_dev_fatal(dev, err, "reading mode"); | |
305 | return; | |
306 | } | |
307 | ||
308 | device_type = xenbus_read(XBT_NIL, dev->otherend, "device-type", NULL); | |
309 | if (!IS_ERR(device_type)) { | |
310 | cdrom = strcmp(device_type, "cdrom") == 0; | |
311 | kfree(device_type); | |
312 | } | |
313 | ||
314 | if (be->major == 0 && be->minor == 0) { | |
315 | /* Front end dir is a number, which is used as the handle. */ | |
316 | ||
317 | char *p = strrchr(dev->otherend, '/') + 1; | |
318 | long handle = simple_strtoul(p, NULL, 0); | |
319 | ||
320 | be->major = major; | |
321 | be->minor = minor; | |
322 | ||
323 | err = vbd_create(be->blkif, handle, major, minor, | |
324 | (NULL == strchr(be->mode, 'w')), cdrom); | |
325 | if (err) { | |
326 | be->major = be->minor = 0; | |
327 | xenbus_dev_fatal(dev, err, "creating vbd structure"); | |
328 | return; | |
329 | } | |
330 | ||
331 | err = xenvbd_sysfs_addif(dev); | |
332 | if (err) { | |
333 | vbd_free(&be->blkif->vbd); | |
334 | be->major = be->minor = 0; | |
335 | xenbus_dev_fatal(dev, err, "creating sysfs entries"); | |
336 | return; | |
337 | } | |
338 | ||
339 | /* We're potentially connected now */ | |
340 | update_blkif_status(be->blkif); | |
341 | } | |
342 | } | |
343 | ||
344 | ||
345 | /** | |
346 | * Callback received when the frontend's state changes. | |
347 | */ | |
348 | static void frontend_changed(struct xenbus_device *dev, | |
349 | enum xenbus_state frontend_state) | |
350 | { | |
351 | struct backend_info *be = dev->dev.driver_data; | |
352 | int err; | |
353 | ||
354 | DPRINTK("%s", xenbus_strstate(frontend_state)); | |
355 | ||
356 | switch (frontend_state) { | |
357 | case XenbusStateInitialising: | |
358 | if (dev->state == XenbusStateClosed) { | |
359 | printk(KERN_INFO "%s: %s: prepare for reconnect\n", | |
360 | __FUNCTION__, dev->nodename); | |
361 | xenbus_switch_state(dev, XenbusStateInitWait); | |
362 | } | |
363 | break; | |
364 | ||
365 | case XenbusStateInitialised: | |
366 | case XenbusStateConnected: | |
367 | /* Ensure we connect even when two watches fire in | |
368 | close successsion and we miss the intermediate value | |
369 | of frontend_state. */ | |
370 | if (dev->state == XenbusStateConnected) | |
371 | break; | |
372 | ||
373 | err = connect_ring(be); | |
374 | if (err) | |
375 | break; | |
376 | update_blkif_status(be->blkif); | |
377 | break; | |
378 | ||
379 | case XenbusStateClosing: | |
380 | blkif_disconnect(be->blkif); | |
381 | xenbus_switch_state(dev, XenbusStateClosing); | |
382 | break; | |
383 | ||
384 | case XenbusStateClosed: | |
385 | xenbus_switch_state(dev, XenbusStateClosed); | |
386 | if (xenbus_dev_is_online(dev)) | |
387 | break; | |
388 | /* fall through if not online */ | |
389 | case XenbusStateUnknown: | |
390 | device_unregister(&dev->dev); | |
391 | break; | |
392 | ||
393 | default: | |
394 | xenbus_dev_fatal(dev, -EINVAL, "saw state %d at frontend", | |
395 | frontend_state); | |
396 | break; | |
397 | } | |
398 | } | |
399 | ||
400 | ||
401 | /* ** Connection ** */ | |
402 | ||
403 | ||
404 | /** | |
405 | * Write the physical details regarding the block device to the store, and | |
406 | * switch to Connected state. | |
407 | */ | |
408 | static void connect(struct backend_info *be) | |
409 | { | |
410 | struct xenbus_transaction xbt; | |
411 | int err; | |
412 | struct xenbus_device *dev = be->dev; | |
413 | ||
414 | DPRINTK("%s", dev->otherend); | |
415 | ||
416 | /* Supply the information about the device the frontend needs */ | |
417 | again: | |
418 | err = xenbus_transaction_start(&xbt); | |
419 | if (err) { | |
420 | xenbus_dev_fatal(dev, err, "starting transaction"); | |
421 | return; | |
422 | } | |
423 | ||
424 | err = blkback_barrier(xbt, be, 1); | |
425 | if (err) | |
426 | goto abort; | |
427 | ||
428 | err = xenbus_printf(xbt, dev->nodename, "sectors", "%llu", | |
429 | vbd_size(&be->blkif->vbd)); | |
430 | if (err) { | |
431 | xenbus_dev_fatal(dev, err, "writing %s/sectors", | |
432 | dev->nodename); | |
433 | goto abort; | |
434 | } | |
435 | ||
436 | /* FIXME: use a typename instead */ | |
437 | err = xenbus_printf(xbt, dev->nodename, "info", "%u", | |
438 | vbd_info(&be->blkif->vbd)); | |
439 | if (err) { | |
440 | xenbus_dev_fatal(dev, err, "writing %s/info", | |
441 | dev->nodename); | |
442 | goto abort; | |
443 | } | |
444 | err = xenbus_printf(xbt, dev->nodename, "sector-size", "%lu", | |
445 | vbd_secsize(&be->blkif->vbd)); | |
446 | if (err) { | |
447 | xenbus_dev_fatal(dev, err, "writing %s/sector-size", | |
448 | dev->nodename); | |
449 | goto abort; | |
450 | } | |
451 | ||
452 | err = xenbus_transaction_end(xbt, 0); | |
453 | if (err == -EAGAIN) | |
454 | goto again; | |
455 | if (err) | |
456 | xenbus_dev_fatal(dev, err, "ending transaction"); | |
457 | ||
458 | err = xenbus_switch_state(dev, XenbusStateConnected); | |
459 | if (err) | |
460 | xenbus_dev_fatal(dev, err, "switching to Connected state", | |
461 | dev->nodename); | |
462 | ||
463 | return; | |
464 | abort: | |
465 | xenbus_transaction_end(xbt, 1); | |
466 | } | |
467 | ||
468 | ||
469 | static int connect_ring(struct backend_info *be) | |
470 | { | |
471 | struct xenbus_device *dev = be->dev; | |
472 | unsigned long ring_ref; | |
473 | unsigned int evtchn; | |
474 | char protocol[64] = ""; | |
475 | int err; | |
476 | ||
477 | DPRINTK("%s", dev->otherend); | |
478 | ||
479 | err = xenbus_gather(XBT_NIL, dev->otherend, "ring-ref", "%lu", &ring_ref, | |
480 | "event-channel", "%u", &evtchn, NULL); | |
481 | if (err) { | |
482 | xenbus_dev_fatal(dev, err, | |
483 | "reading %s/ring-ref and event-channel", | |
484 | dev->otherend); | |
485 | return err; | |
486 | } | |
487 | ||
488 | be->blkif->blk_protocol = BLKIF_PROTOCOL_NATIVE; | |
489 | err = xenbus_gather(XBT_NIL, dev->otherend, "protocol", | |
490 | "%63s", protocol, NULL); | |
491 | if (err) | |
492 | strcpy(protocol, "unspecified, assuming native"); | |
493 | else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_NATIVE)) | |
494 | be->blkif->blk_protocol = BLKIF_PROTOCOL_NATIVE; | |
495 | else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_X86_32)) | |
496 | be->blkif->blk_protocol = BLKIF_PROTOCOL_X86_32; | |
497 | else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_X86_64)) | |
498 | be->blkif->blk_protocol = BLKIF_PROTOCOL_X86_64; | |
499 | else { | |
500 | xenbus_dev_fatal(dev, err, "unknown fe protocol %s", protocol); | |
501 | return -1; | |
502 | } | |
503 | printk(KERN_INFO | |
504 | "blkback: ring-ref %ld, event-channel %d, protocol %d (%s)\n", | |
505 | ring_ref, evtchn, be->blkif->blk_protocol, protocol); | |
506 | ||
507 | /* Map the shared frame, irq etc. */ | |
508 | err = blkif_map(be->blkif, ring_ref, evtchn); | |
509 | if (err) { | |
510 | xenbus_dev_fatal(dev, err, "mapping ring-ref %lu port %u", | |
511 | ring_ref, evtchn); | |
512 | return err; | |
513 | } | |
514 | ||
515 | return 0; | |
516 | } | |
517 | ||
518 | ||
519 | /* ** Driver Registration ** */ | |
520 | ||
521 | ||
522 | static const struct xenbus_device_id blkback_ids[] = { | |
523 | { "vbd" }, | |
524 | { "" } | |
525 | }; | |
526 | ||
527 | ||
528 | static struct xenbus_driver blkback = { | |
529 | .name = "vbd", | |
530 | .owner = THIS_MODULE, | |
531 | .ids = blkback_ids, | |
532 | .probe = blkback_probe, | |
533 | .remove = blkback_remove, | |
534 | .otherend_changed = frontend_changed | |
535 | }; | |
536 | ||
537 | ||
538 | void blkif_xenbus_init(void) | |
539 | { | |
540 | xenbus_register_backend(&blkback); | |
541 | } |