ia64/linux-2.6.18-xen.hg

view drivers/xen/scsiback/xenbus.c @ 562:66faefe721eb

pvSCSI backend driver

Signed-off-by: Tomonari Horikoshi <t.horikoshi@jp.fujitsu.com>
Signed-off-by: Jun Kamada <kama@jp.fujitsu.com>
author Keir Fraser <keir.fraser@citrix.com>
date Mon Jun 02 09:58:27 2008 +0100 (2008-06-02)
parents
children 920abc7b20ac
line source
1 /*
2 * Xen SCSI backend driver
3 *
4 * Copyright (c) 2008, FUJITSU Limited
5 *
6 * Based on the blkback driver code.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License version 2
10 * as published by the Free Software Foundation; or, when distributed
11 * separately from the Linux kernel or incorporated into other
12 * software packages, subject to the following license:
13 *
14 * Permission is hereby granted, free of charge, to any person obtaining a copy
15 * of this source file (the "Software"), to deal in the Software without
16 * restriction, including without limitation the rights to use, copy, modify,
17 * merge, publish, distribute, sublicense, and/or sell copies of the Software,
18 * and to permit persons to whom the Software is furnished to do so, subject to
19 * the following conditions:
20 *
21 * The above copyright notice and this permission notice shall be included in
22 * all copies or substantial portions of the Software.
23 *
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
30 * IN THE SOFTWARE.
31 */
33 #include <stdarg.h>
34 #include <linux/module.h>
35 #include <linux/kthread.h>
36 #include <scsi/scsi.h>
37 #include <scsi/scsi_host.h>
38 #include <scsi/scsi_device.h>
40 #include "common.h"
42 struct backend_info
43 {
44 struct xenbus_device *dev;
45 struct vscsibk_info *info;
46 };
49 static int __vscsiif_name(struct backend_info *be, char *buf)
50 {
51 struct xenbus_device *dev = be->dev;
52 unsigned int domid, id;
54 sscanf(dev->nodename, "backend/vscsi/%u/%u", &domid, &id);
55 snprintf(buf, TASK_COMM_LEN, "vscsi.%u.%u", be->info->domid, id);
57 return 0;
58 }
60 static int scsiback_map(struct backend_info *be)
61 {
62 struct xenbus_device *dev = be->dev;
63 unsigned long ring_ref;
64 unsigned int evtchn;
65 int err;
66 char name[TASK_COMM_LEN];
68 err = xenbus_gather(XBT_NIL, dev->otherend,
69 "ring-ref", "%lu", &ring_ref,
70 "event-channel", "%u", &evtchn, NULL);
71 if (err) {
72 xenbus_dev_fatal(dev, err, "reading %s ring", dev->otherend);
73 return err;
74 }
76 err = scsiback_init_sring(be->info, ring_ref, evtchn);
77 if (err)
78 return err;
80 err = __vscsiif_name(be, name);
81 if (err) {
82 xenbus_dev_error(dev, err, "get scsiback dev name");
83 return err;
84 }
86 be->info->kthread = kthread_run(scsiback_schedule, be->info, name);
87 if (IS_ERR(be->info->kthread)) {
88 err = PTR_ERR(be->info->kthread);
89 be->info->kthread = NULL;
90 xenbus_dev_error(be->dev, err, "start vscsiif");
91 return err;
92 }
94 return 0;
95 }
98 struct scsi_device *scsiback_get_scsi_device(struct ids_tuple *phy)
99 {
100 struct Scsi_Host *shost;
101 struct scsi_device *sdev = NULL;
103 shost = scsi_host_lookup(phy->hst);
104 if (IS_ERR(shost)) {
105 printk(KERN_ERR "scsiback: host%d doesn't exist.\n",
106 phy->hst);
107 goto invald_value;
108 }
109 sdev = scsi_device_lookup(shost, phy->chn, phy->tgt, phy->lun);
110 if (!sdev) {
111 printk(KERN_ERR "scsiback: %d:%d:%d:%d doesn't exist.\n",
112 phy->hst, phy->chn, phy->tgt, phy->lun);
113 goto invald_value;
114 }
116 invald_value:
117 return (sdev);
118 }
120 #define VSCSIBACK_OP_ADD_OR_DEL_LUN 1
122 static void scsiback_do_lun_hotplug(struct backend_info *be, int op)
123 {
124 int i, err = 0;
125 struct ids_tuple phy, vir;
126 int device_state;
127 char str[64], state_str[64];
128 char **dir;
129 unsigned int dir_n = 0;
130 struct xenbus_device *dev = be->dev;
131 struct scsi_device *sdev;
132 struct xenbus_transaction xbt;
135 err = xenbus_transaction_start(&xbt);
136 if (err) {
137 xenbus_dev_fatal(dev, err, "starting transaction");
138 }
140 dir = xenbus_directory(xbt, dev->nodename, "vscsi-devs", &dir_n);
141 if (IS_ERR(dir))
142 return;
144 for (i = 0; i < dir_n; i++) {
146 /* read status */
147 snprintf(state_str, sizeof(state_str), "vscsi-devs/%s/state", dir[i]);
148 err = xenbus_scanf(xbt, dev->nodename, state_str, "%u",
149 &device_state);
150 if (XENBUS_EXIST_ERR(err))
151 continue;
153 /* physical SCSI device */
154 snprintf(str, sizeof(str), "vscsi-devs/%s/p-dev", dir[i]);
155 err = xenbus_scanf(xbt, dev->nodename, str,
156 "%u:%u:%u:%u", &phy.hst, &phy.chn, &phy.tgt, &phy.lun);
157 if (XENBUS_EXIST_ERR(err)) {
158 xenbus_printf(xbt, dev->nodename, state_str,
159 "%d", XenbusStateClosing);
160 continue;
161 }
163 /* virtual SCSI device */
164 snprintf(str, sizeof(str), "vscsi-devs/%s/v-dev", dir[i]);
165 err = xenbus_scanf(xbt, dev->nodename, str,
166 "%u:%u:%u:%u", &vir.hst, &vir.chn, &vir.tgt, &vir.lun);
167 if (XENBUS_EXIST_ERR(err)) {
168 xenbus_printf(xbt, dev->nodename, state_str,
169 "%d", XenbusStateClosing);
170 continue;
171 }
173 switch (op) {
174 case VSCSIBACK_OP_ADD_OR_DEL_LUN:
175 if (device_state == XenbusStateInitialising) {
176 sdev = scsiback_get_scsi_device(&phy);
177 if (!sdev) {
178 xenbus_printf(xbt, dev->nodename, state_str,
179 "%d", XenbusStateClosing);
180 } else {
181 err = scsiback_add_translation_entry(be->info, sdev, &vir);
182 if (!err) {
183 xenbus_printf(xbt, dev->nodename, state_str,
184 "%d", XenbusStateInitialised);
185 } else {
186 xenbus_printf(xbt, dev->nodename, state_str,
187 "%d", XenbusStateClosing);
188 }
189 }
190 }
192 if (device_state == XenbusStateClosing) {
193 err = scsiback_del_translation_entry(be->info, &vir);
194 if (err)
195 goto fail;
196 else {
197 xenbus_printf(xbt, dev->nodename, state_str,
198 "%d", XenbusStateClosed);
199 }
200 }
202 break;
203 /*When it is necessary, processing is added here.*/
204 default:
205 break;
206 }
207 }
209 xenbus_transaction_end(xbt, 0);
210 kfree(dir);
211 return ;
212 fail :
213 xenbus_transaction_end(xbt, 1);
214 kfree(dir);
215 xenbus_dev_fatal(dev, err, "read or write %s ", str);
216 return;
217 }
220 static void scsiback_frontend_changed(struct xenbus_device *dev,
221 enum xenbus_state frontend_state)
222 {
223 struct backend_info *be = dev->dev.driver_data;
224 int err;
226 switch (frontend_state) {
227 case XenbusStateInitialising:
228 break;
229 case XenbusStateInitialised:
230 err = scsiback_map(be);
231 if (err)
232 break;
234 scsiback_do_lun_hotplug(be, VSCSIBACK_OP_ADD_OR_DEL_LUN);
235 err = xenbus_switch_state(dev, XenbusStateConnected);
236 if (err)
237 xenbus_dev_fatal(dev, err, "switching to Connected state",
238 dev->nodename);
239 break;
240 case XenbusStateConnected:
241 if (dev->state == XenbusStateConnected)
242 break;
244 err = xenbus_switch_state(dev, XenbusStateConnected);
245 if (err)
246 xenbus_dev_fatal(dev, err, "switching to Connected state",
247 dev->nodename);
248 break;
250 case XenbusStateClosing:
251 scsiback_disconnect(be->info);
252 xenbus_switch_state(dev, XenbusStateClosing);
253 break;
255 case XenbusStateClosed:
256 xenbus_switch_state(dev, XenbusStateClosed);
257 if (xenbus_dev_is_online(dev))
258 break;
260 case XenbusStateReconfiguring:
261 scsiback_do_lun_hotplug(be, VSCSIBACK_OP_ADD_OR_DEL_LUN);
262 err = xenbus_switch_state(dev, XenbusStateReconfigured);
263 if (err)
264 xenbus_dev_fatal(dev, err, "switching to Reconfigured state",
265 dev->nodename);
266 break;
268 case XenbusStateUnknown:
269 device_unregister(&dev->dev);
270 break;
271 default:
272 xenbus_dev_fatal(dev, -EINVAL, "saw state %d at frontend",
273 frontend_state);
274 break;
275 }
276 }
279 static int scsiback_remove(struct xenbus_device *dev)
280 {
281 struct backend_info *be = dev->dev.driver_data;
283 if (be->info) {
284 scsiback_disconnect(be->info);
285 scsiback_release_translation_entry(be->info);
286 scsiback_free(be->info);
287 be->info = NULL;
288 }
290 kfree(be);
291 dev->dev.driver_data = NULL;
293 return 0;
294 }
297 static int scsiback_probe(struct xenbus_device *dev,
298 const struct xenbus_device_id *id)
299 {
300 int err;
302 struct backend_info *be = kzalloc(sizeof(struct backend_info),
303 GFP_KERNEL);
305 DPRINTK("%p %d\n", dev, dev->otherend_id);
307 if (!be) {
308 xenbus_dev_fatal(dev, -ENOMEM,
309 "allocating backend structure");
310 return -ENOMEM;
311 }
312 be->dev = dev;
313 dev->dev.driver_data = be;
315 be->info = vscsibk_info_alloc(dev->otherend_id);
316 if (IS_ERR(be->info)) {
317 err = PTR_ERR(be->info);
318 be->info = NULL;
319 xenbus_dev_fatal(dev, err, "creating scsihost interface");
320 goto fail;
321 }
323 be->info->dev = dev;
324 be->info->irq = 0;
326 scsiback_init_translation_table(be->info);
328 err = xenbus_switch_state(dev, XenbusStateInitWait);
329 if (err)
330 goto fail;
332 return 0;
335 fail:
336 printk(KERN_WARNING "scsiback: %s failed\n",__FUNCTION__);
337 scsiback_remove(dev);
339 return err;
340 }
343 static struct xenbus_device_id scsiback_ids[] = {
344 { "vscsi" },
345 { "" }
346 };
348 static struct xenbus_driver scsiback = {
349 .name = "vscsi",
350 .owner = THIS_MODULE,
351 .ids = scsiback_ids,
352 .probe = scsiback_probe,
353 .remove = scsiback_remove,
354 .otherend_changed = scsiback_frontend_changed
355 };
357 int scsiback_xenbus_init(void)
358 {
359 return xenbus_register_backend(&scsiback);
360 }
362 void scsiback_xenbus_unregister(void)
363 {
364 xenbus_unregister_driver(&scsiback);
365 }