Linux nbd 网络块设备(2)-内核实现
Linux nbd网络块设备(2)-内核实现
关注我,一起学习吧,后续持续更新内核相关
1. 概述:
- 内核linux/drivers/block/nbd.c 是nbd 网络设备的底层驱动实现逻辑。
- 本文主要介绍nbd 设备注册及I/O请求的处理逻辑。
2. nbd 设备的初始化:
-
disk 注册到内核后会产生/dev/nbdx 节点,后续nbd_client 可以通过该节点开始nbd 块设备传输;
-
设置tag_set.ops操作函数:
nbd->tag_set.ops = &nbd_mq_ops; static const struct blk_mq_ops nbd_mq_ops = {.queue_rq = nbd_queue_rq,.complete = nbd_complete_rq,.init_request = nbd_init_request,.timeout = nbd_xmit_timeout, };
-
设置disk 的操作函数:
- 该函数集主要用于nbd_client 打开nbd 设备,执行ioctl 指令,开始块传输等操作;
disk->fops = &nbd_fops; static const struct block_device_operations nbd_fops = {.owner = THIS_MODULE,.open = nbd_open,.release = nbd_release,.ioctl = nbd_ioctl,.compat_ioctl = nbd_ioctl,.free_disk = nbd_free_disk, };
-
nbd 与tag_set 的关联:
-
tag_set 是nbd的成员
-
tag_set的driver_data 指向该nbd
nbd->tag_set nbd->tag_set.driver_data=nbd
-
-
nbd 与disk的关联:
- disk 基于tag_set 分配并初始化
- disk 是nbd 的成员
- disk的private_data 指向该nbd
disk = blk_mq_alloc_disk(&nbd->tag_set, NULL); nbd->disk = disk; disk->private_data = nbd;
3. nbd 设备实例创建流程:
- nbd_fops 是操作disk 时的相关处理函数,执行nbd_open时会实例化config
- 所有的ioctl cmd 均通过nbd_ioctl 进行处理,主要设置config 相关属性
- 执行完NBD_DO_IT 后,nbd 会更新硬件队列并将recv_work 提交工作队列
- sock 存储关系:
-
可以通config→socks[connection_number]→sock 访问到链接nbd_client 建立的sock,基于该sock 读取nbd_client 的数据或发送相关cmd
//nbd.c nbd_add_socket() sock = nbd_get_socket(nbd, arg, &err); nsock->sock = sock; socks[config->num_connections++] = nsock; config->socks = socks;
-
3. nbd I/O 传输流程:
- 通过sock 将块层请求发送给服务端
- 以virtual-media 为例,将读取镜像的请求通过sock 发送给远程服务端
- 通过sock 读取来着服务端的响应及响应数据,然后存储到bio_vec 指定内存。供更底层设备获取。
3.1 nbd 块层请求及数据发送:
- 以virtual-meida 举例,USB层会将USB的请求转换为块请求,并通过nbd_queue_rq 将请求数据通过sock 发给服务端。
3.2 nbd 接收处理:
- recv_work 触发:
- nbd 模组初始化执行nbd_dev_add 的时候,会创建recv_workq接收工作队列;
- nbd 响应NBD_DO_IT ioctl cmd 执行nbd_start_device(nbd);时,将recv_work 提交到recv_workq,内核调度会执行recv_workq的工作线程,并执行recv_work.
- 在recv_work 中主要通过socket读取服务端replay 并读取对应数据到bio_vec