Index: /trunk/Makefile
===================================================================
--- /trunk/Makefile	(revision 656)
+++ /trunk/Makefile	(revision 657)
@@ -130,5 +130,5 @@
 	rm -f $(DISK_IMAGE)
 	./create_dmg  	create $(basename $(DISK_IMAGE))
-	dd              if=$(DISK_IMAGE) of=temp.dmg count=65536
+	dd              if=$(DISK_IMAGE) of=temp.dmg count=131072
 	mv              temp.dmg $(DISK_IMAGE)
 	mmd             -o -i $(DISK_IMAGE) ::/bin                            || true
@@ -139,5 +139,5 @@
 	mcopy           -o -i $(DISK_IMAGE) images/lena_256.raw ::/misc       || true             
 	mcopy           -o -i $(DISK_IMAGE) images/images_128.raw ::/misc     || true             
-	mcopy           -o -i $(DISK_IMAGE) images/philips_1024_2.raw ::/misc || true             
+	mcopy           -o -i $(DISK_IMAGE) images/philips_1024.raw ::/misc   || true             
 	mcopy           -o -i $(DISK_IMAGE) images/couple_512.raw ::/misc     || true             
 	mcopy           -o -i $(DISK_IMAGE) images/wood_32.raw ::/misc        || true             
Index: /trunk/hal/generic/hal_atomic.h
===================================================================
--- /trunk/hal/generic/hal_atomic.h	(revision 656)
+++ /trunk/hal/generic/hal_atomic.h	(revision 657)
@@ -2,5 +2,5 @@
  * hal_atomic.h - Generic Atomic Operations API definition.
  *
- * Authors   Alain Greiner    (2016)
+ * Authors   Alain Greiner    (2016,2017,2018,2019,2020)
  *
  * Copyright (c)  UPMC Sorbonne Universites
Index: /trunk/hal/generic/hal_remote.h
===================================================================
--- /trunk/hal/generic/hal_remote.h	(revision 656)
+++ /trunk/hal/generic/hal_remote.h	(revision 657)
@@ -2,5 +2,5 @@
  * hal_remote.h - Generic Remote Access API definition.
  *
- * Authors    Alain Greiner (2016,2017,2018,2019)
+ * Authors    Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c)  UPMC Sorbonne Universites
Index: /trunk/hal/generic/hal_uspace.h
===================================================================
--- /trunk/hal/generic/hal_uspace.h	(revision 656)
+++ /trunk/hal/generic/hal_uspace.h	(revision 657)
@@ -2,5 +2,5 @@
  * hal_uspace.h - Generic User Space Access API definition
  *
- * Authors    Alain Greiner (2016,2017,2018,2019)
+ * Authors    Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c)  UPMC Sorbonne Universites
Index: /trunk/hal/tsar_mips32/core/hal_macros.h
===================================================================
--- /trunk/hal/tsar_mips32/core/hal_macros.h	(revision 656)
+++ /trunk/hal/tsar_mips32/core/hal_macros.h	(revision 657)
@@ -26,5 +26,5 @@
 
 //////////////////////////////////////////////////////////////////////////////////////////
-//     User-side, hardware dependant, macros definition.
+//   Hardware dependant, macros definition.
 //
 //   Any architecture specific implementation must implement these macros.
Index: /trunk/hal/tsar_mips32/core/hal_remote.c
===================================================================
--- /trunk/hal/tsar_mips32/core/hal_remote.c	(revision 656)
+++ /trunk/hal/tsar_mips32/core/hal_remote.c	(revision 657)
@@ -3,5 +3,5 @@
  * 
  * Authors : Mohammed Karaoui (2015)
- *           Alain Greiner    (2016)
+ *           Alain Greiner    (2016,2017,2018,2019)
  *
  * Copyright (c) UPMC Sorbonne Universites
Index: /trunk/hal/tsar_mips32/core/hal_uspace.c
===================================================================
--- /trunk/hal/tsar_mips32/core/hal_uspace.c	(revision 656)
+++ /trunk/hal/tsar_mips32/core/hal_uspace.c	(revision 657)
@@ -2,5 +2,5 @@
  * hal_uspace.c - implementation of Generic User Space Access API for MIPS32
  * 
- * Author        Alain Greiner   (2016,2017,2018,2019)
+ * Author        Alain Greiner   (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -54,5 +54,4 @@
     uint32_t dst = (uint32_t)GET_PTR( k_dst_xp );
     uint32_t cxy = (uint32_t)GET_CXY( k_dst_xp );
-    
  
 #if DEBUG_HAL_USPACE
Index: /trunk/hal/tsar_mips32/drivers/soclib_bdv.c
===================================================================
--- /trunk/hal/tsar_mips32/drivers/soclib_bdv.c	(revision 656)
+++ /trunk/hal/tsar_mips32/drivers/soclib_bdv.c	(revision 657)
@@ -2,5 +2,5 @@
  * soclib_bdv.c - soclib simple block device driver implementation.
  *
- * Author     Alain Greiner (2016,2017,2018)
+ * Author     Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
Index: /trunk/hal/tsar_mips32/drivers/soclib_dma.c
===================================================================
--- /trunk/hal/tsar_mips32/drivers/soclib_dma.c	(revision 656)
+++ /trunk/hal/tsar_mips32/drivers/soclib_dma.c	(revision 657)
@@ -2,5 +2,5 @@
  * soclib_dma.c - soclib Multi Channels DMA driver implementation
  *
- * Author     Alain Greiner (2017)
+ * Author     Alain Greiner (2017,2018,2019,2020)
 
  * Copyright (c) UPMC Sorbonne Universites
@@ -39,6 +39,6 @@
     chdev->isr = &soclib_dma_isr;
 
-    // enable interrupts
-	hal_remote_s32( XPTR( dma_cxy , dma_ptr + DMA_IRQ_DISABLED ) , 0 );
+    // disable interrupts
+	hal_remote_s32( XPTR( dma_cxy , dma_ptr + DMA_IRQ_DISABLED ) , 1 );
 
 } // soclib_dma_init()
@@ -47,18 +47,32 @@
 void __attribute__ ((noinline)) soclib_dma_cmd( xptr_t thread_xp )
 {
+    bool_t     sync;
     xptr_t     dev_xp;       // extended pointer on DMA devive
     xptr_t     dst_xp;       // extended pointer on destination buffer
     xptr_t     src_xp;       // extended pointer on source buffer
     uint32_t   size;         // buffer size
+    uint32_t   status;       // DMA status
 
     // get client thread cluster and local pointer
-    cxy_t      thread_cxy = GET_CXY( thread_xp );
-    thread_t * thread_ptr = (thread_t *)GET_PTR( thread_xp );
-
-    // get command arguments and extended pointer on DMA device
-    dev_xp = (xptr_t)hal_remote_l64( XPTR( thread_cxy , &thread_ptr->dma_cmd.dev_xp ) );
-    dst_xp = (xptr_t)hal_remote_l64( XPTR( thread_cxy , &thread_ptr->dma_cmd.dst_xp ) );
-    src_xp = (xptr_t)hal_remote_l64( XPTR( thread_cxy , &thread_ptr->dma_cmd.src_xp ) );
-    size   =         hal_remote_l32 ( XPTR( thread_cxy , &thread_ptr->dma_cmd.size   ) );
+    cxy_t      client_cxy = GET_CXY( thread_xp );
+    thread_t * client_ptr = (thread_t *)GET_PTR( thread_xp );
+
+#if (DEBUG_HAL_IOC_RX || DEBUG_HAL_IOC_TX)
+uint32_t    cycle        = (uint32_t)hal_get_cycles();
+thread_t  * this         = CURRENT_THREAD;
+process_t * process      = hal_remote_lpt( XPTR( th_cxy , &th_ptr->process ) );
+pid_t       client_pid   = hal_remote_l32( XPTR( th_cxy , &process->pid ) );
+trdid_t     client_trdid = hal_remote_l32( XPTR( th_cxy , &th_ptr->trdid ) );
+#endif 
+
+// TODO both the client and the server threads are allways local,
+// we could replace all these remote accesses by local accesses !!!  [AG]
+
+    // get command arguments and extended pointer on DMA device descriptor
+    sync   =         hal_remote_l32( XPTR( client_cxy , &client_ptr->dma_cmd.sync   ) );
+    dev_xp = (xptr_t)hal_remote_l64( XPTR( client_cxy , &client_ptr->dma_cmd.dev_xp ) );
+    dst_xp = (xptr_t)hal_remote_l64( XPTR( client_cxy , &client_ptr->dma_cmd.dst_xp ) );
+    src_xp = (xptr_t)hal_remote_l64( XPTR( client_cxy , &client_ptr->dma_cmd.src_xp ) );
+    size   =         hal_remote_l32( XPTR( client_cxy , &client_ptr->dma_cmd.size   ) );
 
     // get DMA device cluster and local pointer
@@ -66,10 +80,8 @@
     chdev_t * dev_ptr = (chdev_t *)GET_PTR( dev_xp );
 
-    // get extended pointer on SOCLIB-DMA peripheral
-    xptr_t     dma_xp = hal_remote_l32( XPTR( dev_cxy , &dev_ptr->base ) );
-
-    // get SOCLIB_DMA device cluster and local pointer
+    // get pointers on DMA peripheral
+    xptr_t     dma_xp  = hal_remote_l32( XPTR( dev_cxy , &dev_ptr->base ) );
     cxy_t      dma_cxy = GET_CXY( dma_xp );
-    uint32_t * dma_ptr = (uint32_t *)GET_PTR( dma_xp );
+    uint32_t * dma_ptr = GET_PTR( dma_xp );
 
     // get DMA channel index and channel base address
@@ -82,15 +94,84 @@
     uint32_t   src_msb = (uint32_t)(src_xp>>32);
 
-    // set SOCLIB_DMA registers to start tranfer operation
+    // set SOCLIB_DMA registers and launch tranfer operation
     hal_remote_s32( XPTR( dma_cxy , base + DMA_SRC     ) , src_lsb ); 
     hal_remote_s32( XPTR( dma_cxy , base + DMA_SRC_EXT ) , src_msb ); 
     hal_remote_s32( XPTR( dma_cxy , base + DMA_DST     ) , dst_lsb );
     hal_remote_s32( XPTR( dma_cxy , base + DMA_DST_EXT ) , dst_msb );
-    hal_remote_s32( XPTR( dma_cxy , base + DMA_LEN     ) , size    );
-
-    // Block and deschedule server thread
-    thread_block( XPTR( local_cxy , CURRENT_THREAD ) , THREAD_BLOCKED_ISR );
-    sched_yield("blocked on ISR");
-    
+    hal_remote_s32( XPTR( dma_cxy , base + DMA_LEN_STS ) , size    );
+
+ #if DEBUG_HAL_DMA
+if( DEBUG_HAL_DMA < cycle )
+printk("\n[%s] thread[%x,%x] launched DMA for client thread[%x,%x] / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, client_pid, client_trdid, cycle );
+#endif
+
+    // waiting policy  depends on the command type
+    // - for an asynchronous command, this function is called by the server thread
+    //   => block and deschedule after launching the transfer. 
+    //   The operation status is reported in the command by the ISR, and the
+    //   server thread is re-activated by the ISR.
+    // - for a synchronous command, this function is called by the client thread
+    //   => mask the DMA_IRQ and poll the DMA status register until transfer completion,
+    //   and reports status in the command when the transfer is completed.
+
+    if( sync )                            // client thread poll status until completion
+    {
+        while( 1 )
+        {
+            status = hal_remote_l32( XPTR( dma_cxy , base + DMA_LEN_STS ) );
+
+            if( (status == DMA_SUCCESS) || (status == DMA_IDLE) )
+            {
+                // set operation status in command
+                hal_remote_s32( XPTR( client_cxy , &client_ptr->dma_cmd.error ) , 0 );
+
+#if DEBUG_HAL_DMA
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_HAL_DMA < cycle )
+printk("\n[%s] thread[%x,%x] exit after SYNC success / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, cycle );
+#endif
+                // exit while
+                break;
+            }
+            else if( (status == DMA_ERROR_READ) || (status == DMA_ERROR_WRITE) )
+            {
+                // set operation status in command
+                hal_remote_s32( XPTR( client_cxy , &client_ptr->dma_cmd.error ) , 1 );
+
+#if DEBUG_HAL_DMA
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_HAL_DMA < cycle )
+printk("\n[%s] thread[%x,%x] exit after SYNC failure / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, cycle );
+#endif
+                // exit while
+                break;
+            }
+        }
+    }    
+    else                                // server thread block and deschedule
+    {
+        // server thread blocks on ISR
+        thread_block( XPTR( local_cxy , CURRENT_THREAD ) , THREAD_BLOCKED_ISR );
+
+        // enable DMA interrupts
+	    hal_remote_s32( XPTR( dma_cxy , dma_ptr + DMA_IRQ_DISABLED ) , 0 );
+
+        // server thread deschedules
+        sched_yield("blocked on ISR");
+
+        // disable DMA interrupts
+	    hal_remote_s32( XPTR( dma_cxy , dma_ptr + DMA_IRQ_DISABLED ) , 1 );
+
+#if DEBUG_HAL_DMA
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_HAL_DMA < cycle )
+printk("\n[%s] thread[%x,%x] exit after ASYNC / client thread[%x,%x] / cycle %d\n",
+__FUNCTION__ , this->process->pid, this->trdid, client_pid, client_trdid, cycle );
+#endif
+
+    }
 } // soclib_dma_cmd()
 
@@ -98,10 +179,10 @@
 void __attribute__ ((noinline)) soclib_dma_isr( chdev_t * chdev )
 {
-    // get extended pointer on client thread
+    // get extended pointer on server thread
+    xptr_t server_xp = XPTR( local_cxy , &chdev->server );
+
+   // get extended pointer on client thread
     xptr_t root      = XPTR( local_cxy , &chdev->wait_root );
     xptr_t client_xp = XLIST_FIRST( root , thread_t , wait_list );
-
-    // get extended pointer on server thread
-    xptr_t server_xp = XPTR( local_cxy , &chdev->server );
 
     // get client thread cluster and local pointer
@@ -113,9 +194,9 @@
     uint32_t * dma_ptr  = (uint32_t *)GET_PTR( chdev->base );
 
-    // get DMA channel base address
+    // build DMA channel base address
     uint32_t * base = dma_ptr + (DMA_SPAN * chdev->channel);
 
     // get DMA status register 
-	uint32_t status = hal_remote_l32( XPTR( dma_cxy , base + DMA_LEN ) );   
+	uint32_t status = hal_remote_l32( XPTR( dma_cxy , base + DMA_LEN_STS ) );   
 
     // acknowledge IRQ
@@ -129,7 +210,4 @@
     thread_unblock( server_xp , THREAD_BLOCKED_ISR );
 
-    // unblock client thread
-    thread_unblock( client_xp , THREAD_BLOCKED_IO );
-
 } // soclib_dma_isr()
 
Index: /trunk/hal/tsar_mips32/drivers/soclib_dma.h
===================================================================
--- /trunk/hal/tsar_mips32/drivers/soclib_dma.h	(revision 656)
+++ /trunk/hal/tsar_mips32/drivers/soclib_dma.h	(revision 657)
@@ -39,5 +39,5 @@
     DMA_SRC		     = 0,    /*! source buffer 32 LSB address bits                         */
     DMA_DST		     = 1,    /*! source buffer 32 LSB address bits                         */
-    DMA_LEN		     = 2,    /*! number of bytes (on write) / transfer status (on read)    */
+    DMA_LEN_STS	     = 2,    /*! number of bytes (on write) / transfer status (on read)    */
     DMA_RESET		 = 3,    /*! desactivate channel (can be usde to acknowledge IRQ)      */
     DMA_IRQ_DISABLED = 4,    /*! no IRQ generated if non zero                              */
@@ -60,5 +60,6 @@
 
 /********************************************************************************************
- * This function access the SOCLIB_DMA hardware register to enable interrupts.
+ * This function access the SOCLIB_DMA hardware register to disable interrupts,
+ * because the most frequent operations are supposed to be synchronous accesses.
  ********************************************************************************************
  * @ chdev     : pointer on DMA chdev descriptor.
@@ -67,10 +68,13 @@
 
 /********************************************************************************************
- * This function is called by the server thread associated to the DMA device.
- * It access the command embedded in the calling thread descriptor, (format defined in the 
- * dev_dma.h file) and access the SOCLIB_DMA hardware registers to start command execution.
- * Then it blocks on the THREAD_BLOCKED_DEV_ISR and deschedules, because each DMA channel 
- * can only execute one command at a given time. 
- * It is re-activated by the ISR signaling the transfer completion.
+ * This function can be called by the "server" thread associated to the DMA channel, for an
+ * asynchronous access, or can be directly called by the "client" thread for a synchronous
+ * access. In both cases, it get the command arguments from the calling thread descriptor,
+ * and access the DMA registers to launch the DMA transfer. 
+ * Then, the waiting policy depends on the command type:
+ * - for asynchronous access, it enables the DMA interrupts, blocks on THREAD_BLOCKED_ISR,
+ *   and deschedules. It will be re-activated by the soclib_dma_isr() function.
+ * - for a synchronous transfer, it polls the DMA status register until completion,
+ *   and reports the transfer status in the command registered in the client thread.  
  ********************************************************************************************
  * @ thread_xp  : extended pointer on the client thread.
@@ -80,7 +84,7 @@
 /********************************************************************************************
  * This Interrupt Service Routine is executed when the IRQ signaling the completion of 
- * a DMA command is received by a core. It acknowledge the IRQ by accessing the proper
- * SOCLIB_DMA register, unblock the client thread, and unblock the server thread that
- * can starts execution of a new command if the waiting queue is not emppty.
+ * an asynchronous DMA command is received by a core. It acknowledge the IRQ by accessing
+ * the proper SOCLIB_DMA register, reports the transfer status in the command registered
+ * in the client thread descriptor, and unblock the server thread.
  ********************************************************************************************
  * @ chdev     : pointer on DMA chdev descriptor.
Index: /trunk/hal/tsar_mips32/drivers/soclib_fbf.c
===================================================================
--- /trunk/hal/tsar_mips32/drivers/soclib_fbf.c	(revision 656)
+++ /trunk/hal/tsar_mips32/drivers/soclib_fbf.c	(revision 657)
@@ -2,5 +2,5 @@
  * soclib_fbf.c - soclib frame-buffer driver implementation.
  *
- * Author Alain greiner (2016,2017,2018,2019)
+ * Author Alain greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -59,10 +59,10 @@
 void __attribute__((noinline)) soclib_fbf_cmd( xptr_t thread_xp )
 {
-    uint32_t   cmd_type;    // READ / WRITE / SYNC_READ / SYNC_WRITE
-    uint32_t   offset;
-    uint32_t   length;
-    void     * buffer;      // pointer on memory buffer in user space
-    xptr_t     fbf_xp;
+    uint32_t   type;        // USER_WRITE / USER_READ / KERNEL_WRITE / KERNEL_READ
+    uint32_t   offset;      // offset in FBF (in pixels)
+    uint32_t   npixels;     // number of pixels to move
+    xptr_t     fbf_xp;      // extended pointer on FBF chdev descriptor
     uint32_t   status;      // I/0 operation status (from BDV)
+    void     * buffer;      // pointer on kernel or user buffer 
 
     // get client thread cluster and local pointer
@@ -78,9 +78,9 @@
 
     // get command arguments
-    cmd_type =         hal_remote_l32( XPTR( th_cxy , &th_ptr->fbf_cmd.type   ) );
-    offset   =         hal_remote_l32( XPTR( th_cxy , &th_ptr->fbf_cmd.offset ) );
-    length   =         hal_remote_l32( XPTR( th_cxy , &th_ptr->fbf_cmd.length ) );
-    buffer   =         hal_remote_lpt( XPTR( th_cxy , &th_ptr->fbf_cmd.buffer ) );
-    fbf_xp   = (xptr_t)hal_remote_l64( XPTR( th_cxy , &th_ptr->fbf_cmd.dev_xp ) );
+    type     = hal_remote_l32( XPTR( th_cxy , &th_ptr->fbf_cmd.type    ) );
+    offset   = hal_remote_l32( XPTR( th_cxy , &th_ptr->fbf_cmd.offset  ) );
+    npixels  = hal_remote_l32( XPTR( th_cxy , &th_ptr->fbf_cmd.npixels ) );
+    fbf_xp   = hal_remote_l64( XPTR( th_cxy , &th_ptr->fbf_cmd.dev_xp  ) );
+    buffer   = hal_remote_lpt( XPTR( th_cxy , &th_ptr->fbf_cmd.buffer  ) );
 
     // get cluster and local pointer on FBF chdev
@@ -92,45 +92,61 @@
 
     // execute command
-    if( cmd_type == FBF_READ )     // use a (kernel -> user) memcpy
+    if( type == FBF_DRIVER_USER_WRITE )     // user_buffer => FBF 
     {
 
 #if DEBUG_HAL_FBF 
 if( DEBUG_HAL_FBF < cycle )
-printk("\n[%s] client thread[%x,%x] / READ / offset %d / length %d / buffer %x / fbf (%x,%x)\n",
-__FUNCTION__ , client_pid, client_trdid,
-offset, length, buffer, GET_CXY(base_xp), GET_PTR(base_xp) );
+printk("\n[%s] client thread[%x,%x] / USER_WRITE / offset %d / npixels %d / buf %x\n",
+__FUNCTION__ , client_pid, client_trdid, offset, npixels, buffer );
 #endif
-        hal_copy_to_uspace( buffer,
-                            base_xp + offset,
-                            length );
-#if DEBUG_HAL_FBF 
-if( DEBUG_HAL_FBF < cycle )
-printk("\n[%s] client thread[%x,%x] / READ successful / cycle %d\n",
-__FUNCTION__ , client_pid, client_trdid , cycle );
-#endif
-
+        hal_copy_from_uspace( base_xp + offset,
+                              buffer,
+                              npixels );
     }
-    else  // cmd_type == FBF_WRITE => use a (user -> kernel)  memcpy
+    else if( type == FBF_DRIVER_USER_READ )  // FBF => user_buffer
     {
 
 #if DEBUG_HAL_FBF 
 if( DEBUG_HAL_FBF < cycle )
-printk("\n[%s] client thread[%x,%x] / WRITE / offset %d / length %d / buffer %x / fbf (%x,%x)\n",
-__FUNCTION__ , client_pid, client_trdid, 
-offset, length, buffer, GET_CXY(base_xp), GET_PTR(base_xp) );
+printk("\n[%s] client thread[%x,%x] / USER_READ / offset %d / npixels %d / buf %x\n",
+__FUNCTION__ , client_pid, client_trdid, offset, npixels, buffer );
 #endif
-        hal_copy_from_uspace( base_xp + offset,
-                              buffer,
-                              length );
+        hal_copy_to_uspace( buffer,
+                            base_xp + offset,
+                            npixels );
+    }
+    else if( type == FBF_DRIVER_KERNEL_WRITE )  // kernel_buffer => FBF
+    {
+
 #if DEBUG_HAL_FBF 
 if( DEBUG_HAL_FBF < cycle )
-printk("\n[%s] client thread[%x,%x] / WRITE successful / cycle %d\n",
-__FUNCTION__ , client_pid, client_trdid , cycle );
+printk("\n[%s] client thread[%x,%x] / KERNEL_WRITE / offset %d / npixels %d / buf %x\n",
+__FUNCTION__ , client_pid, client_trdid, offset, npixels, buffer );
 #endif
+        hal_remote_memcpy( base_xp + offset,
+                           XPTR( local_cxy , buffer ),
+                           npixels );
+    }
+    else if( type == FBF_DRIVER_KERNEL_READ )  // FBF => kernel_buffer
+    {
 
+#if DEBUG_HAL_FBF 
+if( DEBUG_HAL_FBF < cycle )
+printk("\n[%s] client thread[%x,%x] / KERNEL_READ / offset %d / npixels %d / buf %x\n",
+__FUNCTION__ , client_pid, client_trdid, offset, npixels, buffer );
+#endif
+        hal_remote_memcpy( XPTR( local_cxy , buffer ),
+                           base_xp + offset,
+                           npixels );
     }
 
     // set success in command
     hal_remote_s32( XPTR( th_cxy , &th_ptr->fbf_cmd.error ) , 0 );
+
+#if DEBUG_HAL_FBF 
+if( DEBUG_HAL_FBF < cycle )
+printk("\n[%s] client thread[%x,%x] / successful move / cycle %d\n",
+__FUNCTION__ , client_pid, client_trdid , cycle );
+#endif
             
 }  // end soclib_fbf_cmd()
Index: /trunk/hal/tsar_mips32/drivers/soclib_mmc.c
===================================================================
--- /trunk/hal/tsar_mips32/drivers/soclib_mmc.c	(revision 656)
+++ /trunk/hal/tsar_mips32/drivers/soclib_mmc.c	(revision 657)
@@ -2,5 +2,5 @@
  * soclib_mmc.c - soclib L2 cache driver implementation.
  *
- * Author     Alain Greiner (2016,2017,2018,2019)
+ * Author     Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -18,8 +18,7 @@
  *
  * You should have received a copy of the GNU General Public License
- * along with ALMOS-MKH.; if not, write to the Free Software Foundation,
+ * along with ALMOS-MKH; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  */
-
 
 #include <hal_kernel_types.h>
@@ -29,5 +28,4 @@
 #include <thread.h>
 #include <printk.h>
-
 
 ///////////////////////////////////////
@@ -44,5 +42,4 @@
     *(base + (SOCLIB_MMC_ERROR_FUNC << 7) + SOCLIB_MMC_ERROR_IRQ_ENABLE) = 1;
 } 
-
 
 //////////////////////////////////////////////////////////////
@@ -85,25 +82,44 @@
 
         // set SOCLIB_MMC registers to start INVAL/SYNC operation
-        hal_remote_s32( XPTR( seg_cxy , seg_ptr + SOCLIB_MMC_ADDR_LO    ) , (uint32_t)buf_ptr );
-        hal_remote_s32( XPTR( seg_cxy , seg_ptr + SOCLIB_MMC_ADDR_HI    ) , (uint32_t)dev_cxy );
-        hal_remote_s32( XPTR( seg_cxy , seg_ptr + SOCLIB_MMC_BUF_LENGTH ) , buf_size ); 
-        hal_remote_s32( XPTR( seg_cxy , seg_ptr + SOCLIB_MMC_CMD_TYPE   ) , cc_cmd ); 
+        hal_remote_s32( XPTR( seg_cxy , seg_ptr +
+        MMC_REG( SOCLIB_MMC_CC_FUNC , SOCLIB_MMC_ADDR_LO) ) , (uint32_t)buf_ptr );
+
+        hal_remote_s32( XPTR( seg_cxy , seg_ptr +
+        MMC_REG( SOCLIB_MMC_CC_FUNC , SOCLIB_MMC_ADDR_HI) ) , dev_cxy );
+
+        hal_remote_s32( XPTR( seg_cxy , seg_ptr +
+        MMC_REG( SOCLIB_MMC_CC_FUNC , SOCLIB_MMC_BUF_LENGTH) ) , buf_size );
+
+        hal_remote_s32( XPTR( seg_cxy , seg_ptr +
+        MMC_REG( SOCLIB_MMC_CC_FUNC , SOCLIB_MMC_CMD_TYPE) ) , cc_cmd );
     }
-    else  // (type == MMC_GET_ERROR) or (type == MMC_GET_ERROR) pr (type == MMC_GET_INSTRU )
+    else if( (type == MMC_ERROR_GET) || (type == MMC_ERROR_SET) )
     {
         // get src/dst buffer local pointer and register index
         reg_ptr   = (uint32_t *)hal_remote_lpt( XPTR( th_cxy , &th_ptr->mmc_cmd.reg_ptr ) );
-        reg_index = hal_remote_l32( XPTR( th_cxy , &th_ptr->mmc_cmd.reg_index ) );
+        reg_index =             hal_remote_l32( XPTR( th_cxy , &th_ptr->mmc_cmd.reg_index ) );
 
-        // move register to/from local buffer 
-        if( (type == MMC_GET_ERROR) || (type == MMC_GET_INSTRU) )
-        {
-            *reg_ptr =  hal_remote_l32( XPTR( seg_cxy , seg_ptr + reg_index ) );
-        }
-        else  // type == MMC_SET_ERROR
-        {
-            hal_remote_s32( XPTR( seg_cxy , seg_ptr + reg_index ) , *reg_ptr );
-        }
+        // build extended pointer on relevant MMC register
+        xptr_t mmc_reg_xp = XPTR( seg_cxy , seg_ptr +
+                                  MMC_REG( SOCLIB_MMC_ERROR_FUNC , reg_index ) );
+
+        // move MMC register to/from local buffer 
+        if( type == MMC_ERROR_GET ) *reg_ptr = hal_remote_l32( mmc_reg_xp );
+        else                        hal_remote_s32( mmc_reg_xp , *reg_ptr );
     }
+    else  // type == MMC_INSTRU_GET
+    {
+        // get dst buffer local pointer and register index
+        reg_ptr   = (uint32_t *)hal_remote_lpt( XPTR( th_cxy , &th_ptr->mmc_cmd.reg_ptr ) );
+        reg_index =             hal_remote_l32( XPTR( th_cxy , &th_ptr->mmc_cmd.reg_index ) );
+
+        // build extended pointer on relevant MMC register
+        xptr_t mmc_reg_xp = XPTR( seg_cxy , seg_ptr +
+                                  MMC_REG( SOCLIB_MMC_INSTR_FUNC , reg_index ) );
+
+        // move MMC register to local buffer  
+        *reg_ptr = hal_remote_l32( mmc_reg_xp );
+    }
+
 } // end soclib_mmc_cmd()
 
@@ -121,6 +137,6 @@
 
     // print an error message on kernel terminal 
-    printk("\n[ERROR] reported from MMC : cxy %x / cycle %d / bad address [%x,%x] / srcid %x\n",
-    local_cxy , (uint32_t)hal_get_cycles() , paddr_hi , paddr_lo , srcid );
+    printk("\n[ERROR] reported from MMC : bad address [%x,%x] / srcid %x / cycle %d\n",
+    paddr_hi , paddr_lo , srcid , (uint32_t)hal_get_cycles() );
 
     // reset MMC IRQ
@@ -128,5 +144,2 @@
     
 } // end soclib_mmc_isr()
-
-
-
Index: /trunk/hal/tsar_mips32/drivers/soclib_mmc.h
===================================================================
--- /trunk/hal/tsar_mips32/drivers/soclib_mmc.h	(revision 656)
+++ /trunk/hal/tsar_mips32/drivers/soclib_mmc.h	(revision 657)
@@ -2,5 +2,5 @@
  * soclib_mmc.h - TSAR L2 cache driver definition.
  *
- * Author    Alain Greiner (2016,2017,2018,2019)
+ * Author    Alain Greiner (2016,2017,2018,2019),2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -49,4 +49,12 @@
 };
 
+enum SoclibMemCacheConfigCmd
+{
+    SOCLIB_MMC_CC_NOP     = 0,
+    SOCLIB_MMC_CC_INVAL   = 1,
+    SOCLIB_MMC_CC_SYNC    = 2,
+};
+
+// CONFIG Registers indexes
 enum SoclibMemCacheCCRegs
 {
@@ -57,11 +65,5 @@
 };
 
-enum SoclibMemCacheConfigCmd
-{
-    SOCLIB_MMC_CC_NOP     = 0,
-    SOCLIB_MMC_CC_INVAL   = 1,
-    SOCLIB_MMC_CC_SYNC    = 2,
-};
-
+// INSTRU Registers indexes
 enum SoclibMemCacheInstrRegs 
 {
@@ -143,4 +145,5 @@
 };
 
+// ERROR registers indexes
 enum SoclibMemCacheRerrorRegs
 {
@@ -157,6 +160,6 @@
 
 /********************************************************************************************
- * This function initializes the SOCLIB_MMC peripheral hardware registers related
- * to error signaling. It enables the MMC peripheral IRQ.
+ * This function initializes the SOCLIB_MMC peripheral hardware registers.
+ * It enables the MMC peripheral IRQ.
  ********************************************************************************************
  * @ chdev      : local pointer on the MMC chdev descriptor.
@@ -175,5 +178,5 @@
 /********************************************************************************************
  * This Interrupt Service Routine is executed when the MMC IRQ signals a faulty address.
- * The ISR should access the vci_mem_cache component to get the faulty physical address 
+ * The ISR should access the relevant MMC register to get the faulty physical address 
  * and the associated SRCID, and acknowledges the IRQ.
  ********************************************************************************************
Index: /trunk/hal/tsar_mips32/drivers/soclib_nic.c
===================================================================
--- /trunk/hal/tsar_mips32/drivers/soclib_nic.c	(revision 656)
+++ /trunk/hal/tsar_mips32/drivers/soclib_nic.c	(revision 657)
@@ -2,5 +2,5 @@
  * soclib_nic.c - SOCLIB_NIC (Network Interface Controler) driver implementation.
  *
- * Author     Alain Greiner (2016,2017,2018,2019)
+ * Author     Alain Greiner (2016,2017,2018,2019,2020:)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -93,5 +93,5 @@
 void __attribute__ ((noinline)) soclib_nic_cmd( xptr_t thread_xp )
 {
-    uint32_t       cmd;          // command type    
+    uint32_t       type;         // command type    
     char         * buffer;       // pointer on command buffer   
     uint32_t       length;       // Ethernet packet length
@@ -109,5 +109,5 @@
 
     // get command arguments
-    cmd    = thread_ptr->nic_cmd.cmd;
+    type   = thread_ptr->nic_cmd.type;
     buffer = thread_ptr->nic_cmd.buffer;
     length = thread_ptr->nic_cmd.length;
@@ -121,5 +121,5 @@
 
     // analyse command type
-    switch( cmd )
+    switch( type )
     {
         /////////////////////////////////////////////////////////////////////////////
@@ -274,5 +274,5 @@
         break;  // end READABLE
         default: {
-            assert( false, "Unknown command <%x>\n", cmd );
+            assert( false, "Unknown command <%x>\n", type );
         }
     }
Index: /trunk/kernel/Makefile
===================================================================
--- /trunk/kernel/Makefile	(revision 656)
+++ /trunk/kernel/Makefile	(revision 657)
@@ -83,4 +83,5 @@
               build/kern/process.o          \
               build/kern/chdev.o            \
+              build/kern/socket.o           \
               build/kern/cluster.o          \
               build/kern/scheduler.o        \
@@ -126,4 +127,5 @@
               build/libk/remote_condvar.o   \
               build/libk/remote_barrier.o   \
+              build/libk/remote_buf.o       \
               build/libk/memcpy.o           \
               build/libk/htab.o             \
@@ -192,5 +194,6 @@
               build/syscalls/sys_get_nb_cores.o    \
               build/syscalls/sys_get_thread_info.o \
-              build/syscalls/sys_fbf.o
+              build/syscalls/sys_fbf.o             \
+              build/syscalls/sys_socket.o
 
 VFS_OBJS    = build/fs/vfs.o              \
Index: /trunk/kernel/devices/dev_dma.c
===================================================================
--- /trunk/kernel/devices/dev_dma.c	(revision 656)
+++ /trunk/kernel/devices/dev_dma.c	(revision 657)
@@ -2,5 +2,5 @@
  * dev_dma.c - DMA (Interrupt Controler Unit) generic device API implementation.
  *
- * Authors   Alain Greiner  (2016,2017,2018,2019)
+ * Authors   Alain Greiner  (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -84,11 +84,11 @@
 
 ////////////////////////////////////////////////
-error_t dev_dma_remote_memcpy( xptr_t    dst_xp,
-                               xptr_t    src_xp,
-                               uint32_t  size )
+error_t dev_dma_async_memcpy( xptr_t    dst_xp,
+                              xptr_t    src_xp,
+                              uint32_t  size )
 {
     thread_t * this = CURRENT_THREAD;
 
-#if CONGIG_DEBUG_DEV_DMA
+#if DEBUG_DEV_DMA
 uint32_t cycle = (uint32_t)hal_get_cycles();
 if( CONGIG_DEBUG_DEV_DMA < cycle )
@@ -107,4 +107,5 @@
 
     // register command in calling thread descriptor
+    this->dma_cmd.sync    = false;
     this->dma_cmd.dev_xp  = dev_xp;
     this->dma_cmd.dst_xp  = dst_xp;
@@ -112,10 +113,10 @@
     this->dma_cmd.size    = size;
 
-    // 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.
+    // register the client thread in waiting queue, activate the server thread,
+    // block the client thread on THREAD_BLOCKED_IO, and deschedule.
+    // it is re-activated by the server thread  when the transfer is completed.
     chdev_register_command( dev_xp );
 
-#if CONGIG_DEBUG_DEV_DMA
+#if DEBUG_DEV_DMA
 cycle = (uint32_t)hal_get_cycles();
 if( CONGIG_DEBUG_DEV_DMA < cycle )
@@ -127,4 +128,54 @@
     return this->dma_cmd.error;  
 
-}  // dev_dma_remote_memcpy()
+}  // dev_dma_async_memcpy()
 
+//////////////////////////////////////////////
+error_t dev_dma_sync_memcpy( xptr_t    dst_xp,
+                             xptr_t    src_xp,
+                             uint32_t  size )
+{
+    thread_t * this = CURRENT_THREAD;
+
+#if DEBUG_DEV_DMA
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( CONGIG_DEBUG_DEV_DMA < cycle )
+printk("\n[DBG] %s : thread %x enters / dst %l / src %l / size = %x\n",
+__FUNCTION__ , this, dst_xp, src_xp, size );
+#endif
+
+    // select DMA channel corresponding to core lid
+    uint32_t channel = this->core->lid;
+
+    // get extended pointer on selected DMA chdev descriptor
+    xptr_t  dev_xp = chdev_dir.dma[channel];
+
+// check DMA chdev definition
+assert( (dev_xp != XPTR_NULL) , "undefined DMA chdev descriptor" );
+
+    // register command in calling thread descriptor
+    this->dma_cmd.sync    = true;
+    this->dma_cmd.dev_xp  = dev_xp;
+    this->dma_cmd.dst_xp  = dst_xp;
+    this->dma_cmd.src_xp  = src_xp;
+    this->dma_cmd.size    = size;
+
+    // get driver command function
+    cxy_t       dev_cxy = GET_CXY( dev_xp );
+    chdev_t   * dev_ptr = GET_PTR( dev_xp );
+    dev_cmd_t * cmd = (dev_cmd_t *)hal_remote_lpt( XPTR( dev_cxy , &dev_ptr->cmd ) );
+
+    // call directly the blocking driver function
+    cmd( XPTR( local_cxy , this ) );
+
+#if DEBUG_DEV_DMA
+cycle = (uint32_t)hal_get_cycles();
+if( CONGIG_DEBUG_DEV_DMA < cycle )
+printk("\n[DBG] %s : thread %x exit / dst %l / src %l / size = %x\n",
+__FUNCTION__ , this, dst_xp, src_xp, size );
+#endif
+
+    // return I/O operation status from calling thread descriptor
+    return this->dma_cmd.error;  
+
+}  // dev_dma_sync_memcpy()
+
Index: /trunk/kernel/devices/dev_dma.h
===================================================================
--- /trunk/kernel/devices/dev_dma.h	(revision 656)
+++ /trunk/kernel/devices/dev_dma.h	(revision 657)
@@ -2,5 +2,5 @@
  * dev_dma.h - DMA (Direct Memory Access) generic device API definition.
  *
- * Authors   Alain Greiner  (2016,2017,2018)
+ * Authors   Alain Greiner  (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -36,6 +36,9 @@
  * to/from remote clusters. The burst size is defined by the cache line size.
  * Each DMA channel is described by a specific chdev descriptor, handling its private
- * waiting threads queue. It implement one single command : move data from a (remote)
- * source buffer to a (remote) destination buffer.
+ * waiting threads queue. It implements two blocking commands :
+ * - move synchronously data from a remote source buffer to a remote destination buffer,
+ *   using a polling policy to wait completion (No DMA_IRQ use).
+ * - move synchronously data from a remote source buffer to a remote destination buffer,
+ *   using a descheduling policy to wait completion (reactivated bythe IDMA_IRQ).
  ****************************************************************************************/
  
@@ -50,5 +53,6 @@
 typedef struct dma_command_s
 {
-    xptr_t      dev_xp;    /*! extended pointer on the DMA chdev descriptor             */
+    bool_t      sync;      /*! polling policy if true / descheduling policy if false     */
+    xptr_t      dev_xp;    /*! extended pointer on the DMA chdev descriptor              */
     xptr_t      src_xp;    /*! extended pointer on source buffer.                        */
     xptr_t      dst_xp;    /*! extended pointer on destination buffer.                   */
@@ -83,14 +87,27 @@
 /*****************************************************************************************
  * This blocking function register a DMA request in the device queue.
- * It uses a descheduling policy to wait completion, and return an error status
- * when the transfer is completed.
+ * It uses a descheduling policy to wait completion, 
+ * It return an error status when the transfer is completed.
  *****************************************************************************************
- * @ dst        : extended pointer on destination buffer.
- * @ src        : extended pointer on Rsource buffer.
+ * @ dst_xp     : extended pointer on destination buffer.
+ * @ src_xp     : extended pointer on source buffer.
  * @ size       : number of bytes to move.
  ****************************************************************************************/
-error_t dev_dma_remote_memcpy( xptr_t     dst_xp,
-                               xptr_t     src_xp,
-                               uint32_t   size );    
+error_t dev_dma_async_memcpy( xptr_t     dst_xp,
+                              xptr_t     src_xp,
+                              uint32_t   size );    
+
+/*****************************************************************************************
+ * This blocking function register a DMA request in the device queue.
+ * It uses a polling policy to wait completion.
+ * It return an error status when the transfer is completed.
+ *****************************************************************************************
+ * @ dst_xp     : extended pointer on destination buffer.
+ * @ src_xp     : extended pointer on source buffer.
+ * @ size       : number of bytes to move.
+ ****************************************************************************************/
+error_t dev_dma_sync_memcpy( xptr_t     dst_xp,
+                             xptr_t     src_xp,
+                             uint32_t   size );    
 
 
Index: /trunk/kernel/devices/dev_fbf.c
===================================================================
--- /trunk/kernel/devices/dev_fbf.c	(revision 656)
+++ /trunk/kernel/devices/dev_fbf.c	(revision 657)
@@ -2,5 +2,5 @@
  * dev_fbf.c - FBF (Frame Buffer) generic device API implementation.
  * 
- * Author  Alain Greiner    (2016,2017,2018,2019)
+ * Author  Alain Greiner    (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -26,7 +26,12 @@
 #include <hal_gpt.h>
 #include <hal_drivers.h>
+#include <hal_irqmask.h>
+#include <hal_macros.h>
+#include <hal_uspace.h>
+#include <hal_vmm.h>
 #include <thread.h>
 #include <printk.h>
 #include <string.h>
+#include <memcpy.h>
 #include <chdev.h>
 #include <dev_fbf.h>
@@ -41,8 +46,12 @@
 char * dev_fbf_cmd_str( uint32_t cmd_type )
 {
-    if     ( cmd_type == FBF_READ       )  return "READ";
-    else if( cmd_type == FBF_WRITE      )  return "WRITE";
-    else if( cmd_type == FBF_GET_CONFIG )  return "GET_CONFIG";
-    else                                   return "undefined";
+    if     ( cmd_type == FBF_GET_CONFIG     )  return "GET_CONFIG";
+    else if( cmd_type == FBF_CREATE_WINDOW  )  return "CREATE_WINDOW";
+    else if( cmd_type == FBF_DELETE_WINDOW  )  return "DELETE_WINDOW";
+    else if( cmd_type == FBF_MOVE_WINDOW    )  return "MOVE_WINDOW";
+    else if( cmd_type == FBF_REFRESH_WINDOW )  return "REFRESH_WINDOW";
+    else if( cmd_type == FBF_DIRECT_WRITE   )  return "DIRECT_WRITE";
+    else if( cmd_type == FBF_DIRECT_READ    )  return "DIRECT_READ";
+    else                                       return "undefined";
 }
 
@@ -50,8 +59,27 @@
 void dev_fbf_init( chdev_t  * fbf )
 {
+    uint32_t wid;
+
     // set chdev name
     strcpy( fbf->name, "fbf" );
 
-    // call driver init function
+    // initialize lock protecting the windows
+    remote_rwlock_init( XPTR( local_cxy , &fbf->ext.fbf.windows_lock ),
+                        LOCK_FBF_WINDOWS ); 
+
+    // initialize root of windows xlist
+    xlist_root_init( XPTR( local_cxy , &fbf->ext.fbf.windows_root ) );
+
+    // initialize windows_tbl[] array
+    for( wid = 0 ; wid < CONFIG_FBF_WINDOWS_MAX_NR ; wid++ ) 
+    {
+        fbf->ext.fbf.windows_tbl[wid] = XPTR_NULL;
+    }
+
+    // initialize wid allocator bitmap
+    bitmap_init( fbf->ext.fbf.windows_bitmap , CONFIG_FBF_WINDOWS_MAX_NR );
+
+    // call driver init function to initialize the harware FBF
+    // and initialize the width, height, and subsampling FBF chdev fields
     hal_drivers_fbf_init( fbf );
 
@@ -66,5 +94,5 @@
     xptr_t  dev_xp = chdev_dir.fbf[0];
 
-    assert( (dev_xp != XPTR_NULL) , "undefined FBF chdev descriptor" );
+assert( (dev_xp != XPTR_NULL) , "undefined FBF chdev descriptor" );
 
     // get FBF chdev cluster and local pointer
@@ -79,19 +107,282 @@
 }  // end dev_fbf_get_config()
 
-/////////////////////////////////////////////////////
-error_t dev_fbf_move_data( uint32_t   cmd_type,
-                           void     * user_buffer,
-                           uint32_t   length,
-                           uint32_t   offset )
-{
-    // get pointer on calling thread
-    thread_t * this = CURRENT_THREAD;
+///////////////////////////////////////////////
+uint32_t dev_fbf_create_window( uint32_t   nlines,
+                                uint32_t   npixels,
+                                uint32_t   l_min,
+                                uint32_t   p_min,
+                                intptr_t * user_buffer )
+{
+    kmem_req_t     req;
+    fbf_window_t * window;      // window descriptor (created in local cluster)
+    vseg_t       * vseg;        // vseg descriptor (created in reference cluster)
+    intptr_t       vseg_base;   // vseg base address in user space  
+
+    // get local pointers on calling thread and process
+    thread_t  * this    = CURRENT_THREAD;
+    process_t * process = this->process;
 
 #if DEBUG_DEV_FBF
 uint32_t   cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_DEV_FBF < cycle )
-printk("\n[%s] thread[%x,%x] : %s / buffer %x / length %d / offset %x / cycle %d\n",
-__FUNCTION__ , this->process->pid, this->trdid,  
-dev_fbf_cmd_str(cmd_type), user_buffer, length, offset, cycle );
+printk("\n[%s] thread[%x,%x] enter : nlines %d / npixels %d / l_min %d / p_min %d / cycle %d\n",
+__FUNCTION__ , process->pid, this->trdid, nlines, npixels, l_min, p_min, cycle );
+#endif
+
+    // get cluster and pointers on FBF chdev
+    xptr_t      fbf_xp  = chdev_dir.fbf[0];
+    cxy_t       fbf_cxy = GET_CXY( fbf_xp );
+    chdev_t   * fbf_ptr = GET_PTR( fbf_xp );
+
+// check fbf_xp definition
+assert( (fbf_xp != XPTR_NULL) , "undefined FBF chdev descriptor" );
+
+    // get FBF width and height
+    uint32_t fbf_width  = hal_remote_l32( XPTR( fbf_cxy , &fbf_ptr->ext.fbf.width ) );
+    uint32_t fbf_height = hal_remote_l32( XPTR( fbf_cxy , &fbf_ptr->ext.fbf.height ) );
+
+    // check new window size and coordinates
+    if( (((l_min + nlines) > fbf_height) || ((p_min + npixels) > fbf_width)) )
+    {
+        printk("\n[ERROR] in %s / thread[%x,%x]" 
+        "illegal new coordinates (%d,%d) for window (%d,%d) in fbf (%d,%d)\n",
+        process->pid, this->trdid, p_min, l_min, npixels, nlines, fbf_width, fbf_height );
+        return -1;
+    }
+
+    // build extended pointers on windows lock, root, and wid allocator 
+    xptr_t windows_lock_xp   = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_lock );
+    xptr_t windows_root_xp   = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_root );
+    xptr_t windows_bitmap_xp = XPTR( fbf_cxy ,  fbf_ptr->ext.fbf.windows_bitmap );
+ 
+    // allocate memory for the window descriptor in local cluster
+    req.type   = KMEM_KCM;
+    req.order  = bits_log2( sizeof(fbf_window_t) );
+    req.flags  = AF_ZERO | AF_KERNEL;
+    window     = kmem_alloc( &req );
+
+    if( window == NULL )
+    {
+        printk("\n[ERROR] in %s / thread[%x,%x] cannot allocate window descriptor\n",
+        __FUNCTION__, process->pid, this->trdid );
+        return -1;
+    }
+
+#if (DEBUG_DEV_FBF & 1)
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_FBF < cycle )
+printk("\n[%s] thread[%x,%x] created window descriptor %x / cycle %d\n",
+__FUNCTION__ , process->pid, this->trdid, window, cycle );
+#endif
+
+    // getpointers on reference process
+    xptr_t      ref_xp  = process->ref_xp;
+    process_t * ref_ptr = GET_PTR( ref_xp );
+    cxy_t       ref_cxy = GET_CXY( ref_xp );
+
+    // allocate a new vseg, and introduce it in the reference process VSL
+    if( ref_cxy == local_cxy )
+    {
+        vseg = vmm_create_vseg( process,            // owner process
+                                VSEG_TYPE_ANON,     // localised, public
+                                0,                  // base, unused for ANON
+                                nlines * npixels,   // size
+                                0,                  // file_offset, unused for ANON 
+                                0,                  // file_size, unused for ANON
+                                XPTR_NULL,          // mapper_xp, unused for ANON
+                                local_cxy );        // mapping cluster
+    }
+    else
+    {
+        rpc_vmm_create_vseg_client( ref_cxy,
+                                    ref_ptr,
+                                    VSEG_TYPE_ANON,
+                                    0,                 // base, unused for ANON
+                                    nlines * npixels,  // size
+                                    0,                 // file_offset, unused for ANON
+                                    0,                 // file size, unused for ANON
+                                    XPTR_NULL,         // mapper_xp, unused for ANON
+                                    local_cxy,
+                                    &vseg );
+    } 
+        
+    if( vseg == NULL )
+    {
+        printk("\n[ERROR] in %s / thread[%x,%x] cannot create vseg in reference cluster\n",
+        __FUNCTION__, process->pid, this->trdid );
+        req.ptr = (void *)window;
+        kmem_free( &req );
+        return -1;
+    }
+
+    // get vseg base
+    vseg_base = (intptr_t)hal_remote_lpt( XPTR( ref_cxy , &vseg->min ) );
+
+#if (DEBUG_DEV_FBF & 1)
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_FBF < cycle )
+printk("\n[%s] thread[%x,%x] allocated vseg / base %x / cycle %d\n",
+__FUNCTION__ , process->pid, this->trdid, vseg_base, cycle );
+#endif
+
+    // take the lock protecting windows in write mode
+    remote_rwlock_wr_acquire( windows_lock_xp );
+
+    // allocate a wid from allocator in FBF descriptor extension
+    uint32_t wid = bitmap_remote_alloc( windows_bitmap_xp , CONFIG_FBF_WINDOWS_MAX_NR );
+
+    if( wid == 0xFFFFFFFF )
+    {
+        printk("\n[ERROR] in %s / thread[%x,%x] cannot allocate buffer for window\n",
+        __FUNCTION__, process->pid, this->trdid );
+        req.ptr = (void *)window;
+        kmem_free( &req );
+        vmm_remove_vseg( process , vseg );
+        return -1;
+    }
+ 
+    // initialize window descriptor
+    window->pid     = process->pid;
+    window->wid     = wid;
+    window->height  = nlines;
+    window->width   = npixels;
+    window->l_min   = l_min;
+    window->p_min   = p_min;
+    window->hidden  = false;
+    window->buffer  = (uint8_t *)vseg_base;
+
+    // register new window in xlist rooted in FBF extension
+    xlist_add_last( windows_root_xp , XPTR( local_cxy , &window->xlist ) );
+
+    // build extended pointer on relevant entry in windows_tbl[] array
+    xptr_t windows_tbl_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_tbl[wid] );
+
+    // register new window in windows_tbl[] stored in FBF extension
+    hal_remote_s64( windows_tbl_xp , XPTR( local_cxy , window ) );
+
+    // release the lock protecting windows in write mode
+    remote_rwlock_wr_release( windows_lock_xp );
+
+#if DEBUG_DEV_FBF
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_FBF < cycle )
+printk("\n[%s] thread[%x,%x] exit / wid %d / buffer %x / cycle %d\n",
+__FUNCTION__ , this->process->pid, this->trdid, wid , window->buffer, cycle );
+#endif
+
+#if (DEBUG_DEV_FBF & 1)
+hal_vmm_display( ref_xp , true );
+#endif
+
+    // return pointer on allocated buffer
+    *user_buffer = vseg_base;
+
+    return wid;
+
+}  // end dev_fbf_create_window()
+
+////////////////////////////////////////////////////////////////////////////////////////
+// This static function is called by the dev_fbf_display() function.
+// For a partial FBF line, identified by the <f_line>, <f_p_min>, <f_p_max> arguments
+// in FBF reference, and for one remote window, identified by the <window_xp> argument,
+// it updates the target buffer identified by <t_buffer>, and containing exactly
+// (f_p_max - f_p_min) pixels. 
+// Depending on the actual overlap between the window and the <t_buffer> representing 
+// the partial FBF line, it moves up to (f_p_max - f_p_min) pixels from the window 
+// buffer to the target buffer. The number of moved pixels can be nul if no overlap.
+////////////////////////////////////////////////////////////////////////////////////////
+// @ f_line     : [in]  line index in FBF reference (from 0 to fbf_height-1).
+// @ f_p_min    : [in]  first pixel in FBF line.
+// @ f_p_max    : [in]  last pixel in FBF line (excluded). 
+// @ window_xp  : [in]  extended pointer on checked window .
+// @ t_buffer   : [out] local pointer on target buffer to be updated.
+///////////////////////////////////////////////////////////////////////////////////////
+__attribute__ ((noinline)) static void handle_one_window( uint32_t  f_line,
+                                                          uint32_t  f_p_min,
+                                                          uint32_t  f_p_max,
+                                                          xptr_t    window_xp,
+                                                          uint8_t * t_buffer )
+{
+    // get remote window descriptor cluster and local pointer
+    cxy_t          window_cxy = GET_CXY( window_xp );
+    fbf_window_t * window_ptr = GET_PTR( window_xp );
+
+    // get remote window min/max coordinates in FBF reference
+    uint32_t  w_l_min    = hal_remote_l32( XPTR( window_cxy , &window_ptr->l_min ) );
+    uint32_t  w_p_min    = hal_remote_l32( XPTR( window_cxy , &window_ptr->p_min ) );
+    uint32_t  w_height   = hal_remote_l32( XPTR( window_cxy , &window_ptr->height ) );
+    uint32_t  w_width    = hal_remote_l32( XPTR( window_cxy , &window_ptr->width  ) );
+    uint32_t  w_l_max    = w_l_min + w_height;
+    uint32_t  w_p_max    = w_p_min + w_width;
+
+    // does nothing if partial FBF line does not overlap the window
+    if( (f_line < w_l_min)  || (f_line >= w_l_max) ||
+        (f_p_max < w_p_min) || (f_p_min >= w_p_max) ) return;
+
+    // get pointer on window buffer in user space
+    uint8_t * w_buffer = hal_remote_lpt( XPTR( window_cxy , &window_ptr->buffer ) ); 
+
+    // get min & max indexes for pixels to be moved in FBF reference
+    uint32_t f_pixel_min = (f_p_min < w_p_min) ? w_p_min : f_p_min;
+    uint32_t f_pixel_max = (f_p_max < w_p_max) ? f_p_max : w_p_max;
+
+    // compute number of pixels to move from w_buffer to f_buffer
+    uint32_t npixels = f_pixel_max - f_pixel_min;
+
+    // compute offset in target buffer
+    uint32_t t_offset = f_pixel_min - f_p_min; 
+
+    // compute line index in window  
+    uint32_t w_line = f_line - w_l_min;
+
+    // compute offset in window buffer
+    uint32_t w_offset = (w_line * w_height) + f_pixel_min - w_p_min;
+
+    // move pixels from w_buffer (user space) to t_buffer in kernel space
+    hal_copy_from_uspace( XPTR( local_cxy  , &t_buffer[t_offset]  ),
+                          &w_buffer[w_offset], 
+                          npixels );
+
+}  // end handle_one_window()
+
+////////////////////////////////////////////////////////////////////////////////////////
+// This static function is called by dev_fbf_refresh_window(), dev_fbf_move_window(),
+// dev_fbf_resize_window(), and dev_fbf_delete_window(). It updates all lines of the
+// window identified by the <window_xp>, <line_first>, and <line_last>> arguments. 
+// It scan all registered windows to take into account the overlap priorities defined 
+// by the windows xlist. It does not take the lock protecting the xlist, that must be 
+// taken by the calling function.
+////////////////////////////////////////////////////////////////////////////////////////
+// @ window_xp  : [in]  extended pointer on window defining the FBF pixels to refresh.
+// @ line_first : [in]  first line index.
+// @ line_last  : [in]  last line index (excluded).
+////////////////////////////////////////////////////////////////////////////////////////
+error_t fbf_update( xptr_t    window_xp,
+                    uint32_t  line_first,
+                    uint32_t  line_last )
+{
+    uint32_t       line;                // iterator to scan the FBF lines
+    uint32_t       pixel;               // iterator to scan pixels in one FBF line
+    xptr_t         iter_xp;             // iterator to scan the list of windows
+    error_t        error;
+
+    // this intermediate buffer stores one line in
+    // target window, to handle other windows overlap
+    uint8_t       line_buffer[CONFIG_FBF_WINDOWS_MAX_WIDTH];
+
+    // get pointer on calling thread and core lid
+    thread_t  * this = CURRENT_THREAD;
+
+    // get window cluster and local pointer
+    cxy_t          window_cxy = GET_CXY( window_xp );
+    fbf_window_t * window_ptr = GET_PTR( window_xp );
+
+#if DEBUG_DEV_FBF
+uint32_t wid   = hal_remote_l32( XPTR( window_cxy , &window_ptr->wid ) );
+uint32_t lid   = this->core->lid;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_FBF < cycle )
+printk("\n[%s] core[%x,%d] enter / wid %d / cycle %d\n",
+__FUNCTION__, local_cxy, lid, wid, cycle );
 #endif
 
@@ -101,6 +392,553 @@
     chdev_t   * fbf_ptr = GET_PTR( fbf_xp );
 
-// check fbf_xp definition
-assert( (fbf_xp != XPTR_NULL) , "undefined FBF chdev descriptor" );
+    // get frame buffer width
+    uint32_t fbf_width  = hal_remote_l32( XPTR( fbf_cxy , &fbf_ptr->ext.fbf.width ) );
+
+    // get pointer on driver command function
+    dev_cmd_t * cmd = hal_remote_lpt( XPTR( fbf_cxy , &fbf_ptr->cmd ) );
+
+    // build extended pointers on windows xlist root
+    xptr_t  windows_root_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_root );
+
+    // get window size and coordinates
+    uint32_t  p_min     = hal_remote_l32( XPTR( window_cxy , &window_ptr->p_min ) );
+    uint32_t  l_min     = hal_remote_l32( XPTR( window_cxy , &window_ptr->l_min ) );
+    uint32_t  w_pixels  = hal_remote_l32( XPTR( window_cxy , &window_ptr->width  ) );
+
+    error = 0;
+
+    // loop on target window lines (FBF coordinates) 
+    for( line = l_min + line_first ; line < (l_min + line_last) ; line++ )
+    {
+        // reset the line buffer to default value
+        for( pixel = 0 ; pixel < w_pixels ; pixel++ ) line_buffer[pixel] = 127;
+
+        // loop on all windows
+        XLIST_FOREACH( windows_root_xp , iter_xp )
+        {
+            // get pointers on remote window
+            xptr_t         tgt_xp  = XLIST_ELEMENT( iter_xp , fbf_window_t , xlist );
+            fbf_window_t * tgt_ptr = GET_PTR( window_xp );
+            cxy_t          tgt_cxy = GET_CXY( window_xp );
+
+            bool_t hidden = hal_remote_l32( XPTR( tgt_cxy , &tgt_ptr->hidden ) );
+
+            // fill the line_buf for this window if not hidden
+            if( hidden == false ) handle_one_window( line,               // line index
+                                                     p_min,              // pixel_min
+                                                     p_min + w_pixels,   // pixel_max 
+                                                     tgt_xp,             // window_xp      
+                                                     line_buffer );      
+        }  // end for windows
+
+        // compute offset in FBF
+        uint32_t fbf_offset = p_min + (line * fbf_width);
+
+        // register command in calling thread descriptor
+        this->fbf_cmd.dev_xp    = fbf_xp;
+        this->fbf_cmd.type      = FBF_DRIVER_KERNEL_WRITE;
+        this->fbf_cmd.buffer    = line_buffer;
+        this->fbf_cmd.npixels   = w_pixels;
+        this->fbf_cmd.offset    = fbf_offset;
+
+        // call driver to display one line
+        cmd( XPTR( local_cxy , this ) );
+
+        error |= this->fbf_cmd.error;
+    
+    }  // end for lines
+
+#if DEBUG_DEV_FBF
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_FBF < cycle )
+printk("\n[%s] core[%x,%d] exit / wid %d / cycle %d\n",
+__FUNCTION__, local_cxy, this->core->lid, wid, cycle );
+#endif
+
+    // return I/O operation status
+    return error;
+
+}  // end fbf_update()
+
+//////////////////////////////////////////////
+error_t dev_fbf_delete_window( uint32_t  wid )
+{
+    kmem_req_t     req;
+
+    thread_t  * this    = CURRENT_THREAD;
+    process_t * process = this->process;
+
+#if DEBUG_DEV_FBF
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_FBF < cycle )
+printk("\n[%s] thread[%x,%x] enters : wid %d / cycle %d\n",
+__FUNCTION__ , process->pid, this->trdid, wid, cycle );
+#endif
+
+    // get cluster and pointers on FBF chdev
+    xptr_t      fbf_xp  = chdev_dir.fbf[0];
+    cxy_t       fbf_cxy = GET_CXY( fbf_xp );
+    chdev_t   * fbf_ptr = GET_PTR( fbf_xp );
+
+    // build extended pointers on windows lock, and wid allocator 
+    xptr_t windows_lock_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_lock );
+    xptr_t wid_bitmap_xp   = XPTR( fbf_cxy ,  fbf_ptr->ext.fbf.windows_bitmap );
+
+    // build extended pointer on relevant entry in windows_tbl[] array
+    xptr_t  windows_tbl_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_tbl[wid] );
+
+    // get extended pointer on remote window descriptor
+    xptr_t window_xp  = hal_remote_l64( windows_tbl_xp );
+
+    if( window_xp == XPTR_NULL )
+    {
+        printk("\n[ERROR] in %s / thread[%x,%x] / wid %d non registered\n",
+        __FUNCTION__, process->pid, this->trdid, wid );
+        return -1;
+    }
+
+    // get cluster and local pointer on remote window
+    cxy_t          window_cxy = GET_CXY( window_xp );
+    fbf_window_t * window_ptr = GET_PTR( window_xp );
+
+    // get process owner PID
+    pid_t owner_pid = hal_remote_l32( XPTR( window_cxy , &window_ptr->pid ) );
+
+    // check caller PID / owner PID
+    if( owner_pid != process->pid )
+    {
+        printk("\n[ERROR] in %s : caller PID (%x) != owner PID (%x)\n",
+        __FUNCTION__, process->pid , owner_pid );
+        return -1;
+    }
+
+    // get associated buffer, and number of lines
+    uint8_t  * buffer = hal_remote_lpt( XPTR( window_cxy , &window_ptr->buffer ) );
+    uint32_t   nlines = hal_remote_l32( XPTR( window_cxy , &window_ptr->height ) );
+
+    // 1. take the lock protecting windows in write mode
+    remote_rwlock_wr_acquire( windows_lock_xp );
+
+    // 2. update the FBF window
+    fbf_update( window_xp , 0 , nlines );
+
+    // 3. remove the window from windows_tbl[] array
+    hal_remote_s64( windows_tbl_xp , XPTR_NULL );
+
+    // 4. remove the window from xlist      
+    xlist_unlink( XPTR( window_cxy , &window_ptr->xlist ) );
+
+    // 5. release wid to bitmap
+    bitmap_remote_clear( wid_bitmap_xp , wid );
+
+    // 6. release the lock protecting windows in write mode
+    remote_rwlock_wr_release( windows_lock_xp );
+ 
+    // 7. release memory allocated for window descriptor
+    req.type = KMEM_KCM;
+    req.ptr  = window_ptr;
+    kmem_remote_free( window_cxy , &req );
+
+    // 8. release the associated vseg
+    vmm_global_delete_vseg( process , (intptr_t)buffer );
+    
+#if DEBUG_DEV_FBF
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_FBF < cycle )
+printk("\n[%s] thread[%x,%x] exit / cycle %d\n",
+__FUNCTION__ , process->pid, this->trdid, cycle );
+#endif
+
+    return 0;
+
+}  // end dev_fbf_delete_window()
+
+////////////////////////////////////////////
+error_t dev_fbf_move_window( uint32_t  wid,
+                             uint32_t  l_min,
+                             uint32_t  p_min )
+{
+    thread_t  * this    = CURRENT_THREAD;
+    process_t * process = this->process;
+
+#if DEBUG_DEV_FBF
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_FBF < cycle )
+printk("\n[%s] thread[%x,%x] enters : wid %d / l_min %d / p_min %d / cycle %d\n",
+__FUNCTION__ , process->pid, this->trdid, wid, l_min, p_min, cycle );
+#endif
+
+    // get cluster and pointers on FBF chdev
+    xptr_t      fbf_xp  = chdev_dir.fbf[0];
+    cxy_t       fbf_cxy = GET_CXY( fbf_xp );
+    chdev_t   * fbf_ptr = GET_PTR( fbf_xp );
+
+    // build extended pointers on windows lock and root 
+    xptr_t windows_lock_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_lock );
+    xptr_t windows_root_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_root );
+
+    // build extended pointer on relevant entry in windows_tbl[] array
+    xptr_t  windows_tbl_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_tbl[wid] );
+
+    // get extended pointer on remote window descriptor
+    xptr_t window_xp  = hal_remote_l64( windows_tbl_xp );
+
+    if( window_xp == XPTR_NULL )
+    {
+        printk("\n[ERROR] in %s / thread[%x,%x] / wid %d non registered\n",
+        __FUNCTION__, process->pid, this->trdid, wid );
+        return -1;
+    }
+
+    // get cluster and local pointer for remote window
+    cxy_t          window_cxy = GET_CXY( window_xp );
+    fbf_window_t * window_ptr = GET_PTR( window_xp );
+
+    // get process owner PID, coordinates, and number of lines
+    pid_t    owner_pid = hal_remote_l32( XPTR( window_cxy , &window_ptr->pid ) );
+    uint32_t p_zero    = hal_remote_l32( XPTR( window_cxy , &window_ptr->p_min ) );
+    uint32_t l_zero    = hal_remote_l32( XPTR( window_cxy , &window_ptr->l_min ) );
+    uint32_t nlines    = hal_remote_l32( XPTR( window_cxy , &window_ptr->height ) );
+
+    // check caller PID / owner PID
+    if( owner_pid != process->pid )
+    {
+        printk("\n[ERROR] in %s : caller PID (%x) != owner PID (%x)\n",
+        __FUNCTION__, process->pid , owner_pid );
+        return -1;
+    }
+
+    // does nothing if no change
+    if( (p_zero == p_min) && (l_zero == l_min) )  return 0;
+
+    // 1. take the lock protecting windows in write mode
+    remote_rwlock_wr_acquire( windows_lock_xp );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] lock taken\n", __FUNCTION__ );
+#endif
+
+    // 2. gives the window the lowest priority 
+    xptr_t xlist_entry_xp =  XPTR( window_cxy , &window_ptr->xlist );
+    xlist_unlink( xlist_entry_xp );
+    xlist_add_first( windows_root_xp , xlist_entry_xp );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] set low priority \n", __FUNCTION__ );
+#endif
+
+    // 3. set the "hidden" flag in window descriptor
+    hal_remote_s32( XPTR( window_cxy , &window_ptr->hidden ) , true );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] hidden set\n", __FUNCTION__ );
+#endif
+
+    // 4. refresh the FBF for the current window position
+    fbf_update( window_xp , 0 , nlines );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] refreshed old position\n", __FUNCTION__ );
+#endif
+
+    // 5. set the new coordinates in the window descriptor,
+    hal_remote_s32( XPTR( window_cxy , &window_ptr->l_min ), l_min );
+    hal_remote_s32( XPTR( window_cxy , &window_ptr->p_min ), p_min );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] l_min & p_min updated\n", __FUNCTION__ );
+#endif
+
+    // 6. gives the window the highest priority 
+    xlist_unlink( xlist_entry_xp );
+    xlist_add_last( windows_root_xp , xlist_entry_xp );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] set high priority\n", __FUNCTION__ );
+#endif
+
+    // 7. reset the "hidden" flag in window descriptor
+    hal_remote_s32( XPTR( window_cxy , &window_ptr->hidden ) , false );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] hidden reset\n", __FUNCTION__ );
+#endif
+
+    // 8. refresh the FBF for the new window position
+    fbf_update( window_xp , 0 , nlines );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] refresh new position\n", __FUNCTION__ );
+#endif
+
+    // 9. release the lock protecting windows in write mode
+    remote_rwlock_wr_release( windows_lock_xp );
+ 
+#if DEBUG_DEV_FBF
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_FBF < cycle )
+printk("\n[%s] thread[%x,%x] exit / cycle %d\n",
+__FUNCTION__ , process->pid, this->trdid, cycle );
+#endif
+
+    return 0;
+
+}  // end dev_fbf_move_window()
+
+/////////////////////////////////////////////
+error_t dev_fbf_resize_window( uint32_t  wid,
+                               uint32_t  width,
+                               uint32_t  height )
+{
+    thread_t  * this    = CURRENT_THREAD;
+    process_t * process = this->process;
+
+#if DEBUG_DEV_FBF
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_FBF < cycle )
+printk("\n[%s] thread[%x,%x] enters : wid %d / width %d / height %d / cycle %d\n",
+__FUNCTION__ , process->pid , this->trdid , wid, width , height , cycle );
+#endif
+
+    // get cluster and pointers on FBF chdev
+    xptr_t      fbf_xp  = chdev_dir.fbf[0];
+    cxy_t       fbf_cxy = GET_CXY( fbf_xp );
+    chdev_t   * fbf_ptr = GET_PTR( fbf_xp );
+
+    // build extended pointers on windows lock and root 
+    xptr_t windows_lock_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_lock );
+    xptr_t windows_root_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_root );
+
+    // build extended pointer on relevant entry in windows_tbl[] array
+    xptr_t  windows_tbl_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_tbl[wid] );
+
+    // get extended pointer on remote window descriptor
+    xptr_t window_xp  = hal_remote_l64( windows_tbl_xp );
+
+    if( window_xp == XPTR_NULL )
+    {
+        printk("\n[ERROR] in %s / thread[%x,%x] / wid %d non registered\n",
+        __FUNCTION__, process->pid, this->trdid, wid );
+        return -1;
+    }
+
+    // get cluster and local pointer for remote window
+    cxy_t          window_cxy = GET_CXY( window_xp );
+    fbf_window_t * window_ptr = GET_PTR( window_xp );
+
+    // get process owner PID, width, height, and buffer
+    pid_t    owner_pid = hal_remote_l32( XPTR( window_cxy , &window_ptr->pid ) );
+    uint32_t nlines    = hal_remote_l32( XPTR( window_cxy , &window_ptr->height ) );
+    uint32_t npixels   = hal_remote_l32( XPTR( window_cxy , &window_ptr->width ) );
+    void   * base      = hal_remote_lpt( XPTR( window_cxy , &window_ptr->buffer ) );
+
+    // check caller PID / owner PID
+    if( owner_pid != process->pid )
+    {
+        printk("\n[ERROR] in %s : caller PID (%x) != owner PID (%x)\n",
+        __FUNCTION__, process->pid , owner_pid );
+        return -1;
+    }
+
+    // does nothing if no change
+    if( (width == npixels) && (height == nlines) ) return 0;
+
+    // compute old_size and new size
+    uint32_t old_size = nlines * npixels;
+    uint32_t new_size = width * height;
+
+    // 1. take the lock protecting windows in write mode
+    remote_rwlock_wr_acquire( windows_lock_xp );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] lock taken\n", __FUNCTION__ );
+#endif
+
+    // 2. gives the window the lowest priority (remove, then add first)
+    xptr_t xlist_entry_xp =  XPTR( window_cxy , &window_ptr->xlist );
+    xlist_unlink( xlist_entry_xp );
+    xlist_add_first( windows_root_xp , xlist_entry_xp );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] set low priority\n", __FUNCTION__ );
+#endif
+
+    // 3. set the "hidden" flag in window descriptor
+    hal_remote_s32( XPTR( window_cxy , &window_ptr->hidden ) , true );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] hidden set\n", __FUNCTION__ );
+#endif
+
+    // 4. refresh the FBF for the current window size
+    fbf_update( window_xp , 0 , nlines );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] refreshed old window\n", __FUNCTION__ );
+#endif
+
+    // 5. set the new width & height in the window descriptor,
+    hal_remote_s32( XPTR( window_cxy , &window_ptr->width  ), width );
+    hal_remote_s32( XPTR( window_cxy , &window_ptr->height ), height );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] width & height updated\n", __FUNCTION__ );
+#endif
+
+    // 6. resize vseg if required
+    vmm_global_resize_vseg( process, (intptr_t)base, (intptr_t)base, width * height );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] vseg resized\n", __FUNCTION__ );
+#endif
+
+    // 7. fill buffer extension if required
+    if( new_size > old_size )  memset( base + old_size , 0 , new_size - old_size );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] buffer extension initialized\n", __FUNCTION__ );
+#endif
+
+    // 8. gives the window the highest priority 
+    xlist_unlink( xlist_entry_xp );
+    xlist_add_last( windows_root_xp , xlist_entry_xp );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] set high priority\n", __FUNCTION__ );
+#endif
+
+    // 9. reset the "hidden" flag in window descriptor
+    hal_remote_s32( XPTR( window_cxy , &window_ptr->hidden ) , false );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] hidden reset\n", __FUNCTION__ );
+#endif
+
+    // 10. refresh the FBF for the new window position
+    fbf_update( window_xp , 0 , height );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] refresh new position\n", __FUNCTION__ );
+#endif
+
+    // 11. release the lock protecting windows in write mode
+    remote_rwlock_wr_release( windows_lock_xp );
+ 
+#if DEBUG_DEV_FBF
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_FBF < cycle )
+printk("\n[%s] thread[%x,%x] exit / cycle %d\n",
+__FUNCTION__ , process->pid, this->trdid, cycle );
+#endif
+
+    return 0;
+
+}  // end dev_fbf_resize_window()
+
+///////////////////////////////////////////////
+error_t dev_fbf_refresh_window( uint32_t  wid,
+                                uint32_t  line_first,
+                                uint32_t  line_last )
+{
+    // get local pointers on calling thread and process
+    thread_t  * this    = CURRENT_THREAD;
+    process_t * process = this->process;
+
+#if DEBUG_DEV_FBF
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_FBF < cycle )
+printk("\n[%s] thread[%x,%x] enters for wid %d / first %d / last %d / cycle %d\n",
+__FUNCTION__ , process->pid, this->trdid, wid, line_first, line_last, cycle );
+#endif
+
+    // get cluster and pointers on FBF chdev
+    xptr_t      fbf_xp  = chdev_dir.fbf[0];
+    cxy_t       fbf_cxy = GET_CXY( fbf_xp );
+    chdev_t   * fbf_ptr = GET_PTR( fbf_xp );
+
+    // build extended pointer on windows lock
+    xptr_t  windows_lock_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_lock );
+
+    // build extended pointer on relevant entry in windows_tbl[] array
+    xptr_t  windows_tbl_xp  = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_tbl[wid] );
+
+    // get pointers on remote window descriptor
+    xptr_t         window_xp  = hal_remote_l64( windows_tbl_xp );
+    cxy_t          window_cxy = GET_CXY( window_xp );
+    fbf_window_t * window_ptr = GET_PTR( window_xp );
+
+    // check <wid> argument
+    if( window_xp == XPTR_NULL )
+    {
+        printk("\n[ERROR] in %s / thread[%x,%x] / wid %d non registered\n",
+        __FUNCTION__, process->pid, this->trdid, wid );
+        return -1;
+    }
+
+    // get process owner PID
+    pid_t owner_pid = hal_remote_l32( XPTR( window_cxy , &window_ptr->pid ) );
+
+    // check caller PID / owner PID
+    if( owner_pid != process->pid )
+    {
+        printk("\n[ERROR] in %s : caller PID (%x) != owner PID (%x)\n",
+        __FUNCTION__, process->pid , owner_pid );
+        return -1;
+    }
+
+    // get number of lines in window
+    uint32_t nlines = hal_remote_l32( XPTR( window_cxy , &window_ptr->height ) ); 
+
+    // check <line_first> and <line_last> arguments
+    if( (line_first >= nlines) || (line_last > nlines) || (line_first >= line_last) )
+    {
+        printk("\n[ERROR] in %s : illegal (l_first %d , l_last %d) / height %d\n",
+        __FUNCTION__, line_first, line_last, nlines );
+        return -1;
+    }
+
+    // take the lock protecting windows xlist in read mode
+    remote_rwlock_rd_acquire( windows_lock_xp );
+
+    // update FBF 
+    fbf_update( window_xp , line_first , line_last );
+
+    // release the lock protecting windows xlist in write mode
+    remote_rwlock_rd_release( windows_lock_xp );
+
+#if DEBUG_DEV_FBF
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_FBF < cycle )
+printk("\n[%s] thread[%x,%x] exit for wid %d / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, wid, cycle );
+#endif
+
+    return 0;
+
+}  // end dev_fbf_refresh_window()
+
+///////////////////////////////////////////////
+// TODO Deprecated : january 2020 [AG]
+///////////////////////////////////////////////
+error_t dev_fbf_move_data( bool_t     is_write,
+                           void     * user_buffer,
+                           uint32_t   npixels,
+                           uint32_t   offset )
+{
+    // get pointer on calling thread
+    thread_t * this = CURRENT_THREAD;
+
+#if DEBUG_DEV_FBF
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_FBF < cycle )
+printk("\n[%s] thread[%x,%x] :  buffer %x / npixels %d / offset %x / cycle %d\n",
+__FUNCTION__ , this->process->pid, this->trdid,  
+user_buffer, npixels, offset, cycle );
+#endif
+
+    // get pointers on FBF chdev
+    xptr_t      fbf_xp  = chdev_dir.fbf[0];
+    cxy_t       fbf_cxy = GET_CXY( fbf_xp );
+    chdev_t   * fbf_ptr = GET_PTR( fbf_xp );
 
     // get frame buffer width and height
@@ -108,14 +946,18 @@
     uint32_t height = hal_remote_l32 ( XPTR( fbf_cxy , &fbf_ptr->ext.fbf.height ) );
 
-// check offset and length versus FBF size
-assert( ((offset + length) <= (width * height)) ,
-"offset %d / length %d / width %d / height %d\n", offset, length, width, height ); 
+    // check offset and npixels versus FBF size
+    if( ((offset + npixels) > (width * height)) )
+    {
+        printk("\n[ERROR] in %s : offset (%d) + npixels (%d) / width (%d) / height (%d)\n",
+        __FUNCTION__, offset, npixels, width, height ); 
+        return -1;
+    }
 
     // register command in calling thread descriptor
     this->fbf_cmd.dev_xp    = fbf_xp;
-    this->fbf_cmd.type      = cmd_type;
+    this->fbf_cmd.type      = is_write ? FBF_DRIVER_USER_WRITE : FBF_DRIVER_USER_READ;
     this->fbf_cmd.buffer    = user_buffer;
     this->fbf_cmd.offset    = offset;
-    this->fbf_cmd.length    = length;
+    this->fbf_cmd.npixels   = npixels;
 
     // get driver command function
@@ -130,7 +972,6 @@
 cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_DEV_FBF < cycle )
-printk("\n[%s] thread[%x,%x] completes %s / error = %d / cycle %d\n",
-__FUNCTION__ , this->process->pid, this->trdid,  
-dev_fbf_cmd_str(cmd_type), error , cycle );
+printk("\n[%s] thread[%x,%x] exit / cycle %d\n",
+__FUNCTION__ , this->process->pid, this->trdid, cycle );
 #endif
 
@@ -139,2 +980,4 @@
 
 }  // end dev_fbf_move_data()
+
+
Index: /trunk/kernel/devices/dev_fbf.h
===================================================================
--- /trunk/kernel/devices/dev_fbf.h	(revision 656)
+++ /trunk/kernel/devices/dev_fbf.h	(revision 657)
@@ -1,6 +1,6 @@
 /*
- * dev_fbf.h - FBF (Block Device Controler) generic device API definition.
+ * dev_fbf.h - FBF (Frame Buffer) generic device API definition.
  * 
- * Author  Alain Greiner    (2016,2017,2018,2019)
+ * Author  Alain Greiner    (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -27,4 +27,6 @@
 #include <hal_kernel_types.h>
 #include <shared_fbf.h>
+#include <remote_rwlock.h>
+#include <bits.h>
 
 /****  Forward declarations  ****/
@@ -33,22 +35,60 @@
 
 /*****************************************************************************************
- *     Generic Frame Buffer Controler definition
+ *      Frame Buffer Controler API definition
  *
  * This device provide access to an external graphic display, that is seen
  * as a fixed size frame buffer, mapped in the kernel address space.
- * The supported  pixel encoding types are defined in the <shared_fbf.h> file.
- *
- * It supports three command types:
- * GET_CONFIG : return frame buffer size and type.
- * READ       : move bytes from frame buffer to memory / deschedule the calling thread.
- * WRITE      : move bytes from memory to frame buffer / deschedule the calling thread.
- *
- * The READ and WRITE operations do not use the FBF device waiting queue, 
- * the server thread, and the IOC IRQ. The client thread does not deschedule:
- * it registers the command in the thread descriptor, and calls directly the FBF driver.
- * that makes a (user <-> kernel) memcpy.
+ * The only pixel encoding type in the current implementation is one byte per pixel
+ * (256 levels of gray).
+ *
+ * It supports a first API, for the user syscalls, implementing a simple windows manager.
+ * This windows manager allows any process to create and use for display one (or several) 
+ * window(s). Each window defines a private buffer, dynamically allocated in user space,
+ * that can be directly accessed by the owner process.
+ * These windows can be moved in the frame buffer, they can be resized, they can overlap
+ * other windows, but a window must be entirely contained in the frame buffer.
+ *
+ * To avoid contention, the window descriptor, and the associated user buffer are not
+ * allocated in the cluster containing the FBF chdev, but are distributed: each window
+ * is allocated in the cluster defined by the thread that required the window creation.
+ *
+ * Each window has a single process owner, but all the windows are registered in the FBF
+ * chdev as a windows_tbl[] array, indexed by the window identifier (wid), and each entry
+ * contains an extended pointer on the window descriptor. All windows are also registered
+ * in a trans-cluster xlist, defining the overlapping order (last window in xlist has the
+ * highest priority). 
+ *
+ * To refresh a window <wid>, the owner process calls the dev_fbf_refresh_window()
+ * function, that sends parallel RPC_FBF_DISPLAY requests to other cores. All cores
+ * synchronously execute the dev_fbf_display() function. This function scan all windows
+ * to respect the overlaping order, and updates all pixels of the <wid> window.
  * 
- * Note: As we don't use any external DMA to move data, but a purely software approach,
- * there is no L2/L3 coherence issue.
+ * 1) This "syscall" API defines five syscalls : 
+ * - FBF_GET_CONFIG     : returns the FBF width, height, and pixel encoding type.
+ * - FBF_CREATE_WINDOW  : create a new window , owned by the calling process.
+ * - FBF_DELETE_WINDOW  : delete a registered window. 
+ * - FBF_MOVE_WINDOW    : move in FBF a registered window.
+ * - FBF_REFRESH_WINDOW : request refresh of a given window.
+ *
+ * These 5 operations do not use the FBF device waiting queue, the associated
+ * device thread and the FBF IRQ, as the client thread does NOT deschedule,
+ * and does NOT call the FBF driver.
+ *
+ * 2) Two extra syscalls exist but are deprecated:
+ * - FBF_DIRECT_WRITE   : move synchronously pixels from an user buffer to the FBF.
+ * - FBF_DIRECT_READ    : move synchronously pixels from the FBF to an user buffer.
+ *
+ * For these deprecated operations, the client thread calls
+ * directly the driver to move data between the user buffer and the FBF.
+ * 
+ * 3) The FBF device defines defines four command types to access FBF driver(s) :
+ * - FBF_DRIVER_KERNEL_WRITE : move pixels from a kernel window to the FBF.
+ * - FBF_DRIVER_KERNEL_READ  : move pixels from the FBF to a kernel window.
+ * - FBF_DRIVER_USER_WRITE   : move bytes from an user buffer to the FBF. 
+ * - FBF_DRIVER_USER_READ    : move bytes from the FBF to an user buffer. 
+ *
+ * Note: As we don't use any external DMA to move data to or from the frame buffer,
+ * but only software memcpy, there is no L2/L3 coherence issue for this device.
+ *
  *****************************************************************************************/
 
@@ -59,7 +99,13 @@
 typedef struct fbf_extend_s
 {
-    uint32_t           width;         /*! number of pixels per line.                     */
-    uint32_t           height;        /*! total number of lines.                         */
-    uint32_t           subsampling;   /*! pixel encoding type.                           */
+    remote_rwlock_t windows_lock;        /*! lock protecting windows xlist               */
+    xlist_entry_t   windows_root;        /*! root of the windows xlist                   */
+
+    xptr_t          windows_tbl[CONFIG_FBF_WINDOWS_MAX_NR];         /*! window desc.     */
+    bitmap_t        windows_bitmap[CONFIG_FBF_WINDOWS_MAX_NR >> 5]; /*! wid allocator    */
+
+    uint32_t        width;               /*! number of pixels per line.                  */
+    uint32_t        height;              /*! total number of lines.                      */
+    uint32_t        subsampling;         /*! pixel encoding type.                        */
 }
 fbf_extend_t;
@@ -70,5 +116,5 @@
  *****************************************************************************************/
 
-enum fbf_impl_e
+typedef enum
 {
     IMPL_FBF_SCL =   0,     
@@ -77,20 +123,55 @@
 fbf_impl_t;
 
+/******************************************************************************************
+ * This structure defines the FBF command for all drivers implementing the FBF device.
+ *****************************************************************************************/
+
+typedef enum
+{
+    FBF_DRIVER_USER_READ     = 1,
+    FBF_DRIVER_USER_WRITE    = 2,
+    FBF_DRIVER_KERNEL_READ   = 3,
+    FBF_DRIVER_KERNEL_WRITE  = 4,
+}
+fbf_driver_cmd_type_t;
+
 typedef struct fbf_command_s
 {
     xptr_t      dev_xp;        /*! extended pointer on device descriptor                 */
-    uint32_t    type;          /*! requested operation type.                             */
-    uint32_t    length;        /*! number of bytes.                                      */
-    uint32_t    offset;        /*! offset in frame buffer (bytes)                        */
-    void      * buffer;        /*! pointer on memory buffer in user space                */
+    uint32_t    type;          /*! requested driver operation type.                      */
+    uint32_t    npixels;       /*! number of bytes.                                      */
+    uint32_t    offset;        /*! offset in frame buffer (pixels)                       */
+    void      * buffer;        /*! pointer on memory buffer (kernel or user)             */
     uint32_t    error;         /*! operation status (0 if success)                       */
 }
 fbf_command_t;
 
-
-/******************************************************************************************
- * This function returns a printable string for a given FBF command  <cmd_type>.
- ******************************************************************************************
- * @ cmd_type   :  FBF command type (defined in shared_fbf.h file).
+/******************************************************************************************
+ * This structure defines an FBF window descriptor, allocated to a given user process.
+ * The window descriptor and the associated buffer are allocated in the cluster where
+ * is running the thread requesting the window creation.
+ * The <wid> allocator, the window_tbl[] array, and the root of the xlist of windows are
+ * imlemented in the FBF device extension.
+ *****************************************************************************************/
+
+typedef struct fbf_window_s
+{
+    pid_t          pid;         /*! owner process identifier                             */
+    uint32_t       wid;         /*! window identifier                                    */
+    uint32_t       height;      /*! number of lines in window                            */
+    uint32_t       width;       /*! number of pixels per line in window                  */
+    uint32_t       l_min;       /*! first line index in FBF                              */
+    uint32_t       p_min;       /*! first pixel index in FBF                             */
+    uint8_t      * buffer;      /*! pointer on buffer in user space                      */
+    bool_t         hidden;      /*! no display on FBF when true                          */
+    xlist_entry_t  xlist;       /*! member of registered FBF windows list                */
+}
+fbf_window_t;
+
+/******************************************************************************************
+ * This function returns a printable string for a given FBF user command  <cmd_type>.
+ * WARNING : It must be kept consistent with the enum in the <shared_fbf.h> file
+ ******************************************************************************************
+ * @ cmd_type   :  FBF user command type (defined in shared_fbf.h file).
  * @ returns a string pointer.
  *****************************************************************************************/
@@ -100,5 +181,5 @@
  * This function completes the FBF chdev descriptor initialisation.
  * It calls the specific driver initialisation function, to initialise the hardware
- * device and the chdev extension. It must be called by a local thread.
+ * device, and the chdev extension. It must be called by a local thread.
  ******************************************************************************************
  * @ chdev      : pointer on FBF chdev descriptor.
@@ -107,7 +188,9 @@
 
 /******************************************************************************************
- * This function returns the frame buffer size and type.
+ * This function implements the fbf_get_config() syscall, and returns the FBF
+ * size and type. It can be called by a client thread running in any cluster.
  * It does NOT access the hardware, as the size and type have been registered
- * in the chdev descriptor extension.
+ * in the chdev descriptor extension by the dev_fbf_init() function.
+ * It can be called by any thread running in any cluster.
  ******************************************************************************************
  * @ width     : [out] number of pixels per line.
@@ -120,20 +203,162 @@
 
 /******************************************************************************************
- * This blocking function moves <length> bytes between the frame buffer, starting from
- * byte defined by <offset>, and an user buffer defined by the <user_buffer> argument.
- * It can be called by a client thread running in any cluster.
- * The transfer direction are defined by the <cmd_type> argument.
+ * This function implements the fbf_create_window() syscall.
+ * It registers a new window in the windows_tbl[] array, and the windows list,
+ * registers in the reference cluster an ANON vseg, that will be mapped in local cluster. 
+ * The window index <wid> is dynamically allocated. The owner is the calling process.
+ * The FBF window is defined by the <nlines>, <npixels>, <l_min>, <p_min> arguments.
+ * It can be called by any thread running in any cluster. As this vseg is not directly 
+ * mapped to the frame buffer, the owner process can access this private buffer without
+ * syscall. As for any vseg, the physical memory is allocated on demand at each page fault.
+* The created vseg base address in user space is returned in the <user_base> argument.
+ *
+ * Implementation note:
+ * 1. it allocates memory in the local cluster for the window,
+ * 2. it creates in the associated vseg,
+ * 3. it initializes the window descriptor,
+ * 4. it takes the lock protecting the windows in write mode,
+ * 5. it allocates a new  <wid>,
+ * 6. it registers the window in the window_tbl[] array,
+ * 7. it registers the window in the windows list,
+ * 8. it releases the lock protecting windows.
+ * It does not call the FBF driver. 
+ ******************************************************************************************
+ * @ nlines      : [in]  number of lines in window.
+ * @ npixels     : [in]  number of pixels per line in window.
+ * @ l_min       : [in]  first pixel index in FBF.
+ * @ p_min       : [in]  first line index in FBF.
+ * @ user_base   : [out] pointer on allocated buffer base in user space.
+ * @ return the <wid> index if success / returns -1 if failure
+ *****************************************************************************************/
+uint32_t dev_fbf_create_window( uint32_t   nlines,
+                                uint32_t   npixels,
+                                uint32_t   l_min,
+                                uint32_t   p_min,
+                                intptr_t * user_base );
+
+/******************************************************************************************
+ * This function implements the fbf_delete_window() syscall to delete a FBF window,
+ * and release all memory allocated for this window and for the associated vseg.
+ * releases the memory allocated for the window buffer and for the window descriptor.
+ * It can be called by any thread running in any cluster.
+ *
+ * Implementation note:
+ * 1. it takes the lock protecting windows in write mode,
+ * 2. it set the hidden flag in deleted window descriptor,
+ * 3. it refresh the FBF window,
+ * 4. it removes the window from windows_tbl[] array,
+ * 5. it removes the window from xlist,      
+ * 6. it releases the wid to bitmap,
+ * 7. it releases the lock protecting windows,
+ * 8. it releases the memory allocated for window descriptor,
+ * 9. it deletes the associated vseg in all clusters
+ * It does not call directly the FBF driver.
+ ******************************************************************************************
+ * @ wid       : [in] window index in window_tbl[].
+ * @ returns 0 if success / returns -1 if wid not registered.
+ *****************************************************************************************/
+error_t dev_fbf_delete_window( uint32_t wid );
+                                
+/******************************************************************************************
+ * This function implements the fbf_move_window() syscall.
+ * It moves a window identified by the <wid> argument to a new position in the FBF,
+ * defined by the <l_min> and <p_min> arguments.
+ * It can be called by any thread running in any cluster.
+ *
+ * Implementation note:
+ * 1. it takes the lock protecting windows in write mode,
+ * 2. it gives the modified window the lowest priority,
+ * 3. it set the "hidden" flag in window descriptor,
+ * 4. it refresh the FBF for the current window position,
+ * 5. it set the new coordinates in the window descriptor,
+ * 6. it gives the modified window the highest priority,
+ * 7. it reset the "hidden" flag in window descriptor,
+ * 8. it refresh the FBF for the new window position,
+ * 9. it releases the lock protecting windows,
+ * It does not call directly the FBF driver.
+ ******************************************************************************************
+ * @ wid       : [in] window index in window_tbl[].
+ * @ l_min     : [in] new first pixel index in FBF.
+ * @ p_min     : [in] new first line index in FBF.
+ * @ returns 0 if success / returns -1 if illegal arguments.
+ *****************************************************************************************/
+error_t dev_fbf_move_window( uint32_t wid,
+                             uint32_t l_min,
+                             uint32_t p_min );
+
+/******************************************************************************************
+ * This function implements the fbf_resize_window() syscall.
+ * It changes the <width> and <height> of a window identified by the <wid> argument.
+ * It updates the associated vseg "size" if required, but does not change the vseg "base".
+ * When the new window buffer is larger than the existing one, it is 0 filled.
+ * It can be called by any thread running in any cluster.
+ *
+ * Implementation note:
+ * 1.  it takes the lock protecting windows in write mode,
+ * 2.  it gives the modified window the lowest priority,
+ * 3.  it set the "hidden" flag in window descriptor,
+ * 4.  it refresh the FBF for the current window,
+ * 5.  it set the new size in the window descriptor,
+ * 6.  it resizes the associated vseg if required,
+ * 7.  if fill the window buffer extension with 0 if required,
+ * 8.  it gives the modified window the highest priority,
+ * 9.  it reset the "hidden" flag in window descriptor,
+ * 10. it refresh the FBF for the new window,
+ * 11. it releases the lock protecting windows,
+ * It does not call directly the FBF driver.
+ ******************************************************************************************
+ * @ wid       : [in] window index in window_tbl[].
+ * @ width     : [in] new number of pixels per line.
+ * @ height    : [in] new number of lines.
+ * @ returns 0 if success / returns -1 if illegal arguments.
+ *****************************************************************************************/
+error_t dev_fbf_resize_window( uint32_t wid,
+                               uint32_t width,
+                               uint32_t height );
+
+/******************************************************************************************
+ * This function implements the fbf_refresh_window() syscall.
+ * It allows an owner process to signal the windows manager that some lines of a window
+ * identified by the <wid>, <line_min>, and <line_max> argument have been modified, and
+ * must be refreshed in the FBF. It scans all the registered FBF windows to respect the
+ * overlap order defined by the windows xlist.
+ * It can be called by any thread running in any cluster.
+ *
+ * Implementation note:
+ * 1. it takes the lock protecting windows in read mode,
+ * 2. it refresh the FBF,
+ * 3. it releases the lock protecting windows,
+ * It does not call directly the FBF driver.
+ ******************************************************************************************
+ * @ wid        : [in] window index in window_tbl[]
+ * @ line_first : [in] first line index in window.
+ * @ line_last  : [in] last line index (excluded).
+ * @ returns 0 if success / returns -1 if wid not registered.
+ *****************************************************************************************/
+error_t dev_fbf_refresh_window( uint32_t wid,
+                                uint32_t line_first,
+                                uint32_t line_last );
+
+/******************************************************************************************
+ * WARNING : This function is deprecated ( january 2020 [AG] ). It was defined 
+ * to implement the fbf_read() and fbf_write() deprecated syscalls.
+ *
+ * It  moves <length> bytes between the frame buffer, starting from pixel defined
+ * by the <offset> argument, and an user buffer defined by the <user_buffer> argument.
+ * The transfer direction are defined by the <is_write> argument.
+ * An error is returned when <offset> + <npixels> is larger than the FBF size.
  * The request is registered in the client thread descriptor, but the client thread is
  * not descheduled, and calls directly the FBF driver.
- ******************************************************************************************
- * @ cmd_type    : FBF_READ / FBF_WRITE / FBF_SYNC_READ / FBF_SYN_WRITE.
- * @ user_buffer : pointer on memory buffer in user space.
- * @ length      : number of bytes.
- * @ offset      : first byte in frame buffer.    
- * @ returns 0 if success / returns EINVAL if error.
- *****************************************************************************************/
-error_t dev_fbf_move_data( uint32_t   cmd_type,
+ * It can be called by a client thread running in any cluster.
+ ******************************************************************************************
+ * @ is_write    : [in] write FBF weh true / read FBF when false
+ * @ user_buffer : [in] pointer on memory buffer in user space.
+ * @ npixels     : [in] number of bytes.
+ * @ offset      : [in] first byte in frame buffer.    
+ * @ returns 0 if success / returns -1 if error.
+ *****************************************************************************************/
+error_t dev_fbf_move_data( bool_t     is_write,
                            void     * user_buffer,
-                           uint32_t   length,
+                           uint32_t   npixels,
                            uint32_t   offset );
 
Index: /trunk/kernel/devices/dev_ioc.c
===================================================================
--- /trunk/kernel/devices/dev_ioc.c	(revision 656)
+++ /trunk/kernel/devices/dev_ioc.c	(revision 657)
@@ -2,5 +2,5 @@
  * dev_ioc.c - IOC (Block Device Controler) generic device API implementation.
  *
- * Author  Alain Greiner    (2016,2017,2018,2019)
+ * Author  Alain Greiner    (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -37,6 +37,6 @@
 extern chdev_directory_t  chdev_dir;     // allocated in kernel_init.c
 
-////////////////////////////////////////
-char * dev_ioc_cmd_str( cmd_type_t cmd )
+////////////////////////////////////////////
+char * dev_ioc_cmd_str( ioc_cmd_type_t cmd )
 {
     if     ( cmd == IOC_READ       )  return "READ";
@@ -91,6 +91,44 @@
 }  // end dev_ioc_init()
 
-///////////////////////////////////////////////
-error_t dev_ioc_move_data( uint32_t   cmd_type,
+////////////////////////////////////////////////////////////////////////////////////
+// This static function executes an asynchronous SYNC_READ or SYNC_WRITE request.
+// thread in the IOC device waiting queue, activates the server thread, blocks on 
+// the THREAD_BLOCKED_IO condition and deschedules.
+// The clent is re-activated by the server thread after IO operation completion.
+////////////////////////////////////////////////////////////////////////////////////
+static error_t dev_ioc_move( uint32_t   cmd_type,
+                             xptr_t     buffer_xp,
+                             uint32_t   lba,
+                             uint32_t   count )
+{
+    thread_t * this = CURRENT_THREAD;              // pointer on client thread
+
+    // get extended pointer on IOC chdev descriptor
+    xptr_t      ioc_xp  = chdev_dir.ioc[0];
+
+// check dev_xp
+assert( (ioc_xp != XPTR_NULL) , "undefined IOC chdev descriptor" );
+
+    // register command in client thread  
+    this->ioc_cmd.dev_xp    = ioc_xp;
+    this->ioc_cmd.type      = cmd_type;
+    this->ioc_cmd.buf_xp    = buffer_xp;
+    this->ioc_cmd.lba       = lba;
+    this->ioc_cmd.count     = count;
+
+    // register client thread in IOC queue, blocks and deschedules 
+    chdev_register_command( ioc_xp );
+
+    // return I/O operation status
+    return this->ioc_cmd.error;
+
+}  // end dev_ioc_move()
+
+////////////////////////////////////////////////////////////////////////////////////
+// This static function executes a synchronous READ or WRITE request.
+// It register the command in the client thread descriptor, and calls directly
+// the driver cmd function.
+////////////////////////////////////////////////////////////////////////////////////
+error_t dev_ioc_sync_move( uint32_t   cmd_type,
                            xptr_t     buffer_xp,
                            uint32_t   lba,
@@ -98,21 +136,4 @@
 {
     thread_t * this = CURRENT_THREAD;              // pointer on client thread
-
-#if ( DEBUG_DEV_IOC_RX  || DEBUG_DEV_IOC_TX )
-uint32_t   cycle = (uint32_t)hal_get_cycles();
-#endif
-
-    // software L2/L3 cache coherence for memory buffer
-    if( chdev_dir.iob )
-    {
-        if( (cmd_type == IOC_SYNC_READ) || (cmd_type == IOC_READ) )
-        {
-            dev_mmc_inval( buffer_xp , count<<9 );
-        }
-        else  // (cmd_type == IOC_SYNC_WRITE) or (cmd_type == IOC_WRITE)
-        {
-            dev_mmc_sync ( buffer_xp , count<<9 );
-        }
-    }
 
     // get extended pointer on IOC chdev descriptor
@@ -129,85 +150,153 @@
     this->ioc_cmd.count     = count;
 
-    // for a synchronous acces, the driver is directly called by the client thread 
-    if( (cmd_type == IOC_SYNC_READ) || (cmd_type == IOC_SYNC_WRITE) )
-    {
-
-#if DEBUG_DEV_IOC_RX
-uint32_t   cycle = (uint32_t)hal_get_cycles();
-if( (DEBUG_DEV_IOC_RX < cycle) && (cmd_type == IOC_SYNC_READ) )
-printk("\n[%s] thread[%x,%x] enters for SYNC_READ / lba  %x / buffer[%x,%x] / cycle %d\n",
-__FUNCTION__ , this->process->pid, this->trdid, lba,
-GET_CXY(buffer_xp), GET_PTR(buffer_xp), cycle );
-#endif
-
-#if DEBUG_DEV_IOC_TX
-uint32_t   cycle = (uint32_t)hal_get_cycles();
-if( (DEBUG_DEV_IOC_TX < cycle) && (cmd_type == IOC_SYNC_WRITE) )
-printk("\n[%s] thread[%x,%x] enters for SYNC_WRITE / lba  %x / buffer[%x,%x] / cycle %d\n",
-__FUNCTION__ , this->process->pid, this->trdid, lba,
-GET_CXY(buffer_xp), GET_PTR(buffer_xp), cycle );
-#endif
-        // get driver command function
-        cxy_t       ioc_cxy = GET_CXY( ioc_xp );
-        chdev_t   * ioc_ptr = GET_PTR( ioc_xp );
-        dev_cmd_t * cmd = (dev_cmd_t *)hal_remote_lpt( XPTR( ioc_cxy , &ioc_ptr->cmd ) );
-
-        // call driver function
-        cmd( XPTR( local_cxy , this ) );
-
-#if DEBUG_DEV_IOC_RX
-if( (DEBUG_DEV_IOC_RX < cycle) && (cmd_type == IOC_SYNC_READ) )
-printk("\n[%s] thread[%x,%x] resumes for IOC_SYNC_READ\n",
-__FUNCTION__, this->process->pid , this->trdid )
-#endif
-
-#if DEBUG_DEV_IOC_TX
-if( (DEBUG_DEV_IOC_RX < cycle) && (cmd_type == IOC_SYNC_WRITE) )
-printk("\n[%s] thread[%x,%x] resumes for IOC_SYNC_WRITE\n",
-__FUNCTION__, this->process->pid , this->trdid )
-#endif
-
-    }
-    // for an asynchronous access, the client thread registers in the chdev waiting queue,
-    // activates server thread, blocks on THREAD_BLOCKED_IO and deschedules.
-    // It is re-activated by the server thread after IO operation completion.
-    else  // (cmd_type == IOC_READ) || (cmd_type == IOC_WRITE)
-    {
-
-#if DEBUG_DEV_IOC_RX
-uint32_t   cycle = (uint32_t)hal_get_cycles();
-if( (DEBUG_DEV_IOC_RX < cycle) && (cmd_type == IOC_READ) )
-printk("\n[%s] thread[%x,%x] enters for READ / lba  %x / buffer[%x,%x] / cycle %d\n",
-__FUNCTION__ , this->process->pid, this->trdid, lba,
-GET_CXY(buffer_xp), GET_PTR(buffer_xp), cycle );
-#endif
-
-#if DEBUG_DEV_IOC_TX
-uint32_t   cycle = (uint32_t)hal_get_cycles();
-if( (DEBUG_DEV_IOC_TX < cycle) && (cmd_type == IOC_WRITE) )
-printk("\n[%s] thread[%x,%x] enters for WRITE / lba  %x / buffer[%x,%x] / cycle %d\n",
-__FUNCTION__ , this->process->pid, this->trdid, lba,
-GET_CXY(buffer_xp), GET_PTR(buffer_xp), cycle );
-#endif
-        chdev_register_command( ioc_xp );
-
-#if(DEBUG_DEV_IOC_RX )
-if( (DEBUG_DEV_IOC_RX < cycle) && (cmd_type == IOC_READ) )
-printk("\n[%s] thread[%x,%x] resumes for IOC_READ\n",
-__FUNCTION__, this->process->pid , this->trdid )
-#endif
-
-#if(DEBUG_DEV_IOC_TX & 1)
-if( (DEBUG_DEV_IOC_RX < cycle) && (cmd_type == IOC_WRITE) )
-printk("\n[%s] thread[%x,%x] resumes for IOC_WRITE\n",
-__FUNCTION__, this->process->pid , this->trdid )
-#endif
-
-    }
+    // get driver command function
+    cxy_t       ioc_cxy = GET_CXY( ioc_xp );
+    chdev_t   * ioc_ptr = GET_PTR( ioc_xp );
+    dev_cmd_t * cmd = (dev_cmd_t *)hal_remote_lpt( XPTR( ioc_cxy , &ioc_ptr->cmd ) );
+
+    // call driver function whithout blocking & descheduling
+    cmd( XPTR( local_cxy , this ) );
 
     // return I/O operation status
     return this->ioc_cmd.error;
 
-}  // end dev_ioc_move_data()
-
-
+}  // end dev_ioc_sync_move()
+
+///////////////////////////////////////////
+error_t dev_ioc_read( xptr_t     buffer_xp,
+                      uint32_t   lba,
+                      uint32_t   count )
+{
+
+#if DEBUG_DEV_IOC_RX
+thread_t * this  = CURRENT_THREAD;
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_IOC_RX < cycle )
+printk("\n[%s] thread[%x,%x] enters IOC_READ / lba  %x / buffer[%x,%x] / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, lba,
+GET_CXY(buffer_xp), GET_PTR(buffer_xp), cycle );
+#endif
+
+    // software L2/L3 cache coherence for memory buffer
+    if( chdev_dir.iob ) dev_mmc_inval( buffer_xp , count<<9 );
+
+    // request an asynchronous transfer
+    error_t error = dev_ioc_move( IOC_READ,
+                                  buffer_xp,
+                                  lba,
+                                  count );
+#if(DEBUG_DEV_IOC_RX & 1)
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_IOC_RX < cycle )
+printk("\n[%s] thread[%x,%x] exit IOC_READ / cycle %d\n",
+__FUNCTION__, this->process->pid , this->trdid , cycle );
+#endif
+
+    return error;
+
+}  // end dev_ioc_read()
+
+///////////////////////////////////////////
+error_t dev_ioc_write( xptr_t     buffer_xp,
+                       uint32_t   lba,
+                       uint32_t   count )
+{
+
+#if DEBUG_DEV_IOC_TX
+thread_t * this  = CURRENT_THREAD;
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_IOC_TX < cycle )
+printk("\n[%s] thread[%x,%x] enters IOC_WRITE / lba  %x / buffer[%x,%x] / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, lba,
+GET_CXY(buffer_xp), GET_PTR(buffer_xp), cycle );
+#endif
+
+    // software L2/L3 cache coherence for memory buffer
+    if( chdev_dir.iob ) dev_mmc_sync ( buffer_xp , count<<9 );
+
+    // request a blocking, but asynchronous, transfer
+    error_t error = dev_ioc_move( IOC_WRITE,
+                          buffer_xp,
+                          lba,
+                          count );
+#if(DEBUG_DEV_IOC_TX & 1)
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_IOC_TX < cycle )
+printk("\n[%s] thread[%x,%x] exit IOC_WRITE / cycle %d\n",
+__FUNCTION__, this->process->pid , this->trdid , cycle );
+#endif
+
+    return error;
+
+}  // end dev_ioc_write()
+
+
+///////////////////////////////////////////
+error_t dev_ioc_sync_read( xptr_t     buffer_xp,
+                           uint32_t   lba,
+                           uint32_t   count )
+{
+
+#if DEBUG_DEV_IOC_RX
+thread_t * this  = CURRENT_THREAD;
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_IOC_RX < cycle )
+printk("\n[%s] thread[%x,%x] enters IOC_SYNC_READ / lba  %x / buffer[%x,%x] / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, lba,
+GET_CXY(buffer_xp), GET_PTR(buffer_xp), cycle );
+#endif
+
+    // software L2/L3 cache coherence for memory buffer
+    if( chdev_dir.iob ) dev_mmc_inval( buffer_xp , count<<9 );
+
+    // request an asynchronous transfer
+    error_t error = dev_ioc_sync_move( IOC_SYNC_READ,
+                                       buffer_xp,
+                                       lba,
+                                       count );
+#if(DEBUG_DEV_IOC_RX & 1)
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_IOC_RX < cycle )
+printk("\n[%s] thread[%x,%x] exit IOC_SYNC_READ / cycle %d\n",
+__FUNCTION__, this->process->pid , this->trdid , cycle );
+#endif
+
+    return error;
+
+}  // end dev_ioc_sync_read()  
+
+/////////////////////////////////////////////////
+error_t dev_ioc_sync_write( xptr_t     buffer_xp,
+                            uint32_t   lba,
+                            uint32_t   count )
+{
+
+#if DEBUG_DEV_IOC_TX
+thread_t * this  = CURRENT_THREAD;
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_IOC_TX < cycle )
+printk("\n[%s] thread[%x,%x] enters IOC_SYNC_WRITE / lba  %x / buffer[%x,%x] / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, lba,
+GET_CXY(buffer_xp), GET_PTR(buffer_xp), cycle );
+#endif
+
+    // software L2/L3 cache coherence for memory buffer
+    if( chdev_dir.iob ) dev_mmc_sync ( buffer_xp , count<<9 );
+
+    // request a blocking, but asynchronous, transfer
+    error_t error = dev_ioc_sync_move( IOC_SYNC_WRITE,
+                                       buffer_xp,
+                                       lba,
+                                       count );
+#if(DEBUG_DEV_IOC_TX & 1)
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_IOC_TX < cycle )
+printk("\n[%s] thread[%x,%x] exit IOC_SYNC_WRITE / cycle %d\n",
+__FUNCTION__, this->process->pid , this->trdid , cycle );
+#endif
+
+    return error;
+
+}  // end dev_ioc_sync_write() 
+
+
Index: /trunk/kernel/devices/dev_ioc.h
===================================================================
--- /trunk/kernel/devices/dev_ioc.h	(revision 656)
+++ /trunk/kernel/devices/dev_ioc.h	(revision 657)
@@ -2,5 +2,5 @@
  * dev_ioc.h - IOC (Block Device Controler) generic device API definition.
  * 
- * Author  Alain Greiner    (2016,2017,2018,2019)
+ * Author  Alain Greiner    (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -45,5 +45,5 @@
  * - SYNC_WRITE : move blocks from memory to device, with a busy waiting policy.
  *
- * For the he READ or WRITE operations, the client thread is descheduled, and the work
+ * For the READ or WRITE operations, the client thread is descheduled, and the work
  * is done by the server thread associated to the IOC device:
  * The client thread calls the dev_ioc_move_data() kernel functions that (i) registers
@@ -93,5 +93,5 @@
 
 /******************************************************************************************
- * This defines the (implementation independant)  command passed to the driver.
+ * This structure defines the IOC command for all drivers implementing the IOC device.
  *****************************************************************************************/
 
@@ -103,5 +103,5 @@
     IOC_SYNC_WRITE = 3,
 }
-cmd_type_t;
+ioc_cmd_type_t;
 
 typedef struct ioc_command_s
@@ -111,5 +111,5 @@
     uint32_t    lba;        /*! first block index                                        */
     uint32_t    count;      /*! number of blocks                                         */
-    xptr_t      buf_xp;     /*! extended pointer on memory buffer                        */
+    xptr_t      buf_xp;     /*! extended pointer on kernel memory buffer                 */
     uint32_t    error;      /*! operation status (0 if success)                          */
 }
@@ -122,5 +122,5 @@
  * @ return pointer on string.
  *****************************************************************************************/
-char * dev_ioc_cmd_str( cmd_type_t cmd );
+char * dev_ioc_cmd_str( ioc_cmd_type_t cmd );
 
 /******************************************************************************************
@@ -138,23 +138,74 @@
 
 /******************************************************************************************
- * This blocking function moves <count> contiguous blocks of data between the block device
- * starting from block defined by the <lba> argument and a kernel memory buffer, defined
- * by the <buffer_xp> argument. The transfer direction and mode are defined by the 
- * <cmd_type> argument. The request is always registered in the calling thread descriptor.
- * - In synchronous mode, the calling thread is not descheduled, and directly calls the
- *   IOC driver, polling the IOC status to detect transfer completion.
- * - In asynchronous mode, the calling thread blocks and deschedules, and the IOC driver
- *   is called by the server thread associated to the IOC device.
- ******************************************************************************************
- * @ cmd_type  : IOC_READ / IOC_WRITE / IOC_SYNC_READ / IOC_SYN_WRITE.
- * @ buffer_xp : extended pointer on kernel buffer in memory (must be block aligned).
- * @ lba       : first block index on device.
- * @ count     : number of blocks to transfer.
- * @ returns 0 if success / returns -1 if error.
- *****************************************************************************************/
-error_t dev_ioc_move_data( uint32_t  cmd_type,
-                           xptr_t    buffer_xp,
+ * This blocking function register an asynchronous READ request : move <count> contiguous
+ * blocks from the block device, starting from block defined by the <lba> argument, to a
+ * kernel buffer defined by the <buffer_xp> argument.
+ * It register the request in the client thread descriptor, it register the client thread
+ * in the IOC device queue, it blocks on the THREAD_BLOCKED_IO condition, and deschedules.
+ * It will be reactivated by the DEV server thread when the transfer is completed.
+ * It can be executed by a thread running in any cluster.
+ ******************************************************************************************
+ * @ buffer_xp : extended pointer on kernel buffer in memory (must be block aligned).
+ * @ lba       : first block index on device.
+ * @ count     : number of blocks to transfer.
+ * @ returns 0 if success / returns -1 if error.
+ *****************************************************************************************/
+error_t dev_ioc_read( xptr_t    buffer_xp,
+                      uint32_t  lba,
+                      uint32_t  count );
+
+/******************************************************************************************
+ * This blocking function register an asynchronous WRITE request : move <count> contiguous
+ * blocks from a kernel buffer defined by the <buffer_xp> argument to the block device,
+ * starting from block defined by the <lba> argument.
+ * It register the request in the client thread descriptor, it register the client thread
+ * in the IOC device queue, it blocks on the THREAD_BLOCKED_IO condition, and deschedules.
+ * It will be reactivated by the DEV server thread when the transfer is completed.
+ * It can be executed by a thread running in any cluster.
+ ******************************************************************************************
+ * @ buffer_xp : extended pointer on kernel buffer in memory (must be block aligned).
+ * @ lba       : first block index on device.
+ * @ count     : number of blocks to transfer.
+ * @ returns 0 if success / returns -1 if error.
+ *****************************************************************************************/
+error_t dev_ioc_write( xptr_t    buffer_xp,
+                       uint32_t  lba,
+                       uint32_t  count );
+
+/******************************************************************************************
+ * This blocking function executes a synchronous SYNC_READ request : it moves <count>
+ * contiguous blocks of data from the block device, starting from block defined by the
+ * <lba> argument to a kernel memory buffer, defined by the <buffer_xp> argument. 
+ * The request is registered in the calling thread descriptor, but the client thread calls
+ * directly the driver cmd function, that is also a blocking function returning only
+ * when the transfer is completed.
+ * It can be executed by a thread running in any cluster.
+ ******************************************************************************************
+ * @ buffer_xp : extended pointer on kernel buffer in memory (must be block aligned).
+ * @ lba       : first block index on device.
+ * @ count     : number of blocks to transfer.
+ * @ returns 0 if success / returns -1 if error.
+ *****************************************************************************************/
+error_t dev_ioc_sync_read( xptr_t    buffer_xp,
                            uint32_t  lba,
                            uint32_t  count );
 
+/******************************************************************************************
+ * This blocking function executes a synchronous SYNC_WRITE request : it moves <count>
+ * contiguous blocks of data from a kernel memory buffer, defined by the <buffer_xp>
+ * argument to the block device, starting from block defined by the <lba> argument. 
+ * The request is registered in the calling thread descriptor, but the client thread calls
+ * directly the driver cmd() function, that is also a blocking function returning only
+ * when the transfer is completed.
+ * It can be executed by a thread running in any cluster.
+ ******************************************************************************************
+ * @ buffer_xp : extended pointer on kernel buffer in memory (must be block aligned).
+ * @ lba       : first block index on device.
+ * @ count     : number of blocks to transfer.
+ * @ returns 0 if success / returns -1 if error.
+ *****************************************************************************************/
+error_t dev_ioc_sync_write( xptr_t    buffer_xp,
+                            uint32_t  lba,
+                            uint32_t  count );
+
 #endif	/* _DEV_IOC_H */
Index: /trunk/kernel/devices/dev_mmc.c
===================================================================
--- /trunk/kernel/devices/dev_mmc.c	(revision 656)
+++ /trunk/kernel/devices/dev_mmc.c	(revision 657)
@@ -2,5 +2,5 @@
  * dev_mmc.c - MMC (Memory Cache Controler) generic device API implementation.
  * 
- * Author  Alain Greiner    (2016,2017,2018)
+ * Author  Alain Greiner    (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -176,5 +176,5 @@
 
 /////////////////////////////////////////
-error_t dev_mmc_set_error( cxy_t     cxy,
+error_t dev_mmc_error_set( cxy_t     cxy,
                            uint32_t  index,
                            uint32_t  wdata )
@@ -185,5 +185,5 @@
     // store command arguments in thread descriptor
     this->mmc_cmd.dev_xp    = chdev_dir.mmc[cxy];
-    this->mmc_cmd.type      = MMC_SET_ERROR;
+    this->mmc_cmd.type      = MMC_ERROR_SET;
     this->mmc_cmd.reg_index = index;
     this->mmc_cmd.reg_ptr   = &wdata;
@@ -194,5 +194,5 @@
                         
 //////////////////////////////////////////
-error_t dev_mmc_get_error( cxy_t      cxy,
+error_t dev_mmc_error_get( cxy_t      cxy,
                            uint32_t   index,
                            uint32_t * rdata )
@@ -203,5 +203,5 @@
     // store command arguments in thread descriptor
     this->mmc_cmd.dev_xp    = chdev_dir.mmc[cxy];
-    this->mmc_cmd.type      = MMC_GET_ERROR;
+    this->mmc_cmd.type      = MMC_ERROR_GET;
     this->mmc_cmd.reg_index = index;
     this->mmc_cmd.reg_ptr   = rdata;
@@ -210,9 +210,9 @@
     return dev_mmc_access( this ); 
 }
-                        
-////////////////////////////////////////////////////
-error_t dev_mmc_get_instrumentation( cxy_t      cxy,
-                                     uint32_t   index,
-                                     uint32_t * rdata )
+
+//////////////////////////////////////////
+error_t dev_mmc_instr_get( cxy_t      cxy,
+                           uint32_t   index,
+                           uint32_t * rdata )
 {
     // get calling thread local pointer
@@ -221,5 +221,5 @@
     // store command arguments in thread descriptor
     this->mmc_cmd.dev_xp    = chdev_dir.mmc[cxy];
-    this->mmc_cmd.type      = MMC_GET_INSTRU;
+    this->mmc_cmd.type      = MMC_INSTR_GET;
     this->mmc_cmd.reg_index = index;
     this->mmc_cmd.reg_ptr   = rdata;
@@ -228,3 +228,2 @@
     return dev_mmc_access( this ); 
 }
-
Index: /trunk/kernel/devices/dev_mmc.h
===================================================================
--- /trunk/kernel/devices/dev_mmc.h	(revision 656)
+++ /trunk/kernel/devices/dev_mmc.h	(revision 657)
@@ -2,5 +2,5 @@
  * dev_mmc.h - MMC (Generic L2 cache controller) device API definition.
  *
- * Authors   Alain Greiner  (2016,2017,2018)
+ * Authors   Alain Greiner  (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -34,10 +34,15 @@
  * acting in all clusters containing a level 2 cache controller.
  *
- * It supports  five command types: 
+ * It supports three different services:
+ * 1) L2/L3 software cache-coherence operations.
+ * 2) error reporting for architecture specific addressing error.
+ * 3) architecture specific intrumentation registers access. 
+ *
+ * It supports therefore five command types: 
  * - MMC_CC_INVAL   : invalidate all cache lines covering a given buffer in L2 cache.
  * - MMC_CC_SYNC    : synchronize all cache lines covering a given buffer to L3 cache.
- * - MMC_GET_ERROR  : return content of a given error signaling register.
- * - MMC_SET_ERROR  : set a given error signaling register.
- * - MMC_GET_INSTRU : return content of a given instrumentation register.
+ * - MMC_ERROR_GET  : return content of a given error signaling register.
+ * - MMC_ERROR_SET  : set a given error signaling register.
+ * - MMC_INSTR_GET  : return content of a given instrumentation register.
  *
  * As all L2 caches can be accessed by any thread running in any cluster, a calling
@@ -73,7 +78,7 @@
     MMC_CC_INVAL   = 0,
     MMC_CC_SYNC    = 1,
-    MMC_GET_ERROR  = 2,
-    MMC_SET_ERROR  = 3,
-    MMC_GET_INSTRU = 4,
+    MMC_ERROR_GET  = 2,
+    MMC_ERROR_SET  = 3,
+    MMC_INSTR_GET  = 4,
 };
 
@@ -126,5 +131,5 @@
                         
 /*****************************************************************************************
- * This function set a value in one error signaling MMC register.
+ * This function set a value in one (architecture specific) MMC_ERROR register.
  * It can be executed by any thread in any cluster, because it uses remote accesses
  * to access the L2 cache instrumentation interface in any cluster.
@@ -135,12 +140,12 @@
  * @ return 0 if success / return EINVAL if failure
  ****************************************************************************************/
-error_t dev_mmc_set_error( cxy_t    cxy,
+error_t dev_mmc_error_set( cxy_t    cxy,
                            uint32_t index,
                            uint32_t wdata );
               
 /*****************************************************************************************
- * This function returns the value contained in one error signaling MMC register.
- * It can be executed by any thread in any cluster, because it uses remote accesses
- * to access the L2 cache instrumentation interface in any cluster.
+ * This function returns the value contained in one (architecture specific) MMC_ERROR
+ * register. It can be executed by any thread in any cluster, because it uses remote
+ * accesses to access the L2 cache instrumentation interface in any cluster.
  *****************************************************************************************
  * @ cxy     : MMC cluster identifier.
@@ -149,22 +154,21 @@
  * @ return 0 if success / return EINVAL if failure
  ****************************************************************************************/
-error_t dev_mmc_get_error( cxy_t      cxy,
+error_t dev_mmc_error_get( cxy_t      cxy,
                            uint32_t   index, 
                            uint32_t * rdata );
               
-
 /*****************************************************************************************
- * This function returns the value contained in one instrumentation MMC register.
- * It can be executed by any thread in any cluster, because it uses remote accesses
- * to access the L2 cache configuration interface in any cluster.
+ * This function returns the value contained in one (architecture specific) MMC_INSTR
+ * register. It can be executed by any thread in any cluster, because it uses remote
+ * accesses to access the L2 cache instrumentation interface in any cluster.
  *****************************************************************************************
  * @ cxy     : MMC cluster identifier.
- * @ index   : instrumentation register index in MMC peripheral.
+ * @ index   : error register index in MMC peripheral.
  * @ rdata   : local pointer on buffer for returned value.
  * @ return 0 if success / return EINVAL if failure
  ****************************************************************************************/
-error_t dev_mmc_get_instrumentation( cxy_t      cxy,
-                                     uint32_t   index,
-                                     uint32_t * rdata );
-
+error_t dev_mmc_instr_get( cxy_t      cxy,
+                           uint32_t   index, 
+                           uint32_t * rdata );
+              
 #endif  /* _DEV_MMC_H_ */
Index: /trunk/kernel/devices/dev_nic.c
===================================================================
--- /trunk/kernel/devices/dev_nic.c	(revision 656)
+++ /trunk/kernel/devices/dev_nic.c	(revision 657)
@@ -2,5 +2,5 @@
  * dev_nic.c - NIC (Network Controler) generic device API implementation.
  * 
- * Author  Alain Greiner    (2016,2017,2018,2019)
+ * Author  Alain Greiner    (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -24,15 +24,192 @@
 #include <hal_kernel_types.h>
 #include <hal_special.h>
+#include <hal_uspace.h>
+#include <remote_buf.h>
 #include <printk.h>
 #include <chdev.h>
 #include <thread.h>
+#include <socket.h>
 #include <hal_drivers.h>
 #include <dev_nic.h>
+#include <vfs.h>
+#include <shared_socket.h>
 
 /////////////////////////////////////////////////////////////////////////////////////////
-// Extern global variables
+//                 Extern global variables
 /////////////////////////////////////////////////////////////////////////////////////////
 
 extern chdev_directory_t  chdev_dir;         // allocated in kernel_init.c
+
+////////////////////////////////////////////////////////////////////////////////////////////
+// This static function is used by the dev_nic_rx_handle_tcp() & dev_nic_tx_handle_tcp()
+// functions to check acceptability of a given sequence number. It returns true when 
+// the <seq> argument is contained in a wrap-around window defined by the <min> and <max>
+// arguments. The window wrap-around when (min > max).
+////////////////////////////////////////////////////////////////////////////////////////////
+// @ seq   : [in] value to be checked.
+// @ min   : [in] first  base.
+// @ max   : [in] window size.
+////////////////////////////////////////////////////////////////////////////////////////////
+static inline bool_t is_in_window( uint32_t seq,
+                                   uint32_t min,
+                                   uint32_t max )
+{
+    if( max >= min )    // no wrap_around => only one window [min,max]
+    {
+        return( (seq >= min) && (seq <= max) ); 
+    }
+    else                // window wrap-around => two windows [min,0xFFFFFFFF] and [0,max]
+    {
+        return( (seq <= max) || (seq >= min) );
+    }
+}  
+
+////////////////////////////////////////////////////////////////////////////////////////////
+// this static function compute a channel index in range [0,nic_channelx[ from
+// a remote IP address and remote port.
+// TODO this function should be provided by the NIC driver.
+////////////////////////////////////////////////////////////////////////////////////////////
+// @ addr     : [in] IP address.
+// @ port     : [in] TCP/UDP port.
+////////////////////////////////////////////////////////////////////////////////////////////
+static inline uint32_t dev_nic_channel_index( uint32_t addr,
+                                              uint16_t port )
+{
+    // get number of NIC channels
+    uint32_t nic_channels = LOCAL_CLUSTER->nb_nic_channels;
+ 
+    // compute NIC channel index
+    return ( ((addr     ) & 0xFF) ^
+             ((addr > 8 ) & 0xFF) ^
+             ((addr > 16) & 0xFF) ^
+             ((addr > 24) & 0xFF) ^
+             ((port     ) & 0xFF) ^
+             ((port > 8 ) & 0xFF) ) % nic_channels;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// This static function computes the checksum for an IP packet header.
+// The "checksum" field itself is not taken into account for this computation.
+////////////////////////////////////////////////////////////////////////////////////////
+// @ buffer      : [in] pointer on IP packet header (20 bytes)
+// @ return the checksum value on 16 bits
+////////////////////////////////////////////////////////////////////////////////////////
+uint16_t dev_nic_ip_checksum( uint8_t  * buffer )
+{
+    uint32_t   i;           
+    uint32_t   cs;      // 32 bits accumulator
+    uint16_t * buf;     
+    
+    buf = (uint16_t *)buffer;
+
+    // compute checksum 
+    for( i = 0 , cs = 0 ; i < 10 ; i++ )
+    {
+        if( i != 5 )  cs += buf[i];
+    }
+
+    // one's complement
+    return ~cs;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// This static function computes the checksum for an UDP packet defined by 
+// the <buffer> and <size> arguments.
+////////////////////////////////////////////////////////////////////////////////////////
+// @ buffer      : [in] pointer on UDP packet base.
+// @ size        : [in] number of bytes in this packet (including header).
+// @ return the checksum value on 16 bits
+////////////////////////////////////////////////////////////////////////////////////////
+uint16_t dev_nic_udp_checksum( uint8_t  * buffer,
+                               uint32_t   size )
+{
+    uint32_t   i;           
+    uint32_t   carry;
+    uint32_t   cs;      // 32 bits accumulator
+    uint16_t * buf;     
+    uint32_t   max;     // number of uint16_t in packet
+    
+    // compute max & buf
+    buf = (uint16_t *)buffer;
+    max = size >> 1;
+
+    // extend buffer[] if required
+    if( size & 1 )
+    {
+        max++;
+        buffer[size] = 0;
+    }
+
+    // compute checksum for UDP packet
+    for( i = 0 , cs = 0 ; i < size ; i++ )  cs += buf[i];
+
+    // handle carry
+    carry = (cs >> 16);
+    if( carry ) 
+    {
+        cs += carry; 
+        carry = (cs >> 16);
+        if( carry ) cs += carry;
+    } 
+
+    // one's complement
+    return ~cs;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// This static function computes the checksum for a TCP segment defined by
+// the <buffer> and <size> arguments.
+// It includes the pseudo header defined by the <src_ip_addr>, <dst_ip_addr>,
+// <size> arguments, and by the TCP_PROTOCOL code.
+////////////////////////////////////////////////////////////////////////////////////////
+// @ buffer      : [in] pointer on TCP segment base.
+// @ size        : [in] number of bytes in this segment (including header).
+// @ src_ip_addr : [in] source IP address (pseudo header)
+// @ dst_ip_addr : [in] destination IP address (pseudo header)
+// @ return the checksum value on 16 bits
+////////////////////////////////////////////////////////////////////////////////////////
+uint16_t dev_nic_tcp_checksum( uint8_t  * buffer,
+                               uint32_t   size,
+                               uint32_t   src_ip_addr,
+                               uint32_t   dst_ip_addr )
+{
+    uint32_t   i;           
+    uint32_t   carry;
+    uint32_t   cs;      // 32 bits accumulator
+    uint16_t * buf;
+    uint32_t   max;     // number of uint16_t in segment
+
+    // compute max & buf
+    buf = (uint16_t *)buffer;
+    max = size >> 1;
+
+    // extend buffer[] if required
+    if( size & 1 )
+    {
+        max++;
+        buffer[size] = 0;
+    }
+
+    // compute checksum for TCP segment
+    for( i = 0 , cs = 0 ; i < size ; i++ )  cs += buf[i];
+
+    // complete checksum for pseudo-header
+    cs += src_ip_addr;
+    cs += dst_ip_addr;
+    cs += PROTOCOL_TCP;
+    cs += size;
+
+    // handle carry
+    carry = (cs >> 16);
+    if( carry ) 
+    {
+        cs += carry; 
+        carry = (cs >> 16);
+        if( carry ) cs += carry;
+    } 
+
+    // one's complement
+    return ~cs;
+}
 
 //////////////////////////////////
@@ -80,168 +257,2761 @@
 }  // end dev_nic_init()
 
+
+/////////////////////////////////////////////////////////////////////////////////////////
+//                 Functions implementing the SOCKET related syscalls
+/////////////////////////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////
+int dev_nic_socket( uint32_t   domain,
+                    uint32_t   type )
+{
+    uint32_t    fdid;
+    socket_t  * socket;
+    error_t     error;
+
+    // allocate memory for the file descriptor and for the socket
+    error = socket_create( local_cxy,
+                           domain,
+                           type,
+                           &socket,    // unused here
+                           &fdid );
+
+    if( error ) return -1;
+    return fdid;
+}
+
+////////////////////////////////
+int dev_nic_bind( uint32_t fdid,
+                  uint32_t addr,
+                  uint16_t port )
+{
+    vfs_inode_type_t    type;
+    socket_t          * socket;
+    uint32_t            state;
+
+    thread_t  * this    = CURRENT_THREAD;
+    process_t * process = this->process;
+
+    // get pointers on file descriptor
+    xptr_t       file_xp  = process_fd_get_xptr( process , fdid );
+    vfs_file_t * file_ptr = GET_PTR( file_xp );
+    cxy_t        file_cxy = GET_CXY( file_xp );
+
+    // check file_xp
+    if( file_xp == XPTR_NULL )
+    {
+        printk("\n[ERROR] in %s : undefined fdid %d",
+        __FUNCTION__, fdid );
+        return -1;
+    }
+
+    type   = hal_remote_l32( XPTR( file_cxy , &file_ptr->type ) );
+    socket = hal_remote_lpt( XPTR( file_cxy , &file_ptr->socket ) );
+
+    // check file descriptor type
+    if( type != INODE_TYPE_SOCK )
+    {
+        printk("\n[ERROR] in %s : illegal file type %s",
+        __FUNCTION__, vfs_inode_type_str( type ) );
+        return -1;
+    }
+
+    state = (type == SOCK_STREAM) ? TCP_STATE_BOUND : UDP_STATE_BOUND;
+
+    // update the socket descriptor
+    hal_remote_s32( XPTR( file_cxy , &socket->local_addr ) , addr );
+    hal_remote_s32( XPTR( file_cxy , &socket->local_port ) , port );
+    hal_remote_s32( XPTR( file_cxy , &socket->state      ) , state );
+
+    return 0;
+
+}  // end dev_nic_bind()
+
+//////////////////////////////////
+int dev_nic_listen( uint32_t fdid,
+                    uint32_t max_pending )
+{
+    xptr_t              file_xp;
+    vfs_file_t        * file_ptr;
+    cxy_t               file_cxy;
+    vfs_inode_type_t    file_type;
+    socket_t          * socket_ptr;
+    uint32_t            socket_type;
+    uint32_t            socket_state;
+
+    thread_t  * this    = CURRENT_THREAD;
+    process_t * process = this->process;
+
+    if( max_pending != 0 )
+    {
+        printk("\n[WARNING] in %s : max_pending argument non supported\n",
+        __FUNCTION__ );
+    }
+
+    // get pointers on file descriptor
+    file_xp  = process_fd_get_xptr( process , fdid );
+    file_ptr = GET_PTR( file_xp );
+    file_cxy = GET_CXY( file_xp );
+
+    // check file_xp
+    if( file_xp == XPTR_NULL )
+    {
+        printk("\n[ERROR] in %s : undefined fdid %d",
+        __FUNCTION__, fdid );
+        return -1;
+    }
+
+    file_type  = hal_remote_l32( XPTR( file_cxy , &file_ptr->type ) );
+    socket_ptr = hal_remote_lpt( XPTR( file_cxy , &file_ptr->socket ) );
+
+    // check file descriptor type
+    if( file_type != INODE_TYPE_SOCK )
+    {
+        printk("\n[ERROR] in %s : illegal file type %s",
+        __FUNCTION__, vfs_inode_type_str(file_type) );
+        return -1;
+    }
+
+    // get socket type and state
+    socket_type  = hal_remote_l32( XPTR( file_cxy , &socket_ptr->type )); 
+    socket_state = hal_remote_l32( XPTR( file_cxy , &socket_ptr->state )); 
+
+    // check socket type 
+    if( socket_type != SOCK_STREAM )
+    {
+        printk("\n[ERROR] in %s : illegal socket type",
+        __FUNCTION__ );
+        return -1;
+    }
+    
+    // check socket state
+    if( socket_state != TCP_STATE_BOUND )
+    {
+        printk("\n[ERROR] in %s : illegal socket state %s",
+        __FUNCTION__, socket_state_str(socket_state) );
+        return -1;
+    }
+    
+    // update socket.state
+    hal_remote_s32( XPTR( file_cxy , &socket_ptr->state ) , TCP_STATE_LISTEN );
+
+    return 0;
+
+}  // end dev_nic_listen()
+
 ///////////////////////////////////
-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;
-
-// check thread can yield
-assert( (thread_ptr->busylocks == 0),
-"cannot yield : busylocks = %d\n", thread_ptr->busylocks );
+int dev_nic_connect( uint32_t fdid,
+                     uint32_t remote_addr,
+                     uint16_t remote_port )
+{
+    vfs_inode_type_t    file_type;
+    socket_t          * socket;
+    uint32_t            socket_state;     // socket state
+    uint32_t            socket_type;      // socket type  
+    uint32_t            local_addr;       // local IP address
+    uint32_t            local_port;       // local port
+    xptr_t              tx_server_xp;     // extended pointer on TX server thread
+    thread_t          * tx_server_ptr;    // local pointer on TX server thread
+
+    thread_t  * this    = CURRENT_THREAD;
+    process_t * process = this->process;
+
+    // get pointers on file descriptor
+    xptr_t       file_xp  = process_fd_get_xptr( process , fdid );
+    vfs_file_t * file_ptr = GET_PTR( file_xp );
+    cxy_t        file_cxy = GET_CXY( file_xp );
+
+    // check file_xp
+    if( file_xp == XPTR_NULL )
+    {
+        printk("\n[ERROR] in %s : undefined fdid %d",
+        __FUNCTION__, fdid );
+        return -1;
+    }
+
+    file_type = hal_remote_l32( XPTR( file_cxy , &file_ptr->type ) );
+    socket    = hal_remote_lpt( XPTR( file_cxy , &file_ptr->socket ) );
+
+    // check file descriptor type
+    if( file_type != INODE_TYPE_SOCK )
+    {
+        printk("\n[ERROR] in %s : illegal file type %s",
+        __FUNCTION__, vfs_inode_type_str( file_type ) );
+        return -1;
+    }
+
+    // get relevant socket infos
+    socket_type   = hal_remote_l32( XPTR( file_cxy , &socket->type ) );
+    socket_state  = hal_remote_l32( XPTR( file_cxy , &socket->state ) );
+    local_addr    = hal_remote_l32( XPTR( file_cxy , &socket->local_addr ) );
+    local_port    = hal_remote_l32( XPTR( file_cxy , &socket->local_port ) );
+
+    if( socket_type == SOCK_DGRAM )       // UDP
+    {
+        if( socket_state != UDP_STATE_BOUND )
+        {
+            printk("\n[ERROR] in %s : illegal socket statea %s for CONNECT",
+            __FUNCTION__, socket_state_str(socket_state) );
+            return -1;
+        }
+    }
+    else if( socket_type == SOCK_STREAM )  // TCP
+    {
+        if( socket_state != TCP_STATE_BOUND )
+        {
+            printk("\n[ERROR] in %s : illegal socket state %s for CONNECT",
+            __FUNCTION__, socket_state_str(socket_state) );
+            return -1;
+        }
+    }
+    else
+    {
+        printk("\n[ERROR] in %s : illegal socket type %d for CONNECT",
+        __FUNCTION__, socket_type );
+        return -1;
+    }
+
+    // compute nic_channel index from remote_addr and remote_port
+    uint32_t nic_channel = dev_nic_channel_index( remote_addr , remote_port );
+
+    // link new socket to chdev servers
+    socket_link_to_servers( XPTR( file_cxy , socket ),
+                            nic_channel );
+
+    // update the socket descriptor
+    hal_remote_s32( XPTR( file_cxy , &socket->remote_addr ) , remote_addr  );
+    hal_remote_s32( XPTR( file_cxy , &socket->remote_port ) , remote_port  );
+    hal_remote_s32( XPTR( file_cxy , &socket->nic_channel ) , nic_channel  );
+
+    // the actual connection mechanism depends on socket type
+    // UDP : client thread directly updates the local socket state
+    // TCP : client thread request TX server thread to start the 3 steps handshake
+
+    if( socket_type == SOCK_DGRAM )  // UDP
+    {
+        // directly update the local socket state
+        hal_remote_s32( XPTR( file_cxy , &socket->state ) , UDP_STATE_CONNECT );
+    }
+    else                             // TCP
+    {
+        // get pointers on NIC_TX[index] chdev
+        xptr_t    tx_chdev_xp  = chdev_dir.nic_tx[nic_channel];
+        chdev_t * tx_chdev_ptr = GET_PTR( tx_chdev_xp );
+        cxy_t     tx_chdev_cxy = GET_CXY( tx_chdev_xp );
+
+        // get pointers on NIC_TX[channel] server thread
+        tx_server_ptr = hal_remote_lpt( XPTR( tx_chdev_cxy , &tx_chdev_ptr->server ));
+        tx_server_xp  = XPTR( tx_chdev_cxy , tx_server_ptr );
+
+        // register command arguments in socket descriptor
+        hal_remote_s64( XPTR( file_cxy , &socket->tx_cmd ),
+                        SOCKET_TX_CONNECT );
+
+        // update the "tx_client" field in socket descriptor
+        hal_remote_s64( XPTR( file_cxy , &socket->tx_client ),
+                        XPTR( local_cxy , this ) );
+
+        // unblock NIC_TX server thread
+        thread_unblock( tx_server_xp , THREAD_BLOCKED_CLIENT );
+ 
+        // block on THREAD_BLOCKED_IO condition and deschedules
+        thread_block( XPTR( local_cxy , this ) , THREAD_BLOCKED_IO );
+        sched_yield( "blocked in connect" );
+
+        // reset the "tx_client" field in socket descriptor
+        hal_remote_s64( XPTR( file_cxy , &socket->tx_client ),
+                        XPTR_NULL );
+    }
+
+    return 0;
+
+}  // end dev_nic_connect()
+
+////////////////////////////////////
+int dev_nic_accept( uint32_t   fdid,
+                    uint32_t * remote_addr,
+                    uint16_t * remote_port )
+{
+    xptr_t              file_xp;             // extended pointer on remote file
+    vfs_file_t        * file_ptr;
+    cxy_t               file_cxy;
+    vfs_inode_type_t    file_type;           // file descriptor type
+    socket_t          * socket;              // local pointer on remote waiting socket
+    uint32_t            socket_type;         // waiting socket type    
+    uint32_t            socket_state;        // waiting socket state
+    uint32_t            socket_domain;       // waiting socket domain
+    uint32_t            socket_local_addr;   // waiting socket local IP address
+    uint32_t            socket_local_port;   // waiting socket local port
+    xptr_t              crqq_xp;             // extended pointer on socket.crqq queue
+    socket_t          * new_socket;          // local pointer on new socket
+    uint32_t            new_fdid;            // new socket file descriptor index
+    sockaddr_t          new_sockaddr;        // one request in crqq queue
+    uint32_t            new_remote_addr;     // new socket remote IP address
+    uint32_t            new_remote_port;     // new socket remote port
+    error_t             error;
+
+    thread_t  * this    = CURRENT_THREAD;
+    process_t * process = this->process;
+
+    // get pointers on file descriptor
+    file_xp  = process_fd_get_xptr( process , fdid );
+    file_ptr = GET_PTR( file_xp );
+    file_cxy = GET_CXY( file_xp );
+
+    // check file_xp
+    if( file_xp == XPTR_NULL )
+    {
+        printk("\n[ERROR] in %s : undefined fdid %d",
+        __FUNCTION__, fdid );
+        return -1;
+    }
+
+    file_type = hal_remote_l32( XPTR( file_cxy , &file_ptr->type ) );
+    socket    = hal_remote_lpt( XPTR( file_cxy , &file_ptr->socket ) );
+
+    // check file descriptor type
+    if( file_type != INODE_TYPE_SOCK )
+    {
+        printk("\n[ERROR] in %s : illegal file type %s / thread[%x,%x]\n",
+        __FUNCTION__, vfs_inode_type_str(file_type), process->pid, this->trdid );
+        return -1;
+    }
+
+    // get socket type, domain, state, local_addr and local_port
+    socket_type       = hal_remote_l32( XPTR( file_cxy , &socket->type )); 
+    socket_state      = hal_remote_l32( XPTR( file_cxy , &socket->state )); 
+    socket_domain     = hal_remote_l32( XPTR( file_cxy , &socket->domain )); 
+    socket_local_addr = hal_remote_l32( XPTR( file_cxy , &socket->local_addr )); 
+    socket_local_port = hal_remote_l32( XPTR( file_cxy , &socket->local_port )); 
+
+    // check socket type 
+    if( socket_type != SOCK_STREAM )
+    {
+        printk("\n[ERROR] in %s : illegal socket type / thread[%x,%x]\n",
+        __FUNCTION__, process->pid , this->trdid );
+        return -1;
+    }
+    
+    // check socket state
+    if( socket_state != TCP_STATE_LISTEN ) 
+    {
+        printk("\n[ERROR] in %s : illegal socket state %s / thread[%x,%x]\n",
+        __FUNCTION__, socket_state_str(socket_state), process->pid, this->trdid );
+        return -1;
+    }
+    
+    // select a cluster for the new socket
+    cxy_t new_cxy = cluster_random_select();
+
+    // allocate memory for the new socket descriptor 
+    error = socket_create( new_cxy,
+                           socket_domain,
+                           socket_type,
+                           &new_socket,
+                           &new_fdid );
+    if( error )
+    {
+        printk("\n[ERROR] in %s : cannot allocate new socket / thread[%x,%x]\n",
+        __FUNCTION__, process->pid, this->trdid );
+        return -1;
+    }
+   
+    // build extended pointer on socket.crqq
+    crqq_xp  = XPTR( file_cxy , &socket->crqq );
+
+    // blocks and deschedules if requests queue empty
+    if( remote_buf_status( crqq_xp ) == 0 )
+    {
+        thread_block( XPTR( local_cxy , this ) , THREAD_BLOCKED_IO );
+        sched_yield( "socket.crqq queue empty");
+    }
+        
+    // extract first request from the socket.crqq queue
+    remote_buf_get_to_kernel( crqq_xp,
+                              (uint8_t *)(&new_sockaddr),
+                              sizeof(sockaddr_t) );
+
+    new_remote_addr = new_sockaddr.s_addr;
+    new_remote_port = new_sockaddr.s_port;
+
+    // compute NIC channel index from remote_addr and remote_port
+    uint32_t nic_channel = dev_nic_channel_index( new_remote_addr , new_remote_port );
+
+    // update new socket descriptor 
+    new_socket->local_addr  = hal_remote_l32(XPTR( file_cxy , &socket->local_addr ));
+    new_socket->local_port  = hal_remote_l32(XPTR( file_cxy , &socket->local_port ));
+    new_socket->remote_addr = new_remote_addr;
+    new_socket->remote_port = new_remote_port;
+    new_socket->nic_channel = nic_channel;
+
+    // link new socket to chdev servers
+    socket_link_to_servers( XPTR( new_cxy , new_socket ),
+                            nic_channel );
+    // return success
+    *remote_addr = new_remote_addr;
+    *remote_port = new_remote_port;
+
+    return new_fdid;
+
+}  // end dev_nic_accept()
+
+////////////////////////////////////////////////////////////////////////////////////////
+// This static and blocking function is called by the four functions :
+// dev_nic_send() / dev_nic_recv() / dev_nic_sendto() / dev_nic_recvfrom().
+////////////////////////////////////////////////////////////////////////////////////////
+// Implementation note
+// The behavior is very different for SEND & RECV :
+// - For a SEND, the client thread checks that there is no TX command registered
+//   in the socket. It registers the command arguments in the socket descriptor
+//   (tx_client, tx_cmd, tx_buf, tx_len). Then the client thread unblocks the 
+//   TX server thread from the BLOCKED_CLIENT condition, blocks itself on the
+//   BLOCKED_IO condition, and deschedules. It is unblocked by the TX server thread
+//   when the last byte has been sent (for UDP) or acknowledged (for TCP).
+//   When the client thread resumes, it reset the command in socket, and returns.
+// - For a RECV, the client thread checks that there is no RX command registered
+//   in the socket. It registers itself in socket (rx_client). It checks the status
+//   of the receive buffer. It the rx_buf is empty, it blocks on the BLOCKED_IO
+//   condition, and deschedules. It is unblocked by the RX server thread when an UDP
+//   packet or TCP segment has been writen in the rx_buf. When it resumes, it moves
+//   the available data from the rx_buf to the user buffer, reset its registration
+//   in socket (reset the rx_buf for an UDP socket), and returns.
+////////////////////////////////////////////////////////////////////////////////////////
+int dev_nic_register_cmd( bool_t     is_send,
+                          uint32_t   fdid,
+                          uint8_t  * u_buf,
+                          uint32_t   length,
+                          bool_t     explicit,
+                          uint32_t   explicit_addr,
+                          uint32_t   explicit_port )
+{
+    vfs_inode_type_t    file_type;       // file descriptor type
+    socket_t          * socket_ptr;      // local pointer on socket descriptor
+    uint32_t            socket_state;    // current socket state
+    uint32_t            socket_type;     // socket type (UDP/TCP)
+    uint32_t            nic_channel;     // NIC channel for this socket
+    xptr_t              socket_lock_xp;  // extended pointer on socket lock
+    xptr_t              file_xp;         // extended pointer on file descriptor
+    vfs_file_t        * file_ptr;
+    cxy_t               file_cxy;
+    xptr_t              chdev_xp;        // extended pointer on NIC_TX[channel] chdev
+    chdev_t           * chdev_ptr;
+    cxy_t               chdev_cxy;
+    uint32_t            remote_addr;
+    uint32_t            remote_port;
+    uint32_t            status;          // number of bytes in rx_buf
+    int32_t             moved_bytes;     // total number of moved bytes (fot return)
+    xptr_t              server_xp;       // extended pointer on NIC_TX / NIC_RX server thread
+    thread_t          * server_ptr;      // local pointer on NIC_TX / NIC_RX server thread
+
+    thread_t  * this    = CURRENT_THREAD;
+    process_t * process = this->process;
+
+    // get pointers on file descriptor identifying the socket
+    file_xp  = process_fd_get_xptr( process , fdid );
+    file_ptr = GET_PTR( file_xp );
+    file_cxy = GET_CXY( file_xp );
+
+    if( file_xp == XPTR_NULL )
+    {
+        printk("\n[ERROR] in %s : undefined fdid %d / thread%x,%x]\n",
+        __FUNCTION__, fdid , process->pid, this->trdid );
+        return -1;
+    }
+ 
+    // get file type and socket pointer
+    file_type  = hal_remote_l32( XPTR( file_cxy , &file_ptr->type ) );
+
+    // get local pointer on socket
+    socket_ptr = hal_remote_lpt( XPTR( file_cxy , &file_ptr->socket ) );
+
+    // check file descriptor type
+    if( file_type != INODE_TYPE_SOCK )
+    {
+        printk("\n[ERROR] in %s : illegal file type %s / fdid %d / thread%x,%x]\n",
+        __FUNCTION__, vfs_inode_type_str(file_type), fdid, process->pid, this->trdid );
+        return -1;
+    }
+
+    // build extended pointer on file lock protecting socket 
+    socket_lock_xp = XPTR( file_cxy , &file_ptr->lock );
+
+    // take the socket lock
+    remote_rwlock_wr_acquire( socket_lock_xp );
+
+    // get socket type, state, and channel
+    socket_type  = hal_remote_l32( XPTR( file_cxy , &socket_ptr->type ));
+    socket_state = hal_remote_l32( XPTR( file_cxy , &socket_ptr->state ));
+    nic_channel  = hal_remote_l32( XPTR( file_cxy , &socket_ptr->nic_channel ));
+
+    // check socket state / type
+    if( socket_type == SOCK_STREAM )     // TCP socket
+    {
+        if( socket_state != TCP_STATE_ESTAB )
+        {
+            printk("\n[ERROR] in %s : illegal SEND/RECV for state %s / thread%x,%x]\n",
+            __FUNCTION__, socket_state_str(socket_state), process->pid, this->trdid );
+            return -1;
+        }
+
+        if( explicit )
+        {
+            // get remote IP address and type from socket descriptor
+            remote_addr = hal_remote_l32( XPTR( file_cxy , &socket_ptr->remote_addr ));
+            remote_port = hal_remote_l32( XPTR( file_cxy , &socket_ptr->remote_port ));
+
+            if( (remote_addr != explicit_addr) || (remote_port != explicit_port) )
+            {
+                printk("\n[ERROR] in %s : wrong expliciy access / thread%x,%x]\n",
+                __FUNCTION__, process->pid, this->trdid );
+                return -1;
+            }
+        }
+    }
+    else                              // UDP socket
+    {
+        if( explicit )
+        {
+            if( socket_state == UDP_STATE_UNBOUND )
+            {
+                printk("\n[ERROR] in %s : illegal SEND/RECV for state %s / thread%x,%x]\n",
+                __FUNCTION__, socket_state_str(socket_state), process->pid, this->trdid );
+                return -1;
+            }
+
+            // update remote IP address and port into socket descriptor
+            hal_remote_s32( XPTR( file_cxy , &socket_ptr->remote_addr ), explicit_addr );
+            hal_remote_s32( XPTR( file_cxy , &socket_ptr->remote_port ), explicit_port );
+        }
+        else
+        {
+            if( socket_state != UDP_STATE_CONNECT )
+            {
+                printk("\n[ERROR] in %s : illegal SEND/RECV for state %s / thread%x,%x]\n",
+                __FUNCTION__, socket_state_str(socket_state), process->pid, this->trdid );
+                return -1;
+            }
+        }
+    }
+
+    ///////////////////////////////////////////////////////
+    if( is_send )                       // SEND command
+    {
+        // build extended pointer on socket "tx_client"
+        xptr_t client_xp = XPTR( file_cxy , &socket_ptr->tx_client );
+
+        // check no previous SEND command
+        xptr_t client = hal_remote_l64( client_xp );
+
+        if( client != XPTR_NULL )  // release socket lock and return error
+        {
+            // release socket lock
+            remote_rwlock_wr_release( socket_lock_xp );
+                    
+            // get previous thread cluster & local pointer
+            cxy_t      prev_cxy = GET_CXY( client );
+            thread_t * prev_ptr = GET_PTR( client );
+
+            // get previous command type and trdid 
+            uint32_t prev_cmd = hal_remote_l32( XPTR( prev_cxy , &prev_ptr->nic_cmd.type )); 
+            uint32_t prev_tid = hal_remote_l32( XPTR( prev_cxy , &prev_ptr->trdid ));
+
+            printk("\n[ERROR] in %s : previous command %s for thread %x / thread%x,%x]\n",
+            __FUNCTION__, socket_cmd_str(prev_cmd), prev_tid,
+            process->pid, this->trdid );
+
+            return -1;
+        }
+
+        // client thread registers in socket descriptor
+        hal_remote_s64( client_xp , XPTR( local_cxy , this ) );
+        hal_remote_s32( XPTR( file_cxy , &socket_ptr->tx_cmd  ) , SOCKET_TX_SEND );
+        hal_remote_spt( XPTR( file_cxy , &socket_ptr->tx_buf  ) , u_buf );
+        hal_remote_s32( XPTR( file_cxy , &socket_ptr->tx_len  ) , length );
+        hal_remote_s32( XPTR( file_cxy , &socket_ptr->tx_todo ) , length );
+
+        // release socket lock
+        remote_rwlock_wr_release( socket_lock_xp );
+                    
+        // get pointers on relevant chdev
+        chdev_xp  = chdev_dir.nic_tx[nic_channel];
+        chdev_ptr = GET_PTR( chdev_xp );
+        chdev_cxy = GET_CXY( chdev_xp );
+
+        // get pointers on NIC_TX[channel] server thread
+        server_ptr = hal_remote_lpt( XPTR( chdev_cxy , &chdev_ptr->server ));
+        server_xp  = XPTR( chdev_cxy , server_ptr );
+
+        // unblocks the NIC_TX server thread
+        thread_unblock( server_xp , THREAD_BLOCKED_CLIENT );
+
+        // client thread blocks itself and deschedules
+        thread_block( XPTR( local_cxy , this ) , THREAD_BLOCKED_IO );
+        sched_yield( "blocked in nic_io" );
+
+        // take the socket lock when unblocked
+        remote_rwlock_wr_acquire( socket_lock_xp );
+
+        // unlink client thread from socket 
+        hal_remote_s64( client_xp , XPTR_NULL );
+                    
+        // release socket lock
+        remote_rwlock_wr_release( socket_lock_xp );
+                    
+        // exit waiting loop and return
+        return length;
+
+    }  // end SEND
+
+    ////////////////////////////////////////////////////////
+    else                                 // RECV command
+    {
+        // build extended pointers on socket "rx_client"
+        xptr_t client_xp = XPTR( file_cxy , &socket_ptr->rx_client );
+
+        // check no previous RECV command
+        xptr_t client = hal_remote_l64( client_xp );
+
+        if( client != XPTR_NULL )  // release socket lock and return error
+        {
+            // release socket lock
+            remote_rwlock_wr_release( socket_lock_xp );
+                    
+            // get previous thread cluster & local pointer
+            cxy_t      prev_cxy = GET_CXY( client );
+            thread_t * prev_ptr = GET_PTR( client );
+
+            // get previous command type and trdid 
+            uint32_t prev_cmd = hal_remote_l32( XPTR( prev_cxy , &prev_ptr->nic_cmd.type )); 
+            uint32_t prev_tid = hal_remote_l32( XPTR( prev_cxy , &prev_ptr->trdid ));
+
+            printk("\n[ERROR] in %s : previous command %s for thread %x / thread%x,%x]\n",
+            __FUNCTION__, socket_cmd_str(prev_cmd), prev_tid, process->pid, this->trdid );
+            return -1;
+        }
+
+        // build extended pointer on "rx_buf"
+        xptr_t rx_buf_xp = XPTR( file_cxy , &socket_ptr->rx_buf );
+
+        // get rx_buf status from socket
+        status = remote_buf_status( rx_buf_xp );
+
+        if( status == 0 )    // rx_buf empty => blocks and deschedules
+        { 
+            // release socket lock
+            remote_rwlock_wr_release( socket_lock_xp );
+                 
+            // client thread blocks itself and deschedules
+            thread_block( XPTR( local_cxy , this ) , THREAD_BLOCKED_IO );
+            sched_yield( "blocked in nic_io" );
+
+            // take socket lock
+            remote_rwlock_wr_release( socket_lock_xp );
+        }
+
+        // number of moved bytes cannot be larger than u_buf size
+        moved_bytes = ( length < status ) ? length : status;
+
+        // move data from kernel rx_buf to user u_buf
+        remote_buf_get_to_user( rx_buf_xp,
+                                 u_buf,
+                                 moved_bytes );
+
+        // reset rx_buf for an UDP socket
+        if( socket_type == SOCK_DGRAM ) remote_buf_reset( rx_buf_xp );
+
+        // unlink client thread from socket 
+        hal_remote_s64( client_xp , XPTR_NULL );
+                    
+        // release socket lock
+        remote_rwlock_wr_release( socket_lock_xp );
+                    
+        // exit waiting loop and return
+        return moved_bytes;
+
+    }  // end SEND  
+
+} // end dev_nic_register_cmd()
+
+
+///////////////////////////////////
+int dev_nic_send( uint32_t    fdid,
+                  uint8_t   * u_buf,
+                  uint32_t    length )
+{
+#if DEBUG_DEV_NIC_TX
+thread_t  * this    = CURRENT_THREAD;
+process_t * process = this->process;
+trdid_t     trdid   = this->trdid;
+pid_t       pid     = process->pid;
+uint32_t cycle = (uint32_t)hal_get_cycle();
+if (DEBUG_DEV_NIC_TX < cycle )
+printk("[%s] thread[%x,%x] enters : fdid %d / buf %x / length %d / cycle %d\n",
+__FUNCTION__, pid, trdid, fdid, u_buf, length, cycle );
+#endif
+
+    error_t error = dev_nic_register_cmd( true,           // SEND
+                                          fdid,
+                                          u_buf,
+                                          length,
+                                          false, 0, 0 );  // no explicit remote socket
+#if DEBUG_DEV_NIC_TX
+cycle = (uint32_t)hal_get_cycle();
+if (DEBUG_DEV_NIC_TX < cycle )
+printk("[%s] thread[%x,%x] exit : fdid %d / cycle %d\n",
+__FUNCTION__, pid, trdid, cycle );
+#endif
+
+    return error;
+
+}  // end dev_nic_send()
+
+///////////////////////////////////
+int dev_nic_recv( uint32_t    fdid,
+                  uint8_t   * u_buf,
+                  uint32_t    length )
+{
+#if DEBUG_DEV_NIC_RX
+thread_t  * this    = CURRENT_THREAD;
+process_t * process = this->process;
+trdid_t     trdid   = this->trdid;
+pid_t       pid     = process->pid;
+uint32_t    cycle   = (uint32_t)hal_get_cycle();
+if (DEBUG_DEV_NIC_RX < cycle )
+printk("[%s] thread[%x,%x] enters : fdid %d / buf %x / length %d / cycle %d\n",
+__FUNCTION__, pid, trdid, fdid, u_buf, length, cycle );
+#endif
+
+    error_t error = dev_nic_register_cmd( false,          // RECV
+                                          fdid,
+                                          u_buf,
+                                          length,
+                                          false, 0, 0 );  // no explicit remote socket
+#if DEBUG_DEV_NIC_RX
+cycle = (uint32_t)hal_get_cycle();
+if (DEBUG_DEV_NIC_RX < cycle )
+printk("[%s] thread[%x,%x] exit : fdid %d / cycle %d\n",
+__FUNCTION__, pid, trdid, cycle );
+#endif
+
+    return error;
+
+} // end dev_nic_recv()
+
+/////////////////////////////////////
+int dev_nic_sendto( uint32_t    fdid,
+                    uint8_t   * u_buf,
+                    uint32_t    length,
+                    uint32_t    remote_addr,
+                    uint32_t    remote_port )
+{
+#if DEBUG_DEV_NIC_TX
+thread_t  * this    = CURRENT_THREAD;
+process_t * process = this->process;
+trdid_t     trdid   = this->trdid;
+pid_t       pid     = process->pid;
+uint32_t cycle = (uint32_t)hal_get_cycle();
+if (DEBUG_DEV_NIC_TX < cycle )
+printk("[%s] thread[%x,%x] enters : fdid %d / buf %x / length %d / cycle %d\n",
+__FUNCTION__, pid, trdid, fdid, u_buf, length, cycle );
+#endif
+
+    error_t error = dev_nic_register_cmd( true,          // SEND
+                                          fdid,
+                                          u_buf,
+                                          length,
+                                          true,          // explicit remote socket
+                                          remote_addr,
+                                          remote_port );
+#if DEBUG_DEV_NIC_TX
+cycle = (uint32_t)hal_get_cycle();
+if (DEBUG_DEV_NIC_TX < cycle )
+printk("[%s] thread[%x,%x] exit : fdid %d / cycle %d\n",
+__FUNCTION__, pid, trdid, cycle );
+#endif
+
+    return error;
+
+}  // end dev_nic_sendto()
+
+///////////////////////////////////////
+int dev_nic_recvfrom( uint32_t    fdid,
+                      uint8_t   * u_buf,
+                      uint32_t    length,
+                      uint32_t    remote_addr,
+                      uint32_t    remote_port )
+{
+#if DEBUG_DEV_NIC_RX
+thread_t  * this    = CURRENT_THREAD;
+process_t * process = this->process;
+trdid_t     trdid   = this->trdid;
+pid_t       pid     = process->pid;
+uint32_t    cycle   = (uint32_t)hal_get_cycle();
+if (DEBUG_DEV_NIC_RX < cycle )
+printk("[%s] thread[%x,%x] enters : fdid %d / buf %x / length %d / cycle %d\n",
+__FUNCTION__, pid, trdid, fdid, u_buf, length, cycle );
+#endif
+
+    error_t error = dev_nic_register_cmd( false,         // RECV
+                                          fdid,
+                                          u_buf,
+                                          length,
+                                          true,          // explicit remote socket
+                                          remote_addr,
+                                          remote_port );
+#if DEBUG_DEV_NIC_RX
+cycle = (uint32_t)hal_get_cycle();
+if (DEBUG_DEV_NIC_RX < cycle )
+printk("[%s] thread[%x,%x] exit : fdid %d / cycle %d\n",
+__FUNCTION__, pid, trdid, cycle );
+#endif
+
+    return error;
+
+}  // end dev_nic_recvfrom()
+
+
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+//               Functions called by the NIC_RX server thread
+///////////////////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// This static function is called by the NIC_RX[channel] server thread to register
+// a send request defined by the <flags> argument in the R2T queue  specified by 
+// the <queue_xp> argument.
+/////////////////////////////////////////////////////////////////////////////////////////
+// @ queue_xp   : [in] extended pointer on the R2T qeue descriptor.
+// @ flags      : [in] flags to be set in the TCP segment.
+/////////////////////////////////////////////////////////////////////////////////////////
+static void dev_nic_rx_put_r2t_request( xptr_t    queue_xp,
+                                        uint32_t  flags )
+{
+    while( 1 )
+    {
+        error_t error = remote_buf_put_from_kernel( queue_xp,
+                                                    (uint8_t *)(&flags),
+                                                    1 );
+
+        if( error )  sched_yield( "waiting R2T queue" );
+        else         break;
+    }
+
+}  // end dev_nic_rx_put_r2t_request()
+  
+///////////////////////////////////////////////////////////////////////////////////////////
+// This static function is called by the dev_nic_rx_server() function.
+// It calls directly the NIC driver (with the READABLE command) and returns the status
+// of the NIC_RX queue identified by the <chdev> argument.
+// in the <readable> buffer.
+///////////////////////////////////////////////////////////////////////////////////////////
+// @ chdev     : [in]  local pointer on NIC_TX chdev.
+// @ readable  : [out] zero if queue empty.  
+// @ returns 0 if success / returns -1 if failure in accessing NIC device.
+///////////////////////////////////////////////////////////////////////////////////////////
+error_t dev_nic_rx_queue_readable( chdev_t  * chdev,
+                                   uint32_t * readable )
+{
+    thread_t * this = CURRENT_THREAD;
+
+    // initialize NIC_READABLE command in thread descriptor
+    this->nic_cmd.dev_xp = XPTR( local_cxy , chdev );
+    this->nic_cmd.type   = NIC_CMD_READABLE;
+
+    // call driver to test readable
+    chdev->cmd( XPTR( local_cxy , this ) );
+
+    // return status
+    *readable = this->nic_cmd.status;
+
+    // return error
+    return this->nic_cmd.error;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// This static function is called by the dev_nic_rx_server() function.
+// It moves one Ethernet packet from the NIC_RX_QUEUE identified the <chdev> argument,
+// to the 2K bytes kernel buffer identified by the <buffer> argument. The actual
+// Ethernet packet length is returned in the <length> argument.
+// It calls directly the NIC driver with the READ command, without registering in the
+// waiting queue, because only the NIC_RX server thread can access this NIC_RX_QUEUE.
+///////////////////////////////////////////////////////////////////////////////////////////
+// @ chdev   : [in]  local pointer on NIC_TX chdev.
+// @ buffer  : [in]  local pointer on destination kernel buffer.
+// @ length  : [out] Ethernet packet size in bytes.
+// @ returns 0 if success / returns -1 if failure in accessing NIC device.
+///////////////////////////////////////////////////////////////////////////////////////////
+error_t dev_nic_rx_move_packet( chdev_t  * chdev,
+                                uint8_t  * k_buf,
+                                uint32_t * length )
+{
+    thread_t * this = CURRENT_THREAD;
 
 #if DEBUG_DEV_NIC_RX
 uint32_t cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_DEV_NIC_RX < cycle )
-printk("\n[DBG] %s : thread %x enters for packet %x in cluster %x\n", 
-__FUNCTION__ , thread_ptr , pkd , local_cxy );
+printk("\n[%s] thread[%x,%x] enters / cycle %d\n", 
+__FUNCTION__, this->process->pid, this->trdid, cycle );
 #endif
 
-    // get pointer on NIC-RX chdev descriptor
-    uint32_t   channel = thread_ptr->chdev->channel; 
-    xptr_t     dev_xp  = chdev_dir.nic_rx[channel];
-    cxy_t      dev_cxy = GET_CXY( dev_xp );
-    chdev_t  * dev_ptr = (chdev_t *)GET_PTR( dev_xp );
-
-    assert( (dev_xp != XPTR_NULL) , "undefined NIC chdev descriptor" );
-
-    assert( (dev_cxy == local_cxy) , " chdev must be local" );
-
-    // initialize command in thread descriptor
-    thread_ptr->nic_cmd.dev_xp = dev_xp;
-
-    // call driver to test readable
-    thread_ptr->nic_cmd.cmd = NIC_CMD_READABLE;
-    dev_ptr->cmd( thread_xp );
+    // initialize NIC_READ command in thread descriptor
+    this->nic_cmd.type    = NIC_CMD_READ;
+    this->nic_cmd.buffer  = k_buf;
+
+    // call NIC driver  
+    chdev->cmd( XPTR( local_cxy , this ) );
+
+    // returns packet length   
+    *length = this->nic_cmd.length;
 
     // check error
-    error = thread_ptr->nic_cmd.error;
-    if( error ) return error;
-
-    // block and deschedule if queue non readable
-    if( thread_ptr->nic_cmd.status == false )  
-    {
-        // enable NIC-RX IRQ 
-        dev_pic_enable_irq( core->lid , dev_xp );
-
-        // block client thread on THREAD_BLOCKED_IO
-        thread_block( XPTR( local_cxy , thread_ptr ) , THREAD_BLOCKED_IO );
-
-        // deschedule client thread
-        sched_yield("client blocked on I/O");
-
-        // disable NIC-RX IRQ 
-        dev_pic_disable_irq( core->lid , dev_xp );
-    }
-
-    // call driver for actual read 
-    thread_ptr->nic_cmd.cmd     = NIC_CMD_READ;
-    thread_ptr->nic_cmd.buffer  = pkd->buffer;
-    dev_ptr->cmd( thread_xp );
-
-    // check error
-    error = thread_ptr->nic_cmd.error;
-    if( error ) return error;
-
-    // returns packet length   
-    pkd->length = thread_ptr->nic_cmd.length;
+    if( this->nic_cmd.error ) 
+    {
 
 #if DEBUG_DEV_NIC_RX
 cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_DEV_NIC_RX < cycle )
-printk("\n[DBG] %s : thread %x exit for packet %x in cluster %x\n", 
-__FUNCTION__ , thread_ptr , pkd , local_cxy );
+printk("\n[%s] thread[%x,%x] exit / ERROR in NIC_RX / cycle %d\n", 
+__FUNCTION__, this->process->pid, this->trdid, cycle );
 #endif
 
+        return -1;
+    }
+    else
+    {
+
+#if DEBUG_DEV_NIC_RX
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_NIC_RX < cycle )
+printk("\n[%s] thread[%x,%x] exit / SUCCESS / cycle %d\n", 
+__FUNCTION__, this->process->pid, this->trdid, cycle );
+#endif
+
+        return 0;
+    }
+
+}   // end dev_nic_rx_move_packet()
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// This static function is called by the dev_nic_rx_server() function.
+// It analyses an Ethernet frame contained in the kernel buffer defined
+// by the <buffer> argument, and returns in the  <ip_length> argument the length
+// of the IP packet contained in the Ethernet packet payload.
+///////////////////////////////////////////////////////////////////////////////////////////
+// @ buffer     : [in] pointer on a received Ethernet packet
+// @ ip_length  : [out] length of IP packet (in bytes).
+// @ return 0 if success / return -1 if illegal packet length. 
+///////////////////////////////////////////////////////////////////////////////////////////
+static error_t dev_nic_rx_check_eth( uint8_t  * buffer,
+                                     uint32_t * ip_length )
+{
+    uint32_t length = ((uint32_t)buffer[12] << 8) | (uint32_t)buffer[13];
+
+    *ip_length = length;
+
     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;
+}
+   
+///////////////////////////////////////////////////////////////////////////////////////////
+// This static function analyses the IP packet contained in the kernel buffer
+// defined by the <buffer> argument, and returns in the <ip_src_addr>, <ip_dst_addr>, 
+// <header_length> and <protocol> arguments the informations contained in the IP header.
+// It checks the IP packet length versus the value contained in Ethernet header.
+// It checks the IP header checksum.
+///////////////////////////////////////////////////////////////////////////////////////////
+// @ buffer          : [in] pointer on the IP packet.
+// @ expected_length : [in] expected IP packet length (from Ethernet header). 
+// @ ip_src_addr     : [out] source IP address.
+// @ ip_dst_addr     : [out] destination IP address.
+// @ protocol        : [out] transport protocol type.
+// @ return 0 if success / return -1 if illegal packet. 
+///////////////////////////////////////////////////////////////////////////////////////////
+static error_t dev_nic_rx_check_ip( uint8_t  * buffer,
+                                    uint32_t   expected_length, 
+                                    uint32_t * ip_src_addr, 
+                                    uint32_t * ip_dst_addr,
+                                    uint32_t * trsp_protocol )
+{
+    uint32_t length = ((uint32_t)buffer[2] << 8) | (uint32_t)buffer[3];
+
+    // discard packet if eth_length != ip_length
+    if( length != expected_length )
+    {
+
+#if DEBUG_NIC_DEV
+thread_t * this = CURRENT_THREAD;
+printk("\n[%s] thread[%x,%x] enters : length (%d) != expected_length (%d)\n",
+__FUNCTION__, this->process->pid, this->trdid, length, expected_length );
+#endif
+
+        return -1;
+    }
+
+    // compute IP header checksum 
+    uint32_t received_cs = (uint32_t)dev_nic_ip_checksum( buffer );
+
+    // extract IP header checksum
+    uint32_t computed_cs = ((uint32_t)buffer[10] << 8) | ((uint32_t)buffer[11]);
+
+    // discard packet if bad checksum
+    if( received_cs != computed_cs )
+    {
+
+#if DEBUG_NIC_DEV
+thread_t * this = CURRENT_THREAD;
+printk("\n[%s] thread[%x,%x] computed checksum (%d) != received checksum (%d)\n",
+__FUNCTION__, this->process->pid, this->trdid, computed_cs, received_cs );
+#endif
+
+        return -1;
+    }
+
+
+    *ip_src_addr = ((uint32_t)buffer[12] << 24) |
+                   ((uint32_t)buffer[13] << 16) |
+                   ((uint32_t)buffer[14] <<  8) |
+                   ((uint32_t)buffer[15]      ) ;
+
+    *ip_dst_addr = ((uint32_t)buffer[16] << 24) |
+                   ((uint32_t)buffer[17] << 16) |
+                   ((uint32_t)buffer[18] <<  8) |
+                   ((uint32_t)buffer[19]      ) ;
+
+    *trsp_protocol = (uint32_t)buffer[9];
+   
+    return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// This static function analyses the UDP packet contained in the kernel buffer
+// defined by the <k_buf> and <k_length> arguments.
+// It checks the UDP checksum, and discard corrupted packets.
+// It scans the list of sockets attached to the NIC_RX chdev to find a matching socket,
+// and discard the received packet if no UDP socket found.
+// Finally, it copies the payload to the socket "rx_buf", as long as the packet payload
+// is not larger than the rx_buf. 
+// It set the "rx_valid" flip-flop, and unblock the client thread when the last expected
+// byte has been received.
+///////////////////////////////////////////////////////////////////////////////////////////
+// @ chdev         : [in] local pointer on local NIC_RX chdev descriptor.
+// @ k_buf         : [in] pointer on the UDP packet in local kernel buffer.
+// @ k_length      : [in] number of bytes in buffer (including UDP header).
+// @ pkt_src_addr  : [in] source IP address (from IP packet header).
+// @ pkt_dst_addr  : [in] destination IP address (from IP packet header).
+///////////////////////////////////////////////////////////////////////////////////////////
+static void dev_nic_rx_handle_udp_packet( chdev_t  * chdev,
+                                          uint8_t  * k_buf,
+                                          uint32_t   k_length,
+                                          uint32_t   pkt_src_addr,
+                                          uint32_t   pkt_dst_addr )
+{
+    xptr_t     root_xp;           // extended pointer on attached sockets list root 
+    xptr_t     lock_xp;           // extended pointer on chdev lock 
+    xptr_t     iter_xp;           // iterator on socket list
+    xptr_t     socket_xp;         // extended pointer on socket descriptor
+    cxy_t      socket_cxy;
+    socket_t * socket_ptr;
+    uint32_t   socket_type;       // socket type
+    uint32_t   socket_state;      // socket state
+    uint32_t   local_addr;        // local IP address from socket
+    uint32_t   local_port;        // local port from socket
+    uint32_t   remote_addr;       // remote IP address from socket
+    uint32_t   remote_port;       // remote port from socket
+    bool_t     match_socket;      // matching socket found
+    uint16_t   checksum;          // computed checksum
+    uint16_t   pkt_checksum;      // received checksum
+    xptr_t     socket_rbuf_xp;    // extended pointer on socket rx_buf
+    xptr_t     socket_lock_xp;    // extended pointer on socket lock
+    xptr_t     socket_client_xp;  // extended pointer on socket rx_client field
+    xptr_t     client_xp;         // extended pointer on client thread descriptor
+    uint32_t   payload;           // number of bytes in payload
+    uint32_t   status;            // number of bytes in rx_buf
+    uint32_t   space;             // number of free slots in rx_buf
+    uint32_t   moved_bytes;       // number of bytes actually moved to rx_buf
+
+    // build extended pointers on list of sockets attached to NIC_RX chdev
+    root_xp = XPTR( local_cxy , &chdev->wait_root );
+    lock_xp = XPTR( local_cxy , &chdev->wait_lock );
+
+    // compute UDP packet checksum 
+    checksum = dev_nic_udp_checksum( k_buf , k_length );
+
+    // get checksum from received packet header 
+    pkt_checksum = ((uint16_t)k_buf[6] << 8) | (uint16_t)k_buf[7];
+
+    // discard corrupted packet  
+    if( pkt_checksum != checksum ) return;
+    
+    // get src_port and dst_port from UDP header
+    uint32_t pkt_src_port = ((uint32_t)k_buf[0] << 8) | (uint32_t)k_buf[1];
+    uint32_t pkt_dst_port = ((uint32_t)k_buf[2] << 8) | (uint32_t)k_buf[3];
+
+    // discard unexpected packet
+    if( xlist_is_empty( root_xp ) ) return;
+ 
+    // take the tock protecting the sockets list
+    remote_busylock_acquire( lock_xp );
+
+    match_socket = false;
+
+    // scan sockets list to find a match
+    XLIST_FOREACH( root_xp , iter_xp )
+    {
+        // get socket cluster and local pointer
+        socket_xp  = XLIST_ELEMENT( iter_xp , socket_t , rx_list );
+        socket_ptr = GET_PTR( socket_xp );
+        socket_cxy = GET_CXY( socket_xp );
+
+        // get socket type 
+        socket_type  = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->type ));
+        socket_state = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->state ));
+                
+        // skip TCP socket
+        if( socket_type == SOCK_STREAM ) continue;
+
+        // get relevant info from socket descriptor
+        local_addr  = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->local_addr )); 
+        remote_addr = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->remote_addr )); 
+        local_port  = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->local_port )); 
+        remote_port = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->remote_port )); 
+
+        // compute matching
+        bool_t local_match  = (local_addr  == pkt_dst_addr) && 
+                              (local_port  == pkt_dst_port);
+
+        bool_t remote_match = (remote_addr == pkt_src_addr) &&
+                              (remote_port == pkt_src_port);
+
+        if (socket_state == UDP_STATE_CONNECT ) match_socket = local_match && remote_match;
+        else                                    match_socket = local_match;
+
+        // exit loop when socket found
+        if( match_socket ) break;
+    }
+
+    // release the lock protecting the sockets list
+    remote_busylock_release( lock_xp );
+
+    // discard unexpected packet
+    if( match_socket == false ) return;
+   
+    // build extended pointers on various socket fields
+    socket_rbuf_xp   = XPTR( socket_cxy , &socket_ptr->rx_buf );
+    socket_lock_xp   = XPTR( socket_cxy , &socket_ptr->lock ); 
+    socket_client_xp = XPTR( socket_cxy , &socket_ptr->rx_client );
+
+    // take the lock protecting the socket 
+    remote_rwlock_wr_acquire( socket_lock_xp );
+
+    // get status & space from rx_buf
+    status = remote_buf_status( socket_rbuf_xp );
+    space  = NIC_RX_BUF_SIZE - status;
+
+    // get client thread 
+    client_xp  = hal_remote_l64( socket_client_xp );
+
+    // get number of bytes in payload
+    payload = k_length - UDP_HEAD_LEN;
+
+    // compute number of bytes to move : min (space , seg_payload)
+    moved_bytes = ( space < payload ) ? space : payload;
+
+    // move payload from kernel buffer to socket rx_buf
+    remote_buf_put_from_kernel( socket_rbuf_xp,
+                                 k_buf + UDP_HEAD_LEN,
+                                 moved_bytes );
+
+    // unblock client thread if registered
+    if( client_xp != XPTR_NULL )
+    {
+        thread_unblock( client_xp , THREAD_BLOCKED_IO );
+    }
+
+    // release the lock protecting the socket
+    remote_rwlock_wr_release( socket_lock_xp );
+
+}  // end dev_nic_rx_handle_udp_packet()
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// This static function is called by the dev_nic_rx_server() function to handle one RX
+// TCP segment contained in a kernel buffer defined by the <k_buf> & <k_length> arguments.
+// It the received segment doesn't match an existing local socket, or is corrupted,
+// this faulty segment is discarded. 
+///////////////////////////////////////////////////////////////////////////////////////////
+// Implementation note:
+// 1) It checks the TCP checksum, and discard the corrupted segment.
+// 2) It scans the list of sockets attached to the RX chdev, to find the socket
+//    matching the TCP segment header, and discards the segment if no socket found.
+// 3) When a socket has been found, it takes the lock protecting the socket state,
+//    because the socket is accessed by both the NIC_TX and NIC_RX server threads.
+// 4) Depending on the socket state, it handle the received segment, including the
+//    SYN, FIN, ACK and RST flags. It updates the socket state when required, moves
+//    data to the rx_buf when possible, and registers requests to the TX server 
+//    thread in the R2T queue attached to the socket, to insert control flags in the
+//    TX stream, as required.
+// 5) Finally, it releases the lock protecting the socke and returns.
+///////////////////////////////////////////////////////////////////////////////////////////
+// @ chdev         : [in] local pointer on local NIC_RX chdev descriptor.
+// @ k_buf         : [in] pointer on the TCP packet in local kernel buffer.
+// @ k_length      : [in] number of bytes in buffer (including TCP header).
+// @ seg_src_addr  : [in] source IP address (from IP packet header).
+// @ seg_dst_addr  : [in] destination IP address (from IP packet header).
+///////////////////////////////////////////////////////////////////////////////////////////
+static void dev_nic_rx_handle_tcp_segment( chdev_t  * chdev,
+                                           uint8_t  * k_buf,
+                                           uint32_t   k_length,
+                                           uint32_t   seg_src_addr,
+                                           uint32_t   seg_dst_addr )
+{
+    xptr_t     root_xp;           // extended pointer on attached sockets list root 
+    xptr_t     lock_xp;           // extended pointer on chdev lock 
+    xptr_t     iter_xp;           // iterator for these queues
+    bool_t     match_socket;      // true if socket found
+    xptr_t     socket_xp;         // extended pointer on matching socket descriptor
+    cxy_t      socket_cxy;
+    socket_t * socket_ptr;
+    uint32_t   local_addr;        // local IP address from socket
+    uint32_t   local_port;        // local port from socket
+    uint32_t   remote_addr;       // remote IP address from socket
+    uint32_t   remote_port;       // remote port from socket
+    uint32_t   socket_state;      // socket state
+    uint32_t   socket_type;       // socket type
+    uint32_t   socket_tx_nxt;    // next byte to send in TX stream
+    uint32_t   socket_tx_una;    // first unacknowledged byte in TX stream
+    uint32_t   socket_rx_nxt;    // next expected byte in RX stream
+    uint32_t   socket_rx_wnd;    // current window value in RX stream
+    xptr_t     socket_lock_xp;    // extended pointer on lock protecting socket state
+    xptr_t     socket_rx_buf_xp;  // extended pointer on socket rx_buf
+    xptr_t     socket_r2tq_xp;    // extended pointer on socket r2t queue
+    xptr_t     socket_client_xp;  // extended pointer on socket rx_client thread
+    uint16_t   checksum;          // computed TCP segment chechsum
+
+    // build extended pointer on xlist of all sockets attached to NIC_RX chdev
+    root_xp = XPTR( local_cxy , &chdev->wait_root );
+    lock_xp = XPTR( local_cxy , &chdev->wait_lock );
+
+    // get relevant infos from TCP segment header
+    uint32_t seg_src_port = ((uint32_t)k_buf[0] << 8) | (uint32_t)k_buf[1];
+    uint32_t seg_dst_port = ((uint32_t)k_buf[2] << 8) | (uint32_t)k_buf[3];
+
+    uint32_t seg_seq_num  = ((uint32_t)k_buf[4]  << 24) | 
+                            ((uint32_t)k_buf[5]  << 16) |
+                            ((uint32_t)k_buf[6]  <<  8) |
+                            ((uint32_t)k_buf[7]       );
+
+    uint32_t seg_ack_num  = ((uint32_t)k_buf[8]  << 24) | 
+                            ((uint32_t)k_buf[9]  << 16) |
+                            ((uint32_t)k_buf[10] <<  8) |
+                            ((uint32_t)k_buf[11]      );
+
+    uint8_t  seg_hlen     = k_buf[12] >> 2;       // TCP header length in bytes
+ 
+    uint8_t  seg_flags    = k_buf[13];
+
+    bool_t   seg_ack_set  = ((seg_flags & TCP_FLAG_ACK) != 0);
+    bool_t   seg_syn_set  = ((seg_flags & TCP_FLAG_SYN) != 0);
+    bool_t   seg_fin_set  = ((seg_flags & TCP_FLAG_FIN) != 0);
+    bool_t   seg_rst_set  = ((seg_flags & TCP_FLAG_RST) != 0);
+
+    uint16_t seg_window   = ((uint32_t)k_buf[14] << 8) | (uint32_t)k_buf[15];
+
+    uint16_t seg_checksum = ((uint32_t)k_buf[16] << 8) | (uint32_t)k_buf[17];
+
+    uint32_t seg_payload  = k_length - seg_hlen;  // number of bytes in payload
+
+    // 1. compute TCP checksum
+    checksum = dev_nic_tcp_checksum( k_buf,
+                                     k_length,
+                                     seg_src_addr,
+                                     seg_dst_addr );
+
+    // discard segment if corrupted 
+    if( seg_checksum != checksum ) return;
+    
+    match_socket = false;
+
+    // take the lock protecting the list of sockets
+    remote_busylock_acquire( lock_xp );
+
+    // 2. scan list of sockets to find a matching socket
+    XLIST_FOREACH( root_xp , iter_xp )
+    {
+        // get socket cluster and local pointer
+        socket_xp  = XLIST_ELEMENT( iter_xp , socket_t , rx_list );
+        socket_ptr = GET_PTR( socket_xp );
+        socket_cxy = GET_CXY( socket_xp );
+
+        // get socket type and state
+        socket_type  = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->type ));
+        socket_state = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->state ));
+                
+        // skip UDP socket
+        if( socket_type == SOCK_DGRAM ) continue;
+
+        // get relevant socket infos for matching
+        local_addr   = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->local_addr )); 
+        remote_addr  = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->remote_addr )); 
+        local_port   = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->local_port )); 
+        remote_port  = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->remote_port )); 
+                
+        // compute matching condition
+        // (in LISTEN state, remote_port and remote_addr can be unspecified)
+        if( socket_state == TCP_STATE_LISTEN )
+        {
+            match_socket = (local_addr  == seg_dst_addr) && 
+                           (local_port  == seg_dst_port) ;
+        }
+        else
+        {
+            match_socket = (local_addr  == seg_dst_addr) && 
+                           (local_port  == seg_dst_port) &&
+                           (remote_addr == seg_src_addr) &&
+                           (remote_port == seg_src_port) ;
+        }
+
+        // exit loop if matching
+        if( match_socket ) break;
+
+    }  // end loop on sockets
+
+    // release the lock protecting the list of sockets
+    remote_busylock_release( lock_xp );
+
+    // discard segment if no matching socket found
+    if( match_socket == false ) return;
+
+    // From here the actions depend on both the socket state,
+    // and the received segment flags
+    // - update socket state,
+    // - move data to rx_buf,
+    // - make a R2T request when required
+
+    // build extended pointers on various socket fields
+    socket_lock_xp    = XPTR( socket_cxy , &socket_ptr->lock );
+    socket_rx_buf_xp  = XPTR( socket_cxy , &socket_ptr->rx_buf );
+    socket_r2tq_xp    = XPTR( socket_cxy , &socket_ptr->r2tq );
+    socket_client_xp  = XPTR( socket_cxy , &socket_ptr->rx_client );
+
+    // 3. take the lock protecting the matching socket
+    remote_rwlock_wr_acquire( socket_lock_xp );
+
+    // get relevant socket infos from socket descriptor
+    socket_state   = hal_remote_l32(XPTR( socket_cxy , &socket_ptr->state ));
+    socket_rx_nxt = hal_remote_l32(XPTR( socket_cxy , &socket_ptr->rx_nxt )); 
+    socket_rx_wnd = hal_remote_l32(XPTR( socket_cxy , &socket_ptr->rx_wnd )); 
+    socket_tx_una = hal_remote_l32(XPTR( socket_cxy , &socket_ptr->tx_una )); 
+    socket_tx_nxt = hal_remote_l32(XPTR( socket_cxy , &socket_ptr->tx_nxt )); 
+
+    switch( socket_state )
+    {
+        //////////////////////
+        case TCP_STATE_LISTEN:
+        {
+            // [1] discard segment if RST flag
+            if( seg_rst_set )  return;
+
+            // [2] send a RST & discard segment if ACK flag
+            if( seg_ack_set )
+            {
+                    // set socket.tx_nxt to seg_ack_num
+                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_nxt ),
+                                    seg_ack_num );
+
+                    // make RST request to R2T queue
+                    dev_nic_rx_put_r2t_request( socket_r2tq_xp,
+                                                TCP_FLAG_RST );
+                    // discard segment
+                    break;
+                }
+
+                // [3] handle SYN flag
+                if( seg_syn_set )
+                {
+                    // set socket.rx_nxt to seg_seq_num + 1
+                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_nxt ),
+                                    seg_seq_num + 1 );
+
+                    // set socket.tx_nxt to ISS
+                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_nxt ),
+                                    TCP_ISS );
+
+                    // set socket.rx_irs to seg_seq_num 
+                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_irs ),
+                                    seg_seq_num + 1 );
+
+                    // make SYN.ACK request to R2T queue
+                    dev_nic_rx_put_r2t_request( socket_r2tq_xp,
+                                                TCP_FLAG_SYN | TCP_FLAG_ACK );
+                     
+                    // set socket.tx_nxt to ISS + 1
+                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_nxt ),
+                                    TCP_ISS + 1 );
+
+                    // set socket.tx_una to ISS
+                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_una ),
+                                    TCP_ISS ); 
+            
+                    // update socket.state
+                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ),
+                                    TCP_STATE_SYN_RCVD );
+
+                    // update socket.remote_addr 
+                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->remote_addr ),
+                                    seg_src_addr );
+
+                    // update socket.remote_port 
+                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->remote_port ),
+                                    seg_src_port );
+                }                
+                break;
+            }
+            ////////////////////////
+            case TCP_STATE_SYN_SENT:
+            {
+                // [1] check ACK flag 
+                if( seg_ack_set )
+                {
+                    if( seg_ack_num != TCP_ISS + 1 )  // ACK not acceptable
+                    {
+                        // discard segment if RST
+                        if( seg_rst_set ) break;
+
+                        // set socket.tx_nxt to seg_ack_num
+                        hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_nxt ),
+                                        seg_ack_num );
+
+                        // make an RST request to R2T queue
+                        dev_nic_rx_put_r2t_request( socket_r2tq_xp,
+                                                    TCP_FLAG_RST );
+                        // discard segment
+                        break;
+                    }
+                }
+
+                // [2] check RST flag
+                if( seg_rst_set )
+                {
+                    // TODO signal "error: connection reset" to user
+
+                    // update socket.state
+                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ),
+                                    TCP_STATE_BOUND );
+
+                    // discard segment
+                    break;
+                }
+
+                // [3] handle SYN flag when (no ACK or acceptable ACK, and no RST)
+                if( seg_syn_set )
+                {
+                    // TODO Ne faut-il pas tester seg_seq_num ?
+
+                    if( seg_ack_set )  // received both SYN and ACK
+                    { 
+                        // set socket.rx_nxt to seg_seq_num + 1 
+                        hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_nxt ),
+                                        seg_seq_num + 1 );
+
+                        // set socket.tx_una to seg_ack_num 
+                        hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_una ),
+                                        seg_ack_num );
+
+                        // set socket.rx_irs to seg_seq_num
+                        hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_irs ),
+                                        seg_seq_num );
+
+                        // update socket.state
+                        hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ),
+                                        TCP_STATE_ESTAB );
+
+                        // make an ACK request to R2T queue
+                        dev_nic_rx_put_r2t_request( socket_r2tq_xp,
+                                                    TCP_FLAG_ACK );
+                    }
+                    else               // received SYN without ACK
+                    {
+                        // update socket.state
+                        hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ),
+                                        TCP_STATE_SYN_RCVD );
+
+                        // set socket.tx_nxt to ISS
+                        hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_nxt ),
+                                        TCP_ISS );     
+
+                        // make a SYN.ACK request to R2T queue
+                        dev_nic_rx_put_r2t_request( socket_r2tq_xp,
+                                                    TCP_FLAG_SYN | TCP_FLAG_ACK );
+                    }
+                }
+                break;
+            }
+            ////////////////////////
+            case TCP_STATE_SYN_RCVD:
+            case TCP_STATE_ESTAB:
+            case TCP_STATE_FIN_WAIT1:
+            case TCP_STATE_FIN_WAIT2:
+            case TCP_STATE_CLOSE_WAIT:
+            case TCP_STATE_CLOSING:
+            case TCP_STATE_LAST_ACK:
+            case TCP_STATE_TIME_WAIT:
+            {
+                // [1] check sequence number 
+
+                // compute min & max acceptable sequence numbers
+                uint32_t seq_min  = socket_rx_nxt;
+                uint32_t seq_max  = socket_rx_nxt + socket_rx_wnd - 1;
+
+                // compute sequence number for last byte in segment 
+                uint32_t seg_seq_last = seg_seq_num + seg_payload - 1;
+
+                if( (seg_seq_num != socket_rx_nxt) ||     // out_of_order 
+                    (is_in_window( seg_seq_last, 
+                                   seq_min,
+                                   seq_max ) == false) )  // out_of_window
+                {
+                    // discard segment
+                    return;
+                } 
+
+                // [2] handle RST flag
+
+                if( seg_rst_set )
+                {
+                     if( socket_state == TCP_STATE_SYN_RCVD )
+                     {
+                         // TODO unblock all clients threads with "reset" responses
+                     }
+                     else if( (socket_state == TCP_STATE_ESTAB     ) ||
+                              (socket_state == TCP_STATE_FIN_WAIT1 ) ||
+                              (socket_state == TCP_STATE_FIN_WAIT2 ) ||
+                              (socket_state == TCP_STATE_CLOSE_WAIT) )
+                     {
+                         // TODO all pending send & received commands 
+                         // must receive "reset" responses
+
+                         // TODO destroy the socket
+                     }
+                     else  // all other states
+                     {
+                             
+
+                }
+
+                // [3] handle security & precedence TODO ... someday
+
+                // [4] handle SYN flag
+
+                if( seg_syn_set )        // received SYN
+                {
+                    // TODO signal error to user
+
+                    // make an RST request to R2T queue 
+                    dev_nic_rx_put_r2t_request( socket_r2tq_xp,
+                                                TCP_FLAG_RST );
+                    // update socket state
+                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ),
+                                    TCP_STATE_BOUND );
+                }
+
+                // [5] handle  ACK flag 
+
+                if( seg_ack_set == false ) 
+                {
+                    // discard segment when ACK not set
+                    break;
+                }
+                else if( socket_state == TCP_STATE_SYN_RCVD )
+                {
+                    if( is_in_window( seg_ack_num , socket_tx_una , socket_tx_nxt ) ) 
+                    {
+                        // update socket.state to ESTAB
+                        hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ),
+                                              TCP_STATE_ESTAB );
+                    }
+                    else   // unacceptable ACK
+                    {
+                        // set socket.tx_nxt to seg_ack_num 
+                        hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_nxt ),
+                                        seg_ack_num );
+
+                        // make an RST request to R2T queue 
+                        dev_nic_rx_put_r2t_request( socket_r2tq_xp,
+                                                    TCP_FLAG_RST );
+                    }
+                }
+                else if( (socket_state == TCP_STATE_ESTAB)      ||
+                         (socket_state == TCP_STATE_FIN_WAIT1)  ||
+                         (socket_state == TCP_STATE_FIN_WAIT1)  ||
+                         (socket_state == TCP_STATE_CLOSE_WAIT) ||
+                         (socket_state == TCP_STATE_CLOSING)    )
+                {
+                    if( is_in_window( seg_ack_num + 1 , socket_tx_una , socket_tx_nxt ) ) 
+                    {
+                        // update socket.tx_una
+                        hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_una ),
+                                        seg_ack_num );
+
+                        // update socket.tx_wnd  
+                        hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_wnd ),
+                                        seg_window );
+                    }
+                    else   // unacceptable ACK
+                    {
+                        // discard segment
+                        break;
+                    }
+                
+                    // specific for FIN_WAIT1 
+                    if( socket_state == TCP_STATE_FIN_WAIT1 )
+                    {
+                        if( seg_fin_set )
+                        {
+                            // update socket.state
+                            hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ),
+                                                  TCP_STATE_FIN_WAIT2 );
+                        }
+                    }
+
+                    // specific for CLOSING
+                    if( socket_state == TCP_STATE_CLOSING )
+                    {
+                        if( seg_ack_set )
+                        {
+                            // update socket.state
+                            hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ),
+                                                  TCP_STATE_TIME_WAIT );
+                        }
+                        else
+                        {
+                            // discard segment
+                            break;
+                        }
+                    }
+                }
+                else if( socket_state == TCP_STATE_LAST_ACK )
+                {
+                    if( seg_ack_set )
+                    {
+                        // update socket.state
+                        hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ),
+                                              TCP_STATE_TIME_WAIT );
+                    }
+                    
+                }
+
+                // [6] handle URG flag  TODO ... someday
+
+                // [7] Move DATA to rx_buf and unblock client thread
+
+                if( seg_payload )
+                {
+                    if( (socket_state == TCP_STATE_ESTAB)     ||
+                        (socket_state == TCP_STATE_FIN_WAIT1) ||
+                        (socket_state == TCP_STATE_FIN_WAIT2) )
+                    {
+                        // get number of bytes already stored in rx_buf
+                        uint32_t status = remote_buf_status( socket_rx_buf_xp );
+
+                        // compute empty space in rx_buf
+                        uint32_t space = NIC_RX_BUF_SIZE - status;
+
+                        // compute number of bytes to move : min (space , seg_payload)
+                        uint32_t nbytes = ( space < seg_payload ) ? space : seg_payload;
+
+                        // move payload from k_buf to rx_buf
+                        remote_buf_put_from_kernel( socket_rx_buf_xp, 
+                                                    k_buf + seg_hlen,
+                                                    nbytes );
+                        // update socket.rx_nxt 
+                        hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_nxt ),
+                                              socket_rx_nxt + nbytes );
+
+                        // update socket.rx_wnd 
+                        hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_wnd ),
+                                        socket_rx_wnd - nbytes ); 
+
+                        // make an ACK request to R2T queue
+                        dev_nic_rx_put_r2t_request( socket_r2tq_xp,
+                                                    TCP_FLAG_ACK );
+
+                        // get extended pointer on rx_client thread
+                        xptr_t client_xp = hal_remote_l64( socket_client_xp );
+                        
+                        // unblock client thread 
+                        if( client_xp != XPTR_NULL )
+                        {
+                            thread_unblock( client_xp , THREAD_BLOCKED_IO );
+                        }
+                    }
+                }
+
+                // [8] handle FIN flag 
+
+                if( seg_fin_set )
+                {
+                    if( (socket_state == TCP_STATE_UNBOUND) ||
+                        (socket_state == TCP_STATE_BOUND)   ||
+                        (socket_state == TCP_STATE_LISTEN)  ||
+                        (socket_state == TCP_STATE_SYN_SENT) ) 
+                    {
+                        // discard segment
+                        break;
+                    }
+                    else // all other states
+                    {
+                        // TODO signal "connection closing"
+
+                        // make an ACK request to R2T queue
+                        dev_nic_rx_put_r2t_request( socket_r2tq_xp,
+                                                    TCP_FLAG_ACK );
+
+                        // increment socket.rx_nxt
+                        hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_nxt ),
+                                        socket_rx_nxt + 1 ); 
+
+                        if( (socket_state == TCP_STATE_SYN_RCVD) ||
+                            (socket_state == TCP_STATE_ESTAB) )
+                        {
+                            // update socket.state
+                            hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ),
+                                            TCP_STATE_TIME_WAIT );
+                        }
+                        else if( socket_state == TCP_STATE_FIN_WAIT1 )
+                        { 
+                            if( seg_ack_set ) 
+                            {
+                                // TODO start "time-wait" timer / turn off others timers
+
+                                // update socket.state
+                                hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ),
+                                                TCP_STATE_TIME_WAIT );
+                            }
+                            else
+                            {
+                                // update socket.state
+                                hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ),
+                                                TCP_STATE_CLOSING );
+                            }
+                        }
+                        else if( socket_state == TCP_STATE_FIN_WAIT2 )
+                        {
+                            // TODO start "time-wait" timer / turn off other timers
+
+                            // update socket.state
+                            hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ),
+                                            TCP_STATE_TIME_WAIT );
+                        }
+                        else if( socket_state == TCP_STATE_TIME_WAIT )
+                        {
+                            // TODO restart "time_wait" timer
+                        }
+                    }
+                }  // end if FIN 
+            }  // end case sockets synchronized
+        }  // end switch socket state
+
+        // release the lock protecting socket
+        remote_rwlock_wr_acquire( socket_lock_xp );
+
+    }  // end socket found
+
+}  // end dev_nic_rx_handle_tcp_segment()
+
+
+/////////////////////////////////////////
+void dev_nic_rx_server( chdev_t * chdev )
+{
+    uint8_t       k_buf[2048];          // kernel buffer for one ETH/IP/UDP packet
+                                  
+    uint32_t      pkt_src_addr;         // packet source IP address 
+    uint32_t      pkt_dst_addr;         // packet destination IP address
+    uint32_t      trsp_protocol;        // transport protocol (TCP / UDP)
+    uint32_t      eth_length;           // size of Ethernet packet (bytes)
+    uint32_t      ip_length;            // size of IP packet in bytes
+    uint32_t      nic_queue_readable;   // NIC_RX queue non empty when true
+    error_t       error;
+
+    thread_t * this = CURRENT_THREAD;
+
+// check chdev direction and type
+assert( (chdev->func == DEV_FUNC_NIC) && (chdev->is_rx == true) ,
+"illegal chdev type or direction" );
 
 // check thread can yield
-assert( (thread_ptr->busylocks == 0),
-"cannot yield : busylocks = %d\n", thread_ptr->busylocks );
+assert( (this->busylocks == 0),
+"cannot yield : busylocks = %d\n", this->busylocks );
+
+    while( 1 )
+    {
+        // check NIC_RX_QUEUE readable
+        error = dev_nic_rx_queue_readable( chdev,
+                                           &nic_queue_readable );
+        if( error )
+        {
+            printk("\n[PANIC] in %s : cannot access NIC_TX[%d] queue\n",
+            __FUNCTION__, chdev->channel );
+        }
+    
+        if( nic_queue_readable ) // NIC_TX_QUEUE non empty
+        {
+            // moves one Ethernet packet to kernel buffer
+            error = dev_nic_rx_move_packet( chdev,
+                                            k_buf,
+                                            &eth_length );
+            if( error )
+            {
+                printk("\n[PANIC] in %s : cannot read the NIC_TX[%d] queue\n",
+                __FUNCTION__, chdev->channel );
+            }
+
+            // analyse the ETH header
+            error = dev_nic_rx_check_eth( k_buf,
+                                          &ip_length );
+
+            // discard packet if error reported by Ethernet layer
+            if( error ) continue; 
+
+            // analyse the IP header
+            error = dev_nic_rx_check_ip( k_buf + ETH_HEAD_LEN,
+                                         ip_length, 
+                                         &pkt_src_addr, 
+                                         &pkt_dst_addr,
+                                         &trsp_protocol );
+
+            // discard packet if error reported by IP layer
+            if( error ) continue; 
+
+            // call relevant transport protocol
+            if( trsp_protocol == PROTOCOL_UDP )   
+            { 
+                dev_nic_rx_handle_udp_packet( chdev,
+                                              k_buf + ETH_HEAD_LEN + IP_HEAD_LEN,
+                                              ip_length - IP_HEAD_LEN,
+                                              pkt_src_addr,
+                                              pkt_dst_addr );
+            }
+            else if ( trsp_protocol == PROTOCOL_TCP)
+            {
+                dev_nic_rx_handle_tcp_segment( chdev,
+                                               k_buf + ETH_HEAD_LEN + IP_HEAD_LEN,
+                                               ip_length - IP_HEAD_LEN,
+                                               pkt_src_addr,
+                                               pkt_dst_addr );
+            }
+        }
+        else     // block and deschedule if NIC_RX_QUEUE empty
+        {
+            thread_block( XPTR( local_cxy , this ) , THREAD_BLOCKED_ISR );
+            sched_yield( "waiting RX client" );
+        }
+
+    } // end of while loop
+
+}  // end dev_nic_rx_server()
+
+
+
+
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+//              Functions used by the NIC_TX server thread
+///////////////////////////////////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// These static functions are called by the NIC_TX server thread to report the 
+// completion  (success or error) of a TX command.
+// - it print an error message in case of error.
+// - it updates the "tx_error"  field in socket descriptor.
+// - it unblocks the client thread.
+///////////////////////////////////////////////////////////////////////////////////////////
+// @ socket_xp    : [in] extended pointer on socket
+// @ cmd_type     : [in] SOCKET_TX_CONNECT / SOCKET_TX_SEND / SOCKET_TX_CLOSE
+// @ socket_state : [in] current socket state
+///////////////////////////////////////////////////////////////////////////////////////////
+static void dev_nic_tx_report_error( xptr_t    socket_xp,
+                                     uint32_t  cmd_type,
+                                     uint32_t  socket_state )
+{
+    printk("\n[ERROR] in %s : command %s in %s state\n",
+    __FUNCTION__, socket_cmd_str(cmd_type), socket_state_str(socket_state) );
+
+    // get socket thread cluster and local pointer
+    socket_t * socket_ptr = GET_PTR( socket_xp );
+    cxy_t      socket_cxy = GET_CXY( socket_xp );
+
+    // set tx_error field in socket descriptor
+    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_error ) , 1 );
+
+    // get extended point on client thread
+    xptr_t client_xp = hal_remote_l64( XPTR( socket_cxy , &socket_ptr->tx_client ));
+
+    // unblock the client thread
+    thread_unblock( client_xp , THREAD_BLOCKED_IO );
+}
+
+////////////////////////////////////////////////////////////
+static void dev_nic_tx_report_success( xptr_t    socket_xp )
+{
+    // get socket thread cluster and local pointer
+    socket_t * socket_ptr = GET_PTR( socket_xp );
+    cxy_t      socket_cxy = GET_CXY( socket_xp );
+
+    // set tx_error field in socket descriptor
+    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_error ) , 0 );
+
+    // get extended point on client thread
+    xptr_t client_xp = hal_remote_l64( XPTR( socket_cxy , &socket_ptr->tx_client ));
+
+    // unblock the client thread
+    thread_unblock( client_xp , THREAD_BLOCKED_IO );
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// This static function is called by the dev_nic_tx_server() function.
+// It calls directly the NIC driver (WRITABLE command) and returns the status
+// of the NIC_TX queue identified by the <chdev> argument.
+// in the <writable> buffer.
+///////////////////////////////////////////////////////////////////////////////////////////
+// @ chdev     : [in]  local pointer on NIC_TX chdev.
+// @ length    : [in]  packet length in bytes.
+// @ writable  : [out] zero if queue full.  
+// @ returns 0 if success / returns -1 if failure in accessing NIC device.
+///////////////////////////////////////////////////////////////////////////////////////////
+error_t dev_nic_tx_queue_writable( chdev_t  * chdev,
+                                   uint32_t   length,
+                                   uint32_t * writable )
+{
+    thread_t * this = CURRENT_THREAD;
+
+    // initialize READABLE command in thread descriptor
+    this->nic_cmd.dev_xp = XPTR( local_cxy , chdev );
+    this->nic_cmd.type   = NIC_CMD_WRITABLE;
+    this->nic_cmd.length = length;
+
+    // call driver to test writable
+    chdev->cmd( XPTR( local_cxy , this ) );
+
+    // return status
+    *writable = this->nic_cmd.status;
+
+    // return error
+    return this->nic_cmd.error;
+
+}  // end dev_nic_tx_queue_writable
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// This static function is called by the dev_nic_tx_server() function.
+// It moves one ETH/IP/UDP packet from the kernel buffer identified by the <buffer> and 
+// <length> arguments to the NIC_TX_QUEUE identified the <chdev> argument.
+// It calls directly the NIC driver, without registering in a waiting queue, because 
+// only this NIC_TX server thread can access this NIC_TX_QUEUE.
+// 1) It checks NIC_TX_QUEUE status in a while loop, using the NIC_CMD_WRITABLE command.
+//    As long as the queue is not writable, it blocks and deschedules. It is re-activated 
+//    by the NIC-TX ISR as soon as the queue changes status.
+// 2) When the queue is writable, it put the ETH/IP/UDP packet into the NIC_TX_QUEUE,
+//   using the driver NIC_CMD_WRITE command.
+// Both commands are successively registered in this NIC-TX server thread descriptor
+// to be passed to the driver. 
+///////////////////////////////////////////////////////////////////////////////////////////
+// @ chdev   : [in] local pointer on NIC_TX chdev.
+// @ buffer  : [in] pointer on a local kernel buffer (2K bytes).
+// @ length  : [in] actual Ethernet packet length in bytes.
+///////////////////////////////////////////////////////////////////////////////////////////
+void dev_nic_tx_move_packet( chdev_t  * chdev,
+                             uint8_t  * buffer,
+                             uint32_t   length )
+{
+    error_t    error;
+    uint32_t   writable;
+
+    thread_t * this = CURRENT_THREAD;
+
+    // get extended pointers on server tread and chdev 
+    xptr_t     thread_xp = XPTR( local_cxy , this );
+    xptr_t     chdev_xp  = XPTR( local_cxy , chdev );
+
+    // get local pointer on core running this server thead
+    core_t * core = this->core;
+
+// check thread can yield
+assert( (this->busylocks == 0),
+"cannot yield : busylocks = %d\n", this->busylocks );
 
 #if DEBUG_DEV_NIC_RX
 uint32_t cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_DEV_NIC_RX < cycle )
-printk("\n[DBG] %s : thread %x enters for packet %x in cluster %x\n", 
-__FUNCTION__ , thread_ptr , pkd , local_cxy );
+printk("\n[%s] thread[%x,%x] enters for packet %x / cycle %d\n", 
+__FUNCTION__, this->process->pid, this->trdid, pkd, cycle );
 #endif
 
-    // get pointer on NIC-TX chdev descriptor
-    uint32_t   channel = thread_ptr->chdev->channel; 
-    xptr_t     dev_xp  = chdev_dir.nic_tx[channel];
-    cxy_t      dev_cxy = GET_CXY( dev_xp );
-    chdev_t  * dev_ptr = (chdev_t *)GET_PTR( dev_xp );
-
-    assert( (dev_xp != XPTR_NULL) , "undefined NIC chdev descriptor" );
-
-    assert( (dev_cxy == local_cxy) , " chdev must be local" );
-
-    // initialize command in thread descriptor
-    thread_ptr->nic_cmd.dev_xp = dev_xp;
-
-    // call driver to test writable
-    thread_ptr->nic_cmd.cmd = NIC_CMD_WRITABLE;
-    dev_ptr->cmd( thread_xp );
-
-    // check error
-    error = thread_ptr->nic_cmd.error;
-    if( error ) return error;
-
-    // block and deschedule if queue non writable
-    if( thread_ptr->nic_cmd.status == false )  
-    {
-        // enable NIC-TX IRQ 
-        dev_pic_enable_irq( core->lid ,dev_xp );
-
-        // block client thread on THREAD_BLOCKED I/O condition
-        thread_block( XPTR( local_cxy , thread_ptr ) , THREAD_BLOCKED_IO );
-
-        // deschedule client thread
-        sched_yield("client blocked on I/O");
-
-        // disable NIC-TX IRQ 
-        dev_pic_disable_irq( core->lid , dev_xp );
-    }
-
-    // call driver for actual write
-    thread_ptr->nic_cmd.cmd    = NIC_CMD_WRITE;
-    thread_ptr->nic_cmd.buffer = pkd->buffer;
-    thread_ptr->nic_cmd.length = pkd->length;
-    dev_ptr->cmd( thread_xp );
-
-    // check error
-    error = thread_ptr->nic_cmd.error;
-    if( error ) return error;
+    // check NIC_TX_QUEUE writable
+    while( 1 )
+    {
+        error = dev_nic_tx_queue_writable( chdev,
+                                           length,
+                                           &writable );
+        if( error )
+        {
+            printk("\n[PANIC] in %s : cannot access NIC_TX queue\n", __FUNCTION__ );
+            return;
+        }
+            
+        if( writable == 0 )  // block & deschedule if non writable
+        {
+            // enable NIC-TX IRQ 
+            dev_pic_enable_irq( core->lid , chdev_xp );
+
+            // block TX server thread
+            thread_block( thread_xp , THREAD_BLOCKED_ISR );
+
+            // deschedule TX server thread
+            sched_yield("client blocked on NIC_TX queue full");
+
+            // disable NIC-TX IRQ 
+            dev_pic_disable_irq( core->lid , chdev_xp );
+        }
+        else                // exit loop if writable
+        {
+            break;
+        }
+    }
+
+    // initialize WRITE command in server thread descriptor
+    this->nic_cmd.dev_xp = chdev_xp;
+    this->nic_cmd.type   = NIC_CMD_WRITE;
+    this->nic_cmd.buffer = buffer;
+    this->nic_cmd.length = length;
+
+    // call driver to move packet
+    chdev->cmd( thread_xp );
 
 #if DEBUG_DEV_NIC_RX
 cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_DEV_NIC_RX < cycle )
-printk("\n[DBG] %s : thread %x exit for packet %x in cluster %x\n", 
-__FUNCTION__ , thread_ptr , pkd , local_cxy );
+printk("\n[%s] thread[%x,%x] exit for packet %x\n", 
+__FUNCTION__ , this->process->pid, this->trdid , pkd );
 #endif
 
-    return 0;
-}  // end dev_nic_write()
-
-
-
+    return;
+
+}  // end dev_nic_tx_move_packet()
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// This static function is called by the dev_nic_tx_server() function to build an UDP
+// header in the kernel buffer defined by the <k_buf> arguement,  as specified by the 
+// <socket_xp> argument. The <length> argument defines the number of bytes in payload.
+// It set the "src_port", "dst_port", "total_length" and "checksum" fields in UDP header.
+// The payload must be previouly loaded in the pernel buffer.
+///////////////////////////////////////////////////////////////////////////////////////////
+// @ k_buf      : [in]  pointer on first byte of UDP header in kernel buffer.
+// @ socket_xp  : [in]  extended pointer on socket.
+// @ length     : [in]  number of bytes in payload.
+///////////////////////////////////////////////////////////////////////////////////////////
+void dev_nic_tx_build_udp_header( uint8_t  * k_buf,
+                                  xptr_t     socket_xp,
+                                  uint32_t   length )
+{
+    uint16_t   checksum;        // checksum value
+    uint32_t   total_length;    // total UDP packet length
+    uint32_t   local_addr;      // local IP address
+    uint32_t   remote_addr;     // remote IP address
+    uint32_t   local_port;      // local port
+    uint32_t   remote_port;     // remote port
+
+    // get socket cluster an local pointer
+    socket_t * socket_ptr = GET_PTR( socket_xp );
+    cxy_t      socket_cxy = GET_CXY( socket_xp );
+
+    // get relevant infos from socket
+    local_addr  = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->local_addr )); 
+    remote_addr = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->remote_addr )); 
+    local_port  = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->local_port )); 
+    remote_port = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->remote_port )); 
+
+    // compute UDP packet total length
+    total_length = length + UDP_HEAD_LEN;
+
+    // set src_port and dst_port in header
+    k_buf[0] = local_port >> 8; 
+    k_buf[1] = local_port; 
+    k_buf[2] = remote_port >> 8; 
+    k_buf[3] = remote_port; 
+
+    // set packet length in header
+    k_buf[4] = total_length >> 8;
+    k_buf[5] = total_length;
+    
+    // compute UDP packet checksum
+    checksum = dev_nic_udp_checksum( k_buf , total_length );
+
+    // set checksum
+    k_buf[6] = checksum >> 8;
+    k_buf[7] = checksum;
+
+}  // end dev_nic_tx_build_udp_header()
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// This static function is called by the dev_nic_tx_server() function. 
+// It builds a TCP header in the kernel buffer defined by the <k_buf> argument.
+// The payload must have been previouly registered in this buffer.
+// The "local_addr", "local_port", "remote_addr", "remote_port", seq_num", "ack_num",
+// and "window" fields are obtained from the <socket_xp> argument.
+// The <length> argument defines the number of bytes in payload, and the <flags> argument
+// defines the flags to be set in TCP header.
+///////////////////////////////////////////////////////////////////////////////////////////
+// @ k_buf      : [in]  pointer on first byte of TCP header in kernel buffer.
+// @ length     : [in]  number of bytes in payload.
+// @ socket_xp  : [in]  extended pointer on socket.
+// @ flags      : [in]  flags to be set in TCP header.
+///////////////////////////////////////////////////////////////////////////////////////////
+void dev_nic_tx_build_tcp_header( uint8_t  * k_buf,
+                                  uint32_t   length,
+                                  xptr_t     socket_xp,
+                                  uint8_t    flags )
+{
+    uint16_t   checksum;        // global segment checksum
+    uint32_t   total_length;    // total UDP packet length
+    uint32_t   src_addr;        // local IP address
+    uint32_t   dst_addr;        // remote IP address
+    uint16_t   src_port;        // local port
+    uint16_t   dst_port;        // remote port
+    uint32_t   seq_num;         // first byte of segment in TX stream
+    uint32_t   ack_num;         // next expected byte in RX stream
+    uint16_t   window;          // window of accepted segments in RX stream
+
+    // get socket cluster an local pointer
+    socket_t * sock_ptr = GET_PTR( socket_xp );
+    cxy_t      sock_cxy = GET_CXY( socket_xp );
+
+    // get relevant infos from socket
+    src_addr = hal_remote_l32(XPTR( sock_cxy , &sock_ptr->local_addr )); 
+    dst_addr = hal_remote_l32(XPTR( sock_cxy , &sock_ptr->remote_addr )); 
+    src_port = hal_remote_l32(XPTR( sock_cxy , &sock_ptr->local_port )); 
+    dst_port = hal_remote_l32(XPTR( sock_cxy , &sock_ptr->remote_port )); 
+    seq_num  = hal_remote_l32(XPTR( sock_cxy , &sock_ptr->tx_nxt ));
+    ack_num  = hal_remote_l32(XPTR( sock_cxy , &sock_ptr->rx_nxt ));
+    window   = hal_remote_l32(XPTR( sock_cxy , &sock_ptr->rx_wnd ));
+
+    // compute TCP segment total length
+    total_length = length + TCP_HEAD_LEN;
+
+    // set "src_port" and "dst_port" 
+    k_buf[0]  = src_port >> 8; 
+    k_buf[1]  = src_port; 
+    k_buf[2]  = dst_port >> 8; 
+    k_buf[3]  = dst_port; 
+
+    // set "seq_num"  
+    k_buf[4]  = seq_num >> 24;
+    k_buf[5]  = seq_num >> 16;
+    k_buf[6]  = seq_num >>  8;
+    k_buf[7]  = seq_num;
+
+    // set "ack_num"  
+    k_buf[8]  = ack_num >> 24;
+    k_buf[9]  = ack_num >> 16;
+    k_buf[10] = ack_num >>  8;
+    k_buf[11] = ack_num;
+
+    // set "hlen"
+    k_buf[12] = 5;
+
+    // set "flags"
+    k_buf[13] = flags & 0x3F;
+
+    // set "window"
+    k_buf[14] = window >> 8;
+    k_buf[15] = window;
+
+    // reset "checksum"
+    k_buf[16] = 0;
+    k_buf[17] = 0;
+    
+    // set "urgent_ptr"
+    k_buf[18] = 0;
+    k_buf[19] = 0;
+  
+    // compute TCP segment checksum
+    checksum = dev_nic_tcp_checksum( k_buf,
+                                     total_length,
+                                     src_addr,
+                                     dst_addr );
+    // set "checksum"
+    k_buf[16] = checksum >> 8;
+    k_buf[17] = checksum;
+
+}  // end dev_nic_tx_build_tcp_header()
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// This static function is called by the dev_nic_tx_server() function.
+// It builds the IP header in the 20 first bytes of <buffer>.
+///////////////////////////////////////////////////////////////////////////////////////////
+// @ buffer     : pointer on first byte of IP header in kernel buffer
+// @ src_addr   : source IP address.
+// @ dst_addr   : destination IP address.
+// @ length     : number of bytes in IP packet payload.
+///////////////////////////////////////////////////////////////////////////////////////////
+void dev_nic_tx_build_ip_header( uint8_t * buffer,
+                                 uint32_t  src_addr,
+                                 uint32_t  dst_addr,
+                                 uint16_t  length )
+{
+    uint16_t   hcs;
+
+    uint16_t   total = length + IP_HEAD_LEN;
+
+    buffer[0]  = 0x45;         // IPV4 / IHL = 20 bytes  
+    buffer[1]  = 0;            // DSCP / ECN
+    buffer[2]  = total >> 8;  
+    buffer[3]  = total;  
+
+    buffer[4]  = 0x40;         // Don't Fragment
+    buffer[5]  = 0; 
+    buffer[6]  = 0;
+    buffer[7]  = 0; 
+
+    buffer[8]  = 0xFF;         // TTL
+    buffer[9]  = 0x11;         // UDP protocol 
+    
+    buffer[12] = src_addr >> 24; 
+    buffer[13] = src_addr >> 16; 
+    buffer[14] = src_addr >> 8; 
+    buffer[15] = src_addr; 
+
+    buffer[16] = dst_addr >> 24; 
+    buffer[17] = dst_addr >> 16; 
+    buffer[18] = dst_addr >> 8;
+    buffer[19] = dst_addr; 
+
+    // compute IP header checksum
+    hcs = dev_nic_ip_checksum( buffer ); 
+
+    // set checksum 
+    buffer[10] = hcs >> 8;
+    buffer[11] = hcs; 
+
+}  // end dev_nic_tx_build_ip_header
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// This static function is called by the dev_nic_tx_server() function.
+// It builds the Ethernet header in the 14 first bytes of <buffer>.
+///////////////////////////////////////////////////////////////////////////////////////////
+// @ buffer     : pointer on first byte of Ethernet header in kernel buffer
+// @ src_mac_54 : two MSB bytes in source MAC address.
+// @ src_mac_32 : two MED bytes in source MAC address.
+// @ src_mac_10 : two LSB bytes in source MAC address.
+// @ dst_mac_54 : two MSB bytes in destination MAC address.
+// @ dst_mac_32 : two MED bytes in destination MAC address.
+// @ dst_mac_10 : two LSB bytes in destination MAC address.
+// @ length     : number of bytes in Ethernet frame payload.
+///////////////////////////////////////////////////////////////////////////////////////////
+void dev_nic_tx_build_eth_header( uint8_t * buffer,
+                                  uint16_t  src_mac_54,
+                                  uint16_t  src_mac_32,
+                                  uint16_t  src_mac_10,
+                                  uint16_t  dst_mac_54,
+                                  uint16_t  dst_mac_32,
+                                  uint16_t  dst_mac_10,
+                                  uint32_t  length )
+{
+    buffer[0]  = dst_mac_54 >> 8;
+    buffer[1]  = dst_mac_54;
+    buffer[2]  = dst_mac_32 >> 8;
+    buffer[3]  = dst_mac_32;
+    buffer[4]  = dst_mac_10 >> 8;
+    buffer[5]  = dst_mac_10;
+
+    buffer[6]  = src_mac_54 >> 8;
+    buffer[7]  = src_mac_54;
+    buffer[8]  = src_mac_32 >> 8;
+    buffer[9]  = src_mac_32;
+    buffer[10] = src_mac_10 >> 8;
+    buffer[11] = src_mac_10;
+
+    buffer[12] = length >> 8;
+    buffer[13] = length;
+
+}  // end dev_nic_tx_build_eth_header()
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// This static function is called by the dev_nic_tx_server() function to handle one 
+// TX command, or one R2T request, registered in the socket identified by the <socket_xp>
+// argument. If there is one valid command, or if the R2T queue is non empty (for a TCP
+// socket), it builds an ETH/IP/UDP packet (or a ETH/IP/TCP segment), in the buffer 
+// defined by the  <k_buf> argument, and registers it in the NIC_TX queue defined by the 
+// <chdev> argument. The supported commands are SOCKET_SEND/SOCKET_CONNECT/SOCKET_CLOSE.
+// It unblocks the client thread when the command is completed.
+///////////////////////////////////////////////////////////////////////////////////////////
+// When there is a packet to send, it makes the following actions:
+// 1) it takes the lock protecting the socket state.
+// 2) it get the command arguments from client thread descriptor.
+// 3) it build an UDP packet or a TCP segment, depending on both the command type, and
+//    the socket state, updates the socket state, and unblocks the client thread.
+// 4) it release the lock protecting the socket.
+// 5) it build the IP header.
+// 6) it build the ETH header.
+// 7) it copies the packet in the NIC_TX queue.
+///////////////////////////////////////////////////////////////////////////////////////////
+// @ socket_xp   : [in] extended pointer on client socket.  
+// @ k_buf       : [in] local pointer on kernel buffer (2 Kbytes).
+// @ chdev       : [in] local pointer on NIC_RX chdev.
+///////////////////////////////////////////////////////////////////////////////////////////
+static void dev_nic_tx_handle_one_cmd( xptr_t    socket_xp,
+                                       uint8_t * k_buf,
+                                       chdev_t * chdev )
+{
+    socket_t  * socket_ptr;
+    cxy_t       socket_cxy;
+    xptr_t      client_xp;       // extended pointer on client thread  
+    thread_t  * client_ptr;
+    cxy_t       client_cxy;
+    sock_cmd_t  cmd;             // NIC command type
+    uint8_t   * buf;             // pointer on user buffer
+    uint32_t    len;             // user buffer length
+    uint32_t    todo;            // number of bytes not yet sent
+    uint32_t    socket_type;     // socket type (UDP/TCP)
+    uint32_t    socket_state;    // socket state        
+    xptr_t      socket_lock_xp;  // extended pointer on socket lock
+    xptr_t      socket_r2tq_xp;  // extended pointer on R2T queue
+    uint32_t    src_ip_addr;     // source IP address
+    uint32_t    dst_ip_addr;     // destination IP address
+    uint32_t    tx_una;          // next byte to be sent
+    uint32_t    tx_nxt;          // first unacknowledged byte
+    uint32_t    nbytes;          // number of bytes in UDP/TCP packet payload
+    uint8_t   * k_base;          // pointer UDP/TCP packet in kernel buffer
+    uint32_t    trsp_length;     // length of TCP/UDP packet
+    uint8_t     r2t_flags;       // flags defined by one R2T queue request
+    bool_t      do_send;         // build & send a packet when true
+  
+    // get socket cluster and local pointer
+    socket_cxy = GET_CXY( socket_xp );
+    socket_ptr = GET_PTR( socket_xp );
+    
+    // build extended pointer on socket lock and r2t queue
+    socket_lock_xp = XPTR( socket_cxy , &socket_ptr->lock );
+    socket_r2tq_xp = XPTR( socket_cxy , &socket_ptr->r2tq );
+
+    // 1. take lock protecting this socket
+    remote_rwlock_wr_acquire( socket_lock_xp );
+
+    // get pointers on TX client thread from socket
+    client_xp = hal_remote_l64( XPTR( socket_cxy , &socket_ptr->tx_client ));
+    client_cxy = GET_CXY( client_xp );
+    client_ptr = GET_PTR( client_xp );
+
+    // check valid command 
+    if( client_xp != XPTR_NULL )   // valid command found
+    {
+        // 2. get command arguments from socket
+        cmd  = hal_remote_l32( XPTR(socket_cxy , &socket_ptr->tx_cmd ));
+        buf  = hal_remote_lpt( XPTR(socket_cxy , &socket_ptr->tx_buf ));
+        len  = hal_remote_l32( XPTR(socket_cxy , &socket_ptr->tx_len ));
+        todo = hal_remote_l32( XPTR(socket_cxy , &socket_ptr->tx_todo ));
+        
+        // get socket type and state
+        socket_type  = hal_remote_l32( XPTR(socket_cxy , &socket_ptr->type ));
+        socket_state = hal_remote_l32( XPTR(socket_cxy , &socket_ptr->state ));
+
+        // 3. UDP : build UDP packet and update UDP socket state
+        if( socket_type == SOCK_DGRAM )        
+        {
+            if( socket_state == UDP_STATE_UNBOUND )
+            {
+                // report illegal command
+                dev_nic_tx_report_error( socket_xp, cmd, socket_state );
+
+                do_send = false;
+            }
+            else  // BOUND or CONNECT state
+            {
+                if( cmd == SOCKET_TX_SEND ) 
+                {
+                    // compute payload length
+                    nbytes = ( PAYLOAD_MAX_LEN < todo ) ? PAYLOAD_MAX_LEN : todo;
+
+                    // compute UDP packet base in kernel buffer
+                    k_base = k_buf + ETH_HEAD_LEN + IP_HEAD_LEN;
+
+                    // move payload to kernel buffer
+                    hal_copy_from_uspace( XPTR(local_cxy , k_base + UDP_HEAD_LEN ),
+                                          buf + (len - todo),
+                                          nbytes );
+                    // build UDP header 
+                    dev_nic_tx_build_udp_header( k_base,
+                                                 socket_xp,
+                                                 nbytes );
+
+                    // update "tx_todo" in socket descriptor 
+                    hal_remote_s32( XPTR(socket_cxy , socket_ptr->tx_todo),
+                                    todo - nbytes );
+
+                    // unblock client thread when SEND command completed
+                    if( nbytes == todo )
+                    {
+                        dev_nic_tx_report_success( socket_xp );
+                    }
+
+                    do_send = true;
+                }
+                else
+                {
+                    // report illegal command
+                    dev_nic_tx_report_error( socket_xp, cmd, socket_state );
+
+                    do_send = false;
+                }
+            }
+
+            // compute transport packet length
+            trsp_length = UDP_HEAD_LEN + nbytes;
+
+        }  // end UDP
+
+        // 3. TCP : build TCP segment and update TCP socket state
+        if( socket_type == SOCK_STREAM ) 
+        {
+            // extract one request from TCP socket R2T queue if queue non empty
+            if( remote_buf_status( socket_r2tq_xp ) )
+            {
+                remote_buf_get_to_kernel( socket_r2tq_xp , &r2t_flags , 1 );
+            }
+            else
+            {
+                r2t_flags = 0;
+            }
+
+            /////////////////////////////////////
+            if( socket_state == TCP_STATE_ESTAB )    // connected TCP socket
+            {
+                if( cmd == SOCKET_TX_SEND )          
+                {
+                    // get  "tx_nxt", and "tx_una" from socket descriptor
+                    tx_nxt = hal_remote_l32( XPTR(socket_cxy , &socket_ptr->tx_nxt ));
+                    tx_una = hal_remote_l32( XPTR(socket_cxy , &socket_ptr->tx_una ));
+
+                    // compute actual payload length
+                    nbytes = ( PAYLOAD_MAX_LEN < todo ) ? PAYLOAD_MAX_LEN : todo;
+
+                    // compute TCP segment base in kernel buffer
+                    k_base = k_buf + ETH_HEAD_LEN + IP_HEAD_LEN;
+
+                    // move payload to kernel buffer
+                    hal_copy_from_uspace( XPTR( local_cxy , k_base + TCP_HEAD_LEN ),
+                                          buf + (len - todo),
+                                          nbytes );
+
+                    // build TCP header 
+                    dev_nic_tx_build_tcp_header( k_base,
+                                                 socket_xp,
+                                                 nbytes,                       // payload
+                                                 TCP_FLAG_ACK | r2t_flags );   // flags
+
+                    // update "tx_todo" in socket descriptor
+                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_todo ),
+                                    todo - nbytes );
+
+                    // update "tx_nxt" in socket descriptor
+                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_nxt ),
+                                    tx_nxt + nbytes );
+
+                    // unblock client thread when SEND command completed
+                    if( (todo == 0) && (tx_nxt == tx_una) ) 
+                    {
+                        dev_nic_tx_report_success( socket_xp );
+                    }
+
+                    do_send = true;
+                }
+                else if( cmd == SOCKET_TX_CLOSE )          
+                {
+                    // build TCP FIN segment
+                    dev_nic_tx_build_tcp_header( k_base,
+                                                 socket_xp,
+                                                 0,                            // payload
+                                                 TCP_FLAG_FIN | r2t_flags );   // flags
+                    // update socket state
+                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ),
+                                    TCP_STATE_FIN_WAIT1 );
+
+                    do_send = true;
+                }
+                else  // cmd == CONNECT
+                {
+                    // report illegal command
+                    dev_nic_tx_report_error( socket_xp , cmd , socket_state );
+
+                    do_send = false;
+                }
+            }
+            //////////////////////////////////////////
+            else if( socket_state == TCP_STATE_BOUND )  // unconnected TCP socket
+            {
+                if ( cmd == SOCKET_TX_CONNECT )  
+                {
+                    // set socket.tx_nxt
+                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_nxt ),
+                                     TCP_ISS  );
+                     
+                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_nxt ), 0 );
+                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_wnd ),
+                                    NIC_RX_BUF_SIZE);
+                                     
+                    // build TCP SYN segment
+                    dev_nic_tx_build_tcp_header( k_base,
+                                                  socket_xp,
+                                                  0,                // payload
+                                                  TCP_FLAG_SYN );   // flags
+                    // update socket state
+                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ),
+                                     TCP_STATE_SYN_SENT );
+
+                    do_send = true;
+                }
+                else   // cmd == SEND / CLOSE
+                {
+                    // report illegal command
+                    dev_nic_tx_report_error( socket_xp, cmd, socket_state );
+
+                    do_send = false;
+                }
+            }
+            ///////////////////////////////////////////
+            else if( socket_state == TCP_STATE_LISTEN )  // server wait connect
+            {
+                if( cmd == SOCKET_TX_CONNECT )          
+                {
+                    // update socket.state
+                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ),
+                                    TCP_STATE_SYN_SENT );
+                    
+                    // set socket.tx_una  
+                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_una ),
+                                    TCP_ISS );
+
+                    // set socket.tx_nxt 
+                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_una ),
+                                    TCP_ISS + 1 );
+
+                    // build TCP SYN segment
+                    dev_nic_tx_build_tcp_header( k_base,
+                                                 socket_xp,
+                                                 0,                // payload
+                                                 TCP_FLAG_SYN );   // flags
+                    do_send = true;
+                }
+                else  // cmd == CLOSE / SEND
+                {
+                    // report illegal command
+                    dev_nic_tx_report_error( socket_xp, cmd, socket_state );
+
+                    do_send = false;
+                }
+            }
+            /////////////////////////////////////////////
+            else if( socket_state == TCP_STATE_SYN_RCVD )  // socket wait ACK
+            {
+                if( cmd == SOCKET_TX_CLOSE )          
+                {
+                    // build TCP FIN segment
+                    dev_nic_tx_build_tcp_header( k_base,
+                                                 socket_xp,
+                                                 0,                // payload
+                                                 TCP_FLAG_FIN );   // flags
+                    // update socket state 
+                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ),
+                                    TCP_STATE_FIN_WAIT1 );
+
+                    do_send = true;
+                }
+                else  // SEND / CONNECT
+                {
+                    // report illegal command
+                    dev_nic_tx_report_error( socket_xp, cmd, socket_state );
+
+                    do_send = false;
+                }
+            }
+            ////////////////////////////////////////////////
+            else if( socket_state == TCP_STATE_CLOSE_WAIT )  // wait local close()
+            {
+                if( cmd == SOCKET_TX_CLOSE )          
+                {
+                    // build TCP FIN segment
+                    dev_nic_tx_build_tcp_header( k_base,
+                                                 socket_xp,
+                                                 0,                // payload
+                                                 TCP_FLAG_FIN );   // flags
+                    // update socket state 
+                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ),
+                                    TCP_STATE_LAST_ACK );
+
+                    do_send = true;
+                }
+                else  // SEND / CONNECT
+                {
+                    // report illegal command
+                    dev_nic_tx_report_error( socket_xp, cmd, socket_state );
+
+                    do_send = false;
+                }
+            }
+            ////
+            else
+            {
+                    // report illegal command
+                    dev_nic_tx_report_error( socket_xp, cmd, socket_state );
+
+                    do_send = false;
+            }
+
+            // compute TCP segment length
+            trsp_length = TCP_HEAD_LEN + nbytes;
+        }
+    }  
+    else                        // no valid command found
+    {
+        if( socket_type == SOCK_DGRAM )    //  UDP socket
+        {
+            do_send = false;
+        }
+        else                               //  TCP socket
+        {
+            if( remote_buf_status( socket_r2tq_xp ) == 0 )  // R2T queue empty
+            {
+                do_send = false;
+            }
+            else                                // pending request in R2T queue
+            {
+                // get one request from R2T queue 
+                remote_buf_get_to_kernel( socket_r2tq_xp , &r2t_flags , 1 );
+
+                // build TCP header for an empty segment
+                dev_nic_tx_build_tcp_header( k_base,
+                                             socket_xp,
+                                             0,             // payload
+                                             r2t_flags );   // flags
+                do_send = true;
+            }          
+        }
+    }
+
+    // 4. release the lock protecting the socket
+    remote_rwlock_wr_release( socket_lock_xp );
+
+    // return if no packet to send
+    if( do_send == false ) return;
+
+    // 5. build IP header
+    dev_nic_tx_build_ip_header( k_buf + ETH_HEAD_LEN,
+                                src_ip_addr,
+                                dst_ip_addr,
+                                IP_HEAD_LEN + trsp_length );
+
+    // 6. build ETH header
+    dev_nic_tx_build_eth_header( k_buf,
+                                 (uint16_t)SRC_MAC_54,
+                                 (uint16_t)SRC_MAC_32,
+                                 (uint16_t)SRC_MAC_10,
+                                 (uint16_t)DST_MAC_54,
+                                 (uint16_t)DST_MAC_32,
+                                 (uint16_t)DST_MAC_10,
+                                 ETH_HEAD_LEN + IP_HEAD_LEN + trsp_length );
+
+    // 7. move packet to NIC_TX queue
+    dev_nic_tx_move_packet( chdev,
+                            k_buf,
+                            ETH_HEAD_LEN + IP_HEAD_LEN + trsp_length );
+
+}  // end dev_nic_tx_handle_one_cmd()
+
+/////////////////////////////////////////
+void dev_nic_tx_server( chdev_t * chdev )
+{
+    uint8_t       k_buf[NIC_KERNEL_BUF_SIZE];  // buffer for one packet
+
+    xptr_t        root_xp;         // extended pointer on clients list root
+    xptr_t        lock_xp;         // extended pointer on lock protecting this list
+    xptr_t        socket_xp;       // extended pointer on on client socket 
+    socket_t    * socket_ptr;
+    cxy_t         socket_cxy;
+    xptr_t        entry_xp;        // extended pointer on socket tx_list entry
+
+    thread_t * this = CURRENT_THREAD;
+
+// check chdev direction and type
+assert( (chdev->func == DEV_FUNC_NIC) && (chdev->is_rx == false) ,
+"illegal chdev type or direction" );
+
+// check thread can yield
+assert( (this->busylocks == 0),
+"cannot yield : busylocks = %d\n", this->busylocks );
+
+    // build extended pointer on client sockets lock & root 
+    lock_xp = XPTR( local_cxy , &chdev->wait_lock );
+    root_xp = XPTR( local_cxy , &chdev->wait_root );
+
+    while( 1 )  // TX server infinite loop
+    {
+        // take the lock protecting the client sockets queue
+        remote_busylock_acquire( lock_xp );
+
+        /////////////// block and deschedule if no clients
+        if( xlist_is_empty( root_xp ) == false )
+        {
+            // release the lock protecting the TX client sockets queue
+            remote_busylock_release( lock_xp );
+ 
+            // block and deschedule
+            thread_block( XPTR( local_cxy , this ) , THREAD_BLOCKED_CLIENT );
+            sched_yield( "waiting client" );
+        }
+        //////////////  
+        else
+        {
+            // get first client socket
+            socket_xp  = XLIST_FIRST( root_xp , socket_t , tx_list );
+            socket_cxy = GET_CXY( socket_xp );
+            socket_ptr = GET_PTR( socket_xp );
+ 
+            // build extended pointer on socket xlist_entry
+            entry_xp = XPTR( socket_cxy , &socket_ptr->tx_list );
+
+            // remove this socket from the waiting queue
+            xlist_unlink( entry_xp );
+
+            // release the lock protecting the client sockets queue
+            remote_busylock_release( lock_xp );
+
+            // handle this TX client
+            dev_nic_tx_handle_one_cmd( socket_xp,
+                                       k_buf,
+                                       chdev ); 
+
+            // take the lock protecting the client sockets queue
+            remote_busylock_acquire( lock_xp );
+
+            // add this socket in last position of queue
+            xlist_add_last( root_xp , entry_xp ); 
+
+            // release the lock protecting the client sockets queue
+            remote_busylock_release( lock_xp );
+        }
+    }   // end while
+}  // end dev_nic_tx_server()
+
+
Index: /trunk/kernel/devices/dev_nic.h
===================================================================
--- /trunk/kernel/devices/dev_nic.h	(revision 656)
+++ /trunk/kernel/devices/dev_nic.h	(revision 657)
@@ -2,5 +2,5 @@
  * dev_nic.h - NIC (Network Controler) generic device API definition.
  * 
- * Author  Alain Greiner    (2016,2017,2018)
+ * Author  Alain Greiner    (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -27,48 +27,111 @@
 #include <kernel_config.h>
 #include <hal_kernel_types.h>
+#include <remote_busylock.h>
+#include <remote_buf.h>
+#include <xlist.h>
+
+/****  Forward declarations  ****/
+
+struct chdev_s;
 
 /*****************************************************************************************
  *     Generic Network Interface Controler definition
  *
- * This device provide access to an external Gigabit Ethernet network controler.
- * It assume that the NIC hardware peripheral handles two packets queues for sent (TX)
- * and received (RX) packets. Packets are (Ethernet/IPV4).
+ * This device provides access to a generic Gigabit Ethernet network controler.
+ * It assumes that the NIC hardware peripheral handles two packets queues for sent (TX)
+ * and received (RX) packets.
+ *
+ * The supported protocols stack is : Ethernet / IPV4 / TCP or UDP
  * 
- * The NIC device is handling an (infinite) stream of packets to or from the network. 
+ * 1) hardware assumptions
+ *
+ * The NIC device is handling two (infinite) streams of packets to or from the network. 
  * It is the driver responsibility to move the RX packets from the NIC to the RX queue,
  * and the TX packets from the TX queue to the NIC.
  *
- * AS the RX and TX queues are independant, there is one NIC-RX device descriptor 
- * to handle RX packets, and another NIC-TX device descriptor to handle TX packets.
- * In order to improve throughput, the hardware NIC controller can optionnally implement
- * multiple channels:
- * - The RX channels are indexed by an hash key derived from the source IP address.
- * - The TX channels are indexed by an hash key derived from the destination IP address.
- * These 2*N devices, and 2*N associated server threads, are distributed in 2*N clusters.
- * The  2*N server threads implement the protocols stack. The RX server threads block
- * and deschedule when the RX queue is empty. The TX server stack block and deschedule
- * when the queue is full. 
- *
- * It is the driver responsibily to re-activate a blocked server thread when
- * the queue state is modified: not full for TX, or not empty for RX.
+ * AS the RX and TX queues are independant, there is one NIC_RX device descriptor 
+ * to handle RX packets, and another NIC_TX device descriptor to handle TX packets.
+ *
+ * In order to improve throughput, the NIC controller can implement multiple (N) channels.
+ * In this case, the channel index is defined by an hash function computed from the remote
+ * IP address and port. This index is computed by the hardware for an RX packet, and is 
+ * computed by the kernel for a TX packet, using a specific driver function. TODO ...
+ * The 2*N chdevs, and the associated server threads implementing the protocols stack,
+ * are distributed in 2*N different clusters.
+ *
+ * 2) User API
+ * 
+ * On the user side, ALMOS-MKH implements the POSIX socket API. 
+ * The kernel functions implementing the socket related syscalls are :
+ * - dev_nic_socket()    : create a local socket registered in process fd_array[].
+ * - dev_nic_bind()      : attach a local IP address and port to a local socket.
+ * - dev_nic_listen()    : local server makes a passive open.
+ * - dev_nic_connect()   : local client makes an active open to a remote server.
+ * - dev_nic_accept()    : local server accept a new remote client.
+ * - dev_nic_send()      : send data on a connected socket.
+ * - dev_nic_recv()      : receive data on a connected socket.
+ * - dev_nic_sendto()    : send a packet to a remote (IP address/port). 
+ * - dev_nic_recvfrom()  : receive a paket from a remote (IP address/port).
+ * - dev_nic_close()     : close a socket
+ *
+ * 3) TX stream
+ *
+ * The internal API between the client threads and the TX server thread defines
+ * the 3 following commands: 
+ *   . SOCKET_TX_CONNECT : request to execute the 3 steps TCP connection handshake.
+ *   . SOCKET_TX_SEND    : send data to a remote socket (UDP or TCP).
+ *   . SOCKET_TX_CLOSE   : request to execute the 3 steps TCP close handshake.
+ *
+ * - These 3 commands are blocking for the client thread that registers the command in the
+ *   socket descriptor, blocks on the BLOCKED_IO condition, and deschedules.
+ * - The TX server thread is acting as a multiplexer. It scans the list of attached sockets,
+ *   to handle all valid commands: one UDP packet or TCP segment per iteration.
+ *   It uses the user buffer defined by the client thread, and attached to socket descriptor,
+ *   as a retransmission buffer. It blocks and deschedules on the BLOCKED_CLIENT condition,
+ *   when there is no more active TX command registered in any socket. It is re-activated 
+ *   by the first client thread registering a new TX command in the socket descriptor.
+ *   It unblocks a client thread only when a command is fully completed. It signals errors
+ *   to the client thread using the tx_error field in socket descriptor.
+ *
+ * 4) RX stream
+ *
+ * The communication between the RX server thread and the client threads expecting data
+ * is done through receive buffers (one private buffer per socket) that are handled
+ * as single-writer / single reader-FIFOs, called rx_buf. 
+ * - The RX server thread is acting as a demultiplexor: it handle one TCP segment or UDP
+ *   packet per iteration, and register the data in the rx_buf of the socket matching
+ *   the packet. It simply discard all packets that does not match a registered socket.
+ *   When a client thread is registered in the socket descriptor, the RX server thread
+ *   unblocks this client thread as soon as there is data available in rx_buf.
+ *   It blocks and deschedules on the BLOCKED_ISR condition when there is no more packets
+ *   in the NIC_RX queue. It is unblocked by the hardware ISR.
+ * - The client thread simply access the rx_buf attached to socket descriptor, and consumes
+ *   the available data when the rx_buf is non empty. It blocks on the BLOCKED_IO condition,
+ *   and deschedules when the rx_buf is empty. 
+ *
+ * 5) R2T queue
+ *
+ * To implement the TCP "3 steps handshake" protocol, the RX server thread can directly
+ * request the associated TX server thread to send control packets in  the TX stream, 
+ * using a dedicate R2T (RX to TX) FIFO stored in the socket descriptor.
+ *
+ * 6) NIC driver API
+ *
+ * The generic NIC device "driver" API defines the following commands to the NIC driver:
+ * - READABLE : returns true if at least one RX paquet is available in RX queue.
+ * - WRITABLE : returns true if at least one empty slot is available in TX queue.
+ * - READ     : consume one packet from the RX queue.
+ * - WRITE    : produce one packet to the TX queue.
+ * All RX or TX paquets are sent or received in standard 2 Kbytes kernel buffers,
+ * that are dynamically allocated by the protocols stack. 
+ *
+ * The actual TX an RX queues structures depends on the hardware NIC implementation,
+ * and are defined in the HAL specific driver code. 
  *
  * WARNING: the WTI mailboxes used by the driver ro receive events from the hardware
  * (available RX packet, or available free TX slot, for a given channel), must be 
  * statically allocated during the kernel initialisation phase, and must be 
- * routed to the cluster containing the associated device descriptor and server thread.
- * to simplify the server thread re-activation.
- *
- * Finally, the generic NIC device API defines the following commands:
- * - READABLE : returns true if at least one RX paquet is available in RX queue.
- * - WRITABLE : returns true if atleast one empty slot is available in TX queue.
- * - READ     : consume one packet from the RX queue.
- * - WRITE    : produce one packet fto the TX queue.
- * All RX or TX paquets are sent or received in standard 2 Kbytes kernel buffers,
- * that are dynamically allocated by the protocols stack. The structure pkd_t
- * defining a packet descriptor is defined below, and contain the buffer pointer
- * and the actual Ethernet packet Length.
- *
- * The actual TX an RX queues structures depends on the hardware NIC implementation,
- * and are defined in the driver code. 
+ * routed to the cluster containing the associated TX/RX chdev and server thread.
+ *
  *****************************************************************************************/
 
@@ -78,32 +141,59 @@
 
 /******************************************************************************************
- * This defines the extension for the generic IOC device.
+ *   Various constants used by the Protocols stack
+ *****************************************************************************************/
+
+#define SRC_MAC_54             0x54
+#define SRC_MAC_32             0x32
+#define SRC_MAC_10             0x10
+#define DST_MAC_54             0x54
+#define DST_MAC_32             0x32
+#define DST_MAC_10             0x10
+
+#define TCP_HEAD_LEN           20
+#define UDP_HEAD_LEN           8
+#define IP_HEAD_LEN            20
+#define ETH_HEAD_LEN           14
+
+#define PROTOCOL_UDP           0x11
+#define PROTOCOL_TCP           0x06
+
+#define TCP_ISS                0x10000
+
+#define PAYLOAD_MAX_LEN        1500         // max payload for and UDP packet or a TCP segment
+
+#define TCP_FLAG_FIN           0x01
+#define TCP_FLAG_SYN           0x02
+#define TCP_FLAG_RST           0x04
+#define TCP_FLAG_PSH           0x08
+#define TCP_FLAG_ACK           0x10
+#define TCP_FLAG_URG           0x20
+
+#define NIC_RX_BUF_SIZE        0x100000     // 1 Mbytes
+#define NIC_R2T_QUEUE_SIZE     0x64         // smallest KCM size
+#define NIC_CRQ_QUEUE_SIZE     0x8          // 8 * sizeof(sockaddr_t) = smallest KCM size
+#define NIC_PKT_MAX_SIZE       1500         // for Ethernet
+#define NIC_KERNEL_BUF_SIZE    2000         // for on ETH/IP/TCP packet
+
+/*****************************************************************************************
+ * This defines the extension for the generic NIC device.
  * The actual queue descriptor depends on the implementation.
- *****************************************************************************************/
+ *
+ * WARNING : for all NIC_TX and NIC_RX chdevs, the xlist rooted in in the chdev
+ *           ("wait_root" and "wait_lock" fields) is actually a list of sockets.
+ ****************************************************************************************/
 
 typedef struct nic_extend_s
 {
-    void     * queue;     /*! local pointer on the packets queue descriptor (RX or TX)   */
+    void         * queue;       /*! local pointer on NIC queue descriptor (RX or TX)    */
 }
 nic_extend_t;
 
-/******************************************************************************************
- * This structure defines the Ethernet/IPV4 packet descriptor, that is sent to,
- * or received from, the protocols stack.
- *****************************************************************************************/
-
-typedef struct pkd_s
-{
-    char      * buffer;   /*! local pointer on 2 Kbytes buffer containing packet         */  
-    uint32_t    length;   /*! actual number of bytes                                     */
-}
-pkd_t;
-
-/******************************************************************************************
+/*****************************************************************************************
  * This enum defines the various implementations of the generic NIC peripheral.
  * This array must be kept consistent with the define in the arch_info.h file.
- *****************************************************************************************/
-
-enum nic_impl_e
+ ****************************************************************************************/
+
+typedef enum nic_impl_e
 {
     IMPL_NIC_CBF =   0,     
@@ -112,14 +202,17 @@
 nic_impl_t;
 
-/******************************************************************************************
- * This defines the (implementation independant) command  passed to the NIC driver.
- *****************************************************************************************/
+/****************************************************************************************
+ * This defines the (implementation independant) commands to access the NIC hardware.
+ * These commands are registered by the NIC_TX and NIC_RX server threads in the
+ * server thread descriptor, to be used by the NIC driver. 
+ * The buffer is always a 2K bytes kernel buffer, containing an Ethernet packet.
+ ****************************************************************************************/
 
 typedef enum nic_cmd_e
 {
-    NIC_CMD_WRITABLE = 0,  /*! test TX queue not full (for a given length packet)        */
-    NIC_CMD_WRITE    = 1,  /*! put one (given length) packet to TX queue                 */
-    NIC_CMD_READABLE = 2,  /*! test RX queue not empty (for any length packet)           */
-    NIC_CMD_READ     = 3,  /*! get one (any length) packet from RX queue                 */
+    NIC_CMD_WRITABLE  = 10,   /*! test TX queue not full (for a given packet length)    */
+    NIC_CMD_WRITE     = 11,   /*! put one (given length) packet to TX queue             */
+    NIC_CMD_READABLE  = 12,   /*! test RX queue not empty (for any packet length)       */
+    NIC_CMD_READ      = 13,   /*! get one (any length) packet from RX queue             */
 }
 nic_cmd_t;
@@ -127,12 +220,75 @@
 typedef struct nic_command_s
 {
-    xptr_t      dev_xp;   /*! extended pointer on device descriptor                      */
-    nic_cmd_t   cmd;      /*! requested operation type                                   */
-    char      * buffer;   /*! local pointer on 2 Kbytes buffer containing packet         */  
-    uint32_t    length;   /*! actual number of bytes                                     */
-    bool_t      status;   /*! return true if writable or readable (depend on command)    */
-    uint32_t    error;    /*! return an error from the hardware (0 if no error)          */
+    xptr_t      dev_xp;       /*! extended pointer on NIC chdev descriptor              */
+    nic_cmd_t   type;         /*! command type                                          */
+    uint8_t   * buffer;       /*! local pointer on buffer (kernel or user space)        */  
+    uint32_t    length;       /*! number of bytes in buffer                             */
+    uint32_t    status;       /*! return value (depends on command type)                */
+    uint32_t    error;        /*! return an error from the hardware (0 if no error)     */
 }
 nic_command_t;
+
+/***************************************************************************************** 
+ * This structure defines a socket descriptor. In order to parallelize the transfers,
+ * the set of all registered sockets is split in several subsets.
+ * The number of subsets is the number of  NIC channels. 
+ * The distribution key is computed from the (remote_addr/remote_port) couple.
+ * This computation is done by the NIC hardware for RX packets, 
+ * and by the dev_nic_connect() function for the TX packets.
+ *
+ * A socket is attached to the NIC_TX[channel] & NIC_RX[channel] chdevs.
+ * Each socket descriptor allows the TX and TX server threads to access various buffers:
+ * - the user "send" buffer contains the data to be send by the TX server thread.
+ * - the kernel "receive" buffer contains the data received by the RX server thread.
+ * - the kernel "r2t" buffer allows the RX server thread to make direct requests
+ *   to the associated TX server (to implement the TCP 3 steps handshake).
+ *
+ * The synchronisation mechanism between the clients threads and the servers threads
+ * is different for TX and RX transfers:
+ *
+ * 1) For a TX transfer, it can exist only one client thread for a given socket,
+ *    the transfer is always initiated by the local process, and all TX commands
+ *    (CONNECT/SEND/CLOSE) are blocking for the client thread. The user buffer is
+ *    used by TCP to handle retransmissions when required.in case of re
+ *    The client thread registers the command in the thread descriptor, registers itself
+ *    in the socket descriptor, unblocks the TX server thread from the BLOCKED_CLIENT 
+ *    condition, blocks itself on the BLOCKED_IO condition, and deschedules. 
+ *    When the command is completed, the TX server thread unblocks the client thread.
+ *    The TX server blocks itself on the BLOCKED_CLIENT condition, when there is no
+ *    pending commands and the R2T queue is empty. It is unblocked when a client
+ *    register a new command, or when the TX server thread register a mew request
+ *    in the R2T queue.
+ *    The tx_valid flip-flop is SET by the client thread to signal a valid command. 
+ *    It is RESET by the server thread when the command is completed: For a SEND, 
+ *    all bytes have been sent (UDP) or acknowledged (TCP).
+ *
+ * 2) For an RX transfer, it can exist only one client thread for a given socket,
+ *    but the transfer is initiated by the remote process, and the  RECV command
+ *    is not really blocking: the data can arrive before the local RECV command is
+ *    executed, and the server thread does not wait to receive all requested data
+ *    to deliver data to client thread. Therefore each socket contains a receive
+ *    buffer (rx_buf) handled as a single-writer/single-reader fifo.
+ *    The client thread consumes data from the rx_buf when possible. It blocks on the
+ *    BLOCKED_IO condition and deschedules when the rx_buf is empty.
+ *    It is unblocked by the RX server thread when new data is available in the rx_buf.
+ *    The RX server blocks itself on the BLOCKED_ISR condition When the NIC_RX packets
+ *    queue is empty. It is unblocked by the hardware when new packets are available.
+ * 
+ * Note : the socket domains and types are defined in the "shared_socket.h" file.
+ ****************************************************************************************/
+
+/******************************************************************************************
+ * This function returns a printable string for a given NIC command <type>.
+ ******************************************************************************************
+ * @ type    : NIC command type
+ *****************************************************************************************/
+char * nic_cmd_str( uint32_t type );
+   
+/******************************************************************************************
+ * This function returns a printable string for a given socket <state>.
+ ******************************************************************************************
+ * @ state    : socket state
+ *****************************************************************************************/
+char * socket_state_str( uint32_t state );
 
 /******************************************************************************************
@@ -143,4 +299,6 @@
  * device and the specific data structures when required.
  * It creates the associated server thread and allocates a WTI from local ICU.
+ * For a TX_NIC chedv, it allocates and initializes the R2T waiting queue used by the 
+ * NIC_RX[channel] server to send direct requests to the NIC_TX[channel] server.
  * It must de executed by a local thread.
  ******************************************************************************************
@@ -149,82 +307,266 @@
 void dev_nic_init( struct chdev_s * chdev );
 
-/******************************************************************************************
- * This blocking function must be called by the kernel thread running in the cluster
- * containing the NIC_RX channel device descriptor.
- * It read one packet (Ethernet/IPV4) from the NIC_RX queue associated to the NIC channel.
- * It calls directly the NIC driver, without registering in a waiting queue, because 
- * only this NIC_RX thread can access this packets queue.
- * 1) It test the packets queue status, using the NIC_CMD_WRITABLE command.
- *    If it is empty, it unmask the NIC-RX channel IRQ, blocks and deschedule.
- *    It is re-activated by the NIC-RX ISR (generated by the NIC) as soon as the queue
- *    becomes not empty.
- * 2) if the queue is not empty, it get one packet, using the driver NIC_CMD_READ command.
- * Both commands are successively registered in the NIC-RX server thread descriptor
- * to be passed to the driver. 
- *
- * WARNING : for a RX packet the initiator is the NIC hardware, and the protocols
- * stack is traversed upward, from the point of view of function calls.
- ******************************************************************************************
- * @ pkd     : pointer on packet descriptor (expected).
- * @ returns 0 if success / returns non zero if ENOMEM, or error reported from NIC.
- *****************************************************************************************/
-error_t dev_nic_read( pkd_t * pkd );
-
-/******************************************************************************************
- * This blocking function must be called by the kernel thread running in the cluster
- * containing the NIC_TX channel device descriptor.
- * It writes one packet (Ethernet/IPV4) to the NIC_RX queue associated to the NIC channel.
- * It calls directly the NIC driver, without registering in a waiting queue, because 
- * only this NIC_TX thread can access this packets queue.
- * 1) It test the packets queue status, using the NIC_CMD_READABLE command.
- *    If it is full, it unmask the NIC-TX channel IRQ, blocks and deschedule.
- *    It is re-activated by the NIC-TX ISR (generated by the NIC) as soon as the queue
- *    is not full.
- * 2) If the queue is not empty, it put one packet, using the driver NIC_CMD_WRITE command.
- * Both commands are successively registered in the NIC-TX server thread descriptor
- * to be passed to the driver. 
- *
- * WARNING : for a TX packet the initiator is the "client" thread, and the protocols
- * stack is traversed downward from the point of view of function calls.
- ******************************************************************************************
- * @ pkd     : pointer on packet descriptor (to be filed).
- * @ returns 0 if success / returns if length > 2K, undefined key, or error from NIC.
- *****************************************************************************************/
-error_t dev_nic_write( pkd_t * pkd );
-
-
-/******************************************************************************************
- * This function is executed by the server thread associated to a NIC channel device 
- * descriptor (RX or TX). This thread is created by the dev_nic_init() function.
- * It executes an infinite loop, handling one packet per iteration.
- *
- * -- For a TX channel --
- * 1) It allocates a 2 Kbytes buffer.
- * 2) It copies the client TCP/UDP packet in this buffer.
- * 3) It calls the IP layer to add the IP header.
- * 4) It calls the ETH layer to add the ETH header.
- * 5) It calls the dev_nic_write() blocking function to move the packet to the TX queue.
- * 6) It releases the 2 Kbytes buffer.
- *
- * When the waiting threads queue is empty, it blocks on the THREAD_BLOCKED_IO_CMD
- * condition and deschedule. It is re-activated by a client thread registering a command.
- *
- * -- For a RX channel --
- * 1) It allocates a 2 Kbytes buffer.
- * 2  It calls the dev_nic_read() blocking function to move the ETH packet to this buffer.
- * 3) It calls the ETH layer to analyse the ETH header.
- * 4) It calls the IP layer to analyse the IP header. TODO ???
- * 5) It calls the transport (TCP/UDP) layer. TODO ???
- * 5) It deliver the packet to the client thread. TODO ???
- * 6) It releases the 2 Kbytes buffer.
- *
- * When the RX packets queue is empty, it blocks on the THREAD_BLOCKED_IO_CMD
- * condition and deschedule. It is re-activated by the NIC driver when this queue
- * becomes non empty. 
- ******************************************************************************************
- * @ dev     : local pointer on NIC chdev descriptor.
- *****************************************************************************************/
-void dev_nic_server( struct chdev_s * chdev );
-
+
+/* functions implementing the socket API */
+
+/****************************************************************************************
+ * This function implements the socket() syscall.
+ * This function allocates and intializes in the calling thread cluster:
+ * - a new socket descriptor, defined by the <domain> and <type> arguments,
+ * - a new file descriptor, associated to this socket,
+ * It registers the file descriptor in the reference process fd_array[], set 
+ * the socket state to IDLE, and returns the <fdid> value.
+ ****************************************************************************************
+ * @ domain  : [in] socket protocol family (AF_UNIX / AF_INET)
+ * @ type    : [in] socket type (SOCK_DGRAM / SOCK_STREAM).
+ * @ return a file descriptor <fdid> if success / return -1 if failure.
+***************************************************************************************/
+int dev_nic_socket( uint32_t   domain,
+                    uint32_t   type );
+
+/****************************************************************************************
+ * This function implements the bind() syscall.
+ * It initializes the  "local_addr" and "local_port" fields in the socket
+ * descriptor identified by the <fdid> argument and set the socket state to BOUND.
+ * It can be called by a thread running in any cluster.
+ ****************************************************************************************
+ * @ fdid      : [in] file descriptor identifying the socket.
+ * @ addr      : [in] local IP address.
+ * @ port      : [in] local port.
+ * @ return 0 if success / return -1 if failure.
+ ***************************************************************************************/
+int dev_nic_bind( uint32_t fdid,
+                  uint32_t addr,
+                  uint16_t port );
+
+/****************************************************************************************
+ * This function implements the listen() syscall().
+ * It is called by a (local) server process to specify the max size of the queue
+ * registering the (remote) client process connections, and set the socket identified
+ * by the <fdid> argument to LISTEN state. It applies only to sockets of type TCP.
+ * It can be called by a thread running in any cluster.
+ * TODO handle the <max_pending> argument...
+ ****************************************************************************************
+ * @ fdid        : [in] file descriptor identifying the local server socket.
+ * @ max_pending : [in] max number of accepted remote client connections.
+ ***************************************************************************************/
+int dev_nic_listen( uint32_t fdid,
+                    uint32_t max_pending );
+
+/****************************************************************************************
+ * This function implements the connect() syscall.
+ * It is used by a (local) client process to connect a local socket identified by 
+ * the <fdid> argument, to a remote socket identified by the <remote_addr> and 
+ * <remote_port> arguments. It can be used for both  UDP and TCP sockets. 
+ * It computes the nic_channel index from <remote_addr> and <remote_port> values,
+ * and initializes "remote_addr","remote_port", "nic_channel" in local socket.
+ * It registers the socket in the two lists of clients rooted in the NIC_RX[channel] 
+ * and NIC_TX[channel] chdevs.  It can be called by a thread running in any cluster.
+ * WARNING : the clients are the socket descriptors, and NOT the threads descriptors.
+ ****************************************************************************************
+ * Implementation Note:
+ * - For a TCP socket, it updates the "remote_addr", "remote_port", "nic_channel" fields
+ *   in the socket descriptor defined by the <fdid> argument, and register this socket,
+ *   in the lists of sockets attached to the NIC_TX and NIC_RX chdevs.
+ *   Then, it registers a CONNECT command in the "nic_cmd" field ot the client thread
+ *   descriptor to request the NIC_TX server thread to execute the 3 steps handshake,
+ *   and updates the "tx_client" field in the socket descriptor. It unblocks the NIC_TX 
+ *   server thread, blocks on the THREAD_BLOCKED_IO condition and deschedules.
+ * - For an UDP socket, it simply updates "remote_addr", "remote_port", "nic_channel"
+ *   in the socket descriptor defined by the <fdid> argument, and register this socket,
+ *   in the lists of sockets attached to the NIC_TX and NIC_RX chdevs.
+ *   Then, it set the socket state to CONNECT, without unblocking the NIC_TX server 
+ *   thread, and without blocking itself.
+ * TODO : the nic_channel index computation must be done by a driver specific function.
+ ****************************************************************************************
+ * @ fdid          : [in] file descriptor identifying the socket.
+ * @ remote_addr   : [in] remote IP address.
+ * @ remote_port   : [in] remote port.
+ * @ return 0 if success / return -1 if failure.
+ ***************************************************************************************/
+int dev_nic_connect( uint32_t  fdid,
+                     uint32_t  remote_addr,
+                     uint16_t  remote_port );
+
+/****************************************************************************************
+ * This function implements the accept() syscall().
+ * It is executed by a server process, waiting for one (or several) client process(es)
+ * requesting a connection on a socket identified by the <fdid> argument.
+ * This socket was previouly created with socket(), bound to a local address with bind(), 
+ * and is listening for connections after a listen().
+ * This function extracts the first connection request on the CRQQ queue of pending
+ * requests, creates a new socket with the same properties as the existing socket,
+ * and allocates a new file descriptor for this new socket.
+ * If no pending connections are present on the queue, it blocks the caller until a 
+ * connection is present. 
+ * The new socket cannot accept more connections, but the original socket remains open.
+ * It returns the new socket <fdid>, and register in the <address> an <port> arguments
+ * the remote client IP address & port. It applies only to sockets of type SOCK_STREAM.
+ ****************************************************************************************
+ * @ fdid         : [in] file descriptor identifying the listening socket.
+ * @ address      : [out] server IP address.
+ * @ port         : [out] server port address length in bytes.
+ * @ return the new socket <fdid> if success / return -1 if failure
+ ***************************************************************************************/
+int dev_nic_accept( uint32_t   fdid,
+                    uint32_t * address,
+                    uint16_t * port );
+
+/****************************************************************************************
+ * This blocking function implements the send() syscall.
+ * It is used to send data stored in the user buffer, identified the <u_buf> and <length>
+ * arguments, to a connected (TCP or UDP) socket, identified by the <fdid> argument.
+ * The work is actually done by the NIC_TX server thread, and the synchronisation
+ * between the client and the server threads uses the "rx_valid" set/reset flip-flop:
+ * The client thread registers itself in the socket descriptor, registers in the queue 
+ * rooted in the NIC_TX[index] chdev, set "rx_valid", unblocks the server thread, and 
+ * finally blocks on THREAD_BLOCKED_IO, and deschedules. 
+ * When the TX server thread completes the command (all data has been sent for an UDP
+ * socket, or acknowledeged for a TCP socket), the server thread reset "rx_valid" and
+ * unblocks the client thread.
+ * This function can be called by a thread running in any cluster.
+ * WARNING : This implementation does not support several concurent SEND/SENDTO commands
+ * on the same socket, as only one TX thread can register in a given socket.
+ ****************************************************************************************
+ * @ fdid      : [in] file descriptor identifying the socket.
+ * @ u_buf     : [in] pointer on buffer containing packet in user space.
+ * @ length    : [in] packet size in bytes.
+ * @ return number of sent bytes if success / return -1 if failure.
+ ***************************************************************************************/
+int dev_nic_send( uint32_t    fdid,
+                  uint8_t   * u_buf,
+                  uint32_t    length );
+
+/****************************************************************************************
+ * This blocking function implements the sendto() syscall. 
+ * It registers the <remote_addr> and <remote_port> arguments in the local socket
+ * descriptor, and does the same thing as the dev_nic_send() function above, 
+ * but can be called  on an unconnected UDP socket.
+ ****************************************************************************************
+ * @ fdid        : [in] file descriptor identifying the socket.
+ * @ u_buf       : [in] pointer on buffer containing packet in user space.
+ * @ length      : [in] packet size in bytes.
+ * @ remote_addr : [in] destination IP address.
+ * @ remote_port : [in] destination port.
+ * @ return number of sent bytes if success / return -1 if failure.
+ ***************************************************************************************/
+int dev_nic_sendto( uint32_t    fdid,
+                    uint8_t   * u_buf,
+                    uint32_t    length,
+                    uint32_t    remote_addr,
+                    uint32_t    remote_port );
+
+/****************************************************************************************
+ * This blocking function implements the recv() syscall.
+ * It is used to receive data that has been stored by the NIC_RX server thread in the
+ * rx_buf of a connected (TCP or UDP) socket, identified by the <fdid> argument.
+ * The synchronisation between the client and the server threads uses the "rx_valid"
+ * set/reset flip-flop: If "rx_valid" is set, the client simply moves the available
+ * data from the "rx_buf" to the user buffer identified by the <u_buf> and <length>
+ * arguments, and reset the "rx_valid" flip_flop. If "rx_valid" is not set, the client
+ * thread register itself in the socket descriptor, registers in the clients queue rooted
+ * in the NIC_RX[index] chdev, and finally blocks on THREAD_BLOCKED_IO, and deschedules. 
+ * The client thread is re-activated by the RX server, that set the "rx_valid" flip-flop 
+ * as soon as data is available in the "rcv_buf" (can be less than the user buffer size).
+ * This  function can be called by a thread running in any cluster.
+ * WARNING : This implementation does not support several concurent RECV/RECVFROM 
+ * commands on the same socket, as only one RX thread can register in a given socket.
+ ****************************************************************************************
+ * @ fdid      : [in] file descriptor identifying the socket.
+ * @ u_buf     : [in] pointer on buffer in user space.
+ * @ length    : [in] buffer size in bytes.
+ * @ return number of received bytes if success / return -1 if failure.
+ ***************************************************************************************/
+int dev_nic_recv( uint32_t    fdid,
+                  uint8_t   * u_buf,
+                  uint32_t    length );
+
+/****************************************************************************************
+ * This blocking function implements the recvfrom() syscall.
+ * It registers the <remote_addr> and <remote_port> arguments in the local socket
+ * descriptor, and does the same thing as the dev_nic_recv() function above, 
+ * but can be called  on an unconnected UDP socket.
+ ****************************************************************************************
+ * @ fdid        : [in] file descriptor identifying the socket.
+ * @ u_buf       : [in] pointer on buffer containing packet in user space.
+ * @ length      : [in] packet size in bytes.
+ * @ remote_addr : [in] destination IP address.
+ * @ remote_port : [in] destination port.
+ * @ return number of received bytes if success / return -1 if failure.
+ ***************************************************************************************/
+int dev_nic_recvfrom( uint32_t    fdid,
+                      uint8_t   * u_buf,
+                      uint32_t    length,
+                      uint32_t    remote_addr,
+                      uint32_t    remote_port );
+
+
+/*  Instrumentation functions */
+
+
+/******************************************************************************************
+ * This instrumentation function displays on the TXT0 kernel terminal the content
+ * of the instrumentation registers contained in the NIC device.
+ *****************************************************************************************/
+void dev_nic_print_stats( void );
+
+/******************************************************************************************
+ * This instrumentation function reset all instrumentation registers contained
+ * in the NIC device.
+ *****************************************************************************************/
+void dev_nic_clear_stats( void );
+
+
+/* Functions executed by the TX and RX server threads */ 
+ 
+/******************************************************************************************
+ * This function is executed by the server thread associated to a NIC_TX[channel] chdev. 
+ * This TX server thread is created by the dev_nic_init() function.
+ * It build and send UDP packets or TCP segments for all clients threads registered in
+ * the NIC_TX[channel] chdev. The command types are (CONNECT / SEND / CLOSE), and the
+ * priority between clients is round-robin. It takes into account the request registered
+ * by the RX server thread in the R2T queue associated to the involved socket.
+ * When a command is completed, it unblocks the client thread. For a SEND command, the 
+ * last byte must have been sent for an UDP socket, and it must have been acknowledged
+ * for a TCP socket.
+ * When the TX client threads queue is empty, it blocks on THREAD_BLOCKED_CLIENT
+ * condition and deschedules. It is re-activated by a client thread registering a command.
+ ******************************************************************************************
+ * Implementation note:
+ * It execute an infinite loop in which it takes the lock protecting the clients list
+ * to build a "kleenex" list of currently registered clients.
+ * For each client registered in this "kleenex" list, it takes the lock protecting the
+ * socket state, build one packet/segment in a local 2K bytes kernel buffer, calls the 
+ * transport layer to add the UDP/TCP header, calls the IP layer to add the IP header, 
+ * calls the ETH layer to add the ETH header, and moves the packet to the NIC_TX_QUEUE.
+ * Finally, it updates the socket state, and release the socket lock.
+ ******************************************************************************************
+ * @ chdev    : [in] local pointer on one local NIC_TX[channel] chdev descriptor.
+ *****************************************************************************************/
+void dev_nic_tx_server( struct chdev_s * chdev );
+
+
+/******************************************************************************************
+ * This function is executed by the server thread associated to a NIC_RX[channel] chdev. 
+ * This RX server thread is created by the dev_nic_init() function.
+ * It handles all UDP packets or TCP segments received by the sockets attached to 
+ * the NIC_RX[channel] chdev. It writes the received data in the socket rcv_buf, and
+ * unblocks the client thread waiting on a RECV command.
+ * To implement the three steps handshahke required by a TCP connection, it posts direct
+ * requests to the TX server, using the R2T queue attached to the involved socket. 
+ * It blocks on the THREAD_BLOCKED_ISR condition and deschedules when the NIC_RX_QUEUE
+ * is empty. It is re-activated by the NIC_RX_ISR, when the queue becomes non empty.
+ ******************************************************************************************
+ * Implementation note:
+ * It executes an infinite loop in which it extracts one packet from the NIC_RX_QUEUE
+ * of received packets, copies this packet in a local 2 kbytes kernel buffer, checks 
+ * the Ethernet header, checks the IP header, calls the relevant (TCP or UDP) transport
+ * protocol that search a matching socket for the received packet. It copies the payload 
+ * to the relevant socket rcv_buf when the packet is acceptable, and unblocks the client
+ * thread. It discard the packet if no socket found.
+ ******************************************************************************************
+ * @ chdev    : [in] local pointer on one local NIC_RX[channel] chdev descriptor.
+ *****************************************************************************************/
+void dev_nic_rx_server( struct chdev_s * chdev );
 
 #endif	/* _DEV_NIC_H */
Index: /trunk/kernel/devices/dev_txt.c
===================================================================
--- /trunk/kernel/devices/dev_txt.c	(revision 656)
+++ /trunk/kernel/devices/dev_txt.c	(revision 657)
@@ -133,40 +133,4 @@
 }  // end dev_txt_init()
 
-//////////////////////////////////////////////////////////////////////////////////
-// This static function is called by dev_txt_read(), dev_txt_write() functions.
-////////////////////////////////////i/////////////////////////////////////////////
-static error_t dev_txt_access( uint32_t   type,
-                               uint32_t   channel,
-                               char     * buffer,
-                               uint32_t   count )
-{
-    xptr_t     dev_xp;
-    thread_t * this = CURRENT_THREAD;
-
-    // check channel argument
-    assert( (channel < CONFIG_MAX_TXT_CHANNELS) , "illegal channel index" );
-
-    // get extended pointer on remote TXT chdev descriptor
-    if( type == TXT_WRITE )  dev_xp = chdev_dir.txt_tx[channel];
-    else                     dev_xp = chdev_dir.txt_rx[channel];
-
-    assert( (dev_xp != XPTR_NULL) , "undefined TXT chdev descriptor" );
-
-    // register command in calling thread descriptor
-    this->txt_cmd.dev_xp  = dev_xp;
-    this->txt_cmd.type    = type;
-    this->txt_cmd.buf_xp  = XPTR( local_cxy , buffer );
-    this->txt_cmd.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.
-    chdev_register_command( dev_xp );
-
-    // return I/O operation status from calling thread descriptor
-    return this->txt_cmd.error;
-
-}  // end dev_txt_access()
-
 /////////////////////////////////////////
 error_t dev_txt_write( uint32_t   channel,
@@ -180,6 +144,7 @@
 #endif
 
+    thread_t * this = CURRENT_THREAD;
+
 #if DEBUG_DEV_TXT_TX
-thread_t * this  = CURRENT_THREAD;
 uint32_t   cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_DEV_TXT_TX < cycle )
@@ -188,14 +153,20 @@
 #endif
 
+// check channel argument
+assert( (channel < CONFIG_MAX_TXT_CHANNELS) , "illegal channel index" );
+
+    // get pointers on chdev
+    xptr_t    dev_xp  = chdev_dir.txt_tx[channel];
+    cxy_t     dev_cxy = GET_CXY( dev_xp );
+    chdev_t * dev_ptr = GET_PTR( dev_xp );
+
+// check dev_xp 
+assert( (dev_xp != XPTR_NULL) , "undefined TXT chdev descriptor" );
+
     // If we use MTTY (vci_multi_tty), we do a synchronous write on TXT[0]
     // If we use TTY  (vci_tty_tsar), we do a standard asynchronous write
     // TODO this is not very clean ... [AG]
 
-    // get pointers on chdev
-    xptr_t dev_xp = chdev_dir.txt_tx[0];
-    cxy_t     dev_cxy = GET_CXY( dev_xp );
-    chdev_t * dev_ptr = GET_PTR( dev_xp );
-
-    if( dev_ptr->impl == IMPL_TXT_MTY )
+    if( dev_ptr->impl == IMPL_TXT_MTY )  
     {
         // get driver command function
@@ -216,6 +187,17 @@
     else
     {
-        // register command in chdev queue for an asynchronous access
-        error = dev_txt_access( TXT_WRITE , channel , buffer , count );
+        // register command in calling thread descriptor
+        this->txt_cmd.dev_xp  = dev_xp;
+        this->txt_cmd.type    = TXT_WRITE;
+        this->txt_cmd.buf_xp  = XPTR( local_cxy , buffer );
+        this->txt_cmd.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.
+        chdev_register_command( dev_xp );
+
+        // get I/O operation status from calling thread descriptor
+        error = this->txt_cmd.error;
 
         if( error )
@@ -251,6 +233,7 @@
 #endif
 
+    thread_t * this = CURRENT_THREAD;
+
 #if DEBUG_DEV_TXT_RX
-thread_t * this  = CURRENT_THREAD;
 uint32_t   cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_DEV_TXT_RX < cycle )
@@ -259,11 +242,31 @@
 #endif
 
-    // register command in chdev queue for an asynchronous access
-    error = dev_txt_access( TXT_READ , channel , buffer , 1 );
+// check channel argument
+assert( (channel < CONFIG_MAX_TXT_CHANNELS) , "illegal channel index" );
+
+    // get pointers on chdev
+    xptr_t    dev_xp  = chdev_dir.txt_rx[channel];
+
+// check dev_xp 
+assert( (dev_xp != XPTR_NULL) , "undefined TXT chdev descriptor" );
+
+    // register command in calling thread descriptor
+    this->txt_cmd.dev_xp  = dev_xp;
+    this->txt_cmd.type    = TXT_READ;
+    this->txt_cmd.buf_xp  = XPTR( local_cxy , buffer );
+    this->txt_cmd.count   = 1;
+
+    // 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.
+    chdev_register_command( dev_xp );
+
+    // get I/O operation status from calling thread descriptor
+    error = this->txt_cmd.error;
 
     if( error )
     {
-        printk("\n[ERROR] in %s : cannot get character / cycle %d\n",
-        __FUNCTION__, (uint32_t)hal_get_cycles() );
+         printk("\n[ERROR] in %s : cannot write string %s / cycle %d\n",
+         __FUNCTION__, buffer, (uint32_t)hal_get_cycles() );
     }
 
Index: /trunk/kernel/fs/devfs.c
===================================================================
--- /trunk/kernel/fs/devfs.c	(revision 656)
+++ /trunk/kernel/fs/devfs.c	(revision 657)
@@ -3,5 +3,5 @@
  *
  * Author   Mohamed Lamine Karaoui (2014,2015)
- *          Alain Greiner (2016,2017,2018,2019)
+ *          Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c) Sorbonne Universites
@@ -53,6 +53,6 @@
 #endif
 
-/////////////////////////////////////
-devfs_ctx_t * devfs_ctx_alloc( void )
+///////////////////////////////////
+xptr_t devfs_ctx_alloc( cxy_t cxy )
 {
     kmem_req_t    req;
@@ -62,26 +62,43 @@
     req.flags   = AF_KERNEL | AF_ZERO;
 
-	return kmem_alloc( &req );
+    // allocates devfs context from target cluster 
+	return XPTR( cxy , kmem_remote_alloc( cxy , &req ) );
 }
 
 /////////////////////////////////////////////
-void devfs_ctx_init( devfs_ctx_t * devfs_ctx,
-                     xptr_t        devfs_dev_inode_xp,
-                     xptr_t        devfs_external_inode_xp )
+void devfs_ctx_init( xptr_t     devfs_ctx_xp,
+                     xptr_t     devfs_dev_inode_xp,
+                     xptr_t     devfs_external_inode_xp )
 {
-    devfs_ctx->dev_inode_xp      = devfs_dev_inode_xp;
-    devfs_ctx->external_inode_xp = devfs_external_inode_xp;
-
-    fs_context[FS_TYPE_DEVFS].extend = devfs_ctx;
+    // get cluster and local pointer on remote devfs context
+    devfs_ctx_t * devfs_ctx_ptr = GET_PTR( devfs_ctx_xp );
+    cxy_t         devfs_ctx_cxy = GET_CXY( devfs_ctx_xp );
+
+    // set values in remote devfs context
+    hal_remote_s64( XPTR( devfs_ctx_cxy  , &devfs_ctx_ptr->dev_inode_xp ),
+                    devfs_dev_inode_xp );
+
+    hal_remote_s64( XPTR( devfs_ctx_cxy  , &devfs_ctx_ptr->external_inode_xp ),
+                    devfs_external_inode_xp );
+
+    // register devfs context in the remote fs_context array[] 
+    hal_remote_spt( XPTR( devfs_ctx_cxy , &fs_context[FS_TYPE_DEVFS].extend ),
+                          devfs_ctx_ptr ); 
 }
 
-/////////////////////////////////////////////////
-void devfs_ctx_destroy( devfs_ctx_t * devfs_ctx )
+//////////////////////////////////////////////
+void devfs_ctx_destroy( xptr_t  devfs_ctx_xp )
 {
     kmem_req_t    req;
 
+    // get cluster and local pointer on devfs context
+    devfs_ctx_t * devfs_ctx_ptr = GET_PTR( devfs_ctx_xp );
+    cxy_t         devfs_ctx_cxy = GET_CXY( devfs_ctx_xp );
+
     req.type = KMEM_KCM;
-    req.ptr  = devfs_ctx;
-    kmem_free( &req );
+    req.ptr  = devfs_ctx_ptr;
+
+    // release devfs context descriptor to remote cluster 
+    kmem_remote_free( devfs_ctx_cxy , &req );
 }
 
@@ -95,4 +112,6 @@
     vfs_inode_t * inode;
 
+    // get 
+
     // create DEVFS "dev" inode in cluster 0
     error = vfs_add_child_in_parent( 0,                // cxy
@@ -158,7 +177,8 @@
 ///////////////////////////////////////////////////
 void devfs_local_init( xptr_t   devfs_dev_inode_xp,
-                       xptr_t   devfs_external_inode_xp,
-                       xptr_t * devfs_internal_inode_xp )
+                       xptr_t   devfs_external_inode_xp )
 {
+    xptr_t        internal_inode_xp;   // extended pointer on <internal> inode
+    vfs_inode_t * internal_inode_ptr;  // local pointer on <internal> inode
     char          node_name[16];
     xptr_t        chdev_xp;
@@ -168,6 +188,7 @@
     vfs_inode_t * inode_ptr;
     uint32_t      channel;
-    xptr_t        unused_xp;    // required by add_child_in_parent()
+    xptr_t        unused_xp;           // required by add_child_in_parent()
     error_t       error;
+    
 
 #if DEBUG_DEVFS_LOCAL_INIT 
@@ -179,20 +200,20 @@
 #endif
 
-    // create "internal" directory 
+    // create <internal> directory 
     snprintf( node_name , 16 , "internal_%x" , local_cxy );
 
-    error = vfs_add_child_in_parent( local_cxy,
-                                     FS_TYPE_DEVFS,
-                                     devfs_dev_inode_xp,
-                                     node_name,
+    error = vfs_add_child_in_parent( local_cxy,              // target cluster
+                                     FS_TYPE_DEVFS,          // FS type
+                                     devfs_dev_inode_xp,     // parent inode
+                                     node_name,              // child name
                                      &unused_xp,
-                                     devfs_internal_inode_xp );
-
-    // set inode "type" field
-    inode_ptr = GET_PTR( *devfs_internal_inode_xp );
-    inode_ptr->type = INODE_TYPE_DEV;
+                                     &internal_inode_xp );   // child inode
+
+    // set <internal> inode "type" field
+    internal_inode_ptr       = GET_PTR( internal_inode_xp );
+    internal_inode_ptr->type = INODE_TYPE_DEV;
  
     // create dentries <.> and <..> in <internal>
-    error |= vfs_add_special_dentries( *devfs_internal_inode_xp,
+    error |= vfs_add_special_dentries( internal_inode_xp,
                                        devfs_dev_inode_xp );
 
@@ -226,5 +247,5 @@
         error = vfs_add_child_in_parent( local_cxy,
                                          FS_TYPE_DEVFS,
-                                         *devfs_internal_inode_xp,
+                                         internal_inode_xp,
                                          chdev_ptr->name,
                                          &unused_xp,
@@ -270,5 +291,5 @@
             error = vfs_add_child_in_parent( local_cxy,
                                              FS_TYPE_DEVFS,
-                                             *devfs_internal_inode_xp,
+                                             internal_inode_xp,
                                              chdev_ptr->name,
                                              &unused_xp,
Index: /trunk/kernel/fs/devfs.h
===================================================================
--- /trunk/kernel/fs/devfs.h	(revision 656)
+++ /trunk/kernel/fs/devfs.h	(revision 657)
@@ -3,5 +3,5 @@
  *
  * Authors       Mohamed Lamine Karaoui (2014,2015)
- *               Alain Greiner (2016,2017)
+ *               Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c) 2011,2012 UPMC Sorbonne Universites
@@ -26,7 +26,8 @@
 #define _DEVFS_H_
 
+
 //////////////////////////////////////////////////////////////////////////////////////////
-// The DEVFS File System contains inodes and dentries associated to all chdev descriptors 
-// availables in the architecture. 
+// The DEVFS File System contains the inodes and dentries associated to the chdev
+// descriptors available in the architecture. 
 //
 // It is structured as a three levels tree structure :
@@ -41,8 +42,6 @@
 //
 // The DEVFS extensions to the generic VFS are the following:
-// 1) The vfs_ctx_t "extend" void* field is pointing on the devfs_ctx_t structure.
-//    This structure contains two extended pointers on the DEVFS "dev" directory inode,
-//    and on the "external" directory inode.
-// 2) The vfs_inode_t "extend" void* field is pointing on the chdev descriptor.
+// 1) The vfs_ctx_t "extend" field is pointing on the devfs_ctx_t structure.
+// 2) The vfs_inode_t "extend" field is pointing on the chdev descriptor.
 //////////////////////////////////////////////////////////////////////////////////////////
 
@@ -57,12 +56,12 @@
 }
 devfs_ctx_t;
-
-
 /*****************************************************************************************
- * This fuction allocates memory from local cluster for a DEVFS context descriptor.
+ * This fuction allocates memory for a DEVFS context descriptor in cluster identified
+ * by the <cxy> argument.
  *****************************************************************************************
- * @ return a pointer on the created context / return NULL if failure.
+ * @ cxy     : [in] target cluster identifier.
+ * @ return an extended pointer on the created context / return NULL if failure.
  ****************************************************************************************/
-devfs_ctx_t * devfs_ctx_alloc( void );
+xptr_t  devfs_ctx_alloc( cxy_t cxy );
 
 /*****************************************************************************************
@@ -70,11 +69,11 @@
  * to the relevant VFS context in the local cluster.
  *****************************************************************************************
- * @ devfs_ctx               : local pointer on DEVFS context.
+ * @ devfs_ctx               : [in]  extended pointer on DEVFS context.
  * @ devfs_dev_inode_xp      : [out] extended pointer on  <dev> inode.
  * @ devfs_external_inode_xp : [out] extended pointer on  <external> inode.
  ****************************************************************************************/
-void devfs_ctx_init( devfs_ctx_t * devfs_ctx,
-                     xptr_t        devfs_dev_inode_xp,
-                     xptr_t        devfs_external_inode_xp );
+void devfs_ctx_init( xptr_t    devfs_ctx_xp,
+                     xptr_t    devfs_dev_inode_xp,
+                     xptr_t    devfs_external_inode_xp );
 
 /*****************************************************************************************
@@ -83,14 +82,13 @@
  * @ devfs_ctx   : local pointer on DEVFS context.
  ****************************************************************************************/
-void devfs_ctx_destroy( devfs_ctx_t * devfs_ctx );
+void devfs_ctx_destroy( xptr_t  devfs_ctx_xp );
 
 /*****************************************************************************************
- * This function start to create the DEVFS subtree. 
- * This function should be called once in the cluster containing the VFS parent inode.
- * More precisely, it creates in cluster 0 the "dev" and "external" DEVFS directories.
- * For each one, it creates the inode and link the associated dentry to parent inode.
- * The DEVFS root inode is linked to the VFS parent inode identified by <parent_inode_xp>.
+ * This function starts to create the DEVFS subtree. 
+ * This function creates in cluster 0 the "dev" and "external" DEVFS directories.
+ * For each one, it creates the inode and the associated dentry. The DEVFS root inode
+ * <dev is linked to the VFS parent inode identified by <parent_inode_xp>.
  *****************************************************************************************
- * @ parent_inode_xp         : extended pointer on the parent VFS inode.
+ * @ parent_inode_xp         : [in]  extended pointer on the parent VFS inode.
  * @ devfs_dev_inode_xp      : [out] extended pointer on created <dev> inode.
  * @ devfs_external_inode_xp : [out] extended pointer on created <external> inode.
@@ -105,16 +103,14 @@
  * 1. In each cluster (i), it creates the "internal" directory, 
  *    linked to the DEVFS "dev" parent directory.
- * 2. In each cluster (i), it creates - for each external chdev in cluster (i) - 
+ * 2. In each cluster (i), it creates, for each external chdev in cluster (i), 
  *    a pseudo-file, linked to the DEVFS "external" parent directory.
- * 3. In each cluster (i), it creates - for each internal chdev in cluster (i) -
+ * 3. In each cluster (i), it creates, for each internal chdev in cluster (i),
  *    a pseudo-file, linked to the DEVFS "internal" parent directory.
  *****************************************************************************************
- * @ devfs_dev_inode_xp      : extended pointer on DEVFS root inode.
- * @ devfs_external_inode_xp : extended pointer on DEVFS external inode.
- * @ devfs_internal_inode_xp : [out] extended pointer on created <internal> inode.
+ * @ devfs_dev_inode_xp      : [in]  extended pointer on DEVFS root inode.
+ * @ devfs_external_inode_xp : [in]  extended pointer on DEVFS external inode.
  ****************************************************************************************/
 void devfs_local_init( xptr_t   devfs_dev_inode_xp,
-                       xptr_t   devfs_external_inode_xp,
-                       xptr_t * devfs_internal_inode_xp );
+                       xptr_t   devfs_external_inode_xp );
                        
 /****************************************************************************************** 
Index: /trunk/kernel/fs/fatfs.c
===================================================================
--- /trunk/kernel/fs/fatfs.c	(revision 656)
+++ /trunk/kernel/fs/fatfs.c	(revision 657)
@@ -2,5 +2,5 @@
  * fatfs.c - FATFS file system API implementation.
  *
- * Author    Alain Greiner (2016,2017,2018,2019)
+ * Author    Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -122,5 +122,5 @@
         for( i = nbytes ; i > 0 ; i-- )
         {
-            res = (res<<8) | hal_remote_lb( buffer_xp+offset+i-1 );
+            res = (res<<8) | hal_remote_lb( buffer_xp + offset + i-1 );
         }
     }
@@ -129,5 +129,5 @@
         for( i = 0 ; i < nbytes ; i++ )
         {
-            res = (res<<8) | hal_remote_lb( buffer_xp+offset+i );
+            res = (res<<8) | hal_remote_lb( buffer_xp + offset + i );
         }
     }
@@ -135,4 +135,6 @@
 
 }  // end fatfs_get_remote_record()
+
+/*
 
 //////////////////////////////////////////////////////////////////////////////////////////
@@ -164,4 +166,6 @@
 }  // end fatfs_set_record()
 
+*/
+
 //////////////////////////////////////////////////////////////////////////////////////////
 // This function writes one, two, or four bytes from a 32 bits integer to a remote
@@ -185,5 +189,5 @@
         for( i = nbytes ; i > 0 ; i-- )
         {
-            hal_remote_sb( (buffer_xp+offset+i-1) , (uint8_t)(value>>((i-1)<<3)) );
+            hal_remote_sb( (buffer_xp + offset + i-1 ) , (uint8_t)(value>>((i-1)<<3)) );
         }
     }
@@ -192,5 +196,5 @@
         for( i = 0 ; i < nbytes ; i++ )
         {
-            hal_remote_sb( (buffer_xp+offset+i) , (uint8_t)(value>>((nbytes-1-i)<<3)) );
+            hal_remote_sb( (buffer_xp + offset + i) , (uint8_t)(value>>((nbytes-1-i)<<3)) );
         }
     }
@@ -470,5 +474,4 @@
     cxy_t         fat_cxy;         // FAT cluster identifier
     fatfs_ctx_t * fatfs_ctx;       // local pointer on FATFS context in FAT cluster
-    xptr_t        fat_mapper_xp;   // extended pointer on FAT mapper
     mapper_t    * fat_mapper_ptr;  // local pointer on FAT mapper
     uint32_t      page_id;         // current page index in FAT mapper
@@ -482,10 +485,6 @@
     fatfs_ctx = GET_PTR( fatfs_ctx_xp );
  
-    // get FAT mapper pointers from FATFS context 
-    fat_mapper_xp  = hal_remote_l64( XPTR( fat_cxy , &fatfs_ctx->fat_mapper_xp ) );
-    fat_mapper_ptr = GET_PTR( fat_mapper_xp );
-
-// check FAT cluster
-assert( (fat_cxy == GET_CXY( fat_mapper_xp )) , "unconsistent FAT cluster" );
+    // get FAT mapper pointer from FATFS context 
+    fat_mapper_ptr  = hal_remote_lpt( XPTR( fat_cxy , &fatfs_ctx->fat_mapper ) );
 
     // build extended pointer on FAT mapper radix tree
@@ -575,5 +574,5 @@
     
     // update the FS_INFO sector on IOC device
-    return dev_ioc_move_data( IOC_SYNC_WRITE , fs_info_buffer_xp , fs_info_lba , 1 );
+    return dev_ioc_sync_write( fs_info_buffer_xp , fs_info_lba , 1 );
  
 }  // end fatfs_update_ioc_fsinfo()
@@ -581,33 +580,34 @@
 //////////////////////////////////////////////////////////////////////////////////////////
 // This static function decrements the  "free_clusters" variable, and updates the 
-// "free_cluster_hint" variable in the FATFS context in FAT cluster, identified 
-// by the <fat_ctx_xp> argument, when a new <cluster> has been allocated from FAT.
+// "free_cluster_hint" variable in the FATFS context in FAT cluster,  when a new 
+// <cluster_id> has been allocated from the FAT.
+// It synchronously updates the FS_INFO sector on the IOC device. 
+// The FATFS context in FAT cluster is identified by the <fat_ctx_xp> argument.
+// It can be called by a thead running in any cluster. 
 // It scan all slots in the FAT mapper seen as an array of 32 bits words, looking for the
-// first free slot larger than the <cluster> argument, to update "free_cluster_hint".
-// It synchronously updates the FS_INFO sector on the IOC device. 
-// It can be called by a thead running in any cluster. 
+// first free slot larger than the <cluster_id>.
 // The lock protecting exclusive access to the FAT must be taken by the calling function. 
 //////////////////////////////////////////////////////////////////////////////////////////
 // @ fatfs_ctx_xp  : extended pointer on FATFS context in FAT cluster. 
-// @ cluster       : recently allocated cluster index in FAT.
+// @ cluster_id    : recently allocated cluster index in FAT.
 // @ return 0 if success, return -1 if the FS_INFO sector cannot be updated.
 //////////////////////////////////////////////////////////////////////////////////////////
 static error_t fatfs_free_clusters_decrement( xptr_t    fatfs_ctx_xp,
-                                              uint32_t  cluster )
+                                              uint32_t  cluster_id )
 {
     error_t       error;
-    cxy_t         fat_cxy;        // FAT cluster identifier
-    fatfs_ctx_t * fat_ctx_ptr;    // local pointer on fatfs context in FAT cluster
-    xptr_t        fat_mapper_xp;  // extended pointer on FAT mapper
-    xptr_t        hint_xp;        // extended pointer on "free_cluster_hint" shared variable
-    xptr_t        numb_xp;        // extended pointer on "free_clusters" shared variable 
-    uint32_t      numb;           // "free_clusters" variable current value
-    uint32_t      hint;           // "free_cluster_hint" variable current value
-    uint32_t      page_id;        // page index in FAT mapper
-    uint32_t      slot_id;        // slot index in one page of FAT (1024 slots per page)
-    uint32_t      page_max;       // max number of pages in FAT mapper
-    xptr_t        page_xp;        // extended pointer on current page in FAT mapper
-    xptr_t        base_xp;        // extended pointer on current page base
-    xptr_t        slot_xp;        // extended pointer on current slot in FAT mapper
+    cxy_t         fat_cxy;         // FAT cluster identifier
+    fatfs_ctx_t * fat_ctx_ptr;     // local pointer on fatfs context in FAT cluster
+    mapper_t    * fat_mapper_ptr;  // local pointer on FAT mapper
+    xptr_t        fat_mapper_xp;   // extended pointer on FAT mapper
+    xptr_t        hint_xp;         // extended pointer on "free_cluster_hint" shared variable
+    xptr_t        numb_xp;         // extended pointer on "free_clusters" shared variable 
+    uint32_t      page_id;         // page index in FAT mapper
+    uint32_t      slot_id;         // slot index in one page of FAT (1024 slots per page)
+    uint32_t      page_max;        // max number of pages in FAT mapper
+    xptr_t        page_xp;         // extended pointer on current page in FAT mapper
+    xptr_t        base_xp;         // extended pointer on current page base
+    xptr_t        slot_xp;         // extended pointer on current slot in FAT mapper
+    uint32_t      found;           // free slot found when non zero
 
 #if DEBUG_FATFS_FREE_CLUSTERS
@@ -615,6 +615,6 @@
 thread_t * this  = CURRENT_THREAD;
 if( DEBUG_FATFS_FREE_CLUSTERS < cycle )
-printk("\n[%s] thread[%x,%x] enter for allocated cluster %x / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, cluster , cycle );
+printk("\n[%s] thread[%x,%x] enter for allocated cluster_id %x / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, cluster_id , cycle );
 #endif
 
@@ -627,22 +627,19 @@
     numb_xp = XPTR( fat_cxy , &fat_ctx_ptr->free_clusters );
 
-    // update "free_clusters" value
-    numb = hal_remote_l32( numb_xp ) - 1;
-    hal_remote_s32( numb_xp , numb );
-
-    // get extended pointer on FAT mapper
-    fat_mapper_xp = hal_remote_l64( XPTR( fat_cxy , &fat_ctx_ptr->fat_mapper_xp ) );
-
-    // initialise variables to scan the FAT mapper
-    // and find the first free slot > cluster
-    page_id  = (cluster + 1) >> 10;
-    slot_id  = (cluster + 1) & 0x3FF;
+    // get pointers on FAT mapper from FATFS context
+    fat_mapper_ptr = hal_remote_lpt( XPTR( fat_cxy , &fat_ctx_ptr->fat_mapper ) );
+    fat_mapper_xp  = XPTR( fat_cxy , fat_mapper_ptr );
+
+    // initialise the loop variables to scan the FAT mapper
+    page_id  = (cluster_id + 1) >> 10;
+    slot_id  = (cluster_id + 1) & 0x3FF;
     page_max = hal_remote_l32( XPTR( fat_cxy, &fat_ctx_ptr->fat_sectors_count ) ) >> 3;
-
-    // scan FAT mapper / loop on pages
-    while ( page_id < page_max )            
+    found    = 0;
+
+    // scan FAT mapper : first loop on pages
+    while ( (page_id < page_max) && (found == 0) )            
     {
         // get current page from mapper
-        page_xp = mapper_remote_get_page( fat_mapper_xp , page_id );
+        page_xp = mapper_get_fat_page( fat_mapper_xp , page_id );
 
         if( page_xp == XPTR_NULL )
@@ -655,6 +652,6 @@
         base_xp = ppm_page2base( page_xp );
 
-        // scan FAT mapper / loop on slots
-        while ( slot_id < 1024 )
+        // scan the FAT mapper : second loop on slots
+        while ( (slot_id < 1024) && (found == 0) )
         {
             // get extended pointer on current slot
@@ -664,43 +661,50 @@
             if ( hal_remote_l32( slot_xp ) == FREE_CLUSTER )
             {
-                // update "free_cluster_hint" value
-                hint = (page_id << 10) + slot_id - 1;
-                hal_remote_s32( hint_xp , hint );
-
-                // update FS_INFO sector on IOC device
-                error = fatfs_update_ioc_fat( fatfs_ctx_xp,
-                                              page_id,
-                                              page_id );
-
-                if( error ) 
-                {
-                    printk("\n[ERROR] in %s : cannot update FS_INFO on IOC\n", __FUNCTION__ );
-                    return -1;
-                }
+                // exit both loops
+                found = 1;
+            }
+            else
+            {
+                // update slot_id if not found
+                slot_id++;
+            }
+        }  // end loop on slots
+
+        // update page_id & slot_id variables if not found
+        if( found == 0 )
+        {
+            page_id++;
+            slot_id = 0;
+        }
+    }  // end loop on pages
+
+    if( found )  // free cluster found
+    {
+        // update "free_clusters" and "free_cluster_hint" value in FATFS context
+        hal_remote_atomic_add( numb_xp , -1 );
+        hal_remote_s32( hint_xp , (page_id << 10) + slot_id - 1 );
+
+        // update FS_INFO sector on IOC device
+        error = fatfs_update_ioc_fsinfo( fatfs_ctx_xp );
+
+        if( error ) 
+        {
+            printk("\n[ERROR] in %s : cannot update FS_INFO on IOC\n", __FUNCTION__ );
+            return -1;
+        }
 
 #if DEBUG_FATFS_FREE_CLUSTERS 
-cycle = (uint32_t)hal_get_cycles();
-if( DEBUG_FATFS_FREE_CLUSTERS < (uint32_t)hal_get_cycles() )
-printk("\n[%s] thread[%x,%x] exit / hint %x / free %x / cycle %d\n",
+if( DEBUG_FATFS_FREE_CLUSTERS < cycle )
+printk("\n[%s] thread[%x,%x] exit / hint %x / free %x\n",
 __FUNCTION__, this->process->pid, this->trdid, 
-hal_remote_l32(hint_xp), hal_remote_l32(numb_xp), cycle );
-#endif
-                return 0;
-            }
-
-            // update slot_id
-            slot_id = 0;
-
-        }  // end loop on slots
-
-        // update page_id & slot_id variables
-        page_id++;
-        slot_id = 0;
-
-    }  // end loop on pages
-
-    // return error if no free cluster found 
-    printk("\n[ERROR] in %s : No free cluster found\n", __FUNCTION__ );
-    return -1;
+hal_remote_l32(hint_xp), hal_remote_l32(numb_xp) );
+#endif
+        return 0;
+    }
+    else  // free cluster not found
+    {
+        printk("\n[ERROR] in %s : No free cluster_id found\n", __FUNCTION__ );
+        return -1;
+    }
     
 }  // end fatfs_free_clusters_decrement()
@@ -709,19 +713,19 @@
 // This static function increments the "free_clusters" variable, and updates the
 // "free_cluster_hint" variables in the FATFS context in FAT cluster, identified 
-// by the <fat_ctx_xp> argument, when a FATFS cluster is released.
+// by the <fat_ctx_xp> argument, when a FATFS <cluster_id> has been released.
 // If the released cluster index is smaller than the current (hint) value, 
 // it set "free_cluster_hint" <= cluster.
-// It does NOT update the  FS_INFO sector on the IOC device.
+// It does NOT update the  FS_INFO sector on the IOC device, as this is done by the
+// calling fatfs_release_inode() function.
 // It can be called by a thead running in any cluster. 
 // The lock protecting exclusive access to the FAT must be taken by the calling function. 
 //////////////////////////////////////////////////////////////////////////////////////////
 // @ fatfs_ctx_xp  : extended pointer on FATFS context in FAT cluster. 
-// @ cluster       : recently released cluster index in FAT.
+// @ cluster_id    : recently released cluster index in FAT.
 // @ return 0 if success, return -1 if the FS_INFO sector cannot be updated.
 //////////////////////////////////////////////////////////////////////////////////////////
 static error_t fatfs_free_clusters_increment( xptr_t   fatfs_ctx_xp,
-                                              uint32_t cluster )
-{
-    error_t       error;
+                                              uint32_t cluster_id )
+{
     cxy_t         fat_cxy;      // FAT cluster identifier
     fatfs_ctx_t * fat_ctx_ptr;  // local pointer on fatfs context in FAT cluster
@@ -735,6 +739,6 @@
 thread_t * this  = CURRENT_THREAD;
 if( DEBUG_FATFS_FREE_CLUSTERS < cycle )
-printk("\n[%s] thread[%x,%x] enter for released cluster %x / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, cluster , cycle );
+printk("\n[%s] thread[%x,%x] enter for released cluster_id %x / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, cluster_id , cycle );
 #endif
 
@@ -753,5 +757,5 @@
     // update "numb" and "hint" variables as required
     numb++;
-    if ( (cluster - 1) < hint ) hint = cluster - 1;
+    if ( (cluster_id - 1) < hint ) hint = cluster_id - 1;
 
     // update free_clusters
@@ -759,19 +763,9 @@
     hal_remote_s32( hint_xp , hint );
 
-    // update FS_INFO sector on IOC device
-    error = fatfs_update_ioc_fsinfo( fatfs_ctx_xp );
-
-    if( error ) 
-    {
-        printk("\n[ERROR] in %s : cannot update FS_INFO on IOC\n", __FUNCTION__ );
-        return -1;
-    }
-
 #if DEBUG_FATFS_FREE_CLUSTERS 
-cycle = (uint32_t)hal_get_cycles();
-if( DEBUG_FATFS_FREE_CLUSTERS < (uint32_t)hal_get_cycles() )
-printk("\n[%s] thread[%x,%x] exit / hint %x / free %x / cycle %d\n",
+if( DEBUG_FATFS_FREE_CLUSTERS < cycle )
+printk("\n[%s] thread[%x,%x] exit / hint %x / free %x\n",
 __FUNCTION__, this->process->pid, this->trdid,
-hal_remote_l32( hint_xp ), hal_remote_l32( numb_xp ), cycle );
+hal_remote_l32( hint_xp ), hal_remote_l32( numb_xp ) );
 #endif
 
@@ -853,7 +847,222 @@
 
 //////////////////////////////////////////////////////////////////////////////////////////
+// This static function access the FAT mapper to allocate a new cluster in the FATFS,
+// and returns in <searched_cluster_id> the FATFS cluster index of a free cluster.
+// It updates the FAT mapper (handling miss from IOC device if required) :
+// - if the <last_cluster_id> is zero, the new cluster is the first allocated cluster,
+//   and the <searched_cluster_id> FAT slot is set to END_OF_CHAIN_CLUSTER.
+// - if the <last_cluster_id> argument is not zero, the new cluster is not the first,
+//   the <last_cluster_id> FAT slot is set to <searched_cluster_id>, 
+//   the <searched_cluster_id> FAT slot is set to END_OF_CHAIN_CLUSTER.
+// This function also updates the  two "free_cluster_hint" and "free_clusters" variables
+// stored in the FATFS context. It takes the rwlock stored in the FATFS context in the
+// FAT cluster to get exclusive access to the FAT.
+// This function synchronously updates the FAT region on IOC device. 
+// It can be called by a thread running in any cluster as it uses remote accesses.
+//////////////////////////////////////////////////////////////////////////////////////////
+// @ last_cluster_id      : [in]  previous last cluster index. 
+// @ searched_cluster_id  : [out] allocated cluster index.
+// @ return 0 if success / return -1 if no more free clusters on IOC device.
+//////////////////////////////////////////////////////////////////////////////////////////
+static error_t fatfs_cluster_alloc( uint32_t   last_cluster_id,
+                                    uint32_t * searched_cluster_id )
+{
+    error_t       error;
+    uint32_t      free_clusters;   // total number of free clusters
+    uint32_t      hint;            // hint + 1 is the first free cluster
+    uint32_t      new_cluster_id;  // allocated cluster index in FAT
+    uint32_t      new_page_id;     // allocated cluster page index in FAT mapper
+    uint32_t      new_slot_id;     // allocated cluster slot index in page (1024 slots per page)
+    xptr_t        new_page_xp;     // extended pointer on FAT page for allocated cluster
+    xptr_t        new_slot_xp;     // extended pointer on allocated cluster slot in FAT 
+    uint32_t      last_page_id;    // last cluster page index in FAT mapper
+    uint32_t      last_slot_id;    // last cluster slot index in page (1024 slots per page)
+    xptr_t        last_slot_xp;    // extended pointer on last cluster slot in FAT 
+    xptr_t        last_page_xp;    // extended pointer on FAT page for last cluster
+    vfs_ctx_t   * vfs_ctx;         // local pointer on VFS context (same in all clusters)
+    fatfs_ctx_t * loc_fatfs_ctx;   // local pointer on local FATFS context
+    fatfs_ctx_t * fat_fatfs_ctx;   // local pointer on FATFS context in FAT cluster
+    mapper_t    * fat_mapper_ptr;  // local pointer on FAT mapper
+    xptr_t        fat_mapper_xp;   // extended pointer on FAT mapper
+    cxy_t         fat_cxy;         // FAT mapper cluster identifier
+    xptr_t        lock_xp;         // extended pointer on lock protecting free clusters info
+    xptr_t        hint_xp;         // extended pointer on free_cluster_hint in FAT cluster
+    xptr_t        free_xp;         // extended pointer on free_clusters_number in FAT cluster
+    
+#if DEBUG_FATFS_CLUSTER_ALLOC
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+thread_t * this  = CURRENT_THREAD;
+if( DEBUG_FATFS_CLUSTER_ALLOC < cycle )
+printk("\n[%s] thread[%x,%x] enter / lats_cluster_id %x / cycle = %d\n",
+__FUNCTION__, this->process->pid, this->trdid, last_cluster_id, cycle );
+#endif
+
+    // get local pointer on VFS context (same in all clusters)
+    vfs_ctx = &fs_context[FS_TYPE_FATFS];
+
+    // get local pointer on local FATFS context
+    loc_fatfs_ctx = vfs_ctx->extend;
+
+    // get FAT cluster
+    fat_cxy = CONFIG_VFS_ROOT_CXY;
+
+    // get pointers on FAT mapper
+    fat_mapper_ptr = loc_fatfs_ctx->fat_mapper;
+    fat_mapper_xp  = XPTR( fat_cxy , fat_mapper_ptr );
+
+    // get local pointer on FATFS context in FAT cluster 
+    fat_fatfs_ctx = hal_remote_lpt( XPTR( fat_cxy , &vfs_ctx->extend ) );
+
+    // build relevant extended pointers on free clusters info in FAT cluster 
+    lock_xp = XPTR( fat_cxy , &fat_fatfs_ctx->lock );
+    hint_xp = XPTR( fat_cxy , &fat_fatfs_ctx->free_cluster_hint );
+    free_xp = XPTR( fat_cxy , &fat_fatfs_ctx->free_clusters );
+
+    // take the FAT lock in write mode
+    remote_rwlock_wr_acquire( lock_xp );
+
+    // get hint and free_clusters values from FATFS context in FAT cluster
+    hint          = hal_remote_l32( hint_xp );
+    free_clusters = hal_remote_l32( free_xp );
+        
+#if (DEBUG_FATFS_CLUSTER_ALLOC & 1)
+if( DEBUG_FATFS_CLUSTER_ALLOC < cycle )
+printk("\n[%s] thread[%x,%x] get free info : hint %x / free_clusters %x\n",
+__FUNCTION__, this->process->pid, this->trdid, hint, free_clusters );
+#endif
+
+    // check "free_clusters"
+    if ( free_clusters == 0 )
+    {
+        printk("\n[ERROR] in %s : no more free FATFS clusters\n", __FUNCTION__ );
+        remote_rwlock_wr_release( lock_xp );
+        return -1;
+    }
+    else if ( free_clusters < CONFIG_VFS_FREE_CLUSTERS_MIN )
+    {
+        printk("\n[WARNING] in %s : only %d free FATFS clusters\n", 
+        __FUNCTION__, CONFIG_VFS_FREE_CLUSTERS_MIN );
+    }
+
+    // get new cluster, page & slot indexes in FAT
+    new_cluster_id = hint + 1; 
+    new_page_id    = new_cluster_id >> 10;
+    new_slot_id    = new_cluster_id & 0x3FF;
+
+    // get relevant FAT page descriptor from FAT mapper
+    new_page_xp = mapper_get_fat_page( fat_mapper_xp , new_page_id );
+
+    if( new_page_xp == XPTR_NULL )
+    {
+        printk("\n[ERROR] in %s : cannot acces FAT mapper\n", __FUNCTION__ );
+        remote_rwlock_wr_release( lock_xp );
+        return -1;
+    }
+
+    // build extended pointer on new cluster slot in FAT mapper
+    new_slot_xp = ppm_page2base( new_page_xp ) + (new_slot_id << 2);
+         
+    // check selected cluster actually free
+    if( hal_remote_l32( new_slot_xp ) != FREE_CLUSTER )
+    { 
+        printk("\n[ERROR] in %s : selected cluster_id %x not free\n",
+        __FUNCTION__, new_cluster_id );
+        remote_rwlock_wr_release( lock_xp );
+        return -1;
+    }
+
+    // update new_cluster slot in FAT mapper
+    hal_remote_s32( new_slot_xp  , END_OF_CHAIN_CLUSTER_MIN );
+
+    // handle last_cluster_id argument if non zero
+    if( last_cluster_id )
+    {
+        // get last cluster page & slot indexes in FAT
+        last_page_id    = last_cluster_id >> 10;
+        last_slot_id    = last_cluster_id & 0x3FF;
+
+        // get relevant FAT page descriptor from FAT mapper
+        last_page_xp = mapper_get_fat_page( fat_mapper_xp , last_page_id );
+
+        if( last_page_xp == XPTR_NULL )
+        {
+            printk("\n[ERROR] in %s : cannot acces FAT mapper\n", __FUNCTION__ );
+            remote_rwlock_wr_release( lock_xp );
+            return -1;
+        }
+
+        // build extended pointer on new cluster slot in FAT mapper
+        last_slot_xp = ppm_page2base( last_page_xp ) + (last_slot_id << 2);
+         
+        // check last cluster actually end of chain
+        if( hal_remote_l32( last_slot_xp ) != END_OF_CHAIN_CLUSTER_MIN )
+        { 
+            printk("\n[ERROR] in %s : last_cluster_id %x not END_OF_CHAIN\n",
+            __FUNCTION__, last_cluster_id );
+            remote_rwlock_wr_release( lock_xp );
+            return -1;
+        }
+
+        // update last_cluster slot in FAT mapper
+        hal_remote_s32( last_slot_xp , new_cluster_id );
+    }
+    else
+    {
+        last_page_xp = XPTR_NULL;
+    }
+
+    // update the FAT new_page on device
+    error = fatfs_move_page( new_page_xp , IOC_SYNC_WRITE );
+
+    if( error )
+    { 
+        printk("\n[ERROR] in %s : cannot update FAT on IOC device\n", __FUNCTION__ );
+        remote_rwlock_wr_release( lock_xp );
+        return -1;
+    }
+
+    // update the FAT last_page on device when required
+    if( (last_page_xp != XPTR_NULL) && (last_page_xp != new_page_xp) )
+    {
+        error = fatfs_move_page( last_page_xp , IOC_SYNC_WRITE );
+
+        if( error )
+        { 
+            printk("\n[ERROR] in %s : cannot update FAT on IOC device\n", __FUNCTION__ );
+            remote_rwlock_wr_release( lock_xp );
+            return -1;
+        }
+    }
+
+    // update free cluster info in FATFS context and in FS_INFO sector
+    error = fatfs_free_clusters_decrement( XPTR( fat_cxy , fat_fatfs_ctx ) , new_cluster_id );
+
+    if( error )
+    { 
+        printk("\n[ERROR] in %s : cannot update free cluster info\n", __FUNCTION__ );
+        remote_rwlock_wr_release( lock_xp );
+        return -1;
+    }
+
+    // release FAT lock
+    remote_rwlock_wr_release( lock_xp );
+
+#if DEBUG_FATFS_CLUSTER_ALLOC 
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_FATFS_CLUSTER_ALLOC < cycle )
+printk("\n[%s] thread[%x,%x] exit / allocated cluster_id %x in FAT / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, new_cluster_id, cycle );
+#endif
+
+    *searched_cluster_id = new_cluster_id;
+    return 0;
+
+}  // end fatfs_cluster_alloc()
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
 // This static function access the FAT (File Allocation Table), stored in the FAT mapper,
 // and returns in <searched_cluster_id> the FATFS cluster_id for a given page of a given 
-// inode, identified by the <searched_page_id> argument, that is the page index in file
+// inode, identified by the <searched_page_id> argument that is the page index in file
 // (i.e. the page index in file mapper). The entry point in the FAT is defined by the
 // <first_cluster_id> argument, that is the cluster_id of an already allocated cluster.
@@ -861,6 +1070,5 @@
 // fatfs_inode extension), or any page of the file whose <first_page_id> argument
 // is smaller than the searched <first_page_id> argument. 
-// This function can be called by a thread running in any cluster, as it uses remote
-// access primitives when the FAT mapper is remote.
+// This function can be called by a thread running in any cluster.
 // The FAT mapper being a WRITE-THROUGH cache, this function updates the FAT mapper 
 // from informations stored on IOC device in case of miss when scanning the FAT mapper.
@@ -885,4 +1093,8 @@
     uint32_t   current_cluster_id;     // index of cluster in FATFS
     xptr_t     lock_xp;                // extended pointer on FAT lock
+    xptr_t     fat_mapper_xp;          // extended pointer on FAT mapper
+    mapper_t * fat_mapper_ptr;         // local pointer on FAT mapper
+    cxy_t      fat_cxy;                // FAT cluster
+    error_t    error;
 
 assert( (searched_page_id > first_page_id) ,
@@ -893,6 +1105,7 @@
 thread_t * this  = CURRENT_THREAD;
 if( DEBUG_FATFS_GET_CLUSTER < cycle )
-printk("\n[%s] thread[%x,%x] enter / first_cluster_id %x / searched_page_id %d / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, first_cluster_id, searched_page_id, cycle );
+printk("\n[%s] thread[%x,%x] enter / frst_pid %x / frst_cid %x / srch_pid %d / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid,
+first_page_id, first_cluster_id, searched_page_id, cycle );
 #endif
 
@@ -903,7 +1116,10 @@
     fatfs_ctx_t * loc_fatfs_ctx = vfs_ctx->extend;
 
-    // get extended pointer and cluster on FAT mapper
-    xptr_t fat_mapper_xp  = loc_fatfs_ctx->fat_mapper_xp;
-    cxy_t  fat_cxy        = GET_CXY( fat_mapper_xp );
+    // get FAT cluster
+    fat_cxy = CONFIG_VFS_ROOT_CXY;
+    
+    // get pointers on FAT mapper
+    fat_mapper_ptr = loc_fatfs_ctx->fat_mapper;
+    fat_mapper_xp  = XPTR( fat_cxy , fat_mapper_ptr );
 
     // get local pointer on FATFS context in FAT cluster 
@@ -931,5 +1147,5 @@
 
         // get pointer on current page descriptor in FAT mapper
-        xptr_t current_page_xp = mapper_remote_get_page( fat_mapper_xp , fat_page_index );
+        xptr_t current_page_xp = mapper_get_fat_page( fat_mapper_xp , fat_page_index );
 
         if( current_page_xp == XPTR_NULL )
@@ -942,12 +1158,11 @@
 
         // get pointer on buffer containing the FAT mapper page
-        xptr_t base_xp = ppm_page2base( current_page_xp );
-        uint32_t * buffer = (uint32_t *)GET_PTR( base_xp );
+        xptr_t     base_xp = ppm_page2base( current_page_xp );
+        uint32_t * buffer  = (uint32_t *)GET_PTR( base_xp );
 
         // get next_cluster_id from FAT slot  
         uint32_t next_cluster_id = hal_remote_l32( XPTR( fat_cxy, &buffer[fat_slot_index] ) );
 
-        // allocate a new FAT cluster when there is no cluster
-        // allocated on device for the current page
+        // allocate a new FAT cluster when END_OF_CHAIN found
         if( next_cluster_id >= END_OF_CHAIN_CLUSTER_MIN ) 
         {
@@ -956,6 +1171,6 @@
 
             // allocate a new cluster_id (and update both FAT mapper and FAT on device).
-            error_t error = fatfs_cluster_alloc( &next_cluster_id );
-
+            error = fatfs_cluster_alloc( current_cluster_id,
+                                         &next_cluster_id );
             if( error )
             {
@@ -968,5 +1183,5 @@
 #if (DEBUG_FATFS_GET_CLUSTER & 1)
 if( DEBUG_FATFS_GET_CLUSTER < cycle )
-printk("\n[%s] allocated a new cluster_id %d in FATFS\n",
+printk("\n[%s] allocated a new cluster_id %x in FATFS\n",
 __FUNCTION__, next_cluster_id );
 #endif
@@ -977,6 +1192,6 @@
 #if (DEBUG_FATFS_GET_CLUSTER & 1)
 if( DEBUG_FATFS_GET_CLUSTER < cycle )
-printk("\n[%s] traverse FAT / fat_page_index %d / fat_slot_index %d / next_cluster_id %x\n",
-__FUNCTION__, fat_page_index, fat_slot_index , next_cluster_id );
+printk("\n[%s] traverse FAT / current_cluster_id %x / next_cluster_id %x\n",
+__FUNCTION__, current_cluster_id , next_cluster_id );
 #endif
 
@@ -991,6 +1206,7 @@
 #if DEBUG_FATFS_GET_CLUSTER
 if( DEBUG_FATFS_GET_CLUSTER < cycle )
-printk("\n[%s] thread[%x,%x] exit / searched_cluster_id = %d\n",
-__FUNCTION__, this->process->pid, this->trdid, current_cluster_id );
+printk("\n[%s] thread[%x,%x] exit / frst_pid %d / frst_cid %x / srch_pid %d / srch_cid %x\n",
+__FUNCTION__, this->process->pid, this->trdid,
+first_page_id, first_cluster_id, searched_page_id, current_cluster_id );
 #endif
 
@@ -1000,747 +1216,14 @@
 }  // end fatfs_get_cluster()
 
-
-
-
-
-//////////////////////////////////////////////////////////////////////////////////////////
-//              FATFS specific extern functions 
-//////////////////////////////////////////////////////////////////////////////////////////
-
-///////////////////////////////////
-void fatfs_display_ctx( cxy_t cxy )
-{
-    // get pointer on local FATFS context
-    vfs_ctx_t   * vfs_ctx = &fs_context[FS_TYPE_FATFS];
-	fatfs_ctx_t * ctx     = hal_remote_lpt( XPTR( cxy , &vfs_ctx->extend ) );
-
-    uint32_t fat_sectors       = hal_remote_l32( XPTR( cxy , &ctx->fat_sectors_count ) );
-    uint32_t sector_size       = hal_remote_l32( XPTR( cxy , &ctx->bytes_per_sector ) );
-    uint32_t sec_per_clus      = hal_remote_l32( XPTR( cxy , &ctx->sectors_per_cluster ) );
-    uint32_t fat_lba           = hal_remote_l32( XPTR( cxy , &ctx->fat_begin_lba ) );
-    uint32_t data_lba          = hal_remote_l32( XPTR( cxy , &ctx->cluster_begin_lba ) );
-    uint32_t fsinfo_lba        = hal_remote_l32( XPTR( cxy , &ctx->fs_info_lba ) );
-    uint32_t root_dir_clus     = hal_remote_l32( XPTR( cxy , &ctx->root_dir_cluster ) );
-    uint32_t free_clusters     = hal_remote_l32( XPTR( cxy , &ctx->free_clusters ) );
-    uint32_t free_cluster_hint = hal_remote_l32( XPTR( cxy , &ctx->free_cluster_hint ) );
-    xptr_t   mapper_xp         = hal_remote_l64( XPTR( cxy , &ctx->fat_mapper_xp ) );
-    void   * fs_info_buffer    = hal_remote_lpt( XPTR( cxy , &ctx->fs_info_buffer ) );
-
-    printk("\n*** FAT context in cluster %x\n" 
-           "- fat_sectors       = %d\n"
-           "- sector size       = %d\n"
-           "- cluster size      = %d\n"
-           "- fat_lba           = %x\n"
-           "- data_lba          = %x\n"
-           "- fsinfo_lba        = %x\n"
-           "- root_dir_cluster  = %x\n"
-           "- free_clusters     = %x\n"
-           "- free_cluster_hint = %x\n"
-           "- fat_mapper_ptr    = %x\n"
-           "- fs_info_buffer    = %x\n",
-           cxy,
-           fat_sectors,
-           sector_size,
-           sector_size * sec_per_clus,
-           fat_lba,
-           data_lba,
-           fsinfo_lba,
-           root_dir_clus,
-           free_clusters,
-           free_cluster_hint,
-           GET_PTR( mapper_xp ),
-           fs_info_buffer );
-
-}  // end fatfs_ctx_display()
-
-//////////////////////////////////////////
-void fatfs_display_fat( uint32_t  page_id,
-                        uint32_t  min_slot,
-                        uint32_t  nb_slots )
-{
-    uint32_t line;
-
-    // compute number of lines to display
-    uint32_t min_line = min_slot >> 3;
-    uint32_t max_line = (min_slot + nb_slots - 1) >> 3;
-
-    // get pointer on local FATFS context
-    vfs_ctx_t   * vfs_ctx       = &fs_context[FS_TYPE_FATFS];
-    fatfs_ctx_t * loc_fatfs_ctx = (fatfs_ctx_t *)vfs_ctx->extend;
-
-    // get pointers on FAT mapper (in FAT cluster)
-    xptr_t     mapper_xp  = loc_fatfs_ctx->fat_mapper_xp;
-    cxy_t      mapper_cxy = GET_CXY( mapper_xp );
-
-    // get pointer on FATFS context in FAT cluster
-    fatfs_ctx_t * fat_fatfs_ctx = hal_remote_lpt( XPTR( mapper_cxy , &vfs_ctx->extend ) );
- 
-    // get current value of hint and free_clusters
-    uint32_t hint = hal_remote_l32( XPTR( mapper_cxy , &fat_fatfs_ctx->free_cluster_hint ) );
-    uint32_t free = hal_remote_l32( XPTR( mapper_cxy , &fat_fatfs_ctx->free_clusters ) );
-
-    // get extended pointer on requested page descriptor in FAT mapper 
-    xptr_t page_xp = mapper_remote_get_page( mapper_xp , page_id );
-
-    // get pointers on requested page base
-    xptr_t     base_xp  = ppm_page2base( page_xp );
-    void     * base     = GET_PTR( base_xp );
-
-    printk("\n***** FAT mapper / cxy %x / page_id %d / base %x / free_clusters %x / hint %x\n",
-    mapper_cxy, page_id, base, free, hint );
-
-    for( line = min_line ; line <= max_line ; line++ )
-    {
-        printk("%d : %X | %X | %X | %X | %X | %X | %X | %X\n", (line<<3),
-        hal_remote_l32( base_xp + ((line<<5)      ) ),
-        hal_remote_l32( base_xp + ((line<<5) + 4  ) ),
-        hal_remote_l32( base_xp + ((line<<5) + 8  ) ),
-        hal_remote_l32( base_xp + ((line<<5) + 12 ) ),
-        hal_remote_l32( base_xp + ((line<<5) + 16 ) ),
-        hal_remote_l32( base_xp + ((line<<5) + 20 ) ),
-        hal_remote_l32( base_xp + ((line<<5) + 24 ) ),
-        hal_remote_l32( base_xp + ((line<<5) + 28 ) ) );
-    }
-
-}  // end fatfs_display_fat()
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////
-// Generic API : the following functions are called by the kernel VFS
-//               and must be defined by all supported file systems.
-///////////////////////////////////////////////////////////////////////////////////////
-
-/////////////////////////////////////
-fatfs_ctx_t * fatfs_ctx_alloc( void )
-{
-    kmem_req_t    req;
-	req.type    = KMEM_KCM;
-	req.order   = bits_log2( sizeof(fatfs_ctx_t) );
-    req.flags   = AF_KERNEL | AF_ZERO;
-
-	return kmem_alloc( &req );
-}
-
-//////////////////////////////////////////////
-void fatfs_ctx_init( fatfs_ctx_t * fatfs_ctx )
-{
-    error_t       error;
-    kmem_req_t    req;
-    uint8_t     * buffer;
-    xptr_t        buffer_xp;
-
-#if DEBUG_FATFS_CTX_INIT
-uint32_t   cycle = (uint32_t)hal_get_cycles();
-thread_t * this  = CURRENT_THREAD;
-if( DEBUG_FATFS_CTX_INIT < cycle )
-printk("\n[%s] thread[%x,%x] enter for fatfs_ctx = %x / cycle %d\n",
-__FUNCTION__ , this->process->pid, this->trdid, fatfs_ctx , cycle );
-#endif
-
-// check argument
-assert( (fatfs_ctx != NULL) , "pointer on FATFS context is NULL" );
-
-// check only cluster 0 does FATFS initialization
-assert( (local_cxy == 0) , "only cluster 0 can initialize FATFS");
-
-    // allocate a permanent 512 bytes buffer to store
-    // - temporarily the BOOT sector
-    // - permanently the FS_INFO sector
-	req.type    = KMEM_KCM;
-    req.order   = 9;                    // 512 bytes
-    req.flags   = AF_KERNEL | AF_ZERO;
-	buffer      = kmem_alloc( &req );
-
-    if( buffer == NULL ) 
-    {
-        printk("\n[PANIC] in %s : cannot allocate buffer\n", __FUNCTION__ );
-        hal_core_sleep();
-    }
-     
-    buffer_xp   = XPTR( local_cxy , buffer );
-
-    // load the BOOT record from device
-    error = dev_ioc_move_data( IOC_SYNC_READ , buffer_xp , 0 , 1 );
-
-    if ( error )
-    {
-        printk("\n[PANIC] in %s : cannot access boot record\n", __FUNCTION__ );
-        hal_core_sleep();
-    }
-
-#if (DEBUG_FATFS_CTX_INIT & 0x1)
-if( DEBUG_FATFS_CTX_INIT < cycle )
-putb( "boot record", buffer , 256 );
-#endif
-
-    // get sector size from boot record
-    uint32_t sector_size = fatfs_get_record( BPB_BYTSPERSEC , buffer );
-    if ( sector_size != 512 )
-    {
-        printk("\n[PANIC] in %s : sector size must be 512 bytes\n", __FUNCTION__ );
-        hal_core_sleep();
-    }
-
-    // get cluster size from boot record
-    uint32_t nb_sectors = fatfs_get_record( BPB_SECPERCLUS , buffer );
-    if ( nb_sectors != 8 )
-    {
-        printk("\n[PANIC] in %s : cluster size must be 8 sectors\n", __FUNCTION__ );
-        hal_core_sleep();
-    }
-
-    // get number of FAT copies from boot record
-    uint32_t nb_fats = fatfs_get_record( BPB_NUMFATS , buffer );
-    if ( nb_fats != 1 )
-    {
-        printk("\n[PANIC] in %s : number of FAT copies must be 1\n", __FUNCTION__ );
-        hal_core_sleep();
-    }
-
-    // get number of sectors in FAT from boot record
-    uint32_t fat_sectors = fatfs_get_record( BPB_FAT32_FATSZ32 , buffer );
-    if ( (fat_sectors & 0xF) != 0 )
-    {
-        printk("\n[PANIC] in %s : FAT size not multiple of 16 sectors\n", __FUNCTION__ );
-        hal_core_sleep();
-    }
-
-    // get root cluster from boot record
-    uint32_t root_cluster = fatfs_get_record( BPB_FAT32_ROOTCLUS , buffer );
-    if ( root_cluster != 2 ) 
-    {
-        printk("\n[PANIC] in %s : root cluster index must be 2\n", __FUNCTION__ );
-        hal_core_sleep();
-    }
-
-    // get FAT lba from boot record
-    uint32_t fat_lba = fatfs_get_record( BPB_RSVDSECCNT , buffer );
-
-    // get FS_INFO sector lba from boot record
-    uint32_t fs_info_lba = fatfs_get_record( BPB_FAT32_FSINFO , buffer );
-
-    // load the FS_INFO record from device
-    error = dev_ioc_move_data( IOC_SYNC_READ , buffer_xp , fs_info_lba , 1 );
-
-    if ( error )
-    {
-        printk("\n[PANIC] in %s : cannot access FS_INFO record\n", __FUNCTION__ );
-        hal_core_sleep();
-    }
-
-    // get free_clusters number from FS_INFO record
-    uint32_t free_clusters = fatfs_get_record( FS_FREE_CLUSTERS , buffer );
-    if ( free_clusters >= fat_sectors << 7 )
-    {
-        printk("\n[PANIC] in %s : unconsistent free_clusters\n", __FUNCTION__ );
-        hal_core_sleep();
-    }
-
-    // get free_cluster_hint from FS_INFO record
-    uint32_t free_cluster_hint = fatfs_get_record( FS_FREE_CLUSTER_HINT , buffer );
-
-    if ( free_cluster_hint >= fat_sectors << 7 )
-    {
-        printk("\n[PANIC] in %s : unconsistent free_cluster_hint\n", __FUNCTION__ );
-        hal_core_sleep();
-    }
-
-    // allocate a mapper for the FAT itself
-    mapper_t * fat_mapper = mapper_create( FS_TYPE_FATFS );
-    if ( fat_mapper == NULL )
-    {
-        printk("\n[PANIC] in %s : no memory for FAT mapper\n", __FUNCTION__ );
-        hal_core_sleep();
-    }
-
-    // the inode field is NULL for the FAT mapper
-    fat_mapper->inode = NULL;
-
-    // initialize the FATFS context
-    fatfs_ctx->fat_begin_lba         = fat_lba;
-    fatfs_ctx->fat_sectors_count     = fat_sectors; 
-    fatfs_ctx->bytes_per_sector      = sector_size;
-    fatfs_ctx->sectors_per_cluster   = nb_sectors;
-    fatfs_ctx->cluster_begin_lba     = fat_lba + fat_sectors;
-    fatfs_ctx->root_dir_cluster      = 2;
-    fatfs_ctx->fat_mapper_xp         = XPTR( local_cxy , fat_mapper );
-    fatfs_ctx->fs_info_lba           = fs_info_lba;
-    fatfs_ctx->free_clusters         = free_clusters;
-    fatfs_ctx->free_cluster_hint     = free_cluster_hint;
-    fatfs_ctx->fs_info_buffer        = buffer;
-
-    remote_rwlock_init( XPTR( local_cxy , &fatfs_ctx->lock ) , LOCK_FATFS_FAT );
-
-#if (DEBUG_FATFS_CTX_INIT & 0x1)
-if( DEBUG_FATFS_CTX_INIT < cycle )
-fatfs_ctx_display( fatfs_ctx );
-#endif
-
-#if DEBUG_FATFS_CTX_INIT
-cycle = (uint32_t)hal_get_cycles();
-if( DEBUG_FATFS_CTX_INIT < cycle )
-printk("\n[%s]  thread[%x,%x] exit for fatfs_ctx = %x / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, fatfs_ctx, cycle );
-#endif
-
-}  // end fatfs_ctx_init()
-
-/////////////////////////////////////////////////
-void fatfs_ctx_destroy( fatfs_ctx_t * fatfs_ctx )
-{
-    kmem_req_t    req;
-    req.type = KMEM_KCM;
-    req.ptr  = fatfs_ctx;
-    kmem_free( &req );
-}
-
-///////////////////////////////////////////////
-error_t fatfs_add_dentry( vfs_inode_t  * inode,
-                          vfs_dentry_t * dentry )
-{
-    error_t       error;
-    uint32_t      length;        // dentry name length
-    uint32_t      nb_lfn;        // number or required LFN
-    char          sfn[11];       // buffer for SFN name
-    uint8_t       checksum;      // name checksum 
-    mapper_t    * mapper;        // loal pointer on parent inode mapper
-    xptr_t        mapper_xp;     // extended pointer on parent inode mapper
-    xptr_t        child_xp;      // extended pointer on child inode 
-    cxy_t         child_cxy;     // child inode cluster
-    vfs_inode_t * child_ptr;     // child inode local pointer
-    uint32_t      size;          // child inode size
-    uint32_t      type;          // child inode type
-    uint32_t      cluster;       // child inode cluster index
-
-#if DEBUG_FATFS_ADD_DENTRY
-char       dir_name[CONFIG_VFS_MAX_NAME_LENGTH];
-uint32_t   cycle = (uint32_t)hal_get_cycles();
-thread_t * this  = CURRENT_THREAD;
-vfs_inode_get_name( XPTR( local_cxy , inode ) , dir_name );
-if( DEBUG_FATFS_ADD_DENTRY < cycle )
-printk("\n[%s]  thread[%x,%x] enter / parent <%s> / child <%s> / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, dir_name, dentry->name, cycle );
-#endif
-
-// check arguments
-assert( (inode != NULL) , "inode pointer is NULL\n" );
-assert( (dentry != NULL) , "dentry pointer is NULL\n" );
-assert( (inode->mapper != NULL ) , "mapper pointer is NULL\n" );
-  
-    // get pointers on directory mapper
-    mapper    = inode->mapper;
-    mapper_xp = XPTR( local_cxy , mapper );
-
-    // get extended pointers on remote child inode
-    child_xp  = dentry->child_xp;
-    child_cxy = GET_CXY( child_xp );
-    child_ptr = GET_PTR( child_xp );
-
-    // get relevant infos from child inode
-    type    =                     hal_remote_l32( XPTR( child_cxy , &child_ptr->type ) );
-    size    =                     hal_remote_l32( XPTR( child_cxy , &child_ptr->size ) );
-    cluster = (uint32_t)(intptr_t)hal_remote_lpt( XPTR( child_cxy , &child_ptr->extend ) );
-
-    // analyse dentry name
-    error = fatfs_name_format( dentry->name,
-                               &length,
-                               &nb_lfn,
-                               sfn,
-                               &checksum );
-    if ( error )
-    {
-        printk("\n[ERROR] in %s : dentry name > 31 bytes\n", __FUNCTION__ );
-        return -1;
-    }
-                               
-    // Search end of directory with two embedded loops:
-    // - scan the pages in the mapper
-    // - scan the entries in each page to find NO_MORE_ENTRY
-
-    xptr_t     page_xp;                 // extended pointer on page descriptor
-    xptr_t     base_xp;                 // extended pointer on page base
-    uint8_t  * base;                    // local pointer on page base (array of bytes)
-    uint32_t   page_id = 0;             // page index in mapper
-    uint32_t   offset  = 0;             // position in page
-    uint32_t   found   = 0;             // NO_MORE_ENTRY found
-
-    // loop on pages in mapper
-    while ( found == 0 )
-    {
-        // get extended pointer on page descriptor in mapper 
-        page_xp  = mapper_remote_get_page( mapper_xp , page_id );
-
-        if ( page_xp == XPTR_NULL )
-        {
-            printk("\n[ERROR] in %s : cannot extend directory mapper\n", __FUNCTION__ );
-            return -1;
-        }
-        
-        // get pointer on page base
-        base_xp = ppm_page2base( page_xp );
-        base = GET_PTR( base_xp );
-
-        // loop on directory entries in this page
-        while ( (offset < 4096) && (found == 0) )
-        {
-            if ( fatfs_get_record( LDIR_ORD, (base + offset) ) == NO_MORE_ENTRY )
-            {
-                found = 1;
-            }  
-            else
-            {
-                offset = offset + 32;
-            }
-        }  // end loop on entries
-
-        if ( found == 0 )
-        {
-            page_id++;
-            offset = 0;
-        }
-    }  // end loop on pages
-
-    // Modify the directory mapper: depending on the name length,
-    // the new child requires to write (3, 4, or 5) directory entries.
-    // To actually register the new child, we use a 5 steps FSM
-    // (one state per entry to be written), that is traversed as:
-    // LFN3 -> LFN2 -> LFN1 -> NORMAL -> NOMORE 
-    // At most two pages are modified:
-    // - the page containing the NO_MORE_ENTRY is always modified
-    // - the following page can be modified if the name spread on to pages.
-
-    char * name = dentry->name;
-
-    uint32_t step;          // FSM state
-
-    if      ( nb_lfn == 1 ) step = 3;
-    else if ( nb_lfn == 2 ) step = 4;
-    else if ( nb_lfn == 3 ) step = 5;
-    
-    uint8_t  * entry;       // pointer on directory entry to be written
-    uint32_t   i;           // byte index in one 32 bytes directory
-    uint32_t   c;           // character index in name
-
-    while ( step )   
-    {
-        // when the new child is split on two pages,
-        // we need to access a new page in mapper
-        if ( offset >= 4096 )
-        {
-            // copy the modified page to IOC device
-            fatfs_move_page( page_xp , IOC_SYNC_WRITE );   
-
-            // get the next page in FAT mapper
-            page_xp  = mapper_remote_get_page( mapper_xp , page_id + 1 );
-
-            if ( page_xp == XPTR_NULL )
-            {
-                printk("\n[ERROR] in %s : cannot extend directory mapper\n", __FUNCTION__ );
-                return -1;
-            }
-        
-            // get pointer on page base
-            base_xp = ppm_page2base( page_xp );
-            base = GET_PTR( base_xp );
-            
-            // update offset
-            offset = 0;
-        }
-
-        // compute directory entry address
-        entry = base + offset;
-
-#if (DEBUG_FATFS_ADD_DENTRY & 1)
-cycle = (uint32_t)hal_get_cycles();
-if( DEBUG_FATFS_ADD_DENTRY < cycle )
-printk("\n[%s] FSM step = %d / offset = %x / nb_lfn = %d / cycle %d\n",
-__FUNCTION__, step, offset, nb_lfn, cycle );
-#endif
-
-        // write 32 bytes (one directory entry) per iteration
-        switch ( step )
-        {
-            case 5:   // write LFN3 entry
-            {
-                c = 26;
-                // scan the 32 bytes in dir_entry
-                for ( i = 0 ; i < 32 ; i++ )
-                {
-                    if (i == 0)
-                    {
-                        if ( nb_lfn == 3) entry[i] = 0x43;
-                        else              entry[i] = 0x03;
-                    }
-                    else if ( ( ((i >= 1 ) && (i<=10) && ((i&1)==1))   ||
-                                ((i >= 14) && (i<=25) && ((i&1)==0))   ||
-                                ((i >= 28) && (i<=31) && ((i&1)==0)) ) &&
-                              ( c < length ) )
-                    {
-                                          entry[i] = name[c];
-                                          c++;
-                    }
-                    else if (i == 11)     entry[i] = 0x0F;
-                    else if (i == 13)     entry[i] = checksum;
-                    else                  entry[i] = 0x00;
-                }
-                step--;
-                break;
-            }
-            case 4:   // write LFN2 entry  
-            {
-                c = 13;
-                // scan the 32 bytes in dir_entry
-                for ( i = 0 ; i < 32 ; i++ )
-                {
-                    if (i == 0)
-                    {
-                        if ( nb_lfn == 2) entry[i] = 0x42;
-                        else              entry[i] = 0x02;
-                    }
-                    else if ( ( ((i >= 1 ) && (i<=10) && ((i&1)==1))   ||
-                                ((i >= 14) && (i<=25) && ((i&1)==0))   ||
-                                ((i >= 28) && (i<=31) && ((i&1)==0)) ) &&
-                              ( c < length ) )
-                    {
-                                          entry[i] = name[c];
-                                          c++;
-                    }
-                    else if (i == 11)     entry[i] = 0x0F;
-                    else if (i == 13)     entry[i] = checksum;
-                    else                  entry[i] = 0x00;
-                }
-                step--;
-                break;
-            }
-            case 3:   // Write LFN1 entry   
-            {
-                c = 0;
-                // scan the 32 bytes in dir_entry
-                for ( i = 0 ; i < 32 ; i++ )
-                {
-                    if (i == 0)
-                    {
-                        if ( nb_lfn == 1) entry[i] = 0x41;
-                        else              entry[i] = 0x01;
-                    }
-                    else if ( ( ((i >= 1 ) && (i<=10) && ((i&1)==1))   ||
-                                ((i >= 14) && (i<=25) && ((i&1)==0))   ||
-                                ((i >= 28) && (i<=31) && ((i&1)==0)) ) &&
-                              ( c < length ) )
-                    {
-                                          entry[i] = name[c];
-                                          c++;
-                    }
-                    else if (i == 11)     entry[i] = 0x0F;
-                    else if (i == 13)     entry[i] = checksum;
-                    else                  entry[i] = 0x00;
-                }
-                step--;
-                break;
-            }
-            case 2:   // write NORMAL entry     
-            {
-                // scan the 32 bytes in dir_entry
-                for ( i = 0 ; i < 32 ; i++ )
-                {
-                    if      ( i < 11 )                              // 8.3 SFN
-                    {
-                                          entry[i] = sfn[i];
-                    }
-                    else if (i == 11)                               // ATTR
-                    {
-                        if (type == INODE_TYPE_DIR)  entry[i] = 0x10;
-                        else                         entry[i] = 0x20;
-                    }
-                    else if (i == 20)     entry[i] = cluster>>16;   // cluster.B2
-                    else if (i == 21)     entry[i] = cluster>>24;   // cluster.B3
-                    else if (i == 26)     entry[i] = cluster>>0;    // cluster.B0
-                    else if (i == 27)     entry[i] = cluster>>8;    // cluster.B1
-                    else if (i == 28)     entry[i] = size>>0;       // size.B0
-                    else if (i == 29)     entry[i] = size>>8;       // size.B1
-                    else if (i == 30)     entry[i] = size>>16;      // size.B2
-                    else if (i == 31)     entry[i] = size>>24;      // size.B3
-                    else                  entry[i] = 0x00;
-                }
-
-                // update the "extend" field in dentry descriptor
-                dentry->extend = (void*)(intptr_t)(((page_id<<12) + offset)>>5);
-
-                step--;
-                break;
-            }
-            case 1:   // write NOMORE entry  
-            {
-                entry [0] = 0x00;
-                step--;
-                break;
-            }
-        } // end switch step
-
-        offset += 32;
-
-    } // exit while     
-
-#if (DEBUG_FATFS_ADD_DENTRY & 1)
-cycle = (uint32_t)hal_get_cycles();
-if( DEBUG_FATFS_ADD_DENTRY < cycle )
-printk("\n[%s]  thread[%x,%x] before IOC access / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, cycle );
-#endif
-
-    // copy the modified page to the IOC device
-    fatfs_move_page( page_xp , IOC_SYNC_WRITE );   
-
-#if DEBUG_FATFS_ADD_DENTRY
-cycle = (uint32_t)hal_get_cycles();
-if( DEBUG_FATFS_ADD_DENTRY < cycle )
-printk("\n[%s]  thread[%x,%x] exit / parent <%s> / child <%s> / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, dir_name, dentry->name, cycle );
-#endif
-
-    return 0;
-
-}  // end fatfs_add_dentry()
-
-//////////////////////////////////////////////////
-error_t fatfs_remove_dentry( vfs_inode_t  * inode,
-                             vfs_dentry_t * dentry )
-{
-    xptr_t     mapper_xp;  // extended pointer on mapper
-    mapper_t * mapper;     // local pointer on mapper
-    xptr_t     page_xp;    // extended pointer on mapper page descriptor
-    xptr_t     base_xp;    // extended pointer on mapper page base
-    uint8_t  * base;       // local pointer on mapper page base
-
-#if DEBUG_FATFS_REMOVE_DENTRY
-char       dir_name[CONFIG_VFS_MAX_NAME_LENGTH];
-uint32_t   cycle = (uint32_t)hal_get_cycles();
-thread_t * this  = CURRENT_THREAD;
-vfs_inode_get_name( XPTR( local_cxy , inode ) , dir_name );
-if( DEBUG_FATFS_REMOVE_DENTRY < cycle )
-printk("\n[%s] thread[%x,%x] enter / parent <%s> / child <%s> / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, dir_name, dentry->name, cycle );
-#endif
-
-// check arguments
-assert( (inode != NULL) , "inode pointer is NULL\n" );
-assert( (dentry != NULL) , "dentry pointer is NULL\n" );
-assert( (inode->type == INODE_TYPE_DIR) , "inode is not a directory\n" );
-assert( (inode->mapper != NULL ) , "mapper pointer is NULL\n" );
-
-    // get pointers on directory mapper
-    mapper    = inode->mapper;
-    mapper_xp = XPTR( local_cxy , mapper );
-
-    // compute number of LFN entries
-    uint32_t nb_lfn;
-    uint32_t name_length = strlen( dentry->name );
-
-    if      ( name_length <= 13 ) nb_lfn  = 1;
-    else if ( name_length <= 26 ) nb_lfn  = 2;
-    else                          nb_lfn  = 3;
-
-    // we must invalidate (2, 3 or 4) 32 bytes entries:
-    // the NORMAL entry (registered in dentry->extend) and all preceding LFN entries
-    // At most two pages are modified:
-    // - the page containing the NORMAL entry is always modified.
-    // - the preceding page is modified when the name spread on two pages. 
-
-    // get 32 bytes directory entry index from dentry->extend 
-    uint32_t  dentry_id  = (uint32_t)(intptr_t)dentry->extend; 
-
-    // get page index and offset in parent directory mapper
-    uint32_t  page_id    = dentry_id >> 7;
-    uint32_t  offset     = (dentry_id & 0x7F)<<5;
-
-#if DEBUG_FATFS_REMOVE_DENTRY & 1
-if( DEBUG_FATFS_REMOVE_DENTRY < cycle )
-printk("\n[%s] dentry_id %x / page_id %x / offset %x\n",
-__FUNCTION__, dentry_id, page_id, offset );
-#endif
-
-    // get extended pointer on page descriptor from parent directory mapper
-    page_xp  = mapper_remote_get_page( mapper_xp , page_id );
-
-    if ( page_xp == XPTR_NULL )
-    {
-        printk("\n[ERROR] in %s : cannot extend directory mapper\n", __FUNCTION__ );
-        return -1;
-    }
-        
-    // get pointers on page base
-    base_xp = ppm_page2base( page_xp );
-    base    = GET_PTR( base_xp );
-
-    // invalidate NORMAL entry in directory cache
-    base[offset] = 0xE5;
-
-    // invalidate LFN entries
-    while ( nb_lfn )
-    {
-        if (offset == 0)  // we must load page (page_id - 1)
-        {
-
-// check page_id 
-assert( (page_id > 0), "page_id and offset cannot be both 0\n" );
-
-            // copy the modified page to the IOC device
-            fatfs_move_page( page_xp , IOC_SYNC_WRITE );   
-
-            // get extended pointer on page descriptor from parent directory mapper
-            page_xp  = mapper_remote_get_page( mapper_xp , page_id );
-
-            if ( page_xp == XPTR_NULL )
-            {
-                printk("\n[ERROR] in %s : cannot access directory mapper\n", __FUNCTION__ );
-                return -1;
-            }
-        
-            // get pointers on page base
-            base_xp = ppm_page2base( page_xp );
-            base    = GET_PTR( base_xp );
-
-            // update offset
-            offset = 4096;
-        }
-
-        offset = offset - 32;
-
-// check for LFN entry
-assert( (fatfs_get_record( DIR_ATTR, base + offset ) == ATTR_LONG_NAME_MASK ),
-"this directory entry must be a LFN\n");
-
-        // invalidate LFN entry
-        base[offset] = 0xE5;
-
-        nb_lfn--;
-    }     
-
-    // copy the modified page to the IOC device
-    fatfs_move_page( page_xp , IOC_SYNC_WRITE );   
-    
-
-#if DEBUG_FATFS_REMOVE_DENTRY
-cycle = (uint32_t)hal_get_cycles();
-if( DEBUG_FATFS_REMOVE_DENTRY < cycle )
-printk("\n[%s] thread[%x,%x] exit / parent %s / child %s / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, dir_name, dentry->name, cycle );
-#endif
-
-    return 0;
-
-}  // end fatfs_remove_dentry
-
-
 //////////////////////////////////////////////////////////////////////////////////////////////
-// This static function scan the pages of a mapper containing a FAT32 directory, identified 
-// by the <mapper> argument, to find the directory entry identified by the <name> argument,
-// and return a pointer on the directory entry, described as and array of 32 bytes, and the
-// index of this entry in the FAT32 mapper, seen as an array of 32 bytes entries.
-// It is called by the fatfs_new_dentry() and fatfs_update_dentry() functions.
-// It must be called by a thread running in the cluster containing the mapper.
+// This static function scan the pages of a directory mapper, identified by the <mapper_xp> 
+// argument, to find the directory entry identified by the <name> argument, and returns
+// a pointer on the directory entry, described as an array of 32 bytes, and the index of 
+// this entry in the FAT32 mapper, seen as an array of 32 bytes entries. 
+// It makes a local copy of each directory entry to reduce the number of remote accesses.
+// It is called by the fatfs_new_dentry_from_mapper() function.
+// It can be called by a thread running in any cluster.
 //////////////////////////////////////////////////////////////////////////////////////////////
-// @ mapper    : [in]  local pointer on directory mapper.
+// @ mapper_xp : [in]  extended pointer on directory mapper.
 // @ name      : [in]  searched directory entry name.
 // @ entry     : [out] buffer for the pointer on the 32 bytes directory entry (when found).
@@ -1748,23 +1231,23 @@
 // @ return 0 if found / return 1 if not found / return -1 if mapper access error. 
 //////////////////////////////////////////////////////////////////////////////////////////////
-static error_t fatfs_scan_directory( mapper_t *  mapper,
+static error_t fatfs_scan_directory( xptr_t      mapper_xp,
                                      char     *  name,
                                      uint8_t  ** entry,
                                      uint32_t *  index )
 {
-    // Two embedded loops to scan the directory mapper:
-    // - scan the parent directory mapper pages 
-    // - scan the directory entries in each 4 Kbytes page
-
-// check parent_inode and child_inode
-assert( (mapper != NULL) , "mapper pointer is NULL\n" );
-assert( (name   != NULL ), "child name is undefined\n" );
-assert( (entry  != NULL ), "entry buffer undefined\n" );
+    uint8_t  buf[32];   // local buffer for one FAT32 directory entry
+
+// check arguments
+assert( (mapper_xp != XPTR_NULL) , "mapper pointer is NULL\n" );
+assert( (name      != NULL     ) , "child name is undefined\n" );
 
 #if DEBUG_FATFS_SCAN_DIRECTORY
-char       parent_name[CONFIG_VFS_MAX_NAME_LENGTH];
-uint32_t   cycle = (uint32_t)hal_get_cycles();
-thread_t * this  = CURRENT_THREAD;
-vfs_inode_get_name( XPTR( local_cxy , mapper->inode ) , parent_name );
+char          parent_name[CONFIG_VFS_MAX_NAME_LENGTH];
+uint32_t      cycle      = (uint32_t)hal_get_cycles();
+thread_t    * this       = CURRENT_THREAD;
+mapper_t    * mapper_ptr = GET_PTR( mapper_xp );
+cxy_t         mapper_cxy = GET_CXY( mapper_xp );
+vfs_inode_t * inode_ptr  = hal_remote_lpt( XPTR( mapper_cxy , &mapper_ptr->inode ) );
+vfs_inode_get_name( XPTR( mapper_cxy , inode_ptr ) , parent_name );
 if( DEBUG_FATFS_SCAN_DIRECTORY < cycle )
 printk("\n[%s]  thread[%x,%x] enter to search child <%s> in parent <%s> / cycle %d\n",
@@ -1777,8 +1260,6 @@
     char       lfn2[16];         // buffer for one partial cname
     char       lfn3[16];         // buffer for one partial cname
-    xptr_t     mapper_xp;        // extended pointer on mapper descriptor
-    xptr_t     page_xp;          // extended pointer on page descriptor
-    xptr_t     base_xp;          // extended pointer on page base
-    uint8_t  * base;             // local pointer on page base
+    xptr_t     page_xp;          // extended pointer on one page descriptor
+    xptr_t     base_xp;          // extended pointer on one page base
     uint8_t    attr;             // directory entry ATTR field
     uint8_t    ord;              // directory entry ORD field
@@ -1789,5 +1270,7 @@
     uint32_t   offset    = 0;    // byte offset in page
 
-    mapper_xp = XPTR( local_cxy , mapper );
+    // Two embedded loops to scan the directory mapper:
+    // - scan the parent directory mapper pages 
+    // - scan the directory entries in each 4 Kbytes page
 
     // scan the mapper pages
@@ -1795,5 +1278,5 @@
     {
         // get one page
-        page_xp = mapper_remote_get_page( mapper_xp , page_id );
+        page_xp = mapper_get_page( mapper_xp , page_id );
 
         if( page_xp == XPTR_NULL)
@@ -1804,7 +1287,6 @@
         // get page base
         base_xp = ppm_page2base( page_xp );
-        base    = (uint8_t *)GET_PTR( base_xp );
-
-#if (DEBUG_FATFS_SCAN_DIRECTORY & 0x1)
+
+#if (DEBUG_FATFS_SCAN_DIRECTORY & 1)
 if( DEBUG_FATFS_SCAN_DIRECTORY < cycle )
 mapper_display_page( mapper_xp , page_xp , 256 );
@@ -1813,6 +1295,10 @@
         while( (offset < 4096) && (found == 0) )
         {
-            attr = fatfs_get_record( DIR_ATTR , base + offset );   
-            ord  = fatfs_get_record( LDIR_ORD , base + offset );   
+            // makes a local copy of current directory entry  (32 bytes)
+            hal_remote_memcpy( XPTR( local_cxy , buf ) , base_xp + offset , 32 );
+
+            // get attr and ord from local buffer
+            attr = fatfs_get_record( DIR_ATTR , buf );   
+            ord  = fatfs_get_record( LDIR_ORD , buf );   
 
             if (ord == NO_MORE_ENTRY)                 // no more entry => break
@@ -1832,7 +1318,7 @@
                 seq = ord & 0x3;
                 lfn = (seq > lfn) ? seq : lfn;   
-                if      ( seq == 1 ) fatfs_get_name_from_long( base + offset, lfn1 );
-                else if ( seq == 2 ) fatfs_get_name_from_long( base + offset, lfn2 );
-                else if ( seq == 3 ) fatfs_get_name_from_long( base + offset, lfn3 );
+                if      ( seq == 1 ) fatfs_get_name_from_long( buf , lfn1 );
+                else if ( seq == 2 ) fatfs_get_name_from_long( buf , lfn2 );
+                else if ( seq == 3 ) fatfs_get_name_from_long( buf , lfn3 );
                 offset = offset + 32;
             }
@@ -1842,5 +1328,5 @@
                 if      ( lfn == 0 )
                 {
-                    fatfs_get_name_from_short( base + offset , cname );
+                    fatfs_get_name_from_short( buf , cname );
                 }
                 else if ( lfn == 1 )
@@ -1863,11 +1349,13 @@
                 if ( strcmp( name , cname ) == 0 )
                 {
+                    uint8_t * base = GET_PTR( base_xp );
                     *entry = base + offset;
-                    *index = ((page_id<<12) + offset)>>5; 
-                    found     = 1;
+                    *index = ( (page_id << 12) + offset ) >> 5; 
+                    found  = 1;
                 }
                 offset = offset + 32;
                 lfn    = 0;
             }
+
         }  // end loop on directory entries in page
 
@@ -1881,5 +1369,4 @@
 
 #if DEBUG_FATFS_SCAN_DIRECTORY
-cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_FATFS_SCAN_DIRECTORY < cycle )
 printk("\n[%s]  thread[%x,%x] exit / found child <%s> in <%s>\n",
@@ -1892,5 +1379,4 @@
 
 #if DEBUG_FATFS_SCAN_DIRECTORY
-cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_FATFS_SCAN_DIRECTORY < cycle )
 printk("\n[%s]  thread[%x,%x] exit / child <%s> in <%s> not found\n",
@@ -1909,193 +1395,1246 @@
 
 
-
-/////////////////////////////////////////////////////
-error_t fatfs_new_dentry( vfs_inode_t * parent_inode,
-                          char        * name,
-                          xptr_t        child_inode_xp )
-{
-    uint8_t      * entry;            // pointer on FAT32 directory entry (array of 32 bytes)
-    uint32_t       index;            // index of FAT32 directory entry in mapper
-    mapper_t     * mapper;           // pointer on directory mapper
-    uint32_t       cluster;          // directory entry cluster
-    uint32_t       size;             // directory entry size
-    bool_t         is_dir;           // directory entry type (file/dir)
-    xptr_t         root_xp;          // extended pointer on root of parent dentries
-    xptr_t         iter_xp;          // iterator for this list
-    cxy_t          child_inode_cxy;  // child inode cluster  
-    vfs_inode_t  * child_inode_ptr;  // child inode local pointer
-    xptr_t         dentry_xp;        // extended pointer on searched dentry descriptor
-    cxy_t          dentry_cxy;       // cluster identifier of dentry (must be local_cxy)
-    vfs_dentry_t * dentry_ptr;       // local pointer 
-    error_t        error;
-
-    char           parent_name[CONFIG_VFS_MAX_NAME_LENGTH];
-
-// check arguments
-assert( (parent_inode   != NULL)       , "parent_inode is NULL\n" );
-assert( (name           != NULL)       , "name is NULL\n" );
-assert( (child_inode_xp != XPTR_NULL ) , "child_inode is NULL\n" );
-
-    // get child inode cluster and local pointer
-    child_inode_cxy = GET_CXY( child_inode_xp );
-    child_inode_ptr = GET_PTR( child_inode_xp );
-
-    // build extended pointer on root of list of parent dentries
-    root_xp = XPTR( child_inode_cxy , &child_inode_ptr->parents );
-
-// check child inode has at least one parent
-assert( (xlist_is_empty( root_xp ) == false ), "child inode must have one parent\n");
-
-#if DEBUG_FATFS_NEW_DENTRY
+//////////////////////////////////////////////////////////////////////////////////////////
+//              FATFS debug functions 
+//////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////
+void fatfs_display_ctx( cxy_t cxy )
+{
+    // get pointer on local FATFS context
+    vfs_ctx_t   * vfs_ctx = &fs_context[FS_TYPE_FATFS];
+	fatfs_ctx_t * ctx     = hal_remote_lpt( XPTR( cxy , &vfs_ctx->extend ) );
+
+    uint32_t fat_sectors       = hal_remote_l32( XPTR( cxy , &ctx->fat_sectors_count ) );
+    uint32_t sector_size       = hal_remote_l32( XPTR( cxy , &ctx->bytes_per_sector ) );
+    uint32_t sec_per_clus      = hal_remote_l32( XPTR( cxy , &ctx->sectors_per_cluster ) );
+    uint32_t fat_lba           = hal_remote_l32( XPTR( cxy , &ctx->fat_begin_lba ) );
+    uint32_t data_lba          = hal_remote_l32( XPTR( cxy , &ctx->cluster_begin_lba ) );
+    uint32_t fsinfo_lba        = hal_remote_l32( XPTR( cxy , &ctx->fs_info_lba ) );
+    uint32_t root_dir_clus     = hal_remote_l32( XPTR( cxy , &ctx->root_dir_cluster ) );
+    uint32_t free_clusters     = hal_remote_l32( XPTR( cxy , &ctx->free_clusters ) );
+    uint32_t free_cluster_hint = hal_remote_l32( XPTR( cxy , &ctx->free_cluster_hint ) );
+    void   * fat_mapper        = hal_remote_lpt( XPTR( cxy , &ctx->fat_mapper ) );
+    void   * fs_info_buffer    = hal_remote_lpt( XPTR( cxy , &ctx->fs_info_buffer ) );
+
+    printk("\n*** FAT context in cluster %x\n" 
+           "- fat_sectors       = %d\n"
+           "- sector size       = %d\n"
+           "- cluster size      = %d\n"
+           "- fat_lba           = %x\n"
+           "- data_lba          = %x\n"
+           "- fsinfo_lba        = %x\n"
+           "- root_dir_cluster  = %x\n"
+           "- free_clusters     = %x\n"
+           "- free_cluster_hint = %x\n"
+           "- fat_mapper        = %x\n"
+           "- fs_info_buffer    = %x\n",
+           cxy,
+           fat_sectors,
+           sector_size,
+           sector_size * sec_per_clus,
+           fat_lba,
+           data_lba,
+           fsinfo_lba,
+           root_dir_clus,
+           free_clusters,
+           free_cluster_hint,
+           fat_mapper,
+           fs_info_buffer );
+
+}  // end fatfs_display_ctx()
+
+//////////////////////////////////////////
+void fatfs_display_fat( uint32_t  min_slot,
+                        uint32_t  nb_slots )
+{
+    // one FAT mapper page contains 1024 slots = 128 lines of 8 slots
+
+    uint32_t   page_id;
+    uint32_t   line;
+    cxy_t      fat_cxy;         // FAT cluster
+    mapper_t * mapper;          // local pointer on FAT mapper
+    xptr_t     mapper_xp;       // extended pointer on fat_mapper
+    uint32_t   min_cluster_id;  // index of min slot to be displayed
+    uint32_t   min_page_id;     // index of min page to be displayed
+    uint32_t   min_line_id;     // index of min line in min page ( < 128 )
+    uint32_t   max_cluster_id;  // index of max slot to be displayed
+    uint32_t   max_page_id;     // index of max page to be displayed
+    uint32_t   max_line_id;     // index of max line in max page ( < 128 )
+
+    // compute min values
+    min_cluster_id = min_slot & 0xFFFFFFF8;
+    min_line_id    = (min_cluster_id & 0x3FF) >> 3;
+    min_page_id    = min_cluster_id >> 10;
+
+    // compute max values
+    max_cluster_id = min_slot + nb_slots - 1;
+    max_line_id    = (max_cluster_id & 0x3FF) >> 3;
+    max_page_id    = max_cluster_id >> 10;
+
+    // get pointer on local FATFS context
+    vfs_ctx_t   * vfs_ctx       = &fs_context[FS_TYPE_FATFS];
+    fatfs_ctx_t * loc_fatfs_ctx = (fatfs_ctx_t *)vfs_ctx->extend;
+
+    // get FAT cluster
+    fat_cxy = CONFIG_VFS_ROOT_CXY;
+
+    // get pointers on FAT mapper (in FAT cluster)
+    mapper    = loc_fatfs_ctx->fat_mapper;
+    mapper_xp = XPTR( fat_cxy , mapper );
+
+    // get pointer on FATFS context in FAT cluster
+    fatfs_ctx_t * fat_fatfs_ctx = hal_remote_lpt( XPTR( fat_cxy , &vfs_ctx->extend ) );
+ 
+    // get current value of hint and free_clusters
+    uint32_t hint = hal_remote_l32( XPTR( fat_cxy , &fat_fatfs_ctx->free_cluster_hint ) );
+    uint32_t free = hal_remote_l32( XPTR( fat_cxy , &fat_fatfs_ctx->free_clusters ) );
+
+    printk("\n***** FAT mapper / cxy %x / free_clusters %x / hint %x\n", fat_cxy, free, hint );
+
+    // scan all pages as required by min_page_id and max_page_id
+    for( page_id = min_page_id ; page_id <= max_page_id ; page_id++ )
+    {
+        // get extended pointer on requested page descriptor in FAT mapper 
+        xptr_t page_xp = mapper_get_fat_page( mapper_xp , page_id );
+
+        // get extended pointer on requested page base
+        xptr_t     base_xp  = ppm_page2base( page_xp );
+
+        // compute min_line & max_line in current page
+        uint32_t min_line = (page_id == min_page_id) ? min_line_id : 0;
+        uint32_t max_line = (page_id == max_page_id) ? max_line_id : 127;
+
+        // loop on lines in current page
+        for( line = min_line ; line <= max_line ; line++ )
+        {
+            printk("%x : %X | %X | %X | %X | %X | %X | %X | %X\n",
+            (page_id << 10) + (line <<3 ),
+            hal_remote_l32( base_xp + ((line<<5)      ) ),
+            hal_remote_l32( base_xp + ((line<<5) + 4  ) ),
+            hal_remote_l32( base_xp + ((line<<5) + 8  ) ),
+            hal_remote_l32( base_xp + ((line<<5) + 12 ) ),
+            hal_remote_l32( base_xp + ((line<<5) + 16 ) ),
+            hal_remote_l32( base_xp + ((line<<5) + 20 ) ),
+            hal_remote_l32( base_xp + ((line<<5) + 24 ) ),
+            hal_remote_l32( base_xp + ((line<<5) + 28 ) ) );
+        }
+    }
+}  // end fatfs_display_fat()
+
+/////////////////////////////////////
+error_t fatfs_check_free_info( void )
+{
+    error_t       error;
+    fatfs_ctx_t * fatfs_ctx_ptr;              // local pointer on fatfs context in cluster 0
+    uint32_t      ctx_free_clusters;          // number of free clusters from fatfs context
+    uint32_t      ctx_free_cluster_hint;      // free cluster hint from fatfs context
+    uint32_t      ioc_free_clusters;          // number of free clusters from fatfs context
+    uint32_t      ioc_free_cluster_hint;      // free cluster hint from fatfs context
+    uint32_t      fs_info_lba;                // lba of FS_INFO sector on IOC device
+    uint8_t     * fs_info_buffer;             // local pointer on FS_INFO buffer in cluster 0
+    xptr_t        fs_info_buffer_xp;          // extended pointer on FS_INFO buffer in cluster 0
+    uint8_t       tmp_buf[512];               // 512 bytes temporary buffer 
+    xptr_t        tmp_buf_xp;                 // extended pointer on temporary buffer
+
+#if DEBUG_FATFS_SYNC_FSINFO
 uint32_t   cycle = (uint32_t)hal_get_cycles();
 thread_t * this  = CURRENT_THREAD;
-vfs_inode_get_name( XPTR( local_cxy , parent_inode ) , parent_name );
-if( DEBUG_FATFS_NEW_DENTRY < cycle )
+if( DEBUG_FATFS_SYNC_FSINFO < cycle )
+printk("\n[%s] thread[%x,%x] enter / cycle %d\n",
+__FUNCTION__ , this->process->pid, this->trdid, cycle );
+#endif
+
+    // get pointer on fatfs context in cluster 0
+    fatfs_ctx_ptr = hal_remote_lpt( XPTR( 0 , &fs_context[FS_TYPE_FATFS].extend ) );
+
+    // get "free_clusters" and "free_cluster_hint" from fatfs context in cluster 0
+    ctx_free_clusters     = hal_remote_l32( XPTR( 0 , &fatfs_ctx_ptr->free_clusters ) );
+    ctx_free_cluster_hint = hal_remote_l32( XPTR( 0 , &fatfs_ctx_ptr->free_cluster_hint ) );
+
+    // get fs_info_lba 
+    fs_info_lba = hal_remote_l32( XPTR( 0 , &fatfs_ctx_ptr->fs_info_lba ) );
+
+    // build extended pointer on temporary buffer
+    tmp_buf_xp = XPTR( local_cxy , tmp_buf );
+
+    // copy FS_INFO sector from IOC to local buffer
+    error = dev_ioc_sync_read( tmp_buf_xp , fs_info_lba , 1 );
+
+    if ( error )
+    {
+        printk("\n[ERROR] in %s : cannot access FS_INFO on IOC device\n", __FUNCTION__ );
+        return -1;
+    }
+
+    // get current values of "free_clusters" and "free_cluster_hint" from FS_INFO on IOC
+    ioc_free_clusters     = fatfs_get_remote_record( FS_FREE_CLUSTERS     , tmp_buf_xp );
+    ioc_free_cluster_hint = fatfs_get_remote_record( FS_FREE_CLUSTER_HINT , tmp_buf_xp );
+
+#if DEBUG_FATFS_SYNC_FSINFO
+if( DEBUG_FATFS_SYNC_FSINFO < cycle )
+printk("\n[%s] thread[%x,%x] / ctx_free %x / ioc_free %x / ctx_hint %x / ioc_hint %x\n",
+__FUNCTION__ , this->process->pid, this->trdid, 
+ctx_free_clusters, ioc_free_clusters, ctx_free_cluster_hint, ioc_free_cluster_hint );
+#endif
+
+    // check values
+    if( (ioc_free_clusters     != ctx_free_clusters) || 
+        (ioc_free_cluster_hint != ctx_free_cluster_hint) )
+    {
+        printk("\n[WARNING] in %s : unconsistent free clusters info\n"
+        " ioc_free %x / ctx_free %x / ioc_hint %x / ctx_hint %x\n",
+        __FUNCTION__, ioc_free_clusters, ctx_free_clusters,
+        ioc_free_cluster_hint, ctx_free_cluster_hint );
+
+        // get pointers on FS_INFO buffer in cluster 0
+        fs_info_buffer    = hal_remote_lpt( XPTR( 0 , &fatfs_ctx_ptr->fs_info_buffer ) );
+        fs_info_buffer_xp = XPTR( 0 , fs_info_buffer );
+
+        // update FS_INFO buffer in cluster 0 
+        fatfs_set_remote_record(FS_FREE_CLUSTERS    ,fs_info_buffer_xp,ctx_free_clusters );
+        fatfs_set_remote_record(FS_FREE_CLUSTER_HINT,fs_info_buffer_xp,ctx_free_cluster_hint);
+
+        // update the FS_INFO sector on IOC device
+        error = dev_ioc_sync_write( fs_info_buffer_xp , fs_info_lba , 1 );
+
+        if ( error )
+        {
+            printk("\n[ERROR] in %s : cannot update FS_INFO on IOC device\n", __FUNCTION__ );
+            return -1;
+        }
+    }
+
+#if DEBUG_FATFS_SYNC_FSINFO
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_FATFS_SYNC_FSINFO < cycle )
+printk("\n[%s] thread[%x,%x] exit / cycle %d\n",
+__FUNCTION__ , this->process->pid, this->trdid, cycle );
+#endif
+
+    return 0;
+
+}  // end fatfs_check_free_info()
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////
+// Generic API : the following functions are called by the kernel VFS
+//               and must be defined by all supported file systems.
+///////////////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////
+xptr_t  fatfs_ctx_alloc( cxy_t  cxy )
+{
+    kmem_req_t    req;
+
+    // allocate memory from remote cluster
+	req.type     = KMEM_KCM;
+	req.order    = bits_log2( sizeof(fatfs_ctx_t) );
+    req.flags    = AF_KERNEL | AF_ZERO;
+ 
+    return XPTR( cxy , kmem_remote_alloc( cxy , &req ) );
+
+}  //end faffs_ctx_alloc()
+
+///////////////////////////////////////////////
+error_t  fatfs_ctx_init( xptr_t  fatfs_ctx_xp )
+{
+    error_t       error;
+    kmem_req_t    req;
+    cxy_t         cxy;             // FATFS context cluster identifier
+    fatfs_ctx_t * fatfs_ctx_ptr;   // local pointer on FATFS context
+    uint8_t     * buffer;          // local pointer on 512 bytes buffer
+    xptr_t        buffer_xp;       // extended pointer on 512 bytes buffer
+    xptr_t        fat_mapper_xp;   // extended pointer on FAT mapper
+    mapper_t    * fat_mapper;      // local pointer on FAT mapper
+
+    // get FATFS context cluster and local pointer
+    cxy           = GET_CXY( fatfs_ctx_xp );        
+    fatfs_ctx_ptr = GET_PTR( fatfs_ctx_xp );
+    
+#if DEBUG_FATFS_CTX_INIT
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+thread_t * this  = CURRENT_THREAD;
+if( DEBUG_FATFS_CTX_INIT < cycle )
+printk("\n[%s] thread[%x,%x] enter for fatfs_ctx (%x,%x) / cycle %d\n",
+__FUNCTION__ , this->process->pid, this->trdid, cxy, fatfs_ctx_ptr , cycle );
+#endif
+
+    // allocate a 512 bytes buffer in remote cluster, used to store
+    // temporarily the BOOT sector, and permanently the FS_INFO sector
+	req.type    = KMEM_KCM;
+    req.order   = 9;                    // 512 bytes
+    req.flags   = AF_KERNEL | AF_ZERO;
+	buffer      = kmem_remote_alloc( cxy , &req );
+
+    if( buffer == NULL ) 
+    {
+        printk("\n[PANIC] in %s : cannot allocate buffer in cluster %x\n",
+        __FUNCTION__ , cxy );
+        return -1;
+    }
+     
+    // build extended pointer on buffer
+    buffer_xp = XPTR( cxy , buffer );
+
+    // load the BOOT record from device
+    error = dev_ioc_sync_read( buffer_xp , 0 , 1 );
+
+    if ( error )
+    {
+        printk("\n[PANIC] in %s : cannot access boot record\n", __FUNCTION__ );
+        return -1;
+    }
+
+#if (DEBUG_FATFS_CTX_INIT & 0x1)
+uint8_t bs[256];
+hal_remote_memcpy( XPTR( local_cxy , bs ) , buffer_xp , 256 );
+if( DEBUG_FATFS_CTX_INIT < cycle )
+putb( "boot record", bs , 256 );
+#endif
+
+    // get sector size from boot record
+    uint32_t sector_size = fatfs_get_remote_record( BPB_BYTSPERSEC , buffer_xp );
+
+    if ( sector_size != 512 )
+    {
+        printk("\n[PANIC] in %s : sector size must be 512 bytes\n", __FUNCTION__ );
+        return -1;
+    }
+
+    // get cluster size from boot record
+    uint32_t nb_sectors = fatfs_get_remote_record( BPB_SECPERCLUS , buffer_xp );
+
+    if ( nb_sectors != 8 )
+    {
+        printk("\n[PANIC] in %s : cluster size must be 8 sectors\n", __FUNCTION__ );
+        return -1;
+    }
+
+    // get number of FAT copies from boot record
+    uint32_t nb_fats = fatfs_get_remote_record( BPB_NUMFATS , buffer_xp );
+
+    if ( nb_fats != 1 )
+    {
+        printk("\n[PANIC] in %s : number of FAT copies must be 1\n", __FUNCTION__ );
+        return -1;
+    }
+
+    // get number of sectors in FAT from boot record
+    uint32_t fat_sectors = fatfs_get_remote_record( BPB_FAT32_FATSZ32 , buffer_xp );
+
+    if ( (fat_sectors & 0xF) != 0 )
+    {
+        printk("\n[PANIC] in %s : FAT size not multiple of 16 sectors\n", __FUNCTION__ );
+        return -1;
+    }
+
+    // get root cluster from boot record
+    uint32_t root_cluster = fatfs_get_remote_record( BPB_FAT32_ROOTCLUS , buffer_xp );
+
+    if ( root_cluster != 2 ) 
+    {
+        printk("\n[PANIC] in %s : root cluster index must be 2\n", __FUNCTION__ );
+        return -1;
+    }
+
+    // get FAT lba from boot record
+    uint32_t fat_lba = fatfs_get_remote_record( BPB_RSVDSECCNT , buffer_xp );
+
+    // get FS_INFO sector lba from boot record
+    uint32_t fs_info_lba = fatfs_get_remote_record( BPB_FAT32_FSINFO , buffer_xp );
+
+    // load the FS_INFO record from device
+    error = dev_ioc_sync_read( buffer_xp , fs_info_lba , 1 );
+
+    if ( error )
+    {
+        printk("\n[PANIC] in %s : cannot access FS_INFO record\n", __FUNCTION__ );
+        return -1;
+    }
+
+    // get free_clusters number from FS_INFO record
+    uint32_t free_clusters = fatfs_get_remote_record( FS_FREE_CLUSTERS , buffer_xp );
+
+    if ( free_clusters >= fat_sectors << 7 )
+    {
+        printk("\n[PANIC] in %s : unconsistent free_clusters\n", __FUNCTION__ );
+        return -1;
+    }
+
+    // get free_cluster_hint from FS_INFO record
+    uint32_t free_hint = fatfs_get_remote_record( FS_FREE_CLUSTER_HINT , buffer_xp );
+
+    if ( free_hint >= fat_sectors << 7 )
+    {
+        printk("\n[PANIC] in %s : unconsistent free_cluster_hint\n", __FUNCTION__ );
+        return -1;
+    }
+
+    // allocate a mapper for the FAT in remote cluster
+    fat_mapper_xp  = mapper_create( cxy , FS_TYPE_FATFS );
+
+    // get local pointer on FAT mapper
+    fat_mapper = GET_PTR( fat_mapper_xp );
+
+    if ( fat_mapper == NULL )
+    {
+        printk("\n[PANIC] in %s : no memory for FAT mapper in cluster %x\n",
+        __FUNCTION__ , cxy );
+        return -1;
+    }
+
+#if (DEBUG_FATFS_CTX_INIT & 0x1)
+if( DEBUG_FATFS_CTX_INIT < cycle )
+printk("\n[%s] sector_size %d / nb_sectors %d / fat_sectors %x / hint %x\n",
+__FUNCTION__, sector_size, nb_sectors, fat_sectors, free_hint );
+#endif
+
+    // the inode field is NULL for the FAT mapper
+    hal_remote_spt( XPTR( cxy , &fat_mapper->inode ) , NULL );
+
+    // initialize the FATFS context
+    hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->fat_begin_lba       ), fat_lba );
+    hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->fat_sectors_count   ), fat_sectors );
+    hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->bytes_per_sector    ), sector_size );
+    hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->sectors_per_cluster ), nb_sectors );
+    hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->cluster_begin_lba   ), fat_lba + fat_sectors );
+    hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->root_dir_cluster    ), 2 );
+    hal_remote_spt( XPTR( cxy , &fatfs_ctx_ptr->fat_mapper          ), fat_mapper );
+    hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->fs_info_lba         ), fs_info_lba );
+    hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->free_clusters       ), free_clusters );
+    hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->free_cluster_hint   ), free_hint );
+    hal_remote_spt( XPTR( cxy , &fatfs_ctx_ptr->fs_info_buffer      ), buffer );
+
+    // initialize FATFS lock
+    remote_rwlock_init( XPTR( cxy , &fatfs_ctx_ptr->lock ) , LOCK_FATFS_FAT );
+
+#if DEBUG_FATFS_CTX_INIT
+if( DEBUG_FATFS_CTX_INIT < cycle )
+printk("\n[%s]  thread[%x,%x] exit for fatfs_ctx (%x,%x)\n",
+__FUNCTION__, this->process->pid, this->trdid, cxy, fatfs_ctx_ptr );
+#endif
+
+	return 0;
+
+}  // end fatfs_ctx_init()
+
+//////////////////////////////////////////////
+void fatfs_ctx_destroy( xptr_t  fatfs_ctx_xp )
+{
+    kmem_req_t   req;
+    mapper_t   * fat_mapper;
+    uint8_t    * fs_info_buffer;
+
+    // get FATFS context cluster and local pointer
+    fatfs_ctx_t * fatfs_ctx_ptr = GET_PTR( fatfs_ctx_xp );
+    cxy_t         fatfs_ctx_cxy = GET_CXY( fatfs_ctx_xp );
+
+    // get pointer on FAT mapper
+    fat_mapper = hal_remote_lpt( XPTR( fatfs_ctx_cxy , &fatfs_ctx_ptr->fat_mapper ) );
+
+    // release FAT mapper
+    mapper_destroy( XPTR( fatfs_ctx_cxy , fat_mapper ) );
+
+    // get pointer on FS_INFO buffer
+    fs_info_buffer = hal_remote_lpt( XPTR( fatfs_ctx_cxy , &fatfs_ctx_ptr->fs_info_buffer ) );
+
+    // release FS_INFO buffer
+    req.type = KMEM_KCM;
+    req.ptr  = fs_info_buffer;
+    kmem_remote_free( fatfs_ctx_cxy , &req );
+
+    // release FATFS context descriptor
+    req.type = KMEM_KCM;
+    req.ptr  = fatfs_ctx_ptr;
+    kmem_remote_free( fatfs_ctx_cxy , &req );
+
+}  // end fatfs_ctx_destroy()
+
+/////////////////////////////////////////////////////////
+error_t fatfs_add_dentry( xptr_t         parent_inode_xp,
+                          vfs_dentry_t * dentry_ptr )
+{
+    error_t       error;
+    vfs_inode_t * parent_inode_ptr;   // parent inode local pointer
+    cxy_t         parent_cxy;         // pparent inode cluster
+    xptr_t        child_inode_xp;     // extended pointer on child inode 
+    cxy_t         child_cxy;          // child inode cluster
+    vfs_inode_t * child_inode_ptr;    // child inode local pointer
+    uint32_t      length;             // dentry name length
+    uint32_t      nb_lfn;             // number or required LFN
+    char          sfn[11];            // buffer for SFN name
+    uint8_t       checksum;           // name checksum 
+    mapper_t    * mapper_ptr;         // local pointer on parent directory mapper
+    xptr_t        mapper_xp;          // extended pointer on parent directory mapper
+    uint32_t      size;               // child inode size
+    uint32_t      type;               // child inode type
+    void        * extend;             // child inode extension
+    uint32_t      cluster_id;         // child inode first cluster_id in FATFS 
+
+    char          child_name[CONFIG_VFS_MAX_NAME_LENGTH];
+
+    uint8_t       buf[32];            // local buffer for one FAT32 directory entry
+ 
+// check arguments
+assert( (parent_inode_xp != XPTR_NULL) , "parent_inode_xp argument is NULL\n" );
+assert( (dentry_ptr      != NULL)      , "dentry_ptr argument is NULL\n" );
+
+    // get directory inode cluster and local pointer 
+    parent_cxy       = GET_CXY( parent_inode_xp );
+    parent_inode_ptr = GET_PTR( parent_inode_xp );
+
+    // get extended pointers on child inode
+    child_inode_xp  = hal_remote_l64( XPTR( parent_cxy , &dentry_ptr->child_xp ) );
+    child_cxy       = GET_CXY( child_inode_xp );
+    child_inode_ptr = GET_PTR( child_inode_xp );
+
+    // get a local copy of the child name
+    vfs_inode_get_name( child_inode_xp  , child_name );
+
+#if DEBUG_FATFS_ADD_DENTRY
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+thread_t * this  = CURRENT_THREAD;
+char       parent_name[CONFIG_VFS_MAX_NAME_LENGTH];
+vfs_inode_get_name( parent_inode_xp , parent_name );
+if( DEBUG_FATFS_ADD_DENTRY < cycle )
+printk("\n[%s]  thread[%x,%x] enter for <%s> in <%s> directory / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, child_name, parent_name, cycle );
+#endif
+
+    // get pointers on parent directory mapper
+    mapper_ptr = hal_remote_lpt( XPTR( parent_cxy , &parent_inode_ptr->mapper ) );
+    mapper_xp  = XPTR( parent_cxy , mapper_ptr );
+
+#if (DEBUG_FATFS_ADD_DENTRY & 1)
+mapper_display_page( mapper_xp , 0 , 512 );
+#endif
+
+    // get relevant infos from child inode
+    type       = hal_remote_l32( XPTR( child_cxy , &child_inode_ptr->type ) );
+    size       = hal_remote_l32( XPTR( child_cxy , &child_inode_ptr->size ) );
+    extend     = hal_remote_lpt( XPTR( child_cxy , &child_inode_ptr->extend ) );
+    cluster_id = (uint32_t)(intptr_t)extend;
+
+    // analyse child name
+    error = fatfs_name_format( child_name,
+                               &length,
+                               &nb_lfn,
+                               sfn,
+                               &checksum );
+    if ( error )
+    {
+        printk("\n[ERROR] in %s : dentry name > 31 bytes\n", __FUNCTION__ );
+        return -1;
+    }
+                               
+    // Search end of directory with two embedded loops:
+    // - scan the pages in the mapper
+    // - scan the entries in each page to find NO_MORE_ENTRY
+
+    xptr_t     page_xp;                 // extended pointer on page descriptor
+    xptr_t     base_xp;                 // extended pointer on page base
+
+    // initialise loop variables
+    uint32_t   page_id = 0;             // page index in mapper
+    uint32_t   offset  = 0;             // position in page
+    uint32_t   found   = 0;             // NO_MORE_ENTRY found
+
+    // loop on pages in mapper
+    while ( found == 0 )
+    {
+        // get extended pointer on page descriptor in mapper 
+        page_xp  = mapper_get_page( mapper_xp , page_id );
+
+        if ( page_xp == XPTR_NULL )
+        {
+            printk("\n[ERROR] in %s : cannot extend directory mapper\n",
+            __FUNCTION__ );
+            return -1;
+        }
+        
+        // get pointer on page base
+        base_xp = ppm_page2base( page_xp );
+
+        // loop on directory entries in this page
+        while ( (offset < 4096) && (found == 0) )
+        {
+            if ( fatfs_get_remote_record( LDIR_ORD, (base_xp + offset) ) == NO_MORE_ENTRY )
+            {
+                found = 1;
+            }  
+            else
+            {
+                offset = offset + 32;
+            }
+        }  // end loop on entries
+
+        if ( found == 0 )
+        {
+            page_id++;
+            offset = 0;
+        }
+    }  // end loop on pages
+
+#if (DEBUG_FATFS_ADD_DENTRY & 1)
+if( DEBUG_FATFS_ADD_DENTRY < cycle )
+printk("\n[%s]  thread[%x,%x] found NO_MORE_ENTRY : page_id %d / offset %d\n",
+__FUNCTION__, this->process->pid, this->trdid, page_id, offset );
+#endif
+
+    // Modify the directory mapper: depending on the name length,
+    // the new child requires to write (3, 4, or 5) directory entries.
+    // We build one complete directory entry in a local buffer
+    // before copying it the remote mapper. We use use a 5 steps FSM 
+    // (one state per entry to be written), that is traversed as :
+    // LFN3 -> LFN2 -> LFN1 -> NORMAL -> NOMORE 
+    // At most two pages are modified:
+    // - the page containing the NO_MORE_ENTRY is always modified
+    // - the following page can be modified if the name spread on two pages.
+
+    uint32_t step;          // FSM state
+
+    if      ( nb_lfn == 1 ) step = 3;
+    else if ( nb_lfn == 2 ) step = 4;
+    else if ( nb_lfn == 3 ) step = 5;
+    
+    uint32_t   i;           // byte index in one 32 bytes directory
+    uint32_t   c;           // character index in name
+
+    while ( step )   
+    {
+        // this block is only executed when the new name spread 
+        // on two pages, and we need to access a new page in mapper
+        if ( offset >= 4096 )
+        {
+            // copy the modified page to IOC device
+            error = fatfs_move_page( page_xp , IOC_SYNC_WRITE );   
+
+            if ( error )
+            {
+                printk("\n[ERROR] in %s : cannot update directory on device\n",
+                __FUNCTION__ );
+                return -1;
+            }
+
+            // get the next page in directory mapper
+            page_xp  = mapper_get_page( mapper_xp , page_id + 1 );
+
+            if ( page_xp == XPTR_NULL )
+            {
+                printk("\n[ERROR] in %s : cannot extend directory mapper\n",
+                __FUNCTION__ );
+                return -1;
+            }
+        
+            // get pointer on page base
+            base_xp = ppm_page2base( page_xp );
+            
+            // update offset
+            offset = 0;
+        }
+
+#if (DEBUG_FATFS_ADD_DENTRY & 1)
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_FATFS_ADD_DENTRY < cycle )
+printk("\n[%s] FSM step = %d / offset = %x / nb_lfn = %d / cycle %d\n",
+__FUNCTION__, step, offset, nb_lfn, cycle );
+#endif
+
+        // write one FATFS directory entry (32 bytes) per iteration
+        switch ( step )
+        {
+            case 5:   // write LFN3 entry
+            {
+                c = 26;
+
+                // write 32 bytes in local buf
+                for ( i = 0 ; i < 32 ; i++ )
+                {
+                    if (i == 0)
+                    {
+                        if ( nb_lfn == 3) buf[i] = 0x43;
+                        else              buf[i] = 0x03;
+                    }
+                    else if ( ( ((i >= 1 ) && (i<=10) && ((i&1)==1))   ||
+                                ((i >= 14) && (i<=25) && ((i&1)==0))   ||
+                                ((i >= 28) && (i<=31) && ((i&1)==0)) ) &&
+                              ( c < length ) )
+                    {
+                                          buf[i] = child_name[c];
+                                          c++;
+                    }
+                    else if (i == 11)     buf[i] = 0x0F;
+                    else if (i == 13)     buf[i] = checksum;
+                    else                  buf[i] = 0x00;
+                }
+
+                // copy 32 bytes from local buffer to remote mapper
+                hal_remote_memcpy( base_xp + offset , XPTR( local_cxy  , buf ) , 32 );
+
+                step--;
+                break;
+            }
+            case 4:   // write LFN2 entry  
+            {
+                c = 13;
+
+                // write 32 bytes in local buf
+                for ( i = 0 ; i < 32 ; i++ )
+                {
+                    if (i == 0)
+                    {
+                        if ( nb_lfn == 2) buf[i] = 0x42;
+                        else              buf[i] = 0x02;
+                    }
+                    else if ( ( ((i >= 1 ) && (i<=10) && ((i&1)==1))   ||
+                                ((i >= 14) && (i<=25) && ((i&1)==0))   ||
+                                ((i >= 28) && (i<=31) && ((i&1)==0)) ) &&
+                              ( c < length ) )
+                    {
+                                          buf[i] = child_name[c];
+                                          c++;
+                    }
+                    else if (i == 11)     buf[i] = 0x0F;
+                    else if (i == 13)     buf[i] = checksum;
+                    else                  buf[i] = 0x00;
+                }
+
+                // copy 32 bytes from local buffer to remote mapper
+                hal_remote_memcpy( base_xp + offset , XPTR( local_cxy  , buf ) , 32 );
+
+                step--;
+                break;
+            }
+            case 3:   // Write LFN1 entry   
+            {
+                c = 0;
+
+                // write 32 bytes in local buf
+                for ( i = 0 ; i < 32 ; i++ )
+                {
+                    if (i == 0)
+                    {
+                        if ( nb_lfn == 1) buf[i] = 0x41;
+                        else              buf[i] = 0x01;
+                    }
+                    else if ( ( ((i >= 1 ) && (i<=10) && ((i&1)==1))   ||
+                                ((i >= 14) && (i<=25) && ((i&1)==0))   ||
+                                ((i >= 28) && (i<=31) && ((i&1)==0)) ) &&
+                              ( c < length ) )
+                    {
+                                          buf[i] = child_name[c];
+                                          c++;
+                    }
+                    else if (i == 11)     buf[i] = 0x0F;
+                    else if (i == 13)     buf[i] = checksum;
+                    else                  buf[i] = 0x00;
+                }
+
+                // copy 32 bytes from local buffer to remote mapper
+                hal_remote_memcpy( base_xp + offset , XPTR( local_cxy  , buf ) , 32 );
+
+                step--;
+                break;
+            }
+            case 2:   // write NORMAL entry     
+            {
+                // write 32 bytes in local buf
+                for ( i = 0 ; i < 32 ; i++ )
+                {
+                    if      ( i < 11 )                               // 8.3 SFN
+                    {
+                                          buf[i] = sfn[i];
+                    }
+                    else if (i == 11)                                // ATTR
+                    {
+                        if (type == INODE_TYPE_DIR)  buf[i] = 0x10;
+                        else                         buf[i] = 0x20;
+                    }
+                    else if (i == 20)     buf[i] = cluster_id>>16;   // cluster.B2
+                    else if (i == 21)     buf[i] = cluster_id>>24;   // cluster.B3
+                    else if (i == 26)     buf[i] = cluster_id>>0;    // cluster.B0
+                    else if (i == 27)     buf[i] = cluster_id>>8;    // cluster.B1
+                    else if (i == 28)     buf[i] = size>>0;          // size.B0
+                    else if (i == 29)     buf[i] = size>>8;          // size.B1
+                    else if (i == 30)     buf[i] = size>>16;         // size.B2
+                    else if (i == 31)     buf[i] = size>>24;         // size.B3
+                    else                  buf[i] = 0x00;
+                }
+
+                // copy 32 bytes from local buffer to remote mapper
+                hal_remote_memcpy( base_xp + offset , XPTR( local_cxy  , buf ) , 32 );
+
+                // set the dentry "extend" field 
+                hal_remote_spt( XPTR( parent_cxy , &dentry_ptr->extend ),
+                                (void *)(intptr_t)(((page_id << 12) + offset) >> 5 ) );
+                step--;
+                break;
+            }
+            case 1:   // write NOMORE entry  
+            {
+                hal_remote_s32( base_xp + offset , 0 );
+
+                step--;
+                break;
+            }
+
+        } // end switch step
+
+        offset += 32;
+
+    } // end while     
+
+    // copy the modified page to the IOC device
+    error = fatfs_move_page( page_xp , IOC_SYNC_WRITE );   
+
+    if ( error )
+    {
+        printk("\n[ERROR] in %s : cannot update directory on device\n",
+        __FUNCTION__ );
+        return -1;
+    }
+
+#if DEBUG_FATFS_ADD_DENTRY
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_FATFS_ADD_DENTRY < cycle )
+printk("\n[%s]  thread[%x,%x] exit for <%s> in <%s> directory\n",
+__FUNCTION__, this->process->pid, this->trdid, child_name, parent_name );
+#endif
+
+#if (DEBUG_FATFS_ADD_DENTRY & 1)
+mapper_display_page( mapper_xp , 0 , 512 );
+#endif
+
+    return 0;
+
+}  // end fatfs_add_dentry()
+
+////////////////////////////////////////////////////////////
+error_t fatfs_remove_dentry( xptr_t         parent_inode_xp,
+                             vfs_dentry_t * dentry_ptr )
+{
+    error_t       error;
+    vfs_inode_t * parent_inode_ptr;   // parent inode local pointer
+    cxy_t         parent_cxy;         // pparent inode cluster
+    xptr_t        child_inode_xp;     // extended pointer on child inode 
+    cxy_t         child_cxy;          // child inode cluster
+    vfs_inode_t * child_inode_ptr;    // child inode local pointer
+    xptr_t        mapper_xp;          // extended pointer on mapper
+    mapper_t    * mapper_ptr;         // local pointer on mapper
+    xptr_t        page_xp;            // extended pointer on mapper page descriptor
+    xptr_t        base_xp;            // extended pointer on mapper page base
+    uint32_t      dentry_id;          // FAT32 directory entry index
+    uint32_t      page_id;            // page index in directory mapper
+    uint32_t      offset;             // offset in this mapper page
+
+    char          child_name[CONFIG_VFS_MAX_NAME_LENGTH];
+
+// check arguments
+assert( (parent_inode_xp != XPTR_NULL) , "parent_inode_xp argument is NULL\n" );
+assert( (dentry_ptr      != NULL)      , "dentry_ptr argument is NULL\n" );
+
+    // get directory inode cluster and local pointer 
+    parent_cxy       = GET_CXY( parent_inode_xp );
+    parent_inode_ptr = GET_PTR( parent_inode_xp );
+
+    // get extended pointers on child inode
+    child_inode_xp  = hal_remote_l64( XPTR( parent_cxy , &dentry_ptr->child_xp ) );
+    child_cxy       = GET_CXY( child_inode_xp );
+    child_inode_ptr = GET_PTR( child_inode_xp );
+
+    // get a local copy of the child name
+    vfs_inode_get_name( child_inode_xp  , child_name );
+
+#if DEBUG_FATFS_REMOVE_DENTRY
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+thread_t * this  = CURRENT_THREAD;
+char       parent_name[CONFIG_VFS_MAX_NAME_LENGTH];
+vfs_inode_get_name( parent_inode_xp , parent_name );
+if( DEBUG_FATFS_REMOVE_DENTRY < cycle )
+printk("\n[%s]  thread[%x,%x] enter for <%s> in <%s> directory / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, child_name, parent_name, cycle );
+#endif
+
+    // get pointers on directory mapper
+    mapper_ptr = hal_remote_lpt( XPTR( parent_cxy , &parent_inode_ptr->mapper ) );
+    mapper_xp  = XPTR( parent_cxy , mapper_ptr );
+
+    // compute number of LFN entries
+    uint32_t nb_lfn;
+    uint32_t name_length = strlen( child_name );
+
+    if      ( name_length <= 13 ) nb_lfn  = 1;
+    else if ( name_length <= 26 ) nb_lfn  = 2;
+    else                          nb_lfn  = 3;
+
+    // We must invalidate (2,3,4) 32 bytes entries:
+    // - the NORMAL entry, registered in dentry->extend.
+    // - the (1,2,3) preceding LFN entries.
+    // At most two pages are modified:
+    // - the page containing the NORMAL entry is always modified.
+    // - the preceding page is modified when the name spread on two pages. 
+
+    // get NORMAL entry index from dentry extension 
+    dentry_id  = (uint32_t)(intptr_t)hal_remote_lpt( XPTR( parent_cxy , &dentry_ptr->extend ) ); 
+
+    // get page index and offset in parent directory mapper
+    page_id  = dentry_id >> 7;
+    offset   = (dentry_id & 0x7F)<<5;
+
+#if DEBUG_FATFS_REMOVE_DENTRY & 1
+if( DEBUG_FATFS_REMOVE_DENTRY < cycle )
+printk("\n[%s] dentry_id %x / page_id %x / offset %x\n",
+__FUNCTION__, dentry_id, page_id, offset );
+#endif
+
+    // get extended pointer on page descriptor 
+    page_xp  = mapper_get_page( mapper_xp , page_id );
+
+    if ( page_xp == XPTR_NULL )
+    {
+        printk("\n[ERROR] in %s : cannot access directory mapper\n", __FUNCTION__ );
+        return -1;
+    }
+        
+    // get extended pointer on page base
+    base_xp = ppm_page2base( page_xp );
+
+    // invalidate NORMAL entry in directory cache
+    hal_remote_sb( base_xp + offset , 0xE5 );
+
+    // invalidate LFN entries
+    while ( nb_lfn )
+    {
+        // this block is only executed when the removed name
+        // spread on two mapper pages
+        if (offset == 0)  // we must load page (page_id - 1)
+        {
+            // copy the modified page to the IOC device
+            error = fatfs_move_page( page_xp , IOC_SYNC_WRITE );   
+
+            if ( error )
+            {
+                printk("\n[ERROR] in %s : cannot update directory on device\n",
+                __FUNCTION__ );
+                return -1;
+            }
+
+            // get extended pointer on page descriptor 
+            page_xp  = mapper_get_page( mapper_xp , page_id );
+
+            if ( page_xp == XPTR_NULL )
+            {
+                printk("\n[ERROR] in %s : cannot access directory mapper\n", __FUNCTION__ );
+                return -1;
+            }
+        
+            // get extended pointer on page base
+            base_xp = ppm_page2base( page_xp );
+
+            // update offset
+            offset = 4096;
+        }
+
+        offset = offset - 32;
+
+// check for LFN entry
+assert( (fatfs_get_remote_record( DIR_ATTR, base_xp + offset ) == ATTR_LONG_NAME_MASK ),
+"this directory entry must be a LFN\n");
+
+        // invalidate LFN entry
+        hal_remote_sb( base_xp + offset , 0xE5 );
+
+        nb_lfn--;
+    }     
+
+    // copy the modified page to the IOC device
+    error = fatfs_move_page( page_xp , IOC_SYNC_WRITE );   
+    
+    if ( error )
+    {
+        printk("\n[ERROR] in %s : cannot update directory on device\n",
+        __FUNCTION__ );
+        return -1;
+    }
+
+#if DEBUG_FATFS_REMOVE_DENTRY
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_FATFS_REMOVE_DENTRY < cycle )
+printk("\n[%s] thread[%x,%x] exit for <%s> in <%s> directory\n",
+__FUNCTION__, this->process->pid, this->trdid, child_name, parent_name );
+#endif
+
+    return 0;
+
+}  // end fatfs_remove_dentry
+
+/////////////////////////////////////////////////////////////////////
+error_t fatfs_new_dentry_from_mapper( xptr_t         parent_inode_xp,
+                                      vfs_dentry_t * dentry_ptr )
+{
+    uint32_t       cluster_id;        // directory entry first FATFS cluster
+    uint32_t       size;              // directory entry size
+    bool_t         is_dir;            // directory entry type (file/dir)
+    cxy_t          parent_cxy;        // parent inode cluster  
+    vfs_inode_t  * parent_inode_ptr;  // parent inode local pointer
+    mapper_t     * parent_mapper;     // pointer on parent directory mapper
+    xptr_t         child_inode_xp;    // extended pointer on child inode
+    cxy_t          child_cxy;         // child inode cluster  
+    vfs_inode_t  * child_inode_ptr;   // child inode local pointer
+    error_t        error;
+
+    uint8_t        buf[32];           // FAT32 directory entry local copy
+
+    char           parent_name[CONFIG_VFS_MAX_NAME_LENGTH];     // local parent name copy
+    char           child_name[CONFIG_VFS_MAX_NAME_LENGTH];      // local child name copy
+
+// check arguments
+assert( (parent_inode_xp != XPTR_NULL)  , "parent_inode_xp is NULL\n" );
+assert( (dentry_ptr      != NULL ) , "dentry_ptr is NULL\n" );
+
+    // get parent inode cluster and local pointer
+    parent_cxy       = GET_CXY( parent_inode_xp );
+    parent_inode_ptr = GET_PTR( parent_inode_xp );
+
+    // get child inode cluster and pointers
+    child_inode_xp  = hal_remote_l64( XPTR( parent_cxy , &dentry_ptr->child_xp ) );
+    child_cxy       = GET_CXY( child_inode_xp );
+    child_inode_ptr = GET_PTR( child_inode_xp );
+
+    // get child and parent names
+    vfs_inode_get_name( parent_inode_xp , parent_name );
+    vfs_inode_get_name( child_inode_xp  , child_name );
+
+#if DEBUG_FATFS_NEW_DENTRY_FROM
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+thread_t * this  = CURRENT_THREAD;
+if( DEBUG_FATFS_NEW_DENTRY_FROM < cycle )
 printk("\n[%s]  thread[%x,%x] enter for child <%s> in parent <%s> / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, name , parent_name , cycle );
+__FUNCTION__, this->process->pid, this->trdid, child_name , parent_name , cycle );
 #endif
 
     // get local pointer on parent mapper
-    mapper = parent_inode->mapper;
-
-    // get pointer and index in mapper for searched directory entry 
-    error  = fatfs_scan_directory( mapper, name , &entry , &index );
-
-    // return non fatal error if not found
-    if( error )
-    {
-        vfs_inode_get_name( XPTR( local_cxy , parent_inode ) , parent_name );
-        printk("\n[ERROR] in %s : cannot find <%s> entry in <%s> directory mapper\n",
-        __FUNCTION__, name , parent_name, name );
-        return -1;
-    }
-  
-
-    // get relevant infos from FAT32 directory entry
-    cluster = (fatfs_get_record( DIR_FST_CLUS_HI , entry ) << 16) |
-              (fatfs_get_record( DIR_FST_CLUS_LO , entry )      ) ;
-    is_dir  = (fatfs_get_record( DIR_ATTR        , entry ) & ATTR_DIRECTORY);
-    size    =  fatfs_get_record( DIR_FILE_SIZE   , entry );
-
-    // scan list of parent dentries to search the parent_inode
-    bool_t found = false;
-    XLIST_FOREACH( root_xp , iter_xp )
-    {
-        // get pointers on dentry
-        dentry_xp  = XLIST_ELEMENT( iter_xp , vfs_dentry_t , parents );
-        dentry_cxy = GET_CXY( dentry_xp );
-        dentry_ptr = GET_PTR( dentry_xp );
-
-        // get local pointer on current parent directory inode
-        vfs_inode_t * current = hal_remote_lpt( XPTR( dentry_cxy , &dentry_ptr->parent ) );
-
-        // check if current parent is the searched parent
-        if( XPTR( dentry_cxy , current ) == XPTR( local_cxy , parent_inode ) )
-        {
-            found = true;
-            break;
-        }
-    }
-
-    if( found == false )
-    { 
-        vfs_inode_get_name( XPTR( local_cxy , parent_inode ) , parent_name );
-        printk("\n[ERROR] in %s : cannot find <%s> directory in list of parents for <%s>\n",
-        __FUNCTION__, parent_name, name );
-        return -1;
-    }
+    parent_mapper = hal_remote_lpt( XPTR( parent_cxy , &parent_inode_ptr->mapper ) );
+
+    // try to get pointer and index of directory entry in mapper 
+    uint8_t      * entry = NULL;
+    uint32_t       index = 0;
+
+    error  = fatfs_scan_directory( XPTR( parent_cxy , parent_mapper ),
+                                   child_name, 
+                                   &entry,
+                                   &index );
+    
+    // an error can be non fatal, for a new (created) entry
+    if( error )  return -1;
+
+    // get local copy of found directory entry
+    hal_remote_memcpy( XPTR( local_cxy  , buf ),
+                       XPTR( parent_cxy , entry ), 32 );
+
+    // get relevant infos from directory entry
+    cluster_id = (fatfs_get_record( DIR_FST_CLUS_HI , buf ) << 16) |
+                 (fatfs_get_record( DIR_FST_CLUS_LO , buf )      ) ;
+    is_dir     = (fatfs_get_record( DIR_ATTR        , buf ) & ATTR_DIRECTORY );
+    size       =  fatfs_get_record( DIR_FILE_SIZE   , buf );
 
     // update the child inode "type", "size", and "extend" fields
     vfs_inode_type_t type = (is_dir) ? INODE_TYPE_DIR : INODE_TYPE_FILE;
 
-    hal_remote_s32( XPTR( child_inode_cxy , &child_inode_ptr->type   ) , type );
-    hal_remote_s32( XPTR( child_inode_cxy , &child_inode_ptr->size   ) , size );
-    hal_remote_s32( XPTR( child_inode_cxy , &child_inode_ptr->extend ) , cluster );
+    hal_remote_s32( XPTR( child_cxy , &child_inode_ptr->type   ) , type );
+    hal_remote_s32( XPTR( child_cxy , &child_inode_ptr->size   ) , size );
+    hal_remote_s32( XPTR( child_cxy , &child_inode_ptr->extend ) , cluster_id );
 
     // update the dentry "extend" field
-    dentry_ptr->extend = (void *)(intptr_t)index;
-
-#if DEBUG_FATFS_NEW_DENTRY
+    hal_remote_spt( XPTR( parent_cxy , &dentry_ptr->extend ) , (void *)(intptr_t)index );
+
+#if DEBUG_FATFS_NEW_DENTRY_FROM
 cycle = (uint32_t)hal_get_cycles();
-if( DEBUG_FATFS_NEW_DENTRY < cycle )
-printk("\n[%s]  thread[%x,%x] exit for <%s> in <%s> / cluster_id %x / size %d  / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, name, parent_name, cluster, size,  cycle );
-#endif
-
-
-#if (DEBUG_FATFS_NEW_DENTRY & 1)
-if( DEBUG_FATFS_NEW_DENTRY < cycle )
-{
-    fatfs_display_fat( 0 , 0 , 64 );
-    fatfs_display_fat( cluster >> 10 ,  (cluster & 0x3FF) , 32 );
-}
+if( DEBUG_FATFS_NEW_DENTRY_FROM < cycle )
+printk("\n[%s]  thread[%x,%x] exit for <%s> in <%s> / cluster_id %x / size %d\n",
+__FUNCTION__, this->process->pid, this->trdid, child_name, parent_name, cluster_id, size );
+#endif
+
+#if (DEBUG_FATFS_NEW_DENTRY_FROM & 1)
+if( DEBUG_FATFS_NEW_DENTRY_FROM < cycle )
+fatfs_display_fat( cluster_id , 32 );
 #endif
 
     return 0;
 
-}  // end fatfs_new_dentry()
-
-//////////////////////////////////////////////////
-error_t fatfs_update_dentry( vfs_inode_t  * inode,
-                             vfs_dentry_t * dentry,
-                             uint32_t       size )
-{
-    uint8_t  * entry;    // pointer on FAT32 directory entry (array of 32 bytes)
-    uint32_t   index;    // index of FAT32 directory entry in mapper 
-    mapper_t * mapper;   // pointer on directory mapper
-    error_t    error;
-
-    char       dir_name[CONFIG_VFS_MAX_NAME_LENGTH];
+}  // end fatfs_new_dentry_from_mapper()
+
+///////////////////////////////////////////////////////////////////
+error_t fatfs_new_dentry_to_mapper( xptr_t         parent_inode_xp,
+                                    vfs_dentry_t * dentry_ptr )
+{
+    uint32_t       cluster_id;        // directory entry cluster
+    cxy_t          parent_cxy;        // parent inode cluster identifier  
+    vfs_inode_t  * parent_inode_ptr;  // child inode local pointer
+    xptr_t         child_inode_xp;    // extended pointer on child inode
+    cxy_t          child_cxy;         // child inode cluster identifier  
+    vfs_inode_t  * child_inode_ptr;   // child inode local pointer
+    error_t        error;
+
+    char           parent_name[CONFIG_VFS_MAX_NAME_LENGTH];
+    char           child_name[CONFIG_VFS_MAX_NAME_LENGTH];
 
 // check arguments
-assert( (inode  != NULL) , "inode is NULL\n" );
-assert( (dentry != NULL) , "dentry is NULL\n" );
+assert( (parent_inode_xp != XPTR_NULL) , "parent_inode_xp argument is NULL\n" );
+assert( (dentry_ptr      != NULL)      , "dentry_ptr argument NULL\n" );
+
+    // get child inode cluster and local pointer
+    parent_cxy       = GET_CXY( parent_inode_xp );
+    parent_inode_ptr = GET_PTR( parent_inode_xp );
+
+    // get child inode cluster and pointers
+    child_inode_xp  = hal_remote_l64( XPTR( parent_cxy , &dentry_ptr->child_xp ) );
+    child_cxy       = GET_CXY( child_inode_xp );
+    child_inode_ptr = GET_PTR( child_inode_xp );
+
+    // get child and parent names
+    vfs_inode_get_name( parent_inode_xp , parent_name );
+    vfs_inode_get_name( child_inode_xp  , child_name );
+
+#if DEBUG_FATFS_NEW_DENTRY_TO_MAP
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+thread_t * this  = CURRENT_THREAD;
+if( DEBUG_FATFS_NEW_DENTRY_TO_MAP < cycle )
+printk("\n[%s]  thread[%x,%x] enter for child <%s> in parent <%s> / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, child_name , parent_name , cycle );
+#endif
+
+    // 1. allocate one FATFS cluster (update FAT and FSINFO) 
+    error = fatfs_cluster_alloc( 0 , &cluster_id );
+
+    if( error )
+    {
+        printk("\n[ERROR] in %s : cannot find a free cluster_id\n",
+        __FUNCTION__ );
+        return -1;
+    }
+       
+    // 2. register cluster_id in inode descriptor
+    hal_remote_spt( XPTR( child_cxy , &child_inode_ptr->extend ),
+                    (void*)(intptr_t)cluster_id );
+    
+    // 3. introduce dentry in the directory mapper
+    error = fatfs_add_dentry( parent_inode_xp , dentry_ptr );
+        
+    if( error )
+    {
+        printk("\n[ERROR] in %s : cannot update parent directory mapper\n",
+        __FUNCTION__ );
+        // TODO release cluster_id [AG]
+        return -1;
+    }
+
+#if DEBUG_FATFS_NEW_DENTRY_TO_MAP
+if( DEBUG_FATFS_NEW_DENTRY_TO_MAP < cycle )
+printk("\n[%s]  thread[%x,%x] exit for <%s> in <%s> / cluster_id %x\n",
+__FUNCTION__, this->process->pid, this->trdid, child_name, parent_name, cluster_id );
+#endif
+
+    return 0;
+
+}  // end fatfs_new_dentry_to mapper()
+
+
+////////////////////////////////////////////////////////////
+error_t fatfs_update_dentry( xptr_t         parent_inode_xp,
+                             vfs_dentry_t * dentry_ptr )
+{
+    cxy_t         parent_cxy;        // parent directory cluster identifier
+    vfs_inode_t * parent_inode_ptr;  // extended pointer on parent directory inode
+    mapper_t    * parent_mapper_ptr; // local pointer on parent directory mapper
+    xptr_t        parent_mapper_xp;  // extended pointer on parent directory mapper
+    xptr_t        child_inode_xp;    // extended pointer on child inode
+    cxy_t         child_cxy;         // child inode cluster identifier
+    vfs_inode_t * child_inode_ptr;   // extended pointer on child inode
+
+    uint32_t      current_size;      // current size in directory mapper 
+    uint32_t      new_size;          // new size (from child inode) 
+    
+    uint32_t      entry_id;          // directory entry index in parent directory mapper
+    uint32_t      page_id;           // page_index in parent directory mapper
+    uint32_t      offset;            // directory entry offset in page
+    xptr_t        page_xp;           // extended pointer on page descriptor
+    xptr_t        base_xp;           // extended pointer on page base 
+    xptr_t        entry_xp;          // extended pointer on directory entry
+
+    error_t       error;
+
+    char       parent_name[CONFIG_VFS_MAX_NAME_LENGTH];
+    char       child_name[CONFIG_VFS_MAX_NAME_LENGTH];
+
+// check arguments
+assert( (parent_inode_xp  != XPTR_NULL) , "parent_inode_xp argument is NULL\n" );
+assert( (dentry_ptr       != NULL)      , "dentry_ptr argument is NULL\n" );
+
+    // get parent directory cluster ans local pointer
+    parent_inode_ptr = GET_PTR( parent_inode_xp );
+    parent_cxy       = GET_CXY( parent_inode_xp );
+
+    // get extended pointer on child inode
+    child_inode_xp  = hal_remote_l64( XPTR( parent_cxy , &dentry_ptr->child_xp ) );
+
+    // get child and parent names
+    vfs_inode_get_name( parent_inode_xp , parent_name );
+    vfs_inode_get_name( child_inode_xp  , child_name );
 
 #if DEBUG_FATFS_UPDATE_DENTRY
 uint32_t   cycle = (uint32_t)hal_get_cycles();
 thread_t * this  = CURRENT_THREAD;
-vfs_inode_get_name( XPTR( local_cxy , inode ) , dir_name );
 if( DEBUG_FATFS_UPDATE_DENTRY < cycle )
-printk("\n[%s]  thread[%x,%x] enter for <%s/%s> / size %d / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, dir_name, dentry->name, size, cycle );
-#endif
-
-    // get local pointer on mapper
-    mapper = inode->mapper;
-
-    // get pointer and index in mapper for searched directory entry
-    error  = fatfs_scan_directory( mapper, dentry->name , &entry , &index );
-
-    if( error )
-    { 
-        vfs_inode_get_name( XPTR( local_cxy , inode ) , dir_name );
-        printk("\n[ERROR] in %s : cannot find <%s> in parent mapper <%s>\n",
-        __FUNCTION__, dentry->name, dir_name );
-        return -1;
-    }
-
-    // get current size value
-    uint32_t current_size = fatfs_get_record( DIR_FILE_SIZE , entry );
+printk("\n[%s]  thread[%x,%x] enter for <%s> in <%s> / new_size %d / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, child_name, parent_name, new_size, cycle );
+#endif
+
+    // get child inode cluster and local pointer
+    child_cxy       = GET_CXY( child_inode_xp );
+    child_inode_ptr = GET_PTR( child_inode_xp );
+ 
+    // get size from child inode
+    new_size = hal_remote_l32( XPTR( child_cxy , &child_inode_ptr->size ) );
+ 
+    // get local and extended pointers on parent directory mapper
+    parent_mapper_ptr = hal_remote_lpt( XPTR( parent_cxy , &parent_inode_ptr->mapper ) );
+    parent_mapper_xp  = XPTR( parent_cxy , parent_mapper_ptr );
+
+    // get directory entry index from dentry extension
+    entry_id = (intptr_t)hal_remote_lpt( XPTR( parent_cxy , &dentry_ptr->extend ) );
+
+    // get page index and offset in parent directory mapper
+    page_id  = entry_id >> 7;
+    offset   = (entry_id & 0x7F) << 5;
+
+    // get extended pointers on page descriptor and page base
+    page_xp = mapper_get_page( parent_mapper_xp , page_id );     
+    base_xp = ppm_page2base( page_xp ); 
+
+    // build extended pointer on directory entry
+    entry_xp = base_xp + offset;
+
+    // get current size from directory mapper
+    current_size = fatfs_get_remote_record( DIR_FILE_SIZE , entry_xp );
 
     // update dentry in mapper & device only if required
-    if( size != current_size )
+    if( new_size != current_size )
     {
         // set size field in FAT32 directory entry
-        fatfs_set_record( DIR_FILE_SIZE , entry , size );
-
-        // get pointer on modified page base
-        void * base = (void *)((intptr_t)entry & (~CONFIG_PPM_PAGE_MASK)); 
-
-        // get extended pointer on modified page descriptor
-        xptr_t page_xp = ppm_base2page( XPTR( local_cxy , base ) );
-
-        // synchronously update the modified page on device
+        fatfs_set_remote_record( DIR_FILE_SIZE , entry_xp , new_size );
+
+        // synchronously update the modified mapper page on device
         error = fatfs_move_page( page_xp , IOC_SYNC_WRITE );
 
         if( error )
         { 
-            vfs_inode_get_name( XPTR( local_cxy , inode ) , dir_name );
             printk("\n[ERROR] in %s : cannot update parent directory <%s> on device\n",
-            __FUNCTION__, dir_name );
+            __FUNCTION__, parent_name );
             return -1;
         }
@@ -2105,6 +2644,6 @@
 cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_FATFS_UPDATE_DENTRY < cycle )
-printk("\n[%s]  thread[%x,%x] exit / updated size for <%s/%s> / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, dir_name, dentry->name, cycle );
+printk("\n[%s]  thread[%x,%x] exit for <%s> in <%s> directory / size %d / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, parent_name, child->name, new_size, cycle );
 #endif
 
@@ -2168,5 +2707,5 @@
     {
         // get one page from mapper
-        page_xp = mapper_remote_get_page( mapper_xp , page_id );
+        page_xp = mapper_get_page( mapper_xp , page_id );
 
         if( page_xp == XPTR_NULL) return -1;
@@ -2273,12 +2812,22 @@
 }  // end fatfs_get_user_dir()
 
-///////////////////////////////////////////////
-error_t fatfs_sync_inode( vfs_inode_t * inode )
-{
+///////////////////////////////////////////
+error_t fatfs_sync_inode( xptr_t inode_xp )
+{
+    cxy_t         inode_cxy;      // remote inode cluster
+    vfs_inode_t * inode_ptr;      // remote inode local pointer
+    mapper_t    * mapper;         // remote inode mapper local pointer
+    uint32_t      size;           // remote inode size in bytes
+    uint32_t      type;           // remote inode type
+    xptr_t        rt_xp;          // extended pointer on mapper radix tree
+    uint32_t      npages;         // number of pages in mapper
+    uint32_t      page_id;        // current page index in mapper
+    xptr_t        page_xp;        // extended pointer on current page
+    page_t      * page_ptr;       // local pointer on current page
+    uint32_t      flags;          // current page flags
+    error_t       error;
 
 // check inode pointer and cluster index
-assert( (inode != NULL)                  , "inode pointer undefined\n" );
-assert( (inode->mapper != NULL )         , "mapper pointer undefined\n" );
-assert( (inode->type == INODE_TYPE_FILE) , "inode must be a file\n" );     
+assert( (inode_xp != XPTR_NULL)          , "inode pointer undefined\n" );
 
 #if DEBUG_FATFS_SYNC_INODE
@@ -2286,5 +2835,5 @@
 uint32_t   cycle = (uint32_t)hal_get_cycles();
 thread_t * this  = CURRENT_THREAD;
-vfs_inode_get_name( XPTR( local_cxy , inode ) , name );
+vfs_inode_get_name( inode_xp , name );
 if( DEBUG_FATFS_SYNC_INODE < cycle )
 printk("\n[%s] thread[%x,%x] enter for <%s> / cycle %d\n",
@@ -2292,40 +2841,49 @@
 #endif
 
-    error_t    error;
-    mapper_t * mapper;
-    page_t   * page;
-    uint32_t   page_id;
-
-    // get mapper from inode 
-    mapper = inode->mapper;
-
-    // compute max number of pages in mapper from file size
-    uint32_t size  = inode->size;
-    uint32_t pages = size >> CONFIG_PPM_PAGE_SHIFT;
-    if( size & CONFIG_PPM_PAGE_MASK ) pages++; 
+    // get inode cluster and local pointer
+    inode_cxy = GET_CXY( inode_xp );
+    inode_ptr = GET_PTR( inode_xp );
+
+    //get inode mapper pointer
+    mapper    = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->mapper ) );
+
+assert( (mapper != NULL) , "mapper pointer is NULL\n" );     
+
+    // get inode type and size
+    size = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->size ) );
+    type = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->type ) );
+
+assert( (type == INODE_TYPE_FILE) , "inode is not a file\n" );     
+
+    // compute number of pages
+    npages = size >> CONFIG_PPM_PAGE_SHIFT;
+    if( size & CONFIG_PPM_PAGE_MASK ) npages++; 
          
-    // get pointer on mapper radix tree
-    grdxt_t * rt = &mapper->rt;
+    // build pointers on mapper radix tree
+    rt_xp = XPTR( inode_cxy , &mapper->rt );
 
     // scan all pages
-    for( page_id = 0 ; page_id < pages ; page_id++ )
+    for( page_id = 0 ; page_id < npages ; page_id++ )
     {
         // get page descriptor from mapper
-        page = grdxt_lookup( rt , page_id );
+        page_xp = grdxt_remote_lookup( rt_xp , page_id );
 
         // check all existing pages
-        if ( page != NULL )
-        {
-            if ( page->flags & PG_DIRTY )
+        if ( page_xp != XPTR_NULL )
+        {
+            // get page cluster and local pointer
+            page_ptr = GET_PTR( page_xp );
+
+            // get page flags
+            flags = hal_remote_l32( XPTR( inode_cxy , &page_ptr->flags ) );
+   
+            if ( flags & PG_DIRTY )
             {
 
 #if (DEBUG_FATFS_SYNC_INODE & 1)
 if( DEBUG_FATFS_SYNC_INODE < cycle )
-printk("\n[%s] thread[%x,%x] synchronizes page %d from <%s> mapper to IOC device\n",
+printk("\n[%s] thread[%x,%x] synchronizes page %d of <%s> mapper to IOC device\n",
 __FUNCTION__, page_id, name );
 #endif
-                // build extended pointer on page descriptor
-                xptr_t page_xp = XPTR( local_cxy , page );
-
                 // move page from mapper to device
                 error = fatfs_move_page( page_xp , IOC_WRITE );
@@ -2342,6 +2900,6 @@
 cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_FATFS_SYNC_INODE < cycle )
-printk("\n[%s] thread[%x,%x] exit for <%s> / cycle %d\n",
-__FUNCTION__ , this->process->pid, this->trdid, name, cycle );
+printk("\n[%s] thread[%x,%x] exit for <%s>\n",
+__FUNCTION__ , this->process->pid, this->trdid, name );
 #endif
 
@@ -2349,13 +2907,19 @@
 
 }  // end fatfs_sync_inode()
-
-
-
-
-
 
 //////////////////////////////
 error_t fatfs_sync_fat( void )
 {
+
+    fatfs_ctx_t * fatfs_ctx; 
+    cxy_t         fat_cxy;
+    mapper_t    * mapper_ptr;
+    xptr_t        mapper_xp;
+    uint32_t      start_page_id;
+    uint32_t      found_page_id;
+    page_t      * page_ptr;
+    xptr_t        page_xp;
+    uint32_t      flags;
+    error_t       error;
 
 #if DEBUG_FATFS_SYNC_FAT
@@ -2366,36 +2930,35 @@
 __FUNCTION__ , this->process->pid, this->trdid, cycle );
 #endif
-
-    uint32_t   page_id;
-    error_t    error;
-
-    // get FAT mapper pointers an cluster 
-    fatfs_ctx_t * fatfs_ctx  = fs_context[FS_TYPE_FATFS].extend;
-    xptr_t        mapper_xp  = fatfs_ctx->fat_mapper_xp;
-    cxy_t         mapper_cxy = GET_CXY( mapper_xp );
-    mapper_t    * mapper_ptr = GET_PTR( mapper_xp );
-
-    // compute max number of 4 Kbytes pages in FAT mapper 
-    // TODO : this could be improved (see fatfs.h) [AG]
-    uint32_t   pages = fatfs_ctx->fat_sectors_count >> 3;
-         
+    
+    // get FAT cluster
+    fat_cxy = CONFIG_VFS_ROOT_CXY;
+    
+    // get FAT mapper pointers  
+    fatfs_ctx  = fs_context[FS_TYPE_FATFS].extend;
+    mapper_ptr = fatfs_ctx->fat_mapper;
+    mapper_xp  = XPTR( fat_cxy , mapper_ptr );
+
     // get pointers on remote FAT mapper radix tree
     grdxt_t  * rt_ptr = &mapper_ptr->rt;
-    xptr_t     rt_xp  = XPTR( mapper_cxy , rt_ptr );
-
-    // scan all pages
-    for( page_id = 0 ; page_id < pages ; page_id++ )
-    {
-        // get extended pointer on page descriptor from FAT mapper
-        xptr_t page_xp = grdxt_remote_lookup( rt_xp , page_id );
-
-        // check all existing pages
-        if ( page_xp != XPTR_NULL )
-        {
-            page_t * page_ptr = GET_PTR( page_xp );
-            uint32_t flags    = hal_remote_l32( XPTR( mapper_cxy , &page_ptr->flags ) );
-
-            if ( flags & PG_DIRTY )
-            {
+    xptr_t     rt_xp  = XPTR( fat_cxy , rt_ptr );
+
+    // initialise page_id
+    start_page_id = 0;
+
+    // scan FAT mapper
+    while( 1 )
+    {
+        // get one page
+        page_xp = grdxt_remote_get_first( rt_xp , start_page_id , &found_page_id );
+
+        // exit loop when no more page found 
+        if ( page_xp != XPTR_NULL ) break;
+
+        // get page flags
+        page_ptr = GET_PTR( page_xp );
+        flags    = hal_remote_l32( XPTR( fat_cxy , &page_ptr->flags ) );
+
+        if ( flags & PG_DIRTY )
+        {
 
 #if (DEBUG_FATFS_SYNC_FAT & 1)
@@ -2404,13 +2967,16 @@
 __FUNCTION__, page_id );
 #endif
-                // move page from mapper to device
-                error = fatfs_move_page( page_xp , IOC_SYNC_WRITE );
-
-                if ( error )  return -1;
-
-                // reset page dirty flag
-                ppm_page_undo_dirty( page_xp );
-            }
-        }
+            // move page from mapper to device
+            error = fatfs_move_page( page_xp , IOC_SYNC_WRITE );
+
+            if ( error )  return -1;
+
+            // reset page dirty flag
+            ppm_page_undo_dirty( page_xp );
+        }
+
+        // update loop variable
+        start_page_id = found_page_id + 1;
+
     }  // end loop on pages
 
@@ -2418,6 +2984,6 @@
 cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_FATFS_SYNC_FAT < cycle )
-printk("\n[%s] thread[%x,%x] exit / cycle %d\n",
-__FUNCTION__ , this->process->pid, this->trdid, cycle );
+printk("\n[%s] thread[%x,%x] exit\n",
+__FUNCTION__ , this->process->pid, this->trdid );
 #endif
 
@@ -2425,250 +2991,19 @@
 
 }  // end fatfs_sync_fat()
-
-////////////////////////////////////
-error_t fatfs_sync_free_info( void )
-{
-    error_t       error;
-    fatfs_ctx_t * fatfs_ctx_ptr;              // local pointer on fatfs context in cluster 0
-    uint32_t      ctx_free_clusters;          // number of free clusters from fatfs context
-    uint32_t      ctx_free_cluster_hint;      // free cluster hint from fatfs context
-    uint32_t      ioc_free_clusters;          // number of free clusters from fatfs context
-    uint32_t      ioc_free_cluster_hint;      // free cluster hint from fatfs context
-    uint32_t      fs_info_lba;                // lba of FS_INFO sector on IOC device
-    uint8_t     * fs_info_buffer;             // local pointer on FS_INFO buffer in cluster 0
-    xptr_t        fs_info_buffer_xp;          // extended pointer on FS_INFO buffer in cluster 0
-    uint8_t       tmp_buf[512];               // 512 bytes temporary buffer 
-    xptr_t        tmp_buf_xp;                 // extended pointer on temporary buffer
-
-#if DEBUG_FATFS_SYNC_FSINFO
-uint32_t   cycle = (uint32_t)hal_get_cycles();
-thread_t * this  = CURRENT_THREAD;
-if( DEBUG_FATFS_SYNC_FSINFO < cycle )
-printk("\n[%s] thread[%x,%x] enter / cycle %d\n",
-__FUNCTION__ , this->process->pid, this->trdid, cycle );
-#endif
-
-    // get pointer on fatfs context in cluster 0
-    fatfs_ctx_ptr = hal_remote_lpt( XPTR( 0 , &fs_context[FS_TYPE_FATFS].extend ) );
-
-    // get "free_clusters" and "free_cluster_hint" from fatfs context in cluster 0
-    ctx_free_clusters     = hal_remote_l32( XPTR( 0 , &fatfs_ctx_ptr->free_clusters ) );
-    ctx_free_cluster_hint = hal_remote_l32( XPTR( 0 , &fatfs_ctx_ptr->free_cluster_hint ) );
-
-    // get fs_info_lba 
-    fs_info_lba = hal_remote_l32( XPTR( 0 , &fatfs_ctx_ptr->fs_info_lba ) );
-
-    // build extended pointer on temporary buffer
-    tmp_buf_xp = XPTR( local_cxy , tmp_buf );
-
-    // copy FS_INFO sector from IOC to local buffer
-    error = dev_ioc_move_data( IOC_SYNC_READ , tmp_buf_xp , fs_info_lba , 1 );
-
-    if ( error )
-    {
-        printk("\n[ERROR] in %s : cannot access FS_INFO on IOC device\n", __FUNCTION__ );
-        return -1;
-    }
-
-    // get current values of "free_clusters" and "free_cluster_hint" from FS_INFO on IOC
-    ioc_free_clusters     = fatfs_get_remote_record( FS_FREE_CLUSTERS     , tmp_buf_xp );
-    ioc_free_cluster_hint = fatfs_get_remote_record( FS_FREE_CLUSTER_HINT , tmp_buf_xp );
-
-#if DEBUG_FATFS_SYNC_FSINFO
-if( DEBUG_FATFS_SYNC_FSINFO < cycle )
-printk("\n[%s] thread[%x,%x] / ctx_free %x / ioc_free %x / ctx_hint %x / ioc_hint %x\n",
-__FUNCTION__ , this->process->pid, this->trdid, 
-ctx_free_clusters, ioc_free_clusters, ctx_free_cluster_hint, ioc_free_cluster_hint );
-#endif
-
-    // check values
-    if( (ioc_free_clusters     != ctx_free_clusters) || 
-        (ioc_free_cluster_hint != ctx_free_cluster_hint) )
-    {
-        printk("\n[WARNING] in %s : unconsistent free clusters info\n"
-        " ioc_free %x / ctx_free %x / ioc_hint %x / ctx_hint %x\n",
-        __FUNCTION__, ioc_free_clusters, ctx_free_clusters,
-        ioc_free_cluster_hint, ctx_free_cluster_hint );
-
-        // get pointers on FS_INFO buffer in cluster 0
-        fs_info_buffer    = hal_remote_lpt( XPTR( 0 , &fatfs_ctx_ptr->fs_info_buffer ) );
-        fs_info_buffer_xp = XPTR( 0 , fs_info_buffer );
-
-        // update FS_INFO buffer in cluster 0 
-        fatfs_set_remote_record(FS_FREE_CLUSTERS    ,fs_info_buffer_xp,ctx_free_clusters );
-        fatfs_set_remote_record(FS_FREE_CLUSTER_HINT,fs_info_buffer_xp,ctx_free_cluster_hint);
-
-        // update the FS_INFO sector on IOC device
-        error = dev_ioc_move_data( IOC_SYNC_WRITE , fs_info_buffer_xp , fs_info_lba , 1 );
-
-        if ( error )
-        {
-            printk("\n[ERROR] in %s : cannot update FS_INFO on IOC device\n", __FUNCTION__ );
-            return -1;
-        }
-    }
-
-#if DEBUG_FATFS_SYNC_FSINFO
-cycle = (uint32_t)hal_get_cycles();
-if( DEBUG_FATFS_SYNC_FSINFO < cycle )
-printk("\n[%s] thread[%x,%x] exit / cycle %d\n",
-__FUNCTION__ , this->process->pid, this->trdid, cycle );
-#endif
-
-    return 0;
-
-}  // end fatfs_sync_free_info()
-
-//////////////////////////////////////////////////////////
-error_t fatfs_cluster_alloc( uint32_t * searched_cluster )
-{
-    error_t       error;
-    uint32_t      page_id;        // page index in FAT mapper
-    uint32_t      slot_id;        // slot index in page (1024 slots per page)
-    uint32_t      cluster;        // first free cluster index in FAT
-    uint32_t      free_clusters;  // total number of free clusters
-    vfs_ctx_t   * vfs_ctx;        // local pointer on VFS context (same in all clusters)
-    fatfs_ctx_t * loc_fatfs_ctx;  // local pointer on local FATFS context
-    fatfs_ctx_t * fat_fatfs_ctx;  // local pointer on FATFS context in FAT cluster
-    xptr_t        fat_mapper_xp;  // extended pointer on FAT mapper
-    cxy_t         fat_cxy;        // Fat mapper cluster identifier
-    xptr_t        page_xp;        // extended pointer on current page descriptor in mapper
-    xptr_t        slot_xp;        // extended pointer on FAT slot defined by hint
-    xptr_t        lock_xp;        // extended pointer on lock protecting free clusters info
-    xptr_t        hint_xp;        // extended pointer on free_cluster_hint in FAT cluster
-    xptr_t        free_xp;        // extended pointer on free_clusters_number in FAT cluster
-
-#if DEBUG_FATFS_CLUSTER_ALLOC
-uint32_t   cycle = (uint32_t)hal_get_cycles();
-thread_t * this  = CURRENT_THREAD;
-if( DEBUG_FATFS_CLUSTER_ALLOC < cycle )
-printk("\n[%s] thread[%x,%x] enter / cycle = %d\n",
-__FUNCTION__, this->process->pid, this->trdid, cycle );
-#endif
-
-    // get local pointer on VFS context (same in all clusters)
-    vfs_ctx = &fs_context[FS_TYPE_FATFS];
-
-    // get local pointer on local FATFS context
-    loc_fatfs_ctx = vfs_ctx->extend;
-
-    // get extended pointer on FAT mapper
-    fat_mapper_xp  = loc_fatfs_ctx->fat_mapper_xp;
-
-    // get FAT cluster
-    fat_cxy = GET_CXY( fat_mapper_xp );
-    
-    // get local pointer on FATFS context in FAT cluster 
-    fat_fatfs_ctx = hal_remote_lpt( XPTR( fat_cxy , &vfs_ctx->extend ) );
-
-    // build relevant extended pointers on free clusters info in mapper cluster 
-    lock_xp = XPTR( fat_cxy , &fat_fatfs_ctx->lock );
-    hint_xp = XPTR( fat_cxy , &fat_fatfs_ctx->free_cluster_hint );
-    free_xp = XPTR( fat_cxy , &fat_fatfs_ctx->free_clusters );
-
-    // take the FAT lock in write mode
-    remote_rwlock_wr_acquire( lock_xp );
-
-    // get hint and free_clusters values from FATFS context in FAT cluster
-    cluster       = hal_remote_l32( hint_xp ) + 1;
-    free_clusters = hal_remote_l32( free_xp );
-        
-#if (DEBUG_FATFS_CLUSTER_ALLOC & 1)
-if( DEBUG_FATFS_CLUSTER_ALLOC < cycle )
-printk("\n[%s] thread[%x,%x] get free info : hint %x / free_clusters %x\n",
-__FUNCTION__, this->process->pid, this->trdid, (cluster - 1), free_clusters );
-#endif
-
-    // check "free_clusters"
-    if ( free_clusters == 0 )
-    {
-        printk("\n[ERROR] in %s : no more free FATFS clusters\n", __FUNCTION__ );
-        remote_rwlock_wr_release( lock_xp );
-        return -1;
-    }
-    else if ( free_clusters < CONFIG_VFS_FREE_CLUSTERS_MIN )
-    {
-        printk("\n[WARNING] in %s : only %n free FATFS clusters\n", 
-        __FUNCTION__, CONFIG_VFS_FREE_CLUSTERS_MIN );
-    }
-
-    // get page index & slot index for selected cluster 
-    page_id  = cluster >> 10;
-    slot_id  = cluster & 0x3FF;
-
-    // get relevant page descriptor from FAT mapper
-    page_xp = mapper_remote_get_page( fat_mapper_xp , page_id );
-
-    if( page_xp == XPTR_NULL )
-    {
-        printk("\n[ERROR] in %s : cannot acces FAT mapper\n", __FUNCTION__ );
-        remote_rwlock_wr_release( lock_xp );
-        return -1;
-    }
-
-    // build extended pointer on selected cluster slot in FAT mapper
-    slot_xp = ppm_page2base( page_xp ) + (slot_id << 2);
-         
-    // check selected cluster actually free
-    if( hal_remote_l32( slot_xp ) != FREE_CLUSTER )
-    { 
-        printk("\n[ERROR] in %s : selected cluster %x not free\n", __FUNCTION__, cluster );
-        remote_rwlock_wr_release( lock_xp );
-        return -1;
-    }
-
-    // update free cluster info in FATFS context and in FS_INFO sector
-    error = fatfs_free_clusters_decrement( XPTR( fat_cxy , fat_fatfs_ctx ) , cluster );
-
-    if( error )
-    { 
-        printk("\n[ERROR] in %s : cannot update free cluster info\n", __FUNCTION__ );
-        remote_rwlock_wr_release( lock_xp );
-        return -1;
-    }
-
-    // update FAT mapper
-    hal_remote_s32( slot_xp , END_OF_CHAIN_CLUSTER_MAX );
-
-    // we don't mark the FAT mapper page as dirty,
-    // because we synchronously update FAT on IOC device
-    error = fatfs_move_page( page_xp , IOC_SYNC_WRITE );
-
-    if( error )
-    { 
-        printk("\n[ERROR] in %s : cannot update FAT on IOC device\n", __FUNCTION__ );
-        remote_rwlock_wr_release( lock_xp );
-        return -1;
-    }
-
-    // release FAT lock
-    remote_rwlock_wr_release( lock_xp );
-
-#if DEBUG_FATFS_CLUSTER_ALLOC 
-cycle = (uint32_t)hal_get_cycles();
-if( DEBUG_FATFS_CLUSTER_ALLOC < cycle )
-printk("\n[%s] thread[%x,%x] exit / allocated cluster %x in FAT / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, cluster, cycle );
-#endif
-
-    *searched_cluster = cluster;
-    return 0;
-
-}  // end fatfs_cluster_alloc()
 
 //////////////////////////////////////////////
 error_t fatfs_release_inode( xptr_t inode_xp )
 {
-    vfs_ctx_t   * vfs_ctx;        // local pointer on VFS context (same in all clusters).
-    fatfs_ctx_t * loc_fatfs_ctx;  // local pointer on local FATFS context
-    cxy_t         fat_cxy;        // FAT cluster identifier
-    xptr_t        fatfs_ctx_xp;   // extended pointer on FATFS context in FAT cluster
-    fatfs_ctx_t * fatfs_ctx_ptr;  // local pointer on FATFS context in FAT cluster
-    xptr_t        fat_mapper_xp;  // extended pointer on FAT mapper
-    xptr_t        lock_xp;        // extended pointer on lock protecting FAT.
-    xptr_t        first_xp;       // extended pointer on inode extension
-    uint32_t      first_cluster;  // first cluster index for released inode
-    vfs_inode_t * inode_ptr;      // local pointer on target inode
-    cxy_t         inode_cxy;      // target inode cluster identifier
+    vfs_ctx_t   * vfs_ctx;           // local pointer on VFS context (same in all clusters)
+    cxy_t         fat_cxy;           // FAT cluster identifier
+    fatfs_ctx_t * fatfs_ctx_ptr;     // local pointer on FATFS context in FAT cluster
+    xptr_t        fatfs_ctx_xp;      // extended pointer on FATFS-context in FAT cluster
+    mapper_t    * fat_mapper_ptr;    // local pointer on FAT mapper
+    xptr_t        fat_mapper_xp;     // extended pointer on FAT mapper
+    xptr_t        lock_xp;           // extended pointer on lock protecting FAT.
+    xptr_t        first_xp;          // extended pointer on inode extension
+    uint32_t      first_cluster_id;  // first cluster index for released inode
+    vfs_inode_t * inode_ptr;         // local pointer on target inode
+    cxy_t         inode_cxy;         // target inode cluster identifier
     error_t       error;
 
@@ -2680,10 +3015,10 @@
     inode_cxy     = GET_CXY( inode_xp );
 
-    // get first_cluster from inode extension
-    first_xp      = XPTR( inode_cxy , &inode_ptr->extend );
-    first_cluster = (uint32_t)(intptr_t)hal_remote_lpt( first_xp );
+    // get first_cluster_id from inode extension
+    first_xp         = XPTR( inode_cxy , &inode_ptr->extend );
+    first_cluster_id = (uint32_t)(intptr_t)hal_remote_lpt( first_xp );
 
 // check first cluster index
-assert( (first_cluster != 0) , "inode extend is NULL\n" );
+assert( (first_cluster_id != 0) , "inode extend is NULL\n" );
 
 #if DEBUG_FATFS_RELEASE_INODE
@@ -2693,6 +3028,6 @@
 vfs_inode_get_name( inode_xp , name );
 if( DEBUG_FATFS_RELEASE_INODE < cycle )
-printk("\n[%s] thread[%x,%x] enter for <%s> / first_cluster %x / cycle %d\n",
-__FUNCTION__ , this->process->pid, this->trdid, name, first_cluster, cycle );
+printk("\n[%s] thread[%x,%x] enter for <%s> / first_cluster_id %x / cycle %d\n",
+__FUNCTION__ , this->process->pid, this->trdid, name, first_cluster_id, cycle );
 #endif
 
@@ -2700,16 +3035,16 @@
     vfs_ctx = &fs_context[FS_TYPE_FATFS];
 
-    // get local pointer on local FATFS context
-    loc_fatfs_ctx = vfs_ctx->extend;
-
-    // get FAT mapper cluster
-    fat_mapper_xp  = loc_fatfs_ctx->fat_mapper_xp;
-    fat_cxy        = GET_CXY( fat_mapper_xp );
+    // get FAT cluster
+    fat_cxy = CONFIG_VFS_ROOT_CXY;
+
+    // get pointers on FATFS context in FAT cluster 
+    fatfs_ctx_ptr = hal_remote_lpt( XPTR( fat_cxy , &vfs_ctx->extend ) );
+    fatfs_ctx_xp  = XPTR( fat_cxy , fatfs_ctx_ptr ); 
+
+    // get FAT mapper pointers
+    fat_mapper_ptr = hal_remote_lpt( XPTR( fat_cxy , &fatfs_ctx_ptr->fat_mapper ) );
+	fat_mapper_xp  = XPTR( fat_cxy , fat_mapper_ptr );
     
-    // get pointers on FATFS context in FAT cluster 
-    fatfs_ctx_ptr  = hal_remote_lpt( XPTR( fat_cxy , &vfs_ctx->extend ) );
-    fatfs_ctx_xp   = XPTR( fat_cxy , fatfs_ctx_ptr );
-
-    // get extended pointer on FAT lock in FAT cluster
+    // build extended pointer on FAT lock in FAT cluster
     lock_xp = XPTR( fat_cxy , &fatfs_ctx_ptr->lock );
 
@@ -2727,5 +3062,5 @@
     if ( fatfs_recursive_release( fat_mapper_xp,
                                   fatfs_ctx_xp,
-                                  first_cluster,
+                                  first_cluster_id,
                                   &dirty_page_min,
                                   &dirty_page_max ) )
@@ -2791,9 +3126,10 @@
 }  // end fatfs_release_inode()
 
-////////////////////////////////////////////
-error_t fatfs_move_page( xptr_t     page_xp,
-                         cmd_type_t cmd_type )
-{
-    error_t       error;
+/////////////////////////////////////////////////
+error_t fatfs_move_page( xptr_t          page_xp,
+                         ioc_cmd_type_t  cmd_type )
+{
+    error_t       error = 0;
+
     vfs_inode_t * inode_ptr;
     mapper_t    * mapper_ptr;      
@@ -2829,5 +3165,5 @@
 #if DEBUG_FATFS_MOVE_PAGE
 if( DEBUG_FATFS_MOVE_PAGE < cycle )
-printk("\n[%s] thread[%x,%x] enters for %s /  page %d in FAT mapper / cycle %d\n",
+printk("\n[%s] thread[%x,%x] enters %s for  page %d in FAT mapper / cycle %d\n",
 __FUNCTION__, this->process->pid, this->trdid, dev_ioc_cmd_str(cmd_type), page_id, cycle );
 #endif
@@ -2836,9 +3172,14 @@
  
         // access IOC device
-        error = dev_ioc_move_data( cmd_type , buffer_xp , lba , 8 );
+        if     (cmd_type == IOC_SYNC_WRITE) error = dev_ioc_sync_write( buffer_xp, lba, 8 );
+        else if(cmd_type == IOC_SYNC_READ ) error = dev_ioc_sync_read( buffer_xp, lba, 8 );
+        else
+        {
+            printk("\n[ERROR] in %s : illegal asynchronous FAT access\n", __FUNCTION__ );
+        }
 
         if( error )
         {
-            printk("\n[ERROR] in %s : cannot access device\n", __FUNCTION__ );
+            printk("\n[ERROR] in %s : cannot access IOC device\n", __FUNCTION__ );
             return -1;
         }
@@ -2846,6 +3187,6 @@
 #if DEBUG_FATFS_MOVE_PAGE
 if( DEBUG_FATFS_MOVE_PAGE < cycle )
-printk("\n[%s] thread[%x,%x] exit / page %d in FAT mapper\n",
-__FUNCTION__, this->process->pid, this->trdid, page_id, cycle );
+printk("\n[%s] thread[%x,%x] exit %s for page %d in FAT mapper\n",
+__FUNCTION__, this->process->pid, this->trdid, dev_ioc_cmd_str(cmd_type), page_id );
 #endif
 
@@ -2856,9 +3197,11 @@
 
 #if DEBUG_FATFS_MOVE_PAGE
-vfs_inode_get_name( XPTR( page_cxy , inode_ptr ) , name );
 if( DEBUG_FATFS_MOVE_PAGE < cycle )
-printk("\n[%s] thread[%x,%x] enters for %s / page %d in <%s> mapper/ cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, 
-dev_ioc_cmd_str( cmd_type ), page_id, name, cycle );
+{
+    vfs_inode_get_name( XPTR( page_cxy , inode_ptr ) , name );
+    printk("\n[%s] thread[%x,%x] enters %s for page %d in <%s> mapper / cycle %d\n",
+    __FUNCTION__, this->process->pid, this->trdid, 
+    dev_ioc_cmd_str( cmd_type ), page_id, name, cycle );
+}
 #endif
 
@@ -2893,6 +3236,13 @@
         uint32_t lba = fatfs_lba_from_cluster( fatfs_ctx , searched_cluster_id );
 
-        // access IOC device to move 8 blocks
-        error = dev_ioc_move_data( cmd_type , buffer_xp , lba , 8 );
+        // access IOC device
+        if     (cmd_type == IOC_WRITE     ) error = dev_ioc_write( buffer_xp, lba, 8 );
+        else if(cmd_type == IOC_READ      ) error = dev_ioc_read( buffer_xp, lba, 8 );
+        else if(cmd_type == IOC_SYNC_READ ) error = dev_ioc_sync_read( buffer_xp, lba, 8 );
+        else if(cmd_type == IOC_SYNC_WRITE) error = dev_ioc_sync_write( buffer_xp, lba, 8 );
+        else
+        {
+            printk("\n[ERROR] in %s : illegal cmd_type\n", __FUNCTION__ );
+        }
 
         if( error )
@@ -2904,7 +3254,14 @@
 #if DEBUG_FATFS_MOVE_PAGE
 if( DEBUG_FATFS_MOVE_PAGE < cycle )
-vfs_inode_get_name( XPTR( page_cxy, inode_ptr ) , name );
-printk("\n[%s] thread[%x,%x] exit / page %d in <%s> mapper / cluster_id %x\n",
-__FUNCTION__, this->process->pid, this->trdid, page_id, name, searched_cluster_id );
+{
+    printk("\n[%s] thread[%x,%x] exit %s for page %d in <%s> mapper / cluster_id %x\n",
+    __FUNCTION__, this->process->pid, this->trdid, 
+    dev_ioc_cmd_str( cmd_type ), page_id, name, searched_cluster_id );
+}
+#endif
+
+#if (DEBUG_FATFS_MOVE_PAGE & 1)
+if( DEBUG_FATFS_MOVE_PAGE < cycle )
+fatfs_display_fat( searched_cluster_id , 64 );
 #endif
 
Index: /trunk/kernel/fs/fatfs.h
===================================================================
--- /trunk/kernel/fs/fatfs.h	(revision 656)
+++ /trunk/kernel/fs/fatfs.h	(revision 657)
@@ -2,6 +2,5 @@
  * fatfs.h - FATFS file system API definition.
  *
- * Author    Mohamed Lamine Karaoui (2014,2015)
- *           Alain Greiner (2016,2017,2018)
+ * Author    Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -27,5 +26,5 @@
 
 #include <hal_kernel_types.h>
-#include <remote_queuelock.h>
+#include <remote_rwlock.h>
 #include <vfs.h>
 #include <dev_ioc.h>
@@ -36,14 +35,12 @@
  *
  * The FATFS specific extensions to the generic VFS are the following:
- * 1) The vfs_ctx_t "extend" field is a void* pointing on the fatfs_ctx_t structure.
- *    This structure contains various general informations such as the total
- *    number of sectors in FAT region, the number of bytes per sector, the number 
- *    of sectors per cluster, the lba of FAT region, the lba of data region, or the
- *    cluster index for the root directory. It contains also an extended pointer
- *    on the FAT mapper.
+ * 1) The vfs_ctx_t "extend" field contains a local pointer on the local 
+ *    fatfs_ctx_t structure.
+ *
  * 2) The vfs_inode_t "extend" contains, for each inode,
- *    the first FAT32 cluster_id (after cast to intptr).
- * 3) The vfs_dentry_t "extend" field contains, for each dentry, the entry index
- *    in the FATFS directory (32 bytes per FATFS directory entry).
+ *    the first FATFS cluster_id (after cast to intptr).
+ *
+ * 3) The vfs_dentry_t "extend" field contains a local pointer on the local
+ *    FATFS directory entry (32 bytes) in the directory mapper.
  *
  * In the FAT32 File System, the File Allocation Table is is actually an array 
@@ -57,6 +54,4 @@
  *****************************************************************************************/
  
-///////////////////////////////////////////////////////////////////////////////////////////
-
 /*************** Partition Boot Sector Format **********************************/
 //                                     offset |  length
@@ -182,4 +177,8 @@
  * This structure defines a FATFS specific context extension to the VFS context.
  * This fatfs context is replicated in all clusters.
+ * It contains read-only informations such as the total number of sectors in FAT region,
+ * the number of bytes per sector, the number of sectors per cluster, the lba of FAT,
+ * the lba of data region, the cluster_id for the root directory, and an extended 
+ * pointer on the FAT mapper.
  *
  * WARNING 1 : All access to the FAT are protected by a remote_rwlock. 
@@ -189,9 +188,9 @@
  *   functions to modify the FAT in both the FAT mapper and on IOC device.
  *
- * WARNING 2 : Most fields are constant values, but the <free_cluster_hint>,
- * <free_clusters>, <lock>, and the <fs_info_buffer> are shared variables, 
- * that can be modified by any thread running in any cluster. The <fs_info_buffer> 
- * contains a copy of the FS_INFO sector, and is only allocated in the FAT cluster 
- * (cluster 0). It is used to synchronously update the free clusters info on IOC device.
+ * WARNING 2 : Most fields are constant values, but <free_cluster_hint>, <free_clusters>,
+ * <lock>, and the buffer pointed by the <fs_info_xp> are shared variables, that can
+ * modified by any thread running in any cluster. The <fs_info_buffer>  contains
+ * a copy of the FS_INFO sector, and is only allocated in the FAT cluster. 
+ * It is used to synchronously update the free clusters info on IOC device.
  *  => For all these variables, only the values stored in the FAT cluster must be used.
  ****************************************************************************************/
@@ -207,9 +206,9 @@
     uint32_t            fs_info_lba;           /*! lba of FS_INFO sector                */
     uint32_t            root_dir_cluster;      /*! cluster index for  root directory    */
-    xptr_t              fat_mapper_xp;         /*! extended pointer on FAT mapper       */
+    struct mapper_s   * fat_mapper;            /*! local pointer on FAT mapper          */
 
     /* shared variables (only the copy in FAT cluster must be used)                     */
-    uint32_t            free_cluster_hint;     /*! cluster[hint+1] is the first free    */
-    uint32_t            free_clusters;         /*! free clusters number                 */
+    uint32_t            free_cluster_hint;     /*! free_cluster_hint + 1 is first free  */
+    uint32_t            free_clusters;         /*! number of free clusters              */
     remote_rwlock_t     lock;                  /*! exclusive access to FAT              */
     uint8_t           * fs_info_buffer;        /*! local pointer on FS_INFO buffer      */
@@ -224,5 +223,5 @@
  * This debug function display the content of the FATFS context copy in cluster
  * identified by the <cxy> argument.
- * This function can be called by a thread running in any cluster.
+ * This function can be called by any thread running in any cluster.
  *****************************************************************************************
  * @ cxy       :  target cluster identifier.
@@ -232,16 +231,27 @@
 /*****************************************************************************************
  * This debug function access the FAT mapper to display the current FAT state,
- * as defined by the <page_id>, <min_slot>, and <nb_slots> arguments.
- * It loads the missing pages from IOC to mapper if required.
- * This function can be called by a thread running in any cluster.
- *****************************************************************************************
- * @ page_id   : page index in FAT mapper (one page is 4 Kbytes = 1024 slots).
- * @ min_slot  : first slot in page
- * @ nb_slots  : number of slots (one slot is 4 bytes).
- ****************************************************************************************/
-void fatfs_display_fat( uint32_t  page_id,
-                        uint32_t  min_slot,
+ * as defined by the <min_slot>, and <nb_slots> arguments.
+ * It display as many lines (8 slots par line) as required to display <nb_slots>,
+ * starting from the <min_slot>. The displayed slots can spread on several FAT mapper
+ * pages. It loads the missing pages from IOC to mapper if required.
+ * This function can be called by any thread running in any cluster.
+ *****************************************************************************************
+ * @ min_slot   : first FATFS cluster index.
+ * @ nb_slots   : number of slots (one slot is 4 bytes.
+ ****************************************************************************************/
+void fatfs_display_fat( uint32_t  min_slot,
                         uint32_t  nb_slots );
 
+/*****************************************************************************************
+ * This function checks the current values of the "free_clusters" and "free_cluster_hint" 
+ * variables in the FS_INFO sector on IOC, versus the values stored in the fatfs context.
+ * As these values are synchronously updated on IOC device at each modification, 
+ * it does nothing if the values are equal. It updates the FS_INFO sector on IOC device,
+ * and displays a warning message on TXT0 if they are not equal.
+ * This function can be called by any thread running in any cluster. 
+ *****************************************************************************************
+ * @ return 0 if success / return -1 if failure during IOC device access.
+ ****************************************************************************************/
+error_t fatfs_check_free_info( void );
 
 //////////////////////////////////////////////////////////////////////////////////////////
@@ -251,33 +261,41 @@
 
 /*****************************************************************************************
- * This fuction allocates memory from local cluster for a FATFS context descriptor.
- *****************************************************************************************
- * @ return a pointer on the created context / return NULL if failure.
- ****************************************************************************************/
-fatfs_ctx_t * fatfs_ctx_alloc( void );
-
-/*****************************************************************************************
- * This function access the boot device, and initialises the local FATFS context,
- * from informations contained in the boot record. This initialisation includes the
- * creation of the FAT mapper in cluster 0.
- *****************************************************************************************
- * @ vfs_ctx   : local pointer on VFS context for FATFS.
- ****************************************************************************************/
-void fatfs_ctx_init( fatfs_ctx_t * fatfs_ctx );
-
-/*****************************************************************************************
- * This function releases memory dynamically allocated for the FATFS context extension.
- *****************************************************************************************
- * @ vfs_ctx   : local pointer on VFS context.
- ****************************************************************************************/
-void fatfs_ctx_destroy( fatfs_ctx_t * fatfs_ctx );
+ * This fuction allocates memory for a FATFS context descriptor in a cluster
+ * identified by the <cxy> argument.
+ *****************************************************************************************
+ * @ cxy   : target cluster identifier.
+ * @ return an extended pointer on the created context / return XPTR_NULL if failure.
+ ****************************************************************************************/
+xptr_t  fatfs_ctx_alloc( cxy_t  cxy );
+
+/*****************************************************************************************
+ * This fuction initialize a fatfs context identified by the <fatfs_ctx_xp> argument
+ * from informations found in the IOC device boot record. This initialisation includes
+ * allocation of the FS_INFO buffer and creation of the FAT mapper in the same cluster.
+ *****************************************************************************************
+ * @ fatfs_ctx_xp   : extended pointer on fatfs context.
+ * @ return 0 if success / return -1 if failure.
+ ****************************************************************************************/
+error_t  fatfs_ctx_init( xptr_t  fatfs_ctx_xp );
+
+/*****************************************************************************************
+ * This function releases memory dynamically allocated for the FATFS context.
+ *****************************************************************************************
+ * @ vfs_ctx_xp   : extended pointer on FATFS context.
+ ****************************************************************************************/
+void fatfs_ctx_destroy( xptr_t  fatfs_ctx_xp );
 
 /*****************************************************************************************
  * This function implements the generic vfs_fs_add_dentry() function for the FATFS.
  *****************************************************************************************
- * This function updates a directory mapper identified by the <inode> argument
- * to add a new directory entry identified by the <dentry> argument.
+ * This function introduces in a directory mapper identified by the <parent_inode_xp>
+ * argument a new directory entry identified by the <dentry_ptr> argument.
+ * The dentry descriptor and the associated inode descriptor must have been previously
+ * allocated, initialized, and registered in the Inode Tree. 
+ * The dentry descriptor defines the "name" field.
+ * The inode descriptor defines the "type", "size", and "cluster_id" fields.
+ * The "extension field" in dentry descriptor is set : index in the FAT32 directory.
  * All modified pages in the directory mapper are synchronously updated on IOC device.
- * It must be called by a thread running in the cluster containing the directory inode.
+ * This function can be called by any thread running in any cluster.
  *
  * Implementation note : this function works in two steps:
@@ -285,22 +303,22 @@
  *   to find the end of directory (NO_MORE_ENTRY marker).
  * - Then it writes 3, 4, or 5 directory entries (depending on the name length), using 
- *   a 5 steps FSM (one state per entry to be written), updates on IOC device the 
- *   modified pages, and updates the dentry extension field, that must contain 
- *   the dentry index in FATFS directory.
- *****************************************************************************************
- * @ inode    : local pointer on directory inode.
- * @ dentry   : local pointer on dentry.
- * @ return 0 if success / return ENOENT if not found, or EIO if no access to IOC device.
- ****************************************************************************************/
-error_t fatfs_add_dentry( struct vfs_inode_s  * inode,
-                          struct vfs_dentry_s * dentry );
+ *   a 5 steps FSM (one state per entry to be written), and updates on IOC device the 
+ *   modified pages.
+ *****************************************************************************************
+ * @ parent_inode_xp : [in]  extended pointer on parent directory inode.
+ * @ dentry_ptr      : [in]  local pointer on dentry (in parent directory cluster).
+ * @ index           : [out] index of the new entry in the FAT32 directory.
+ * @ return 0 if success / return -1 if failure.
+ ****************************************************************************************/
+error_t fatfs_add_dentry( xptr_t                parent_inode_xp,
+                          struct vfs_dentry_s * dentry_ptr );
 
 /*****************************************************************************************
  * This function implements the generic vfs_fs_remove_dentry() function for the FATFS.
  *****************************************************************************************
- * This function updates a directory identified by the <inode> argument
- * to remove a directory entry identified by the <dentry> argument.
+ * This function updates a directory identified by the <parent_inode_xp> argument
+ * to remove a directory entry identified by the <dentry_ptr> argument.
  * All modified pages in directory mapper are synchronously updated on IOC device.
- * It must be called by a thread running in the cluster containing the inode.
+ * This function can be called by any thread running in any cluster.
  *
  * Implementation note: this function uses the dentry extension to directly access 
@@ -308,50 +326,72 @@
  * updates the modified pages on IOC device.
  *****************************************************************************************
- * @ inode    : local pointer on directory inode.
- * @ dentry   : local pointer on dentry.
- * @ return 0 if success / return ENOENT if not found, or EIO if no access to IOC device.
- ****************************************************************************************/
-error_t fatfs_remove_dentry( struct vfs_inode_s  * inode,
-                             struct vfs_dentry_s * dentry );
-
-/*****************************************************************************************
- * This function implements the generic vfs_fs_new_dentry() function for the FATFS.
- *****************************************************************************************
- * It scan a parent directory mapper, identified by the <parent_inode> argument to find
- * a directory entry identified by the <name> argument.  In case of success, it completes
- * initialization the inode/dentry couple, identified by the  <child_inode_xp> argument.
- * The child inode descriptor, and the associated dentry descriptor must have been
- * previously allocated by the caller.
+ * @ parent_inode_xp   : [in] extended pointer on parent directory inode.
+ * @ dentry_ptr        : [in] local pointer on dentry (in parent directory cluster).
+ * @ return 0 if success / return -1 if failure.
+ ****************************************************************************************/
+error_t fatfs_remove_dentry( xptr_t                parent_inode_xp,
+                             struct vfs_dentry_s * dentry_ptr );
+
+/*****************************************************************************************
+ * This function implements the generic vfs_fs_new_dentry_from_mapper() for the FATFS.
+ *****************************************************************************************
+ * It scan a parent directory mapper, identified by the <parent_inode_xp> argument 
+ * to find a directory entry name defined by the <dentry_ptr> argument, and completes
+ * the initialization of the dentry and the associated child_inode descriptors,
+ * from informations found in the parent directory mapper :
  * - It set the "type", "size", and "extend" fields in the child inode descriptor.
  * - It set the " extend" field in the dentry descriptor.
- * It must be called by a thread running in the cluster containing the parent inode.
- *****************************************************************************************
- * @ parent_inode    : local pointer on parent inode (directory).
- * @ name            : child name.
- * @ child_inode_xp  : extended pointer on remote child inode (file or directory).
+ * The child inode descriptor, and the dentry descriptor must have been previously
+ * allocated and introduced in the Inode Tree.
+ * This function can be called by any thread running in any cluster.
+ *****************************************************************************************
+ * @ parent_inode_xp : extended pointer on parent inode (directory).
+ * @ dentry_ptr      : local pointer on new dentry (in parent inode cluster).
+ * @ return 0 if success / return -1 if failure.
+ ****************************************************************************************/
+error_t fatfs_new_dentry_from_mapper( xptr_t                parent_inode_xp,
+                                      struct vfs_dentry_s * dentry_ptr );
+
+/*****************************************************************************************
+ * This function implements the generic vfs_fs_new_dentry_to_mapper() for the FATFS.
+ *****************************************************************************************
+ * This function  introduces a brand new dentry identified by the <dentry_ptr> argument
+ * in the mapper of a directory identified by the <parent_inode_xp> argument.
+ * It is called by the vfs_lookup() function.
+ * The child inode descriptor, and the dentry descriptor must have been previously
+ * allocated and introduced in the Inode Tree. The dentry descriptor contains the name.
+ * 1. It allocates a new FATFS cluster_id,
+ * 2. It registers the allocated cluster_id in the child inode extension,
+ * 3. It add a new entry (32 bytes) in the directory mapper,
+ * This function can be called by any thread running in any cluster.
+ *****************************************************************************************
+ * @ parent_inode_xp : [in]  extended pointer on parent inode (directory).
+ * @ dentry_ptr      : [in]  local pointer on dentry (in parent inode cluster).
+ * @ return 0 if success / return -1 if failure.
+ ****************************************************************************************/
+error_t fatfs_new_dentry_to_mapper( xptr_t                parent_inode_xp,
+                                    struct vfs_dentry_s * dentry_ptr );
+
+/*****************************************************************************************
+ * This function implements the generic vfs_fs_update_dentry() function for the FATFS.
+ *****************************************************************************************
+ * It update the "size" of a directory entry identified by the <dentry_ptr> argument in
+ * the mapper of a directory identified by the <parent_inode_xp> argument, from the
+ * value registered in the inode descriptor.
+ * It scan the directory mapper to find the entry such as name == dentry_ptr->name.
+ * It set the "size" field in the directory mapper, and updates the modified directory
+ * page on the IOC device.
+ * This function can be called by any thread running in any cluster.
+ *
+ * TODO the current implementation uses the fatfs_scan_directory to access the 
+ * FAT32 directory by name. We can access directly this directory entry if we use
+ * the dentry "extend" field...
+ *****************************************************************************************
+ * @ parent_inode_xp : extended pointer on inode (directory).
+ * @ dentry_ptr      : local pointer on dentry (in parent directory cluster).
  * @ return 0 if success / return -1 if child not found.
  ****************************************************************************************/
-error_t fatfs_new_dentry( struct vfs_inode_s * parent_inode,
-                          char               * name,
-                          xptr_t               child_inode_xp );
-
-/*****************************************************************************************
- * This function implements the generic vfs_fs_update_dentry() function for the FATFS.
- *****************************************************************************************
- * It update the size of a directory entry identified by the <dentry> argument in
- * the mapper of a directory identified by the <inode> argument, as defined by the
- * <size> argument.
- * It scan the mapper to find the entry identified by the dentry "name" field.
- * It set the "size" field in the in the directory mapper AND marks the page as DIRTY.
- * It must be called by a thread running in the cluster containing the directory inode.
- *****************************************************************************************
- * @ inode        : local pointer on inode (directory).
- * @ dentry       : local pointer on dentry (for name).
- * @ size         : new size value.
- * @ return 0 if success / return ENOENT if child not found.
- ****************************************************************************************/
-error_t fatfs_update_dentry( struct vfs_inode_s  * inode,
-                             struct vfs_dentry_s * dentry,
-                             uint32_t              size );
+error_t fatfs_update_dentry( xptr_t                parent_inode_xp,
+                             struct vfs_dentry_s * dentry_ptr );
 
 /*****************************************************************************************
@@ -367,7 +407,9 @@
  * the Inode Tree is dynamically created, and all dirent fields are documented in the
  * dirent array. Otherwise, only the dentry name is documented.
- * It must be called by a thread running in the cluster containing the directory inode.
- *****************************************************************************************
- * @ inode      : [in]  local pointer on directory inode.
+ *
+ * WARNING : It must be called by a thread running in the cluster containing the
+ * target directory inode.
+ *****************************************************************************************
+ * @ parent_inode_xp  : [in]  extended pointer on directory inode.
  * @ array      : [in]  local pointer on array of dirents.
  * @ max_dirent : [in]  max number of slots in dirent array.
@@ -390,14 +432,15 @@
  *****************************************************************************************
  * It updates the FATFS on the IOC device for a given inode identified by 
- * the <inode> argument. It scan all pages registered in the associated mapper,
+ * the <inode_xp> argument. It scan all pages registered in the associated mapper,
  * and copies from mapper to device each page marked as dirty.
  * WARNING : The target <inode> cannot be a directory, because all modifications in a 
  * directory are synchronously done on the IOC device by the two fatfs_add_dentry() 
  * and fatfs_remove_dentry() functions.
- *****************************************************************************************
- * @ inode   : local pointer on inode.
- * @ return 0 if success / return -1 if failure during IOC device access. 
- ****************************************************************************************/
-error_t fatfs_sync_inode( struct vfs_inode_s * inode );
+ * This function can be called by any thread running in any cluster.
+ *****************************************************************************************
+ * @ inode_xp   : extended pointer on inode.
+ * @ return 0 if success / return -1 if failure. 
+ ****************************************************************************************/
+error_t fatfs_sync_inode( xptr_t  inode_xp );
 
 /*****************************************************************************************
@@ -407,9 +450,8 @@
  * It scan all clusters registered in the FAT mapper, and copies from mapper to device 
  * each page marked as dirty.
- *
- * TODO : the current implementation check ALL pages in the FAT region, even if most
- * pages are empty, and not copied in mapper. It is sub-optimal.
- * A solution is to maintain in the FAT context two "dirty_min" and "dirty_max"
- * variables defining the smallest/largest dirty page index in FAT mapper... 
+ * This function can be called by any thread running in any cluster. 
+ *
+ * Implementation note : this function uses the grdxt_remote_get_first() function
+ * to test only the pages actually registered in the FAT mapper.
  *****************************************************************************************
  * @ return 0 if success / return -1 if failure during IOC device access.
@@ -418,41 +460,7 @@
 
 /*****************************************************************************************
- * This function implements the generic vfs_fs_sync_fsinfo() function for the FATFS.
- *****************************************************************************************
- * It checks the current values of the "free_clusters" and "free_cluster_hint" variables
- * in the FS_INFO sector on IOC, versus the values stored in the fatfs context.
- * As these values are synchronously updated on IOC device at each modification, 
- * it does nothing if the values are equal. It updates the FS_INFO sector on IOC device,
- * and displays a warning message on TXT0 if they are not equal.
- * This function can be called by any thread running in any cluster. 
- *****************************************************************************************
- * @ return 0 if success / return -1 if failure during IOC device access.
- ****************************************************************************************/
-error_t fatfs_sync_free_info( void );
-
-/*****************************************************************************************
- * This function implements the generic vfs_fs_cluster_alloc() function for the FATFS.
- *****************************************************************************************
- * It access the FAT (File allocation table), stored in the FAT mapper, and returns
- * in <searched_cluster> the FATFS cluster index of a free cluster.
- * It can be called by a thread running in any cluster, as it uses remote access
- * primitives when the FAT mapper is remote. It takes the rwlock stored in the FATFS
- * context located in the same cluster as the FAT mapper itself, to get exclusive
- * access to the FAT. It uses and updates the <free_cluster_hint> and <free_clusters>
- * variables stored in this FATFS context.
- * - it updates the <free_cluster_hint> and <free_clusters> variables in FATFS context.
- * - it updates the FAT mapper (handling miss from IOC device if required). 
- * - it synchronously updates the FAT region on IOC device. 
- * - it returns the allocated cluster index.
- *****************************************************************************************
- * @ searched_cluster_id  : [out] allocated FATFS cluster index.
- * @ return 0 if success / return -1 if no more free clusters on IOC device.
- ****************************************************************************************/
-error_t fatfs_cluster_alloc( uint32_t * searched_cluster_id );
-
-/*****************************************************************************************
  * This function implements the generic vfs_fs_release_inode() function for the FATFS.
  *****************************************************************************************
- * This function is used to remove a given file or directory from FATFS the file system.
+ * This function is used to remove a given file or directory from the FATFS file system.
  * It releases all clusters allocated to a file/directory identified by the <inode_xp>
  * argument. All released clusters are marked FREE_CLUSTER in the FAT mapper. 
@@ -462,4 +470,5 @@
  * synchronously update all modified pages in the FAT mapper to the IOC device.
  * Finally the FS-INFO sector on the IOC device is updated.
+ * This function can be called by any thread running in any cluster.
  *****************************************************************************************
  * @ inode_xp   : extended pointer on inode.
@@ -478,15 +487,8 @@
  * - For a regular file, it scan the FAT mapper to get the cluster_id on IOC device,
  *   and read/write this cluster.
- * It can be called by any thread running in any cluster.
+ * This function can be called by any thread running in any cluster.
  *
  * WARNING : For the FAT mapper, the inode field in the mapper MUST be NULL, as this
  *           is used to indicate that the corresponding mapper is the FAT mapper.
- *
- * TODO : In this first implementation, the entry point in the FAT to get the cluster_id
- *        is always the cluster_id of the first page, registered in the inode extension.
- *        This can introduce a quadratic cost when trying of acessing all pages of a
- *        big file. An optimisation would be to introduce in the inode extension two
- *        new fields <other_page_id> & <other_cluster_id>, defining a second entry point
- *        in the FAT.
  *****************************************************************************************
  * @ page_xp   : extended pointer on page descriptor.
@@ -494,6 +496,6 @@
  * @ return 0 if success / return EIO if error during device access. 
  ****************************************************************************************/
-error_t fatfs_move_page( xptr_t      page_xp,
-                         cmd_type_t  cmd_type );
+error_t fatfs_move_page( xptr_t          page_xp,
+                         ioc_cmd_type_t  cmd_type );
 
 
Index: /trunk/kernel/fs/ramfs.h
===================================================================
--- /trunk/kernel/fs/ramfs.h	(revision 656)
+++ /trunk/kernel/fs/ramfs.h	(revision 657)
@@ -3,5 +3,5 @@
  *
  * Authors   Mohamed Lamine Karaoui (2014,2015)
- *           Alain Greiner (2016,2017)
+ *           Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -29,5 +29,4 @@
 // The RAMFS File System does not uses any external device to store data.
 // It stores the dynamically created files and directories in the VFS mappers.
-// The ramfs_read_page() and ramfs_write_page() functions should never be used.
 // The RAMFS cannot be used as the root File System.
 //
Index: /trunk/kernel/fs/vfs.c
===================================================================
--- /trunk/kernel/fs/vfs.c	(revision 656)
+++ /trunk/kernel/fs/vfs.c	(revision 657)
@@ -3,5 +3,5 @@
  *
  * Author  Mohamed Lamine Karaoui (2015)
- *         Alain Greiner (2016,2017,2018,2019)
+ *         Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -33,5 +33,4 @@
 #include <xhtab.h>
 #include <string.h>
-#include <rpc.h>
 #include <errno.h>
 #include <kmem.h>
@@ -59,7 +58,7 @@
 //////////////////////////////////////////////////////////////////////////////////////////
 
-////////////////////////////////////////
-void vfs_ctx_init( vfs_fs_type_t   type,
-                   uint32_t        attr,
+///////////////////////////////////////
+void vfs_ctx_init( cxy_t           cxy,
+                   vfs_fs_type_t   fs_type,
 	               uint32_t        total_clusters,
 	               uint32_t        cluster_size,
@@ -67,43 +66,58 @@
                    void          * extend )
 {
-    vfs_ctx_t * vfs_ctx = &fs_context[type];
-
-    vfs_ctx->type           = type;
-    vfs_ctx->attr           = attr;
-    vfs_ctx->total_clusters = total_clusters;
-    vfs_ctx->cluster_size   = cluster_size;
-    vfs_ctx->vfs_root_xp    = vfs_root_xp;
-    vfs_ctx->extend         = extend;
-
-    busylock_init( &vfs_ctx->lock , LOCK_VFS_CTX );
-
-    bitmap_init( vfs_ctx->bitmap , BITMAP_SIZE(CONFIG_VFS_MAX_INODES) ); 
-}
-
-////////////////////////////////////////////
-error_t vfs_ctx_inum_alloc( vfs_ctx_t * ctx,
+    // get pointer on relevant VFS context (same in all clusters) 
+    vfs_ctx_t * vfs_ctx_ptr = &fs_context[fs_type];
+
+    // initialise VFS context fields 
+    hal_remote_s32( XPTR( cxy , &vfs_ctx_ptr->type           ) , fs_type );
+    hal_remote_s32( XPTR( cxy , &vfs_ctx_ptr->total_clusters ) , total_clusters );
+    hal_remote_s32( XPTR( cxy , &vfs_ctx_ptr->cluster_size   ) , cluster_size );
+    hal_remote_s64( XPTR( cxy , &vfs_ctx_ptr->vfs_root_xp    ) , vfs_root_xp );
+    hal_remote_spt( XPTR( cxy , &vfs_ctx_ptr->extend         ) , extend );
+
+    // initialize VFS context lock
+    remote_busylock_init( XPTR( cxy , &vfs_ctx_ptr->lock ) , LOCK_VFS_CTX );
+
+    // initialize inum allocator
+    bitmap_remote_init( XPTR( cxy , &vfs_ctx_ptr->bitmap ),
+                        BITMAP_SIZE(CONFIG_VFS_MAX_INODES) ); 
+
+} // end vfs_ctx_init()
+
+///////////////////////////////////////////////
+error_t vfs_ctx_inum_alloc( xptr_t      ctx_xp,
                             uint32_t  * inum )
 {
+    // get context cluster and local pointer
+    cxy_t       ctx_cxy = GET_CXY( ctx_xp );
+    vfs_ctx_t * ctx_ptr = GET_PTR( ctx_xp );
+
+    // build extended pointer on lock protecting the inum allocator
+    xptr_t lock_xp = XPTR( ctx_cxy , &ctx_ptr->lock );
+
+    // build extended pointer on inum bitmap
+    xptr_t bitmap_xp = XPTR( ctx_cxy , &ctx_ptr->bitmap );
+
     // get lock on inum allocator
-    busylock_acquire( &ctx->lock );
+    remote_busylock_acquire( lock_xp );
 
     // get lid from local inum allocator
-    uint32_t lid = bitmap_ffc( ctx->bitmap , CONFIG_VFS_MAX_INODES );
+    uint32_t lid = bitmap_remote_ffc( bitmap_xp , CONFIG_VFS_MAX_INODES );
 
     if( lid == 0xFFFFFFFF )   // no more free slot => error
     {
         // release lock
-        busylock_release( &ctx->lock );
+        remote_busylock_release( lock_xp );
 
         // return error
-        return 1;
+        return -1;
     }
     else              // found => return inum
     {
         // set slot allocated
-        bitmap_set( ctx->bitmap , lid );
+        bitmap_remote_set( bitmap_xp , lid );
 
         // release lock
-        busylock_release( &ctx->lock );
+        remote_busylock_release( lock_xp );
 
         // return inum
@@ -111,12 +125,29 @@
         return 0;
     }
-}
-
-////////////////////////////////////////////
-void vfs_ctx_inum_release( vfs_ctx_t * ctx,
-                           uint32_t    inum )
-{
-    bitmap_clear( ctx->bitmap , inum & 0xFFFF ); 
-}
+}  // end vfs_ctx_inum_alloc()
+
+/////////////////////////////////////////////
+void vfs_ctx_inum_release( xptr_t     ctx_xp,
+                           uint32_t   inum )
+{
+    // get context cluster and local pointer
+    cxy_t       ctx_cxy = GET_CXY( ctx_xp );
+    vfs_ctx_t * ctx_ptr = GET_PTR( ctx_xp );
+
+    // build extended pointer on inum bitmap
+    xptr_t bitmap_xp = XPTR( ctx_cxy , &ctx_ptr->bitmap );
+
+    // build extended pointer on lock
+    xptr_t lock_xp = XPTR( ctx_cxy , &ctx_ptr->lock );
+
+    // get lock 
+    remote_busylock_acquire( lock_xp );
+
+    bitmap_remote_clear( bitmap_xp , inum & 0xFFFF ); 
+
+    // release lock
+    remote_busylock_release( lock_xp );
+
+}  // end vfs_ctx_inum_release()
 
 //////////////////////////////////////////////////////////////////////////////////////////
@@ -140,6 +171,7 @@
 }
 
-////////////////////////////////////////////////////
-error_t vfs_inode_create( vfs_fs_type_t     fs_type,
+////////////////////////////////////////////////
+error_t vfs_inode_create( cxy_t             cxy,
+                          vfs_fs_type_t     fs_type,
                           uint32_t          attr,
                           uint32_t          rights,
@@ -148,10 +180,10 @@
                           xptr_t          * inode_xp )
 {
-    mapper_t         * mapper;     // associated mapper( to be allocated)
-    vfs_inode_t      * inode;      // inode descriptor (to be allocated)
-    
-    uint32_t           inum;       // inode identifier (to be allocated)
-    vfs_ctx_t        * ctx;        // file system context 
-	kmem_req_t         req;        // request to kernel memory allocator
+    xptr_t             mapper_xp;    // extended pointer on associated mapper
+    mapper_t         * mapper_ptr;   // local pointer on associated mapper
+    vfs_inode_t      * inode_ptr;    // local pointer on allocated inode
+    uint32_t           inum;         // inode identifier (to be allocated)
+    vfs_ctx_t        * ctx;          // file system context 
+	kmem_req_t         req;          // request to kernel memory allocator
     error_t            error;
 
@@ -166,71 +198,73 @@
     }
 
-    // allocate inum
-    error = vfs_ctx_inum_alloc( ctx , &inum );
-
-    if( error )
-    {
-        printk("\n[ERROR] in %s : cannot allocate inum\n", __FUNCTION__ );
-        return -1;
-    }
-
-    // allocate memory for mapper
-    mapper = mapper_create( fs_type );
-
-    if( mapper == NULL )
-    {
-        printk("\n[ERROR] in %s : cannot allocate mapper\n", __FUNCTION__ );
-        vfs_ctx_inum_release( ctx , inum );
-        return ENOMEM;
-    }
-
 // check inode descriptor contained in one page
 assert( (sizeof(vfs_inode_t) <= CONFIG_PPM_PAGE_SIZE),
 "inode descriptor must fit in one page" );
 
+    // allocate inum
+    error = vfs_ctx_inum_alloc( XPTR( cxy , ctx ) , &inum );
+
+    if( error )
+    {
+        printk("\n[ERROR] in %s : cannot allocate inum\n", __FUNCTION__ );
+        return -1;
+    }
+
+    // allocate memory for mapper in cluster cxy
+    mapper_xp = mapper_create( cxy , fs_type );
+
+    if( mapper_xp == XPTR_NULL )
+    {
+        printk("\n[ERROR] in %s : cannot allocate mapper\n", __FUNCTION__ );
+        vfs_ctx_inum_release( XPTR( cxy , ctx ) , inum );
+        return -1;
+    }
+
+    mapper_ptr = GET_PTR( mapper_xp );
+
     // allocate one page for VFS inode descriptor
-    // because the embedded "children xhtab footprint
-	req.type  = KMEM_PPM;
-	req.order = 0;
-    req.flags = AF_KERNEL | AF_ZERO;
-	inode     = kmem_alloc( &req );
-
-    if( inode == NULL )
+    // because the embedded "children" xhtab footprint
+	req.type   = KMEM_PPM;
+	req.order  = 0;
+    req.flags  = AF_KERNEL | AF_ZERO;
+	inode_ptr  = kmem_remote_alloc( cxy , &req );
+
+    if( inode_ptr == NULL )
     {
         printk("\n[ERROR] in %s : cannot allocate inode descriptor\n", __FUNCTION__ );
-        vfs_ctx_inum_release( ctx , inum );
-        mapper_destroy( mapper );
+        vfs_ctx_inum_release( XPTR( cxy , ctx ) , inum );
+        mapper_destroy( mapper_xp );
         return -1;
     }
 
+    // initialise inode field in mapper
+    hal_remote_spt( XPTR( cxy , &mapper_ptr->inode ) , inode_ptr );
+ 
     // initialize inode descriptor
-    inode->type       = INODE_TYPE_FILE;     // default value
-    inode->inum       = inum;
-    inode->attr       = attr;
-    inode->rights     = rights;
-    inode->uid        = uid;
-    inode->gid        = gid;
-    inode->ctx        = ctx;
-    inode->mapper     = mapper;
-    inode->extend     = NULL;
-    inode->links      = 0;
-
-    // initialise inode field in mapper
-    mapper->inode     = inode;
- 
+    hal_remote_s32( XPTR( cxy , &inode_ptr->type   ) , INODE_TYPE_FILE );  // default value
+    hal_remote_s32( XPTR( cxy , &inode_ptr->inum   ) , inum );  
+    hal_remote_s32( XPTR( cxy , &inode_ptr->attr   ) , attr );  
+    hal_remote_s32( XPTR( cxy , &inode_ptr->rights ) , rights );  
+    hal_remote_s32( XPTR( cxy , &inode_ptr->links  ) , 0 );
+    hal_remote_s32( XPTR( cxy , &inode_ptr->uid    ) , uid );  
+    hal_remote_s32( XPTR( cxy , &inode_ptr->gid    ) , gid );  
+    hal_remote_spt( XPTR( cxy , &inode_ptr->ctx    ) , ctx );  
+    hal_remote_spt( XPTR( cxy , &inode_ptr->mapper ) , mapper_ptr );  
+    hal_remote_spt( XPTR( cxy , &inode_ptr->extend ) , NULL );  
+
     // initialize chidren dentries xhtab
-    xhtab_init( &inode->children , XHTAB_DENTRY_TYPE );
+    xhtab_init( XPTR( cxy , &inode_ptr->children ) , XHTAB_DENTRY_TYPE );
 
     // initialize parents dentries xlist
-    xlist_root_init( XPTR( local_cxy , &inode->parents ) );
+    xlist_root_init( XPTR( cxy , &inode_ptr->parents ) );
  
     // initialize lock protecting size 
-    remote_rwlock_init( XPTR( local_cxy , &inode->size_lock ), LOCK_VFS_SIZE );
+    remote_rwlock_init( XPTR( cxy , &inode_ptr->size_lock ), LOCK_VFS_SIZE );
 
     // initialise lock protecting inode tree traversal
-    remote_rwlock_init( XPTR( local_cxy , &inode->main_lock ), LOCK_VFS_MAIN );
+    remote_rwlock_init( XPTR( cxy , &inode_ptr->main_lock ), LOCK_VFS_MAIN );
 
     // return extended pointer on inode
-    *inode_xp = XPTR( local_cxy , inode );
+    *inode_xp = XPTR( cxy , inode_ptr );
 
 #if DEBUG_VFS_INODE_CREATE
@@ -238,6 +272,6 @@
 thread_t *     this       = CURRENT_THREAD;
 if( DEBUG_VFS_INODE_CREATE < cycle )
-printk("\n[%s] thread[%x,%x] created inode (%x,%x) / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, local_cxy, inode, cycle );
+printk("\n[%s] thread[%x,%x] created inode (%x,%x) / ctx %x / fs_type %d / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, cxy, inode_ptr, ctx, ctx->type, cycle );
 #endif
  
@@ -246,15 +280,19 @@
 }  // end vfs_inode_create()  
 
-/////////////////////////////////////////////
-void vfs_inode_destroy( vfs_inode_t * inode )
-{
+//////////////////////////////////////////
+void vfs_inode_destroy( xptr_t  inode_xp )
+{
+    // get cluster and local pointer
+    vfs_inode_t * inode_ptr = GET_PTR( inode_xp );
+    cxy_t         inode_cxy = GET_CXY( inode_xp );
+
     // release memory allocated for mapper
-    mapper_destroy( inode->mapper );
-
-    // release memory allocate for inode descriptor
+    mapper_destroy( XPTR( inode_cxy , &inode_ptr->mapper ) );
+
+    // release memory allocated for inode descriptor
 	kmem_req_t req;
 	req.type  = KMEM_PPM;
-	req.ptr   = inode;
-	kmem_free( &req );
+	req.ptr   = inode_ptr;
+	kmem_remote_free( inode_cxy , &req );
 
 }  // end vfs_inode_destroy()
@@ -339,6 +377,6 @@
     
     // get inode cluster and local pointer
+    inode_ptr = GET_PTR( inode_xp );
     inode_cxy = GET_CXY( inode_xp );
-    inode_ptr = GET_PTR( inode_xp );
 
     // build extended pointer on parents dentries root
@@ -367,26 +405,27 @@
 }  // end vfs_inode_get_name()
 
-///////////////////////////////////////////////////////
-error_t vfs_inode_load_all_pages( vfs_inode_t * inode )
-{
-
-assert( (inode != NULL) , "inode pointer is NULL" );
-
+////////////////////////////////////////////////////
+error_t vfs_inode_load_all_pages( xptr_t  inode_xp )
+{
     uint32_t   page_id;
     xptr_t     page_xp;
 
-    mapper_t * mapper = inode->mapper;
-    uint32_t   size   = inode->size;
-
-assert( (mapper != NULL) , "mapper pointer is NULL" );
+
+    // get inode cluster and local pointer
+    vfs_inode_t * inode_ptr = GET_PTR( inode_xp );
+    cxy_t         inode_cxy = GET_CXY( inode_xp );
+
+    // get pointer on mapper and size
+    mapper_t * mapper = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->mapper ) );
+    uint32_t   size   = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->size ) );
 
 #if DEBUG_VFS_INODE_LOAD_ALL
+char       name[CONFIG_VFS_MAX_NAME_LENGTH];
 uint32_t   cycle = (uint32_t)hal_get_cycles();
 thread_t * this  = CURRENT_THREAD;
-char       name[CONFIG_VFS_MAX_NAME_LENGTH];
-vfs_inode_get_name( XPTR( local_cxy , inode ) , name );
+vfs_inode_get_name( inode_xp , name );
 if( DEBUG_VFS_INODE_LOAD_ALL < cycle )
 printk("\n[%s] thread[%x,%x] enter for <%s> in cluster %x / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, name, local_cxy, cycle );
+__FUNCTION__, this->process->pid, this->trdid, name, inode_cxy, cycle );
 #endif
 
@@ -400,5 +439,5 @@
         // If the mage is missing, this function allocates the missing page,
         // and load the page from IOC device into mapper
-        page_xp = mapper_remote_get_page( XPTR( local_cxy , mapper ), page_id );
+        page_xp = mapper_get_page( XPTR( inode_cxy , mapper ), page_id );
 
         if( page_xp == XPTR_NULL ) return -1;
@@ -408,6 +447,6 @@
 cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_VFS_INODE_LOAD_ALL < cycle )
-printk("\n[%s] thread[%x,%x] exit for <%x> in cluster %x / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, name, local_cxy, cycle );
+printk("\n[%s] thread[%x,%x] exit for <%s> in cluster %x\n",
+__FUNCTION__, this->process->pid, this->trdid, name, inode_cxy );
 #endif
 
@@ -425,4 +464,5 @@
     vfs_inode_get_name( inode_xp , name );
 
+    // get inode cluster and local pointer
     cxy_t         inode_cxy = GET_CXY( inode_xp );
     vfs_inode_t * inode_ptr = GET_PTR( inode_xp );
@@ -461,12 +501,21 @@
 //////////////////////////////////////////////////////////////////////////////////////////
 
-///////////////////////////////////////////////////
-error_t vfs_dentry_create( vfs_fs_type_t   fs_type,
+///////////////////////////////////////////////
+error_t vfs_dentry_create( cxy_t           cxy,
+                           vfs_fs_type_t   fs_type,
                            char          * name,
                            xptr_t        * dentry_xp )
 {
-    vfs_ctx_t      * ctx;        // context descriptor
-    vfs_dentry_t   * dentry;     // dentry descriptor (to be allocated)
-	kmem_req_t       req;        // request to kernel memory allocator
+	kmem_req_t       req;            // request to kernel memory allocator
+    vfs_ctx_t      * ctx = NULL;     // context descriptor
+    vfs_dentry_t   * dentry_ptr;     // dentry descriptor (to be allocated)
+
+#if DEBUG_VFS_DENTRY_CREATE
+thread_t * this  = CURRENT_THREAD;
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_VFS_DENTRY_CREATE < cycle )
+printk("\n[%s] thread[%x,%x] enters for <%s> /  fs_type %x / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, name, fs_type, cycle );
+#endif
 
     // get pointer on context
@@ -476,5 +525,5 @@
     else 
     {
-        ctx = NULL;
+        printk("\n[ERROR] in %s undefined fs_type %d\n", __FUNCTION__, fs_type );
         return -1;
     }
@@ -483,13 +532,13 @@
     uint32_t length = strlen( name );
 
-    if( length >= CONFIG_VFS_MAX_NAME_LENGTH ) return EINVAL;
+    if( length >= CONFIG_VFS_MAX_NAME_LENGTH ) return -1;
 
     // allocate memory for dentry descriptor
-	req.type  = KMEM_KCM;
-	req.order = bits_log2( sizeof(vfs_dentry_t) );
-    req.flags = AF_KERNEL | AF_ZERO;
-	dentry    = kmem_alloc( &req );
-
-    if( dentry == NULL ) 
+	req.type   = KMEM_KCM;
+	req.order  = bits_log2( sizeof(vfs_dentry_t) );
+    req.flags  = AF_KERNEL | AF_ZERO;
+	dentry_ptr = kmem_remote_alloc( cxy , &req );
+
+    if( dentry_ptr == NULL ) 
     {
         printk("\n[ERROR] in %s : cannot allocate dentry descriptor\n",
@@ -499,18 +548,19 @@
 
     // initialize dentry descriptor
-    dentry->ctx     = ctx;
-    dentry->length  = length;
-    dentry->extend  = NULL;
-    strcpy( dentry->name , name );
+    hal_remote_spt( XPTR( cxy , &dentry_ptr->ctx    ) , ctx );
+    hal_remote_s32( XPTR( cxy , &dentry_ptr->length ) , length );
+    hal_remote_spt( XPTR( cxy , &dentry_ptr->extend ) , NULL );
+
+    // register name
+    hal_remote_strcpy( XPTR( cxy, dentry_ptr->name ),
+                       XPTR( local_cxy, name ) );
 
     // return extended pointer on dentry 
-    *dentry_xp = XPTR( local_cxy , dentry );
+    *dentry_xp = XPTR( cxy , dentry_ptr );
 
 #if DEBUG_VFS_DENTRY_CREATE
-thread_t * this  = CURRENT_THREAD;
-uint32_t   cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_VFS_DENTRY_CREATE < cycle )
-printk("\n[%s] thread[%x,%x] created dentry <%s> : (%x,%x) / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, name, local_cxy, dentry, cycle );
+printk("\n[%s] thread[%x,%x] exit for <%s> / dentry (%x,%x)\n",
+__FUNCTION__, this->process->pid, this->trdid, name, cxy, dentry_ptr );
 #endif
 
@@ -519,12 +569,16 @@
 }  // end vfs_dentry_create()
 
-////////////////////////////////////////////////
-void vfs_dentry_destroy( vfs_dentry_t * dentry )
-{
+////////////////////////////////////////////
+void vfs_dentry_destroy( xptr_t  dentry_xp )
+{
+    // get cluster and local pointer
+    vfs_dentry_t * dentry_ptr = GET_PTR( dentry_xp );
+    cxy_t          dentry_cxy = GET_CXY( dentry_xp );
+  
     // release memory allocated to dentry
 	kmem_req_t req;
 	req.type  = KMEM_KCM;
-	req.ptr   = dentry;
-	kmem_free( &req );
+	req.ptr   = dentry_ptr;
+	kmem_remote_free( dentry_cxy , &req );
 
 }  // end vfs_dentry_destroy()
@@ -536,10 +590,17 @@
 
 /////////////////////////////////////////////
-error_t vfs_file_create( vfs_inode_t * inode,
-                         uint32_t      attr,
-                         xptr_t      * file_xp )
-{
-    vfs_file_t  * file;
+error_t vfs_file_create( xptr_t     inode_xp,
+                         uint32_t   attr,
+                         xptr_t   * file_xp )
+{
+    vfs_file_t  * file_ptr;
 	kmem_req_t    req;
+    uint32_t      type;
+    mapper_t    * mapper;
+    vfs_ctx_t   * ctx;
+
+    // get inode cluster and local pointer
+    vfs_inode_t * inode_ptr = GET_PTR( inode_xp );
+    cxy_t         inode_cxy = GET_CXY( inode_xp );
 
 #if DEBUG_VFS_FILE_CREATE
@@ -547,6 +608,6 @@
 uint32_t cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_VFS_OPEN < cycle )
-printk("\n[%s] thread[%x,%x] enter for inode %x in cluster %x / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, inode, local_cxy, cycle );
+printk("\n[%s] thread[%x,%x] enter for inode (%x,%x) / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, inode_cxy, inode_ptr, cycle );
 #endif
 
@@ -555,27 +616,31 @@
 	req.order = bits_log2( sizeof(vfs_file_t) );
     req.flags = AF_KERNEL | AF_ZERO;
-	file      = kmem_alloc( &req );
-
-    if( file == NULL ) return ENOMEM;
+	file_ptr  = kmem_remote_alloc( inode_cxy , &req );
+
+    if( file_ptr == NULL ) return -1;
+
+    // get type, ctx and mapper from inode descriptor
+    type   = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->type ) );
+    ctx    = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->ctx ) );
+    mapper = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->mapper ) );
 
     // initializes new file descriptor
-    file->gc       = 0;
-    file->type     = inode->type;
-    file->attr     = attr;
-    file->offset   = 0;
-    file->refcount = 1;
-    file->inode    = inode;
-    file->ctx      = inode->ctx;
-    file->mapper   = inode->mapper;
-
-    remote_rwlock_init( XPTR( local_cxy , &file->lock ), LOCK_VFS_FILE );
-
-    *file_xp = XPTR( local_cxy , file );
+    hal_remote_s32( XPTR( inode_cxy , &file_ptr->type     ) , type );
+    hal_remote_s32( XPTR( inode_cxy , &file_ptr->attr     ) , attr );
+    hal_remote_s32( XPTR( inode_cxy , &file_ptr->offset   ) , 0 );
+    hal_remote_s32( XPTR( inode_cxy , &file_ptr->refcount ) , 1 );
+    hal_remote_spt( XPTR( inode_cxy , &file_ptr->inode    ) , inode_ptr );
+    hal_remote_spt( XPTR( inode_cxy , &file_ptr->ctx      ) , ctx );
+    hal_remote_spt( XPTR( inode_cxy , &file_ptr->mapper   ) , mapper );
+
+    remote_rwlock_init( XPTR( inode_cxy , &file_ptr->lock ), LOCK_VFS_FILE );
+
+    *file_xp = XPTR( inode_cxy , file_ptr );
 
 #if DEBUG_VFS_FILE_CREATE
 cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_VFS_OPEN < cycle )
-printk("\n[%s] thread[%x,%x] created file %x in cluster %x / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, file, local_cxy, cycle );
+printk("\n[%s] thread[%x,%x] created file (%x,%x) %x\n",
+__FUNCTION__, this->process->pid, this->trdid, inode_cxy, file_ptr, cycle );
 #endif
 
@@ -584,20 +649,25 @@
 }  // end vfs_file_create()
 
-///////////////////////////////////////////
-void vfs_file_destroy( vfs_file_t *  file )
-{
+////////////////////////////////////////
+void vfs_file_destroy( xptr_t  file_xp )
+{
+    // get file cluster and local pointer
+    vfs_file_t * file_ptr = GET_PTR( file_xp );
+    cxy_t        file_cxy = GET_CXY( file_xp );
+
+    // release file descriptor
 	kmem_req_t req;
 	req.type  = KMEM_KCM;
-	req.ptr   = file;
-	kmem_free( &req );
+	req.ptr   = file_ptr;
+	kmem_remote_free( file_cxy , &req );
 
 #if DEBUG_VFS_CLOSE
 char name[CONFIG_VFS_MAX_NAME_LENGTH];
-vfs_file_get_name( XPTR( local_cxy , file ) , name );
+vfs_file_get_name( file_xp , name );
 thread_t * this = CURRENT_THREAD;
 uint32_t cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_VFS_CLOSE < cycle )
 printk("\n[%s] thread[%x,%x] deleted file <%s> in cluster %x / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, name, local_cxy, cycle );
+__FUNCTION__, this->process->pid, this->trdid, name, file_cxy, cycle );
 #endif
 
@@ -738,12 +808,5 @@
 
     // create a new file descriptor in cluster containing inode
-    if( inode_cxy == local_cxy )      // target cluster is local
-    {
-        error = vfs_file_create( inode_ptr , file_attr , &file_xp );
-    }
-    else                              // target cluster is remote
-    {
-        rpc_vfs_file_create_client( inode_cxy , inode_ptr , file_attr , &file_xp , &error );
-    }
+    error = vfs_file_create( inode_xp , file_attr , &file_xp );
 
     if( error )  return error;
@@ -764,6 +827,7 @@
 cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_VFS_OPEN < cycle )
-printk("\n[%s] thread[%x,%x] exit for <%s> / fdid %d / cxy %x / cycle %d\n",
-__FUNCTION__, process->pid, this->trdid, path, file_id, GET_CXY( file_xp ), cycle );
+printk("\n[%s] thread[%x,%x] exit for <%s> / fdid %d / file(%x,%x) / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, path, file_id,
+GET_CXY( file_xp ), GET_PTR( file_xp ), cycle );
 #endif
 
@@ -880,6 +944,6 @@
     __FUNCTION__ , this->process->pid, nbytes );
     else            
-    printk("\n[%s] thread[%x,%x] exit / %d bytes moved from buffer to mapper / size %d\n",
-    __FUNCTION__ , this->process->pid, nbytes, hal_remote_l32(XPTR(file_cxy,&inode->size)) );
+    printk("\n[%s] thread[%x,%x] exit / %d bytes moved from buffer to mapper\n",
+    __FUNCTION__ , this->process->pid, nbytes );
 }
 #endif
@@ -1035,5 +1099,5 @@
 {
     cxy_t         file_cxy;         // cluster containing the file descriptor.
-    vfs_file_t  * file_ptr;         // local ponter on file descriptor
+    vfs_file_t  * file_ptr;         // local pointer on file descriptor
     cxy_t         owner_cxy;        // process owner cluster
     pid_t         pid;              // process identifier
@@ -1076,14 +1140,5 @@
 
     // copy all dirty pages from mapper to device
-    if( file_cxy == local_cxy )
-    {
-        error = mapper_sync( mapper_ptr );
-    }
-    else
-    {
-        rpc_mapper_sync_client( file_cxy,
-                                mapper_ptr,
-                                &error );
-    }
+    error = mapper_sync( XPTR( file_cxy , mapper_ptr ) );
 
     if( error )
@@ -1128,18 +1183,6 @@
  
         // update dentry size in parent directory mapper
-        if( parent_cxy == local_cxy )
-        {
-            error = vfs_fs_update_dentry( parent_inode_ptr,
-                                          parent_dentry_ptr,
-                                          size );
-        }
-        else
-        {
-            rpc_vfs_fs_update_dentry_client( parent_cxy,
-                                             parent_inode_ptr,
-                                             parent_dentry_ptr,
-                                             size,
-                                             &error );
-        }
+        error = vfs_fs_update_dentry( XPTR( parent_cxy , parent_inode_ptr ),
+                                      parent_dentry_ptr );
 
         if( error )
@@ -1159,14 +1202,5 @@
 
         // copy all dirty pages from parent mapper to device
-        if( parent_cxy == local_cxy )
-        {
-            error = mapper_sync( parent_mapper_ptr );
-        }
-        else
-        {
-            rpc_mapper_sync_client( parent_cxy,
-                                    parent_mapper_ptr,
-                                    &error );
-        }
+        error = mapper_sync( XPTR( parent_cxy , parent_mapper_ptr ) );
 
         if( error )
@@ -1222,12 +1256,5 @@
     //////// 4) release memory allocated to file descriptor in remote cluster
 
-    if( file_cxy == local_cxy )             // file cluster is local
-    {
-        vfs_file_destroy( file_ptr );
-    }
-    else                                    // file cluster is local
-    {
-        rpc_vfs_file_destroy_client( file_cxy , file_ptr );
-    }
+    vfs_file_destroy( file_xp );
 
 #if DEBUG_VFS_CLOSE
@@ -1320,19 +1347,8 @@
 
     // 2. create one new dentry in parent cluster
-    if( parent_cxy == local_cxy )  
-    {
-        error = vfs_dentry_create( parent_fs_type,
-                                   last_name,
-                                   &dentry_xp );
-    }
-    else
-    {
-        rpc_vfs_dentry_create_client( parent_cxy,
-                                      parent_fs_type,
-                                      last_name,
-                                      &dentry_xp,
-                                      &error );
-    }
-
+    error = vfs_dentry_create( parent_cxy,
+                               parent_fs_type,
+                               last_name,
+                               &dentry_xp );
     if( error )
     {
@@ -1361,32 +1377,18 @@
     inode_cxy = cluster_random_select();
     
-    if( inode_cxy == local_cxy )      // target cluster is local 
-    {
-        error = vfs_inode_create( parent_fs_type,
-                                  attr,
-                                  rights,
-                                  uid,
-                                  gid,
-                                  &inode_xp );
-    }
-    else                              // target cluster is remote
-    {
-        rpc_vfs_inode_create_client( inode_cxy,
-                                     parent_fs_type,
-                                     attr,
-                                     rights,
-                                     uid,
-                                     gid,
-                                     &inode_xp,
-                                     &error );
-    }
-                                     
+    // create inode
+    error = vfs_inode_create( inode_cxy,
+                              parent_fs_type,
+                              attr,
+                              rights,
+                              uid,
+                              gid,
+                              &inode_xp );
     if( error )
     {
         remote_rwlock_wr_release( lock_xp );
         printk("\n[ERROR] in %s : cannot create new inode in cluster %x for <%s>\n",
-               __FUNCTION__ , inode_cxy , path );
-        if( parent_cxy == local_cxy ) vfs_dentry_destroy( dentry_ptr );
-        else rpc_vfs_dentry_destroy_client( parent_cxy , dentry_ptr );
+         __FUNCTION__ , inode_cxy , path );
+        vfs_dentry_destroy( dentry_xp );
         return -1;
     }
@@ -1434,7 +1436,6 @@
         remote_rwlock_wr_release( lock_xp );
         printk("\n[ERROR] in %s : cannot create new inode in cluster %x for <%s>\n",
-               __FUNCTION__ , inode_cxy , path );
-        if( parent_cxy == local_cxy ) vfs_dentry_destroy( dentry_ptr );
-        else rpc_vfs_dentry_destroy_client( parent_cxy , dentry_ptr );
+        __FUNCTION__ , inode_cxy , path );
+        vfs_dentry_destroy( dentry_xp );
         return -1;
     }
@@ -1445,17 +1446,6 @@
     // 8. update parent directory mapper 
     //    and synchronize the parent directory on IOC device
-    if (parent_cxy == local_cxy)
-    {
-        error = vfs_fs_add_dentry( parent_ptr,
-                                   dentry_ptr );
-    }
-    else
-    {
-        rpc_vfs_fs_add_dentry_client( parent_cxy,
-                                      parent_ptr,
-                                      dentry_ptr,
-                                      &error );
-    }
-
+    error = vfs_fs_add_dentry( parent_xp,
+                               dentry_ptr );
     if( error )
     {
@@ -1589,19 +1579,8 @@
     {
         // 1. create one new dentry
-        if( new_parent_cxy == local_cxy )  
-        {
-            error = vfs_dentry_create( inode_fs_type,
-                                       new_name,
-                                       &dentry_xp );
-        }
-        else
-        {
-            rpc_vfs_dentry_create_client( new_parent_cxy,
-                                          inode_fs_type,
-                                          new_name,
-                                          &dentry_xp,
-                                          &error );
-        }
-
+        error = vfs_dentry_create( new_parent_cxy,
+                                   inode_fs_type,
+                                   new_name,
+                                   &dentry_xp );
         if( error )
         {
@@ -1643,15 +1622,6 @@
         //    and synchronize the parent directory on IOC device
         if (new_parent_cxy == local_cxy)
-        {
-            error = vfs_fs_add_dentry( new_parent_ptr,
-                                       dentry_ptr );
-        }
-        else
-        {
-            rpc_vfs_fs_add_dentry_client( new_parent_cxy,
-                                          new_parent_ptr,
-                                          dentry_ptr,
-                                          &error );
-        }
+        error = vfs_fs_add_dentry( new_parent_xp,
+                                   dentry_ptr );
         if( error )
         {
@@ -1703,5 +1673,6 @@
     vfs_fs_type_t     fs_type;            // File system type
 
-    char              name[CONFIG_VFS_MAX_NAME_LENGTH];  // name of link to remove
+    char              child_name[CONFIG_VFS_MAX_NAME_LENGTH];   // name of link to remove
+    char              parent_name[CONFIG_VFS_MAX_NAME_LENGTH];  // name of parent directory
 
     thread_t  * this    = CURRENT_THREAD;
@@ -1731,20 +1702,19 @@
                         VFS_LOOKUP_PARENT,
                         &parent_xp,
-                        name );
+                        child_name );
     if( error ) 
     {
         remote_rwlock_wr_release( lock_xp );
         printk("\n[ERROR] in %s : cannot get parent inode for <%s> in <%s>\n",
-        __FUNCTION__, name, path );
+        __FUNCTION__, child_name, path );
         return -1;
     }
-
-    // get parent inode cluster and local pointer
+    
+    // get parent inode name, cluster and local pointer
+    vfs_inode_get_name( parent_xp , parent_name );
     parent_cxy = GET_CXY( parent_xp );
     parent_ptr = GET_PTR( parent_xp );
  
 #if( DEBUG_VFS_UNLINK & 1 )
-char parent_name[CONFIG_VFS_MAX_NAME_LENGTH];
-vfs_inode_get_name( parent_xp , parent_name );
 if( DEBUG_VFS_UNLINK < cycle )
 printk("\n[%s] thread[%x,%x] : parent inode <%s> is (%x,%x)\n",
@@ -1756,7 +1726,7 @@
 
     // try to get extended pointer on dentry from Inode Tree
-    dentry_xp = xhtab_lookup( children_xp , name );
+    dentry_xp = xhtab_lookup( children_xp , child_name );
     
-    // when dentry not found in Inode Tree, try to get it from inode tree
+    // when dentry not found in Inode Tree, try to get it from mapper
 
     if( dentry_xp == XPTR_NULL )           // miss target dentry in Inode Tree
@@ -1765,6 +1735,6 @@
 #if( DEBUG_VFS_UNLINK & 1 )
 if( DEBUG_VFS_UNLINK < cycle )
-printk("\n[%s] thread[%x,%x] : inode <%s> not found => scan parent mapper\n",
-__FUNCTION__, process->pid, this->trdid, name );
+printk("\n[%s] thread[%x,%x] : inode <%s> not found => scan parent mapper <%s>\n",
+__FUNCTION__, process->pid, this->trdid, child_name , parent_name );
 #endif
         // get parent inode FS type
@@ -1779,13 +1749,11 @@
                                          fs_type, 
                                          parent_xp, 
-                                         name, 
+                                         child_name, 
                                          &dentry_xp,
                                          &inode_xp );
         if( error )
         {
-            printk("\n[ERROR] in %s : cannot create inode <%s> in path <%s>\n",
-            __FUNCTION__ , name, path );
-
-            vfs_remove_child_from_parent( dentry_xp );
+            printk("\n[ERROR] in %s : cannot create inode <%s> in Inode Tree\n",
+            __FUNCTION__ , child_name );
             return -1;
         }
@@ -1797,23 +1765,10 @@
         // scan parent mapper to find the missing dentry, and complete 
         // initialisation of new dentry and new inode descriptors In Inode Tree
-        if( parent_cxy == local_cxy )
+        error = vfs_fs_new_dentry_from_mapper( parent_xp,
+                                               dentry_ptr );
+        if ( error ) 
         {
-            error = vfs_fs_new_dentry( parent_ptr,
-                                       name,
-                                       inode_xp );
-        }
-        else
-        {
-            rpc_vfs_fs_new_dentry_client( parent_cxy,
-                                          parent_ptr,
-                                          name,
-                                          inode_xp,
-                                          &error );
-        }
-
-        if ( error )   // dentry not found in parent mapper
-        {
-            printk("\n[ERROR] in %s : cannot get dentry <%s> in path <%s>\n",
-            __FUNCTION__ , name, path );
+            printk("\n[ERROR] in %s : cannot get entry <%s> in parent <%s> mapper\n",
+            __FUNCTION__ , child_name, parent_name );
             return -1;
         }
@@ -1822,5 +1777,5 @@
 if( DEBUG_VFS_UNLINK < cycle )
 printk("\n[%s] thread[%x,%x] : created missing inode & dentry <%s> in cluster %x\n",
-__FUNCTION__, process->pid, this->trdid, name, inode_cxy );
+__FUNCTION__, process->pid, this->trdid, child_name, inode_cxy );
 #endif
 
@@ -1856,5 +1811,6 @@
 if( DEBUG_VFS_UNLINK < cycle )
 printk("\n[%s] thread[%x,%x] : unlink inode <%s> / type %s / %d links\n",
-__FUNCTION__, process->pid, this->trdid, name, vfs_inode_type_str(inode_type), inode_links );
+__FUNCTION__, process->pid, this->trdid, child_name, 
+vfs_inode_type_str(inode_type), inode_links );
 #endif
 
@@ -1898,17 +1854,6 @@
         // 2. update parent directory mapper
         //    and synchronize the parent directory on IOC device
-        if (parent_cxy == local_cxy)
-        {
-            error = vfs_fs_remove_dentry( parent_ptr,
-                                          dentry_ptr );
-        }
-        else            
-        {
-            rpc_vfs_fs_remove_dentry_client( parent_cxy,
-                                             parent_ptr,
-                                             dentry_ptr,
-                                             &error );
-        }
-
+        error = vfs_fs_remove_dentry( parent_xp,
+                                      dentry_ptr );
         if( error )
         {
@@ -2172,5 +2117,5 @@
 
 //////////////////////////////////////////////////////////////////////////
-// This static function is called by the vfs_display() function.
+// This recursive function is called by the vfs_display() function.
 // that is supposed to take the TXT0 lock.
 //////////////////////////////////////////////////////////////////////////
@@ -2184,5 +2129,4 @@
     uint32_t           inode_size;
     uint32_t           inode_attr;
-    uint32_t           inode_dirty;
     void             * inode_extd;
 
@@ -2234,10 +2178,10 @@
 
     // compute dirty
-    inode_dirty = ((inode_attr & INODE_ATTR_DIRTY) != 0);
+    // inode_dirty = ((inode_attr & INODE_ATTR_DIRTY) != 0); unused [AG] dec 2019
 
     // display inode
-    nolock_printk("%s<%s> : %s / extd %x / %d bytes / dirty %d / cxy %x / inode %x / mapper %x\n",
+    nolock_printk("%s<%s> : %s / extd %x / %d bytes / cxy %x / inode %x / mapper %x\n",
     indent_str[indent], name, vfs_inode_type_str( inode_type ), (uint32_t)inode_extd,
-    inode_size, inode_dirty, inode_cxy, inode_ptr, mapper_ptr );
+    inode_size, inode_cxy, inode_ptr, mapper_ptr );
 
     // scan directory entries when current inode is a directory
@@ -2328,5 +2272,5 @@
 
     // print header
-    nolock_printk("\n***** file system state\n\n");
+    nolock_printk("\n***** current VFS state\n\n");
 
     // call recursive function
@@ -2484,4 +2428,5 @@
     vfs_inode_t      * parent_ptr;   // local pointer on parent inode  
     xptr_t             dentry_xp;    // extended pointer on dentry       
+    vfs_dentry_t     * dentry_ptr;   // local pointer on dentry
     xptr_t             child_xp;     // extended pointer on child inode
     cxy_t              child_cxy;    // cluster for child inode
@@ -2532,4 +2477,6 @@
     last      = false;
     child_xp  = XPTR_NULL;
+    child_cxy = 0;
+    child_ptr = NULL;
 
     // loop on nodes in pathname
@@ -2570,9 +2517,5 @@
                                &child_xp );
 
-        // get child inode local pointer and cluster
-        child_ptr  = GET_PTR( child_xp );
-        child_cxy  = GET_CXY( child_xp );
-
-        if( found == false )                              // not found in Inode Tree 
+        if( found == false )                          // child not found in Inode Tree 
         {
             // when a inode is not found in the Inode Tree:
@@ -2606,5 +2549,5 @@
 #endif
                 // get parent inode FS type
-                ctx_ptr    = hal_remote_lpt( XPTR( parent_cxy,&parent_ptr->ctx ) );
+                ctx_ptr    = hal_remote_lpt( XPTR( parent_cxy , &parent_ptr->ctx ) );
                 fs_type    = hal_remote_l32( XPTR( parent_cxy , &ctx_ptr->type ) );
 
@@ -2626,6 +2569,7 @@
                 }
 
-                // get child inode local pointer
-                child_ptr = GET_PTR( child_xp );
+                // get child inode and dentry local pointers
+                child_ptr  = GET_PTR( child_xp );
+                dentry_ptr = GET_PTR( dentry_xp );
 
 #if (DEBUG_VFS_LOOKUP & 1)
@@ -2636,32 +2580,17 @@
                 // scan parent mapper to find the missing dentry, and complete 
                 // the initialisation of dentry and child inode descriptors
-                if( parent_cxy == local_cxy )
-                {
-                    error = vfs_fs_new_dentry( parent_ptr,
-                                               name,
-                                               child_xp );
-                }
-                else
-                {
-                    rpc_vfs_fs_new_dentry_client( parent_cxy,
-                                                  parent_ptr,
-                                                  name,
-                                                  child_xp,
-                                                  &error );
-                }
-
-                // when the missing dentry is not in the parent mapper,
-                // a new dentry must be registered in parent directory mapper
-                if ( error )
+                error = vfs_fs_new_dentry_from_mapper( parent_xp,
+                                                       dentry_ptr );
+
+                if ( error )  // an error can be fatal or non-fatal :
                 {
                     if ( last && create )  // add a brand new dentry in parent directory
                     {
-                        error = vfs_new_dentry_init( parent_xp,                
-                                                     dentry_xp,
-                                                     child_xp );
+                        error = vfs_fs_new_dentry_to_mapper( parent_xp,                
+                                                             dentry_ptr );
                         if ( error )
                         {
-                            printk("\n[ERROR] in %s : cannot init inode <%s> in path <%s>\n",
-                            __FUNCTION__, name, pathname );
+                            printk("\n[ERROR] in %s : cannot add dentry <%s> in parent dir\n",
+                            __FUNCTION__, name );
                             vfs_remove_child_from_parent( dentry_xp );
                             return -1;
@@ -2674,6 +2603,4 @@
 vfs_inode_display( child_xp );
 #endif
-
-
                     }
                     else                   // not last or not create => error
@@ -2704,14 +2631,6 @@
                     if( type == INODE_TYPE_DIR ) 
                     {
-                        if( child_cxy == local_cxy )
-                        {
-                            error = vfs_inode_load_all_pages( child_ptr );
-                        }
-                        else
-                        {
-                            rpc_vfs_inode_load_all_pages_client( child_cxy,
-                                                                 child_ptr,
-                                                                 &error );
-                        }
+                        error = vfs_inode_load_all_pages( child_xp );
+
                         if ( error )
                         {
@@ -2731,6 +2650,9 @@
             }
         }
-        else                                    // child directly found in inode tree
+        else                                    // child found in Inode Tree
         {
+            // get child inode local pointer and cluster
+            child_ptr  = GET_PTR( child_xp );
+            child_cxy  = GET_CXY( child_xp );
         
 #if (DEBUG_VFS_LOOKUP & 1)
@@ -2759,8 +2681,4 @@
         // }
 
-        // take lock on child inode and release lock on parent
-        // vfs_inode_lock( child_xp );
-        // vfs_inode_unlock( parent_xp );
-
         // exit when last
         if ( last )           // last inode in path  => return relevant info
@@ -2792,106 +2710,19 @@
             }
         }
-        else                     // not the last inode in path => update loop variables
+        else                     // not the last node in path => update loop variables
         {
             parent_xp = child_xp;
             current   = next;
         }
-    }
+    }  // end while loop on nodes in pathname
+
+#if ( DEBUG_VFS_LOOKUP & 1 )
+if( DEBUG_VFS_LOOKUP < cycle )
+vfs_display( root_xp );
+#endif
 
     return 0;
 
 }  // end vfs_lookup()
-
-////////////////////////////////////////////////
-error_t vfs_new_dentry_init( xptr_t   parent_xp,
-                             xptr_t   dentry_xp,
-                             xptr_t   child_xp )
-{
-    error_t     error;
-    uint32_t    cluster_id;
-    uint32_t    child_type;
-    uint32_t    child_size;
-
-#if DEBUG_VFS_NEW_DENTRY_INIT
-char parent_name[CONFIG_VFS_MAX_NAME_LENGTH];
-char child_name[CONFIG_VFS_MAX_NAME_LENGTH];
-vfs_inode_get_name( parent_xp , parent_name );
-vfs_inode_get_name( child_xp  , child_name );
-uint32_t   cycle = (uint32_t)hal_get_cycles();
-thread_t * this  = CURRENT_THREAD;
-if( DEBUG_VFS_NEW_DENTRY_INIT < cycle )
-printk("\n[%s] thread[%x,%x] enter / parent <%s> / child <%s> / cycle %d\n",
-__FUNCTION__ , this->process->pid, this->trdid, parent_name, child_name, cycle );
-#endif
-
-    // get parent inode cluster and local pointer
-    cxy_t          parent_cxy = GET_CXY( parent_xp );
-    vfs_inode_t  * parent_ptr = GET_PTR( parent_xp );
-
-    // get dentry local pointer
-    vfs_dentry_t * dentry_ptr = GET_PTR( dentry_xp );
-
-    // get child inode cluster and local pointer
-    cxy_t          child_cxy  = GET_CXY( child_xp );
-    vfs_inode_t  * child_ptr  = GET_PTR( child_xp );
-
-    // 1. allocate one free cluster_id in file system to child inode,
-    // and update the File Allocation Table in both the FAT mapper and IOC device.
-    // It depends on the child inode FS type.
-    vfs_ctx_t * ctx = hal_remote_lpt( XPTR( child_cxy , &child_ptr->ctx ) );
-
-    error = vfs_fs_cluster_alloc( ctx->type,
-                                  &cluster_id );
-    if ( error )
-    {
-        printk("\n[ERROR] in %s : cannot find a free VFS cluster_id\n",
-        __FUNCTION__ );
-        return -1;
-    }
-
-#if( DEBUG_VFS_NEW_DENTRY_INIT & 1)
-if( DEBUG_VFS_NEW_DENTRY_INIT < cycle )
-printk("\n[%s] thread[%x,%x] allocated FS cluster_id %x to <%s>\n",
-__FUNCTION__ , this->process->pid, this->trdid, cluster_id, child_name );
-#endif
-
-    // 2. update the child inode descriptor size and extend
-    child_type = hal_remote_l32( XPTR( child_cxy , &child_ptr->type ) );
-    child_size = 0;
-    
-    hal_remote_s32( XPTR( child_cxy , &child_ptr->size )   , child_size );
-    hal_remote_spt( XPTR( child_cxy , &child_ptr->extend ) , (void*)(intptr_t)cluster_id );
-
-    // 3. update the parent inode mapper, and 
-    // update the dentry extension if required
-    if( local_cxy == parent_cxy )
-    {
-        error = vfs_fs_add_dentry( parent_ptr,
-                                   dentry_ptr );
-    }
-    else
-    {
-        rpc_vfs_fs_add_dentry_client( parent_cxy,
-                                      parent_ptr,
-                                      dentry_ptr,
-                                      &error );
-    }
-    if ( error )
-    {
-        printk("\n[ERROR] in %s : cannot register child in parent directory\n",
-        __FUNCTION__ );
-        return -1;
-    }
-
-#if DEBUG_VFS_NEW_DENTRY_INIT
-cycle = (uint32_t)hal_get_cycles();
-if( DEBUG_VFS_NEW_DENTRY_INIT < cycle )
-printk("\n[%s] thread[%x,%x] exit / parent <%s> / child <%s> / cycle %d\n",
-__FUNCTION__ , this->process->pid, this->trdid, parent_name, child_name, cycle );
-#endif
-
-    return 0;
-
-}  // end vfs_new_dentry_init()
 
 ///////////////////////////////////////////////////
@@ -2906,7 +2737,4 @@
     xptr_t          dentry_xp;         // extended pointer on dentry (used for . and ..)
     vfs_dentry_t  * dentry_ptr;        // local pointer on dentry (used for . and ..) 
-
-    // xptr_t          parents_root_xp;   // extended pointer on inode "parents" field
-    // xptr_t          parents_entry_xp;  // extended pointer on dentry "parents" field
     xptr_t          children_xhtab_xp; // extended pointer on inode "children" field
     xptr_t          children_entry_xp; // extended pointer on dentry "children" field
@@ -2924,5 +2752,5 @@
 #endif
 
-    // get new directory cluster and local pointer
+    // get child directory cluster and local pointer
     child_cxy  = GET_CXY( child_xp );
     child_ptr  = GET_PTR( child_xp );
@@ -2933,18 +2761,8 @@
 
     //////////////////////////// create <.> dentry //////////////////////
-    if( child_cxy == local_cxy )     
-    {
-        error = vfs_dentry_create( fs_type,
-                                   ".",
-                                   &dentry_xp );
-    }
-    else
-    {
-        rpc_vfs_dentry_create_client( child_cxy,
-                                      fs_type,
-                                      ".",
-                                      &dentry_xp,
-                                      &error );
-    }
+    error = vfs_dentry_create( child_cxy,
+                               fs_type,
+                               ".",
+                               &dentry_xp );
     if( error )
     {
@@ -2967,5 +2785,7 @@
     children_xhtab_xp = XPTR( child_cxy , &child_ptr->children );
     children_entry_xp = XPTR( child_cxy , &dentry_ptr->children );
+
     error = xhtab_insert( children_xhtab_xp , "." , children_entry_xp );
+
     if( error )
     {
@@ -2974,12 +2794,5 @@
         return -1;
     }
-
     
-    // don't register <.> dentry in child_inode xlist of parents 
-    // parents_root_xp  = XPTR( child_cxy , &child_ptr->parents );
-    // parents_entry_xp = XPTR( child_cxy , &dentry_ptr->parents );
-    // xlist_add_first( parents_root_xp , parents_entry_xp );
-    // hal_remote_atomic_add( XPTR( child_cxy , &child_ptr->links ) , 1 );
-
     // update "parent" and "child_xp" fields in <.> dentry
     hal_remote_s64( XPTR( child_cxy , &dentry_ptr->child_xp ) , child_xp );
@@ -2997,16 +2810,6 @@
     if( child_xp != parent_xp )
     {
-        if( child_cxy == local_cxy )
-        { 
-            error = vfs_fs_add_dentry( child_ptr,
-                                       dentry_ptr );
-        }
-        else
-        {
-            rpc_vfs_fs_add_dentry_client( child_cxy,
-                                          child_ptr,
-                                          dentry_ptr,
-                                          &error );
-        }
+        error = vfs_fs_add_dentry( child_xp,
+                                   dentry_ptr );
         if( error )
         {
@@ -3026,18 +2829,8 @@
 
     ///////////////////////////// create <..> dentry ///////////////////////
-    if( child_cxy == local_cxy )     
-    {
-        error = vfs_dentry_create( fs_type,
-                                   "..",
-                                   &dentry_xp );
-    }
-    else
-    {
-        rpc_vfs_dentry_create_client( child_cxy,
-                                      fs_type,
-                                      "..",
-                                      &dentry_xp,
-                                      &error );
-    }
+    error = vfs_dentry_create( child_cxy,
+                               fs_type,
+                               "..",
+                               &dentry_xp );
     if( error )
     {
@@ -3085,16 +2878,6 @@
     if( child_xp != parent_xp )
     {
-        if( child_cxy == local_cxy )
-        { 
-            error = vfs_fs_add_dentry( child_ptr,
-                                       dentry_ptr );
-        }
-        else
-        {
-            rpc_vfs_fs_add_dentry_client( child_cxy,
-                                          child_ptr,
-                                          dentry_ptr,
-                                          &error );
-        }
+        error = vfs_fs_add_dentry( child_xp,
+                                   dentry_ptr );
         if( error )
         {
@@ -3259,5 +3042,5 @@
 
       
-////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////
 error_t vfs_add_child_in_parent( cxy_t              child_cxy,
                                  vfs_fs_type_t      fs_type,
@@ -3296,19 +3079,8 @@
 
     // 1. create dentry in parent cluster
-    if( parent_cxy == local_cxy )           // parent cluster is local
-    {
-        error = vfs_dentry_create( fs_type,
-                                   name,
-                                   &new_dentry_xp );
-    }
-    else                                    // parent cluster is remote
-    {
-        rpc_vfs_dentry_create_client( parent_cxy,
-                                      fs_type,
-                                      name,
-                                      &new_dentry_xp,
-                                      &error );
-    }
-                                      
+    error = vfs_dentry_create( parent_cxy,
+                               fs_type,
+                               name,
+                               &new_dentry_xp );
     if( error )
     {
@@ -3334,25 +3106,11 @@
     uint32_t gid  = 0;
     
-    if( child_cxy == local_cxy )      // child cluster is local 
-    {
-        error = vfs_inode_create( fs_type,
-                                  attr,
-                                  mode,
-                                  uid,
-                                  gid,
-                                  &new_inode_xp );
-    }
-    else                              // child cluster is remote
-    {
-        rpc_vfs_inode_create_client( child_cxy,
-                                     fs_type,
-                                     attr,
-                                     mode,
-                                     uid,
-                                     gid,
-                                     &new_inode_xp,
-                                     &error );
-    }
-                                     
+    error = vfs_inode_create( child_cxy,
+                              fs_type,
+                              attr,
+                              mode,
+                              uid,
+                              gid,
+                              &new_inode_xp );
     if( error )
     {
@@ -3360,6 +3118,5 @@
                __FUNCTION__ , child_cxy );
  
-        if( parent_cxy == local_cxy ) vfs_dentry_destroy( new_dentry_ptr );
-        else rpc_vfs_dentry_destroy_client( parent_cxy , new_dentry_ptr );
+        vfs_dentry_destroy( new_dentry_xp );
         return -1;
     }
@@ -3374,5 +3131,4 @@
 #endif
 
-
     // 3. register new_dentry in new_inode xlist of parents 
     parents_root_xp  = XPTR( child_cxy , &new_inode_ptr->parents );
@@ -3457,26 +3213,8 @@
 
     // delete dentry descriptor
-    if( parent_cxy == local_cxy )
-    {
-         vfs_dentry_destroy( dentry_ptr );
-    }
-    else
-    {
-         rpc_vfs_dentry_destroy_client( parent_cxy,
-                                        dentry_ptr );
-    }
+    vfs_dentry_destroy( dentry_xp );
 
     // delete child_inode descriptor if last link
-    if( links == 1 )
-    {
-        if( child_cxy == local_cxy )
-        {
-            vfs_inode_destroy( child_inode_ptr );
-        }
-        else
-        {
-            rpc_vfs_inode_destroy_client( child_cxy , child_inode_ptr );
-        }
-    }
+    if( links == 1 )  vfs_inode_destroy( child_inode_xp );
 
 }  // end vfs_remove_child_from_parent()
@@ -3489,35 +3227,36 @@
 //////////////////////////////////////////////////////////////////////////////////////////
 
-//////////////////////////////////////////////
-error_t vfs_fs_move_page( xptr_t      page_xp,
-                          cmd_type_t  cmd_type )
+///////////////////////////////////////////////////
+error_t vfs_fs_add_dentry( xptr_t         inode_xp,
+                           vfs_dentry_t * dentry_ptr )
 {
     error_t error = 0;
 
-assert( (page_xp != XPTR_NULL) , "page pointer is NULL" );
-
-    page_t * page_ptr = GET_PTR( page_xp );
-    cxy_t    page_cxy = GET_CXY( page_xp );
-
-    // get local pointer on page mapper
-    mapper_t * mapper = hal_remote_lpt( XPTR( page_cxy , &page_ptr->mapper ) );
-
-assert( (mapper != NULL) , "no mapper for page" );
+assert( (inode_xp   != XPTR_NULL) , "inode_xp argument is NULL" );
+assert( (dentry_ptr != NULL     ) , "dentry_ptr argument is NULL" );
+
+    vfs_inode_t * inode_ptr = GET_PTR( inode_xp );
+    cxy_t         inode_cxy = GET_CXY( inode_xp );
+
+    // get inode mapper
+    mapper_t * mapper = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->mapper ) );
+
+assert( (mapper != NULL) , "mapper pointer is NULL")        
 
     // get FS type
-    vfs_fs_type_t fs_type = hal_remote_l32( XPTR( page_cxy , &mapper->type ) );
+    vfs_fs_type_t fs_type = hal_remote_l32( XPTR( inode_cxy , &mapper->fs_type ) );
 
     // call relevant FS function
     if( fs_type == FS_TYPE_FATFS )
     {
-        error = fatfs_move_page( page_xp , cmd_type ); 
+        error = fatfs_add_dentry( inode_xp , dentry_ptr ); 
     }
     else if( fs_type == FS_TYPE_RAMFS )
     {
-        assert( false , "should not be called for RAMFS\n" );
+        error = 0;     // does nothing for RAMFS
     }
     else if( fs_type == FS_TYPE_DEVFS )
     {
-        assert( false , "should not be called for DEVFS\n" );
+        error = 0;     // does nothing for DEVFS
     }
     else
@@ -3528,26 +3267,31 @@
     return error;
 
-}  // end vfs_fs_move_page()
-
-////////////////////////////////////////////////
-error_t vfs_fs_add_dentry( vfs_inode_t  * inode,
-                           vfs_dentry_t * dentry )
+}  // end vfs_fs_add_dentry()
+
+//////////////////////////////////////////////////////
+error_t vfs_fs_remove_dentry( xptr_t         inode_xp,
+                              vfs_dentry_t * dentry_ptr )
 {
     error_t error = 0;
 
-assert( (inode  != NULL) , "inode  pointer is NULL" );
-assert( (dentry != NULL) , "dentry pointer is NULL" );
-
-    mapper_t * mapper = inode->mapper;
-
-assert( (mapper != NULL) , "mapper pointer is NULL" );
+assert( (inode_xp   != XPTR_NULL) , "inode_xp argument is NULL" );
+assert( (dentry_ptr != NULL     ) , "dentry_ptr argument is NULL" );
+
+    vfs_inode_t * inode_ptr = GET_PTR( inode_xp );
+    cxy_t         inode_cxy = GET_CXY( inode_xp );
+
+    // get inode mapper
+    mapper_t * mapper = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->mapper ) );
+
+assert( (mapper != NULL) , "mapper pointer is NULL")        
 
     // get FS type
-    vfs_fs_type_t fs_type = mapper->type;
+    vfs_fs_type_t fs_type = hal_remote_l32( XPTR( inode_cxy , &mapper->fs_type ) );
 
     // call relevant FS function
     if( fs_type == FS_TYPE_FATFS )
     {
-        error = fatfs_add_dentry( inode , dentry ); 
+        error = fatfs_remove_dentry( inode_xp , dentry_ptr ); 
+
     }
     else if( fs_type == FS_TYPE_RAMFS )
@@ -3566,34 +3310,38 @@
     return error;
 
-}  // end vfs_fs_add_dentry()
-
-///////////////////////////////////////////////////
-error_t vfs_fs_remove_dentry( vfs_inode_t  * inode,
-                              vfs_dentry_t * dentry )
+}  // end vfs_fs_remove_dentry()
+
+///////////////////////////////////////////////////////////////
+error_t vfs_fs_new_dentry_from_mapper( xptr_t         inode_xp,
+                                       vfs_dentry_t * dentry_ptr )
 {
     error_t error = 0;
 
-assert( (inode  != NULL) , "inode  pointer is NULL" );
-assert( (dentry != NULL) , "dentry pointer is NULL" );
-
-    mapper_t * mapper = inode->mapper;
-
-assert( (mapper != NULL) , "mapper pointer is NULL" );
+assert( (inode_xp   != XPTR_NULL) , "inode_xp argument is NULL" );
+assert( (dentry_ptr != NULL     ) , "dentry_ptr argument is NULL" );
+
+    vfs_inode_t * inode_ptr = GET_PTR( inode_xp );
+    cxy_t         inode_cxy = GET_CXY( inode_xp );
+
+    // get inode mapper
+    mapper_t * mapper = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->mapper ) );
+
+assert( (mapper != NULL) , "mapper pointer is NULL")        
 
     // get FS type
-    vfs_fs_type_t fs_type = mapper->type;
+    vfs_fs_type_t fs_type = hal_remote_l32( XPTR( inode_cxy , &mapper->fs_type ) );
 
     // call relevant FS function
     if( fs_type == FS_TYPE_FATFS )
     {
-        error = fatfs_remove_dentry( inode , dentry ); 
+        error = fatfs_new_dentry_from_mapper( inode_xp , dentry_ptr );
     }
     else if( fs_type == FS_TYPE_RAMFS )
     {
-        error = 0;     // does nothing for RAMFS
+        assert( false , "should not be called for RAMFS" );
     }
     else if( fs_type == FS_TYPE_DEVFS )
     {
-        error = 0;     // does nothing for DEVFS
+        assert( false , "should not be called for DEVFS" );
     }
     else
@@ -3604,24 +3352,30 @@
     return error;
 
-}  // end vfs_fs_remove_dentry()
-
-////////////////////////////////////////////////
-error_t vfs_fs_new_dentry( vfs_inode_t * parent,
-                           char        * name,
-                           xptr_t        child_xp )
+} // end vfs_fs_new_dentry_from_mapper()
+
+///////////////////////////////////////////////////////////////
+error_t vfs_fs_new_dentry_to_mapper( xptr_t         inode_xp,
+                                     vfs_dentry_t * dentry_ptr )
 {
     error_t error = 0;
 
-// check arguments
-assert( (parent != NULL) , "parent pointer is NULL");
-assert( (child_xp != XPTR_NULL) , "child pointer is NULL");
-
-    // get parent inode FS type
-    vfs_fs_type_t fs_type = parent->ctx->type;
+assert( (inode_xp   != XPTR_NULL) , "inode_xp argument is NULL" );
+assert( (dentry_ptr != NULL     ) , "dentry_ptr argument is NULL" );
+
+    vfs_inode_t * inode_ptr = GET_PTR( inode_xp );
+    cxy_t         inode_cxy = GET_CXY( inode_xp );
+
+    // get inode mapper
+    mapper_t * mapper = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->mapper ) );
+
+assert( (mapper != NULL) , "mapper pointer is NULL")        
+
+    // get FS type
+    vfs_fs_type_t fs_type = hal_remote_l32( XPTR( inode_cxy , &mapper->fs_type ) );
 
     // call relevant FS function
     if( fs_type == FS_TYPE_FATFS )
     {
-        error = fatfs_new_dentry( parent , name , child_xp );
+        error = fatfs_new_dentry_to_mapper( inode_xp , dentry_ptr );
     }
     else if( fs_type == FS_TYPE_RAMFS )
@@ -3640,24 +3394,30 @@
     return error;
 
-} // end vfs_fs_new_dentry()
-
-///////////////////////////////////////////////////
-error_t vfs_fs_update_dentry( vfs_inode_t  * inode,
-                              vfs_dentry_t * dentry,
-                              uint32_t       size )
+} // end vfs_fs_new_dentry_to_mapper()
+
+//////////////////////////////////////////////////////
+error_t vfs_fs_update_dentry( xptr_t         inode_xp,
+                              vfs_dentry_t * dentry_ptr )
 {
     error_t error = 0;
 
-// check arguments
-assert( (inode  != NULL) , "inode  pointer is NULL");
-assert( (dentry != NULL) , "dentry pointer is NULL");
-
-    // get parent inode FS type
-    vfs_fs_type_t fs_type = inode->ctx->type;
+assert( (inode_xp   != XPTR_NULL) , "inode_xp argument is NULL" );
+assert( (dentry_ptr != NULL     ) , "dentry_ptr argument is NULL" );
+
+    vfs_inode_t * inode_ptr = GET_PTR( inode_xp );
+    cxy_t         inode_cxy = GET_CXY( inode_xp );
+
+    // get inode mapper
+    mapper_t * mapper = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->mapper ) );
+
+assert( (mapper != NULL) , "mapper pointer is NULL")        
+
+    // get FS type
+    vfs_fs_type_t fs_type = hal_remote_l32( XPTR( inode_cxy , &mapper->fs_type ) );
 
     // call relevant FS function
     if( fs_type == FS_TYPE_FATFS )
     {
-        error = fatfs_update_dentry( inode , dentry , size );
+        error = fatfs_update_dentry( inode_xp , dentry_ptr );
     }
     else if( fs_type == FS_TYPE_RAMFS )
@@ -3739,19 +3499,26 @@
 }  // end vfs_fs_get_user_dir()
  
-////////////////////////////////////////////////
-error_t vfs_fs_sync_inode( vfs_inode_t * inode )
+/////////////////////////////////////////////
+error_t vfs_fs_sync_inode( xptr_t  inode_xp )
 {
     error_t error = 0;
 
-// check arguments
-assert( (inode != NULL) , "inode pointer is NULL");
-
-    // get inode FS type
-    vfs_fs_type_t fs_type = inode->ctx->type;
+assert( (inode_xp != XPTR_NULL) , "inode_xp argument is NULL");
+
+    vfs_inode_t * inode_ptr = GET_PTR( inode_xp );
+    cxy_t         inode_cxy = GET_CXY( inode_xp );
+
+    // get inode mapper
+    mapper_t * mapper = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->mapper ) );
+
+assert( (mapper != NULL) , "mapper pointer is NULL")        
+
+    // get FS type
+    vfs_fs_type_t fs_type = hal_remote_l32( XPTR( inode_cxy , &mapper->fs_type ) );
 
     // call relevant FS function
     if( fs_type == FS_TYPE_FATFS )
     {
-        error = fatfs_sync_inode( inode );
+        error = fatfs_sync_inode( inode_xp );
     }
     else if( fs_type == FS_TYPE_RAMFS )
@@ -3799,59 +3566,4 @@
 }  // end vfs_fs_sync_fat()
 
-//////////////////////////////////////////////////////
-error_t vfs_fs_sync_free_info( vfs_fs_type_t fs_type )
-{
-    error_t error = 0;
-
-    // call relevant FS function
-    if( fs_type == FS_TYPE_FATFS )
-    {
-        error = fatfs_sync_free_info();
-    }
-    else if( fs_type == FS_TYPE_RAMFS )
-    {
-        assert( false , "should not be called for RAMFS" );
-    }
-    else if( fs_type == FS_TYPE_DEVFS )
-    {
-        assert( false , "should not be called for DEVFS" );
-    }
-    else
-    {
-        assert( false , "undefined file system type" );
-    }
-
-    return error;
-
-}  // end vfs_fs_sync_fat()
-
-/////////////////////////////////////////////////
-error_t vfs_fs_cluster_alloc( uint32_t   fs_type,
-                              uint32_t * cluster )
-{
-    error_t error = 0;
-
-    // call relevant FS function
-    if( fs_type == FS_TYPE_FATFS )
-    {
-        error = fatfs_cluster_alloc( cluster );
-    }
-    else if( fs_type == FS_TYPE_RAMFS )
-    {
-        assert( false , "should not be called for RAMFS" );
-    }
-    else if( fs_type == FS_TYPE_DEVFS )
-    {
-        assert( false , "should not be called for DEVFS" );
-    }
-    else
-    {
-        assert( false , "undefined file system type" );
-    }
-
-    return error;
-
-} // end vfs_fs_cluster_alloc()
-
 ////////////////////////////////////////////////
 error_t vfs_fs_release_inode( xptr_t  inode_xp )
@@ -3859,10 +3571,10 @@
     error_t error = 0;
 
-assert( (inode_xp  != XPTR_NULL) , "inode pointer is NULL")        
+assert( (inode_xp  != XPTR_NULL) , "inode_xp argument is NULL")        
 
     vfs_inode_t * inode_ptr = GET_PTR( inode_xp );
     cxy_t         inode_cxy = GET_CXY( inode_xp );
 
-    // get local pointer on page mapper 
+    // get local pointer on mapper 
     mapper_t * mapper = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->mapper ) );
 
@@ -3870,5 +3582,5 @@
 
     // get FS type from mapper
-    vfs_fs_type_t fs_type = hal_remote_l32( XPTR( inode_cxy , &mapper->type ) );
+    vfs_fs_type_t fs_type = hal_remote_l32( XPTR( inode_cxy , &mapper->fs_type ) );
 
     // call relevant FS function
@@ -3894,3 +3606,44 @@
 }  // end vfs_fs_release_inode()
 
-
+//////////////////////////////////////////////////
+error_t vfs_fs_move_page( xptr_t          page_xp,
+                          ioc_cmd_type_t  cmd_type )
+{
+    error_t error = 0;
+
+assert( (page_xp != XPTR_NULL) , "page pointer is NULL" );
+
+    page_t * page_ptr = GET_PTR( page_xp );
+    cxy_t    page_cxy = GET_CXY( page_xp );
+
+    // get local pointer on  mapper
+    mapper_t * mapper = hal_remote_lpt( XPTR( page_cxy , &page_ptr->mapper ) );
+
+assert( (mapper != NULL) , "no mapper for page" );
+
+    // get FS type
+    vfs_fs_type_t fs_type = hal_remote_l32( XPTR( page_cxy , &mapper->fs_type ) );
+
+    // call relevant FS function
+    if( fs_type == FS_TYPE_FATFS )
+    {
+        error = fatfs_move_page( page_xp , cmd_type ); 
+    }
+    else if( fs_type == FS_TYPE_RAMFS )
+    {
+        assert( false , "should not be called for RAMFS\n" );
+    }
+    else if( fs_type == FS_TYPE_DEVFS )
+    {
+        assert( false , "should not be called for DEVFS\n" );
+    }
+    else
+    {
+        assert( false , "undefined file system type" );
+    }
+
+    return error;
+
+}  // end vfs_fs_move_page()
+
+
Index: /trunk/kernel/fs/vfs.h
===================================================================
--- /trunk/kernel/fs/vfs.h	(revision 656)
+++ /trunk/kernel/fs/vfs.h	(revision 657)
@@ -3,5 +3,5 @@
  *
  * Author  Mohamed Lamine Karaoui (2014,2015)
- *         Alain Greiner (2016,2017,2018)
+ *         Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -55,4 +55,5 @@
 struct vseg_s;
 struct page_s;
+struct ksock_s;
 
 /******************************************************************************************
@@ -72,13 +73,13 @@
  * The <extend> field is a pointer on the FS specific context extension. 
  * This extension is dynamically allocated by kernel_init in all clusters.
- * In each cluster, both this VFS context and the FS specific context are handled as
- * private by the local OS intance.
+ * In each cluster, the inum allocator can be accessed by any thread runing
+ * in any cluster, and is therefore protected by a remote_busylock. 
  *****************************************************************************************/
 
 typedef enum
 {
-	FS_TYPE_DEVFS = 0,
-	FS_TYPE_FATFS = 1,
-	FS_TYPE_RAMFS = 2,
+	FS_TYPE_RAMFS = 0,
+	FS_TYPE_DEVFS = 1,
+	FS_TYPE_FATFS = 2,
  
 	FS_TYPES_NR   = 3,
@@ -95,12 +96,11 @@
 typedef struct vfs_ctx_s
 {
-	vfs_fs_type_t  type;                     /*! File System type                        */
-	uint32_t	   attr;                     /*! global attributes for all files in FS   */
-	uint32_t       total_clusters;           /*! total number of clusters on device      */
-	uint32_t       cluster_size;             /*! cluster size on device (bytes)          */
-	xptr_t         vfs_root_xp;              /*! extended pointer on VFS root inode      */
-    busylock_t     lock;                     /*! lock protecting inum allocator          */
-    uint32_t       bitmap[BITMAP_SIZE(CONFIG_VFS_MAX_INODES)];  /* inum allocator        */
-    void         * extend;                   /*! FS specific context extension           */
+	vfs_fs_type_t      type;                 /*! File System type                        */
+	uint32_t           total_clusters;       /*! total number of clusters on device      */
+	uint32_t           cluster_size;         /*! cluster size on device (bytes)          */
+	xptr_t             vfs_root_xp;          /*! extended pointer on VFS root inode      */
+    remote_busylock_t  lock;                 /*! lock protecting inum allocator          */
+    uint32_t           bitmap[BITMAP_SIZE(CONFIG_VFS_MAX_INODES)];  /* inum allocator    */
+    void               * extend;             /*! FS specific context extension           */
 }
 vfs_ctx_t;
@@ -109,5 +109,5 @@
  * This structure define a VFS inode.
  * An inode can have several children dentries (if it is a directory), an can have several
- * parents dentries (if it hass several aliases links):
+ * parents dentries (if it has several aliases links):
  * - The "parents" field is the root of the xlist of parents dentries, and the "links"
  *   fiels define the number of aliases parent dentries. only a FILE inode can have
@@ -145,7 +145,7 @@
 typedef enum 
 {
-    INODE_ATTR_DIRTY   =     0x01,       /* modified versus the value on device          */ 
-    INODE_ATTR_INLOAD  =     0x04,       /* loading from device in progress              */
-    INODE_ATTR_NEW     =     0x08,       /* not saved on device yet                      */
+    INODE_ATTR_DIRTY   =     0x01,       /*! modified versus the value on device         */ 
+    INODE_ATTR_INLOAD  =     0x04,       /*! loading from device in progress             */
+    INODE_ATTR_NEW     =     0x08,       /*! not saved on device yet                     */
 }
 vfs_inode_attr_t;
@@ -193,5 +193,5 @@
 
 /******************************************************************************************
- Rpt* This structure defines a directory entry.
+ * This structure defines a directory entry.
  * A dentry contains the name of a remote file/dir, an extended pointer on the 
  * inode representing this file/dir, a local pointer on the inode representing 
@@ -225,10 +225,10 @@
 typedef enum
 {
-    FD_ATTR_READ_ENABLE    = 0x01,     /*! read access possible                         */
-    FD_ATTR_WRITE_ENABLE   = 0x02,     /*! write access possible                        */
-    FD_ATTR_APPEND         = 0x04,     /*! append on each write                         */
-    FD_ATTR_CLOSE_EXEC     = 0x08,     /*! close file on exec                           */
-    FD_ATTR_SYNC           = 0x10,     /*! synchronise FS on each write                 */
-    FD_ATTR_IS_DIR         = 0x20,     /*! this is a directory                          */
+    FD_ATTR_READ_ENABLE    = 0x01,     /*! read access possible                          */
+    FD_ATTR_WRITE_ENABLE   = 0x02,     /*! write access possible                         */
+    FD_ATTR_APPEND         = 0x04,     /*! append on each write                          */
+    FD_ATTR_CLOSE_EXEC     = 0x08,     /*! close file on exec                            */
+    FD_ATTR_SYNC           = 0x10,     /*! synchronise FS on each write                  */
+    FD_ATTR_IS_DIR         = 0x20,     /*! this is a directory                           */
 }
 vfs_file_attr_t;
@@ -237,5 +237,4 @@
 {
 	struct vfs_ctx_s      * ctx;        /*! local pointer on FS context.                 */
-	uint32_t                gc;         /*! generation counter                           */
 	vfs_file_attr_t         attr;       /*! file attributes bit vector (see above)       */
 	vfs_inode_type_t        type;       /*! same type as inode                           */
@@ -245,4 +244,5 @@
 	struct mapper_s       * mapper;     /*! associated file cache                        */
 	struct vfs_inode_s    * inode;      /*! local pointer on associated inode            */
+    struct socket_s       * socket;     /*! local pointer on associated socket           */
 	void                  * extend;     /*! FS specific extension                        */
 }
@@ -255,8 +255,10 @@
 
 /****************************************************************************************** 
- * This function initialise a (statically allocated) VFS context in local cluster. 
- ******************************************************************************************
+ * This function initialises a (statically allocated) VFS context in cluster identified
+ * by the <cxy> argument. 
+ * It is called by the kernel_init() function.
+ ******************************************************************************************
+ * @ cxy            : target cluster identifier.
  * @ fs_type        : file system type.
- * @ attr           : global attributes (for all files in FS.
  * @ total_clusters : total number of clusters on device.
  * @ cluster_size   : cluster size on device (bytes).
@@ -264,6 +266,6 @@
  * @ extend         : fs_type_specific extension.
  *****************************************************************************************/
-void vfs_ctx_init( vfs_fs_type_t   type,
-                   uint32_t        attr,
+void vfs_ctx_init( cxy_t           cxy,
+                   vfs_fs_type_t   type,
 	               uint32_t        total_clusters,
 	               uint32_t        cluster_size,
@@ -277,18 +279,18 @@
  * - the 16 LSB bits contains the local inode identifier  : lid
  ******************************************************************************************
- * @ ctx      : local pointer on file system context.
- * @ inum     : [ou] buffer for allocated inode identifier.
+ * @ ctx_xp   : [in]  extended pointer on file system context.
+ * @ inum     : [out] buffer for allocated inode identifier.
  * @ return 0 if success / return non-zero if error.
  *****************************************************************************************/
-error_t vfs_ctx_inum_alloc( vfs_ctx_t * ctx,
-                            uint32_t  * inum );
+error_t vfs_ctx_inum_alloc( xptr_t     ctx_xp,
+                            uint32_t * inum );
 
 /****************************************************************************************** 
  * This function release an inode identifier.                                  
  ******************************************************************************************
- * @ ctx      : local pointer on file system context.
- * @ inum     : released inode identifier.
- *****************************************************************************************/
-void vfs_ctx_inum_release( vfs_ctx_t * ctx,
+ * @ ctx_xp   : [in] extended pointer on file system context.
+ * @ inum     : [in] released inode identifier.
+ *****************************************************************************************/
+void vfs_ctx_inum_release( xptr_t      ctx_xp,
                            uint32_t    inum );
 
@@ -305,19 +307,21 @@
 
 /****************************************************************************************** 
- * This function allocates memory from local cluster for an inode descriptor and the 
- * associated mapper, and partially initialise this inode from arguments values.
+ * This function allocates memory in cluster identified by the <cxy> argument 
+ * for an inode descriptor and for the associated mapper, and partially initialise 
+ * this inode from arguments values.
  * It does NOT link it to the Inode Tree, as this is done by add_child_in_parent(). 
- * It must called by a local thread. Use the RPC_INODE_CREATE if client thread is remote.
- ******************************************************************************************
- * @ fs_type    : file system type.
- * @ inode_type : inode type.
- * @ attr       : inode attributes.
- * @ rights     : inode access rights.
- * @ uid        : user owner ID.
- * @ gid        : group owner ID.
+ * It can be called by any thread running in any cluster.
+ ******************************************************************************************
+ * @ cxy        : [in]  target cluster identifier
+ * @ fs_type    : [in]  file system type.
+ * @ attr       : [in]  inode attributes.
+ * @ rights     : [in]  inode access rights.
+ * @ uid        : [in]  user owner ID.
+ * @ gid        : [in]  group owner ID.
  * @ inode_xp   : [out] buffer for extended pointer on created inode.
- * @ return 0 if success / return ENOMEM or EINVAL if error.
- *****************************************************************************************/
-error_t vfs_inode_create( vfs_fs_type_t     fs_type,
+ * @ return 0 if success / return -1 if error.
+ *****************************************************************************************/
+error_t vfs_inode_create( cxy_t             cxy,
+                          vfs_fs_type_t     fs_type,
                           uint32_t          attr,
                           uint32_t          rights,
@@ -330,14 +334,14 @@
  * all memory allocated to the mapper (both mapper descriptor and radix tree).
  * The mapper should not contain any dirty page (should be synchronized before deletion).
- * It must be executed by a thread running in the cluster containing the inode.
- * Use the rpc_vfs_inode_destroy_client() function if required.
+ * It can be called by any thread running in any cluster.
  ****************************************************************************************** 
- * @ inode  : local pointer on inode descriptor.
- *****************************************************************************************/
-void vfs_inode_destroy( vfs_inode_t *  inode );  
+ * @ inode_xp  : extended pointer on inode descriptor.
+ *****************************************************************************************/
+void vfs_inode_destroy( xptr_t  inode_xp );  
 
 /****************************************************************************************** 
  * This function returns the <size> of a file/dir from a remote inode,
  * taking the remote_rwlock protecting <size> in READ_MODE.
+ * It can be called by any thread running in any cluster.
  *****************************************************************************************
  * @ inode_xp  : extended pointer on the remote inode.
@@ -350,4 +354,5 @@
  * It takes the rwlock protecting the file size in WRITE_MODE, and set the "size" field
  * when the current size is smaller than the requested <size> argument. 
+ * It can be called by any thread running in any cluster.
  *****************************************************************************************
  * @ inode_xp  : extended pointer on the remote inode.
@@ -358,6 +363,7 @@
 
 /****************************************************************************************** 
- * This function takes the main lock of a remote inode.
+ * This function takes the main lock of a remote inode identified by the <inode_xp>.
  * This lock protect all inode fields, including the children dentries.
+ * It can be called by any thread running in any cluster.
  *****************************************************************************************
  * @ inode_xp  : extended pointer on the remote inode.
@@ -366,6 +372,7 @@
 
 /****************************************************************************************** 
- * This function releases the main lock of a remote inode.
+ * This function releases the main lock of a remote inode identified by <inode_xp>.
  * This lock protect all inode fiels, including the children dentries.
+ * It can be called by any thread running in any cluster.
  *****************************************************************************************
  * @ inode_xp  : extended pointer on the remote inode.
@@ -377,4 +384,5 @@
  * argument to a local buffer identified by the <name> argument.
  * The local buffer size must be at least CONFIG_VFS_MAX_NAME_LENGTH. 
+ * It can be called by any thread running in any cluster.
  ******************************************************************************************
  * @ inode_xp  : extended pointer on the remote inode.
@@ -385,19 +393,19 @@
 
 /******************************************************************************************
- * This function accesses successively all pages of a file identified by the <inode>,
+ * This function accesses successively all pages of a file identified by the <inode_xp>,
  * argument, to force misses, and load all pages into mapper.
  * The target inode can be a directory or a file, but this function is mainly used 
  * to prefetch a complete directory to the mapper.
- * It must be executed by a thread running in the cluster containing the inode.
  * This function does NOT take any lock.
- ******************************************************************************************
- * @ inode  : local pointer on inode.
+ * It can be called by any thread running in any cluster.
+ ******************************************************************************************
+ * @ inode_xp  : extended pointer on inode.
  * @ return 0 if success / return -1 if device access failure.
  *****************************************************************************************/
-error_t vfs_inode_load_all_pages( vfs_inode_t * inode );
-
-/****************************************************************************************** 
- * This debug function display the curren state of an inode descriptor identified by
- * the <inode_xp> argument.
+error_t vfs_inode_load_all_pages( xptr_t inode_xp );
+
+/****************************************************************************************** 
+ * This debug function display the curren state of an inode descriptor.
+ * It can be called by any thread running in any cluster.
  *****************************************************************************************/
 void vfs_inode_display( xptr_t inode_xp );
@@ -408,8 +416,10 @@
 
 /****************************************************************************************** 
- * This function allocates memory from local cluster for a dentry descriptor, 
- * initialises it from  arguments values, and returns the extended pointer on dentry.
- * It must called by a local thread. Use the RPC_DENTRY_CREATE if client thread is remote.
- ******************************************************************************************
+ * This function allocates memory in cluster identified by the <cxy> argument
+ * for a dentry descriptor, initialises it from  arguments values, and returns 
+ * in <dentry_xp> the extended pointer on dentry.
+ * It can be called by any thread running in any cluster.
+ ******************************************************************************************
+ * @ cxy        : [in]  target cluster identifier
  * @ fs_type    : [in]  file system type.
  * @ name       : [in]  directory entry file/dir name.
@@ -417,5 +427,6 @@
  * @ return 0 if success / return ENOMEM or EINVAL if error.
  *****************************************************************************************/
-error_t vfs_dentry_create( vfs_fs_type_t   fs_type,
+error_t vfs_dentry_create( cxy_t           cxy,
+                           vfs_fs_type_t   fs_type,
                            char          * name,
                            xptr_t        * dentry_xp );
@@ -424,10 +435,9 @@
  * This function removes the dentry from the parent inode xhtab, and releases the memory
  * allocated to the dentry descriptor.
- * It must be executed by a thread running in the cluster containing the dentry.
- * Use the RPC_DENTRY_DESTROY if required.
+ * It can be called by any thread running in any cluster.
  ****************************************************************************************** 
- * @ dentry  : [in] local pointer on dentry descriptor.
- *****************************************************************************************/
-void vfs_dentry_destroy( vfs_dentry_t *  dentry );  
+ * @ dentry_xp  : [in] extended pointer on dentry descriptor.
+ *****************************************************************************************/
+void vfs_dentry_destroy( xptr_t  dentry_xp );  
 
 
@@ -437,30 +447,31 @@
 
 /****************************************************************************************** 
- * This function allocates memory and initializes a new local file descriptor.
- * It must be executed in the cluster containing the inode.
- * If the client thread is not running in the owner cluster, it must use the
- * rpc_vfs_file_create_client() function.
+ * This function allocates memory and initializes a new file descriptor in the
+ * cluster defined by the <inode_xp> argument.
+ * It can be called by any thread running in any cluster.
  ****************************************************************************************** 
- * @ inode    : local pointer on associated inode.
- * @ attr     : file descriptor attributes.
- * @ file_xp  : [out] buffer for extended pointer on created file descriptor.
+ * @ inode_xp  : [in]  extended pointer on associated inode.
+ * @ attr      : [in]  file descriptor attributes.
+ * @ file_xp   : [out] buffer for extended pointer on created file descriptor.
  * @ return 0 if success / return ENOMEM if error.
  *****************************************************************************************/
-error_t vfs_file_create( vfs_inode_t * inode,
+error_t vfs_file_create( xptr_t        inode_xp,
                          uint32_t      attr,
                          xptr_t      * file_xp );  
 
 /****************************************************************************************** 
- * This function releases memory allocated to a local file descriptor.
- * It must be executed by a thread running in the cluster containing the inode,
- * and the file refcount must be zero. Use the RPC_VFS_FILE_DESTROY if required.
+ * This function releases memory allocated to file descriptor identified
+ * by the <file_xp> argument.
+ * It can be called by any thread running in any cluster.
  ****************************************************************************************** 
- * @ file  : local pointer on file descriptor.
- *****************************************************************************************/
-void vfs_file_destroy( vfs_file_t *  file );  
+ * @ file_xp  : [in] extended pointer on file descriptor.
+ *****************************************************************************************/
+void vfs_file_destroy( xptr_t  file_xp );  
 
 /****************************************************************************************** 
  * These functions increment (resp. decrement) the count field in a remote file
  * descriptor, using a remote_atomic access. 
+ *****************************************************************************************
+ * @ file_xp  : extended pointer on file descriptor.
  *****************************************************************************************/
 void vfs_file_count_up  ( xptr_t   file_xp );
@@ -472,7 +483,7 @@
  * The local buffer size must be at least CONFIG_VFS_MAX_NAME_LENGTH. 
  *****************************************************************************************
- * @ file_xp  : extended pointer on the remote inode.
- * @ name     : local buffer pointer.
- *****************************************************************************************/
+ * @ ionde_xp  : [in] extended pointer on the remote inode.
+ * @ name      : [out] local string.
+ ***************************************************************************************/
 void vfs_file_get_name( xptr_t inode_xp,
                         char * name );
@@ -596,29 +607,7 @@
 
 /******************************************************************************************
- * This function is called by the vfs_lookup() function when a new (dentry/inode) must
- * be created from scratch and introduced in both the parent mapper and the IOC device.
- * The dentry and inode descriptors must have been previously created by the caller.
- * 1. It allocates one cluster from the relevant FS, updates the FAT mapper,
- *    and synchronously update the IOC device).
- * 2. It set the "size", and "extend" fields in child inode descriptor.
- *    The size is 4096 for a directory, the size is 0 for a file.
- * 3. It updates the parent directory mapper to introduce the new child,
- *    and synchronously update the IOC device.
- * 4. It set the "extend" field in dentry descriptor.
- * It can be called by a thread running in any cluster.
- ******************************************************************************************
- * @ parent_xp   : extended pointer on parent inode descriptor.
- * @ dentry_xp   : extended pointer on new dentry descriptor.
- * @ child_xp    : extended pointer on child inode descriptor.
- * @ return 0 if success / -1 if failure.
- *****************************************************************************************/
-error_t vfs_new_dentry_init( xptr_t   parent_xp,
-                             xptr_t   dentry_xp,
-                             xptr_t   child_xp );
-
-/******************************************************************************************
- * This function creates in the directory identified by the <child_xp> argument, 
+ * This function creates in the directory identified by the <child_inode_xp> argument, 
  * the two special dentries <.> and <..>. The parent directory inode is defined
- * by the <parent_xp> argument. The two dentries are introduced in the Inode Tree.
+ * by the <parent_inode_xp> argument. The two dentries are introduced in the Inode Tree.
  * They are also introduced in the child directory mapper, and the IOC device is updated.
  * This function is called by all functions creating a brand new directory : vfs_mkdir(),
@@ -629,6 +618,6 @@
  * @ return 0 if success / -1 if failure.
  *****************************************************************************************/
-error_t vfs_add_special_dentries( xptr_t  child_xp,
-                                  xptr_t  parent_xp );
+error_t vfs_add_special_dentries( xptr_t  child_inode_xp,
+                                  xptr_t  parent_inode_xp );
 
 /******************************************************************************************
@@ -893,35 +882,16 @@
 
 /******************************************************************************************
- *        These functions define the VFS "FS" API to a specific File System
- *****************************************************************************************/
-
-/******************************************************************************************
- * This function makes the I/O operation to move one page identified by the <page_xp>
- * argument to/from the IOC device from/to the mapper, as defined by the <cmd_type>.
+ *      These functions define the VFS "FS" API to access a specific File System
+ *****************************************************************************************/
+
+/******************************************************************************************
+ * This function introduces in a directory mapper identified by the <parent_inode_xp>
+ * argument, a new entry identified by the <dentry_ptr> argument. 
  * Depending on the file system type, it calls the proper, FS specific function.
- * It is used in case of MISS on the mapper (read), or when a dirty page in the mapper
- * must be updated in the File System (write).
- * The mapper pointer, and the page index in file are obtained from the page descriptor.
- * It can be executed by any thread running in any cluster.
- * This function does NOT take any lock.
- ******************************************************************************************
- * @ page_xp   : extended pointer on page descriptor (for mapper and page_id).
- * @ cmd_type  : IOC_READ / IOC_WRITE / IOC_SYNC_READ / IOC_SYNC_WRITE
- * @ returns 0 if success / return -1 if device access failure.
- *****************************************************************************************/
-error_t vfs_fs_move_page( xptr_t      page_xp,
-                          cmd_type_t  cmd_type );
-
-/******************************************************************************************
- * This function updates the mapper associated to a directory inode identified by the 
- * <parent> argument, to add a new entry identified by the <dentry> argument. 
- * The directory inode descriptor and the dentry descriptor are in the same cluster.
- * Depending on the file system type, it calls the proper, FS specific function.
- * It also updates the dentry descriptor and/or the inode descriptor extensions
- * as required by the specific file system type.
+ * All informations related to the new directory must be contained in the dentry 
+ * descriptor, or in the associated child inode descriptor.
+ * The dentry descriptor "extend" field is updated as required by the specific FS. 
  * Finally, it synchronously updates the parent directory on IOC device.
- *
- * It must be executed by a thread running in the cluster containing the parent directory.
- * It can be the RPC_VFS_FS_ADD_DENTRY. This function does NOT take any lock.
+ * This function can be called by any thread running in any cluster.
  ******************************************************************************************
  * @ parent  : local pointer on parent (directory) inode.
@@ -929,66 +899,69 @@
  * @ return 0 if success / return -1 if device access failure.
  *****************************************************************************************/
-error_t vfs_fs_add_dentry( vfs_inode_t  * parent,
-                           vfs_dentry_t * dentry );
-
-/******************************************************************************************
- * This function updates the mapper associated to a directory inode identified by the 
- * <parent> argument, to remove an entry identified by the <dentry> argument. 
- * The directory inode descriptor and the dentry descriptor are in the same cluster.
+error_t vfs_fs_add_dentry( xptr_t         parent_inode_xp,
+                           vfs_dentry_t * dentry_ptr );
+
+/******************************************************************************************
+ * This function removes from a directory mapper identified by the <parent_inode_xp> 
+ * argument, an entry identified by the <dentry_ptr> argument. 
  * Depending on the file system type, it calls the proper, FS specific function.
  * Finally, it synchronously updates the parent directory on IOC device.
- *
- * Depending on the file system type, it calls the relevant, FS specific function.
- * It must be executed by a thread running in the cluster containing the parent directory.
- * It can be the RPC_VFS_FS_REMOVE_DENTRY. This function does NOT take any lock.
- ******************************************************************************************
- * @ parent  : local pointer on parent (directory) inode.
- * @ dentry  : local pointer on dentry containing name.
+ * This function can be called by any thread running in any cluster.
+ ******************************************************************************************
+ * @ parent_inode_xp  : extended pointer on parent (directory) inode.
+ * @ dentry_ptr       : local pointer on dentry containing name.
  * @ return 0 if success / return -1 if device access failure.
  *****************************************************************************************/
-error_t vfs_fs_remove_dentry( vfs_inode_t  * parent,
-                              vfs_dentry_t * dentry );
-
-/******************************************************************************************
- * This function scan the mapper of an an existing parent inode directory, identified by
- * the <parent> argument to find a directory entry identified by the <name> argument,
- * and updates both the child inode descriptor, identified by the <child_xp> argument,
- * and the associated dentry descriptor :
+error_t vfs_fs_remove_dentry( xptr_t         parent_inode_xp,
+                              vfs_dentry_t * dentry_ptr );
+
+/******************************************************************************************
+ * This function scan a directory mapper, identified by the <parent_inode_xp> argument
+ * to find a directory entry identified by the <dentry_ptr> argument,
+ * and updates both the child inode descriptor, and the associated dentry descriptor:
  * - It set the "size", "type", and "extend" fields in inode descriptor.
  * - It set the "extend" field in dentry descriptor.
- * It is called by the vfs_lookup() function in case of miss.
- *
+ * It is called by the vfs_lookup() function in case of miss, and does NOT take any lock.
  * Depending on the file system type, it calls the relevant, FS specific function.
- * It must be called by a thread running in the cluster containing the parent inode.
- * It can be the RPC_VFS_FS_NEW_DENTRY. This function does NOT take any lock.
- ******************************************************************************************
- * @ parent    : local pointer on parent inode (directory).
- * @ name      : child name.
- * @ child_xp  : extended pointer on remote child inode (file or directory)
+ * This function can be called by any thread running in any cluster.
+ ******************************************************************************************
+ * @ parent_inode_xp  : extended pointer on parent inode (directory).
+ * @ dentry_ptr       : local pointer on new entry (in parent inode cluster).
  * @ return 0 if success / return -1 if dentry not found.
  *****************************************************************************************/
-error_t vfs_fs_new_dentry( vfs_inode_t * parent,
-                           char        * name,
-                           xptr_t        child_xp );
-
-/******************************************************************************************
- * This function scan the mapper of an an existing inode directory, identified by
- * the <inode> argument, to find a directory entry identified by the <dentry> argument,
- * and update the size for this directory entry in mapper, as defined by <size>.
+error_t vfs_fs_new_dentry_from_mapper( xptr_t         parent_inode_xp,
+                                       vfs_dentry_t * dentry_ptr );
+
+/*****************************************************************************************
+ * This function  introduces a brand new dentry identified by the <dentry_ptr> argument
+ * in the mapper of a directory identified by the <parent_inode_xp> argument.
+ * It is called by the vfs_lookup() function, and does NOT take any lock.
+ * The child inode descriptor, and the dentry descriptor must have been previously
+ * allocated and introduced in the Inode Tree. The dentry descriptor contains the name. 
+ * Depending on the file system type, it calls the relevant, FS specific function.
+ * This function can be called by any thread running in any cluster.
+ *****************************************************************************************
+ * @ parent_inode_xp : [in]  extended pointer on parent inode (directory).
+ * @ dentry_ptr      : [in]  local pointer on dentry (in parent inode cluster).
+ * @ return 0 if success / return -1 if failure.
+ ****************************************************************************************/
+error_t vfs_fs_new_dentry_to_mapper( xptr_t         parent_inode_xp,
+                                     vfs_dentry_t * dentry_ptr );
+
+/******************************************************************************************
+ * This function updates the "size" field of a directory entry identified by the 
+ * <dentry_ptr> argument in a directory mapper identified by the <parent_inode_xp>
+ * from the value contained in the inode descriptor.
  * The parent directory on device is synchronously updated.
  * It is called by the vfs_close() function.
- *
  * Depending on the file system type, it calls the relevant, FS specific function.
- * It must be called by a thread running in the cluster containing the parent inode.
- * It can be the RPC_VFS_FS_UPDATE_DENTRY. This function does NOT take any lock.
- ******************************************************************************************
- * @ parent    : local pointer on parent inode (directory).
- * @ dentry    : local pointer on dentry.
- * @ size      : new size value (bytes).
- * @ return 0 if success / return ENOENT if not found.
- *****************************************************************************************/
-error_t vfs_fs_update_dentry( vfs_inode_t  * inode,
-                              vfs_dentry_t * dentry,
-                              uint32_t       size );
+ * This function can be called by any thread running in any cluster.
+ ******************************************************************************************
+ * @ parent_inode_xp  : local pointer on parent inode (directory).
+ * @ dentry_ptr       : local pointer on dentry (in parent directory cluster).
+ * @ return 0 if success / return -1 if not found.
+ *****************************************************************************************/
+error_t vfs_fs_update_dentry( xptr_t         parent_inode_xp,
+                              vfs_dentry_t * dentry_ptr );
 
 /******************************************************************************************
@@ -1002,8 +975,9 @@
  * the Inode Tree is dynamically created, and all dirent fiels are documented in the
  * dirent array. Otherwise, only the dentry name is documented.
- *
+ * This function does NOT take any lock.
  * Depending on the file system type, it calls the relevant, FS specific function.
- * It must be called by a thread running in the cluster containing the parent inode.
- * This function does NOT take any lock.
+ *
+ * WARNING : this function must be called by a thread running in the cluster containing
+ * the target directory inode.
  ******************************************************************************************
  * @ inode      : [in]  local pointer on directory inode.
@@ -1031,12 +1005,11 @@
  * directory are synchronously done on the IOC device by the two vfs_fs_add_dentry() 
  * and vfs_fs_remove_dentry() functions.
- *
  * Depending on the file system type, it calls the relevant, FS specific function.
- * It must be called by a thread running in the inode cluster.
+ * This function can be called by any thread running in any cluster.
  *****************************************************************************************
- * @ inode   : local pointer on inode.
- * @ return 0 if success / return EIO if failure during device access. 
+ * @ inode_xp   : remote pointer on inode.
+ * @ return 0 if success / return -1 if failure. 
  ****************************************************************************************/
-error_t vfs_fs_sync_inode( struct vfs_inode_s * inode );
+error_t vfs_fs_sync_inode( xptr_t inode_xp );
 
 /*****************************************************************************************
@@ -1044,5 +1017,4 @@
  * for the FAT itself. It scan all clusters registered in the FAT mapper, and copies 
  * to device each page marked as dirty.
- *
  * Depending on the file system type, it calls the relevant, FS specific function.
  * It can be called by a thread running in any cluster.
@@ -1053,30 +1025,4 @@
 error_t vfs_fs_sync_fat( vfs_fs_type_t fs_type );
 
-/*****************************************************************************************
- * This function updates the free clusters info on the IOC device for the FS defined
- * by the <fs_type> argument. 
- *
- * Depending on the file system type, it calls the relevant, FS specific function.
- * It can be called by a thread running in any cluster.
- *****************************************************************************************
- * @ fs_type   : specific file system type.
- * @ return 0 if success / return EIO if failure during device access.
- ****************************************************************************************/
-error_t vfs_fs_sync_free_info( vfs_fs_type_t fs_type );
-
-/******************************************************************************************
- * This function allocates a free cluster from the FS identified by the <fs_type>
- * argument. It updates the selected FS File Allocation Table.
- *
- * Depending on the file system type, it calls the relevant, FS specific function.
- * It can be called by a thread running in any cluster.
- ******************************************************************************************
- * @ fs_type   : [in]  File System type.
- * @ cluster   : [out] cluster index in File system.
- * @ return 0 if success / return -1 if no free cluster
- *****************************************************************************************/
-error_t vfs_fs_cluster_alloc( uint32_t   fs_type,
-                              uint32_t * cluster );
-
 /******************************************************************************************
  * This function makes all I/O operations required to release all clusters allocated
@@ -1084,6 +1030,7 @@
  * Depending on the file system type, it calls the proper, FS specific function.
  * It is called by the vfs_unlink() function.
- * It can be executed by a thread running in any cluster.
  * This function does NOT take any lock.
+ * Depending on the file system type, it calls the relevant, FS specific function.
+ * This function can be executed by any thread running in any cluster.
  ******************************************************************************************
  * @ inode_xp  : extended pointer on inode.
@@ -1092,4 +1039,21 @@
 error_t vfs_fs_release_inode( xptr_t  inode_xp ); 
 
+/******************************************************************************************
+ * This function makes the I/O operation to move one page identified by the <page_xp>
+ * argument to/from the IOC device from/to the mapper, as defined by the <cmd_type>.
+ * It is used in case of MISS on the mapper (read), or when a dirty page in the mapper
+ * must be updated in the File System (write).
+ * The mapper pointer, and the page index in file are obtained from the page descriptor.
+ * This function does NOT take any lock.
+ * Depending on the file system type, it calls the proper, FS specific function.
+ * This function can be executed by any thread running in any cluster.
+ ******************************************************************************************
+ * @ page_xp   : extended pointer on page descriptor (for mapper and page_id).
+ * @ cmd_type  : IOC_READ / IOC_WRITE / IOC_SYNC_READ / IOC_SYNC_WRITE
+ * @ returns 0 if success / return -1 if device access failure.
+ *****************************************************************************************/
+error_t vfs_fs_move_page( xptr_t          page_xp,
+                          ioc_cmd_type_t  cmd_type );
+
 
 #endif	/* _VFS_H_ */
Index: /trunk/kernel/kern/chdev.c
===================================================================
--- /trunk/kernel/kern/chdev.c	(revision 656)
+++ /trunk/kernel/kern/chdev.c	(revision 657)
@@ -1,6 +1,6 @@
 /*
- * chdev.c - channel device descriptor operations implementation.
+ * chdev.c - channel device API implementation.
  *
- * Authors  Alain Greiner   (2016)
+ * Authors  Alain Greiner   (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -148,5 +148,6 @@
 #endif
 
-    thread_t * this = CURRENT_THREAD;
+    thread_t * this      = CURRENT_THREAD;
+    xptr_t     client_xp = XPTR( local_cxy , this );
 
     // get chdev cluster and local pointer
@@ -197,7 +198,7 @@
     lock_xp            = XPTR( chdev_cxy , &chdev_ptr->wait_lock );
 
-    // The following actions execute in critical section,
-    // because the lock_acquire / lock_release : 
-    // (1) take the lock protecting the chdev state
+    // The following actions are executed in critical section,
+    // (because the busylock_acquire / busylock_release) 
+    // (1) take the lock protecting the waiting queue
     // (2) register client thread in server queue 
     // (3) unblock the server thread and block client thread
@@ -224,6 +225,6 @@
  
     // 3. client thread unblocks server thread and blocks itself
-    thread_unblock( server_xp , THREAD_BLOCKED_IDLE );
-    thread_block( XPTR( local_cxy , CURRENT_THREAD ) , THREAD_BLOCKED_IO );
+    thread_unblock( server_xp , THREAD_BLOCKED_CLIENT );
+    thread_block( client_xp , THREAD_BLOCKED_IO );
 
 #if (DEBUG_CHDEV_CMD_TX & 1)
@@ -300,8 +301,6 @@
     server = CURRENT_THREAD;
 
-    // build extended pointer on root of client threads queue
+    // build extended pointer on root & lock of client threads queue
     root_xp = XPTR( local_cxy , &chdev->wait_root );
-
-    // build extended pointer on lock protecting client threads queue
     lock_xp = XPTR( local_cxy , &chdev->wait_lock );
 
@@ -351,5 +350,5 @@
 #endif
             // block 
-            thread_block( XPTR( local_cxy , server ) , THREAD_BLOCKED_IDLE ); 
+            thread_block( XPTR( local_cxy , server ) , THREAD_BLOCKED_CLIENT ); 
 
             // deschedule
@@ -358,13 +357,14 @@
         else                            // waiting queue not empty
         {
+            // get pointers on first client thread
+            client_xp  = XLIST_FIRST( root_xp , thread_t , wait_list );
+            client_cxy = GET_CXY( client_xp );
+            client_ptr = GET_PTR( client_xp );
+
+            // remove this client thread from chdev waiting queue
+            xlist_unlink( XPTR( client_cxy , &client_ptr->wait_list ) );
+
             // release lock protecting the waiting queue
             remote_busylock_release( lock_xp );
-
-            // get extended pointer on first client thread
-            client_xp = XLIST_FIRST( root_xp , thread_t , wait_list );
-
-            // get client thread cluster and local pointer
-            client_cxy = GET_CXY( client_xp );
-            client_ptr = GET_PTR( client_xp );
 
 #if( DEBUG_CHDEV_SERVER_TX || DEBUG_CHDEV_SERVER_RX )
@@ -402,13 +402,4 @@
             // unblock client thread when driver returns
             thread_unblock( client_xp , THREAD_BLOCKED_IO );
-
-            // get the lock protecting the waiting queue
-            remote_busylock_acquire( lock_xp );
-
-            // remove this client thread from chdev waiting queue
-            xlist_unlink( XPTR( client_cxy , &client_ptr->wait_list ) );
-
-            // release lock protecting the waiting queue
-            remote_busylock_release( lock_xp );
 
 #if DEBUG_CHDEV_SERVER_RX
Index: /trunk/kernel/kern/chdev.h
===================================================================
--- /trunk/kernel/kern/chdev.h	(revision 656)
+++ /trunk/kernel/kern/chdev.h	(revision 657)
@@ -1,6 +1,6 @@
 /*
- * chdev.h - channel device (chdev) descriptor definition.
+ * chdev.h - channel device (chdev) descriptor and API definition.
  * 
- * Authors  Alain Greiner    (2016)
+ * Authors  Alain Greiner    (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -36,5 +36,5 @@
 #include <dev_txt.h>
 
-/******************************************************************************************
+/****************************************************************************************
  *       Channel Device descriptor definition
  *
@@ -42,5 +42,5 @@
  * independant) Channel Device descriptor (in brief "chdev").
  * ALMOS-MKH supports multi-channels peripherals, and defines one separated chdev 
- * descriptor for each channel (and for each RX/TX direction for the NIC and TXT devices).
+ * descriptor for each channel (and for each RX/TX direction for NIC and TXT devices).
  * Each chdev contains a trans-clusters waiting queue, registering the "client threads",
  * and an associated "server thread", handling these requests.
@@ -50,5 +50,5 @@
  * - the server cluster, containing the chdev and the server thread,
  * - the I/O cluster, containing the physical device.
- *****************************************************************************************/
+ ***************************************************************************************/
 
 /****  Forward declarations  ****/
@@ -58,12 +58,12 @@
 struct  boot_info_s;
 
-/******************************************************************************************
- * These macros extract the functionality and the implementation from the peripheral type.
- *****************************************************************************************/
+/****************************************************************************************
+ * These macros extract functionality and  implementation from the peripheral type.
+ ***************************************************************************************/
  
 #define FUNC_FROM_TYPE( type )    ((uint32_t)(type>>16))
 #define IMPL_FROM_TYPE( type )    ((uint32_t)(type & 0x0000FFFF))
 
-/******************************************************************************************
+/****************************************************************************************
  * This define the generic prototypes for the three functions that must be defined
  * by the drivers implementing a generic device:
@@ -73,5 +73,5 @@
  * The "cmd", "isr", and "aux" driver functions are registered in the generic chdev
  * descriptor at kernel init, and are called to start and complete an I/O operation.  
-*****************************************************************************************/
+***************************************************************************************/
 
 typedef void (dev_ini_t) ( xptr_t dev );      
@@ -80,5 +80,5 @@
 typedef void (dev_aux_t) ( void * args );  
 
-/******************************************************************************************
+/****************************************************************************************
  * This enum defines the supported generic device types.
  * These types are functionnal types: all (architecture specific) implementations 
@@ -89,5 +89,5 @@
  *           distributed LAPIC controler, but it does not exist as a chdev in the kernel,
  *           as it is hidden in the driver associated to the PIC device.
- *****************************************************************************************/
+ ***************************************************************************************/
  
 enum dev_func_type 
@@ -116,8 +116,10 @@
  * For each device type ***, the specific extension is defined in the "dev_***.h" file.
  *
- * NOTE : For most chdevs, the busylock is used to protect the waiting queue changes,
+ * NOTE . For most chdevs, the busylock is used to protect the waiting queue changes,
  *        when a thread register in this queue, or is removed after service.
- *        This busylock is also used to protect direct access to the kernel TXT0 terminal
- *        (without using the server thread).
+ *      . This busylock is also used to protect direct access to the shared
+ *        kernel TXT0 terminal, that does not use the waiting queue.
+ *      . For the NIC chdevs it is also used to protect registration (or removal) of a
+ *        socket in the list of attached sockets rooted in NIC device extension.
  *****************************************************************************************/
 
@@ -125,5 +127,5 @@
 {
 	uint32_t             func;        /*! peripheral functionnal type                    */
-	uint32_t             impl;        /*! peripheral inplementation subtype              */
+	uint32_t             impl;        /*! peripheral implementation type                 */
     uint32_t             channel;     /*! channel index                                  */
     bool_t               is_rx;       /*! relevant for NIC and TXT peripherals           */
@@ -185,24 +187,24 @@
 chdev_directory_t;
 
-/******************************************************************************************
+/****************************************************************************************
  * This function display relevant values for a chdev descriptor.
- ******************************************************************************************
+ ****************************************************************************************
  * @ chdev   : pointer on chdev.
- *****************************************************************************************/
+ ***************************************************************************************/
 void chdev_print( chdev_t * chdev );
 
-/******************************************************************************************
+/****************************************************************************************
  * This function returns a printable string for a device functionnal types.
- ******************************************************************************************
+ ****************************************************************************************
  * @ func_type  : functionnal type.
  * @ return pointer on string.
- *****************************************************************************************/
+ ***************************************************************************************/
 char * chdev_func_str( uint32_t func_type );
 
-/******************************************************************************************
+/****************************************************************************************
  * This  function allocates memory and initializes a chdev descriptor in local cluster,
  * from arguments values.  It should be called by a local thread. 
  * The device specific fields are initialised later.
- ******************************************************************************************
+ ****************************************************************************************
  * @ func      : functionnal type.
  * @ impl      : implementation type.
@@ -211,5 +213,5 @@
  * @ base      : extended pointer on peripheral segment base.
  * @ return a local pointer on created chdev / return NULL if failure.
- *****************************************************************************************/
+ ***************************************************************************************/
 chdev_t * chdev_create( uint32_t    func,
                         uint32_t    impl,
@@ -218,50 +220,75 @@
                         xptr_t      base );
 
-/******************************************************************************************
- * This function registers the calling thread in the waiting queue of a remote 
- * chdev descriptor, activates (i.e. unblock) the server thread associated to chdev,
- * and blocks itself on the THREAD_BLOCKED_IO condition.
- ******************************************************************************************
+/****************************************************************************************
+ * This generid function is executed by an user thread requesting an IOC or TXT chdev
+ * service. It registers the calling thread in the waiting queue of a the remote
+ * chdev descriptor identified by the <chdev_xp> argument.
+ * It activates (i.e. unblocks) the server thread associated to chdev, blocks itself,
+ * and deschedule.  It is supposed to be re-activated by the server thread. 
+ * It can be called by a thread running in any cluster.
+ * It cannot be used for a NIC or FBF chdev, because it is only convenient for one shot
+ * I/O operations, as the server thread removes the client thread from the waiting
+ * queue when it starts to execute the command.
+ ****************************************************************************************
+ * Implementation Note:
+ * The following actions are executed in a critical section, thanks to the
+ * busylock_acquire / busylock_release mechanism :
+ * 1) it takes the lock protecting the waiting queue.
+ * 2) it registers client thread in the server queue. 
+ * 3) it unblocks the server thread from the THREAD_BLOCKED_CLIENT condition.
+ * 4) it blocks the client thread on the THREAD_BLOCKED_IO condition.
+ * 5) it send an IPI to the core running the server thread to force scheduling.
+ * 6) it releases the lock protecting waiting queue.
+ * 7) it deschedules.
+ ****************************************************************************************
  * @ chdev_xp  : extended pointer on remote chdev descriptor.
- *****************************************************************************************/
+ ***************************************************************************************/
 void chdev_register_command( xptr_t chdev_xp );
 
-/******************************************************************************************
- * This function is executed by the server thread associated to a chdev descriptor.
- * It executes an infinite loop to handle sequencially all commands registered by the
- * client threads in the device waiting queue, until the queue is empty.
- * The driver CMD function being blocking, these functions return only when the command
+/****************************************************************************************
+ * This generic function is executed by the server thread associated to an IOC or TXT
+ * chdev identified by the <chdev> argument, to execute all commands registered in the
+ * waiting queue attached to this chev. 
+ * When the clients queue is empty, the server thread blocks on the THREAD_BLOCKED_CLIENT
+ * condition and deschedules. It is supposed to be re-activated by a client thread 
+ * registering a new command.
+ * It cannot be used for a NIC or FBF chdev, because it is only convenient for one shot
+ * I/O operations, as the server thread removes the client thread from the waiting
+ * queue when it starts to execute the command.
+ ****************************************************************************************
+ * Implementation Note:
+ * All driver CMD functions are supposed to be blocking, and return only when the command
  * is completed. These functions can use either a busy waiting policy, or a descheduling
- * policy, blocking on the THREAD_BLOCKED_IO_ISR condition, and reactivated by the ISR.
- * When the waiting queue is empty, the server thread blocks on the THREAD_BLOCKED_IO_CMD
- * condition and deschedule. It is re-activated by a client thread registering a command.
- ******************************************************************************************
+ * policy, blocking on the THREAD_BLOCKED_ISR condition. In the descheduling scenario,
+ * the server thread is supposed to be reactivated by the ISR attached to the hardware
+ * interrupts signaling command completion.
+ ****************************************************************************************
  * @ chdev   : local pointer on device descriptor.
- *****************************************************************************************/
+ ***************************************************************************************/
 void chdev_server_func( chdev_t * chdev );
 
-/******************************************************************************************
+/****************************************************************************************
  * This function returns an extended pointer on the chdev associated to a pseudo file
  * descriptor (type INODE_TYPE_DEV) identified by the <file_xp> argument.
  * It can be called by a thread running in any cluster.
  * It enters kernel panic if the inode has not the expected type.
- ******************************************************************************************
+ ****************************************************************************************
  * @ file_xp   : extended pointer on the pseudo file descriptor.
  * @ return an extended pointer on chdev.
- *****************************************************************************************/
+ ***************************************************************************************/
 xptr_t chdev_from_file( xptr_t file_xp );
 
-/******************************************************************************************
+/****************************************************************************************
  * This function displays the local copy of the external chdevs directory.
  * (global variable replicated in all clusters)
- *****************************************************************************************/
+ ***************************************************************************************/
 void chdev_dir_display( void );
 
-/******************************************************************************************
+/****************************************************************************************
  * This function displays the list of threads registered in the queue associated
  * to the chdev identified by the <chdev_xp>. 
- ******************************************************************************************
+ ****************************************************************************************
  * # root_xp  : extended pointer
- *****************************************************************************************/
+ ***************************************************************************************/
 void chdev_queue_display( xptr_t chdev_xp );
 
Index: /trunk/kernel/kern/cluster.c
===================================================================
--- /trunk/kernel/kern/cluster.c	(revision 656)
+++ /trunk/kernel/kern/cluster.c	(revision 657)
@@ -4,5 +4,5 @@
  * Author  Ghassan Almaless (2008,2009,2010,2011,2012)
  *         Mohamed Lamine Karaoui (2015)
- *         Alain Greiner (2016,2017,2018,2019)
+ *         Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
Index: /trunk/kernel/kern/cluster.h
===================================================================
--- /trunk/kernel/kern/cluster.h	(revision 656)
+++ /trunk/kernel/kern/cluster.h	(revision 657)
@@ -4,5 +4,5 @@
  * authors  Ghassan Almaless (2008,2009,2010,2011,2012)
  *          Mohamed Lamine Karaoui (2015)
- *          Alain Greiner (2016,2017,2018,2019)
+ *          Alain Greiner (2016,2017,2018,2019,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -197,7 +197,8 @@
 
 /****************************************************************************************** 
- * This function (pseudo) randomly selects a valid cluster.
+ * This function selects (pseudo) randomly a valid cluster.
  * It is called by the vfs_cluster_lookup() function to place a new (missing) inode.
  * It is called by the vmm_page_allocate() function to place a distributed vseg page.
+ * It is called by the dev_nic_accept() function to place a new server socket.
  ******************************************************************************************
  * @ returns the selected cluster identifier.
Index: /trunk/kernel/kern/core.c
===================================================================
--- /trunk/kernel/kern/core.c	(revision 656)
+++ /trunk/kernel/kern/core.c	(revision 657)
@@ -54,4 +54,24 @@
     // initialize scheduler
 	sched_init( core );
+}
+
+//////////////////////
+lid_t core_lid( void )
+{
+    uint32_t    i;
+
+    // get pointer on local cluser descriptor
+    cluster_t * cluster = LOCAL_CLUSTER;
+
+    // get core gid from hardware register
+    gid_t gid = hal_get_gid();
+
+    // makes an associative search in core_tbl[] from gid
+    for( i = 0 ; i < cluster->cores_nr ; i++ )
+    {
+        if( gid == cluster->core_tbl[i].gid ) return i;
+    }
+
+    assert( false , "core not found" );
 }
 
Index: /trunk/kernel/kern/core.h
===================================================================
--- /trunk/kernel/kern/core.h	(revision 656)
+++ /trunk/kernel/kern/core.h	(revision 657)
@@ -83,4 +83,12 @@
 
 /***************************************************************************************
+ * This function returns the calling core local index (lid), making an associative
+ * in the local core_tbl[] array based on the hardwired (gid).
+ ***************************************************************************************
+ * @ returns always the lid value.
+ **************************************************************************************/
+lid_t core_lid( void );
+
+/***************************************************************************************
  * This function returns a pseudo random number from the core descriptor
  * private random generator.
Index: unk/kernel/kern/dma.h
===================================================================
--- /trunk/kernel/kern/dma.h	(revision 656)
+++ 	(revision )
@@ -1,37 +1,0 @@
-/*
- * kern/dma.h - DMA related access
- * 
- * Copyright (c) 2008,2009,2010,2011,2012 Ghassan Almaless
- * Copyright (c) 2011,2012 UPMC Sorbonne Universites
- *
- * This file is part of ALMOS-kernel.
- *
- * ALMOS-kernel 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-kernel 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
- */
-
-#ifndef _DMA_H_
-#define _DMA_H_
-
-#include <types.h>
-#include <kmem.h>
-
-#define DMA_SYNC    0
-#define DMA_ASYNC   1
-
-int sys_dma_memcpy (void *src, void *dst, size_t size);
-error_t dma_memcpy (void *dst, void *src, size_t size, uint_t isAsync);
-
-KMEM_OBJATTR_INIT(dma_kmem_request_init);
-
-#endif	/* _DMA_H_ */
Index: /trunk/kernel/kern/do_syscall.c
===================================================================
--- /trunk/kernel/kern/do_syscall.c	(revision 656)
+++ /trunk/kernel/kern/do_syscall.c	(revision 657)
@@ -112,4 +112,5 @@
     sys_get_thread_info,    // 55
     sys_fbf,                // 56
+    sys_socket,             // 57
 };
 
@@ -181,4 +182,5 @@
     case SYS_GET_THREAD_INFO:              return "GET_THREAD_INFO";  // 55
     case SYS_FBF:                          return "FBF";              // 56
+    case SYS_SOCKET:                       return "SOCKET";           // 57
 
     default:                               return "undefined";
Index: /trunk/kernel/kern/kernel_init.c
===================================================================
--- /trunk/kernel/kern/kernel_init.c	(revision 656)
+++ /trunk/kernel/kern/kernel_init.c	(revision 657)
@@ -3,5 +3,5 @@
  *
  * Authors :  Mohamed Lamine Karaoui (2015)
- *            Alain Greiner  (2016,2017,2018,2019)
+ *            Alain Greiner  (2016,2017,2018,2019,2020)
  *
  * Copyright (c) Sorbonne Universites
@@ -136,9 +136,9 @@
     "VMM_STACK",             //  3
     "VMM_MMAP",              //  4
-    "VFS_CTX",               //  5
-    "KCM_STATE",             //  6
-    "KHM_STATE",             //  7
-    "HTAB_STATE",            //  8
-
+    "KCM_STATE",             //  5
+    "KHM_STATE",             //  6
+    "HTAB_STATE",            //  7
+
+    "VFS_CTX",               //  8
     "PPM_FREE",              //  9
     "THREAD_JOIN",           // 10
@@ -172,4 +172,5 @@
     "VFS_MAIN",              // 34
     "FATFS_FAT",             // 35
+    "FBF_WINDOWS",           // 36
 };        
 
@@ -874,5 +875,5 @@
 // @ cxy     : [out] cluster identifier.
 // @ lid     : [out] core global identifier (hardware).
-// @ return 0 if success / return EINVAL if not found.
+// @ return 0 if success / return -1 if not found.
 ///////////////////////////////////////////////////////////////////////////////////////////
 static error_t __attribute__ ((noinline)) get_core_identifiers( boot_info_t * info,
@@ -898,27 +899,5 @@
         }
     }
-    return EINVAL;
-}
-
-
-
-
-
-/////////////////////////////////
-// kleenex debug function
-/////////////////////////////////
-void display_fat( uint32_t step )
-{
-    fatfs_ctx_t * fatfs_ctx = fs_context[FS_TYPE_FATFS].extend;
-    if( fatfs_ctx != NULL ) 
-    {
-        printk("\n[%s] step %d at cycle %d\n", __FUNCTION__, step, (uint32_t)hal_get_cycles() );
-        xptr_t     mapper_xp = fatfs_ctx->fat_mapper_xp;
-        mapper_display_page( mapper_xp , 0 , 128 );
-    }
-    else
-    {
-        printk("\n[%s] step %d : fatfs context not initialized\n", __FUNCTION__, step );
-    }
+    return -1;
 }
 
@@ -949,5 +928,4 @@
     xptr_t       devfs_dev_inode_xp;        // extended pointer on DEVFS dev inode    
     xptr_t       devfs_external_inode_xp;   // extended pointer on DEVFS external inode       
-    xptr_t       devfs_internal_inode_xp;   // extended pointer on DEVFS internal inode       
 
     error_t      error;
@@ -1139,5 +1117,5 @@
 #endif
 
-#if( DEBUG_KERNEL_INIT & 1 )
+#if CONFIG_INSTRUMENTATION_CHDEVS
 if( (core_lid ==  0) & (local_cxy == 0) ) 
 chdev_dir_display();
@@ -1145,8 +1123,19 @@
     
     /////////////////////////////////////////////////////////////////////////////////
-    // STEP 6 : all cores enable IPI (Inter Procesor Interrupt),
-    //          all cores unblock the idle thread, and register it in scheduler.
-    //          core[0] in cluster[0] creates the VFS root inode.
-    //          It access the boot device to initialize the file system context.
+    // STEP 6 : All cores enable IPI (Inter Procesor Interrupt),
+    //          All cores unblock the idle thread, and register it in scheduler.
+    //          The core[0] in cluster defined by the CONFIG_VFS_ROOT_CXY parameter,
+    //          access the IOC device to initialize the VFS for the FS identified
+    //          by the CONFIG_VFS_ROOT_IS_*** parameter. It does the following
+    //          actions in the VFS_ROOT cluster :
+    //          1. allocate and initialize the selected FS context,
+    //          2. create and initializes the VFS root inodes,
+    //          3. initialize the VFS context for FATFS (in fs_context[] array),
+    //          4. create the <.> and <..> dentries in VFS root directory,
+    //          5. register the VFS root inode in process_zero descriptor, 
+    //          6. allocate the DEVFS context, 
+    //          7. initialize the VFS context for DEVFS (in fs_context[] array),
+    //          8. create the <dev> and <external> inodes,
+    //          9. initialize the DEVFS context.
     /////////////////////////////////////////////////////////////////////////////////
 
@@ -1159,76 +1148,151 @@
     core->scheduler.idle = thread;
 
-    // core[O] in cluster[0] creates the VFS root
-    if( (core_lid ==  0) && (local_cxy == 0 ) ) 
+    // core[O] in VFS_ROOT cluster creates the VFS root
+    if( (core_lid ==  0) && (local_cxy == CONFIG_VFS_ROOT_CXY ) ) 
     {
-        vfs_root_inode_xp = XPTR_NULL;
-
         // Only FATFS is supported yet,
-        // other File System can be introduced here
+        // TODO other File System can be introduced below
         if( CONFIG_VFS_ROOT_IS_FATFS )
         {
-            // 1. allocate memory for FATFS context in cluster 0
-            fatfs_ctx_t * fatfs_ctx = fatfs_ctx_alloc();
-
-            if( fatfs_ctx == NULL )
+            // 1. allocate memory and initialize FATFS context in VFS_ROOT cluster
+            xptr_t  fatfs_ctx_xp = fatfs_ctx_alloc( CONFIG_VFS_ROOT_CXY );
+
+            if( fatfs_ctx_xp == XPTR_NULL )
             {
-                printk("\n[PANIC] in %s : cannot create FATFS context in cluster 0\n",
-                __FUNCTION__ );
+                printk("\n[PANIC] in %s : cannot allocate FATFS context in cluster %x\n",
+                __FUNCTION__ , CONFIG_VFS_ROOT_CXY );
                 hal_core_sleep();
             }
 
-            // 2. access boot device to initialize FATFS context
-            fatfs_ctx_init( fatfs_ctx );
-
-            // 3. get various informations from FATFS context
-            uint32_t root_dir_cluster = fatfs_ctx->root_dir_cluster;
-            uint32_t cluster_size     = fatfs_ctx->bytes_per_sector * 
-                                        fatfs_ctx->sectors_per_cluster;
-            uint32_t total_clusters   = fatfs_ctx->fat_sectors_count << 7;
- 
-            // 4. create VFS root inode in cluster 0
-            error = vfs_inode_create( FS_TYPE_FATFS,                       // fs_type
-                                      0,                                   // attr
-                                      0,                                   // rights
-                                      0,                                   // uid
-                                      0,                                   // gid
-                                      &vfs_root_inode_xp );                // return
+            // initialise FATFS context in VFS_ROOT cluster from IOC device (boot_record)
+            error = fatfs_ctx_init( fatfs_ctx_xp );
+
             if( error )
             {
-                printk("\n[PANIC] in %s : cannot create VFS root inode in cluster 0\n",
-                __FUNCTION__ );
+                printk("\n[PANIC] in %s : cannot initialize FATFS context in cluster %x\n",
+                __FUNCTION__ , CONFIG_VFS_ROOT_CXY );
                 hal_core_sleep();
             }
 
-            // 5. update FATFS root inode "type" and "extend" fields  
-            cxy_t         vfs_root_cxy = GET_CXY( vfs_root_inode_xp );
-            vfs_inode_t * vfs_root_ptr = GET_PTR( vfs_root_inode_xp );
-            hal_remote_s32( XPTR( vfs_root_cxy , &vfs_root_ptr->type ), INODE_TYPE_DIR );
-            hal_remote_spt( XPTR( vfs_root_cxy , &vfs_root_ptr->extend ), 
+#if( DEBUG_KERNEL_INIT & 1 )
+printk("\n[%s] initialized FATFS context in cluster %x\n",
+__FUNCTION__, CONFIG_VFS_ROOT_CXY );
+#endif
+    
+            // get various informations from FATFS context
+            fatfs_ctx_t * fatfs_ctx_ptr = GET_PTR( fatfs_ctx_xp );
+
+            uint32_t root_dir_cluster    = hal_remote_l32( XPTR( CONFIG_VFS_ROOT_CXY,
+                                           &fatfs_ctx_ptr->root_dir_cluster ) );
+
+            uint32_t bytes_per_sector    = hal_remote_l32( XPTR( CONFIG_VFS_ROOT_CXY,
+                                           &fatfs_ctx_ptr->bytes_per_sector ) );
+ 
+            uint32_t sectors_per_cluster = hal_remote_l32( XPTR( CONFIG_VFS_ROOT_CXY,
+                                           &fatfs_ctx_ptr->sectors_per_cluster ) );
+ 
+            uint32_t cluster_size        = bytes_per_sector * sectors_per_cluster;
+ 
+            uint32_t fat_sectors_count   = hal_remote_l32( XPTR( CONFIG_VFS_ROOT_CXY,
+                                           &fatfs_ctx_ptr->fat_sectors_count ) ) << 7;
+
+            uint32_t total_clusters      = fat_sectors_count << 7;
+ 
+            // 2. create VFS root inode in VFS_ROOT cluster
+            // TODO define attr, rights, uid, gid
+            error = vfs_inode_create( CONFIG_VFS_ROOT_CXY,          // target cluster
+                                      FS_TYPE_FATFS,                // fs_type
+                                      0,                            // attr
+                                      0,                            // rights
+                                      0,                            // uid
+                                      0,                            // gid
+                                      &vfs_root_inode_xp );         // return
+            if( error )
+            {
+                printk("\n[PANIC] in %s : cannot create VFS root inode in cluster %x\n",
+                __FUNCTION__ , CONFIG_VFS_ROOT_CXY );
+                hal_core_sleep();
+            }
+
+#if( DEBUG_KERNEL_INIT & 1 )
+vfs_inode_t * root_inode = GET_PTR( vfs_root_inode_xp );
+printk("\n[%s] created </> root inode %x in cluster %x / ctx %x\n",
+__FUNCTION__, root_inode, CONFIG_VFS_ROOT_CXY, root_inode->ctx );
+#endif
+    
+            // update FATFS root inode "type" and "extend" fields  
+            vfs_inode_t * vfs_root_inode_ptr = GET_PTR( vfs_root_inode_xp );
+
+            hal_remote_s32( XPTR( CONFIG_VFS_ROOT_CXY , &vfs_root_inode_ptr->type ),
+                            INODE_TYPE_DIR );
+
+            hal_remote_spt( XPTR( CONFIG_VFS_ROOT_CXY , &vfs_root_inode_ptr->extend ), 
                             (void*)(intptr_t)root_dir_cluster );
 
-            // 6. initialize the generic VFS context for FATFS 
-            vfs_ctx_init( FS_TYPE_FATFS,                               // fs type
-                          0,                                           // attributes: unused 
-	                      total_clusters,                              // number of clusters
-	                      cluster_size,                                // bytes
-	                      vfs_root_inode_xp,                           // VFS root
-                          fatfs_ctx );                                 // extend
+            // 3. initialize the VFS context for FATFS in VFS_ROOT cluster
+            vfs_ctx_init( CONFIG_VFS_ROOT_CXY,                      // target cluster
+                          FS_TYPE_FATFS,                            // fs type
+	                      total_clusters,                           // number of clusters
+	                      cluster_size,                             // bytes
+	                      vfs_root_inode_xp,                        // VFS root
+                          fatfs_ctx_ptr );                          // extend
+
+#if( DEBUG_KERNEL_INIT & 1 )
+vfs_ctx_t * vfs_for_fatfs_ctx =  &fs_context[FS_TYPE_FATFS];
+printk("\n[%s] initialized VFS_for_FATFS context in cluster %x / ctx %x / fs_type %d\n",
+__FUNCTION__, CONFIG_VFS_ROOT_CXY, vfs_for_fatfs_ctx, vfs_for_fatfs_ctx->type );
+#endif
         }
         else
         {
-            printk("\n[PANIC] in %s : unsupported VFS type in cluster 0\n",
-            __FUNCTION__ );
+            printk("\n[PANIC] in %s : unsupported VFS type in cluster %x\n",
+            __FUNCTION__ , CONFIG_VFS_ROOT_CXY );
             hal_core_sleep();
         }
 
-        // create the <.> and <..> dentries in VFS root directory
+        // 4. create the <.> and <..> dentries in VFS root directory
         // the VFS root parent inode is the VFS root inode itself 
         vfs_add_special_dentries( vfs_root_inode_xp,
                                   vfs_root_inode_xp );
 
-        // register VFS root inode in process_zero descriptor of cluster 0
-        process_zero.vfs_root_xp = vfs_root_inode_xp;
-        process_zero.cwd_xp      = vfs_root_inode_xp;
+        // 5. register VFS root inode in target cluster process_zero descriptor 
+        hal_remote_s64( XPTR( CONFIG_VFS_ROOT_CXY , &process_zero.vfs_root_xp ),
+                        vfs_root_inode_xp );
+        hal_remote_s64( XPTR( CONFIG_VFS_ROOT_CXY , &process_zero.cwd_xp ),
+                        vfs_root_inode_xp );
+
+        // 6. allocate memory for DEVFS context in VFS_ROOT cluster
+        xptr_t devfs_ctx_xp = devfs_ctx_alloc( CONFIG_VFS_ROOT_CXY );
+
+        if( devfs_ctx_xp == XPTR_NULL )
+        {
+            printk("\n[PANIC] in %s : cannot create DEVFS context in cluster %x\n",
+            __FUNCTION__ , CONFIG_VFS_ROOT_CXY );
+            hal_core_sleep();
+        }
+
+        // 7. initialize the VFS context for DEVFS in VFS_ROOT cluster 
+        vfs_ctx_init( CONFIG_VFS_ROOT_CXY,                          // target cluster
+                      FS_TYPE_DEVFS,                                // fs type
+	                  0,                                            // total_clusters: unused
+	                  0,                                            // cluster_size: unused 
+	                  vfs_root_inode_xp,                            // VFS root
+                      GET_PTR( devfs_ctx_xp ) );                    // extend
+
+#if( DEBUG_KERNEL_INIT & 1 )
+vfs_ctx_t * vfs_for_devfs_ctx =  &fs_context[FS_TYPE_DEVFS];
+printk("\n[%s] initialized VFS_for_DEVFS context in cluster %x / ctx %x / fs_type %d\n",
+__FUNCTION__, CONFIG_VFS_ROOT_CXY, vfs_for_devfs_ctx, vfs_for_devfs_ctx->type );
+#endif
+
+        // 8. create "dev" and "external" inodes (directories)
+        devfs_global_init( vfs_root_inode_xp,
+                           &devfs_dev_inode_xp,
+                           &devfs_external_inode_xp );
+
+        // 9. initializes DEVFS context in VFS_ROOT cluster
+        devfs_ctx_init( devfs_ctx_xp,
+                        devfs_dev_inode_xp,
+                        devfs_external_inode_xp );
     }
 
@@ -1240,28 +1304,31 @@
 
 #if DEBUG_KERNEL_INIT
-if( (core_lid ==  0) & (local_cxy == 0) ) 
-printk("\n[%s] exit barrier 6 : VFS root (%x,%x) in cluster 0 / cycle %d\n",
-__FUNCTION__, GET_CXY(process_zero.vfs_root_xp),
-GET_PTR(process_zero.vfs_root_xp), (uint32_t)hal_get_cycles() );
-#endif
-
-    /////////////////////////////////////////////////////////////////////////////////
-    // STEP 7 : In all other clusters than cluster[0], the core[0] allocates memory
-    //          for the selected FS context, and initialise the local FS context and 
-    //          the local VFS context from values stored in cluster 0. 
-    //          They get the VFS root inode extended pointer from cluster 0.
-    /////////////////////////////////////////////////////////////////////////////////
-
-    if( (core_lid ==  0) && (local_cxy != 0) ) 
+if( (core_lid ==  0) & (local_cxy == CONFIG_VFS_ROOT_CXY) ) 
+printk("\n[%s] exit barrier 6 : VFS root inode (%x) created in cluster (%x) / cycle %d\n",
+__FUNCTION__, GET_CXY(vfs_root_inode_xp), 
+GET_PTR(vfs_root_inode_xp), (uint32_t)hal_get_cycles() );
+#endif
+
+    /////////////////////////////////////////////////////////////////////////////////
+    // STEP 7 : In all clusters other than the VFS_ROOT cluster, the core[0] makes
+    //          the following local actions to complete the VFS initialisation :
+    //          1. allocate a local context for the selected FS extension,
+    //          2. copy FS context from VFS_ROOT cluster to local cluster,
+    //          3. copy VFS_for_FATFS context from VFS_ROOT cluster to local cluster, 
+    //          4. allocate a local context for the DEVFS extension, 
+    //          5. copy DEVFS context from VFS_ROOT cluster to local cluster,
+    //          6. update the local "root_inode_xp" field in process_zero.
+    /////////////////////////////////////////////////////////////////////////////////
+
+    if( (core_lid ==  0) && (local_cxy != CONFIG_VFS_ROOT_CXY) ) 
     {
-        // File System must be FATFS in this implementation,
-        // but other File System can be introduced here
+        // only FATFS is supported yet
+        // TODO other File System can be introduced below
         if( CONFIG_VFS_ROOT_IS_FATFS )
         {
-            // 1. allocate memory for local FATFS context
-            fatfs_ctx_t * local_fatfs_ctx = fatfs_ctx_alloc();
-
-            // check memory
-            if( local_fatfs_ctx == NULL )
+            // 1. allocate a local FATFS context extension
+            xptr_t local_fatfs_ctx_xp = fatfs_ctx_alloc( local_cxy );
+
+            if( local_fatfs_ctx_xp == XPTR_NULL )
             {
                 printk("\n[PANIC] in %s : cannot create FATFS context in cluster %x\n",
@@ -1270,33 +1337,75 @@
             }
 
-            // 2. get local pointer on VFS context for FATFS
-            vfs_ctx_t   * vfs_ctx = &fs_context[FS_TYPE_FATFS];
-
-            // 3. get local pointer on FATFS context in cluster 0 
-            fatfs_ctx_t * remote_fatfs_ctx = hal_remote_lpt( XPTR( 0 , &vfs_ctx->extend ) );
-
-            // 4. copy FATFS context from cluster 0 to local cluster
-            hal_remote_memcpy( XPTR( local_cxy , local_fatfs_ctx ), 
-                               XPTR( 0 ,         remote_fatfs_ctx ), sizeof(fatfs_ctx_t) );
-
-            // 5. copy VFS context from cluster 0 to local cluster
-            hal_remote_memcpy( XPTR( local_cxy , vfs_ctx ), 
-                               XPTR( 0 ,         vfs_ctx ), sizeof(vfs_ctx_t) );
-
-            // 6. update extend field in local copy of VFS context
-            vfs_ctx->extend = local_fatfs_ctx;
-
-            if( ((fatfs_ctx_t *)vfs_ctx->extend)->sectors_per_cluster != 8 )
-            {
-                printk("\n[PANIC] in %s : illegal FATFS context in cluster %x\n",
-                __FUNCTION__ , local_cxy );
-                hal_core_sleep();
-            }
-        }
-
-        // get extended pointer on VFS root inode from cluster 0
-        vfs_root_inode_xp = hal_remote_l64( XPTR( 0 , &process_zero.vfs_root_xp ) );
-
-        // update local process_zero descriptor
+            // get local pointer on VFS_for_FATFS context (same in all clusters) 
+            vfs_ctx_t * vfs_fat_ctx_ptr = &fs_context[FS_TYPE_FATFS];
+
+            // build extended pointer on VFS_for_FATFS "extend" field in VFS_ROOT cluster
+            xptr_t fatfs_extend_xp = XPTR( CONFIG_VFS_ROOT_CXY , &vfs_fat_ctx_ptr->extend );
+
+            // get local pointer on FATFS context in VFS_ROOT cluster 
+            fatfs_ctx_t * remote_fatfs_ctx_ptr = hal_remote_lpt( fatfs_extend_xp );
+
+            // build extended pointer on FATFS context in VFS_ROOT cluster
+            xptr_t remote_fatfs_ctx_xp = XPTR( CONFIG_VFS_ROOT_CXY , remote_fatfs_ctx_ptr );
+
+            // 2. copy FATFS context from VFS_ROOT cluster to local cluster
+            hal_remote_memcpy( local_fatfs_ctx_xp,
+                               remote_fatfs_ctx_xp,
+                               sizeof(fatfs_ctx_t) );
+
+            // build extended pointer on remote VFS_for_FATFS context
+            xptr_t remote_vfs_ctx_xp = XPTR( CONFIG_VFS_ROOT_CXY , vfs_fat_ctx_ptr );
+
+            // build extended pointer on local VFS_for_FATFS context
+            xptr_t local_vfs_ctx_xp = XPTR( local_cxy , vfs_fat_ctx_ptr );
+ 
+            // 3. copy VFS_for_FATFS context from VFS_ROOT cluster to local cluster
+            hal_remote_memcpy( local_vfs_ctx_xp,
+                               remote_vfs_ctx_xp,
+                               sizeof(vfs_ctx_t) );
+
+            // update "extend" field in local VFS_for_FATFS context
+            vfs_fat_ctx_ptr->extend = GET_PTR( local_fatfs_ctx_xp );
+
+// check local FATFS and VFS context copies
+assert( (((fatfs_ctx_t *)vfs_fat_ctx_ptr->extend)->sectors_per_cluster == 8),
+"illegal FATFS context in cluster %x\n", local_cxy );
+
+        }
+        else
+        {
+            printk("\n[PANIC] in %s : unsupported VFS type in cluster %x\n",
+            __FUNCTION__ , local_cxy );
+            hal_core_sleep();
+        }
+
+        // 4. allocate a local DEVFS context extension, 
+        xptr_t local_devfs_ctx_xp = devfs_ctx_alloc( local_cxy );
+
+        // get local pointer on VFS_for_DEVFS context (same in all clusters) 
+        vfs_ctx_t * vfs_dev_ctx_ptr = &fs_context[FS_TYPE_DEVFS];
+
+        // build extended pointer on VFS_for_DEVFS extend field in VFS_ROOT cluster
+        xptr_t remote_extend_xp = XPTR( CONFIG_VFS_ROOT_CXY , &vfs_dev_ctx_ptr->extend );
+
+        // get local pointer on DEVFS context in VFS_ROOT cluster 
+        devfs_ctx_t * remote_devfs_ctx_ptr = hal_remote_lpt( remote_extend_xp );
+
+        // build extended pointer on FATFS context in VFS_ROOT cluster
+        xptr_t remote_devfs_ctx_xp = XPTR( CONFIG_VFS_ROOT_CXY , remote_devfs_ctx_ptr );
+
+        // 5. copy DEVFS context from VFS_ROOT cluster to local cluster
+        hal_remote_memcpy( local_devfs_ctx_xp,
+                           remote_devfs_ctx_xp,
+                           sizeof(devfs_ctx_t) );
+
+        // update "extend" field in local VFS_for_DEVFS context
+        vfs_dev_ctx_ptr->extend = GET_PTR( local_devfs_ctx_xp );
+
+        // get extended pointer on VFS root inode from VFS_ROOT cluster
+        vfs_root_inode_xp = hal_remote_l64( XPTR( CONFIG_VFS_ROOT_CXY,
+                                                  &process_zero.vfs_root_xp ) );
+
+        // 6. update local process_zero descriptor
         process_zero.vfs_root_xp = vfs_root_inode_xp;
         process_zero.cwd_xp      = vfs_root_inode_xp;
@@ -1310,46 +1419,24 @@
 
 #if DEBUG_KERNEL_INIT
-if( (core_lid ==  0) & (local_cxy == 1) ) 
-printk("\n[%s] exit barrier 7 : VFS root (%x,%x) in cluster 1 / cycle %d\n",
-__FUNCTION__, GET_CXY(process_zero.vfs_root_xp),
-GET_PTR(process_zero.vfs_root_xp), (uint32_t)hal_get_cycles() );
-#endif
-
-    /////////////////////////////////////////////////////////////////////////////////
-    // STEP 8 : core[0] in cluster 0 makes the global DEVFS initialisation:
-    //          It initializes the DEVFS context, and creates the DEVFS
-    //          "dev" and "external" inodes in cluster 0.
-    /////////////////////////////////////////////////////////////////////////////////
-
-    if( (core_lid ==  0) && (local_cxy == 0) ) 
+if( (core_lid ==  0) & (local_cxy == 0) ) 
+printk("\n[%s] exit barrier 7 : VFS & DEVFS contexts replicated in all clusters / cycle %d\n",
+__FUNCTION__ , (uint32_t)hal_get_cycles() );
+#endif
+
+    /////////////////////////////////////////////////////////////////////////////////
+    // STEP 8 : In all clusters in parallel, core[0] completes DEVFS initialization.
+    //          Each core[0] creates the local DEVFS "internal" directory,
+    //          and creates the pseudo-files for chdevs placed in local cluster.
+    /////////////////////////////////////////////////////////////////////////////////
+
+    if( core_lid == 0 )
     {
-        // 1. allocate memory for DEVFS context extension in cluster 0
-        devfs_ctx_t * devfs_ctx = devfs_ctx_alloc();
-
-        if( devfs_ctx == NULL )
-        {
-            printk("\n[PANIC] in %s : cannot create DEVFS context in cluster 0\n",
-            __FUNCTION__ , local_cxy );
-            hal_core_sleep();
-        }
-
-        // 2. initialize the DEVFS entry in the vfs_context[] array 
-        vfs_ctx_init( FS_TYPE_DEVFS,                                // fs type
-                      0,                                            // attributes: unused
-	                  0,                                            // total_clusters: unused
-	                  0,                                            // cluster_size: unused 
-	                  vfs_root_inode_xp,                            // VFS root
-                      devfs_ctx );                                  // extend
-
-        // 3. create "dev" and "external" inodes (directories)
-        devfs_global_init( process_zero.vfs_root_xp,
-                           &devfs_dev_inode_xp,
-                           &devfs_external_inode_xp );
-
-        // 4. initializes DEVFS context extension
-        devfs_ctx_init( devfs_ctx,
-                        devfs_dev_inode_xp,
-                        devfs_external_inode_xp );
-    }    
+        // get local pointer on local DEVFS context
+        devfs_ctx_t * ctx = fs_context[FS_TYPE_DEVFS].extend;
+
+        // populate DEVFS in all clusters
+        devfs_local_init( ctx->dev_inode_xp,
+                          ctx->external_inode_xp );
+    }
 
     /////////////////////////////////////////////////////////////////////////////////
@@ -1361,41 +1448,5 @@
 #if DEBUG_KERNEL_INIT
 if( (core_lid ==  0) & (local_cxy == 0) ) 
-printk("\n[%s] exit barrier 8 : DEVFS root initialized in cluster 0 / cycle %d\n",
-__FUNCTION__, (uint32_t)hal_get_cycles() );
-#endif
-
-    /////////////////////////////////////////////////////////////////////////////////
-    // STEP 9 : In all clusters in parallel, core[0] completes DEVFS initialization.
-    //          Each core[0] get the "dev" and "external" extended pointers from
-    //          values stored in cluster(0), creates the DEVFS "internal" directory, 
-    //          and creates the pseudo-files for all chdevs in local cluster.
-    /////////////////////////////////////////////////////////////////////////////////
-
-    if( core_lid == 0 )
-    {
-        // get extended pointer on "extend" field of VFS context for DEVFS in cluster 0
-        xptr_t  extend_xp = XPTR( 0 , &fs_context[FS_TYPE_DEVFS].extend );
-
-        // get pointer on DEVFS context in cluster 0
-        devfs_ctx_t * devfs_ctx = hal_remote_lpt( extend_xp );
-        
-        devfs_dev_inode_xp      = hal_remote_l64( XPTR( 0 , &devfs_ctx->dev_inode_xp ) );
-        devfs_external_inode_xp = hal_remote_l64( XPTR( 0 , &devfs_ctx->external_inode_xp ) );
-
-        // populate DEVFS in all clusters
-        devfs_local_init( devfs_dev_inode_xp,
-                          devfs_external_inode_xp,
-                          &devfs_internal_inode_xp );
-    }
-
-    /////////////////////////////////////////////////////////////////////////////////
-    if( core_lid == 0 ) xbarrier_wait( XPTR( 0 , &global_barrier ), 
-                                        (info->x_size * info->y_size) );
-    barrier_wait( &local_barrier , info->cores_nr );
-    /////////////////////////////////////////////////////////////////////////////////
-
-#if DEBUG_KERNEL_INIT
-if( (core_lid ==  0) & (local_cxy == 0) ) 
-printk("\n[%s] exit barrier 9 : DEVFS initialized in cluster 0 / cycle %d\n",
+printk("\n[%s] exit barrier 8 : DEVFS initialized in all clusters / cycle %d\n",
 __FUNCTION__, (uint32_t)hal_get_cycles() );
 #endif
@@ -1407,7 +1458,7 @@
 
     /////////////////////////////////////////////////////////////////////////////////
-    // STEP 10 : core[0] in cluster 0 creates the first user process (process_init).
-    //           This include the first user process VMM (GPT and VSL) creation.
-    //           Finally, it prints the ALMOS-MKH banner.
+    // STEP 9 : core[0] in cluster 0 creates the first user process (process_init).
+    //          This include the process VMM (GPT and VSL) creation.
+    //          Finally, it prints the ALMOS-MKH banner.
     /////////////////////////////////////////////////////////////////////////////////
 
@@ -1419,5 +1470,5 @@
 #if DEBUG_KERNEL_INIT
 if( (core_lid ==  0) & (local_cxy == 0) ) 
-printk("\n[%s] exit barrier 10 : process_init created in cluster 0 / cycle %d\n",
+printk("\n[%s] exit barrier 9 : process_init created in cluster 0 / cycle %d\n",
 __FUNCTION__, (uint32_t)hal_get_cycles() );
 #endif
Index: /trunk/kernel/kern/printk.h
===================================================================
--- /trunk/kernel/kern/printk.h	(revision 656)
+++ /trunk/kernel/kern/printk.h	(revision 657)
@@ -2,5 +2,5 @@
  * printk.h - Kernel Log & debug messages API definition.
  *
- * authors  Alain Greiner (2016,2017,2018)
+ * authors  Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -123,5 +123,5 @@
 
 /**********************************************************************************
- * This function displays a non-formated message on TXT0 terminal.
+ * This debug function displays a non-formated message on TXT0 terminal.
  * This function is actually used to debug the assembly level kernel functions.
  **********************************************************************************
@@ -131,5 +131,5 @@
 
 /**********************************************************************************
- * This function displays a 32 bits value in hexadecimal on TXT0 terminal.
+ * This debug function displays a 32 bits value in hexadecimal on TXT0 terminal.
  * This function is actually used to debug the assembly level kernel functions.
  **********************************************************************************
@@ -139,5 +139,5 @@
 
 /**********************************************************************************
- * This function displays a 32 bits signed value in decimal on TXT0 terminal.
+ * This debug function displays a 32 bits signed value in decimal on TXT0 terminal.
  * This function is actually used to debug the assembly level kernel functions.
  **********************************************************************************
@@ -147,5 +147,5 @@
 
 /**********************************************************************************
- * This function displays a 64 bits value in hexadecimal on TXT0 terminal.
+ * This debug function displays a 64 bits value in hexadecimal on TXT0 terminal.
  * This function is actually used to debug the assembly level kernel functions.
  **********************************************************************************
@@ -158,5 +158,5 @@
  * array of bytes defined by <buffer> and <size> arguments (16 bytes per line).
  * The <string> argument is displayed before the buffer content.
- * The line format is an address folowed by 16 (hexa) bytes.
+ * The line format is an address followed by 16 (hexa) bytes.
  **********************************************************************************
  * @ string   : buffer name or identifier.
Index: /trunk/kernel/kern/process.c
===================================================================
--- /trunk/kernel/kern/process.c	(revision 656)
+++ /trunk/kernel/kern/process.c	(revision 657)
@@ -4,5 +4,5 @@
  * Authors  Ghassan Almaless (2008,2009,2010,2011,2012)
  *          Mohamed Lamine Karaoui (2015)
- *          Alain Greiner (2016,2017,2018,2019)
+ *          Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -1103,9 +1103,9 @@
     xptr_t    xp;
 
-    // get reference process cluster and local pointer
+    // get target process cluster and local pointer
     process_t * process_ptr = GET_PTR( process_xp );
     cxy_t       process_cxy = GET_CXY( process_xp );
 
-// check client process is reference process
+// check target process is reference process
 assert( (process_xp == hal_remote_l64( XPTR( process_cxy , &process_ptr->ref_xp ) ) ),
 "client process must be reference process\n" );
@@ -1158,4 +1158,85 @@
 
 }  // end process_fd_register()
+
+/////////////////////////////////////////////
+void process_fd_remove( xptr_t    process_xp,
+                        uint32_t  fdid )
+{
+    pid_t       pid;           // target process PID 
+    lpid_t      lpid;          // target process LPID
+    xptr_t      iter_xp;       // iterator for list of process copies
+    xptr_t      copy_xp;       // extended pointer on process copy
+    process_t * copy_ptr;      // local pointer on process copy  
+    cxy_t       copy_cxy;      // process copy cluster identifier
+
+// check process_xp argument
+assert( (process_xp != XPTR_NULL), "process_xp argument cannot be XPTR_NULL");
+
+    // get target process cluster and local pointer
+    process_t * process_ptr = GET_PTR( process_xp );
+    cxy_t       process_cxy = GET_CXY( process_xp );
+
+    // get target process pid and lpid
+    pid  = hal_remote_l32( XPTR( process_cxy , &process_ptr->pid) );
+    lpid = LPID_FROM_PID( pid );
+
+    // get process descriptor in owner cluster and in reference cluster
+    xptr_t  owner_xp = hal_remote_l64( XPTR( process_cxy , &process_ptr->owner_xp ));
+    xptr_t  ref_xp   = hal_remote_l64( XPTR( process_cxy , &process_ptr->ref_xp   ));
+
+// check target process in in owner cluster 
+assert( (process_xp == owner_xp), "target process must be in owner process\n" );
+
+#if DEBUG_PROCESS_FD_REMOVE
+uint32_t    cycle = (uint32_t)hal_get_cycles();
+thread_t  * this  = CURRENT_THREAD;
+if( DEBUG_PROCESS_FD_REMOVE < cycle )
+printk("\n[%s] thread[%x,%x] enter for fdid %d in process %x / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, fdid, pid, cycle );
+#endif
+
+    // build extended pointers on list_of_copies root and lock (in owner cluster)
+    xptr_t copies_root_xp = XPTR( process_cxy , &LOCAL_CLUSTER->pmgr.copies_root[lpid] );
+    xptr_t copies_lock_xp = XPTR( process_cxy , &LOCAL_CLUSTER->pmgr.copies_lock[lpid] );
+ 
+    // get reference process cluster and local pointer
+    process_t * ref_ptr = GET_PTR( ref_xp );
+    cxy_t       ref_cxy = GET_CXY( ref_xp );
+
+    // build extended pointer on lock protecting reference fd_array
+    xptr_t fd_lock_xp = XPTR( ref_cxy , &ref_ptr->fd_array.lock );
+
+    // take lock protecting reference fd_array
+	remote_queuelock_acquire( fd_lock_xp );
+
+    // take the lock protecting the list of copies
+    remote_queuelock_acquire( copies_lock_xp );
+
+    // loop on list of process copies
+    XLIST_FOREACH( copies_root_xp , iter_xp )
+    {
+        // get pointers on process copy
+        copy_xp  = XLIST_ELEMENT( iter_xp , process_t , copies_list );
+        copy_ptr = GET_PTR( copy_xp );
+        copy_cxy = GET_CXY( copy_xp );
+
+        // release the fd_array entry in process copy
+        hal_remote_s64( XPTR( copy_cxy , &copy_ptr->fd_array.array[fdid] ), XPTR_NULL );
+    }
+
+    // release the lock protecting reference fd_array
+	remote_queuelock_release( fd_lock_xp );
+
+    // release the lock protecting the list of copies
+    remote_queuelock_release( copies_lock_xp );
+
+#if DEBUG_PROCESS_FD_REMOVE
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_PROCESS_FD_REMOVE < cycle )
+printk("\n[%s] thread[%x,%x] exit for fdid %d in process %x / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, fdid, pid, cycle );
+#endif
+
+}  // end process_fd_remove()
 
 ////////////////////////////////////////////////
Index: /trunk/kernel/kern/process.h
===================================================================
--- /trunk/kernel/kern/process.h	(revision 656)
+++ /trunk/kernel/kern/process.h	(revision 657)
@@ -4,5 +4,5 @@
  * Authors  Ghassan Almaless (2008,2009,2010,2011,2012)
  *          Mohamed Lamine Karaoui (2015)
- *          Alain Greiner (2016,2017,2018,2019)
+ *          Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -70,6 +70,6 @@
 /*********************************************************************************************
  * This structure defines an array of extended pointers on the open file descriptors
- * for a given process. We use an extended pointer because the open file descriptor
- * is always stored in the same cluster as the inode associated to the file.
+ * for a given process. We use an extended pointer because the open file descriptors
+ * are always stored in the same cluster as the inode associated to the file.
  * A free entry in this array contains the XPTR_NULL value.
  * The array size is defined by the CONFIG_PROCESS_FILE_MAX_NR parameter.
@@ -79,7 +79,9 @@
  *       - the fd_array[] in a process copy is simply a cache containing a subset of the
  *         open files to speed the fdid to xptr translation, but the "lock" and "current
- *         fields are not used.
- *       - all modifications made by the process_fd_remove() are done in reference cluster
- *         and reported in all process_copies.
+ *         fields are not significant for these copies.
+ *       - the modifications made by the process_fd_remove() function are done in the
+ *         reference cluster in all process_copies.
+ *       - The modifications made by the process_fd_register() function are done in the
+ *         reference cluster, and in the cluster containing the calling thread.
  ********************************************************************************************/
 
@@ -436,9 +438,9 @@
 
 /*********************************************************************************************
- * This function allocates a free slot in the fd_array of the reference process
+ * This function allocates a free slot in the fd_array of the reference process descriptor
  * identified by the <process_xp> argument, register the <file_xp> argument in the
  * allocated slot, and return the slot index in the <fdid> buffer.
- * It can be called by any thread in any cluster, because it uses remote access
- * primitives to access the reference process descriptor.
+ * Note: we must use the reference process descriptor, because the reference fd_array is
+ * contained in the reference cluster.  It can be called by any thread in any cluster.
  * It takes the lock protecting the reference fd_array against concurrent accesses.
  *********************************************************************************************
@@ -454,15 +456,15 @@
 /*********************************************************************************************
  * This function uses as many remote accesses as required, to reset an entry in fd_array[],
- * in all clusters containing a copy. The entry is identified by the <fdid> argument.
- * This function must be executed by a thread running in reference cluster, that contains
- * the complete list of process descriptors copies.
+ * identified by the <fdid> argument, in all clusters containing a copy of the 
+ * process descriptor, identified by the <process_xp> argument. 
+ * Note: we must use the owner process descriptor, because only this owner cluster contains
+ * the list of process copies. It can be called by any thread in any cluster.
  * It takes the lock protecting the reference fd_array against concurrent accesses.
- * TODO this function is not implemented yet.
  *********************************************************************************************
  * @ process  : [in] pointer on the local process descriptor.
  * @ fdid     : [in] file descriptor index in the fd_array.
  ********************************************************************************************/
-void process_fd_remove( process_t * process,
-                        uint32_t    fdid );
+void process_fd_remove( xptr_t     process_xp,
+                        uint32_t   fdid );
 
 /*********************************************************************************************
Index: /trunk/kernel/kern/rpc.c
===================================================================
--- /trunk/kernel/kern/rpc.c	(revision 656)
+++ /trunk/kernel/kern/rpc.c	(revision 657)
@@ -2,5 +2,5 @@
  * rpc.c - RPC operations implementation.
  * 
- * Author    Alain Greiner (2016,2017,2018,2019)
+ * Author    Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c)  UPMC Sorbonne Universites
@@ -59,28 +59,17 @@
     &rpc_thread_user_create_server,        // 6
     &rpc_thread_kernel_create_server,      // 7
-    &rpc_vfs_fs_update_dentry_server,      // 8
+    &rpc_undefined,                        // 8
     &rpc_process_sigaction_server,         // 9
 
-    &rpc_vfs_inode_create_server,          // 10  
-    &rpc_vfs_inode_destroy_server,         // 11  
-    &rpc_vfs_dentry_create_server,         // 12  
-    &rpc_vfs_dentry_destroy_server,        // 13  
-    &rpc_vfs_file_create_server,           // 14
-    &rpc_vfs_file_destroy_server,          // 15
-    &rpc_vfs_fs_new_dentry_server,         // 16
-    &rpc_vfs_fs_add_dentry_server,         // 17
-    &rpc_vfs_fs_remove_dentry_server,      // 18
-    &rpc_vfs_inode_load_all_pages_server,  // 19 
-
-    &rpc_undefined,                        // 20
-    &rpc_undefined,                        // 21
-    &rpc_undefined,                        // 22
-    &rpc_undefined,                        // 23
-    &rpc_mapper_sync_server,               // 24
-    &rpc_vmm_resize_vseg_server,           // 25
-    &rpc_vmm_remove_vseg_server,           // 26 
-    &rpc_vmm_create_vseg_server,           // 27
-    &rpc_vmm_set_cow_server,               // 28
-    &rpc_undefined,                        // 29
+    &rpc_undefined,                        // 10
+    &rpc_undefined,                        // 11
+    &rpc_undefined,                        // 12
+    &rpc_undefined,                        // 13
+    &rpc_undefined,                        // 14
+    &rpc_vmm_resize_vseg_server,           // 15
+    &rpc_vmm_remove_vseg_server,           // 16 
+    &rpc_vmm_create_vseg_server,           // 17
+    &rpc_vmm_set_cow_server,               // 18
+    &rpc_undefined,                        // 19
 };
 
@@ -95,28 +84,17 @@
     "THREAD_USER_CREATE",        // 6
     "THREAD_KERNEL_CREATE",      // 7
-    "VFS_FS_UPDATE_DENTRY",      // 8
+    "FBF_DISPLAY",               // 8
     "PROCESS_SIGACTION",         // 9
 
-    "VFS_INODE_CREATE",          // 10
-    "VFS_INODE_DESTROY",         // 11
-    "VFS_DENTRY_CREATE",         // 12
-    "VFS_DENTRY_DESTROY",        // 13
-    "VFS_FILE_CREATE",           // 14
-    "VFS_FILE_DESTROY",          // 15
-    "VFS_FS_NEW_DENTRY",         // 16
-    "VFS_FS_ADD_DENTRY",         // 17
-    "VFS_FS_REMOVE_DENTRY",      // 18
-    "VFS_INODE_LOAD_ALL_PAGES",  // 19
-
-    "VMM_GLOBAL_RESIZE_VSEG",    // 20
-    "VMM_GLOBAL_UPDATE_PTE",     // 21
-    "undefined_22",              // 22
-    "undefined_23",              // 23
-    "MAPPER_SYNC",               // 24
-    "undefined_25",              // 25
-    "VMM_REMOVE_VSEG",           // 26
-    "VMM_CREATE_VSEG",           // 27
-    "VMM_SET_COW",               // 28
-    "undefined_29",              // 29
+    "undefined_10",              // 10
+    "undefined_11",              // 11
+    "undefined_12",              // 12
+    "undefined_13",              // 13
+    "undefined_14",              // 14
+    "VMM_RESIZE_VSEG",           // 15
+    "VMM_REMOVE_VSEG",           // 16
+    "VMM_CREATE_VSEG",           // 17
+    "VMM_SET_COW",               // 18
+    "undefined_19",              // 19
 };
 
@@ -423,212 +401,24 @@
 
 
-/////////////////////////////////////////////////////////////////////////////////////////
-// [0]       RPC_PMEM_GET_PAGES deprecated [AG] May 2019
-/////////////////////////////////////////////////////////////////////////////////////////
-
-/*
-///////////////////////////////////////////////
-void rpc_pmem_get_pages_client( cxy_t      cxy,
-                                uint32_t   order,      // in 
-                                page_t  ** page )      // out 
-{
-#if DEBUG_RPC_PMEM_GET_PAGES
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_PMEM_GET_PAGES )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
-#endif
-
-    uint32_t responses = 1;
-
-    // initialise RPC descriptor header 
-    rpc_desc_t  rpc;
-    rpc.index     = RPC_PMEM_GET_PAGES;
-    rpc.blocking  = true;
-    rpc.rsp       = &responses;
-
-    // set input arguments in RPC descriptor 
-    rpc.args[0] = (uint64_t)order;
-
-    // register RPC request in remote RPC fifo
-    rpc_send( cxy , &rpc );
-
-    // get output arguments from RPC descriptor
-    *page = (page_t *)(intptr_t)rpc.args[1];
-
-#if DEBUG_RPC_PMEM_GET_PAGES
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_PMEM_GET_PAGES )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
-#endif
-}
-
-///////////////////////////////////////////
-void rpc_pmem_get_pages_server( xptr_t xp )
-{
-#if DEBUG_RPC_PMEM_GET_PAGES
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_PMEM_GET_PAGES )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
-#endif
-
-    // get client cluster identifier and pointer on RPC descriptor
-    cxy_t        cxy  = GET_CXY( xp );
-    rpc_desc_t * desc = GET_PTR( xp );
-
-    // get input arguments from client RPC descriptor
-    uint32_t order = (uint32_t)hal_remote_l64( XPTR( cxy , &desc->args[0] ) );
-    
-    // call local pmem allocator
-    page_t * page = ppm_alloc_pages( order ); 
-
-    // set output arguments into client RPC descriptor
-    hal_remote_s64( XPTR( cxy , &desc->args[1] ) , (uint64_t)(intptr_t)page );
-
-#if DEBUG_RPC_PMEM_GET_PAGES
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_PMEM_GET_PAGES )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
-#endif
-}
-*/
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// [1]       RPC_PMEM_RELEASE_PAGES deprecated [AG] may 2019
-/////////////////////////////////////////////////////////////////////////////////////////
-
-/*
-//////////////////////////////////////////////////
-void rpc_pmem_release_pages_client( cxy_t     cxy,
-                                    page_t  * page )      // out 
-{
-#if DEBUG_RPC_PMEM_RELEASE_PAGES
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_PMEM_RELEASE_PAGES )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-
-    uint32_t responses = 1;
-
-    // initialise RPC descriptor header 
-    rpc_desc_t  rpc;
-    rpc.index    = RPC_PMEM_RELEASE_PAGES;
-    rpc.blocking = true;
-    rpc.rsp      = &responses;
-
-    // set input arguments in RPC descriptor 
-    rpc.args[0] = (uint64_t)(intptr_t)page;
-
-    // register RPC request in remote RPC fifo
-    rpc_send( cxy , &rpc );
-
-#if DEBUG_RPC_PMEM_RELEASE_PAGES
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_PMEM_RELEASE_PAGES )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
-#endif
-}
-
-///////////////////////////////////////////////
-void rpc_pmem_release_pages_server( xptr_t xp )
-{
-#if DEBUG_RPC_PMEM_RELEASE_PAGES
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_PMEM_RELEASE_PAGES )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-
-    // get client cluster identifier and pointer on RPC descriptor
-    cxy_t        cxy  = GET_CXY( xp );
-    rpc_desc_t * desc = GET_PTR( xp );
-
-    // get input arguments from client RPC descriptor
-    page_t * page = (page_t *)(intptr_t)hal_remote_l64( XPTR( cxy , &desc->args[0] ) );
-    
-    // release memory to local pmem 
-    kmem_req_t req;
-    req.type = KMEM_PPM;
-    req.ptr  = page;
-    kmem_free( &req );
-
-#if DEBUG_RPC_PMEM_RELEASE_PAGES
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_PMEM_RELEASE_PAGES )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
-#endif
-}
-*/
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// [2]          RPC_PPM_DISPLAY deprecated [AG] May 2019    
-/////////////////////////////////////////////////////////////////////////////////////////
-
-/*
-/////////////////////////////////////////
-void rpc_ppm_display_client( cxy_t  cxy )
-{
-#if DEBUG_RPC_PPM_DISPLAY
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_PPM_DISPLAY )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-
-    uint32_t responses = 1;
-
-    // initialise RPC descriptor header 
-    rpc_desc_t  rpc;
-    rpc.index    = RPC_PPM_DISPLAY;
-    rpc.blocking = true;
-    rpc.rsp      = &responses;
-
-    // register RPC request in remote RPC fifo
-    rpc_send( cxy , &rpc );
-
-#if DEBUG_RPC_PPM_DISPLAY
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_PPM_DISPLAY )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
-#endif
-}
-
-////////////////////////////////////////////////////////////////////
-void rpc_ppm_display_server( xptr_t __attribute__((__unused__)) xp )
-{
-#if DEBUG_RPC_PPM_DISPLAY
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_PPM_DISPLAY )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-
-    // call local kernel function
-    ppm_display();
-
-#if DEBUG_RPC_PPM_DISPLAY
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_PPM_DISPLAY )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
-#endif
-}
-*/
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// [3]           Marshaling functions attached to RPC_PROCESS_MAKE_FORK 
+/***************************************************************************************/
+/************ Marshaling functions ordered by increasing index *************************/
+/***************************************************************************************/
+
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// [0]           undefined                                               
+/////////////////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// [1]           undefined                                               
+/////////////////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// [2]           undefined                                               
+/////////////////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// [3]           Marshaling function attached to RPC_MAKE_FORK           
 /////////////////////////////////////////////////////////////////////////////////////////
 
@@ -1073,87 +863,6 @@
 
 /////////////////////////////////////////////////////////////////////////////////////////
-// [8]   Marshaling functions attached to RPC_VFS_FS_UPDATE_DENTRY
-/////////////////////////////////////////////////////////////////////////////////////////
-
-/////////////////////////////////////////////////////////
-void rpc_vfs_fs_update_dentry_client( cxy_t          cxy,
-                                      vfs_inode_t  * inode,
-                                      vfs_dentry_t * dentry,
-                                      uint32_t       size,
-                                      error_t      * error )
-{
-#if DEBUG_RPC_VFS_FS_UPDATE_DENTRY
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_FS_UPDATE_DENTRY )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-
-    uint32_t responses = 1;
-
-    // initialise RPC descriptor header 
-    rpc_desc_t  rpc;
-    rpc.index    = RPC_VFS_FS_UPDATE_DENTRY;
-    rpc.blocking = true;
-    rpc.rsp      = &responses;
-
-    // set input arguments in RPC descriptor 
-    rpc.args[0] = (uint64_t)(intptr_t)inode;
-    rpc.args[1] = (uint64_t)(intptr_t)dentry;
-    rpc.args[2] = (uint64_t)size;
-
-    // register RPC request in remote RPC fifo
-    rpc_send( cxy , &rpc );
-
-    // get output values from RPC descriptor
-    *error   = (error_t)rpc.args[3];
-
-#if DEBUG_RPC_VFS_FS_UPDATE_DENTRY
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_FS_UPDATE_DENTRY )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-}
-
-/////////////////////////////////////////////////
-void rpc_vfs_fs_update_dentry_server( xptr_t xp )
-{
-#if DEBUG_RPC_VFS_FS_UPDATE_DENTRY
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_FS_UPDATE_DENTRY )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-
-    error_t        error;
-    vfs_inode_t  * inode;
-    vfs_dentry_t * dentry;
-    uint32_t       size;
-
-    // get client cluster identifier and pointer on RPC descriptor
-    cxy_t        client_cxy  = GET_CXY( xp );
-    rpc_desc_t * desc        = GET_PTR( xp );
-
-    // get input arguments
-    inode  = (vfs_inode_t*)(intptr_t) hal_remote_l64(XPTR(client_cxy , &desc->args[0]));
-    dentry = (vfs_dentry_t*)(intptr_t)hal_remote_l64(XPTR(client_cxy , &desc->args[1]));
-    size   = (uint32_t)               hal_remote_l64(XPTR(client_cxy , &desc->args[2]));
-
-    // call the kernel function
-    error = vfs_fs_update_dentry( inode , dentry , size );
-
-    // set output argument
-    hal_remote_s64( XPTR( client_cxy , &desc->args[3] ) , (uint64_t)error );
-
-#if DEBUG_RPC_VFS_FS_UPDATE_DENTRY
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_FS_UPDATE_DENTRY )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-}
+// [8]      undefined
+/////////////////////////////////////////////////////////////////////////////////////////
 
 /////////////////////////////////////////////////////////////////////////////////////////
@@ -1197,5 +906,5 @@
 process_action_str( action ), pid, cycle );
 #endif
-}  // end rpc_process_sigaction_client() 
+}
 
 //////////////////////////////////////////////
@@ -1241,1149 +950,28 @@
 process_action_str( action ), pid, cycle );
 #endif
-} // end rpc_process_sigaction_server() 
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// [10]     Marshaling functions attached to RPC_VFS_INODE_CREATE 
-/////////////////////////////////////////////////////////////////////////////////////////
-
-/////////////////////////////////////////////////////
-void rpc_vfs_inode_create_client( cxy_t          cxy,     
-                                  uint32_t       fs_type,    // in
-                                  uint32_t       attr,       // in
-                                  uint32_t       rights,     // in
-                                  uint32_t       uid,        // in
-                                  uint32_t       gid,        // in
-                                  xptr_t       * inode_xp,   // out
-                                  error_t      * error )     // out
-{
-#if DEBUG_RPC_VFS_INODE_CREATE
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_INODE_CREATE )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-
-    uint32_t responses = 1;
-
-    // initialise RPC descriptor header 
-    rpc_desc_t  rpc;
-    rpc.index    = RPC_VFS_INODE_CREATE;
-    rpc.blocking = true;
-    rpc.rsp      = &responses;
-
-    // set input arguments in RPC descriptor 
-    rpc.args[0] = (uint64_t)fs_type;
-    rpc.args[1] = (uint64_t)attr;
-    rpc.args[2] = (uint64_t)rights;
-    rpc.args[3] = (uint64_t)uid;
-    rpc.args[4] = (uint64_t)gid;
-
-    // register RPC request in remote RPC fifo
-    rpc_send( cxy , &rpc );
-
-    // get output values from RPC descriptor
-    *inode_xp = (xptr_t)rpc.args[5];
-    *error    = (error_t)rpc.args[6];
-
-#if DEBUG_RPC_VFS_INODE_CREATE
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_INODE_CREATE )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
-#endif
-}
-
-/////////////////////////////////////////////
-void rpc_vfs_inode_create_server( xptr_t xp )
-{
-#if DEBUG_RPC_VFS_INODE_CREATE
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_INODE_CREATE )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
-#endif
-
-    uint32_t         fs_type;
-    uint32_t         attr;
-    uint32_t         rights;
-    uint32_t         uid;
-    uint32_t         gid;
-    xptr_t           inode_xp;
-    error_t          error;
-
-    // get client cluster identifier and pointer on RPC descriptor
-    cxy_t        client_cxy  = GET_CXY( xp );
-    rpc_desc_t * desc        = GET_PTR( xp );
-
-    // get input arguments from client rpc descriptor
-    fs_type    = (uint32_t)  hal_remote_l64( XPTR( client_cxy , &desc->args[0] ) );
-    attr       = (uint32_t)  hal_remote_l64( XPTR( client_cxy , &desc->args[1] ) );
-    rights     = (uint32_t)  hal_remote_l64( XPTR( client_cxy , &desc->args[2] ) );
-    uid        = (uid_t)     hal_remote_l64( XPTR( client_cxy , &desc->args[3] ) );
-    gid        = (gid_t)     hal_remote_l64( XPTR( client_cxy , &desc->args[4] ) );
-
-    // call local kernel function
-    error = vfs_inode_create( fs_type,
-                              attr,
-                              rights,
-                              uid,
-                              gid,
-                              &inode_xp );
-
-    // set output arguments
-    hal_remote_s64( XPTR( client_cxy , &desc->args[5] ) , (uint64_t)inode_xp );
-    hal_remote_s64( XPTR( client_cxy , &desc->args[6] ) , (uint64_t)error );
-
-#if DEBUG_RPC_VFS_INODE_CREATE
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_INODE_CREATE )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
-#endif
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// [11]          Marshaling functions attached to RPC_VFS_INODE_DESTROY 
-/////////////////////////////////////////////////////////////////////////////////////////
-
-/////////////////////////////////////////////////////////////
-void rpc_vfs_inode_destroy_client( cxy_t                cxy,
-                                   struct vfs_inode_s * inode )
-{
-#if DEBUG_RPC_VFS_INODE_DESTROY
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_INODE_DESTROY )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
-#endif
-
-    uint32_t responses = 1;
-
-    // initialise RPC descriptor header 
-    rpc_desc_t  rpc;
-    rpc.index    = RPC_VFS_INODE_DESTROY;
-    rpc.blocking = true;
-    rpc.rsp      = &responses;
-
-    // set input arguments in RPC descriptor 
-    rpc.args[0] = (uint64_t)(intptr_t)inode;
-    
-    // register RPC request in remote RPC fifo
-    rpc_send( cxy , &rpc );
-
-#if DEBUG_RPC_VFS_INODE_DESTROY
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_INODE_DESTROY )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
-#endif
-}
-
-//////////////////////////////////////////////
-void rpc_vfs_inode_destroy_server( xptr_t xp )
-{
-#if DEBUG_RPC_VFS_INODE_DESTROY
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_INODE_DESTROY )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
-#endif
-
-    vfs_inode_t * inode;
-
-    // get client cluster identifier and pointer on RPC descriptor
-    cxy_t        client_cxy  = GET_CXY( xp );
-    rpc_desc_t * desc        = GET_PTR( xp );
-
-    // get argument "inode" from client RPC descriptor
-    inode = (vfs_inode_t *)(intptr_t)hal_remote_l64( XPTR( client_cxy , &desc->args[0] ) );
-                       
-    // call local kernel function
-    vfs_inode_destroy( inode );
-
-#if DEBUG_RPC_VFS_INODE_DESTROY
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_INODE_DESTROY )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
-#endif
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// [12]          Marshaling functions attached to RPC_VFS_DENTRY_CREATE 
-/////////////////////////////////////////////////////////////////////////////////////////
-
-//////////////////////////////////////////////////////////////
-void rpc_vfs_dentry_create_client( cxy_t                  cxy,
-                                   uint32_t               type,         // in
-                                   char                 * name,         // in
-                                   xptr_t               * dentry_xp,    // out
-                                   error_t              * error )       // out
-{
-#if DEBUG_RPC_VFS_DENTRY_CREATE
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_DENTRY_CREATE )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
-#endif
-
-    uint32_t responses = 1;
-
-    // initialise RPC descriptor header 
-    rpc_desc_t  rpc;
-    rpc.index    = RPC_VFS_DENTRY_CREATE;
-    rpc.blocking = true;
-    rpc.rsp      = &responses;
-
-    // set input arguments in RPC descriptor 
-    rpc.args[0] = (uint64_t)type;
-    rpc.args[1] = (uint64_t)(intptr_t)name;
-
-    // register RPC request in remote RPC fifo
-    rpc_send( cxy , &rpc );
-
-    // get output values from RPC descriptor
-    *dentry_xp = (xptr_t)rpc.args[2];
-    *error     = (error_t)rpc.args[3];
-
-#if DEBUG_RPC_VFS_DENTRY_CREATE
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_DENTRY_CREATE )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
-#endif
-}
-
-//////////////////////////////////////////////
-void rpc_vfs_dentry_create_server( xptr_t xp )
-{
-#if DEBUG_RPC_VFS_DENTRY_CREATE
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_DENTRY_CREATE )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-
-    uint32_t      type;
-    char        * name;
-    xptr_t        dentry_xp;
-    error_t       error;
-    char          name_copy[CONFIG_VFS_MAX_NAME_LENGTH];
-
-    // get client cluster identifier and pointer on RPC descriptor
-    cxy_t        client_cxy  = GET_CXY( xp );
-    rpc_desc_t * desc        = GET_PTR( xp );
-
-    // get arguments "name", "type", and "parent" from client RPC descriptor
-    type   = (uint32_t)         hal_remote_l64( XPTR( client_cxy , &desc->args[0] ) );
-    name   = (char *)(intptr_t) hal_remote_l64( XPTR( client_cxy , &desc->args[1] ) );
-
-    // makes a local copy of  name
-    hal_remote_strcpy( XPTR( local_cxy , name_copy ),
-                       XPTR( client_cxy , name ) );
-
-    // call local kernel function
-    error = vfs_dentry_create( type,
-                               name_copy,
-                               &dentry_xp );
-    // set output arguments
-    hal_remote_s64( XPTR( client_cxy , &desc->args[2] ) , (uint64_t)dentry_xp );
-    hal_remote_s64( XPTR( client_cxy , &desc->args[3] ) , (uint64_t)error );
-
-#if DEBUG_RPC_VFS_DENTRY_CREATE
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_DENTRY_CREATE )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
-#endif
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// [13]          Marshaling functions attached to RPC_VFS_DENTRY_DESTROY 
-/////////////////////////////////////////////////////////////////////////////////////////
-
-///////////////////////////////////////////////////////
-void rpc_vfs_dentry_destroy_client( cxy_t          cxy,
-                                    vfs_dentry_t * dentry )
-{
-#if DEBUG_RPC_VFS_DENTRY_DESTROY
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_DENTRY_DESTROY )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-
-    uint32_t responses = 1;
-
-    // initialise RPC descriptor header 
-    rpc_desc_t  rpc;
-    rpc.index    = RPC_VFS_DENTRY_DESTROY;
-    rpc.blocking = true;
-    rpc.rsp      = &responses;
-
-    // set input arguments in RPC descriptor 
-    rpc.args[0] = (uint64_t)(intptr_t)dentry;
-    
-    // register RPC request in remote RPC fifo
-    rpc_send( cxy , &rpc );
-
-#if DEBUG_RPC_VFS_DENTRY_DESTROY
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_DENTRY_DESTROY )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
-#endif
-}
-
-///////////////////////////////////////////////
-void rpc_vfs_dentry_destroy_server( xptr_t xp )
-{
-#if DEBUG_RPC_VFS_DENTRY_DESTROY
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_DENTRY_DESTROY )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-
-    vfs_dentry_t * dentry;
-
-    // get client cluster identifier and pointer on RPC descriptor
-    cxy_t        client_cxy  = GET_CXY( xp );
-    rpc_desc_t * desc        = GET_PTR( xp );
-
-    // get arguments "dentry" from client RPC descriptor
-    dentry = (vfs_dentry_t *)(intptr_t)hal_remote_l64( XPTR( client_cxy , &desc->args[0] ) );
-                       
-    // call local kernel function
-    vfs_dentry_destroy( dentry );
-
-#if DEBUG_RPC_VFS_DENTRY_DESTROY
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_DENTRY_DESTROY )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
-#endif
-}
-
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// [14]          Marshaling functions attached to RPC_VFS_FILE_CREATE  
-/////////////////////////////////////////////////////////////////////////////////////////
-
-//////////////////////////////////////////////////////////////
-void rpc_vfs_file_create_client( cxy_t                  cxy,
-                                 struct vfs_inode_s   * inode,       // in
-                                 uint32_t               file_attr,   // in
-                                 xptr_t               * file_xp,     // out
-                                 error_t              * error )      // out
-{
-#if DEBUG_RPC_VFS_FILE_CREATE
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_FILE_CREATE )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-
-    uint32_t responses = 1;
-
-    // initialise RPC descriptor header 
-    rpc_desc_t  rpc;
-    rpc.index    = RPC_VFS_FILE_CREATE;
-    rpc.blocking = true;
-    rpc.rsp      = &responses;
-
-    // set input arguments in RPC descriptor 
-    rpc.args[0] = (uint64_t)(intptr_t)inode;
-    rpc.args[1] = (uint64_t)file_attr;
-
-    // register RPC request in remote RPC fifo
-    rpc_send( cxy , &rpc );
-
-    // get output values from RPC descriptor
-    *file_xp = (xptr_t)rpc.args[2];
-    *error   = (error_t)rpc.args[3];
-
-#if DEBUG_RPC_VFS_FILE_CREATE
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_FILE_CREATE )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
-#endif
-}
-
-////////////////////////////////////////////
-void rpc_vfs_file_create_server( xptr_t xp )
-{
-#if DEBUG_RPC_VFS_FILE_CREATE
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_FILE_CREATE )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-
-    uint32_t      file_attr;
-    vfs_inode_t * inode;
-    xptr_t        file_xp;
-    error_t       error;
-
-    // get client cluster identifier and pointer on RPC descriptor
-    cxy_t        client_cxy  = GET_CXY( xp );
-    rpc_desc_t * desc        = GET_PTR( xp );
-
-    // get arguments "file_attr" and "inode" from client RPC descriptor
-    inode     = (vfs_inode_t *)(intptr_t)hal_remote_l64( XPTR( client_cxy , &desc->args[0] ) );
-    file_attr = (uint32_t)               hal_remote_l64( XPTR( client_cxy , &desc->args[1] ) );
-                       
-    // call local kernel function
-    error = vfs_file_create( inode,
-                             file_attr,
-                             &file_xp );
- 
-    // set output arguments
-    hal_remote_s64( XPTR( client_cxy , &desc->args[2] ) , (uint64_t)file_xp );
-    hal_remote_s64( XPTR( client_cxy , &desc->args[3] ) , (uint64_t)error );
-
-#if DEBUG_RPC_VFS_FILE_CREATE
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_FILE_CREATE )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
-#endif
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// [15]          Marshaling functions attached to RPC_VFS_FILE_DESTROY  
-/////////////////////////////////////////////////////////////////////////////////////////
-
-///////////////////////////////////////////////////
-void rpc_vfs_file_destroy_client( cxy_t        cxy,
-                                  vfs_file_t * file )
-{
-#if DEBUG_RPC_VFS_FILE_DESTROY
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_FILE_DESTROY )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-
-    uint32_t responses = 1;
-
-    // initialise RPC descriptor header 
-    rpc_desc_t  rpc;
-    rpc.index    = RPC_VFS_FILE_DESTROY;
-    rpc.blocking = true;
-    rpc.rsp      = &responses;
-
-    // set input arguments in RPC descriptor 
-    rpc.args[0] = (uint64_t)(intptr_t)file;
-    
-    // register RPC request in remote RPC fifo
-    rpc_send( cxy , &rpc );
-
-#if DEBUG_RPC_VFS_FILE_DESTROY
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_FILE_DESTROY )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
-#endif
-}
-
-/////////////////////////////////////////////
-void rpc_vfs_file_destroy_server( xptr_t xp )
-{
-#if DEBUG_RPC_VFS_FILE_DESTROY
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_FILE_DESTROY )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-
-    vfs_file_t * file;
-
-    // get client cluster identifier and pointer on RPC descriptor
-    cxy_t        client_cxy  = GET_CXY( xp );
-    rpc_desc_t * desc        = GET_PTR( xp );
-
-    // get arguments "dentry" from client RPC descriptor
-    file = (vfs_file_t *)(intptr_t)hal_remote_l64( XPTR( client_cxy , &desc->args[0] ) );
-                       
-    // call local kernel function
-    vfs_file_destroy( file );
-
-#if DEBUG_RPC_VFS_FILE_DESTROY
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_FILE_DESTROY )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
-#endif
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// [16]      Marshaling functions attached to RPC_VFS_FS_GET_DENTRY 
-/////////////////////////////////////////////////////////////////////////////////////////
-
-/////////////////////////////////////////////////////////
-void rpc_vfs_fs_new_dentry_client( cxy_t         cxy,
-                                   vfs_inode_t * parent_inode,    // in
-                                   char        * name,            // in
-                                   xptr_t        child_inode_xp,  // in
-                                   error_t     * error )          // out
-{
-#if DEBUG_RPC_VFS_FS_NEW_DENTRY
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_FS_NEW_DENTRY )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-
-    uint32_t responses = 1;
-
-    // initialise RPC descriptor header 
-    rpc_desc_t  rpc;
-    rpc.index    = RPC_VFS_FS_NEW_DENTRY;
-    rpc.blocking = true;
-    rpc.rsp      = &responses;
-
-    // set input arguments in RPC descriptor 
-    rpc.args[0] = (uint64_t)(intptr_t)parent_inode;
-    rpc.args[1] = (uint64_t)(intptr_t)name;
-    rpc.args[2] = (uint64_t)child_inode_xp;
-
-    // register RPC request in remote RPC fifo
-    rpc_send( cxy , &rpc );
-
-    // get output values from RPC descriptor
-    *error   = (error_t)rpc.args[3];
-
-#if DEBUG_RPC_VFS_FS_NEW_DENTRY
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_FS_NEW_DENTRY )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-}
-
-//////////////////////////////////////////////
-void rpc_vfs_fs_new_dentry_server( xptr_t xp )
-{
-#if DEBUG_RPC_VFS_FS_NEW_DENTRY
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_FS_NEW_DENTRY )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-
-    error_t       error;
-    vfs_inode_t * parent;
-    xptr_t        child_xp;
-    char        * name;
-
-    char          name_copy[CONFIG_VFS_MAX_NAME_LENGTH];
-
-    // get client cluster identifier and pointer on RPC descriptor
-    cxy_t        client_cxy  = GET_CXY( xp );
-    rpc_desc_t * desc        = GET_PTR( xp );
-
-    // get arguments "parent", "name", and "child_xp"
-    parent     = (vfs_inode_t*)(intptr_t)hal_remote_l64(XPTR(client_cxy , &desc->args[0]));
-    name       = (char*)(intptr_t)       hal_remote_l64(XPTR(client_cxy , &desc->args[1]));
-    child_xp   = (xptr_t)                hal_remote_l64(XPTR(client_cxy , &desc->args[2]));
-
-    // get name local copy
-    hal_remote_strcpy( XPTR( local_cxy , name_copy ) ,
-                       XPTR( client_cxy , name ) );
-
-    // call the kernel function
-    error = vfs_fs_new_dentry( parent , name_copy , child_xp );
-
-    // set output argument
-    hal_remote_s64( XPTR( client_cxy , &desc->args[3] ) , (uint64_t)error );
-
-#if DEBUG_RPC_VFS_FS_NEW_DENTRY
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_FS_NEW_DENTRY )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// [17]      Marshaling function attached to RPC_VFS_FS_ADD_DENTRY  
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void rpc_vfs_fs_add_dentry_client( cxy_t          cxy,
-                                   vfs_inode_t  * parent,     // in
-                                   vfs_dentry_t * dentry,     // in
-                                   error_t      * error )     // out
-{
-#if DEBUG_RPC_VFS_FS_ADD_DENTRY
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_FS_ADD_DENTRY )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-
-    uint32_t responses = 1;
-
-    // initialise RPC descriptor header 
-    rpc_desc_t  rpc;
-    rpc.index    = RPC_VFS_FS_ADD_DENTRY;
-    rpc.blocking = true;
-    rpc.rsp      = &responses;
-
-    // set input arguments in RPC descriptor 
-    rpc.args[0] = (uint64_t)(intptr_t)parent;
-    rpc.args[1] = (uint64_t)(intptr_t)dentry;
-
-    // register RPC request in remote RPC fifo
-    rpc_send( cxy , &rpc );
-
-    // get output values from RPC descriptor
-    *error   = (error_t)rpc.args[2];
-
-#if DEBUG_RPC_VFS_FS_ADD_DENTRY
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_FS_ADD_DENTRY )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-}
-
-//////////////////////////////////////////////
-void rpc_vfs_fs_add_dentry_server( xptr_t xp )
-{
-#if DEBUG_RPC_VFS_FS_ADD_DENTRY
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_FS_ADD_DENTRY )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-
-    error_t        error;
-    vfs_inode_t  * parent;
-    vfs_dentry_t * dentry;
-
-    // get client cluster identifier and pointer on RPC descriptor
-    cxy_t        client_cxy  = GET_CXY( xp );
-    rpc_desc_t * desc        = GET_PTR( xp );
-
-    // get input arguments
-    parent = (vfs_inode_t*)(intptr_t) hal_remote_l64(XPTR(client_cxy , &desc->args[0]));
-    dentry = (vfs_dentry_t*)(intptr_t)hal_remote_l64(XPTR(client_cxy , &desc->args[1]));
-
-    // call the kernel function
-    error = vfs_fs_add_dentry( parent , dentry );
-
-    // set output argument
-    hal_remote_s64( XPTR( client_cxy , &desc->args[2] ) , (uint64_t)error );
-
-#if DEBUG_RPC_VFS_FS_ADD_DENTRY
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_FS_ADD_DENTRY )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// [18]      Marshaling function attached to RPC_VFS_FS_REMOVE_DENTRY 
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void rpc_vfs_fs_remove_dentry_client( cxy_t          cxy,
-                                      vfs_inode_t  * parent,     // in
-                                      vfs_dentry_t * dentry,     // in
-                                      error_t      * error )     // out
-{
-#if DEBUG_RPC_VFS_FS_REMOVE_DENTRY
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_FS_REMOVE_DENTRY )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-
-    uint32_t responses = 1;
-
-    // initialise RPC descriptor header 
-    rpc_desc_t  rpc;
-    rpc.index    = RPC_VFS_FS_REMOVE_DENTRY;
-    rpc.blocking = true;
-    rpc.rsp      = &responses;
-
-    // set input arguments in RPC descriptor 
-    rpc.args[0] = (uint64_t)(intptr_t)parent;
-    rpc.args[1] = (uint64_t)(intptr_t)dentry;
-
-    // register RPC request in remote RPC fifo
-    rpc_send( cxy , &rpc );
-
-    // get output values from RPC descriptor
-    *error   = (error_t)rpc.args[2];
-
-#if DEBUG_RPC_VFS_FS_REMOVE_DENTRY
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_FS_REMOVE_DENTRY )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-}
-
-/////////////////////////////////////////////////
-void rpc_vfs_fs_remove_dentry_server( xptr_t xp )
-{
-#if DEBUG_RPC_VFS_FS_REMOVE_DENTRY
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_FS_REMOVE_DENTRY )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-
-    error_t        error;
-    vfs_inode_t  * parent;
-    vfs_dentry_t * dentry;
-
-    // get client cluster identifier and pointer on RPC descriptor
-    cxy_t        client_cxy  = GET_CXY( xp );
-    rpc_desc_t * desc        = GET_PTR( xp );
-
-    // get input arguments
-    parent = (vfs_inode_t*)(intptr_t) hal_remote_l64(XPTR(client_cxy , &desc->args[0]));
-    dentry = (vfs_dentry_t*)(intptr_t)hal_remote_l64(XPTR(client_cxy , &desc->args[1]));
-
-    // call the kernel function
-    error = vfs_fs_remove_dentry( parent , dentry );
-
-    // set output argument
-    hal_remote_s64( XPTR( client_cxy , &desc->args[2] ) , (uint64_t)error );
-
-#if DEBUG_RPC_VFS_FS_REMOVE_DENTRY
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_FS_REMOVE_DENTRY )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// [19]     Marshaling functions attached to RPC_VFS_INODE_LOAD_ALL_PAGES 
-/////////////////////////////////////////////////////////////////////////////////////////
-
-////////////////////////////////////////////////////////////
-void rpc_vfs_inode_load_all_pages_client( cxy_t         cxy,
-                                          vfs_inode_t * inode,      // in
-                                          error_t     * error )     // out
-{
-#if DEBUG_RPC_VFS_INODE_LOAD_ALL_PAGES
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_INODE_LOAD_ALL_PAGES )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-
-    uint32_t responses = 1;
-
-    // initialise RPC descriptor header 
-    rpc_desc_t  rpc;
-    rpc.index    = RPC_VFS_INODE_LOAD_ALL_PAGES;
-    rpc.blocking = true;
-    rpc.rsp      = &responses;
-
-    // set input arguments in RPC descriptor 
-    rpc.args[0] = (uint64_t)(intptr_t)inode;
-
-    // register RPC request in remote RPC fifo
-    rpc_send( cxy , &rpc );
-
-    // get output values from RPC descriptor
-    *error   = (error_t)rpc.args[1];
-
-#if DEBUG_RPC_VFS_INODE_LOAD_ALL_PAGES
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_INODE_LOAD_ALL_PAGES )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-}
-
-/////////////////////////////////////////////////////
-void rpc_vfs_inode_load_all_pages_server( xptr_t xp )
-{
-#if DEBUG_RPC_VFS_INODE_LOAD_ALL_PAGES
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_INODE_LOAD_ALL_PAGES )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-
-    error_t       error;
-    vfs_inode_t * inode;
-
-    // get client cluster identifier and pointer on RPC descriptor
-    cxy_t        client_cxy  = GET_CXY( xp );
-    rpc_desc_t * desc        = GET_PTR( xp );
-
-    // get input argument
-    inode = (vfs_inode_t*)(intptr_t)hal_remote_l64(XPTR(client_cxy , &desc->args[0]));
-
-    // call the kernel function
-    error = vfs_inode_load_all_pages( inode );
-
-    // set output argument
-    hal_remote_s64( XPTR( client_cxy , &desc->args[1] ) , (uint64_t)error );
-
-#if DEBUG_RPC_VFS_INODE_LOAD_ALL_PAGES
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VFS_INODE_LOAD_ALL_PAGES )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// [20]          RPC_VMM_GET_VSEG deprecated [AG] sept 2019
-/////////////////////////////////////////////////////////////////////////////////////////
-
-/*
-//////////////////////////////////////////////////
-void rpc_vmm_get_vseg_client( cxy_t       cxy,      
-                              process_t * process,     // in  
-                              intptr_t    vaddr,       // in  
-                              xptr_t    * vseg_xp,     // out
-                              error_t   * error )      // out
-{
-#if DEBUG_RPC_VMM_GET_VSEG
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VMM_GET_VSEG )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-
-    uint32_t responses = 1;
-
-    // initialise RPC descriptor header 
-    rpc_desc_t  rpc;
-    rpc.index    = RPC_VMM_GET_VSEG;
-    rpc.blocking = true;
-    rpc.rsp      = &responses;
-
-    // set input arguments in RPC descriptor 
-    rpc.args[0] = (uint64_t)(intptr_t)process;
-    rpc.args[1] = (uint64_t)vaddr;
-
-    // register RPC request in remote RPC fifo
-    rpc_send( cxy , &rpc );
-
-    // get output argument from rpc descriptor
-    *vseg_xp = rpc.args[2];
-    *error   = (error_t)rpc.args[3];
-
-#if DEBUG_RPC_VMM_GET_VSEG
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VMM_GET_VSEG )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-}
-
-/////////////////////////////////////////
-void rpc_vmm_get_vseg_server( xptr_t xp )
-{
-#if DEBUG_RPC_VMM_GET_VSEG
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VMM_GET_VSEG )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-
-    process_t   * process;
-    intptr_t      vaddr;
-    vseg_t      * vseg_ptr;
-    xptr_t        vseg_xp;
-    error_t       error;
-
-    // get client cluster identifier and pointer on RPC descriptor
-    cxy_t        client_cxy  = GET_CXY( xp );
-    rpc_desc_t * desc        = GET_PTR( xp );
-
-    // get input argument from client RPC descriptor
-    process = (process_t *)(intptr_t)hal_remote_l64( XPTR( client_cxy , &desc->args[0] ) );
-    vaddr   = (intptr_t)hal_remote_l64( XPTR( client_cxy , &desc->args[1] ) );
-    
-    // call local kernel function
-    error = vmm_get_vseg( process , vaddr , &vseg_ptr );
-
-    // set output arguments to client RPC descriptor
-    vseg_xp = XPTR( local_cxy , vseg_ptr );
-    hal_remote_s64( XPTR( client_cxy , &desc->args[2] ) , (uint64_t)vseg_xp );
-    hal_remote_s64( XPTR( client_cxy , &desc->args[3] ) , (uint64_t)error );
-
-#if DEBUG_RPC_VMM_GET_VSEG
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_VMM_GET_VSEG )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-}
-*/
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// [21]    undefined
-/////////////////////////////////////////////////////////////////////////////////////////
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// [22]          RPC_KCM_ALLOC deprecated [AG] sept 2019
-/////////////////////////////////////////////////////////////////////////////////////////
-
-/*
-//////////////////////////////////////////
-void rpc_kcm_alloc_client( cxy_t      cxy,
-                           uint32_t   kmem_type,   // in
-                           xptr_t   * buf_xp )     // out
-{
-#if DEBUG_RPC_KCM_ALLOC
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_KCM_ALLOC )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-
-    uint32_t responses = 1;
-
-    // initialise RPC descriptor header 
-    rpc_desc_t  rpc;
-    rpc.index    = RPC_KCM_ALLOC;
-    rpc.blocking = true;
-    rpc.rsp      = &responses;
-
-    // set input arguments in RPC descriptor 
-    rpc.args[0] = (uint64_t)kmem_type;
-
-    // register RPC request in remote RPC fifo
-    rpc_send( cxy , &rpc );
-
-    // get output arguments from RPC descriptor
-    *buf_xp = (xptr_t)rpc.args[1];
-
-#if DEBUG_RPC_KCM_ALLOC
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_KCM_ALLOC )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-}
-
-//////////////////////////////////////
-void rpc_kcm_alloc_server( xptr_t xp )
-{
-#if DEBUG_RPC_KCM_ALLOC
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_KCM_ALLOC )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-
-    // get client cluster identifier and pointer on RPC descriptor
-    cxy_t        client_cxy  = GET_CXY( xp );
-    rpc_desc_t * desc        = GET_PTR( xp );
-
-    // get input argument "kmem_type" from client RPC descriptor
-    uint32_t kmem_type = (uint32_t)hal_remote_l64( XPTR( client_cxy , &desc->args[0] ) );
-
-    // allocates memory for kcm
-    kmem_req_t  req;
-    req.type  = kmem_type;
-    req.flags = AF_ZERO;
-    void * buf_ptr = kmem_alloc( &req );
-
-    // set output argument
-    xptr_t buf_xp = XPTR( local_cxy , buf_ptr );
-    hal_remote_s64( XPTR( client_cxy , &desc->args[1] ) , (uint64_t)buf_xp );
-
-#if DEBUG_RPC_KCM_ALLOC
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_KCM_ALLOC )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-}   
-*/
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// [23]          RPC_KCM_FREE deprecated [AG] sept 2019
-/////////////////////////////////////////////////////////////////////////////////////////
-
-/*
-/////////////////////////////////////////
-void rpc_kcm_free_client( cxy_t      cxy,
-                          void     * buf,          // in
-                          uint32_t   kmem_type )   // in
-{
-#if DEBUG_RPC_KCM_FREE
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_KCM_FREE )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-
-    uint32_t responses = 1;
-
-    // initialise RPC descriptor header 
-    rpc_desc_t  rpc;
-    rpc.index    = RPC_KCM_FREE;
-    rpc.blocking = true;
-    rpc.rsp      = &responses;
-
-    // set input arguments in RPC descriptor 
-    rpc.args[0] = (uint64_t)(intptr_t)buf;
-    rpc.args[1] = (uint64_t)kmem_type;
-
-    // register RPC request in remote RPC fifo
-    rpc_send( cxy , &rpc );
-
-#if DEBUG_RPC_KCM_FREE
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_KCM_FREE )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-}
-
-/////////////////////////////////////
-void rpc_kcm_free_server( xptr_t xp )
-{
-#if DEBUG_RPC_KCM_FREE
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_KCM_FREE )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-
-    // get client cluster identifier and pointer on RPC descriptor
-    cxy_t        client_cxy  = GET_CXY( xp );
-    rpc_desc_t * desc        = GET_PTR( xp );
-
-    // get input arguments "buf" and "kmem_type" from client RPC descriptor
-    void     * buf = (void *)(intptr_t)hal_remote_l64( XPTR( client_cxy , &desc->args[0] ) );
-    uint32_t   kmem_type = (uint32_t)hal_remote_l64( XPTR( client_cxy , &desc->args[1] ) );
-
-    // releases memory
-    kmem_req_t  req;
-    req.type = kmem_type;
-    req.ptr  = buf;
-    kmem_free( &req );
-
-#if DEBUG_RPC_KCM_FREE
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_KCM_FREE )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-}   
-*/
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// [24]          Marshaling functions attached to RPC_MAPPER_SYNC 
-/////////////////////////////////////////////////////////////////////////////////////////
-
-///////////////////////////////////////////////////
-void rpc_mapper_sync_client( cxy_t             cxy,
-                             struct mapper_s * mapper,
-                             error_t         * error )
-{
-#if DEBUG_RPC_MAPPER_SYNC
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_MAPPER_SYNC )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-
-    uint32_t responses = 1;
-
-    // initialise RPC descriptor header 
-    rpc_desc_t  rpc;
-    rpc.index    = RPC_MAPPER_SYNC;
-    rpc.blocking = true;
-    rpc.rsp      = &responses;
-
-    // set input arguments in RPC descriptor 
-    rpc.args[0] = (uint64_t)(intptr_t)mapper;
-
-    // register RPC request in remote RPC fifo
-    rpc_send( cxy , &rpc );
-
-    // get output values from RPC descriptor
-    *error   = (error_t)rpc.args[1];
-
-#if DEBUG_RPC_MAPPER_SYNC
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_MAPPER_SYNC )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-}
-
-////////////////////////////////////////
-void rpc_mapper_sync_server( xptr_t xp )
-{
-#if DEBUG_RPC_MAPPER_SYNC
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_MAPPER_SYNC )
-printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-
-    mapper_t * mapper;
-    error_t    error;
-
-    // get client cluster identifier and pointer on RPC descriptor
-    cxy_t        client_cxy  = GET_CXY( xp );
-    rpc_desc_t * desc        = GET_PTR( xp );
-
-    // get arguments from client RPC descriptor
-    mapper  = (mapper_t *)(intptr_t)hal_remote_l64( XPTR( client_cxy , &desc->args[0] ) );
-
-    // call local kernel function
-    error = mapper_sync( mapper );
-
-    // set output argument to client RPC descriptor
-    hal_remote_s64( XPTR( client_cxy , &desc->args[1] ) , (uint64_t)error );
-
-#if DEBUG_RPC_MAPPER_SYNC
-cycle = (uint32_t)hal_get_cycles();
-if( cycle > DEBUG_RPC_MAPPER_SYNC )
-printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
-#endif
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// [25]          Marshaling functions attached to RPC_VMM_RESIZE_VSEG 
+} 
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// [10]          undefined                                          
+/////////////////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// [11]          undefined                                          
+/////////////////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// [12]          undefined                                         
+/////////////////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// [13]          undefined                                        
+/////////////////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// [14]          undefined                                        
+/////////////////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// [15]          Marshaling functions attached to RPC_VMM_RESIZE_VSEG 
 /////////////////////////////////////////////////////////////////////////////////////////
 
@@ -2478,5 +1066,5 @@
 
 /////////////////////////////////////////////////////////////////////////////////////////
-// [26]  Marshaling functions attached to RPC_VMM_REMOVE_VSEG 
+// [16]  Marshaling functions attached to RPC_VMM_REMOVE_VSEG 
 /////////////////////////////////////////////////////////////////////////////////////////
 
@@ -2560,5 +1148,5 @@
 
 /////////////////////////////////////////////////////////////////////////////////////////
-// [27]          Marshaling functions attached to RPC_VMM_CREATE_VSEG 
+// [17]          Marshaling functions attached to RPC_VMM_CREATE_VSEG 
 /////////////////////////////////////////////////////////////////////////////////////////
 
@@ -2662,5 +1250,5 @@
 
 /////////////////////////////////////////////////////////////////////////////////////////
-// [28]          Marshaling functions attached to RPC_VMM_SET_COW 
+// [18]          Marshaling functions attached to RPC_VMM_SET_COW 
 /////////////////////////////////////////////////////////////////////////////////////////
 
@@ -2730,10 +1318,295 @@
 }
 
+
+
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+//                            DEPRECATED RPCs
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+
+/*
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// [0]       RPC_PMEM_GET_PAGES deprecated [AG] May 2019
+/////////////////////////////////////////////////////////////////////////////////////////
+void rpc_pmem_get_pages_client( cxy_t      cxy,
+                                uint32_t   order,      // in 
+                                page_t  ** page )      // out 
+{
+#if DEBUG_RPC_PMEM_GET_PAGES
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_PMEM_GET_PAGES )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
+#endif
+
+    uint32_t responses = 1;
+
+    // initialise RPC descriptor header 
+    rpc_desc_t  rpc;
+    rpc.index     = RPC_PMEM_GET_PAGES;
+    rpc.blocking  = true;
+    rpc.rsp       = &responses;
+
+    // set input arguments in RPC descriptor 
+    rpc.args[0] = (uint64_t)order;
+
+    // register RPC request in remote RPC fifo
+    rpc_send( cxy , &rpc );
+
+    // get output arguments from RPC descriptor
+    *page = (page_t *)(intptr_t)rpc.args[1];
+
+#if DEBUG_RPC_PMEM_GET_PAGES
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_PMEM_GET_PAGES )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
+#endif
+}
+
+///////////////////////////////////////////
+void rpc_pmem_get_pages_server( xptr_t xp )
+{
+#if DEBUG_RPC_PMEM_GET_PAGES
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_PMEM_GET_PAGES )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
+#endif
+
+    // get client cluster identifier and pointer on RPC descriptor
+    cxy_t        cxy  = GET_CXY( xp );
+    rpc_desc_t * desc = GET_PTR( xp );
+
+    // get input arguments from client RPC descriptor
+    uint32_t order = (uint32_t)hal_remote_l64( XPTR( cxy , &desc->args[0] ) );
+    
+    // call local pmem allocator
+    page_t * page = ppm_alloc_pages( order ); 
+
+    // set output arguments into client RPC descriptor
+    hal_remote_s64( XPTR( cxy , &desc->args[1] ) , (uint64_t)(intptr_t)page );
+
+#if DEBUG_RPC_PMEM_GET_PAGES
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_PMEM_GET_PAGES )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
+#endif
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// [1]       RPC_PMEM_RELEASE_PAGES deprecated [AG] may 2019
+/////////////////////////////////////////////////////////////////////////////////////////
+void rpc_pmem_release_pages_client( cxy_t     cxy,
+                                    page_t  * page )      // out 
+{
+#if DEBUG_RPC_PMEM_RELEASE_PAGES
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_PMEM_RELEASE_PAGES )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+
+    uint32_t responses = 1;
+
+    // initialise RPC descriptor header 
+    rpc_desc_t  rpc;
+    rpc.index    = RPC_PMEM_RELEASE_PAGES;
+    rpc.blocking = true;
+    rpc.rsp      = &responses;
+
+    // set input arguments in RPC descriptor 
+    rpc.args[0] = (uint64_t)(intptr_t)page;
+
+    // register RPC request in remote RPC fifo
+    rpc_send( cxy , &rpc );
+
+#if DEBUG_RPC_PMEM_RELEASE_PAGES
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_PMEM_RELEASE_PAGES )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
+#endif
+}
+
+///////////////////////////////////////////////
+void rpc_pmem_release_pages_server( xptr_t xp )
+{
+#if DEBUG_RPC_PMEM_RELEASE_PAGES
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_PMEM_RELEASE_PAGES )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+
+    // get client cluster identifier and pointer on RPC descriptor
+    cxy_t        cxy  = GET_CXY( xp );
+    rpc_desc_t * desc = GET_PTR( xp );
+
+    // get input arguments from client RPC descriptor
+    page_t * page = (page_t *)(intptr_t)hal_remote_l64( XPTR( cxy , &desc->args[0] ) );
+    
+    // release memory to local pmem 
+    kmem_req_t req;
+    req.type = KMEM_PPM;
+    req.ptr  = page;
+    kmem_free( &req );
+
+#if DEBUG_RPC_PMEM_RELEASE_PAGES
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_PMEM_RELEASE_PAGES )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
+#endif
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// [2]          RPC_PPM_DISPLAY deprecated [AG] May 2019    
+/////////////////////////////////////////////////////////////////////////////////////////
+void rpc_ppm_display_client( cxy_t  cxy )
+{
+#if DEBUG_RPC_PPM_DISPLAY
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_PPM_DISPLAY )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+
+    uint32_t responses = 1;
+
+    // initialise RPC descriptor header 
+    rpc_desc_t  rpc;
+    rpc.index    = RPC_PPM_DISPLAY;
+    rpc.blocking = true;
+    rpc.rsp      = &responses;
+
+    // register RPC request in remote RPC fifo
+    rpc_send( cxy , &rpc );
+
+#if DEBUG_RPC_PPM_DISPLAY
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_PPM_DISPLAY )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+void rpc_ppm_display_server( xptr_t __attribute__((__unused__)) xp )
+{
+#if DEBUG_RPC_PPM_DISPLAY
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_PPM_DISPLAY )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+
+    // call local kernel function
+    ppm_display();
+
+#if DEBUG_RPC_PPM_DISPLAY
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_PPM_DISPLAY )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
+#endif
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// [8]   RPC_VFS_FS_UPDATE_DENTRY deprecated [AG] dec 2019
+/////////////////////////////////////////////////////////////////////////////////////////
+void rpc_vfs_fs_update_dentry_client( cxy_t          cxy,
+                                      vfs_inode_t  * inode,
+                                      vfs_dentry_t * dentry,
+                                      uint32_t       size,
+                                      error_t      * error )
+{
+#if DEBUG_RPC_VFS_FS_UPDATE_DENTRY
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_FS_UPDATE_DENTRY )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+
+    uint32_t responses = 1;
+
+    // initialise RPC descriptor header 
+    rpc_desc_t  rpc;
+    rpc.index    = RPC_VFS_FS_UPDATE_DENTRY;
+    rpc.blocking = true;
+    rpc.rsp      = &responses;
+
+    // set input arguments in RPC descriptor 
+    rpc.args[0] = (uint64_t)(intptr_t)inode;
+    rpc.args[1] = (uint64_t)(intptr_t)dentry;
+    rpc.args[2] = (uint64_t)size;
+
+    // register RPC request in remote RPC fifo
+    rpc_send( cxy , &rpc );
+
+    // get output values from RPC descriptor
+    *error   = (error_t)rpc.args[3];
+
+#if DEBUG_RPC_VFS_FS_UPDATE_DENTRY
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_FS_UPDATE_DENTRY )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+}
+
+/////////////////////////////////////////////////
+void rpc_vfs_fs_update_dentry_server( xptr_t xp )
+{
+#if DEBUG_RPC_VFS_FS_UPDATE_DENTRY
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_FS_UPDATE_DENTRY )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+
+    error_t        error;
+    vfs_inode_t  * inode;
+    vfs_dentry_t * dentry;
+    uint32_t       size;
+
+    // get client cluster identifier and pointer on RPC descriptor
+    cxy_t        client_cxy  = GET_CXY( xp );
+    rpc_desc_t * desc        = GET_PTR( xp );
+
+    // get input arguments
+    inode  = (vfs_inode_t*)(intptr_t) hal_remote_l64(XPTR(client_cxy , &desc->args[0]));
+    dentry = (vfs_dentry_t*)(intptr_t)hal_remote_l64(XPTR(client_cxy , &desc->args[1]));
+    size   = (uint32_t)               hal_remote_l64(XPTR(client_cxy , &desc->args[2]));
+
+    // call the kernel function
+    error = vfs_fs_update_dentry( inode , dentry , size );
+
+    // set output argument
+    hal_remote_s64( XPTR( client_cxy , &desc->args[3] ) , (uint64_t)error );
+
+#if DEBUG_RPC_VFS_FS_UPDATE_DENTRY
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_FS_UPDATE_DENTRY )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+}
+
+
 /////////////////////////////////////////////////////////////////////////////////////////
 // [29]      RPC_VMM_DISPLAY deprecated [AG] June 2019
 /////////////////////////////////////////////////////////////////////////////////////////
-
-/*
-/////////////////////////////////////////////
 void rpc_hal_vmm_display_client( cxy_t       cxy,
                              process_t * process,
@@ -2804,3 +1677,1187 @@
 }
 
+/////////////////////////////////////////////////////////////////////////////////////////
+// [10]  to RPC_VFS_INODE_CREATE   deprecated [AG] dec 2019
+/////////////////////////////////////////////////////////////////////////////////////////
+void rpc_vfs_inode_create_client( cxy_t          cxy,     
+                                  uint32_t       fs_type,    // in
+                                  uint32_t       attr,       // in
+                                  uint32_t       rights,     // in
+                                  uint32_t       uid,        // in
+                                  uint32_t       gid,        // in
+                                  xptr_t       * inode_xp,   // out
+                                  error_t      * error )     // out
+{
+#if DEBUG_RPC_VFS_INODE_CREATE
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_INODE_CREATE )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+
+    uint32_t responses = 1;
+
+    // initialise RPC descriptor header 
+    rpc_desc_t  rpc;
+    rpc.index    = RPC_VFS_INODE_CREATE;
+    rpc.blocking = true;
+    rpc.rsp      = &responses;
+
+    // set input arguments in RPC descriptor 
+    rpc.args[0] = (uint64_t)fs_type;
+    rpc.args[1] = (uint64_t)attr;
+    rpc.args[2] = (uint64_t)rights;
+    rpc.args[3] = (uint64_t)uid;
+    rpc.args[4] = (uint64_t)gid;
+
+    // register RPC request in remote RPC fifo
+    rpc_send( cxy , &rpc );
+
+    // get output values from RPC descriptor
+    *inode_xp = (xptr_t)rpc.args[5];
+    *error    = (error_t)rpc.args[6];
+
+#if DEBUG_RPC_VFS_INODE_CREATE
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_INODE_CREATE )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
+#endif
+}
+
+/////////////////////////////////////////////
+void rpc_vfs_inode_create_server( xptr_t xp )
+{
+#if DEBUG_RPC_VFS_INODE_CREATE
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_INODE_CREATE )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
+#endif
+
+    uint32_t         fs_type;
+    uint32_t         attr;
+    uint32_t         rights;
+    uint32_t         uid;
+    uint32_t         gid;
+    xptr_t           inode_xp;
+    error_t          error;
+
+    // get client cluster identifier and pointer on RPC descriptor
+    cxy_t        client_cxy  = GET_CXY( xp );
+    rpc_desc_t * desc        = GET_PTR( xp );
+
+    // get input arguments from client rpc descriptor
+    fs_type    = (uint32_t)  hal_remote_l64( XPTR( client_cxy , &desc->args[0] ) );
+    attr       = (uint32_t)  hal_remote_l64( XPTR( client_cxy , &desc->args[1] ) );
+    rights     = (uint32_t)  hal_remote_l64( XPTR( client_cxy , &desc->args[2] ) );
+    uid        = (uid_t)     hal_remote_l64( XPTR( client_cxy , &desc->args[3] ) );
+    gid        = (gid_t)     hal_remote_l64( XPTR( client_cxy , &desc->args[4] ) );
+
+    // call local kernel function
+    error = vfs_inode_create( fs_type,
+                              attr,
+                              rights,
+                              uid,
+                              gid,
+                              &inode_xp );
+
+    // set output arguments
+    hal_remote_s64( XPTR( client_cxy , &desc->args[5] ) , (uint64_t)inode_xp );
+    hal_remote_s64( XPTR( client_cxy , &desc->args[6] ) , (uint64_t)error );
+
+#if DEBUG_RPC_VFS_INODE_CREATE
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_INODE_CREATE )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
+#endif
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// [11]  to RPC_VFS_INODE_DESTROY   deprecated [AG] dec 2019
+/////////////////////////////////////////////////////////////////////////////////////////
+void rpc_vfs_inode_destroy_client( cxy_t                cxy,
+                                   struct vfs_inode_s * inode )
+{
+#if DEBUG_RPC_VFS_INODE_DESTROY
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_INODE_DESTROY )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
+#endif
+
+    uint32_t responses = 1;
+
+    // initialise RPC descriptor header 
+    rpc_desc_t  rpc;
+    rpc.index    = RPC_VFS_INODE_DESTROY;
+    rpc.blocking = true;
+    rpc.rsp      = &responses;
+
+    // set input arguments in RPC descriptor 
+    rpc.args[0] = (uint64_t)(intptr_t)inode;
+    
+    // register RPC request in remote RPC fifo
+    rpc_send( cxy , &rpc );
+
+#if DEBUG_RPC_VFS_INODE_DESTROY
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_INODE_DESTROY )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
+#endif
+}
+
+//////////////////////////////////////////////
+void rpc_vfs_inode_destroy_server( xptr_t xp )
+{
+#if DEBUG_RPC_VFS_INODE_DESTROY
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_INODE_DESTROY )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
+#endif
+
+    vfs_inode_t * inode;
+
+    // get client cluster identifier and pointer on RPC descriptor
+    cxy_t        client_cxy  = GET_CXY( xp );
+    rpc_desc_t * desc        = GET_PTR( xp );
+
+    // get argument "inode" from client RPC descriptor
+    inode = (vfs_inode_t *)(intptr_t)hal_remote_l64( XPTR( client_cxy , &desc->args[0] ) );
+                       
+    // call local kernel function
+    vfs_inode_destroy( inode );
+
+#if DEBUG_RPC_VFS_INODE_DESTROY
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_INODE_DESTROY )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
+#endif
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// [12]  RPC_VFS_DENTRY_CREATE   deprecated [AG] dec 2019
+/////////////////////////////////////////////////////////////////////////////////////////
+void rpc_vfs_dentry_create_client( cxy_t                  cxy,
+                                   uint32_t               type,         // in
+                                   char                 * name,         // in
+                                   xptr_t               * dentry_xp,    // out
+                                   error_t              * error )       // out
+{
+#if DEBUG_RPC_VFS_DENTRY_CREATE
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_DENTRY_CREATE )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
+#endif
+
+    uint32_t responses = 1;
+
+    // initialise RPC descriptor header 
+    rpc_desc_t  rpc;
+    rpc.index    = RPC_VFS_DENTRY_CREATE;
+    rpc.blocking = true;
+    rpc.rsp      = &responses;
+
+    // set input arguments in RPC descriptor 
+    rpc.args[0] = (uint64_t)type;
+    rpc.args[1] = (uint64_t)(intptr_t)name;
+
+    // register RPC request in remote RPC fifo
+    rpc_send( cxy , &rpc );
+
+    // get output values from RPC descriptor
+    *dentry_xp = (xptr_t)rpc.args[2];
+    *error     = (error_t)rpc.args[3];
+
+#if DEBUG_RPC_VFS_DENTRY_CREATE
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_DENTRY_CREATE )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
+#endif
+}
+
+//////////////////////////////////////////////
+void rpc_vfs_dentry_create_server( xptr_t xp )
+{
+#if DEBUG_RPC_VFS_DENTRY_CREATE
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_DENTRY_CREATE )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+
+    uint32_t      type;
+    char        * name;
+    xptr_t        dentry_xp;
+    error_t       error;
+    char          name_copy[CONFIG_VFS_MAX_NAME_LENGTH];
+
+    // get client cluster identifier and pointer on RPC descriptor
+    cxy_t        client_cxy  = GET_CXY( xp );
+    rpc_desc_t * desc        = GET_PTR( xp );
+
+    // get arguments "name", "type", and "parent" from client RPC descriptor
+    type   = (uint32_t)         hal_remote_l64( XPTR( client_cxy , &desc->args[0] ) );
+    name   = (char *)(intptr_t) hal_remote_l64( XPTR( client_cxy , &desc->args[1] ) );
+
+    // makes a local copy of  name
+    hal_remote_strcpy( XPTR( local_cxy , name_copy ),
+                       XPTR( client_cxy , name ) );
+
+    // call local kernel function
+    error = vfs_dentry_create( type,
+                               name_copy,
+                               &dentry_xp );
+    // set output arguments
+    hal_remote_s64( XPTR( client_cxy , &desc->args[2] ) , (uint64_t)dentry_xp );
+    hal_remote_s64( XPTR( client_cxy , &desc->args[3] ) , (uint64_t)error );
+
+#if DEBUG_RPC_VFS_DENTRY_CREATE
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_DENTRY_CREATE )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
+#endif
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// [13]  RPC_VFS_DENTRY_DESTROY   deprecated [AG] dec 2019
+/////////////////////////////////////////////////////////////////////////////////////////
+void rpc_vfs_dentry_destroy_client( cxy_t          cxy,
+                                    vfs_dentry_t * dentry )
+{
+#if DEBUG_RPC_VFS_DENTRY_DESTROY
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_DENTRY_DESTROY )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+
+    uint32_t responses = 1;
+
+    // initialise RPC descriptor header 
+    rpc_desc_t  rpc;
+    rpc.index    = RPC_VFS_DENTRY_DESTROY;
+    rpc.blocking = true;
+    rpc.rsp      = &responses;
+
+    // set input arguments in RPC descriptor 
+    rpc.args[0] = (uint64_t)(intptr_t)dentry;
+    
+    // register RPC request in remote RPC fifo
+    rpc_send( cxy , &rpc );
+
+#if DEBUG_RPC_VFS_DENTRY_DESTROY
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_DENTRY_DESTROY )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
+#endif
+}
+
+///////////////////////////////////////////////
+void rpc_vfs_dentry_destroy_server( xptr_t xp )
+{
+#if DEBUG_RPC_VFS_DENTRY_DESTROY
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_DENTRY_DESTROY )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+
+    vfs_dentry_t * dentry;
+
+    // get client cluster identifier and pointer on RPC descriptor
+    cxy_t        client_cxy  = GET_CXY( xp );
+    rpc_desc_t * desc        = GET_PTR( xp );
+
+    // get arguments "dentry" from client RPC descriptor
+    dentry = (vfs_dentry_t *)(intptr_t)hal_remote_l64( XPTR( client_cxy , &desc->args[0] ) );
+                       
+    // call local kernel function
+    vfs_dentry_destroy( dentry );
+
+#if DEBUG_RPC_VFS_DENTRY_DESTROY
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_DENTRY_DESTROY )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
+#endif
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// [14]  RPC_VFS_FILE_CREATE  deprecated [AG] dec 2019
+/////////////////////////////////////////////////////////////////////////////////////////
+void rpc_vfs_file_create_client( cxy_t                  cxy,
+                                 struct vfs_inode_s   * inode,       // in
+                                 uint32_t               file_attr,   // in
+                                 xptr_t               * file_xp,     // out
+                                 error_t              * error )      // out
+{
+#if DEBUG_RPC_VFS_FILE_CREATE
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_FILE_CREATE )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+
+    uint32_t responses = 1;
+
+    // initialise RPC descriptor header 
+    rpc_desc_t  rpc;
+    rpc.index    = RPC_VFS_FILE_CREATE;
+    rpc.blocking = true;
+    rpc.rsp      = &responses;
+
+    // set input arguments in RPC descriptor 
+    rpc.args[0] = (uint64_t)(intptr_t)inode;
+    rpc.args[1] = (uint64_t)file_attr;
+
+    // register RPC request in remote RPC fifo
+    rpc_send( cxy , &rpc );
+
+    // get output values from RPC descriptor
+    *file_xp = (xptr_t)rpc.args[2];
+    *error   = (error_t)rpc.args[3];
+
+#if DEBUG_RPC_VFS_FILE_CREATE
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_FILE_CREATE )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
+#endif
+}
+
+////////////////////////////////////////////
+void rpc_vfs_file_create_server( xptr_t xp )
+{
+#if DEBUG_RPC_VFS_FILE_CREATE
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_FILE_CREATE )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+
+    uint32_t      file_attr;
+    vfs_inode_t * inode;
+    xptr_t        file_xp;
+    error_t       error;
+
+    // get client cluster identifier and pointer on RPC descriptor
+    cxy_t        client_cxy  = GET_CXY( xp );
+    rpc_desc_t * desc        = GET_PTR( xp );
+
+    // get arguments "file_attr" and "inode" from client RPC descriptor
+    inode     = (vfs_inode_t *)(intptr_t)hal_remote_l64( XPTR( client_cxy , &desc->args[0] ) );
+    file_attr = (uint32_t)               hal_remote_l64( XPTR( client_cxy , &desc->args[1] ) );
+                       
+    // call local kernel function
+    error = vfs_file_create( inode,
+                             file_attr,
+                             &file_xp );
+ 
+    // set output arguments
+    hal_remote_s64( XPTR( client_cxy , &desc->args[2] ) , (uint64_t)file_xp );
+    hal_remote_s64( XPTR( client_cxy , &desc->args[3] ) , (uint64_t)error );
+
+#if DEBUG_RPC_VFS_FILE_CREATE
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_FILE_CREATE )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
+#endif
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// [15]  RPC_VFS_FILE_DESTROY    deprecated [AG] dec 2019
+/////////////////////////////////////////////////////////////////////////////////////////
+void rpc_vfs_file_destroy_client( cxy_t        cxy,
+                                  vfs_file_t * file )
+{
+#if DEBUG_RPC_VFS_FILE_DESTROY
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_FILE_DESTROY )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+
+    uint32_t responses = 1;
+
+    // initialise RPC descriptor header 
+    rpc_desc_t  rpc;
+    rpc.index    = RPC_VFS_FILE_DESTROY;
+    rpc.blocking = true;
+    rpc.rsp      = &responses;
+
+    // set input arguments in RPC descriptor 
+    rpc.args[0] = (uint64_t)(intptr_t)file;
+    
+    // register RPC request in remote RPC fifo
+    rpc_send( cxy , &rpc );
+
+#if DEBUG_RPC_VFS_FILE_DESTROY
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_FILE_DESTROY )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
+#endif
+}
+
+/////////////////////////////////////////////
+void rpc_vfs_file_destroy_server( xptr_t xp )
+{
+#if DEBUG_RPC_VFS_FILE_DESTROY
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_FILE_DESTROY )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+
+    vfs_file_t * file;
+
+    // get client cluster identifier and pointer on RPC descriptor
+    cxy_t        client_cxy  = GET_CXY( xp );
+    rpc_desc_t * desc        = GET_PTR( xp );
+
+    // get arguments "dentry" from client RPC descriptor
+    file = (vfs_file_t *)(intptr_t)hal_remote_l64( XPTR( client_cxy , &desc->args[0] ) );
+                       
+    // call local kernel function
+    vfs_file_destroy( file );
+
+#if DEBUG_RPC_VFS_FILE_DESTROY
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_FILE_DESTROY )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
+#endif
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// [16]  RPC_VFS_FS_GET_DENTRY   deprecated [AG] dec 2019
+/////////////////////////////////////////////////////////////////////////////////////////
+void rpc_vfs_fs_new_dentry_client( cxy_t         cxy,
+                                   vfs_inode_t * parent_inode,    // in
+                                   char        * name,            // in
+                                   xptr_t        child_inode_xp,  // in
+                                   error_t     * error )          // out
+{
+#if DEBUG_RPC_VFS_FS_NEW_DENTRY
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_FS_NEW_DENTRY )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+
+    uint32_t responses = 1;
+
+    // initialise RPC descriptor header 
+    rpc_desc_t  rpc;
+    rpc.index    = RPC_VFS_FS_NEW_DENTRY;
+    rpc.blocking = true;
+    rpc.rsp      = &responses;
+
+    // set input arguments in RPC descriptor 
+    rpc.args[0] = (uint64_t)(intptr_t)parent_inode;
+    rpc.args[1] = (uint64_t)(intptr_t)name;
+    rpc.args[2] = (uint64_t)child_inode_xp;
+
+    // register RPC request in remote RPC fifo
+    rpc_send( cxy , &rpc );
+
+    // get output values from RPC descriptor
+    *error   = (error_t)rpc.args[3];
+
+#if DEBUG_RPC_VFS_FS_NEW_DENTRY
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_FS_NEW_DENTRY )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+}
+
+//////////////////////////////////////////////
+void rpc_vfs_fs_new_dentry_server( xptr_t xp )
+{
+#if DEBUG_RPC_VFS_FS_NEW_DENTRY
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_FS_NEW_DENTRY )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+
+    error_t       error;
+    vfs_inode_t * parent;
+    xptr_t        child_xp;
+    char        * name;
+
+    char          name_copy[CONFIG_VFS_MAX_NAME_LENGTH];
+
+    // get client cluster identifier and pointer on RPC descriptor
+    cxy_t        client_cxy  = GET_CXY( xp );
+    rpc_desc_t * desc        = GET_PTR( xp );
+
+    // get arguments "parent", "name", and "child_xp"
+    parent     = (vfs_inode_t*)(intptr_t)hal_remote_l64(XPTR(client_cxy , &desc->args[0]));
+    name       = (char*)(intptr_t)       hal_remote_l64(XPTR(client_cxy , &desc->args[1]));
+    child_xp   = (xptr_t)                hal_remote_l64(XPTR(client_cxy , &desc->args[2]));
+
+    // get name local copy
+    hal_remote_strcpy( XPTR( local_cxy , name_copy ) ,
+                       XPTR( client_cxy , name ) );
+
+    // call the kernel function
+    error = vfs_fs_new_dentry( parent , name_copy , child_xp );
+
+    // set output argument
+    hal_remote_s64( XPTR( client_cxy , &desc->args[3] ) , (uint64_t)error );
+
+#if DEBUG_RPC_VFS_FS_NEW_DENTRY
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_FS_NEW_DENTRY )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// [17]  RPC_VFS_FS_ADD_DENTRY  deprecated [AG] dec 2019
+/////////////////////////////////////////////////////////////////////////////////////////
+void rpc_vfs_fs_add_dentry_client( cxy_t          cxy,
+                                   vfs_inode_t  * parent,     // in
+                                   vfs_dentry_t * dentry,     // in
+                                   error_t      * error )     // out
+{
+#if DEBUG_RPC_VFS_FS_ADD_DENTRY
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_FS_ADD_DENTRY )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+
+    uint32_t responses = 1;
+
+    // initialise RPC descriptor header 
+    rpc_desc_t  rpc;
+    rpc.index    = RPC_VFS_FS_ADD_DENTRY;
+    rpc.blocking = true;
+    rpc.rsp      = &responses;
+
+    // set input arguments in RPC descriptor 
+    rpc.args[0] = (uint64_t)(intptr_t)parent;
+    rpc.args[1] = (uint64_t)(intptr_t)dentry;
+
+    // register RPC request in remote RPC fifo
+    rpc_send( cxy , &rpc );
+
+    // get output values from RPC descriptor
+    *error   = (error_t)rpc.args[2];
+
+#if DEBUG_RPC_VFS_FS_ADD_DENTRY
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_FS_ADD_DENTRY )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+}
+
+//////////////////////////////////////////////
+void rpc_vfs_fs_add_dentry_server( xptr_t xp )
+{
+#if DEBUG_RPC_VFS_FS_ADD_DENTRY
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_FS_ADD_DENTRY )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+
+    error_t        error;
+    vfs_inode_t  * parent;
+    vfs_dentry_t * dentry;
+
+    // get client cluster identifier and pointer on RPC descriptor
+    cxy_t        client_cxy  = GET_CXY( xp );
+    rpc_desc_t * desc        = GET_PTR( xp );
+
+    // get input arguments
+    parent = (vfs_inode_t*)(intptr_t) hal_remote_l64(XPTR(client_cxy , &desc->args[0]));
+    dentry = (vfs_dentry_t*)(intptr_t)hal_remote_l64(XPTR(client_cxy , &desc->args[1]));
+
+    // call the kernel function
+    error = vfs_fs_add_dentry( parent , dentry );
+
+    // set output argument
+    hal_remote_s64( XPTR( client_cxy , &desc->args[2] ) , (uint64_t)error );
+
+#if DEBUG_RPC_VFS_FS_ADD_DENTRY
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_FS_ADD_DENTRY )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// [18]   RPC_VFS_FS_REMOVE_DENTRY    deprecated [AG] dec 2019
+/////////////////////////////////////////////////////////////////////////////////////////
+void rpc_vfs_fs_remove_dentry_client( cxy_t          cxy,
+                                      vfs_inode_t  * parent,     // in
+                                      vfs_dentry_t * dentry,     // in
+                                      error_t      * error )     // out
+{
+#if DEBUG_RPC_VFS_FS_REMOVE_DENTRY
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_FS_REMOVE_DENTRY )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+
+    uint32_t responses = 1;
+
+    // initialise RPC descriptor header 
+    rpc_desc_t  rpc;
+    rpc.index    = RPC_VFS_FS_REMOVE_DENTRY;
+    rpc.blocking = true;
+    rpc.rsp      = &responses;
+
+    // set input arguments in RPC descriptor 
+    rpc.args[0] = (uint64_t)(intptr_t)parent;
+    rpc.args[1] = (uint64_t)(intptr_t)dentry;
+
+    // register RPC request in remote RPC fifo
+    rpc_send( cxy , &rpc );
+
+    // get output values from RPC descriptor
+    *error   = (error_t)rpc.args[2];
+
+#if DEBUG_RPC_VFS_FS_REMOVE_DENTRY
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_FS_REMOVE_DENTRY )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+}
+
+/////////////////////////////////////////////////
+void rpc_vfs_fs_remove_dentry_server( xptr_t xp )
+{
+#if DEBUG_RPC_VFS_FS_REMOVE_DENTRY
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_FS_REMOVE_DENTRY )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+
+    error_t        error;
+    vfs_inode_t  * parent;
+    vfs_dentry_t * dentry;
+
+    // get client cluster identifier and pointer on RPC descriptor
+    cxy_t        client_cxy  = GET_CXY( xp );
+    rpc_desc_t * desc        = GET_PTR( xp );
+
+    // get input arguments
+    parent = (vfs_inode_t*)(intptr_t) hal_remote_l64(XPTR(client_cxy , &desc->args[0]));
+    dentry = (vfs_dentry_t*)(intptr_t)hal_remote_l64(XPTR(client_cxy , &desc->args[1]));
+
+    // call the kernel function
+    error = vfs_fs_remove_dentry( parent , dentry );
+
+    // set output argument
+    hal_remote_s64( XPTR( client_cxy , &desc->args[2] ) , (uint64_t)error );
+
+#if DEBUG_RPC_VFS_FS_REMOVE_DENTRY
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_FS_REMOVE_DENTRY )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// [19]   RPC_VFS_INODE_LOAD_ALL_PAGES deprecated [AG] dec 2019
+/////////////////////////////////////////////////////////////////////////////////////////
+void rpc_vfs_inode_load_all_pages_client( cxy_t         cxy,
+                                          vfs_inode_t * inode,      // in
+                                          error_t     * error )     // out
+{
+#if DEBUG_RPC_VFS_INODE_LOAD_ALL_PAGES
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_INODE_LOAD_ALL_PAGES )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+
+    uint32_t responses = 1;
+
+    // initialise RPC descriptor header 
+    rpc_desc_t  rpc;
+    rpc.index    = RPC_VFS_INODE_LOAD_ALL_PAGES;
+    rpc.blocking = true;
+    rpc.rsp      = &responses;
+
+    // set input arguments in RPC descriptor 
+    rpc.args[0] = (uint64_t)(intptr_t)inode;
+
+    // register RPC request in remote RPC fifo
+    rpc_send( cxy , &rpc );
+
+    // get output values from RPC descriptor
+    *error   = (error_t)rpc.args[1];
+
+#if DEBUG_RPC_VFS_INODE_LOAD_ALL_PAGES
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_INODE_LOAD_ALL_PAGES )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+}
+
+/////////////////////////////////////////////////////
+void rpc_vfs_inode_load_all_pages_server( xptr_t xp )
+{
+#if DEBUG_RPC_VFS_INODE_LOAD_ALL_PAGES
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_INODE_LOAD_ALL_PAGES )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+
+    error_t       error;
+    vfs_inode_t * inode;
+
+    // get client cluster identifier and pointer on RPC descriptor
+    cxy_t        client_cxy  = GET_CXY( xp );
+    rpc_desc_t * desc        = GET_PTR( xp );
+
+    // get input argument
+    inode = (vfs_inode_t*)(intptr_t)hal_remote_l64(XPTR(client_cxy , &desc->args[0]));
+
+    // call the kernel function
+    error = vfs_inode_load_all_pages( inode );
+
+    // set output argument
+    hal_remote_s64( XPTR( client_cxy , &desc->args[1] ) , (uint64_t)error );
+
+#if DEBUG_RPC_VFS_INODE_LOAD_ALL_PAGES
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VFS_INODE_LOAD_ALL_PAGES )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// [20]  RPC_VMM_GET_VSEG deprecated [AG] sept 2019
+/////////////////////////////////////////////////////////////////////////////////////////
+void rpc_vmm_get_vseg_client( cxy_t       cxy,      
+                              process_t * process,     // in  
+                              intptr_t    vaddr,       // in  
+                              xptr_t    * vseg_xp,     // out
+                              error_t   * error )      // out
+{
+#if DEBUG_RPC_VMM_GET_VSEG
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VMM_GET_VSEG )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+
+    uint32_t responses = 1;
+
+    // initialise RPC descriptor header 
+    rpc_desc_t  rpc;
+    rpc.index    = RPC_VMM_GET_VSEG;
+    rpc.blocking = true;
+    rpc.rsp      = &responses;
+
+    // set input arguments in RPC descriptor 
+    rpc.args[0] = (uint64_t)(intptr_t)process;
+    rpc.args[1] = (uint64_t)vaddr;
+
+    // register RPC request in remote RPC fifo
+    rpc_send( cxy , &rpc );
+
+    // get output argument from rpc descriptor
+    *vseg_xp = rpc.args[2];
+    *error   = (error_t)rpc.args[3];
+
+#if DEBUG_RPC_VMM_GET_VSEG
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VMM_GET_VSEG )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+}
+
+/////////////////////////////////////////
+void rpc_vmm_get_vseg_server( xptr_t xp )
+{
+#if DEBUG_RPC_VMM_GET_VSEG
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VMM_GET_VSEG )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+
+    process_t   * process;
+    intptr_t      vaddr;
+    vseg_t      * vseg_ptr;
+    xptr_t        vseg_xp;
+    error_t       error;
+
+    // get client cluster identifier and pointer on RPC descriptor
+    cxy_t        client_cxy  = GET_CXY( xp );
+    rpc_desc_t * desc        = GET_PTR( xp );
+
+    // get input argument from client RPC descriptor
+    process = (process_t *)(intptr_t)hal_remote_l64( XPTR( client_cxy , &desc->args[0] ) );
+    vaddr   = (intptr_t)hal_remote_l64( XPTR( client_cxy , &desc->args[1] ) );
+    
+    // call local kernel function
+    error = vmm_get_vseg( process , vaddr , &vseg_ptr );
+
+    // set output arguments to client RPC descriptor
+    vseg_xp = XPTR( local_cxy , vseg_ptr );
+    hal_remote_s64( XPTR( client_cxy , &desc->args[2] ) , (uint64_t)vseg_xp );
+    hal_remote_s64( XPTR( client_cxy , &desc->args[3] ) , (uint64_t)error );
+
+#if DEBUG_RPC_VMM_GET_VSEG
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_VMM_GET_VSEG )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// [22]          RPC_KCM_ALLOC deprecated [AG] sept 2019
+/////////////////////////////////////////////////////////////////////////////////////////
+void rpc_kcm_alloc_client( cxy_t      cxy,
+                           uint32_t   kmem_type,   // in
+                           xptr_t   * buf_xp )     // out
+{
+#if DEBUG_RPC_KCM_ALLOC
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_KCM_ALLOC )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+
+    uint32_t responses = 1;
+
+    // initialise RPC descriptor header 
+    rpc_desc_t  rpc;
+    rpc.index    = RPC_KCM_ALLOC;
+    rpc.blocking = true;
+    rpc.rsp      = &responses;
+
+    // set input arguments in RPC descriptor 
+    rpc.args[0] = (uint64_t)kmem_type;
+
+    // register RPC request in remote RPC fifo
+    rpc_send( cxy , &rpc );
+
+    // get output arguments from RPC descriptor
+    *buf_xp = (xptr_t)rpc.args[1];
+
+#if DEBUG_RPC_KCM_ALLOC
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_KCM_ALLOC )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+}
+
+//////////////////////////////////////
+void rpc_kcm_alloc_server( xptr_t xp )
+{
+#if DEBUG_RPC_KCM_ALLOC
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_KCM_ALLOC )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+
+    // get client cluster identifier and pointer on RPC descriptor
+    cxy_t        client_cxy  = GET_CXY( xp );
+    rpc_desc_t * desc        = GET_PTR( xp );
+
+    // get input argument "kmem_type" from client RPC descriptor
+    uint32_t kmem_type = (uint32_t)hal_remote_l64( XPTR( client_cxy , &desc->args[0] ) );
+
+    // allocates memory for kcm
+    kmem_req_t  req;
+    req.type  = kmem_type;
+    req.flags = AF_ZERO;
+    void * buf_ptr = kmem_alloc( &req );
+
+    // set output argument
+    xptr_t buf_xp = XPTR( local_cxy , buf_ptr );
+    hal_remote_s64( XPTR( client_cxy , &desc->args[1] ) , (uint64_t)buf_xp );
+
+#if DEBUG_RPC_KCM_ALLOC
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_KCM_ALLOC )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+}   
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// [23]          RPC_KCM_FREE deprecated [AG] sept 2019
+/////////////////////////////////////////////////////////////////////////////////////////
+void rpc_kcm_free_client( cxy_t      cxy,
+                          void     * buf,          // in
+                          uint32_t   kmem_type )   // in
+{
+#if DEBUG_RPC_KCM_FREE
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_KCM_FREE )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+
+    uint32_t responses = 1;
+
+    // initialise RPC descriptor header 
+    rpc_desc_t  rpc;
+    rpc.index    = RPC_KCM_FREE;
+    rpc.blocking = true;
+    rpc.rsp      = &responses;
+
+    // set input arguments in RPC descriptor 
+    rpc.args[0] = (uint64_t)(intptr_t)buf;
+    rpc.args[1] = (uint64_t)kmem_type;
+
+    // register RPC request in remote RPC fifo
+    rpc_send( cxy , &rpc );
+
+#if DEBUG_RPC_KCM_FREE
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_KCM_FREE )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+}
+
+/////////////////////////////////////
+void rpc_kcm_free_server( xptr_t xp )
+{
+#if DEBUG_RPC_KCM_FREE
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_KCM_FREE )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+
+    // get client cluster identifier and pointer on RPC descriptor
+    cxy_t        client_cxy  = GET_CXY( xp );
+    rpc_desc_t * desc        = GET_PTR( xp );
+
+    // get input arguments "buf" and "kmem_type" from client RPC descriptor
+    void     * buf = (void *)(intptr_t)hal_remote_l64( XPTR( client_cxy , &desc->args[0] ) );
+    uint32_t   kmem_type = (uint32_t)hal_remote_l64( XPTR( client_cxy , &desc->args[1] ) );
+
+    // releases memory
+    kmem_req_t  req;
+    req.type = kmem_type;
+    req.ptr  = buf;
+    kmem_free( &req );
+
+#if DEBUG_RPC_KCM_FREE
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_KCM_FREE )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+}   
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// [24]          Marshaling functions attached to RPC_MAPPER_SYNC 
+/////////////////////////////////////////////////////////////////////////////////////////
+void rpc_mapper_sync_client( cxy_t             cxy,
+                             struct mapper_s * mapper,
+                             error_t         * error )
+{
+#if DEBUG_RPC_MAPPER_SYNC
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_MAPPER_SYNC )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+
+    uint32_t responses = 1;
+
+    // initialise RPC descriptor header 
+    rpc_desc_t  rpc;
+    rpc.index    = RPC_MAPPER_SYNC;
+    rpc.blocking = true;
+    rpc.rsp      = &responses;
+
+    // set input arguments in RPC descriptor 
+    rpc.args[0] = (uint64_t)(intptr_t)mapper;
+
+    // register RPC request in remote RPC fifo
+    rpc_send( cxy , &rpc );
+
+    // get output values from RPC descriptor
+    *error   = (error_t)rpc.args[1];
+
+#if DEBUG_RPC_MAPPER_SYNC
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_MAPPER_SYNC )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+}
+
+////////////////////////////////////////
+void rpc_mapper_sync_server( xptr_t xp )
+{
+#if DEBUG_RPC_MAPPER_SYNC
+thread_t * this = CURRENT_THREAD;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_MAPPER_SYNC )
+printk("\n[%s] thread[%x,%x] on core %d enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+
+    mapper_t * mapper;
+    error_t    error;
+
+    // get client cluster identifier and pointer on RPC descriptor
+    cxy_t        client_cxy  = GET_CXY( xp );
+    rpc_desc_t * desc        = GET_PTR( xp );
+
+    // get arguments from client RPC descriptor
+    mapper  = (mapper_t *)(intptr_t)hal_remote_l64( XPTR( client_cxy , &desc->args[0] ) );
+
+    // call local kernel function
+    error = mapper_sync( mapper );
+
+    // set output argument to client RPC descriptor
+    hal_remote_s64( XPTR( client_cxy , &desc->args[1] ) , (uint64_t)error );
+
+#if DEBUG_RPC_MAPPER_SYNC
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_RPC_MAPPER_SYNC )
+printk("\n[%s] thread[%x,%x] on core %d exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid , cycle );
+#endif
+}
+
 */
+
+
+
+/*
+////////////////////////////////////////////////////
+void rpc_fbf_display_client( cxy_t        cxy,
+                             xptr_t       window_xp,
+                             uint32_t     cores,
+                             error_t    * error )
+{
+#if DEBUG_RPC_FBF_DISPLAY
+uint32_t  cycle = (uint32_t)hal_get_cycles();
+thread_t * this = CURRENT_THREAD;
+if( DEBUG_RPC_FBF_DISPLAY < cycle )
+printk("\n[%s] thread[%x,%x] on core %d : enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
+#endif
+
+    uint32_t    responses = 1;
+    rpc_desc_t  rpc;
+
+    // initialise RPC descriptor header 
+    rpc.index    = RPC_FBF_DISPLAY;
+    rpc.blocking = true;
+    rpc.rsp      = &responses;
+
+    // set input arguments in RPC descriptor 
+    rpc.args[0] = (uint64_t)window_xp;
+    rpc.args[1] = (uint64_t)cores;
+
+    // register RPC request in remote RPC fifo 
+    rpc_send( cxy , &rpc );
+
+    // get output argument from RPC descriptor
+    *error     = (error_t)rpc.args[2];
+
+#if DEBUG_RPC_FBF_DISPLAY
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_RPC_FBF_DISPLAY < cycle )
+printk("\n[%s] thread[%x,%x] on core %d : exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
+#endif
+}
+
+////////////////////////////////////////
+void rpc_fbf_display_server( xptr_t xp )
+{
+    // get client cluster identifier and pointer on RPC descriptor
+    cxy_t        client_cxy = GET_CXY( xp );
+    rpc_desc_t * desc       = GET_PTR( xp );
+
+    // get arguments from RPC descriptor
+    xptr_t   window_xp = (xptr_t  )hal_remote_l64( XPTR(client_cxy , &desc->args[0]) );
+    uint32_t ncores    = (uint32_t)hal_remote_l64( XPTR(client_cxy , &desc->args[1]) );
+
+#if DEBUG_RPC_FBF_DISPLAY
+uint32_t cycle = (uint32_t)hal_get_cycles();
+thread_t * this = CURRENT_THREAD;
+if( DEBUG_RPC_FBF_DISPLAY < cycle )
+printk("\n[%s] thread[%x,%x] on core %d : enter / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
+#endif
+
+    // call relevant kernel function 
+    error_t error = dev_fbf_parallel_display( window_xp , ncores );
+
+    // WARNING : for parallel RPCs, the return error argument is shared
+    hal_remote_atomic_or( XPTR( client_cxy , &desc->args[2] ) , error );
+
+#if DEBUG_RPC_FBF_DISPLAY
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_RPC_FBF_DISPLAY < cycle )
+printk("\n[%s] thread[%x,%x] on core %d : exit / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, this->core->lid, cycle );
+#endif
+} 
+*/
+
+
Index: /trunk/kernel/kern/rpc.h
===================================================================
--- /trunk/kernel/kern/rpc.h	(revision 656)
+++ /trunk/kernel/kern/rpc.h	(revision 657)
@@ -2,5 +2,5 @@
  * rpc.h - RPC (Remote Procedure Call) operations definition.
  *
- * Author  Alain Greiner (2016,2017,2018)
+ * Author  Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -68,30 +68,19 @@
     RPC_THREAD_USER_CREATE        = 6,
     RPC_THREAD_KERNEL_CREATE      = 7,
-    RPC_VFS_FS_UPDATE_DENTRY      = 8,
+    RPC_UNDEFINED_8               = 8,
     RPC_PROCESS_SIGACTION         = 9,
 
-    RPC_VFS_INODE_CREATE          = 10,
-    RPC_VFS_INODE_DESTROY         = 11,
-    RPC_VFS_DENTRY_CREATE         = 12,
-    RPC_VFS_DENTRY_DESTROY        = 13,
-    RPC_VFS_FILE_CREATE           = 14,
-    RPC_VFS_FILE_DESTROY          = 15,
-    RPC_VFS_FS_NEW_DENTRY         = 16,
-    RPC_VFS_FS_ADD_DENTRY         = 17,
-    RPC_VFS_FS_REMOVE_DENTRY      = 18,
-    RPC_VFS_INODE_LOAD_ALL_PAGES  = 19,
-
-    RPC_UNDEFINED_20              = 20,   //
-    RPC_UNDEFINED_21              = 21,   //
-    RPC_UNDEFINED_22              = 22,   // 
-    RPC_UNDEFINED_23              = 23,   //
-    RPC_MAPPER_SYNC               = 24,
-    RPC_VMM_RESIZE_VSEG           = 25,
-    RPC_VMM_REMOVE_VSEG           = 26,
-    RPC_VMM_CREATE_VSEG           = 27,
-    RPC_VMM_SET_COW               = 28,
-    RPC_UNDEFINED_29              = 29,   //
-
-    RPC_MAX_INDEX                 = 30,
+    RPC_UNDEFINED_10              = 10,   //
+    RPC_UNDEFINED_11              = 11,   //
+    RPC_UNDEFINED_12              = 12,   // 
+    RPC_UNDEFINED_13              = 13,   //
+    RPC_UNDEFINED_14              = 14,   //
+    RPC_VMM_RESIZE_VSEG           = 15,
+    RPC_VMM_REMOVE_VSEG           = 16,
+    RPC_VMM_CREATE_VSEG           = 17,
+    RPC_VMM_SET_COW               = 18,
+    RPC_UNDEFINED_19              = 19,   //
+
+    RPC_MAX_INDEX                 = 20,
 }
 rpc_index_t;
@@ -288,27 +277,12 @@
 
 /***********************************************************************************
- * [8] The RPC_VFS_FS_UPDATE_DENTRY allows a client thread to request a remote
- * cluster to update the <size> field of a directory entry in the mapper of a
- * remote directory inode, identified by the <inode> local pointer.
- * The target entry name is identified by the <dentry> local pointer. 
- ***********************************************************************************
- * @ cxy     : server cluster identifier.
- * @ inode   : [in] local pointer on remote directory inode.
- * @ dentry  : [in] local pointer on remote dentry.
- * @ size    : [in] new size value.
- * @ error   : [out] error status (0 if success).
- **********************************************************************************/
-void rpc_vfs_fs_update_dentry_client( cxy_t                 cxy,
-                                      struct vfs_inode_s  * inode,
-                                      struct vfs_dentry_s * dentry,
-                                      uint32_t              size,
-                                      error_t             * error );
-
-void rpc_vfs_fs_update_dentry_server( xptr_t xp );
+ * [8] undefined
+ **********************************************************************************/
 
 /***********************************************************************************
  * [9] The RPC_PROCESS_SIGACTION allows a client thread to request a remote cluster
  * to execute a given sigaction, defined by the <action_type> for a given process,
- * identified by the <pid> argument.
+ * identified by the <pid> argument.  When this RPC is used in parallel mode,
+ * the rpc_process_sigaction_client() function is not used. 
  ***********************************************************************************
  * @ cxy     : server cluster identifier.
@@ -323,202 +297,25 @@
 
 /*********************************************************************************** 
- * [10] The RPC_VFS_INODE_CREATE creates an inode and the associated mapper in a
- * remote cluster. The parent dentry must have been previously created.
- * It returns an extended pointer on the created inode.
- ***********************************************************************************
- * @ cxy        :  server cluster identifier.
- * @ fs_type    : [in]  file system type.
- * @ inode_type : [in]  file system type.
- * @ attr       : [in]  inode attributes.
- * @ rights     : [in]  access rights
- * @ uid        : [in]  user ID
- * @ gid        : [in]  group ID
- * @ inode_xp   : [out] buffer for extended pointer on created inode.
- * @ error      : [out] error status (0 if success).
- **********************************************************************************/
-void rpc_vfs_inode_create_client( cxy_t      cxy,
-                                  uint32_t   fs_type,
-                                  uint32_t   attr,    
-                                  uint32_t   rights,  
-                                  uint32_t   uid,
-                                  uint32_t   gid,
-                                  xptr_t   * inode_xp,
-                                  error_t  * error );
-
-void rpc_vfs_inode_create_server( xptr_t xp );
+ * [10] undefined
+ **********************************************************************************/
 
 /*********************************************************************************** 
- * [11] The RPC_VFS_INODE_DESTROY releases memory allocated for an inode descriptor
- * and for the associated mapper in a remote cluster. 
- ***********************************************************************************
- * @ cxy       :  server cluster identifier
- * @ inode     : [in]  local pointer on inode.
- **********************************************************************************/
-void rpc_vfs_inode_destroy_client( cxy_t                cxy,
-                                   struct vfs_inode_s * inode );
-
-void rpc_vfs_inode_destroy_server( xptr_t xp );
+ * [11] undefined
+ **********************************************************************************/
 
 /*********************************************************************************** 
- * [12] The RPC_VFS_DENTRY_CREATE creates a dentry in a remote cluster. 
- * It returns an extended pointer on the created dentry.
- ***********************************************************************************
- * @ cxy        :  server cluster identifier
- * @ type       : [in]  file system type.
- * @ name       : [in]  directory entry name.
- * @ dentry_xp  : [out] buffer for extended pointer on created dentry.
- * @ error      : [out] error status (0 if success).
- **********************************************************************************/
-void rpc_vfs_dentry_create_client( cxy_t                  cxy,
-                                   uint32_t               type,
-                                   char                 * name,    
-                                   xptr_t               * dentry_xp,
-                                   error_t              * error );
-
-void rpc_vfs_dentry_create_server( xptr_t xp );
+ * [12] undefined                                                       
+ **********************************************************************************/
 
 /*********************************************************************************** 
- * [13] The RPC_VFS_DENTRY_DESTROY remove a denfry from the parent inode XHTAB,
- * and releases memory allocated for the dentry descriptor in a remote cluster. 
- ***********************************************************************************
- * @ cxy       :  server cluster identifier
- * @ dentry     : [in]  local pointer on dentry.
- **********************************************************************************/
-void rpc_vfs_dentry_destroy_client( cxy_t                 cxy,
-                                    struct vfs_dentry_s * dentry );
-
-void rpc_vfs_dentry_destroy_server( xptr_t xp );
-
-/*********************************************************************************** 
- * [14] The RPC_VFS_FILE_CREATE creates a file descriptor in a remote cluster. 
- * It returns an extended pointer on the created file structure.
- ***********************************************************************************
- * @ cxy        :  server cluster identifier
- * @ inode      : [in]  local pointer on parent inode.
- * @ file_attr  : [in]  new file attributes.
- * @ file_xp    : [out] buffer for extended pointer on created file.
- * @ error      : [out] error status (0 if success).
- **********************************************************************************/
-void rpc_vfs_file_create_client( cxy_t                  cxy,
-                                 struct vfs_inode_s   * inode,
-                                 uint32_t               file_attr,
-                                 xptr_t               * file_xp,
-                                 error_t              * error );
-
-void rpc_vfs_file_create_server( xptr_t xp );
-
-/*********************************************************************************** 
- * [15] The RPC_VFS_FILE_DESTROY releases memory allocated for a file descriptor
- * in a remote cluster. 
- ***********************************************************************************
- * @ cxy       :  server cluster identifier
- * @ file       : [in]  local pointer on file.
- **********************************************************************************/
-void rpc_vfs_file_destroy_client( cxy_t               cxy,
-                                  struct vfs_file_s * file );
-
-void rpc_vfs_file_destroy_server( xptr_t xp );
-
-/*********************************************************************************** 
- * [16] The RPC_VFS_FS_GET_DENTRY calls the vfs_fs_new_dentry()
- * function in a remote cluster containing a parent inode directory to scan the 
- * associated mapper, find a directory entry identified by its name, and update
- * both the - existing - child inode and dentry.
- ***********************************************************************************
- * @ cxy            : server cluster identifier
- * @ parent_inode   : [in]  local pointer on parent inode.
- * @ name           : [in]  local pointer on child name (in client cluster).
- * @ child_inode_xp : [in]  extended pointer on child inode (in another cluster).
- * @ error          : [out] error status (0 if success).
- **********************************************************************************/
-void rpc_vfs_fs_new_dentry_client( cxy_t                cxy,
-                                   struct vfs_inode_s * parent_inode,
-                                   char               * name,
-                                   xptr_t               child_inode_xp,
-                                   error_t            * error );
-
-void rpc_vfs_fs_new_dentry_server( xptr_t xp );
-
-/*********************************************************************************** 
- * [17] The RPC_VFS_FS_ADD_DENTRY calls the vfs_fs_add_dentry() function in a 
- * remote cluster containing a directory inode and mapper, to add a new dentry
- * in the mapper of this directory.
- ***********************************************************************************
- * @ cxy            : server cluster identifier
- * @ parent         : [in]  local pointer on directory inode.
- * @ dentry         : [in]  local pointer on dentry.
- * @ error          : [out] error status (0 if success).
- **********************************************************************************/
-void rpc_vfs_fs_add_dentry_client( cxy_t,
-                                   struct vfs_inode_s  * parent,
-                                   struct vfs_dentry_s * dentry,
-                                   error_t             * error );
-
-void rpc_vfs_fs_add_dentry_server( xptr_t xp );
-
-/*********************************************************************************** 
- * [18] The RPC_VFS_FS_REMOVE_DENTRY calls the vfs_fs_remove_dentry() function in a
- * remote cluster containing a directory inode and mapper, to remove a dentry from
- * the mapper of this directory.
- ***********************************************************************************
- * @ cxy            : server cluster identifier
- * @ parent         : [in]  local pointer on directory inode.
- * @ dentry         : [in]  local pointer on dentry.
- * @ error          : [out] error status (0 if success).
- **********************************************************************************/
-void rpc_vfs_fs_remove_dentry_client( cxy_t,
-                                      struct vfs_inode_s  * parent,
-                                      struct vfs_dentry_s * dentry,
-                                      error_t             * error );
-
-void rpc_vfs_fs_remove_dentry_server( xptr_t xp );
-
-/***********************************************************************************
- * [19] The RPC_VFS_INODE_LOAD_ALL_PAGES calls the vfs_inode_load_all_pages()
- * function a remote cluster containing an inode to load all pages in the
- * associated mapper.  
- ***********************************************************************************
- * @ cxy     : server cluster identifier
- * @ inode   : [in]  local pointer on inode in server cluster.
- * @ error   : [out] error status (0 if success).
- **********************************************************************************/
-void rpc_vfs_inode_load_all_pages_client( cxy_t                cxy,
-                                          struct vfs_inode_s * inode,
-                                          error_t            * error );
-
-void rpc_vfs_inode_load_all_pages_server( xptr_t xp );
-
-/***********************************************************************************
- * [20] undefined
- **********************************************************************************/
-
-/***********************************************************************************
- * [21] undefined
- **********************************************************************************/
-
-/***********************************************************************************
- * [22] undefined
- **********************************************************************************/
-
-/***********************************************************************************
- * [23] undefined
- **********************************************************************************/
-
-/***********************************************************************************
- * [24] The RPC_MAPPER_SYNC allows a client thread to synchronize on disk
- * all dirty pages of a remote mapper.
- ***********************************************************************************
- * @ cxy       : server cluster identifier.
- * @ mapper    : [in] local pointer on mapper in server cluster.
- * @ error       : [out] error status (0 if success).
- **********************************************************************************/
-void rpc_mapper_sync_client( cxy_t             cxy,
-                             struct mapper_s * mapper,
-                             error_t         * error );
-
-void rpc_mapper_sync_server( xptr_t xp );
-
-/***********************************************************************************
- * [25] The RPC_VMM_RESIZE_VSEG allows a client thread to request a remote cluster
+ * [13] undefined                                       
+ **********************************************************************************/
+
+/***********************************************************************************
+ * [14] undefined
+ **********************************************************************************/
+
+/***********************************************************************************
+ * [15] The RPC_VMM_RESIZE_VSEG allows a client thread to request a remote cluster
  * to resize a vseg identified by the <base> argument in a process descriptor
  * identified by the <pid> argument, as defined by the <new_base> and <new_size>
@@ -540,5 +337,5 @@
 
 /***********************************************************************************
- * [26] The RPC_VMM_REMOVE_VSEG allows a client thread  to request a remote cluster
+ * [16] The RPC_VMM_REMOVE_VSEG allows a client thread  to request a remote cluster
  * to delete a vseg identified by the <vaddr> argument in a process descriptor
  * identified by the <pid> argument.
@@ -556,5 +353,5 @@
 
 /***********************************************************************************
- * [27] The RPC_VMM_CREATE_VSEG allows a client thread to request the remote 
+ * [17] The RPC_VMM_CREATE_VSEG allows a client thread to request the remote 
  * reference cluster of a given process to allocate and register in the reference
  * process VMM a new vseg descriptor.
@@ -587,5 +384,5 @@
 
 /***********************************************************************************
- * [28] The RPC_VMM_SET_COW allows a client thread to request the remote reference
+ * [18] The RPC_VMM_SET_COW allows a client thread to request the remote reference
  * cluster to set the COW flag and reset the WRITABLE flag of all GPT entries for
  * the DATA, MMAP and REMOTE vsegs of process identified by the <process> argument.
@@ -602,5 +399,5 @@
 
 /***********************************************************************************
- * [29] undefined
+ * [19] undefined
  **********************************************************************************/
 
Index: /trunk/kernel/kern/socket.c
===================================================================
--- /trunk/kernel/kern/socket.c	(revision 657)
+++ /trunk/kernel/kern/socket.c	(revision 657)
@@ -0,0 +1,298 @@
+/*
+ * socket.c - socket API implementation.
+ *
+ * Authors  Alain Greiner   (2016,2017,2018,2019,2020)
+ *
+ * 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 <kernel_config.h>
+#include <hal_kernel_types.h>
+#include <hal_remote.h>
+#include <shared_socket.h>
+#include <process.h>
+#include <remote_buf.h>
+#include <printk.h>
+#include <kmem.h>
+#include <thread.h>
+#include <vfs.h>
+#include <socket.h>
+
+//////////////////////////////////////////////////////////////////////////////////////
+// Extern global variables
+//////////////////////////////////////////////////////////////////////////////////////
+
+extern chdev_directory_t  chdev_dir;         // allocated in kernel_init.c
+
+//////////////////////////////////////
+char * socket_cmd_str( uint32_t type )
+{
+    switch( type )
+    {
+        case SOCKET_TX_CONNECT  : return "CONNECT";
+        case SOCKET_TX_SEND     : return "SEND";
+        case SOCKET_TX_CLOSE    : return "CLOSE";
+
+        default:                return "undefined";
+    }
+}
+   
+/////////////////////////////////////////
+char * socket_state_str( uint32_t state )
+{
+    switch( state )
+    {
+        case UDP_STATE_UNBOUND    : return "UDP_UNBOUND";
+        case UDP_STATE_BOUND      : return "UDP_BOUND";
+        case UDP_STATE_CONNECT    : return "UDP_CONNECT";
+
+        case TCP_STATE_UNBOUND    : return "TCP_UNBOUND";
+        case TCP_STATE_BOUND      : return "TCP_BOUND";
+        case TCP_STATE_LISTEN     : return "TCP_LISTEN";
+        case TCP_STATE_SYN_SENT   : return "TCP_SYN_SENT";
+        case TCP_STATE_SYN_RCVD   : return "TCP_SYN_RCVD";
+        case TCP_STATE_ESTAB      : return "TCP_ESTAB";
+        case TCP_STATE_FIN_WAIT1  : return "TCP_FIN_WAIT1";
+        case TCP_STATE_FIN_WAIT2  : return "TCP_FIN_WAIT2";
+        case TCP_STATE_CLOSING    : return "TCP_CLOSING";
+        case TCP_STATE_TIME_WAIT  : return "TCP_TIME_WAIT";
+        case TCP_STATE_CLOSE_WAIT : return "TCP_CLOSE_WAIT";
+        case TCP_STATE_LAST_ACK   : return "TCP_LAST_ACK";
+
+        default:                    return "undefined";
+    }
+}
+
+///////////////////////////////////////
+error_t socket_create( cxy_t       cxy,
+                       uint32_t    domain,
+                       uint32_t    type,
+                       socket_t ** socket_ptr,
+                       uint32_t  * fdid_ptr )
+{
+    uint32_t    fdid;
+
+    thread_t  * this    = CURRENT_THREAD;
+    process_t * process = this->process;
+
+    kmem_req_t     req;
+    socket_t     * socket;
+    vfs_file_t   * file;
+    uint32_t       state;
+    error_t        error;
+
+    // allocate memory for socket descriptor 
+    req.type   = KMEM_KCM;
+    req.order  = bits_log2( sizeof(socket_t) );
+    req.flags  = AF_ZERO;
+    socket     = kmem_remote_alloc( cxy , &req );
+
+    if( socket == NULL )
+    {
+        printk("\n[ERROR] in %s : cannot allocate socket descriptor / thread[%x,%x]\n",
+        __FUNCTION__, process->pid, this->trdid );
+        return -1;
+    }
+
+    // allocate memory for rx_buf buffer
+    error = remote_buf_create( XPTR( cxy , &socket->rx_buf ),
+                               NIC_RX_BUF_SIZE );
+
+    if( error )
+    {
+        printk("\n[ERROR] in %s : cannot allocate rx_buf / thread[%x,%x]\n",
+        __FUNCTION__, process->pid, this->trdid );
+        req.type = KMEM_KCM;
+        req.ptr  = socket;
+        kmem_remote_free( cxy , &req );
+        return -1;
+    }
+
+    // allocate memory for r2tq queue 
+    error = remote_buf_create( XPTR( cxy , &socket->r2tq ),
+                               NIC_R2T_QUEUE_SIZE );
+    if( error )
+    {
+        printk("\n[ERROR] in %s : cannot allocate R2T queue / thread[%x,%x]\n",
+        __FUNCTION__, process->pid, this->trdid );
+        remote_buf_destroy( XPTR( cxy , &socket->rx_buf ) );
+        req.type = KMEM_KCM;
+        req.ptr  = socket;
+        kmem_remote_free( cxy , &req );
+        return -1;
+    }
+
+    // allocate memory for crqq queue 
+    error = remote_buf_create( XPTR( cxy , &socket->crqq ),
+                               NIC_CRQ_QUEUE_SIZE * sizeof(sockaddr_t) );
+    if( error )
+    {
+        printk("\n[ERROR] in %s : cannot allocate CRQ queue / thread[%x,%x]\n",
+        __FUNCTION__, process->pid, this->trdid );
+        remote_buf_destroy( XPTR( cxy , &socket->r2tq ) );
+        remote_buf_destroy( XPTR( cxy , &socket->rx_buf ) );
+        req.type = KMEM_KCM;
+        req.ptr  = socket;
+        kmem_remote_free( cxy , &req );
+        return -1;
+    }
+
+    //  allocate memory for file descriptor
+	req.type  = KMEM_KCM;
+	req.order = bits_log2( sizeof(vfs_file_t) );
+    req.flags = AF_ZERO;
+	file      = kmem_remote_alloc( cxy , &req );
+
+    if( file == NULL ) 
+    {
+        printk("\n[ERROR] in %s : cannot allocate file descriptor / thread[%x,%x]\n",
+        __FUNCTION__, process->pid, this->trdid );
+        remote_buf_destroy( XPTR( cxy , &socket->crqq ) );
+        remote_buf_destroy( XPTR( cxy , &socket->r2tq ) );
+        remote_buf_destroy( XPTR( cxy , &socket->rx_buf ) );
+        req.type = KMEM_KCM;
+        req.ptr  = socket;
+        kmem_remote_free( cxy , &req );
+        return -1;
+    }
+    
+    // get an fdid value, and register file descriptor in fd_array[]
+    error = process_fd_register( process->ref_xp,
+                                 XPTR( cxy , file ),
+                                 &fdid );
+    if ( error ) 
+    {
+        printk("\n[ERROR] in %s : cannot register file descriptor / thread[%x,%x]\n",
+        __FUNCTION__, process->pid, this->trdid );
+        req.type = KMEM_KCM;
+        req.ptr  = file;
+        kmem_free( &req );
+        remote_buf_destroy( XPTR( cxy , &socket->crqq ) );
+        remote_buf_destroy( XPTR( cxy , &socket->r2tq ) );
+        remote_buf_destroy( XPTR( cxy , &socket->rx_buf ) );
+        req.ptr  = socket;
+        kmem_free( &req );
+        return -1;
+    }
+    state = (type == SOCK_STREAM) ? TCP_STATE_UNBOUND : UDP_STATE_UNBOUND;
+
+    // initialise socket descriptor 
+    hal_remote_s32( XPTR( cxy , &socket->domain    ) , domain );
+    hal_remote_s32( XPTR( cxy , &socket->type      ) , type );
+    hal_remote_s32( XPTR( cxy , &socket->pid       ) , process->pid );
+    hal_remote_s32( XPTR( cxy , &socket->state     ) , state );
+    hal_remote_s64( XPTR( cxy , &socket->tx_client ) , XPTR_NULL );
+    hal_remote_s64( XPTR( cxy , &socket->rx_client ) , XPTR_NULL );
+
+    // initialize file descriptor 
+    hal_remote_s32( XPTR( cxy , &file->type        ) , INODE_TYPE_SOCK );
+    hal_remote_spt( XPTR( cxy , &file->socket      ) , socket );
+    hal_remote_s32( XPTR( cxy , &file->refcount    ) , 1 );
+
+    remote_rwlock_init( XPTR( cxy , &file->lock ) , LOCK_VFS_FILE );
+   
+    // return success
+    *socket_ptr = socket;
+    *fdid_ptr   = fdid;
+
+    return 0;
+
+}  // end socket_create
+
+////////////////////////////////////
+void socket_destroy( uint32_t fdid )
+{
+    uint32_t            type;
+    socket_t          * socket;
+    kmem_req_t          req;
+
+    thread_t  * this    = CURRENT_THREAD;
+    process_t * process = this->process;
+
+    // get pointers on file descriptor
+    xptr_t       file_xp  = process_fd_get_xptr( process , fdid );
+    vfs_file_t * file     = GET_PTR( file_xp );
+    cxy_t        cxy      = GET_CXY( file_xp );
+
+    type   = hal_remote_l32( XPTR( cxy , &file->type ) );
+    socket = hal_remote_lpt( XPTR( cxy , &file->socket ) );
+
+// check file descriptor pointer
+assert( (file_xp != XPTR_NULL), "illegal fdid\n" );
+
+// check file descriptor type
+assert( (type == INODE_TYPE_SOCK), "illegal file type\n" );
+
+    // remove the file descriptor from the process
+    process_fd_remove( process->owner_xp , fdid );
+
+    // release memory allocated for file descriptor
+    req.type = KMEM_KCM;
+    req.ptr  = file;
+    kmem_remote_free( cxy , &req );
+
+    // release memory allocated for buffers attached to socket descriptor
+    remote_buf_destroy( XPTR( cxy , &socket->crqq ) );
+    remote_buf_destroy( XPTR( cxy , &socket->r2tq ) );
+    remote_buf_destroy( XPTR( cxy , &socket->rx_buf ) );
+
+    // release memory allocated for socket descriptor
+    req.type = KMEM_KCM;
+    req.ptr  = socket;
+    kmem_remote_free( cxy , &req );
+
+}  // end socket_destroy()
+
+/////////////////////////////////////////////////
+void socket_link_to_servers( xptr_t    socket_xp,
+                             uint32_t  nic_channel ) 
+{
+    cxy_t      socket_cxy = GET_CXY( socket_xp );
+    socket_t * socket_ptr = GET_PTR( socket_xp );
+
+    // get pointers on NIC_TX[index] chdev
+    xptr_t    tx_chdev_xp  = chdev_dir.nic_tx[nic_channel];
+    chdev_t * tx_chdev_ptr = GET_PTR( tx_chdev_xp );
+    cxy_t     tx_chdev_cxy = GET_CXY( tx_chdev_xp );
+
+    // build extended pointers on root of sockets attached to NIC_TX[channel] chdev
+    xptr_t    tx_root_xp = XPTR( tx_chdev_cxy , &tx_chdev_ptr->wait_root );
+    xptr_t    tx_lock_xp = XPTR( tx_chdev_cxy , &tx_chdev_ptr->wait_lock );
+
+    // register socket in the NIC_TX[channel] chdev clients queue
+    remote_rwlock_wr_acquire( tx_lock_xp );
+    xlist_add_last( tx_root_xp , XPTR( socket_cxy , &socket_ptr->tx_list ) );
+    remote_rwlock_wr_release( tx_lock_xp );
+
+    // get pointers on NIC_RX[index] chdev
+    xptr_t    rx_chdev_xp  = chdev_dir.nic_rx[nic_channel];
+    chdev_t * rx_chdev_ptr = GET_PTR( rx_chdev_xp );
+    cxy_t     rx_chdev_cxy = GET_CXY( rx_chdev_xp );
+
+    // build extended pointer on root of sockets attached to NIC_TX[channel] chdev
+    xptr_t    rx_root_xp = XPTR( rx_chdev_cxy , &rx_chdev_ptr->wait_root );
+    xptr_t    rx_lock_xp = XPTR( rx_chdev_cxy , &rx_chdev_ptr->wait_lock );
+
+    // register socket in the NIC_RX[channel] chdev clients queue
+    remote_rwlock_wr_acquire( rx_lock_xp );
+    xlist_add_last( rx_root_xp , XPTR( socket_cxy , &socket_ptr->rx_list ) );
+    remote_rwlock_wr_release( rx_lock_xp );
+
+}  // end socket_link_to_server()
+
+
Index: /trunk/kernel/kern/socket.h
===================================================================
--- /trunk/kernel/kern/socket.h	(revision 657)
+++ /trunk/kernel/kern/socket.h	(revision 657)
@@ -0,0 +1,217 @@
+/*
+ * socket.h - socket descriptor and API definition.
+ * 
+ * Authors  Alain Greiner    (2016,2017,2018,2019,2020)
+ *
+ * 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
+ */
+
+#ifndef _SOCKET_H_
+#define _SOCKET_H_
+
+#include <kernel_config.h>
+#include <hal_kernel_types.h>
+#include <xlist.h>
+#include <remote_buf.h>
+#include <remote_busylock.h>
+
+/****************************************************************************************
+ * This defines the three commands that can be requested by a client thread to
+ * a TX server thread. These commands are registered in the socket descriptor.
+ ****************************************************************************************/
+
+typedef enum sock_cmd_e
+{
+    SOCKET_TX_CONNECT = 20,   /*! request 3 steps handshake (TCP socket only)           */
+    SOCKET_TX_SEND    = 21,   /*! request to send data      (TCP or UDP socket)         */
+    SOCKET_TX_CLOSE   = 22,   /*! request 3 steps handshake (TCP socket only)           */
+}
+sock_cmd_t;
+
+/***************************************************************************************** 
+ * This structure defines a socket descriptor. In order to parallelize the transfers,
+ * the set of all registered sockets is split in several subsets.
+ * The number of subsets is the number of  NIC channels. 
+ * The distribution key is computed from the (remote_addr/remote_port) couple.
+ * This computation is done by the NIC hardware for RX packets, 
+ * and by the dev_nic_connect() function for the TX packets.
+ *
+ * A socket is attached to the NIC_TX[channel] & NIC_RX[channel] chdevs.
+ * Each socket descriptor allows the TX and TX server threads to access various buffers:
+ * - the user "send" buffer contains the data to be send by the TX server thread.
+ * - the kernel "receive" buffer contains the data received by the RX server thread.
+ * - the kernel "r2t" buffer allows the RX server thread to make direct requests
+ *   to the associated TX server (to implement the TCP 3 steps handshake).
+ *
+ * The synchronisation mechanism between the clients threads and the servers threads
+ * is different for TX and RX transfers:
+ *
+ * 1) For a TX transfer, it can exist only one client thread for a given socket,
+ *    the transfer is always initiated by the local process, and all TX commands
+ *    (CONNECT/SEND/CLOSE) are blocking for the client thread. The user buffer is
+ *    used by TCP to handle retransmissions when required.in case of re
+ *    The client thread registers the command in the thread descriptor, registers itself
+ *    in the socket descriptor, unblocks the TX server thread from the BLOCKED_CLIENT 
+ *    condition, blocks itself on the BLOCKED_IO condition, and deschedules. 
+ *    When the command is completed, the TX server thread unblocks the client thread.
+ *    The TX server blocks itself on the BLOCKED_CLIENT condition, when there is no
+ *    pending commands and the R2T queue is empty. It is unblocked when a client
+ *    register a new command, or when the TX server thread register a mew request
+ *    in the R2T queue.
+ *    The tx_valid flip-flop is SET by the client thread to signal a valid command. 
+ *    It is RESET by the server thread when the command is completed: For a SEND, 
+ *    all bytes have been sent (UDP) or acknowledged (TCP).
+ *
+ * 2) For an RX transfer, it can exist only one client thread for a given socket,
+ *    but the transfer is initiated by the remote process, and the  RECV command
+ *    is not really blocking: the data can arrive before the local RECV command is
+ *    executed, and the server thread does not wait to receive all requested data
+ *    to deliver data to client thread. Therefore each socket contains a receive
+ *    buffer (rx_buf) handled as a single-writer/single-reader fifo.
+ *    The client thread consumes data from the rx_buf when possible. It blocks on the
+ *    BLOCKED_IO condition and deschedules when the rx_buf is empty.
+ *    It is unblocked by the RX server thread when new data is available in the rx_buf.
+ *    The RX server blocks itself on the BLOCKED_ISR condition When the NIC_RX packets
+ *    queue is empty. It is unblocked by the hardware when new packets are available.
+ * 
+ * Note : the socket domains and types are defined in the "shared_socket.h" file.
+ ****************************************************************************************/
+
+typedef enum udp_socket_state_e
+{
+    UDP_STATE_UNBOUND   = 0,
+    UDP_STATE_BOUND     = 1,
+    UDP_STATE_CONNECT   = 2,
+}
+udp_socket_state_t;
+
+typedef enum tcp_socket_state_e
+{
+    TCP_STATE_UNBOUND    = 10,
+    TCP_STATE_BOUND      = 11,
+    TCP_STATE_LISTEN     = 12,
+    TCP_STATE_SYN_SENT   = 13,
+    TCP_STATE_SYN_RCVD   = 14,
+    TCP_STATE_ESTAB      = 15,
+    TCP_STATE_FIN_WAIT1  = 16,
+    TCP_STATE_FIN_WAIT2  = 17,
+    TCP_STATE_CLOSING    = 18,
+    TCP_STATE_TIME_WAIT  = 19,
+    TCP_STATE_CLOSE_WAIT = 20,
+    TCP_STATE_LAST_ACK   = 21,
+}
+tcp_socket_state_t;
+
+typedef struct socket_s
+{
+    remote_busylock_t lock;          /*! lock protecting socket state                  */
+    uint32_t          domain;        /*! domain : AF_LOCAL / AF_INET                   */
+    uint32_t          type;          /*! type : SOCK_DGRAM / SOCK_STREAM               */
+    pid_t             pid;           /*! owner process identifier                      */
+    uint32_t          state;         /*! see above                                     */
+    uint32_t          local_addr;    /*! local  socket IP address                      */
+    uint32_t          remote_addr;   /*! remote socket IP address                      */
+    uint32_t          local_port;    /*! local  socket port number                     */
+    uint32_t          remote_port;   /*! remote socket port number                     */
+    uint32_t          nic_channel;   /*! derived from (remote_addr,remote_port)        */
+
+    xlist_entry_t     tx_list;       /*! sockets attached to same NIC_TX chdev         */
+    xptr_t            tx_client;     /*! extended pointer on TX client thread          */
+    sock_cmd_t        tx_cmd;        /*! command type (CONNECT/SEND/CLOSE)             */
+    uint8_t         * tx_buf;        /*! pointer on user buffer in user space          */
+    uint32_t          tx_len;        /*! number of bytes to be sent in command         */
+    uint32_t          tx_todo;       /*! number of bytes not yet sent                  */
+    uint32_t          tx_error;      /*! signal a TX command error                     */
+
+    xlist_entry_t     rx_list;       /*! sockets attached to same NIC_RX chdev         */
+    xptr_t            rx_client;     /*! extended pointer on RX client thread          */
+    remote_buf_t      rx_buf;        /*! embedded receive buffer descriptor            */
+
+    remote_buf_t      r2tq;          /*! RX_to_TX requests queue descriptor            */
+
+    remote_buf_t      crqq;          /*! connection requests queue descriptor          */         
+    /* the following fields defines the TCB used for a TCP connection                  */
+
+    uint32_t          tx_una;        /*! first unack byte in TX_data stream            */
+    uint32_t          tx_nxt;        /*! next byte to send in TX_data stream           */
+    uint32_t          tx_wnd;        /*! number of acceptable bytes in TX_data stream  */
+
+    uint32_t          rx_nxt;        /*! next expected byte in RX_data stream          */
+    uint32_t          rx_wnd;        /*! number of acceptable bytes in RX_data stream  */
+    uint32_t          rx_irs;        /*! initial sequence number in RX_data stream     */
+}
+socket_t;
+
+/****************************************************************************************
+ * This function returns a printable string for a client_to_tx_server command type.
+ ****************************************************************************************
+ * type   :  SOCKET_TX_CONNECT / SOCKET_TX_SEND / SOCKET_TX_CLOSE 
+ ***************************************************************************************/
+char * socket_cmd_str( uint32_t type );
+
+/****************************************************************************************
+ * This function returns a printable string for an UDP or TCP socket state.
+ ****************************************************************************************
+ * state  :  UDP_STATE_*** / TCP_STATE*** 
+ ***************************************************************************************/
+char * socket_state_str( uint32_t state );
+
+/****************************************************************************************
+ * This function allocates memory in the cluster defined by the <cxy> argument for a
+ * socket descriptor defined by the <domain> and <type> aruments, for the associated
+ * file descriptor, and for the various kernel buffers descriptors contained in the
+ * socket descriptor : "rx_buf" (receive buffer), "r2tq" (RX to RX requests queue), and 
+ * "crqq" (connect requests queue). It initialises the socket desccriptor static fields,
+ * (other than local_addr, local_port, remote_addr, remote_port).
+ * It registers the file descriptor in the reference process fd_array[], set the socket
+ * the socket state to UNBOUND, and returns the file identifier in the <fdid> argument.
+ ****************************************************************************************
+ * @ cxy     : [in]  target cluster identifier.
+ * @ domain  : [in]  socket protocol family (must be AF_INET).
+ * @ type    : [in]  socket type (SOCK_DGRAM / SOCK_STREAM).
+ * @ socket  : [out] buffer for a local pointer on created socket.
+ * @ fdid    : [out] buffer for file identifier.
+ * @ return 0 if success / return -1 if failure (no memory).
+ ***************************************************************************************/
+error_t socket_create( cxy_t              cxy,
+                       uint32_t           domain,
+                       uint32_t           type,
+                       struct socket_s ** socket,
+                       uint32_t         * fdid );
+                       
+/****************************************************************************************
+ * This functions releases all memory allocated to a socket identified by the <fdid>
+ * argument. This include the file descriptor, the socket descriptor, and all buffers
+ * referenced by the socket descriptor.
+ ****************************************************************************************
+ * @ fdid   : [in]  file descriptor index.
+ ***************************************************************************************/
+void socket_destroy( uint32_t fdid );
+
+/****************************************************************************************
+ * This functions registers the socket defined by the <socket_xp> argument into the
+ * lists of sockets attached to the NIC_TX and NIC_TX chdevs identified by the
+ * <nic_channel> argument.
+ ****************************************************************************************
+ * @ socket_xp   : [in]  extended pointer on socket descriptor
+ * @ nic_channel : [in]  NIC channel index.
+ ***************************************************************************************/
+void socket_link_to_servers( xptr_t    socket_xp,
+                             uint32_t  nic_channel );
+ 
+#endif	/* _SOCKET_H_ */
Index: /trunk/kernel/kern/thread.h
===================================================================
--- /trunk/kernel/kern/thread.h	(revision 656)
+++ /trunk/kernel/kern/thread.h	(revision 657)
@@ -3,5 +3,5 @@
  *
  * Author  Ghassan Almaless (2008,2009,2010,2011,2012)
- *         Alain Greiner (2016,2017,2018,2019)
+ *         Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -81,17 +81,18 @@
  **************************************************************************************/
 
-#define THREAD_BLOCKED_GLOBAL    0x0001  /*! thread deactivated / wait activation     */
-#define THREAD_BLOCKED_IO        0x0002  /*! thread wait IO operation completion      */
-#define THREAD_BLOCKED_MAPPER    0x0004  /*! thread wait mapper                       */
-#define THREAD_BLOCKED_EXIT      0x0008  /*! thread blocked in join / wait exit       */
-#define THREAD_BLOCKED_JOIN      0x0010  /*! thread blocked in exit / wait join       */
-#define THREAD_BLOCKED_SEM       0x0020  /*! thread wait semaphore                    */
-#define THREAD_BLOCKED_PAGE      0x0040  /*! thread wait page access                  */
-#define THREAD_BLOCKED_IDLE      0x0080  /*! thread RPC wait RPC_FIFO non empty       */
-#define THREAD_BLOCKED_USERSYNC  0x0100  /*! thread wait (cond/mutex/barrier)         */
-#define THREAD_BLOCKED_RPC       0x0200  /*! thread wait RPC completion               */
-#define THREAD_BLOCKED_ISR       0x0400  /*! thread DEV wait ISR                      */
-#define THREAD_BLOCKED_WAIT      0x0800  /*! thread wait child process termination    */
-#define THREAD_BLOCKED_LOCK      0x1000  /*! thread wait queuelock or rwlock          */
+#define THREAD_BLOCKED_GLOBAL    0x0001  /*! ANY : deactivated / wait activation      */
+#define THREAD_BLOCKED_IO        0x0002  /*! USR : wait IO operation completion       */
+#define THREAD_BLOCKED_MAPPER    0x0004  /*! ??? : wait mapper                        */
+#define THREAD_BLOCKED_EXIT      0x0008  /*! USR : blocked in join / wait exit        */
+#define THREAD_BLOCKED_JOIN      0x0010  /*! USR : blocked in exit / wait join        */
+#define THREAD_BLOCKED_SEM       0x0020  /*! USR : wait semaphore                     */
+#define THREAD_BLOCKED_PAGE      0x0040  /*! ??? : wait page access                  */
+#define THREAD_BLOCKED_IDLE      0x0080  /*! RPC : RPC_FIFO non empty                 */
+#define THREAD_BLOCKED_USERSYNC  0x0100  /*! USR : wait cond / mutex / barrier        */
+#define THREAD_BLOCKED_RPC       0x0200  /*! ANY : RPC completion                     */
+#define THREAD_BLOCKED_ISR       0x0400  /*! DEV : wait hardware IRQ                  */
+#define THREAD_BLOCKED_WAIT      0x0800  /*! USR : wait child process termination     */
+#define THREAD_BLOCKED_LOCK      0x1000  /*! ANY : wait queuelock or rwlock           */
+#define THREAD_BLOCKED_CLIENT    0x2000  /*! DEV : client threads queue non empty     */
 
 /***************************************************************************************
@@ -190,4 +191,5 @@
     list_entry_t        wait_list;       /*! member of a local waiting queue          */
     xlist_entry_t       wait_xlist;      /*! member of a trans-cluster waiting queue  */
+    xlist_entry_t       tmp_xlist;       /*! member of a trans-cluster kleenex queue  */
 
 	uint32_t            busylocks;	     /*! number of taken busylocks                */
Index: /trunk/kernel/kernel_config.h
===================================================================
--- /trunk/kernel/kernel_config.h	(revision 656)
+++ /trunk/kernel/kernel_config.h	(revision 657)
@@ -61,5 +61,5 @@
 #define DEBUG_DEV_PIC                     0
 
-#define DEBUG_DEVFS_GLOBAL_INIT           0
+#define DEBUG_DEVFS_GLOBAL_INIT           0 
 #define DEBUG_DEVFS_LOCAL_INIT            0
 #define DEBUG_DEVFS_MOVE                  0
@@ -121,4 +121,5 @@
 
 #define DEBUG_MAPPER_GET_PAGE             0
+#define DEBUG_MAPPER_GET_FAT_PAGE         0
 #define DEBUG_MAPPER_HANDLE_MISS          0
 #define DEBUG_MAPPER_MOVE_KERNEL          0
@@ -152,22 +153,15 @@
 #define DEBUG_RPC_SERVER_GENERIC          0
 
-#define DEBUG_RPC_MAPPER_MOVE_USER        0
+#define DEBUG_RPC_FBF_DISPLAY             0
 #define DEBUG_RPC_PROCESS_MAKE_FORK       0
 #define DEBUG_RPC_PROCESS_SIGACTION       0
 #define DEBUG_RPC_THREAD_USER_CREATE      0
 #define DEBUG_RPC_THREAD_KERNEL_CREATE    0
-#define DEBUG_RPC_VFS_DENTRY_CREATE       0
-#define DEBUG_RPC_VFS_DENTRY_DESTROY      0
-#define DEBUG_RPC_VFS_DEVICE_GET_DENTRY   0
-#define DEBUG_RPC_VFS_FILE_CREATE         0
-#define DEBUG_RPC_VFS_FILE_DESTROY        0
-#define DEBUG_RPC_VFS_FS_NEW_DENTRY       0
-#define DEBUG_RPC_VFS_FS_ADD_DENTRY       0
-#define DEBUG_RPC_VFS_INODE_CREATE        0
-#define DEBUG_RPC_VFS_INODE_DESTROY       0
+#define DEBUG_RPC_USER_DIR_CREATE         0
+#define DEBUG_RPC_USER_DIR_DESTROY        0
 #define DEBUG_RPC_VMM_CREATE_VSEG         0
-#define DEBUG_RPC_VMM_GET_PTE             0
-#define DEBUG_RPC_VMM_GET_VSEG            0
-#define DEBUG_RPC_VMM_DELETE_VSEG         0
+#define DEBUG_RPC_VMM_RESIZE_VSEG         0
+#define DEBUG_RPC_VMM_REMOVE_VSEG         0
+#define DEBUG_RPC_VMM_SET_COW             0
 
 #define DEBUG_RWLOCK_TYPE                 0        // lock type 0 is undefined => no debug
@@ -246,5 +240,5 @@
 #define DEBUG_VFS_FILE_CREATE             0
 #define DEBUG_VFS_GET_PATH                0
-#define DEBUG_VFS_INODE_CREATE            0 
+#define DEBUG_VFS_INODE_CREATE            0
 #define DEBUG_VFS_INODE_LOAD_ALL          0
 #define DEBUG_VFS_KERNEL_MOVE             0
@@ -290,12 +284,12 @@
 #define LOCK_VMM_STACK         3   // local  (B)  protect VMM stack vseg allocator   
 #define LOCK_VMM_MMAP          4   // local  (B)  protect VMM mmap vseg allocator
-#define LOCK_VFS_CTX           5   // local  (B)  protect vfs context state
-#define LOCK_KCM_STATE         6   // local  (B)  protect KCM allocator state
-#define LOCK_KHM_STATE         7   // local  (B)  protect KHM allocator state
-#define LOCK_HTAB_STATE        8   // local  (B)  protect a local htab state
-
+#define LOCK_KCM_STATE         5   // local  (B)  protect KCM allocator state
+#define LOCK_KHM_STATE         6   // local  (B)  protect KHM allocator state
+#define LOCK_HTAB_STATE        7   // local  (B)  protect a local htab state
+
+#define LOCK_VFS_CTX           8   // remote (B)  protect vfs context state
 #define LOCK_PPM_FREE          9   // remote (B)  protect PPM allocator free_pages lists
 #define LOCK_THREAD_JOIN      10   // remote (B)  protect join/exit between two threads
-#define LOCK_XHTAB_STATE      11   // remote (B)  protect  a distributed xhtab state
+#define LOCK_XHTAB_STATE      11   // remote (B)  protect a distributed xhtab state
 #define LOCK_CHDEV_QUEUE      12   // remote (B)  protect chdev threads waiting queue
 #define LOCK_CHDEV_TXT0       13   // remote (B)  protect access to kernel terminal TXT0
@@ -325,5 +319,6 @@
 #define LOCK_VFS_FILE         33   // remote (RW) protect file descriptor state
 #define LOCK_VFS_MAIN         34   // remote (RW) protect vfs traversal (in root inode)
-#define LOCK_FATFS_FAT        35   // remote (RW) protect exclusive access to the FATFS FAT
+#define LOCK_FATFS_FAT        35   // remote (RW) protect exclusive access to the VFS FAT
+#define LOCK_FBF_WINDOWS      36   // remote (RW) protect FBF windows set
 
 ////////////////////////////////////////////////////////////////////////////////////////////
@@ -383,4 +378,6 @@
 ////////////////////////////////////////////////////////////////////////////////////////////
 
+#define CONFIG_VFS_ROOT_CXY                 0          // VFS_ROOT and FAT mapper cluster
+
 #define CONFIG_VFS_MAX_INODES               128        // max number of inodes per cluster
 #define CONFIG_VFS_MAX_NAME_LENGTH          56         // dentry name max length (bytes)
@@ -397,9 +394,16 @@
 
 ////////////////////////////////////////////////////////////////////////////////////////////
+//                              FBF WINDOWS       
+////////////////////////////////////////////////////////////////////////////////////////////
+
+#define CONFIG_FBF_WINDOWS_MAX_NR           64         // max number of windows
+#define CONFIG_FBF_WINDOWS_MAX_WIDTH        1024       // max number of pixels in FBF line
+#define CONFIG_FBF_WINDOWS_MAX_HEIGHT       1024       // max number of lines in FBF
+
+////////////////////////////////////////////////////////////////////////////////////////////
 //                                  DQDT        
 ////////////////////////////////////////////////////////////////////////////////////////////
 
 #define CONFIG_DQDT_LEVELS_NR               5
-#define CONFIG_DQDT_TICKS_PER_QUANTUM       1        // number of ticks between updates
 
 ////////////////////////////////////////////////////////////////////////////////////////////
@@ -414,8 +418,8 @@
 ////////////////////////////////////////////////////////////////////////////////////////////
 
-#define CONFIG_SCHED_TICK_MS_PERIOD         10000   // number of milliseconds per period
-#define CONFIG_SCHED_TICKS_PER_QUANTUM      1       // number of ticks between scheduling
-#define CONFIG_SCHED_MAX_THREADS_NR         32      // max number of threads per core
-#define CONFIG_SCHED_IDLE_MODE_SLEEP        0       // idle thread use sleep mode if non 0
+#define CONFIG_SCHED_TICK_MS_PERIOD         10000    // number of milliseconds per period
+#define CONFIG_SCHED_TICKS_PER_QUANTUM      1        // number of ticks between scheduling
+#define CONFIG_SCHED_MAX_THREADS_NR         32       // max number of threads per core
+#define CONFIG_SCHED_IDLE_MODE_SLEEP        0        // idle thread use sleep mode if non 0
 
 ////////////////////////////////////////////////////////////////////////////////////////////
@@ -423,7 +427,7 @@
 ////////////////////////////////////////////////////////////////////////////////////////////
 
-#define CONFIG_THREADS_MAX_PER_CLUSTER      32      // max threads per cluster per process
-#define CONFIG_THREAD_DESC_SIZE             0x4000  // thread desc size (with kernel stack)
-#define CONFIG_THREAD_DESC_ORDER            2       // ln( number of 4K pages )
+#define CONFIG_THREADS_MAX_PER_CLUSTER      32       // max threads per cluster per process
+#define CONFIG_THREAD_DESC_SIZE             0x4000   // thread desc size (with kernel stack)
+#define CONFIG_THREAD_DESC_ORDER            2        // ln( number of 4K pages )
 
 ////////////////////////////////////////////////////////////////////////////////////////////
@@ -433,5 +437,5 @@
 #define CONFIG_REMOTE_FIFO_SLOTS		    16
 #define CONFIG_REMOTE_FIFO_MAX_ITERATIONS   1024
-#define CONFIG_RPC_THREADS_MAX              4      // max number of RPC threads per core
+#define CONFIG_RPC_THREADS_MAX              4       // max number of RPC threads per core
 
 ////////////////////////////////////////////////////////////////////////////////////////////
@@ -439,14 +443,14 @@
 ////////////////////////////////////////////////////////////////////////////////////////////
 
-#define CONFIG_VMM_VSPACE_SIZE        0x100000     // virtual space          : 4   Gbytes
-
-#define CONFIG_VMM_UTILS_BASE         0x000200     // UTILS zone base        : 2   Mbytes
-#define CONFIG_VMM_ELF_BASE           0x000400     // ELF zone base          : 4   Mbytes
-#define CONFIG_VMM_HEAP_BASE          0x040000     // HEAP zone base         : 32  Mbytes
-#define CONFIG_VMM_STACK_BASE         0x0C0000     // STACK zone base        : 3   Gbytes
-
-#define CONFIG_VMM_ARGS_SIZE          0x000004     // args vseg size         : 16  Kbytes
-#define CONFIG_VMM_ENVS_SIZE          0x000008     // envs vseg size         : 32  Kbytes
-#define CONFIG_VMM_STACK_SIZE         0x001000     // single stack vseg size : 16  Mbytes
+#define CONFIG_VMM_VSPACE_SIZE        0x100000      // virtual space          : 4   Gbytes
+
+#define CONFIG_VMM_UTILS_BASE         0x000200      // UTILS zone base        : 2   Mbytes
+#define CONFIG_VMM_ELF_BASE           0x000400      // ELF zone base          : 4   Mbytes
+#define CONFIG_VMM_HEAP_BASE          0x040000      // HEAP zone base         : 32  Mbytes
+#define CONFIG_VMM_STACK_BASE         0x0C0000      // STACK zone base        : 3   Gbytes
+ 
+#define CONFIG_VMM_ARGS_SIZE          0x000004      // args vseg size         : 16  Kbytes
+#define CONFIG_VMM_ENVS_SIZE          0x000008      // envs vseg size         : 32  Kbytes
+#define CONFIG_VMM_STACK_SIZE         0x001000      // single stack vseg size : 16  Mbytes
 
 #define CONFIG_VMM_HEAP_MAX_ORDER     18           // max size of MMAP vseg  :  1  Gbytes
@@ -455,10 +459,10 @@
 ////////////////////////////////////////////////////////////////////////////////////////////
 
-#define CONFIG_PPM_PAGE_SIZE          4096         // physical page size (bytes)
-#define CONFIG_PPM_PAGE_SHIFT         12           // physical page shift (bits)
-#define CONFIG_PPM_PAGE_MASK          0x00000FFF   // physical page mask     
-#define CONFIG_PPM_MAX_ORDER          16           // ln(total number of pages per cluster)
-#define CONFIG_PPM_HEAP_ORDER         10           // ln(number of heap pages per cluster)
-#define CONFIG_PPM_MAX_RSVD           32           // max reserved zones on the machine
+#define CONFIG_PPM_PAGE_SIZE          4096          // physical page size (bytes)
+#define CONFIG_PPM_PAGE_SHIFT         12            // physical page shift (bits)
+#define CONFIG_PPM_PAGE_MASK          0x00000FFF    // physical page mask     
+#define CONFIG_PPM_MAX_ORDER          16            // ln(total number of pages per cluster)
+#define CONFIG_PPM_HEAP_ORDER         10            // ln(number of heap pages per cluster)
+#define CONFIG_PPM_MAX_RSVD           32            // max reserved zones on the machine
 
 #define CONFIG_PPM_PAGE_ALIGNED       __attribute__((aligned(CONFIG_PPM_PAGE_SIZE)))
@@ -470,5 +474,6 @@
 #define CONFIG_INSTRUMENTATION_SYSCALLS    0
 #define CONFIG_INSTRUMENTATION_PGFAULTS    0
-#define CONFIG_INSTRUMENTATION_FOOTPRINT   0
+#define CONFIG_INSTRUMENTATION_FOOTPRINT   1
+#define CONFIG_INSTRUMENTATION_CHDEVS      0
 #define CONFIG_INSTRUMENTATION_GPT         0
 
Index: /trunk/kernel/libk/bits.c
===================================================================
--- /trunk/kernel/libk/bits.c	(revision 656)
+++ /trunk/kernel/libk/bits.c	(revision 657)
@@ -1,7 +1,7 @@
 /*
- * bits.c - bits manipulation functions implementation
+ * bits.c - bitmap API implementation
  *
  * Author  Ghassan Almaless (2008,2009,2010,2011,2012)
- *         Alain Greiner    (2016,2017,2018,2019)
+ *         Alain Greiner    (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -26,4 +26,8 @@
 #include <bits.h>
 
+//////////////////////////////////////////////////////////////////////////////
+//////////////     local access functions     ///////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
 ////////////////////////////////////
 void bitmap_init( bitmap_t * bitmap,
@@ -42,6 +46,6 @@
                         uint32_t   index )
 {
-	uint32_t  word = index / 32;
-	uint32_t  bit  = index % 32;
+	uint32_t  word = index >> 5;
+	uint32_t  bit  = index & 0x1F;
 
 	bitmap[word] |= ( 1 << bit );
@@ -52,6 +56,6 @@
                           uint32_t   index )
 {
-	uint32_t  word = index / 32;
-	uint32_t  bit  = index % 32;
+	uint32_t  word = index >> 5;
+	uint32_t  bit  = index & 0x1F;
 
 	bitmap[word] &= ~( 1 << bit );
@@ -62,9 +66,44 @@
                             uint32_t   index )
 {
-	uint32_t  word = index / 32;
-	uint32_t  bit  = index % 32;
+	uint32_t  word = index >> 5;
+	uint32_t  bit  = index & 0x1F;
 
 	return (bitmap[word] & ( 1 << bit )) != 0;
 }
+
+/////////////////////////////////////////
+uint32_t bitmap_alloc( bitmap_t * bitmap,
+                       uint32_t   size )
+{
+    uint32_t max_word;
+    uint32_t max_bit; 
+	uint32_t word;
+	uint32_t bit;
+ 
+    if( size )
+    {
+        max_word = ( (size-1) >>5 ) + 1;
+
+        for( word = 0 ; word < max_word ; word++ )
+	    {
+            max_bit = (word == (max_word - 1)) ? (size & 0x1F) : 32;
+
+		    if(bitmap[word] != 0XFFFFFFFF)
+		    {
+			    for(bit = 0 ; bit < max_bit ; bit++)
+			    {
+				    if( (bitmap[word] & (1 << bit)) == 0 )
+                    {
+                        bitmap[word] |= (1 << bit);
+                        return (word*32 + bit);
+                    }
+			    }
+            }
+		}
+	}
+
+	return -1;
+
+}  // end bitmap_alloc()
 
 //////////////////////////////////////////
@@ -207,4 +246,5 @@
 {
     uint32_t max_word;
+    uint32_t max_bit;
 	uint32_t word;
 	uint32_t bit;
@@ -216,7 +256,9 @@
         for( word = 0 ; word < max_word ; word++ )
 	    {
+            max_bit = (word == (max_word - 1)) ? (size & 0x1F) : 32;
+
 		    if(bitmap[word] != 0)
 		    {
-			    for(bit = 0 ; bit < 32 ; bit++)
+			    for(bit = 0 ; bit < max_bit ; bit++)
 			    {
 				    if( bitmap[word] & (1 << bit) ) return (word*32 + bit);
@@ -235,4 +277,5 @@
 {
     uint32_t max_word;
+    uint32_t max_bit;
 	uint32_t word;
 	uint32_t bit;
@@ -244,7 +287,9 @@
         for( word = 0 ; word < max_word ; word++ )
 	    {
+            max_bit = (word == (max_word - 1)) ? (size & 0x1F) : 32;
+
 		    if(bitmap[word] != 0XFFFFFFFF)
 		    {
-			    for(bit = 0 ; bit < 32 ; bit++)
+			    for(bit = 0 ; bit < max_bit ; bit++)
 			    {
 				    if( (bitmap[word] & (1 << bit)) == 0 ) return (word*32 + bit);
@@ -258,2 +303,131 @@
 }  // bitmap_ffc()
 
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////     remote access functions     ///////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////
+void bitmap_remote_init( xptr_t   bitmap_xp,
+                         uint32_t len )
+{
+    bitmap_t * bitmap_ptr = GET_PTR( bitmap_xp );
+    cxy_t      bitmap_cxy = GET_CXY( bitmap_xp );
+
+    uint32_t word;
+    uint32_t nwords = BITMAP_SIZE( len );
+
+    for( word = 0 ; word < nwords ; word++ )
+    {
+        hal_remote_s32( XPTR( bitmap_cxy , &bitmap_ptr[word] ) , 0 );
+    }
+}
+
+////////////////////////////////////////////////////
+inline void bitmap_remote_set( xptr_t     bitmap_xp,
+                               uint32_t   index )
+{
+    bitmap_t * bitmap_ptr = GET_PTR( bitmap_xp );
+    cxy_t      bitmap_cxy = GET_CXY( bitmap_xp );
+
+	uint32_t  word = index / 32;
+	uint32_t  bit  = index % 32;
+
+    hal_remote_atomic_or( XPTR( bitmap_cxy , &bitmap_ptr[word] ) , (1 <<bit) );
+}
+
+//////////////////////////////////////////////////////
+inline void bitmap_remote_clear( xptr_t     bitmap_xp, 
+                                 uint32_t   index )
+{
+    bitmap_t * bitmap_ptr = GET_PTR( bitmap_xp );
+    cxy_t      bitmap_cxy = GET_CXY( bitmap_xp );
+
+	uint32_t  word = index / 32;
+	uint32_t  bit  = index % 32;
+
+    hal_remote_atomic_and( XPTR( bitmap_cxy , &bitmap_ptr[word] ) , ~(1 <<bit) );
+}
+
+///////////////////////////////////////////////////
+uint32_t bitmap_remote_alloc( xptr_t     bitmap_xp,
+                              uint32_t   size )
+{
+    uint32_t max_word;
+    uint32_t max_bit; 
+	uint32_t word;
+	uint32_t bit;
+    xptr_t   word_xp;
+    uint32_t value;
+ 
+    bitmap_t * bitmap_ptr = GET_PTR( bitmap_xp );
+    cxy_t      bitmap_cxy = GET_CXY( bitmap_xp );
+
+    if( size )
+    {
+        max_word = ( (size-1) >>5 ) + 1;
+
+        for( word = 0 ; word < max_word ; word++ )
+	    {
+            max_bit = (word == (max_word - 1)) ? (size & 0x1F) : 32;
+
+            word_xp = XPTR( bitmap_cxy , &bitmap_ptr[word] );
+
+            value   = hal_remote_l32( word_xp );
+
+		    if( value != 0XFFFFFFFF )
+		    {
+			    for(bit = 0 ; bit < max_bit ; bit++)
+			    {
+				    if( (value & (1 << bit)) == 0 )
+                    {
+                        hal_remote_s32( word_xp , value | (1 << bit) );
+                        return (word*32 + bit);
+                    }
+			    }
+            }
+		}
+	}
+
+	return -1;
+
+}  // end bitmap_alloc()
+
+///////////////////////////////////////////////
+uint32_t bitmap_remote_ffc( xptr_t   bitmap_xp, 
+                                     uint32_t size )
+{
+    uint32_t max_word;
+    uint32_t max_bit;
+	uint32_t word;
+	uint32_t bit;
+    uint32_t value;
+ 
+    bitmap_t * bitmap_ptr = GET_PTR( bitmap_xp );
+    cxy_t      bitmap_cxy = GET_CXY( bitmap_xp );
+
+    if( size )
+    {
+        max_word = ( (size-1) >>5 ) + 1;
+
+        for( word = 0 ; word < max_word ; word++ )
+	    {
+            max_bit = (word == (max_word - 1)) ? (size & 0x1F) : 32;
+
+            value = hal_remote_l32( XPTR( bitmap_cxy , &bitmap_ptr[word] ) );
+
+		    if( value != 0xFFFFFFFF )
+		    {
+			    for(bit = 0 ; bit < max_bit ; bit++)
+			    {
+				    if( (value & (1 << bit)) == 0 ) return (word*32 + bit);
+			    }
+            }
+		}
+	}
+
+	return -1;
+
+}  // bitmap_remote_ffc()
+
+
Index: /trunk/kernel/libk/bits.h
===================================================================
--- /trunk/kernel/libk/bits.h	(revision 656)
+++ /trunk/kernel/libk/bits.h	(revision 657)
@@ -1,7 +1,7 @@
 /*
- * bits.h - bits manipulation helper functions
+ * bits.h - bitmap API definition
  *
  * Author   Ghassan Almaless (2008,2009,2010,2011,2012)
- *          Alain Greiner    (2016,2017,2018,2019)
+ *          Alain Greiner    (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -28,15 +28,21 @@
 #include <kernel_config.h>
 #include <hal_kernel_types.h>
-
-/*********************************************************************************************
- * These macros are NOT used by the bitmap, but can be useful in other contexts... [AG]
- *********************************************************************************************/
-
-#define ARROUND_UP(val, size) (((val) & ((size) -1)) ? ((val) & ~((size)-1)) + (size) : (val))
-#define ARROUND_DOWN(val, size)  ((val) & ~((size) - 1))
-
-#define ABS(x) (((x) < 0) ? -(x) : (x))
-#define MIN(x,y) (((x) < (y)) ? (x) : (y))
-#define MAX(x,y) (((x) < (y)) ? (y) : (x))
+#include <hal_remote.h>
+
+/**********************************************************************************************
+ * This file defines the API to access a generic bitmap, that can be local or remote.
+ * It is implemented as an array of uint32_t words.
+ * The number of entries in this array is statically defined at compile time
+ * and defines the max number of items that can be registered in the bitmap.
+ * The remote accesses are used in the VFS by the inum allocator.
+ *********************************************************************************************/
+
+typedef uint32_t    bitmap_t;
+
+/**********************************************************************************************
+ * This macro returns the number of 32 bits words required to register <size> entries.
+ *********************************************************************************************/
+
+#define BITMAP_SIZE(size) ( ((size) & 31) ? (((size)>>5) + 1) : ((size)>>5) )
 
 /**********************************************************************************************
@@ -44,4 +50,5 @@
  * It returns 0xFFFFFFFF if data is larger than 0x80000000.
  *********************************************************************************************/
+
 #define POW2_ROUNDUP(data) ( (data <= 0x00000001) ? 0x00000001  : \
                              (data <= 0x00000002) ? 0x00000002  : \
@@ -77,23 +84,29 @@
                              (data <= 0x80000000) ? 0x80000000  : 0xFFFFFFFF )
 
-/**********************************************************************************************
- * This macro returns the number of 32 bits words required to register <size> entries.
- *********************************************************************************************/
-
-#define BITMAP_SIZE(size) ( ((size) & 31) ? (((size)>>5) + 1) : ((size)>>5) )
-
-typedef uint32_t    bitmap_t;
-
-/*********************************************************************************************
- * This function reset all bits in a bitmap. (array ot 32 bits words).
+/*********************************************************************************************
+ * These macros are NOT used by the bitmap, but are useful in other contexts... [AG]
+ *********************************************************************************************/
+
+#define ARROUND_UP(val, size) (((val) & ((size) -1)) ? ((val) & ~((size)-1)) + (size) : (val))
+#define ARROUND_DOWN(val, size)  ((val) & ~((size) - 1))
+
+#define ABS(x) (((x) < 0) ? -(x) : (x))
+#define MIN(x,y) (((x) < (y)) ? (x) : (y))
+#define MAX(x,y) (((x) < (y)) ? (y) : (x))
+
+/*********************************************************************************************
+ * This function reset all bits in a local or remote bitmap. 
  *********************************************************************************************
  * @ bitmap  : pointer on first word in the bitmap.
- * @ len     : number of bits to reset. 
- ********************************************************************************************/
-void bitmap_init( bitmap_t * bitmap,
-                  uint32_t   len );
-
-/*********************************************************************************************
- * This function set a specific bit in a bitmap.
+ * @ size    : number of bits in bitmap. 
+ ********************************************************************************************/
+extern void bitmap_init( bitmap_t * bitmap,
+                         uint32_t   size );
+
+extern void bitmap_remote_init( xptr_t   bitmap_xp,
+                                uint32_t size );
+
+/*********************************************************************************************
+ * These functions set a specific bit in a local or remote bitmap.
  *********************************************************************************************
  * @ bitmap  : pointer on the bitmap
@@ -103,6 +116,9 @@
                                uint32_t   index );
 
-/*********************************************************************************************
- * This function clear a specific bit in a bitmap.
+extern inline void bitmap_remote_set( xptr_t    bitmap_xp,
+                                      uint32_t  index );
+
+/*********************************************************************************************
+ * These functions clear a specific bit in a local or remote bitmap.
  *********************************************************************************************
  * @ bitmap  : pointer on the bitmap
@@ -112,6 +128,24 @@
                                  uint32_t   index );
 
-/*********************************************************************************************
- * This function returns a specific bit in a bitmap.
+extern inline void bitmap_remote_clear( xptr_t     bitmap_xp, 
+                                        uint32_t   index );
+
+/*********************************************************************************************
+ * These functions search the first bit non-set in a local or remote bitmap, in the
+ * range [0 , size-1], set this bit, and return the index of the found bit.
+ * The lock protecting the bitmap must be taken by the caller.
+ *********************************************************************************************
+ * @ bitmap  : pointer on the bitmap.
+ * @ size    : number of bits to scan.
+ * @ returns index of found bit / returns 0xFFFFFFFF if not found.
+ ********************************************************************************************/
+extern uint32_t bitmap_alloc( bitmap_t * bitmap, 
+                              uint32_t   size );
+
+extern uint32_t bitmap_remote_alloc( xptr_t    bitmap_xp,
+                                     uint32_t  size );
+
+/*********************************************************************************************
+ * This function returns the index of aa specific bit in a bitmap.
  *********************************************************************************************
  * @ bitmap  : pointer on the bitmap
@@ -179,5 +213,6 @@
 
 /*********************************************************************************************
- * This function returns the index of first bit cleared in a bitmap, starting from bit 0.
+ * These functions return the index of first bit cleared in a local or remote bitmap,
+ * starting from bit 0.
  *********************************************************************************************
  * @ bitmap  : pointer on the bitmap
@@ -187,4 +222,7 @@
 extern uint32_t bitmap_ffc( bitmap_t * bitmap, 
                             uint32_t   size );
+
+extern uint32_t bitmap_remote_ffc( xptr_t   bitmap_xp, 
+                                   uint32_t size );
 
 /*********************************************************************************************
Index: /trunk/kernel/libk/elf.c
===================================================================
--- /trunk/kernel/libk/elf.c	(revision 656)
+++ /trunk/kernel/libk/elf.c	(revision 657)
@@ -224,7 +224,9 @@
 	error_t      error;
 
-    // get file name for error reporting and debug
+    // get file cluster and local pointer
     cxy_t         file_cxy = GET_CXY( file_xp );
     vfs_file_t  * file_ptr = GET_PTR( file_xp );
+
+    // get file name for error reporting and debug
     vfs_inode_t * inode    = hal_remote_lpt( XPTR( file_cxy , &file_ptr->inode ) );
     vfs_inode_get_name( XPTR( file_cxy , inode ) , name );
Index: /trunk/kernel/libk/grdxt.c
===================================================================
--- /trunk/kernel/libk/grdxt.c	(revision 656)
+++ /trunk/kernel/libk/grdxt.c	(revision 657)
@@ -332,4 +332,112 @@
 ////////////////////////////////////////////////////////////////////////////////////////
 
+////////////////////////////////////////////
+error_t grdxt_remote_init( xptr_t     rt_xp,
+                           uint32_t   ix1_width,
+                           uint32_t   ix2_width,
+                           uint32_t   ix3_width )
+{
+    void      ** root;
+	kmem_req_t   req;
+
+    // get cluster and local pointer
+    cxy_t     rt_cxy = GET_CXY( rt_xp );
+    grdxt_t * rt_ptr = GET_PTR( rt_xp );
+  
+    // initialize widths
+    hal_remote_s32( XPTR( rt_cxy , &rt_ptr->ix1_width ) , ix1_width );
+    hal_remote_s32( XPTR( rt_cxy , &rt_ptr->ix2_width ) , ix2_width );
+    hal_remote_s32( XPTR( rt_cxy , &rt_ptr->ix3_width ) , ix3_width );
+
+    // allocates first level array
+	req.type  = KMEM_KCM;
+	req.order = ix1_width + ( (sizeof(void*) == 4) ? 2 : 3 );
+	req.flags = AF_KERNEL | AF_ZERO;
+	root      = kmem_remote_alloc( rt_cxy , &req );
+
+	if( root == NULL )
+    {
+        printk("\n[ERROR] in %s : cannot allocate first level array\n", __FUNCTION__);
+        return -1;
+    }
+  
+    // register first level array in rt descriptor
+    hal_remote_spt( XPTR( rt_cxy , &rt_ptr->root ) , root );
+ 
+	return 0;
+
+}  // end grdxt_remote_init()
+
+//////////////////////////////////////////
+void grdxt_remote_destroy( xptr_t  rt_xp )
+{
+	kmem_req_t req;
+
+    uint32_t   w1;
+    uint32_t   w2;
+    uint32_t   w3;
+
+	uint32_t   ix1;
+	uint32_t   ix2;
+	uint32_t   ix3;
+
+    void    ** ptr1;
+    void    ** ptr2;
+    void    ** ptr3;
+
+    // get cluster and local pointer
+    cxy_t     rt_cxy = GET_CXY( rt_xp );
+    grdxt_t * rt_ptr = GET_PTR( rt_xp );
+
+    // get widths
+    w1 = hal_remote_l32( XPTR( rt_cxy , &rt_ptr->ix1_width ) );
+    w2 = hal_remote_l32( XPTR( rt_cxy , &rt_ptr->ix2_width ) );
+    w3 = hal_remote_l32( XPTR( rt_cxy , &rt_ptr->ix3_width ) );
+
+    // get ptr1
+    ptr1 = hal_remote_lpt( XPTR( rt_cxy , &rt_ptr->root ) );
+
+	for( ix1=0 ; ix1 < (uint32_t)(1 << w1) ; ix1++ )
+	{
+        // get ptr2
+        ptr2 = hal_remote_lpt( XPTR( rt_cxy , &ptr1[ix1] ) );
+
+		if( ptr2 == NULL ) continue;
+
+        for( ix2=0 ; ix2 < (uint32_t)(1 << w2) ; ix2++ )
+        {
+            // get ptr2
+            ptr3 = hal_remote_lpt( XPTR( rt_cxy , &ptr2[ix2] ) );
+
+		    if( ptr3 == NULL ) continue;
+
+            for( ix3=0 ; ix3 < (uint32_t)(1 << w3) ; ix3++ )
+            {
+                 if( ptr3[ix3] != NULL )
+                 {
+                     printk("\n[WARNING] in %s : ptr3[%d][%d][%d] non empty\n",
+                     __FUNCTION__, ix1, ix2, ix3 );
+                 }
+            }
+
+            // release level 3 array 
+            req.type = KMEM_KCM;
+		    req.ptr  = ptr3;
+		    kmem_remote_free( rt_cxy , &req );
+        }
+
+        // release level 2 array
+        req.type = KMEM_KCM;
+		req.ptr  = ptr2;
+	    kmem_remote_free( rt_cxy , &req );
+    }
+
+    // release level 1 array
+    req.type = KMEM_KCM;
+	req.ptr  = ptr1;
+    kmem_remote_free( rt_cxy , &req );
+
+}  // end grdxt_remote_destroy()
+
 //////////////////////////////////////////////
 error_t grdxt_remote_insert( xptr_t     rt_xp,
@@ -464,5 +572,5 @@
 
 ////////////////////////////////////////////
-void * grdxt_remote_remove( xptr_t    rt_xp,
+xptr_t grdxt_remote_remove( xptr_t    rt_xp,
                             uint32_t  key )
 {
@@ -489,9 +597,9 @@
     // get ptr2
 	void ** ptr2 = hal_remote_lpt( XPTR( rt_cxy , &ptr1[ix1] ) );
-	if( ptr2 == NULL ) return NULL;
+	if( ptr2 == NULL ) return XPTR_NULL;
 
     // get ptr3
 	void ** ptr3 = hal_remote_lpt( XPTR( rt_cxy , &ptr2[ix2] ) );
-	if( ptr3 == NULL ) return NULL;
+	if( ptr3 == NULL ) return XPTR_NULL;
 
     // get value
@@ -502,5 +610,5 @@
 	hal_fence();
 
-	return value;
+	return XPTR( rt_cxy , value );
 
 }  // end grdxt_remote_remove()
@@ -523,7 +631,7 @@
 
     // compute indexes
-    uint32_t        ix1 = key >> (w2 + w3);              // index in level 1 array
-	uint32_t        ix2 = (key >> w3) & ((1 << w2) -1);  // index in level 2 array
-	uint32_t        ix3 = key & ((1 << w3) - 1);         // index in level 3 array
+    uint32_t       ix1 = key >> (w2 + w3);              // index in level 1 array
+	uint32_t       ix2 = (key >> w3) & ((1 << w2) -1);  // index in level 2 array
+	uint32_t       ix3 = key & ((1 << w3) - 1);         // index in level 3 array
 
     // get ptr1
@@ -546,4 +654,70 @@
 
 }  // end grdxt_remote_lookup()
+
+////////////////////////////////////////////////
+xptr_t grdxt_remote_get_first( xptr_t     rt_xp,
+                               uint32_t   start_key,
+                               uint32_t * found_key )
+{
+    uint32_t        ix1;
+    uint32_t        ix2;
+    uint32_t        ix3;
+
+    void        ** ptr1;       // local base address of remote first level array
+    void        ** ptr2;       // local base address of remote second level array
+    void        ** ptr3;       // local base address of remote third level array
+
+    // get cluster and local pointer on remote rt descriptor
+    grdxt_t       * rt_ptr = GET_PTR( rt_xp );
+    cxy_t           rt_cxy = GET_CXY( rt_xp );
+
+    // get widths
+    uint32_t        w1 = hal_remote_l32( XPTR( rt_cxy , &rt_ptr->ix1_width ) );
+    uint32_t        w2 = hal_remote_l32( XPTR( rt_cxy , &rt_ptr->ix2_width ) );
+    uint32_t        w3 = hal_remote_l32( XPTR( rt_cxy , &rt_ptr->ix3_width ) );
+
+// Check key value
+assert( ((start_key >> (w1 + w2 + w3)) == 0 ), "illegal key value %x\n", start_key );
+
+    // compute min indexes
+    uint32_t       min1 = start_key >> (w2 + w3);            
+	uint32_t       min2 = (start_key >> w3) & ((1 << w2) -1);
+	uint32_t       min3 = start_key & ((1 << w3) - 1);  
+
+    // compute max indexes
+    uint32_t       max1 = 1 << w1;
+    uint32_t       max2 = 1 << w2;
+    uint32_t       max3 = 1 << w3;
+
+    // get ptr1
+    ptr1 = hal_remote_lpt( XPTR( rt_cxy , &rt_ptr->root ) );
+
+    for( ix1 = min1 ; ix1 < max1 ; ix1++ )
+    {
+        ptr2 = hal_remote_lpt( XPTR( rt_cxy , &ptr1[ix1] ) );
+        if( ptr2 == NULL ) continue;
+
+        for( ix2 = min2 ; ix2 < max2 ; ix2++ )
+        {
+            ptr3 = hal_remote_lpt( XPTR( rt_cxy , &ptr2[ix2] ) );
+            if( ptr3 == NULL ) continue;
+
+            for( ix3 = min3 ; ix3 < max3 ; ix3++ )
+            {
+                void * item = hal_remote_lpt( XPTR( rt_cxy , &ptr3[ix3] ) );
+
+                if( item == NULL ) continue;
+                else                    
+                {
+                    *found_key = (ix1 << (w2+w3)) | (ix2 << w3) | ix3;
+                    return XPTR( rt_cxy , item );
+                }
+            }
+        }
+    }
+
+    return XPTR_NULL;
+
+}  // end grdxt_remote_get_first()
 
 /////////////////////////i/////////////////
Index: /trunk/kernel/libk/grdxt.h
===================================================================
--- /trunk/kernel/libk/grdxt.h	(revision 656)
+++ /trunk/kernel/libk/grdxt.h	(revision 657)
@@ -2,5 +2,5 @@
  * grdxt.h - Three-levels Generic Radix-tree definition.
  * 
- * Authors  Alain Greiner (2016,2017,2018,2019)
+ * Authors  Alain Greiner (2016,2017,2018,2019,2019)
  *
  * Copyright  UPMC Sorbonne Universites
@@ -124,6 +124,6 @@
 /*******************************************************************************************
  * This function scan all radix-tree entries in increasing key order, starting from
- * the value defined by the <start_key> argument, and return a pointer on the first valid
- * registered item, and the found item key value.
+ * the key defined by the <start_key> argument. It returns a pointer on the first valid
+ * registered item, and returns in the <found_key> buffer the found item key value.
  * It must be called by a local thread.
  *******************************************************************************************
@@ -142,6 +142,32 @@
 
 /*******************************************************************************************
+ * This function initialises the radix-tree descriptor,
+ * and allocates memory for the first level array of pointers.
+ * It can be called by any thread running in any cluster
+ *******************************************************************************************
+ * @ rt_xp     : extended pointer on the radix-tree descriptor.
+ * @ ix1_width : number of bits in ix1 field
+ * @ ix2_width : number of bits in ix2 field
+ * @ ix3_width : number of bits in ix3 field
+ * @ returns 0 if success / returns ENOMEM if no more memory.      
+ ******************************************************************************************/
+error_t grdxt_remote_init( xptr_t     rt_xp,
+                           uint32_t   ix1_width,
+                           uint32_t   ix2_width,
+                           uint32_t   ix3_width );
+
+/*******************************************************************************************
+ * This function releases all memory allocated to the radix-tree infrastructure.
+ * A warning message is printed on the kernel TXT0 if the radix tree is not empty.
+ * It can be called by any thread running in any cluster
+ *******************************************************************************************
+ * @ rt_xp    : extended pointer on the radix-tree descriptor.
+ ******************************************************************************************/
+void grdxt_remote_destroy( xptr_t  rt_xp );
+
+/*******************************************************************************************
  * This function insert a new item in a - possibly remote - radix tree.
  * It dynamically allocates memory for new second and third level arrays if required.
+ * It can be called by any thread running in any cluster
  *******************************************************************************************
  * @ rt_xp   : extended pointer on the radix-tree descriptor.
@@ -157,10 +183,11 @@
  * This function removes an item identified by its key from a - possibly remote - radix
  * tree, and returns a local pointer on the removed item. No memory is released.
+ * It can be called by a thread running in any cluster
  *******************************************************************************************
  * @ rt_xp   : pointer on the radix-tree descriptor.
  * @ key     : key value.
- * @ returns local pointer on removed item if success / returns NULL if failure.
- ******************************************************************************************/
-void * grdxt_remote_remove( xptr_t    rt_xp,
+ * @ returns extended pointer on removed item if success / returns XPTR_NULL if failure.
+ ******************************************************************************************/
+xptr_t grdxt_remote_remove( xptr_t    rt_xp,
                             uint32_t  key );
 
@@ -169,8 +196,9 @@
  * on the item identified by the <key> argument, from the radix tree identified by
  * the <rt_xp> remote pointer.
+ * It can be called by a thread running in any cluster
  *******************************************************************************************
  * @ rt_xp   : extended pointer on the radix-tree descriptor.
  * @ key     : key value.
- * @ returns an extended pointer on found item if success / returns XPTR_NULL if failure.
+ * @ returns extended pointer on found item if success / returns XPTR_NULL if not found.
  ******************************************************************************************/
 xptr_t grdxt_remote_lookup( xptr_t     rt_xp, 
@@ -178,5 +206,22 @@
 
 /*******************************************************************************************
+ * This function scan all radix-tree entries of a - possibly remote - radix tree <rt_xp>,
+ * in increasing key order, starting from the key defined by the <start_key> argument.
+ * It returns an extended pointer on the first valid registered item, and returns in the
+ * <found_key> buffer the found item key value.
+ * It can be called by a thread running in any cluster
+ *******************************************************************************************
+ * @ rt_xp      : extended pointer on the radix-tree descriptor.
+ * @ start_key  : key starting value for the scan.
+ * @ found_key  : [out] buffer for found key value.
+ * @ return xptr on first valid item if found / return XPTR_NULL if no item found.
+ ******************************************************************************************/
+xptr_t grdxt_remote_get_first( xptr_t     rt_xp,
+                               uint32_t   start_key,
+                               uint32_t * found_key );
+
+/*******************************************************************************************
  * This function displays the current content of a possibly remote radix_tree.
+ * It can be called by a thread running in any cluster
  *******************************************************************************************
  * @ rt      : extended pointer on the radix-tree descriptor.
Index: /trunk/kernel/libk/remote_buf.c
===================================================================
--- /trunk/kernel/libk/remote_buf.c	(revision 657)
+++ /trunk/kernel/libk/remote_buf.c	(revision 657)
@@ -0,0 +1,332 @@
+/*
+ * remote_buf.c Remotely accessible, circular buffer implementation.
+ *
+ * Authors : Alain Greiner  (2016,2017,2018,2019,2020)
+ *
+ * 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 <hal_kernel_types.h>
+#include <hal_irqmask.h>
+#include <hal_remote.h>
+#include <hal_uspace.h>
+#include <bits.h>
+#include <memcpy.h>
+#include <kmem.h>
+#include <remote_buf.h>
+
+///////////////////////////////////////////
+error_t remote_buf_create( xptr_t   buf_xp,
+                           uint32_t size )
+{
+    kmem_req_t     req;
+    uint8_t      * data;
+
+    remote_buf_t * buf_ptr = GET_PTR( buf_xp );
+    cxy_t          buf_cxy = GET_CXY( buf_xp );
+
+    // allocate the data buffer
+    if( size >= CONFIG_PPM_PAGE_SIZE )
+    {
+        req.type  = KMEM_PPM;
+        req.order = bits_log2( size >> CONFIG_PPM_PAGE_SHIFT );
+        req.flags = AF_NONE;
+        data = kmem_remote_alloc( buf_cxy , &req );
+    }
+    else
+    {
+        req.type  = KMEM_KCM;
+        req.order = bits_log2( size );
+        req.flags = AF_NONE;
+        data = kmem_remote_alloc( buf_cxy , &req );
+    }
+
+    if( data == NULL )  return -1;
+
+    // initialize buffer descriptor
+    hal_remote_s32( XPTR( buf_cxy , &buf_ptr->size ) , size );
+    hal_remote_s32( XPTR( buf_cxy , &buf_ptr->ptw  ) , 0 );
+    hal_remote_s32( XPTR( buf_cxy , &buf_ptr->ptr  ) , 0 );
+    hal_remote_s32( XPTR( buf_cxy , &buf_ptr->sts  ) , 0 );
+    hal_remote_spt( XPTR( buf_cxy , &buf_ptr->data ) , data );
+
+    return 0;
+
+}  // end remote_buf_create()
+
+/////////////////////////////////////////
+void remote_buf_destroy( xptr_t  buf_xp )
+{
+    kmem_req_t   req;
+
+    remote_buf_t * buf_ptr = GET_PTR( buf_xp );
+    cxy_t          buf_cxy = GET_CXY( buf_xp );
+
+    // release memory allocated to data buffer
+    if( buf_ptr->size >= CONFIG_PPM_PAGE_SIZE )
+    {
+        req.type  = KMEM_PPM;
+        req.ptr   = hal_remote_lpt( XPTR( buf_cxy , &buf_ptr->data ) );
+        kmem_remote_free( buf_cxy , &req );
+    }
+    else
+    {
+        req.type  = KMEM_KCM;
+        req.ptr   = hal_remote_lpt( XPTR( buf_cxy , &buf_ptr->data ) );
+        kmem_remote_free( buf_cxy , &req );
+    }
+}
+
+/////////////////////////////////////////
+void remote_buf_reset( xptr_t    buf_xp )
+{
+    remote_buf_t * buf_ptr = GET_PTR( buf_xp );
+    cxy_t          buf_cxy = GET_CXY( buf_xp );
+
+    hal_remote_s32( XPTR( buf_cxy , &buf_ptr->ptw ) , 0 );
+    hal_remote_s32( XPTR( buf_cxy , &buf_ptr->ptr ) , 0 );
+    hal_remote_s32( XPTR( buf_cxy , &buf_ptr->sts ) , 0 );
+}
+
+/////////////////////////////////////////////////
+error_t remote_buf_get_to_user( xptr_t    buf_xp,
+                                uint8_t * u_buf, 
+                                uint32_t  nbytes )
+{
+    remote_buf_t * buf_ptr = GET_PTR( buf_xp );
+    cxy_t          buf_cxy = GET_CXY( buf_xp );
+
+    // build relevant extended pointers
+    xptr_t sts_xp  = XPTR( buf_cxy , &buf_ptr->sts );
+    xptr_t ptr_xp  = XPTR( buf_cxy , &buf_ptr->ptr );
+    xptr_t size_xp = XPTR( buf_cxy , &buf_ptr->size );
+    xptr_t data_xp = XPTR( buf_cxy , &buf_ptr->data );
+
+    // get relevant infos from remote buffer descriptor
+    uint32_t  sts  = hal_remote_l32( sts_xp );
+    uint32_t  ptr  = hal_remote_l32( ptr_xp );
+    uint32_t  size = hal_remote_l32( size_xp );
+    uint8_t * data = hal_remote_lpt( data_xp );
+
+    // check enough bytes in buffer
+    if( nbytes > sts ) return -1;
+
+    // move nbytes 
+    if( (ptr + nbytes) <= size)  // no wrap around => one move
+    {
+        hal_copy_to_uspace( u_buf,
+                            XPTR( buf_cxy , data + ptr ),
+                            nbytes );
+    }
+    else                         // wrap around => two moves
+    {
+        uint32_t bytes_1 = size - ptr;
+        uint32_t bytes_2 = nbytes - bytes_1;
+     
+        hal_copy_to_uspace( u_buf,
+                            XPTR( buf_cxy , data + ptr ),
+                            bytes_1 );
+
+        hal_copy_to_uspace( u_buf + bytes_1,
+                            XPTR( buf_cxy , data ),
+                            bytes_2 );
+    }
+
+    // update ptr in buffer descriptor
+    hal_remote_s32( ptr_xp , (ptr + nbytes) % size );
+
+    // atomically update sts
+    hal_remote_atomic_add( sts_xp , -nbytes );
+
+    return 0;
+
+}  // end remote_buf_get_to_user()
+
+///////////////////////////////////////////////////
+error_t remote_buf_get_to_kernel( xptr_t    buf_xp,
+                                  uint8_t * k_buf,
+                                  uint32_t  nbytes )
+{
+    remote_buf_t * buf_ptr = GET_PTR( buf_xp );
+    cxy_t          buf_cxy = GET_CXY( buf_xp );
+
+    // build relevant extended pointers
+    xptr_t sts_xp  = XPTR( buf_cxy , &buf_ptr->sts );
+    xptr_t ptr_xp  = XPTR( buf_cxy , &buf_ptr->ptr );
+    xptr_t size_xp = XPTR( buf_cxy , &buf_ptr->size );
+    xptr_t data_xp = XPTR( buf_cxy , &buf_ptr->data );
+
+    // get relevant infos from remote buffer descriptor
+    uint32_t  sts  = hal_remote_l32( sts_xp );
+    uint32_t  ptr  = hal_remote_l32( ptr_xp );
+    uint32_t  size = hal_remote_l32( size_xp );
+    uint8_t * data = hal_remote_lpt( data_xp );
+
+    // check enough bytes in buffer
+    if( nbytes > sts ) return -1;
+
+    // move nbytes 
+    if( (ptr + nbytes) <= size)  // no wrap around => one move
+    {
+        hal_remote_memcpy( XPTR( local_cxy , k_buf ),
+                           XPTR( buf_cxy   , data + ptr ),
+                           nbytes );
+    }
+    else                         // wrap around => two moves
+    {
+        uint32_t bytes_1 = size - ptr;
+        uint32_t bytes_2 = nbytes - bytes_1;
+     
+        hal_remote_memcpy( XPTR( local_cxy , k_buf ),
+                           XPTR( buf_cxy   , data + ptr ),
+                           bytes_1 );
+
+        hal_remote_memcpy( XPTR( local_cxy , k_buf + bytes_1 ),
+                           XPTR( buf_cxy   , data ),
+                           bytes_2 );
+    }
+
+    // update ptr in buffer descriptor
+    hal_remote_s32( ptr_xp , (ptr + nbytes) % size );
+
+    // atomically update sts
+    hal_remote_atomic_add( sts_xp , -nbytes );
+
+    return 0;
+
+}  // end remote_buf_get_to_user()
+
+///////////////////////////////////////////////////
+error_t remote_buf_put_from_user( xptr_t    buf_xp,
+                                  uint8_t * u_buf,
+                                  uint32_t  nbytes )
+{
+    remote_buf_t * buf_ptr = GET_PTR( buf_xp );
+    cxy_t          buf_cxy = GET_CXY( buf_xp );
+
+    // build relevant extended pointers
+    xptr_t sts_xp  = XPTR( buf_cxy , &buf_ptr->sts );
+    xptr_t ptw_xp  = XPTR( buf_cxy , &buf_ptr->ptw );
+    xptr_t size_xp = XPTR( buf_cxy , &buf_ptr->size );
+    xptr_t data_xp = XPTR( buf_cxy , &buf_ptr->data );
+
+    // get relevant infos from remote buffer descriptor
+    uint32_t  sts  = hal_remote_l32( sts_xp );
+    uint32_t  ptw  = hal_remote_l32( ptw_xp );
+    uint32_t  size = hal_remote_l32( size_xp );
+    uint8_t * data = hal_remote_lpt( data_xp );
+
+    // check enough space in buffer
+    if( nbytes > (size - sts) ) return -1;
+
+    // move nbytes 
+    if( (ptw + nbytes) <= size)  // no wrap around => one move
+    {
+        hal_copy_from_uspace( XPTR( buf_cxy , data + ptw ),
+                              u_buf,
+                              nbytes );
+    }
+    else                         // wrap around => two moves
+    {
+        uint32_t bytes_1 = size - ptw;
+        uint32_t bytes_2 = nbytes - bytes_1;
+     
+        hal_copy_from_uspace( XPTR( buf_cxy , data + ptw ),
+                              u_buf,
+                              bytes_1 );
+
+        hal_copy_from_uspace( XPTR( buf_cxy , data ),
+                              u_buf + bytes_1,
+                              bytes_2 );
+    }
+
+    // update ptw in buffer descriptor
+    hal_remote_s32( ptw_xp , (ptw + nbytes) % size );
+
+    // atomically update sts
+    hal_remote_atomic_add( sts_xp , nbytes );
+
+    return 0;
+
+}  // end remote_buf_put_from_user()
+
+/////////////////////////////////////////////////////
+error_t remote_buf_put_from_kernel( xptr_t    buf_xp,
+                                    uint8_t * k_buf, 
+                                    uint32_t  nbytes )
+{
+    remote_buf_t * buf_ptr = GET_PTR( buf_xp );
+    cxy_t          buf_cxy = GET_CXY( buf_xp );
+
+    // build relevant extended pointers
+    xptr_t sts_xp  = XPTR( buf_cxy , &buf_ptr->sts );
+    xptr_t ptw_xp  = XPTR( buf_cxy , &buf_ptr->ptw );
+    xptr_t size_xp = XPTR( buf_cxy , &buf_ptr->size );
+    xptr_t data_xp = XPTR( buf_cxy , &buf_ptr->data );
+
+    // get relevant infos from remote buffer descriptor
+    uint32_t  sts  = hal_remote_l32( sts_xp );
+    uint32_t  ptw  = hal_remote_l32( ptw_xp );
+    uint32_t  size = hal_remote_l32( size_xp );
+    uint8_t * data = hal_remote_lpt( data_xp );
+
+    // check enough space in buffer
+    if( nbytes > (size - sts) ) return -1;
+
+    // move nbytes 
+    if( (ptw + nbytes) <= size)  // no wrap around => one move
+    {
+        hal_remote_memcpy( XPTR( buf_cxy   , data + ptw ),
+                           XPTR( local_cxy , k_buf ),
+                           nbytes );
+    }
+    else                         // wrap around => two moves
+    {
+        uint32_t bytes_1 = size - ptw;
+        uint32_t bytes_2 = nbytes - bytes_1;
+     
+        hal_remote_memcpy( XPTR( buf_cxy   , data + ptw ),
+                           XPTR( local_cxy , k_buf ),
+                           bytes_1 );
+
+        hal_remote_memcpy( XPTR( buf_cxy   , data ),
+                           XPTR( local_cxy , k_buf + bytes_1 ),
+                           bytes_2 );
+    }
+
+    // update ptw in buffer descriptor
+    hal_remote_s32( ptw_xp , (ptw + nbytes) % size );
+
+    // atomically update sts
+    hal_remote_atomic_add( sts_xp , nbytes );
+
+    return 0;
+
+}  // end remote_buf_put_from_kernel()
+
+////////////////////////////////////////////
+uint32_t remote_buf_status( xptr_t  buf_xp )
+{
+    remote_buf_t * buf_ptr = GET_PTR( buf_xp );
+    cxy_t          buf_cxy = GET_CXY( buf_xp );
+
+    return hal_remote_l32( XPTR( buf_cxy , &buf_ptr->sts ) );
+    
+}  // end remote_buf_status()
+
+
Index: /trunk/kernel/libk/remote_buf.h
===================================================================
--- /trunk/kernel/libk/remote_buf.h	(revision 657)
+++ /trunk/kernel/libk/remote_buf.h	(revision 657)
@@ -0,0 +1,157 @@
+/*
+ * remote_buf.h -  Remotely accessible, circular buffer definition.
+ *
+ * Authors : 
+ *           Alain Greiner (2016,2017,2018,2019,2020)
+ *
+ * Copyright (c) UPMC Sorbonne Universites
+ *
+ * This file is part of ALMOS-MKH.
+ *
+ * ALMOS-MHH 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
+ */
+
+#ifndef _REMOTE_BUFFER_H_
+#define _REMOTE_BUFFER_H_
+
+#include <kernel_config.h>
+#include <hal_kernel_types.h>
+
+/************************************************************************************
+ * This structure and the associated access functions define a remotely accessible,
+ * kernel buffer, handled as a single-reader & single-writer FIFO. 
+ * Each buffer slot contains one byte, and the number of slots is dynamically 
+ * defined by an argument of the "remote_buf_create()" function. 
+ * The "put()" and "get()" access functions defined below move another buffer,
+ * (can be in kernel or in user space) to/from this circular kernel buffer.
+ * This structure is NOT protected by a lock, as the only shared variable is the 
+ * "sts" variable, that is updated by hal_remote_atomic_add() access primitives.
+ * It is used by to implement the socket receive buffer.
+ * - the "ptw" field defines the first empty slot (for write).
+ * - the "ptr" field defines the first non empty slot (for read).
+ * - the "sts" field defines the total number of non empty slots.
+ ***********************************************************************************/
+
+typedef struct remote_buf_s
+{
+    uint32_t   size;    /*! number of slots in data buffer                         */
+	uint32_t   ptw;     /*! first empty slot index                                 */
+	uint32_t   ptr;     /*! first non-empty slot index                             */
+    uint32_t   sts;     /*! current number of non empty slots                      */
+	uint8_t  * data;    /*! local pointer on local data buffer                     */
+}
+remote_buf_t;
+
+/************************************************************************************
+ * This function initializes a remote_buf descriptor identified by the <buf_xp>
+ * argument. It allocates memory for the data, as defined by the <size> argument,
+ * and initializes the buffer as empty. It must be called by a local thread.
+ ************************************************************************************
+ * @ buf_xp    : [in] extended pointer on buffer descriptor.
+ * @ size      : [in] number of bytes in buffer.
+ * @ return  0 if success / return -1 if failure (no memory).
+ ***********************************************************************************/
+error_t remote_buf_create( xptr_t   buf_xp,
+                           uint32_t size );
+
+/************************************************************************************
+ * This function releases memory allocated for the data buffer of a remote-buf
+ * descriptor identified by the <buf_xp> argument. 
+ * It must be called by a local thread.
+ ************************************************************************************
+ * @ buf_xp      : [in] extended pointer on buffer descriptor.
+ ***********************************************************************************/
+void remote_buf_destroy( xptr_t buf_xp );
+
+/************************************************************************************
+ * This function initialises as empty a buffer identified by the <buf_xp> argument.
+ * It can be called by a thread runing in any cluster.
+ ************************************************************************************
+ * @ buf_xp    : [in] extended pointer on remote buffer descriptor.
+ ***********************************************************************************/
+void remote_buf_reset( xptr_t buf_xp );
+
+/************************************************************************************
+ * This function moves <nbytes> from a remote_buf_t identified by the <buf_xp>
+ * argument to an user buffer identified by the <u_buf> argument.
+ * It uses the hal_copy_to_uspace() function, and updates "sts" and "ptr".
+ * The calling function is supposed to previously check the buffer status. 
+ * It can be called by a thread runing in any cluster.
+ ************************************************************************************
+ * @ buf_xp    : [in] extended pointer pointer on remote buffer descriptor.
+ * @ u_buf     : [in] pointer on destination user buffer in user space.
+ * @ nbytes    : [in] number of bytes to move.
+ * @ return 0 if success / return -1 if not enough bytes in buffer.
+ ***********************************************************************************/
+error_t remote_buf_get_to_user( xptr_t    buf_xp,
+                                uint8_t * u_buf,
+                                uint32_t  nbytes );
+
+/************************************************************************************
+ * This function moves <nbytes> from a remote_buf_t identified by the <buf_xp>
+ * argument to a local kernel buffer identified by the <k_buf> argument.
+ * It uses an hal_remote_memcpy() function, and updates the "sts" and "ptr" fields.
+ * The calling function is supposed to previously check the buffer status. 
+ * It can be called by a thread runing in any cluster.
+ ************************************************************************************
+ * @ buf_xp    : [in] extended pointer pointer on remote buffer descriptor.
+ * @ k_buf     : [in] local pointer on local destination kernel buffer.
+ * @ nbytes    : [in] number of bytes to move.
+ * @ return 0 if success / return -1 if not enough bytes in buffer.
+ ***********************************************************************************/
+error_t remote_buf_get_to_kernel( xptr_t    buf_xp,
+                                  uint8_t * k_buf,
+                                  uint32_t  nbytes );
+
+/************************************************************************************
+ * This function moves <nbytes> from an user buffer identified by the <u_buf> 
+ * argument to a remote_buf_t identified by the <buf_xp> argument.
+ * It uses the hal_copy_from_uspace() function, and updates "sts" and "ptw"..
+ * The calling function is supposed to previously check the buffer status. 
+ * It can be called by a thread runing in any cluster.
+ ************************************************************************************
+ * @ buf_xp    : [in] extended pointer pointer on remote buffer descriptor.
+ * @ u_buf     : [in] pointer on source user buffer in user space.
+ * @ nbytes    : [in] number of bytes to move.
+ * @ return 0 if success / return -1 if not enough bytes in buffer.
+ ***********************************************************************************/
+error_t remote_buf_put_from_user( xptr_t    buf_xp,
+                                  uint8_t * u_buf,
+                                  uint32_t  nbytes );
+
+/************************************************************************************
+ * This function moves <nbytes> from a local kernel buffer identified by the <k_buf> 
+ * argument to a remote_buf_t identified by the <buf_xp> argument.
+ * It uses an hal_remote_memcpy() function, and updates the "sts" and "ptw" fields.
+ * The calling function is supposed to previously check the buffer status. 
+ * It can be called by a thread runing in any cluster.
+ ************************************************************************************
+ * @ buf_xp    : [in] extended pointer pointer on remote buffer descriptor.
+ * @ k_buf     : [in] local pointer on local source kernel buffer.
+ * @ nbytes    : [in] number of bytes to move.
+ * @ return 0 if success / return -1 if not enough bytes in buffer.
+ ***********************************************************************************/
+error_t remote_buf_put_from_kernel( xptr_t    buf_xp,
+                                    uint8_t * k_buf,
+                                    uint32_t  nbytes );
+
+/************************************************************************************
+ * This function returns the current number of bytes stored in buffer.
+ ************************************************************************************
+ * @ buf_xp    : [in] extended pointer pointer on remote buffer descriptor.
+ * @ return current number of bytes in buffer.
+ ***********************************************************************************/
+uint32_t remote_buf_status( xptr_t  buf_xp );
+
+#endif	/* _REMOTE_BUFFER_H_ */
Index: /trunk/kernel/libk/remote_fifo.c
===================================================================
--- /trunk/kernel/libk/remote_fifo.c	(revision 656)
+++ /trunk/kernel/libk/remote_fifo.c	(revision 657)
@@ -65,5 +65,5 @@
     watchdog = 0;
 
-    // get write slot index with atomic increment 
+    // get write slot index with atomic increment QQQQQQQQQQ
 	wr_id = hal_remote_atomic_add( XPTR( fifo_cxy , &fifo_ptr->wr_id ) , 1 );
 
Index: /trunk/kernel/libk/remote_rwlock.h
===================================================================
--- /trunk/kernel/libk/remote_rwlock.h	(revision 656)
+++ /trunk/kernel/libk/remote_rwlock.h	(revision 657)
@@ -1,6 +1,6 @@
 /*
- * remote_rwlock.h - kernel remote read/writelock definition.
+ * remote_rwlock.h - kernel remote read/write lock definition.
  *
- * Authors   Alain Greiner   (2016,2017,2018,2019)
+ * Authors   Alain Greiner   (2016,2017,2018,2020)
  * 
  * Copyright (c) UPMC Sorbonne Universites
Index: /trunk/kernel/libk/xhtab.c
===================================================================
--- /trunk/kernel/libk/xhtab.c	(revision 656)
+++ /trunk/kernel/libk/xhtab.c	(revision 657)
@@ -53,13 +53,4 @@
 
 	return (name[0] % XHASHTAB_SIZE);
-/*
-    uint32_t   index = 0;
-	while( *name )
-    {
-        index = index + (*(name++) ^ index);
-    }
-	return index % XHASHTAB_SIZE;
-*/
-
 } 
 
@@ -128,23 +119,29 @@
 ////////////////////////////////////////////////////////////////////////////////////////
 
-//////////////////////////////////////////
-void xhtab_init( xhtab_t          * xhtab,
+/////////////////////////////////////////////
+void xhtab_init( xptr_t             xhtab_xp,
                  xhtab_item_type_t  type )
 {
-	uint32_t i;
+    uint32_t i;
+
+    // get cluster and local pointer
+    xhtab_t * ptr = GET_PTR( xhtab_xp );
+    cxy_t     cxy = GET_CXY( xhtab_xp );
 
     // initialize lock
-    remote_busylock_init( XPTR( local_cxy , &xhtab->lock), LOCK_XHTAB_STATE );
-
-    xhtab->items            = 0;
-    xhtab->current_index    = 0;
-    xhtab->current_xlist_xp = XPTR_NULL;
-
+    remote_busylock_init( XPTR( cxy , &ptr->lock), LOCK_XHTAB_STATE );
+
+    // initialise various fiels
+    hal_remote_s32( XPTR( cxy , &ptr->items ) , 0 );
+    hal_remote_s32( XPTR( cxy , &ptr->current_index ) , 0 );
+    hal_remote_s64( XPTR( cxy , &ptr->current_xlist_xp ) , XPTR_NULL );
+   
+    // initialize functions pointers
     if( type == XHTAB_DENTRY_TYPE )
     {
-        xhtab->item_match_key  = &xhtab_dentry_item_match_key;
-        xhtab->index_from_key  = &xhtab_dentry_index_from_key;
-        xhtab->item_from_xlist = &xhtab_dentry_item_from_xlist;
-        xhtab->item_print_key  = &xhtab_dentry_item_print_key;
+        hal_remote_spt( XPTR( cxy , &ptr->item_match_key  ) , &xhtab_dentry_item_match_key );
+        hal_remote_spt( XPTR( cxy , &ptr->index_from_key  ) , &xhtab_dentry_index_from_key );
+        hal_remote_spt( XPTR( cxy , &ptr->item_from_xlist ) , &xhtab_dentry_item_from_xlist );
+        hal_remote_spt( XPTR( cxy , &ptr->item_print_key  ) , &xhtab_dentry_item_print_key );
     }
     else
@@ -153,24 +150,20 @@
     }
 
+    // initialize all lists
+    for( i=0 ; i < XHASHTAB_SIZE ; i++ )
+    {
+        xlist_root_init( XPTR( cxy , &ptr->roots[i] ) );
+    }
+ 
 #if DEBUG_XHTAB
 printk("\n[%s] for xhtab (%x,%x)\n"
-" - index_from_key  = %x (@ %x)\n"
-" - item_match_key  = %x (@ %x)\n"
-" - item_from_xlist = %x (@ %x)\n",
-__FUNCTION__, local_cxy, xhtab,
-xhtab->index_from_key , &xhtab->index_from_key,
-xhtab->item_match_key , &xhtab->item_match_key,
-xhtab->item_from_xlist, &xhtab->item_from_xlist );
-#endif
-
-	for( i=0 ; i < XHASHTAB_SIZE ; i++ )
-    {
-		xlist_root_init( XPTR( local_cxy , &xhtab->roots[i] ) );
-
-#if (DEBUG_XHTAB & 1)
-printk("\n - initialize root[%d] / %x\n", i , &xhtab->roots[i] ); 
-#endif
-
-    }  
+" - index_from_key  = %x\n"
+" - item_match_key  = %x\n"
+" - item_from_xlist = %x\n",
+__FUNCTION__, cxy, ptr,
+hal_remote_lpt( XPTR( cxy , &ptr->index_from_key ) ),
+hal_remote_lpt( XPTR( cxy , &ptr->item_match_key ) ), 
+hal_remote_lpt( XPTR( cxy , &ptr->item_from_xlist ) ) ); 
+#endif
 
 }  // end xhtab_init()
@@ -200,4 +193,8 @@
     xhtab_ptr = GET_PTR( xhtab_xp );
 
+#if DEBUG_XHTAB
+printk("\n[%s] enter : index = %d / key = %s\n", __FUNCTION__ , index , key );
+#endif
+
     // get pointer on "item_from_xlist" function
     item_from_xlist = (item_from_xlist_t *)hal_remote_lpt( XPTR( xhtab_cxy , 
@@ -206,5 +203,4 @@
     item_match_key = (item_match_key_t *)hal_remote_lpt( XPTR( xhtab_cxy , 
                                                          &xhtab_ptr->item_match_key ) );
-
     // scan sub-list[index]
     XLIST_FOREACH( XPTR( xhtab_cxy , &xhtab_ptr->roots[index] ) , xlist_xp )
@@ -216,4 +212,9 @@
         if( item_match_key( item_xp , key ) ) return item_xp;
     }
+
+#if DEBUG_XHTAB
+printk("\n[%s] exit\n", __FUNCTION__ );
+#endif
+
 
     // No matching item found
@@ -248,15 +249,6 @@
     index_from_key = (index_from_key_t *)hal_remote_lpt( XPTR( xhtab_cxy , 
                                                          &xhtab_ptr->index_from_key ) );
-#if DEBUG_XHTAB
-printk("\n[%s] remote = %x / direct = %x / @ = %x\n",
-__FUNCTION__, index_from_key, xhtab_ptr->index_from_key, &xhtab_ptr->index_from_key );
-#endif
-
     // compute index from key
 	index = index_from_key( key );
-
-#if DEBUG_XHTAB
-printk("\n[%s] index = %x\n", __FUNCTION__, index );
-#endif
 
     // take the lock protecting hash table
@@ -285,5 +277,5 @@
     
 #if DEBUG_XHTAB
-printk("\n[%s] success / <%s>\n", __FUNCTION__, key );
+printk("\n[%s] success for <%s>\n", __FUNCTION__, key );
 #endif
 
@@ -362,5 +354,5 @@
     
 #if DEBUG_XHTAB
-printk("\n[%s] enter / %s\n", __FUNCTION__, key );
+printk("\n[%s] enter\n", __FUNCTION__ );
 #endif
 
@@ -368,20 +360,12 @@
     remote_busylock_acquire( XPTR( xhtab_cxy , &xhtab_ptr->lock ) );
     
-#if DEBUG_XHTAB
-printk("\n[%s] after lock acquire / %s\n", __FUNCTION__, key );
-#endif
-
     // scan sub-list
     item_xp = xhtab_scan( xhtab_xp , index , key );
 
-#if DEBUG_XHTAB
-printk("\n[%s] after xhtab scan / %s\n", __FUNCTION__, key );
-#endif
-
     // release the lock protecting hash table
     remote_busylock_release( XPTR( xhtab_cxy , &xhtab_ptr->lock ) );
 
 #if DEBUG_XHTAB
-printk("\n[%s] after lock release / %s\n", __FUNCTION__, key );
+printk("\n[%s] exit\n", __FUNCTION__ );
 #endif
 
Index: /trunk/kernel/libk/xhtab.h
===================================================================
--- /trunk/kernel/libk/xhtab.h	(revision 656)
+++ /trunk/kernel/libk/xhtab.h	(revision 657)
@@ -105,10 +105,9 @@
 /******************************************************************************************
  * This function initializes an empty hash table (zero registered item).
- * The initialisation must be done by a thread running in cluster containing the table.
  ******************************************************************************************
- * @ xhtab    : local pointer on local xhtab to be initialized.
- * @ type     : item type (see above).
+ * @ xhtab_xp  : extended pointer on xhtab.
+ * @ type      : item type (see above).
  *****************************************************************************************/
-void xhtab_init( xhtab_t           * xhtab,
+void xhtab_init( xptr_t              xhtab_xp,
                  xhtab_item_type_t   type );
 
Index: /trunk/kernel/libk/xlist.h
===================================================================
--- /trunk/kernel/libk/xlist.h	(revision 656)
+++ /trunk/kernel/libk/xlist.h	(revision 657)
@@ -2,5 +2,5 @@
  * xlist.h - Trans-cluster double circular linked list, using extended pointers.
  *
- * Author : Alain Greiner (2016,2017,2018,2019)
+ * Author : Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -50,4 +50,5 @@
 /***************************************************************************
  * This macro returns the offset (in bytes) of a field in a structure.
+ ***************************************************************************
  * @ type   : structure type
  * @ member : name of the field
@@ -61,4 +62,5 @@
  * This macro returns an extended pointer on the structure containing an
  * embedded xlist_entry_t field.
+ ***************************************************************************
  * @ xlist_xp : extended pointer on the xlist_entry_t field
  * @ type     : type of the structure containing the xlist_entry_t
@@ -74,4 +76,5 @@
  * the root xlist_entry_t.
  * WARNING : check list non empty before using this macro.
+ ***************************************************************************
  * @ root_xp : extended pointer on the root xlist_entry_t 
  * @ type    : type of the linked elements
@@ -88,4 +91,5 @@
  * the root xlist_entry_t.
  * WARNING : check list non empty before using this macro.
+ ***************************************************************************
  * @ root_xp : extended pointer on the root xlist_entry_t
  * @ type    : type of the linked elements
@@ -100,4 +104,5 @@
  * This macro traverses an extended double linked list in forward order.
  * WARNING : the iter variable should NOT be deleted during traversal. 
+ ***************************************************************************
  * @ root_xp  : extended pointer on the root xlist_entry_t
  * @ iter_xp  : current extended pointer on a xlist_entry_t
@@ -112,4 +117,5 @@
  * This macro traverses an extended double linked list in backward order.
  * WARNING : the iter variable should NOT be deleted during traversal. 
+ ***************************************************************************
  * @ root_xp  : extended pointer on the root xlist_entry_t
  * @ iter_xp  : current extended pointer on a xlist_entry_t
@@ -124,4 +130,5 @@
  * This function returns an extended pointer on the next xlist_entry_t,
  * from an extended pointer on a reference xlist_entry_t.
+ ***************************************************************************
  * @ root    : extended pointer on the root xlist_entry_t
  * @ ref     : extended pointer on the reference xlist_entry_t
@@ -144,4 +151,5 @@
 /***************************************************************************
  * This function returns an extended pointer on the previous xlist_entry_t.
+ ***************************************************************************
  * @ root    : extended pointer on the root xlist_entry_t
  * @ ref     : extended pointer on the reference xlist_entry_t
@@ -165,4 +173,5 @@
  * This function initialises the root of an extended double linked list.
  * The root can be located in any cluster.
+ ***************************************************************************
  * @ root_xp   :  extended pointer on the root xlist_entry_t
 xixi **************************************************************************/
@@ -176,4 +185,5 @@
  * This function initialises an entry of an extended double linked list.
  * The entry can be located in any cluster.
+ ***************************************************************************
  * @ entry_xp  : extended pointer on the xlist_entry_t
  **************************************************************************/
@@ -188,4 +198,5 @@
  * double linked list. Four extended pointers must be modified.
  * The lock protecting the list should have been previously taken.
+ ***************************************************************************
  * @ root_xp   : extended pointer on the root xlist_entry_t
  * @ entry_xp  : extended pointer on the xlist_entry_t to be inserted
@@ -214,4 +225,5 @@
  * double linked list.  Four extended pointers must be modified.
  * The lock protecting the list should have been previously taken.
+ ***************************************************************************
  * @ root_xp   : extended pointer on the root xlist_entry_t
  * @ entry_xp  : extended pointer on the xlist_entry_t to be inserted
@@ -239,4 +251,5 @@
 /***************************************************************************
  * This function returns true if the list is empty.
+ ***************************************************************************
  * @ root_xp  : extended pointer on the root xlist_entry_t.
  **************************************************************************/
@@ -253,4 +266,5 @@
  * Two extended pointers must be modified.
  * The memory allocated to the removed entry is not released.
+ ***************************************************************************
  * @ xp : extended pointer on the xlist_entry_t to be removed.
  **************************************************************************/
@@ -277,4 +291,5 @@
  * Four extended pointers must be modified.
  * The memory allocated to the removed entry is not released.
+ ***************************************************************************
  * @ old      : extended pointer on the xlist_entry_t to be removed.
  * @ new      : extended pointer on the xlist_entry_t to be inserted.
@@ -307,4 +322,5 @@
 /***************************************************************************
  * This debug function displays all entries of an xlist.
+ ***************************************************************************
  * @ root_xp : extended pointer on the root xlist_entry_t.
  * @ string  : list identifier displayed in header.
Index: /trunk/kernel/mm/kcm.c
===================================================================
--- /trunk/kernel/mm/kcm.c	(revision 656)
+++ /trunk/kernel/mm/kcm.c	(revision 657)
@@ -43,10 +43,10 @@
 //////////////////////////////////////////////////////////////////////////////////////
 // This static function must be called by a local thread.
-// It returns a pointer on a block allocated from a non-full kcm_page.
-// It makes a panic if no block is available in selected page.
+// It returns a pointer on a block allocated from an active kcm_page.
+// It makes a panic if no block is available in the selected page.
 // It changes the page status as required.
 //////////////////////////////////////////////////////////////////////////////////////
 // @ kcm      : pointer on KCM allocator.
-// @ kcm_page : pointer on a non-full kcm_page.
+// @ kcm_page : pointer on an active kcm_page.
 // @ return pointer on allocated block.
 /////////////////////////////////////////////////////////////////////////////////////
@@ -64,5 +64,4 @@
     uint32_t index  = 1;
     uint64_t mask   = (uint64_t)0x2;
-    uint32_t found  = 0;
 
 	// allocate first free block in kcm_page, update status,
@@ -70,10 +69,9 @@
     while( index <= max )
     {
-        if( (status & mask) == 0 )   // block non allocated
+        if( (status & mask) == 0 )   // block found
         {
+            // update page count and status
             kcm_page->status = status | mask;
             kcm_page->count  = count + 1;
-            found  = 1;
-
             break;      
         }
@@ -83,5 +81,5 @@
     }
 
-    // change the page list if almost full
+    // change the page list if found block is the last
     if( count == max-1 )
     {
@@ -162,9 +160,9 @@
 
 /////////////////////////////////////////////////////////////////////////////////////
-// This private static function must be called by a local thread.
-// It returns one non-full kcm_page with te following policy :
+// This static function must be called by a local thread.
+// It returns one non-full kcm_page with the following policy :
 // - if the "active_list" is non empty, it returns the first "active" page,
 //   without modifying the KCM state.
-// - if the "active_list" is empty, it allocates a new page fromm PPM, inserts
+// - if the "active_list" is empty, it allocates a new page from PPM, inserts
 //   this page in the active_list, and returns it. 
 /////////////////////////////////////////////////////////////////////////////////////
@@ -275,5 +273,6 @@
 	// release KCM lock
 	remote_busylock_release( lock_xp );
-}
+
+}  // end kcm_destroy()
 
 //////////////////////////////////
@@ -284,5 +283,5 @@
 	void       * block_ptr;
 
-    // min block size is 64 bytes
+   // min block size is 64 bytes
     if( order < 6 ) order = 6;
 
@@ -301,4 +300,16 @@
     kcm_page = kcm_get_page( kcm_ptr );
 
+#if DEBUG_KCM
+thread_t * this  = CURRENT_THREAD;
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+if( (DEBUG_KCM < cycle) && (local_cxy == 1) )
+{
+printk("\n[%s] thread[%x,%x] enters / order %d / page %x / kcm %x / page_status (%x|%x)\n",
+__FUNCTION__, this->process->pid, this->trdid, order, kcm_page, kcm_ptr,
+GET_CXY( kcm_page->status ), GET_PTR( kcm_page->status ) );
+kcm_remote_display( local_cxy , kcm_ptr );
+}
+#endif
+
     if( kcm_page == NULL )
 	{
@@ -314,10 +325,11 @@
 
 #if DEBUG_KCM
-thread_t * this  = CURRENT_THREAD;
-uint32_t   cycle = (uint32_t)hal_get_cycles();
-if( DEBUG_KCM < cycle )
-printk("\n[%s] thread[%x,%x] allocated block %x / order %d / kcm %x / status[%x,%x] / count %d\n",
-__FUNCTION__, this->process->pid, this->trdid, block_ptr, order, kcm_ptr,
-GET_CXY(kcm_page->status), GET_PTR(kcm_page->status), kcm_page->count );
+if( (DEBUG_KCM < cycle) && (local_cxy == 1) )
+{
+printk("\n[%s] thread[%x,%x] exit / order %d / block %x / kcm %x / page_status (%x|%x)\n",
+__FUNCTION__, this->process->pid, this->trdid, order, block_ptr, kcm_ptr,
+GET_CXY( kcm_page->status ), GET_PTR( kcm_page->status ) );
+kcm_remote_display( local_cxy , kcm_ptr );
+}
 #endif
 
@@ -344,8 +356,11 @@
 thread_t * this  = CURRENT_THREAD;
 uint32_t   cycle = (uint32_t)hal_get_cycles();
-if( DEBUG_KCM < cycle )
-printk("\n[%s] thread[%x,%x] release block %x / order %d / kcm %x / status [%x,%x] / count %d\n",
-__FUNCTION__, this->process->pid, this->trdid, block_ptr, kcm_ptr->order, kcm_ptr,
-GET_CXY(kcm_page->status), GET_PTR(kcm_page->status), kcm_page->count );
+if( (DEBUG_KCM < cycle) && (local_cxy == 1) )
+{
+printk("\n[%s] thread[%x,%x] enters / order %d / block %x / page %x / kcm %x / status [%x,%x]\n",
+__FUNCTION__, this->process->pid, this->trdid, kcm_ptr->order, block_ptr, kcm_page, kcm_ptr,
+GET_CXY(kcm_page->status), GET_PTR(kcm_page->status) );
+kcm_remote_display( local_cxy , kcm_ptr );
+}
 #endif
 
@@ -361,5 +376,16 @@
 	// release lock
 	remote_busylock_release( lock_xp );
+
+#if DEBUG_KCM
+if( (DEBUG_KCM < cycle) && (local_cxy == 1) )
+{
+printk("\n[%s] thread[%x,%x] exit / order %d / page %x / status [%x,%x]\n",
+__FUNCTION__, this->process->pid, this->trdid, kcm_ptr->order, kcm_ptr,
+GET_CXY(kcm_page->status), GET_PTR(kcm_page->status) );
+kcm_remote_display( local_cxy , kcm_ptr );
 }
+#endif
+
+}  // end kcm_free() 
 
 /////////////////////////////////////////////////////////////////////////////////////
@@ -369,11 +395,11 @@
 /////////////////////////////////////////////////////////////////////////////////////
 // This static function can be called by any thread running in any cluster.
-// It returns a local pointer on a block allocated from an non-full kcm_page.
-// It makes a panic if no block available in selected page.
+// It returns a local pointer on a block allocated from an active kcm_page.
+// It makes a panic if no block available in the selected kcm_page.
 // It changes the page status as required.
 /////////////////////////////////////////////////////////////////////////////////////
-// @ kcm_cxy  : remote KCM cluster identidfier.
+// @ kcm_cxy  : remote KCM cluster identifier.
 // @ kcm_ptr  : local pointer on remote KCM allocator.
-// @ kcm_page : pointer on active kcm page to use.
+// @ kcm_page : local pointer on remote active kcm_page to use.
 // @ return a local pointer on the allocated block. 
 /////////////////////////////////////////////////////////////////////////////////////
@@ -392,5 +418,4 @@
     uint32_t index  = 1;
     uint64_t mask   = (uint64_t)0x2;
-    uint32_t found  = 0;
     
 	// allocate first free block in kcm_page, update status,
@@ -398,9 +423,8 @@
     while( index <= max )
     {
-        if( (status & mask) == 0 )   // block non allocated
+        if( (status & mask) == 0 )   // block found
         {
             hal_remote_s64( XPTR( kcm_cxy , &kcm_page->status ) , status | mask );
-            hal_remote_s64( XPTR( kcm_cxy , &kcm_page->count  ) , count + 1 );
-            found  = 1; 
+            hal_remote_s32( XPTR( kcm_cxy , &kcm_page->count  ) , count + 1 );
             break;      
         }
@@ -410,5 +434,5 @@
     }
 
-	// change the page list if almost full
+	// change the page list if found block is the last
 	if( count == max-1 )
 	{
@@ -631,9 +655,40 @@
                          kcm_t * kcm_ptr )
 {
+    list_entry_t * iter;
+    kcm_page_t   * kcm_page;
+    uint64_t       status;
+    uint32_t       count;
+
     uint32_t order           = hal_remote_l32( XPTR( kcm_cxy , &kcm_ptr->order) );
     uint32_t full_pages_nr   = hal_remote_l32( XPTR( kcm_cxy , &kcm_ptr->full_pages_nr ) );
     uint32_t active_pages_nr = hal_remote_l32( XPTR( kcm_cxy , &kcm_ptr->active_pages_nr ) );
 
-	printk("*** KCM / cxy %x / order %d / full_pages %d / empty_pages %d / active_pages %d\n",
+	printk("*** KCM : cxy %x / order %d / full_pages_nr %d / active_pages_nr %d\n",
 	kcm_cxy, order, full_pages_nr, active_pages_nr );
-}
+
+    if( active_pages_nr )
+    {
+        LIST_REMOTE_FOREACH( kcm_cxy , &kcm_ptr->active_root , iter )
+        {
+            kcm_page = LIST_ELEMENT( iter , kcm_page_t , list );
+            status   = hal_remote_l64( XPTR( kcm_cxy , &kcm_page->status ) );
+            count    = hal_remote_l32( XPTR( kcm_cxy , &kcm_page->count ) );
+
+            printk("- active page %x / status (%x,%x) / count %d\n",
+            kcm_page, GET_CXY( status ), GET_PTR( status ), count );
+        }
+    }
+
+    if( full_pages_nr )
+    {
+        LIST_REMOTE_FOREACH( kcm_cxy , &kcm_ptr->full_root , iter )
+        {
+            kcm_page = LIST_ELEMENT( iter , kcm_page_t , list );
+            status   = hal_remote_l64( XPTR( kcm_cxy , &kcm_page->status ) );
+            count    = hal_remote_l32( XPTR( kcm_cxy , &kcm_page->count ) );
+
+            printk("- full page %x / status (%x,%x) / count %d\n",
+            kcm_page, GET_CXY( status ), GET_PTR( status ), count );
+        }
+    }
+}  // end kcm remote_display()
Index: /trunk/kernel/mm/kcm.h
===================================================================
--- /trunk/kernel/mm/kcm.h	(revision 656)
+++ /trunk/kernel/mm/kcm.h	(revision 657)
@@ -92,5 +92,5 @@
  * It initializes a Kernel Cache Manager, depending on block size.
  ****************************************************************************************
- * @ kcm      : pointer on KCM manager to initialize.
+ * @ kcm      : pointer on KCM to be initialized.
  * @ order    : ln(block_size).
  ***************************************************************************************/
@@ -122,4 +122,7 @@
  ***************************************************************************************/
 void kcm_free( void    * block_ptr );
+
+
+
 
 /****************************************************************************************
Index: /trunk/kernel/mm/kmem.c
===================================================================
--- /trunk/kernel/mm/kmem.c	(revision 656)
+++ /trunk/kernel/mm/kmem.c	(revision 657)
@@ -2,5 +2,5 @@
  * kmem.c - kernel memory allocator implementation.
  *
- * Authors  Alain Greiner (2016,2017,2018,2019)
+ * Authors  Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
Index: /trunk/kernel/mm/mapper.c
===================================================================
--- /trunk/kernel/mm/mapper.c	(revision 656)
+++ /trunk/kernel/mm/mapper.c	(revision 657)
@@ -3,5 +3,5 @@
  *
  * Authors   Mohamed Lamine Karaoui (2015)
- *           Alain Greiner (2016,2017,2018,2019)
+ *           Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c)  UPMC Sorbonne Universites
@@ -46,60 +46,59 @@
 
 
-//////////////////////////////////////////////
-mapper_t * mapper_create( vfs_fs_type_t type )
-{
-    mapper_t * mapper;
+/////////////////////////////////////
+xptr_t  mapper_create( cxy_t     cxy,
+                       uint32_t  type )
+{
+    mapper_t * mapper_ptr;
     kmem_req_t req;
     error_t    error;
 
     // allocate memory for mapper descriptor
-    req.type  = KMEM_KCM;
-    req.order = bits_log2( sizeof(mapper_t) );
-    req.flags = AF_KERNEL | AF_ZERO;
-    mapper    = kmem_alloc( &req );
-
-    if( mapper == NULL )
+    req.type    = KMEM_KCM;
+    req.order   = bits_log2( sizeof(mapper_t) );
+    req.flags   = AF_KERNEL | AF_ZERO;
+    mapper_ptr  = kmem_remote_alloc( cxy , &req );
+
+    if( mapper_ptr == NULL )
     {
         printk("\n[ERROR] in %s : no memory for mapper descriptor\n", __FUNCTION__ );
-        return NULL;
-    }
-
-    // initialize refcount & inode
-    mapper->refcount = 0;
-    mapper->inode    = NULL;
+        return XPTR_NULL;
+    }
+
+    // initialize refcount and type
+    hal_remote_s32( XPTR( cxy , &mapper_ptr->refcount ) , 0 );
+    hal_remote_s32( XPTR( cxy , &mapper_ptr->fs_type )  , type );
 
     // initialize radix tree
-    error = grdxt_init( &mapper->rt,
-                        CONFIG_MAPPER_GRDXT_W1,
-                        CONFIG_MAPPER_GRDXT_W2,
-                        CONFIG_MAPPER_GRDXT_W3 );
+    error = grdxt_remote_init( XPTR( cxy , &mapper_ptr->rt ),
+                               CONFIG_MAPPER_GRDXT_W1,
+                               CONFIG_MAPPER_GRDXT_W2,
+                               CONFIG_MAPPER_GRDXT_W3 );
     if( error )
     {
         printk("\n[ERROR] in %s : cannot initialize radix tree\n", __FUNCTION__ );
         req.type  = KMEM_KCM;
-        req.ptr   = mapper;
-        kmem_free( &req );
-        return NULL;
-    }
-
-    // initialize mapper type
-    mapper->type = type;
+        req.ptr   = mapper_ptr;
+        kmem_remote_free( cxy , &req );
+        return XPTR_NULL;
+    }
 
     // initialize mapper lock
-    remote_rwlock_init( XPTR( local_cxy , &mapper->lock ) , LOCK_MAPPER_STATE );
+    remote_rwlock_init( XPTR( cxy , &mapper_ptr->lock ) , LOCK_MAPPER_STATE );
 
     // initialize waiting threads xlist (empty)
-    xlist_root_init( XPTR( local_cxy , &mapper->wait_root ) );
+    xlist_root_init( XPTR( cxy , &mapper_ptr->wait_root ) );
 
     // initialize vsegs xlist (empty)
-    xlist_root_init( XPTR( local_cxy , &mapper->vsegs_root ) );
-
-    return mapper;
+    xlist_root_init( XPTR( cxy , &mapper_ptr->vsegs_root ) );
+
+    return XPTR( cxy , mapper_ptr );
 
 }  // end mapper_create()
 
 ////////////////////////////////////////
-void mapper_destroy( mapper_t * mapper )
-{
+void mapper_destroy( xptr_t  mapper_xp )
+{
+    xptr_t     page_xp;
     page_t   * page;
     uint32_t   found_index = 0;
@@ -107,15 +106,24 @@
     kmem_req_t req;
 
+    cxy_t      mapper_cxy = GET_CXY( mapper_xp ); 
+    mapper_t * mapper_ptr = GET_PTR( mapper_xp );
+
+    // build extended pointer on radix tree
+    xptr_t rt_xp = XPTR( mapper_cxy , &mapper_ptr->rt );
+
     // scan radix tree
     do
     {
         // get page from radix tree
-        page = (page_t *)grdxt_get_first( &mapper->rt , start_index , &found_index );
-
+        page_xp = grdxt_remote_get_first( rt_xp,
+                                          start_index , 
+                                          &found_index );
+        page = GET_PTR( page_xp );
+        
         // release registered pages to PPM
         if( page != NULL )
         {
             // remove page from mapper and release to PPM
-            mapper_remote_release_page( XPTR( local_cxy , mapper ) , page );
+            mapper_remote_release_page( mapper_xp , page );
 
             // update start_key value for next page
@@ -126,22 +134,22 @@
 
     // release the memory allocated to radix tree itself
-    grdxt_destroy( &mapper->rt );
+    grdxt_remote_destroy( rt_xp );
 
     // release memory for mapper descriptor
     req.type = KMEM_KCM;
-    req.ptr  = mapper;
-    kmem_free( &req );
+    req.ptr  = mapper_ptr;
+    kmem_remote_free( mapper_cxy , &req );
 
 }  // end mapper_destroy()
 
-////////////////////////////////////////////////////////
-error_t mapper_remote_handle_miss( xptr_t     mapper_xp,
-                                   uint32_t   page_id,
-                                   xptr_t   * page_xp_ptr )
+/////////////////////////////////////////////////
+error_t mapper_handle_miss( xptr_t     mapper_xp,
+                            uint32_t   page_id,
+                            xptr_t   * page_xp_ptr )
 {
     error_t    error;
 
-    uint32_t   inode_size;    
-    uint32_t   inode_type;
+    uint32_t   inode_size = 0;    
+    uint32_t   inode_type = 0;
 
     thread_t * this = CURRENT_THREAD;
@@ -159,9 +167,4 @@
         inode_size = hal_remote_l32( XPTR( mapper_cxy , &inode->size ) );
         inode_type = hal_remote_l32( XPTR( mapper_cxy , &inode->type ) );
-    }
-    else
-    {
-        inode_size = 0;
-        inode_type = 0;
     }
 
@@ -267,9 +270,9 @@
     return 0;
 
-}  // end mapper_remote_handle_miss()
-
-////////////////////////////////////////////////////
-xptr_t  mapper_remote_get_page( xptr_t    mapper_xp,
-                                uint32_t  page_id )
+}  // end mapper_handle_miss()
+
+/////////////////////////////////////////////
+xptr_t  mapper_get_page( xptr_t    mapper_xp,
+                         uint32_t  page_id )
 {
     error_t       error;
@@ -281,15 +284,13 @@
     cxy_t      mapper_cxy = GET_CXY( mapper_xp );
 
+assert( (hal_remote_lpt( XPTR( mapper_cxy , &mapper_ptr->inode ) ) != NULL ),
+"should not be used for the FAT mapper");
+
 #if DEBUG_MAPPER_GET_PAGE
-vfs_inode_t * inode = hal_remote_lpt( XPTR( mapper_cxy , &mapper_ptr->inode ) );
 uint32_t      cycle = (uint32_t)hal_get_cycles();
 char          name[CONFIG_VFS_MAX_NAME_LENGTH];
-if( (DEBUG_MAPPER_GET_PAGE < cycle) && (inode == NULL) )  // FAT mapper
-{
-    printk("\n[%s] thread[%x,%x] enter for page %d of FAT mapper / cycle %d\n",
-    __FUNCTION__, this->process->pid, this->trdid, page_id, cycle );
-}
-if( (DEBUG_MAPPER_GET_PAGE < cycle) && (inode != NULL) )  // file mapper
-{
+if( DEBUG_MAPPER_GET_PAGE < cycle )  
+{
+    vfs_inode_t * inode = hal_remote_lpt( XPTR( mapper_cxy , &mapper_ptr->inode ) );
     vfs_inode_get_name( XPTR( mapper_cxy , inode ) , name );
     printk("\n[%s] thread[%x,%x] enter for page %d of <%s> mapper / cycle %d\n",
@@ -330,7 +331,7 @@
         if ( page_xp == XPTR_NULL )  // miss confirmed => handle it
         {
-            error = mapper_remote_handle_miss( mapper_xp,
-                                               page_id,
-                                               &page_xp );
+            error = mapper_handle_miss( mapper_xp,
+                                        page_id,
+                                        &page_xp );
             if( error )
             {
@@ -343,14 +344,7 @@
 
 #if (DEBUG_MAPPER_GET_PAGE & 1)
-if( (DEBUG_MAPPER_GET_PAGE < cycle) && (inode != NULL) )
-{
-    printk("\n[%s] thread[%x,%x] introduced missing page in <%s> mapper / ppn %x\n",
-    __FUNCTION__, this->process->pid, this->trdid, name, ppm_page2ppn(page_xp) );
-}
-if( (DEBUG_MAPPER_GET_PAGE < cycle) && (inode == NULL) )
-{
-    printk("\n[%s] thread[%x,%x] introduced missing page in FAT mapper / ppn %x\n",
-    __FUNCTION__, this->process->pid, this->trdid, ppm_page2ppn(page_xp) );
-}
+if( DEBUG_MAPPER_GET_PAGE < cycle )
+printk("\n[%s] thread[%x,%x] introduced missing page %d in <%s> mapper / ppn %x\n",
+__FUNCTION__, this->process->pid, this->trdid, page_id, name, ppm_page2ppn(page_xp) );
 #endif
         
@@ -365,15 +359,7 @@
 
 #if DEBUG_MAPPER_GET_PAGE
-cycle = (uint32_t)hal_get_cycles();
-if( (DEBUG_MAPPER_GET_PAGE < cycle) && (inode != NULL) )
-{
-    printk("\n[%s] thread[%x,%x] exit for page %d of <%s> mapper / ppn %x\n",
-    __FUNCTION__, this->process->pid, this->trdid, page_id, name, ppm_page2ppn(page_xp) );
-}
-if( (DEBUG_MAPPER_GET_PAGE < cycle) && (inode == NULL) )
-{
-    printk("\n[%s] thread[%x,%x] exit for page %d of FAT mapper  / ppn %x\n",
-    __FUNCTION__, this->process->pid, this->trdid, page_id, ppm_page2ppn(page_xp) );
-}
+if( DEBUG_MAPPER_GET_PAGE < cycle )
+printk("\n[%s] thread[%x,%x] exit for page %d of <%s> mapper / ppn %x\n",
+__FUNCTION__, this->process->pid, this->trdid, page_id, name, ppm_page2ppn(page_xp) );
 #endif
 
@@ -385,5 +371,101 @@
     return page_xp;
 
-}  // end mapper_remote_get_page()
+}  // end mapper_get_page()
+
+/////////////////////////////////////////////////
+xptr_t  mapper_get_fat_page( xptr_t    mapper_xp,
+                             uint32_t  page_id )
+{
+    error_t       error;
+
+    thread_t * this = CURRENT_THREAD;
+
+    // get mapper cluster and local pointer
+    mapper_t * mapper_ptr = GET_PTR( mapper_xp );
+    cxy_t      mapper_cxy = GET_CXY( mapper_xp );
+
+assert( (hal_remote_lpt( XPTR( mapper_cxy , &mapper_ptr->inode ) ) == NULL ),
+"should be used for the FAT mapper");
+
+#if DEBUG_MAPPER_GET_FAT_PAGE
+uint32_t      cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_MAPPER_GET_FAT_PAGE < cycle ) 
+printk("\n[%s] thread[%x,%x] enter for page %d of FAT mapper / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, page_id, cycle );
+#endif
+
+#if( DEBUG_MAPPER_GET_FAT_PAGE & 2 )
+if( DEBUG_MAPPER_GET_FAT_PAGE < cycle ) 
+ppm_remote_display( local_cxy );
+#endif
+
+    // check thread can yield
+    thread_assert_can_yield( this , __FUNCTION__ );
+
+    // build extended pointer on mapper lock and mapper rt
+    xptr_t lock_xp  = XPTR( mapper_cxy , &mapper_ptr->lock );
+    xptr_t rt_xp    = XPTR( mapper_cxy , &mapper_ptr->rt );
+
+    // take mapper lock in READ_MODE
+    remote_rwlock_rd_acquire( lock_xp );
+
+    // search page in radix tree
+    xptr_t page_xp  = grdxt_remote_lookup( rt_xp , page_id );
+
+    // test mapper miss 
+    if( page_xp == XPTR_NULL )                  // miss => handle it
+    {
+        // release the lock in READ_MODE and take it in WRITE_MODE
+        remote_rwlock_rd_release( lock_xp );
+        remote_rwlock_wr_acquire( lock_xp );
+
+        // second test on missing page because the page status can be modified
+        // by another thread, when passing from READ_MODE to WRITE_MODE.
+        // from this point there is no concurrent accesses to mapper.
+        page_xp = grdxt_remote_lookup( rt_xp , page_id );
+
+        if ( page_xp == XPTR_NULL )  // miss confirmed => handle it
+        {
+            error = mapper_handle_miss( mapper_xp,
+                                        page_id,
+                                        &page_xp );
+            if( error )
+            {
+                printk("\n[ERROR] in %s : thread[%x,%x] cannot handle mapper miss\n",
+                __FUNCTION__ , this->process->pid, this->trdid );
+                remote_rwlock_wr_release( lock_xp );
+                return XPTR_NULL;
+            }
+        }
+
+#if (DEBUG_MAPPER_GET_FAT_PAGE & 1)
+if( DEBUG_MAPPER_GET_FAT_PAGE < cycle )
+printk("\n[%s] thread[%x,%x] introduced missing page %d in FAT mapper / ppn %x\n",
+__FUNCTION__, this->process->pid, this->trdid, page_id, ppm_page2ppn(page_xp) );
+#endif
+        
+        // release mapper lock from WRITE_MODE
+        remote_rwlock_wr_release( lock_xp );
+    }
+    else                                              // hit 
+    {
+        // release mapper lock from READ_MODE
+        remote_rwlock_rd_release( lock_xp );
+    }
+
+#if DEBUG_MAPPER_GET_FAT_PAGE
+if( DEBUG_MAPPER_GET_FAT_PAGE < cycle )
+printk("\n[%s] thread[%x,%x] exit for page %d of FAT mapper  / ppn %x\n",
+__FUNCTION__, this->process->pid, this->trdid, page_id, ppm_page2ppn(page_xp) );
+#endif
+
+#if( DEBUG_MAPPER_GET_FAT_PAGE & 2)
+if( DEBUG_MAPPER_GET_FAT_PAGE < cycle ) 
+ppm_remote_display( local_cxy );
+#endif
+
+    return page_xp;
+
+}  // end mapper_get_fat_page()
 
 ////////////////////////////////////////////////////
@@ -481,5 +563,5 @@
 
         // get extended pointer on page descriptor in mapper
-        page_xp = mapper_remote_get_page( mapper_xp , page_id ); 
+        page_xp = mapper_get_page( mapper_xp , page_id ); 
 
         if ( page_xp == XPTR_NULL ) return -1;
@@ -519,5 +601,5 @@
 __FUNCTION__, this->process->pid, this->trdid, page_bytes,
 local_cxy, buf_ptr, name, GET_CXY(map_xp), GET_PTR(map_xp) );
-mapper_display_page(  mapper_xp , page_xp , 128 );
+mapper_display_page(  mapper_xp , page_id , 128 );
 #endif
 
@@ -617,5 +699,5 @@
 
         // get extended pointer on page descriptor
-        page_xp = mapper_remote_get_page( mapper_xp , page_id );
+        page_xp = mapper_get_page( mapper_xp , page_id );
 
         if ( page_xp == XPTR_NULL ) return -1;
@@ -678,5 +760,5 @@
     
     // get page containing the searched word
-    page_xp  = mapper_remote_get_page( mapper_xp , page_id );
+    page_xp  = mapper_get_page( mapper_xp , page_id );
 
     if( page_xp == XPTR_NULL )  return -1;
@@ -702,5 +784,5 @@
 
     // get page containing the searched word
-    page_xp  = mapper_remote_get_page( mapper_xp , page_id );
+    page_xp  = mapper_get_page( mapper_xp , page_id );
 
     if( page_xp == XPTR_NULL ) return -1;
@@ -719,13 +801,13 @@
 }  // end mapper_remote_set_32()
 
-/////////////////////////////////////////
-error_t mapper_sync( mapper_t *  mapper )
-{
-    page_t   * page;                // local pointer on current page descriptor
-    xptr_t     page_xp;             // extended pointer on current page descriptor
-    grdxt_t  * rt;                  // pointer on radix_tree descriptor
-    uint32_t   start_key;           // start page index in mapper
-    uint32_t   found_key;           // current page index in mapper
+////////////////////////////////////////
+error_t mapper_sync( xptr_t  mapper_xp )
+{
+    uint32_t   found_key;           // unused, required by grdxt_remote_get_first()
     error_t    error;
+
+    // get mapper cluster and local pointer
+    mapper_t * mapper_ptr = GET_PTR( mapper_xp );
+    cxy_t      mapper_cxy = GET_CXY( mapper_xp );
 
 #if DEBUG_MAPPER_SYNC
@@ -733,12 +815,12 @@
 uint32_t   cycle = (uint32_t)hal_get_cycles();
 char       name[CONFIG_VFS_MAX_NAME_LENGTH];
-vfs_inode_get_name( XPTR( local_cxy , mapper->inode ) , name );
-#endif
-
-    // get pointer on radix tree
-    rt = &mapper->rt;
+vfs_inode_get_name( XPTR( mapper_cxy , &mapper_ptr->inode ) , name );
+#endif
+
+    // build extended pointer on radix tree
+    xptr_t   rt_xp = XPTR( mapper_cxy , &mapper_ptr->rt );
 
     // initialise loop variable 
-    start_key = 0;
+    uint32_t start_key = 0;
 
     // scan radix-tree until last page found
@@ -746,16 +828,17 @@
     {
         // get page descriptor from radix tree
-        page = (page_t *)grdxt_get_first( rt , start_key , &found_key );
+        xptr_t page_xp = grdxt_remote_get_first( rt_xp , start_key , &found_key );
          
-        if( page == NULL ) break;
-
-assert( (page->index == found_key ), "page_index (%d) != key (%d)", page->index, found_key );
-assert( (page->order == 0), "page_order (%d] != 0", page->order );
-
-        // build extended pointer on page descriptor
-        page_xp = XPTR( local_cxy , page );
+        page_t * page_ptr = GET_PTR( page_xp );
+
+        // exit loop when last page found
+        if( page_ptr == NULL ) break;
+
+        // get page flags & index fields
+        uint32_t flags = hal_remote_l32( XPTR( mapper_cxy , &page_ptr->flags ) );
+        uint32_t index = hal_remote_l32( XPTR( mapper_cxy , &page_ptr->index ) );
 
         // synchronize page if dirty
-        if( (page->flags & PG_DIRTY) != 0 )
+        if( flags & PG_DIRTY )
         {
 
@@ -763,5 +846,5 @@
 if( cycle > DEBUG_MAPPER_SYNC )
 printk("\n[%s] thread[%x,%x] synchonise page %d of <%s> to IOC device\n",
-__FUNCTION__, this->process->pid, this->trdid, page->index, name );
+__FUNCTION__, this->process->pid, this->trdid, page_ptr->index, name );
 #endif
             // copy page to file system
@@ -771,5 +854,5 @@
             {
                 printk("\n[ERROR] in %s : cannot synchonize dirty page %d\n", 
-                __FUNCTION__, page->index );
+                __FUNCTION__, page_ptr->index );
                 return -1;
             }
@@ -784,10 +867,10 @@
 if( cycle > DEBUG_MAPPER_SYNC )
 printk("\n[%s] thread[%x,%x] skip page %d for <%s>\n",
-__FUNCTION__, this->process->pid, this->trdid, page->index, name );
+__FUNCTION__, this->process->pid, this->trdid, page_ptr->index, name );
 #endif
         }
 
         // update loop variable
-        start_key = page->index + 1;
+        start_key = index + 1;
     }  // end while
 
@@ -798,5 +881,5 @@
 ///////////////////////////////////////////////
 void mapper_display_page( xptr_t     mapper_xp,
-                          xptr_t     page_xp,
+                          uint32_t   page_id,
                           uint32_t   nbytes )
 {
@@ -809,5 +892,4 @@
 assert( (nbytes <= 4096)         , "nbytes cannot be larger than 4096");
 assert( (mapper_xp != XPTR_NULL) , "mapper_xp argument cannot be null");
-assert( (page_xp   != XPTR_NULL) , "page_xp argument cannot be null");
 
     // get mapper cluster and local pointer
@@ -815,14 +897,18 @@
     mapper_t * mapper_ptr = GET_PTR( mapper_xp );
 
-    // get page cluster an local pointer
+    // get extended pointer on page descriptor
+    xptr_t page_xp = mapper_get_page( mapper_xp , page_id );
+
+    // get page cluster and local pointer
     cxy_t    page_cxy = GET_CXY( page_xp );
     page_t * page_ptr = GET_PTR( page_xp );
 
     // get page_id and mapper from page descriptor
-    uint32_t   page_id = hal_remote_l32( XPTR( page_cxy , &page_ptr->index ) );
+    uint32_t   index   = hal_remote_l32( XPTR( page_cxy , &page_ptr->index ) );
     mapper_t * mapper  = hal_remote_lpt( XPTR( page_cxy , &page_ptr->mapper ) );
 
 assert( (mapper_cxy == page_cxy ) , "mapper and page must be in same cluster");
-assert( (mapper_ptr == mapper   ) , "unconsistent mapper_xp & page_xp arguments");
+assert( (mapper_ptr == mapper   ) , "unconsistent mapper field in page descriptor");
+assert( (page_id    == index    ) , "unconsistent index  field in page descriptor");
 
     // get inode
Index: /trunk/kernel/mm/mapper.h
===================================================================
--- /trunk/kernel/mm/mapper.h	(revision 656)
+++ /trunk/kernel/mm/mapper.h	(revision 657)
@@ -61,6 +61,4 @@
  * - In the present implementation the cache size for a given file increases on demand,
  *   and the  allocated memory is only released when the mapper/inode is destroyed.
- *
- * TODO the "type" field in mapper descriptor is redundant and probably unused. 
  ******************************************************************************************/
 
@@ -73,5 +71,5 @@
 {
 	struct vfs_inode_s * inode;	      /*! owner inode                                     */
-    uint32_t             type;        /*! file system type                                */
+    uint32_t             fs_type;     /*! file system type                                */
 	grdxt_t	             rt; 	      /*! embedded pages cache descriptor (radix tree)    */
 	remote_rwlock_t      lock;        /*! several readers / only one writer               */
@@ -84,22 +82,24 @@
 
 /*******************************************************************************************
- * This function allocates physical memory for a mapper descriptor, and initializes it
- * (refcount <= 0) / inode <= NULL).
- * It must be executed by a thread running in the cluster containing the mapper.
- *******************************************************************************************
- * @ type   : type of the mapper to create.
- * @ return : pointer on created mapper if success / return NULL if no memory
- ******************************************************************************************/
-mapper_t * mapper_create( vfs_fs_type_t type );
-
-/*******************************************************************************************
- * This function releases all physical memory allocated for a mapper.
- * Both the mapper descriptor and the radix tree are released.
+ * This function allocates physical memory for a mapper descriptor, in cluster
+ * identified by the <cxy> argument. It initializes it (refcount <= 0) / inode <= NULL).
+ * It can be executed by any thread running in any cluster.
+ *******************************************************************************************
+ * @ cxy    : target cluster identifier.
+ * @ type   : FS type.
+ * @ return an extended pointer on created mapper if success / return NULL if no memory
+ ******************************************************************************************/
+xptr_t  mapper_create( cxy_t     cxy,
+                       uint32_t  type );
+
+/*******************************************************************************************
+ * This function releases all physical memory allocated for a mapper, identified
+ * by the <mapper_xp> argument. Both the mapper descriptor and the radix tree are released.
  * It does NOT synchronize dirty pages. Use the vfs_sync_inode() function if required.
- * It must be executed by a thread running in the cluster containing the mapper.
- *******************************************************************************************
- * @ mapper	 : target mapper.
- ******************************************************************************************/
-void mapper_destroy( mapper_t * mapper );
+ * It can be executed by any thread running in any cluster.
+ *******************************************************************************************
+ * @ mapper_xp	 : extended pointer on target mapper.
+ ******************************************************************************************/
+void mapper_destroy( xptr_t  mapper_xp );
 
 /*******************************************************************************************
@@ -117,7 +117,7 @@
  * @ return 0 if success / return -1 if IOC cannot be accessed.
  ******************************************************************************************/
-error_t mapper_remote_handle_miss( xptr_t     mapper_xp,
-                                   uint32_t   page_id,
-                                   xptr_t   * page_xp );
+error_t mapper_handle_miss( xptr_t     mapper_xp,
+                            uint32_t   page_id,
+                            xptr_t   * page_xp );
 
 /*******************************************************************************************
@@ -180,6 +180,6 @@
 
 /*******************************************************************************************
- * This function returns an extended pointer on a page descriptor.
- * The - possibly remote - mapper is identified by the <mapper_xp> argument.
+ * This function returns an extended pointer on a page descriptor for a regular mapper
+ * (i.e. this mapper is NOT the FAT mapper), identified by the <mapper_xp> argument.
  * The page is identified by <page_id> argument (page index in the file).
  * It can be executed by a thread running in any cluster, as it uses remote
@@ -193,6 +193,22 @@
  * @ returns extended pointer on page descriptor if success / return XPTR_NULL if error.
  ******************************************************************************************/
-xptr_t mapper_remote_get_page( xptr_t    mapper_xp,
-                               uint32_t  page_id );
+xptr_t mapper_get_page( xptr_t    mapper_xp,
+                        uint32_t  page_id );
+
+/*******************************************************************************************
+ * This function returns an extended pointer on a page descriptor for the FAT mapper.
+ * The page is identified by <page_id> argument (page index in the FAT mapper).
+ * It can be executed by a thread running in any cluster, as it uses remote
+ * access primitives to scan the mapper.
+ * In case of miss, this function takes the mapper lock in WRITE_MODE, and call the 
+ * mapper_handle_miss() to load the missing page from device to mapper, using an RPC 
+ * when the mapper is remote.
+ *******************************************************************************************
+ * @ mapper_xp	: extended pointer on the mapper.
+ * @ page_id	: page index in file
+ * @ returns extended pointer on page descriptor if success / return XPTR_NULL if error.
+ ******************************************************************************************/
+xptr_t mapper_get_fat_page( xptr_t    mapper_xp,
+                            uint32_t  page_id );
 
 /*******************************************************************************************
@@ -234,18 +250,17 @@
 
 /*******************************************************************************************
- * This function scan all pages present in the mapper identified by the <mapper> argument,
- * and synchronize all pages marked as "dirty" on disk. 
+ * This function scan all pages present in the mapper identified by the <mapper_xp>
+ * argument, and synchronize all pages marked as "dirty" on disk. 
  * These pages are unmarked and removed from the local PPM dirty_list. 
- * This function must be called by a local thread running in same cluster as the mapper.
- * A remote thread must call the RPC_MAPPER_SYNC function.
- *******************************************************************************************
- * @ mapper   	: [in]  local pointer on local mapper.
- * @ returns 0 if success / return -1 if error.
- ******************************************************************************************/
-error_t mapper_sync( mapper_t *  mapper );
+ * It can be called by any thread running in any cluster.
+ *******************************************************************************************
+ * @ mapper_xp	: [in]  extended pointer on local mapper.
+ * @ returns 0 if success / return -1 if error.
+ ******************************************************************************************/
+error_t mapper_sync( xptr_t  mapper_xp );
 
 /*******************************************************************************************
  * This debug function displays the content of a given page of a given mapper, identified
- * by the <mapper_xp> and <page_xp> arguments.
+ * by the <mapper_xp> and <page_id> arguments.
  * The number of bytes to display in page is defined by the <nbytes> argument.
  * The format is eigth (32 bits) words per line in hexadecimal.
@@ -253,10 +268,10 @@
  *******************************************************************************************
  * @ mapper_xp	: [in]  extended pointer on the mapper.
- * @ page_xp	: [in]  extended pointer on page descriptor.
+ * @ page_id	: [in]  page_index in mapper.
  * @ nbytes     : [in]  number of bytes in page.
  * @ returns 0 if success / return -1 if error.
  ******************************************************************************************/
 void mapper_display_page( xptr_t     mapper_xp,
-                          xptr_t     page_xp,
+                          uint32_t   page_id,
                           uint32_t   nbytes );
 
Index: /trunk/kernel/mm/ppm.c
===================================================================
--- /trunk/kernel/mm/ppm.c	(revision 656)
+++ /trunk/kernel/mm/ppm.c	(revision 657)
@@ -3,5 +3,5 @@
  *
  * Authors  Ghassan Almaless (2008,2009,2010,2011,2012)
- *          Alain Greiner    (2016,2017,2018,2019)
+ *          Alain Greiner    (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
Index: /trunk/kernel/mm/vmm.c
===================================================================
--- /trunk/kernel/mm/vmm.c	(revision 656)
+++ /trunk/kernel/mm/vmm.c	(revision 657)
@@ -3,5 +3,5 @@
  *
  * Authors   Ghassan Almaless (2008,2009,2010,2011, 2012)
- *           Alain Greiner (2016,2017,2018,2019)
+ *           Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -1988,5 +1988,5 @@
     xptr_t gpt_xp = XPTR( local_cxy , &process->vmm.gpt );
 
-    // loop on PTEs in GPT to unmap PTE if (oldd_vpn_min <= vpn < new_vpn_min)
+    // loop on PTEs in GPT to unmap PTE if (old_vpn_min <= vpn < new_vpn_min)
 	for( vpn = old_vpn_min ; vpn < new_vpn_min ; vpn++ )
     {
@@ -2292,5 +2292,5 @@
         
         // get extended pointer on page descriptor
-        page_xp = mapper_remote_get_page( mapper_xp , page_id );
+        page_xp = mapper_get_page( mapper_xp , page_id );
 
         if ( page_xp == XPTR_NULL ) return EINVAL;
Index: /trunk/kernel/mm/vmm.h
===================================================================
--- /trunk/kernel/mm/vmm.h	(revision 656)
+++ /trunk/kernel/mm/vmm.h	(revision 657)
@@ -3,6 +3,5 @@
  *
  * Authors   Ghassan Almaless (2008,2009,2010,2011, 2012)
- *           Mohamed Lamine Karaoui (2015)
- *           Alain Greiner (2016,2017,2018,2019)
+ *           Alain Greiner (2016,2017,2018,2019,2020))
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -31,4 +30,5 @@
 #include <list.h>
 #include <queuelock.h>
+#include <remote_queuelock.h>
 #include <hal_gpt.h>
 #include <vseg.h>
@@ -208,8 +208,12 @@
 
 /*********************************************************************************************
- * This function modifies the size of the vseg identified by <process> and <base> arguments
- * in all clusters containing a VSL copy, as defined by <new_base> and <new_size> arguments.
- * This function is called by the sys_munmap() function, and can be called by a thread
- * running in any cluster, as it uses remote accesses.
+ * This function modifies the vseg identified by <process> and <base> arguments in all
+ * clusters containing a VSL copy, as defined by <new_base> and <new_size> arguments.
+ * The new vseg, defined by the <new_base> and <new_size> arguments must be included
+ * in the existing vseg. The target VSL size and base fields are modified in the VSL.
+ * This is done in all clusters containing a VMM copy to maintain VMM coherence. 
+ * It is called by the sys_munmap() and dev_fbf_resize_window() functions.
+ * It can be called by a thread running in any cluster, as it uses the vmm_resize_vseg() in
+ * the local cluster, and parallel RPC_VMM_RESIZE_VSEG for remote clusters.
  * It cannot fail, as only vseg registered  in VSL copies are updated.
  *********************************************************************************************
@@ -228,6 +232,7 @@
  * the VSL and remove all associated PTE entries from the GPT.
  * This is done in all clusters containing a VMM copy to maintain VMM coherence. 
- * This function can be called by a thread running in any cluster, as it uses the
- * vmm_remove_vseg() in the local cluster, and the RPC_VMM_REMOVE_VSEG for remote clusters.
+ * It is called by the sys_munmap() and dev_fbf_resize_window() functions.
+ * It can be called by a thread running in any cluster, as it uses the vmm_remove_vseg() in
+ * the local cluster, and parallel RPC_VMM_REMOVE_VSEG for remote clusters.
  * It cannot fail, as only vseg registered  in VSL copies are deleted.
  *********************************************************************************************
@@ -317,6 +322,5 @@
  * It must be called by a local thread, running in the cluster containing the modified VMM.
  * Use the RPC_VMM_REMOVE_VSEG if required.
- * It makes a kernel panic if the process is not registered in the local cluster,
- * or if the vseg is not registered in the process VSL.
+ * It makes a kernel panic if the process is not registered in the local cluster.
  * For all vseg types, the vseg is detached from local VSL, and all associated PTEs are
  * unmapped from local GPT. Other actions depend on the vseg type:
@@ -340,13 +344,9 @@
 /*********************************************************************************************
  * This function resize a local vseg identified by the <process> and <vseg> arguments.
- * It is called by the vmm_global_resize() function.
- * It must be called by a local thread, running in the cluster containing the modified VMM.
+ * Both the "size" and "base" fields are modified in the process VSL. When the new vseg
+ * contains less pages than the target vseg, the relevant pages are removed from the GPT. 
+ * It is called by the vmm_global_resize() and dev_fbf_resize_window() functions.
+ * It must be called by a local thread, running in the cluster containing the modified VSL.
  * Use the RPC_VMM_RESIZE_VSEG if required.
- * It makes a kernel panic if the process is not registered in the local cluster,
- * or if the vseg is not registered in the process VSL.
- * The new vseg, defined by the <new_base> and <new_size> arguments must be strictly
- * included in the target vseg. The target VSL size and base fields are modified in the VSL.
- * If the new vseg contains less pages than the target vseg, the relevant pages are
- * removed from the GPT. 
  * The VSL lock protecting the VSL must be taken by the caller.
  *********************************************************************************************
@@ -454,4 +454,3 @@
                          ppn_t  * ppn );
 
-
 #endif /* _VMM_H_ */
Index: /trunk/kernel/mm/vseg.c
===================================================================
--- /trunk/kernel/mm/vseg.c	(revision 656)
+++ /trunk/kernel/mm/vseg.c	(revision 657)
@@ -2,5 +2,5 @@
  * vseg.c - virtual segment (vseg) related operations
  *
- * Authors   Alain Greiner (2016,2017,2018,2019)
+ * Authors   Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
Index: /trunk/kernel/mm/vseg.h
===================================================================
--- /trunk/kernel/mm/vseg.h	(revision 656)
+++ /trunk/kernel/mm/vseg.h	(revision 657)
@@ -2,5 +2,5 @@
  * vseg.h - virtual segment (vseg) related operations
  *
- * Authors  Alain Greiner (2016,2017,2018,2019)
+ * Authors  Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
Index: /trunk/kernel/syscalls/shared_include/shared_fbf.h
===================================================================
--- /trunk/kernel/syscalls/shared_include/shared_fbf.h	(revision 656)
+++ /trunk/kernel/syscalls/shared_include/shared_fbf.h	(revision 657)
@@ -1,6 +1,6 @@
 /*
- * shared_framebuffer.h - Shared mnemonics used by the frame buffer related syscalls.
+ * shared_fbf.h - Shared mnemonics used by the frame buffer related syscalls.
  *
- * Author  Alain Greiner (2016,2017,2018,2019)
+ * Author  Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -26,14 +26,19 @@
 
 /******************************************************************************************* 
- * This enum defines the operation mnemonics for frame buffer access.
+ * This enum defines the user operation mnemonics for frame buffer access.
  ******************************************************************************************/
 
 typedef enum
 {
-    FBF_GET_CONFIG = 0,      
-    FBF_READ       = 1,
-    FBF_WRITE      = 2,
+    FBF_GET_CONFIG     = 0,      
+    FBF_DIRECT_READ    = 1,
+    FBF_DIRECT_WRITE   = 2,
+    FBF_CREATE_WINDOW  = 3,
+    FBF_DELETE_WINDOW  = 4,
+    FBF_REFRESH_WINDOW = 5,
+    FBF_MOVE_WINDOW    = 6,
+    FBF_RESIZE_WINDOW  = 7,
 }
-fbf_operation_type_t;
+fbf_usr_operation_type_t;
 
 #endif  // _SHARED_FRAMEBUFFER_H_
Index: /trunk/kernel/syscalls/shared_include/syscalls_numbers.h
===================================================================
--- /trunk/kernel/syscalls/shared_include/syscalls_numbers.h	(revision 656)
+++ /trunk/kernel/syscalls/shared_include/syscalls_numbers.h	(revision 657)
@@ -93,6 +93,7 @@
     SYS_GET_THREAD_INFO = 55,
     SYS_FBF             = 56,
+    SYS_SOCKET          = 57,
 
-    SYSCALLS_NR         = 57,
+    SYSCALLS_NR         = 58,
 
 } syscalls_t;
Index: /trunk/kernel/syscalls/sys_display.c
===================================================================
--- /trunk/kernel/syscalls/sys_display.c	(revision 656)
+++ /trunk/kernel/syscalls/sys_display.c	(revision 657)
@@ -2,5 +2,5 @@
  * sys_display.c - display the current state of a kernel structure on TXT0
  *
- * Author    Alain Greiner (2016,2017,2018, 2019)
+ * Author    Alain Greiner (2016,2017,2018,2019,2020)
  *  
  * Copyright (c) UPMC Sorbonne Universites
@@ -380,5 +380,5 @@
 
             // get extended pointer on target page
-            page_xp = mapper_remote_get_page( mapper_xp , page_id );
+            page_xp = mapper_get_page( mapper_xp , page_id );
 
             if( page_xp == XPTR_NULL )
@@ -446,11 +446,11 @@
         case DISPLAY_FAT:
         {
-            uint32_t  entries = (uint32_t)arg1;
-
-            if( entries > 4096 )
+            uint32_t  slots = (uint32_t)arg1;
+
+            if( slots > 1024 )
 	        {
 
 #if DEBUG_SYSCALLS_ERROR
-printk("\n[ERROR] in %s for FAT : nb_entries larger than 4096\n",
+printk("\n[ERROR] in %s for FAT : nb_slots larger than 1024\n",
 __FUNCTION__ );
 #endif
@@ -459,5 +459,5 @@
 	        }
 
-            if( entries == 0 )  // display fat context in cluster cxy
+            if( slots == 0 )  // display fat context in cluster cxy
             {
                 uint32_t  cxy = (uint32_t)arg0;
@@ -476,9 +476,9 @@
                 fatfs_display_ctx( cxy );
             }
-            else                  // display nb_entries in page 
+            else                  // display nb_slots in page 
             {                        
-                uint32_t page = (uint32_t)arg0;
-
-                fatfs_display_fat( page , 0 , entries );
+                uint32_t min  = (uint32_t)arg0;
+
+                fatfs_display_fat( min , slots );
             }
 
Index: /trunk/kernel/syscalls/sys_fbf.c
===================================================================
--- /trunk/kernel/syscalls/sys_fbf.c	(revision 656)
+++ /trunk/kernel/syscalls/sys_fbf.c	(revision 657)
@@ -2,5 +2,5 @@
  * sys_fbf.c - Acces the frame buffer peripheral.
  * 
- * Authors     Alain Greiner (2016,2017,2018,2019)
+ * Authors     Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -33,24 +33,28 @@
 #include <syscalls.h>
 
-//////////////////////////////////
-int sys_fbf( uint32_t   operation,
-             void     * arg0,           
-             void     * arg1,
-             void     * arg2 )
+////////////////////////
+int sys_fbf( reg_t arg0,
+             reg_t arg1,
+             reg_t arg2,
+             reg_t arg3 )
 {
 	vseg_t         * vseg;        // for vaddr check
     error_t          error;
 
+#if (DEBUG_SYS_FBF || CONFIG_INSTRUMENTATION_SYSCALLS)
+uint64_t     tm_start = hal_get_cycles();
+#endif
+
     thread_t       * this    = CURRENT_THREAD;
     process_t      * process = this->process;
 
-#if (DEBUG_SYS_FBF || CONFIG_INSTRUMENTATION_SYSCALLS)
-uint64_t     tm_start = hal_get_cycles();
-#endif
+    // for some operations, the MSB of arg0 can contain the wid 
+    uint32_t operation = arg0 & 0xFFFF;
 
 #if DEBUG_SYS_FBF
 if( DEBUG_SYS_FBF < tm_start )
-printk("\n[%s] thread[%x,%x] enter for %s / cycle %d\n",
-__FUNCTION__, process->pid, this->trdid, dev_fbf_cmd_str( operation ), (uint32_t)tm_start );
+printk("\n[%s] thread[%x,%x] enter for %s / arg1 %x / arg2 %x / arg3 %x / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, dev_fbf_cmd_str( operation ),
+arg1, arg2, arg3, (uint32_t)tm_start );
 #endif
 
@@ -65,18 +69,5 @@
             uint32_t type;
 
-            // check arg0 (width) in user vspace
-	        error = vmm_get_vseg( process , (intptr_t)arg0 , &vseg );
-	        if( error )
-            {
-
-#if DEBUG_SYSCALLS_ERROR
-printk("\n[ERROR] in %s : unmapped arg0 %x for %s / thread[%x,%x]\n",
-__FUNCTION__ , arg0, dev_fbf_cmd_str(operation), process->pid, this->trdid );
-#endif
-                this->errno = EINVAL;
-                return -1;
-            }
-
-            // check arg1 (height) in user vspace
+            // check "width" in user vspace
 	        error = vmm_get_vseg( process , (intptr_t)arg1 , &vseg );
 	        if( error )
@@ -88,9 +79,8 @@
 #endif
                 this->errno = EINVAL;
-                return -1;
-            }
-
-            // check arg2 (type) in user vspace
-	        error = vmm_get_vseg( process , (intptr_t)arg2 , &vseg );
+            }
+
+            // check "height" in user vspace
+	        error |= vmm_get_vseg( process , (intptr_t)arg2 , &vseg );
 	        if( error )
             {
@@ -101,5 +91,16 @@
 #endif
                 this->errno = EINVAL;
-                return -1;
+            }
+
+            // check "type" in user vspace
+	        error |= vmm_get_vseg( process , (intptr_t)arg3 , &vseg );
+	        if( error )
+            {
+
+#if DEBUG_SYSCALLS_ERROR
+printk("\n[ERROR] in %s : unmapped arg3 %x for %s / thread[%x,%x]\n",
+__FUNCTION__ , arg3, dev_fbf_cmd_str(operation), process->pid, this->trdid );
+#endif
+                this->errno = EINVAL;
             }
 
@@ -107,35 +108,149 @@
             dev_fbf_get_config( &width , &height , &type );
 
-            // transfer to user space
-            hal_copy_to_uspace( arg0 , XPTR( local_cxy , &width )  , sizeof(uint32_t) );
-            hal_copy_to_uspace( arg1 , XPTR( local_cxy , &height ) , sizeof(uint32_t) );
-            hal_copy_to_uspace( arg2 , XPTR( local_cxy , &type )   , sizeof(uint32_t) );
-        
-            break;
-        }
-        //////////////
-        case FBF_READ:
-        case FBF_WRITE:
-        {
-            // check arg0 (buffer) in user space
-	        error = vmm_get_vseg( process , (intptr_t)arg0 , &vseg );
-	        if( error )
-            {
-
-#if DEBUG_SYSCALLS_ERROR
-printk("\n[ERROR] in %s : unmapped arg0 %x for %s / thread[%x,%x]\n",
-__FUNCTION__ , arg0, dev_fbf_cmd_str(operation), process->pid, this->trdid );
-#endif
-                this->errno = EINVAL;
-                return -1;
-            }
-
-            // get length and offset values
-
-            uint32_t length = (uint32_t)(intptr_t)arg1;
-            uint32_t offset = (uint32_t)(intptr_t)arg2;
+            // transfer to user space if no error
+            if( error == 0 )
+            {
+                hal_copy_to_uspace( (void*)arg1, XPTR(local_cxy , &width ), sizeof(uint32_t) );
+                hal_copy_to_uspace( (void*)arg2, XPTR(local_cxy , &height), sizeof(uint32_t) );
+                hal_copy_to_uspace( (void*)arg3, XPTR(local_cxy , &type  ), sizeof(uint32_t) );
+            }
+            break;
+        }
+        /////////////////////
+        case FBF_DIRECT_READ:
+        case FBF_DIRECT_WRITE:
+        {
+            void    * buffer   = (void *)arg1;
+            uint32_t  npixels  = arg2;
+            uint32_t  offset   = arg3;
+            bool_t    is_write = (operation == FBF_DIRECT_WRITE);
+            
+            // check buffer in user space
+	        error = vmm_get_vseg( process , (intptr_t)buffer , &vseg );
+	        if( error )
+            {
+
+#if DEBUG_SYSCALLS_ERROR
+printk("\n[ERROR] in %s : unmapped buffer %x for %s / thread[%x,%x]\n",
+__FUNCTION__ , buffer, dev_fbf_cmd_str(operation), process->pid, this->trdid );
+#endif
+                this->errno = EINVAL;
+            }
+            else
+            {
+                // call relevant kernel function
+                error |= dev_fbf_move_data( is_write, buffer, npixels, offset );           
+
+                if( error )
+                {
+
+#if DEBUG_SYSCALLS_ERROR
+printk("\n[ERROR] in %s : cannot move data for %s / buffer %x / thread[%x,%x]\n",
+__FUNCTION__ , dev_fbf_cmd_str(operation), buffer, process->pid, this->trdid );
+#endif
+                    this->errno = EINVAL;
+                }
+            }
+            break;
+        }
+        ///////////////////////
+        case FBF_CREATE_WINDOW:
+        {
+            uint32_t   l_zero  = arg1 >> 16;
+            uint32_t   p_zero  = arg1 & 0xFFFF;
+            uint32_t   nlines  = arg2 >> 16;
+            uint32_t   npixels = arg2 & 0xFFFF;
+
+            // check buffer in user space
+	        error = vmm_get_vseg( process , (intptr_t)arg3 , &vseg );
+	        if( error )
+            {
+
+#if DEBUG_SYSCALLS_ERROR
+printk("\n[ERROR] in %s : unmapped user buffer %x for %s / thread[%x,%x]\n",
+__FUNCTION__ , (intptr_t)arg3, dev_fbf_cmd_str(operation), process->pid, this->trdid );
+#endif
+                this->errno = EINVAL;
+            }
+            else
+            {
+                // allocated buffer base address
+                intptr_t  user_buffer;
+     
+                // call relevant kernel function
+                error = dev_fbf_create_window( nlines,
+                                               npixels,
+                                               l_zero,
+                                               p_zero,
+                                               &user_buffer );
+                if( error == -1 )
+                {
+
+#if DEBUG_SYSCALLS_ERROR
+printk("\n[ERROR] in %s : cannot create window for %s / thread[%x,%x]\n",
+__FUNCTION__ , dev_fbf_cmd_str(operation), process->pid, this->trdid );
+#endif
+                    this->errno = EINVAL;
+                }
+
+                // copy vseg base address to user space buffer
+                hal_copy_to_uspace( (void *)arg3,
+                                    XPTR( local_cxy , &user_buffer ),
+                                    sizeof(intptr_t) );
+                hal_fence();
+            }
+            break;
+        }
+        ///////////////////////
+        case FBF_DELETE_WINDOW:
+        {
+            uint32_t  wid = arg1;
+
+            // call relevant kernel function
+            error = dev_fbf_delete_window( wid );
+                
+            if( error )
+            {
+
+#if DEBUG_SYSCALLS_ERROR
+printk("\n[ERROR] in %s : cannot delete window for %s / thread[%x,%x]\n",
+__FUNCTION__ , dev_fbf_cmd_str(operation), process->pid, this->trdid );
+#endif
+                this->errno = EINVAL;
+            }
+            break;
+        }
+        ////////////////////////
+        case FBF_REFRESH_WINDOW:
+        {
+            uint32_t  wid        = arg1;
+            uint32_t  line_first = arg2;
+            uint32_t  line_last  = arg3;
+
+            // call relevant kernel function
+            error = dev_fbf_refresh_window( wid,
+                                            line_first,
+                                            line_last );
+                
+            if( error )
+            {
+
+#if DEBUG_SYSCALLS_ERROR
+printk("\n[ERROR] in %s : cannot refresh window for %s / thread[%x,%x]\n",
+__FUNCTION__ , dev_fbf_cmd_str(operation), process->pid, this->trdid );
+#endif
+                this->errno = EINVAL;
+            }
+            break;
+        }
+        /////////////////////
+        case FBF_MOVE_WINDOW:
+        {
+            uint32_t  wid      = arg1; 
+            uint32_t  l_zero   = arg2;
+            uint32_t  p_zero   = arg3;
 
             // call relevant kernel function to move data
-            error = dev_fbf_move_data( operation , arg0 , length , offset ); 
+            error = dev_fbf_move_window( wid, l_zero, p_zero ); 
 
             if( error )
@@ -143,11 +258,30 @@
 
 #if DEBUG_SYSCALLS_ERROR
-printk("\n[ERROR] in %s : cannot move data for %s / buffer %x / thread[%x,%x]\n",
-__FUNCTION__ , dev_fbf_cmd_str(operation), arg0, process->pid, this->trdid );
-#endif
-                this->errno = EINVAL;
-                return -1;
-            }
-          
+printk("\n[ERROR] in %s : cannot move window / thread[%x,%x]\n",
+__FUNCTION__ , dev_fbf_cmd_str(operation), process->pid, this->trdid );
+#endif
+                this->errno = EINVAL;
+            }
+            break;
+        }
+        ///////////////////////
+        case FBF_RESIZE_WINDOW:
+        {
+            uint32_t  wid      = arg1; 
+            uint32_t  width    = arg2;
+            uint32_t  height   = arg3;
+
+            // call relevant kernel function to move data
+            error = dev_fbf_resize_window( wid , width , height ); 
+
+            if( error )
+            {
+
+#if DEBUG_SYSCALLS_ERROR
+printk("\n[ERROR] in %s : cannot move window / thread[%x,%x]\n",
+__FUNCTION__ , dev_fbf_cmd_str(operation), process->pid, this->trdid );
+#endif
+                this->errno = EINVAL;
+            }
             break;
         }
@@ -161,6 +295,7 @@
 #endif
             this->errno = EINVAL;
-            return -1;
-        }
+            error = -1;
+        }
+        break;
 	}  // end switch on operation
 
@@ -173,7 +308,6 @@
 #if DEBUG_SYS_FBF
 if( DEBUG_SYS_FBF < tm_end )
-printk("\n[%s] thread[%x,%x] exit for %s / cost = %d / cycle %d\n",
-__FUNCTION__, process->pid, this->trdid, dev_fbf_cmd_str( operation ),
-(uint32_t)(tm_end - tm_start), (uint32_t)tm_end );
+printk("\n[%s] thread[%x,%x] exit for %s / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, dev_fbf_cmd_str( operation ), (uint32_t)tm_end );
 #endif
 
@@ -183,5 +317,5 @@
 #endif
 
-    return 0;
+    return error;
 
 }  // end sys_fbf()
Index: /trunk/kernel/syscalls/sys_mmap.c
===================================================================
--- /trunk/kernel/syscalls/sys_mmap.c	(revision 656)
+++ /trunk/kernel/syscalls/sys_mmap.c	(revision 657)
@@ -2,5 +2,5 @@
  * sys_mmap.c - map files, memory or devices into process virtual address space
  * 
- * Authors       Alain Greiner (2016,2017,2018,2019)
+ * Authors       Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
Index: /trunk/kernel/syscalls/syscalls.h
===================================================================
--- /trunk/kernel/syscalls/syscalls.h	(revision 656)
+++ /trunk/kernel/syscalls/syscalls.h	(revision 657)
@@ -2,5 +2,5 @@
  * syscalls.h - Kernel side services for syscall handling.
  * 
- * Author     Alain Greiner (2016,2017,2018,2019)
+ * Author     Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -748,17 +748,36 @@
 
 /****************************************************************************************** 
- * [56] This function implements the non-standard "fbf_get_config", "fbf_read" and
- * "fbf_write" syscalls, used to access the frame buffer peripheral.
- ******************************************************************************************
- * @ operation  : [in] operation type (defined in shared_fbf.h)
- * @ arg0       : if config : pointer on width   / else : pointer on user buffer  
- * @ arg1       : if config : pointer on height  / else : number of bytes to move 
- * @ arg2       : if config : pointer on type    / else : offset in frame buffer
+ * [56] This generic function implements the non-standard FBF related syscalls.
+ * The operation types mnemonics are defined in the <shared_fbf> file.
+ * The supported operations are defined in the <almosmkh.h> & <almosmkh.c> files.
+ * This function ckecks the syscall arguments, and call the relevant kernel function.
+ ******************************************************************************************
+ * @ arg0       : operation type (mnemonics defined in shared_fbf.h)
+ * @ arg1       : depends on operation type
+ * @ arg2       : depends on operation type
+ * @ arg3       : depends on operation type
  * @ return 0 if success / return -1 if illegal argument.
  *****************************************************************************************/
-int sys_fbf( uint32_t   operation,
-             void     * arg0,
-             void     * arg1,
-             void     * arg2 );
+int sys_fbf( reg_t   arg0,
+             reg_t   arg1,
+             reg_t   arg2,
+             reg_t   arg3 );
+
+/****************************************************************************************** 
+ * [57] This generic function implements the socket related syscalls.
+ * The operation types mnemonics are defined in the <shared_socket> file.
+ * The supported operations are defined in the <socket.h> & <socket.c> files.
+ * This function ckecks the syscall arguments, and call the relevant kernel function.
+ ******************************************************************************************
+ * @ arg0       : operation type (mnemonics defined in shared_socket.h)
+ * @ arg1       : depends on operation type
+ * @ arg2       : depends on operation type
+ * @ arg3       : depends on operation type
+ * @ return 0 if success / return -1 if illegal argument.
+ *****************************************************************************************/
+int sys_socket( reg_t   arg0,
+                reg_t   arg1,
+                reg_t   arg2,
+                reg_t   arg3 );
 
 #endif  // _SYSCALLS_H_
Index: /trunk/libs/libalmosmkh/almosmkh.c
===================================================================
--- /trunk/libs/libalmosmkh/almosmkh.c	(revision 656)
+++ /trunk/libs/libalmosmkh/almosmkh.c	(revision 657)
@@ -2,5 +2,5 @@
  * almosmkh.c - User level ALMOS-MKH specific library implementation.
  * 
- * Author     Alain Greiner (2016,2017,2018,2019)
+ * Author     Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -122,6 +122,6 @@
 }
 
-///////////////////////////////
-unsigned int get_uint32( void )
+///////////////////////////////////////
+int get_uint32( unsigned int * buffer )
 {
     unsigned int  i;
@@ -168,5 +168,6 @@
         else if ( c == 0 )                                      // EOF character
         {
-            return -1;
+            overflow = 1;
+            done     = 1;
         }
 
@@ -208,5 +209,6 @@
     {
         // return value
-        return value;
+        *buffer = value;
+        return 0;
     }
     else
@@ -222,5 +224,6 @@
 
         // return 0 value 
-        return 0;
+        *buffer = 0;
+        return -1;
     }
 }  // end get_uint32()
@@ -388,11 +391,11 @@
 
 ///////////////////////////////////////
-int display_fat( unsigned int  page_id,
-                 unsigned int  nb_entries )
+int display_fat( unsigned int  min_slot,
+                 unsigned int  nb_slots )
 {
     return hal_user_syscall( SYS_DISPLAY,
                              DISPLAY_FAT,
-                             (reg_t)page_id,
-                             (reg_t)nb_entries, 0 );
+                             (reg_t)min_slot,
+                             (reg_t)nb_slots, 0 );
 } 
 
@@ -412,4 +415,5 @@
 {
    char          cmd;
+   int           error;
 
    while( 1 )
@@ -424,9 +428,14 @@
         if( cmd == 'b' )
         {
+            unsigned int pid;
+            unsigned int trdid;
+
             printf("b / pid = ");
-            unsigned int pid = get_uint32();
+            error = get_uint32( &pid );
+
             printf(" / trdid = ");
-            unsigned int trdid = get_uint32();
-            display_busylocks( pid , trdid );
+            error |= get_uint32( &trdid );
+
+            if( error == 0 ) display_busylocks( pid , trdid );
         }
         // return to calling process
@@ -436,12 +445,17 @@
             break;
         }
-        // display FAT mapper(page,entries)
+        // display FAT mapper(min,slots)
         else if( cmd == 'f' )
         {
-            printf("f / page = ");
-            unsigned int page = get_uint32();
-            printf(" / entries = ");
-            unsigned int entries = get_uint32();
-            display_fat( page , entries );
+            unsigned int min;
+            unsigned int slots;
+
+            printf(" / min = ");
+            error = get_uint32( &min );
+
+            printf(" / slots = ");
+            error |= get_uint32( &slots );
+
+            if( error == 0 ) display_fat( min , slots );
         }
         // list all supported commands
@@ -451,5 +465,5 @@
                    "- b : display on TXT0 busylocks taken by thread[pid,trdid]\n"
                    "- c : resume calling process execution\n"
-                   "- f : display on TXT0 FAT mapper[page,entries]\n"
+                   "- f : display on TXT0 FAT mapper[min_slot,nb_slotss]\n"
                    "- h : list of supported commands\n"
                    "- m : display on TXT0 mapper[path,page,nbytes]\n"
@@ -467,10 +481,16 @@
         {
             char  path[128];
+            unsigned int page;
+            unsigned int nbytes;
+
             printf("m / path = ");
-            int error = get_string( path , 128 );
+            error = get_string( path , 128 );
+
             printf(" / page = ");
-            unsigned int page = get_uint32();
+            error |= get_uint32( &page );
+
             printf(" / nbytes = ");
-            unsigned int nbytes = get_uint32();
+            error |= get_uint32( &nbytes );
+
             if( error == 0 ) display_mapper( path , page , nbytes );
         }
@@ -478,7 +498,10 @@
         else if( cmd == 'p' )
         {
+            unsigned int cxy;
+
             printf("p / cxy = ");
-            unsigned int cxy = get_uint32();
-            display_cluster_processes( cxy , 0 );
+            error = get_uint32( &cxy );
+
+            if( error == 0 ) display_cluster_processes( cxy , 0 );
         }
         // display DQDT
@@ -491,27 +514,42 @@
         else if( cmd == 's' )
         {
+            unsigned int cxy;
+            unsigned int lid;
+
             printf("s / cxy = ");
-            unsigned int cxy = get_uint32();
+            error = get_uint32( &cxy );
+
             printf(" / lid = ");
-            unsigned int lid = get_uint32();
-            display_sched( cxy , lid );
+            error |= get_uint32( &lid );
+
+            if( error == 0 ) display_sched( cxy , lid );
         }
         // display all processes attached to TXT(txt_id)
         else if( cmd == 't' )
         {
+            unsigned int txt_id;
+
             printf("t / txt_id = ");
-            unsigned int txt_id = get_uint32();
-            display_txt_processes( txt_id );
+            error = get_uint32( &txt_id );
+
+            if( error == 0 ) display_txt_processes( txt_id );
         }
         // display vmm state for process(cxy, pid)
         else if( cmd == 'v' )
         {
+            unsigned int cxy;
+            unsigned int pid;
+            unsigned int map;
+
             printf("v / cxy = ");
-            unsigned int cxy = get_uint32();
+            error = get_uint32( &cxy );
+
             printf(" / pid = ");
-            unsigned int pid = get_uint32();
+            error |= get_uint32( &pid );
+
             printf(" / mapping = ");
-            unsigned int map = get_uint32();
-            display_vmm( cxy , pid , map );
+            error |= get_uint32( &map );
+
+            if( error == 0 ) display_vmm( cxy , pid , map );
         }
         // force the calling process to exit
@@ -524,11 +562,18 @@
         else if( cmd == 'y' )
         {
+            unsigned int active;
+            unsigned int cxy;
+            unsigned int lid;
+
             printf("y / active = ");
-            unsigned int active = get_uint32();
+            error = get_uint32( &active );
+
             printf(" / cxy = ");
-            unsigned int cxy    = get_uint32();
+            error |= get_uint32( &cxy );
+
             printf(" / lid = ");
-            unsigned int lid    = get_uint32();
-            trace( active , cxy , lid );
+            error |= get_uint32( &lid );
+
+            if( error == 0 ) trace( active , cxy , lid );
         }
     }  // en while
@@ -1500,5 +1545,5 @@
 {
     return hal_user_syscall( SYS_FBF,
-                             FBF_GET_CONFIG, 
+                             (reg_t)FBF_GET_CONFIG, 
                              (reg_t)width,
                              (reg_t)height,                           
@@ -1512,5 +1557,5 @@
 {
     return hal_user_syscall( SYS_FBF,
-                             FBF_READ,
+                             (reg_t)FBF_DIRECT_READ, 
                              (reg_t)buffer,
                              (reg_t)length,                           
@@ -1524,8 +1569,66 @@
 {
     return hal_user_syscall( SYS_FBF,
-                             FBF_WRITE,
+                             (reg_t)FBF_DIRECT_WRITE,
                              (reg_t)buffer,
                              (reg_t)length,                           
                              (reg_t)offset );    
+}
+
+//////////////////////////////////////////////
+int fbf_create_window( unsigned int    l_zero,
+                       unsigned int    p_zero,
+                       unsigned int    nlines,
+                       unsigned int    npixels,
+                       void         ** buffer )
+{
+    return hal_user_syscall( SYS_FBF,
+                             (reg_t)FBF_CREATE_WINDOW,
+                             (reg_t)((l_zero << 16) | p_zero),
+                             (reg_t)((nlines << 16) | npixels),
+                             (reg_t)buffer );
+}
+
+//////////////////////////////////////////
+int fbf_delete_window( unsigned int  wid )
+{
+    return hal_user_syscall( SYS_FBF,
+                             (reg_t)FBF_DELETE_WINDOW,
+                             (reg_t)wid, 0, 0 );
+}
+
+///////////////////////////////////////
+int fbf_move_window( unsigned int  wid,
+                     unsigned int  l_zero,
+                     unsigned int  p_zero )
+{
+    return hal_user_syscall( SYS_FBF,
+                             (reg_t)FBF_MOVE_WINDOW,
+                             (reg_t)wid,
+                             (reg_t)l_zero,
+                             (reg_t)p_zero );
+}
+
+/////////////////////////////////////////
+int fbf_resize_window( unsigned int  wid,
+                       unsigned int  width,
+                       unsigned int  height )
+{
+    return hal_user_syscall( SYS_FBF,
+                             (reg_t)FBF_RESIZE_WINDOW,
+                             (reg_t)wid,
+                             (reg_t)width,
+                             (reg_t)height );
+}
+
+//////////////////////////////////////////
+int fbf_refresh_window( unsigned int  wid,
+                        unsigned int  line_first,
+                        unsigned int  line_last )
+{
+    return hal_user_syscall( SYS_FBF,
+                             (reg_t)FBF_REFRESH_WINDOW,
+                             (reg_t)wid,
+                             (reg_t)line_first,
+                             (reg_t)line_last );
 }
 
Index: /trunk/libs/libalmosmkh/almosmkh.h
===================================================================
--- /trunk/libs/libalmosmkh/almosmkh.h	(revision 656)
+++ /trunk/libs/libalmosmkh/almosmkh.h	(revision 657)
@@ -2,5 +2,5 @@
  * almosmkh.h - User level ALMOS-MKH specific library definition.
  * 
- * Author     Alain Greiner (2016,2017,2018,2019)
+ * Author     Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -29,9 +29,12 @@
  * - non standard system calls.
  * - debug functions.
- * - remote malloc extensions. 
+ * - remote malloc extensions.
+ * - a parallel_pthread_create function.
+ * - Frame Buffer access syscalls.
  **************************************************************************************/
 
 #include <pthread.h>
 #include <shared_almos.h>
+
 
 /****************** Non standard (ALMOS_MKH specific) system calls ********************/
@@ -138,10 +141,25 @@
 
 /***************************************************************************************
- * This syscall returns an unsigned 32 bits integer from the standard "stdin" stream.
+ * This syscall returns an 32 bits integer from the standard "stdin" stream.
+ * The value is copied in buffer pointed by the <buf> argument
  * Both decimal numbers and hexadecimal numbers (prefixed by 0x) are supported.
  ***************************************************************************************
- * returns the integer value if success / returns -1 if failure.
- **************************************************************************************/
-unsigned int get_uint32( void );
+ * @ buffer    : pointer on buffer.
+ * @ returns 0 if success / returns -1 if failure.
+ **************************************************************************************/
+int get_uint32( unsigned int * buffer );
+
+/***************************************************************************************
+ * This syscall returns a character string from the standard "stdin" stream.
+ * The string is copied in buffer pointed by the <string> argument. 
+ * The string length (including the NUL terminating character) cannot be larger
+ * than the size defined by the <size> argument.
+ ***************************************************************************************
+ * @ string   : pointer on the string buffer.
+ * @ maxlen   : max number of bytes in string buffer.
+ * @ returns 0 if success / returns -1 if failure.
+ **************************************************************************************/
+int get_string( char * string,
+                int    maxlen );
 
 
@@ -278,14 +296,16 @@
 
 /*************************************************************************************** 
- * This debug syscall displays on the kernel terminal TXT0 the content of one given
- * page of the FAT mapper.
- * It can be called by any thread running in any cluster.
- ***************************************************************************************
- * @ page_id    : page index in file.
- * @ nb_entries : number of bytes to display.
+ * This debug syscall displays on the kernel terminal TXT0 the content of <nb_slots>
+ * of the FAT mapper, starting from <min_slot>.
+ * If <nb_slots> is zero, it displays the state of the FATFS context in cluster
+ * defined by the <min_slot> argument interpreted as a cluster identifier.
+ * It can be called by any thread running in any cluster.
+ ***************************************************************************************
+ * @ min_slot   : first slot in page
+ * @ nb_slots   : number of slots to display.
  * @ return 0 if success / return -1 if page not found.
  **************************************************************************************/
-int display_fat( unsigned int page_id,
-                 unsigned int nb_entries );
+int display_fat( unsigned int min_slot,
+                 unsigned int nb_slots );
 
 /***************************************************************************************** 
@@ -551,6 +571,4 @@
 
 
-
-
 /********* Non standard (ALMOS-MKH specific) Frame Buffer access syscalls   *************/
 
@@ -562,10 +580,11 @@
 
 /*****************************************************************************************
- * This function returns in the <width> and <height> arguments the buffer size.
+ * This function returns in the <width>, <height> and <type> arguments 
+ * the implementation specific frame buffer features.
  *****************************************************************************************
  * @ width   : [out] number of pixels per line.
  * @ height  : [out] number of lines.
  * @ type    : [out] pixel encoding type.
- * @ returns 0 if success / returns -1 if error.
+ * @ returns 0 if success / returns -1 if not found.
  ****************************************************************************************/
 int fbf_get_config( unsigned int * width,
@@ -574,11 +593,101 @@
 
 /*****************************************************************************************
- * This blocking function moves <length> bytes from the frame buffer, starting 
- * from <offset>, to the user buffer defined by <buffer> argument.
+ * This function creates a new window defined by the <l_zero>, <p_zero>, <nlines>,
+ * and <npixels> arguments for the calling process, and register the process PID, the
+ * allocated <wid>, and the window size and coordinates in the FBF device descriptor.
+ * It returns in the output argument <buffer> the pointer on the buffer associated to
+ * the window, mapped in user space.
+ * Warning : Both pixel [p_zero,l_zero] and pixel [p_zero+npixels-1,l_zero+nlines-1]
+ * must be contained in the frame buffer.
+ *****************************************************************************************
+ * @ l_zero  : [in]  first line index in FBF coordinates system.
+ * @ p_zero  : [in]  first pixel index in FBF coordinates system.
+ * @ nlines  : [in]  number of lines in the window.
+ * @ npixels : [in]  number of pixels per line in the window.
+ * @ buffer  : [out] pointer on 
+ * @ returns <wid> if success / returns -1 if illegal size or coordinates.
+ ****************************************************************************************/
+int fbf_create_window( unsigned int   l_zero,
+                       unsigned int   p_zero,
+                       unsigned int   nlines,
+                       unsigned int   npixels,
+                       void        ** buffer );
+
+/*****************************************************************************************
+ * This function deletes an existing window, identified by the <wid> argument.
+ * The calling process must be the window owner.
+ *****************************************************************************************
+ * @ wid     : window identifier.
+ * @ returns 0 if success / returns -1 if not found or process not owner.
+ ****************************************************************************************/
+int fbf_delete_window( unsigned int  wid );
+
+/*****************************************************************************************
+ * This function refreshes in FBF all lines of a window identified by the <wid> argument,
+ * when the line index is in the interval [line_first,line_last[.
+ * It scans all registered windows to take into account the windows overlap.
+ *****************************************************************************************
+ * @ wid        : window identifier.
+ * @ line_first : first line index.
+ * @ line_last  : last line index (excluded).
+ * @ returns 0 if success / returns -1 if illegal argument.
+ ****************************************************************************************/
+int fbf_refresh_window( unsigned int   wid,
+                        unsigned int   line_first,
+                        unsigned int   line_last );
+
+/*****************************************************************************************
+ * This function changes the size of a window identified by the <wid> argument as defined
+ * by the <width> and height> arguments. The calling process must be the window owner.
+ * WARNING : Both pixel [p_zero,l_zero] and pixel [p_zero+width-1,l_zero+height-1]
+ * must be contained in the frame buffer.
+ *****************************************************************************************
+ * @ wid     : window identifier.
+ * @ width   : number of pixels in line (one byte per pixel).
+ * @ height  : number of lines in window.
+ * @ returns 0 if success / returns -1 if illegal arguments.
+ ****************************************************************************************/
+int fbf_resize_window( unsigned int   wid,
+                       unsigned int   width,
+                       unsigned int   height );
+
+/*****************************************************************************************
+ * This function changes the <l_zero> & <p_zero> coordinates of a window identified
+ * by the <wid> argument. The calling process must be the window owner.
+ * WARNING : Both pixel [p_zero,l_zero] and pixel [p_zero+width-1,l_zero+height-1]
+ * must be contained in the frame buffer.
+ *****************************************************************************************
+ * @ wid     : window identifier.
+ * @ l_zero  : new first line index in FBF.
+ * @ p_zero  : new first pixel index in FBF.
+ * @ returns 0 if success / returns -1 if illegal arguments.
+ ****************************************************************************************/
+int fbf_move_window( unsigned int  wid,
+                     unsigned int  l_zero,
+                     unsigned int  p_zero );
+
+/*****************************************************************************************
+ * This syscall is deprecated (january 2020 AG)
+ * This blocking function moves <length> pixels from the user buffer defined by <buffer>
+ * argument to the frame buffer, starting at <offset> in FBF.
  *****************************************************************************************
  * @ buffer  : pointer on buffer in user space.
  * @ length  : number of pixels (one byte per pixel).
- * @ offset  : first pixel index in frame buffer.
- * @ returns 0 if success / returns -1 if error.
+ * @ offset  : first pixel index in window.
+ * @ returns 0 if success / returns -1 if illegal arguments.
+ ****************************************************************************************/
+int fbf_write( void         * buffer,
+               unsigned int   length,
+               unsigned int   offset );
+
+/*****************************************************************************************
+ * This syscall is deprecated (january 2020 AG)
+ * This blocking function moves <length> pixels from the frame buffer, starting at 
+ * <offset> in FBF, to the user buffer defined by <buffer>  argument.
+ *****************************************************************************************
+ * @ buffer  : pointer on buffer in user space.
+ * @ length  : number of pixels (one byte per pixel).
+ * @ offset  : first pixel index in window.
+ * @ returns 0 if success / returns -1 if illegal arguments.
  ****************************************************************************************/
 int fbf_read( void         * buffer,
@@ -586,16 +695,4 @@
               unsigned int   offset );
 
-/*****************************************************************************************
- * This blocking function moves <length> bytes from the user buffer defined by <buffer>
- * argument to the frame buffer, starting at <offset> in frame buffer.
- *****************************************************************************************
- * @ buffer  : pointer on buffer in user space.
- * @ length  : number of pixels (one byte per pixel).
- * @ offset  : first pixel index in frame buffer.
- * @ returns 0 if success / returns -1 if error.
- ****************************************************************************************/
-int fbf_write( void         * buffer,
-               unsigned int   length,
-               unsigned int   offset );
 
 #endif /* _LIBALMOSMKH_H_ */
Index: /trunk/params-hard.mk
===================================================================
--- /trunk/params-hard.mk	(revision 656)
+++ /trunk/params-hard.mk	(revision 657)
@@ -2,6 +2,6 @@
 
 ARCH      = /Users/alain/soc/tsar-trunk-svn-2013/platforms/tsar_generic_iob
-X_SIZE    = 1
-Y_SIZE    = 1
+X_SIZE    = 2
+Y_SIZE    = 2
 NB_PROCS  = 1
 NB_TTYS   = 2
Index: /trunk/user/display/display.c
===================================================================
--- /trunk/user/display/display.c	(revision 656)
+++ /trunk/user/display/display.c	(revision 657)
@@ -15,5 +15,6 @@
 #include <almosmkh.h>
 
-#define FILENAME    "misc/images_128.raw"
+#define PATH_MAX_LENGHT    128
+
 #define FBF_TYPE    420         
 #define NPIXELS     128
@@ -56,4 +57,5 @@
     }
 }        
+
 ////////////////
 int main( void )
@@ -64,14 +66,7 @@
     unsigned int    fbf_type;
     unsigned int    nbytes;
-    unsigned int    image = 0;
     int             val;
     int             error;
-
-    unsigned long long start_cycle;
-
-    // get  start cycle
-    get_cycle( &start_cycle );
-
-    printf("\n[display] starts at cycle %d\n", (unsigned int)start_cycle );
+    char            pathname[PATH_MAX_LENGHT];
 
     // check frame buffer size
@@ -92,51 +87,50 @@
     }
 
+    // get pathname for input file 
+    while( 1 )
+    {
+        printf("\n[display] path = ");
+
+        error = get_string( pathname , PATH_MAX_LENGHT );
+
+        if ( error )  printf("\n[display error] cannot get path for input file\n" );
+        else  break;
+    }
+
     // open file
+    int fd = open( pathname , O_RDONLY , 0 );
 
-    FILE * f = fopen( FILENAME , NULL );
-//  int fd = open( FILENAME, O_RDONLY , 0 );
-
-    if( f == NULL )
+    if( fd < 0 )
     {
-        printf("\n[display error] Cannot open file <%s>\n", FILENAME );
+        printf("\n[display error] Cannot open file <%s>\n", pathname );
         exit( 0 );
     }
 
-    printf("\n[display] open file <%s>\n", FILENAME );
+    printf("\n[display] open file <%s>\n", pathname );
 
-    // loop on images
-    while ( 1 )
+    // load buffer from file
+    nbytes = read( fd , buffer , NPIXELS * NLINES );
+
+    if( nbytes != NPIXELS * NLINES )
     {
-        // load image from file
-        nbytes = fread( buffer , 1 , NPIXELS * NLINES , f );
-//      nbytes = read( fd , buffer , NPIXELS * NLINES );
-        if( nbytes != NPIXELS * NLINES )
-        {
-            printf("\n[display error] Cannot load image %d\n", image );
-            exit( 0 );
-        }
-
-        printf("\n[display] load image %d in buffer %x\n", image, buffer );
- 
-        // display image
-        error = fbf_write( buffer , NPIXELS * NLINES , 0 );
-
-        if( error )
-        {
-            printf("\n[display error] Cannot access frame buffer\n");
-            exit( 0 );
-        }
-
-        printf("\n[display] display image %d\n", image );
-
-        image++;
-
-        // interactive behaviour
-        val = getchar();
+        printf("\n[display error] Cannot load image \n" );
+        exit( 0 );
     }
 
+    printf("\n[display] load image in buffer %x\n", buffer );
+ 
+    // display image
+    error = fbf_write( buffer , NPIXELS * NLINES , 0 );
+
+    if( error )
+    {
+        printf("\n[display error] Cannot access frame buffer\n");
+        exit( 0 );
+    }
+
+    printf("\n[display] display image\n");
+
     // close file
-   fclose( f );
-// close( fd );
+    close( fd );
    
     exit(0);
Index: /trunk/user/ksh/ksh.c
===================================================================
--- /trunk/user/ksh/ksh.c	(revision 656)
+++ /trunk/user/ksh/ksh.c	(revision 657)
@@ -413,6 +413,6 @@
                "         display  barrier  pid\n"
                "         display  mapper   path     page     nbytes\n"
-               "         display  fat      page     entries\n"
-               "         display  fat      cxy      0\n");
+               "         display  fat      min      nslots\n"
+               "         display  fat      cxy      0\n" );
     }
     ////////////////////////////////////
@@ -581,14 +581,14 @@
         if( argc != 4 )
         {
-		    printf("  usage: display fat page_id nb_entries\n");
+		    printf("  usage: display fat  min_slot  nb_slots\n");
 	    }
         else
         {
-	        unsigned int page_id    = atoi(argv[2]);
-            unsigned int nb_entries = atoi(argv[3]);
-
-            if( display_fat( page_id, nb_entries ) )
+	        unsigned int min_slot = atoi(argv[2]);
+            unsigned int nb_slots = atoi(argv[3]);
+
+            if( display_fat( min_slot, nb_slots ) )
             {
-                printf("  error: cannot display page %d of fat\n", page_id );
+                printf("  error: cannot display fat\n");
             }
         }
@@ -1234,5 +1234,5 @@
 else
 {
-    strcpy( cmd , "load bin/user/kleenex.elf" );
+    strcpy( cmd , "load bin/user/transpose.elf" );
     printf("[ksh] %s\n", cmd );
     execute( cmd );
Index: /trunk/user/pgcd/pgcd.c
===================================================================
--- /trunk/user/pgcd/pgcd.c	(revision 656)
+++ /trunk/user/pgcd/pgcd.c	(revision 657)
@@ -18,8 +18,8 @@
 void main( void )
 {
-    int                opx;
-    int                opy;
-    int                x;
-    int                y;
+    unsigned int       opx;
+    unsigned int       opy;
+    unsigned int       x;
+    unsigned int       y;
     unsigned long long cycle;
     unsigned int       cxy;
@@ -34,10 +34,10 @@
     // get operand X
     printf("operand X = ");
-    opx = get_uint32();
+    get_uint32( &opx );
     printf("\n");
 
     // get operand Y
     printf("operand Y = ");
-    opy = get_uint32();
+    get_uint32( &opy );
     printf("\n");
 
Index: /trunk/user/transpose/transpose.c
===================================================================
--- /trunk/user/transpose/transpose.c	(revision 656)
+++ /trunk/user/transpose/transpose.c	(revision 657)
@@ -15,16 +15,12 @@
 // A core is identified by two indexes [cxy,lid] : cxy is the cluster identifier,
 // (that is NOT required to be a continuous index), and lid is the local core index,
-// (that must be in the [Ø,NCORES-1] range).
+// (that must be in the [0,NCORES-1] range).
 //
 // The main() function can run on any core in any cluster. This main thread
-// makes the initialisations, uses the pthread_create() syscall to launch (NTHREADS-1)
-// other threads in "attached" mode running in parallel the execute() function, calls 
-// himself the execute() function, wait completion of the (NTHREADS-1) other threads 
-// with a pthread_join(), and finally calls the instrument() function to display
-// and register the instrumentation results when execution is completed.
-// All threads run the execute() function, but each thread transposes only
-// (NLINES / NTHREADS) lines. This requires that NLINES == k * NTHREADS.
+// makes the initialisations, load the input file to the "image_in" buffer,
+// launches the working threads, calls the instrument() function when all working
+// threads complete, and saves the result "image_out" buffer to the output file.
 //
-// The number N of working threads is always defined by the number of cores availables
+// The number of working threads is always defined by the number of cores availables
 // in the architecture, but this application supports three placement modes.
 // In all modes, the working threads are identified by the [tid] continuous index 
@@ -51,11 +47,12 @@
 //   per core, and the same relation between the thread[tid] and the core[cxy][lpid].
 //    
-// The buf_in[x,y] and buf_out[put buffers containing the direct and transposed images
-// are distributed in clusters: each thread[cid][0] allocate a local input buffer
-// and load in this buffer all lines that must be handled by the threads sharing the
-// same cid, from the mapper of the input image file.
-// In the execute function, all threads in the group defined by the cid index read pixels
-// from the local buf_in[cid] buffer, and write pixels to all remote buf_out[cid] buffers.
-// Finally, each thread displays a part of the transposed image to the frame buffer.
+// Each working thread[cid][lid] run the "execute" function, that uses the "buf_in" and
+// "buf_out" local buffers, containing the direct and transposed images:
+// Each thread[cid][0] allocates two buf_in[cid] and buf_out[cid] buffers, load from
+// "image_in" to buf_in[cid] all lines that must be handled by the threads sharing the
+// same cid, and finally save from buf_out[cid] to "image_out" all lines that have been
+// transposed to buf_out[cid]. 
+// Each thread[cid][lid] in the group defined by the cid index read pixels from the 
+// local buf_in[cid] buffer, and write pixels to all remote // buf_out[cid] buffers.
 //
 // - The image  must fit the frame buffer size, that must be power of 2.
@@ -82,19 +79,32 @@
 #define THREADS_MAX           (X_MAX * Y_MAX * CORES_MAX)  // max number of threads
 
-#define IMAGE_SIZE            512                          // image size 
 #define IMAGE_TYPE            420                          // pixel encoding type
-#define INPUT_FILE_PATH       "/misc/couple_512.raw"       // input file pathname 
-#define OUTPUT_FILE_PATH      "/misc/transposed_512.raw"   // output file pathname 
+
+//#define IMAGE_SIZE            128                          // image size 
+//#define INPUT_FILE_PATH       "/misc/images_128.raw"       // input file pathname 
+//#define OUTPUT_FILE_PATH      "/misc/transposed_128.raw"   // output file pathname 
+
+//#define IMAGE_SIZE            256                          // image size 
+//#define INPUT_FILE_PATH       "/misc/lena_256.raw"         // input file pathname 
+#//define OUTPUT_FILE_PATH      "/misc/transposed_256.raw"   // output file pathname 
+
+//#define IMAGE_SIZE            512                          // image size 
+//#define INPUT_FILE_PATH       "/misc/couple_512.raw"       // input file pathname 
+//#define OUTPUT_FILE_PATH      "/misc/transposed_512.raw"   // output file pathname 
+
+#define IMAGE_SIZE            1024                         // image size 
+#define INPUT_FILE_PATH       "/misc/philips_1024.raw"     // input file pathname 
+#define OUTPUT_FILE_PATH      "/misc/transposed_1024.raw"  // output file pathname 
 
 #define SAVE_RESULT_FILE      0                            // save result image on disk
-#define USE_DQT_BARRIER       1                            // quad-tree barrier if non zero 
+#define USE_DQT_BARRIER       0                            // quad-tree barrier if non zero 
 
 #define NO_PLACEMENT          0                            // uncontrolefdthread placement
-#define EXPLICIT_PLACEMENT    0                            // explicit threads placement
-#define PARALLEL_PLACEMENT    1                            // parallel threads placement
-
-#define VERBOSE_MAIN          0                            // main function print comments
-#define VERBOSE_EXEC          0                            // exec function print comments
-#define VERBOSE_INSTRU        0                            // instru function print comments
+#define EXPLICIT_PLACEMENT    1                            // explicit threads placement
+#define PARALLEL_PLACEMENT    0                            // parallel threads placement
+
+#define VERBOSE_MAIN          1                            // main function print comments
+#define VERBOSE_MAIN_DETAILED 0                            // main function print comments
+#define VERBOSE_EXEC          1                            // exec function print comments
 
 
@@ -109,20 +119,28 @@
 // instrumentation counters for each thread in each cluster 
 // indexed by [cid][lid] : cluster continuous index / thread local index
+unsigned int ALOC_START[CLUSTERS_MAX][CORES_MAX] = {{ 0 }};
+unsigned int ALOC_END  [CLUSTERS_MAX][CORES_MAX] = {{ 0 }};
 unsigned int LOAD_START[CLUSTERS_MAX][CORES_MAX] = {{ 0 }};
 unsigned int LOAD_END  [CLUSTERS_MAX][CORES_MAX] = {{ 0 }};
 unsigned int TRSP_START[CLUSTERS_MAX][CORES_MAX] = {{ 0 }};
 unsigned int TRSP_END  [CLUSTERS_MAX][CORES_MAX] = {{ 0 }};
-unsigned int DISP_START[CLUSTERS_MAX][CORES_MAX] = {{ 0 }};
-unsigned int DISP_END  [CLUSTERS_MAX][CORES_MAX] = {{ 0 }};
-
-// pointer on buffer containing the input image, maped by the main to the input file
-unsigned char *  image_in;
-
-// pointer on buffer containing the output image, maped by the main to the output file
-unsigned char *  image_out;
-
-// arrays of pointers on distributed buffers indexed by [cid] : cluster continuous index
-unsigned char *  buf_in_ptr [CLUSTERS_MAX];
-unsigned char *  buf_out_ptr[CLUSTERS_MAX];
+unsigned int SAVE_START[CLUSTERS_MAX][CORES_MAX] = {{ 0 }};
+unsigned int SAVE_END  [CLUSTERS_MAX][CORES_MAX] = {{ 0 }};
+unsigned int FREE_START[CLUSTERS_MAX][CORES_MAX] = {{ 0 }};
+unsigned int FREE_END  [CLUSTERS_MAX][CORES_MAX] = {{ 0 }};
+
+// buffer containing the input image, loaded by the main from input file
+unsigned char  image_in[IMAGE_SIZE * IMAGE_SIZE];
+
+// buffer containing the output image, saved by the main to output file
+unsigned char  image_out[IMAGE_SIZE * IMAGE_SIZE];
+
+// arrays of pointers on distributed buffers indexed by [cid] 
+unsigned char *  buf_in [CLUSTERS_MAX];
+unsigned char *  buf_out[CLUSTERS_MAX];
+
+// pointer and identifier for dynamically allocated FBF window 
+void   *  win_buf;
+int       wid;
 
 // synchronisation barrier (all working threads)
@@ -205,5 +223,5 @@
     }
         
-    // main thread get identifiers for core executing main
+    // get identifiers for core executing main
     unsigned int  cxy_main;
     unsigned int  lid_main;
@@ -214,5 +232,11 @@
     unsigned int nthreads  = nclusters * ncores;
 
-    // main thread get FBF size and type
+    if( nthreads > IMAGE_SIZE )
+    {
+        printf("\n[transpose error] number of threads larger than number of lines\n");
+        exit( 0 );
+    }
+
+    // get FBF size and type
     unsigned int   fbf_width;
     unsigned int   fbf_height;
@@ -220,5 +244,5 @@
     fbf_get_config( &fbf_width , &fbf_height , &fbf_type );
 
-    if( (fbf_width != IMAGE_SIZE) || (fbf_height != IMAGE_SIZE) || (fbf_type != IMAGE_TYPE) )
+    if( (fbf_width < IMAGE_SIZE) || (fbf_height < IMAGE_SIZE) || (fbf_type != IMAGE_TYPE) )
     {
         printf("\n[transpose error] image does not fit FBF size or type\n");
@@ -226,17 +250,12 @@
     }
 
-    if( nthreads > IMAGE_SIZE )
-    {
-        printf("\n[transpose error] number of threads larger than number of lines\n");
-        exit( 0 );
-    }
-
-    unsigned int npixels = IMAGE_SIZE * IMAGE_SIZE;
+    // define total number of pixels
+    int npixels = IMAGE_SIZE * IMAGE_SIZE;
 
     // define instrumentation file name
     if( NO_PLACEMENT )
     {
-        printf("\n[transpose] %d cluster(s) / %d core(s) / FBF[%d*%d] / PID %x / NO_PLACE\n",
-        nclusters, ncores, fbf_width, fbf_height, getpid() );
+        printf("\n[transpose] %d cluster(s) / %d core(s) / <%s> / PID %x / NO_PLACE\n",
+        nclusters, ncores, INPUT_FILE_PATH , getpid() );
 
         // build instrumentation file name
@@ -251,6 +270,6 @@
     if( EXPLICIT_PLACEMENT )
     {
-        printf("\n[transpose] %d cluster(s) / %d core(s) / FBF[%d*%d] / PID %x / EXPLICIT\n",
-        nclusters, ncores, fbf_width, fbf_height, getpid() );
+        printf("\n[transpose] %d cluster(s) / %d core(s) / <%s> / PID %x / EXPLICIT\n",
+        nclusters, ncores, INPUT_FILE_PATH , getpid() );
 
         // build instrumentation file name
@@ -265,6 +284,6 @@
     if( PARALLEL_PLACEMENT )
     {
-        printf("\n[transpose] %d cluster(s) / %d core(s) / FBF[%d*%d] / PID %x / PARALLEL\n",
-        nclusters, ncores, fbf_width, fbf_height, getpid() );
+        printf("\n[transpose] %d cluster(s) / %d core(s) / <%s> / PID %x / PARALLEL\n",
+        nclusters, ncores, INPUT_FILE_PATH , getpid() );
 
         // build instrumentation file name
@@ -277,10 +296,28 @@
     }
 
+    // open a window in FBF
+    wid = fbf_create_window( 0,             // l_zero
+                             0,             // p_zero
+                             IMAGE_SIZE,    // lines
+                             IMAGE_SIZE,    // pixels
+                             &win_buf );
+    if( wid < 0) 
+    {
+        printf("\n[transpose error] cannot open FBF window\n");
+        exit( 0 );
+    }
+
+#if  VERBOSE_MAIN
+printf("\n[transpose] main on core[%x,%d] created FBF window %d / buffer %x\n",
+cxy_main, lid_main, wid , win_buf );
+#endif
+
     // open instrumentation file
     snprintf( pathname , 64 , "/home/%s", filename );
     FILE * f = fopen( pathname , NULL );
+
     if ( f == NULL ) 
     { 
-        printf("\n[transpose error] cannot open instrumentation file %s\n", pathname );
+        printf("\n[transpose error] cannot open instru file %s\n", pathname );
         exit( 0 );
     }
@@ -312,9 +349,9 @@
 
 #if  VERBOSE_MAIN
-printf("\n[transpose] main on core[%x,%d] completes barrier initialisation\n",
+printf("\n[transpose] main on core[%x,%d] completed barrier initialisation\n",
 cxy_main, lid_main );
 #endif
 
-    // main thread open input file
+    // open input file
     int fd_in = open( INPUT_FILE_PATH , O_RDONLY , 0 ); 
 
@@ -329,33 +366,5 @@
 #endif
 
-    // main thread map image_in buffer to input image file
-    image_in = (unsigned char *)mmap( NULL,
-                                      npixels,
-                                      PROT_READ,
-                                      MAP_FILE | MAP_SHARED,
-                                      fd_in,
-                                      0 );     // offset 
-    if ( image_in == NULL ) 
-    { 
-        printf("\n[transpose error] main cannot map buffer to file %s\n", INPUT_FILE_PATH );
-        exit( 0 );
-    }
-
-#if  VERBOSE_MAIN
-printf("\n[transpose] main map buffer to file <%s>\n", INPUT_FILE_PATH );
-#endif
-
-    // main thread display input image on FBF
-    if( fbf_write( image_in, 
-                   npixels,
-                   0 ) )
-    {
-        printf("\n[transpose error] main cannot access FBF\n");
-        exit( 0 );
-    }
-
-#if SAVE_RESULT_IMAGE
-
-    // main thread open output file
+    // open output file
     int fd_out = open( OUTPUT_FILE_PATH , O_CREAT , 0 ); 
 
@@ -366,26 +375,14 @@
     }
 
+    // move input image to input buffer
+    if( read( fd_in , image_in , npixels ) != npixels )
+    {
+        printf("\n[transpose error] main cannot read input image\n");
+        exit( 0 );
+    }
+
 #if  VERBOSE_MAIN
-printf("\n[transpose] main open file <%s> / fd = %d\n", OUTPUT_FILE_PATH , fd_out );
-#endif
-
-    // main thread map image_out buffer to output image file
-    image_out = (unsigned char *)mmap( NULL,
-                                       npixels,
-                                       PROT_WRITE,
-                                       MAP_FILE | MAP_SHARED,
-                                       fd_out,
-                                       0 );     // offset 
-    if ( image_out == NULL ) 
-    { 
-        printf("\n[transpose error] main cannot map buf_out to file %s\n", OUTPUT_FILE_PATH );
-        exit( 0 );
-    }
-
-#if  VERBOSE_MAIN
-printf("\n[transpose] main map buffer to file <%s>\n", OUTPUT_FILE_PATH );
-#endif
-
-#endif  // SAVE_RESULT_IMAGE
+printf("\n[transpose] main moved file <%s> to buf_in\n", INPUT_FILE_PATH );
+#endif
 
     /////////////////////////////////////////////////////////////////////////////////////
@@ -417,5 +414,5 @@
                 }
 
-#if VERBOSE_MAIN
+#if VERBOSE_MAIN_DETAILED
 printf("\n[transpose] main created thread %d\n", tid );
 #endif
@@ -450,6 +447,6 @@
             }
 
-#if VERBOSE_MAIN 
-printf("\n[transpose] main successfully joined thread %x\n", tid );
+#if VERBOSE_MAIN_DETAILED
+printf("\n[transpose] main joined thread %x\n", tid );
 #endif
         
@@ -500,5 +497,6 @@
                             exit( 0 );
                         }
-#if VERBOSE_MAIN 
+
+#if VERBOSE_MAIN_DETAILED
 printf("\n[transpose] main created thread[%d] on core[%x,%d]\n", tid, cxy, l );
 #endif
@@ -536,6 +534,7 @@
                     exit( 0 );
                 }
-#if VERBOSE_MAIN 
-printf("\n[transpose] main joined thread %d on core[%x,%d]\n", tid , cxy , l );
+
+#if VERBOSE_MAIN_DETAILED
+printf("\n[transpose] main joined thread %d\n", tid );
 #endif
             }
@@ -567,19 +566,63 @@
     /////////////////////////////////////////////////////////////////////////////
 
-    // main thread register instrumentation results
+    // register instrumentation results
     instrument( f , filename );
 
-    // main thread close input file 
+#if VERBOSE_MAIN
+printf("\n[transpose] main completed instrumentation\n");
+#endif
+
+/*
+    printf("\n> ");
+    getchar();
+
+    // move window
+    if( fbf_move_window( wid , 100 , 100 ) )
+    {
+        printf("\n[transpose error] main cannot move FBF window\n");
+        exit( 0 );
+    }
+
+    printf("\n> ");
+    getchar();
+*/    
+    // save image_out to output file
+    if( write( fd_out , image_out , npixels ) != npixels )
+    {
+        printf("\n[transpose error] main cannot write output image\n");
+        exit( 0 );
+    }
+
+#if VERBOSE_MAIN
+printf("\n[transpose] main saved buf_out to output file\n");
+#endif
+
+    // close input file 
     close( fd_in );
 
-#if SAVE_RESULT_IMAGE
-
-    // main thread close output file 
+#if VERBOSE_MAIN
+printf("\n[transpose] main closed input file\n");
+#endif
+
+    // close output file 
     close( fd_out );
 
-#endif
-
-    // main close instrumentation file
+#if VERBOSE_MAIN
+printf("\n[transpose] main closed output file\n");
+#endif
+
+    // close instrumentation file
     fclose( f );
+
+#if VERBOSE_MAIN
+printf("\n[transpose] main closed instrumentation file\n");
+#endif
+
+    // delete FBF window 
+    if( fbf_delete_window( wid ) )
+    {
+        printf("\n[transpose error] main cannot delete FBF window\n");
+        exit( 0 );
+    }
 
     // main thread suicide
@@ -597,8 +640,10 @@
 {
     unsigned long long   date;
+    unsigned int         l;         // line index for loop
+    unsigned int         p;         // pixel index for loop
+    int                  error;
+
+    unsigned char      * wbuf = win_buf;
  
-    unsigned int l;                         // line index for loop
-    unsigned int p;                         // pixel index for loop
-
     pthread_parallel_work_args_t * args = (pthread_parallel_work_args_t *)arguments;
 
@@ -613,6 +658,6 @@
     // get thread abstract identifiers 
     unsigned int tid = args->tid;
-    unsigned int cid = tid / ncores;   
-    unsigned int lid = tid % ncores;
+    unsigned int cid = tid / ncores;    // abstract cluster index
+    unsigned int lid = tid % ncores;    // local thread index
 
 #if VERBOSE_EXEC
@@ -620,4 +665,7 @@
 unsigned int lpid;
 get_core_id( &cxy , &lpid );   // get core physical identifiers
+#endif
+
+#if VERBOSE_EXEC
 printf("\n[transpose] exec[%d] on core[%x,%d] enters parallel exec\n",
 tid , cxy , lpid );
@@ -625,28 +673,33 @@
 
     get_cycle( &date );
-    LOAD_START[cid][lid] = (unsigned int)date;
-
-    // build total number of pixels per image
+    ALOC_START[cid][lid] = (unsigned int)date;
+
+    // compute total number of pixels per image
     unsigned int npixels = IMAGE_SIZE * IMAGE_SIZE;      
 
-    // build total number of threads and clusters
+    // compute total number of threads and clusters
     unsigned int nclusters = x_size * y_size;
     unsigned int nthreads  = nclusters * ncores;
 
-    unsigned int buf_size = npixels / nclusters;     // number of bytes in buf_in & buf_out
-    unsigned int offset   = cid * buf_size;       // offset in file (bytes)
-
-    unsigned char  * buf_in = NULL;        // private pointer on local input buffer
-    unsigned char  * buf_out = NULL;       // private pointer on local output buffer
-
-    // Each thread[cid,0] allocate a local buffer buf_in, and register
-    // the base adress in the global variable buf_in_ptr[cid]
-    // this local buffer is shared by all threads with the same cid
+    // compute number of pixels per cid & per thread
+    unsigned int pixels_per_cid = npixels / nclusters;
+    unsigned int pixels_per_lid = pixels_per_cid / ncores;
+
+    // compute first and last line per thread
+    unsigned int lines_per_cid = pixels_per_cid / IMAGE_SIZE;
+    unsigned int lines_per_lid = pixels_per_lid / IMAGE_SIZE;
+
+    unsigned int line_first = (cid * lines_per_cid) + (lid * lines_per_lid);
+    unsigned int line_last  = line_first + lines_per_lid;
+
+    // Each thread[cid,0] allocates two local buffers, and register the base 
+    // adresses in the global variable buf_in_ptr[cid] & buf_out_ptr[cid].
+    
     if( lid == 0 )
     {
         // allocate buf_in
-        buf_in = (unsigned char *)malloc( buf_size );
-
-        if( buf_in == NULL )
+        buf_in[cid] = (unsigned char *)malloc( pixels_per_cid );
+
+        if( buf_in[cid] == NULL )
         {
             printf("\n[transpose error] thread[%d] cannot allocate buf_in\n", tid );
@@ -654,7 +707,4 @@
         }
 
-        // register buf_in buffer in global array of pointers
-        buf_in_ptr[cid] = buf_in;
-
 #if VERBOSE_EXEC
 printf("\n[transpose] exec[%d] on core[%x,%d] allocated buf_in = %x\n",
@@ -662,27 +712,8 @@
 #endif
 
-    }
-
-    // Each thread[cid,0] copy relevant part of the image_in to buf_in
-    if( lid == 0 )
-    {
-        memcpy( buf_in,
-                image_in + offset,
-                buf_size );
-    } 
-
-#if VERBOSE_EXEC
-printf("\n[transpose] exec[%d] on core[%x,%d] loaded buf_in[%d]\n",
-tid , cxy , lpid , cid );
-#endif
-
-    // Each thread[cid,0] allocate a local buffer buf_out, and register
-    // the base adress in the global variable buf_out_ptr[cid]
-    if( lid == 0 )
-    {
         // allocate buf_out
-        buf_out = (unsigned char *)malloc( buf_size );
-
-        if( buf_out == NULL )
+        buf_out[cid] = (unsigned char *)malloc( pixels_per_cid );
+
+        if( buf_out[cid] == NULL )
         {
             printf("\n[transpose error] thread[%d] cannot allocate buf_in\n", tid );
@@ -690,7 +721,4 @@
         }
 
-        // register buf_in buffer in global array of pointers
-        buf_out_ptr[cid] = buf_out;
-
 #if VERBOSE_EXEC
 printf("\n[transpose] exec[%d] on core[%x,%d] allocated buf_out = %x\n",
@@ -699,5 +727,44 @@
 
     }
-    
+
+    get_cycle( &date );
+    ALOC_END[cid][lid] = (unsigned int)date;
+
+    /////////////////////////////////
+    pthread_barrier_wait( &barrier );
+    /////////////////////////////////
+
+    get_cycle( &date );
+    LOAD_START[cid][lid] = (unsigned int)date;
+
+    // all threads copy relevant part of the image_in to buf_in[cid]
+    memcpy( buf_in[cid] + (lid * pixels_per_lid), 
+            image_in + (cid * pixels_per_cid) + (lid * pixels_per_lid),
+            pixels_per_lid );
+
+#if VERBOSE_EXEC
+printf("\n[transpose] exec[%d] on core[%x,%d] loaded buf_in[%d]\n",
+tid , cxy , lpid , cid );
+#endif
+
+    // all local threads copy part of buf_in[cid] to FBF window for display
+    memcpy( wbuf + (cid * pixels_per_cid) + (lid * pixels_per_lid),
+            buf_in[cid] + (lid * pixels_per_lid),
+            pixels_per_lid );
+
+#if  VERBOSE_EXEC
+printf("\n[transpose] exec[%d] on core[%x,%d] loaded buf_in to FBF (first %d / last %d)\n",
+tid , cxy , lpid , line_first , line_last );
+#endif
+
+    // retresh window
+    error = fbf_refresh_window( wid , line_first , line_last );
+
+    if( error )
+    {
+        printf("\n[transpose error] exec[%d] cannot refresh FBF window\n", tid );
+        exit( 0 );
+    }
+
     get_cycle( &date );
     LOAD_END[cid][lid] = (unsigned int)date;
@@ -705,17 +772,14 @@
     /////////////////////////////////
     pthread_barrier_wait( &barrier );
+    /////////////////////////////////
 
     get_cycle( &date );
     TRSP_START[cid][lid] = (unsigned int)date;
 
-    // All threads contribute to parallel transpose from buf_in to buf_out
+    // All threads contribute to parallel transpose from buf_in to buf_out:
     // each thread makes the transposition for nlt lines (nlt = npixels/nthreads)
     // from line [tid*nlt] to line [(tid + 1)*nlt - 1]
     // (p,l) are the absolute pixel coordinates in the source image
-    // (l,p) are the absolute pixel coordinates in the source image
-    // (p,l) are the absolute pixel coordinates in the dest image
-
-    get_cycle( &date );
-    TRSP_START[cid][lid] = (unsigned int)date;
+    // (l,p) are the absolute pixel coordinates in the dest image
 
     unsigned int nlt   = IMAGE_SIZE / nthreads;    // number of lines per thread
@@ -729,5 +793,5 @@
     unsigned char byte;
 
-    unsigned int first = tid * nlt;     // first line index for a given thread
+    unsigned int first = tid * nlt;        // first line index for a given thread
     unsigned int last  = first + nlt;      // last line index for a given thread
 
@@ -742,5 +806,5 @@
             src_index = (l % nlc) * IMAGE_SIZE + p;
 
-            byte        = buf_in_ptr[src_cid][src_index];
+            byte = buf_in[src_cid][src_index];
 
             // write one byte to remote buf_out
@@ -748,5 +812,5 @@
             dst_index = (p % nlc) * IMAGE_SIZE + l;
 
-            buf_out_ptr[dst_cid][dst_index] = byte;
+            buf_out[dst_cid][dst_index] = byte;
         }
     }
@@ -762,40 +826,32 @@
     /////////////////////////////////
     pthread_barrier_wait( &barrier );
+    /////////////////////////////////
 
     get_cycle( &date );
-    DISP_START[cid][lid] = (unsigned int)date;
-
-    // All threads contribute to parallel display 
-    // from local buf_out to frame buffer
-    unsigned int  npt   = npixels / nthreads;   // number of pixels per thread
-
-    if( fbf_write( &buf_out_ptr[cid][lid * npt], 
-                   npt,
-                   npt * tid ) )
-    {
-        printf("\n[transpose error] thread[%d] cannot access FBF\n", tid );
-        pthread_exit( &THREAD_EXIT_FAILURE );
-    }
-
-#if VERBOSE_EXEC
-printf("\n[transpose] exec[%d] on core [%x,%d] completes display\n",
-tid, cxy , lpid );
-#endif
-
-    get_cycle( &date );
-    DISP_END[cid][lid] = (unsigned int)date;
-
-    /////////////////////////////////
-    pthread_barrier_wait( &barrier );
-
-#if SAVE_RESULT_IMAGE
-
-    // Each thread[cid,0] copy buf_out to relevant part of image_out
-    if( lid == 0 )
-    {
-        memcpy( image_out + offset,
-                buf_out,
-                buf_size );
-    } 
+    SAVE_START[cid][lid] = (unsigned int)date;
+
+    // each local threads copy part of buf_out[cid] to FBF window for display
+    memcpy( wbuf + (cid * pixels_per_cid) + (lid * pixels_per_lid),
+            buf_out[cid] + (lid * pixels_per_lid),
+            pixels_per_lid );
+
+#if  VERBOSE_EXEC
+printf("\n[transpose] exec[%d] on core[%x,%d] loaded buf_out to FBF (first %d / last %d)\n",
+tid , cxy , lpid , line_first , line_last );
+#endif
+
+    // refresh window
+    error = fbf_refresh_window( wid , line_first , line_last );
+
+    if( error )
+    {
+        printf("\n[transpose error] exec[%d] cannot refresh FBF window\n", tid );
+        exit( 0 );
+    }
+
+    // each local thread copy relevant part of buf_out to image_out
+    memcpy( image_out + (cid * pixels_per_cid) + (lid * pixels_per_lid),
+            buf_out[cid] + (lid * pixels_per_lid),
+            pixels_per_lid );
 
 #if VERBOSE_EXEC
@@ -804,13 +860,35 @@
 #endif
 
-#endif
-
-    // Each thread[cid,0] releases local buffer buf_out
+    get_cycle( &date );
+    SAVE_END[cid][lid] = (unsigned int)date;
+
+    /////////////////////////////////
+    pthread_barrier_wait( &barrier );
+    /////////////////////////////////
+
+    get_cycle( &date );
+    FREE_START[cid][lid] = (unsigned int)date;
+
+    // Each thread[cid,0] release local buffers buf_in & buf_out
+
     if( lid == 0 )
     {
-        // release buf_out
-        free( buf_in );
-        free( buf_out );
-    }
+        // release local buffers
+        free( buf_in[cid] );
+        free( buf_out[cid] );
+
+#if VERBOSE_EXEC
+printf("\n[transpose] exec[%d] on core[%x,%d] released buf_in & buf_out\n",
+tid , cxy , lpid );
+#endif
+
+    }
+
+    get_cycle( &date );
+    FREE_END[cid][lid] = (unsigned int)date;
+
+    /////////////////////////////////
+    pthread_barrier_wait( &barrier );
+    /////////////////////////////////
     
     // thread termination depends on the placement policy
@@ -829,5 +907,13 @@
         // <work> threads are running in attached mode
         // each thread, but de main, simply exit
-        if ( tid != tid_main )  pthread_exit( &THREAD_EXIT_SUCCESS );
+        if ( tid != tid_main )  
+        {
+
+#if VERBOSE_EXEC
+printf("\n[transpose] exec[%d] on core[%x,%d] exit\n",
+tid , cxy , lpid );
+#endif
+            pthread_exit( &THREAD_EXIT_SUCCESS );
+        }
     }
 
@@ -838,14 +924,15 @@
 
 
-///////////////////////////
+//////////////////////////
 void instrument( FILE * f,
                  char * filename )
 {
-    unsigned int x, y, l;
-
-#if VERBOSE_EXEC
-printf("\n[transpose] main enters instrument\n" );
-#endif
-
+    unsigned int cid;
+    unsigned int l;
+
+    unsigned int min_aloc_start = 0xFFFFFFFF;
+    unsigned int max_aloc_start = 0;
+    unsigned int min_aloc_ended = 0xFFFFFFFF;
+    unsigned int max_aloc_ended = 0;
     unsigned int min_load_start = 0xFFFFFFFF;
     unsigned int max_load_start = 0;
@@ -856,30 +943,37 @@
     unsigned int min_trsp_ended = 0xFFFFFFFF;
     unsigned int max_trsp_ended = 0;
-    unsigned int min_disp_start = 0xFFFFFFFF;
-    unsigned int max_disp_start = 0;
-    unsigned int min_disp_ended = 0xFFFFFFFF;
-    unsigned int max_disp_ended = 0;
+    unsigned int min_save_start = 0xFFFFFFFF;
+    unsigned int max_save_start = 0;
+    unsigned int min_save_ended = 0xFFFFFFFF;
+    unsigned int max_save_ended = 0;
+    unsigned int min_free_start = 0xFFFFFFFF;
+    unsigned int max_free_start = 0;
+    unsigned int min_free_ended = 0xFFFFFFFF;
+    unsigned int max_free_ended = 0;
  
-    for (x = 0; x < x_size; x++)
-    {
-        for (y = 0; y < y_size; y++)
+    for (cid = 0; cid < (x_size * y_size) ; cid++)
+    {
+        for ( l = 0 ; l < ncores ; l++ )
         {
-            unsigned int cid = y_size * x + y;
-
-            for ( l = 0 ; l < ncores ; l++ )
-            {
-                if (LOAD_START[cid][l] < min_load_start)  min_load_start = LOAD_START[cid][l];
-                if (LOAD_START[cid][l] > max_load_start)  max_load_start = LOAD_START[cid][l];
-                if (LOAD_END[cid][l]   < min_load_ended)  min_load_ended = LOAD_END[cid][l]; 
-                if (LOAD_END[cid][l]   > max_load_ended)  max_load_ended = LOAD_END[cid][l];
-                if (TRSP_START[cid][l] < min_trsp_start)  min_trsp_start = TRSP_START[cid][l];
-                if (TRSP_START[cid][l] > max_trsp_start)  max_trsp_start = TRSP_START[cid][l];
-                if (TRSP_END[cid][l]   < min_trsp_ended)  min_trsp_ended = TRSP_END[cid][l];
-                if (TRSP_END[cid][l]   > max_trsp_ended)  max_trsp_ended = TRSP_END[cid][l];
-                if (DISP_START[cid][l] < min_disp_start)  min_disp_start = DISP_START[cid][l];
-                if (DISP_START[cid][l] > max_disp_start)  max_disp_start = DISP_START[cid][l];
-                if (DISP_END[cid][l]   < min_disp_ended)  min_disp_ended = DISP_END[cid][l];
-                if (DISP_END[cid][l]   > max_disp_ended)  max_disp_ended = DISP_END[cid][l];
-            }
+            if (ALOC_START[cid][l] < min_aloc_start)  min_aloc_start = ALOC_START[cid][l];
+            if (ALOC_START[cid][l] > max_aloc_start)  max_aloc_start = ALOC_START[cid][l];
+            if (ALOC_END[cid][l]   < min_aloc_ended)  min_aloc_ended = ALOC_END[cid][l]; 
+            if (ALOC_END[cid][l]   > max_aloc_ended)  max_aloc_ended = ALOC_END[cid][l];
+            if (LOAD_START[cid][l] < min_load_start)  min_load_start = LOAD_START[cid][l];
+            if (LOAD_START[cid][l] > max_load_start)  max_load_start = LOAD_START[cid][l];
+            if (LOAD_END[cid][l]   < min_load_ended)  min_load_ended = LOAD_END[cid][l]; 
+            if (LOAD_END[cid][l]   > max_load_ended)  max_load_ended = LOAD_END[cid][l];
+            if (TRSP_START[cid][l] < min_trsp_start)  min_trsp_start = TRSP_START[cid][l];
+            if (TRSP_START[cid][l] > max_trsp_start)  max_trsp_start = TRSP_START[cid][l];
+            if (TRSP_END[cid][l]   < min_trsp_ended)  min_trsp_ended = TRSP_END[cid][l];
+            if (TRSP_END[cid][l]   > max_trsp_ended)  max_trsp_ended = TRSP_END[cid][l];
+            if (SAVE_START[cid][l] < min_save_start)  min_save_start = SAVE_START[cid][l];
+            if (SAVE_START[cid][l] > max_save_start)  max_save_start = SAVE_START[cid][l];
+            if (SAVE_END[cid][l]   < min_save_ended)  min_save_ended = SAVE_END[cid][l];
+            if (SAVE_END[cid][l]   > max_save_ended)  max_save_ended = SAVE_END[cid][l];
+            if (FREE_START[cid][l] < min_free_start)  min_free_start = FREE_START[cid][l];
+            if (FREE_START[cid][l] > max_free_start)  max_free_start = FREE_START[cid][l];
+            if (FREE_END[cid][l]   < min_free_ended)  min_free_ended = FREE_END[cid][l];
+            if (FREE_END[cid][l]   > max_free_ended)  max_free_ended = FREE_END[cid][l];
         }
     }
@@ -887,4 +981,14 @@
     printf( "\n ------ %s ------\n" , filename );
     fprintf( f , "\n ------ %s ------\n" , filename );
+
+    printf( " - ALOC_START : min = %d / max = %d / delta = %d\n",
+           min_aloc_start, max_aloc_start, max_aloc_start-min_aloc_start ); 
+    fprintf( f , " - ALOC_START : min = %d / max = %d / delta = %d\n",
+           min_aloc_start, max_aloc_start, max_aloc_start-min_aloc_start ); 
+
+    printf( " - ALOC_END   : min = %d / max = %d / delta = %d\n",
+           min_aloc_start, max_aloc_start, max_aloc_start-min_aloc_start ); 
+    fprintf( f , " - ALOC_END   : min = %d / max = %d / delta = %d\n",
+           min_aloc_start, max_aloc_start, max_aloc_start-min_aloc_start ); 
 
     printf( " - LOAD_START : min = %d / max = %d / delta = %d\n",
@@ -908,17 +1012,54 @@
            min_trsp_ended, max_trsp_ended, max_trsp_ended-min_trsp_ended ); 
 
-    printf( " - DISP_START : min = %d / max = %d / delta = %d\n",
-           min_disp_start, max_disp_start, max_disp_start-min_disp_start ); 
-    fprintf( f , " - DISP_START : min = %d / max = %d / delta = %d\n",
-           min_disp_start, max_disp_start, max_disp_start-min_disp_start ); 
-
-    printf( " - DISP_END   : min = %d / max = %d / delta = %d\n",
-           min_disp_ended, max_disp_ended, max_disp_ended-min_disp_ended ); 
-    fprintf( f , " - DISP_END   : min = %d / max = %d / delta = %d\n",
-           min_disp_ended, max_disp_ended, max_disp_ended-min_disp_ended ); 
-
-    printf( "\n   Sequencial = %d / Parallel = %d\n", SEQUENCIAL_TIME, PARALLEL_TIME );
-    fprintf( f , "\n   Sequencial = %d / Parallel = %d\n", SEQUENCIAL_TIME, PARALLEL_TIME );
-
+    printf( " - SAVE_START : min = %d / max = %d / delta = %d\n",
+           min_save_start, max_save_start, max_save_start-min_save_start ); 
+    fprintf( f , " - SAVE_START : min = %d / max = %d / delta = %d\n",
+           min_save_start, max_save_start, max_save_start-min_save_start ); 
+
+    printf( " - SAVE_END   : min = %d / max = %d / delta = %d\n",
+           min_save_ended, max_save_ended, max_save_ended-min_save_ended ); 
+    fprintf( f , " - SAVE_END   : min = %d / max = %d / delta = %d\n",
+           min_save_ended, max_save_ended, max_save_ended-min_save_ended ); 
+
+    printf( " - FREE_START : min = %d / max = %d / delta = %d\n",
+           min_free_start, max_free_start, max_free_start-min_free_start ); 
+    fprintf( f , " - FREE_START : min = %d / max = %d / delta = %d\n",
+           min_free_start, max_free_start, max_free_start-min_free_start ); 
+
+    printf( " - FREE_END   : min = %d / max = %d / delta = %d\n",
+           min_free_start, max_free_start, max_free_start-min_free_start ); 
+    fprintf( f , " - FREE_END   : min = %d / max = %d / delta = %d\n",
+           min_free_start, max_free_start, max_free_start-min_free_start ); 
+
+
+    printf( "\n   Sequencial %d"
+            "\n   Parallel   %d"
+            "\n   Alloc      %d"
+            "\n   Load       %d"
+            "\n   Transpose  %d"
+            "\n   Save       %d"
+            "\n   Free       %d\n" ,
+            SEQUENCIAL_TIME / 1000 ,
+            PARALLEL_TIME / 1000 ,
+            (max_aloc_ended - min_aloc_start) / 1000 ,
+            (max_load_ended - min_load_start) / 1000 ,
+            (max_trsp_ended - min_trsp_start) / 1000 ,
+            (max_save_ended - min_save_start) / 1000 ,
+            (max_free_ended - min_free_start) / 1000 );
+
+    fprintf( f , "\n   Sequencial %d"
+            "\n   Parallel   %d"
+            "\n   Alloc      %d"
+            "\n   Load       %d"
+            "\n   Transpose  %d"
+            "\n   Save       %d"
+            "\n   Free       %d\n" ,
+            SEQUENCIAL_TIME / 1000 ,
+            PARALLEL_TIME / 1000 ,
+            (max_aloc_ended - min_aloc_start) / 1000 ,
+            (max_load_ended - min_load_start) / 1000 ,
+            (max_trsp_ended - min_trsp_start) / 1000 ,
+            (max_save_ended - min_save_start) / 1000 ,
+            (max_free_ended - min_free_start) / 1000 );
 }  // end instrument()
 
