/*
 * page.h - physical page descriptor and related operations
 *
 * Authors Ghassan Almalles (2008,2009,2010,2011,2012)
 *         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 _PAGE_H_
#define _PAGE_H_

#include <kernel_config.h>
#include <hal_kernel_types.h>
#include <remote_busylock.h>
#include <list.h>

/***   Forward declarations   ***/

struct mapper_s;

/*************************************************************************************
 * This  defines the flags that can be attached to a physical page.
 ************************************************************************************/

#define PG_INIT		    0x0001     // page descriptor has been initialised
#define PG_RESERVED	    0x0002     // cannot be allocated by PPM
#define PG_FREE		    0x0004     // page not yet allocated by PPM
#define PG_DIRTY	    0x0040     // page has been written
#define PG_COW          0x0080     // page is copy-on-write

/*************************************************************************************
 * This structure defines a physical page descriptor.
 * - The remote_busylock is used to allows any remote thread to atomically
 *   test/modify the forks counter or the page flags.
 * - The list entry is used to register the page in a free list or in dirty list.
 *   The refcount is used for page release to KMEM.
 * NOTE: the size is 48 bytes for a 32 bits core.
 ************************************************************************************/

typedef struct page_s
{
    uint32_t          flags;          /*! flags defined above                  (4)  */
    uint32_t          order;          /*! log2( number of small pages)         (4)  */
    struct mapper_s * mapper;         /*! local pointer on associated mapper   (4)  */
    uint32_t          index;          /*! page index in mapper                 (4)  */
	list_entry_t      list;           /*! for both dirty pages and free pages  (8)  */
	int32_t           refcount;       /*! references counter for page release  (4)  */
	uint32_t          forks;          /*! number of pending forks              (4)  */
	remote_busylock_t lock;           /*! protect forks or flags modifs        (16) */
}
page_t;

/*************************************************************************************
 * This function must be called by a thread running in the local cluster.
 * It initializes the page descriptor.
 *************************************************************************************
 * @ page    : pointer to page descriptor
 ************************************************************************************/
inline void page_init( page_t * page );

/*************************************************************************************
 * This function must be called by a thread running in the local cluster.
 * It atomically set one or several flags in page descriptor flags.
 *************************************************************************************
 * @ page    : pointer to page descriptor.
 * @ value   : all non zero bits in value will be set.
 ************************************************************************************/
inline void page_set_flag( page_t   * page,
                           uint32_t   value );

/*************************************************************************************
 * This function must be called by a thread running in the local cluster.
 * It atomically reset one or several flags in page descriptor flags.
 *************************************************************************************
 * @ page    : pointer to page descriptor.
 * @ value   : all non zero bits in value will be cleared.
 ************************************************************************************/
inline void page_clear_flag( page_t   * page,
                             uint32_t   value );

/*************************************************************************************
 * This function must be called by a thread running in the local cluster.
 * It tests the value of one or several flags in page descriptor flags.
 *************************************************************************************
 * @ page    : pointer to page descriptor.
 * @ value   : all non zero bits will be tested.
 * @ returns true if at least one non zero bit in value is set / false otherwise.
 ************************************************************************************/
inline bool_t page_is_flag( page_t   * page,
                            uint32_t   value );

/*************************************************************************************
 * This function must be called by a thread running in the local cluster.
 * It resets to 0 all bytes in a given page.
 *************************************************************************************
 * @ page     : pointer on page descriptor.
 ************************************************************************************/
void page_zero( page_t * page );

/*************************************************************************************
 * This function must be called by a thread running in the local cluster.
 * It atomically increments the page refcount.
 *************************************************************************************
 * @ page     : pointer on page descriptor.
 ************************************************************************************/
inline void page_refcount_up( page_t * page );

/*************************************************************************************
 * This function must be called by a thread running in the local cluster.
 * It atomically decrements the page refcount.
 *************************************************************************************
 * @ page     : pointer on page descriptor.
 ************************************************************************************/
inline void page_refcount_down( page_t * page );




/*************************************************************************************
 * This function can be called by any thread running in any cluster.
 * It atomically set one or several flags in a remote page descriptor
 * identified by the <page_xp> argument.
 *************************************************************************************
 * @ page_xp : extended pointer to page descriptor.
 * @ value   : all non zero bits in value will be set.
 ************************************************************************************/
inline void page_remote_set_flag( xptr_t    page_xp,
                                  uint32_t  value );

/*************************************************************************************
 * This function can be called by any thread running in any cluster.
 * It atomically reset one or several flags in a remote page descriptor
 * identified by the <page_xp> argument.
 *************************************************************************************
 * @ page_xp : extended pointer to page descriptor.
 * @ value   : all non zero bits in value will be cleared.
 ************************************************************************************/
inline void page_remote_clear_flag( xptr_t    page_xp,
                                    uint32_t  value );

/*************************************************************************************
 * This function can be called by any thread running in any cluster.
 * It tests the value of one or several flags in a remote page descriptor
 * identified by the <page_xp> argument.
 *************************************************************************************
 * @ page_xp : extended pointer to page descriptor.
 * @ value   : all non zero bits will be tested.
 * @ returns true if at least one non zero bit in value is set / false otherwise.
 ************************************************************************************/
inline bool_t page_remote_is_flag( xptr_t    page_xp,
                                   uint32_t  value );

/*************************************************************************************
 * This function can be called by any thread running in any cluster.
 * It atomically increments the refcount for the remote page identified by
 * the <page_xp> argument.
 *************************************************************************************
 * @ page_xp   : extended pointer on page descriptor.
 ************************************************************************************/
inline void page_remote_refcount_up( xptr_t page_xp );

/*************************************************************************************
 * This function can be called by any thread running in any cluster.
 * It atomically decrements the refcount for the remote page identified by
 * the <page_xp> argument.
 *************************************************************************************
 * @ page_xp   : extended pointer on page descriptor.
 ************************************************************************************/
inline void page_remote_refcount_down( xptr_t  page_xp );

/*************************************************************************************
 * This debug function can be called by any thread running in any cluster.
 * It displays the values contained in a page descriptor.
 *************************************************************************************
 * @ page_xp     : extended pointer on page descriptor.
 ************************************************************************************/
void page_remote_display( xptr_t  page_xp );

#endif	/* _PAGE_H_ */
