diff options
Diffstat (limited to 'host/lib/transport/dpdk_simple.cpp')
-rw-r--r-- | host/lib/transport/dpdk_simple.cpp | 272 |
1 files changed, 148 insertions, 124 deletions
diff --git a/host/lib/transport/dpdk_simple.cpp b/host/lib/transport/dpdk_simple.cpp index 001775934..50855f36a 100644 --- a/host/lib/transport/dpdk_simple.cpp +++ b/host/lib/transport/dpdk_simple.cpp @@ -1,179 +1,203 @@ // -// Copyright 2019 Ettus Research, a National Instruments Company +// Copyright 2019 Ettus Research, a National Instruments Brand // // SPDX-License-Identifier: GPL-3.0-or-later // +#include <uhd/transport/frame_buff.hpp> #include <uhd/utils/log.hpp> +#include <uhdlib/transport/dpdk/udp.hpp> +#include <uhdlib/transport/dpdk_io_service.hpp> +#include <uhdlib/transport/dpdk_io_service_client.hpp> #include <uhdlib/transport/dpdk_simple.hpp> -#include <uhdlib/transport/uhd-dpdk.h> +#include <uhdlib/transport/links.hpp> +#include <uhdlib/transport/udp_dpdk_link.hpp> #include <arpa/inet.h> + namespace uhd { namespace transport { namespace { - constexpr uint64_t USEC = 1000000; - // Non-data fields are headers (Ethernet + IPv4 + UDP) + CRC - constexpr size_t DPDK_SIMPLE_NONDATA_SIZE = 14 + 20 + 8 + 4; +constexpr double SEND_TIMEOUT_MS = 500; // seconds } class dpdk_simple_impl : public dpdk_simple { public: - dpdk_simple_impl(struct uhd_dpdk_ctx &ctx, const std::string &addr, - const std::string &port, bool filter_bcast) + dpdk_simple_impl(const std::string& addr, const std::string& port) { - UHD_ASSERT_THROW(ctx.is_init_done()); - - // Get NIC that can route to addr - int port_id = ctx.get_route(addr); - UHD_ASSERT_THROW(port_id >= 0); - - _port_id = port_id; - uint32_t dst_ipv4 = (uint32_t) inet_addr(addr.c_str()); - uint16_t dst_port = htons(std::stoi(port, NULL, 0)); - - struct uhd_dpdk_sockarg_udp sockarg = { - .is_tx = false, - .filter_bcast = filter_bcast, - .local_port = 0, - .remote_port = dst_port, - .dst_addr = dst_ipv4, - .num_bufs = 1 + link_params_t link_params = _get_default_link_params(); + _link = + uhd::transport::udp_dpdk_link::make(addr, port, link_params); + UHD_LOG_TRACE("DPDK::SIMPLE", + "Creating simple UDP object for " << addr << ":" << port + << ", DPDK port index " + << _link->get_port()->get_port_id()); + // The context must be initialized, or we'd never get here + auto ctx = uhd::transport::dpdk::dpdk_ctx::get(); + UHD_ASSERT_THROW(ctx->is_init_done()); + + // Init I/O service + _port_id = _link->get_port()->get_port_id(); + _io_service = ctx->get_io_service(_port_id); + // This is normally done by the I/O service manager, but with DPDK, this + // is all it does so we skip that step + UHD_LOG_TRACE("DPDK::SIMPLE", "Attaching link to I/O service..."); + _io_service->attach_recv_link(_link); + _io_service->attach_send_link(_link); + + auto recv_cb = [this](buff_t::uptr& buff, recv_link_if*, send_link_if*) { + return this->_recv_callback(buff); + }; + + auto fc_cb = [this](buff_t::uptr buff, recv_link_if*, send_link_if*) { + this->_recv_fc_callback(std::move(buff)); }; - _rx_sock = uhd_dpdk_sock_open(_port_id, UHD_DPDK_SOCK_UDP, &sockarg); - UHD_ASSERT_THROW(_rx_sock != nullptr); - - // Backfill the local port, in case it was auto-assigned - uhd_dpdk_udp_get_info(_rx_sock, &sockarg); - sockarg.is_tx = true; - sockarg.remote_port = dst_port; - sockarg.dst_addr = dst_ipv4; - sockarg.num_bufs = 1; - _tx_sock = uhd_dpdk_sock_open(_port_id, UHD_DPDK_SOCK_UDP, &sockarg); - UHD_ASSERT_THROW(_tx_sock != nullptr); - UHD_LOG_TRACE("DPDK", "Created simple transports between " << addr << ":" - << ntohs(dst_port) << " and NIC(" << _port_id - << "):" << ntohs(sockarg.local_port)); + + _recv_io = _io_service->make_recv_client(_link, + link_params.num_recv_frames, + recv_cb, + nullptr, // No send/fc link + 0, // No send frames + fc_cb); + + auto send_cb = [this](buff_t::uptr buff, transport::send_link_if*) { + this->_send_callback(std::move(buff)); + }; + _send_io = _io_service->make_send_client(_link, + link_params.num_send_frames, + send_cb, + nullptr, // no FC link + 0, + nullptr, // No receive callback necessary + [](const size_t) { return true; } // We can always send + ); + UHD_LOG_TRACE("DPDK::SIMPLE", "Constructor complete"); } - ~dpdk_simple_impl(void) {} + ~dpdk_simple_impl(void) + { + UHD_LOG_TRACE("DPDK::SIMPLE", + "~dpdk_simple_impl(), DPDK port index " << _link->get_port()->get_port_id()); + // Disconnect the clients from the I/O service + _send_io.reset(); + _recv_io.reset(); + // Disconnect the link from the I/O service + _io_service->detach_recv_link(_link); + _io_service->detach_send_link(_link); + } - /*! - * Send and release outstanding buffer + /*! Send and release outstanding buffer * * \param length bytes of data to send * \return number of bytes sent (releases buffer if sent) */ - size_t send(const boost::asio::const_buffer& buff) + size_t send(const boost::asio::const_buffer& user_buff) { - struct rte_mbuf* tx_mbuf; - size_t frame_size = _get_tx_buf(&tx_mbuf); - UHD_ASSERT_THROW(tx_mbuf) - size_t nbytes = boost::asio::buffer_size(buff); - UHD_ASSERT_THROW(nbytes <= frame_size) - const uint8_t* user_data = boost::asio::buffer_cast<const uint8_t*>(buff); - - uint8_t* pkt_data = (uint8_t*) uhd_dpdk_buf_to_data(_tx_sock, tx_mbuf); - std::memcpy(pkt_data, user_data, nbytes); - tx_mbuf->pkt_len = nbytes; - tx_mbuf->data_len = nbytes; - - int num_tx = uhd_dpdk_send(_tx_sock, &tx_mbuf, 1); - if (num_tx == 0) - return 0; + // Extract buff and sanity check + const size_t nbytes = boost::asio::buffer_size(user_buff); + UHD_ASSERT_THROW(nbytes <= _link->get_send_frame_size()) + const uint8_t* user_data = boost::asio::buffer_cast<const uint8_t*>(user_buff); + + // Get send buff + auto buff = _send_io->get_send_buff(SEND_TIMEOUT_MS); + UHD_ASSERT_THROW(buff); + buff->set_packet_size(nbytes); + std::memcpy(buff->data(), user_data, nbytes); + + // Release send buff (send the packet) + _send_io->release_send_buff(std::move(buff)); return nbytes; } - /*! - * Receive a single packet. + /*! Receive a single packet. + * * Buffer provided by transport (must be freed before next operation). * * \param buf a pointer to place to write buffer location * \param timeout the timeout in seconds * \return the number of bytes received or zero on timeout */ - size_t recv(const boost::asio::mutable_buffer& buff, double timeout) + size_t recv(const boost::asio::mutable_buffer& user_buff, double timeout) { - struct rte_mbuf *rx_mbuf; - size_t buff_size = boost::asio::buffer_size(buff); - uint8_t* user_data = boost::asio::buffer_cast<uint8_t*>(buff); + size_t user_buff_size = boost::asio::buffer_size(user_buff); + uint8_t* user_data = boost::asio::buffer_cast<uint8_t*>(user_buff); - int bufs = uhd_dpdk_recv(_rx_sock, &rx_mbuf, 1, (int) (timeout*USEC)); - if (bufs != 1 || rx_mbuf == nullptr) { - return 0; - } - if ((rx_mbuf->ol_flags & PKT_RX_IP_CKSUM_MASK) == PKT_RX_IP_CKSUM_BAD) { - uhd_dpdk_free_buf(rx_mbuf); + auto buff = _recv_io->get_recv_buff(static_cast<int32_t>(timeout * 1e3)); + if (!buff) { return 0; } - uhd_dpdk_get_src_ipv4(_rx_sock, rx_mbuf, &_last_recv_addr); - const size_t nbytes = uhd_dpdk_get_len(_rx_sock, rx_mbuf); - UHD_ASSERT_THROW(nbytes <= buff_size); + // Extract the sender's address. This is only possible because we know + // the memory layout of the buff + struct udp_hdr* udp_hdr_end = (struct udp_hdr*)buff->data(); + struct ipv4_hdr* ip_hdr_end = (struct ipv4_hdr*)(&udp_hdr_end[-1]); + struct ipv4_hdr* ip_hdr = (struct ipv4_hdr*)(&ip_hdr_end[-1]); + _last_recv_addr = ip_hdr->src_addr; + + // Extract the buffer data + const size_t copy_len = std::min(user_buff_size, buff->packet_size()); + if (copy_len < buff->packet_size()) { + UHD_LOG_WARNING("DPDK", "Truncating recv packet"); + } + std::memcpy(user_data, buff->data(), copy_len); - uint8_t* pkt_data = (uint8_t*) uhd_dpdk_buf_to_data(_rx_sock, rx_mbuf); - std::memcpy(user_data, pkt_data, nbytes); - _put_rx_buf(rx_mbuf); - return nbytes; + // Housekeeping + _recv_io->release_recv_buff(std::move(buff)); + return copy_len; } - /*! - * Get the last IP address as seen by recv(). - * Only use this with the broadcast socket. - */ std::string get_recv_addr(void) { - char addr_str[INET_ADDRSTRLEN]; - struct in_addr ipv4_addr; - ipv4_addr.s_addr = _last_recv_addr; - inet_ntop(AF_INET, &ipv4_addr, addr_str, sizeof(addr_str)); - return std::string(addr_str); + return dpdk::ipv4_num_to_str(_last_recv_addr); } - /*! - * Get the IP address for the destination - */ std::string get_send_addr(void) { - struct in_addr ipv4_addr; - int status = uhd_dpdk_get_ipv4_addr(_port_id, &ipv4_addr.s_addr, nullptr); - UHD_ASSERT_THROW(status); - char addr_str[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, &ipv4_addr, addr_str, sizeof(addr_str)); - return std::string(addr_str); + return dpdk::ipv4_num_to_str(_link->get_remote_ipv4()); } + private: - /*! - * Request a single send buffer of specified size. - * - * \param buf a pointer to place to write buffer location - * \return the maximum length of the buffer - */ - size_t _get_tx_buf(struct rte_mbuf** buf) + using buff_t = frame_buff; + + link_params_t _get_default_link_params() { - int bufs = uhd_dpdk_request_tx_bufs(_tx_sock, buf, 1, 0); - if (bufs != 1) { - *buf = nullptr; - return 0; - } - return _mtu - DPDK_SIMPLE_NONDATA_SIZE; + link_params_t link_params; + link_params.recv_frame_size = 8000; + link_params.send_frame_size = 8000; + link_params.num_recv_frames = 1; + link_params.num_send_frames = 1; + link_params.recv_buff_size = 8000; + link_params.send_buff_size = 8000; + return link_params; } - /*! - * Return/free receive buffer - * Can also use to free un-sent TX bufs - */ - void _put_rx_buf(struct rte_mbuf *rx_mbuf) + void _send_callback(buff_t::uptr buff) + { + _link->release_send_buff(std::move(buff)); + } + + bool _recv_callback(buff_t::uptr&) { - UHD_ASSERT_THROW(rx_mbuf) - uhd_dpdk_free_buf(rx_mbuf); + // Queue it up + return true; } + void _recv_fc_callback(buff_t::uptr buff) + { + _link->release_recv_buff(std::move(buff)); + } + + /*** Attributes **********************************************************/ unsigned int _port_id; - size_t _mtu; - struct uhd_dpdk_socket *_tx_sock; - struct uhd_dpdk_socket *_rx_sock; uint32_t _last_recv_addr; + + udp_dpdk_link::sptr _link; + + dpdk_io_service::sptr _io_service; + + send_io_if::sptr _send_io; + + recv_io_if::sptr _recv_io; }; dpdk_simple::~dpdk_simple(void) {} @@ -182,16 +206,16 @@ dpdk_simple::~dpdk_simple(void) {} * DPDK simple transport public make functions **********************************************************************/ udp_simple::sptr dpdk_simple::make_connected( - struct uhd_dpdk_ctx &ctx, const std::string &addr, const std::string &port -){ - return udp_simple::sptr(new dpdk_simple_impl(ctx, addr, port, true)); + const std::string& addr, const std::string& port) +{ + return udp_simple::sptr(new dpdk_simple_impl(addr, port)); } +// For DPDK, this is not special and the same as make_connected udp_simple::sptr dpdk_simple::make_broadcast( - struct uhd_dpdk_ctx &ctx, const std::string &addr, const std::string &port -){ - return udp_simple::sptr(new dpdk_simple_impl(ctx, addr, port, false)); + const std::string& addr, const std::string& port) +{ + return udp_simple::sptr(new dpdk_simple_impl(addr, port)); } }} // namespace uhd::transport - |