/* * remote_condvar.c - distributed kernel condvar implementaion * * Author 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 #include #include #include /////////////////////////////////////////////////// xptr_t remote_condvar_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 condvars list xptr_t root_xp = XPTR( ref_cxy , &ref_ptr->condvar_root ); // scan reference process condvars list xptr_t iter_xp; xptr_t condvar_xp; cxy_t condvar_cxy; struct remote_condvar_s * condvar_ptr; intptr_t current; bool_t found = false; XLIST_FOREACH( root_xp , iter_xp ) { condvar_xp = XLIST_ELEMENT( iter_xp , remote_condvar_t , list ); condvar_cxy = GET_CXY( condvar_xp ); condvar_ptr = (remote_condvar_t *)GET_PTR( condvar_xp ); current = (intptr_t)hal_remote_lpt( XPTR( condvar_cxy , &condvar_ptr->ident ) ); if( ident == current ) { found = true; break; } } if( found == false ) return XPTR_NULL; else return condvar_xp; } // end remote_condvar_from_ident() /////////////////////////////////////////////// error_t remote_condvar_create( intptr_t ident ) { xptr_t condvar_xp; remote_condvar_t * condvar_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 condvar descriptor if( ref_cxy == local_cxy ) // local cluster is the reference { kmem_req_t req; req.type = KMEM_CONDVAR; req.flags = AF_ZERO; condvar_ptr = kmem_alloc( &req ); condvar_xp = XPTR( local_cxy , condvar_ptr ); } else // reference is remote { rpc_kcm_alloc_client( ref_cxy , KMEM_CONDVAR , &condvar_xp ); condvar_ptr = (remote_condvar_t *)GET_PTR( condvar_xp ); } if( condvar_ptr == NULL ) return ENOMEM; // initialise condvar hal_remote_spt( XPTR( ref_cxy , &condvar_ptr->ident ) , (void*)ident ); xlist_entry_init( XPTR( ref_cxy , &condvar_ptr->list ) ); xlist_root_init( XPTR( ref_cxy , &condvar_ptr->root ) ); // register condvar in reference process xlist xptr_t root_xp = XPTR( ref_cxy , &ref_ptr->condvar_root ); xptr_t xp_list = XPTR( ref_cxy , &condvar_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_condvar_create() //////////////////////////////////////////////// void remote_condvar_destroy( xptr_t condvar_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 condvar cluster and local pointer cxy_t condvar_cxy = GET_CXY( condvar_xp ); remote_condvar_t * condvar_ptr = (remote_condvar_t *)GET_PTR( condvar_xp ); // remove condvar from reference process xlist remote_spinlock_lock( XPTR( ref_cxy , &ref_ptr->sync_lock ) ); xlist_unlink( XPTR( condvar_cxy , &condvar_ptr->list ) ); remote_spinlock_unlock( XPTR( ref_cxy , &ref_ptr->sync_lock ) ); // release memory allocated for condvaraphore descriptor if( condvar_cxy == local_cxy ) // reference is local { kmem_req_t req; req.type = KMEM_BARRIER; req.ptr = condvar_ptr; kmem_free( &req ); } else // reference is remote { rpc_kcm_free_client( condvar_cxy , condvar_ptr , KMEM_BARRIER ); } } // end remote_condvar_destroy() //////////////////////////////////////////// void remote_condvar_wait( xptr_t condvar_xp, xptr_t mutex_xp ) { // unlock the mutex remote_mutex_unlock( mutex_xp ); thread_t * this = CURRENT_THREAD; // get condvar cluster an local pointer remote_condvar_t * condvar_ptr = (remote_condvar_t *)GET_PTR( condvar_xp ); cxy_t condvar_cxy = GET_CXY( condvar_xp ); // get extended pointer on condvar waiting queue xptr_t root_xp = XPTR( condvar_cxy , &condvar_ptr->root ); // get extended pointer on calling thread xlist_entry xptr_t entry_xp = XPTR( local_cxy , &this->wait_list ); // register the calling thread in the condvar waiting queue remote_spinlock_lock( XPTR( condvar_cxy , &condvar_ptr->lock ) ); xlist_add_last( root_xp , entry_xp ); remote_spinlock_unlock( XPTR( condvar_cxy , &condvar_ptr->lock ) ); // block the calling thread thread_block( XPTR( local_cxy , CURRENT_THREAD ) , THREAD_BLOCKED_USERSYNC ); sched_yield("blocked on condvar"); // lock the mutex before return remote_mutex_unlock( mutex_xp ); } // end remote_condvar_wait() /////////////////////////////////////////////// void remote_condvar_signal( xptr_t condvar_xp ) { reg_t irq_state; // get condvar cluster an local pointer remote_condvar_t * condvar_ptr = (remote_condvar_t *)GET_PTR( condvar_xp ); cxy_t condvar_cxy = GET_CXY( condvar_xp ); // get extended pointer on condvar waiting queue xptr_t root_xp = XPTR( condvar_cxy , &condvar_ptr->root ); if( xlist_is_empty( root_xp ) ) return; // disable interrupts hal_disable_irq( &irq_state ); // get extended pointer on the first waiting thread xptr_t thread_xp = XLIST_FIRST_ELEMENT( root_xp , thread_t , wait_list ); // remove the first waiting thread from queue remote_spinlock_lock( XPTR( condvar_cxy , &condvar_ptr->lock ) ); xlist_unlink( XPTR( condvar_cxy , &condvar_ptr->list ) ); remote_spinlock_unlock( XPTR( condvar_cxy , &condvar_ptr->lock ) ); // unblock first waiting thread thread_unblock( thread_xp , THREAD_BLOCKED_USERSYNC ); // restore interrupts hal_restore_irq( irq_state ); } // end remote_condvar_signal() ////////////////////////////////////////////////// void remote_condvar_broadcast( xptr_t condvar_xp ) { reg_t irq_state; // get condvar cluster an local pointer remote_condvar_t * condvar_ptr = (remote_condvar_t *)GET_PTR( condvar_xp ); cxy_t condvar_cxy = GET_CXY( condvar_xp ); // get extended pointer on condvar waiting queue xptr_t root_xp = XPTR( condvar_cxy , &condvar_ptr->root ); if( xlist_is_empty( root_xp ) ) return; // disable interrupts hal_disable_irq( &irq_state ); // loop on waiting threads xptr_t iter_xp; xptr_t thread_xp; XLIST_FOREACH( root_xp , iter_xp ) { // get extended pointer on waiting thread thread_xp = XLIST_ELEMENT( iter_xp , thread_t , wait_list ); // remove waiting thread from queue remote_spinlock_lock( XPTR( condvar_cxy , &condvar_ptr->lock ) ); xlist_unlink( XPTR( condvar_cxy , &condvar_ptr->list ) ); remote_spinlock_unlock( XPTR( condvar_cxy , &condvar_ptr->lock ) ); // unblock waiting thread thread_unblock( thread_xp , THREAD_BLOCKED_USERSYNC ); } // restore interrupts hal_restore_irq( irq_state ); } // end remote_condvar_broadcast()