/*
 * cluster.h - Cluster-Manager definition
 * 
 * authors  Ghassan Almaless (2008,2009,2010,2011,2012)
 *          Mohamed Lamine Karaoui (2015)
 *          Alain Greiner (2016)
 *
 * Copyright (c) UPMC Sorbonne Universites
 *
 * This file is part of ALMOS-MKH.
 *
 * ALMOS-MKH is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2.0 of the License.
 *
 * ALMOS-MKH is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ALMOS-MKH; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef _CLUSTER_H_
#define _CLUSTER_H_

#include <almos_config.h>
#include <hal_types.h>
#include <bits.h>
#include <spinlock.h>
#include <readlock.h>
#include <remote_barrier.h>
#include <list.h>
#include <xlist.h>
#include <dqdt.h>
#include <kmem.h>
#include <hal_atomic.h>
#include <ppm.h>
#include <kcm.h>
#include <khm.h>
#include <rpc.h>
#include <core.h>
#include <process.h>

/**** Forward declarations  ****/

struct core_s;
struct process_s;
struct device_s;


/*******************************************************************************************
 * This structure defines the process manager, that is part of the cluster manager.
 * For any process P, the process descriptor is replicated in all clusters containing
 * at least one thread of process P, but only the "reference" cluster descriptor contains 
 * the reference (complete) structures such as the GPT, the VSL, or the FDT.
 * The "owner" cluster is in charge to allocate a lpid (local process index), 
 * for all process owned by a cluster K, and to register the "reference" cluster for
 * all process owned by K. 
 *
 * Warning : the "owner" cluster, and the "reference" cluster can be different clusters. 
 *
 * The process manager of a cluster K maintains three structures:
 * 1) The pref_tbl[] is an array indexed by lpid, for all processes owned by cluster K.
 *    Each entry contains an extended pointer on the reference process descriptor.
 * 2) The local_root is the root of the local list of process descriptors in cluster K.
 *    A process descriptor P is present in K, as soon as P has a thread in cluster K.
 * 3) The copies_root[] array is indexed by lpid. Each entry contains the root of
 *    the xlist of copies for a given process owned by cluster K.   
 ******************************************************************************************/

typedef struct process_manager_s
{
	xptr_t            pref_tbl[CONFIG_MAX_PROCESS_PER_CLUSTER];  /*! reference  process   */
	spinlock_t        pref_lock;              /*! lock protecting lpid allocation/release */
    uint32_t          pref_nr;                /*! number of processes owned by cluster    */

    list_entry_t      local_root;             /*! root of list of process in cluster      */
    spinlock_t        local_lock;             /*! lock protecting access to local list    */
    uint32_t          local_nr;               /*! number of process in cluster            */

    xlist_entry_t     copies_root[CONFIG_MAX_PROCESS_PER_CLUSTER];  /*! roots of lists    */ 
    remote_spinlock_t copies_lock[CONFIG_MAX_PROCESS_PER_CLUSTER];  /*! one lock per list */
    uint32_t          copies_nr[CONFIG_MAX_PROCESS_PER_CLUSTER];    /*! number of copies  */
}
pmgr_t;

/*******************************************************************************************
 * This structure defines a cluster manager.
 * It contains both global platform informations, and cluster specific ressources
 * managed by the local kernel instance.
 ******************************************************************************************/

typedef struct cluster_s
{
	spinlock_t        kcm_lock;           /*! local, protect creation of KCM allocators   */
    remote_barrier_t  barrier;            /*! used to synchronize kernel parallel init    */

    // global parameters

	uint32_t          paddr_width;        /*! numer of bits in physical address           */
    uint32_t          x_width;            /*! number of bits to code x_size  (can be 0)   */
    uint32_t          y_width;            /*! number of bits to code y_size  (can be 0)   */
	uint32_t          x_size;             /*! number of clusters in a row    (can be 1)   */
	uint32_t          y_size;             /*! number of clusters in a column (can be 1)   */
	cxy_t             io_cxy;             /*! io cluster identifier                       */
    uint32_t          dqdt_root_level;    /*! index of root node in dqdt_tbl[]            */

    // local parameters

	uint32_t          cores_nr;           /*! number of cores in cluster                  */
    uint32_t          cores_in_kernel;    /*! number of cores currently in kernel mode    */

	core_t            core_tbl[CONFIG_MAX_LOCAL_CORES];         /*! embedded cores        */

	ppm_t             ppm;                /*! embedded kernel page manager                */
	khm_t             khm;                /*! embedded kernel heap manager                */
	kcm_t             kcm;                /*! embedded kernel cache manager (for KCMs)    */

    kcm_t           * kcm_tbl[KMEM_TYPES_NR];         /*! pointers on allocated KCMs      */

    uint32_t          ram_size;           /*! physical memory size                        */
    uint32_t          ram_base;           /*! physical memory base (local address)        */

	rpc_fifo_t        rpc_fifo;           /*! cluster RPC fifo (shared)                   */
	list_entry_t      devlist;            /*! root of list of devices in cluster          */
    struct device_s * icu;                /*! pointer on local XCU device                 */

    int32_t           pages_var;          /*! pages number increment from last update     */
    int32_t           threads_var;        /*! threads number increment from last update   */

	dqdt_node_t       dqdt_tbl[CONFIG_MAX_DQDT_DEPTH];     /*! embedded DQDT nodes        */

    pmgr_t            pmgr;               /*! embedded process manager                    */

	char              name[CONFIG_SYSFS_NAME_LEN];  
	sysfs_entry_t     node;
} 
cluster_t;

/******************************************************************************************
 * This global variable is allocated in the kernel_init.c file.
 * There is one cluster_manager per cluster, with the same local address, 
 * but different content, in all clusters containing a kernel instance. 
 *****************************************************************************************/

extern cluster_t cluster_manager;

/******************************************************************************************
 * This macro returns a local pointer on the local cluster manager.
 *****************************************************************************************/

#define LOCAL_CLUSTER    (&cluster_manager)

/******************************************************************************************
 * This generic function initialises the local cluster manager from informations found
 * in the local boot-info structure. It initializes the following local resources:
 * - the global platform parameters,
 * - the specific cluster parameters,
 * - the lock protecting KCM creation,
 * - the local DQDT nodes,
 * - the PPM, KHM, and KCM allocators,
 * - the local core descriptors,
 * - the local RPC FIFO,
 * - the process manager.
 * It does NOT initialise the local device descriptors.
 ******************************************************************************************
 * @ info : pointer on the local boot_info_t structure build by the bootloader.
 *****************************************************************************************/
error_t cluster_init( boot_info_t * info );         

/******************************************************************************************
 * This function cheks the validity of a cluster identifier. TODO useful ??? [AG]
 ******************************************************************************************
 * @ cxy    : cluster identifier to be checked.
 * @ returns true if the identified cluster does not exist.
 *****************************************************************************************/
bool_t cluster_is_undefined( cxy_t cxy );

/******************************************************************************************
 * This function register sysfs information in cluster TODO ???  [AG]
 *****************************************************************************************/
void cluster_sysfs_register();


/*****************************************************************************************/
/***************   Process Management Operations   ***************************************/
/*****************************************************************************************/

/******************************************************************************************
 * This function returns an extended pointer on the reference process descriptor
 * from the process PID.  It can be called by any thread running in any cluster,
 * as it uses a RPC to the owner cluster if the current cluster is not the owner.
 ******************************************************************************************
 * @ pid  : process identifier.
 * @ return extended pointer on the reference process descriptor.
 *****************************************************************************************/
xptr_t cluster_get_reference_process_from_pid( pid_t pid );

/****************************************************************************************** 
 * This function allocates a new PID in local cluster, that becomes the process owner.
 * It register the process descriptor extended pointer in the local processs manager 
 * pref_tbl[] array. This function is called by the rpc_process_alloc_pid() function for
 * remote registration, or by the process_init_create() function for local registration.
 ******************************************************************************************
 * @ process    : [in]  extended pointer on the process descriptor.
 * @ pid        : [out] allocated PID.
 * @ return 0 if success / return EAGAIN if no PID slot available 
 *****************************************************************************************/
error_t cluster_pid_alloc( xptr_t    process_xp,
                           pid_t   * pid );

/****************************************************************************************** 
 * This function removes a PID from the local process manager pref_tbl[] array.
 * It checks that removed process is owned by the local cluster and the lpid is legal.
 * No memory is released by this function.
 ******************************************************************************************
 * @ pid        : allocated PID.
 *****************************************************************************************/
void cluster_pid_release( pid_t  pid );

/******************************************************************************************
 * This function returns a pointer on the local process descriptor from the PID.
 * It uses the RPC
 * to create a local process descriptor copy if it does not exist yet.
 ******************************************************************************************
 * @ pid     : searched process identifier.
 * @ returns process descriptor pointer if found / returns NULL if not found.
 *****************************************************************************************/
struct process_s * cluster_get_local_process_from_pid( pid_t pid );

/******************************************************************************************
 * This function registers a local process descriptor in the process manager local_list.
 ******************************************************************************************
 * @ process     : pointer on local process descriptor.
 *****************************************************************************************/
void cluster_process_local_link( struct process_s * process ); 

/******************************************************************************************
 * This function removes a local process descriptor from the process manager local_list.
 ******************************************************************************************
 * @ process     : pointer on local process descriptor.
 *****************************************************************************************/
void cluster_process_local_unlink( struct process_s * process ); 

/******************************************************************************************
 * This function registers a local process descriptor in the owner process manager
 * copies_list, that can be in a remote cluster.
 ******************************************************************************************
 * @ process     : pointer on local process descriptor.
 *****************************************************************************************/
void cluster_process_copies_link( struct process_s * process ); 

/******************************************************************************************
 * This function removes a local process descriptor from the owner process manager
 * copies_list, that can be in a remote cluster.
 ******************************************************************************************
 * @ process     : pointer on local process descriptor.
 *****************************************************************************************/
void cluster_process_copies_unlink( struct process_s * process ); 



/*****************************************************************************************/
/***************   Cores Management Operations   *****************************************/
/*****************************************************************************************/

/******************************************************************************************
 * This function increments the "cores_in_kernel" variable in cluster descriptor.
 *****************************************************************************************/
void cluster_core_kernel_enter();

/******************************************************************************************
 * This function decrements the "cores_in_kernel" variable in cluster descriptor.
 *****************************************************************************************/
void cluster_core_kernel_exit();

/******************************************************************************************
 * This function returns the core local index that has the lowest usage in local cluster.
 *****************************************************************************************/
lid_t cluster_select_local_core();

#endif	/* _CLUSTER_H_ */
