1 /*
2 * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
3 * Copyright (c) 2020-2022 Huawei Device Co., Ltd. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this list of
9 * conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
12 * of conditions and the following disclaimer in the documentation and/or other materials
13 * provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
16 * to endorse or promote products derived from this software without specific prior written
17 * permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include "los_rwlock_pri.h"
33 #include "stdint.h"
34 #include "los_spinlock.h"
35 #include "los_mp.h"
36 #include "los_task_pri.h"
37 #include "los_exc.h"
38 #include "los_sched_pri.h"
39
40 #ifdef LOSCFG_BASE_IPC_RWLOCK
41 #define RWLOCK_COUNT_MASK 0x00FFFFFFU
42
LOS_RwlockIsValid(const LosRwlock * rwlock)43 BOOL LOS_RwlockIsValid(const LosRwlock *rwlock)
44 {
45 if ((rwlock != NULL) && ((rwlock->magic & RWLOCK_COUNT_MASK) == OS_RWLOCK_MAGIC)) {
46 return TRUE;
47 }
48
49 return FALSE;
50 }
51
LOS_RwlockInit(LosRwlock * rwlock)52 UINT32 LOS_RwlockInit(LosRwlock *rwlock)
53 {
54 UINT32 intSave;
55
56 if (rwlock == NULL) {
57 return LOS_EINVAL;
58 }
59
60 SCHEDULER_LOCK(intSave);
61 if ((rwlock->magic & RWLOCK_COUNT_MASK) == OS_RWLOCK_MAGIC) {
62 SCHEDULER_UNLOCK(intSave);
63 return LOS_EPERM;
64 }
65
66 rwlock->rwCount = 0;
67 rwlock->writeOwner = NULL;
68 LOS_ListInit(&(rwlock->readList));
69 LOS_ListInit(&(rwlock->writeList));
70 rwlock->magic = OS_RWLOCK_MAGIC;
71 SCHEDULER_UNLOCK(intSave);
72 return LOS_OK;
73 }
74
LOS_RwlockDestroy(LosRwlock * rwlock)75 UINT32 LOS_RwlockDestroy(LosRwlock *rwlock)
76 {
77 UINT32 intSave;
78
79 if (rwlock == NULL) {
80 return LOS_EINVAL;
81 }
82
83 SCHEDULER_LOCK(intSave);
84 if ((rwlock->magic & RWLOCK_COUNT_MASK) != OS_RWLOCK_MAGIC) {
85 SCHEDULER_UNLOCK(intSave);
86 return LOS_EBADF;
87 }
88
89 if (rwlock->rwCount != 0) {
90 SCHEDULER_UNLOCK(intSave);
91 return LOS_EBUSY;
92 }
93
94 (VOID)memset_s(rwlock, sizeof(LosRwlock), 0, sizeof(LosRwlock));
95 SCHEDULER_UNLOCK(intSave);
96 return LOS_OK;
97 }
98
OsRwlockCheck(const LosRwlock * rwlock)99 STATIC UINT32 OsRwlockCheck(const LosRwlock *rwlock)
100 {
101 if (rwlock == NULL) {
102 return LOS_EINVAL;
103 }
104
105 if (OS_INT_ACTIVE) {
106 return LOS_EINTR;
107 }
108
109 /* DO NOT Call blocking API in system tasks */
110 LosTaskCB *runTask = (LosTaskCB *)OsCurrTaskGet();
111 if (runTask->taskStatus & OS_TASK_FLAG_SYSTEM_TASK) {
112 return LOS_EPERM;
113 }
114
115 return LOS_OK;
116 }
117
OsRwlockPriCompare(LosTaskCB * runTask,LOS_DL_LIST * rwList)118 STATIC BOOL OsRwlockPriCompare(LosTaskCB *runTask, LOS_DL_LIST *rwList)
119 {
120 if (!LOS_ListEmpty(rwList)) {
121 LosTaskCB *highestTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(rwList));
122 if (OsSchedParamCompare(runTask, highestTask) < 0) {
123 return TRUE;
124 }
125 return FALSE;
126 }
127 return TRUE;
128 }
129
OsRwlockRdPendOp(LosTaskCB * runTask,LosRwlock * rwlock,UINT32 timeout)130 STATIC UINT32 OsRwlockRdPendOp(LosTaskCB *runTask, LosRwlock *rwlock, UINT32 timeout)
131 {
132 UINT32 ret;
133
134 /*
135 * When the rwlock mode is read mode or free mode and the priority of the current read task
136 * is higher than the first pended write task. current read task can obtain this rwlock.
137 */
138 if (rwlock->rwCount >= 0) {
139 if (OsRwlockPriCompare(runTask, &(rwlock->writeList))) {
140 if (rwlock->rwCount == INT8_MAX) {
141 return LOS_EINVAL;
142 }
143 rwlock->rwCount++;
144 return LOS_OK;
145 }
146 }
147
148 if (!timeout) {
149 return LOS_EINVAL;
150 }
151
152 if (!OsPreemptableInSched()) {
153 return LOS_EDEADLK;
154 }
155
156 /* The current task is not allowed to obtain the write lock when it obtains the read lock. */
157 if ((LosTaskCB *)(rwlock->writeOwner) == runTask) {
158 return LOS_EINVAL;
159 }
160
161 /*
162 * When the rwlock mode is write mode or the priority of the current read task
163 * is lower than the first pended write task, current read task will be pended.
164 */
165 LOS_DL_LIST *node = OsSchedLockPendFindPos(runTask, &(rwlock->readList));
166 ret = runTask->ops->wait(runTask, node, timeout);
167 if (ret == LOS_ERRNO_TSK_TIMEOUT) {
168 return LOS_ETIMEDOUT;
169 }
170
171 return ret;
172 }
173
OsRwlockWrPendOp(LosTaskCB * runTask,LosRwlock * rwlock,UINT32 timeout)174 STATIC UINT32 OsRwlockWrPendOp(LosTaskCB *runTask, LosRwlock *rwlock, UINT32 timeout)
175 {
176 UINT32 ret;
177
178 /* When the rwlock is free mode, current write task can obtain this rwlock. */
179 if (rwlock->rwCount == 0) {
180 rwlock->rwCount = -1;
181 rwlock->writeOwner = (VOID *)runTask;
182 return LOS_OK;
183 }
184
185 /* Current write task can use one rwlock once again if the rwlock owner is it. */
186 if ((rwlock->rwCount < 0) && ((LosTaskCB *)(rwlock->writeOwner) == runTask)) {
187 if (rwlock->rwCount == INT8_MIN) {
188 return LOS_EINVAL;
189 }
190 rwlock->rwCount--;
191 return LOS_OK;
192 }
193
194 if (!timeout) {
195 return LOS_EINVAL;
196 }
197
198 if (!OsPreemptableInSched()) {
199 return LOS_EDEADLK;
200 }
201
202 /*
203 * When the rwlock is read mode or other write task obtains this rwlock, current
204 * write task will be pended.
205 */
206 LOS_DL_LIST *node = OsSchedLockPendFindPos(runTask, &(rwlock->writeList));
207 ret = runTask->ops->wait(runTask, node, timeout);
208 if (ret == LOS_ERRNO_TSK_TIMEOUT) {
209 ret = LOS_ETIMEDOUT;
210 }
211
212 return ret;
213 }
214
OsRwlockRdUnsafe(LosRwlock * rwlock,UINT32 timeout)215 UINT32 OsRwlockRdUnsafe(LosRwlock *rwlock, UINT32 timeout)
216 {
217 if ((rwlock->magic & RWLOCK_COUNT_MASK) != OS_RWLOCK_MAGIC) {
218 return LOS_EBADF;
219 }
220
221 return OsRwlockRdPendOp(OsCurrTaskGet(), rwlock, timeout);
222 }
223
OsRwlockTryRdUnsafe(LosRwlock * rwlock,UINT32 timeout)224 UINT32 OsRwlockTryRdUnsafe(LosRwlock *rwlock, UINT32 timeout)
225 {
226 if ((rwlock->magic & RWLOCK_COUNT_MASK) != OS_RWLOCK_MAGIC) {
227 return LOS_EBADF;
228 }
229
230 LosTaskCB *runTask = OsCurrTaskGet();
231 if ((LosTaskCB *)(rwlock->writeOwner) == runTask) {
232 return LOS_EINVAL;
233 }
234
235 /*
236 * When the rwlock mode is read mode or free mode and the priority of the current read task
237 * is lower than the first pended write task, current read task can not obtain the rwlock.
238 */
239 if ((rwlock->rwCount >= 0) && !OsRwlockPriCompare(runTask, &(rwlock->writeList))) {
240 return LOS_EBUSY;
241 }
242
243 /*
244 * When the rwlock mode is write mode, current read task can not obtain the rwlock.
245 */
246 if (rwlock->rwCount < 0) {
247 return LOS_EBUSY;
248 }
249
250 return OsRwlockRdPendOp(runTask, rwlock, timeout);
251 }
252
OsRwlockWrUnsafe(LosRwlock * rwlock,UINT32 timeout)253 UINT32 OsRwlockWrUnsafe(LosRwlock *rwlock, UINT32 timeout)
254 {
255 if ((rwlock->magic & RWLOCK_COUNT_MASK) != OS_RWLOCK_MAGIC) {
256 return LOS_EBADF;
257 }
258
259 return OsRwlockWrPendOp(OsCurrTaskGet(), rwlock, timeout);
260 }
261
OsRwlockTryWrUnsafe(LosRwlock * rwlock,UINT32 timeout)262 UINT32 OsRwlockTryWrUnsafe(LosRwlock *rwlock, UINT32 timeout)
263 {
264 if ((rwlock->magic & RWLOCK_COUNT_MASK) != OS_RWLOCK_MAGIC) {
265 return LOS_EBADF;
266 }
267
268 /* When the rwlock is read mode, current write task will be pended. */
269 if (rwlock->rwCount > 0) {
270 return LOS_EBUSY;
271 }
272
273 /* When other write task obtains this rwlock, current write task will be pended. */
274 LosTaskCB *runTask = OsCurrTaskGet();
275 if ((rwlock->rwCount < 0) && ((LosTaskCB *)(rwlock->writeOwner) != runTask)) {
276 return LOS_EBUSY;
277 }
278
279 return OsRwlockWrPendOp(runTask, rwlock, timeout);
280 }
281
LOS_RwlockRdLock(LosRwlock * rwlock,UINT32 timeout)282 UINT32 LOS_RwlockRdLock(LosRwlock *rwlock, UINT32 timeout)
283 {
284 UINT32 intSave;
285
286 UINT32 ret = OsRwlockCheck(rwlock);
287 if (ret != LOS_OK) {
288 return ret;
289 }
290
291 SCHEDULER_LOCK(intSave);
292 ret = OsRwlockRdUnsafe(rwlock, timeout);
293 SCHEDULER_UNLOCK(intSave);
294 return ret;
295 }
296
LOS_RwlockTryRdLock(LosRwlock * rwlock)297 UINT32 LOS_RwlockTryRdLock(LosRwlock *rwlock)
298 {
299 UINT32 intSave;
300
301 UINT32 ret = OsRwlockCheck(rwlock);
302 if (ret != LOS_OK) {
303 return ret;
304 }
305
306 SCHEDULER_LOCK(intSave);
307 ret = OsRwlockTryRdUnsafe(rwlock, 0);
308 SCHEDULER_UNLOCK(intSave);
309 return ret;
310 }
311
LOS_RwlockWrLock(LosRwlock * rwlock,UINT32 timeout)312 UINT32 LOS_RwlockWrLock(LosRwlock *rwlock, UINT32 timeout)
313 {
314 UINT32 intSave;
315
316 UINT32 ret = OsRwlockCheck(rwlock);
317 if (ret != LOS_OK) {
318 return ret;
319 }
320
321 SCHEDULER_LOCK(intSave);
322 ret = OsRwlockWrUnsafe(rwlock, timeout);
323 SCHEDULER_UNLOCK(intSave);
324 return ret;
325 }
326
LOS_RwlockTryWrLock(LosRwlock * rwlock)327 UINT32 LOS_RwlockTryWrLock(LosRwlock *rwlock)
328 {
329 UINT32 intSave;
330
331 UINT32 ret = OsRwlockCheck(rwlock);
332 if (ret != LOS_OK) {
333 return ret;
334 }
335
336 SCHEDULER_LOCK(intSave);
337 ret = OsRwlockTryWrUnsafe(rwlock, 0);
338 SCHEDULER_UNLOCK(intSave);
339 return ret;
340 }
341
OsRwlockGetMode(LOS_DL_LIST * readList,LOS_DL_LIST * writeList)342 STATIC UINT32 OsRwlockGetMode(LOS_DL_LIST *readList, LOS_DL_LIST *writeList)
343 {
344 BOOL isReadEmpty = LOS_ListEmpty(readList);
345 BOOL isWriteEmpty = LOS_ListEmpty(writeList);
346 if (isReadEmpty && isWriteEmpty) {
347 return RWLOCK_NONE_MODE;
348 }
349 if (!isReadEmpty && isWriteEmpty) {
350 return RWLOCK_READ_MODE;
351 }
352 if (isReadEmpty && !isWriteEmpty) {
353 return RWLOCK_WRITE_MODE;
354 }
355 LosTaskCB *pendedReadTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(readList));
356 LosTaskCB *pendedWriteTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(writeList));
357 if (OsSchedParamCompare(pendedWriteTask, pendedReadTask) <= 0) {
358 return RWLOCK_WRITEFIRST_MODE;
359 }
360 return RWLOCK_READFIRST_MODE;
361 }
362
OsRwlockPostOp(LosRwlock * rwlock,BOOL * needSched)363 STATIC UINT32 OsRwlockPostOp(LosRwlock *rwlock, BOOL *needSched)
364 {
365 UINT32 rwlockMode;
366 LosTaskCB *resumedTask = NULL;
367
368 rwlock->rwCount = 0;
369 rwlock->writeOwner = NULL;
370 rwlockMode = OsRwlockGetMode(&(rwlock->readList), &(rwlock->writeList));
371 if (rwlockMode == RWLOCK_NONE_MODE) {
372 return LOS_OK;
373 }
374 /* In this case, rwlock will wake the first pended write task. */
375 if ((rwlockMode == RWLOCK_WRITE_MODE) || (rwlockMode == RWLOCK_WRITEFIRST_MODE)) {
376 resumedTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&(rwlock->writeList)));
377 rwlock->rwCount = -1;
378 rwlock->writeOwner = (VOID *)resumedTask;
379 resumedTask->ops->wake(resumedTask);
380 if (needSched != NULL) {
381 *needSched = TRUE;
382 }
383 return LOS_OK;
384 }
385
386 rwlock->rwCount = 1;
387 resumedTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&(rwlock->readList)));
388 resumedTask->ops->wake(resumedTask);
389 while (!LOS_ListEmpty(&(rwlock->readList))) {
390 resumedTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&(rwlock->readList)));
391 if (rwlockMode == RWLOCK_READFIRST_MODE) {
392 LosTaskCB *pendedWriteTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&(rwlock->writeList)));
393 if (OsSchedParamCompare(resumedTask, pendedWriteTask) >= 0) {
394 break;
395 }
396 }
397 if (rwlock->rwCount == INT8_MAX) {
398 return EINVAL;
399 }
400 rwlock->rwCount++;
401 resumedTask->ops->wake(resumedTask);
402 }
403 if (needSched != NULL) {
404 *needSched = TRUE;
405 }
406 return LOS_OK;
407 }
408
OsRwlockUnlockUnsafe(LosRwlock * rwlock,BOOL * needSched)409 UINT32 OsRwlockUnlockUnsafe(LosRwlock *rwlock, BOOL *needSched)
410 {
411 if ((rwlock->magic & RWLOCK_COUNT_MASK) != OS_RWLOCK_MAGIC) {
412 return LOS_EBADF;
413 }
414
415 if (rwlock->rwCount == 0) {
416 return LOS_EPERM;
417 }
418
419 LosTaskCB *runTask = OsCurrTaskGet();
420 if ((rwlock->rwCount < 0) && ((LosTaskCB *)(rwlock->writeOwner) != runTask)) {
421 return LOS_EPERM;
422 }
423
424 /*
425 * When the rwCount of the rwlock more than 1 or less than -1, the rwlock mode will
426 * not changed after current unlock operation, so pended tasks can not be waken.
427 */
428 if (rwlock->rwCount > 1) {
429 rwlock->rwCount--;
430 return LOS_OK;
431 }
432
433 if (rwlock->rwCount < -1) {
434 rwlock->rwCount++;
435 return LOS_OK;
436 }
437
438 return OsRwlockPostOp(rwlock, needSched);
439 }
440
LOS_RwlockUnLock(LosRwlock * rwlock)441 UINT32 LOS_RwlockUnLock(LosRwlock *rwlock)
442 {
443 UINT32 intSave;
444 BOOL needSched = FALSE;
445
446 UINT32 ret = OsRwlockCheck(rwlock);
447 if (ret != LOS_OK) {
448 return ret;
449 }
450
451 SCHEDULER_LOCK(intSave);
452 ret = OsRwlockUnlockUnsafe(rwlock, &needSched);
453 SCHEDULER_UNLOCK(intSave);
454 LOS_MpSchedule(OS_MP_CPU_ALL);
455 if (needSched == TRUE) {
456 LOS_Schedule();
457 }
458 return ret;
459 }
460
461 #endif /* LOSCFG_BASE_IPC_RWLOCK */
462
463