hinic3 WQE(Work Queue Element)结构详解
本文基于 hinic3
驱动源码,对 WQE(Work Queue Element)做详细讲解。如需查阅完整源码和结构体定义可参考hinic3_nic_qp.h等文件。
1. WQE 的作用
- WQE(Work Queue Element):网卡芯片可识别的数据格式。
- sk_buff:Linux 协议栈中的数据结构,硬件不可直接识别。
- 驱动作用:负责将
sk_buff
转换为 WQE,供硬件使用。
类比
+---------------------+ 转换 +------------------+ 交互
| 上层 sk_buff | -------------> | WQE | ----> 网卡芯片
+---------------------+ 驱动 +------------------+ 硬件
2. SQ队列与wqebb
- SQ:发送队列(Send Queue)。
- wqebb(Work Queue Element Basic Block):SQ的最小分配单元,每个大小为16字节(16B)。
- 最大深度:16K(16384)个 wqebb。
- 一个WQE 可以包括多个 wqebb。
+---------+---------+---------+ ... +---------+
| wqebb 0 | wqebb 1 | wqebb 2 | ... | wqebb N |
+---------+---------+---------+ ... +---------+
|<--- 一个WQE由多个wqebb组成 --->|
相关宏定义见 hinic3_nic_io.h:
#define HINIC3_SQ_WQEBB_SHIFT 4
#define HINIC3_SQ_WQEBB_SIZE BIT(HINIC3_SQ_WQEBB_SHIFT) // 16B
3. SQ WQE 的三种类型
设计目的
为提升端到端性能,hinic3 的 SQ WQE 结构支持三种类型:
- 1. Extended SQ WQE with normal TS(支持无状态卸载,结构最复杂,支持各种 offload)
- 2. Extended SQ WQE with small TS(不支持无状态卸载,支持多SGE)
- 3. Compact SQ normal WQE(不支持无状态卸载,且只含单SGE,结构最简单)
4. WQE 结构与bit详细解析
4.1 Extended SQ WQE with normal TS(支持无状态卸载)
结构组成
- CTRL(Control Segment):描述WQE属性、部分卸载信息
- TASK:状态卸载标志(16B)
- BDSL(Buffer Descriptor Segment List):指向数据块的指针数组
- QSF:卸载相关任务字段
结构框图
CTRL字段详细bit位解释(来自hinic3_nic_qp.h)
字段 | 含义 |
---|---|
O | Owner bit,在队列翻转时,需要进行 obit 的翻转 |
EC | wqe type: 1’b1 - extended wqe type;1’b0 - compact wqe type |
DN | 1’b1 - Direct wqe type;1’b0 - Normal wqe type,NIC 默认设置为 0 |
DF | Data format:1’b0 – SGL;1’b1 - inline data,NIC 使用 SGL 方式 |
TS | Task section size:1’b1 - 16B;1’b0 - 46bit |
BDSL | SGE number(包括 wqe header 和 BDSL section 中的 sge 数量) |
相关宏定义:
#define SQ_CTRL_BD0_LEN_SHIFT 0
#define SQ_CTRL_RSVD_SHIFT 18
#define SQ_CTRL_BUFDESC_NUM_SHIFT 19
#define SQ_CTRL_TASKSECT_LEN_SHIFT 27
#define SQ_CTRL_DATA_FORMAT_SHIFT 28
#define SQ_CTRL_DIRECT_SHIFT 29
#define SQ_CTRL_EXTENDED_SHIFT 30
#define SQ_CTRL_OWNER_SHIFT 31
#define SQ_CTRL_BD0_LEN_MASK 0x3FFFFU
#define SQ_CTRL_RSVD_MASK 0x1U
#define SQ_CTRL_BUFDESC_NUM_MASK 0xFFU
#define SQ_CTRL_TASKSECT_LEN_MASK 0x1U
#define SQ_CTRL_DATA_FORMAT_MASK 0x1U
#define SQ_CTRL_DIRECT_MASK 0x1U
#define SQ_CTRL_EXTENDED_MASK 0x1U
#define SQ_CTRL_OWNER_MASK 0x1U
TASK字段结构(16B)
相关宏定义:
#define SQ_TASK_INFO0_TUNNEL_FLAG_SHIFT 19
#define SQ_TASK_INFO0_ESP_NEXT_PROTO_SHIFT 22
#define SQ_TASK_INFO0_INNER_L4_EN_SHIFT 24
#define SQ_TASK_INFO0_INNER_L3_EN_SHIFT 25
#define SQ_TASK_INFO0_INNER_L4_PSEUDO_SHIFT 26
#define SQ_TASK_INFO0_OUT_L4_EN_SHIFT 27
#define SQ_TASK_INFO0_OUT_L3_EN_SHIFT 28
#define SQ_TASK_INFO0_OUT_L4_PSEUDO_SHIFT 29
#define SQ_TASK_INFO0_ESP_OFFLOAD_SHIFT 30
#define SQ_TASK_INFO0_IPSEC_PROTO_SHIFT 31
#define SQ_TASK_INFO0_TUNNEL_FLAG_MASK 0x1U
#define SQ_TASK_INFO0_ESP_NEXT_PROTO_MASK 0x3U
#define SQ_TASK_INFO0_INNER_L4_EN_MASK 0x1U
#define SQ_TASK_INFO0_INNER_L3_EN_MASK 0x1U
#define SQ_TASK_INFO0_INNER_L4_PSEUDO_MASK 0x1U
#define SQ_TASK_INFO0_OUT_L4_EN_MASK 0x1U
#define SQ_TASK_INFO0_OUT_L3_EN_MASK 0x1U
#define SQ_TASK_INFO0_OUT_L4_PSEUDO_MASK 0x1U
#define SQ_TASK_INFO0_ESP_OFFLOAD_MASK 0x1U
#define SQ_TASK_INFO0_IPSEC_PROTO_MASK 0x1U
QSF 字段(主要和卸载相关)
Field | Range | Comments |
---|---|---|
pri | [31:29] | RSV |
Uc | [28] | RSV |
sctp | [27] | Sctp packet. |
mss | [26:13] | mss |
tcp_udp_cs | [12] | RSV |
tso | [11] | Transmit segmentation offload is activated when the tso flag is set. Not support TSO, when pkt_type=2’b01; |
ufo | [10] | For UDP packet, engine reads the whole UDP packet from host by 1 DMA read, and IPSU calculates UDP checksum, uCode does IP segment. |
Pld_ofs | [9:2] | Pkt_type = 2’b00: Payload offset. It is the start position to calculate tcp/udp checksum or sctp CRC. Unit is 2B, the max value is 239 (239*2=478B) Pkt_type = 2’b01: Bit9:5 – RSV Bit4:2 – cmd_code to AAD |
Pkt_type | [1:0] | pkt_type: 2’b00 – normal sq WQE; 2’b01 – sq direct-forward WQE; Others – RSV; |
相关宏定义:
#define SQ_CTRL_QUEUE_INFO_PKT_TYPE_SHIFT 0
#define SQ_CTRL_QUEUE_INFO_PLDOFF_SHIFT 2
#define SQ_CTRL_QUEUE_INFO_UFO_SHIFT 10
#define SQ_CTRL_QUEUE_INFO_TSO_SHIFT 11
#define SQ_CTRL_QUEUE_INFO_TCPUDP_CS_SHIFT 12
#define SQ_CTRL_QUEUE_INFO_MSS_SHIFT 13
#define SQ_CTRL_QUEUE_INFO_SCTP_SHIFT 27
#define SQ_CTRL_QUEUE_INFO_UC_SHIFT 28
#define SQ_CTRL_QUEUE_INFO_PRI_SHIFT 29
#define SQ_CTRL_QUEUE_INFO_PKT_TYPE_MASK 0x3U
#define SQ_CTRL_QUEUE_INFO_PLDOFF_MASK 0xFFU
#define SQ_CTRL_QUEUE_INFO_UFO_MASK 0x1U
#define SQ_CTRL_QUEUE_INFO_TSO_MASK 0x1U
#define SQ_CTRL_QUEUE_INFO_TCPUDP_CS_MASK 0x1U
#define SQ_CTRL_QUEUE_INFO_MSS_MASK 0x3FFFU
#define SQ_CTRL_QUEUE_INFO_SCTP_MASK 0x1U
#define SQ_CTRL_QUEUE_INFO_UC_MASK 0x1U
#define SQ_CTRL_QUEUE_INFO_PRI_MASK 0x7U
BDSL(Buffer Descriptor Segment List)
由若干个 struct hinic3_sq_bufdesc
组成,每个struct hinic3_sq_bufdesc
结构体可以包含一个SGE 数据:
struct hinic3_sq_bufdesc {
u32 len; // 31bit长度
u32 rsvd;
u32 hi_addr; // 高32位物理地址
u32 lo_addr; // 低32位物理地址
};
4.2 Extended SQ WQE with small TS(不支持无状态卸载,多SGE)
- 结构类似normal TS,但不能用offload。
- BDSL部分要求sge_num >= 2,CTRL里可以带一个SGE指针。
4.3 Compact SQ normal WQE(单SGE,极简)
- 只占用1个wqebb(16B),极致精简。
- 适用于不需要offload且报文只含1个SGE的场景。
5. Doorbell 结构详解
驱动填充完WQE后,用doorbell机制通知硬件。hinic3采用64bit doorbell,结构如下:
Doorbell数据结构(hinic3_nic_io.h)
struct hinic3_nic_db {
u32 db_info;
u32 pi_hi;
};
db_info字段按位解释:
- QID[12:0]:队列编号
- CFLAG[23]:0表示SQ, 1表示RQ
- Cos[26:24]:优先级
- Type[31:27]:类型(一般1)
- PI: Producer Index
相关宏定义:
#define DB_INFO_QID_SHIFT 0
#define DB_INFO_NON_FILTER_SHIFT 22
#define DB_INFO_CFLAG_SHIFT 23
#define DB_INFO_COS_SHIFT 24
#define DB_INFO_TYPE_SHIFT 27
#define DB_INFO_QID_MASK 0x1FFFU
#define DB_INFO_NON_FILTER_MASK 0x1U
#define DB_INFO_CFLAG_MASK 0x1U
#define DB_INFO_COS_MASK 0x7U
#define DB_INFO_TYPE_MASK 0x1FU
6. RQ WQE 结构与bit详细解析
6.1 RQ WQE 结构体
hinic3 的 RQ(Receive Queue)WQE 主要有两种结构:普通模式和扩展模式。其核心结构体如下:
struct hinic3_rq_normal_wqe {
u32 buf_hi_addr;
u32 buf_lo_addr;
u32 cqe_hi_addr;
u32 cqe_lo_addr;
};
struct hinic3_rq_extend_wqe {
struct hinic3_sge_sect buf_desc;
struct hinic3_sge_sect cqe_sect;
};
struct hinic3_rq_wqe {
union {
struct hinic3_rq_normal_wqe normal_wqe;
struct hinic3_rq_extend_wqe extend_wqe;
};
};
- buf_hi_addr / buf_lo_addr:数据缓冲区物理地址高/低 32 位。
- cqe_hi_addr / cqe_lo_addr:CQE 缓冲区物理地址高/低 32 位。
- buf_desc / cqe_sect:扩展模式下的 SGE 描述符。
由结构体可见,RQ WQE 也是16byte。其内存分配和管理与SQ WQE共用一套代码和接口。有差异的是结构体中每bit 的含义不一样。
6.2 RQ CQE 结构体
硬件收包后会写入 CQE(Completion Queue Element),结构如下:
struct hinic3_rq_cqe {
u32 status;
u32 vlan_len;
u32 offload_type;
u32 hash_val;
u32 xid;
u32 decrypt_info;
u32 rsvd6;
u32 pkt_info;
};
RQ WQE/CQE 结构框图
+-------------------+-------------------+-------------------+-------------------+
| status (32b) | vlan_len (32b) | offload_type (32b)| hash_val (32b) |
+-------------------+-------------------+-------------------+-------------------+
| xid (32b) | decrypt_info(32b) | rsvd6 (32b) | pkt_info (32b) |
+-------------------+-------------------+-------------------+-------------------+
主要字段说明
字段 | 含义 |
---|---|
status | 状态字段,包含校验和错误、LRO、RXDONE 等信息 |
vlan_len | 高 16 位为 VLAN Tag,低 16 位为包长度 |
offload_type | 硬件卸载类型,包含包类型、IP 类型、VLAN、RSS 等 |
hash_val | RSS Hash 值 |
xid | 交换 ID |
decrypt_info | 解密相关信息 |
pkt_info | 超级 CQE、包数、首包/尾包长度等 |
6.3 关键bit位与宏定义
1. offload_type 字段
字段 | 位宽/偏移 | 说明 |
---|---|---|
PKT_TYPE | [4:0] | 包类型 |
IP_TYPE | [6:5] | IP 类型 |
ENC_L3_TYPE | [7] | 封装 L3 类型 |
TUNNEL_FMT | [11:8] | 隧道包格式 |
PKT_UMBCAST | [20:19] | 组播/广播 |
VLAN_EN | [21] | VLAN 有效 |
RSS_TYPE | [31:24] | RSS 类型 |
相关宏定义:
#define RQ_CQE_OFFOLAD_TYPE_PKT_TYPE_SHIFT 0
#define RQ_CQE_OFFOLAD_TYPE_IP_TYPE_SHIFT 5
#define RQ_CQE_OFFOLAD_TYPE_ENC_L3_TYPE_SHIFT 7
#define RQ_CQE_OFFOLAD_TYPE_TUNNEL_PKT_FORMAT_SHIFT 8
#define RQ_CQE_OFFOLAD_TYPE_PKT_UMBCAST_SHIFT 19
#define RQ_CQE_OFFOLAD_TYPE_VLAN_EN_SHIFT 21
#define RQ_CQE_OFFOLAD_TYPE_RSS_TYPE_SHIFT 24
#define RQ_CQE_OFFOLAD_TYPE_PKT_TYPE_MASK 0x1FU
#define RQ_CQE_OFFOLAD_TYPE_IP_TYPE_MASK 0x3U
#define RQ_CQE_OFFOLAD_TYPE_ENC_L3_TYPE_MASK 0x1U
#define RQ_CQE_OFFOLAD_TYPE_TUNNEL_PKT_FORMAT_MASK 0xFU
#define RQ_CQE_OFFOLAD_TYPE_PKT_UMBCAST_MASK 0x3U
#define RQ_CQE_OFFOLAD_TYPE_VLAN_EN_MASK 0x1U
#define RQ_CQE_OFFOLAD_TYPE_RSS_TYPE_MASK 0xFFU
2. vlan_len 字段
- 高 16 位为 VLAN Tag,低 16 位为包长度。
相关宏定义:
#define RQ_CQE_SGE_VLAN_SHIFT 0
#define RQ_CQE_SGE_LEN_SHIFT 16
#define RQ_CQE_SGE_VLAN_MASK 0xFFFFU
#define RQ_CQE_SGE_LEN_MASK 0xFFFFU
3. status 字段
字段 | 位宽/偏移 | 说明 |
---|---|---|
CSUM_ERR | [15:0] | 校验和错误 |
NUM_LRO | [23:16] | LRO 包数 |
LRO_PUSH | [25] | LRO 推送 |
LRO_ENTER | [26] | LRO 进入 |
LRO_INTR | [27] | LRO 中断 |
FLUSH | [28] | Flush 标志 |
DECRY_PKT | [29] | 解密包 |
BP_EN | [30] | BP 使能 |
RXDONE | [31] | 收包完成 |
相关宏定义:
#define RQ_CQE_STATUS_CSUM_ERR_SHIFT 0
#define RQ_CQE_STATUS_NUM_LRO_SHIFT 16
#define RQ_CQE_STATUS_LRO_PUSH_SHIFT 25
#define RQ_CQE_STATUS_LRO_ENTER_SHIFT 26
#define RQ_CQE_STATUS_LRO_INTR_SHIFT 27
#define RQ_CQE_STATUS_BP_EN_SHIFT 30
#define RQ_CQE_STATUS_RXDONE_SHIFT 31
#define RQ_CQE_STATUS_DECRY_PKT_SHIFT 29
#define RQ_CQE_STATUS_FLUSH_SHIFT 28
#define RQ_CQE_STATUS_CSUM_ERR_MASK 0xFFFFU
#define RQ_CQE_STATUS_NUM_LRO_MASK 0xFFU
#define RQ_CQE_STATUS_LRO_PUSH_MASK 0X1U
#define RQ_CQE_STATUS_LRO_ENTER_MASK 0X1U
#define RQ_CQE_STATUS_LRO_INTR_MASK 0X1U
#define RQ_CQE_STATUS_BP_EN_MASK 0X1U
#define RQ_CQE_STATUS_RXDONE_MASK 0x1U
#define RQ_CQE_STATUS_FLUSH_MASK 0x1U
#define RQ_CQE_STATUS_DECRY_PKT_MASK 0x1U
4. pkt_info 字段
- 超级 CQE、包数、首包/尾包长度等。
相关宏定义:
#define RQ_CQE_SUPER_CQE_EN_MASK 0x1
#define RQ_CQE_PKT_NUM_MASK 0x1FU
#define RQ_CQE_PKT_FIRST_LEN_MASK 0x1FFFU
#define RQ_CQE_PKT_LAST_LEN_MASK 0x1FFFU
5. decrypt_info 字段
- 解密状态、ESP next header。
相关宏定义:
#define RQ_CQE_DECRY_INFO_DECRY_STATUS_SHIFT 8
#define RQ_CQE_DECRY_INFO_ESP_NEXT_HEAD_SHIFT 0
#define RQ_CQE_DECRY_INFO_DECRY_STATUS_MASK 0xFFU
#define RQ_CQE_DECRY_INFO_ESP_NEXT_HEAD_MASK 0xFFU
6.4 RQ CQE 字段解析流程示例
static inline u32 hinic3_get_pkt_len_for_super_cqe(const struct hinic3_rq_cqe *cqe, bool last)
{
u32 pkt_len = hinic3_hw_cpu32(cqe->pkt_info);
if (!last)
return RQ_CQE_PKT_LEN_GET(pkt_len, FIRST_LEN);
else
return RQ_CQE_PKT_LEN_GET(pkt_len, LAST_LEN);
}