Soft-RoCE内核实现

参考http://hustcat.github.io/linux-soft-roce-implementation/

  内核在4.9实现的Soft-RoCE实现了RoCEv2.

队列初始化

libRXE (user space library)

ibv_create_qp
|--- rxe_create_qp
    |--- ibv_cmd_create_qp

  • ibv_create_qp
LATEST_SYMVER_FUNC(ibv_create_qp, 1_1, "IBVERBS_1.1",
           struct ibv_qp *,
           struct ibv_pd *pd,
           struct ibv_qp_init_attr *qp_init_attr)
{
    struct ibv_qp *qp = pd->context->ops.create_qp(pd, qp_init_attr); ///rxe_ctx_ops
///..
}

  • rxe_create_qp
static struct ibv_qp *rxe_create_qp(struct ibv_pd *pd,
                    struct ibv_qp_init_attr *attr)
{
    struct ibv_create_qp cmd;
    struct rxe_create_qp_resp resp;
    struct rxe_qp *qp;
    int ret;
////..
    ret = ibv_cmd_create_qp(pd, &qp->ibv_qp, attr, &cmd, sizeof cmd,
                &resp.ibv_resp, sizeof resp); /// ibv_create_qp CMD, to kernel
///...
    qp->sq.max_sge = attr->cap.max_send_sge;
    qp->sq.max_inline = attr->cap.max_inline_data;
    qp->sq.queue = mmap(NULL, resp.sq_mi.size, PROT_READ | PROT_WRITE,
                MAP_SHARED,
                pd->context->cmd_fd, resp.sq_mi.offset); ///mmap,参考rxe_mmap

ibv_context->cmd_fd指向对应的ibv_device,由ibv_open_device返回。

ibv_cmd_create_qp会通过ibv_context->cmd_fd给内核发送IB_USER_VERBS_CMD_CREATE_QP命令,参考libiverbs@ibv_cmd_create_qp.

对应的内核write函数为ib_uverbs_write:

///drivers/infiniband/core/uverbs_main.c
static const struct file_operations uverbs_fops = {
    .owner   = THIS_MODULE,
    .write   = ib_uverbs_write,
    .open    = ib_uverbs_open,
    .release = ib_uverbs_close,
    .llseek  = no_llseek,
};

  • ibv_open_device
///libibverbs/device.c
LATEST_SYMVER_FUNC(ibv_open_device, 1_1, "IBVERBS_1.1",
           struct ibv_context *,
           struct ibv_device *device)
{
    struct verbs_device *verbs_device = verbs_get_device(device);
    char *devpath;
    int cmd_fd, ret;
    struct ibv_context *context;
    struct verbs_context *context_ex;

    if (asprintf(&devpath, "/dev/infiniband/%s", device->dev_name) < 0)
        return NULL;

    /*
     * We'll only be doing writes, but we need O_RDWR in case the
     * provider needs to mmap() the file.
     */
    cmd_fd = open(devpath, O_RDWR | O_CLOEXEC); /// /dev/infiniband/uverbs0
    free(devpath);

    if (cmd_fd < 0)
        return NULL;

    if (!verbs_device->ops->init_context) {
        context = verbs_device->ops->alloc_context(device, cmd_fd); ///rxe_alloc_context, rxe_dev_ops
        if (!context)
            goto err;
    }
///...
    context->device = device;
    context->cmd_fd = cmd_fd;
    pthread_mutex_init(&context->mutex, NULL);

    ibverbs_device_hold(device);

    return context;
///...
}

kernel (rdma_rxe module)

  • ib_uverbs_create_qp

IB_USER_VERBS_CMD_CREATE_QP的处理函数为函数ib_uverbs_create_qp.

ib_uverbs_write
|--- ib_uverbs_create_qp
     |--- create_qp
          |--- ib_device->create_qp
               |--- rxe_create_qp

create_qp调用ib_device->create_qp,对于RXE, 为函数rxe_create_qp, 参考rxe_register_device.

  • rxe_create_qp
rxe_create_qp
|--- rxe_qp_from_init
     |--- rxe_qp_init_req

rxe_qp_from_init完成发送队列和接收队列的初始化。

  • rxe_qp_init_req

rxe_qp_init_req主要做以下一些事情:

创建对应的UDP socket

调用rxe_queue_init完成发送队列的初始化.

初始化对应的tasklet

  • rxe_queue_init

rxe_queue_init给队列分配内存空间:

struct rxe_queue *rxe_queue_init(struct rxe_dev *rxe,
                 int *num_elem,
                 unsigned int elem_size)
{
    struct rxe_queue *q;
    size_t buf_size;
    unsigned int num_slots;
///...
    buf_size = sizeof(struct rxe_queue_buf) + num_slots * elem_size;

    q->buf = vmalloc_user(buf_size);
///...
}

rxe_queue->buf指向的内存缓冲区,由rxe_mmap映射到用户空间,队列的element对应数据结构struct rxe_send_wqe.

libiverbs API调用ibv_post_send时,会将对应的struct rxe_send_wqe加入到该队列,参考rdma-core@post_one_send.

  • rxe_mmap
/**
 * rxe_mmap - create a new mmap region
 * @context: the IB user context of the process making the mmap() call
 * @vma: the VMA to be initialized
 * Return zero if the mmap is OK. Otherwise, return an errno.
 */
int rxe_mmap(struct ib_ucontext *context, struct vm_area_struct *vma)
{
    struct rxe_dev *rxe = to_rdev(context->device);
    unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
    unsigned long size = vma->vm_end - vma->vm_start;
    struct rxe_mmap_info *ip, *pp;
///...
found_it:
    list_del_init(&ip->pending_mmaps);
    spin_unlock_bh(&rxe->pending_lock);

    ret = remap_vmalloc_range(vma, ip->obj, 0);
    if (ret) {
        pr_err("rxe: err %d from remap_vmalloc_range\n", ret);
        goto done;
    }

    vma->vm_ops = &rxe_vm_ops;
    vma->vm_private_data = ip;
    rxe_vma_open(vma);
///...
}

发送数据

libRXE

rxe_post_send会将struct ibv_send_wr转成struct rxe_send_wqe,并加入到发送队列rxe_qp->rq,然后通过cmd_fd给RXE内核模块发送IB_USER_VERBS_CMD_POST_SEND命令:

///providers/rxe/rxe.c
/* this API does not make a distinction between
   restartable and non-restartable errors */
static int rxe_post_send(struct ibv_qp *ibqp,
             struct ibv_send_wr *wr_list,
             struct ibv_send_wr **bad_wr)
{
    int rc = 0;
    int err;
    struct rxe_qp *qp = to_rqp(ibqp);/// ibv_qp -> rxe_qp
    struct rxe_wq *sq = &qp->sq;

    if (!bad_wr)
        return EINVAL;

    *bad_wr = NULL;

    if (!sq || !wr_list || !sq->queue)
        return EINVAL;

    pthread_spin_lock(&sq->lock);

    while (wr_list) {
        rc = post_one_send(qp, sq, wr_list); /// ibv_send_wr -> rxe_send_wqe, enqueue
        if (rc) {
            *bad_wr = wr_list;
            break;
        }

        wr_list = wr_list->next;
    }

    pthread_spin_unlock(&sq->lock);

    err =  post_send_db(ibqp); /// IB_USER_VERBS_CMD_POST_SEND cmd
    return err ? err : rc;
}

kernel

处理的IB_USER_VERBS_CMD_POST_SEND的函数为ib_uverbs_post_send:

ib_uverbs_post_send -> ib_device->post_send -> rxe_post_send -> rxe_requester -> ip_local_out

  • rxe_post_send
static int rxe_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
             struct ib_send_wr **bad_wr)
{
    int err = 0;
    struct rxe_qp *qp = to_rqp(ibqp); ///ib_qp -> rxe_qp
///...
    /*
     * Must sched in case of GSI QP because ib_send_mad() hold irq lock,
     * and the requester call ip_local_out_sk() that takes spin_lock_bh.
     */
    must_sched = (qp_type(qp) == IB_QPT_GSI) ||
            (queue_count(qp->sq.queue) > 1);

    rxe_run_task(&qp->req.task, must_sched); /// to rxe_requester

    return err;
}

  • rxe_requester

rxe_requesterrxe_qp队列取出rxe_send_wqe,生成对应的skb_buff,然后下发给对应的rxe_dev设备:

///sw/rxe/rxe_req.c
int rxe_requester(void *arg)
{
    struct rxe_qp *qp = (struct rxe_qp *)arg;
    struct rxe_pkt_info pkt;
    struct sk_buff *skb;
    struct rxe_send_wqe *wqe;
///...
    wqe = req_next_wqe(qp); /// get rxe_send_wqe
///...
    /// rxe_send_wqe -> skb
    skb = init_req_packet(qp, wqe, opcode, payload, &pkt);
///...
    ret = rxe_xmit_packet(to_rdev(qp->ibqp.device), qp, &pkt, skb);
///...
}

static inline int rxe_xmit_packet(struct rxe_dev *rxe, struct rxe_qp *qp,
                  struct rxe_pkt_info *pkt, struct sk_buff *skb)
{
///...
    if (pkt->mask & RXE_LOOPBACK_MASK) {
        memcpy(SKB_TO_PKT(skb), pkt, sizeof(*pkt));
        err = rxe->ifc_ops->loopback(skb);
    } else {
        err = rxe->ifc_ops->send(rxe, pkt, skb);/// ifc_ops->send, send
    }
///...
}

ifc_ops->send最后会调用ip_local_out,从对应的物理NIC发送出去。

Refs

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,084评论 1 32
  • 声明: 以下只是学习笔记,仅作为记录。定义如下: 简单使用 对比之前用法,我们每次都会实例化一个迭代器,传入内容,...
    NinthDay阅读 619评论 0 1
  • 家:宀,表示住房;豕,猪。古人所以用豕而不是牛、狗、鸡等等,显然是欣赏猪超强的生存力、强大的繁殖力。 简友这个平台...
    花痴_halou阅读 165评论 4 5
  • “奇怪,明明给我打了电话的,有显示的。” 闻月月坐在自家的超市里自言自语。现在的她又一次清闲起来,刚刚的一阵忙碌,...
    淡然若怡阅读 302评论 0 0
  • 今天是我们农历的腊月十六了!时间过的真的很快啊!白天和宝宝一起去逛街这是她每天忠实的粉丝!走哪儿跟哪儿发现现在的孩...
    沐恩_d40f阅读 155评论 0 1