spdk 入门 hello world源码解析

SPDK NVMe SSD 探测与基本操作

hello_world.c main方法源码如下

int main(int argc, char **argv)
{
   int rc;
   struct spdk_env_opts opts;

   /*
    * SPDK relies on an abstraction around the local environment
    * named env that handles memory allocation and PCI device operations.
    * This library must be initialized first.
    *
    */
   spdk_env_opts_init(&opts);
   rc = parse_args(argc, argv, &opts);
   if (rc != 0) {
      return rc;
   }

   opts.name = "hello_world";
   if (spdk_env_init(&opts) < 0) {
      fprintf(stderr, "Unable to initialize SPDK env\n");
      return 1;
   }

   printf("Initializing NVMe Controllers\n");

   if (g_vmd && spdk_vmd_init()) {
      fprintf(stderr, "Failed to initialize VMD."
         " Some NVMe devices can be unavailable.\n");
   }

   /*
    * Start the SPDK NVMe enumeration process.  probe_cb will be called
    *  for each NVMe controller found, giving our application a choice on
    *  whether to attach to each controller.  attach_cb will then be
    *  called for each controller after the SPDK NVMe driver has completed
    *  initializing the controller we chose to attach.
    */
   rc = spdk_nvme_probe(&g_trid, NULL, probe_cb, attach_cb, NULL);
   if (rc != 0) {
      fprintf(stderr, "spdk_nvme_probe() failed\n");
      rc = 1;
      goto exit;
   }

   if (TAILQ_EMPTY(&g_controllers)) {
      fprintf(stderr, "no NVMe controllers found\n");
      rc = 1;
      goto exit;
   }

   printf("Initialization complete.\n");
   hello_world();
   cleanup();
   if (g_vmd) {
      spdk_vmd_fini();
   }

exit:
   cleanup();
   spdk_env_fini();
   return rc;
}

main()的处理流程为:

  1. 源码317行,上文12行 spdk_env_opts_init(&opts);
  2. 源码320行,上文19行 spdk_env_init(&opts);
  3. 源码331行,上文38行 rc = spdk_nvme_probe(NULL, NULL, probe_cb, attach_cb, NULL);
  4. 源码345行,上文52行 hello_world();
  5. 源码346行,上文59行 cleanup();

步骤1-步骤2是spdk运行环境初始化

步骤3是调用函数spdk_nvme_probe()主动发现NVMe SSDs设备

步骤4是调用hello_world(),做简单的读写操作

步骤5调用cleanup()释放内存资源,detach NVMe SSD设备等

接下来分析下关键函数spdk_nvme_probe()

分析之前先搞清楚下面两个问题:

1、每一块NVMe SSD里都有一个控制器(Controller),那么发现的所有NVMe SSD(也就是NVMe Controllers)以什么方式组织在一起?

2、每一块NVMe SSD都可以划分为多个namespace(类似逻辑分区的概念),那么这些namespace以什么方式组织在一起呢?

答案:

链表结构,看下hello_world.c代码

struct ctrlr_entry {
   struct spdk_nvme_ctrlr    *ctrlr;
   TAILQ_ENTRY(ctrlr_entry)   link;
   char            name[1024];
};

struct ns_entry {
   struct spdk_nvme_ctrlr *ctrlr;
   struct spdk_nvme_ns    *ns;
   TAILQ_ENTRY(ns_entry)  link;
   struct spdk_nvme_qpair *qpair;
};

static TAILQ_HEAD(, ctrlr_entry) g_controllers = TAILQ_HEAD_INITIALIZER(g_controllers);
static TAILQ_HEAD(, ns_entry) g_namespaces = TAILQ_HEAD_INITIALIZER(g_namespaces);

上面的

g_controllers是管理所有的nvme ssd的全局链表头

g_namespaces是管理所有的namespace的全局链表头

所以main函数里面的

if (TAILQ_EMPTY(&g_controllers)) {
   fprintf(stderr, "no NVMe controllers found\n");
   rc = 1;
   goto exit;
}

就是因为g_controllers为空,就是因为没有找到nvme sdd

接下来看下main函数里面是如何使用spdk_nvme_probe()的

rc = spdk_nvme_probe(&g_trid, NULL, probe_cb, attach_cb, NULL);

如上 probe_cb 和 attach_cb是两个回调函数(其实还有个remove_cb,只是上面未使用,传了个NULL)

probe_cb:当发现一个nvme设备的时候被调用

attach_cb:当一个nvme设备已经被attach到一个用户态的nvme驱动的时候被调用

probe_cb, attach_cb以及remove_cb的相关源码信息在spdk/include/spdk/nvme.h

/**
 * Enumerate the bus indicated by the transport ID and attach the userspace NVMe
 * driver to each device found if desired.
 *
 * This function is not thread safe and should only be called from one thread at
 * a time while no other threads are actively using any NVMe devices.
 *
 * If called from a secondary process, only devices that have been attached to
 * the userspace driver in the primary process will be probed.
 *
 * If called more than once, only devices that are not already attached to the
 * SPDK NVMe driver will be reported.
 *
 * To stop using the the controller and release its associated resources,
 * call spdk_nvme_detach() with the spdk_nvme_ctrlr instance from the attach_cb()
 * function.
 *
 * \param trid The transport ID indicating which bus to enumerate. If the trtype
 * is PCIe or trid is NULL, this will scan the local PCIe bus. If the trtype is
 * RDMA, the traddr and trsvcid must point at the location of an NVMe-oF discovery
 * service.
 * \param cb_ctx Opaque value which will be passed back in cb_ctx parameter of
 * the callbacks.
 * \param probe_cb will be called once per NVMe device found in the system.
 * \param attach_cb will be called for devices for which probe_cb returned true
 * once that NVMe controller has been attached to the userspace driver.
 * \param remove_cb will be called for devices that were attached in a previous
 * spdk_nvme_probe() call but are no longer attached to the system. Optional;
 * specify NULL if removal notices are not desired.
 *
 * \return 0 on success, -1 on failure.
 */
int spdk_nvme_probe(const struct spdk_nvme_transport_id *trid,
          void *cb_ctx,
          spdk_nvme_probe_cb probe_cb,
          spdk_nvme_attach_cb attach_cb,
          spdk_nvme_remove_cb remove_cb);
          
          
/**
 * Callback for spdk_nvme_probe() enumeration.
 *
 * \param cb_ctx Opaque value passed to spdk_nvme_probe().
 * \param trid NVMe transport identifier.
 * \param opts NVMe controller initialization options. This structure will be
 * populated with the default values on entry, and the user callback may update
 * any options to request a different value. The controller may not support all
 * requested parameters, so the final values will be provided during the attach
 * callback.
 *
 * \return true to attach to this device.
 */
typedef bool (*spdk_nvme_probe_cb)(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
               struct spdk_nvme_ctrlr_opts *opts);

/**
 * Callback for spdk_nvme_attach() to report a device that has been attached to
 * the userspace NVMe driver.
 *
 * \param cb_ctx Opaque value passed to spdk_nvme_attach_cb().
 * \param trid NVMe transport identifier.
 * \param ctrlr Opaque handle to NVMe controller.
 * \param opts NVMe controller initialization options that were actually used.
 * Options may differ from the requested options from the attach call depending
 * on what the controller supports.
 */
typedef void (*spdk_nvme_attach_cb)(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
                struct spdk_nvme_ctrlr *ctrlr,
                const struct spdk_nvme_ctrlr_opts *opts);

/**
 * Callback for spdk_nvme_remove() to report that a device attached to the userspace
 * NVMe driver has been removed from the system.
 *
 * The controller will remain in a failed state (any new I/O submitted will fail).
 *
 * The controller must be detached from the userspace driver by calling spdk_nvme_detach()
 * once the controller is no longer in use. It is up to the library user to ensure
 * that no other threads are using the controller before calling spdk_nvme_detach().
 *
 * \param cb_ctx Opaque value passed to spdk_nvme_remove_cb().
 * \param ctrlr NVMe controller instance that was removed.
 */
typedef void (*spdk_nvme_remove_cb)(void *cb_ctx, struct spdk_nvme_ctrlr *ctrlr);

 接下来看下struct spdk_nvme_transport_id

/**
 * NVMe transport identifier.
 *
 * This identifies a unique endpoint on an NVMe fabric.
 *
 * A string representation of a transport ID may be 
SPDK存储性能开发套件)官方文档中文版。 第一章 简介 1 1.1.什么是SPDK? 1 1.2.入门 1 1.3. Vagrant开发环境 3 1.4.更新日志(略) 6 第二章 概念 6 2.1. 用户空间驱动程序** 6 2.2. 来自用户空间的DMA** 7 2.3. 消息传递和并发** 9 2.4. NAND Flash SSD内部 13 2.5. 将I / O提交到NVMe设备** 15 2.5.1 NVMe规范 15 2.5.2 SPDK NVMe驱动程序I / O路径 15 2.6. 使用Vhost-user进行虚拟化I / O. 16 2.6.1 介绍 16 2.6.2 QEMU 17 2.6.3 设备初始化 18 2.6.4 I / O路径 19 2.6.5 SPDK优化 20 2.7. SPDK目录结构概述 20 2.8. SPDK移植指南 22 第三章 用户指南 22 3.1. 系统配置用户指南 22 3.1.1 IOMMU配置 22 3.2. SPDK应用程序概述 23 3.2.1 配置SPDK应用程序 23 3.3. iSCSI Target 26 3.3.1. iSCSI Target入门指南 26 3.3.2. 通过配置文件配置iSCSI Target 27 3.3.3. 通过RPC方法配置iSCSI Target 28 3.3.4. 配置iSCSI启动器 29 3.3.5. rpc配置示例*** 30 3.3.6. iSCSI 热插拔 32 3.4. NVMe over Fabrics Target 32 3.5. Vhost Target(略) 37 3.6 块设备用户指南 38 3.6.1 bdev介绍 38 3.6.2 通用RPC命令 38 3.6.3 Ceph RBD 39 3.6.4 压缩虚拟Bdev模块 40 3.6.5 加密虚拟Bdev模块 41 3.6.6 延迟vbdev模块 41 3.6.7 GPT(GUID分区表) 42 3.6.8 iSCSI bdev 43 3.6.9 Linux AIO bdev 43 3.6.10 OCF虚拟bdev 43 3.6.11 Malloc bdev 44 3.6.12 NULL bdev 44 3.6.13 NVMe bdev 44 3.6.14 逻辑卷Lvol 45 3.6.15 RAID 46 3.6.16 Passthru 46 3.6.17 Pmem 46 3.6.18 Virtio Block 47 3.6.19 Virtio SCSI 47 3.7 BlobFS(Blobstore文件系统) 48 3.7.1 RocksDB集成 48 3.7.2 FUSE插件 49 3.8 JSON-RPC方法(略) 49 第四章 程序员指南 49 4.1. Blobstore程序员指南 49 4.1.1 介绍 50 4.1.2 运作理论 50 4.1.3 设计注意事项 52 4.1.4 例子 54 4.1.5配置 54 4.1.6 组件细节 54 4.2. 块设备层编程指南 56 4.3 编写自定义块设备模块 58 4.3.1 介绍 58 4.3.2 创建一个新模块 59 4.3.3创建虚拟Bdev 60 4.4 NVMe over Fabrics目标编程指南 61 4.4.1 介绍 61 4.4.2 原语结构体 61 4.4.3 基础函数 62 4.4.4访问控制 62 4.4.5发现子系统 62 4.4.6 传输 63 4.4.7选择线程模型 63 4.4.8 跨CPU核心扩展 63 4.4.9 零拷贝支持 63 4.4.10 RDMA 63 4.5 Flash传输层 64 4.5.1 术语 64 4.5.2 使用方法 67 4.6 GDB宏用户指南 69 4.6.1 介绍 69 4.6.2 加载gdb宏 71 4.6.3 使用gdb数据目录 72 4.6.4 使用.gdbinit加载宏 72 4.6.5 为什么我们需要显式调用spdk_load_macros 72 4.6.6 以上可用的宏总结 73 4.6.7 添加新宏 73 4.7 SPDK “Reduce”块压缩算法 73 4.7.1 介绍 73 4.7.2 例子 74 4.8 通知库 78 第五章 基本信息 79 5.1 事件框架 79 5.1.1 事件框架设计注意事项 80 5.1.2 SPDK事件框架组件 80 5.1.3 应用框架 80 5.2 逻辑卷 81 5.2.1 术语 81 5.2.2 配置逻辑卷 84 5.3 矢量数据包处理(略) 86 第六章 杂项 86 6.1 介绍 86 6.2 NVMe的P2P API 86 6.3 确定设备支持 87 6.4 P2P问题 87 第七章 驱动程序 88 7.1 NVMe驱动程序*** 88 7.1.1 介绍 88 7.1.2 例子 88 7.1.3 公共接口 89 7.1.4 NVMe驱动程序设计 89 7.1.5 NVMe over Fabrics主机支持 91 7.1.6 NVMe多进程 91 7.1.7 NVMe Hotplug 92 7.2 I/OAT驱动程序 93 7.2.1 公共接口 93 7.2.2 关键功能 93 7.3 Virtio驱动程序 93 7.3.1 介绍 93 7.3.2 2MB大页面 93 第八章 工具 94 8.1 SPDK CLI 94 8.1.1 安装所需的依赖项 94 8.1.2 运行SPDK应用程序实例 94 8.1.3 运行SPDK CLI 94 8.1.4 可选 - 创建Python虚拟环境 94 8.2 nvme-CLI 95 8.2.1 nvme-cli with SPDK入门指南 95 8.2.2 使用场景 95 第九章 性能测试报告(略) 96 第十章NVMe-oF Target跟踪点*** 96 10.1 介绍 96 10.2 启用跟踪点 97 10.3 捕获事件的快照 97 10.4 捕获足够的跟踪事件 98 10.5 添加新的跟踪点 99
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值