Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

677 lines
18 KiB

  1. /*****************************************************************************
  2. * sync.cpp - synchronization
  3. *****************************************************************************
  4. * Copyright (c) 1997-2000 Microsoft Corporation. All rights reserved.
  5. */
  6. #include "private.h"
  7. /*****************************************************************************
  8. * IInterruptSyncInit
  9. *****************************************************************************
  10. * Interface for interrupt synchronizer with Init.
  11. */
  12. DECLARE_INTERFACE_(IInterruptSyncInit,IInterruptSync)
  13. {
  14. DEFINE_ABSTRACT_UNKNOWN() // For IUnknown
  15. // For IInterruptSync
  16. STDMETHOD_(NTSTATUS,CallSynchronizedRoutine)
  17. ( THIS_
  18. IN PINTERRUPTSYNCROUTINE Routine,
  19. IN PVOID DynamicContext
  20. ) PURE;
  21. STDMETHOD_(PKINTERRUPT,GetKInterrupt)
  22. ( THIS
  23. ) PURE;
  24. STDMETHOD_(NTSTATUS,Connect)
  25. ( THIS
  26. ) PURE;
  27. STDMETHOD_(void,Disconnect)
  28. ( THIS
  29. ) PURE;
  30. STDMETHOD_(NTSTATUS,RegisterServiceRoutine)
  31. ( THIS_
  32. IN PINTERRUPTSYNCROUTINE Routine,
  33. IN PVOID DynamicContext,
  34. IN BOOLEAN First
  35. ) PURE;
  36. // For IInterruptSyncInit
  37. STDMETHOD_(NTSTATUS,Init)
  38. ( THIS_
  39. IN PRESOURCELIST ResourceList,
  40. IN ULONG ResourceIndex,
  41. IN INTERRUPTSYNCMODE Mode
  42. ) PURE;
  43. };
  44. typedef IInterruptSyncInit *PINTERRUPTSYNCINIT;
  45. /*****************************************************************************
  46. * CInterruptSync
  47. *****************************************************************************
  48. * Interrupt synchronizer implementation.
  49. */
  50. class CInterruptSync
  51. : public IInterruptSyncInit,
  52. public CUnknown
  53. {
  54. private:
  55. CM_PARTIAL_RESOURCE_DESCRIPTOR m_descriptor;
  56. INTERRUPTSYNCMODE m_mode;
  57. PKINTERRUPT m_pKInterrupt;
  58. LIST_ENTRY m_listEntry;
  59. KSPIN_LOCK m_kSpinLock;
  60. KIRQL m_kIrql;
  61. public:
  62. DECLARE_STD_UNKNOWN();
  63. CInterruptSync(PUNKNOWN pUnknownOuter);
  64. ~CInterruptSync();
  65. STDMETHODIMP_(NTSTATUS) Init
  66. (
  67. IN PRESOURCELIST ResourceList,
  68. IN ULONG ResourceIndex,
  69. IN INTERRUPTSYNCMODE Mode
  70. );
  71. IMP_IInterruptSync;
  72. friend
  73. BOOLEAN
  74. CInterruptSyncServiceRoutine
  75. (
  76. IN PKINTERRUPT Interrupt,
  77. IN PVOID PVoidContext
  78. );
  79. friend
  80. BOOLEAN
  81. CInterruptSyncWrapperRoutine
  82. (
  83. IN PVOID PVoidContext
  84. );
  85. };
  86. /*****************************************************************************
  87. * ISRLISTENTRY
  88. *****************************************************************************
  89. * Entry in the list of ISRs.
  90. */
  91. typedef struct
  92. {
  93. LIST_ENTRY ListEntry;
  94. PINTERRUPTSYNCROUTINE Routine;
  95. PVOID DynamicContext;
  96. }
  97. ISRLISTENTRY, *PISRLISTENTRY;
  98. /*****************************************************************************
  99. * WRAPPERROUTINECONTEXT
  100. *****************************************************************************
  101. * Context for synchronized routine wrapper function.
  102. */
  103. typedef struct
  104. {
  105. PINTERRUPTSYNCROUTINE Routine;
  106. PVOID DynamicContext;
  107. CInterruptSync * InterruptSync;
  108. NTSTATUS NtStatus;
  109. }
  110. WRAPPERROUTINECONTEXT, *PWRAPPERROUTINECONTEXT;
  111. /*****************************************************************************
  112. * Factory
  113. */
  114. #pragma code_seg("PAGE")
  115. /*****************************************************************************
  116. * CreateInterruptSync()
  117. *****************************************************************************
  118. * Creates an interrupt synchronization object.
  119. */
  120. NTSTATUS
  121. CreateInterruptSync
  122. (
  123. OUT PUNKNOWN * Unknown,
  124. IN REFCLSID,
  125. IN PUNKNOWN UnknownOuter OPTIONAL,
  126. IN POOL_TYPE PoolType
  127. )
  128. {
  129. PAGED_CODE();
  130. ASSERT(Unknown);
  131. _DbgPrintF(DEBUGLVL_LIFETIME,("Creating INTERRUPTSYNC"));
  132. STD_CREATE_BODY_
  133. (
  134. CInterruptSync,
  135. Unknown,
  136. UnknownOuter,
  137. PoolType,
  138. PINTERRUPTSYNC
  139. );
  140. }
  141. /*****************************************************************************
  142. * PcNewInterruptSync()
  143. *****************************************************************************
  144. * Creates and initializes an interrupt-level synchronization object.
  145. */
  146. PORTCLASSAPI
  147. NTSTATUS
  148. NTAPI
  149. PcNewInterruptSync
  150. (
  151. OUT PINTERRUPTSYNC * OutInterruptSync,
  152. IN PUNKNOWN OuterUnknown OPTIONAL,
  153. IN PRESOURCELIST ResourceList,
  154. IN ULONG ResourceIndex,
  155. IN INTERRUPTSYNCMODE Mode
  156. )
  157. {
  158. PAGED_CODE();
  159. ASSERT(OutInterruptSync);
  160. ASSERT(ResourceList);
  161. //
  162. // Invalidate Parameters.
  163. //
  164. if (NULL == OutInterruptSync ||
  165. NULL == ResourceList)
  166. {
  167. _DbgPrintF(DEBUGLVL_TERSE, ("PcInterruptSync : Invalid Parameter"));
  168. return STATUS_INVALID_PARAMETER;
  169. }
  170. PUNKNOWN unknown;
  171. NTSTATUS ntStatus = CreateInterruptSync( &unknown,
  172. GUID_NULL,
  173. OuterUnknown,
  174. NonPagedPool );
  175. if (NT_SUCCESS(ntStatus))
  176. {
  177. PINTERRUPTSYNCINIT interruptSync;
  178. ntStatus = unknown->QueryInterface( IID_IInterruptSync,
  179. (PVOID *) &interruptSync );
  180. if (NT_SUCCESS(ntStatus))
  181. {
  182. ntStatus = interruptSync->Init( ResourceList,
  183. ResourceIndex,
  184. Mode );
  185. if(NT_SUCCESS(ntStatus))
  186. {
  187. *OutInterruptSync = interruptSync;
  188. }
  189. else
  190. {
  191. interruptSync->Release();
  192. }
  193. }
  194. unknown->Release();
  195. }
  196. return ntStatus;
  197. }
  198. #pragma code_seg("PAGE")
  199. /*****************************************************************************
  200. * CInterruptSync member functions
  201. */
  202. /*****************************************************************************
  203. * CInterruptSync::CInterruptSync()
  204. *****************************************************************************
  205. * Constructor.
  206. */
  207. CInterruptSync::
  208. CInterruptSync
  209. ( IN PUNKNOWN pUnknownOuter
  210. )
  211. : CUnknown(pUnknownOuter)
  212. {
  213. PAGED_CODE();
  214. KeInitializeSpinLock(&m_kSpinLock);
  215. InitializeListHead(&m_listEntry);
  216. }
  217. #pragma code_seg()
  218. /*****************************************************************************
  219. * CInterruptSync::~CInterruptSync()
  220. *****************************************************************************
  221. * Destructor.
  222. */
  223. CInterruptSync::~CInterruptSync()
  224. {
  225. _DbgPrintF(DEBUGLVL_LIFETIME,("Destroying INTERRUPTSYNC (0x%08x)",this));
  226. //
  227. // Make sure we're disconnected.
  228. //
  229. Disconnect();
  230. //
  231. // Delete the list of ISRs.
  232. //
  233. if (! IsListEmpty(&m_listEntry))
  234. {
  235. KIRQL kIrqlOld;
  236. KeAcquireSpinLock(&m_kSpinLock,&kIrqlOld);
  237. //
  238. // Get rid of any remaining members.
  239. //
  240. while (! IsListEmpty(&m_listEntry))
  241. {
  242. PLIST_ENTRY pListEntry = RemoveHeadList(&m_listEntry);
  243. delete PISRLISTENTRY(pListEntry);
  244. }
  245. KeReleaseSpinLock(&m_kSpinLock,kIrqlOld);
  246. }
  247. }
  248. #pragma code_seg("PAGE")
  249. /*****************************************************************************
  250. * CDmaChannel::NonDelegatingQueryInterface()
  251. *****************************************************************************
  252. * Obtains an interface.
  253. */
  254. STDMETHODIMP_(NTSTATUS)
  255. CInterruptSync::
  256. NonDelegatingQueryInterface
  257. (
  258. REFIID Interface,
  259. PVOID * Object
  260. )
  261. {
  262. PAGED_CODE();
  263. ASSERT(Object);
  264. if (IsEqualGUIDAligned(Interface,IID_IUnknown))
  265. {
  266. *Object = PVOID(PUNKNOWN(this));
  267. }
  268. else if (IsEqualGUIDAligned(Interface,IID_IInterruptSync))
  269. {
  270. *Object = PVOID(PINTERRUPTSYNCINIT(this));
  271. }
  272. else
  273. {
  274. *Object = NULL;
  275. }
  276. if (*Object)
  277. {
  278. PUNKNOWN(*Object)->AddRef();
  279. return STATUS_SUCCESS;
  280. }
  281. return STATUS_INVALID_PARAMETER;
  282. }
  283. /*****************************************************************************
  284. * CInterruptSync::Init()
  285. *****************************************************************************
  286. * Initializes the synchronization object.
  287. */
  288. STDMETHODIMP_(NTSTATUS)
  289. CInterruptSync::
  290. Init
  291. (
  292. IN PRESOURCELIST ResourceList,
  293. IN ULONG ResourceIndex,
  294. IN INTERRUPTSYNCMODE Mode
  295. )
  296. {
  297. PAGED_CODE();
  298. _DbgPrintF(DEBUGLVL_LIFETIME,("Initializing INTERRUPTSYNC (0x%08x)",this));
  299. ASSERT(ResourceList);
  300. m_mode = Mode;
  301. PCM_PARTIAL_RESOURCE_DESCRIPTOR pDescriptor = ResourceList->FindTranslatedInterrupt(ResourceIndex);
  302. NTSTATUS ntStatus = ( pDescriptor ? STATUS_SUCCESS : STATUS_INSUFFICIENT_RESOURCES );
  303. if (NT_SUCCESS(ntStatus))
  304. {
  305. m_descriptor = *pDescriptor;
  306. m_pKInterrupt = NULL;
  307. m_kIrql = KIRQL(m_descriptor.u.Interrupt.Level);
  308. }
  309. return ntStatus;
  310. }
  311. #pragma code_seg()
  312. /*****************************************************************************
  313. * CInterruptSyncServiceRoutine()
  314. *****************************************************************************
  315. * Wrapper for service routine.
  316. */
  317. static
  318. BOOLEAN
  319. CInterruptSyncServiceRoutine
  320. (
  321. IN PKINTERRUPT Interrupt,
  322. IN PVOID PVoidContext
  323. )
  324. {
  325. CInterruptSync *pCInterruptSync = (CInterruptSync *)(PVoidContext);
  326. BOOLEAN bResult = FALSE;
  327. //
  328. // Call ISRs as indicated by mode.
  329. //
  330. while (1)
  331. {
  332. BOOLEAN bResultThisPass = FALSE;
  333. for
  334. ( PLIST_ENTRY pListEntry = pCInterruptSync->m_listEntry.Flink;
  335. pListEntry != &pCInterruptSync->m_listEntry;
  336. pListEntry = pListEntry->Flink
  337. )
  338. {
  339. PISRLISTENTRY pIsrListEntry = PISRLISTENTRY(pListEntry);
  340. ASSERT(pIsrListEntry->Routine);
  341. if( NT_SUCCESS( pIsrListEntry->Routine( PINTERRUPTSYNC(pCInterruptSync),
  342. pIsrListEntry->DynamicContext ) ) )
  343. {
  344. bResult = TRUE;
  345. bResultThisPass = TRUE;
  346. if (pCInterruptSync->m_mode == InterruptSyncModeNormal)
  347. {
  348. break;
  349. }
  350. }
  351. }
  352. if( (pCInterruptSync->m_mode != InterruptSyncModeRepeat) ||
  353. (! bResultThisPass) )
  354. {
  355. break;
  356. }
  357. }
  358. return bResult;
  359. }
  360. /*****************************************************************************
  361. * CInterruptSync::Connect()
  362. *****************************************************************************
  363. * Initializes the synchronization object.
  364. */
  365. STDMETHODIMP_(NTSTATUS)
  366. CInterruptSync::
  367. Connect
  368. ( void
  369. )
  370. {
  371. _DbgPrintF(DEBUGLVL_BLAB,("CInterruptSync::Connect"));
  372. NTSTATUS ntStatus = STATUS_SUCCESS;
  373. KAFFINITY InterruptAffinity;
  374. //
  375. // Don't even think about connecting if we don't have any
  376. // ISR's in our list
  377. //
  378. KIRQL oldIrql;
  379. KeAcquireSpinLock( &m_kSpinLock, &oldIrql );
  380. if( IsListEmpty( &m_listEntry ) )
  381. {
  382. ntStatus = STATUS_UNSUCCESSFUL;
  383. }
  384. KeReleaseSpinLock( &m_kSpinLock, oldIrql );
  385. //
  386. // Connect if not already connected
  387. //
  388. if ( (NT_SUCCESS(ntStatus)) && (!m_pKInterrupt) )
  389. {
  390. InterruptAffinity = m_descriptor.u.Interrupt.Affinity;
  391. //
  392. // If an interrupt affinity override was specified in the registry, and
  393. // we are running on an MP machine, and at least one active processor
  394. // will be able to handle our devices interrupt if only processors
  395. // in both the device interrupt affinity specification AND the registry
  396. // interrupt affinity are allowed to handle the interrupts, then
  397. // limit the device interrupt affinity to the subset of both affinity
  398. // masks.
  399. //
  400. if (gInterruptAffinity &&
  401. KeNumberProcessors > 1 &&
  402. (InterruptAffinity&gInterruptAffinity&KeQueryActiveProcessors()) ) {
  403. InterruptAffinity &= gInterruptAffinity;
  404. }
  405. ntStatus = IoConnectInterrupt( &m_pKInterrupt,
  406. CInterruptSyncServiceRoutine,
  407. PVOID(this),
  408. &m_kSpinLock, // TODO: Spin lock sharing?
  409. m_descriptor.u.Interrupt.Vector,
  410. m_kIrql,
  411. m_kIrql, // TODO: Different for >1 interrupt?
  412. ((m_descriptor.Flags & CM_RESOURCE_INTERRUPT_LATCHED) ?
  413. Latched : LevelSensitive),
  414. (m_descriptor.ShareDisposition != CmResourceShareDeviceExclusive),
  415. InterruptAffinity,
  416. FALSE );
  417. if(NT_SUCCESS(ntStatus))
  418. {
  419. ASSERT(m_pKInterrupt);
  420. }
  421. }
  422. return ntStatus;
  423. }
  424. #pragma code_seg("PAGE")
  425. /*****************************************************************************
  426. * CInterruptSync::Disconnect()
  427. *****************************************************************************
  428. * Disconnect from the interrupt.
  429. */
  430. STDMETHODIMP_(void)
  431. CInterruptSync::
  432. Disconnect
  433. ( void
  434. )
  435. {
  436. _DbgPrintF(DEBUGLVL_BLAB,("CInterruptSync::Disconnect"));
  437. PAGED_CODE();
  438. if (m_pKInterrupt)
  439. {
  440. IoDisconnectInterrupt(m_pKInterrupt);
  441. m_pKInterrupt = NULL;
  442. }
  443. }
  444. #pragma code_seg()
  445. /*****************************************************************************
  446. * CServiceGroup::RegisterServiceRoutine()
  447. *****************************************************************************
  448. * Add a service routine.
  449. */
  450. STDMETHODIMP_(NTSTATUS)
  451. CInterruptSync::
  452. RegisterServiceRoutine
  453. (
  454. IN PINTERRUPTSYNCROUTINE Routine,
  455. IN PVOID DynamicContext,
  456. IN BOOLEAN First
  457. )
  458. {
  459. _DbgPrintF(DEBUGLVL_BLAB,("CInterruptSync::RegisterServiceRoutine"));
  460. ASSERT(Routine);
  461. NTSTATUS ntStatus = STATUS_SUCCESS;
  462. //
  463. // ensure we really have a routine
  464. //
  465. if( !Routine )
  466. {
  467. ntStatus = STATUS_INVALID_PARAMETER;
  468. }
  469. if( NT_SUCCESS(ntStatus) )
  470. {
  471. //
  472. // Create a new member.
  473. //
  474. PISRLISTENTRY pIsrListEntry = new(NonPagedPool,'lIcP') ISRLISTENTRY;
  475. if (pIsrListEntry)
  476. {
  477. pIsrListEntry->Routine = Routine;
  478. pIsrListEntry->DynamicContext = DynamicContext;
  479. //
  480. // Add the member to the list.
  481. //
  482. KIRQL kIrqlOld;
  483. KeAcquireSpinLock(&m_kSpinLock,&kIrqlOld);
  484. if (First)
  485. {
  486. InsertHeadList( &m_listEntry, &pIsrListEntry->ListEntry );
  487. }
  488. else
  489. {
  490. InsertTailList( &m_listEntry, &pIsrListEntry->ListEntry );
  491. }
  492. KeReleaseSpinLock(&m_kSpinLock,kIrqlOld);
  493. }
  494. else
  495. {
  496. ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  497. }
  498. }
  499. return ntStatus;
  500. }
  501. /*****************************************************************************
  502. * CInterruptSyncWrapperRoutine()
  503. *****************************************************************************
  504. * Wrapper for synchronized routines.
  505. */
  506. static
  507. BOOLEAN
  508. CInterruptSyncWrapperRoutine
  509. (
  510. IN PVOID PVoidContext
  511. )
  512. {
  513. PWRAPPERROUTINECONTEXT pContext = PWRAPPERROUTINECONTEXT(PVoidContext);
  514. pContext->NtStatus = pContext->Routine( PINTERRUPTSYNC(pContext->InterruptSync),
  515. pContext->DynamicContext);
  516. return TRUE;
  517. }
  518. /*****************************************************************************
  519. * CInterruptSync::CallSynchronizedRoutine()
  520. *****************************************************************************
  521. * Call a synchronized routine.
  522. */
  523. STDMETHODIMP_(NTSTATUS)
  524. CInterruptSync::
  525. CallSynchronizedRoutine
  526. (
  527. IN PINTERRUPTSYNCROUTINE Routine,
  528. IN PVOID DynamicContext
  529. )
  530. {
  531. WRAPPERROUTINECONTEXT context;
  532. context.Routine = Routine;
  533. context.DynamicContext = DynamicContext;
  534. context.InterruptSync = this;
  535. context.NtStatus = STATUS_SUCCESS;
  536. if (m_pKInterrupt)
  537. {
  538. if (!KeSynchronizeExecution(m_pKInterrupt,CInterruptSyncWrapperRoutine,&context ) )
  539. {
  540. context.NtStatus = STATUS_UNSUCCESSFUL;
  541. }
  542. }
  543. else if (KeGetCurrentIrql() <= DISPATCH_LEVEL)
  544. {
  545. _DbgPrintF(DEBUGLVL_TERSE,("Interrupt not connected yet, using spinlock"));
  546. KIRQL kIrqlOld;
  547. KeAcquireSpinLock(&m_kSpinLock,&kIrqlOld);
  548. // we have no interrupt yet, so synchronize the best you can
  549. (void)CInterruptSyncWrapperRoutine(&context);
  550. KeReleaseSpinLock(&m_kSpinLock,kIrqlOld);
  551. }
  552. else
  553. {
  554. context.NtStatus = STATUS_UNSUCCESSFUL;
  555. _DbgPrintF(DEBUGLVL_TERSE,("Interrupt not connected yet, but IRQL > DISPATCH_LEVEL"));
  556. }
  557. return context.NtStatus;
  558. }
  559. /*****************************************************************************
  560. * CInterruptSync::GetKInterrupt()
  561. *****************************************************************************
  562. * Get a WDM InterruptObject from a portcls sync object.
  563. */
  564. STDMETHODIMP_(PKINTERRUPT)
  565. CInterruptSync::
  566. GetKInterrupt
  567. ( void
  568. )
  569. {
  570. return m_pKInterrupt;
  571. }