kernel: Schedule retries for deferred timeouts
fa1aa037b3f307480342179a778a860c54e2dbd4fd068c0341f3942bdf97d337
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 | } |