/* * remote_mutex.c - Access a POSIX mutex. * * Authors Alain Greiner (2016) * * Copyright (c) UPMC Sorbonne Universites * * This file is part of ALMOS-MKH. * * ALMOS-MKH is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2.0 of the License. * * ALMOS-MKH is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ALMOS-MKH; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include ///////////////////////////////////////////////// xptr_t remote_mutex_from_ident( intptr_t ident ) { // get pointer on local process_descriptor process_t * process = CURRENT_THREAD->process; // get extended pointer on reference process xptr_t ref_xp = process->ref_xp; // get cluster and local pointer on reference process cxy_t ref_cxy = GET_CXY( ref_xp ); process_t * ref_ptr = (process_t *)GET_PTR( ref_xp ); // get extended pointer on root of mutex list xptr_t root_xp = XPTR( ref_cxy , &ref_ptr->mutex_root ); // scan reference process mutex list xptr_t iter_xp; xptr_t mutex_xp; cxy_t mutex_cxy; remote_mutex_t * mutex_ptr; intptr_t current; bool_t found = false; XLIST_FOREACH( root_xp , iter_xp ) { mutex_xp = XLIST_ELEMENT( iter_xp , remote_mutex_t , list ); mutex_cxy = GET_CXY( mutex_xp ); mutex_ptr = (remote_mutex_t *)GET_PTR( mutex_xp ); current = (intptr_t)hal_remote_lpt( XPTR( mutex_cxy , &mutex_ptr->ident ) ); if( ident == current ) { found = true; break; } } if( found == false ) return XPTR_NULL; else return mutex_xp; } // end remote_mutex_from_ident() ///////////////////////////////////////////// error_t remote_mutex_create( intptr_t ident ) { xptr_t mutex_xp; remote_mutex_t * mutex_ptr; // get pointer on local process descriptor process_t * process = CURRENT_THREAD->process; // get extended pointer on reference process xptr_t ref_xp = process->ref_xp; // get reference process cluster and local pointer cxy_t ref_cxy = GET_CXY( ref_xp ); process_t * ref_ptr = (process_t *)GET_PTR( ref_xp ); // allocate memory for barrier descriptor if( ref_cxy == local_cxy ) // local cluster is the reference { kmem_req_t req; req.type = KMEM_MUTEX; req.flags = AF_ZERO; mutex_ptr = kmem_alloc( &req ); mutex_xp = XPTR( local_cxy , mutex_ptr ); } else // reference is remote { rpc_kcm_alloc_client( ref_cxy , KMEM_MUTEX , &mutex_xp ); mutex_ptr = (remote_mutex_t *)GET_PTR( mutex_xp ); } if( mutex_ptr == NULL ) return ENOMEM; // initialise mutex hal_remote_sw ( XPTR( ref_cxy , &mutex_ptr->value ) , 0 ); hal_remote_spt( XPTR( ref_cxy , &mutex_ptr->ident ) , (void *)ident ); hal_remote_swd( XPTR( ref_cxy , &mutex_ptr->owner ) , XPTR_NULL ); xlist_entry_init( XPTR( ref_cxy , &mutex_ptr->list ) ); xlist_root_init( XPTR( ref_cxy , &mutex_ptr->root ) ); remote_spinlock_init( XPTR( ref_cxy , &mutex_ptr->lock ) ); // register mutex in reference process xlist xptr_t root_xp = XPTR( ref_cxy , &ref_ptr->mutex_root ); xptr_t xp_list = XPTR( ref_cxy , &mutex_ptr->list ); remote_spinlock_lock( XPTR( ref_cxy , &ref_ptr->sync_lock ) ); xlist_add_first( root_xp , xp_list ); remote_spinlock_unlock( XPTR( ref_cxy , &ref_ptr->sync_lock ) ); return 0; } // end remote_mutex_create() //////////////////////////////////////////// void remote_mutex_destroy( xptr_t mutex_xp ) { // get pointer on local process descriptor process_t * process = CURRENT_THREAD->process; // get extended pointer on reference process xptr_t ref_xp = process->ref_xp; // get reference process cluster and local pointer cxy_t ref_cxy = GET_CXY( ref_xp ); process_t * ref_ptr = (process_t *)GET_PTR( ref_xp ); // get mutex cluster and local pointer cxy_t mutex_cxy = GET_CXY( mutex_xp ); remote_mutex_t * mutex_ptr = (remote_mutex_t *)GET_PTR( mutex_xp ); // remove mutex from reference process xlist remote_spinlock_lock( XPTR( ref_cxy , &ref_ptr->sync_lock ) ); xlist_unlink( XPTR( mutex_cxy , &mutex_ptr->list ) ); remote_spinlock_unlock( XPTR( ref_cxy , &ref_ptr->sync_lock ) ); // release memory allocated for mutexaphore descriptor if( mutex_cxy == local_cxy ) // reference is local { kmem_req_t req; req.type = KMEM_MUTEX; req.ptr = mutex_ptr; kmem_free( &req ); } else // reference is remote { rpc_kcm_free_client( mutex_cxy , mutex_ptr , KMEM_BARRIER ); } } // end remote_mutex_destroy() ///////////////////////////////////////// void remote_mutex_lock( xptr_t mutex_xp ) { bool_t success; reg_t irq_state; // get cluster and local pointer on remote mutex remote_mutex_t * mutex_ptr = (remote_mutex_t *)GET_PTR( mutex_xp ); cxy_t mutex_cxy = GET_CXY( mutex_xp ); // get cluster and local pointer on calling thread cxy_t thread_cxy = local_cxy; thread_t * thread_ptr = CURRENT_THREAD; // get extended pointers on mutex value xptr_t value_xp = XPTR( mutex_cxy , &mutex_ptr->value ); // Try to take the mutex success = hal_remote_atomic_cas( value_xp , 0 , 1 ); if( success ) // take the lock { // register calling thread as mutex owner xptr_t owner_xp = XPTR( mutex_cxy , &mutex_ptr->owner ); hal_remote_swd( owner_xp , XPTR( thread_cxy , thread_ptr ) ); // increment calling thread remote_locks hal_remote_atomic_add( XPTR( thread_cxy , &thread_ptr->remote_locks ) , 1 ); } else // deschedule and register calling thread in queue { // disable interrupts hal_disable_irq( &irq_state ); // register calling thread in mutex waiting queue xptr_t root_xp = XPTR( mutex_cxy , &mutex_ptr->root ); xptr_t entry_xp = XPTR( thread_cxy , &thread_ptr->wait_list ); remote_spinlock_lock( XPTR( mutex_cxy , &mutex_ptr->lock ) ); xlist_add_last( root_xp , entry_xp ); remote_spinlock_unlock( XPTR( mutex_cxy , &mutex_ptr->lock ) ); // block & deschedule the calling thread thread_block( XPTR( local_cxy , thread_ptr ) , THREAD_BLOCKED_USERSYNC ); sched_yield("blocked on mutex"); // restore interrupts hal_restore_irq( irq_state ); } hal_fence(); } // end remote_mutex_lock() /////////////////////////////////////////// void remote_mutex_unlock( xptr_t mutex_xp ) { reg_t irq_state; // get cluster and local pointer on remote mutex remote_mutex_t * mutex_ptr = (remote_mutex_t *)GET_PTR( mutex_xp ); cxy_t mutex_cxy = GET_CXY( mutex_xp ); // get cluster and local pointer on calling thread cxy_t thread_cxy = local_cxy; thread_t * thread_ptr = CURRENT_THREAD; // get extended pointers on mutex value, root, lock & owner fields xptr_t value_xp = XPTR( mutex_cxy , &mutex_ptr->value ); xptr_t owner_xp = XPTR( mutex_cxy , &mutex_ptr->owner ); xptr_t root_xp = XPTR( mutex_cxy , &mutex_ptr->root ); // disable interrupts hal_disable_irq( &irq_state ); // unregister owner thread, hal_remote_swd( owner_xp , XPTR_NULL ); // decrement calling thread remote_locks hal_remote_atomic_add( XPTR( thread_cxy , &thread_ptr->remote_locks ) , -1 ); // activate first waiting thread if required if( xlist_is_empty( root_xp ) == false ) // one waiiting thread { // get extended pointer on first waiting thread xptr_t thread_xp = XLIST_FIRST_ELEMENT( root_xp , thread_t , wait_list ); // remove first waiting thread from queue remote_spinlock_lock( XPTR( mutex_cxy , &mutex_ptr->lock ) ); xlist_unlink( XPTR( mutex_cxy , &mutex_ptr->list ) ); remote_spinlock_unlock( XPTR( mutex_cxy , &mutex_ptr->lock ) ); // unblock first waiting thread thread_unblock( thread_xp , THREAD_BLOCKED_USERSYNC ); } else // no waiting thread { // release mutex hal_remote_sw( value_xp , 0 ); } // restore interrupts hal_restore_irq( irq_state ); } // end remote_mutex_unlock()