在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监听的函数。下面是一些相关函数的代码片段和详细讲解:
- 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响应,其中包含新添加监听的信息。如果添加失败,函数将返回一个错误响应。
- 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函数来实现创建监听器的功能。
- 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类型的结构体指针作为参数,该结构体包含了创建监听器所需的所有信息。函数首先检查传入的参数,然后调用相应的传输层函数来实现创建监听器的功能。
- 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等技术内容,立即学习