aboutsummaryrefslogtreecommitdiffstats
path: root/tests/perf/40ge2p1xl710-eth-l2xcbase-ndrpdrdisc.robot
blob: 853e03fbb6c4f7ecced307f87af85afa09f67d38 (plain)
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
# Copyright (c) 2017 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

*** Settings ***
| Resource | resources/libraries/robot/performance.robot
| Library | resources.libraries.python.InterfaceUtil
| Library | resources.libraries.python.NodePath
| ...
| Force Tags | 3_NODE_SINGLE_LINK_TOPO | PERFTEST | HW_ENV | NDRPDRDISC
| ... | NIC_Intel-XL710 | ETH | L2XCFWD | BASE
| ...
| Suite Setup | 3-node Performance Suite Setup with DUT's NIC model
| ... | L2 | Intel-XL710
| Suite Teardown | 3-node Performance Suite Teardown
| ...
| Test Setup | Performance test setup
| Test Teardown | Performance test teardown | ${min_rate}pps | ${framesize}
| ... | 3-node-xconnect
| ...
| Documentation | *RFC2544: Pkt throughput L2XC test cases*
| ...
| ... | *[Top] Network Topologies:* TG-DUT1-DUT2-TG 3-node circular topology
| ... | with single links between nodes.
| ... | *[Enc] Packet Encapsulations:* Eth-IPv4 for L2 cross connect.
| ... | *[Cfg] DUT configuration:* DUT1 and DUT2 are configured with L2 cross-
| ... | connect. DUT1 and DUT2 tested with 2p40GE NIC XL710 by Intel.
| ... | *[Ver] TG verification:* TG finds and reports throughput NDR (Non Drop
| ... | Rate) with zero packet loss tolerance or throughput PDR (Partial Drop
| ... | Rate) with non-zero packet loss tolerance (LT) expressed in percentage
| ... | of packets transmitted. NDR and PDR are discovered for different
| ... | Ethernet L2 frame sizes using either binary search or linear search
| ... | algorithms with configured starting rate and final step that determines
| ... | throughput measurement resolution. Test packets are generated by TG on
| ... | links to DUTs. TG traffic profile contains two L3 flow-groups
| ... | (flow-group per direction, 253 flows per flow-group) with all packets
| ... | containing Ethernet header, IPv4 header with IP protocol=61 and static
| ... | payload. MAC addresses are matching MAC addresses of the TG node
| ... | interfaces.
| ... | *[Ref] Applicable standard specifications:* RFC2544.

*** Variables ***
# XL710-DA2 bandwidth limit ~49Gbps/2=24.5Gbps
| ${s_24.5G} | ${24500000000}
# XL710-DA2 Mpps limit 37.5Mpps/2=18.75Mpps
| ${s_18.75Mpps} | ${18750000}

*** Test Cases ***
| tc01-64B-1t1c-eth-l2xcbase-ndrdisc
| | [Documentation]
| | ... | [Cfg] DUT runs L2XC switching config with 1 thread, 1 phy core, \
| | ... | 1 receive queue per NIC port. [Ver] Find NDR for 64 Byte frames
| | ... | using binary search start at 18.75Mpps rate, step 100kpps.
| | [Tags] | 64B | 1T1C | STHREAD | NDRDISC
| | ${framesize}= | Set Variable | 64
| | ${min_rate}= | Set Variable | ${100000}
| | ${max_rate}= | Set Variable | ${s_18.75Mpps}
| | ${binary_min}= | Set Variable | ${min_rate}
| | ${binary_max}= | Set Variable | ${max_rate}
| | ${threshold}= | Set Variable | ${min_rate}
| | Given Add '1' worker threads and rxqueues '1' in 3-node single-link topo
| | And   Add PCI devices to DUTs from 3-node single link topology
| | And   Add No Multi Seg to all DUTs
| | And   Apply startup configuration on all VPP DUTs
| | And   L2 xconnect initialized in a 3-node circular topology
| | Then Find NDR using binary search and pps | ${framesize} | ${binary_min}
| | ...                                       | ${binary_max} | 3-node-xconnect
| | ...                                       | ${min_rate} | ${max_rate}
| | ...                                       | ${threshold}

| tc03-1518B-1t1c-eth-l2xcbase-ndrdisc
| | [Documentation]
| | ... | [Cfg] DUT runs L2XC switching config with 1 thread, 1 phy core, \
| | ... | 1 receive queue per NIC port. [Ver] Find NDR for 1518 Byte frames
| | ... | using binary search start at 24.5G rate, step 10kpps.
| | [Tags] | 1518B | 1T1C | STHREAD | NDRDISC
| | ${framesize}= | Set Variable | 1518
| | ${min_rate}= | Set Variable | ${10000}
| | ${max_rate}= | Calculate pps | ${s_24.5G} | ${framesize}
| | ${binary_min}= | Set Variable | ${min_rate}
| | ${binary_max}= | Set Variable | ${max_rate}
| | ${threshold}= | Set Variable | ${min_rate}
| | Given Add '1' worker threads and rxqueues '1' in 3-node single-link topo
| | And   Add PCI devices to DUTs from 3-node single link topology
| | And   Add No Multi Seg to all DUTs
| | And   Apply startup configuration on all VPP DUTs
| | And   L2 xconnect initialized in a 3-node circular topology
| | Then Find NDR using binary search and pps | ${framesize} | ${binary_min}
| | ...                                       | ${binary_max} | 3-node-xconnect
| | ...                                       | ${min_rate} | ${max_rate}
| | ...                                       | ${threshold}

| tc07-64B-2t2c-eth-l2xcbase-ndrdisc
| | [Documentation]
| | ... | [Cfg] DUT runs L2XC switching config with 2 threads, 2 phy cores, \
| | ... | 1 receive queue per NIC port. [Ver] Find NDR for 64 Byte frames
| | ... | using binary search start at 18.75Mpps rate, step 100kpps.
| | [Tags] | 64B | 2T2C | MTHREAD | NDRDISC
| | ${framesize}= | Set Variable | 64
| | ${min_rate}= | Set Variable | ${100000}
| | ${max_rate}= | Set Variable | ${s_18.75Mpps}
| | ${binary_min}= | Set Variable | ${min_rate}
| | ${binary_max}= | Set Variable | ${max_rate}
| | ${threshold}= | Set Variable | ${min_rate}
| | Given Add '2' worker threads and rxqueues '1' in 3-node single-link topo
| | And   Add PCI devices to DUTs from 3-node single link topology
| | And   Add No Multi Seg to all DUTs
| | And   Apply startup configuration on all VPP DUTs
| | And   L2 xconnect initialized in a 3-node circular topology
| | Then Find NDR using binary search and pps | ${framesize} | ${binary_min}
| | ...                                       | ${binary_max} | 3-node-xconnect
| | ...                                       | ${min_rate} | ${max_rate}
| | ...                                       | ${threshold}

| tc09-1518B-2t2c-eth-l2xcbase-ndrdisc
| | [Documentation]
| | ... | [Cfg] DUT runs L2XC switching config with 2 threads, 2 phy cores, \
| | ... | 1 receive queue per NIC port. [Ver] Find NDR for 1518 Byte frames
| | ... | using binary search start at 24.5G rate, step 10kpps.
| | [Tags] | 1518B | 2T2C | MTHREAD | NDRDISC | SKIP_PATCH
| | ${framesize}= | Set Variable | 1518
| | ${min_rate}= | Set Variable | ${10000}
| | ${max_rate}= | Calculate pps | ${s_24.5G} | ${framesize}
| | ${binary_min}= | Set Variable | ${min_rate}
| | ${binary_max}= | Set Variable | ${max_rate}
| | ${threshold}= | Set Variable | ${min_rate}
| | Given Add '2' worker threads and rxqueues '1' in 3-node single-link topo
| | And   Add PCI devices to DUTs from 3-node single link topology
| | And   Add No Multi Seg to all DUTs
| | And   Apply startup configuration on all VPP DUTs
| | And   L2 xconnect initialized in a 3-node circular topology
| | Then Find NDR using binary search and pps | ${framesize} | ${binary_min}
| | ...                                       | ${binary_max} | 3-node-xconnect
| | ...                                       | ${min_rate} | ${max_rate}
| | ...                                       | ${threshold}

| tc13-64B-4t4c-eth-l2xcbase-ndrdisc
| | [Documentation]
| | ... | [Cfg] DUT runs L2XC switching config with 4 threads, 4 phy cores, \
| | ... | 2 receive queues per NIC port. [Ver] Find NDR for 64 Byte frames
| | ... | using binary search start at 18.75Mpps rate, step 100kpps.
| | [Tags] | 64B | 4T4C | MTHREAD | NDRDISC
| | ${framesize}= | Set Variable | 64
| | ${min_rate}= | Set Variable | ${100000}
| | ${max_rate}= | Set Variable | ${s_18.75Mpps}
| | ${binary_min}= | Set Variable | ${min_rate}
| | ${binary_max}= | Set Variable | ${max_rate}
| | ${threshold}= | Set Variable | ${min_rate}
| | Given Add '4' worker threads and rxqueues '2' in 3-node single-link topo
| | And   Add PCI devices to DUTs from 3-node single link topology
| | And   Add No Multi Seg to all DUTs
| | And   Apply startup configuration on all VPP DUTs
| | And   L2 xconnect initialized in a 3-node circular topology
| | Then Find NDR using binary search and pps | ${framesize} | ${binary_min}
| | ...                                       | ${binary_max} | 3-node-xconnect
| | ...                                       | ${min_rate} | ${max_rate}
| | ...                                       | ${threshold}

| tc15-1518B-4t4c-eth-l2xcbase-ndrdisc
| | [Documentation]
| | ... | [Cfg] DUT runs L2XC switching config with 4 threads, 4 phy cores, \
| | ... | 2 receive queues per NIC port. [Ver] Find NDR for 1518 Byte frames
| | ... | using binary search start at 24.5G rate, step 10kpps.
| | [Tags] | 1518B | 4T4C | MTHREAD | NDRDISC | SKIP_PATCH
| | ${framesize}= | Set Variable | 1518
| | ${min_rate}= | Set Variable | ${10000}
| | ${max_rate}= | Calculate pps | ${s_24.5G} | ${framesize}
| | ${binary_min}= | Set Variable | ${min_rate}
| | ${binary_max}= | Set Variable | ${max_rate}
| | ${threshold}= | Set Variable | ${min_rate}
| | Given Add '4' worker threads and rxqueues '2' in 3-node single-link topo
| | And   Add PCI devices to DUTs from 3-node single link topology
| | And   Add No Multi Seg to all DUTs
| | And   Apply startup configuration on all VPP DUTs
| | And   L2 xconnect initialized in a 3-node circular topology
| | Then Find NDR using binary search and pps | ${framesize} | ${binary_min}
| | ...                                       | ${binary_max} | 3-node-xconnect
| | ...                                       | ${min_rate} | ${max_rate}
| | ...                                       | ${threshold}

| tc19-IMIX-1t1c-eth-l2xcbase-ndrdisc
| | [Documentation]
| | ... | [Cfg] DUT runs L2XC switching config with 1 thread, 1 phy core, \
| | ... | 1 receive queue per NIC port. [Ver] Find NDR for IMIX_v4_1 frame size
| | ... | using binary search start at 24.5G rate, step 100kpps.
| | ... | IMIX_v4_1 = (28x64B;16x570B;4x1518B)
| | [Tags] | IMIX | 1T1C | STHREAD | NDRDISC
| | ${framesize}= | Set Variable | IMIX_v4_1
| | ${min_rate}= | Set Variable | ${100000}
| | ${max_rate}= | Calculate pps | ${s_24.5G} | ${353.83333}
| | ${binary_min}= | Set Variable | ${min_rate}
| | ${binary_max}= | Set Variable | ${max_rate}
| | ${threshold}= | Set Variable | ${min_rate}
| | Given Add '1' worker threads and rxqueues '1' in 3-node single-link topo
| | And   Add all PCI devices to all DUTs
| | And   Add No Multi Seg to all DUTs
| | And   Apply startup configuration on all VPP DUTs
| | And   L2 xconnect initialized in a 3-node circular topology
| | Then Find NDR using binary search and pps | ${framesize} | ${binary_min}
| | ...                                       | ${binary_max} | 3-node-xconnect
| | ...                                       | ${min_rate} | ${max_rate}
| | ...                                       | ${threshold}

| tc20-IMIX-2t2c-eth-l2xcbase-ndrdisc
| | [Documentation]
| | ... | [Cfg] DUT runs L2XC switching config with 2 thread, 2 phy core, \
| | ... | 1 receive queue per NIC port. [Ver] Find NDR for IMIX_v4_1 frame size
| | ... | using binary search start at 24.5G rate, step 100kpps.
| | ... | IMIX_v4_1 = (28x64B;16x570B;4x1518B)
| | [Tags] | IMIX | 2T2C | MTHREAD | NDRDISC | SKIP_PATCH
| | ${framesize}= | Set Variable | IMIX_v4_1
| | ${min_rate}= | Set Variable | ${100000}
| | ${max_rate}= | Calculate pps | ${s_24.5G} | ${353.83333}
| | ${binary_min}= | Set Variable | ${min_rate}
| | ${binary_max}= | Set Variable | ${max_rate}
| | ${threshold}= | Set Variable | ${min_rate}
| | Given Add '2' worker threads and rxqueues '1' in 3-node single-link topo
| | And   Add all PCI devices to all DUTs
| | And   Add No Multi Seg to all DUTs
| | And   Apply startup configuration on all VPP DUTs
| | And   L2 xconnect initialized in a 3-node circular topology
| | Then Find NDR using binary search and pps | ${framesize} | ${binary_min}
| | ...                                       | ${binary_max} | 3-node-xconnect
| | ...                                       | ${min_rate} | ${max_rate}
| | ...                                       | ${threshold}

| tc21-IMIX-4t4c-eth-l2xcbase-ndrdisc
| | [Documentation]
| | ... | [Cfg] DUT runs L2XC switching config with 4 thread, 4 phy core, \
| | ... | 2 receive queue per NIC port. [Ver] Find NDR for IMIX_v4_1 frame size
| | ... | using binary search start at 24.5G rate, step 100kpps.
| | ... | IMIX_v4_1 = (28x64B;16x570B;4x1518B)
| | [Tags] | IMIX | 4T4C | MTHREAD | NDRDISC | SKIP_PATCH
| | ${framesize}= | Set Variable | IMIX_v4_1
| | ${min_rate}= | Set Variable | ${100000}
| | ${max_rate}= | Calculate pps | ${s_24.5G} | ${353.83333}
| | ${binary_min}= | Set Variable | ${min_rate}
| | ${binary_max}= | Set Variable | ${max_rate}
| | ${threshold}= | Set Variable | ${min_rate}
| | Given Add '4' worker threads and rxqueues '2' in 3-node single-link topo
| | And   Add all PCI devices to all DUTs
| | And   Add No Multi Seg to all DUTs
| | And   Apply startup configuration on all VPP DUTs
| | And   L2 xconnect initialized in a 3-node circular topology
| | Then Find NDR using binary search and pps | ${framesize} | ${binary_min}
| | ...                                       | ${binary_max} | 3-node-xconnect
| | ...                                       | ${min_rate} | ${max_rate}
| | ...                                       | ${threshold}
u32 context = *reinterpret_cast<u32 *> ( (static_cast<u8 *> (shm_data) + vapi_get_context_offset (id))); const auto x = requests.front (); matching_req = x; if (context == x->context) { std::tie (rv, break_dispatch) = x->assign_response (id, shm_data); } else { std::tie (rv, break_dispatch) = x->assign_response (id, nullptr); } if (break_dispatch) { requests.pop_front (); } } else { if (events[id]) { std::tie (rv, break_dispatch) = events[id]->assign_response (id, shm_data); matching_req = events[id]; } else { msg_free (shm_data); } } if ((matching_req && matching_req == limit && break_dispatch) || VAPI_OK != rv) { return rv; } loop_again = !requests.empty () || (event_count > 0); } return rv; } /** * @brief convenience wrapper function */ vapi_error_e dispatch (const Common_req &limit) { return dispatch (&limit); } /** * @brief wait for response to a specific request * * @param req request to wait for response for * * @return VAPI_OK on success, other error code on error */ vapi_error_e wait_for_response (const Common_req &req) { if (RESPONSE_READY == req.get_response_state ()) { return VAPI_OK; } return dispatch (req); } private: void msg_free (void *shm_data) { #if VAPI_CPP_DEBUG_LEAKS on_shm_data_free (shm_data); #endif vapi_msg_free (vapi_ctx, shm_data); } template <template <typename XReq, typename XResp, typename... XArgs> class X, typename Req, typename Resp, typename... Args> vapi_error_e send (X<Req, Resp, Args...> *req) { if (!req) { return VAPI_EINVAL; } u32 req_context = req_context_counter.fetch_add (1, std::memory_order_relaxed); req->request.shm_data->header.context = req_context; vapi_swap_to_be<Req> (req->request.shm_data); std::lock_guard<std::recursive_mutex> lock (requests_mutex); vapi_error_e rv = vapi_send (vapi_ctx, req->request.shm_data); if (VAPI_OK == rv) { VAPI_DBG ("Push %p", req); requests.emplace_back (req); req->set_context (req_context); #if VAPI_CPP_DEBUG_LEAKS on_shm_data_free (req->request.shm_data); #endif req->request.shm_data = nullptr; /* consumed by vapi_send */ } else { vapi_swap_to_host<Req> (req->request.shm_data); } return rv; } template <template <typename XReq, typename XResp, typename... XArgs> class X, typename Req, typename Resp, typename... Args> vapi_error_e send_with_control_ping (X<Req, Resp, Args...> *req) { if (!req) { return VAPI_EINVAL; } u32 req_context = req_context_counter.fetch_add (1, std::memory_order_relaxed); req->request.shm_data->header.context = req_context; vapi_swap_to_be<Req> (req->request.shm_data); std::lock_guard<std::recursive_mutex> lock (requests_mutex); vapi_error_e rv = vapi_send_with_control_ping ( vapi_ctx, req->request.shm_data, req_context); if (VAPI_OK == rv) { VAPI_DBG ("Push %p", req); requests.emplace_back (req); req->set_context (req_context); #if VAPI_CPP_DEBUG_LEAKS on_shm_data_free (req->request.shm_data); #endif req->request.shm_data = nullptr; /* consumed by vapi_send */ } else { vapi_swap_to_host<Req> (req->request.shm_data); } return rv; } void unregister_request (Common_req *request) { std::lock_guard<std::recursive_mutex> lock (requests_mutex); std::remove (requests.begin (), requests.end (), request); } template <typename M> void register_event (Event_registration<M> *event) { const vapi_msg_id_t id = M::get_msg_id (); std::lock_guard<std::recursive_mutex> lock (events_mutex); events[id] = event; ++event_count; } template <typename M> void unregister_event (Event_registration<M> *event) { const vapi_msg_id_t id = M::get_msg_id (); std::lock_guard<std::recursive_mutex> lock (events_mutex); events[id] = nullptr; --event_count; } vapi_ctx_t vapi_ctx; std::atomic_ulong req_context_counter; std::mutex dispatch_mutex; std::recursive_mutex requests_mutex; std::recursive_mutex events_mutex; std::deque<Common_req *> requests; std::vector<Common_req *> events; int event_count; template <typename Req, typename Resp, typename... Args> friend class Request; template <typename Req, typename Resp, typename... Args> friend class Dump; template <typename M> friend class Result_set; template <typename M> friend class Event_registration; template <typename M, typename... Args> friend M *vapi_alloc (Connection &con, Args...); template <typename M> friend class Msg; #if VAPI_CPP_DEBUG_LEAKS void on_shm_data_alloc (void *shm_data) { if (shm_data) { auto pos = shm_data_set.find (shm_data); if (pos == shm_data_set.end ()) { shm_data_set.insert (shm_data); } else { printf ("Double-add shm_data @%p!\n", shm_data); } } } void on_shm_data_free (void *shm_data) { auto pos = shm_data_set.find (shm_data); if (pos == shm_data_set.end ()) { printf ("Freeing untracked shm_data @%p!\n", shm_data); } else { shm_data_set.erase (pos); } } std::unordered_set<void *> shm_data_set; #endif }; template <typename Req, typename Resp, typename... Args> class Request; template <typename Req, typename Resp, typename... Args> class Dump; template <class, class = void> struct vapi_has_payload_trait : std::false_type { }; template <class... T> using vapi_void_t = void; template <class T> struct vapi_has_payload_trait<T, vapi_void_t<decltype (&T::payload)>> : std::true_type { }; template <typename M> void vapi_msg_set_msg_id (vapi_msg_id_t id) { Msg<M>::set_msg_id (id); } /** * Class representing a message stored in shared memory */ template <typename M> class Msg { public: Msg (const Msg &) = delete; ~Msg () { VAPI_DBG ("Destroy Msg<%s>@%p, shm_data@%p", vapi_get_msg_name (get_msg_id ()), this, shm_data); if (shm_data) { con.get ().msg_free (shm_data); shm_data = nullptr; } } static vapi_msg_id_t get_msg_id () { return *msg_id_holder (); } template <typename X = M> typename std::enable_if<vapi_has_payload_trait<X>::value, decltype (X::payload) &>::type get_payload () const { return shm_data->payload; } private: Msg (Msg<M> &&msg) : con{msg.con} { VAPI_DBG ("Move construct Msg<%s> from msg@%p to msg@%p, shm_data@%p", vapi_get_msg_name (get_msg_id ()), &msg, this, msg.shm_data); shm_data = msg.shm_data; msg.shm_data = nullptr; } Msg<M> &operator= (Msg<M> &&msg) { VAPI_DBG ("Move assign Msg<%s> from msg@%p to msg@%p, shm_data@%p", vapi_get_msg_name (get_msg_id ()), &msg, this, msg.shm_data); con.get ().msg_free (shm_data); con = msg.con; shm_data = msg.shm_data; msg.shm_data = nullptr; return *this; } struct Msg_allocator : std::allocator<Msg<M>> { template <class U, class... Args> void construct (U *p, Args &&... args) { ::new ((void *)p) U (std::forward<Args> (args)...); } template <class U> struct rebind { typedef Msg_allocator other; }; }; static void set_msg_id (vapi_msg_id_t id) { assert ((VAPI_INVALID_MSG_ID == *msg_id_holder ()) || (id == *msg_id_holder ())); *msg_id_holder () = id; } static vapi_msg_id_t *msg_id_holder () { static vapi_msg_id_t my_id{VAPI_INVALID_MSG_ID}; return &my_id; } Msg (Connection &con, void *shm_data) : con{con} { if (!con.is_msg_available (get_msg_id ())) { throw Msg_not_available_exception (); } this->shm_data = static_cast<shm_data_type *> (shm_data); VAPI_DBG ("New Msg<%s>@%p shm_data@%p", vapi_get_msg_name (get_msg_id ()), this, shm_data); } void assign_response (vapi_msg_id_t resp_id, void *shm_data) { assert (nullptr == this->shm_data); if (resp_id != get_msg_id ()) { throw Unexpected_msg_id_exception (); } this->shm_data = static_cast<M *> (shm_data); vapi_swap_to_host<M> (this->shm_data); VAPI_DBG ("Assign response to Msg<%s>@%p shm_data@%p", vapi_get_msg_name (get_msg_id ()), this, shm_data); } std::reference_wrapper<Connection> con; using shm_data_type = M; shm_data_type *shm_data; friend class Connection; template <typename Req, typename Resp, typename... Args> friend class Request; template <typename Req, typename Resp, typename... Args> friend class Dump; template <typename X> friend class Event_registration; template <typename X> friend class Result_set; friend struct Msg_allocator; template <typename X> friend void vapi_msg_set_msg_id (vapi_msg_id_t id); }; /** * Class representing a simple request - with a single response message */ template <typename Req, typename Resp, typename... Args> class Request : public Common_req { public: Request (Connection &con, Args... args, std::function<vapi_error_e (Request<Req, Resp, Args...> &)> callback = nullptr) : Common_req{con}, callback{callback}, request{con, vapi_alloc<Req> (con, args...)}, response{con, nullptr} { } Request (const Request &) = delete; virtual ~Request () { if (RESPONSE_NOT_READY == get_response_state ()) { con.unregister_request (this); } } vapi_error_e execute () { return con.send (this); } const Msg<Req> &get_request (void) const { return request; } const Msg<Resp> &get_response (void) { return response; } private: virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id, void *shm_data) { assert (RESPONSE_NOT_READY == get_response_state ()); response.assign_response (id, shm_data); set_response_state (RESPONSE_READY); if (nullptr != callback) { return std::make_pair (callback (*this), true); } return std::make_pair (VAPI_OK, true); } std::function<vapi_error_e (Request<Req, Resp, Args...> &)> callback; Msg<Req> request; Msg<Resp> response; friend class Connection; }; /** * Class representing iterable set of responses of the same type */ template <typename M> class Result_set { public: ~Result_set () { } Result_set (const Result_set &) = delete; bool is_complete () const { return complete; } size_t size () const { return set.size (); } using const_iterator = typename std::vector<Msg<M>, typename Msg<M>::Msg_allocator>::const_iterator; const_iterator begin () const { return set.begin (); } const_iterator end () const { return set.end (); } void free_response (const_iterator pos) { set.erase (pos); } void free_all_responses () { set.clear (); } private: void mark_complete () { complete = true; } void assign_response (vapi_msg_id_t resp_id, void *shm_data) { if (resp_id != Msg<M>::get_msg_id ()) { { throw Unexpected_msg_id_exception (); } } else if (shm_data) { vapi_swap_to_host<M> (static_cast<M *> (shm_data)); set.emplace_back (con, shm_data); VAPI_DBG ("Result_set@%p emplace_back shm_data@%p", this, shm_data); } } Result_set (Connection &con) : con (con), complete{false} { } Connection &con; bool complete; std::vector<Msg<M>, typename Msg<M>::Msg_allocator> set; template <typename Req, typename Resp, typename... Args> friend class Dump; template <typename X> friend class Event_registration; }; /** * Class representing a dump request - zero or more identical responses to a * single request message */ template <typename Req, typename Resp, typename... Args> class Dump : public Common_req { public: Dump (Connection &con, Args... args, std::function<vapi_error_e (Dump<Req, Resp, Args...> &)> callback = nullptr) : Common_req{con}, request{con, vapi_alloc<Req> (con, args...)}, result_set{con}, callback{callback} { } Dump (const Dump &) = delete; virtual ~Dump () { } virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id, void *shm_data) { if (id == vapi_msg_id_control_ping_reply) { con.msg_free (shm_data); result_set.mark_complete (); set_response_state (RESPONSE_READY); if (nullptr != callback) { return std::make_pair (callback (*this), true); } return std::make_pair (VAPI_OK, true); } else { result_set.assign_response (id, shm_data); } return std::make_pair (VAPI_OK, false); } vapi_error_e execute () { return con.send_with_control_ping (this); } Msg<Req> &get_request (void) { return request; } using resp_type = typename Msg<Resp>::shm_data_type; const Result_set<Resp> &get_result_set (void) const { return result_set; } private: Msg<Req> request; Result_set<resp_type> result_set; std::function<vapi_error_e (Dump<Req, Resp, Args...> &)> callback; friend class Connection; }; /** * Class representing event registration - incoming events (messages) from * vpp as a result of a subscription (typically a want_* simple request) */ template <typename M> class Event_registration : public Common_req { public: Event_registration ( Connection &con, std::function<vapi_error_e (Event_registration<M> &)> callback = nullptr) : Common_req{con}, result_set{con}, callback{callback} { if (!con.is_msg_available (M::get_msg_id ())) { throw Msg_not_available_exception (); } con.register_event (this); } Event_registration (const Event_registration &) = delete; virtual ~Event_registration () { con.unregister_event (this); } virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id, void *shm_data) { result_set.assign_response (id, shm_data); if (nullptr != callback) { return std::make_pair (callback (*this), true); } return std::make_pair (VAPI_OK, true); } using resp_type = typename M::shm_data_type; Result_set<resp_type> &get_result_set (void) { return result_set; } private: Result_set<resp_type> result_set; std::function<vapi_error_e (Event_registration<M> &)> callback; }; }; #endif /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */