Index: trunk/kernel/mm/kcm.c
===================================================================
--- trunk/kernel/mm/kcm.c	(revision 672)
+++ trunk/kernel/mm/kcm.c	(revision 683)
@@ -36,4 +36,10 @@
 #include <kcm.h>
 
+///////////////////////////////////////////////////////////////////////////////////////////
+//         global variables
+///////////////////////////////////////////////////////////////////////////////////////////
+
+extern chdev_directory_t    chdev_dir;          // allocated in kernel_init.c
+
 
 /////////////////////////////////////////////////////////////////////////////////////
@@ -42,5 +48,5 @@
 
 //////////////////////////////////////////////////////////////////////////////////////
-// This static function must be called by a local thread.
+// This static function is called by the kcm_alloc() function.
 // 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.
@@ -55,10 +61,11 @@
 {
     // initialise variables
-    uint32_t size   = 1 << kcm->order;
-    uint32_t max    = kcm->max_blocks;
+    uint32_t order  = kcm->order;
     uint32_t count  = kcm_page->count;
     uint64_t status = kcm_page->status;
 
-assert( __FUNCTION__, (count < max) , "kcm_page should not be full" );
+// check kcm page not full
+assert( __FUNCTION__, (count < 63) ,
+"kcm_page should not be full / cxy %x / order %d / count %d", local_cxy, order, count );
 
     uint32_t index  = 1;
@@ -67,5 +74,5 @@
 	// allocate first free block in kcm_page, update status,
     // and count , compute index of allocated block in kcm_page 
-    while( index <= max )
+    while( index <= 63 )
     {
         if( (status & mask) == 0 )   // block found
@@ -81,6 +88,6 @@
     }
 
-    // change the page list if found block is the last
-    if( count == max-1 )
+    // switch page to full if last block
+    if( (count + 1) == 63 )
     {
 		list_unlink( &kcm_page->list);
@@ -92,13 +99,5 @@
 
 	// compute return pointer
-	void * ptr = (void *)((intptr_t)kcm_page + (index * size) );
-
-#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 in page %x / size %d / count %d / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, ptr, kcm_page, size, count + 1, cycle );
-#endif
+	void * ptr = (void *)((intptr_t)kcm_page + (index << order));
 
 	return ptr;
@@ -107,5 +106,5 @@
 
 /////////////////////////////////////////////////////////////////////////////////////
-// This private static function must be called by a local thread.
+// This static function is called by the kcm_free() function.
 // It releases a previously allocated block to the relevant kcm_page.
 // It makes a panic if the released block is not allocated in this page.
@@ -121,11 +120,10 @@
 {
     // initialise variables
-    uint32_t max    = kcm->max_blocks;
-    uint32_t size   = 1 << kcm->order;
+    uint32_t order  = kcm->order;
     uint32_t count  = kcm_page->count;
     uint64_t status = kcm_page->status;
     
-	// compute block index from block pointer
-	uint32_t index = ((intptr_t)block_ptr - (intptr_t)kcm_page) / size;
+	// compute block index from block pointer and kcm_page pointer
+	uint32_t index = ((intptr_t)block_ptr - (intptr_t)kcm_page) >> order;
 
     // compute mask in bit vector
@@ -136,5 +134,4 @@
         printk("\n[WARNING] in %s : block[%x,%x] not allocated / kcm %x / kcm_page %x\n",
         __FUNCTION__, local_cxy, block_ptr, kcm, kcm_page );
-        printk("   status %L / mask %L / sts & msk %L\n", status, mask, (status & mask) );
         kcm_remote_display( local_cxy , kcm );
         return;
@@ -145,6 +142,6 @@
 	kcm_page->count  = count - 1;
 
-	// change the page mode if page was full
-	if( count == max )
+	// switch page to active if it was full
+	if( count == 63 )
 	{
 		list_unlink( &kcm_page->list );
@@ -155,17 +152,8 @@
 	}
 
-#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] block %x / page %x / size %d / count %d / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, block_ptr, kcm_page, size, count - 1, cycle );
-#endif
-
 }  // kcm_put_block()
 
 /////////////////////////////////////////////////////////////////////////////////////
-// This static function must be called by a local thread.
-// It returns one non-full kcm_page with the following policy :
+// This static function  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.
@@ -188,12 +176,17 @@
     else                            // allocate a new page from PPM
 	{
-        // get one 4 Kbytes page from local PPM
-        page_t * page = ppm_alloc_pages( 0 );
+        // get KCM order
+        uint32_t order = kcm->order;
+
+        // get one kcm_page from  PPM
+        page_t * page = ppm_alloc_pages( order + 6 - CONFIG_PPM_PAGE_ORDER );
 
 	    if( page == NULL )
 	    {
-		    printk("\n[ERROR] in %s : failed to allocate page in cluster %x\n",
-	        __FUNCTION__ , local_cxy );
-
+
+#if DEBUG_KCM_ERROR
+printk("\n[ERROR] in %s : failed to allocate page in cluster %x\n",
+__FUNCTION__ , local_cxy );
+#endif
 		    return NULL;
         }
@@ -202,5 +195,5 @@
 	    xptr_t base_xp = ppm_page2base( XPTR( local_cxy , page ) );
 
-        // get local pointer on kcm_page
+        // get local pointer on kcm_page 
 	    kcm_page = GET_PTR( base_xp );
 
@@ -225,7 +218,10 @@
 {
 
-assert( __FUNCTION__, ((order > 5) && (order < 12)) , "order must be in [6,11]" );
-
-assert( __FUNCTION__, (CONFIG_PPM_PAGE_SHIFT == 12) , "check status bit_vector width" );
+// check argument
+assert( __FUNCTION__, (order < CONFIG_PPM_PAGE_ORDER),
+"order argument %d too large", order );
+
+assert( __FUNCTION__, (order >= CONFIG_CACHE_LINE_ORDER),
+"order argument %d too small", order );
 
 	// initialize lock
@@ -238,14 +234,11 @@
 	list_root_init( &kcm->active_root );
 
-	// initialize order and max_blocks
-	kcm->order      = order;
-    kcm->max_blocks = ( CONFIG_PPM_PAGE_SIZE >> order ) - 1;
+	// initialize order 
+	kcm->order = order;
  
 #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] initialised KCM / order %d / max_blocks %d\n",
-__FUNCTION__, this->process->pid, this->trdid, order, kcm->max_blocks );
+if( (DEBUG_KCM_ORDER == order) && (DEBUG_KCM_CXY == local_cxy) )
+printk("\n[%s] cxy %x / order %d\n",
+__FUNCTION__, local_cxy, order );
 #endif
 
@@ -287,18 +280,24 @@
 void * kcm_alloc( uint32_t order )
 {
-    kcm_t      * kcm_ptr;
+    kcm_t      * kcm;
 	kcm_page_t * kcm_page;
-	void       * block_ptr;
-
-   // min block size is 64 bytes
-    if( order < 6 ) order = 6;
-
-assert( __FUNCTION__, (order < 12) , "order = %d / must be less than 12" , order );
+	void       * block;
+
+// check argument
+assert( __FUNCTION__, (order < CONFIG_PPM_PAGE_ORDER),
+"order argument %d too large", order );
+
+#if DEBUG_KCM
+uint32_t cycle = (uint32_t)hal_get_cycles();
+#endif
+
+    // smallest block size is a cache line
+    if (order < CONFIG_CACHE_LINE_ORDER) order = CONFIG_CACHE_LINE_ORDER;
 
     // get local pointer on relevant KCM allocator
-    kcm_ptr = &LOCAL_CLUSTER->kcm[order - 6];
+    kcm = &LOCAL_CLUSTER->kcm[order - CONFIG_CACHE_LINE_ORDER];
 
     // build extended pointer on local KCM lock
-    xptr_t lock_xp = XPTR( local_cxy , &kcm_ptr->lock );
+    xptr_t lock_xp = XPTR( local_cxy , &kcm->lock );
 
 	// get KCM lock
@@ -306,17 +305,5 @@
 
     // get a non-full kcm_page
-    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 )
-{
-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
+    kcm_page = kcm_get_page( kcm );
 
     if( kcm_page == NULL )
@@ -326,70 +313,83 @@
 	}
 
-	// get a block from selected active page
-	block_ptr = kcm_get_block( kcm_ptr , kcm_page );
+#if DEBUG_KCM
+if( (DEBUG_KCM_ORDER == order) && (DEBUG_KCM_CXY == local_cxy) && (DEBUG_KCM < cycle) )
+printk("\n[%s] enter / order %d / kcm[%x,%x] / nb_full %d / nb_active %d\n"
+"    page %x / status [%x,%x] / count %d\n",
+__FUNCTION__, order, local_cxy, kcm, kcm->full_pages_nr, kcm->active_pages_nr,
+kcm_page, (uint32_t)(kcm_page->status>>32), (uint32_t)(kcm_page->status), kcm_page->count );
+#endif
+
+	// allocate a block from selected active page
+	block = kcm_get_block( kcm , kcm_page );
+
+#if DEBUG_KCM
+if( (DEBUG_KCM_ORDER == order) && (DEBUG_KCM_CXY == local_cxy) && (DEBUG_KCM < cycle) )
+printk("\n[%s] exit  / order %d / kcm[%x,%x] / nb_full %d / nb_active %d\n"
+"    page %x / status [%x,%x] / count %d\n",
+__FUNCTION__, order, local_cxy, kcm, kcm->full_pages_nr, kcm->active_pages_nr,
+kcm_page, (uint32_t)(kcm_page->status>>32), (uint32_t)(kcm_page->status), kcm_page->count );
+#endif
 
 	// release lock
 	remote_busylock_release( lock_xp );
 
-#if DEBUG_KCM
-if( DEBUG_KCM < cycle )
-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 ) );
-#endif
-
-	return block_ptr;
+	return block;
 
 }  // end kcm_alloc()
 
-/////////////////////////////////
-void kcm_free( void * block_ptr )
-{
-    kcm_t      * kcm_ptr;
+///////////////////////////////
+void kcm_free( void    * block,
+               uint32_t  order )
+{
+    kcm_t      * kcm;
 	kcm_page_t * kcm_page;
 
 // check argument
-assert( __FUNCTION__, (block_ptr != NULL) , "block pointer cannot be NULL" );
+assert( __FUNCTION__, (block != NULL), 
+"block pointer cannot be NULL" );
+
+#if DEBUG_KCM
+uint32_t cycle = (uint32_t)hal_get_cycles();
+#endif
+
+    // smallest block size is a cache line
+    if (order < CONFIG_CACHE_LINE_ORDER) order = CONFIG_CACHE_LINE_ORDER;
+
+    // get local pointer on relevant KCM allocator
+    kcm = &LOCAL_CLUSTER->kcm[order - CONFIG_CACHE_LINE_ORDER];
 
     // get local pointer on KCM page
-	kcm_page = (kcm_page_t *)((intptr_t)block_ptr & ~CONFIG_PPM_PAGE_MASK);
-
-    // get local pointer on KCM descriptor
-	kcm_ptr = kcm_page->kcm;
-
-#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 / 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
+    intptr_t kcm_page_mask = (1 << (order + 6)) - 1; 
+	kcm_page = (kcm_page_t *)((intptr_t)block & ~kcm_page_mask);
 
     // build extended pointer on local KCM lock
-    xptr_t lock_xp = XPTR( local_cxy , &kcm_ptr->lock );
+    xptr_t lock_xp = XPTR( local_cxy , &kcm->lock );
 
 	// get lock
 	remote_busylock_acquire( lock_xp );
 
-	// release block
-	kcm_put_block( kcm_ptr , kcm_page , block_ptr );
+#if DEBUG_KCM
+if( (DEBUG_KCM_ORDER == order) && (DEBUG_KCM_CXY == local_cxy) && (DEBUG_KCM < cycle) )
+printk("\n[%s] exit  / order %d / kcm[%x,%x] / nb_full %d / nb_active %d\n"
+"    page %x / status [%x,%x] / count %d\n",
+__FUNCTION__, order, local_cxy, kcm, kcm->full_pages_nr, kcm->active_pages_nr,
+kcm_page, (uint32_t)(kcm_page->status>>32), (uint32_t)(kcm_page->status), kcm_page->count );
+#endif
+
+	// release the block to the relevant page
+	kcm_put_block( kcm , kcm_page , block );
+
+#if DEBUG_KCM
+if( (DEBUG_KCM_ORDER == order) && (DEBUG_KCM_CXY == local_cxy) && (DEBUG_KCM < cycle) )
+printk("\n[%s] exit  / order %d / kcm[%x,%x] / nb_full %d / nb_active %d\n"
+"    page %x / status [%x,%x] / count %d\n",
+__FUNCTION__, order, local_cxy, kcm, kcm->full_pages_nr, kcm->active_pages_nr,
+kcm_page, (uint32_t)(kcm_page->status>>32), (uint32_t)(kcm_page->status), kcm_page->count );
+#endif
 
 	// 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() 
 
@@ -400,5 +400,6 @@
 
 /////////////////////////////////////////////////////////////////////////////////////
-// This static function can be called by any thread running in any cluster.
+// This static function is called by the kcm_remote_alloc() function.
+// It can be called by any thread running in any cluster.
 // 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.
@@ -415,10 +416,10 @@
 {
     uint32_t order  = hal_remote_l32( XPTR( kcm_cxy , &kcm_ptr->order ) );
-    uint32_t max    = hal_remote_l32( XPTR( kcm_cxy , &kcm_ptr->max_blocks ) );
     uint32_t count  = hal_remote_l32( XPTR( kcm_cxy , &kcm_page->count ) );
     uint64_t status = hal_remote_l64( XPTR( kcm_cxy , &kcm_page->status ) );
-    uint32_t size   = 1 << order;
-
-assert( __FUNCTION__, (count < max) , "kcm_page should not be full" );
+
+// check kcm_page not full
+assert( __FUNCTION__, (count < 63) , 
+"kcm_page should not be full / cxy %x / order %d / count %d", kcm_cxy, order, count );
 
     uint32_t index  = 1;
@@ -427,5 +428,5 @@
 	// allocate first free block in kcm_page, update status,
     // and count , compute index of allocated block in kcm_page 
-    while( index <= max )
+    while( index <= 63 )
     {
         if( (status & mask) == 0 )   // block found
@@ -440,6 +441,6 @@
     }
 
-	// change the page list if found block is the last
-	if( count == max-1 )
+	// swich the page to full if last block
+	if( (count + 1) == 63 )
 	{
 		list_remote_unlink( kcm_cxy , &kcm_page->list );
@@ -451,14 +452,5 @@
 
 	// compute return pointer
-	void * ptr = (void *)((intptr_t)kcm_page + (index * size) );
-
-#if DEBUG_KCM_REMOTE
-thread_t * this  = CURRENT_THREAD;
-uint32_t   cycle = (uint32_t)hal_get_cycles();
-if( DEBUG_KCM_REMOTE < cycle )
-printk("\n[%s] thread[%x,%x] get block %x in page %x / cluster %x / size %x / count %d\n",
-__FUNCTION__, this->process->pid, this->trdid, 
-ptr, kcm_page, kcm_cxy, size, count + 1 );
-#endif
+	void * ptr = (void *)((intptr_t)kcm_page + (index << order));
 
 	return ptr;
@@ -467,5 +459,6 @@
 
 /////////////////////////////////////////////////////////////////////////////////////
-// This private static function can be called by any thread running in any cluster.
+// This static function is called by the kcm_remote_free() function.
+// It can be called by any thread running in any cluster.
 // It releases a previously allocated block to the relevant kcm_page.
 // It changes the kcm_page status as required.
@@ -481,12 +474,10 @@
                                                              void       * block_ptr )
 {
-    uint32_t max    = hal_remote_l32( XPTR( kcm_cxy , &kcm_ptr->max_blocks ) );
     uint32_t order  = hal_remote_l32( XPTR( kcm_cxy , &kcm_ptr->order ) );
     uint32_t count  = hal_remote_l32( XPTR( kcm_cxy , &kcm_page->count ) );
     uint64_t status = hal_remote_l64( XPTR( kcm_cxy , &kcm_page->status ) );
-    uint32_t size   = 1 << order;
     
-	// compute block index from block pointer
-	uint32_t index = ((intptr_t)block_ptr - (intptr_t)kcm_page) / size;
+	// compute block index from block pointer and kcm_page pointer
+	uint32_t index = ((intptr_t)block_ptr - (intptr_t)kcm_page) >> order;
 
     // compute mask in bit vector
@@ -497,5 +488,4 @@
         printk("\n[WARNING] in %s : block[%x,%x] not allocated / kcm %x / kcm_page %x\n",
         __FUNCTION__, kcm_cxy, block_ptr, kcm_ptr, kcm_page );
-        printk("   status %L / mask %L / sts & msk %L\n", status, mask, (status & mask) );
         kcm_remote_display( kcm_cxy , kcm_ptr );
         return;
@@ -506,6 +496,6 @@
 	hal_remote_s32( XPTR( kcm_cxy , &kcm_page->count  ) , count - 1 );
 
-	// change the page list if page was full
-	if( count == max )
+	// switch the page to active if page was full
+	if( count == 63 )
 	{
 		list_remote_unlink( kcm_cxy , &kcm_page->list );
@@ -516,16 +506,8 @@
 	}
 
-#if (DEBUG_KCM_REMOTE & 1)
-thread_t * this  = CURRENT_THREAD;
-uint32_t   cycle = (uint32_t)hal_get_cycles();
-if( DEBUG_KCM_REMOTE < cycle )
-printk("\n[%s] thread[%x,%x] block %x / page %x / cluster %x / size %x / count %d\n",
-__FUNCTION__, this->process->pid, this->trdid, block_ptr, kcm_page, size, count - 1 )
-#endif
-
 }  // end kcm_remote_put_block()
 
 /////////////////////////////////////////////////////////////////////////////////////
-// This private static function can be called by any thread running in any cluster.
+// This static function can be called by any thread running in any cluster.
 // It gets one non-full KCM page from the remote KCM.
 // It allocates a page from remote PPM to populate the freelist, and initialises
@@ -545,12 +527,17 @@
     else                            // allocate a new page from PPM
 	{
-        // get one 4 Kbytes page from remote PPM
-        xptr_t page_xp = ppm_remote_alloc_pages( kcm_cxy , 0 );
-
+        // get KCM order
+        uint32_t order = hal_remote_l32( XPTR( kcm_cxy , &kcm_ptr->order ));
+
+        // get one kcm_page from PPM
+        xptr_t page_xp = ppm_remote_alloc_pages( kcm_cxy,
+                                                 order + 6 - CONFIG_PPM_PAGE_ORDER );
 	    if( page_xp == XPTR_NULL )
 	    {
-		    printk("\n[ERROR] in %s : failed to allocate page in cluster %x\n",
-	        __FUNCTION__ , kcm_cxy );
-
+
+#if DEBUG_KCM_ERROR
+printk("\n[ERROR] in %s : failed to allocate page in cluster %x\n",
+__FUNCTION__ , kcm_cxy );
+#endif
 		    return NULL;
         }
@@ -585,9 +572,16 @@
     void       * block_ptr;
 
-    if( order < 6 ) order = 6;
-
-assert( __FUNCTION__, (order < 12) , "order = %d / must be less than 12" , order );
-
-    // get local pointer on relevant KCM allocator
+// check kcm_cxy argument
+assert( __FUNCTION__, cluster_is_active( kcm_cxy ),
+"cluster %x not active", kcm_cxy );
+
+// check order argument
+assert( __FUNCTION__, (order < CONFIG_PPM_PAGE_ORDER) ,
+"order argument %d too large", order );
+
+    // smallest size is a cache line
+    if( order < CONFIG_CACHE_LINE_ORDER ) order = CONFIG_CACHE_LINE_ORDER;
+
+    // get local pointer on relevant KCM allocator (same in all clusters)
     kcm_ptr = &LOCAL_CLUSTER->kcm[order - 6];
 
@@ -607,37 +601,76 @@
 	}
 
+#if DEBUG_KCM
+uint32_t cycle     = (uint32_t)hal_get_cycles();
+uint32_t nb_full   = hal_remote_l32( XPTR( kcm_cxy , &kcm_ptr->full_pages_nr ));
+uint32_t nb_active = hal_remote_l32( XPTR( kcm_cxy , &kcm_ptr->active_pages_nr ));
+uint64_t status    = hal_remote_l64( XPTR( kcm_cxy , &kcm_page->status ));
+uint32_t count     = hal_remote_l32( XPTR( kcm_cxy , &kcm_page->count ));
+#endif
+
+
+#if DEBUG_KCM
+if( (DEBUG_KCM_ORDER == order) && (DEBUG_KCM_CXY == local_cxy) && (DEBUG_KCM < cycle) )
+printk("\n[%s] enter / order %d / kcm[%x,%x] / nb_full %d / nb_active %d\n"
+"    page %x / status [%x,%x] / count %d\n",
+__FUNCTION__, order, kcm_cxy, kcm_ptr, nb_full, nb_active,
+kcm_page, (uint32_t)(status>>32), (uint32_t)(status), kcm_page->count );
+#endif
+
 	// get a block from selected active page
 	block_ptr = kcm_remote_get_block( kcm_cxy , kcm_ptr , kcm_page );
 
+#if DEBUG_KCM
+if( (DEBUG_KCM_ORDER == order) && (DEBUG_KCM_CXY == local_cxy) && (DEBUG_KCM < cycle) )
+printk("\n[%s] exit  / order %d / kcm[%x,%x] / nb_full %d / nb_active %d\n"
+"    page %x / status [%x,%x] / count %d\n",
+__FUNCTION__, order, kcm_cxy, kcm_ptr, nb_full, nb_active,
+kcm_page, (uint32_t)(status>>32), (uint32_t)(status), kcm_page->count );
+#endif
+
 	// release lock
 	remote_busylock_release( lock_xp );
 
-#if DEBUG_KCM_REMOTE
-thread_t * this  = CURRENT_THREAD;
-uint32_t   cycle = (uint32_t)hal_get_cycles();
-if( DEBUG_KCM_REMOTE < cycle )
-printk("\n[%s] thread[%x,%x] allocated block %x / order %d / kcm[%x,%x]\n",
-__FUNCTION__, this->process->pid, this->trdid, block_ptr, order, kcm_cxy, kcm_ptr );
-#endif
-
 	return block_ptr;
 
 }  // end kcm_remote_alloc()
 
-/////////////////////////////////////
-void kcm_remote_free( cxy_t  kcm_cxy,
-                      void * block_ptr )
+////////////////////////////////////////
+void kcm_remote_free( cxy_t     kcm_cxy,
+                      void    * block_ptr,
+                      uint32_t  order )
 {
 	kcm_t      * kcm_ptr;
 	kcm_page_t * kcm_page;
 
-// check argument
-assert( __FUNCTION__, (block_ptr != NULL) , "block pointer cannot be NULL" );
-
-    // get local pointer on remote KCM page
-	kcm_page = (kcm_page_t *)((intptr_t)block_ptr & ~CONFIG_PPM_PAGE_MASK);
-
-    // get local pointer on remote KCM
-	kcm_ptr = hal_remote_lpt( XPTR( kcm_cxy , &kcm_page->kcm ) );
+// check kcm_cxy argument
+assert( __FUNCTION__, cluster_is_active( kcm_cxy ),
+"cluster %x not active", kcm_cxy );
+
+// check block_ptr argument
+assert( __FUNCTION__, (block_ptr != NULL),
+"block pointer cannot be NULL" );
+
+// check order argument
+assert( __FUNCTION__, (order < CONFIG_PPM_PAGE_ORDER) ,
+"order argument %d too large", order );
+
+    // smallest block size is a cache line
+    if (order < CONFIG_CACHE_LINE_ORDER) order = CONFIG_CACHE_LINE_ORDER;
+
+    // get local pointer on relevant KCM allocator (same in all clusters)
+    kcm_ptr = &LOCAL_CLUSTER->kcm[order - CONFIG_CACHE_LINE_ORDER];
+
+    // get local pointer on KCM page
+    intptr_t kcm_page_mask = (1 << (order + 6)) - 1; 
+	kcm_page = (kcm_page_t *)((intptr_t)block_ptr & ~kcm_page_mask);
+
+#if DEBUG_KCM
+uint32_t cycle     = (uint32_t)hal_get_cycles();
+uint32_t nb_full   = hal_remote_l32( XPTR( kcm_cxy , &kcm_ptr->full_pages_nr ));
+uint32_t nb_active = hal_remote_l32( XPTR( kcm_cxy , &kcm_ptr->active_pages_nr ));
+uint64_t status    = hal_remote_l64( XPTR( kcm_cxy , &kcm_page->status ));
+uint32_t count     = hal_remote_l32( XPTR( kcm_cxy , &kcm_page->count ));
+#endif
 
     // build extended pointer on remote KCM lock
@@ -647,18 +680,25 @@
 	remote_busylock_acquire( lock_xp );
 
-	// release block
+#if DEBUG_KCM
+if( (DEBUG_KCM_ORDER == order) && (DEBUG_KCM_CXY == local_cxy) && (DEBUG_KCM < cycle) )
+printk("\n[%s] enter / order %d / kcm[%x,%x] / nb_full %d / nb_active %d\n"
+"    page %x / status [%x,%x] / count %d\n",
+__FUNCTION__, order, kcm_cxy, kcm_ptr, nb_full, nb_active,
+kcm_page, (uint32_t)(status>>32), (uint32_t)(status), kcm_page->count );
+#endif
+
+	// release the block to the relevant page
 	kcm_remote_put_block( kcm_cxy , kcm_ptr , kcm_page , block_ptr );
+
+#if DEBUG_KCM
+if( (DEBUG_KCM_ORDER == order) && (DEBUG_KCM_CXY == local_cxy) && (DEBUG_KCM < cycle) )
+printk("\n[%s] exit  / order %d / kcm[%x,%x] / nb_full %d / nb_active %d\n"
+"    page %x / status [%x,%x] / count %d\n",
+__FUNCTION__, order, kcm_cxy, kcm_ptr, nb_full, nb_active,
+kcm_page, (uint32_t)(status>>32), (uint32_t)(status), kcm_page->count );
+#endif
 
 	// release lock
 	remote_busylock_release( lock_xp );
-
-#if DEBUG_KCM_REMOTE
-thread_t * this  = CURRENT_THREAD;
-uint32_t   cycle = (uint32_t)hal_get_cycles();
-uint32_t   order = hal_remote_l32( XPTR( kcm_cxy , &kcm_ptr->order ) );
-if( DEBUG_KCM_REMOTE < cycle )
-printk("\n[%s] thread[%x,%x] released block %x / order %d / kcm[%x,%x]\n",
-__FUNCTION__, this->process->pid, this->trdid, block_ptr, order, kcm_cxy, kcm_ptr );
-#endif
 
 }  // end kcm_remote_free
@@ -673,9 +713,20 @@
     uint32_t       count;
 
+    // get pointers on TXT0 chdev
+    xptr_t    txt0_xp  = chdev_dir.txt_tx[0];
+    cxy_t     txt0_cxy = GET_CXY( txt0_xp );
+    chdev_t * txt0_ptr = GET_PTR( txt0_xp );
+
+    // get extended pointer on remote TXT0 chdev lock
+    xptr_t    txt0_lock_xp = XPTR( txt0_cxy , &txt0_ptr->wait_lock );
+
+    // get TXT0 lock 
+    remote_busylock_acquire( txt0_lock_xp );
+
     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_nr %d / active_pages_nr %d\n",
+	nolock_printk("*** KCM : cxy %x / order %d / full_pages_nr %d / active_pages_nr %d\n",
 	kcm_cxy, order, full_pages_nr, active_pages_nr );
 
@@ -688,6 +739,6 @@
             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 );
+            nolock_printk("- active page %x / status (%x,%x) / count %d\n",
+            kcm_page, (uint32_t)( status<< 32 ), (uint32_t)( status ), count );
         }
     }
@@ -701,7 +752,11 @@
             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 );
+            nolock_printk("- full page %x / status (%x,%x) / count %d\n",
+            kcm_page, (uint32_t)( status<< 32 ), (uint32_t)( status ), count );
         }
     }
+
+    // release TXT0 lock 
+    remote_busylock_release( txt0_lock_xp );
+
 }  // end kcm remote_display()
