usrlock.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  1. /*++
  2. Copyright (c) 2015 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. usrlock.c
  9. Abstract:
  10. This module implements kernel support for user mode locking.
  11. Author:
  12. Evan Green 24-Apr-2015
  13. Environment:
  14. Kernel
  15. --*/
  16. //
  17. // ------------------------------------------------------------------- Includes
  18. //
  19. #include <minoca/kernel/kernel.h>
  20. #include "psp.h"
  21. //
  22. // ---------------------------------------------------------------- Definitions
  23. //
  24. //
  25. // ------------------------------------------------------ Data Type Definitions
  26. //
  27. typedef enum _USER_LOCK_TYPE {
  28. UserLockTypeInvalid,
  29. UserLockTypeProcess,
  30. UserLockTypeImageSection,
  31. UserLockTypeFileObject,
  32. } USER_LOCK_TYPE, *PUSER_LOCK_TYPE;
  33. /*++
  34. Structure Description:
  35. This structure defines a user mode lock, which is basically just a wait
  36. queue that can be looked up.
  37. Members:
  38. TreeNode - Stores the accounting structure for keeping the entry in a
  39. Red-Black tree.
  40. Object - Stores a pointer to the object this lock is tied to. This is a
  41. process for a process local lock, an image section for a lock in a
  42. private memory region, or a file object in a shared memory region.
  43. Offset - Stores either 1) the offset into the file object, 2) the offset
  44. into the image section, or 3) the user mode address in the process
  45. address space, depending on the type of lock.
  46. ReferenceCount - Stores the reference count of the object.
  47. Type - Stores the object type, used when trying to release the lock.
  48. WaitQueue - Stores the wait queue itself.
  49. --*/
  50. typedef struct _USER_LOCK {
  51. RED_BLACK_TREE_NODE TreeNode;
  52. PVOID Object;
  53. UINTN Offset;
  54. USER_LOCK_TYPE Type;
  55. WAIT_QUEUE WaitQueue;
  56. } USER_LOCK, *PUSER_LOCK;
  57. //
  58. // ----------------------------------------------- Internal Function Prototypes
  59. //
  60. KSTATUS
  61. PspUserLockWait (
  62. PSYSTEM_CALL_USER_LOCK Parameters
  63. );
  64. KSTATUS
  65. PspUserLockWake (
  66. PSYSTEM_CALL_USER_LOCK Parameters
  67. );
  68. KSTATUS
  69. PspInitializeUserLock (
  70. PVOID Address,
  71. BOOL Private,
  72. PUSER_LOCK Lock
  73. );
  74. VOID
  75. PspReleaseUserLockObject (
  76. PUSER_LOCK Lock
  77. );
  78. COMPARISON_RESULT
  79. PspCompareUserLocks (
  80. PRED_BLACK_TREE Tree,
  81. PRED_BLACK_TREE_NODE FirstNode,
  82. PRED_BLACK_TREE_NODE SecondNode
  83. );
  84. //
  85. // -------------------------------------------------------------------- Globals
  86. //
  87. PQUEUED_LOCK PsUserLockLock;
  88. RED_BLACK_TREE PsUserLockTree;
  89. //
  90. // ------------------------------------------------------------------ Functions
  91. //
  92. INTN
  93. PsSysUserLock (
  94. PVOID SystemCallParameter
  95. )
  96. /*++
  97. Routine Description:
  98. This routine implements the system call for user mode locking.
  99. Arguments:
  100. SystemCallParameter - Supplies a pointer to the parameters supplied with
  101. the system call. This structure will be a stack-local copy of the
  102. actual parameters passed from user-mode.
  103. Return Value:
  104. STATUS_SUCCESS or positive integer on success.
  105. Error status code on failure.
  106. --*/
  107. {
  108. ULONG Operation;
  109. PSYSTEM_CALL_USER_LOCK Parameters;
  110. KSTATUS Status;
  111. Parameters = SystemCallParameter;
  112. Operation = Parameters->Operation & USER_LOCK_OPERATION_MASK;
  113. switch (Operation) {
  114. case UserLockWait:
  115. Status = PspUserLockWait(Parameters);
  116. break;
  117. case UserLockWake:
  118. Status = PspUserLockWake(Parameters);
  119. break;
  120. default:
  121. Status = STATUS_INVALID_PARAMETER;
  122. break;
  123. }
  124. return Status;
  125. }
  126. VOID
  127. PspInitializeUserLocking (
  128. VOID
  129. )
  130. /*++
  131. Routine Description:
  132. This routine sets up the user locking subsystem.
  133. Arguments:
  134. None.
  135. Return Value:
  136. None.
  137. --*/
  138. {
  139. PsUserLockLock = KeCreateQueuedLock();
  140. ASSERT(PsUserLockLock != NULL);
  141. RtlRedBlackTreeInitialize(&PsUserLockTree, 0, PspCompareUserLocks);
  142. return;
  143. }
  144. KSTATUS
  145. PspUserLockWake (
  146. PSYSTEM_CALL_USER_LOCK Parameters
  147. )
  148. /*++
  149. Routine Description:
  150. This routine wakes up those blocked on the given user mode address.
  151. Arguments:
  152. Parameters - Supplies a pointer to the wait parameters.
  153. Return Value:
  154. Status code.
  155. --*/
  156. {
  157. PUSER_LOCK FoundLock;
  158. PRED_BLACK_TREE_NODE FoundNode;
  159. USER_LOCK Lock;
  160. BOOL Private;
  161. ULONG ProcessesReleased;
  162. KSTATUS Status;
  163. Private = FALSE;
  164. if ((Parameters->Operation & USER_LOCK_PRIVATE) != 0) {
  165. Private = TRUE;
  166. }
  167. Status = PspInitializeUserLock(Parameters->Address, Private, &Lock);
  168. if (!KSUCCESS(Status)) {
  169. return Status;
  170. }
  171. //
  172. // Release the specified number of processes.
  173. //
  174. ProcessesReleased = 0;
  175. KeAcquireQueuedLock(PsUserLockLock);
  176. while (Parameters->Value != 0) {
  177. FoundNode = RtlRedBlackTreeSearch(&PsUserLockTree, &(Lock.TreeNode));
  178. if (FoundNode == NULL) {
  179. break;
  180. }
  181. //
  182. // Remove it from the tree first. The locks are stack allocated, so as
  183. // soon as the thread is made ready the memory could go invalid.
  184. //
  185. FoundLock = RED_BLACK_TREE_VALUE(FoundNode, USER_LOCK, TreeNode);
  186. RtlRedBlackTreeRemove(&PsUserLockTree, FoundNode);
  187. ObSignalQueue(&(FoundLock->WaitQueue), SignalOptionSignalAll);
  188. //
  189. // The object can go away as soon as it's known to be removed from the
  190. // tree. Make sure this thread is done touching the object before
  191. // indicating to the woken thread that it can destroy this memory.
  192. //
  193. FoundNode->Parent = NULL;
  194. ProcessesReleased += 1;
  195. if (Parameters->Value != MAX_ULONG) {
  196. Parameters->Value -= 1;
  197. }
  198. }
  199. KeReleaseQueuedLock(PsUserLockLock);
  200. PspReleaseUserLockObject(&Lock);
  201. Parameters->Value = ProcessesReleased;
  202. return STATUS_SUCCESS;
  203. }
  204. //
  205. // --------------------------------------------------------- Internal Functions
  206. //
  207. KSTATUS
  208. PspUserLockWait (
  209. PSYSTEM_CALL_USER_LOCK Parameters
  210. )
  211. /*++
  212. Routine Description:
  213. This routine performs a wait on the user lock.
  214. Arguments:
  215. Parameters - Supplies a pointer to the wait parameters.
  216. Return Value:
  217. Status code.
  218. --*/
  219. {
  220. ULONGLONG ElapsedTimeInMilliseconds;
  221. ULONGLONG EndTime;
  222. ULONGLONG Frequency;
  223. USER_LOCK Lock;
  224. BOOL Private;
  225. ULONGLONG StartTime;
  226. KSTATUS Status;
  227. ULONG UserValue;
  228. Private = FALSE;
  229. if ((Parameters->Operation & USER_LOCK_PRIVATE) != 0) {
  230. Private = TRUE;
  231. }
  232. Status = PspInitializeUserLock(Parameters->Address, Private, &Lock);
  233. if (!KSUCCESS(Status)) {
  234. return Status;
  235. }
  236. ObInitializeWaitQueue(&(Lock.WaitQueue), NotSignaled);
  237. KeAcquireQueuedLock(PsUserLockLock);
  238. //
  239. // If the read failed, then bail out.
  240. //
  241. if (MmUserRead32(Parameters->Address, &UserValue) == FALSE) {
  242. Status = STATUS_ACCESS_VIOLATION;
  243. } else {
  244. //
  245. // If the value changed between the time user mode started to ask for
  246. // a wait and now, bail out.
  247. //
  248. if (UserValue != Parameters->Value) {
  249. Status = STATUS_OPERATION_WOULD_BLOCK;
  250. //
  251. // The value is the same, commit to going down.
  252. //
  253. } else {
  254. Status = STATUS_SUCCESS;
  255. RtlRedBlackTreeInsert(&PsUserLockTree, &(Lock.TreeNode));
  256. }
  257. }
  258. KeReleaseQueuedLock(PsUserLockLock);
  259. if (!KSUCCESS(Status)) {
  260. goto UserLockWaitEnd;
  261. }
  262. //
  263. // Wait for somebody to wake this thread (or a signal, or a timeout).
  264. //
  265. ASSERT(SYS_WAIT_TIME_INDEFINITE == WAIT_TIME_INDEFINITE);
  266. StartTime = 0;
  267. if (Parameters->TimeoutInMilliseconds != SYS_WAIT_TIME_INDEFINITE) {
  268. StartTime = KeGetRecentTimeCounter();
  269. }
  270. Status = ObWaitOnQueue(&(Lock.WaitQueue),
  271. WAIT_FLAG_INTERRUPTIBLE,
  272. Parameters->TimeoutInMilliseconds);
  273. //
  274. // If a user lock wait is interrupted by a signal, allow it to restart
  275. // after the signal is applied if the handler allows restarts. Update the
  276. // timeout, so the next round doesn't wait too long.
  277. //
  278. if (Status == STATUS_INTERRUPTED) {
  279. if (Parameters->TimeoutInMilliseconds != SYS_WAIT_TIME_INDEFINITE) {
  280. EndTime = KeGetRecentTimeCounter();
  281. Frequency = HlQueryTimeCounterFrequency();
  282. ElapsedTimeInMilliseconds = ((EndTime - StartTime) *
  283. MILLISECONDS_PER_SECOND) /
  284. Frequency;
  285. if (ElapsedTimeInMilliseconds < Parameters->TimeoutInMilliseconds) {
  286. Parameters->TimeoutInMilliseconds -= ElapsedTimeInMilliseconds;
  287. } else {
  288. Parameters->TimeoutInMilliseconds = 0;
  289. }
  290. }
  291. Status = STATUS_RESTART_AFTER_SIGNAL;
  292. }
  293. //
  294. // Remove the object from the tree, racing with the parent who may
  295. // have already done it to save the extra lock acquire.
  296. //
  297. if (Lock.TreeNode.Parent != NULL) {
  298. KeAcquireQueuedLock(PsUserLockLock);
  299. if (Lock.TreeNode.Parent != NULL) {
  300. RtlRedBlackTreeRemove(&PsUserLockTree, &(Lock.TreeNode));
  301. Lock.TreeNode.Parent = NULL;
  302. }
  303. KeReleaseQueuedLock(PsUserLockLock);
  304. }
  305. UserLockWaitEnd:
  306. PspReleaseUserLockObject(&Lock);
  307. return Status;
  308. }
  309. KSTATUS
  310. PspInitializeUserLock (
  311. PVOID Address,
  312. BOOL Private,
  313. PUSER_LOCK Lock
  314. )
  315. /*++
  316. Routine Description:
  317. This routine initializes the user lock state.
  318. Arguments:
  319. Address - Supplies a pointer to the usermode address to contend on.
  320. Private - Supplies a boolean indicating whether or not the lock is
  321. private to the process (TRUE) or potentially shared between multiple
  322. processes (FALSE).
  323. Lock - Supplies a pointer where the initialized lock structure will be
  324. returned on success.
  325. Return Value:
  326. Status code.
  327. --*/
  328. {
  329. BOOL Shared;
  330. if (Private != FALSE) {
  331. Lock->Object = PsGetCurrentProcess();
  332. Lock->Offset = (UINTN)Address;
  333. Lock->Type = UserLockTypeProcess;
  334. } else {
  335. Lock->Object = MmGetObjectForAddress(Address, &(Lock->Offset), &Shared);
  336. if (Lock->Object == NULL) {
  337. return STATUS_ACCESS_VIOLATION;
  338. }
  339. if (Shared != FALSE) {
  340. Lock->Type = UserLockTypeFileObject;
  341. } else {
  342. Lock->Type = UserLockTypeImageSection;
  343. }
  344. }
  345. return STATUS_SUCCESS;
  346. }
  347. VOID
  348. PspReleaseUserLockObject (
  349. PUSER_LOCK Lock
  350. )
  351. /*++
  352. Routine Description:
  353. This routine releases the reference on a user lock backing object, which
  354. is either a process, image section, or file object.
  355. Arguments:
  356. Lock - Supplies a pointer to the lock being torn down.
  357. Return Value:
  358. None.
  359. --*/
  360. {
  361. BOOL Shared;
  362. Shared = FALSE;
  363. switch (Lock->Type) {
  364. case UserLockTypeProcess:
  365. break;
  366. case UserLockTypeFileObject:
  367. Shared = TRUE;
  368. //
  369. // Fall through.
  370. //
  371. case UserLockTypeImageSection:
  372. MmReleaseObjectReference(Lock->Object, Shared);
  373. break;
  374. default:
  375. ASSERT(FALSE);
  376. break;
  377. }
  378. return;
  379. }
  380. COMPARISON_RESULT
  381. PspCompareUserLocks (
  382. PRED_BLACK_TREE Tree,
  383. PRED_BLACK_TREE_NODE FirstNode,
  384. PRED_BLACK_TREE_NODE SecondNode
  385. )
  386. /*++
  387. Routine Description:
  388. This routine compares two Red-Black tree nodes that in this case are user
  389. mode lock objects.
  390. Arguments:
  391. Tree - Supplies a pointer to the Red-Black tree that owns both nodes.
  392. FirstNode - Supplies a pointer to the left side of the comparison.
  393. SecondNode - Supplies a pointer to the second side of the comparison.
  394. Return Value:
  395. Same if the two nodes have the same value.
  396. Ascending if the first node is less than the second node.
  397. Descending if the second node is less than the first node.
  398. --*/
  399. {
  400. PUSER_LOCK FirstLock;
  401. PUSER_LOCK SecondLock;
  402. //
  403. // Compare raw pointers to the objects first.
  404. //
  405. FirstLock = RED_BLACK_TREE_VALUE(FirstNode, USER_LOCK, TreeNode);
  406. SecondLock = RED_BLACK_TREE_VALUE(SecondNode, USER_LOCK, TreeNode);
  407. if (FirstLock->Object < SecondLock->Object) {
  408. return ComparisonResultAscending;
  409. } else if (FirstLock->Object > SecondLock->Object) {
  410. return ComparisonResultDescending;
  411. } else if (FirstLock->Offset > SecondLock->Offset) {
  412. return ComparisonResultDescending;
  413. } else if (FirstLock->Offset < SecondLock->Offset) {
  414. return ComparisonResultAscending;
  415. }
  416. return ComparisonResultSame;
  417. }