Source code of Windows XP (NT5)
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.

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