pub mod clock;
pub mod completion;
pub mod cputime;
pub mod fair;
pub mod fair_tree;
pub mod fifo;
#[cfg(feature = "fifo_demo")]
pub mod fifo_demo;
pub mod idle;
pub mod loadavg;
pub mod pelt;
pub mod prio;
pub mod syscall;

use core::{
    intrinsics::{likely, unlikely},
    panic::Location,
    sync::atomic::{compiler_fence, fence, AtomicUsize, Ordering},
};

use alloc::{
    boxed::Box,
    collections::LinkedList,
    sync::{Arc, Weak},
    vec::Vec,
};
use system_error::SystemError;

use crate::{
    arch::{cpu::current_cpu_id, interrupt::ipi::send_ipi, ipc::signal::Signal, CurrentIrqArch},
    exception::{
        ipi::{IpiKind, IpiTarget},
        InterruptArch,
    },
    init::initial_kthread::{get_system_state, SystemState},
    libs::{
        cpumask::{AtomicCpuMask, CpuMask},
        lazy_init::Lazy,
        spinlock::{SpinLock, SpinLockGuard},
    },
    mm::percpu::{PerCpu, PerCpuVar},
    process::{
        account_context_switch, preempt::PreemptGuard, ProcessControlBlock, ProcessFlags,
        ProcessManager, ProcessState, SchedInfo,
    },
    sched::idle::IdleScheduler,
    smp::{
        core::smp_get_processor_id,
        cpu::{smp_cpu_manager, smp_cpu_manager_initialized, ProcessorId},
    },
    time::{clocksource::HZ, timer::clock},
};

use self::{
    clock::{ClockUpdataFlag, SchedClock},
    cputime::{irq_time_read, CpuTimeFunc, IrqTime},
    fair::{CfsRunQueue, CompletelyFairScheduler, FairSchedEntity},
    fifo::FifoScheduler,
    prio::{PrioUtil, MAX_RT_PRIO},
};

static mut CPU_IRQ_TIME: Option<Vec<&'static mut IrqTime>> = None;
pub static IDLE_CPUS: AtomicCpuMask = AtomicCpuMask::new();

// 这里虽然rq是percpu的，但是在负载均衡的时候需要修改对端cpu的rq，所以仍需加锁
static CPU_RUNQUEUE: Lazy<PerCpuVar<Arc<CpuRunQueue>>> = PerCpuVar::define_lazy();

pub const SCHED_FIXEDPOINT_SHIFT: u64 = 10;
#[allow(dead_code)]
pub const SCHED_FIXEDPOINT_SCALE: u64 = 1 << SCHED_FIXEDPOINT_SHIFT;
#[allow(dead_code)]
pub const SCHED_CAPACITY_SHIFT: u64 = SCHED_FIXEDPOINT_SHIFT;
#[allow(dead_code)]
pub const SCHED_CAPACITY_SCALE: u64 = 1 << SCHED_CAPACITY_SHIFT;

#[inline]
pub fn cpu_irq_time(cpu: ProcessorId) -> &'static mut IrqTime {
    unsafe { CPU_IRQ_TIME.as_mut().unwrap()[cpu.data() as usize] }
}

#[inline]
pub fn cpu_rq(cpu: usize) -> Arc<CpuRunQueue> {
    CPU_RUNQUEUE.ensure();
    unsafe {
        CPU_RUNQUEUE
            .get()
            .force_get(ProcessorId::new(cpu as u32))
            .clone()
    }
}

#[inline]
fn task_is_idle(pcb: &Arc<ProcessControlBlock>) -> bool {
    pcb.sched_info().policy() == SchedPolicy::IDLE
}

#[inline]
pub(crate) fn rq_is_idle_cpu(rq: &CpuRunQueue) -> bool {
    task_is_idle(&rq.current()) && rq.nr_running == 0
}

#[inline]
pub fn cpu_is_online(cpu: ProcessorId) -> bool {
    smp_cpu_manager().is_online_cpu(cpu)
}

pub(crate) fn try_current_task(cpu: ProcessorId) -> Option<Arc<ProcessControlBlock>> {
    cpu_rq(cpu.data() as usize).try_current_task()
}

pub fn nr_iowait() -> u32 {
    smp_cpu_manager()
        .present_cpus()
        .iter_cpu()
        .map(|cpu| cpu_rq(cpu.data() as usize).nr_iowait() as u32)
        .sum()
}

#[inline]
fn boot_in_progress() -> bool {
    matches!(
        get_system_state(),
        SystemState::Booting | SystemState::Scheduling | SystemState::FreeingInitMem
    )
}

pub fn pick_idle_cpu(allowed: &CpuMask) -> Option<ProcessorId> {
    if !smp_cpu_manager_initialized() {
        return IDLE_CPUS.first_and(allowed);
    }

    allowed
        .iter_cpu()
        .find(|&cpu| IDLE_CPUS.get(cpu) && cpu_is_online(cpu))
}

#[inline]
fn cpu_allowed_and_online(allowed: &CpuMask, cpu: ProcessorId) -> bool {
    allowed.get(cpu).unwrap_or(false) && (!smp_cpu_manager_initialized() || cpu_is_online(cpu))
}

#[inline]
fn first_allowed_online_cpu(allowed: &CpuMask) -> Option<ProcessorId> {
    allowed
        .iter_cpu()
        .find(|&cpu| !smp_cpu_manager_initialized() || cpu_is_online(cpu))
}

fn rq_nr_running(cpu: ProcessorId) -> usize {
    let rq = cpu_rq(cpu.data() as usize);
    let (rq, _guard) = rq.self_lock();
    rq.nr_running
}

fn select_fork_idle_cpu(allowed: &CpuMask, fallback_cpu: ProcessorId) -> Option<ProcessorId> {
    let idle_cpu = allowed
        .iter_cpu()
        .find(|&cpu| cpu_allowed_and_online(allowed, cpu) && IDLE_CPUS.get(cpu))?;

    if idle_cpu == fallback_cpu || !cpu_allowed_and_online(allowed, fallback_cpu) {
        return Some(idle_cpu);
    }

    // Linux does fork balancing at first wakeup, but it is still a load decision.
    // If the parent rq has no extra queued work, placing the first child locally
    // lets later fork bursts spread naturally as rq.nr_running changes. Once the
    // parent rq has more queued work than the idle sibling, prefer the idle CPU.
    if rq_nr_running(fallback_cpu) <= rq_nr_running(idle_cpu) {
        return Some(fallback_cpu);
    }

    Some(idle_cpu)
}

fn select_least_loaded_cpu(allowed: &CpuMask, fallback_cpu: ProcessorId) -> ProcessorId {
    let mut best_cpu = fallback_cpu;
    let mut best_load = usize::MAX;

    for cpu in allowed.iter_cpu() {
        if !cpu_allowed_and_online(allowed, cpu) {
            continue;
        }

        let load = rq_nr_running(cpu);
        if load < best_load || (load == best_load && cpu == fallback_cpu) {
            best_cpu = cpu;
            best_load = load;
        }
    }

    best_cpu
}

/// 选择目标 CPU。调用者必须在 pi_lock 保护下读取 cpus_allowed 并传入，
pub fn select_task_rq(
    pcb: &Arc<ProcessControlBlock>,
    prev_cpu: ProcessorId,
    wake_flags: WakeupFlags,
    allowed: &CpuMask,
) -> ProcessorId {
    let current_cpu = smp_get_processor_id();
    let fallback_cpu = if cpu_allowed_and_online(allowed, prev_cpu) {
        prev_cpu
    } else if cpu_allowed_and_online(allowed, current_cpu) {
        current_cpu
    } else {
        first_allowed_online_cpu(allowed)
            .or_else(|| allowed.iter_cpu().next())
            .unwrap_or(prev_cpu)
    };

    if pcb.flags().contains(ProcessFlags::KTHREAD) || pcb.sched_info().policy() != SchedPolicy::CFS
    {
        return fallback_cpu;
    }

    if wake_flags.contains(WakeupFlags::WF_FORK) {
        return select_fork_idle_cpu(allowed, fallback_cpu)
            .unwrap_or_else(|| select_least_loaded_cpu(allowed, fallback_cpu));
    }

    if wake_flags.contains(WakeupFlags::WF_TTWU) {
        if let Some(idle_cpu) = pick_idle_cpu(allowed) {
            return idle_cpu;
        }

        return select_least_loaded_cpu(allowed, fallback_cpu);
    }

    fallback_cpu
}

lazy_static! {
    pub static ref SCHED_FEATURES: SchedFeature = SchedFeature::GENTLE_FAIR_SLEEPERS
        | SchedFeature::START_DEBIT
        | SchedFeature::LAST_BUDDY
        | SchedFeature::CACHE_HOT_BUDDY
        | SchedFeature::WAKEUP_PREEMPTION
        | SchedFeature::NONTASK_CAPACITY
        | SchedFeature::TTWU_QUEUE
        | SchedFeature::SIS_UTIL
        | SchedFeature::RT_PUSH_IPI
        | SchedFeature::PLACE_LAG
        | SchedFeature::PLACE_DEADLINE_INITIAL
        | SchedFeature::RUN_TO_PARITY
        | SchedFeature::ALT_PERIOD
        | SchedFeature::BASE_SLICE
        | SchedFeature::UTIL_EST
        | SchedFeature::UTIL_EST_FASTUP;
}

pub trait Scheduler {
    /// ## 加入当任务进入可运行状态时调用。它将调度实体（任务）放到红黑树中，增加nr_running变量的值。
    fn enqueue(rq: &mut CpuRunQueue, pcb: Arc<ProcessControlBlock>, flags: EnqueueFlag);

    /// ## 当任务不再可运行时被调用，对应的调度实体被移出红黑树。它减少nr_running变量的值。
    fn dequeue(rq: &mut CpuRunQueue, pcb: Arc<ProcessControlBlock>, flags: DequeueFlag);

    /// ## 主动让出cpu，这个函数的行为基本上是出队，紧接着入队
    fn yield_task(rq: &mut CpuRunQueue);

    /// ## 检查进入可运行状态的任务能否抢占当前正在运行的任务
    fn check_preempt_current(
        rq: &mut CpuRunQueue,
        pcb: &Arc<ProcessControlBlock>,
        flags: WakeupFlags,
    );

    /// ## 选择接下来最适合运行的任务
    #[allow(dead_code)]
    fn pick_task(rq: &mut CpuRunQueue) -> Option<Arc<ProcessControlBlock>>;

    /// ## 选择接下来最适合运行的任务
    fn pick_next_task(
        rq: &mut CpuRunQueue,
        pcb: Option<Arc<ProcessControlBlock>>,
    ) -> Option<Arc<ProcessControlBlock>>;

    /// ## 被时间滴答函数调用，它可能导致进程切换。驱动了运行时抢占。
    fn tick(rq: &mut CpuRunQueue, pcb: Arc<ProcessControlBlock>, queued: bool);

    /// ## 在进程fork时，如需加入cfs，则调用
    fn task_fork(pcb: Arc<ProcessControlBlock>);

    fn put_prev_task(rq: &mut CpuRunQueue, prev: Arc<ProcessControlBlock>);
}

/// 调度策略
///
/// 裸字段，由调用者持 rq_lock 或 pi_lock 保护。
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[repr(u8)]
pub enum SchedPolicy {
    /// 实时进程
    RT = 0,
    /// 先进先出调度
    FIFO = 1,
    /// 完全公平调度
    CFS = 2,
    /// IDLE
    IDLE = 3,
}

impl SchedPolicy {
    #[inline]
    pub fn to_u8(self) -> u8 {
        self as u8
    }

    #[inline]
    pub fn from_u8(val: u8) -> Self {
        match val {
            0 => SchedPolicy::RT,
            1 => SchedPolicy::FIFO,
            2 => SchedPolicy::CFS,
            3 => SchedPolicy::IDLE,
            _ => panic!("Invalid SchedPolicy value: {val}"),
        }
    }
}

#[allow(dead_code)]
pub struct TaskGroup {
    /// CFS管理的调度实体，percpu的
    entitys: Vec<Arc<FairSchedEntity>>,
    /// 每个CPU的CFS运行队列
    cfs: Vec<Arc<CfsRunQueue>>,
    /// 父节点
    parent: Option<Arc<TaskGroup>>,

    shares: u64,
}

#[derive(Debug, Default)]
pub struct LoadWeight {
    /// 负载权重
    pub weight: u64,
    /// weight的倒数，方便计算
    pub inv_weight: u32,
}

impl LoadWeight {
    /// 用于限制权重在一个合适的区域内
    pub const SCHED_FIXEDPOINT_SHIFT: u32 = 10;

    pub const WMULT_SHIFT: u32 = 32;
    pub const WMULT_CONST: u32 = !0;

    pub const NICE_0_LOAD_SHIFT: u32 = Self::SCHED_FIXEDPOINT_SHIFT + Self::SCHED_FIXEDPOINT_SHIFT;
    pub const NICE_0_LOAD: u64 = 1u64 << Self::NICE_0_LOAD_SHIFT;
    pub const SCHED_PRIO_TO_WEIGHT: [u64; 40] = [
        88761, 71755, 56483, 46273, 36291, 29154, 23254, 18705, 14949, 11916, 9548, 7620, 6100,
        4904, 3906, 3121, 2501, 1991, 1586, 1277, 1024, 820, 655, 526, 423, 335, 272, 215, 172,
        137, 110, 87, 70, 56, 45, 36, 29, 23, 18, 15,
    ];

    pub fn update_load_add(&mut self, inc: u64) {
        self.weight += inc;
        self.inv_weight = 0;
    }

    pub fn update_load_sub(&mut self, dec: u64) {
        self.weight -= dec;
        self.inv_weight = 0;
    }

    pub fn update_load_set(&mut self, weight: u64) {
        self.weight = weight;
        self.inv_weight = 0;
    }

    pub fn set_load_weight_from_prio(&mut self, prio: i32) {
        let index = (prio - MAX_RT_PRIO).clamp(0, Self::SCHED_PRIO_TO_WEIGHT.len() as i32 - 1);
        self.update_load_set(Self::scale_load(Self::SCHED_PRIO_TO_WEIGHT[index as usize]));
    }

    /// ## 更新负载权重的倒数
    pub fn update_inv_weight(&mut self) {
        // 已经更新
        if likely(self.inv_weight != 0) {
            return;
        }

        let w = Self::scale_load_down(self.weight);

        if unlikely(w >= Self::WMULT_CONST as u64) {
            // 高位有数据
            self.inv_weight = 1;
        } else if unlikely(w == 0) {
            // 倒数去最大
            self.inv_weight = Self::WMULT_CONST;
        } else {
            // 计算倒数
            self.inv_weight = Self::WMULT_CONST / w as u32;
        }
    }

    /// ## 计算任务的执行时间差
    ///
    /// 计算公式：(delta_exec * (weight * self.inv_weight)) >> WMULT_SHIFT
    pub fn calculate_delta(&mut self, delta_exec: u64, weight: u64) -> u64 {
        // 降低精度
        let mut fact = Self::scale_load_down(weight);

        // 记录fact高32位
        let mut fact_hi = (fact >> 32) as u32;
        // 用于恢复
        let mut shift = Self::WMULT_SHIFT;

        self.update_inv_weight();

        if unlikely(fact_hi != 0) {
            // 这里表示高32位还有数据
            // 需要计算最高位，然后继续调整fact
            let fs = 32 - fact_hi.leading_zeros();
            shift -= fs;

            // 确保高32位全为0
            fact >>= fs;
        }

        // 这里确定了fact已经在32位内
        fact *= self.inv_weight as u64;

        fact_hi = (fact >> 32) as u32;

        if fact_hi != 0 {
            // 这里表示高32位还有数据
            // 需要计算最高位，然后继续调整fact
            let fs = 32 - fact_hi.leading_zeros();
            shift -= fs;

            // 确保高32位全为0
            fact >>= fs;
        }

        return ((delta_exec as u128 * fact as u128) >> shift) as u64;
    }

    /// ## 将负载权重缩小到到一个小的范围中计算，相当于减小精度计算
    pub const fn scale_load_down(mut weight: u64) -> u64 {
        if weight != 0 {
            weight >>= Self::SCHED_FIXEDPOINT_SHIFT;

            if weight < 2 {
                weight = 2;
            }
        }
        weight
    }

    #[allow(dead_code)]
    pub const fn scale_load(weight: u64) -> u64 {
        weight << Self::SCHED_FIXEDPOINT_SHIFT
    }
}

pub trait SchedArch {
    /// 开启当前核心的调度
    fn enable_sched_local();
    /// 关闭当前核心的调度
    #[allow(dead_code)]
    fn disable_sched_local();

    /// 在第一次开启调度之前，进行初始化工作。
    ///
    /// 注意区别于sched_init，这个函数只是做初始化时钟的工作等等。
    fn initial_setup_sched_local() {}
}

/// ## PerCpu的运行队列，其中维护了各个调度器对应的rq
#[allow(dead_code)]
#[derive(Debug)]
pub struct CpuRunQueue {
    lock: SpinLock<()>,

    cpu: ProcessorId,
    clock_task: u64,
    clock: u64,
    prev_irq_time: u64,
    clock_updata_flags: ClockUpdataFlag,

    /// 过载
    overload: bool,

    next_balance: u64,

    /// 运行任务数
    nr_running: usize,

    /// 被阻塞的任务数量
    nr_uninterruptible: usize,

    /// 因 IO 阻塞而睡眠的任务数量（用于区分 iowait）
    nr_iowait: AtomicUsize,

    /// 记录上次更新负载时间
    calc_load_update: u64,
    calc_load_active: isize,

    /// CFS调度器
    cfs: Arc<CfsRunQueue>,

    fifo: fifo::FifoRunQueue,

    clock_pelt: u64,
    lost_idle_time: u64,
    clock_idle: u64,

    cfs_tasks: LinkedList<Arc<FairSchedEntity>>,

    /// 最近一次的调度信息
    sched_info: SchedInfo,

    /// 当前在运行队列上执行的进程
    current: Weak<ProcessControlBlock>,

    idle: Weak<ProcessControlBlock>,
}

impl CpuRunQueue {
    #[inline]
    pub fn cpu(&self) -> ProcessorId {
        self.cpu
    }

    pub fn new(cpu: ProcessorId) -> Self {
        Self {
            lock: SpinLock::new(()),
            cpu,
            clock_task: 0,
            clock: 0,
            prev_irq_time: 0,
            clock_updata_flags: ClockUpdataFlag::empty(),
            overload: false,
            next_balance: 0,
            nr_running: 0,
            nr_uninterruptible: 0,
            nr_iowait: AtomicUsize::new(0),
            calc_load_update: clock() + (5 * HZ + 1),
            calc_load_active: 0,
            cfs: Arc::new(CfsRunQueue::new()),
            fifo: fifo::FifoRunQueue::new(),
            clock_pelt: 0,
            lost_idle_time: 0,
            clock_idle: 0,
            cfs_tasks: LinkedList::new(),
            sched_info: SchedInfo::default(),
            current: Weak::new(),
            idle: Weak::new(),
        }
    }

    /// 此函数只能在关中断的情况下使用！！！
    /// 获取到 rq 的可变引用，并显式持有 rq 锁。
    #[allow(clippy::mut_from_ref)]
    #[track_caller]
    pub fn self_lock(&self) -> (&mut Self, SpinLockGuard<'_, ()>) {
        let mut spins = 0usize;
        let guard = loop {
            if let Ok(guard) = self.lock.try_lock_irqsave() {
                break guard;
            }

            spins += 1;
            if spins >= 1_000_000 {
                let caller = Location::caller();
                panic!(
                    "CpuRunQueue::self_lock spinout on cpu {:?}, caller {}:{}",
                    self.cpu,
                    caller.file(),
                    caller.line()
                );
            }

            core::hint::spin_loop();
        };
        (self.force_mut_locked(), guard)
    }

    /// Best-effort current-task snapshot for lockup diagnostics.
    ///
    /// The caller must tolerate `None`: a stalled CPU may itself hold the
    /// runqueue lock, so diagnostic code must never spin waiting for it.
    pub(crate) fn try_current_task(&self) -> Option<Arc<ProcessControlBlock>> {
        let guard = self.lock.try_lock_irqsave().ok()?;
        let current = self.current();
        drop(guard);
        Some(current)
    }

    #[allow(clippy::mut_from_ref)]
    fn force_mut(&self) -> &mut Self {
        unsafe { (self as *const Self as *mut Self).as_mut().unwrap() }
    }

    /// 仅允许在已经持有 rq 锁的路径中使用。
    #[allow(clippy::mut_from_ref)]
    pub fn force_mut_locked(&self) -> &mut Self {
        assert!(
            self.lock.is_locked(),
            "rq must be locked before mutable access"
        );
        self.force_mut()
    }

    pub fn enqueue_task(&mut self, pcb: Arc<ProcessControlBlock>, flags: EnqueueFlag) {
        if !flags.contains(EnqueueFlag::ENQUEUE_NOCLOCK) {
            self.update_rq_clock();
        }

        if !flags.contains(EnqueueFlag::ENQUEUE_RESTORE) {
            let sched_info = pcb.sched_info().sched_stat.upgradeable_read_irqsave();
            if sched_info.last_queued == 0 {
                sched_info.upgrade().last_queued = self.clock;
            }
        }

        match pcb.sched_info().policy() {
            SchedPolicy::CFS => CompletelyFairScheduler::enqueue(self, pcb, flags),
            SchedPolicy::FIFO => FifoScheduler::enqueue(self, pcb, flags),
            SchedPolicy::RT => todo!(),
            SchedPolicy::IDLE => IdleScheduler::enqueue(self, pcb, flags),
        }

        // TODO:https://code.dragonos.org.cn/xref/linux-6.6.21/kernel/sched/core.c#239
    }

    pub fn dequeue_task(&mut self, pcb: Arc<ProcessControlBlock>, flags: DequeueFlag) {
        // TODO:sched_core

        if !flags.contains(DequeueFlag::DEQUEUE_NOCLOCK) {
            self.update_rq_clock()
        }

        if !flags.contains(DequeueFlag::DEQUEUE_SAVE) {
            let sched_info = pcb.sched_info().sched_stat.upgradeable_read_irqsave();

            if sched_info.last_queued > 0 {
                let delta = self.clock - sched_info.last_queued;

                let mut sched_info = sched_info.upgrade();
                sched_info.last_queued = 0;
                sched_info.run_delay += delta as usize;

                self.sched_info.run_delay += delta as usize;
            }
        }

        match pcb.sched_info().policy() {
            SchedPolicy::CFS => CompletelyFairScheduler::dequeue(self, pcb, flags),
            SchedPolicy::FIFO => FifoScheduler::dequeue(self, pcb, flags),
            SchedPolicy::RT => todo!(),
            SchedPolicy::IDLE => IdleScheduler::dequeue(self, pcb, flags),
        }
    }

    /// 将任务加入运行队列，设置 on_rq = Queued。
    pub fn activate_task(&mut self, pcb: &Arc<ProcessControlBlock>, mut flags: EnqueueFlag) {
        // 1. 迁移标志处理
        if *pcb.sched_info().on_rq.lock_irqsave() == OnRq::Migrating {
            flags |= EnqueueFlag::ENQUEUE_MIGRATED;
        }

        if flags.contains(EnqueueFlag::ENQUEUE_MIGRATED) {
            todo!()
        }

        // 2. enqueue_task
        self.enqueue_task(pcb.clone(), flags);

        // 3. 设置 on_rq = Queued
        *pcb.sched_info().on_rq.lock_irqsave() = OnRq::Queued;
    }

    /// 检查对应的task是否可以抢占当前运行的task
    #[allow(clippy::comparison_chain)]
    pub fn check_preempt_current(&mut self, pcb: &Arc<ProcessControlBlock>, flags: WakeupFlags) {
        if pcb.sched_info().policy() == self.current().sched_info().policy() {
            match self.current().sched_info().policy() {
                SchedPolicy::CFS => {
                    CompletelyFairScheduler::check_preempt_current(self, pcb, flags)
                }
                SchedPolicy::FIFO => FifoScheduler::check_preempt_current(self, pcb, flags),
                SchedPolicy::RT => todo!(),
                SchedPolicy::IDLE => IdleScheduler::check_preempt_current(self, pcb, flags),
            }
        } else if pcb.sched_info().policy() < self.current().sched_info().policy() {
            // 调度优先级更高
            self.resched_current();
        }

        if *self.current().sched_info().on_rq.lock_irqsave() == OnRq::Queued
            && self.current().flags().contains(ProcessFlags::NEED_SCHEDULE)
        {
            self.clock_updata_flags
                .insert(ClockUpdataFlag::RQCF_REQ_SKIP);
        }
    }

    /// 远端策略调整场景下的保守抢占检查。
    ///
    /// Linux wakeup path 会在持有目标 rq 锁且目标 rq 时钟已更新后执行完整的
    /// wakeup-preempt 检查；DragonOS 的 `ProcessManager::wakeup()` queued fast path
    /// 也遵循这个约束。该函数只用于没有先更新目标 rq clock 的裸远端路径，
    /// 因此只能保留那些不依赖 `rq.clock_task` 最新值的抢占决策：
    /// - 更高调度类抢占；
    /// - FIFO 优先级抢占；
    /// - idle 被非 idle 任务抢占。
    ///
    /// CFS 的 wakeup-preempt 需要像 Linux `check_preempt_wakeup()` 一样先更新当前实体；
    /// 调用者若不能证明已持目标 rq lock 并更新 rq clock，就必须在这里跳过。
    #[allow(clippy::comparison_chain)]
    pub fn check_preempt_remote(&mut self, pcb: &Arc<ProcessControlBlock>, flags: WakeupFlags) {
        let current = self.current();
        let current_policy = current.sched_info().policy();
        let next_policy = pcb.sched_info().policy();

        if current.flags().contains(ProcessFlags::NEED_SCHEDULE) {
            if current_policy == SchedPolicy::IDLE {
                self.resched_current();
            }
            return;
        }

        if next_policy < current_policy {
            self.resched_current();
        } else if next_policy == current_policy {
            match current_policy {
                SchedPolicy::CFS => {}
                SchedPolicy::FIFO => FifoScheduler::check_preempt_current(self, pcb, flags),
                SchedPolicy::RT => todo!(),
                SchedPolicy::IDLE => IdleScheduler::check_preempt_current(self, pcb, flags),
            }
        }

        if *self.current().sched_info().on_rq.lock_irqsave() == OnRq::Queued
            && self.current().flags().contains(ProcessFlags::NEED_SCHEDULE)
        {
            self.clock_updata_flags
                .insert(ClockUpdataFlag::RQCF_REQ_SKIP);
        }
    }

    /// 设置 on_rq，将任务移出运行队列
    pub fn deactivate_task(&mut self, pcb: Arc<ProcessControlBlock>, flags: DequeueFlag) {
        // 1. 根据标志设置 on_rq 状态
        *pcb.sched_info().on_rq.lock_irqsave() =
            if flags.intersects(DequeueFlag::DEQUEUE_SLEEP | DequeueFlag::DEQUEUE_STOPPED) {
                OnRq::None
            } else {
                OnRq::Migrating
            };

        // 2. dequeue_task
        self.dequeue_task(pcb, flags);
    }

    #[inline]
    pub fn cfs_rq(&self) -> Arc<CfsRunQueue> {
        self.cfs.clone()
    }

    /// 获取因 IO 阻塞而睡眠的任务数量
    #[inline]
    pub fn nr_iowait(&self) -> usize {
        self.nr_iowait.load(Ordering::Relaxed)
    }

    pub fn dec_nr_iowait(&self) {
        let result = self
            .nr_iowait
            .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |nr| nr.checked_sub(1));
        debug_assert!(result.is_ok(), "nr_iowait underflow");
    }

    /// 更新rq时钟
    pub fn update_rq_clock(&mut self) {
        // Match Linux rq clock rules: callers may update a remote rq while
        // holding that rq's lock. DragonOS sched_clock_cpu() returns a
        // globally comparable clock on supported architectures, and irq time is
        // tracked per CPU inside update_rq_clock_task().
        let clock = SchedClock::sched_clock_cpu(self.cpu);
        self.update_rq_clock_from_clock(clock);
    }

    pub fn update_rq_clock_from_clock(&mut self, clock: u64) {
        // 需要跳过这次时钟更新
        if self
            .clock_updata_flags
            .contains(ClockUpdataFlag::RQCF_ACT_SKIP)
        {
            return;
        }

        if clock < self.clock {
            return;
        }

        let delta = clock - self.clock;
        self.clock += delta;
        // error!("clock {}", self.clock);
        self.update_rq_clock_task(delta);
    }

    /// 更新任务时钟
    pub fn update_rq_clock_task(&mut self, mut delta: u64) {
        let mut irq_delta = irq_time_read(self.cpu) - self.prev_irq_time;
        compiler_fence(Ordering::SeqCst);

        if irq_delta > delta {
            irq_delta = delta;
        }

        self.prev_irq_time += irq_delta;

        delta -= irq_delta;

        // todo: psi?

        compiler_fence(Ordering::SeqCst);
        self.clock_task += delta;
        compiler_fence(Ordering::SeqCst);
        // todo: pelt?
    }

    pub fn calculate_global_load_tick(&mut self) {
        loadavg::calc_global_load_tick(self);
    }

    pub fn add_nr_running(&mut self, nr_running: usize) {
        let prev = self.nr_running;

        self.nr_running = prev + nr_running;
        loadavg::inc_nr_running(nr_running);
        if prev < 2 && self.nr_running >= 2 && !self.overload {
            self.overload = true;
        }
    }

    pub fn sub_nr_running(&mut self, count: usize) {
        self.nr_running -= count;
        loadavg::dec_nr_running(count);
        if self.nr_running < 2 && self.overload {
            self.overload = false;
        }
    }

    pub fn dec_nr_uninterruptible(&mut self) {
        if self.nr_uninterruptible > 0 {
            self.nr_uninterruptible -= 1;
            loadavg::dec_nr_uninterruptible(1);
        }
    }

    /// 在运行idle？
    pub fn sched_idle_rq(&self) -> bool {
        return unlikely(
            self.nr_running == self.cfs.idle_h_nr_running as usize && self.nr_running > 0,
        );
    }

    #[inline]
    pub fn current(&self) -> Arc<ProcessControlBlock> {
        self.current.upgrade().unwrap()
    }

    #[inline]
    pub fn set_current(&mut self, pcb: Weak<ProcessControlBlock>) {
        self.current = pcb;
    }

    #[inline]
    pub fn set_idle(&mut self, pcb: Weak<ProcessControlBlock>) {
        self.idle = pcb;
    }

    #[inline]
    pub fn clock_task(&self) -> u64 {
        self.clock_task
    }

    /// 重新调度当前进程
    pub fn resched_current(&self) {
        let current = self.current();
        let cpu = self.cpu;
        let already_requested = current.flags().contains(ProcessFlags::NEED_SCHEDULE);
        current.flags().insert(ProcessFlags::NEED_SCHEDULE);
        if cpu == smp_get_processor_id() {
            return;
        }

        // A remote idle CPU may be halted; kick it even if the flag was already
        // set so it observes the pending reschedule promptly.
        if already_requested && current.sched_info().policy() != SchedPolicy::IDLE {
            return;
        }

        // 向目标cpu发送重调度ipi
        send_resched_ipi(cpu);
    }

    /// 选择下一个task
    pub fn pick_next_task(&mut self, prev: Arc<ProcessControlBlock>) -> Arc<ProcessControlBlock> {
        debug_assert_eq!(prev.sched_info().on_cpu(), Some(self.cpu));

        let mut next: Option<Arc<ProcessControlBlock>> = None;

        if self.fifo.nr_running() > 0 {
            next = FifoScheduler::pick_next_task(self, Some(prev.clone()));
        }

        if next.is_none() {
            next = CompletelyFairScheduler::pick_next_task(self, Some(prev.clone()));
        }

        if next.is_none()
            && !task_is_idle(&prev)
            && prev.sched_info().state().is_runnable()
            && *prev.sched_info().on_rq.lock_irqsave() == OnRq::Queued
        {
            next = Some(prev.clone());
        }

        let next = next.unwrap_or_else(|| self.idle.upgrade().unwrap());

        if !Arc::ptr_eq(&prev, &next) {
            match prev.sched_info().policy() {
                SchedPolicy::FIFO => FifoScheduler::put_prev_task(self, prev),
                SchedPolicy::RT => todo!(),
                SchedPolicy::CFS => CompletelyFairScheduler::put_prev_task(self, prev),
                SchedPolicy::IDLE => IdleScheduler::put_prev_task(self, prev),
            }

            if next.sched_info().policy() == SchedPolicy::CFS {
                CompletelyFairScheduler::set_next_task(self, next.clone());
            }
        }

        next
    }
}

bitflags! {
    pub struct SchedFeature:u32 {
        /// 给予睡眠任务仅有 50% 的服务赤字。这意味着睡眠任务在被唤醒后会获得一定的服务，但不能过多地占用资源。
        const GENTLE_FAIR_SLEEPERS = 1 << 0;
        /// 将新任务排在前面，以避免已经运行的任务被饿死
        const START_DEBIT = 1 << 1;
        /// 在调度时优先选择上次唤醒的任务，因为它可能会访问之前唤醒的任务所使用的数据，从而提高缓存局部性。
        const NEXT_BUDDY = 1 << 2;
        /// 在调度时优先选择上次运行的任务，因为它可能会访问与之前运行的任务相同的数据，从而提高缓存局部性。
        const LAST_BUDDY = 1 << 3;
        /// 认为任务的伙伴（buddy）在缓存中是热点，减少缓存伙伴被迁移的可能性，从而提高缓存局部性。
        const CACHE_HOT_BUDDY = 1 << 4;
        /// 允许唤醒时抢占当前任务。
        const WAKEUP_PREEMPTION = 1 << 5;
        /// 基于任务未运行时间来减少 CPU 的容量。
        const NONTASK_CAPACITY = 1 << 6;
        /// 将远程唤醒排队到目标 CPU，并使用调度器 IPI 处理它们，以减少运行队列锁的争用。
        const TTWU_QUEUE = 1 << 7;
        /// 在唤醒时尝试限制对最后级联缓存（LLC）域的无谓扫描。
        const SIS_UTIL = 1 << 8;
        /// 在 RT（Real-Time）任务迁移时，通过发送 IPI 来减少 CPU 之间的锁竞争。
        const RT_PUSH_IPI = 1 << 9;
        /// 启用估计的 CPU 利用率功能，用于调度决策。
        const UTIL_EST = 1 << 10;
        const UTIL_EST_FASTUP = 1 << 11;
        /// 启用备选调度周期
        const ALT_PERIOD = 1 << 12;
        /// 启用基本时间片
        const BASE_SLICE = 1 << 13;
        /// EEVDF: once picked, keep running until ineligible or it gets a new slice.
        const RUN_TO_PARITY = 1 << 14;
        /// EEVDF: preserve virtual lag when placing entities.
        const PLACE_LAG = 1 << 15;
        /// EEVDF: give initial forked entities half a slice.
        const PLACE_DEADLINE_INITIAL = 1 << 16;
    }

    pub struct EnqueueFlag: u8 {
        const ENQUEUE_WAKEUP	= 0x01;
        const ENQUEUE_RESTORE	= 0x02;
        const ENQUEUE_MOVE	= 0x04;
        const ENQUEUE_NOCLOCK	= 0x08;

        const ENQUEUE_MIGRATED	= 0x40;

        const ENQUEUE_INITIAL	= 0x80;
    }

    pub struct DequeueFlag: u8 {
        const DEQUEUE_SLEEP		= 0x01;
        const DEQUEUE_SAVE		= 0x02; /* Matches ENQUEUE_RESTORE */
        const DEQUEUE_MOVE		= 0x04; /* Matches ENQUEUE_MOVE */
        const DEQUEUE_NOCLOCK		= 0x08; /* Matches ENQUEUE_NOCLOCK */

        /// 任务因 job-control stop（如 SIGSTOP/SIGTSTP）不可运行而出队。
        ///
        /// 这不是 sleep，也不是 migrate：应当将 OnRq 置为 None。
        const DEQUEUE_STOPPED		= 0x10;
    }

    pub struct WakeupFlags: u8 {
        /* Wake flags. The first three directly map to some SD flag value */
        const WF_EXEC         = 0x02; /* Wakeup after exec; maps to SD_BALANCE_EXEC */
        const WF_FORK         = 0x04; /* Wakeup after fork; maps to SD_BALANCE_FORK */
        const WF_TTWU         = 0x08; /* Wakeup;            maps to SD_BALANCE_WAKE */

        const WF_SYNC         = 0x10; /* Waker goes to sleep after wakeup */
        const WF_MIGRATED     = 0x20; /* Internal use, task got migrated */
        const WF_CURRENT_CPU  = 0x40; /* Prefer to move the wakee to the current CPU. */
    }

    pub struct SchedMode: u8 {
        /*
        * Constants for the sched_mode argument of __schedule().
        *
        * The mode argument allows RT enabled kernels to differentiate a
        * preemption from blocking on an 'sleeping' spin/rwlock. Note that
        * SM_MASK_PREEMPT for !RT has all bits set, which allows the compiler to
        * optimize the AND operation out and just check for zero.
        */
        /// 在调度过程中不会再次进入队列，即需要手动唤醒
        const SM_NONE			= 0x0;
        /// 重新加入队列，即当前进程被抢占，需要时钟调度
        const SM_PREEMPT		= 0x1;
        /// rt相关
        const SM_RTLOCK_WAIT		= 0x2;
        /// 默认与SM_PREEMPT相同
        const SM_MASK_PREEMPT	= Self::SM_PREEMPT.bits;
    }
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum OnRq {
    Queued,
    Migrating,
    None,
}

impl ProcessManager {
    pub fn update_process_times(user_tick: bool) {
        let pcb = Self::current_pcb();
        CpuTimeFunc::irqtime_account_process_tick(&pcb, user_tick, 1);

        scheduler_tick();
    }
}

/// ## 时钟tick时调用此函数
pub fn scheduler_tick() {
    fence(Ordering::SeqCst);
    // 获取当前CPU索引
    let cpu_idx = smp_get_processor_id().data() as usize;

    // 获取当前CPU的请求队列
    let rq = cpu_rq(cpu_idx);

    let (rq, guard) = rq.self_lock();

    // 获取当前请求队列的当前请求
    let current = rq.current();

    // 更新请求队列时钟
    rq.update_rq_clock();
    match current.sched_info().policy() {
        SchedPolicy::CFS => CompletelyFairScheduler::tick(rq, current, false),
        SchedPolicy::FIFO => FifoScheduler::tick(rq, current, false),
        SchedPolicy::RT => todo!(),
        SchedPolicy::IDLE => IdleScheduler::tick(rq, current, false),
    }

    rq.calculate_global_load_tick();

    drop(guard);
    // TODO:处理负载均衡
}

/// ## 执行调度
/// 若preempt_count不为0则报错
#[inline]
pub fn schedule(sched_mod: SchedMode) {
    let _guard = unsafe { CurrentIrqArch::save_and_disable_irq() };
    assert_eq!(ProcessManager::current_pcb().preempt_count(), 0);
    __schedule(sched_mod);
}

/// IO 调度函数：标记当前进程正在等待 IO 并触发调度
///
/// 此函数用于在进程因 IO 操作而需要睡眠时调用，它会：
/// 1. 设置 IN_IOWAIT 标志，表示进程正在等待 IO
/// 2. 调用 schedule() 触发调度
/// 3. 进程被唤醒后自动清除 IN_IOWAIT 标志
///
/// 这样可以正确统计 iowait 时间，区分 CPU 空闲是因为等待 IO 还是纯粹空闲
pub fn io_schedule() {
    let current = ProcessManager::current_pcb();
    // 设置 IO 等待标志
    current.flags().insert(ProcessFlags::IN_IOWAIT);
    // 调用普通调度
    schedule(SchedMode::SM_NONE);
    // 被唤醒后清除 IO 等待标志
    current.flags().remove(ProcessFlags::IN_IOWAIT);
}

/// ## 执行调度
/// 此函数与schedule的区别为，该函数不会检查preempt_count
/// 适用于时钟中断等场景
pub fn __schedule(sched_mod: SchedMode) {
    __schedule_inner(sched_mod, None);
}

pub(crate) fn __schedule_with_current(sched_mod: SchedMode, current: Arc<ProcessControlBlock>) {
    __schedule_inner(sched_mod, Some(current));
}

fn __schedule_inner(sched_mod: SchedMode, current: Option<Arc<ProcessControlBlock>>) {
    // 中断/IPI 路径上的抢占调度请求在 preempt_count 非零时不能直接切走当前任务，
    // 否则会破坏依赖 preempt_disable() 的临界区（例如当前 RCU 实现的读侧临界区）。
    if sched_mod.contains(SchedMode::SM_MASK_PREEMPT) {
        let current = ProcessManager::current_pcb();
        if current.preempt_count() > 0 {
            current.flags().insert(ProcessFlags::NEED_SCHEDULE);
            return;
        }
    }

    let cpu = smp_get_processor_id().data() as usize;
    let rq = cpu_rq(cpu);
    let _irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() };
    let prev = match current {
        Some(current) => current,
        None => {
            let prev = rq.current();
            if let ProcessState::Exited(_) = prev.clone().sched_info().state() {
                // 从exit进的Schedule
                ProcessManager::current_pcb()
            } else {
                prev
            }
        }
    };
    crate::rcu::note_context_switch(&prev);

    // TODO: hrtick_clear(rq);

    let (rq, guard) = rq.self_lock();
    // 对标 Linux __schedule() rq_lock 后的 smp_mb__after_spinlock()：
    // x86_64 (TSO) 上 spinlock 已含 full barrier，此处为冗余；RISC-V 上必要。
    fence(Ordering::SeqCst);

    rq.clock_updata_flags = ClockUpdataFlag::from_bits_truncate(rq.clock_updata_flags.bits() << 1);

    rq.update_rq_clock();
    rq.clock_updata_flags = ClockUpdataFlag::RQCF_UPDATE;

    // 对标 Linux __schedule() 的 prev_state 检查：
    //   Linux 条件: (!(sched_mode & SM_MASK_PREEMPT) && prev_state)
    //   ——非抢占 + prev 非 RUNNING 时进入 deactivate 分支。
    //
    // DragonOS 扩展：SM_PREEMPT 路径也需处理非 runnable 的 prev，
    // 因为异步 stop_task() 可在发送端上下文将远端 current 设为 Stopped（仅 kick 不 dequeue），
    // 导致远端 IPI 退出时 prev 已非 runnable 但仍在 runqueue 上。
    // 此时必须在这里完成唯一一次出队。
    //
    // on_rq == Queued 守卫保证 deactivate 幂等：
    //   若 stop_task 已出队（!is_current 分支），此处 on_rq != Queued，跳过。
    //   若 stop_task 未出队（远端 current 分支），此处 on_rq == Queued，执行出队。
    let mut prev_state = prev.sched_info().state();
    if !prev_state.is_runnable() {
        let interruptible = prev_state.is_blocked_interruptable();
        let wake_kill = prev_state.is_stopped();

        // signal_pending_state 仅对 SM_NONE（自愿调度）生效：
        //   TASK_INTERRUPTIBLE → 有任意信号 → 恢复 RUNNING
        //   TASK_STOPPED       → 仅 SIGKILL → 恢复 RUNNING
        //   TASK_UNINTERRUPTIBLE → 不检查信号，直接 deactivate
        // SM_PREEMPT（被抢占）不检查信号，因为异步 stop 不应被信号恢复。
        let signal_wake = !sched_mod.contains(SchedMode::SM_MASK_PREEMPT)
            && Signal::signal_pending_state(interruptible, wake_kill, &prev);

        if signal_wake {
            prev.sched_info().set_state(ProcessState::Runnable);
            prev_state = ProcessState::Runnable;
        } else {
            let _ = take_current_migration_target(&prev);

            if *prev.sched_info().on_rq.lock_irqsave() == OnRq::Queued {
                // sched_contributes_to_load 在 deactivate_task 之前
                if matches!(prev_state, ProcessState::Blocked(false)) {
                    rq.nr_uninterruptible += 1;
                    loadavg::inc_nr_uninterruptible(1);
                }

                rq.deactivate_task(
                    prev.clone(),
                    DequeueFlag::DEQUEUE_SLEEP | DequeueFlag::DEQUEUE_NOCLOCK,
                );

                // nr_iowait++ happens after deactivate_task.
                if prev_state.is_blocked() && prev.flags().contains(ProcessFlags::IN_IOWAIT) {
                    rq.nr_iowait.fetch_add(1, Ordering::Relaxed);
                }
            }
        }
    }

    let mut migrate_prev_to = None;
    if prev_state.is_runnable() {
        if let Some(dest_cpu) = take_current_migration_target(&prev) {
            // 当前任务迁移在真正切出当前 CPU 时才标记 rseq migrate，
            // 避免在迁移请求发起后、任务仍运行在旧 CPU 时过早处理 NEED_RSEQ。
            crate::process::rseq::Rseq::on_migrate(&prev);

            debug_assert!(
                !task_is_idle(&prev),
                "idle task must not be migrated through current task migration"
            );
            debug_assert!(
                cpu_is_online(dest_cpu),
                "current task migration target {:?} must be online",
                dest_cpu
            );
            // Do not read cpus_allowed under rq_lock: sched_setaffinity uses
            // pi_lock -> rq_lock. The switch tail, after releasing this rq,
            // revalidates the destination under pi_lock immediately before
            // enqueue and therefore closes concurrent affinity changes
            // without introducing rq_lock -> pi_lock inversion here.

            rq.deactivate_task(
                prev.clone(),
                DequeueFlag::DEQUEUE_MOVE | DequeueFlag::DEQUEUE_NOCLOCK,
            );

            if prev.sched_info().policy() == SchedPolicy::CFS {
                let mut se = prev.sched_info().sched_entity();
                crate::sched::fair::FairSchedEntity::for_each_in_group(&mut se, |se| {
                    se.cfs_rq().force_mut().set_current(Weak::default());
                    (true, true)
                });
            }

            *prev.sched_info().on_rq.lock_irqsave() = OnRq::None;
            migrate_prev_to = Some(dest_cpu);
        }
    }

    let next = rq.pick_next_task(prev.clone());
    if task_is_idle(&next) {
        IDLE_CPUS.set(rq.cpu);
    } else if task_is_idle(&prev) {
        IDLE_CPUS.clear(rq.cpu);
    }

    prev.flags().remove(ProcessFlags::NEED_SCHEDULE);
    fence(Ordering::SeqCst);
    if likely(!Arc::ptr_eq(&prev, &next)) {
        crate::process::rseq::Rseq::on_preempt(&prev);

        assert!(
            next.sched_info().try_mark_running(),
            "scheduler selected a task which is still running on another CPU"
        );
        rq.set_current(Arc::downgrade(&next));
        compiler_fence(Ordering::SeqCst);
        account_context_switch();
        if let Some(dest_cpu) = migrate_prev_to {
            unsafe {
                crate::process::PROCESS_SWITCH_RESULT
                    .as_mut()
                    .unwrap()
                    .get_mut()
                    .migrate_prev_to = Some(dest_cpu);
            }
        }
        drop(guard);

        unsafe { ProcessManager::switch_process(prev, next) };
    } else {
        assert!(
            migrate_prev_to.is_none(),
            "current task migration must switch away from the migrated task"
        );
        // A tick preempt request may pick the same task again. It still reached
        // the scheduler from a preemptible return-to-user boundary, so rseq must
        // observe a bounded preempt event even when no other entity is chosen.
        if sched_mod.contains(SchedMode::SM_MASK_PREEMPT) {
            crate::process::rseq::Rseq::on_preempt(&prev);
        }
        drop(guard);
        assert!(
            Arc::ptr_eq(&ProcessManager::current_pcb(), &prev),
            "{}",
            ProcessManager::current_pcb().basic().name()
        );
    }
}

pub fn sched_fork(pcb: &Arc<ProcessControlBlock>) -> Result<(), SystemError> {
    let current = ProcessManager::current_pcb();
    let (mut fork_prio, mut fork_static_prio, parent_policy, reset_on_fork) = {
        let pi_guard = current.sched_info().pi_lock_irqsave();
        (
            current.sched_info().normal_prio(),
            current.sched_info().static_prio(),
            current.sched_info().policy(),
            pi_guard.sched_reset_on_fork(),
        )
    };

    // This PR can only set RESET_ON_FORK on CFS tasks. Match Linux's fair
    // policy rule: preserve non-negative nice, but reset negative nice to 0.
    if reset_on_fork && parent_policy == SchedPolicy::CFS && fork_static_prio < prio::DEFAULT_PRIO {
        fork_prio = prio::DEFAULT_PRIO;
        fork_static_prio = prio::DEFAULT_PRIO;
    }

    // 子进程是 TASK_NEW，不可见。可以直接写裸字段
    // 子进程继承父进程的 prio、static_prio、normal_prio
    pcb.sched_info().set_prio(fork_prio);
    pcb.sched_info().set_static_prio(fork_static_prio);
    pcb.sched_info().set_normal_prio(fork_prio);
    pcb.sched_info()
        .pi_lock_irqsave()
        .set_sched_reset_on_fork(false);

    if PrioUtil::dl_prio(fork_prio) {
        return Err(SystemError::EAGAIN_OR_EWOULDBLOCK);
    } else if PrioUtil::rt_prio(fork_prio) {
        // 子进程继承父进程的调度策略（FIFO/RR），而非统一设为 RT
        pcb.sched_info().set_policy(parent_policy);
    } else {
        pcb.sched_info().set_policy(SchedPolicy::CFS);
    }

    pcb.sched_info()
        .sched_entity()
        .force_mut()
        .load
        .set_load_weight_from_prio(fork_static_prio);

    pcb.sched_info()
        .sched_entity()
        .force_mut()
        .init_entity_runnable_average();

    Ok(())
}

pub fn sched_cgroup_fork(pcb: &Arc<ProcessControlBlock>) {
    let fork_cpu = smp_get_processor_id();

    __set_task_cpu(pcb, fork_cpu);
    match pcb.sched_info().policy() {
        SchedPolicy::RT => todo!(),
        SchedPolicy::FIFO => FifoScheduler::task_fork(pcb.clone()),
        SchedPolicy::CFS => CompletelyFairScheduler::task_fork(pcb.clone()),
        SchedPolicy::IDLE => todo!(),
    }

    debug_assert_eq!(
        pcb.sched_info().sched_entity().cfs_rq().rq().cpu(),
        fork_cpu,
        "fork-time task_fork must only charge the local rq clock"
    );
}

fn __set_task_cpu(pcb: &Arc<ProcessControlBlock>, cpu: ProcessorId) {
    debug_assert!(
        cpu_is_online(cpu) || boot_in_progress(),
        "__set_task_cpu target cpu {:?} must be online outside boot",
        cpu
    );

    // TODO: Fixme There is not implement group sched;
    let se = pcb.sched_info().sched_entity();
    let rq = cpu_rq(cpu.data() as usize);
    se.force_mut().set_cfs(Arc::downgrade(&rq.cfs));
}

/// 对标 Linux ttwu_queue + ttwu_do_activate
pub fn enqueue_task_on_cpu(
    pcb: &Arc<ProcessControlBlock>,
    target_cpu: ProcessorId,
    wake_flags: WakeupFlags,
    was_uninterruptible: bool,
) {
    assert!(
        !pcb.sched_info().is_running(),
        "cannot enqueue a task before its previous switch-out completes"
    );
    __set_task_cpu(pcb, target_cpu);
    pcb.sched_info().set_on_cpu(Some(target_cpu));

    let rq = cpu_rq(target_cpu.data() as usize);
    let (rq, _guard) = rq.self_lock();

    rq.update_rq_clock();

    if was_uninterruptible {
        rq.dec_nr_uninterruptible();
    }

    let was_idle = rq_is_idle_cpu(rq);

    rq.activate_task(
        pcb,
        EnqueueFlag::ENQUEUE_WAKEUP | EnqueueFlag::ENQUEUE_NOCLOCK,
    );

    if was_idle && !rq_is_idle_cpu(rq) {
        IDLE_CPUS.clear(target_cpu);
    }

    rq.check_preempt_current(pcb, wake_flags);
}

pub fn request_task_migration(
    pcb: &Arc<ProcessControlBlock>,
    dest_cpu: ProcessorId,
) -> Result<(), SystemError> {
    let Some(src_cpu) = pcb.sched_info().on_cpu() else {
        // A new task consumes its placement hint in wake_up_new_task(). An
        // existing off-rq task must keep its previous task_cpu until wakeup so
        // sleep/iowait accounting is charged back to the rq that dequeued it.
        return Ok(());
    };

    if src_cpu == dest_cpu {
        return Ok(());
    }

    let rq = cpu_rq(src_cpu.data() as usize);
    let update_clock = src_cpu == smp_get_processor_id();
    let (rq, _guard) = rq.self_lock();

    if update_clock {
        rq.update_rq_clock();
    }

    if Arc::ptr_eq(&rq.current(), pcb) {
        pcb.sched_info().set_migrate_to(Some(dest_cpu));
        pcb.flags()
            .insert(ProcessFlags::NEED_MIGRATE | ProcessFlags::NEED_SCHEDULE);

        if src_cpu != smp_get_processor_id() {
            send_resched_ipi(src_cpu);
        }

        return Ok(());
    }

    if *pcb.sched_info().on_rq.lock_irqsave() == OnRq::Queued {
        rq.dequeue_task(
            pcb.clone(),
            DequeueFlag::DEQUEUE_MOVE | DequeueFlag::DEQUEUE_NOCLOCK,
        );
        crate::process::rseq::Rseq::on_migrate(pcb);
        *pcb.sched_info().on_rq.lock_irqsave() = OnRq::None;
        pcb.sched_info().set_on_cpu(None);
        drop(_guard);

        pcb.sched_info().wait_until_not_running();
        enqueue_task_on_cpu(pcb, dest_cpu, WakeupFlags::WF_MIGRATED, false);
        return Ok(());
    }

    // Linux does not set_task_cpu() for blocked tasks directly. DragonOS
    // wakeup() uses task_cpu as the previous rq for sleep/iowait accounting and
    // then selects a legal target from cpus_allowed, so keep off-rq tasks bound
    // to the rq that actually dequeued them.
    Ok(())
}

pub fn take_current_migration_target(current: &Arc<ProcessControlBlock>) -> Option<ProcessorId> {
    if !current.flags().contains(ProcessFlags::NEED_MIGRATE) {
        return None;
    }

    let dest_cpu = current.sched_info().migrate_to();
    current.sched_info().set_migrate_to(None);
    current.flags().remove(ProcessFlags::NEED_MIGRATE);
    dest_cpu
}

#[inline(never)]
pub fn sched_init() {
    // 初始化percpu变量
    unsafe {
        CPU_IRQ_TIME = Some(Vec::with_capacity(PerCpu::MAX_CPU_NUM as usize));
        CPU_IRQ_TIME
            .as_mut()
            .unwrap()
            .resize_with(PerCpu::MAX_CPU_NUM as usize, || Box::leak(Box::default()));

        let mut cpu_runqueue = Vec::with_capacity(PerCpu::MAX_CPU_NUM as usize);
        for cpu in 0..PerCpu::MAX_CPU_NUM as usize {
            let rq = Arc::new(CpuRunQueue::new(ProcessorId::new(cpu as u32)));
            rq.cfs.force_mut().set_rq(Arc::downgrade(&rq));
            cpu_runqueue.push(rq);
        }

        CPU_RUNQUEUE.init(PerCpuVar::new(cpu_runqueue).unwrap());
    };

    // 初始化 per-CPU CPU 时间统计
    cputime::init_kernel_cpu_stat();
}

#[inline]
pub fn send_resched_ipi(cpu: ProcessorId) {
    send_ipi(IpiKind::KickCpu, IpiTarget::Specified(cpu));
}

pub fn sched_yield() {
    // 禁用中断
    let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() };

    let pcb = ProcessManager::current_pcb();
    let rq = cpu_rq(pcb.sched_info().on_cpu().unwrap_or(current_cpu_id()).data() as usize);
    let (rq, guard) = rq.self_lock();

    // TODO: schedstat_inc(rq->yld_count);

    match pcb.sched_info().policy() {
        SchedPolicy::CFS => CompletelyFairScheduler::yield_task(rq),
        SchedPolicy::FIFO => FifoScheduler::yield_task(rq),
        SchedPolicy::RT => rq.resched_current(),
        SchedPolicy::IDLE => {}
    }

    let preempt_guard = PreemptGuard::new();

    drop(guard);
    drop(irq_guard);

    drop(preempt_guard);

    schedule(SchedMode::SM_NONE);
}

/// Reschedule at a preemptible kernel boundary only when the scheduler has
/// already requested it. Unlike `sched_yield()`, this does not voluntarily
/// give up the CPU or alter scheduling-class yield state when no reschedule is
/// pending.
#[inline]
pub fn cond_resched() {
    if ProcessManager::current_pcb()
        .flags()
        .contains(ProcessFlags::NEED_SCHEDULE)
    {
        schedule(SchedMode::SM_PREEMPT);
    }
}

#[cfg(test)]
mod tests {
    use super::loadavg;
    use crate::sched::CpuRunQueue;
    use crate::smp::cpu::ProcessorId;

    #[test]
    fn rq_overload_clears_after_nr_running_drops_below_two() {
        let mut rq = CpuRunQueue::new(ProcessorId::new(0));
        let global_before = loadavg::nr_running();

        rq.add_nr_running(2);
        assert!(rq.overload);
        assert_eq!(rq.nr_running, 2);

        rq.sub_nr_running(1);
        assert!(!rq.overload);
        assert_eq!(rq.nr_running, 1);

        assert_eq!(loadavg::nr_running(), global_before + 1);

        rq.sub_nr_running(1);
        assert_eq!(rq.nr_running, 0);
        assert_eq!(loadavg::nr_running(), global_before);
    }
}
