/*
 * mapper.h - Kernel cache for VFS files/directories definition.
 *
 * Authors   Mohamed Lamine Karaoui (2015)
 *           Alain Greiner (2016,2017,2018,2019)
 *
 * 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_kernel_types.h>
#include <hal_atomic.h>
#include <xlist.h>
#include <grdxt.h>
#include <rwlock.h>

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

struct page_s;
struct vfs_inode_s;

/*******************************************************************************************
 * This mapper_t object implements the kernel cache for a given VFS 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/directory.
 * - 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 same cluster as the radix tree.
 * - The mapper is protected by a "remote_rwlock", to support several simultaneous 
 *   "readers", and only one "writer".
 * - A "reader" thread, calling the mapper_remote_get_page() function to get a page 
 *   descriptor pointer from the page index in file, can be running in any cluster. 
 * - A "writer" thread, calling the mapper_handle_miss() function to handle a page miss
 *   must be local (running in the mapper cluster).
 * - The vfs_fs_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 directory 
 *   into the mapper (prefetch).
 * - 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 inode is destroyed.
 *
 * WARNING : This mapper implementation makes the assumption that the PPM page size
 *           is 4 Kbytes. This code should be modified to support a generic page size,
 *           defined by the CONFIG_PPM_PAGE_SIZE parameter. 
 ******************************************************************************************/


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

typedef struct mapper_s
{
	struct vfs_inode_s * inode;	      /*! owner inode                                     */
    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               */
	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 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 can be executed by any thread running in any cluster.
 *******************************************************************************************
 * @ mapper_xp	 : extended pointer on target mapper.
 ******************************************************************************************/
void mapper_destroy( xptr_t  mapper_xp );

/*******************************************************************************************
 * This function load from the IOC device a missing page identified by the <page_id> 
 * argument into a - possibly remote - mapper identified by the <mapper_xp> argument.
 * It can be executed by a thread running in any cluster.
 * It allocates a physical page from the remote cluster PPM, initialises it by accessing
 * the IOC device, and registers the page in the remote mapper radix tree.
 * WARNING : the calling function mapper_remote_get_page() is supposed to take and release
 * the lock protecting the mapper in WRITE_MODE.
 *******************************************************************************************
 * @ mapper_xp	 : [in]  extended pointer on remote mapper.
 * @ page_id     : [in]  missing page index in file.
 * @ page_xp     : [out] buffer for extended pointer on missing page descriptor.
 * @ return 0 if success / return -1 if IOC cannot be accessed.
 ******************************************************************************************/
error_t mapper_handle_miss( xptr_t     mapper_xp,
                            uint32_t   page_id,
                            xptr_t   * page_xp );

/*******************************************************************************************
 * This function removes a physical page from a - possibly remote - mapper,
 * and releases the page to the remote PPM. 
 * It can be executed by any thread running in any cluster. 
 * It takes the mapper lock in WRITE_MODE to update the mapper.
 *******************************************************************************************
 * @ mapper	: extended pointer on the remote mapper.
 * @ page	: local pointer on the page in remote mapper.
 ******************************************************************************************/
void mapper_remote_release_page( xptr_t          mapper_xp,
                                 struct page_s * page );

/*******************************************************************************************
 * This function move data between a remote mapper, identified by the <mapper_xp> argument,
 * and a distributed user buffer. It can be called by a thread running in any cluster.
 * It is called by the vfs_user_move() to implement sys_read() and sys_write() syscalls.
 * 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 mapper being an extendable cache, it is automatically extended when required.
 * The "offset" field in the file descriptor, and the "size" field in inode descriptor
 * are not modified by this function.
 *******************************************************************************************
 * @ mapper_xp    : extended 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 -1 if error.
 ******************************************************************************************/
error_t mapper_move_user( xptr_t     mapper_xp,
                          bool_t     to_buffer,
                          uint32_t   file_offset,
                          void     * u_buf,
                          uint32_t   size );

/********************************************************************************************
 * This function move <size> bytes from/to a remote mapper, identified by the <mapper_xp>
 * argument, to/from a remote kernel buffer, identified by the <buffer_xp> argument.
 * It can be called by a thread running in any cluster. 
 * If required, the data transfer is split in "fragments", where one fragment contains 
 * contiguous bytes in the same mapper page. Each fragment uses a "remote_memcpy".
 * In case of write to mapper, the dirty bit is set for all pages written in the mapper.
 *******************************************************************************************
 * @ mapper_xp    : extended 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 -1 if error.
 ******************************************************************************************/
error_t mapper_move_kernel( xptr_t     mapper_xp,
                            bool_t     to_buffer,
                            uint32_t   file_offset,
                            xptr_t     buffer_xp,
                            uint32_t   size );

/*******************************************************************************************
 * 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
 * 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_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 );

/*******************************************************************************************
 * This function allows to read a single word in a mapper seen as and array of uint32_t.
 * It has bee designed to support remote access to the FAT mapper of the FATFS. 
 * It can be called by any thread running in any cluster.
 * In case of miss, it takes the mapper lock in WRITE_MODE, load the missing
 * page from device to mapper, and release the mapper lock.
 *******************************************************************************************
 * @ mapper_xp	: [in]  extended pointer on the mapper.
 * @ page_id    : [in]  page index in mapper.
 * @ word_id    : [in]  32 bits word index in page.
 * @ value      : [out] local pointer on destination buffer.
 * @ returns 0 if success / return -1 if error.
 ******************************************************************************************/
error_t mapper_remote_get_32( xptr_t     mapper_xp,
                              uint32_t   page_id,
                              uint32_t   word_id,
                              uint32_t * value );

/*******************************************************************************************
 * This function allows to write a single word to a mapper seen as and array of uint32_t.
 * It has been designed to support remote access to the FAT mapper of the FATFS. 
 * It can be called by any thread running in any cluster.
 * In case of miss, it takes the mapper lock in WRITE_MODE, load the missing
 * page from device to mapper, and release the mapper lock.
 * It does not update the FAT on IOC device.
 *******************************************************************************************
 * @ mapper_xp	: [in]  extended pointer on the mapper.
 * @ page_id    : [in]  page index in mapper.
 * @ word_id    : [in]  32 bits word index in page.
 * @ value      : [in]  value to be written.
 * @ returns 0 if success / return -1 if error.
 ******************************************************************************************/
error_t mapper_remote_set_32( xptr_t     mapper_xp,
                              uint32_t   page_id,
                              uint32_t   word_id,
                              uint32_t   value );

/*******************************************************************************************
 * 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. 
 * 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_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.
 * It can be called by any thread running in any cluster.
 *******************************************************************************************
 * @ mapper_xp	: [in]  extended pointer on the mapper.
 * @ 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,
                          uint32_t   page_id,
                          uint32_t   nbytes );


#endif /* _MAPPER_H_ */
