L4Re Operating System Framework
Interface and Usage Documentation
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
port_l4virtio.h
1/*
2 * Copyright (C) 2016-2017, 2020, 2022-2024 Kernkonzept GmbH.
3 * Author(s): Jean Wolter <jean.wolter@kernkonzept.com>
4 * Alexander Warg <warg@os.inf.tu-dresden.de>
5 * Georg Kotheimer <georg.kotheimer@kernkonzept.com>
6 *
7 * License: see LICENSE.spdx (in this directory or the directories above)
8 */
9#pragma once
10
11#include "port.h"
12#include "request_l4virtio.h"
13#include "virtio_net.h"
14
15#include <l4/cxx/pair>
16
17#include <vector>
18
36class L4virtio_port : public Port_iface, public Virtio_net
37{
38public:
42 explicit L4virtio_port(unsigned vq_max, unsigned num_ds, char const *name,
43 l4_uint8_t const *mac)
44 : Port_iface(name), Virtio_net(vq_max)
45 {
46 init_mem_info(num_ds);
47
48 Features hf = _dev_config.host_features(0);
49 if (mac)
50 {
51 _mac = Mac_addr((char const *)mac);
52 memcpy((void *)_dev_config.priv_config()->mac, mac,
53 sizeof(_dev_config.priv_config()->mac));
54
55 hf.mac() = true;
56 Dbg d(Dbg::Port, Dbg::Info);
57 d.cprintf("%s: Adding Mac '", _name);
58 _mac.print(d);
59 d.cprintf("' to host features to %x\n", hf.raw);
60 }
61 _dev_config.host_features(0) = hf.raw;
62 _dev_config.reset_hdr();
63 Dbg(Dbg::Port, Dbg::Info)
64 .printf("%s: Set host features to %x\n", _name,
65 _dev_config.host_features(0));
66#if CONFIG_VNS_STATS
67 _mac.to_array(_stats->mac);
68#endif
69 }
70
71 void rx_notify_disable_and_remember() override
72 {
73 kick_disable_and_remember();
74 }
75
76 void rx_notify_emit_and_enable() override
77 {
78 kick_emit_and_enable();
79 }
80
81 bool is_gone() const override
82 {
83 return obj_cap() && !obj_cap().validate().label();
84 }
85
87 bool tx_work_pending() const
88 {
89 return L4_LIKELY(tx_q()->ready()) && tx_q()->desc_avail();
90 }
91
93 std::optional<Virtio_net_request> get_tx_request()
94 {
96 }
97
105
106 Result handle_request(Port_iface *src_port, Net_transfer &src,
107 l4_uint64_t *bytes_transferred) override
108 {
109 Virtio_vlan_mangle mangle = create_vlan_mangle(src_port);
110
111 Dbg trace(Dbg::Request, Dbg::Trace, "REQ-VIO");
112 trace.printf("%s: Transfer request %p.\n", _name, src.req_id());
113
114 Buffer dst;
115 int total = 0;
116 l4_uint16_t num_merged = 0;
117 l4_uint64_t total_merged = 0;
119 std::vector<Consumed_entry> consumed;
120
121 Virtio_net *dst_dev = this;
122 Virtqueue *dst_queue = rx_q();
125 Virtio_net::Hdr *dst_header = nullptr;
126
127 for (;;)
128 {
129 try
130 {
131 if (src.done())
132 // Request completely copied to destination.
133 break;
134 }
136 {
137 trace.printf("\tTransfer failed, bad descriptor exception, dropping.\n");
138
139 // Handle partial transfers to destination port.
140 if (!consumed.empty())
141 // Partial transfer, rewind to before first descriptor of transfer.
142 dst_queue->rewind_avail(consumed.at(0).first);
143 else if (dst_head)
144 // Partial transfer, still at first _dst_head.
145 dst_queue->rewind_avail(dst_head);
146 throw;
147 }
148
149 /* The source data structures are already initialized, the header
150 is consumed and src stands at the very first real buffer.
151 Initialize the target data structures if necessary and fill the
152 header. */
153 if (!dst_head)
154 {
155 if (!dst_queue->ready())
156 return Result::Dropped;
157
158 auto r = dst_queue->next_avail();
159
160 if (L4_UNLIKELY(!r))
161 {
162 trace.printf("\tTransfer failed, destination queue depleted, dropping.\n");
163 // Abort incomplete transfer.
164 if (!consumed.empty())
165 dst_queue->rewind_avail(consumed.front().first);
166 return Result::Dropped;
167 }
168
169 try
170 {
171 dst_head = dst_req_proc.start(dst_dev->mem_info(), r, &dst);
172 }
174 {
175 Dbg(Dbg::Request, Dbg::Warn, "REQ")
176 .printf("%s: bad descriptor exception: %s - %i"
177 " -- signal device error in destination device %p.\n",
178 __PRETTY_FUNCTION__, e.message(), e.error, dst_dev);
179
180 dst_dev->device_error();
181 return Result::Exception; // Must not touch the dst queues anymore.
182 }
183
184 if (!dst_header)
185 {
186 if (dst.left < sizeof(Virtio_net::Hdr))
188 "Target buffer too small for header");
189 dst_header = reinterpret_cast<Virtio_net::Hdr *>(dst.pos);
190 trace.printf("\tCopying header to %p (size: %u)\n",
191 dst.pos, dst.left);
192 /*
193 * Header and csum offloading/general segmentation offloading
194 *
195 * We just copy the original header from source to
196 * destination and have to consider three different
197 * cases:
198 * - no flags are set
199 * - we got a packet that is completely checksummed
200 * and correctly fragmented, there is nothing to
201 * do other then copying.
202 * - virtio_net_hdr_f_needs_csum set
203 * - the packet is partially checksummed; if we would
204 * send the packet out on the wire we would have
205 * to calculate checksums now. But here we rely on
206 * the ability of our guest to handle partially
207 * checksummed packets and simply delegate the
208 * checksum calculation to them.
209 * - gso_type != gso_none
210 * - the packet needs to be segmented; if we would
211 * send it out on the wire we would have to
212 * segment it now. But again we rely on the
213 * ability of our guest to handle gso
214 *
215 * We currently assume that our guests negotiated
216 * virtio_net_f_guest_*, this needs to be checked in
217 * the future.
218 *
219 * We also discussed the usage of
220 * virtio_net_hdr_f_data_valid to remove the need to
221 * checksum packets at all. But since our clients send
222 * partially checksummed packets anyway the only
223 * interesting case would be a packet without
224 * net_hdr_f_needs_checksum set. In that case we would
225 * signal that we checked the checksum and the
226 * checksum is actually correct. Since we do not know
227 * the origin of the packet (it could have been send
228 * by an external node and could have been routed to
229 * u) we can not signal this without actually
230 * verifying the checksum. Otherwise a packet with an
231 * invalid checksum could be successfully delivered.
232 */
233 total = sizeof(Virtio_net::Hdr);
234 src.copy_header(dst_header);
235 mangle.rewrite_hdr(dst_header);
236 dst.skip(total);
237 }
238 ++num_merged;
239 }
240
241 bool has_dst_buffer = !dst.done();
242 if (!has_dst_buffer)
243 try
244 {
245 // The current dst buffer is full, try to get next chained buffer.
246 has_dst_buffer = dst_req_proc.next(dst_dev->mem_info(), &dst);
247 }
249 {
250 Dbg(Dbg::Request, Dbg::Warn, "REQ")
251 .printf("%s: bad descriptor exception: %s - %i"
252 " -- signal device error in destination device %p.\n",
253 __PRETTY_FUNCTION__, e.message(), e.error, dst_dev);
254 dst_dev->device_error();
255 return Result::Exception; // Must not touch the dst queues anymore.
256 }
257
258 if (has_dst_buffer)
259 {
260 auto &src_buf = src.cur_buf();
261 trace.printf("\tCopying %p#%p:%u (%x) -> %p#%p:%u (%x)\n",
262 src_port, src_buf.pos, src_buf.left, src_buf.left,
263 static_cast<Port_iface *>(this),
264 dst.pos, dst.left, dst.left);
265
266 total += mangle.copy_pkt(dst, src_buf);
267 }
268 else if (negotiated_features().mrg_rxbuf())
269 {
270 // save descriptor information for later
271 trace.printf("\tSaving descriptor for later\n");
272 consumed.push_back(Consumed_entry(dst_head, total));
273 total_merged += total;
274 total = 0;
276 }
277 else
278 {
279 trace.printf("\tTransfer failed, destination buffer too small, dropping.\n");
280 // Abort incomplete transfer.
281 dst_queue->rewind_avail(dst_head);
282 return Result::Dropped;
283 }
284 }
285
286 /*
287 * Finalize the Request delivery. Call `finish()` on the destination
288 * port's receive queue, which will result in triggering the destination
289 * client IRQ.
290 */
291
292 if (!dst_header)
293 {
294 if (!total)
295 trace.printf("\tTransfer - not started yet, dropping\n");
296 return Result::Dropped;
297 }
298
299 if (consumed.empty())
300 {
301 assert(dst_head);
302 assert(num_merged == 1);
303 trace.printf("\tTransfer - Invoke dst_queue->finish()\n");
304 dst_header->num_buffers = 1;
305 dst_queue->finish(dst_head, dst_dev, total);
306 *bytes_transferred = total;
307 }
308 else
309 {
310 assert(dst_head);
311 dst_header->num_buffers = num_merged;
312 consumed.push_back(Consumed_entry(dst_head, total));
313 trace.printf("\tTransfer - Invoke dst_queue->finish(iter)\n");
314 *bytes_transferred = total + total_merged;
315 dst_queue->finish(consumed.begin(), consumed.end(), dst_dev);
316 }
317 return Result::Delivered;
318 }
319};
320
Exception for an abstract runtime error.
Definition exceptions:129
void device_error()
Transition device into DEVICE_NEEDS_RESET state.
Definition l4virtio:1024
void init_mem_info(unsigned num)
Initialize the memory region list to the given maximum.
Definition l4virtio:1012
Mem_list const * mem_info() const
Get the memory region list used for this device.
Definition l4virtio:898
Encapsulate the state for processing a VIRTIO request.
Definition virtio:473
bool next(DESC_MAN *dm, ARGS... args)
Switch to the next descriptor in a descriptor chain.
Definition virtio:570
void start(DESC_MAN *dm, Virtqueue *ring, Virtqueue::Head_desc const &request, ARGS... args)
Start processing a new request.
Definition virtio:501
VIRTIO request, essentially a descriptor from the available ring.
Definition virtio:94
A Port on the Virtio Net Switch.
void drop_requests()
Drop all requests pending in the transmission queue.
L4virtio_port(unsigned vq_max, unsigned num_ds, char const *name, l4_uint8_t const *mac)
Create a Virtio net port object.
bool tx_work_pending() const
Check whether there is any work pending on the transmission queue.
std::optional< Virtio_net_request > get_tx_request()
Get one request from the transmission queue.
A wrapper class around the value of a MAC address.
Definition mac_addr.h:20
A network request to only a single destination.
Definition request.h:34
virtual void copy_header(Virtio_net::Hdr *dst_header) const =0
Populate the virtio-net header for the destination.
void const * req_id() const
Identifier for the underlying Net_request, used for logging purposes.
Definition request.h:41
virtual bool done()=0
Check whether the transfer has been completed, i.e.
Buffer & cur_buf()
Buffer containing (a part of) the packet data.
Definition request.h:54
static std::optional< Virtio_net_request > get_request(Virtio_net *dev, L4virtio::Svr::Virtqueue *queue)
Construct a request from the next entry of a provided queue.
static void drop_requests(Virtio_net *dev, L4virtio::Svr::Virtqueue *queue)
Drop all requests of a specific queue.
The Base class of a Port.
Definition virtio_net.h:74
Virtqueue * rx_q()
Getter for the receive queue.
Definition virtio_net.h:307
Virtqueue * tx_q()
Getter for the transmission queue.
Definition virtio_net.h:305
Class for VLAN packet rewriting.
Definition vlan.h:37
void rewrite_hdr(Virtio_net::Hdr *hdr)
Rewrite the virtio network header.
Definition vlan.h:142
l4_uint32_t copy_pkt(Buffer &dst, Buffer &src)
Copy packet from src to dst.
Definition vlan.h:93
unsigned char l4_uint8_t
Unsigned 8bit value.
Definition l4int.h:25
unsigned short int l4_uint16_t
Unsigned 16bit value.
Definition l4int.h:27
unsigned long long l4_uint64_t
Unsigned 64bit value.
Definition l4int.h:31
@ L4_EINVAL
Invalid argument.
Definition err.h:46
#define L4_UNLIKELY(x)
Expression is unlikely to execute.
Definition compiler.h:275
#define L4_LIKELY(x)
Expression is likely to execute.
Definition compiler.h:274
Pair implementation.
Data buffer used to transfer packets.
Cap< RPC_IFACE > obj_cap() const
Get the (typed) capability to this object.
Definition ipc_epiface:269
Exception used by Queue to indicate descriptor errors.
Definition virtio:398
char const * message() const
Get a human readable description of the error code.
Definition virtio:430
l4_uint32_t left
Bytes left in buffer.
Definition virtio:309
char * pos
Current buffer position.
Definition virtio:308
l4_uint32_t skip(l4_uint32_t bytes)
Skip given number of bytes in this buffer.
Definition virtio:375
bool done() const
Check if there are no more bytes left in the buffer.
Definition virtio:388
Pair of two values.
Definition pair:28