fc6fc1c4d4d1659629a7876a9df593cad33c8a4b
[safe/jmp/linux-2.6] / drivers / scsi / osd / osd_uld.c
1 /*
2  * osd_uld.c - OSD Upper Layer Driver
3  *
4  * A Linux driver module that registers as a SCSI ULD and probes
5  * for OSD type SCSI devices.
6  * It's main function is to export osd devices to in-kernel users like
7  * osdfs and pNFS-objects-LD. It also provides one ioctl for running
8  * in Kernel tests.
9  *
10  * Copyright (C) 2008 Panasas Inc.  All rights reserved.
11  *
12  * Authors:
13  *   Boaz Harrosh <bharrosh@panasas.com>
14  *   Benny Halevy <bhalevy@panasas.com>
15  *
16  * This program is free software; you can redistribute it and/or modify
17  * it under the terms of the GNU General Public License version 2
18  *
19  * Redistribution and use in source and binary forms, with or without
20  * modification, are permitted provided that the following conditions
21  * are met:
22  *
23  *  1. Redistributions of source code must retain the above copyright
24  *     notice, this list of conditions and the following disclaimer.
25  *  2. Redistributions in binary form must reproduce the above copyright
26  *     notice, this list of conditions and the following disclaimer in the
27  *     documentation and/or other materials provided with the distribution.
28  *  3. Neither the name of the Panasas company nor the names of its
29  *     contributors may be used to endorse or promote products derived
30  *     from this software without specific prior written permission.
31  *
32  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
33  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
34  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
35  * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
36  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
37  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
38  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
39  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
40  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
41  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
42  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43  */
44
45 #include <linux/namei.h>
46 #include <linux/cdev.h>
47 #include <linux/fs.h>
48 #include <linux/module.h>
49 #include <linux/device.h>
50 #include <linux/idr.h>
51 #include <linux/major.h>
52 #include <linux/file.h>
53
54 #include <scsi/scsi.h>
55 #include <scsi/scsi_driver.h>
56 #include <scsi/scsi_device.h>
57 #include <scsi/scsi_ioctl.h>
58
59 #include <scsi/osd_initiator.h>
60 #include <scsi/osd_sec.h>
61
62 #include "osd_debug.h"
63
64 #ifndef TYPE_OSD
65 #  define TYPE_OSD 0x11
66 #endif
67
68 #ifndef SCSI_OSD_MAJOR
69 #  define SCSI_OSD_MAJOR 260
70 #endif
71 #define SCSI_OSD_MAX_MINOR 64
72
73 static const char osd_name[] = "osd";
74 static const char *osd_version_string = "open-osd 0.2.0";
75
76 MODULE_AUTHOR("Boaz Harrosh <bharrosh@panasas.com>");
77 MODULE_DESCRIPTION("open-osd Upper-Layer-Driver osd.ko");
78 MODULE_LICENSE("GPL");
79 MODULE_ALIAS_CHARDEV_MAJOR(SCSI_OSD_MAJOR);
80 MODULE_ALIAS_SCSI_DEVICE(TYPE_OSD);
81
82 struct osd_uld_device {
83         int minor;
84         struct device class_dev;
85         struct cdev cdev;
86         struct osd_dev od;
87         struct gendisk *disk;
88 };
89
90 struct osd_dev_handle {
91         struct osd_dev od;
92         struct file *file;
93         struct osd_uld_device *oud;
94 } ;
95
96 static DEFINE_IDA(osd_minor_ida);
97
98 static struct class osd_uld_class = {
99         .owner          = THIS_MODULE,
100         .name           = "scsi_osd",
101 };
102
103 /*
104  * Char Device operations
105  */
106
107 static int osd_uld_open(struct inode *inode, struct file *file)
108 {
109         struct osd_uld_device *oud = container_of(inode->i_cdev,
110                                         struct osd_uld_device, cdev);
111
112         get_device(&oud->class_dev);
113         /* cache osd_uld_device on file handle */
114         file->private_data = oud;
115         OSD_DEBUG("osd_uld_open %p\n", oud);
116         return 0;
117 }
118
119 static int osd_uld_release(struct inode *inode, struct file *file)
120 {
121         struct osd_uld_device *oud = file->private_data;
122
123         OSD_DEBUG("osd_uld_release %p\n", file->private_data);
124         file->private_data = NULL;
125         put_device(&oud->class_dev);
126         return 0;
127 }
128
129 /* FIXME: Only one vector for now */
130 unsigned g_test_ioctl;
131 do_test_fn *g_do_test;
132
133 int osduld_register_test(unsigned ioctl, do_test_fn *do_test)
134 {
135         if (g_test_ioctl)
136                 return -EINVAL;
137
138         g_test_ioctl = ioctl;
139         g_do_test = do_test;
140         return 0;
141 }
142 EXPORT_SYMBOL(osduld_register_test);
143
144 void osduld_unregister_test(unsigned ioctl)
145 {
146         if (ioctl == g_test_ioctl) {
147                 g_test_ioctl = 0;
148                 g_do_test = NULL;
149         }
150 }
151 EXPORT_SYMBOL(osduld_unregister_test);
152
153 static do_test_fn *_find_ioctl(unsigned cmd)
154 {
155         if (g_test_ioctl == cmd)
156                 return g_do_test;
157         else
158                 return NULL;
159 }
160
161 static long osd_uld_ioctl(struct file *file, unsigned int cmd,
162         unsigned long arg)
163 {
164         struct osd_uld_device *oud = file->private_data;
165         int ret;
166         do_test_fn *do_test;
167
168         do_test = _find_ioctl(cmd);
169         if (do_test)
170                 ret = do_test(&oud->od, cmd, arg);
171         else {
172                 OSD_ERR("Unknown ioctl %d: osd_uld_device=%p\n", cmd, oud);
173                 ret = -ENOIOCTLCMD;
174         }
175         return ret;
176 }
177
178 static const struct file_operations osd_fops = {
179         .owner          = THIS_MODULE,
180         .open           = osd_uld_open,
181         .release        = osd_uld_release,
182         .unlocked_ioctl = osd_uld_ioctl,
183 };
184
185 struct osd_dev *osduld_path_lookup(const char *name)
186 {
187         struct osd_uld_device *oud;
188         struct osd_dev_handle *odh;
189         struct file *file;
190         int error;
191
192         if (!name || !*name) {
193                 OSD_ERR("Mount with !path || !*path\n");
194                 return ERR_PTR(-EINVAL);
195         }
196
197         odh = kzalloc(sizeof(*odh), GFP_KERNEL);
198         if (unlikely(!odh))
199                 return ERR_PTR(-ENOMEM);
200
201         file = filp_open(name, O_RDWR, 0);
202         if (IS_ERR(file)) {
203                 error = PTR_ERR(file);
204                 goto free_od;
205         }
206
207         if (file->f_op != &osd_fops){
208                 error = -EINVAL;
209                 goto close_file;
210         }
211
212         oud = file->private_data;
213
214         odh->od = oud->od;
215         odh->file = file;
216         odh->oud = oud;
217
218         return &odh->od;
219
220 close_file:
221         fput(file);
222 free_od:
223         kfree(odh);
224         return ERR_PTR(error);
225 }
226 EXPORT_SYMBOL(osduld_path_lookup);
227
228 void osduld_put_device(struct osd_dev *od)
229 {
230         if (od && !IS_ERR(od)) {
231                 struct osd_dev_handle *odh =
232                                 container_of(od, struct osd_dev_handle, od);
233                 struct osd_uld_device *oud = odh->oud;
234
235                 BUG_ON(od->scsi_device != oud->od.scsi_device);
236
237                 /* If scsi has released the device (logout), and exofs has last
238                  * reference on oud it will be freed by above osd_uld_release
239                  * within fput below. But this will oops in cdev_release which
240                  * is called after the fops->release. A get_/put_ pair makes
241                  * sure we have a cdev for the duration of fput
242                  */
243                 get_device(&oud->class_dev);
244                 fput(odh->file);
245                 put_device(&oud->class_dev);
246                 kfree(odh);
247         }
248 }
249 EXPORT_SYMBOL(osduld_put_device);
250
251 /*
252  * Scsi Device operations
253  */
254
255 static int __detect_osd(struct osd_uld_device *oud)
256 {
257         struct scsi_device *scsi_device = oud->od.scsi_device;
258         char caps[OSD_CAP_LEN];
259         int error;
260
261         /* sending a test_unit_ready as first command seems to be needed
262          * by some targets
263          */
264         OSD_DEBUG("start scsi_test_unit_ready %p %p %p\n",
265                         oud, scsi_device, scsi_device->request_queue);
266         error = scsi_test_unit_ready(scsi_device, 10*HZ, 5, NULL);
267         if (error)
268                 OSD_ERR("warning: scsi_test_unit_ready failed\n");
269
270         osd_sec_init_nosec_doall_caps(caps, &osd_root_object, false, true);
271         if (osd_auto_detect_ver(&oud->od, caps))
272                 return -ENODEV;
273
274         return 0;
275 }
276
277 static void __remove(struct device *dev)
278 {
279         struct osd_uld_device *oud = container_of(dev, struct osd_uld_device,
280                                                   class_dev);
281         struct scsi_device *scsi_device = oud->od.scsi_device;
282
283         if (oud->cdev.owner)
284                 cdev_del(&oud->cdev);
285
286         osd_dev_fini(&oud->od);
287         scsi_device_put(scsi_device);
288
289         OSD_INFO("osd_remove %s\n",
290                  oud->disk ? oud->disk->disk_name : NULL);
291
292         if (oud->disk)
293                 put_disk(oud->disk);
294         ida_remove(&osd_minor_ida, oud->minor);
295
296         kfree(oud);
297 }
298
299 static int osd_probe(struct device *dev)
300 {
301         struct scsi_device *scsi_device = to_scsi_device(dev);
302         struct gendisk *disk;
303         struct osd_uld_device *oud;
304         int minor;
305         int error;
306
307         if (scsi_device->type != TYPE_OSD)
308                 return -ENODEV;
309
310         do {
311                 if (!ida_pre_get(&osd_minor_ida, GFP_KERNEL))
312                         return -ENODEV;
313
314                 error = ida_get_new(&osd_minor_ida, &minor);
315         } while (error == -EAGAIN);
316
317         if (error)
318                 return error;
319         if (minor >= SCSI_OSD_MAX_MINOR) {
320                 error = -EBUSY;
321                 goto err_retract_minor;
322         }
323
324         error = -ENOMEM;
325         oud = kzalloc(sizeof(*oud), GFP_KERNEL);
326         if (NULL == oud)
327                 goto err_retract_minor;
328
329         dev_set_drvdata(dev, oud);
330         oud->minor = minor;
331
332         /* allocate a disk and set it up */
333         /* FIXME: do we need this since sg has already done that */
334         disk = alloc_disk(1);
335         if (!disk) {
336                 OSD_ERR("alloc_disk failed\n");
337                 goto err_free_osd;
338         }
339         disk->major = SCSI_OSD_MAJOR;
340         disk->first_minor = oud->minor;
341         sprintf(disk->disk_name, "osd%d", oud->minor);
342         oud->disk = disk;
343
344         /* hold one more reference to the scsi_device that will get released
345          * in __release, in case a logout is happening while fs is mounted
346          */
347         scsi_device_get(scsi_device);
348         osd_dev_init(&oud->od, scsi_device);
349
350         /* Detect the OSD Version */
351         error = __detect_osd(oud);
352         if (error) {
353                 OSD_ERR("osd detection failed, non-compatible OSD device\n");
354                 goto err_put_disk;
355         }
356
357         /* init the char-device for communication with user-mode */
358         cdev_init(&oud->cdev, &osd_fops);
359         oud->cdev.owner = THIS_MODULE;
360         error = cdev_add(&oud->cdev,
361                          MKDEV(SCSI_OSD_MAJOR, oud->minor), 1);
362         if (error) {
363                 OSD_ERR("cdev_add failed\n");
364                 goto err_put_disk;
365         }
366
367         /* class device member */
368         oud->class_dev.devt = oud->cdev.dev;
369         oud->class_dev.class = &osd_uld_class;
370         oud->class_dev.parent = dev;
371         oud->class_dev.release = __remove;
372         error = dev_set_name(&oud->class_dev, disk->disk_name);
373         if (error) {
374                 OSD_ERR("dev_set_name failed => %d\n", error);
375                 goto err_put_cdev;
376         }
377
378         error = device_register(&oud->class_dev);
379         if (error) {
380                 OSD_ERR("device_register failed => %d\n", error);
381                 goto err_put_cdev;
382         }
383
384         get_device(&oud->class_dev);
385
386         OSD_INFO("osd_probe %s\n", disk->disk_name);
387         return 0;
388
389 err_put_cdev:
390         cdev_del(&oud->cdev);
391 err_put_disk:
392         scsi_device_put(scsi_device);
393         put_disk(disk);
394 err_free_osd:
395         dev_set_drvdata(dev, NULL);
396         kfree(oud);
397 err_retract_minor:
398         ida_remove(&osd_minor_ida, minor);
399         return error;
400 }
401
402 static int osd_remove(struct device *dev)
403 {
404         struct scsi_device *scsi_device = to_scsi_device(dev);
405         struct osd_uld_device *oud = dev_get_drvdata(dev);
406
407         if (!oud || (oud->od.scsi_device != scsi_device)) {
408                 OSD_ERR("Half cooked osd-device %p,%p || %p!=%p",
409                         dev, oud, oud ? oud->od.scsi_device : NULL,
410                         scsi_device);
411         }
412
413         device_unregister(&oud->class_dev);
414
415         put_device(&oud->class_dev);
416         return 0;
417 }
418
419 /*
420  * Global driver and scsi registration
421  */
422
423 static struct scsi_driver osd_driver = {
424         .owner                  = THIS_MODULE,
425         .gendrv = {
426                 .name           = osd_name,
427                 .probe          = osd_probe,
428                 .remove         = osd_remove,
429         }
430 };
431
432 static int __init osd_uld_init(void)
433 {
434         int err;
435
436         err = class_register(&osd_uld_class);
437         if (err) {
438                 OSD_ERR("Unable to register sysfs class => %d\n", err);
439                 return err;
440         }
441
442         err = register_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0),
443                                      SCSI_OSD_MAX_MINOR, osd_name);
444         if (err) {
445                 OSD_ERR("Unable to register major %d for osd ULD => %d\n",
446                         SCSI_OSD_MAJOR, err);
447                 goto err_out;
448         }
449
450         err = scsi_register_driver(&osd_driver.gendrv);
451         if (err) {
452                 OSD_ERR("scsi_register_driver failed => %d\n", err);
453                 goto err_out_chrdev;
454         }
455
456         OSD_INFO("LOADED %s\n", osd_version_string);
457         return 0;
458
459 err_out_chrdev:
460         unregister_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), SCSI_OSD_MAX_MINOR);
461 err_out:
462         class_unregister(&osd_uld_class);
463         return err;
464 }
465
466 static void __exit osd_uld_exit(void)
467 {
468         scsi_unregister_driver(&osd_driver.gendrv);
469         unregister_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), SCSI_OSD_MAX_MINOR);
470         class_unregister(&osd_uld_class);
471         OSD_INFO("UNLOADED %s\n", osd_version_string);
472 }
473
474 module_init(osd_uld_init);
475 module_exit(osd_uld_exit);