/**
 * Copyright (C) Mellanox Technologies Ltd. 2018.  ALL RIGHTS RESERVED.
 *
 * See file LICENSE for terms.
 */

#ifndef UCS_REG_CACHE_INT_H_
#define UCS_REG_CACHE_INT_H_

#include <ucs/datastruct/list.h>
#include <ucs/type/spinlock.h>


/* Names of rcache stats counters */
enum {
    UCS_RCACHE_GETS,                /* number of get operations */
    UCS_RCACHE_HITS_FAST,           /* number of fast path hits */
    UCS_RCACHE_HITS_SLOW,           /* number of slow path hits */
    UCS_RCACHE_MISSES,              /* number of misses */
    UCS_RCACHE_MERGES,              /* number of region merges */
    UCS_RCACHE_UNMAPS,              /* number of memory unmap events */
    UCS_RCACHE_UNMAP_INVALIDATES,   /* number of regions invalidated because
                                       of unmap events */
    UCS_RCACHE_PUTS,                /* number of put operations */
    UCS_RCACHE_REGS,                /* number of memory registrations */
    UCS_RCACHE_DEREGS,              /* number of memory deregistrations */
    UCS_RCACHE_STAT_LAST
};


struct ucs_rcache {
    ucs_rcache_params_t      params;      /**< rcache parameters (immutable) */

    pthread_rwlock_t         pgt_lock;    /**< Protects the page table and all
                                               regions whose refcount is 0 */
    ucs_pgtable_t            pgtable;     /**< page table to hold the regions */


    ucs_spinlock_t           lock;        /**< Protects 'mp', 'inv_q' and 'gc_list'.
                                               This is a separate lock because we
                                               may want to invalidate regions
                                               while the page table lock is held by
                                               the calling context.
                                               @note: This lock should always be
                                               taken **after** 'pgt_lock'. */
    ucs_mpool_t              mp;          /**< Memory pool to allocate entries for
                                               inv_q and page table entries, since
                                               we cannot use regular malloc().
                                               The backing storage is original mmap()
                                               which does not generate memory events */
    ucs_queue_head_t         inv_q;       /**< Regions which were invalidated during
                                               memory events */
    ucs_list_link_t          gc_list;     /**< list for regions to destroy, regions
                                               could not be destroyed from memhook */

    unsigned long            num_regions; /**< Total number of managed regions */
    size_t                   total_size;  /**< Total size of registered memory */

    struct {
        ucs_spinlock_t       lock;        /**< Lock for this structure */
        ucs_list_link_t      list;        /**< List of regions, sorted by usage:
                                               The head of the list is the least
                                               recently used region, and the tail
                                               is the most recently used region. */
        unsigned long        count;       /**< Number of regions on list */
    } lru;
    
    char                     *name;       /**< Name of the cache, for debug purpose */

    UCS_STATS_NODE_DECLARE(stats)

    ucs_list_link_t          list;        /**< list entry in global ucs_rcache list */
};

#endif
