亚欧色一区w666天堂,色情一区二区三区免费看,少妇特黄A片一区二区三区,亚洲人成网站999久久久综合,国产av熟女一区二区三区

  • 發布文章
  • 消息中心
點贊
收藏
評論
分享
原創

softroce-rxe源碼解析

2023-10-07 01:33:47
933
0

一、基本簡介

rxe是完全(quan)使(shi)用軟件實現(xian)的rdma協議。基本架構如下(xia)圖所示,特性如下(xia):

  • 基于udp實現
  • 向用戶呈現的是一套標準的IB verbs接口
  • 基于標準的IB spec實現
  • 所有的verbs操作都需要陷入內核。不同于硬件RDMA,數據路徑by-pass kernel
  • 支持RDMA的操作:SEND/RECV、WRITE、READ、ATOMIC
  • 支持RC、UC、UD
  • 支持NAK重傳、超時重傳機制
  • 支持累計ACK,WQE是一個一個確定,CI依次+1
  • 支持CQ中斷,但不支持EQ。
  • 不支持擁塞管理(沒有PFC、ECN),依賴于內核協議棧的擁塞管理

代碼路徑:

用(yong)戶態代(dai)碼路徑(jing):rdma-core/providers/rxe/

內核(he)代(dai)碼路徑:kernel/driver/infiniband/sw/rxe/

本文的(de)分析基于內核(he)4.19版本的(de)代碼。

二、設備初始化

1、rxe模塊中使用到的兩個機制pool和queue

(1)Pool機制-rxe用于管理qpc,cqc,mrc等context使用的方法,兩個結構(gou)體如(ru)下所示:

struct rxe_pool_entry {
       struct rxe_pool             *pool;
       struct kref             ref_cnt;
       struct list_head     list;
       /* only used if indexed or keyed */
       struct rb_node             node;
       u32               index;
};

struct rxe_pool {
       struct rxe_dev       *rxe;
       spinlock_t              pool_lock; /* pool spinlock */
       size_t                    elem_size;      /*qp、cq、mr等結構體的大小*/
       struct kref             ref_cnt;
       void               (*cleanup)(struct rxe_pool_entry *obj);
       enum rxe_pool_state    state;
       enum rxe_pool_flags    flags;
       enum rxe_elem_type    type;   /*pool 的類型*/
       unsigned int         max_elem;   /*支持的pool的個數*/
       atomic_t        num_elem;   /*當前pool中的個數*/

       /* only used if indexed or keyed */
       struct rb_root        tree;   /*用于管理pool*/
       unsigned long             *table; /*index bitmap*/
       size_t                    table_size; /*根據max_index和min_index計算的table size*/
       u32               max_index;  /*和范圍*/
       u32               min_index;
       u32               last;       /*記錄上次分配的bit,便于查找index*/
       size_t                    key_offset;
       size_t                    key_size;
};
  • 所有context要包含struct rxe_pool_entry成員
  • 使用紅黑樹管理context
  • pool中記錄了qp,cq,mr等元素的一些基本信息,在創建時候使用。用于申請內存、資源檢測是否足夠等等。

(2)Queue機制-rxe模塊用于管理qp、cq隊列使(shi)用的(de)方法,如下所(suo)示(shi):

struct rxe_queue結(jie)構體說(shuo)明

struct rxe_queue {
       struct rxe_dev       *rxe;
       struct rxe_queue_buf    *buf;
       struct rxe_mmap_info   *ip;
       size_t                    buf_size;
       size_t                    elem_size;
       unsigned int         log2_elem_size;   
       unsigned int         index_mask;
};

          buf_size = sizeof(rxe_queue_buf) + queue depth * queue entry size

          elem_size = queue entry size

          index_mask = queue depth – 1

          buf = vmalloc_user(buf_size),用于給用戶態mmap使用。

struct rxe_queue_buf結構體說(shuo)明:

  struct rxe_queue_buf {
       __u32                   log2_elem_size;
       __u32                   index_mask;   /*隊列深度的掩碼*/
       __u32                   pad_1[30];
       __u32                   producer_index;   /*生產者index*/
       __u32                   pad_2[31];
       __u32                   consumer_index;  /*消費者index*/
       __u32                   pad_3[31];
       __u8                     data[0];                 /*隊列的起始地址*/
};

       內(nei)(nei)核和(he)用(yong)(yong)戶(hu)(hu)態對(dui)queue的(de)操作主要使用(yong)(yong)到index_mask、producer_index;和(he)consumer_index。對(dui)于queue中的(de)pi和(he)ci的(de)修(xiu)(xiu)改(gai)陳述:(1)mmap的(de)內(nei)(nei)存(cun),所以內(nei)(nei)核和(he)用(yong)(yong)戶(hu)(hu)態是操作的(de)一個數據(2)CQ的(de)PI內(nei)(nei)核修(xiu)(xiu)改(gai),加鎖(suo);CI用(yong)(yong)戶(hu)(hu)態修(xiu)(xiu)改(gai),加鎖(suo);(3)SQ的(de)PI用(yong)(yong)戶(hu)(hu)態修(xiu)(xiu)改(gai),加鎖(suo);CI內(nei)(nei)核修(xiu)(xiu)改(gai),不(bu)加鎖(suo);(4)RQ的(de)PI用(yong)(yong)戶(hu)(hu)態修(xiu)(xiu)改(gai),加鎖(suo);CI內(nei)(nei)核修(xiu)(xiu)改(gai),不(bu)加鎖(suo)

2、rxe模塊的加載

通過insmod rdma_rxe.ko觸(chu)發rxe模(mo)塊的(de)加載,主要(yao)完(wan)成下面(mian)兩件事(shi)情

  • 初始化udp tunnel,使用udp框架完成收發包
  • 注冊netdev事件,關心網卡的UP、DOWN、MTU變化等事

3、rdma設備的加載

通過sysfs或者netlink發起rxe設(she)備add請求(qiu),與(yu)內核rxe模塊通信完(wan)成rxe設(she)備的(de)加載(具體使用方法見后文)。

rxe模塊響(xiang)應add請求,通過rxe_add()函(han)數完成(cheng)結構體(ti)struct rxe_dev{}的初始化(hua)

  • 初始化rxe_dev->attr,配置rdma設備cap(max qp、max_cq、max sge、max mr等等)
  • 初始化rxe_dev->port,配置prot的屬性(guid、MTU等等)
  • 初始化rxe_dev->rxe_poll指向的結構體。pool使用rbtree管理創建的qp、mr等資源。
  • 完成IB device的注冊

三、MR

1MR注冊

上圖是MR注冊的(de)基本過程。最(zui)終完(wan)成struct rxe_mem{}的(de)初始化(hua)。

struct rxe_mem {
	struct rxe_pool_entry	pelem;
	union {
		struct ib_mr		ibmr;
		struct ib_mw		ibmw;
	};
	struct rxe_pd		*pd;
	struct ib_umem		*umem;
	u32			lkey;
	u32			rkey;
	enum rxe_mem_state	state;
	enum rxe_mem_type	type;
	u64			va;			/*用戶傳入的地址*/
	u64			iova;			/*用戶傳入的地址*/
	size_t			length;	/*用戶傳入的長度*/
	u32			offset;		/*第一個entry的偏移*/
	int			access;		/*訪問權限*/
	int			page_shift;
	int			page_mask;
	int			map_shift;
	int			map_mask;
	u32			num_buf;			/* entry的個數*/
	u32			nbuf;
	u32			max_buf;	
	u32			num_map;		/*一級表的個數*/
	struct rxe_map		**map;	/*二級頁表*/
};

說明:

(1)4K頁表存(cun)放256個地(di)址(zhi)(zhi)entry(struct rxe_phys_buf)。enrty包含了地(di)址(zhi)(zhi)和長度(du)地(di)址(zhi)(zhi)和長度(du)都(dou)是通過ib_umem_get()整理(li)后得來(lai)的。size不一定全(quan)部是4K。可以使連(lian)續地(di)址(zhi)(zhi)的大小

(2)MR的組(zu)織形式(shi)(shi)以二(er)級頁表(biao)的方式(shi)(shi)組(zu)織,

(3)MR中記錄頁表存放的地址(zhi),地址(zhi)存放的是(shi)內核的虛擬地址(zhi)

(4)rxe_mem->offset是第一個(ge)entry中的可使用的偏移(yi)。

2、lookup mr過程

(1)根(gen)據(ju)sge的(de)key獲(huo)取index,從rb tree中找到對應的(de)mr context。 需要(yao)循環遍歷。

(2)根(gen)據sge中的addr也mr context中保(bao)存的地(di)址進行比較,將數據memcpy到(dao)payload對(dui)應(ying)的地(di)址中。這(zhe)塊是sge對(dui)應(ying)的buf和(he)skb 對(dui)應(ying)的buf互相copy(TX和(he)RX)

四、CQ

1CQ創建

上圖是創建CQ的(de)(de)過(guo)程。(1)CQ有創建個數的(de)(de)最大限制。(2)CQ不同于MR、QP不需要(yao)用(yong)rb_tree管理,通過(guo)QP找到CQ即(ji)可。結構體(ti)如下(xia):

struct rxe_cq {
	struct rxe_pool_entry	pelem;
	struct ib_cq		ibcq;
	struct rxe_queue	*queue;
	spinlock_t		cq_lock;
	u8			notify;
	bool			is_dying;
	int			is_user;
	struct tasklet_struct	comp_task;	/*中斷函數*/
};

2、POLL CQ

(1)內核在(zai)RX流(liu)程中(zhong)完成CQE結構(gou)體的賦值,根據CQ的PI,填(tian)充(chong)CQE,PI++,填(tian)充(chong)CQE過程需要加鎖

(2)用戶態根據CQ addr、CI和PI,獲取CQE, CI++, 返回給APP.Poll cq 需(xu)要(yao)加鎖的

 

3、CQ的中斷模式;

cq->complete  task用于中(zhong)斷(duan)模式。調(diao)用創建CQ時復制的(de)ib_uverbs_comp_handle()處理(li)函數。ib_uverbs_comp_handler()函數調(diao)用wake_up_interruptible()喚(huan)醒(xing)對應阻塞(sai)的(de)線程,通知用戶態處理(li)。

五、QP

1QP創建

上圖是(shi)創建QP的(de)(de)基本過(guo)程,完成struct rxe_qp{}的(de)(de)初(chu)始化。

struct rxe_qp {
	struct rxe_pool_entry	pelem;
	struct ib_qp		ibqp;
	struct ib_qp_attr	attr;
	unsigned int		valid;
	unsigned int		mtu;
	int			is_user;
	struct rxe_pd		*pd;
	struct rxe_srq		*srq;
	struct rxe_cq		*scq;
	struct rxe_cq		*rcq;
	enum ib_sig_type	sq_sig_type;
	struct rxe_sq		sq;
	struct rxe_rq		rq;
	struct socket		*sk;
	u32			dst_cookie;
	struct rxe_av		pri_av;
	struct rxe_av		alt_av;
	struct list_head	grp_list;
	spinlock_t		grp_lock; /* guard grp_list */
	struct sk_buff_head	req_pkts;   /*內核協議棧的req pkt*/
	struct sk_buff_head	resp_pkts;
	struct sk_buff_head	send_pkts;

	struct rxe_req_info	req;        /*tx處理*/
	struct rxe_comp_info	comp;   /*ACK報文*/
	struct rxe_resp_info	resp;   /*respond報文*/

	atomic_t		ssn;
	atomic_t		skb_out;
	int			need_req_skb;
	struct timer_list retrans_timer;    /*超時重傳*/
	u64 qp_timeout_jiffies;
	/* Timer for handling RNR NAKS. */
	struct timer_list rnr_nak_timer;    /*rnr nak 重傳*/
	spinlock_t		state_lock; /* guard requester and completer */
	struct execute_work	cleanup_work;
};

說明:

(1)不同于其他的(de)硬件RDMA廠(chang)商,沒(mei)有db_record

(2)RXE是(shi)內核(he)申請queue buf,用戶態(tai)映射。硬件廠(chang)商是(shi)用戶態(tai)申請

(3)收發包(bao)的回調函數參數是(shi)QP。不需(xu)要(yao)lookup QO

(4)wqe size是固定的(de)(de),內核和(he)用戶(hu)態用一(yi)個結構體(ti)。SQ和(he)RQ的(de)(de)組(zu)織如下:

2TX-POST SEND

上圖是Post send的(de)基(ji)本過程。Send wqe的(de)結(jie)構體(ti)如下:

struct rxe_send_wqe {
	struct rxe_send_wr	wr;		/*wqe hdr信息*/
	struct rxe_av		av;
	__u32			status;
	__u32			state;		/*wqe的狀態 pending、process*/
	__aligned_u64		iova;
	__u32			mask;
	__u32			first_psn;		/*wqe對應報文的第一個psn*/
	__u32			last_psn;		/*wqe對應報文的最后一個psn*/
	__u32			ack_length;
	__u32			ssn;
	__u32			has_rd_atomic;
	struct rxe_dma_info	dma;	/*sge信息*/
};

說明:

(1)post send敲doorbel是向內核發送了(le)POST_SEND的命(ming)令,代碼如下;

cmd.hdr.command	= IB_USER_VERBS_CMD_POST_SEND;
cmd.hdr.in_words = sizeof(cmd) / 4;
cmd.hdr.out_words = sizeof(resp) / 4;
cmd.response	= (uintptr_t)&resp;
cmd.qp_handle	= ibqp->handle;
cmd.wr_count	= 0;
cmd.sge_count	= 0;
cmd.wqe_size	= sizeof(struct ibv_send_wr);
write(ibqp->context->cmd_fd, &cmd, sizeof(cmd));

(2)報文發(fa)送是在當前post send的上(shang)下文完成

(3)分片的(de)報(bao)文要記錄buf信(xin)息,一次只獲取(qu)(qu)mtu大小(xiao)的(de)報(bao)文(lookup mr),填充獲取(qu)(qu)addr和payload len

(4)對于需(xu)要(yao)ACK的(de)wqe,需(xu)要(yao)pending,還需(xu)要(yao)放在(zai)SQ中

(5)QP的ci在ack后,更新

(6)post send過程(cheng)加(jia)(jia)(jia)鎖(suo)了(分為用戶態PI加(jia)(jia)(jia)鎖(suo)和內核態QP處理報文過程(cheng)加(jia)(jia)(jia)鎖(suo))所以同(tong)時只有一個(ge)線(xian)程(cheng)能操(cao)作隊列。多個(ge)線(xian)程(cheng)操(cao)作同(tong)一個(ge)qp時,第一個(ge)獲(huo)取到QP資源的線(xian)程(cheng)會(hui)繼續(xu)處理到沒有wqe,后面(mian)的線(xian)程(cheng)直接返回。

(7)最終發包是調用rxe_send()函數完成。

3RX-POST RECV

上圖是post recv的過程(cheng)(cheng)。(1)post recv過程(cheng)(cheng)需要加鎖,所(suo)以同(tong)時只有一個線程(cheng)(cheng)下(xia)發(fa)rqe(2)操作不會下(xia)限到(dao)內核。結構體如(ru)下(xia):

struct rxe_recv_wqe {
	__aligned_u64		wr_id;
	__u32			num_sge;	/*sge個數*/
	__u32			padding;
	struct rxe_dma_info	dma;	/*sge*/
};

4RX

上圖(tu)是RX的流程,rxe收(shou)到的包(bao)只有IB頭(tou)+payload兩部分(fen),UDP之前的包(bao)頭(tou)已(yi)經被剝離。收(shou)到的包(bao)分(fen)為兩種包(bao):(1)處理(li)(li)requester;(2)處理(li)(li)ACK報文;代碼如下:

static inline void rxe_rcv_pkt(struct rxe_dev *rxe, struct rxe_pkt_info *pkt,struct sk_buff *skb)
{
	if (pkt->mask & RXE_REQ_MASK)
		rxe_resp_queue_pkt(rxe, pkt->qp, skb);
	else
		rxe_comp_queue_pkt(rxe, pkt->qp, skb);
}

5RX-請求報文

上圖(tu)是處理request報文的過程。簡單說明:

(1)RDMA read request在softirq上下文(wen)處理,獲取(qu)buf,通過rxe_send發送

(2)Requester 隊列中(zhong)的報文大于1個(ge),在softirq中(zhong)處(chu)理

(3)只有一個報文(wen),在UDP tunnel收包上下文(wen)處理

6RX-ACK報文

上圖是處理ACK報文的過程。簡單說(shuo)明:

(1)當(dang)respond 隊列中的(de)報(bao)文數(shu)量大于1,在softirp上下文處理

(2)一個報文(wen),在UDP tunnel收包上下文(wen)處(chu)理(li)

(3)沒(mei)有處理cq full的情況,應該是(shi)認為(wei)報文是(shi)成功發送的,用(yong)戶不用(yong)關心。

(4)SQ ci ++

7RX-ACK報文之RDMA_READ_RESPOND

解(jie)析ACK報文,判斷opcode是(shi)rdma read resp,處理過程如下:

(1)首先從(cong)SQ中獲(huo)(huo)取根據CI和SQ_addr獲(huo)(huo)取wqe

(2)解析wqe,獲取要(yao)存儲報文(wen)的sge

(3)將rdma read resp的報文寫到對應(ying)的buf中

  • 如果是多個rdma read resp報文,wqe還繼續維持pending,同時記錄寫sge的偏移(寫struct rxe_send_wqe->dma)。
  • 當多個rdma read resp 報文全部收齊后,ci ++,釋放wqe。

8RX-累計ACK功能

(1)首先從SQ中獲取根(gen)據CI和SQ_addr獲取wqe。

(2)解析(xi)wqe,發現wqe->last_psn(這(zhe)個wqe對應(ying)的(de)最(zui)后一個分片報(bao)文(wen)的(de)psn) < ack 報(bao)文(wen)中的(de)psn時,認為是累(lei)計(ji)ack

  • Send/write操作,會處理到相同psn的wqe即可,一個wqe上送一個cqe。如果不是全部上送cqe或者不要求上送cqe,則不上送cqe
  • 如果pending的wqe是Rdma read resp 或者atomic報文就任務丟包了,需要重傳

使用方法:

1、前提條件:

(1)確定設備上是否有rxe的用戶態驅動(dong),不存在需要更(geng)新/安裝rdma-core

  • 查看/etc/libibverbs.d/目錄下是否有driver
  • 查看設備上是否有librxe-rdma.so

(2)確(que)定(ding)設備上(shang)是否有(you)IB、rxe的內核驅動,不存在就找對應的內核源碼,單獨編譯(yi)driver/infiniband/生成驅動

  • 查看是否有ko,ib_core.ko,rdma_rxe.ko

2、添加設備

(1)兩種方法:

  • 通過rdma link add命令,(只在高版本內核中支持)
    • rdma link add xxx type rxe netdev xxx 命令通過netlink與內核IB模塊交互,與c中的注冊模塊完成rxe設備的添加

  • 通過sysfs添加
    • echo xxx(網卡) > /sys/module/rdma_rxe/parameters/add。如果沒有parameters目錄,就是加載ko不正確。

3、性能測試:

使用perftest測試,使用標卡和cx6對(dui)打測試,中(zhong)間一跳交換機

  • 8個進程占用8個核,帶寬:22Gb
  • 時延:26us
  • PPS:0.5M

分析

  • 數據面需要陷入內核操作
  • 數據包需要經過一次完整的協議棧
  • rxe發包過程中,需要將內存通過CPU將數據寫入發包緩存中,網卡還需要一次dma才能將數據發送出去
  • rxe的收包是瓶頸,寫內存是CPU處理 softirq

4、測試網卡的選擇

  • 盡可能的使用不帶有rdma功能的網卡
  • 使用Mlnx網卡測試rxe的時候,網卡會截獲rdma報文,不會上送rxe。

 

0條評論
0 / 1000
z****n
10文(wen)章(zhang)數
1粉絲(si)數
z****n
10 文(wen)章 | 1 粉絲
原創(chuang)

softroce-rxe源碼解析

2023-10-07 01:33:47
933
0

一、基本簡介

rxe是完(wan)全使用軟件實現的rdma協議。基(ji)本(ben)架構如下圖(tu)所示,特性(xing)如下:

  • 基于udp實現
  • 向用戶呈現的是一套標準的IB verbs接口
  • 基于標準的IB spec實現
  • 所有的verbs操作都需要陷入內核。不同于硬件RDMA,數據路徑by-pass kernel
  • 支持RDMA的操作:SEND/RECV、WRITE、READ、ATOMIC
  • 支持RC、UC、UD
  • 支持NAK重傳、超時重傳機制
  • 支持累計ACK,WQE是一個一個確定,CI依次+1
  • 支持CQ中斷,但不支持EQ。
  • 不支持擁塞管理(沒有PFC、ECN),依賴于內核協議棧的擁塞管理

代碼路徑:

用(yong)戶態代碼路(lu)徑:rdma-core/providers/rxe/

內核代(dai)碼路徑:kernel/driver/infiniband/sw/rxe/

本(ben)(ben)文(wen)的分析基于內核4.19版本(ben)(ben)的代碼。

二、設備初始化

1、rxe模塊中使用到的兩個機制pool和queue

(1)Pool機制-rxe用于管理qpc,cqc,mrc等context使用的方法,兩(liang)個結(jie)構體如下所示(shi):

struct rxe_pool_entry {
       struct rxe_pool             *pool;
       struct kref             ref_cnt;
       struct list_head     list;
       /* only used if indexed or keyed */
       struct rb_node             node;
       u32               index;
};

struct rxe_pool {
       struct rxe_dev       *rxe;
       spinlock_t              pool_lock; /* pool spinlock */
       size_t                    elem_size;      /*qp、cq、mr等結構體的大小*/
       struct kref             ref_cnt;
       void               (*cleanup)(struct rxe_pool_entry *obj);
       enum rxe_pool_state    state;
       enum rxe_pool_flags    flags;
       enum rxe_elem_type    type;   /*pool 的類型*/
       unsigned int         max_elem;   /*支持的pool的個數*/
       atomic_t        num_elem;   /*當前pool中的個數*/

       /* only used if indexed or keyed */
       struct rb_root        tree;   /*用于管理pool*/
       unsigned long             *table; /*index bitmap*/
       size_t                    table_size; /*根據max_index和min_index計算的table size*/
       u32               max_index;  /*和范圍*/
       u32               min_index;
       u32               last;       /*記錄上次分配的bit,便于查找index*/
       size_t                    key_offset;
       size_t                    key_size;
};
  • 所有context要包含struct rxe_pool_entry成員
  • 使用紅黑樹管理context
  • pool中記錄了qp,cq,mr等元素的一些基本信息,在創建時候使用。用于申請內存、資源檢測是否足夠等等。

(2)Queue機制-rxe模(mo)塊用于管理qp、cq隊列使(shi)用的方法,如下所(suo)示:

struct rxe_queue結構體說明

struct rxe_queue {
       struct rxe_dev       *rxe;
       struct rxe_queue_buf    *buf;
       struct rxe_mmap_info   *ip;
       size_t                    buf_size;
       size_t                    elem_size;
       unsigned int         log2_elem_size;   
       unsigned int         index_mask;
};

          buf_size = sizeof(rxe_queue_buf) + queue depth * queue entry size

  &nbsp;       elem_size = queue entry size

     ;     index_mask = queue depth – 1

          buf = vmalloc_user(buf_size),用(yong)于(yu)給用(yong)戶態mmap使用(yong)。

struct rxe_queue_buf結構體(ti)說明:

  struct rxe_queue_buf {
       __u32                   log2_elem_size;
       __u32                   index_mask;   /*隊列深度的掩碼*/
       __u32                   pad_1[30];
       __u32                   producer_index;   /*生產者index*/
       __u32                   pad_2[31];
       __u32                   consumer_index;  /*消費者index*/
       __u32                   pad_3[31];
       __u8                     data[0];                 /*隊列的起始地址*/
};

       內(nei)(nei)核(he)和(he)(he)(he)用(yong)戶(hu)(hu)態(tai)對queue的(de)(de)(de)操作主要使用(yong)到index_mask、producer_index;和(he)(he)(he)consumer_index。對于queue中的(de)(de)(de)pi和(he)(he)(he)ci的(de)(de)(de)修(xiu)(xiu)(xiu)(xiu)改陳述:(1)mmap的(de)(de)(de)內(nei)(nei)存,所以內(nei)(nei)核(he)和(he)(he)(he)用(yong)戶(hu)(hu)態(tai)是(shi)操作的(de)(de)(de)一個(ge)數據(ju)(2)CQ的(de)(de)(de)PI內(nei)(nei)核(he)修(xiu)(xiu)(xiu)(xiu)改,加(jia)(jia)鎖(suo);CI用(yong)戶(hu)(hu)態(tai)修(xiu)(xiu)(xiu)(xiu)改,加(jia)(jia)鎖(suo);(3)SQ的(de)(de)(de)PI用(yong)戶(hu)(hu)態(tai)修(xiu)(xiu)(xiu)(xiu)改,加(jia)(jia)鎖(suo);CI內(nei)(nei)核(he)修(xiu)(xiu)(xiu)(xiu)改,不加(jia)(jia)鎖(suo);(4)RQ的(de)(de)(de)PI用(yong)戶(hu)(hu)態(tai)修(xiu)(xiu)(xiu)(xiu)改,加(jia)(jia)鎖(suo);CI內(nei)(nei)核(he)修(xiu)(xiu)(xiu)(xiu)改,不加(jia)(jia)鎖(suo)

2、rxe模塊的加載

通(tong)過insmod rdma_rxe.ko觸(chu)發rxe模(mo)塊的(de)加載,主要完成(cheng)下面兩(liang)件事情

  • 初始化udp tunnel,使用udp框架完成收發包
  • 注冊netdev事件,關心網卡的UP、DOWN、MTU變化等事

3、rdma設備的加載

通(tong)過sysfs或者(zhe)netlink發起rxe設備(bei)add請求,與(yu)內(nei)核rxe模塊通(tong)信完(wan)成rxe設備(bei)的(de)加載(具(ju)體使(shi)用方(fang)法見后文)。

rxe模塊響應add請求(qiu),通過rxe_add()函數完(wan)成結構體(ti)struct rxe_dev{}的初始化

  • 初始化rxe_dev->attr,配置rdma設備cap(max qp、max_cq、max sge、max mr等等)
  • 初始化rxe_dev->port,配置prot的屬性(guid、MTU等等)
  • 初始化rxe_dev->rxe_poll指向的結構體。pool使用rbtree管理創建的qp、mr等資源。
  • 完成IB device的注冊

三、MR

1MR注冊

上圖是(shi)MR注冊的(de)基本過(guo)程。最(zui)終完(wan)成struct rxe_mem{}的(de)初始化。

struct rxe_mem {
	struct rxe_pool_entry	pelem;
	union {
		struct ib_mr		ibmr;
		struct ib_mw		ibmw;
	};
	struct rxe_pd		*pd;
	struct ib_umem		*umem;
	u32			lkey;
	u32			rkey;
	enum rxe_mem_state	state;
	enum rxe_mem_type	type;
	u64			va;			/*用戶傳入的地址*/
	u64			iova;			/*用戶傳入的地址*/
	size_t			length;	/*用戶傳入的長度*/
	u32			offset;		/*第一個entry的偏移*/
	int			access;		/*訪問權限*/
	int			page_shift;
	int			page_mask;
	int			map_shift;
	int			map_mask;
	u32			num_buf;			/* entry的個數*/
	u32			nbuf;
	u32			max_buf;	
	u32			num_map;		/*一級表的個數*/
	struct rxe_map		**map;	/*二級頁表*/
};

說明:

(1)4K頁表存放256個地(di)(di)(di)址(zhi)entry(struct rxe_phys_buf)。enrty包含了地(di)(di)(di)址(zhi)和長度(du)地(di)(di)(di)址(zhi)和長度(du)都是(shi)通過(guo)ib_umem_get()整理后(hou)得來的。size不一(yi)定全部是(shi)4K。可以使連續(xu)地(di)(di)(di)址(zhi)的大小

(2)MR的(de)組(zu)織形(xing)式(shi)以二級頁表的(de)方式(shi)組(zu)織,

(3)MR中記錄頁表存放(fang)(fang)的地(di)址,地(di)址存放(fang)(fang)的是內(nei)核的虛擬地(di)址

(4)rxe_mem->offset是第一個entry中的(de)可使用的(de)偏移(yi)。

2、lookup mr過程

(1)根據(ju)sge的key獲取(qu)index,從rb tree中找到對應的mr context。 需要循環遍歷。

(2)根據sge中的(de)(de)(de)addr也mr context中保存的(de)(de)(de)地址進行(xing)比較,將數據memcpy到payload對(dui)應的(de)(de)(de)地址中。這塊是sge對(dui)應的(de)(de)(de)buf和skb 對(dui)應的(de)(de)(de)buf互相copy(TX和RX)

四、CQ

1CQ創建

上圖是創(chuang)建(jian)CQ的(de)過程。(1)CQ有創(chuang)建(jian)個數(shu)的(de)最大(da)限制。(2)CQ不(bu)同于(yu)MR、QP不(bu)需要用rb_tree管理,通(tong)過QP找到(dao)CQ即可。結構(gou)體(ti)如下(xia):

struct rxe_cq {
	struct rxe_pool_entry	pelem;
	struct ib_cq		ibcq;
	struct rxe_queue	*queue;
	spinlock_t		cq_lock;
	u8			notify;
	bool			is_dying;
	int			is_user;
	struct tasklet_struct	comp_task;	/*中斷函數*/
};

2、POLL CQ

(1)內核在RX流程(cheng)中完成CQE結構(gou)體的賦值,根據CQ的PI,填(tian)充(chong)CQE,PI++,填(tian)充(chong)CQE過(guo)程(cheng)需要加鎖

(2)用(yong)戶(hu)態根據CQ addr、CI和PI,獲取CQE, CI++, 返回給(gei)APP.Poll cq 需要(yao)加鎖的

 

3、CQ的中斷模式;

cq->complete  task用于(yu)中(zhong)斷(duan)模(mo)式。調(diao)用創建CQ時復制的(de)ib_uverbs_comp_handle()處(chu)理(li)函數。ib_uverbs_comp_handler()函數調(diao)用wake_up_interruptible()喚醒(xing)對應(ying)阻塞(sai)的(de)線程(cheng),通知用戶態處(chu)理(li)。

五、QP

1QP創建

上圖是創(chuang)建QP的(de)基本過程,完(wan)成struct rxe_qp{}的(de)初(chu)始化。

struct rxe_qp {
	struct rxe_pool_entry	pelem;
	struct ib_qp		ibqp;
	struct ib_qp_attr	attr;
	unsigned int		valid;
	unsigned int		mtu;
	int			is_user;
	struct rxe_pd		*pd;
	struct rxe_srq		*srq;
	struct rxe_cq		*scq;
	struct rxe_cq		*rcq;
	enum ib_sig_type	sq_sig_type;
	struct rxe_sq		sq;
	struct rxe_rq		rq;
	struct socket		*sk;
	u32			dst_cookie;
	struct rxe_av		pri_av;
	struct rxe_av		alt_av;
	struct list_head	grp_list;
	spinlock_t		grp_lock; /* guard grp_list */
	struct sk_buff_head	req_pkts;   /*內核協議棧的req pkt*/
	struct sk_buff_head	resp_pkts;
	struct sk_buff_head	send_pkts;

	struct rxe_req_info	req;        /*tx處理*/
	struct rxe_comp_info	comp;   /*ACK報文*/
	struct rxe_resp_info	resp;   /*respond報文*/

	atomic_t		ssn;
	atomic_t		skb_out;
	int			need_req_skb;
	struct timer_list retrans_timer;    /*超時重傳*/
	u64 qp_timeout_jiffies;
	/* Timer for handling RNR NAKS. */
	struct timer_list rnr_nak_timer;    /*rnr nak 重傳*/
	spinlock_t		state_lock; /* guard requester and completer */
	struct execute_work	cleanup_work;
};

說明:

(1)不同于其他(ta)的硬件RDMA廠商(shang),沒(mei)有db_record

(2)RXE是內核申請queue buf,用(yong)戶(hu)態映射(she)。硬件(jian)廠商是用(yong)戶(hu)態申請

(3)收發(fa)包的回調函(han)數(shu)參數(shu)是QP。不需(xu)要lookup QO

(4)wqe size是固定的,內核和(he)(he)用戶態用一個結構體。SQ和(he)(he)RQ的組織如下(xia):

2TX-POST SEND

上圖(tu)是(shi)Post send的基本過程。Send wqe的結構體(ti)如下:

struct rxe_send_wqe {
	struct rxe_send_wr	wr;		/*wqe hdr信息*/
	struct rxe_av		av;
	__u32			status;
	__u32			state;		/*wqe的狀態 pending、process*/
	__aligned_u64		iova;
	__u32			mask;
	__u32			first_psn;		/*wqe對應報文的第一個psn*/
	__u32			last_psn;		/*wqe對應報文的最后一個psn*/
	__u32			ack_length;
	__u32			ssn;
	__u32			has_rd_atomic;
	struct rxe_dma_info	dma;	/*sge信息*/
};

說明:

(1)post send敲doorbel是向內(nei)核發送了POST_SEND的命令,代碼(ma)如下;

cmd.hdr.command	= IB_USER_VERBS_CMD_POST_SEND;
cmd.hdr.in_words = sizeof(cmd) / 4;
cmd.hdr.out_words = sizeof(resp) / 4;
cmd.response	= (uintptr_t)&resp;
cmd.qp_handle	= ibqp->handle;
cmd.wr_count	= 0;
cmd.sge_count	= 0;
cmd.wqe_size	= sizeof(struct ibv_send_wr);
write(ibqp->context->cmd_fd, &cmd, sizeof(cmd));

(2)報文(wen)發(fa)送(song)是在當前post send的上下文(wen)完成

(3)分片的報文(wen)要記錄buf信息(xi),一次(ci)只獲取mtu大小的報文(wen)(lookup mr),填充獲取addr和payload len

(4)對于需要(yao)(yao)ACK的wqe,需要(yao)(yao)pending,還需要(yao)(yao)放在SQ中(zhong)

(5)QP的ci在ack后(hou),更新

(6)post send過(guo)程加鎖了(分為用戶(hu)態PI加鎖和(he)內核態QP處(chu)理(li)報文過(guo)程加鎖)所(suo)以同時只有一(yi)個線(xian)(xian)程能操作隊列(lie)。多個線(xian)(xian)程操作同一(yi)個qp時,第(di)一(yi)個獲(huo)取(qu)到QP資源(yuan)的線(xian)(xian)程會繼續處(chu)理(li)到沒(mei)有wqe,后面(mian)的線(xian)(xian)程直接返(fan)回。

(7)最終發包是調(diao)用rxe_send()函數(shu)完成。

3RX-POST RECV

上(shang)圖是post recv的(de)過程(cheng)。(1)post recv過程(cheng)需要加(jia)鎖,所以同時只有一(yi)個線程(cheng)下(xia)發rqe(2)操作不會下(xia)限到內核(he)。結(jie)構體如下(xia):

struct rxe_recv_wqe {
	__aligned_u64		wr_id;
	__u32			num_sge;	/*sge個數*/
	__u32			padding;
	struct rxe_dma_info	dma;	/*sge*/
};

4RX

上圖是(shi)RX的(de)流程,rxe收到的(de)包(bao)只(zhi)有(you)IB頭+payload兩部分(fen),UDP之前的(de)包(bao)頭已(yi)經被剝(bo)離。收到的(de)包(bao)分(fen)為兩種(zhong)包(bao):(1)處(chu)理requester;(2)處(chu)理ACK報文;代碼如下(xia):

static inline void rxe_rcv_pkt(struct rxe_dev *rxe, struct rxe_pkt_info *pkt,struct sk_buff *skb)
{
	if (pkt->mask & RXE_REQ_MASK)
		rxe_resp_queue_pkt(rxe, pkt->qp, skb);
	else
		rxe_comp_queue_pkt(rxe, pkt->qp, skb);
}

5RX-請求報文

上圖是處理(li)request報(bao)文的過程。簡單說明(ming):

(1)RDMA read request在softirq上下文處理,獲取buf,通過rxe_send發送

(2)Requester 隊列中的報文大于1個(ge),在(zai)softirq中處理

(3)只有一個報(bao)文(wen),在UDP tunnel收包上下文(wen)處理

6RX-ACK報文

上圖(tu)是處理ACK報文的過程(cheng)。簡單(dan)說明:

(1)當respond 隊列中的(de)報文(wen)(wen)數量大于1,在softirp上下(xia)文(wen)(wen)處理

(2)一個報文,在UDP tunnel收包上(shang)下文處理

(3)沒(mei)有處理cq full的(de)情況,應該是認為報文是成功發(fa)送的(de),用戶不用關心(xin)。

(4)SQ ci ++

7RX-ACK報文之RDMA_READ_RESPOND

解析ACK報文(wen),判斷opcode是(shi)rdma read resp,處(chu)理過程如下:

(1)首(shou)先從SQ中獲取(qu)根據CI和(he)SQ_addr獲取(qu)wqe

(2)解析wqe,獲取要(yao)存儲報文的sge

(3)將rdma read resp的報文寫到對應的buf中(zhong)

  • 如果是多個rdma read resp報文,wqe還繼續維持pending,同時記錄寫sge的偏移(寫struct rxe_send_wqe->dma)。
  • 當多個rdma read resp 報文全部收齊后,ci ++,釋放wqe。

8RX-累計ACK功能

(1)首先從(cong)SQ中(zhong)獲取(qu)根據(ju)CI和(he)SQ_addr獲取(qu)wqe。

(2)解析wqe,發現wqe->last_psn(這個wqe對應的最后一個分片報文的psn) < ack 報文中的psn時(shi),認為是累計ack

  • Send/write操作,會處理到相同psn的wqe即可,一個wqe上送一個cqe。如果不是全部上送cqe或者不要求上送cqe,則不上送cqe
  • 如果pending的wqe是Rdma read resp 或者atomic報文就任務丟包了,需要重傳

使用方法:

1、前提條件:

(1)確定設備(bei)上是(shi)否(fou)有rxe的用(yong)戶態(tai)驅(qu)動,不存在(zai)需要更新/安裝rdma-core

  • 查看/etc/libibverbs.d/目錄下是否有driver
  • 查看設備上是否有librxe-rdma.so

(2)確定設備上(shang)是否有(you)IB、rxe的內核驅(qu)動,不存在就(jiu)找對應的內核源碼,單獨編譯driver/infiniband/生(sheng)成驅(qu)動

  • 查看是否有ko,ib_core.ko,rdma_rxe.ko

2、添加設備

(1)兩種方法:

  • 通過rdma link add命令,(只在高版本內核中支持)
    • rdma link add xxx type rxe netdev xxx 命令通過netlink與內核IB模塊交互,與c中的注冊模塊完成rxe設備的添加

  • 通過sysfs添加
    • echo xxx(網卡) > /sys/module/rdma_rxe/parameters/add。如果沒有parameters目錄,就是加載ko不正確。

3、性能測試:

使用perftest測試(shi),使用標(biao)卡和cx6對打測試(shi),中間一跳交換機

  • 8個進程占用8個核,帶寬:22Gb
  • 時延:26us
  • PPS:0.5M

分析

  • 數據面需要陷入內核操作
  • 數據包需要經過一次完整的協議棧
  • rxe發包過程中,需要將內存通過CPU將數據寫入發包緩存中,網卡還需要一次dma才能將數據發送出去
  • rxe的收包是瓶頸,寫內存是CPU處理 softirq

4、測試網卡的選擇

  • 盡可能的使用不帶有rdma功能的網卡
  • 使用Mlnx網卡測試rxe的時候,網卡會截獲rdma報文,不會上送rxe。

 

文章來自個人專欄
文章 | 訂閱
0條評論
0 / 1000
請輸入你的評論
0
0