source: trunk/kernel/libk/remote_mutex.c @ 563

Last change on this file since 563 was 563, checked in by alain, 2 years ago

Complete restructuration of kernel spinlocks.

File size: 13.4 KB
Line 
1/*
2 * remote_mutex.c - POSIX mutex implementation.
3 *
4 * Authors   Alain   Greiner (2016,2017,2018)
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_remote.h>
27#include <thread.h>
28#include <xlist.h>
29#include <scheduler.h>
30#include <remote_busylock.h>
31#include <remote_mutex.h>
32
33
34/////////////////////////////////////////////////
35xptr_t remote_mutex_from_ident( intptr_t  ident )
36{
37    // get pointer on local process_descriptor
38    process_t * process = CURRENT_THREAD->process;
39
40    // get extended pointer on reference process
41    xptr_t      ref_xp = process->ref_xp;
42
43    // get cluster and local pointer on reference process
44    cxy_t          ref_cxy = GET_CXY( ref_xp );
45    process_t    * ref_ptr = (process_t *)GET_PTR( ref_xp );
46
47    // get extended pointers on mutexes list 
48    xptr_t root_xp = XPTR( ref_cxy , &ref_ptr->mutex_root );
49    xptr_t lock_xp = XPTR( ref_cxy , &ref_ptr->sync_lock );
50
51    // get lock protecting synchro lists
52    remote_queuelock_acquire( lock_xp );
53 
54    // scan reference process mutex list
55    xptr_t           iter_xp;
56    xptr_t           mutex_xp;
57    cxy_t            mutex_cxy;
58    remote_mutex_t * mutex_ptr;
59    intptr_t         current;
60    bool_t           found = false;
61           
62    XLIST_FOREACH( root_xp , iter_xp )
63    {
64        mutex_xp  = XLIST_ELEMENT( iter_xp , remote_mutex_t , list );
65        mutex_cxy = GET_CXY( mutex_xp );
66        mutex_ptr = (remote_mutex_t *)GET_PTR( mutex_xp );
67        current     = (intptr_t)hal_remote_lpt( XPTR( mutex_cxy , &mutex_ptr->ident ) );   
68        if( ident == current )
69        {
70            found = true;
71            break;
72        }
73    }
74
75    // relese lock protecting synchros lists
76    remote_queuelock_release( lock_xp );
77 
78    if( found == false )  return XPTR_NULL;
79    else                  return mutex_xp;
80
81}  // end remote_mutex_from_ident()
82
83/////////////////////////////////////////////
84error_t remote_mutex_create( intptr_t ident )
85{ 
86    xptr_t           mutex_xp;
87    remote_mutex_t * mutex_ptr;
88
89    // get pointer on local process descriptor
90    process_t * process = CURRENT_THREAD->process;
91
92    // get extended pointer on reference process
93    xptr_t      ref_xp = process->ref_xp;
94
95    // get reference process cluster and local pointer
96    cxy_t       ref_cxy = GET_CXY( ref_xp );
97    process_t * ref_ptr = (process_t *)GET_PTR( ref_xp );
98
99    // allocate memory for mutex descriptor
100    if( ref_cxy == local_cxy )                  // local cluster is the reference
101    {
102        kmem_req_t req;   
103        req.type    = KMEM_MUTEX;
104        req.flags   = AF_ZERO;
105        mutex_ptr   = kmem_alloc( &req );
106        mutex_xp    = XPTR( local_cxy , mutex_ptr );
107    }
108    else                                       // reference is remote
109    {
110        rpc_kcm_alloc_client( ref_cxy , KMEM_MUTEX , &mutex_xp );
111        mutex_ptr = GET_PTR( mutex_xp );
112    }
113
114    if( mutex_ptr == NULL ) return 0xFFFFFFFF;
115
116    // initialise mutex
117    hal_remote_s32 ( XPTR( ref_cxy , &mutex_ptr->taken )   , 0 );
118    hal_remote_spt( XPTR( ref_cxy , &mutex_ptr->ident )   , (void *)ident );
119    xlist_entry_init( XPTR( ref_cxy , &mutex_ptr->list ) );
120    xlist_root_init( XPTR( ref_cxy , &mutex_ptr->root ) );
121    hal_remote_s64( XPTR( ref_cxy , &mutex_ptr->owner ) , XPTR_NULL );
122    remote_busylock_init( XPTR( ref_cxy , &mutex_ptr->lock ), LOCK_MUTEX_STATE );
123
124    // get root of mutexes list in process, and list_entry in mutex
125    xptr_t root_xp = XPTR( ref_cxy , &ref_ptr->mutex_root );
126    xptr_t xp_list = XPTR( ref_cxy , &mutex_ptr->list );
127
128    // get lock protecting user synchros lists
129    remote_queuelock_acquire( XPTR( ref_cxy , &ref_ptr->sync_lock ) );
130
131    // register mutex in process descriptor
132    xlist_add_first( root_xp , xp_list );
133
134    // release lock protecting user synchros lists
135    remote_queuelock_release( XPTR( ref_cxy , &ref_ptr->sync_lock ) );
136
137#if DEBUG_MUTEX
138thread_t * this = CURRENT_THREAD;
139if( (uint32_t)hal_get_cycles() > DEBUG_QUEUELOCK )
140printk("\n[DBG] %s : thread %x in %x process / mutex(%x,%x)\n",
141__FUNCTION__, this->trdid, this->process->pid, local_cxy, mutex_ptr );
142#endif
143
144
145    return 0;
146
147}  // end remote_mutex_create()
148
149////////////////////////////////////////////
150void remote_mutex_destroy( xptr_t mutex_xp )
151{
152    // get pointer on local process descriptor
153    process_t * process = CURRENT_THREAD->process;
154
155    // get extended pointer on reference process
156    xptr_t      ref_xp = process->ref_xp;
157
158    // get reference process cluster and local pointer
159    cxy_t       ref_cxy = GET_CXY( ref_xp );
160    process_t * ref_ptr = (process_t *)GET_PTR( ref_xp );
161
162    // get mutex cluster and local pointer
163    cxy_t            mutex_cxy = GET_CXY( mutex_xp );
164    remote_mutex_t * mutex_ptr = (remote_mutex_t *)GET_PTR( mutex_xp );
165
166    // get lock protecting user synchros lists
167    remote_queuelock_acquire( XPTR( ref_cxy , &ref_ptr->sync_lock ) );
168
169    // remove mutex from reference process xlist
170    xlist_unlink( XPTR( mutex_cxy , &mutex_ptr->list ) );
171
172    // release lock protecting user synchros lists
173    remote_queuelock_release( XPTR( ref_cxy , &ref_ptr->sync_lock ) );
174
175    // release memory allocated for mutexaphore descriptor
176    if( mutex_cxy == local_cxy )                            // reference is local
177    {
178        kmem_req_t  req;
179        req.type = KMEM_MUTEX;
180        req.ptr  = mutex_ptr;
181        kmem_free( &req );
182    }
183    else                                                  // reference is remote
184    {
185        rpc_kcm_free_client( mutex_cxy , mutex_ptr , KMEM_BARRIER );
186    }
187
188}  // end remote_mutex_destroy()
189
190/////////////////////////////////////////
191void remote_mutex_lock( xptr_t mutex_xp )
192{ 
193    // get cluster and local pointer on mutex
194    remote_mutex_t * mutex_ptr = GET_PTR( mutex_xp );
195    cxy_t            mutex_cxy = GET_CXY( mutex_xp );
196
197    // get extended pointers on mutex fields
198    xptr_t           taken_xp = XPTR( mutex_cxy , &mutex_ptr->taken );
199    xptr_t           owner_xp = XPTR( mutex_cxy , &mutex_ptr->owner );
200    xptr_t           root_xp  = XPTR( mutex_cxy , &mutex_ptr->root );
201    xptr_t           lock_xp  = XPTR( mutex_cxy , &mutex_ptr->lock );
202
203    // get cluster and pointers on calling thread
204    cxy_t            caller_cxy = local_cxy;
205    thread_t       * caller_ptr = CURRENT_THREAD;
206    xptr_t           caller_xp  = XPTR( caller_cxy , caller_ptr );
207
208// check calling thread can yield
209assert( (caller_ptr->busylocks == 0),
210"cannot yield : busylocks = %d\n", caller_ptr->busylocks );
211
212    while( 1 )
213    {
214        // get busylock protecting mutex state
215        remote_busylock_acquire( lock_xp );
216
217        // test mutex state
218        if( hal_remote_l32( taken_xp ) == 0 )                 // success
219        {
220            // register calling thread as mutex owner
221            hal_remote_s64( owner_xp , caller_xp );
222
223            // update mutex state
224            hal_remote_s32( taken_xp , 1 );
225
226#if DEBUG_MUTEX
227thread_t * this = CURRENT_THREAD;
228if( (uint32_t)hal_get_cycles() > DEBUG_MUTEX )
229printk("\n[DBG] %s : thread %x in process %x SUCCESS on mutex(%x,%x)\n",
230__FUNCTION__, this->trdid, this->process->pid, mutex_cxy, mutex_ptr );
231#endif
232
233            // release busylock protecting mutex state
234            remote_busylock_release( lock_xp ); 
235
236             return;
237        }
238        else                                                 //  already taken
239        {
240            // block the calling thread   
241            thread_block( caller_xp , THREAD_BLOCKED_USERSYNC );
242
243            // register calling thread in mutex waiting queue
244            xptr_t entry_xp = XPTR( caller_cxy , &caller_ptr->wait_xlist );
245            xlist_add_last( root_xp , entry_xp );
246
247#if DEBUG_MUTEX
248thread_t * this = CURRENT_THREAD;
249if( (uint32_t)hal_get_cycles() > DEBUG_MUTEX )
250printk("\n[DBG] %s : thread %x in process %x BLOCKED on mutex(%x,%x)\n",
251__FUNCTION__, this->trdid, this->process->pid, mutex_cxy, mutex_ptr );
252#endif
253
254            // release busylock protecting mutex state
255            remote_busylock_release( lock_xp ); 
256
257            // deschedule calling thread
258            sched_yield("blocked on mutex");
259        }
260    } 
261}  // end remote_mutex_lock()
262
263//////////////////////////////////////////////
264error_t remote_mutex_unlock( xptr_t mutex_xp )
265{
266    // memory barrier before mutex release
267    hal_fence();
268
269    // get cluster and local pointer on mutex
270    remote_mutex_t * mutex_ptr = GET_PTR( mutex_xp );
271    cxy_t            mutex_cxy = GET_CXY( mutex_xp );
272
273    // get cluster and pointers on calling thread
274    cxy_t            caller_cxy = local_cxy;
275    thread_t       * caller_ptr = CURRENT_THREAD;
276    xptr_t           caller_xp  = XPTR( caller_cxy , caller_ptr );
277
278    // get extended pointers on mutex fields
279    xptr_t           taken_xp = XPTR( mutex_cxy , &mutex_ptr->taken );
280    xptr_t           owner_xp = XPTR( mutex_cxy , &mutex_ptr->owner );
281    xptr_t           root_xp  = XPTR( mutex_cxy , &mutex_ptr->root );
282    xptr_t           lock_xp  = XPTR( mutex_cxy , &mutex_ptr->lock );
283
284    // get busylock protecting mutex state
285    remote_busylock_acquire( lock_xp );
286   
287    // check calling thread is mutex owner
288    if( hal_remote_l64( owner_xp ) != caller_xp )
289    {
290        // release busylock protecting mutex state
291        remote_busylock_release( lock_xp );
292
293        return 0xFFFFFFFF;
294    }
295
296#if DEBUG_MUTEX
297thread_t * this = CURRENT_THREAD;
298if( (uint32_t)hal_get_cycles() > DEBUG_MUTEX )
299printk("\n[DBG] %s : thread %x in %x process EXIT / mutex(%x,%x)\n",
300__FUNCTION__, this->trdid, this->process->pid, mutex_cxy, mutex_ptr );
301#endif
302
303    // update owner field,
304    hal_remote_s64( owner_xp , XPTR_NULL );
305
306    // update taken field
307    hal_remote_s32( taken_xp , 0 );
308
309    // unblock first waiting thread if waiting list non empty
310    if( xlist_is_empty( root_xp ) == false )
311    {
312        // get extended pointer on first waiting thread
313        xptr_t     thread_xp  = XLIST_FIRST( root_xp , thread_t , wait_xlist );
314        thread_t * thread_ptr = GET_PTR( thread_xp );
315        cxy_t      thread_cxy = GET_CXY( thread_xp );
316
317#if DEBUG_MUTEX
318if( (uint32_t)hal_get_cycles() > DEBUG_MUTEX )
319{
320trdid_t     trdid   = hal_remote_l32( XPTR( thread_cxy , &thread_ptr->trdid ) );
321process_t * process = hal_remote_lpt( XPTR( thread_cxy , &thread_ptr->process ) );
322pid_t       pid     = hal_remote_l32( XPTR( thread_cxy , &process->pid ) );
323printk("\n[DBG] %s : thread %x in process %x UNBLOCK thread %x in process %d / mutex(%x,%x)\n",
324__FUNCTION__, this->trdid, this->process->pid, trdid, pid, mutex_cxy, mutex_ptr );
325}
326#endif
327
328        // remove this thread from waiting queue
329        xlist_unlink( XPTR( thread_cxy , &thread_ptr->wait_xlist ) );
330
331        // unblock first waiting thread
332        thread_unblock( thread_xp , THREAD_BLOCKED_USERSYNC ); 
333    }
334   
335    // release busylock protecting mutex state
336    remote_busylock_release( lock_xp );
337
338    return 0;
339
340}  // end remote_mutex_unlock()
341
342///////////////////////////////////////////////
343error_t remote_mutex_trylock( xptr_t mutex_xp )
344{
345    // get cluster and local pointer on mutex
346    remote_mutex_t * mutex_ptr = GET_PTR( mutex_xp );
347    cxy_t            mutex_cxy = GET_CXY( mutex_xp );
348
349    // get cluster and pointers on calling thread
350    cxy_t            caller_cxy = local_cxy;
351    thread_t       * caller_ptr = CURRENT_THREAD;
352    xptr_t           caller_xp  = XPTR( caller_cxy , caller_ptr );
353
354    // get extended pointers on mutex fields
355    xptr_t           taken_xp = XPTR( mutex_cxy , &mutex_ptr->taken );
356    xptr_t           owner_xp = XPTR( mutex_cxy , &mutex_ptr->owner );
357    xptr_t           lock_xp  = XPTR( mutex_cxy , &mutex_ptr->lock );
358
359    // get busylock protecting mutex state
360    remote_busylock_acquire( lock_xp );
361
362    // test mutex state
363    if( hal_remote_l32( taken_xp ) == 0 )                 // success
364    {
365        // register calling thread as mutex owner
366        hal_remote_s64( owner_xp , caller_xp );
367
368        // update mutex state
369        hal_remote_s32( taken_xp , 1 );
370
371#if DEBUG_MUTEX
372thread_t * this = CURRENT_THREAD;
373if( (uint32_t)hal_get_cycles() > DEBUG_QUEUELOCK )
374printk("\n[DBG] %s : SUCCESS for thread %x in process %x / mutex(%x,%x)\n",
375__FUNCTION__, this->trdid, this->process->pid, mutex_cxy, mutex_ptr );
376#endif
377        // release busylock protecting mutex state
378        remote_busylock_release( lock_xp ); 
379
380        return 0;
381    }
382    else                                                 //  already taken
383    {
384
385#if DEBUG_MUTEX
386thread_t * this = CURRENT_THREAD;
387if( (uint32_t)hal_get_cycles() > DEBUG_QUEUELOCK )
388printk("\n[DBG] %s : FAILURE for thread %x in process %x / mutex(%x,%x)\n",
389__FUNCTION__, this->trdid, this->process->pid, mutex_cxy, mutex_ptr );
390#endif
391        // release busylock protecting mutex state
392        remote_busylock_release( lock_xp ); 
393
394        return 0xFFFFFFFF;
395    }
396}  // end remote_mutex_trylock()
Note: See TracBrowser for help on using the repository browser.