blob: c13d8dc54c87ccaa4371cbaa147f14115e7e1ac2 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use fidl_fuchsia_hardware_ethernet as sys;
use fuchsia_zircon as zx;
use shared_buffer::SharedBuffer;
use std::fmt;
use std::sync::{Arc, Mutex};
#[repr(C)]
#[derive(Debug)]
pub struct FifoEntry {
pub offset: u32,
pub length: u16,
pub flags: u16,
cookie: u64,
}
unsafe impl fuchsia_async::FifoEntry for FifoEntry {}
impl From<sys::FifoEntry> for FifoEntry {
fn from(
sys::FifoEntry {
offset,
length,
flags,
cookie,
}: sys::FifoEntry,
) -> Self {
Self {
offset,
length,
flags,
cookie,
}
}
}
fn fifo_entry(offset: u32, length: u16) -> FifoEntry {
FifoEntry {
offset,
length,
flags: 0,
cookie: 0,
}
}
pub struct RxBuffer {
data: SharedBuffer,
offset: usize,
buflist: Arc<Mutex<BufferMap>>,
}
impl RxBuffer {
pub fn len(&self) -> usize {
self.data.len()
}
pub fn read(&self, dst: &mut [u8]) -> usize {
self.data.read(dst)
}
}
impl Drop for RxBuffer {
fn drop(&mut self) {
self.buflist.lock().unwrap().set_bit(self.offset);
}
}
impl fmt::Debug for RxBuffer {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
f.debug_struct("RxBuffer")
.field("offset", &self.offset)
.field("len", &self.data.len())
.finish()
}
}
pub struct TxBuffer<'a> {
data: SharedBuffer,
offset: usize,
length: usize,
marker: ::std::marker::PhantomData<&'a BufferPool>,
}
impl<'a> TxBuffer<'a> {
pub fn write(&mut self, src: &[u8]) -> usize {
self.length = self.data.write(src);
self.length
}
pub fn entry(self) -> FifoEntry {
FifoEntry {
offset: self.offset as u32,
length: self.length as u16,
flags: 0,
cookie: 0,
}
}
}
/// A BufferPool represents a pool of memory that may be parceled out and used as buffers for
/// communicating with an Ethernet device.
///
/// Transmit and receive buffers are currently tracked separately. (TODO(tkilbourn): explain why)
/// In the memory pool, receive buffers are allocated first, followed by the transmit buffers. The
/// available/in-flight indexes represent the index within the given type of buffer, not the global
/// buffer pool.
pub struct BufferPool {
/// The pointer and length representing the entire memory available to the pool.
base: *mut u8,
len: usize,
/// The total number of buffers of each type.
num_buffers: usize,
/// The size of a buffer in the pool.
buffer_size: usize,
/// The offsets of the available buffers within the pool.
rx_avail: Arc<Mutex<BufferMap>>,
tx_avail: BufferMap,
/// The offsets of buffers sent to another process.
rx_in_flight: BufferMap,
tx_in_flight: BufferMap,
}
// The only field within a BufferPool which is not already Send is the
// `base: *mut u8` field. We use that field as the base from which to calculate
// offsets which are obtained through thread-safe mechanisms (accessing the
// other fields of the struct, all of which are Send). Thus, there is no race
// condition. Further, since we never give out overlapping memory, it is safe
// for multiple different threads to hold onto memory vended by a BufferPool.
// Additionally, since the VMO is expected to be shared with another process
// anyways, we already expect it to be subject to unsynchronized manipulation.
// TODO(tkilbourn): write more tests (and/or proofs) that we don't give out overlapping memory
unsafe impl Send for BufferPool{}
impl BufferPool {
/// Create a new `BufferPool` out of the given VMO, with each buffer having length
/// `buffer_size`.
pub fn new(vmo: zx::Vmo, buffer_size: usize) -> Result<BufferPool, zx::Status> {
let len = vmo.get_size()? as usize;
let mapped = zx::Vmar::root_self().map(
0,
&vmo,
0,
len,
zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE,
)?;
let num_buffers = len / buffer_size / 2;
let mut rx_avail = BufferMap::new(num_buffers);
for i in 0..num_buffers {
rx_avail.set_bit(i);
}
let mut tx_avail = BufferMap::new(num_buffers);
for i in 0..num_buffers {
tx_avail.set_bit(i);
}
Ok(BufferPool {
base: mapped as *mut u8,
len,
num_buffers,
rx_avail: Arc::new(Mutex::new(rx_avail)),
tx_avail,
rx_in_flight: BufferMap::new(num_buffers),
tx_in_flight: BufferMap::new(num_buffers),
buffer_size,
})
}
/// Allocate a receive buffer and return the Ethernet fifo entry needed to queue it for the
/// driver.
pub fn alloc_rx_buffer(&mut self) -> Option<FifoEntry> {
let mut rx_avail = self.rx_avail.lock().unwrap();
let offset = rx_avail.find_first_set()?;
rx_avail.clear_bit(offset);
self.rx_in_flight.set_bit(offset);
let fifo_offset = offset * self.buffer_size;
Some(fifo_entry(fifo_offset as u32, self.buffer_size as u16))
}
/// Allocate a transmit buffer that may later be queued to the Ethernet device.
pub fn alloc_tx_buffer<'a>(&'a mut self) -> Option<TxBuffer<'a>> {
let offset = self.tx_avail.find_first_set()?;
self.tx_avail.clear_bit(offset);
self.tx_in_flight.set_bit(offset);
let fifo_offset = (self.num_buffers + offset) * self.buffer_size;
Some(TxBuffer {
data: unsafe {
SharedBuffer::new(self.base.offset(fifo_offset as isize), self.buffer_size)
},
offset: fifo_offset,
length: 0,
marker: ::std::marker::PhantomData,
})
}
/// Return a transmit buffer returned by the Ethernet device via the tx fifo. The index is
/// determined by the offset from the `base` of the pool.
pub fn release_tx_buffer(&mut self, fifo_offset: usize) {
assert!(fifo_offset % self.buffer_size == 0);
let offset = fifo_offset / self.buffer_size - self.num_buffers;
assert!(self.tx_in_flight.get_bit(offset));
self.tx_avail.set_bit(offset);
self.tx_in_flight.clear_bit(offset);
}
/// Create an `RxBuffer` from the offset+len obtained from the rx fifo from the Ethernet
/// device. The buffer is no longer considered either available or in-flight.
pub fn map_rx_buffer(&mut self, fifo_offset: usize, len: usize) -> RxBuffer {
assert!(fifo_offset % self.buffer_size == 0);
assert!(len <= self.buffer_size);
let offset = fifo_offset / self.buffer_size;
assert!(self.rx_in_flight.get_bit(offset));
self.rx_in_flight.clear_bit(offset);
RxBuffer {
data: unsafe { SharedBuffer::new(self.base.offset(fifo_offset as isize), len) },
offset,
buflist: Arc::clone(&self.rx_avail),
}
}
}
impl fmt::Debug for BufferPool {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
f.debug_struct("BufferPool")
.field("base", &self.base)
.field("len", &self.len)
.field("num_buffers", &self.num_buffers)
.field("buffer_size", &self.buffer_size)
.field("rx_avail", &self.rx_avail)
.field("tx_avail", &self.tx_avail)
.field("rx_in_flight", &self.rx_in_flight)
.field("tx_in_flight", &self.tx_in_flight)
.finish()
}
}
impl Drop for BufferPool {
fn drop(&mut self) {
unsafe {
zx::Vmar::root_self()
.unmap(self.base as usize, self.len)
.unwrap();
}
}
}
/// A bitmap used to store buffer status.
#[derive(Debug)]
struct BufferMap {
map: Vec<u64>,
len: usize,
}
impl BufferMap {
/// Create a new `BufferMap` with the given number of bits. The underlying storage may use more
/// bits, but these are not accessible.
fn new(size: usize) -> BufferMap {
assert!(size < usize::max_value() - 63);
let byte_size = (size + 63) / 64;
BufferMap {
map: vec![0; byte_size],
len: size,
}
}
/// Set the given bit. Panics if the bit is out of the range.
fn set_bit(&mut self, bit: usize) {
assert!(bit < self.len, "bit index out of bounds");
let byte_offset = bit / 64;
let bit_offset = bit % 64;
self.map[byte_offset] |= 1 << bit_offset;
}
/// Clear the given bit. Panics if the bit is out of range.
fn clear_bit(&mut self, bit: usize) {
assert!(bit < self.len, "bit index out of bounds");
let byte_offset = bit / 64;
let bit_offset = bit % 64;
self.map[byte_offset] &= !(1 << bit_offset);
}
/// Check whether the given bit is set. Panics if the bit is out of range.
fn get_bit(&self, bit: usize) -> bool {
assert!(bit < self.len, "bit index out of bounds");
let byte_offset = bit / 64;
let bit_offset = bit % 64;
self.map[byte_offset] & (1 << bit_offset) != 0
}
/// Finds the lowest index bit that is set in the map or `None` if all bits are cleared. The
/// bit is *not* cleared after returning from this method.
fn find_first_set(&self) -> Option<usize> {
for (i, byte) in self.map.iter().enumerate() {
let ntz = byte.trailing_zeros();
if ntz < 64 {
return Some(i * 64 + ntz as usize);
}
}
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_buffer_map() {
let map = BufferMap::new(1);
assert_eq!(1, map.map.len());
assert_eq!(1, map.len);
let map = BufferMap::new(64);
assert_eq!(1, map.map.len());
assert_eq!(64, map.len);
let map = BufferMap::new(65);
assert_eq!(2, map.map.len());
assert_eq!(65, map.len);
}
#[test]
fn test_set_bit() {
let mut map = BufferMap::new(100);
assert_eq!(&[0, 0], &map.map[..]);
map.set_bit(0);
assert_eq!(&[1, 0], &map.map[..]);
map.set_bit(7);
assert_eq!(&[0b1000_0001, 0], &map.map[..]);
map.set_bit(7);
assert_eq!(&[0b1000_0001, 0], &map.map[..]);
map.set_bit(64);
assert_eq!(&[0b1000_0001, 1], &map.map[..]);
map.set_bit(99);
assert_eq!(&[0b1000_0001, (1 << 35) | 1], &map.map[..]);
}
#[test]
fn test_clear_bit() {
let mut map = BufferMap::new(100);
map.map[0] = 0xff;
map.clear_bit(0);
assert_eq!(&[0b1111_1110, 0], &map.map[..]);
map.clear_bit(7);
assert_eq!(&[0b0111_1110, 0], &map.map[..]);
map.clear_bit(64);
assert_eq!(&[0b0111_1110, 0], &map.map[..]);
map.map[1] = 0x7f;
map.clear_bit(64);
assert_eq!(&[0b0111_1110, 0b0111_1110], &map.map[..]);
map.clear_bit(70);
assert_eq!(&[0b0111_1110, 0b0011_1110], &map.map[..]);
}
#[test]
fn test_get_bit() {
let mut map = BufferMap::new(100);
map.map[0] = 1;
assert!(map.get_bit(0));
assert!(!map.get_bit(1));
map.map[0] = 1 << 63;
assert!(!map.get_bit(62));
assert!(map.get_bit(63));
assert!(!map.get_bit(64));
map.map[0] = 0;
map.map[1] = 1;
assert!(!map.get_bit(63));
assert!(map.get_bit(64));
assert!(!map.get_bit(65));
map.map[1] = 1 << 35;
assert!(!map.get_bit(98));
assert!(map.get_bit(99));
}
#[test]
fn test_find_first_set() {
let mut map = BufferMap::new(100);
assert_eq!(None, map.find_first_set());
map.set_bit(0);
assert_eq!(Some(0), map.find_first_set());
map.clear_bit(0);
map.set_bit(1);
assert_eq!(Some(1), map.find_first_set());
map.set_bit(63);
assert_eq!(Some(1), map.find_first_set());
map.clear_bit(1);
assert_eq!(Some(63), map.find_first_set());
map.set_bit(64);
assert_eq!(Some(63), map.find_first_set());
map.clear_bit(63);
assert_eq!(Some(64), map.find_first_set());
map.set_bit(99);
assert_eq!(Some(64), map.find_first_set());
map.clear_bit(64);
assert_eq!(Some(99), map.find_first_set());
map.set_bit(5);
assert_eq!(Some(5), map.find_first_set());
}
#[test]
#[should_panic]
fn test_set_bit_oob() {
let mut map = BufferMap::new(1);
map.set_bit(1);
}
#[test]
#[should_panic]
fn test_clear_bit_oob() {
let mut map = BufferMap::new(1);
map.clear_bit(1);
}
#[test]
#[should_panic]
fn test_get_bit_oob() {
let map = BufferMap::new(1);
let _ = map.get_bit(1);
}
}