8#include "pedigree/kernel/LockGuard.h"
9#include "pedigree/kernel/Log.h"
10#include "pedigree/kernel/machine/Device.h"
11#include "pedigree/kernel/machine/IrqManager.h"
12#include "pedigree/kernel/machine/SplitIrqHandler.h"
13#include "pedigree/kernel/process/TerminationDeferral.h"
14#include "pedigree/kernel/process/Thread.h"
15#include "pedigree/kernel/processor/Processor.h"
16#include "pedigree/kernel/processor/ProcessorInformation.h"
17#include "pedigree/kernel/utilities/String.h"
20class SplitLifecycleGuard {
23 : m_Busy(busy), m_Owned(m_Busy.compareAndSwap(0, 1)) {}
25 ~SplitLifecycleGuard() {
41SplitIrqHandler::SplitIrqHandler(
const String& name)
44 m_RegistrationCount(0),
46 m_AcceptingRegistrations(0),
47 m_Dispatcher(name, 1, dispatchThreaded, this),
51 m_PublicationFailures(0),
53 m_CompletedBatches(0),
56#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
58 m_RegistrationPublishedHook(nullptr)
63SplitIrqHandler::~SplitIrqHandler() {
64 if (m_Started || m_RegistrationCount || m_LifecycleBusy || m_AcceptingRegistrations ||
65 m_Dispatcher.isInitialised() || __atomic_load_n(&m_PendingWork, __ATOMIC_ACQUIRE)) {
67 "A split IRQ handler reached its base destructor while "
68 "active; the most-derived destructor must disable its device "
69 "source and call shutdownSplitIrq().");
76 SplitLifecycleGuard lifecycle(m_LifecycleBusy);
77 if (!lifecycle.owned()) {
80 if (m_Started || m_RegistrationCount) {
84 __atomic_store_n(&m_PendingWork,
static_cast<size_t>(0), __ATOMIC_RELEASE);
85 m_PublicationFailures = 0;
87 m_CompletedBatches = 0;
98 m_AcceptingRegistrations = 1;
108 SplitLifecycleGuard lifecycle(m_LifecycleBusy);
109 if (!lifecycle.owned() || !m_AcceptingRegistrations || !m_Started || m_Stopping ||
110 m_RegistrationCount >= MaxRegistrations) {
119#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
120 if (m_RegistrationPublishedHook) {
121 m_RegistrationPublishedHook(
this);
125 Registration& registration = m_Registrations[m_RegistrationCount++];
126 registration.manager = &manager;
127 registration.id = id;
131irq_id_t SplitIrqHandler::registerPciSplitIrq(
IrqManager& manager,
Device& device,
134 SplitLifecycleGuard lifecycle(m_LifecycleBusy);
135 if (!lifecycle.owned() || !m_AcceptingRegistrations || !m_Started || m_Stopping ||
136 m_RegistrationCount >= MaxRegistrations) {
145#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
146 if (m_RegistrationPublishedHook) {
147 m_RegistrationPublishedHook(
this);
151 Registration& registration = m_Registrations[m_RegistrationCount++];
152 registration.manager = &manager;
153 registration.id = id;
164 if (current->getHostedSignalDepth()) {
173 SplitLifecycleGuard lifecycle(m_LifecycleBusy);
174 if (!lifecycle.owned()) {
179 return m_RegistrationCount == 0 && !m_AcceptingRegistrations && !m_Dispatcher.isInitialised() &&
180 !__atomic_load_n(&m_PendingWork, __ATOMIC_ACQUIRE);
189 m_AcceptingRegistrations = 0;
205 while (m_RegistrationCount) {
206 Registration& registration = m_Registrations[m_RegistrationCount - 1];
207 if (!registration.manager || !registration.id ||
214 registration.manager =
nullptr;
216 --m_RegistrationCount;
227 if (__atomic_load_n(&m_PendingWork, __ATOMIC_ACQUIRE)) {
238 if (__atomic_load_n(&m_PendingWork, __ATOMIC_ACQUIRE)) {
248 const HardStageDisposition disposition =
hardIrq(number, state, work);
249 if (disposition == HardStageDisposition::NotHandled) {
250 return HardIrqDisposition::NotHandled;
252 if (disposition == HardStageDisposition::Deferred) {
253 return publishWork(work ? work : 1) ? HardIrqDisposition::Handled
256 return HardIrqDisposition::Handled;
259void SplitIrqHandler::dispatchThreaded(
void* context, uint8_t,
size_t) {
263void SplitIrqHandler::dispatchThreaded() {
264 const size_t work = __atomic_exchange_n(&m_PendingWork,
static_cast<size_t>(0), __ATOMIC_ACQ_REL);
273 m_CompletedBatches += 1;
277bool SplitIrqHandler::publishWork(
size_t work) {
278 __atomic_or_fetch(&m_PendingWork, work, __ATOMIC_ACQ_REL);
281 m_PublicationFailures += 1;
287#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
292void SplitIrqHandler::setRegistrationPublishedHookForTest(RegistrationPublishedHook hook) {
293 m_RegistrationPublishedHook = hook;
296size_t SplitIrqHandler::publicationFailuresForTest()
const {
297 return m_PublicationFailures;
300size_t SplitIrqHandler::pendingWorkForTest()
const {
301 return __atomic_load_n(&m_PendingWork, __ATOMIC_ACQUIRE);
304size_t SplitIrqHandler::deferredIrqsForTest()
const {
305 return m_DeferredIrqs;
308size_t SplitIrqHandler::completedBatchesForTest()
const {
309 return m_CompletedBatches;
312bool SplitIrqHandler::publishWorkForTest(
size_t work) {
313 return publishWork(work);
316void SplitIrqHandler::rejectNextPublicationForTest() {
317 m_Dispatcher.rejectNextPublicationForTest();
virtual irq_id_t registerHardPciIrqHandler(HardIrqHandler *handler, Device *pDevice, const IrqPolicy &policy)=0
virtual bool unregisterHandler(irq_id_t Id, IrqHandlerBase *handler)=0
virtual irq_id_t registerHardIsaIrqHandler(uint8_t irq, HardIrqHandler *handler, const IrqPolicy &policy)=0
static bool getInterrupts()
static ProcessorInformation & information()
virtual void rearmIrqSources(size_t work)=0
virtual bool quiesceIrqSources()=0
::HardIrqDisposition irq(irq_id_t number, InterruptState &state) final
virtual HardStageDisposition hardIrq(irq_id_t number, InterruptState &state, size_t &work)=0
irq_id_t registerIsaSplitIrq(IrqManager &manager, uint8_t irq, const IrqPolicy &policy)
virtual void threadedIrq(size_t work)=0
bool initialiseSplitIrq()
bool isCurrentWorker() const
bool publishFromInterrupt(uint8_t line, size_t cookie)