【C语言】InfiniBand内核驱动_mlx4_ib_post_send

一、注释

以下是`_mlx4_ib_post_send`函数的注释,该函数用于处理InfiniBand工作请求(WRs)的发送过程:

static int _mlx4_ib_post_send(struct ib_qp *ibqp, const struct ib_send_wr *wr,const struct ib_send_wr **bad_wr, bool drain)
{struct mlx4_ib_qp *qp = to_mqp(ibqp); // 从 ib_qp 结构转换为 mlx4_ib_qp 结构void *wqe; // 工作队列元素(WQE)struct mlx4_wqe_ctrl_seg *uninitialized_var(ctrl); // 控制段,初始化状态未确定struct mlx4_wqe_data_seg *dseg;unsigned long flags;int nreq; // 请求计数int err = 0; // 错误码,默认为0unsigned ind; // 索引int uninitialized_var(size); // 大小,初始化状态未确定unsigned uninitialized_var(seglen); // 数据段长度,初始化状态未确定__be32 dummy; // 用于LSO(Large Send Offload)WQE__be32 *lso_wqe; // 指向LSO WQE__be32 uninitialized_var(lso_hdr_sz); // LSO头部大小,初始化状态未确定__be32 blh;int i;int inl = 0; // 是否是内联struct mlx4_ib_dev *mdev = to_mdev(ibqp->device); // 从 ib_device 转换为 mlx4_ib_devif (qp->mlx4_ib_qp_type == MLX4_IB_QPT_GSI) {// 处理特定类型的队列对应的特殊情形}// 锁住发送队列以防止并发访问spin_lock_irqsave(&qp->sq.lock, flags);if (mdev->dev->persist->state & MLX4_DEVICE_STATE_INTERNAL_ERROR && !drain) {// 如果设备内部出错且不是清空队列,则返回错误err = -EIO;*bad_wr = wr; // 设置出错的WRnreq = 0;goto out; // 跳出执行}ind = qp->sq_next_wqe; // 获取下一个WQE的索引// 循环处理每个发送WRfor (nreq = 0; wr; ++nreq, wr = wr->next) {// 对每个工作请求(WR)执行发送处理}// 完成所有请求的处理后,发送通知并更新WQE
out:if (nreq == 1 && inl && size > 1 && size < qp->bf.buf_size / 16) {// 如果只有一个请求且为内联,则进行优化处理} else if (nreq) {qp->sq.head += nreq; // 增加头部索引// 确保描述符写入完成后更新doorbell记录wmb();// 写入doorbell以通知硬件可以开始执行__raw_writel((__force u32)qp->doorbell_qpn, qp->bf.uar->map + MLX4_SEND_DOORBELL);}if (likely(nreq)) {// 最后,更新WQE并解锁}spin_unlock_irqrestore(&qp->sq.lock, flags); // 解锁发送队列return err; // 返回错误码
}

这个函数的主要工作流程是:
1. 准备发送队列和资源。
2. 对输入的工作请求(WRs)进行循环处理。
3. 处理每一个WR的发送逻辑,并构造WQEs。
4. 更新发送队列的状态和索引。
5. 如果所有WQEs都正确构造,则通知硬件进行处理。
6. 最后,解锁发送队列并返回。
它包括了很多细节,如错误处理、优化路径、数据准备、判断发送的消息类型等。需要注意,这个函数可能修改`qp`结构体内的状态,并在出现错误时适当设置错误代码和`bad_wr`。

// _mlx4_ib_post_send函数是处理Infiniband Work Request发送操作的函数
// 该函数以Mellanox的mlx4网卡为例展示了如何将IB操作转换为HW可以理解的WQE(Work Queue Element)格式
static int _mlx4_ib_post_send(struct ib_qp *ibqp, const struct ib_send_wr *wr,const struct ib_send_wr **bad_wr, bool drain)
{// 将通用的ib_qp指针转换为mlx4_ib_qp结构指针struct mlx4_ib_qp *qp = to_mqp(ibqp);void *wqe;// 未初始化的控制段指针struct mlx4_wqe_ctrl_seg *uninitialized_var(ctrl);struct mlx4_wqe_data_seg *dseg;unsigned long flags;int nreq;// 错误码初始为0int err = 0;unsigned ind;// 未初始化的变量,用于存储WQE大小int uninitialized_var(size);// 未初始化的变量,用于存储段长度unsigned uninitialized_var(seglen);__be32 dummy;__be32 *lso_wqe;// 未初始化的变量,用于存储LSO头部大小__be32 uninitialized_var(lso_hdr_sz);__be32 blh;int i;// 是否执行内存内发送int inl = 0;// 获取mlx4_ib设备指针struct mlx4_ib_dev *mdev = to_mdev(ibqp->device);// 如果QP类型为GSI(Global Service Interface),则执行特殊处理if (qp->mlx4_ib_qp_type == MLX4_IB_QPT_GSI) {struct mlx4_ib_sqp *sqp = to_msqp(qp);// 处理RoCE v2 GSI特殊情况if (sqp->roce_v2_gsi) {struct mlx4_ib_ah *ah = to_mah(ud_wr(wr)->ah);enum ib_gid_type gid_type;union ib_gid gid;// 对多功能设备执行特殊处理if (mlx4_is_mfunc(mdev->dev)) {enum mlx4_roce_gid_type roce_gid_type;union ib_gid sgid;// 从缓存中获取RoCE GID信息,而不是直接使用硬件err = mlx4_get_roce_gid_from_slave(mdev->dev,be32_to_cpu(ah->av.ib.port_pd) >> 24,ah->av.ib.gid_index, &sgid.raw[0], &roce_gid_type);gid_type = mlx4_gid_type_to_ib_gid_type(roce_gid_type);} else  {err = fill_gid_by_hw_index(mdev, sqp->qp.port,ah->av.ib.gid_index,&gid, &gid_type);}// 如果是RoCE v2,则切换到roce_v2_gsi QPqp = (gid_type == IB_GID_TYPE_ROCE_UDP_ENCAP) ?to_mqp(sqp->roce_v2_gsi) : qp;}}// 上锁以保护发送队列修改操作spin_lock_irqsave(&qp->sq.lock, flags);// 如果设备处于内部错误状态,并且非排空模式,则直接返回输入/输出错误if (mdev->dev->persist->state & MLX4_DEVICE_STATE_INTERNAL_ERROR && !drain) {err = -EIO;// 设置bad_wr指向出错的WR*bad_wr = wr;nreq = 0;goto out;}// 获取下一个WQE索引ind = qp->sq_next_wqe;// 遍历所有的WR,构造WQE并更新到硬件for (nreq = 0; wr; ++nreq, wr = wr->next) {// 储存相关信息,稍后用于构建LSO WQElso_wqe = &dummy;blh = 0;// 如果发送队列溢出,则返回空间不足错误if (mlx4_wq_overflow(&qp->sq, nreq, qp->ibqp.send_cq)) {err = -ENOMEM;*bad_wr = wr;goto out; 
}// 如果发送段的数量超过了队列的最大允许值,返回错误if (unlikely(wr->num_sge > qp->sq.max_gs)) {err = -EINVAL;*bad_wr = wr;goto out;}// 获取当前WQE在队列中的位置ctrl = wqe = get_send_wqe(qp, ind & (qp->sq.wqe_cnt - 1));// 初始化vlan标签为0((u32 )(&ctrl->qpn_vlan.vlan_tag)) = 0;// 记录当前工作请求的IDqp->sq.wrid[(qp->sq.head + nreq) & (qp->sq.wqe_cnt - 1)] = wr->wr_id;// 设置控制段的标志位,指示是否需要成帧和Solicited事件ctrl->srcrb_flags =(wr->send_flags & IB_SEND_SIGNALED ?cpu_to_be32(MLX4_WQE_CTRL_CQ_UPDATE) : 0) |(wr->send_flags & IB_SEND_SOLICITED ?cpu_to_be32(MLX4_WQE_CTRL_SOLICITED) : 0) |((wr->send_flags & IB_SEND_IP_CSUM) ?cpu_to_be32(MLX4_WQE_CTRL_IP_CSUM |MLX4_WQE_CTRL_TCP_UDP_CSUM) : 0) |qp->sq_signal_bits;// 设置立即数,如果包含在wr发送标志中ctrl->imm = send_ieth(wr);// 准备后续段所需的空间wqe += sizeof *ctrl;size = sizeof *ctrl / 16;// 根据QP类型和操作码,修改WQE以适配不同类型的请求switch (qp->mlx4_ib_qp_type) {// RC和UC类型QP的处理case MLX4_IB_QPT_RC:case MLX4_IB_QPT_UC:switch (wr->opcode) {case IB_WR_ATOMIC_CMP_AND_SWP:case IB_WR_ATOMIC_FETCH_AND_ADD:case IB_WR_MASKED_ATOMIC_FETCH_AND_ADD:// 为原子操作设置远程地址段和原子段set_raddr_seg(wqe, atomic_wr(wr)->remote_addr,atomic_wr(wr)->rkey);wqe  += sizeof(struct mlx4_wqe_raddr_seg);set_atomic_seg(wqe, atomic_wr(wr));wqe  += sizeof(struct mlx4_wqe_atomic_seg);size += (sizeof(struct mlx4_wqe_raddr_seg) +sizeof(struct mlx4_wqe_atomic_seg)) / 16;break;case IB_WR_MASKED_ATOMIC_CMP_AND_SWP:// 类似地,为掩码原子操作设置段set_raddr_seg(wqe, atomic_wr(wr)->remote_addr,atomic_wr(wr)->rkey);wqe  += sizeof(struct mlx4_wqe_raddr_seg);set_masked_atomic_seg(wqe, atomic_wr(wr));wqe  += sizeof(struct mlx4_wqe_masked_atomic_seg);size += (sizeof(struct mlx4_wqe_raddr_seg) +sizeof(struct mlx4_wqe_masked_atomic_seg)) / 16;break;case IB_WR_RDMA_READ:case IB_WR_RDMA_WRITE:case IB_WR_RDMA_WRITE_WITH_IMM:// 为RDMA Read/Write设置远程地址段set_raddr_seg(wqe, rdma_wr(wr)->remote_addr,rdma_wr(wr)->rkey);wqe  += sizeof(struct mlx4_wqe_raddr_seg);size += sizeof(struct mlx4_wqe_raddr_seg) / 16;break;case IB_WR_LOCAL_INV:// 本地无效操作的控制段设置ctrl->srcrb_flags |=cpu_to_be32(MLX4_WQE_CTRL_STRONG_ORDER);set_local_inv_seg(wqe, wr->ex.invalidate_rkey);wqe  += sizeof(struct mlx4_wqe_local_inval_seg);size += sizeof(struct mlx4_wqe_local_inval_seg) / 16;break;case IB_WR_REG_MR:// 用于注册内存区域的操作ctrl->srcrb_flags |=cpu_to_be32(MLX4_WQE_CTRL_STRONG_ORDER);set_reg_seg(wqe, reg_wr(wr));wqe  += sizeof(struct mlx4_wqe_fmr_seg);size += sizeof(struct mlx4_wqe_fmr_seg) / 16;break;default:// 其他情况不需要额外的段break;}break;// 所有单播和多播数据包都通过特定于设备的QPs进行路由// QP0处理SMI(子管理器接口),而QP1处理GSI(全局服务接口))// 下面是特殊的QP类型的处理// 在MLX4_IB_QPT_TUN_SMI_OWNER和以下类型中,需要构建不同类型的WQE// 省略其他特定处理...(请根据需要增补代码注释)// 提交WQE后的一些尾部处理// 写入数据段,逆序写入,从而最后一个写操作会覆写每个cache line内的时间戳// 这种写入顺序是为了避免WQE预取导致的问题dseg = wqe;dseg += wr->num_sge - 1;// 对于MLX类型的发送,还需要添加一个用于ICRC // 根据QP的不同类型,处理不同的WR(Work Request)操作switch (qp->mlx4_ib_qp_type) {// 略过前面的case...case MLX4_IB_QPT_TUN_SMI_OWNER:// 如果QP是用于管理和配置子虚拟机(SVM)的特殊QP// 构建用于代理SMI所有者的Infiniband消息头err =  build_sriov_qp0_header(to_msqp(qp), ud_wr(wr),ctrl, &seglen);if (unlikely(err)) {// 如果构建消息头失败,记录错误的Work Request然后退出*bad_wr = wr;goto out;}// 更新wqe指针和size到正确的位置wqe  += seglen;size += seglen / 16;break;case MLX4_IB_QPT_TUN_SMI:case MLX4_IB_QPT_TUN_GSI:// 当这个QP是用于子虚拟机(SVM)回复MADs(管理数据)时的User Datagram(QP类型为UD),// 对一个数据报的Segment执行设置set_datagram_seg(wqe, ud_wr(wr));// 在数据段的AV中设置强制环回位*(__be32 *) wqe |= cpu_to_be32(0x80000000);// 更新wqe指针和size到正确的位置wqe  += sizeof (struct mlx4_wqe_datagram_seg);size += sizeof (struct mlx4_wqe_datagram_seg) / 16;break;case MLX4_IB_QPT_UD:// 对UD类型的QP设置数据报Segmentset_datagram_seg(wqe, ud_wr(wr));// 更新wqe指针和size到正确的位置wqe  += sizeof (struct mlx4_wqe_datagram_seg);size += sizeof (struct mlx4_wqe_datagram_seg) / 16;// 如果WR的操作码是LSO,则构建LSO Segmentif (wr->opcode == IB_WR_LSO) {err = build_lso_seg(wqe, ud_wr(wr), qp, &seglen,&lso_hdr_sz, &blh);if (unlikely(err)) {// 如果构建LSO Segment失败,记录错误的Work Request然后退出*bad_wr = wr;goto out;}lso_wqe = (__be32 *) wqe;// 更新wqe指针和size到正确的位置wqe  += seglen;size += seglen / 16;}break;case MLX4_IB_QPT_PROXY_SMI_OWNER:// 对于代理SMI所有者的QP构建Infiniband消息头err = build_sriov_qp0_header(to_msqp(qp), ud_wr(wr),ctrl, &seglen);if (unlikely(err)) {// 如果构建消息头失败,记录错误的Work Request然后退出*bad_wr = wr;goto out;}// 更新wqe指针和size到正确的位置wqe  += seglen;size += seglen / 16;// 在高速缓存线的边界上开始隧道头部,添加16字节的空inline数据以实现对齐add_zero_len_inline(wqe);wqe += 16;size++;// 构建隧道头部build_tunnel_header(ud_wr(wr), wqe, &seglen);// 更新wqe指针和size到正确的位置wqe  += seglen;size += seglen / 16;break;case MLX4_IB_QPT_PROXY_SMI:case MLX4_IB_QPT_PROXY_GSI:// 如果这是代理特殊QP类型的QP,首先添加指向隧道QP的UD Segmentset_tunnel_datagram_seg(to_mdev(ibqp->device), wqe,ud_wr(wr),qp->mlx4_ib_qp_type);// 更新wqe指针和size到正确的位置wqe  += sizeof (struct mlx4_wqe_datagram_seg);size += sizeof (struct mlx4_wqe_datagram_seg) / 16;// 然后添加带有地址信息的头部build_tunnel_header(ud_wr(wr), wqe, &seglen);// 更新wqe指针和size到正确的位置wqe  += seglen;size += seglen / 16;break;case MLX4_IB_QPT_SMI:case MLX4_IB_QPT_GSI:// 对于SMI和GSI类型的QP,构建mlx消息头err = build_mlx_header(to_msqp(qp), ud_wr(wr), ctrl,&seglen);if (unlikely(err)) {// 如果构建消息头失败,记录错误的Work Request然后退出*bad_wr = wr;goto out;}// 更新wqe指针和size到正确的位置wqe  += seglen;size += seglen / 16;break;// 之后的default省略...}// 略过对数据段写入的部分代码...out:// 错误处理和退出逻辑,略...return err;
}/** 以相反的顺序写入数据段,以便在每个缓存行内最后覆盖缓存行标记。* 这样可以避免WQE预取时出现的问题。*/dseg = wqe;dseg += wr->num_sge - 1;/* 添加额外的内联数据段,用于MLX发送的ICRC */if (unlikely(qp->mlx4_ib_qp_type == MLX4_IB_QPT_SMI ||qp->mlx4_ib_qp_type == MLX4_IB_QPT_GSI ||qp->mlx4_ib_qp_type &(MLX4_IB_QPT_PROXY_SMI_OWNER | MLX4_IB_QPT_TUN_SMI_OWNER))) {set_mlx_icrc_seg(dseg + 1);size += sizeof (struct mlx4_wqe_data_seg) / 16;}if (wr->send_flags & IB_SEND_INLINE && wr->num_sge) {int sz;// 如果指定了使用内联发送,并且有SGE(Scatter/Gather元素),则安置内联数据err = lay_inline_data(qp, wr, wqe, &sz);if (err) {*bad_wr = wr;goto out;}inl = 1;size += sz;} else {// 根据SGE数量更新WQE大小size += wr->num_sge *(sizeof(struct mlx4_wqe_data_seg) / 16);// 以相反顺序设置数据段for (i = wr->num_sge - 1; i >= 0; --i, --dseg)set_data_seg(dseg, wr->sg_list + i);}// 强制内存屏障,确保所有的数据段都被写入之后再写入LSO段wmb();// 可能会用LSO段覆盖之前的缓存行标记*lso_wqe = lso_hdr_sz;// 设置WQE控制段的大小和栅栏位标志ctrl->qpn_vlan.fence_size = (wr->send_flags & IB_SEND_FENCE ?MLX4_WQE_CTRL_FENCE : 0) | size;/** 在设置所有权位之前,确保描述符完全写入到内存。* 因为硬件可能在我们设置所有权位之后就开始执行描述符。*/wmb();if (wr->opcode < 0 || wr->opcode >= ARRAY_SIZE(mlx4_ib_opcode)) {*bad_wr = wr;err = -EINVAL;goto out;}ctrl->owner_opcode = mlx4_ib_opcode[wr->opcode] |(ind & qp->sq.wqe_cnt ? cpu_to_be32(1 << 31) : 0) | blh;/** 通过在响铃门铃之后才标记最后一个发送队列WQE,我们可以提高延迟性能,* 因此,仅当还有更多WQE要发布时才能在这里标记。*/if (wr->next)stamp_send_wqe(qp, ind + qp->sq_spare_wqes);ind++;}
out:// 如果只发送了一个请求,并且使用内联方式且WQE大小小于缓冲区,// 可以直接把控制段写入BlueFlame页来提高吞吐量if (nreq == 1 && inl && size > 1 && size < qp->bf.buf_size / 16) {// 设置门铃QPN,用于在硬件上标志这个QP是准备发送的ctrl->owner_opcode |= htonl((qp->sq_next_wqe & 0xffff) << 8);// 设置VLAN标签,门铃QPN低位是之前已经设置为0的,所以直接用或赋值是正确的*(__be32 *)(&ctrl->qpn_vlan.vlan_tag) |= qp->doorbell_qpn;// 确保描述符已经刷新到内存,然后才写入蓝色火焰页wmb();// 在发送队列中移动head指针++qp->sq.head;// 将控制段复制到注册寄存器中,在64字节对齐mlx4_bf_copy(qp->bf.reg + qp->bf.offset, (unsigned long *)ctrl,ALIGN(size * 16, 64));// 再次确保写操作已完成wc_wmb();// 反转BlueFlame页的偏移值,因为有可能有两个缓冲区按顺序使用qp->bf.offset ^= qp->bf.buf_size;} else if (nreq) {// 如果有多个请求,更新发送队列头部,准备发送下一批请求qp->sq.head += nreq;// 确保描述符写在门铃记录前wmb();// 写门铃以通知硬件可以发送数据了__raw_writel((__force u32)qp->doorbell_qpn,qp->bf.uar->map + MLX4_SEND_DOORBELL);}// 如果发送了请求,给发送队列最后一个WQE打上时间戳,并更新下一个WQE索引if (likely(nreq)) {stamp_send_wqe(qp, ind + qp->sq_spare_wqes - 1);qp->sq_next_wqe = ind;}// 解锁,发送操作完成spin_unlock_irqrestore(&qp->sq.lock, flags);// 返回错误码,如果没有发送错误,将返回0,意味着成功return err;
}

在上面这段代码中,`out`标签之后的逻辑是在所有的请求被处理完毕之后执行的清理和终结操作。这部分代码主要是负责处理数据发送后的一些状态更新,如合理地更新队列头指针、撤销锁及通知硬件可以进行数据发送等。同时,根据不同情况(如是否只有一个请求,请求是否使用了内联),代码对性能的优化手段也有所不同。返回值`err`代表整个发送操作的结果,如果整个操作无误,则返回0,表示成功执行。

二、讲解

该函数 _mlx4_ib_post_send() 是在 Mellanox 的 IB (InfiniBand) 驱动 mlx4_ib 模块中实现的,主要负责将 IB (InfiniBand) QP (Queue Pair) 的发送工作请求 (Work Request) 提交到硬件层。这个过程包括构建 WQE (Work Queue Element) 的各种段并将其放入发送队列中。在硬件处理完成后,关联的 CQ (Completion Queue) 将得到通知。
下面是对代码的中文逐步解读:
1. 首先,函数接受几个参数,包括:
- ibqp:一个指向所涉及的 IB (InfiniBand) QP (Queue Pair) 的指针。
- wr:指向用户提交的发送工作请求(Work Request)的指针。
- bad_wr:如果发送过程中遇到错误,这个参数会指向发生错误的工作请求。
- drain:一个布尔值,用来标记是否应该“排空”(清除)QP中的所有工作请求。
2. 函数体内部,定义了多个局部变量来构建 WQE (Work Queue Element),同时获取 IB 设备和 QP 的内部表示。
3. spin_lock_irqsave(&qp->sq.lock, flags); 用来获取发送队列的自旋锁以保护共享数据结构。
4. 函数遍历由 wr 指定的工作请求列表,为每个工作请求构建一个或多个 WQE 段。不同类型的操作,如原子操作 (ATOMIC)、RDMA 读写、发送带有 IMM 数据等将需要不同的 WQE 段来描述。
5. 每次循环的内部逻辑主要包括:
- 检查工作请求的有效性。
- 构建控制段(`ctrl` segment),它包括请求的类型,是否需要生成完成通知等信息。
- 根据操作类型 (wr->opcode) 添加特定的段,例如寻址信息 (raddr segment) 对于 RDMA 和原子操作、数据段 (dseg segment) 对于包含数据的请求。
6. 对于数据包含的发送请求,可能会选择以内联方式(`IB_SEND_INLINE` 标志)发送数据,这表示将数据直接放在 WQE 中而不通过外部的内存缓冲区。
7. 一旦 WQE 构建完成,会执行内存障碍操作以确保 WQE 写入内存后再更新硬件。
8. 然后,根据是否使用 BlueFlame (BF) 门铃机制来决定如何通知硬件处理新的 WQE。如果使用 BF 机制,它将 WQE 直接拷贝到一特定内存区域中,不使用 BF 则是通过写入相应的门铃记录来通知硬件。
9. 最后,函数释放自旋锁并返回执行结果。如果函数在处理 WRs 时遇到错误,则 err 变量会记录错误代码,`bad_wr` 会指向出错的 WR。
请注意,这个函数是 Mellanox InfiniBand 驱动的内部实现细节之一,它依赖于特定的硬件和驱动程序架构,非常复杂且难以理解,除非您熟悉 RDMA 编程和 InfiniBand 协议。上述解释提供了该函数的高级概述。

ChatGPT

在InfiniBand架构下,MLX4是指Mellanox Technologies提供的一种网络适配器硬件,在Linux内核中,`mlx4_ib`是Mellanox InfiniBand协议驱动的一部分,它主要用于管理和配置Mellanox网络设备。
mlx4_ib_post_send函数是该驱动中用于处理发送操作的函数之一。这个函数的作用是将一个或多个发送请求(通常是WR,Work Request)放入一个指定的发送队列(Send Queue)。发送请求一旦被加入队列,硬件就会处理它,并最终将数据发送出去。
这个函数的原型可能如下所示(根据不同版本的Linux内核和Mellanox驱动,原型可能略有不同):

int mlx4_ib_post_send(struct ib_qp *qp, const struct ib_send_wr *wr,const struct ib_send_wr **bad_wr);

参数说明:
- qp:指向`ib_qp`结构的指针,它代表一个队列对(Queue Pair),在InfiniBand通信中用来表示连接的一个端点。
- wr:指向`ib_send_wr`结构的指针,即要发送的工作请求。它可以是一个单独的请求,也可以是多个请求的链表。
- bad_wr:如果函数失败,这个参数会指向中断处理的那个工作请求。
如果函数成功执行,它会返回0;如果失败,则返回负的错误码,并且`bad_wr`将会指向导致问题的工作请求。
对`mlx4_ib_post_send`函数的修改通常需要深入的内核编程经验,以及对InfiniBand架构和具体的硬件实现有深入理解。如果你需要对这个函数进行调试或修改,应该参考具体的Mellanox文档及内核源码,以便了解该函数的具体实现细节以及与其他内核组件的交互方式。

通义灵码

在InfiniBand硬件架构中,mlx4_ib_post_send 函数通常是 Mellanox Technologies 所开发的 InfiniBand 驱动(mlx4 驱动)的一部分,用于处理发送数据包到远程节点的过程。这个函数通常位于 mlx4_ib.c 文件中,它是 Linux 内核模块实现的一部分,负责将用户空间的数据缓冲区映射到硬件传输队列,并通过 InfiniBand HCA(Host Channel Adapter)硬件进行实际的数据发送操作。

mlx4_ib_post_send 函数的主要任务包括但不限于以下几点:

  1. 解析工作请求(Work Request, WR):根据IB verbs接口规范接收来自上层协议栈或应用程序的工作请求结构体,比如RDMA Send/Write/Read请求。

  2. 设置发送描述符(Work Queue Entry, WQE):基于接收到的请求信息,填充硬件传输队列中的一个或多个描述符,这些描述符包含了诸如目标QPN(Queue Pair Number)、传输长度、LKey(Local Key)等相关信息。

  3. 内存映射与同步:如果必要的话,确保用户空间的数据已经被正确地映射到HCA可访问的地址空间,并完成必要的内存一致性同步。

  4. 向硬件提交发送:最后,函数会将填充好的WQE提交到HCA的发送队列中,触发硬件开始执行数据传输。

  5. 错误处理:检查潜在的错误条件,并在发生错误时采取适当的动作,如更新状态、返回错误代码等。

由于具体实现细节依赖于驱动的具体版本以及InfiniBand设备特性,实际的函数签名和内部逻辑可能会有所不同。但上述概述描绘了这类函数的核心功能和作用。如果你有具体的代码片段或者更详细的问题,可以进一步讨论。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/787118.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PS从入门到精通视频各类教程整理全集,包含素材、作业等(5)

PS从入门到精通视频各类教程整理全集&#xff0c;包含素材、作业等 最新PS以及插件合集&#xff0c;可在我以往文章中找到 由于阿里云盘有分享次受限制和文件大小限制&#xff0c;今天先分享到这里&#xff0c;后续持续更新 初寒调色案例及练习图 等文件 https://www.alipan.…

记一次使用spring事件机制失效排查修复

前言 在日常业务开发中过程&#xff0c;我们有时候为了业务解耦&#xff0c;会利用spring的机制&#xff0c;就是利用spring提供的ApplicationListener、ApplicationEventMulticaster等核心API来实现。&#xff08;注&#xff1a; 我这边列的是核心底层API接口&#xff0c;正常…

电脑常见故障检测方法与对应问题分析说明

电脑常见故障检测方法与对应问题分析说明 前言说明1、机器无法开机故障2、屏幕无法显示3、无法联网4、能开机但是无法进入系统&#xff0c;提示not boot5、USB接口无法识别U盘 前言说明 本文为小白向&#xff0c;许多内容属于经验学而非科学&#xff0c;还望大佬们轻喷。 如上…

Word中使用Zotero插入文献

1. word 里面有插件 2. 插入文献 2.1 点击【Add/Edit Citation】 2.2 在方条中输入文献,回车 3. 显示Reference 在文档中选好reference的位置, 点击【Add/Edit Bibliograph

pygwalker+streamlit python看板库使用体验

算作前言 在 B 站看到 pygwalker 的介绍&#xff0c;很感兴趣。 是一个类似于简化版的 tableau 工具。 原版 docs PyGWalker 文档 – Kanaries 搭建看板 直接结合 streamlit 使用&#xff0c;streamlit 真的神器。 import pygwalker as pyg import pandas as pd import str…

分布式全闪占比剧增 152%,2023 年企业存储市场报告发布

近日&#xff0c;IDC 发布了 2023 年度的中国存储市场报告。根据该报告&#xff0c;在 2023 年软件定义存储的市场占比进一步扩大&#xff0c;分布式全闪的增长尤其亮眼&#xff0c;其市场份额从 2022 年的 7% 剧增到 2023 年的 17.7%&#xff0c;增长了 152%。 01 中国企业存…

递归遍历目录结构和树状展现

在D盘下创建文件夹“电影”&#xff0c;在文件夹“电影”下创建“华语”、“好莱坞”&#xff0c;在文件夹“华语”下创建文件“人民的名义.mp4”、“天安门传奇.mp4”、“程序员统治世界.mp4”&#xff0c;在文件夹“好莱坞”下创建文件“国王的演讲.mp4”、“速度与激情8.mp4…

Java接口与继承实践:Ether通信系统的构建(day16)

创建一个接口Icontroller, 再创建一个接口IReceiver, 创建一个子类实现IReceiver&#xff0c; 创建一个子类实现IContrller&#xff0c; 创建一个类Ether 创建一个Signal类 创建一个类Radiosignal继承Signal 创建一个用户User 最后创建一个Main类 今日总结&#xff1a…

梵宁教育课程介绍:设计之路,从这里开始

梵宁教育作为一家靠谱且正规的线上教育机构&#xff0c;在师资力量、教学内容、教学方式、服务保障以及社会责任等方面都表现出色。他们致力于为广大学员提供高质量的学习设计教育服务&#xff0c;帮助他们掌握核心知识和技能&#xff0c;实现个人价值和职业梦想。 梵宁教育的课…

【Android Studio3.5.2安装以及错误错误解决】

前言 下面是博主在安装Android studio时遇到的一些问题&#xff0c;并且花费很长时间寻找解决方法&#xff0c;经过了血和泪的教训下面将自己在安装过程中遇到的查看的资料贴出来&#xff08;感谢各位大佬的文章帮助本闲狗解答疑惑&#xff0c;此处贴出原文链接&#xff0c;如…

【WEEK5】 【DAY5】DML语言【中文版】

2024.3.29 Friday 目录 3.DML语言3.1.外键&#xff08;了解&#xff09;3.1.1.概念3.1.2.作用3.1.3.添加&#xff08;书写&#xff09;外键的几种方法3.1.3.1.创建表时直接在主动引用的表里写&#xff08;被引用的表的被引用的部分&#xff09;3.1.3.2.先创建表后修改表以添加…

《系统架构设计师教程(第2版)》第8章-系统质量属性与架构评估-02-系统架构评估

文章目录 1. 一些重要概念1.1 敏感点 (Sensitivity Point)1.2 权衡点 (Tradeoff Point)1.3 风险承担者 (Stakeholders)1.3.1 系统生产者1.3.2 系统消费者1.3.3 系统服务人员1.3.4 其它人员 1.4 场景 (scenarios) 2. 系统架构评估方法2.1 基于场景的架构分析方法&#xff08;SAA…

漏洞-CORS跨域资源共享JSONP回调

CORS跨域资源共享 Cors漏洞详解 - FreeBuf网络安全行业门户 响应标头中 当Access-Control-Allow-Origin字段为*时&#xff0c;就代表任意域都可以访问&#xff0c;就导致了Cors漏洞的产生 构造poc <!DOCTYPE> <html> <script type"text/javascript&quo…

什么是智慧公厕?智慧城市下的智慧公厕有什么功能和特点?

随着科技的不断进步和城市化的加快发展&#xff0c;智慧城市已经成为我们生活中的一部分。而在智慧城市的建设中&#xff0c;智慧公厕作为城市基础设施的重要组成部分发挥着重要的作用。那么什么是智慧公厕&#xff1f;智慧公厕是针对公共厕所的日常使用、运行、管理、运营等过…

分析:两种不同的函数模板写法,其中一种为何不行

接上篇&#xff1a; 利用类型&#xff0c;做函数模板的“重载”-CSDN博客 比较两种模板的写法 为什么左边不可行&#xff1a; 注意&#xff0c;左边的写法的第二个模板参数&#xff0c;是默认参数的形式。为何这里采取了默认参数的形式呢&#xff0c;本意是想让编译器来走sfi…

Java学习之类和对象、内存底层

目录 表格结构和类结构 表格的动作和类的方法 与面向过程的区别 具体实现 对象和类的详解 类的定义 属性&#xff08;field 成员变量&#xff09; 方法 示例--编写简单的学生类 简单内存分析(理解面向对象) 构造方法(构造器 constructor) 声明格式&#xff1a; 四…

super关键字的使用总结

一、super关键字的使用1. 为什么需要super&#xff1f;举例1&#xff1a;子类继承父类以后&#xff0c;对父类的方法进行了重写&#xff0c;那么在子类中&#xff0c;是否还可以对父类中被重写的方法进行调用&#xff1f; 可以&#xff01;举例2&#xff1a;子类继承父类以后&a…

一个页面实现两个滚动条【前端】

一个页面实现两个滚动条【前端】 前言版权推荐一个页面实现两个滚动条最后 前言 2024-4-2 12:54:46 以下内容源自《【前端】》 仅供学习交流使用 版权 禁止其他平台发布时删除以下此话 本文首次发布于CSDN平台 作者是CSDN日星月云 博客主页是https://jsss-1.blog.csdn.net …

【zlm】音视频流与音频流合并的设计

目录 设想一 设想二 方案三 关键技术 测试语句 测试脚本 参考文档 设想一 //开始录制_option.mp4_save_path custom_path;_option.mp4_max_second max_second;vector<Track::Ptr> mytracks getTracks();auto src MediaSource::find( DEFAULT_VHOST, "1&quo…

练习14 Web [极客大挑战 2019]Upload

phtml格式绕过&#xff0c;burp修改content-type绕过&#xff0c;常见的文件上传存放目录名 题目就叫upload&#xff0c;打开靶机 直接上传一个图片格式的一句话木马&#xff0c;返回如下&#xff1a; 提交练习5和9中的两种可以执行图片格式php代码的文件&#xff0c;修改con…