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(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(cleanup, 0)) {
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(cleanup, thread);
393void IrqHandlerRegistry::unpinActionMutation(DispatchCleanup& cleanup,
Thread* thread) {
394 unpublishDispatch(cleanup,
true);
396 thread->disarmAtomicStateCleanup(cleanup.cleanup);
400bool IrqHandlerRegistry::closeSlotAdmission(HandlerSlot& slot,
size_t expectedPublication,
401 size_t& closedPublication) {
402 const size_t originalPublication = expectedPublication;
403 const uint8_t irq = irqOf(expectedPublication);
404 const size_t graceBucket = irq % GraceBucketCount;
405 const bool canWait = canWaitForActionFinalization();
406 if (!acquireFinalizationGate(slot, canWait)) {
407 if (modeOf(originalPublication) == SlotMode::Draining) {
408 size_t draining = originalPublication;
410 __atomic_compare_exchange_n(
411 &slot.publication, &draining,
412 makePublication(generationOf(originalPublication), irq, SlotMode::Enabled,
413 deliveryOf(originalPublication)),
414 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
420 size_t boundaryExpected = 0;
421 while (!__atomic_compare_exchange_n(&m_OccurrenceBoundaryLocks[graceBucket], &boundaryExpected,
422 static_cast<size_t>(1),
false, __ATOMIC_ACQUIRE,
425 releaseFinalizationGate(slot);
426 if (modeOf(originalPublication) == SlotMode::Draining) {
427 size_t draining = originalPublication;
429 __atomic_compare_exchange_n(
430 &slot.publication, &draining,
431 makePublication(generationOf(originalPublication), irq, SlotMode::Enabled,
432 deliveryOf(originalPublication)),
433 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
438 boundaryExpected = 0;
442 const size_t occurrenceEpoch =
443 __atomic_load_n(&m_OccurrenceEpochs[graceBucket], __ATOMIC_SEQ_CST);
444 size_t boundary = occurrenceEpoch + 1;
448 __atomic_store_n(&slot.retirementEpoch, boundary, __ATOMIC_SEQ_CST);
449 const size_t cancellingPublication =
450 makePublication(generationOf(expectedPublication), irq, SlotMode::Cancelling,
451 deliveryOf(expectedPublication));
453 if (!__atomic_compare_exchange_n(&slot.publication, &expectedPublication, cancellingPublication,
454 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
455 __atomic_store_n(&slot.retirementEpoch,
static_cast<size_t>(0), __ATOMIC_SEQ_CST);
457 __atomic_store_n(&m_OccurrenceBoundaryLocks[graceBucket],
static_cast<size_t>(0),
459 releaseFinalizationGate(slot);
466 __atomic_store_n(&m_OccurrenceEpochs[graceBucket], boundary, __ATOMIC_SEQ_CST);
468#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
469 HandlerHazardHook boundaryHook = __atomic_load_n(&m_HandlerHazardHook, __ATOMIC_ACQUIRE);
471 boundaryHook(__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE),
472 HandlerHazardStage::RetirementBoundaryPublished);
476 closedPublication = makePublication(generationOf(cancellingPublication), irq, SlotMode::Closed,
477 deliveryOf(cancellingPublication));
478 size_t expectedCancelling = cancellingPublication;
480 __atomic_compare_exchange_n(&slot.publication, &expectedCancelling, closedPublication,
false,
481 __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
483 __atomic_store_n(&m_OccurrenceBoundaryLocks[graceBucket],
static_cast<size_t>(0),
485 releaseFinalizationGate(slot);
487 FATAL_NOLOCK(
"IRQ handler closure state changed before publication.");
492bool IrqHandlerRegistry::occurrencePrecedesRetirement(
const HandlerSlot& slot,
493 AdmissionCutoff admissionCutoff)
const {
494 const size_t retirementEpoch = __atomic_load_n(&slot.retirementEpoch, __ATOMIC_SEQ_CST);
495 return retirementEpoch && !generationReached(admissionCutoff.occurrenceEpoch, retirementEpoch);
498void IrqHandlerRegistry::tryReclaimTombstones(uint8_t irq) {
499 const size_t graceBucket = irq % GraceBucketCount;
500 if (__atomic_load_n(&m_OccurrenceReaders[graceBucket][0], __ATOMIC_SEQ_CST) ||
501 __atomic_load_n(&m_OccurrenceReaders[graceBucket][1], __ATOMIC_SEQ_CST)) {
505 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
506 HandlerSlot& slot = m_Handlers[i];
507 size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
508 const uint8_t slotIrq = irqOf(publication);
509 if (modeOf(publication) != SlotMode::Tombstone || slotIrq % GraceBucketCount != graceBucket ||
510 __atomic_load_n(&slot.pendingThreadedGeneration, __ATOMIC_ACQUIRE) ||
511 __atomic_load_n(&slot.claimedThreadedGeneration, __ATOMIC_ACQUIRE) ||
512 hasQuiescedGeneration(slot)) {
516 const size_t reclaimingPublication = makePublication(
517 generationOf(publication), slotIrq, SlotMode::Retiring, deliveryOf(publication));
518 if (!__atomic_compare_exchange_n(&slot.publication, &publication, reclaimingPublication,
false,
519 __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
522 if (hasActiveDispatch(slot, publication)) {
523 size_t reclaiming = reclaimingPublication;
524 if (!__atomic_compare_exchange_n(&slot.publication, &reclaiming, publication,
false,
525 __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
526 FATAL_NOLOCK(
"IRQ tombstone changed while action mutation was pinned.");
534 __atomic_store_n(&slot.admissionEpoch,
static_cast<size_t>(0), __ATOMIC_RELEASE);
535 __atomic_store_n(&slot.retirementEpoch,
static_cast<size_t>(0), __ATOMIC_RELEASE);
536 __atomic_store_n(&slot.publication,
537 makePublication(generationOf(reclaimingPublication), InvalidIrq,
538 SlotMode::Empty, Delivery::Threaded),
543bool IrqHandlerRegistry::retireSlot(HandlerSlot& slot,
size_t expectedPublication,
546 const size_t retiringPublication =
547 makePublication(generationOf(expectedPublication), irqOf(expectedPublication),
548 SlotMode::Retiring, deliveryOf(expectedPublication));
549 if (!__atomic_compare_exchange_n(&slot.publication, &expectedPublication, retiringPublication,
550 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
555 if (hasActiveDispatch(slot, expectedPublication)) {
556 size_t retiring = retiringPublication;
557 if (!__atomic_compare_exchange_n(&slot.publication, &retiring, expectedPublication,
false,
558 __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
559 FATAL_NOLOCK(
"IRQ slot changed while action mutation was pinned.");
565 assert(__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) == expectedHandler);
566 const uint8_t irq = irqOf(retiringPublication);
567 if (deliveryOf(retiringPublication) == Delivery::Threaded) {
568 size_t* actions[] = {&slot.pendingThreadedGeneration, &slot.claimedThreadedGeneration};
569 for (
size_t action = 0; action < 2; ++action) {
570 size_t generation = __atomic_load_n(actions[action], __ATOMIC_ACQUIRE);
574 publishSlotQuiesced(slot, irq, generation, QuiescedLane::Retirement);
575 if (__atomic_compare_exchange_n(actions[action], &generation,
static_cast<size_t>(0),
false,
576 __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)) {
582 const size_t hardHandoffState =
583 __atomic_exchange_n(&slot.hardHandoffState,
static_cast<size_t>(0), __ATOMIC_ACQ_REL);
584 if (hardHandoffState & 1U) {
588 __atomic_store_n(&slot.handler,
nullptr, __ATOMIC_RELEASE);
589 __atomic_store_n(&slot.policy,
static_cast<size_t>(0), __ATOMIC_RELEASE);
590 __atomic_store_n(&slot.publication,
591 makePublication(generationOf(retiringPublication), irq, SlotMode::Tombstone,
592 deliveryOf(retiringPublication)),
595 tryReclaimTombstones(irq);
599bool IrqHandlerRegistry::retireSlotOrObserveClosed(HandlerSlot& slot,
size_t expectedPublication,
601 if (retireSlot(slot, expectedPublication, expectedHandler)) {
605 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
606 return modeOf(publication) == SlotMode::Empty || modeOf(publication) == SlotMode::Closed ||
607 modeOf(publication) == SlotMode::Retiring || modeOf(publication) == SlotMode::Tombstone ||
608 generationOf(publication) != generationOf(expectedPublication) ||
609 __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) != expectedHandler;
612bool IrqHandlerRegistry::publishDispatch(DispatchCleanup& cleanup,
size_t controllerGeneration) {
613 HandlerSlot& slot = *cleanup.slot;
614 void* token = &cleanup;
615 assert(cleanup.owner);
616 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
617 ActiveDispatch& dispatch = m_ActiveDispatches[i];
619 __atomic_store_n(&cleanup.activeDispatch, &dispatch, __ATOMIC_RELAXED);
620 void* expectedToken =
nullptr;
621 if (__atomic_compare_exchange_n(&dispatch.token, &expectedToken, token,
false, __ATOMIC_SEQ_CST,
623 __atomic_add_fetch(&dispatch.generation,
static_cast<size_t>(1), __ATOMIC_ACQ_REL);
624 __atomic_store_n(&dispatch.owner, cleanup.owner, __ATOMIC_RELAXED);
625 __atomic_store_n(&dispatch.admittedPublication, cleanup.publication, __ATOMIC_RELAXED);
626 __atomic_store_n(&dispatch.controllerGeneration, controllerGeneration, __ATOMIC_RELAXED);
627 __atomic_store_n(&dispatch.callback,
628 cleanup.callback ?
static_cast<size_t>(1) : static_cast<size_t>(0),
631#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
632 HandlerHazardHook hazardHook =
nullptr;
633 if (cleanup.callback) {
634 hazardHook = __atomic_load_n(&m_HandlerHazardHook, __ATOMIC_ACQUIRE);
636 hazardHook(__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE), HandlerHazardStage::Claimed);
644 __atomic_store_n(&dispatch.slot, &slot, __ATOMIC_SEQ_CST);
646#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
647 if (cleanup.callback) {
648 hazardHook = __atomic_load_n(&m_HandlerHazardHook, __ATOMIC_ACQUIRE);
650 hazardHook(__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE),
651 HandlerHazardStage::Committed);
662bool IrqHandlerRegistry::unpublishDispatch(DispatchCleanup& cleanup,
bool required) {
663 ActiveDispatch* dispatch = __atomic_load_n(&cleanup.activeDispatch, __ATOMIC_RELAXED);
664 void* token = &cleanup;
665 if (!dispatch || __atomic_load_n(&dispatch->token, __ATOMIC_ACQUIRE) != token) {
667 FATAL_NOLOCK(
"IRQ callback hazard was released more than once.");
672 const size_t generation = __atomic_load_n(&dispatch->generation, __ATOMIC_ACQUIRE);
673 HandlerSlot* publishedSlot = __atomic_load_n(&dispatch->slot, __ATOMIC_SEQ_CST);
674 if (__atomic_load_n(&dispatch->token, __ATOMIC_ACQUIRE) != token ||
675 __atomic_load_n(&dispatch->generation, __ATOMIC_ACQUIRE) != generation) {
677 FATAL_NOLOCK(
"IRQ callback hazard changed during release.");
682 HandlerSlot& slot = *cleanup.slot;
683 const size_t admittedPublication = cleanup.publication;
684 if (publishedSlot && publishedSlot != &slot) {
685 FATAL_NOLOCK(
"IRQ callback hazard changed slots during release.");
689 const bool committed = publishedSlot == &slot;
690 const bool callback = __atomic_load_n(&dispatch->callback, __ATOMIC_RELAXED) != 0;
691 const size_t controllerGeneration =
692 __atomic_load_n(&dispatch->controllerGeneration, __ATOMIC_RELAXED);
693 __atomic_store_n(&dispatch->slot,
nullptr, __ATOMIC_SEQ_CST);
694 __atomic_store_n(&dispatch->admittedPublication,
static_cast<size_t>(0), __ATOMIC_RELAXED);
695 __atomic_store_n(&dispatch->controllerGeneration,
static_cast<size_t>(0), __ATOMIC_RELAXED);
696 __atomic_store_n(&dispatch->callback,
static_cast<size_t>(0), __ATOMIC_RELAXED);
697 __atomic_store_n(&dispatch->owner,
nullptr, __ATOMIC_RELAXED);
698 __atomic_store_n(&dispatch->token,
nullptr, __ATOMIC_RELEASE);
700#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
702 HandlerHazardHook releaseHook =
nullptr;
703 if (committed && callback) {
704 releasedHandler = __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE);
705 releaseHook = __atomic_load_n(&m_HandlerHazardHook, __ATOMIC_ACQUIRE);
709 if (committed && callback && !required && controllerGeneration &&
710 deliveryOf(admittedPublication) == Delivery::Threaded) {
711 publishSlotQuiesced(slot, irqOf(admittedPublication), controllerGeneration,
712 QuiescedLane::Callback);
713 size_t claimed = controllerGeneration;
714 __atomic_compare_exchange_n(&slot.claimedThreadedGeneration, &claimed,
static_cast<size_t>(0),
715 false, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE);
719 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
720 if (generationOf(publication) == generationOf(admittedPublication) &&
721 modeOf(publication) == SlotMode::Closed && !hasActiveDispatch(slot, admittedPublication)) {
722 IrqHandlerBase* handler = __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE);
724 retireSlot(slot, publication, handler);
729#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
731 releaseHook(releasedHandler, HandlerHazardStage::Released);
737void IrqHandlerRegistry::abandonDispatch(
void* context) {
738 DispatchCleanup* dispatch =
reinterpret_cast<DispatchCleanup*
>(context);
739 const bool callbackBoundaryEntered = dispatch->restoreInterruptState;
740 if (dispatch->restoreDeviceHardIrqDepth) {
742 dispatch->restoreDeviceHardIrqDepth =
false;
744 dispatch->registry->unpublishDispatch(*dispatch,
false);
745 restoreDispatchInterruptState(*dispatch);
747#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
748 if (dispatch->callback) {
749 DispatchAbandonHook hook =
750 __atomic_load_n(&dispatch->registry->m_DispatchAbandonHook, __ATOMIC_ACQUIRE);
752 hook(dispatch->owner, callbackBoundaryEntered);
758void IrqHandlerRegistry::restoreDispatchInterruptState(DispatchCleanup& dispatch) {
759 if (!dispatch.restoreInterruptState) {
763 const bool previousInterruptState = dispatch.previousInterruptState;
764 dispatch.restoreInterruptState =
false;
768bool IrqHandlerRegistry::hasActiveDispatch(HandlerSlot& target,
size_t admittedPublication)
const {
769 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
770 const ActiveDispatch& dispatch = m_ActiveDispatches[i];
771 void* token = __atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE);
776 const size_t generation = __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE);
777 HandlerSlot* slot = __atomic_load_n(&dispatch.slot, __ATOMIC_SEQ_CST);
778 const size_t dispatchPublication =
779 __atomic_load_n(&dispatch.admittedPublication, __ATOMIC_RELAXED);
780 if (slot == &target && generationOf(dispatchPublication) == generationOf(admittedPublication) &&
781 __atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE) == token &&
782 __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE) == generation) {
789bool IrqHandlerRegistry::findCurrentDispatch(
void* owner, HandlerSlot* target,
790 size_t targetPublication,
791 bool& callbackContext)
const {
792 callbackContext =
false;
793 bool foundTarget =
false;
794 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
795 const ActiveDispatch& dispatch = m_ActiveDispatches[i];
796 void* token = __atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE);
801 const size_t generation = __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE);
802 void* dispatchOwner = __atomic_load_n(&dispatch.owner, __ATOMIC_RELAXED);
803 HandlerSlot* slot = __atomic_load_n(&dispatch.slot, __ATOMIC_SEQ_CST);
804 const size_t dispatchPublication =
805 __atomic_load_n(&dispatch.admittedPublication, __ATOMIC_RELAXED);
806 if (__atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE) != token ||
807 __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE) != generation) {
811 if (dispatchOwner == owner && slot && __atomic_load_n(&dispatch.callback, __ATOMIC_RELAXED)) {
812 callbackContext =
true;
813 foundTarget |= slot == target && target &&
814 generationOf(dispatchPublication) == generationOf(targetPublication);
820void* IrqHandlerRegistry::currentDispatchOwner() {
822 Thread* thread = information.getCurrentThread();
823 return thread ?
static_cast<void*
>(thread) : static_cast<void*>(&information);
826bool IrqHandlerRegistry::registerHandler(uint8_t irq,
IrqHandlerBase* handler, Delivery delivery,
828 if (!handler || (delivery != Delivery::Threaded && delivery != Delivery::HardOnly)) {
833 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
834 HandlerSlot& slot = m_Handlers[i];
835 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
836 const SlotMode mode = modeOf(publication);
837 if (mode == SlotMode::Empty || mode == SlotMode::Cancelling || mode == SlotMode::Closed ||
838 mode == SlotMode::Retiring || mode == SlotMode::Tombstone || irqOf(publication) != irq) {
841 const Delivery existingDelivery = deliveryOf(publication);
842 const size_t existingPolicy = __atomic_load_n(&slot.policy, __ATOMIC_ACQUIRE);
843 if ((existingDelivery == delivery && existingPolicy != policy) ||
844 (existingDelivery != delivery && !mixedPoliciesCompatible(existingPolicy, policy))) {
848 if (__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) == handler) {
853 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
854 HandlerSlot& slot = m_Handlers[i];
855 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
856 if (modeOf(publication) == SlotMode::Empty &&
857 !__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) &&
858 !hasActiveDispatch(slot, publication)) {
859 const size_t generation = generationOf(publication) + 1;
860 if (!generation || generation > MaximumPublicationGeneration) {
864 size_t admissionEpoch = __atomic_load_n(&m_AdmissionEpoch, __ATOMIC_RELAXED) + 1;
865 if (!admissionEpoch) {
868 __atomic_store_n(&slot.pendingThreadedGeneration,
static_cast<size_t>(0), __ATOMIC_RELEASE);
869 __atomic_store_n(&slot.claimedThreadedGeneration,
static_cast<size_t>(0), __ATOMIC_RELEASE);
870 for (
size_t lane = 0; lane < QuiescedLaneCount; ++lane) {
871 __atomic_store_n(&slot.quiescedThreadedGenerations[lane],
static_cast<size_t>(0),
874 __atomic_store_n(&slot.retirementEpoch,
static_cast<size_t>(0), __ATOMIC_RELEASE);
875 __atomic_store_n(&slot.hardHandoffState, delivery == Delivery::HardOnly ? generation << 1 : 0,
877 __atomic_store_n(&slot.handler, handler, __ATOMIC_RELEASE);
879 __atomic_store_n(&slot.policy, policy, __ATOMIC_RELEASE);
880 __atomic_store_n(&slot.admissionEpoch, admissionEpoch, __ATOMIC_RELEASE);
881 __atomic_store_n(&slot.publication,
882 makePublication(generation, irq, SlotMode::Enabled, delivery),
887 __atomic_store_n(&m_AdmissionEpoch, admissionEpoch, __ATOMIC_RELEASE);
897 return registerHandler(irq, handler, Delivery::Threaded, 0);
902 return policy.validForThreaded() &&
903 registerHandler(irq, handler, Delivery::Threaded, encodePolicy(&policy));
907 return registerHandler(irq, handler, Delivery::HardOnly, 0);
912 return policy.validForHard() &&
913 registerHandler(irq, handler, Delivery::HardOnly, encodePolicy(&policy));
918 LineMode ignoredDelivery = LineMode::Empty;
923 uint8_t irq,
IrqHandlerBase* handler, LineMode& removedDelivery) {
924 removedDelivery = LineMode::Empty;
926 return UnregisterResult::NotFound;
929 void* owner = currentDispatchOwner();
933 canYield = canYield && !current->getHostedSignalDepth();
936 bool callbackContext =
false;
937 findCurrentDispatch(owner,
nullptr, 0, callbackContext);
939 if (!canYield || callbackContext) {
944 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
946 size_t publication = __atomic_load_n(&candidate.publication, __ATOMIC_SEQ_CST);
947 if (modeOf(publication) != SlotMode::Enabled || irqOf(publication) != irq ||
948 __atomic_load_n(&candidate.handler, __ATOMIC_ACQUIRE) != handler) {
952 removedDelivery = lineModeForDelivery(deliveryOf(publication));
953 bool currentTargetDispatch =
false;
954 if (findCurrentDispatch(owner, &candidate, publication, currentTargetDispatch)) {
955 size_t closedPublication = 0;
956 const bool deferred = closeSlotAdmission(candidate, publication, closedPublication);
958 return UnregisterResult::Deferred;
960 return UnregisterResult::Rejected;
963 const size_t drainingPublication = makePublication(
964 generationOf(publication), irq, SlotMode::Draining, deliveryOf(publication));
966 const bool draining =
967 __atomic_compare_exchange_n(&candidate.publication, &publication, drainingPublication,
968 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
971 return UnregisterResult::Rejected;
974 if (hasActiveDispatch(candidate, drainingPublication)) {
975 size_t expectedPublication = drainingPublication;
977 __atomic_compare_exchange_n(
978 &candidate.publication, &expectedPublication,
979 makePublication(generationOf(drainingPublication), irq, SlotMode::Enabled,
980 deliveryOf(drainingPublication)),
981 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
983 return UnregisterResult::Rejected;
986 size_t closedPublication = 0;
987 if (!closeSlotAdmission(candidate, drainingPublication, closedPublication)) {
988 return UnregisterResult::Rejected;
991 return retireSlotOrObserveClosed(candidate, closedPublication, handler)
992 ? UnregisterResult::Completed
993 : UnregisterResult::Rejected;
996 return UnregisterResult::NotFound;
1006 size_t publication = 0;
1007 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
1008 size_t candidatePublication = __atomic_load_n(&m_Handlers[i].publication, __ATOMIC_SEQ_CST);
1009 if (modeOf(candidatePublication) != SlotMode::Empty && irqOf(candidatePublication) == irq &&
1010 __atomic_load_n(&m_Handlers[i].handler, __ATOMIC_ACQUIRE) == handler) {
1011 slot = &m_Handlers[i];
1012 publication = candidatePublication;
1019 return UnregisterResult::NotFound;
1022 removedDelivery = lineModeForDelivery(deliveryOf(publication));
1023 const bool selfUnregister = findCurrentDispatch(owner, slot, publication, callbackContext);
1024 if (selfUnregister) {
1025 if (modeOf(publication) != SlotMode::Enabled) {
1027 return UnregisterResult::Rejected;
1030 size_t closedPublication = 0;
1031 const bool deferred = closeSlotAdmission(*slot, publication, closedPublication);
1033 return deferred ? UnregisterResult::Deferred : UnregisterResult::Rejected;
1036 if (modeOf(publication) != SlotMode::Enabled) {
1038 return UnregisterResult::Rejected;
1046 size_t closedPublication = 0;
1047 if (!closeSlotAdmission(*slot, publication, closedPublication)) {
1048 return UnregisterResult::Rejected;
1051 if (!hasActiveDispatch(*slot, closedPublication)) {
1053 const bool retired = retireSlotOrObserveClosed(*slot, closedPublication, handler);
1055 return retired ? UnregisterResult::Completed : UnregisterResult::Rejected;
1058 uintptr_t previousDebugAddress = 0;
1060 current->
setDebugState(Thread::CallbackDrain,
reinterpret_cast<uintptr_t
>(handler));
1061 while (hasActiveDispatch(*slot, closedPublication)) {
1066 current->
setDebugState(previousDebugState, previousDebugAddress);
1069 const bool retired = retireSlotOrObserveClosed(*slot, closedPublication, handler);
1071 return retired ? UnregisterResult::Completed : UnregisterResult::Rejected;
1076 size_t dispatchGeneration, irq_id_t callbackId) {
1079 disposition = HardIrqDisposition::NotHandled;
1082 return dispatchHard(irq, state, disposition, onlyHandler, dispatchGeneration, admissionCutoff,
1088 const size_t graceBucket = irq % GraceBucketCount;
1089 if (!acquireOccurrenceReaderLeases(irq, 0, 1)) {
1092#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1093 observeOccurrenceCaptureForTest(irq, OccurrenceCaptureStage::BankZeroClaimed, 0);
1095 if (!acquireOccurrenceReaderLeases(irq, 1, 1)) {
1096 releaseOccurrenceReaderLeases(irq, 0, 1);
1099#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1100 observeOccurrenceCaptureForTest(irq, OccurrenceCaptureStage::BankOneClaimed, 0);
1108 const size_t occurrenceEpoch =
1109 __atomic_load_n(&m_OccurrenceEpochs[graceBucket], __ATOMIC_SEQ_CST);
1110#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1111 observeOccurrenceCaptureForTest(irq, OccurrenceCaptureStage::EpochSampled, occurrenceEpoch);
1113 const size_t readerBank = occurrenceEpoch & 1;
1114 releaseOccurrenceReaderLeases(irq, readerBank ^ 1, 1);
1115#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1116 observeOccurrenceCaptureForTest(irq, OccurrenceCaptureStage::UnusedBankReleased, occurrenceEpoch);
1118 const size_t admissionEpoch = __atomic_load_n(&m_AdmissionEpoch, __ATOMIC_ACQUIRE);
1119 cutoff = {admissionEpoch, occurrenceEpoch, (
static_cast<size_t>(irq) * 2) + readerBank + 1};
1125 const size_t graceBucket = irq % GraceBucketCount;
1126 if (!acquireOccurrenceReaderLeases(irq, 0, 2)) {
1129#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1130 observeOccurrenceCaptureForTest(irq, OccurrenceCaptureStage::BankZeroClaimed, 0);
1132 if (!acquireOccurrenceReaderLeases(irq, 1, 2)) {
1133 releaseOccurrenceReaderLeases(irq, 0, 2);
1136#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1137 observeOccurrenceCaptureForTest(irq, OccurrenceCaptureStage::BankOneClaimed, 0);
1140 const size_t occurrenceEpoch =
1141 __atomic_load_n(&m_OccurrenceEpochs[graceBucket], __ATOMIC_SEQ_CST);
1142#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1143 observeOccurrenceCaptureForTest(irq, OccurrenceCaptureStage::EpochSampled, occurrenceEpoch);
1145 const size_t readerBank = occurrenceEpoch & 1;
1146 releaseOccurrenceReaderLeases(irq, readerBank ^ 1, 2);
1147#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1148 observeOccurrenceCaptureForTest(irq, OccurrenceCaptureStage::UnusedBankReleased, occurrenceEpoch);
1150 const size_t admissionEpoch = __atomic_load_n(&m_AdmissionEpoch, __ATOMIC_ACQUIRE);
1152 (
static_cast<size_t>(irq) * 2) + readerBank + 1};
1153 cutoffs = {cutoff, cutoff};
1157bool IrqHandlerRegistry::acquireOccurrenceReaderLeases(uint8_t irq,
size_t readerBank,
1159 if (readerBank > 1 || !count) {
1160 FATAL_NOLOCK(
"Invalid IRQ occurrence reader acquisition.");
1164 const size_t graceBucket = irq % GraceBucketCount;
1165 const size_t previous =
1166 __atomic_fetch_add(&m_OccurrenceReaders[graceBucket][readerBank], count, __ATOMIC_SEQ_CST);
1167 if (previous > ~
static_cast<size_t>(0) - count) {
1168 __atomic_fetch_sub(&m_OccurrenceReaders[graceBucket][readerBank], count, __ATOMIC_SEQ_CST);
1169 FATAL_NOLOCK(
"IRQ occurrence reader count overflowed.");
1175void IrqHandlerRegistry::releaseOccurrenceReaderLeases(uint8_t irq,
size_t readerBank,
1177 if (readerBank > 1 || !count) {
1178 FATAL_NOLOCK(
"Invalid IRQ occurrence reader release.");
1182 const size_t graceBucket = irq % GraceBucketCount;
1183 const size_t previous =
1184 __atomic_fetch_sub(&m_OccurrenceReaders[graceBucket][readerBank], count, __ATOMIC_SEQ_CST);
1185 if (previous < count) {
1186 __atomic_fetch_add(&m_OccurrenceReaders[graceBucket][readerBank], count, __ATOMIC_SEQ_CST);
1187 FATAL_NOLOCK(
"IRQ occurrence reader count underflowed.");
1191 const size_t remaining = previous - count;
1193 tryReclaimTombstones(irq);
1198 if (!admissionCutoff.readerToken) {
1202 const size_t token = admissionCutoff.readerToken - 1;
1203 if (token >= IrqCount * 2) {
1204 FATAL_NOLOCK(
"Invalid IRQ occurrence reader token.");
1207 const uint8_t irq =
static_cast<uint8_t
>(token / 2);
1208 const size_t readerBank = token & 1;
1209 releaseOccurrenceReaderLeases(irq, readerBank, 1);
1212void IrqHandlerRegistry::beginAdmissionCutoffCleanup(AdmissionCutoffCleanup& cleanup) {
1216 cleanup.ownsCutoff = cleanup.cutoff.readerToken != 0;
1217 if (cleanup.thread && cleanup.ownsCutoff) {
1221 cleanup.thread->armAtomicStateCleanup(cleanup.cleanup, abandonAdmissionCutoff, &cleanup);
1226void IrqHandlerRegistry::finishAdmissionCutoffCleanup(AdmissionCutoffCleanup& cleanup) {
1229 if (cleanup.ownsCutoff) {
1232 cleanup.ownsCutoff =
false;
1235 if (cleanup.thread && cleanup.cleanup.armed) {
1236 cleanup.thread->disarmAtomicStateCleanup(cleanup.cleanup);
1238 cleanup.registry =
nullptr;
1242void IrqHandlerRegistry::abandonAdmissionCutoff(
void* context) {
1243 AdmissionCutoffCleanup* cleanup =
reinterpret_cast<AdmissionCutoffCleanup*
>(context);
1244 if (!cleanup || !cleanup->registry || !cleanup->ownsCutoff) {
1248 cleanup->ownsCutoff =
false;
1249 cleanup->registry->releaseAdmissionCutoff(cleanup->cutoff);
1250 cleanup->registry =
nullptr;
1255 size_t dispatchGeneration, AdmissionCutoff admissionCutoff,
1256 irq_id_t callbackId) {
1258 AdmissionCutoffCleanup cutoffCleanup(
this, admissionCutoff);
1259 beginAdmissionCutoffCleanup(cutoffCleanup);
1260 bool admitted =
false;
1261 disposition = HardIrqDisposition::NotHandled;
1262 const size_t cutoffEpoch = admissionCutoff.epoch;
1263 const size_t expectedReaderToken =
1264 (
static_cast<size_t>(irq) * 2) + (admissionCutoff.occurrenceEpoch & 1) + 1;
1265 if (admissionCutoff.readerToken != expectedReaderToken) {
1266 finishAdmissionCutoffCleanup(cutoffCleanup);
1270 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
1271 HandlerSlot& slot = m_Handlers[i];
1272 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
1273 const SlotMode mode = modeOf(publication);
1274 if (irqOf(publication) != irq || deliveryOf(publication) != Delivery::HardOnly) {
1277 const size_t admissionEpoch = __atomic_load_n(&slot.admissionEpoch, __ATOMIC_ACQUIRE);
1278 if (!admissionEpoch ||
1279 (admissionEpoch != cutoffEpoch && generationReached(admissionEpoch, cutoffEpoch))) {
1283 if (mode == SlotMode::Cancelling || mode == SlotMode::Closed || mode == SlotMode::Retiring ||
1284 mode == SlotMode::Tombstone) {
1285 if (occurrencePrecedesRetirement(slot, admissionCutoff)) {
1287 if (disposition == HardIrqDisposition::NotHandled) {
1288 disposition = HardIrqDisposition::Handled;
1293 if (mode != SlotMode::Enabled && mode != SlotMode::Draining) {
1297 IrqHandlerBase* handler = __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE);
1298 if (!handler || (onlyHandler && handler !=
static_cast<IrqHandlerBase*
>(onlyHandler))) {
1302#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1303 HandlerPrePinHook prePinHook = __atomic_load_n(&m_HandlerPrePinHook, __ATOMIC_ACQUIRE);
1305 prePinHook(handler);
1309 void* owner = currentDispatchOwner();
1311 DispatchCleanup dispatchCleanup(
this, &slot, owner, publication);
1316 thread->armAtomicStateCleanup(dispatchCleanup.cleanup, abandonDispatch, &dispatchCleanup);
1319#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1320 HandlerHazardHook hazardHook = __atomic_load_n(&m_HandlerHazardHook, __ATOMIC_ACQUIRE);
1322 hazardHook(handler, HandlerHazardStage::BeforeClaim);
1326 if (!publishDispatch(dispatchCleanup, dispatchGeneration)) {
1328 thread->disarmAtomicStateCleanup(dispatchCleanup.cleanup);
1330 finishAdmissionCutoffCleanup(cutoffCleanup);
1331 FATAL_NOLOCK(
"IRQ callback hazard table exhausted.");
1335 size_t currentPublication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
1336 SlotMode currentMode = modeOf(currentPublication);
1337 const bool sameLifetime =
1338 generationOf(currentPublication) == generationOf(publication) &&
1339 irqOf(currentPublication) == irq && deliveryOf(currentPublication) == Delivery::HardOnly &&
1340 __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) == handler &&
1341 __atomic_load_n(&slot.admissionEpoch, __ATOMIC_ACQUIRE) == admissionEpoch;
1342 if (sameLifetime && currentMode == SlotMode::Draining) {
1347 size_t expectedPublication = currentPublication;
1348 const size_t enabledPublication = makePublication(generationOf(currentPublication), irq,
1349 SlotMode::Enabled, Delivery::HardOnly);
1351 __atomic_compare_exchange_n(&slot.publication, &expectedPublication, enabledPublication,
1352 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
1354 currentPublication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
1355 currentMode = modeOf(currentPublication);
1358 if (generationOf(currentPublication) != generationOf(publication) ||
1359 irqOf(currentPublication) != irq || deliveryOf(currentPublication) != Delivery::HardOnly ||
1360 currentMode != SlotMode::Enabled ||
1361 __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) != handler ||
1362 __atomic_load_n(&slot.admissionEpoch, __ATOMIC_ACQUIRE) != admissionEpoch) {
1363 unpublishDispatch(dispatchCleanup,
true);
1365 thread->disarmAtomicStateCleanup(dispatchCleanup.cleanup);
1372#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1373 HandlerPinHook hook = __atomic_load_n(&m_HandlerPinHook, __ATOMIC_ACQUIRE);
1384 dispatchCleanup.restoreInterruptState =
true;
1387 dispatchCleanup.restoreDeviceHardIrqDepth);
1389 static_cast<HardIrqHandler*
>(handler)->irq(callbackId ? callbackId : irq, state);
1391 size_t expected = generationOf(publication) << 1;
1392 if (__atomic_compare_exchange_n(&slot.hardHandoffState, &expected, expected | 1U,
false,
1393 __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)) {
1399 (callbackDisposition == HardIrqDisposition::Handled &&
1400 disposition == HardIrqDisposition::NotHandled)) {
1401 disposition = callbackDisposition;
1404 unpublishDispatch(dispatchCleanup,
true);
1406 thread->disarmAtomicStateCleanup(dispatchCleanup.cleanup);
1408 restoreDispatchInterruptState(dispatchCleanup);
1411 finishAdmissionCutoffCleanup(cutoffCleanup);
1413 disposition = HardIrqDisposition::Handled;
1419 const size_t epochBucket = irq % GraceBucketCount;
1420 for (
size_t attempt = 0; attempt < 2; ++attempt) {
1421 const size_t startEpoch = __atomic_load_n(&
m_HardHandoffEpochs[epochBucket], __ATOMIC_ACQUIRE);
1422 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
1424 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
1425 const SlotMode mode = modeOf(publication);
1426 if ((mode != SlotMode::Enabled && mode != SlotMode::Draining) || irqOf(publication) != irq ||
1427 deliveryOf(publication) != Delivery::HardOnly) {
1431 const size_t generation = generationOf(publication);
1432 if (__atomic_load_n(&slot.
hardHandoffState, __ATOMIC_ACQUIRE) != ((generation << 1) | 1U)) {
1436 const size_t currentPublication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
1437 const SlotMode currentMode = modeOf(currentPublication);
1438 if ((currentMode == SlotMode::Enabled || currentMode == SlotMode::Draining) &&
1439 irqOf(currentPublication) == irq &&
1440 deliveryOf(currentPublication) == Delivery::HardOnly &&
1441 generationOf(currentPublication) == generation) {
1465 AdmissionCutoff admissionCutoff) {
1466 AdmissionCutoffCleanup cutoffCleanup(
this, admissionCutoff);
1467 beginAdmissionCutoffCleanup(cutoffCleanup);
1468 const size_t expectedReaderToken =
1469 (
static_cast<size_t>(irq) * 2) + (admissionCutoff.occurrenceEpoch & 1) + 1;
1470 if (admissionCutoff.readerToken != expectedReaderToken) {
1471 finishAdmissionCutoffCleanup(cutoffCleanup);
1474 if (!threadedGenerationValid(irq, dispatchGeneration)) {
1475 finishAdmissionCutoffCleanup(cutoffCleanup);
1483 size_t admissionEpoch;
1485 Candidate candidates[MaxHandlerSlots];
1486 size_t candidateCount = 0;
1487 const size_t cutoffEpoch = admissionCutoff.epoch;
1488 bool admitted =
false;
1490 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
1491 HandlerSlot& slot = m_Handlers[i];
1492 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
1493 const SlotMode mode = modeOf(publication);
1494 if (irqOf(publication) != irq || deliveryOf(publication) != Delivery::Threaded) {
1498 const size_t admissionEpoch = __atomic_load_n(&slot.admissionEpoch, __ATOMIC_ACQUIRE);
1499 if (!admissionEpoch ||
1500 (admissionEpoch != cutoffEpoch && generationReached(admissionEpoch, cutoffEpoch))) {
1504 if (mode == SlotMode::Cancelling || mode == SlotMode::Closed || mode == SlotMode::Retiring ||
1505 mode == SlotMode::Tombstone) {
1506 if (occurrencePrecedesRetirement(slot, admissionCutoff)) {
1507 publishSlotQuiesced(slot, irq, dispatchGeneration, QuiescedLane::Controller);
1512 if (mode != SlotMode::Enabled && mode != SlotMode::Draining) {
1516 IrqHandlerBase* handler = __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE);
1518 candidates[candidateCount++] = {&slot, publication, handler, admissionEpoch};
1522 for (
size_t i = 0; i < candidateCount; ++i) {
1523 HandlerSlot& slot = *candidates[i].slot;
1524 const size_t publication = candidates[i].publication;
1526 const size_t admissionEpoch = candidates[i].admissionEpoch;
1527 void* owner = currentDispatchOwner();
1529 DispatchCleanup dispatchCleanup(
this, &slot, owner, publication,
false);
1533 thread->armAtomicStateCleanup(dispatchCleanup.cleanup, abandonDispatch, &dispatchCleanup);
1536#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1537 HandlerHazardHook hazardHook = __atomic_load_n(&m_HandlerHazardHook, __ATOMIC_ACQUIRE);
1539 hazardHook(handler, HandlerHazardStage::BeforeClaim);
1543 if (!publishDispatch(dispatchCleanup, dispatchGeneration)) {
1545 thread->disarmAtomicStateCleanup(dispatchCleanup.cleanup);
1547 finishAdmissionCutoffCleanup(cutoffCleanup);
1548 FATAL_NOLOCK(
"IRQ callback hazard table exhausted.");
1552 size_t currentPublication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
1553 SlotMode currentMode = modeOf(currentPublication);
1554 const bool sameLifetime =
1555 generationOf(currentPublication) == generationOf(publication) &&
1556 irqOf(currentPublication) == irq && deliveryOf(currentPublication) == Delivery::Threaded &&
1557 __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) == handler &&
1558 __atomic_load_n(&slot.admissionEpoch, __ATOMIC_ACQUIRE) == admissionEpoch;
1559 if (sameLifetime && (currentMode == SlotMode::Enabled || currentMode == SlotMode::Draining) &&
1560 threadedGenerationValid(irq, dispatchGeneration)) {
1561 if (currentMode == SlotMode::Draining) {
1562 size_t expectedPublication = currentPublication;
1563 const size_t enabledPublication = makePublication(generationOf(currentPublication), irq,
1564 SlotMode::Enabled, Delivery::Threaded);
1566 __atomic_compare_exchange_n(&slot.publication, &expectedPublication, enabledPublication,
1567 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
1569 currentPublication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
1570 currentMode = modeOf(currentPublication);
1573 if (currentMode != SlotMode::Enabled) {
1574 publishSlotQuiesced(slot, irq, dispatchGeneration, QuiescedLane::Controller);
1577 if (currentMode == SlotMode::Enabled) {
1578#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1579 HandlerHazardHook publicationHook = __atomic_load_n(&m_HandlerHazardHook, __ATOMIC_ACQUIRE);
1580 if (publicationHook) {
1581 publicationHook(handler, HandlerHazardStage::BeforePendingExchange);
1585 const size_t pending = __atomic_load_n(&slot.pendingThreadedGeneration, __ATOMIC_ACQUIRE);
1586 if (!pending || !generationReached(pending, dispatchGeneration)) {
1592 __atomic_exchange_n(&slot.pendingThreadedGeneration, dispatchGeneration,
1596#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1597 publicationHook = __atomic_load_n(&m_HandlerHazardHook, __ATOMIC_ACQUIRE);
1598 if (publicationHook) {
1599 publicationHook(handler, HandlerHazardStage::PendingExchanged);
1603 if (!threadedGenerationValid(irq, dispatchGeneration)) {
1604 size_t stale = dispatchGeneration;
1605 __atomic_compare_exchange_n(&slot.pendingThreadedGeneration, &stale,
1606 static_cast<size_t>(0),
false, __ATOMIC_ACQ_REL,
1611 const size_t finalPublication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
1612 const SlotMode finalMode = modeOf(finalPublication);
1613 if (generationOf(finalPublication) != generationOf(publication) ||
1614 irqOf(finalPublication) != irq ||
1615 (finalMode != SlotMode::Enabled && finalMode != SlotMode::Draining) ||
1616 __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) != handler ||
1617 __atomic_load_n(&slot.admissionEpoch, __ATOMIC_ACQUIRE) != admissionEpoch) {
1618 publishSlotQuiesced(slot, irq, dispatchGeneration, QuiescedLane::Controller);
1624 if (occurrencePrecedesRetirement(slot, admissionCutoff)) {
1625 publishSlotQuiesced(slot, irq, dispatchGeneration, QuiescedLane::Controller);
1630 unpublishDispatch(dispatchCleanup,
true);
1632 thread->disarmAtomicStateCleanup(dispatchCleanup.cleanup);
1636 finishAdmissionCutoffCleanup(cutoffCleanup);
1642 irq_id_t callbackId) {
1643 result = {
false,
false};
1644 bool admitted =
false;
1645 if (!threadedGenerationValid(irq, dispatchGeneration)) {
1653 if (dispatchThread->getHostedSignalDepth()) {
1663 if (cutoffCaptured) {
1664 beginAdmissionCutoffCleanup(cutoffCleanup);
1667 if (!cutoffCaptured) {
1677 if (__atomic_load_n(&m_ThreadedActionMutationWriters, __ATOMIC_SEQ_CST)) {
1681 const size_t actionMutationGeneration =
1682 __atomic_load_n(&m_ThreadedActionMutationGeneration, __ATOMIC_SEQ_CST);
1683 if (__atomic_load_n(&m_ThreadedActionMutationWriters, __ATOMIC_SEQ_CST)) {
1688 Candidate candidates[MaxHandlerSlots];
1689 size_t candidateCount = 0;
1694 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
1696 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
1697 if (irqOf(publication) != irq || deliveryOf(publication) != Delivery::Threaded) {
1701 for (
size_t lane = 0; lane < QuiescedLaneCount; ++lane) {
1702 size_t* publicationLane = &slot.quiescedThreadedGenerations[lane];
1703 size_t quiesced = __atomic_load_n(publicationLane, __ATOMIC_ACQUIRE);
1704 if (quiesced && !threadedGenerationValid(irq, quiesced)) {
1705 __atomic_compare_exchange_n(publicationLane, &quiesced,
static_cast<size_t>(0),
false,
1706 __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE);
1709 if (quiesced && generationReached(dispatchGeneration, quiesced) &&
1710 __atomic_compare_exchange_n(publicationLane, &quiesced,
static_cast<size_t>(0),
false,
1711 __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)) {
1713 result.allowRearm =
true;
1717#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1718 HandlerHazardHook quiescedHook = __atomic_load_n(&m_HandlerHazardHook, __ATOMIC_ACQUIRE);
1720 quiescedHook(__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE),
1721 HandlerHazardStage::QuiescedObserved);
1725 if (modeOf(publication) != SlotMode::Enabled) {
1729 const size_t pending = __atomic_load_n(&slot.pendingThreadedGeneration, __ATOMIC_ACQUIRE);
1730 if (!pending || !generationReached(dispatchGeneration, pending)) {
1733 if (!threadedGenerationValid(irq, pending)) {
1734 size_t stale = pending;
1735 __atomic_compare_exchange_n(&slot.pendingThreadedGeneration, &stale,
static_cast<size_t>(0),
1736 false, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE);
1740 IrqHandlerBase* handler = __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE);
1741 if (!handler || (onlyHandler && handler !=
static_cast<IrqHandlerBase*
>(onlyHandler))) {
1745 candidates[candidateCount++] = {&slot, publication, handler};
1748 for (
size_t i = 0; i < candidateCount; ++i) {
1750 const size_t publication = candidates[i].publication;
1753#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1754 HandlerPrePinHook prePinHook = __atomic_load_n(&m_HandlerPrePinHook, __ATOMIC_ACQUIRE);
1756 prePinHook(handler);
1760 void* owner = currentDispatchOwner();
1767 thread->armAtomicStateCleanup(dispatchCleanup.cleanup, abandonDispatch, &dispatchCleanup);
1770#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1771 HandlerHazardHook hazardHook = __atomic_load_n(&m_HandlerHazardHook, __ATOMIC_ACQUIRE);
1773 hazardHook(handler, HandlerHazardStage::BeforeClaim);
1777 if (!publishDispatch(dispatchCleanup, dispatchGeneration)) {
1779 thread->disarmAtomicStateCleanup(dispatchCleanup.cleanup);
1781 finishAdmissionCutoffCleanup(cutoffCleanup);
1782 FATAL_NOLOCK(
"IRQ callback hazard table exhausted.");
1786 if (__atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST) != publication ||
1787 __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) != handler) {
1788 unpublishDispatch(dispatchCleanup,
true);
1790 thread->disarmAtomicStateCleanup(dispatchCleanup.cleanup);
1795#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1796 HandlerPinHook hook = __atomic_load_n(&m_HandlerPinHook, __ATOMIC_ACQUIRE);
1802 size_t pending = __atomic_load_n(&slot.pendingThreadedGeneration, __ATOMIC_ACQUIRE);
1803 bool claimed =
false;
1804 size_t claimedGeneration = 0;
1805 while (pending && generationReached(dispatchGeneration, pending)) {
1806 size_t emptyClaim = 0;
1807 if (!__atomic_compare_exchange_n(&slot.claimedThreadedGeneration, &emptyClaim, pending,
1808 false, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)) {
1812 claimedGeneration = pending;
1813 size_t exactPending = pending;
1814 if (__atomic_compare_exchange_n(&slot.pendingThreadedGeneration, &exactPending,
1815 static_cast<size_t>(0),
false, __ATOMIC_ACQ_REL,
1816 __ATOMIC_ACQUIRE)) {
1820 size_t exactClaim = claimedGeneration;
1821 __atomic_compare_exchange_n(&slot.claimedThreadedGeneration, &exactClaim,
1822 static_cast<size_t>(0),
false, __ATOMIC_ACQ_REL,
1824 pending = exactPending;
1827 unpublishDispatch(dispatchCleanup,
true);
1829 thread->disarmAtomicStateCleanup(dispatchCleanup.cleanup);
1837 static_cast<IrqHandler*
>(handler)->irq(callbackId ? callbackId : irq);
1838 if (disposition == IrqDisposition::Handled) {
1839 result.handled =
true;
1840 result.allowRearm =
true;
1842 result.allowRearm =
true;
1844 unpublishDispatch(dispatchCleanup,
true);
1846 thread->disarmAtomicStateCleanup(dispatchCleanup.cleanup);
1849 if (!acquireFinalizationGate(slot,
true)) {
1850 finishAdmissionCutoffCleanup(cutoffCleanup);
1851 FATAL(
"IRQ action finalization gate could not be acquired.");
1855 size_t finalPublication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
1856#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1857 HandlerHazardHook finalizationHook = __atomic_load_n(&m_HandlerHazardHook, __ATOMIC_ACQUIRE);
1858 if (finalizationHook) {
1859 finalizationHook(handler, HandlerHazardStage::BeforeClaimFinalization);
1862 if (generationOf(finalPublication) == generationOf(publication) &&
1863 irqOf(finalPublication) == irq && deliveryOf(finalPublication) == Delivery::Threaded &&
1864 modeOf(finalPublication) == SlotMode::Draining &&
1865 __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) == handler) {
1866 size_t expectedPublication = finalPublication;
1868 __atomic_compare_exchange_n(&slot.publication, &expectedPublication, publication,
false,
1869 __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
1871 finalPublication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
1873 if (finalPublication == publication) {
1874 size_t exactClaim = claimedGeneration;
1875 __atomic_compare_exchange_n(&slot.claimedThreadedGeneration, &exactClaim,
1876 static_cast<size_t>(0),
false, __ATOMIC_ACQ_REL,
1879 publishSlotQuiesced(slot, irq, claimedGeneration, QuiescedLane::Callback);
1880 size_t exactClaim = claimedGeneration;
1881 __atomic_compare_exchange_n(&slot.claimedThreadedGeneration, &exactClaim,
1882 static_cast<size_t>(0),
false, __ATOMIC_ACQ_REL,
1885 releaseFinalizationGate(slot);
1888 bool admissionResolving =
false;
1889 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
1891 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
1892 if (irqOf(publication) != irq || deliveryOf(publication) != Delivery::Threaded) {
1896 size_t pending = __atomic_load_n(&slot.pendingThreadedGeneration, __ATOMIC_ACQUIRE);
1897 size_t claimed = __atomic_load_n(&slot.claimedThreadedGeneration, __ATOMIC_ACQUIRE);
1898 const bool pendingReached = pending && generationReached(dispatchGeneration, pending);
1899 const bool claimedReached = claimed && generationReached(dispatchGeneration, claimed);
1900 if (!pendingReached && !claimedReached) {
1903 if (pendingReached && !threadedGenerationValid(irq, pending)) {
1904 size_t stale = pending;
1905 __atomic_compare_exchange_n(&slot.pendingThreadedGeneration, &stale,
static_cast<size_t>(0),
1906 false, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE);
1910 const SlotMode mode = modeOf(publication);
1911 if (mode == SlotMode::Draining || mode == SlotMode::Cancelling || mode == SlotMode::Closed ||
1912 mode == SlotMode::Retiring) {
1917 admissionResolving =
true;
1922 const size_t finalActionMutationWriters =
1923 __atomic_load_n(&m_ThreadedActionMutationWriters, __ATOMIC_SEQ_CST);
1924 const size_t finalActionMutationGeneration =
1925 __atomic_load_n(&m_ThreadedActionMutationGeneration, __ATOMIC_SEQ_CST);
1926 if (finalActionMutationWriters || finalActionMutationGeneration != actionMutationGeneration) {
1930 if (!admissionResolving && !candidateCount) {
1933 if (admissionResolving) {
1938 finishAdmissionCutoffCleanup(cutoffCleanup);
1942size_t IrqHandlerRegistry::handlerCount(uint8_t irq) {
1944 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
1945 const size_t publication = __atomic_load_n(&m_Handlers[i].publication, __ATOMIC_SEQ_CST);
1946 const SlotMode mode = modeOf(publication);
1947 if ((mode == SlotMode::Enabled || mode == SlotMode::Draining) && irqOf(publication) == irq) {
1958 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
1960 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
1961 if (modeOf(publication) == SlotMode::Enabled && irqOf(publication) == irq &&
1962 __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) == handler &&
1963 __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST) == publication) {
1971 bool threaded =
false;
1973 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
1974 const size_t publication = __atomic_load_n(&m_Handlers[i].publication, __ATOMIC_SEQ_CST);
1975 const SlotMode mode = modeOf(publication);
1976 if ((mode != SlotMode::Enabled && mode != SlotMode::Draining) || irqOf(publication) != irq) {
1980 if (deliveryOf(publication) == Delivery::Threaded) {
1985 if (threaded && hard) {
1986 return LineMode::Mixed;
1989 return threaded ? LineMode::Threaded : hard ? LineMode::HardOnly : LineMode::Empty;
1994 for (
size_t attempt = 0; attempt < LineSnapshotAttempts; ++attempt) {
1995 if (__atomic_load_n(&m_MutationWriters, __ATOMIC_SEQ_CST)) {
1998 const size_t generation = __atomic_load_n(&m_MutationGeneration, __ATOMIC_SEQ_CST);
1999 if (__atomic_load_n(&m_MutationWriters, __ATOMIC_SEQ_CST)) {
2004 size_t threadedPolicy = 0;
2005 size_t hardPolicy = 0;
2006 bool observedThreaded =
false;
2007 bool observedHard =
false;
2008 bool consistent =
true;
2009 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
2011 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
2012 if (modeOf(publication) != SlotMode::Enabled || irqOf(publication) != irq) {
2016 const size_t policy = __atomic_load_n(&slot.policy, __ATOMIC_ACQUIRE);
2017 if (__atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST) != publication) {
2022 if (deliveryOf(publication) == Delivery::Threaded) {
2023 if (observedThreaded && threadedPolicy != policy) {
2027 observedThreaded =
true;
2028 threadedPolicy = policy;
2030 if (observedHard && hardPolicy != policy) {
2034 observedHard =
true;
2035 hardPolicy = policy;
2037 ++observed.handlerCount;
2040 size_t observedPolicy = 0;
2041 if (consistent && observedThreaded && observedHard) {
2042 consistent = mixedPoliciesCompatible(hardPolicy, threadedPolicy);
2043 observed.mode = LineMode::Mixed;
2044 observedPolicy = effectiveMixedPolicy(hardPolicy, threadedPolicy);
2045 }
else if (consistent && observedThreaded) {
2046 observed.mode = LineMode::Threaded;
2047 observedPolicy = threadedPolicy;
2048 }
else if (consistent && observedHard) {
2049 observed.mode = LineMode::HardOnly;
2050 observedPolicy = hardPolicy;
2055 const size_t finalWriters = __atomic_load_n(&m_MutationWriters, __ATOMIC_SEQ_CST);
2056 const size_t finalGeneration = __atomic_load_n(&m_MutationGeneration, __ATOMIC_SEQ_CST);
2057 if (finalWriters || generation != finalGeneration) {
2064 observed.mutationGeneration = finalGeneration;
2065 decodePolicy(observedPolicy, observed);
2066 configuration = observed;
2075 exactGeneration = 0;
2076 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
2078 void* token = __atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE);
2083 const size_t generation = __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE);
2084 HandlerSlot* slot = __atomic_load_n(&dispatch.slot, __ATOMIC_SEQ_CST);
2085 const size_t publication = __atomic_load_n(&dispatch.admittedPublication, __ATOMIC_RELAXED);
2086 const size_t controllerGeneration =
2087 __atomic_load_n(&dispatch.controllerGeneration, __ATOMIC_RELAXED);
2088 if (slot && irqOf(publication) == irq && deliveryOf(publication) == Delivery::HardOnly &&
2089 __atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE) == token &&
2090 __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE) == generation &&
2091 __atomic_load_n(&dispatch.slot, __ATOMIC_SEQ_CST) == slot) {
2093 exactGeneration = count == 1 ? controllerGeneration : 0;
2100 uintptr_t& exactHandlerIdentity)
const {
2102 exactHandlerIdentity = 0;
2103 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
2105 void* token = __atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE);
2110 const size_t generation = __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE);
2111 HandlerSlot* slot = __atomic_load_n(&dispatch.slot, __ATOMIC_SEQ_CST);
2112 const size_t publication = __atomic_load_n(&dispatch.admittedPublication, __ATOMIC_RELAXED);
2113 IrqHandlerBase* handler = slot ? __atomic_load_n(&slot->handler, __ATOMIC_ACQUIRE) :
nullptr;
2114 if (slot && handler && irqOf(publication) == irq &&
2115 deliveryOf(publication) == Delivery::Threaded &&
2116 __atomic_load_n(&dispatch.callback, __ATOMIC_RELAXED) &&
2117 __atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE) == token &&
2118 __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE) == generation &&
2119 __atomic_load_n(&dispatch.slot, __ATOMIC_SEQ_CST) == slot) {
2121 exactHandlerIdentity = count == 1 ?
reinterpret_cast<uintptr_t
>(handler) : 0;
2127#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
2128void IrqHandlerRegistry::setHandlerPinHook(HandlerPinHook hook) {
2129 __atomic_store_n(&m_HandlerPinHook, hook, __ATOMIC_RELEASE);
2132void IrqHandlerRegistry::setHandlerPrePinHook(HandlerPrePinHook hook) {
2133 __atomic_store_n(&m_HandlerPrePinHook, hook, __ATOMIC_RELEASE);
2136void IrqHandlerRegistry::setHandlerHazardHook(HandlerHazardHook hook) {
2137 __atomic_store_n(&m_HandlerHazardHook, hook, __ATOMIC_RELEASE);
2140void IrqHandlerRegistry::setDispatchAbandonHook(DispatchAbandonHook hook) {
2141 __atomic_store_n(&m_DispatchAbandonHook, hook, __ATOMIC_RELEASE);
2144void IrqHandlerRegistry::setOccurrenceCaptureHookForTest(OccurrenceCaptureHook hook) {
2145 __atomic_store_n(&m_OccurrenceCaptureHook, hook, __ATOMIC_RELEASE);
2148void IrqHandlerRegistry::observeOccurrenceCaptureForTest(uint8_t irq, OccurrenceCaptureStage stage,
2149 size_t occurrenceEpoch) {
2150 OccurrenceCaptureHook hook = __atomic_load_n(&m_OccurrenceCaptureHook, __ATOMIC_ACQUIRE);
2152 hook(
this, irq, stage, occurrenceEpoch);
2156void IrqHandlerRegistry::withMutationLockForTest(MutationLockHook hook) {
2164void IrqHandlerRegistry::withMutationEpochForTest(MutationLockHook hook) {
2172size_t IrqHandlerRegistry::activeDispatchCountForTest(
IrqHandlerBase* handler) {
2174 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
2175 ActiveDispatch& dispatch = m_ActiveDispatches[i];
2176 void* token = __atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE);
2180 const size_t generation = __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE);
2181 HandlerSlot* slot = __atomic_load_n(&dispatch.slot, __ATOMIC_SEQ_CST);
2183 slot ? __atomic_load_n(&slot->handler, __ATOMIC_ACQUIRE) : nullptr;
2184 if (activeHandler == handler && __atomic_load_n(&dispatch.callback, __ATOMIC_RELAXED) &&
2185 __atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE) == token &&
2186 __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE) == generation) {
2193size_t IrqHandlerRegistry::claimedDispatchCountForOwnerForTest(
void* owner) {
2199 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
2200 ActiveDispatch& dispatch = m_ActiveDispatches[i];
2201 void* token = __atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE);
2206 const size_t generation = __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE);
2207 void* dispatchOwner = __atomic_load_n(&dispatch.owner, __ATOMIC_RELAXED);
2208 if (dispatchOwner == owner && __atomic_load_n(&dispatch.callback, __ATOMIC_RELAXED) &&
2209 __atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE) == token &&
2210 __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE) == generation) {
2217bool IrqHandlerRegistry::containsHandlerForTest(uint8_t irq,
IrqHandlerBase* handler) {
2218 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
2219 HandlerSlot& slot = m_Handlers[i];
2220 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
2221 if (modeOf(publication) != SlotMode::Empty && irqOf(publication) == irq &&
2222 __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) == handler &&
2223 __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST) == publication) {
2230size_t IrqHandlerRegistry::tombstoneCountForTest(uint8_t irq)
const {
2232 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
2233 const size_t publication = __atomic_load_n(&m_Handlers[i].publication, __ATOMIC_SEQ_CST);
2234 if (modeOf(publication) == SlotMode::Tombstone && irqOf(publication) == irq) {
2241size_t IrqHandlerRegistry::threadedActionMutationWriterCountForTest()
const {
2242 return __atomic_load_n(&m_ThreadedActionMutationWriters, __ATOMIC_SEQ_CST);
2245bool IrqHandlerRegistry::setThreadedActionLanesForTest(
IrqHandlerBase* handler,
2246 size_t pendingGeneration,
2247 size_t claimedGeneration,
2248 size_t quiescedGeneration) {
2249 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
2250 HandlerSlot& slot = m_Handlers[i];
2251 if (__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) != handler) {
2255 __atomic_store_n(&slot.pendingThreadedGeneration, pendingGeneration, __ATOMIC_RELEASE);
2256 __atomic_store_n(&slot.claimedThreadedGeneration, claimedGeneration, __ATOMIC_RELEASE);
2257 for (
size_t lane = 0; lane < QuiescedLaneCount; ++lane) {
2258 __atomic_store_n(&slot.quiescedThreadedGenerations[lane],
static_cast<size_t>(0),
2261 __atomic_store_n(quiescedLane(slot, QuiescedLane::Controller), quiescedGeneration,
2268bool IrqHandlerRegistry::consumeThreadedQuiescedForTest(
IrqHandlerBase* handler,
2269 size_t generation) {
2270 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
2271 HandlerSlot& slot = m_Handlers[i];
2272 if (__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) != handler) {
2276 for (
size_t lane = 0; lane < QuiescedLaneCount; ++lane) {
2277 size_t exact = generation;
2278 if (__atomic_compare_exchange_n(&slot.quiescedThreadedGenerations[lane], &exact,
2279 static_cast<size_t>(0),
false, __ATOMIC_ACQ_REL,
2280 __ATOMIC_ACQUIRE)) {
2289bool IrqHandlerRegistry::publishControllerQuiescedForTest(
IrqHandlerBase* handler, uint8_t irq,
2290 size_t generation) {
2291 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
2292 HandlerSlot& slot = m_Handlers[i];
2293 if (__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) != handler) {
2297 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 captureMixedAdmissionCutoffs(uint8_t irq, MixedAdmissionCutoffs &cutoffs)
size_t threadedDispatchState(uint8_t irq, uintptr_t &exactHandlerIdentity) const
bool containsHandler(uint8_t irq, IrqHandlerBase *handler) 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)
bool dispatchHard(uint8_t irq, InterruptState &state, HardIrqDisposition &disposition, HardIrqHandler *onlyHandler=nullptr, size_t dispatchGeneration=0, irq_id_t callbackId=0)
void invalidateThreadedLine(uint8_t irq, size_t throughGeneration)
size_t m_HardHandoffEpochs[GraceBucketCount]
bool dispatchThreaded(uint8_t irq, size_t dispatchGeneration, ThreadedDispatchResult &result, IrqHandler *onlyHandler=nullptr, irq_id_t callbackId=0)
bool hardLineQuarantined(uint8_t irq) const
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)