├── README.md ├── common └── stackbd.h ├── create_loop_device.sh ├── module ├── Makefile └── stackbd.c └── util ├── Makefile └── stackbd_util.c /README.md: -------------------------------------------------------------------------------- 1 | stackbd 2 | ======= 3 | -------------------------------------------------------------------------------- /common/stackbd.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* IOCTL */ 4 | #define STACKBD_DO_IT _IOW( 0xad, 0, char * ) 5 | 6 | #define STACKBD_NAME "stackbd" 7 | #define STACKBD_NAME_0 STACKBD_NAME "0" 8 | -------------------------------------------------------------------------------- /create_loop_device.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | file_dev=$HOME/disk_file 4 | loop_dev=/dev/loop0 5 | 6 | # if [ -e $loop_dev ]; then 7 | # echo "$loop_dev already exists" 8 | # exit 1 9 | # fi 10 | 11 | if [ -e $file_dev ]; then 12 | echo "$file_dev already exists" 13 | else 14 | dd if=/dev/urandom of=$file_dev bs=1024 count=100000 15 | fi 16 | 17 | sudo losetup $loop_dev $file_dev 18 | -------------------------------------------------------------------------------- /module/Makefile: -------------------------------------------------------------------------------- 1 | EXTRA_CFLAGS += -D_GNU_SOURCE 2 | obj-m := stackbd.o 3 | 4 | all: 5 | make -C /usr/src/linux-headers-$(shell uname -r) SUBDIRS=$(PWD) modules 6 | 7 | clean: 8 | make -C /usr/src/linux-headers-$(shell uname -r) SUBDIRS=$(PWD) clean 9 | -------------------------------------------------------------------------------- /module/stackbd.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include /* printk() */ 6 | #include /* everything... */ 7 | #include /* error codes */ 8 | #include /* size_t */ 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #include "../common/stackbd.h" 18 | 19 | #define STACKBD_BDEV_MODE (FMODE_READ | FMODE_WRITE | FMODE_EXCL) 20 | #define DEBUGGG printk("stackbd: %d\n", __LINE__); 21 | /* 22 | * We can tweak our hardware sector size, but the kernel talks to us 23 | * in terms of small sectors, always. 24 | */ 25 | #define KERNEL_SECTOR_SIZE 512 26 | 27 | 28 | MODULE_LICENSE("Dual BSD/GPL"); 29 | 30 | static int major_num = 0; 31 | module_param(major_num, int, 0); 32 | static int LOGICAL_BLOCK_SIZE = 512; 33 | module_param(LOGICAL_BLOCK_SIZE, int, 0); 34 | 35 | /* 36 | * The internal representation of our device. 37 | */ 38 | static struct stackbd_t { 39 | sector_t capacity; /* Sectors */ 40 | struct gendisk *gd; 41 | spinlock_t lock; 42 | struct bio_list bio_list; 43 | struct task_struct *thread; 44 | int is_active; 45 | struct block_device *bdev_raw; 46 | /* Our request queue */ 47 | struct request_queue *queue; 48 | } stackbd; 49 | 50 | static DECLARE_WAIT_QUEUE_HEAD(req_event); 51 | 52 | static void stackbd_io_fn(struct bio *bio) 53 | { 54 | // printk("stackdb: Mapping sector: %llu -> %llu, dev: %s -> %s\n", 55 | // bio->bi_sector, 56 | // lba != EMPTY_REAL_LBA ? lba : bio->bi_sector, 57 | // bio->bi_bdev->bd_disk->disk_name, 58 | // bdev_raw->bd_disk->disk_name); 59 | // 60 | // if (lba != EMPTY_REAL_LBA) 61 | // bio->bi_sector = lba; 62 | bio->bi_bdev = stackbd.bdev_raw; 63 | 64 | trace_block_bio_remap(bdev_get_queue(stackbd.bdev_raw), bio, 65 | bio->bi_bdev->bd_dev, bio->bi_sector); 66 | 67 | /* No need to call bio_endio() */ 68 | generic_make_request(bio); 69 | } 70 | 71 | static int stackbd_threadfn(void *data) 72 | { 73 | struct bio *bio; 74 | 75 | set_user_nice(current, -20); 76 | 77 | while (!kthread_should_stop()) 78 | { 79 | /* wake_up() is after adding bio to list. No need for condition */ 80 | wait_event_interruptible(req_event, kthread_should_stop() || 81 | !bio_list_empty(&stackbd.bio_list)); 82 | 83 | spin_lock_irq(&stackbd.lock); 84 | if (bio_list_empty(&stackbd.bio_list)) 85 | { 86 | spin_unlock_irq(&stackbd.lock); 87 | continue; 88 | } 89 | 90 | bio = bio_list_pop(&stackbd.bio_list); 91 | spin_unlock_irq(&stackbd.lock); 92 | 93 | stackbd_io_fn(bio); 94 | } 95 | 96 | return 0; 97 | } 98 | 99 | /* 100 | * Handle an I/O request. 101 | */ 102 | static void stackbd_make_request(struct request_queue *q, struct bio *bio) 103 | { 104 | printk("stackbd: make request %-5s block %-12llu #pages %-4hu total-size " 105 | "%-10u\n", bio_data_dir(bio) == WRITE ? "write" : "read", 106 | bio->bi_sector, bio->bi_vcnt, bio->bi_size); 107 | 108 | // printk("<%p> Make request %s %s %s\n", bio, 109 | // bio->bi_rw & REQ_SYNC ? "SYNC" : "", 110 | // bio->bi_rw & REQ_FLUSH ? "FLUSH" : "", 111 | // bio->bi_rw & REQ_NOIDLE ? "NOIDLE" : ""); 112 | // 113 | spin_lock_irq(&stackbd.lock); 114 | if (!stackbd.bdev_raw) 115 | { 116 | printk("stackbd: Request before bdev_raw is ready, aborting\n"); 117 | goto abort; 118 | } 119 | if (!stackbd.is_active) 120 | { 121 | printk("stackbd: Device not active yet, aborting\n"); 122 | goto abort; 123 | } 124 | bio_list_add(&stackbd.bio_list, bio); 125 | wake_up(&req_event); 126 | spin_unlock_irq(&stackbd.lock); 127 | 128 | return; 129 | 130 | abort: 131 | spin_unlock_irq(&stackbd.lock); 132 | printk("<%p> Abort request\n\n", bio); 133 | bio_io_error(bio); 134 | } 135 | 136 | static struct block_device *stackbd_bdev_open(char dev_path[]) 137 | { 138 | /* Open underlying device */ 139 | struct block_device *bdev_raw = lookup_bdev(dev_path); 140 | printk("Opened %s\n", dev_path); 141 | 142 | if (IS_ERR(bdev_raw)) 143 | { 144 | printk("stackbd: error opening raw device <%lu>\n", PTR_ERR(bdev_raw)); 145 | return NULL; 146 | } 147 | 148 | if (!bdget(bdev_raw->bd_dev)) 149 | { 150 | printk("stackbd: error bdget()\n"); 151 | return NULL; 152 | } 153 | 154 | if (blkdev_get(bdev_raw, STACKBD_BDEV_MODE, &stackbd)) 155 | { 156 | printk("stackbd: error blkdev_get()\n"); 157 | bdput(bdev_raw); 158 | return NULL; 159 | } 160 | 161 | return bdev_raw; 162 | } 163 | 164 | static int stackbd_start(char dev_path[]) 165 | { 166 | unsigned max_sectors; 167 | 168 | if (!(stackbd.bdev_raw = stackbd_bdev_open(dev_path))) 169 | return -EFAULT; 170 | 171 | /* Set up our internal device */ 172 | stackbd.capacity = get_capacity(stackbd.bdev_raw->bd_disk); 173 | printk("stackbd: Device real capacity: %llu\n", stackbd.capacity); 174 | 175 | set_capacity(stackbd.gd, stackbd.capacity); 176 | 177 | max_sectors = queue_max_hw_sectors(bdev_get_queue(stackbd.bdev_raw)); 178 | blk_queue_max_hw_sectors(stackbd.queue, max_sectors); 179 | printk("stackbd: Max sectors: %u\n", max_sectors); 180 | 181 | stackbd.thread = kthread_create(stackbd_threadfn, NULL, 182 | stackbd.gd->disk_name); 183 | if (IS_ERR(stackbd.thread)) 184 | { 185 | printk("stackbd: error kthread_create <%lu>\n", 186 | PTR_ERR(stackbd.thread)); 187 | goto error_after_bdev; 188 | } 189 | 190 | printk("stackbd: done initializing successfully\n"); 191 | stackbd.is_active = 1; 192 | wake_up_process(stackbd.thread); 193 | 194 | return 0; 195 | 196 | error_after_bdev: 197 | blkdev_put(stackbd.bdev_raw, STACKBD_BDEV_MODE); 198 | bdput(stackbd.bdev_raw); 199 | 200 | return -EFAULT; 201 | } 202 | 203 | static int stackbd_ioctl(struct block_device *bdev, fmode_t mode, 204 | unsigned int cmd, unsigned long arg) 205 | { 206 | char dev_path[80]; 207 | void __user *argp = (void __user *)arg; 208 | 209 | switch (cmd) 210 | { 211 | case STACKBD_DO_IT: 212 | printk("\n*** DO IT!!!!!!! ***\n\n"); 213 | 214 | if (copy_from_user(dev_path, argp, sizeof(dev_path))) 215 | return -EFAULT; 216 | 217 | return stackbd_start(dev_path); 218 | default: 219 | return -ENOTTY; 220 | } 221 | } 222 | 223 | /* 224 | * The HDIO_GETGEO ioctl is handled in blkdev_ioctl(), which 225 | * calls this. We need to implement getgeo, since we can't 226 | * use tools such as fdisk to partition the drive otherwise. 227 | */ 228 | int stackbd_getgeo(struct block_device * block_device, struct hd_geometry * geo) 229 | { 230 | long size; 231 | 232 | /* We have no real geometry, of course, so make something up. */ 233 | size = stackbd.capacity * (LOGICAL_BLOCK_SIZE / KERNEL_SECTOR_SIZE); 234 | geo->cylinders = (size & ~0x3f) >> 6; 235 | geo->heads = 4; 236 | geo->sectors = 16; 237 | geo->start = 0; 238 | return 0; 239 | } 240 | 241 | /* 242 | * The device operations structure. 243 | */ 244 | static struct block_device_operations stackbd_ops = { 245 | .owner = THIS_MODULE, 246 | .getgeo = stackbd_getgeo, 247 | .ioctl = stackbd_ioctl, 248 | }; 249 | 250 | static int __init stackbd_init(void) 251 | { 252 | /* Set up our internal device */ 253 | spin_lock_init(&stackbd.lock); 254 | 255 | /* blk_alloc_queue() instead of blk_init_queue() so it won't set up the 256 | * queue for requests. 257 | */ 258 | if (!(stackbd.queue = blk_alloc_queue(GFP_KERNEL))) 259 | { 260 | printk("stackbd: alloc_queue failed\n"); 261 | return -EFAULT; 262 | } 263 | 264 | blk_queue_make_request(stackbd.queue, stackbd_make_request); 265 | blk_queue_logical_block_size(stackbd.queue, LOGICAL_BLOCK_SIZE); 266 | 267 | /* Get registered */ 268 | if ((major_num = register_blkdev(major_num, STACKBD_NAME)) < 0) 269 | { 270 | printk("stackbd: unable to get major number\n"); 271 | goto error_after_alloc_queue; 272 | } 273 | 274 | /* Gendisk structure */ 275 | if (!(stackbd.gd = alloc_disk(16))) 276 | goto error_after_redister_blkdev; 277 | stackbd.gd->major = major_num; 278 | stackbd.gd->first_minor = 0; 279 | stackbd.gd->fops = &stackbd_ops; 280 | stackbd.gd->private_data = &stackbd; 281 | strcpy(stackbd.gd->disk_name, STACKBD_NAME_0); 282 | stackbd.gd->queue = stackbd.queue; 283 | add_disk(stackbd.gd); 284 | 285 | printk("stackbd: init done\n"); 286 | 287 | return 0; 288 | 289 | error_after_redister_blkdev: 290 | unregister_blkdev(major_num, STACKBD_NAME); 291 | error_after_alloc_queue: 292 | blk_cleanup_queue(stackbd.queue); 293 | 294 | return -EFAULT; 295 | } 296 | 297 | static void __exit stackbd_exit(void) 298 | { 299 | printk("stackbd: exit\n"); 300 | 301 | if (stackbd.is_active) 302 | { 303 | kthread_stop(stackbd.thread); 304 | blkdev_put(stackbd.bdev_raw, STACKBD_BDEV_MODE); 305 | bdput(stackbd. bdev_raw); 306 | } 307 | 308 | del_gendisk(stackbd.gd); 309 | put_disk(stackbd.gd); 310 | unregister_blkdev(major_num, STACKBD_NAME); 311 | blk_cleanup_queue(stackbd.queue); 312 | } 313 | 314 | module_init(stackbd_init); 315 | module_exit(stackbd_exit); 316 | -------------------------------------------------------------------------------- /util/Makefile: -------------------------------------------------------------------------------- 1 | 2 | stackbd_util: stackbd_util.o ../common/stackbd.h 3 | gcc -o stackbd_util stackbd_util.c 4 | 5 | clean: 6 | rm stackbd_util.o stackbd_util 7 | -------------------------------------------------------------------------------- /util/stackbd_util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../common/stackbd.h" 8 | 9 | int main(int argc, char *argv[]) 10 | { 11 | int stackbd; 12 | 13 | if (argc < 2) 14 | { 15 | printf("Usage:\n\t%s /dev/\n", argv[0]); 16 | return 1; 17 | } 18 | 19 | if ((stackbd = open("/dev/" STACKBD_NAME_0, O_RDWR)) < 0) 20 | { 21 | printf("error: open /dev/%s: %m", STACKBD_NAME_0); 22 | return stackbd; 23 | } 24 | 25 | printf("do it... <%s>\n", argv[1]); 26 | if (ioctl(stackbd, STACKBD_DO_IT, argv[1]) < 0) 27 | { 28 | fprintf(stderr, "Kernel call returned: %m"); 29 | return 1; 30 | } 31 | printf("OK\n"); 32 | 33 | return 0; 34 | } 35 | --------------------------------------------------------------------------------