Nothing to see here, move along meow
0

Configure Feed

Select the types of activity you want to include in your feed.

at main 16 kB View raw
1use crate::cap::object::ObjectTag; 2use crate::cap::pool::POOL; 3use crate::mem::phys::BitmapFrameAllocator; 4use crate::proc::{BlockedReason, PROCESSES}; 5use crate::sched; 6use crate::types::Priority; 7use lancer_core::header::KernelObjectHeader; 8use lancer_core::object_layout::{EndpointObject, KernelObject, SchedContextObject}; 9 10fn attach_priority(pid: crate::types::Pid, priority: Priority) { 11 let header = KernelObjectHeader::new(ObjectTag::SchedContext, 0, 64); 12 let mut sc = SchedContextObject::init_default(header); 13 sc.budget_us = 10_000; 14 sc.period_us = 100_000; 15 sc.remaining_us = 10_000; 16 sc.priority = priority.raw(); 17 sc.attached_pid = lancer_core::header::NONE_SENTINEL; 18 let (sc_id, sc_gen) = 19 crate::tests::helpers::alloc_typed(&mut POOL.lock(), ObjectTag::SchedContext, sc) 20 .expect("alloc sched context"); 21 let mut ptable = PROCESSES.lock(); 22 ptable[pid].attach_sched_context(sc_id, sc_gen, priority); 23} 24 25crate::kernel_test!( 26 fn pick_next_selects_highest_priority() { 27 let mut allocator = BitmapFrameAllocator; 28 let mut ptable = PROCESSES.lock(); 29 30 let a_created = ptable.allocate(&mut allocator).expect("alloc A"); 31 let b_created = ptable.allocate(&mut allocator).expect("alloc B"); 32 ptable.start(a_created).expect("start A"); 33 ptable.start(b_created).expect("start B"); 34 let a_pid = a_created.pid(); 35 let b_pid = b_created.pid(); 36 drop(ptable); 37 38 attach_priority(a_pid, Priority::new(50)); 39 attach_priority(b_pid, Priority::new(100)); 40 41 let ptable = PROCESSES.lock(); 42 let best = (0..crate::types::MAX_PIDS as u32).fold( 43 None::<(crate::types::Pid, Priority)>, 44 |best, idx| { 45 let pid = crate::types::Pid::new(idx); 46 let proc = match ptable.get(pid) { 47 Some(p) => p, 48 None => return best, 49 }; 50 if !proc.is_runnable() { 51 return best; 52 } 53 let prio = proc.effective_priority(); 54 match best { 55 Some((_, best_prio)) if prio <= best_prio => best, 56 _ => Some((pid, prio)), 57 } 58 }, 59 ); 60 61 assert!( 62 best.is_some_and(|(pid, _)| pid == b_pid), 63 "pick_next should select highest priority process (B)" 64 ); 65 drop(ptable); 66 67 let mut ptable = PROCESSES.lock(); 68 ptable.destroy(a_pid, &mut allocator); 69 ptable.destroy(b_pid, &mut allocator); 70 } 71); 72 73crate::kernel_test!( 74 fn pick_next_skips_blocked_processes() { 75 let mut allocator = BitmapFrameAllocator; 76 let mut ptable = PROCESSES.lock(); 77 78 let a_created = ptable.allocate(&mut allocator).expect("alloc A"); 79 let b_created = ptable.allocate(&mut allocator).expect("alloc B"); 80 ptable.start(a_created).expect("start A"); 81 ptable.start(b_created).expect("start B"); 82 let a_pid = a_created.pid(); 83 let b_pid = b_created.pid(); 84 drop(ptable); 85 86 attach_priority(a_pid, Priority::new(200)); 87 attach_priority(b_pid, Priority::new(50)); 88 89 let mut ptable = PROCESSES.lock(); 90 ptable.simulate_dispatch(a_pid); 91 let (ep_id, ep_gen) = 92 crate::tests::helpers::alloc_endpoint(&mut POOL.lock()).expect("alloc ep"); 93 let _ = ptable[a_pid] 94 .block_on(crate::proc::BlockedReason::Sending(ep_id, ep_gen)) 95 .expect("block A"); 96 97 let best = (0..crate::types::MAX_PIDS as u32).fold( 98 None::<(crate::types::Pid, Priority)>, 99 |best, idx| { 100 let pid = crate::types::Pid::new(idx); 101 let proc = match ptable.get(pid) { 102 Some(p) => p, 103 None => return best, 104 }; 105 if !proc.is_runnable() { 106 return best; 107 } 108 let prio = proc.effective_priority(); 109 match best { 110 Some((_, best_prio)) if prio <= best_prio => best, 111 _ => Some((pid, prio)), 112 } 113 }, 114 ); 115 116 assert!( 117 best.is_some_and(|(pid, _)| pid == b_pid), 118 "should skip blocked A and select B" 119 ); 120 121 ptable.destroy(a_pid, &mut allocator); 122 ptable.destroy(b_pid, &mut allocator); 123 let _ = POOL.lock().dec_ref_phys(ep_id, ep_gen); 124 } 125); 126 127crate::kernel_test!( 128 fn pick_next_skips_exhausted_budget() { 129 let mut allocator = BitmapFrameAllocator; 130 let mut ptable = PROCESSES.lock(); 131 132 let a_created = ptable.allocate(&mut allocator).expect("alloc A"); 133 let b_created = ptable.allocate(&mut allocator).expect("alloc B"); 134 ptable.start(a_created).expect("start A"); 135 ptable.start(b_created).expect("start B"); 136 let a_pid = a_created.pid(); 137 let b_pid = b_created.pid(); 138 drop(ptable); 139 140 let header = KernelObjectHeader::new(ObjectTag::SchedContext, 0, 64); 141 let mut sc = SchedContextObject::init_default(header); 142 sc.budget_us = 1000; 143 sc.period_us = 10000; 144 sc.remaining_us = 0; 145 sc.priority = Priority::new(200).raw(); 146 sc.replenish_at = u64::MAX; 147 sc.attached_pid = a_pid.raw(); 148 let (sc_id, sc_gen) = 149 crate::tests::helpers::alloc_typed(&mut POOL.lock(), ObjectTag::SchedContext, sc) 150 .expect("alloc sched context"); 151 { 152 let mut ptable = PROCESSES.lock(); 153 ptable[a_pid].attach_sched_context(sc_id, sc_gen, Priority::new(200)); 154 } 155 156 attach_priority(b_pid, Priority::new(50)); 157 158 let ptable = PROCESSES.lock(); 159 let mut pool = POOL.lock(); 160 let now_us = crate::wcet::tsc::cycles_to_ns(crate::wcet::tsc::read_tsc()) / 1000; 161 162 let budget_ok_a = match ptable[a_pid].sched_context() { 163 None => true, 164 Some((sc_id, sc_gen)) => match pool.write_as::<SchedContextObject>(sc_id, sc_gen) { 165 Ok(sc) => { 166 crate::sched::context::replenish(sc, now_us); 167 !crate::sched::context::is_exhausted(sc) 168 } 169 Err(_) => true, 170 }, 171 }; 172 drop(pool); 173 drop(ptable); 174 175 assert!(!budget_ok_a, "A should have exhausted budget"); 176 177 let ptable = PROCESSES.lock(); 178 assert!(ptable[b_pid].is_runnable(), "B should be runnable"); 179 drop(ptable); 180 181 let mut ptable = PROCESSES.lock(); 182 ptable.destroy(a_pid, &mut allocator); 183 ptable.destroy(b_pid, &mut allocator); 184 } 185); 186 187crate::kernel_test!( 188 fn budget_consume_decrements_remaining() { 189 let header = KernelObjectHeader::new(ObjectTag::SchedContext, 0, 64); 190 let mut sc = SchedContextObject::init_default(header); 191 sc.budget_us = 1000; 192 sc.period_us = 10000; 193 sc.remaining_us = 1000; 194 sc.priority = Priority::new(100).raw(); 195 assert!(sc.remaining_us == 1000); 196 197 crate::sched::context::consume(&mut sc, 300, 1000); 198 assert!( 199 sc.remaining_us == 700, 200 "remaining should be 700, got {}", 201 sc.remaining_us 202 ); 203 204 crate::sched::context::consume(&mut sc, 500, 1500); 205 assert!( 206 sc.remaining_us == 200, 207 "remaining should be 200, got {}", 208 sc.remaining_us 209 ); 210 } 211); 212 213crate::kernel_test!( 214 fn budget_consume_saturates_at_zero() { 215 let header = KernelObjectHeader::new(ObjectTag::SchedContext, 0, 64); 216 let mut sc = SchedContextObject::init_default(header); 217 sc.budget_us = 100; 218 sc.period_us = 1000; 219 sc.remaining_us = 100; 220 sc.priority = Priority::new(100).raw(); 221 222 crate::sched::context::consume(&mut sc, 200, 1000); 223 assert!(sc.remaining_us == 0, "should saturate at 0"); 224 assert!( 225 crate::sched::context::is_exhausted(&sc), 226 "should be exhausted" 227 ); 228 } 229); 230 231crate::kernel_test!( 232 fn budget_replenish_resets_remaining() { 233 let header = KernelObjectHeader::new(ObjectTag::SchedContext, 0, 64); 234 let mut sc = SchedContextObject::init_default(header); 235 sc.budget_us = 1000; 236 sc.period_us = 5000; 237 sc.remaining_us = 0; 238 sc.replenish_at = 100; 239 sc.priority = Priority::new(100).raw(); 240 241 crate::sched::context::replenish(&mut sc, 200); 242 assert!( 243 sc.remaining_us == 1000, 244 "remaining should be reset to budget" 245 ); 246 assert!( 247 sc.replenish_at == 100 + 5000, 248 "replenish_at should advance by period from previous at" 249 ); 250 } 251); 252 253crate::kernel_test!( 254 fn budget_replenish_ignores_early_call() { 255 let header = KernelObjectHeader::new(ObjectTag::SchedContext, 0, 64); 256 let mut sc = SchedContextObject::init_default(header); 257 sc.budget_us = 1000; 258 sc.period_us = 5000; 259 sc.remaining_us = 0; 260 sc.replenish_at = 500; 261 sc.priority = Priority::new(100).raw(); 262 263 crate::sched::context::replenish(&mut sc, 100); 264 assert!( 265 sc.remaining_us == 0, 266 "should not replenish before replenish_at" 267 ); 268 } 269); 270 271crate::kernel_test!( 272 fn effective_priority_propagation_chain() { 273 let mut allocator = BitmapFrameAllocator; 274 let mut ptable = PROCESSES.lock(); 275 276 let a_created = ptable.allocate(&mut allocator).expect("alloc A"); 277 let b_created = ptable.allocate(&mut allocator).expect("alloc B"); 278 let c_created = ptable.allocate(&mut allocator).expect("alloc C"); 279 ptable.start(a_created).expect("start A"); 280 ptable.start(b_created).expect("start B"); 281 ptable.start(c_created).expect("start C"); 282 let a_pid = a_created.pid(); 283 let b_pid = b_created.pid(); 284 let c_pid = c_created.pid(); 285 286 let (ep1_id, ep1_gen) = 287 crate::tests::helpers::alloc_endpoint(&mut POOL.lock()).expect("alloc ep1"); 288 let (ep2_id, ep2_gen) = 289 crate::tests::helpers::alloc_endpoint(&mut POOL.lock()).expect("alloc ep2"); 290 291 { 292 let mut pool = POOL.lock(); 293 pool.write_as::<EndpointObject>(ep1_id, ep1_gen) 294 .expect("get ep1") 295 .holder = b_pid.raw(); 296 pool.write_as::<EndpointObject>(ep2_id, ep2_gen) 297 .expect("get ep2") 298 .holder = c_pid.raw(); 299 } 300 301 ptable.simulate_dispatch(a_pid); 302 let _ = ptable[a_pid] 303 .block_on(crate::proc::BlockedReason::Sending(ep1_id, ep1_gen)) 304 .expect("block A"); 305 306 ptable.simulate_dispatch(b_pid); 307 let _ = ptable[b_pid] 308 .block_on(crate::proc::BlockedReason::Sending(ep2_id, ep2_gen)) 309 .expect("block B"); 310 311 { 312 let pool = POOL.lock(); 313 sched::propagate_priority(&mut ptable, &pool, a_pid, Priority::new(250)); 314 } 315 316 assert!( 317 ptable[a_pid].effective_priority() >= Priority::new(250), 318 "A should have boosted priority" 319 ); 320 assert!( 321 ptable[b_pid].effective_priority() >= Priority::new(250), 322 "B should have boosted priority via holder chain" 323 ); 324 assert!( 325 ptable[c_pid].effective_priority() >= Priority::new(250), 326 "C should have boosted priority via holder chain" 327 ); 328 329 ptable.destroy(a_pid, &mut allocator); 330 ptable.destroy(b_pid, &mut allocator); 331 ptable.destroy(c_pid, &mut allocator); 332 let _ = POOL.lock().dec_ref_phys(ep1_id, ep1_gen); 333 let _ = POOL.lock().dec_ref_phys(ep2_id, ep2_gen); 334 } 335); 336 337crate::kernel_test!( 338 fn priority_propagation_cycle_terminates() { 339 let mut allocator = BitmapFrameAllocator; 340 let mut ptable = PROCESSES.lock(); 341 342 let a_created = ptable.allocate(&mut allocator).expect("alloc A"); 343 let b_created = ptable.allocate(&mut allocator).expect("alloc B"); 344 ptable.start(a_created).expect("start A"); 345 ptable.start(b_created).expect("start B"); 346 let a_pid = a_created.pid(); 347 let b_pid = b_created.pid(); 348 349 let (ep1_id, ep1_gen) = 350 crate::tests::helpers::alloc_endpoint(&mut POOL.lock()).expect("alloc ep1"); 351 let (ep2_id, ep2_gen) = 352 crate::tests::helpers::alloc_endpoint(&mut POOL.lock()).expect("alloc ep2"); 353 354 { 355 let mut pool = POOL.lock(); 356 pool.write_as::<EndpointObject>(ep1_id, ep1_gen) 357 .expect("get ep1") 358 .holder = b_pid.raw(); 359 pool.write_as::<EndpointObject>(ep2_id, ep2_gen) 360 .expect("get ep2") 361 .holder = a_pid.raw(); 362 } 363 364 ptable.simulate_dispatch(a_pid); 365 let _ = ptable[a_pid] 366 .block_on(BlockedReason::Sending(ep1_id, ep1_gen)) 367 .expect("block A"); 368 369 ptable.simulate_dispatch(b_pid); 370 let _ = ptable[b_pid] 371 .block_on(BlockedReason::Sending(ep2_id, ep2_gen)) 372 .expect("block B"); 373 374 { 375 let pool = POOL.lock(); 376 sched::propagate_priority(&mut ptable, &pool, a_pid, Priority::new(200)); 377 } 378 379 assert!( 380 ptable[a_pid].effective_priority() >= Priority::new(200), 381 "A should have boosted priority after propagation" 382 ); 383 assert!( 384 ptable[b_pid].effective_priority() >= Priority::new(200), 385 "B should have boosted priority (propagated through chain before cycle detected)" 386 ); 387 388 ptable.destroy(a_pid, &mut allocator); 389 ptable.destroy(b_pid, &mut allocator); 390 let _ = POOL.lock().dec_ref_phys(ep1_id, ep1_gen); 391 let _ = POOL.lock().dec_ref_phys(ep2_id, ep2_gen); 392 } 393); 394 395crate::kernel_test!( 396 fn sched_create_rejects_huge_period() { 397 let budget_us: u64 = 1000; 398 let period_us: u64 = 4_000_000_000; 399 let max_period_us: u64 = 3_600_000_000; 400 401 assert!( 402 period_us > max_period_us, 403 "period exceeding MAX_PERIOD_US should be rejected by validation" 404 ); 405 406 let valid = 407 budget_us > 0 && period_us > 0 && budget_us <= period_us && period_us <= max_period_us; 408 assert!(!valid, "huge period should fail validation"); 409 } 410); 411 412crate::kernel_test!( 413 fn stale_sched_context_treated_as_exhausted() { 414 let mut allocator = BitmapFrameAllocator; 415 let mut ptable = PROCESSES.lock(); 416 417 let proc_created = ptable.allocate(&mut allocator).expect("alloc proc"); 418 ptable.start(proc_created).expect("start proc"); 419 let pid = proc_created.pid(); 420 421 let header = KernelObjectHeader::new(ObjectTag::SchedContext, 0, 64); 422 let mut sc = SchedContextObject::init_default(header); 423 sc.budget_us = 1000; 424 sc.period_us = 10000; 425 sc.remaining_us = 1000; 426 sc.priority = Priority::new(100).raw(); 427 let (sc_id, sc_gen) = 428 crate::tests::helpers::alloc_typed(&mut POOL.lock(), ObjectTag::SchedContext, sc) 429 .expect("alloc sched context"); 430 431 ptable[pid].attach_sched_context(sc_id, sc_gen, Priority::new(100)); 432 433 let _ = POOL.lock().revoke_phys(sc_id, sc_gen); 434 435 assert!( 436 ptable[pid].sched_context().is_some(), 437 "process still references stale sched context" 438 ); 439 440 { 441 let mut pool = POOL.lock(); 442 let budget_ok = match ptable[pid].sched_context() { 443 None => true, 444 Some((id, generation)) => match pool.write_as::<SchedContextObject>(id, generation) 445 { 446 Ok(sc) => { 447 crate::sched::context::replenish(sc, 0); 448 !crate::sched::context::is_exhausted(sc) 449 } 450 Err(_) => false, 451 }, 452 }; 453 assert!( 454 !budget_ok, 455 "stale sched context must be treated as exhausted (budget_ok=false), not unlimited" 456 ); 457 } 458 459 ptable.destroy(pid, &mut allocator); 460 } 461);