File: test_mlx5_flow.py

package info (click to toggle)
rdma-core 61.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 13,124 kB
  • sloc: ansic: 176,798; python: 15,496; sh: 2,742; perl: 1,465; makefile: 73
file content (589 lines) | stat: -rw-r--r-- 29,056 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
# SPDX-License-Identifier: (GPL-2.0 OR Linux-OpenIB)
# Copyright (c) 2020 Nvidia All rights reserved. See COPYING file
"""
Test module for pyverbs' mlx5 flow module.
"""

import unittest
import errno

from pyverbs.providers.mlx5.mlx5dv_flow import Mlx5FlowMatcher, \
    Mlx5FlowMatcherAttr, Mlx5FlowMatchParameters, Mlx5FlowActionAttr, Mlx5Flow,\
    Mlx5PacketReformatFlowAction
from pyverbs.providers.mlx5.mlx5dv import Mlx5Context, Mlx5DVContextAttr, Mlx5DevxObj
from pyverbs.pyverbs_error import PyverbsRDMAError, PyverbsUserError
from pyverbs.providers.mlx5.mlx5_enums import mlx5dv_flow_matcher_attr_mask, mlx5dv_flow_matcher_attr_mask, mlx5dv_flow_action_type, \
    MLX5DV_FLOW_TABLE_TYPE_NIC_RX_, MLX5DV_FLOW_TABLE_TYPE_RDMA_TRANSPORT_RX_, MLX5DV_FLOW_TABLE_TYPE_RDMA_TRANSPORT_TX_, \
    MLX5DV_FLOW_TABLE_TYPE_RDMA_RX_, MLX5DV_FLOW_TABLE_TYPE_RDMA_TX_, MLX5DV_FLOW_TABLE_TYPE_NIC_TX_, \
    MLX5DV_FLOW_ACTION_PACKET_REFORMAT_TYPE_L2_TO_L2_TUNNEL_
from pyverbs.libibverbs_enums import ibv_flow_flags
from tests.mlx5_base import Mlx5RDMATestCase, create_privileged_context, Mlx5RcResources
from tests.utils import PacketConsts, is_eth, requires_no_sriov, requires_eth,\
    requires_cap_net_raw, requires_root
from tests.base import RawResources
import tests.utils as u
import struct


MAX_MATCH_PARAM_SIZE = 0x180
NIC_RX_RDMA_TABLE_TYPE = 0X7
NIC_TX_RDMA_TABLE_TYPE = 0X8
RDMA_TRANSPORT_RX = 0xd
RDMA_TRANSPORT_TX = 0xe
DROP_ACTION = 0X2
ALLOW_ACTION = 0X1
TABLE_LEVEL = 200


@u.skip_unsupported
def requires_reformat_support(func):
    def func_wrapper(instance):
        nic_tbl_caps = u.query_nic_flow_table_caps(instance)
        # Verify that both NIC RX and TX support reformat actions by checking
        # the following PRM fields: encap_general_header,
        # log_max_packet_reformat, and reformat (for both RX and TX).
        if not(nic_tbl_caps.encap_general_header and
               nic_tbl_caps.log_max_packet_reformat_context and
               nic_tbl_caps.flow_table_properties_nic_receive.reformat and
               nic_tbl_caps.flow_table_properties_nic_transmit.reformat):
            raise unittest.SkipTest('NIC flow table does not support reformat')
        return func(instance)
    return func_wrapper


def gen_vxlan_l2_tunnel_encap_header(msg_size):
    vxlan_header = u.gen_vxlan_header()
    udp_header = u.gen_udp_header(packet_len=msg_size + len(vxlan_header),
                                  dst_port=PacketConsts.VXLAN_PORT)
    ip_header = u.gen_ipv4_header(packet_len=msg_size + len(vxlan_header) + len(udp_header))
    mac_header = u.gen_ethernet_header()
    return mac_header + ip_header + udp_header + vxlan_header


def check_rdma_transport_domain_caps(agr_obj):
    """
    Check if the device and the given resources support rdma transport domain
    creation. If not, it raises unittest.SkipTest.
    This function should be called (directly or indirectly) from test functions
    only.
    :param agr_obj: Aggregation object which contains all resources necessary.
    """
    from tests.mlx5_prm_structs import QueryAdvRdmaCapOut, \
    QueryHcaCapIn, QueryCmdHcaCapOut, QueryHcaCapOp, QueryHcaCapMod
    query_cap_in = QueryHcaCapIn(op_mod=0x1)
    query_cap_out = QueryCmdHcaCapOut(agr_obj.ctx.devx_general_cmd(
        query_cap_in, len(QueryCmdHcaCapOut())))

    if query_cap_out.status:
        raise unittest.SkipTest('Failed to query general HCA CAPs with syndrome '
                                f'({query_cap_out.syndrome}')

    if not query_cap_out.capability.adv_rdma_cap:
        raise unittest.SkipTest("The device doesn't support adv_rdma_cap")

    query_adv_rdma_cap_in = QueryHcaCapIn(op_mod=(QueryHcaCapOp.ADV_RDMA_CAP << 0x1) | \
                                            QueryHcaCapMod.CURRENT)
    query_adv_rdma_cap_out = QueryAdvRdmaCapOut(agr_obj.ctx.devx_general_cmd(
        query_adv_rdma_cap_in, len(QueryAdvRdmaCapOut())))

    if not query_adv_rdma_cap_out.capability.rdma_transport_rx_flow_table_properties.ft_support or \
       not query_adv_rdma_cap_out.capability.rdma_transport_tx_flow_table_properties.ft_support:
        raise unittest.SkipTest("The device doesn't support the RDMA transport domain")


class Mlx5FlowResources(RawResources):
    def create_matcher(self, mask, match_criteria_enable, flags=0,
                       ft_type=MLX5DV_FLOW_TABLE_TYPE_NIC_RX_):
        """
        Creates a matcher from a provided mask.
        :param mask: The mask to match on (in bytes)
        :param match_criteria_enable: Bitmask representing which of the
                                      headers and parameters in match_criteria
                                      are used
        :param flags: Flow matcher flags
        :param ft_type: Flow table type
        :return: Resulting matcher
        """
        try:
            flow_match_param = Mlx5FlowMatchParameters(len(mask), mask)
            attr = Mlx5FlowMatcherAttr(match_mask=flow_match_param,
                                       match_criteria_enable=match_criteria_enable,
                                       flags=flags, ft_type=ft_type)
            matcher = Mlx5FlowMatcher(self.ctx, attr)
        except PyverbsRDMAError as ex:
            if ex.error_code in [errno.EOPNOTSUPP, errno.EPROTONOSUPPORT]:
                raise unittest.SkipTest('Matcher creation is not supported')
            raise ex
        return matcher

    @requires_cap_net_raw()
    @requires_eth()
    def create_qps(self):
        super().create_qps()


class Mlx5CounterFlowResources(Mlx5FlowResources):

    def create_context(self):
        mlx5dv_attr = Mlx5DVContextAttr()
        try:
            self.ctx = Mlx5Context(mlx5dv_attr, name=self.dev_name)
        except PyverbsUserError as ex:
            raise unittest.SkipTest(f'Could not open mlx5 context ({ex})')
        except PyverbsRDMAError:
            raise unittest.SkipTest('Opening mlx5 context is not supported')

    def query_bulk_counter_cap(self):
        """
        Query the device for the maximum allowed bulk allocation size for flow counters.
        This method queries the HCA capabilities to determine the maximum log2 bulk size
        that can be allocated for flow counters. It first checks for general HCA capabilities,
        then queries HCA_CAP_2 for the specific flow_counter_bulk_log_max_alloc field.
        :return: The maximum log2 bulk allocation size for flow counters.
        """
        from tests.mlx5_prm_structs import QueryCmdHcaCap2Out, \
            QueryHcaCapIn, QueryCmdHcaCapOut, QueryHcaCapOp, QueryHcaCapMod
        query_cap_in = QueryHcaCapIn(op_mod=0x1)
        query_cap_out = QueryCmdHcaCapOut(self.ctx.devx_general_cmd(
            query_cap_in, len(QueryCmdHcaCapOut())))
        if query_cap_out.status:
            raise PyverbsRDMAError('Failed to query general HCA CAPs with syndrome '
                                   f'({query_cap_out.syndrome}')

        if not query_cap_out.capability.hca_cap_2:
            raise unittest.SkipTest('The device doesn\'t support general HCA CAPs 2')
        query_cap2_in = QueryHcaCapIn(op_mod=(QueryHcaCapOp.HCA_CAP_2 << 0x1) | \
                                              QueryHcaCapMod.CURRENT)
        query_cap2_out = QueryCmdHcaCap2Out(self.ctx.devx_general_cmd(
            query_cap2_in, len(QueryCmdHcaCap2Out())))
        return query_cap2_out.capability.flow_counter_bulk_log_max_alloc

    @staticmethod
    def create_counter(ctx):
        """
        Create flow counter.
        :param ctx: The player context to create the counter on.
        :return: The counter object and the flow counter ID .
        """
        from tests.mlx5_prm_structs import AllocFlowCounterIn, AllocFlowCounterOut
        counter = Mlx5DevxObj(ctx, AllocFlowCounterIn(), len(AllocFlowCounterOut()))
        flow_counter_id = AllocFlowCounterOut(counter.out_view).flow_counter_id
        return counter, flow_counter_id

    def create_bulk_counter(self, ctx, bulk_size=0):
        """
        Create flow bulk counter.
        :param ctx: The player context to create the counter on.
        :param bulk_size: The bulk size to use for the counter.
        :return: The counter object and the flow counter ID .
        """
        from tests.mlx5_prm_structs import AllocFlowCounterIn, AllocFlowCounterOut
        bulk_log_max_alloc = self.query_bulk_counter_cap()
        if bulk_log_max_alloc > 0:
            # Bulk size is log2 of the number of counters eg. if bulk_size is 9, then there
            # are 512 counters
            cmd_in = AllocFlowCounterIn(flow_counter_bulk_log_size=bulk_size)
        else:
            # If bulk_log_max_alloc=0 then use old bitmask field, 0b100 means 512 counters
            cmd_in = AllocFlowCounterIn(flow_counter_bulk=0b100)
        counter = Mlx5DevxObj(ctx, cmd_in, len(AllocFlowCounterOut()))
        flow_counter_id = AllocFlowCounterOut(counter.out_view).flow_counter_id
        return counter, flow_counter_id

    @staticmethod
    def query_counter_packets(counter, flow_counter_id):
        """
        Query flow counter packets count.
        :param counter: The counter for the query.
        :param flow_counter_id: The flow counter ID for the query.
        :return: Number of packets on this counter.
        """
        from tests.mlx5_prm_structs import QueryFlowCounterIn, QueryFlowCounterOut
        query_in = QueryFlowCounterIn(flow_counter_id=flow_counter_id)
        counter_out = QueryFlowCounterOut(counter.query(query_in, len(QueryFlowCounterOut())))
        return counter_out.flow_statistics.packets

    @staticmethod
    def query_bulk_counter(counter, flow_counter_id, num_of_counters=1):
        """
        Query flow counter packets count for bulk counters.
        :param counter: The counter for the query.
        :param flow_counter_id: The flow counter ID for the query.
        :param num_of_counters: Number of counters to query.
        :return: Counter statistics.
        """
        from tests.mlx5_prm_structs import QueryFlowCounterIn, QueryBulkFlowCounterOut
        query_in = QueryFlowCounterIn(flow_counter_id=flow_counter_id,
                                      num_of_counters=num_of_counters)
        counter_out = QueryBulkFlowCounterOut(counter.query(query_in,
                                                            len(QueryBulkFlowCounterOut(
                                                                num_statistics=num_of_counters))))
        return counter_out.flow_statistics


class Mlx5RCFlowResources(Mlx5RcResources):
    def __init__(self, dev_name, ib_port, gid_index, is_privileged_ctx=False, **kwargs):
        """
        Initializes a Mlx5RCFlowResources object with the given values and creates
        basic RDMA resources.
        :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
        :param is_privileged_ctx: If True, creates a privileged context (default: False)
        """
        self.obj_to_cleanup = []
        self.is_privileged_ctx = is_privileged_ctx
        super().__init__(dev_name, ib_port, gid_index, **kwargs)

    def create_context(self):
        if self.is_privileged_ctx:
            create_privileged_context(self)
            check_rdma_transport_domain_caps(self)
            return
        super().create_context()

    def close_resources(self):
        for obj in self.obj_to_cleanup:
            if obj:
                obj.close()

    def create_matcher(self, mask, match_criteria_enable, flags=0,
                       ft_type=MLX5DV_FLOW_TABLE_TYPE_NIC_RX_, ib_port=1, priority=0):
        """
        Creates a matcher from a provided mask.
        :param mask: The mask to match on (in bytes)
        :param match_criteria_enable: Bitmask representing which of the
                                      headers and parameters in match_criteria
                                      are used
        :param flags: Flow matcher flags.
        :param ft_type: Flow table type.
        :param ib_port: Specify its corresponding port.
        :param priority: Priority value for the matcher.
        :return: Resulting matcher
        """
        try:
            flow_match_param = Mlx5FlowMatchParameters(len(mask), mask)
            comp_mask = mlx5dv_flow_matcher_attr_mask.MLX5DV_FLOW_MATCHER_MASK_FT_TYPE

            if ft_type in [MLX5DV_FLOW_TABLE_TYPE_RDMA_TRANSPORT_RX_,
                           MLX5DV_FLOW_TABLE_TYPE_RDMA_TRANSPORT_TX_]:
                if not is_eth(self.ctx, ib_port):
                    raise unittest.SkipTest('Must be run on Ethernet link layer')

                comp_mask |= mlx5dv_flow_matcher_attr_mask.MLX5DV_FLOW_MATCHER_MASK_IB_PORT

            attr = Mlx5FlowMatcherAttr(match_mask=flow_match_param,
                                       match_criteria_enable=match_criteria_enable,
                                       flags=flags, ft_type=ft_type, comp_mask=comp_mask,
                                       ib_port=ib_port, priority=priority)
            matcher = Mlx5FlowMatcher(self.ctx, attr)
        except PyverbsRDMAError as ex:
            if ex.error_code in [errno.EOPNOTSUPP, errno.EPROTONOSUPPORT]:
                raise unittest.SkipTest('Matcher creation is not supported')
            raise ex
        return matcher

    def store_for_cleanup_stage(self, flow_table_obj):
        """
        Stores the given object for cleanup in the correct order.
        :param flow_table_obj: The object to be stored.
        """
        self.obj_to_cleanup.insert(0, flow_table_obj)

    def create_devx_flow_table(self, table_type, level):
        """
        Creates a DEVX flow table with the given type and level.
        :param table_type: The flow table type.
        :param level: The flow table level.
        :return: A tuple containing the created flow table object and the table ID.
        """
        from tests.mlx5_prm_structs import CreateFlowTableIn,\
            CreateFlowTableOut, FlowTableContext,CreateFlowTableOut
        cmd_in = CreateFlowTableIn(table_type=table_type,
                                   flow_table_context=FlowTableContext(level=level))
        flow_table_obj = Mlx5DevxObj(self.ctx, cmd_in, len(CreateFlowTableOut()))
        self.store_for_cleanup_stage(flow_table_obj)
        out = CreateFlowTableOut(flow_table_obj.out_view)
        return flow_table_obj, out.table_id

    def create_devx_flow_group(self, table_id, table_type):
        """
        Creates a DEVX flow group for the specified table.
        :param table_id: The ID of the flow table.
        :param table_type: The type of the flow table.
        :return: The ID of the created flow group.
        """
        from tests.mlx5_prm_structs import CreateFlowGroupIn, CreateFlowGroupOut
        cmd_in = CreateFlowGroupIn(table_type=table_type,
                                   table_id=table_id)
        flow_group_ojb = Mlx5DevxObj(self.ctx, cmd_in, len(CreateFlowGroupOut()))
        self.store_for_cleanup_stage(flow_group_ojb)
        out = CreateFlowGroupOut(flow_group_ojb.out_view)
        return out.group_id

    def create_devx_flow_entry(self, table_id, group_id, action, table_type):
        """
        Creates a DEVX flow table entry with the specified parameters.
        :param table_id: The ID of the flow table.
        :param group_id: The ID of the flow group.
        :param action: The action for the flow entry.
        :param table_type: The type of the flow table.
        :return: The created flow table entry object.
        """
        from tests.mlx5_prm_structs import SetFlowTableEntryIn, FlowContext, SetFlowTableEntryOut
        cmd_in = SetFlowTableEntryIn(table_type=table_type, table_id=table_id,
                                     flow_context=FlowContext(group_id=group_id, action=action))
        flow_table_entry_obj = Mlx5DevxObj(self.ctx, cmd_in, len(SetFlowTableEntryOut()))
        self.store_for_cleanup_stage(flow_table_entry_obj)


class Mlx5MatcherTest(Mlx5RDMATestCase):
    def setUp(self):
        super().setUp()
        self.iters = 10
        self.server = None
        self.client = None

    def tearDown(self):
        """
        Cleans up resources if manual cleanup is needed.
        """
        if self.server and hasattr(self.server, 'obj_to_cleanup'):
            self.server.close_resources()
        if self.client and hasattr(self.client, 'obj_to_cleanup'):
            self.client.close_resources()
        super().tearDown()

    @u.skip_unsupported
    def test_create_empty_matcher(self):
        """
        Creates an empty matcher
        """
        self.res = Mlx5FlowResources(**self.dev_info)
        empty_mask = bytes(MAX_MATCH_PARAM_SIZE)
        self.res.create_matcher(empty_mask, u.MatchCriteriaEnable.NONE)

    @u.skip_unsupported
    def test_create_smac_matcher(self):
        """
        Creates a matcher to match on outer source mac
        """
        self.res = Mlx5FlowResources(**self.dev_info)
        smac_mask = bytes([0xff, 0xff, 0xff, 0xff, 0xff, 0xff])
        self.res.create_matcher(smac_mask, u.MatchCriteriaEnable.OUTER)

    def generic_test_mlx5_flow_table(self, flow_table_type_mapping, action, ib_port=1,
                                     traffic=False, priority=0):
        """
        This function performs the following steps on the server side:
        1. Creates a DEVX flow table with the specified table type.
        2. Creates a flow group in the DEVX flow table.
        3. Inserts a flow table entry (FTE) into the flow table group.
        4. Creates an mlx5 flow with an empty matcher (to steer all packets) on the specified
        table type, directing them to the DEVX flow table created in step #1.
        5. If the `traffic` flag is set to `True`, RDMA traffic is run to test the flow steering.

        :param flow_table_type_mapping: Dictionary mapping flow table types.
        :param action: Specifies the action to be performed on the matched packets.
        :param ib_port: The port for flow matching.
        :param traffic: Boolean flag indicating whether RDMA traffic should run.
        :param priority: Priority value for the matcher.
        """
        empty_mask = bytes(MAX_MATCH_PARAM_SIZE)

        for ft_type, table_type in flow_table_type_mapping.items():
            devx_table_obj, table_id = self.server.create_devx_flow_table(table_type,
                                                                          level=TABLE_LEVEL)
            matcher = self.server.create_matcher(empty_mask, u.MatchCriteriaEnable.NONE,
                                                 ft_type=ft_type, ib_port=ib_port,
                                                 priority=priority)
            self.server.store_for_cleanup_stage(matcher)
            group_id = self.server.create_devx_flow_group(table_id, table_type)
            self.server.create_devx_flow_entry(table_id, group_id, action, table_type)
            empty_value_param = Mlx5FlowMatchParameters(len(empty_mask), empty_mask)
            action_dest = Mlx5FlowActionAttr(action_type=mlx5dv_flow_action_type.MLX5DV_FLOW_ACTION_DEST_DEVX,
                                             obj=devx_table_obj)
            self.server.flow = Mlx5Flow(matcher, empty_value_param, [action_dest], 1)

            if traffic:
                u.traffic(client=self.client, server=self.server,iters=self.iters,
                          gid_idx=self.gid_index, port=self.ib_port, is_cq_ex=True)

    @u.skip_unsupported
    @requires_no_sriov()
    @requires_cap_net_raw()
    def test_flow_table_drop(self):
        """
        Creates rules with DevX objects for RDMA RX and TX tables.
            - Creates two flow tables, one for RDMA RX and one for RDMA TX.
            - Configures matchers with different flow tables.
            - Configures flow actions to drop.
        """
        self.create_players(Mlx5RCFlowResources)
        flow_table_type_mapping = {
            MLX5DV_FLOW_TABLE_TYPE_RDMA_RX_: NIC_RX_RDMA_TABLE_TYPE,
            MLX5DV_FLOW_TABLE_TYPE_RDMA_TX_: NIC_TX_RDMA_TABLE_TYPE}
        self.generic_test_mlx5_flow_table(flow_table_type_mapping, action=DROP_ACTION)

    @u.skip_unsupported
    @requires_root()
    def test_flow_rdma_transport_domain_traffic(self):
        """
        Creates a devx table object with the RDMA transport domain,
        Verifies that the traffic passes successfully.
        """
        self.rdma_transport_domain_test(priority=0)

    @u.skip_unsupported
    @requires_root()
    def test_priority_rdma_transport_domain_traffic(self):
        """
        Creates a devx table object with the RDMA transport domain with priority,
        Verifies that the traffic passes successfully.
        """
        self.rdma_transport_domain_test(priority=1)

    def rdma_transport_domain_test(self, priority):
        """
        :param priority: Priority value for the flow table matcher.
        """
        self.client = Mlx5RcResources(**self.dev_info)
        self.server = Mlx5RCFlowResources(is_privileged_ctx=True, **self.dev_info)
        self.pre_run()
        self.sync_remote_attr()
        flow_table_type_mapping = {
            MLX5DV_FLOW_TABLE_TYPE_RDMA_TRANSPORT_RX_: RDMA_TRANSPORT_RX,
            MLX5DV_FLOW_TABLE_TYPE_RDMA_TRANSPORT_TX_: RDMA_TRANSPORT_TX}
        self.generic_test_mlx5_flow_table(flow_table_type_mapping, action=ALLOW_ACTION, ib_port=1,
                                          traffic=True, priority=priority)

    @u.skip_unsupported
    def test_smac_matcher_to_qp_flow(self):
        """
        Creates a matcher to match on outer source mac and a flow that forwards
        packets to QP when matching on source mac.
        """
        self.create_players(Mlx5FlowResources)
        smac_mask = bytes([0xff] * 6)
        matcher = self.server.create_matcher(smac_mask,
                                             u.MatchCriteriaEnable.OUTER)
        smac_value = struct.pack('!6s',
                                 bytes.fromhex(PacketConsts.SRC_MAC.replace(':', '')))
        value_param = Mlx5FlowMatchParameters(len(smac_value), smac_value)
        action_qp = Mlx5FlowActionAttr(action_type=mlx5dv_flow_action_type.MLX5DV_FLOW_ACTION_DEST_IBV_QP,
                                       qp=self.server.qp)
        self.server.flow = Mlx5Flow(matcher, value_param, [action_qp], 1)
        u.raw_traffic(self.client, self.server, self.iters)

    @u.skip_unsupported
    def test_counter_qp_flow(self):
        """
        Creates a matcher to match on outer source mac and a flow that forwards
        packets to QP when matching on source mac.
        """
        self.create_players(Mlx5CounterFlowResources)
        empty_bytes_arr = bytes(MAX_MATCH_PARAM_SIZE)
        empty_value_param = Mlx5FlowMatchParameters(len(empty_bytes_arr), empty_bytes_arr)
        matcher = self.server.create_matcher(empty_bytes_arr, u.MatchCriteriaEnable.NONE)
        qp_attr = Mlx5FlowActionAttr(action_type=mlx5dv_flow_action_type.MLX5DV_FLOW_ACTION_DEST_IBV_QP,
                                     qp=self.server.qp)
        counter, flow_counter_id = self.server.create_counter(self.server.ctx)
        ctr_attr = Mlx5FlowActionAttr(action_type=mlx5dv_flow_action_type.MLX5DV_FLOW_ACTION_COUNTERS_DEVX,
                                      obj=counter)
        self.server.flow = Mlx5Flow(matcher, empty_value_param, [ctr_attr, qp_attr], 2)
        u.raw_traffic(self.client, self.server, self.iters)
        sent_packets = self.server.query_counter_packets(counter, flow_counter_id)
        self.assertEqual(sent_packets, self.iters, 'Counter of metadata missed some sent packets')

    @u.skip_unsupported
    def test_counters_bulk_flow(self):
        """
        Flow action counters with bulk size.
        Creates 4 flows with different dst ip addresses.
        Sends 10 packets to each flow.
        Verifies each flow get a hit and goes to QP final destination and counter is incremented
        accordingly.
        """
        from tests.mlx5_prm_structs import FlowTableEntryMatchSetLyr24, FlowTableEntryMatchParam

        self.create_players(Mlx5CounterFlowResources)
        outer_header = FlowTableEntryMatchSetLyr24(ethertype=0xffff, ip_version=0xf,
                                                   src_ip_mask=0xffffffff)
        mask = FlowTableEntryMatchParam(outer_headers=outer_header)
        matcher = self.server.create_matcher(mask, u.MatchCriteriaEnable.OUTER)
        action_qp_attr = Mlx5FlowActionAttr(
            action_type=mlx5dv_flow_action_type.MLX5DV_FLOW_ACTION_DEST_IBV_QP,
            qp=self.server.qp)
        # Create a counter with bulk_size=2 means there are 4 counters in the bulk.
        counter, flow_counter_id = self.server.create_bulk_counter(self.server.ctx, bulk_size=2)
        action_type = mlx5dv_flow_action_type.MLX5DV_FLOW_ACTION_COUNTERS_DEVX_WITH_OFFSET
        flows = []
        flows_num = 4

        for flow_idx in range(flows_num):
            src_ip = f'{flow_idx + 2}.{flow_idx + 2}.{flow_idx + 2}.{flow_idx + 2}'
            packet = u.gen_packet(self.client.msg_size, ip_version=4, src_ipv4=src_ip)
            # Each offset is different counter
            action_counter_attr = Mlx5FlowActionAttr(action_type=action_type, obj=counter,
                                                     offset=flow_idx)
            outer_header = FlowTableEntryMatchSetLyr24(ethertype=PacketConsts.ETHER_TYPE_IPV4,
                                                       ip_version=4, src_ip4=src_ip)
            value_param = Mlx5FlowMatchParameters(len(outer_header), outer_header)
            flows.append(Mlx5Flow(matcher, value_param, [action_counter_attr, action_qp_attr], 2))
            u.raw_traffic(self.client, self.server, self.iters, packet_to_send=packet)
            ctr_stats = self.server.query_bulk_counter(counter, flow_counter_id,
                                                       num_of_counters=flows_num)
            # Verify counters
            for j in range(flows_num):
                exp_num_pkts = self.iters if j <= flow_idx else 0
                self.assertEqual(ctr_stats[j].packets, exp_num_pkts,
                                 f'Counter {j} in metadata missed some sent packets')

    @requires_reformat_support
    @u.requires_encap_disabled_if_eswitch_on
    def test_tx_packet_reformat(self):
        """
        Creates packet reformat (encap) action on TX and with QP action on RX
        verifies that the packet was encapsulated as expected.
        """
        self.client = Mlx5FlowResources(**self.dev_info)
        outer = gen_vxlan_l2_tunnel_encap_header(self.client.msg_size)
        # Due to encapsulation action Ipv4 and UDP checksum of the outer header
        # will be recalculated, need to skip them during packet validation.
        ipv4_id_idx = [18, 19]
        ipv4_chksum_idx = [24, 25]
        udp_chksum_idx = [34, 35]
        # Server will receive encaped packet so message size must include the
        # length of the outer part.
        self.server = Mlx5FlowResources(msg_size=self.client.msg_size + len(outer),
                                        **self.dev_info)
        empty_bytes_arr = bytes(MAX_MATCH_PARAM_SIZE)
        empty_value_param = Mlx5FlowMatchParameters(len(empty_bytes_arr),
                                                    empty_bytes_arr)

        # TX steering
        tx_matcher = self.client.create_matcher(empty_bytes_arr,
                                                u.MatchCriteriaEnable.NONE,
                                                ibv_flow_flags.IBV_FLOW_ATTR_FLAGS_EGRESS,
                                                MLX5DV_FLOW_TABLE_TYPE_NIC_TX_)
        # Create encap action
        reformat_action = Mlx5PacketReformatFlowAction(
            self.client.ctx, data=outer,
            reformat_type=MLX5DV_FLOW_ACTION_PACKET_REFORMAT_TYPE_L2_TO_L2_TUNNEL_,
            ft_type=MLX5DV_FLOW_TABLE_TYPE_NIC_TX_)
        action_reformat_attr = Mlx5FlowActionAttr(flow_action=reformat_action,
                                                  action_type=mlx5dv_flow_action_type.MLX5DV_FLOW_ACTION_IBV_FLOW_ACTION)
        self.client.flow = Mlx5Flow(tx_matcher, empty_value_param,
                                    [action_reformat_attr], 1)

        # RX steering
        rx_matcher = self.server.create_matcher(empty_bytes_arr, u.MatchCriteriaEnable.NONE)
        action_qp_attr = Mlx5FlowActionAttr(action_type=mlx5dv_flow_action_type.MLX5DV_FLOW_ACTION_DEST_IBV_QP,
                                            qp=self.server.qp)
        self.server.flow = Mlx5Flow(rx_matcher, empty_value_param, [action_qp_attr], 1)

        # Send traffic and validate packet
        packet = u.gen_packet(self.client.msg_size)
        u.raw_traffic(self.client, self.server, self.iters,
                      expected_packet=outer + packet,
                      skip_idxs=ipv4_id_idx + ipv4_chksum_idx + udp_chksum_idx)