From 103dcdeea8f6f2adb453b12e4c1b2be1d8c744c9 Mon Sep 17 00:00:00 2001 From: Sebastian Sumpf Date: Tue, 28 Jan 2020 13:41:51 +0100 Subject: [PATCH] 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 --- .../server/vmm/spec/arm_v8/virtio_console.h | 3 ++ .../server/vmm/spec/arm_v8/virtio_device.h | 54 ++++++++++++++----- .../src/server/vmm/spec/arm_v8/virtio_net.h | 54 +++++++++++++++---- 3 files changed, 86 insertions(+), 25 deletions(-) diff --git a/repos/os/src/server/vmm/spec/arm_v8/virtio_console.h b/repos/os/src/server/vmm/spec/arm_v8/virtio_console.h index 3c4cd01b19..6cc5139fb5 100644 --- a/repos/os/src/server/vmm/spec/arm_v8/virtio_console.h +++ b/repos/os/src/server/vmm/spec/arm_v8/virtio_console.h @@ -34,6 +34,8 @@ class Vmm::Virtio_console : public Virtio_device { auto read = [&] (addr_t data, size_t size) { + if (!_terminal.avail()) return 0ul; + size_t length = _terminal.read((void *)data, size); return length; }; @@ -58,6 +60,7 @@ class Vmm::Virtio_console : public Virtio_device _assert_irq(); } + Register _device_specific_features() { return 0; } public: Virtio_console(const char * const name, diff --git a/repos/os/src/server/vmm/spec/arm_v8/virtio_device.h b/repos/os/src/server/vmm/spec/arm_v8/virtio_device.h index 67a7c59671..c83c5bc020 100644 --- a/repos/os/src/server/vmm/spec/arm_v8/virtio_device.h +++ b/repos/os/src/server/vmm/spec/arm_v8/virtio_device.h @@ -100,6 +100,8 @@ class Vmm::Virtio_avail : public Genode::Mmio struct Flags : Register<0x0, 16> { }; struct Idx : Register<0x2, 16> { }; struct Ring : Register_array<0x4, 16, Virtio_queue_data::MAX_QUEUE_SIZE, 16> { }; + + bool inject_irq() { return (read() & 1) == 0; } }; @@ -135,24 +137,36 @@ class Vmm::Virtio_queue Virtio_avail _avail { _ram.local_address(_data.driver(), 6 + 2 * _data.num) }; Virtio_used _used { _ram.local_address(_data.device(), 6 + 8 * _data.num) }; - uint16_t _idx { 0 }; - uint32_t _length { _data.num }; - bool _tx { _data.tx }; + uint16_t _idx { 0 }; + uint32_t _length { _data.num }; + bool _tx { _data.tx }; + bool const _verbose { false }; public: Virtio_queue(Virtio_queue_data &data, Ram &ram) : _data(data), _ram(ram) { } + + bool verbose() const { return _verbose; } + template bool notify(FUNC func) { + asm volatile ("dmb ish" : : : "memory"); uint16_t used_idx = _used.read(); uint16_t avail_idx = _avail.read(); - uint16_t queue_idx = _tx ? avail_idx : used_idx + 1; 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()); + + while (_idx != avail_idx && written < _length) { + uint16_t id = _avail.read(_idx % _length); /* make sure id stays in ring */ @@ -175,15 +189,24 @@ class Vmm::Virtio_queue Virtio_used::Elem::access_t elem = 0; Virtio_used::Elem::Id::set(elem, id); Virtio_used::Elem::Length::set(elem, length); - _used.write(elem, id); + _used.write(elem, _idx % _length); written++; _idx++; if (used_idx + written == avail_idx) break; } - _used.write(used_idx + written); + if (written) { + used_idx += written; + _used.write(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 Register _device_specific_features() = 0; /*************** ** Registers ** @@ -257,22 +280,23 @@ class Vmm::Virtio_device : public Vmm::Mmio_device VIRTIO_F_VERSION_1 = 1, }; + Virtio_device &_device; Mmio_register &_selector; Register read(Address_range&, Cpu&) override { /* lower 32 bit */ - if (_selector.value() == 0) return 0; + if (_selector.value() == 0) return _device._device_specific_features(); /* upper 32 bit */ return VIRTIO_F_VERSION_1; } - DeviceFeatures(Mmio_register &selector) + DeviceFeatures(Virtio_device &device, Mmio_register &selector) : 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 { @@ -282,7 +306,9 @@ class Vmm::Virtio_device : public Vmm::Mmio_device void write(Address_range&, Cpu&, Register reg) override { - if (_selector.value() == 0) _lower = reg; + if (_selector.value() == 0) { + _lower = reg; + } _upper = reg; } diff --git a/repos/os/src/server/vmm/spec/arm_v8/virtio_net.h b/repos/os/src/server/vmm/spec/arm_v8/virtio_net.h index 9ee1a7c981..d25ee2b991 100644 --- a/repos/os/src/server/vmm/spec/arm_v8/virtio_net.h +++ b/repos/os/src/server/vmm/spec/arm_v8/virtio_net.h @@ -35,7 +35,8 @@ class Vmm::Virtio_net : public Virtio_device enum { BUF_SIZE = Nic::Packet_allocator::DEFAULT_PACKET_SIZE * 128, 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 _handler; @@ -52,26 +53,32 @@ class Vmm::Virtio_net : public Virtio_device /* RX */ 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(); 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), _nic.rx()->packet_content(rx_packet), - sz); + sz - NIC_HEADER_SIZE); _nic.rx()->acknowledge_packet(rx_packet); + Genode::memset((void*)data, 0, NIC_HEADER_SIZE); + return sz; }; if (!_queue[RX].constructed()) return; - bool progress = false; - while (_nic.rx()->packet_avail() && _nic.rx()->ready_to_ack()) { - if (!_queue[RX]->notify(recv)) break; - progress = true; - } + bool irq = _queue[RX]->notify(recv); - if (progress) _assert_irq(); + if (irq) _assert_irq(); } void _tx() @@ -86,7 +93,7 @@ class Vmm::Virtio_net : public Virtio_device try { tx_packet = _nic.tx()->alloc_packet(size); } catch (Nic::Session::Tx::Source::Packet_alloc_failed) { - return 0lu; } + return 0ul; } Genode::memcpy(_nic.tx()->packet_content(tx_packet), (void *)data, size); @@ -108,10 +115,33 @@ class Vmm::Virtio_net : public Virtio_device void _notify(unsigned /* idx */) override { - _rx(); _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: Virtio_net(const char * const name, @@ -122,13 +152,15 @@ class Vmm::Virtio_net : public Virtio_device Mmio_bus &bus, Ram &ram, 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), _handler(cpu, _env.ep(), *this, &Virtio_net::_handle) { /* set device ID to network */ _device_id(0x1); + add(_config_area); + _nic.tx_channel()->sigh_ready_to_submit(_handler); _nic.tx_channel()->sigh_ack_avail (_handler); _nic.rx_channel()->sigh_ready_to_ack (_handler);