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.

1132 lines
29 KiB

  1. TITLE "Spin Locks"
  2. ;++
  3. ;
  4. ; Copyright (c) 1989 Microsoft Corporation
  5. ;
  6. ; Module Name:
  7. ;
  8. ; spinlock.asm
  9. ;
  10. ; Abstract:
  11. ;
  12. ; This module implements the routines for acquiring and releasing
  13. ; spin locks.
  14. ;
  15. ; Author:
  16. ;
  17. ; Bryan Willman (bryanwi) 13 Dec 89
  18. ;
  19. ; Environment:
  20. ;
  21. ; Kernel mode only.
  22. ;
  23. ; Revision History:
  24. ;
  25. ; Ken Reneris (kenr) 22-Jan-1991
  26. ; Removed KeAcquireSpinLock macros, and made functions
  27. ;--
  28. PAGE
  29. .586p
  30. include ks386.inc
  31. include callconv.inc ; calling convention macros
  32. include i386\kimacro.inc
  33. include mac386.inc
  34. include irqli386.inc
  35. EXTRNP _KeBugCheckEx,5
  36. _TEXT$00 SEGMENT PARA PUBLIC 'CODE'
  37. ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
  38. PAGE
  39. SUBTTL "Ke Acquire Spin Lock At DPC Level"
  40. ;++
  41. ;
  42. ; VOID
  43. ; KefAcquireSpinLockAtDpcLevel (
  44. ; IN PKSPIN_LOCK SpinLock
  45. ; )
  46. ;
  47. ; Routine Description:
  48. ;
  49. ; This function acquires a kernel spin lock.
  50. ;
  51. ; N.B. This function assumes that the current IRQL is set properly.
  52. ; It neither raises nor lowers IRQL.
  53. ;
  54. ; Arguments:
  55. ;
  56. ; (ecx) SpinLock - Supplies a pointer to an kernel spin lock.
  57. ;
  58. ; Return Value:
  59. ;
  60. ; None.
  61. ;
  62. ;--
  63. align 16
  64. cPublicFastCall KefAcquireSpinLockAtDpcLevel, 1
  65. cPublicFpo 0, 0
  66. if DBG
  67. push ecx
  68. CurrentIrql
  69. pop ecx
  70. cmp al, DISPATCH_LEVEL
  71. jl short asld50
  72. endif
  73. ifdef NT_UP
  74. fstRET KefAcquireSpinLockAtDpcLevel
  75. else
  76. ;
  77. ; Attempt to assert the lock
  78. ;
  79. asld10: ACQUIRE_SPINLOCK ecx,<short asld20>
  80. fstRET KefAcquireSpinLockAtDpcLevel
  81. ;
  82. ; Lock is owned, spin till it looks free, then go get it again.
  83. ;
  84. align 4
  85. asld20: SPIN_ON_SPINLOCK ecx,<short asld10>
  86. endif
  87. if DBG
  88. asld50: stdCall _KeBugCheckEx,<IRQL_NOT_GREATER_OR_EQUAL,ecx,eax,0,0>
  89. int 3 ; help debugger backtrace.
  90. endif
  91. fstENDP KefAcquireSpinLockAtDpcLevel
  92. ;++
  93. ;
  94. ; VOID
  95. ; KeAcquireSpinLockAtDpcLevel (
  96. ; IN PKSPIN_LOCK SpinLock
  97. ; )
  98. ;
  99. ; Routine Description:
  100. ;
  101. ; Thunk for standard call callers
  102. ;
  103. ;--
  104. cPublicProc _KeAcquireSpinLockAtDpcLevel, 1
  105. cPublicFpo 1,0
  106. ifndef NT_UP
  107. mov ecx,[esp+4] ; SpinLock
  108. aslc10: ACQUIRE_SPINLOCK ecx,<short aslc20>
  109. stdRET _KeAcquireSpinLockAtDpcLevel
  110. aslc20: SPIN_ON_SPINLOCK ecx,<short aslc10>
  111. endif
  112. stdRET _KeAcquireSpinLockAtDpcLevel
  113. stdENDP _KeAcquireSpinLockAtDpcLevel
  114. PAGE
  115. SUBTTL "Ke Release Spin Lock From Dpc Level"
  116. ;++
  117. ;
  118. ; VOID
  119. ; KefReleaseSpinLockFromDpcLevel (
  120. ; IN PKSPIN_LOCK SpinLock
  121. ; )
  122. ;
  123. ; Routine Description:
  124. ;
  125. ; This function releases a kernel spin lock.
  126. ;
  127. ; N.B. This function assumes that the current IRQL is set properly.
  128. ; It neither raises nor lowers IRQL.
  129. ;
  130. ; Arguments:
  131. ;
  132. ; (ecx) SpinLock - Supplies a pointer to an executive spin lock.
  133. ;
  134. ; Return Value:
  135. ;
  136. ; None.
  137. ;
  138. ;--
  139. align 16
  140. cPublicFastCall KefReleaseSpinLockFromDpcLevel ,1
  141. cPublicFpo 0,0
  142. ifndef NT_UP
  143. RELEASE_SPINLOCK ecx
  144. endif
  145. fstRET KefReleaseSpinLockFromDpcLevel
  146. fstENDP KefReleaseSpinLockFromDpcLevel
  147. ;++
  148. ;
  149. ; VOID
  150. ; KeReleaseSpinLockFromDpcLevel (
  151. ; IN PKSPIN_LOCK SpinLock
  152. ; )
  153. ;
  154. ; Routine Description:
  155. ;
  156. ; Thunk for standard call callers
  157. ;
  158. ;--
  159. cPublicProc _KeReleaseSpinLockFromDpcLevel, 1
  160. cPublicFpo 1,0
  161. ifndef NT_UP
  162. mov ecx, [esp+4] ; (ecx) = SpinLock
  163. RELEASE_SPINLOCK ecx
  164. endif
  165. stdRET _KeReleaseSpinLockFromDpcLevel
  166. stdENDP _KeReleaseSpinLockFromDpcLevel
  167. PAGE
  168. SUBTTL "Ki Acquire Kernel Spin Lock"
  169. ;++
  170. ;
  171. ; VOID
  172. ; FASTCALL
  173. ; KiAcquireSpinLock (
  174. ; IN PKSPIN_LOCK SpinLock
  175. ; )
  176. ;
  177. ; Routine Description:
  178. ;
  179. ; This function acquires a kernel spin lock.
  180. ;
  181. ; N.B. This function assumes that the current IRQL is set properly.
  182. ; It neither raises nor lowers IRQL.
  183. ;
  184. ; Arguments:
  185. ;
  186. ; (ecx) SpinLock - Supplies a pointer to an kernel spin lock.
  187. ;
  188. ; Return Value:
  189. ;
  190. ; None.
  191. ;
  192. ;--
  193. align 16
  194. cPublicFastCall KiAcquireSpinLock ,1
  195. cPublicFpo 0,0
  196. ifndef NT_UP
  197. ;
  198. ; Attempt to assert the lock
  199. ;
  200. asl10: ACQUIRE_SPINLOCK ecx,<short asl20>
  201. fstRET KiAcquireSpinLock
  202. ;
  203. ; Lock is owned, spin till it looks free, then go get it again.
  204. ;
  205. align 4
  206. asl20: SPIN_ON_SPINLOCK ecx,<short asl10>
  207. else
  208. fstRET KiAcquireSpinLock
  209. endif
  210. fstENDP KiAcquireSpinLock
  211. PAGE
  212. SUBTTL "Ki Release Kernel Spin Lock"
  213. ;++
  214. ;
  215. ; VOID
  216. ; FASTCALL
  217. ; KiReleaseSpinLock (
  218. ; IN PKSPIN_LOCK SpinLock
  219. ; )
  220. ;
  221. ; Routine Description:
  222. ;
  223. ; This function releases a kernel spin lock.
  224. ;
  225. ; N.B. This function assumes that the current IRQL is set properly.
  226. ; It neither raises nor lowers IRQL.
  227. ;
  228. ; Arguments:
  229. ;
  230. ; (ecx) SpinLock - Supplies a pointer to an executive spin lock.
  231. ;
  232. ; Return Value:
  233. ;
  234. ; None.
  235. ;
  236. ;--
  237. align 16
  238. cPublicFastCall KiReleaseSpinLock ,1
  239. cPublicFpo 0,0
  240. ifndef NT_UP
  241. RELEASE_SPINLOCK ecx
  242. endif
  243. fstRET KiReleaseSpinLock
  244. fstENDP KiReleaseSpinLock
  245. PAGE
  246. SUBTTL "Try to acquire Kernel Spin Lock"
  247. ;++
  248. ;
  249. ; BOOLEAN
  250. ; KeTryToAcquireSpinLock (
  251. ; IN PKSPIN_LOCK SpinLock,
  252. ; OUT PKIRQL OldIrql
  253. ; )
  254. ;
  255. ; Routine Description:
  256. ;
  257. ; This function attempts acquires a kernel spin lock. If the
  258. ; spinlock is busy, it is not acquire and FALSE is returned.
  259. ;
  260. ; Arguments:
  261. ;
  262. ; SpinLock (TOS+4) - Supplies a pointer to an kernel spin lock.
  263. ; OldIrql (TOS+8) = Location to store old irql
  264. ;
  265. ; Return Value:
  266. ; TRUE - Spinlock was acquired & irql was raise
  267. ; FALSE - SpinLock was not acquired - irql is unchanged.
  268. ;
  269. ;--
  270. align dword
  271. cPublicProc _KeTryToAcquireSpinLock ,2
  272. cPublicFpo 2,0
  273. ifdef NT_UP
  274. ; UP Version of KeTryToAcquireSpinLock
  275. RaiseIrql DISPATCH_LEVEL
  276. mov ecx, [esp+8] ; (ecx) -> ptr to OldIrql
  277. mov [ecx], al ; save OldIrql
  278. mov eax, 1 ; Return TRUE
  279. stdRET _KeTryToAcquireSpinLock
  280. else
  281. ; MP Version of KeTryToAcquireSpinLock
  282. mov edx,[esp+4] ; (edx) -> spinlock
  283. ;
  284. ; First check the spinlock without asserting a lock
  285. ;
  286. TEST_SPINLOCK edx,<short ttsl10>
  287. ;
  288. ; Spinlock looks free raise irql & try to acquire it
  289. ;
  290. ;
  291. ; raise to dispatch_level
  292. ;
  293. RaiseIrql DISPATCH_LEVEL
  294. mov edx, [esp+4] ; (edx) -> spinlock
  295. mov ecx, [esp+8] ; (ecx) = Return OldIrql
  296. ACQUIRE_SPINLOCK edx,<short ttsl20>
  297. mov [ecx], al ; save OldIrql
  298. mov eax, 1 ; spinlock was acquired, return TRUE
  299. stdRET _KeTryToAcquireSpinLock
  300. ttsl10: xor eax, eax ; return FALSE
  301. YIELD
  302. stdRET _KeTryToAcquireSpinLock
  303. ttsl20:
  304. YIELD
  305. mov ecx, eax ; (ecx) = OldIrql
  306. LowerIrql ecx
  307. xor eax, eax ; return FALSE
  308. stdRET _KeTryToAcquireSpinLock
  309. endif
  310. stdENDP _KeTryToAcquireSpinLock
  311. PAGE
  312. SUBTTL "Ki Try to acquire Kernel Spin Lock"
  313. ;++
  314. ;
  315. ; BOOLEAN
  316. ; FASTCALL
  317. ; KeTryToAcquireSpinLockAtDpcLevel (
  318. ; IN PKSPIN_LOCK SpinLock
  319. ; )
  320. ;
  321. ; Routine Description:
  322. ;
  323. ; This function attempts acquires a kernel spin lock. If the
  324. ; spinlock is busy, it is not acquire and FALSE is returned.
  325. ;
  326. ; Arguments:
  327. ;
  328. ; SpinLock (ecx) - Supplies a pointer to an kernel spin lock.
  329. ;
  330. ; Return Value:
  331. ;
  332. ; TRUE - Spinlock was acquired
  333. ; FALSE - SpinLock was not acquired
  334. ;
  335. ;--
  336. align dword
  337. cPublicFastCall KeTryToAcquireSpinLockAtDpcLevel ,1
  338. cPublicFpo 0, 0
  339. ;
  340. ; First check the spinlock without asserting a lock
  341. ;
  342. ifndef NT_UP
  343. TEST_SPINLOCK ecx, <short atsl20>
  344. ;
  345. ; Spinlock looks free try to acquire it.
  346. ;
  347. ACQUIRE_SPINLOCK ecx, <short atsl20>
  348. endif
  349. mov eax, 1 ; spinlock was acquired, return TRUE
  350. fstRET KeTryToAcquireSpinLockAtDpcLevel
  351. ifndef NT_UP
  352. atsl20: YIELD ;
  353. xor eax, eax ; return FALSE
  354. fstRET KeTryToAcquireSpinLockAtDpclevel
  355. endif
  356. fstENDP KeTryToAcquireSpinLockAtDpcLevel
  357. ;++
  358. ;
  359. ; BOOLEAN
  360. ; KeTestSpinLock (
  361. ; IN PKSPIN_LOCK SpinLock
  362. ; )
  363. ;
  364. ; Routine Description:
  365. ;
  366. ; This function tests a kernel spin lock. If the spinlock is
  367. ; busy, FALSE is returned. If not, TRUE is returned. The spinlock
  368. ; is never acquired. This is provided to allow code to spin at low
  369. ; IRQL, only raising the IRQL when there is a reasonable hope of
  370. ; acquiring the lock.
  371. ;
  372. ; Arguments:
  373. ;
  374. ; SpinLock (ecx) - Supplies a pointer to a kernel spin lock.
  375. ;
  376. ; Return Value:
  377. ; TRUE - Spinlock appears available
  378. ; FALSE - SpinLock is busy
  379. ;
  380. ;--
  381. cPublicFastCall KeTestSpinLock ,1
  382. TEST_SPINLOCK ecx,<short tso10>
  383. mov eax, 1
  384. fstRET KeTestSpinLock
  385. tso10: YIELD
  386. xor eax, eax
  387. fstRET KeTestSpinLock
  388. fstENDP KeTestSpinLock
  389. page ,132
  390. subttl "Acquire In Stack Queued SpinLock At Dpc Level"
  391. ifdef QLOCK_STAT_GATHER
  392. EXTRNP KiQueueStatTrySucceeded,2,,FASTCALL
  393. EXTRNP KiQueueStatTryFailed,1,,FASTCALL
  394. EXTRNP KiQueueStatTry,1,,FASTCALL
  395. EXTRNP KiQueueStatAcquireQueuedLock,1,,FASTCALL
  396. EXTRNP KiQueueStatAcquireQueuedLockRTS,1,,FASTCALL
  397. EXTRNP KiQueueStatTryAcquire,3,,FASTCALL
  398. EXTRNP KiQueueStatReleaseQueuedLock,2,,FASTCALL
  399. EXTRNP KiQueueStatAcquire,1,,FASTCALL
  400. EXTRNP KiQueueStatRelease,1,,FASTCALL
  401. EXTRNP KiAcquireQueuedLock,1,,FASTCALL
  402. EXTRNP KiReleaseQueuedLock,1,,FASTCALL
  403. ;
  404. ; The following routines are used to wrap the actual calls to the
  405. ; real routines which have been usurped here by patching the import
  406. ; table.
  407. ;
  408. cPublicFastCall __cap_KeAcquireQueuedSpinLock,1
  409. sub esp, 8 ; make room to save time
  410. push ecx ; save args
  411. rdtsc ; get time
  412. mov [esp].4, eax ; save low part
  413. mov [esp].8, edx ; save high part
  414. mov ecx, [esp] ; restore arg
  415. fstCall KiQueueStatAcquireQueuedLock
  416. acqst: mov ecx, esp ; set arg pointer for data accum
  417. push eax ; save result
  418. fstCall KiQueueStatAcquire
  419. pop eax ; restore result
  420. add esp, 12 ; restore stack pointer
  421. fstRET __cap_KeAcquireQueuedSpinLock
  422. fstENDP __cap_KeAcquireQueuedSpinLock
  423. cPublicFastCall __cap_KeAcquireQueuedSpinLockRaiseToSynch,1
  424. sub esp, 8 ; make room to save time
  425. push ecx ; save args
  426. rdtsc ; get time
  427. mov [esp].4, eax ; save low part
  428. mov [esp].8, edx ; save high part
  429. mov ecx, [esp] ; restore arg
  430. fstCall KiQueueStatAcquireQueuedLockRTS
  431. jmp short acqst ; use common code to finish
  432. fstENDP __cap_KeAcquireQueuedSpinLockRaiseToSynch
  433. cPublicFastCall __cap_KeTryToAcquireQueuedSpinLockRaiseToSynch,2
  434. push ecx ; save arg
  435. push SYNCH_LEVEL
  436. tryst: fstCall KiQueueStatTryAcquire
  437. push eax ; save result
  438. mov ecx, esp
  439. fstCall KiQueueStatTry
  440. pop eax ; restore result
  441. add esp, 4 ; drop saved arg
  442. or eax, eax ; some assembly callers expect appropriate flags
  443. fstRET __cap_KeTryToAcquireQueuedSpinLockRaiseToSynch
  444. fstENDP __cap_KeTryToAcquireQueuedSpinLockRaiseToSynch
  445. cPublicFastCall __cap_KeTryToAcquireQueuedSpinLock,2
  446. push ecx ; save arg
  447. push DISPATCH_LEVEL
  448. jmp short tryst ; use common code to finish
  449. fstENDP __cap_KeTryToAcquireQueuedSpinLock
  450. cPublicFastCall __cap_KeReleaseQueuedSpinLock,2
  451. push ecx ; save args
  452. mov ecx, esp ; set arg for stat release routine
  453. push edx ; save other arg
  454. fstCall KiQueueStatRelease
  455. pop edx
  456. pop ecx
  457. fstCall KiQueueStatReleaseQueuedLock
  458. fstRET __cap_KeReleaseQueuedSpinLock
  459. fstENDP __cap_KeReleaseQueuedSpinLock
  460. ;
  461. ; KeAcquireQueuedSpinLockAtDpcLevel
  462. ; KeReleaseQueuedSpinLockFromDpcLevel
  463. ;
  464. ; These two routines are defined here in assembly code so
  465. ; as to capture the caller's address.
  466. ;
  467. cPublicFastCall KeAcquireQueuedSpinLockAtDpcLevel,1
  468. sub esp, 8 ; make room to save time
  469. push ecx ; save args
  470. rdtsc ; get time
  471. mov [esp].4, eax ; save low part
  472. mov [esp].8, edx ; save high part
  473. mov ecx, [esp] ; restore arg
  474. fstCall KiAcquireQueuedLock
  475. mov ecx, esp
  476. fstCall KiQueueStatAcquire
  477. add esp, 12 ; restore SP
  478. fstRET KeAcquireQueuedSpinLockAtDpcLevel
  479. fstENDP KeAcquireQueuedSpinLockAtDpcLevel
  480. cPublicFastCall KeReleaseQueuedSpinLockFromDpcLevel,1
  481. push ecx ; save args
  482. mov ecx, esp ; set arg for stat release routine
  483. fstCall KiQueueStatRelease
  484. pop ecx
  485. fstCall KiReleaseQueuedLock
  486. fstRET KeReleaseQueuedSpinLockFromDpcLevel
  487. fstENDP KeReleaseQueuedSpinLockFromDpcLevel
  488. ;
  489. ; KiCaptureQueuedSpinlockRoutines
  490. ;
  491. ; Replace the import table entries for the x86 HAL queued spinlock
  492. ; routines with our statistic capturing variety.
  493. ;
  494. EXTRNP KeAcquireQueuedSpinLock,1,IMPORT,FASTCALL
  495. EXTRNP KeAcquireQueuedSpinLockRaiseToSynch,1,IMPORT,FASTCALL
  496. EXTRNP KeTryToAcquireQueuedSpinLockRaiseToSynch,2,IMPORT,FASTCALL
  497. EXTRNP KeTryToAcquireQueuedSpinLock,2,IMPORT,FASTCALL
  498. EXTRNP KeReleaseQueuedSpinLock,2,IMPORT,FASTCALL
  499. cPublicFastCall KiCaptureQueuedSpinlockRoutines,0
  500. mov eax, @__cap_KeAcquireQueuedSpinLock@4
  501. mov [__imp_@KeAcquireQueuedSpinLock@4], eax
  502. mov eax, @__cap_KeAcquireQueuedSpinLockRaiseToSynch@4
  503. mov [__imp_@KeAcquireQueuedSpinLockRaiseToSynch@4], eax
  504. mov eax, @__cap_KeTryToAcquireQueuedSpinLockRaiseToSynch@8
  505. mov [__imp_@KeTryToAcquireQueuedSpinLockRaiseToSynch@8], eax
  506. mov eax, @__cap_KeTryToAcquireQueuedSpinLock@8
  507. mov [__imp_@KeTryToAcquireQueuedSpinLock@8], eax
  508. mov eax, @__cap_KeReleaseQueuedSpinLock@8
  509. mov [__imp_@KeReleaseQueuedSpinLock@8], eax
  510. fstRet KiCaptureQueuedSpinlockRoutines
  511. fstENDP KiCaptureQueuedSpinlockRoutines
  512. else
  513. ;++
  514. ;
  515. ; VOID
  516. ; FASTCALL
  517. ; KeAcquireInStackQueuedSpinLockAtDpcLevel (
  518. ; IN PKSPIN_LOCK SpinLock,
  519. ; IN PKLOCK_QUEUE_HANDLE LockHandle
  520. ; )
  521. ;
  522. ; Routine Description:
  523. ;
  524. ; This function acquires the specified in stack queued spin lock at the
  525. ; current IRQL.
  526. ;
  527. ; Arguments:
  528. ;
  529. ; SpinLock (ecx) - Supplies the address of a spin lock.
  530. ;
  531. ; LockHandle (edx) - Supplies the address of an in stack lock handle.
  532. ;
  533. ; Return Value:
  534. ;
  535. ; None.
  536. ;--
  537. align 16
  538. cPublicFastCall KeAcquireInStackQueuedSpinLockAtDpcLevel,2
  539. cPublicFpo 0,0
  540. ifndef NT_UP
  541. xor eax, eax ; set next link to NULL
  542. mov [edx].LqhNext, eax ;
  543. mov [edx].LqhLock, ecx ; set spin lock address
  544. lea ecx, dword ptr [edx+LqhNext] ; compute address of lock queue
  545. jmp short @KeAcquireQueuedSpinLockAtDpcLevel@4 ; finish in common code
  546. else
  547. fstRET KeAcquireInStackQueuedSpinLockAtDpcLevel
  548. endif
  549. fstENDP KeAcquireInStackQueuedSpinLockAtDpcLevel
  550. page ,132
  551. subttl "Acquire Queued SpinLock"
  552. ;++
  553. ;
  554. ; VOID
  555. ; KeAcquireQueuedSpinLockAtDpcLevel (
  556. ; IN PKSPIN_LOCK_QUEUE QueuedLock
  557. ; )
  558. ;
  559. ; Routine Description:
  560. ;
  561. ; This function acquires the specified queued spinlock.
  562. ; No change to IRQL is made, IRQL is not returned. It is
  563. ; expected IRQL is sufficient to avoid context switch.
  564. ;
  565. ; Unlike the equivalent Ke versions of these routines,
  566. ; the argument to this routine is the address of the
  567. ; lock queue entry (for the lock to be acquired) in the
  568. ; PRCB rather than the LockQueueNumber. This saves us
  569. ; a couple of instructions as the address can be calculated
  570. ; at compile time.
  571. ;
  572. ; NOTE: This code may be modified for use during textmode
  573. ; setup if this is an MP kernel running with a UP HAL.
  574. ;
  575. ; Arguments:
  576. ;
  577. ; LockQueueEntry (ecx) - Supplies the address of the queued
  578. ; spinlock entry in this processor's
  579. ; PRCB.
  580. ;
  581. ; Return Value:
  582. ;
  583. ; None.
  584. ;
  585. ; N.B. ecx is preserved, assembly code callers can take advantage
  586. ; of this by avoiding setting up ecx for the call to release if
  587. ; the caller can preserve the lock that long.
  588. ;
  589. ;--
  590. ; compile time assert sizeof(KSPIN_LOCK_QUEUE) == 8
  591. .errnz (LOCK_QUEUE_HEADER_SIZE - 8)
  592. align 16
  593. cPublicFastCall KeAcquireQueuedSpinLockAtDpcLevel,1
  594. cPublicFpo 0,0
  595. ifndef NT_UP
  596. ; Get address of the actual lock.
  597. mov edx, [ecx].LqLock
  598. ifdef CAPKERN_SYNCH_POINTS
  599. push edx
  600. push 000010101h ; 1 Dword, Timestamp, Subcode = 1
  601. call _CAP_Log_NInt
  602. add esp, 8
  603. endif
  604. mov eax, ecx ; save Lock Queue entry address
  605. ; Exchange the value of the lock with the address of this
  606. ; Lock Queue entry.
  607. xchg [edx], eax
  608. cmp eax, 0 ; check if lock is held
  609. jnz short @f ; jiff held
  610. ; note: the actual lock address will be word aligned, we use
  611. ; the bottom two bits as indicators, bit 0 is LOCK_QUEUE_WAIT,
  612. ; bit 1 is LOCK_QUEUE_OWNER.
  613. or edx, LOCK_QUEUE_OWNER ; mark self as lock owner
  614. mov [ecx].LqLock, edx
  615. ; lock has been acquired, return.
  616. aqsl20:
  617. endif
  618. fstRET KeAcquireQueuedSpinLockAtDpcLevel
  619. ifndef NT_UP
  620. @@:
  621. if DBG
  622. ; make sure it isn't already held by THIS processor.
  623. test edx, LOCK_QUEUE_OWNER
  624. jz short @f
  625. ; KeBugCheckEx(SPIN_LOCK_ALREADY_OWNED,
  626. ; actual lock address,
  627. ; my context,
  628. ; previous acquirer,
  629. ; 2);
  630. stdCall _KeBugCheckEx,<SPIN_LOCK_ALREADY_OWNED,edx,ecx,eax,2>
  631. @@:
  632. endif
  633. ; The lock is already held by another processor. Set the wait
  634. ; bit in this processor's Lock Queue entry, then set the next
  635. ; field in the Lock Queue entry of the last processor to attempt
  636. ; to acquire the lock (this is the address returned by the xchg
  637. ; above) to point to THIS processor's lock queue entry.
  638. or edx, LOCK_QUEUE_WAIT ; set lock bit
  639. mov [ecx].LqLock, edx
  640. mov [eax].LqNext, ecx ; set previous acquirer's
  641. ; next field.
  642. ifdef CAPKERN_SYNCH_POINTS
  643. and edx, 0FFFFFFFCh
  644. push edx
  645. xor edx, edx
  646. ; Wait.
  647. aqsl30: inc edx
  648. test [ecx].LqLock, LOCK_QUEUE_WAIT ; check if still waiting
  649. jz short aqsl40 ; jif lock acquired
  650. YIELD ; fire avoidance.
  651. jmp short aqsl30 ; else, continue waiting
  652. aqsl40: push edx
  653. push 000020104h ; 2 Dwords, Timestamp, Subcode = 4
  654. call _CAP_Log_NInt
  655. add esp, 12
  656. jmp short aqsl20
  657. else
  658. ; Wait.
  659. @@:
  660. test [ecx].LqLock, LOCK_QUEUE_WAIT ; check if still waiting
  661. jz short aqsl20 ; jif lock acquired
  662. YIELD ; fire avoidance.
  663. jmp short @b ; else, continue waiting
  664. endif
  665. endif
  666. fstENDP KeAcquireQueuedSpinLockAtDpcLevel
  667. page ,132
  668. subttl "Release In Stack Queued SpinLock From Dpc Level"
  669. ;++
  670. ;
  671. ; VOID
  672. ; FASTCALL
  673. ; KeReleaseInStackQueuedSpinLockFromDpcLevel (
  674. ; IN PKLOCK_QUEUE_HANDLE LockHandle
  675. ; )
  676. ;
  677. ; Routine Description:
  678. ;
  679. ; This function releases a queued spinlock and preserves the current
  680. ; IRQL.
  681. ;
  682. ; Arguments:
  683. ;
  684. ; LockHandle (ecx) - Supplies the address of a lock handle.
  685. ;
  686. ; Return Value:
  687. ;
  688. ; None.
  689. ;
  690. ;--
  691. cPublicFastCall KeReleaseInStackQueuedSpinLockFromDpcLevel,1
  692. cPublicFpo 0,0
  693. ifndef NT_UP
  694. lea ecx, dword ptr[ecx+LqhNext] ; compute address of lock queue
  695. jmp short @KeReleaseQueuedSpinLockFromDpcLevel@4 ; finish in common code
  696. else
  697. fstRET KeReleaseInStackQueuedSpinLockFromDpcLevel
  698. endif
  699. fstENDP KeReleaseInStackQueuedSpinLockFromDpcLevel
  700. page ,132
  701. subttl "Release Queued SpinLock"
  702. ;++
  703. ;
  704. ; VOID
  705. ; KeReleaseQueuedSpinLockFromDpcLevel (
  706. ; IN PKSPIN_LOCK_QUEUE QueuedLock
  707. ; )
  708. ;
  709. ; Routine Description:
  710. ;
  711. ; This function releases a queued spinlock.
  712. ; No change to IRQL is made, IRQL is not returned. It is
  713. ; expected IRQL is sufficient to avoid context switch.
  714. ;
  715. ; NOTE: This code may be modified for use during textmode
  716. ; setup if this is an MP kernel running with a UP HAL.
  717. ;
  718. ; Arguments:
  719. ;
  720. ; LockQueueEntry (ecx) - Supplies the address of the queued
  721. ; spinlock entry in this processor's
  722. ; PRCB.
  723. ;
  724. ; Return Value:
  725. ;
  726. ; None.
  727. ;
  728. ;--
  729. cPublicFastCall KeReleaseQueuedSpinLockFromDpcLevel,1
  730. cPublicFpo 0,0
  731. .errnz (LOCK_QUEUE_OWNER - 2) ; error if not bit 1 for btr
  732. ifndef NT_UP
  733. mov eax, ecx ; need in eax for cmpxchg
  734. mov edx, [ecx].LqNext
  735. mov ecx, [ecx].LqLock
  736. ifdef CAPKERN_SYNCH_POINTS
  737. push ecx
  738. and ecx, 0FFFFFFFCh
  739. push ecx
  740. push 000010107h ; 1 Dword, Timestamp, Subcode = 7
  741. call _CAP_Log_NInt
  742. add esp, 8
  743. pop ecx
  744. endif
  745. ; Quick check: If Lock Queue entry's Next field is not NULL,
  746. ; there is another waiter. Don't bother with ANY atomic ops
  747. ; in this case.
  748. ;
  749. ; N.B. Careful ordering, the test will clear the CF bit and set
  750. ; the ZF bit appropriately if the Next Field (in EDX) is zero.
  751. ; The BTR will set the CF bit to the previous value of the owner
  752. ; bit.
  753. test edx, edx
  754. ; Clear the "I am owner" field in the Lock entry.
  755. btr ecx, 1 ; clear owner bit
  756. if DBG
  757. jnc short rqsl90 ; bugcheck if was not set
  758. ; tests CF
  759. endif
  760. mov [eax].LqLock, ecx ; clear lock bit in queue entry
  761. jnz short rqsl40 ; jif another processor waits
  762. ; tests ZF
  763. xor edx, edx ; new lock owner will be NULL
  764. push eax ; save &PRCB->LockQueue[Number]
  765. ; Use compare exchange to attempt to clear the actual lock.
  766. ; If there are still no processors waiting for the lock when
  767. ; the compare exchange happens, the old contents of the lock
  768. ; should be the address of this lock entry (eax).
  769. lock cmpxchg [ecx], edx ; store 0 if no waiters
  770. pop eax ; restore lock queue address
  771. jnz short rqsl60 ; jif store failed
  772. ; The lock has been released. Return to caller.
  773. endif
  774. fstRET KeReleaseQueuedSpinLockFromDpcLevel
  775. ifndef NT_UP
  776. ; Another processor is waiting on this lock. Hand the lock
  777. ; to that processor by getting the address of its LockQueue
  778. ; entry, turning ON its owner bit and OFF its wait bit.
  779. rqsl40: xor [edx].LqLock, (LOCK_QUEUE_OWNER+LOCK_QUEUE_WAIT)
  780. ; Done, the other processor now owns the lock, clear the next
  781. ; field in my LockQueue entry (to preserve the order for entering
  782. ; the queue again) and return.
  783. mov [eax].LqNext, 0
  784. fstRET KeReleaseQueuedSpinLockFromDpcLevel
  785. ; We get here if another processor is attempting to acquire
  786. ; the lock but had not yet updated the next field in this
  787. ; processor's Queued Lock Next field. Wait for the next
  788. ; field to be updated.
  789. ifdef CAPKERN_SYNCH_POINTS
  790. rqsl60: push ecx
  791. xor ecx, ecx
  792. rqsl70: inc ecx
  793. mov edx, [eax].LqNext
  794. test edx, edx ; check if still 0
  795. jnz short rqsl80 ; jif Next field now set.
  796. YIELD ; wait a bit
  797. jmp short rqsl70 ; continue waiting
  798. rqsl80: push ecx
  799. push 000020104h ; 2 Dwords, Timestamp, Subcode = 4
  800. call _CAP_Log_NInt
  801. add esp, 12
  802. jmp short rqsl40
  803. else
  804. rqsl60: mov edx, [eax].LqNext
  805. test edx, edx ; check if still 0
  806. jnz short rqsl40 ; jif Next field now set.
  807. YIELD ; wait a bit
  808. jmp short rqsl60 ; continue waiting
  809. endif
  810. if DBG
  811. rqsl90:
  812. stdCall _KeBugCheckEx,<SPIN_LOCK_NOT_OWNED,ecx,eax,0,0>
  813. int 3 ; help debugger back trace.
  814. endif
  815. endif
  816. fstENDP KeReleaseQueuedSpinLockFromDpcLevel
  817. endif
  818. page ,132
  819. subttl "Try to Acquire Queued SpinLock"
  820. ;++
  821. ;
  822. ; LOGICAL
  823. ; KeTryToAcquireQueuedSpinLockAtRaisedIrql (
  824. ; IN PKSPIN_LOCK_QUEUE QueuedLock
  825. ; )
  826. ;
  827. ; Routine Description:
  828. ;
  829. ; This function attempts to acquire the specified queued spinlock.
  830. ; No change to IRQL is made, IRQL is not returned. It is
  831. ; expected IRQL is sufficient to avoid context switch.
  832. ;
  833. ; NOTE: This code may be modified for use during textmode
  834. ; setup if this is an MP kernel running with a UP HAL.
  835. ;
  836. ; Arguments:
  837. ;
  838. ; LockQueueEntry (ecx) - Supplies the address of the queued
  839. ; spinlock entry in this processor's
  840. ; PRCB.
  841. ;
  842. ; Return Value:
  843. ;
  844. ; TRUE if the lock was acquired, FALSE otherwise.
  845. ; N.B. ZF is set if FALSE returned, clear otherwise.
  846. ;
  847. ;--
  848. align 16
  849. cPublicFastCall KeTryToAcquireQueuedSpinLockAtRaisedIrql,1
  850. cPublicFpo 0,0
  851. ifndef NT_UP
  852. ; Get address of Lock Queue entry
  853. mov edx, [ecx].LqLock
  854. ifdef CAPKERN_SYNCH_POINTS
  855. push edx
  856. push 000010108h ; 1 Dword, Timestamp, Subcode = 8
  857. call _CAP_Log_NInt
  858. add esp, 8
  859. endif
  860. ; Store the Lock Queue entry address in the lock ONLY if the
  861. ; current lock value is 0.
  862. xor eax, eax ; old value must be 0
  863. lock cmpxchg [edx], ecx
  864. jnz short taqsl60
  865. ; Lock has been acquired.
  866. ; note: the actual lock address will be word aligned, we use
  867. ; the bottom two bits as indicators, bit 0 is LOCK_QUEUE_WAIT,
  868. ; bit 1 is LOCK_QUEUE_OWNER.
  869. or edx, LOCK_QUEUE_OWNER ; mark self as lock owner
  870. mov [ecx].LqLock, edx
  871. ifdef QLOCK_STAT_GATHER
  872. mov edx, [esp]
  873. fstCall KiQueueStatTrySucceeded
  874. endif
  875. or eax, 1 ; return TRUE
  876. fstRET KeTryToAcquireQueuedSpinLockAtRaisedIrql
  877. taqsl60:
  878. if 0
  879. ; note: it is not fatal if the current processor already owns the
  880. ; lock as this is perfectly normal - just return FALSE.
  881. test edx, LOCK_QUEUE_OWNER
  882. jz short @f
  883. stdCall _KeBugCheckEx,<SPIN_LOCK_ALREADY_OWNED, edx, ecx,0,1>
  884. @@:
  885. endif
  886. ; The lock is already held by another processor. Indicate
  887. ; failure to the caller.
  888. ifdef QLOCK_STAT_GATHER
  889. fstCall KiQueueStatTryFailed
  890. endif
  891. xor eax, eax ; return FALSE
  892. fstRET KeTryToAcquireQueuedSpinLockAtRaisedIrql
  893. ; In the event that this is an MP kernel running with a UP
  894. ; HAL, the following UP version is copied over the MP version
  895. ; during kernel initialization.
  896. public _KeTryToAcquireQueuedSpinLockAtRaisedIrqlUP
  897. _KeTryToAcquireQueuedSpinLockAtRaisedIrqlUP:
  898. endif
  899. ; UP version, always succeed.
  900. xor eax, eax
  901. or eax, 1
  902. fstRet KeTryToAcquireQueuedSpinLockAtRaisedIrql
  903. fstENDP KeTryToAcquireQueuedSpinLockAtRaisedIrql
  904. _TEXT$00 ends
  905. end