/* * bd.c * * Copyright (c) 2004 Evgeniy Polyakov * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include //#define BD_DEBUG #ifdef BD_DEBUG #define dprintk(f, a...) printk(KERN_EMERG f, ##a) #else #define dprintk(f, a...) do {} while(0) #endif #define BD_READ 0 #define BD_WRITE 1 static char bd_name[] = "bd"; static unsigned int bd_major = 123; module_param(bd_major, uint, 0); static unsigned int bd_sector_size = 512; module_param(bd_sector_size, uint, 0); static unsigned int bd_max_request_size = 1024*1024*50; module_param(bd_max_request_size, uint, 0); static int sync_mode = 0; module_param(sync_mode, uint, 0); static void *bd_request_data; static spinlock_t bd_lock = SPIN_LOCK_UNLOCKED; static struct gendisk *bd_disk; static unsigned long req_counter = 1; extern void bd_encrypt(void *src, void *dst, u64 size, void *priv); extern void bd_decrypt(void *src, void *dst, u64 size, void *priv); static int bd_ioctl(struct inode *, struct file *, unsigned int, unsigned long); static struct block_device_operations bd_fops = { .owner = THIS_MODULE, .ioctl = bd_ioctl, }; static int bd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { dprintk("%s: cmd=%u, arg=%ld.\n", __func__, cmd, arg); return 0; } static int bd_process_data(int cmd, void *buf, u64 off, u64 size, void *priv) { if (size > bd_max_request_size) { dprintk("Too big size %llu, setting to %u.\n", size, bd_max_request_size); size = bd_max_request_size; } if (off + size > bd_max_request_size) { dprintk("Too big size %llu or offset %llu, exiting.\n", size, off); size = bd_max_request_size; } switch (cmd) { case BD_READ: bd_decrypt(((char *)bd_request_data) + off, buf, size, priv); break; case BD_WRITE: bd_encrypt(buf, ((char *)bd_request_data) + off, size, priv); break; } dprintk("%s finished cmd=%s, size=%llu, off=%llu.\n", __func__, (cmd == BD_WRITE)?"WRITE":"READ", size, off); return 0; } static __inline__ int bd_process_bvec(int cmd, struct bio_vec *bvec, u64 off, void *priv) { int err; char *kaddr; void *buf; u64 size; kaddr = page_address(bvec->bv_page); buf = kaddr + bvec->bv_offset; size = bvec->bv_len; err = bd_process_data(cmd, buf, off, size, priv); return err; } static void bd_request(request_queue_t * q) { struct request *req; u64 size, off; int err, cmd, bad_bio; struct bio *bio; while ((req = elv_next_request(q)) != NULL) { blkdev_dequeue_request(req); (unsigned long)req->special = 0; off = req->sector * bd_sector_size; size = req->nr_sectors * bd_sector_size; cmd = (rq_data_dir(req) == 1)?BD_WRITE:BD_READ; dprintk("%s: TRANSFER: req=%p [%lu], cmd=%s, off=%llu [sector=%llu], size=%llu [nr_sectors=%lu], flags=%lx.\n", bd_name, req, (unsigned long)req->special, (cmd == BD_WRITE)?"WRITE":"READ", off, req->sector, size, req->nr_sectors, req->flags); bad_bio = 0; if (!(req->flags & REQ_CMD)) { dprintk("%s: wrong command.\n", bd_name); goto err_out_wrong_command; } rq_for_each_bio(bio, req) { struct bio_vec *bvec; int i; dprintk("BIO: bi_size=%u, bvec=%p.\n", bio->bi_size, bio->bi_io_vec); if (!bio->bi_size || !bio->bi_io_vec) { bad_bio = 1; continue; } bio_for_each_segment(bvec, bio, i) { dprintk("%s: i=%d, req=%p, size=%d.\n", bd_disk->disk_name, i, req, bvec->bv_len); err = bd_process_bvec(cmd, bvec, off, req); if (err) { bad_bio = 1; continue; } off += bvec->bv_len; size -= bvec->bv_len; } /* * bio->bi_end_io = NULL; * bio_endio(bio, bio->bi_size, (bad_bio)?-EIO:0); */ } dprintk("cmd=%s has been processed: err=%d\n", (cmd == BD_WRITE)?"WRITE":"READ", req->errors); err_out_wrong_command: if (sync_mode) { if (!end_that_request_first(req, (bad_bio)?0:1, req->nr_sectors)); end_that_request_last(req); } } } static void bd_setup(struct gendisk *d) { request_queue_t *q; d->major = bd_major; d->first_minor = 0; d->fops = &bd_fops; d->private_data = NULL; d->flags = GENHD_FL_SUPPRESS_PARTITION_INFO; sprintf(d->disk_name, "%s%d", bd_name, 0); q = d->queue; blk_queue_hardsect_size(q, bd_sector_size); set_capacity(d, bd_max_request_size/bd_sector_size); add_disk(d); } int __devinit bd_init(void) { int err; bd_request_data = vmalloc(bd_max_request_size); if (!bd_request_data) { dprintk("Failed to allocate %d bytes for %s requests.\n", bd_max_request_size, bd_name); return -ENOMEM; } err = register_blkdev(bd_major, bd_name); if (err) { dprintk("Failed to register blkdev with major %u: err=%d.\n", bd_major, err); return err; } bd_disk = alloc_disk(1); if (!bd_disk) { dprintk("Failed to allocate a disk.\n"); goto err_out_unregister_blkdev; } bd_disk->queue = blk_init_queue(bd_request, &bd_lock); if (!bd_disk->queue) { dprintk("Failed to initialize blk queue: err=%d.\n", err); goto err_out_free_disk; } bd_setup(bd_disk); dprintk("%s has beed successfully added.\n", bd_name); return 0; err_out_free_disk: put_disk(bd_disk); err_out_unregister_blkdev: unregister_blkdev(bd_major, "bd"); vfree(bd_request_data); return -EINVAL; } void __devexit bd_fini(void) { del_gendisk(bd_disk); blk_cleanup_queue(bd_disk->queue); put_disk(bd_disk); unregister_blkdev(bd_major, "bd"); vfree(bd_request_data); dprintk("%s has beed successfully removed.\n", bd_name); } module_init(bd_init); module_exit(bd_fini); MODULE_AUTHOR("Evgeniy Polyakov "); MODULE_LICENSE("GPL");