| import unittest |
| import random |
| import errno |
| |
| from pyverbs.wq import WQInitAttr, WQAttr, WQ, RwqIndTableInitAttr, RwqIndTable, RxHashConf |
| from tests.utils import requires_root_on_eth, PacketConsts |
| from tests.base import RDMATestCase, PyverbsRDMAError, MLNX_VENDOR_ID, \ |
| CX3_MLNX_PART_ID, CX3Pro_MLNX_PART_ID |
| from pyverbs.qp import QPInitAttrEx, QPEx |
| from tests.test_flow import FlowRes |
| from pyverbs.flow import Flow |
| from pyverbs.cq import CQ |
| import pyverbs.enums as e |
| import tests.utils as u |
| |
| |
| WRS_PER_ROUND = 512 |
| CQS_NUM = 2 |
| TOEPLITZ_KEY_LEN = 40 |
| HASH_KEY = [0x2c, 0xc6, 0x81, 0xd1, 0x5b, 0xdb, 0xf4, 0xf7, |
| 0xfc, 0xa2, 0x83, 0x19, 0xdb, 0x1a, 0x3e, 0x94, |
| 0x6b, 0x9e, 0x38, 0xd9, 0x2c, 0x9c, 0x03, 0xd1, |
| 0xad, 0x99, 0x44, 0xa7, 0xd9, 0x56, 0x3d, 0x59, |
| 0x06, 0x3c, 0x25, 0xf3, 0xfc, 0x1f, 0xdc, 0x2a] |
| |
| |
| def requires_indirection_table_support(func): |
| def wrapper(instance): |
| dev_attrs = instance.ctx.query_device() |
| vendor_id = dev_attrs.vendor_id |
| vendor_pid = dev_attrs.vendor_part_id |
| if vendor_id == MLNX_VENDOR_ID and vendor_pid in [CX3_MLNX_PART_ID, |
| CX3Pro_MLNX_PART_ID]: |
| raise unittest.SkipTest('WQN must be aligned with the Indirection Table size in CX3') |
| return func(instance) |
| return wrapper |
| |
| |
| class RssRes(FlowRes): |
| def __init__(self, dev_name, ib_port, gid_index, log_ind_tbl_size=3): |
| """ |
| Initialize rss resources based on Flow resources that include RSS Raw QP. |
| :param dev_name: Device name to be used |
| :param ib_port: IB port of the device to use |
| :param gid_index: Which GID index to use |
| """ |
| self.log_ind_tbl_size = log_ind_tbl_size |
| self.wqs = [] |
| self.cqs = [] |
| self.ind_table = None |
| super().__init__(dev_name=dev_name, ib_port=ib_port, |
| gid_index=gid_index) |
| |
| def create_cq(self): |
| self.cqs = [CQ(self.ctx, WRS_PER_ROUND) for _ in range(CQS_NUM)] |
| |
| @requires_root_on_eth() |
| def create_qps(self): |
| """ |
| Initializes self.qps with RSS QPs. |
| :return: None |
| """ |
| qp_init_attr = self.create_qp_init_attr() |
| for _ in range(self.qp_count): |
| try: |
| qp = QPEx(self.ctx, qp_init_attr) |
| self.qps.append(qp) |
| self.qps_num.append(qp.qp_num) |
| self.psns.append(random.getrandbits(24)) |
| except PyverbsRDMAError as ex: |
| if ex.error_code == errno.EOPNOTSUPP: |
| raise unittest.SkipTest(f'Create QPEx type {qp_init_attr.qp_type} is not ' |
| 'supported') |
| raise ex |
| |
| def create_qp_init_attr(self): |
| self.create_ind_table() |
| mask = e.IBV_QP_INIT_ATTR_CREATE_FLAGS | e.IBV_QP_INIT_ATTR_PD | \ |
| e.IBV_QP_INIT_ATTR_RX_HASH | e.IBV_QP_INIT_ATTR_IND_TABLE |
| return QPInitAttrEx(qp_type=e.IBV_QPT_RAW_PACKET, comp_mask=mask, pd=self.pd, |
| hash_conf=self.hash_conf, ind_table=self.ind_tbl) |
| |
| @requires_indirection_table_support |
| def create_ind_table(self): |
| self.ind_tbl = RwqIndTable(self.ctx, self.initiate_table_attr()) |
| self.hash_conf = self.init_rx_hash_config() |
| |
| def initiate_table_attr(self): |
| self.create_wqs() |
| return RwqIndTableInitAttr(self.log_ind_tbl_size, self.wqs) |
| |
| def create_wqs(self): |
| wqias = [self.initiate_wq_attr(cq) for cq in self.cqs] |
| for i in range(1 << self.log_ind_tbl_size): |
| wq = WQ(self.ctx, wqias[i % CQS_NUM]) |
| wq.modify(WQAttr(attr_mask=e.IBV_WQ_ATTR_STATE, wq_state=e.IBV_WQS_RDY)) |
| self.wqs.append(wq) |
| return self.wqs |
| |
| def initiate_wq_attr(self, cq): |
| return WQInitAttr(wq_context=None, wq_pd=self.pd, wq_cq=cq, wq_type=e.IBV_WQT_RQ, |
| max_wr=WRS_PER_ROUND, max_sge=self.ctx.query_device().max_sge, |
| comp_mask=0, create_flags=0) |
| |
| def init_rx_hash_config(self): |
| return RxHashConf(rx_hash_function=e.IBV_RX_HASH_FUNC_TOEPLITZ, |
| rx_hash_key_len=len(HASH_KEY), |
| rx_hash_key=HASH_KEY, |
| rx_hash_fields_mask=e.IBV_RX_HASH_DST_IPV4 | e.IBV_RX_HASH_SRC_IPV4) |
| |
| def _create_flow(self, flow_attr): |
| return [Flow(qp, flow_attr) for qp in self.qps] |
| |
| |
| class RSSTrafficTest(RDMATestCase): |
| """ |
| Test various functionalities of the RSS QPs. |
| """ |
| def setUp(self): |
| super().setUp() |
| self.iters = 1 |
| self.server = None |
| self.client = None |
| |
| def create_players(self): |
| """ |
| Init RSS tests resources. |
| RSS-QP can recive traffic only, so client will be based on Flow tests resources. |
| """ |
| self.client = FlowRes(**self.dev_info) |
| self.server = RssRes(**self.dev_info) |
| |
| def flow_traffic(self, specs, l3=PacketConsts.IP_V4, |
| l4=PacketConsts.UDP_PROTO): |
| """ |
| Execute raw ethernet traffic with given specs flow. |
| :param specs: List of flow specs to match on the QP |
| :param l3: Packet layer 3 type: 4 for IPv4 or 6 for IPv6 |
| :param l4: Packet layer 4 type: 'tcp' or 'udp' |
| :return: None |
| """ |
| self.flows = self.server.create_flow(specs) |
| u.raw_rss_traffic(self.client, self.server, self.iters, l3, l4, |
| num_packets=32) |
| |
| def test_rss_traffic(self): |
| self.create_players() |
| self.flow_traffic([self.server.create_eth_spec()]) |