【块存储block源码分析】 linux内核模块ceph nbd源码分析

     jewel+ 版本支持 rbd-nbd 的特性,需要 map 支持较多特性的 rbd image 时,可以使用该 nbd driver

     NBD(Network Block Device) 可以将一个远程主机的磁盘空间,当作一个块设备来使用,就像一块硬盘一样。NBD是一个内核模块,大部分Linux发行版都包含它

     与 RBD 内核驱动程序相比,NBD有许多优点:

  • RBD-KO开发和特性的添加必须要经过稳定的内核
  • RBD-KO开发慢于librbd,如果要与librbd开发保持同步,需要时间和努力
  • NBD 已经很好地集成到内核多年,是内核的一部分

Linux块设备驱动知识

(1)当一个用户程序要向磁盘写入数据时,会发发出write()系统调用给内核。

(2)内核会调用虚拟文件系统相应的函数,将需要写入发文件描述符和文件内容指针传递给该函数。

(3)内核需要确定写入磁盘的位置,通过映射层知道需要写入磁盘的哪一块。

(4)根据磁盘的文件系统的类型,调用不同文件格式的写入函数,江苏数据发送给通用块层(比如ext2和ext3文件系统的写入函数是不同的,这些函数由内核开发者实现,驱动开发者不用实现这类函数)

(5)数据到达通用块层后,就对块设备发出写请求。内核利用通用块层的启动I/O调度器,对数据进行排序。

(6)同用块层下面是"I/O调度器"。调度器作用是把物理上相邻的读写合并在一起,这样可以加快访问速度。

(7)最后快设备驱动向磁盘发送指令和数据,将数据写入磁盘。

    参考: https://www.cnblogs.com/big-devil/p/8590007.html

结构体 nbd_device

    关注队列,gendisk 

struct nbd_device {
        int flags;
        int harderror;          /* Code of hard error                   */
        struct socket * sock;   /* If == NULL, device is not ready, yet */
        int magic;

        spinlock_t queue_lock;
        struct list_head queue_head;    /* Requests waiting result */
        struct request *active_req;
        wait_queue_head_t active_wq;
        struct list_head waiting_queue; /* Requests to be sent */
        wait_queue_head_t waiting_wq;

        struct mutex tx_lock;
        struct gendisk *disk;
        int blksize;
        u64 bytesize;
        pid_t pid; /* pid of nbd-client, if attached */
        int xmit_timeout;
        int disconnect; /* a disconnect has been requested by user */
};

1. nbd_init

    1.1 kcalloc 分配物理内存与高端内存映射

      可以获取以字节为单位的一块内核内存,GFP_KERNEL flag 一种常规的分配方式, 可能会阻塞. 这个标志在睡眠安全时用在进程的长下文代码中. 为了获取调用者所需的内存, 内核会尽力而为. 这个标志应该是首选标志

    1.2 alloc_disk 函数分配通用磁盘gendisk的结构体

    1.3 blk_init_queue 分配请求队列,并初始化

request_queue_t *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock)

第一个参数是指向"请求处理函数"的指针,该函数直接和硬盘打交道,用来处理数据在内存和硬盘之间的传输。

    1.4 register_blkdev 注册块设备

       作用:分局major分配一个块设备号,在/proc/devices中新增加一行数据,表示块设备的信息

#define NBD_MAJOR        43   /* Network block device    */

     初始化gendisk结构体的数据成员,包括major,fops,queue 等赋初值

for (i = 0; i < nbds_max; i++) {
        struct gendisk *disk = nbd_dev[i].disk;
        nbd_dev[i].magic = NBD_MAGIC;
        INIT_LIST_HEAD(&nbd_dev[i].waiting_queue);
        spin_lock_init(&nbd_dev[i].queue_lock);
        INIT_LIST_HEAD(&nbd_dev[i].queue_head);
        mutex_init(&nbd_dev[i].tx_lock);
        init_waitqueue_head(&nbd_dev[i].active_wq);
        init_waitqueue_head(&nbd_dev[i].waiting_wq);
        nbd_dev[i].blksize = 1024;
        nbd_dev[i].bytesize = 0;
        disk->major = NBD_MAJOR;
        disk->first_minor = i << part_shift;
        disk->fops = &nbd_fops;
        disk->private_data = &nbd_dev[i];
        sprintf(disk->disk_name, "nbd%d", i);
        set_capacity(disk, 0);
        add_disk(disk);
}

     1.5 add_disk 函数激活磁盘设备

      (当调用该函数后就可以对磁盘进行操作(访问),所以调用该函数之前必须所有的准备工作就绪)

 

      nbd_init 总结:

  参考:https://cloud.tencent.com/developer/article/1380637

2. nbd_fops 注册

      根据 disk->fops = &nbd_fops;,可以看到只关注 ioctl 函数

static const struct block_device_operations nbd_fops =
{
        .owner =        THIS_MODULE,
        .ioctl =        nbd_ioctl,
};

    2.1 nbd_ioctl 函数

       从nbd server 端收到调用 ioctl,实现在 ceph 代码中的 rbd-nbd,直接关键函数 __nbd_ioctl

static int nbd_ioctl(struct block_device *bdev, fmode_t mode,
                     unsigned int cmd, unsigned long arg)
{
        error = __nbd_ioctl(bdev, nbd, cmd, arg);
}

3. __nbd_ioctl 函数

static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd,
                       unsigned int cmd, unsigned long arg)
{

    根据 cmd 区分不同的操作类型

    3.1 NBD_DO_IT

      3.1.1 kthread_create

   在后台执行一些操作,这种任务就可以通过内核线程(kernle thread)完成独立运行在内核空间的标准进程。内核线程和普通的进程间的区别在于内核线程没有独立的地址空间,mm 指针被设置为 NULL;它只在内核空间运行,从来不切换到用户空间去

      3.1.2  wake_up_process(thread)

   线程创建后,不会马上运行,而是需要将kthread_create() 返回的task_struct指针传给wake_up_process(),然后通过此函数运行线程。        

      3.1.3 nbd_do_it

4. do_nbd_request 函数

参考:

    https://blog.sourcerer.io/writing-a-simple-linux-kernel-module-d9dc3762c234

    http://derekmolloy.ie/writing-a-linux-kernel-module-part-1-introduction/

    http://en.tldp.org/LDP/lkmpg/2.6/lkmpg.pdf

发布了236 篇原创文章 · 获赞 301 · 访问量 38万+

猜你喜欢

转载自blog.csdn.net/zhonglinzhang/article/details/103815508
今日推荐