kernel: Schedule retries for deferred timeouts

fa1aa037b3f307480342179a778a860c54e2dbd4fd068c0341f3942bdf97d337
Alexis Sellier committed ago 1 parent cd18ef8b
kernel/kernel/dispatch.rad +2 -2
150 150
        }
151 151
    }
152 152
    lifecycle::cancelRequests(&mut domains::STORE, &mut remote::STORE);
153 153
    let cancelled = lifecycle::cancelAllocations(&mut lifecycle::CALLS, &mut domains::STORE, &mut pages::STORE, &mut loader::STATE, &mut registry::STORE);
154 154
    let maintenanceClock = now(HARTS[id].timer);
155 -
    let mut maintenance = cancelled or domains::STORE.dead > 0 or pages::STORE.backings.pool.retiredCount > 0;
155 +
    let mut maintenance = timeout.pending or cancelled or domains::STORE.dead > 0 or pages::STORE.backings.pool.retiredCount > 0;
156 156
    if maintenance and maintenanceClock >= HARTS[id].nextMaintenance {
157 157
        let reaped = lifecycle::reapNext(&mut domains::STORE, &mut pages::STORE, &mut budgets::STORE, &mut interrupts::STORE);
158 158
        let reclaimed = frames::reclaim(&mut pages::STORE.backings.pool, 256);
159 -
        set maintenance = cancelled or reaped or pages::STORE.backings.pool.retiredCount > 0;
159 +
        set maintenance = timeout.pending or cancelled or reaped or pages::STORE.backings.pool.retiredCount > 0;
160 160
        let after = now(HARTS[id].timer);
161 161
        let interval = HARTS[id].maintenance;
162 162
        set HARTS[id].nextMaintenance = 0xffffffffffffffff;
163 163
        if interval < 0xffffffffffffffff - after {
164 164
            set HARTS[id].nextMaintenance = after + interval;
kernel/kernel/tests/timers.rad +18 -3
28 28
    set DOMAINS.records[owner.index].memory = &mut MEMORY;
29 29
    try! events::open(&mut DOMAINS.events, owner, &mut MEMORY.ring);
30 30
    return owner;
31 31
}
32 32
33 -
/// A timeout reports the hart of a receiver whose context moved before delivery.
33 +
/// A deferred timeout retries on its owning hart after its receiver waits elsewhere.
34 34
@test unsafe fn remoteReceiver() throws (testing::TestError) {
35 35
    let owner = initialize();
36 36
    set DOMAINS.contextSlots[0] = slots::Slot { generation: 1, state: slots::State::Live };
37 37
    let context = abi::Ref { index: 0, generation: 1 };
38 38
    set DOMAINS.contexts[0].owner = owner;
40 40
    set BUDGETS.slots[0] = slots::Slot { generation: 1, state: slots::State::Live };
41 41
    set BUDGETS.windows[0] = budgets::Window {
42 42
        hart: 1, start: 10, end: 100, owner, handle: abi::Handle(0), context,
43 43
    };
44 44
    try! timers::request(&mut TIMERS, &DOMAINS, owner, 0, 0, 15, 17);
45 +
    for i in 0..events::ORDINARY {
46 +
        assert try! domains::notify(&mut DOMAINS, owner,
47 +
            events::Event { kind: events::WAKEUP, reserved: 0, code: i, value: 0 });
48 +
    }
49 +
    let deferred = try! timers::service(&mut TIMERS, &mut DOMAINS, &BUDGETS, 0, 15);
50 +
    assert deferred.pending and deferred.harts == 0;
51 +
    for i in 0..events::ORDINARY {
52 +
        let event = try! events::pop(&mut MEMORY.ring) else panic "wake";
53 +
    }
54 +
    set DOMAINS.contexts[0].state = domains::ContextState::Waiting;
55 +
    let remote = try! timers::service(&mut TIMERS, &mut DOMAINS, &BUDGETS, 1, 15);
56 +
    assert not remote.pending and remote.harts == 0;
45 57
    let result = try! timers::service(&mut TIMERS, &mut DOMAINS, &BUDGETS, 0, 15);
46 -
    assert result.harts == 2 and result.deadline == 0xffffffffffffffff;
58 +
    assert result.harts == 2 and result.deadline == 0xffffffffffffffff and not result.pending;
47 59
    assert DOMAINS.contexts[0].state == domains::ContextState::Ready;
48 60
    let again = try! timers::service(&mut TIMERS, &mut DOMAINS, &BUDGETS, 0, 16);
49 61
    assert again.harts == 0;
50 62
}
51 63
77 89
    let mut full = false;
78 90
    try timers::request(&mut TIMERS, &DOMAINS, owner, 0, 0, 1, 99) catch error {
79 91
        assert (error == abi::Error::Busy); set full = true;
80 92
    };
81 93
    assert (full);
82 -
    assert ((try! timers::service(&mut TIMERS, &mut DOMAINS, &BUDGETS, 0, 1)).deadline == 0xffffffffffffffff);
94 +
    let deferred = try! timers::service(&mut TIMERS, &mut DOMAINS, &BUDGETS, 0, 1);
95 +
    assert deferred.pending and deferred.deadline == 0xffffffffffffffff;
96 +
    assert ((try! timers::service(&mut TIMERS, &mut DOMAINS, &BUDGETS, 0, 2)).pending);
83 97
    for i in 0..events::ORDINARY {
84 98
        let event = try! events::pop(&mut MEMORY.ring) else panic "wake";
85 99
    }
86 100
    let next = try! timers::service(&mut TIMERS, &mut DOMAINS, &BUDGETS, 0, 2);
101 +
    assert not next.pending;
87 102
    for i in 0..limits::TIMEOUTS_PER_HART {
88 103
        let event = try! events::pop(&mut MEMORY.ring) else panic "retained timeout";
89 104
        assert (event.kind == events::TIMEOUT and event.code == i);
90 105
    }
91 106
    try! timers::request(&mut TIMERS, &DOMAINS, owner, 0, 2, 1, 100);
kernel/kernel/timers.rad +6 -2
59 59
export record Result: Copy {
60 60
    /// Earliest future timeout, or the maximum tick when none is armed.
61 61
    deadline: u64,
62 62
    /// Harts with retained receiver authority that must refresh dispatch.
63 63
    harts: u32,
64 +
    /// Due notifications that require a scheduled maintenance retry.
65 +
    pending: bool,
64 66
}
65 67
66 68
/// Deliver due entries and report the next boundary and affected dispatch harts.
67 -
/// Full queues retain due entries for the next kernel entry without a timer spin.
69 +
/// Full queues retain due entries and request a scheduled maintenance retry.
68 70
export unsafe fn service(store: &mut Store, receivers: &mut domains::Store, windows: &budgets::Store, hart: u32, now: u64) -> Result throws (abi::Error) {
69 71
    if hart >= limits::HARTS {
70 72
        throw abi::Error::InvalidArg;
71 73
    }
72 -
    let mut result = Result { deadline: 0xffffffffffffffff, harts: 0 };
74 +
    let mut result = Result { deadline: 0xffffffffffffffff, harts: 0, pending: false };
73 75
    for i in 0..limits::TIMEOUTS_PER_HART {
74 76
        let entry = store.entries[hart][i];
75 77
        let owner = entry.owner else {
76 78
            continue;
77 79
        };
87 89
            continue;
88 90
        }
89 91
        if try domains::notify(receivers, owner, events::Event { kind: events::TIMEOUT, reserved: 0, code: entry.token, value: 0 }) {
90 92
            set store.entries[hart][i].owner = nil;
91 93
            set result.harts |= budgets::wakeTargets(windows, receivers, owner, now);
94 +
        } else {
95 +
            set result.pending = true;
92 96
        }
93 97
    }
94 98
    return result;
95 99
}