Files
moslab-code/src/l4/pkg/virtio-net-switch/lib/include/stats.h
2025-09-12 15:55:45 +02:00

193 lines
5.2 KiB
C++

/*
* Copyright (C) 2020 Kernkonzept GmbH.
* Author(s): Jan Klötzke <jan.kloetzke@kernkonzept.com>
*
* 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 <l4/sys/capability>
#include <l4/sys/cxx/ipc_iface>
#include <l4/re/dataspace>
#include <l4/re/util/unique_cap>
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<Statistics_if, L4::Kobject, 1>
{
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<L4::Cap<L4Re::Dataspace> > 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<get_buffer_t, sync_t> Rpcs;
};
/**
* Client interface.
*
* The data is only updated on Monitor::sync().
*/
class Monitor
{
public:
Monitor(L4Re::Util::Unique_del_cap<Statistics_if> cap)
: _cap(std::move(cap))
{
_ds = L4Re::Util::make_unique_cap<L4Re::Dataspace>();
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<Virtio_net_switch::Statistics *>(addr);
}
~Monitor()
{
L4Re::Env::env()->rm()->detach(reinterpret_cast<l4_addr_t>(_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<L4Re::Dataspace> _ds;
L4Re::Util::Unique_del_cap<Statistics_if> _cap;
Statistics *_stats;
};
class Port_monitor
{
private:
std::shared_ptr<Monitor> _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<Monitor> 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;
}
};
}