aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/transport/dpdk_simple.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/transport/dpdk_simple.cpp')
-rw-r--r--host/lib/transport/dpdk_simple.cpp272
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
-