source: trunk/kernel/libk/rwlock.c @ 631

Last change on this file since 631 was 629, checked in by alain, 6 years ago

Remove the "giant" rwlock protecting the GPT, and
use the GPT_LOCKED attribute in each PTE to prevent
concurrent modifications of one GPT entry.
The version number has been incremented to 2.1.

File size: 10.5 KB
Line 
1/*
2 * rwlock.c - kernel local read/write lock implementation.
3 *
4 * Author  Alain Greiner     (2016,2017,2018,2019)
5 *
6 * Copyright (c) UPMC Sorbonne Universites
7 *
8 * This file is part of ALMOS-MKH.
9 *
10 * ALMOS-MKH is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; version 2.0 of the License.
13 *
14 * ALMOS-MKH is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with ALMOS-MKH; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24#include <kernel_config.h>
25#include <hal_kernel_types.h>
26#include <hal_atomic.h>
27#include <hal_special.h>
28#include <hal_irqmask.h>
29#include <thread.h>
30#include <printk.h>
31#include <rwlock.h>
32
33//////////////////////////////////////////////////////////////////////////////
34//                Extern global variables
35//////////////////////////////////////////////////////////////////////////////
36
37extern char * lock_type_str[];          // allocated in kernel_init.c
38
39
40//////////////////////////////////
41void rwlock_init( rwlock_t * lock,
42                  uint32_t   type )
43{ 
44        lock->taken   = 0;
45    lock->count   = 0;
46
47    list_root_init( &lock->rd_root );
48    list_root_init( &lock->wr_root );
49
50    busylock_init( &lock->lock , type );
51
52#if DEBUG_RWLOCK_TYPE
53thread_t * this = CURRENT_THREAD;
54if( (type           == DEBUG_RWLOCK_TYPE) &&
55    ((intptr_t)lock == DEBUG_RWLOCK_PTR ) &&
56    (local_cxy      == DEBUG_RWLOCK_CXY ) )
57printk("\n[%s] thread[%x,%x] initialise lock %s [%x,%x]\n",
58__FUNCTION__, this->process->pid, this->trdid,
59lock_type_str[type], local_cxy, lock );
60#endif
61
62}
63
64/////////////////////////////////////////
65void rwlock_rd_acquire( rwlock_t * lock )
66{
67    thread_t * this = CURRENT_THREAD;
68
69    // check calling thread can yield
70    thread_assert_can_yield( this , __FUNCTION__ );
71
72    // get busylock
73    busylock_acquire( &lock->lock );
74
75#if DEBUG_RWLOCK_TYPE
76uint32_t lock_type = lock->lock.type;
77#endif
78
79    // block and deschedule if lock already taken
80    while( lock->taken )
81    {
82
83#if DEBUG_RWLOCK_TYPE
84if( (lock_type      == DEBUG_RWLOCK_TYPE) &&
85    ((intptr_t)lock == DEBUG_RWLOCK_PTR ) &&
86    (local_cxy      == DEBUG_RWLOCK_CXY ) )
87printk("\n[%s] thread[%x,%x] READ BLOCK on rwlock %s [%x,%x] / taken %d / count %d\n",
88__FUNCTION__, this->process->pid, this->trdid, 
89lock_type_str[lock_type], local_cxy, lock, lock->taken, lock->count );
90#endif
91        // register reader thread in waiting queue
92        list_add_last( &lock->rd_root , &this->wait_list );
93
94        // block reader thread
95        thread_block( XPTR( local_cxy , this ) , THREAD_BLOCKED_LOCK );
96       
97        // release busylock
98        busylock_release( &lock->lock );
99
100        // deschedule
101        sched_yield("reader wait rwlock");
102       
103        // get busylock
104        busylock_acquire( &lock->lock );
105    }
106
107    // increment number of readers
108    lock->count++;
109
110#if DEBUG_RWLOCK_TYPE
111if( (lock_type      == DEBUG_RWLOCK_TYPE) &&
112    ((intptr_t)lock == DEBUG_RWLOCK_PTR ) &&
113    (local_cxy      == DEBUG_RWLOCK_CXY ) )
114printk("\n[%s] thread[%x,%x] READ ACQUIRE rwlock %s [%x,%x] / taken %d / count %d\n",
115__FUNCTION__, this->process->pid, this->trdid, 
116lock_type_str[lock_type], local_cxy, lock, lock->taken, lock->count );
117#endif
118
119    // release busylock
120    busylock_release( &lock->lock );
121
122}  // end rwlock_rd_acquire()
123
124/////////////////////////////////////////
125void rwlock_wr_acquire( rwlock_t * lock )
126{
127    thread_t * this = CURRENT_THREAD;
128
129    // check calling thread can yield
130    thread_assert_can_yield( this , __FUNCTION__ );
131
132    // get busylock
133    busylock_acquire( &lock->lock );
134
135#if DEBUG_RWLOCK_TYPE
136uint32_t lock_type = lock->lock.type;
137#endif
138
139    // block and deschedule if lock already taken or existing read access
140    while( lock->taken || lock->count )
141    {
142
143#if DEBUG_RWLOCK_TYPE
144if( (lock_type      == DEBUG_RWLOCK_TYPE) &&
145    ((intptr_t)lock == DEBUG_RWLOCK_PTR ) &&
146    (local_cxy      == DEBUG_RWLOCK_CXY ) )
147printk("\n[%s] thread[%x,%x] WRITE BLOCK on rwlock %s [%x,%x] / taken %d / count %d\n",
148__FUNCTION__, this->process->pid, this->trdid, 
149lock_type_str[lock_type], local_cxy, lock, lock->taken, lock->count );
150#endif
151        // register writer in waiting queue
152        list_add_last( &lock->wr_root , &this->wait_list );
153
154        // block writer thread
155        thread_block( XPTR( local_cxy , this ) , THREAD_BLOCKED_LOCK );
156       
157        // release busylock
158        busylock_release( &lock->lock );
159
160        // deschedule
161        sched_yield("writer wait rwlock");
162       
163        // get busylock
164        busylock_acquire( &lock->lock );
165    }
166
167    // take the rwlock
168    lock->taken = 1;
169
170#if DEBUG_RWLOCK_TYPE
171if( (lock_type      == DEBUG_RWLOCK_TYPE) &&
172    ((intptr_t)lock == DEBUG_RWLOCK_PTR ) &&
173    (local_cxy      == DEBUG_RWLOCK_CXY ) )
174printk("\n[%s] thread[%x,%x] WRITE ACQUIRE rwlock %s [%x,%x] / taken %d / count %d\n",
175__FUNCTION__, this->process->pid, this->trdid, 
176lock_type_str[lock_type], local_cxy, lock, lock->taken, lock->count );
177#endif
178
179    // release busylock
180    busylock_release( &lock->lock );
181
182}  // end rwlock_wr_acquire()
183
184/////////////////////////////////////////
185void rwlock_rd_release( rwlock_t * lock )
186{
187    // synchronize memory before lock release
188    hal_fence();
189
190    // get busylock
191    busylock_acquire( &lock->lock );
192
193    // decrement number of readers
194    lock->count--;
195
196#if DEBUG_RWLOCK_TYPE
197thread_t * this = CURRENT_THREAD;
198uint32_t lock_type = lock->lock.type;
199if( (lock_type      == DEBUG_RWLOCK_TYPE) &&
200    ((intptr_t)lock == DEBUG_RWLOCK_PTR ) &&
201    (local_cxy      == DEBUG_RWLOCK_CXY ) )
202printk("\n[%s] thread[%x,%x] READ RELEASE rwlock %s [%x,%x] / taken %d / count %d\n",
203__FUNCTION__, this->process->pid, this->trdid, 
204lock_type_str[lock_type], local_cxy, lock, lock->taken, lock->count );
205#endif
206
207    // release first writer in waiting queue if no current readers
208    // and writers waiting queue non empty
209    if( (lock->count == 0) && (list_is_empty( &lock->wr_root ) == false) )
210    {
211        // get first writer thread
212        thread_t * thread = LIST_FIRST( &lock->wr_root , thread_t , wait_list );
213
214#if DEBUG_RWLOCK_TYPE
215if( (lock_type      == DEBUG_RWLOCK_TYPE) &&
216    ((intptr_t)lock == DEBUG_RWLOCK_PTR ) &&
217    (local_cxy      == DEBUG_RWLOCK_CXY ) )
218printk("\n[%s] thread[%x,%x] UNBLOCK thread[%x,%x] / rwlock %s [%x,%x]\n",
219__FUNCTION__, this->process->pid, this->trdid, thread->process->pid, thread->trdid,
220lock_type_str[lock_type], local_cxy, lock );
221#endif
222
223        // remove this waiting thread from waiting list
224        list_unlink( &thread->wait_list );
225
226        // unblock this waiting thread
227        thread_unblock( XPTR( local_cxy , thread ) , THREAD_BLOCKED_LOCK );
228    }
229    // release all readers in waiting queue if writers waiting queue empty
230    // and readers waiting queue non empty
231    else if( list_is_empty( &lock->wr_root ) && (list_is_empty( &lock->rd_root ) == false) )
232    {
233        while( list_is_empty( &lock->rd_root ) == false )
234        {
235            // get first reader thread
236            thread_t * thread = LIST_FIRST( &lock->wr_root , thread_t , wait_list );
237
238#if DEBUG_RWLOCK_TYPE
239if( (lock_type      == DEBUG_RWLOCK_TYPE) &&
240    ((intptr_t)lock == DEBUG_RWLOCK_PTR ) &&
241    (local_cxy      == DEBUG_RWLOCK_CXY ) )
242printk("\n[%s] thread[%x,%x] UNBLOCK thread[%x,%x] / rwlock %s [%x,%x]\n",
243__FUNCTION__, this->process->pid, this->trdid, thread->process->pid, thread->trdid,
244lock_type_str[lock_type], local_cxy, lock );
245#endif
246   
247            // remove this waiting thread from waiting list
248            list_unlink( &thread->wait_list );
249
250            // unblock this waiting thread
251            thread_unblock( XPTR( local_cxy , thread ) , THREAD_BLOCKED_LOCK );
252        }
253    }
254
255    // release busylock
256    busylock_release( &lock->lock );
257
258}  // end rwlock_rd_release()
259
260/////////////////////////////////////////
261void rwlock_wr_release( rwlock_t * lock )
262{
263    // synchronize memory before lock release
264    hal_fence();
265
266    // get busylock
267    busylock_acquire( &lock->lock );
268
269    // release the rwlock
270    lock->taken = 0;
271
272#if DEBUG_RWLOCK_TYPE
273thread_t * this = CURRENT_THREAD;
274uint32_t lock_type = lock->lock.type;
275if( (lock_type      == DEBUG_RWLOCK_TYPE) &&
276    ((intptr_t)lock == DEBUG_RWLOCK_PTR ) &&
277    (local_cxy      == DEBUG_RWLOCK_CXY ) )
278printk("\n[%s] thread[%x,%x] WRITE RELEASE rwlock %s [%x,%x] / taken %d / count %d\n",
279__FUNCTION__, this->process->pid, this->trdid, 
280lock_type_str[lock_type], local_cxy, lock, lock->taken, lock->count );
281#endif
282
283    // release first waiting writer thread if writers waiting queue non empty
284    if( list_is_empty( &lock->wr_root ) == false )
285    {
286        // get first writer thread
287        thread_t * thread = LIST_FIRST( &lock->wr_root , thread_t , wait_list );
288
289#if DEBUG_RWLOCK_TYPE
290if( (lock_type      == DEBUG_RWLOCK_TYPE) &&
291    ((intptr_t)lock == DEBUG_RWLOCK_PTR ) &&
292    (local_cxy      == DEBUG_RWLOCK_CXY ) )
293printk("\n[%s] thread[%x,%x] UNBLOCK thread[%x,%x] / rwlock %s [%x,%x]\n",
294__FUNCTION__, this->process->pid, this->trdid, thread->process->pid, thread->trdid,
295lock_type_str[lock_type], local_cxy, lock );
296#endif
297        // remove this waiting thread from waiting list
298        list_unlink( &thread->wait_list );
299
300        // unblock this waiting thread
301        thread_unblock( XPTR( local_cxy , thread ) , THREAD_BLOCKED_LOCK );
302    }
303
304    // check readers waiting queue and release all if writers waiting queue empty
305    else 
306    {
307        while( list_is_empty( &lock->rd_root ) == false )
308        {
309            // get first reader thread
310            thread_t * thread = LIST_FIRST( &lock->rd_root , thread_t , wait_list );
311
312#if DEBUG_RWLOCK_TYPE
313if( (lock_type      == DEBUG_RWLOCK_TYPE) &&
314    ((intptr_t)lock == DEBUG_RWLOCK_PTR ) &&
315    (local_cxy      == DEBUG_RWLOCK_CXY ) )
316printk("\n[%s] thread[%x,%x] UNBLOCK thread[%x,%x] / rwlock %s [%x,%x]\n",
317__FUNCTION__, this->process->pid, this->trdid, thread->process->pid, thread->trdid,
318lock_type_str[lock_type], local_cxy, lock );
319#endif
320            // remove this waiting thread from waiting list
321            list_unlink( &thread->wait_list );
322
323            // unblock this waiting thread
324            thread_unblock( XPTR( local_cxy , thread ) , THREAD_BLOCKED_LOCK );
325        }
326    }
327
328    // release busylock
329    busylock_release( &lock->lock );
330
331}  // end rwlock_wr_release()
332
333
Note: See TracBrowser for help on using the repository browser.