NVMEoF 内核initiator端实现源码分析

1 篇文章 0 订阅

initiator端命令

获取target信息

        这个实现是在nvme-cli模块内部实现的,跟一个discovery控制器连接,获取可以连接的所有的nvme子系统的信息,可能会有多个。如下的示例中,只有一个nvme subsystem可以连接:nqn.2016-06.io.spdk:cnode1。

        具体命令如下:

root@cc:/home/cc# nvme discover -t rdma -a 192.168.50.242 -s 4420

Discovery Log Number of Records 1, Generation counter 1

=====Discovery Log Entry 0======

trtype: rdma

adrfam: ipv4

subtype: nvme subsystem

treq: not required

portid: 0

trsvcid: 4420

subnqn: nqn.2016-06.io.spdk:cnode1

traddr: 192.168.50.242

eflags: none

rdma_prtype: not specified

rdma_qptype: connected

rdma_cms: rdma-cm

rdma_pkey: 0x0000

 跟target建立连接

root@cc:/home/cc# nvme connect -t rdma -n "nqn.2016-06.io.spdk:cnode1" -a 192.168.50.242 -s 4420

        这个流程的实现是nvme-cli模块往,nvme-fabrics内核模块创建的/dev/nvme-fabric字符设备写入命令字符串,然后由nvme-fabrics内核模块来实现。

        nvme-fabrics内核模块的实现,主要在drivers\nvme\host\fabrics.c文件中。

connect流程

        nvme-fabrics内核模块的初始化:static int __init nvmf_init(void)

        nvme-fabrics内核模块初始化创建的/dev/nvme-fabrics字符文件,接收nvme connect命令,建立和target的连接,创建一个nvme虚拟设备。具体流程为nvmf_dev_write回调函数,调用nvmf_create_ctrl创建一个struct nvme_ctrl实体。nvmf_create_ctrl函数的流程如下:

        nvme-rdma模块的初始化函数为static int __init nvme_rdma_init_module(void),主要做两件事:

  • 注册一个ib设备的client:

static struct ib_client nvme_rdma_ib_client = {
    .name   = "nvme_rdma",
    .remove = nvme_rdma_remove_one
};
ib_register_client(&nvme_rdma_ib_client);
  • 向nvme-fabric模块注册一个rdma的传输层:nvmf_register_transport(&nvme_rdma_transport);

 

static struct nvmf_transport_ops nvme_rdma_transport = {
    .name       = "rdma",
    .module     = THIS_MODULE,
    .required_opts  = NVMF_OPT_TRADDR,
    .allowed_opts   = NVMF_OPT_TRSVCID | NVMF_OPT_RECONNECT_DELAY |
              NVMF_OPT_HOST_TRADDR | NVMF_OPT_CTRL_LOSS_TMO |
              NVMF_OPT_NR_WRITE_QUEUES | NVMF_OPT_NR_POLL_QUEUES |
              NVMF_OPT_TOS,
    .create_ctrl    = nvme_rdma_create_ctrl,
};
nvmf_register_transport(&nvme_rdma_transport);

核心函数为:

static struct nvme_ctrl *nvme_rdma_create_ctrl(struct device *dev, 
                                               struct nvmf_ctrl_options *opts)

其处理流程如下:

int nvme_rdma_setup_ctrl(struct nvme_rdma_ctrl *ctrl, bool new)流程如下

int nvme_rdma_configure_admin_queue(struct nvme_rdma_ctrl *ctrl, bool new)流程如下:

int nvme_rdma_alloc_queue(struct nvme_rdma_ctrl *ctrl, int idx, size_t queue_size)

初始化nvme_rdma_queue 的一些信息,建立一个ib的连接,并等待连接建立完成(3秒钟没有建立完成,认为超时失败)

struct nvme_rdma_queue {
    struct nvme_rdma_qe *rsp_ring;
    int         queue_size;  //管理队列固定32,io队列根据获取的信息确定,软件目前最大设置为128
    
    //admin队列,固定64字节;
    //io队列,根据Identify Controller Data Structure中的
    //I/O Queue Command Capsule Supported Size (IOCCSZ)决定
    size_t          cmnd_capsule_len; 
    struct nvme_rdma_ctrl   *ctrl;  //反指向所属的nvme_rdma_ctrl
    struct nvme_rdma_device *device; //自己创建的rdma device,
            //和一个ib_device对应,有一个指向该ib_device的指针,
            //和一个在该ib设备上创建的protect domian的指针
            //以及在该ib设备上,执行单次rdma send操作所能携带的SG的最大个数
    struct ib_cq        *ib_cq; //qp的sq和rq对应的cq
    struct ib_qp        *qp;  //rdma sq and rq

    unsigned long       flags; //3个bit状态:NVME_RDMA_Q_ALLOCATED、LIVE、TR_READY
    struct rdma_cm_id   *cm_id; //调用rdma_create_id创建的rdma的连接管理实体,
                     //会注册rdma connect manage的回调nvme_rdma_cm_handler,该函数会对
                     //各种rdma connect的连接状态,进行下一步的处理
    int         cm_error;
    struct completion   cm_done; //用于线程等待异步操作执行完成,主要是等待rdma建立连接
    bool            pi_support;  //t10卡的特殊特性, protect information,暂不关心
    int         cq_size;
    struct mutex        queue_lock;
};
struct nvme_rdma_device {
    struct ib_device    *dev;
    struct ib_pd        *pd;
    struct kref     ref;
    struct list_head    entry;
    unsigned int        num_inline_segments;
};

 

struct nvme_rdma_request {
    struct nvme_request req; //nvme core模块定义的request
    
    struct ib_mr        *mr;  //找ib的qb获取mr,将nvme请求的data部分,注册到该mr
                      //req->mr = ib_mr_pool_get(queue->qp, &queue->qp->rdma_mrs)
             //ib_map_mr_sg(req->mr, req->data_sgl.sg_table.sgl, count, NULL, SZ_4K);
    
    struct nvme_rdma_qe sqe;  //data指向一个nvme cmd数据缓存,这个是申请的一块内存
                    //sqe.data = kzalloc(sizeof(struct nvme_command), GFP_KERNEL);
                    //sqe.dma是将sqe.data映射为ib设备的一个地址
                    //sqe->cqe.done = nvme_rdma_send_done;
    
    union nvme_result   result;
    __le16          status;
    refcount_t      ref;
    
    //cmd采用ib sg的方式发送,如果cmd携带inline capule data,则会有多个ib sg,
    //目前最多带4个inline capule data
    struct ib_sge       sge[1 + NVME_RDMA_MAX_INLINE_SEGMENTS]; 
    u32         num_sge; //当前cmd的ib sg个数,如果不带inline capule data,则只有一个
    
    struct ib_reg_wr    reg_wr; //ib sq的请求,请求的内容是让硬件快速注册nvme data的mr
    struct ib_cqe       reg_cqe; //对应上面的reg_wr请求的完成回调
    
    struct nvme_rdma_queue  *queue;
    
    struct nvme_rdma_sgl    data_sgl; //nvme请求的data部分的sgl(linux lib)
                    //blk mq模块,会将block请求的data部分,转换成sgl:
         //req->data_sgl.nents = blk_rq_map_sg(rq->q, rq, req->data_sgl.sg_table.sgl);
    
    struct nvme_rdma_sgl    *metadata_sgl;
    bool            use_sig_mr;
};

mq-blk->nvme rdma->ib verbs数据结构

ib提交请求send请求接口

static inline int ib_post_send(struct ib_qp *qp,
                   const struct ib_send_wr *send_wr,
                   const struct ib_send_wr **bad_send_wr)
  • struct ib_send_wr,可以串起来,一次发送多个请求;

  • 对于每一个请求,除了要指定请求的类型(mr注册/send/read/write等等),请求的数据,还需要一个给定一个struct ib_cqe指针(业务方申请地址存放),这个里面要指定一个回调函数;send操作的最终结果,会通过这个回调通知业务方。

  • 回调函数的入参有两个,一个是ib qb,一个是之前发送请求时指定的struct ib_cqe指针。业务方通过struct ib_cqe指针,找到自己的上下文信息。

In-capsule data的场景

只能是以下全满足的场景:

  • 只能是IO write操作(管理队列不支持In-capsule data)

  • io请求的sgl数目不能超过ib一次请求可以携带的sg个数-1(cmd占用一个sg),另外nvme rdma软件侧,额外限制了最大sgl个数为4个。

  • data + cmd长度,不能超过target控制器支持的最大capsule长度(I/O Queue Command Capsule Supported Size (IOCCSZ))。这里要求target控制器的in-capsule data offset必须为0,没有考虑不为0的情况。(要支持in-capsule data offset不为0,则代码实现有点复杂度,ib接口要额外填充一块in-capsule data offset的数据,这块数据要额外申请内存)。

  • 25
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值