upload tizen1.0 source
[kernel/linux-2.6.36.git] / drivers / block / osdblk.c
1
2 /*
3    osdblk.c -- Export a single SCSI OSD object as a Linux block device
4
5
6    Copyright 2009 Red Hat, Inc.
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program; see the file COPYING.  If not, write to
19    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
20
21
22    Instructions for use
23    --------------------
24
25    1) Map a Linux block device to an existing OSD object.
26
27       In this example, we will use partition id 1234, object id 5678,
28       OSD device /dev/osd1.
29
30       $ echo "1234 5678 /dev/osd1" > /sys/class/osdblk/add
31
32
33    2) List all active blkdev<->object mappings.
34
35       In this example, we have performed step #1 twice, creating two blkdevs,
36       mapped to two separate OSD objects.
37
38       $ cat /sys/class/osdblk/list
39       0 174 1234 5678 /dev/osd1
40       1 179 1994 897123 /dev/osd0
41
42       The columns, in order, are:
43       - blkdev unique id
44       - blkdev assigned major
45       - OSD object partition id
46       - OSD object id
47       - OSD device
48
49
50    3) Remove an active blkdev<->object mapping.
51
52       In this example, we remove the mapping with blkdev unique id 1.
53
54       $ echo 1 > /sys/class/osdblk/remove
55
56
57    NOTE:  The actual creation and deletion of OSD objects is outside the scope
58    of this driver.
59
60  */
61
62 #include <linux/kernel.h>
63 #include <linux/device.h>
64 #include <linux/module.h>
65 #include <linux/fs.h>
66 #include <linux/slab.h>
67 #include <scsi/osd_initiator.h>
68 #include <scsi/osd_attributes.h>
69 #include <scsi/osd_sec.h>
70 #include <scsi/scsi_device.h>
71
72 #define DRV_NAME "osdblk"
73 #define PFX DRV_NAME ": "
74
75 /* #define _OSDBLK_DEBUG */
76 #ifdef _OSDBLK_DEBUG
77 #define OSDBLK_DEBUG(fmt, a...) \
78         printk(KERN_NOTICE "osdblk @%s:%d: " fmt, __func__, __LINE__, ##a)
79 #else
80 #define OSDBLK_DEBUG(fmt, a...) \
81         do { if (0) printk(fmt, ##a); } while (0)
82 #endif
83
84 MODULE_AUTHOR("Jeff Garzik <jeff@garzik.org>");
85 MODULE_DESCRIPTION("block device inside an OSD object osdblk.ko");
86 MODULE_LICENSE("GPL");
87
88 struct osdblk_device;
89
90 enum {
91         OSDBLK_MINORS_PER_MAJOR = 256,          /* max minors per blkdev */
92         OSDBLK_MAX_REQ          = 32,           /* max parallel requests */
93         OSDBLK_OP_TIMEOUT       = 4 * 60,       /* sync OSD req timeout */
94 };
95
96 struct osdblk_request {
97         struct request          *rq;            /* blk layer request */
98         struct bio              *bio;           /* cloned bio */
99         struct osdblk_device    *osdev;         /* associated blkdev */
100 };
101
102 struct osdblk_device {
103         int                     id;             /* blkdev unique id */
104
105         int                     major;          /* blkdev assigned major */
106         struct gendisk          *disk;          /* blkdev's gendisk and rq */
107         struct request_queue    *q;
108
109         struct osd_dev          *osd;           /* associated OSD */
110
111         char                    name[32];       /* blkdev name, e.g. osdblk34 */
112
113         spinlock_t              lock;           /* queue lock */
114
115         struct osd_obj_id       obj;            /* OSD partition, obj id */
116         uint8_t                 obj_cred[OSD_CAP_LEN]; /* OSD cred */
117
118         struct osdblk_request   req[OSDBLK_MAX_REQ]; /* request table */
119
120         struct list_head        node;
121
122         char                    osd_path[0];    /* OSD device path */
123 };
124
125 static struct class *class_osdblk;              /* /sys/class/osdblk */
126 static DEFINE_MUTEX(ctl_mutex); /* Serialize open/close/setup/teardown */
127 static LIST_HEAD(osdblkdev_list);
128
129 static const struct block_device_operations osdblk_bd_ops = {
130         .owner          = THIS_MODULE,
131 };
132
133 static const struct osd_attr g_attr_logical_length = ATTR_DEF(
134         OSD_APAGE_OBJECT_INFORMATION, OSD_ATTR_OI_LOGICAL_LENGTH, 8);
135
136 static void osdblk_make_credential(u8 cred_a[OSD_CAP_LEN],
137                                    const struct osd_obj_id *obj)
138 {
139         osd_sec_init_nosec_doall_caps(cred_a, obj, false, true);
140 }
141
142 /* copied from exofs; move to libosd? */
143 /*
144  * Perform a synchronous OSD operation.  copied from exofs; move to libosd?
145  */
146 static int osd_sync_op(struct osd_request *or, int timeout, uint8_t *credential)
147 {
148         int ret;
149
150         or->timeout = timeout;
151         ret = osd_finalize_request(or, 0, credential, NULL);
152         if (ret)
153                 return ret;
154
155         ret = osd_execute_request(or);
156
157         /* osd_req_decode_sense(or, ret); */
158         return ret;
159 }
160
161 /*
162  * Perform an asynchronous OSD operation.  copied from exofs; move to libosd?
163  */
164 static int osd_async_op(struct osd_request *or, osd_req_done_fn *async_done,
165                    void *caller_context, u8 *cred)
166 {
167         int ret;
168
169         ret = osd_finalize_request(or, 0, cred, NULL);
170         if (ret)
171                 return ret;
172
173         ret = osd_execute_request_async(or, async_done, caller_context);
174
175         return ret;
176 }
177
178 /* copied from exofs; move to libosd? */
179 static int extract_attr_from_req(struct osd_request *or, struct osd_attr *attr)
180 {
181         struct osd_attr cur_attr = {.attr_page = 0}; /* start with zeros */
182         void *iter = NULL;
183         int nelem;
184
185         do {
186                 nelem = 1;
187                 osd_req_decode_get_attr_list(or, &cur_attr, &nelem, &iter);
188                 if ((cur_attr.attr_page == attr->attr_page) &&
189                     (cur_attr.attr_id == attr->attr_id)) {
190                         attr->len = cur_attr.len;
191                         attr->val_ptr = cur_attr.val_ptr;
192                         return 0;
193                 }
194         } while (iter);
195
196         return -EIO;
197 }
198
199 static int osdblk_get_obj_size(struct osdblk_device *osdev, u64 *size_out)
200 {
201         struct osd_request *or;
202         struct osd_attr attr;
203         int ret;
204
205         /* start request */
206         or = osd_start_request(osdev->osd, GFP_KERNEL);
207         if (!or)
208                 return -ENOMEM;
209
210         /* create a get-attributes(length) request */
211         osd_req_get_attributes(or, &osdev->obj);
212
213         osd_req_add_get_attr_list(or, &g_attr_logical_length, 1);
214
215         /* execute op synchronously */
216         ret = osd_sync_op(or, OSDBLK_OP_TIMEOUT, osdev->obj_cred);
217         if (ret)
218                 goto out;
219
220         /* extract length from returned attribute info */
221         attr = g_attr_logical_length;
222         ret = extract_attr_from_req(or, &attr);
223         if (ret)
224                 goto out;
225
226         *size_out = get_unaligned_be64(attr.val_ptr);
227
228 out:
229         osd_end_request(or);
230         return ret;
231
232 }
233
234 static void osdblk_osd_complete(struct osd_request *or, void *private)
235 {
236         struct osdblk_request *orq = private;
237         struct osd_sense_info osi;
238         int ret = osd_req_decode_sense(or, &osi);
239
240         if (ret) {
241                 ret = -EIO;
242                 OSDBLK_DEBUG("osdblk_osd_complete with err=%d\n", ret);
243         }
244
245         /* complete OSD request */
246         osd_end_request(or);
247
248         /* complete request passed to osdblk by block layer */
249         __blk_end_request_all(orq->rq, ret);
250 }
251
252 static void bio_chain_put(struct bio *chain)
253 {
254         struct bio *tmp;
255
256         while (chain) {
257                 tmp = chain;
258                 chain = chain->bi_next;
259
260                 bio_put(tmp);
261         }
262 }
263
264 static struct bio *bio_chain_clone(struct bio *old_chain, gfp_t gfpmask)
265 {
266         struct bio *tmp, *new_chain = NULL, *tail = NULL;
267
268         while (old_chain) {
269                 tmp = bio_kmalloc(gfpmask, old_chain->bi_max_vecs);
270                 if (!tmp)
271                         goto err_out;
272
273                 __bio_clone(tmp, old_chain);
274                 tmp->bi_bdev = NULL;
275                 gfpmask &= ~__GFP_WAIT;
276                 tmp->bi_next = NULL;
277
278                 if (!new_chain)
279                         new_chain = tail = tmp;
280                 else {
281                         tail->bi_next = tmp;
282                         tail = tmp;
283                 }
284
285                 old_chain = old_chain->bi_next;
286         }
287
288         return new_chain;
289
290 err_out:
291         OSDBLK_DEBUG("bio_chain_clone with err\n");
292         bio_chain_put(new_chain);
293         return NULL;
294 }
295
296 static void osdblk_rq_fn(struct request_queue *q)
297 {
298         struct osdblk_device *osdev = q->queuedata;
299
300         while (1) {
301                 struct request *rq;
302                 struct osdblk_request *orq;
303                 struct osd_request *or;
304                 struct bio *bio;
305                 bool do_write, do_flush;
306
307                 /* peek at request from block layer */
308                 rq = blk_fetch_request(q);
309                 if (!rq)
310                         break;
311
312                 /* filter out block requests we don't understand */
313                 if (rq->cmd_type != REQ_TYPE_FS &&
314                     !(rq->cmd_flags & REQ_HARDBARRIER)) {
315                         blk_end_request_all(rq, 0);
316                         continue;
317                 }
318
319                 /* deduce our operation (read, write, flush) */
320                 /* I wish the block layer simplified cmd_type/cmd_flags/cmd[]
321                  * into a clearly defined set of RPC commands:
322                  * read, write, flush, scsi command, power mgmt req,
323                  * driver-specific, etc.
324                  */
325
326                 do_flush = rq->cmd_flags & REQ_FLUSH;
327                 do_write = (rq_data_dir(rq) == WRITE);
328
329                 if (!do_flush) { /* osd_flush does not use a bio */
330                         /* a bio clone to be passed down to OSD request */
331                         bio = bio_chain_clone(rq->bio, GFP_ATOMIC);
332                         if (!bio)
333                                 break;
334                 } else
335                         bio = NULL;
336
337                 /* alloc internal OSD request, for OSD command execution */
338                 or = osd_start_request(osdev->osd, GFP_ATOMIC);
339                 if (!or) {
340                         bio_chain_put(bio);
341                         OSDBLK_DEBUG("osd_start_request with err\n");
342                         break;
343                 }
344
345                 orq = &osdev->req[rq->tag];
346                 orq->rq = rq;
347                 orq->bio = bio;
348                 orq->osdev = osdev;
349
350                 /* init OSD command: flush, write or read */
351                 if (do_flush)
352                         osd_req_flush_object(or, &osdev->obj,
353                                              OSD_CDB_FLUSH_ALL, 0, 0);
354                 else if (do_write)
355                         osd_req_write(or, &osdev->obj, blk_rq_pos(rq) * 512ULL,
356                                       bio, blk_rq_bytes(rq));
357                 else
358                         osd_req_read(or, &osdev->obj, blk_rq_pos(rq) * 512ULL,
359                                      bio, blk_rq_bytes(rq));
360
361                 OSDBLK_DEBUG("%s 0x%x bytes at 0x%llx\n",
362                         do_flush ? "flush" : do_write ?
363                                 "write" : "read", blk_rq_bytes(rq),
364                         blk_rq_pos(rq) * 512ULL);
365
366                 /* begin OSD command execution */
367                 if (osd_async_op(or, osdblk_osd_complete, orq,
368                                  osdev->obj_cred)) {
369                         osd_end_request(or);
370                         blk_requeue_request(q, rq);
371                         bio_chain_put(bio);
372                         OSDBLK_DEBUG("osd_execute_request_async with err\n");
373                         break;
374                 }
375
376                 /* remove the special 'flush' marker, now that the command
377                  * is executing
378                  */
379                 rq->special = NULL;
380         }
381 }
382
383 static void osdblk_free_disk(struct osdblk_device *osdev)
384 {
385         struct gendisk *disk = osdev->disk;
386
387         if (!disk)
388                 return;
389
390         if (disk->flags & GENHD_FL_UP)
391                 del_gendisk(disk);
392         if (disk->queue)
393                 blk_cleanup_queue(disk->queue);
394         put_disk(disk);
395 }
396
397 static int osdblk_init_disk(struct osdblk_device *osdev)
398 {
399         struct gendisk *disk;
400         struct request_queue *q;
401         int rc;
402         u64 obj_size = 0;
403
404         /* contact OSD, request size info about the object being mapped */
405         rc = osdblk_get_obj_size(osdev, &obj_size);
406         if (rc)
407                 return rc;
408
409         /* create gendisk info */
410         disk = alloc_disk(OSDBLK_MINORS_PER_MAJOR);
411         if (!disk)
412                 return -ENOMEM;
413
414         sprintf(disk->disk_name, DRV_NAME "%d", osdev->id);
415         disk->major = osdev->major;
416         disk->first_minor = 0;
417         disk->fops = &osdblk_bd_ops;
418         disk->private_data = osdev;
419
420         /* init rq */
421         q = blk_init_queue(osdblk_rq_fn, &osdev->lock);
422         if (!q) {
423                 put_disk(disk);
424                 return -ENOMEM;
425         }
426
427         /* switch queue to TCQ mode; allocate tag map */
428         rc = blk_queue_init_tags(q, OSDBLK_MAX_REQ, NULL);
429         if (rc) {
430                 blk_cleanup_queue(q);
431                 put_disk(disk);
432                 return rc;
433         }
434
435         /* Set our limits to the lower device limits, because osdblk cannot
436          * sleep when allocating a lower-request and therefore cannot be
437          * bouncing.
438          */
439         blk_queue_stack_limits(q, osd_request_queue(osdev->osd));
440
441         blk_queue_prep_rq(q, blk_queue_start_tag);
442         blk_queue_ordered(q, QUEUE_ORDERED_DRAIN_FLUSH);
443
444         disk->queue = q;
445
446         q->queuedata = osdev;
447
448         osdev->disk = disk;
449         osdev->q = q;
450
451         /* finally, announce the disk to the world */
452         set_capacity(disk, obj_size / 512ULL);
453         add_disk(disk);
454
455         printk(KERN_INFO "%s: Added of size 0x%llx\n",
456                 disk->disk_name, (unsigned long long)obj_size);
457
458         return 0;
459 }
460
461 /********************************************************************
462  * /sys/class/osdblk/
463  *                   add        map OSD object to blkdev
464  *                   remove     unmap OSD object
465  *                   list       show mappings
466  *******************************************************************/
467
468 static void class_osdblk_release(struct class *cls)
469 {
470         kfree(cls);
471 }
472
473 static ssize_t class_osdblk_list(struct class *c,
474                                 struct class_attribute *attr,
475                                 char *data)
476 {
477         int n = 0;
478         struct list_head *tmp;
479
480         mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
481
482         list_for_each(tmp, &osdblkdev_list) {
483                 struct osdblk_device *osdev;
484
485                 osdev = list_entry(tmp, struct osdblk_device, node);
486
487                 n += sprintf(data+n, "%d %d %llu %llu %s\n",
488                         osdev->id,
489                         osdev->major,
490                         osdev->obj.partition,
491                         osdev->obj.id,
492                         osdev->osd_path);
493         }
494
495         mutex_unlock(&ctl_mutex);
496         return n;
497 }
498
499 static ssize_t class_osdblk_add(struct class *c,
500                                 struct class_attribute *attr,
501                                 const char *buf, size_t count)
502 {
503         struct osdblk_device *osdev;
504         ssize_t rc;
505         int irc, new_id = 0;
506         struct list_head *tmp;
507
508         if (!try_module_get(THIS_MODULE))
509                 return -ENODEV;
510
511         /* new osdblk_device object */
512         osdev = kzalloc(sizeof(*osdev) + strlen(buf) + 1, GFP_KERNEL);
513         if (!osdev) {
514                 rc = -ENOMEM;
515                 goto err_out_mod;
516         }
517
518         /* static osdblk_device initialization */
519         spin_lock_init(&osdev->lock);
520         INIT_LIST_HEAD(&osdev->node);
521
522         /* generate unique id: find highest unique id, add one */
523
524         mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
525
526         list_for_each(tmp, &osdblkdev_list) {
527                 struct osdblk_device *osdev;
528
529                 osdev = list_entry(tmp, struct osdblk_device, node);
530                 if (osdev->id > new_id)
531                         new_id = osdev->id + 1;
532         }
533
534         osdev->id = new_id;
535
536         /* add to global list */
537         list_add_tail(&osdev->node, &osdblkdev_list);
538
539         mutex_unlock(&ctl_mutex);
540
541         /* parse add command */
542         if (sscanf(buf, "%llu %llu %s", &osdev->obj.partition, &osdev->obj.id,
543                    osdev->osd_path) != 3) {
544                 rc = -EINVAL;
545                 goto err_out_slot;
546         }
547
548         /* initialize rest of new object */
549         sprintf(osdev->name, DRV_NAME "%d", osdev->id);
550
551         /* contact requested OSD */
552         osdev->osd = osduld_path_lookup(osdev->osd_path);
553         if (IS_ERR(osdev->osd)) {
554                 rc = PTR_ERR(osdev->osd);
555                 goto err_out_slot;
556         }
557
558         /* build OSD credential */
559         osdblk_make_credential(osdev->obj_cred, &osdev->obj);
560
561         /* register our block device */
562         irc = register_blkdev(0, osdev->name);
563         if (irc < 0) {
564                 rc = irc;
565                 goto err_out_osd;
566         }
567
568         osdev->major = irc;
569
570         /* set up and announce blkdev mapping */
571         rc = osdblk_init_disk(osdev);
572         if (rc)
573                 goto err_out_blkdev;
574
575         return count;
576
577 err_out_blkdev:
578         unregister_blkdev(osdev->major, osdev->name);
579 err_out_osd:
580         osduld_put_device(osdev->osd);
581 err_out_slot:
582         mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
583         list_del_init(&osdev->node);
584         mutex_unlock(&ctl_mutex);
585
586         kfree(osdev);
587 err_out_mod:
588         OSDBLK_DEBUG("Error adding device %s\n", buf);
589         module_put(THIS_MODULE);
590         return rc;
591 }
592
593 static ssize_t class_osdblk_remove(struct class *c,
594                                         struct class_attribute *attr,
595                                         const char *buf,
596                                         size_t count)
597 {
598         struct osdblk_device *osdev = NULL;
599         int target_id, rc;
600         unsigned long ul;
601         struct list_head *tmp;
602
603         rc = strict_strtoul(buf, 10, &ul);
604         if (rc)
605                 return rc;
606
607         /* convert to int; abort if we lost anything in the conversion */
608         target_id = (int) ul;
609         if (target_id != ul)
610                 return -EINVAL;
611
612         /* remove object from list immediately */
613         mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
614
615         list_for_each(tmp, &osdblkdev_list) {
616                 osdev = list_entry(tmp, struct osdblk_device, node);
617                 if (osdev->id == target_id) {
618                         list_del_init(&osdev->node);
619                         break;
620                 }
621                 osdev = NULL;
622         }
623
624         mutex_unlock(&ctl_mutex);
625
626         if (!osdev)
627                 return -ENOENT;
628
629         /* clean up and free blkdev and associated OSD connection */
630         osdblk_free_disk(osdev);
631         unregister_blkdev(osdev->major, osdev->name);
632         osduld_put_device(osdev->osd);
633         kfree(osdev);
634
635         /* release module ref */
636         module_put(THIS_MODULE);
637
638         return count;
639 }
640
641 static struct class_attribute class_osdblk_attrs[] = {
642         __ATTR(add,     0200, NULL, class_osdblk_add),
643         __ATTR(remove,  0200, NULL, class_osdblk_remove),
644         __ATTR(list,    0444, class_osdblk_list, NULL),
645         __ATTR_NULL
646 };
647
648 static int osdblk_sysfs_init(void)
649 {
650         int ret = 0;
651
652         /*
653          * create control files in sysfs
654          * /sys/class/osdblk/...
655          */
656         class_osdblk = kzalloc(sizeof(*class_osdblk), GFP_KERNEL);
657         if (!class_osdblk)
658                 return -ENOMEM;
659
660         class_osdblk->name = DRV_NAME;
661         class_osdblk->owner = THIS_MODULE;
662         class_osdblk->class_release = class_osdblk_release;
663         class_osdblk->class_attrs = class_osdblk_attrs;
664
665         ret = class_register(class_osdblk);
666         if (ret) {
667                 kfree(class_osdblk);
668                 class_osdblk = NULL;
669                 printk(PFX "failed to create class osdblk\n");
670                 return ret;
671         }
672
673         return 0;
674 }
675
676 static void osdblk_sysfs_cleanup(void)
677 {
678         if (class_osdblk)
679                 class_destroy(class_osdblk);
680         class_osdblk = NULL;
681 }
682
683 static int __init osdblk_init(void)
684 {
685         int rc;
686
687         rc = osdblk_sysfs_init();
688         if (rc)
689                 return rc;
690
691         return 0;
692 }
693
694 static void __exit osdblk_exit(void)
695 {
696         osdblk_sysfs_cleanup();
697 }
698
699 module_init(osdblk_init);
700 module_exit(osdblk_exit);
701