source: trunk/kernel/devices/dev_ioc.c @ 1

Last change on this file since 1 was 1, checked in by alain, 5 years ago

First import

File size: 11.0 KB
Line 
1/*
2 * dev_ioc.c - IOC (Block Device Controler) generic device API implementation.
3 *
4 * Author  Alain Greiner    (2016)
5 *
6 * Copyright (c) UPMC Sorbonne Universites
7 *
8 * This file is part of ALMOS-MK
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-kernel; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24#include <almos_config.h>
25#include <hal_types.h>
26#include <hal_gpt.h>
27#include <soclib_bdv.h>
28#include <soclib_hba.h>
29//#include <soclib_sdc.h>
30//#include <soclib_spi.h>
31//#include <soclib_rdk.h>
32#include <thread.h>
33#include <printk.h>
34#include <dev_ioc.h>
35
36/////////////////////////////////////////////////////////////////////////////////////////
37// Extern global variables
38/////////////////////////////////////////////////////////////////////////////////////////
39
40extern devices_directory_t  devices_dir;         // allocated in kernel_init.c
41
42extern devices_input_irq_t  devices_input_irq;   // allocated in kernel_init.c
43
44//////////////////////////////////
45void dev_ioc_init( xptr_t dev_xp )
46{
47    // get IOC device descriptor cluster and local pointer
48    cxy_t      dev_cxy = GET_CXY( dev_xp );
49    device_t * dev_ptr = (device_t *)GET_PTR( dev_xp );
50
51    // get implementation from device descriptor
52    uint32_t  impl = hal_remote_lw( XPTR( dev_cxy , &dev_ptr->impl ) );
53
54    // set driver specific fields in device descriptor
55    // and call driver init function
56    if( impl == IMPL_IOC_BDV )
57    {
58        hal_remote_spt( XPTR( dev_cxy , &dev_ptr->cmd ) , &soclib_bdv_command );
59        hal_remote_spt( XPTR( dev_cxy , &dev_ptr->isr ) , &soclib_bdv_isr );
60        hal_remote_memcpy( XPTR( dev_cxy , &dev_ptr->name ),
61                           XPTR( local_cxy , "IOC_BDV" ) , 16 );
62        soclib_bdv_init( dev_xp );
63    }
64    else if( impl == IMPL_IOC_HBA )
65    {
66        hal_remote_spt( XPTR( dev_cxy , &dev_ptr->cmd ) , &soclib_hba_command );
67        hal_remote_spt( XPTR( dev_cxy , &dev_ptr->isr ) , &soclib_hba_isr );
68        hal_remote_memcpy( XPTR( dev_cxy , &dev_ptr->name ),
69                           XPTR( local_cxy , "IOC_HBA" ) , 16 );
70        soclib_hba_init( dev_xp );
71    }
72//    else if( impl == IMPL_IOC_SDC )
73//    {
74//        hal_remote_spt( XPTR( dev_cxy , &dev_ptr->cmd ) , &soclib_sdc_command );
75//        hal_remote_spt( XPTR( dev_cxy , &dev_ptr->isr ) , &soclib_sdc_isr );
76//        hal_remote_memcpy( XPTR( dev_cxy , &dev_ptr->name ),
77//                           XPTR( local_cxy , "IOC_SDC" ) , 16 );
78//        soclib_sdc_init( dev_xp );
79//    }
80//    else if( impl == IMPL_IOC_SPI )
81//    {
82//        hal_remote_spt( XPTR( dev_cxy , &dev_ptr->cmd ) , &soclib_spi_command );
83//        hal_remote_spt( XPTR( dev_cxy , &dev_ptr->isr ) , &soclib_spi_isr );
84//        hal_remote_memcpy( XPTR( dev_cxy , &dev_ptr->name ),
85//                           XPTR( local_cxy , "IOC_SPI" ) , 16 );
86//        soclib_spi_init( dev_xp );
87//    }
88//    else if( impl == IMPL_IOC_RDK )
89//    {
90//        hal_remote_spt( XPTR( dev_cxy , &dev_ptr->cmd ) , &soclib_rdk_command );
91//        hal_remote_spt( XPTR( dev_cxy , &dev_ptr->isr ) , &soclib_rdk_isr );
92//        hal_remote_memcpy( XPTR( dev_cxy , &dev_ptr->name ),
93//                           XPTR( local_cxy , "IOC_RDK" ) , 16 );
94//        soclib_rdk_init( dev_xp );
95//    }
96    else
97    {
98        printk("\n[PANIC] in %s: undefined IOC device implementation\n", __FUNCTION__ );
99        hal_core_sleep();
100    }
101
102    // create server thread
103    thread_t * new_thread_ptr;
104    xptr_t     new_thread_xp;
105    error_t    error;
106
107    if( dev_cxy == local_cxy )         // device cluster is local
108    {
109        error = thread_kernel_create( &new_thread_ptr,
110                                      THREAD_DEV,
111                                      &dev_ioc_server,
112                                      dev_ptr,
113                                      cluster_select_local_core() ); 
114
115        new_thread_xp = XPTR( local_cxy , new_thread_ptr );
116    }
117    else                                    // device cluster is remote
118    {
119        rpc_thread_kernel_create_client( dev_cxy,
120                                         THREAD_DEV,
121                                         &dev_ioc_server,
122                                         dev_ptr,
123                                         &new_thread_xp,
124                                         &error );
125
126        new_thread_ptr = (thread_t *)GET_PTR( new_thread_xp );
127    }
128    if( error )
129    {
130        printk("\n[PANIC] in %s : cannot create server thread\n", __FUNCTION__ );
131        hal_core_sleep();
132    }
133
134    // set "server" field in device descriptor
135    hal_remote_spt( XPTR( dev_cxy , &dev_ptr->server ) , new_thread_ptr );
136   
137    // start server thread
138    thread_unblock( new_thread_xp , THREAD_BLOCKED_GLOBAL );
139 
140}  // end dev_ioc_init()
141
142//////////////////////////////////////////////////////////////////////////////////
143// This static function is called by dev_ioc_read() & dev_ioc_write() functions.
144// It builds and registers the command in the calling thread descriptor, after
145// translation of buffer virtual address to physical address.
146// Then, it registers the calling thead in device waiting queue.
147// Finally it blocks on the THREAD_BLOCKED_DEV condition and deschedule.
148////////////////////////////////////i/////////////////////////////////////////////
149static error_t dev_ioc_access( bool_t    to_mem,
150                               char    * buffer,
151                               uint32_t  lba,
152                               uint32_t  count )
153{
154    thread_t  * this      = CURRENT_THREAD;              // pointer on client thread
155    cxy_t       local_cxy = local_cxy;                   // client thread cluster
156
157    error_t     error;
158    paddr_t     buf_paddr;
159    bool_t      ident = CONFIG_KERNEL_IDENTITY;
160
161    // Get buffer physical address
162    error = vmm_v2p_translate( ident , buffer , &buf_paddr );
163 
164    if( error )  return EINVAL;
165
166    ioc_dmsg("\n[INFO] in %s : thread %x in process %x"
167             " for lba = %x / vaddr = %x / paddr = %llx\n", 
168             __FUNCTION__ , this->trdid , this->process->pid , 
169             lba , (uint32_t)buffer , buf_paddr );
170
171#if USE_IOB    // software L2/L3 cache coherence for memory buffer
172
173    if ( to_mem )  dev_mmc_inval( buf_paddr, count<<9 );
174    else           dev_mmc_sync( buf_paddr, count<<9 );
175
176#endif     // end software L2/L3 cache coherence
177
178    // get extended pointer on IOC device descriptor
179    xptr_t  dev_xp = devices_dir.ioc;
180
181    if ( dev_xp == XPTR_NULL )
182    {
183        printk("\n[PANIC] in %s : undefined IOC device descriptor\n", __FUNCTION__ );
184        hal_core_sleep();
185    }
186
187    // get a free WTI mailbox 
188    uint32_t wti_id;
189    while( 1 )
190    {
191        wti_id = dev_icu_wti_alloc();
192        if( wti_id == -1 )  sched_yield();
193        else                break;
194    }
195
196    // enable WTI IRQ in local ICU and update WTI interrupt vector
197    dev_icu_enable_irq( local_cxy, CURRENT_CORE->lid , WTI_TYPE , wti_id , dev_xp );
198
199    // link IOC IRQ to WTI mailbox in PIC component
200    uint32_t irq_id = devices_input_irq.ioc; 
201    dev_pic_bind_irq( irq_id , local_cxy , wti_id );
202
203    // store command in thread descriptor
204    this->dev.ioc.dev_xp    = dev_xp;
205    this->dev.ioc.to_mem    = to_mem;
206    this->dev.ioc.buf_xp    = XPTR( local_cxy , buffer );
207    this->dev.ioc.lba       = lba;
208    this->dev.ioc.count     = count;
209
210    // register client thread in waiting queue, activate server thread,
211    // block client thread on THREAD_BLOCKED_IO and deschedule.
212    // it is re-activated by the ISR signaling IO operation completion.
213    device_register_command( dev_xp , this );
214
215    // access PIC to unlink the IOC IRQ
216    dev_pic_unbind_irq( irq_id );
217
218    // disable WTI IRQ in ICU and update interrupt vector
219    dev_icu_disable_irq( local_cxy , CURRENT_CORE->lid , WTI_TYPE , wti_id );
220
221    // release  WTI mailbox
222    dev_icu_wti_release( wti_id );
223
224    ioc_dmsg("\n[INFO] in %s : thread %x in process %x completes / error = %d\n", 
225             __FUNCTION__ , this->trdid , this->process->pid , this->dev.ioc.error );
226
227    // return I/O operation status
228    return this->dev.ioc.error; 
229
230}  // end dev_ioc_access()
231
232////////////////////////////////////////////
233error_t dev_ioc_read( char         * buffer,
234                      uint32_t       lba,
235                      uint32_t       count )
236{
237    return dev_ioc_access( true , buffer , lba , count ); 
238} 
239
240////////////////////////////////////////////
241error_t dev_ioc_write( char         * buffer,
242                       uint32_t       lba,
243                       uint32_t       count )
244{
245    return dev_ioc_access( false , buffer , lba , count ); 
246}
247                       
248/////////////////////////////////////
249void dev_ioc_server( device_t * dev )
250{
251    xptr_t     client_xp;    // extended pointer on waiting thread
252    cxy_t      client_cxy;   // cluster of client thread
253    thread_t * client_ptr;   // local pointer on client thread
254    thread_t * server;       // local pointer on server thread
255    xptr_t     root_xp;      // extended pointer on device waiting queue root
256
257    server    = CURRENT_THREAD;
258
259    root_xp   = XPTR( local_cxy , &dev->wait_root );
260
261        // infinite loop handling commands registered in the IOC waiting queue
262    // TODO If we want to implement an "elevator" mecanism (i.e. sort all
263    // pending command on the LBA to optimize physical device accesses),
264    // it should be done in this loop...
265
266    while( 1 )
267    {
268        // get lock protecting queue
269        remote_spinlock_lock( XPTR( local_cxy , &dev->wait_lock ) );
270
271        // block and deschedule server thread if waiting queue empty
272        if( xlist_is_empty( root_xp ) )
273        {
274            thread_block( server , THREAD_BLOCKED_DEV_QUEUE );
275            remote_spinlock_unlock( XPTR( local_cxy , &dev->wait_lock ) );
276            sched_yield();
277        }
278        else
279        {
280            remote_spinlock_unlock( XPTR( local_cxy , &dev->wait_lock ) );
281        } 
282
283        // get extended pointer on first client thread
284        client_xp = XLIST_FIRST_ELEMENT( root_xp , thread_t , wait_list );
285
286        // call driver command function to start I/O operation
287        dev->cmd( client_xp );
288       
289        // get client thread cluster and local pointer
290        client_cxy = GET_CXY( client_xp );
291        client_ptr = (thread_t *)GET_PTR( client_xp );
292
293        // remove the client thread from waiting queue
294        remote_spinlock_lock( XPTR( local_cxy , &dev->wait_lock ) );
295        xlist_unlink( XPTR( client_cxy , &client_ptr->wait_list ) );
296        remote_spinlock_unlock( XPTR( local_cxy , &dev->wait_lock ) );
297
298    }  // end while
299
300}  // end dev_ioc_server()
Note: See TracBrowser for help on using the repository browser.