8#include "pedigree/kernel/ActivityDiagnostics.h"
9#include "pedigree/kernel/LockGuard.h"
10#include "pedigree/kernel/Log.h"
11#include "pedigree/kernel/machine/IrqHandler.h"
12#include "pedigree/kernel/machine/IrqHandlerRegistry.h"
13#include "pedigree/kernel/process/Scheduler.h"
14#include "pedigree/kernel/process/TerminationDeferral.h"
15#include "pedigree/kernel/process/Thread.h"
16#include "pedigree/kernel/processor/Processor.h"
17#include "pedigree/kernel/processor/ProcessorInformation.h"
18#include "pedigree/kernel/processor/state.h"
19#include "pedigree/kernel/utilities/assert.h"
21#include "system/kernel/core/processor/DeviceHardIrqContext.h"
23static_assert(__atomic_always_lock_free(
sizeof(
size_t),
nullptr),
24 "IRQ callback hazard words must be lock-free");
25static_assert(__atomic_always_lock_free(
sizeof(
void*),
nullptr),
26 "IRQ callback hazard pointers must be lock-free");
28IrqHandlerRegistry::IrqHandlerRegistry()
31 m_HardHandoffEpochs(),
32 m_ThreadedInvalidationGenerations(),
33 m_ThreadedActionMutationGeneration(0),
34 m_ThreadedActionMutationWriters(0),
36 m_OccurrenceReaders(),
37 m_OccurrenceBoundaryLocks(),
40 m_MutationGeneration(0),
42#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
44 m_HandlerPinHook(nullptr),
45 m_HandlerPrePinHook(nullptr),
46 m_HandlerHazardHook(nullptr),
47 m_DispatchAbandonHook(nullptr),
48 m_OccurrenceCaptureHook(nullptr)
53size_t IrqHandlerRegistry::makePublication(
size_t generation, uint8_t irq, SlotMode mode,
55 return (generation << GenerationShift) | (
static_cast<size_t>(irq) << IrqShift) |
56 (
static_cast<size_t>(delivery) << DeliveryShift) |
static_cast<size_t>(mode);
59size_t IrqHandlerRegistry::generationOf(
size_t publication) {
60 return publication >> GenerationShift;
63uint8_t IrqHandlerRegistry::irqOf(
size_t publication) {
64 return static_cast<uint8_t
>((publication & IrqMask) >> IrqShift);
67IrqHandlerRegistry::SlotMode IrqHandlerRegistry::modeOf(
size_t publication) {
68 return static_cast<SlotMode
>(publication & ModeMask);
71IrqHandlerRegistry::Delivery IrqHandlerRegistry::deliveryOf(
size_t publication) {
72 return static_cast<Delivery
>((publication & DeliveryMask) >> DeliveryShift);
75bool IrqHandlerRegistry::generationReached(
size_t current,
size_t target) {
76 return static_cast<intptr_t
>(current - target) >= 0;
79bool IrqHandlerRegistry::threadedGenerationValid(uint8_t irq,
size_t generation)
const {
84 const size_t invalidThrough =
85 __atomic_load_n(&m_ThreadedInvalidationGenerations[irq], __ATOMIC_ACQUIRE);
86 return !invalidThrough || !generationReached(invalidThrough, generation);
89size_t* IrqHandlerRegistry::quiescedLane(HandlerSlot& slot, QuiescedLane lane) {
90 const size_t index =
static_cast<size_t>(lane);
91 assert(index < QuiescedLaneCount);
92 return &slot.quiescedThreadedGenerations[index];
95const size_t* IrqHandlerRegistry::quiescedLane(
const HandlerSlot& slot, QuiescedLane lane) {
96 const size_t index =
static_cast<size_t>(lane);
97 assert(index < QuiescedLaneCount);
98 return &slot.quiescedThreadedGenerations[index];
101bool IrqHandlerRegistry::hasQuiescedGeneration(
const HandlerSlot& slot) {
102 for (
size_t i = 0; i < QuiescedLaneCount; ++i) {
103 if (__atomic_load_n(&slot.quiescedThreadedGenerations[i], __ATOMIC_ACQUIRE)) {
110void IrqHandlerRegistry::publishSlotQuiesced(HandlerSlot& slot, uint8_t irq,
111 size_t dispatchGeneration, QuiescedLane lane) {
112 if (!dispatchGeneration) {
116 ThreadedActionMutationCleanup actionMutation(
this);
117 beginThreadedActionMutation(actionMutation);
118 if (!threadedGenerationValid(irq, dispatchGeneration)) {
119 finishThreadedActionMutation(actionMutation);
123 publishSlotQuiescedValue(slot, irq, dispatchGeneration, lane);
124 finishThreadedActionMutation(actionMutation);
127void IrqHandlerRegistry::publishSlotQuiescedValue(HandlerSlot& slot, uint8_t irq,
128 size_t dispatchGeneration, QuiescedLane lane) {
129 if (!dispatchGeneration || !threadedGenerationValid(irq, dispatchGeneration)) {
133#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
134 HandlerHazardHook mutationHook = __atomic_load_n(&m_HandlerHazardHook, __ATOMIC_ACQUIRE);
136 mutationHook(__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE),
137 HandlerHazardStage::BeforeQuiescedExchange);
141 size_t* publicationLane = quiescedLane(slot, lane);
142 const size_t published = __atomic_load_n(publicationLane, __ATOMIC_ACQUIRE);
143 if (published && generationReached(published, dispatchGeneration)) {
147 __atomic_exchange_n(publicationLane, dispatchGeneration, __ATOMIC_ACQ_REL);
149#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
150 mutationHook = __atomic_load_n(&m_HandlerHazardHook, __ATOMIC_ACQUIRE);
152 mutationHook(__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE),
153 HandlerHazardStage::QuiescedExchanged);
157 if (!threadedGenerationValid(irq, dispatchGeneration)) {
158 size_t stale = dispatchGeneration;
159 __atomic_compare_exchange_n(publicationLane, &stale,
static_cast<size_t>(0),
false,
160 __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE);
165 size_t throughGeneration) {
166 if (!throughGeneration) {
170 const size_t invalidThrough =
171 __atomic_load_n(&m_ThreadedInvalidationGenerations[irq], __ATOMIC_ACQUIRE);
172 if (invalidThrough && generationReached(invalidThrough, throughGeneration)) {
176 __atomic_exchange_n(&m_ThreadedInvalidationGenerations[irq], throughGeneration, __ATOMIC_ACQ_REL);
180 if (!throughGeneration) {
185 beginThreadedActionMutation(actionMutation);
186 size_t invalidThrough =
187 __atomic_load_n(&m_ThreadedInvalidationGenerations[irq], __ATOMIC_ACQUIRE);
188 while (!invalidThrough || generationReached(throughGeneration, invalidThrough)) {
189 if (__atomic_compare_exchange_n(&m_ThreadedInvalidationGenerations[irq], &invalidThrough,
190 throughGeneration,
false, __ATOMIC_RELEASE, __ATOMIC_ACQUIRE)) {
194 if (invalidThrough && generationReached(invalidThrough, throughGeneration)) {
195 throughGeneration = invalidThrough;
198 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
200 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
201 if (irqOf(publication) != irq || deliveryOf(publication) != Delivery::Threaded) {
205 void* owner = currentDispatchOwner();
208#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
209 HandlerHazardHook mutationHook = __atomic_load_n(&m_HandlerHazardHook, __ATOMIC_ACQUIRE);
211 mutationHook(__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE),
212 HandlerHazardStage::BeforeActionMutationPin);
215 if (!pinActionMutation(slot, publication, cleanup, thread)) {
219 size_t* actions[] = {&slot.pendingThreadedGeneration, &slot.claimedThreadedGeneration,
220 quiescedLane(slot, QuiescedLane::Controller),
221 quiescedLane(slot, QuiescedLane::Callback),
222 quiescedLane(slot, QuiescedLane::Retirement)};
223 for (
size_t action = 0; action <
sizeof(actions) /
sizeof(actions[0]); ++action) {
224 size_t generation = __atomic_load_n(actions[action], __ATOMIC_ACQUIRE);
225 while (generation && generationReached(throughGeneration, generation)) {
226 if (__atomic_compare_exchange_n(actions[action], &generation,
static_cast<size_t>(0),
false,
227 __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)) {
232 unpinActionMutation(slot, publication, cleanup, thread);
234 finishThreadedActionMutation(actionMutation);
235 tryReclaimTombstones(irq);
238size_t IrqHandlerRegistry::encodePolicy(
const IrqPolicy* policy) {
243 return PolicyValid | (
static_cast<size_t>(policy->trigger()) << PolicyTriggerShift) |
244 (
static_cast<size_t>(policy->controllerAck()) << PolicyControllerAckShift) |
245 (
static_cast<size_t>(policy->lineRelease()) << PolicyLineReleaseShift);
248void IrqHandlerRegistry::decodePolicy(
size_t policy, LineConfiguration& configuration) {
249 configuration.policyConfigured = policy & PolicyValid;
250 if (!configuration.policyConfigured) {
254 configuration.trigger =
static_cast<IrqTrigger>((policy >> PolicyTriggerShift) & 3);
255 configuration.controllerAck =
257 configuration.lineRelease =
static_cast<IrqLineRelease>((policy >> PolicyLineReleaseShift) & 1);
260bool IrqHandlerRegistry::mixedPoliciesCompatible(
size_t first,
size_t second) {
261 if (!(first & PolicyValid) || !(second & PolicyValid)) {
265 return (first & PolicyMixedCompatibilityMask) == (second & PolicyMixedCompatibilityMask);
268size_t IrqHandlerRegistry::effectiveMixedPolicy(
size_t hard,
size_t threaded) {
269 size_t effective = hard;
270 if (threaded & PolicyLineReleaseMask) {
271 effective |= PolicyLineReleaseMask;
276IrqHandlerRegistry::LineMode IrqHandlerRegistry::lineModeForDelivery(Delivery delivery) {
277 return delivery == Delivery::Threaded ? LineMode::Threaded : LineMode::HardOnly;
280void IrqHandlerRegistry::beginMutation() {
283 __atomic_add_fetch(&m_MutationWriters,
static_cast<size_t>(1), __ATOMIC_SEQ_CST);
286void IrqHandlerRegistry::finishMutation() {
289 __atomic_add_fetch(&m_MutationGeneration,
static_cast<size_t>(1), __ATOMIC_SEQ_CST);
290 __atomic_sub_fetch(&m_MutationWriters,
static_cast<size_t>(1), __ATOMIC_SEQ_CST);
293void IrqHandlerRegistry::beginThreadedActionMutation(ThreadedActionMutationCleanup& cleanup) {
297 if (cleanup.thread) {
298 cleanup.thread->armAtomicStateCleanup(cleanup.cleanup, abandonThreadedActionMutation, &cleanup);
300 __atomic_add_fetch(&m_ThreadedActionMutationWriters,
static_cast<size_t>(1), __ATOMIC_SEQ_CST);
304void IrqHandlerRegistry::finishThreadedActionMutation(ThreadedActionMutationCleanup& cleanup) {
307 completeThreadedActionMutation();
308 if (cleanup.thread) {
309 cleanup.thread->disarmAtomicStateCleanup(cleanup.cleanup);
311 cleanup.registry =
nullptr;
315void IrqHandlerRegistry::completeThreadedActionMutation() {
316 __atomic_add_fetch(&m_ThreadedActionMutationGeneration,
static_cast<size_t>(1), __ATOMIC_SEQ_CST);
317 const size_t previous = __atomic_fetch_sub(&m_ThreadedActionMutationWriters,
318 static_cast<size_t>(1), __ATOMIC_SEQ_CST);
320 __atomic_store_n(&m_ThreadedActionMutationWriters,
static_cast<size_t>(0), __ATOMIC_SEQ_CST);
321 FATAL_NOLOCK(
"IRQ action-mutation writer count underflowed.");
325void IrqHandlerRegistry::abandonThreadedActionMutation(
void* context) {
326 ThreadedActionMutationCleanup* cleanup =
327 reinterpret_cast<ThreadedActionMutationCleanup*
>(context);
328 if (!cleanup || !cleanup->registry) {
332 cleanup->registry->completeThreadedActionMutation();
333 cleanup->registry =
nullptr;
336bool IrqHandlerRegistry::canWaitForActionFinalization() {
340 canWait = canWait && !current->getHostedSignalDepth();
345bool IrqHandlerRegistry::acquireFinalizationGate(HandlerSlot& slot,
bool canWait) {
346 bool contentionReported =
false;
349 if (__atomic_compare_exchange_n(&slot.finalizationGate, &expected,
static_cast<size_t>(1),
350 false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) {
353#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
354 if (!contentionReported) {
355 HandlerHazardHook hook = __atomic_load_n(&m_HandlerHazardHook, __ATOMIC_ACQUIRE);
357 hook(__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE),
358 HandlerHazardStage::FinalizationContended);
360 contentionReported =
true;
370void IrqHandlerRegistry::releaseFinalizationGate(HandlerSlot& slot) {
371 __atomic_store_n(&slot.finalizationGate,
static_cast<size_t>(0), __ATOMIC_RELEASE);
374bool IrqHandlerRegistry::pinActionMutation(HandlerSlot& slot,
size_t publication,
375 DispatchCleanup& cleanup,
Thread* thread) {
377 thread->armAtomicStateCleanup(cleanup.cleanup, abandonDispatch, &cleanup);
379 if (!publishDispatch(slot, cleanup.owner, &cleanup, publication, 0,
false)) {
381 thread->disarmAtomicStateCleanup(cleanup.cleanup);
383 FATAL_NOLOCK(
"IRQ action-mutation hazard table exhausted.");
386 if (__atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST) != publication) {
387 unpinActionMutation(slot, publication, cleanup, thread);
393void IrqHandlerRegistry::unpinActionMutation(HandlerSlot& slot,
size_t publication,
394 DispatchCleanup& cleanup,
Thread* thread) {
395 unpublishDispatch(&cleanup, slot, publication,
true);
397 thread->disarmAtomicStateCleanup(cleanup.cleanup);
401bool IrqHandlerRegistry::closeSlotAdmission(HandlerSlot& slot,
size_t expectedPublication,
402 size_t& closedPublication) {
403 const size_t originalPublication = expectedPublication;
404 const uint8_t irq = irqOf(expectedPublication);
405 const size_t graceBucket = irq % GraceBucketCount;
406 const bool canWait = canWaitForActionFinalization();
407 if (!acquireFinalizationGate(slot, canWait)) {
408 if (modeOf(originalPublication) == SlotMode::Draining) {
409 size_t draining = originalPublication;
411 __atomic_compare_exchange_n(
412 &slot.publication, &draining,
413 makePublication(generationOf(originalPublication), irq, SlotMode::Enabled,
414 deliveryOf(originalPublication)),
415 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
421 size_t boundaryExpected = 0;
422 while (!__atomic_compare_exchange_n(&m_OccurrenceBoundaryLocks[graceBucket], &boundaryExpected,
423 static_cast<size_t>(1),
false, __ATOMIC_ACQUIRE,
426 releaseFinalizationGate(slot);
427 if (modeOf(originalPublication) == SlotMode::Draining) {
428 size_t draining = originalPublication;
430 __atomic_compare_exchange_n(
431 &slot.publication, &draining,
432 makePublication(generationOf(originalPublication), irq, SlotMode::Enabled,
433 deliveryOf(originalPublication)),
434 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
439 boundaryExpected = 0;
443 const size_t occurrenceEpoch =
444 __atomic_load_n(&m_OccurrenceEpochs[graceBucket], __ATOMIC_SEQ_CST);
445 size_t boundary = occurrenceEpoch + 1;
449 __atomic_store_n(&slot.retirementEpoch, boundary, __ATOMIC_SEQ_CST);
450 const size_t cancellingPublication =
451 makePublication(generationOf(expectedPublication), irq, SlotMode::Cancelling,
452 deliveryOf(expectedPublication));
454 if (!__atomic_compare_exchange_n(&slot.publication, &expectedPublication, cancellingPublication,
455 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
456 __atomic_store_n(&slot.retirementEpoch,
static_cast<size_t>(0), __ATOMIC_SEQ_CST);
458 __atomic_store_n(&m_OccurrenceBoundaryLocks[graceBucket],
static_cast<size_t>(0),
460 releaseFinalizationGate(slot);
467 __atomic_store_n(&m_OccurrenceEpochs[graceBucket], boundary, __ATOMIC_SEQ_CST);
469#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
470 HandlerHazardHook boundaryHook = __atomic_load_n(&m_HandlerHazardHook, __ATOMIC_ACQUIRE);
472 boundaryHook(__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE),
473 HandlerHazardStage::RetirementBoundaryPublished);
477 closedPublication = makePublication(generationOf(cancellingPublication), irq, SlotMode::Closed,
478 deliveryOf(cancellingPublication));
479 size_t expectedCancelling = cancellingPublication;
481 __atomic_compare_exchange_n(&slot.publication, &expectedCancelling, closedPublication,
false,
482 __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
484 __atomic_store_n(&m_OccurrenceBoundaryLocks[graceBucket],
static_cast<size_t>(0),
486 releaseFinalizationGate(slot);
488 FATAL_NOLOCK(
"IRQ handler closure state changed before publication.");
493bool IrqHandlerRegistry::occurrencePrecedesRetirement(
const HandlerSlot& slot,
494 AdmissionCutoff admissionCutoff)
const {
495 const size_t retirementEpoch = __atomic_load_n(&slot.retirementEpoch, __ATOMIC_SEQ_CST);
496 return retirementEpoch && !generationReached(admissionCutoff.occurrenceEpoch, retirementEpoch);
499void IrqHandlerRegistry::tryReclaimTombstones(uint8_t irq) {
500 const size_t graceBucket = irq % GraceBucketCount;
501 if (__atomic_load_n(&m_OccurrenceReaders[graceBucket][0], __ATOMIC_SEQ_CST) ||
502 __atomic_load_n(&m_OccurrenceReaders[graceBucket][1], __ATOMIC_SEQ_CST)) {
506 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
507 HandlerSlot& slot = m_Handlers[i];
508 size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
509 const uint8_t slotIrq = irqOf(publication);
510 if (modeOf(publication) != SlotMode::Tombstone || slotIrq % GraceBucketCount != graceBucket ||
511 __atomic_load_n(&slot.pendingThreadedGeneration, __ATOMIC_ACQUIRE) ||
512 __atomic_load_n(&slot.claimedThreadedGeneration, __ATOMIC_ACQUIRE) ||
513 hasQuiescedGeneration(slot)) {
517 const size_t reclaimingPublication = makePublication(
518 generationOf(publication), slotIrq, SlotMode::Retiring, deliveryOf(publication));
519 if (!__atomic_compare_exchange_n(&slot.publication, &publication, reclaimingPublication,
false,
520 __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
523 if (hasActiveDispatch(slot, publication)) {
524 size_t reclaiming = reclaimingPublication;
525 if (!__atomic_compare_exchange_n(&slot.publication, &reclaiming, publication,
false,
526 __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
527 FATAL_NOLOCK(
"IRQ tombstone changed while action mutation was pinned.");
535 __atomic_store_n(&slot.admissionEpoch,
static_cast<size_t>(0), __ATOMIC_RELEASE);
536 __atomic_store_n(&slot.retirementEpoch,
static_cast<size_t>(0), __ATOMIC_RELEASE);
537 __atomic_store_n(&slot.publication,
538 makePublication(generationOf(reclaimingPublication), InvalidIrq,
539 SlotMode::Empty, Delivery::Threaded),
544bool IrqHandlerRegistry::retireSlot(HandlerSlot& slot,
size_t expectedPublication,
547 const size_t retiringPublication =
548 makePublication(generationOf(expectedPublication), irqOf(expectedPublication),
549 SlotMode::Retiring, deliveryOf(expectedPublication));
550 if (!__atomic_compare_exchange_n(&slot.publication, &expectedPublication, retiringPublication,
551 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
556 if (hasActiveDispatch(slot, expectedPublication)) {
557 size_t retiring = retiringPublication;
558 if (!__atomic_compare_exchange_n(&slot.publication, &retiring, expectedPublication,
false,
559 __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
560 FATAL_NOLOCK(
"IRQ slot changed while action mutation was pinned.");
566 assert(__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) == expectedHandler);
567 const uint8_t irq = irqOf(retiringPublication);
568 if (deliveryOf(retiringPublication) == Delivery::Threaded) {
569 size_t* actions[] = {&slot.pendingThreadedGeneration, &slot.claimedThreadedGeneration};
570 for (
size_t action = 0; action < 2; ++action) {
571 size_t generation = __atomic_load_n(actions[action], __ATOMIC_ACQUIRE);
575 publishSlotQuiesced(slot, irq, generation, QuiescedLane::Retirement);
576 if (__atomic_compare_exchange_n(actions[action], &generation,
static_cast<size_t>(0),
false,
577 __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)) {
583 const size_t hardHandoffState =
584 __atomic_exchange_n(&slot.hardHandoffState,
static_cast<size_t>(0), __ATOMIC_ACQ_REL);
585 if (hardHandoffState & 1U) {
589 __atomic_store_n(&slot.handler,
nullptr, __ATOMIC_RELEASE);
590 __atomic_store_n(&slot.policy,
static_cast<size_t>(0), __ATOMIC_RELEASE);
591 __atomic_store_n(&slot.publication,
592 makePublication(generationOf(retiringPublication), irq, SlotMode::Tombstone,
593 deliveryOf(retiringPublication)),
596 tryReclaimTombstones(irq);
600bool IrqHandlerRegistry::retireSlotOrObserveClosed(HandlerSlot& slot,
size_t expectedPublication,
602 if (retireSlot(slot, expectedPublication, expectedHandler)) {
606 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
607 return modeOf(publication) == SlotMode::Empty || modeOf(publication) == SlotMode::Closed ||
608 modeOf(publication) == SlotMode::Retiring || modeOf(publication) == SlotMode::Tombstone ||
609 generationOf(publication) != generationOf(expectedPublication) ||
610 __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) != expectedHandler;
614 void* owner,
void* token,
615 size_t admittedPublication,
616 size_t controllerGeneration,
620 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
621 ActiveDispatch& dispatch = m_ActiveDispatches[i];
622 void* expectedToken =
nullptr;
623 if (__atomic_compare_exchange_n(&dispatch.token, &expectedToken, token,
false, __ATOMIC_SEQ_CST,
625 __atomic_add_fetch(&dispatch.generation,
static_cast<size_t>(1), __ATOMIC_ACQ_REL);
626 __atomic_store_n(&dispatch.owner, owner, __ATOMIC_RELAXED);
627 __atomic_store_n(&dispatch.admittedPublication, admittedPublication, __ATOMIC_RELAXED);
628 __atomic_store_n(&dispatch.controllerGeneration, controllerGeneration, __ATOMIC_RELAXED);
629 __atomic_store_n(&dispatch.callback,
630 callback ?
static_cast<size_t>(1) : static_cast<size_t>(0),
633#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
634 HandlerHazardHook hazardHook =
nullptr;
636 hazardHook = __atomic_load_n(&m_HandlerHazardHook, __ATOMIC_ACQUIRE);
638 hazardHook(__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE), HandlerHazardStage::Claimed);
646 __atomic_store_n(&dispatch.slot, &slot, __ATOMIC_SEQ_CST);
648#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
650 hazardHook = __atomic_load_n(&m_HandlerHazardHook, __ATOMIC_ACQUIRE);
652 hazardHook(__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE),
653 HandlerHazardStage::Committed);
664bool IrqHandlerRegistry::unpublishDispatch(
void* token, HandlerSlot& slot,
665 size_t admittedPublication,
bool required) {
668 bool committed =
false;
669 bool callback =
false;
670 size_t controllerGeneration = 0;
671 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
672 ActiveDispatch& dispatch = m_ActiveDispatches[i];
673 if (__atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE) != token) {
677 const size_t generation = __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE);
678 HandlerSlot* publishedSlot = __atomic_load_n(&dispatch.slot, __ATOMIC_SEQ_CST);
679 if (__atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE) != token ||
680 __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE) != generation) {
684 if (publishedSlot && publishedSlot != &slot) {
685 FATAL_NOLOCK(
"IRQ callback hazard changed slots during release.");
689 committed = publishedSlot == &slot;
690 callback = __atomic_load_n(&dispatch.callback, __ATOMIC_RELAXED) != 0;
691 controllerGeneration = __atomic_load_n(&dispatch.controllerGeneration, __ATOMIC_RELAXED);
692 __atomic_store_n(&dispatch.slot,
nullptr, __ATOMIC_SEQ_CST);
693 __atomic_store_n(&dispatch.admittedPublication,
static_cast<size_t>(0), __ATOMIC_RELAXED);
694 __atomic_store_n(&dispatch.controllerGeneration,
static_cast<size_t>(0), __ATOMIC_RELAXED);
695 __atomic_store_n(&dispatch.callback,
static_cast<size_t>(0), __ATOMIC_RELAXED);
696 __atomic_store_n(&dispatch.owner,
nullptr, __ATOMIC_RELAXED);
697 __atomic_store_n(&dispatch.token,
nullptr, __ATOMIC_RELEASE);
704 FATAL_NOLOCK(
"IRQ callback hazard was released more than once.");
709#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
711 HandlerHazardHook releaseHook =
nullptr;
712 if (committed && callback) {
713 releasedHandler = __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE);
714 releaseHook = __atomic_load_n(&m_HandlerHazardHook, __ATOMIC_ACQUIRE);
718 if (committed && callback && !required && controllerGeneration &&
719 deliveryOf(admittedPublication) == Delivery::Threaded) {
720 publishSlotQuiesced(slot, irqOf(admittedPublication), controllerGeneration,
721 QuiescedLane::Callback);
722 size_t claimed = controllerGeneration;
723 __atomic_compare_exchange_n(&slot.claimedThreadedGeneration, &claimed,
static_cast<size_t>(0),
724 false, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE);
727 if (committed && !hasActiveDispatch(slot, admittedPublication)) {
728 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
729 if (generationOf(publication) == generationOf(admittedPublication) &&
730 modeOf(publication) == SlotMode::Closed) {
731 IrqHandlerBase* handler = __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE);
733 retireSlot(slot, publication, handler);
738#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
740 releaseHook(releasedHandler, HandlerHazardStage::Released);
746void IrqHandlerRegistry::abandonDispatch(
void* context) {
747 DispatchCleanup* dispatch =
reinterpret_cast<DispatchCleanup*
>(context);
748 const bool callbackBoundaryEntered = dispatch->restoreInterruptState;
749 if (dispatch->restoreDeviceHardIrqDepth) {
751 dispatch->restoreDeviceHardIrqDepth =
false;
753 dispatch->registry->unpublishDispatch(dispatch, *dispatch->slot, dispatch->publication,
false);
754 restoreDispatchInterruptState(*dispatch);
756#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
757 if (dispatch->callback) {
758 DispatchAbandonHook hook =
759 __atomic_load_n(&dispatch->registry->m_DispatchAbandonHook, __ATOMIC_ACQUIRE);
761 hook(dispatch->owner, callbackBoundaryEntered);
767void IrqHandlerRegistry::restoreDispatchInterruptState(DispatchCleanup& dispatch) {
768 if (!dispatch.restoreInterruptState) {
772 const bool previousInterruptState = dispatch.previousInterruptState;
773 dispatch.restoreInterruptState =
false;
777bool IrqHandlerRegistry::hasActiveDispatch(HandlerSlot& target,
size_t admittedPublication)
const {
778 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
779 const ActiveDispatch& dispatch = m_ActiveDispatches[i];
780 void* token = __atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE);
785 const size_t generation = __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE);
786 HandlerSlot* slot = __atomic_load_n(&dispatch.slot, __ATOMIC_SEQ_CST);
787 const size_t dispatchPublication =
788 __atomic_load_n(&dispatch.admittedPublication, __ATOMIC_RELAXED);
789 if (slot == &target && generationOf(dispatchPublication) == generationOf(admittedPublication) &&
790 __atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE) == token &&
791 __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE) == generation) {
798bool IrqHandlerRegistry::findCurrentDispatch(
void* owner, HandlerSlot* target,
799 size_t targetPublication,
800 bool& callbackContext)
const {
801 callbackContext =
false;
802 bool foundTarget =
false;
803 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
804 const ActiveDispatch& dispatch = m_ActiveDispatches[i];
805 void* token = __atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE);
810 const size_t generation = __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE);
811 void* dispatchOwner = __atomic_load_n(&dispatch.owner, __ATOMIC_RELAXED);
812 HandlerSlot* slot = __atomic_load_n(&dispatch.slot, __ATOMIC_SEQ_CST);
813 const size_t dispatchPublication =
814 __atomic_load_n(&dispatch.admittedPublication, __ATOMIC_RELAXED);
815 if (__atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE) != token ||
816 __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE) != generation) {
820 if (dispatchOwner == owner && slot && __atomic_load_n(&dispatch.callback, __ATOMIC_RELAXED)) {
821 callbackContext =
true;
822 foundTarget |= slot == target && target &&
823 generationOf(dispatchPublication) == generationOf(targetPublication);
829void* IrqHandlerRegistry::currentDispatchOwner() {
831 Thread* thread = information.getCurrentThread();
832 return thread ?
static_cast<void*
>(thread) : static_cast<void*>(&information);
835bool IrqHandlerRegistry::registerHandler(uint8_t irq,
IrqHandlerBase* handler, Delivery delivery,
837 if (!handler || (delivery != Delivery::Threaded && delivery != Delivery::HardOnly)) {
842 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
843 HandlerSlot& slot = m_Handlers[i];
844 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
845 const SlotMode mode = modeOf(publication);
846 if (mode == SlotMode::Empty || mode == SlotMode::Cancelling || mode == SlotMode::Closed ||
847 mode == SlotMode::Retiring || mode == SlotMode::Tombstone || irqOf(publication) != irq) {
850 const Delivery existingDelivery = deliveryOf(publication);
851 const size_t existingPolicy = __atomic_load_n(&slot.policy, __ATOMIC_ACQUIRE);
852 if ((existingDelivery == delivery && existingPolicy != policy) ||
853 (existingDelivery != delivery && !mixedPoliciesCompatible(existingPolicy, policy))) {
857 if (__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) == handler) {
862 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
863 HandlerSlot& slot = m_Handlers[i];
864 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
865 if (modeOf(publication) == SlotMode::Empty &&
866 !__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) &&
867 !hasActiveDispatch(slot, publication)) {
868 const size_t generation = generationOf(publication) + 1;
869 if (!generation || generation > MaximumPublicationGeneration) {
873 size_t admissionEpoch = __atomic_load_n(&m_AdmissionEpoch, __ATOMIC_RELAXED) + 1;
874 if (!admissionEpoch) {
877 __atomic_store_n(&slot.pendingThreadedGeneration,
static_cast<size_t>(0), __ATOMIC_RELEASE);
878 __atomic_store_n(&slot.claimedThreadedGeneration,
static_cast<size_t>(0), __ATOMIC_RELEASE);
879 for (
size_t lane = 0; lane < QuiescedLaneCount; ++lane) {
880 __atomic_store_n(&slot.quiescedThreadedGenerations[lane],
static_cast<size_t>(0),
883 __atomic_store_n(&slot.retirementEpoch,
static_cast<size_t>(0), __ATOMIC_RELEASE);
884 __atomic_store_n(&slot.hardHandoffState, delivery == Delivery::HardOnly ? generation << 1 : 0,
886 __atomic_store_n(&slot.handler, handler, __ATOMIC_RELEASE);
888 __atomic_store_n(&slot.policy, policy, __ATOMIC_RELEASE);
889 __atomic_store_n(&slot.admissionEpoch, admissionEpoch, __ATOMIC_RELEASE);
890 __atomic_store_n(&slot.publication,
891 makePublication(generation, irq, SlotMode::Enabled, delivery),
896 __atomic_store_n(&m_AdmissionEpoch, admissionEpoch, __ATOMIC_RELEASE);
906 return registerHandler(irq, handler, Delivery::Threaded, 0);
911 return policy.validForThreaded() &&
912 registerHandler(irq, handler, Delivery::Threaded, encodePolicy(&policy));
916 return registerHandler(irq, handler, Delivery::HardOnly, 0);
921 return policy.validForHard() &&
922 registerHandler(irq, handler, Delivery::HardOnly, encodePolicy(&policy));
927 LineMode ignoredDelivery = LineMode::Empty;
932 uint8_t irq,
IrqHandlerBase* handler, LineMode& removedDelivery) {
933 removedDelivery = LineMode::Empty;
935 return UnregisterResult::NotFound;
938 void* owner = currentDispatchOwner();
942 canYield = canYield && !current->getHostedSignalDepth();
945 bool callbackContext =
false;
946 findCurrentDispatch(owner,
nullptr, 0, callbackContext);
948 if (!canYield || callbackContext) {
953 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
955 size_t publication = __atomic_load_n(&candidate.publication, __ATOMIC_SEQ_CST);
956 if (modeOf(publication) != SlotMode::Enabled || irqOf(publication) != irq ||
957 __atomic_load_n(&candidate.handler, __ATOMIC_ACQUIRE) != handler) {
961 removedDelivery = lineModeForDelivery(deliveryOf(publication));
962 bool currentTargetDispatch =
false;
963 if (findCurrentDispatch(owner, &candidate, publication, currentTargetDispatch)) {
964 size_t closedPublication = 0;
965 const bool deferred = closeSlotAdmission(candidate, publication, closedPublication);
967 return UnregisterResult::Deferred;
969 return UnregisterResult::Rejected;
972 const size_t drainingPublication = makePublication(
973 generationOf(publication), irq, SlotMode::Draining, deliveryOf(publication));
975 const bool draining =
976 __atomic_compare_exchange_n(&candidate.publication, &publication, drainingPublication,
977 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
980 return UnregisterResult::Rejected;
983 if (hasActiveDispatch(candidate, drainingPublication)) {
984 size_t expectedPublication = drainingPublication;
986 __atomic_compare_exchange_n(
987 &candidate.publication, &expectedPublication,
988 makePublication(generationOf(drainingPublication), irq, SlotMode::Enabled,
989 deliveryOf(drainingPublication)),
990 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
992 return UnregisterResult::Rejected;
995 size_t closedPublication = 0;
996 if (!closeSlotAdmission(candidate, drainingPublication, closedPublication)) {
997 return UnregisterResult::Rejected;
1000 return retireSlotOrObserveClosed(candidate, closedPublication, handler)
1001 ? UnregisterResult::Completed
1002 : UnregisterResult::Rejected;
1005 return UnregisterResult::NotFound;
1015 size_t publication = 0;
1016 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
1017 size_t candidatePublication = __atomic_load_n(&m_Handlers[i].publication, __ATOMIC_SEQ_CST);
1018 if (modeOf(candidatePublication) != SlotMode::Empty && irqOf(candidatePublication) == irq &&
1019 __atomic_load_n(&m_Handlers[i].handler, __ATOMIC_ACQUIRE) == handler) {
1020 slot = &m_Handlers[i];
1021 publication = candidatePublication;
1028 return UnregisterResult::NotFound;
1031 removedDelivery = lineModeForDelivery(deliveryOf(publication));
1032 const bool selfUnregister = findCurrentDispatch(owner, slot, publication, callbackContext);
1033 if (selfUnregister) {
1034 if (modeOf(publication) != SlotMode::Enabled) {
1036 return UnregisterResult::Rejected;
1039 size_t closedPublication = 0;
1040 const bool deferred = closeSlotAdmission(*slot, publication, closedPublication);
1042 return deferred ? UnregisterResult::Deferred : UnregisterResult::Rejected;
1045 if (modeOf(publication) != SlotMode::Enabled) {
1047 return UnregisterResult::Rejected;
1055 size_t closedPublication = 0;
1056 if (!closeSlotAdmission(*slot, publication, closedPublication)) {
1057 return UnregisterResult::Rejected;
1060 if (!hasActiveDispatch(*slot, closedPublication)) {
1062 const bool retired = retireSlotOrObserveClosed(*slot, closedPublication, handler);
1064 return retired ? UnregisterResult::Completed : UnregisterResult::Rejected;
1067 uintptr_t previousDebugAddress = 0;
1069 current->
setDebugState(Thread::CallbackDrain,
reinterpret_cast<uintptr_t
>(handler));
1070 while (hasActiveDispatch(*slot, closedPublication)) {
1075 current->
setDebugState(previousDebugState, previousDebugAddress);
1078 const bool retired = retireSlotOrObserveClosed(*slot, closedPublication, handler);
1080 return retired ? UnregisterResult::Completed : UnregisterResult::Rejected;
1085 size_t dispatchGeneration) {
1088 disposition = HardIrqDisposition::NotHandled;
1091 return dispatchHard(irq, state, disposition, onlyHandler, dispatchGeneration, admissionCutoff);
1096 const size_t graceBucket = irq % GraceBucketCount;
1097 if (!acquireOccurrenceReaderLeases(irq, 0, 1)) {
1100#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1101 observeOccurrenceCaptureForTest(irq, OccurrenceCaptureStage::BankZeroClaimed, 0);
1103 if (!acquireOccurrenceReaderLeases(irq, 1, 1)) {
1104 releaseOccurrenceReaderLeases(irq, 0, 1);
1107#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1108 observeOccurrenceCaptureForTest(irq, OccurrenceCaptureStage::BankOneClaimed, 0);
1116 const size_t occurrenceEpoch =
1117 __atomic_load_n(&m_OccurrenceEpochs[graceBucket], __ATOMIC_SEQ_CST);
1118#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1119 observeOccurrenceCaptureForTest(irq, OccurrenceCaptureStage::EpochSampled, occurrenceEpoch);
1121 const size_t readerBank = occurrenceEpoch & 1;
1122 releaseOccurrenceReaderLeases(irq, readerBank ^ 1, 1);
1123#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1124 observeOccurrenceCaptureForTest(irq, OccurrenceCaptureStage::UnusedBankReleased, occurrenceEpoch);
1126 const size_t admissionEpoch = __atomic_load_n(&m_AdmissionEpoch, __ATOMIC_ACQUIRE);
1127 cutoff = {admissionEpoch, occurrenceEpoch, (
static_cast<size_t>(irq) * 2) + readerBank + 1};
1133 const size_t graceBucket = irq % GraceBucketCount;
1134 if (!acquireOccurrenceReaderLeases(irq, 0, 2)) {
1137#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1138 observeOccurrenceCaptureForTest(irq, OccurrenceCaptureStage::BankZeroClaimed, 0);
1140 if (!acquireOccurrenceReaderLeases(irq, 1, 2)) {
1141 releaseOccurrenceReaderLeases(irq, 0, 2);
1144#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1145 observeOccurrenceCaptureForTest(irq, OccurrenceCaptureStage::BankOneClaimed, 0);
1148 const size_t occurrenceEpoch =
1149 __atomic_load_n(&m_OccurrenceEpochs[graceBucket], __ATOMIC_SEQ_CST);
1150#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1151 observeOccurrenceCaptureForTest(irq, OccurrenceCaptureStage::EpochSampled, occurrenceEpoch);
1153 const size_t readerBank = occurrenceEpoch & 1;
1154 releaseOccurrenceReaderLeases(irq, readerBank ^ 1, 2);
1155#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1156 observeOccurrenceCaptureForTest(irq, OccurrenceCaptureStage::UnusedBankReleased, occurrenceEpoch);
1158 const size_t admissionEpoch = __atomic_load_n(&m_AdmissionEpoch, __ATOMIC_ACQUIRE);
1160 (
static_cast<size_t>(irq) * 2) + readerBank + 1};
1161 cutoffs = {cutoff, cutoff};
1165bool IrqHandlerRegistry::acquireOccurrenceReaderLeases(uint8_t irq,
size_t readerBank,
1167 if (readerBank > 1 || !count) {
1168 FATAL_NOLOCK(
"Invalid IRQ occurrence reader acquisition.");
1172 const size_t graceBucket = irq % GraceBucketCount;
1173 const size_t previous =
1174 __atomic_fetch_add(&m_OccurrenceReaders[graceBucket][readerBank], count, __ATOMIC_SEQ_CST);
1175 if (previous > ~
static_cast<size_t>(0) - count) {
1176 __atomic_fetch_sub(&m_OccurrenceReaders[graceBucket][readerBank], count, __ATOMIC_SEQ_CST);
1177 FATAL_NOLOCK(
"IRQ occurrence reader count overflowed.");
1183void IrqHandlerRegistry::releaseOccurrenceReaderLeases(uint8_t irq,
size_t readerBank,
1185 if (readerBank > 1 || !count) {
1186 FATAL_NOLOCK(
"Invalid IRQ occurrence reader release.");
1190 const size_t graceBucket = irq % GraceBucketCount;
1191 const size_t previous =
1192 __atomic_fetch_sub(&m_OccurrenceReaders[graceBucket][readerBank], count, __ATOMIC_SEQ_CST);
1193 if (previous < count) {
1194 __atomic_fetch_add(&m_OccurrenceReaders[graceBucket][readerBank], count, __ATOMIC_SEQ_CST);
1195 FATAL_NOLOCK(
"IRQ occurrence reader count underflowed.");
1199 const size_t remaining = previous - count;
1201 tryReclaimTombstones(irq);
1206 if (!admissionCutoff.readerToken) {
1210 const size_t token = admissionCutoff.readerToken - 1;
1211 if (token >= IrqCount * 2) {
1212 FATAL_NOLOCK(
"Invalid IRQ occurrence reader token.");
1215 const uint8_t irq =
static_cast<uint8_t
>(token / 2);
1216 const size_t readerBank = token & 1;
1217 releaseOccurrenceReaderLeases(irq, readerBank, 1);
1220void IrqHandlerRegistry::beginAdmissionCutoffCleanup(AdmissionCutoffCleanup& cleanup) {
1224 cleanup.ownsCutoff = cleanup.cutoff.readerToken != 0;
1225 if (cleanup.thread && cleanup.ownsCutoff) {
1229 cleanup.thread->armAtomicStateCleanup(cleanup.cleanup, abandonAdmissionCutoff, &cleanup);
1234void IrqHandlerRegistry::finishAdmissionCutoffCleanup(AdmissionCutoffCleanup& cleanup) {
1237 if (cleanup.ownsCutoff) {
1240 cleanup.ownsCutoff =
false;
1243 if (cleanup.thread && cleanup.cleanup.armed) {
1244 cleanup.thread->disarmAtomicStateCleanup(cleanup.cleanup);
1246 cleanup.registry =
nullptr;
1250void IrqHandlerRegistry::abandonAdmissionCutoff(
void* context) {
1251 AdmissionCutoffCleanup* cleanup =
reinterpret_cast<AdmissionCutoffCleanup*
>(context);
1252 if (!cleanup || !cleanup->registry || !cleanup->ownsCutoff) {
1256 cleanup->ownsCutoff =
false;
1257 cleanup->registry->releaseAdmissionCutoff(cleanup->cutoff);
1258 cleanup->registry =
nullptr;
1263 size_t dispatchGeneration, AdmissionCutoff admissionCutoff) {
1265 AdmissionCutoffCleanup cutoffCleanup(
this, admissionCutoff);
1266 beginAdmissionCutoffCleanup(cutoffCleanup);
1267 bool admitted =
false;
1268 disposition = HardIrqDisposition::NotHandled;
1269 const size_t cutoffEpoch = admissionCutoff.epoch;
1270 const size_t expectedReaderToken =
1271 (
static_cast<size_t>(irq) * 2) + (admissionCutoff.occurrenceEpoch & 1) + 1;
1272 if (admissionCutoff.readerToken != expectedReaderToken) {
1273 finishAdmissionCutoffCleanup(cutoffCleanup);
1277 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
1278 HandlerSlot& slot = m_Handlers[i];
1279 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
1280 const SlotMode mode = modeOf(publication);
1281 if (irqOf(publication) != irq || deliveryOf(publication) != Delivery::HardOnly) {
1284 const size_t admissionEpoch = __atomic_load_n(&slot.admissionEpoch, __ATOMIC_ACQUIRE);
1285 if (!admissionEpoch ||
1286 (admissionEpoch != cutoffEpoch && generationReached(admissionEpoch, cutoffEpoch))) {
1290 if (mode == SlotMode::Cancelling || mode == SlotMode::Closed || mode == SlotMode::Retiring ||
1291 mode == SlotMode::Tombstone) {
1292 if (occurrencePrecedesRetirement(slot, admissionCutoff)) {
1294 if (disposition == HardIrqDisposition::NotHandled) {
1295 disposition = HardIrqDisposition::Handled;
1300 if (mode != SlotMode::Enabled && mode != SlotMode::Draining) {
1304 IrqHandlerBase* handler = __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE);
1305 if (!handler || (onlyHandler && handler !=
static_cast<IrqHandlerBase*
>(onlyHandler))) {
1309#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1310 HandlerPrePinHook prePinHook = __atomic_load_n(&m_HandlerPrePinHook, __ATOMIC_ACQUIRE);
1312 prePinHook(handler);
1316 void* owner = currentDispatchOwner();
1318 DispatchCleanup dispatchCleanup(
this, &slot, owner, publication);
1323 thread->armAtomicStateCleanup(dispatchCleanup.cleanup, abandonDispatch, &dispatchCleanup);
1326#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1327 HandlerHazardHook hazardHook = __atomic_load_n(&m_HandlerHazardHook, __ATOMIC_ACQUIRE);
1329 hazardHook(handler, HandlerHazardStage::BeforeClaim);
1333 if (!publishDispatch(slot, owner, &dispatchCleanup, publication, dispatchGeneration)) {
1335 thread->disarmAtomicStateCleanup(dispatchCleanup.cleanup);
1337 finishAdmissionCutoffCleanup(cutoffCleanup);
1338 FATAL_NOLOCK(
"IRQ callback hazard table exhausted.");
1342 size_t currentPublication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
1343 SlotMode currentMode = modeOf(currentPublication);
1344 const bool sameLifetime =
1345 generationOf(currentPublication) == generationOf(publication) &&
1346 irqOf(currentPublication) == irq && deliveryOf(currentPublication) == Delivery::HardOnly &&
1347 __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) == handler &&
1348 __atomic_load_n(&slot.admissionEpoch, __ATOMIC_ACQUIRE) == admissionEpoch;
1349 if (sameLifetime && currentMode == SlotMode::Draining) {
1354 size_t expectedPublication = currentPublication;
1355 const size_t enabledPublication = makePublication(generationOf(currentPublication), irq,
1356 SlotMode::Enabled, Delivery::HardOnly);
1358 __atomic_compare_exchange_n(&slot.publication, &expectedPublication, enabledPublication,
1359 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
1361 currentPublication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
1362 currentMode = modeOf(currentPublication);
1365 if (generationOf(currentPublication) != generationOf(publication) ||
1366 irqOf(currentPublication) != irq || deliveryOf(currentPublication) != Delivery::HardOnly ||
1367 currentMode != SlotMode::Enabled ||
1368 __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) != handler ||
1369 __atomic_load_n(&slot.admissionEpoch, __ATOMIC_ACQUIRE) != admissionEpoch) {
1370 unpublishDispatch(&dispatchCleanup, slot, publication,
true);
1372 thread->disarmAtomicStateCleanup(dispatchCleanup.cleanup);
1379#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1380 HandlerPinHook hook = __atomic_load_n(&m_HandlerPinHook, __ATOMIC_ACQUIRE);
1391 dispatchCleanup.restoreInterruptState =
true;
1394 dispatchCleanup.restoreDeviceHardIrqDepth);
1398 size_t expected = generationOf(publication) << 1;
1399 if (__atomic_compare_exchange_n(&slot.hardHandoffState, &expected, expected | 1U,
false,
1400 __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)) {
1406 (callbackDisposition == HardIrqDisposition::Handled &&
1407 disposition == HardIrqDisposition::NotHandled)) {
1408 disposition = callbackDisposition;
1411 unpublishDispatch(&dispatchCleanup, slot, publication,
true);
1413 thread->disarmAtomicStateCleanup(dispatchCleanup.cleanup);
1415 restoreDispatchInterruptState(dispatchCleanup);
1418 finishAdmissionCutoffCleanup(cutoffCleanup);
1420 disposition = HardIrqDisposition::Handled;
1426 const size_t epochBucket = irq % GraceBucketCount;
1427 for (
size_t attempt = 0; attempt < 2; ++attempt) {
1428 const size_t startEpoch = __atomic_load_n(&
m_HardHandoffEpochs[epochBucket], __ATOMIC_ACQUIRE);
1429 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
1431 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
1432 const SlotMode mode = modeOf(publication);
1433 if ((mode != SlotMode::Enabled && mode != SlotMode::Draining) || irqOf(publication) != irq ||
1434 deliveryOf(publication) != Delivery::HardOnly) {
1438 const size_t generation = generationOf(publication);
1439 if (__atomic_load_n(&slot.
hardHandoffState, __ATOMIC_ACQUIRE) != ((generation << 1) | 1U)) {
1443 const size_t currentPublication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
1444 const SlotMode currentMode = modeOf(currentPublication);
1445 if ((currentMode == SlotMode::Enabled || currentMode == SlotMode::Draining) &&
1446 irqOf(currentPublication) == irq &&
1447 deliveryOf(currentPublication) == Delivery::HardOnly &&
1448 generationOf(currentPublication) == generation) {
1472 AdmissionCutoff admissionCutoff) {
1473 AdmissionCutoffCleanup cutoffCleanup(
this, admissionCutoff);
1474 beginAdmissionCutoffCleanup(cutoffCleanup);
1475 const size_t expectedReaderToken =
1476 (
static_cast<size_t>(irq) * 2) + (admissionCutoff.occurrenceEpoch & 1) + 1;
1477 if (admissionCutoff.readerToken != expectedReaderToken) {
1478 finishAdmissionCutoffCleanup(cutoffCleanup);
1481 if (!threadedGenerationValid(irq, dispatchGeneration)) {
1482 finishAdmissionCutoffCleanup(cutoffCleanup);
1490 size_t admissionEpoch;
1492 Candidate candidates[MaxHandlerSlots];
1493 size_t candidateCount = 0;
1494 const size_t cutoffEpoch = admissionCutoff.epoch;
1495 bool admitted =
false;
1497 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
1498 HandlerSlot& slot = m_Handlers[i];
1499 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
1500 const SlotMode mode = modeOf(publication);
1501 if (irqOf(publication) != irq || deliveryOf(publication) != Delivery::Threaded) {
1505 const size_t admissionEpoch = __atomic_load_n(&slot.admissionEpoch, __ATOMIC_ACQUIRE);
1506 if (!admissionEpoch ||
1507 (admissionEpoch != cutoffEpoch && generationReached(admissionEpoch, cutoffEpoch))) {
1511 if (mode == SlotMode::Cancelling || mode == SlotMode::Closed || mode == SlotMode::Retiring ||
1512 mode == SlotMode::Tombstone) {
1513 if (occurrencePrecedesRetirement(slot, admissionCutoff)) {
1514 publishSlotQuiesced(slot, irq, dispatchGeneration, QuiescedLane::Controller);
1519 if (mode != SlotMode::Enabled && mode != SlotMode::Draining) {
1523 IrqHandlerBase* handler = __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE);
1525 candidates[candidateCount++] = {&slot, publication, handler, admissionEpoch};
1529 for (
size_t i = 0; i < candidateCount; ++i) {
1530 HandlerSlot& slot = *candidates[i].slot;
1531 const size_t publication = candidates[i].publication;
1533 const size_t admissionEpoch = candidates[i].admissionEpoch;
1534 void* owner = currentDispatchOwner();
1536 DispatchCleanup dispatchCleanup(
this, &slot, owner, publication,
false);
1540 thread->armAtomicStateCleanup(dispatchCleanup.cleanup, abandonDispatch, &dispatchCleanup);
1543#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1544 HandlerHazardHook hazardHook = __atomic_load_n(&m_HandlerHazardHook, __ATOMIC_ACQUIRE);
1546 hazardHook(handler, HandlerHazardStage::BeforeClaim);
1550 if (!publishDispatch(slot, owner, &dispatchCleanup, publication, dispatchGeneration,
false)) {
1552 thread->disarmAtomicStateCleanup(dispatchCleanup.cleanup);
1554 finishAdmissionCutoffCleanup(cutoffCleanup);
1555 FATAL_NOLOCK(
"IRQ callback hazard table exhausted.");
1559 size_t currentPublication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
1560 SlotMode currentMode = modeOf(currentPublication);
1561 const bool sameLifetime =
1562 generationOf(currentPublication) == generationOf(publication) &&
1563 irqOf(currentPublication) == irq && deliveryOf(currentPublication) == Delivery::Threaded &&
1564 __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) == handler &&
1565 __atomic_load_n(&slot.admissionEpoch, __ATOMIC_ACQUIRE) == admissionEpoch;
1566 if (sameLifetime && (currentMode == SlotMode::Enabled || currentMode == SlotMode::Draining) &&
1567 threadedGenerationValid(irq, dispatchGeneration)) {
1568 if (currentMode == SlotMode::Draining) {
1569 size_t expectedPublication = currentPublication;
1570 const size_t enabledPublication = makePublication(generationOf(currentPublication), irq,
1571 SlotMode::Enabled, Delivery::Threaded);
1573 __atomic_compare_exchange_n(&slot.publication, &expectedPublication, enabledPublication,
1574 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
1576 currentPublication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
1577 currentMode = modeOf(currentPublication);
1580 if (currentMode != SlotMode::Enabled) {
1581 publishSlotQuiesced(slot, irq, dispatchGeneration, QuiescedLane::Controller);
1584 if (currentMode == SlotMode::Enabled) {
1585#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1586 HandlerHazardHook publicationHook = __atomic_load_n(&m_HandlerHazardHook, __ATOMIC_ACQUIRE);
1587 if (publicationHook) {
1588 publicationHook(handler, HandlerHazardStage::BeforePendingExchange);
1592 const size_t pending = __atomic_load_n(&slot.pendingThreadedGeneration, __ATOMIC_ACQUIRE);
1593 if (!pending || !generationReached(pending, dispatchGeneration)) {
1599 __atomic_exchange_n(&slot.pendingThreadedGeneration, dispatchGeneration,
1603#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1604 publicationHook = __atomic_load_n(&m_HandlerHazardHook, __ATOMIC_ACQUIRE);
1605 if (publicationHook) {
1606 publicationHook(handler, HandlerHazardStage::PendingExchanged);
1610 if (!threadedGenerationValid(irq, dispatchGeneration)) {
1611 size_t stale = dispatchGeneration;
1612 __atomic_compare_exchange_n(&slot.pendingThreadedGeneration, &stale,
1613 static_cast<size_t>(0),
false, __ATOMIC_ACQ_REL,
1618 const size_t finalPublication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
1619 const SlotMode finalMode = modeOf(finalPublication);
1620 if (generationOf(finalPublication) != generationOf(publication) ||
1621 irqOf(finalPublication) != irq ||
1622 (finalMode != SlotMode::Enabled && finalMode != SlotMode::Draining) ||
1623 __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) != handler ||
1624 __atomic_load_n(&slot.admissionEpoch, __ATOMIC_ACQUIRE) != admissionEpoch) {
1625 publishSlotQuiesced(slot, irq, dispatchGeneration, QuiescedLane::Controller);
1631 if (occurrencePrecedesRetirement(slot, admissionCutoff)) {
1632 publishSlotQuiesced(slot, irq, dispatchGeneration, QuiescedLane::Controller);
1637 unpublishDispatch(&dispatchCleanup, slot, publication,
true);
1639 thread->disarmAtomicStateCleanup(dispatchCleanup.cleanup);
1643 finishAdmissionCutoffCleanup(cutoffCleanup);
1649 result = {
false,
false};
1650 bool admitted =
false;
1651 if (!threadedGenerationValid(irq, dispatchGeneration)) {
1659 if (dispatchThread->getHostedSignalDepth()) {
1669 if (cutoffCaptured) {
1670 beginAdmissionCutoffCleanup(cutoffCleanup);
1673 if (!cutoffCaptured) {
1683 if (__atomic_load_n(&m_ThreadedActionMutationWriters, __ATOMIC_SEQ_CST)) {
1687 const size_t actionMutationGeneration =
1688 __atomic_load_n(&m_ThreadedActionMutationGeneration, __ATOMIC_SEQ_CST);
1689 if (__atomic_load_n(&m_ThreadedActionMutationWriters, __ATOMIC_SEQ_CST)) {
1694 Candidate candidates[MaxHandlerSlots];
1695 size_t candidateCount = 0;
1700 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
1702 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
1703 if (irqOf(publication) != irq || deliveryOf(publication) != Delivery::Threaded) {
1707 for (
size_t lane = 0; lane < QuiescedLaneCount; ++lane) {
1708 size_t* publicationLane = &slot.quiescedThreadedGenerations[lane];
1709 size_t quiesced = __atomic_load_n(publicationLane, __ATOMIC_ACQUIRE);
1710 if (quiesced && !threadedGenerationValid(irq, quiesced)) {
1711 __atomic_compare_exchange_n(publicationLane, &quiesced,
static_cast<size_t>(0),
false,
1712 __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE);
1715 if (quiesced && generationReached(dispatchGeneration, quiesced) &&
1716 __atomic_compare_exchange_n(publicationLane, &quiesced,
static_cast<size_t>(0),
false,
1717 __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)) {
1719 result.allowRearm =
true;
1723#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1724 HandlerHazardHook quiescedHook = __atomic_load_n(&m_HandlerHazardHook, __ATOMIC_ACQUIRE);
1726 quiescedHook(__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE),
1727 HandlerHazardStage::QuiescedObserved);
1731 if (modeOf(publication) != SlotMode::Enabled) {
1735 const size_t pending = __atomic_load_n(&slot.pendingThreadedGeneration, __ATOMIC_ACQUIRE);
1736 if (!pending || !generationReached(dispatchGeneration, pending)) {
1739 if (!threadedGenerationValid(irq, pending)) {
1740 size_t stale = pending;
1741 __atomic_compare_exchange_n(&slot.pendingThreadedGeneration, &stale,
static_cast<size_t>(0),
1742 false, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE);
1746 IrqHandlerBase* handler = __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE);
1747 if (!handler || (onlyHandler && handler !=
static_cast<IrqHandlerBase*
>(onlyHandler))) {
1751 candidates[candidateCount++] = {&slot, publication, handler};
1754 for (
size_t i = 0; i < candidateCount; ++i) {
1756 const size_t publication = candidates[i].publication;
1759#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1760 HandlerPrePinHook prePinHook = __atomic_load_n(&m_HandlerPrePinHook, __ATOMIC_ACQUIRE);
1762 prePinHook(handler);
1766 void* owner = currentDispatchOwner();
1773 thread->armAtomicStateCleanup(dispatchCleanup.cleanup, abandonDispatch, &dispatchCleanup);
1776#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1777 HandlerHazardHook hazardHook = __atomic_load_n(&m_HandlerHazardHook, __ATOMIC_ACQUIRE);
1779 hazardHook(handler, HandlerHazardStage::BeforeClaim);
1783 if (!publishDispatch(slot, owner, &dispatchCleanup, publication, dispatchGeneration)) {
1785 thread->disarmAtomicStateCleanup(dispatchCleanup.cleanup);
1787 finishAdmissionCutoffCleanup(cutoffCleanup);
1788 FATAL_NOLOCK(
"IRQ callback hazard table exhausted.");
1792 if (__atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST) != publication ||
1793 __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) != handler) {
1794 unpublishDispatch(&dispatchCleanup, slot, publication,
true);
1796 thread->disarmAtomicStateCleanup(dispatchCleanup.cleanup);
1801#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1802 HandlerPinHook hook = __atomic_load_n(&m_HandlerPinHook, __ATOMIC_ACQUIRE);
1808 size_t pending = __atomic_load_n(&slot.pendingThreadedGeneration, __ATOMIC_ACQUIRE);
1809 bool claimed =
false;
1810 size_t claimedGeneration = 0;
1811 while (pending && generationReached(dispatchGeneration, pending)) {
1812 size_t emptyClaim = 0;
1813 if (!__atomic_compare_exchange_n(&slot.claimedThreadedGeneration, &emptyClaim, pending,
1814 false, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)) {
1818 claimedGeneration = pending;
1819 size_t exactPending = pending;
1820 if (__atomic_compare_exchange_n(&slot.pendingThreadedGeneration, &exactPending,
1821 static_cast<size_t>(0),
false, __ATOMIC_ACQ_REL,
1822 __ATOMIC_ACQUIRE)) {
1826 size_t exactClaim = claimedGeneration;
1827 __atomic_compare_exchange_n(&slot.claimedThreadedGeneration, &exactClaim,
1828 static_cast<size_t>(0),
false, __ATOMIC_ACQ_REL,
1830 pending = exactPending;
1833 unpublishDispatch(&dispatchCleanup, slot, publication,
true);
1835 thread->disarmAtomicStateCleanup(dispatchCleanup.cleanup);
1843 if (disposition == IrqDisposition::Handled) {
1844 result.handled =
true;
1845 result.allowRearm =
true;
1847 result.allowRearm =
true;
1849 unpublishDispatch(&dispatchCleanup, slot, publication,
true);
1851 thread->disarmAtomicStateCleanup(dispatchCleanup.cleanup);
1854 if (!acquireFinalizationGate(slot,
true)) {
1855 finishAdmissionCutoffCleanup(cutoffCleanup);
1856 FATAL(
"IRQ action finalization gate could not be acquired.");
1860 size_t finalPublication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
1861#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1862 HandlerHazardHook finalizationHook = __atomic_load_n(&m_HandlerHazardHook, __ATOMIC_ACQUIRE);
1863 if (finalizationHook) {
1864 finalizationHook(handler, HandlerHazardStage::BeforeClaimFinalization);
1867 if (generationOf(finalPublication) == generationOf(publication) &&
1868 irqOf(finalPublication) == irq && deliveryOf(finalPublication) == Delivery::Threaded &&
1869 modeOf(finalPublication) == SlotMode::Draining &&
1870 __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) == handler) {
1871 size_t expectedPublication = finalPublication;
1873 __atomic_compare_exchange_n(&slot.publication, &expectedPublication, publication,
false,
1874 __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
1876 finalPublication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
1878 if (finalPublication == publication) {
1879 size_t exactClaim = claimedGeneration;
1880 __atomic_compare_exchange_n(&slot.claimedThreadedGeneration, &exactClaim,
1881 static_cast<size_t>(0),
false, __ATOMIC_ACQ_REL,
1884 publishSlotQuiesced(slot, irq, claimedGeneration, QuiescedLane::Callback);
1885 size_t exactClaim = claimedGeneration;
1886 __atomic_compare_exchange_n(&slot.claimedThreadedGeneration, &exactClaim,
1887 static_cast<size_t>(0),
false, __ATOMIC_ACQ_REL,
1890 releaseFinalizationGate(slot);
1893 bool admissionResolving =
false;
1894 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
1896 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
1897 if (irqOf(publication) != irq || deliveryOf(publication) != Delivery::Threaded) {
1901 size_t pending = __atomic_load_n(&slot.pendingThreadedGeneration, __ATOMIC_ACQUIRE);
1902 size_t claimed = __atomic_load_n(&slot.claimedThreadedGeneration, __ATOMIC_ACQUIRE);
1903 const bool pendingReached = pending && generationReached(dispatchGeneration, pending);
1904 const bool claimedReached = claimed && generationReached(dispatchGeneration, claimed);
1905 if (!pendingReached && !claimedReached) {
1908 if (pendingReached && !threadedGenerationValid(irq, pending)) {
1909 size_t stale = pending;
1910 __atomic_compare_exchange_n(&slot.pendingThreadedGeneration, &stale,
static_cast<size_t>(0),
1911 false, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE);
1915 const SlotMode mode = modeOf(publication);
1916 if (mode == SlotMode::Draining || mode == SlotMode::Cancelling || mode == SlotMode::Closed ||
1917 mode == SlotMode::Retiring) {
1922 admissionResolving =
true;
1927 const size_t finalActionMutationWriters =
1928 __atomic_load_n(&m_ThreadedActionMutationWriters, __ATOMIC_SEQ_CST);
1929 const size_t finalActionMutationGeneration =
1930 __atomic_load_n(&m_ThreadedActionMutationGeneration, __ATOMIC_SEQ_CST);
1931 if (finalActionMutationWriters || finalActionMutationGeneration != actionMutationGeneration) {
1935 if (!admissionResolving && !candidateCount) {
1938 if (admissionResolving) {
1943 finishAdmissionCutoffCleanup(cutoffCleanup);
1947size_t IrqHandlerRegistry::handlerCount(uint8_t irq) {
1949 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
1950 const size_t publication = __atomic_load_n(&m_Handlers[i].publication, __ATOMIC_SEQ_CST);
1951 const SlotMode mode = modeOf(publication);
1952 if ((mode == SlotMode::Enabled || mode == SlotMode::Draining) && irqOf(publication) == irq) {
1960 bool threaded =
false;
1962 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
1963 const size_t publication = __atomic_load_n(&m_Handlers[i].publication, __ATOMIC_SEQ_CST);
1964 const SlotMode mode = modeOf(publication);
1965 if ((mode != SlotMode::Enabled && mode != SlotMode::Draining) || irqOf(publication) != irq) {
1969 if (deliveryOf(publication) == Delivery::Threaded) {
1974 if (threaded && hard) {
1975 return LineMode::Mixed;
1978 return threaded ? LineMode::Threaded : hard ? LineMode::HardOnly : LineMode::Empty;
1983 for (
size_t attempt = 0; attempt < LineSnapshotAttempts; ++attempt) {
1984 if (__atomic_load_n(&m_MutationWriters, __ATOMIC_SEQ_CST)) {
1987 const size_t generation = __atomic_load_n(&m_MutationGeneration, __ATOMIC_SEQ_CST);
1988 if (__atomic_load_n(&m_MutationWriters, __ATOMIC_SEQ_CST)) {
1993 size_t threadedPolicy = 0;
1994 size_t hardPolicy = 0;
1995 bool observedThreaded =
false;
1996 bool observedHard =
false;
1997 bool consistent =
true;
1998 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
2000 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
2001 if (modeOf(publication) != SlotMode::Enabled || irqOf(publication) != irq) {
2005 const size_t policy = __atomic_load_n(&slot.policy, __ATOMIC_ACQUIRE);
2006 if (__atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST) != publication) {
2011 if (deliveryOf(publication) == Delivery::Threaded) {
2012 if (observedThreaded && threadedPolicy != policy) {
2016 observedThreaded =
true;
2017 threadedPolicy = policy;
2019 if (observedHard && hardPolicy != policy) {
2023 observedHard =
true;
2024 hardPolicy = policy;
2026 ++observed.handlerCount;
2029 size_t observedPolicy = 0;
2030 if (consistent && observedThreaded && observedHard) {
2031 consistent = mixedPoliciesCompatible(hardPolicy, threadedPolicy);
2032 observed.mode = LineMode::Mixed;
2033 observedPolicy = effectiveMixedPolicy(hardPolicy, threadedPolicy);
2034 }
else if (consistent && observedThreaded) {
2035 observed.mode = LineMode::Threaded;
2036 observedPolicy = threadedPolicy;
2037 }
else if (consistent && observedHard) {
2038 observed.mode = LineMode::HardOnly;
2039 observedPolicy = hardPolicy;
2044 const size_t finalWriters = __atomic_load_n(&m_MutationWriters, __ATOMIC_SEQ_CST);
2045 const size_t finalGeneration = __atomic_load_n(&m_MutationGeneration, __ATOMIC_SEQ_CST);
2046 if (finalWriters || generation != finalGeneration) {
2053 observed.mutationGeneration = finalGeneration;
2054 decodePolicy(observedPolicy, observed);
2055 configuration = observed;
2064 exactGeneration = 0;
2065 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
2067 void* token = __atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE);
2072 const size_t generation = __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE);
2073 HandlerSlot* slot = __atomic_load_n(&dispatch.slot, __ATOMIC_SEQ_CST);
2074 const size_t publication = __atomic_load_n(&dispatch.admittedPublication, __ATOMIC_RELAXED);
2075 const size_t controllerGeneration =
2076 __atomic_load_n(&dispatch.controllerGeneration, __ATOMIC_RELAXED);
2077 if (slot && irqOf(publication) == irq && deliveryOf(publication) == Delivery::HardOnly &&
2078 __atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE) == token &&
2079 __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE) == generation &&
2080 __atomic_load_n(&dispatch.slot, __ATOMIC_SEQ_CST) == slot) {
2082 exactGeneration = count == 1 ? controllerGeneration : 0;
2089 uintptr_t& exactHandlerIdentity)
const {
2091 exactHandlerIdentity = 0;
2092 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
2094 void* token = __atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE);
2099 const size_t generation = __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE);
2100 HandlerSlot* slot = __atomic_load_n(&dispatch.slot, __ATOMIC_SEQ_CST);
2101 const size_t publication = __atomic_load_n(&dispatch.admittedPublication, __ATOMIC_RELAXED);
2102 IrqHandlerBase* handler = slot ? __atomic_load_n(&slot->handler, __ATOMIC_ACQUIRE) :
nullptr;
2103 if (slot && handler && irqOf(publication) == irq &&
2104 deliveryOf(publication) == Delivery::Threaded &&
2105 __atomic_load_n(&dispatch.callback, __ATOMIC_RELAXED) &&
2106 __atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE) == token &&
2107 __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE) == generation &&
2108 __atomic_load_n(&dispatch.slot, __ATOMIC_SEQ_CST) == slot) {
2110 exactHandlerIdentity = count == 1 ?
reinterpret_cast<uintptr_t
>(handler) : 0;
2116#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
2117void IrqHandlerRegistry::setHandlerPinHook(HandlerPinHook hook) {
2118 __atomic_store_n(&m_HandlerPinHook, hook, __ATOMIC_RELEASE);
2121void IrqHandlerRegistry::setHandlerPrePinHook(HandlerPrePinHook hook) {
2122 __atomic_store_n(&m_HandlerPrePinHook, hook, __ATOMIC_RELEASE);
2125void IrqHandlerRegistry::setHandlerHazardHook(HandlerHazardHook hook) {
2126 __atomic_store_n(&m_HandlerHazardHook, hook, __ATOMIC_RELEASE);
2129void IrqHandlerRegistry::setDispatchAbandonHook(DispatchAbandonHook hook) {
2130 __atomic_store_n(&m_DispatchAbandonHook, hook, __ATOMIC_RELEASE);
2133void IrqHandlerRegistry::setOccurrenceCaptureHookForTest(OccurrenceCaptureHook hook) {
2134 __atomic_store_n(&m_OccurrenceCaptureHook, hook, __ATOMIC_RELEASE);
2137void IrqHandlerRegistry::observeOccurrenceCaptureForTest(uint8_t irq, OccurrenceCaptureStage stage,
2138 size_t occurrenceEpoch) {
2139 OccurrenceCaptureHook hook = __atomic_load_n(&m_OccurrenceCaptureHook, __ATOMIC_ACQUIRE);
2141 hook(
this, irq, stage, occurrenceEpoch);
2145void IrqHandlerRegistry::withMutationLockForTest(MutationLockHook hook) {
2153void IrqHandlerRegistry::withMutationEpochForTest(MutationLockHook hook) {
2161size_t IrqHandlerRegistry::activeDispatchCountForTest(
IrqHandlerBase* handler) {
2163 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
2164 ActiveDispatch& dispatch = m_ActiveDispatches[i];
2165 void* token = __atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE);
2169 const size_t generation = __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE);
2170 HandlerSlot* slot = __atomic_load_n(&dispatch.slot, __ATOMIC_SEQ_CST);
2172 slot ? __atomic_load_n(&slot->handler, __ATOMIC_ACQUIRE) : nullptr;
2173 if (activeHandler == handler && __atomic_load_n(&dispatch.callback, __ATOMIC_RELAXED) &&
2174 __atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE) == token &&
2175 __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE) == generation) {
2182size_t IrqHandlerRegistry::claimedDispatchCountForOwnerForTest(
void* owner) {
2188 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
2189 ActiveDispatch& dispatch = m_ActiveDispatches[i];
2190 void* token = __atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE);
2195 const size_t generation = __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE);
2196 void* dispatchOwner = __atomic_load_n(&dispatch.owner, __ATOMIC_RELAXED);
2197 if (dispatchOwner == owner && __atomic_load_n(&dispatch.callback, __ATOMIC_RELAXED) &&
2198 __atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE) == token &&
2199 __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE) == generation) {
2206bool IrqHandlerRegistry::containsHandlerForTest(uint8_t irq,
IrqHandlerBase* handler) {
2207 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
2208 HandlerSlot& slot = m_Handlers[i];
2209 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
2210 if (modeOf(publication) != SlotMode::Empty && irqOf(publication) == irq &&
2211 __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) == handler &&
2212 __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST) == publication) {
2219size_t IrqHandlerRegistry::tombstoneCountForTest(uint8_t irq)
const {
2221 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
2222 const size_t publication = __atomic_load_n(&m_Handlers[i].publication, __ATOMIC_SEQ_CST);
2223 if (modeOf(publication) == SlotMode::Tombstone && irqOf(publication) == irq) {
2230size_t IrqHandlerRegistry::threadedActionMutationWriterCountForTest()
const {
2231 return __atomic_load_n(&m_ThreadedActionMutationWriters, __ATOMIC_SEQ_CST);
2234bool IrqHandlerRegistry::setThreadedActionLanesForTest(
IrqHandlerBase* handler,
2235 size_t pendingGeneration,
2236 size_t claimedGeneration,
2237 size_t quiescedGeneration) {
2238 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
2239 HandlerSlot& slot = m_Handlers[i];
2240 if (__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) != handler) {
2244 __atomic_store_n(&slot.pendingThreadedGeneration, pendingGeneration, __ATOMIC_RELEASE);
2245 __atomic_store_n(&slot.claimedThreadedGeneration, claimedGeneration, __ATOMIC_RELEASE);
2246 for (
size_t lane = 0; lane < QuiescedLaneCount; ++lane) {
2247 __atomic_store_n(&slot.quiescedThreadedGenerations[lane],
static_cast<size_t>(0),
2250 __atomic_store_n(quiescedLane(slot, QuiescedLane::Controller), quiescedGeneration,
2257bool IrqHandlerRegistry::consumeThreadedQuiescedForTest(
IrqHandlerBase* handler,
2258 size_t generation) {
2259 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
2260 HandlerSlot& slot = m_Handlers[i];
2261 if (__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) != handler) {
2265 for (
size_t lane = 0; lane < QuiescedLaneCount; ++lane) {
2266 size_t exact = generation;
2267 if (__atomic_compare_exchange_n(&slot.quiescedThreadedGenerations[lane], &exact,
2268 static_cast<size_t>(0),
false, __ATOMIC_ACQ_REL,
2269 __ATOMIC_ACQUIRE)) {
2278bool IrqHandlerRegistry::publishControllerQuiescedForTest(
IrqHandlerBase* handler, uint8_t irq,
2279 size_t generation) {
2280 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
2281 HandlerSlot& slot = m_Handlers[i];
2282 if (__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) != handler) {
2286 publishSlotQuiesced(slot, irq, generation, QuiescedLane::Controller);
static void restoreDepth(size_t previousDepth)
void invalidateThreadedGenerationFromInterrupt(uint8_t irq, size_t throughGeneration)
bool snapshotLineConfiguration(uint8_t irq, LineConfiguration &configuration) const
void releaseAdmissionCutoff(AdmissionCutoff admissionCutoff)
bool dispatchThreaded(uint8_t irq, size_t dispatchGeneration, ThreadedDispatchResult &result, IrqHandler *onlyHandler=nullptr)
bool captureMixedAdmissionCutoffs(uint8_t irq, MixedAdmissionCutoffs &cutoffs)
size_t threadedDispatchState(uint8_t irq, uintptr_t &exactHandlerIdentity) const
bool publishThreadedDispatch(uint8_t irq, size_t dispatchGeneration)
bool captureAdmissionCutoff(uint8_t irq, AdmissionCutoff &cutoff)
bool registerThreadedHandler(uint8_t irq, IrqHandler *handler)
UnregisterResult unregisterHandler(uint8_t irq, IrqHandlerBase *handler)
void invalidateThreadedLine(uint8_t irq, size_t throughGeneration)
size_t m_HardHandoffEpochs[GraceBucketCount]
bool hardLineQuarantined(uint8_t irq) const
bool dispatchHard(uint8_t irq, InterruptState &state, HardIrqDisposition &disposition, HardIrqHandler *onlyHandler=nullptr, size_t dispatchGeneration=0)
size_t hardDispatchState(uint8_t irq, size_t &exactGeneration) const
bool registerHardHandler(uint8_t irq, HardIrqHandler *handler)
LineMode lineMode(uint8_t irq)
static bool getInterrupts()
static ProcessorInformation & information()
static bool inDeviceHardIrq()
static void setInterrupts(bool bEnable)
static Scheduler & instance()
bool acquire(bool recurse=false, bool safe=true)
void setDebugState(DebugState state, uintptr_t address)
DebugState getDebugState(uintptr_t &address)