kernel: Schedule retries for deferred timeouts
920f8fc3a34f161d9350d08812b39397d0fe2c76eae1bb7d11d6dce202ba7787
1 parent
b6ea2575
kernel/kernel/dispatch.rad
+2 -2
| 133 | 133 | if let case remote::Action::Ready(context) = delivery.request.action { signalContext(context, clock); } |
|
| 134 | 134 | } |
|
| 135 | 135 | lifecycle::cancelRequests(&mut domains::STORE, &mut remote::STORE); |
|
| 136 | 136 | let cancelled = lifecycle::cancelAllocations(&mut lifecycle::CALLS, &mut domains::STORE, &mut pages::STORE, &mut loader::STATE, &mut registry::STORE); |
|
| 137 | 137 | let maintenanceClock = now(HARTS[id].timer); |
|
| 138 | - | let mut maintenance = cancelled or domains::STORE.dead > 0 or pages::STORE.backings.pool.retiredCount > 0; |
|
| 138 | + | let mut maintenance = timeout.pending or cancelled or domains::STORE.dead > 0 or pages::STORE.backings.pool.retiredCount > 0; |
|
| 139 | 139 | if maintenance and maintenanceClock >= HARTS[id].nextMaintenance { |
|
| 140 | 140 | let reaped = lifecycle::reapNext(&mut domains::STORE, &mut pages::STORE, &mut budgets::STORE, &mut interrupts::STORE); |
|
| 141 | 141 | let reclaimed = frames::reclaim(&mut pages::STORE.backings.pool, 256); |
|
| 142 | - | set maintenance = cancelled or reaped or pages::STORE.backings.pool.retiredCount > 0; |
|
| 142 | + | set maintenance = timeout.pending or cancelled or reaped or pages::STORE.backings.pool.retiredCount > 0; |
|
| 143 | 143 | let after = now(HARTS[id].timer); |
|
| 144 | 144 | let interval = HARTS[id].maintenance; |
|
| 145 | 145 | set HARTS[id].nextMaintenance = 0xffffffffffffffff; |
|
| 146 | 146 | if interval < 0xffffffffffffffff - after { set HARTS[id].nextMaintenance = after + interval; } |
|
| 147 | 147 | } |
kernel/kernel/tests/timers.rad
+16 -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 { let event = try! events::pop(&mut MEMORY.ring) else panic "wake"; } |
|
| 52 | + | set DOMAINS.contexts[0].state = domains::ContextState::Waiting; |
|
| 53 | + | let remote = try! timers::service(&mut TIMERS, &mut DOMAINS, &BUDGETS, 1, 15); |
|
| 54 | + | assert not remote.pending and remote.harts == 0; |
|
| 45 | 55 | let result = try! timers::service(&mut TIMERS, &mut DOMAINS, &BUDGETS, 0, 15); |
|
| 46 | - | assert result.harts == 2 and result.deadline == 0xffffffffffffffff; |
|
| 56 | + | assert result.harts == 2 and result.deadline == 0xffffffffffffffff and not result.pending; |
|
| 47 | 57 | assert DOMAINS.contexts[0].state == domains::ContextState::Ready; |
|
| 48 | 58 | let again = try! timers::service(&mut TIMERS, &mut DOMAINS, &BUDGETS, 0, 16); |
|
| 49 | 59 | assert again.harts == 0; |
|
| 50 | 60 | } |
|
| 51 | 61 |
| 75 | 85 | let mut full = false; |
|
| 76 | 86 | try timers::request(&mut TIMERS, &DOMAINS, owner, 0, 0, 1, 99) catch error { |
|
| 77 | 87 | assert (error == abi::Error::Busy); set full = true; |
|
| 78 | 88 | }; |
|
| 79 | 89 | assert (full); |
|
| 80 | - | assert ((try! timers::service(&mut TIMERS, &mut DOMAINS, &BUDGETS, 0, 1)).deadline == 0xffffffffffffffff); |
|
| 90 | + | let deferred = try! timers::service(&mut TIMERS, &mut DOMAINS, &BUDGETS, 0, 1); |
|
| 91 | + | assert deferred.pending and deferred.deadline == 0xffffffffffffffff; |
|
| 92 | + | assert ((try! timers::service(&mut TIMERS, &mut DOMAINS, &BUDGETS, 0, 2)).pending); |
|
| 81 | 93 | for i in 0..events::ORDINARY { let event = try! events::pop(&mut MEMORY.ring) else panic "wake"; } |
|
| 82 | 94 | let next = try! timers::service(&mut TIMERS, &mut DOMAINS, &BUDGETS, 0, 2); |
|
| 95 | + | assert not next.pending; |
|
| 83 | 96 | for i in 0..limits::TIMEOUTS_PER_HART { |
|
| 84 | 97 | let event = try! events::pop(&mut MEMORY.ring) else panic "retained timeout"; |
|
| 85 | 98 | assert (event.kind == events::TIMEOUT and event.code == i); |
|
| 86 | 99 | } |
|
| 87 | 100 | try! timers::request(&mut TIMERS, &DOMAINS, owner, 0, 2, 1, 100); |
kernel/kernel/timers.rad
+6 -2
| 51 | 51 | export record Result: Copy { |
|
| 52 | 52 | /// Earliest future timeout, or the maximum tick when none is armed. |
|
| 53 | 53 | deadline: u64, |
|
| 54 | 54 | /// Harts with retained receiver authority that must refresh dispatch. |
|
| 55 | 55 | harts: u32, |
|
| 56 | + | /// Due notifications that require a scheduled maintenance retry. |
|
| 57 | + | pending: bool, |
|
| 56 | 58 | } |
|
| 57 | 59 | ||
| 58 | 60 | /// Deliver due entries and report the next boundary and affected dispatch harts. |
|
| 59 | - | /// Full queues retain due entries for the next kernel entry without a timer spin. |
|
| 61 | + | /// Full queues retain due entries and request a scheduled maintenance retry. |
|
| 60 | 62 | export unsafe fn service(store: &mut Store, receivers: &mut domains::Store, windows: &budgets::Store, hart: u32, now: u64) -> Result throws (abi::Error) { |
|
| 61 | 63 | if hart >= limits::HARTS { throw abi::Error::InvalidArg; } |
|
| 62 | - | let mut result = Result { deadline: 0xffffffffffffffff, harts: 0 }; |
|
| 64 | + | let mut result = Result { deadline: 0xffffffffffffffff, harts: 0, pending: false }; |
|
| 63 | 65 | for i in 0..limits::TIMEOUTS_PER_HART { |
|
| 64 | 66 | let entry = store.entries[hart][i]; |
|
| 65 | 67 | let owner = entry.owner else { continue; }; |
|
| 66 | 68 | if not slots::matches(&receivers.slots[..], owner, slots::State::Live) |
|
| 67 | 69 | or receivers.records[owner.index].state == domains::Lifecycle::Dead { |
| 73 | 75 | continue; |
|
| 74 | 76 | } |
|
| 75 | 77 | if try domains::notify(receivers, owner, events::Event { kind: events::TIMEOUT, reserved: 0, code: entry.token, value: 0 }) { |
|
| 76 | 78 | set store.entries[hart][i].owner = nil; |
|
| 77 | 79 | set result.harts |= budgets::wakeTargets(windows, receivers, owner, now); |
|
| 80 | + | } else { |
|
| 81 | + | set result.pending = true; |
|
| 78 | 82 | } |
|
| 79 | 83 | } |
|
| 80 | 84 | return result; |
|
| 81 | 85 | } |