#include <linux/bio.h> #include <linux/blk-mq.h> #include <linux/blkdev.h> #include <linux/errno.h> #include <linux/fcntl.h> #include <linux/fs.h> #include <linux/genhd.h> #include <linux/module.h> #include <linux/string.h> #include <linux/types.h> #include <linux/vmalloc.h> #define DISK_NAME "vramdisk" #define MEMSIZE 0x19000 // Size of Ram disk in sectors int major = 0; // Variable for Major Number #define MDISK_SECTOR_SIZE 512 #define MBR_SIZE MDISK_SECTOR_SIZE #define MBR_DISK_SIGNATURE_OFFSET 440 #define MBR_DISK_SIGNATURE_SIZE 4 #define PARTITION_TABLE_OFFSET 446 #define PARTITION_ENTRY_SIZE 16 #define PARTITION_TABLE_SIZE 64 #define MBR_SIGNATURE_OFFSET 510 #define MBR_SIGNATURE_SIZE 2 #define MBR_SIGNATURE 0xAA55 #define BR_SIZE MDISK_SECTOR_SIZE #define BR_SIGNATURE_OFFSET 510 #define BR_SIGNATURE_SIZE 2 #define BR_SIGNATURE 0xAA55 typedef struct { unsigned char boot_type; // 0x00 - Inactive; 0x80 - Active (Bootable) unsigned char start_head; unsigned char start_sec : 6; unsigned char start_cyl_hi : 2; unsigned char start_cyl; unsigned char part_type; unsigned char end_head; unsigned char end_sec : 6; unsigned char end_cyl_hi : 2; unsigned char end_cyl; unsigned int abs_start_sec; unsigned int sec_in_part; } PartEntry; typedef PartEntry PartTable[4]; static struct lock_class_key sr_bio_compl_lkclass; #define SEC_PER_HEAD 63 #define HEAD_PER_CYL 255 #define HEAD_SIZE (SEC_PER_HEAD * MDISK_SECTOR_SIZE) #define CYL_SIZE (SEC_PER_HEAD * HEAD_PER_CYL * MDISK_SECTOR_SIZE) #define sec4size(s) ((((s) % CYL_SIZE) % HEAD_SIZE) / MDISK_SECTOR_SIZE) #define head4size(s) (((s) % CYL_SIZE) / HEAD_SIZE) #define cyl4size(s) ((s) / CYL_SIZE) static PartTable def_part_table = { { boot_type: 0x00, start_sec: 0x2, start_head: 0x0, start_cyl: 0x0, part_type: 0x83, end_head: 0x3, end_sec: 0x20, end_cyl: 0x9F, abs_start_sec: 0x1, sec_in_part: 0x4FFF // 10Mbyte }, { boot_type: 0x00, start_head: 0x4, start_sec: 0x1, start_cyl: 0x0, part_type: 0x05, // extended partition type end_sec: 0x20, end_head: 0xB, end_cyl: 0x9F, abs_start_sec: 0x5000, sec_in_part: 0xA000 } }; static unsigned int def_log_part_br_abs_start_sector[] = {0x5000, 0xA000}; static const PartTable def_log_part_table[] = { { { boot_type: 0x00, start_head: 0x4, start_sec: 0x2, start_cyl: 0x0, part_type: 0x83, end_head: 0x7, end_sec: 0x20, end_cyl: 0x9F, abs_start_sec: 0x1, sec_in_part: 0x4FFF }, { boot_type: 0x00, start_head: 0x8, start_sec: 0x01, start_cyl: 0x00, part_type: 0x05, end_head: 0xB, end_sec: 0x20, end_cyl: 0x9F, abs_start_sec: 0x5000, sec_in_part: 0x5000 } }, { { boot_type: 0x00, start_head: 0x8, start_sec: 0x02, start_cyl: 0x00, part_type: 0x83, end_head: 0xB, end_sec: 0x20, end_cyl: 0x9F, abs_start_sec: 0x1, sec_in_part: 0x4FFF } } }; static void copy_mbr(u8 *disk) { memset(disk, 0x0, MBR_SIZE); *(unsigned long *)(disk + MBR_DISK_SIGNATURE_OFFSET) = 0x36E5756D; memcpy(disk + PARTITION_TABLE_OFFSET, &def_part_table, PARTITION_TABLE_SIZE); *(unsigned short *)(disk + MBR_SIGNATURE_OFFSET) = MBR_SIGNATURE; } static void copy_br(u8 *disk, int abs_start_sector, const PartTable *part_table) { disk += (abs_start_sector * MDISK_SECTOR_SIZE); memset(disk, 0x0, BR_SIZE); memcpy(disk + PARTITION_TABLE_OFFSET, part_table, PARTITION_TABLE_SIZE); *(unsigned short *)(disk + BR_SIGNATURE_OFFSET) = BR_SIGNATURE; } void copy_mbr_n_br(u8 *disk) { int i; copy_mbr(disk); for (i = 0; i < ARRAY_SIZE(def_log_part_table); ++i) copy_br(disk, def_log_part_br_abs_start_sector[i], &def_log_part_table[i]); } /* Structure associated with Block device*/ struct vram_disk_device { size_t size; u8 *data; atomic_t nr_users; // spinlock_t lock; struct blk_mq_tag_set tag_set; struct request_queue *queue; struct gendisk *gd; } device; // struct vram_disk_device *x; static int vramdisk_open(struct block_device *x, fmode_t mode) { struct vram_disk_device *dev = x->bd_disk->private_data; if (dev == NULL) { printk(KERN_WARNING DISK_NAME "driver: invalid disk private_data\n"); return -ENXIO; } atomic_inc(&dev->nr_users); printk(KERN_INFO DISK_NAME "driver: open \n"); return 0; } static void vramdisk_release(struct gendisk *disk, fmode_t mode) { struct vram_disk_device *dev = disk->private_data; if (dev) { atomic_dec(&dev->nr_users); printk(KERN_INFO DISK_NAME "driver: closed \n"); } else printk(KERN_WARNING DISK_NAME "driver: invalid disk private_data\n"); } static struct block_device_operations fops = { .owner = THIS_MODULE, .open = vramdisk_open, .release = vramdisk_release, }; int vramdisk_init(void) { device.size = MEMSIZE; (device.data) = vmalloc(MEMSIZE * MDISK_SECTOR_SIZE); if (device.data == NULL) { printk(KERN_WARNING DISK_NAME "driver: vmalloc failure.\n"); return -ENOMEM; } /* Load partition table */ copy_mbr_n_br(device.data); return 0; } static int rb_transfer(struct request *req, unsigned int *nr_bytes) { int dir = rq_data_dir(req); sector_t start_sector = blk_rq_pos(req); unsigned int sector_cnt = blk_rq_sectors(req); /* no of sector on which opn to be done*/ struct bio_vec bv; #define BV_PAGE(bv) ((bv).bv_page) #define BV_OFFSET(bv) ((bv).bv_offset) #define BV_LEN(bv) ((bv).bv_len) struct req_iterator iter; sector_t sector_offset = 0; unsigned int sectors; u8 *buffer; rq_for_each_segment(bv, req, iter) { buffer = page_address(BV_PAGE(bv)) + BV_OFFSET(bv); if (BV_LEN(bv) % (MDISK_SECTOR_SIZE) != 0) { printk(KERN_ERR DISK_NAME ": bio size is not a multiple of sector size\n"); return -EIO; } sectors = BV_LEN(bv) / MDISK_SECTOR_SIZE; printk(KERN_DEBUG DISK_NAME ": Start Sector: %llu, Sector Offset: %llu; " "Buffer: %p; Length: %u sectors\n", (unsigned long long)(start_sector), (unsigned long long)(sector_offset), buffer, sectors); if (dir == WRITE) /* Write to the device */ memcpy((device.data) + ((start_sector + sector_offset) * MDISK_SECTOR_SIZE), buffer, sectors * MDISK_SECTOR_SIZE); else /* Read from the device */ memcpy(buffer, (device.data) + ((start_sector + sector_offset) * MDISK_SECTOR_SIZE), sectors * MDISK_SECTOR_SIZE); sector_offset += sectors; *nr_bytes += sectors * MDISK_SECTOR_SIZE; } if (sector_offset != sector_cnt) { printk(KERN_ERR DISK_NAME ": bio info doesn't match with the request info"); return -EIO; } return 0; #undef BV_PAGE #undef BV_OFFSET #undef BV_LEN } /* request handling function */ /** * Changing according to blk multi-queue feature */ static blk_status_t handle_request(struct blk_mq_hw_ctx *hardware_context, const struct blk_mq_queue_data *bd) { blk_status_t status = BLK_STS_OK; unsigned int nr_bytes = 0; struct request *req = bd->rq; switch (rb_transfer(req, &nr_bytes)) { case -EIO: { status = BLK_STS_IOERR; } break; default: { status = BLK_STS_OK; } break; }; if (blk_update_request(req, status, nr_bytes)) // GPL-only symbol BUG(); __blk_mq_end_request(req, status); return status; } static struct blk_mq_ops vram_queue_ops = { .queue_rq = handle_request, }; int device_setup(void) { /* Register block device */ major = register_blkdev(major, DISK_NAME); // major no. allocation if (major <= 0) { printk(KERN_WARNING DISK_NAME ": unable to get major number\n"); return -EBUSY; } printk(KERN_ALERT "Major Number is : %d", major); if (vramdisk_init()) { return -ENOMEM; } device.tag_set.ops = &vram_queue_ops; device.tag_set.nr_hw_queues = 1; device.tag_set.queue_depth = 128; device.tag_set.numa_node = NUMA_NO_NODE; device.tag_set.cmd_size = sizeof(struct vram_disk_device); device.tag_set.flags = BLK_MQ_F_SHOULD_MERGE; device.tag_set.driver_data = &device; if (blk_mq_alloc_tag_set(&(device.tag_set))) { return -ENOMEM; } device.queue = blk_mq_init_queue(&(device.tag_set)); if (IS_ERR(device.queue)) { blk_mq_free_tag_set(&(device.tag_set)); return -ENOMEM; } device.queue->queuedata = &device; /* Initialize gendisk */ device.gd = __alloc_disk_node(device.queue, 7, &sr_bio_compl_lkclass); if (!device.gd) { printk(KERN_INFO DISK_NAME ": alloc_disk failure\n"); return -EBUSY; } device.gd->minors = 7; (device.gd)->major = major; // major no to gendisk device.gd->first_minor = 0; // first minor of gendisk device.gd->fops = &fops; device.gd->private_data = &device; device.gd->queue = device.queue; printk(KERN_INFO "THIS IS DEVICE SIZE %zu", device.size); // snprintf(((device.gd)->disk_name), DISK_NAME_LEN, DISK_NAME); set_capacity(device.gd, device.size); add_disk(device.gd); return 0; } static int __init vramdisk_drive_init(void) { int ret = 0; ret = device_setup(); return ret; } void vramdisk_cleanup(void) { vfree(device.data); } void __exit vramdisk_drive_exit(void) { del_gendisk(device.gd); put_disk(device.gd); blk_cleanup_queue(device.queue); blk_mq_free_tag_set(&(device.tag_set)); // cleanup device buffer in ram vramdisk_cleanup(); unregister_blkdev(major, DISK_NAME); } module_init(vramdisk_drive_init); module_exit(vramdisk_drive_exit); MODULE_LICENSE("Dual MIT/GPL"); MODULE_AUTHOR("Bitway"); MODULE_DESCRIPTION("BLOCK DRIVER");