1 /**
2 * Copyright (C) Mellanox Technologies Ltd. 2001-2015.  ALL RIGHTS RESERVED.
3 *
4 * See file LICENSE for terms.
5 */
6 
7 #include "ud_base.h"
8 
9 #include <uct/uct_test.h>
10 
11 extern "C" {
12 #include <ucs/time/time.h>
13 #include <ucs/datastruct/queue.h>
14 #include <ucs/datastruct/ptr_array.h>
15 #include <uct/ib/ud/base/ud_ep.h>
16 #include <uct/ib/ud/base/ud_iface.h>
17 }
18 
19 
20 class test_ud_timer : public ud_base_test {
21 public:
22     /* ack while doing retransmit */
23     static int packet_count, rx_limit;
rx_npackets(uct_ud_ep_t * ep,uct_ud_neth_t * neth)24     static ucs_status_t rx_npackets(uct_ud_ep_t *ep, uct_ud_neth_t *neth)
25     {
26         if (packet_count++ < rx_limit) {
27             return UCS_OK;
28         }
29         else {
30             return UCS_ERR_INVALID_PARAM;
31         }
32     }
33     /* test slow timer and restransmit */
34     static int tick_count;
35 
tick_counter(uct_ud_ep_t * ep,uct_ud_neth_t * neth)36     static ucs_status_t tick_counter(uct_ud_ep_t *ep, uct_ud_neth_t *neth)
37     {
38         uct_ud_iface_t *iface = ucs_derived_of(ep->super.super.iface,
39                                                uct_ud_iface_t);
40 
41         /* hack to disable retransmit */
42         ep->tx.send_time = ucs_twheel_get_time(&iface->tx.timer);
43         tick_count++;
44         return UCS_OK;
45     }
46 
drop_packet(uct_ud_ep_t * ep,uct_ud_neth_t * neth)47     static ucs_status_t drop_packet(uct_ud_ep_t *ep, uct_ud_neth_t *neth)
48     {
49         return UCS_ERR_INVALID_PARAM;
50     }
51 
wait_for_rx_sn(unsigned sn)52     void wait_for_rx_sn(unsigned sn)
53     {
54         ucs_time_t deadline = ucs_get_time() +
55                               ucs_time_from_sec(10) * ucs::test_time_multiplier();
56         while ((ucs_get_time() < deadline) && (ucs_frag_list_sn(&ep(m_e2)->rx.ooo_pkts) < sn)) {
57             usleep(1000);
58         }
59     }
60 
wait_for_ep_destroyed(uct_ud_iface_t * iface,uint32_t ep_idx)61     void wait_for_ep_destroyed(uct_ud_iface_t *iface, uint32_t ep_idx)
62     {
63         ucs_time_t deadline = ucs_get_time() +
64                               ucs_time_from_sec(60) * ucs::test_time_multiplier();
65         void *ud_ep_tmp GTEST_ATTRIBUTE_UNUSED_;
66 
67         while ((ucs_get_time() < deadline) &&
68                ucs_ptr_array_lookup(&iface->eps, ep_idx, ud_ep_tmp)) {
69             usleep(1000);
70         }
71     }
72 };
73 
74 int test_ud_timer::rx_limit = 10;
75 int test_ud_timer::packet_count = 0;
76 int test_ud_timer::tick_count = 0;
77 
78 
79 /* single packet received without progress */
80 UCS_TEST_SKIP_COND_P(test_ud_timer, tx1,
81                      !check_caps(UCT_IFACE_FLAG_PUT_SHORT)) {
82     connect();
83     EXPECT_UCS_OK(tx(m_e1));
84     wait_for_rx_sn(1);
85     EXPECT_EQ(2, ep(m_e1)->tx.psn);
86     EXPECT_EQ(1, ucs_frag_list_sn(&ep(m_e2)->rx.ooo_pkts));
87 }
88 
89 /* multiple packets received without progress */
90 UCS_TEST_SKIP_COND_P(test_ud_timer, txn,
91                      !check_caps(UCT_IFACE_FLAG_PUT_SHORT)) {
92     unsigned i, N = 42;
93 
94     connect();
95     set_tx_win(m_e1, 1024);
96     for (i = 0; i < N; i++) {
97         EXPECT_UCS_OK(tx(m_e1));
98     }
99     wait_for_rx_sn(N);
100     EXPECT_EQ(N+1, ep(m_e1)->tx.psn);
101     EXPECT_EQ(N, ucs_frag_list_sn(&ep(m_e2)->rx.ooo_pkts));
102 }
103 
104 UCS_TEST_P(test_ud_timer, ep_destroy, "UD_TIMEOUT=1s") {
105     void *ud_ep_tmp GTEST_ATTRIBUTE_UNUSED_;
106     connect();
107 
108     uct_ud_ep_t    *ud_ep = ep(m_e1);
109     uct_ud_iface_t *iface = ucs_derived_of(ud_ep->super.super.iface,
110                                            uct_ud_iface_t);
111     uint32_t       ep_idx = ud_ep->ep_id;
112     EXPECT_TRUE(ucs_ptr_array_lookup(&iface->eps, ep_idx, ud_ep_tmp));
113 
114     m_e1->destroy_eps();
115     wait_for_ep_destroyed(iface, ep_idx);
116     EXPECT_FALSE(ucs_ptr_array_lookup(&iface->eps, ep_idx, ud_ep_tmp));
117 }
118 
UCS_TEST_P(test_ud_timer,backoff_config)119 UCS_TEST_P(test_ud_timer, backoff_config) {
120     /* check minimum allowed value */
121     ASSERT_UCS_OK(uct_config_modify(m_iface_config,
122                   "UD_TIMER_BACKOFF",
123                   ucs::to_string(UCT_UD_MIN_TIMER_TIMER_BACKOFF).c_str()));
124     entity *e = uct_test::create_entity(0);
125     m_entities.push_back(e);
126 
127     {
128         /* iface creation should fail with back off value less than
129          * UCT_UD_MIN_TIMER_TIMER_BACKOFF */
130         ASSERT_UCS_OK(uct_config_modify(m_iface_config,
131                       "UD_TIMER_BACKOFF",
132                       ucs::to_string(UCT_UD_MIN_TIMER_TIMER_BACKOFF - 0.1).c_str()));
133         scoped_log_handler wrap_err(wrap_errors_logger);
134         uct_iface_h iface;
135         ucs_status_t status = uct_iface_open(e->md(), e->worker(),
136                                              &e->iface_params(),
137                                              m_iface_config, &iface);
138         EXPECT_EQ(UCS_ERR_INVALID_PARAM, status);
139         EXPECT_EQ(NULL, iface);
140     }
141 }
142 
143 #if UCT_UD_EP_DEBUG_HOOKS
144 /* no traffic - no ticks */
UCS_TEST_P(test_ud_timer,tick1)145 UCS_TEST_P(test_ud_timer, tick1) {
146     connect();
147     tick_count = 0;
148     ep(m_e1)->timer_hook = tick_counter;
149     twait(500);
150     EXPECT_EQ(0, tick_count);
151 }
152 
153 /* ticks while tx  window is not empty */
154 UCS_TEST_SKIP_COND_P(test_ud_timer, tick2,
155                      !check_caps(UCT_IFACE_FLAG_PUT_SHORT)) {
156     connect();
157     tick_count = 0;
158     ep(m_e1)->timer_hook = tick_counter;
159     EXPECT_UCS_OK(tx(m_e1));
160     twait(500);
161     EXPECT_LT(0, tick_count);
162 }
163 
164 /* retransmit one packet */
165 
166 UCS_TEST_SKIP_COND_P(test_ud_timer, retransmit1,
167                      !check_caps(UCT_IFACE_FLAG_PUT_SHORT)) {
168     connect();
169     ep(m_e2)->rx.rx_hook = drop_packet;
170     EXPECT_UCS_OK(tx(m_e1));
171     short_progress_loop();
172     EXPECT_EQ(0, ucs_frag_list_sn(&ep(m_e2)->rx.ooo_pkts));
173     ep(m_e2)->rx.rx_hook = uct_ud_ep_null_hook;
174     EXPECT_EQ(2, ep(m_e1)->tx.psn);
175     wait_for_rx_sn(1);
176     EXPECT_EQ(2, ep(m_e1)->tx.psn);
177     EXPECT_EQ(1, ucs_frag_list_sn(&ep(m_e2)->rx.ooo_pkts));
178 }
179 
180 /* retransmit many packets */
181 UCS_TEST_SKIP_COND_P(test_ud_timer, retransmitn,
182                      !check_caps(UCT_IFACE_FLAG_PUT_SHORT)) {
183 
184     unsigned i, N = 42;
185 
186     connect();
187     set_tx_win(m_e1, 1024);
188     ep(m_e2)->rx.rx_hook = drop_packet;
189     for (i = 0; i < N; i++) {
190         EXPECT_UCS_OK(tx(m_e1));
191     }
192     short_progress_loop();
193     EXPECT_EQ(0, ucs_frag_list_sn(&ep(m_e2)->rx.ooo_pkts));
194     ep(m_e2)->rx.rx_hook = uct_ud_ep_null_hook;
195     EXPECT_EQ(N+1, ep(m_e1)->tx.psn);
196     wait_for_rx_sn(N);
197     EXPECT_EQ(N+1, ep(m_e1)->tx.psn);
198     EXPECT_EQ(N, ucs_frag_list_sn(&ep(m_e2)->rx.ooo_pkts));
199 }
200 
201 
202 UCS_TEST_SKIP_COND_P(test_ud_timer, partial_drop,
203                      !check_caps(UCT_IFACE_FLAG_PUT_SHORT)) {
204 
205     unsigned i, N = 24;
206     int orig_avail;
207 
208     connect();
209     set_tx_win(m_e1, 1024);
210     packet_count = 0;
211     rx_limit = 10;
212     ep(m_e2)->rx.rx_hook = rx_npackets;
213     for (i = 0; i < N; i++) {
214         EXPECT_UCS_OK(tx(m_e1));
215     }
216     short_progress_loop();
217     EXPECT_EQ(rx_limit, ucs_frag_list_sn(&ep(m_e2)->rx.ooo_pkts));
218     ep(m_e2)->rx.rx_hook = uct_ud_ep_null_hook;
219     EXPECT_EQ(N+1, ep(m_e1)->tx.psn);
220     orig_avail = iface(m_e1)->tx.available;
221     /* allow only 6 outgoing packets. It will allow to get ack
222      * from receiver
223      */
224     iface(m_e1)->tx.available = 6;
225     twait(500);
226     iface(m_e1)->tx.available = orig_avail-6;
227     short_progress_loop();
228 
229     EXPECT_EQ(N+1, ep(m_e1)->tx.psn);
230     wait_for_rx_sn(N);
231     EXPECT_EQ(N, ucs_frag_list_sn(&ep(m_e2)->rx.ooo_pkts));
232 }
233 #endif
234 
235 UCT_INSTANTIATE_UD_TEST_CASE(test_ud_timer)
236