Repository: aya-rs/aya (language: rust)

Issue:
cant' use LruHashMap in userspace
the userspace HashMap try_from impl

```rust
impl<'a, V: Pod, K: Pod> TryFrom<&'a mut Map> for HashMap<&'a mut MapData, V, K> {
    type Error = MapError;

    fn try_from(map: &'a mut Map) -> Result<HashMap<&'a mut MapData, V, K>, MapError> {
        match map {
            Map::HashMap(m) => {
                HashMap::new(m)
            }
            _ => Err(MapError::InvalidMapType { map_type: map.map_type() }),
        }
    }
}
impl<V: Pod, K: Pod> TryFrom<Map> for HashMap<MapData, V, K> {
    type Error = MapError;

    fn try_from(map: Map) -> Result<HashMap<MapData, V, K>, MapError> {
        match map {
            Map::HashMap(m) => HashMap::new(m),
            _ => Err(MapError::InvalidMapType { map_type: map.map_type() }),
        }
    }
}
```

the expanded codes show HashMap only can from bpf hashmap, but not lru hashmap, I remember HashMap can try_from a LruHashMap at previous commit

the latest commit 5c86b7e is broken


Discussion / hints:
https://docs.rs/aya/latest/src/aya/maps/hash_map/hash_map.rs.html#48

the crates.io version handled the lruhashmap
maybe we can add a new type LruHashMap to fix this problem
Do you have a test? I don't understand this issue.
sorry for the late reply

kernel space codes

main.rs

```rust
#[xdp(name = "lru_test")]
fn lru_test(ctx: XdpContext) -> u32 {
    kernel::lru::lru_test(ctx)
}

#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
    unsafe { core::hint::unreachable_unchecked() }
}
```

lru.rs

```rust
use aya_bpf::bindings::xdp_action::XDP_PASS;
use aya_bpf::macros::map;
use aya_bpf::maps::LruHashMap;
use aya_bpf::programs::XdpContext;

#[map]
static LRU_MAP: LruHashMap<u32, u32> = LruHashMap::with_max_entries(100, 0);

pub fn lru_test(_ctx: XdpContext) -> u32 {
    let key = 1;
    let value = 1;
    let _ = LRU_MAP.insert(&key, &value, 0);
    
    XDP_PASS
}
```

user space codes

lru_test.rs

```rust
use std::env;

use aya::Bpf;
use aya::maps::HashMap;
use aya::programs::{Xdp, XdpFlags};

pub fn run() {
    let path = env::args().nth(1).unwrap();
    let mut bpf = Bpf::load_file(path).unwrap();

    let mut lru_map: HashMap<_, u32, u32> = bpf.map_mut("LRU_MAP").unwrap().try_into().unwrap();

    lru_map.insert(2, 2, 0).unwrap();

    let prog: &mut Xdp = bpf.program_mut("lru_test").unwrap().try_into().unwrap();

    prog.load().unwrap();

    let link_id = prog.attach("lo", XdpFlags::default()).unwrap();

    prog.detach(link_id).unwrap();
}
```

main.rs

```rust
#[tokio::main]
async fn main() {
    user::lru_test::run();
}
```

compile bpf codes with `cargo b -r`

compile user space codes with `cargo b`

run it `sudo ../target/debug/user ../target/bpfel-unknown-none/release/kernel`

and it will fail with these messages

> thread 'main' panicked at user/src/lru_test.rs:11:88:
> called `Result::unwrap()` on an `Err` value: InvalidMapType { map_type: 9 }
> note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
> [1]    210886 IOT instruction  sudo ../target/debug/user ../target/bpfel-unknown-none/release/kernel
I think @ajwerner has a fix for this.

Fix patch:
diff --git a/aya/src/maps/hash_map/hash_map.rs b/aya/src/maps/hash_map/hash_map.rs
index 1f8a2f9c7..8d091582f 100644
--- a/aya/src/maps/hash_map/hash_map.rs
+++ b/aya/src/maps/hash_map/hash_map.rs
@@ -108,43 +108,22 @@ mod tests {
     use libc::{EFAULT, ENOENT};
 
     use crate::{
-        bpf_map_def,
         generated::{
             bpf_attr, bpf_cmd,
             bpf_map_type::{BPF_MAP_TYPE_HASH, BPF_MAP_TYPE_LRU_HASH},
         },
-        maps::{Map, MapData},
-        obj::{self, maps::LegacyMap, BpfSectionKind},
+        maps::Map,
+        obj,
         sys::{override_syscall, SysResult, Syscall},
     };
 
-    use super::*;
+    use super::{
+        super::test_utils::{self, new_map},
+        *,
+    };
 
     fn new_obj_map() -> obj::Map {
-        obj::Map::Legacy(LegacyMap {
-            def: bpf_map_def {
-                map_type: BPF_MAP_TYPE_HASH as u32,
-                key_size: 4,
-                value_size: 4,
-                max_entries: 1024,
-                ..Default::default()
-            },
-            section_index: 0,
-            section_kind: BpfSectionKind::Maps,
-            data: Vec::new(),
-            symbol_index: None,
-        })
-    }
-
-    fn new_map(obj: obj::Map) -> MapData {
-        override_syscall(|call| match call {
-            Syscall::Bpf {
-                cmd: bpf_cmd::BPF_MAP_CREATE,
-                ..
-            } => Ok(1337),
-            call => panic!("unexpected syscall {:?}", call),
-        });
-        MapData::create(obj, "foo", None).unwrap()
+        test_utils::new_obj_map(BPF_MAP_TYPE_HASH)
     }
 
     fn sys_error(value: i32) -> SysResult<c_long> {
@@ -213,21 +192,10 @@ mod tests {
 
     #[test]
     fn test_try_from_ok_lru() {
-        let map = new_map(obj::Map::Legacy(LegacyMap {
-            def: bpf_map_def {
-                map_type: BPF_MAP_TYPE_LRU_HASH as u32,
-                key_size: 4,
-                value_size: 4,
-                max_entries: 1024,
-                ..Default::default()
-            },
-            section_index: 0,
-            section_kind: BpfSectionKind::Maps,
-            symbol_index: None,
-            data: Vec::new(),
-        }));
-        let map = Map::HashMap(map);
-
+        let map_data = || new_map(test_utils::new_obj_map(BPF_MAP_TYPE_LRU_HASH));
+        let map = Map::HashMap(map_data());
+        assert!(HashMap::<_, u32, u32>::try_from(&map).is_ok());
+        let map = Map::LruHashMap(map_data());
         assert!(HashMap::<_, u32, u32>::try_from(&map).is_ok())
     }
 
diff --git a/aya/src/maps/hash_map/mod.rs b/aya/src/maps/hash_map/mod.rs
index 87fc0d70a..5e18cb19b 100644
--- a/aya/src/maps/hash_map/mod.rs
+++ b/aya/src/maps/hash_map/mod.rs
@@ -41,3 +41,41 @@ pub(crate) fn remove<K: Pod>(map: &MapData, key: &K) -> Result<(), MapError> {
             .into()
         })
 }
+
+#[cfg(test)]
+mod test_utils {
+    use crate::{
+        bpf_map_def,
+        generated::{bpf_cmd, bpf_map_type},
+        maps::MapData,
+        obj::{self, maps::LegacyMap, BpfSectionKind},
+        sys::{override_syscall, Syscall},
+    };
+
+    pub(super) fn new_map(obj: obj::Map) -> MapData {
+        override_syscall(|call| match call {
+            Syscall::Bpf {
+                cmd: bpf_cmd::BPF_MAP_CREATE,
+                ..
+            } => Ok(1337),
+            call => panic!("unexpected syscall {:?}", call),
+        });
+        MapData::create(obj, "foo", None).unwrap()
+    }
+
+    pub(super) fn new_obj_map(map_type: bpf_map_type) -> obj::Map {
+        obj::Map::Legacy(LegacyMap {
+            def: bpf_map_def {
+                map_type: map_type as u32,
+                key_size: 4,
+                value_size: 4,
+                max_entries: 1024,
+                ..Default::default()
+            },
+            section_index: 0,
+            section_kind: BpfSectionKind::Maps,
+            data: Vec::new(),
+            symbol_index: None,
+        })
+    }
+}
diff --git a/aya/src/maps/hash_map/per_cpu_hash_map.rs b/aya/src/maps/hash_map/per_cpu_hash_map.rs
index 5ae177f2e..aabc71f6e 100644
--- a/aya/src/maps/hash_map/per_cpu_hash_map.rs
+++ b/aya/src/maps/hash_map/per_cpu_hash_map.rs
@@ -146,3 +146,30 @@ impl<T: Borrow<MapData>, K: Pod, V: Pod> IterableMap<K, PerCpuValues<V>>
         Self::get(self, key, 0)
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use crate::{
+        generated::bpf_map_type::{BPF_MAP_TYPE_LRU_PERCPU_HASH, BPF_MAP_TYPE_PERCPU_HASH},
+        maps::Map,
+    };
+
+    use super::{super::test_utils, *};
+
+    #[test]
+    fn test_try_from_ok() {
+        let map = Map::PerCpuHashMap(test_utils::new_map(test_utils::new_obj_map(
+            BPF_MAP_TYPE_PERCPU_HASH,
+        )));
+        assert!(PerCpuHashMap::<_, u32, u32>::try_from(&map).is_ok())
+    }
+    #[test]
+    fn test_try_from_ok_lru() {
+        let map_data =
+            || test_utils::new_map(test_utils::new_obj_map(BPF_MAP_TYPE_LRU_PERCPU_HASH));
+        let map = Map::PerCpuHashMap(map_data());
+        assert!(PerCpuHashMap::<_, u32, u32>::try_from(&map).is_ok());
+        let map = Map::PerCpuLruHashMap(map_data());
+        assert!(PerCpuHashMap::<_, u32, u32>::try_from(&map).is_ok())
+    }
+}
diff --git a/aya/src/maps/mod.rs b/aya/src/maps/mod.rs
index cd0825301..4d8183e9a 100644
--- a/aya/src/maps/mod.rs
+++ b/aya/src/maps/mod.rs
@@ -291,9 +291,9 @@ macro_rules! impl_try_from_map {
     // rather than the repeated idents used later because the macro language does not allow one
     // repetition to be pasted inside another.
     ($ty_param:tt {
-        $($ty:ident $(from $variant:ident)?),+ $(,)?
+        $($ty:ident $(from $($variant:ident)|+)?),+ $(,)?
     }) => {
-        $(impl_try_from_map!(<$ty_param> $ty $(from $variant)?);)+
+        $(impl_try_from_map!(<$ty_param> $ty $(from $($variant)|+)?);)+
     };
     // Add the "from $variant" using $ty as the default if it is missing.
     (<$ty_param:tt> $ty:ident) => {
@@ -301,17 +301,17 @@ macro_rules! impl_try_from_map {
     };
     // Dispatch for each of the lifetimes.
     (
-        <($($ty_param:ident),*)> $ty:ident from $variant:ident
+        <($($ty_param:ident),*)> $ty:ident from $($variant:ident)|+
     ) => {
-        impl_try_from_map!(<'a> ($($ty_param),*) $ty from $variant);
-        impl_try_from_map!(<'a mut> ($($ty_param),*) $ty from $variant);
-        impl_try_from_map!(<> ($($ty_param),*) $ty from $variant);
+        impl_try_from_map!(<'a> ($($ty_param),*) $ty from $($variant)|+);
+        impl_try_from_map!(<'a mut> ($($ty_param),*) $ty from $($variant)|+);
+        impl_try_from_map!(<> ($($ty_param),*) $ty from $($variant)|+);
     };
     // An individual impl.
     (
         <$($l:lifetime $($m:ident)?)?>
         ($($ty_param:ident),*)
-        $ty:ident from $variant:ident
+        $ty:ident from $($variant:ident)|+
     ) => {
         impl<$($l,)? $($ty_param: Pod),*> TryFrom<$(&$l $($m)?)? Map>
             for $ty<$(&$l $($m)?)? MapData, $($ty_param),*>
@@ -320,7 +320,7 @@ macro_rules! impl_try_from_map {
 
             fn try_from(map: $(&$l $($m)?)? Map) -> Result<Self, Self::Error> {
                 match map {
-                    Map::$variant(map_data) => Self::new(map_data),
+                    $(Map::$variant(map_data) => Self::new(map_data),)+
                     map => Err(MapError::InvalidMapType {
                         map_type: map.map_type()
                     }),
@@ -353,8 +353,8 @@ impl_try_from_map!((V) {
 });
 
 impl_try_from_map!((K, V) {
-    HashMap,
-    PerCpuHashMap,
+    HashMap from HashMap|LruHashMap,
+    PerCpuHashMap from PerCpuHashMap|PerCpuLruHashMap,
     LpmTrie,
 });
 


Test patch: