diff options
| author | Josh Blum <josh@joshknows.com> | 2010-04-12 12:22:29 -0700 | 
|---|---|---|
| committer | Josh Blum <josh@joshknows.com> | 2010-04-12 12:22:29 -0700 | 
| commit | 0021abf18aaf3831d8aaa40574f1876c4f346a92 (patch) | |
| tree | d0385274110a646dd84838c198627721d1387ce9 | |
| parent | 8ee3d7200169983e7a20409ed5e8c37907fe66e1 (diff) | |
| download | uhd-0021abf18aaf3831d8aaa40574f1876c4f346a92.tar.gz uhd-0021abf18aaf3831d8aaa40574f1876c4f346a92.tar.bz2 uhd-0021abf18aaf3831d8aaa40574f1876c4f346a92.zip | |
Created zero copy interface/framework, made use of it in usrp2 udp transport stuff.
| -rw-r--r-- | host/include/uhd/transport/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | host/include/uhd/transport/smart_buffer.hpp | 45 | ||||
| -rw-r--r-- | host/include/uhd/transport/udp_zero_copy.hpp | 28 | ||||
| -rw-r--r-- | host/include/uhd/transport/zero_copy.hpp | 133 | ||||
| -rw-r--r-- | host/lib/transport/udp_zero_copy_asio.cpp | 105 | ||||
| -rw-r--r-- | host/lib/usrp/usrp2/io_impl.cpp | 21 | ||||
| -rw-r--r-- | host/lib/usrp/usrp2/usrp2_impl.hpp | 2 | 
7 files changed, 223 insertions, 113 deletions
| diff --git a/host/include/uhd/transport/CMakeLists.txt b/host/include/uhd/transport/CMakeLists.txt index 14b5ccd29..9a94ead1b 100644 --- a/host/include/uhd/transport/CMakeLists.txt +++ b/host/include/uhd/transport/CMakeLists.txt @@ -18,9 +18,9 @@  INSTALL(FILES      if_addrs.hpp -    smart_buffer.hpp      udp_simple.hpp      udp_zero_copy.hpp      vrt.hpp +    zero_copy.hpp      DESTINATION ${INCLUDE_DIR}/uhd/transport  ) diff --git a/host/include/uhd/transport/smart_buffer.hpp b/host/include/uhd/transport/smart_buffer.hpp deleted file mode 100644 index a9bc259e9..000000000 --- a/host/include/uhd/transport/smart_buffer.hpp +++ /dev/null @@ -1,45 +0,0 @@ -// -// Copyright 2010 Ettus Research LLC -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program.  If not, see <http://www.gnu.org/licenses/>. -// - -#ifndef INCLUDED_UHD_TRANSPORT_SMART_BUFFER_HPP -#define INCLUDED_UHD_TRANSPORT_SMART_BUFFER_HPP - -#include <boost/asio/buffer.hpp> -#include <boost/utility.hpp> -#include <boost/shared_ptr.hpp> - -namespace uhd{ namespace transport{ - -/*! - * A buffer that knows how to free itself: - * - * This is just the smart buffer interface. - * A transport implementation will have its own - * internal (custom) smart buffer implementation. - * - * A smart buffer contains a boost asio const buffer. - * On destruction, the buffer contents will be freed. - */ -class smart_buffer : boost::noncopyable{ -public: -    typedef boost::shared_ptr<smart_buffer> sptr; -    virtual const boost::asio::const_buffer &get(void) const = 0; -}; - -}} //namespace - -#endif /* INCLUDED_UHD_TRANSPORT_SMART_BUFFER_HPP */ diff --git a/host/include/uhd/transport/udp_zero_copy.hpp b/host/include/uhd/transport/udp_zero_copy.hpp index 0441a8e74..fd1cec46e 100644 --- a/host/include/uhd/transport/udp_zero_copy.hpp +++ b/host/include/uhd/transport/udp_zero_copy.hpp @@ -19,24 +19,22 @@  #define INCLUDED_UHD_TRANSPORT_UDP_ZERO_COPY_HPP  #include <uhd/config.hpp> -#include <uhd/transport/smart_buffer.hpp> -#include <boost/asio/buffer.hpp> -#include <boost/utility.hpp> +#include <uhd/transport/zero_copy.hpp>  #include <boost/shared_ptr.hpp>  namespace uhd{ namespace transport{  /*!   * A zero copy udp transport provides an efficient way to handle data. - * by avoiding the extra copy when recv() is called on the socket. - * Rather, the zero copy transport gives the caller a memory reference. + * by avoiding the extra copy when recv() or send() is called on the socket. + * Rather, the zero copy transport gives the caller memory references.   * The caller informs the transport when it is finished with the reference.   *   * On linux systems, the zero copy transport can use a kernel packet ring.   * If no platform specific solution is available, make returns a boost asio - * implementation that wraps the functionality around a standard recv() call. + * implementation that wraps the functionality around a standard send/recv calls.   */ -class UHD_API udp_zero_copy : boost::noncopyable{ +class UHD_API udp_zero_copy : public zero_copy_if{  public:      typedef boost::shared_ptr<udp_zero_copy> sptr; @@ -54,22 +52,6 @@ public:       * \param port a string representing the destination port       */      static sptr make(const std::string &addr, const std::string &port); - -    /*! -     * Send a single buffer. -     * Blocks until the data is sent. -     * \param buff single asio buffer -     * \return the number of bytes sent -     */ -    virtual size_t send(const boost::asio::const_buffer &buff) = 0; - -    /*! -     * Receive a buffer. -     * Blocks until data is received or a timeout occurs. -     * The memory is managed by the implementation. -     * \return a smart buffer (empty on timeout) -     */ -    virtual smart_buffer::sptr recv(void) = 0;  };  }} //namespace diff --git a/host/include/uhd/transport/zero_copy.hpp b/host/include/uhd/transport/zero_copy.hpp new file mode 100644 index 000000000..4fc1df9de --- /dev/null +++ b/host/include/uhd/transport/zero_copy.hpp @@ -0,0 +1,133 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_TRANSPORT_ZERO_COPY_HPP +#define INCLUDED_UHD_TRANSPORT_ZERO_COPY_HPP + +#include <uhd/config.hpp> +#include <boost/asio/buffer.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> + +namespace uhd{ namespace transport{ + +/*! + * A managed receive buffer: + * Contains a reference to transport-managed memory, + * and a method to release the memory after reading. + */ +class UHD_API managed_recv_buffer : boost::noncopyable{ +public: +    typedef boost::shared_ptr<managed_recv_buffer> sptr; + +    /*! +     * Signal to the transport that we are done with the buffer. +     * This should be called to release the buffer to the transport. +     * After calling, the referenced memory should be considered invalid. +     */ +    virtual void done(void) = 0; + +    /*! +     * Get the size of the underlying buffer. +     * \return the number of bytes +     */ +    size_t size(void){ +        return boost::asio::buffer_size(this->get()); +    } + +    /*! +     * Get a pointer to the underlying buffer. +     * \return a pointer into memory +     */ +    template <class T> T cast(void){ +        return boost::asio::buffer_cast<T>(this->get()); +    } + +private: +    /*! +     * Get a reference to the internal const buffer. +     * The buffer has a reference to memory and a size. +     * \return a boost asio const buffer +     */ +    virtual const boost::asio::const_buffer &get(void) = 0; +}; + +/*! + * A managed send buffer: + * Contains a reference to transport-managed memory, + * and a method to release the memory after writing. + */ +class UHD_API managed_send_buffer : boost::noncopyable{ +public: +    typedef boost::shared_ptr<managed_send_buffer> sptr; + +    /*! +     * Signal to the transport that we are done with the buffer. +     * This should be called to commit the write to the transport object. +     * After calling, the referenced memory should be considered invalid. +     * \param num_bytes the number of bytes written into the buffer +     */ +    virtual void done(size_t num_bytes) = 0; + +    /*! +     * Get the size of the underlying buffer. +     * \return the number of bytes +     */ +    size_t size(void){ +        return boost::asio::buffer_size(this->get()); +    } + +    /*! +     * Get a pointer to the underlying buffer. +     * \return a pointer into memory +     */ +    template <class T> T cast(void){ +        return boost::asio::buffer_cast<T>(this->get()); +    } + +private: +    /*! +     * Get a reference to the internal mutable buffer. +     * The buffer has a reference to memory and a size. +     * \return a boost asio mutable buffer +     */ +    virtual const boost::asio::mutable_buffer &get(void) = 0; +}; + +/*! + * A zero-copy interface for transport objects. + * Provides a way to get send and receive buffers + * with memory managed by the transport object. + */ +class UHD_API zero_copy_if : boost::noncopyable{ +public: +    typedef boost::shared_ptr<zero_copy_if> sptr; + +    /*! +     * Get a new receive buffer from this transport object. +     */ +    virtual managed_recv_buffer::sptr get_recv_buff(void) = 0; + +    /*! +     * Get a new send buffer from this transport object. +     */ +    virtual managed_send_buffer::sptr get_send_buff(void) = 0; +}; + +}} //namespace + +#endif /* INCLUDED_UHD_TRANSPORT_ZERO_COPY_HPP */ diff --git a/host/lib/transport/udp_zero_copy_asio.cpp b/host/lib/transport/udp_zero_copy_asio.cpp index 1fc8ce14a..13fd50f65 100644 --- a/host/lib/transport/udp_zero_copy_asio.cpp +++ b/host/lib/transport/udp_zero_copy_asio.cpp @@ -25,39 +25,74 @@  using namespace uhd::transport;  /*********************************************************************** - * Smart buffer implementation for udp zerocopy none - * - * This smart buffer implemention houses a const buffer. - * When the smart buffer is deleted, the buffer is freed. - * The memory in the const buffer is allocated with new [], - * and so the destructor frees the buffer with delete []. + * Managed receive buffer implementation for udp zero-copy asio: + *   Frees the memory held by the const buffer on done.   **********************************************************************/ -class smart_buffer_impl : public smart_buffer{ +class managed_recv_buffer_impl : public managed_recv_buffer{  public: -    smart_buffer_impl(const boost::asio::const_buffer &buff){ -        _buff = buff; +    managed_recv_buffer_impl(const boost::asio::const_buffer &buff) : _buff(buff){ +        _done = false;      } -    ~smart_buffer_impl(void){ +    ~managed_recv_buffer_impl(void){ +        if (not _done) this->done(); +    } + +    void done(void){ +        _done = true;          delete [] boost::asio::buffer_cast<const boost::uint32_t *>(_buff);      } -    const boost::asio::const_buffer &get(void) const{ +private: +    const boost::asio::const_buffer &get(void){          return _buff;      } +    const boost::asio::const_buffer _buff; +    bool _done; +}; + +/*********************************************************************** + * Managed send buffer implementation for udp zero-copy asio: + *   Sends and frees the memory held by the mutable buffer on done. + **********************************************************************/ +class managed_send_buffer_impl : public managed_send_buffer{ +public: +    managed_send_buffer_impl( +        const boost::asio::mutable_buffer &buff, +        boost::asio::ip::udp::socket *socket +    ) : _buff(buff){ +        _done = false; +        _socket = socket; +    } + +    ~managed_send_buffer_impl(void){ +        if (not _done) this->done(0); +    } + +    void done(size_t num_bytes){ +        _done = true; +        boost::uint32_t *mem = boost::asio::buffer_cast<boost::uint32_t *>(_buff); +        _socket->send(boost::asio::buffer(mem, num_bytes)); +        delete [] mem; +    } +  private: -    boost::asio::const_buffer _buff; +    const boost::asio::mutable_buffer &get(void){ +        return _buff; +    } + +    const boost::asio::mutable_buffer _buff; +    boost::asio::ip::udp::socket      *_socket; +    bool _done;  };  /*********************************************************************** - * UDP zero copy implementation class - * - * This is the portable zero copy implementation for systems - * where a faster, platform specific solution is not available. - * - * It uses boost asio udp sockets and the standard recv() class, - * and in-fact, is not actually doing a zero-copy implementation. + * Zero Copy UDP implementation with ASIO: + *   This is the portable zero copy implementation for systems + *   where a faster, platform specific solution is not available. + *   However, it is not a true zero copy implementation as each + *   send and recv requires a copy operation to/from userspace.   **********************************************************************/  class udp_zero_copy_impl : public udp_zero_copy{  public: @@ -66,8 +101,8 @@ public:      ~udp_zero_copy_impl(void);      //send/recv -    size_t send(const boost::asio::const_buffer &buff); -    smart_buffer::sptr recv(void); +    managed_recv_buffer::sptr get_recv_buff(void); +    managed_send_buffer::sptr get_send_buff(void);  private:      boost::asio::ip::udp::socket   *_socket; @@ -107,31 +142,33 @@ udp_zero_copy_impl::~udp_zero_copy_impl(void){      delete _socket;  } -size_t udp_zero_copy_impl::send(const boost::asio::const_buffer &buff){ -    return _socket->send(boost::asio::buffer(buff)); -} - -smart_buffer::sptr udp_zero_copy_impl::recv(void){ -    size_t available = 0; +managed_recv_buffer::sptr udp_zero_copy_impl::get_recv_buff(void){ +    boost::uint32_t *buff_mem = new boost::uint32_t[1500/sizeof(boost::uint32_t)];      //implement timeout through polling and sleeping +    size_t available = 0;      boost::asio::deadline_timer timer(_socket->get_io_service());      timer.expires_from_now(boost::posix_time::milliseconds(100));      while (not ((available = _socket->available()) or timer.expires_from_now().is_negative())){          boost::this_thread::sleep(boost::posix_time::milliseconds(1));      } -    //allocate memory and create buffer -    boost::uint32_t *buff_mem = new boost::uint32_t[available/sizeof(boost::uint32_t)]; -    boost::asio::mutable_buffer buff(buff_mem, available); -      //receive only if data is available      if (available){ -        _socket->receive(boost::asio::buffer(buff)); +        available = _socket->receive(boost::asio::buffer(buff_mem, available));      } -    //create a new smart buffer to house the data -    return smart_buffer::sptr(new smart_buffer_impl(buff)); +    //create a new managed buffer to house the data +    return managed_recv_buffer::sptr( +        new managed_recv_buffer_impl(boost::asio::buffer(buff_mem, available)) +    ); +} + +managed_send_buffer::sptr udp_zero_copy_impl::get_send_buff(void){ +    boost::uint32_t *buff_mem = new boost::uint32_t[1500/sizeof(boost::uint32_t)]; +    return managed_send_buffer::sptr( +        new managed_send_buffer_impl(boost::asio::buffer(buff_mem, 1500), _socket) +    );  }  size_t udp_zero_copy_impl::get_recv_buff_size(void){ diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp index a58e32619..a54c0a409 100644 --- a/host/lib/usrp/usrp2/io_impl.cpp +++ b/host/lib/usrp/usrp2/io_impl.cpp @@ -42,11 +42,13 @@ void usrp2_impl::io_init(void){      //send a small data packet so the usrp2 knows the udp source port      //and the maximum number of lines (32 bit words) per packet +    managed_send_buffer::sptr send_buff = _data_transport->get_send_buff();      boost::uint32_t data[2] = {          htonl(USRP2_INVALID_VRT_HEADER),          htonl(_max_rx_samples_per_packet)      }; -    _data_transport->send(asio::buffer(&data, sizeof(data))); +    memcpy(send_buff->cast<void*>(), data, sizeof(data)); +    send_buff->done(sizeof(data));  }  #define unrolled_loop(__inst, __len){ \ @@ -127,15 +129,15 @@ static inline void usrp2_items_to_host_items(   **********************************************************************/  void usrp2_impl::recv_raw(rx_metadata_t &metadata){      //do a receive -    _rx_smart_buff = _data_transport->recv(); +    _rx_smart_buff = _data_transport->get_recv_buff();      //unpack the vrt header -    size_t num_packet_words32 = asio::buffer_size(_rx_smart_buff->get())/sizeof(boost::uint32_t); +    size_t num_packet_words32 = _rx_smart_buff->size()/sizeof(boost::uint32_t);      if (num_packet_words32 == 0){          _rx_copy_buff = boost::asio::buffer("", 0);          return; //must exit here after setting the buffer      } -    const boost::uint32_t *vrt_hdr = asio::buffer_cast<const boost::uint32_t *>(_rx_smart_buff->get()); +    const boost::uint32_t *vrt_hdr = _rx_smart_buff->cast<const boost::uint32_t *>();      size_t num_header_words32_out, num_payload_words32_out, packet_count_out;      try{          vrt::unpack( @@ -176,11 +178,12 @@ size_t usrp2_impl::send(  ){      tx_metadata_t metadata = metadata_; //rw copy to change later -    boost::uint32_t tx_mem[_mtu/sizeof(boost::uint32_t)]; +    transport::managed_send_buffer::sptr send_buff = _data_transport->get_send_buff(); +    boost::uint32_t *tx_mem = send_buff->cast<boost::uint32_t *>();      size_t num_samps = std::min( -        asio::buffer_size(buff)/io_type.size, -        size_t(_max_tx_samples_per_packet) -    ); +        asio::buffer_size(buff), +        send_buff->size() +    )/io_type.size;      //kill the end of burst flag if this is a fragment      if (asio::buffer_size(buff)/io_type.size < num_samps) @@ -214,7 +217,7 @@ size_t usrp2_impl::send(      }      //send and return number of samples -    _data_transport->send(asio::buffer(tx_mem, num_packet_words32*sizeof(boost::uint32_t))); +    send_buff->done(num_packet_words32*sizeof(boost::uint32_t));      return num_samps;  } diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index baa6530b8..be09c4ee1 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -136,7 +136,7 @@ private:          (_mtu - _hdrs)/sizeof(boost::uint32_t) -          uhd::transport::vrt::max_header_words32      ; -    uhd::transport::smart_buffer::sptr _rx_smart_buff; +    uhd::transport::managed_recv_buffer::sptr _rx_smart_buff;      boost::asio::const_buffer _rx_copy_buff;      size_t _fragment_offset_in_samps;      void io_init(void); | 
