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

Last change on this file since 640 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
RevLine 
[1]1/*
[563]2 * rwlock.c - kernel local read/write lock implementation.
[1]3 *
[629]4 * Author  Alain Greiner     (2016,2017,2018,2019)
[1]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
[14]24#include <kernel_config.h>
[457]25#include <hal_kernel_types.h>
[1]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
[563]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 )
[1]43{ 
[563]44        lock->taken   = 0;
[1]45    lock->count   = 0;
[409]46
[563]47    list_root_init( &lock->rd_root );
48    list_root_init( &lock->wr_root );
[409]49
[563]50    busylock_init( &lock->lock , type );
[603]51
[610]52#if DEBUG_RWLOCK_TYPE
[603]53thread_t * this = CURRENT_THREAD;
[629]54if( (type           == DEBUG_RWLOCK_TYPE) &&
55    ((intptr_t)lock == DEBUG_RWLOCK_PTR ) &&
56    (local_cxy      == DEBUG_RWLOCK_CXY ) )
[603]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
[1]62}
63
[563]64/////////////////////////////////////////
65void rwlock_rd_acquire( rwlock_t * lock )
[1]66{
[563]67    thread_t * this = CURRENT_THREAD;
[1]68
[563]69    // check calling thread can yield
70    thread_assert_can_yield( this , __FUNCTION__ );
[1]71
[563]72    // get busylock
73    busylock_acquire( &lock->lock );
74
[623]75#if DEBUG_RWLOCK_TYPE
76uint32_t lock_type = lock->lock.type;
77#endif
78
[563]79    // block and deschedule if lock already taken
80    while( lock->taken )
[1]81    {
[563]82
[610]83#if DEBUG_RWLOCK_TYPE
[629]84if( (lock_type      == DEBUG_RWLOCK_TYPE) &&
85    ((intptr_t)lock == DEBUG_RWLOCK_PTR ) &&
86    (local_cxy      == DEBUG_RWLOCK_CXY ) )
[603]87printk("\n[%s] thread[%x,%x] READ BLOCK on rwlock %s [%x,%x] / taken %d / count %d\n",
[600]88__FUNCTION__, this->process->pid, this->trdid, 
[610]89lock_type_str[lock_type], local_cxy, lock, lock->taken, lock->count );
[563]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 );
[1]105    }
106
[603]107    // increment number of readers
108    lock->count++;
109
[610]110#if DEBUG_RWLOCK_TYPE
[629]111if( (lock_type      == DEBUG_RWLOCK_TYPE) &&
112    ((intptr_t)lock == DEBUG_RWLOCK_PTR ) &&
113    (local_cxy      == DEBUG_RWLOCK_CXY ) )
[603]114printk("\n[%s] thread[%x,%x] READ ACQUIRE rwlock %s [%x,%x] / taken %d / count %d\n",
[600]115__FUNCTION__, this->process->pid, this->trdid, 
[610]116lock_type_str[lock_type], local_cxy, lock, lock->taken, lock->count );
[563]117#endif
[1]118
[563]119    // release busylock
120    busylock_release( &lock->lock );
[409]121
[563]122}  // end rwlock_rd_acquire()
[1]123
[563]124/////////////////////////////////////////
125void rwlock_wr_acquire( rwlock_t * lock )
126{
127    thread_t * this = CURRENT_THREAD;
[1]128
[563]129    // check calling thread can yield
130    thread_assert_can_yield( this , __FUNCTION__ );
[1]131
[563]132    // get busylock
133    busylock_acquire( &lock->lock );
[1]134
[623]135#if DEBUG_RWLOCK_TYPE
136uint32_t lock_type = lock->lock.type;
137#endif
138
[563]139    // block and deschedule if lock already taken or existing read access
140    while( lock->taken || lock->count )
141    {
142
[610]143#if DEBUG_RWLOCK_TYPE
[629]144if( (lock_type      == DEBUG_RWLOCK_TYPE) &&
145    ((intptr_t)lock == DEBUG_RWLOCK_PTR ) &&
146    (local_cxy      == DEBUG_RWLOCK_CXY ) )
[603]147printk("\n[%s] thread[%x,%x] WRITE BLOCK on rwlock %s [%x,%x] / taken %d / count %d\n",
[600]148__FUNCTION__, this->process->pid, this->trdid, 
[610]149lock_type_str[lock_type], local_cxy, lock, lock->taken, lock->count );
[563]150#endif
151        // register writer in waiting queue
152        list_add_last( &lock->wr_root , &this->wait_list );
[1]153
[603]154        // block writer thread
[563]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
[603]167    // take the rwlock
168    lock->taken = 1;
169
[610]170#if DEBUG_RWLOCK_TYPE
[629]171if( (lock_type      == DEBUG_RWLOCK_TYPE) &&
172    ((intptr_t)lock == DEBUG_RWLOCK_PTR ) &&
173    (local_cxy      == DEBUG_RWLOCK_CXY ) )
[603]174printk("\n[%s] thread[%x,%x] WRITE ACQUIRE rwlock %s [%x,%x] / taken %d / count %d\n",
[600]175__FUNCTION__, this->process->pid, this->trdid, 
[610]176lock_type_str[lock_type], local_cxy, lock, lock->taken, lock->count );
[409]177#endif
178
[563]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
[603]193    // decrement number of readers
194    lock->count--;
195
[610]196#if DEBUG_RWLOCK_TYPE
[600]197thread_t * this = CURRENT_THREAD;
[610]198uint32_t lock_type = lock->lock.type;
[629]199if( (lock_type      == DEBUG_RWLOCK_TYPE) &&
200    ((intptr_t)lock == DEBUG_RWLOCK_PTR ) &&
201    (local_cxy      == DEBUG_RWLOCK_CXY ) )
[603]202printk("\n[%s] thread[%x,%x] READ RELEASE rwlock %s [%x,%x] / taken %d / count %d\n",
[600]203__FUNCTION__, this->process->pid, this->trdid, 
[610]204lock_type_str[lock_type], local_cxy, lock, lock->taken, lock->count );
[563]205#endif
[1]206
[563]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
[610]214#if DEBUG_RWLOCK_TYPE
[629]215if( (lock_type      == DEBUG_RWLOCK_TYPE) &&
216    ((intptr_t)lock == DEBUG_RWLOCK_PTR ) &&
217    (local_cxy      == DEBUG_RWLOCK_CXY ) )
[600]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,
[610]220lock_type_str[lock_type], local_cxy, lock );
[563]221#endif
[1]222
[563]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) )
[1]232    {
[563]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 );
[1]237
[610]238#if DEBUG_RWLOCK_TYPE
[629]239if( (lock_type      == DEBUG_RWLOCK_TYPE) &&
240    ((intptr_t)lock == DEBUG_RWLOCK_PTR ) &&
241    (local_cxy      == DEBUG_RWLOCK_CXY ) )
[600]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,
[610]244lock_type_str[lock_type], local_cxy, lock );
[563]245#endif
246   
247            // remove this waiting thread from waiting list
248            list_unlink( &thread->wait_list );
[1]249
[563]250            // unblock this waiting thread
251            thread_unblock( XPTR( local_cxy , thread ) , THREAD_BLOCKED_LOCK );
252        }
[1]253    }
254
[563]255    // release busylock
256    busylock_release( &lock->lock );
[1]257
[563]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
[603]269    // release the rwlock
270    lock->taken = 0;
271
[610]272#if DEBUG_RWLOCK_TYPE
[600]273thread_t * this = CURRENT_THREAD;
[610]274uint32_t lock_type = lock->lock.type;
[629]275if( (lock_type      == DEBUG_RWLOCK_TYPE) &&
276    ((intptr_t)lock == DEBUG_RWLOCK_PTR ) &&
277    (local_cxy      == DEBUG_RWLOCK_CXY ) )
[603]278printk("\n[%s] thread[%x,%x] WRITE RELEASE rwlock %s [%x,%x] / taken %d / count %d\n",
[600]279__FUNCTION__, this->process->pid, this->trdid, 
[610]280lock_type_str[lock_type], local_cxy, lock, lock->taken, lock->count );
[409]281#endif
282
[563]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 );
[1]288
[610]289#if DEBUG_RWLOCK_TYPE
[629]290if( (lock_type      == DEBUG_RWLOCK_TYPE) &&
291    ((intptr_t)lock == DEBUG_RWLOCK_PTR ) &&
292    (local_cxy      == DEBUG_RWLOCK_CXY ) )
[600]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,
[610]295lock_type_str[lock_type], local_cxy, lock );
[409]296#endif
[563]297        // remove this waiting thread from waiting list
298        list_unlink( &thread->wait_list );
[409]299
[563]300        // unblock this waiting thread
301        thread_unblock( XPTR( local_cxy , thread ) , THREAD_BLOCKED_LOCK );
302    }
[337]303
[563]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
[610]312#if DEBUG_RWLOCK_TYPE
[629]313if( (lock_type      == DEBUG_RWLOCK_TYPE) &&
314    ((intptr_t)lock == DEBUG_RWLOCK_PTR ) &&
315    (local_cxy      == DEBUG_RWLOCK_CXY ) )
[600]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,
[610]318lock_type_str[lock_type], local_cxy, lock );
[563]319#endif
320            // remove this waiting thread from waiting list
321            list_unlink( &thread->wait_list );
[1]322
[563]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.