前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Intel E810/ICE DPU RDMA 及MLX中断原理分析2(CE/AE)

Intel E810/ICE DPU RDMA 及MLX中断原理分析2(CE/AE)

原创
作者头像
晓兵
发布于 2025-03-29 13:59:27
发布于 2025-03-29 13:59:27
8100
代码可运行
举报
文章被收录于专栏:Linux内核Linux内核DPU
运行总次数:0
代码可运行

接上文: https://cloud.tencent.com/developer/article/2472554

创建CQ时的comp_vector参数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct ibv_cq *ibv_create_cq(struct ibv_context *context, int cqe,
                             void *cq_context,
                             struct ibv_comp_channel *channel,
                             int comp_vector);
​
​
comp_vector: MSI-X 完成向量,用于发出完成事件信号。如果已配置这些中断的 IRQ 关联掩码以将每个 MSI-X 中断分散到不同的核心处理,则可以使用此参数将完成工作负载分散到多个核心。值可以是 [0..context->num_comp_vectors)

E810设置 num_comp_vectors

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static void irdma_init_rdma_device(struct irdma_device *iwdev)
{
        struct pci_dev *pcidev = iwdev->rf->pcidev;if (iwdev->roce_mode)
                irdma_init_roce_device(iwdev);
        else
                irdma_init_iw_device(iwdev);
​
        iwdev->ibdev.phys_port_cnt = 1;
        iwdev->ibdev.num_comp_vectors = iwdev->rf->ceqs_count; // 完成通知向量数量等于创建的CEQ总数
        iwdev->ibdev.dev.parent = &pcidev->dev;
        ib_set_device_ops(&iwdev->ibdev, &irdma_dev_ops);
}

获取上下文时, 直接返回设置上的num_comp_vectors

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static int ib_uverbs_get_context(struct uverbs_attr_bundle *attrs)
{
    struct ib_uverbs_get_context_resp resp;
    struct ib_uverbs_get_context cmd;
    struct ib_device *ib_dev;
    struct ib_uobject *uobj;
    int ret;
​
    ret = uverbs_request(attrs, &cmd, sizeof(cmd));
    if (ret)
        return ret;
​
    ret = ib_alloc_ucontext(attrs);
    if (ret)
        return ret;
​
    uobj = uobj_alloc(UVERBS_OBJECT_ASYNC_EVENT, attrs, &ib_dev);
    if (IS_ERR(uobj)) {
        ret = PTR_ERR(uobj);
        goto err_ucontext;
    }
​
    resp = (struct ib_uverbs_get_context_resp){
        .num_comp_vectors = attrs->ufile->device->num_comp_vectors, // 获取上下文时, 直接返回设置上的num_comp_vectors
        .async_fd = uobj->id,
    };
    ret = uverbs_response(attrs, &resp, sizeof(resp));
    if (ret)
        goto err_uobj;
​
    ret = ib_init_ucontext(attrs);
    if (ret)
        goto err_uobj;ib_uverbs_init_async_event_file(
        container_of(uobj, struct ib_uverbs_async_event_file, uobj));
    rdma_alloc_commit_uobject(uobj, attrs);
    return 0;err_uobj:
    rdma_alloc_abort_uobject(uobj, attrs, false);
err_ucontext:
    rdma_restrack_put(&attrs->context->res);
    kfree(attrs->context);
    attrs->context = NULL;
    return ret;
}
​
​
static int ib_uverbs_add_one(struct ib_device *device)
{
    int devnum;
    dev_t base;
    struct ib_uverbs_device *uverbs_dev;
    int ret;if (!device->ops.alloc_ucontext ||
        device->type == RDMA_DEVICE_TYPE_SMI)
        return -EOPNOTSUPP;
​
    uverbs_dev = kzalloc(sizeof(*uverbs_dev), GFP_KERNEL);
    if (!uverbs_dev)
        return -ENOMEM;
​
    ret = init_srcu_struct(&uverbs_dev->disassociate_srcu);
    if (ret) {
        kfree(uverbs_dev);
        return -ENOMEM;
    }device_initialize(&uverbs_dev->dev);
    uverbs_dev->dev.class = &uverbs_class;
    uverbs_dev->dev.parent = device->dev.parent;
    uverbs_dev->dev.release = ib_uverbs_release_dev;
    uverbs_dev->groups[0] = &dev_attr_group;
    uverbs_dev->dev.groups = uverbs_dev->groups;
    refcount_set(&uverbs_dev->refcount, 1);
    init_completion(&uverbs_dev->comp);
    uverbs_dev->xrcd_tree = RB_ROOT;
    mutex_init(&uverbs_dev->xrcd_tree_mutex);
    mutex_init(&uverbs_dev->lists_mutex);
    INIT_LIST_HEAD(&uverbs_dev->uverbs_file_list);
    rcu_assign_pointer(uverbs_dev->ib_dev, device);
    uverbs_dev->num_comp_vectors = device->num_comp_vectors; //注册ib_device时, 将device上的num_comp_vectors设置到uverbs_dev上
​
    devnum = ida_alloc_max(&uverbs_ida, IB_UVERBS_MAX_DEVICES - 1,
                   GFP_KERNEL);
    if (devnum < 0) {
        ret = -ENOMEM;
        goto err;
    }
    uverbs_dev->devnum = devnum;
    if (devnum >= IB_UVERBS_NUM_FIXED_MINOR)
        base = dynamic_uverbs_dev + devnum - IB_UVERBS_NUM_FIXED_MINOR;
    else
        base = IB_UVERBS_BASE_DEV + devnum;
​
    ret = ib_uverbs_create_uapi(device, uverbs_dev);
    if (ret)
        goto err_uapi;
​
    uverbs_dev->dev.devt = base;
    dev_set_name(&uverbs_dev->dev, "uverbs%d", uverbs_dev->devnum);cdev_init(&uverbs_dev->cdev,
          device->ops.mmap ? &uverbs_mmap_fops : &uverbs_fops);
    uverbs_dev->cdev.owner = THIS_MODULE;
​
    ret = cdev_device_add(&uverbs_dev->cdev, &uverbs_dev->dev);
    if (ret)
        goto err_uapi;ib_set_client_data(device, &uverbs_client, uverbs_dev);
    return 0;err_uapi:
    ida_free(&uverbs_ida, devnum);
err:
    if (refcount_dec_and_test(&uverbs_dev->refcount))
        ib_uverbs_comp_dev(uverbs_dev);
    wait_for_completion(&uverbs_dev->comp);
    put_device(&uverbs_dev->dev);
    return ret;
}

irdma_sc_dev中定义记录ceq是的数组

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct irdma_sc_dev {
    struct list_head cqp_cmd_head; /* head of the CQP command list */
    spinlock_t cqp_lock; /* protect CQP list access */
    bool stats_idx_array[IRDMA_MAX_STATS_COUNT_GEN_1];
    struct irdma_dma_mem vf_fpm_query_buf[IRDMA_MAX_PE_ENA_VF_COUNT];
    u64 fpm_query_buf_pa;
    u64 fpm_commit_buf_pa;
    __le64 *fpm_query_buf;
    __le64 *fpm_commit_buf;
    struct irdma_hw *hw;
    u8 __iomem *db_addr;
    u32 __iomem *wqe_alloc_db;
    u32 __iomem *cq_arm_db;
    u32 __iomem *aeq_alloc_db;
    u32 __iomem *cqp_db;
    u32 __iomem *cq_ack_db;
    u32 __iomem *ceq_itr_mask_db;
    u32 __iomem *aeq_itr_mask_db;
    u32 __iomem *hw_regs[IRDMA_MAX_REGS];
    u32 ceq_itr;   /* Interrupt throttle, usecs between interrupts: 0 disabled. 2 - 8160 */
    u64 hw_masks[IRDMA_MAX_MASKS];
    u64 hw_shifts[IRDMA_MAX_SHIFTS];
    const struct irdma_hw_stat_map *hw_stats_map;
    u64 hw_stats_regs[IRDMA_HW_STAT_INDEX_MAX_GEN_1];
    u64 feature_info[IRDMA_MAX_FEATURES];
    u64 cqp_cmd_stats[IRDMA_MAX_CQP_OPS];
    struct irdma_hw_attrs hw_attrs;
    struct irdma_hmc_info *hmc_info;
    struct irdma_sc_cqp *cqp;
    struct irdma_sc_aeq *aeq;
    struct irdma_sc_ceq *ceq[IRDMA_CEQ_MAX_COUNT]; // CEQ数组, IRDMA_CEQ_MAX_COUNT = 1024
    struct irdma_sc_cq *ccq;
    const struct irdma_irq_ops *irq_ops;
    struct irdma_hmc_fpm_misc hmc_fpm_misc;
    struct irdma_ws_node *ws_tree_root;
    struct mutex ws_mutex; /* ws tree mutex */
    u16 num_vfs;
    u8 hmc_fn_id;
    u8 vf_id;
    bool vchnl_up:1;
    bool ceq_valid:1;
    u8 pci_rev;
    int (*ws_add)(struct irdma_sc_vsi *vsi, u8 user_pri);
    void (*ws_remove)(struct irdma_sc_vsi *vsi, u8 user_pri);
    void (*ws_reset)(struct irdma_sc_vsi *vsi);
};

创建CEQ时递增RF上的CEQ计数器(ceqs_count)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static int irdma_setup_ceqs(struct irdma_pci_f *rf, struct irdma_sc_vsi *vsi)
{
    u32 i;
    u32 ceq_id;
    struct irdma_ceq *iwceq;
    struct irdma_msix_vector *msix_vec;
    int status;
    u32 num_ceqs;
​
    num_ceqs = min(rf->msix_count, rf->sc_dev.hmc_fpm_misc.max_ceqs); // CEQ数量等于: 中断向量数与HW_HMC中记录的最大CEQS数量间较小的那个
    i = (rf->msix_shared) ? 1 : 2;
    for (ceq_id = 1; i < num_ceqs; i++, ceq_id++) { // CEQ_ID递增
        iwceq = &rf->ceqlist[ceq_id]; // 处理时, 遍历该列表, 参考: irdma_process_ceq(rf, rf->ceqlist)
        status = irdma_create_ceq(rf, iwceq, ceq_id, vsi);
        if (status) {
            ibdev_dbg(&rf->iwdev->ibdev,
                  "ERR: create ceq status = %d\n", status);
            goto del_ceqs;
        }
        spin_lock_init(&iwceq->ce_lock);
        msix_vec = &rf->iw_msixtbl[i];
        iwceq->irq = msix_vec->irq;
        iwceq->msix_idx = msix_vec->idx;
        status = irdma_cfg_ceq_vector(rf, iwceq, ceq_id, msix_vec);
        if (status) {
            irdma_destroy_ceq(rf, iwceq);
            goto del_ceqs;
        }
        irdma_ena_intr(&rf->sc_dev, msix_vec->idx);
        rf->ceqs_count++; // 递增CEQ计数器
    }return 0;del_ceqs:
    irdma_del_ceqs(rf);return status;
}

删除所有CEQ(ceq0除外)时, CEQ计数器设置为1

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static void irdma_del_ceqs(struct irdma_pci_f *rf)
{
    struct irdma_ceq *iwceq = &rf->ceqlist[1];
    struct irdma_msix_vector *msix_vec;
    u32 i = 0;if (rf->msix_shared)
        msix_vec = &rf->iw_msixtbl[1];
    else
        msix_vec = &rf->iw_msixtbl[2];for (i = 1; i < rf->ceqs_count; i++, msix_vec++, iwceq++) {
        rf->sc_dev.irq_ops->irdma_cfg_ceq(&rf->sc_dev, msix_vec->ceq_id,
                          msix_vec->idx, false);
        irdma_destroy_irq(rf, msix_vec, iwceq);
        irdma_cqp_ceq_cmd(&rf->sc_dev, &iwceq->sc_ceq,
                  IRDMA_OP_CEQ_DESTROY);
        dma_free_coherent(rf->sc_dev.hw->device, iwceq->mem.size,
                  iwceq->mem.va, iwceq->mem.pa);
        iwceq->mem.va = NULL;
    }
    rf->ceqs_count = 1; // 删除所有CEQ(ceq0除外)时, CEQ计数器设置为1
}

删除ceq0时, 将CEQ计数器设置为0

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static void irdma_del_ceq_0(struct irdma_pci_f *rf)
{
    struct irdma_ceq *iwceq = rf->ceqlist;
    struct irdma_msix_vector *msix_vec;if (rf->msix_shared) {
        msix_vec = &rf->iw_msixtbl[0];
        rf->sc_dev.irq_ops->irdma_cfg_ceq(&rf->sc_dev,
                          msix_vec->ceq_id,
                          msix_vec->idx, false);
        irdma_destroy_irq(rf, msix_vec, rf);
    } else {
        msix_vec = &rf->iw_msixtbl[1];
        irdma_destroy_irq(rf, msix_vec, iwceq);
    }irdma_destroy_ceq(rf, iwceq);
    rf->sc_dev.ceq_valid = false;
    rf->ceqs_count = 0; // 删除ceq0时, 将CEQ计数器设置为0
}

创建CQ时设置ceq_id为comp_vector

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
irdma_create_cq
    ...
    if (attr->comp_vector < rf->ceqs_count)
        info.ceq_id = attr->comp_vector; // 创建CQ时设置ceq_id为comp_vector
    ...
        
int irdma_sc_cq_init(struct irdma_sc_cq *cq, struct irdma_cq_init_info *info)
{
    u32 pble_obj_cnt;
​
    pble_obj_cnt = info->dev->hmc_info->hmc_obj[IRDMA_HMC_IW_PBLE].cnt;
    if (info->virtual_map && info->first_pm_pbl_idx >= pble_obj_cnt)
        return -EINVAL;
​
    cq->cq_pa = info->cq_base_pa;
    cq->dev = info->dev;
    cq->ceq_id = info->ceq_id; // 在cq上设置ceq_id
    info->cq_uk_init_info.cqe_alloc_db = cq->dev->cq_arm_db;
    info->cq_uk_init_info.cq_ack_db = cq->dev->cq_ack_db;
    irdma_uk_cq_init(&cq->cq_uk, &info->cq_uk_init_info);
​
    cq->virtual_map = info->virtual_map;
    cq->pbl_chunk_size = info->pbl_chunk_size;
    cq->ceqe_mask = info->ceqe_mask;
    cq->cq_type = (info->type) ? info->type : IRDMA_CQ_TYPE_IWARP;
    cq->shadow_area_pa = info->shadow_area_pa;
    cq->shadow_read_threshold = info->shadow_read_threshold;
    cq->ceq_id_valid = info->ceq_id_valid;
    cq->tph_en = info->tph_en;
    cq->tph_val = info->tph_val;
    cq->first_pm_pbl_idx = info->first_pm_pbl_idx;
    cq->vsi = info->vsi;return 0;
}

ceq上记录ceq_id

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct irdma_sc_ceq {
    u32 size;
    u64 ceq_elem_pa;
    struct irdma_sc_dev *dev;
    struct irdma_ceqe *ceqe_base;
    void *pbl_list;
    u32 ceq_id; // 记录该ceq_id作为标识
    u32 elem_cnt;
    struct irdma_ring ceq_ring;
    u8 pbl_chunk_size;
    u8 tph_val;
    u32 first_pm_pbl_idx;
    u8 polarity;
    struct irdma_sc_vsi *vsi;
    struct irdma_sc_cq **reg_cq;
    u32 reg_cq_size;
    spinlock_t req_cq_lock; /* protect access to reg_cq array */
    bool virtual_map:1;
    bool tph_en:1;
    bool itr_no_expire:1;
};

初始化CEQ时将ceq记录到dev的ceq数组中

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static int irdma_setup_ceqs(struct irdma_pci_f *rf, struct irdma_sc_vsi *vsi)
{
    u32 i;
    u32 ceq_id;
    struct irdma_ceq *iwceq;
    struct irdma_msix_vector *msix_vec;
    int status;
    u32 num_ceqs;
​
    num_ceqs = min(rf->msix_count, rf->sc_dev.hmc_fpm_misc.max_ceqs);
    i = (rf->msix_shared) ? 1 : 2;
    for (ceq_id = 1; i < num_ceqs; i++, ceq_id++) {
        iwceq = &rf->ceqlist[ceq_id];
        status = irdma_create_ceq(rf, iwceq, ceq_id, vsi); // ceq_id从1开始自增
        if (status) {
            ibdev_dbg(&rf->iwdev->ibdev,
                  "ERR: create ceq status = %d\n", status);
            goto del_ceqs;
        }
        spin_lock_init(&iwceq->ce_lock);
        msix_vec = &rf->iw_msixtbl[i];
        iwceq->irq = msix_vec->irq;
        iwceq->msix_idx = msix_vec->idx;
        status = irdma_cfg_ceq_vector(rf, iwceq, ceq_id, msix_vec);
        if (status) {
            irdma_destroy_ceq(rf, iwceq);
            goto del_ceqs;
        }
        irdma_ena_intr(&rf->sc_dev, msix_vec->idx);
        rf->ceqs_count++;
    }return 0;del_ceqs:
    irdma_del_ceqs(rf);return status;
}
​
​
int irdma_sc_ceq_init(struct irdma_sc_ceq *ceq,
              struct irdma_ceq_init_info *info)
{
    u32 pble_obj_cnt;if (info->elem_cnt < info->dev->hw_attrs.min_hw_ceq_size ||
        info->elem_cnt > info->dev->hw_attrs.max_hw_ceq_size)
        return -EINVAL;if (info->ceq_id >= info->dev->hmc_fpm_misc.max_ceqs)
        return -EINVAL;
    pble_obj_cnt = info->dev->hmc_info->hmc_obj[IRDMA_HMC_IW_PBLE].cnt;if (info->virtual_map && info->first_pm_pbl_idx >= pble_obj_cnt)
        return -EINVAL;
​
    ceq->size = sizeof(*ceq);
    ceq->ceqe_base = (struct irdma_ceqe *)info->ceqe_base;
    ceq->ceq_id = info->ceq_id;
    ceq->dev = info->dev;
    ceq->elem_cnt = info->elem_cnt;
    ceq->ceq_elem_pa = info->ceqe_pa;
    ceq->virtual_map = info->virtual_map;
    ceq->itr_no_expire = info->itr_no_expire;
    ceq->reg_cq = info->reg_cq;
    ceq->reg_cq_size = 0;
    spin_lock_init(&ceq->req_cq_lock);
    ceq->pbl_chunk_size = (ceq->virtual_map ? info->pbl_chunk_size : 0);
    ceq->first_pm_pbl_idx = (ceq->virtual_map ? info->first_pm_pbl_idx : 0);
    ceq->pbl_list = (ceq->virtual_map ? info->pbl_list : NULL);
    ceq->tph_en = info->tph_en;
    ceq->tph_val = info->tph_val;
    ceq->vsi = info->vsi;
    ceq->polarity = 1;
    IRDMA_RING_INIT(ceq->ceq_ring, ceq->elem_cnt);
    ceq->dev->ceq[info->ceq_id] = ceq; // 根据ceq_id递增, 依次将ceq按ceq_id作为索引记录到dev的ceq数组中return 0;
}

销毁CQ时清理CEQE

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static int irdma_destroy_cq(struct ib_cq *ib_cq, struct ib_udata *udata)
{
    struct irdma_device *iwdev = to_iwdev(ib_cq->device);
    struct irdma_cq *iwcq = to_iwcq(ib_cq);
    struct irdma_sc_cq *cq = &iwcq->sc_cq;
    struct irdma_sc_dev *dev = cq->dev;
    struct irdma_sc_ceq *ceq = dev->ceq[cq->ceq_id]; // 根据cq上的ceq_id从dev的ceq数组索引获取ceq
    struct irdma_ceq *iwceq = container_of(ceq, struct irdma_ceq, sc_ceq);
    unsigned long flags;spin_lock_irqsave(&iwcq->lock, flags);
    if (!list_empty(&iwcq->cmpl_generated))
        irdma_remove_cmpls_list(iwcq);
    if (!list_empty(&iwcq->resize_list))
        irdma_process_resize_list(iwcq, iwdev, NULL);
    spin_unlock_irqrestore(&iwcq->lock, flags);irdma_cq_rem_ref(ib_cq);
    wait_for_completion(&iwcq->free_cq);irdma_cq_wq_destroy(iwdev->rf, cq);spin_lock_irqsave(&iwceq->ce_lock, flags);
    irdma_sc_cleanup_ceqes(cq, ceq); // 清理CEQE
    spin_unlock_irqrestore(&iwceq->ce_lock, flags);
    irdma_cq_free_rsrc(iwdev->rf, iwcq);return 0;
}

rdma-core实现

ibv_get_cq_event

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
接口定义:
/**
 * ibv_get_cq_event - Read next CQ event
 * @channel: Channel to get next event from.
 * @cq: Used to return pointer to CQ.
 * @cq_context: Used to return consumer-supplied CQ context.
 *
 * All completion events returned by ibv_get_cq_event() must
 * eventually be acknowledged with ibv_ack_cq_events().
 */
int ibv_get_cq_event(struct ibv_comp_channel *channel,
             struct ibv_cq **cq, void **cq_context);实现:
LATEST_SYMVER_FUNC(ibv_get_cq_event, 1_1, "IBVERBS_1.1",
           int,
           struct ibv_comp_channel *channel,
           struct ibv_cq **cq, void **cq_context)
{
    struct ib_uverbs_comp_event_desc ev;if (read(channel->fd, &ev, sizeof ev) != sizeof ev)
        return -1;*cq         = (struct ibv_cq *) (uintptr_t) ev.cq_handle;
    *cq_context = (*cq)->cq_context;get_ops((*cq)->context)->cq_event(*cq);return 0;
}

perftest

v2.0提交记录

https://github.com/linux-rdma/perftest/commit/d66d7888ec4513ca0fd826216c64e20129d0bef8

  1. All tests use shared resources (functions and structs): 所有测试共享资源(以下方法和结构体)

* alloc_ctx * ctx_set_send_wqes * ctx_set_recv_wqes * run_iter_bw * run_iter_bw_server

  1. All BW tests support multi QPs execution (-q flag).
  2. All BW tests support Dual port mode (-O flag)
  3. All BW tests support Post list WQEs feature (-l flag).
  4. All BW tests support Duration (instead of iterations) mode.(所有 BW 测试都支持持续时间(而不是迭代)模式)
  5. bug fixes in all tests.

Still known issues:

  1. RDMA CM QPs in read and atomic benchmarks.
  2. RDMA CM UD and UC SEND mode.
  3. SEND BW bidirectional mode with UD/UC.

发送和写立即数需要创建接收CQ

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int create_cqs(struct pingpong_context *ctx, struct perftest_parameters *user_param)
{
    int ret;
    int dct_only = 0, need_recv_cq = 0;
    int tx_buffer_depth = user_param->tx_depth;if (user_param->connection_type == DC) {
        dct_only = (user_param->machine == SERVER && !(user_param->duplex || user_param->tst == LAT));
    }if (dct_only)
        tx_buffer_depth = user_param->rx_depth;if ((user_param->connection_type == DC && !dct_only) || (user_param->verb == SEND || user_param->verb == WRITE_IMM))
        need_recv_cq = 1; // 发送和写立即数需要创建接收CQ
​
    ret = create_reg_cqs(ctx, user_param, tx_buffer_depth, need_recv_cq);return ret;
}

创建QP的时候决定RECV_CQ是共用SEND_CQ或者之前单独创建的RECV_CQ

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct ibv_qp* ctx_qp_create(struct pingpong_context *ctx,
        struct perftest_parameters *user_param, int qp_index)
{
    struct ibv_qp* qp = NULL;
    int dc_num_of_qps = user_param->num_of_qps / 2;
​
    int is_dc_server_side = 0;
    struct ibv_qp_init_attr attr;
    memset(&attr, 0, sizeof(struct ibv_qp_init_attr));
    struct ibv_qp_cap *qp_cap = &attr.cap;
​
    #ifdef HAVE_IBV_WR_API
    enum ibv_wr_opcode opcode;
    struct ibv_qp_init_attr_ex attr_ex;
    memset(&attr_ex, 0, sizeof(struct ibv_qp_init_attr_ex));
    #ifdef HAVE_MLX5DV
    struct mlx5dv_qp_init_attr attr_dv;
    memset(&attr_dv, 0, sizeof(attr_dv));
    #ifdef HAVE_OOO_RECV_WRS
    struct mlx5dv_context ctx_dv;
    memset(&ctx_dv, 0, sizeof(ctx_dv));
    #endif
    #endif
    #ifdef HAVE_SRD
    struct efadv_qp_init_attr efa_attr = {};
    #endif
    #endif
    #ifdef HAVE_HNSDV
    struct hnsdv_qp_init_attr hns_attr = {};
    #endif
​
    attr.send_cq = ctx->send_cq;
    attr.recv_cq = (user_param->verb == SEND || user_param->verb == WRITE_IMM) ? ctx->recv_cq : ctx->send_cq; // 创建QP的时候决定RECV_CQ是共用SEND_CQ或者之前单独创建的RECV_CQ
​
    is_dc_server_side = ((!(user_param->duplex || user_param->tst == LAT) &&
                          (user_param->machine == SERVER)) ||
                         ((user_param->duplex || user_param->tst == LAT) &&
                          (qp_index >= dc_num_of_qps)));

中断转忙轮询设计

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
​
run_iter_bw
    ...
if (totccnt < tot_iters || (user_param->test_type == DURATION &&  totccnt < totscnt)) {
                /* Make sure all completions from previous event were polled before waiting for another */
                if (user_param->use_event && ne == 0) {
                    fprintf(stdout, "Client: ctx_notify_events send_channel %s(), %s:%d\n", __FUNCTION__, __FILE__, __LINE__);
                    if (ctx_notify_events(ctx->send_channel)) {
                        fprintf(stderr, "Couldn't request CQ notification\n");
                        return_value = FAILURE;
                        goto cleaning;
                    }
                }
                ne = ibv_poll_cq(ctx->send_cq, CTX_POLL_BATCH, wc);
                if (ne > 0) {
                    for (i = 0; i < ne; i++) {
                        wc_id = (int)wc[i].wr_id;if (wc[i].status != IBV_WC_SUCCESS) {
                            NOTIFY_COMP_ERROR_SEND(wc[i],totscnt,totccnt);
                            return_value = FAILURE;
                            goto cleaning;
                        }
                        int fill = user_param->cq_mod;
                        if (user_param->fill_count && ctx->ccnt[wc_id] + user_param->cq_mod > user_param->iters) {
                            fill = user_param->iters - ctx->ccnt[wc_id];
                        }
                        ctx->ccnt[wc_id] += fill;
                        totccnt += fill;if (user_param->noPeak == OFF) {
                            if (totccnt > tot_iters)
                                user_param->tcompleted[user_param->iters*num_of_qps - 1] = get_cycles();
                            else
                                user_param->tcompleted[totccnt-1] = get_cycles();
                        }

在设置发送WQE中设置发送标记(决定是否生成CQE)

启动参数中设置cq_mod(参数Q)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
case 'Q': CHECK_VALUE(user_param->cq_mod,int,MIN_CQ_MOD,MAX_CQ_MOD,"CQ moderation");
      user_param->req_cq_mod = 1;
      break;

在ctx_set_send_reg_wqes中根据cq_mod设置发送标记

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void ctx_set_send_reg_wqes(struct pingpong_context *ctx,
        struct perftest_parameters *user_param,
        struct pingpong_dest *rem_dest)
{
    int i,j;
    ...
    if ((j + 1) % user_param->cq_mod == 0) { // 已发送个数+1个如果是cq_mod的倍数则设置当前WQE需要产生CQE标记位
        ctx->wr[i*user_param->post_list + j].send_flags = IBV_SEND_SIGNALED;
    } else {
        ctx->wr[i*user_param->post_list + j].send_flags = 0;
    }
    ...
        
最新开源实现:    
if (j == (user_param->post_list - 1)) {
    ctx->wr[i*user_param->post_list + j].next = NULL;
} else {
    ctx->wr[i*user_param->post_list + j].next = &ctx->wr[i*user_param->post_list+j+1];
}
if ((j + 1) % user_param->cq_mod == 0) {
    ctx->wr[i*user_param->post_list + j].send_flags = IBV_SEND_SIGNALED;
    #ifdef HAVE_IBV_WR_API
    ctx->qpx[i]->wr_flags = IBV_SEND_SIGNALED;
    #endif
} else {
...

如果没有设置req_cq_mod, 则设置其为post_list大小(post_list大小必须为cq_mod的倍数)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (user_param->post_list > 1) {
    if (!user_param->req_cq_mod) {
        user_param->cq_mod = user_param->post_list;
        printf(RESULT_LINE);
        printf("Post List requested - CQ moderation will be the size of the post list\n");
    } else if ((user_param->post_list % user_param->cq_mod) != 0) {
        printf(RESULT_LINE);
        fprintf(stderr, " Post list size must be a multiple of CQ moderation\n");
        exit(1);
    }
}

提交记录

https://github.com/linux-rdma/perftest/commit/56d025e4f19a6ebe0aaf45bbc6abf2186aa85dfb

允许在发送列表(post_list)模式下覆盖 CQ 调节(moderation)(控制CQE生成) (https://github.com/linux-rdma/perftest/pull/58) * 使用发送列表(post_list)和 cq mod 修复报告的峰值 BW 如果启用了发送列表(post_list)模式,则每个 post_list 迭代仅获取一次“已发布”时间戳。同样,如果启用了 CQ 调节(控制CQE生成),则每个 cq_mod 迭代仅获取一次完成时间戳。通过考虑此问题并跳过 tposted 和 tcompleted 数组中的空字段来修复报告的峰值 BW。还修复了填充 tcompleted 数组时的错误。签名人:Firas Jahjah * 允许在发送列表(post_list)模式下覆盖 CQ 调节(控制CQE生成) 发布 WQE 列表时,允许指定 CQ 调节(控制CQE生成)而不是覆盖它。为此,每 X WQE 请求一次 SIGNAL,并修复 BW 测试以支持 cq_mod != post_list 的情况

显示生成CQE(explicit)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
explicit CQE (where #tot_iterations % cq_mod != 0).

报告带宽统计时根据cq_mod处理

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (user_param->noPeak == OFF) {
    /* Find the peak bandwidth unless asked not to in command line 查找峰值带宽,除非命令行要求不要这样做 */
    for (i = 0; i < num_of_calculated_iters * num_of_qps; i += user_param->post_list) {
        for (j = ROUND_UP(i + 1, user_param->cq_mod) - 1; j < num_of_calculated_iters * num_of_qps;
                j += user_param->cq_mod) {
            t = (user_param->tcompleted[j] - user_param->tposted[i]) / (j - i + 1);
            if (t < opt_delta)
                opt_delta  = t;
        }
        /* Handle case where CQE was explicitly signaled on last iteration. 处理上次迭代中明确发出 CQE 信号的情况 */
        if ((num_of_calculated_iters * num_of_qps) % user_param->cq_mod) {
            j = num_of_calculated_iters * num_of_qps - 1;
            t = (user_param->tcompleted[j] - user_param->tposted[i]) / (j - i + 1);
            if (t < opt_delta)
                opt_delta  = t;
        }
    }
}

linux

SMC协议中会使用内核中断模式

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int smc_ib_ready_link(struct smc_link *lnk)
{
    struct smc_link_group *lgr = smc_get_lgr(lnk);
    int rc = 0;
​
    rc = smc_ib_modify_qp_init(lnk);
    if (rc)
        goto out;
​
    rc = smc_ib_modify_qp_rtr(lnk);
    if (rc)
        goto out;
    smc_wr_remember_qp_attr(lnk);
    rc = ib_req_notify_cq(lnk->smcibdev->roce_cq_recv,
                  IB_CQ_SOLICITED_MASK); // 调用内核ARM,通知HW产生CQE时, 以中断模式上报
    if (rc)
        goto out;
    rc = smc_wr_rx_post_init(lnk);
    if (rc)
        goto out;
    smc_wr_remember_qp_attr(lnk);if (lgr->role == SMC_SERV) {
        rc = smc_ib_modify_qp_rts(lnk);
        if (rc)
            goto out;
        smc_wr_remember_qp_attr(lnk);
    }
out:
    return rc;
}

阿里erdma设备回调函数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static const struct ib_device_ops erdma_device_ops = {
    .owner = THIS_MODULE,
    .driver_id = RDMA_DRIVER_ERDMA,
    .uverbs_abi_ver = ERDMA_ABI_VERSION,.alloc_hw_port_stats = erdma_alloc_hw_port_stats,
    .alloc_mr = erdma_ib_alloc_mr,
    .alloc_pd = erdma_alloc_pd,
    .alloc_ucontext = erdma_alloc_ucontext,
    .create_cq = erdma_create_cq,
    .create_qp = erdma_create_qp,
    .dealloc_pd = erdma_dealloc_pd,
    .dealloc_ucontext = erdma_dealloc_ucontext,
    .dereg_mr = erdma_dereg_mr,
    .destroy_cq = erdma_destroy_cq,
    .destroy_qp = erdma_destroy_qp,
    .disassociate_ucontext = erdma_disassociate_ucontext,
    .get_dma_mr = erdma_get_dma_mr,
    .get_hw_stats = erdma_get_hw_stats,
    .get_port_immutable = erdma_get_port_immutable,
    .map_mr_sg = erdma_map_mr_sg,
    .mmap = erdma_mmap,
    .mmap_free = erdma_mmap_free,
    .post_recv = erdma_post_recv,
    .post_send = erdma_post_send,
    .poll_cq = erdma_poll_cq,
    .query_device = erdma_query_device,
    .query_gid = erdma_query_gid,
    .query_port = erdma_query_port,
    .query_qp = erdma_query_qp,
    .req_notify_cq = erdma_req_notify_cq, // ARM实现
    .reg_user_mr = erdma_reg_user_mr,
    .modify_qp = erdma_modify_qp,INIT_RDMA_OBJ_SIZE(ib_cq, erdma_cq, ibcq),
    INIT_RDMA_OBJ_SIZE(ib_pd, erdma_pd, ibpd),
    INIT_RDMA_OBJ_SIZE(ib_ucontext, erdma_ucontext, ibucontext),
    INIT_RDMA_OBJ_SIZE(ib_qp, erdma_qp, ibqp),
};ARM实现:
int erdma_req_notify_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags)
{
    struct erdma_cq *cq = to_ecq(ibcq);
    unsigned long irq_flags;
    int ret = 0;spin_lock_irqsave(&cq->kern_cq.lock, irq_flags);notify_cq(cq, (flags & IB_CQ_SOLICITED_MASK) == IB_CQ_SOLICITED);if ((flags & IB_CQ_REPORT_MISSED_EVENTS) && get_next_valid_cqe(cq))
        ret = 1;
​
    cq->kern_cq.notify_cnt++;spin_unlock_irqrestore(&cq->kern_cq.lock, irq_flags);return ret;
}static void notify_cq(struct erdma_cq *cq, u8 solcitied)
{
    u64 db_data =
        FIELD_PREP(ERDMA_CQDB_IDX_MASK, (cq->kern_cq.notify_cnt)) |
        FIELD_PREP(ERDMA_CQDB_CQN_MASK, cq->cqn) |
        FIELD_PREP(ERDMA_CQDB_ARM_MASK, 1) |
        FIELD_PREP(ERDMA_CQDB_SOL_MASK, solcitied) |
        FIELD_PREP(ERDMA_CQDB_CMDSN_MASK, cq->kern_cq.cmdsn) |
        FIELD_PREP(ERDMA_CQDB_CI_MASK, cq->kern_cq.ci);*cq->kern_cq.dbrec = db_data;
    writeq(db_data, cq->kern_cq.db);
}

UCX

SE

UCX中使用SE模式

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ucs_status_t uct_ib_iface_arm_cq(uct_ib_iface_t *iface,
                                 uct_ib_dir_t dir,
                                 int solicited_only)
{
    int ret;
​
    ret = ibv_req_notify_cq(iface->cq[dir], solicited_only); // SE标记
    if (ret != 0) {
        ucs_error("ibv_req_notify_cq("UCT_IB_IFACE_FMT", %d, sol=%d) failed: %m",
                  UCT_IB_IFACE_ARG(iface), dir, solicited_only);
        return UCS_ERR_IO_ERROR;
    }
    return UCS_OK;
}

UCT的IB设备结构中的ARM与IBV_SEND_SOLICITED标记位

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * IB device (corresponds to HCA)
 */
typedef struct uct_ib_device {
    struct ibv_context          *ibv_context;    /* Verbs context */
    uct_ib_device_attr          dev_attr;        /* Cached device attributes */
    uint8_t                     first_port;      /* Number of first port (usually 1) */
    uint8_t                     num_ports;       /* Amount of physical ports */
    ucs_sys_cpuset_t            local_cpus;      /* CPUs local to device */
    int                         async_events;    /* Whether async events are handled */
    int                         max_zcopy_log_sge; /* Maximum sges log for zcopy am */
    UCS_STATS_NODE_DECLARE(stats)
    struct ibv_port_attr        port_attr[UCT_IB_DEV_MAX_PORTS]; /* Cached port attributes */
    uct_ib_pci_id_t             pci_id;          /* PCI identifiers */
    ucs_sys_device_t            sys_dev;         /* System device id */
    double                      pci_bw;          /* Supported PCI bandwidth */
    unsigned                    flags;
    uint8_t                     atomic_arg_sizes;
    uint8_t                     atomic_arg_sizes_be;
    uint8_t                     ext_atomic_arg_sizes;
    uint8_t                     ext_atomic_arg_sizes_be;
    uint8_t                     pci_fadd_arg_sizes;
    uint8_t                     pci_cswap_arg_sizes;
    uint8_t                     atomic_align;
    uint8_t                     lag_level;
    uint8_t                     req_notify_cq_support; /* Also indicates
                                                          IBV_SEND_SOLICITED
                                                          support */ ARM与发送SE标记位同时支持?
    uint8_t                     ordered_send_comp;
    uint64_t                    mr_access_flags;
    uint32_t                    max_inline_data;/* AH hash */
    khash_t(uct_ib_ah)          ah_hash;
    ucs_recursive_spinlock_t    ah_lock;
    /* Async event subscribers */
    ucs_spinlock_t              async_event_lock;
    khash_t(uct_ib_async_event) async_events_hash;
} uct_ib_device_t;

IB协议要求

请求完成通知(11.4.2.2 REQUEST COMPLETION NOTIFICATION)

  • 对于特定的 CQ(particular),每次请求完成通知调用时最多调用一次处理程序
  • 调用 CQ 事件处理程序时,它仅表示已将新条目添加到指定的 CQ(HW先产生CQE, 然后在触发中断到驱动这边处理CEQ, 调用完成回调)

请求在将指定类型的下一个完成条目添加到指定 CQ 时调用 CQ 事件处理程序。对于特定的 CQ(particular),每次请求完成通知调用时最多调用一次处理程序。启用通知之前存在的任何 CQ 条目都不会导致调用处理程序完成事件有两种类型:请求的(solicited)或未经请求(unsolicited)的。当传入的发送或 RDMA 写入即时(Send or RDMA Write with Immediate)数据消息(设置了请求的事件标头位)导致成功的接收工作完成添加到 CQ 时,或者当不成功的工作完成添加到 CQ 时,会发生请求的完成事件(Solicited Completion Event )。当任何其他成功的接收工作完成或任何成功的发送工作完成添加到 CQ 时,会发生未经请求的完成事件。C11-29:CI 应支持请求的和未经请求的完成事件类型(Unsolicited Completion Event)。消费者请求完成通知时,必须指定是否为以下事件调用通知回调:

• 仅限下一个请求的完成事件(next Solicited Completion Event only),或

• 下一个请求或非请求的完成事件(the next Solicited or Unsolicited Completion Event)

C11-29.1.1:当“仅限下一个请求的完成事件”未完成时,CI 应在以下任一情况下调用通知回调:

• 设置了请求的事件标头位的传入发送导致成功的接收工作完成被添加到指定的 CQ(Send with the Solicited Event Header bit set)。

• 设置了请求的事件标头位的传入 RDMA 写入和立即数据导致成功的接收工作完成被添加到指定的 CQ(Write with Immediate Data with the Solicited Event Header bit set )

• 将不成功的发送或接收工作完成添加到指定的 CQ(An unsuccessful Send or Receive Work Completion), 也就是发送或接收失败

C11-29.1.2:当“下一个请求的或非请求的完成事件”未完成时,CI 应在将任何工作完成添加到指定的 CQ 时调用通知回调(只要有CQE产生, 就需要调用一次完成回调)。如果请求完成通知处于待处理状态,则在完成事件之前对同一 CQ 的请求完成通知的后续调用仅在通知发生时才会生效(如果之前的ARM被阻塞, 后续需要等回调完成, ARM才能生效)。下一个完成事件的请求完成(next completion event )通知优先于同一 CQ 的请求事件完成的请求完成通知(next SE)。如果对同一 CQ 进行了多次请求完成通知调用,并且至少有一个请求将类型设置为下一个完成,则在将下一个完成添加到该 CQ 时将调用 CQ 事件处理程序。即使在指定 CQ 的完成事件之前进行了多个 CQ 通知请求,CQ 事件处理程序也只会被调用一次。一旦调用 CQ 事件处理程序,必须先注册另一个完成通知请求,然后才能再次调用 CQ 事件处理程序。C11-30:当 CQ 上请求的完成类型的完成通知请求未完成,并且对该 CQ 进行了另一个指定下一个完成通知的请求时,CI 应将未完成的完成通知类型更改为下一个完成。 C11-31:当 CQ 上下一个完成的完成通知请求未完成,并且对该 CQ 发出了另一个通知请求时,CI 不得更改未完成的完成通知类型。在调用此例程之前,必须指定 CQ 事件处理程序。如果在生成事件时尚未注册 CQ 事件处理程序,则不会进行处理程序调用。调用 CQ 事件处理程序时,它仅表示已将新条目添加到指定的 CQ(HW先产生CQE, 然后在触发中断到驱动这边处理CEQ, 调用完成回调)。HCA 和 CQ 句柄被传递给 CQ 事件处理程序,因此 CQ 事件处理程序可以确定哪个 CQ 导致它被调用。调用处理程序例程后,消费者必须再次调用请求完成通知,以便在向该 CQ 添加新条目时收到通知。消费者有责任调用轮询完成动词来检索工作完成。注意:如果消费者在没有与 CQ 关联的 CQ 事件处理程序 ID 的 CQ 句柄上请求完成通知,则该操作将不起作用。也就是说,不会生成任何完成事件。输入修饰符:• HCA 句柄。• CQ 句柄。• 请求的完成通知类型。类型是下一个完成或请求的完成发生时。输出修饰符:• 动词结果:• 操作已成功完成。• 无效的 HCA 句柄。• 无效的 CQ 句柄。• 无效的完成通知类型

设置完成事件回调(11.5.1 SET COMPLETION EVENT HANDLER)

描述:将完成处理程序标识符(Completion Handler Identifier)与完成事件处理程序地址关联。如果 HCA 支持基本队列管理扩展,则每个 HCA 可以注册多个 CQ 事件处理程序。对于给定的完成处理程序标识符,对此动词的其他调用将覆盖与完成处理程序标识符关联的完成事件处理程序地址。此调用不会自动请求完成事件的通知。必须调用请求完成通知动词才能请求通知。传递给 CQ 事件处理程序的参数包括:• HCA 句柄。• CQ 句柄。输入修饰符:• HCA 句柄。• 完成事件处理程序地址。• 完成事件处理程序标识符:• 如果为零,CI 将创建完成处理程序标识符并分配完成事件处理程序地址。• 如果非零,CI 将替换与完成事件处理程序标识符标识的现有完成处理程序关联的完成事件处理程序地址。如果完成事件处理程序地址为零,则清除完成事件处理程序地址。注意:当 CQ 与已清除的完成事件处理程序相关联时,不得生成完成事件。输出修饰符:• 完成事件处理程序标识符。仅当输入修饰符“完成事件处理程序标识符”设置为零时才返回。• 动词结果:• 操作成功完成。• HCA 句柄无效。• 完成事件处理程序标识符无效。• HCA 不支持基本队列管理扩展 • 资源不足,无法完成请求

设置异步完成事件回调(11.5.2 SET ASYNCHRONOUS EVENT HANDLER)

描述:注册异步事件处理程序。每个 HCA 只能注册一个异步事件处理程序。对此动词的额外调用将覆盖要调用的处理程序例程。额外的调用不会生成额外的处理程序例程。C11-32:即使在已注册现有异步事件处理程序的情况下,CI 也应使用此动词中指定的异步事件处理程序。注册异步事件处理程序后,所有后续异步事件都将导致调用该处理程序。在注册异步事件处理程序之前,异步事件将丢失。调用异步事件处理程序时传递给它的参数包括:• HCA 句柄。• 事件记录。这包含指示资源类型和标识符以及发生哪个事件的信息。有关更多信息,请参阅异步事件。输入修饰符:• HCA 句柄。• 处理程序地址。输出修饰符:• 动词结果:• 操作已成功完成。• 无效的 HCA 句柄

FAQ

  • 何为共享模式(msix_shared)?
  • 如何处理CPU核心数不足(cpu核心比中断向量少)?
  • CEQ0有何作用?

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Intel E810/ICE DPU RDMA 及MLX中断原理分析1(CE/AE)
一旦 E810 返回到 CEQE0,CEQE_Valid 位的极性就会切换,以避免软件需要为每个处理的 CEQE 返回并清除 CEQE_Valid 位(用极性位来提高处理效率)。
晓兵
2024/12/01
2500
Intel E810/ICE DPU RDMA 及MLX中断原理分析1(CE/AE)
Intel E810-iRDMA网卡-Linux内核驱动和用户态源码分析
用户空间直接访问 (UDA) 旨在以通用方式提供用户空间访问队列,但 E810 不支持此功能。 UDA 仅在内核中可用,并且仅限于 iWARP 连接设置和错误处理。 UDA 在用户空间中不可用
晓兵
2024/04/25
1.5K0
Intel E810-iRDMA网卡-Linux内核驱动和用户态源码分析
RDMA_verbs详解-修改队列对属性(ibv_modify_qp)-ceph-ucx-nccl-intel rdma驱动源码解析
QP上可变化的属性描述了QP的发送和接收属性。 在 UC 和 RC QP 中,这意味着将 QP 与远程 QP 连接。 在 Infiniband 中,应向子网管理员 (SA) 执行路径查询,以确定 QP 应配置哪些属性或作为最佳解决方案,使用通信管理器 (CM) 或通用 RDMA CM 代理 (CMA) 连接 QP。 然而,有些应用程序(如ceph)更喜欢自行连接 QP,并通过套接字交换数据来决定使用哪些 QP 属性。 在 RoCE 中,应在连接的 QP 的 QP 属性中配置 GRH,或在 UD QP 的地址句柄(Address Handle)中配置 GRH。 在 iWARP 中,应仅使用通用 RDMA CM 代理 (CMA) 连接 QP。 结构体: struct ibv_qp_attr 描述了队列对QP的属性:
晓兵
2024/02/24
8504
RDMA_verbs详解-修改队列对属性(ibv_modify_qp)-ceph-ucx-nccl-intel rdma驱动源码解析
RDMA - 通信库Libfabric的verbs实现(GDR概览)
Libfabric: https://github.com/ofiwg/libfabric.git
晓兵
2024/12/28
2370
RDMA - 通信库Libfabric的verbs实现(GDR概览)
RDMA - IB SPEC 错误检测和处理以及IntelE810异步事件源码分析
IBA 使用分层错误管理架构 (LEMA) 方法。每个级别负责检测和管理适合该层的错误,然后再将数据包或消息传递到堆栈中的下一层。因此,传输层会响应传输特有的错误,包括数据包头中的错误和无法正确传输消息。在传输层中检测到的错误会报告给传输的客户端。在本节中,传输层与其客户端之间的接口在概念上显示为发送或接收队列。对于 HCA,传输通过将完成代码写入完成队列 (CQ) 上的完成队列条目 (CQE) 来向其客户端指示错误。与往常一样,TCA 可以根据自己的需要自由报告错误(或不报告)。为了简化讨论,将分别讨论请求方和响应方的错误行为。这会导致以下部分中描述请求方和响应方错误的摘要表之间出现少量重复。具体而言,当响应方检测到错误并报告给请求方时,就会发生重叠。然而,这些重叠区域严格限于可靠的服务类别。请求者向其客户端报告的错误分为两类。
晓兵
2024/12/28
1782
RDMA - IB SPEC 错误检测和处理以及IntelE810异步事件源码分析
RDMA_verbs详解-ibv_reg_mr(注册内存)源码分析
博客: https://cloud.tencent.com/developer/user/5060293/articles | https://logread.cn | https://blog.csdn.net/ssbandjl | https://www.zhihu.com/people/ssbandjl/posts
晓兵
2024/03/23
1.4K0
RDMA_verbs详解-ibv_reg_mr(注册内存)源码分析
Linux RDMA RXE/SoftRoCE 软件RoCE-内核驱动源码
rdma_rxe 内核模块提供 RoCEv2 协议的软件实现。 RoCEv2 协议是存在于 UDP/IPv4 或 UDP/IPv6 之上的 RDMA 传输协议。 InfiniBand (IB) 基本传输标头 (BTH) 封装在 UDP 数据包中。 创建 RXE 实例后,通过 RXE 进行通信与通过任何 OFED 兼容的 Infiniband HCA 进行通信相同,尽管在某些情况下会涉及寻址问题。 特别是,虽然 GRH 标头的使用在 IB 子网中是可选的,但对于 RoCE 来说是强制性的。 基于 IB 动词编写的动词应用程序应该可以无缝工作,但它们需要在创建地址向量时提供 GRH 信息。 修改库和驱动程序以提供硬件所需的从 GID 到 MAC 地址的映射
晓兵
2024/04/10
2.1K2
Linux RDMA RXE/SoftRoCE 软件RoCE-内核驱动源码
RDMA的内存管理(IB内核管理用户态内存) - ib_umem
从内核模块暴露IB内存接口: ib_umem_get()/ib_umem_release() ,可让低级驱动程序控制何时调用 ib_umem_get() 来pin和 DMA 映射用户空间,该方案优于以前在调用 ib_uverbs_reg_mr 中的 reg_user_mr 前后的处理逻辑 。 还将这些函数移至 ib_core 模块而不是 ib_uverbs 中,以便使用它们的驱动程序模块不依赖于 ib_uverbs。 这具有许多优点:
晓兵
2024/11/17
3510
RDMA的内存管理(IB内核管理用户态内存) - ib_umem
RDMA - inline 内联提高小包性能-降低时延(减少两个 PCIe 往返延迟)
这篇文章描述了通过 PCI Express 结构在 CPU 和 NIC 之间发生的一系列协调事件,以通过 InfiniBand 互连传输消息并发出其完成信号。
晓兵
2024/06/16
8480
RDMA - inline 内联提高小包性能-降低时延(减少两个 PCIe 往返延迟)
RDMA技术 - 请求事件SE(SOLICITED EVENT)-降低CPU开销
请求事件是一种机制,请求方发送消息,当响应方收到消息时,响应方会生成特殊(即请求的)事件。当工作完成添加到响应方(在接收队列中)的完成队列时,将为消息生成事件,因此它仅对发送(SEND)、立即发送(Send with immediate)和 RDMA 立即写入(Write with immediate)操作有效(因为只有这些操作会在响应方生成工作完成)
晓兵
2024/12/21
1670
RDMA技术 - 请求事件SE(SOLICITED EVENT)-降低CPU开销
分布式存储_高性能RDMA网络_架构设计_性能调优参考_网卡排查命令_笔记
博客: https://logread.cn | https://blog.csdn.net/ssbandjl | https://cloud.tencent.com/developer/user/5060293/articles
晓兵
2023/11/03
4.6K0
分布式存储_高性能RDMA网络_架构设计_性能调优参考_网卡排查命令_笔记
RDMA技术 - Nvidia DPU_MLX5驱动手册 - 完成队列
HCA 实现完成队列,用于在工作请求完成后发布完成报告。本节讨论 CQ 的结构和操作。CQ 是一个包含以下实体的对象:
晓兵
2024/12/21
2890
RDMA技术 - Nvidia DPU_MLX5驱动手册 - 完成队列
Linux源码分析-RDMA的通信连接管理CM模块
RDMA CM 是一种通信管理器,用于设置可靠、连接和不可靠的数据报数据传输。 它提供用于建立连接的 RDMA 传输中立接口。 API 概念基于套接字,但适用于基于队列对 (QP) 的语义:通信必须通过特定的 RDMA 设备进行,并且数据传输基于消息。 RDMA CM 可以控制 RDMA API 的 QP 和通信管理(连接建立/拆除)部分,或者仅控制通信管理部分。 它与 libibverbs 库定义的 verbs API 结合使用。 libibverbs 库提供了发送和接收数据所需的底层接口。 RDMA CM 可以异步或同步操作。 用户通过在特定调用中使用 rdma_cm 事件通道参数来控制操作模式。 如果提供了事件通道,rdma_cm 标识符将报告该通道上的事件数据(例如连接结果)。 如果未提供通道,则所选 rdma_cm 标识符的所有 rdma_cm 操作将被阻止,直到完成。 RDMA CM 为不同的 libibverbs 提供商提供了一个选项来宣传和使用特定于该提供商的各种 QP 配置选项。 此功能称为 ECE(增强连接建立)
晓兵
2024/04/27
1K0
Linux源码分析-RDMA的通信连接管理CM模块
DAOS Mercury(HG) Libfabric(OFI) RDMA 分层verbs接口调用详解
参考之前的分享中, DAOS与RDMA分层关系如下图, DAOS引擎 -> CART(RPC/大块数据/集合RPC请求) -> Mercury(HG: RPC注册/回调/RPC操作/预期/非预期消息/大块消息/轮训/阻塞/网络抽象等) -> Libfabric(网络抽象层,对rxm,rdma verbs, socket, 共享内存等抽象) -> RDMA(verbs) -> rdma-core(用户态驱动) -> RDMA网卡硬件驱动(如:英伟达 mellanox迈络思), 今天咱们来探讨一下分层verbs接口调用详情
晓兵
2023/10/05
9972
DAOS Mercury(HG) Libfabric(OFI) RDMA 分层verbs接口调用详解
RDMA-Linux-infiniband-RDMA子系统-源码分析-IB架构-IB设备初始化和注册-内核uverbs接口注册-GID缓存机制
该驱动程序通过 Linux 网络堆栈实现 InfiniBand RDMA 传输。 它使具有标准以太网适配器的系统能够与 RoCE 适配器或运行 RXE 驱动程序的另一个系统进行互操作。 有关 InfiniBand 和 RoCE 的文档可以从 www.infinibandta.org 和 www.openfabrics.org 下载。 (另请参见 siw,它是 iWARP 的类似软件驱动程序。)该驱动程序分为两层,一层与 Linux RDMA 堆栈接口,并实现内核或用户空间动词 API。 用户空间动词 API 需要一个名为 librxe 的支持库,该支持库由通用用户空间动词 API libibverbs 加载。 另一层与第 3 层的 Linux 网络堆栈接口。要配置和使用 soft-RoCE 驱动程序,请使用“配置 Soft-RoCE (RXE)”部分下的以下 wiki 页面:https://github.com/linux-rdma/rdma-core/blob/master/Documentation/rxe.md
晓兵
2024/04/19
1.7K0
RDMA-Linux-infiniband-RDMA子系统-源码分析-IB架构-IB设备初始化和注册-内核uverbs接口注册-GID缓存机制
Intel E810网卡芯片架构简介及以太内核驱动初始化源码分析
E810 基于 100G 以太网控制器核心模块,该模块用于多种英特尔产品。图 1-1 说明了控制器核心逻辑与组成完整 E810 设备的 I/O 和支持功能之间的关系
晓兵
2024/12/14
3590
Intel E810网卡芯片架构简介及以太内核驱动初始化源码分析
RDMA - ODP按需分页设计原理-优点-源码浅析
HMM: 异构内存管理(Heterogeneous Memory Management)
晓兵
2024/06/14
6420
RDMA - ODP按需分页设计原理-优点-源码浅析
优化 RDMA 代码的建议和技巧-rdma性能优化技巧-避坑指南-RDMA资源
DMA 代表直接内存访问。这意味着应用程序可以在 CPU 干预的情况下直接访问(读/写)主机内存。如果您在主机之间执行此操作,它将成为远程直接内存访问 (RDMA)
晓兵
2023/12/19
1.8K2
优化 RDMA 代码的建议和技巧-rdma性能优化技巧-避坑指南-RDMA资源
DAOS_分布式存储_mercury_libfabric_rdma_rpc高性能网络_大块数据_bulk传输_中断_轮询自动切换_等笔记
博客: https://logread.cn | https://blog.csdn.net/ssbandjl | https://cloud.tencent.com/developer/user/5060293/articles
晓兵
2023/11/03
3940
DAOS_分布式存储_mercury_libfabric_rdma_rpc高性能网络_大块数据_bulk传输_中断_轮询自动切换_等笔记
RDMA - GDR GPU Direct RDMA快速入门1
NVIDIA GPUDirect 是一系列技术, 用于增强 GPU间(P2P)或GPU与第三方设备(RDMA)间的数据移动和访问, 无论您是在探索海量数据、研究科学问题、训练神经网络还是为金融市场建模,您都需要一个具有最高数据吞吐量的计算平台。GPU 的数据处理速度比 CPU 快得多,随着 GPU 计算能力的提高,对 IO 带宽的需求也随之增加。NVIDIA GPUDirect®是Magnum IO的一部分,可增强 NVIDIA 数据中心 GPU 的数据移动和访问。使用 GPUDirect,网络适配器和存储驱动器可以直接读取和写入 GPU 内存,从而消除不必要的内存复制、减少 CPU 开销和延迟,从而显着提高性能。这些技术(包括 GPUDirect Storage(GDS)、GPUDirect RDMA(GDR)、GPUDirect 点对点 (P2P) 和 GPUDirect Video)通过一套全面的 API 呈现
晓兵
2025/03/30
1520
RDMA - GDR GPU Direct RDMA快速入门1
推荐阅读
相关推荐
Intel E810/ICE DPU RDMA 及MLX中断原理分析1(CE/AE)
更多 >
领券
社区富文本编辑器全新改版!诚邀体验~
全新交互,全新视觉,新增快捷键、悬浮工具栏、高亮块等功能并同时优化现有功能,全面提升创作效率和体验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文