Skip to content

Commit e1225e0

Browse files
ahubbe-amdabhijitG-xlnx
authored andcommitted
tests: test PHC completion time stamp
Add a test for completion time stamp. Validate the wall clock time by opening the PTP hardware clock device node to read the current time and compare it to the wall clock completion time stamps. Expect that completion time stamps are within the past one second of the current PHC time at the end of the test. Signed-off-by: Allen Hubbe <allen.hubbe@amd.com> Signed-off-by: Abhijit Gangurde <abhijit.gangurde@amd.com>
1 parent 6fafa5b commit e1225e0

2 files changed

Lines changed: 174 additions & 0 deletions

File tree

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ rdma_python_test(tests
5454
test_odp.py
5555
test_pd.py
5656
test_parent_domain.py
57+
test_phc_timestamp.py
5758
test_qp.py
5859
test_qpex.py
5960
test_rdmacm.py

tests/test_phc_timestamp.py

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
# SPDX-License-Identifier: (GPL-2.0 OR Linux-OpenIB)
2+
# Copyright 2026 Advanced Micro Devices, Inc. All rights reserved.
3+
4+
import unittest
5+
import datetime
6+
import time
7+
import os
8+
import errno
9+
10+
from pyverbs.libibverbs_enums import IBV_WC_EX_WITH_COMPLETION_TIMESTAMP as FREE_RUNNING, \
11+
IBV_WC_EX_WITH_COMPLETION_TIMESTAMP_WALLCLOCK as REAL_TIME
12+
from tests.base import RCResources, RDMATestCase, PyverbsAPITestCase
13+
from pyverbs.pyverbs_error import PyverbsRDMAError
14+
from pyverbs.cq import CqInitAttrEx, CQEX
15+
from tests.test_flow import FlowRes
16+
from pyverbs.qp import QPInitAttr
17+
from pyverbs.cq import PollCqAttr
18+
import pyverbs.libibverbs_enums as e
19+
import tests.utils as u
20+
21+
GIGA = 1000000000
22+
23+
def timestamp_res_cls(base_class):
24+
"""
25+
This is a factory function which creates a class that inherits base_class of
26+
any BaseResources type.
27+
:param base_class: The base resources class to inherit from.
28+
:return: TimeStampRes class.
29+
"""
30+
class TimeStampRes(base_class):
31+
def __init__(self, dev_name, ib_port, gid_index, qp_type, send_ts=None,
32+
recv_ts=None):
33+
self.qp_type = qp_type
34+
self.send_ts = send_ts
35+
self.recv_ts = recv_ts
36+
self.timestamp = None
37+
self.scq = None
38+
self.rcq = None
39+
self.phc_file = None
40+
self.open_phc_dev(dev_name)
41+
super().__init__(dev_name=dev_name, ib_port=ib_port, gid_index=gid_index)
42+
43+
def __del__(self):
44+
if self.phc_file is not None:
45+
self.phc_file.close()
46+
47+
def open_phc_dev(self, dev_name):
48+
try:
49+
phc_name = os.listdir(f'/sys/class/infiniband/{dev_name}/device/ptp')[0]
50+
self.phc_file = open(f'/dev/{phc_name}', 'rb')
51+
self.phc_clkid = (~self.phc_file.fileno() << 3) | 3
52+
except:
53+
raise unittest.SkipTest('No PHC or failed to open')
54+
55+
def read_phc(self):
56+
return time.clock_gettime(self.phc_clkid)
57+
58+
def create_cq(self):
59+
self.scq = self._create_ex_cq(self.send_ts)
60+
self.rcq = self._create_ex_cq(self.recv_ts)
61+
62+
def _create_ex_cq(self, timestamp=None):
63+
"""
64+
Create an Extended CQ.
65+
:param timestamp: If set, the timestamp type to use.
66+
"""
67+
wc_flags = e.IBV_WC_STANDARD_FLAGS
68+
if timestamp:
69+
wc_flags |= timestamp
70+
cia = CqInitAttrEx(cqe=self.num_msgs, wc_flags=wc_flags)
71+
try:
72+
cq = CQEX(self.ctx, cia)
73+
except PyverbsRDMAError as ex:
74+
if ex.error_code == errno.EOPNOTSUPP:
75+
raise unittest.SkipTest('Create Extended CQ is not supported')
76+
raise ex
77+
return cq
78+
79+
def create_qp_init_attr(self):
80+
return QPInitAttr(qp_type=self.qp_type, scq=self.scq,
81+
rcq=self.rcq, srq=self.srq, cap=self.create_qp_cap())
82+
83+
return TimeStampRes
84+
85+
86+
class TimeStampTest(RDMATestCase):
87+
"""
88+
Test various types of timestamping formats.
89+
"""
90+
def setUp(self):
91+
super().setUp()
92+
self.send_ts = None
93+
self.recv_ts = None
94+
self.qp_type = None
95+
96+
@property
97+
def resource_arg(self):
98+
return {'send_ts': self.send_ts, 'recv_ts': self.recv_ts,
99+
'qp_type': self.qp_type}
100+
101+
def test_timestamp_free_running_rc_traffic(self):
102+
"""
103+
Test free running timestamp on RC traffic.
104+
"""
105+
self.qp_type = e.IBV_QPT_RC
106+
self.send_ts = self.recv_ts = FREE_RUNNING
107+
self.create_players(timestamp_res_cls(RCResources), **self.resource_arg)
108+
self.ts_traffic()
109+
110+
def test_timestamp_real_time_rc_traffic(self):
111+
"""
112+
Test real time timestamp on RC traffic.
113+
"""
114+
self.qp_type = e.IBV_QPT_RC
115+
self.send_ts = self.recv_ts = REAL_TIME
116+
self.create_players(timestamp_res_cls(RCResources), **self.resource_arg)
117+
self.ts_traffic()
118+
self.verify_ts(self.client.timestamp / GIGA, self.client.read_phc())
119+
self.verify_ts(self.server.timestamp / GIGA, self.server.read_phc())
120+
121+
def verify_ts(self, timestamp, phc_now):
122+
"""
123+
Verify that the timestamp is in the past one second
124+
"""
125+
if self.config['verbosity']:
126+
print(f'timestamp {timestamp} current {phc_now} difference {phc_now - timestamp}')
127+
if timestamp > phc_now:
128+
raise PyverbsRDMAError(f'Completion timestamp is in the future: {timestamp} > {phc_now}')
129+
if timestamp < phc_now - 1:
130+
raise PyverbsRDMAError(f'Completion timestamp is too far in the past: {timestamp} < {phc_now - 1}')
131+
132+
@staticmethod
133+
def poll_cq_ex_ts(cqex, ts_type=None):
134+
"""
135+
Poll completion from the extended CQ.
136+
:param cqex: CQEX to poll from
137+
:param ts_type: If set, read the CQE timestamp in this format
138+
:return: The CQE timestamp if it requested.
139+
"""
140+
polling_timeout = 10
141+
start = datetime.datetime.now()
142+
ts = 0
143+
144+
poll_attr = PollCqAttr()
145+
ret = cqex.start_poll(poll_attr)
146+
while ret == 2 and (datetime.datetime.now() - start).seconds < polling_timeout:
147+
ret = cqex.start_poll(poll_attr)
148+
if ret == 2:
149+
raise PyverbsRDMAError('Failed to poll CQEX - Got timeout')
150+
if ret != 0:
151+
raise PyverbsRDMAError('Failed to poll CQEX')
152+
if cqex.status != e.IBV_WC_SUCCESS:
153+
raise PyverbsRDMAError('Completion status is {cqex.status}')
154+
if ts_type == FREE_RUNNING:
155+
ts = cqex.read_timestamp()
156+
if ts_type == REAL_TIME:
157+
ts = cqex.read_completion_wallclock_ns()
158+
cqex.end_poll()
159+
return ts
160+
161+
def ts_traffic(self):
162+
"""
163+
Run RDMA traffic and read the completions timestamps.
164+
"""
165+
s_recv_wr = u.get_recv_wr(self.server)
166+
u.post_recv(self.server, s_recv_wr)
167+
if self.qp_type == e.IBV_QPT_RAW_PACKET:
168+
c_send_wr, _, _ = u.get_send_elements_raw_qp(self.client)
169+
else:
170+
c_send_wr, _ = u.get_send_elements(self.client, False)
171+
u.send(self.client, c_send_wr, e.IBV_WR_SEND, False, 0)
172+
self.client.timestamp = self.poll_cq_ex_ts(self.client.scq, ts_type=self.send_ts)
173+
self.server.timestamp = self.poll_cq_ex_ts(self.server.rcq, ts_type=self.recv_ts)

0 commit comments

Comments
 (0)