/* * dev_nic.c - NIC (Network Controler) generic device API implementation. * * 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 ///////////////////////////////////////////////////////////////////////////////////////// // 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_nic_init( xptr_t dev_xp ) { // get 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 "impl" , "channel" , "is_rx" fields from device descriptor uint32_t impl = hal_remote_lw( XPTR( dev_cxy , &dev_ptr->impl ) ); uint32_t is_rx = hal_remote_lw( XPTR( dev_cxy , &dev_ptr->is_rx ) ); uint32_t channel = hal_remote_lw( XPTR( dev_cxy , &dev_ptr->channel ) ); // set driver specific fields in device descriptor // and call driver init function if( impl == IMPL_NIC_SOC ) { hal_remote_spt( XPTR( dev_cxy , &dev_ptr->cmd ) , &soclib_nic_cmd ); hal_remote_spt( XPTR( dev_cxy , &dev_ptr->isr ) , &soclib_nic_isr ); hal_remote_memcpy( XPTR( dev_cxy , &dev_ptr->name ), XPTR( local_cxy , "NIC_SOC" ) , 16 ); soclib_nic_init( dev_xp ); } // else if( impl == IMPL_NIC_I86) // { // hal_remote_spt( XPTR( cxy , &ptr->cmd ) , &i86_nic_cmd ); // hal_remote_spt( XPTR( cxy , &ptr->isr ) , &i86_nic_isr ); // hal_remote_memcpy( XPTR( cxy , &ptr->name ), // XPTR( local_cxy , "NIC_I86" ) , 16 ); // i86_nic_init( dev ); // } else { printk("\n[PANIC] in %s : undefined NIC device implementation\n", __FUNCTION__ ); hal_core_sleep(); } // get a free WTI mailbox for this NIC device uint32_t wti_id; if( dev_cxy == local_cxy ) // NIC device cluster is local { wti_id = dev_icu_wti_alloc(); } else // NIC device cluster is remote { rpc_icu_wti_alloc_client( dev_cxy , &wti_id ); } if( wti_id == -1 ) { printk("\n[PANIC] in %s : cannot allocate WTI mailbox\n", __FUNCTION__ ); hal_core_sleep(); } // enable WTI IRQ in remote ICU and update WTI interrupt vector dev_icu_enable_irq( dev_cxy, 0 , WTI_TYPE , wti_id , dev_xp ); // link NIC IRQ to WTI mailbox in PIC component uint32_t irq_id; if( is_rx ) irq_id = devices_input_irq.nic_rx[channel]; else irq_id = devices_input_irq.nic_tx[channel]; dev_pic_bind_irq( irq_id , local_cxy , wti_id ); // 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_nic_init() /////////////////////////////////// error_t dev_nic_read( pkd_t * pkd ) { error_t error; // get pointers on this NIC-RX kernel thread thread_t * thread_ptr = CURRENT_THREAD; xptr_t thread_xp = XPTR( local_cxy , thread_ptr ); // get local pointer on core running this kernel thead core_t * core = thread_ptr->core; nic_dmsg("\n[INFO] %s enters for NIC-RX thread on core %d in cluster %x\n", __FUNCTION__ , core->lid , local_cxy ); // get pointer on NIC-RX device descriptor uint32_t channel = thread_ptr->dev_channel; xptr_t dev_xp = devices_dir.nic_rx[channel]; cxy_t dev_cxy = GET_CXY( dev_xp ); device_t * dev_ptr = (device_t *)GET_PTR( dev_xp ); if ( dev_xp == XPTR_NULL ) { printk("\n[PANIC] in %s : undefined NIC device descriptor\n", __FUNCTION__ ); hal_core_sleep(); } if ( dev_cxy != local_cxy ) { printk("\n[PANIC] in %s : device descriptor must be local\n", __FUNCTION__ ); hal_core_sleep(); } // initialize command in thread descriptor thread_ptr->dev.nic.dev_xp = dev_xp; // call driver to test readable thread_ptr->dev.nic.cmd = NIC_CMD_READABLE; dev_ptr->cmd( thread_xp ); // check error error = thread_ptr->dev.nic.error; if( error ) return error; // block and deschedule if queue non readable if( thread_ptr->dev.nic.status == false ) { // get NIC-RX IRQ index and type uint32_t irq_type = dev_ptr->irq_type; uint32_t irq_id = dev_ptr->irq_id; // enable NIC-RX IRQ dev_icu_enable_irq( local_cxy , core->lid , irq_type , irq_id , dev_xp ); // block on THREAD_BLOCKED I/O condition and deschedule thread_block( thread_ptr , THREAD_BLOCKED_IO ); sched_yield(); // disable NIC-RX channel IRQ dev_icu_disable_irq( local_cxy , core->lid , irq_type , irq_id ); } // call driver for actual read thread_ptr->dev.nic.cmd = NIC_CMD_READ; thread_ptr->dev.nic.buffer = pkd->buffer; dev_ptr->cmd( thread_xp ); // check error error = thread_ptr->dev.nic.error; if( error ) return error; // returns packet length pkd->length = thread_ptr->dev.nic.length; nic_dmsg("\n[INFO] %s exit for NIC-RX thread on core %d in cluster %x\n", __FUNCTION__ , core->lid , local_cxy ); return 0; } // end dev_nic_read() //////////////////////////////////// error_t dev_nic_write( pkd_t * pkd ) { error_t error; // get pointers on the NIC-TX kernel tread thread_t * thread_ptr = CURRENT_THREAD; xptr_t thread_xp = XPTR( local_cxy , thread_ptr ); // get local pointer on core running this kernel thead core_t * core = thread_ptr->core; nic_dmsg("\n[INFO] %s enters for NIC-RX thread on core %d in cluster %x\n", __FUNCTION__ , core->lid , local_cxy ); // get pointer on NIC-TX device descriptor uint32_t channel = thread_ptr->dev_channel; xptr_t dev_xp = devices_dir.nic_tx[channel]; cxy_t dev_cxy = GET_CXY( dev_xp ); device_t * dev_ptr = (device_t *)GET_PTR( dev_xp ); if ( dev_xp == XPTR_NULL ) { printk("\n[PANIC] in %s : undefined NIC device descriptor\n", __FUNCTION__ ); hal_core_sleep(); } if ( dev_cxy != local_cxy ) { printk("\n[PANIC] in %s : device descriptor must be local\n", __FUNCTION__ ); hal_core_sleep(); } // initialize command in thread descriptor thread_ptr->dev.nic.dev_xp = dev_xp; // call driver to test writable thread_ptr->dev.nic.cmd = NIC_CMD_WRITABLE; dev_ptr->cmd( thread_xp ); // check error error = thread_ptr->dev.nic.error; if( error ) return error; // block and deschedule if queue non writable if( thread_ptr->dev.nic.status == false ) { // get NIC-TX IRQ index and type uint32_t irq_type = dev_ptr->irq_type; uint32_t irq_id = dev_ptr->irq_id; // enable NIC-TX IRQ dev_icu_enable_irq( local_cxy , core->lid , irq_type , irq_id , dev_xp ); // block on THREAD_BLOCKED I/O condition and deschedule thread_block( thread_ptr , THREAD_BLOCKED_IO ); sched_yield(); // disable NIC-TX IRQ dev_icu_disable_irq( local_cxy , core->lid , irq_type , irq_id ); } // call driver for actual write thread_ptr->dev.nic.cmd = NIC_CMD_WRITE; thread_ptr->dev.nic.buffer = pkd->buffer; thread_ptr->dev.nic.length = pkd->length; dev_ptr->cmd( thread_xp ); // check error error = thread_ptr->dev.nic.error; if( error ) return error; nic_dmsg("\n[INFO] %s exit for NIC-TX thread on core %d in cluster %x\n", __FUNCTION__ , core->lid , local_cxy ); return 0; } // end dev_nic_write()