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 
osdblk_make_credential(u8 cred_a[OSD_CAP_LEN],const struct osd_obj_id * obj)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  */
osd_sync_op(struct osd_request * or,int timeout,uint8_t * credential)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  */
osd_async_op(struct osd_request * or,osd_req_done_fn * async_done,void * caller_context,u8 * cred)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? */
extract_attr_from_req(struct osd_request * or,struct osd_attr * attr)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 
osdblk_get_obj_size(struct osdblk_device * osdev,u64 * size_out)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 
osdblk_osd_complete(struct osd_request * or,void * private)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 
bio_chain_put(struct bio * chain)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 
bio_chain_clone(struct bio * old_chain,gfp_t gfpmask)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 
osdblk_rq_fn(struct request_queue * q)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 			blk_end_request_all(rq, 0);
315 			continue;
316 		}
317 
318 		/* deduce our operation (read, write, flush) */
319 		/* I wish the block layer simplified cmd_type/cmd_flags/cmd[]
320 		 * into a clearly defined set of RPC commands:
321 		 * read, write, flush, scsi command, power mgmt req,
322 		 * driver-specific, etc.
323 		 */
324 
325 		do_flush = rq->cmd_flags & REQ_FLUSH;
326 		do_write = (rq_data_dir(rq) == WRITE);
327 
328 		if (!do_flush) { /* osd_flush does not use a bio */
329 			/* a bio clone to be passed down to OSD request */
330 			bio = bio_chain_clone(rq->bio, GFP_ATOMIC);
331 			if (!bio)
332 				break;
333 		} else
334 			bio = NULL;
335 
336 		/* alloc internal OSD request, for OSD command execution */
337 		or = osd_start_request(osdev->osd, GFP_ATOMIC);
338 		if (!or) {
339 			bio_chain_put(bio);
340 			OSDBLK_DEBUG("osd_start_request with err\n");
341 			break;
342 		}
343 
344 		orq = &osdev->req[rq->tag];
345 		orq->rq = rq;
346 		orq->bio = bio;
347 		orq->osdev = osdev;
348 
349 		/* init OSD command: flush, write or read */
350 		if (do_flush)
351 			osd_req_flush_object(or, &osdev->obj,
352 					     OSD_CDB_FLUSH_ALL, 0, 0);
353 		else if (do_write)
354 			osd_req_write(or, &osdev->obj, blk_rq_pos(rq) * 512ULL,
355 				      bio, blk_rq_bytes(rq));
356 		else
357 			osd_req_read(or, &osdev->obj, blk_rq_pos(rq) * 512ULL,
358 				     bio, blk_rq_bytes(rq));
359 
360 		OSDBLK_DEBUG("%s 0x%x bytes at 0x%llx\n",
361 			do_flush ? "flush" : do_write ?
362 				"write" : "read", blk_rq_bytes(rq),
363 			blk_rq_pos(rq) * 512ULL);
364 
365 		/* begin OSD command execution */
366 		if (osd_async_op(or, osdblk_osd_complete, orq,
367 				 osdev->obj_cred)) {
368 			osd_end_request(or);
369 			blk_requeue_request(q, rq);
370 			bio_chain_put(bio);
371 			OSDBLK_DEBUG("osd_execute_request_async with err\n");
372 			break;
373 		}
374 
375 		/* remove the special 'flush' marker, now that the command
376 		 * is executing
377 		 */
378 		rq->special = NULL;
379 	}
380 }
381 
osdblk_free_disk(struct osdblk_device * osdev)382 static void osdblk_free_disk(struct osdblk_device *osdev)
383 {
384 	struct gendisk *disk = osdev->disk;
385 
386 	if (!disk)
387 		return;
388 
389 	if (disk->flags & GENHD_FL_UP)
390 		del_gendisk(disk);
391 	if (disk->queue)
392 		blk_cleanup_queue(disk->queue);
393 	put_disk(disk);
394 }
395 
osdblk_init_disk(struct osdblk_device * osdev)396 static int osdblk_init_disk(struct osdblk_device *osdev)
397 {
398 	struct gendisk *disk;
399 	struct request_queue *q;
400 	int rc;
401 	u64 obj_size = 0;
402 
403 	/* contact OSD, request size info about the object being mapped */
404 	rc = osdblk_get_obj_size(osdev, &obj_size);
405 	if (rc)
406 		return rc;
407 
408 	/* create gendisk info */
409 	disk = alloc_disk(OSDBLK_MINORS_PER_MAJOR);
410 	if (!disk)
411 		return -ENOMEM;
412 
413 	sprintf(disk->disk_name, DRV_NAME "%d", osdev->id);
414 	disk->major = osdev->major;
415 	disk->first_minor = 0;
416 	disk->fops = &osdblk_bd_ops;
417 	disk->private_data = osdev;
418 
419 	/* init rq */
420 	q = blk_init_queue(osdblk_rq_fn, &osdev->lock);
421 	if (!q) {
422 		put_disk(disk);
423 		return -ENOMEM;
424 	}
425 
426 	/* switch queue to TCQ mode; allocate tag map */
427 	rc = blk_queue_init_tags(q, OSDBLK_MAX_REQ, NULL);
428 	if (rc) {
429 		blk_cleanup_queue(q);
430 		put_disk(disk);
431 		return rc;
432 	}
433 
434 	/* Set our limits to the lower device limits, because osdblk cannot
435 	 * sleep when allocating a lower-request and therefore cannot be
436 	 * bouncing.
437 	 */
438 	blk_queue_stack_limits(q, osd_request_queue(osdev->osd));
439 
440 	blk_queue_prep_rq(q, blk_queue_start_tag);
441 	blk_queue_flush(q, REQ_FLUSH);
442 
443 	disk->queue = q;
444 
445 	q->queuedata = osdev;
446 
447 	osdev->disk = disk;
448 	osdev->q = q;
449 
450 	/* finally, announce the disk to the world */
451 	set_capacity(disk, obj_size / 512ULL);
452 	add_disk(disk);
453 
454 	printk(KERN_INFO "%s: Added of size 0x%llx\n",
455 		disk->disk_name, (unsigned long long)obj_size);
456 
457 	return 0;
458 }
459 
460 /********************************************************************
461  * /sys/class/osdblk/
462  *                   add	map OSD object to blkdev
463  *                   remove	unmap OSD object
464  *                   list	show mappings
465  *******************************************************************/
466 
class_osdblk_release(struct class * cls)467 static void class_osdblk_release(struct class *cls)
468 {
469 	kfree(cls);
470 }
471 
class_osdblk_list(struct class * c,struct class_attribute * attr,char * data)472 static ssize_t class_osdblk_list(struct class *c,
473 				struct class_attribute *attr,
474 				char *data)
475 {
476 	int n = 0;
477 	struct list_head *tmp;
478 
479 	mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
480 
481 	list_for_each(tmp, &osdblkdev_list) {
482 		struct osdblk_device *osdev;
483 
484 		osdev = list_entry(tmp, struct osdblk_device, node);
485 
486 		n += sprintf(data+n, "%d %d %llu %llu %s\n",
487 			osdev->id,
488 			osdev->major,
489 			osdev->obj.partition,
490 			osdev->obj.id,
491 			osdev->osd_path);
492 	}
493 
494 	mutex_unlock(&ctl_mutex);
495 	return n;
496 }
497 
class_osdblk_add(struct class * c,struct class_attribute * attr,const char * buf,size_t count)498 static ssize_t class_osdblk_add(struct class *c,
499 				struct class_attribute *attr,
500 				const char *buf, size_t count)
501 {
502 	struct osdblk_device *osdev;
503 	ssize_t rc;
504 	int irc, new_id = 0;
505 	struct list_head *tmp;
506 
507 	if (!try_module_get(THIS_MODULE))
508 		return -ENODEV;
509 
510 	/* new osdblk_device object */
511 	osdev = kzalloc(sizeof(*osdev) + strlen(buf) + 1, GFP_KERNEL);
512 	if (!osdev) {
513 		rc = -ENOMEM;
514 		goto err_out_mod;
515 	}
516 
517 	/* static osdblk_device initialization */
518 	spin_lock_init(&osdev->lock);
519 	INIT_LIST_HEAD(&osdev->node);
520 
521 	/* generate unique id: find highest unique id, add one */
522 
523 	mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
524 
525 	list_for_each(tmp, &osdblkdev_list) {
526 		struct osdblk_device *osdev;
527 
528 		osdev = list_entry(tmp, struct osdblk_device, node);
529 		if (osdev->id > new_id)
530 			new_id = osdev->id + 1;
531 	}
532 
533 	osdev->id = new_id;
534 
535 	/* add to global list */
536 	list_add_tail(&osdev->node, &osdblkdev_list);
537 
538 	mutex_unlock(&ctl_mutex);
539 
540 	/* parse add command */
541 	if (sscanf(buf, "%llu %llu %s", &osdev->obj.partition, &osdev->obj.id,
542 		   osdev->osd_path) != 3) {
543 		rc = -EINVAL;
544 		goto err_out_slot;
545 	}
546 
547 	/* initialize rest of new object */
548 	sprintf(osdev->name, DRV_NAME "%d", osdev->id);
549 
550 	/* contact requested OSD */
551 	osdev->osd = osduld_path_lookup(osdev->osd_path);
552 	if (IS_ERR(osdev->osd)) {
553 		rc = PTR_ERR(osdev->osd);
554 		goto err_out_slot;
555 	}
556 
557 	/* build OSD credential */
558 	osdblk_make_credential(osdev->obj_cred, &osdev->obj);
559 
560 	/* register our block device */
561 	irc = register_blkdev(0, osdev->name);
562 	if (irc < 0) {
563 		rc = irc;
564 		goto err_out_osd;
565 	}
566 
567 	osdev->major = irc;
568 
569 	/* set up and announce blkdev mapping */
570 	rc = osdblk_init_disk(osdev);
571 	if (rc)
572 		goto err_out_blkdev;
573 
574 	return count;
575 
576 err_out_blkdev:
577 	unregister_blkdev(osdev->major, osdev->name);
578 err_out_osd:
579 	osduld_put_device(osdev->osd);
580 err_out_slot:
581 	mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
582 	list_del_init(&osdev->node);
583 	mutex_unlock(&ctl_mutex);
584 
585 	kfree(osdev);
586 err_out_mod:
587 	OSDBLK_DEBUG("Error adding device %s\n", buf);
588 	module_put(THIS_MODULE);
589 	return rc;
590 }
591 
class_osdblk_remove(struct class * c,struct class_attribute * attr,const char * buf,size_t count)592 static ssize_t class_osdblk_remove(struct class *c,
593 					struct class_attribute *attr,
594 					const char *buf,
595 					size_t count)
596 {
597 	struct osdblk_device *osdev = NULL;
598 	int target_id, rc;
599 	unsigned long ul;
600 	struct list_head *tmp;
601 
602 	rc = strict_strtoul(buf, 10, &ul);
603 	if (rc)
604 		return rc;
605 
606 	/* convert to int; abort if we lost anything in the conversion */
607 	target_id = (int) ul;
608 	if (target_id != ul)
609 		return -EINVAL;
610 
611 	/* remove object from list immediately */
612 	mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
613 
614 	list_for_each(tmp, &osdblkdev_list) {
615 		osdev = list_entry(tmp, struct osdblk_device, node);
616 		if (osdev->id == target_id) {
617 			list_del_init(&osdev->node);
618 			break;
619 		}
620 		osdev = NULL;
621 	}
622 
623 	mutex_unlock(&ctl_mutex);
624 
625 	if (!osdev)
626 		return -ENOENT;
627 
628 	/* clean up and free blkdev and associated OSD connection */
629 	osdblk_free_disk(osdev);
630 	unregister_blkdev(osdev->major, osdev->name);
631 	osduld_put_device(osdev->osd);
632 	kfree(osdev);
633 
634 	/* release module ref */
635 	module_put(THIS_MODULE);
636 
637 	return count;
638 }
639 
640 static struct class_attribute class_osdblk_attrs[] = {
641 	__ATTR(add,	0200, NULL, class_osdblk_add),
642 	__ATTR(remove,	0200, NULL, class_osdblk_remove),
643 	__ATTR(list,	0444, class_osdblk_list, NULL),
644 	__ATTR_NULL
645 };
646 
osdblk_sysfs_init(void)647 static int osdblk_sysfs_init(void)
648 {
649 	int ret = 0;
650 
651 	/*
652 	 * create control files in sysfs
653 	 * /sys/class/osdblk/...
654 	 */
655 	class_osdblk = kzalloc(sizeof(*class_osdblk), GFP_KERNEL);
656 	if (!class_osdblk)
657 		return -ENOMEM;
658 
659 	class_osdblk->name = DRV_NAME;
660 	class_osdblk->owner = THIS_MODULE;
661 	class_osdblk->class_release = class_osdblk_release;
662 	class_osdblk->class_attrs = class_osdblk_attrs;
663 
664 	ret = class_register(class_osdblk);
665 	if (ret) {
666 		kfree(class_osdblk);
667 		class_osdblk = NULL;
668 		printk(PFX "failed to create class osdblk\n");
669 		return ret;
670 	}
671 
672 	return 0;
673 }
674 
osdblk_sysfs_cleanup(void)675 static void osdblk_sysfs_cleanup(void)
676 {
677 	if (class_osdblk)
678 		class_destroy(class_osdblk);
679 	class_osdblk = NULL;
680 }
681 
osdblk_init(void)682 static int __init osdblk_init(void)
683 {
684 	int rc;
685 
686 	rc = osdblk_sysfs_init();
687 	if (rc)
688 		return rc;
689 
690 	return 0;
691 }
692 
osdblk_exit(void)693 static void __exit osdblk_exit(void)
694 {
695 	osdblk_sysfs_cleanup();
696 }
697 
698 module_init(osdblk_init);
699 module_exit(osdblk_exit);
700 
701