add an SMP lock-stress test case
Four pairs push 400k sequenced messages through small channels; checks FIFO order and cross-core execution. Verified to fail with the lock disabled.
This commit is contained in:
@@ -70,6 +70,8 @@ pub fn run(case: []const u8, boot_info: *const BootInfo) void {
|
||||
ipcTest();
|
||||
} else if (eql(case, "smp")) {
|
||||
smpTest();
|
||||
} else if (eql(case, "smp-stress")) {
|
||||
stressTest();
|
||||
} else if (eql(case, "fault-ud")) {
|
||||
faultInvalidOpcode();
|
||||
} else if (eql(case, "fault-pf")) {
|
||||
@@ -482,6 +484,92 @@ fn smpTest() void {
|
||||
result();
|
||||
}
|
||||
|
||||
// --- SMP stress: hammer the big kernel lock across cores ------------------
|
||||
|
||||
const stress_pairs = 4; // producer/consumer pairs (8 tasks; fits the 16-task pool)
|
||||
const stress_msgs = 100_000; // messages per pair
|
||||
const stress_cap = 4; // small channel -> constant block/wake, more lock churn
|
||||
|
||||
var stress_chan = [_]ipc.Channel(u64, stress_cap){.{}} ** stress_pairs;
|
||||
var stress_recv = [_]u64{0} ** stress_pairs; // messages received per pair
|
||||
var stress_order_ok = [_]bool{true} ** stress_pairs; // FIFO order held per pair
|
||||
var stress_cores = [_]bool{false} ** 8; // cores that ran a consumer
|
||||
var stress_prod_claim: usize = 0;
|
||||
var stress_cons_claim: usize = 0;
|
||||
|
||||
fn stressProducer() void {
|
||||
// Claim a unique pair index (atomic: producers start on different cores).
|
||||
const idx = @atomicRmw(usize, &stress_prod_claim, .Add, 1, .monotonic);
|
||||
var v: u64 = 1;
|
||||
while (v <= stress_msgs) : (v += 1) stress_chan[idx].send(v);
|
||||
sched.exit();
|
||||
}
|
||||
|
||||
fn stressConsumer() void {
|
||||
const idx = @atomicRmw(usize, &stress_cons_claim, .Add, 1, .monotonic);
|
||||
var expected: u64 = 1;
|
||||
while (expected <= stress_msgs) : (expected += 1) {
|
||||
const got = stress_chan[idx].recv();
|
||||
if (got != expected) stress_order_ok[idx] = false; // lost/reordered => lock broke
|
||||
const c = sched.currentCpuIndex();
|
||||
if (c < stress_cores.len) stress_cores[c] = true;
|
||||
stress_recv[idx] = expected;
|
||||
}
|
||||
sched.exit();
|
||||
}
|
||||
|
||||
/// Stress the big kernel lock under sustained cross-core contention. Each pair drives
|
||||
/// `stress_msgs` sequenced messages through a 4-slot channel — every send and recv
|
||||
/// takes the lock, and the small buffer forces constant block/wake (so the scheduler
|
||||
/// churns too). A single-producer/single-consumer channel must deliver in strict FIFO
|
||||
/// order; if the lock let two cores into a critical section at once, the ring buffer
|
||||
/// corrupts and the consumer sees a wrong or out-of-order value (or the run hangs /
|
||||
/// faults). Passing means ~320k lock acquisitions across the cores stayed consistent.
|
||||
fn stressTest() void {
|
||||
log("DANOS-TEST-BEGIN: smp-stress\n", .{});
|
||||
stress_chan = [_]ipc.Channel(u64, stress_cap){.{}} ** stress_pairs;
|
||||
stress_recv = [_]u64{0} ** stress_pairs;
|
||||
stress_order_ok = [_]bool{true} ** stress_pairs;
|
||||
stress_cores = [_]bool{false} ** 8;
|
||||
stress_prod_claim = 0;
|
||||
stress_cons_claim = 0;
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < stress_pairs) : (i += 1) sched.spawn(stressConsumer, 4);
|
||||
i = 0;
|
||||
while (i < stress_pairs) : (i += 1) sched.spawn(stressProducer, 4);
|
||||
|
||||
// Drop below the workers so they get the cores; wake periodically to check for
|
||||
// completion. A broken lock instead hangs here (harness timeout) or faults.
|
||||
sched.setPriority(1);
|
||||
var spins: u64 = 0;
|
||||
while (spins < 40_000_000_000) : (spins += 1) {
|
||||
var done = true;
|
||||
for (stress_recv) |n| {
|
||||
if (n < stress_msgs) done = false;
|
||||
}
|
||||
if (done) break;
|
||||
}
|
||||
sched.setPriority(4);
|
||||
|
||||
var total: u64 = 0;
|
||||
for (stress_recv) |n| total += n;
|
||||
var order_ok = true;
|
||||
for (stress_order_ok) |ok| {
|
||||
if (!ok) order_ok = false;
|
||||
}
|
||||
var cores: u32 = 0;
|
||||
for (stress_cores) |s| {
|
||||
if (s) cores += 1;
|
||||
}
|
||||
|
||||
log("DANOS-STRESS: {d}/{d} pairs complete on {d} cores\n", .{ total, @as(u64, stress_pairs) * stress_msgs, cores });
|
||||
check("every message delivered", total == @as(u64, stress_pairs) * stress_msgs);
|
||||
check("strict FIFO order held (no lock corruption)", order_ok);
|
||||
check("contention was genuinely cross-core", cores >= 2);
|
||||
result();
|
||||
}
|
||||
|
||||
fn faultInvalidOpcode() void {
|
||||
log("DANOS-TEST-BEGIN: fault-ud\n", .{});
|
||||
asm volatile ("ud2");
|
||||
|
||||
Reference in New Issue
Block a user