diff options
author | Michael West <michael.west@ettus.com> | 2017-11-16 11:59:45 -0800 |
---|---|---|
committer | Martin Braun <martin.braun@ettus.com> | 2018-07-25 15:34:03 -0700 |
commit | cb9c97d643ac51279e61439c4e7caae9b1212c7d (patch) | |
tree | c6f7ff98dae9b979a75a924aab3c843963d7fd0c /host/lib/usrp/x300/x300_impl.cpp | |
parent | 7ed7b207735fee5f7bd055472e591935b5f96cf5 (diff) | |
download | uhd-cb9c97d643ac51279e61439c4e7caae9b1212c7d.tar.gz uhd-cb9c97d643ac51279e61439c4e7caae9b1212c7d.tar.bz2 uhd-cb9c97d643ac51279e61439c4e7caae9b1212c7d.zip |
X300: Change Ethernet buffering
Ethernet buffering is now done so that most of the buffering is done in
the socket buffers and multiple frames are only used to support the
receive side offload of the socket I/O. Eliminates dropped packets at
high full duplex rates.
Diffstat (limited to 'host/lib/usrp/x300/x300_impl.cpp')
-rw-r--r-- | host/lib/usrp/x300/x300_impl.cpp | 347 |
1 files changed, 177 insertions, 170 deletions
diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp index 60be75f2b..d0915d592 100644 --- a/host/lib/usrp/x300/x300_impl.cpp +++ b/host/lib/usrp/x300/x300_impl.cpp @@ -473,8 +473,10 @@ void x300_impl::mboard_members_t::discover_eth( // Choose the interface based on the index parity if (i % 2 == 0) { conn_iface.type = X300_IFACE_ETH0; + conn_iface.link_rate = loaded_fpga_image == "HG" ? X300_MAX_RATE_1GIGE : X300_MAX_RATE_10GIGE; } else { conn_iface.type = X300_IFACE_ETH1; + conn_iface.link_rate = X300_MAX_RATE_10GIGE; } break; } @@ -492,15 +494,19 @@ void x300_impl::mboard_members_t::discover_eth( if (addr == boost::asio::ip::address_v4( uint32_t(X300_DEFAULT_IP_ETH0_1G)).to_string()) { conn_iface.type = X300_IFACE_ETH0; + conn_iface.link_rate = X300_MAX_RATE_1GIGE; } else if (addr == boost::asio::ip::address_v4( uint32_t(X300_DEFAULT_IP_ETH1_1G)).to_string()) { conn_iface.type = X300_IFACE_ETH1; + conn_iface.link_rate = X300_MAX_RATE_1GIGE; } else if (addr == boost::asio::ip::address_v4( uint32_t(X300_DEFAULT_IP_ETH0_10G)).to_string()) { conn_iface.type = X300_IFACE_ETH0; + conn_iface.link_rate = X300_MAX_RATE_10GIGE; } else if (addr == boost::asio::ip::address_v4( uint32_t(X300_DEFAULT_IP_ETH1_10G)).to_string()) { conn_iface.type = X300_IFACE_ETH1; + conn_iface.link_rate = X300_MAX_RATE_10GIGE; } else { throw uhd::assertion_error(str(boost::format( "X300 Initialization Error: Failed to match address %s with " @@ -624,91 +630,6 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) if (key.find("send") != std::string::npos) mb.send_args[key] = dev_addr[key]; } - if (mb.xport_path == "eth" ) { - /* This is an ETH connection. Figure out what the maximum supported frame - * size is for the transport in the up and down directions. The frame size - * depends on the host PIC's NIC's MTU settings. To determine the frame size, - * we test for support up to an expected "ceiling". If the user - * specified a frame size, we use that frame size as the ceiling. If no - * frame size was specified, we use the maximum UHD frame size. - * - * To optimize performance, the frame size should be greater than or equal - * to the frame size that UHD uses so that frames don't get split across - * multiple transmission units - this is why the limits passed into the - * 'determine_max_frame_size' function are actually frame sizes. */ - frame_size_t req_max_frame_size; - req_max_frame_size.recv_frame_size = (mb.recv_args.has_key("recv_frame_size")) \ - ? boost::lexical_cast<size_t>(mb.recv_args["recv_frame_size"]) \ - : X300_10GE_DATA_FRAME_MAX_SIZE; - req_max_frame_size.send_frame_size = (mb.send_args.has_key("send_frame_size")) \ - ? boost::lexical_cast<size_t>(mb.send_args["send_frame_size"]) \ - : X300_10GE_DATA_FRAME_MAX_SIZE; - - #if defined UHD_PLATFORM_LINUX - const std::string mtu_tool("ip link"); - #elif defined UHD_PLATFORM_WIN32 - const std::string mtu_tool("netsh"); - #else - const std::string mtu_tool("ifconfig"); - #endif - - // Detect the frame size on the path to the USRP - try { - frame_size_t pri_frame_sizes = determine_max_frame_size( - eth_addrs.at(0), req_max_frame_size - ); - - _max_frame_sizes = pri_frame_sizes; - if (eth_addrs.size() > 1) { - frame_size_t sec_frame_sizes = determine_max_frame_size( - eth_addrs.at(1), req_max_frame_size - ); - - // Choose the minimum of the max frame sizes - // to ensure we don't exceed any one of the links' MTU - _max_frame_sizes.recv_frame_size = std::min( - pri_frame_sizes.recv_frame_size, - sec_frame_sizes.recv_frame_size - ); - - _max_frame_sizes.send_frame_size = std::min( - pri_frame_sizes.send_frame_size, - sec_frame_sizes.send_frame_size - ); - } - } catch(std::exception &e) { - UHD_LOGGER_ERROR("X300") << e.what() ; - } - - if ((mb.recv_args.has_key("recv_frame_size")) - && (req_max_frame_size.recv_frame_size > _max_frame_sizes.recv_frame_size)) { - UHD_LOGGER_WARNING("X300") - << boost::format("You requested a receive frame size of (%lu) but your NIC's max frame size is (%lu).") - % req_max_frame_size.recv_frame_size - % _max_frame_sizes.recv_frame_size - << boost::format("Please verify your NIC's MTU setting using '%s' or set the recv_frame_size argument appropriately.") - % mtu_tool - << "UHD will use the auto-detected max frame size for this connection." - ; - } - - if ((mb.recv_args.has_key("send_frame_size")) - && (req_max_frame_size.send_frame_size > _max_frame_sizes.send_frame_size)) { - UHD_LOGGER_WARNING("X300") - << boost::format("You requested a send frame size of (%lu) but your NIC's max frame size is (%lu).") - % req_max_frame_size.send_frame_size - % _max_frame_sizes.send_frame_size - << boost::format("Please verify your NIC's MTU setting using '%s' or set the send_frame_size argument appropriately.") - % mtu_tool - << "UHD will use the auto-detected max frame size for this connection." - ; - } - - _tree->create<size_t>(mb_path / "mtu/recv").set(_max_frame_sizes.recv_frame_size); - _tree->create<size_t>(mb_path / "mtu/send").set(_max_frame_sizes.send_frame_size); - _tree->create<double>(mb_path / "link_max_rate").set(X300_MAX_RATE_10GIGE); - } - //create basic communication UHD_LOGGER_DEBUG("X300") << "Setting up basic communication..."; if (mb.xport_path == "nirio") { @@ -828,11 +749,129 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) _tree->create<std::string>(mb_path / "codename").set("Yetti"); //////////////////////////////////////////////////////////////////// - // determine routing based on address match + // discover ethernet interfaces, frame sizes, and link rates //////////////////////////////////////////////////////////////////// - if (mb.xport_path != "nirio") { + if (mb.xport_path == "eth" ) { + double link_max_rate = 0.0; + // Discover ethernet interfaces mb.discover_eth(mb_eeprom, eth_addrs); + + /* This is an ETH connection. Figure out what the maximum supported frame + * size is for the transport in the up and down directions. The frame size + * depends on the host PC's NIC's MTU settings. To determine the frame size, + * we test for support up to an expected "ceiling". If the user + * specified a frame size, we use that frame size as the ceiling. If no + * frame size was specified, we use the maximum UHD frame size. + * + * To optimize performance, the frame size should be greater than or equal + * to the frame size that UHD uses so that frames don't get split across + * multiple transmission units - this is why the limits passed into the + * 'determine_max_frame_size' function are actually frame sizes. */ + frame_size_t req_max_frame_size; + req_max_frame_size.recv_frame_size = (mb.recv_args.has_key("recv_frame_size")) \ + ? boost::lexical_cast<size_t>(mb.recv_args["recv_frame_size"]) \ + : X300_DATA_FRAME_MAX_SIZE; + req_max_frame_size.send_frame_size = (mb.send_args.has_key("send_frame_size")) \ + ? boost::lexical_cast<size_t>(mb.send_args["send_frame_size"]) \ + : X300_DATA_FRAME_MAX_SIZE; + + #if defined UHD_PLATFORM_LINUX + const std::string mtu_tool("ip link"); + #elif defined UHD_PLATFORM_WIN32 + const std::string mtu_tool("netsh"); + #else + const std::string mtu_tool("ifconfig"); + #endif + + // Detect the frame size on the path to the USRP + try { + frame_size_t pri_frame_sizes = determine_max_frame_size( + eth_addrs.at(0), req_max_frame_size + ); + + _max_frame_sizes = pri_frame_sizes; + if (eth_addrs.size() > 1) { + frame_size_t sec_frame_sizes = determine_max_frame_size( + eth_addrs.at(1), req_max_frame_size + ); + + // Choose the minimum of the max frame sizes + // to ensure we don't exceed any one of the links' MTU + _max_frame_sizes.recv_frame_size = std::min( + pri_frame_sizes.recv_frame_size, + sec_frame_sizes.recv_frame_size + ); + + _max_frame_sizes.send_frame_size = std::min( + pri_frame_sizes.send_frame_size, + sec_frame_sizes.send_frame_size + ); + } + } catch(std::exception &e) { + UHD_LOGGER_ERROR("X300") << e.what() ; + } + + if ((mb.recv_args.has_key("recv_frame_size")) + && (req_max_frame_size.recv_frame_size > _max_frame_sizes.recv_frame_size)) { + UHD_LOGGER_WARNING("X300") + << boost::format("You requested a receive frame size of (%lu) but your NIC's max frame size is (%lu).") + % req_max_frame_size.recv_frame_size + % _max_frame_sizes.recv_frame_size + + << boost::format("Please verify your NIC's MTU setting using '%s' or set the recv_frame_size argument appropriately.") + % mtu_tool + << "UHD will use the auto-detected max frame size for this connection." + ; + } + + if ((mb.send_args.has_key("send_frame_size")) + && (req_max_frame_size.send_frame_size > _max_frame_sizes.send_frame_size)) { + UHD_LOGGER_WARNING("X300") + << boost::format("You requested a send frame size of (%lu) but your NIC's max frame size is (%lu).") + % req_max_frame_size.send_frame_size + % _max_frame_sizes.send_frame_size + + << boost::format("Please verify your NIC's MTU setting using '%s' or set the send_frame_size argument appropriately.") + % mtu_tool + << "UHD will use the auto-detected max frame size for this connection." + ; + } + + // Check frame sizes + for (auto conn : mb.eth_conns) + { + link_max_rate += conn.link_rate; + + size_t rec_send_frame_size = conn.link_rate == X300_MAX_RATE_1GIGE ? X300_1GE_DATA_FRAME_SEND_SIZE : X300_10GE_DATA_FRAME_SEND_SIZE; + size_t rec_recv_frame_size = conn.link_rate == X300_MAX_RATE_1GIGE ? X300_1GE_DATA_FRAME_RECV_SIZE : X300_10GE_DATA_FRAME_RECV_SIZE; + + if (_max_frame_sizes.send_frame_size < rec_send_frame_size) + { + UHD_LOGGER_WARNING("X300") + << boost::format("For the %s connection, UHD recommends a send frame size of at least %lu for best\nperformance, but your configuration will only allow %lu.") + % conn.addr + % rec_send_frame_size + % _max_frame_sizes.send_frame_size + << "This may negatively impact your maximum achievable sample rate.\nCheck the MTU on the interface and/or the send_frame_size argument." + ; + } + + if (_max_frame_sizes.recv_frame_size < rec_recv_frame_size) + { + UHD_LOGGER_WARNING("X300") + << boost::format("For the %s connection, UHD recommends a receive frame size of at least %lu for best\nperformance, but your configuration will only allow %lu.") + % conn.addr + % rec_recv_frame_size + % _max_frame_sizes.recv_frame_size + << "This may negatively impact your maximum achievable sample rate.\nCheck the MTU on the interface and/or the recv_frame_size argument." + ; + } + } + + _tree->create<size_t>(mb_path / "mtu/recv").set(_max_frame_sizes.recv_frame_size); + _tree->create<size_t>(mb_path / "mtu/send").set(_max_frame_sizes.send_frame_size); + _tree->create<double>(mb_path / "link_max_rate").set(link_max_rate); } //////////////////////////////////////////////////////////////////// @@ -1264,97 +1303,66 @@ uhd::both_xports_t x300_impl::make_transport( xport_type == TX_DATA ? mb.next_tx_src_addr : xport_type == RX_DATA ? mb.next_rx_src_addr : mb.next_src_addr; - std::string interface_addr = mb.eth_conns[next_src_addr].addr; + x300_eth_conn_t conn = mb.eth_conns[next_src_addr]; const uint32_t xbar_src_addr = next_src_addr==0 ? X300_SRC_ADDR0 : X300_SRC_ADDR1; const uint32_t xbar_src_dst = - mb.eth_conns[next_src_addr].type==X300_IFACE_ETH0 ? X300_XB_DST_E0 : X300_XB_DST_E1; - next_src_addr = (next_src_addr + 1) % mb.eth_conns.size(); + conn.type==X300_IFACE_ETH0 ? X300_XB_DST_E0 : X300_XB_DST_E1; + if (xport_type != TX_DATA) next_src_addr = (next_src_addr + 1) % mb.eth_conns.size(); xports.send_sid = this->allocate_sid(mb, address, xbar_src_addr, xbar_src_dst); xports.recv_sid = xports.send_sid.reversed(); - /* Determine what the recommended frame size is for this - * connection type.*/ - size_t eth_data_rec_frame_size = 0; - - fs_path mboard_path = fs_path("/mboards") / mb_index / "link_max_rate"; - - if (mb.loaded_fpga_image == "HG") { - size_t max_link_rate = 0; - if (xbar_src_dst == X300_XB_DST_E0) { - eth_data_rec_frame_size = X300_1GE_DATA_FRAME_MAX_SIZE; - max_link_rate += X300_MAX_RATE_1GIGE; - } else if (xbar_src_dst == X300_XB_DST_E1) { - eth_data_rec_frame_size = X300_10GE_DATA_FRAME_MAX_SIZE; - max_link_rate += X300_MAX_RATE_10GIGE; + // Set size and number of frames + size_t system_max_send_frame_size = (size_t) _max_frame_sizes.send_frame_size; + size_t system_max_recv_frame_size = (size_t) _max_frame_sizes.recv_frame_size; + default_buff_args.send_frame_size = std::min(system_max_send_frame_size, X300_ETH_MSG_FRAME_SIZE); + default_buff_args.recv_frame_size = std::min(system_max_recv_frame_size, X300_ETH_MSG_FRAME_SIZE); + default_buff_args.num_send_frames = 1; // never need multiple frames on send + default_buff_args.num_recv_frames = 1; // only need multiple frames with offload thread + default_buff_args.send_buff_size = conn.link_rate / 50; // 20ms + default_buff_args.recv_buff_size = std::max(conn.link_rate / 50, X300_ETH_MSG_NUM_FRAMES * X300_ETH_MSG_FRAME_SIZE); // enough to hold greater of 20ms or number of msg frames + if (xport_type == TX_DATA) + { + size_t default_frame_size = conn.link_rate == X300_MAX_RATE_1GIGE ? X300_1GE_DATA_FRAME_SEND_SIZE : X300_10GE_DATA_FRAME_SEND_SIZE; + default_buff_args.send_frame_size = args.cast<size_t>("send_frame_size", std::min(default_frame_size, system_max_send_frame_size)); + if (default_buff_args.send_frame_size > system_max_send_frame_size) + { + UHD_LOGGER_WARNING("X300") + << boost::format("Requested send_frame_size of %d exceeds the maximum allowed on the %s connection. Using %d.") + % default_buff_args.send_frame_size + % conn.addr + % system_max_send_frame_size + ; + default_buff_args.send_frame_size = system_max_send_frame_size; } - _tree->access<double>(mboard_path).set(max_link_rate); - } else if (mb.loaded_fpga_image == "XG" or mb.loaded_fpga_image == "XA") { - eth_data_rec_frame_size = X300_10GE_DATA_FRAME_MAX_SIZE; - size_t max_link_rate = X300_MAX_RATE_10GIGE; - max_link_rate *= mb.eth_conns.size(); - _tree->access<double>(mboard_path).set(max_link_rate); - } else if (mb.loaded_fpga_image == "HA") { - eth_data_rec_frame_size = X300_1GE_DATA_FRAME_MAX_SIZE; - size_t max_link_rate = X300_MAX_RATE_1GIGE; - max_link_rate *= mb.eth_conns.size(); - _tree->access<double>(mboard_path).set(max_link_rate); - } - - if (eth_data_rec_frame_size == 0) { - throw uhd::runtime_error("Unable to determine ETH link type."); } + else if (xport_type == RX_DATA) + { + size_t default_frame_size = conn.link_rate == X300_MAX_RATE_1GIGE ? X300_1GE_DATA_FRAME_RECV_SIZE : X300_10GE_DATA_FRAME_RECV_SIZE; + default_buff_args.recv_frame_size = args.cast<size_t>("recv_frame_size", std::min(default_frame_size, system_max_recv_frame_size)); + if (default_buff_args.recv_frame_size > system_max_recv_frame_size) + { + UHD_LOGGER_WARNING("X300") + << boost::format("Requested recv_frame_size of %d exceeds the maximum allowed on the %s connection. Using %d.") + % default_buff_args.recv_frame_size + % conn.addr + % system_max_recv_frame_size + ; + default_buff_args.recv_frame_size = system_max_recv_frame_size; + } + // set default buffering for data + default_buff_args.recv_buff_size = conn.link_rate / 10; // 100ms + default_buff_args.num_recv_frames = 2; // set some buffers so the offload thread actually offloads the socket I/O - /* Print a warning if the system's max available frame size is less than the most optimal - * frame size for this type of connection. */ - if (_max_frame_sizes.send_frame_size < eth_data_rec_frame_size) { - UHD_LOGGER_WARNING("X300") - << boost::format("For this connection, UHD recommends a send frame size of at least %lu for best\nperformance, but your system's MTU will only allow %lu.") - % eth_data_rec_frame_size - % _max_frame_sizes.send_frame_size - << "This may negatively impact your maximum achievable sample rate." - ; + // set buffering for flow control messages + default_buff_args.num_send_frames = 1; } - if (_max_frame_sizes.recv_frame_size < eth_data_rec_frame_size) { - UHD_LOGGER_WARNING("X300") - << boost::format("For this connection, UHD recommends a receive frame size of at least %lu for best\nperformance, but your system's MTU will only allow %lu.") - % eth_data_rec_frame_size - % _max_frame_sizes.recv_frame_size - << "This may negatively impact your maximum achievable sample rate." - ; - } - - size_t system_max_send_frame_size = (size_t) _max_frame_sizes.send_frame_size; - size_t system_max_recv_frame_size = (size_t) _max_frame_sizes.recv_frame_size; - - // Make sure frame sizes do not exceed the max available value supported by UHD - default_buff_args.send_frame_size = - (xport_type == TX_DATA) - ? std::min(system_max_send_frame_size, X300_10GE_DATA_FRAME_MAX_SIZE) - : std::min(system_max_send_frame_size, X300_ETH_MSG_FRAME_SIZE); - - default_buff_args.recv_frame_size = - (xport_type == RX_DATA) - ? std::min(system_max_recv_frame_size, X300_10GE_DATA_FRAME_MAX_SIZE) - : std::min(system_max_recv_frame_size, X300_ETH_MSG_FRAME_SIZE); - - default_buff_args.num_send_frames = - (xport_type == TX_DATA) - ? X300_ETH_DATA_NUM_FRAMES - : X300_ETH_MSG_NUM_FRAMES; - - default_buff_args.num_recv_frames = - (xport_type == RX_DATA) - ? X300_ETH_DATA_NUM_FRAMES - : X300_ETH_MSG_NUM_FRAMES; - //make a new transport - fpga has no idea how to talk to us on this yet udp_zero_copy::buff_params buff_params; - xports.recv = udp_zero_copy::make( - interface_addr, + conn.addr, BOOST_STRINGIZE(X300_VITA_UDP_PORT), default_buff_args, buff_params, @@ -1365,12 +1373,12 @@ uhd::both_xports_t x300_impl::make_transport( if (xport_type == RX_DATA) { xports.recv = zero_copy_recv_offload::make( xports.recv, - X300_THREAD_BUFFER_TIMEOUT + X300_RECV_OFFLOAD_BUFFER_TIMEOUT ); } xports.send = xports.recv; - //For the UDP transport the buffer size if the size of the socket buffer + //For the UDP transport the buffer size is the size of the socket buffer //in the kernel xports.recv_buff_size = buff_params.recv_buff_size; xports.send_buff_size = buff_params.send_buff_size; @@ -1381,9 +1389,8 @@ uhd::both_xports_t x300_impl::make_transport( //send a mini packet with SID into the ZPU //ZPU will reprogram the ethernet framer - UHD_LOGGER_TRACE("X300") - << "programming packet for new xport on " - << interface_addr << " sid " << xports.send_sid; + UHD_LOGGER_DEBUG("X300") << "programming packet for new xport on " + << conn.addr << " sid " << xports.send_sid ; //YES, get a __send__ buffer from the __recv__ socket //-- this is the only way to program the framer for recv: managed_send_buffer::sptr buff = xports.recv->get_send_buff(); @@ -1691,9 +1698,9 @@ x300_impl::frame_size_t x300_impl::determine_max_frame_size(const std::string &a //Reducing range of (min,max) by setting max value to 10gig max_frame_size as larger sizes are not supported size_t min_recv_frame_size = sizeof(x300_mtu_t); - size_t max_recv_frame_size = std::min(user_frame_size.recv_frame_size, X300_10GE_DATA_FRAME_MAX_SIZE) & size_t(~3); + size_t max_recv_frame_size = std::min(user_frame_size.recv_frame_size, X300_DATA_FRAME_MAX_SIZE) & size_t(~3); size_t min_send_frame_size = sizeof(x300_mtu_t); - size_t max_send_frame_size = std::min(user_frame_size.send_frame_size, X300_10GE_DATA_FRAME_MAX_SIZE) & size_t(~3); + size_t max_send_frame_size = std::min(user_frame_size.send_frame_size, X300_DATA_FRAME_MAX_SIZE) & size_t(~3); UHD_LOGGER_DEBUG("X300") << "Determining maximum frame size... "; while (min_recv_frame_size < max_recv_frame_size) |