diff --git a/kernel-headers/rdma/ionic-abi.h b/kernel-headers/rdma/ionic-abi.h index 7b589d3e9..ba262ef95 100644 --- a/kernel-headers/rdma/ionic-abi.h +++ b/kernel-headers/rdma/ionic-abi.h @@ -48,6 +48,7 @@ struct ionic_ctx_resp { __u8 expdb_qtypes; __u8 rsvd2[3]; + __aligned_u64 phc_offset; }; struct ionic_qdesc { @@ -112,4 +113,15 @@ struct ionic_srq_resp { __aligned_u64 rq_cmb_offset; }; +struct ionic_phc_state { + __u32 seq; + __u32 rsvd; + __aligned_u64 mask; + __aligned_u64 tick; + __aligned_u64 nsec; + __aligned_u64 frac; + __u32 mult; + __u32 shift; +}; + #endif /* IONIC_ABI_H */ diff --git a/providers/ionic/ionic.c b/providers/ionic/ionic.c index a91fa19e4..6451aab00 100644 --- a/providers/ionic/ionic.c +++ b/providers/ionic/ionic.c @@ -5,6 +5,7 @@ #include #include +#include #include "ionic.h" @@ -86,6 +87,16 @@ static struct verbs_context *ionic_alloc_context(struct ibv_device *ibdev, } ctx->dbpage = ctx->dbpage_page + (resp.dbell_offset & mask); + if (resp.phc_offset) { + ctx->phc_state = mmap(NULL, IONIC_PAGE_SIZE, PROT_READ, MAP_SHARED, + cmd_fd, resp.phc_offset); + if (ctx->phc_state == MAP_FAILED) { + ctx->phc_state = NULL; + rc = errno; + goto err_phc; + } + } + pthread_mutex_init(&ctx->mut, NULL); ionic_tbl_init(&ctx->qp_tbl); @@ -98,6 +109,8 @@ static struct verbs_context *ionic_alloc_context(struct ibv_device *ibdev, verbs_debug(&ctx->vctx, "Attached to ctx %p", ctx); return &ctx->vctx; +err_phc: + ionic_unmap(ctx->dbpage_page, 1u << ctx->pg_shift); err_cmd: verbs_uninit_context(&ctx->vctx); err_ctx: diff --git a/providers/ionic/ionic.h b/providers/ionic/ionic.h index d618645df..9f0ad7367 100644 --- a/providers/ionic/ionic.h +++ b/providers/ionic/ionic.h @@ -54,7 +54,9 @@ enum { IBV_WC_EX_WITH_SRC_QP | IBV_WC_EX_WITH_SLID | IBV_WC_EX_WITH_SL | - IBV_WC_EX_WITH_DLID_PATH_BITS + IBV_WC_EX_WITH_DLID_PATH_BITS | + IBV_WC_EX_WITH_COMPLETION_TIMESTAMP | + IBV_WC_EX_WITH_COMPLETION_TIMESTAMP_WALLCLOCK }; struct ionic_ctx { @@ -79,6 +81,7 @@ struct ionic_ctx { void *dbpage_page; uint64_t *dbpage; + struct ionic_phc_state *phc_state; pthread_mutex_t mut; struct ionic_tbl_root qp_tbl; @@ -112,6 +115,7 @@ struct ionic_cq { struct list_head poll_sq; struct list_head poll_rq; bool flush; + bool do_timestamp; struct list_head flush_sq; struct list_head flush_rq; struct ionic_queue q; @@ -123,6 +127,16 @@ struct ionic_cq { int reserve_pending; uint16_t arm_any_prod; uint16_t arm_sol_prod; + uint64_t phc_tick; +}; + +struct ionic_phc { + uint64_t mask; + uint64_t tick; + uint64_t nsec; + uint64_t frac; + uint32_t mult; + uint32_t shift; }; struct ionic_vcq { @@ -130,11 +144,16 @@ struct ionic_vcq { struct ionic_cq cq[2]; uint8_t udma_mask; uint8_t poll_idx; + bool phc_update; + bool cur_wc_pending; struct ibv_wc cur_wc; /* for use with start_poll/next_poll */ + uint64_t cur_wc_timestamp; + struct ionic_phc phc; }; struct ionic_sq_meta { uint64_t wrid; + uint64_t cqe_timestamp; uint32_t len; uint16_t seq; uint8_t ibop; diff --git a/providers/ionic/ionic_fw_types.h b/providers/ionic/ionic_fw_types.h index f715d8583..b57f7f9a2 100644 --- a/providers/ionic/ionic_fw_types.h +++ b/providers/ionic/ionic_fw_types.h @@ -74,7 +74,7 @@ union ionic_v1_pld { struct ionic_v1_cqe { union { struct { - __u64 wqe_id; + __le64 wqe_idx_timestamp; __be32 src_qpn_op; __u8 src_mac[6]; __be16 vlan_tag; @@ -84,13 +84,19 @@ struct ionic_v1_cqe { __u8 rsvd[4]; __be32 msg_msn; __u8 rsvd2[8]; - __u64 npg_wqe_id; + __le64 npg_wqe_idx_timestamp; } send; }; __be32 status_length; __be32 qid_type_flags; }; +/* bits for cqe wqe_idx and timestamp */ +enum ionic_v1_cqe_wqe_idx_timestamp_bits { + IONIC_V1_CQE_WQE_IDX_MASK = 0xffff, + IONIC_V1_CQE_TIMESTAMP_SHIFT = 16, +}; + /* bits for cqe recv */ enum ionic_v1_cqe_src_qpn_bits { IONIC_V1_CQE_RECV_QPN_MASK = 0xffffff, @@ -125,7 +131,7 @@ enum ionic_v1_cqe_qtf_bits { /* v1 base wqe header */ struct ionic_v1_base_hdr { - __u64 wqe_id; + __le64 wqe_idx; __u8 op; __u8 num_sge_key; __be16 flags; diff --git a/providers/ionic/ionic_verbs.c b/providers/ionic/ionic_verbs.c index 589024c71..7a5fbc209 100644 --- a/providers/ionic/ionic_verbs.c +++ b/providers/ionic/ionic_verbs.c @@ -164,6 +164,38 @@ static inline void ionic_mmio_memcpy_x64(void *dst, const void *src, size_t byte #define IONIC_OP(version, opname) \ ((version) < 2 ? IONIC_V1_OP_##opname : IONIC_V2_OP_##opname) +static int ionic_phc_update(struct ionic_phc *phc, struct ionic_phc_state *state) +{ + uint32_t seq, *state_seq; + int retry; + + state_seq = &state->seq; + + for (retry = 10; true; --retry) { + if (!retry) + return EBUSY; + + seq = atomic_load((_Atomic(uint32_t) *)state_seq); + + /* odd seq means state is updating */ + if (seq & 1) + continue; + + phc->mask = state->mask; + phc->tick = state->tick; + phc->nsec = state->nsec; + phc->frac = state->frac; + phc->mult = state->mult; + phc->shift = state->shift; + + /* same seq means state did not update */ + if (seq == atomic_load((_Atomic(uint32_t) *)state_seq)) + break; + } + + return 0; +} + static int ionic_query_device_ex(struct ibv_context *ibctx, const struct ibv_query_device_ex_input *input, struct ibv_device_attr_ex *ex, @@ -171,6 +203,7 @@ static int ionic_query_device_ex(struct ibv_context *ibctx, { struct ibv_device_attr *dev_attr = &ex->orig_attr; struct ib_uverbs_ex_query_device_resp resp = {}; + struct ionic_ctx *ctx = to_ionic_ctx(ibctx); size_t resp_size = sizeof(resp); int rc; @@ -183,6 +216,24 @@ static int ionic_query_device_ex(struct ibv_context *ibctx, if (rc < 0) dev_attr->fw_ver[0] = 0; + if (ctx->phc_state) { + struct ionic_phc phc = {}; + + rc = ionic_phc_update(&phc, ctx->phc_state); + if (!rc) { + const uint64_t ns_per_ms = 1000000; + const uint32_t shift = phc.shift; + const uint32_t mult = phc.mult; + + if (ex_size > offsetof(typeof(*ex), completion_timestamp_mask)) + ex->completion_timestamp_mask = phc.mask; + + if (ex_size > offsetof(typeof(*ex), hca_core_clock)) + ex->hca_core_clock = + ((ns_per_ms << shift) + (mult >> 1)) / mult; + } + } + return 0; } @@ -436,6 +487,25 @@ static void ionic_vcq_cq_init2(struct ionic_cq *cq, uint32_t resp_cqid) ionic_queue_dbell_init(&cq->q, cq->cqid); } +static uint64_t ionic_phc_ts_to_ns(struct ionic_phc *phc, uint64_t ts) +{ + uint64_t dt, dt_sign, ns; + + dt = (ts - phc->tick) & phc->mask; + dt_sign = phc->mask ^ (phc->mask >> 1); + + ns = phc->nsec; + + if (dt & dt_sign) { + dt = -dt & phc->mask; + ns -= ((dt * phc->mult) - phc->frac) >> phc->shift; + } else { + ns += ((dt * phc->mult) + phc->frac) >> phc->shift; + } + + return ns; +} + /* * NOTE: ionic_start_poll, ionic_next_poll and ionic_end_poll provide a * minimal implementations of the ibv_cq_ex polling mechanism, sufficient to @@ -447,14 +517,34 @@ static int ionic_start_poll(struct ibv_cq_ex *ibcq_ex, struct ibv_poll_cq_attr * { struct ibv_cq *ibcq = ibv_cq_ex_to_cq(ibcq_ex); struct ionic_vcq *vcq = to_ionic_vcq(ibcq); + int rc; - int rc = ionic_poll_cq(ibcq, 1, &vcq->cur_wc); + if (!vcq->cur_wc_pending) { + rc = ionic_poll_cq(ibcq, 1, &vcq->cur_wc); - if (rc != 1) /* no completions ready or poll failed */ - return (rc == 0) ? ENOENT : rc; + if (rc != 1) /* no completions ready or poll failed */ + return (rc == 0) ? ENOENT : rc; + + ibcq_ex->wr_id = vcq->cur_wc.wr_id; + ibcq_ex->status = vcq->cur_wc.status; + + /* if there is an error after polling the cur_wc, + * don't replace it in the next call to start. + */ + vcq->cur_wc_pending = true; + } + + if (vcq->phc_update) { + struct ionic_ctx *ctx = to_ionic_ctx(ibcq->context); + + rc = ionic_phc_update(&vcq->phc, ctx->phc_state); + if (rc) + return rc; + } + + /* success, so the next call to start should replace cur_wc */ + vcq->cur_wc_pending = false; - ibcq_ex->wr_id = vcq->cur_wc.wr_id; - ibcq_ex->status = vcq->cur_wc.status; return 0; } @@ -548,6 +638,22 @@ static uint8_t ionic_wc_read_dlid_path_bits(struct ibv_cq_ex *ibcq_ex) return vcq->cur_wc.dlid_path_bits; } +static uint64_t ionic_wc_read_completion_ts(struct ibv_cq_ex *ibcq_ex) +{ + struct ibv_cq *ibcq = ibv_cq_ex_to_cq(ibcq_ex); + struct ionic_vcq *vcq = to_ionic_vcq(ibcq); + + return vcq->cur_wc_timestamp; +} + +static uint64_t ionic_wc_read_completion_wallclock_ns(struct ibv_cq_ex *ibcq_ex) +{ + struct ibv_cq *ibcq = ibv_cq_ex_to_cq(ibcq_ex); + struct ionic_vcq *vcq = to_ionic_vcq(ibcq); + + return ionic_phc_ts_to_ns(&vcq->phc, vcq->cur_wc_timestamp); +} + static struct ibv_cq_ex *ionic_create_cq_ex(struct ibv_context *ibctx, struct ibv_cq_init_attr_ex *ex) { @@ -633,6 +739,23 @@ static struct ibv_cq_ex *ionic_create_cq_ex(struct ibv_context *ibctx, vcq->vcq.cq_ex.read_slid = ionic_wc_read_slid; if (ex->wc_flags & IBV_WC_EX_WITH_DLID_PATH_BITS) vcq->vcq.cq_ex.read_dlid_path_bits = ionic_wc_read_dlid_path_bits; + if (ex->wc_flags & IBV_WC_EX_WITH_COMPLETION_TIMESTAMP) { + if (!ctx->phc_state) { + rc = EOPNOTSUPP; + goto err_udma; + } + vcq->vcq.cq_ex.read_completion_ts = ionic_wc_read_completion_ts; + } + if (ex->wc_flags & IBV_WC_EX_WITH_COMPLETION_TIMESTAMP_WALLCLOCK) { + if (!ctx->phc_state) { + rc = EOPNOTSUPP; + goto err_udma; + } + + vcq->phc_update = true; + vcq->vcq.cq_ex.read_completion_wallclock_ns = + ionic_wc_read_completion_wallclock_ns; + } return &vcq->vcq.cq_ex; @@ -703,6 +826,7 @@ static int ionic_flush_recv(struct ionic_qp *qp, struct ibv_wc *wc) struct ionic_rq_meta *meta; struct ionic_v1_wqe *wqe; struct ionic_ctx *ctx; + uint64_t wqe_idx; if (!qp->rq.flush) return 0; @@ -711,20 +835,19 @@ static int ionic_flush_recv(struct ionic_qp *qp, struct ibv_wc *wc) return 0; wqe = ionic_queue_at_cons(&qp->rq.queue); + wqe_idx = le64toh(wqe->base.wqe_idx); ctx = to_ionic_ctx(qp->vqp.qp.context); - /* wqe_id must be a valid queue index */ - if (unlikely(wqe->base.wqe_id >> qp->rq.queue.depth_log2)) { - verbs_err(&ctx->vctx, "invalid id %#lx", - (unsigned long)wqe->base.wqe_id); + /* wqe_idx must be a valid queue index */ + if (unlikely(wqe_idx >> qp->rq.queue.depth_log2)) { + verbs_err(&ctx->vctx, "invalid id %#lx", (unsigned long)wqe_idx); return -EIO; } - /* wqe_id must indicate a request that is outstanding */ - meta = &qp->rq.meta[wqe->base.wqe_id]; + /* wqe_idx must indicate a request that is outstanding */ + meta = &qp->rq.meta[wqe_idx]; if (unlikely(meta->next != IONIC_META_POSTED)) { - verbs_err(&ctx->vctx, "wqe not posted %#lx", - (unsigned long)wqe->base.wqe_id); + verbs_err(&ctx->vctx, "wqe not posted %#lx", (unsigned long)wqe_idx); return -EIO; } @@ -803,8 +926,9 @@ static int ionic_poll_recv(struct ionic_ctx *ctx, struct ionic_cq *cq, { struct ionic_qp *qp = NULL; struct ionic_rq_meta *meta; + uint64_t wqe_idx_timestamp; + uint16_t vlan_tag, wqe_idx; uint32_t src_qpn, st_len; - uint16_t vlan_tag; uint8_t op; if (cqe_qp->rq.flush) @@ -814,7 +938,7 @@ static int ionic_poll_recv(struct ionic_ctx *ctx, struct ionic_cq *cq, st_len = be32toh(cqe->status_length); - /* ignore wqe_id in case of flush error */ + /* ignore wqe_idx in case of flush error */ if (ionic_v1_cqe_error(cqe) && st_len == IONIC_STS_WQE_FLUSHED_ERR) { cqe_qp->rq.flush = true; cq->flush = true; @@ -831,19 +955,21 @@ static int ionic_poll_recv(struct ionic_ctx *ctx, struct ionic_cq *cq, return -EIO; } - /* wqe_id must be a valid queue index */ - if (unlikely(cqe->recv.wqe_id >> qp->rq.queue.depth_log2)) { - verbs_err(&ctx->vctx, "invalid id %#lx", - (unsigned long)cqe->recv.wqe_id); + wqe_idx_timestamp = le64toh(cqe->recv.wqe_idx_timestamp); + wqe_idx = wqe_idx_timestamp & IONIC_V1_CQE_WQE_IDX_MASK; + + /* wqe_idx must be a valid queue index */ + if (unlikely(wqe_idx >> qp->rq.queue.depth_log2)) { + verbs_err(&ctx->vctx, "invalid id %#lx", (unsigned long)wqe_idx); return -EIO; } - /* wqe_id must indicate a request that is outstanding */ - meta = &qp->rq.meta[qp->rq.meta_idx[cqe->recv.wqe_id]]; + /* wqe_idx must indicate a request that is outstanding */ + meta = &qp->rq.meta[qp->rq.meta_idx[wqe_idx]]; if (unlikely(meta->next != IONIC_META_POSTED)) { verbs_err(&ctx->vctx, "wqe is not posted for idx %lu meta_idx %u qpid %u rq.prod %u rq.cons %u cqid %u", - (unsigned long)cqe->recv.wqe_id, - qp->rq.meta_idx[cqe->recv.wqe_id], + (unsigned long)wqe_idx, + qp->rq.meta_idx[wqe_idx], qp->qpid, qp->rq.queue.prod, qp->rq.queue.cons, cq->cqid); return -EIO; @@ -851,6 +977,7 @@ static int ionic_poll_recv(struct ionic_ctx *ctx, struct ionic_cq *cq, meta->next = qp->rq.meta_head; qp->rq.meta_head = meta; + cq->vcq->cur_wc_timestamp = wqe_idx_timestamp >> IONIC_V1_CQE_TIMESTAMP_SHIFT; memset(wc, 0, sizeof(*wc)); @@ -977,6 +1104,8 @@ static int ionic_poll_send(struct ionic_ctx *ctx, /* produce wc only if signaled or error status */ } while (!meta->signal && meta->ibsts == IBV_WC_SUCCESS); + cq->vcq->cur_wc_timestamp = meta->cqe_timestamp; + memset(wc, 0, sizeof(*wc)); wc->status = meta->ibsts; @@ -1040,6 +1169,7 @@ static int ionic_comp_msn(struct ionic_ctx *ctx, { struct ionic_sq_meta *meta; uint16_t cqe_seq, cqe_idx; + uint64_t timestamp; int rc; if (qp->sq.flush) @@ -1061,6 +1191,16 @@ static int ionic_comp_msn(struct ionic_ctx *ctx, return rc; } + timestamp = le64toh(cqe->send.npg_wqe_idx_timestamp) >> IONIC_V1_CQE_TIMESTAMP_SHIFT; + + for (cqe_idx = qp->sq.msn_cons; + cqe_idx != cqe_seq; + cqe_idx = ionic_queue_next(&qp->sq.queue, cqe_idx)) { + meta = &qp->sq.meta[cqe_idx]; + if (meta->remote) + meta->cqe_timestamp = timestamp; + } + qp->sq.msn_cons = cqe_seq; if (ionic_v1_cqe_error(cqe)) { @@ -1086,7 +1226,8 @@ static int ionic_comp_npg(struct ionic_ctx *ctx, struct ionic_v1_cqe *cqe) { struct ionic_sq_meta *meta; - uint16_t cqe_idx; + uint64_t wqe_idx_timestamp; + uint16_t wqe_idx; uint32_t st_len; if (qp->sq.flush) @@ -1107,9 +1248,11 @@ static int ionic_comp_npg(struct ionic_ctx *ctx, return 0; } - cqe_idx = cqe->send.npg_wqe_id & qp->sq.queue.mask; - meta = &qp->sq.meta[cqe_idx]; + wqe_idx_timestamp = le64toh(cqe->send.npg_wqe_idx_timestamp); + wqe_idx = wqe_idx_timestamp & qp->sq.queue.mask; + meta = &qp->sq.meta[wqe_idx]; meta->local_comp = true; + meta->cqe_timestamp = wqe_idx_timestamp >> IONIC_V1_CQE_TIMESTAMP_SHIFT; if (ionic_v1_cqe_error(cqe)) { struct ionic_cq *cq = @@ -2100,7 +2243,7 @@ static void ionic_v1_prep_base(struct ionic_qp *qp, meta->signal = false; meta->local_comp = false; - wqe->base.wqe_id = qp->sq.queue.prod; + wqe->base.wqe_idx = htole64(qp->sq.queue.prod); if (qp->sq.color) wqe->base.flags |= htobe16(IONIC_V1_FLAG_COLOR); @@ -2708,7 +2851,7 @@ static int ionic_v1_prep_recv(struct ionic_qp *qp, meta->wrid = wr->wr_id; - wqe->base.wqe_id = qp->rq.queue.prod; + wqe->base.wqe_idx = htole64(qp->rq.queue.prod); wqe->base.num_sge_key = wr->num_sge; qp->rq.meta_idx[qp->rq.queue.prod] = meta - qp->rq.meta; @@ -2987,6 +3130,9 @@ static void ionic_free_context(struct ibv_context *ibctx) pthread_mutex_destroy(&ctx->mut); + if (ctx->phc_state) + munmap(ctx->phc_state, IONIC_PAGE_SIZE); + ionic_unmap(ctx->dbpage_page, 1u << ctx->pg_shift); verbs_uninit_context(&ctx->vctx); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3ad9bfc5b..d4eb49e3a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -54,6 +54,7 @@ rdma_python_test(tests test_odp.py test_pd.py test_parent_domain.py + test_phc_timestamp.py test_qp.py test_qpex.py test_rdmacm.py diff --git a/tests/test_phc_timestamp.py b/tests/test_phc_timestamp.py new file mode 100644 index 000000000..5c4c38c7d --- /dev/null +++ b/tests/test_phc_timestamp.py @@ -0,0 +1,173 @@ +# SPDX-License-Identifier: (GPL-2.0 OR Linux-OpenIB) +# Copyright 2026 Advanced Micro Devices, Inc. All rights reserved. + +import unittest +import datetime +import time +import os +import errno + +from pyverbs.libibverbs_enums import IBV_WC_EX_WITH_COMPLETION_TIMESTAMP as FREE_RUNNING, \ + IBV_WC_EX_WITH_COMPLETION_TIMESTAMP_WALLCLOCK as REAL_TIME +from tests.base import RCResources, RDMATestCase, PyverbsAPITestCase +from pyverbs.pyverbs_error import PyverbsRDMAError +from pyverbs.cq import CqInitAttrEx, CQEX +from tests.test_flow import FlowRes +from pyverbs.qp import QPInitAttr +from pyverbs.cq import PollCqAttr +import pyverbs.libibverbs_enums as e +import tests.utils as u + +GIGA = 1000000000 + +def timestamp_res_cls(base_class): + """ + This is a factory function which creates a class that inherits base_class of + any BaseResources type. + :param base_class: The base resources class to inherit from. + :return: TimeStampRes class. + """ + class TimeStampRes(base_class): + def __init__(self, dev_name, ib_port, gid_index, qp_type, send_ts=None, + recv_ts=None): + self.qp_type = qp_type + self.send_ts = send_ts + self.recv_ts = recv_ts + self.timestamp = None + self.scq = None + self.rcq = None + self.phc_file = None + self.open_phc_dev(dev_name) + super().__init__(dev_name=dev_name, ib_port=ib_port, gid_index=gid_index) + + def __del__(self): + if self.phc_file is not None: + self.phc_file.close() + + def open_phc_dev(self, dev_name): + try: + phc_name = os.listdir(f'/sys/class/infiniband/{dev_name}/device/ptp')[0] + self.phc_file = open(f'/dev/{phc_name}', 'rb') + self.phc_clkid = (~self.phc_file.fileno() << 3) | 3 + except: + raise unittest.SkipTest('No PHC or failed to open') + + def read_phc(self): + return time.clock_gettime(self.phc_clkid) + + def create_cq(self): + self.scq = self._create_ex_cq(self.send_ts) + self.rcq = self._create_ex_cq(self.recv_ts) + + def _create_ex_cq(self, timestamp=None): + """ + Create an Extended CQ. + :param timestamp: If set, the timestamp type to use. + """ + wc_flags = e.IBV_WC_STANDARD_FLAGS + if timestamp: + wc_flags |= timestamp + cia = CqInitAttrEx(cqe=self.num_msgs, wc_flags=wc_flags) + try: + cq = CQEX(self.ctx, cia) + except PyverbsRDMAError as ex: + if ex.error_code == errno.EOPNOTSUPP: + raise unittest.SkipTest('Create Extended CQ is not supported') + raise ex + return cq + + def create_qp_init_attr(self): + return QPInitAttr(qp_type=self.qp_type, scq=self.scq, + rcq=self.rcq, srq=self.srq, cap=self.create_qp_cap()) + + return TimeStampRes + + +class TimeStampTest(RDMATestCase): + """ + Test various types of timestamping formats. + """ + def setUp(self): + super().setUp() + self.send_ts = None + self.recv_ts = None + self.qp_type = None + + @property + def resource_arg(self): + return {'send_ts': self.send_ts, 'recv_ts': self.recv_ts, + 'qp_type': self.qp_type} + + def test_timestamp_free_running_rc_traffic(self): + """ + Test free running timestamp on RC traffic. + """ + self.qp_type = e.IBV_QPT_RC + self.send_ts = self.recv_ts = FREE_RUNNING + self.create_players(timestamp_res_cls(RCResources), **self.resource_arg) + self.ts_traffic() + + def test_timestamp_real_time_rc_traffic(self): + """ + Test real time timestamp on RC traffic. + """ + self.qp_type = e.IBV_QPT_RC + self.send_ts = self.recv_ts = REAL_TIME + self.create_players(timestamp_res_cls(RCResources), **self.resource_arg) + self.ts_traffic() + self.verify_ts(self.client.timestamp / GIGA, self.client.read_phc()) + self.verify_ts(self.server.timestamp / GIGA, self.server.read_phc()) + + def verify_ts(self, timestamp, phc_now): + """ + Verify that the timestamp is in the past one second + """ + if self.config['verbosity']: + print(f'timestamp {timestamp} current {phc_now} difference {phc_now - timestamp}') + if timestamp > phc_now: + raise PyverbsRDMAError(f'Completion timestamp is in the future: {timestamp} > {phc_now}') + if timestamp < phc_now - 1: + raise PyverbsRDMAError(f'Completion timestamp is too far in the past: {timestamp} < {phc_now - 1}') + + @staticmethod + def poll_cq_ex_ts(cqex, ts_type=None): + """ + Poll completion from the extended CQ. + :param cqex: CQEX to poll from + :param ts_type: If set, read the CQE timestamp in this format + :return: The CQE timestamp if it requested. + """ + polling_timeout = 10 + start = datetime.datetime.now() + ts = 0 + + poll_attr = PollCqAttr() + ret = cqex.start_poll(poll_attr) + while ret == 2 and (datetime.datetime.now() - start).seconds < polling_timeout: + ret = cqex.start_poll(poll_attr) + if ret == 2: + raise PyverbsRDMAError('Failed to poll CQEX - Got timeout') + if ret != 0: + raise PyverbsRDMAError('Failed to poll CQEX') + if cqex.status != e.IBV_WC_SUCCESS: + raise PyverbsRDMAError('Completion status is {cqex.status}') + if ts_type == FREE_RUNNING: + ts = cqex.read_timestamp() + if ts_type == REAL_TIME: + ts = cqex.read_completion_wallclock_ns() + cqex.end_poll() + return ts + + def ts_traffic(self): + """ + Run RDMA traffic and read the completions timestamps. + """ + s_recv_wr = u.get_recv_wr(self.server) + u.post_recv(self.server, s_recv_wr) + if self.qp_type == e.IBV_QPT_RAW_PACKET: + c_send_wr, _, _ = u.get_send_elements_raw_qp(self.client) + else: + c_send_wr, _ = u.get_send_elements(self.client, False) + u.send(self.client, c_send_wr, e.IBV_WR_SEND, False, 0) + self.client.timestamp = self.poll_cq_ex_ts(self.client.scq, ts_type=self.send_ts) + self.server.timestamp = self.poll_cq_ex_ts(self.server.rcq, ts_type=self.recv_ts)