1 /**
2 * Copyright (C) Mellanox Technologies Ltd. 2001-2020.  ALL RIGHTS RESERVED.
3 *
4 * See file LICENSE for terms.
5 */
6 
7 
8 #ifndef UCT_UD_IFACE_H
9 #define UCT_UD_IFACE_H
10 
11 #include <uct/base/uct_worker.h>
12 #include <uct/ib/base/ib_device.h>
13 #include <uct/ib/base/ib_iface.h>
14 #include <ucs/datastruct/sglib_wrapper.h>
15 #include <ucs/datastruct/ptr_array.h>
16 #include <ucs/datastruct/sglib.h>
17 #include <ucs/datastruct/list.h>
18 #include <ucs/datastruct/arbiter.h>
19 #include <ucs/async/async.h>
20 #include <ucs/time/timer_wheel.h>
21 #include <ucs/sys/compiler_def.h>
22 #include <ucs/sys/sock.h>
23 
24 #include "ud_def.h"
25 #include "ud_ep.h"
26 #include "ud_iface_common.h"
27 
28 BEGIN_C_DECLS
29 
30 
31 #define UCT_UD_MIN_TIMER_TIMER_BACKOFF 1.0
32 
33 
34 /** @file ud_iface.h */
35 
36 enum {
37     UCT_UD_IFACE_STAT_RX_DROP,
38     UCT_UD_IFACE_STAT_LAST
39 };
40 
41 
42 /* flags for uct_ud_iface_send_ctl() */
43 enum {
44     UCT_UD_IFACE_SEND_CTL_FLAG_INLINE    = UCS_BIT(0),
45     UCT_UD_IFACE_SEND_CTL_FLAG_SOLICITED = UCS_BIT(1),
46     UCT_UD_IFACE_SEND_CTL_FLAG_SIGNALED  = UCS_BIT(2)
47 };
48 
49 
50 /* TODO: maybe tx_moderation can be defined at compile-time since tx completions are used only to know how much space is there in tx qp */
51 
52 typedef struct uct_ud_iface_config {
53     uct_ib_iface_config_t         super;
54     uct_ud_iface_common_config_t  ud_common;
55     double                        peer_timeout;
56     double                        timer_tick;
57     double                        timer_backoff;
58     double                        event_timer_tick;
59     int                           dgid_check;
60     unsigned                      max_window;
61     unsigned                      rx_async_max_poll;
62 } uct_ud_iface_config_t;
63 
64 
65 struct uct_ud_iface_peer {
66     uct_ud_iface_peer_t   *next;
67     union ibv_gid          dgid;
68     uint16_t               dlid;
69     uint32_t               dst_qpn;
70     uint8_t                path_index;
71     uint32_t               conn_id_last;
72     ucs_list_link_t        ep_list; /* ep list ordered by connection id */
73 };
74 
75 
76 static inline int
uct_ud_iface_peer_cmp(uct_ud_iface_peer_t * a,uct_ud_iface_peer_t * b)77 uct_ud_iface_peer_cmp(uct_ud_iface_peer_t *a, uct_ud_iface_peer_t *b)
78 {
79     return (int)a->dst_qpn - (int)b->dst_qpn ||
80            memcmp(a->dgid.raw, b->dgid.raw, sizeof(union ibv_gid)) ||
81            ((int)a->dlid - (int)b->dlid) ||
82            ((int)a->path_index - (int)b->path_index);
83 }
84 
85 
uct_ud_iface_peer_hash(uct_ud_iface_peer_t * a)86 static inline int uct_ud_iface_peer_hash(uct_ud_iface_peer_t *a)
87 {
88     return (a->dlid + a->dgid.global.interface_id +
89             a->dgid.global.subnet_prefix + (a->path_index * 137)) %
90            UCT_UD_HASH_SIZE;
91 }
92 
93 
94 SGLIB_DEFINE_LIST_PROTOTYPES(uct_ud_iface_peer_t, uct_ud_iface_peer_cmp, next)
95 SGLIB_DEFINE_HASHED_CONTAINER_PROTOTYPES(uct_ud_iface_peer_t, UCT_UD_HASH_SIZE,
96                                          uct_ud_iface_peer_hash)
97 
98 
99 #if UCT_UD_EP_DEBUG_HOOKS
100 
101 typedef ucs_status_t (*uct_ud_iface_hook_t)(uct_ud_iface_t *iface, uct_ud_neth_t *neth);
102 
103 
104 #define UCT_UD_IFACE_HOOK_DECLARE(_name) \
105     uct_ud_iface_hook_t _name;
106 
107 
108 #define UCT_UD_IFACE_HOOK_CALL_RX(_iface, _neth, _len) \
109     if ((_iface)->rx.hook(_iface, _neth) != UCS_OK) { \
110         ucs_trace_data("RX: dropping packet"); \
111         return; \
112     }
113 
114 
115 #define UCT_UD_IFACE_HOOK_INIT(_iface) { \
116         (_iface)->rx.hook = uct_ud_iface_null_hook; \
117     }
118 
119 
uct_ud_iface_null_hook(uct_ud_iface_t * iface,uct_ud_neth_t * neth)120 static inline ucs_status_t uct_ud_iface_null_hook(uct_ud_iface_t *iface,
121                                                   uct_ud_neth_t *neth)
122 {
123     return UCS_OK;
124 }
125 
126 #else
127 
128 #define UCT_UD_IFACE_HOOK_DECLARE(_name)
129 #define UCT_UD_IFACE_HOOK_CALL_RX(_iface, _neth, _len)
130 #define UCT_UD_IFACE_HOOK_INIT(_iface)
131 
132 #endif
133 
134 
135 typedef struct uct_ud_iface_ops {
136     uct_ib_iface_ops_t        super;
137     unsigned                  (*async_progress)(uct_ud_iface_t *iface);
138     uint16_t                  (*send_ctl)(uct_ud_ep_t *ud_ep, uct_ud_send_skb_t *skb,
139                                           const uct_ud_iov_t *iov, uint16_t iovcnt,
140                                           int flags, int max_log_sge);
141     void                      (*ep_free)(uct_ep_h ep);
142     ucs_status_t              (*create_qp)(uct_ib_iface_t *iface, uct_ib_qp_attr_t *attr,
143                                            struct ibv_qp **qp_p);
144 } uct_ud_iface_ops_t;
145 
146 
147 /* device GIDs set */
148 KHASH_TYPE(uct_ud_iface_gid, union ibv_gid, char);
149 
150 
151 static UCS_F_ALWAYS_INLINE
uct_ud_iface_kh_gid_hash_func(union ibv_gid gid)152 khint32_t uct_ud_iface_kh_gid_hash_func(union ibv_gid gid)
153 {
154     return kh_int64_hash_func(gid.global.subnet_prefix ^
155                               gid.global.interface_id);
156 }
157 
158 
159 static UCS_F_ALWAYS_INLINE int
uct_ud_gid_equal(const union ibv_gid * a,const union ibv_gid * b,size_t length)160 uct_ud_gid_equal(const union ibv_gid *a, const union ibv_gid *b, size_t length)
161 {
162     ucs_assert(length <= sizeof(union ibv_gid));
163     return !memcmp(UCS_PTR_BYTE_OFFSET(a, sizeof(*a) - length),
164                    UCS_PTR_BYTE_OFFSET(b, sizeof(*b) - length), length);
165 }
166 
167 
168 static UCS_F_ALWAYS_INLINE int
uct_ud_iface_kh_gid_hash_equal(union ibv_gid a,union ibv_gid b)169 uct_ud_iface_kh_gid_hash_equal(union ibv_gid a, union ibv_gid b)
170 {
171     return uct_ud_gid_equal(&a, &b, sizeof(a));
172 }
173 
174 
175 KHASH_IMPL(uct_ud_iface_gid, union ibv_gid, char, 0,
176            uct_ud_iface_kh_gid_hash_func, uct_ud_iface_kh_gid_hash_equal)
177 
178 
179 struct uct_ud_iface {
180     uct_ib_iface_t           super;
181     struct ibv_qp           *qp;
182     struct {
183         ucs_mpool_t          mp;
184         unsigned             available;
185         unsigned             quota;
186         unsigned             async_max_poll;
187         ucs_queue_head_t     pending_q;
188         UCT_UD_IFACE_HOOK_DECLARE(hook)
189     } rx;
190     struct {
191         uct_ud_send_skb_t     *skb; /* ready to use skb */
192         ucs_mpool_t            mp;
193         /* got async events but pending queue was not dispatched */
194         uint8_t                async_before_pending;
195         int16_t                available;
196         unsigned               unsignaled;
197         ucs_queue_head_t       outstanding_q;
198         ucs_arbiter_t          pending_q;
199         ucs_queue_head_t       async_comp_q;
200         ucs_twheel_t           timer;
201         ucs_time_t             tick;
202         double                 timer_backoff;
203         unsigned               timer_sweep_count;
204     } tx;
205     struct {
206         ucs_time_t           peer_timeout;
207         unsigned             tx_qp_len;
208         unsigned             max_inline;
209         int                  check_grh_dgid;
210         unsigned             max_window;
211     } config;
212 
213     UCS_STATS_NODE_DECLARE(stats)
214 
215     ucs_ptr_array_t       eps;
216     uct_ud_iface_peer_t  *peers[UCT_UD_HASH_SIZE];
217     struct {
218         ucs_time_t                tick;
219         int                       timer_id;
220         void                      *event_arg;
221         uct_async_event_cb_t      event_cb;
222         unsigned                  disable;
223     } async;
224 
225     /* used for GRH GID filter */
226     struct {
227         union ibv_gid             last;
228         unsigned                  last_len;
229         khash_t(uct_ud_iface_gid) hash;
230     } gid_table;
231 };
232 
233 
234 UCS_CLASS_DECLARE(uct_ud_iface_t, uct_ud_iface_ops_t*, uct_md_h,
235                   uct_worker_h, const uct_iface_params_t*,
236                   const uct_ud_iface_config_t*,
237                   uct_ib_iface_init_attr_t*)
238 
239 
240 struct uct_ud_ctl_hdr {
241     uint8_t                    type;
242     uint8_t                    reserved[3];
243     union {
244         struct {
245             uct_ud_ep_addr_t   ep_addr;
246             uint32_t           conn_id;
247             uint8_t            path_index;
248         } conn_req;
249         struct {
250             uint32_t           src_ep_id;
251         } conn_rep;
252         uint32_t               data;
253     };
254     uct_ud_peer_name_t         peer;
255     /* For CREQ packet, IB address follows */
256 } UCS_S_PACKED;
257 
258 
259 extern ucs_config_field_t uct_ud_iface_config_table[];
260 
261 
262 ucs_status_t uct_ud_iface_query(uct_ud_iface_t *iface,
263                                 uct_iface_attr_t *iface_attr,
264                                 size_t am_max_iov, size_t am_max_hdr);
265 
266 void uct_ud_iface_release_desc(uct_recv_desc_t *self, void *desc);
267 
268 ucs_status_t uct_ud_iface_get_address(uct_iface_h tl_iface, uct_iface_addr_t *addr);
269 
270 void uct_ud_iface_add_ep(uct_ud_iface_t *iface, uct_ud_ep_t *ep);
271 
272 void uct_ud_iface_remove_ep(uct_ud_iface_t *iface, uct_ud_ep_t *ep);
273 
274 void uct_ud_iface_replace_ep(uct_ud_iface_t *iface, uct_ud_ep_t *old_ep, uct_ud_ep_t *new_ep);
275 
276 ucs_status_t uct_ud_iface_flush(uct_iface_h tl_iface, unsigned flags,
277                                 uct_completion_t *comp);
278 
279 ucs_status_t uct_ud_iface_complete_init(uct_ud_iface_t *iface);
280 
281 void uct_ud_iface_remove_async_handlers(uct_ud_iface_t *iface);
282 
283 void uct_ud_dump_packet(uct_base_iface_t *iface, uct_am_trace_type_t type,
284                         void *data, size_t length, size_t valid_length,
285                         char *buffer, size_t max);
286 
287 union ibv_gid* uct_ud_grh_get_dgid(struct ibv_grh *grh, size_t dgid_len);
288 
289 uct_ud_send_skb_t *uct_ud_iface_ctl_skb_get(uct_ud_iface_t *iface);
290 
291 /*
292 management of connecting endpoints (cep)
293 
294 Such endpoint are created either by explicitely calling ep_create_connected()
295 or implicitely as a result of UD connection protocol. Calling
296 ep_create_connected() may reuse already existing endpoint that was implicitely
297 created.
298 
299 UD connection protocol
300 
301 The protocol allows connection establishment in environment where UD packets
302 can be dropped, duplicated or reordered. The connection is done as 3 way
303 handshake:
304 
305 1: CREQ (src_if_addr, src_ep_addr, conn_id)
306 Connection request. It includes source interface address, source ep address
307 and connection id.
308 
309 Connection id is essentially a counter of endpoints that are created by
310 ep_create_connected(). The counter is per destination interface. Purpose of
311 conn_id is to ensure order between multiple CREQ packets and to handle
312 simultanuous connection establishment. The case when both sides call
313 ep_create_connected(). The rule is that connected endpoints must have
314 same conn_id.
315 
316 2: CREP (dest_ep_id)
317 
318 Connection reply. It includes id of destination endpoint and optinally ACK
319 request flag. From this point reliability is handled by UD protocol as
320 source and destination endpoint ids are known.
321 
322 Endpoint may be created upon reception of CREQ. It is possible that the
323 endpoint already exists because CREQ is retransmitted or because of
324 simultaneous connection. In any case endpoint connection id must be
325 equal to connection id in CREQ.
326 
327 3: ACK
328 
329 Ack on connection reply. It may be send as part of the data packet.
330 
331 Implicit endpoints reuse
332 
333 Endpoints created upon receive of CREP request can be re-used when
334 application calls ep_create_connected().
335 
336 Data structure
337 
338 Hash table and double linked sorted list:
339 hash(src_if_addr) -> peer ->ep (list sorted in descending order)
340 
341 List is used to save memory (8 bytes instead of 500-1000 bytes of hashtable)
342 In many cases list will provide fast lookup and insertion.
343 It is expected that most of connect requests will arrive in order. In
344 such case the insertion is O(1) because it is done to the head of the
345 list. Lookup is O(number of 'passive' eps) which is expected to be small.
346 
347 TODO: add and maintain pointer to the list element with conn_id equal to
348 conn_last_id. This will allow for O(1) endpoint lookup.
349 
350 Connection id assignment:
351 
352   0 1 ... conn_last_id, +1, +2, ... UCT_UD_EP_CONN_ID_MAX
353 
354 Ids upto (not including) conn_last_id are already assigned to endpoints.
355 Any endpoint with conn_id >= conn_last_id is created on receive of CREQ
356 There may be holes because CREQs are not received in order.
357 
358 Call to ep_create_connected() will try reuse endpoint with
359 conn_id = conn_last_id
360 
361 If there is no such endpoint new endpoint with id conn_last_id
362 will be created.
363 
364 In both cases conn_last_id = conn_last_id + 1
365 
366 */
367 void uct_ud_iface_cep_init(uct_ud_iface_t *iface);
368 
369 /* find ep that is connected to (src_if, src_ep),
370  * if conn_id == UCT_UD_EP_CONN_ID_MAX then try to
371  * reuse ep with conn_id == conn_last_id
372  */
373 uct_ud_ep_t *uct_ud_iface_cep_lookup(uct_ud_iface_t *iface,
374                                      const uct_ib_address_t *src_ib_addr,
375                                      const uct_ud_iface_addr_t *src_if_addr,
376                                      uint32_t conn_id, int path_index);
377 
378 /* remove ep */
379 void uct_ud_iface_cep_remove(uct_ud_ep_t *ep);
380 
381 /*
382  * rollback last ordered insert (conn_id == UCT_UD_EP_CONN_ID_MAX).
383  */
384 void uct_ud_iface_cep_rollback(uct_ud_iface_t *iface,
385                                const uct_ib_address_t *src_ib_addr,
386                                const uct_ud_iface_addr_t *src_if_addr,
387                                uct_ud_ep_t *ep);
388 
389 /* insert new ep that is connected to src_if_addr */
390 ucs_status_t uct_ud_iface_cep_insert(uct_ud_iface_t *iface,
391                                      const uct_ib_address_t *src_ib_addr,
392                                      const uct_ud_iface_addr_t *src_if_addr,
393                                      uct_ud_ep_t *ep, uint32_t conn_id,
394                                      int path_index);
395 
396 void uct_ud_iface_cep_cleanup(uct_ud_iface_t *iface);
397 
398 ucs_status_t uct_ud_iface_dispatch_pending_rx_do(uct_ud_iface_t *iface);
399 
400 ucs_status_t uct_ud_iface_event_arm(uct_iface_h tl_iface, unsigned events);
401 
402 void uct_ud_iface_progress_enable(uct_iface_h tl_iface, unsigned flags);
403 
404 void uct_ud_iface_progress_disable(uct_iface_h tl_iface, unsigned flags);
405 
406 void uct_ud_iface_ctl_skb_complete(uct_ud_iface_t *iface,
407                                    uct_ud_ctl_desc_t *cdesc, int is_async);
408 
409 void uct_ud_iface_send_completion(uct_ud_iface_t *iface, uint16_t sn,
410                                   int is_async);
411 
412 void uct_ud_iface_dispatch_async_comps_do(uct_ud_iface_t *iface);
413 
414 
uct_ud_iface_can_tx(uct_ud_iface_t * iface)415 static UCS_F_ALWAYS_INLINE int uct_ud_iface_can_tx(uct_ud_iface_t *iface)
416 {
417     return iface->tx.available > 0;
418 }
419 
420 
uct_ud_iface_has_skbs(uct_ud_iface_t * iface)421 static UCS_F_ALWAYS_INLINE int uct_ud_iface_has_skbs(uct_ud_iface_t *iface)
422 {
423     return iface->tx.skb || !ucs_mpool_is_empty(&iface->tx.mp);
424 }
425 
426 
uct_ud_creq_ib_addr(uct_ud_ctl_hdr_t * conn_req)427 static inline uct_ib_address_t* uct_ud_creq_ib_addr(uct_ud_ctl_hdr_t *conn_req)
428 {
429     ucs_assert(conn_req->type == UCT_UD_PACKET_CREQ);
430     return (uct_ib_address_t*)(conn_req + 1);
431 }
432 
433 
uct_ud_enter(uct_ud_iface_t * iface)434 static UCS_F_ALWAYS_INLINE void uct_ud_enter(uct_ud_iface_t *iface)
435 {
436     UCS_ASYNC_BLOCK(iface->super.super.worker->async);
437 }
438 
439 
uct_ud_leave(uct_ud_iface_t * iface)440 static UCS_F_ALWAYS_INLINE void uct_ud_leave(uct_ud_iface_t *iface)
441 {
442     UCS_ASYNC_UNBLOCK(iface->super.super.worker->async);
443 }
444 
445 
446 static UCS_F_ALWAYS_INLINE unsigned
uct_ud_grh_get_dgid_len(struct ibv_grh * grh)447 uct_ud_grh_get_dgid_len(struct ibv_grh *grh)
448 {
449     static const uint8_t ipmask = 0xf0;
450     uint8_t ipver               = ((*(uint8_t*)grh) & ipmask);
451 
452     return (ipver == (6 << 4)) ? UCS_IPV6_ADDR_LEN : UCS_IPV4_ADDR_LEN;
453 }
454 
455 
456 static UCS_F_ALWAYS_INLINE int
uct_ud_iface_check_grh(uct_ud_iface_t * iface,void * packet,int is_grh_present)457 uct_ud_iface_check_grh(uct_ud_iface_t *iface, void *packet, int is_grh_present)
458 {
459     struct ibv_grh *grh = (struct ibv_grh *)packet;
460     size_t gid_len;
461     union ibv_gid *gid;
462     khiter_t khiter;
463     char gid_str[128] UCS_V_UNUSED;
464 
465     if (!iface->config.check_grh_dgid) {
466         return 1;
467     }
468 
469     if (ucs_unlikely(!is_grh_present)) {
470         ucs_warn("RoCE packet does not contain GRH");
471         return 1;
472     }
473 
474     gid_len = uct_ud_grh_get_dgid_len(grh);
475     if (ucs_likely((gid_len == iface->gid_table.last_len) &&
476                     uct_ud_gid_equal(&grh->dgid, &iface->gid_table.last,
477                                      gid_len))) {
478         return 1;
479     }
480 
481     gid    = uct_ud_grh_get_dgid(grh, gid_len);
482     khiter = kh_get(uct_ud_iface_gid, &iface->gid_table.hash, *gid);
483     if (ucs_likely(khiter != kh_end(&iface->gid_table.hash))) {
484         iface->gid_table.last     = *gid;
485         iface->gid_table.last_len = gid_len;
486         return 1;
487     }
488 
489     UCS_STATS_UPDATE_COUNTER(iface->stats, UCT_UD_IFACE_STAT_RX_DROP, 1);
490     ucs_trace_data("iface %p: drop packet with wrong dgid %s", iface,
491                    uct_ib_gid_str(gid, gid_str, sizeof(gid_str)));
492     return 0;
493 }
494 
495 
496 /* get time of the last async wakeup */
497 static UCS_F_ALWAYS_INLINE ucs_time_t
uct_ud_iface_get_async_time(uct_ud_iface_t * iface)498 uct_ud_iface_get_async_time(uct_ud_iface_t *iface)
499 {
500     return iface->super.super.worker->async->last_wakeup;
501 }
502 
503 
504 static UCS_F_ALWAYS_INLINE ucs_time_t
uct_ud_iface_get_time(uct_ud_iface_t * iface)505 uct_ud_iface_get_time(uct_ud_iface_t *iface)
506 {
507     return ucs_get_time();
508 }
509 
510 
511 static UCS_F_ALWAYS_INLINE void
uct_ud_iface_twheel_sweep(uct_ud_iface_t * iface)512 uct_ud_iface_twheel_sweep(uct_ud_iface_t *iface)
513 {
514     if (iface->tx.timer_sweep_count++ % UCT_UD_SKIP_SWEEP) {
515         return;
516     }
517 
518     if (ucs_twheel_is_empty(&iface->tx.timer)) {
519         return;
520     }
521 
522     ucs_twheel_sweep(&iface->tx.timer, uct_ud_iface_get_time(iface));
523 }
524 
525 
526 static UCS_F_ALWAYS_INLINE void
uct_ud_iface_progress_pending(uct_ud_iface_t * iface,const uintptr_t is_async)527 uct_ud_iface_progress_pending(uct_ud_iface_t *iface, const uintptr_t is_async)
528 {
529     uct_ud_iface_twheel_sweep(iface);
530 
531     if (!is_async) {
532         iface->tx.async_before_pending = 0;
533     }
534 
535     if (!uct_ud_iface_can_tx(iface)) {
536         return;
537     }
538 
539     ucs_arbiter_dispatch(&iface->tx.pending_q, 1, uct_ud_ep_do_pending,
540                          (void *)is_async);
541 }
542 
543 
544 static UCS_F_ALWAYS_INLINE int
uct_ud_iface_has_pending_async_ev(uct_ud_iface_t * iface)545 uct_ud_iface_has_pending_async_ev(uct_ud_iface_t *iface)
546 {
547     return iface->tx.async_before_pending;
548 }
549 
550 
551 static UCS_F_ALWAYS_INLINE void
uct_ud_iface_raise_pending_async_ev(uct_ud_iface_t * iface)552 uct_ud_iface_raise_pending_async_ev(uct_ud_iface_t *iface)
553 {
554     if (!ucs_arbiter_is_empty(&iface->tx.pending_q)) {
555         iface->tx.async_before_pending = 1;
556     }
557 }
558 
559 
560 static UCS_F_ALWAYS_INLINE uint16_t
uct_ud_iface_send_ctl(uct_ud_iface_t * iface,uct_ud_ep_t * ep,uct_ud_send_skb_t * skb,const uct_ud_iov_t * iov,uint16_t iovcnt,int flags,int max_log_sge)561 uct_ud_iface_send_ctl(uct_ud_iface_t *iface, uct_ud_ep_t *ep, uct_ud_send_skb_t *skb,
562                       const uct_ud_iov_t *iov, uint16_t iovcnt, int flags,
563                       int max_log_sge)
564 {
565     uct_ud_iface_ops_t *ud_ops = ucs_derived_of(iface->super.ops,
566                                                 uct_ud_iface_ops_t);
567     return ud_ops->send_ctl(ep, skb, iov, iovcnt, flags, max_log_sge);
568 }
569 
570 
571 static UCS_F_ALWAYS_INLINE void
uct_ud_iface_add_ctl_desc(uct_ud_iface_t * iface,uct_ud_ctl_desc_t * cdesc)572 uct_ud_iface_add_ctl_desc(uct_ud_iface_t *iface, uct_ud_ctl_desc_t *cdesc)
573 {
574     ucs_queue_push(&iface->tx.outstanding_q, &cdesc->queue);
575 }
576 
577 
578 /* Go over all active eps and remove them. Do it this way because class destructors are not
579  * virtual
580  */
581 #define UCT_UD_IFACE_DELETE_EPS(_iface, _ep_type_t) \
582     { \
583         int _i; \
584         _ep_type_t *_ep; \
585         ucs_ptr_array_for_each(_ep, _i, &(_iface)->eps) { \
586             UCS_CLASS_DELETE(_ep_type_t, _ep); \
587         } \
588     }
589 
590 
591 static UCS_F_ALWAYS_INLINE ucs_status_t
uct_ud_iface_dispatch_pending_rx(uct_ud_iface_t * iface)592 uct_ud_iface_dispatch_pending_rx(uct_ud_iface_t *iface)
593 {
594     if (ucs_likely(ucs_queue_is_empty(&iface->rx.pending_q))) {
595         return UCS_OK;
596     }
597     return uct_ud_iface_dispatch_pending_rx_do(iface);
598 }
599 
600 
601 static UCS_F_ALWAYS_INLINE void
uct_ud_iface_dispatch_async_comps(uct_ud_iface_t * iface)602 uct_ud_iface_dispatch_async_comps(uct_ud_iface_t *iface)
603 {
604     if (ucs_likely(ucs_queue_is_empty(&iface->tx.async_comp_q))) {
605         return;
606     }
607     uct_ud_iface_dispatch_async_comps_do(iface);
608 }
609 
610 #if ENABLE_PARAMS_CHECK
611 #define UCT_UD_CHECK_LENGTH(iface, header_len, payload_len, msg) \
612      do { \
613          int mtu; \
614          mtu =  uct_ib_mtu_value(uct_ib_iface_port_attr(&(iface)->super)->active_mtu); \
615          UCT_CHECK_LENGTH(sizeof(uct_ud_neth_t) + payload_len + header_len, \
616                           0, mtu, msg); \
617      } while(0);
618 
619 #define UCT_UD_CHECK_BCOPY_LENGTH(iface, len) \
620     UCT_UD_CHECK_LENGTH(iface, 0, len, "am_bcopy length")
621 
622 #define UCT_UD_CHECK_ZCOPY_LENGTH(iface, header_len, payload_len) \
623     UCT_UD_CHECK_LENGTH(iface, header_len, payload_len, "am_zcopy payload")
624 
625 #else
626 #define UCT_UD_CHECK_ZCOPY_LENGTH(iface, header_len, payload_len)
627 #define UCT_UD_CHECK_BCOPY_LENGTH(iface, len)
628 #endif
629 
630 END_C_DECLS
631 
632 #endif
633