30 static constexpr size_t LineCount = 16;
31 static constexpr size_t CascadeIrq = 2;
33 enum class TriggerMode : uint8_t {
40 for (
size_t i = 0; i < LineCount; ++i) {
41 m_TriggerModes[i] = TriggerMode::Unconfigured;
42 m_ControllerAck[i] = IrqControllerAck::AfterHardStage;
43 m_HardHandlerCounts[i] = 0;
44 m_ThreadedHandlerCounts[i] = 0;
45 m_DispatchGenerations[i] = 0;
46 m_AcknowledgedGenerations[i] = 0;
47 m_AcknowledgementPending[i] =
false;
48 m_ThreadedPending[i] =
false;
49 m_TransitionPending[i] =
false;
50 m_RequestedEnabled[i] =
true;
51 m_SchedulerOwned[i] =
false;
52 m_PciRouteReserved[i] =
false;
53 m_IoApicReserved[i] =
false;
58 if (irq >= LineCount || irq == CascadeIrq || m_SchedulerOwned[irq] || m_IoApicReserved[irq] ||
59 (delivery != IrqDelivery::Hard && delivery != IrqDelivery::Threaded)) {
63 if ((delivery == IrqDelivery::Hard && !policy.validForHard()) ||
64 (delivery == IrqDelivery::Threaded && !policy.validForThreaded())) {
68 const TriggerMode requested =
69 policy.trigger() == IrqTrigger::Edge ? TriggerMode::Edge : TriggerMode::Level;
70 return m_TriggerModes[irq] == TriggerMode::Unconfigured ||
71 (m_TriggerModes[irq] == requested && m_ControllerAck[irq] == policy.controllerAck());
74 bool canRegister(
size_t irq,
const IrqPolicy& policy)
const {
75 return canRegister(irq, policy, legacyDelivery(policy));
78 bool canReservePciRoute(
size_t irq)
const {
79 return irq < LineCount && (PicElcrProgrammable & bit(irq)) &&
80 canRegister(irq, IrqPolicy::pciIntxThreaded(), IrqDelivery::Threaded);
85 assert(canReservePciRoute(irq));
86 m_PciRouteReserved[irq] =
true;
87 m_TriggerModes[irq] = TriggerMode::Level;
88 m_ControllerAck[irq] = IrqControllerAck::AfterHardStage;
89 if (!handlerCount(irq))
90 m_RequestedEnabled[irq] =
false;
94 bool pciRouteReserved(
size_t irq)
const {
95 assert(irq < LineCount);
96 return m_PciRouteReserved[irq];
99 bool canReserveIoApic(
size_t irq)
const {
100 return irq < LineCount && (PicElcrProgrammable & bit(irq)) && !handlerCount(irq) &&
101 !m_SchedulerOwned[irq] && !m_AcknowledgementPending[irq] && !m_ThreadedPending[irq];
106 assert(canReserveIoApic(irq));
107 m_IoApicReserved[irq] =
true;
108 m_RequestedEnabled[irq] =
false;
112 bool canRegisterScheduler(
size_t irq,
const IrqPolicy& policy)
const {
113 return irq == 0 && !m_SchedulerOwned[irq] && !handlerCount(irq) &&
114 policy == IrqPolicy::edgeHard();
117 void schedulerRegistered(
size_t irq,
const IrqPolicy& policy) {
118 assert(canRegisterScheduler(irq, policy));
119 handlerRegistered(irq, policy, IrqDelivery::Hard);
120 m_SchedulerOwned[irq] =
true;
123 void schedulerUnregistered(
size_t irq) {
124 assert(irq < LineCount && m_SchedulerOwned[irq]);
125 m_SchedulerOwned[irq] =
false;
126 handlerUnregistered(irq, IrqDelivery::Hard);
129 bool schedulerRegistered(
size_t irq)
const {
130 assert(irq < LineCount);
131 return m_SchedulerOwned[irq];
135 assert(canRegister(irq, policy, delivery));
136 const bool firstHandler = handlerCount(irq) == 0;
137 if (m_TriggerModes[irq] == TriggerMode::Unconfigured) {
138 m_TriggerModes[irq] =
139 policy.trigger() == IrqTrigger::Edge ? TriggerMode::Edge : TriggerMode::Level;
140 m_ControllerAck[irq] = policy.controllerAck();
142 if (delivery == IrqDelivery::Hard) {
143 ++m_HardHandlerCounts[irq];
145 ++m_ThreadedHandlerCounts[irq];
148 m_AcknowledgementPending[irq] =
false;
149 m_ThreadedPending[irq] =
false;
150 m_AcknowledgedGenerations[irq] = m_DispatchGenerations[irq];
151 m_RequestedEnabled[irq] =
true;
156 void handlerRegistered(
size_t irq,
const IrqPolicy& policy) {
157 handlerRegistered(irq, policy, legacyDelivery(policy));
160 void handlerUnregistered(
size_t irq,
IrqDelivery delivery) {
161 assert(irq < LineCount);
162 assert(delivery == IrqDelivery::Hard || delivery == IrqDelivery::Threaded);
163 if (delivery == IrqDelivery::Hard) {
164 assert(m_HardHandlerCounts[irq]);
165 --m_HardHandlerCounts[irq];
167 assert(m_ThreadedHandlerCounts[irq]);
168 --m_ThreadedHandlerCounts[irq];
169 if (!m_ThreadedHandlerCounts[irq] && m_ThreadedPending[irq]) {
170 m_ThreadedPending[irq] =
false;
175 if (!handlerCount(irq)) {
176 m_AcknowledgementPending[irq] =
false;
177 m_ThreadedPending[irq] =
false;
178 m_AcknowledgedGenerations[irq] = m_DispatchGenerations[irq];
179 m_RequestedEnabled[irq] =
false;
180 m_TriggerModes[irq] =
181 m_PciRouteReserved[irq] ? TriggerMode::Level : TriggerMode::Unconfigured;
182 m_ControllerAck[irq] = IrqControllerAck::AfterHardStage;
187 void handlerUnregistered(
size_t irq) {
188 assert(irq < LineCount);
189 assert(!m_HardHandlerCounts[irq] || !m_ThreadedHandlerCounts[irq]);
190 handlerUnregistered(irq,
191 m_ThreadedHandlerCounts[irq] ? IrqDelivery::Threaded :
IrqDelivery::Hard);
194 size_t beginDispatch(
size_t irq) {
195 assert(irq < LineCount);
196 size_t generation = ++m_DispatchGenerations[irq];
198 generation = ++m_DispatchGenerations[irq];
209 assert(irq < LineCount);
210 if (!needsAcknowledgement || !handlerCount(irq) ||
211 generationReached(m_AcknowledgedGenerations[irq], dispatchGeneration)) {
215 m_AcknowledgementPending[irq] =
true;
219 bool acknowledge(
size_t irq) {
220 assert(irq < LineCount);
221 if (!handlerCount(irq)) {
225 m_AcknowledgedGenerations[irq] = m_DispatchGenerations[irq];
226 if (m_AcknowledgementPending[irq]) {
227 m_AcknowledgementPending[irq] =
false;
235 assert(irq < LineCount);
236 if (lineRelease(irq) == IrqLineRelease::AfterThreadedCompletion) {
237 m_ThreadedPending[irq] =
true;
247 assert(irq < LineCount);
248 if (m_DispatchGenerations[irq] != dispatchGeneration) {
252 if (lineRelease(irq) == IrqLineRelease::AfterThreadedCompletion &&
253 (allowRearm || !handlerCount(irq))) {
254 m_ThreadedPending[irq] =
false;
260 bool threadedPending(
size_t irq)
const {
261 assert(irq < LineCount);
262 return m_ThreadedPending[irq];
265 bool acknowledgementPending(
size_t irq)
const {
266 assert(irq < LineCount);
267 return m_AcknowledgementPending[irq];
270 void beginLineTransition(
size_t irq) {
271 assert(irq < LineCount);
272 m_TransitionPending[irq] =
true;
276 void finishLineTransition(
size_t irq) {
277 assert(irq < LineCount);
278 m_TransitionPending[irq] =
false;
282 bool lineTransitionPending(
size_t irq)
const {
283 assert(irq < LineCount);
284 return m_TransitionPending[irq];
287 size_t handlerCount(
size_t irq)
const {
288 assert(irq < LineCount);
289 return m_HardHandlerCounts[irq] + m_ThreadedHandlerCounts[irq];
292 size_t hardHandlerCount(
size_t irq)
const {
293 assert(irq < LineCount);
294 return m_HardHandlerCounts[irq];
297 size_t threadedHandlerCount(
size_t irq)
const {
298 assert(irq < LineCount);
299 return m_ThreadedHandlerCounts[irq];
303 assert(irq < LineCount);
304 const bool hard = m_HardHandlerCounts[irq] != 0;
305 const bool threaded = m_ThreadedHandlerCounts[irq] != 0;
306 if (hard && threaded) {
307 return IrqDelivery::Mixed;
310 return IrqDelivery::Hard;
313 return IrqDelivery::Threaded;
315 return IrqDelivery::None;
318 bool edgeTriggered(
size_t irq)
const {
319 assert(irq < LineCount);
320 return m_TriggerModes[irq] == TriggerMode::Edge;
324 assert(irq < LineCount);
325 return m_TriggerModes[irq] == TriggerMode::Edge ? IrqTrigger::Edge : IrqTrigger::Level;
329 assert(irq < LineCount);
330 return m_ControllerAck[irq];
334 assert(irq < LineCount);
335 return m_TriggerModes[irq] == TriggerMode::Level && m_ThreadedHandlerCounts[irq]
336 ? IrqLineRelease::AfterThreadedCompletion
337 : IrqLineRelease::AfterHardStage;
340 bool enabled(
size_t irq)
const {
341 assert(irq < LineCount);
342 return (m_Mask & bit(irq)) == 0;
345 bool requestedEnabled(
size_t irq)
const {
346 assert(irq < LineCount);
347 return m_RequestedEnabled[irq];
350 size_t dispatchGeneration(
size_t irq)
const {
351 assert(irq < LineCount);
352 return m_DispatchGenerations[irq];
355 size_t acknowledgedGeneration(
size_t irq)
const {
356 assert(irq < LineCount);
357 return m_AcknowledgedGenerations[irq];
360 void setEnabled(
size_t irq,
bool enabled) {
361 assert(irq < LineCount);
362 m_RequestedEnabled[irq] = enabled;
366 void setAllEnabled(
bool enabled) {
367 for (
size_t i = 0; i < LineCount; ++i) {
368 m_RequestedEnabled[i] = enabled;
374 m_RequestedEnabled[2] =
true;
379 uint16_t mask()
const {
383 uint8_t masterMask()
const {
384 return static_cast<uint8_t
>(m_Mask & 0xFF);
387 uint8_t slaveMask()
const {
388 return static_cast<uint8_t
>(m_Mask >> 8);
393 if (policy.lineRelease() == IrqLineRelease::AfterThreadedCompletion ||
394 (policy.trigger() == IrqTrigger::Edge &&
395 policy.controllerAck() == IrqControllerAck::AfterHardStage)) {
396 return IrqDelivery::Threaded;
398 return IrqDelivery::Hard;
401 static uint16_t bit(
size_t irq) {
402 return static_cast<uint16_t
>(
static_cast<uint16_t
>(1U) << irq);
405 static bool generationReached(
size_t current,
size_t target) {
406 return static_cast<intptr_t
>(current - target) >= 0;
411 for (
size_t i = 0; i < LineCount; ++i) {
412 if (!m_RequestedEnabled[i] || m_AcknowledgementPending[i] || m_ThreadedPending[i] ||
413 m_TransitionPending[i] || m_IoApicReserved[i] ||
414 (m_PciRouteReserved[i] && !handlerCount(i))) {
421 if ((mask &
static_cast<uint16_t
>(0xFF00)) !=
static_cast<uint16_t
>(0xFF00)) {
422 mask &=
static_cast<uint16_t
>(~bit(2));
428 TriggerMode m_TriggerModes[LineCount];
430 size_t m_HardHandlerCounts[LineCount];
431 size_t m_ThreadedHandlerCounts[LineCount];
432 size_t m_DispatchGenerations[LineCount];
433 size_t m_AcknowledgedGenerations[LineCount];
434 bool m_AcknowledgementPending[LineCount];
435 bool m_ThreadedPending[LineCount];
436 bool m_TransitionPending[LineCount];
437 bool m_RequestedEnabled[LineCount];
438 bool m_SchedulerOwned[LineCount];
439 bool m_PciRouteReserved[LineCount];
440 bool m_IoApicReserved[LineCount];
507 static constexpr size_t LineCount = PicIrqState::LineCount;
508 static constexpr size_t CountBits = 12;
509 static constexpr size_t CountMask = (
static_cast<size_t>(1) << CountBits) - 1;
510 static constexpr size_t LifetimeMask =
static_cast<size_t>(~static_cast<size_t>(0)) >> CountBits;
511 static constexpr size_t TransitionLifetime = LifetimeMask;
513 static_assert(
sizeof(size_t) >=
sizeof(uint64_t),
514 "PIC lifetime-qualified handoff requires lock-free 64-bit words");
515 static_assert(__atomic_always_lock_free(
sizeof(
size_t),
nullptr),
516 "PIC controller handoff words must be lock-free");
524 for (
size_t i = 0; i < LineCount; ++i) {
535 for (
size_t i = 0; i < LineCount; ++i) {
536 if (entry[i] || tail[i] || tailEoi[i] || staleEntry[i] || staleTail[i] || staleTailEoi[i]) {
543 size_t entry[LineCount];
544 size_t tail[LineCount];
545 size_t tailEoi[LineCount];
546 size_t staleEntry[LineCount];
547 size_t staleTail[LineCount];
548 size_t staleTailEoi[LineCount];
552 for (
size_t i = 0; i < LineCount; ++i) {
553 m_LifetimeGenerations[i] = 1;
554 m_LineLifetimes[i] = 1;
555 m_EntryCounts[i] = tagged(1, 0);
556 m_TailNoEoiCounts[i] = tagged(1, 0);
557 m_TailEoiCounts[i] = tagged(1, 0);
558 m_StaleEntryCounts[i] = 0;
559 m_StaleTailNoEoiCounts[i] = 0;
560 m_StaleTailEoiCounts[i] = 0;
566 size_t expected = Clean;
567 return __atomic_compare_exchange_n(&m_State, &expected, Owner,
false, __ATOMIC_ACQUIRE,
573 const size_t state = __atomic_load_n(&m_State, __ATOMIC_ACQUIRE);
578 size_t expected = state;
579 return __atomic_compare_exchange_n(&m_State, &expected, state | Owner,
false, __ATOMIC_ACQ_REL,
583 size_t currentLifetime(
size_t irq)
const {
584 assert(irq < LineCount);
585 return __atomic_load_n(&m_LineLifetimes[irq], __ATOMIC_ACQUIRE);
590 assert(irq < LineCount);
591 replaceLifetime(irq, TransitionLifetime);
596 assert(irq < LineCount);
597 size_t lifetime = ++m_LifetimeGenerations[irq];
598 lifetime &= LifetimeMask;
599 if (!lifetime || lifetime == TransitionLifetime) {
601 m_LifetimeGenerations[irq] = lifetime;
603 replaceLifetime(irq, lifetime);
609 assert(irq < LineCount);
610 queueTagged(m_EntryCounts[irq], m_StaleEntryCounts[irq], lifetime);
611 publishPending(irq == 0);
616 bool queueTail(
size_t irq,
size_t lifetime,
bool owesEoi) {
617 assert(irq < LineCount);
619 queueTagged(m_TailEoiCounts[irq], m_StaleTailEoiCounts[irq], lifetime);
621 queueTagged(m_TailNoEoiCounts[irq], m_StaleTailNoEoiCounts[irq], lifetime);
623 publishPending(irq == 0);
629 assert(irq < LineCount);
630 publishPending(irq == 0);
637 size_t expected = __atomic_load_n(&m_State, __ATOMIC_ACQUIRE);
639 if ((expected & (Owner | Pending)) != (Owner | Pending)) {
642 }
while (!__atomic_compare_exchange_n(&m_State, &expected, Owner,
false, __ATOMIC_ACQ_REL,
645 for (
size_t i = 0; i < LineCount; ++i) {
646 const size_t lifetime = currentLifetime(i);
647 takeTagged(m_EntryCounts[i], m_StaleEntryCounts[i], lifetime, actions.entry[i],
648 actions.staleEntry[i]);
650 size_t tailNoEoi = 0;
651 size_t staleTailNoEoi = 0;
652 takeTagged(m_TailNoEoiCounts[i], m_StaleTailNoEoiCounts[i], lifetime, tailNoEoi,
654 takeTagged(m_TailEoiCounts[i], m_StaleTailEoiCounts[i], lifetime, actions.tailEoi[i],
655 actions.staleTailEoi[i]);
656 actions.tail[i] = tailNoEoi + actions.tailEoi[i];
657 actions.staleTail[i] = staleTailNoEoi + actions.staleTailEoi[i];
664 size_t expected = Owner;
665 return __atomic_compare_exchange_n(&m_State, &expected, Clean,
false, __ATOMIC_RELEASE,
669 bool hasPending()
const {
670 return (__atomic_load_n(&m_State, __ATOMIC_ACQUIRE) & Pending) != 0;
673 bool urgentPending()
const {
674 return (__atomic_load_n(&m_State, __ATOMIC_ACQUIRE) & Urgent) != 0;
679 publishPending(
false);
684 size_t expected = Owner | Pending;
685 return __atomic_compare_exchange_n(&m_State, &expected, Pending,
false, __ATOMIC_RELEASE,
690 static constexpr size_t Clean = 0;
691 static constexpr size_t Owner = 1U << 0;
692 static constexpr size_t Pending = 1U << 1;
693 static constexpr size_t Urgent = 1U << 2;
695 static size_t tagged(
size_t lifetime,
size_t count) {
696 return ((lifetime & LifetimeMask) << CountBits) | (count & CountMask);
699 static size_t taggedLifetime(
size_t value) {
700 return value >> CountBits;
703 static size_t taggedCount(
size_t value) {
704 return value & CountMask;
707 static void addStale(
size_t& counter,
size_t count) {
709 __atomic_fetch_add(&counter, count, __ATOMIC_RELAXED);
713 static void queueTagged(
size_t& currentCounter,
size_t& staleCounter,
size_t lifetime) {
714 const size_t qualifiedLifetime = lifetime & LifetimeMask;
715 if (!qualifiedLifetime || qualifiedLifetime == TransitionLifetime) {
716 addStale(staleCounter, 1);
720 size_t expected = __atomic_load_n(¤tCounter, __ATOMIC_ACQUIRE);
721 const size_t count = taggedCount(expected);
722 if (taggedLifetime(expected) != qualifiedLifetime || count == CountMask ||
723 !__atomic_compare_exchange_n(¤tCounter, &expected,
724 tagged(qualifiedLifetime, count + 1),
false, __ATOMIC_ACQ_REL,
729 addStale(staleCounter, 1);
733 static void takeTagged(
size_t& currentCounter,
size_t& staleCounter,
size_t lifetime,
734 size_t& current,
size_t& stale) {
736 __atomic_exchange_n(¤tCounter, tagged(lifetime, 0), __ATOMIC_ACQ_REL);
737 const size_t count = taggedCount(value);
738 if (taggedLifetime(value) == lifetime) {
743 stale += __atomic_exchange_n(&staleCounter,
static_cast<size_t>(0), __ATOMIC_ACQ_REL);
746 void replaceLifetime(
size_t irq,
size_t lifetime) {
748 size_t value = __atomic_exchange_n(&m_EntryCounts[irq], tagged(lifetime, 0), __ATOMIC_ACQ_REL);
749 moved += taggedCount(value);
750 addStale(m_StaleEntryCounts[irq], taggedCount(value));
752 value = __atomic_exchange_n(&m_TailNoEoiCounts[irq], tagged(lifetime, 0), __ATOMIC_ACQ_REL);
753 moved += taggedCount(value);
754 addStale(m_StaleTailNoEoiCounts[irq], taggedCount(value));
756 value = __atomic_exchange_n(&m_TailEoiCounts[irq], tagged(lifetime, 0), __ATOMIC_ACQ_REL);
757 moved += taggedCount(value);
758 addStale(m_StaleTailEoiCounts[irq], taggedCount(value));
760 __atomic_store_n(&m_LineLifetimes[irq], lifetime, __ATOMIC_RELEASE);
762 publishPending(irq == 0);
766 void publishPending(
bool urgent) {
767 __atomic_fetch_or(&m_State, Pending | (urgent ? Urgent : 0), __ATOMIC_RELEASE);
771 size_t m_LifetimeGenerations[LineCount];
772 size_t m_LineLifetimes[LineCount];
773 size_t m_EntryCounts[LineCount];
774 size_t m_TailNoEoiCounts[LineCount];
775 size_t m_TailEoiCounts[LineCount];
776 size_t m_StaleEntryCounts[LineCount];
777 size_t m_StaleTailNoEoiCounts[LineCount];
778 size_t m_StaleTailEoiCounts[LineCount];