/* * dev_ioc.c - IOC (Block Device Controler) generic device API implementation. * * Author Alain Greiner (2016) * * Copyright (c) UPMC Sorbonne Universites * * This file is part of ALMOS-MK * * 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-kernel; 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 ///////////////////////////////////////////////////////////////////////////////////////// // Extern global variables ///////////////////////////////////////////////////////////////////////////////////////// extern devices_directory_t devices_dir; // allocated in kernel_init.c extern devices_input_irq_t devices_input_irq; // allocated in kernel_init.c ////////////////////////////////// void dev_ioc_init( xptr_t dev_xp ) { // get IOC device descriptor cluster and local pointer cxy_t dev_cxy = GET_CXY( dev_xp ); device_t * dev_ptr = (device_t *)GET_PTR( dev_xp ); // get implementation from device descriptor uint32_t impl = hal_remote_lw( XPTR( dev_cxy , &dev_ptr->impl ) ); // set driver specific fields in device descriptor // and call driver init function if( impl == IMPL_IOC_BDV ) { hal_remote_spt( XPTR( dev_cxy , &dev_ptr->cmd ) , &soclib_bdv_command ); hal_remote_spt( XPTR( dev_cxy , &dev_ptr->isr ) , &soclib_bdv_isr ); hal_remote_memcpy( XPTR( dev_cxy , &dev_ptr->name ), XPTR( local_cxy , "IOC_BDV" ) , 16 ); soclib_bdv_init( dev_xp ); } else if( impl == IMPL_IOC_HBA ) { hal_remote_spt( XPTR( dev_cxy , &dev_ptr->cmd ) , &soclib_hba_command ); hal_remote_spt( XPTR( dev_cxy , &dev_ptr->isr ) , &soclib_hba_isr ); hal_remote_memcpy( XPTR( dev_cxy , &dev_ptr->name ), XPTR( local_cxy , "IOC_HBA" ) , 16 ); soclib_hba_init( dev_xp ); } // else if( impl == IMPL_IOC_SDC ) // { // hal_remote_spt( XPTR( dev_cxy , &dev_ptr->cmd ) , &soclib_sdc_command ); // hal_remote_spt( XPTR( dev_cxy , &dev_ptr->isr ) , &soclib_sdc_isr ); // hal_remote_memcpy( XPTR( dev_cxy , &dev_ptr->name ), // XPTR( local_cxy , "IOC_SDC" ) , 16 ); // soclib_sdc_init( dev_xp ); // } // else if( impl == IMPL_IOC_SPI ) // { // hal_remote_spt( XPTR( dev_cxy , &dev_ptr->cmd ) , &soclib_spi_command ); // hal_remote_spt( XPTR( dev_cxy , &dev_ptr->isr ) , &soclib_spi_isr ); // hal_remote_memcpy( XPTR( dev_cxy , &dev_ptr->name ), // XPTR( local_cxy , "IOC_SPI" ) , 16 ); // soclib_spi_init( dev_xp ); // } // else if( impl == IMPL_IOC_RDK ) // { // hal_remote_spt( XPTR( dev_cxy , &dev_ptr->cmd ) , &soclib_rdk_command ); // hal_remote_spt( XPTR( dev_cxy , &dev_ptr->isr ) , &soclib_rdk_isr ); // hal_remote_memcpy( XPTR( dev_cxy , &dev_ptr->name ), // XPTR( local_cxy , "IOC_RDK" ) , 16 ); // soclib_rdk_init( dev_xp ); // } else { printk("\n[PANIC] in %s: undefined IOC device implementation\n", __FUNCTION__ ); hal_core_sleep(); } // create server thread thread_t * new_thread_ptr; xptr_t new_thread_xp; error_t error; if( dev_cxy == local_cxy ) // device cluster is local { error = thread_kernel_create( &new_thread_ptr, THREAD_DEV, &dev_ioc_server, dev_ptr, cluster_select_local_core() ); new_thread_xp = XPTR( local_cxy , new_thread_ptr ); } else // device cluster is remote { rpc_thread_kernel_create_client( dev_cxy, THREAD_DEV, &dev_ioc_server, dev_ptr, &new_thread_xp, &error ); new_thread_ptr = (thread_t *)GET_PTR( new_thread_xp ); } if( error ) { printk("\n[PANIC] in %s : cannot create server thread\n", __FUNCTION__ ); hal_core_sleep(); } // set "server" field in device descriptor hal_remote_spt( XPTR( dev_cxy , &dev_ptr->server ) , new_thread_ptr ); // start server thread thread_unblock( new_thread_xp , THREAD_BLOCKED_GLOBAL ); } // end dev_ioc_init() ////////////////////////////////////////////////////////////////////////////////// // This static function is called by dev_ioc_read() & dev_ioc_write() functions. // It builds and registers the command in the calling thread descriptor, after // translation of buffer virtual address to physical address. // Then, it registers the calling thead in device waiting queue. // Finally it blocks on the THREAD_BLOCKED_DEV condition and deschedule. ////////////////////////////////////i///////////////////////////////////////////// static error_t dev_ioc_access( bool_t to_mem, char * buffer, uint32_t lba, uint32_t count ) { thread_t * this = CURRENT_THREAD; // pointer on client thread cxy_t local_cxy = local_cxy; // client thread cluster error_t error; paddr_t buf_paddr; bool_t ident = CONFIG_KERNEL_IDENTITY; // Get buffer physical address error = vmm_v2p_translate( ident , buffer , &buf_paddr ); if( error ) return EINVAL; ioc_dmsg("\n[INFO] in %s : thread %x in process %x" " for lba = %x / vaddr = %x / paddr = %llx\n", __FUNCTION__ , this->trdid , this->process->pid , lba , (uint32_t)buffer , buf_paddr ); #if USE_IOB // software L2/L3 cache coherence for memory buffer if ( to_mem ) dev_mmc_inval( buf_paddr, count<<9 ); else dev_mmc_sync( buf_paddr, count<<9 ); #endif // end software L2/L3 cache coherence // get extended pointer on IOC device descriptor xptr_t dev_xp = devices_dir.ioc; if ( dev_xp == XPTR_NULL ) { printk("\n[PANIC] in %s : undefined IOC device descriptor\n", __FUNCTION__ ); hal_core_sleep(); } // get a free WTI mailbox uint32_t wti_id; while( 1 ) { wti_id = dev_icu_wti_alloc(); if( wti_id == -1 ) sched_yield(); else break; } // enable WTI IRQ in local ICU and update WTI interrupt vector dev_icu_enable_irq( local_cxy, CURRENT_CORE->lid , WTI_TYPE , wti_id , dev_xp ); // link IOC IRQ to WTI mailbox in PIC component uint32_t irq_id = devices_input_irq.ioc; dev_pic_bind_irq( irq_id , local_cxy , wti_id ); // store command in thread descriptor this->dev.ioc.dev_xp = dev_xp; this->dev.ioc.to_mem = to_mem; this->dev.ioc.buf_xp = XPTR( local_cxy , buffer ); this->dev.ioc.lba = lba; this->dev.ioc.count = count; // register client thread in waiting queue, activate server thread, // block client thread on THREAD_BLOCKED_IO and deschedule. // it is re-activated by the ISR signaling IO operation completion. device_register_command( dev_xp , this ); // access PIC to unlink the IOC IRQ dev_pic_unbind_irq( irq_id ); // disable WTI IRQ in ICU and update interrupt vector dev_icu_disable_irq( local_cxy , CURRENT_CORE->lid , WTI_TYPE , wti_id ); // release WTI mailbox dev_icu_wti_release( wti_id ); ioc_dmsg("\n[INFO] in %s : thread %x in process %x completes / error = %d\n", __FUNCTION__ , this->trdid , this->process->pid , this->dev.ioc.error ); // return I/O operation status return this->dev.ioc.error; } // end dev_ioc_access() //////////////////////////////////////////// error_t dev_ioc_read( char * buffer, uint32_t lba, uint32_t count ) { return dev_ioc_access( true , buffer , lba , count ); } //////////////////////////////////////////// error_t dev_ioc_write( char * buffer, uint32_t lba, uint32_t count ) { return dev_ioc_access( false , buffer , lba , count ); } ///////////////////////////////////// void dev_ioc_server( device_t * dev ) { xptr_t client_xp; // extended pointer on waiting thread cxy_t client_cxy; // cluster of client thread thread_t * client_ptr; // local pointer on client thread thread_t * server; // local pointer on server thread xptr_t root_xp; // extended pointer on device waiting queue root server = CURRENT_THREAD; root_xp = XPTR( local_cxy , &dev->wait_root ); // infinite loop handling commands registered in the IOC waiting queue // TODO If we want to implement an "elevator" mecanism (i.e. sort all // pending command on the LBA to optimize physical device accesses), // it should be done in this loop... while( 1 ) { // get lock protecting queue remote_spinlock_lock( XPTR( local_cxy , &dev->wait_lock ) ); // block and deschedule server thread if waiting queue empty if( xlist_is_empty( root_xp ) ) { thread_block( server , THREAD_BLOCKED_DEV_QUEUE ); remote_spinlock_unlock( XPTR( local_cxy , &dev->wait_lock ) ); sched_yield(); } else { remote_spinlock_unlock( XPTR( local_cxy , &dev->wait_lock ) ); } // get extended pointer on first client thread client_xp = XLIST_FIRST_ELEMENT( root_xp , thread_t , wait_list ); // call driver command function to start I/O operation dev->cmd( client_xp ); // get client thread cluster and local pointer client_cxy = GET_CXY( client_xp ); client_ptr = (thread_t *)GET_PTR( client_xp ); // remove the client thread from waiting queue remote_spinlock_lock( XPTR( local_cxy , &dev->wait_lock ) ); xlist_unlink( XPTR( client_cxy , &client_ptr->wait_list ) ); remote_spinlock_unlock( XPTR( local_cxy , &dev->wait_lock ) ); } // end while } // end dev_ioc_server()