/*
 * mapper.h - Map memory, file or device in process virtual address space.
 *
 * Authors   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 _MAPPER_H_
#define _MAPPER_H_

#include <hal_types.h>
#include <hal_atomic.h>
#include <xlist.h>
#include <grdxt.h>
#include <rwlock.h>

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

struct page_s;
struct vfs_inode_s;

/*******************************************************************************************
 * The mapper implements the kernel cache for a given file or directory.
 * There is one mapper per file/dir. It is implemented as a three levels radix tree,
 * entirely stored in the same cluster as the inode representing the file/dir.
 * - The fast retrieval key is the page index in the file.
 *   The ix1_width, ix2_width, ix3_width sub-indexes are configuration parameters.
 * - The leaves are pointers on physical page descriptors, dynamically allocated
 *   in the local cluster.
 * - In a given cluster, a mapper is a "private" structure: a thread accessing the mapper
 *   must be running in the cluster containing it (can be a local thread or a RPC thread).
 * - The mapper is protected by a blocking "rwlock", to support several simultaneous
 *   readers, and only one writer. This lock implement a busy waiting policy.
 * - The mapper_get_page() function that return a page descriptor pointer from a page
 *   index in file is in charge of handling the miss on the mapper cache.
 * - The vfs_mapper_move_page() function access the file system to handle a mapper miss,
 *   or update a dirty page on device.
 * - The vfs_mapper_load_all() functions is used to load all pages of a given file 
 *   or directory into the mapper.
 * - the mapper_move_user() function is used to move data to or from an user buffer.
 *   This user space buffer can be physically distributed in several clusters.
 * - the mapper_move_kernel() function is used to move data to or from a remote kernel
 *   buffer, that can be physically located in any cluster.
 * - 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.
 ******************************************************************************************/


/*******************************************************************************************
 * This structure defines the mapper descriptor.
 ******************************************************************************************/

typedef struct mapper_s
{
	struct vfs_inode_s * inode;	      /*! owner inode                                     */
    uint32_t             type;        /*! file system type                                */
	grdxt_t	             radix;	      /*! pages cache implemented as a radix tree         */
	rwlock_t             lock;        /*! several readers / only one writer               */
	uint32_t	         refcount;    /*! several vsegs can refer the same file           */
	xlist_entry_t        vsegs_root;  /*! root of list of vsegs refering this mapper      */
	xlist_entry_t        wait_root;   /*! root of list of threads waiting on mapper       */
    list_entry_t         dirty_root;  /*! root of list of dirty pages                     */
}
mapper_t;

/*******************************************************************************************
 * This structure defines a "fragment". It is used to move data between the kernel mapper,
 * and an user buffer, that can be split in several distributed physical pages located
 * in different clusters. A fragment is a set of contiguous bytes in the file.
 * - It can be stored in one single physical page in the user buffer.
 * - It can spread two successive physical pages in the kernel mapper.
 ******************************************************************************************/

typedef struct fragment_s
{
    uint32_t    file_offset;         /*! offset of fragment in file (i.e. in mapper)      */
    uint32_t    size;                /*! number of bytes in fragment                      */
    cxy_t       buf_cxy;             /*! user buffer cluster identifier                   */
    void      * buf_ptr;             /*! local pointer on first byte in user buffer       */
}
fragment_t;

/*******************************************************************************************
 * 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.
 *******************************************************************************************
 * @ return pointer on created mapper if success / return NULL if no memory
 ******************************************************************************************/
mapper_t * mapper_create();

/*******************************************************************************************
 * This function releases all physical pages allocated for the mapper.
 * It synchronizes all dirty pages (i.e. update the file on disk) if required.
 * The mapper descriptor and the radix tree themselves are released.
 * It must be executed by a thread running in the cluster containing the mapper.
 *******************************************************************************************
 * @ mapper	 : target mapper.
 * @ return 0 if success / return EIO if a dirty page cannot be updated on device.
 ******************************************************************************************/
error_t mapper_destroy( mapper_t * mapper );

/*******************************************************************************************
 * This function move data between a mapper and a - possibly distributed - user buffer.
 * It must be called by a thread running in the cluster containing the mapper.
 * It is called by the vfs_user_move() function to implement sys_read() and sys_write().
 * If required, the data transfer is split in "fragments", where one fragment contains 
 * contiguous bytes in the same mapper page.
 * It uses "hal_uspace" accesses to move a fragment to/from the user buffer.
 * In case of write, the dirty bit is set for all pages written in the mapper.
 * The offset in the file descriptor is not modified by this function.
 *******************************************************************************************
 * @ mapper       : local pointer on mapper.
 * @ to_buffer    : mapper -> buffer if true / buffer -> mapper if false.
 * @ file_offset  : first byte to move in file.
 * @ u_buf        : user space pointer on user buffer.
 * @ size         : number of bytes to move.
 * returns O if success / returns EINVAL if error.
 ******************************************************************************************/
error_t mapper_move_user( mapper_t * mapper,
                          bool_t     to_buffer,
                          uint32_t   file_offset,
                          void     * u_buf,
                          uint32_t   size );

/*******************************************************************************************
 * This function move data between a mapper and a remote kernel buffer.
 * It must be called by a thread running in the cluster containing the mapper.
 * If required, the data transfer is split in "fragments", where one fragment contains 
 * contiguous bytes in the same mapper page.
 * It uses a "remote_memcpy" to move a fragment to/from the kernel buffer.
 * In case of write, the dirty bit is set for all pages written in the mapper.
 * The offset in the file descriptor is not modified by this function.
 *******************************************************************************************
 * @ mapper       : local pointer on mapper.
 * @ to_buffer    : mapper -> buffer if true / buffer -> mapper if false.
 * @ file_offset  : first byte to move in file.
 * @ buffer_xp    : extended pointer on kernel buffer.
 * @ size         : number of bytes to move.
 * returns O if success / returns EINVAL if error.
 ******************************************************************************************/
error_t mapper_move_kernel( mapper_t * mapper,
                            bool_t     to_buffer,
                            uint32_t   file_offset,
                            xptr_t     buffer_xp,
                            uint32_t   size );


/*******************************************************************************************
 * This function removes a physical page from the mapper, update the FS if the page
 * is dirty, and releases the page to PPM. It is called by the mapper_destroy() function.
 * It must be executed by a thread running in the cluster containing the mapper.
 * It takes both the page lock and the mapper lock in WRITE_MODE to release the page.
 *******************************************************************************************
 * @ mapper	: local pointer on the mapper.
 * @ page	: pointer on page to remove.
 * @ return 0 if success / return EIO if a dirty page cannot be copied to FS.
 ******************************************************************************************/
error_t mapper_release_page( mapper_t      * mapper,
                             struct page_s * page );

/*******************************************************************************************
 * This function searches a physical page descriptor from its index in mapper.
 * It must be executed by a thread running in the cluster containing the mapper.
 * In case of miss, it takes the mapper lock in WRITE_MODE, load the missing
 * page from device to the mapper, and release the mapper lock.
 *******************************************************************************************
 * @ mapper	: local pointer on the mapper.
 * @ index	: page index in file
 * @ returns pointer on page descriptor if success / return NULL if error.
 ******************************************************************************************/
struct page_s * mapper_get_page( mapper_t * mapper,
                                 uint32_t   index );

 

#endif /* _MAPPER_H_ */
