1// Copyright 2017 The Fuchsia Authors 2// 3// Use of this source code is governed by a MIT-style 4// license that can be found in the LICENSE file or at 5// https://opensource.org/licenses/MIT 6 7#include <object/mbuf.h> 8 9#include <lib/user_copy/user_ptr.h> 10 11#include <fbl/algorithm.h> 12#include <fbl/alloc_checker.h> 13 14#define LOCAL_TRACE 0 15 16constexpr size_t MBufChain::MBuf::kHeaderSize; 17constexpr size_t MBufChain::MBuf::kMallocSize; 18constexpr size_t MBufChain::MBuf::kPayloadSize; 19constexpr size_t MBufChain::kSizeMax; 20 21size_t MBufChain::MBuf::rem() const { 22 return kPayloadSize - (off_ + len_); 23} 24 25MBufChain::~MBufChain() { 26 while (!tail_.is_empty()) 27 delete tail_.pop_front(); 28 while (!freelist_.is_empty()) 29 delete freelist_.pop_front(); 30} 31 32bool MBufChain::is_full() const { 33 return size_ >= kSizeMax; 34} 35 36bool MBufChain::is_empty() const { 37 return size_ == 0; 38} 39 40size_t MBufChain::Read(user_out_ptr<void> dst, size_t len, bool datagram) { 41 if (size_ == 0) { 42 return 0; 43 } 44 45 if (datagram && len > tail_.front().pkt_len_) 46 len = tail_.front().pkt_len_; 47 48 size_t pos = 0; 49 while (pos < len && !tail_.is_empty()) { 50 MBuf& cur = tail_.front(); 51 char* src = cur.data_ + cur.off_; 52 size_t copy_len = MIN(cur.len_, len - pos); 53 if (dst.byte_offset(pos).copy_array_to_user(src, copy_len) != ZX_OK) 54 return pos; 55 pos += copy_len; 56 cur.off_ += static_cast<uint32_t>(copy_len); 57 cur.len_ -= static_cast<uint32_t>(copy_len); 58 size_ -= copy_len; 59 if (cur.len_ == 0 || datagram) { 60 size_ -= cur.len_; 61 if (head_ == &cur) 62 head_ = nullptr; 63 FreeMBuf(tail_.pop_front()); 64 } 65 } 66 if (datagram) { 67 // Drain any leftover mbufs in the datagram packet. 68 while (!tail_.is_empty() && tail_.front().pkt_len_ == 0) { 69 MBuf* cur = tail_.pop_front(); 70 size_ -= cur->len_; 71 if (head_ == cur) 72 head_ = nullptr; 73 FreeMBuf(cur); 74 } 75 } 76 return pos; 77} 78 79zx_status_t MBufChain::WriteDatagram(user_in_ptr<const void> src, size_t len, 80 size_t* written) { 81 if (len == 0) { 82 return ZX_ERR_INVALID_ARGS; 83 } 84 if (len > kSizeMax) 85 return ZX_ERR_OUT_OF_RANGE; 86 if (len + size_ > kSizeMax) 87 return ZX_ERR_SHOULD_WAIT; 88 89 fbl::SinglyLinkedList<MBuf*> bufs; 90 for (size_t need = 1 + ((len - 1) / MBuf::kPayloadSize); need != 0; need--) { 91 auto buf = AllocMBuf(); 92 if (buf == nullptr) { 93 while (!bufs.is_empty()) 94 FreeMBuf(bufs.pop_front()); 95 return ZX_ERR_SHOULD_WAIT; 96 } 97 bufs.push_front(buf); 98 } 99 100 size_t pos = 0; 101 for (auto& buf : bufs) { 102 size_t copy_len = fbl::min(MBuf::kPayloadSize, len - pos); 103 if (src.byte_offset(pos).copy_array_from_user(buf.data_, copy_len) != ZX_OK) { 104 while (!bufs.is_empty()) 105 FreeMBuf(bufs.pop_front()); 106 return ZX_ERR_INVALID_ARGS; // Bad user buffer. 107 } 108 pos += copy_len; 109 buf.len_ += static_cast<uint32_t>(copy_len); 110 } 111 112 bufs.front().pkt_len_ = static_cast<uint32_t>(len); 113 114 // Successfully built the packet mbufs. Put it on the socket. 115 while (!bufs.is_empty()) { 116 auto next = bufs.pop_front(); 117 if (head_ == nullptr) { 118 tail_.push_front(next); 119 } else { 120 tail_.insert_after(tail_.make_iterator(*head_), next); 121 } 122 head_ = next; 123 } 124 125 *written = len; 126 size_ += len; 127 return ZX_OK; 128} 129 130zx_status_t MBufChain::WriteStream(user_in_ptr<const void> src, size_t len, size_t* written) { 131 if (head_ == nullptr) { 132 head_ = AllocMBuf(); 133 if (head_ == nullptr) 134 return ZX_ERR_SHOULD_WAIT; 135 tail_.push_front(head_); 136 } 137 138 size_t pos = 0; 139 while (pos < len) { 140 if (head_->rem() == 0) { 141 auto next = AllocMBuf(); 142 if (next == nullptr) 143 break; 144 tail_.insert_after(tail_.make_iterator(*head_), next); 145 head_ = next; 146 } 147 void* dst = head_->data_ + head_->off_ + head_->len_; 148 size_t copy_len = fbl::min(head_->rem(), len - pos); 149 if (size_ + copy_len > kSizeMax) { 150 copy_len = kSizeMax - size_; 151 if (copy_len == 0) 152 break; 153 } 154 if (src.byte_offset(pos).copy_array_from_user(dst, copy_len) != ZX_OK) 155 break; 156 pos += copy_len; 157 head_->len_ += static_cast<uint32_t>(copy_len); 158 size_ += copy_len; 159 } 160 161 if (pos == 0) 162 return ZX_ERR_SHOULD_WAIT; 163 164 *written = pos; 165 return ZX_OK; 166} 167 168MBufChain::MBuf* MBufChain::AllocMBuf() { 169 if (freelist_.is_empty()) { 170 fbl::AllocChecker ac; 171 MBuf* buf = new (&ac) MBuf(); 172 return (!ac.check()) ? nullptr : buf; 173 } 174 return freelist_.pop_front(); 175} 176 177void MBufChain::FreeMBuf(MBuf* buf) { 178 buf->off_ = 0u; 179 buf->len_ = 0u; 180 freelist_.push_front(buf); 181} 182