Skip to main content

dpdk_stdlib/
eal.rs

1use crate::error::{DpdkError, DpdkResult};
2use std::ffi::CString;
3
4pub struct Eal;
5
6impl Eal {
7    pub fn init(args: &[&str]) -> DpdkResult<Self> {
8        let c_args: Result<Vec<CString>, _> = args.iter().map(|s| CString::new(*s)).collect();
9        let c_args = c_args.map_err(|_| DpdkError::EalInitFailed(-1))?;
10        let mut argv: Vec<*mut i8> = c_args.iter().map(|s| s.as_ptr() as *mut i8).collect();
11
12        let result = unsafe {
13            dpdk_sys::rte_eal_init(argv.len() as i32, argv.as_mut_ptr())
14        };
15
16        if result < 0 {
17            return Err(DpdkError::EalInitFailed(result));
18        }
19
20        Ok(Self)
21    }
22}
23
24impl Drop for Eal {
25    fn drop(&mut self) {
26        unsafe {
27            dpdk_sys::rte_eal_cleanup();
28        }
29    }
30}
31
32#[cfg(test)]
33mod tests {
34    use super::*;
35    use serial_test::serial;
36
37    #[test]
38    #[serial(eal)]
39    fn test_eal_init_and_cleanup_lifecycle() {
40        dpdk_sys::stub_eal_reset();
41
42        let eal = Eal::init(&["test-app", "-l", "0", "-n", "4"])
43            .expect("Eal::init should succeed");
44
45        assert!(
46            dpdk_sys::stub_eal_is_initialized(),
47            "EAL should be initialized after Eal::init()"
48        );
49
50        drop(eal);
51
52        assert!(
53            dpdk_sys::stub_eal_is_cleaned_up(),
54            "EAL should be in cleaned-up state after Eal is dropped"
55        );
56
57        dpdk_sys::stub_eal_reset();
58    }
59
60    #[test]
61    #[serial(eal)]
62    fn test_eal_must_be_alive_for_mempool_creation() {
63        dpdk_sys::stub_eal_reset();
64
65        let _eal = Eal::init(&["test-app", "-l", "0", "-n", "4"])
66            .expect("Eal::init should succeed");
67        assert!(dpdk_sys::stub_eal_is_initialized());
68
69        let pool_name = std::ffi::CString::new("test_pool").unwrap();
70        let ptr = unsafe {
71            dpdk_sys::rte_pktmbuf_pool_create(
72                pool_name.as_ptr(),
73                1024,
74                256,
75                0,
76                dpdk_sys::RTE_MBUF_DEFAULT_BUF_SIZE,
77                0,
78            )
79        };
80        assert!(!ptr.is_null(), "Mempool creation should succeed while EAL is alive");
81        unsafe { dpdk_sys::rte_mempool_free(ptr) };
82
83        drop(_eal);
84        dpdk_sys::stub_eal_reset();
85    }
86
87    #[test]
88    #[serial(eal)]
89    fn test_mempool_create_fails_after_eal_cleanup() {
90        dpdk_sys::stub_eal_reset();
91
92        // Reproduce the exact buggy sequence: init EAL, drop it, try mempool.
93        // This is the pattern that caused the production segfault.
94        {
95            let _eal = Eal::init(&["test-app", "-l", "0", "-n", "4"])
96                .expect("Eal::init should succeed");
97        }
98
99        assert!(
100            dpdk_sys::stub_eal_is_cleaned_up(),
101            "EAL should be in cleaned-up state after Eal is dropped"
102        );
103
104        // This is the call that segfaulted with real DPDK. With stateful stubs,
105        // it returns NULL instead of crashing.
106        let pool_name = std::ffi::CString::new("orphan_pool").unwrap();
107        let ptr = unsafe {
108            dpdk_sys::rte_pktmbuf_pool_create(
109                pool_name.as_ptr(),
110                1024,
111                256,
112                0,
113                dpdk_sys::RTE_MBUF_DEFAULT_BUF_SIZE,
114                0,
115            )
116        };
117        assert!(
118            ptr.is_null(),
119            "Mempool creation should fail (return NULL) when EAL is cleaned up — \
120             with real DPDK this segfaults because rte_config->mem_config is NULL"
121        );
122
123        dpdk_sys::stub_eal_reset();
124    }
125}