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