void TunIntf::handlerReady(uint16_t events) noexcept { CHECK(fd_ != -1); const int MaxSentOneTime = 16; // TODO: The packet size should be as same as MTU (hard code 1500 for now). // Since this is L3 packet size, we should also reserve some space for L2 // header, which is 18 bytes (including one vlan tag) const int l3Len = 1500; int sent = 0; int dropped = 0; uint64_t bytes = 0; bool fdFail = false; try { while (sent + dropped < MaxSentOneTime) { std::unique_ptr<TxPacket> pkt; pkt = sw_->allocateL3TxPacket(l3Len); auto buf = pkt->buf(); int ret = 0; do { ret = read(fd_, buf->writableTail(), buf->tailroom()); } while (ret == -1 && errno == EINTR); if (ret < 0) { if (errno != EAGAIN) { sysLogError(ret, "Failed to read on ", fd_); // Cannot continue read on this fd fdFail = true; } break; } else if (ret == 0) { // Nothing to read. It shall not happen as the fd is non-blocking. // Just add this case to be safe. break; } else if (ret > buf->tailroom()) { // The pkt is larger than the buffer. We don't have complete packet. // It shall not happen unless the MTU is mis-match. Drop the packet. LOG(ERROR) << "Too large packet (" << ret << " > " << buf->tailroom() << ") received from host. Drop the packet."; dropped++; } else { bytes += ret; buf->append(ret); sw_->sendL3Packet(rid_, std::move(pkt)); sent++; } } } catch (const std::exception& ex) { LOG(ERROR) << "Hit some error when forwarding packets :" << folly::exceptionStr(ex); } if (fdFail) { unregisterHandler(); } VLOG(4) << "Forwarded " << sent << " packets (" << bytes << " bytes) from host @ fd " << fd_ << " for router " << rid_ << " dropped:" << dropped; }
fbstring IOBuf::moveToFbString() { // malloc-allocated buffers are just fine, everything else needs // to be turned into one. if (!sharedInfo() || // user owned, not ours to give up sharedInfo()->freeFn || // not malloc()-ed headroom() != 0 || // malloc()-ed block doesn't start at beginning tailroom() == 0 || // no room for NUL terminator isShared() || // shared isChained()) { // chained // We might as well get rid of all head and tailroom if we're going // to reallocate; we need 1 byte for NUL terminator. coalesceAndReallocate(0, computeChainDataLength(), this, 1); } // Ensure NUL terminated *writableTail() = 0; fbstring str(reinterpret_cast<char*>(writableData()), length(), capacity(), AcquireMallocatedString()); if (flags() & kFlagFreeSharedInfo) { delete sharedInfo(); } // Reset to a state where we can be deleted cleanly flagsAndSharedInfo_ = 0; buf_ = nullptr; clear(); return str; }
string dumpChain(const folly::IOBuf* buf) { stringstream out; auto b = buf; do { out << "iobuf of size " << b->length() << " tailroom " << b->tailroom(); b = b->next(); } while (b != buf); return out.str(); }
pair<void*,uint64_t> IOBufQueue::preallocateSlow(uint64_t min, uint64_t newAllocationSize, uint64_t max) { // Avoid grabbing update guard, since we're manually setting the cache ptrs. flushCache(); // Allocate a new buffer of the requested max size. unique_ptr<IOBuf> newBuf(IOBuf::create(std::max(min, newAllocationSize))); tailStart_ = newBuf->writableTail(); cachePtr_->cachedRange = std::pair<uint8_t*, uint8_t*>( tailStart_, tailStart_ + newBuf->tailroom()); appendToChain(head_, std::move(newBuf), false); return make_pair(writableTail(), std::min<uint64_t>(max, tailroom())); }
void IOBuf::reserveSlow(uint64_t minHeadroom, uint64_t minTailroom) { size_t newCapacity = (size_t)length_ + minHeadroom + minTailroom; DCHECK_LT(newCapacity, UINT32_MAX); // reserveSlow() is dangerous if anyone else is sharing the buffer, as we may // reallocate and free the original buffer. It should only ever be called if // we are the only user of the buffer. DCHECK(!isSharedOne()); // We'll need to reallocate the buffer. // There are a few options. // - If we have enough total room, move the data around in the buffer // and adjust the data_ pointer. // - If we're using an internal buffer, we'll switch to an external // buffer with enough headroom and tailroom. // - If we have enough headroom (headroom() >= minHeadroom) but not too much // (so we don't waste memory), we can try one of two things, depending on // whether we use jemalloc or not: // - If using jemalloc, we can try to expand in place, avoiding a memcpy() // - If not using jemalloc and we don't have too much to copy, // we'll use realloc() (note that realloc might have to copy // headroom + data + tailroom, see smartRealloc in folly/Malloc.h) // - Otherwise, bite the bullet and reallocate. if (headroom() + tailroom() >= minHeadroom + minTailroom) { uint8_t* newData = writableBuffer() + minHeadroom; memmove(newData, data_, length_); data_ = newData; return; } size_t newAllocatedCapacity = 0; uint8_t* newBuffer = nullptr; uint64_t newHeadroom = 0; uint64_t oldHeadroom = headroom(); // If we have a buffer allocated with malloc and we just need more tailroom, // try to use realloc()/xallocx() to grow the buffer in place. SharedInfo* info = sharedInfo(); if (info && (info->freeFn == nullptr) && length_ != 0 && oldHeadroom >= minHeadroom) { size_t headSlack = oldHeadroom - minHeadroom; newAllocatedCapacity = goodExtBufferSize(newCapacity + headSlack); if (usingJEMalloc()) { // We assume that tailroom is more useful and more important than // headroom (not least because realloc / xallocx allow us to grow the // buffer at the tail, but not at the head) So, if we have more headroom // than we need, we consider that "wasted". We arbitrarily define "too // much" headroom to be 25% of the capacity. if (headSlack * 4 <= newCapacity) { size_t allocatedCapacity = capacity() + sizeof(SharedInfo); void* p = buf_; if (allocatedCapacity >= jemallocMinInPlaceExpandable) { if (xallocx(p, newAllocatedCapacity, 0, 0) == newAllocatedCapacity) { newBuffer = static_cast<uint8_t*>(p); newHeadroom = oldHeadroom; } // if xallocx failed, do nothing, fall back to malloc/memcpy/free } } } else { // Not using jemalloc size_t copySlack = capacity() - length_; if (copySlack * 2 <= length_) { void* p = realloc(buf_, newAllocatedCapacity); if (UNLIKELY(p == nullptr)) { throw std::bad_alloc(); } newBuffer = static_cast<uint8_t*>(p); newHeadroom = oldHeadroom; } } } // None of the previous reallocation strategies worked (or we're using // an internal buffer). malloc/copy/free. if (newBuffer == nullptr) { newAllocatedCapacity = goodExtBufferSize(newCapacity); void* p = malloc(newAllocatedCapacity); if (UNLIKELY(p == nullptr)) { throw std::bad_alloc(); } newBuffer = static_cast<uint8_t*>(p); if (length_ > 0) { assert(data_ != nullptr); memcpy(newBuffer + minHeadroom, data_, length_); } if (sharedInfo()) { freeExtBuffer(); } newHeadroom = minHeadroom; } uint64_t cap; initExtBuffer(newBuffer, newAllocatedCapacity, &info, &cap); if (flags() & kFlagFreeSharedInfo) { delete sharedInfo(); } setFlagsAndSharedInfo(0, info); capacity_ = cap; buf_ = newBuffer; data_ = newBuffer + newHeadroom; // length_ is unchanged }