use std::{
ffi::CString,
mem::ManuallyDrop,
sync::atomic::{AtomicU64, Ordering},
};
use crate::{
bindings::{
vlib_combined_counter_main_t, vlib_counter_t, vlib_free_combined_counter,
vlib_free_simple_counter, vlib_helper_zero_combined_counter,
vlib_helper_zero_simple_counter, vlib_simple_counter_main_t,
vlib_validate_combined_counter, vlib_validate_simple_counter,
},
vlib::{BarrierHeldMainRef, MainRef},
vppinfra,
};
pub struct SimpleCounter {
counter: vlib_simple_counter_main_t,
}
impl SimpleCounter {
pub fn new(name: &str, stat_segment_name: &str) -> Self {
let name_cstr = CString::new(name).unwrap();
let stats_segment_name_cstr = CString::new(stat_segment_name).unwrap();
Self {
counter: vlib_simple_counter_main_t {
counters: std::ptr::null_mut(),
name: name_cstr.into_raw(),
stat_segment_name: stats_segment_name_cstr.into_raw(),
stats_entry_index: 0,
},
}
}
pub fn allocate_index(&self, _vm: &BarrierHeldMainRef, index: u32) -> SimpleCounterIndex<'_> {
unsafe {
vlib_validate_simple_counter(std::ptr::addr_of!(self.counter).cast_mut(), index);
vlib_helper_zero_simple_counter(std::ptr::addr_of!(self.counter).cast_mut(), index);
SimpleCounterIndex::from_parts(self, index)
}
}
}
impl Drop for SimpleCounter {
fn drop(&mut self) {
unsafe {
let _name_cstr = CString::from_raw(self.counter.name);
let _stat_segment_name_cstr = CString::from_raw(self.counter.stat_segment_name);
vlib_free_simple_counter(std::ptr::addr_of_mut!(self.counter));
}
}
}
unsafe impl Send for SimpleCounter {}
unsafe impl Sync for SimpleCounter {}
pub struct SimpleCounterIndex<'counter> {
counter: &'counter SimpleCounter,
index: u32,
}
impl<'counter> SimpleCounterIndex<'counter> {
pub fn into_parts(self) -> (&'counter SimpleCounter, u32) {
let me = ManuallyDrop::new(self);
(me.counter, me.index)
}
pub unsafe fn from_parts(counter: &'counter SimpleCounter, index: u32) -> Self {
Self { counter, index }
}
fn counter_ptr(&self, vm: &MainRef) -> *mut u64 {
let thread_index = vm.thread_index();
unsafe {
let this_thread_counters = *self.counter.counter.counters.add(thread_index as usize);
this_thread_counters.add(self.index as usize)
}
}
pub fn increment(&self, vm: &MainRef, count: u64) {
unsafe {
let counter = self.counter_ptr(vm);
let new_count = *counter + count;
AtomicU64::from_ptr(counter).store(new_count, Ordering::Relaxed);
}
}
pub unsafe fn zero(&self) {
unsafe {
vlib_helper_zero_simple_counter(
std::ptr::addr_of!(self.counter.counter).cast_mut(),
self.index,
);
}
}
pub fn get(&self, _vm: &MainRef) -> u64 {
unsafe {
let per_thread_counters =
vppinfra::vec::VecRef::from_raw(self.counter.counter.counters);
per_thread_counters
.iter()
.fold(0u64, |sum, counter_by_index| {
let counter = AtomicU64::from_ptr(counter_by_index.add(self.index as usize));
sum + counter.load(Ordering::Relaxed)
})
}
}
}
unsafe impl Send for SimpleCounterIndex<'_> {}
unsafe impl Sync for SimpleCounterIndex<'_> {}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct CombinedCount {
pub packets: u64,
pub bytes: u64,
}
pub struct CombinedCounter {
counter: vlib_combined_counter_main_t,
}
impl CombinedCounter {
pub fn new(name: &str, stat_segment_name: &str) -> Self {
let name_cstr = CString::new(name).unwrap();
let stats_segment_name_cstr = CString::new(stat_segment_name).unwrap();
Self {
counter: vlib_combined_counter_main_t {
counters: std::ptr::null_mut(),
name: name_cstr.into_raw(),
stat_segment_name: stats_segment_name_cstr.into_raw(),
stats_entry_index: 0,
},
}
}
pub fn allocate_index(&self, _vm: &BarrierHeldMainRef, index: u32) -> CombinedCounterIndex<'_> {
unsafe {
vlib_validate_combined_counter(std::ptr::addr_of!(self.counter).cast_mut(), index);
vlib_helper_zero_combined_counter(std::ptr::addr_of!(self.counter).cast_mut(), index);
CombinedCounterIndex::from_parts(self, index)
}
}
}
impl Drop for CombinedCounter {
fn drop(&mut self) {
unsafe {
let _name_cstr = CString::from_raw(self.counter.name);
let _stat_segment_name_cstr = CString::from_raw(self.counter.stat_segment_name);
vlib_free_combined_counter(std::ptr::addr_of_mut!(self.counter));
}
}
}
unsafe impl Send for CombinedCounter {}
unsafe impl Sync for CombinedCounter {}
pub struct CombinedCounterIndex<'counter> {
counter: &'counter CombinedCounter,
index: u32,
}
impl<'counter> CombinedCounterIndex<'counter> {
pub fn into_parts(self) -> (&'counter CombinedCounter, u32) {
let me = ManuallyDrop::new(self);
(me.counter, me.index)
}
pub unsafe fn from_parts(counter: &'counter CombinedCounter, index: u32) -> Self {
Self { counter, index }
}
fn counter_ptr(&self, vm: &MainRef) -> *mut vlib_counter_t {
let thread_index = vm.thread_index();
unsafe {
let this_thread_counters = *self.counter.counter.counters.add(thread_index as usize);
this_thread_counters.add(self.index as usize)
}
}
pub fn increment(&self, vm: &MainRef, packets: u64, bytes: u64) {
unsafe {
let counter = self.counter_ptr(vm);
let new_packets = (*counter).packets + packets;
AtomicU64::from_ptr(std::ptr::addr_of_mut!((*counter).packets))
.store(new_packets, Ordering::Relaxed);
let new_bytes = (*counter).bytes + bytes;
AtomicU64::from_ptr(std::ptr::addr_of_mut!((*counter).bytes))
.store(new_bytes, Ordering::Relaxed);
}
}
pub unsafe fn zero(&self) {
unsafe {
vlib_helper_zero_combined_counter(
std::ptr::addr_of!(self.counter.counter).cast_mut(),
self.index,
);
}
}
pub fn get(&self, _vm: &MainRef) -> CombinedCount {
unsafe {
let per_thread_counters =
vppinfra::vec::VecRef::from_raw(self.counter.counter.counters);
per_thread_counters.iter().fold(
CombinedCount {
packets: 0,
bytes: 0,
},
|sum, counter_by_index| {
let packets = AtomicU64::from_ptr(std::ptr::addr_of_mut!(
(*counter_by_index.add(self.index as usize)).packets
));
let bytes = AtomicU64::from_ptr(std::ptr::addr_of_mut!(
(*counter_by_index.add(self.index as usize)).bytes
));
CombinedCount {
packets: sum.packets + packets.load(Ordering::Relaxed),
bytes: sum.bytes + bytes.load(Ordering::Relaxed),
}
},
)
}
}
}
unsafe impl Send for CombinedCounterIndex<'_> {}
unsafe impl Sync for CombinedCounterIndex<'_> {}