/* * Copyright (C) 2020 Kernkonzept GmbH. * Author(s): Jan Klötzke * * This file is distributed under the terms of the GNU General Public * License, version 2. Please see the COPYING-GPL-2 file for details. */ #pragma once #include #include #include #include namespace Virtio_net_switch { // Statistics for one port. struct Port_statistics { l4_uint64_t tx_num; // number of successful send requests l4_uint64_t tx_dropped; // number of dropped send request l4_uint64_t tx_bytes; // bytes successfully sent l4_uint64_t rx_num; // number of successful receive requests l4_uint64_t rx_dropped; // number of dropped receive requests l4_uint64_t rx_bytes; // bytes successfully received unsigned char mac[6]; // MAC address of a port char name[20]; // name of a port unsigned char in_use; // 1 iff the data structure is currently // in use, 0 otherwise }; // Base statistics data structure, resides at the beginning of shared memory struct Statistics { // This value increases on any change in the data structure. E.g. when a // port on the switch is created or discarded. l4_uint64_t age; // The maximum number of ports that the switch supports. l4_uint64_t max_ports; struct Port_statistics port_stats[]; }; class Statistics_if : public L4::Kobject_t { public: /** * Get shared memory buffer containing port statistics. * * \param[out] ds Capability of the dataspace containing port statistics. * * \retval 0 Success * \retval <0 Error */ L4_INLINE_RPC(long, get_buffer, (L4::Ipc::Out > ds)); /** * Instruct the switch to update the statistics information in the shared * memory buffer. * * \retval 0 Success * \retval <0 Error */ L4_INLINE_RPC(long, sync, ()); typedef L4::Typeid::Rpcs Rpcs; }; /** * Client interface. * * The data is only updated on Monitor::sync(). */ class Monitor { public: Monitor(L4Re::Util::Unique_del_cap cap) : _cap(std::move(cap)) { _ds = L4Re::Util::make_unique_cap(); L4Re::chksys(_cap->get_buffer(_ds.get()), "Could not get stats dataspace from switch."); void *addr; L4Re::chksys(L4Re::Env::env()->rm()->attach(&addr, _ds->size(), L4Re::Rm::F::Search_addr | L4Re::Rm::F::R, L4::Ipc::make_cap(_ds.get(), L4_CAP_FPAGE_RO)), "Could not attach stats dataspace."); _stats = reinterpret_cast(addr); } ~Monitor() { L4Re::Env::env()->rm()->detach(reinterpret_cast(_stats), 0); } Port_statistics *get_port_stats(char const *name) const { for(size_t i = 0; i < _stats->max_ports; ++i) { if (_stats->port_stats[i].in_use && !strncmp (_stats->port_stats[i].name, name, sizeof(_stats->port_stats[i].name))) return &_stats->port_stats[i]; } return nullptr; } bool get_port_mac(char const *name, unsigned char *mac) const { for (l4_uint64_t i = 0; i < _stats->max_ports; ++i) { if (_stats->port_stats[i].in_use && !strncmp (_stats->port_stats[i].name, name, 20)) { std::memcpy(mac, _stats->port_stats[i].mac, 6); return true; } } return false; } void sync() const { L4Re::chksys(_cap->sync(), "Synchronizing statistics information failed.\n"); } l4_uint64_t age() const { return _stats->age; } private: L4Re::Util::Unique_cap _ds; L4Re::Util::Unique_del_cap _cap; Statistics *_stats; }; class Port_monitor { private: std::shared_ptr _m; char _name[20]; l4_uint64_t _age; Port_statistics *_stats; //only valid for the given age public: /** * The statistics information is only valid after calling Monitor::sync(). */ Port_monitor(std::shared_ptr m, char const *name) : _m(m) { strncpy(_name, name, sizeof(_name) - 1); _name[sizeof(_name) - 1] = '\0'; _age = _m->age(); _stats = _m->get_port_stats(_name); } /** * Get statistics for this port. * * *NOTE* This data is updated on Monitor::sync(). It is up to the client to * call sync() appropriately. */ void stats(l4_uint64_t *tx_num, l4_uint64_t *tx_dropped, l4_uint64_t *tx_bytes, l4_uint64_t *rx_num, l4_uint64_t *rx_dropped, l4_uint64_t *rx_bytes) { // ports have changed if (_age != _m->age()) { _stats = _m->get_port_stats(_name); _age = _m->age(); } // no port found if (!_stats) { tx_num = tx_dropped = tx_bytes = rx_num = rx_dropped = rx_bytes = 0; return; } *tx_num = _stats->tx_num; *tx_dropped = _stats->tx_dropped; *tx_bytes = _stats->tx_bytes; *rx_num = _stats->rx_num; *rx_dropped = _stats->rx_dropped; *rx_bytes = _stats->rx_bytes; } }; }