]> bbs.cooldavid.org Git - net-next-2.6.git/blob - drivers/block/osdblk.c
block: remove wrappers for request type/flags
[net-next-2.6.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->special == (void *) 0xdeadbeefUL);
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_prepare_flush(struct request_queue *q, struct request *rq)
384 {
385         /* add driver-specific marker, to indicate that this request
386          * is a flush command
387          */
388         rq->special = (void *) 0xdeadbeefUL;
389 }
390
391 static void osdblk_free_disk(struct osdblk_device *osdev)
392 {
393         struct gendisk *disk = osdev->disk;
394
395         if (!disk)
396                 return;
397
398         if (disk->flags & GENHD_FL_UP)
399                 del_gendisk(disk);
400         if (disk->queue)
401                 blk_cleanup_queue(disk->queue);
402         put_disk(disk);
403 }
404
405 static int osdblk_init_disk(struct osdblk_device *osdev)
406 {
407         struct gendisk *disk;
408         struct request_queue *q;
409         int rc;
410         u64 obj_size = 0;
411
412         /* contact OSD, request size info about the object being mapped */
413         rc = osdblk_get_obj_size(osdev, &obj_size);
414         if (rc)
415                 return rc;
416
417         /* create gendisk info */
418         disk = alloc_disk(OSDBLK_MINORS_PER_MAJOR);
419         if (!disk)
420                 return -ENOMEM;
421
422         sprintf(disk->disk_name, DRV_NAME "%d", osdev->id);
423         disk->major = osdev->major;
424         disk->first_minor = 0;
425         disk->fops = &osdblk_bd_ops;
426         disk->private_data = osdev;
427
428         /* init rq */
429         q = blk_init_queue(osdblk_rq_fn, &osdev->lock);
430         if (!q) {
431                 put_disk(disk);
432                 return -ENOMEM;
433         }
434
435         /* switch queue to TCQ mode; allocate tag map */
436         rc = blk_queue_init_tags(q, OSDBLK_MAX_REQ, NULL);
437         if (rc) {
438                 blk_cleanup_queue(q);
439                 put_disk(disk);
440                 return rc;
441         }
442
443         /* Set our limits to the lower device limits, because osdblk cannot
444          * sleep when allocating a lower-request and therefore cannot be
445          * bouncing.
446          */
447         blk_queue_stack_limits(q, osd_request_queue(osdev->osd));
448
449         blk_queue_prep_rq(q, blk_queue_start_tag);
450         blk_queue_ordered(q, QUEUE_ORDERED_DRAIN_FLUSH, osdblk_prepare_flush);
451
452         disk->queue = q;
453
454         q->queuedata = osdev;
455
456         osdev->disk = disk;
457         osdev->q = q;
458
459         /* finally, announce the disk to the world */
460         set_capacity(disk, obj_size / 512ULL);
461         add_disk(disk);
462
463         printk(KERN_INFO "%s: Added of size 0x%llx\n",
464                 disk->disk_name, (unsigned long long)obj_size);
465
466         return 0;
467 }
468
469 /********************************************************************
470  * /sys/class/osdblk/
471  *                   add        map OSD object to blkdev
472  *                   remove     unmap OSD object
473  *                   list       show mappings
474  *******************************************************************/
475
476 static void class_osdblk_release(struct class *cls)
477 {
478         kfree(cls);
479 }
480
481 static ssize_t class_osdblk_list(struct class *c,
482                                 struct class_attribute *attr,
483                                 char *data)
484 {
485         int n = 0;
486         struct list_head *tmp;
487
488         mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
489
490         list_for_each(tmp, &osdblkdev_list) {
491                 struct osdblk_device *osdev;
492
493                 osdev = list_entry(tmp, struct osdblk_device, node);
494
495                 n += sprintf(data+n, "%d %d %llu %llu %s\n",
496                         osdev->id,
497                         osdev->major,
498                         osdev->obj.partition,
499                         osdev->obj.id,
500                         osdev->osd_path);
501         }
502
503         mutex_unlock(&ctl_mutex);
504         return n;
505 }
506
507 static ssize_t class_osdblk_add(struct class *c,
508                                 struct class_attribute *attr,
509                                 const char *buf, size_t count)
510 {
511         struct osdblk_device *osdev;
512         ssize_t rc;
513         int irc, new_id = 0;
514         struct list_head *tmp;
515
516         if (!try_module_get(THIS_MODULE))
517                 return -ENODEV;
518
519         /* new osdblk_device object */
520         osdev = kzalloc(sizeof(*osdev) + strlen(buf) + 1, GFP_KERNEL);
521         if (!osdev) {
522                 rc = -ENOMEM;
523                 goto err_out_mod;
524         }
525
526         /* static osdblk_device initialization */
527         spin_lock_init(&osdev->lock);
528         INIT_LIST_HEAD(&osdev->node);
529
530         /* generate unique id: find highest unique id, add one */
531
532         mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
533
534         list_for_each(tmp, &osdblkdev_list) {
535                 struct osdblk_device *osdev;
536
537                 osdev = list_entry(tmp, struct osdblk_device, node);
538                 if (osdev->id > new_id)
539                         new_id = osdev->id + 1;
540         }
541
542         osdev->id = new_id;
543
544         /* add to global list */
545         list_add_tail(&osdev->node, &osdblkdev_list);
546
547         mutex_unlock(&ctl_mutex);
548
549         /* parse add command */
550         if (sscanf(buf, "%llu %llu %s", &osdev->obj.partition, &osdev->obj.id,
551                    osdev->osd_path) != 3) {
552                 rc = -EINVAL;
553                 goto err_out_slot;
554         }
555
556         /* initialize rest of new object */
557         sprintf(osdev->name, DRV_NAME "%d", osdev->id);
558
559         /* contact requested OSD */
560         osdev->osd = osduld_path_lookup(osdev->osd_path);
561         if (IS_ERR(osdev->osd)) {
562                 rc = PTR_ERR(osdev->osd);
563                 goto err_out_slot;
564         }
565
566         /* build OSD credential */
567         osdblk_make_credential(osdev->obj_cred, &osdev->obj);
568
569         /* register our block device */
570         irc = register_blkdev(0, osdev->name);
571         if (irc < 0) {
572                 rc = irc;
573                 goto err_out_osd;
574         }
575
576         osdev->major = irc;
577
578         /* set up and announce blkdev mapping */
579         rc = osdblk_init_disk(osdev);
580         if (rc)
581                 goto err_out_blkdev;
582
583         return count;
584
585 err_out_blkdev:
586         unregister_blkdev(osdev->major, osdev->name);
587 err_out_osd:
588         osduld_put_device(osdev->osd);
589 err_out_slot:
590         mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
591         list_del_init(&osdev->node);
592         mutex_unlock(&ctl_mutex);
593
594         kfree(osdev);
595 err_out_mod:
596         OSDBLK_DEBUG("Error adding device %s\n", buf);
597         module_put(THIS_MODULE);
598         return rc;
599 }
600
601 static ssize_t class_osdblk_remove(struct class *c,
602                                         struct class_attribute *attr,
603                                         const char *buf,
604                                         size_t count)
605 {
606         struct osdblk_device *osdev = NULL;
607         int target_id, rc;
608         unsigned long ul;
609         struct list_head *tmp;
610
611         rc = strict_strtoul(buf, 10, &ul);
612         if (rc)
613                 return rc;
614
615         /* convert to int; abort if we lost anything in the conversion */
616         target_id = (int) ul;
617         if (target_id != ul)
618                 return -EINVAL;
619
620         /* remove object from list immediately */
621         mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
622
623         list_for_each(tmp, &osdblkdev_list) {
624                 osdev = list_entry(tmp, struct osdblk_device, node);
625                 if (osdev->id == target_id) {
626                         list_del_init(&osdev->node);
627                         break;
628                 }
629                 osdev = NULL;
630         }
631
632         mutex_unlock(&ctl_mutex);
633
634         if (!osdev)
635                 return -ENOENT;
636
637         /* clean up and free blkdev and associated OSD connection */
638         osdblk_free_disk(osdev);
639         unregister_blkdev(osdev->major, osdev->name);
640         osduld_put_device(osdev->osd);
641         kfree(osdev);
642
643         /* release module ref */
644         module_put(THIS_MODULE);
645
646         return count;
647 }
648
649 static struct class_attribute class_osdblk_attrs[] = {
650         __ATTR(add,     0200, NULL, class_osdblk_add),
651         __ATTR(remove,  0200, NULL, class_osdblk_remove),
652         __ATTR(list,    0444, class_osdblk_list, NULL),
653         __ATTR_NULL
654 };
655
656 static int osdblk_sysfs_init(void)
657 {
658         int ret = 0;
659
660         /*
661          * create control files in sysfs
662          * /sys/class/osdblk/...
663          */
664         class_osdblk = kzalloc(sizeof(*class_osdblk), GFP_KERNEL);
665         if (!class_osdblk)
666                 return -ENOMEM;
667
668         class_osdblk->name = DRV_NAME;
669         class_osdblk->owner = THIS_MODULE;
670         class_osdblk->class_release = class_osdblk_release;
671         class_osdblk->class_attrs = class_osdblk_attrs;
672
673         ret = class_register(class_osdblk);
674         if (ret) {
675                 kfree(class_osdblk);
676                 class_osdblk = NULL;
677                 printk(PFX "failed to create class osdblk\n");
678                 return ret;
679         }
680
681         return 0;
682 }
683
684 static void osdblk_sysfs_cleanup(void)
685 {
686         if (class_osdblk)
687                 class_destroy(class_osdblk);
688         class_osdblk = NULL;
689 }
690
691 static int __init osdblk_init(void)
692 {
693         int rc;
694
695         rc = osdblk_sysfs_init();
696         if (rc)
697                 return rc;
698
699         return 0;
700 }
701
702 static void __exit osdblk_exit(void)
703 {
704         osdblk_sysfs_cleanup();
705 }
706
707 module_init(osdblk_init);
708 module_exit(osdblk_exit);
709