Skip to main content

zenith_linux/
umem.rs

1//! UMEM 内存管理
2//!
3//! 负责 AF_XDP 所需的用户态内存区域管理:
4//! - mmap 连续物理内存
5//! - 锁定页面(mlock)防止交换
6//! - HugePage 优化(可选)
7//! - 提供安全的内存访问接口
8//!
9//! # unsafe 使用
10//! 本文件封装 mmap/munmap/mlock/sysconf 等原生系统调用与裸指针操作。
11//! 是 crate 内部四个被精确放开 `#[allow(unsafe_code)]` 的文件之一。
12
13#![allow(unsafe_code)]
14
15use crate::error::{LinuxError, Result, UmemError};
16use std::sync::atomic::{AtomicU64, Ordering};
17
18/// UMEM 配置
19#[derive(Debug, Clone)]
20pub struct UmemConfig {
21    /// 总大小(字节),必须是 PAGE_SIZE 的倍数
22    pub size: usize,
23    /// 是否使用 HugePage
24    pub hugepage: bool,
25    /// 是否锁定内存
26    pub locked: bool,
27    /// 共享内存标志(用于多个 XSK 共享)
28    pub shared: bool,
29}
30
31impl Default for UmemConfig {
32    fn default() -> Self {
33        Self {
34            size: 0,
35            hugepage: false,
36            locked: true,
37            shared: false,
38        }
39    }
40}
41
42/// 内存区域信息
43#[derive(Debug, Clone)]
44pub struct UmemRegion {
45    /// 起始地址(虚拟地址)
46    pub addr: *mut u8,
47    /// 大小(字节)
48    pub size: usize,
49    /// 是否使用 HugePage
50    pub hugepage: bool,
51    /// 是否已锁定
52    pub locked: bool,
53    /// 页偏移(相对于起始地址)
54    pub page_offset: u64,
55}
56
57// SAFETY: UmemRegion 仅持有 mmap 元数据(基址/长度/标志),无内部可变性。
58// 真实共享模型:UmemManager 经 `Arc<UmemManager>` 被多 Worker 共享(XskConfig
59// 的 shared_umem / 多队列场景),mmap 区地址在 munmap 前固定;munmap 仅在
60// UmemManager Drop 中原子 CAS(1→0) 成功后执行唯一一次,此后 incref 因
61// ref_count==0 fail-closed,绝不在活跃引用下提前解除映射。
62// 数据竞争由"按帧分片独占"契约在结构层面排除:各 Worker 只访问自己
63// 持有的帧区间(frame index 互斥分片,由描述符引擎位图保证单 Owner),
64// 同一帧同一时刻不存在两个活跃借用;与内核 DMA 的顺序由 ring 索引的
65// Acquire/Release 原子语义建立。因此元数据可安全跨线程传递/引用。
66unsafe impl Send for UmemRegion {}
67unsafe impl Sync for UmemRegion {}
68
69/// UMEM 管理器
70///
71/// 管理 AF_XDP 所需的用户态内存区域。
72/// 提供 mmap、lock、HugePage 等功能。
73pub struct UmemManager {
74    /// 内存区域
75    region: Option<UmemRegion>,
76    /// 配置
77    config: UmemConfig,
78    /// 引用计数(用于共享模式)
79    ref_count: AtomicU64,
80    /// 是否已初始化
81    initialized: bool,
82}
83
84impl std::fmt::Debug for UmemManager {
85    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86        f.debug_struct("UmemManager")
87            .field("config", &self.config)
88            .field("initialized", &self.initialized)
89            .field("ref_count", &self.ref_count)
90            .finish()
91    }
92}
93
94impl UmemManager {
95    /// 创建 UMEM 管理器
96    ///
97    /// # 参数
98    /// * `config` - UMEM 配置
99    ///
100    /// # 返回
101    /// * `Result<Self>` - 管理器实例
102    pub fn new(config: UmemConfig) -> Result<Self> {
103        if config.size == 0 {
104            return Err(LinuxError::Umem(UmemError::InsufficientSize {
105                actual: 0,
106                required: 4096,
107            }));
108        }
109
110        // 检查对齐(复用 crate 级 page_size() 封装:失败回退 4096,无裸 sysconf)
111        let page_size = crate::page_size();
112        if !config.size.is_multiple_of(page_size) {
113            return Err(LinuxError::Umem(UmemError::NotAligned {
114                actual: config.size,
115                expected: page_size,
116            }));
117        }
118
119        Ok(Self {
120            region: None,
121            config,
122            ref_count: AtomicU64::new(0),
123            initialized: false,
124        })
125    }
126
127    /// 创建内存映射
128    ///
129    /// # 返回
130    /// * `Result<()>` - 成功或错误
131    pub fn create(&mut self) -> Result<()> {
132        if self.initialized {
133            return Err(LinuxError::Umem(UmemError::AlreadyCreated));
134        }
135
136        // 复用 crate 级 page_size() 封装(sysconf 失败时回退 4096);
137        // sysconf 裸调用只保留在 lib.rs 的唯一 wrapper 内,此处不得绕过
138        let page_size = crate::page_size();
139
140        // 确定映射标志
141        let mut flags = libc::MAP_PRIVATE | libc::MAP_ANONYMOUS;
142        if self.config.shared {
143            flags = libc::MAP_SHARED | libc::MAP_ANONYMOUS;
144        }
145
146        // HugePage 优化
147        let region_size = if self.config.hugepage {
148            // 尝试 2MB HugePage
149            let hugepage_size = 2 * 1024 * 1024;
150            // 向上取整到 HugePage 大小(checked 算术:size + hugepage_size - 1
151            // 在 size 接近 usize::MAX 时可能溢出,Fail-Closed 返回错误)
152            self.config
153                .size
154                .checked_add(hugepage_size - 1)
155                .map(|v| v & !(hugepage_size - 1))
156                .ok_or_else(|| {
157                    LinuxError::InsufficientResources(
158                        "UMEM size 对齐 HugePage 时溢出".to_string(),
159                    )
160                })?
161        } else {
162            self.config.size
163        };
164
165        if self.config.hugepage {
166            flags |= libc::MAP_HUGETLB;
167        }
168
169        // SAFETY: mmap 参数均为有效值:
170        // - addr: null_mut(),让内核选择映射地址
171        // - length: region_size,已按 HugePage 大小对齐
172        // - prot: PROT_READ | PROT_WRITE,允许读写
173        // - flags: MAP_PRIVATE/MAP_SHARED + MAP_ANONYMOUS,按需可选 MAP_HUGETLB
174        // - fd: -1,匿名映射无需文件描述符
175        // - offset: 0,匿名映射无偏移
176        // 返回值已检查 MAP_FAILED,失败则立即返回错误。
177        let addr = unsafe {
178            libc::mmap(
179                std::ptr::null_mut(),
180                region_size,
181                libc::PROT_READ | libc::PROT_WRITE,
182                flags,
183                -1,
184                0,
185            )
186        };
187
188        if addr == libc::MAP_FAILED {
189            let err = std::io::Error::last_os_error();
190            return Err(LinuxError::Umem(UmemError::MmapFailed(format!(
191                "mmap failed: {}",
192                err
193            ))));
194        }
195
196        let addr = addr as *mut u8;
197
198        // 锁定内存(防止交换)
199        if self.config.locked {
200            // SAFETY: addr 来自上方成功的 mmap 返回值(已检查非 MAP_FAILED),
201            // region_size 与 mmap 时使用的大小完全一致,保证 mlock 操作在有效映射区域内。
202            let lock_result = unsafe { libc::mlock(addr as *const libc::c_void, region_size) };
203            if lock_result != 0 {
204                let err = std::io::Error::last_os_error();
205                // SAFETY: addr 为刚才成功 mmap 的区域,region_size 与 mmap 时一致。
206                // mlock 失败后立即 munmap 清理,此时映射区域仍有效(未被 munmap 过)。
207                unsafe {
208                    libc::munmap(addr as *mut libc::c_void, region_size);
209                }
210                return Err(LinuxError::Umem(UmemError::LockFailed(format!(
211                    "mlock failed: {}",
212                    err
213                ))));
214            }
215        }
216
217        // SAFETY: addr 为有效 mmap 区域,region_size 与 mmap 一致。
218        // MADV_HUGEPAGE 建议内核使用 HugePage,仅在启用 hugepage 时调用,
219        // 避免在非 HugePage 场景下产生无意义的 madvise 系统调用开销。
220        if self.config.hugepage {
221            let madvise_ret = unsafe {
222                libc::madvise(addr as *mut libc::c_void, region_size, libc::MADV_HUGEPAGE)
223            };
224            if madvise_ret != 0 {
225                tracing::debug!("madvise(MADV_HUGEPAGE) 失败: {}", std::io::Error::last_os_error());
226            }
227        }
228
229        // 计算页偏移
230        let page_offset = (addr as usize / page_size) as u64;
231
232        self.region = Some(UmemRegion {
233            addr,
234            size: region_size,
235            hugepage: self.config.hugepage,
236            locked: self.config.locked,
237            page_offset,
238        });
239
240        self.initialized = true;
241        self.ref_count.store(1, Ordering::SeqCst);
242
243        Ok(())
244    }
245
246    /// 获取内存区域
247    ///
248    /// # 返回
249    /// * `Option<&UmemRegion>` - 内存区域引用
250    pub fn region(&self) -> Option<&UmemRegion> {
251        self.region.as_ref()
252    }
253
254    /// 获取起始地址
255    ///
256    /// # 返回
257    /// * `*mut u8` - 起始地址
258    pub fn as_ptr(&self) -> *mut u8 {
259        self.region.as_ref().map_or(std::ptr::null_mut(), |r| r.addr)
260    }
261
262    /// 获取 UMEM 映射区的只读切片(checked 边界,越界返回 None)
263    ///
264    /// 用于真实 AF_XDP 模式:内核 DMA 将收包数据写入本映射区,
265    /// 数据面经本方法零拷贝读取帧内容。
266    ///
267    /// # 安全
268    /// 返回切片的生命周期绑定 `&self`;映射区所有权由 `UmemManager` 持有,
269    ///  munmap 仅发生在 drop(调用方以 `Arc<UmemManager>` 保证使用期间存活)。
270    #[inline]
271    pub fn slice(&self, offset: usize, len: usize) -> Option<&[u8]> {
272        let end = offset.checked_add(len)?;
273        if end > self.config.size {
274            return None;
275        }
276        let addr = self.as_ptr();
277        if addr.is_null() {
278            return None;
279        }
280        // SAFETY: offset+len <= self.config.size(上方已校验),addr 为 create()
281        // 成功 mmap 的有效区域基址;切片生命周期绑定 &self,映射区在
282        // UmemManager drop(munmap)前始终有效;内核 DMA 并发写入本区域是
283        // AF_XDP 的固有语义,读取侧观察到的是字节级快照,无 UB。
284        Some(unsafe { std::slice::from_raw_parts(addr.cast_const().add(offset), len) })
285    }
286
287    /// 获取 UMEM 映射区的可变切片(checked 边界,越界返回 None)
288    ///
289    /// 用于真实 AF_XDP 模式的 TX 路径:出站帧内容写入本映射区后由内核发送。
290    ///
291    /// # 安全
292    /// 接收 `&self` 借用并返回 `&mut [u8]`,技术上违反 Rust 别名规则
293    /// (aliasing rules)。UMEM 为与内核共享的可变内存(mmap MAP_SHARED),
294    /// 语义上等价于 `UnsafeCell<[u8]>`。调用方(单 Owner Worker)在数据面
295    /// 单线程顺序访问,保证同一帧区域无并发活跃借用,满足 SAFETY 不变量。
296    ///
297    /// # Safety
298    /// 调用者必须确保同一帧区域不会被两个线程同时通过 `slice_mut` 获取可变引用。
299    /// UMEM 区域是 mmap 映射的共享内存,不由 Rust 所有权模型管理,
300    /// 帧级别的互斥由 Fill Ring / Completion Ring 的单消费者语义保证。
301    #[allow(clippy::mut_from_ref)]
302    #[inline]
303    pub fn slice_mut(&self, offset: usize, len: usize) -> Option<&mut [u8]> {
304        let end = offset.checked_add(len)?;
305        if end > self.config.size {
306            return None;
307        }
308        let addr = self.as_ptr();
309        if addr.is_null() {
310            return None;
311        }
312        // SAFETY: offset+len <= self.config.size(上方已校验),addr 为有效
313        // mmap 基址;UMEM 为内核共享可变内存,单 Owner Worker 在数据面单
314        // 线程顺序访问,保证同一帧区域无并发活跃借用。
315        Some(unsafe { std::slice::from_raw_parts_mut(addr.add(offset), len) })
316    }
317
318    /// 获取页偏移
319    ///
320    /// # 返回
321    /// * `u64` - 页偏移
322    pub fn page_offset(&self) -> u64 {
323        self.region.as_ref().map_or(0, |r| r.page_offset)
324    }
325
326    /// 获取大小
327    ///
328    /// # 返回
329    /// * `usize` - 大小(字节)
330    pub fn size(&self) -> usize {
331        self.config.size
332    }
333
334    /// 是否已初始化
335    ///
336    /// # 返回
337    /// * `bool` - 是否已初始化
338    pub fn is_initialized(&self) -> bool {
339        self.initialized
340    }
341
342    /// 增加引用计数(共享模式)
343    ///
344    /// # Fail-Closed(返回错误而非 panic)
345    /// - 若 `UmemManager` 尚未调用 `create()`,返回 `UmemError::NotCreated`
346    ///   (避免对未映射内存建立引用)。
347    /// - 若引用计数已为 0(说明最后一个引用已触发 `munmap`),返回
348    ///   `UmemError::MunmapFailed`(防止 0→1 复活导致 UAF)。
349    /// - 若 `fetch_add` 后溢出 `u64::MAX`,Rust 原子为 wrapping,但 `u64` 的空间
350    ///   对任何实际场景均足够(2^64 次),不可能溢出。
351    ///
352    /// # 返回
353    /// * `Result<u64>` - 递增后的新引用计数
354    pub fn incref(&self) -> Result<u64> {
355        // 1) 未初始化:不允许建立引用
356        if !self.initialized {
357            return Err(LinuxError::Umem(UmemError::NotCreated));
358        }
359        // 2) CAS 循环:原子地检查 ref_count != 0 并递增,消除 TOCTOU 竞态窗口。
360        // 旧实现使用 load(Acquire) + fetch_add(SeqCst) 两步操作,存在
361        // load 与 fetch_add 之间的竞态窗口(另一线程可能在两步之间
362        // 将 ref_count 从 >0 减到 0 并触发 munmap)。CAS 循环保证
363        // 「检查非零」与「递增」在同一原子操作中完成。
364        loop {
365            let current = self.ref_count.load(Ordering::Acquire);
366            if current == 0 {
367                return Err(LinuxError::Umem(UmemError::MunmapFailed(
368                    "incref: region already munmapped (ref_count == 0, use-after-free prevented)"
369                        .to_string(),
370                )));
371            }
372            match self.ref_count.compare_exchange(
373                current,
374                current + 1,
375                Ordering::SeqCst,
376                Ordering::Acquire,
377            ) {
378                Ok(_) => return Ok(current + 1),
379                Err(_) => continue,
380            }
381        }
382    }
383
384    /// 减少引用计数
385    ///
386    /// 返回递减后的新值。递减到 0 **不会**在此处执行 `munmap`;`munmap`
387    /// 的唯一入口在 `Drop::drop` 中通过原子 CAS(1 → 0)触发,
388    /// 确保每个映射区域精确释放一次且仅一次(无 double-free)。
389    pub fn decref(&self) -> u64 {
390        // SeqCst:与 Drop 中的 CAS 以及其他线程的 incref/decref 全序。
391        self.ref_count.fetch_sub(1, Ordering::SeqCst) - 1
392    }
393}
394
395impl Drop for UmemManager {
396    fn drop(&mut self) {
397        if let Some(region) = &self.region {
398            // GAP-1 修复:将原来的 `load + if ref_count <= 1` TOCTOU 窗口
399            // 替换为一条原子 RMW(compare_exchange 1 → 0)。
400            //
401            // 成功路径(CAS Ok(1)):
402            //   - RMW 之前 ref_count 精确等于 1
403            //   - 本线程成功将其置为 0
404            //   - 任何并发的 incref(incref 已使用 CAS 循环,无 TOCTOU 窗口):
405            //     * CAS 之前就完成了递增 → ref_count 变为 ≥2,Drop 的 CAS 失败
406            //     * CAS 之后到达 incref → 观察到 ref_count == 0 → incref 返回 Err(Fail-Closed)
407            //   - 任何并发的 decref:
408            //     * CAS 之前完成 → ref_count < 1 → CAS 失败(安全不释放)
409            //     * CAS 之后到达 → ref_count == 0,decref 正常减为 -1(wrapping 到 u64::MAX)
410            //       但这是调用者未按「incref/decref 严格成对」契约使用的违例;
411            //       实际释放只会发生一次,不会 double-munmap。
412            //
413            // 失败路径(CAS Err(actual)):
414            //   - actual > 1:仍有活跃引用,跳过 munmap(由未来最后一次 Drop/CAS 触发)
415            //   - actual == 0:已被另一条 Drop 路径成功 CAS 并 munmap,跳过
416            //   两种情况均安全,绝不 double-free。
417            //
418            // AcqRel 成功顺序:对 munmap 前的所有写入(本线程 + 其他线程之前通过 ref_count 同步的)
419            //   建立 happens-before;失败使用 Acquire 即可(不修改内存)。
420            let cas_result = self.ref_count.compare_exchange(
421                1,
422                0,
423                Ordering::AcqRel,
424                Ordering::Acquire,
425            );
426            if cas_result.is_ok() {
427                // SAFETY: CAS(1 → 0) 成功保证:
428                //   (a) 本映射从未被 munmap 过(ref_count 到 0 是首次,且此后 incref 断言阻止复活)
429                //   (b) 没有其他并发 Drop 会再次走到 munmap 分支(它们的 CAS 必失败)
430                //   (c) region.addr / region.size 来自之前成功的 mmap,长度与 mmap 时完全匹配
431                //   → 精确 munmap 一次,无 double-free,无越界释放。
432                unsafe {
433                    libc::munmap(region.addr as *mut libc::c_void, region.size);
434                }
435            }
436        }
437    }
438}
439
440#[cfg(test)]
441mod tests {
442    use super::*;
443
444    #[test]
445    fn test_umem_config_default() {
446        let config = UmemConfig::default();
447        assert_eq!(config.size, 0);
448        assert!(!config.hugepage);
449        assert!(config.locked);
450        assert!(!config.shared);
451    }
452
453    #[test]
454    fn test_umem_manager_creation() {
455        let config = UmemConfig {
456            size: 0,
457            ..Default::default()
458        };
459        let result = UmemManager::new(config);
460        assert!(result.is_err());
461    }
462
463    #[test]
464    fn test_umem_manager_create() {
465        let config = UmemConfig {
466            size: 4096 * 256, // 1MB
467            hugepage: false,
468            locked: false, // 测试时不锁定(需要权限)
469            shared: false,
470        };
471        let mut manager = UmemManager::new(config).unwrap();
472        assert!(!manager.is_initialized());
473
474        let result = manager.create();
475        assert!(result.is_ok());
476        assert!(manager.is_initialized());
477        assert!(!manager.as_ptr().is_null());
478        assert_eq!(manager.size(), 4096 * 256);
479    }
480
481    #[test]
482    fn test_umem_alignment_check() {
483        let config = UmemConfig {
484            size: 4096 + 1, // 未对齐
485            ..Default::default()
486        };
487        let result = UmemManager::new(config);
488        assert!(result.is_err());
489    }
490
491    #[test]
492    fn test_umem_double_create() {
493        let config = UmemConfig {
494            size: 4096 * 64,
495            hugepage: false,
496            locked: false,
497            shared: false,
498        };
499        let mut manager = UmemManager::new(config).unwrap();
500        manager.create().unwrap();
501        let result = manager.create();
502        assert!(result.is_err());
503    }
504
505    #[test]
506    fn test_umem_size_zero_rejected() {
507        let config = UmemConfig {
508            size: 0,
509            ..Default::default()
510        };
511        let result = UmemManager::new(config);
512        assert!(result.is_err());
513    }
514
515    #[test]
516    fn test_umem_unaligned_size_rejected() {
517        let page_size = crate::page_size();
518        let config = UmemConfig {
519            size: page_size + 1,
520            ..Default::default()
521        };
522        let result = UmemManager::new(config);
523        assert!(result.is_err());
524    }
525
526    #[test]
527    fn test_umem_aligned_size_accepted() {
528        let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) } as usize;
529        for multiplier in [1, 2, 4, 8, 16, 32, 64, 128, 256] {
530            let config = UmemConfig {
531                size: page_size * multiplier,
532                hugepage: false,
533                locked: false,
534                shared: false,
535            };
536            let result = UmemManager::new(config);
537            assert!(
538                result.is_ok(),
539                "Size {} ({} * {}) should be valid",
540                page_size * multiplier,
541                page_size,
542                multiplier
543            );
544        }
545    }
546
547    #[test]
548    fn test_umem_region_access_after_create() {
549        let config = UmemConfig {
550            size: 4096 * 128,
551            hugepage: false,
552            locked: false,
553            shared: false,
554        };
555        let mut manager = UmemManager::new(config).unwrap();
556        assert!(manager.region().is_none());
557        assert!(manager.as_ptr().is_null());
558        assert_eq!(manager.page_offset(), 0);
559
560        manager.create().unwrap();
561        assert!(manager.region().is_some());
562        assert!(!manager.as_ptr().is_null());
563
564        let region = manager.region().unwrap();
565        assert!(!region.addr.is_null());
566        assert!(region.size >= 4096 * 128);
567        assert!(!region.hugepage);
568        assert!(!region.locked);
569    }
570
571    #[test]
572    fn test_umem_size_method_returns_config_size() {
573        let config = UmemConfig {
574            size: 4096 * 64,
575            hugepage: false,
576            locked: false,
577            shared: false,
578        };
579        let manager = UmemManager::new(config.clone()).unwrap();
580        assert_eq!(manager.size(), config.size);
581    }
582
583    #[test]
584    fn test_umem_ref_counting() {
585        let config = UmemConfig {
586            size: 4096 * 64,
587            hugepage: false,
588            locked: false,
589            shared: false,
590        };
591        let mut manager = UmemManager::new(config).unwrap();
592        manager.create().unwrap();
593
594        let ref1 = manager.incref().unwrap();
595        assert_eq!(ref1, 2);
596
597        let ref2 = manager.incref().unwrap();
598        assert_eq!(ref2, 3);
599
600        let dec1 = manager.decref();
601        assert_eq!(dec1, 2);
602
603        let dec2 = manager.decref();
604        assert_eq!(dec2, 1);
605    }
606
607    #[test]
608    fn test_umem_incref_before_create_fails() {
609        // Fail-Closed:未 create() 时 incref 返回 Err(NotCreated),不 panic
610        let config = UmemConfig {
611            size: 4096 * 16,
612            hugepage: false,
613            locked: false,
614            shared: false,
615        };
616        let manager = UmemManager::new(config).unwrap();
617        let result = manager.incref();
618        assert!(result.is_err());
619        assert!(matches!(
620            result.unwrap_err(),
621            LinuxError::Umem(UmemError::NotCreated)
622        ));
623    }
624
625    #[test]
626    fn test_umem_hugepage_align_overflow_fails() {
627        // Fail-Closed:size 页对齐但接近 usize::MAX(= 2^64 - 4096),
628        // HugePage 对齐时 size + 2MB - 1 溢出,必须返回错误而非 wrap
629        let config = UmemConfig {
630            size: usize::MAX - 4095,
631            hugepage: true,
632            locked: false,
633            shared: false,
634        };
635        let mut manager = UmemManager::new(config).unwrap();
636        let result = manager.create();
637        assert!(result.is_err(), "HugePage 对齐溢出必须 Fail-Closed");
638    }
639
640    #[test]
641    fn test_umem_config_clone() {
642        let config = UmemConfig {
643            size: 4096 * 32,
644            hugepage: true,
645            locked: true,
646            shared: true,
647        };
648        let cloned = config.clone();
649        assert_eq!(cloned.size, config.size);
650        assert_eq!(cloned.hugepage, config.hugepage);
651        assert_eq!(cloned.locked, config.locked);
652        assert_eq!(cloned.shared, config.shared);
653    }
654
655    #[test]
656    fn test_umem_manager_debug_format() {
657        let config = UmemConfig {
658            size: 4096 * 16,
659            hugepage: false,
660            locked: false,
661            shared: false,
662        };
663        let manager = UmemManager::new(config).unwrap();
664        let debug = format!("{:?}", manager);
665        assert!(debug.contains("UmemManager"));
666        assert!(debug.contains("initialized"));
667        assert!(debug.contains("ref_count"));
668    }
669
670    #[test]
671    fn test_umem_initialized_state() {
672        let config = UmemConfig {
673            size: 4096 * 32,
674            hugepage: false,
675            locked: false,
676            shared: false,
677        };
678        let mut manager = UmemManager::new(config).unwrap();
679        assert!(!manager.is_initialized());
680
681        manager.create().unwrap();
682        assert!(manager.is_initialized());
683    }
684
685    #[test]
686    fn test_umem_large_allocation() {
687        let config = UmemConfig {
688            size: 4096 * 1024, // 4MB
689            hugepage: false,
690            locked: false,
691            shared: false,
692        };
693        let mut manager = match UmemManager::new(config) {
694            Ok(m) => m,
695            Err(_) => return, // 跳过如果内存不足
696        };
697
698        let result = manager.create();
699        if result.is_ok() {
700            assert!(manager.is_initialized());
701            assert!(!manager.as_ptr().is_null());
702        }
703    }
704
705    #[test]
706    fn test_umem_shared_config_flag() {
707        let config = UmemConfig {
708            size: 4096 * 32,
709            hugepage: false,
710            locked: false,
711            shared: true,
712        };
713        let mut manager = UmemManager::new(config).unwrap();
714        let result = manager.create();
715        assert!(result.is_ok());
716        assert!(manager.is_initialized());
717    }
718
719    #[test]
720    fn test_umem_region_send_sync() {
721        fn assert_send_sync<T: Send + Sync>() {}
722        assert_send_sync::<UmemRegion>();
723    }
724}