Commit 9af8f2be authored by Dan Melnic's avatar Dan Melnic Committed by Facebook Github Bot

Adding UDP generic segmentation offload support

Summary: Adding UDP generic segmentation offload support

Reviewed By: siyengar

Differential Revision: D8825396

fbshipit-source-id: 5974307c2a901f4967e6f1c678b604ec1c21b3d0
parent 4813f785
...@@ -227,11 +227,12 @@ void AsyncUDPSocket::setFD(int fd, FDOwnership ownership) { ...@@ -227,11 +227,12 @@ void AsyncUDPSocket::setFD(int fd, FDOwnership ownership) {
localAddress_.setFromLocalAddress(fd_); localAddress_.setFromLocalAddress(fd_);
} }
ssize_t AsyncUDPSocket::write( ssize_t AsyncUDPSocket::writeGSO(
const folly::SocketAddress& address, const folly::SocketAddress& address,
const std::unique_ptr<folly::IOBuf>& buf) { const std::unique_ptr<folly::IOBuf>& buf,
int gso) {
// UDP's typical MTU size is 1500, so high number of buffers // UDP's typical MTU size is 1500, so high number of buffers
// really do not make sense. Optimze for buffer chains with // really do not make sense. Optimize for buffer chains with
// buffers less than 16, which is the highest I can think of // buffers less than 16, which is the highest I can think of
// for a real use case. // for a real use case.
iovec vec[16]; iovec vec[16];
...@@ -243,13 +244,20 @@ ssize_t AsyncUDPSocket::write( ...@@ -243,13 +244,20 @@ ssize_t AsyncUDPSocket::write(
iovec_len = 1; iovec_len = 1;
} }
return writev(address, vec, iovec_len); return writev(address, vec, iovec_len, gso);
}
ssize_t AsyncUDPSocket::write(
const folly::SocketAddress& address,
const std::unique_ptr<folly::IOBuf>& buf) {
return writeGSO(address, buf, 0);
} }
ssize_t AsyncUDPSocket::writev( ssize_t AsyncUDPSocket::writev(
const folly::SocketAddress& address, const folly::SocketAddress& address,
const struct iovec* vec, const struct iovec* vec,
size_t iovec_len) { size_t iovec_len,
int gso) {
CHECK_NE(-1, fd_) << "Socket not yet bound"; CHECK_NE(-1, fd_) << "Socket not yet bound";
sockaddr_storage addrStorage; sockaddr_storage addrStorage;
...@@ -264,9 +272,34 @@ ssize_t AsyncUDPSocket::writev( ...@@ -264,9 +272,34 @@ ssize_t AsyncUDPSocket::writev(
msg.msg_controllen = 0; msg.msg_controllen = 0;
msg.msg_flags = 0; msg.msg_flags = 0;
#ifdef FOLLY_HAVE_MSG_ERRQUEUE
if (gso > 0) {
char control[CMSG_SPACE(sizeof(uint16_t))];
msg.msg_control = control;
msg.msg_controllen = sizeof(control);
struct cmsghdr* cm = CMSG_FIRSTHDR(&msg);
cm->cmsg_level = SOL_UDP;
cm->cmsg_type = UDP_SEGMENT;
cm->cmsg_len = CMSG_LEN(sizeof(uint16_t));
uint16_t gso_len = static_cast<uint16_t>(gso);
memcpy(CMSG_DATA(cm), &gso_len, sizeof(gso_len));
return sendmsg(fd_, &msg, 0);
}
#else
CHECK_LT(gso, 1) << "GSO not supported";
#endif
return sendmsg(fd_, &msg, 0); return sendmsg(fd_, &msg, 0);
} }
ssize_t AsyncUDPSocket::writev(
const folly::SocketAddress& address,
const struct iovec* vec,
size_t iovec_len) {
return writev(address, vec, iovec_len, 0);
}
void AsyncUDPSocket::resumeRead(ReadCallback* cob) { void AsyncUDPSocket::resumeRead(ReadCallback* cob) {
CHECK(!readCallback_) << "Another read callback already installed"; CHECK(!readCallback_) << "Another read callback already installed";
CHECK_NE(-1, fd_) << "UDP server socket not yet bind to an address"; CHECK_NE(-1, fd_) << "UDP server socket not yet bind to an address";
...@@ -465,6 +498,29 @@ bool AsyncUDPSocket::updateRegistration() noexcept { ...@@ -465,6 +498,29 @@ bool AsyncUDPSocket::updateRegistration() noexcept {
return registerHandler(uint16_t(flags | PERSIST)); return registerHandler(uint16_t(flags | PERSIST));
} }
bool AsyncUDPSocket::setGSO(int val) {
int ret = ::setsockopt(fd_, SOL_UDP, UDP_SEGMENT, &val, sizeof(val));
gso_ = ret ? -1 : val;
return !ret;
}
int AsyncUDPSocket::getGSO() {
// check if we can return the cached value
if (FOLLY_UNLIKELY(!gso_.hasValue())) {
int gso = -1;
socklen_t optlen = sizeof(gso);
if (!::getsockopt(fd_, SOL_UDP, UDP_SEGMENT, &gso, &optlen)) {
gso_ = gso;
} else {
gso_ = -1;
}
}
return gso_.value();
}
void AsyncUDPSocket::detachEventBase() { void AsyncUDPSocket::detachEventBase() {
DCHECK(eventBase_ && eventBase_->isInEventBaseThread()); DCHECK(eventBase_ && eventBase_->isInEventBaseThread());
registerHandler(uint16_t(NONE)); registerHandler(uint16_t(NONE));
......
...@@ -136,9 +136,29 @@ class AsyncUDPSocket : public EventHandler { ...@@ -136,9 +136,29 @@ class AsyncUDPSocket : public EventHandler {
const folly::SocketAddress& address, const folly::SocketAddress& address,
const std::unique_ptr<folly::IOBuf>& buf); const std::unique_ptr<folly::IOBuf>& buf);
/**
* Send the data in buffer to destination. Returns the return code from
* ::sendmsg.
* gso is the generic segmentation offload value
* writeGSO will return -1 if
* buf->computeChainDataLength() <= gso
* Before calling writeGSO with a positive value
* verify GSO is supported on this platform by calling getGSO
*/
virtual ssize_t writeGSO(
const folly::SocketAddress& address,
const std::unique_ptr<folly::IOBuf>& buf,
int gso);
/** /**
* Send data in iovec to destination. Returns the return code from sendmsg. * Send data in iovec to destination. Returns the return code from sendmsg.
*/ */
virtual ssize_t writev(
const folly::SocketAddress& address,
const struct iovec* vec,
size_t veclen,
int gso);
virtual ssize_t writev( virtual ssize_t writev(
const folly::SocketAddress& address, const folly::SocketAddress& address,
const struct iovec* vec, const struct iovec* vec,
...@@ -252,6 +272,12 @@ class AsyncUDPSocket : public EventHandler { ...@@ -252,6 +272,12 @@ class AsyncUDPSocket : public EventHandler {
virtual void attachEventBase(folly::EventBase* evb); virtual void attachEventBase(folly::EventBase* evb);
// generic segmentation offload get/set
// negative return value means GSO is not available
int getGSO();
bool setGSO(int val);
protected: protected:
virtual ssize_t sendmsg(int socket, const struct msghdr* message, int flags) { virtual ssize_t sendmsg(int socket, const struct msghdr* message, int flags) {
return ::sendmsg(socket, message, flags); return ::sendmsg(socket, message, flags);
...@@ -289,6 +315,10 @@ class AsyncUDPSocket : public EventHandler { ...@@ -289,6 +315,10 @@ class AsyncUDPSocket : public EventHandler {
int sndBuf_{0}; int sndBuf_{0};
int busyPollUs_{0}; int busyPollUs_{0};
// generic segmentation offload value, if available
// See https://lwn.net/Articles/188489/ for more details
folly::Optional<int> gso_;
ErrMessageCallback* errMessageCallback_{nullptr}; ErrMessageCallback* errMessageCallback_{nullptr};
}; };
......
This diff is collapsed.
...@@ -48,6 +48,22 @@ ...@@ -48,6 +48,22 @@
#define MSG_ZEROCOPY 0x4000000 #define MSG_ZEROCOPY 0x4000000
#endif #endif
#ifndef SOL_UDP
#define SOL_UDP 17
#endif
#ifndef ETH_MAX_MTU
#define ETH_MAX_MTU 0xFFFFU
#endif
#ifndef UDP_SEGMENT
#define UDP_SEGMENT 103
#endif
#ifndef UDP_MAX_SEGMENTS
#define UDP_MAX_SEGMENTS (1 << 6UL)
#endif
#else #else
#include <folly/portability/IOVec.h> #include <folly/portability/IOVec.h>
#include <folly/portability/SysTypes.h> #include <folly/portability/SysTypes.h>
...@@ -62,6 +78,8 @@ using sa_family_t = ADDRESS_FAMILY; ...@@ -62,6 +78,8 @@ using sa_family_t = ADDRESS_FAMILY;
#define SO_EE_ORIGIN_ZEROCOPY 0 #define SO_EE_ORIGIN_ZEROCOPY 0
#define SO_ZEROCOPY 0 #define SO_ZEROCOPY 0
#define MSG_ZEROCOPY 0x0 #define MSG_ZEROCOPY 0x0
#define SOL_UDP 0x0
#define UDP_SEGMENT 0x0
// We don't actually support either of these flags // We don't actually support either of these flags
// currently. // currently.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment