kernel 4.0 块操作

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/slab.h>        
#include <linux/fs.h>
#include <linux/hdreg.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/bio.h>

static int vmem_disk_major;
module_param(vmem_disk_major, int, 0);

#define HARDSECT_SIZE 512
#define NSECTORS 1024
#define NDEVICES 4

enum {
    VMEMD_QUEUE  = 0, /* Use request_queue */
    VMEMD_NOQUEUE = 1, /* Use make_request */
};
static int request_mode = VMEMD_QUEUE;
module_param(request_mode, int, 0);


#define VMEM_DISK_MINORS    16
#define KERNEL_SECTOR_SIZE  512

struct vmem_disk_dev {
    int size;                       /* Device size in sectors */
    u8 *data;                       /* The data array */
    spinlock_t lock;                /* For mutual exclusion */
    struct request_queue *queue;    /* The device request queue */
    struct gendisk *gd;             /* The gendisk structure */
};

static struct vmem_disk_dev *devices;

/*
 * Handle an I/O request.
 */
static void vmem_disk_transfer(struct vmem_disk_dev *dev, unsigned long sector,
        unsigned long nsect, char *buffer, int write)
{
    unsigned long offset = sector*KERNEL_SECTOR_SIZE;
    unsigned long nbytes = nsect*KERNEL_SECTOR_SIZE;

    if ((offset + nbytes) > dev->size) {
        printk (KERN_NOTICE "Beyond-end write (%ld %ld)\n", offset, nbytes);
        return;
    }
    if (write)
        memcpy(dev->data + offset, buffer, nbytes);
    else
        memcpy(buffer, dev->data + offset, nbytes);
}

/*
 * Transfer a single BIO.
 */
static int vmem_disk_xfer_bio(struct vmem_disk_dev *dev, struct bio *bio)
{
        struct bio_vec bvec;
        struct bvec_iter iter;
        sector_t sector = bio->bi_iter.bi_sector;

    bio_for_each_segment(bvec, bio, iter) {
        char *buffer = __bio_kmap_atomic(bio, iter);
        vmem_disk_transfer(dev, sector, bio_cur_bytes(bio) >> 9,
            buffer, bio_data_dir(bio) == WRITE);
        sector += bio_cur_bytes(bio) >> 9;
        __bio_kunmap_atomic(buffer);
    }
    return 0;
}

/*
 * The request_queue version.
 */
static void vmem_disk_request(struct request_queue *q)
{
    struct request *req;
    struct bio *bio;

    while ((req = blk_peek_request(q)) != NULL) {
        struct vmem_disk_dev *dev = req->rq_disk->private_data;
        if (req->cmd_type != REQ_TYPE_FS) {
            printk (KERN_NOTICE "Skip non-fs request\n");
            blk_start_request(req);
            __blk_end_request_all(req, -EIO);
            continue;
        }

        blk_start_request(req);
        __rq_for_each_bio(bio, req)
            vmem_disk_xfer_bio(dev, bio);
        __blk_end_request_all(req, 0);
    }
}


/*
 * The direct make request version.
 */
static void vmem_disk_make_request(struct request_queue *q, struct bio *bio)
{
    struct vmem_disk_dev *dev = q->queuedata;
    int status;

    status = vmem_disk_xfer_bio(dev, bio);
    bio_endio(bio, status);
}

static int vmem_disk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
    long size;
    struct vmem_disk_dev *dev = bdev->bd_disk->private_data;

    size = dev->size*(HARDSECT_SIZE/KERNEL_SECTOR_SIZE);
    geo->cylinders = (size & ~0x3f) >> 6;
    geo->heads = 4;
    geo->sectors = 16;
    geo->start = 4;

    return 0;
}

/*
 * The device operations structure.
 */
static struct block_device_operations vmem_disk_ops = {
    .getgeo          = vmem_disk_getgeo,
};

static void setup_device(struct vmem_disk_dev *dev, int which)
{
    memset (dev, 0, sizeof (struct vmem_disk_dev));
    dev->size = NSECTORS*HARDSECT_SIZE;
    dev->data = vmalloc(dev->size);
    if (dev->data == NULL) {
        printk (KERN_NOTICE "vmalloc failure.\n");
        return;
    }
    spin_lock_init(&dev->lock);

    /*
     * The I/O queue, depending on whether we are using our own
     * make_request function or not.
     */
    switch (request_mode) {
    case VMEMD_NOQUEUE:
        dev->queue = blk_alloc_queue(GFP_KERNEL);
        if (dev->queue == NULL)
            goto out_vfree;
        blk_queue_make_request(dev->queue, vmem_disk_make_request);
        break;
    default:
        printk(KERN_NOTICE "Bad request mode %d, using simple\n", request_mode);
    case VMEMD_QUEUE:
        dev->queue = blk_init_queue(vmem_disk_request, &dev->lock);
        if (dev->queue == NULL)
            goto out_vfree;
        break;
    }
    blk_queue_logical_block_size(dev->queue, HARDSECT_SIZE);
    dev->queue->queuedata = dev;

    dev->gd = alloc_disk(VMEM_DISK_MINORS);
    if (!dev->gd) {
        printk (KERN_NOTICE "alloc_disk failure\n");
        goto out_vfree;
    }
    dev->gd->major = vmem_disk_major;
    dev->gd->first_minor = which*VMEM_DISK_MINORS;
    dev->gd->fops = &vmem_disk_ops;
    dev->gd->queue = dev->queue;
    dev->gd->private_data = dev;
    snprintf (dev->gd->disk_name, 32, "vmem_disk%c", which + 'a');
    set_capacity(dev->gd, NSECTORS*(HARDSECT_SIZE/KERNEL_SECTOR_SIZE));
    add_disk(dev->gd);
    return;

out_vfree:
    if (dev->data)
        vfree(dev->data);
}


static int __init vmem_disk_init(void)
{
    int i;

    vmem_disk_major = register_blkdev(vmem_disk_major, "vmem_disk");
    if (vmem_disk_major <= 0) {
        printk(KERN_WARNING "vmem_disk: unable to get major number\n");
        return -EBUSY;
    }

    devices = kmalloc(NDEVICES*sizeof (struct vmem_disk_dev), GFP_KERNEL);
    if (!devices)
        goto out_unregister;
    for (i = 0; i < NDEVICES; i++)
        setup_device(devices + i, i);

    return 0;

out_unregister:
    unregister_blkdev(vmem_disk_major, "sbd");
    return -ENOMEM;
}
module_init(vmem_disk_init);

static void vmem_disk_exit(void)
{
    int i;

    for (i = 0; i < NDEVICES; i++) {
        struct vmem_disk_dev *dev = devices + i;

        if (dev->gd) {
            del_gendisk(dev->gd);
            put_disk(dev->gd);
        }
        if (dev->queue) {
            if (request_mode == VMEMD_NOQUEUE)
                kobject_put (&dev->queue->kobj);
            else
                blk_cleanup_queue(dev->queue);
        }
        if (dev->data)
            vfree(dev->data);
    }
    unregister_blkdev(vmem_disk_major, "vmem_disk");
    kfree(devices);
}
module_exit(vmem_disk_exit);

MODULE_AUTHOR("Barry Song <baohua@kernel.org>");
MODULE_LICENSE("GPLv2");
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值