OSDN Git Service

First commitment for the BlackTank LPC1769.
[blacktank/blacktank.git] / kernel / extension / mutex / kernel / mutex.c
1 /*
2  *  TOPPERS/ASP Kernel
3  *      Toyohashi Open Platform for Embedded Real-Time Systems/
4  *      Advanced Standard Profile Kernel
5  * 
6  *  Copyright (C) 2005-2009 by Embedded and Real-Time Systems Laboratory
7  *              Graduate School of Information Science, Nagoya Univ., JAPAN
8  * 
9  *  上記著作権者は,以下の(1)〜(4)の条件を満たす場合に限り,本ソフトウェ
10  *  ア(本ソフトウェアを改変したものを含む.以下同じ)を使用・複製・改
11  *  変・再配布(以下,利用と呼ぶ)することを無償で許諾する.
12  *  (1) 本ソフトウェアをソースコードの形で利用する場合には,上記の著作
13  *      権表示,この利用条件および下記の無保証規定が,そのままの形でソー
14  *      スコード中に含まれていること.
15  *  (2) 本ソフトウェアを,ライブラリ形式など,他のソフトウェア開発に使
16  *      用できる形で再配布する場合には,再配布に伴うドキュメント(利用
17  *      者マニュアルなど)に,上記の著作権表示,この利用条件および下記
18  *      の無保証規定を掲載すること.
19  *  (3) 本ソフトウェアを,機器に組み込むなど,他のソフトウェア開発に使
20  *      用できない形で再配布する場合には,次のいずれかの条件を満たすこ
21  *      と.
22  *    (a) 再配布に伴うドキュメント(利用者マニュアルなど)に,上記の著
23  *        作権表示,この利用条件および下記の無保証規定を掲載すること.
24  *    (b) 再配布の形態を,別に定める方法によって,TOPPERSプロジェクトに
25  *        報告すること.
26  *  (4) 本ソフトウェアの利用により直接的または間接的に生じるいかなる損
27  *      害からも,上記著作権者およびTOPPERSプロジェクトを免責すること.
28  *      また,本ソフトウェアのユーザまたはエンドユーザからのいかなる理
29  *      由に基づく請求からも,上記著作権者およびTOPPERSプロジェクトを
30  *      免責すること.
31  * 
32  *  本ソフトウェアは,無保証で提供されているものである.上記著作権者お
33  *  よびTOPPERSプロジェクトは,本ソフトウェアに関して,特定の使用目的
34  *  に対する適合性も含めて,いかなる保証も行わない.また,本ソフトウェ
35  *  アの利用により直接的または間接的に生じたいかなる損害に関しても,そ
36  *  の責任を負わない.
37  * 
38  *  @(#) $Id: mutex.c 1694 2010-01-01 15:59:09Z ertl-hiro $
39  */
40
41 /*
42  *              ミューテックス機能
43  */
44
45 #include "kernel_impl.h"
46 #include "check.h"
47 #include "task.h"
48 #include "wait.h"
49 #include "mutex.h"
50
51 /*
52  *  トレースログマクロのデフォルト定義
53  */
54 #ifndef LOG_LOC_MTX_ENTER
55 #define LOG_LOC_MTX_ENTER(mtxid)
56 #endif /* LOG_LOC_MTX_ENTER */
57
58 #ifndef LOG_LOC_MTX_LEAVE
59 #define LOG_LOC_MTX_LEAVE(ercd)
60 #endif /* LOG_LOC_MTX_LEAVE */
61
62 #ifndef LOG_PLOC_MTX_ENTER
63 #define LOG_PLOC_MTX_ENTER(mtxid)
64 #endif /* LOG_PLOC_MTX_ENTER */
65
66 #ifndef LOG_PLOC_MTX_LEAVE
67 #define LOG_PLOC_MTX_LEAVE(ercd)
68 #endif /* LOG_PLOC_MTX_LEAVE */
69
70 #ifndef LOG_TLOC_MTX_ENTER
71 #define LOG_TLOC_MTX_ENTER(mtxid, tmout)
72 #endif /* LOG_TLOC_MTX_ENTER */
73
74 #ifndef LOG_TLOC_MTX_LEAVE
75 #define LOG_TLOC_MTX_LEAVE(ercd)
76 #endif /* LOG_TLOC_MTX_LEAVE */
77
78 #ifndef LOG_UNL_MTX_ENTER
79 #define LOG_UNL_MTX_ENTER(mtxid)
80 #endif /* LOG_UNL_MTX_ENTER */
81
82 #ifndef LOG_UNL_MTX_LEAVE
83 #define LOG_UNL_MTX_LEAVE(ercd)
84 #endif /* LOG_UNL_MTX_LEAVE */
85
86 #ifndef LOG_INI_MTX_ENTER
87 #define LOG_INI_MTX_ENTER(mtxid)
88 #endif /* LOG_INI_MTX_ENTER */
89
90 #ifndef LOG_INI_MTX_LEAVE
91 #define LOG_INI_MTX_LEAVE(ercd)
92 #endif /* LOG_INI_MTX_LEAVE */
93
94 #ifndef LOG_REF_MTX_ENTER
95 #define LOG_REF_MTX_ENTER(mtxid, pk_rmtx)
96 #endif /* LOG_REF_MTX_ENTER */
97
98 #ifndef LOG_REF_MTX_LEAVE
99 #define LOG_REF_MTX_LEAVE(ercd, pk_rmtx)
100 #endif /* LOG_REF_MTX_LEAVE */
101
102 /*
103  *  ミューテックスの数
104  */
105 #define tnum_mtx        ((uint_t)(tmax_mtxid - TMIN_MTXID + 1))
106
107 /*
108  *  ミューテックスIDからミューテックス管理ブロックを取り出すためのマクロ
109  */
110 #define INDEX_MTX(mtxid)        ((uint_t)((mtxid) - TMIN_MTXID))
111 #define get_mtxcb(mtxid)        (&(mtxcb_table[INDEX_MTX(mtxid)]))
112
113 /*
114  *  ミューテックス管理ブロック中のmutex_queueへのポインタから,ミューテッ
115  *  クス管理ブロックへのポインタを取り出すためのマクロ
116  */
117 #define MTXCB_QUEUE(p_queue) \
118                         ((MTXCB *)(((char *) p_queue) - offsetof(MTXCB, mutex_queue)))
119
120 /*
121  *  ミューテックスのプロトコルを判断するマクロ
122  */
123 #define MTXPROTO_MASK                   0x03U
124 #define MTXPROTO(p_mtxcb)               ((p_mtxcb)->p_mtxinib->mtxatr & MTXPROTO_MASK)
125 #define MTX_CEILING(p_mtxcb)    (MTXPROTO(p_mtxcb) == TA_CEILING)
126
127 /*
128  *  フックルーチン呼出し用の変数
129  */
130 #ifdef TOPPERS_mtxhook
131
132 bool_t  (*mtxhook_check_ceilpri)(TCB *p_tcb, uint_t bpriority) = NULL;
133 bool_t  (*mtxhook_scan_ceilmtx)(TCB *p_tcb) = NULL;
134 bool_t  (*mtxhook_release_all)(TCB *p_tcb) = NULL;
135
136 #endif /* TOPPERS_mtxhook */
137
138 /* 
139  *  ミューテックス機能の初期化
140  */
141 #ifdef TOPPERS_mtxini
142
143 void
144 initialize_mutex(void)
145 {
146         uint_t  i;
147         MTXCB   *p_mtxcb;
148
149         mtxhook_check_ceilpri = mutex_check_ceilpri;
150         mtxhook_scan_ceilmtx = mutex_scan_ceilmtx;
151         mtxhook_release_all = mutex_release_all;
152
153         for (p_mtxcb = mtxcb_table, i = 0; i < tnum_mtx; p_mtxcb++, i++) {
154                 queue_initialize(&(p_mtxcb->wait_queue));
155                 p_mtxcb->p_mtxinib = &(mtxinib_table[i]);
156                 p_mtxcb->p_loctsk = NULL;
157         }
158 }
159
160 #endif /* TOPPERS_mtxini */
161
162 /* 
163  *  上限優先度違反のチェック
164  */
165 #ifdef TOPPERS_mtxchk
166
167 bool_t
168 mutex_check_ceilpri(TCB *p_tcb, uint_t bpriority)
169 {
170         QUEUE   *p_queue;
171         MTXCB   *p_mtxcb;
172
173         /*
174          *  タスクがロックしている優先度上限ミューテックスの中で,上限優先
175          *  度がbpriorityよりも低いものがあれば,falseを返す.
176          */
177         p_queue = p_tcb->mutex_queue.p_next;
178         while (p_queue != &(p_tcb->mutex_queue)) {
179                 p_mtxcb = MTXCB_QUEUE(p_queue);
180                 if (MTX_CEILING(p_mtxcb) && bpriority < p_mtxcb->p_mtxinib->ceilpri) {
181                         return(false);
182                 }
183                 p_queue = p_queue->p_next;
184         }
185
186         /*
187          *  タスクが優先度上限ミューテックスのロックを待っている場合に,そ
188          *  の上限優先度がbpriorityよりも低くければ,falseを返す.
189          */
190         if (TSTAT_WAIT_MTX(p_tcb->tstat)) {
191                 p_mtxcb = ((WINFO_MTX *)(p_tcb->p_winfo))->p_mtxcb;
192                 if (MTX_CEILING(p_mtxcb) && bpriority < p_mtxcb->p_mtxinib->ceilpri) {
193                         return(false);
194                 }
195         }
196
197         /*
198          *  いずれの条件にも当てはまらなければtrueを返す.
199          */
200         return(true);
201 }
202
203 #endif /* TOPPERS_mtxchk */
204
205 /* 
206  *  優先度上限ミューテックスをロックしているかのチェック
207  */
208 #ifdef TOPPERS_mtxscan
209
210 bool_t
211 mutex_scan_ceilmtx(TCB *p_tcb)
212 {
213         QUEUE   *p_queue;
214         MTXCB   *p_mtxcb;
215
216         p_queue = p_tcb->mutex_queue.p_next;
217         while (p_queue != &(p_tcb->mutex_queue)) {
218                 p_mtxcb = MTXCB_QUEUE(p_queue);
219                 if (MTX_CEILING(p_mtxcb)) {
220                         return(true);
221                 }
222                 p_queue = p_queue->p_next;
223         }
224         return(false);
225 }
226
227 #endif /* TOPPERS_mtxscan */
228
229 /* 
230  *  タスクの現在優先度の計算
231  */
232 #ifdef TOPPERS_mtxcalc
233
234 uint_t
235 mutex_calc_priority(TCB *p_tcb)
236 {
237         uint_t  priority;
238         QUEUE   *p_queue;
239         MTXCB   *p_mtxcb;
240
241         priority = p_tcb->bpriority;
242         p_queue = p_tcb->mutex_queue.p_next;
243         while (p_queue != &(p_tcb->mutex_queue)) {
244                 p_mtxcb = MTXCB_QUEUE(p_queue);
245                 if (MTX_CEILING(p_mtxcb) && p_mtxcb->p_mtxinib->ceilpri < priority) {
246                         priority = p_mtxcb->p_mtxinib->ceilpri;
247                 }
248                 p_queue = p_queue->p_next;
249         }
250         return(priority);
251 }
252
253 #endif /* TOPPERS_mtxcalc */
254
255 /*
256  *  要素優先度が上がる(または増える)場合の現在優先度変更処理
257  */
258 Inline bool_t
259 mutex_raise_priority(TCB *p_tcb, uint_t newpri)
260 {
261         if (newpri < p_tcb->priority) {
262                 return(change_priority(p_tcb, newpri, true));
263         }
264         return(false);
265 }
266
267 /*
268  *  要素優先度が下がる(または減る)場合の現在優先度変更処理
269  */
270 Inline bool_t
271 mutex_drop_priority(TCB *p_tcb, uint_t oldpri)
272 {
273         uint_t  newpri;
274
275         if (oldpri == p_tcb->priority) {
276                 newpri = mutex_calc_priority(p_tcb);
277                 if (newpri != p_tcb->priority) {
278                         return(change_priority(p_tcb, newpri, true));
279                 }
280         }
281         return(false);
282 }
283
284 /*
285  *  ミューテックスをロックした場合の処理
286  */
287 Inline bool_t
288 mutex_acquire(TCB *p_loctsk, MTXCB *p_mtxcb)
289 {
290         p_mtxcb->p_loctsk = p_loctsk;
291         queue_insert_prev(&(p_loctsk->mutex_queue), &(p_mtxcb->mutex_queue));
292         if (MTX_CEILING(p_mtxcb)) {
293                 return(mutex_raise_priority(p_loctsk, p_mtxcb->p_mtxinib->ceilpri));
294         }
295         return(false);
296 }
297
298 /*
299  *  ミューテックスのロック解除
300  */
301 #ifdef TOPPERS_mtxrel
302
303 bool_t
304 mutex_release(MTXCB *p_mtxcb)
305 {
306         TCB             *p_tcb;
307
308         if (queue_empty(&(p_mtxcb->wait_queue))) {
309                 p_mtxcb->p_loctsk = NULL;
310                 return(false);
311         }
312         else {
313                 /*
314                  *  ミューテックス待ちキューの先頭タスク(p_tcb)に,ミューテッ
315                  *  クスをロックさせる.
316                  */
317                 p_tcb = (TCB *) queue_delete_next(&(p_mtxcb->wait_queue));
318                 wait_dequeue_tmevtb(p_tcb);
319                 p_tcb->p_winfo->wercd = E_OK;
320
321                 p_mtxcb->p_loctsk = p_tcb;
322                 queue_insert_prev(&(p_tcb->mutex_queue), &(p_mtxcb->mutex_queue));
323                 if (MTX_CEILING(p_mtxcb)) {
324                         if (p_mtxcb->p_mtxinib->ceilpri < p_tcb->priority) {
325                                 p_tcb->priority = p_mtxcb->p_mtxinib->ceilpri;
326                         }
327                 }
328                 return(make_non_wait(p_tcb));
329         }
330 }
331
332 #endif /* TOPPERS_mtxrel */
333
334 /*
335  *  タスクがロックしているすべてのミューテックスのロック解除
336  */
337 #ifdef TOPPERS_mtxrela
338
339 bool_t
340 mutex_release_all(TCB *p_tcb)
341 {
342         MTXCB   *p_mtxcb;
343         bool_t  dspreq = false;
344
345         while (!queue_empty(&(p_tcb->mutex_queue))) {
346                 p_mtxcb = MTXCB_QUEUE(p_tcb->mutex_queue.p_next);
347                 queue_delete(&(p_mtxcb->mutex_queue));
348                 if (mutex_release(p_mtxcb)) {
349                         dspreq = true;
350                 }
351         }
352         return(dspreq);
353 }
354
355 #endif /* TOPPERS_mtxrela */
356
357 /*
358  *  ミューテックスのロック
359  */
360 #ifdef TOPPERS_loc_mtx
361
362 ER
363 loc_mtx(ID mtxid)
364 {
365         MTXCB   *p_mtxcb;
366         WINFO_MTX winfo_mtx;
367         ER              ercd;
368
369         LOG_LOC_MTX_ENTER(mtxid);
370         CHECK_DISPATCH();
371         CHECK_MTXID(mtxid);
372         p_mtxcb = get_mtxcb(mtxid);
373
374         t_lock_cpu();
375         if (MTX_CEILING(p_mtxcb)
376                                 && p_runtsk->bpriority < p_mtxcb->p_mtxinib->ceilpri) {
377                 ercd = E_ILUSE;
378         }
379         else if (p_mtxcb->p_loctsk == NULL) {
380                 (void) mutex_acquire(p_runtsk, p_mtxcb);
381                 /*
382                  *  優先度上限ミューテックスをロックした場合,p_runtskの優先度
383                  *  が上がる可能性があるが,ディスパッチが必要になることはない.
384                  */
385                 assert(!(p_runtsk != p_schedtsk && dspflg));
386                 ercd = E_OK;
387         }
388         else if (p_mtxcb->p_loctsk == p_runtsk) {
389                 ercd = E_ILUSE;
390         }
391         else {
392                 p_runtsk->tstat = (TS_WAITING | TS_WAIT_MTX);
393                 wobj_make_wait((WOBJCB *) p_mtxcb, (WINFO_WOBJ *) &winfo_mtx);
394                 dispatch();
395                 ercd = winfo_mtx.winfo.wercd;
396         }
397         t_unlock_cpu();
398
399   error_exit:
400         LOG_LOC_MTX_LEAVE(ercd);
401         return(ercd);
402 }
403
404 #endif /* TOPPERS_loc_mtx */
405
406 /*
407  *  ミューテックスのロック(ポーリング)
408  */
409 #ifdef TOPPERS_ploc_mtx
410
411 ER
412 ploc_mtx(ID mtxid)
413 {
414         MTXCB   *p_mtxcb;
415         ER              ercd;
416
417         LOG_PLOC_MTX_ENTER(mtxid);
418         CHECK_TSKCTX_UNL();
419         CHECK_MTXID(mtxid);
420         p_mtxcb = get_mtxcb(mtxid);
421
422         t_lock_cpu();
423         if (MTX_CEILING(p_mtxcb)
424                                 && p_runtsk->bpriority < p_mtxcb->p_mtxinib->ceilpri) {
425                 ercd = E_ILUSE;
426         }
427         else if (p_mtxcb->p_loctsk == NULL) {
428                 (void) mutex_acquire(p_runtsk, p_mtxcb);
429                 /*
430                  *  優先度上限ミューテックスをロックした場合,p_runtskの優先度
431                  *  が上がる可能性があるが,ディスパッチが必要になることはない.
432                  */
433                 assert(!(p_runtsk != p_schedtsk && dspflg));
434                 ercd = E_OK;
435         }
436         else if (p_mtxcb->p_loctsk == p_runtsk) {
437                 ercd = E_ILUSE;
438         }
439         else {
440                 ercd = E_TMOUT;
441         }
442         t_unlock_cpu();
443
444   error_exit:
445         LOG_PLOC_MTX_LEAVE(ercd);
446         return(ercd);
447 }
448
449 #endif /* TOPPERS_ploc_mtx */
450
451 /*
452  *  ミューテックスのロック(タイムアウトあり)
453  */
454 #ifdef TOPPERS_tloc_mtx
455
456 ER
457 tloc_mtx(ID mtxid, TMO tmout)
458 {
459         MTXCB   *p_mtxcb;
460         WINFO_MTX winfo_mtx;
461         TMEVTB  tmevtb;
462         ER              ercd;
463
464         LOG_TLOC_MTX_ENTER(mtxid, tmout);
465         CHECK_DISPATCH();
466         CHECK_MTXID(mtxid);
467         CHECK_TMOUT(tmout);
468         p_mtxcb = get_mtxcb(mtxid);
469
470         t_lock_cpu();
471         if (MTX_CEILING(p_mtxcb)
472                                 && p_runtsk->bpriority < p_mtxcb->p_mtxinib->ceilpri) {
473                 ercd = E_ILUSE;
474         }
475         else if (p_mtxcb->p_loctsk == NULL) {
476                 (void) mutex_acquire(p_runtsk, p_mtxcb);
477                 /*
478                  *  優先度上限ミューテックスをロックした場合,p_runtskの優先度
479                  *  が上がる可能性があるが,ディスパッチが必要になることはない.
480                  */
481                 assert(!(p_runtsk != p_schedtsk && dspflg));
482                 ercd = E_OK;
483         }
484         else if (p_mtxcb->p_loctsk == p_runtsk) {
485                 ercd = E_ILUSE;
486         }
487         else if (tmout == TMO_POL) {
488                 ercd = E_TMOUT;
489         }
490         else {
491                 p_runtsk->tstat = (TS_WAITING | TS_WAIT_MTX);
492                 wobj_make_wait_tmout((WOBJCB *) p_mtxcb, (WINFO_WOBJ *) &winfo_mtx,
493                                                                                                                 &tmevtb, tmout);
494                 dispatch();
495                 ercd = winfo_mtx.winfo.wercd;
496         }
497         t_unlock_cpu();
498
499   error_exit:
500         LOG_TLOC_MTX_LEAVE(ercd);
501         return(ercd);
502 }
503
504 #endif /* TOPPERS_tloc_mtx */
505
506 /*
507  *  ミューテックスのロック解除
508  */
509 #ifdef TOPPERS_unl_mtx
510
511 ER
512 unl_mtx(ID mtxid)
513 {
514         MTXCB   *p_mtxcb;
515         bool_t  dspreq = false;
516         ER              ercd;
517     
518         LOG_UNL_MTX_ENTER(mtxid);
519         CHECK_TSKCTX_UNL();
520         CHECK_MTXID(mtxid);
521         p_mtxcb = get_mtxcb(mtxid);
522
523         t_lock_cpu();
524         if (p_mtxcb->p_loctsk != p_runtsk) {
525                 ercd = E_ILUSE;
526         }
527         else {
528                 queue_delete(&(p_mtxcb->mutex_queue));
529                 if (MTX_CEILING(p_mtxcb)) {
530                         if (mutex_drop_priority(p_runtsk, p_mtxcb->p_mtxinib->ceilpri)) {
531                                 dspreq = true;
532                         }
533                 }
534                 if (mutex_release(p_mtxcb)) {
535                         dspreq = true;
536                 }
537                 if (dspreq) {
538                         dispatch();
539                 }
540                 ercd = E_OK;
541         }
542         t_unlock_cpu();
543
544   error_exit:
545         LOG_UNL_MTX_LEAVE(ercd);
546         return(ercd);
547 }
548
549 #endif /* TOPPERS_unl_mtx */
550
551 /*
552  *  ミューテックスの初期化
553  */
554 #ifdef TOPPERS_ini_mtx
555
556 ER
557 ini_mtx(ID mtxid)
558 {
559         MTXCB   *p_mtxcb;
560         TCB             *p_loctsk;
561         bool_t  dspreq;
562         ER              ercd;
563     
564         LOG_INI_MTX_ENTER(mtxid);
565         CHECK_TSKCTX_UNL();
566         CHECK_MTXID(mtxid);
567         p_mtxcb = get_mtxcb(mtxid);
568
569         t_lock_cpu();
570         dspreq = init_wait_queue(&(p_mtxcb->wait_queue));
571         p_loctsk = p_mtxcb->p_loctsk;
572         if (p_loctsk != NULL) {
573                 queue_delete(&(p_mtxcb->mutex_queue));
574                 p_mtxcb->p_loctsk = NULL;
575                 if (MTX_CEILING(p_mtxcb)) {
576                         if (mutex_drop_priority(p_loctsk, p_mtxcb->p_mtxinib->ceilpri)) {
577                                 dspreq = true;
578                         }
579                 }
580         }
581         if (dspreq) {
582                 dispatch();
583         }
584         ercd = E_OK;
585         t_unlock_cpu();
586
587   error_exit:
588         LOG_INI_MTX_LEAVE(ercd);
589         return(ercd);
590 }
591
592 #endif /* TOPPERS_ini_mtx */
593
594 /*
595  *  ミューテックスの状態参照
596  */
597 #ifdef TOPPERS_ref_mtx
598
599 ER
600 ref_mtx(ID mtxid, T_RMTX *pk_rmtx)
601 {
602         MTXCB   *p_mtxcb;
603         ER              ercd;
604     
605         LOG_REF_MTX_ENTER(mtxid, pk_rmtx);
606         CHECK_TSKCTX_UNL();
607         CHECK_MTXID(mtxid);
608         p_mtxcb = get_mtxcb(mtxid);
609
610         t_lock_cpu();
611         pk_rmtx->htskid = (p_mtxcb->p_loctsk != NULL) ? TSKID(p_mtxcb->p_loctsk)
612                                                                                                         : TSK_NONE;
613         pk_rmtx->wtskid = wait_tskid(&(p_mtxcb->wait_queue));
614         ercd = E_OK;
615         t_unlock_cpu();
616
617   error_exit:
618         LOG_REF_MTX_LEAVE(ercd, pk_rmtx);
619         return(ercd);
620 }
621
622 #endif /* TOPPERS_ref_mtx */