lcpfs 2026.1.102

LCP File System - A ZFS-inspired copy-on-write filesystem for Rust
// Copyright 2025 LunaOS Contributors
// SPDX-License-Identifier: Apache-2.0

//! # TRIM Engine
//!
//! This module implements asynchronous TRIM/DISCARD operations for SSDs
//! and other storage devices that benefit from knowing about freed blocks.
//!
//! ## Overview
//!
//! Modern SSDs and thin-provisioned storage need TRIM commands to:
//! - Improve write performance (pre-erased blocks ready for writes)
//! - Enable wear leveling (better block distribution)
//! - Reclaim space on thin-provisioned storage
//!
//! ## Batching
//!
//! TRIM operations are batched for efficiency:
//! - Ranges queued until threshold (100) reached
//! - Batches of up to 256 ranges processed together
//! - Background execution on dedicated core

use alloc::collections::VecDeque;
use alloc::vec::Vec;
use lazy_static::lazy_static;
use spin::Mutex;
// Scheduler access via crate::spawn_on_core
//

/// Range to be trimmed/discarded
#[derive(Clone, Copy)]
pub struct TrimRange {
    /// Offset in bytes
    pub offset: u64,
    /// Length in bytes
    pub length: u64,
}

/// TRIM/discard engine for asynchronous space reclamation
pub struct TrimEngine {
    /// Queue of pending trim operations
    pub queue: VecDeque<TrimRange>,
    /// Running flag
    pub is_running: bool,
}

lazy_static! {
    /// Global TRIM reactor instance
    pub static ref TRIM_REACTOR: Mutex<TrimEngine> = Mutex::new(TrimEngine::new());
}

impl Default for TrimEngine {
    fn default() -> Self {
        Self::new()
    }
}

impl TrimEngine {
    /// Create a new TRIM engine
    pub const fn new() -> Self {
        Self {
            queue: VecDeque::new(),
            is_running: false,
        }
    }

    /// Queue a range for discard
    pub fn queue_discard(&mut self, offset: u64, length: u64) {
        self.queue.push_back(TrimRange { offset, length });
        if !self.is_running && self.queue.len() > 100 {
            self.ignite();
        }
    }

    fn ignite(&mut self) {
        self.is_running = true;
        crate::spawn_on_core(Self::run_batch, Some(3));
        crate::lcpfs_println!("[ TRIM ] Reactor Ignited.");
    }

    fn run_batch() {
        let mut ranges = Vec::new();
        {
            let mut engine = TRIM_REACTOR.lock();
            for _ in 0..256 {
                if let Some(r) = engine.queue.pop_front() {
                    ranges.push(r);
                } else {
                    break;
                }
            }
            if engine.queue.is_empty() {
                engine.is_running = false;
            }
        }

        if ranges.is_empty() {
            return;
        }

        crate::lcpfs_println!("[ TRIM ] Discarded {} ranges.", ranges.len());
        let remaining = TRIM_REACTOR.lock().queue.len();
        if remaining > 0 {
            crate::spawn_on_core(Self::run_batch, Some(3));
        }
    }
}