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
16 constexpr size_t MBufChain::MBuf::kHeaderSize;
17 constexpr size_t MBufChain::MBuf::kMallocSize;
18 constexpr size_t MBufChain::MBuf::kPayloadSize;
19 constexpr size_t MBufChain::kSizeMax;
20
rem() const21 size_t MBufChain::MBuf::rem() const {
22 return kPayloadSize - (off_ + len_);
23 }
24
~MBufChain()25 MBufChain::~MBufChain() {
26 while (!tail_.is_empty())
27 delete tail_.pop_front();
28 while (!freelist_.is_empty())
29 delete freelist_.pop_front();
30 }
31
is_full() const32 bool MBufChain::is_full() const {
33 return size_ >= kSizeMax;
34 }
35
is_empty() const36 bool MBufChain::is_empty() const {
37 return size_ == 0;
38 }
39
Read(user_out_ptr<void> dst,size_t len,bool datagram)40 size_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
WriteDatagram(user_in_ptr<const void> src,size_t len,size_t * written)79 zx_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
WriteStream(user_in_ptr<const void> src,size_t len,size_t * written)130 zx_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
AllocMBuf()168 MBufChain::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
FreeMBuf(MBuf * buf)177 void MBufChain::FreeMBuf(MBuf* buf) {
178 buf->off_ = 0u;
179 buf->len_ = 0u;
180 freelist_.push_front(buf);
181 }
182