Loading...
Searching...
No Matches
port_l4virtio.h
/*
* Copyright (C) 2016-2017, 2020, 2022-2024 Kernkonzept GmbH.
* Author(s): Jean Wolter <jean.wolter@kernkonzept.com>
* Alexander Warg <warg@os.inf.tu-dresden.de>
* Georg Kotheimer <georg.kotheimer@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "port.h"
#include "request_l4virtio.h"
#include "virtio_net.h"
#include <l4/cxx/pair>
#include <vector>
class L4virtio_port : public Port_iface, public Virtio_net
{
public:
explicit L4virtio_port(unsigned vq_max, unsigned num_ds, char const *name,
l4_uint8_t const *mac)
: Port_iface(name), Virtio_net(vq_max)
{
init_mem_info(num_ds);
Features hf = _dev_config.host_features(0);
if (mac)
{
_mac = Mac_addr((char const *)mac);
memcpy((void *)_dev_config.priv_config()->mac, mac,
sizeof(_dev_config.priv_config()->mac));
hf.mac() = true;
Dbg d(Dbg::Port, Dbg::Info);
d.cprintf("%s: Adding Mac '", _name);
_mac.print(d);
d.cprintf("' to host features to %x\n", hf.raw);
}
_dev_config.host_features(0) = hf.raw;
_dev_config.reset_hdr();
Dbg(Dbg::Port, Dbg::Info)
.printf("%s: Set host features to %x\n", _name,
_dev_config.host_features(0));
#if CONFIG_VNS_STATS
_mac.to_array(_stats->mac);
#endif
}
void rx_notify_disable_and_remember() override
{
kick_disable_and_remember();
}
void rx_notify_emit_and_enable() override
{
kick_emit_and_enable();
}
bool is_gone() const override
{
return obj_cap() && !obj_cap().validate().label();
}
bool tx_work_pending() const
{
return L4_LIKELY(tx_q()->ready()) && tx_q()->desc_avail();
}
std::optional<Virtio_net_request> get_tx_request()
{
return Virtio_net_request::get_request(this, tx_q());
}
void drop_requests()
{ Virtio_net_request::drop_requests(this, tx_q()); }
Result handle_request(Port_iface *src_port, Net_transfer &src,
l4_uint64_t *bytes_transferred) override
{
Virtio_vlan_mangle mangle = create_vlan_mangle(src_port);
Dbg trace(Dbg::Request, Dbg::Trace, "REQ-VIO");
trace.printf("%s: Transfer request %p.\n", _name, src.req_id());
Buffer dst;
int total = 0;
l4_uint16_t num_merged = 0;
l4_uint64_t total_merged = 0;
typedef cxx::Pair<L4virtio::Svr::Virtqueue::Head_desc, l4_uint32_t> Consumed_entry;
std::vector<Consumed_entry> consumed;
Virtio_net *dst_dev = this;
Virtqueue *dst_queue = rx_q();
L4virtio::Svr::Virtqueue::Head_desc dst_head;
L4virtio::Svr::Request_processor dst_req_proc;
Virtio_net::Hdr *dst_header = nullptr;
for (;;)
{
try
{
if (src.done())
// Request completely copied to destination.
break;
}
catch (L4virtio::Svr::Bad_descriptor &e)
{
trace.printf("\tTransfer failed, bad descriptor exception, dropping.\n");
// Handle partial transfers to destination port.
if (!consumed.empty())
// Partial transfer, rewind to before first descriptor of transfer.
dst_queue->rewind_avail(consumed.at(0).first);
else if (dst_head)
// Partial transfer, still at first _dst_head.
dst_queue->rewind_avail(dst_head);
throw;
}
/* The source data structures are already initialized, the header
is consumed and src stands at the very first real buffer.
Initialize the target data structures if necessary and fill the
header. */
if (!dst_head)
{
if (!dst_queue->ready())
return Result::Dropped;
auto r = dst_queue->next_avail();
if (L4_UNLIKELY(!r))
{
trace.printf("\tTransfer failed, destination queue depleted, dropping.\n");
// Abort incomplete transfer.
if (!consumed.empty())
dst_queue->rewind_avail(consumed.front().first);
return Result::Dropped;
}
try
{
dst_head = dst_req_proc.start(dst_dev->mem_info(), r, &dst);
}
catch (L4virtio::Svr::Bad_descriptor &e)
{
Dbg(Dbg::Request, Dbg::Warn, "REQ")
.printf("%s: bad descriptor exception: %s - %i"
" -- signal device error in destination device %p.\n",
__PRETTY_FUNCTION__, e.message(), e.error, dst_dev);
dst_dev->device_error();
return Result::Exception; // Must not touch the dst queues anymore.
}
if (!dst_header)
{
if (dst.left < sizeof(Virtio_net::Hdr))
throw L4::Runtime_error(-L4_EINVAL,
"Target buffer too small for header");
dst_header = reinterpret_cast<Virtio_net::Hdr *>(dst.pos);
trace.printf("\tCopying header to %p (size: %u)\n",
dst.pos, dst.left);
/*
* Header and csum offloading/general segmentation offloading
*
* We just copy the original header from source to
* destination and have to consider three different
* cases:
* - no flags are set
* - we got a packet that is completely checksummed
* and correctly fragmented, there is nothing to
* do other then copying.
* - virtio_net_hdr_f_needs_csum set
* - the packet is partially checksummed; if we would
* send the packet out on the wire we would have
* to calculate checksums now. But here we rely on
* the ability of our guest to handle partially
* checksummed packets and simply delegate the
* checksum calculation to them.
* - gso_type != gso_none
* - the packet needs to be segmented; if we would
* send it out on the wire we would have to
* segment it now. But again we rely on the
* ability of our guest to handle gso
*
* We currently assume that our guests negotiated
* virtio_net_f_guest_*, this needs to be checked in
* the future.
*
* We also discussed the usage of
* virtio_net_hdr_f_data_valid to remove the need to
* checksum packets at all. But since our clients send
* partially checksummed packets anyway the only
* interesting case would be a packet without
* net_hdr_f_needs_checksum set. In that case we would
* signal that we checked the checksum and the
* checksum is actually correct. Since we do not know
* the origin of the packet (it could have been send
* by an external node and could have been routed to
* u) we can not signal this without actually
* verifying the checksum. Otherwise a packet with an
* invalid checksum could be successfully delivered.
*/
total = sizeof(Virtio_net::Hdr);
src.copy_header(dst_header);
mangle.rewrite_hdr(dst_header);
dst.skip(total);
}
++num_merged;
}
bool has_dst_buffer = !dst.done();
if (!has_dst_buffer)
try
{
// The current dst buffer is full, try to get next chained buffer.
has_dst_buffer = dst_req_proc.next(dst_dev->mem_info(), &dst);
}
catch (L4virtio::Svr::Bad_descriptor &e)
{
Dbg(Dbg::Request, Dbg::Warn, "REQ")
.printf("%s: bad descriptor exception: %s - %i"
" -- signal device error in destination device %p.\n",
__PRETTY_FUNCTION__, e.message(), e.error, dst_dev);
dst_dev->device_error();
return Result::Exception; // Must not touch the dst queues anymore.
}
if (has_dst_buffer)
{
auto &src_buf = src.cur_buf();
trace.printf("\tCopying %p#%p:%u (%x) -> %p#%p:%u (%x)\n",
src_port, src_buf.pos, src_buf.left, src_buf.left,
static_cast<Port_iface *>(this),
dst.pos, dst.left, dst.left);
total += mangle.copy_pkt(dst, src_buf);
}
else if (negotiated_features().mrg_rxbuf())
{
// save descriptor information for later
trace.printf("\tSaving descriptor for later\n");
consumed.push_back(Consumed_entry(dst_head, total));
total_merged += total;
total = 0;
dst_head = L4virtio::Svr::Virtqueue::Head_desc();
}
else
{
trace.printf("\tTransfer failed, destination buffer too small, dropping.\n");
// Abort incomplete transfer.
dst_queue->rewind_avail(dst_head);
return Result::Dropped;
}
}
/*
* Finalize the Request delivery. Call `finish()` on the destination
* port's receive queue, which will result in triggering the destination
* client IRQ.
*/
if (!dst_header)
{
if (!total)
trace.printf("\tTransfer - not started yet, dropping\n");
return Result::Dropped;
}
if (consumed.empty())
{
assert(dst_head);
assert(num_merged == 1);
trace.printf("\tTransfer - Invoke dst_queue->finish()\n");
dst_header->num_buffers = 1;
dst_queue->finish(dst_head, dst_dev, total);
*bytes_transferred = total;
}
else
{
assert(dst_head);
dst_header->num_buffers = num_merged;
consumed.push_back(Consumed_entry(dst_head, total));
trace.printf("\tTransfer - Invoke dst_queue->finish(iter)\n");
*bytes_transferred = total + total_merged;
dst_queue->finish(consumed.begin(), consumed.end(), dst_dev);
}
return Result::Delivered;
}
};
L4::Cap_base::validate
l4_msgtag_t validate(l4_utcb_t *u=l4_utcb()) const noexcept
Check whether a capability is present (refers to an object).
Definition capability:81
L4virtio::Svr::Device_t::device_error
void device_error()
Transition device into DEVICE_NEEDS_RESET state.
Definition l4virtio:1024
L4virtio::Svr::Device_t< No_custom_data >::init_mem_info
void init_mem_info(unsigned num)
Definition l4virtio:1012
L4virtio::Svr::Device_t::mem_info
Mem_list const * mem_info() const
Get the memory region list used for this device.
Definition l4virtio:898
L4virtio::Svr::Request_processor
Encapsulate the state for processing a VIRTIO request.
Definition virtio:473
L4virtio::Svr::Request_processor::next
bool next(DESC_MAN *dm, ARGS... args)
Switch to the next descriptor in a descriptor chain.
Definition virtio:570
L4virtio::Svr::Request_processor::start
void start(DESC_MAN *dm, Virtqueue *ring, Virtqueue::Head_desc const &request, ARGS... args)
Start processing a new request.
Definition virtio:501
L4virtio::Svr::Virtqueue::Head_desc
VIRTIO request, essentially a descriptor from the available ring.
Definition virtio:94
L4virtio::Svr::Virtqueue::desc_avail
bool desc_avail() const
Test for available descriptors.
Definition virtio:175
L4virtio::Svr::Virtqueue::next_avail
Request next_avail()
Get the next available descriptor from the available ring.
Definition virtio:136
L4virtio::Svr::Virtqueue::finish
void finish(Head_desc &d, QUEUE_OBSERVER *o, l4_uint32_t len=0)
Add a descriptor to the used ring, and notify an observer.
Definition virtio:240
L4virtio::Svr::Virtqueue::rewind_avail
void rewind_avail(Head_desc const &d)
Return unfinished descriptors to the available ring, i.e.
Definition virtio:160
L4virtio::Virtqueue::ready
bool ready() const
Test if this queue is in working state.
Definition virtqueue:400
L4virtio_port::drop_requests
void drop_requests()
Drop all requests pending in the transmission queue.
Definition port_l4virtio.h:103
L4virtio_port::L4virtio_port
L4virtio_port(unsigned vq_max, unsigned num_ds, char const *name, l4_uint8_t const *mac)
Create a Virtio net port object.
Definition port_l4virtio.h:42
L4virtio_port::tx_work_pending
bool tx_work_pending() const
Check whether there is any work pending on the transmission queue.
Definition port_l4virtio.h:87
L4virtio_port::get_tx_request
std::optional< Virtio_net_request > get_tx_request()
Get one request from the transmission queue.
Definition port_l4virtio.h:93
Mac_addr
A wrapper class around the value of a MAC address.
Definition mac_addr.h:20
Net_transfer
A network request to only a single destination.
Definition request.h:34
Net_transfer::copy_header
virtual void copy_header(Virtio_net::Hdr *dst_header) const =0
Populate the virtio-net header for the destination.
Net_transfer::req_id
void const * req_id() const
Identifier for the underlying Net_request, used for logging purposes.
Definition request.h:41
Net_transfer::done
virtual bool done()=0
Check whether the transfer has been completed, i.e.
Net_transfer::cur_buf
Buffer & cur_buf()
Buffer containing (a part of) the packet data.
Definition request.h:54
Virtio_net_request::get_request
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.
Definition request_l4virtio.h:199
Virtio_net_request::drop_requests
static void drop_requests(Virtio_net *dev, L4virtio::Svr::Virtqueue *queue)
Drop all requests of a specific queue.
Definition request_l4virtio.h:172
Virtio_net
The Base class of a Port.
Definition virtio_net.h:74
Virtio_net::rx_q
Virtqueue * rx_q()
Getter for the receive queue.
Definition virtio_net.h:307
Virtio_net::tx_q
Virtqueue * tx_q()
Getter for the transmission queue.
Definition virtio_net.h:305
Virtio_vlan_mangle
Class for VLAN packet rewriting.
Definition vlan.h:37
Virtio_vlan_mangle::rewrite_hdr
void rewrite_hdr(Virtio_net::Hdr *hdr)
Rewrite the virtio network header.
Definition vlan.h:142
Virtio_vlan_mangle::copy_pkt
l4_uint32_t copy_pkt(Buffer &dst, Buffer &src)
Copy packet from src to dst.
Definition vlan.h:93
l4_uint8_t
unsigned char l4_uint8_t
Unsigned 8bit value.
Definition l4int.h:25
l4_uint16_t
unsigned short int l4_uint16_t
Unsigned 16bit value.
Definition l4int.h:27
l4_uint64_t
unsigned long long l4_uint64_t
Unsigned 64bit value.
Definition l4int.h:31
L4_EINVAL
@ L4_EINVAL
Invalid argument.
Definition err.h:46
L4_UNLIKELY
#define L4_UNLIKELY(x)
Expression is unlikely to execute.
Definition compiler.h:284
L4_LIKELY
#define L4_LIKELY(x)
Expression is likely to execute.
Definition compiler.h:283
pair
Pair implementation.
Buffer
Data buffer used to transfer packets.
Definition virtio_net_buffer.h:20
L4::Epiface_t0< L4virtio::Device, L4::Epiface >::obj_cap
Cap< L4virtio::Device > obj_cap() const
Definition ipc_epiface:269
L4virtio::Svr::Bad_descriptor::message
char const * message() const
Get a human readable description of the error code.
Definition virtio:430
L4virtio::Svr::Data_buffer::left
l4_uint32_t left
Bytes left in buffer.
Definition virtio:309
L4virtio::Svr::Data_buffer::pos
char * pos
Current buffer position.
Definition virtio:308
L4virtio::Svr::Data_buffer::skip
l4_uint32_t skip(l4_uint32_t bytes)
Skip given number of bytes in this buffer.
Definition virtio:375
L4virtio::Svr::Data_buffer::done
bool done() const
Check if there are no more bytes left in the buffer.
Definition virtio:388
L4virtio::Svr::Dev_features::raw
l4_uint32_t raw
The raw value of the features bitmap.
Definition virtio:68
cxx::Pair
Pair of two values.
Definition pair:28
l4_msgtag_t::label
long label() const L4_NOTHROW
Get the protocol value.
Definition types.h:158