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