vmm: ARMv8 virtio improvements
net: * increase queue size to 1024 (more stable on Linux) * use mac address from Nic session instead of random one * handle data that is larger than rx descriptor correctly (copy less) * clear descriptor header (12 bytes) on rx generic: * always use 'avail_idx' (tx and rx) * added barriers when reading/writing queues (TMP) Ref #3620
This commit is contained in:
parent
f77531138a
commit
103dcdeea8
|
@ -34,6 +34,8 @@ class Vmm::Virtio_console : public Virtio_device
|
||||||
{
|
{
|
||||||
auto read = [&] (addr_t data, size_t size)
|
auto read = [&] (addr_t data, size_t size)
|
||||||
{
|
{
|
||||||
|
if (!_terminal.avail()) return 0ul;
|
||||||
|
|
||||||
size_t length = _terminal.read((void *)data, size);
|
size_t length = _terminal.read((void *)data, size);
|
||||||
return length;
|
return length;
|
||||||
};
|
};
|
||||||
|
@ -58,6 +60,7 @@ class Vmm::Virtio_console : public Virtio_device
|
||||||
_assert_irq();
|
_assert_irq();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Register _device_specific_features() { return 0; }
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Virtio_console(const char * const name,
|
Virtio_console(const char * const name,
|
||||||
|
|
|
@ -100,6 +100,8 @@ class Vmm::Virtio_avail : public Genode::Mmio
|
||||||
struct Flags : Register<0x0, 16> { };
|
struct Flags : Register<0x0, 16> { };
|
||||||
struct Idx : Register<0x2, 16> { };
|
struct Idx : Register<0x2, 16> { };
|
||||||
struct Ring : Register_array<0x4, 16, Virtio_queue_data::MAX_QUEUE_SIZE, 16> { };
|
struct Ring : Register_array<0x4, 16, Virtio_queue_data::MAX_QUEUE_SIZE, 16> { };
|
||||||
|
|
||||||
|
bool inject_irq() { return (read<Flags>() & 1) == 0; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -135,24 +137,36 @@ class Vmm::Virtio_queue
|
||||||
Virtio_avail _avail { _ram.local_address(_data.driver(), 6 + 2 * _data.num) };
|
Virtio_avail _avail { _ram.local_address(_data.driver(), 6 + 2 * _data.num) };
|
||||||
Virtio_used _used { _ram.local_address(_data.device(), 6 + 8 * _data.num) };
|
Virtio_used _used { _ram.local_address(_data.device(), 6 + 8 * _data.num) };
|
||||||
|
|
||||||
uint16_t _idx { 0 };
|
uint16_t _idx { 0 };
|
||||||
uint32_t _length { _data.num };
|
uint32_t _length { _data.num };
|
||||||
bool _tx { _data.tx };
|
bool _tx { _data.tx };
|
||||||
|
bool const _verbose { false };
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Virtio_queue(Virtio_queue_data &data, Ram &ram)
|
Virtio_queue(Virtio_queue_data &data, Ram &ram)
|
||||||
: _data(data), _ram(ram) { }
|
: _data(data), _ram(ram) { }
|
||||||
|
|
||||||
|
|
||||||
|
bool verbose() const { return _verbose; }
|
||||||
|
|
||||||
template <typename FUNC>
|
template <typename FUNC>
|
||||||
bool notify(FUNC func)
|
bool notify(FUNC func)
|
||||||
{
|
{
|
||||||
|
asm volatile ("dmb ish" : : : "memory");
|
||||||
uint16_t used_idx = _used.read<Virtio_used::Idx>();
|
uint16_t used_idx = _used.read<Virtio_used::Idx>();
|
||||||
uint16_t avail_idx = _avail.read<Virtio_avail::Idx>();
|
uint16_t avail_idx = _avail.read<Virtio_avail::Idx>();
|
||||||
uint16_t queue_idx = _tx ? avail_idx : used_idx + 1;
|
|
||||||
|
|
||||||
uint16_t written = 0;
|
uint16_t written = 0;
|
||||||
while (_idx != queue_idx && written < _length) {
|
|
||||||
|
if (_verbose)
|
||||||
|
Genode::log(_length > 64 ? "net $ " : "console $ ",
|
||||||
|
"[", _tx ? "tx] " : "rx] ",
|
||||||
|
"idx: ", _idx, " avail_idx: ", avail_idx, " used_idx: ", used_idx,
|
||||||
|
" queue length: ", _length, " avail flags: ", _avail.read<Virtio_avail::Flags>());
|
||||||
|
|
||||||
|
while (_idx != avail_idx && written < _length) {
|
||||||
|
|
||||||
uint16_t id = _avail.read<Virtio_avail::Ring>(_idx % _length);
|
uint16_t id = _avail.read<Virtio_avail::Ring>(_idx % _length);
|
||||||
|
|
||||||
/* make sure id stays in ring */
|
/* make sure id stays in ring */
|
||||||
|
@ -175,15 +189,24 @@ class Vmm::Virtio_queue
|
||||||
Virtio_used::Elem::access_t elem = 0;
|
Virtio_used::Elem::access_t elem = 0;
|
||||||
Virtio_used::Elem::Id::set(elem, id);
|
Virtio_used::Elem::Id::set(elem, id);
|
||||||
Virtio_used::Elem::Length::set(elem, length);
|
Virtio_used::Elem::Length::set(elem, length);
|
||||||
_used.write<Virtio_used::Elem>(elem, id);
|
_used.write<Virtio_used::Elem>(elem, _idx % _length);
|
||||||
written++; _idx++;
|
written++; _idx++;
|
||||||
|
|
||||||
if (used_idx + written == avail_idx) break;
|
if (used_idx + written == avail_idx) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
_used.write<Virtio_used::Idx>(used_idx + written);
|
if (written) {
|
||||||
|
used_idx += written;
|
||||||
|
_used.write<Virtio_used::Idx>(used_idx);
|
||||||
|
asm volatile ("dmb ish" : : : "memory");
|
||||||
|
}
|
||||||
|
|
||||||
return written > 0;
|
if (written && _verbose)
|
||||||
|
Genode::log(_length > 64 ? "net $ " : "console $ ",
|
||||||
|
"[", _tx ? "tx] " : "rx] ", "updated used_idx: ", used_idx,
|
||||||
|
" written: ", written);
|
||||||
|
|
||||||
|
return written > 0 && _avail.inject_irq();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -245,7 +268,7 @@ class Vmm::Virtio_device : public Vmm::Mmio_device
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void _notify(unsigned idx) = 0;
|
virtual void _notify(unsigned idx) = 0;
|
||||||
|
virtual Register _device_specific_features() = 0;
|
||||||
|
|
||||||
/***************
|
/***************
|
||||||
** Registers **
|
** Registers **
|
||||||
|
@ -257,22 +280,23 @@ class Vmm::Virtio_device : public Vmm::Mmio_device
|
||||||
VIRTIO_F_VERSION_1 = 1,
|
VIRTIO_F_VERSION_1 = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Virtio_device &_device;
|
||||||
Mmio_register &_selector;
|
Mmio_register &_selector;
|
||||||
|
|
||||||
Register read(Address_range&, Cpu&) override
|
Register read(Address_range&, Cpu&) override
|
||||||
{
|
{
|
||||||
/* lower 32 bit */
|
/* lower 32 bit */
|
||||||
if (_selector.value() == 0) return 0;
|
if (_selector.value() == 0) return _device._device_specific_features();
|
||||||
|
|
||||||
/* upper 32 bit */
|
/* upper 32 bit */
|
||||||
return VIRTIO_F_VERSION_1;
|
return VIRTIO_F_VERSION_1;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceFeatures(Mmio_register &selector)
|
DeviceFeatures(Virtio_device &device, Mmio_register &selector)
|
||||||
: Mmio_register("DeviceFeatures", Mmio_register::RO, 0x10, 4),
|
: Mmio_register("DeviceFeatures", Mmio_register::RO, 0x10, 4),
|
||||||
_selector(selector)
|
_device(device), _selector(selector)
|
||||||
{ }
|
{ }
|
||||||
} _device_features { _reg_container.regs[4] };
|
} _device_features { *this, _reg_container.regs[4] };
|
||||||
|
|
||||||
struct DriverFeatures : Mmio_register
|
struct DriverFeatures : Mmio_register
|
||||||
{
|
{
|
||||||
|
@ -282,7 +306,9 @@ class Vmm::Virtio_device : public Vmm::Mmio_device
|
||||||
|
|
||||||
void write(Address_range&, Cpu&, Register reg) override
|
void write(Address_range&, Cpu&, Register reg) override
|
||||||
{
|
{
|
||||||
if (_selector.value() == 0) _lower = reg;
|
if (_selector.value() == 0) {
|
||||||
|
_lower = reg;
|
||||||
|
}
|
||||||
_upper = reg;
|
_upper = reg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,8 @@ class Vmm::Virtio_net : public Virtio_device
|
||||||
enum { BUF_SIZE = Nic::Packet_allocator::DEFAULT_PACKET_SIZE * 128,
|
enum { BUF_SIZE = Nic::Packet_allocator::DEFAULT_PACKET_SIZE * 128,
|
||||||
NIC_HEADER_SIZE = 12 };
|
NIC_HEADER_SIZE = 12 };
|
||||||
|
|
||||||
Nic::Connection _nic { _env, &_tx_alloc, BUF_SIZE, BUF_SIZE };
|
Nic::Connection _nic { _env, &_tx_alloc, BUF_SIZE, BUF_SIZE };
|
||||||
|
Nic::Mac_address _mac { _nic.mac_address() };
|
||||||
|
|
||||||
Cpu::Signal_handler<Virtio_net> _handler;
|
Cpu::Signal_handler<Virtio_net> _handler;
|
||||||
|
|
||||||
|
@ -52,26 +53,32 @@ class Vmm::Virtio_net : public Virtio_device
|
||||||
/* RX */
|
/* RX */
|
||||||
auto recv = [&] (addr_t data, size_t size)
|
auto recv = [&] (addr_t data, size_t size)
|
||||||
{
|
{
|
||||||
|
if (!_nic.rx()->packet_avail() || !_nic.rx()->ready_to_ack())
|
||||||
|
return 0ul;
|
||||||
|
|
||||||
Nic::Packet_descriptor const rx_packet = _nic.rx()->get_packet();
|
Nic::Packet_descriptor const rx_packet = _nic.rx()->get_packet();
|
||||||
|
|
||||||
size_t sz = Genode::min(size, rx_packet.size() + NIC_HEADER_SIZE);
|
size_t sz = Genode::min(size, rx_packet.size() + NIC_HEADER_SIZE);
|
||||||
|
|
||||||
|
if (_queue[RX]->verbose() && sz < rx_packet.size() + NIC_HEADER_SIZE)
|
||||||
|
Genode::warning("[rx] trim packet from ",
|
||||||
|
rx_packet.size() + NIC_HEADER_SIZE, " -> ", sz, " bytes");
|
||||||
|
|
||||||
Genode::memcpy((void *)(data + NIC_HEADER_SIZE),
|
Genode::memcpy((void *)(data + NIC_HEADER_SIZE),
|
||||||
_nic.rx()->packet_content(rx_packet),
|
_nic.rx()->packet_content(rx_packet),
|
||||||
sz);
|
sz - NIC_HEADER_SIZE);
|
||||||
_nic.rx()->acknowledge_packet(rx_packet);
|
_nic.rx()->acknowledge_packet(rx_packet);
|
||||||
|
|
||||||
|
Genode::memset((void*)data, 0, NIC_HEADER_SIZE);
|
||||||
|
|
||||||
return sz;
|
return sz;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!_queue[RX].constructed()) return;
|
if (!_queue[RX].constructed()) return;
|
||||||
|
|
||||||
bool progress = false;
|
bool irq = _queue[RX]->notify(recv);
|
||||||
while (_nic.rx()->packet_avail() && _nic.rx()->ready_to_ack()) {
|
|
||||||
if (!_queue[RX]->notify(recv)) break;
|
|
||||||
progress = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (progress) _assert_irq();
|
if (irq) _assert_irq();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _tx()
|
void _tx()
|
||||||
|
@ -86,7 +93,7 @@ class Vmm::Virtio_net : public Virtio_device
|
||||||
try {
|
try {
|
||||||
tx_packet = _nic.tx()->alloc_packet(size); }
|
tx_packet = _nic.tx()->alloc_packet(size); }
|
||||||
catch (Nic::Session::Tx::Source::Packet_alloc_failed) {
|
catch (Nic::Session::Tx::Source::Packet_alloc_failed) {
|
||||||
return 0lu; }
|
return 0ul; }
|
||||||
|
|
||||||
Genode::memcpy(_nic.tx()->packet_content(tx_packet),
|
Genode::memcpy(_nic.tx()->packet_content(tx_packet),
|
||||||
(void *)data, size);
|
(void *)data, size);
|
||||||
|
@ -108,10 +115,33 @@ class Vmm::Virtio_net : public Virtio_device
|
||||||
|
|
||||||
void _notify(unsigned /* idx */) override
|
void _notify(unsigned /* idx */) override
|
||||||
{
|
{
|
||||||
_rx();
|
|
||||||
_tx();
|
_tx();
|
||||||
|
_rx();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Register _device_specific_features() override
|
||||||
|
{
|
||||||
|
enum { VIRTIO_NET_F_MAC = 1u << 5 };
|
||||||
|
return VIRTIO_NET_F_MAC;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ConfigArea : Mmio_register
|
||||||
|
{
|
||||||
|
Nic::Mac_address &mac;
|
||||||
|
|
||||||
|
Register read(Address_range& range, Cpu&) override
|
||||||
|
{
|
||||||
|
if (range.start > 5) return 0;
|
||||||
|
|
||||||
|
return mac.addr[range.start];
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigArea(Nic::Mac_address &mac)
|
||||||
|
: Mmio_register("ConfigArea", Mmio_register::RO, 0x100, 8),
|
||||||
|
mac(mac)
|
||||||
|
{ }
|
||||||
|
} _config_area { _mac };
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Virtio_net(const char * const name,
|
Virtio_net(const char * const name,
|
||||||
|
@ -122,13 +152,15 @@ class Vmm::Virtio_net : public Virtio_device
|
||||||
Mmio_bus &bus,
|
Mmio_bus &bus,
|
||||||
Ram &ram,
|
Ram &ram,
|
||||||
Genode::Env &env)
|
Genode::Env &env)
|
||||||
: Virtio_device(name, addr, size, irq, cpu, bus, ram, 128),
|
: Virtio_device(name, addr, size, irq, cpu, bus, ram, 1024),
|
||||||
_env(env),
|
_env(env),
|
||||||
_handler(cpu, _env.ep(), *this, &Virtio_net::_handle)
|
_handler(cpu, _env.ep(), *this, &Virtio_net::_handle)
|
||||||
{
|
{
|
||||||
/* set device ID to network */
|
/* set device ID to network */
|
||||||
_device_id(0x1);
|
_device_id(0x1);
|
||||||
|
|
||||||
|
add(_config_area);
|
||||||
|
|
||||||
_nic.tx_channel()->sigh_ready_to_submit(_handler);
|
_nic.tx_channel()->sigh_ready_to_submit(_handler);
|
||||||
_nic.tx_channel()->sigh_ack_avail (_handler);
|
_nic.tx_channel()->sigh_ack_avail (_handler);
|
||||||
_nic.rx_channel()->sigh_ready_to_ack (_handler);
|
_nic.rx_channel()->sigh_ready_to_ack (_handler);
|
||||||
|
|
Loading…
Reference in New Issue