Browse Source

iface: use heapless LinearMap for neighbor cache.

Dario Nieuwenhuis 2 years ago
parent
commit
4eb49b808a

+ 1 - 2
examples/benchmark.rs

@@ -4,7 +4,6 @@ mod utils;
 
 use log::debug;
 use std::cmp;
-use std::collections::BTreeMap;
 use std::io::{Read, Write};
 use std::net::TcpStream;
 use std::os::unix::io::AsRawFd;
@@ -82,7 +81,7 @@ fn main() {
         _ => panic!("invalid mode"),
     };
 
-    let neighbor_cache = NeighborCache::new(BTreeMap::new());
+    let neighbor_cache = NeighborCache::new();
 
     let tcp1_rx_buffer = tcp::SocketBuffer::new(vec![0; 65535]);
     let tcp1_tx_buffer = tcp::SocketBuffer::new(vec![0; 65535]);

+ 1 - 1
examples/client.rs

@@ -35,7 +35,7 @@ fn main() {
     let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format");
     let port = u16::from_str(&matches.free[1]).expect("invalid port format");
 
-    let neighbor_cache = NeighborCache::new(BTreeMap::new());
+    let neighbor_cache = NeighborCache::new();
 
     let tcp_rx_buffer = tcp::SocketBuffer::new(vec![0; 1500]);
     let tcp_tx_buffer = tcp::SocketBuffer::new(vec![0; 1500]);

+ 2 - 3
examples/dhcp_client.rs

@@ -2,10 +2,9 @@
 mod utils;
 
 use log::*;
-use std::collections::BTreeMap;
 use std::os::unix::io::AsRawFd;
 
-use smoltcp::iface::{Interface, InterfaceBuilder, NeighborCache, Routes, SocketSet};
+use smoltcp::iface::{Interface, InterfaceBuilder, NeighborCache, SocketSet};
 use smoltcp::socket::dhcpv4;
 use smoltcp::time::Instant;
 use smoltcp::wire::{EthernetAddress, IpCidr, Ipv4Address, Ipv4Cidr};
@@ -28,7 +27,7 @@ fn main() {
     let mut device =
         utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
 
-    let neighbor_cache = NeighborCache::new(BTreeMap::new());
+    let neighbor_cache = NeighborCache::new();
     let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]);
     let mut ip_addrs = heapless::Vec::<IpCidr, 5>::new();
     ip_addrs

+ 1 - 2
examples/dns.rs

@@ -15,7 +15,6 @@ use smoltcp::time::Instant;
 use smoltcp::wire::{
     DnsQueryType, EthernetAddress, HardwareAddress, IpAddress, IpCidr, Ipv4Address, Ipv6Address,
 };
-use std::collections::BTreeMap;
 use std::os::unix::io::AsRawFd;
 
 fn main() {
@@ -33,7 +32,7 @@ fn main() {
         utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
     let name = &matches.free[0];
 
-    let neighbor_cache = NeighborCache::new(BTreeMap::new());
+    let neighbor_cache = NeighborCache::new();
 
     let servers = &[
         Ipv4Address::new(8, 8, 4, 4).into(),

+ 1 - 2
examples/httpclient.rs

@@ -1,7 +1,6 @@
 mod utils;
 
 use log::debug;
-use std::collections::BTreeMap;
 use std::os::unix::io::AsRawFd;
 use std::str::{self, FromStr};
 use url::Url;
@@ -29,7 +28,7 @@ fn main() {
     let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format");
     let url = Url::parse(&matches.free[1]).expect("invalid url format");
 
-    let neighbor_cache = NeighborCache::new(BTreeMap::new());
+    let neighbor_cache = NeighborCache::new();
 
     let tcp_rx_buffer = tcp::SocketBuffer::new(vec![0; 1024]);
     let tcp_tx_buffer = tcp::SocketBuffer::new(vec![0; 1024]);

+ 1 - 4
examples/loopback.rs

@@ -82,16 +82,13 @@ fn main() {
         utils::parse_middleware_options(&mut matches, device, /*loopback=*/ true)
     };
 
-    let mut neighbor_cache_entries = [None; 8];
-    let mut neighbor_cache = NeighborCache::new(&mut neighbor_cache_entries[..]);
-
     let mut ip_addrs = heapless::Vec::<IpCidr, 5>::new();
     ip_addrs
         .push(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8))
         .unwrap();
     let mut iface = InterfaceBuilder::new()
         .hardware_addr(EthernetAddress::default().into())
-        .neighbor_cache(neighbor_cache)
+        .neighbor_cache(NeighborCache::new())
         .ip_addrs(ip_addrs)
         .finalize(&mut device);
 

+ 1 - 2
examples/multicast.rs

@@ -1,7 +1,6 @@
 mod utils;
 
 use log::debug;
-use std::collections::BTreeMap;
 use std::os::unix::io::AsRawFd;
 
 use smoltcp::iface::{InterfaceBuilder, NeighborCache, SocketSet};
@@ -28,7 +27,7 @@ fn main() {
     let fd = device.as_raw_fd();
     let mut device =
         utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
-    let neighbor_cache = NeighborCache::new(BTreeMap::new());
+    let neighbor_cache = NeighborCache::new();
 
     let local_addr = Ipv4Address::new(192, 168, 69, 2);
 

+ 1 - 2
examples/ping.rs

@@ -4,7 +4,6 @@ use byteorder::{ByteOrder, NetworkEndian};
 use log::debug;
 use smoltcp::iface::SocketSet;
 use std::cmp;
-use std::collections::BTreeMap;
 use std::collections::HashMap;
 use std::os::unix::io::AsRawFd;
 use std::str::FromStr;
@@ -106,7 +105,7 @@ fn main() {
             .unwrap_or(5),
     );
 
-    let neighbor_cache = NeighborCache::new(BTreeMap::new());
+    let neighbor_cache = NeighborCache::new();
 
     let remote_addr = address;
 

+ 1 - 1
examples/server.rs

@@ -29,7 +29,7 @@ fn main() {
     let mut device =
         utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
 
-    let neighbor_cache = NeighborCache::new(BTreeMap::new());
+    let neighbor_cache = NeighborCache::new();
 
     let udp_rx_buffer = udp::PacketBuffer::new(
         vec![udp::PacketMetadata::EMPTY, udp::PacketMetadata::EMPTY],

+ 1 - 1
examples/sixlowpan.rs

@@ -68,7 +68,7 @@ fn main() {
     let mut device =
         utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
 
-    let neighbor_cache = NeighborCache::new(BTreeMap::new());
+    let neighbor_cache = NeighborCache::new();
 
     let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 1280]);
     let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 1280]);

+ 1 - 1
examples/sixlowpan_benchmark.rs

@@ -148,7 +148,7 @@ fn main() {
         _ => panic!("invalid mode"),
     };
 
-    let neighbor_cache = NeighborCache::new(BTreeMap::new());
+    let neighbor_cache = NeighborCache::new();
 
     let tcp1_rx_buffer = tcp::SocketBuffer::new(vec![0; 4096]);
     let tcp1_tx_buffer = tcp::SocketBuffer::new(vec![0; 4096]);

+ 4 - 4
src/iface/interface/mod.rs

@@ -259,7 +259,7 @@ pub struct InterfaceInner<'a> {
     rand: Rand,
 
     #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
-    neighbor_cache: Option<NeighborCache<'a>>,
+    neighbor_cache: Option<NeighborCache>,
     #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
     hardware_addr: Option<HardwareAddress>,
     #[cfg(feature = "medium-ieee802154")]
@@ -288,7 +288,7 @@ pub struct InterfaceBuilder<'a> {
     #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
     hardware_addr: Option<HardwareAddress>,
     #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
-    neighbor_cache: Option<NeighborCache<'a>>,
+    neighbor_cache: Option<NeighborCache>,
     #[cfg(feature = "medium-ieee802154")]
     pan_id: Option<Ieee802154Pan>,
     ip_addrs: Vec<IpCidr, MAX_IP_ADDRS_NUM>,
@@ -337,7 +337,7 @@ let mut device = // ...
 let hw_addr = // ...
 # EthernetAddress::default();
 let neighbor_cache = // ...
-# NeighborCache::new(BTreeMap::new());
+# NeighborCache::new();
 # #[cfg(feature = "proto-ipv4-fragmentation")]
 # let ipv4_frag_cache = // ...
 # ReassemblyBuffer::new(vec![], BTreeMap::new());
@@ -496,7 +496,7 @@ let iface = builder.finalize(&mut device);
 
     /// Set the Neighbor Cache the interface will use.
     #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
-    pub fn neighbor_cache(mut self, neighbor_cache: NeighborCache<'a>) -> Self {
+    pub fn neighbor_cache(mut self, neighbor_cache: NeighborCache) -> Self {
         self.neighbor_cache = Some(neighbor_cache);
         self
     }

+ 2 - 2
src/iface/interface/tests.rs

@@ -90,7 +90,7 @@ fn create_ethernet<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) {
 
     let iface_builder = InterfaceBuilder::new()
         .hardware_addr(EthernetAddress::default().into())
-        .neighbor_cache(NeighborCache::new(BTreeMap::new()))
+        .neighbor_cache(NeighborCache::new())
         .ip_addrs(ip_addrs);
 
     #[cfg(feature = "proto-sixlowpan-fragmentation")]
@@ -126,7 +126,7 @@ fn create_ieee802154<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) {
 
     let iface_builder = InterfaceBuilder::new()
         .hardware_addr(Ieee802154Address::default().into())
-        .neighbor_cache(NeighborCache::new(BTreeMap::new()))
+        .neighbor_cache(NeighborCache::new())
         .ip_addrs(ip_addrs);
 
     #[cfg(feature = "proto-sixlowpan-fragmentation")]

+ 26 - 130
src/iface/neighbor.rs

@@ -1,11 +1,16 @@
 // Heads up! Before working on this file you should read, at least,
 // the parts of RFC 1122 that discuss ARP.
 
-use managed::ManagedMap;
+use heapless::LinearMap;
 
 use crate::time::{Duration, Instant};
 use crate::wire::{HardwareAddress, IpAddress};
 
+#[cfg(not(test))]
+pub const NEIGHBOR_CACHE_SIZE: usize = 16;
+#[cfg(test)]
+pub const NEIGHBOR_CACHE_SIZE: usize = 3;
+
 /// A cached neighbor.
 ///
 /// A neighbor mapping translates from a protocol address to a hardware address,
@@ -41,73 +46,23 @@ impl Answer {
 }
 
 /// A neighbor cache backed by a map.
-///
-/// # Examples
-///
-/// On systems with heap, this cache can be created with:
-///
-/// ```rust
-/// use std::collections::BTreeMap;
-/// use smoltcp::iface::NeighborCache;
-/// let mut neighbor_cache = NeighborCache::new(BTreeMap::new());
-/// ```
-///
-/// On systems without heap, use:
-///
-/// ```rust
-/// use smoltcp::iface::NeighborCache;
-/// let mut neighbor_cache_storage = [None; 8];
-/// let mut neighbor_cache = NeighborCache::new(&mut neighbor_cache_storage[..]);
-/// ```
 #[derive(Debug)]
-pub struct Cache<'a> {
-    storage: ManagedMap<'a, IpAddress, Neighbor>,
+pub struct Cache {
+    storage: LinearMap<IpAddress, Neighbor, NEIGHBOR_CACHE_SIZE>,
     silent_until: Instant,
-    #[cfg(feature = "alloc")]
-    gc_threshold: usize,
 }
 
-impl<'a> Cache<'a> {
+impl Cache {
     /// Minimum delay between discovery requests, in milliseconds.
     pub(crate) const SILENT_TIME: Duration = Duration::from_millis(1_000);
 
     /// Neighbor entry lifetime, in milliseconds.
     pub(crate) const ENTRY_LIFETIME: Duration = Duration::from_millis(60_000);
 
-    /// Default number of entries in the cache before GC kicks in
-    #[cfg(feature = "alloc")]
-    pub(crate) const GC_THRESHOLD: usize = 1024;
-
-    /// Create a cache. The backing storage is cleared upon creation.
-    ///
-    /// # Panics
-    /// This function panics if `storage.len() == 0`.
-    pub fn new<T>(storage: T) -> Cache<'a>
-    where
-        T: Into<ManagedMap<'a, IpAddress, Neighbor>>,
-    {
-        let mut storage = storage.into();
-        storage.clear();
-
-        Cache {
-            storage,
-            #[cfg(feature = "alloc")]
-            gc_threshold: Self::GC_THRESHOLD,
-            silent_until: Instant::from_millis(0),
-        }
-    }
-
-    #[cfg(feature = "alloc")]
-    pub fn new_with_limit<T>(storage: T, gc_threshold: usize) -> Cache<'a>
-    where
-        T: Into<ManagedMap<'a, IpAddress, Neighbor>>,
-    {
-        let mut storage = storage.into();
-        storage.clear();
-
-        Cache {
-            storage,
-            gc_threshold,
+    /// Create a cache.
+    pub fn new() -> Self {
+        Self {
+            storage: LinearMap::new(),
             silent_until: Instant::from_millis(0),
         }
     }
@@ -121,24 +76,6 @@ impl<'a> Cache<'a> {
         debug_assert!(protocol_addr.is_unicast());
         debug_assert!(hardware_addr.is_unicast());
 
-        #[cfg(feature = "alloc")]
-        let current_storage_size = self.storage.len();
-
-        match self.storage {
-            ManagedMap::Borrowed(_) => (),
-            #[cfg(feature = "alloc")]
-            ManagedMap::Owned(ref mut map) => {
-                if current_storage_size >= self.gc_threshold {
-                    let new_btree_map = map
-                        .iter_mut()
-                        .map(|(key, value)| (*key, *value))
-                        .filter(|(_, v)| timestamp < v.expires_at)
-                        .collect();
-
-                    *map = new_btree_map;
-                }
-            }
-        };
         let neighbor = Neighbor {
             expires_at: timestamp + Self::ENTRY_LIFETIME,
             hardware_addr,
@@ -158,24 +95,13 @@ impl<'a> Cache<'a> {
                 net_trace!("filled {} => {} (was empty)", protocol_addr, hardware_addr);
             }
             Err((protocol_addr, neighbor)) => {
-                // If we're going down this branch, it means that a fixed-size cache storage
-                // is full, and we need to evict an entry.
-                let old_protocol_addr = match self.storage {
-                    ManagedMap::Borrowed(ref mut pairs) => {
-                        pairs
-                            .iter()
-                            .min_by_key(|pair_opt| {
-                                let (_protocol_addr, neighbor) = pair_opt.unwrap();
-                                neighbor.expires_at
-                            })
-                            .expect("empty neighbor cache storage") // unwraps min_by_key
-                            .unwrap() // unwraps pair
-                            .0
-                    }
-                    // Owned maps can extend themselves.
-                    #[cfg(feature = "alloc")]
-                    ManagedMap::Owned(_) => unreachable!(),
-                };
+                // If we're going down this branch, it means the cache is full, and we need to evict an entry.
+                let old_protocol_addr = *self
+                    .storage
+                    .iter()
+                    .min_by_key(|(_, neighbor)| neighbor.expires_at)
+                    .expect("empty neighbor cache storage")
+                    .0;
 
                 let _old_neighbor = self.storage.remove(&old_protocol_addr).unwrap();
                 match self.storage.insert(protocol_addr, neighbor) {
@@ -229,7 +155,6 @@ impl<'a> Cache<'a> {
 mod test {
     use super::*;
     use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3, MOCK_IP_ADDR_4};
-    use std::collections::BTreeMap;
 
     use crate::wire::EthernetAddress;
 
@@ -240,8 +165,7 @@ mod test {
 
     #[test]
     fn test_fill() {
-        let mut cache_storage = [Default::default(); 3];
-        let mut cache = Cache::new(&mut cache_storage[..]);
+        let mut cache = Cache::new();
 
         assert!(!cache
             .lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0))
@@ -273,8 +197,7 @@ mod test {
 
     #[test]
     fn test_expire() {
-        let mut cache_storage = [Default::default(); 3];
-        let mut cache = Cache::new(&mut cache_storage[..]);
+        let mut cache = Cache::new();
 
         cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0));
         assert_eq!(
@@ -291,8 +214,7 @@ mod test {
 
     #[test]
     fn test_replace() {
-        let mut cache_storage = [Default::default(); 3];
-        let mut cache = Cache::new(&mut cache_storage[..]);
+        let mut cache = Cache::new();
 
         cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0));
         assert_eq!(
@@ -306,33 +228,9 @@ mod test {
         );
     }
 
-    #[test]
-    fn test_cache_gc() {
-        let mut cache = Cache::new_with_limit(BTreeMap::new(), 2);
-        cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(100));
-        cache.fill(MOCK_IP_ADDR_2, HADDR_B, Instant::from_millis(50));
-        // Adding third item after the expiration of the previous
-        // two should garbage collect
-        cache.fill(
-            MOCK_IP_ADDR_3,
-            HADDR_C,
-            Instant::from_millis(50) + Cache::ENTRY_LIFETIME * 2,
-        );
-
-        assert_eq!(cache.storage.len(), 1);
-        assert_eq!(
-            cache.lookup(
-                &MOCK_IP_ADDR_3,
-                Instant::from_millis(50) + Cache::ENTRY_LIFETIME * 2
-            ),
-            Answer::Found(HADDR_C)
-        );
-    }
-
     #[test]
     fn test_evict() {
-        let mut cache_storage = [Default::default(); 3];
-        let mut cache = Cache::new(&mut cache_storage[..]);
+        let mut cache = Cache::new();
 
         cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(100));
         cache.fill(MOCK_IP_ADDR_2, HADDR_B, Instant::from_millis(50));
@@ -357,8 +255,7 @@ mod test {
 
     #[test]
     fn test_hush() {
-        let mut cache_storage = [Default::default(); 3];
-        let mut cache = Cache::new(&mut cache_storage[..]);
+        let mut cache = Cache::new();
 
         assert_eq!(
             cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)),
@@ -378,8 +275,7 @@ mod test {
 
     #[test]
     fn test_flush() {
-        let mut cache_storage = [Default::default(); 3];
-        let mut cache = Cache::new(&mut cache_storage[..]);
+        let mut cache = Cache::new();
 
         cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0));
         assert_eq!(