首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >基于DOAS文件系统接口(DFS)暴露的SPDK块设备

基于DOAS文件系统接口(DFS)暴露的SPDK块设备

原创
作者头像
晓兵
修改2023-09-05 08:07:35
修改2023-09-05 08:07:35
1K0
举报
文章被收录于专栏:daosdaos

整体架构

步骤

  1. 编译daos, 记录daos安装目录, 比如/opt/daos, 启动daos_agent, daos_engine, daos_server
  2. 编译spdk

git clone https://github.com/spdk/spdk.git

git submodule update --init

./configure --with-daos #指定daos include目录和lib目录

make -j 16

  1. 启动spdk nvmf_tgt sudo HUGE_EVEN_ALLOC=yes scripts/setup.sh

sudo ./build/bin/nvmf_tgt -m 21,22,23,24

  1. 创建传输层
代码语言:txt
复制
sudo ./scripts/rpc.py nvmf_create_transport -t TCP -u 2097152 -i 2097152
  1. 创建块设备: ./scripts/rpc.py bdev_daos_create daosdev0 test-pool test-cont 64 4096 # 4K * 64 = 256KB -> |4K|4K|...|
  2. 创建子系统: ./scripts/rpc.py nvmfcreate_subsystem $subsystem -a -s SPDK0000000000000$i -d SPDK_Virtual_Controller$i
  3. 子系统添加命名空间: ./scripts/rpc.py nvmf_subsystem_add_ns $subsystem disk$i
  4. 添加监听: scripts/rpc.py nvmf_subsystem_add_listener $subsystem -t tcp -a ${BIND_IP} -s 4420
  5. nvme客户端连接: nvme connect-all -t tcp -a 172.31.91.61 -s 4420, 得到块设备后, 格式化即可使用, 如: mkfs.ext4 -F -O mmp /dev/nvme1n1

在设计方面,此 bdev 是一个名为 bdev 本身的文件,位于 DAOS POSIX 容器中,每个 io 通道使用 daos 事件队列。 每个 io 通道都有一个事件队列来支撑最佳 IO 吞吐量。 该实现使用每个设备通道的独立池和容器连接以获得最佳 IO 吞吐量

关键函数

bdev_daos_create

代码语言:txt
复制
bdev_get_daos_engine 初始化daos引擎
代码语言:txt
复制
bdev_daos_io_channel_create_cb 用创建通道的方式检测容器连通性, 如果在通道创建过程中,由于参数不正确而发生错误,例如: 池/容器名称错误,或其他一些内部 DAOS 错误(如达到 CART 上下文限制),bdev_daos_io_channel_create_cb() 会发出有关此类错误的信号,但是,spdk_io_device_register() 不会将它们考虑在内。 设备创建成功,返回成功的 RPC 响应并将 bdev 留在 bdev 列表中,但它完全无法使用且不可修改, 尝试在创建通道的时候连接到DAOS容器,所以在这里模拟创建一个通道,这样我们就可以在创建DAOS bdev的时候返回一个失败,而不是等到第一个通道创建的时候才发现,留下不可用的bdev注册
代码语言:txt
复制
spdk_io_device_register 将不透明的 io_device 上下文注册为 I/O 设备。 I/O设备注册后,可以使用spdk_get_io_channel()函数返回I/O通道
代码语言:txt
复制
spdk_bdev_register 注册一个新的 bdev。 必须从 SPDK APP线程调用此函数
代码语言:c
复制
大致流程
./scripts/rpc.py bdev_daos_create daosdev0 test-pool test-cont 64 4096  -> from spdk.rpc.client -> 4K * 64 = 256KB -> |4K|4K|...|
SPDK_RPC_REGISTER("bdev_daos_create", rpc_bdev_daos_create, SPDK_RPC_RUNTIME)
rpc_bdev_daos_create -> module/bdev/daos/bdev_daos_rpc.c
  create_bdev_daos
    block_size % 512 512对齐
    daos->disk.fn_table = &daos_fn_table -> static const struct spdk_bdev_fn_table daos_fn_table -> 提交IO请求 -> bdev_daos_submit_request -> ... -> dfs_write ->  src/client/dfs/dfs.c -> daos_array_write
    bdev_get_daos_engine
      daos_init()
    bdev_daos_io_channel_create_cb
      spdk_call_unaffinitized(_bdev_daos_io_channel_create_cb, ch)
        rte_thread_get_affinity(&orig_cpuset)
        spdk_unaffinitize_thread() 移除cpu亲和性
          CPU_ZERO(&new_cpuset)
        _bdev_daos_io_channel_create_cb
          bdev_get_daos_engine
          daos_pool_connect
          daos_cont_open
          dfs_mount(ch->pool, ch->cont, O_RDWR, &ch->dfs)
          dfs_open
          daos_eq_create
        rte_thread_set_affinity(&orig_cpuset) 将cpu亲和性设回去
      ch->poller = SPDK_POLLER_REGISTER(bdev_daos_channel_poll, ch, 0)
    bdev_daos_io_channel_destroy_cb
      spdk_poller_unregister
      daos_eq_destroy
      dfs_release
      dfs_umount
      daos_cont_close
      daos_pool_disconnect
      bdev_daos_put_engine
    spdk_io_device_register(daos, bdev_daos_io_channel_create_cb, bdev_daos_io_channel_destroy_cb -> lib/thread/thread.c
      thread = spdk_get_thread() 此外,在注册 io 设备之前将 UT 更改为 set_thread()
      dev->create_cb = create_cb = bdev_daos_io_channel_create_cb
      dev->destroy_cb = destroy_cb
      tmp = RB_INSERT(io_device_tree, &g_io_devices, dev) -> io_device_tree_RB_INSERT(&g_io_devices, dev) -> 将IO设备对象插入红黑树(g_io_devices全局红黑树头) -> 利用宏生成红黑树插入函数: #define RB_GENERATE_INSERT -> name##_RB_INSERT
    spdk_bdev_register -> lib/bdev/bdev.c -> spdk_bdev_register(struct spdk_bdev *bdev)
      bdev_register(bdev) -> lib/bdev/bdev.c -> bdev_register(struct spdk_bdev *bdev)
        spdk_bdev_get_memory_domains 获取给定 bdev 使用的 SPDK 内存域。 如果 bdev 报告它使用内存域,这意味着它可以使用位于这些内存域中的数据缓冲区。 用户可以调用此函数并将 domains 设置为 NULL 并将 array_size 设置为 0 以获取 bdev 使用的内存域数
        spdk_bdev_is_md_separate 查询元数据是否与块数据交织或与块数据分离
        bdev_alloc_io_stat
        bdev_name_add
        spdk_bdev_get_buf_align
        spdk_io_device_register 将不透明的 io_device 上下文注册为 I/O 设备。 I/O设备注册后,可以使用spdk_get_io_channel()函数返回I/O通道, 许多 bdev 模块创建自己的 bdev 结构,其中 spdk_bdev 作为第一个成员。 bdev.c 当前使用 spdk_bdev 指针作为其 io_device 句柄,强制 bdev 模块选择不同的东西。将其更改为使用 spdk_bdev 指针 + 1 个字节作为其 io_device 句柄。 实际句柄并不重要——它只需要是独一无二的。 这将简化 bdev 模块开发
        TAILQ_INSERT_TAIL(&g_bdev_mgr.bdevs, bdev, internal.link) 上链表
      bdev_open 注册 bdev 时,bdev 模块会在通知 bdev 寄存器之前对其进行检查。检查可能是异步的,例如 当 bdev 模块必须在新的 bdev 上执行 I/O 时。这会导致竞争条件,其中 bdev 可能会在检查未完成时被破坏。 然后,一旦所有模块都发出检查已完成的信号,`bdev_register_finished` 将对释放的 bdev 指针进行无效访问。要解决此问题,请推迟取消注册,直到通过打开 bdev 上的描述符完成检查
      bdev_examine
      spdk_bdev_wait_for_examine(bdev_register_finished, desc) 当所有 bdev 完成检查过程时报告。 注册的 cb_fn 只会被调用一次。 需要再次调用此函数以接收有关检查过程的进一步报告
    *bdev = &(daos->disk)

关键数据结构

struct bdev_daos *daos;

struct bdev_daos_io_channel ch = {};

注意点

默认容器类型为: OC_SX (oclass SX 保证IOPS优先, 该参数用于数据冗余和保护)

思考

  1. daos bdev优点, 支持rdma和全闪nvme介质, 将daos后端存储能力通过通用的块暴露给应用
  2. 编程思想: 用最少的成本, 尽快返回RPC错误并退出; 分层解耦思想; 独立通道(优先级通道); 用户层尽量开箱即用,复用原来的接口, 降低学习成本

参考

https://spdk.io/doc/bdev.html

https://github.com/ssbandjl/spdk/commit/2e283fcb67a8ee1d9b4f470f17bec57bbe3adad5

https://docs.daos.io/v2.3/user/blockdev/

https://www.cnblogs.com/whl320124/articles/10064186.html

https://rootw.github.io/2018/05/SPDK-subsys-bdev/

DAOS项目简介视频

晓兵

博客: https://logread.cn | https://blog.csdn.net/ssbandjl | https://cloud.tencent.com/developer/user/5060293/articles

公众号: 云原生云

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 整体架构
  • 关键函数
  • 关键数据结构
  • 注意点
  • 思考
  • 参考
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档