| # SPDX-License-Identifier: (GPL-2.0 OR Linux-OpenIB) |
| # Copyright (c) 2021 Nvidia, Inc. All rights reserved. See COPYING file |
| |
| from enum import Enum |
| import unittest |
| |
| from pyverbs.providers.mlx5.mlx5dv import Mlx5Context, Mlx5DVContextAttr |
| from pyverbs.pyverbs_error import PyverbsRDMAError, PyverbsUserError |
| from tests.mlx5_base import Mlx5RDMATestCase, Mlx5RcResources |
| import pyverbs.providers.mlx5.mlx5_enums as dve |
| from pyverbs.pd import PD |
| from pyverbs.mr import MR |
| import pyverbs.enums as e |
| import tests.utils as u |
| |
| |
| class BadFlowType(Enum): |
| DIFFERENT_PD = 1 |
| MR_ILLEGAL_ACCESS = 2 |
| |
| |
| class Mlx5DmaResources(Mlx5RcResources): |
| def create_send_ops_flags(self): |
| self.dv_send_ops_flags = dve.MLX5DV_QP_EX_WITH_MEMCPY |
| self.send_ops_flags = e.IBV_QP_EX_WITH_SEND |
| |
| |
| class DmaGgaMemcpy(Mlx5RDMATestCase): |
| def create_resources(self, bad_flow_type=0, **resource_arg): |
| """ |
| Creates DmaGga test resources that include a "server" resource that |
| can be used to send the MEMCPY WR, and a destination MR to copy data to. |
| The destination MR can be created on a different PD or with insufficient |
| access permissions, according to the bad_flow_type. |
| :param bad_flow_type: (Optional) An enum of BadFlowType that indicates |
| the bad flow type (default: 0 - good flow) |
| :param resource_arg: Dict of args that specify the resource specific |
| attributes. |
| :return: None |
| """ |
| self.server = Mlx5DmaResources(**self.dev_info, **resource_arg) |
| self.dest_pd = self.server.pd |
| dest_mr_access = e.IBV_ACCESS_LOCAL_WRITE |
| if bad_flow_type == BadFlowType.DIFFERENT_PD: |
| self.dest_pd = PD(self.server.ctx) |
| elif bad_flow_type == BadFlowType.MR_ILLEGAL_ACCESS: |
| dest_mr_access = e.IBV_ACCESS_REMOTE_READ |
| self.dest_mr = MR(self.dest_pd, self.server.msg_size, dest_mr_access) |
| # No need to connect the QPs |
| self.server.pre_run([0], [0]) |
| |
| def dma_memcpy(self, msg_size=1024, bad_flow=False): |
| """ |
| Creates resources and posts a memcpy WR. |
| After posting the WR, the WC opcode and the data are verified. |
| :param msg_size: Size of the data to be copied (in Bytes) |
| :param bad_flow: If True, do not fill data in the MRs (default: False) |
| :return: None |
| """ |
| self.create_resources(msg_size=msg_size) |
| if not bad_flow: |
| self.dest_mr.write('0' * msg_size, msg_size) |
| self.server.mr.write('s' * msg_size, msg_size) |
| self.server.qp.wr_start() |
| self.server.qp.wr_flags = e.IBV_SEND_SIGNALED |
| self.server.qp.wr_memcpy(self.dest_mr.lkey, self.dest_mr.buf, self.server.mr.lkey, |
| self.server.mr.buf, msg_size) |
| self.server.qp.wr_complete() |
| u.poll_cq_ex(self.server.cq) |
| wc_opcode = self.server.cq.read_opcode() |
| self.assertEqual(wc_opcode, dve.MLX5DV_WC_MEMCPY, |
| 'WC opcode validation failed') |
| self.assertEqual(self.dest_mr.read(msg_size, 0), |
| self.server.mr.read(msg_size, 0)) |
| |
| def dma_memcpy_bad_protection_flow(self, bad_flow_type): |
| """ |
| Creates resources with bad protection and posts a memcpy WR. |
| The bad protection is either a destination MR created on a different PD |
| or a destination MR created with insufficient access permissions. |
| :param bad_flow_type: An enum of BadFlowType that indicates the bad flow type |
| :return: None |
| """ |
| self.create_resources(bad_flow_type) |
| self.server.qp.wr_start() |
| self.server.qp.wr_flags = e.IBV_SEND_SIGNALED |
| self.server.qp.wr_memcpy(self.dest_mr.lkey, self.dest_mr.buf, |
| self.server.mr.lkey, |
| self.server.mr.buf, self.server.msg_size) |
| self.server.qp.wr_complete() |
| with self.assertRaises(PyverbsRDMAError): |
| u.poll_cq_ex(self.server.cq) |
| self.assertEqual(self.server.cq.status, e.IBV_WC_LOC_PROT_ERR, |
| 'Expected CQE with Local Protection Error') |
| |
| def test_dma_memcpy_data(self): |
| self.dma_memcpy() |
| |
| def test_dma_memcpy_different_pd_bad_flow(self): |
| self.dma_memcpy_bad_protection_flow(BadFlowType.DIFFERENT_PD) |
| |
| def test_dma_memcpy_protection_bad_flow(self): |
| self.dma_memcpy_bad_protection_flow(BadFlowType.MR_ILLEGAL_ACCESS) |
| |
| def test_dma_memcpy_large_data_bad_flow(self): |
| """ |
| Bad flow test, testing DMA memcpy with data larger than the maximum |
| allowed size, according to the HCA capabilities. |
| :return: None |
| """ |
| try: |
| ctx = Mlx5Context(Mlx5DVContextAttr(), 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') |
| max_size = ctx.query_mlx5_device( |
| dve.MLX5DV_CONTEXT_MASK_WR_MEMCPY_LENGTH).max_wr_memcpy_length |
| max_size = max_size if max_size else 1024 |
| |
| with self.assertRaises(PyverbsRDMAError): |
| self.dma_memcpy(max_size + 1, bad_flow=True) |