/*
 * remote_busylock.c - remote kernel busy-waiting lock implementation.
 *
 * Authors     Alain Greiner (2016,2017,2018)
 *
 * 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_irqmask.h>
#include <hal_special.h>
#include <hal_remote.h>
#include <thread.h>
#include <remote_busylock.h>

//////////////////////////////////////////////////////////////////////////////
//                Extern global variables
//////////////////////////////////////////////////////////////////////////////

extern char               * lock_type_str[];    // allocated in kernel_init.c
extern chdev_directory_t    chdev_dir;          // allocated in kernel_init.c


////////////////////////////////////////////
void remote_busylock_init( xptr_t   lock_xp,
                           uint32_t type )
{
    // get remote lock cluster and local pointer 
    cxy_t        lock_cxy = GET_CXY( lock_xp );
    busylock_t * lock_ptr = GET_PTR( lock_xp );

    hal_remote_s32( XPTR( lock_cxy , &lock_ptr->ticket  ) , 0 );
    hal_remote_s32( XPTR( lock_cxy , &lock_ptr->current ) , 0 );
    hal_remote_s32( XPTR( lock_cxy , &lock_ptr->type    ) , type );

#if DEBUG_BUSYLOCK
    xlist_entry_init( XPTR( lock_cxy , &lock_ptr->xlist ) ); 
#endif

}

////////////////////////////////////////////////
void remote_busylock_acquire( xptr_t   lock_xp )
{
    reg_t      save_sr;
    thread_t * this = CURRENT_THREAD;

    // get remote lock cluster and local pointer 
    cxy_t        lock_cxy = GET_CXY( lock_xp );
    busylock_t * lock_ptr = GET_PTR( lock_xp );

    // enter critical section 
    hal_disable_irq( &save_sr );
 
    // get one ticket
    uint32_t ticket = hal_remote_atomic_add( XPTR( lock_cxy , &lock_ptr->ticket ) , 1 );

    // poll current until success
    xptr_t current_xp = XPTR( lock_cxy , &lock_ptr->current );
    while( hal_remote_l32( current_xp ) != ticket ) asm volatile ("nop");

    // increment thread locks counter
    this->busylocks++;

    // save SR in lock descriptor
    hal_remote_s32( XPTR( lock_cxy , &lock_ptr->save_sr ) , save_sr );

    // memory barrier to update busylock and thread state
    hal_fence();
    
#if DEBUG_BUSYLOCK
uint32_t type = hal_remote_l32( XPTR( lock_cxy , &lock_ptr->type ) );
if( (type != LOCK_CHDEV_TXT0) &&
    ((uint32_t)hal_get_cycles() > DEBUG_BUSYLOCK) )
{
    xptr_t root_xp = XPTR( local_cxy , &this->busylocks_root );

    // update thread list of busyslocks
    xlist_add_last( root_xp , XPTR( lock_cxy  , &lock_ptr->xlist ) );
}
#endif

#if( DEBUG_BUSYLOCK && DEBUG_BUSYLOCK_THREAD_XP )
if( (type != LOCK_CHDEV_TXT0) && 
    (XPTR( local_cxy , this ) == DEBUG_BUSYLOCK_THREAD_XP) )
{
    // get cluster and local pointer of target thread
    cxy_t      thread_cxy = GET_CXY( DEBUG_BUSYLOCK_THREAD_XP );
    thread_t * thread_ptr = GET_PTR( DEBUG_BUSYLOCK_THREAD_XP );

    // display message on kernel TXT0
    printk("\n### thread [%x,%x] ACQUIRE lock %s\n",
    thread_cxy, thread_ptr, lock_type_str[type] );
}
#endif

}  // end remote_busylock_acquire()

///////////////////////////////////////////////
void remote_busylock_release( xptr_t  lock_xp )
{
    thread_t * this = CURRENT_THREAD;

    // memory barrier to update the protected object
    hal_fence();

    // get remote lock cluster and local pointer 
    cxy_t        lock_cxy = GET_CXY( lock_xp );
    busylock_t * lock_ptr = GET_PTR( lock_xp );

    // update lock state
    hal_remote_atomic_add( XPTR( lock_cxy , &lock_ptr->current ) , 1 );

    // decrement thread locks counter
    this->busylocks--;

    // memory barrier to update busylock and thread state
    hal_fence();

#if DEBUG_BUSYLOCK
uint32_t type = hal_remote_l32( XPTR( lock_cxy , &lock_ptr->type ) );
if( (type != LOCK_CHDEV_TXT0) && 
    (XPTR( local_cxy , this ) == DEBUG_BUSYLOCK_THREAD_XP) &&
    ((uint32_t)hal_get_cycles() > DEBUG_BUSYLOCK) )
{
    // remove lock from thread list of busyslocks
    xlist_unlink( XPTR( lock_cxy  , &lock_ptr->xlist ) );
}
#endif

#if (DEBUG_BUSYLOCK && DEBUG_BUSYLOCK_THREAD_XP )
if( (type != LOCK_CHDEV_TXT0) && 
    (XPTR( local_cxy , this ) == DEBUG_BUSYLOCK_THREAD_XP) )
{
    // get cluster and local pointer of target thread
    cxy_t      thread_cxy = GET_CXY( DEBUG_BUSYLOCK_THREAD_XP );
    thread_t * thread_ptr = GET_PTR( DEBUG_BUSYLOCK_THREAD_XP );

    // display message on kernel TXT0
    printk("\n### thread [%x,%x] RELEASE lock %s\n",
    thread_cxy, thread_ptr, lock_type_str[type] );
}
#endif
                                  
    // exit critical section
    hal_restore_irq( hal_remote_l32( XPTR( lock_cxy , &lock_ptr->save_sr ) ) );
 
}  // end remote_busylock_release()


