【RDMA】RDMA SEND/WRITE编程实例(IBV Verbs )

本文介绍了基于Verbs的RDMA Remote Direct Memory Access(RC)通信编程实例,涵盖Main函数中的关键步骤,如资源初始化、连接、发送、接收和资源销毁。还提供了更多编程示例链接及运行方法,帮助读者深入理解RDMA编程。
摘要由CSDN通过智能技术生成

目录

前言

RDMA编程基础

更多例子

基于Verbs的RDMA RC通信编程示例概要

Main()

{

print_config()

resources_init()

resources_create()

sock_connect()

connect_qp()

post_send

poll_completion

resources_destroy

}

例子运行方法

代码1(Send, Receive, RDMA Read, RDMA Write)

代码2:增加 uc和rc的选择,增加Tos的设置

更多讲解教程

LINUX 编程例子


前言

bandaoyu 本文随时更新,本文链接:http://t.csdn.cn/l6FuP

RDMA编程基础

《RDMA编程入门》:https://blog.csdn.net/bandaoyu/article/details/125681856

存储大师班 | RDMA简介与编程基础 -https://zhuanlan.zhihu.com/p/387549948

 大家可以关注一下mellonx的vma,貌似可以直接用socket api通信,方便很多:【RDMA】降低CPU除了RDMA (vbers)还是VMA ?|使用socket进行RDMA编程?_bandaoyu的note-CSDN博客前言看介绍,像是mellonx针对其kernel bypass网卡(RDMA网卡)提供的一个lib库,该lib库对外提供socket api,使得用户的程序不需要修改就可以直接使用kernel bypass网卡(如RDMA网卡)。我们都知道RDMA 网卡目前使用的是rdma_cm和vbers api编程,和socket不一样,如果能用socket对RDMA编程,那确实是很大的利好。官网介绍什么是VMA?Mellanox Interconnect Community官方介绍:M

更多例子:

https://github.com/gpudirect/libibverbs/tree/master/examples

使用说明:

Package libibverbs-utils - man pages | ManKier

基于Verbs的RDMA RC通信编程示例概要

(https://docs.mellanox.com/display/RDMAAwareProgrammingv17/Programming+Examples+Using+IBV+Verbs)

以下是编程示例中的功能概述,以它们被调用的顺序为准。

Main()

{

Parse command line.

解析命令行。用户可以设置测试的TCP端口,设备名称和设备端口。如果设置,这些值将覆盖config中的默认值。最后一个参数是服务器名称。如果设置了服务器名称,则程序以客户端模式运行,连接服务器名称指定的服务器,否则程序处于服务器模式。

Call print_config().       #打印运行参数|如果没有给就是默认参数

Call resources_init().   #初始化结构体等变量

Call resources_create().#创建socket、contex、pd、cq、qp、申请内存和注册内存

Call connect_qp().         #qp_to_init、to RTR、RTS

Call post_send()            #使用IBV_WR_SEND operation (服务端)

Call poll_completion().  

请注意,服务器端希望从SEND请求中获得a completion,而客户端希望得到RECEIVE completion。

(Note that the server side expects a completion from the SEND request and the client side expects a RECEIVE completion.)
如果处于客户端模式,则显示 通过RECEIVE操作接收到的消息,如果处于服务器模式,请向缓冲区加载新消息。

(If in client mode, show the message we received via the RECEIVE operation, otherwise, if we are in server mode, load the buffer with a new message.)

Sync client<->server.

到这,服务器直接进入下一个(Sync client<->server)同步。客户端严格完成下面列的所有(Client only)RDMA操作。

(At this point the server goes directly to the next sync. All RDMA operations are done strictly by the client.)

***Client only ***

Call post_send with IBV_WR_RDMA_READ to perform a RDMA read of server’s buffer.

Call poll_completion.

显示服务器的消息。
向发送缓冲区Setup 新消息数据。

Call post_send with IBV_WR_RDMA_WRITE to perform a RDMA write of server’s buffer.

Call poll_completion.

*** End client only operations ***

Sync client<->server.

If server mode, show buffer, proving RDMA write worked.

Call resources_destroy.

Free device name string.

Done.

}

print_config()

{打印出configuration 配置信息。}

resources_init()

{ resources struct结构体清0}

resources_create()

{

  • Call sock_connect

调用sock_connect用TCP套接字连接到peer(对等方)。

获取设备列表(devices list),找到我们想要的设备,然后将其打开。

释放设备列表。(Free device list.)

  • Get the port information. #获取port 信息
  • Create a PD. //ibv_alloc_pd
  • Create a CQ. //ibv_create_cq
  • Allocate a buffer,并初始化和注册。res->buf = (char *)malloc(size) >>memset(res->buf, 0, size);>>ibv_reg_mr(res->pd, res->buf

分配一个缓冲区,对其进行初始化,然后对其进行注册。

  • Create a QP. //res->qp = ibv_create_qp(res->pd, &qp_init_attr);

}

sock_connect()

{

如果是客户端,请解析服务器的DNS地址并启动与服务器的连接。
如果是服务器,请在指示的端口上侦听传入的连接。

}

connect_qp()

{

  • Call modify_qp_to_init.
  • Call post_receive.
  • Call sock_sync_data  在服务器和客户端之间交换信息。
  • Call modify_qp_to_rtr.
  • Call modify_qp_to_rts.
  • Call sock_sync_data  同步客户端<->服务器

}

  • modify_qp_to_init

将QP转换为INIT状态。

  • post_receive

为接收缓冲区准备一个scatter/gather 条目。
       准备RR。
       发布RR。

Prepare an RR.

Post the RR.

  • sock_sync_data

使用用sock_connect创建的TCP套接字,在客户端和服务器之间同步给定的数据集。由于此函数处于阻塞状态,因此还会与伪数据一起调用该函数以同步客户端和服务器的时序。

(Using the TCP socket created with sock_connect, synchronize the given set of data between client and the server. Since this function is blocking, it is also called with dummy data to synchronize the timing of the client and server.)

  • modify_qp_to_rtr

将QP转换为RTR状态。

  • modify_qp_to_rts

将QP转换为RTS状态。

post_send

{

为要发送(或在RDMA读取情况下接收)的数据准备scatter/gather 条目。
创建一个SR。请注意,IBV_SEND_SIGNALED是冗余的(Note that IBV_SEND_SIGNALED is redundant.)。
如果这是RDMA操作,请设置address 和key。
Post SR。

}

poll_completion

{Poll CQ,直到找到一个CQE或达到MAX_POLL_CQ_TIMEOUT毫秒 超时。}

resources_destroy

{释放资源。}

}

例子运行方法


编译库的需求:libibverbs
编译参数:GCC <文件名> -o service -libverbs


运行方式:
1. 有IB网络支持:
服务端:./service
客户端:./service 服务端IP

2. 走ROCE:
服务端:./service -g 0
客户端:./service -g 0 服务端IP

服务端:

./RDMA_RC_example -g 0 -i 1 -d mlx5_1

客户端:

./RDMA_RC_example 172.17.31.53  -g 0 -d mlx5_1   #172.17.31.53  是 mlx5_1的IP

关于代码中出现的问题请到github中添加issue
博主github:https://github.com/fruitdish/RDMA-EXAMPLE/tree/master/01

代码1(Send, Receive, RDMA Read, RDMA Write)

(RDMA_RC_example.c · bruce/RDMA-Tutorial - Gitee.com)

(csdn源码下载:https://download.csdn.net/download/bandaoyu/25343833)

注意:

struct config_t config =
{
    NULL,  /* dev_name */
    NULL,  /* server_name */
    19875, /* tcp_port */
    1,     /* ib_port */  //-1     /* gid_idx 源码此处初始值应该是bug,会导致运行时不传递参数-g 时connect_qp()中的if (config.gid_idx >= 0)无法满足*/  
     0       /* gid_idx */
};

/*
* BUILD COMMAND:
* gcc -Wall -O0 -g -o RDMA_RC_example RDMA_RC_example.c -libverbs
*server:
*./RDMA_RC_example  -d mlx5_0 -i 1 -g 3
*client:
*./RDMA_RC_example 192.169.31.53 -d mlx5_0 -i 1 -g 3
*/
/******************************************************************************
*
* RDMA Aware Networks Programming Example
*
* This code demonstrates how to perform the following operations using 
* the * VPI Verbs API:
* Send
* Receive
* RDMA Read
* RDMA Write
*
*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <inttypes.h>
#include <endian.h>
#include <byteswap.h>
#include <getopt.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <infiniband/verbs.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

/* poll CQ timeout in millisec (2 seconds) */
#define MAX_POLL_CQ_TIMEOUT 2000
#define SRV_MSG "Server's message "
#define RDMAMSGR "RDMA read operation "
#define RDMAMSGW "RDMA write operation"
#define MSG_SIZE 64
#if __BYTE_ORDER == __LITTLE_ENDIAN
static inline uint64_t htonll(uint64_t x)
{
    return bswap_64(x);
}
static inline uint64_t ntohll(uint64_t x)
{
    return bswap_64(x);
}
#elif __BYTE_ORDER == __BIG_ENDIAN
static inline uint64_t htonll(uint64_t x)
{
    return x;
}
static inline uint64_t ntohll(uint64_t x)
{
    return x;
}
#else
#error __BYTE_ORDER is neither __LITTLE_ENDIAN nor __BIG_ENDIAN
#endif

/* structure of test parameters */
struct config_t
{
    const char *dev_name; /* IB device name */
    char *server_name;    /* server host name */
    uint32_t tcp_port;    /* server TCP port */
    int ib_port;          /* local IB port to work with */
    int gid_idx;          /* gid index to use */
};

/* structure to exchange data which is needed to connect the QPs */
struct cm_con_data_t
{
    uint64_t addr;        /* Buffer address */
    uint32_t rkey;        /* Remote key */
    uint32_t qp_num;      /* QP number */
    uint16_t lid;         /* LID of the IB port */
    uint8_t gid[16];      /* gid */
} __attribute__((packed));

/* structure of system resources */
struct resources
{
    struct ibv_device_attr device_attr; /* Device attributes */
    struct ibv_port_attr port_attr;     /* IB port attributes */
    struct cm_con_data_t remote_props;  /* values to connect to remote side */
    struct ibv_context *ib_ctx;         /* device handle */
    struct ibv_pd *pd;                  /* PD handle */
    struct ibv_cq *cq;                  /* CQ handle */
    struct ibv_qp *qp;                  /* QP handle */
    struct ibv_mr *mr;                  /* MR handle for buf */
    char *buf;                          /* memory buffer pointer, used for RDMA and send ops */
    int sock;                           /* TCP socket file descriptor */
};

struct config_t config =
{
    NULL,  /* dev_name */
    NULL,  /* server_name */
    19875, /* tcp_port */
    1,     /* ib_port */
    //-1     /* gid_idx 源码此处初始值应该是bug,会导致connect_qp()中的if (config.gid_idx >= 0)无法满足*/  
     0       /* gid_idx */
};

/******************************************************************************
Socket operations:
For simplicity, the example program uses TCP sockets to exchange control
information. If a TCP/IP stack/connection is not available, connection manager
(CM) may be used to pass this information. Use of CM is beyond the scope of
this example
******************************************************************************/
/******************************************************************************
* Function: sock_connect
* Input:
* servername: URL of server to connect to (NULL for server mode)
* port: port of service
*
* Output:none
*
* Returns: socket (fd) on success, negative error code on failure
*
* Description:
* Connect a socket. If servername is specified a client connection will be
* initiated to the indicated server and port. Otherwise listen on the
* indicated port for an incoming connection.
*
******************************************************************************/
static int sock_connect(const char *servername, int port)
{
    struct addrinfo *resolved_addr = NULL;
    struct addrinfo *iterator;
    char service[6];
    int sockfd = -1;
    int listenfd = 0;
    int tmp;
    struct addrinfo hints =
    {
        .ai_flags    = AI_PASSIVE,
        .ai_family   = AF_INET,
        .ai_socktype = SOCK_STREAM
    };

    if(sprintf(service, "%d", port) < 0)
    {
        goto sock_connect_exit;
    }

    /* Resolve DNS address, use sockfd as temp storage */
    sockfd = getaddrinfo(servername, service, &hints, &resolved_addr);
    if(sockfd < 0)
    {
        fprintf(stderr, "%s for %s:%d\n", gai_strerror(sockfd), servername, port);
        goto sock_connect_exit;
    }

    /* Search through results and find the one we want */
    for(iterator = resolved_addr; iterator ; iterator = iterator->ai_next)
    {
        sockfd = socket(iterator->ai_family, iterator->ai_socktype, iterator->ai_protocol);
        if(sockfd >= 0)
        {
            if(servername)
			{
                /* Client mode. Initiate connection to remote */
                if((tmp=connect(sockfd, iterator->ai_addr, iterator->ai_addrlen)))
                {
                    fprintf(stdout, "failed connect \n");
                    close(sockfd);
                    sockfd = -1;
                }
			}
            else
            {
                /* Server mode. Set up listening socket an accept a connection */
                listenfd = sockfd;
                sockfd = -1;
                if(bind(listenfd, iterator->ai_addr, iterator->ai_addrlen))
                {
                    goto sock_connect_exit;
                }
                listen(listenfd, 1);
                sockfd = accept(listenfd, NULL, 0);
            }
        }
    }

sock_connect_exit:
    if(listenfd)
    {
        close(listenfd);
    }

    if(resolved_addr)
    {
        freeaddrinfo(resolved_addr);
    }

    if(sockfd < 0)
    {
        if(servername)
        {
            fprintf(stderr, "Couldn't connect to %s:%d\n", servername, port);
        }
        else
        {
            perror("server accept");
            fprintf(stderr, "accept() failed\n");
        }
    }

    return sockfd;
}

/******************************************************************************
* Function: sock_sync_data
* Input:
* sock: socket to transfer data on
* xfer_size: size of data to transfer
* local_data: pointer to data to be sent to remote
*
* Output: remote_data pointer to buffer to receive remote data
*
* Returns: 0 on success, negative error code on failure
*
* Description:
* Sync data across a socket. The indicated local data will be sent to the
* remote. It will then wait for the remote to send its data back. It is
* assumed that the two sides are in sync and call this function in the proper
* order. Chaos will ensue if they are not. :)
*
* Also note this is a blocking function and will wait for the full data to be
* received from the remote.
*
******************************************************************************/
int sock_sync_data(int sock, int xfer_size, char *local_data, char *remote_data)
{
    int rc;
    int read_bytes = 0;
    int total_read_bytes = 0;
    rc = write(sock, local_data, xfer_size);

    if(rc < xfer_size)
    {
        fprintf(stderr, "Failed writing data during sock_sync_data\n");
    }
    else
    {
        rc = 0;
    }

    while(!rc && total_read_bytes < xfer_size)
    {
        read_bytes = read(sock, remote_data, xfer_size);
        if(read_bytes > 0)
        {
            total_read_bytes += read_bytes;
        }
        else
        {
            rc = read_bytes;
        }
    }
    return rc;
}
/******************************************************************************
End of socket operations
******************************************************************************/

/* poll_completion */
/******************************************************************************
* Function: poll_completion
*
* Input:
* res: pointer to resources structure
*
* Output: none
*
* Returns: 0 on success, 1 on failure
*
* Description:
* Poll the completion queue for a single event. This function will continue to
* poll the queue until MAX_POLL_CQ_TIMEOUT milliseconds have passed.
*
******************************************************************************/
static int poll_completion(struct resources *res)
{
    struct ibv_wc wc;
    unsigned long start_time_msec;
    unsigned long cur_time_msec;
    struct timeval cur_time;
    int poll_result;
    int rc = 0;
    /* poll the completion for a while before giving up of doing it .. */
    gettimeofday(&cur_time, NULL);
    start_time_msec = (cur_time.tv_sec * 1000) + (cur_time.tv_usec / 1000);
    do
    {
        poll_result = ibv_poll_cq(res->cq, 1, &wc);
        gettimeofday(&cur_time, NULL);
        cur_time_msec = (cur_time.tv_sec * 1000) + (cur_time.tv_usec / 1000);
    }
    while((poll_result == 0) && ((cur_time_msec - start_time_msec) < MAX_POLL_CQ_TIMEOUT));

    if(poll_result < 0)
    {
        /* poll CQ failed */
        fprintf(stderr, "poll CQ failed\n");
        rc = 1;
    }
    else if(poll_result == 0)
    {
        /* the CQ is empty */
        fprintf(stderr, "
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值