diff options
| author | Martin Braun <martin.braun@ettus.com> | 2017-12-15 19:02:45 -0800 | 
|---|---|---|
| committer | Martin Braun <martin.braun@ettus.com> | 2017-12-22 15:06:03 -0800 | 
| commit | 5adde5ab9c369e9c0389949c82804e9f23e39541 (patch) | |
| tree | 217c4333df3ffcde60eafe51c2c9d44c2a86d7a7 /host/lib/usrp | |
| parent | 6af41376c18c62ca7bfe8cccdd6564922d3fdd6a (diff) | |
| download | uhd-5adde5ab9c369e9c0389949c82804e9f23e39541.tar.gz uhd-5adde5ab9c369e9c0389949c82804e9f23e39541.tar.bz2 uhd-5adde5ab9c369e9c0389949c82804e9f23e39541.zip | |
mpmd: Add get_mtu() API and MTU discovery for UDP transports
The mpmd_xport_mgr classes can now return their own MTU. The UDP xport
manager is a special case, it doesn't actually know its MTU, and thus
runs an MTU discovery, using the MPM-ECHO command to discover MTU by
sending variable-size packets as a probing mechanism.
Reviewed-by: Trung Tran <trung.tran@ettus.com>
Diffstat (limited to 'host/lib/usrp')
| -rw-r--r-- | host/lib/usrp/mpmd/mpmd_xport_ctrl_base.hpp | 4 | ||||
| -rw-r--r-- | host/lib/usrp/mpmd/mpmd_xport_ctrl_liberio.cpp | 6 | ||||
| -rw-r--r-- | host/lib/usrp/mpmd/mpmd_xport_ctrl_liberio.hpp | 4 | ||||
| -rw-r--r-- | host/lib/usrp/mpmd/mpmd_xport_ctrl_udp.cpp | 122 | ||||
| -rw-r--r-- | host/lib/usrp/mpmd/mpmd_xport_ctrl_udp.hpp | 7 | 
5 files changed, 143 insertions, 0 deletions
| diff --git a/host/lib/usrp/mpmd/mpmd_xport_ctrl_base.hpp b/host/lib/usrp/mpmd/mpmd_xport_ctrl_base.hpp index e5a3b7e31..c3e71d495 100644 --- a/host/lib/usrp/mpmd/mpmd_xport_ctrl_base.hpp +++ b/host/lib/usrp/mpmd/mpmd_xport_ctrl_base.hpp @@ -38,6 +38,10 @@ public:      virtual bool is_valid(          const mpmd_xport_mgr::xport_info_t& xport_info      ) const = 0; + +    virtual size_t get_mtu( +        const uhd::direction_t dir +    ) const = 0;  };  }}} /* namespace uhd::mpmd::xport */ diff --git a/host/lib/usrp/mpmd/mpmd_xport_ctrl_liberio.cpp b/host/lib/usrp/mpmd/mpmd_xport_ctrl_liberio.cpp index 2e4f024b4..30fb27e04 100644 --- a/host/lib/usrp/mpmd/mpmd_xport_ctrl_liberio.cpp +++ b/host/lib/usrp/mpmd/mpmd_xport_ctrl_liberio.cpp @@ -129,6 +129,12 @@ bool mpmd_xport_ctrl_liberio::is_valid(      return xport_info.at("type") == "liberio";  } +size_t mpmd_xport_ctrl_liberio::get_mtu( +    const uhd::direction_t /* dir */ +) const { +    return 8000; +} +  uhd::transport::muxed_zero_copy_if::sptr  mpmd_xport_ctrl_liberio::make_muxed_liberio_xport(          const std::string &tx_dev, diff --git a/host/lib/usrp/mpmd/mpmd_xport_ctrl_liberio.hpp b/host/lib/usrp/mpmd/mpmd_xport_ctrl_liberio.hpp index 76bb88f03..52fc6ae48 100644 --- a/host/lib/usrp/mpmd/mpmd_xport_ctrl_liberio.hpp +++ b/host/lib/usrp/mpmd/mpmd_xport_ctrl_liberio.hpp @@ -35,6 +35,10 @@ public:          const mpmd_xport_mgr::xport_info_t& xport_info      ) const; +    size_t get_mtu( +        const uhd::direction_t dir +    ) const ; +  private:      /*! Create a muxed liberio transport for control packets */      uhd::transport::muxed_zero_copy_if::sptr make_muxed_liberio_xport( diff --git a/host/lib/usrp/mpmd/mpmd_xport_ctrl_udp.cpp b/host/lib/usrp/mpmd/mpmd_xport_ctrl_udp.cpp index 0da080724..d900467d3 100644 --- a/host/lib/usrp/mpmd/mpmd_xport_ctrl_udp.cpp +++ b/host/lib/usrp/mpmd/mpmd_xport_ctrl_udp.cpp @@ -4,9 +4,13 @@  // SPDX-License-Identifier: GPL-3.0  // +#include "mpmd_impl.hpp"  #include "mpmd_xport_mgr.hpp"  #include "mpmd_xport_ctrl_udp.hpp"  #include <uhd/transport/udp_zero_copy.hpp> +#include <uhd/transport/udp_simple.hpp> +#include <uhd/transport/udp_constants.hpp> +  using namespace uhd;  using namespace uhd::mpmd::xport; @@ -22,6 +26,8 @@ namespace {      const size_t MPMD_10GE_DATA_FRAME_MAX_SIZE = 8000; // CHDR packet size in bytes +    const double MPMD_MTU_DISCOVERY_TIMEOUT = 0.02; // seconds +      std::vector<std::string> get_addrs_from_mb_args(          const uhd::device_addr_t& mb_args      ) { @@ -36,6 +42,104 @@ namespace {          }          return addrs;      } + +    /*! Do a binary search to discover MTU +     * +     * Uses the MPM echo service to figure out MTU. We simply send a bunch of +     * packets and see if they come back until we converged on the path MTU. +     * The end result must lie between \p min_frame_size and \p max_frame_size. +     * +     * \param address IP address +     * \param port UDP port (yeah it's a string!) +     * \param min_frame_size Minimum frame size, initialize algorithm to start +     *                       with this value +     * \param max_frame_size Maximum frame size, initialize algorithm to start +     *                       with this value +     * \param echo_timeout Timeout value in seconds. For frame sizes that +     *                     exceed the MTU, we don't expect a response, and this +     *                     is the amount of time we'll wait before we assume +     *                     the frame size exceeds the MTU. +     */ +    size_t discover_mtu( +            const std::string &address, +            const std::string &port, +            size_t min_frame_size, +            size_t max_frame_size, +            const double echo_timeout = 0.020 +    ) { +        const size_t echo_prefix_offset = +            uhd::mpmd::mpmd_impl::MPM_ECHO_CMD.size(); +        const size_t mtu_hdr_len = echo_prefix_offset + 10; +        UHD_ASSERT_THROW(min_frame_size < max_frame_size); +        UHD_ASSERT_THROW(min_frame_size % 4 == 0); +        UHD_ASSERT_THROW(max_frame_size % 4 == 0); +        UHD_ASSERT_THROW(min_frame_size >= echo_prefix_offset + mtu_hdr_len); +        using namespace uhd::transport; +        // The return port will probably differ from the discovery port, so we +        // need a "broadcast" UDP connection; using make_connected() would +        // drop packets +        udp_simple::sptr udp = udp_simple::make_broadcast(address, port); +        std::string send_buf(uhd::mpmd::mpmd_impl::MPM_ECHO_CMD); +        send_buf.resize(max_frame_size, '#'); +        UHD_ASSERT_THROW(send_buf.size() == max_frame_size); +        std::vector<uint8_t> recv_buf; +        recv_buf.resize(max_frame_size, ' '); + +        // Little helper to check returned packets match the sent ones +        auto require_bufs_match = [&recv_buf, &send_buf, mtu_hdr_len]( +                const size_t len +            ){ +                if (len < mtu_hdr_len or std::memcmp( +                        (void *) &recv_buf[0], +                        (void *) &send_buf[0], +                        mtu_hdr_len +                ) != 0) { +                    throw uhd::runtime_error("Unexpected content of MTU " +                                             "discovery return packet!"); +                } +            }; +        UHD_LOG_TRACE("MPMD", "Determining UDP MTU... "); +        size_t seq_no = 0; +        while (min_frame_size < max_frame_size) { +            // Only test multiples of 4 bytes! +            const size_t test_frame_size = +                (max_frame_size/2 + min_frame_size/2 + 3) & ~size_t(3); +            // Encode sequence number and current size in the string, makes it +            // easy to debug in code or Wireshark. Is also used for identifying +            // response packets. +            std::sprintf( +                &send_buf[echo_prefix_offset], +                ";%04lu,%04lu", +                seq_no++, +                test_frame_size +            ); +            UHD_LOG_TRACE("MPMD", "Testing frame size " << test_frame_size); +            udp->send(boost::asio::buffer(&send_buf[0], test_frame_size)); + +            const size_t len = +                udp->recv(boost::asio::buffer(recv_buf), echo_timeout); +            if (len == 0) { +                // Nothing received, so this is probably too big +                max_frame_size = test_frame_size - 4; +            } else if (len >= test_frame_size) { +                // Size went through, so bump the minimum +                require_bufs_match(len); +                min_frame_size = test_frame_size; +            } else if (len < test_frame_size) { +                // This is an odd case. Something must have snipped the packet +                // on the way back. Still, we'll just back off and try +                // something smaller. +                UHD_LOG_DEBUG("MPMD", +                    "Unexpected packet truncation during MTU discovery."); +                require_bufs_match(len); +                max_frame_size = len; +            } +        } +        UHD_LOG_DEBUG("MPMD", +            "Path MTU for address " << address << ": " << min_frame_size); +        return min_frame_size; +    } +  } @@ -45,7 +149,21 @@ mpmd_xport_ctrl_udp::mpmd_xport_ctrl_udp(    , _recv_args(filter_args(mb_args, "recv"))    , _send_args(filter_args(mb_args, "send"))    , _available_addrs(get_addrs_from_mb_args(mb_args)) +  , _mtu(MPMD_10GE_DATA_FRAME_MAX_SIZE)  { +    auto discover_mtu_for_ip = [](const std::string &ip_addr){ +        return discover_mtu( +            ip_addr, +            std::to_string(mpmd_impl::MPM_DISCOVERY_PORT), +            IP_PROTOCOL_MIN_MTU_SIZE-IP_PROTOCOL_UDP_PLUS_IP_HEADER, +            MPMD_10GE_DATA_FRAME_MAX_SIZE, +            MPMD_MTU_DISCOVERY_TIMEOUT +        ); +    }; + +    for (const auto &ip_addr : _available_addrs) { +        _mtu = std::min(_mtu, discover_mtu_for_ip(ip_addr)); +    }  }  uhd::both_xports_t @@ -105,3 +223,7 @@ bool mpmd_xport_ctrl_udp::is_valid(      ) != _available_addrs.cend();  } +size_t mpmd_xport_ctrl_udp::get_mtu(const uhd::direction_t /*dir*/) const +{ +    return _mtu; +} diff --git a/host/lib/usrp/mpmd/mpmd_xport_ctrl_udp.hpp b/host/lib/usrp/mpmd/mpmd_xport_ctrl_udp.hpp index 0c8c19479..fad775c39 100644 --- a/host/lib/usrp/mpmd/mpmd_xport_ctrl_udp.hpp +++ b/host/lib/usrp/mpmd/mpmd_xport_ctrl_udp.hpp @@ -34,11 +34,18 @@ public:          const mpmd_xport_mgr::xport_info_t& xport_info      ) const; +    size_t get_mtu( +        const uhd::direction_t dir +    ) const; +  private:      const uhd::device_addr_t _mb_args;      const uhd::dict<std::string, std::string> _recv_args;      const uhd::dict<std::string, std::string> _send_args; +    //! A list of IP addresses we can connect our CHDR connections to      const std::vector<std::string> _available_addrs; +    //! MTU +    size_t _mtu;  };  }}} /* namespace uhd::mpmd::xport */ | 
