1
1
openmpi/ompi/mca/btl/usnic
Nathan Hjelm a14e0f10d4 Per RFC: Remove des_src and des_dst members from the
mca_btl_base_segment_t and replace them with des_local and des_remote

This change also updates the BTL version to 3.0.0. This commit does
not represent the final version of BTL 3.0.0. More changes are coming.

In making this change I updated all of the BTLs as well as BTL user's
to use the new structure members. Please evaluate your component to
ensure the changes are correct.

RFC text:

This is the first of several BTL interface changes I am proposing for
the 1.9/2.0 release series.

What: Change naming of btl descriptor members. I propose we change
des_src and des_dst (and their associated counts) to be des_local and
des_remote. For receive callbacks the des_local member will be used to
communicate the segment information to the callback. The proposed change
will include updating all of the doxygen in btl.h as well as updating
all BTLs and BTL users to use the new naming scheme.

Why: My btl usage makes use of both put and get operations on the same
descriptor. With the current naming scheme I need to ensure that there
is consistency beteen the segments described in des_src and des_dst
depending on whether a put or get operation is executed. Additionally,
the current naming prevents BTLs that do not require prepare/RMA matched
operations (do not set MCA_BTL_FLAGS_RDMA_MATCHED) from executing
multiple simultaneous put AND get operations. At the moment the
descriptor can only be used with one or the other. The naming change
makes it easier for BTL users to setup/modify descriptors for RMA
operations as the local segment and remote segment are always in the
same member field. The only issue I forsee with this change is that it
will require a little more work to move BTL fixes to the 1.8 release
series.

This commit was SVN r32196.
2014-07-10 16:31:15 +00:00
..
test usnic: add graph utility code 2014-02-26 07:49:54 +00:00
btl_usnic_ack.c usnic: Shrink sequence numbers to 16 bits 2014-02-26 07:40:10 +00:00
btl_usnic_ack.h usnic: Shrink sequence numbers to 16 bits 2014-02-26 07:40:10 +00:00
btl_usnic_cagent.c usnic: move some defines into btl_usnic_util.h 2014-06-18 15:19:36 +00:00
btl_usnic_cclient.c usnic: let connectivity client timeout if agent never appears 2014-06-02 18:14:35 +00:00
btl_usnic_compat.h usnic: improve interface matching (esp. for UDP) 2014-02-26 07:50:26 +00:00
btl_usnic_component.c Per RFC: Remove des_src and des_dst members from the 2014-07-10 16:31:15 +00:00
btl_usnic_connectivity.h usnic: update cclient/cagent to use unix domain sockets (not RML) 2014-05-09 20:35:36 +00:00
btl_usnic_endpoint.c usnic: re-order teardown sequence now that del_procs() is actually called 2014-05-21 23:59:17 +00:00
btl_usnic_endpoint.h usnic: ensure that procs agree on use_udp value 2014-05-08 16:43:50 +00:00
btl_usnic_ext.c Fix ibv_port_query() usnic extension usage 2014-03-04 22:11:47 +00:00
btl_usnic_ext.h Fix ibv_port_query() usnic extension usage 2014-03-04 22:11:47 +00:00
btl_usnic_frag.c Per RFC: Remove des_src and des_dst members from the 2014-07-10 16:31:15 +00:00
btl_usnic_frag.h usnic: remove unnecessary header union 2014-03-03 21:33:12 +00:00
btl_usnic_graph.c usnic: add graph utility code 2014-02-26 07:49:54 +00:00
btl_usnic_graph.h usnic: add graph utility code 2014-02-26 07:49:54 +00:00
btl_usnic_hwloc.c usnic: v1.6<->trunk unification, trunk side 2013-09-06 03:21:34 +00:00
btl_usnic_hwloc.h First commit of the Cisco usNIC BTL. 2013-07-19 22:13:58 +00:00
btl_usnic_libnl1_utils.c usnic: properly check src iface in route queries 2014-06-25 22:39:02 +00:00
btl_usnic_libnl3_utils.c usnic: properly check src iface in route queries 2014-06-25 22:39:02 +00:00
btl_usnic_libnl_utils.h usnic: properly check src iface in route queries 2014-06-25 22:39:02 +00:00
btl_usnic_map.c btl_usnic_map.c: Arrgh! Forgot to svn add this file. 2014-07-08 20:09:31 +00:00
btl_usnic_mca.c usnic: add btl_usnic_connectivity_map MCA param to output link information 2014-07-08 19:14:46 +00:00
btl_usnic_module.c Per RFC: Remove des_src and des_dst members from the 2014-07-10 16:31:15 +00:00
btl_usnic_module.h usnic: improve interface matching (esp. for UDP) 2014-02-26 07:50:26 +00:00
btl_usnic_proc.c usnic: properly check src iface in route queries 2014-06-25 22:39:02 +00:00
btl_usnic_proc.h usnic: improve interface matching (esp. for UDP) 2014-02-26 07:50:26 +00:00
btl_usnic_recv.c Per RFC: Remove des_src and des_dst members from the 2014-07-10 16:31:15 +00:00
btl_usnic_recv.h usnic: check packet/segment lengths 2014-02-26 07:47:19 +00:00
btl_usnic_send.c Per RFC: Remove des_src and des_dst members from the 2014-07-10 16:31:15 +00:00
btl_usnic_send.h usnic: Shrink sequence numbers to 16 bits 2014-02-26 07:40:10 +00:00
btl_usnic_stats.c Refs trac:3091 2013-11-12 23:01:04 +00:00
btl_usnic_stats.h Move all usNIC stats to _stats.c|h and export them as MPI_T pvars. 2013-10-28 22:23:08 +00:00
btl_usnic_test.c usnic: add simple unit testing infrastructure 2014-02-26 07:47:50 +00:00
btl_usnic_test.h usnic: add simple unit testing infrastructure 2014-02-26 07:47:50 +00:00
btl_usnic_util.c usnic: use the correct output stream name 2014-06-24 18:13:49 +00:00
btl_usnic_util.h usnic: move some defines into btl_usnic_util.h 2014-06-18 15:19:36 +00:00
btl_usnic.h usnic: add btl_usnic_connectivity_map MCA param to output link information 2014-07-08 19:14:46 +00:00
configure.m4 usnic: properly check src iface in route queries 2014-06-25 22:39:02 +00:00
help-mpi-btl-usnic.txt usnic: make this show_help message like the others 2014-07-03 17:17:34 +00:00
Makefile.am usnic: add btl_usnic_connectivity_map MCA param to output link information 2014-07-08 19:14:46 +00:00
README.test usnic: add simple unit testing infrastructure 2014-02-26 07:47:50 +00:00
README.txt Remove all of our "to-do" items from the README.txt. 2013-10-11 16:43:56 +00:00

Design notes on usnic BTL

======================================
nomenclature

fragment - something the PML asks us to send or put, any size
segment - something we can put on the wire in a single packet
chunk - a piece of a fragment that fits into one segment

a segment can contain either an entire fragment or a chunk of a fragment

each segment and fragment has associated descriptor.

Each segment data structure has a block of registered memory associated with
it which matches MTU for that segment
ACK - acks get special small segments with only enough memory for an ACK
non-ACK segments always have a parent fragment

fragments are either large (> MTU) or small (<= MTU)
a small fragment has a segment descriptor embedded within it since it
always needs exactly one.

a large fragment has no permanently associated segments, but allocates them
as needed.

======================================
channels

a channel is a queue pair with an associated completion queue
each channel has its own MTU and r/w queue entry counts

There are 2 channels, command and data
command queue is generally for higher priority fragments
data queue is for standard data traffic
command queue should possibly be called "priority" queue

command queue is shorter and has a smaller MTU that the data queue
this makes the command queue a lot faster than the data queue, so we 
hijack it for sending very small fragments (<= tiny_mtu, currently 768 bytes)

command queue is used for ACKs and tiny fragments
data queue is used for everything else

PML fragments marked priority should perhaps use command queue

======================================
sending

Normally, all send requests are simply enqueued and then actually posted
to the NIC by the routine ompi_btl_usnic_module_progress_sends().  
"fastpath" tiny sends are the exception.

Each module maintains a queue of endpoints that are ready to send.
An endpoint is ready to send if all of the following are met:
- the endpoint has fragments to send
- the endpoint has send credits
- the endpoint's send window is "open" (not full of un-ACKed segments)

Each module also maintains a list of segments that need to be retransmitted.
Note that the list of pending retrans is per-module, not per-endpoint.

send progression first posts any pending retransmissions, always using the 
data channel.  (reason is that if we start getting heavy congestion and
there are lots of retransmits, it becomes more important than ever to 
prioritize ACKs, clogging command channel with retrans data makes things worse,
not better)

Next, progression loops sending segments to the endpoint at the top of
the "endpoints_with_sends" queue.  When an endpoint exhausts its send 
credits or fills its send window or runs out of segments to send, it removes
itself from the endpoint_with_sends list.  Any pending ACKs will be
picked up and piggy-backed on these sends.

Finally, any endpoints that still need ACKs whose timer has expired will
be sent explicit ACK packets.

[double-click fragment sending]
The middle part of the progression loop handles both small (single-segment)
and large (multi-segment) sends.

For small fragments, the verbs descriptor within the embedded segment is
updated with length, BTL header is updated, then we call 
ompi_btl_usnic_endpoint_send_segment() to send the segment.
After posting, we make a PML callback if needed.

For large fragments, a little more is needed.  segments froma large 
fragment have a slightly larger BTL header which contains a fragment ID,
and offset, and a size.  The fragment ID is allocated when the first chunk
the fragment is sent.  A segment gets allocated, next blob of data is
copied into this segment, segment is posted.  If last chunk of fragment
sent, perform callback if needed, then remove fragment from endpoint
send queue.

[double-click ompi_btl_usnic_endpoint_send_segment()]

This is common posting code for large or small segments.  It assigns a 
sequence number to a segment, checks for an ACK to piggy-back,
posts the segment to the NIC, and then starts the retransmit timer
by checking the segment into hotel.  Send credits are consumed here.


======================================
send dataflow

PML control messages with no user data are sent via:
desc = usnic_alloc(size)
usnic_send(desc)

user messages less than eager limit and 1st part of larger
messages are sent via:
desc = usnic_prepare_src(convertor, size)
usnic_send(desc)

larger msgs
desc = usnic_prepare_src(convertor, size)
usnic_put(desc)


usnic_alloc() currently asserts the length is "small", allocates and
fills in a small fragment.  src pointer will point to start of 
associated registered mem + sizeof BTL header, and PML will put its
data there.

usnic_prepare_src() allocated either a large or small fragment based on size
The fragment descriptor is filled in to have 2 SG entries, 1st pointing to
place where PML should construct its header.  If the data convertor says
data is contiguous, 2nd SG entry points to user buffer, else it is null and
sf_convertor is filled in with address of convertor.

usnic_send()
If the fragment being sent is small enough, has contiguous data, and
"very few" command queue send WQEs have been consumed, usnic_send() does
a fastpath send.  This means it posts the segment immediately to the NIC
with INLINE flag set.

If all of the conditions for fastpath send are not met, and this is a small
fragment, the user data is copied into the associated registered memory at this
time and the SG list in the descriptor is collapsed to one entry.

After the checks above are done, the fragment is enqueued to be sent
via ompi_btl_usnic_endpoint_enqueue_frag() 

usnic_put()
PML will have filled in destination address in descriptor.  This is saved
and the fragment is enqueued for processing.


ompi_btl_usnic_endpoint_enqueue_frag()
This appends the fragment to the "to be sent" list of the endpoint and
conditionally adds the endpoint to the list of endpoints with data to send
via ompi_btl_usnic_check_rts()

======================================
receive dataflow

BTL packets has one of 3 types in header: frag, chunk, or ack.

A frag packet is a full PML fragment.
A chunk packet is a piece of a fragment that needs to be reassembled.
An ack packet is header only with a sequence number being ACKed.

Both frag and chunk packets go through some of the same processing.
Both may carry piggy-backed ACKs which may need to be processed.
Both have sequence numbers which must be processed and may result in 
dropping the packet and/or queueing an ACK to the sender.

frag packets may be either regular PML fragments or PUT segments.
If the "put_addr" field of the BTL header is set, this is a PUT and
the data is copied directly to the user buffer.  If this field is NULL,
the segment is passed up to the PML.  The PML is expected to do everything
it needs with this packet in the callback, including copying data out if
needed.  Once the callback is complete, the receive buffer is recycled.

chunk packets are parts of a larger fragment.  If an active fragment receive
for the matching fragment ID cannot be found, and new fragment info
descriptor is allocated.  If this is not a PUT (put_addr == NULL), we 
malloc() data to reassemble the fragment into.  Each subsequent chunk
is copied either into this reassembly buffer or directly into user memory.
When the last chunk of a fragment arrives, a PML callback is made for non-PUTs,
then the fragment info descriptor is released.

======================================
fast receive optimization

In order to optimize latency of small packets, the component progress routine
implements a fast path for receives.  If the first completion is a receive on
the priority queue, then it is handled by a routine called
ompi_btl_usnic_recv_fast() which does nothing but validates that the packet
is OK to be received (sequence number OK and not a DUP) and then delivers it
to the PML.  This packet is recorded in the channel structure, and all
bookeeping for the packet is deferred until the next time component_progress
is called again.

This fast path cannot be taken every time we pass through component_progress
because there will be other completions that need processing, and the receive
bookeeping for one fast receive must be complete before allowing another fast
receive to occur, as only one recv segment can be saved for deferred
processing at a time.  This is handled by maintaining a variable in
ompi_btl_usnic_recv_fast() called fastpath_ok which is set to false every time
the fastpath is taken.  A call into the regular progress routine will set this
flag back to true.



======================================
reliability:

every packet has sequence #
each endpoint has a "send window" , currently 4096 entries.
once a segment is sent, it is saved in window array until ACK is received
ACKs acknowledge all packets <= specified sequence #
rcvr only ACKs a sequence # when all packets up to that sequence have arrived

each pkt has dflt retrans timer of 100ms
packet will be scheduled for retrans if timer expires

Once a segment is sent, it always has its retransmit timer started.
This is accomplished by opal_hotel_checkin()
Any time a segment is posted to the NIC for retransmit, it is checked out
of the hotel (timer stopped).
So, a send segment is always in one of 4 states:
- on free list, unallocated
- on endpoint to-send list in the case of segment associated with small fragment
- posted to NIC and in hotel awaiting ACK
- on module re-send list awaiting retransmission

rcvr:
- if a pkt with seq >= expected seq is received, schedule ack of largest
  in-order sequence received if not already scheduled.  dflt time is 50us
- if a packet with seq < expected seq arrives, we send an ACK immediately,
  as this indicates a lost ACK

sender:
duplicate ACK triggers immediate retrans if one is not pending for that segment


======================================
Reordering induced by two queues and piggy-backing:

ACKs can be reordered-
  not an issue at all, old ACKs are simply ignored

Sends can be reordered-
(small send can jump far ahead of large sends)
large send followed by lots of small sends could trigger many retrans
of the large sends.  smalls would have to be paced pretty precisely to
keep command queue empty enough and also beat out the large sends.
send credits limit how many larges can be queued on the sender, but there
could be many on the receiver