NVMf RPC接口文件 nvmf_rpc.c

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: NVMf RPC接口文件 nvmf_rpc.c


在spdk的lib/nvmf/下的NVMf RPC接口文件nvmf_rpc.c是干啥的?有什么用发挥了什么功能

nvmf_rpc.c是SPDK的NVMf(NVMe over Fabrics)远程过程调用(RPC)接口文件,它定义了NVMf管理器(NVMf Target)的RPC接口。

NVMf管理器是一种软件实体,它在NVMe存储设备和网络之间提供桥梁,使得通过网络访问NVMe存储设备成为可能。NVMf管理器实现了NVMf协议和NVMe协议之间的转换,并提供了一些管理和控制接口,以便远程主机能够使用NVMe协议访问远程存储设备。

nvmf_rpc.c中的函数实现了管理和控制NVMf管理器的RPC接口,包括创建和删除NVMf命名空间(Namespace)、注册和注销NVMf传输连接(Transport Connection)等。这些RPC接口可以通过RPC机制被远程主机调用,从而实现对NVMf管理器的管理和控制。

总之,nvmf_rpc.c文件实现了NVMf管理器的RPC接口,提供了一种方便的远程管理和控制NVMf管理器的方式。

在SPDK(Storage Performance Development Kit)中,nvmf_rpc.c文件包含了许多用于管理NVMe over Fabrics(NVMf)子系统的RPC命令,包括创建RDMA Port监听的函数。下面是一些相关函数的代码片段和详细讲解:

  1. rpc_nvmf_subsystem_add_listener
rpc_nvmf_subsystem_add_listener(struct spdk_jsonrpc_request *request,
       const struct spdk_json_val *params)
{
  struct rpc_listen_ctx ctx = {};
  struct spdk_json_write_ctx *w;
  struct spdk_nvme_transport_id trid = {};
  struct spdk_nvmf_subsystem *subsystem;
  const char *name;
  uint16_t port;
  int rc;
  if (g_nvmf_tgt.pause) {
    ctx.request = request;
    ctx.params = params;
    TAILQ_INSERT_TAIL(&g_nvmf_tgt.listen_queue, &ctx, link);
    return;
  }
  /* ... 解析参数 ... */
  rc = spdk_nvmf_subsystem_add_listener(subsystem, &trid, nvmf_rpc_listen_done, request);
  if (rc) {
    SPDK_DEBUGLOG(SPDK_LOG_NVMF_RPC, "Failed to add listener. rc:%d\n", rc);
    goto invalid;
  }
  w = spdk_jsonrpc_begin_result(request);
  spdk_json_write_object_begin(w);
  spdk_json_write_named_string(w, "subsystem", name);
  spdk_json_write_named_string(w, "transport", trid.trstring);
  spdk_json_write_named_string(w, "traddr", trid.traddr);
  spdk_json_write_named_uint32(w, "trsvcid", trid.trsvcid);
  spdk_json_write_object_end(w);
  spdk_jsonrpc_end_result(request, w);
  return;
invalid:
  spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Failed to add listener.");
}

函数rpc_nvmf_subsystem_add_listener用于在指定的NVMf子系统中添加一个新的RDMA端口监听。函数首先解析输入参数,然后调用SPDK库中的spdk_nvmf_subsystem_add_listener函数来实现添加监听的功能。如果添加成功,函数将返回一个JSON RPC响应,其中包含新添加监听的信息。如果添加失败,函数将返回一个错误响应。

  1. nvmf_rpc_listen_paused
nvmf_rpc_listen_paused(void *arg1, void *arg2)
{
  struct rpc_listen_ctx *ctx = arg1;
  struct spdk_json_write_ctx *w;
  struct spdk_nvme_transport_id trid = {};
  struct spdk_nvmf_subsystem *subsystem;
  const char *name;
  uint16_t port;
  int rc;
  /* ... 解析参数 ... */
  rc = spdk_nvmf_subsystem_add_listener(subsystem, &trid, nvmf_rpc_listen_done, ctx->request);
  if (rc) {
    SPDK_DEBUGLOG(SPDK_LOG_NVMF_RPC, "Failed to add listener. rc:%d\n", rc);
    spdk_jsonrpc_send_error_response(ctx->request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
             "Failed to add listener.");
  } else {
    w = spdk_jsonrpc_begin_result(ctx->request);
    spdk_json_write_object_begin(w);
    spdk_json_write_named_string(w, "subsystem", name);
    spdk_json_write_named_string(w, "transport",trid.trstring);
spdk_json_write_named_string(w, "traddr", trid.traddr);
spdk_json_write_named_uint32(w, "trsvcid", trid.trsvcid);
spdk_json_write_object_end(w);
spdk_jsonrpc_end_result(ctx->request, w);
}

函数nvmf_rpc_listen_paused是一个回调函数,当NVMf目标暂停时被调用。它接收之前暂停时排队的监听请求上下文ctx,并根据该请求的参数来添加新的RDMA端口监听。如果添加成功,它将发送JSON RPC响应。否则,它将发送一个错误响应。

3. spdk_nvmf_tgt_listen

int
spdk_nvmf_tgt_listen(struct spdk_nvmf_listen_opts *opts)
{
  /* ... 检查opts参数 ... */
  return spdk_nvmf_transport_listen(opts);
}

函数spdk_nvmf_tgt_listen用于为SPDK NVMf目标创建一个新的监听器。它接收一个spdk_nvmf_listen_opts类型的结构体指针作为参数,该结构体包含了创建监听器所需的所有信息。函数首先检查传入的参数,然后调用SPDK库中的spdk_nvmf_transport_listen函数来实现创建监听器的功能。

  1. spdk_nvmf_transport_listen
int
spdk_nvmf_transport_listen(struct spdk_nvmf_listen_opts *opts)
{
  /* ... 检查opts参数 ... */
  return g_nvmf_transports[opts->transport->ops->type]->listen(opts);
}

函数spdk_nvmf_transport_listen是一个SPDK NVMf传输层的通用函数,用于为指定的传输类型创建一个新的监听器。它接收一个spdk_nvmf_listen_opts类型的结构体指针作为参数,该结构体包含了创建监听器所需的所有信息。函数首先检查传入的参数,然后调用相应的传输层函数来实现创建监听器的功能。

  1. nvmf_rdma_listen
static int
nvmf_rdma_listen(struct spdk_nvmf_transport *transport,
     const struct spdk_nvme_transport_id *trid,
     struct spdk_nvmf_listen_opts *opts)
{
  struct spdk_nvmf_rdma_transport *rtransport;
  struct spdk_nvmf_rdma_device *device;
  struct spdk_nvmf_rdma_listen_addr *addr;
  struct spdk_nvmf_rdma_qpair *qpair;
  struct sockaddr_storage listen_addr;
  socklen_t addr_size;
  char addr_buf[SPDK_NVMF_TRADDR_MAX_LEN + 1];
  int rc;
  rtransport = SPDK_CONTAINEROF(transport, struct spdk_nvmf_rdma_transport, transport);
  device = nvmf_rdma_find_device(trid, rtransport);
  if (!device) {
    SPDK_ERRLOG("Unable to find device with guid: %s\n", trid->traddr);
    return -EINVAL;
  }
  /* ... 创建sockaddr_storage结构体 ... */
  addr_size = rdma_sockaddr_size((struct sockaddr *)&listen_addr);
  memcpy(&opts->listen_id, trid, sizeof(opts->listen_id));
opts->listen_fd = rdma_create_ep(rtransport->cm_channel, (struct sockaddr *)&listen_addr,
NULL, rtransport->cm_event_channel);
if (opts->listen_fd < 0) {
SPDK_ERRLOG("rdma_create_ep() failed, errno %d: %s\n", errno, spdk_strerror(errno));
return -errno;
}
rc = rdma_listen(opts->listen_fd, 128); /* 128 is the maximum backlog for listen() */
if (rc < 0) {
  SPDK_ERRLOG("rdma_listen() failed, errno %d: %s\n", errno, spdk_strerror(errno));
  close(opts->listen_fd);
  return -errno;
}
SPDK_DEBUGLOG(SPDK_LOG_NVMF_RDMA, "Added RDMA listen address %s port %u\n",
        spdk_nvmf_trtype_str(opts->listen_id.trtype), opts->listen_id.trsvcid);
/* Add the new listen address */
addr = calloc(1, sizeof(*addr));
if (!addr) {
  SPDK_ERRLOG("Unable to allocate memory for listen address\n");
  close(opts->listen_fd);
  return -ENOMEM;
}
SPDK_DEBUGLOG(SPDK_LOG_NVMF_RDMA, "Adding listen addr %p\n", addr);
addr->id = opts->listen_id;
addr->device = device;
addr->listen_fd = opts->listen_fd;
TAILQ_INSERT_TAIL(&rtransport->listen_addrs, addr, link);
/* Queue up an accept event for the listen socket */
qpair = spdk_nvmf_rdma_qpair_create(device, rtransport, addr, 0);
if (!qpair) {
  SPDK_ERRLOG("Unable to allocate qpair for listening on %s:%u\n",
        trid->traddr, trid->trsvcid);
  close(opts->listen_fd);
  return -ENOMEM;
}
return 0;
}

函数nvmf_rdma_listen用于为RDMA传输类型创建一个新的监听器。它接收一个spdk_nvmf_transport类型的结构体指针、一个spdk_nvme_transport_id类型的结构体指针和一个spdk_nvmf_listen_opts类型的结构体指针作为参数。函数首先在指定的传输类型中查找与给定设备ID匹配的设备。如果找到了匹配的设备,则创建一个新的RDMA监听器。函数在创建监听器时,需要先构造一个sockaddr_storage类型的地址结构体,然后使用rdma_create_ep函数创建一个RDMA端点。接下来,函数使用rdma_listen函数将监听器绑定到指定的端口上。如果创建监听器成功,则函数将创建一个新的RDMA QPair对象,并将其添加到指定设备的QPairs列表中。如果失败,则函数将发送一个错误响应。最后,函数返回0表示成功,返回其他值表示失败。

总之,这些函数都是用于在SPDK的nvmf_rpc.c文件中创建RDMA Port监听的函数。它们通过调用SPDK库中的不同函数来实现监听器的创建和管理。这些函数的详细代码和实现原理可以帮助开发人员更好地理解SPDK NVMf目标的实现细节。

函数spdk_nvmf_tgt_listen用于启动SPDK NVMf目标的监听器。它接受一个spdk_nvmf_listen_opts类型的结构体指针作为参数,该结构体包含了所需的监听器选项。函数首先使用指定的传输类型初始化全局变量g_nvmf_tgt.max_queue_depth和g_nvmf_tgt.max_qpairs_per_ctrlr,这两个变量分别代表每个队列的最大深度和每个控制器的最大QP数。接下来,函数通过调用指定传输类型的transport->listen函数来创建一个新的监听器,并将其添加到全局变量g_nvmf_tgt.subsystems中。如果创建监听器成功,则函数返回0。否则,它将释放监听器所需的资源,并返回错误代码。

总之,这些函数的作用是创建和管理SPDK NVMf目标的RDMA监听器。它们通过调用SPDK库中的不同函数来实现监听器的创建和管理。了解这些函数的实现细节可以帮助开发人员更好地理解SPDK NVMf目标的工作原理,并在需要时进行调试和优化。

函数nvmf_rpc_listen_paused用于在RPC事件中处理新的RDMA监听器请求。它接受一个spdk_jsonrpc_request类型的结构体指针、一个spdk_json_val类型的结构体指针和一个spdk_json_write_ctx类型的结构体指针作为参数。函数首先解析JSON参数对象,并使用SPDK库中的spdk_nvmf_transport_name_to_opts函数从参数中提取传输类型和选项。接下来,函数调用SPDK库中的spdk_nvmf_tgt_find_subsystem函数查找与指定名称匹配的子系统,并将其返回到全局变量g_nvmf_tgt.subsystems中。如果找不到子系统,则函数将发送一个错误响应。如果找到子系统,则函数使用spdk_nvmf_transport_create函数创建一个新的传输类型,并使用spdk_nvmf_transport_add_listener函数将其添加到子系统的监听器列表中。如果创建监听器成功,则函数将发送一个成功响应。否则,它将发送一个错误响应。

函数rpc_nvmf_subsystem_add_listener用于在RPC事件中处理新的RDMA监听器请求。它接受一个spdk_jsonrpc_request类型的结构体指针、一个spdk_json_val类型的结构体指针和一个spdk_json_write_ctx类型的结构体指针作为参数。函数首先解析JSON参数对象,并使用SPDK库中的spdk_nvmf_transport_name_to_opts函数从参数中提取传输类型和选项。接下来,函数调用SPDK库中的spdk_nvmf_tgt_find_subsystem函数查找与指定名称匹配的子系统,并将其返回到全局变量g_nvmf_tgt.subsystems中。如果找不到子系统,则函数将发送一个错误响应。如果找到子系统,则函数使用spdk_nvmf_transport_create函数创建一个新的传输类型,并使用spdk_nvmf_transport_add_listener函数将其添加到子系统的监听器列表中。如果创建监听器成功,则函数将发送一个成功响应。否则,它将发送一个错误响应。

总之,这些函数是用于在SPDK的nvmf_rpc.c文件中处理新的RDMA监听器请求的函数。它们通过调用SPDK库中的不同函数来实现监听器的创建和管理,并使用JSON-RPC协议与客户端进行通信。这些函数的详细代码和实现原理可以帮助开发人员更好地理解SPDK NVMf目标的实现细节,并在需要时进行调试和优化。

函数nvmf_rdma_listen用于创建和启动RDMA监听器。它接受一个nvmf_rdma_device结构体指针、一个nvmf_rdma_listen_opts结构体指针、一个spdk_nvmf_listen_opts结构体指针和一个nvmf_rdma_listen_cb回调函数作为参数。函数首先使用指定的RDMA设备和选项初始化RDMA监听器的相关参数,包括端口号、队列深度、接收队列大小、传输类型等等。接下来,函数使用SPDK库中的ibv_query_device函数查询RDMA设备的属性,并使用ibv_create_comp_channel和ibv_create_cq函数创建RDMA设备的完成通道和完成队列。然后,函数使用ibv_alloc_pd和ibv_reg_mr函数分配和注册内存区域,用于接收和发送RDMA数据。接着,函数使用ibv_create_srq函数创建共享接收队列,并使用ibv_post_srq_recv函数将初始接收请求添加到队列中。最后,函数使用ibv_listen函数创建RDMA监听器,并启动接收请求循环以接收新的连接请求。如果创建监听器成功,则函数调用指定的回调函数,并返回RDMA监听器的句柄。否则,它将释放所有已分配的资源,并返回NULL。

总之,函数nvmf_rdma_listen是用于创建和启动RDMA监听器的函数。它使用了RDMA库中的不同函数来实现RDMA设备的初始化、内存的分配和注册、共享接收队列的创建、RDMA监听器的创建和启动等功能。了解这个函数的实现细节可以帮助开发人员更好地理解RDMA技术的原理和实现,以及在需要时进行调试和优化。

在spdk里面的nvmf_rpc.c文件中创建RDMA Port 的意义与流程

在SPDK中,nvmf_rpc.c文件主要实现了与NVMf子系统的交互。其中,创建RDMA Port的过程是在RPC层完成的,目的是为了在用户调用SPDK库时,自动创建RDMA端口并监听连接请求,从而简化用户代码的编写。下面我们来详细说说在spdk里面的nvmf_rpc.c文件中创建RDMA Port的意义与流程。

在SPDK中,RDMA技术用于实现NVMf(Non-Volatile Memory express over Fabrics)协议的远程访问。NVMf是一种基于NVMe协议的远程存储访问协议,允许使用RDMA技术在远程计算机上访问本地存储设备。因此,在SPDK中实现NVMf协议需要创建RDMA端口,接收来自远程计算机的连接请求,并将这些请求映射到本地的NVMe设备。

nvmf_rpc.c文件中的创建RDMA Port的过程分为三个阶段:初始化RDMA设备、创建RDMA监听器和启动监听循环。下面我们分别介绍这三个阶段的具体流程:

初始化RDMA设备:在这个阶段,nvmf_rpc_subsystem_add_listener函数会调用spdk_nvmf_transport_create函数创建NVMf传输层实例,并将其与指定的RDMA设备进行关联。这个函数会调用nvmf_rdma_create函数创建一个RDMA传输层实例,并使用指定的RDMA设备初始化RDMA传输层实例的相关参数,包括端口号、队列深度、接收队列大小、传输类型等等。这个函数还会使用ibv_query_device函数查询RDMA设备的属性,并使用ibv_create_comp_channel和ibv_create_cq函数创建RDMA设备的完成通道和完成队列。

创建RDMA监听器:在这个阶段,nvmf_rpc_subsystem_add_listener函数会调用nvmf_rpc_listen_paused函数创建RDMA监听器,并将其与指定的NVMf传输层实例进行关联。这个函数会调用spdk_nvmf_tgt_listen函数创建一个RDMA监听器,并使用指定的选项初始化RDMA监听器的相关参数,包括NVMe控制器、队列深度、接收队列大小、传输类型等等。这个函数还会使用nvmf_rdma_listen函数创建和启动RDMA监听器,以接收来自远程计算机的连接请求。

启动监听循环:在这个阶段,nvmf_rpc_subsystem_add_listener函数会调用spdk_nvmf_transport_accept函数启动监听循环,用于接收来自远程计算机的连接请求。这个函数会使用RDMA监听器的句柄和相关参数创建一个监听循环,并使用ibv_get_cq_event函数等待新的连接请求。如果有新的连接请求到达,则会调用spdk_nvmf_tgt_accept函数处理这个连接请求,将其映射到本地的NVMe设备,并向远程计算机发送连接响应。如果没有新的连接请求,则会继续等待下一个连接请求。

总的来说,创建RDMA Port的过程可以简化用户代码的编写,使用户可以更方便地使用SPDK库来实现NVMf协议的远程存储访问。这个过程主要涉及到RDMA设备的初始化、RDMA监听器的创建和启动监听循环三个阶段,每个阶段都涉及到不同的函数调用和参数设置,需要仔细理解和配置。

推荐一个零声学院免费公开课程,个人觉得老师讲得不错,分享给大家:Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,立即学习

相关文章
|
23天前
|
存储 前端开发 JavaScript
前端的全栈之路Meteor篇(四):RPC方法注册及调用-更轻量的服务接口提供方式
RPC机制通过前后端的`callAsync`方法实现了高效的数据交互。后端通过`Meteor.methods()`注册方法,支持异步操作;前端使用`callAsync`调用后端方法,代码更简洁、易读。本文详细介绍了Methods注册机制、异步支持及最佳实践。
|
4月前
|
运维 监控 IDE
通用研发提效问题之在软件研发的各个阶段,提升效率的工具和方法,如何解决
通用研发提效问题之在软件研发的各个阶段,提升效率的工具和方法,如何解决
|
弹性计算 JSON Devops
阿里云效(Aliyun DevOps)的接口使用的是RPC(Remote Procedure Call)协议
阿里云效(Aliyun DevOps)的接口使用的是RPC(Remote Procedure Call)协议
391 1
|
存储 搜索推荐 API
如何设计 RPC 接口
如何设计 RPC 接口
244 0
|
传感器
ENVI:如何自定义RPC文件?
ENVI:如何自定义RPC文件?
228 0
|
负载均衡 Dubbo 前端开发
HTTP 与 RPC 接口区别
HTTP 与 RPC 接口是两种常见的接口通信协议。本文将会介绍它们的定义,区别和相同之处,应用场景以及目前的技术发展趋势,并给出接口代码示例和开发常用工具。
HTTP 与 RPC  接口区别
|
JSON 编解码 监控
2023年,真的需要Thrift这样一个RPC微服务框架来拯救一下传统HTTP接口(api)了
目前市面上类似Django的drf框架基于json的http接口解决方案大行其道,人们也热衷于在接口不多、系统与系统交互较少的情况下使用它,http接口的优点就是简单、直接、开发方便,门槛低,利用现成的http协议进行传输。
2023年,真的需要Thrift这样一个RPC微服务框架来拯救一下传统HTTP接口(api)了
|
监控 网络协议 Java
RPC和HTTP接口
RPC(即Remote Procedure Call,远程过程调用)和HTTP(HyperText Transfer Protocol,超文本传输协议)他们最本质的区别,就是RPC主要工作在TCP协议之上,而HTTP服务主要是工作在HTTP协议之上,我们都知道HTTP协议是在传输层协议TCP之上的,所以效率来看的话,RPC当然是要更胜一筹。
165 0
|
JSON 网络协议 测试技术
RPC接口测试技术-websocket 自动化测试实践
RPC接口测试技术-websocket 自动化测试实践